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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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. bool oldCorked;
  108. // use corked to make zlib a bit more efficient since we're not trying
  109. // to end the stream here, just make some room
  110. oldCorked = corked;
  111. corked = true;
  112. flush();
  113. corked = oldCorked;
  114. }
  115. }
  116. void ZlibOutStream::deflate(int flush)
  117. {
  118. int rc;
  119. if (!underlying)
  120. throw Exception("ZlibOutStream: underlying OutStream has not been set");
  121. if ((flush == Z_NO_FLUSH) && (zs->avail_in == 0))
  122. return;
  123. do {
  124. size_t chunk;
  125. zs->next_out = underlying->getptr(1);
  126. zs->avail_out = chunk = underlying->avail();
  127. #ifdef ZLIBOUT_DEBUG
  128. vlog.debug("calling deflate, avail_in %d, avail_out %d",
  129. zs->avail_in,zs->avail_out);
  130. #endif
  131. rc = ::deflate(zs, flush);
  132. if (rc < 0) {
  133. // Silly zlib returns an error if you try to flush something twice
  134. if ((rc == Z_BUF_ERROR) && (flush != Z_NO_FLUSH))
  135. break;
  136. throw Exception("ZlibOutStream: deflate failed");
  137. }
  138. #ifdef ZLIBOUT_DEBUG
  139. vlog.debug("after deflate: %d bytes",
  140. zs->next_out-underlying->getptr());
  141. #endif
  142. underlying->setptr(chunk - zs->avail_out);
  143. } while (zs->avail_out == 0);
  144. }
  145. void ZlibOutStream::checkCompressionLevel()
  146. {
  147. int rc;
  148. if (newLevel != compressionLevel) {
  149. #ifdef ZLIBOUT_DEBUG
  150. vlog.debug("change: avail_in %d",zs->avail_in);
  151. #endif
  152. // zlib is just horribly stupid. It does an implicit flush on
  153. // parameter changes, but the flush it does is not one that forces
  154. // out all the data. And since you cannot flush things again, we
  155. // cannot force out our data after the parameter change. Hence we
  156. // need to do a more proper flush here first.
  157. deflate(Z_SYNC_FLUSH);
  158. rc = deflateParams (zs, newLevel, Z_DEFAULT_STRATEGY);
  159. if (rc < 0) {
  160. // The implicit flush can result in this error, caused by the
  161. // explicit flush we did above. It should be safe to ignore though
  162. // as the first flush should have left things in a stable state...
  163. if (rc != Z_BUF_ERROR)
  164. throw Exception("ZlibOutStream: deflateParams failed");
  165. }
  166. compressionLevel = newLevel;
  167. }
  168. }