You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ZlibOutStream.cxx 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This software is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this software; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17. * USA.
  18. */
  19. #ifdef HAVE_CONFIG_H
  20. #include <config.h>
  21. #endif
  22. #include <stdio.h>
  23. #include <rdr/ZlibOutStream.h>
  24. #include <rdr/Exception.h>
  25. #include <rfb/LogWriter.h>
  26. #include <zlib.h>
  27. #undef ZLIBOUT_DEBUG
  28. static rfb::LogWriter vlog("ZlibOutStream");
  29. using namespace rdr;
  30. enum { DEFAULT_BUF_SIZE = 16384 };
  31. ZlibOutStream::ZlibOutStream(OutStream* os, int compressLevel)
  32. : underlying(os), compressionLevel(compressLevel), newLevel(compressLevel),
  33. bufSize(DEFAULT_BUF_SIZE), offset(0)
  34. {
  35. zs = new z_stream;
  36. zs->zalloc = Z_NULL;
  37. zs->zfree = Z_NULL;
  38. zs->opaque = Z_NULL;
  39. zs->next_in = Z_NULL;
  40. zs->avail_in = 0;
  41. if (deflateInit(zs, compressLevel) != Z_OK) {
  42. delete zs;
  43. throw Exception("ZlibOutStream: deflateInit failed");
  44. }
  45. ptr = start = new U8[bufSize];
  46. end = start + bufSize;
  47. }
  48. ZlibOutStream::~ZlibOutStream()
  49. {
  50. try {
  51. flush();
  52. } catch (Exception&) {
  53. }
  54. delete [] start;
  55. deflateEnd(zs);
  56. delete zs;
  57. }
  58. void ZlibOutStream::setUnderlying(OutStream* os)
  59. {
  60. underlying = os;
  61. }
  62. void ZlibOutStream::setCompressionLevel(int level)
  63. {
  64. if (level < -1 || level > 9)
  65. level = -1; // Z_DEFAULT_COMPRESSION
  66. newLevel = level;
  67. }
  68. size_t ZlibOutStream::length()
  69. {
  70. return offset + ptr - start;
  71. }
  72. void ZlibOutStream::flush()
  73. {
  74. checkCompressionLevel();
  75. zs->next_in = start;
  76. zs->avail_in = ptr - start;
  77. #ifdef ZLIBOUT_DEBUG
  78. vlog.debug("flush: avail_in %d",zs->avail_in);
  79. #endif
  80. // Force out everything from the zlib encoder
  81. deflate(corked ? Z_NO_FLUSH : Z_SYNC_FLUSH);
  82. if (zs->avail_in == 0) {
  83. offset += ptr - start;
  84. ptr = start;
  85. } else {
  86. // didn't consume all the data? try shifting what's left to the
  87. // start of the buffer.
  88. memmove(start, zs->next_in, ptr - zs->next_in);
  89. offset += zs->next_in - start;
  90. ptr -= zs->next_in - start;
  91. }
  92. }
  93. void ZlibOutStream::cork(bool enable)
  94. {
  95. OutStream::cork(enable);
  96. underlying->cork(enable);
  97. }
  98. void ZlibOutStream::overrun(size_t needed)
  99. {
  100. #ifdef ZLIBOUT_DEBUG
  101. vlog.debug("overrun");
  102. #endif
  103. if (needed > bufSize)
  104. throw Exception("ZlibOutStream overrun: buffer size exceeded");
  105. checkCompressionLevel();
  106. while (avail() < needed) {
  107. // use corked to make zlib a bit more efficient since we're not trying
  108. // to end the stream here, just make some room
  109. corked = true;
  110. flush();
  111. corked = false;
  112. }
  113. }
  114. void ZlibOutStream::deflate(int flush)
  115. {
  116. int rc;
  117. if (!underlying)
  118. throw Exception("ZlibOutStream: underlying OutStream has not been set");
  119. if ((flush == Z_NO_FLUSH) && (zs->avail_in == 0))
  120. return;
  121. do {
  122. size_t chunk;
  123. zs->next_out = underlying->getptr(1);
  124. zs->avail_out = chunk = underlying->avail();
  125. #ifdef ZLIBOUT_DEBUG
  126. vlog.debug("calling deflate, avail_in %d, avail_out %d",
  127. zs->avail_in,zs->avail_out);
  128. #endif
  129. rc = ::deflate(zs, flush);
  130. if (rc < 0) {
  131. // Silly zlib returns an error if you try to flush something twice
  132. if ((rc == Z_BUF_ERROR) && (flush != Z_NO_FLUSH))
  133. break;
  134. throw Exception("ZlibOutStream: deflate failed");
  135. }
  136. #ifdef ZLIBOUT_DEBUG
  137. vlog.debug("after deflate: %d bytes",
  138. zs->next_out-underlying->getptr());
  139. #endif
  140. underlying->setptr(chunk - zs->avail_out);
  141. } while (zs->avail_out == 0);
  142. }
  143. void ZlibOutStream::checkCompressionLevel()
  144. {
  145. int rc;
  146. if (newLevel != compressionLevel) {
  147. #ifdef ZLIBOUT_DEBUG
  148. vlog.debug("change: avail_in %d",zs->avail_in);
  149. #endif
  150. // zlib is just horribly stupid. It does an implicit flush on
  151. // parameter changes, but the flush it does is not one that forces
  152. // out all the data. And since you cannot flush things again, we
  153. // cannot force out our data after the parameter change. Hence we
  154. // need to do a more proper flush here first.
  155. deflate(Z_SYNC_FLUSH);
  156. rc = deflateParams (zs, newLevel, Z_DEFAULT_STRATEGY);
  157. if (rc < 0) {
  158. // The implicit flush can result in this error, caused by the
  159. // explicit flush we did above. It should be safe to ignore though
  160. // as the first flush should have left things in a stable state...
  161. if (rc != Z_BUF_ERROR)
  162. throw Exception("ZlibOutStream: deflateParams failed");
  163. }
  164. compressionLevel = newLevel;
  165. }
  166. }