Make brotli a submodule
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "brotli"]
|
||||||
|
path = brotli
|
||||||
|
url = https://github.com/google/brotli.git
|
||||||
Submodule
+1
Submodule brotli added at 94000f6490
-202
@@ -1,202 +0,0 @@
|
|||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +0,0 @@
|
|||||||
#brotli/dec
|
|
||||||
|
|
||||||
include ../../shared.mk
|
|
||||||
|
|
||||||
OBJS = bit_reader.o decode.o huffman.o safe_malloc.o streams.o
|
|
||||||
|
|
||||||
all : $(OBJS)
|
|
||||||
|
|
||||||
clean :
|
|
||||||
rm -f $(OBJS)
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
This directory holds the decoder for brotli compression format.
|
|
||||||
|
|
||||||
Brotli is proposed to be used at the byte-compression level in WOFF 2.0 format.
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Bit reading helpers
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "./bit_reader.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int BrotliInitBitReader(BrotliBitReader* const br, BrotliInput input) {
|
|
||||||
size_t i;
|
|
||||||
assert(br != NULL);
|
|
||||||
|
|
||||||
br->buf_ptr_ = br->buf_;
|
|
||||||
br->input_ = input;
|
|
||||||
br->val_ = 0;
|
|
||||||
br->pos_ = 0;
|
|
||||||
br->bit_pos_ = 0;
|
|
||||||
br->bit_end_pos_ = 0;
|
|
||||||
br->eos_ = 0;
|
|
||||||
if (!BrotliReadMoreInput(br)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
for (i = 0; i < sizeof(br->val_); ++i) {
|
|
||||||
br->val_ |= ((uint64_t)br->buf_[br->pos_]) << (8 * i);
|
|
||||||
++br->pos_;
|
|
||||||
}
|
|
||||||
return (br->bit_end_pos_ > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Bit reading helpers
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BROTLI_DEC_BIT_READER_H_
|
|
||||||
#define BROTLI_DEC_BIT_READER_H_
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include "./streams.h"
|
|
||||||
#include "./types.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define BROTLI_MAX_NUM_BIT_READ 25
|
|
||||||
#define BROTLI_READ_SIZE 4096
|
|
||||||
#define BROTLI_IBUF_SIZE (2 * BROTLI_READ_SIZE + 32)
|
|
||||||
#define BROTLI_IBUF_MASK (2 * BROTLI_READ_SIZE - 1)
|
|
||||||
|
|
||||||
#define UNALIGNED_COPY64(dst, src) memcpy(dst, src, 8)
|
|
||||||
|
|
||||||
static const uint32_t kBitMask[BROTLI_MAX_NUM_BIT_READ] = {
|
|
||||||
0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767,
|
|
||||||
65535, 131071, 262143, 524287, 1048575, 2097151, 4194303, 8388607, 16777215
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
/* Input byte buffer, consist of a ringbuffer and a "slack" region where */
|
|
||||||
/* bytes from the start of the ringbuffer are copied. */
|
|
||||||
uint8_t buf_[BROTLI_IBUF_SIZE];
|
|
||||||
uint8_t* buf_ptr_; /* next input will write here */
|
|
||||||
BrotliInput input_; /* input callback */
|
|
||||||
uint64_t val_; /* pre-fetched bits */
|
|
||||||
uint32_t pos_; /* byte position in stream */
|
|
||||||
uint32_t bit_pos_; /* current bit-reading position in val_ */
|
|
||||||
uint32_t bit_end_pos_; /* bit-reading end position from LSB of val_ */
|
|
||||||
int eos_; /* input stream is finished */
|
|
||||||
} BrotliBitReader;
|
|
||||||
|
|
||||||
int BrotliInitBitReader(BrotliBitReader* const br, BrotliInput input);
|
|
||||||
|
|
||||||
/* Return the prefetched bits, so they can be looked up. */
|
|
||||||
static BROTLI_INLINE uint32_t BrotliPrefetchBits(BrotliBitReader* const br) {
|
|
||||||
return (uint32_t)(br->val_ >> br->bit_pos_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* For jumping over a number of bits in the bit stream when accessed with */
|
|
||||||
/* BrotliPrefetchBits and BrotliFillBitWindow. */
|
|
||||||
static BROTLI_INLINE void BrotliSetBitPos(BrotliBitReader* const br,
|
|
||||||
uint32_t val) {
|
|
||||||
#ifdef BROTLI_DECODE_DEBUG
|
|
||||||
uint32_t n_bits = val - br->bit_pos_;
|
|
||||||
const uint32_t bval = (uint32_t)(br->val_ >> br->bit_pos_) & kBitMask[n_bits];
|
|
||||||
printf("[BrotliReadBits] %010d %2d val: %6x\n",
|
|
||||||
(br->pos_ << 3) + br->bit_pos_ - 64, n_bits, bval);
|
|
||||||
#endif
|
|
||||||
br->bit_pos_ = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reload up to 64 bits byte-by-byte */
|
|
||||||
static BROTLI_INLINE void ShiftBytes(BrotliBitReader* const br) {
|
|
||||||
while (br->bit_pos_ >= 8) {
|
|
||||||
br->val_ >>= 8;
|
|
||||||
br->val_ |= ((uint64_t)br->buf_[br->pos_ & BROTLI_IBUF_MASK]) << 56;
|
|
||||||
++br->pos_;
|
|
||||||
br->bit_pos_ -= 8;
|
|
||||||
br->bit_end_pos_ -= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fills up the input ringbuffer by calling the input callback.
|
|
||||||
|
|
||||||
Does nothing if there are at least 32 bytes present after current position.
|
|
||||||
|
|
||||||
Returns 0 if either:
|
|
||||||
- the input callback returned an error, or
|
|
||||||
- there is no more input and the position is past the end of the stream.
|
|
||||||
|
|
||||||
After encountering the end of the input stream, 32 additional zero bytes are
|
|
||||||
copied to the ringbuffer, therefore it is safe to call this function after
|
|
||||||
every 32 bytes of input is read.
|
|
||||||
*/
|
|
||||||
static BROTLI_INLINE int BrotliReadMoreInput(BrotliBitReader* const br) {
|
|
||||||
if (br->bit_end_pos_ > 256) {
|
|
||||||
return 1;
|
|
||||||
} else if (br->eos_) {
|
|
||||||
return br->bit_pos_ <= br->bit_end_pos_;
|
|
||||||
} else {
|
|
||||||
uint8_t* dst = br->buf_ptr_;
|
|
||||||
int bytes_read = BrotliRead(br->input_, dst, BROTLI_READ_SIZE);
|
|
||||||
if (bytes_read < 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (bytes_read < BROTLI_READ_SIZE) {
|
|
||||||
br->eos_ = 1;
|
|
||||||
/* Store 32 bytes of zero after the stream end. */
|
|
||||||
#if (defined(__x86_64__) || defined(_M_X64))
|
|
||||||
*(uint64_t*)(dst + bytes_read) = 0;
|
|
||||||
*(uint64_t*)(dst + bytes_read + 8) = 0;
|
|
||||||
*(uint64_t*)(dst + bytes_read + 16) = 0;
|
|
||||||
*(uint64_t*)(dst + bytes_read + 24) = 0;
|
|
||||||
#else
|
|
||||||
memset(dst + bytes_read, 0, 32);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if (dst == br->buf_) {
|
|
||||||
/* Copy the head of the ringbuffer to the slack region. */
|
|
||||||
#if (defined(__x86_64__) || defined(_M_X64))
|
|
||||||
UNALIGNED_COPY64(br->buf_ + BROTLI_IBUF_SIZE - 32, br->buf_);
|
|
||||||
UNALIGNED_COPY64(br->buf_ + BROTLI_IBUF_SIZE - 24, br->buf_ + 8);
|
|
||||||
UNALIGNED_COPY64(br->buf_ + BROTLI_IBUF_SIZE - 16, br->buf_ + 16);
|
|
||||||
UNALIGNED_COPY64(br->buf_ + BROTLI_IBUF_SIZE - 8, br->buf_ + 24);
|
|
||||||
#else
|
|
||||||
memcpy(br->buf_ + (BROTLI_READ_SIZE << 1), br->buf_, 32);
|
|
||||||
#endif
|
|
||||||
br->buf_ptr_ = br->buf_ + BROTLI_READ_SIZE;
|
|
||||||
} else {
|
|
||||||
br->buf_ptr_ = br->buf_;
|
|
||||||
}
|
|
||||||
br->bit_end_pos_ += ((uint32_t)bytes_read << 3);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Advances the Read buffer by 5 bytes to make room for reading next 24 bits. */
|
|
||||||
static BROTLI_INLINE void BrotliFillBitWindow(BrotliBitReader* const br) {
|
|
||||||
if (br->bit_pos_ >= 40) {
|
|
||||||
#if (defined(__x86_64__) || defined(_M_X64))
|
|
||||||
br->val_ >>= 40;
|
|
||||||
/* The expression below needs a little-endian arch to work correctly. */
|
|
||||||
/* This gives a large speedup for decoding speed. */
|
|
||||||
br->val_ |= *(const uint64_t*)(
|
|
||||||
br->buf_ + (br->pos_ & BROTLI_IBUF_MASK)) << 24;
|
|
||||||
br->pos_ += 5;
|
|
||||||
br->bit_pos_ -= 40;
|
|
||||||
br->bit_end_pos_ -= 40;
|
|
||||||
#else
|
|
||||||
ShiftBytes(br);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reads the specified number of bits from Read Buffer. */
|
|
||||||
static BROTLI_INLINE uint32_t BrotliReadBits(
|
|
||||||
BrotliBitReader* const br, int n_bits) {
|
|
||||||
uint32_t val;
|
|
||||||
BrotliFillBitWindow(br);
|
|
||||||
val = (uint32_t)(br->val_ >> br->bit_pos_) & kBitMask[n_bits];
|
|
||||||
#ifdef BROTLI_DECODE_DEBUG
|
|
||||||
printf("[BrotliReadBits] %010d %2d val: %6x\n",
|
|
||||||
(br->pos_ << 3) + br->bit_pos_ - 64, n_bits, val);
|
|
||||||
#endif
|
|
||||||
br->bit_pos_ += (uint32_t)n_bits;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* BROTLI_DEC_BIT_READER_H_ */
|
|
||||||
@@ -1,259 +0,0 @@
|
|||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Lookup table to map the previous two bytes to a context id.
|
|
||||||
|
|
||||||
There are four different context modeling modes defined here:
|
|
||||||
CONTEXT_LSB6: context id is the least significant 6 bits of the last byte,
|
|
||||||
CONTEXT_MSB6: context id is the most significant 6 bits of the last byte,
|
|
||||||
CONTEXT_UTF8: second-order context model tuned for UTF8-encoded text,
|
|
||||||
CONTEXT_SIGNED: second-order context model tuned for signed integers.
|
|
||||||
|
|
||||||
The context id for the UTF8 context model is calculated as follows. If p1
|
|
||||||
and p2 are the previous two bytes, we calcualte the context as
|
|
||||||
|
|
||||||
context = kContextLookup[p1] | kContextLookup[p2 + 256].
|
|
||||||
|
|
||||||
If the previous two bytes are ASCII characters (i.e. < 128), this will be
|
|
||||||
equivalent to
|
|
||||||
|
|
||||||
context = 4 * context1(p1) + context2(p2),
|
|
||||||
|
|
||||||
where context1 is based on the previous byte in the following way:
|
|
||||||
|
|
||||||
0 : non-ASCII control
|
|
||||||
1 : \t, \n, \r
|
|
||||||
2 : space
|
|
||||||
3 : other punctuation
|
|
||||||
4 : " '
|
|
||||||
5 : %
|
|
||||||
6 : ( < [ {
|
|
||||||
7 : ) > ] }
|
|
||||||
8 : , ; :
|
|
||||||
9 : .
|
|
||||||
10 : =
|
|
||||||
11 : number
|
|
||||||
12 : upper-case vowel
|
|
||||||
13 : upper-case consonant
|
|
||||||
14 : lower-case vowel
|
|
||||||
15 : lower-case consonant
|
|
||||||
|
|
||||||
and context2 is based on the second last byte:
|
|
||||||
|
|
||||||
0 : control, space
|
|
||||||
1 : punctuation
|
|
||||||
2 : upper-case letter, number
|
|
||||||
3 : lower-case letter
|
|
||||||
|
|
||||||
If the last byte is ASCII, and the second last byte is not (in a valid UTF8
|
|
||||||
stream it will be a continuation byte, value between 128 and 191), the
|
|
||||||
context is the same as if the second last byte was an ASCII control or space.
|
|
||||||
|
|
||||||
If the last byte is a UTF8 lead byte (value >= 192), then the next byte will
|
|
||||||
be a continuation byte and the context id is 2 or 3 depending on the LSB of
|
|
||||||
the last byte and to a lesser extent on the second last byte if it is ASCII.
|
|
||||||
|
|
||||||
If the last byte is a UTF8 continuation byte, the second last byte can be:
|
|
||||||
- continuation byte: the next byte is probably ASCII or lead byte (assuming
|
|
||||||
4-byte UTF8 characters are rare) and the context id is 0 or 1.
|
|
||||||
- lead byte (192 - 207): next byte is ASCII or lead byte, context is 0 or 1
|
|
||||||
- lead byte (208 - 255): next byte is continuation byte, context is 2 or 3
|
|
||||||
|
|
||||||
The possible value combinations of the previous two bytes, the range of
|
|
||||||
context ids and the type of the next byte is summarized in the table below:
|
|
||||||
|
|
||||||
|--------\-----------------------------------------------------------------|
|
|
||||||
| \ Last byte |
|
|
||||||
| Second \---------------------------------------------------------------|
|
|
||||||
| last byte \ ASCII | cont. byte | lead byte |
|
|
||||||
| \ (0-127) | (128-191) | (192-) |
|
|
||||||
|=============|===================|=====================|==================|
|
|
||||||
| ASCII | next: ASCII/lead | not valid | next: cont. |
|
|
||||||
| (0-127) | context: 4 - 63 | | context: 2 - 3 |
|
|
||||||
|-------------|-------------------|---------------------|------------------|
|
|
||||||
| cont. byte | next: ASCII/lead | next: ASCII/lead | next: cont. |
|
|
||||||
| (128-191) | context: 4 - 63 | context: 0 - 1 | context: 2 - 3 |
|
|
||||||
|-------------|-------------------|---------------------|------------------|
|
|
||||||
| lead byte | not valid | next: ASCII/lead | not valid |
|
|
||||||
| (192-207) | | context: 0 - 1 | |
|
|
||||||
|-------------|-------------------|---------------------|------------------|
|
|
||||||
| lead byte | not valid | next: cont. | not valid |
|
|
||||||
| (208-) | | context: 2 - 3 | |
|
|
||||||
|-------------|-------------------|---------------------|------------------|
|
|
||||||
|
|
||||||
The context id for the signed context mode is calculated as:
|
|
||||||
|
|
||||||
context = (kContextLookup[512 + p1] << 3) | kContextLookup[512 + p2].
|
|
||||||
|
|
||||||
For any context modeling modes, the context ids can be calculated by |-ing
|
|
||||||
together two lookups from one table using context model dependent offsets:
|
|
||||||
|
|
||||||
context = kContextLookup[offset1 + p1] | kContextLookup[offset2 + p2].
|
|
||||||
|
|
||||||
where offset1 and offset2 are dependent on the context mode.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BROTLI_DEC_CONTEXT_H_
|
|
||||||
#define BROTLI_DEC_CONTEXT_H_
|
|
||||||
|
|
||||||
#include "./types.h"
|
|
||||||
|
|
||||||
enum ContextType {
|
|
||||||
CONTEXT_LSB6 = 0,
|
|
||||||
CONTEXT_MSB6 = 1,
|
|
||||||
CONTEXT_UTF8 = 2,
|
|
||||||
CONTEXT_SIGNED = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Common context lookup table for all context modes. */
|
|
||||||
static const uint8_t kContextLookup[1792] = {
|
|
||||||
/* CONTEXT_UTF8, last byte. */
|
|
||||||
/* ASCII range. */
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 4, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
8, 12, 16, 12, 12, 20, 12, 16, 24, 28, 12, 12, 32, 12, 36, 12,
|
|
||||||
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 32, 32, 24, 40, 28, 12,
|
|
||||||
12, 48, 52, 52, 52, 48, 52, 52, 52, 48, 52, 52, 52, 52, 52, 48,
|
|
||||||
52, 52, 52, 52, 52, 48, 52, 52, 52, 52, 52, 24, 12, 28, 12, 12,
|
|
||||||
12, 56, 60, 60, 60, 56, 60, 60, 60, 56, 60, 60, 60, 60, 60, 56,
|
|
||||||
60, 60, 60, 60, 60, 56, 60, 60, 60, 60, 60, 24, 12, 28, 12, 0,
|
|
||||||
/* UTF8 continuation byte range. */
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
/* UTF8 lead byte range. */
|
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
|
||||||
/* CONTEXT_UTF8 second last byte. */
|
|
||||||
/* ASCII range. */
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1,
|
|
||||||
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1,
|
|
||||||
1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 0,
|
|
||||||
/* UTF8 continuation byte range. */
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
/* UTF8 lead byte range. */
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
/* CONTEXT_SIGNED, second last byte. */
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
|
||||||
/* CONTEXT_SIGNED, last byte, same as the above values shifted by 3 bits. */
|
|
||||||
0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
|
||||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
|
||||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
|
||||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
|
||||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
|
||||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
|
||||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
|
||||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
|
||||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
|
||||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
|
||||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
|
||||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
|
||||||
40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
|
|
||||||
40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
|
|
||||||
40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
|
|
||||||
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 56,
|
|
||||||
/* CONTEXT_LSB6, last byte. */
|
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
||||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
|
||||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
|
||||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
||||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
|
||||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
|
||||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
||||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
|
||||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
|
||||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
||||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
|
||||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
|
||||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
|
||||||
/* CONTEXT_MSB6, last byte. */
|
|
||||||
0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
|
|
||||||
4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
|
|
||||||
8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11,
|
|
||||||
12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
|
|
||||||
16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19,
|
|
||||||
20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23,
|
|
||||||
24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27,
|
|
||||||
28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31,
|
|
||||||
32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35,
|
|
||||||
36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39,
|
|
||||||
40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43,
|
|
||||||
44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47,
|
|
||||||
48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51,
|
|
||||||
52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55,
|
|
||||||
56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59,
|
|
||||||
60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63,
|
|
||||||
/* CONTEXT_{M,L}SB6, second last byte, */
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int kContextLookupOffsets[8] = {
|
|
||||||
/* CONTEXT_LSB6 */
|
|
||||||
1024, 1536,
|
|
||||||
/* CONTEXT_MSB6 */
|
|
||||||
1280, 1536,
|
|
||||||
/* CONTEXT_UTF8 */
|
|
||||||
0, 256,
|
|
||||||
/* CONTEXT_SIGNED */
|
|
||||||
768, 512,
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* BROTLI_DEC_CONTEXT_H_ */
|
|
||||||
-1116
File diff suppressed because it is too large
Load Diff
@@ -1,54 +0,0 @@
|
|||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
API for Brotli decompression
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BROTLI_DEC_DECODE_H_
|
|
||||||
#define BROTLI_DEC_DECODE_H_
|
|
||||||
|
|
||||||
#include "./streams.h"
|
|
||||||
#include "./types.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Sets *decoded_size to the decompressed size of the given encoded stream. */
|
|
||||||
/* This function only works if the encoded buffer has a single meta block, */
|
|
||||||
/* and this meta block must have the "is last" bit set. */
|
|
||||||
/* Returns 1 on success, 0 on failure. */
|
|
||||||
int BrotliDecompressedSize(size_t encoded_size,
|
|
||||||
const uint8_t* encoded_buffer,
|
|
||||||
size_t* decoded_size);
|
|
||||||
|
|
||||||
/* Decompresses the data in encoded_buffer into decoded_buffer, and sets */
|
|
||||||
/* *decoded_size to the decompressed length. */
|
|
||||||
/* Returns 0 if there was either a bit stream error or memory allocation */
|
|
||||||
/* error, and 1 otherwise. */
|
|
||||||
/* If decoded size is zero, returns 1 and keeps decoded_buffer unchanged. */
|
|
||||||
int BrotliDecompressBuffer(size_t encoded_size,
|
|
||||||
const uint8_t* encoded_buffer,
|
|
||||||
size_t* decoded_size,
|
|
||||||
uint8_t* decoded_buffer);
|
|
||||||
|
|
||||||
/* Same as above, but uses the specified input and output callbacks instead */
|
|
||||||
/* of reading from and writing to pre-allocated memory buffers. */
|
|
||||||
int BrotliDecompress(BrotliInput input, BrotliOutput output);
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* BROTLI_DEC_DECODE_H_ */
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,164 +0,0 @@
|
|||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Utilities for building Huffman decoding tables.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "./huffman.h"
|
|
||||||
#include "./safe_malloc.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MAX_LENGTH 15
|
|
||||||
|
|
||||||
/* Returns reverse(reverse(key, len) + 1, len), where reverse(key, len) is the
|
|
||||||
bit-wise reversal of the len least significant bits of key. */
|
|
||||||
static BROTLI_INLINE int GetNextKey(int key, int len) {
|
|
||||||
int step = 1 << (len - 1);
|
|
||||||
while (key & step) {
|
|
||||||
step >>= 1;
|
|
||||||
}
|
|
||||||
return (key & (step - 1)) + step;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stores code in table[0], table[step], table[2*step], ..., table[end] */
|
|
||||||
/* Assumes that end is an integer multiple of step */
|
|
||||||
static BROTLI_INLINE void ReplicateValue(HuffmanCode* table,
|
|
||||||
int step, int end,
|
|
||||||
HuffmanCode code) {
|
|
||||||
do {
|
|
||||||
end -= step;
|
|
||||||
table[end] = code;
|
|
||||||
} while (end > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the table width of the next 2nd level table. count is the histogram
|
|
||||||
of bit lengths for the remaining symbols, len is the code length of the next
|
|
||||||
processed symbol */
|
|
||||||
static BROTLI_INLINE int NextTableBitSize(const int* const count,
|
|
||||||
int len, int root_bits) {
|
|
||||||
int left = 1 << (len - root_bits);
|
|
||||||
while (len < MAX_LENGTH) {
|
|
||||||
left -= count[len];
|
|
||||||
if (left <= 0) break;
|
|
||||||
++len;
|
|
||||||
left <<= 1;
|
|
||||||
}
|
|
||||||
return len - root_bits;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
|
||||||
int root_bits,
|
|
||||||
const uint8_t* const code_lengths,
|
|
||||||
int code_lengths_size) {
|
|
||||||
HuffmanCode code; /* current table entry */
|
|
||||||
HuffmanCode* table; /* next available space in table */
|
|
||||||
int len; /* current code length */
|
|
||||||
int symbol; /* symbol index in original or sorted table */
|
|
||||||
int key; /* reversed prefix code */
|
|
||||||
int step; /* step size to replicate values in current table */
|
|
||||||
int low; /* low bits for current root entry */
|
|
||||||
int mask; /* mask for low bits */
|
|
||||||
int table_bits; /* key length of current table */
|
|
||||||
int table_size; /* size of current table */
|
|
||||||
int total_size; /* sum of root table size and 2nd level table sizes */
|
|
||||||
int* sorted; /* symbols sorted by code length */
|
|
||||||
int count[MAX_LENGTH + 1] = { 0 }; /* number of codes of each length */
|
|
||||||
int offset[MAX_LENGTH + 1]; /* offsets in sorted table for each length */
|
|
||||||
|
|
||||||
sorted = (int*)malloc((size_t)code_lengths_size * sizeof(*sorted));
|
|
||||||
if (sorted == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* build histogram of code lengths */
|
|
||||||
for (symbol = 0; symbol < code_lengths_size; symbol++) {
|
|
||||||
count[code_lengths[symbol]]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* generate offsets into sorted symbol table by code length */
|
|
||||||
offset[1] = 0;
|
|
||||||
for (len = 1; len < MAX_LENGTH; len++) {
|
|
||||||
offset[len + 1] = offset[len] + count[len];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* sort symbols by length, by symbol order within each length */
|
|
||||||
for (symbol = 0; symbol < code_lengths_size; symbol++) {
|
|
||||||
if (code_lengths[symbol] != 0) {
|
|
||||||
sorted[offset[code_lengths[symbol]]++] = symbol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table = root_table;
|
|
||||||
table_bits = root_bits;
|
|
||||||
table_size = 1 << table_bits;
|
|
||||||
total_size = table_size;
|
|
||||||
|
|
||||||
/* special case code with only one value */
|
|
||||||
if (offset[MAX_LENGTH] == 1) {
|
|
||||||
code.bits = 0;
|
|
||||||
code.value = (uint16_t)sorted[0];
|
|
||||||
for (key = 0; key < total_size; ++key) {
|
|
||||||
table[key] = code;
|
|
||||||
}
|
|
||||||
free(sorted);
|
|
||||||
return total_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* fill in root table */
|
|
||||||
key = 0;
|
|
||||||
symbol = 0;
|
|
||||||
for (len = 1, step = 2; len <= root_bits; ++len, step <<= 1) {
|
|
||||||
for (; count[len] > 0; --count[len]) {
|
|
||||||
code.bits = (uint8_t)(len);
|
|
||||||
code.value = (uint16_t)sorted[symbol++];
|
|
||||||
ReplicateValue(&table[key], step, table_size, code);
|
|
||||||
key = GetNextKey(key, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* fill in 2nd level tables and add pointers to root table */
|
|
||||||
mask = total_size - 1;
|
|
||||||
low = -1;
|
|
||||||
for (len = root_bits + 1, step = 2; len <= MAX_LENGTH; ++len, step <<= 1) {
|
|
||||||
for (; count[len] > 0; --count[len]) {
|
|
||||||
if ((key & mask) != low) {
|
|
||||||
table += table_size;
|
|
||||||
table_bits = NextTableBitSize(count, len, root_bits);
|
|
||||||
table_size = 1 << table_bits;
|
|
||||||
total_size += table_size;
|
|
||||||
low = key & mask;
|
|
||||||
root_table[low].bits = (uint8_t)(table_bits + root_bits);
|
|
||||||
root_table[low].value = (uint16_t)((table - root_table) - low);
|
|
||||||
}
|
|
||||||
code.bits = (uint8_t)(len - root_bits);
|
|
||||||
code.value = (uint16_t)sorted[symbol++];
|
|
||||||
ReplicateValue(&table[key >> root_bits], step, table_size, code);
|
|
||||||
key = GetNextKey(key, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(sorted);
|
|
||||||
return total_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Utilities for building Huffman decoding tables.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BROTLI_DEC_HUFFMAN_H_
|
|
||||||
#define BROTLI_DEC_HUFFMAN_H_
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include "./types.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t bits; /* number of bits used for this symbol */
|
|
||||||
uint16_t value; /* symbol value or table offset */
|
|
||||||
} HuffmanCode;
|
|
||||||
|
|
||||||
/* Builds Huffman lookup table assuming code lengths are in symbol order. */
|
|
||||||
/* Returns false in case of error (invalid tree or memory error). */
|
|
||||||
int BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
|
||||||
int root_bits,
|
|
||||||
const uint8_t* const code_lengths,
|
|
||||||
int code_lengths_size);
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* BROTLI_DEC_HUFFMAN_H_ */
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Lookup tables to map prefix codes to value ranges. This is used during
|
|
||||||
decoding of the block lengths, literal insertion lengths and copy lengths.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BROTLI_DEC_PREFIX_H_
|
|
||||||
#define BROTLI_DEC_PREFIX_H_
|
|
||||||
|
|
||||||
/* Represents the range of values belonging to a prefix code: */
|
|
||||||
/* [offset, offset + 2^nbits) */
|
|
||||||
struct PrefixCodeRange {
|
|
||||||
int offset;
|
|
||||||
int nbits;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct PrefixCodeRange kBlockLengthPrefixCode[] = {
|
|
||||||
{ 1, 2}, { 5, 2}, { 9, 2}, { 13, 2},
|
|
||||||
{ 17, 3}, { 25, 3}, { 33, 3}, { 41, 3},
|
|
||||||
{ 49, 4}, { 65, 4}, { 81, 4}, { 97, 4},
|
|
||||||
{ 113, 5}, { 145, 5}, { 177, 5}, { 209, 5},
|
|
||||||
{ 241, 6}, { 305, 6}, { 369, 7}, { 497, 8},
|
|
||||||
{ 753, 9}, { 1265, 10}, {2289, 11}, {4337, 12},
|
|
||||||
{8433, 13}, {16625, 24}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct PrefixCodeRange kInsertLengthPrefixCode[] = {
|
|
||||||
{ 0, 0}, { 1, 0}, { 2, 0}, { 3, 0},
|
|
||||||
{ 4, 0}, { 5, 0}, { 6, 1}, { 8, 1},
|
|
||||||
{ 10, 2}, { 14, 2}, { 18, 3}, { 26, 3},
|
|
||||||
{ 34, 4}, { 50, 4}, { 66, 5}, { 98, 5},
|
|
||||||
{ 130, 6}, { 194, 7}, { 322, 8}, { 578, 9},
|
|
||||||
{1090, 10}, {2114, 12}, {6210, 14}, {22594, 24},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct PrefixCodeRange kCopyLengthPrefixCode[] = {
|
|
||||||
{ 2, 0}, { 3, 0}, { 4, 0}, { 5, 0},
|
|
||||||
{ 6, 0}, { 7, 0}, { 8, 0}, { 9, 0},
|
|
||||||
{ 10, 1}, { 12, 1}, { 14, 2}, { 18, 2},
|
|
||||||
{ 22, 3}, { 30, 3}, { 38, 4}, { 54, 4},
|
|
||||||
{ 70, 5}, { 102, 5}, { 134, 6}, { 198, 7},
|
|
||||||
{326, 8}, { 582, 9}, {1094, 10}, {2118, 24},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int kInsertRangeLut[9] = {
|
|
||||||
0, 0, 8, 8, 0, 16, 8, 16, 16,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int kCopyRangeLut[9] = {
|
|
||||||
0, 8, 0, 8, 16, 0, 16, 8, 16,
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* BROTLI_DEC_PREFIX_H_ */
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Size-checked memory allocation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "./safe_malloc.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Returns 0 in case of overflow of nmemb * size. */
|
|
||||||
static int CheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
|
|
||||||
const uint64_t total_size = nmemb * size;
|
|
||||||
if (nmemb == 0) return 1;
|
|
||||||
if ((uint64_t)size > BROTLI_MAX_ALLOCABLE_MEMORY / nmemb) return 0;
|
|
||||||
if (total_size != (size_t)total_size) return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* BrotliSafeMalloc(uint64_t nmemb, size_t size) {
|
|
||||||
if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
|
|
||||||
assert(nmemb * size > 0);
|
|
||||||
return malloc((size_t)(nmemb * size));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Size-checked memory allocation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BROTLI_UTILS_UTILS_H_
|
|
||||||
#define BROTLI_UTILS_UTILS_H_
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "./types.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This is the maximum memory amount that we will ever try to allocate. */
|
|
||||||
#define BROTLI_MAX_ALLOCABLE_MEMORY (1 << 30)
|
|
||||||
|
|
||||||
/* size-checking safe malloc/calloc: verify that the requested size is not too
|
|
||||||
large, or return NULL. You don't need to call these for constructs like
|
|
||||||
malloc(sizeof(foo)), but only if there's font-dependent size involved
|
|
||||||
somewhere (like: malloc(decoded_size * sizeof(*something))). That's why this
|
|
||||||
safe malloc() borrows the signature from calloc(), pointing at the dangerous
|
|
||||||
underlying multiply involved.
|
|
||||||
*/
|
|
||||||
void* BrotliSafeMalloc(uint64_t nmemb, size_t size);
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* BROTLI_UTILS_UTILS_H_ */
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Functions for streaming input and output.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#ifndef _WIN32
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
#include "./streams.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int BrotliMemInputFunction(void* data, uint8_t* buf, size_t count) {
|
|
||||||
BrotliMemInput* input = (BrotliMemInput*)data;
|
|
||||||
if (input->pos > input->length) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (input->pos + count > input->length) {
|
|
||||||
count = input->length - input->pos;
|
|
||||||
}
|
|
||||||
memcpy(buf, input->buffer + input->pos, count);
|
|
||||||
input->pos += count;
|
|
||||||
return (int)count;
|
|
||||||
}
|
|
||||||
|
|
||||||
BrotliInput BrotliInitMemInput(const uint8_t* buffer, size_t length,
|
|
||||||
BrotliMemInput* mem_input) {
|
|
||||||
BrotliInput input;
|
|
||||||
mem_input->buffer = buffer;
|
|
||||||
mem_input->length = length;
|
|
||||||
mem_input->pos = 0;
|
|
||||||
input.cb_ = &BrotliMemInputFunction;
|
|
||||||
input.data_ = mem_input;
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BrotliMemOutputFunction(void* data, const uint8_t* buf, size_t count) {
|
|
||||||
BrotliMemOutput* output = (BrotliMemOutput*)data;
|
|
||||||
if (output->pos + count > output->length) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memcpy(output->buffer + output->pos, buf, count);
|
|
||||||
output->pos += count;
|
|
||||||
return (int)count;
|
|
||||||
}
|
|
||||||
|
|
||||||
BrotliOutput BrotliInitMemOutput(uint8_t* buffer, size_t length,
|
|
||||||
BrotliMemOutput* mem_output) {
|
|
||||||
BrotliOutput output;
|
|
||||||
mem_output->buffer = buffer;
|
|
||||||
mem_output->length = length;
|
|
||||||
mem_output->pos = 0;
|
|
||||||
output.cb_ = &BrotliMemOutputFunction;
|
|
||||||
output.data_ = mem_output;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BrotliStdinInputFunction(void* data, uint8_t* buf, size_t count) {
|
|
||||||
#ifndef _WIN32
|
|
||||||
return (int)read(STDIN_FILENO, buf, count);
|
|
||||||
#else
|
|
||||||
return -1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
BrotliInput BrotliStdinInput() {
|
|
||||||
BrotliInput in;
|
|
||||||
in.cb_ = BrotliStdinInputFunction;
|
|
||||||
in.data_ = NULL;
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BrotliStdoutOutputFunction(void* data, const uint8_t* buf, size_t count) {
|
|
||||||
#ifndef _WIN32
|
|
||||||
return (int)write(STDOUT_FILENO, buf, count);
|
|
||||||
#else
|
|
||||||
return -1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
BrotliOutput BrotliStdoutOutput() {
|
|
||||||
BrotliOutput out;
|
|
||||||
out.cb_ = BrotliStdoutOutputFunction;
|
|
||||||
out.data_ = NULL;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BrotliFileOutputFunction(void* data, const uint8_t* buf, size_t count) {
|
|
||||||
return (int)fwrite(buf, 1, count, (FILE*)data);
|
|
||||||
}
|
|
||||||
|
|
||||||
BrotliOutput BrotliFileOutput(FILE* f) {
|
|
||||||
BrotliOutput out;
|
|
||||||
out.cb_ = BrotliFileOutputFunction;
|
|
||||||
out.data_ = f;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Functions for streaming input and output.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BROTLI_DEC_STREAMS_H_
|
|
||||||
#define BROTLI_DEC_STREAMS_H_
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "./types.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Function pointer type used to read len bytes into buf. Returns the */
|
|
||||||
/* number of bytes read or -1 on error. */
|
|
||||||
typedef int (*BrotliInputFunction)(void* data, uint8_t* buf, size_t len);
|
|
||||||
|
|
||||||
/* Input callback function with associated data. */
|
|
||||||
typedef struct {
|
|
||||||
BrotliInputFunction cb_;
|
|
||||||
void* data_;
|
|
||||||
} BrotliInput;
|
|
||||||
|
|
||||||
/* Reads len bytes into buf, using the in callback. */
|
|
||||||
static BROTLI_INLINE int BrotliRead(BrotliInput in, uint8_t* buf, size_t len) {
|
|
||||||
return in.cb_(in.data_, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Function pointer type used to write len bytes into buf. Returns the */
|
|
||||||
/* number of bytes written or -1 on error. */
|
|
||||||
typedef int (*BrotliOutputFunction)(void* data, const uint8_t* buf, size_t len);
|
|
||||||
|
|
||||||
/* Output callback function with associated data. */
|
|
||||||
typedef struct {
|
|
||||||
BrotliOutputFunction cb_;
|
|
||||||
void* data_;
|
|
||||||
} BrotliOutput;
|
|
||||||
|
|
||||||
/* Writes len bytes into buf, using the out callback. */
|
|
||||||
static BROTLI_INLINE int BrotliWrite(BrotliOutput out,
|
|
||||||
const uint8_t* buf, size_t len) {
|
|
||||||
return out.cb_(out.data_, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Memory region with position. */
|
|
||||||
typedef struct {
|
|
||||||
const uint8_t* buffer;
|
|
||||||
size_t length;
|
|
||||||
size_t pos;
|
|
||||||
} BrotliMemInput;
|
|
||||||
|
|
||||||
/* Input callback where *data is a BrotliMemInput struct. */
|
|
||||||
int BrotliMemInputFunction(void* data, uint8_t* buf, size_t count);
|
|
||||||
|
|
||||||
/* Returns an input callback that wraps the given memory region. */
|
|
||||||
BrotliInput BrotliInitMemInput(const uint8_t* buffer, size_t length,
|
|
||||||
BrotliMemInput* mem_input);
|
|
||||||
|
|
||||||
/* Output buffer with position. */
|
|
||||||
typedef struct {
|
|
||||||
uint8_t* buffer;
|
|
||||||
size_t length;
|
|
||||||
size_t pos;
|
|
||||||
} BrotliMemOutput;
|
|
||||||
|
|
||||||
/* Output callback where *data is a BrotliMemOutput struct. */
|
|
||||||
int BrotliMemOutputFunction(void* data, const uint8_t* buf, size_t count);
|
|
||||||
|
|
||||||
/* Returns an output callback that wraps the given memory region. */
|
|
||||||
BrotliOutput BrotliInitMemOutput(uint8_t* buffer, size_t length,
|
|
||||||
BrotliMemOutput* mem_output);
|
|
||||||
|
|
||||||
/* Input callback that reads from standard input. */
|
|
||||||
int BrotliStdinInputFunction(void* data, uint8_t* buf, size_t count);
|
|
||||||
BrotliInput BrotliStdinInput();
|
|
||||||
|
|
||||||
/* Output callback that writes to standard output. */
|
|
||||||
int BrotliStdoutOutputFunction(void* data, const uint8_t* buf, size_t count);
|
|
||||||
BrotliOutput BrotliStdoutOutput();
|
|
||||||
|
|
||||||
/* Output callback that writes to a file. */
|
|
||||||
int BrotliFileOutputFunction(void* data, const uint8_t* buf, size_t count);
|
|
||||||
BrotliOutput BrotliFileOutput(FILE* f);
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* BROTLI_DEC_STREAMS_H_ */
|
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Transformations on dictionary words.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BROTLI_DEC_TRANSFORM_H_
|
|
||||||
#define BROTLI_DEC_TRANSFORM_H_
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include "./types.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum WordTransformType {
|
|
||||||
kIdentity = 0,
|
|
||||||
kOmitLast1 = 1,
|
|
||||||
kOmitLast2 = 2,
|
|
||||||
kOmitLast3 = 3,
|
|
||||||
kOmitLast4 = 4,
|
|
||||||
kOmitLast5 = 5,
|
|
||||||
kOmitLast6 = 6,
|
|
||||||
kOmitLast7 = 7,
|
|
||||||
kOmitLast8 = 8,
|
|
||||||
kOmitLast9 = 9,
|
|
||||||
kUppercaseFirst = 10,
|
|
||||||
kUppercaseAll = 11,
|
|
||||||
kOmitFirst1 = 12,
|
|
||||||
kOmitFirst2 = 13,
|
|
||||||
kOmitFirst3 = 14,
|
|
||||||
kOmitFirst4 = 15,
|
|
||||||
kOmitFirst5 = 16,
|
|
||||||
kOmitFirst6 = 17,
|
|
||||||
kOmitFirst7 = 18,
|
|
||||||
kOmitFirst8 = 19,
|
|
||||||
kOmitFirst9 = 20
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char* prefix;
|
|
||||||
enum WordTransformType transform;
|
|
||||||
const char* suffix;
|
|
||||||
} Transform;
|
|
||||||
|
|
||||||
static const Transform kTransforms[] = {
|
|
||||||
{ "", kIdentity, "" },
|
|
||||||
{ "", kIdentity, " " },
|
|
||||||
{ " ", kIdentity, " " },
|
|
||||||
{ "", kOmitFirst1, "" },
|
|
||||||
{ "", kUppercaseFirst, " " },
|
|
||||||
{ "", kIdentity, " the " },
|
|
||||||
{ " ", kIdentity, "" },
|
|
||||||
{ "s ", kIdentity, " " },
|
|
||||||
{ "", kIdentity, " of " },
|
|
||||||
{ "", kUppercaseFirst, "" },
|
|
||||||
{ "", kIdentity, " and " },
|
|
||||||
{ "", kOmitFirst2, "" },
|
|
||||||
{ "", kOmitLast1, "" },
|
|
||||||
{ ", ", kIdentity, " " },
|
|
||||||
{ "", kIdentity, ", " },
|
|
||||||
{ " ", kUppercaseFirst, " " },
|
|
||||||
{ "", kIdentity, " in " },
|
|
||||||
{ "", kIdentity, " to " },
|
|
||||||
{ "e ", kIdentity, " " },
|
|
||||||
{ "", kIdentity, "\"" },
|
|
||||||
{ "", kIdentity, "." },
|
|
||||||
{ "", kIdentity, "\">" },
|
|
||||||
{ "", kIdentity, "\n" },
|
|
||||||
{ "", kOmitLast3, "" },
|
|
||||||
{ "", kIdentity, "]" },
|
|
||||||
{ "", kIdentity, " for " },
|
|
||||||
{ "", kOmitFirst3, "" },
|
|
||||||
{ "", kOmitLast2, "" },
|
|
||||||
{ "", kIdentity, " a " },
|
|
||||||
{ "", kIdentity, " that " },
|
|
||||||
{ " ", kUppercaseFirst, "" },
|
|
||||||
{ "", kIdentity, ". " },
|
|
||||||
{ ".", kIdentity, "" },
|
|
||||||
{ " ", kIdentity, ", " },
|
|
||||||
{ "", kOmitFirst4, "" },
|
|
||||||
{ "", kIdentity, " with " },
|
|
||||||
{ "", kIdentity, "'" },
|
|
||||||
{ "", kIdentity, " from " },
|
|
||||||
{ "", kIdentity, " by " },
|
|
||||||
{ "", kOmitFirst5, "" },
|
|
||||||
{ "", kOmitFirst6, "" },
|
|
||||||
{ " the ", kIdentity, "" },
|
|
||||||
{ "", kOmitLast4, "" },
|
|
||||||
{ "", kIdentity, ". The " },
|
|
||||||
{ "", kUppercaseAll, "" },
|
|
||||||
{ "", kIdentity, " on " },
|
|
||||||
{ "", kIdentity, " as " },
|
|
||||||
{ "", kIdentity, " is " },
|
|
||||||
{ "", kOmitLast7, "" },
|
|
||||||
{ "", kOmitLast1, "ing " },
|
|
||||||
{ "", kIdentity, "\n\t" },
|
|
||||||
{ "", kIdentity, ":" },
|
|
||||||
{ " ", kIdentity, ". " },
|
|
||||||
{ "", kIdentity, "ed " },
|
|
||||||
{ "", kOmitFirst9, "" },
|
|
||||||
{ "", kOmitFirst7, "" },
|
|
||||||
{ "", kOmitLast6, "" },
|
|
||||||
{ "", kIdentity, "(" },
|
|
||||||
{ "", kUppercaseFirst, ", " },
|
|
||||||
{ "", kOmitLast8, "" },
|
|
||||||
{ "", kIdentity, " at " },
|
|
||||||
{ "", kIdentity, "ly " },
|
|
||||||
{ " the ", kIdentity, " of " },
|
|
||||||
{ "", kOmitLast5, "" },
|
|
||||||
{ "", kOmitLast9, "" },
|
|
||||||
{ " ", kUppercaseFirst, ", " },
|
|
||||||
{ "", kUppercaseFirst, "\"" },
|
|
||||||
{ ".", kIdentity, "(" },
|
|
||||||
{ "", kUppercaseAll, " " },
|
|
||||||
{ "", kUppercaseFirst, "\">" },
|
|
||||||
{ "", kIdentity, "=\"" },
|
|
||||||
{ " ", kIdentity, "." },
|
|
||||||
{ ".com/", kIdentity, "" },
|
|
||||||
{ " the ", kIdentity, " of the " },
|
|
||||||
{ "", kUppercaseFirst, "'" },
|
|
||||||
{ "", kIdentity, ". This " },
|
|
||||||
{ "", kIdentity, "," },
|
|
||||||
{ ".", kIdentity, " " },
|
|
||||||
{ "", kUppercaseFirst, "(" },
|
|
||||||
{ "", kUppercaseFirst, "." },
|
|
||||||
{ "", kIdentity, " not " },
|
|
||||||
{ " ", kIdentity, "=\"" },
|
|
||||||
{ "", kIdentity, "er " },
|
|
||||||
{ " ", kUppercaseAll, " " },
|
|
||||||
{ "", kIdentity, "al " },
|
|
||||||
{ " ", kUppercaseAll, "" },
|
|
||||||
{ "", kIdentity, "='" },
|
|
||||||
{ "", kUppercaseAll, "\"" },
|
|
||||||
{ "", kUppercaseFirst, ". " },
|
|
||||||
{ " ", kIdentity, "(" },
|
|
||||||
{ "", kIdentity, "ful " },
|
|
||||||
{ " ", kUppercaseFirst, ". " },
|
|
||||||
{ "", kIdentity, "ive " },
|
|
||||||
{ "", kIdentity, "less " },
|
|
||||||
{ "", kUppercaseAll, "'" },
|
|
||||||
{ "", kIdentity, "est " },
|
|
||||||
{ " ", kUppercaseFirst, "." },
|
|
||||||
{ "", kUppercaseAll, "\">" },
|
|
||||||
{ " ", kIdentity, "='" },
|
|
||||||
{ "", kUppercaseFirst, "," },
|
|
||||||
{ "", kIdentity, "ize " },
|
|
||||||
{ "", kUppercaseAll, "." },
|
|
||||||
{ "\xc2\xa0", kIdentity, "" },
|
|
||||||
{ " ", kIdentity, "," },
|
|
||||||
{ "", kUppercaseFirst, "=\"" },
|
|
||||||
{ "", kUppercaseAll, "=\"" },
|
|
||||||
{ "", kIdentity, "ous " },
|
|
||||||
{ "", kUppercaseAll, ", " },
|
|
||||||
{ "", kUppercaseFirst, "='" },
|
|
||||||
{ " ", kUppercaseFirst, "," },
|
|
||||||
{ " ", kUppercaseAll, "=\"" },
|
|
||||||
{ " ", kUppercaseAll, ", " },
|
|
||||||
{ "", kUppercaseAll, "," },
|
|
||||||
{ "", kUppercaseAll, "(" },
|
|
||||||
{ "", kUppercaseAll, ". " },
|
|
||||||
{ " ", kUppercaseAll, "." },
|
|
||||||
{ "", kUppercaseAll, "='" },
|
|
||||||
{ " ", kUppercaseAll, ". " },
|
|
||||||
{ " ", kUppercaseFirst, "=\"" },
|
|
||||||
{ " ", kUppercaseAll, "='" },
|
|
||||||
{ " ", kUppercaseFirst, "='" },
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int kNumTransforms = sizeof(kTransforms) / sizeof(kTransforms[0]);
|
|
||||||
|
|
||||||
static int ToUpperCase(uint8_t *p, int len) {
|
|
||||||
if (p[0] < 0xc0) {
|
|
||||||
if (p[0] >= 'a' && p[0] <= 'z') {
|
|
||||||
p[0] ^= 32;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
/* An overly simplified uppercasing model for utf-8. */
|
|
||||||
if (p[0] < 0xe0) {
|
|
||||||
p[1] ^= 32;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
/* An arbitrary transform for three byte characters. */
|
|
||||||
p[2] ^= 5;
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE int TransformDictionaryWord(
|
|
||||||
uint8_t* dst, const uint8_t* word, int len, int transform) {
|
|
||||||
const char* prefix = kTransforms[transform].prefix;
|
|
||||||
const char* suffix = kTransforms[transform].suffix;
|
|
||||||
const int t = kTransforms[transform].transform;
|
|
||||||
int skip = t < kOmitFirst1 ? 0 : t - (kOmitFirst1 - 1);
|
|
||||||
int idx = 0;
|
|
||||||
int i = 0;
|
|
||||||
uint8_t* uppercase;
|
|
||||||
if (skip > len) {
|
|
||||||
skip = len;
|
|
||||||
}
|
|
||||||
while (*prefix) { dst[idx++] = (uint8_t)*prefix++; }
|
|
||||||
word += skip;
|
|
||||||
len -= skip;
|
|
||||||
if (t <= kOmitLast9) {
|
|
||||||
len -= t;
|
|
||||||
}
|
|
||||||
while (i < len) { dst[idx++] = word[i++]; }
|
|
||||||
uppercase = &dst[idx - len];
|
|
||||||
if (t == kUppercaseFirst) {
|
|
||||||
ToUpperCase(uppercase, len);
|
|
||||||
} else if (t == kUppercaseAll) {
|
|
||||||
while (len > 0) {
|
|
||||||
int step = ToUpperCase(uppercase, len);
|
|
||||||
uppercase += step;
|
|
||||||
len -= step;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (*suffix) { dst[idx++] = (uint8_t)*suffix++; }
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* BROTLI_DEC_TRANSFORM_H_ */
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Common types
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BROTLI_DEC_TYPES_H_
|
|
||||||
#define BROTLI_DEC_TYPES_H_
|
|
||||||
|
|
||||||
#include <stddef.h> /* for size_t */
|
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <inttypes.h>
|
|
||||||
#if defined(__cplusplus) || !defined(__STRICT_ANSI__) \
|
|
||||||
|| __STDC_VERSION__ >= 199901L
|
|
||||||
#define BROTLI_INLINE inline
|
|
||||||
#else
|
|
||||||
#define BROTLI_INLINE
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
typedef signed char int8_t;
|
|
||||||
typedef unsigned char uint8_t;
|
|
||||||
typedef signed short int16_t;
|
|
||||||
typedef unsigned short uint16_t;
|
|
||||||
typedef signed int int32_t;
|
|
||||||
typedef unsigned int uint32_t;
|
|
||||||
typedef unsigned long long int uint64_t;
|
|
||||||
typedef long long int int64_t;
|
|
||||||
#define BROTLI_INLINE __forceinline
|
|
||||||
#endif /* _MSC_VER */
|
|
||||||
|
|
||||||
#endif /* BROTLI_DEC_TYPES_H_ */
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#brotli/enc
|
|
||||||
|
|
||||||
include ../../shared.mk
|
|
||||||
|
|
||||||
OBJS = backward_references.o block_splitter.o encode.o entropy_encode.o histogram.o literal_cost.o prefix.o
|
|
||||||
|
|
||||||
all : $(OBJS)
|
|
||||||
|
|
||||||
clean :
|
|
||||||
rm -f $(OBJS) $(SO)
|
|
||||||
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
This directory holds the encoder for brotli compression format.
|
|
||||||
|
|
||||||
Brotli is proposed to be used at the byte-compression level in WOFF 2.0 format.
|
|
||||||
@@ -1,267 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Function to find backward reference copies.
|
|
||||||
|
|
||||||
#include "./backward_references.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "./command.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
template<typename Hasher>
|
|
||||||
void CreateBackwardReferences(size_t num_bytes,
|
|
||||||
size_t position,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
const float* literal_cost,
|
|
||||||
size_t ringbuffer_mask,
|
|
||||||
const size_t max_backward_limit,
|
|
||||||
Hasher* hasher,
|
|
||||||
std::vector<Command>* commands) {
|
|
||||||
// Length heuristic that seems to help probably by better selection
|
|
||||||
// of lazy matches of similar lengths.
|
|
||||||
int insert_length = 0;
|
|
||||||
size_t i = position & ringbuffer_mask;
|
|
||||||
const int i_diff = position - i;
|
|
||||||
const size_t i_end = i + num_bytes;
|
|
||||||
|
|
||||||
const int random_heuristics_window_size = 512;
|
|
||||||
int apply_random_heuristics = i + random_heuristics_window_size;
|
|
||||||
|
|
||||||
double average_cost = 0.0;
|
|
||||||
for (int k = position; k < position + num_bytes; ++k) {
|
|
||||||
average_cost += literal_cost[k & ringbuffer_mask];
|
|
||||||
}
|
|
||||||
average_cost /= num_bytes;
|
|
||||||
hasher->set_average_cost(average_cost);
|
|
||||||
|
|
||||||
// M1 match is for considering for two repeated copies, if moving
|
|
||||||
// one literal form the previous copy to the current one allows the
|
|
||||||
// current copy to be more efficient (because the way static dictionary
|
|
||||||
// codes words). M1 matching improves text compression density by ~0.15 %.
|
|
||||||
bool match_found_M1 = false;
|
|
||||||
size_t best_len_M1 = 0;
|
|
||||||
size_t best_len_code_M1 = 0;
|
|
||||||
size_t best_dist_M1 = 0;
|
|
||||||
double best_score_M1 = 0;
|
|
||||||
while (i + 2 < i_end) {
|
|
||||||
size_t best_len = 0;
|
|
||||||
size_t best_len_code = 0;
|
|
||||||
size_t best_dist = 0;
|
|
||||||
double best_score = 0;
|
|
||||||
size_t max_distance = std::min(i + i_diff, max_backward_limit);
|
|
||||||
bool in_dictionary;
|
|
||||||
hasher->set_insert_length(insert_length);
|
|
||||||
bool match_found = hasher->FindLongestMatch(
|
|
||||||
ringbuffer, literal_cost, ringbuffer_mask,
|
|
||||||
i + i_diff, i_end - i, max_distance,
|
|
||||||
&best_len, &best_len_code, &best_dist, &best_score,
|
|
||||||
&in_dictionary);
|
|
||||||
bool best_in_dictionary = in_dictionary;
|
|
||||||
if (match_found) {
|
|
||||||
if (match_found_M1 && best_score_M1 > best_score) {
|
|
||||||
// Two copies after each other. Take the last literal from the
|
|
||||||
// last copy, and use it as the first of this one.
|
|
||||||
(commands->rbegin())->copy_length_ -= 1;
|
|
||||||
(commands->rbegin())->copy_length_code_ -= 1;
|
|
||||||
hasher->Store(ringbuffer + i, i + i_diff);
|
|
||||||
--i;
|
|
||||||
best_len = best_len_M1;
|
|
||||||
best_len_code = best_len_code_M1;
|
|
||||||
best_dist = best_dist_M1;
|
|
||||||
best_score = best_score_M1;
|
|
||||||
// in_dictionary doesn't need to be correct, but it is the only
|
|
||||||
// reason why M1 matching should be beneficial here. Setting it here
|
|
||||||
// will only disable further M1 matching against this copy.
|
|
||||||
best_in_dictionary = true;
|
|
||||||
in_dictionary = true;
|
|
||||||
} else {
|
|
||||||
// Found a match. Let's look for something even better ahead.
|
|
||||||
int delayed_backward_references_in_row = 0;
|
|
||||||
while (i + 4 < i_end &&
|
|
||||||
delayed_backward_references_in_row < 4) {
|
|
||||||
size_t best_len_2 = 0;
|
|
||||||
size_t best_len_code_2 = 0;
|
|
||||||
size_t best_dist_2 = 0;
|
|
||||||
double best_score_2 = 0;
|
|
||||||
max_distance = std::min(i + i_diff + 1, max_backward_limit);
|
|
||||||
hasher->Store(ringbuffer + i, i + i_diff);
|
|
||||||
match_found = hasher->FindLongestMatch(
|
|
||||||
ringbuffer, literal_cost, ringbuffer_mask,
|
|
||||||
i + i_diff + 1, i_end - i - 1, max_distance,
|
|
||||||
&best_len_2, &best_len_code_2, &best_dist_2, &best_score_2,
|
|
||||||
&in_dictionary);
|
|
||||||
double cost_diff_lazy = 0;
|
|
||||||
if (best_len >= 4) {
|
|
||||||
cost_diff_lazy +=
|
|
||||||
literal_cost[(i + 4) & ringbuffer_mask] - average_cost;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const int tail_length = best_len_2 - best_len + 1;
|
|
||||||
for (int k = 0; k < tail_length; ++k) {
|
|
||||||
cost_diff_lazy -=
|
|
||||||
literal_cost[(i + best_len + k) & ringbuffer_mask] -
|
|
||||||
average_cost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If we are not inserting any symbols, inserting one is more
|
|
||||||
// expensive than if we were inserting symbols anyways.
|
|
||||||
if (insert_length < 1) {
|
|
||||||
cost_diff_lazy += 0.97;
|
|
||||||
}
|
|
||||||
// Add bias to slightly avoid lazy matching.
|
|
||||||
cost_diff_lazy += 2.0 + delayed_backward_references_in_row * 0.2;
|
|
||||||
cost_diff_lazy += 0.04 * literal_cost[i & ringbuffer_mask];
|
|
||||||
|
|
||||||
if (match_found && best_score_2 >= best_score + cost_diff_lazy) {
|
|
||||||
// Ok, let's just write one byte for now and start a match from the
|
|
||||||
// next byte.
|
|
||||||
++insert_length;
|
|
||||||
++delayed_backward_references_in_row;
|
|
||||||
best_len = best_len_2;
|
|
||||||
best_len_code = best_len_code_2;
|
|
||||||
best_dist = best_dist_2;
|
|
||||||
best_score = best_score_2;
|
|
||||||
best_in_dictionary = in_dictionary;
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply_random_heuristics =
|
|
||||||
i + 2 * best_len + random_heuristics_window_size;
|
|
||||||
Command cmd;
|
|
||||||
cmd.insert_length_ = insert_length;
|
|
||||||
cmd.copy_length_ = best_len;
|
|
||||||
cmd.copy_length_code_ = best_len_code;
|
|
||||||
cmd.copy_distance_ = best_dist;
|
|
||||||
commands->push_back(cmd);
|
|
||||||
insert_length = 0;
|
|
||||||
++i;
|
|
||||||
if (best_dist <= std::min(i + i_diff, max_backward_limit)) {
|
|
||||||
hasher->set_last_distance(best_dist);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy all copied literals to the hasher, except the last one.
|
|
||||||
// We cannot store the last one yet, otherwise we couldn't find
|
|
||||||
// the possible M1 match.
|
|
||||||
for (int j = 1; j < best_len - 1; ++j) {
|
|
||||||
if (i + 2 < i_end) {
|
|
||||||
hasher->Store(ringbuffer + i, i + i_diff);
|
|
||||||
}
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
// Prepare M1 match.
|
|
||||||
if (hasher->HasStaticDictionary() &&
|
|
||||||
best_len >= 4 && i + 20 < i_end && !best_in_dictionary) {
|
|
||||||
max_distance = std::min(i + i_diff, max_backward_limit);
|
|
||||||
match_found_M1 = hasher->FindLongestMatch(
|
|
||||||
ringbuffer, literal_cost, ringbuffer_mask,
|
|
||||||
i + i_diff, i_end - i, max_distance,
|
|
||||||
&best_len_M1, &best_len_code_M1, &best_dist_M1, &best_score_M1,
|
|
||||||
&in_dictionary);
|
|
||||||
} else {
|
|
||||||
match_found_M1 = false;
|
|
||||||
in_dictionary = false;
|
|
||||||
}
|
|
||||||
// This byte is just moved from the previous copy to the current,
|
|
||||||
// that is no gain.
|
|
||||||
best_score_M1 -= literal_cost[i & ringbuffer_mask];
|
|
||||||
// Adjust for losing the opportunity for lazy matching.
|
|
||||||
best_score_M1 -= 3.75;
|
|
||||||
|
|
||||||
// Store the last one of the match.
|
|
||||||
if (i + 2 < i_end) {
|
|
||||||
hasher->Store(ringbuffer + i, i + i_diff);
|
|
||||||
}
|
|
||||||
++i;
|
|
||||||
} else {
|
|
||||||
match_found_M1 = false;
|
|
||||||
++insert_length;
|
|
||||||
hasher->Store(ringbuffer + i, i + i_diff);
|
|
||||||
++i;
|
|
||||||
// If we have not seen matches for a long time, we can skip some
|
|
||||||
// match lookups. Unsuccessful match lookups are very very expensive
|
|
||||||
// and this kind of a heuristic speeds up compression quite
|
|
||||||
// a lot.
|
|
||||||
if (i > apply_random_heuristics) {
|
|
||||||
// Going through uncompressible data, jump.
|
|
||||||
if (i > apply_random_heuristics + 4 * random_heuristics_window_size) {
|
|
||||||
// It is quite a long time since we saw a copy, so we assume
|
|
||||||
// that this data is not compressible, and store hashes less
|
|
||||||
// often. Hashes of non compressible data are less likely to
|
|
||||||
// turn out to be useful in the future, too, so we store less of
|
|
||||||
// them to not to flood out the hash table of good compressible
|
|
||||||
// data.
|
|
||||||
int i_jump = std::min(i + 16, i_end - 4);
|
|
||||||
for (; i < i_jump; i += 4) {
|
|
||||||
hasher->Store(ringbuffer + i, i + i_diff);
|
|
||||||
insert_length += 4;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int i_jump = std::min(i + 8, i_end - 2);
|
|
||||||
for (; i < i_jump; i += 2) {
|
|
||||||
hasher->Store(ringbuffer + i, i + i_diff);
|
|
||||||
insert_length += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
insert_length += (i_end - i);
|
|
||||||
|
|
||||||
if (insert_length > 0) {
|
|
||||||
Command cmd;
|
|
||||||
cmd.insert_length_ = insert_length;
|
|
||||||
cmd.copy_length_ = 0;
|
|
||||||
cmd.copy_distance_ = 0;
|
|
||||||
commands->push_back(cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateBackwardReferences(size_t num_bytes,
|
|
||||||
size_t position,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
const float* literal_cost,
|
|
||||||
size_t ringbuffer_mask,
|
|
||||||
const size_t max_backward_limit,
|
|
||||||
Hashers* hashers,
|
|
||||||
Hashers::Type hash_type,
|
|
||||||
std::vector<Command>* commands) {
|
|
||||||
switch (hash_type) {
|
|
||||||
case Hashers::HASH_15_8_4:
|
|
||||||
CreateBackwardReferences(
|
|
||||||
num_bytes, position, ringbuffer, literal_cost,
|
|
||||||
ringbuffer_mask, max_backward_limit,
|
|
||||||
hashers->hash_15_8_4.get(),
|
|
||||||
commands);
|
|
||||||
break;
|
|
||||||
case Hashers::HASH_15_8_2:
|
|
||||||
CreateBackwardReferences(
|
|
||||||
num_bytes, position, ringbuffer, literal_cost,
|
|
||||||
ringbuffer_mask, max_backward_limit,
|
|
||||||
hashers->hash_15_8_2.get(),
|
|
||||||
commands);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Function to find backward reference copies.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_BACKWARD_REFERENCES_H_
|
|
||||||
#define BROTLI_ENC_BACKWARD_REFERENCES_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "./hash.h"
|
|
||||||
#include "./command.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
void CreateBackwardReferences(size_t num_bytes,
|
|
||||||
size_t position,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
const float* literal_cost,
|
|
||||||
size_t ringbuffer_mask,
|
|
||||||
const size_t max_backward_limit,
|
|
||||||
Hashers* hashers,
|
|
||||||
Hashers::Type hash_type,
|
|
||||||
std::vector<Command>* commands);
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_BACKWARD_REFERENCES_H_
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Functions to estimate the bit cost of Huffman trees.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_BIT_COST_H_
|
|
||||||
#define BROTLI_ENC_BIT_COST_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "./entropy_encode.h"
|
|
||||||
#include "./fast_log.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
static const int kHuffmanExtraBits[kCodeLengthCodes] = {
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline int HuffmanTreeBitCost(const int* counts, const uint8_t* depth) {
|
|
||||||
int nbits = 0;
|
|
||||||
for (int i = 0; i < kCodeLengthCodes; ++i) {
|
|
||||||
nbits += counts[i] * (depth[i] + kHuffmanExtraBits[i]);
|
|
||||||
}
|
|
||||||
return nbits;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int HuffmanTreeBitCost(
|
|
||||||
const Histogram<kCodeLengthCodes>& histogram,
|
|
||||||
const EntropyCode<kCodeLengthCodes>& entropy) {
|
|
||||||
return HuffmanTreeBitCost(&histogram.data_[0], &entropy.depth_[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int HuffmanBitCost(const uint8_t* depth, int length) {
|
|
||||||
int max_depth = 1;
|
|
||||||
int histogram[kCodeLengthCodes] = { 0 };
|
|
||||||
int tail_start = 0;
|
|
||||||
int prev_value = 8;
|
|
||||||
// compute histogram of compacted huffman tree
|
|
||||||
for (int i = 0; i < length;) {
|
|
||||||
const int value = depth[i];
|
|
||||||
if (value > max_depth) {
|
|
||||||
max_depth = value;
|
|
||||||
}
|
|
||||||
int reps = 1;
|
|
||||||
for (int k = i + 1; k < length && depth[k] == value; ++k) {
|
|
||||||
++reps;
|
|
||||||
}
|
|
||||||
i += reps;
|
|
||||||
if (i == length && value == 0)
|
|
||||||
break;
|
|
||||||
if (value == 0) {
|
|
||||||
if (reps < 3) {
|
|
||||||
histogram[0] += reps;
|
|
||||||
} else {
|
|
||||||
reps -= 2;
|
|
||||||
while (reps > 0) {
|
|
||||||
++histogram[17];
|
|
||||||
reps >>= 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tail_start = i;
|
|
||||||
if (value != prev_value) {
|
|
||||||
++histogram[value];
|
|
||||||
--reps;
|
|
||||||
}
|
|
||||||
prev_value = value;
|
|
||||||
if (reps < 3) {
|
|
||||||
histogram[value] += reps;
|
|
||||||
} else {
|
|
||||||
reps -= 2;
|
|
||||||
while (reps > 0) {
|
|
||||||
++histogram[16];
|
|
||||||
reps >>= 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create huffman tree of huffman tree
|
|
||||||
uint8_t cost[kCodeLengthCodes] = { 0 };
|
|
||||||
CreateHuffmanTree(histogram, kCodeLengthCodes, 7, cost);
|
|
||||||
// account for rle extra bits
|
|
||||||
cost[16] += 2;
|
|
||||||
cost[17] += 3;
|
|
||||||
|
|
||||||
int tree_size = 0;
|
|
||||||
int bits = 18 + 2 * max_depth; // huffman tree of huffman tree cost
|
|
||||||
for (int i = 0; i < kCodeLengthCodes; ++i) {
|
|
||||||
bits += histogram[i] * cost[i]; // huffman tree bit cost
|
|
||||||
tree_size += histogram[i];
|
|
||||||
}
|
|
||||||
return bits;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int kSize>
|
|
||||||
double PopulationCost(const Histogram<kSize>& histogram) {
|
|
||||||
if (histogram.total_count_ == 0) {
|
|
||||||
return 12;
|
|
||||||
}
|
|
||||||
int count = 0;
|
|
||||||
for (int i = 0; i < kSize && count < 5; ++i) {
|
|
||||||
if (histogram.data_[i] > 0) {
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (count == 1) {
|
|
||||||
return 12;
|
|
||||||
}
|
|
||||||
if (count == 2) {
|
|
||||||
return 20 + histogram.total_count_;
|
|
||||||
}
|
|
||||||
uint8_t depth[kSize] = { 0 };
|
|
||||||
CreateHuffmanTree(&histogram.data_[0], kSize, 15, depth);
|
|
||||||
int bits = 0;
|
|
||||||
for (int i = 0; i < kSize; ++i) {
|
|
||||||
bits += histogram.data_[i] * depth[i];
|
|
||||||
}
|
|
||||||
if (count == 3) {
|
|
||||||
bits += 28;
|
|
||||||
} else if (count == 4) {
|
|
||||||
bits += 37;
|
|
||||||
} else {
|
|
||||||
bits += HuffmanBitCost(depth, kSize);
|
|
||||||
}
|
|
||||||
return bits;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_BIT_COST_H_
|
|
||||||
@@ -1,391 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Block split point selection utilities.
|
|
||||||
|
|
||||||
#include "./block_splitter.h"
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "./cluster.h"
|
|
||||||
#include "./command.h"
|
|
||||||
#include "./fast_log.h"
|
|
||||||
#include "./histogram.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
static const int kMaxLiteralHistograms = 100;
|
|
||||||
static const int kMaxCommandHistograms = 50;
|
|
||||||
static const double kLiteralBlockSwitchCost = 26;
|
|
||||||
static const double kCommandBlockSwitchCost = 13.5;
|
|
||||||
static const double kDistanceBlockSwitchCost = 14.6;
|
|
||||||
static const int kLiteralStrideLength = 70;
|
|
||||||
static const int kCommandStrideLength = 40;
|
|
||||||
static const int kSymbolsPerLiteralHistogram = 544;
|
|
||||||
static const int kSymbolsPerCommandHistogram = 530;
|
|
||||||
static const int kSymbolsPerDistanceHistogram = 544;
|
|
||||||
static const int kMinLengthForBlockSplitting = 128;
|
|
||||||
static const int kIterMulForRefining = 2;
|
|
||||||
static const int kMinItersForRefining = 100;
|
|
||||||
|
|
||||||
void CopyLiteralsToByteArray(const std::vector<Command>& cmds,
|
|
||||||
const uint8_t* data,
|
|
||||||
std::vector<uint8_t>* literals) {
|
|
||||||
// Count how many we have.
|
|
||||||
size_t total_length = 0;
|
|
||||||
for (int i = 0; i < cmds.size(); ++i) {
|
|
||||||
total_length += cmds[i].insert_length_;
|
|
||||||
}
|
|
||||||
if (total_length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate.
|
|
||||||
literals->resize(total_length);
|
|
||||||
|
|
||||||
// Loop again, and copy this time.
|
|
||||||
size_t pos = 0;
|
|
||||||
size_t from_pos = 0;
|
|
||||||
for (int i = 0; i < cmds.size() && pos < total_length; ++i) {
|
|
||||||
memcpy(&(*literals)[pos], data + from_pos, cmds[i].insert_length_);
|
|
||||||
pos += cmds[i].insert_length_;
|
|
||||||
from_pos += cmds[i].insert_length_ + cmds[i].copy_length_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CopyCommandsToByteArray(const std::vector<Command>& cmds,
|
|
||||||
std::vector<uint16_t>* insert_and_copy_codes,
|
|
||||||
std::vector<uint8_t>* distance_prefixes) {
|
|
||||||
for (int i = 0; i < cmds.size(); ++i) {
|
|
||||||
const Command& cmd = cmds[i];
|
|
||||||
insert_and_copy_codes->push_back(cmd.command_prefix_);
|
|
||||||
if (cmd.copy_length_ > 0 && cmd.distance_prefix_ != 0xffff) {
|
|
||||||
distance_prefixes->push_back(cmd.distance_prefix_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename HistogramType, typename DataType>
|
|
||||||
void InitialEntropyCodes(const DataType* data, size_t length,
|
|
||||||
int literals_per_histogram,
|
|
||||||
int max_histograms,
|
|
||||||
size_t stride,
|
|
||||||
std::vector<HistogramType>* vec) {
|
|
||||||
int total_histograms = length / literals_per_histogram + 1;
|
|
||||||
if (total_histograms > max_histograms) {
|
|
||||||
total_histograms = max_histograms;
|
|
||||||
}
|
|
||||||
unsigned int seed = 7;
|
|
||||||
int block_length = length / total_histograms;
|
|
||||||
for (int i = 0; i < total_histograms; ++i) {
|
|
||||||
int pos = length * i / total_histograms;
|
|
||||||
if (i != 0) {
|
|
||||||
pos += rand_r(&seed) % block_length;
|
|
||||||
}
|
|
||||||
if (pos + stride >= length) {
|
|
||||||
pos = length - stride - 1;
|
|
||||||
}
|
|
||||||
HistogramType histo;
|
|
||||||
histo.Add(data + pos, stride);
|
|
||||||
vec->push_back(histo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename HistogramType, typename DataType>
|
|
||||||
void RandomSample(unsigned int* seed,
|
|
||||||
const DataType* data,
|
|
||||||
size_t length,
|
|
||||||
size_t stride,
|
|
||||||
HistogramType* sample) {
|
|
||||||
size_t pos = 0;
|
|
||||||
if (stride >= length) {
|
|
||||||
pos = 0;
|
|
||||||
stride = length;
|
|
||||||
} else {
|
|
||||||
pos = rand_r(seed) % (length - stride + 1);
|
|
||||||
}
|
|
||||||
sample->Add(data + pos, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename HistogramType, typename DataType>
|
|
||||||
void RefineEntropyCodes(const DataType* data, size_t length,
|
|
||||||
size_t stride,
|
|
||||||
std::vector<HistogramType>* vec) {
|
|
||||||
int iters =
|
|
||||||
kIterMulForRefining * length / stride + kMinItersForRefining;
|
|
||||||
unsigned int seed = 7;
|
|
||||||
iters = ((iters + vec->size() - 1) / vec->size()) * vec->size();
|
|
||||||
for (int iter = 0; iter < iters; ++iter) {
|
|
||||||
HistogramType sample;
|
|
||||||
RandomSample(&seed, data, length, stride, &sample);
|
|
||||||
int ix = iter % vec->size();
|
|
||||||
(*vec)[ix].AddHistogram(sample);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static float BitCost(int total, int count) {
|
|
||||||
return count == 0 ? FastLog2(total) + 2 : FastLog2(total) - FastLog2(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename DataType, int kSize>
|
|
||||||
void FindBlocks(const DataType* data, const size_t length,
|
|
||||||
const double block_switch_bitcost,
|
|
||||||
const std::vector<Histogram<kSize> > &vec,
|
|
||||||
uint8_t *block_id) {
|
|
||||||
if (vec.size() <= 1) {
|
|
||||||
for (int i = 0; i < length; ++i) {
|
|
||||||
block_id[i] = 0;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int vecsize = vec.size();
|
|
||||||
double* insert_cost = new double[kSize * vecsize];
|
|
||||||
memset(insert_cost, 0, sizeof(insert_cost[0]) * kSize * vecsize);
|
|
||||||
for (int i = 0; i < kSize; ++i) {
|
|
||||||
for (int j = 0; j < vecsize; ++j) {
|
|
||||||
insert_cost[i * vecsize + j] =
|
|
||||||
BitCost(vec[j].total_count_, vec[j].data_[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
double *cost = new double[vecsize];
|
|
||||||
memset(cost, 0, sizeof(cost[0]) * vecsize);
|
|
||||||
bool* switch_signal = new bool[length * vecsize];
|
|
||||||
memset(switch_signal, 0, sizeof(switch_signal[0]) * length * vecsize);
|
|
||||||
// After each iteration of this loop, cost[k] will contain the difference
|
|
||||||
// between the minimum cost of arriving at the current byte position using
|
|
||||||
// entropy code k, and the minimum cost of arriving at the current byte
|
|
||||||
// position. This difference is capped at the block switch cost, and if it
|
|
||||||
// reaches block switch cost, it means that when we trace back from the last
|
|
||||||
// position, we need to switch here.
|
|
||||||
for (size_t byte_ix = 0; byte_ix < length; ++byte_ix) {
|
|
||||||
int ix = byte_ix * vecsize;
|
|
||||||
int insert_cost_ix = data[byte_ix] * vecsize;
|
|
||||||
double min_cost = 1e99;
|
|
||||||
for (int k = 0; k < vecsize; ++k) {
|
|
||||||
// We are coding the symbol in data[byte_ix] with entropy code k.
|
|
||||||
cost[k] += insert_cost[insert_cost_ix + k];
|
|
||||||
if (cost[k] < min_cost) {
|
|
||||||
min_cost = cost[k];
|
|
||||||
block_id[byte_ix] = k;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
double block_switch_cost = block_switch_bitcost;
|
|
||||||
// More blocks for the beginning.
|
|
||||||
if (byte_ix < 2000) {
|
|
||||||
block_switch_cost *= 0.77 + 0.07 * byte_ix / 2000;
|
|
||||||
}
|
|
||||||
for (int k = 0; k < vecsize; ++k) {
|
|
||||||
cost[k] -= min_cost;
|
|
||||||
if (cost[k] >= block_switch_cost) {
|
|
||||||
cost[k] = block_switch_cost;
|
|
||||||
switch_signal[ix + k] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Now trace back from the last position and switch at the marked places.
|
|
||||||
int byte_ix = length - 1;
|
|
||||||
int ix = byte_ix * vecsize;
|
|
||||||
int cur_id = block_id[byte_ix];
|
|
||||||
while (byte_ix > 0) {
|
|
||||||
--byte_ix;
|
|
||||||
ix -= vecsize;
|
|
||||||
if (switch_signal[ix + cur_id]) {
|
|
||||||
cur_id = block_id[byte_ix];
|
|
||||||
}
|
|
||||||
block_id[byte_ix] = cur_id;
|
|
||||||
}
|
|
||||||
delete[] insert_cost;
|
|
||||||
delete[] cost;
|
|
||||||
delete[] switch_signal;
|
|
||||||
}
|
|
||||||
|
|
||||||
int RemapBlockIds(uint8_t* block_ids, const size_t length) {
|
|
||||||
std::map<uint8_t, uint8_t> new_id;
|
|
||||||
int next_id = 0;
|
|
||||||
for (int i = 0; i < length; ++i) {
|
|
||||||
if (new_id.find(block_ids[i]) == new_id.end()) {
|
|
||||||
new_id[block_ids[i]] = next_id;
|
|
||||||
++next_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i < length; ++i) {
|
|
||||||
block_ids[i] = new_id[block_ids[i]];
|
|
||||||
}
|
|
||||||
return next_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename HistogramType, typename DataType>
|
|
||||||
void BuildBlockHistograms(const DataType* data, const size_t length,
|
|
||||||
uint8_t* block_ids,
|
|
||||||
std::vector<HistogramType>* histograms) {
|
|
||||||
int num_types = RemapBlockIds(block_ids, length);
|
|
||||||
histograms->clear();
|
|
||||||
histograms->resize(num_types);
|
|
||||||
for (int i = 0; i < length; ++i) {
|
|
||||||
(*histograms)[block_ids[i]].Add(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename HistogramType, typename DataType>
|
|
||||||
void ClusterBlocks(const DataType* data, const size_t length,
|
|
||||||
uint8_t* block_ids) {
|
|
||||||
std::vector<HistogramType> histograms;
|
|
||||||
std::vector<int> block_index(length);
|
|
||||||
int cur_idx = 0;
|
|
||||||
HistogramType cur_histogram;
|
|
||||||
for (int i = 0; i < length; ++i) {
|
|
||||||
bool block_boundary = (i + 1 == length || block_ids[i] != block_ids[i + 1]);
|
|
||||||
block_index[i] = cur_idx;
|
|
||||||
cur_histogram.Add(data[i]);
|
|
||||||
if (block_boundary) {
|
|
||||||
histograms.push_back(cur_histogram);
|
|
||||||
cur_histogram.Clear();
|
|
||||||
++cur_idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::vector<HistogramType> clustered_histograms;
|
|
||||||
std::vector<int> histogram_symbols;
|
|
||||||
// Block ids need to fit in one byte.
|
|
||||||
static const int kMaxNumberOfBlockTypes = 256;
|
|
||||||
ClusterHistograms(histograms, 1, histograms.size(),
|
|
||||||
kMaxNumberOfBlockTypes,
|
|
||||||
&clustered_histograms,
|
|
||||||
&histogram_symbols);
|
|
||||||
for (int i = 0; i < length; ++i) {
|
|
||||||
block_ids[i] = histogram_symbols[block_index[i]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuildBlockSplit(const std::vector<uint8_t>& block_ids, BlockSplit* split) {
|
|
||||||
int cur_id = block_ids[0];
|
|
||||||
int cur_length = 1;
|
|
||||||
split->num_types_ = -1;
|
|
||||||
for (int i = 1; i < block_ids.size(); ++i) {
|
|
||||||
if (block_ids[i] != cur_id) {
|
|
||||||
split->types_.push_back(cur_id);
|
|
||||||
split->lengths_.push_back(cur_length);
|
|
||||||
split->num_types_ = std::max(split->num_types_, cur_id);
|
|
||||||
cur_id = block_ids[i];
|
|
||||||
cur_length = 0;
|
|
||||||
}
|
|
||||||
++cur_length;
|
|
||||||
}
|
|
||||||
split->types_.push_back(cur_id);
|
|
||||||
split->lengths_.push_back(cur_length);
|
|
||||||
split->num_types_ = std::max(split->num_types_, cur_id);
|
|
||||||
++split->num_types_;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename HistogramType, typename DataType>
|
|
||||||
void SplitByteVector(const std::vector<DataType>& data,
|
|
||||||
const int literals_per_histogram,
|
|
||||||
const int max_histograms,
|
|
||||||
const int sampling_stride_length,
|
|
||||||
const double block_switch_cost,
|
|
||||||
BlockSplit* split) {
|
|
||||||
if (data.empty()) {
|
|
||||||
split->num_types_ = 0;
|
|
||||||
return;
|
|
||||||
} else if (data.size() < kMinLengthForBlockSplitting) {
|
|
||||||
split->num_types_ = 1;
|
|
||||||
split->types_.push_back(0);
|
|
||||||
split->lengths_.push_back(data.size());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::vector<HistogramType> histograms;
|
|
||||||
// Find good entropy codes.
|
|
||||||
InitialEntropyCodes(data.data(), data.size(),
|
|
||||||
literals_per_histogram,
|
|
||||||
max_histograms,
|
|
||||||
sampling_stride_length,
|
|
||||||
&histograms);
|
|
||||||
RefineEntropyCodes(data.data(), data.size(),
|
|
||||||
sampling_stride_length,
|
|
||||||
&histograms);
|
|
||||||
// Find a good path through literals with the good entropy codes.
|
|
||||||
std::vector<uint8_t> block_ids(data.size());
|
|
||||||
for (int i = 0; i < 10; ++i) {
|
|
||||||
FindBlocks(data.data(), data.size(),
|
|
||||||
block_switch_cost,
|
|
||||||
histograms,
|
|
||||||
&block_ids[0]);
|
|
||||||
BuildBlockHistograms(data.data(), data.size(), &block_ids[0], &histograms);
|
|
||||||
}
|
|
||||||
ClusterBlocks<HistogramType>(data.data(), data.size(), &block_ids[0]);
|
|
||||||
BuildBlockSplit(block_ids, split);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SplitBlock(const std::vector<Command>& cmds,
|
|
||||||
const uint8_t* data,
|
|
||||||
BlockSplit* literal_split,
|
|
||||||
BlockSplit* insert_and_copy_split,
|
|
||||||
BlockSplit* dist_split) {
|
|
||||||
// Create a continuous array of literals.
|
|
||||||
std::vector<uint8_t> literals;
|
|
||||||
CopyLiteralsToByteArray(cmds, data, &literals);
|
|
||||||
|
|
||||||
// Compute prefix codes for commands.
|
|
||||||
std::vector<uint16_t> insert_and_copy_codes;
|
|
||||||
std::vector<uint8_t> distance_prefixes;
|
|
||||||
CopyCommandsToByteArray(cmds,
|
|
||||||
&insert_and_copy_codes,
|
|
||||||
&distance_prefixes);
|
|
||||||
|
|
||||||
|
|
||||||
SplitByteVector<HistogramLiteral>(
|
|
||||||
literals,
|
|
||||||
kSymbolsPerLiteralHistogram, kMaxLiteralHistograms,
|
|
||||||
kLiteralStrideLength, kLiteralBlockSwitchCost,
|
|
||||||
literal_split);
|
|
||||||
SplitByteVector<HistogramCommand>(
|
|
||||||
insert_and_copy_codes,
|
|
||||||
kSymbolsPerCommandHistogram, kMaxCommandHistograms,
|
|
||||||
kCommandStrideLength, kCommandBlockSwitchCost,
|
|
||||||
insert_and_copy_split);
|
|
||||||
SplitByteVector<HistogramDistance>(
|
|
||||||
distance_prefixes,
|
|
||||||
kSymbolsPerDistanceHistogram, kMaxCommandHistograms,
|
|
||||||
kCommandStrideLength, kDistanceBlockSwitchCost,
|
|
||||||
dist_split);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SplitBlockByTotalLength(const std::vector<Command>& all_commands,
|
|
||||||
int input_size,
|
|
||||||
int target_length,
|
|
||||||
std::vector<std::vector<Command> >* blocks) {
|
|
||||||
int num_blocks = input_size / target_length + 1;
|
|
||||||
int length_limit = input_size / num_blocks + 1;
|
|
||||||
int total_length = 0;
|
|
||||||
std::vector<Command> cur_block;
|
|
||||||
for (int i = 0; i < all_commands.size(); ++i) {
|
|
||||||
const Command& cmd = all_commands[i];
|
|
||||||
int cmd_length = cmd.insert_length_ + cmd.copy_length_;
|
|
||||||
if (total_length > length_limit) {
|
|
||||||
blocks->push_back(cur_block);
|
|
||||||
cur_block.clear();
|
|
||||||
total_length = 0;
|
|
||||||
}
|
|
||||||
cur_block.push_back(cmd);
|
|
||||||
total_length += cmd_length;
|
|
||||||
}
|
|
||||||
blocks->push_back(cur_block);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Block split point selection utilities.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_BLOCK_SPLITTER_H_
|
|
||||||
#define BROTLI_ENC_BLOCK_SPLITTER_H_
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "./command.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
struct BlockSplit {
|
|
||||||
int num_types_;
|
|
||||||
std::vector<uint8_t> types_;
|
|
||||||
std::vector<int> type_codes_;
|
|
||||||
std::vector<int> lengths_;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BlockSplitIterator {
|
|
||||||
explicit BlockSplitIterator(const BlockSplit& split)
|
|
||||||
: split_(split), idx_(0), type_(0), length_(0) {
|
|
||||||
if (!split.lengths_.empty()) {
|
|
||||||
length_ = split.lengths_[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Next() {
|
|
||||||
if (length_ == 0) {
|
|
||||||
++idx_;
|
|
||||||
type_ = split_.types_[idx_];
|
|
||||||
length_ = split_.lengths_[idx_];
|
|
||||||
}
|
|
||||||
--length_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BlockSplit& split_;
|
|
||||||
int idx_;
|
|
||||||
int type_;
|
|
||||||
int length_;
|
|
||||||
};
|
|
||||||
|
|
||||||
void CopyLiteralsToByteArray(const std::vector<Command>& cmds,
|
|
||||||
const uint8_t* data,
|
|
||||||
std::vector<uint8_t>* literals);
|
|
||||||
|
|
||||||
void SplitBlock(const std::vector<Command>& cmds,
|
|
||||||
const uint8_t* data,
|
|
||||||
BlockSplit* literal_split,
|
|
||||||
BlockSplit* insert_and_copy_split,
|
|
||||||
BlockSplit* dist_split);
|
|
||||||
|
|
||||||
void SplitBlockByTotalLength(const std::vector<Command>& all_commands,
|
|
||||||
int input_size,
|
|
||||||
int target_length,
|
|
||||||
std::vector<std::vector<Command> >* blocks);
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_BLOCK_SPLITTER_H_
|
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Functions for clustering similar histograms together.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_CLUSTER_H_
|
|
||||||
#define BROTLI_ENC_CLUSTER_H_
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <complex>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "./bit_cost.h"
|
|
||||||
#include "./entropy_encode.h"
|
|
||||||
#include "./fast_log.h"
|
|
||||||
#include "./histogram.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
struct HistogramPair {
|
|
||||||
int idx1;
|
|
||||||
int idx2;
|
|
||||||
bool valid;
|
|
||||||
double cost_combo;
|
|
||||||
double cost_diff;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HistogramPairComparator {
|
|
||||||
bool operator()(const HistogramPair& p1, const HistogramPair& p2) {
|
|
||||||
if (p1.cost_diff != p2.cost_diff) {
|
|
||||||
return p1.cost_diff > p2.cost_diff;
|
|
||||||
}
|
|
||||||
return abs(p1.idx1 - p1.idx2) > abs(p2.idx1 - p2.idx2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Returns entropy reduction of the context map when we combine two clusters.
|
|
||||||
inline double ClusterCostDiff(int size_a, int size_b) {
|
|
||||||
int size_c = size_a + size_b;
|
|
||||||
return size_a * FastLog2(size_a) + size_b * FastLog2(size_b) -
|
|
||||||
size_c * FastLog2(size_c);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Computes the bit cost reduction by combining out[idx1] and out[idx2] and if
|
|
||||||
// it is below a threshold, stores the pair (idx1, idx2) in the *pairs heap.
|
|
||||||
template<int kSize>
|
|
||||||
void CompareAndPushToHeap(const Histogram<kSize>* out,
|
|
||||||
const int* cluster_size,
|
|
||||||
int idx1, int idx2,
|
|
||||||
std::vector<HistogramPair>* pairs) {
|
|
||||||
if (idx1 == idx2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (idx2 < idx1) {
|
|
||||||
int t = idx2;
|
|
||||||
idx2 = idx1;
|
|
||||||
idx1 = t;
|
|
||||||
}
|
|
||||||
bool store_pair = false;
|
|
||||||
HistogramPair p;
|
|
||||||
p.idx1 = idx1;
|
|
||||||
p.idx2 = idx2;
|
|
||||||
p.valid = true;
|
|
||||||
p.cost_diff = 0.5 * ClusterCostDiff(cluster_size[idx1], cluster_size[idx2]);
|
|
||||||
p.cost_diff -= out[idx1].bit_cost_;
|
|
||||||
p.cost_diff -= out[idx2].bit_cost_;
|
|
||||||
|
|
||||||
if (out[idx1].total_count_ == 0) {
|
|
||||||
p.cost_combo = out[idx2].bit_cost_;
|
|
||||||
store_pair = true;
|
|
||||||
} else if (out[idx2].total_count_ == 0) {
|
|
||||||
p.cost_combo = out[idx1].bit_cost_;
|
|
||||||
store_pair = true;
|
|
||||||
} else {
|
|
||||||
double threshold = pairs->empty() ? 1e99 :
|
|
||||||
std::max(0.0, (*pairs)[0].cost_diff);
|
|
||||||
Histogram<kSize> combo = out[idx1];
|
|
||||||
combo.AddHistogram(out[idx2]);
|
|
||||||
double cost_combo = PopulationCost(combo);
|
|
||||||
if (cost_combo < threshold - p.cost_diff) {
|
|
||||||
p.cost_combo = cost_combo;
|
|
||||||
store_pair = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (store_pair) {
|
|
||||||
p.cost_diff += p.cost_combo;
|
|
||||||
pairs->push_back(p);
|
|
||||||
push_heap(pairs->begin(), pairs->end(), HistogramPairComparator());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int kSize>
|
|
||||||
void HistogramCombine(Histogram<kSize>* out,
|
|
||||||
int* cluster_size,
|
|
||||||
int* symbols,
|
|
||||||
int symbols_size,
|
|
||||||
int max_clusters) {
|
|
||||||
double cost_diff_threshold = 0.0;
|
|
||||||
int min_cluster_size = 1;
|
|
||||||
std::set<int> all_symbols;
|
|
||||||
std::vector<int> clusters;
|
|
||||||
for (int i = 0; i < symbols_size; ++i) {
|
|
||||||
if (all_symbols.find(symbols[i]) == all_symbols.end()) {
|
|
||||||
all_symbols.insert(symbols[i]);
|
|
||||||
clusters.push_back(symbols[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We maintain a heap of histogram pairs, ordered by the bit cost reduction.
|
|
||||||
std::vector<HistogramPair> pairs;
|
|
||||||
for (int idx1 = 0; idx1 < clusters.size(); ++idx1) {
|
|
||||||
for (int idx2 = idx1 + 1; idx2 < clusters.size(); ++idx2) {
|
|
||||||
CompareAndPushToHeap(out, cluster_size, clusters[idx1], clusters[idx2],
|
|
||||||
&pairs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (clusters.size() > min_cluster_size) {
|
|
||||||
if (pairs[0].cost_diff >= cost_diff_threshold) {
|
|
||||||
cost_diff_threshold = 1e99;
|
|
||||||
min_cluster_size = max_clusters;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Take the best pair from the top of heap.
|
|
||||||
int best_idx1 = pairs[0].idx1;
|
|
||||||
int best_idx2 = pairs[0].idx2;
|
|
||||||
out[best_idx1].AddHistogram(out[best_idx2]);
|
|
||||||
out[best_idx1].bit_cost_ = pairs[0].cost_combo;
|
|
||||||
cluster_size[best_idx1] += cluster_size[best_idx2];
|
|
||||||
for (int i = 0; i < symbols_size; ++i) {
|
|
||||||
if (symbols[i] == best_idx2) {
|
|
||||||
symbols[i] = best_idx1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i + 1 < clusters.size(); ++i) {
|
|
||||||
if (clusters[i] >= best_idx2) {
|
|
||||||
clusters[i] = clusters[i + 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clusters.pop_back();
|
|
||||||
// Invalidate pairs intersecting the just combined best pair.
|
|
||||||
for (int i = 0; i < pairs.size(); ++i) {
|
|
||||||
HistogramPair& p = pairs[i];
|
|
||||||
if (p.idx1 == best_idx1 || p.idx2 == best_idx1 ||
|
|
||||||
p.idx1 == best_idx2 || p.idx2 == best_idx2) {
|
|
||||||
p.valid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Pop invalid pairs from the top of the heap.
|
|
||||||
while (!pairs.empty() && !pairs[0].valid) {
|
|
||||||
pop_heap(pairs.begin(), pairs.end(), HistogramPairComparator());
|
|
||||||
pairs.pop_back();
|
|
||||||
}
|
|
||||||
// Push new pairs formed with the combined histogram to the heap.
|
|
||||||
for (int i = 0; i < clusters.size(); ++i) {
|
|
||||||
CompareAndPushToHeap(out, cluster_size, best_idx1, clusters[i], &pairs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Histogram refinement
|
|
||||||
|
|
||||||
// What is the bit cost of moving histogram from cur_symbol to candidate.
|
|
||||||
template<int kSize>
|
|
||||||
double HistogramBitCostDistance(const Histogram<kSize>& histogram,
|
|
||||||
const Histogram<kSize>& candidate) {
|
|
||||||
if (histogram.total_count_ == 0) {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
Histogram<kSize> tmp = histogram;
|
|
||||||
tmp.AddHistogram(candidate);
|
|
||||||
return PopulationCost(tmp) - candidate.bit_cost_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the best 'out' histogram for each of the 'in' histograms.
|
|
||||||
// Note: we assume that out[]->bit_cost_ is already up-to-date.
|
|
||||||
template<int kSize>
|
|
||||||
void HistogramRemap(const Histogram<kSize>* in, int in_size,
|
|
||||||
Histogram<kSize>* out, int* symbols) {
|
|
||||||
std::set<int> all_symbols;
|
|
||||||
for (int i = 0; i < in_size; ++i) {
|
|
||||||
all_symbols.insert(symbols[i]);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < in_size; ++i) {
|
|
||||||
int best_out = i == 0 ? symbols[0] : symbols[i - 1];
|
|
||||||
double best_bits = HistogramBitCostDistance(in[i], out[best_out]);
|
|
||||||
for (std::set<int>::const_iterator k = all_symbols.begin();
|
|
||||||
k != all_symbols.end(); ++k) {
|
|
||||||
const double cur_bits = HistogramBitCostDistance(in[i], out[*k]);
|
|
||||||
if (cur_bits < best_bits) {
|
|
||||||
best_bits = cur_bits;
|
|
||||||
best_out = *k;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
symbols[i] = best_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recompute each out based on raw and symbols.
|
|
||||||
for (std::set<int>::const_iterator k = all_symbols.begin();
|
|
||||||
k != all_symbols.end(); ++k) {
|
|
||||||
out[*k].Clear();
|
|
||||||
}
|
|
||||||
for (int i = 0; i < in_size; ++i) {
|
|
||||||
out[symbols[i]].AddHistogram(in[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reorder histograms in *out so that the new symbols in *symbols come in
|
|
||||||
// increasing order.
|
|
||||||
template<int kSize>
|
|
||||||
void HistogramReindex(std::vector<Histogram<kSize> >* out,
|
|
||||||
std::vector<int>* symbols) {
|
|
||||||
std::vector<Histogram<kSize> > tmp(*out);
|
|
||||||
std::map<int, int> new_index;
|
|
||||||
int next_index = 0;
|
|
||||||
for (int i = 0; i < symbols->size(); ++i) {
|
|
||||||
if (new_index.find((*symbols)[i]) == new_index.end()) {
|
|
||||||
new_index[(*symbols)[i]] = next_index;
|
|
||||||
(*out)[next_index] = tmp[(*symbols)[i]];
|
|
||||||
++next_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out->resize(next_index);
|
|
||||||
for (int i = 0; i < symbols->size(); ++i) {
|
|
||||||
(*symbols)[i] = new_index[(*symbols)[i]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clusters similar histograms in 'in' together, the selected histograms are
|
|
||||||
// placed in 'out', and for each index in 'in', *histogram_symbols will
|
|
||||||
// indicate which of the 'out' histograms is the best approximation.
|
|
||||||
template<int kSize>
|
|
||||||
void ClusterHistograms(const std::vector<Histogram<kSize> >& in,
|
|
||||||
int num_contexts, int num_blocks,
|
|
||||||
int max_histograms,
|
|
||||||
std::vector<Histogram<kSize> >* out,
|
|
||||||
std::vector<int>* histogram_symbols) {
|
|
||||||
const int in_size = num_contexts * num_blocks;
|
|
||||||
std::vector<int> cluster_size(in_size, 1);
|
|
||||||
out->resize(in_size);
|
|
||||||
histogram_symbols->resize(in_size);
|
|
||||||
for (int i = 0; i < in_size; ++i) {
|
|
||||||
(*out)[i] = in[i];
|
|
||||||
(*out)[i].bit_cost_ = PopulationCost(in[i]);
|
|
||||||
(*histogram_symbols)[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collapse similar histograms within a block type.
|
|
||||||
if (num_contexts > 1) {
|
|
||||||
for (int i = 0; i < num_blocks; ++i) {
|
|
||||||
HistogramCombine(&(*out)[0], &cluster_size[0],
|
|
||||||
&(*histogram_symbols)[i * num_contexts], num_contexts,
|
|
||||||
max_histograms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collapse similar histograms.
|
|
||||||
HistogramCombine(&(*out)[0], &cluster_size[0],
|
|
||||||
&(*histogram_symbols)[0], in_size,
|
|
||||||
max_histograms);
|
|
||||||
|
|
||||||
// Find the optimal map from original histograms to the final ones.
|
|
||||||
HistogramRemap(&in[0], in_size, &(*out)[0], &(*histogram_symbols)[0]);
|
|
||||||
|
|
||||||
// Convert the context map to a canonical form.
|
|
||||||
HistogramReindex(out, histogram_symbols);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_CLUSTER_H_
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// This class models a sequence of literals and a backward reference copy.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_COMMAND_H_
|
|
||||||
#define BROTLI_ENC_COMMAND_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
// Command holds a sequence of literals and a backward reference copy.
|
|
||||||
class Command {
|
|
||||||
public:
|
|
||||||
// distance_code_ is initialized to 17 because it refers to the distance
|
|
||||||
// code of a backward distance of 1, this way the last insert-only command
|
|
||||||
// won't use the last-distance short code, and accordingly distance_prefix_ is
|
|
||||||
// set to 16
|
|
||||||
Command() : insert_length_(0), copy_length_(0), copy_length_code_(0),
|
|
||||||
copy_distance_(0), distance_code_(17),
|
|
||||||
distance_prefix_(16), command_prefix_(0),
|
|
||||||
distance_extra_bits_(0), distance_extra_bits_value_(0) {}
|
|
||||||
|
|
||||||
uint32_t insert_length_;
|
|
||||||
uint32_t copy_length_;
|
|
||||||
uint32_t copy_length_code_;
|
|
||||||
uint32_t copy_distance_;
|
|
||||||
// Values <= 16 are short codes, values > 16 are distances shifted by 16.
|
|
||||||
uint32_t distance_code_;
|
|
||||||
uint16_t distance_prefix_;
|
|
||||||
uint16_t command_prefix_;
|
|
||||||
int distance_extra_bits_;
|
|
||||||
uint32_t distance_extra_bits_value_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_COMMAND_H_
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Functions to map previous bytes into a context id.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_CONTEXT_H_
|
|
||||||
#define BROTLI_ENC_CONTEXT_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
// Second-order context lookup table for UTF8 byte streams.
|
|
||||||
//
|
|
||||||
// If p1 and p2 are the previous two bytes, we calcualte the context as
|
|
||||||
//
|
|
||||||
// context = kUTF8ContextLookup[p1] | kUTF8ContextLookup[p2 + 256].
|
|
||||||
//
|
|
||||||
// If the previous two bytes are ASCII characters (i.e. < 128), this will be
|
|
||||||
// equivalent to
|
|
||||||
//
|
|
||||||
// context = 4 * context1(p1) + context2(p2),
|
|
||||||
//
|
|
||||||
// where context1 is based on the previous byte in the following way:
|
|
||||||
//
|
|
||||||
// 0 : non-ASCII control
|
|
||||||
// 1 : \t, \n, \r
|
|
||||||
// 2 : space
|
|
||||||
// 3 : other punctuation
|
|
||||||
// 4 : " '
|
|
||||||
// 5 : %
|
|
||||||
// 6 : ( < [ {
|
|
||||||
// 7 : ) > ] }
|
|
||||||
// 8 : , ; :
|
|
||||||
// 9 : .
|
|
||||||
// 10 : =
|
|
||||||
// 11 : number
|
|
||||||
// 12 : upper-case vowel
|
|
||||||
// 13 : upper-case consonant
|
|
||||||
// 14 : lower-case vowel
|
|
||||||
// 15 : lower-case consonant
|
|
||||||
//
|
|
||||||
// and context2 is based on the second last byte:
|
|
||||||
//
|
|
||||||
// 0 : control, space
|
|
||||||
// 1 : punctuation
|
|
||||||
// 2 : upper-case letter, number
|
|
||||||
// 3 : lower-case letter
|
|
||||||
//
|
|
||||||
// If the last byte is ASCII, and the second last byte is not (in a valid UTF8
|
|
||||||
// stream it will be a continuation byte, value between 128 and 191), the
|
|
||||||
// context is the same as if the second last byte was an ASCII control or space.
|
|
||||||
//
|
|
||||||
// If the last byte is a UTF8 lead byte (value >= 192), then the next byte will
|
|
||||||
// be a continuation byte and the context id is 2 or 3 depending on the LSB of
|
|
||||||
// the last byte and to a lesser extent on the second last byte if it is ASCII.
|
|
||||||
//
|
|
||||||
// If the last byte is a UTF8 continuation byte, the second last byte can be:
|
|
||||||
// - continuation byte: the next byte is probably ASCII or lead byte (assuming
|
|
||||||
// 4-byte UTF8 characters are rare) and the context id is 0 or 1.
|
|
||||||
// - lead byte (192 - 207): next byte is ASCII or lead byte, context is 0 or 1
|
|
||||||
// - lead byte (208 - 255): next byte is continuation byte, context is 2 or 3
|
|
||||||
//
|
|
||||||
// The possible value combinations of the previous two bytes, the range of
|
|
||||||
// context ids and the type of the next byte is summarized in the table below:
|
|
||||||
//
|
|
||||||
// |--------\-----------------------------------------------------------------|
|
|
||||||
// | \ Last byte |
|
|
||||||
// | Second \---------------------------------------------------------------|
|
|
||||||
// | last byte \ ASCII | cont. byte | lead byte |
|
|
||||||
// | \ (0-127) | (128-191) | (192-) |
|
|
||||||
// |=============|===================|=====================|==================|
|
|
||||||
// | ASCII | next: ASCII/lead | not valid | next: cont. |
|
|
||||||
// | (0-127) | context: 4 - 63 | | context: 2 - 3 |
|
|
||||||
// |-------------|-------------------|---------------------|------------------|
|
|
||||||
// | cont. byte | next: ASCII/lead | next: ASCII/lead | next: cont. |
|
|
||||||
// | (128-191) | context: 4 - 63 | context: 0 - 1 | context: 2 - 3 |
|
|
||||||
// |-------------|-------------------|---------------------|------------------|
|
|
||||||
// | lead byte | not valid | next: ASCII/lead | not valid |
|
|
||||||
// | (192-207) | | context: 0 - 1 | |
|
|
||||||
// |-------------|-------------------|---------------------|------------------|
|
|
||||||
// | lead byte | not valid | next: cont. | not valid |
|
|
||||||
// | (208-) | | context: 2 - 3 | |
|
|
||||||
// |-------------|-------------------|---------------------|------------------|
|
|
||||||
static const uint8_t kUTF8ContextLookup[512] = {
|
|
||||||
// Last byte.
|
|
||||||
//
|
|
||||||
// ASCII range.
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 4, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
8, 12, 16, 12, 12, 20, 12, 16, 24, 28, 12, 12, 32, 12, 36, 12,
|
|
||||||
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 32, 32, 24, 40, 28, 12,
|
|
||||||
12, 48, 52, 52, 52, 48, 52, 52, 52, 48, 52, 52, 52, 52, 52, 48,
|
|
||||||
52, 52, 52, 52, 52, 48, 52, 52, 52, 52, 52, 24, 12, 28, 12, 12,
|
|
||||||
12, 56, 60, 60, 60, 56, 60, 60, 60, 56, 60, 60, 60, 60, 60, 56,
|
|
||||||
60, 60, 60, 60, 60, 56, 60, 60, 60, 60, 60, 24, 12, 28, 12, 0,
|
|
||||||
// UTF8 continuation byte range.
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
// UTF8 lead byte range.
|
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
|
||||||
// Second last byte.
|
|
||||||
//
|
|
||||||
// ASCII range.
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1,
|
|
||||||
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1,
|
|
||||||
1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 0,
|
|
||||||
// UTF8 continuation byte range.
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
// UTF8 lead byte range.
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Context lookup table for small signed integers.
|
|
||||||
static const int kSigned3BitContextLookup[] = {
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ContextType {
|
|
||||||
CONTEXT_LSB6 = 0,
|
|
||||||
CONTEXT_MSB6 = 1,
|
|
||||||
CONTEXT_UTF8 = 2,
|
|
||||||
CONTEXT_SIGNED = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline uint8_t Context(uint8_t p1, uint8_t p2, int mode) {
|
|
||||||
switch (mode) {
|
|
||||||
case CONTEXT_LSB6:
|
|
||||||
return p1 & 0x3f;
|
|
||||||
case CONTEXT_MSB6:
|
|
||||||
return p1 >> 2;
|
|
||||||
case CONTEXT_UTF8:
|
|
||||||
return kUTF8ContextLookup[p1] | kUTF8ContextLookup[p2 + 256];
|
|
||||||
case CONTEXT_SIGNED:
|
|
||||||
return (kSigned3BitContextLookup[p1] << 3) + kSigned3BitContextLookup[p2];
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_CONTEXT_H_
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,93 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// API for Brotli compression
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_ENCODE_H_
|
|
||||||
#define BROTLI_ENC_ENCODE_H_
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include "./hash.h"
|
|
||||||
#include "./ringbuffer.h"
|
|
||||||
#include "./static_dict.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
struct BrotliParams {
|
|
||||||
enum Mode {
|
|
||||||
MODE_TEXT = 0,
|
|
||||||
MODE_FONT = 1,
|
|
||||||
};
|
|
||||||
Mode mode;
|
|
||||||
|
|
||||||
BrotliParams() : mode(MODE_TEXT) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class BrotliCompressor {
|
|
||||||
public:
|
|
||||||
explicit BrotliCompressor(BrotliParams params);
|
|
||||||
~BrotliCompressor();
|
|
||||||
|
|
||||||
// Writes the stream header into the internal output buffer.
|
|
||||||
void WriteStreamHeader();
|
|
||||||
|
|
||||||
// Encodes the data in input_buffer as a meta-block and writes it to
|
|
||||||
// encoded_buffer and sets *encoded_size to the number of bytes that was
|
|
||||||
// written.
|
|
||||||
void WriteMetaBlock(const size_t input_size,
|
|
||||||
const uint8_t* input_buffer,
|
|
||||||
const bool is_last,
|
|
||||||
size_t* encoded_size,
|
|
||||||
uint8_t* encoded_buffer);
|
|
||||||
|
|
||||||
// Writes a zero-length meta-block with end-of-input bit set to the
|
|
||||||
// internal output buffer and copies the output buffer to encoded_buffer and
|
|
||||||
// sets *encoded_size to the number of bytes written.
|
|
||||||
void FinishStream(size_t* encoded_size, uint8_t* encoded_buffer);
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Initializes the hasher with the hashes of dictionary words.
|
|
||||||
void StoreDictionaryWordHashes();
|
|
||||||
|
|
||||||
BrotliParams params_;
|
|
||||||
int window_bits_;
|
|
||||||
std::unique_ptr<Hashers> hashers_;
|
|
||||||
Hashers::Type hash_type_;
|
|
||||||
int dist_ringbuffer_[4];
|
|
||||||
size_t dist_ringbuffer_idx_;
|
|
||||||
size_t input_pos_;
|
|
||||||
RingBuffer ringbuffer_;
|
|
||||||
std::vector<float> literal_cost_;
|
|
||||||
int storage_ix_;
|
|
||||||
uint8_t* storage_;
|
|
||||||
static StaticDictionary *static_dictionary_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Compresses the data in input_buffer into encoded_buffer, and sets
|
|
||||||
// *encoded_size to the compressed length.
|
|
||||||
// Returns 0 if there was an error and 1 otherwise.
|
|
||||||
int BrotliCompressBuffer(BrotliParams params,
|
|
||||||
size_t input_size,
|
|
||||||
const uint8_t* input_buffer,
|
|
||||||
size_t* encoded_size,
|
|
||||||
uint8_t* encoded_buffer);
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_ENCODE_H_
|
|
||||||
@@ -1,504 +0,0 @@
|
|||||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Entropy encoding (Huffman) utilities.
|
|
||||||
|
|
||||||
#include "./entropy_encode.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <limits>
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include "./histogram.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
struct HuffmanTree {
|
|
||||||
HuffmanTree();
|
|
||||||
HuffmanTree(int count, int16_t left, int16_t right)
|
|
||||||
: total_count_(count),
|
|
||||||
index_left_(left),
|
|
||||||
index_right_or_value_(right) {
|
|
||||||
}
|
|
||||||
int total_count_;
|
|
||||||
int16_t index_left_;
|
|
||||||
int16_t index_right_or_value_;
|
|
||||||
};
|
|
||||||
|
|
||||||
HuffmanTree::HuffmanTree() {}
|
|
||||||
|
|
||||||
// Sort the root nodes, least popular first.
|
|
||||||
bool SortHuffmanTree(const HuffmanTree &v0, const HuffmanTree &v1) {
|
|
||||||
if (v0.total_count_ == v1.total_count_) {
|
|
||||||
return v0.index_right_or_value_ > v1.index_right_or_value_;
|
|
||||||
}
|
|
||||||
return v0.total_count_ < v1.total_count_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetDepth(const HuffmanTree &p,
|
|
||||||
HuffmanTree *pool,
|
|
||||||
uint8_t *depth,
|
|
||||||
int level) {
|
|
||||||
if (p.index_left_ >= 0) {
|
|
||||||
++level;
|
|
||||||
SetDepth(pool[p.index_left_], pool, depth, level);
|
|
||||||
SetDepth(pool[p.index_right_or_value_], pool, depth, level);
|
|
||||||
} else {
|
|
||||||
depth[p.index_right_or_value_] = level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// This function will create a Huffman tree.
|
|
||||||
//
|
|
||||||
// The catch here is that the tree cannot be arbitrarily deep.
|
|
||||||
// Brotli specifies a maximum depth of 15 bits for "code trees"
|
|
||||||
// and 7 bits for "code length code trees."
|
|
||||||
//
|
|
||||||
// count_limit is the value that is to be faked as the minimum value
|
|
||||||
// and this minimum value is raised until the tree matches the
|
|
||||||
// maximum length requirement.
|
|
||||||
//
|
|
||||||
// This algorithm is not of excellent performance for very long data blocks,
|
|
||||||
// especially when population counts are longer than 2**tree_limit, but
|
|
||||||
// we are not planning to use this with extremely long blocks.
|
|
||||||
//
|
|
||||||
// See http://en.wikipedia.org/wiki/Huffman_coding
|
|
||||||
void CreateHuffmanTree(const int *data,
|
|
||||||
const int length,
|
|
||||||
const int tree_limit,
|
|
||||||
uint8_t *depth) {
|
|
||||||
// For block sizes below 64 kB, we never need to do a second iteration
|
|
||||||
// of this loop. Probably all of our block sizes will be smaller than
|
|
||||||
// that, so this loop is mostly of academic interest. If we actually
|
|
||||||
// would need this, we would be better off with the Katajainen algorithm.
|
|
||||||
for (int count_limit = 1; ; count_limit *= 2) {
|
|
||||||
std::vector<HuffmanTree> tree;
|
|
||||||
tree.reserve(2 * length + 1);
|
|
||||||
|
|
||||||
for (int i = 0; i < length; ++i) {
|
|
||||||
if (data[i]) {
|
|
||||||
const int count = std::max(data[i], count_limit);
|
|
||||||
tree.push_back(HuffmanTree(count, -1, i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const int n = tree.size();
|
|
||||||
if (n == 1) {
|
|
||||||
depth[tree[0].index_right_or_value_] = 1; // Only one element.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(tree.begin(), tree.end(), SortHuffmanTree);
|
|
||||||
|
|
||||||
// The nodes are:
|
|
||||||
// [0, n): the sorted leaf nodes that we start with.
|
|
||||||
// [n]: we add a sentinel here.
|
|
||||||
// [n + 1, 2n): new parent nodes are added here, starting from
|
|
||||||
// (n+1). These are naturally in ascending order.
|
|
||||||
// [2n]: we add a sentinel at the end as well.
|
|
||||||
// There will be (2n+1) elements at the end.
|
|
||||||
const HuffmanTree sentinel(std::numeric_limits<int>::max(), -1, -1);
|
|
||||||
tree.push_back(sentinel);
|
|
||||||
tree.push_back(sentinel);
|
|
||||||
|
|
||||||
int i = 0; // Points to the next leaf node.
|
|
||||||
int j = n + 1; // Points to the next non-leaf node.
|
|
||||||
for (int k = n - 1; k > 0; --k) {
|
|
||||||
int left, right;
|
|
||||||
if (tree[i].total_count_ <= tree[j].total_count_) {
|
|
||||||
left = i;
|
|
||||||
++i;
|
|
||||||
} else {
|
|
||||||
left = j;
|
|
||||||
++j;
|
|
||||||
}
|
|
||||||
if (tree[i].total_count_ <= tree[j].total_count_) {
|
|
||||||
right = i;
|
|
||||||
++i;
|
|
||||||
} else {
|
|
||||||
right = j;
|
|
||||||
++j;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The sentinel node becomes the parent node.
|
|
||||||
int j_end = tree.size() - 1;
|
|
||||||
tree[j_end].total_count_ =
|
|
||||||
tree[left].total_count_ + tree[right].total_count_;
|
|
||||||
tree[j_end].index_left_ = left;
|
|
||||||
tree[j_end].index_right_or_value_ = right;
|
|
||||||
|
|
||||||
// Add back the last sentinel node.
|
|
||||||
tree.push_back(sentinel);
|
|
||||||
}
|
|
||||||
SetDepth(tree[2 * n - 1], &tree[0], depth, 0);
|
|
||||||
|
|
||||||
// We need to pack the Huffman tree in tree_limit bits.
|
|
||||||
// If this was not successful, add fake entities to the lowest values
|
|
||||||
// and retry.
|
|
||||||
if (*std::max_element(&depth[0], &depth[length]) <= tree_limit) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reverse(uint8_t* v, int start, int end) {
|
|
||||||
--end;
|
|
||||||
while (start < end) {
|
|
||||||
int tmp = v[start];
|
|
||||||
v[start] = v[end];
|
|
||||||
v[end] = tmp;
|
|
||||||
++start;
|
|
||||||
--end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteHuffmanTreeRepetitions(
|
|
||||||
const int previous_value,
|
|
||||||
const int value,
|
|
||||||
int repetitions,
|
|
||||||
uint8_t* tree,
|
|
||||||
uint8_t* extra_bits,
|
|
||||||
int* tree_size) {
|
|
||||||
if (previous_value != value) {
|
|
||||||
tree[*tree_size] = value;
|
|
||||||
extra_bits[*tree_size] = 0;
|
|
||||||
++(*tree_size);
|
|
||||||
--repetitions;
|
|
||||||
}
|
|
||||||
if (repetitions == 7) {
|
|
||||||
tree[*tree_size] = value;
|
|
||||||
extra_bits[*tree_size] = 0;
|
|
||||||
++(*tree_size);
|
|
||||||
--repetitions;
|
|
||||||
}
|
|
||||||
if (repetitions < 3) {
|
|
||||||
for (int i = 0; i < repetitions; ++i) {
|
|
||||||
tree[*tree_size] = value;
|
|
||||||
extra_bits[*tree_size] = 0;
|
|
||||||
++(*tree_size);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
repetitions -= 3;
|
|
||||||
int start = *tree_size;
|
|
||||||
while (repetitions >= 0) {
|
|
||||||
tree[*tree_size] = 16;
|
|
||||||
extra_bits[*tree_size] = repetitions & 0x3;
|
|
||||||
++(*tree_size);
|
|
||||||
repetitions >>= 2;
|
|
||||||
--repetitions;
|
|
||||||
}
|
|
||||||
Reverse(tree, start, *tree_size);
|
|
||||||
Reverse(extra_bits, start, *tree_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteHuffmanTreeRepetitionsZeros(
|
|
||||||
int repetitions,
|
|
||||||
uint8_t* tree,
|
|
||||||
uint8_t* extra_bits,
|
|
||||||
int* tree_size) {
|
|
||||||
if (repetitions == 11) {
|
|
||||||
tree[*tree_size] = 0;
|
|
||||||
extra_bits[*tree_size] = 0;
|
|
||||||
++(*tree_size);
|
|
||||||
--repetitions;
|
|
||||||
}
|
|
||||||
if (repetitions < 3) {
|
|
||||||
for (int i = 0; i < repetitions; ++i) {
|
|
||||||
tree[*tree_size] = 0;
|
|
||||||
extra_bits[*tree_size] = 0;
|
|
||||||
++(*tree_size);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
repetitions -= 3;
|
|
||||||
int start = *tree_size;
|
|
||||||
while (repetitions >= 0) {
|
|
||||||
tree[*tree_size] = 17;
|
|
||||||
extra_bits[*tree_size] = repetitions & 0x7;
|
|
||||||
++(*tree_size);
|
|
||||||
repetitions >>= 3;
|
|
||||||
--repetitions;
|
|
||||||
}
|
|
||||||
Reverse(tree, start, *tree_size);
|
|
||||||
Reverse(extra_bits, start, *tree_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int OptimizeHuffmanCountsForRle(int length, int* counts) {
|
|
||||||
int stride;
|
|
||||||
int limit;
|
|
||||||
int sum;
|
|
||||||
uint8_t* good_for_rle;
|
|
||||||
// Let's make the Huffman code more compatible with rle encoding.
|
|
||||||
int i;
|
|
||||||
for (; length >= 0; --length) {
|
|
||||||
if (length == 0) {
|
|
||||||
return 1; // All zeros.
|
|
||||||
}
|
|
||||||
if (counts[length - 1] != 0) {
|
|
||||||
// Now counts[0..length - 1] does not have trailing zeros.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
int nonzeros = 0;
|
|
||||||
int smallest_nonzero = 1 << 30;
|
|
||||||
for (i = 0; i < length; ++i) {
|
|
||||||
if (counts[i] != 0) {
|
|
||||||
++nonzeros;
|
|
||||||
if (smallest_nonzero > counts[i]) {
|
|
||||||
smallest_nonzero = counts[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nonzeros < 5) {
|
|
||||||
// Small histogram will model it well.
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int zeros = length - nonzeros;
|
|
||||||
if (smallest_nonzero < 4) {
|
|
||||||
if (zeros < 6) {
|
|
||||||
for (i = 1; i < length - 1; ++i) {
|
|
||||||
if (counts[i - 1] != 0 && counts[i] == 0 && counts[i + 1] != 0) {
|
|
||||||
counts[i] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nonzeros < 28) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 2) Let's mark all population counts that already can be encoded
|
|
||||||
// with an rle code.
|
|
||||||
good_for_rle = (uint8_t*)calloc(length, 1);
|
|
||||||
if (good_for_rle == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Let's not spoil any of the existing good rle codes.
|
|
||||||
// Mark any seq of 0's that is longer as 5 as a good_for_rle.
|
|
||||||
// Mark any seq of non-0's that is longer as 7 as a good_for_rle.
|
|
||||||
int symbol = counts[0];
|
|
||||||
int stride = 0;
|
|
||||||
for (i = 0; i < length + 1; ++i) {
|
|
||||||
if (i == length || counts[i] != symbol) {
|
|
||||||
if ((symbol == 0 && stride >= 5) ||
|
|
||||||
(symbol != 0 && stride >= 7)) {
|
|
||||||
int k;
|
|
||||||
for (k = 0; k < stride; ++k) {
|
|
||||||
good_for_rle[i - k - 1] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stride = 1;
|
|
||||||
if (i != length) {
|
|
||||||
symbol = counts[i];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
++stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 3) Let's replace those population counts that lead to more rle codes.
|
|
||||||
// Math here is in 24.8 fixed point representation.
|
|
||||||
const int streak_limit = 1240;
|
|
||||||
stride = 0;
|
|
||||||
limit = 256 * (counts[0] + counts[1] + counts[2]) / 3 + 420;
|
|
||||||
sum = 0;
|
|
||||||
for (i = 0; i < length + 1; ++i) {
|
|
||||||
if (i == length || good_for_rle[i] ||
|
|
||||||
(i != 0 && good_for_rle[i - 1]) ||
|
|
||||||
abs(256 * counts[i] - limit) >= streak_limit) {
|
|
||||||
if (stride >= 4 || (stride >= 3 && sum == 0)) {
|
|
||||||
int k;
|
|
||||||
// The stride must end, collapse what we have, if we have enough (4).
|
|
||||||
int count = (sum + stride / 2) / stride;
|
|
||||||
if (count < 1) {
|
|
||||||
count = 1;
|
|
||||||
}
|
|
||||||
if (sum == 0) {
|
|
||||||
// Don't make an all zeros stride to be upgraded to ones.
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
for (k = 0; k < stride; ++k) {
|
|
||||||
// We don't want to change value at counts[i],
|
|
||||||
// that is already belonging to the next stride. Thus - 1.
|
|
||||||
counts[i - k - 1] = count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stride = 0;
|
|
||||||
sum = 0;
|
|
||||||
if (i < length - 2) {
|
|
||||||
// All interesting strides have a count of at least 4,
|
|
||||||
// at least when non-zeros.
|
|
||||||
limit = 256 * (counts[i] + counts[i + 1] + counts[i + 2]) / 3 + 420;
|
|
||||||
} else if (i < length) {
|
|
||||||
limit = 256 * counts[i];
|
|
||||||
} else {
|
|
||||||
limit = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++stride;
|
|
||||||
if (i != length) {
|
|
||||||
sum += counts[i];
|
|
||||||
if (stride >= 4) {
|
|
||||||
limit = (256 * sum + stride / 2) / stride;
|
|
||||||
}
|
|
||||||
if (stride == 4) {
|
|
||||||
limit += 120;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(good_for_rle);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void DecideOverRleUse(const uint8_t* depth, const int length,
|
|
||||||
bool *use_rle_for_non_zero,
|
|
||||||
bool *use_rle_for_zero) {
|
|
||||||
int total_reps_zero = 0;
|
|
||||||
int total_reps_non_zero = 0;
|
|
||||||
int count_reps_zero = 0;
|
|
||||||
int count_reps_non_zero = 0;
|
|
||||||
int new_length = length;
|
|
||||||
for (int i = 0; i < length; ++i) {
|
|
||||||
if (depth[length - i - 1] == 0) {
|
|
||||||
--new_length;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (uint32_t i = 0; i < new_length;) {
|
|
||||||
const int value = depth[i];
|
|
||||||
int reps = 1;
|
|
||||||
// Find rle coding for longer codes.
|
|
||||||
// Shorter codes seem not to benefit from rle.
|
|
||||||
for (uint32_t k = i + 1; k < new_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;
|
|
||||||
}
|
|
||||||
total_reps_non_zero -= count_reps_non_zero * 2;
|
|
||||||
total_reps_zero -= count_reps_zero * 2;
|
|
||||||
*use_rle_for_non_zero = total_reps_non_zero > 2;
|
|
||||||
*use_rle_for_zero = total_reps_zero > 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WriteHuffmanTree(const uint8_t* depth, const int length,
|
|
||||||
uint8_t* tree,
|
|
||||||
uint8_t* extra_bits_data,
|
|
||||||
int* huffman_tree_size) {
|
|
||||||
int previous_value = 8;
|
|
||||||
|
|
||||||
// First gather statistics on if it is a good idea to do rle.
|
|
||||||
bool use_rle_for_non_zero;
|
|
||||||
bool use_rle_for_zero;
|
|
||||||
DecideOverRleUse(depth, length, &use_rle_for_non_zero, &use_rle_for_zero);
|
|
||||||
|
|
||||||
// Actual rle coding.
|
|
||||||
for (uint32_t i = 0; i < length;) {
|
|
||||||
const int value = depth[i];
|
|
||||||
int reps = 1;
|
|
||||||
if (length > 50) {
|
|
||||||
// Find rle coding for longer codes.
|
|
||||||
// Shorter codes seem not to benefit from rle.
|
|
||||||
if ((value != 0 && use_rle_for_non_zero) ||
|
|
||||||
(value == 0 && use_rle_for_zero)) {
|
|
||||||
for (uint32_t k = i + 1; k < length && depth[k] == value; ++k) {
|
|
||||||
++reps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (value == 0) {
|
|
||||||
WriteHuffmanTreeRepetitionsZeros(reps, tree, extra_bits_data,
|
|
||||||
huffman_tree_size);
|
|
||||||
} else {
|
|
||||||
WriteHuffmanTreeRepetitions(previous_value, value, reps, tree,
|
|
||||||
extra_bits_data, huffman_tree_size);
|
|
||||||
previous_value = value;
|
|
||||||
}
|
|
||||||
i += reps;
|
|
||||||
}
|
|
||||||
// Throw away trailing zeros.
|
|
||||||
for (; *huffman_tree_size > 0; --(*huffman_tree_size)) {
|
|
||||||
if (tree[*huffman_tree_size - 1] > 0 && tree[*huffman_tree_size - 1] < 17) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
uint16_t ReverseBits(int num_bits, uint16_t bits) {
|
|
||||||
static const size_t kLut[16] = { // Pre-reversed 4-bit values.
|
|
||||||
0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
|
|
||||||
0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf
|
|
||||||
};
|
|
||||||
size_t retval = kLut[bits & 0xf];
|
|
||||||
for (int i = 4; i < num_bits; i += 4) {
|
|
||||||
retval <<= 4;
|
|
||||||
bits >>= 4;
|
|
||||||
retval |= kLut[bits & 0xf];
|
|
||||||
}
|
|
||||||
retval >>= (-num_bits & 0x3);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void ConvertBitDepthsToSymbols(const uint8_t *depth, int len, uint16_t *bits) {
|
|
||||||
// In Brotli, all bit depths are [1..15]
|
|
||||||
// 0 bit depth means that the symbol does not exist.
|
|
||||||
const int kMaxBits = 16; // 0..15 are values for bits
|
|
||||||
uint16_t bl_count[kMaxBits] = { 0 };
|
|
||||||
{
|
|
||||||
for (int i = 0; i < len; ++i) {
|
|
||||||
++bl_count[depth[i]];
|
|
||||||
}
|
|
||||||
bl_count[0] = 0;
|
|
||||||
}
|
|
||||||
uint16_t next_code[kMaxBits];
|
|
||||||
next_code[0] = 0;
|
|
||||||
{
|
|
||||||
int code = 0;
|
|
||||||
for (int bits = 1; bits < kMaxBits; ++bits) {
|
|
||||||
code = (code + bl_count[bits - 1]) << 1;
|
|
||||||
next_code[bits] = code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i < len; ++i) {
|
|
||||||
if (depth[i]) {
|
|
||||||
bits[i] = ReverseBits(depth[i], next_code[depth[i]]++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Entropy encoding (Huffman) utilities.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_ENTROPY_ENCODE_H_
|
|
||||||
#define BROTLI_ENC_ENTROPY_ENCODE_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "./histogram.h"
|
|
||||||
#include "./prefix.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
// This function will create a Huffman tree.
|
|
||||||
//
|
|
||||||
// The (data,length) contains the population counts.
|
|
||||||
// The tree_limit is the maximum bit depth of the Huffman codes.
|
|
||||||
//
|
|
||||||
// The depth contains the tree, i.e., how many bits are used for
|
|
||||||
// the symbol.
|
|
||||||
//
|
|
||||||
// See http://en.wikipedia.org/wiki/Huffman_coding
|
|
||||||
void CreateHuffmanTree(const int *data,
|
|
||||||
const int length,
|
|
||||||
const int tree_limit,
|
|
||||||
uint8_t *depth);
|
|
||||||
|
|
||||||
// Change the population counts in a way that the consequent
|
|
||||||
// Hufmann tree compression, especially its rle-part will be more
|
|
||||||
// likely to compress this data more efficiently.
|
|
||||||
//
|
|
||||||
// length contains the size of the histogram.
|
|
||||||
// counts contains the population counts.
|
|
||||||
int OptimizeHuffmanCountsForRle(int length, int* counts);
|
|
||||||
|
|
||||||
|
|
||||||
// Write a huffman tree from bit depths into the bitstream representation
|
|
||||||
// of a Huffman tree. The generated Huffman tree is to be compressed once
|
|
||||||
// more using a Huffman tree
|
|
||||||
void WriteHuffmanTree(const uint8_t* depth, const int length,
|
|
||||||
uint8_t* tree,
|
|
||||||
uint8_t* extra_bits_data,
|
|
||||||
int* huffman_tree_size);
|
|
||||||
|
|
||||||
// Get the actual bit values for a tree of bit depths.
|
|
||||||
void ConvertBitDepthsToSymbols(const uint8_t *depth, int len, uint16_t *bits);
|
|
||||||
|
|
||||||
template<int kSize>
|
|
||||||
struct EntropyCode {
|
|
||||||
// How many bits for symbol.
|
|
||||||
uint8_t depth_[kSize];
|
|
||||||
// Actual bits used to represent the symbol.
|
|
||||||
uint16_t bits_[kSize];
|
|
||||||
// How many non-zero depth.
|
|
||||||
int count_;
|
|
||||||
// First four symbols with non-zero depth.
|
|
||||||
int symbols_[4];
|
|
||||||
};
|
|
||||||
|
|
||||||
template<int kSize>
|
|
||||||
void BuildEntropyCode(const Histogram<kSize>& histogram,
|
|
||||||
const int tree_limit,
|
|
||||||
const int alphabet_size,
|
|
||||||
EntropyCode<kSize>* code) {
|
|
||||||
memset(code->depth_, 0, sizeof(code->depth_));
|
|
||||||
memset(code->bits_, 0, sizeof(code->bits_));
|
|
||||||
memset(code->symbols_, 0, sizeof(code->symbols_));
|
|
||||||
code->count_ = 0;
|
|
||||||
if (histogram.total_count_ == 0) return;
|
|
||||||
for (int i = 0; i < kSize; ++i) {
|
|
||||||
if (histogram.data_[i] > 0) {
|
|
||||||
if (code->count_ < 4) code->symbols_[code->count_] = i;
|
|
||||||
++code->count_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (alphabet_size >= 50 && code->count_ >= 16) {
|
|
||||||
int counts[kSize];
|
|
||||||
memcpy(counts, &histogram.data_[0], sizeof(counts[0]) * kSize);
|
|
||||||
OptimizeHuffmanCountsForRle(alphabet_size, counts);
|
|
||||||
CreateHuffmanTree(counts, alphabet_size, tree_limit, &code->depth_[0]);
|
|
||||||
} else {
|
|
||||||
CreateHuffmanTree(&histogram.data_[0], alphabet_size, tree_limit,
|
|
||||||
&code->depth_[0]);
|
|
||||||
}
|
|
||||||
ConvertBitDepthsToSymbols(&code->depth_[0], alphabet_size, &code->bits_[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const int kCodeLengthCodes = 18;
|
|
||||||
|
|
||||||
// Literal entropy code.
|
|
||||||
typedef EntropyCode<256> EntropyCodeLiteral;
|
|
||||||
// Prefix entropy codes.
|
|
||||||
typedef EntropyCode<kNumCommandPrefixes> EntropyCodeCommand;
|
|
||||||
typedef EntropyCode<kNumDistancePrefixes> EntropyCodeDistance;
|
|
||||||
typedef EntropyCode<kNumBlockLenPrefixes> EntropyCodeBlockLength;
|
|
||||||
// Context map entropy code, 256 Huffman tree indexes + 16 run length codes.
|
|
||||||
typedef EntropyCode<272> EntropyCodeContextMap;
|
|
||||||
// Block type entropy code, 256 block types + 2 special symbols.
|
|
||||||
typedef EntropyCode<258> EntropyCodeBlockType;
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_ENTROPY_ENCODE_H_
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Utilities for fast computation of logarithms.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_FAST_LOG_H_
|
|
||||||
#define BROTLI_ENC_FAST_LOG_H_
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
// Return floor(log2(n)) for positive integer n. Returns -1 iff n == 0.
|
|
||||||
inline int Log2Floor(uint32_t n) {
|
|
||||||
#if defined(__clang__) || \
|
|
||||||
(defined(__GNUC__) && \
|
|
||||||
((__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __GNUC__ >= 4))
|
|
||||||
return n == 0 ? -1 : 31 ^ __builtin_clz(n);
|
|
||||||
#else
|
|
||||||
if (n == 0)
|
|
||||||
return -1;
|
|
||||||
int log = 0;
|
|
||||||
uint32_t value = n;
|
|
||||||
for (int i = 4; i >= 0; --i) {
|
|
||||||
int shift = (1 << i);
|
|
||||||
uint32_t x = value >> shift;
|
|
||||||
if (x != 0) {
|
|
||||||
value = x;
|
|
||||||
log += shift;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(value == 1);
|
|
||||||
return log;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return ceiling(log2(n)) for positive integer n. Returns -1 iff n == 0.
|
|
||||||
inline int Log2Ceiling(uint32_t n) {
|
|
||||||
int floor = Log2Floor(n);
|
|
||||||
if (n == (n &~ (n - 1))) // zero or a power of two
|
|
||||||
return floor;
|
|
||||||
else
|
|
||||||
return floor + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A lookup table for small values of log2(int) to be used in entropy
|
|
||||||
// computation.
|
|
||||||
//
|
|
||||||
// ", ".join(["%.16ff" % x for x in [0.0]+[log2(x) for x in range(1, 256)]])
|
|
||||||
static const float kLog2Table[] = {
|
|
||||||
0.0000000000000000f, 0.0000000000000000f, 1.0000000000000000f,
|
|
||||||
1.5849625007211563f, 2.0000000000000000f, 2.3219280948873622f,
|
|
||||||
2.5849625007211561f, 2.8073549220576042f, 3.0000000000000000f,
|
|
||||||
3.1699250014423126f, 3.3219280948873626f, 3.4594316186372978f,
|
|
||||||
3.5849625007211565f, 3.7004397181410922f, 3.8073549220576037f,
|
|
||||||
3.9068905956085187f, 4.0000000000000000f, 4.0874628412503400f,
|
|
||||||
4.1699250014423122f, 4.2479275134435852f, 4.3219280948873626f,
|
|
||||||
4.3923174227787607f, 4.4594316186372973f, 4.5235619560570131f,
|
|
||||||
4.5849625007211570f, 4.6438561897747244f, 4.7004397181410926f,
|
|
||||||
4.7548875021634691f, 4.8073549220576037f, 4.8579809951275728f,
|
|
||||||
4.9068905956085187f, 4.9541963103868758f, 5.0000000000000000f,
|
|
||||||
5.0443941193584534f, 5.0874628412503400f, 5.1292830169449664f,
|
|
||||||
5.1699250014423122f, 5.2094533656289501f, 5.2479275134435852f,
|
|
||||||
5.2854022188622487f, 5.3219280948873626f, 5.3575520046180838f,
|
|
||||||
5.3923174227787607f, 5.4262647547020979f, 5.4594316186372973f,
|
|
||||||
5.4918530963296748f, 5.5235619560570131f, 5.5545888516776376f,
|
|
||||||
5.5849625007211570f, 5.6147098441152083f, 5.6438561897747244f,
|
|
||||||
5.6724253419714961f, 5.7004397181410926f, 5.7279204545631996f,
|
|
||||||
5.7548875021634691f, 5.7813597135246599f, 5.8073549220576046f,
|
|
||||||
5.8328900141647422f, 5.8579809951275719f, 5.8826430493618416f,
|
|
||||||
5.9068905956085187f, 5.9307373375628867f, 5.9541963103868758f,
|
|
||||||
5.9772799234999168f, 6.0000000000000000f, 6.0223678130284544f,
|
|
||||||
6.0443941193584534f, 6.0660891904577721f, 6.0874628412503400f,
|
|
||||||
6.1085244567781700f, 6.1292830169449672f, 6.1497471195046822f,
|
|
||||||
6.1699250014423122f, 6.1898245588800176f, 6.2094533656289510f,
|
|
||||||
6.2288186904958804f, 6.2479275134435861f, 6.2667865406949019f,
|
|
||||||
6.2854022188622487f, 6.3037807481771031f, 6.3219280948873617f,
|
|
||||||
6.3398500028846252f, 6.3575520046180847f, 6.3750394313469254f,
|
|
||||||
6.3923174227787598f, 6.4093909361377026f, 6.4262647547020979f,
|
|
||||||
6.4429434958487288f, 6.4594316186372982f, 6.4757334309663976f,
|
|
||||||
6.4918530963296748f, 6.5077946401986964f, 6.5235619560570131f,
|
|
||||||
6.5391588111080319f, 6.5545888516776376f, 6.5698556083309478f,
|
|
||||||
6.5849625007211561f, 6.5999128421871278f, 6.6147098441152092f,
|
|
||||||
6.6293566200796095f, 6.6438561897747253f, 6.6582114827517955f,
|
|
||||||
6.6724253419714952f, 6.6865005271832185f, 6.7004397181410917f,
|
|
||||||
6.7142455176661224f, 6.7279204545631988f, 6.7414669864011465f,
|
|
||||||
6.7548875021634691f, 6.7681843247769260f, 6.7813597135246599f,
|
|
||||||
6.7944158663501062f, 6.8073549220576037f, 6.8201789624151887f,
|
|
||||||
6.8328900141647422f, 6.8454900509443757f, 6.8579809951275719f,
|
|
||||||
6.8703647195834048f, 6.8826430493618416f, 6.8948177633079437f,
|
|
||||||
6.9068905956085187f, 6.9188632372745955f, 6.9307373375628867f,
|
|
||||||
6.9425145053392399f, 6.9541963103868758f, 6.9657842846620879f,
|
|
||||||
6.9772799234999168f, 6.9886846867721664f, 7.0000000000000000f,
|
|
||||||
7.0112272554232540f, 7.0223678130284544f, 7.0334230015374501f,
|
|
||||||
7.0443941193584534f, 7.0552824355011898f, 7.0660891904577721f,
|
|
||||||
7.0768155970508317f, 7.0874628412503400f, 7.0980320829605272f,
|
|
||||||
7.1085244567781700f, 7.1189410727235076f, 7.1292830169449664f,
|
|
||||||
7.1395513523987937f, 7.1497471195046822f, 7.1598713367783891f,
|
|
||||||
7.1699250014423130f, 7.1799090900149345f, 7.1898245588800176f,
|
|
||||||
7.1996723448363644f, 7.2094533656289492f, 7.2191685204621621f,
|
|
||||||
7.2288186904958804f, 7.2384047393250794f, 7.2479275134435861f,
|
|
||||||
7.2573878426926521f, 7.2667865406949019f, 7.2761244052742384f,
|
|
||||||
7.2854022188622487f, 7.2946207488916270f, 7.3037807481771031f,
|
|
||||||
7.3128829552843557f, 7.3219280948873617f, 7.3309168781146177f,
|
|
||||||
7.3398500028846243f, 7.3487281542310781f, 7.3575520046180847f,
|
|
||||||
7.3663222142458151f, 7.3750394313469254f, 7.3837042924740528f,
|
|
||||||
7.3923174227787607f, 7.4008794362821844f, 7.4093909361377026f,
|
|
||||||
7.4178525148858991f, 7.4262647547020979f, 7.4346282276367255f,
|
|
||||||
7.4429434958487288f, 7.4512111118323299f, 7.4594316186372973f,
|
|
||||||
7.4676055500829976f, 7.4757334309663976f, 7.4838157772642564f,
|
|
||||||
7.4918530963296748f, 7.4998458870832057f, 7.5077946401986964f,
|
|
||||||
7.5156998382840436f, 7.5235619560570131f, 7.5313814605163119f,
|
|
||||||
7.5391588111080319f, 7.5468944598876373f, 7.5545888516776376f,
|
|
||||||
7.5622424242210728f, 7.5698556083309478f, 7.5774288280357487f,
|
|
||||||
7.5849625007211561f, 7.5924570372680806f, 7.5999128421871278f,
|
|
||||||
7.6073303137496113f, 7.6147098441152075f, 7.6220518194563764f,
|
|
||||||
7.6293566200796095f, 7.6366246205436488f, 7.6438561897747244f,
|
|
||||||
7.6510516911789290f, 7.6582114827517955f, 7.6653359171851765f,
|
|
||||||
7.6724253419714952f, 7.6794800995054464f, 7.6865005271832185f,
|
|
||||||
7.6934869574993252f, 7.7004397181410926f, 7.7073591320808825f,
|
|
||||||
7.7142455176661224f, 7.7210991887071856f, 7.7279204545631996f,
|
|
||||||
7.7347096202258392f, 7.7414669864011465f, 7.7481928495894596f,
|
|
||||||
7.7548875021634691f, 7.7615512324444795f, 7.7681843247769260f,
|
|
||||||
7.7747870596011737f, 7.7813597135246608f, 7.7879025593914317f,
|
|
||||||
7.7944158663501062f, 7.8008998999203047f, 7.8073549220576037f,
|
|
||||||
7.8137811912170374f, 7.8201789624151887f, 7.8265484872909159f,
|
|
||||||
7.8328900141647422f, 7.8392037880969445f, 7.8454900509443757f,
|
|
||||||
7.8517490414160571f, 7.8579809951275719f, 7.8641861446542798f,
|
|
||||||
7.8703647195834048f, 7.8765169465650002f, 7.8826430493618425f,
|
|
||||||
7.8887432488982601f, 7.8948177633079446f, 7.9008668079807496f,
|
|
||||||
7.9068905956085187f, 7.9128893362299619f, 7.9188632372745955f,
|
|
||||||
7.9248125036057813f, 7.9307373375628867f, 7.9366379390025719f,
|
|
||||||
7.9425145053392399f, 7.9483672315846778f, 7.9541963103868758f,
|
|
||||||
7.9600019320680806f, 7.9657842846620870f, 7.9715435539507720f,
|
|
||||||
7.9772799234999168f, 7.9829935746943104f, 7.9886846867721664f,
|
|
||||||
7.9943534368588578f
|
|
||||||
};
|
|
||||||
|
|
||||||
// Faster logarithm for small integers, with the property of log2(0) == 0.
|
|
||||||
static inline double FastLog2(int v) {
|
|
||||||
if (v < (int)(sizeof(kLog2Table) / sizeof(kLog2Table[0]))) {
|
|
||||||
return kLog2Table[v];
|
|
||||||
}
|
|
||||||
return log2(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_FAST_LOG_H_
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Function to find maximal matching prefixes of strings.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_FIND_MATCH_LENGTH_H_
|
|
||||||
#define BROTLI_ENC_FIND_MATCH_LENGTH_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "./port.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
// Separate implementation for x86_64, for speed.
|
|
||||||
#if defined(__GNUC__) && defined(ARCH_K8)
|
|
||||||
|
|
||||||
static inline int FindMatchLengthWithLimit(const uint8_t* s1,
|
|
||||||
const uint8_t* s2,
|
|
||||||
size_t limit) {
|
|
||||||
int matched = 0;
|
|
||||||
size_t limit2 = (limit >> 3) + 1; // + 1 is for pre-decrement in while
|
|
||||||
while (PREDICT_TRUE(--limit2)) {
|
|
||||||
if (PREDICT_FALSE(BROTLI_UNALIGNED_LOAD64(s2) ==
|
|
||||||
BROTLI_UNALIGNED_LOAD64(s1 + matched))) {
|
|
||||||
s2 += 8;
|
|
||||||
matched += 8;
|
|
||||||
} else {
|
|
||||||
uint64_t x =
|
|
||||||
BROTLI_UNALIGNED_LOAD64(s2) ^ BROTLI_UNALIGNED_LOAD64(s1 + matched);
|
|
||||||
int matching_bits = __builtin_ctzll(x);
|
|
||||||
matched += matching_bits >> 3;
|
|
||||||
return matched;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
limit = (limit & 7) + 1; // + 1 is for pre-decrement in while
|
|
||||||
while (--limit) {
|
|
||||||
if (PREDICT_TRUE(s1[matched] == *s2)) {
|
|
||||||
++s2;
|
|
||||||
++matched;
|
|
||||||
} else {
|
|
||||||
return matched;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matched;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static inline int FindMatchLengthWithLimit(const uint8_t* s1,
|
|
||||||
const uint8_t* s2,
|
|
||||||
size_t limit) {
|
|
||||||
int matched = 0;
|
|
||||||
const uint8_t* s2_limit = s2 + limit;
|
|
||||||
const uint8_t* 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 &&
|
|
||||||
BROTLI_UNALIGNED_LOAD32(s2_ptr) ==
|
|
||||||
BROTLI_UNALIGNED_LOAD32(s1 + matched)) {
|
|
||||||
s2_ptr += 4;
|
|
||||||
matched += 4;
|
|
||||||
}
|
|
||||||
while ((s2_ptr < s2_limit) && (s1[matched] == *s2_ptr)) {
|
|
||||||
++s2_ptr;
|
|
||||||
++matched;
|
|
||||||
}
|
|
||||||
return matched;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_FIND_MATCH_LENGTH_H_
|
|
||||||
@@ -1,447 +0,0 @@
|
|||||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// A (forgetful) hash table to the data seen by the compressor, to
|
|
||||||
// help create backward references to previous data.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_HASH_H_
|
|
||||||
#define BROTLI_ENC_HASH_H_
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "./transform.h"
|
|
||||||
#include "./fast_log.h"
|
|
||||||
#include "./find_match_length.h"
|
|
||||||
#include "./port.h"
|
|
||||||
#include "./static_dict.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
// kHashMul32 multiplier has these properties:
|
|
||||||
// * The multiplier must be odd. Otherwise we may lose the highest bit.
|
|
||||||
// * No long streaks of 1s or 0s.
|
|
||||||
// * There is no effort to ensure that it is a prime, the oddity is enough
|
|
||||||
// for this use.
|
|
||||||
// * The number has been tuned heuristically against compression benchmarks.
|
|
||||||
static const uint32_t kHashMul32 = 0x1e35a7bd;
|
|
||||||
|
|
||||||
template<int kShiftBits, int kMinLength>
|
|
||||||
inline uint32_t Hash(const uint8_t *data) {
|
|
||||||
if (kMinLength <= 3) {
|
|
||||||
// If kMinLength is 2 or 3, we hash the first 3 bytes of data.
|
|
||||||
uint32_t h = (BROTLI_UNALIGNED_LOAD32(data) & 0xffffff) * kHashMul32;
|
|
||||||
// The higher bits contain more mixture from the multiplication,
|
|
||||||
// so we take our results from there.
|
|
||||||
return h >> (32 - kShiftBits);
|
|
||||||
} else {
|
|
||||||
// If kMinLength is at least 4, we hash the first 4 bytes of data.
|
|
||||||
uint32_t h = BROTLI_UNALIGNED_LOAD32(data) * kHashMul32;
|
|
||||||
// The higher bits contain more mixture from the multiplication,
|
|
||||||
// so we take our results from there.
|
|
||||||
return h >> (32 - kShiftBits);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usually, we always choose the longest backward reference. This function
|
|
||||||
// allows for the exception of that rule.
|
|
||||||
//
|
|
||||||
// If we choose a backward reference that is further away, it will
|
|
||||||
// usually be coded with more bits. We approximate this by assuming
|
|
||||||
// log2(distance). If the distance can be expressed in terms of the
|
|
||||||
// last four distances, we use some heuristic constants to estimate
|
|
||||||
// the bits cost. For the first up to four literals we use the bit
|
|
||||||
// cost of the literals from the literal cost model, after that we
|
|
||||||
// use the average bit cost of the cost model.
|
|
||||||
//
|
|
||||||
// This function is used to sometimes discard a longer backward reference
|
|
||||||
// when it is not much longer and the bit cost for encoding it is more
|
|
||||||
// than the saved literals.
|
|
||||||
inline double BackwardReferenceScore(double average_cost,
|
|
||||||
double start_cost4,
|
|
||||||
double start_cost3,
|
|
||||||
double start_cost2,
|
|
||||||
int copy_length,
|
|
||||||
int backward_reference_offset) {
|
|
||||||
double retval = 0;
|
|
||||||
switch (copy_length) {
|
|
||||||
case 2: retval = start_cost2; break;
|
|
||||||
case 3: retval = start_cost3; break;
|
|
||||||
default: retval = start_cost4 + (copy_length - 4) * average_cost; break;
|
|
||||||
}
|
|
||||||
retval -= 1.20 * Log2Floor(backward_reference_offset);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline double BackwardReferenceScoreUsingLastDistance(double average_cost,
|
|
||||||
double start_cost4,
|
|
||||||
double start_cost3,
|
|
||||||
double start_cost2,
|
|
||||||
int copy_length,
|
|
||||||
int distance_short_code) {
|
|
||||||
double retval = 0;
|
|
||||||
switch (copy_length) {
|
|
||||||
case 2: retval = start_cost2; break;
|
|
||||||
case 3: retval = start_cost3; break;
|
|
||||||
default: retval = start_cost4 + (copy_length - 4) * average_cost; break;
|
|
||||||
}
|
|
||||||
static const double kDistanceShortCodeBitCost[16] = {
|
|
||||||
-0.6, 0.95, 1.17, 1.27,
|
|
||||||
0.93, 0.93, 0.96, 0.96, 0.99, 0.99,
|
|
||||||
1.05, 1.05, 1.15, 1.15, 1.25, 1.25
|
|
||||||
};
|
|
||||||
retval -= kDistanceShortCodeBitCost[distance_short_code];
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A (forgetful) hash table to the data seen by the compressor, to
|
|
||||||
// help create backward references to previous data.
|
|
||||||
//
|
|
||||||
// This is a hash map of fixed size (kBucketSize) to a ring buffer of
|
|
||||||
// fixed size (kBlockSize). The ring buffer contains the last kBlockSize
|
|
||||||
// index positions of the given hash key in the compressed data.
|
|
||||||
template <int kBucketBits, int kBlockBits, int kMinLength>
|
|
||||||
class HashLongestMatch {
|
|
||||||
public:
|
|
||||||
HashLongestMatch()
|
|
||||||
: last_distance1_(4),
|
|
||||||
last_distance2_(11),
|
|
||||||
last_distance3_(15),
|
|
||||||
last_distance4_(16),
|
|
||||||
insert_length_(0),
|
|
||||||
average_cost_(5.4),
|
|
||||||
static_dict_(NULL) {
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
void Reset() {
|
|
||||||
std::fill(&num_[0], &num_[sizeof(num_) / sizeof(num_[0])], 0);
|
|
||||||
}
|
|
||||||
void SetStaticDictionary(const StaticDictionary *dict) {
|
|
||||||
static_dict_ = dict;
|
|
||||||
}
|
|
||||||
bool HasStaticDictionary() const {
|
|
||||||
return static_dict_ != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look at 3 bytes at data.
|
|
||||||
// Compute a hash from these, and store the value of ix at that position.
|
|
||||||
inline void Store(const uint8_t *data, const int ix) {
|
|
||||||
const uint32_t key = Hash<kBucketBits, kMinLength>(data);
|
|
||||||
const int minor_ix = num_[key] & kBlockMask;
|
|
||||||
buckets_[key][minor_ix] = ix;
|
|
||||||
++num_[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store hashes for a range of data.
|
|
||||||
void StoreHashes(const uint8_t *data, size_t len, int startix, int mask) {
|
|
||||||
for (int p = 0; p < len; ++p) {
|
|
||||||
Store(&data[p & mask], startix + p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find a longest backward match of &data[cur_ix] up to the length of
|
|
||||||
// max_length.
|
|
||||||
//
|
|
||||||
// Does not look for matches longer than max_length.
|
|
||||||
// Does not look for matches further away than max_backward.
|
|
||||||
// Writes the best found match length into best_len_out.
|
|
||||||
// Writes the index (&data[index]) offset from the start of the best match
|
|
||||||
// into best_distance_out.
|
|
||||||
// Write the score of the best match into best_score_out.
|
|
||||||
bool FindLongestMatch(const uint8_t * __restrict data,
|
|
||||||
const float * __restrict literal_cost,
|
|
||||||
const size_t ring_buffer_mask,
|
|
||||||
const uint32_t cur_ix,
|
|
||||||
uint32_t max_length,
|
|
||||||
const uint32_t max_backward,
|
|
||||||
size_t * __restrict best_len_out,
|
|
||||||
size_t * __restrict best_len_code_out,
|
|
||||||
size_t * __restrict best_distance_out,
|
|
||||||
double * __restrict best_score_out,
|
|
||||||
bool * __restrict in_dictionary) {
|
|
||||||
*in_dictionary = true;
|
|
||||||
*best_len_code_out = 0;
|
|
||||||
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
|
||||||
const double start_cost4 = literal_cost == NULL ? 20 :
|
|
||||||
literal_cost[cur_ix_masked] +
|
|
||||||
literal_cost[(cur_ix + 1) & ring_buffer_mask] +
|
|
||||||
literal_cost[(cur_ix + 2) & ring_buffer_mask] +
|
|
||||||
literal_cost[(cur_ix + 3) & ring_buffer_mask];
|
|
||||||
const double start_cost3 = literal_cost == NULL ? 15 :
|
|
||||||
literal_cost[cur_ix_masked] +
|
|
||||||
literal_cost[(cur_ix + 1) & ring_buffer_mask] +
|
|
||||||
literal_cost[(cur_ix + 2) & ring_buffer_mask] + 0.3;
|
|
||||||
double start_cost2 = literal_cost == NULL ? 10 :
|
|
||||||
literal_cost[cur_ix_masked] +
|
|
||||||
literal_cost[(cur_ix + 1) & ring_buffer_mask] + 1.2;
|
|
||||||
bool match_found = false;
|
|
||||||
// Don't accept a short copy from far away.
|
|
||||||
double best_score = 8.115;
|
|
||||||
if (insert_length_ < 4) {
|
|
||||||
double cost_diff[4] = { 0.10, 0.04, 0.02, 0.01 };
|
|
||||||
best_score += cost_diff[insert_length_];
|
|
||||||
}
|
|
||||||
size_t best_len = *best_len_out;
|
|
||||||
*best_len_out = 0;
|
|
||||||
size_t best_ix = 1;
|
|
||||||
// Try last distance first.
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
|
||||||
size_t prev_ix = cur_ix;
|
|
||||||
switch(i) {
|
|
||||||
case 0: prev_ix -= last_distance1_; break;
|
|
||||||
case 1: prev_ix -= last_distance2_; break;
|
|
||||||
case 2: prev_ix -= last_distance3_; break;
|
|
||||||
case 3: prev_ix -= last_distance4_; break;
|
|
||||||
|
|
||||||
case 4: prev_ix -= last_distance1_ - 1; break;
|
|
||||||
case 5: prev_ix -= last_distance1_ + 1; break;
|
|
||||||
case 6: prev_ix -= last_distance1_ - 2; break;
|
|
||||||
case 7: prev_ix -= last_distance1_ + 2; break;
|
|
||||||
case 8: prev_ix -= last_distance1_ - 3; break;
|
|
||||||
case 9: prev_ix -= last_distance1_ + 3; break;
|
|
||||||
|
|
||||||
case 10: prev_ix -= last_distance2_ - 1; break;
|
|
||||||
case 11: prev_ix -= last_distance2_ + 1; break;
|
|
||||||
case 12: prev_ix -= last_distance2_ - 2; break;
|
|
||||||
case 13: prev_ix -= last_distance2_ + 2; break;
|
|
||||||
case 14: prev_ix -= last_distance2_ - 3; break;
|
|
||||||
case 15: prev_ix -= last_distance2_ + 3; break;
|
|
||||||
}
|
|
||||||
if (prev_ix >= cur_ix) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const size_t backward = cur_ix - prev_ix;
|
|
||||||
if (PREDICT_FALSE(backward > max_backward)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
prev_ix &= ring_buffer_mask;
|
|
||||||
if (cur_ix_masked + best_len > ring_buffer_mask ||
|
|
||||||
prev_ix + best_len > ring_buffer_mask ||
|
|
||||||
data[cur_ix_masked + best_len] != data[prev_ix + best_len]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const size_t len =
|
|
||||||
FindMatchLengthWithLimit(&data[prev_ix], &data[cur_ix_masked],
|
|
||||||
max_length);
|
|
||||||
if (len >= std::max(kMinLength, 3) ||
|
|
||||||
(kMinLength == 2 && len == 2 && i < 2)) {
|
|
||||||
// Comparing for >= 2 does not change the semantics, but just saves for
|
|
||||||
// a few unnecessary binary logarithms in backward reference score,
|
|
||||||
// since we are not interested in such short matches.
|
|
||||||
const double score = BackwardReferenceScoreUsingLastDistance(
|
|
||||||
average_cost_,
|
|
||||||
start_cost4,
|
|
||||||
start_cost3,
|
|
||||||
start_cost2,
|
|
||||||
len, i);
|
|
||||||
if (best_score < score) {
|
|
||||||
best_score = score;
|
|
||||||
best_len = len;
|
|
||||||
best_ix = backward;
|
|
||||||
*best_len_out = best_len;
|
|
||||||
*best_len_code_out = best_len;
|
|
||||||
*best_distance_out = best_ix;
|
|
||||||
*best_score_out = best_score;
|
|
||||||
match_found = true;
|
|
||||||
*in_dictionary = backward > max_backward;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (kMinLength == 2) {
|
|
||||||
int stop = int(cur_ix) - 64;
|
|
||||||
if (stop < 0) { stop = 0; }
|
|
||||||
start_cost2 -= 1.0;
|
|
||||||
for (int i = cur_ix - 1; i > stop; --i) {
|
|
||||||
size_t prev_ix = i;
|
|
||||||
const size_t backward = cur_ix - prev_ix;
|
|
||||||
if (PREDICT_FALSE(backward > max_backward)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
prev_ix &= ring_buffer_mask;
|
|
||||||
if (data[cur_ix_masked] != data[prev_ix] ||
|
|
||||||
data[cur_ix_masked + 1] != data[prev_ix + 1]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int len = 2;
|
|
||||||
const double score = start_cost2 - 2.3 * Log2Floor(backward);
|
|
||||||
|
|
||||||
if (best_score < score) {
|
|
||||||
best_score = score;
|
|
||||||
best_len = len;
|
|
||||||
best_ix = backward;
|
|
||||||
*best_len_out = best_len;
|
|
||||||
*best_len_code_out = best_len;
|
|
||||||
*best_distance_out = best_ix;
|
|
||||||
match_found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const uint32_t key = Hash<kBucketBits, kMinLength>(&data[cur_ix_masked]);
|
|
||||||
const int * __restrict const bucket = &buckets_[key][0];
|
|
||||||
const int down = (num_[key] > kBlockSize) ? (num_[key] - kBlockSize) : 0;
|
|
||||||
for (int i = num_[key] - 1; i >= down; --i) {
|
|
||||||
int prev_ix = bucket[i & kBlockMask];
|
|
||||||
if (prev_ix >= 0) {
|
|
||||||
const size_t backward = cur_ix - prev_ix;
|
|
||||||
if (PREDICT_FALSE(backward > max_backward)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
prev_ix &= ring_buffer_mask;
|
|
||||||
if (cur_ix_masked + best_len > ring_buffer_mask ||
|
|
||||||
prev_ix + best_len > ring_buffer_mask ||
|
|
||||||
data[cur_ix_masked + best_len] != data[prev_ix + best_len]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const size_t len =
|
|
||||||
FindMatchLengthWithLimit(&data[prev_ix], &data[cur_ix_masked],
|
|
||||||
max_length);
|
|
||||||
if (len >= std::max(kMinLength, 3)) {
|
|
||||||
// Comparing for >= 3 does not change the semantics, but just saves
|
|
||||||
// for a few unnecessary binary logarithms in backward reference
|
|
||||||
// score, since we are not interested in such short matches.
|
|
||||||
const double score = BackwardReferenceScore(average_cost_,
|
|
||||||
start_cost4,
|
|
||||||
start_cost3,
|
|
||||||
start_cost2,
|
|
||||||
len, backward);
|
|
||||||
if (best_score < score) {
|
|
||||||
best_score = score;
|
|
||||||
best_len = len;
|
|
||||||
best_ix = backward;
|
|
||||||
*best_len_out = best_len;
|
|
||||||
*best_len_code_out = best_len;
|
|
||||||
*best_distance_out = best_ix;
|
|
||||||
*best_score_out = best_score;
|
|
||||||
match_found = true;
|
|
||||||
*in_dictionary = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (static_dict_ != NULL) {
|
|
||||||
// We decide based on first 4 bytes how many bytes to test for.
|
|
||||||
int prefix = BROTLI_UNALIGNED_LOAD32(&data[cur_ix_masked]);
|
|
||||||
int maxlen = static_dict_->GetLength(prefix);
|
|
||||||
for (int len = std::min<size_t>(maxlen, max_length);
|
|
||||||
len > best_len && len >= 4; --len) {
|
|
||||||
std::string snippet((const char *)&data[cur_ix_masked], len);
|
|
||||||
int copy_len_code;
|
|
||||||
int word_id;
|
|
||||||
if (static_dict_->Get(snippet, ©_len_code, &word_id)) {
|
|
||||||
const size_t backward = max_backward + word_id + 1;
|
|
||||||
const double score = BackwardReferenceScore(average_cost_,
|
|
||||||
start_cost4,
|
|
||||||
start_cost3,
|
|
||||||
start_cost2,
|
|
||||||
len, backward);
|
|
||||||
if (best_score < score) {
|
|
||||||
best_score = score;
|
|
||||||
best_len = len;
|
|
||||||
best_ix = backward;
|
|
||||||
*best_len_out = best_len;
|
|
||||||
*best_len_code_out = copy_len_code;
|
|
||||||
*best_distance_out = best_ix;
|
|
||||||
*best_score_out = best_score;
|
|
||||||
match_found = true;
|
|
||||||
*in_dictionary = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return match_found;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_last_distance(int v) {
|
|
||||||
if (last_distance1_ != v) {
|
|
||||||
last_distance4_ = last_distance3_;
|
|
||||||
last_distance3_ = last_distance2_;
|
|
||||||
last_distance2_ = last_distance1_;
|
|
||||||
last_distance1_ = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int last_distance() const { return last_distance1_; }
|
|
||||||
|
|
||||||
void set_insert_length(int v) { insert_length_ = v; }
|
|
||||||
|
|
||||||
void set_average_cost(double v) { average_cost_ = v; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Number of hash buckets.
|
|
||||||
static const uint32_t kBucketSize = 1 << kBucketBits;
|
|
||||||
|
|
||||||
// Only kBlockSize newest backward references are kept,
|
|
||||||
// and the older are forgotten.
|
|
||||||
static const uint32_t kBlockSize = 1 << kBlockBits;
|
|
||||||
|
|
||||||
// Mask for accessing entries in a block (in a ringbuffer manner).
|
|
||||||
static const uint32_t kBlockMask = (1 << kBlockBits) - 1;
|
|
||||||
|
|
||||||
// Number of entries in a particular bucket.
|
|
||||||
uint16_t num_[kBucketSize];
|
|
||||||
|
|
||||||
// Buckets containing kBlockSize of backward references.
|
|
||||||
int buckets_[kBucketSize][kBlockSize];
|
|
||||||
|
|
||||||
int last_distance1_;
|
|
||||||
int last_distance2_;
|
|
||||||
int last_distance3_;
|
|
||||||
int last_distance4_;
|
|
||||||
|
|
||||||
// Cost adjustment for how many literals we are planning to insert
|
|
||||||
// anyway.
|
|
||||||
int insert_length_;
|
|
||||||
|
|
||||||
double average_cost_;
|
|
||||||
|
|
||||||
const StaticDictionary *static_dict_;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Hashers {
|
|
||||||
enum Type {
|
|
||||||
HASH_15_8_4 = 0,
|
|
||||||
HASH_15_8_2 = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
void Init(Type type) {
|
|
||||||
switch (type) {
|
|
||||||
case HASH_15_8_4:
|
|
||||||
hash_15_8_4.reset(new HashLongestMatch<15, 8, 4>());
|
|
||||||
break;
|
|
||||||
case HASH_15_8_2:
|
|
||||||
hash_15_8_2.reset(new HashLongestMatch<15, 8, 2>());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetStaticDictionary(const StaticDictionary *dict) {
|
|
||||||
if (hash_15_8_4.get() != NULL) hash_15_8_4->SetStaticDictionary(dict);
|
|
||||||
if (hash_15_8_2.get() != NULL) hash_15_8_2->SetStaticDictionary(dict);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<HashLongestMatch<15, 8, 4> > hash_15_8_4;
|
|
||||||
std::unique_ptr<HashLongestMatch<15, 8, 2> > hash_15_8_2;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_HASH_H_
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Build per-context histograms of literals, commands and distance codes.
|
|
||||||
|
|
||||||
#include "./histogram.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include "./block_splitter.h"
|
|
||||||
#include "./command.h"
|
|
||||||
#include "./context.h"
|
|
||||||
#include "./prefix.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
void BuildHistograms(
|
|
||||||
const std::vector<Command>& cmds,
|
|
||||||
const BlockSplit& literal_split,
|
|
||||||
const BlockSplit& insert_and_copy_split,
|
|
||||||
const BlockSplit& dist_split,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
size_t pos,
|
|
||||||
size_t mask,
|
|
||||||
const std::vector<int>& context_modes,
|
|
||||||
std::vector<HistogramLiteral>* literal_histograms,
|
|
||||||
std::vector<HistogramCommand>* insert_and_copy_histograms,
|
|
||||||
std::vector<HistogramDistance>* copy_dist_histograms) {
|
|
||||||
BlockSplitIterator literal_it(literal_split);
|
|
||||||
BlockSplitIterator insert_and_copy_it(insert_and_copy_split);
|
|
||||||
BlockSplitIterator dist_it(dist_split);
|
|
||||||
for (int i = 0; i < cmds.size(); ++i) {
|
|
||||||
const Command &cmd = cmds[i];
|
|
||||||
insert_and_copy_it.Next();
|
|
||||||
(*insert_and_copy_histograms)[insert_and_copy_it.type_].Add(
|
|
||||||
cmd.command_prefix_);
|
|
||||||
for (int j = 0; j < cmd.insert_length_; ++j) {
|
|
||||||
literal_it.Next();
|
|
||||||
uint8_t prev_byte = pos > 0 ? ringbuffer[(pos - 1) & mask] : 0;
|
|
||||||
uint8_t prev_byte2 = pos > 1 ? ringbuffer[(pos - 2) & mask] : 0;
|
|
||||||
int context = (literal_it.type_ << kLiteralContextBits) +
|
|
||||||
Context(prev_byte, prev_byte2, context_modes[literal_it.type_]);
|
|
||||||
(*literal_histograms)[context].Add(ringbuffer[pos & mask]);
|
|
||||||
++pos;
|
|
||||||
}
|
|
||||||
pos += cmd.copy_length_;
|
|
||||||
if (cmd.copy_length_ > 0 && cmd.distance_prefix_ != 0xffff) {
|
|
||||||
dist_it.Next();
|
|
||||||
int context = (dist_it.type_ << kDistanceContextBits) +
|
|
||||||
((cmd.copy_length_code_ > 4) ? 3 : cmd.copy_length_code_ - 2);
|
|
||||||
(*copy_dist_histograms)[context].Add(cmd.distance_prefix_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuildLiteralHistogramsForBlockType(
|
|
||||||
const std::vector<Command>& cmds,
|
|
||||||
const BlockSplit& literal_split,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
size_t pos,
|
|
||||||
size_t mask,
|
|
||||||
int block_type,
|
|
||||||
int context_mode,
|
|
||||||
std::vector<HistogramLiteral>* histograms) {
|
|
||||||
BlockSplitIterator literal_it(literal_split);
|
|
||||||
for (int i = 0; i < cmds.size(); ++i) {
|
|
||||||
const Command &cmd = cmds[i];
|
|
||||||
for (int j = 0; j < cmd.insert_length_; ++j) {
|
|
||||||
literal_it.Next();
|
|
||||||
if (literal_it.type_ == block_type) {
|
|
||||||
uint8_t prev_byte = pos > 0 ? ringbuffer[(pos - 1) & mask] : 0;
|
|
||||||
uint8_t prev_byte2 = pos > 1 ? ringbuffer[(pos - 2) & mask] : 0;
|
|
||||||
int context = Context(prev_byte, prev_byte2, context_mode);
|
|
||||||
(*histograms)[context].Add(ringbuffer[pos & mask]);
|
|
||||||
}
|
|
||||||
++pos;
|
|
||||||
}
|
|
||||||
pos += cmd.copy_length_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Models the histograms of literals, commands and distance codes.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_HISTOGRAM_H_
|
|
||||||
#define BROTLI_ENC_HISTOGRAM_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <utility>
|
|
||||||
#include "./command.h"
|
|
||||||
#include "./fast_log.h"
|
|
||||||
#include "./prefix.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
class BlockSplit;
|
|
||||||
|
|
||||||
// A simple container for histograms of data in blocks.
|
|
||||||
template<int kDataSize>
|
|
||||||
struct Histogram {
|
|
||||||
Histogram() {
|
|
||||||
Clear();
|
|
||||||
}
|
|
||||||
void Clear() {
|
|
||||||
memset(data_, 0, sizeof(data_));
|
|
||||||
total_count_ = 0;
|
|
||||||
}
|
|
||||||
void Add(int val) {
|
|
||||||
++data_[val];
|
|
||||||
++total_count_;
|
|
||||||
}
|
|
||||||
void Remove(int val) {
|
|
||||||
--data_[val];
|
|
||||||
--total_count_;
|
|
||||||
}
|
|
||||||
template<typename DataType>
|
|
||||||
void Add(const DataType *p, size_t n) {
|
|
||||||
total_count_ += n;
|
|
||||||
n += 1;
|
|
||||||
while(--n) ++data_[*p++];
|
|
||||||
}
|
|
||||||
void AddHistogram(const Histogram& v) {
|
|
||||||
total_count_ += v.total_count_;
|
|
||||||
for (int i = 0; i < kDataSize; ++i) {
|
|
||||||
data_[i] += v.data_[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
double EntropyBitCost() const {
|
|
||||||
double retval = total_count_ * FastLog2(total_count_);
|
|
||||||
for (int i = 0; i < kDataSize; ++i) {
|
|
||||||
retval -= data_[i] * FastLog2(data_[i]);
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
int data_[kDataSize];
|
|
||||||
int total_count_;
|
|
||||||
double bit_cost_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Literal histogram.
|
|
||||||
typedef Histogram<256> HistogramLiteral;
|
|
||||||
// Prefix histograms.
|
|
||||||
typedef Histogram<kNumCommandPrefixes> HistogramCommand;
|
|
||||||
typedef Histogram<kNumDistancePrefixes> HistogramDistance;
|
|
||||||
typedef Histogram<kNumBlockLenPrefixes> HistogramBlockLength;
|
|
||||||
// Context map histogram, 256 Huffman tree indexes + 16 run length codes.
|
|
||||||
typedef Histogram<272> HistogramContextMap;
|
|
||||||
// Block type histogram, 256 block types + 2 special symbols.
|
|
||||||
typedef Histogram<258> HistogramBlockType;
|
|
||||||
|
|
||||||
static const int kLiteralContextBits = 6;
|
|
||||||
static const int kDistanceContextBits = 2;
|
|
||||||
|
|
||||||
void BuildHistograms(
|
|
||||||
const std::vector<Command>& cmds,
|
|
||||||
const BlockSplit& literal_split,
|
|
||||||
const BlockSplit& insert_and_copy_split,
|
|
||||||
const BlockSplit& dist_split,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
size_t pos,
|
|
||||||
size_t mask,
|
|
||||||
const std::vector<int>& context_modes,
|
|
||||||
std::vector<HistogramLiteral>* literal_histograms,
|
|
||||||
std::vector<HistogramCommand>* insert_and_copy_histograms,
|
|
||||||
std::vector<HistogramDistance>* copy_dist_histograms);
|
|
||||||
|
|
||||||
void BuildLiteralHistogramsForBlockType(
|
|
||||||
const std::vector<Command>& cmds,
|
|
||||||
const BlockSplit& literal_split,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
size_t pos,
|
|
||||||
size_t mask,
|
|
||||||
int block_type,
|
|
||||||
int context_mode,
|
|
||||||
std::vector<HistogramLiteral>* histograms);
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_HISTOGRAM_H_
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Literal cost model to allow backward reference replacement to be efficient.
|
|
||||||
|
|
||||||
#include "./literal_cost.h"
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
static int UTF8Position(int last, int c, int clamp) {
|
|
||||||
if (c < 128) {
|
|
||||||
return 0; // Next one is the 'Byte 1' again.
|
|
||||||
} else if (c >= 192) {
|
|
||||||
return std::min(1, clamp); // Next one is the 'Byte 2' of utf-8 encoding.
|
|
||||||
} else {
|
|
||||||
// Let's decide over the last byte if this ends the sequence.
|
|
||||||
if (last < 0xe0) {
|
|
||||||
return 0; // Completed two or three byte coding.
|
|
||||||
} else {
|
|
||||||
return std::min(2, clamp); // Next one is the 'Byte 3' of utf-8 encoding.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int DecideMultiByteStatsLevel(size_t pos, size_t len, size_t mask,
|
|
||||||
const uint8_t *data) {
|
|
||||||
int counts[3] = { 0 };
|
|
||||||
int max_utf8 = 1; // should be 2, but 1 compresses better.
|
|
||||||
int last_c = 0;
|
|
||||||
int utf8_pos = 0;
|
|
||||||
for (int i = 0; i < len; ++i) {
|
|
||||||
int c = data[(pos + i) & mask];
|
|
||||||
utf8_pos = UTF8Position(last_c, c, 2);
|
|
||||||
++counts[utf8_pos];
|
|
||||||
last_c = c;
|
|
||||||
}
|
|
||||||
if (counts[2] < 500) {
|
|
||||||
max_utf8 = 1;
|
|
||||||
}
|
|
||||||
if (counts[1] + counts[2] < 25) {
|
|
||||||
max_utf8 = 0;
|
|
||||||
}
|
|
||||||
return max_utf8;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask,
|
|
||||||
size_t cost_mask, const uint8_t *data,
|
|
||||||
float *cost) {
|
|
||||||
|
|
||||||
// max_utf8 is 0 (normal ascii single byte modeling),
|
|
||||||
// 1 (for 2-byte utf-8 modeling), or 2 (for 3-byte utf-8 modeling).
|
|
||||||
const int max_utf8 = DecideMultiByteStatsLevel(pos, len, mask, data);
|
|
||||||
int histogram[3][256] = { { 0 } };
|
|
||||||
int window_half = 495;
|
|
||||||
int in_window = std::min(static_cast<size_t>(window_half), len);
|
|
||||||
int in_window_utf8[3] = { 0 };
|
|
||||||
|
|
||||||
// Bootstrap histograms.
|
|
||||||
int last_c = 0;
|
|
||||||
int utf8_pos = 0;
|
|
||||||
for (int i = 0; i < in_window; ++i) {
|
|
||||||
int c = data[(pos + i) & mask];
|
|
||||||
++histogram[utf8_pos][c];
|
|
||||||
++in_window_utf8[utf8_pos];
|
|
||||||
utf8_pos = UTF8Position(last_c, c, max_utf8);
|
|
||||||
last_c = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute bit costs with sliding window.
|
|
||||||
for (int i = 0; i < len; ++i) {
|
|
||||||
if (i - window_half >= 0) {
|
|
||||||
// Remove a byte in the past.
|
|
||||||
int c = (i - window_half - 1) < 0 ?
|
|
||||||
0 : data[(pos + i - window_half - 1) & mask];
|
|
||||||
int last_c = (i - window_half - 2) < 0 ?
|
|
||||||
0 : data[(pos + i - window_half - 2) & mask];
|
|
||||||
int utf8_pos2 = UTF8Position(last_c, c, max_utf8);
|
|
||||||
--histogram[utf8_pos2][data[(pos + i - window_half) & mask]];
|
|
||||||
--in_window_utf8[utf8_pos2];
|
|
||||||
}
|
|
||||||
if (i + window_half < len) {
|
|
||||||
// Add a byte in the future.
|
|
||||||
int c = (i + window_half - 1) < 0 ?
|
|
||||||
0 : data[(pos + i + window_half - 1) & mask];
|
|
||||||
int last_c = (i + window_half - 2) < 0 ?
|
|
||||||
0 : data[(pos + i + window_half - 2) & mask];
|
|
||||||
int utf8_pos2 = UTF8Position(last_c, c, max_utf8);
|
|
||||||
++histogram[utf8_pos2][data[(pos + i + window_half) & mask]];
|
|
||||||
++in_window_utf8[utf8_pos2];
|
|
||||||
}
|
|
||||||
int c = i < 1 ? 0 : data[(pos + i - 1) & mask];
|
|
||||||
int last_c = i < 2 ? 0 : data[(pos + i - 2) & mask];
|
|
||||||
int utf8_pos = UTF8Position(last_c, c, max_utf8);
|
|
||||||
int masked_pos = (pos + i) & mask;
|
|
||||||
int histo = histogram[utf8_pos][data[masked_pos]];
|
|
||||||
if (histo == 0) {
|
|
||||||
histo = 1;
|
|
||||||
}
|
|
||||||
float lit_cost = log2(static_cast<double>(in_window_utf8[utf8_pos])
|
|
||||||
/ histo);
|
|
||||||
lit_cost += 0.02905;
|
|
||||||
if (lit_cost < 1.0) {
|
|
||||||
lit_cost *= 0.5;
|
|
||||||
lit_cost += 0.5;
|
|
||||||
}
|
|
||||||
// Make the first bytes more expensive -- seems to help, not sure why.
|
|
||||||
// Perhaps because the entropy source is changing its properties
|
|
||||||
// rapidly in the beginning of the file, perhaps because the beginning
|
|
||||||
// of the data is a statistical "anomaly".
|
|
||||||
if (i < 2000) {
|
|
||||||
lit_cost += 0.7 - ((2000 - i) / 2000.0 * 0.35);
|
|
||||||
}
|
|
||||||
cost[(pos + i) & cost_mask] = lit_cost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EstimateBitCostsForLiterals(size_t pos, size_t len, size_t mask,
|
|
||||||
size_t cost_mask, const uint8_t *data,
|
|
||||||
float *cost) {
|
|
||||||
int histogram[256] = { 0 };
|
|
||||||
int window_half = 2000;
|
|
||||||
int in_window = std::min(static_cast<size_t>(window_half), len);
|
|
||||||
|
|
||||||
// Bootstrap histogram.
|
|
||||||
for (int i = 0; i < in_window; ++i) {
|
|
||||||
++histogram[data[(pos + i) & mask]];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute bit costs with sliding window.
|
|
||||||
for (int i = 0; i < len; ++i) {
|
|
||||||
if (i - window_half >= 0) {
|
|
||||||
// Remove a byte in the past.
|
|
||||||
--histogram[data[(pos + i - window_half) & mask]];
|
|
||||||
--in_window;
|
|
||||||
}
|
|
||||||
if (i + window_half < len) {
|
|
||||||
// Add a byte in the future.
|
|
||||||
++histogram[data[(pos + i + window_half) & mask]];
|
|
||||||
++in_window;
|
|
||||||
}
|
|
||||||
int histo = histogram[data[(pos + i) & mask]];
|
|
||||||
if (histo == 0) {
|
|
||||||
histo = 1;
|
|
||||||
}
|
|
||||||
float lit_cost = log2(static_cast<double>(in_window) / histo);
|
|
||||||
lit_cost += 0.029;
|
|
||||||
if (lit_cost < 1.0) {
|
|
||||||
lit_cost *= 0.5;
|
|
||||||
lit_cost += 0.5;
|
|
||||||
}
|
|
||||||
cost[(pos + i) & cost_mask] = lit_cost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Literal cost model to allow backward reference replacement to be efficient.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_LITERAL_COST_H_
|
|
||||||
#define BROTLI_ENC_LITERAL_COST_H_
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
// Estimates how many bits the literals in the interval [pos, pos + len) in the
|
|
||||||
// ringbuffer (data, mask) will take entropy coded and writes these estimates
|
|
||||||
// to the ringbuffer (cost, mask).
|
|
||||||
void EstimateBitCostsForLiterals(size_t pos, size_t len, size_t mask,
|
|
||||||
size_t cost_mask, const uint8_t *data,
|
|
||||||
float *cost);
|
|
||||||
|
|
||||||
void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask,
|
|
||||||
size_t cost_mask, const uint8_t *data,
|
|
||||||
float *cost);
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_LITERAL_COST_H_
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Macros for endianness, branch prediction and unaligned loads and stores.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_PORT_H_
|
|
||||||
#define BROTLI_ENC_PORT_H_
|
|
||||||
|
|
||||||
#if defined OS_LINUX || defined OS_CYGWIN
|
|
||||||
#include <endian.h>
|
|
||||||
#elif defined OS_FREEBSD
|
|
||||||
#include <machine/endian.h>
|
|
||||||
#elif defined OS_MACOSX
|
|
||||||
#include <machine/endian.h>
|
|
||||||
/* Let's try and follow the Linux convention */
|
|
||||||
#define __BYTE_ORDER BYTE_ORDER
|
|
||||||
#define __LITTLE_ENDIAN LITTLE_ENDIAN
|
|
||||||
#define __BIG_ENDIAN BIG_ENDIAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// define the macros IS_LITTLE_ENDIAN or IS_BIG_ENDIAN
|
|
||||||
// using the above endian definitions from endian.h if
|
|
||||||
// endian.h was included
|
|
||||||
#ifdef __BYTE_ORDER
|
|
||||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
||||||
#define IS_LITTLE_ENDIAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
||||||
#define IS_BIG_ENDIAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#if defined(__LITTLE_ENDIAN__)
|
|
||||||
#define IS_LITTLE_ENDIAN
|
|
||||||
#elif defined(__BIG_ENDIAN__)
|
|
||||||
#define IS_BIG_ENDIAN
|
|
||||||
#endif
|
|
||||||
#endif // __BYTE_ORDER
|
|
||||||
|
|
||||||
#if defined(COMPILER_GCC3)
|
|
||||||
#define PREDICT_FALSE(x) (__builtin_expect(x, 0))
|
|
||||||
#define PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
|
|
||||||
#else
|
|
||||||
#define PREDICT_FALSE(x) x
|
|
||||||
#define PREDICT_TRUE(x) x
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Portable handling of unaligned loads, stores, and copies.
|
|
||||||
// On some platforms, like ARM, the copy functions can be more efficient
|
|
||||||
// then a load and a store.
|
|
||||||
|
|
||||||
#if defined(ARCH_PIII) || defined(ARCH_ATHLON) || \
|
|
||||||
defined(ARCH_K8) || defined(_ARCH_PPC)
|
|
||||||
|
|
||||||
// x86 and x86-64 can perform unaligned loads/stores directly;
|
|
||||||
// modern PowerPC hardware can also do unaligned integer loads and stores;
|
|
||||||
// but note: the FPU still sends unaligned loads and stores to a trap handler!
|
|
||||||
|
|
||||||
#define BROTLI_UNALIGNED_LOAD32(_p) (*reinterpret_cast<const uint32_t *>(_p))
|
|
||||||
#define BROTLI_UNALIGNED_LOAD64(_p) (*reinterpret_cast<const uint64_t *>(_p))
|
|
||||||
|
|
||||||
#define BROTLI_UNALIGNED_STORE32(_p, _val) \
|
|
||||||
(*reinterpret_cast<uint32_t *>(_p) = (_val))
|
|
||||||
#define BROTLI_UNALIGNED_STORE64(_p, _val) \
|
|
||||||
(*reinterpret_cast<uint64_t *>(_p) = (_val))
|
|
||||||
|
|
||||||
#elif defined(__arm__) && \
|
|
||||||
!defined(__ARM_ARCH_5__) && \
|
|
||||||
!defined(__ARM_ARCH_5T__) && \
|
|
||||||
!defined(__ARM_ARCH_5TE__) && \
|
|
||||||
!defined(__ARM_ARCH_5TEJ__) && \
|
|
||||||
!defined(__ARM_ARCH_6__) && \
|
|
||||||
!defined(__ARM_ARCH_6J__) && \
|
|
||||||
!defined(__ARM_ARCH_6K__) && \
|
|
||||||
!defined(__ARM_ARCH_6Z__) && \
|
|
||||||
!defined(__ARM_ARCH_6ZK__) && \
|
|
||||||
!defined(__ARM_ARCH_6T2__)
|
|
||||||
|
|
||||||
// ARMv7 and newer support native unaligned accesses, but only of 16-bit
|
|
||||||
// and 32-bit values (not 64-bit); older versions either raise a fatal signal,
|
|
||||||
// do an unaligned read and rotate the words around a bit, or do the reads very
|
|
||||||
// slowly (trip through kernel mode).
|
|
||||||
|
|
||||||
#define BROTLI_UNALIGNED_LOAD32(_p) (*reinterpret_cast<const uint32_t *>(_p))
|
|
||||||
#define BROTLI_UNALIGNED_STORE32(_p, _val) \
|
|
||||||
(*reinterpret_cast<uint32_t *>(_p) = (_val))
|
|
||||||
|
|
||||||
inline uint64_t BROTLI_UNALIGNED_LOAD64(const void *p) {
|
|
||||||
uint64_t t;
|
|
||||||
memcpy(&t, p, sizeof t);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void BROTLI_UNALIGNED_STORE64(void *p, uint64_t v) {
|
|
||||||
memcpy(p, &v, sizeof v);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// These functions are provided for architectures that don't support
|
|
||||||
// unaligned loads and stores.
|
|
||||||
|
|
||||||
inline uint32_t BROTLI_UNALIGNED_LOAD32(const void *p) {
|
|
||||||
uint32_t t;
|
|
||||||
memcpy(&t, p, sizeof t);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint64_t BROTLI_UNALIGNED_LOAD64(const void *p) {
|
|
||||||
uint64_t t;
|
|
||||||
memcpy(&t, p, sizeof t);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void BROTLI_UNALIGNED_STORE32(void *p, uint32_t v) {
|
|
||||||
memcpy(p, &v, sizeof v);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void BROTLI_UNALIGNED_STORE64(void *p, uint64_t v) {
|
|
||||||
memcpy(p, &v, sizeof v);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_PORT_H_
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Functions for encoding of integers into prefix codes the amount of extra
|
|
||||||
// bits, and the actual values of the extra bits.
|
|
||||||
|
|
||||||
#include "./prefix.h"
|
|
||||||
|
|
||||||
#include "./fast_log.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
// Represents the range of values belonging to a prefix code:
|
|
||||||
// [offset, offset + 2^nbits)
|
|
||||||
struct PrefixCodeRange {
|
|
||||||
int offset;
|
|
||||||
int nbits;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const PrefixCodeRange kBlockLengthPrefixCode[kNumBlockLenPrefixes] = {
|
|
||||||
{ 1, 2}, { 5, 2}, { 9, 2}, { 13, 2},
|
|
||||||
{ 17, 3}, { 25, 3}, { 33, 3}, { 41, 3},
|
|
||||||
{ 49, 4}, { 65, 4}, { 81, 4}, { 97, 4},
|
|
||||||
{ 113, 5}, { 145, 5}, { 177, 5}, { 209, 5},
|
|
||||||
{ 241, 6}, { 305, 6}, { 369, 7}, { 497, 8},
|
|
||||||
{ 753, 9}, { 1265, 10}, {2289, 11}, {4337, 12},
|
|
||||||
{8433, 13}, {16625, 24}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const PrefixCodeRange kInsertLengthPrefixCode[kNumInsertLenPrefixes] = {
|
|
||||||
{ 0, 0}, { 1, 0}, { 2, 0}, { 3, 0},
|
|
||||||
{ 4, 0}, { 5, 0}, { 6, 1}, { 8, 1},
|
|
||||||
{ 10, 2}, { 14, 2}, { 18, 3}, { 26, 3},
|
|
||||||
{ 34, 4}, { 50, 4}, { 66, 5}, { 98, 5},
|
|
||||||
{ 130, 6}, { 194, 7}, { 322, 8}, { 578, 9},
|
|
||||||
{1090, 10}, {2114, 12}, {6210, 14}, {22594, 24},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const PrefixCodeRange kCopyLengthPrefixCode[kNumCopyLenPrefixes] = {
|
|
||||||
{ 2, 0}, { 3, 0}, { 4, 0}, { 5, 0},
|
|
||||||
{ 6, 0}, { 7, 0}, { 8, 0}, { 9, 0},
|
|
||||||
{ 10, 1}, { 12, 1}, { 14, 2}, { 18, 2},
|
|
||||||
{ 22, 3}, { 30, 3}, { 38, 4}, { 54, 4},
|
|
||||||
{ 70, 5}, { 102, 5}, { 134, 6}, { 198, 7},
|
|
||||||
{326, 8}, { 582, 9}, {1094, 10}, {2118, 24},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int kInsertAndCopyRangeLut[9] = {
|
|
||||||
0, 1, 4, 2, 3, 6, 5, 7, 8,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int kInsertRangeLut[9] = {
|
|
||||||
0, 0, 1, 1, 0, 2, 1, 2, 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int kCopyRangeLut[9] = {
|
|
||||||
0, 1, 0, 1, 2, 0, 2, 1, 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
int InsertLengthPrefix(int length) {
|
|
||||||
for (int i = 0; i < kNumInsertLenPrefixes; ++i) {
|
|
||||||
const PrefixCodeRange& range = kInsertLengthPrefixCode[i];
|
|
||||||
if (length >= range.offset && length < range.offset + (1 << range.nbits)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CopyLengthPrefix(int length) {
|
|
||||||
for (int i = 0; i < kNumCopyLenPrefixes; ++i) {
|
|
||||||
const PrefixCodeRange& range = kCopyLengthPrefixCode[i];
|
|
||||||
if (length >= range.offset && length < range.offset + (1 << range.nbits)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CommandPrefix(int insert_length, int copy_length) {
|
|
||||||
if (copy_length == 0) {
|
|
||||||
copy_length = 4;
|
|
||||||
}
|
|
||||||
int insert_prefix = InsertLengthPrefix(insert_length);
|
|
||||||
int copy_prefix = CopyLengthPrefix(copy_length);
|
|
||||||
int range_idx = 3 * (insert_prefix >> 3) + (copy_prefix >> 3);
|
|
||||||
return ((kInsertAndCopyRangeLut[range_idx] << 6) +
|
|
||||||
((insert_prefix & 7) << 3) + (copy_prefix & 7));
|
|
||||||
}
|
|
||||||
|
|
||||||
int InsertLengthExtraBits(int code) {
|
|
||||||
int insert_code = (kInsertRangeLut[code >> 6] << 3) + ((code >> 3) & 7);
|
|
||||||
return kInsertLengthPrefixCode[insert_code].nbits;
|
|
||||||
}
|
|
||||||
|
|
||||||
int InsertLengthOffset(int code) {
|
|
||||||
int insert_code = (kInsertRangeLut[code >> 6] << 3) + ((code >> 3) & 7);
|
|
||||||
return kInsertLengthPrefixCode[insert_code].offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CopyLengthExtraBits(int code) {
|
|
||||||
int copy_code = (kCopyRangeLut[code >> 6] << 3) + (code & 7);
|
|
||||||
return kCopyLengthPrefixCode[copy_code].nbits;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CopyLengthOffset(int code) {
|
|
||||||
int copy_code = (kCopyRangeLut[code >> 6] << 3) + (code & 7);
|
|
||||||
return kCopyLengthPrefixCode[copy_code].offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrefixEncodeCopyDistance(int distance_code,
|
|
||||||
int num_direct_codes,
|
|
||||||
int postfix_bits,
|
|
||||||
uint16_t* code,
|
|
||||||
int* nbits,
|
|
||||||
uint32_t* extra_bits) {
|
|
||||||
distance_code -= 1;
|
|
||||||
if (distance_code < kNumDistanceShortCodes + num_direct_codes) {
|
|
||||||
*code = distance_code;
|
|
||||||
*nbits = 0;
|
|
||||||
*extra_bits = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
distance_code -= kNumDistanceShortCodes + num_direct_codes;
|
|
||||||
distance_code += (1 << (postfix_bits + 2));
|
|
||||||
int bucket = Log2Floor(distance_code) - 1;
|
|
||||||
int postfix_mask = (1 << postfix_bits) - 1;
|
|
||||||
int postfix = distance_code & postfix_mask;
|
|
||||||
int prefix = (distance_code >> bucket) & 1;
|
|
||||||
int offset = (2 + prefix) << bucket;
|
|
||||||
*nbits = bucket - postfix_bits;
|
|
||||||
*code = kNumDistanceShortCodes + num_direct_codes +
|
|
||||||
((2 * (*nbits - 1) + prefix) << postfix_bits) + postfix;
|
|
||||||
*extra_bits = (distance_code - offset) >> postfix_bits;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BlockLengthPrefix(int length) {
|
|
||||||
for (int i = 0; i < kNumBlockLenPrefixes; ++i) {
|
|
||||||
const PrefixCodeRange& range = kBlockLengthPrefixCode[i];
|
|
||||||
if (length >= range.offset && length < range.offset + (1 << range.nbits)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BlockLengthExtraBits(int length_code) {
|
|
||||||
return kBlockLengthPrefixCode[length_code].nbits;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BlockLengthOffset(int length_code) {
|
|
||||||
return kBlockLengthPrefixCode[length_code].offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Functions for encoding of integers into prefix codes the amount of extra
|
|
||||||
// bits, and the actual values of the extra bits.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_PREFIX_H_
|
|
||||||
#define BROTLI_ENC_PREFIX_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
static const int kNumInsertLenPrefixes = 24;
|
|
||||||
static const int kNumCopyLenPrefixes = 24;
|
|
||||||
static const int kNumCommandPrefixes = 704;
|
|
||||||
static const int kNumBlockLenPrefixes = 26;
|
|
||||||
static const int kNumDistanceShortCodes = 16;
|
|
||||||
static const int kNumDistancePrefixes = 520;
|
|
||||||
|
|
||||||
int CommandPrefix(int insert_length, int copy_length);
|
|
||||||
int InsertLengthExtraBits(int prefix);
|
|
||||||
int InsertLengthOffset(int prefix);
|
|
||||||
int CopyLengthExtraBits(int prefix);
|
|
||||||
int CopyLengthOffset(int prefix);
|
|
||||||
|
|
||||||
void PrefixEncodeCopyDistance(int distance_code,
|
|
||||||
int num_direct_codes,
|
|
||||||
int shift_bits,
|
|
||||||
uint16_t* prefix,
|
|
||||||
int* nbits,
|
|
||||||
uint32_t* extra_bits);
|
|
||||||
|
|
||||||
int BlockLengthPrefix(int length);
|
|
||||||
int BlockLengthExtraBits(int prefix);
|
|
||||||
int BlockLengthOffset(int prefix);
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_PREFIX_H_
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Sliding window over the input data.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_RINGBUFFER_H_
|
|
||||||
#define BROTLI_ENC_RINGBUFFER_H_
|
|
||||||
|
|
||||||
// A RingBuffer(window_bits, tail_bits) contains `1 << window_bits' bytes of
|
|
||||||
// data in a circular manner: writing a byte writes it to
|
|
||||||
// `position() % (1 << window_bits)'. For convenience, the RingBuffer array
|
|
||||||
// contains another copy of the first `1 << tail_bits' bytes:
|
|
||||||
// buffer_[i] == buffer_[i + (1 << window_bits)] if i < (1 << tail_bits).
|
|
||||||
class RingBuffer {
|
|
||||||
public:
|
|
||||||
RingBuffer(int window_bits, int tail_bits)
|
|
||||||
: window_bits_(window_bits), tail_bits_(tail_bits), pos_(0) {
|
|
||||||
static const int kSlackForThreeByteHashingEverywhere = 2;
|
|
||||||
const int buflen = (1 << window_bits_) + (1 << tail_bits_);
|
|
||||||
buffer_ = new uint8_t[buflen + kSlackForThreeByteHashingEverywhere];
|
|
||||||
for (int i = 0; i < kSlackForThreeByteHashingEverywhere; ++i) {
|
|
||||||
buffer_[buflen + i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~RingBuffer() {
|
|
||||||
delete [] buffer_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push bytes into the ring buffer.
|
|
||||||
void Write(const uint8_t *bytes, size_t n) {
|
|
||||||
const size_t masked_pos = pos_ & ((1 << window_bits_) - 1);
|
|
||||||
// The length of the writes is limited so that we do not need to worry
|
|
||||||
// about a write
|
|
||||||
WriteTail(bytes, n);
|
|
||||||
if (masked_pos + n <= (1 << window_bits_)) {
|
|
||||||
// A single write fits.
|
|
||||||
memcpy(&buffer_[masked_pos], bytes, n);
|
|
||||||
} else {
|
|
||||||
// Split into two writes.
|
|
||||||
// Copy into the end of the buffer, including the tail buffer.
|
|
||||||
memcpy(&buffer_[masked_pos], bytes,
|
|
||||||
std::min(n,
|
|
||||||
((1 << window_bits_) + (1 << tail_bits_)) - masked_pos));
|
|
||||||
// Copy into the begining of the buffer
|
|
||||||
memcpy(&buffer_[0], bytes + ((1 << window_bits_) - masked_pos),
|
|
||||||
n - ((1 << window_bits_) - masked_pos));
|
|
||||||
}
|
|
||||||
pos_ += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logical cursor position in the ring buffer.
|
|
||||||
size_t position() const { return pos_; }
|
|
||||||
|
|
||||||
uint8_t *start() { return &buffer_[0]; }
|
|
||||||
const uint8_t *start() const { return &buffer_[0]; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void WriteTail(const uint8_t *bytes, size_t n) {
|
|
||||||
const size_t masked_pos = pos_ & ((1 << window_bits_) - 1);
|
|
||||||
if (masked_pos < (1 << tail_bits_)) {
|
|
||||||
// Just fill the tail buffer with the beginning data.
|
|
||||||
const size_t p = (1 << window_bits_) + masked_pos;
|
|
||||||
memcpy(&buffer_[p], bytes, std::min(n, (1 << tail_bits_) - masked_pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size of the ringbuffer is (1 << window_bits) + (1 << tail_bits).
|
|
||||||
const int window_bits_;
|
|
||||||
const int tail_bits_;
|
|
||||||
|
|
||||||
// Position to write in the ring buffer.
|
|
||||||
size_t pos_;
|
|
||||||
// The actual ring buffer containing the data and the copy of the beginning
|
|
||||||
// as a tail.
|
|
||||||
uint8_t *buffer_;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_RINGBUFFER_H_
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Class to model the static dictionary.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_STATIC_DICT_H_
|
|
||||||
#define BROTLI_ENC_STATIC_DICT_H_
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
class StaticDictionary {
|
|
||||||
public:
|
|
||||||
StaticDictionary() {}
|
|
||||||
void Insert(const std::string &str, int len, int dist) {
|
|
||||||
int ix = (dist << 6) + len;
|
|
||||||
std::unordered_map<std::string, int>::const_iterator it = map_.find(str);
|
|
||||||
if (it != map_.end() && ix >= it->second) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
map_[str] = ix;
|
|
||||||
int v = 0;
|
|
||||||
for (int i = 0; i < 4 && i < str.size(); ++i) {
|
|
||||||
v += str[i] << (8 * i);
|
|
||||||
}
|
|
||||||
if (prefix_map_[v] < str.size()) {
|
|
||||||
prefix_map_[v] = str.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int GetLength(int v) const {
|
|
||||||
std::unordered_map<int, int>::const_iterator it = prefix_map_.find(v);
|
|
||||||
if (it == prefix_map_.end()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
bool Get(const std::string &str, int *len, int *dist) const {
|
|
||||||
std::unordered_map<std::string, int>::const_iterator it = map_.find(str);
|
|
||||||
if (it == map_.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int v = it->second;
|
|
||||||
*len = v & 63;
|
|
||||||
*dist = v >> 6;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
std::unordered_map<std::string, int> map_;
|
|
||||||
std::unordered_map<int, int> prefix_map_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_STATIC_DICT_H_
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Transformations on dictionary words.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_TRANSFORM_H_
|
|
||||||
#define BROTLI_ENC_TRANSFORM_H_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "./dictionary.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
enum WordTransformType {
|
|
||||||
kIdentity = 0,
|
|
||||||
kOmitLast1 = 1,
|
|
||||||
kOmitLast2 = 2,
|
|
||||||
kOmitLast3 = 3,
|
|
||||||
kOmitLast4 = 4,
|
|
||||||
kOmitLast5 = 5,
|
|
||||||
kOmitLast6 = 6,
|
|
||||||
kOmitLast7 = 7,
|
|
||||||
kOmitLast8 = 8,
|
|
||||||
kOmitLast9 = 9,
|
|
||||||
kUppercaseFirst = 10,
|
|
||||||
kUppercaseAll = 11,
|
|
||||||
kOmitFirst1 = 12,
|
|
||||||
kOmitFirst2 = 13,
|
|
||||||
kOmitFirst3 = 14,
|
|
||||||
kOmitFirst4 = 15,
|
|
||||||
kOmitFirst5 = 16,
|
|
||||||
kOmitFirst6 = 17,
|
|
||||||
kOmitFirst7 = 18,
|
|
||||||
kOmitFirst8 = 19,
|
|
||||||
kOmitFirst9 = 20,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Transform {
|
|
||||||
const char* prefix;
|
|
||||||
WordTransformType word_transform;
|
|
||||||
const char* suffix;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const Transform kTransforms[] = {
|
|
||||||
{ "", kIdentity, "" },
|
|
||||||
{ "", kIdentity, " " },
|
|
||||||
{ " ", kIdentity, " " },
|
|
||||||
{ "", kOmitFirst1, "" },
|
|
||||||
{ "", kUppercaseFirst, " " },
|
|
||||||
{ "", kIdentity, " the " },
|
|
||||||
{ " ", kIdentity, "" },
|
|
||||||
{ "s ", kIdentity, " " },
|
|
||||||
{ "", kIdentity, " of " },
|
|
||||||
{ "", kUppercaseFirst, "" },
|
|
||||||
{ "", kIdentity, " and " },
|
|
||||||
{ "", kOmitFirst2, "" },
|
|
||||||
{ "", kOmitLast1, "" },
|
|
||||||
{ ", ", kIdentity, " " },
|
|
||||||
{ "", kIdentity, ", " },
|
|
||||||
{ " ", kUppercaseFirst, " " },
|
|
||||||
{ "", kIdentity, " in " },
|
|
||||||
{ "", kIdentity, " to " },
|
|
||||||
{ "e ", kIdentity, " " },
|
|
||||||
{ "", kIdentity, "\"" },
|
|
||||||
{ "", kIdentity, "." },
|
|
||||||
{ "", kIdentity, "\">" },
|
|
||||||
{ "", kIdentity, "\n" },
|
|
||||||
{ "", kOmitLast3, "" },
|
|
||||||
{ "", kIdentity, "]" },
|
|
||||||
{ "", kIdentity, " for " },
|
|
||||||
{ "", kOmitFirst3, "" },
|
|
||||||
{ "", kOmitLast2, "" },
|
|
||||||
{ "", kIdentity, " a " },
|
|
||||||
{ "", kIdentity, " that " },
|
|
||||||
{ " ", kUppercaseFirst, "" },
|
|
||||||
{ "", kIdentity, ". " },
|
|
||||||
{ ".", kIdentity, "" },
|
|
||||||
{ " ", kIdentity, ", " },
|
|
||||||
{ "", kOmitFirst4, "" },
|
|
||||||
{ "", kIdentity, " with " },
|
|
||||||
{ "", kIdentity, "'" },
|
|
||||||
{ "", kIdentity, " from " },
|
|
||||||
{ "", kIdentity, " by " },
|
|
||||||
{ "", kOmitFirst5, "" },
|
|
||||||
{ "", kOmitFirst6, "" },
|
|
||||||
{ " the ", kIdentity, "" },
|
|
||||||
{ "", kOmitLast4, "" },
|
|
||||||
{ "", kIdentity, ". The " },
|
|
||||||
{ "", kUppercaseAll, "" },
|
|
||||||
{ "", kIdentity, " on " },
|
|
||||||
{ "", kIdentity, " as " },
|
|
||||||
{ "", kIdentity, " is " },
|
|
||||||
{ "", kOmitLast7, "" },
|
|
||||||
{ "", kOmitLast1, "ing " },
|
|
||||||
{ "", kIdentity, "\n\t" },
|
|
||||||
{ "", kIdentity, ":" },
|
|
||||||
{ " ", kIdentity, ". " },
|
|
||||||
{ "", kIdentity, "ed " },
|
|
||||||
{ "", kOmitFirst9, "" },
|
|
||||||
{ "", kOmitFirst7, "" },
|
|
||||||
{ "", kOmitLast6, "" },
|
|
||||||
{ "", kIdentity, "(" },
|
|
||||||
{ "", kUppercaseFirst, ", " },
|
|
||||||
{ "", kOmitLast8, "" },
|
|
||||||
{ "", kIdentity, " at " },
|
|
||||||
{ "", kIdentity, "ly " },
|
|
||||||
{ " the ", kIdentity, " of " },
|
|
||||||
{ "", kOmitLast5, "" },
|
|
||||||
{ "", kOmitLast9, "" },
|
|
||||||
{ " ", kUppercaseFirst, ", " },
|
|
||||||
{ "", kUppercaseFirst, "\"" },
|
|
||||||
{ ".", kIdentity, "(" },
|
|
||||||
{ "", kUppercaseAll, " " },
|
|
||||||
{ "", kUppercaseFirst, "\">" },
|
|
||||||
{ "", kIdentity, "=\"" },
|
|
||||||
{ " ", kIdentity, "." },
|
|
||||||
{ ".com/", kIdentity, "" },
|
|
||||||
{ " the ", kIdentity, " of the " },
|
|
||||||
{ "", kUppercaseFirst, "'" },
|
|
||||||
{ "", kIdentity, ". This " },
|
|
||||||
{ "", kIdentity, "," },
|
|
||||||
{ ".", kIdentity, " " },
|
|
||||||
{ "", kUppercaseFirst, "(" },
|
|
||||||
{ "", kUppercaseFirst, "." },
|
|
||||||
{ "", kIdentity, " not " },
|
|
||||||
{ " ", kIdentity, "=\"" },
|
|
||||||
{ "", kIdentity, "er " },
|
|
||||||
{ " ", kUppercaseAll, " " },
|
|
||||||
{ "", kIdentity, "al " },
|
|
||||||
{ " ", kUppercaseAll, "" },
|
|
||||||
{ "", kIdentity, "='" },
|
|
||||||
{ "", kUppercaseAll, "\"" },
|
|
||||||
{ "", kUppercaseFirst, ". " },
|
|
||||||
{ " ", kIdentity, "(" },
|
|
||||||
{ "", kIdentity, "ful " },
|
|
||||||
{ " ", kUppercaseFirst, ". " },
|
|
||||||
{ "", kIdentity, "ive " },
|
|
||||||
{ "", kIdentity, "less " },
|
|
||||||
{ "", kUppercaseAll, "'" },
|
|
||||||
{ "", kIdentity, "est " },
|
|
||||||
{ " ", kUppercaseFirst, "." },
|
|
||||||
{ "", kUppercaseAll, "\">" },
|
|
||||||
{ " ", kIdentity, "='" },
|
|
||||||
{ "", kUppercaseFirst, "," },
|
|
||||||
{ "", kIdentity, "ize " },
|
|
||||||
{ "", kUppercaseAll, "." },
|
|
||||||
{ "\xc2\xa0", kIdentity, "" },
|
|
||||||
{ " ", kIdentity, "," },
|
|
||||||
{ "", kUppercaseFirst, "=\"" },
|
|
||||||
{ "", kUppercaseAll, "=\"" },
|
|
||||||
{ "", kIdentity, "ous " },
|
|
||||||
{ "", kUppercaseAll, ", " },
|
|
||||||
{ "", kUppercaseFirst, "='" },
|
|
||||||
{ " ", kUppercaseFirst, "," },
|
|
||||||
{ " ", kUppercaseAll, "=\"" },
|
|
||||||
{ " ", kUppercaseAll, ", " },
|
|
||||||
{ "", kUppercaseAll, "," },
|
|
||||||
{ "", kUppercaseAll, "(" },
|
|
||||||
{ "", kUppercaseAll, ". " },
|
|
||||||
{ " ", kUppercaseAll, "." },
|
|
||||||
{ "", kUppercaseAll, "='" },
|
|
||||||
{ " ", kUppercaseAll, ". " },
|
|
||||||
{ " ", kUppercaseFirst, "=\"" },
|
|
||||||
{ " ", kUppercaseAll, "='" },
|
|
||||||
{ " ", kUppercaseFirst, "='" },
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int kNumTransforms = sizeof(kTransforms) / sizeof(kTransforms[0]);
|
|
||||||
|
|
||||||
static int ToUpperCase(uint8_t *p, int len) {
|
|
||||||
if (len == 1 || p[0] < 0xc0) {
|
|
||||||
if (p[0] >= 'a' && p[0] <= 'z') {
|
|
||||||
p[0] ^= 32;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (p[0] < 0xe0) {
|
|
||||||
p[1] ^= 32;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
if (len == 2) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
p[2] ^= 5;
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string ApplyTransform(
|
|
||||||
const Transform& t, const uint8_t* word, int len) {
|
|
||||||
std::string ret(t.prefix);
|
|
||||||
if (t.word_transform <= kOmitLast9) {
|
|
||||||
len -= t.word_transform;
|
|
||||||
}
|
|
||||||
if (len > 0) {
|
|
||||||
if (t.word_transform >= kOmitFirst1) {
|
|
||||||
const int skip = t.word_transform - (kOmitFirst1 - 1);
|
|
||||||
if (len > skip) {
|
|
||||||
ret += std::string(word + skip, word + len);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret += std::string(word, word + len);
|
|
||||||
uint8_t *uppercase = reinterpret_cast<uint8_t*>(&ret[ret.size() - len]);
|
|
||||||
if (t.word_transform == kUppercaseFirst) {
|
|
||||||
ToUpperCase(uppercase, len);
|
|
||||||
} else if (t.word_transform == kUppercaseAll) {
|
|
||||||
while (len > 0) {
|
|
||||||
int step = ToUpperCase(uppercase, len);
|
|
||||||
uppercase += step;
|
|
||||||
len -= step;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret += std::string(t.suffix);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string GetTransformedDictionaryWord(int len_code, int word_id) {
|
|
||||||
int num_words = 1 << kBrotliDictionarySizeBitsByLength[len_code];
|
|
||||||
int offset = kBrotliDictionaryOffsetsByLength[len_code];
|
|
||||||
int t = word_id / num_words;
|
|
||||||
int word_idx = word_id % num_words;
|
|
||||||
offset += len_code * word_idx;
|
|
||||||
const uint8_t* word = &kBrotliDictionary[offset];
|
|
||||||
return ApplyTransform(kTransforms[t], word, len_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_TRANSFORM_H_
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Write bits into a byte array.
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_WRITE_BITS_H_
|
|
||||||
#define BROTLI_ENC_WRITE_BITS_H_
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#if defined(OS_MACOSX)
|
|
||||||
#include <machine/endian.h>
|
|
||||||
#else
|
|
||||||
#include <endian.h>
|
|
||||||
#endif
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "./port.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
//#define BIT_WRITER_DEBUG
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
inline void WriteBits(int n_bits,
|
|
||||||
uint64_t bits,
|
|
||||||
int * __restrict pos,
|
|
||||||
uint8_t * __restrict array) {
|
|
||||||
#ifdef BIT_WRITER_DEBUG
|
|
||||||
printf("WriteBits %2d 0x%016llx %10d\n", n_bits, bits, *pos);
|
|
||||||
#endif
|
|
||||||
#ifdef IS_LITTLE_ENDIAN
|
|
||||||
// This branch of the code can write up to 56 bits at a time,
|
|
||||||
// 7 bits are lost by being perhaps already in *p and at least
|
|
||||||
// 1 bit is needed to initialize the bit-stream ahead (i.e. if 7
|
|
||||||
// bits are in *p and we write 57 bits, then the next write will
|
|
||||||
// access a byte that was never initialized).
|
|
||||||
uint8_t *p = &array[*pos >> 3];
|
|
||||||
uint64_t v = *p;
|
|
||||||
v |= bits << (*pos & 7);
|
|
||||||
BROTLI_UNALIGNED_STORE64(p, v); // Set some bits.
|
|
||||||
*pos += n_bits;
|
|
||||||
#else
|
|
||||||
// implicit & 0xff is assumed for uint8_t arithmetics
|
|
||||||
uint8_t *array_pos = &array[*pos >> 3];
|
|
||||||
const int bits_reserved_in_first_byte = (*pos & 7);
|
|
||||||
bits <<= bits_reserved_in_first_byte;
|
|
||||||
*array_pos++ |= bits;
|
|
||||||
for (int bits_left_to_write = n_bits - 8 + bits_reserved_in_first_byte;
|
|
||||||
bits_left_to_write >= 1;
|
|
||||||
bits_left_to_write -= 8) {
|
|
||||||
bits >>= 8;
|
|
||||||
*array_pos++ = bits;
|
|
||||||
}
|
|
||||||
*array_pos = 0;
|
|
||||||
*pos += n_bits;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void WriteBitsPrepareStorage(int pos, uint8_t *array) {
|
|
||||||
#ifdef BIT_WRITER_DEBUG
|
|
||||||
printf("WriteBitsPrepareStorage %10d\n", pos);
|
|
||||||
#endif
|
|
||||||
assert((pos & 7) == 0);
|
|
||||||
array[pos >> 3] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace brotli
|
|
||||||
|
|
||||||
#endif // BROTLI_ENC_WRITE_BITS_H_
|
|
||||||
Reference in New Issue
Block a user