private static unsafe void EmitCopyLen(size_t copylen, uint **commands) { if (copylen < 10) { **commands = (uint)(copylen + 38); } else if (copylen < 134) { size_t tail = copylen - 6; size_t nbits = Log2FloorNonZero(tail) - 1; size_t prefix = tail >> (int)nbits; size_t code = (nbits << 1) + prefix + 44; size_t extra = tail - (prefix << (int)nbits); ** commands = (uint)(code | (extra << 8)); } else if (copylen < 2118) { size_t tail = copylen - 70; size_t nbits = Log2FloorNonZero(tail); size_t code = nbits + 52; size_t extra = tail - ((size_t)1 << (int)nbits); ** commands = (uint)(code | (extra << 8)); } else { size_t extra = copylen - 2118; ** commands = (uint)(63 | (extra << 8)); } ++(*commands); }
private static unsafe void AddMatch(size_t distance, size_t len, size_t len_code, uint *matches) { uint match = (uint)((distance << 5) + len_code); matches[len] = Math.Min(matches[len], match); }
private static unsafe void CopyLiteralsToByteArray(Command *cmds, size_t num_commands, byte *data, size_t offset, size_t mask, byte *literals) { size_t pos = 0; size_t from_pos = offset & mask; size_t i; for (i = 0; i < num_commands; ++i) { size_t insert_len = cmds[i].insert_len_; if (from_pos + insert_len > mask) { size_t head_size = mask + 1 - from_pos; memcpy(literals + pos, data + from_pos, head_size); from_pos = 0; pos += head_size; insert_len -= head_size; } if (insert_len > 0) { memcpy(literals + pos, data + from_pos, insert_len); pos += insert_len; } from_pos = (from_pos + insert_len + CommandCopyLen(&cmds[i])) & mask; } }
private static unsafe size_t FindMatchLengthWithLimit(byte *s1, byte *s2, size_t limit) { size_t matched = 0; byte * s2_limit = s2 + limit; byte * s2_ptr = s2; /* Find out how long the match is. We loop over the data 32 bits at a * time until we find a 32-bit block that doesn't match; then we find * the first non-matching bit and use that to calculate the total * length of the match. */ while (s2_ptr <= s2_limit - 4 && *(uint *)(s2_ptr) == *(uint *)(s1 + matched)) { s2_ptr += 4; matched += 4; } while ((s2_ptr < s2_limit) && (s1[matched] == *s2_ptr)) { ++s2_ptr; ++matched; } return(matched); }
public static unsafe void InitBlockSplitter( ref MemoryManager m, BlockSplitterDistance *self, size_t alphabet_size, size_t min_block_size, double split_threshold, size_t num_symbols, BlockSplit *split, HistogramDistance **histograms, size_t *histograms_size) { size_t max_num_blocks = num_symbols / min_block_size + 1; /* We have to allocate one more histogram than the maximum number of block * types for the current histogram when the meta-block is too big. */ size_t max_num_types = Math.Min(max_num_blocks, BROTLI_MAX_NUMBER_OF_BLOCK_TYPES + 1); self->alphabet_size_ = alphabet_size; self->min_block_size_ = min_block_size; self->split_threshold_ = split_threshold; self->num_blocks_ = 0; self->split_ = split; self->histograms_size_ = histograms_size; self->target_block_size_ = min_block_size; self->block_size_ = 0; self->curr_histogram_ix_ = 0; self->merge_last_count_ = 0; BrotliEnsureCapacity(ref m, sizeof(byte), (void **)&split->types, &split->types_alloc_size, max_num_blocks); BrotliEnsureCapacity(ref m, sizeof(uint), (void **)&split->lengths, &split->lengths_alloc_size, max_num_blocks); self->split_->num_blocks = max_num_blocks; *histograms_size = max_num_types; *histograms = (HistogramDistance *)BrotliAllocate(ref m, *histograms_size * sizeof(HistogramDistance)); self->histograms_ = *histograms; /* Clear only current histogram. */ HistogramDistance.HistogramClear(&self->histograms_[0]); self->last_histogram_ix_0 = self->last_histogram_ix_1 = 0; }
/* Here distance_code is an intermediate code, i.e. one of the special codes or * the actual distance increased by BROTLI_NUM_DISTANCE_SHORT_CODES - 1. */ private static unsafe void PrefixEncodeCopyDistance(size_t distance_code, size_t num_direct_codes, size_t postfix_bits, ushort *code, uint *extra_bits) { if (distance_code < BROTLI_NUM_DISTANCE_SHORT_CODES + num_direct_codes) { *code = (ushort)distance_code; *extra_bits = 0; return; } else { size_t dist = ((size_t)1 << (int)(postfix_bits + 2u)) + (distance_code - BROTLI_NUM_DISTANCE_SHORT_CODES - num_direct_codes); size_t bucket = Log2FloorNonZero(dist) - 1; size_t postfix_mask = (1u << (int)postfix_bits) - 1; size_t postfix = dist & postfix_mask; size_t prefix = (dist >> (int)bucket) & 1; size_t offset = (2 + prefix) << (int)bucket; size_t nbits = bucket - postfix_bits; * code = (ushort)( (BROTLI_NUM_DISTANCE_SHORT_CODES + num_direct_codes + ((2 * (nbits - 1) + prefix) << (int)postfix_bits) + postfix)); *extra_bits = (uint)( (nbits << 24) | ((dist - offset) >> (int)postfix_bits)); } }
public abstract void CreateBackwardReferences( ushort *dictionary_hash, size_t num_bytes, size_t position, byte *ringbuffer, size_t ringbuffer_mask, BrotliEncoderParams *params_, HasherHandle hasher, int *dist_cache, size_t *last_insert_len, Command *commands, size_t *num_commands, size_t *num_literals);
public override unsafe void StitchToPreviousBlock(HasherHandle handle, size_t num_bytes, size_t position, byte *ringbuffer, size_t ringbuffer_mask) { HashToBinaryTree *self = Self(handle); if (num_bytes >= HashTypeLength() - 1 && position >= MAX_TREE_COMP_LENGTH) { /* Store the last `MAX_TREE_COMP_LENGTH - 1` positions in the hasher. * These could not be calculated before, since they require knowledge * of both the previous and the current block. */ size_t i_start = position - MAX_TREE_COMP_LENGTH + 1; size_t i_end = Math.Min(position, i_start + num_bytes); size_t i; for (i = i_start; i < i_end; ++i) { /* Maximum distance is window size - 16, see section 9.1. of the spec. * Furthermore, we have to make sure that we don't look further back * from the start of the next block than the window size, otherwise we * could access already overwritten areas of the ring-buffer. */ size_t max_backward = self->window_mask_ - Math.Max( BROTLI_WINDOW_GAP - 1, position - i); /* We know that i + MAX_TREE_COMP_LENGTH <= position + num_bytes, i.e. the * end of the current block and that we have at least * MAX_TREE_COMP_LENGTH tail in the ring-buffer. */ StoreAndFindMatches(self, ringbuffer, i, ringbuffer_mask, MAX_TREE_COMP_LENGTH, max_backward, null, null); } } }
private static unsafe size_t DecideMultiByteStatsLevel(size_t pos, size_t len, size_t mask, byte *data) { size_t *counts = stackalloc size_t[3]; memset(counts, 0, 3 * sizeof(size_t)); size_t max_utf8 = 1; /* should be 2, but 1 compresses better. */ size_t last_c = 0; size_t i; for (i = 0; i < len; ++i) { size_t c = data[(pos + i) & mask]; ++counts[UTF8Position(last_c, c, 2)]; last_c = c; } if (counts[2] < 500) { max_utf8 = 1; } if (counts[1] + counts[2] < 25) { max_utf8 = 0; } return(max_utf8); }
private static unsafe void InitDictionaryBackwardMatch(BackwardMatch *self, size_t dist, size_t len, size_t len_code) { self->distance = (uint)dist; self->length_and_code = (uint)((len << 5) | (len == len_code ? 0 : len_code)); }
/* This function writes bits into bytes in increasing addresses, and within * a byte least-significant-bit first. * * The function can write up to 56 bits in one go with WriteBits * Example: let's assume that 3 bits (Rs below) have been written already: * * BYTE-0 BYTE+1 BYTE+2 * * 0000 0RRR 0000 0000 0000 0000 * * Now, we could write 5 or less bits in MSB by just sifting by 3 * and OR'ing to BYTE-0. * * For n bits, we take the last 5 bits, OR that with high bits in BYTE-0, * and locate the rest in BYTE+1, BYTE+2, etc. */ private static unsafe void BrotliWriteBits(size_t n_bits, ulong bits, size_t *pos, byte *array) { if (BROTLI_LITTLE_ENDIAN) { byte *p = &array[*pos >> 3]; ulong v = *p; v |= bits << (int)(*pos & 7); *(ulong *)p = v; /* Set some bits. */ *pos += n_bits; } else { /* implicit & 0xff is assumed for uint8_t arithmetics */ byte * array_pos = &array[*pos >> 3]; size_t bits_reserved_in_first_byte = (*pos & 7); size_t bits_left_to_write; bits <<= (int)bits_reserved_in_first_byte; *array_pos++ |= (byte)bits; for (bits_left_to_write = n_bits + bits_reserved_in_first_byte; bits_left_to_write >= 9; bits_left_to_write -= 8) { bits >>= 8; *array_pos++ = (byte)bits; } *array_pos = 0; *pos += n_bits; } }
public override unsafe void CreateBackwardReferences(ushort *dictionary_hash, size_t num_bytes, size_t position, byte *ringbuffer, size_t ringbuffer_mask, BrotliEncoderParams *params_, HasherHandle hasher, int *dist_cache, size_t *last_insert_len, Command *commands, size_t *num_commands, size_t *num_literals) { throw new InvalidOperationException(); }
private static ushort GetInsertLengthCode(size_t insertlen) { if (insertlen < 6) { return((ushort)insertlen); } else if (insertlen < 130) { uint nbits = Log2FloorNonZero(insertlen - 2) - 1u; return((ushort)((nbits << 1) + ((insertlen - 2) >> (int)nbits) + 2)); } else if (insertlen < 2114) { return((ushort)(Log2FloorNonZero(insertlen - 66) + 10)); } else if (insertlen < 6210) { return(21); } else if (insertlen < 22594) { return(22); } else { return(23); } }
private static unsafe void SetCost(uint *histogram, size_t histogram_size, float *cost) { size_t sum = 0; float log2sum; size_t i; for (i = 0; i < histogram_size; i++) { sum += histogram[i]; } log2sum = (float)FastLog2(sum); for (i = 0; i < histogram_size; i++) { if (histogram[i] == 0) { cost[i] = log2sum + 2; continue; } /* Shannon bits for this symbol. */ cost[i] = log2sum - (float)FastLog2(histogram[i]); /* Cannot be coded with less than 1 bit */ if (cost[i] < 1) { cost[i] = 1; } } }
private static unsafe void BrotliCreateBackwardReferences( size_t num_bytes, size_t position, byte *ringbuffer, size_t ringbuffer_mask, BrotliEncoderParams *params_, HasherHandle hasher, int *dist_cache, size_t *last_insert_len, Command *commands, size_t *num_commands, size_t *num_literals) { switch (params_->hasher.type) { case 2: case 3: case 4: case 5: case 6: case 40: case 41: case 42: case 54: fixed(ushort *ksdh = kStaticDictionaryHash) kHashers[params_->hasher.type].CreateBackwardReferences(ksdh, num_bytes, position, ringbuffer, ringbuffer_mask, params_, hasher, dist_cache, last_insert_len, commands, num_commands, num_literals); break; } }
/* Returns the minimum possible copy length that can improve the cost of any */ /* future position. */ private static unsafe size_t ComputeMinimumCopyLength(float start_cost, ZopfliNode *nodes, size_t num_bytes, size_t pos) { /* Compute the minimum possible cost of reaching any future position. */ float min_cost = start_cost; size_t len = 2; size_t next_len_bucket = 4; size_t next_len_offset = 10; while (pos + len <= num_bytes && nodes[pos + len].u.cost <= min_cost) { /* We already reached (pos + len) with no more cost than the minimum * possible cost of reaching anything from this pos, so there is no point in * looking for lengths <= len. */ ++len; if (len == next_len_offset) { /* We reached the next copy length code bucket, so we add one more * extra bit to the minimum cost. */ min_cost += 1.0f; next_len_offset += next_len_bucket; next_len_bucket *= 2; } } return(len); }
/* REQUIRES: nodes[pos].cost < kInfinity * REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */ private static unsafe uint ComputeDistanceShortcut(size_t block_start, size_t pos, size_t max_backward, ZopfliNode *nodes) { size_t clen = ZopfliNodeCopyLength(&nodes[pos]); size_t ilen = nodes[pos].insert_length; size_t dist = ZopfliNodeCopyDistance(&nodes[pos]); /* Since |block_start + pos| is the end position of the command, the copy part * starts from |block_start + pos - clen|. Distances that are greater than * this or greater than |max_backward| are private static unsafe dictionary references, and * do not update the last distances. Also distance code 0 (last distance) * does not update the last distances. */ if (pos == 0) { return(0); } else if (dist + clen <= block_start + pos && dist <= max_backward && ZopfliNodeDistanceCode(&nodes[pos]) > 0) { return((uint)pos); } else { return(nodes[pos - clen - ilen].u.shortcut); } }
private static unsafe void ZopfliCostModelSetFromLiteralCosts(ZopfliCostModel *self, size_t position, byte *ringbuffer, size_t ringbuffer_mask) { float *literal_costs = self->literal_costs_; float *cost_dist = self->cost_dist_; float *cost_cmd = self->cost_cmd_; size_t num_bytes = self->num_bytes_; size_t i; BrotliEstimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask, ringbuffer, &literal_costs[1]); literal_costs[0] = 0.0f; for (i = 0; i < num_bytes; ++i) { literal_costs[i + 1] += literal_costs[i]; } for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) { cost_cmd[i] = (float)FastLog2(11 + (uint)i); } for (i = 0; i < BROTLI_NUM_DISTANCE_SYMBOLS; ++i) { cost_dist[i] = (float)FastLog2(20 + (uint)i); } self->min_cost_cmd_ = (float)FastLog2(11); }
private static unsafe void BrotliOptimizeHistograms(size_t num_direct_distance_codes, size_t distance_postfix_bits, MetaBlockSplit *mb) { byte * good_for_rle = stackalloc byte[BROTLI_NUM_COMMAND_SYMBOLS]; size_t num_distance_codes; size_t i; for (i = 0; i < mb->literal_histograms_size; ++i) { BrotliOptimizeHuffmanCountsForRle(256, mb->literal_histograms[i].data_, good_for_rle); } for (i = 0; i < mb->command_histograms_size; ++i) { BrotliOptimizeHuffmanCountsForRle(BROTLI_NUM_COMMAND_SYMBOLS, mb->command_histograms[i].data_, good_for_rle); } num_distance_codes = BROTLI_NUM_DISTANCE_SHORT_CODES + num_direct_distance_codes + ((2 * BROTLI_MAX_DISTANCE_BITS) << (int)distance_postfix_bits); for (i = 0; i < mb->distance_histograms_size; ++i) { BrotliOptimizeHuffmanCountsForRle(num_distance_codes, mb->distance_histograms[i].data_, good_for_rle); } }
private static unsafe bool BrotliWarmupBitReader(BrotliBitReader *br) { size_t aligned_read_mask = (IntPtr.Size >> 1) - 1; /* Fixing alignment after unaligned BrotliFillWindow would result accumulator * overflow. If unalignment is caused by BrotliSafeReadBits, then there is * enough space in accumulator to fix alignment. */ if (!BROTLI_ALIGNED_READ) { aligned_read_mask = 0; } if (BrotliGetAvailableBits(br) == 0) { if (!BrotliPullByte(br)) { return(false); } } while ((((size_t)br->next_in) & aligned_read_mask) != 0) { if (!BrotliPullByte(br)) { /* If we consumed all the input, we don't care about the alignment. */ return(true); } } return(true); }
private static unsafe bool SearchInStaticDictionary( ushort *dictionary_hash, HasherHandle handle, byte *data, size_t max_length, size_t max_backward, HasherSearchResult *out_, bool shallow) { size_t key; size_t i; bool is_match_found = false; HasherCommon *self = GetHasherCommon(handle); if (self->dict_num_matches < (self->dict_num_lookups >> 7)) { return(false); } key = Hash14(data) << 1; for (i = 0; i < (shallow ? 1u : 2u); ++i, ++key) { size_t item = dictionary_hash[key]; self->dict_num_lookups++; if (item != 0) { bool item_matches = TestStaticDictionaryItem( item, data, max_length, max_backward, out_); if (item_matches) { self->dict_num_matches++; is_match_found = true; } } } return(is_match_found); }
private static unsafe void BrotliConvertBitDepthsToSymbols(byte *depth, size_t len, ushort *bits) { /* In Brotli, all bit depths are [1..15] * 0 bit depth means that the symbol does not exist. */ ushort[] bl_count = new ushort[MAX_HUFFMAN_BITS]; ushort[] next_code = new ushort[MAX_HUFFMAN_BITS]; size_t i; int code = 0; for (i = 0; i < len; ++i) { ++bl_count[depth[i]]; } bl_count[0] = 0; next_code[0] = 0; for (i = 1; i < MAX_HUFFMAN_BITS; ++i) { code = (code + bl_count[i - 1]) << 1; next_code[i] = (ushort)code; } for (i = 0; i < len; ++i) { if (depth[i] != 0) { bits[i] = BrotliReverseBits(depth[i], next_code[depth[i]]++); } } }
private static unsafe void BrotliBuildMetaBlockGreedy(ref MemoryManager m, byte *ringbuffer, size_t pos, size_t mask, byte prev_byte, byte prev_byte2, ContextType literal_context_mode, size_t num_contexts, uint *static_context_map, Command *commands, size_t n_commands, MetaBlockSplit *mb) { if (num_contexts == 1) { BrotliBuildMetaBlockGreedyInternal(ref m, ringbuffer, pos, mask, prev_byte, prev_byte2, literal_context_mode, 1, null, commands, n_commands, mb); } else { BrotliBuildMetaBlockGreedyInternal(ref m, ringbuffer, pos, mask, prev_byte, prev_byte2, literal_context_mode, num_contexts, static_context_map, commands, n_commands, mb); } }
private static unsafe void DecideOverRleUse(byte *depth, size_t length, bool *use_rle_for_non_zero, bool *use_rle_for_zero) { size_t total_reps_zero = 0; size_t total_reps_non_zero = 0; size_t count_reps_zero = 1; size_t count_reps_non_zero = 1; size_t i; for (i = 0; i < length;) { byte value = depth[i]; size_t reps = 1; size_t k; for (k = i + 1; k < length && depth[k] == value; ++k) { ++reps; } if (reps >= 3 && value == 0) { total_reps_zero += reps; ++count_reps_zero; } if (reps >= 4 && value != 0) { total_reps_non_zero += reps; ++count_reps_non_zero; } i += reps; } *use_rle_for_non_zero = (total_reps_non_zero > count_reps_non_zero * 2); *use_rle_for_zero = (total_reps_zero > count_reps_zero * 2); }
/* Find the best 'out_' histogram for each of the 'in' histograms. * When called, clusters[0..num_clusters) contains the unique values from * symbols[0..in_size), but this property is not preserved in this function. * Note: we assume that out_[]->bit_cost_ is already up-to-date. */ public static void BrotliHistogramRemap(HistogramDistance *in_, size_t in_size, uint *clusters, size_t num_clusters, HistogramDistance *out_, uint *symbols) { size_t i; for (i = 0; i < in_size; ++i) { uint best_out = i == 0 ? symbols[0] : symbols[i - 1]; double best_bits = BrotliHistogramBitCostDistance(&in_[i], &out_[best_out]); size_t j; for (j = 0; j < num_clusters; ++j) { double cur_bits = BrotliHistogramBitCostDistance(&in_[i], &out_[clusters[j]]); if (cur_bits < best_bits) { best_bits = cur_bits; best_out = clusters[j]; } } symbols[i] = best_out; } /* Recompute each out_ based on raw and symbols. */ for (i = 0; i < num_clusters; ++i) { HistogramDistance.HistogramClear(&out_[clusters[i]]); } for (i = 0; i < in_size; ++i) { HistogramDistance.HistogramAddHistogram(&out_[symbols[i]], &in_[i]); } }
private static unsafe double ShannonEntropy(uint *population, size_t size, size_t *total) { size_t sum = 0; double retval = 0; uint * population_end = population + size; size_t p; if ((size & 1) != 0) { p = *population++; sum += p; retval -= (double)p * FastLog2(p); } while (population < population_end) { p = *population++; sum += p; retval -= (double)p * FastLog2(p); p = *population++; sum += p; retval -= (double)p * FastLog2(p); } if (sum != 0) { retval += (double)sum * FastLog2(sum); } *total = sum; return(retval); }
private void WriteCore(byte[] buffer, int offset, int count, Brotli.BrotliEncoderOperation operation) { bool flush = operation == Brotli.BrotliEncoderOperation.BROTLI_OPERATION_FLUSH || operation == Brotli.BrotliEncoderOperation.BROTLI_OPERATION_FINISH; byte[] out_buf = new byte[0x1FFFE]; size_t available_in = count, available_out = out_buf.Length; fixed (byte* out_buf_ptr = out_buf) fixed (byte* buf_ptr = buffer) { byte* next_in = buf_ptr + offset; byte* next_out = out_buf_ptr; while ((!flush && available_in > 0) || flush) { if (!Brotli.BrotliEncoderCompressStream(ref _encoderState, operation, &available_in, &next_in, &available_out, &next_out, null)) { throw new InvalidDataException("Compression failed"); } bool hasData = available_out != out_buf.Length; if (hasData) { int out_size = (int)(out_buf.Length - available_out); _stream.Write(out_buf, 0, out_size); available_out = out_buf.Length; next_out = out_buf_ptr; } if (Brotli.BrotliEncoderIsFinished(ref _encoderState)) break; if (!hasData && flush) break; } } }
/// <summary> /// Reads a number of decompressed bytes into the specified byte array. /// </summary> /// <param name="buffer">The array to store decompressed bytes.</param> /// <param name="offset">The byte offset in <paramref name="buffer"/> at which the read bytes will be placed.</param> /// <param name="count">The maximum number of decompressed bytes to read.</param> /// <returns>The number of bytes that were read into the byte array.</returns> public override int Read(byte[] buffer, int offset, int count) { if (_mode != CompressionMode.Decompress) throw new InvalidOperationException("Read is only supported in Decompress mode"); EnsureNotDisposed(); ValidateParameters(buffer, offset, count); int totalWritten = 0; while (offset < buffer.Length && _lastDecoderState != Brotli.BrotliDecoderResult.BROTLI_DECODER_RESULT_SUCCESS) { if (_lastDecoderState == Brotli.BrotliDecoderResult.BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { if (_bufferCount > 0 && _bufferOffset != 0) { Array.Copy(_buffer, _bufferOffset, _buffer, 0, _bufferCount); } _bufferOffset = 0; int numRead = 0; while (_bufferCount < _buffer.Length && ((numRead = _stream.Read(_buffer, _bufferCount, _buffer.Length - _bufferCount)) > 0)) { _bufferCount += numRead; if (_bufferCount > _buffer.Length) throw new InvalidDataException("Invalid input stream detected, more bytes supplied than expected."); } if (_bufferCount <= 0) break; } size_t available_in = _bufferCount; size_t available_in_old = available_in; size_t available_out = count; size_t available_out_old = available_out; fixed (byte* out_buf_ptr = buffer) fixed (byte* in_buf_ptr = _buffer) { byte* in_buf = in_buf_ptr + _bufferOffset; byte* out_buf = out_buf_ptr + offset; _lastDecoderState = Brotli.BrotliDecoderDecompressStream(ref _decoderState, &available_in, &in_buf, &available_out, &out_buf, null); } if (_lastDecoderState == Brotli.BrotliDecoderResult.BROTLI_DECODER_RESULT_ERROR) throw new InvalidDataException("Decompression failed with error code: " + _decoderState.error_code); size_t bytesConsumed = available_in_old - available_in; size_t bytesWritten = available_out_old - available_out; if (bytesConsumed > 0) { _bufferOffset += (int) bytesConsumed; _bufferCount -= (int) bytesConsumed; } if (bytesWritten > 0) { totalWritten += (int)bytesWritten; offset += (int)bytesWritten; count -= (int)bytesWritten; } } return totalWritten; }
public static unsafe byte[] DecompressBuffer(byte[] buffer, int offset, int length) { using (var ms = new MemoryStream()) { // Create the decoder state and intialise it. var s = BrotliCreateDecoderState(); BrotliDecoderStateInit(ref s); // Create a 4k buffer to temporarily store decompressed contents. byte[] writeBuf = new byte[0x10000]; // Pin the output buffer and the input buffer. fixed(byte *outBuffer = writeBuf) { fixed(byte *inBuffer = buffer) { // Specify the length of the input buffer. size_t len = length; // Local vars for input/output buffer. var bufPtr = inBuffer + offset; var outPtr = outBuffer; // Specify the amount of bytes available in the output buffer. size_t availOut = writeBuf.Length; // Total number of bytes decoded. size_t total = 0; // Main decompression loop. BrotliDecoderResult result; while ( (result = BrotliDecoderDecompressStream(ref s, &len, &bufPtr, &availOut, &outPtr, &total)) == BrotliDecoderResult.BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { ms.Write(writeBuf, 0, (int)(writeBuf.Length - availOut)); availOut = writeBuf.Length; outPtr = outBuffer; } // Check the result and write final block. if (result == BrotliDecoderResult.BROTLI_DECODER_RESULT_SUCCESS) { ms.Write(writeBuf, 0, (int)(writeBuf.Length - availOut)); } // Cleanup and throw. BrotliDecoderStateCleanup(ref s); if (result != BrotliDecoderResult.BROTLI_DECODER_RESULT_SUCCESS) { throw new InvalidDataException("Decompress failed with error code: " + s.error_code); } return(ms.ToArray()); } } } }
public override unsafe size_t HashMemAllocInBytes(BrotliEncoderParams *params_, bool one_shot, size_t input_size) { size_t bucket_size = (size_t)1 << params_->hasher.bucket_bits; size_t block_size = (size_t)1 << params_->hasher.block_bits; return(sizeof(HashLongestMatch) + bucket_size * (2 + 4 * block_size)); }