PageRenderTime 39ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/mordor/streams/zlib.cpp

http://github.com/mozy/mordor
C++ | 425 lines | 384 code | 24 blank | 17 comment | 81 complexity | 65da1c18f2c96c0abe2c7e43bf849de0 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include "zlib.h"
  3. #include "mordor/assert.h"
  4. #include "mordor/exception.h"
  5. #include "mordor/log.h"
  6. #ifdef MSVC
  7. #pragma comment(lib, "zdll")
  8. #endif
  9. namespace Mordor {
  10. static Logger::ptr g_log = Log::lookup("mordor:streams:zlib");
  11. ZlibStream::ZlibStream(Stream::ptr parent, bool own, Type type, int level,
  12. int windowBits, int memlevel, Strategy strategy, bool invert)
  13. : MutatingFilterStream(parent, own),
  14. m_closed(true)
  15. {
  16. init(type, level, windowBits, memlevel, strategy, invert);
  17. }
  18. void
  19. ZlibStream::init(Type type, int level, int windowBits, int memlevel, Strategy strategy, bool invert)
  20. {
  21. MORDOR_ASSERT(supportsRead() || supportsWrite());
  22. MORDOR_ASSERT(!(supportsRead() && supportsWrite()));
  23. MORDOR_ASSERT((level >= 0 && level <= 9) || level == Z_DEFAULT_COMPRESSION);
  24. MORDOR_ASSERT(windowBits >= 8 && windowBits <= 15);
  25. MORDOR_ASSERT(memlevel >= 1 && memlevel <= 9);
  26. switch (type) {
  27. case ZLIB:
  28. break;
  29. case DEFLATE:
  30. windowBits = -windowBits;
  31. break;
  32. case GZIP:
  33. windowBits += 16;
  34. break;
  35. default:
  36. MORDOR_ASSERT(false);
  37. }
  38. m_windowBits = windowBits;
  39. m_level = level;
  40. m_memlevel = memlevel;
  41. m_strategy = strategy;
  42. m_doInflate = (!invert && supportsRead()) || (invert && supportsWrite());
  43. reset();
  44. }
  45. void
  46. ZlibStream::reset()
  47. {
  48. m_inBuffer.clear();
  49. m_outBuffer.clear(false);
  50. if (!m_closed) {
  51. if (m_doInflate) {
  52. inflateEnd(&m_strm);
  53. } else {
  54. deflateEnd(&m_strm);
  55. }
  56. m_closed = true;
  57. }
  58. int rc;
  59. memset(&m_strm, 0, sizeof(z_stream));
  60. if (m_doInflate) {
  61. rc = inflateInit2(&m_strm, m_windowBits);
  62. } else {
  63. rc = deflateInit2(&m_strm, m_level, Z_DEFLATED, m_windowBits, m_memlevel,
  64. (int)m_strategy);
  65. }
  66. switch (rc) {
  67. case Z_OK:
  68. m_closed = false;
  69. break;
  70. case Z_MEM_ERROR:
  71. throw std::bad_alloc();
  72. case Z_STREAM_ERROR:
  73. {
  74. std::string message(m_strm.msg ? m_strm.msg : "");
  75. if (m_doInflate) {
  76. inflateEnd(&m_strm);
  77. } else {
  78. deflateEnd(&m_strm);
  79. }
  80. throw std::runtime_error(message);
  81. }
  82. default:
  83. MORDOR_NOTREACHED();
  84. }
  85. }
  86. ZlibStream::ZlibStream(Stream::ptr parent, int level, int windowBits, int memlevel, Strategy strategy,
  87. bool own, bool invert)
  88. : MutatingFilterStream(parent, own),
  89. m_closed(true)
  90. {
  91. init(ZLIB, level, windowBits, memlevel, strategy, invert);
  92. }
  93. ZlibStream::ZlibStream(Stream::ptr parent, bool own, bool invert)
  94. : MutatingFilterStream(parent, own),
  95. m_closed(true)
  96. {
  97. init(ZLIB, Z_DEFAULT_COMPRESSION, 15, 8, DEFAULT, invert);
  98. }
  99. ZlibStream::~ZlibStream()
  100. {
  101. if (!m_closed) {
  102. if (m_doInflate) {
  103. inflateEnd(&m_strm);
  104. } else {
  105. deflateEnd(&m_strm);
  106. }
  107. }
  108. }
  109. void
  110. ZlibStream::close(CloseType type)
  111. {
  112. if ((type == READ && supportsWrite()) ||
  113. (type == WRITE && supportsRead()) ||
  114. m_closed) {
  115. if (ownsParent())
  116. parent()->close(type);
  117. return;
  118. }
  119. if (supportsWrite())
  120. flush(Z_FINISH);
  121. if (m_doInflate)
  122. inflateEnd(&m_strm);
  123. else
  124. deflateEnd(&m_strm);
  125. m_closed = true;
  126. if (ownsParent())
  127. parent()->close(type);
  128. }
  129. size_t ZlibStream::doInflateForRead(Buffer &buffer, size_t length)
  130. {
  131. if (m_closed)
  132. return 0;
  133. struct iovec outbuf = buffer.writeBuffer(length, false);
  134. m_strm.next_out = (Bytef*)outbuf.iov_base;
  135. m_strm.avail_out = outbuf.iov_len;
  136. while (true) {
  137. std::vector<iovec> inbufs = m_inBuffer.readBuffers();
  138. if (!inbufs.empty()) {
  139. m_strm.next_in = (Bytef*)inbufs[0].iov_base;
  140. m_strm.avail_in = inbufs[0].iov_len;
  141. } else {
  142. m_strm.next_in = NULL;
  143. m_strm.avail_in = 0;
  144. }
  145. int rc = inflate(&m_strm, Z_NO_FLUSH);
  146. MORDOR_LOG_DEBUG(g_log) << this << " inflate(("
  147. << (inbufs.empty() ? 0 : inbufs[0].iov_len) << ", "
  148. << outbuf.iov_len << ")): " << rc << " (" << m_strm.avail_in
  149. << ", " << m_strm.avail_out << ")";
  150. if (!inbufs.empty())
  151. m_inBuffer.consume(inbufs[0].iov_len - m_strm.avail_in);
  152. size_t result;
  153. switch (rc) {
  154. case Z_STREAM_END:
  155. // May have still produced output
  156. result = outbuf.iov_len - m_strm.avail_out;
  157. buffer.produce(result);
  158. inflateEnd(&m_strm);
  159. m_closed = true;
  160. return result;
  161. case Z_OK:
  162. result = outbuf.iov_len - m_strm.avail_out;
  163. // It consumed input, but produced no output... DON'T return eof
  164. if (result == 0)
  165. continue;
  166. buffer.produce(result);
  167. return result;
  168. case Z_BUF_ERROR:
  169. // no progress... we need to provide more input (since we're
  170. // guaranteed to provide output)
  171. MORDOR_ASSERT(m_strm.avail_in == 0);
  172. MORDOR_ASSERT(inbufs.empty());
  173. result = parent()->read(m_inBuffer, m_bufferSize);
  174. if (result == 0)
  175. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  176. break;
  177. case Z_MEM_ERROR:
  178. MORDOR_THROW_EXCEPTION(std::bad_alloc());
  179. case Z_NEED_DICT:
  180. MORDOR_THROW_EXCEPTION(NeedPresetDictionaryException());
  181. case Z_DATA_ERROR:
  182. MORDOR_THROW_EXCEPTION(CorruptedZlibStreamException());
  183. default:
  184. MORDOR_NOTREACHED();
  185. }
  186. }
  187. }
  188. size_t ZlibStream::doDeflateForRead(Buffer &buffer, size_t length)
  189. {
  190. if (m_closed)
  191. return 0;
  192. struct iovec outbuf = buffer.writeBuffer(length, false);
  193. m_strm.next_out = (Bytef*)outbuf.iov_base;
  194. m_strm.avail_out = outbuf.iov_len;
  195. while (true) {
  196. bool parentEof = false;
  197. if (m_inBuffer.readAvailable() == 0) {
  198. size_t result = parent()->read(m_inBuffer, m_bufferSize);
  199. if (result == 0) {
  200. parentEof = true;
  201. }
  202. }
  203. struct iovec inbuf = m_inBuffer.readBuffer((size_t)~0, true);
  204. m_strm.next_in = (Bytef*)inbuf.iov_base;
  205. m_strm.avail_in = inbuf.iov_len;
  206. int rc = deflate(&m_strm, parentEof?Z_FINISH:Z_NO_FLUSH);
  207. MORDOR_LOG_DEBUG(g_log) << this << " deflate((" << inbuf.iov_len << ", "
  208. << outbuf.iov_len << "), " << (parentEof?"Z_FINISH":"Z_NO_FLUSH") << "): " << rc << " ("
  209. << m_strm.avail_in << ", " << m_strm.avail_out << ")";
  210. // We are always providing both input and output
  211. MORDOR_ASSERT(rc != Z_BUF_ERROR);
  212. m_inBuffer.consume(inbuf.iov_len - m_strm.avail_in);
  213. size_t result;
  214. switch(rc) {
  215. case Z_STREAM_END:
  216. // Pending output is flushed and there is enough output space
  217. result = outbuf.iov_len - m_strm.avail_out;
  218. buffer.produce(result);
  219. return result;
  220. case Z_OK:
  221. //if deflate returns with Z_OK, this function must be called again with Z_FINISH
  222. //and more output space (updated avail_out) but no more input data, until it returns
  223. //with Z_STREAM_END or an error.
  224. result = outbuf.iov_len - m_strm.avail_out;
  225. if (result == 0)
  226. continue;
  227. buffer.produce(result);
  228. return result;
  229. default:
  230. MORDOR_NOTREACHED();
  231. }
  232. }
  233. }
  234. size_t ZlibStream::doInflateForWrite(const Buffer &buffer, size_t length)
  235. {
  236. MORDOR_ASSERT(!m_closed);
  237. flushBuffer();
  238. while (true) {
  239. if (m_outBuffer.writeAvailable() == 0)
  240. m_outBuffer.reserve(m_bufferSize);
  241. struct iovec outbuf = m_outBuffer.writeBuffer(~0u, false);
  242. size_t len = (std::min)(length, buffer.readAvailable());
  243. struct iovec inbuf = buffer.readBuffer(len, true);
  244. m_strm.next_in = (Bytef*)inbuf.iov_base;
  245. m_strm.avail_in = inbuf.iov_len;
  246. m_strm.next_out = (Bytef*)outbuf.iov_base;
  247. m_strm.avail_out = outbuf.iov_len;
  248. int rc = inflate(&m_strm, Z_NO_FLUSH);
  249. MORDOR_LOG_DEBUG(g_log) << this << " inflate(("
  250. << inbuf.iov_len << ", " << outbuf.iov_len << ")): " << rc
  251. << " (" << m_strm.avail_in << ", " << m_strm.avail_out << ")";
  252. // We are always providing both input and output
  253. MORDOR_ASSERT(rc != Z_BUF_ERROR);
  254. size_t result;
  255. switch (rc) {
  256. case Z_STREAM_END:
  257. // May have still produced output
  258. result = inbuf.iov_len - m_strm.avail_in;
  259. m_outBuffer.produce(outbuf.iov_len - m_strm.avail_out);
  260. m_closed = true;
  261. inflateEnd(&m_strm);
  262. try {
  263. flushBuffer();
  264. } catch (const std::runtime_error&) {
  265. // Swallow it
  266. }
  267. return result;
  268. case Z_OK:
  269. // some progress has been made (more input processed or more output produced)
  270. result = inbuf.iov_len - m_strm.avail_in;
  271. if (result == 0)
  272. continue;
  273. m_outBuffer.produce(outbuf.iov_len - m_strm.avail_out);
  274. flushBuffer();
  275. return result;
  276. case Z_MEM_ERROR:
  277. MORDOR_THROW_EXCEPTION(std::bad_alloc());
  278. case Z_NEED_DICT:
  279. MORDOR_THROW_EXCEPTION(NeedPresetDictionaryException());
  280. case Z_DATA_ERROR:
  281. MORDOR_THROW_EXCEPTION(CorruptedZlibStreamException());
  282. default:
  283. MORDOR_NOTREACHED();
  284. }
  285. }
  286. }
  287. size_t ZlibStream::doDeflateForWrite(const Buffer &buffer, size_t length)
  288. {
  289. MORDOR_ASSERT(!m_closed);
  290. flushBuffer();
  291. while (true) {
  292. if (m_outBuffer.writeAvailable() == 0)
  293. m_outBuffer.reserve(m_bufferSize);
  294. struct iovec inbuf = buffer.readBuffer(length, false);
  295. struct iovec outbuf = m_outBuffer.writeBuffer(~0u, false);
  296. m_strm.next_in = (Bytef*)inbuf.iov_base;
  297. m_strm.avail_in = inbuf.iov_len;
  298. m_strm.next_out = (Bytef*)outbuf.iov_base;
  299. m_strm.avail_out = outbuf.iov_len;
  300. int rc = deflate(&m_strm, Z_NO_FLUSH);
  301. MORDOR_LOG_DEBUG(g_log) << this << " deflate((" << inbuf.iov_len << ", "
  302. << outbuf.iov_len << "), Z_NO_FLUSH): " << rc << " ("
  303. << m_strm.avail_in << ", " << m_strm.avail_out << ")";
  304. // We are always providing both input and output
  305. MORDOR_ASSERT(rc != Z_BUF_ERROR);
  306. // We're not doing Z_FINISH, so we shouldn't get EOF
  307. MORDOR_ASSERT(rc != Z_STREAM_END);
  308. size_t result;
  309. switch(rc) {
  310. case Z_OK:
  311. result = inbuf.iov_len - m_strm.avail_in;
  312. if (result == 0)
  313. continue;
  314. m_outBuffer.produce(outbuf.iov_len - m_strm.avail_out);
  315. try {
  316. flushBuffer();
  317. } catch (const std::runtime_error&) {
  318. // Swallow it
  319. }
  320. return result;
  321. default:
  322. MORDOR_NOTREACHED();
  323. }
  324. }
  325. }
  326. size_t
  327. ZlibStream::read(Buffer &buffer, size_t length)
  328. {
  329. if (m_doInflate)
  330. return doInflateForRead(buffer, length);
  331. else
  332. return doDeflateForRead(buffer, length);
  333. }
  334. size_t
  335. ZlibStream::write(const Buffer &buffer, size_t length)
  336. {
  337. if (m_doInflate)
  338. return doInflateForWrite(buffer, length);
  339. else
  340. return doDeflateForWrite(buffer, length);
  341. }
  342. void
  343. ZlibStream::flush(bool flushParent)
  344. {
  345. flush(Z_SYNC_FLUSH);
  346. if (flushParent)
  347. parent()->flush();
  348. }
  349. void
  350. ZlibStream::flush(int flush)
  351. {
  352. flushBuffer();
  353. while (true) {
  354. if (m_outBuffer.writeAvailable() == 0)
  355. m_outBuffer.reserve(m_bufferSize);
  356. struct iovec outbuf = m_outBuffer.writeBuffer(~0u, false);
  357. MORDOR_ASSERT(m_strm.avail_in == 0);
  358. m_strm.next_out = (Bytef*)outbuf.iov_base;
  359. m_strm.avail_out = outbuf.iov_len;
  360. int rc;
  361. if (m_doInflate) {
  362. rc = inflate(&m_strm, flush);
  363. MORDOR_LOG_DEBUG(g_log) << this << " inflate((0, " << outbuf.iov_len
  364. << "), " << flush << "): " << rc << " (0, " << m_strm.avail_out
  365. << ")";
  366. } else {
  367. rc = deflate(&m_strm, flush);
  368. MORDOR_LOG_DEBUG(g_log) << this << " deflate((0, " << outbuf.iov_len
  369. << "), " << flush << "): " << rc << " (0, " << m_strm.avail_out
  370. << ")";
  371. }
  372. MORDOR_ASSERT(m_strm.avail_in == 0);
  373. m_outBuffer.produce(outbuf.iov_len - m_strm.avail_out);
  374. MORDOR_ASSERT(flush == Z_FINISH || rc != Z_STREAM_END);
  375. switch (rc) {
  376. case Z_STREAM_END:
  377. m_closed = true;
  378. if (m_doInflate)
  379. inflateEnd(&m_strm);
  380. else
  381. deflateEnd(&m_strm);
  382. flushBuffer();
  383. return;
  384. case Z_OK:
  385. break;
  386. case Z_BUF_ERROR:
  387. flushBuffer();
  388. return;
  389. default:
  390. MORDOR_NOTREACHED();
  391. }
  392. }
  393. }
  394. void
  395. ZlibStream::flushBuffer()
  396. {
  397. while (m_outBuffer.readAvailable() > 0)
  398. m_outBuffer.consume(parent()->write(m_outBuffer,
  399. m_outBuffer.readAvailable()));
  400. }
  401. }