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

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