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 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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. ZlibOutStream::ZlibOutStream(OutStream* os, int compressLevel)
  31. : underlying(os), compressionLevel(compressLevel), newLevel(compressLevel)
  32. {
  33. zs = new z_stream;
  34. zs->zalloc = Z_NULL;
  35. zs->zfree = Z_NULL;
  36. zs->opaque = Z_NULL;
  37. zs->next_in = Z_NULL;
  38. zs->avail_in = 0;
  39. if (deflateInit(zs, compressLevel) != Z_OK) {
  40. delete zs;
  41. throw Exception("ZlibOutStream: deflateInit failed");
  42. }
  43. }
  44. ZlibOutStream::~ZlibOutStream()
  45. {
  46. try {
  47. flush();
  48. } catch (Exception&) {
  49. }
  50. deflateEnd(zs);
  51. delete zs;
  52. }
  53. void ZlibOutStream::setUnderlying(OutStream* os)
  54. {
  55. underlying = os;
  56. if (underlying)
  57. underlying->cork(corked);
  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. void ZlibOutStream::flush()
  66. {
  67. BufferedOutStream::flush();
  68. if (underlying != NULL)
  69. underlying->flush();
  70. }
  71. void ZlibOutStream::cork(bool enable)
  72. {
  73. BufferedOutStream::cork(enable);
  74. if (underlying != NULL)
  75. underlying->cork(enable);
  76. }
  77. bool ZlibOutStream::flushBuffer()
  78. {
  79. checkCompressionLevel();
  80. zs->next_in = sentUpTo;
  81. zs->avail_in = ptr - sentUpTo;
  82. #ifdef ZLIBOUT_DEBUG
  83. vlog.debug("flush: avail_in %d",zs->avail_in);
  84. #endif
  85. // Force out everything from the zlib encoder
  86. deflate(corked ? Z_NO_FLUSH : Z_SYNC_FLUSH);
  87. sentUpTo = zs->next_in;
  88. return true;
  89. }
  90. void ZlibOutStream::deflate(int flush)
  91. {
  92. int rc;
  93. if (!underlying)
  94. throw Exception("ZlibOutStream: underlying OutStream has not been set");
  95. if ((flush == Z_NO_FLUSH) && (zs->avail_in == 0))
  96. return;
  97. do {
  98. size_t chunk;
  99. zs->next_out = underlying->getptr(1);
  100. zs->avail_out = chunk = underlying->avail();
  101. #ifdef ZLIBOUT_DEBUG
  102. vlog.debug("calling deflate, avail_in %d, avail_out %d",
  103. zs->avail_in,zs->avail_out);
  104. #endif
  105. rc = ::deflate(zs, flush);
  106. if (rc < 0) {
  107. // Silly zlib returns an error if you try to flush something twice
  108. if ((rc == Z_BUF_ERROR) && (flush != Z_NO_FLUSH))
  109. break;
  110. throw Exception("ZlibOutStream: deflate failed");
  111. }
  112. #ifdef ZLIBOUT_DEBUG
  113. vlog.debug("after deflate: %d bytes",
  114. zs->next_out-underlying->getptr());
  115. #endif
  116. underlying->setptr(chunk - zs->avail_out);
  117. } while (zs->avail_out == 0);
  118. }
  119. void ZlibOutStream::checkCompressionLevel()
  120. {
  121. int rc;
  122. if (newLevel != compressionLevel) {
  123. #ifdef ZLIBOUT_DEBUG
  124. vlog.debug("change: avail_in %d",zs->avail_in);
  125. #endif
  126. // zlib is just horribly stupid. It does an implicit flush on
  127. // parameter changes, but the flush it does is not one that forces
  128. // out all the data. And since you cannot flush things again, we
  129. // cannot force out our data after the parameter change. Hence we
  130. // need to do a more proper flush here first.
  131. deflate(Z_SYNC_FLUSH);
  132. rc = deflateParams (zs, newLevel, Z_DEFAULT_STRATEGY);
  133. if (rc < 0) {
  134. // The implicit flush can result in this error, caused by the
  135. // explicit flush we did above. It should be safe to ignore though
  136. // as the first flush should have left things in a stable state...
  137. if (rc != Z_BUF_ERROR)
  138. throw Exception("ZlibOutStream: deflateParams failed");
  139. }
  140. compressionLevel = newLevel;
  141. }
  142. }