1 /** @file block.c Byte array with copy-on-write semantics.
2
3 @authors Copyright (c) 2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4
5 @par License
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 1. Redistributions of source code must retain the above copyright notice, this
11 list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 <small>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</small>
26 */
27
28 #include "the_Foundation/block.h"
29 #include "the_Foundation/atomic.h"
30 #include "the_Foundation/garbage.h"
31 #include "the_Foundation/string.h"
32 #include "the_Foundation/stream.h"
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #include <strings.h>
38 #include <uniconv.h>
39 #if defined (iHaveZlib)
40 # include <zlib.h>
41 #endif
42
43 /// @todo Needs a ref-counting mutex.
44 static iBlockData emptyBlockData = {
45 .refCount = 1,
46 .data = "",
47 .size = 0,
48 .allocSize = 1,
49 };
50
new_BlockData_(size_t size,size_t allocSize)51 static iBlockData *new_BlockData_(size_t size, size_t allocSize) {
52 iBlockData *d = iMalloc(BlockData);
53 set_Atomic(&d->refCount, 1);
54 d->size = size;
55 d->allocSize = iMax(size + 1, allocSize);
56 d->data = malloc(d->allocSize);
57 return d;
58 }
59
newPrealloc_BlockData_(void * data,size_t size,size_t allocSize)60 static iBlockData *newPrealloc_BlockData_(void *data, size_t size, size_t allocSize) {
61 iBlockData *d = iMalloc(BlockData);
62 set_Atomic(&d->refCount, 1);
63 d->size = size;
64 d->allocSize = allocSize;
65 d->data = data;
66 return d;
67 }
68
duplicate_BlockData_(const iBlockData * d,size_t allocSize)69 static iBlockData *duplicate_BlockData_(const iBlockData *d, size_t allocSize) {
70 iBlockData *dupl = new_BlockData_(d->size, allocSize);
71 memcpy(dupl->data, d->data, iMin(d->allocSize, dupl->size + 1));
72 return dupl;
73 }
74
deref_BlockData_(iBlockData * d)75 static void deref_BlockData_(iBlockData *d) {
76 const int refWas = addRelaxed_Atomic(&d->refCount, -1);
77 if (refWas == 1) {
78 iAssert(d != &emptyBlockData);
79 free(d->data);
80 free(d);
81 }
82 }
83
reserve_BlockData_(iBlockData * d,size_t size)84 static void reserve_BlockData_(iBlockData *d, size_t size) {
85 size++;
86 if (d->allocSize >= size) return;
87 iAssert(value_Atomic(&d->refCount) == 1);
88 iAssert(d->allocSize > 0);
89 /* Reserve increased amount of memory in powers-of-two. */
90 for (d->allocSize = 8; d->allocSize < size; d->allocSize <<= 1) {}
91 d->data = realloc(d->data, d->allocSize);
92 }
93
memcpyFrom_Block_(iBlock * d,const void * data,size_t size)94 static void memcpyFrom_Block_(iBlock *d, const void *data, size_t size) {
95 if (size) {
96 memcpy(d->i->data, data, size);
97 d->i->data[size] = 0;
98 d->i->size = size;
99 }
100 }
101
detach_Block_(iBlock * d,size_t allocSize)102 static void detach_Block_(iBlock *d, size_t allocSize) {
103 if (value_Atomic(&d->i->refCount) > 1) {
104 iBlockData *detached = duplicate_BlockData_(d->i, allocSize);
105 deref_BlockData_(d->i);
106 d->i = detached;
107 }
108 iAssert(value_Atomic(&d->i->refCount) == 1);
109 }
110
111 /*-------------------------------------------------------------------------------------*/
112
113 iDefineTypeConstructionArgs(Block, (size_t size), size)
114
newCStr_Block(const char * cstr)115 iBlock *newCStr_Block(const char *cstr) {
116 iBlock *d = new_Block(strlen(cstr));
117 memcpyFrom_Block_(d, cstr, d->i->size);
118 return d;
119 }
120
newData_Block(const void * data,size_t size)121 iBlock *newData_Block(const void *data, size_t size) {
122 iBlock *d = new_Block(size);
123 memcpyFrom_Block_(d, data, size);
124 return d;
125 }
126
newPrealloc_Block(void * data,size_t size,size_t allocSize)127 iBlock *newPrealloc_Block(void *data, size_t size, size_t allocSize) {
128 iBlock *d = iMalloc(Block);
129 initPrealloc_Block(d, data, size, allocSize);
130 return d;
131 }
132
copy_Block(const iBlock * d)133 iBlock *copy_Block(const iBlock *d) {
134 if (d) {
135 iBlock *dupl = malloc(sizeof(iBlock));
136 initCopy_Block(dupl, d);
137 return dupl;
138 }
139 return NULL;
140 }
141
init_Block(iBlock * d,size_t size)142 void init_Block(iBlock *d, size_t size) {
143 if (size == 0) {
144 d->i = &emptyBlockData;
145 addRelaxed_Atomic(&emptyBlockData.refCount, 1);
146 }
147 else {
148 d->i = new_BlockData_(size, 0);
149 }
150 }
151
initData_Block(iBlock * d,const void * data,size_t size)152 void initData_Block(iBlock *d, const void *data, size_t size) {
153 if (size > 0) {
154 d->i = new_BlockData_(size, 0);
155 memcpyFrom_Block_(d, data, size);
156 }
157 else {
158 init_Block(d, size);
159 }
160 }
161
initCStr_Block(iBlock * d,const char * cstr)162 void initCStr_Block(iBlock *d, const char *cstr) {
163 initData_Block(d, cstr, cstr ? strlen(cstr) : 0);
164 }
165
initPrealloc_Block(iBlock * d,void * data,size_t size,size_t allocSize)166 void initPrealloc_Block(iBlock *d, void *data, size_t size, size_t allocSize) {
167 d->i = newPrealloc_BlockData_(data, size, allocSize);
168 }
169
initCopy_Block(iBlock * d,const iBlock * other)170 void initCopy_Block(iBlock *d, const iBlock *other) {
171 if (other) {
172 addRelaxed_Atomic(&other->i->refCount, 1);
173 d->i = other->i;
174 }
175 else {
176 init_Block(d, 0);
177 }
178 }
179
deinit_Block(iBlock * d)180 void deinit_Block(iBlock *d) {
181 deref_BlockData_(d->i);
182 }
183
serialize_Block(const iBlock * d,iStream * outs)184 void serialize_Block(const iBlock *d, iStream *outs) {
185 writeU32_Stream(outs, (uint32_t) d->i->size);
186 if (d->i->size) {
187 writeData_Stream(outs, d->i->data, d->i->size);
188 }
189 }
190
deserialize_Block(iBlock * d,iStream * ins)191 void deserialize_Block(iBlock *d, iStream *ins) {
192 clear_Block(d);
193 const size_t len = readU32_Stream(ins);
194 if (len) {
195 resize_Block(d, len);
196 readData_Stream(ins, len, d->i->data);
197 }
198 }
199
size_Block(const iBlock * d)200 size_t size_Block(const iBlock *d) {
201 return d->i->size;
202 }
203
at_Block(const iBlock * d,size_t pos)204 char at_Block(const iBlock *d, size_t pos) {
205 iAssert(pos < d->i->size);
206 return d->i->data[pos];
207 }
208
front_Block(const iBlock * d)209 char front_Block(const iBlock *d) {
210 return d->i->data[0];
211 }
212
back_Block(const iBlock * d)213 char back_Block(const iBlock *d) {
214 return d->i->data[d->i->size - 1];
215 }
216
constData_Block(const iBlock * d)217 const void *constData_Block(const iBlock *d) {
218 return d->i->data;
219 }
220
constBegin_Block(const iBlock * d)221 const char *constBegin_Block(const iBlock *d) {
222 return d->i->data;
223 }
224
constEnd_Block(const iBlock * d)225 const char *constEnd_Block(const iBlock *d) {
226 return d->i->data + d->i->size;
227 }
228
mid_Block(const iBlock * d,size_t start,size_t count)229 iBlock *mid_Block(const iBlock *d, size_t start, size_t count) {
230 if (start >= d->i->size) {
231 return new_Block(0);
232 }
233 const size_t midSize = iMin(count, d->i->size - start);
234 iBlock *mid = new_Block(midSize);
235 memcpyFrom_Block_(mid, d->i->data + start, midSize);
236 return mid;
237 }
238
data_Block(iBlock * d)239 void *data_Block(iBlock *d) {
240 detach_Block_(d, 0);
241 return d->i->data;
242 }
243
clear_Block(iBlock * d)244 void clear_Block(iBlock *d) {
245 deref_BlockData_(d->i);
246 d->i = &emptyBlockData;
247 addRelaxed_Atomic(&emptyBlockData.refCount, 1);
248 }
249
reserve_Block(iBlock * d,size_t reservedSize)250 void reserve_Block(iBlock *d, size_t reservedSize) {
251 detach_Block_(d, reservedSize + 1);
252 reserve_BlockData_(d->i, reservedSize);
253 }
254
resize_Block(iBlock * d,size_t size)255 void resize_Block(iBlock *d, size_t size) {
256 if (size < size_Block(d)) {
257 truncate_Block(d, size);
258 return;
259 }
260 reserve_Block(d, size);
261 const size_t oldSize = d->i->size;
262 d->i->size = size;
263 memset(d->i->data + oldSize, 0, d->i->size - oldSize + 1);
264 }
265
truncate_Block(iBlock * d,size_t size)266 void truncate_Block(iBlock *d, size_t size) {
267 if (size < size_Block(d)) {
268 detach_Block_(d, 0);
269 d->i->size = iMin(d->i->size, size); // note: allocated size does not change
270 d->i->data[d->i->size] = 0;
271 }
272 }
273
remove_Block(iBlock * d,size_t start,size_t count)274 void remove_Block(iBlock *d, size_t start, size_t count) {
275 detach_Block_(d, 0);
276 iAssert(start <= d->i->size);
277 if (count == iInvalidSize || start + count > d->i->size) {
278 count = d->i->size - start;
279 }
280 const size_t remainder = d->i->size - start - count;
281 if (remainder > 0) {
282 memmove(d->i->data + start, d->i->data + start + count, remainder);
283 }
284 d->i->size -= count;
285 d->i->data[d->i->size] = 0;
286 }
287
printf_Block(iBlock * d,const char * format,...)288 void printf_Block(iBlock *d, const char *format, ...) {
289 va_list args;
290 va_start(args, format);
291 vprintf_Block(d, format, args);
292 va_end(args);
293 }
294
vprintf_Block(iBlock * d,const char * format,va_list args)295 void vprintf_Block(iBlock *d, const char *format, va_list args) {
296 va_list args2;
297 va_copy(args2, args);
298 const int len = vsnprintf(NULL, 0, format, args);
299 reserve_Block(d, len);
300 vsprintf(d->i->data, format, args2);
301 d->i->size = len;
302 va_end(args2);
303 }
304
fill_Block(iBlock * d,char value)305 void fill_Block(iBlock *d, char value) {
306 detach_Block_(d, 0);
307 memset(d->i->data, value, d->i->size);
308 d->i->data[d->i->size] = 0;
309 }
310
pushBack_Block(iBlock * d,char value)311 void pushBack_Block(iBlock *d, char value) {
312 reserve_Block(d, d->i->size + 1);
313 d->i->data[d->i->size++] = value;
314 d->i->data[d->i->size] = 0;
315 }
316
popBack_Block(iBlock * d)317 void popBack_Block(iBlock *d) {
318 detach_Block_(d, 0);
319 if (d->i->size > 0) {
320 d->i->data[--d->i->size] = 0;
321 }
322 }
323
set_Block(iBlock * d,const iBlock * other)324 void set_Block(iBlock *d, const iBlock *other) {
325 if (d != other) {
326 addRelaxed_Atomic(&other->i->refCount, 1);
327 deref_BlockData_(d->i);
328 d->i = other->i;
329 }
330 }
331
setByte_Block(iBlock * d,size_t pos,char value)332 void setByte_Block(iBlock *d, size_t pos, char value) {
333 detach_Block_(d, 0);
334 iAssert(pos < d->i->size);
335 d->i->data[pos] = value;
336 }
337
setData_Block(iBlock * d,const void * data,size_t size)338 void setData_Block(iBlock *d, const void *data, size_t size) {
339 if (size) {
340 reserve_Block(d, size);
341 memcpyFrom_Block_(d, data, size);
342 }
343 else {
344 clear_Block(d);
345 }
346 }
347
setSubData_Block(iBlock * d,size_t pos,const void * data,size_t size)348 void setSubData_Block(iBlock *d, size_t pos, const void *data, size_t size) {
349 reserve_Block(d, pos + size);
350 iAssert(pos <= d->i->size);
351 memcpy(d->i->data + pos, data, size);
352 d->i->size = iMax(d->i->size, pos + size);
353 if (d->i->size == pos + size) {
354 d->i->data[d->i->size] = 0;
355 }
356 }
357
setCStr_Block(iBlock * d,const char * cstr)358 void setCStr_Block(iBlock *d, const char *cstr) {
359 setData_Block(d, cstr, strlen(cstr));
360 }
361
append_Block(iBlock * d,const iBlock * other)362 void append_Block(iBlock *d, const iBlock *other) {
363 appendData_Block(d, other->i->data, other->i->size);
364 }
365
appendData_Block(iBlock * d,const void * data,size_t size)366 void appendData_Block(iBlock *d, const void *data, size_t size) {
367 reserve_Block(d, d->i->size + size);
368 memcpy(d->i->data + d->i->size, data, size);
369 d->i->size += size;
370 d->i->data[d->i->size] = 0;
371 }
372
appendCStr_Block(iBlock * d,const char * cstr)373 void appendCStr_Block(iBlock *d, const char *cstr) {
374 appendData_Block(d, cstr, strlen(cstr));
375 }
376
insertData_Block(iBlock * d,size_t insertAt,const void * data,size_t size)377 void insertData_Block(iBlock *d, size_t insertAt, const void *data, size_t size) {
378 reserve_Block(d, d->i->size + size);
379 char *start = d->i->data + insertAt;
380 memmove(start + size, start, d->i->size - insertAt);
381 memcpy (start, data, size);
382 d->i->size += size;
383 d->i->data[d->i->size] = 0;
384 }
385
concat_Block(const iBlock * d,const iBlock * other)386 iBlock *concat_Block(const iBlock *d, const iBlock *other) {
387 iBlock *cat = new_Block(d->i->size + other->i->size);
388 memcpy(cat->i->data, d->i->data, d->i->size);
389 memcpy(cat->i->data + d->i->size, other->i->data, other->i->size);
390 cat->i->data[cat->i->size] = 0;
391 return cat;
392 }
393
cmp_Block(const iBlock * d,const iBlock * other)394 int cmp_Block(const iBlock *d, const iBlock *other) {
395 return cmpData_Block(d, other->i->data, other->i->size);
396 }
397
cmpCase_Block(const iBlock * d,const iBlock * other)398 int cmpCase_Block(const iBlock *d, const iBlock *other) {
399 return iCmpStrCase(d->i->data, other->i->data);
400 }
401
cmpCaseN_Block(const iBlock * d,const iBlock * other,size_t size)402 int cmpCaseN_Block(const iBlock *d, const iBlock *other, size_t size) {
403 return iCmpStrNCase(d->i->data, other->i->data, size);
404 }
405
cmpData_Block(const iBlock * d,const char * data,size_t size)406 int cmpData_Block(const iBlock *d, const char *data, size_t size) {
407 return memcmp(d->i->data, data, iMin(size, d->i->size));
408 }
409
cmpCStr_Block(const iBlock * d,const char * cstr)410 int cmpCStr_Block(const iBlock *d, const char *cstr) {
411 return iCmpStr(d->i->data, cstr);
412 }
413
cmpCStrN_Block(const iBlock * d,const char * cstr,size_t len)414 int cmpCStrN_Block(const iBlock *d, const char *cstr, size_t len) {
415 return iCmpStrN(d->i->data, cstr, len);
416 }
417
cmpCaseCStr_Block(const iBlock * d,const char * cstr)418 int cmpCaseCStr_Block(const iBlock *d, const char *cstr) {
419 return iCmpStrCase(d->i->data, cstr);
420 }
421
cmpCaseCStrN_Block(const iBlock * d,const char * cstr,size_t len)422 int cmpCaseCStrN_Block(const iBlock *d, const char *cstr, size_t len) {
423 return iCmpStrNCase(d->i->data, cstr, len);
424 }
425
crc32_Block(const iBlock * d)426 uint32_t crc32_Block(const iBlock *d) {
427 return iCrc32(d->i->data, d->i->size);
428 }
429
md5_Block(const iBlock * d,uint8_t md5_out[16])430 void md5_Block(const iBlock *d, uint8_t md5_out[16]) {
431 iMd5Hash(d->i->data, d->i->size, md5_out);
432 }
433
decode_Block(const iBlock * d,const char * textEncoding)434 iString *decode_Block(const iBlock *d, const char *textEncoding) {
435 size_t len = 0;
436 uint8_t *data = u8_conv_from_encoding(textEncoding,
437 iconveh_question_mark,
438 constData_Block(d),
439 size_Block(d),
440 NULL,
441 NULL,
442 &len);
443 data = realloc(data, len + 1);
444 data[len] = 0;
445 iString *str = iMalloc(String);
446 initPrealloc_Block(&str->chars, data, len, len + 1);
447 return str;
448 }
449
hexEncode_Block(const iBlock * d)450 iString *hexEncode_Block(const iBlock *d) {
451 static const char hexValues[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
452 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
453 iString *hex = new_String();
454 for (const char *i = constBegin_Block(d), *end = constEnd_Block(d); i != end; i++) {
455 const uint8_t val = *i;
456 appendChar_String(hex, hexValues[val >> 4]);
457 appendChar_String(hex, hexValues[val & 15]);
458 }
459 return hex;
460 }
461
fromHex_(char ch)462 static int fromHex_(char ch) {
463 if (ch >= 'a') return ch - 'a' + 10;
464 if (ch >= 'A') return ch - 'A' + 10;
465 return ch - '0';
466 }
467
hexDecode_Rangecc(iRangecc range)468 iBlock *hexDecode_Rangecc(iRangecc range) {
469 iBlock *d = new_Block(size_Range(&range) / 2);
470 size_t pos = 0;
471 for (const char *i = range.start; i < range.end; i += 2) {
472 const uint8_t val = (fromHex_(i[0]) << 4) | fromHex_(i[1]);
473 setByte_Block(d, pos++, val);
474 }
475 return d;
476 }
477
base64Index_(char ch)478 iLocalDef uint8_t base64Index_(char ch) {
479 /* TODO: Replace this with a lookup table. */
480 if (ch == '=') return 0; /* padding */
481 if (ch == '/') return 63;
482 if (ch == '+') return 62;
483 if (ch >= 'a') return ch - 'a' + 26;
484 if (ch >= 'A') return ch - 'A';
485 return ch - '0' + 52;
486 }
487
base64Decode_Block(const iBlock * d)488 iBlock *base64Decode_Block(const iBlock *d) {
489 const size_t outSize = 6 * size_Block(d) / 8;
490 iBlock * decoded = new_Block(outSize);
491 uint16_t comp = 0;
492 int shift = 10;
493 // |-------|-------| comp
494 // 7654321076543210
495 // ^10 shift
496 // aaaaaa
497 // ^4 shift
498 // aaaaaabbbbbb
499 uint8_t *out = data_Block(decoded);
500 for (const char *pos = constBegin_Block(d), *end = constEnd_Block(d); pos != end; pos++) {
501 comp |= base64Index_(*pos) << shift;
502 if (shift <= 8) {
503 *out++ = comp >> 8;
504 comp <<= 8;
505 shift += 8;
506 }
507 shift -= 6;
508 }
509 iAssert((size_t) (out - (uint8_t *) data_Block(decoded)) == outSize); /* all written */
510 return decoded;
511 }
512
replace_Block(iBlock * d,char oldValue,char newValue)513 size_t replace_Block(iBlock *d, char oldValue, char newValue) {
514 size_t count = 0;
515 detach_Block_(d, 0);
516 for (char *i = d->i->data, *end = d->i->data + d->i->size; i != end; ++i) {
517 if (*i == oldValue) {
518 *i = newValue;
519 count++;
520 }
521 }
522 return count;
523 }
524
525 /*-------------------------------------------------------------------------------------*/
526 #if defined (iHaveZlib)
527
528 iDeclareType(ZStream)
529
530 struct Impl_ZStream {
531 z_stream stream;
532 iBlock *out;
533 };
534
init_ZStream_(iZStream * d,const iBlock * in,iBlock * out)535 static void init_ZStream_(iZStream *d, const iBlock *in, iBlock *out) {
536 d->out = out;
537 iZap(d->stream);
538 d->stream.avail_in = (uInt) in->i->size;
539 d->stream.next_in = (Bytef *) in->i->data;
540 d->stream.avail_out = (uInt) out->i->size;
541 d->stream.next_out = (Bytef *) out->i->data;
542 }
543
process_ZStream_(iZStream * d,int (* process)(z_streamp,int))544 static iBool process_ZStream_(iZStream *d, int (*process)(z_streamp, int)) {
545 int opts = Z_NO_FLUSH;
546 for (;;) {
547 int rc = process(&d->stream, opts);
548 if (rc == Z_STREAM_END) {
549 break;
550 }
551 else if (rc != Z_OK && rc != Z_BUF_ERROR) {
552 /* Something went wrong. */
553 return iFalse;
554 }
555 if (d->stream.avail_out == 0) {
556 /* Allocate more room. */
557 const size_t oldSize = size_Block(d->out);
558 resize_Block(d->out, oldSize * 2);
559 d->stream.next_out = (Bytef *) d->out->i->data + oldSize;
560 d->stream.avail_out = (uInt) (size_Block(d->out) - oldSize);
561 }
562 if (d->stream.avail_in == 0) {
563 opts = Z_FINISH;
564 }
565 }
566 truncate_Block(d->out, size_Block(d->out) - d->stream.avail_out);
567 return iTrue;
568 }
569
compressLevel_Block(const iBlock * d,int level)570 iBlock *compressLevel_Block(const iBlock *d, int level) {
571 iBlock *out = new_Block(1024);
572 iZStream z;
573 init_ZStream_(&z, d, out);
574 /*
575 * The deflation is done in raw mode. From zlib documentation:
576 *
577 * "windowBits can also be –8..–15 for raw deflate. In this case, -windowBits
578 * determines the window size. deflate() will then generate raw deflate data with no
579 * zlib header or trailer, and will not compute an adler32 check value."
580 */
581 if (deflateInit2(&z.stream, level, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY) == Z_OK) {
582 if (!process_ZStream_(&z, deflate)) {
583 clear_Block(out);
584 }
585 }
586 deflateEnd(&z.stream);
587 return out;
588 }
589
decompress_Block(const iBlock * d)590 iBlock *decompress_Block(const iBlock *d) {
591 iBlock *out = new_Block(1024);
592 iZStream z;
593 init_ZStream_(&z, d, out);
594 if (inflateInit2(&z.stream, -MAX_WBITS) == Z_OK) {
595 if (!process_ZStream_(&z, inflate)) {
596 clear_Block(out);
597 }
598 }
599 inflateEnd(&z.stream);
600 return out;
601 }
602
603 #endif // HaveZlib
604