OpenShot Audio Library | OpenShotAudio  0.3.2
juce_GZIPDecompressorInputStream.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  The code included in this file is provided under the terms of the ISC license
11  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12  To use, copy, modify, and/or distribute this software for any purpose with or
13  without fee is hereby granted provided that the above copyright notice and
14  this permission notice appear in all copies.
15 
16  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18  DISCLAIMED.
19 
20  ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 #if JUCE_MSVC
27  #pragma warning (push)
28  #pragma warning (disable: 4309 4305 4365)
29 #endif
30 
31 namespace zlibNamespace
32 {
33  #if JUCE_INCLUDE_ZLIB_CODE
34  #if JUCE_CLANG
35  #pragma clang diagnostic push
36  #pragma clang diagnostic ignored "-Wconversion"
37  #pragma clang diagnostic ignored "-Wshadow"
38  #pragma clang diagnostic ignored "-Wdeprecated-register"
39  #if __has_warning("-Wzero-as-null-pointer-constant")
40  #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
41  #endif
42  #if __has_warning("-Wcomma")
43  #pragma clang diagnostic ignored "-Wcomma"
44  #endif
45  #endif
46 
47  #if JUCE_GCC
48  #pragma GCC diagnostic push
49  #pragma GCC diagnostic ignored "-Wconversion"
50  #pragma GCC diagnostic ignored "-Wsign-conversion"
51  #pragma GCC diagnostic ignored "-Wshadow"
52  #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
53  #endif
54 
55  #undef OS_CODE
56  #undef fdopen
57  #define ZLIB_INTERNAL
58  #define NO_DUMMY_DECL
59  #include "zlib/zlib.h"
60  #include "zlib/adler32.c"
61  #include "zlib/compress.c"
62  #undef DO1
63  #undef DO8
64  #include "zlib/crc32.c"
65  #include "zlib/deflate.c"
66  #include "zlib/inffast.c"
67  #undef PULLBYTE
68  #undef LOAD
69  #undef RESTORE
70  #undef INITBITS
71  #undef NEEDBITS
72  #undef DROPBITS
73  #undef BYTEBITS
74  #include "zlib/inflate.c"
75  #include "zlib/inftrees.c"
76  #include "zlib/trees.c"
77  #include "zlib/zutil.c"
78  #undef Byte
79  #undef fdopen
80  #undef local
81  #undef Freq
82  #undef Code
83  #undef Dad
84  #undef Len
85 
86  #if JUCE_CLANG
87  #pragma clang diagnostic pop
88  #endif
89 
90  #if JUCE_GCC
91  #pragma GCC diagnostic pop
92  #endif
93  #else
94  #include JUCE_ZLIB_INCLUDE_PATH
95 
96  #ifndef z_uInt
97  #ifdef uInt
98  #define z_uInt uInt
99  #else
100  #define z_uInt unsigned int
101  #endif
102  #endif
103 
104  #endif
105 }
106 
107 #if JUCE_MSVC
108  #pragma warning (pop)
109 #endif
110 
111 //==============================================================================
112 // internal helper object that holds the zlib structures so they don't have to be
113 // included publicly.
114 class GZIPDecompressorInputStream::GZIPDecompressHelper
115 {
116 public:
117  GZIPDecompressHelper (Format f)
118  {
119  using namespace zlibNamespace;
120  zerostruct (stream);
121  streamIsValid = (inflateInit2 (&stream, getBitsForFormat (f)) == Z_OK);
122  finished = error = ! streamIsValid;
123  }
124 
125  ~GZIPDecompressHelper()
126  {
127  if (streamIsValid)
128  zlibNamespace::inflateEnd (&stream);
129  }
130 
131  bool needsInput() const noexcept { return dataSize <= 0; }
132 
133  void setInput (uint8* const data_, const size_t size) noexcept
134  {
135  data = data_;
136  dataSize = size;
137  }
138 
139  int doNextBlock (uint8* const dest, const unsigned int destSize)
140  {
141  using namespace zlibNamespace;
142 
143  if (streamIsValid && data != nullptr && ! finished)
144  {
145  stream.next_in = data;
146  stream.next_out = dest;
147  stream.avail_in = (z_uInt) dataSize;
148  stream.avail_out = (z_uInt) destSize;
149 
150  switch (inflate (&stream, Z_PARTIAL_FLUSH))
151  {
152  case Z_STREAM_END:
153  finished = true;
154  // deliberate fall-through
155  case Z_OK:
156  data += dataSize - stream.avail_in;
157  dataSize = (z_uInt) stream.avail_in;
158  return (int) (destSize - stream.avail_out);
159 
160  case Z_NEED_DICT:
161  needsDictionary = true;
162  data += dataSize - stream.avail_in;
163  dataSize = (size_t) stream.avail_in;
164  break;
165 
166  case Z_DATA_ERROR:
167  case Z_MEM_ERROR:
168  error = true;
169 
170  default:
171  break;
172  }
173  }
174 
175  return 0;
176  }
177 
178  static int getBitsForFormat (Format f) noexcept
179  {
180  switch (f)
181  {
182  case zlibFormat: return MAX_WBITS;
183  case deflateFormat: return -MAX_WBITS;
184  case gzipFormat: return MAX_WBITS | 16;
185  default: jassertfalse; break;
186  }
187 
188  return MAX_WBITS;
189  }
190 
191  bool finished = true, needsDictionary = false, error = true, streamIsValid = false;
192 
193  enum { gzipDecompBufferSize = 32768 };
194 
195 private:
196  zlibNamespace::z_stream stream;
197  uint8* data = nullptr;
198  size_t dataSize = 0;
199 
200  JUCE_DECLARE_NON_COPYABLE (GZIPDecompressHelper)
201 };
202 
203 //==============================================================================
205  Format f, int64 uncompressedLength)
206  : sourceStream (source, deleteSourceWhenDestroyed),
207  uncompressedStreamLength (uncompressedLength),
208  format (f),
209  originalSourcePos (source->getPosition()),
210  buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize),
211  helper (new GZIPDecompressHelper (f))
212 {
213 }
214 
216  : sourceStream (&source, false),
217  uncompressedStreamLength (-1),
218  format (zlibFormat),
219  originalSourcePos (source.getPosition()),
220  buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize),
221  helper (new GZIPDecompressHelper (zlibFormat))
222 {
223 }
224 
226 {
227 }
228 
230 {
231  return uncompressedStreamLength;
232 }
233 
234 int GZIPDecompressorInputStream::read (void* destBuffer, int howMany)
235 {
236  jassert (destBuffer != nullptr && howMany >= 0);
237 
238  if (howMany > 0 && ! isEof)
239  {
240  int numRead = 0;
241  auto d = static_cast<uint8*> (destBuffer);
242 
243  while (! helper->error)
244  {
245  auto n = helper->doNextBlock (d, (unsigned int) howMany);
246  currentPos += n;
247 
248  if (n == 0)
249  {
250  if (helper->finished || helper->needsDictionary)
251  {
252  isEof = true;
253  return numRead;
254  }
255 
256  if (helper->needsInput())
257  {
258  activeBufferSize = sourceStream->read (buffer, (int) GZIPDecompressHelper::gzipDecompBufferSize);
259 
260  if (activeBufferSize > 0)
261  {
262  helper->setInput (buffer, (size_t) activeBufferSize);
263  }
264  else
265  {
266  isEof = true;
267  return numRead;
268  }
269  }
270  }
271  else
272  {
273  numRead += n;
274  howMany -= n;
275  d += n;
276 
277  if (howMany <= 0)
278  return numRead;
279  }
280  }
281  }
282 
283  return 0;
284 }
285 
287 {
288  return helper->error || helper->finished || isEof;
289 }
290 
292 {
293  return currentPos;
294 }
295 
297 {
298  if (newPos < currentPos)
299  {
300  // to go backwards, reset the stream and start again..
301  isEof = false;
302  activeBufferSize = 0;
303  currentPos = 0;
304  helper.reset (new GZIPDecompressHelper (format));
305 
306  sourceStream->setPosition (originalSourcePos);
307  }
308 
309  skipNextBytes (newPos - currentPos);
310  return true;
311 }
312 
313 
314 //==============================================================================
315 //==============================================================================
316 #if JUCE_UNIT_TESTS
317 
318 struct GZIPDecompressorInputStreamTests : public UnitTest
319 {
320  GZIPDecompressorInputStreamTests()
321  : UnitTest ("GZIPDecompressorInputStreamTests", UnitTestCategories::streams)
322  {}
323 
324  void runTest() override
325  {
326  const MemoryBlock data ("abcdefghijklmnopqrstuvwxyz", 26);
327 
328  MemoryOutputStream mo;
329  GZIPCompressorOutputStream gzipOutputStream (mo);
330  gzipOutputStream.write (data.getData(), data.getSize());
331  gzipOutputStream.flush();
332 
333  MemoryInputStream mi (mo.getData(), mo.getDataSize(), false);
334  GZIPDecompressorInputStream stream (&mi, false, GZIPDecompressorInputStream::zlibFormat, (int64) data.getSize());
335 
336  beginTest ("Read");
337 
338  expectEquals (stream.getPosition(), (int64) 0);
339  expectEquals (stream.getTotalLength(), (int64) data.getSize());
340  expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
341  expect (! stream.isExhausted());
342 
343  size_t numBytesRead = 0;
344  MemoryBlock readBuffer (data.getSize());
345 
346  while (numBytesRead < data.getSize())
347  {
348  numBytesRead += (size_t) stream.read (&readBuffer[numBytesRead], 3);
349 
350  expectEquals (stream.getPosition(), (int64) numBytesRead);
351  expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
352  expect (stream.isExhausted() == (numBytesRead == data.getSize()));
353  }
354 
355  expectEquals (stream.getPosition(), (int64) data.getSize());
356  expectEquals (stream.getNumBytesRemaining(), (int64) 0);
357  expect (stream.isExhausted());
358 
359  expect (readBuffer == data);
360 
361  beginTest ("Skip");
362 
363  stream.setPosition (0);
364  expectEquals (stream.getPosition(), (int64) 0);
365  expectEquals (stream.getTotalLength(), (int64) data.getSize());
366  expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
367  expect (! stream.isExhausted());
368 
369  numBytesRead = 0;
370  const int numBytesToSkip = 5;
371 
372  while (numBytesRead < data.getSize())
373  {
374  stream.skipNextBytes (numBytesToSkip);
375  numBytesRead += numBytesToSkip;
376  numBytesRead = std::min (numBytesRead, data.getSize());
377 
378  expectEquals (stream.getPosition(), (int64) numBytesRead);
379  expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
380  expect (stream.isExhausted() == (numBytesRead == data.getSize()));
381  }
382 
383  expectEquals (stream.getPosition(), (int64) data.getSize());
384  expectEquals (stream.getNumBytesRemaining(), (int64) 0);
385  expect (stream.isExhausted());
386  }
387 };
388 
389 static GZIPDecompressorInputStreamTests gzipDecompressorInputStreamTests;
390 
391 #endif
392 
393 } // namespace juce
int read(void *destBuffer, int maxBytesToRead) override
GZIPDecompressorInputStream(InputStream *sourceStream, bool deleteSourceWhenDestroyed, Format sourceFormat=zlibFormat, int64 uncompressedStreamLength=-1)
virtual void skipNextBytes(int64 numBytesToSkip)