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.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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. #include <stdio.h>
  20. #include <rdr/ZlibOutStream.h>
  21. #include <rdr/Exception.h>
  22. #include <rfb/LogWriter.h>
  23. #include <zlib.h>
  24. #undef ZLIBOUT_DEBUG
  25. static rfb::LogWriter vlog("ZlibOutStream");
  26. using namespace rdr;
  27. enum { DEFAULT_BUF_SIZE = 16384 };
  28. ZlibOutStream::ZlibOutStream(OutStream* os, size_t bufSize_, int compressLevel)
  29. : underlying(os), compressionLevel(compressLevel), newLevel(compressLevel),
  30. bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
  31. {
  32. zs = new z_stream;
  33. zs->zalloc = Z_NULL;
  34. zs->zfree = Z_NULL;
  35. zs->opaque = Z_NULL;
  36. zs->next_in = Z_NULL;
  37. zs->avail_in = 0;
  38. if (deflateInit(zs, compressLevel) != Z_OK) {
  39. delete zs;
  40. throw Exception("ZlibOutStream: deflateInit failed");
  41. }
  42. ptr = start = new U8[bufSize];
  43. end = start + bufSize;
  44. }
  45. ZlibOutStream::~ZlibOutStream()
  46. {
  47. try {
  48. flush();
  49. } catch (Exception&) {
  50. }
  51. delete [] start;
  52. deflateEnd(zs);
  53. delete zs;
  54. }
  55. void ZlibOutStream::setUnderlying(OutStream* os)
  56. {
  57. underlying = os;
  58. }
  59. void ZlibOutStream::setCompressionLevel(int level)
  60. {
  61. if (level < -1 || level > 9)
  62. level = -1; // Z_DEFAULT_COMPRESSION
  63. newLevel = level;
  64. }
  65. size_t ZlibOutStream::length()
  66. {
  67. return offset + ptr - start;
  68. }
  69. void ZlibOutStream::flush()
  70. {
  71. checkCompressionLevel();
  72. zs->next_in = start;
  73. zs->avail_in = ptr - start;
  74. #ifdef ZLIBOUT_DEBUG
  75. vlog.debug("flush: avail_in %d",zs->avail_in);
  76. #endif
  77. // Force out everything from the zlib encoder
  78. deflate(Z_SYNC_FLUSH);
  79. offset += ptr - start;
  80. ptr = start;
  81. }
  82. size_t ZlibOutStream::overrun(size_t itemSize, size_t nItems)
  83. {
  84. #ifdef ZLIBOUT_DEBUG
  85. vlog.debug("overrun");
  86. #endif
  87. if (itemSize > bufSize)
  88. throw Exception("ZlibOutStream overrun: max itemSize exceeded");
  89. checkCompressionLevel();
  90. while ((size_t)(end - ptr) < itemSize) {
  91. zs->next_in = start;
  92. zs->avail_in = ptr - start;
  93. deflate(Z_NO_FLUSH);
  94. // output buffer not full
  95. if (zs->avail_in == 0) {
  96. offset += ptr - start;
  97. ptr = start;
  98. } else {
  99. // but didn't consume all the data? try shifting what's left to the
  100. // start of the buffer.
  101. vlog.info("z out buf not full, but in data not consumed");
  102. memmove(start, zs->next_in, ptr - zs->next_in);
  103. offset += zs->next_in - start;
  104. ptr -= zs->next_in - start;
  105. }
  106. }
  107. if (itemSize * nItems > (size_t)(end - ptr))
  108. nItems = (end - ptr) / itemSize;
  109. return nItems;
  110. }
  111. void ZlibOutStream::deflate(int flush)
  112. {
  113. int rc;
  114. if (!underlying)
  115. throw Exception("ZlibOutStream: underlying OutStream has not been set");
  116. if ((flush == Z_NO_FLUSH) && (zs->avail_in == 0))
  117. return;
  118. do {
  119. underlying->check(1);
  120. zs->next_out = underlying->getptr();
  121. zs->avail_out = underlying->getend() - underlying->getptr();
  122. #ifdef ZLIBOUT_DEBUG
  123. vlog.debug("calling deflate, avail_in %d, avail_out %d",
  124. zs->avail_in,zs->avail_out);
  125. #endif
  126. rc = ::deflate(zs, flush);
  127. if (rc != Z_OK) {
  128. // Silly zlib returns an error if you try to flush something twice
  129. if ((rc == Z_BUF_ERROR) && (flush != Z_NO_FLUSH))
  130. break;
  131. throw Exception("ZlibOutStream: deflate failed");
  132. }
  133. #ifdef ZLIBOUT_DEBUG
  134. vlog.debug("after deflate: %d bytes",
  135. zs->next_out-underlying->getptr());
  136. #endif
  137. underlying->setptr(zs->next_out);
  138. } while (zs->avail_out == 0);
  139. }
  140. void ZlibOutStream::checkCompressionLevel()
  141. {
  142. int rc;
  143. if (newLevel != compressionLevel) {
  144. #ifdef ZLIBOUT_DEBUG
  145. vlog.debug("change: avail_in %d",zs->avail_in);
  146. #endif
  147. // zlib is just horribly stupid. It does an implicit flush on
  148. // parameter changes, but the flush it does is not one that forces
  149. // out all the data. And since you cannot flush things again, we
  150. // cannot force out our data after the parameter change. Hence we
  151. // need to do a more proper flush here first.
  152. deflate(Z_SYNC_FLUSH);
  153. rc = deflateParams (zs, newLevel, Z_DEFAULT_STRATEGY);
  154. if (rc != Z_OK) {
  155. // The implicit flush can result in this error, caused by the
  156. // explicit flush we did above. It should be safe to ignore though
  157. // as the first flush should have left things in a stable state...
  158. if (rc != Z_BUF_ERROR)
  159. throw Exception("ZlibOutStream: deflateParams failed");
  160. }
  161. compressionLevel = newLevel;
  162. }
  163. }