diff --git a/brotli.changes b/brotli.changes index 24da558..8dd5cac 100644 --- a/brotli.changes +++ b/brotli.changes @@ -1,3 +1,15 @@ +------------------------------------------------------------------- +Sun Jan 13 11:24:26 UTC 2019 - bjorn.lie@gmail.com + +- Add brotli_Verbose-CLI+Shared-Brotli.patch: Verbose CLI + start + pulling "Shared-Brotli". + * verbose CLI output; fix gh#google/brotlie#666. + * pull `SHIFT` transforms; currently this is semantically dead + code; later it will be used by "Shared-Brotli". +- Add brotli_Ensure-decompression-consumes-all-input.patch: Ensure + decompression consumes all input. If not, it's a corrupt stream. +- Tweak spec slightly. + ------------------------------------------------------------------- Fri Dec 14 11:01:35 UTC 2018 - Jan Engelhardt diff --git a/brotli.spec b/brotli.spec index 395f701..59094d8 100644 --- a/brotli.spec +++ b/brotli.spec @@ -1,7 +1,7 @@ # # spec file for package brotli # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # Copyright (c) 2017 Buschmann # # All modifications and additions to the file contributed by third parties @@ -25,16 +25,18 @@ Summary: Lossless Compression Algorithm License: MIT Group: Productivity/Archiving/Compression URL: https://github.com/google/brotli -Source: https://github.com/google/brotli/archive/v%version.tar.gz#/%name-%version.tar.gz +Source: %url/archive/v%version.tar.gz#/%name-%version.tar.gz Source99: baselibs.conf +Patch: brotli_Verbose-CLI+Shared-Brotli.patch +Patch1: brotli_Ensure-decompression-consumes-all-input.patch BuildRequires: cmake >= 2.8.6 BuildRequires: gcc-c++ BuildRequires: gzip BuildRequires: pkg-config %description -This package contains the brotli command line utility to compress and -decompress data with the brotli compression algorithm. +This package contains the brotli command line utility to compress +and decompress data with the brotli compression algorithm. Brotli is a generic-purpose lossless compression algorithm that compresses data using a combination of a modern variant of the LZ77 @@ -43,8 +45,8 @@ compression ratio comparable to the best currently available general-purpose compression methods. It is similar in speed with deflate but offers more dense compression. -The specification of the Brotli Compressed Data Format is defined in -RFC 7932. +The specification of the Brotli Compressed Data Format is defined +in RFC 7932. %package -n libbrotlicommon%sover Summary: Common Library for Brotli Compression @@ -62,17 +64,19 @@ Group: System/Libraries Decompression library for the Brotli general purpose lossless data compression algorithm. -The specification of the Brotli Compressed Data Format is defined in -RFC 7932. +The specification of the Brotli Compressed Data Format is defined +in RFC 7932. %package -n libbrotlienc%sover Summary: Library for Brotli Compression Group: System/Libraries %description -n libbrotlienc%sover -Compression library for the Brotli general purpose lossless data compression algorithm. +Compression library for the Brotli general purpose lossless data +compression algorithm. -The specification of the Brotli Compressed Data Format is defined in RFC 7932. +The specification of the Brotli Compressed Data Format is defined +in RFC 7932. %package -n libbrotli-devel Summary: Development and Header Files for Brotli Compression @@ -88,10 +92,11 @@ Obsoletes: libbrotlidec-devel < %version-%release Obsoletes: libbrotlienc-devel < %version-%release %description -n libbrotli-devel -Development and headers files for (de)compressing data using the Brotli general -purpose lossless compression algorithm. +Development and headers files for (de)compressing data using the +Brotli general purpose lossless compression algorithm. -The specification of the Brotli Compressed Data Format is defined in RFC 7932. +The specification of the Brotli Compressed Data Format is defined +in RFC 7932. %prep %autosetup -p1 diff --git a/brotli_Ensure-decompression-consumes-all-input.patch b/brotli_Ensure-decompression-consumes-all-input.patch new file mode 100644 index 0000000..cc60b6d --- /dev/null +++ b/brotli_Ensure-decompression-consumes-all-input.patch @@ -0,0 +1,74 @@ +From 5805f99a533a8f8118699c0100d8c102f3605f65 Mon Sep 17 00:00:00 2001 +From: Justin Ridgewell +Date: Mon, 12 Nov 2018 04:36:00 -0500 +Subject: [PATCH] Ensure decompression consumes all input (#730) + +* Ensure decompression consumes all input + +If not, it's a corrupt stream. + +* Use byte strings +--- + python/_brotli.cc | 4 ++-- + python/tests/decompress_test.py | 4 ++++ + python/tests/decompressor_test.py | 9 +++++++++ + 3 files changed, 15 insertions(+), 2 deletions(-) + +diff --git a/python/_brotli.cc b/python/_brotli.cc +index a6f925ef..5e1828e9 100644 +--- a/python/_brotli.cc ++++ b/python/_brotli.cc +@@ -414,7 +414,7 @@ static BROTLI_BOOL decompress_stream(BrotliDecoderState* dec, + (*output).insert((*output).end(), buffer, buffer + buffer_length); + } + } +- ok = result != BROTLI_DECODER_RESULT_ERROR; ++ ok = result != BROTLI_DECODER_RESULT_ERROR && !available_in; + + Py_END_ALLOW_THREADS + return ok; +@@ -672,7 +672,7 @@ static PyObject* brotli_decompress(PyObject *self, PyObject *args, PyObject *key + if (available_out != 0) + output.insert(output.end(), next_out, next_out + available_out); + } +- ok = result == BROTLI_DECODER_RESULT_SUCCESS; ++ ok = result == BROTLI_DECODER_RESULT_SUCCESS && !available_in; + BrotliDecoderDestroyInstance(state); + + Py_END_ALLOW_THREADS +diff --git a/python/tests/decompress_test.py b/python/tests/decompress_test.py +index 7a9e9e30..814e5633 100644 +--- a/python/tests/decompress_test.py ++++ b/python/tests/decompress_test.py +@@ -31,6 +31,10 @@ def _test_decompress(self, test_data): + self._decompress(test_data) + self._check_decompression(test_data) + ++ def test_garbage_appended(self): ++ with self.assertRaises(brotli.error): ++ brotli.decompress(brotli.compress(b'a') + b'a') ++ + + _test_utils.generate_test_methods(TestDecompress, for_decompression=True) + +diff --git a/python/tests/decompressor_test.py b/python/tests/decompressor_test.py +index 99667bcd..05918ada 100644 +--- a/python/tests/decompressor_test.py ++++ b/python/tests/decompressor_test.py +@@ -43,6 +43,15 @@ def _test_decompress(self, test_data): + self._decompress(test_data) + self._check_decompression(test_data) + ++ def test_garbage_appended(self): ++ with self.assertRaises(brotli.error): ++ self.decompressor.process(brotli.compress(b'a') + b'a') ++ ++ def test_already_finished(self): ++ self.decompressor.process(brotli.compress(b'a')) ++ with self.assertRaises(brotli.error): ++ self.decompressor.process(b'a') ++ + + _test_utils.generate_test_methods(TestDecompressor, for_decompression=True) + + diff --git a/brotli_Verbose-CLI+Shared-Brotli.patch b/brotli_Verbose-CLI+Shared-Brotli.patch new file mode 100644 index 0000000..a555bf4 --- /dev/null +++ b/brotli_Verbose-CLI+Shared-Brotli.patch @@ -0,0 +1,1375 @@ +From d0ffe60b87aa5ec302fcb031c8ebf726c1a1692a Mon Sep 17 00:00:00 2001 +From: Eugene Kliuchnikov +Date: Wed, 24 Oct 2018 16:06:09 +0200 +Subject: [PATCH] Verbose CLI + start pulling "Shared-Brotli" (#722) + +* Verbose CLI + start pulling "Shared-Brotli" + + * vesbose CLI output; fix #666 + * pull `SHIFT` transforms; currently this is semantically dead code; + later it will be used by "Shared-Brotli" +--- + c/common/transform.c | 56 +++ + c/common/transform.h | 5 + + c/enc/backward_references.h | 6 +- + c/enc/backward_references_hq.c | 11 +- + c/enc/backward_references_hq.h | 10 +- + c/enc/backward_references_inc.h | 6 +- + c/enc/encode.c | 2 - + c/enc/encoder_dict.c | 1 - + c/tools/brotli.c | 54 ++- + java/org/brotli/dec/BrotliInputStream.java | 12 +- + java/org/brotli/dec/Decode.java | 63 ++- + java/org/brotli/dec/SynthTest.java | 513 ++++++++++++++++++++- + java/org/brotli/dec/Transform.java | 183 ++++++-- + java/org/brotli/dec/TransformTest.java | 14 +- + 14 files changed, 848 insertions(+), 88 deletions(-) + +diff --git a/c/common/transform.c b/c/common/transform.c +index 426e635f..c182053d 100755 +--- a/c/common/transform.c ++++ b/c/common/transform.c +@@ -166,6 +166,7 @@ static BrotliTransforms kBrotliTransforms = { + kPrefixSuffixMap, + sizeof(kTransformsData) / (3 * sizeof(kTransformsData[0])), + kTransformsData, ++ NULL, /* no extra parameters */ + {0, 12, 27, 23, 42, 63, 56, 48, 59, 64} + }; + +@@ -190,6 +191,48 @@ static int ToUpperCase(uint8_t* p) { + return 3; + } + ++static int Shift(uint8_t* word, int word_len, uint16_t parameter) { ++ /* Limited sign extension: scalar < (1 << 24). */ ++ uint32_t scalar = ++ (parameter & 0x7FFFu) + (0x1000000u - (parameter & 0x8000u)); ++ if (word[0] < 0x80) { ++ /* 1-byte rune / 0sssssss / 7 bit scalar (ASCII). */ ++ scalar += (uint32_t)word[0]; ++ word[0] = (uint8_t)(scalar & 0x7Fu); ++ return 1; ++ } else if (word[0] < 0xC0) { ++ /* Continuation / 10AAAAAA. */ ++ return 1; ++ } else if (word[0] < 0xE0) { ++ /* 2-byte rune / 110sssss AAssssss / 11 bit scalar. */ ++ if (word_len < 2) return 1; ++ scalar += (uint32_t)((word[1] & 0x3Fu) | ((word[0] & 0x1Fu) << 6u)); ++ word[0] = (uint8_t)(0xC0 | ((scalar >> 6u) & 0x1F)); ++ word[1] = (uint8_t)((word[1] & 0xC0) | (scalar & 0x3F)); ++ return 2; ++ } else if (word[0] < 0xF0) { ++ /* 3-byte rune / 1110ssss AAssssss BBssssss / 16 bit scalar. */ ++ if (word_len < 3) return word_len; ++ scalar += (uint32_t)((word[2] & 0x3Fu) | ((word[1] & 0x3Fu) << 6u) | ++ ((word[0] & 0x0Fu) << 12u)); ++ word[0] = (uint8_t)(0xE0 | ((scalar >> 12u) & 0x0F)); ++ word[1] = (uint8_t)((word[1] & 0xC0) | ((scalar >> 6u) & 0x3F)); ++ word[2] = (uint8_t)((word[2] & 0xC0) | (scalar & 0x3F)); ++ return 3; ++ } else if (word[0] < 0xF8) { ++ /* 4-byte rune / 11110sss AAssssss BBssssss CCssssss / 21 bit scalar. */ ++ if (word_len < 4) return word_len; ++ scalar += (uint32_t)((word[3] & 0x3Fu) | ((word[2] & 0x3Fu) << 6u) | ++ ((word[1] & 0x3Fu) << 12u) | ((word[0] & 0x07u) << 18u)); ++ word[0] = (uint8_t)(0xF0 | ((scalar >> 18u) & 0x07)); ++ word[1] = (uint8_t)((word[1] & 0xC0) | ((scalar >> 12u) & 0x3F)); ++ word[2] = (uint8_t)((word[2] & 0xC0) | ((scalar >> 6u) & 0x3F)); ++ word[3] = (uint8_t)((word[3] & 0xC0) | (scalar & 0x3F)); ++ return 4; ++ } ++ return 1; ++} ++ + int BrotliTransformDictionaryWord(uint8_t* dst, const uint8_t* word, int len, + const BrotliTransforms* transforms, int transform_idx) { + int idx = 0; +@@ -221,6 +264,19 @@ int BrotliTransformDictionaryWord(uint8_t* dst, const uint8_t* word, int len, + uppercase += step; + len -= step; + } ++ } else if (t == BROTLI_TRANSFORM_SHIFT_FIRST) { ++ uint16_t param = (uint16_t)(transforms->params[transform_idx * 2] ++ + (transforms->params[transform_idx * 2 + 1] << 8u)); ++ Shift(&dst[idx - len], len, param); ++ } else if (t == BROTLI_TRANSFORM_SHIFT_ALL) { ++ uint16_t param = (uint16_t)(transforms->params[transform_idx * 2] ++ + (transforms->params[transform_idx * 2 + 1] << 8u)); ++ uint8_t* shift = &dst[idx - len]; ++ while (len > 0) { ++ int step = Shift(shift, len, param); ++ shift += step; ++ len -= step; ++ } + } + } + { +diff --git a/c/common/transform.h b/c/common/transform.h +index 456c12db..b6f86cc7 100755 +--- a/c/common/transform.h ++++ b/c/common/transform.h +@@ -37,6 +37,8 @@ enum BrotliWordTransformType { + BROTLI_TRANSFORM_OMIT_FIRST_7 = 18, + BROTLI_TRANSFORM_OMIT_FIRST_8 = 19, + BROTLI_TRANSFORM_OMIT_FIRST_9 = 20, ++ BROTLI_TRANSFORM_SHIFT_FIRST = 21, ++ BROTLI_TRANSFORM_SHIFT_ALL = 22, + BROTLI_NUM_TRANSFORM_TYPES /* Counts transforms, not a transform itself. */ + }; + +@@ -50,6 +52,9 @@ typedef struct BrotliTransforms { + uint32_t num_transforms; + /* Each entry is a [prefix_id, transform, suffix_id] triplet. */ + const uint8_t* transforms; ++ /* Shift for BROTLI_TRANSFORM_SHIFT_FIRST and BROTLI_TRANSFORM_SHIFT_ALL, ++ must be NULL if and only if no such transforms are present. */ ++ const uint8_t* params; + /* Indices of transforms like ["", BROTLI_TRANSFORM_OMIT_LAST_#, ""]. + 0-th element corresponds to ["", BROTLI_TRANSFORM_IDENTITY, ""]. + -1, if cut-off transform does not exist. */ +diff --git a/c/enc/backward_references.h b/c/enc/backward_references.h +index 3a414664..f82a80da 100644 +--- a/c/enc/backward_references.h ++++ b/c/enc/backward_references.h +@@ -25,9 +25,9 @@ extern "C" { + initially the total amount of commands output by previous + CreateBackwardReferences calls, and must be incremented by the amount written + by this call. */ +-BROTLI_INTERNAL void BrotliCreateBackwardReferences( +- size_t num_bytes, size_t position, const uint8_t* ringbuffer, +- size_t ringbuffer_mask, const BrotliEncoderParams* params, ++BROTLI_INTERNAL void BrotliCreateBackwardReferences(size_t num_bytes, ++ size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, ++ const BrotliEncoderParams* params, + HasherHandle hasher, int* dist_cache, size_t* last_insert_len, + Command* commands, size_t* num_commands, size_t* num_literals); + +diff --git a/c/enc/backward_references_hq.c b/c/enc/backward_references_hq.c +index 96b0e708..5737f752 100644 +--- a/c/enc/backward_references_hq.c ++++ b/c/enc/backward_references_hq.c +@@ -419,8 +419,8 @@ static size_t UpdateNodes( + size_t k; + size_t gap = 0; + +- EvaluateNode(block_start, pos, max_backward_limit, gap, starting_dist_cache, +- model, queue, nodes); ++ EvaluateNode(block_start, pos, max_backward_limit, gap, ++ starting_dist_cache, model, queue, nodes); + + { + const PosData* posdata = StartPosQueueAt(queue, 0); +@@ -587,9 +587,10 @@ void BrotliZopfliCreateCommands(const size_t num_bytes, + { + size_t distance = ZopfliNodeCopyDistance(next); + size_t len_code = ZopfliNodeLengthCode(next); +- size_t max_distance = +- BROTLI_MIN(size_t, block_start + pos, max_backward_limit); +- BROTLI_BOOL is_dictionary = TO_BROTLI_BOOL(distance > max_distance + gap); ++ size_t max_distance = BROTLI_MIN(size_t, ++ block_start + pos, max_backward_limit); ++ BROTLI_BOOL is_dictionary = ++ TO_BROTLI_BOOL(distance > max_distance + gap); + size_t dist_code = ZopfliNodeDistanceCode(next); + InitCommand(&commands[i], ¶ms->dist, insert_length, + copy_length, (int)len_code - (int)copy_length, dist_code); +diff --git a/c/enc/backward_references_hq.h b/c/enc/backward_references_hq.h +index 1e4275d4..fb1ff3fa 100644 +--- a/c/enc/backward_references_hq.h ++++ b/c/enc/backward_references_hq.h +@@ -23,14 +23,16 @@ extern "C" { + #endif + + BROTLI_INTERNAL void BrotliCreateZopfliBackwardReferences(MemoryManager* m, +- size_t num_bytes, size_t position, const uint8_t* ringbuffer, +- size_t ringbuffer_mask, const BrotliEncoderParams* params, ++ size_t num_bytes, ++ size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, ++ const BrotliEncoderParams* params, + HasherHandle hasher, int* dist_cache, size_t* last_insert_len, + Command* commands, size_t* num_commands, size_t* num_literals); + + BROTLI_INTERNAL void BrotliCreateHqZopfliBackwardReferences(MemoryManager* m, +- size_t num_bytes, size_t position, const uint8_t* ringbuffer, +- size_t ringbuffer_mask, const BrotliEncoderParams* params, ++ size_t num_bytes, ++ size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, ++ const BrotliEncoderParams* params, + HasherHandle hasher, int* dist_cache, size_t* last_insert_len, + Command* commands, size_t* num_commands, size_t* num_literals); + +diff --git a/c/enc/backward_references_inc.h b/c/enc/backward_references_inc.h +index c18cdb00..e29daf33 100644 +--- a/c/enc/backward_references_inc.h ++++ b/c/enc/backward_references_inc.h +@@ -60,7 +60,8 @@ static BROTLI_NOINLINE void EXPORT_FN(CreateBackwardReferences)( + FN(FindLongestMatch)(hasher, + ¶ms->dictionary, + ringbuffer, ringbuffer_mask, dist_cache, position + 1, max_length, +- max_distance, gap, params->dist.max_distance, &sr2); ++ max_distance, gap, params->dist.max_distance, ++ &sr2); + if (sr2.score >= sr.score + cost_diff_lazy) { + /* Ok, let's just write one byte for now and start a match from the + next byte. */ +@@ -76,7 +77,8 @@ static BROTLI_NOINLINE void EXPORT_FN(CreateBackwardReferences)( + } + apply_random_heuristics = + position + 2 * sr.len + random_heuristics_window_size; +- max_distance = BROTLI_MIN(size_t, position, max_backward_limit); ++ max_distance = BROTLI_MIN(size_t, ++ position, max_backward_limit); + { + /* The first 16 codes are special short-codes, + and the minimum offset is 1. */ +diff --git a/c/enc/encode.c b/c/enc/encode.c +index 141e70aa..c82f2d3f 100644 +--- a/c/enc/encode.c ++++ b/c/enc/encode.c +@@ -114,8 +114,6 @@ typedef struct BrotliEncoderStateStruct { + BROTLI_BOOL is_initialized_; + } BrotliEncoderStateStruct; + +-static BROTLI_BOOL EnsureInitialized(BrotliEncoderState* s); +- + static size_t InputBlockSize(BrotliEncoderState* s) { + return (size_t)1 << s->params.lgblock; + } +diff --git a/c/enc/encoder_dict.c b/c/enc/encoder_dict.c +index 8b2f6ad4..e58ca670 100755 +--- a/c/enc/encoder_dict.c ++++ b/c/enc/encoder_dict.c +@@ -24,7 +24,6 @@ void BrotliInitEncoderDictionary(BrotliEncoderDictionary* dict) { + + dict->cutoffTransformsCount = kCutoffTransformsCount; + dict->cutoffTransforms = kCutoffTransforms; +- + } + + #if defined(__cplusplus) || defined(c_plusplus) +diff --git a/c/tools/brotli.c b/c/tools/brotli.c +index ce05b641..838539ad 100644 +--- a/c/tools/brotli.c ++++ b/c/tools/brotli.c +@@ -86,10 +86,10 @@ typedef struct { + /* Parameters */ + int quality; + int lgwin; ++ int verbosity; + BROTLI_BOOL force_overwrite; + BROTLI_BOOL junk_source; + BROTLI_BOOL copy_stat; +- BROTLI_BOOL verbose; + BROTLI_BOOL write_to_stdout; + BROTLI_BOOL test_integrity; + BROTLI_BOOL decompress; +@@ -121,6 +121,12 @@ typedef struct { + const uint8_t* next_in; + size_t available_out; + uint8_t* next_out; ++ ++ /* Reporting */ ++ /* size_t would be large enough, ++ until 4GiB+ files are compressed / decompressed on 32-bit CPUs. */ ++ size_t total_in; ++ size_t total_out; + } Context; + + /* Parse up to 5 decimal digits. */ +@@ -279,11 +285,11 @@ static Command ParseParams(Context* params) { + command = COMMAND_TEST_INTEGRITY; + continue; + } else if (c == 'v') { +- if (params->verbose) { ++ if (params->verbosity > 0) { + fprintf(stderr, "argument --verbose / -v already set\n"); + return COMMAND_INVALID; + } +- params->verbose = BROTLI_TRUE; ++ params->verbosity = 1; + continue; + } else if (c == 'V') { + /* Don't parse further. */ +@@ -415,11 +421,11 @@ static Command ParseParams(Context* params) { + command_set = BROTLI_TRUE; + command = COMMAND_TEST_INTEGRITY; + } else if (strcmp("verbose", arg) == 0) { +- if (params->verbose) { ++ if (params->verbosity > 0) { + fprintf(stderr, "argument --verbose / -v already set\n"); + return COMMAND_INVALID; + } +- params->verbose = BROTLI_TRUE; ++ params->verbosity = 1; + } else if (strcmp("version", arg) == 0) { + /* Don't parse further. */ + return COMMAND_VERSION; +@@ -787,6 +793,8 @@ static void InitializeBuffers(Context* context) { + context->next_in = NULL; + context->available_out = kFileBufferSize; + context->next_out = context->output; ++ context->total_in = 0; ++ context->total_out = 0; + } + + static BROTLI_BOOL HasMoreInput(Context* context) { +@@ -796,6 +804,7 @@ static BROTLI_BOOL HasMoreInput(Context* context) { + static BROTLI_BOOL ProvideInput(Context* context) { + context->available_in = + fread(context->input, 1, kFileBufferSize, context->fin); ++ context->total_in += context->available_in; + context->next_in = context->input; + if (ferror(context->fin)) { + fprintf(stderr, "failed to read input [%s]: %s\n", +@@ -808,6 +817,7 @@ static BROTLI_BOOL ProvideInput(Context* context) { + /* Internal: should be used only in Provide-/Flush-Output. */ + static BROTLI_BOOL WriteOutput(Context* context) { + size_t out_size = (size_t)(context->next_out - context->output); ++ context->total_out += out_size; + if (out_size == 0) return BROTLI_TRUE; + if (context->test_integrity) return BROTLI_TRUE; + +@@ -833,6 +843,25 @@ static BROTLI_BOOL FlushOutput(Context* context) { + return BROTLI_TRUE; + } + ++static void PrintBytes(size_t value) { ++ if (value < 1024) { ++ fprintf(stderr, "%d B", (int)value); ++ } else if (value < 1048576) { ++ fprintf(stderr, "%0.3f KiB", (double)value / 1024.0); ++ } else if (value < 1073741824) { ++ fprintf(stderr, "%0.3f MiB", (double)value / 1048576.0); ++ } else { ++ fprintf(stderr, "%0.3f GiB", (double)value / 1073741824.0); ++ } ++} ++ ++static void PrintFileProcessingProgress(Context* context) { ++ fprintf(stderr, "[%s]: ", PrintablePath(context->current_input_path)); ++ PrintBytes(context->total_in); ++ fprintf(stderr, " -> "); ++ PrintBytes(context->total_out); ++} ++ + static BROTLI_BOOL DecompressFile(Context* context, BrotliDecoderState* s) { + BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; + InitializeBuffers(context); +@@ -853,6 +882,11 @@ static BROTLI_BOOL DecompressFile(Context* context, BrotliDecoderState* s) { + PrintablePath(context->current_input_path)); + return BROTLI_FALSE; + } ++ if (context->verbosity > 0) { ++ fprintf(stderr, "Decompressed "); ++ PrintFileProcessingProgress(context); ++ fprintf(stderr, "\n"); ++ } + return BROTLI_TRUE; + } else { + fprintf(stderr, "corrupt input [%s]\n", +@@ -915,7 +949,13 @@ static BROTLI_BOOL CompressFile(Context* context, BrotliEncoderState* s) { + } + + if (BrotliEncoderIsFinished(s)) { +- return FlushOutput(context); ++ if (!FlushOutput(context)) return BROTLI_FALSE; ++ if (context->verbosity > 0) { ++ fprintf(stderr, "Compressed "); ++ PrintFileProcessingProgress(context); ++ fprintf(stderr, "\n"); ++ } ++ return BROTLI_TRUE; + } + } + } +@@ -979,11 +1019,11 @@ int main(int argc, char** argv) { + + context.quality = 11; + context.lgwin = -1; ++ context.verbosity = 0; + context.force_overwrite = BROTLI_FALSE; + context.junk_source = BROTLI_FALSE; + context.copy_stat = BROTLI_TRUE; + context.test_integrity = BROTLI_FALSE; +- context.verbose = BROTLI_FALSE; + context.write_to_stdout = BROTLI_FALSE; + context.decompress = BROTLI_FALSE; + context.large_window = BROTLI_FALSE; +diff --git a/java/org/brotli/dec/BrotliInputStream.java b/java/org/brotli/dec/BrotliInputStream.java +index a27e9284..5cc2e284 100644 +--- a/java/org/brotli/dec/BrotliInputStream.java ++++ b/java/org/brotli/dec/BrotliInputStream.java +@@ -85,7 +85,17 @@ public BrotliInputStream(InputStream source, int byteReadBufferSize) throws IOEx + } + + public void setEager(boolean eager) { +- state.isEager = eager ? 1 : 0; ++ boolean isEager = (state.isEager != 0); ++ if (eager == isEager) { ++ /* Shortcut for no-op change. */ ++ return; ++ } ++ if (eager) { ++ Decode.setEager(state); ++ } else { ++ /* Once decoder is "eager", there is no way back. */ ++ throw new IllegalStateException("Brotli decoder has been already switched to eager mode"); ++ } + } + + /** +diff --git a/java/org/brotli/dec/Decode.java b/java/org/brotli/dec/Decode.java +index 9e3d43b0..60bf9c61 100644 +--- a/java/org/brotli/dec/Decode.java ++++ b/java/org/brotli/dec/Decode.java +@@ -18,18 +18,19 @@ + // RunningState + //---------------------------------------------------------------------------- + private static final int UNINITIALIZED = 0; +- private static final int BLOCK_START = 1; +- private static final int COMPRESSED_BLOCK_START = 2; +- private static final int MAIN_LOOP = 3; +- private static final int READ_METADATA = 4; +- private static final int COPY_UNCOMPRESSED = 5; +- private static final int INSERT_LOOP = 6; +- private static final int COPY_LOOP = 7; +- private static final int TRANSFORM = 8; +- private static final int FINISHED = 9; +- private static final int CLOSED = 10; +- private static final int INIT_WRITE = 11; +- private static final int WRITE = 12; ++ private static final int INITIALIZED = 1; ++ private static final int BLOCK_START = 2; ++ private static final int COMPRESSED_BLOCK_START = 3; ++ private static final int MAIN_LOOP = 4; ++ private static final int READ_METADATA = 5; ++ private static final int COPY_UNCOMPRESSED = 6; ++ private static final int INSERT_LOOP = 7; ++ private static final int COPY_LOOP = 8; ++ private static final int TRANSFORM = 9; ++ private static final int FINISHED = 10; ++ private static final int CLOSED = 11; ++ private static final int INIT_WRITE = 12; ++ private static final int WRITE = 13; + + private static final int DEFAULT_CODE_LENGTH = 8; + private static final int CODE_LENGTH_REPEAT_CODE = 16; +@@ -139,6 +140,20 @@ private static int decodeWindowBits(State s) { + return 17; + } + ++ /** ++ * Switch decoder to "eager" mode. ++ * ++ * In "eager" mode decoder returns as soon as there is enough data to fill output buffer. ++ * ++ * @param s initialized state, before any read is performed. ++ */ ++ static void setEager(State s) { ++ if (s.runningState != INITIALIZED) { ++ throw new IllegalStateException("State MUST be freshly initialized"); ++ } ++ s.isEager = 1; ++ } ++ + /** + * Associate input with decoder state. + * +@@ -152,13 +167,7 @@ static void initState(State s, InputStream input) { + s.blockTrees = new int[6 * HUFFMAN_TABLE_SIZE]; + s.input = input; + BitReader.initBitReader(s); +- int windowBits = decodeWindowBits(s); +- if (windowBits == 9) { /* Reserved case for future expansion. */ +- throw new BrotliRuntimeException("Invalid 'windowBits' code"); +- } +- s.maxRingBufferSize = 1 << windowBits; +- s.maxBackwardDistance = s.maxRingBufferSize - 16; +- s.runningState = BLOCK_START; ++ s.runningState = INITIALIZED; + } + + static void close(State s) throws IOException { +@@ -727,6 +736,16 @@ static void decompress(State s) { + if (s.runningState == CLOSED) { + throw new IllegalStateException("Can't decompress after close"); + } ++ if (s.runningState == INITIALIZED) { ++ int windowBits = decodeWindowBits(s); ++ if (windowBits == 9) { /* Reserved case for future expansion. */ ++ throw new BrotliRuntimeException("Invalid 'windowBits' code"); ++ } ++ s.maxRingBufferSize = 1 << windowBits; ++ s.maxBackwardDistance = s.maxRingBufferSize - 16; ++ s.runningState = BLOCK_START; ++ } ++ + int fence = calculateFence(s); + int ringBufferMask = s.ringBufferSize - 1; + byte[] ringBuffer = s.ringBuffer; +@@ -935,9 +954,9 @@ static void decompress(State s) { + int wordIdx = wordId & mask; + int transformIdx = wordId >>> shift; + offset += wordIdx * s.copyLength; +- if (transformIdx < Transform.NUM_TRANSFORMS) { +- int len = Transform.transformDictionaryWord(ringBuffer, s.pos, +- Dictionary.getData(), offset, s.copyLength, transformIdx); ++ if (transformIdx < Transform.NUM_RFC_TRANSFORMS) { ++ int len = Transform.transformDictionaryWord(ringBuffer, s.pos, Dictionary.getData(), ++ offset, s.copyLength, Transform.RFC_TRANSFORMS, transformIdx); + s.pos += len; + s.metaBlockLength -= len; + if (s.pos >= fence) { +diff --git a/java/org/brotli/dec/SynthTest.java b/java/org/brotli/dec/SynthTest.java +index c95a3c90..de91c377 100644 +--- a/java/org/brotli/dec/SynthTest.java ++++ b/java/org/brotli/dec/SynthTest.java +@@ -64,6 +64,474 @@ private void checkSynth(byte[] compressed, boolean expectSuccess, + + /* GENERATED CODE START */ + ++ @Test ++ public void testAllTransforms10() { ++ byte[] compressed = { ++ (byte) 0x1b, (byte) 0xfc, (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, ++ (byte) 0xe3, (byte) 0xb4, (byte) 0x0d, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x5b, ++ (byte) 0x26, (byte) 0x31, (byte) 0x40, (byte) 0x02, (byte) 0x00, (byte) 0xe0, (byte) 0x4e, ++ (byte) 0x1b, (byte) 0x13, (byte) 0x7c, (byte) 0x84, (byte) 0x26, (byte) 0xf8, (byte) 0x04, ++ (byte) 0x10, (byte) 0x4c, (byte) 0xf0, (byte) 0x89, (byte) 0x38, (byte) 0x30, (byte) 0xc1, ++ (byte) 0x27, (byte) 0x4e, (byte) 0xc1, (byte) 0x04, (byte) 0x9f, (byte) 0x64, (byte) 0x06, ++ (byte) 0x26, (byte) 0xf8, (byte) 0x24, (byte) 0x3f, (byte) 0x34, (byte) 0xc1, (byte) 0x27, ++ (byte) 0x7d, (byte) 0x82, (byte) 0x09, (byte) 0x3e, (byte) 0xe9, (byte) 0x16, (byte) 0x4d, ++ (byte) 0xf0, (byte) 0xc9, (byte) 0xd2, (byte) 0xc0, (byte) 0x04, (byte) 0x9f, (byte) 0x0c, ++ (byte) 0x8f, (byte) 0x4c, (byte) 0xf0, (byte) 0xc9, (byte) 0x06, (byte) 0xd1, (byte) 0x04, ++ (byte) 0x9f, (byte) 0x6c, (byte) 0x92, (byte) 0x4d, (byte) 0xf0, (byte) 0xc9, (byte) 0x39, ++ (byte) 0xc1, (byte) 0x04, (byte) 0x9f, (byte) 0xdc, (byte) 0x94, (byte) 0x4c, (byte) 0xf0, ++ (byte) 0xc9, (byte) 0x69, (byte) 0xd1, (byte) 0x04, (byte) 0x9f, (byte) 0x3c, (byte) 0x98, ++ (byte) 0x4d, (byte) 0xf0, (byte) 0x29, (byte) 0x9c, (byte) 0x81, (byte) 0x09, (byte) 0x3e, ++ (byte) 0x45, (byte) 0x37, (byte) 0x31, (byte) 0xc1, (byte) 0xa7, (byte) 0x60, (byte) 0x47, ++ (byte) 0x26, (byte) 0xf8, (byte) 0x14, (byte) 0xfa, (byte) 0xcc, (byte) 0x04, (byte) 0x9f, ++ (byte) 0xc2, (byte) 0x20, (byte) 0x9a, (byte) 0xe0, (byte) 0x53, (byte) 0x48, (byte) 0x54, ++ (byte) 0x13, (byte) 0x7c, (byte) 0x8a, (byte) 0x8f, (byte) 0x6c, (byte) 0x82, (byte) 0x4f, ++ (byte) 0xb1, (byte) 0xd2, (byte) 0x4d, (byte) 0xf0, (byte) 0x29, (byte) 0x67, (byte) 0x82, ++ (byte) 0x09, (byte) 0x3e, (byte) 0xe5, (byte) 0x4f, (byte) 0x31, (byte) 0xc1, (byte) 0xa7, ++ (byte) 0x7c, (byte) 0x4a, (byte) 0x26, (byte) 0xf8, (byte) 0x94, (byte) 0x57, (byte) 0xcd, ++ (byte) 0x04, (byte) 0x9f, (byte) 0x12, (byte) 0x2c, (byte) 0x9a, (byte) 0xe0, (byte) 0x53, ++ (byte) 0xba, (byte) 0x55, (byte) 0x13, (byte) 0x7c, (byte) 0xca, (byte) 0xbf, (byte) 0x6c, ++ (byte) 0x82, (byte) 0x4f, (byte) 0xb9, (byte) 0xd8, (byte) 0x4d, (byte) 0xf0, (byte) 0xa9, ++ (byte) 0x30, (byte) 0x03, (byte) 0x13, (byte) 0x7c, (byte) 0x2a, (byte) 0xd2, (byte) 0xc2, ++ (byte) 0x04, (byte) 0x9f, (byte) 0x4a, (byte) 0x36, (byte) 0x31, (byte) 0xc1, (byte) 0xa7, ++ (byte) 0xca, (byte) 0x6d, (byte) 0x4c, (byte) 0xf0, (byte) 0xa9, (byte) 0x94, (byte) 0x23, ++ (byte) 0x13, (byte) 0x7c, (byte) 0x2a, (byte) 0xeb, (byte) 0xca, (byte) 0x04, (byte) 0x9f, ++ (byte) 0xea, (byte) 0x3c, (byte) 0x33, (byte) 0xc1, (byte) 0xa7, (byte) 0xb2, (byte) 0xef, ++ (byte) 0x4c, (byte) 0xf0, (byte) 0xa9, (byte) 0xf8, (byte) 0x43, (byte) 0x13, (byte) 0x7c, ++ (byte) 0xaa, (byte) 0x00, (byte) 0xd3, (byte) 0x04, (byte) 0x9f, (byte) 0x2a, (byte) 0x42, ++ (byte) 0x35, (byte) 0xc1, (byte) 0xa7, (byte) 0xc2, (byte) 0x70, (byte) 0x4d, (byte) 0xf0, ++ (byte) 0xa9, (byte) 0x52, (byte) 0x64, (byte) 0x13, (byte) 0x7c, (byte) 0x2a, (byte) 0x1a, ++ (byte) 0xdb, (byte) 0x04, (byte) 0x9f, (byte) 0x6a, (byte) 0x48, (byte) 0x37, (byte) 0xc1, ++ (byte) 0xa7, (byte) 0x92, (byte) 0xf2, (byte) 0x4d, (byte) 0xf0, (byte) 0xa9, (byte) 0xc3, ++ (byte) 0x04, (byte) 0x13, (byte) 0x7c, (byte) 0xea, (byte) 0x32, (byte) 0xc3, (byte) 0x04, ++ (byte) 0x9f, (byte) 0x7a, (byte) 0x4e, (byte) 0x31, (byte) 0xc1, (byte) 0xa7, (byte) 0x06, ++ (byte) 0x74, (byte) 0x4c, (byte) 0xf0, (byte) 0xa9, (byte) 0x19, (byte) 0x25, (byte) 0x13, ++ (byte) 0x7c, (byte) 0x6a, (byte) 0x4d, (byte) 0xcb, (byte) 0x04, (byte) 0x9f, (byte) 0x1a, ++ (byte) 0x55, (byte) 0x33, (byte) 0xc1, (byte) 0xa7, (byte) 0x56, (byte) 0xf5, (byte) 0x4c, ++ (byte) 0xf0, (byte) 0xa9, (byte) 0x5d, (byte) 0x45, (byte) 0x13, (byte) 0x7c, (byte) 0xea, ++ (byte) 0x59, (byte) 0xd3, (byte) 0x04, (byte) 0x9f, (byte) 0xfa, (byte) 0x57, (byte) 0x35, ++ (byte) 0xc1, (byte) 0xa7, (byte) 0x66, (byte) 0x76, (byte) 0x4d, (byte) 0xf0, (byte) 0xa9, ++ (byte) 0x9f, (byte) 0x65, (byte) 0x13, (byte) 0x7c, (byte) 0x6a, (byte) 0x6f, (byte) 0xdb, ++ (byte) 0x04, (byte) 0x9f, (byte) 0x9a, (byte) 0x5d, (byte) 0x37, (byte) 0xc1, (byte) 0xa7, ++ (byte) 0x06, (byte) 0xf8, (byte) 0x4d, (byte) 0xf0, (byte) 0x69, (byte) 0x0c, (byte) 0x06, ++ (byte) 0x26, (byte) 0xf8, (byte) 0x34, (byte) 0x08, (byte) 0x07, (byte) 0x13, (byte) 0x7c, ++ (byte) 0x1a, (byte) 0x8b, (byte) 0x85, (byte) 0x09, (byte) 0x3e, (byte) 0x8d, (byte) 0xc8, ++ (byte) 0xc3, (byte) 0x04, (byte) 0x9f, (byte) 0xe6, (byte) 0x65, (byte) 0x62, (byte) 0x82, ++ (byte) 0x4f, (byte) 0xb3, (byte) 0x73, (byte) 0x31, (byte) 0xc1, (byte) 0xa7, (byte) 0x41, ++ (byte) 0xda, (byte) 0x98, (byte) 0xe0, (byte) 0xd3, (byte) 0x54, (byte) 0x7d, (byte) 0x4c, ++ (byte) 0xf0, (byte) 0x69, (byte) 0xc4, (byte) 0x46, (byte) 0x26, (byte) 0xf8, (byte) 0x34, ++ (byte) 0x72, (byte) 0x27, (byte) 0x13, (byte) 0x7c, (byte) 0x1a, (byte) 0xc5, (byte) 0x95, ++ (byte) 0x09, (byte) 0x3e, (byte) 0x8d, (byte) 0xe5, (byte) 0xcb, (byte) 0x04, (byte) 0x9f, ++ (byte) 0x06, (byte) 0x75, (byte) 0x66, (byte) 0x82, (byte) 0x4f, (byte) 0x43, (byte) 0x7b, ++ (byte) 0x33, (byte) 0xc1, (byte) 0xa7, (byte) 0x09, (byte) 0xde, (byte) 0x99, (byte) 0xe0, ++ (byte) 0xd3, (byte) 0x34, (byte) 0xff, (byte) 0x4c, (byte) 0xf0, (byte) 0x69, (byte) 0xb2, ++ (byte) 0x87, (byte) 0x26, (byte) 0xf8, (byte) 0x34, (byte) 0xe9, (byte) 0x47, (byte) 0x13, ++ (byte) 0x7c, (byte) 0x9a, (byte) 0xfb, (byte) 0xa5, (byte) 0x09, (byte) 0x3e, (byte) 0x4d, ++ (byte) 0x01, (byte) 0xd4, (byte) 0x04, (byte) 0x9f, (byte) 0x46, (byte) 0x82, (byte) 0x6a, ++ (byte) 0x82, (byte) 0x4f, (byte) 0x03, (byte) 0x82, (byte) 0x35, (byte) 0xc1, (byte) 0xa7, ++ (byte) 0x61, (byte) 0xe1, (byte) 0x9a, (byte) 0xe0, (byte) 0xd3, (byte) 0xe4, (byte) 0x80, ++ (byte) 0x4d, (byte) 0xf0, (byte) 0x69, (byte) 0x8a, (byte) 0xc8, (byte) 0x26, (byte) 0xf8, ++ (byte) 0x34, (byte) 0x52, (byte) 0x68, (byte) 0x13, (byte) 0x7c, (byte) 0x9a, (byte) 0x2f, ++ (byte) 0xb6, (byte) 0x09, (byte) 0x3e, (byte) 0x8d, (byte) 0x1b, (byte) 0xdc, (byte) 0x04, ++ (byte) 0x9f, (byte) 0x86, (byte) 0x8f, (byte) 0x6e, (byte) 0x82, (byte) 0x4f, (byte) 0xb3, ++ (byte) 0x88, (byte) 0x37, (byte) 0xc1, (byte) 0xa7, (byte) 0xd9, (byte) 0xe4, (byte) 0x9b, ++ (byte) 0xe0, (byte) 0xd3, (byte) 0x9e, (byte) 0x02, (byte) 0x4c, (byte) 0xf0, (byte) 0x69, ++ (byte) 0x6d, (byte) 0x09, (byte) 0x26, (byte) 0xf8, (byte) 0xb4, (byte) 0xc3, (byte) 0x08, ++ (byte) 0x13, (byte) 0x7c, (byte) 0x5a, (byte) 0x68, (byte) 0x86, (byte) 0x09, (byte) 0x3e, ++ (byte) 0xad, (byte) 0x37, (byte) 0xc4, (byte) 0x04, (byte) 0x9f, (byte) 0x56, (byte) 0x9d, ++ (byte) 0x62, (byte) 0x82, (byte) 0x4f, (byte) 0x9b, (byte) 0x8f, (byte) 0x31, (byte) 0xc1, ++ (byte) 0xa7, (byte) 0x2d, (byte) 0xe8, (byte) 0x98, (byte) 0xe0, (byte) 0xd3, (byte) 0x4a, ++ (byte) 0x84, (byte) 0x4c, (byte) 0xf0, (byte) 0x69, (byte) 0x3f, (byte) 0x4a, (byte) 0x26, ++ (byte) 0xf8, (byte) 0xb4, (byte) 0x2c, (byte) 0x29, (byte) 0x13, (byte) 0x7c, (byte) 0xda, ++ (byte) 0x9c, (byte) 0x96, (byte) 0x09, (byte) 0x3e, (byte) 0x2d, (byte) 0x52, (byte) 0xcc, ++ (byte) 0x04, (byte) 0x9f, (byte) 0xb6, (byte) 0xaa, (byte) 0x66, (byte) 0x82, (byte) 0x4f, ++ (byte) 0x2b, (byte) 0x96, (byte) 0x33, (byte) 0xc1, (byte) 0xa7, (byte) 0x7d, (byte) 0xeb, ++ (byte) 0x99, (byte) 0xe0, (byte) 0xd3, (byte) 0xf6, (byte) 0x05, (byte) 0x4d, (byte) 0xf0, ++ (byte) 0x69, (byte) 0x17, (byte) 0x8b, (byte) 0x26, (byte) 0xf8, (byte) 0xb4, (byte) 0x97, ++ (byte) 0x49, (byte) 0x13, (byte) 0x7c, (byte) 0xda, (byte) 0xd1, (byte) 0xa6, (byte) 0x09, ++ (byte) 0x3e, (byte) 0x2d, (byte) 0x6c, (byte) 0xd4, (byte) 0x04, (byte) 0x9f, (byte) 0xb6, ++ (byte) 0xb7, (byte) 0x6a, (byte) 0x82, (byte) 0x4f, (byte) 0xab, (byte) 0x9c, (byte) 0x35, ++ (byte) 0xc1, (byte) 0xa7, (byte) 0xc5, (byte) 0xee, (byte) 0x9a, (byte) 0xe0, (byte) 0xd3, ++ (byte) 0x9a, (byte) 0x87, (byte) 0x4d, (byte) 0xf0, (byte) 0x69, (byte) 0xe9, (byte) 0xcb ++ }; ++ checkSynth( ++ /* ++ * // The stream consists of word "time" with all possible transforms. ++ * main_header ++ * metablock_header_easy: 1533, 1 ++ * command_easy: 10, "|", 2 // = 0 << 10 + 1 + 1 ++ * command_easy: 10, "|", 1037 // = 1 << 10 + 1 + 12 ++ * command_easy: 10, "|", 2073 // = 2 << 10 + 1 + 24 ++ * command_easy: 10, "|", 3110 // = 3 << 10 + 1 + 37 ++ * command_easy: 10, "|", 4144 // = 4 << 10 + 1 + 47 ++ * command_easy: 10, "|", 5180 // = 5 << 10 + 1 + 59 ++ * command_easy: 10, "|", 6220 // = 6 << 10 + 1 + 75 ++ * command_easy: 10, "|", 7256 // = 7 << 10 + 1 + 87 ++ * command_easy: 10, "|", 8294 // = 8 << 10 + 1 + 101 ++ * command_easy: 10, "|", 9333 // = 9 << 10 + 1 + 116 ++ * command_easy: 10, "|", 10368 // = 10 << 10 + 1 + 127 ++ * command_easy: 10, "|", 11408 // = 11 << 10 + 1 + 143 ++ * command_easy: 10, "|", 12441 // = 12 << 10 + 1 + 152 ++ * command_easy: 10, "|", 13475 // = 13 << 10 + 1 + 162 ++ * command_easy: 10, "|", 14513 // = 14 << 10 + 1 + 176 ++ * command_easy: 10, "|", 15550 // = 15 << 10 + 1 + 189 ++ * command_easy: 10, "|", 16587 // = 16 << 10 + 1 + 202 ++ * command_easy: 10, "|", 17626 // = 17 << 10 + 1 + 217 ++ * command_easy: 10, "|", 18665 // = 18 << 10 + 1 + 232 ++ * command_easy: 10, "|", 19703 // = 19 << 10 + 1 + 246 ++ * command_easy: 10, "|", 20739 // = 20 << 10 + 1 + 258 ++ * command_easy: 10, "|", 21775 // = 21 << 10 + 1 + 270 ++ * command_easy: 10, "|", 22812 // = 22 << 10 + 1 + 283 ++ * command_easy: 10, "|", 23848 // = 23 << 10 + 1 + 295 ++ * command_easy: 10, "|", 24880 // = 24 << 10 + 1 + 303 ++ * command_easy: 10, "|", 25916 // = 25 << 10 + 1 + 315 ++ * command_easy: 10, "|", 26956 // = 26 << 10 + 1 + 331 ++ * command_easy: 10, "|", 27988 // = 27 << 10 + 1 + 339 ++ * command_easy: 10, "|", 29021 // = 28 << 10 + 1 + 348 ++ * command_easy: 10, "|", 30059 // = 29 << 10 + 1 + 362 ++ * command_easy: 10, "|", 31100 // = 30 << 10 + 1 + 379 ++ * command_easy: 10, "|", 32136 // = 31 << 10 + 1 + 391 ++ * command_easy: 10, "|", 33173 // = 32 << 10 + 1 + 404 ++ * command_easy: 10, "|", 34209 // = 33 << 10 + 1 + 416 ++ * command_easy: 10, "|", 35247 // = 34 << 10 + 1 + 430 ++ * command_easy: 10, "|", 36278 // = 35 << 10 + 1 + 437 ++ * command_easy: 10, "|", 37319 // = 36 << 10 + 1 + 454 ++ * command_easy: 10, "|", 38355 // = 37 << 10 + 1 + 466 ++ * command_easy: 10, "|", 39396 // = 38 << 10 + 1 + 483 ++ * command_easy: 10, "|", 40435 // = 39 << 10 + 1 + 498 ++ * command_easy: 10, "|", 41465 // = 40 << 10 + 1 + 504 ++ * command_easy: 10, "|", 42494 // = 41 << 10 + 1 + 509 ++ * command_easy: 10, "|", 43534 // = 42 << 10 + 1 + 525 ++ * command_easy: 10, "|", 44565 // = 43 << 10 + 1 + 532 ++ * command_easy: 10, "|", 45606 // = 44 << 10 + 1 + 549 ++ * command_easy: 10, "|", 46641 // = 45 << 10 + 1 + 560 ++ * command_easy: 10, "|", 47680 // = 46 << 10 + 1 + 575 ++ * command_easy: 10, "|", 48719 // = 47 << 10 + 1 + 590 ++ * command_easy: 10, "|", 49758 // = 48 << 10 + 1 + 605 ++ * command_easy: 10, "|", 50786 // = 49 << 10 + 1 + 609 ++ * command_easy: 10, "|", 51824 // = 50 << 10 + 1 + 623 ++ * command_easy: 10, "|", 52861 // = 51 << 10 + 1 + 636 ++ * command_easy: 10, "|", 53897 // = 52 << 10 + 1 + 648 ++ * command_easy: 10, "|", 54935 // = 53 << 10 + 1 + 662 ++ * command_easy: 10, "|", 55973 // = 54 << 10 + 1 + 676 ++ * command_easy: 10, "|", 56999 // = 55 << 10 + 1 + 678 ++ * command_easy: 10, "|", 58027 // = 56 << 10 + 1 + 682 ++ * command_easy: 10, "|", 59056 // = 57 << 10 + 1 + 687 ++ * command_easy: 10, "|", 60092 // = 58 << 10 + 1 + 699 ++ * command_easy: 10, "|", 61129 // = 59 << 10 + 1 + 712 ++ * command_easy: 10, "|", 62156 // = 60 << 10 + 1 + 715 ++ * command_easy: 10, "|", 63195 // = 61 << 10 + 1 + 730 ++ * command_easy: 10, "|", 64233 // = 62 << 10 + 1 + 744 ++ * command_easy: 10, "|", 65277 // = 63 << 10 + 1 + 764 ++ * command_easy: 10, "|", 66307 // = 64 << 10 + 1 + 770 ++ * command_easy: 10, "|", 67333 // = 65 << 10 + 1 + 772 ++ * command_easy: 10, "|", 68371 // = 66 << 10 + 1 + 786 ++ * command_easy: 10, "|", 69407 // = 67 << 10 + 1 + 798 ++ * command_easy: 10, "|", 70444 // = 68 << 10 + 1 + 811 ++ * command_easy: 10, "|", 71480 // = 69 << 10 + 1 + 823 ++ * command_easy: 10, "|", 72517 // = 70 << 10 + 1 + 836 ++ * command_easy: 10, "|", 73554 // = 71 << 10 + 1 + 849 ++ * command_easy: 10, "|", 74591 // = 72 << 10 + 1 + 862 ++ * command_easy: 10, "|", 75631 // = 73 << 10 + 1 + 878 ++ * command_easy: 10, "|", 76679 // = 74 << 10 + 1 + 902 ++ * command_easy: 10, "|", 77715 // = 75 << 10 + 1 + 914 ++ * command_easy: 10, "|", 78757 // = 76 << 10 + 1 + 932 ++ * command_easy: 10, "|", 79793 // = 77 << 10 + 1 + 944 ++ * command_easy: 10, "|", 80830 // = 78 << 10 + 1 + 957 ++ * command_easy: 10, "|", 81866 // = 79 << 10 + 1 + 969 ++ * command_easy: 10, "|", 82902 // = 80 << 10 + 1 + 981 ++ * command_easy: 10, "|", 83942 // = 81 << 10 + 1 + 997 ++ * command_easy: 10, "|", 84980 // = 82 << 10 + 1 + 1011 ++ * command_easy: 10, "|", 86018 // = 83 << 10 + 1 + 1025 ++ * command_easy: 10, "|", 87055 // = 84 << 10 + 1 + 1038 ++ * command_easy: 10, "|", 88093 // = 85 << 10 + 1 + 1052 ++ * command_easy: 10, "|", 89129 // = 86 << 10 + 1 + 1064 ++ * command_easy: 10, "|", 90166 // = 87 << 10 + 1 + 1077 ++ * command_easy: 10, "|", 91202 // = 88 << 10 + 1 + 1089 ++ * command_easy: 10, "|", 92239 // = 89 << 10 + 1 + 1102 ++ * command_easy: 10, "|", 93276 // = 90 << 10 + 1 + 1115 ++ * command_easy: 10, "|", 94315 // = 91 << 10 + 1 + 1130 ++ * command_easy: 10, "|", 95353 // = 92 << 10 + 1 + 1144 ++ * command_easy: 10, "|", 96392 // = 93 << 10 + 1 + 1159 ++ * command_easy: 10, "|", 97432 // = 94 << 10 + 1 + 1175 ++ * command_easy: 10, "|", 98468 // = 95 << 10 + 1 + 1187 ++ * command_easy: 10, "|", 99507 // = 96 << 10 + 1 + 1202 ++ * command_easy: 10, "|", 100544 // = 97 << 10 + 1 + 1215 ++ * command_easy: 10, "|", 101581 // = 98 << 10 + 1 + 1228 ++ * command_easy: 10, "|", 102619 // = 99 << 10 + 1 + 1242 ++ * command_easy: 10, "|", 103655 // = 100 << 10 + 1 + 1254 ++ * command_easy: 10, "|", 104694 // = 101 << 10 + 1 + 1269 ++ * command_easy: 10, "|", 105730 // = 102 << 10 + 1 + 1281 ++ * command_easy: 10, "|", 106767 // = 103 << 10 + 1 + 1294 ++ * command_easy: 10, "|", 107804 // = 104 << 10 + 1 + 1307 ++ * command_easy: 10, "|", 108841 // = 105 << 10 + 1 + 1320 ++ * command_easy: 10, "|", 109878 // = 106 << 10 + 1 + 1333 ++ * command_easy: 10, "|", 110917 // = 107 << 10 + 1 + 1348 ++ * command_easy: 10, "|", 111954 // = 108 << 10 + 1 + 1361 ++ * command_easy: 10, "|", 112991 // = 109 << 10 + 1 + 1374 ++ * command_easy: 10, "|", 114028 // = 110 << 10 + 1 + 1387 ++ * command_easy: 10, "|", 115066 // = 111 << 10 + 1 + 1401 ++ * command_easy: 10, "|", 116104 // = 112 << 10 + 1 + 1415 ++ * command_easy: 10, "|", 117140 // = 113 << 10 + 1 + 1427 ++ * command_easy: 10, "|", 118176 // = 114 << 10 + 1 + 1439 ++ * command_easy: 10, "|", 119213 // = 115 << 10 + 1 + 1452 ++ * command_easy: 10, "|", 120250 // = 116 << 10 + 1 + 1465 ++ * command_easy: 10, "|", 121287 // = 117 << 10 + 1 + 1478 ++ * command_easy: 10, "|", 122325 // = 118 << 10 + 1 + 1492 ++ * command_easy: 10, "|", 123363 // = 119 << 10 + 1 + 1506 ++ * command_easy: 10, "|", 124401 // = 120 << 10 + 1 + 1520 ++ */ ++ compressed, ++ true, ++ "|categories|categories | categories |ategories|Categories |categories the | categories|s cat" ++ + "egories |categories of |Categories|categories and |tegories|categorie|, categories |catego" ++ + "ries, | Categories |categories in |categories to |e categories |categories\"|categories.|c" ++ + "ategories\">|categories\n|categor|categories]|categories for |egories|categori|categories " ++ + "a |categories that | Categories|categories. |.categories| categories, |gories|categories w" ++ + "ith |categories'|categories from |categories by |ories|ries| the categories|catego|categor" ++ + "ies. The |CATEGORIES|categories on |categories as |categories is |cat|categorieing |catego" ++ + "ries\n\t|categories:| categories. |categoriesed |s|ies|cate|categories(|Categories, |ca|ca" ++ + "tegories at |categoriesly | the categories of |categ|c| Categories, |Categories\"|.categor" ++ + "ies(|CATEGORIES |Categories\">|categories=\"| categories.|.com/categories| the categories " ++ + "of the |Categories'|categories. This |categories,|.categories |Categories(|Categories.|cat" ++ + "egories not | categories=\"|categorieser | CATEGORIES |categoriesal | CATEGORIES|categorie" ++ + "s='|CATEGORIES\"|Categories. | categories(|categoriesful | Categories. |categoriesive |cat" ++ + "egoriesless |CATEGORIES'|categoriesest | Categories.|CATEGORIES\">| categories='|Categorie" ++ + "s,|categoriesize |CATEGORIES.|\302\240categories| categories,|Categories=\"|CATEGORIES=" ++ + "\"|categoriesous |CATEGORIES, |Categories='| Categories,| CATEGORIES=\"| CATEGORIES, |CATE" ++ + "GORIES,|CATEGORIES(|CATEGORIES. | CATEGORIES.|CATEGORIES='| CATEGORIES. | Categories=\"| C" ++ + "ATEGORIES='| Categories='" ++ ); ++ } ++ ++ @Test ++ public void testAllTransforms4() { ++ byte[] compressed = { ++ (byte) 0x1b, (byte) 0x40, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, ++ (byte) 0xe3, (byte) 0xb4, (byte) 0x0d, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x5b, ++ (byte) 0x26, (byte) 0x31, (byte) 0x40, (byte) 0x02, (byte) 0x00, (byte) 0xe0, (byte) 0x4e, ++ (byte) 0x1b, (byte) 0x51, (byte) 0x3e, (byte) 0x42, (byte) 0x51, (byte) 0x3e, (byte) 0x81, ++ (byte) 0x02, (byte) 0x51, (byte) 0x3e, (byte) 0x11, (byte) 0x04, (byte) 0xa2, (byte) 0x7c, ++ (byte) 0xe2, (byte) 0x0b, (byte) 0x44, (byte) 0xf9, (byte) 0x24, (byte) 0x1b, (byte) 0x10, ++ (byte) 0xe5, (byte) 0x93, (byte) 0x84, (byte) 0x50, (byte) 0x94, (byte) 0x4f, (byte) 0xba, ++ (byte) 0x02, (byte) 0x51, (byte) 0x3e, (byte) 0x69, (byte) 0x0c, (byte) 0x45, (byte) 0xf9, ++ (byte) 0x64, (byte) 0x39, (byte) 0x20, (byte) 0xca, (byte) 0x27, (byte) 0x13, (byte) 0x22, ++ (byte) 0x51, (byte) 0x3e, (byte) 0xd9, (byte) 0x11, (byte) 0x8a, (byte) 0xf2, (byte) 0xc9, ++ (byte) 0xa2, (byte) 0x58, (byte) 0x94, (byte) 0x4f, (byte) 0x4e, (byte) 0x05, (byte) 0xa2, ++ (byte) 0x7c, (byte) 0x72, (byte) 0x2c, (byte) 0x12, (byte) 0xe5, (byte) 0x93, (byte) 0x83, ++ (byte) 0xa1, (byte) 0x28, (byte) 0x9f, (byte) 0xfc, (byte) 0x8c, (byte) 0x45, (byte) 0xf9, ++ (byte) 0x14, (byte) 0x6e, (byte) 0x40, (byte) 0x94, (byte) 0x4f, (byte) 0x71, (byte) 0x47, ++ (byte) 0x44, (byte) 0xf9, (byte) 0x14, (byte) 0x80, (byte) 0x48, (byte) 0x94, (byte) 0x4f, ++ (byte) 0x81, (byte) 0xc8, (byte) 0x44, (byte) 0xf9, (byte) 0x14, (byte) 0x8e, (byte) 0x50, ++ (byte) 0x94, (byte) 0x4f, (byte) 0x41, (byte) 0x49, (byte) 0x45, (byte) 0xf9, (byte) 0x14, ++ (byte) 0x9b, (byte) 0x58, (byte) 0x94, (byte) 0x4f, (byte) 0x11, (byte) 0xca, (byte) 0x45, ++ (byte) 0xf9, (byte) 0x94, (byte) 0xa3, (byte) 0x40, (byte) 0x94, (byte) 0x4f, (byte) 0x99, ++ (byte) 0x4a, (byte) 0x44, (byte) 0xf9, (byte) 0x94, (byte) 0xb3, (byte) 0x48, (byte) 0x94, ++ (byte) 0x4f, (byte) 0x59, (byte) 0xcb, (byte) 0x44, (byte) 0xf9, (byte) 0x94, (byte) 0xb8, ++ (byte) 0x50, (byte) 0x94, (byte) 0x4f, (byte) 0x09, (byte) 0x4c, (byte) 0x45, (byte) 0xf9, ++ (byte) 0x94, (byte) 0xcb, (byte) 0x58, (byte) 0x94, (byte) 0x4f, (byte) 0x19, (byte) 0xcd, ++ (byte) 0x45, (byte) 0xf9, (byte) 0x54, (byte) 0xd8, (byte) 0x80, (byte) 0x28, (byte) 0x9f, ++ (byte) 0xca, (byte) 0x9b, (byte) 0x10, (byte) 0xe5, (byte) 0x53, (byte) 0x99, (byte) 0x23, ++ (byte) 0xa2, (byte) 0x7c, (byte) 0xaa, (byte) 0x73, (byte) 0x46, (byte) 0x94, (byte) 0x4f, ++ (byte) 0x25, (byte) 0x0f, (byte) 0x89, (byte) 0xf2, (byte) 0xa9, (byte) 0xf0, (byte) 0x29, ++ (byte) 0x51, (byte) 0x3e, (byte) 0xd5, (byte) 0x40, (byte) 0x26, (byte) 0xca, (byte) 0xa7, ++ (byte) 0x62, (byte) 0xe8, (byte) 0x44, (byte) 0xf9, (byte) 0x54, (byte) 0x0d, (byte) 0xa1, ++ (byte) 0x28, (byte) 0x9f, (byte) 0xca, (byte) 0xa1, (byte) 0x14, (byte) 0xe5, (byte) 0x53, ++ (byte) 0x61, (byte) 0xa4, (byte) 0xa2, (byte) 0x7c, (byte) 0xaa, (byte) 0x8c, (byte) 0x56, ++ (byte) 0x94, (byte) 0x4f, (byte) 0x45, (byte) 0x12, (byte) 0x8b, (byte) 0xf2, (byte) 0xa9, ++ (byte) 0x52, (byte) 0x6a, (byte) 0x51, (byte) 0x3e, (byte) 0x95, (byte) 0x4c, (byte) 0x2e, ++ (byte) 0xca, (byte) 0xa7, (byte) 0xda, (byte) 0xe9, (byte) 0x45, (byte) 0xf9, (byte) 0xd4, ++ (byte) 0x44, (byte) 0x81, (byte) 0x28, (byte) 0x9f, (byte) 0xba, (byte) 0xa8, (byte) 0x10, ++ (byte) 0xe5, (byte) 0x53, (byte) 0x37, (byte) 0x25, (byte) 0xa2, (byte) 0x7c, (byte) 0x6a, ++ (byte) 0xaa, (byte) 0x46, (byte) 0x94, (byte) 0x4f, (byte) 0xad, (byte) 0x15, (byte) 0x89, ++ (byte) 0xf2, (byte) 0xa9, (byte) 0xc5, (byte) 0x2a, (byte) 0x51, (byte) 0x3e, (byte) 0xb5, ++ (byte) 0x5a, (byte) 0x26, (byte) 0xca, (byte) 0xa7, (byte) 0x5e, (byte) 0xeb, (byte) 0x44, ++ (byte) 0xf9, (byte) 0xd4, (byte) 0x6c, (byte) 0xa1, (byte) 0x28, (byte) 0x9f, (byte) 0xba, ++ (byte) 0xad, (byte) 0x14, (byte) 0xe5, (byte) 0x53, (byte) 0xcf, (byte) 0xa5, (byte) 0xa2, ++ (byte) 0x7c, (byte) 0x6a, (byte) 0xbd, (byte) 0x56, (byte) 0x94, (byte) 0x4f, (byte) 0xbd, ++ (byte) 0x17, (byte) 0x8b, (byte) 0xf2, (byte) 0xa9, (byte) 0x09, (byte) 0x6b, (byte) 0x51, ++ (byte) 0x3e, (byte) 0x35, (byte) 0x63, (byte) 0x2e, (byte) 0xca, (byte) 0xa7, (byte) 0xd6, ++ (byte) 0xec, (byte) 0x45, (byte) 0xf9, (byte) 0x34, (byte) 0x9b, (byte) 0x01, (byte) 0x51, ++ (byte) 0x3e, (byte) 0x0d, (byte) 0x67, (byte) 0x41, (byte) 0x94, (byte) 0x4f, (byte) 0x43, ++ (byte) 0x9a, (byte) 0x10, (byte) 0xe5, (byte) 0xd3, (byte) 0xa8, (byte) 0x36, (byte) 0x44, ++ (byte) 0xf9, (byte) 0x34, (byte) 0xb1, (byte) 0x11, (byte) 0x51, (byte) 0x3e, (byte) 0xcd, ++ (byte) 0x6d, (byte) 0x45, (byte) 0x94, (byte) 0x4f, (byte) 0xe3, (byte) 0x9b, (byte) 0x11, ++ (byte) 0xe5, (byte) 0xd3, (byte) 0x14, (byte) 0x77, (byte) 0x44, (byte) 0xf9, (byte) 0x34, ++ (byte) 0xcc, (byte) 0x21, (byte) 0x51, (byte) 0x3e, (byte) 0x8d, (byte) 0x75, (byte) 0x49, ++ (byte) 0x94, (byte) 0x4f, (byte) 0x83, (byte) 0x9e, (byte) 0x12, (byte) 0xe5, (byte) 0xd3, ++ (byte) 0xb8, (byte) 0xb7, (byte) 0x44, (byte) 0xf9, (byte) 0x34, (byte) 0xfa, (byte) 0x31, ++ (byte) 0x51, (byte) 0x3e, (byte) 0x0d, (byte) 0x80, (byte) 0x4d, (byte) 0x94, (byte) 0x4f, ++ (byte) 0x73, (byte) 0xa0, (byte) 0x13, (byte) 0xe5, (byte) 0xd3, (byte) 0x34, (byte) 0xf8, ++ (byte) 0x44, (byte) 0xf9, (byte) 0x34, (byte) 0x13, (byte) 0x42, (byte) 0x51, (byte) 0x3e, ++ (byte) 0x4d, (byte) 0x87, (byte) 0x51, (byte) 0x94, (byte) 0x4f, (byte) 0x53, (byte) 0xa2, ++ (byte) 0x14, (byte) 0xe5, (byte) 0xd3, (byte) 0xb4, (byte) 0x38, (byte) 0x45, (byte) 0xf9, ++ (byte) 0x34, (byte) 0x34, (byte) 0x52, (byte) 0x51, (byte) 0x3e, (byte) 0x0d, (byte) 0x8f, ++ (byte) 0x55, (byte) 0x94, (byte) 0x4f, (byte) 0x23, (byte) 0xa4, (byte) 0x15, (byte) 0xe5, ++ (byte) 0xd3, (byte) 0x24, (byte) 0x79, (byte) 0x45, (byte) 0xf9, (byte) 0x34, (byte) 0x4f, ++ (byte) 0x62, (byte) 0x51, (byte) 0x3e, (byte) 0x8d, (byte) 0x95, (byte) 0x59, (byte) 0x94, ++ (byte) 0x4f, (byte) 0xd3, (byte) 0xa5, (byte) 0x16, (byte) 0xe5, (byte) 0xd3, (byte) 0x98, ++ (byte) 0xb9, (byte) 0x45, (byte) 0xf9, (byte) 0x34, (byte) 0x6e, (byte) 0x72, (byte) 0x51, ++ (byte) 0x3e, (byte) 0xcd, (byte) 0x9d, (byte) 0x5d, (byte) 0x94, (byte) 0x4f, (byte) 0x13, ++ (byte) 0xa8, (byte) 0x17, (byte) 0xe5, (byte) 0xd3, (byte) 0x1c, (byte) 0xfa, (byte) 0x45, ++ (byte) 0xf9, (byte) 0xb4, (byte) 0x90, (byte) 0x02, (byte) 0x51, (byte) 0x3e, (byte) 0xed, ++ (byte) 0xa5, (byte) 0x41, (byte) 0x94, (byte) 0x4f, (byte) 0xeb, (byte) 0xa9, (byte) 0x10, ++ (byte) 0xe5, (byte) 0xd3, (byte) 0x9a, (byte) 0x3a, (byte) 0x44, (byte) 0xf9, (byte) 0xb4, ++ (byte) 0xac, (byte) 0x12, (byte) 0x51, (byte) 0x3e, (byte) 0x6d, (byte) 0xad, (byte) 0x45, ++ (byte) 0x94, (byte) 0x4f, (byte) 0xbb, (byte) 0xab, (byte) 0x11, (byte) 0xe5, (byte) 0xd3, ++ (byte) 0x0a, (byte) 0x7b, (byte) 0x44, (byte) 0xf9, (byte) 0xb4, (byte) 0xc9, (byte) 0x22, ++ (byte) 0x51, (byte) 0x3e, (byte) 0x2d, (byte) 0xb4, (byte) 0x49, (byte) 0x94, (byte) 0x4f, ++ (byte) 0x7b, (byte) 0xad, (byte) 0x12, (byte) 0xe5, (byte) 0xd3, (byte) 0x82, (byte) 0xbb, ++ (byte) 0x44, (byte) 0xf9, (byte) 0xb4, (byte) 0xe7, (byte) 0x32, (byte) 0x51, (byte) 0x3e, ++ (byte) 0xad, (byte) 0xbb, (byte) 0x4d, (byte) 0x94, (byte) 0x4f, (byte) 0x5b, (byte) 0xaf, ++ (byte) 0x13, (byte) 0xe5, (byte) 0xd3, (byte) 0xf6, (byte) 0xfb, (byte) 0x44, (byte) 0xf9, ++ (byte) 0xb4, (byte) 0x05, (byte) 0x43, (byte) 0x51, (byte) 0x3e, (byte) 0xed, (byte) 0xc2, ++ (byte) 0x51, (byte) 0x94, (byte) 0x4f, (byte) 0x1b, (byte) 0xb1, (byte) 0x14, (byte) 0xe5, ++ (byte) 0xd3, (byte) 0x62, (byte) 0x3c, (byte) 0x45, (byte) 0xf9, (byte) 0xb4, (byte) 0x1f, ++ (byte) 0x53, (byte) 0x51, (byte) 0x3e, (byte) 0xad, (byte) 0xc9, (byte) 0x55, (byte) 0x94, ++ (byte) 0x4f, (byte) 0xeb, (byte) 0xb2, (byte) 0x15, (byte) 0xe5, (byte) 0xd3, (byte) 0xda, ++ (byte) 0x7c, (byte) 0x45, (byte) 0xf9, (byte) 0xb4, (byte) 0x3e, (byte) 0x63 ++ }; ++ checkSynth( ++ /* ++ * // The stream consists of word "time" with all possible transforms. ++ * main_header ++ * metablock_header_easy: 833, 1 ++ * command_easy: 4, "|", 2 // = 0 << 10 + 1 + 1 ++ * command_easy: 4, "|", 1031 // = 1 << 10 + 1 + 6 ++ * command_easy: 4, "|", 2061 // = 2 << 10 + 1 + 12 ++ * command_easy: 4, "|", 3092 // = 3 << 10 + 1 + 19 ++ * command_easy: 4, "|", 4120 // = 4 << 10 + 1 + 23 ++ * command_easy: 4, "|", 5150 // = 5 << 10 + 1 + 29 ++ * command_easy: 4, "|", 6184 // = 6 << 10 + 1 + 39 ++ * command_easy: 4, "|", 7214 // = 7 << 10 + 1 + 45 ++ * command_easy: 4, "|", 8246 // = 8 << 10 + 1 + 53 ++ * command_easy: 4, "|", 9279 // = 9 << 10 + 1 + 62 ++ * command_easy: 4, "|", 10308 // = 10 << 10 + 1 + 67 ++ * command_easy: 4, "|", 11342 // = 11 << 10 + 1 + 77 ++ * command_easy: 4, "|", 12369 // = 12 << 10 + 1 + 80 ++ * command_easy: 4, "|", 13397 // = 13 << 10 + 1 + 84 ++ * command_easy: 4, "|", 14429 // = 14 << 10 + 1 + 92 ++ * command_easy: 4, "|", 15460 // = 15 << 10 + 1 + 99 ++ * command_easy: 4, "|", 16491 // = 16 << 10 + 1 + 106 ++ * command_easy: 4, "|", 17524 // = 17 << 10 + 1 + 115 ++ * command_easy: 4, "|", 18557 // = 18 << 10 + 1 + 124 ++ * command_easy: 4, "|", 19589 // = 19 << 10 + 1 + 132 ++ * command_easy: 4, "|", 20619 // = 20 << 10 + 1 + 138 ++ * command_easy: 4, "|", 21649 // = 21 << 10 + 1 + 144 ++ * command_easy: 4, "|", 22680 // = 22 << 10 + 1 + 151 ++ * command_easy: 4, "|", 23710 // = 23 << 10 + 1 + 157 ++ * command_easy: 4, "|", 24736 // = 24 << 10 + 1 + 159 ++ * command_easy: 4, "|", 25766 // = 25 << 10 + 1 + 165 ++ * command_easy: 4, "|", 26800 // = 26 << 10 + 1 + 175 ++ * command_easy: 4, "|", 27826 // = 27 << 10 + 1 + 177 ++ * command_easy: 4, "|", 28853 // = 28 << 10 + 1 + 180 ++ * command_easy: 4, "|", 29885 // = 29 << 10 + 1 + 188 ++ * command_easy: 4, "|", 30920 // = 30 << 10 + 1 + 199 ++ * command_easy: 4, "|", 31950 // = 31 << 10 + 1 + 205 ++ * command_easy: 4, "|", 32981 // = 32 << 10 + 1 + 212 ++ * command_easy: 4, "|", 34011 // = 33 << 10 + 1 + 218 ++ * command_easy: 4, "|", 35043 // = 34 << 10 + 1 + 226 ++ * command_easy: 4, "|", 36068 // = 35 << 10 + 1 + 227 ++ * command_easy: 4, "|", 37103 // = 36 << 10 + 1 + 238 ++ * command_easy: 4, "|", 38133 // = 37 << 10 + 1 + 244 ++ * command_easy: 4, "|", 39168 // = 38 << 10 + 1 + 255 ++ * command_easy: 4, "|", 40201 // = 39 << 10 + 1 + 264 ++ * command_easy: 4, "|", 41226 // = 40 << 10 + 1 + 265 ++ * command_easy: 4, "|", 42251 // = 41 << 10 + 1 + 266 ++ * command_easy: 4, "|", 43285 // = 42 << 10 + 1 + 276 ++ * command_easy: 4, "|", 44310 // = 43 << 10 + 1 + 277 ++ * command_easy: 4, "|", 45345 // = 44 << 10 + 1 + 288 ++ * command_easy: 4, "|", 46374 // = 45 << 10 + 1 + 293 ++ * command_easy: 4, "|", 47407 // = 46 << 10 + 1 + 302 ++ * command_easy: 4, "|", 48440 // = 47 << 10 + 1 + 311 ++ * command_easy: 4, "|", 49473 // = 48 << 10 + 1 + 320 ++ * command_easy: 4, "|", 50498 // = 49 << 10 + 1 + 321 ++ * command_easy: 4, "|", 51530 // = 50 << 10 + 1 + 329 ++ * command_easy: 4, "|", 52561 // = 51 << 10 + 1 + 336 ++ * command_easy: 4, "|", 53591 // = 52 << 10 + 1 + 342 ++ * command_easy: 4, "|", 54623 // = 53 << 10 + 1 + 350 ++ * command_easy: 4, "|", 55655 // = 54 << 10 + 1 + 358 ++ * command_easy: 4, "|", 56680 // = 55 << 10 + 1 + 359 ++ * command_easy: 4, "|", 57705 // = 56 << 10 + 1 + 360 ++ * command_easy: 4, "|", 58730 // = 57 << 10 + 1 + 361 ++ * command_easy: 4, "|", 59760 // = 58 << 10 + 1 + 367 ++ * command_easy: 4, "|", 60791 // = 59 << 10 + 1 + 374 ++ * command_easy: 4, "|", 61816 // = 60 << 10 + 1 + 375 ++ * command_easy: 4, "|", 62849 // = 61 << 10 + 1 + 384 ++ * command_easy: 4, "|", 63881 // = 62 << 10 + 1 + 392 ++ * command_easy: 4, "|", 64919 // = 63 << 10 + 1 + 406 ++ * command_easy: 4, "|", 65944 // = 64 << 10 + 1 + 407 ++ * command_easy: 4, "|", 66969 // = 65 << 10 + 1 + 408 ++ * command_easy: 4, "|", 68001 // = 66 << 10 + 1 + 416 ++ * command_easy: 4, "|", 69031 // = 67 << 10 + 1 + 422 ++ * command_easy: 4, "|", 70062 // = 68 << 10 + 1 + 429 ++ * command_easy: 4, "|", 71092 // = 69 << 10 + 1 + 435 ++ * command_easy: 4, "|", 72123 // = 70 << 10 + 1 + 442 ++ * command_easy: 4, "|", 73154 // = 71 << 10 + 1 + 449 ++ * command_easy: 4, "|", 74185 // = 72 << 10 + 1 + 456 ++ * command_easy: 4, "|", 75219 // = 73 << 10 + 1 + 466 ++ * command_easy: 4, "|", 76261 // = 74 << 10 + 1 + 484 ++ * command_easy: 4, "|", 77291 // = 75 << 10 + 1 + 490 ++ * command_easy: 4, "|", 78327 // = 76 << 10 + 1 + 502 ++ * command_easy: 4, "|", 79357 // = 77 << 10 + 1 + 508 ++ * command_easy: 4, "|", 80388 // = 78 << 10 + 1 + 515 ++ * command_easy: 4, "|", 81418 // = 79 << 10 + 1 + 521 ++ * command_easy: 4, "|", 82448 // = 80 << 10 + 1 + 527 ++ * command_easy: 4, "|", 83482 // = 81 << 10 + 1 + 537 ++ * command_easy: 4, "|", 84514 // = 82 << 10 + 1 + 545 ++ * command_easy: 4, "|", 85546 // = 83 << 10 + 1 + 553 ++ * command_easy: 4, "|", 86577 // = 84 << 10 + 1 + 560 ++ * command_easy: 4, "|", 87609 // = 85 << 10 + 1 + 568 ++ * command_easy: 4, "|", 88639 // = 86 << 10 + 1 + 574 ++ * command_easy: 4, "|", 89670 // = 87 << 10 + 1 + 581 ++ * command_easy: 4, "|", 90700 // = 88 << 10 + 1 + 587 ++ * command_easy: 4, "|", 91731 // = 89 << 10 + 1 + 594 ++ * command_easy: 4, "|", 92762 // = 90 << 10 + 1 + 601 ++ * command_easy: 4, "|", 93795 // = 91 << 10 + 1 + 610 ++ * command_easy: 4, "|", 94827 // = 92 << 10 + 1 + 618 ++ * command_easy: 4, "|", 95860 // = 93 << 10 + 1 + 627 ++ * command_easy: 4, "|", 96894 // = 94 << 10 + 1 + 637 ++ * command_easy: 4, "|", 97924 // = 95 << 10 + 1 + 643 ++ * command_easy: 4, "|", 98957 // = 96 << 10 + 1 + 652 ++ * command_easy: 4, "|", 99988 // = 97 << 10 + 1 + 659 ++ * command_easy: 4, "|", 101019 // = 98 << 10 + 1 + 666 ++ * command_easy: 4, "|", 102051 // = 99 << 10 + 1 + 674 ++ * command_easy: 4, "|", 103081 // = 100 << 10 + 1 + 680 ++ * command_easy: 4, "|", 104114 // = 101 << 10 + 1 + 689 ++ * command_easy: 4, "|", 105144 // = 102 << 10 + 1 + 695 ++ * command_easy: 4, "|", 106175 // = 103 << 10 + 1 + 702 ++ * command_easy: 4, "|", 107206 // = 104 << 10 + 1 + 709 ++ * command_easy: 4, "|", 108237 // = 105 << 10 + 1 + 716 ++ * command_easy: 4, "|", 109268 // = 106 << 10 + 1 + 723 ++ * command_easy: 4, "|", 110301 // = 107 << 10 + 1 + 732 ++ * command_easy: 4, "|", 111332 // = 108 << 10 + 1 + 739 ++ * command_easy: 4, "|", 112363 // = 109 << 10 + 1 + 746 ++ * command_easy: 4, "|", 113394 // = 110 << 10 + 1 + 753 ++ * command_easy: 4, "|", 114426 // = 111 << 10 + 1 + 761 ++ * command_easy: 4, "|", 115458 // = 112 << 10 + 1 + 769 ++ * command_easy: 4, "|", 116488 // = 113 << 10 + 1 + 775 ++ * command_easy: 4, "|", 117518 // = 114 << 10 + 1 + 781 ++ * command_easy: 4, "|", 118549 // = 115 << 10 + 1 + 788 ++ * command_easy: 4, "|", 119580 // = 116 << 10 + 1 + 795 ++ * command_easy: 4, "|", 120611 // = 117 << 10 + 1 + 802 ++ * command_easy: 4, "|", 121643 // = 118 << 10 + 1 + 810 ++ * command_easy: 4, "|", 122675 // = 119 << 10 + 1 + 818 ++ * command_easy: 4, "|", 123707 // = 120 << 10 + 1 + 826 ++ */ ++ compressed, ++ true, ++ "|time|time | time |ime|Time |time the | time|s time |time of |Time|time and |me|tim|, time |" ++ + "time, | Time |time in |time to |e time |time\"|time.|time\">|time\n|t|time]|time for |e|ti" ++ + "|time a |time that | Time|time. |.time| time, ||time with |time'|time from |time by ||| th" ++ + "e time||time. The |TIME|time on |time as |time is ||timing |time\n\t|time:| time. |timeed " ++ + "||||time(|Time, ||time at |timely | the time of ||| Time, |Time\"|.time(|TIME |Time\">|tim" ++ + "e=\"| time.|.com/time| the time of the |Time'|time. This |time,|.time |Time(|Time.|time no" ++ + "t | time=\"|timeer | TIME |timeal | TIME|time='|TIME\"|Time. | time(|timeful | Time. |time" ++ + "ive |timeless |TIME'|timeest | Time.|TIME\">| time='|Time,|timeize |TIME.|\302\240time| ti" ++ + "me,|Time=\"|TIME=\"|timeous |TIME, |Time='| Time,| TIME=\"| TIME, |TIME,|TIME(|TIME. | TIM" ++ + "E.|TIME='| TIME. | Time=\"| TIME='| Time='" ++ ); ++ } ++ + @Test + public void testBaseDictWord() { + byte[] compressed = { +@@ -446,6 +914,28 @@ public void testCopyLengthTooLong() { + ); + } + ++ @Test ++ public void testCopyTooLong() { ++ byte[] compressed = { ++ (byte) 0xa1, (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, ++ (byte) 0x1c, (byte) 0xa7, (byte) 0x6d, (byte) 0x00, (byte) 0x00, (byte) 0x38, (byte) 0xd8, ++ (byte) 0x32, (byte) 0x89, (byte) 0x01, (byte) 0x12, (byte) 0x00, (byte) 0x00, (byte) 0x77, ++ (byte) 0xda, (byte) 0x34, (byte) 0xab, (byte) 0xdb, (byte) 0x50, (byte) 0x00 ++ }; ++ checkSynth( ++ /* ++ * // Has a copy length that goes over the end of the meta-block, ++ * // with a ringbuffer wrap. ++ * main_header: 10 ++ * metablock_header_easy: 2, 1 ++ * command_easy: 1024, "a", 1 ++ */ ++ compressed, ++ false, ++ "" ++ ); ++ } ++ + @Test + public void testCustomHuffmanCode() { + byte[] compressed = { +@@ -2199,6 +2689,23 @@ public void testSimplePrefixOutOfRangeSymbols() { + ); + } + ++/* DISABLED: Java decoder does not tolerate extra input after the brotli stream. ++ @Test ++ public void testSimplePrefixPlusExtraData() { ++ byte[] compressed = { ++ (byte) 0x1b, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0xa0, (byte) 0xc3, (byte) 0xc4, ++ (byte) 0xc6, (byte) 0xc8, (byte) 0x02, (byte) 0x00, (byte) 0x70, (byte) 0xb0, (byte) 0x65, ++ (byte) 0x12, (byte) 0x03, (byte) 0x24, (byte) 0x00, (byte) 0x00, (byte) 0xee, (byte) 0xb4, ++ (byte) 0x51, (byte) 0xa0, (byte) 0x1d, (byte) 0x55, (byte) 0xaa ++ }; ++ checkSynth( ++ compressed, ++ true, ++ "abcd" ++ ); ++ } ++*/ ++ + @Test + public void testTooManySymbolsRepeated() { + byte[] compressed = { +@@ -2246,7 +2753,7 @@ public void testTransformedDictWord() { + * main_header + * metablock_header_easy: 9, 1 + * command_inscopy_easy: 0, 4 +- * command_dist_easy: 5121 ++ * command_dist_easy: 5121 // 5 << 10 + 1 + */ + compressed, + true, +@@ -2270,7 +2777,7 @@ public void testTransformedDictWordTooLong() { + * main_header + * metablock_header_easy: 4, 1 + * command_inscopy_easy: 0, 4 +- * command_dist_easy: 5121 ++ * command_dist_easy: 5121 // 5 << 10 + 1 + */ + compressed, + false, +@@ -2280,4 +2787,4 @@ public void testTransformedDictWordTooLong() { + + /* GENERATED CODE END */ + +-} +\ No newline at end of file ++} +diff --git a/java/org/brotli/dec/Transform.java b/java/org/brotli/dec/Transform.java +index b90f2e96..3279ee73 100644 +--- a/java/org/brotli/dec/Transform.java ++++ b/java/org/brotli/dec/Transform.java +@@ -10,13 +10,58 @@ + + /** + * Transformations on dictionary words. ++ * ++ * Transform descriptor is a triplet: {prefix, operator, suffix}. ++ * "prefix" and "suffix" are short strings inserted before and after transformed dictionary word. ++ * "operator" is applied to dictionary word itself. ++ * ++ * Some operators has "built-in" parameters, i.e. parameter is defined by operator ordinal. Other ++ * operators have "external" parameters, supplied via additional table encoded in shared dictionary. ++ * ++ * Operators: ++ * - IDENTITY (0): dictionary word is inserted "as is" ++ * - OMIT_LAST_N (1 - 9): last N octets of dictionary word are not inserted; N == ordinal ++ * - OMIT_FIRST_M (12-20): first M octets of dictionary word are not inserted; M == ordinal - 11 ++ * - UPPERCASE_FIRST (10): first "scalar" is XOR'ed with number 32 ++ * - UPPERCASE_ALL (11): all "scalars" are XOR'ed with number 32 ++ * - SHIFT_FIRST (21): first "scalar" is shifted by number form parameter table ++ * - SHIFT_ALL (22): all "scalar" is shifted by number form parameter table ++ * ++ * Here "scalar" is a variable length character coding similar to UTF-8 encoding. ++ * UPPERCASE_XXX / SHIFT_XXX operators were designed to change the case of UTF-8 encoded characters. ++ * While UPPERCASE_XXX works well only on ASCII charset, SHIFT is much more generic and could be ++ * used for most (all?) alphabets. + */ + final class Transform { + +- static final int NUM_TRANSFORMS = 121; +- private static final int[] TRANSFORMS = new int[NUM_TRANSFORMS * 3]; +- private static final byte[] PREFIX_SUFFIX = new byte[217]; +- private static final int[] PREFIX_SUFFIX_HEADS = new int[51]; ++ static final class Transforms { ++ final int numTransforms; ++ final int[] triplets; ++ final byte[] prefixSuffixStorage; ++ final int[] prefixSuffixHeads; ++ final short[] params; ++ ++ Transforms(int numTransforms, int prefixSuffixLen, int prefixSuffixCount) { ++ this.numTransforms = numTransforms; ++ this.triplets = new int[numTransforms * 3]; ++ this.params = new short[numTransforms]; ++ this.prefixSuffixStorage = new byte[prefixSuffixLen]; ++ this.prefixSuffixHeads = new int[prefixSuffixCount + 1]; ++ } ++ } ++ ++ static final int NUM_RFC_TRANSFORMS = 121; ++ static final Transforms RFC_TRANSFORMS = new Transforms(NUM_RFC_TRANSFORMS, 167, 50); ++ ++ private static final int OMIT_FIRST_LAST_LIMIT = 9; ++ ++ private static final int IDENTITY = 0; ++ private static final int OMIT_LAST_BASE = IDENTITY + 1 - 1; // there is no OMIT_LAST_0. ++ private static final int UPPERCASE_FIRST = OMIT_LAST_BASE + OMIT_FIRST_LAST_LIMIT + 1; ++ private static final int UPPERCASE_ALL = UPPERCASE_FIRST + 1; ++ private static final int OMIT_FIRST_BASE = UPPERCASE_ALL + 1 - 1; // there is no OMIT_FIRST_0. ++ private static final int SHIFT_FIRST = OMIT_FIRST_BASE + OMIT_FIRST_LAST_LIMIT + 1; ++ private static final int SHIFT_ALL = SHIFT_FIRST + 1; + + // Bundle of 0-terminated strings. + private static final String PREFIX_SUFFIX_SRC = "# #s #, #e #.# the #.com/#\u00C2\u00A0# of # and" +@@ -29,71 +74,87 @@ + + " G% ! *A *% H! D I!+! J!+ K +- *4! A L!*4 M N +6 O!*% +.! K *G P +%( ! G *D +D " + + " Q +# *K!*G!+D!+# +G +A +4!+% +K!+4!*D!+K!*K"; + +- private static void unpackTransforms(byte[] prefixSuffix, int[] prefixSuffixHeads, +- int[] transforms, String prefixSuffixSrc, String transformsSrc) { ++ private static void unpackTransforms(byte[] prefixSuffix, ++ int[] prefixSuffixHeads, int[] transforms, String prefixSuffixSrc, String transformsSrc) { + int n = prefixSuffixSrc.length(); + int index = 1; ++ int j = 0; + for (int i = 0; i < n; ++i) { + char c = prefixSuffixSrc.charAt(i); +- prefixSuffix[i] = (byte) c; + if (c == 35) { // == # +- prefixSuffixHeads[index++] = i + 1; +- prefixSuffix[i] = 0; ++ prefixSuffixHeads[index++] = j; ++ } else { ++ prefixSuffix[j++] = (byte) c; + } + } + +- for (int i = 0; i < NUM_TRANSFORMS * 3; ++i) { ++ for (int i = 0; i < NUM_RFC_TRANSFORMS * 3; ++i) { + transforms[i] = transformsSrc.charAt(i) - 32; + } + } + + static { +- unpackTransforms(PREFIX_SUFFIX, PREFIX_SUFFIX_HEADS, TRANSFORMS, PREFIX_SUFFIX_SRC, +- TRANSFORMS_SRC); ++ unpackTransforms(RFC_TRANSFORMS.prefixSuffixStorage, RFC_TRANSFORMS.prefixSuffixHeads, ++ RFC_TRANSFORMS.triplets, PREFIX_SUFFIX_SRC, TRANSFORMS_SRC); + } + +- static int transformDictionaryWord(byte[] dst, int dstOffset, ByteBuffer data, int wordOffset, +- int len, int transformIndex) { ++ static int transformDictionaryWord(byte[] dst, int dstOffset, ByteBuffer src, int srcOffset, ++ int len, Transforms transforms, int transformIndex) { + int offset = dstOffset; ++ int[] triplets = transforms.triplets; ++ byte[] prefixSuffixStorage = transforms.prefixSuffixStorage; ++ int[] prefixSuffixHeads = transforms.prefixSuffixHeads; + int transformOffset = 3 * transformIndex; +- int transformPrefix = PREFIX_SUFFIX_HEADS[TRANSFORMS[transformOffset]]; +- int transformType = TRANSFORMS[transformOffset + 1]; +- int transformSuffix = PREFIX_SUFFIX_HEADS[TRANSFORMS[transformOffset + 2]]; ++ int prefixIdx = triplets[transformOffset]; ++ int transformType = triplets[transformOffset + 1]; ++ int suffixIdx = triplets[transformOffset + 2]; ++ int prefix = prefixSuffixHeads[prefixIdx]; ++ int prefixEnd = prefixSuffixHeads[prefixIdx + 1]; ++ int suffix = prefixSuffixHeads[suffixIdx]; ++ int suffixEnd = prefixSuffixHeads[suffixIdx + 1]; ++ ++ int omitFirst = transformType - OMIT_FIRST_BASE; ++ int omitLast = transformType - OMIT_LAST_BASE; ++ if (omitFirst < 1 || omitFirst > OMIT_FIRST_LAST_LIMIT) { ++ omitFirst = 0; ++ } ++ if (omitLast < 1 || omitLast > OMIT_FIRST_LAST_LIMIT) { ++ omitLast = 0; ++ } + + // Copy prefix. +- while (PREFIX_SUFFIX[transformPrefix] != 0) { +- dst[offset++] = PREFIX_SUFFIX[transformPrefix++]; ++ while (prefix != prefixEnd) { ++ dst[offset++] = prefixSuffixStorage[prefix++]; + } + + // Copy trimmed word. +- int omitFirst = transformType >= 12 ? (transformType - 11) : 0; + if (omitFirst > len) { + omitFirst = len; + } +- wordOffset += omitFirst; ++ srcOffset += omitFirst; + len -= omitFirst; +- len -= transformType <= 9 ? transformType : 0; // Omit last. ++ len -= omitLast; + int i = len; + while (i > 0) { +- dst[offset++] = data.get(wordOffset++); ++ dst[offset++] = src.get(srcOffset++); + i--; + } + + // Ferment. +- if (transformType == 11 || transformType == 10) { ++ if (transformType == UPPERCASE_FIRST || transformType == UPPERCASE_ALL) { + int uppercaseOffset = offset - len; +- if (transformType == 10) { ++ if (transformType == UPPERCASE_FIRST) { + len = 1; + } + while (len > 0) { +- int tmp = dst[uppercaseOffset] & 0xFF; +- if (tmp < 0xc0) { +- if (tmp >= 97 && tmp <= 122) { // in [a..z] range ++ int c0 = dst[uppercaseOffset] & 0xFF; ++ if (c0 < 0xC0) { ++ if (c0 >= 97 && c0 <= 122) { // in [a..z] range + dst[uppercaseOffset] ^= (byte) 32; + } + uppercaseOffset += 1; + len -= 1; +- } else if (tmp < 0xe0) { ++ } else if (c0 < 0xE0) { + dst[uppercaseOffset + 1] ^= (byte) 32; + uppercaseOffset += 2; + len -= 2; +@@ -103,11 +164,71 @@ static int transformDictionaryWord(byte[] dst, int dstOffset, ByteBuffer data, i + len -= 3; + } + } ++ } else if (transformType == SHIFT_FIRST || transformType == SHIFT_ALL) { ++ int shiftOffset = offset - len; ++ short param = transforms.params[transformIndex]; ++ /* Limited sign extension: scalar < (1 << 24). */ ++ int scalar = (param & 0x7FFF) + (0x1000000 - (param & 0x8000)); ++ while (len > 0) { ++ int step = 1; ++ int c0 = dst[shiftOffset] & 0xFF; ++ if (c0 < 0x80) { ++ /* 1-byte rune / 0sssssss / 7 bit scalar (ASCII). */ ++ scalar += c0; ++ dst[shiftOffset] = (byte) (scalar & 0x7F); ++ } else if (c0 < 0xC0) { ++ /* Continuation / 10AAAAAA. */ ++ } else if (c0 < 0xE0) { ++ /* 2-byte rune / 110sssss AAssssss / 11 bit scalar. */ ++ if (len >= 2) { ++ byte c1 = dst[shiftOffset + 1]; ++ scalar += (c1 & 0x3F) | ((c0 & 0x1F) << 6); ++ dst[shiftOffset] = (byte) (0xC0 | ((scalar >> 6) & 0x1F)); ++ dst[shiftOffset + 1] = (byte) ((c1 & 0xC0) | (scalar & 0x3F)); ++ step = 2; ++ } else { ++ step = len; ++ } ++ } else if (c0 < 0xF0) { ++ /* 3-byte rune / 1110ssss AAssssss BBssssss / 16 bit scalar. */ ++ if (len >= 3) { ++ byte c1 = dst[shiftOffset + 1]; ++ byte c2 = dst[shiftOffset + 2]; ++ scalar += (c2 & 0x3F) | ((c1 & 0x3F) << 6) | ((c0 & 0x0F) << 12); ++ dst[shiftOffset] = (byte) (0xE0 | ((scalar >> 12) & 0x0F)); ++ dst[shiftOffset + 1] = (byte) ((c1 & 0xC0) | ((scalar >> 6) & 0x3F)); ++ dst[shiftOffset + 2] = (byte) ((c2 & 0xC0) | (scalar & 0x3F)); ++ step = 3; ++ } else { ++ step = len; ++ } ++ } else if (c0 < 0xF8) { ++ /* 4-byte rune / 11110sss AAssssss BBssssss CCssssss / 21 bit scalar. */ ++ if (len >= 4) { ++ byte c1 = dst[shiftOffset + 1]; ++ byte c2 = dst[shiftOffset + 2]; ++ byte c3 = dst[shiftOffset + 3]; ++ scalar += (c3 & 0x3F) | ((c2 & 0x3F) << 6) | ((c1 & 0x3F) << 12) | ((c0 & 0x07) << 18); ++ dst[shiftOffset] = (byte) (0xF0 | ((scalar >> 18) & 0x07)); ++ dst[shiftOffset + 1] = (byte) ((c1 & 0xC0) | ((scalar >> 12) & 0x3F)); ++ dst[shiftOffset + 2] = (byte) ((c2 & 0xC0) | ((scalar >> 6) & 0x3F)); ++ dst[shiftOffset + 3] = (byte) ((c3 & 0xC0) | (scalar & 0x3F)); ++ step = 4; ++ } else { ++ step = len; ++ } ++ } ++ shiftOffset += step; ++ len -= step; ++ if (transformType == SHIFT_FIRST) { ++ len = 0; ++ } ++ } + } + + // Copy suffix. +- while (PREFIX_SUFFIX[transformSuffix] != 0) { +- dst[offset++] = PREFIX_SUFFIX[transformSuffix++]; ++ while (suffix != suffixEnd) { ++ dst[offset++] = prefixSuffixStorage[suffix++]; + } + + return offset - dstOffset; +diff --git a/java/org/brotli/dec/TransformTest.java b/java/org/brotli/dec/TransformTest.java +index 616ba351..6e04f0dc 100644 +--- a/java/org/brotli/dec/TransformTest.java ++++ b/java/org/brotli/dec/TransformTest.java +@@ -36,8 +36,8 @@ private static long crc64(byte[] data) { + public void testTrimAll() { + byte[] output = new byte[0]; + byte[] input = {119, 111, 114, 100}; // "word" +- Transform.transformDictionaryWord( +- output, 0, ByteBuffer.wrap(input), 0, input.length, 39); ++ Transform.transformDictionaryWord(output, 0, ++ ByteBuffer.wrap(input), 0, input.length, Transform.RFC_TRANSFORMS, 39); + byte[] expectedOutput = new byte[0]; + assertArrayEquals(expectedOutput, output); + } +@@ -46,8 +46,8 @@ public void testTrimAll() { + public void testCapitalize() { + byte[] output = new byte[6]; + byte[] input = {113, -61, -90, -32, -92, -86}; // "qæप" +- Transform.transformDictionaryWord( +- output, 0, ByteBuffer.wrap(input), 0, input.length, 44); ++ Transform.transformDictionaryWord(output, 0, ++ ByteBuffer.wrap(input), 0, input.length, Transform.RFC_TRANSFORMS, 44); + byte[] expectedOutput = {81, -61, -122, -32, -92, -81}; // "QÆय" + assertArrayEquals(expectedOutput, output); + } +@@ -60,9 +60,9 @@ public void testAllTransforms() { + byte[] testWord = {111, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102}; + byte[] output = new byte[2259]; + int offset = 0; +- for (int i = 0; i < Transform.NUM_TRANSFORMS; ++i) { +- offset += Transform.transformDictionaryWord( +- output, offset, ByteBuffer.wrap(testWord), 0, testWord.length, i); ++ for (int i = 0; i < Transform.NUM_RFC_TRANSFORMS; ++i) { ++ offset += Transform.transformDictionaryWord(output, offset, ++ ByteBuffer.wrap(testWord), 0, testWord.length, Transform.RFC_TRANSFORMS, i); + output[offset++] = -1; + } + assertEquals(output.length, offset); +