zlib_source.cc
1 // Copyright 2013, Beeri 15. All rights reserved.
2 // Author: Roman Gershman (romange@gmail.com)
3 //
4 
5 #include "util/zlib_source.h"
6 
7 #include <memory>
8 #include "base/logging.h"
9 #include "strings/strcat.h"
10 
11 namespace util {
12 
13 inline Status ToStatus(int err, StringPiece msg) {
14  if (msg.empty()) // to overcome https://github.com/abseil/abseil-cpp/issues/315
15  msg = "";
16  return Status(StatusCode::IO_ERROR, StrCat("ZLib error ", err, ": ", msg));
17 }
18 
19 static inline int internalInflateInit2(ZlibSource::Format format, z_stream* zcontext) {
20  int windowBitsFormat = 0;
21  switch (format) {
22  case ZlibSource::GZIP:
23  windowBitsFormat = 16;
24  break;
25  case ZlibSource::AUTO:
26  windowBitsFormat = 32;
27  break;
28  case ZlibSource::ZLIB:
29  windowBitsFormat = 0;
30  break;
31  }
32  return inflateInit2(zcontext, /* windowBits */ 15 | windowBitsFormat);
33 }
34 
35 static inline void InitCtx(z_stream* zcontext) {
36  memset(zcontext, 0, sizeof(z_stream));
37 }
38 
39 bool ZlibSource::IsZlibSource(Source* source) {
40  std::array<unsigned char, 2> buf;
41  auto res = source->Read(strings::MutableByteRange(buf));
42  if (!res.ok())
43  return false;
44 
45  bool is_zlib = res.obj == 2 && (buf[0] == 0x1f) && (buf[1] == 0x8b);
46  source->Prepend(strings::ByteRange(buf));
47 
48  return is_zlib;
49 }
50 
51 static constexpr size_t kBufSize = 8192;
52 
53 ZlibSource::ZlibSource(Source* sub_stream, Format format)
54  : sub_stream_(sub_stream), format_(format) {
55  InitCtx(&zcontext_);
56  buf_.reset(new uint8_t[kBufSize]);
57 }
58 
59 ZlibSource::~ZlibSource() {
60  inflateEnd(&zcontext_); // might return error because zcontext_ might already be freed.
61  delete sub_stream_;
62 }
63 
64 StatusObject<size_t> ZlibSource::ReadInternal(const strings::MutableByteRange& range) {
65  zcontext_.next_out = range.begin();
66  zcontext_.avail_out = range.size();
67 
68  while (true) {
69  if (zcontext_.avail_in > 0) {
70  int zerror = inflate(&zcontext_, Z_NO_FLUSH);
71 
72  if (zerror != Z_OK) {
73  if (zerror == Z_STREAM_END) {
74  // There may be multiple zlib-streams and inflate stops when it encounters
75  // Z_STREAM_END before all the requested data is inflated.
76  CHECK_EQ(0, inflateEnd(&zcontext_));
77  if (zcontext_.avail_in) {
78  int reset = internalInflateInit2(format_, &zcontext_);
79  CHECK_EQ(Z_OK, reset);
80  }
81  continue;
82  }
83 
84  return ToStatus(zerror, zcontext_.msg);
85  }
86 
87  if (zcontext_.next_out == range.end())
88  break;
89 
90  DCHECK_EQ(0, zcontext_.avail_in);
91  }
92 
93  auto res = sub_stream_->Read(strings::MutableByteRange(buf_.get(), kBufSize));
94  if (!res.ok())
95  return res;
96 
97  if (res.obj == 0)
98  break;
99 
100  DVLOG(1) << "Read " << res.obj << " bytes";
101 
102  zcontext_.next_in = buf_.get();
103  zcontext_.avail_in = res.obj;
104  if (!zcontext_.state) {
105  int reset = internalInflateInit2(format_, &zcontext_);
106  CHECK_EQ(Z_OK, reset);
107  }
108  }
109 
110  if (zcontext_.avail_in > 0) {
111  CHECK_EQ(0, zcontext_.avail_out);
112  }
113 
114  return zcontext_.next_out - range.begin();
115 }
116 
117 ZlibSink::ZlibSink(Sink* sub, unsigned level, size_t buf_size)
118  : sub_(sub), buf_(new uint8_t[buf_size]), buf_size_(buf_size) {
119  InitCtx(&zcontext_);
120 
121  int lev = level == 0 ? Z_DEFAULT_COMPRESSION : level;
122  int zerror = deflateInit2(&zcontext_, lev, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
123  CHECK_EQ(Z_OK, zerror);
124 
125  zcontext_.next_out = buf_.get();
126  zcontext_.avail_out = buf_size_;
127 }
128 
129 ZlibSink::~ZlibSink() { deflateEnd(&zcontext_); }
130 
131 Status ZlibSink::Append(const strings::ByteRange& slice) {
132  zcontext_.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(slice.data()));
133  zcontext_.avail_in = slice.size();
134 
135  int zerror = deflate(&zcontext_, Z_NO_FLUSH);
136  if (zerror != Z_OK)
137  return ToStatus(zerror, zcontext_.msg);
138 
139  while (zcontext_.avail_out == 0) {
140  strings::ByteRange br(buf_.get(), buf_size_);
141  RETURN_IF_ERROR(sub_->Append(br));
142 
143  zcontext_.next_out = buf_.get();
144  zcontext_.avail_out = buf_size_;
145 
146  int zerror = deflate(&zcontext_, Z_NO_FLUSH);
147  if (zerror != Z_OK)
148  return ToStatus(zerror, zcontext_.msg);
149  }
150  CHECK_EQ(0, zcontext_.avail_in);
151 
152  return Status::OK;
153 }
154 
155 Status ZlibSink::Flush() {
156  while (true) {
157  int zerror = deflate(&zcontext_, Z_FINISH);
158  if (zerror == Z_STREAM_END)
159  break;
160  if (zerror != Z_OK && zerror != Z_BUF_ERROR) {
161  return ToStatus(zerror, zcontext_.msg);
162  }
163 
164  RETURN_IF_ERROR(sub_->Append(strings::ByteRange{buf_.get(), zcontext_.next_out}));
165  zcontext_.next_out = buf_.get();
166  zcontext_.avail_out = buf_size_;
167  }
168  RETURN_IF_ERROR(sub_->Append(strings::ByteRange{buf_.get(), zcontext_.next_out}));
169 
170  return sub_->Flush();
171 }
172 
173 } // namespace util