forked from Tracktion/choc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
choc_zlib.h
3996 lines (3451 loc) · 154 KB
/
choc_zlib.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//
// ██████ ██ ██ ██████ ██████
// ██ ██ ██ ██ ██ ██ ** Classy Header-Only Classes **
// ██ ███████ ██ ██ ██
// ██ ██ ██ ██ ██ ██ https://github.com/Tracktion/choc
// ██████ ██ ██ ██████ ██████
//
// CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license:
//
// Permission to use, copy, modify, and/or distribute this software for any purpose with or
// without fee is hereby granted, provided that the above copyright notice and this permission
// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#ifndef CHOC_GZIPDECOMPRESS_HEADER_INCLUDED
#define CHOC_GZIPDECOMPRESS_HEADER_INCLUDED
#include <memory>
#include <iostream>
#include <streambuf>
#include <vector>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include "../platform/choc_Assert.h"
#include "../text/choc_OpenSourceLicenseList.h"
namespace choc::zlib
{
/// This std::istream decompresses data that was written with zlib or gzip.
/// @see DeflaterStream
class InflaterStream : public std::istream,
private std::streambuf
{
public:
enum class FormatType
{
zlib = 0,
deflate,
gzip
};
InflaterStream (std::shared_ptr<std::istream> compressedData,
FormatType);
~InflaterStream() override;
using pos_type = std::streambuf::pos_type;
using off_type = std::streambuf::off_type;
using char_type = std::streambuf::char_type;
private:
struct Pimpl;
std::unique_ptr<Pimpl> pimpl;
pos_type seekoff (off_type, std::ios_base::seekdir, std::ios_base::openmode) override;
pos_type seekpos (pos_type, std::ios_base::openmode) override;
std::streambuf::int_type underflow() override;
pos_type getPosition() const;
};
//==============================================================================
/// This std::ostream writes compressed data to a stream, which can be later
/// decompressed with the InflaterStream.
/// @see InflaterStream
class DeflaterStream : public std::ostream,
private std::streambuf
{
public:
enum CompressionLevel
{
none = 0,
fastest = 1,
medium = 6,
best = 9,
defaultLevel = -1
};
DeflaterStream (std::shared_ptr<std::ostream> destData,
CompressionLevel compressionLevel,
int windowBits);
~DeflaterStream() override;
using pos_type = std::streambuf::pos_type;
using off_type = std::streambuf::off_type;
using char_type = std::streambuf::char_type;
private:
struct Pimpl;
std::unique_ptr<Pimpl> pimpl;
int overflow (int) override;
};
//==============================================================================
// _ _ _ _
// __| | ___ | |_ __ _ (_)| | ___
// / _` | / _ \| __| / _` || || |/ __|
// | (_| || __/| |_ | (_| || || |\__ \ _ _ _
// \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_)
//
// Code beyond this point is implementation detail...
//
//==============================================================================
struct zlib
{
// This struct contains a very very mangled version of good old zlib,
// where I've purged almost all the macros and global definitions. Keeping
// all the symbols and nastiness locked inside this single struct means
// that it should play nice in a header-only world.
// I even started trying to improve some of the appalling variable names,
// but honestly, life's too short..
CHOC_REGISTER_OPEN_SOURCE_LICENCE (QuickJS, R"(
==============================================================================
ZLIB License:
Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jean-loup Gailly Mark Adler
The data format used by the zlib library is described by RFCs (Request for
Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
(zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
)")
static constexpr auto ZLIB_VERSION = "1.2.3";
static constexpr const char deflate_copyright[] = " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly ";
static constexpr const char inflate_copyright[] = " inflate 1.2.3 Copyright 1995-2005 Mark Adler ";
enum class ErrorCode : int
{
OK = 0,
STREAM_END = 1,
NEED_DICT = 2,
ERRNO = -1,
STREAM_ERROR = -2,
DATA_ERROR = -3,
MEM_ERROR = -4,
BUF_ERROR = -5,
VERSION_ERROR = -6
};
enum class DataType : uint32_t
{
Z_BINARY = 0,
Z_TEXT = 1,
Z_UNKNOWN = 2
};
static constexpr uint8_t Z_DEFLATED = 8;
static constexpr int MAX_WBITS = 15;
static constexpr int MAX_MEM_LEVEL = 9;
static constexpr int DEF_MEM_LEVEL = 8;
static constexpr int MIN_MATCH = 3;
static constexpr int MAX_MATCH = 258;
static constexpr int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;
enum class FlushState
{
Z_NO_FLUSH = 0,
Z_PARTIAL_FLUSH = 1, /* will be removed, use Z_SYNC_FLUSH instead */
Z_SYNC_FLUSH = 2,
Z_FULL_FLUSH = 3,
Z_FINISH = 4,
Z_BLOCK = 5,
unknown = -1
};
//==============================================================================
/*
gzip header information passed to and from zlib routines. See RFC 1952
for more details on the meanings of these fields.
*/
struct gz_header
{
int text; /* true if compressed data believed to be text */
uint64_t time; /* modification time */
int xflags; /* extra flags (not used when writing a gzip file) */
int os; /* operating system */
uint8_t* extra; /* pointer to extra field or nullptr if none */
uint32_t extra_len; /* extra field length (valid if extra != nullptr) */
uint32_t extra_max; /* space at extra (only when reading header) */
uint8_t* name; /* pointer to zero-terminated file name or nullptr */
uint32_t name_max; /* space at name (only when reading header) */
uint8_t* comment; /* pointer to zero-terminated comment or nullptr */
uint32_t comm_max; /* space at comment (only when reading header) */
int hcrc; /* true if there was or will be a header crc */
int done; /* true when done reading gzip header (not used
when writing a gzip file) */
};
//==============================================================================
static constexpr uint32_t LENGTH_CODES = 29;
static constexpr uint32_t LITERALS = 256;
static constexpr uint32_t L_CODES = LITERALS + 1 + LENGTH_CODES;
static constexpr uint32_t D_CODES = 30;
static constexpr uint32_t BL_CODES = 19;
/* Data structure describing a single value and its code string. */
struct ct_data
{
union {
uint16_t freq; /* frequency count */
uint16_t code; /* bit string */
} fc;
union {
uint16_t dad; /* father node in Huffman tree */
uint16_t len; /* length of bit string */
} dl;
};
struct static_tree_desc
{
const ct_data* static_tree; /* static tree or nullptr */
const uint32_t* extra_bits; /* extra bits for each code or nullptr */
int extra_base; /* base index for extra_bits */
int elems; /* max number of elements in the tree */
uint32_t max_length; /* max bit length for the codes */
};
struct tree_desc
{
ct_data* dyn_tree; /* the dynamic tree */
uint32_t max_code; /* largest code with non zero frequency */
const static_tree_desc* stat_desc; /* the corresponding static tree */
};
struct code
{
uint8_t op; /* operation, extra bits, table bits */
uint8_t bits; /* bits in this part of the code */
uint16_t val; /* offset in table or code value */
};
//==============================================================================
struct DeflateStream
{
uint8_t* next_in = nullptr;
uint32_t avail_in = 0;
size_t total_in = 0;
uint8_t* next_out = nullptr;
uint32_t avail_out = 0;
size_t total_out = 0;
//==============================================================================
ErrorCode initialise (int compressionLevel, uint8_t methodToUse, int windowBits, int memLevel)
{
wrap = 1;
msg = nullptr;
if (windowBits < 0)
{
wrap = 0;
windowBits = -windowBits;
}
else if (windowBits > 15)
{
wrap = 2; /* write gzip wrapper instead */
windowBits -= 16;
}
if (memLevel < 1 || memLevel > MAX_MEM_LEVEL
|| methodToUse != Z_DEFLATED
|| windowBits < 8 || windowBits > 15
|| compressionLevel < 0 || compressionLevel > 9)
return ErrorCode::STREAM_ERROR;
if (windowBits == 8)
windowBits = 9; /* until 256-byte window bug fixed */
gzhead = nullptr;
w_bits = (uint32_t) windowBits;
w_size = 1U << w_bits;
w_mask = w_size - 1;
hash_bits = (uint32_t) memLevel + 7;
hash_size = 1U << hash_bits;
hash_mask = hash_size - 1;
hash_shift = ((hash_bits + MIN_MATCH - 1) / MIN_MATCH);
window.resize (w_size * 2);
prev.resize (w_size);
head.resize (hash_size);
lit_bufsize = 1U << (memLevel + 6); /* 16K elements by default */
pending_buf.resize (lit_bufsize * (sizeof (uint16_t) + 2));
if (window.empty() || prev.empty() || head.empty() || pending_buf.empty())
{
status = Status::FINISH_STATE;
setError (ErrorCode::MEM_ERROR);
return ErrorCode::MEM_ERROR;
}
d_buf = reinterpret_cast<uint16_t*> (pending_buf.data() + lit_bufsize);
l_buf = pending_buf.data() + (1 + sizeof (uint16_t)) * lit_bufsize;
level = compressionLevel;
method = methodToUse;
total_in = total_out = 0;
msg = nullptr;
data_type = DataType::Z_UNKNOWN;
pending = 0;
pending_out = pending_buf.data();
if (wrap < 0)
wrap = -wrap; /* was made negative by deflate(..., Z_FINISH); */
status = wrap ? Status::INIT_STATE : Status::BUSY_STATE;
adler = wrap == 2 ? Checksum::crc32 (0, nullptr, 0)
: Checksum::adler32 (0, nullptr, 0);
last_flush = FlushState::Z_NO_FLUSH;
initialiseTrees();
initLongestMatch();
return ErrorCode::OK;
}
ErrorCode deflateParams (int compressionLevel)
{
compress_func func;
auto err = ErrorCode::OK;
if (compressionLevel < 0 || compressionLevel > 9)
return ErrorCode::STREAM_ERROR;
func = compressionConfigs[level].func;
if (func != compressionConfigs[compressionLevel].func && total_in != 0)
err = deflate (FlushState::Z_PARTIAL_FLUSH);
if (level != compressionLevel)
{
level = compressionLevel;
max_lazy_match = compressionConfigs[compressionLevel].max_lazy;
good_match = compressionConfigs[compressionLevel].good_length;
nice_match = compressionConfigs[compressionLevel].nice_length;
max_chain_length = compressionConfigs[compressionLevel].max_chain;
}
return err;
}
ErrorCode deflate (FlushState flush)
{
if (next_out == nullptr || (next_in == nullptr && avail_in != 0)
|| (status == Status::FINISH_STATE && flush != FlushState::Z_FINISH))
return setError (ErrorCode::STREAM_ERROR);
if (avail_out == 0)
return setError (ErrorCode::BUF_ERROR);
auto old_flush = last_flush;
last_flush = flush;
if (status == Status::INIT_STATE)
{
if (wrap == 2)
{
adler = Checksum::crc32 (0, nullptr, 0);
putByte (31);
putByte (139);
putByte (8);
if (gzhead == nullptr)
{
putByte (0);
putByte (0);
putByte (0);
putByte (0);
putByte (0);
putByte (level == 9 ? 2 : (level < 2 ? 4 : 0));
putByte (OS_CODE);
status = Status::BUSY_STATE;
}
else
{
putByte ((gzhead->text ? 1 : 0) + (gzhead->hcrc ? 2 : 0) + (gzhead->extra == nullptr ? 0 : 4) + (gzhead->name == nullptr ? 0 : 8) + (gzhead->comment == nullptr ? 0 : 16));
putInt32 (gzhead->time);
putByte (level == 9 ? 2 : (level < 2 ? 4 : 0));
putByte (gzhead->os);
if (gzhead->extra != nullptr)
putInt16 (gzhead->extra_len);
if (gzhead->hcrc)
adler = Checksum::crc32 (adler, pending_buf.data(), pending);
gzindex = 0;
status = Status::EXTRA_STATE;
}
}
else
{
uint32_t header = (Z_DEFLATED + ((w_bits - 8) << 4)) << 8;
uint32_t level_flags;
if (level < 2)
level_flags = 0;
else if (level < 6)
level_flags = 1;
else if (level == 6)
level_flags = 2;
else
level_flags = 3;
header |= (level_flags << 6);
if (strstart != 0)
header |= 0x20; // preset dictionary flag
header += 31 - (header % 31);
status = Status::BUSY_STATE;
putInt16MSB (header);
if (strstart != 0)
putInt32MSB (adler);
adler = Checksum::adler32 (0, nullptr, 0);
}
}
if (status == Status::EXTRA_STATE)
{
if (gzhead->extra != nullptr)
{
uint32_t beg = pending; /* start of bytes to update crc */
while (gzindex < (gzhead->extra_len & 0xffff))
{
if (pending == pending_buf.size())
{
if (gzhead->hcrc && pending > beg)
adler = Checksum::crc32 (adler, pending_buf.data() + beg, pending - beg);
flushPending();
beg = pending;
if (pending == pending_buf.size())
break;
}
putByte (gzhead->extra[gzindex]);
gzindex++;
}
if (gzhead->hcrc && pending > beg)
adler = Checksum::crc32 (adler, pending_buf.data() + beg, pending - beg);
if (gzindex == gzhead->extra_len)
{
gzindex = 0;
status = Status::NAME_STATE;
}
}
else
status = Status::NAME_STATE;
}
if (status == Status::NAME_STATE)
{
if (gzhead->name != nullptr)
{
uint32_t beg = pending; /* start of bytes to update crc */
int val;
do
{
if (pending == pending_buf.size())
{
if (gzhead->hcrc && pending > beg)
adler = Checksum::crc32 (adler, pending_buf.data() + beg, pending - beg);
flushPending();
beg = pending;
if (pending == pending_buf.size())
{
val = 1;
break;
}
}
val = gzhead->name[gzindex++];
putByte (val);
} while (val != 0);
if (gzhead->hcrc && pending > beg)
adler = Checksum::crc32 (adler, pending_buf.data() + beg, pending - beg);
if (val == 0)
{
gzindex = 0;
status = Status::COMMENT_STATE;
}
}
else
status = Status::COMMENT_STATE;
}
if (status == Status::COMMENT_STATE)
{
if (gzhead->comment != nullptr)
{
uint32_t beg = pending; /* start of bytes to update crc */
int val;
do
{
if (pending == pending_buf.size())
{
if (gzhead->hcrc && pending > beg)
adler = Checksum::crc32 (adler, pending_buf.data() + beg, pending - beg);
flushPending();
beg = pending;
if (pending == pending_buf.size())
{
val = 1;
break;
}
}
val = gzhead->comment[gzindex++];
putByte (val);
} while (val != 0);
if (gzhead->hcrc && pending > beg)
adler = Checksum::crc32 (adler, pending_buf.data() + beg, pending - beg);
if (val == 0)
status = Status::HCRC_STATE;
}
else
status = Status::HCRC_STATE;
}
if (status == Status::HCRC_STATE)
{
if (gzhead->hcrc)
{
if (pending + 2 > pending_buf.size())
flushPending();
if (pending + 2 <= pending_buf.size())
{
putInt16 (adler);
adler = Checksum::crc32 (0, nullptr, 0);
status = Status::BUSY_STATE;
}
}
else
status = Status::BUSY_STATE;
}
if (pending != 0)
{
flushPending();
if (avail_out == 0)
{
/* Since avail_out is 0, deflate will be called again with
* more output space, but possibly with both pending and
* avail_in equal to zero. There won't be anything to do,
* but this is not an error situation so make sure we
* return OK instead of BUF_ERROR at next call of deflate:
*/
last_flush = FlushState::unknown;
return ErrorCode::OK;
}
}
else if (avail_in == 0 && flush <= old_flush && flush != FlushState::Z_FINISH)
{
return setError (ErrorCode::BUF_ERROR);
}
/* User must not provide more input after the first FINISH: */
if (status == Status::FINISH_STATE && avail_in != 0)
return setError (ErrorCode::BUF_ERROR);
if (avail_in != 0 || lookahead != 0 || (flush != FlushState::Z_NO_FLUSH && status != Status::FINISH_STATE))
{
auto blockStatus = (*(compressionConfigs[level].func)) (this, flush);
if (blockStatus == BlockStatus::finish_started || blockStatus == BlockStatus::finish_done)
status = Status::FINISH_STATE;
if (blockStatus == BlockStatus::need_more || blockStatus == BlockStatus::finish_started)
{
if (avail_out == 0)
last_flush = FlushState::unknown; /* avoid BUF_ERROR next call, see above */
return ErrorCode::OK;
}
if (blockStatus == BlockStatus::block_done)
{
if (flush == FlushState::Z_PARTIAL_FLUSH)
{
alignBuffer();
}
else
{
sendStoredBlock (nullptr, 0L, false);
if (flush == FlushState::Z_FULL_FLUSH)
clearHash();
}
flushPending();
if (avail_out == 0)
{
last_flush = FlushState::unknown; /* avoid BUF_ERROR at next call, see above */
return ErrorCode::OK;
}
}
}
CHOC_ASSERT (avail_out > 0);
if (flush != FlushState::Z_FINISH)
return ErrorCode::OK;
if (wrap <= 0)
return ErrorCode::STREAM_END;
if (wrap == 2)
{
putInt32 (adler);
putInt32 (total_in);
}
else
{
putInt32MSB (adler);
}
flushPending();
if (wrap > 0)
wrap = -wrap;
return pending != 0 ? ErrorCode::OK : ErrorCode::STREAM_END;
}
private:
const char* msg = nullptr;
DataType data_type = DataType::Z_BINARY;
unsigned long adler = 0;
enum class Status
{
INIT_STATE,
EXTRA_STATE,
NAME_STATE,
COMMENT_STATE,
HCRC_STATE,
BUSY_STATE,
FINISH_STATE
};
Status status = Status::INIT_STATE;
std::vector<uint8_t> pending_buf;
uint8_t* pending_out = nullptr;
uint32_t pending = 0;
int wrap = 0;
gz_header* gzhead = nullptr;
uint32_t gzindex = 0;
uint8_t method = 0;
FlushState last_flush = FlushState::Z_NO_FLUSH;
uint32_t w_size = 0;
uint32_t w_bits = 0;
uint32_t w_mask = 0;
std::vector<uint8_t> window;
unsigned long window_size = 0;
using Pos = uint16_t;
using IPos = uint32_t;
std::vector<Pos> prev, head;
uint32_t ins_h = 0;
uint32_t hash_size = 0;
uint32_t hash_bits = 0;
uint32_t hash_mask = 0;
uint32_t hash_shift = 0;
long block_start = 0;
uint32_t match_length = 0;
IPos prev_match = 0;
int match_available = 0;
uint32_t strstart = 0;
uint32_t match_start = 0;
uint32_t lookahead = 0;
uint32_t prev_length = 0;
uint32_t max_chain_length = 0;
uint32_t max_lazy_match = 0;
int level = 0;
uint32_t good_match = 0;
int nice_match = 0;
static constexpr uint32_t HEAP_SIZE = 2 * L_CODES + 1;
ct_data dyn_ltree[HEAP_SIZE] = {};
ct_data dyn_dtree[2 * D_CODES + 1] = {};
ct_data bl_tree[2 * BL_CODES + 1] = {};
tree_desc l_desc = {};
tree_desc d_desc = {};
tree_desc bl_desc = {};
static constexpr uint32_t MAX_BITS = 15;
uint16_t bl_count[MAX_BITS + 1] = {};
int heap[2 * L_CODES + 1] = {};
int heap_len = 0;
uint32_t heap_max = 0;
uint8_t depth[2 * L_CODES + 1] = {};
uint8_t* l_buf = nullptr;
uint32_t lit_bufsize = 0;
uint32_t last_lit = 0;
uint16_t* d_buf = nullptr;
unsigned long opt_len = 0;
unsigned long static_len = 0;
uint32_t last_eob_len = 0;
uint16_t outputBitBuffer = 0;
uint32_t outputBitBufferSize = 0;
static constexpr uint32_t SMALLEST = 1;
static constexpr uint32_t END_BLOCK = 256;
static constexpr uint32_t REP_3_6 = 16;
static constexpr uint32_t REPZ_3_10 = 17;
static constexpr uint32_t REPZ_11_138 = 18;
static constexpr uint32_t MAX_BL_BITS = 7;
#if defined (_WIN32) || defined (_WIN64)
static constexpr uint8_t OS_CODE = 0x0b;
#else
static constexpr uint8_t OS_CODE = 0x03;
#endif
static constexpr uint32_t STORED_BLOCK = 0;
static constexpr uint32_t STATIC_TREES = 1;
static constexpr uint32_t DYN_TREES = 2;
static constexpr uint32_t extra_lbits[LENGTH_CODES] /* extra bits for each length code */
= {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0};
static constexpr uint32_t extra_dbits[D_CODES] /* extra bits for each distance code */
= {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13};
static constexpr uint32_t extra_blbits[BL_CODES] /* extra bits for each bit length code */
= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7};
static constexpr uint8_t bl_order[BL_CODES]
= { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
enum class BlockStatus
{
need_more, /* block not completed, need more input or more output */
block_done, /* block flush performed */
finish_started, /* finish started, need only more output at next deflate */
finish_done /* finish done, accept no more input or output */
};
uint32_t read_buf (uint8_t* buf, uint32_t size)
{
uint32_t len = avail_in;
if (len > size)
len = size;
if (len == 0)
return 0;
avail_in -= len;
if (wrap == 1)
adler = Checksum::adler32 (adler, next_in, len);
else if (wrap == 2)
adler = Checksum::crc32 (adler, next_in, len);
memcpy (buf, next_in, len);
next_in += len;
total_in += len;
return len;
}
void flushPending()
{
auto len = pending;
if (len > avail_out)
len = avail_out;
if (len == 0)
return;
memcpy (next_out, pending_out, len);
next_out += len;
pending_out += len;
total_out += len;
avail_out -= len;
pending -= len;
if (pending == 0)
pending_out = pending_buf.data();
}
static const char* getErrorMessage (ErrorCode errorCode)
{
switch (errorCode)
{
case ErrorCode::NEED_DICT: return "need dictionary";
case ErrorCode::STREAM_END: return "stream end";
case ErrorCode::OK: return "";
case ErrorCode::ERRNO: return "file error";
case ErrorCode::STREAM_ERROR: return "stream error";
case ErrorCode::DATA_ERROR: return "data error";
case ErrorCode::MEM_ERROR: return "insufficient memory";
case ErrorCode::BUF_ERROR: return "buffer error";
case ErrorCode::VERSION_ERROR: return "incompatible version";
default: return "";
}
}
ErrorCode setError (ErrorCode errorCode)
{
msg = getErrorMessage (errorCode);
return errorCode;
}
/* ===========================================================================
* Copy without compression as much as possible from the input stream, return
* the current block state.
* This function does not insert new strings in the dictionary since
* uncompressible data is probably not useful. This function is used
* only for the level=0 compression option.
* NOTE: this function should be optimized to avoid extra copying from
* window to pending_buf.
*/
static BlockStatus deflate_stored (DeflateStream* s, FlushState flush)
{
/* Stored blocks are limited to 0xffff bytes, pending_buf is limited
* to pending_buf_size, and each stored block has a 5 byte header:
*/
unsigned long max_block_size = 0xffff;
unsigned long max_start;
if (max_block_size > (unsigned long) (s->pending_buf.size() - 5))
max_block_size = (unsigned long) (s->pending_buf.size() - 5);
/* Copy as much as possible from input to output: */
for (;;)
{
/* Fill the window as much as possible: */
if (s->lookahead <= 1)
{
CHOC_ASSERT (s->strstart < s->w_size + s->MAX_DIST() || s->block_start >= (long) s->w_size);
s->fill_window();
if (s->lookahead == 0 && flush == FlushState::Z_NO_FLUSH)
return BlockStatus::need_more;
if (s->lookahead == 0)
break; /* flush the current block */
}
CHOC_ASSERT (s->block_start >= 0L);
s->strstart += s->lookahead;
s->lookahead = 0;
/* Emit a stored block if pending_buf will be full: */
max_start = (unsigned long) s->block_start + max_block_size;
if (s->strstart == 0 || (unsigned long) s->strstart >= max_start)
{
/* strstart == 0 is possible when wraparound on 16-bit machine */
s->lookahead = (uint32_t) (s->strstart - max_start);
s->strstart = (uint32_t) max_start;
s->flushBlock (0);
}
/* Flush if we may have to slide, otherwise block_start may become
* negative and the data will be gone:
*/
if (s->strstart - (uint32_t) s->block_start >= s->MAX_DIST())
s->flushBlock (0);
}
s->flushBlock (flush == FlushState::Z_FINISH);
return flush == FlushState::Z_FINISH ? BlockStatus::finish_done
: BlockStatus::block_done;
}
/* ===========================================================================
* Compress as much as possible from the input stream, return the current
* block state.
* This function does not perform lazy evaluation of matches and inserts
* new strings in the dictionary only for unmatched strings or for short
* matches. It is used only for the fast compression options.
*/
static BlockStatus deflate_fast (DeflateStream* s, FlushState flush)
{
IPos hash_head = 0; /* head of the hash chain */
bool bflush; /* set if current block must be flushed */
for (;;)
{
/* Make sure that we always have enough lookahead, except
* at the end of the input file. We need MAX_MATCH bytes
* for the next match, plus MIN_MATCH bytes to insert the
* string following the next match.
*/
if (s->lookahead < MIN_LOOKAHEAD)
{
s->fill_window();
if (s->lookahead < MIN_LOOKAHEAD && flush == FlushState::Z_NO_FLUSH)
return BlockStatus::need_more;
if (s->lookahead == 0)
break; /* flush the current block */
}
/* Insert the string window[strstart .. strstart+2] in the
* dictionary, and set hash_head to the head of the hash chain:
*/
if (s->lookahead >= MIN_MATCH)
s->insertString (s->strstart, hash_head);
/* Find the longest match, discarding those <= prev_length.
* At this point we have always match_length < MIN_MATCH
*/
if (hash_head != 0 && s->strstart - hash_head <= s->MAX_DIST())
{
s->match_length = s->longest_match (hash_head);
}
if (s->match_length >= MIN_MATCH)
{
s->tallyDistance (s->strstart - s->match_start, s->match_length - MIN_MATCH, bflush);
s->lookahead -= s->match_length;
/* Insert new strings in the hash table only if the match length
* is not too large. This saves time but degrades compression.
*/
if (s->match_length <= s->max_lazy_match && s->lookahead >= MIN_MATCH)
{
s->match_length--; /* string at strstart already in table */
do
{
s->strstart++;
s->insertString (s->strstart, hash_head);
/* strstart never exceeds WSIZE-MAX_MATCH, so there are
* always MIN_MATCH bytes ahead.
*/
} while (--s->match_length != 0);
s->strstart++;
}
else
{
s->strstart += s->match_length;
s->match_length = 0;
s->ins_h = s->window[s->strstart];
s->updateHash (s->ins_h, s->window[s->strstart + 1]);
/* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
* matter since it will be recomputed at next deflate call.
*/