├── .gitignore ├── README.md ├── build.sh ├── build_dynamic_bundle.sh ├── compress ├── LICENSE ├── README.md ├── compressible.go ├── fse │ ├── bitreader.go │ ├── bitwriter.go │ ├── bytereader.go │ ├── compress.go │ ├── decompress.go │ └── fse.go ├── go.mod ├── huff0 │ ├── bitreader.go │ ├── bitwriter.go │ ├── compress.go │ ├── decompress.go │ ├── decompress_amd64.go │ ├── decompress_amd64.s │ ├── decompress_generic.go │ └── huff0.go ├── internal │ ├── cpuinfo │ │ ├── cpuinfo.go │ │ ├── cpuinfo_amd64.go │ │ └── cpuinfo_amd64.s │ ├── fuzz │ │ └── helpers.go │ ├── godebug │ │ └── godebug.go │ ├── le │ │ ├── le.go │ │ ├── unsafe_disabled.go │ │ └── unsafe_enabled.go │ ├── lz4ref │ │ ├── LICENSE │ │ ├── block.go │ │ └── errors.go │ ├── race │ │ ├── norace.go │ │ └── race.go │ └── snapref │ │ ├── LICENSE │ │ ├── decode.go │ │ ├── decode_other.go │ │ ├── encode.go │ │ ├── encode_other.go │ │ └── snappy.go └── zstd │ ├── bitreader.go │ ├── bitwriter.go │ ├── blockdec.go │ ├── blockenc.go │ ├── blocktype_string.go │ ├── bytebuf.go │ ├── bytereader.go │ ├── decodeheader.go │ ├── decoder.go │ ├── decoder_options.go │ ├── dict.go │ ├── enc_base.go │ ├── enc_best.go │ ├── enc_better.go │ ├── enc_dfast.go │ ├── enc_fast.go │ ├── encoder.go │ ├── encoder_options.go │ ├── framedec.go │ ├── frameenc.go │ ├── fse_decoder.go │ ├── fse_decoder_amd64.go │ ├── fse_decoder_amd64.s │ ├── fse_decoder_generic.go │ ├── fse_encoder.go │ ├── fse_predefined.go │ ├── hash.go │ ├── history.go │ ├── internal │ └── xxhash │ │ ├── LICENSE.txt │ │ ├── xxhash.go │ │ ├── xxhash_amd64.s │ │ ├── xxhash_arm64.s │ │ ├── xxhash_asm.go │ │ ├── xxhash_other.go │ │ └── xxhash_safe.go │ ├── matchlen_amd64.go │ ├── matchlen_amd64.s │ ├── matchlen_generic.go │ ├── seqdec.go │ ├── seqdec_amd64.go │ ├── seqdec_amd64.s │ ├── seqdec_generic.go │ ├── seqenc.go │ ├── snappy.go │ ├── zip.go │ └── zstd.go ├── go.mod └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | run 2 | res.tar.zst 3 | .tmp_run_dir_*/ 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | This project presents a simple way to run executables on Android. 4 | 5 | This is mainly meant to be used in automation apps like Tasker, where we would want to run handy commands like `jq`, `ffmpeg`, etc, many others, natively in Tasker. 6 | 7 |   8 | 9 | For this, we source the executables from Termux and combine all of them into one standalone executable. 10 | 11 | Open Termux if already installed, otherwise get it from [F-Droid - Termux](https://f-droid.org/repo/com.termux_1020.apk). 12 | 13 | yes | pkg up; pkg i -y git 14 | 15 | To build your own flavor - 16 | 17 | git clone --depth 1 https://github.com/HunterXProgrammer/run-android-executable ~/run-android-executable; git -C ~/run-android-executable pull 18 | 19 |   20 | 21 | cd ~/run-android-executable 22 | 23 |   24 | 25 | ./build.sh jq ffmpeg 26 | 27 | (After `./build.sh`, put space seperated list of executables to be packed) 28 | 29 | This will generate the standalone executable named `run`. 30 | 31 | You can close Termux now. Tap exit from notification. 32 | 33 |   34 | 35 | Open Tasker to create a Task and select action [Run Shell]. Use this command to add the executable - 36 | 37 | ``` 38 | cp -f /sdcard/run /data/data/net.dinglisch.android.taskerm/files 39 | ``` 40 | 41 | Only need to run this command once every time you build a new flavor. 42 | 43 |   44 | 45 | After that, you can make `alias` to keep it short in a [Run Shell] action - 46 | 47 | ``` 48 | alias jq="/system/bin/linker$(uname -m | grep -o 64) /data/data/net.dinglisch.android.taskerm/files/run jq" 49 | 50 | alias ffmpeg="/system/bin/linker$(uname -m | grep -o 64) /data/data/net.dinglisch.android.taskerm/files/run ffmpeg" 51 | 52 | ffmpeg --help 53 | 54 | echo '{"Hi":"Hello"}' | jq .Hi 55 | ``` 56 | 57 | Similarly, you can add whatever command you selected when building your own flavor of `run`. 58 | 59 |   60 | 61 | If you want to try the long way, you can write the entire path every time. Both works, but using `alias` to keep it short as seen above is much easier. If you still want to try - 62 | 63 | `/system/bin/linker64 /data/data/net.dinglisch.android.taskerm/files/run jq --help` 64 | 65 | Or 66 | 67 | `/system/bin/linker64 /data/data/net.dinglisch.android.taskerm/files/run ffmpeg --help` 68 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd "$(dirname "${BASH_SOURCE[0]}")" 6 | 7 | clear 2>/dev/null || true 8 | 9 | CURRENT_DIR="$(pwd)" 10 | 11 | if [ -n "$TERMUX_VERSION" ]; then 12 | if [ -z "$1" ]; then 13 | echo "No package specified" 14 | exit 1 15 | fi 16 | yes | termux-setup-storage &>/dev/null 17 | apt update 18 | else 19 | echo "This script should run on Termux" 20 | exit 1 21 | fi 22 | 23 | yes | pkg install -y golang binutils termux-elf-cleaner 24 | 25 | TMP_DIR="$(mktemp -d)" 26 | 27 | cleanup(){ 28 | _EXIT_CODE=$? 29 | rm -rf "$TMP_DIR" 30 | return $_EXIT_CODE 31 | } 32 | 33 | trap cleanup EXIT 34 | 35 | cd "$TMP_DIR" 36 | 37 | cp -rf "$CURRENT_DIR" src 38 | 39 | cd src 40 | 41 | ./build_dynamic_bundle.sh "$@" 42 | 43 | for cmd in $@; do 44 | echo "$(basename "$cmd")," >> available_commands.txt.tmp 45 | done 46 | 47 | sort available_commands.txt.tmp | uniq | tr '\n' ' ' | sed -E 's|, $||' > available_commands.txt 48 | 49 | go mod tidy 50 | 51 | go build -buildvcs=false -ldflags "-s -w -buildid=" -o run . 52 | 53 | termux-elf-cleaner run &>/dev/null 54 | 55 | strip -s -w run 56 | 57 | rm -rf "$CURRENT_DIR/run" 58 | 59 | mv run "$CURRENT_DIR" 60 | 61 | cp -f "$CURRENT_DIR/run" /sdcard/run 62 | -------------------------------------------------------------------------------- /build_dynamic_bundle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | #cd "$(dirname "${BASH_SOURCE[0]}")" 6 | 7 | clear 2>/dev/null || true 8 | 9 | CURRENT_DIR="$(pwd)" 10 | 11 | TMP_DIR="$(mktemp -d)" 12 | 13 | if [ -n "$TERMUX_VERSION" ]; then 14 | apt update 15 | else 16 | echo "This script should run on Termux" 17 | exit 1 18 | fi 19 | 20 | yes | pkg install -y ldd binutils command-not-found tur-repo root-repo x11-repo 21 | 22 | cleanup(){ 23 | _EXIT_CODE=$? 24 | rm -rf "$TMP_DIR" 25 | return $_EXIT_CODE 26 | } 27 | 28 | checkIsDynamic(){ 29 | if readelf -d "$1" 2>/dev/null | grep -q "Dynamic section at"; then 30 | echo "$1" 31 | fi 32 | } 33 | 34 | trap cleanup EXIT 35 | 36 | cd "$TMP_DIR" 37 | 38 | if [ -n "$*" ]; then 39 | package_list=("$@") 40 | else 41 | echo "Error, no package specified" 42 | exit 1 43 | fi 44 | 45 | getPkgPath(){ 46 | unset package 47 | package="$1" 48 | if [[ "$package" == */* ]]; then 49 | checkIsDynamic "$(cd "$CURRENT_DIR"; readlink -f "$package")" 50 | else 51 | if ! command -v "$package" &>/dev/null; then 52 | pkg_install="$("$PREFIX/libexec/termux/command-not-found" "$package" 2>&1 | grep "pkg install" | head -n 1 | sed "s/.* //g")" 53 | if [ -n "$pkg_install" ]; then 54 | echo "" >&2 55 | yes | pkg install -y "$pkg_install" >&2 56 | fi 57 | fi 58 | checkIsDynamic "$(command -v "$package")" 59 | fi 60 | } 61 | 62 | for package in "${package_list[@]}"; do 63 | echo "" 64 | echo " Selected package ${package}..." 65 | echo "" 66 | unset pkgPath 67 | pkgPath="$(getPkgPath "$package")" 68 | if [ -z "$pkgPath" ]; then 69 | echo -e "\n Package $package not valid. Skipping..." 70 | else 71 | pkgBasename="$(basename "$pkgPath")" 72 | mkdir -p bin/lib 73 | cp -L "$pkgPath" "bin/$pkgBasename" 74 | echo -e "\n Getting dependencies..." 75 | echo "" 76 | ldd "$pkgPath" | grep --line-buffered -F "/data/data/com.termux/" | sed --unbuffered "s/.* \//\//" | sed --unbuffered "s/ .*//" | tee shared_libs.txt 77 | for libpath in $(cat shared_libs.txt); do 78 | cp -L "$libpath" bin/lib &>/dev/null 79 | done 80 | fi 81 | done 82 | 83 | find bin -type d -exec chmod 755 "{}" \; 84 | find bin -type f -exec chmod 700 "{}" \; 85 | 86 | echo -e "\n Compressing packages..." 87 | rm -rf res.tar.gz 88 | tar -I "zstd -10" -cf res.tar.zst bin 89 | echo -e "\n Done\n\n-------\n" 90 | 91 | rm -rf "$CURRENT_DIR/res.tar.zst" 92 | mv res.tar.zst "$CURRENT_DIR" 93 | -------------------------------------------------------------------------------- /compress/README.md: -------------------------------------------------------------------------------- 1 | https://github.com/klauspost/compress -------------------------------------------------------------------------------- /compress/compressible.go: -------------------------------------------------------------------------------- 1 | package compress 2 | 3 | import "math" 4 | 5 | // Estimate returns a normalized compressibility estimate of block b. 6 | // Values close to zero are likely uncompressible. 7 | // Values above 0.1 are likely to be compressible. 8 | // Values above 0.5 are very compressible. 9 | // Very small lengths will return 0. 10 | func Estimate(b []byte) float64 { 11 | if len(b) < 16 { 12 | return 0 13 | } 14 | 15 | // Correctly predicted order 1 16 | hits := 0 17 | lastMatch := false 18 | var o1 [256]byte 19 | var hist [256]int 20 | c1 := byte(0) 21 | for _, c := range b { 22 | if c == o1[c1] { 23 | // We only count a hit if there was two correct predictions in a row. 24 | if lastMatch { 25 | hits++ 26 | } 27 | lastMatch = true 28 | } else { 29 | lastMatch = false 30 | } 31 | o1[c1] = c 32 | c1 = c 33 | hist[c]++ 34 | } 35 | 36 | // Use x^0.6 to give better spread 37 | prediction := math.Pow(float64(hits)/float64(len(b)), 0.6) 38 | 39 | // Calculate histogram distribution 40 | variance := float64(0) 41 | avg := float64(len(b)) / 256 42 | 43 | for _, v := range hist { 44 | Δ := float64(v) - avg 45 | variance += Δ * Δ 46 | } 47 | 48 | stddev := math.Sqrt(float64(variance)) / float64(len(b)) 49 | exp := math.Sqrt(1 / float64(len(b))) 50 | 51 | // Subtract expected stddev 52 | stddev -= exp 53 | if stddev < 0 { 54 | stddev = 0 55 | } 56 | stddev *= 1 + exp 57 | 58 | // Use x^0.4 to give better spread 59 | entropy := math.Pow(stddev, 0.4) 60 | 61 | // 50/50 weight between prediction and histogram distribution 62 | return math.Pow((prediction+entropy)/2, 0.9) 63 | } 64 | 65 | // ShannonEntropyBits returns the number of bits minimum required to represent 66 | // an entropy encoding of the input bytes. 67 | // https://en.wiktionary.org/wiki/Shannon_entropy 68 | func ShannonEntropyBits(b []byte) int { 69 | if len(b) == 0 { 70 | return 0 71 | } 72 | var hist [256]int 73 | for _, c := range b { 74 | hist[c]++ 75 | } 76 | shannon := float64(0) 77 | invTotal := 1.0 / float64(len(b)) 78 | for _, v := range hist[:] { 79 | if v > 0 { 80 | n := float64(v) 81 | shannon += math.Ceil(-math.Log2(n*invTotal) * n) 82 | } 83 | } 84 | return int(math.Ceil(shannon)) 85 | } 86 | -------------------------------------------------------------------------------- /compress/fse/bitreader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Klaus Post. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. 5 | 6 | package fse 7 | 8 | import ( 9 | "encoding/binary" 10 | "errors" 11 | "io" 12 | ) 13 | 14 | // bitReader reads a bitstream in reverse. 15 | // The last set bit indicates the start of the stream and is used 16 | // for aligning the input. 17 | type bitReader struct { 18 | in []byte 19 | off uint // next byte to read is at in[off - 1] 20 | value uint64 21 | bitsRead uint8 22 | } 23 | 24 | // init initializes and resets the bit reader. 25 | func (b *bitReader) init(in []byte) error { 26 | if len(in) < 1 { 27 | return errors.New("corrupt stream: too short") 28 | } 29 | b.in = in 30 | b.off = uint(len(in)) 31 | // The highest bit of the last byte indicates where to start 32 | v := in[len(in)-1] 33 | if v == 0 { 34 | return errors.New("corrupt stream, did not find end of stream") 35 | } 36 | b.bitsRead = 64 37 | b.value = 0 38 | if len(in) >= 8 { 39 | b.fillFastStart() 40 | } else { 41 | b.fill() 42 | b.fill() 43 | } 44 | b.bitsRead += 8 - uint8(highBits(uint32(v))) 45 | return nil 46 | } 47 | 48 | // getBits will return n bits. n can be 0. 49 | func (b *bitReader) getBits(n uint8) uint16 { 50 | if n == 0 || b.bitsRead >= 64 { 51 | return 0 52 | } 53 | return b.getBitsFast(n) 54 | } 55 | 56 | // getBitsFast requires that at least one bit is requested every time. 57 | // There are no checks if the buffer is filled. 58 | func (b *bitReader) getBitsFast(n uint8) uint16 { 59 | const regMask = 64 - 1 60 | v := uint16((b.value << (b.bitsRead & regMask)) >> ((regMask + 1 - n) & regMask)) 61 | b.bitsRead += n 62 | return v 63 | } 64 | 65 | // fillFast() will make sure at least 32 bits are available. 66 | // There must be at least 4 bytes available. 67 | func (b *bitReader) fillFast() { 68 | if b.bitsRead < 32 { 69 | return 70 | } 71 | // 2 bounds checks. 72 | v := b.in[b.off-4:] 73 | v = v[:4] 74 | low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) 75 | b.value = (b.value << 32) | uint64(low) 76 | b.bitsRead -= 32 77 | b.off -= 4 78 | } 79 | 80 | // fill() will make sure at least 32 bits are available. 81 | func (b *bitReader) fill() { 82 | if b.bitsRead < 32 { 83 | return 84 | } 85 | if b.off > 4 { 86 | v := b.in[b.off-4:] 87 | v = v[:4] 88 | low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) 89 | b.value = (b.value << 32) | uint64(low) 90 | b.bitsRead -= 32 91 | b.off -= 4 92 | return 93 | } 94 | for b.off > 0 { 95 | b.value = (b.value << 8) | uint64(b.in[b.off-1]) 96 | b.bitsRead -= 8 97 | b.off-- 98 | } 99 | } 100 | 101 | // fillFastStart() assumes the bitreader is empty and there is at least 8 bytes to read. 102 | func (b *bitReader) fillFastStart() { 103 | // Do single re-slice to avoid bounds checks. 104 | b.value = binary.LittleEndian.Uint64(b.in[b.off-8:]) 105 | b.bitsRead = 0 106 | b.off -= 8 107 | } 108 | 109 | // finished returns true if all bits have been read from the bit stream. 110 | func (b *bitReader) finished() bool { 111 | return b.bitsRead >= 64 && b.off == 0 112 | } 113 | 114 | // close the bitstream and returns an error if out-of-buffer reads occurred. 115 | func (b *bitReader) close() error { 116 | // Release reference. 117 | b.in = nil 118 | if b.bitsRead > 64 { 119 | return io.ErrUnexpectedEOF 120 | } 121 | return nil 122 | } 123 | -------------------------------------------------------------------------------- /compress/fse/bitwriter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Klaus Post. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. 5 | 6 | package fse 7 | 8 | import "fmt" 9 | 10 | // bitWriter will write bits. 11 | // First bit will be LSB of the first byte of output. 12 | type bitWriter struct { 13 | bitContainer uint64 14 | nBits uint8 15 | out []byte 16 | } 17 | 18 | // bitMask16 is bitmasks. Has extra to avoid bounds check. 19 | var bitMask16 = [32]uint16{ 20 | 0, 1, 3, 7, 0xF, 0x1F, 21 | 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 22 | 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0xFFFF, 23 | 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 24 | 0xFFFF, 0xFFFF} /* up to 16 bits */ 25 | 26 | // addBits16NC will add up to 16 bits. 27 | // It will not check if there is space for them, 28 | // so the caller must ensure that it has flushed recently. 29 | func (b *bitWriter) addBits16NC(value uint16, bits uint8) { 30 | b.bitContainer |= uint64(value&bitMask16[bits&31]) << (b.nBits & 63) 31 | b.nBits += bits 32 | } 33 | 34 | // addBits16Clean will add up to 16 bits. value may not contain more set bits than indicated. 35 | // It will not check if there is space for them, so the caller must ensure that it has flushed recently. 36 | func (b *bitWriter) addBits16Clean(value uint16, bits uint8) { 37 | b.bitContainer |= uint64(value) << (b.nBits & 63) 38 | b.nBits += bits 39 | } 40 | 41 | // addBits16ZeroNC will add up to 16 bits. 42 | // It will not check if there is space for them, 43 | // so the caller must ensure that it has flushed recently. 44 | // This is fastest if bits can be zero. 45 | func (b *bitWriter) addBits16ZeroNC(value uint16, bits uint8) { 46 | if bits == 0 { 47 | return 48 | } 49 | value <<= (16 - bits) & 15 50 | value >>= (16 - bits) & 15 51 | b.bitContainer |= uint64(value) << (b.nBits & 63) 52 | b.nBits += bits 53 | } 54 | 55 | // flush will flush all pending full bytes. 56 | // There will be at least 56 bits available for writing when this has been called. 57 | // Using flush32 is faster, but leaves less space for writing. 58 | func (b *bitWriter) flush() { 59 | v := b.nBits >> 3 60 | switch v { 61 | case 0: 62 | case 1: 63 | b.out = append(b.out, 64 | byte(b.bitContainer), 65 | ) 66 | case 2: 67 | b.out = append(b.out, 68 | byte(b.bitContainer), 69 | byte(b.bitContainer>>8), 70 | ) 71 | case 3: 72 | b.out = append(b.out, 73 | byte(b.bitContainer), 74 | byte(b.bitContainer>>8), 75 | byte(b.bitContainer>>16), 76 | ) 77 | case 4: 78 | b.out = append(b.out, 79 | byte(b.bitContainer), 80 | byte(b.bitContainer>>8), 81 | byte(b.bitContainer>>16), 82 | byte(b.bitContainer>>24), 83 | ) 84 | case 5: 85 | b.out = append(b.out, 86 | byte(b.bitContainer), 87 | byte(b.bitContainer>>8), 88 | byte(b.bitContainer>>16), 89 | byte(b.bitContainer>>24), 90 | byte(b.bitContainer>>32), 91 | ) 92 | case 6: 93 | b.out = append(b.out, 94 | byte(b.bitContainer), 95 | byte(b.bitContainer>>8), 96 | byte(b.bitContainer>>16), 97 | byte(b.bitContainer>>24), 98 | byte(b.bitContainer>>32), 99 | byte(b.bitContainer>>40), 100 | ) 101 | case 7: 102 | b.out = append(b.out, 103 | byte(b.bitContainer), 104 | byte(b.bitContainer>>8), 105 | byte(b.bitContainer>>16), 106 | byte(b.bitContainer>>24), 107 | byte(b.bitContainer>>32), 108 | byte(b.bitContainer>>40), 109 | byte(b.bitContainer>>48), 110 | ) 111 | case 8: 112 | b.out = append(b.out, 113 | byte(b.bitContainer), 114 | byte(b.bitContainer>>8), 115 | byte(b.bitContainer>>16), 116 | byte(b.bitContainer>>24), 117 | byte(b.bitContainer>>32), 118 | byte(b.bitContainer>>40), 119 | byte(b.bitContainer>>48), 120 | byte(b.bitContainer>>56), 121 | ) 122 | default: 123 | panic(fmt.Errorf("bits (%d) > 64", b.nBits)) 124 | } 125 | b.bitContainer >>= v << 3 126 | b.nBits &= 7 127 | } 128 | 129 | // flush32 will flush out, so there are at least 32 bits available for writing. 130 | func (b *bitWriter) flush32() { 131 | if b.nBits < 32 { 132 | return 133 | } 134 | b.out = append(b.out, 135 | byte(b.bitContainer), 136 | byte(b.bitContainer>>8), 137 | byte(b.bitContainer>>16), 138 | byte(b.bitContainer>>24)) 139 | b.nBits -= 32 140 | b.bitContainer >>= 32 141 | } 142 | 143 | // flushAlign will flush remaining full bytes and align to next byte boundary. 144 | func (b *bitWriter) flushAlign() { 145 | nbBytes := (b.nBits + 7) >> 3 146 | for i := uint8(0); i < nbBytes; i++ { 147 | b.out = append(b.out, byte(b.bitContainer>>(i*8))) 148 | } 149 | b.nBits = 0 150 | b.bitContainer = 0 151 | } 152 | 153 | // close will write the alignment bit and write the final byte(s) 154 | // to the output. 155 | func (b *bitWriter) close() { 156 | // End mark 157 | b.addBits16Clean(1, 1) 158 | // flush until next byte. 159 | b.flushAlign() 160 | } 161 | 162 | // reset and continue writing by appending to out. 163 | func (b *bitWriter) reset(out []byte) { 164 | b.bitContainer = 0 165 | b.nBits = 0 166 | b.out = out 167 | } 168 | -------------------------------------------------------------------------------- /compress/fse/bytereader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Klaus Post. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. 5 | 6 | package fse 7 | 8 | // byteReader provides a byte reader that reads 9 | // little endian values from a byte stream. 10 | // The input stream is manually advanced. 11 | // The reader performs no bounds checks. 12 | type byteReader struct { 13 | b []byte 14 | off int 15 | } 16 | 17 | // init will initialize the reader and set the input. 18 | func (b *byteReader) init(in []byte) { 19 | b.b = in 20 | b.off = 0 21 | } 22 | 23 | // advance the stream b n bytes. 24 | func (b *byteReader) advance(n uint) { 25 | b.off += int(n) 26 | } 27 | 28 | // Uint32 returns a little endian uint32 starting at current offset. 29 | func (b byteReader) Uint32() uint32 { 30 | b2 := b.b[b.off:] 31 | b2 = b2[:4] 32 | v3 := uint32(b2[3]) 33 | v2 := uint32(b2[2]) 34 | v1 := uint32(b2[1]) 35 | v0 := uint32(b2[0]) 36 | return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24) 37 | } 38 | 39 | // unread returns the unread portion of the input. 40 | func (b byteReader) unread() []byte { 41 | return b.b[b.off:] 42 | } 43 | 44 | // remain will return the number of bytes remaining. 45 | func (b byteReader) remain() int { 46 | return len(b.b) - b.off 47 | } 48 | -------------------------------------------------------------------------------- /compress/fse/fse.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Klaus Post. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. 5 | 6 | // Package fse provides Finite State Entropy encoding and decoding. 7 | // 8 | // Finite State Entropy encoding provides a fast near-optimal symbol encoding/decoding 9 | // for byte blocks as implemented in zstd. 10 | // 11 | // See https://github.com/klauspost/compress/tree/master/fse for more information. 12 | package fse 13 | 14 | import ( 15 | "errors" 16 | "fmt" 17 | "math/bits" 18 | ) 19 | 20 | const ( 21 | /*!MEMORY_USAGE : 22 | * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) 23 | * Increasing memory usage improves compression ratio 24 | * Reduced memory usage can improve speed, due to cache effect 25 | * Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ 26 | maxMemoryUsage = 14 27 | defaultMemoryUsage = 13 28 | 29 | maxTableLog = maxMemoryUsage - 2 30 | maxTablesize = 1 << maxTableLog 31 | defaultTablelog = defaultMemoryUsage - 2 32 | minTablelog = 5 33 | maxSymbolValue = 255 34 | ) 35 | 36 | var ( 37 | // ErrIncompressible is returned when input is judged to be too hard to compress. 38 | ErrIncompressible = errors.New("input is not compressible") 39 | 40 | // ErrUseRLE is returned from the compressor when the input is a single byte value repeated. 41 | ErrUseRLE = errors.New("input is single value repeated") 42 | ) 43 | 44 | // Scratch provides temporary storage for compression and decompression. 45 | type Scratch struct { 46 | // Private 47 | count [maxSymbolValue + 1]uint32 48 | norm [maxSymbolValue + 1]int16 49 | br byteReader 50 | bits bitReader 51 | bw bitWriter 52 | ct cTable // Compression tables. 53 | decTable []decSymbol // Decompression table. 54 | maxCount int // count of the most probable symbol 55 | 56 | // Per block parameters. 57 | // These can be used to override compression parameters of the block. 58 | // Do not touch, unless you know what you are doing. 59 | 60 | // Out is output buffer. 61 | // If the scratch is re-used before the caller is done processing the output, 62 | // set this field to nil. 63 | // Otherwise the output buffer will be re-used for next Compression/Decompression step 64 | // and allocation will be avoided. 65 | Out []byte 66 | 67 | // DecompressLimit limits the maximum decoded size acceptable. 68 | // If > 0 decompression will stop when approximately this many bytes 69 | // has been decoded. 70 | // If 0, maximum size will be 2GB. 71 | DecompressLimit int 72 | 73 | symbolLen uint16 // Length of active part of the symbol table. 74 | actualTableLog uint8 // Selected tablelog. 75 | zeroBits bool // no bits has prob > 50%. 76 | clearCount bool // clear count 77 | 78 | // MaxSymbolValue will override the maximum symbol value of the next block. 79 | MaxSymbolValue uint8 80 | 81 | // TableLog will attempt to override the tablelog for the next block. 82 | TableLog uint8 83 | } 84 | 85 | // Histogram allows to populate the histogram and skip that step in the compression, 86 | // It otherwise allows to inspect the histogram when compression is done. 87 | // To indicate that you have populated the histogram call HistogramFinished 88 | // with the value of the highest populated symbol, as well as the number of entries 89 | // in the most populated entry. These are accepted at face value. 90 | // The returned slice will always be length 256. 91 | func (s *Scratch) Histogram() []uint32 { 92 | return s.count[:] 93 | } 94 | 95 | // HistogramFinished can be called to indicate that the histogram has been populated. 96 | // maxSymbol is the index of the highest set symbol of the next data segment. 97 | // maxCount is the number of entries in the most populated entry. 98 | // These are accepted at face value. 99 | func (s *Scratch) HistogramFinished(maxSymbol uint8, maxCount int) { 100 | s.maxCount = maxCount 101 | s.symbolLen = uint16(maxSymbol) + 1 102 | s.clearCount = maxCount != 0 103 | } 104 | 105 | // prepare will prepare and allocate scratch tables used for both compression and decompression. 106 | func (s *Scratch) prepare(in []byte) (*Scratch, error) { 107 | if s == nil { 108 | s = &Scratch{} 109 | } 110 | if s.MaxSymbolValue == 0 { 111 | s.MaxSymbolValue = 255 112 | } 113 | if s.TableLog == 0 { 114 | s.TableLog = defaultTablelog 115 | } 116 | if s.TableLog > maxTableLog { 117 | return nil, fmt.Errorf("tableLog (%d) > maxTableLog (%d)", s.TableLog, maxTableLog) 118 | } 119 | if cap(s.Out) == 0 { 120 | s.Out = make([]byte, 0, len(in)) 121 | } 122 | if s.clearCount && s.maxCount == 0 { 123 | for i := range s.count { 124 | s.count[i] = 0 125 | } 126 | s.clearCount = false 127 | } 128 | s.br.init(in) 129 | if s.DecompressLimit == 0 { 130 | // Max size 2GB. 131 | s.DecompressLimit = (2 << 30) - 1 132 | } 133 | 134 | return s, nil 135 | } 136 | 137 | // tableStep returns the next table index. 138 | func tableStep(tableSize uint32) uint32 { 139 | return (tableSize >> 1) + (tableSize >> 3) + 3 140 | } 141 | 142 | func highBits(val uint32) (n uint32) { 143 | return uint32(bits.Len32(val) - 1) 144 | } 145 | -------------------------------------------------------------------------------- /compress/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/klauspost/compress 2 | 3 | go 1.22 4 | 5 | retract ( 6 | // https://github.com/klauspost/compress/pull/503 7 | v1.14.3 8 | v1.14.2 9 | v1.14.1 10 | ) 11 | -------------------------------------------------------------------------------- /compress/huff0/bitreader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Klaus Post. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. 5 | 6 | package huff0 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | "io" 12 | 13 | "github.com/klauspost/compress/internal/le" 14 | ) 15 | 16 | // bitReader reads a bitstream in reverse. 17 | // The last set bit indicates the start of the stream and is used 18 | // for aligning the input. 19 | type bitReaderBytes struct { 20 | in []byte 21 | off uint // next byte to read is at in[off - 1] 22 | value uint64 23 | bitsRead uint8 24 | } 25 | 26 | // init initializes and resets the bit reader. 27 | func (b *bitReaderBytes) init(in []byte) error { 28 | if len(in) < 1 { 29 | return errors.New("corrupt stream: too short") 30 | } 31 | b.in = in 32 | b.off = uint(len(in)) 33 | // The highest bit of the last byte indicates where to start 34 | v := in[len(in)-1] 35 | if v == 0 { 36 | return errors.New("corrupt stream, did not find end of stream") 37 | } 38 | b.bitsRead = 64 39 | b.value = 0 40 | if len(in) >= 8 { 41 | b.fillFastStart() 42 | } else { 43 | b.fill() 44 | b.fill() 45 | } 46 | b.advance(8 - uint8(highBit32(uint32(v)))) 47 | return nil 48 | } 49 | 50 | // peekByteFast requires that at least one byte is requested every time. 51 | // There are no checks if the buffer is filled. 52 | func (b *bitReaderBytes) peekByteFast() uint8 { 53 | got := uint8(b.value >> 56) 54 | return got 55 | } 56 | 57 | func (b *bitReaderBytes) advance(n uint8) { 58 | b.bitsRead += n 59 | b.value <<= n & 63 60 | } 61 | 62 | // fillFast() will make sure at least 32 bits are available. 63 | // There must be at least 4 bytes available. 64 | func (b *bitReaderBytes) fillFast() { 65 | if b.bitsRead < 32 { 66 | return 67 | } 68 | 69 | // 2 bounds checks. 70 | low := le.Load32(b.in, b.off-4) 71 | b.value |= uint64(low) << (b.bitsRead - 32) 72 | b.bitsRead -= 32 73 | b.off -= 4 74 | } 75 | 76 | // fillFastStart() assumes the bitReaderBytes is empty and there is at least 8 bytes to read. 77 | func (b *bitReaderBytes) fillFastStart() { 78 | // Do single re-slice to avoid bounds checks. 79 | b.value = le.Load64(b.in, b.off-8) 80 | b.bitsRead = 0 81 | b.off -= 8 82 | } 83 | 84 | // fill() will make sure at least 32 bits are available. 85 | func (b *bitReaderBytes) fill() { 86 | if b.bitsRead < 32 { 87 | return 88 | } 89 | if b.off >= 4 { 90 | low := le.Load32(b.in, b.off-4) 91 | b.value |= uint64(low) << (b.bitsRead - 32) 92 | b.bitsRead -= 32 93 | b.off -= 4 94 | return 95 | } 96 | for b.off > 0 { 97 | b.value |= uint64(b.in[b.off-1]) << (b.bitsRead - 8) 98 | b.bitsRead -= 8 99 | b.off-- 100 | } 101 | } 102 | 103 | // finished returns true if all bits have been read from the bit stream. 104 | func (b *bitReaderBytes) finished() bool { 105 | return b.off == 0 && b.bitsRead >= 64 106 | } 107 | 108 | func (b *bitReaderBytes) remaining() uint { 109 | return b.off*8 + uint(64-b.bitsRead) 110 | } 111 | 112 | // close the bitstream and returns an error if out-of-buffer reads occurred. 113 | func (b *bitReaderBytes) close() error { 114 | // Release reference. 115 | b.in = nil 116 | if b.remaining() > 0 { 117 | return fmt.Errorf("corrupt input: %d bits remain on stream", b.remaining()) 118 | } 119 | if b.bitsRead > 64 { 120 | return io.ErrUnexpectedEOF 121 | } 122 | return nil 123 | } 124 | 125 | // bitReaderShifted reads a bitstream in reverse. 126 | // The last set bit indicates the start of the stream and is used 127 | // for aligning the input. 128 | type bitReaderShifted struct { 129 | in []byte 130 | off uint // next byte to read is at in[off - 1] 131 | value uint64 132 | bitsRead uint8 133 | } 134 | 135 | // init initializes and resets the bit reader. 136 | func (b *bitReaderShifted) init(in []byte) error { 137 | if len(in) < 1 { 138 | return errors.New("corrupt stream: too short") 139 | } 140 | b.in = in 141 | b.off = uint(len(in)) 142 | // The highest bit of the last byte indicates where to start 143 | v := in[len(in)-1] 144 | if v == 0 { 145 | return errors.New("corrupt stream, did not find end of stream") 146 | } 147 | b.bitsRead = 64 148 | b.value = 0 149 | if len(in) >= 8 { 150 | b.fillFastStart() 151 | } else { 152 | b.fill() 153 | b.fill() 154 | } 155 | b.advance(8 - uint8(highBit32(uint32(v)))) 156 | return nil 157 | } 158 | 159 | // peekBitsFast requires that at least one bit is requested every time. 160 | // There are no checks if the buffer is filled. 161 | func (b *bitReaderShifted) peekBitsFast(n uint8) uint16 { 162 | return uint16(b.value >> ((64 - n) & 63)) 163 | } 164 | 165 | func (b *bitReaderShifted) advance(n uint8) { 166 | b.bitsRead += n 167 | b.value <<= n & 63 168 | } 169 | 170 | // fillFast() will make sure at least 32 bits are available. 171 | // There must be at least 4 bytes available. 172 | func (b *bitReaderShifted) fillFast() { 173 | if b.bitsRead < 32 { 174 | return 175 | } 176 | 177 | low := le.Load32(b.in, b.off-4) 178 | b.value |= uint64(low) << ((b.bitsRead - 32) & 63) 179 | b.bitsRead -= 32 180 | b.off -= 4 181 | } 182 | 183 | // fillFastStart() assumes the bitReaderShifted is empty and there is at least 8 bytes to read. 184 | func (b *bitReaderShifted) fillFastStart() { 185 | b.value = le.Load64(b.in, b.off-8) 186 | b.bitsRead = 0 187 | b.off -= 8 188 | } 189 | 190 | // fill() will make sure at least 32 bits are available. 191 | func (b *bitReaderShifted) fill() { 192 | if b.bitsRead < 32 { 193 | return 194 | } 195 | if b.off > 4 { 196 | low := le.Load32(b.in, b.off-4) 197 | b.value |= uint64(low) << ((b.bitsRead - 32) & 63) 198 | b.bitsRead -= 32 199 | b.off -= 4 200 | return 201 | } 202 | for b.off > 0 { 203 | b.value |= uint64(b.in[b.off-1]) << ((b.bitsRead - 8) & 63) 204 | b.bitsRead -= 8 205 | b.off-- 206 | } 207 | } 208 | 209 | func (b *bitReaderShifted) remaining() uint { 210 | return b.off*8 + uint(64-b.bitsRead) 211 | } 212 | 213 | // close the bitstream and returns an error if out-of-buffer reads occurred. 214 | func (b *bitReaderShifted) close() error { 215 | // Release reference. 216 | b.in = nil 217 | if b.remaining() > 0 { 218 | return fmt.Errorf("corrupt input: %d bits remain on stream", b.remaining()) 219 | } 220 | if b.bitsRead > 64 { 221 | return io.ErrUnexpectedEOF 222 | } 223 | return nil 224 | } 225 | -------------------------------------------------------------------------------- /compress/huff0/bitwriter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Klaus Post. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. 5 | 6 | package huff0 7 | 8 | // bitWriter will write bits. 9 | // First bit will be LSB of the first byte of output. 10 | type bitWriter struct { 11 | bitContainer uint64 12 | nBits uint8 13 | out []byte 14 | } 15 | 16 | // addBits16Clean will add up to 16 bits. value may not contain more set bits than indicated. 17 | // It will not check if there is space for them, so the caller must ensure that it has flushed recently. 18 | func (b *bitWriter) addBits16Clean(value uint16, bits uint8) { 19 | b.bitContainer |= uint64(value) << (b.nBits & 63) 20 | b.nBits += bits 21 | } 22 | 23 | // encSymbol will add up to 16 bits. value may not contain more set bits than indicated. 24 | // It will not check if there is space for them, so the caller must ensure that it has flushed recently. 25 | func (b *bitWriter) encSymbol(ct cTable, symbol byte) { 26 | enc := ct[symbol] 27 | b.bitContainer |= uint64(enc.val) << (b.nBits & 63) 28 | if false { 29 | if enc.nBits == 0 { 30 | panic("nbits 0") 31 | } 32 | } 33 | b.nBits += enc.nBits 34 | } 35 | 36 | // encTwoSymbols will add up to 32 bits. value may not contain more set bits than indicated. 37 | // It will not check if there is space for them, so the caller must ensure that it has flushed recently. 38 | func (b *bitWriter) encTwoSymbols(ct cTable, av, bv byte) { 39 | encA := ct[av] 40 | encB := ct[bv] 41 | sh := b.nBits & 63 42 | combined := uint64(encA.val) | (uint64(encB.val) << (encA.nBits & 63)) 43 | b.bitContainer |= combined << sh 44 | if false { 45 | if encA.nBits == 0 { 46 | panic("nbitsA 0") 47 | } 48 | if encB.nBits == 0 { 49 | panic("nbitsB 0") 50 | } 51 | } 52 | b.nBits += encA.nBits + encB.nBits 53 | } 54 | 55 | // encFourSymbols adds up to 32 bits from four symbols. 56 | // It will not check if there is space for them, 57 | // so the caller must ensure that b has been flushed recently. 58 | func (b *bitWriter) encFourSymbols(encA, encB, encC, encD cTableEntry) { 59 | bitsA := encA.nBits 60 | bitsB := bitsA + encB.nBits 61 | bitsC := bitsB + encC.nBits 62 | bitsD := bitsC + encD.nBits 63 | combined := uint64(encA.val) | 64 | (uint64(encB.val) << (bitsA & 63)) | 65 | (uint64(encC.val) << (bitsB & 63)) | 66 | (uint64(encD.val) << (bitsC & 63)) 67 | b.bitContainer |= combined << (b.nBits & 63) 68 | b.nBits += bitsD 69 | } 70 | 71 | // flush32 will flush out, so there are at least 32 bits available for writing. 72 | func (b *bitWriter) flush32() { 73 | if b.nBits < 32 { 74 | return 75 | } 76 | b.out = append(b.out, 77 | byte(b.bitContainer), 78 | byte(b.bitContainer>>8), 79 | byte(b.bitContainer>>16), 80 | byte(b.bitContainer>>24)) 81 | b.nBits -= 32 82 | b.bitContainer >>= 32 83 | } 84 | 85 | // flushAlign will flush remaining full bytes and align to next byte boundary. 86 | func (b *bitWriter) flushAlign() { 87 | nbBytes := (b.nBits + 7) >> 3 88 | for i := uint8(0); i < nbBytes; i++ { 89 | b.out = append(b.out, byte(b.bitContainer>>(i*8))) 90 | } 91 | b.nBits = 0 92 | b.bitContainer = 0 93 | } 94 | 95 | // close will write the alignment bit and write the final byte(s) 96 | // to the output. 97 | func (b *bitWriter) close() { 98 | // End mark 99 | b.addBits16Clean(1, 1) 100 | // flush until next byte. 101 | b.flushAlign() 102 | } 103 | -------------------------------------------------------------------------------- /compress/huff0/decompress_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build amd64 && !appengine && !noasm && gc 2 | // +build amd64,!appengine,!noasm,gc 3 | 4 | // This file contains the specialisation of Decoder.Decompress4X 5 | // and Decoder.Decompress1X that use an asm implementation of thir main loops. 6 | package huff0 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | 12 | "github.com/klauspost/compress/internal/cpuinfo" 13 | ) 14 | 15 | // decompress4x_main_loop_x86 is an x86 assembler implementation 16 | // of Decompress4X when tablelog > 8. 17 | // 18 | //go:noescape 19 | func decompress4x_main_loop_amd64(ctx *decompress4xContext) 20 | 21 | // decompress4x_8b_loop_x86 is an x86 assembler implementation 22 | // of Decompress4X when tablelog <= 8 which decodes 4 entries 23 | // per loop. 24 | // 25 | //go:noescape 26 | func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext) 27 | 28 | // fallback8BitSize is the size where using Go version is faster. 29 | const fallback8BitSize = 800 30 | 31 | type decompress4xContext struct { 32 | pbr *[4]bitReaderShifted 33 | peekBits uint8 34 | out *byte 35 | dstEvery int 36 | tbl *dEntrySingle 37 | decoded int 38 | limit *byte 39 | } 40 | 41 | // Decompress4X will decompress a 4X encoded stream. 42 | // The length of the supplied input must match the end of a block exactly. 43 | // The *capacity* of the dst slice must match the destination size of 44 | // the uncompressed data exactly. 45 | func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) { 46 | if len(d.dt.single) == 0 { 47 | return nil, errors.New("no table loaded") 48 | } 49 | if len(src) < 6+(4*1) { 50 | return nil, errors.New("input too small") 51 | } 52 | 53 | use8BitTables := d.actualTableLog <= 8 54 | if cap(dst) < fallback8BitSize && use8BitTables { 55 | return d.decompress4X8bit(dst, src) 56 | } 57 | 58 | var br [4]bitReaderShifted 59 | // Decode "jump table" 60 | start := 6 61 | for i := 0; i < 3; i++ { 62 | length := int(src[i*2]) | (int(src[i*2+1]) << 8) 63 | if start+length >= len(src) { 64 | return nil, errors.New("truncated input (or invalid offset)") 65 | } 66 | err := br[i].init(src[start : start+length]) 67 | if err != nil { 68 | return nil, err 69 | } 70 | start += length 71 | } 72 | err := br[3].init(src[start:]) 73 | if err != nil { 74 | return nil, err 75 | } 76 | 77 | // destination, offset to match first output 78 | dstSize := cap(dst) 79 | dst = dst[:dstSize] 80 | out := dst 81 | dstEvery := (dstSize + 3) / 4 82 | 83 | const tlSize = 1 << tableLogMax 84 | const tlMask = tlSize - 1 85 | single := d.dt.single[:tlSize] 86 | 87 | var decoded int 88 | 89 | if len(out) > 4*4 && !(br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4) { 90 | ctx := decompress4xContext{ 91 | pbr: &br, 92 | peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast() 93 | out: &out[0], 94 | dstEvery: dstEvery, 95 | tbl: &single[0], 96 | limit: &out[dstEvery-4], // Always stop decoding when first buffer gets here to avoid writing OOB on last. 97 | } 98 | if use8BitTables { 99 | decompress4x_8b_main_loop_amd64(&ctx) 100 | } else { 101 | decompress4x_main_loop_amd64(&ctx) 102 | } 103 | 104 | decoded = ctx.decoded 105 | out = out[decoded/4:] 106 | } 107 | 108 | // Decode remaining. 109 | remainBytes := dstEvery - (decoded / 4) 110 | for i := range br { 111 | offset := dstEvery * i 112 | endsAt := offset + remainBytes 113 | if endsAt > len(out) { 114 | endsAt = len(out) 115 | } 116 | br := &br[i] 117 | bitsLeft := br.remaining() 118 | for bitsLeft > 0 { 119 | br.fill() 120 | if offset >= endsAt { 121 | return nil, errors.New("corruption detected: stream overrun 4") 122 | } 123 | 124 | // Read value and increment offset. 125 | val := br.peekBitsFast(d.actualTableLog) 126 | v := single[val&tlMask].entry 127 | nBits := uint8(v) 128 | br.advance(nBits) 129 | bitsLeft -= uint(nBits) 130 | out[offset] = uint8(v >> 8) 131 | offset++ 132 | } 133 | if offset != endsAt { 134 | return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt) 135 | } 136 | decoded += offset - dstEvery*i 137 | err = br.close() 138 | if err != nil { 139 | return nil, err 140 | } 141 | } 142 | if dstSize != decoded { 143 | return nil, errors.New("corruption detected: short output block") 144 | } 145 | return dst, nil 146 | } 147 | 148 | // decompress4x_main_loop_x86 is an x86 assembler implementation 149 | // of Decompress1X when tablelog > 8. 150 | // 151 | //go:noescape 152 | func decompress1x_main_loop_amd64(ctx *decompress1xContext) 153 | 154 | // decompress4x_main_loop_x86 is an x86 with BMI2 assembler implementation 155 | // of Decompress1X when tablelog > 8. 156 | // 157 | //go:noescape 158 | func decompress1x_main_loop_bmi2(ctx *decompress1xContext) 159 | 160 | type decompress1xContext struct { 161 | pbr *bitReaderShifted 162 | peekBits uint8 163 | out *byte 164 | outCap int 165 | tbl *dEntrySingle 166 | decoded int 167 | } 168 | 169 | // Error reported by asm implementations 170 | const error_max_decoded_size_exeeded = -1 171 | 172 | // Decompress1X will decompress a 1X encoded stream. 173 | // The cap of the output buffer will be the maximum decompressed size. 174 | // The length of the supplied input must match the end of a block exactly. 175 | func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) { 176 | if len(d.dt.single) == 0 { 177 | return nil, errors.New("no table loaded") 178 | } 179 | var br bitReaderShifted 180 | err := br.init(src) 181 | if err != nil { 182 | return dst, err 183 | } 184 | maxDecodedSize := cap(dst) 185 | dst = dst[:maxDecodedSize] 186 | 187 | const tlSize = 1 << tableLogMax 188 | const tlMask = tlSize - 1 189 | 190 | if maxDecodedSize >= 4 { 191 | ctx := decompress1xContext{ 192 | pbr: &br, 193 | out: &dst[0], 194 | outCap: maxDecodedSize, 195 | peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast() 196 | tbl: &d.dt.single[0], 197 | } 198 | 199 | if cpuinfo.HasBMI2() { 200 | decompress1x_main_loop_bmi2(&ctx) 201 | } else { 202 | decompress1x_main_loop_amd64(&ctx) 203 | } 204 | if ctx.decoded == error_max_decoded_size_exeeded { 205 | return nil, ErrMaxDecodedSizeExceeded 206 | } 207 | 208 | dst = dst[:ctx.decoded] 209 | } 210 | 211 | // br < 8, so uint8 is fine 212 | bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead 213 | for bitsLeft > 0 { 214 | br.fill() 215 | if len(dst) >= maxDecodedSize { 216 | br.close() 217 | return nil, ErrMaxDecodedSizeExceeded 218 | } 219 | v := d.dt.single[br.peekBitsFast(d.actualTableLog)&tlMask] 220 | nBits := uint8(v.entry) 221 | br.advance(nBits) 222 | bitsLeft -= nBits 223 | dst = append(dst, uint8(v.entry>>8)) 224 | } 225 | return dst, br.close() 226 | } 227 | -------------------------------------------------------------------------------- /compress/huff0/decompress_generic.go: -------------------------------------------------------------------------------- 1 | //go:build !amd64 || appengine || !gc || noasm 2 | // +build !amd64 appengine !gc noasm 3 | 4 | // This file contains a generic implementation of Decoder.Decompress4X. 5 | package huff0 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | ) 11 | 12 | // Decompress4X will decompress a 4X encoded stream. 13 | // The length of the supplied input must match the end of a block exactly. 14 | // The *capacity* of the dst slice must match the destination size of 15 | // the uncompressed data exactly. 16 | func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) { 17 | if len(d.dt.single) == 0 { 18 | return nil, errors.New("no table loaded") 19 | } 20 | if len(src) < 6+(4*1) { 21 | return nil, errors.New("input too small") 22 | } 23 | if use8BitTables && d.actualTableLog <= 8 { 24 | return d.decompress4X8bit(dst, src) 25 | } 26 | 27 | var br [4]bitReaderShifted 28 | // Decode "jump table" 29 | start := 6 30 | for i := 0; i < 3; i++ { 31 | length := int(src[i*2]) | (int(src[i*2+1]) << 8) 32 | if start+length >= len(src) { 33 | return nil, errors.New("truncated input (or invalid offset)") 34 | } 35 | err := br[i].init(src[start : start+length]) 36 | if err != nil { 37 | return nil, err 38 | } 39 | start += length 40 | } 41 | err := br[3].init(src[start:]) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | // destination, offset to match first output 47 | dstSize := cap(dst) 48 | dst = dst[:dstSize] 49 | out := dst 50 | dstEvery := (dstSize + 3) / 4 51 | 52 | const tlSize = 1 << tableLogMax 53 | const tlMask = tlSize - 1 54 | single := d.dt.single[:tlSize] 55 | 56 | // Use temp table to avoid bound checks/append penalty. 57 | buf := d.buffer() 58 | var off uint8 59 | var decoded int 60 | 61 | // Decode 2 values from each decoder/loop. 62 | const bufoff = 256 63 | for { 64 | if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 { 65 | break 66 | } 67 | 68 | { 69 | const stream = 0 70 | const stream2 = 1 71 | br[stream].fillFast() 72 | br[stream2].fillFast() 73 | 74 | val := br[stream].peekBitsFast(d.actualTableLog) 75 | val2 := br[stream2].peekBitsFast(d.actualTableLog) 76 | v := single[val&tlMask] 77 | v2 := single[val2&tlMask] 78 | br[stream].advance(uint8(v.entry)) 79 | br[stream2].advance(uint8(v2.entry)) 80 | buf[stream][off] = uint8(v.entry >> 8) 81 | buf[stream2][off] = uint8(v2.entry >> 8) 82 | 83 | val = br[stream].peekBitsFast(d.actualTableLog) 84 | val2 = br[stream2].peekBitsFast(d.actualTableLog) 85 | v = single[val&tlMask] 86 | v2 = single[val2&tlMask] 87 | br[stream].advance(uint8(v.entry)) 88 | br[stream2].advance(uint8(v2.entry)) 89 | buf[stream][off+1] = uint8(v.entry >> 8) 90 | buf[stream2][off+1] = uint8(v2.entry >> 8) 91 | } 92 | 93 | { 94 | const stream = 2 95 | const stream2 = 3 96 | br[stream].fillFast() 97 | br[stream2].fillFast() 98 | 99 | val := br[stream].peekBitsFast(d.actualTableLog) 100 | val2 := br[stream2].peekBitsFast(d.actualTableLog) 101 | v := single[val&tlMask] 102 | v2 := single[val2&tlMask] 103 | br[stream].advance(uint8(v.entry)) 104 | br[stream2].advance(uint8(v2.entry)) 105 | buf[stream][off] = uint8(v.entry >> 8) 106 | buf[stream2][off] = uint8(v2.entry >> 8) 107 | 108 | val = br[stream].peekBitsFast(d.actualTableLog) 109 | val2 = br[stream2].peekBitsFast(d.actualTableLog) 110 | v = single[val&tlMask] 111 | v2 = single[val2&tlMask] 112 | br[stream].advance(uint8(v.entry)) 113 | br[stream2].advance(uint8(v2.entry)) 114 | buf[stream][off+1] = uint8(v.entry >> 8) 115 | buf[stream2][off+1] = uint8(v2.entry >> 8) 116 | } 117 | 118 | off += 2 119 | 120 | if off == 0 { 121 | if bufoff > dstEvery { 122 | d.bufs.Put(buf) 123 | return nil, errors.New("corruption detected: stream overrun 1") 124 | } 125 | // There must at least be 3 buffers left. 126 | if len(out)-bufoff < dstEvery*3 { 127 | d.bufs.Put(buf) 128 | return nil, errors.New("corruption detected: stream overrun 2") 129 | } 130 | //copy(out, buf[0][:]) 131 | //copy(out[dstEvery:], buf[1][:]) 132 | //copy(out[dstEvery*2:], buf[2][:]) 133 | //copy(out[dstEvery*3:], buf[3][:]) 134 | *(*[bufoff]byte)(out) = buf[0] 135 | *(*[bufoff]byte)(out[dstEvery:]) = buf[1] 136 | *(*[bufoff]byte)(out[dstEvery*2:]) = buf[2] 137 | *(*[bufoff]byte)(out[dstEvery*3:]) = buf[3] 138 | out = out[bufoff:] 139 | decoded += bufoff * 4 140 | } 141 | } 142 | if off > 0 { 143 | ioff := int(off) 144 | if len(out) < dstEvery*3+ioff { 145 | d.bufs.Put(buf) 146 | return nil, errors.New("corruption detected: stream overrun 3") 147 | } 148 | copy(out, buf[0][:off]) 149 | copy(out[dstEvery:], buf[1][:off]) 150 | copy(out[dstEvery*2:], buf[2][:off]) 151 | copy(out[dstEvery*3:], buf[3][:off]) 152 | decoded += int(off) * 4 153 | out = out[off:] 154 | } 155 | 156 | // Decode remaining. 157 | remainBytes := dstEvery - (decoded / 4) 158 | for i := range br { 159 | offset := dstEvery * i 160 | endsAt := offset + remainBytes 161 | if endsAt > len(out) { 162 | endsAt = len(out) 163 | } 164 | br := &br[i] 165 | bitsLeft := br.remaining() 166 | for bitsLeft > 0 { 167 | br.fill() 168 | if offset >= endsAt { 169 | d.bufs.Put(buf) 170 | return nil, errors.New("corruption detected: stream overrun 4") 171 | } 172 | 173 | // Read value and increment offset. 174 | val := br.peekBitsFast(d.actualTableLog) 175 | v := single[val&tlMask].entry 176 | nBits := uint8(v) 177 | br.advance(nBits) 178 | bitsLeft -= uint(nBits) 179 | out[offset] = uint8(v >> 8) 180 | offset++ 181 | } 182 | if offset != endsAt { 183 | d.bufs.Put(buf) 184 | return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt) 185 | } 186 | decoded += offset - dstEvery*i 187 | err = br.close() 188 | if err != nil { 189 | return nil, err 190 | } 191 | } 192 | d.bufs.Put(buf) 193 | if dstSize != decoded { 194 | return nil, errors.New("corruption detected: short output block") 195 | } 196 | return dst, nil 197 | } 198 | 199 | // Decompress1X will decompress a 1X encoded stream. 200 | // The cap of the output buffer will be the maximum decompressed size. 201 | // The length of the supplied input must match the end of a block exactly. 202 | func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) { 203 | if len(d.dt.single) == 0 { 204 | return nil, errors.New("no table loaded") 205 | } 206 | if use8BitTables && d.actualTableLog <= 8 { 207 | return d.decompress1X8Bit(dst, src) 208 | } 209 | var br bitReaderShifted 210 | err := br.init(src) 211 | if err != nil { 212 | return dst, err 213 | } 214 | maxDecodedSize := cap(dst) 215 | dst = dst[:0] 216 | 217 | // Avoid bounds check by always having full sized table. 218 | const tlSize = 1 << tableLogMax 219 | const tlMask = tlSize - 1 220 | dt := d.dt.single[:tlSize] 221 | 222 | // Use temp table to avoid bound checks/append penalty. 223 | bufs := d.buffer() 224 | buf := &bufs[0] 225 | var off uint8 226 | 227 | for br.off >= 8 { 228 | br.fillFast() 229 | v := dt[br.peekBitsFast(d.actualTableLog)&tlMask] 230 | br.advance(uint8(v.entry)) 231 | buf[off+0] = uint8(v.entry >> 8) 232 | 233 | v = dt[br.peekBitsFast(d.actualTableLog)&tlMask] 234 | br.advance(uint8(v.entry)) 235 | buf[off+1] = uint8(v.entry >> 8) 236 | 237 | // Refill 238 | br.fillFast() 239 | 240 | v = dt[br.peekBitsFast(d.actualTableLog)&tlMask] 241 | br.advance(uint8(v.entry)) 242 | buf[off+2] = uint8(v.entry >> 8) 243 | 244 | v = dt[br.peekBitsFast(d.actualTableLog)&tlMask] 245 | br.advance(uint8(v.entry)) 246 | buf[off+3] = uint8(v.entry >> 8) 247 | 248 | off += 4 249 | if off == 0 { 250 | if len(dst)+256 > maxDecodedSize { 251 | br.close() 252 | d.bufs.Put(bufs) 253 | return nil, ErrMaxDecodedSizeExceeded 254 | } 255 | dst = append(dst, buf[:]...) 256 | } 257 | } 258 | 259 | if len(dst)+int(off) > maxDecodedSize { 260 | d.bufs.Put(bufs) 261 | br.close() 262 | return nil, ErrMaxDecodedSizeExceeded 263 | } 264 | dst = append(dst, buf[:off]...) 265 | 266 | // br < 8, so uint8 is fine 267 | bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead 268 | for bitsLeft > 0 { 269 | br.fill() 270 | if false && br.bitsRead >= 32 { 271 | if br.off >= 4 { 272 | v := br.in[br.off-4:] 273 | v = v[:4] 274 | low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) 275 | br.value = (br.value << 32) | uint64(low) 276 | br.bitsRead -= 32 277 | br.off -= 4 278 | } else { 279 | for br.off > 0 { 280 | br.value = (br.value << 8) | uint64(br.in[br.off-1]) 281 | br.bitsRead -= 8 282 | br.off-- 283 | } 284 | } 285 | } 286 | if len(dst) >= maxDecodedSize { 287 | d.bufs.Put(bufs) 288 | br.close() 289 | return nil, ErrMaxDecodedSizeExceeded 290 | } 291 | v := d.dt.single[br.peekBitsFast(d.actualTableLog)&tlMask] 292 | nBits := uint8(v.entry) 293 | br.advance(nBits) 294 | bitsLeft -= nBits 295 | dst = append(dst, uint8(v.entry>>8)) 296 | } 297 | d.bufs.Put(bufs) 298 | return dst, br.close() 299 | } 300 | -------------------------------------------------------------------------------- /compress/internal/cpuinfo/cpuinfo.go: -------------------------------------------------------------------------------- 1 | // Package cpuinfo gives runtime info about the current CPU. 2 | // 3 | // This is a very limited module meant for use internally 4 | // in this project. For more versatile solution check 5 | // https://github.com/klauspost/cpuid. 6 | package cpuinfo 7 | 8 | // HasBMI1 checks whether an x86 CPU supports the BMI1 extension. 9 | func HasBMI1() bool { 10 | return hasBMI1 11 | } 12 | 13 | // HasBMI2 checks whether an x86 CPU supports the BMI2 extension. 14 | func HasBMI2() bool { 15 | return hasBMI2 16 | } 17 | 18 | // DisableBMI2 will disable BMI2, for testing purposes. 19 | // Call returned function to restore previous state. 20 | func DisableBMI2() func() { 21 | old := hasBMI2 22 | hasBMI2 = false 23 | return func() { 24 | hasBMI2 = old 25 | } 26 | } 27 | 28 | // HasBMI checks whether an x86 CPU supports both BMI1 and BMI2 extensions. 29 | func HasBMI() bool { 30 | return HasBMI1() && HasBMI2() 31 | } 32 | 33 | var hasBMI1 bool 34 | var hasBMI2 bool 35 | -------------------------------------------------------------------------------- /compress/internal/cpuinfo/cpuinfo_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build amd64 && !appengine && !noasm && gc 2 | // +build amd64,!appengine,!noasm,gc 3 | 4 | package cpuinfo 5 | 6 | // go:noescape 7 | func x86extensions() (bmi1, bmi2 bool) 8 | 9 | func init() { 10 | hasBMI1, hasBMI2 = x86extensions() 11 | } 12 | -------------------------------------------------------------------------------- /compress/internal/cpuinfo/cpuinfo_amd64.s: -------------------------------------------------------------------------------- 1 | // +build !appengine 2 | // +build gc 3 | // +build !noasm 4 | 5 | #include "textflag.h" 6 | #include "funcdata.h" 7 | #include "go_asm.h" 8 | 9 | TEXT ·x86extensions(SB), NOSPLIT, $0 10 | // 1. determine max EAX value 11 | XORQ AX, AX 12 | CPUID 13 | 14 | CMPQ AX, $7 15 | JB unsupported 16 | 17 | // 2. EAX = 7, ECX = 0 --- see Table 3-8 "Information Returned by CPUID Instruction" 18 | MOVQ $7, AX 19 | MOVQ $0, CX 20 | CPUID 21 | 22 | BTQ $3, BX // bit 3 = BMI1 23 | SETCS AL 24 | 25 | BTQ $8, BX // bit 8 = BMI2 26 | SETCS AH 27 | 28 | MOVB AL, bmi1+0(FP) 29 | MOVB AH, bmi2+1(FP) 30 | RET 31 | 32 | unsupported: 33 | XORQ AX, AX 34 | MOVB AL, bmi1+0(FP) 35 | MOVB AL, bmi2+1(FP) 36 | RET 37 | -------------------------------------------------------------------------------- /compress/internal/fuzz/helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024+ Klaus Post. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package fuzz provides a way to add test cases to a testing.F instance from a zip file. 6 | package fuzz 7 | 8 | import ( 9 | "archive/zip" 10 | "bytes" 11 | "encoding/binary" 12 | "fmt" 13 | "go/ast" 14 | "go/parser" 15 | "go/token" 16 | "io" 17 | "os" 18 | "strconv" 19 | "testing" 20 | ) 21 | 22 | type InputType uint8 23 | 24 | const ( 25 | // TypeRaw indicates that files are raw bytes. 26 | TypeRaw InputType = iota 27 | // TypeGoFuzz indicates files are from Go Fuzzer. 28 | TypeGoFuzz 29 | // TypeOSSFuzz indicates that files are from OSS fuzzer with size before data. 30 | TypeOSSFuzz 31 | ) 32 | 33 | // AddFromZip will read the supplied zip and add all as corpus for f. 34 | // Byte slices only. 35 | func AddFromZip(f *testing.F, filename string, t InputType, short bool) { 36 | file, err := os.Open(filename) 37 | if err != nil { 38 | f.Fatal(err) 39 | } 40 | fi, err := file.Stat() 41 | if fi == nil { 42 | return 43 | } 44 | 45 | if err != nil { 46 | f.Fatal(err) 47 | } 48 | zr, err := zip.NewReader(file, fi.Size()) 49 | if err != nil { 50 | f.Fatal(err) 51 | } 52 | for i, file := range zr.File { 53 | if short && i%10 != 0 { 54 | continue 55 | } 56 | rc, err := file.Open() 57 | if err != nil { 58 | f.Fatal(err) 59 | } 60 | 61 | b, err := io.ReadAll(rc) 62 | if err != nil { 63 | f.Fatal(err) 64 | } 65 | rc.Close() 66 | t := t 67 | if t == TypeOSSFuzz { 68 | t = TypeRaw // Fallback 69 | if len(b) >= 4 { 70 | sz := binary.BigEndian.Uint32(b) 71 | if sz <= uint32(len(b))-4 { 72 | f.Add(b[4 : 4+sz]) 73 | continue 74 | } 75 | } 76 | } 77 | 78 | if bytes.HasPrefix(b, []byte("go test fuzz")) { 79 | t = TypeGoFuzz 80 | } else { 81 | t = TypeRaw 82 | } 83 | 84 | if t == TypeRaw { 85 | f.Add(b) 86 | continue 87 | } 88 | vals, err := unmarshalCorpusFile(b) 89 | if err != nil { 90 | f.Fatal(err) 91 | } 92 | for _, v := range vals { 93 | f.Add(v) 94 | } 95 | } 96 | } 97 | 98 | // ReturnFromZip will read the supplied zip and add all as corpus for f. 99 | // Byte slices only. 100 | func ReturnFromZip(tb testing.TB, filename string, t InputType, fn func([]byte)) { 101 | file, err := os.Open(filename) 102 | if err != nil { 103 | tb.Fatal(err) 104 | } 105 | fi, err := file.Stat() 106 | if fi == nil { 107 | return 108 | } 109 | if err != nil { 110 | tb.Fatal(err) 111 | } 112 | zr, err := zip.NewReader(file, fi.Size()) 113 | if err != nil { 114 | tb.Fatal(err) 115 | } 116 | for _, file := range zr.File { 117 | rc, err := file.Open() 118 | if err != nil { 119 | tb.Fatal(err) 120 | } 121 | 122 | b, err := io.ReadAll(rc) 123 | if err != nil { 124 | tb.Fatal(err) 125 | } 126 | rc.Close() 127 | t := t 128 | if t == TypeOSSFuzz { 129 | t = TypeRaw // Fallback 130 | if len(b) >= 4 { 131 | sz := binary.BigEndian.Uint32(b) 132 | if sz <= uint32(len(b))-4 { 133 | fn(b[4 : 4+sz]) 134 | continue 135 | } 136 | } 137 | } 138 | 139 | if bytes.HasPrefix(b, []byte("go test fuzz")) { 140 | t = TypeGoFuzz 141 | } else { 142 | t = TypeRaw 143 | } 144 | 145 | if t == TypeRaw { 146 | fn(b) 147 | continue 148 | } 149 | vals, err := unmarshalCorpusFile(b) 150 | if err != nil { 151 | tb.Fatal(err) 152 | } 153 | for _, v := range vals { 154 | fn(v) 155 | } 156 | } 157 | } 158 | 159 | // unmarshalCorpusFile decodes corpus bytes into their respective values. 160 | func unmarshalCorpusFile(b []byte) ([][]byte, error) { 161 | if len(b) == 0 { 162 | return nil, fmt.Errorf("cannot unmarshal empty string") 163 | } 164 | lines := bytes.Split(b, []byte("\n")) 165 | if len(lines) < 2 { 166 | return nil, fmt.Errorf("must include version and at least one value") 167 | } 168 | var vals = make([][]byte, 0, len(lines)-1) 169 | for _, line := range lines[1:] { 170 | line = bytes.TrimSpace(line) 171 | if len(line) == 0 { 172 | continue 173 | } 174 | v, err := parseCorpusValue(line) 175 | if err != nil { 176 | return nil, fmt.Errorf("malformed line %q: %v", line, err) 177 | } 178 | vals = append(vals, v) 179 | } 180 | return vals, nil 181 | } 182 | 183 | // parseCorpusValue 184 | func parseCorpusValue(line []byte) ([]byte, error) { 185 | fs := token.NewFileSet() 186 | expr, err := parser.ParseExprFrom(fs, "(test)", line, 0) 187 | if err != nil { 188 | return nil, err 189 | } 190 | call, ok := expr.(*ast.CallExpr) 191 | if !ok { 192 | return nil, fmt.Errorf("expected call expression") 193 | } 194 | if len(call.Args) != 1 { 195 | return nil, fmt.Errorf("expected call expression with 1 argument; got %d", len(call.Args)) 196 | } 197 | arg := call.Args[0] 198 | 199 | if arrayType, ok := call.Fun.(*ast.ArrayType); ok { 200 | if arrayType.Len != nil { 201 | return nil, fmt.Errorf("expected []byte or primitive type") 202 | } 203 | elt, ok := arrayType.Elt.(*ast.Ident) 204 | if !ok || elt.Name != "byte" { 205 | return nil, fmt.Errorf("expected []byte") 206 | } 207 | lit, ok := arg.(*ast.BasicLit) 208 | if !ok || lit.Kind != token.STRING { 209 | return nil, fmt.Errorf("string literal required for type []byte") 210 | } 211 | s, err := strconv.Unquote(lit.Value) 212 | if err != nil { 213 | return nil, err 214 | } 215 | return []byte(s), nil 216 | } 217 | return nil, fmt.Errorf("expected []byte") 218 | } 219 | -------------------------------------------------------------------------------- /compress/internal/godebug/godebug.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package godebug makes the simplified settings in the $GODEBUG environment variable 6 | // available to packages. 7 | // Needed since internal/godebug is not available here. 8 | package godebug 9 | 10 | import "os" 11 | 12 | func Get(key string) string { 13 | s := os.Getenv("GODEBUG") 14 | if s == "" { 15 | return "" 16 | } 17 | // Scan the string backward so that later settings are used 18 | // and earlier settings are ignored. 19 | // Note that a forward scan would cause cached values 20 | // to temporarily use the ignored value before being 21 | // updated to the "correct" one. 22 | end := len(s) 23 | eq := -1 24 | for i := end - 1; i >= -1; i-- { 25 | if i == -1 || s[i] == ',' { 26 | if eq >= 0 { 27 | name, arg := s[i+1:eq], s[eq+1:end] 28 | if name == key { 29 | for j := 0; j < len(arg); j++ { 30 | if arg[j] == '#' { 31 | return arg[:j] 32 | } 33 | } 34 | return arg 35 | } 36 | } 37 | eq = -1 38 | end = i 39 | } else if s[i] == '=' { 40 | eq = i 41 | } 42 | } 43 | return "" 44 | } 45 | -------------------------------------------------------------------------------- /compress/internal/le/le.go: -------------------------------------------------------------------------------- 1 | package le 2 | 3 | type Indexer interface { 4 | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 5 | } 6 | -------------------------------------------------------------------------------- /compress/internal/le/unsafe_disabled.go: -------------------------------------------------------------------------------- 1 | //go:build !(amd64 || arm64 || ppc64le || riscv64) || nounsafe || purego || appengine 2 | 3 | package le 4 | 5 | import ( 6 | "encoding/binary" 7 | ) 8 | 9 | // Load8 will load from b at index i. 10 | func Load8[I Indexer](b []byte, i I) byte { 11 | return b[i] 12 | } 13 | 14 | // Load16 will load from b at index i. 15 | func Load16[I Indexer](b []byte, i I) uint16 { 16 | return binary.LittleEndian.Uint16(b[i:]) 17 | } 18 | 19 | // Load32 will load from b at index i. 20 | func Load32[I Indexer](b []byte, i I) uint32 { 21 | return binary.LittleEndian.Uint32(b[i:]) 22 | } 23 | 24 | // Load64 will load from b at index i. 25 | func Load64[I Indexer](b []byte, i I) uint64 { 26 | return binary.LittleEndian.Uint64(b[i:]) 27 | } 28 | 29 | // Store16 will store v at b. 30 | func Store16(b []byte, v uint16) { 31 | binary.LittleEndian.PutUint16(b, v) 32 | } 33 | 34 | // Store32 will store v at b. 35 | func Store32(b []byte, v uint32) { 36 | binary.LittleEndian.PutUint32(b, v) 37 | } 38 | 39 | // Store64 will store v at b. 40 | func Store64(b []byte, v uint64) { 41 | binary.LittleEndian.PutUint64(b, v) 42 | } 43 | -------------------------------------------------------------------------------- /compress/internal/le/unsafe_enabled.go: -------------------------------------------------------------------------------- 1 | // We enable 64 bit LE platforms: 2 | 3 | //go:build (amd64 || arm64 || ppc64le || riscv64) && !nounsafe && !purego && !appengine 4 | 5 | package le 6 | 7 | import ( 8 | "unsafe" 9 | ) 10 | 11 | // Load8 will load from b at index i. 12 | func Load8[I Indexer](b []byte, i I) byte { 13 | //return binary.LittleEndian.Uint16(b[i:]) 14 | //return *(*uint16)(unsafe.Pointer(&b[i])) 15 | return *(*byte)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(b)), i)) 16 | } 17 | 18 | // Load16 will load from b at index i. 19 | func Load16[I Indexer](b []byte, i I) uint16 { 20 | //return binary.LittleEndian.Uint16(b[i:]) 21 | //return *(*uint16)(unsafe.Pointer(&b[i])) 22 | return *(*uint16)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(b)), i)) 23 | } 24 | 25 | // Load32 will load from b at index i. 26 | func Load32[I Indexer](b []byte, i I) uint32 { 27 | //return binary.LittleEndian.Uint32(b[i:]) 28 | //return *(*uint32)(unsafe.Pointer(&b[i])) 29 | return *(*uint32)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(b)), i)) 30 | } 31 | 32 | // Load64 will load from b at index i. 33 | func Load64[I Indexer](b []byte, i I) uint64 { 34 | //return binary.LittleEndian.Uint64(b[i:]) 35 | //return *(*uint64)(unsafe.Pointer(&b[i])) 36 | return *(*uint64)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(b)), i)) 37 | } 38 | 39 | // Store16 will store v at b. 40 | func Store16(b []byte, v uint16) { 41 | //binary.LittleEndian.PutUint16(b, v) 42 | *(*uint16)(unsafe.Pointer(unsafe.SliceData(b))) = v 43 | } 44 | 45 | // Store32 will store v at b. 46 | func Store32(b []byte, v uint32) { 47 | //binary.LittleEndian.PutUint32(b, v) 48 | *(*uint32)(unsafe.Pointer(unsafe.SliceData(b))) = v 49 | } 50 | 51 | // Store64 will store v at b. 52 | func Store64(b []byte, v uint64) { 53 | //binary.LittleEndian.PutUint64(b, v) 54 | *(*uint64)(unsafe.Pointer(unsafe.SliceData(b))) = v 55 | } 56 | -------------------------------------------------------------------------------- /compress/internal/lz4ref/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Pierre Curto 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of xxHash nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /compress/internal/lz4ref/errors.go: -------------------------------------------------------------------------------- 1 | package lz4ref 2 | 3 | type Error string 4 | 5 | func (e Error) Error() string { return string(e) } 6 | 7 | const ( 8 | ErrInvalidSourceShortBuffer Error = "lz4: invalid source or destination buffer too short" 9 | ) 10 | -------------------------------------------------------------------------------- /compress/internal/race/norace.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !race 6 | 7 | package race 8 | 9 | func ReadSlice[T any](s []T) { 10 | } 11 | 12 | func WriteSlice[T any](s []T) { 13 | } 14 | -------------------------------------------------------------------------------- /compress/internal/race/race.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build race 6 | 7 | package race 8 | 9 | import ( 10 | "runtime" 11 | "unsafe" 12 | ) 13 | 14 | func ReadSlice[T any](s []T) { 15 | if len(s) == 0 { 16 | return 17 | } 18 | runtime.RaceReadRange(unsafe.Pointer(&s[0]), len(s)*int(unsafe.Sizeof(s[0]))) 19 | } 20 | 21 | func WriteSlice[T any](s []T) { 22 | if len(s) == 0 { 23 | return 24 | } 25 | runtime.RaceWriteRange(unsafe.Pointer(&s[0]), len(s)*int(unsafe.Sizeof(s[0]))) 26 | } 27 | -------------------------------------------------------------------------------- /compress/internal/snapref/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /compress/internal/snapref/decode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Snappy-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package snapref 6 | 7 | import ( 8 | "encoding/binary" 9 | "errors" 10 | "io" 11 | ) 12 | 13 | var ( 14 | // ErrCorrupt reports that the input is invalid. 15 | ErrCorrupt = errors.New("snappy: corrupt input") 16 | // ErrTooLarge reports that the uncompressed length is too large. 17 | ErrTooLarge = errors.New("snappy: decoded block is too large") 18 | // ErrUnsupported reports that the input isn't supported. 19 | ErrUnsupported = errors.New("snappy: unsupported input") 20 | 21 | errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length") 22 | ) 23 | 24 | // DecodedLen returns the length of the decoded block. 25 | func DecodedLen(src []byte) (int, error) { 26 | v, _, err := decodedLen(src) 27 | return v, err 28 | } 29 | 30 | // decodedLen returns the length of the decoded block and the number of bytes 31 | // that the length header occupied. 32 | func decodedLen(src []byte) (blockLen, headerLen int, err error) { 33 | v, n := binary.Uvarint(src) 34 | if n <= 0 || v > 0xffffffff { 35 | return 0, 0, ErrCorrupt 36 | } 37 | 38 | const wordSize = 32 << (^uint(0) >> 32 & 1) 39 | if wordSize == 32 && v > 0x7fffffff { 40 | return 0, 0, ErrTooLarge 41 | } 42 | return int(v), n, nil 43 | } 44 | 45 | const ( 46 | decodeErrCodeCorrupt = 1 47 | decodeErrCodeUnsupportedLiteralLength = 2 48 | ) 49 | 50 | // Decode returns the decoded form of src. The returned slice may be a sub- 51 | // slice of dst if dst was large enough to hold the entire decoded block. 52 | // Otherwise, a newly allocated slice will be returned. 53 | // 54 | // The dst and src must not overlap. It is valid to pass a nil dst. 55 | // 56 | // Decode handles the Snappy block format, not the Snappy stream format. 57 | func Decode(dst, src []byte) ([]byte, error) { 58 | dLen, s, err := decodedLen(src) 59 | if err != nil { 60 | return nil, err 61 | } 62 | if dLen <= len(dst) { 63 | dst = dst[:dLen] 64 | } else { 65 | dst = make([]byte, dLen) 66 | } 67 | switch decode(dst, src[s:]) { 68 | case 0: 69 | return dst, nil 70 | case decodeErrCodeUnsupportedLiteralLength: 71 | return nil, errUnsupportedLiteralLength 72 | } 73 | return nil, ErrCorrupt 74 | } 75 | 76 | // NewReader returns a new Reader that decompresses from r, using the framing 77 | // format described at 78 | // https://github.com/google/snappy/blob/master/framing_format.txt 79 | func NewReader(r io.Reader) *Reader { 80 | return &Reader{ 81 | r: r, 82 | decoded: make([]byte, maxBlockSize), 83 | buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize), 84 | } 85 | } 86 | 87 | // Reader is an io.Reader that can read Snappy-compressed bytes. 88 | // 89 | // Reader handles the Snappy stream format, not the Snappy block format. 90 | type Reader struct { 91 | r io.Reader 92 | err error 93 | decoded []byte 94 | buf []byte 95 | // decoded[i:j] contains decoded bytes that have not yet been passed on. 96 | i, j int 97 | readHeader bool 98 | } 99 | 100 | // Reset discards any buffered data, resets all state, and switches the Snappy 101 | // reader to read from r. This permits reusing a Reader rather than allocating 102 | // a new one. 103 | func (r *Reader) Reset(reader io.Reader) { 104 | r.r = reader 105 | r.err = nil 106 | r.i = 0 107 | r.j = 0 108 | r.readHeader = false 109 | } 110 | 111 | func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) { 112 | if _, r.err = io.ReadFull(r.r, p); r.err != nil { 113 | if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { 114 | r.err = ErrCorrupt 115 | } 116 | return false 117 | } 118 | return true 119 | } 120 | 121 | func (r *Reader) fill() error { 122 | for r.i >= r.j { 123 | if !r.readFull(r.buf[:4], true) { 124 | return r.err 125 | } 126 | chunkType := r.buf[0] 127 | if !r.readHeader { 128 | if chunkType != chunkTypeStreamIdentifier { 129 | r.err = ErrCorrupt 130 | return r.err 131 | } 132 | r.readHeader = true 133 | } 134 | chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 135 | if chunkLen > len(r.buf) { 136 | r.err = ErrUnsupported 137 | return r.err 138 | } 139 | 140 | // The chunk types are specified at 141 | // https://github.com/google/snappy/blob/master/framing_format.txt 142 | switch chunkType { 143 | case chunkTypeCompressedData: 144 | // Section 4.2. Compressed data (chunk type 0x00). 145 | if chunkLen < checksumSize { 146 | r.err = ErrCorrupt 147 | return r.err 148 | } 149 | buf := r.buf[:chunkLen] 150 | if !r.readFull(buf, false) { 151 | return r.err 152 | } 153 | checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 154 | buf = buf[checksumSize:] 155 | 156 | n, err := DecodedLen(buf) 157 | if err != nil { 158 | r.err = err 159 | return r.err 160 | } 161 | if n > len(r.decoded) { 162 | r.err = ErrCorrupt 163 | return r.err 164 | } 165 | if _, err := Decode(r.decoded, buf); err != nil { 166 | r.err = err 167 | return r.err 168 | } 169 | if crc(r.decoded[:n]) != checksum { 170 | r.err = ErrCorrupt 171 | return r.err 172 | } 173 | r.i, r.j = 0, n 174 | continue 175 | 176 | case chunkTypeUncompressedData: 177 | // Section 4.3. Uncompressed data (chunk type 0x01). 178 | if chunkLen < checksumSize { 179 | r.err = ErrCorrupt 180 | return r.err 181 | } 182 | buf := r.buf[:checksumSize] 183 | if !r.readFull(buf, false) { 184 | return r.err 185 | } 186 | checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 187 | // Read directly into r.decoded instead of via r.buf. 188 | n := chunkLen - checksumSize 189 | if n > len(r.decoded) { 190 | r.err = ErrCorrupt 191 | return r.err 192 | } 193 | if !r.readFull(r.decoded[:n], false) { 194 | return r.err 195 | } 196 | if crc(r.decoded[:n]) != checksum { 197 | r.err = ErrCorrupt 198 | return r.err 199 | } 200 | r.i, r.j = 0, n 201 | continue 202 | 203 | case chunkTypeStreamIdentifier: 204 | // Section 4.1. Stream identifier (chunk type 0xff). 205 | if chunkLen != len(magicBody) { 206 | r.err = ErrCorrupt 207 | return r.err 208 | } 209 | if !r.readFull(r.buf[:len(magicBody)], false) { 210 | return r.err 211 | } 212 | for i := 0; i < len(magicBody); i++ { 213 | if r.buf[i] != magicBody[i] { 214 | r.err = ErrCorrupt 215 | return r.err 216 | } 217 | } 218 | continue 219 | } 220 | 221 | if chunkType <= 0x7f { 222 | // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). 223 | r.err = ErrUnsupported 224 | return r.err 225 | } 226 | // Section 4.4 Padding (chunk type 0xfe). 227 | // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). 228 | if !r.readFull(r.buf[:chunkLen], false) { 229 | return r.err 230 | } 231 | } 232 | 233 | return nil 234 | } 235 | 236 | // Read satisfies the io.Reader interface. 237 | func (r *Reader) Read(p []byte) (int, error) { 238 | if r.err != nil { 239 | return 0, r.err 240 | } 241 | 242 | if err := r.fill(); err != nil { 243 | return 0, err 244 | } 245 | 246 | n := copy(p, r.decoded[r.i:r.j]) 247 | r.i += n 248 | return n, nil 249 | } 250 | 251 | // ReadByte satisfies the io.ByteReader interface. 252 | func (r *Reader) ReadByte() (byte, error) { 253 | if r.err != nil { 254 | return 0, r.err 255 | } 256 | 257 | if err := r.fill(); err != nil { 258 | return 0, err 259 | } 260 | 261 | c := r.decoded[r.i] 262 | r.i++ 263 | return c, nil 264 | } 265 | -------------------------------------------------------------------------------- /compress/internal/snapref/decode_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Snappy-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package snapref 6 | 7 | // decode writes the decoding of src to dst. It assumes that the varint-encoded 8 | // length of the decompressed bytes has already been read, and that len(dst) 9 | // equals that length. 10 | // 11 | // It returns 0 on success or a decodeErrCodeXxx error code on failure. 12 | func decode(dst, src []byte) int { 13 | var d, s, offset, length int 14 | for s < len(src) { 15 | switch src[s] & 0x03 { 16 | case tagLiteral: 17 | x := uint32(src[s] >> 2) 18 | switch { 19 | case x < 60: 20 | s++ 21 | case x == 60: 22 | s += 2 23 | if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. 24 | return decodeErrCodeCorrupt 25 | } 26 | x = uint32(src[s-1]) 27 | case x == 61: 28 | s += 3 29 | if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. 30 | return decodeErrCodeCorrupt 31 | } 32 | x = uint32(src[s-2]) | uint32(src[s-1])<<8 33 | case x == 62: 34 | s += 4 35 | if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. 36 | return decodeErrCodeCorrupt 37 | } 38 | x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 39 | case x == 63: 40 | s += 5 41 | if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. 42 | return decodeErrCodeCorrupt 43 | } 44 | x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 45 | } 46 | length = int(x) + 1 47 | if length <= 0 { 48 | return decodeErrCodeUnsupportedLiteralLength 49 | } 50 | if length > len(dst)-d || length > len(src)-s { 51 | return decodeErrCodeCorrupt 52 | } 53 | copy(dst[d:], src[s:s+length]) 54 | d += length 55 | s += length 56 | continue 57 | 58 | case tagCopy1: 59 | s += 2 60 | if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. 61 | return decodeErrCodeCorrupt 62 | } 63 | length = 4 + int(src[s-2])>>2&0x7 64 | offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) 65 | 66 | case tagCopy2: 67 | s += 3 68 | if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. 69 | return decodeErrCodeCorrupt 70 | } 71 | length = 1 + int(src[s-3])>>2 72 | offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) 73 | 74 | case tagCopy4: 75 | s += 5 76 | if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. 77 | return decodeErrCodeCorrupt 78 | } 79 | length = 1 + int(src[s-5])>>2 80 | offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) 81 | } 82 | 83 | if offset <= 0 || d < offset || length > len(dst)-d { 84 | return decodeErrCodeCorrupt 85 | } 86 | // Copy from an earlier sub-slice of dst to a later sub-slice. 87 | // If no overlap, use the built-in copy: 88 | if offset >= length { 89 | copy(dst[d:d+length], dst[d-offset:]) 90 | d += length 91 | continue 92 | } 93 | 94 | // Unlike the built-in copy function, this byte-by-byte copy always runs 95 | // forwards, even if the slices overlap. Conceptually, this is: 96 | // 97 | // d += forwardCopy(dst[d:d+length], dst[d-offset:]) 98 | // 99 | // We align the slices into a and b and show the compiler they are the same size. 100 | // This allows the loop to run without bounds checks. 101 | a := dst[d : d+length] 102 | b := dst[d-offset:] 103 | b = b[:len(a)] 104 | for i := range a { 105 | a[i] = b[i] 106 | } 107 | d += length 108 | } 109 | if d != len(dst) { 110 | return decodeErrCodeCorrupt 111 | } 112 | return 0 113 | } 114 | -------------------------------------------------------------------------------- /compress/internal/snapref/encode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Snappy-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package snapref 6 | 7 | import ( 8 | "encoding/binary" 9 | "errors" 10 | "io" 11 | ) 12 | 13 | // Encode returns the encoded form of src. The returned slice may be a sub- 14 | // slice of dst if dst was large enough to hold the entire encoded block. 15 | // Otherwise, a newly allocated slice will be returned. 16 | // 17 | // The dst and src must not overlap. It is valid to pass a nil dst. 18 | // 19 | // Encode handles the Snappy block format, not the Snappy stream format. 20 | func Encode(dst, src []byte) []byte { 21 | if n := MaxEncodedLen(len(src)); n < 0 { 22 | panic(ErrTooLarge) 23 | } else if len(dst) < n { 24 | dst = make([]byte, n) 25 | } 26 | 27 | // The block starts with the varint-encoded length of the decompressed bytes. 28 | d := binary.PutUvarint(dst, uint64(len(src))) 29 | 30 | for len(src) > 0 { 31 | p := src 32 | src = nil 33 | if len(p) > maxBlockSize { 34 | p, src = p[:maxBlockSize], p[maxBlockSize:] 35 | } 36 | if len(p) < minNonLiteralBlockSize { 37 | d += emitLiteral(dst[d:], p) 38 | } else { 39 | d += encodeBlock(dst[d:], p) 40 | } 41 | } 42 | return dst[:d] 43 | } 44 | 45 | // inputMargin is the minimum number of extra input bytes to keep, inside 46 | // encodeBlock's inner loop. On some architectures, this margin lets us 47 | // implement a fast path for emitLiteral, where the copy of short (<= 16 byte) 48 | // literals can be implemented as a single load to and store from a 16-byte 49 | // register. That literal's actual length can be as short as 1 byte, so this 50 | // can copy up to 15 bytes too much, but that's OK as subsequent iterations of 51 | // the encoding loop will fix up the copy overrun, and this inputMargin ensures 52 | // that we don't overrun the dst and src buffers. 53 | const inputMargin = 16 - 1 54 | 55 | // minNonLiteralBlockSize is the minimum size of the input to encodeBlock that 56 | // could be encoded with a copy tag. This is the minimum with respect to the 57 | // algorithm used by encodeBlock, not a minimum enforced by the file format. 58 | // 59 | // The encoded output must start with at least a 1 byte literal, as there are 60 | // no previous bytes to copy. A minimal (1 byte) copy after that, generated 61 | // from an emitCopy call in encodeBlock's main loop, would require at least 62 | // another inputMargin bytes, for the reason above: we want any emitLiteral 63 | // calls inside encodeBlock's main loop to use the fast path if possible, which 64 | // requires being able to overrun by inputMargin bytes. Thus, 65 | // minNonLiteralBlockSize equals 1 + 1 + inputMargin. 66 | // 67 | // The C++ code doesn't use this exact threshold, but it could, as discussed at 68 | // https://groups.google.com/d/topic/snappy-compression/oGbhsdIJSJ8/discussion 69 | // The difference between Go (2+inputMargin) and C++ (inputMargin) is purely an 70 | // optimization. It should not affect the encoded form. This is tested by 71 | // TestSameEncodingAsCppShortCopies. 72 | const minNonLiteralBlockSize = 1 + 1 + inputMargin 73 | 74 | // MaxEncodedLen returns the maximum length of a snappy block, given its 75 | // uncompressed length. 76 | // 77 | // It will return a negative value if srcLen is too large to encode. 78 | func MaxEncodedLen(srcLen int) int { 79 | n := uint64(srcLen) 80 | if n > 0xffffffff { 81 | return -1 82 | } 83 | // Compressed data can be defined as: 84 | // compressed := item* literal* 85 | // item := literal* copy 86 | // 87 | // The trailing literal sequence has a space blowup of at most 62/60 88 | // since a literal of length 60 needs one tag byte + one extra byte 89 | // for length information. 90 | // 91 | // Item blowup is trickier to measure. Suppose the "copy" op copies 92 | // 4 bytes of data. Because of a special check in the encoding code, 93 | // we produce a 4-byte copy only if the offset is < 65536. Therefore 94 | // the copy op takes 3 bytes to encode, and this type of item leads 95 | // to at most the 62/60 blowup for representing literals. 96 | // 97 | // Suppose the "copy" op copies 5 bytes of data. If the offset is big 98 | // enough, it will take 5 bytes to encode the copy op. Therefore the 99 | // worst case here is a one-byte literal followed by a five-byte copy. 100 | // That is, 6 bytes of input turn into 7 bytes of "compressed" data. 101 | // 102 | // This last factor dominates the blowup, so the final estimate is: 103 | n = 32 + n + n/6 104 | if n > 0xffffffff { 105 | return -1 106 | } 107 | return int(n) 108 | } 109 | 110 | var errClosed = errors.New("snappy: Writer is closed") 111 | 112 | // NewWriter returns a new Writer that compresses to w. 113 | // 114 | // The Writer returned does not buffer writes. There is no need to Flush or 115 | // Close such a Writer. 116 | // 117 | // Deprecated: the Writer returned is not suitable for many small writes, only 118 | // for few large writes. Use NewBufferedWriter instead, which is efficient 119 | // regardless of the frequency and shape of the writes, and remember to Close 120 | // that Writer when done. 121 | func NewWriter(w io.Writer) *Writer { 122 | return &Writer{ 123 | w: w, 124 | obuf: make([]byte, obufLen), 125 | } 126 | } 127 | 128 | // NewBufferedWriter returns a new Writer that compresses to w, using the 129 | // framing format described at 130 | // https://github.com/google/snappy/blob/master/framing_format.txt 131 | // 132 | // The Writer returned buffers writes. Users must call Close to guarantee all 133 | // data has been forwarded to the underlying io.Writer. They may also call 134 | // Flush zero or more times before calling Close. 135 | func NewBufferedWriter(w io.Writer) *Writer { 136 | return &Writer{ 137 | w: w, 138 | ibuf: make([]byte, 0, maxBlockSize), 139 | obuf: make([]byte, obufLen), 140 | } 141 | } 142 | 143 | // Writer is an io.Writer that can write Snappy-compressed bytes. 144 | // 145 | // Writer handles the Snappy stream format, not the Snappy block format. 146 | type Writer struct { 147 | w io.Writer 148 | err error 149 | 150 | // ibuf is a buffer for the incoming (uncompressed) bytes. 151 | // 152 | // Its use is optional. For backwards compatibility, Writers created by the 153 | // NewWriter function have ibuf == nil, do not buffer incoming bytes, and 154 | // therefore do not need to be Flush'ed or Close'd. 155 | ibuf []byte 156 | 157 | // obuf is a buffer for the outgoing (compressed) bytes. 158 | obuf []byte 159 | 160 | // wroteStreamHeader is whether we have written the stream header. 161 | wroteStreamHeader bool 162 | } 163 | 164 | // Reset discards the writer's state and switches the Snappy writer to write to 165 | // w. This permits reusing a Writer rather than allocating a new one. 166 | func (w *Writer) Reset(writer io.Writer) { 167 | w.w = writer 168 | w.err = nil 169 | if w.ibuf != nil { 170 | w.ibuf = w.ibuf[:0] 171 | } 172 | w.wroteStreamHeader = false 173 | } 174 | 175 | // Write satisfies the io.Writer interface. 176 | func (w *Writer) Write(p []byte) (nRet int, errRet error) { 177 | if w.ibuf == nil { 178 | // Do not buffer incoming bytes. This does not perform or compress well 179 | // if the caller of Writer.Write writes many small slices. This 180 | // behavior is therefore deprecated, but still supported for backwards 181 | // compatibility with code that doesn't explicitly Flush or Close. 182 | return w.write(p) 183 | } 184 | 185 | // The remainder of this method is based on bufio.Writer.Write from the 186 | // standard library. 187 | 188 | for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err == nil { 189 | var n int 190 | if len(w.ibuf) == 0 { 191 | // Large write, empty buffer. 192 | // Write directly from p to avoid copy. 193 | n, _ = w.write(p) 194 | } else { 195 | n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) 196 | w.ibuf = w.ibuf[:len(w.ibuf)+n] 197 | w.Flush() 198 | } 199 | nRet += n 200 | p = p[n:] 201 | } 202 | if w.err != nil { 203 | return nRet, w.err 204 | } 205 | n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) 206 | w.ibuf = w.ibuf[:len(w.ibuf)+n] 207 | nRet += n 208 | return nRet, nil 209 | } 210 | 211 | func (w *Writer) write(p []byte) (nRet int, errRet error) { 212 | if w.err != nil { 213 | return 0, w.err 214 | } 215 | for len(p) > 0 { 216 | obufStart := len(magicChunk) 217 | if !w.wroteStreamHeader { 218 | w.wroteStreamHeader = true 219 | copy(w.obuf, magicChunk) 220 | obufStart = 0 221 | } 222 | 223 | var uncompressed []byte 224 | if len(p) > maxBlockSize { 225 | uncompressed, p = p[:maxBlockSize], p[maxBlockSize:] 226 | } else { 227 | uncompressed, p = p, nil 228 | } 229 | checksum := crc(uncompressed) 230 | 231 | // Compress the buffer, discarding the result if the improvement 232 | // isn't at least 12.5%. 233 | compressed := Encode(w.obuf[obufHeaderLen:], uncompressed) 234 | chunkType := uint8(chunkTypeCompressedData) 235 | chunkLen := 4 + len(compressed) 236 | obufEnd := obufHeaderLen + len(compressed) 237 | if len(compressed) >= len(uncompressed)-len(uncompressed)/8 { 238 | chunkType = chunkTypeUncompressedData 239 | chunkLen = 4 + len(uncompressed) 240 | obufEnd = obufHeaderLen 241 | } 242 | 243 | // Fill in the per-chunk header that comes before the body. 244 | w.obuf[len(magicChunk)+0] = chunkType 245 | w.obuf[len(magicChunk)+1] = uint8(chunkLen >> 0) 246 | w.obuf[len(magicChunk)+2] = uint8(chunkLen >> 8) 247 | w.obuf[len(magicChunk)+3] = uint8(chunkLen >> 16) 248 | w.obuf[len(magicChunk)+4] = uint8(checksum >> 0) 249 | w.obuf[len(magicChunk)+5] = uint8(checksum >> 8) 250 | w.obuf[len(magicChunk)+6] = uint8(checksum >> 16) 251 | w.obuf[len(magicChunk)+7] = uint8(checksum >> 24) 252 | 253 | if _, err := w.w.Write(w.obuf[obufStart:obufEnd]); err != nil { 254 | w.err = err 255 | return nRet, err 256 | } 257 | if chunkType == chunkTypeUncompressedData { 258 | if _, err := w.w.Write(uncompressed); err != nil { 259 | w.err = err 260 | return nRet, err 261 | } 262 | } 263 | nRet += len(uncompressed) 264 | } 265 | return nRet, nil 266 | } 267 | 268 | // Flush flushes the Writer to its underlying io.Writer. 269 | func (w *Writer) Flush() error { 270 | if w.err != nil { 271 | return w.err 272 | } 273 | if len(w.ibuf) == 0 { 274 | return nil 275 | } 276 | w.write(w.ibuf) 277 | w.ibuf = w.ibuf[:0] 278 | return w.err 279 | } 280 | 281 | // Close calls Flush and then closes the Writer. 282 | func (w *Writer) Close() error { 283 | w.Flush() 284 | ret := w.err 285 | if w.err == nil { 286 | w.err = errClosed 287 | } 288 | return ret 289 | } 290 | -------------------------------------------------------------------------------- /compress/internal/snapref/encode_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Snappy-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package snapref 6 | 7 | func load32(b []byte, i int) uint32 { 8 | b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line. 9 | return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 10 | } 11 | 12 | func load64(b []byte, i int) uint64 { 13 | b = b[i : i+8 : len(b)] // Help the compiler eliminate bounds checks on the next line. 14 | return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | 15 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 16 | } 17 | 18 | // emitLiteral writes a literal chunk and returns the number of bytes written. 19 | // 20 | // It assumes that: 21 | // 22 | // dst is long enough to hold the encoded bytes 23 | // 1 <= len(lit) && len(lit) <= 65536 24 | func emitLiteral(dst, lit []byte) int { 25 | i, n := 0, uint(len(lit)-1) 26 | switch { 27 | case n < 60: 28 | dst[0] = uint8(n)<<2 | tagLiteral 29 | i = 1 30 | case n < 1<<8: 31 | dst[0] = 60<<2 | tagLiteral 32 | dst[1] = uint8(n) 33 | i = 2 34 | default: 35 | dst[0] = 61<<2 | tagLiteral 36 | dst[1] = uint8(n) 37 | dst[2] = uint8(n >> 8) 38 | i = 3 39 | } 40 | return i + copy(dst[i:], lit) 41 | } 42 | 43 | // emitCopy writes a copy chunk and returns the number of bytes written. 44 | // 45 | // It assumes that: 46 | // 47 | // dst is long enough to hold the encoded bytes 48 | // 1 <= offset && offset <= 65535 49 | // 4 <= length && length <= 65535 50 | func emitCopy(dst []byte, offset, length int) int { 51 | i := 0 52 | // The maximum length for a single tagCopy1 or tagCopy2 op is 64 bytes. The 53 | // threshold for this loop is a little higher (at 68 = 64 + 4), and the 54 | // length emitted down below is a little lower (at 60 = 64 - 4), because 55 | // it's shorter to encode a length 67 copy as a length 60 tagCopy2 followed 56 | // by a length 7 tagCopy1 (which encodes as 3+2 bytes) than to encode it as 57 | // a length 64 tagCopy2 followed by a length 3 tagCopy2 (which encodes as 58 | // 3+3 bytes). The magic 4 in the 64±4 is because the minimum length for a 59 | // tagCopy1 op is 4 bytes, which is why a length 3 copy has to be an 60 | // encodes-as-3-bytes tagCopy2 instead of an encodes-as-2-bytes tagCopy1. 61 | for length >= 68 { 62 | // Emit a length 64 copy, encoded as 3 bytes. 63 | dst[i+0] = 63<<2 | tagCopy2 64 | dst[i+1] = uint8(offset) 65 | dst[i+2] = uint8(offset >> 8) 66 | i += 3 67 | length -= 64 68 | } 69 | if length > 64 { 70 | // Emit a length 60 copy, encoded as 3 bytes. 71 | dst[i+0] = 59<<2 | tagCopy2 72 | dst[i+1] = uint8(offset) 73 | dst[i+2] = uint8(offset >> 8) 74 | i += 3 75 | length -= 60 76 | } 77 | if length >= 12 || offset >= 2048 { 78 | // Emit the remaining copy, encoded as 3 bytes. 79 | dst[i+0] = uint8(length-1)<<2 | tagCopy2 80 | dst[i+1] = uint8(offset) 81 | dst[i+2] = uint8(offset >> 8) 82 | return i + 3 83 | } 84 | // Emit the remaining copy, encoded as 2 bytes. 85 | dst[i+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 86 | dst[i+1] = uint8(offset) 87 | return i + 2 88 | } 89 | 90 | func hash(u, shift uint32) uint32 { 91 | return (u * 0x1e35a7bd) >> shift 92 | } 93 | 94 | // EncodeBlockInto exposes encodeBlock but checks dst size. 95 | func EncodeBlockInto(dst, src []byte) (d int) { 96 | if MaxEncodedLen(len(src)) > len(dst) { 97 | return 0 98 | } 99 | 100 | // encodeBlock breaks on too big blocks, so split. 101 | for len(src) > 0 { 102 | p := src 103 | src = nil 104 | if len(p) > maxBlockSize { 105 | p, src = p[:maxBlockSize], p[maxBlockSize:] 106 | } 107 | if len(p) < minNonLiteralBlockSize { 108 | d += emitLiteral(dst[d:], p) 109 | } else { 110 | d += encodeBlock(dst[d:], p) 111 | } 112 | } 113 | return d 114 | } 115 | 116 | // encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It 117 | // assumes that the varint-encoded length of the decompressed bytes has already 118 | // been written. 119 | // 120 | // It also assumes that: 121 | // 122 | // len(dst) >= MaxEncodedLen(len(src)) && 123 | // minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize 124 | func encodeBlock(dst, src []byte) (d int) { 125 | // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. 126 | // The table element type is uint16, as s < sLimit and sLimit < len(src) 127 | // and len(src) <= maxBlockSize and maxBlockSize == 65536. 128 | const ( 129 | maxTableSize = 1 << 14 130 | // tableMask is redundant, but helps the compiler eliminate bounds 131 | // checks. 132 | tableMask = maxTableSize - 1 133 | ) 134 | shift := uint32(32 - 8) 135 | for tableSize := 1 << 8; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { 136 | shift-- 137 | } 138 | // In Go, all array elements are zero-initialized, so there is no advantage 139 | // to a smaller tableSize per se. However, it matches the C++ algorithm, 140 | // and in the asm versions of this code, we can get away with zeroing only 141 | // the first tableSize elements. 142 | var table [maxTableSize]uint16 143 | 144 | // sLimit is when to stop looking for offset/length copies. The inputMargin 145 | // lets us use a fast path for emitLiteral in the main loop, while we are 146 | // looking for copies. 147 | sLimit := len(src) - inputMargin 148 | 149 | // nextEmit is where in src the next emitLiteral should start from. 150 | nextEmit := 0 151 | 152 | // The encoded form must start with a literal, as there are no previous 153 | // bytes to copy, so we start looking for hash matches at s == 1. 154 | s := 1 155 | nextHash := hash(load32(src, s), shift) 156 | 157 | for { 158 | // Copied from the C++ snappy implementation: 159 | // 160 | // Heuristic match skipping: If 32 bytes are scanned with no matches 161 | // found, start looking only at every other byte. If 32 more bytes are 162 | // scanned (or skipped), look at every third byte, etc.. When a match 163 | // is found, immediately go back to looking at every byte. This is a 164 | // small loss (~5% performance, ~0.1% density) for compressible data 165 | // due to more bookkeeping, but for non-compressible data (such as 166 | // JPEG) it's a huge win since the compressor quickly "realizes" the 167 | // data is incompressible and doesn't bother looking for matches 168 | // everywhere. 169 | // 170 | // The "skip" variable keeps track of how many bytes there are since 171 | // the last match; dividing it by 32 (ie. right-shifting by five) gives 172 | // the number of bytes to move ahead for each iteration. 173 | skip := 32 174 | 175 | nextS := s 176 | candidate := 0 177 | for { 178 | s = nextS 179 | bytesBetweenHashLookups := skip >> 5 180 | nextS = s + bytesBetweenHashLookups 181 | skip += bytesBetweenHashLookups 182 | if nextS > sLimit { 183 | goto emitRemainder 184 | } 185 | candidate = int(table[nextHash&tableMask]) 186 | table[nextHash&tableMask] = uint16(s) 187 | nextHash = hash(load32(src, nextS), shift) 188 | if load32(src, s) == load32(src, candidate) { 189 | break 190 | } 191 | } 192 | 193 | // A 4-byte match has been found. We'll later see if more than 4 bytes 194 | // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit 195 | // them as literal bytes. 196 | d += emitLiteral(dst[d:], src[nextEmit:s]) 197 | 198 | // Call emitCopy, and then see if another emitCopy could be our next 199 | // move. Repeat until we find no match for the input immediately after 200 | // what was consumed by the last emitCopy call. 201 | // 202 | // If we exit this loop normally then we need to call emitLiteral next, 203 | // though we don't yet know how big the literal will be. We handle that 204 | // by proceeding to the next iteration of the main loop. We also can 205 | // exit this loop via goto if we get close to exhausting the input. 206 | for { 207 | // Invariant: we have a 4-byte match at s, and no need to emit any 208 | // literal bytes prior to s. 209 | base := s 210 | 211 | // Extend the 4-byte match as long as possible. 212 | // 213 | // This is an inlined version of: 214 | // s = extendMatch(src, candidate+4, s+4) 215 | s += 4 216 | for i := candidate + 4; s < len(src) && src[i] == src[s]; i, s = i+1, s+1 { 217 | } 218 | 219 | d += emitCopy(dst[d:], base-candidate, s-base) 220 | nextEmit = s 221 | if s >= sLimit { 222 | goto emitRemainder 223 | } 224 | 225 | // We could immediately start working at s now, but to improve 226 | // compression we first update the hash table at s-1 and at s. If 227 | // another emitCopy is not our next move, also calculate nextHash 228 | // at s+1. At least on GOARCH=amd64, these three hash calculations 229 | // are faster as one load64 call (with some shifts) instead of 230 | // three load32 calls. 231 | x := load64(src, s-1) 232 | prevHash := hash(uint32(x>>0), shift) 233 | table[prevHash&tableMask] = uint16(s - 1) 234 | currHash := hash(uint32(x>>8), shift) 235 | candidate = int(table[currHash&tableMask]) 236 | table[currHash&tableMask] = uint16(s) 237 | if uint32(x>>8) != load32(src, candidate) { 238 | nextHash = hash(uint32(x>>16), shift) 239 | s++ 240 | break 241 | } 242 | } 243 | } 244 | 245 | emitRemainder: 246 | if nextEmit < len(src) { 247 | d += emitLiteral(dst[d:], src[nextEmit:]) 248 | } 249 | return d 250 | } 251 | -------------------------------------------------------------------------------- /compress/internal/snapref/snappy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Snappy-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package snapref implements the Snappy compression format. It aims for very 6 | // high speeds and reasonable compression. 7 | // 8 | // There are actually two Snappy formats: block and stream. They are related, 9 | // but different: trying to decompress block-compressed data as a Snappy stream 10 | // will fail, and vice versa. The block format is the Decode and Encode 11 | // functions and the stream format is the Reader and Writer types. 12 | // 13 | // The block format, the more common case, is used when the complete size (the 14 | // number of bytes) of the original data is known upfront, at the time 15 | // compression starts. The stream format, also known as the framing format, is 16 | // for when that isn't always true. 17 | // 18 | // The canonical, C++ implementation is at https://github.com/google/snappy and 19 | // it only implements the block format. 20 | package snapref 21 | 22 | import ( 23 | "hash/crc32" 24 | ) 25 | 26 | /* 27 | Each encoded block begins with the varint-encoded length of the decoded data, 28 | followed by a sequence of chunks. Chunks begin and end on byte boundaries. The 29 | first byte of each chunk is broken into its 2 least and 6 most significant bits 30 | called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. 31 | Zero means a literal tag. All other values mean a copy tag. 32 | 33 | For literal tags: 34 | - If m < 60, the next 1 + m bytes are literal bytes. 35 | - Otherwise, let n be the little-endian unsigned integer denoted by the next 36 | m - 59 bytes. The next 1 + n bytes after that are literal bytes. 37 | 38 | For copy tags, length bytes are copied from offset bytes ago, in the style of 39 | Lempel-Ziv compression algorithms. In particular: 40 | - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). 41 | The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 42 | of the offset. The next byte is bits 0-7 of the offset. 43 | - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). 44 | The length is 1 + m. The offset is the little-endian unsigned integer 45 | denoted by the next 2 bytes. 46 | - For l == 3, this tag is a legacy format that is no longer issued by most 47 | encoders. Nonetheless, the offset ranges in [0, 1<<32) and the length in 48 | [1, 65). The length is 1 + m. The offset is the little-endian unsigned 49 | integer denoted by the next 4 bytes. 50 | */ 51 | const ( 52 | tagLiteral = 0x00 53 | tagCopy1 = 0x01 54 | tagCopy2 = 0x02 55 | tagCopy4 = 0x03 56 | ) 57 | 58 | const ( 59 | checksumSize = 4 60 | chunkHeaderSize = 4 61 | magicChunk = "\xff\x06\x00\x00" + magicBody 62 | magicBody = "sNaPpY" 63 | 64 | // maxBlockSize is the maximum size of the input to encodeBlock. It is not 65 | // part of the wire format per se, but some parts of the encoder assume 66 | // that an offset fits into a uint16. 67 | // 68 | // Also, for the framing format (Writer type instead of Encode function), 69 | // https://github.com/google/snappy/blob/master/framing_format.txt says 70 | // that "the uncompressed data in a chunk must be no longer than 65536 71 | // bytes". 72 | maxBlockSize = 65536 73 | 74 | // maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is 75 | // hard coded to be a const instead of a variable, so that obufLen can also 76 | // be a const. Their equivalence is confirmed by 77 | // TestMaxEncodedLenOfMaxBlockSize. 78 | maxEncodedLenOfMaxBlockSize = 76490 79 | 80 | obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize 81 | obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize 82 | ) 83 | 84 | const ( 85 | chunkTypeCompressedData = 0x00 86 | chunkTypeUncompressedData = 0x01 87 | chunkTypePadding = 0xfe 88 | chunkTypeStreamIdentifier = 0xff 89 | ) 90 | 91 | var crcTable = crc32.MakeTable(crc32.Castagnoli) 92 | 93 | // crc implements the checksum specified in section 3 of 94 | // https://github.com/google/snappy/blob/master/framing_format.txt 95 | func crc(b []byte) uint32 { 96 | c := crc32.Update(0, crcTable, b) 97 | return uint32(c>>15|c<<17) + 0xa282ead8 98 | } 99 | -------------------------------------------------------------------------------- /compress/zstd/bitreader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019+ Klaus Post. All rights reserved. 2 | // License information can be found in the LICENSE file. 3 | // Based on work by Yann Collet, released under BSD License. 4 | 5 | package zstd 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "io" 11 | "math/bits" 12 | 13 | "github.com/klauspost/compress/internal/le" 14 | ) 15 | 16 | // bitReader reads a bitstream in reverse. 17 | // The last set bit indicates the start of the stream and is used 18 | // for aligning the input. 19 | type bitReader struct { 20 | in []byte 21 | value uint64 // Maybe use [16]byte, but shifting is awkward. 22 | cursor int // offset where next read should end 23 | bitsRead uint8 24 | } 25 | 26 | // init initializes and resets the bit reader. 27 | func (b *bitReader) init(in []byte) error { 28 | if len(in) < 1 { 29 | return errors.New("corrupt stream: too short") 30 | } 31 | b.in = in 32 | // The highest bit of the last byte indicates where to start 33 | v := in[len(in)-1] 34 | if v == 0 { 35 | return errors.New("corrupt stream, did not find end of stream") 36 | } 37 | b.cursor = len(in) 38 | b.bitsRead = 64 39 | b.value = 0 40 | if len(in) >= 8 { 41 | b.fillFastStart() 42 | } else { 43 | b.fill() 44 | b.fill() 45 | } 46 | b.bitsRead += 8 - uint8(highBits(uint32(v))) 47 | return nil 48 | } 49 | 50 | // getBits will return n bits. n can be 0. 51 | func (b *bitReader) getBits(n uint8) int { 52 | if n == 0 /*|| b.bitsRead >= 64 */ { 53 | return 0 54 | } 55 | return int(b.get32BitsFast(n)) 56 | } 57 | 58 | // get32BitsFast requires that at least one bit is requested every time. 59 | // There are no checks if the buffer is filled. 60 | func (b *bitReader) get32BitsFast(n uint8) uint32 { 61 | const regMask = 64 - 1 62 | v := uint32((b.value << (b.bitsRead & regMask)) >> ((regMask + 1 - n) & regMask)) 63 | b.bitsRead += n 64 | return v 65 | } 66 | 67 | // fillFast() will make sure at least 32 bits are available. 68 | // There must be at least 4 bytes available. 69 | func (b *bitReader) fillFast() { 70 | if b.bitsRead < 32 { 71 | return 72 | } 73 | b.cursor -= 4 74 | b.value = (b.value << 32) | uint64(le.Load32(b.in, b.cursor)) 75 | b.bitsRead -= 32 76 | } 77 | 78 | // fillFastStart() assumes the bitreader is empty and there is at least 8 bytes to read. 79 | func (b *bitReader) fillFastStart() { 80 | b.cursor -= 8 81 | b.value = le.Load64(b.in, b.cursor) 82 | b.bitsRead = 0 83 | } 84 | 85 | // fill() will make sure at least 32 bits are available. 86 | func (b *bitReader) fill() { 87 | if b.bitsRead < 32 { 88 | return 89 | } 90 | if b.cursor >= 4 { 91 | b.cursor -= 4 92 | b.value = (b.value << 32) | uint64(le.Load32(b.in, b.cursor)) 93 | b.bitsRead -= 32 94 | return 95 | } 96 | 97 | b.bitsRead -= uint8(8 * b.cursor) 98 | for b.cursor > 0 { 99 | b.cursor -= 1 100 | b.value = (b.value << 8) | uint64(b.in[b.cursor]) 101 | } 102 | } 103 | 104 | // finished returns true if all bits have been read from the bit stream. 105 | func (b *bitReader) finished() bool { 106 | return b.cursor == 0 && b.bitsRead >= 64 107 | } 108 | 109 | // overread returns true if more bits have been requested than is on the stream. 110 | func (b *bitReader) overread() bool { 111 | return b.bitsRead > 64 112 | } 113 | 114 | // remain returns the number of bits remaining. 115 | func (b *bitReader) remain() uint { 116 | return 8*uint(b.cursor) + 64 - uint(b.bitsRead) 117 | } 118 | 119 | // close the bitstream and returns an error if out-of-buffer reads occurred. 120 | func (b *bitReader) close() error { 121 | // Release reference. 122 | b.in = nil 123 | b.cursor = 0 124 | if !b.finished() { 125 | return fmt.Errorf("%d extra bits on block, should be 0", b.remain()) 126 | } 127 | if b.bitsRead > 64 { 128 | return io.ErrUnexpectedEOF 129 | } 130 | return nil 131 | } 132 | 133 | func highBits(val uint32) (n uint32) { 134 | return uint32(bits.Len32(val) - 1) 135 | } 136 | -------------------------------------------------------------------------------- /compress/zstd/bitwriter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Klaus Post. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. 5 | 6 | package zstd 7 | 8 | // bitWriter will write bits. 9 | // First bit will be LSB of the first byte of output. 10 | type bitWriter struct { 11 | bitContainer uint64 12 | nBits uint8 13 | out []byte 14 | } 15 | 16 | // bitMask16 is bitmasks. Has extra to avoid bounds check. 17 | var bitMask16 = [32]uint16{ 18 | 0, 1, 3, 7, 0xF, 0x1F, 19 | 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 20 | 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0xFFFF, 21 | 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 22 | 0xFFFF, 0xFFFF} /* up to 16 bits */ 23 | 24 | var bitMask32 = [32]uint32{ 25 | 0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 26 | 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 27 | 0x1ffff, 0x3ffff, 0x7FFFF, 0xfFFFF, 0x1fFFFF, 0x3fFFFF, 0x7fFFFF, 0xffFFFF, 28 | 0x1ffFFFF, 0x3ffFFFF, 0x7ffFFFF, 0xfffFFFF, 0x1fffFFFF, 0x3fffFFFF, 0x7fffFFFF, 29 | } // up to 32 bits 30 | 31 | // addBits16NC will add up to 16 bits. 32 | // It will not check if there is space for them, 33 | // so the caller must ensure that it has flushed recently. 34 | func (b *bitWriter) addBits16NC(value uint16, bits uint8) { 35 | b.bitContainer |= uint64(value&bitMask16[bits&31]) << (b.nBits & 63) 36 | b.nBits += bits 37 | } 38 | 39 | // addBits32NC will add up to 31 bits. 40 | // It will not check if there is space for them, 41 | // so the caller must ensure that it has flushed recently. 42 | func (b *bitWriter) addBits32NC(value uint32, bits uint8) { 43 | b.bitContainer |= uint64(value&bitMask32[bits&31]) << (b.nBits & 63) 44 | b.nBits += bits 45 | } 46 | 47 | // addBits64NC will add up to 64 bits. 48 | // There must be space for 32 bits. 49 | func (b *bitWriter) addBits64NC(value uint64, bits uint8) { 50 | if bits <= 31 { 51 | b.addBits32Clean(uint32(value), bits) 52 | return 53 | } 54 | b.addBits32Clean(uint32(value), 32) 55 | b.flush32() 56 | b.addBits32Clean(uint32(value>>32), bits-32) 57 | } 58 | 59 | // addBits32Clean will add up to 32 bits. 60 | // It will not check if there is space for them. 61 | // The input must not contain more bits than specified. 62 | func (b *bitWriter) addBits32Clean(value uint32, bits uint8) { 63 | b.bitContainer |= uint64(value) << (b.nBits & 63) 64 | b.nBits += bits 65 | } 66 | 67 | // addBits16Clean will add up to 16 bits. value may not contain more set bits than indicated. 68 | // It will not check if there is space for them, so the caller must ensure that it has flushed recently. 69 | func (b *bitWriter) addBits16Clean(value uint16, bits uint8) { 70 | b.bitContainer |= uint64(value) << (b.nBits & 63) 71 | b.nBits += bits 72 | } 73 | 74 | // flush32 will flush out, so there are at least 32 bits available for writing. 75 | func (b *bitWriter) flush32() { 76 | if b.nBits < 32 { 77 | return 78 | } 79 | b.out = append(b.out, 80 | byte(b.bitContainer), 81 | byte(b.bitContainer>>8), 82 | byte(b.bitContainer>>16), 83 | byte(b.bitContainer>>24)) 84 | b.nBits -= 32 85 | b.bitContainer >>= 32 86 | } 87 | 88 | // flushAlign will flush remaining full bytes and align to next byte boundary. 89 | func (b *bitWriter) flushAlign() { 90 | nbBytes := (b.nBits + 7) >> 3 91 | for i := uint8(0); i < nbBytes; i++ { 92 | b.out = append(b.out, byte(b.bitContainer>>(i*8))) 93 | } 94 | b.nBits = 0 95 | b.bitContainer = 0 96 | } 97 | 98 | // close will write the alignment bit and write the final byte(s) 99 | // to the output. 100 | func (b *bitWriter) close() { 101 | // End mark 102 | b.addBits16Clean(1, 1) 103 | // flush until next byte. 104 | b.flushAlign() 105 | } 106 | 107 | // reset and continue writing by appending to out. 108 | func (b *bitWriter) reset(out []byte) { 109 | b.bitContainer = 0 110 | b.nBits = 0 111 | b.out = out 112 | } 113 | -------------------------------------------------------------------------------- /compress/zstd/blocktype_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=blockType,literalsBlockType,seqCompMode,tableIndex"; DO NOT EDIT. 2 | 3 | package zstd 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[blockTypeRaw-0] 12 | _ = x[blockTypeRLE-1] 13 | _ = x[blockTypeCompressed-2] 14 | _ = x[blockTypeReserved-3] 15 | } 16 | 17 | const _blockType_name = "blockTypeRawblockTypeRLEblockTypeCompressedblockTypeReserved" 18 | 19 | var _blockType_index = [...]uint8{0, 12, 24, 43, 60} 20 | 21 | func (i blockType) String() string { 22 | if i >= blockType(len(_blockType_index)-1) { 23 | return "blockType(" + strconv.FormatInt(int64(i), 10) + ")" 24 | } 25 | return _blockType_name[_blockType_index[i]:_blockType_index[i+1]] 26 | } 27 | func _() { 28 | // An "invalid array index" compiler error signifies that the constant values have changed. 29 | // Re-run the stringer command to generate them again. 30 | var x [1]struct{} 31 | _ = x[literalsBlockRaw-0] 32 | _ = x[literalsBlockRLE-1] 33 | _ = x[literalsBlockCompressed-2] 34 | _ = x[literalsBlockTreeless-3] 35 | } 36 | 37 | const _literalsBlockType_name = "literalsBlockRawliteralsBlockRLEliteralsBlockCompressedliteralsBlockTreeless" 38 | 39 | var _literalsBlockType_index = [...]uint8{0, 16, 32, 55, 76} 40 | 41 | func (i literalsBlockType) String() string { 42 | if i >= literalsBlockType(len(_literalsBlockType_index)-1) { 43 | return "literalsBlockType(" + strconv.FormatInt(int64(i), 10) + ")" 44 | } 45 | return _literalsBlockType_name[_literalsBlockType_index[i]:_literalsBlockType_index[i+1]] 46 | } 47 | func _() { 48 | // An "invalid array index" compiler error signifies that the constant values have changed. 49 | // Re-run the stringer command to generate them again. 50 | var x [1]struct{} 51 | _ = x[compModePredefined-0] 52 | _ = x[compModeRLE-1] 53 | _ = x[compModeFSE-2] 54 | _ = x[compModeRepeat-3] 55 | } 56 | 57 | const _seqCompMode_name = "compModePredefinedcompModeRLEcompModeFSEcompModeRepeat" 58 | 59 | var _seqCompMode_index = [...]uint8{0, 18, 29, 40, 54} 60 | 61 | func (i seqCompMode) String() string { 62 | if i >= seqCompMode(len(_seqCompMode_index)-1) { 63 | return "seqCompMode(" + strconv.FormatInt(int64(i), 10) + ")" 64 | } 65 | return _seqCompMode_name[_seqCompMode_index[i]:_seqCompMode_index[i+1]] 66 | } 67 | func _() { 68 | // An "invalid array index" compiler error signifies that the constant values have changed. 69 | // Re-run the stringer command to generate them again. 70 | var x [1]struct{} 71 | _ = x[tableLiteralLengths-0] 72 | _ = x[tableOffsets-1] 73 | _ = x[tableMatchLengths-2] 74 | } 75 | 76 | const _tableIndex_name = "tableLiteralLengthstableOffsetstableMatchLengths" 77 | 78 | var _tableIndex_index = [...]uint8{0, 19, 31, 48} 79 | 80 | func (i tableIndex) String() string { 81 | if i >= tableIndex(len(_tableIndex_index)-1) { 82 | return "tableIndex(" + strconv.FormatInt(int64(i), 10) + ")" 83 | } 84 | return _tableIndex_name[_tableIndex_index[i]:_tableIndex_index[i+1]] 85 | } 86 | -------------------------------------------------------------------------------- /compress/zstd/bytebuf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019+ Klaus Post. All rights reserved. 2 | // License information can be found in the LICENSE file. 3 | // Based on work by Yann Collet, released under BSD License. 4 | 5 | package zstd 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | ) 11 | 12 | type byteBuffer interface { 13 | // Read up to 8 bytes. 14 | // Returns io.ErrUnexpectedEOF if this cannot be satisfied. 15 | readSmall(n int) ([]byte, error) 16 | 17 | // Read >8 bytes. 18 | // MAY use the destination slice. 19 | readBig(n int, dst []byte) ([]byte, error) 20 | 21 | // Read a single byte. 22 | readByte() (byte, error) 23 | 24 | // Skip n bytes. 25 | skipN(n int64) error 26 | } 27 | 28 | // in-memory buffer 29 | type byteBuf []byte 30 | 31 | func (b *byteBuf) readSmall(n int) ([]byte, error) { 32 | if debugAsserts && n > 8 { 33 | panic(fmt.Errorf("small read > 8 (%d). use readBig", n)) 34 | } 35 | bb := *b 36 | if len(bb) < n { 37 | return nil, io.ErrUnexpectedEOF 38 | } 39 | r := bb[:n] 40 | *b = bb[n:] 41 | return r, nil 42 | } 43 | 44 | func (b *byteBuf) readBig(n int, dst []byte) ([]byte, error) { 45 | bb := *b 46 | if len(bb) < n { 47 | return nil, io.ErrUnexpectedEOF 48 | } 49 | r := bb[:n] 50 | *b = bb[n:] 51 | return r, nil 52 | } 53 | 54 | func (b *byteBuf) readByte() (byte, error) { 55 | bb := *b 56 | if len(bb) < 1 { 57 | return 0, io.ErrUnexpectedEOF 58 | } 59 | r := bb[0] 60 | *b = bb[1:] 61 | return r, nil 62 | } 63 | 64 | func (b *byteBuf) skipN(n int64) error { 65 | bb := *b 66 | if n < 0 { 67 | return fmt.Errorf("negative skip (%d) requested", n) 68 | } 69 | if int64(len(bb)) < n { 70 | return io.ErrUnexpectedEOF 71 | } 72 | *b = bb[n:] 73 | return nil 74 | } 75 | 76 | // wrapper around a reader. 77 | type readerWrapper struct { 78 | r io.Reader 79 | tmp [8]byte 80 | } 81 | 82 | func (r *readerWrapper) readSmall(n int) ([]byte, error) { 83 | if debugAsserts && n > 8 { 84 | panic(fmt.Errorf("small read > 8 (%d). use readBig", n)) 85 | } 86 | n2, err := io.ReadFull(r.r, r.tmp[:n]) 87 | // We only really care about the actual bytes read. 88 | if err != nil { 89 | if err == io.EOF { 90 | return nil, io.ErrUnexpectedEOF 91 | } 92 | if debugDecoder { 93 | println("readSmall: got", n2, "want", n, "err", err) 94 | } 95 | return nil, err 96 | } 97 | return r.tmp[:n], nil 98 | } 99 | 100 | func (r *readerWrapper) readBig(n int, dst []byte) ([]byte, error) { 101 | if cap(dst) < n { 102 | dst = make([]byte, n) 103 | } 104 | n2, err := io.ReadFull(r.r, dst[:n]) 105 | if err == io.EOF && n > 0 { 106 | err = io.ErrUnexpectedEOF 107 | } 108 | return dst[:n2], err 109 | } 110 | 111 | func (r *readerWrapper) readByte() (byte, error) { 112 | n2, err := io.ReadFull(r.r, r.tmp[:1]) 113 | if err != nil { 114 | if err == io.EOF { 115 | err = io.ErrUnexpectedEOF 116 | } 117 | return 0, err 118 | } 119 | if n2 != 1 { 120 | return 0, io.ErrUnexpectedEOF 121 | } 122 | return r.tmp[0], nil 123 | } 124 | 125 | func (r *readerWrapper) skipN(n int64) error { 126 | n2, err := io.CopyN(io.Discard, r.r, n) 127 | if n2 != n { 128 | err = io.ErrUnexpectedEOF 129 | } 130 | return err 131 | } 132 | -------------------------------------------------------------------------------- /compress/zstd/bytereader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019+ Klaus Post. All rights reserved. 2 | // License information can be found in the LICENSE file. 3 | // Based on work by Yann Collet, released under BSD License. 4 | 5 | package zstd 6 | 7 | // byteReader provides a byte reader that reads 8 | // little endian values from a byte stream. 9 | // The input stream is manually advanced. 10 | // The reader performs no bounds checks. 11 | type byteReader struct { 12 | b []byte 13 | off int 14 | } 15 | 16 | // advance the stream b n bytes. 17 | func (b *byteReader) advance(n uint) { 18 | b.off += int(n) 19 | } 20 | 21 | // overread returns whether we have advanced too far. 22 | func (b *byteReader) overread() bool { 23 | return b.off > len(b.b) 24 | } 25 | 26 | // Int32 returns a little endian int32 starting at current offset. 27 | func (b byteReader) Int32() int32 { 28 | b2 := b.b[b.off:] 29 | b2 = b2[:4] 30 | v3 := int32(b2[3]) 31 | v2 := int32(b2[2]) 32 | v1 := int32(b2[1]) 33 | v0 := int32(b2[0]) 34 | return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24) 35 | } 36 | 37 | // Uint8 returns the next byte 38 | func (b *byteReader) Uint8() uint8 { 39 | v := b.b[b.off] 40 | return v 41 | } 42 | 43 | // Uint32 returns a little endian uint32 starting at current offset. 44 | func (b byteReader) Uint32() uint32 { 45 | if r := b.remain(); r < 4 { 46 | // Very rare 47 | v := uint32(0) 48 | for i := 1; i <= r; i++ { 49 | v = (v << 8) | uint32(b.b[len(b.b)-i]) 50 | } 51 | return v 52 | } 53 | b2 := b.b[b.off:] 54 | b2 = b2[:4] 55 | v3 := uint32(b2[3]) 56 | v2 := uint32(b2[2]) 57 | v1 := uint32(b2[1]) 58 | v0 := uint32(b2[0]) 59 | return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24) 60 | } 61 | 62 | // Uint32NC returns a little endian uint32 starting at current offset. 63 | // The caller must be sure if there are at least 4 bytes left. 64 | func (b byteReader) Uint32NC() uint32 { 65 | b2 := b.b[b.off:] 66 | b2 = b2[:4] 67 | v3 := uint32(b2[3]) 68 | v2 := uint32(b2[2]) 69 | v1 := uint32(b2[1]) 70 | v0 := uint32(b2[0]) 71 | return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24) 72 | } 73 | 74 | // unread returns the unread portion of the input. 75 | func (b byteReader) unread() []byte { 76 | return b.b[b.off:] 77 | } 78 | 79 | // remain will return the number of bytes remaining. 80 | func (b byteReader) remain() int { 81 | return len(b.b) - b.off 82 | } 83 | -------------------------------------------------------------------------------- /compress/zstd/decodeheader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020+ Klaus Post. All rights reserved. 2 | // License information can be found in the LICENSE file. 3 | 4 | package zstd 5 | 6 | import ( 7 | "encoding/binary" 8 | "errors" 9 | "io" 10 | ) 11 | 12 | // HeaderMaxSize is the maximum size of a Frame and Block Header. 13 | // If less is sent to Header.Decode it *may* still contain enough information. 14 | const HeaderMaxSize = 14 + 3 15 | 16 | // Header contains information about the first frame and block within that. 17 | type Header struct { 18 | // SingleSegment specifies whether the data is to be decompressed into a 19 | // single contiguous memory segment. 20 | // It implies that WindowSize is invalid and that FrameContentSize is valid. 21 | SingleSegment bool 22 | 23 | // WindowSize is the window of data to keep while decoding. 24 | // Will only be set if SingleSegment is false. 25 | WindowSize uint64 26 | 27 | // Dictionary ID. 28 | // If 0, no dictionary. 29 | DictionaryID uint32 30 | 31 | // HasFCS specifies whether FrameContentSize has a valid value. 32 | HasFCS bool 33 | 34 | // FrameContentSize is the expected uncompressed size of the entire frame. 35 | FrameContentSize uint64 36 | 37 | // Skippable will be true if the frame is meant to be skipped. 38 | // This implies that FirstBlock.OK is false. 39 | Skippable bool 40 | 41 | // SkippableID is the user-specific ID for the skippable frame. 42 | // Valid values are between 0 to 15, inclusive. 43 | SkippableID int 44 | 45 | // SkippableSize is the length of the user data to skip following 46 | // the header. 47 | SkippableSize uint32 48 | 49 | // HeaderSize is the raw size of the frame header. 50 | // 51 | // For normal frames, it includes the size of the magic number and 52 | // the size of the header (per section 3.1.1.1). 53 | // It does not include the size for any data blocks (section 3.1.1.2) nor 54 | // the size for the trailing content checksum. 55 | // 56 | // For skippable frames, this counts the size of the magic number 57 | // along with the size of the size field of the payload. 58 | // It does not include the size of the skippable payload itself. 59 | // The total frame size is the HeaderSize plus the SkippableSize. 60 | HeaderSize int 61 | 62 | // First block information. 63 | FirstBlock struct { 64 | // OK will be set if first block could be decoded. 65 | OK bool 66 | 67 | // Is this the last block of a frame? 68 | Last bool 69 | 70 | // Is the data compressed? 71 | // If true CompressedSize will be populated. 72 | // Unfortunately DecompressedSize cannot be determined 73 | // without decoding the blocks. 74 | Compressed bool 75 | 76 | // DecompressedSize is the expected decompressed size of the block. 77 | // Will be 0 if it cannot be determined. 78 | DecompressedSize int 79 | 80 | // CompressedSize of the data in the block. 81 | // Does not include the block header. 82 | // Will be equal to DecompressedSize if not Compressed. 83 | CompressedSize int 84 | } 85 | 86 | // If set there is a checksum present for the block content. 87 | // The checksum field at the end is always 4 bytes long. 88 | HasCheckSum bool 89 | } 90 | 91 | // Decode the header from the beginning of the stream. 92 | // This will decode the frame header and the first block header if enough bytes are provided. 93 | // It is recommended to provide at least HeaderMaxSize bytes. 94 | // If the frame header cannot be read an error will be returned. 95 | // If there isn't enough input, io.ErrUnexpectedEOF is returned. 96 | // The FirstBlock.OK will indicate if enough information was available to decode the first block header. 97 | func (h *Header) Decode(in []byte) error { 98 | _, err := h.DecodeAndStrip(in) 99 | return err 100 | } 101 | 102 | // DecodeAndStrip will decode the header from the beginning of the stream 103 | // and on success return the remaining bytes. 104 | // This will decode the frame header and the first block header if enough bytes are provided. 105 | // It is recommended to provide at least HeaderMaxSize bytes. 106 | // If the frame header cannot be read an error will be returned. 107 | // If there isn't enough input, io.ErrUnexpectedEOF is returned. 108 | // The FirstBlock.OK will indicate if enough information was available to decode the first block header. 109 | func (h *Header) DecodeAndStrip(in []byte) (remain []byte, err error) { 110 | *h = Header{} 111 | if len(in) < 4 { 112 | return nil, io.ErrUnexpectedEOF 113 | } 114 | h.HeaderSize += 4 115 | b, in := in[:4], in[4:] 116 | if string(b) != frameMagic { 117 | if string(b[1:4]) != skippableFrameMagic || b[0]&0xf0 != 0x50 { 118 | return nil, ErrMagicMismatch 119 | } 120 | if len(in) < 4 { 121 | return nil, io.ErrUnexpectedEOF 122 | } 123 | h.HeaderSize += 4 124 | h.Skippable = true 125 | h.SkippableID = int(b[0] & 0xf) 126 | h.SkippableSize = binary.LittleEndian.Uint32(in) 127 | return in[4:], nil 128 | } 129 | 130 | // Read Window_Descriptor 131 | // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#window_descriptor 132 | if len(in) < 1 { 133 | return nil, io.ErrUnexpectedEOF 134 | } 135 | fhd, in := in[0], in[1:] 136 | h.HeaderSize++ 137 | h.SingleSegment = fhd&(1<<5) != 0 138 | h.HasCheckSum = fhd&(1<<2) != 0 139 | if fhd&(1<<3) != 0 { 140 | return nil, errors.New("reserved bit set on frame header") 141 | } 142 | 143 | if !h.SingleSegment { 144 | if len(in) < 1 { 145 | return nil, io.ErrUnexpectedEOF 146 | } 147 | var wd byte 148 | wd, in = in[0], in[1:] 149 | h.HeaderSize++ 150 | windowLog := 10 + (wd >> 3) 151 | windowBase := uint64(1) << windowLog 152 | windowAdd := (windowBase / 8) * uint64(wd&0x7) 153 | h.WindowSize = windowBase + windowAdd 154 | } 155 | 156 | // Read Dictionary_ID 157 | // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary_id 158 | if size := fhd & 3; size != 0 { 159 | if size == 3 { 160 | size = 4 161 | } 162 | if len(in) < int(size) { 163 | return nil, io.ErrUnexpectedEOF 164 | } 165 | b, in = in[:size], in[size:] 166 | h.HeaderSize += int(size) 167 | switch len(b) { 168 | case 1: 169 | h.DictionaryID = uint32(b[0]) 170 | case 2: 171 | h.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8) 172 | case 4: 173 | h.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) 174 | } 175 | } 176 | 177 | // Read Frame_Content_Size 178 | // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#frame_content_size 179 | var fcsSize int 180 | v := fhd >> 6 181 | switch v { 182 | case 0: 183 | if h.SingleSegment { 184 | fcsSize = 1 185 | } 186 | default: 187 | fcsSize = 1 << v 188 | } 189 | 190 | if fcsSize > 0 { 191 | h.HasFCS = true 192 | if len(in) < fcsSize { 193 | return nil, io.ErrUnexpectedEOF 194 | } 195 | b, in = in[:fcsSize], in[fcsSize:] 196 | h.HeaderSize += int(fcsSize) 197 | switch len(b) { 198 | case 1: 199 | h.FrameContentSize = uint64(b[0]) 200 | case 2: 201 | // When FCS_Field_Size is 2, the offset of 256 is added. 202 | h.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) + 256 203 | case 4: 204 | h.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) | (uint64(b[2]) << 16) | (uint64(b[3]) << 24) 205 | case 8: 206 | d1 := uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) 207 | d2 := uint32(b[4]) | (uint32(b[5]) << 8) | (uint32(b[6]) << 16) | (uint32(b[7]) << 24) 208 | h.FrameContentSize = uint64(d1) | (uint64(d2) << 32) 209 | } 210 | } 211 | 212 | // Frame Header done, we will not fail from now on. 213 | if len(in) < 3 { 214 | return in, nil 215 | } 216 | tmp := in[:3] 217 | bh := uint32(tmp[0]) | (uint32(tmp[1]) << 8) | (uint32(tmp[2]) << 16) 218 | h.FirstBlock.Last = bh&1 != 0 219 | blockType := blockType((bh >> 1) & 3) 220 | // find size. 221 | cSize := int(bh >> 3) 222 | switch blockType { 223 | case blockTypeReserved: 224 | return in, nil 225 | case blockTypeRLE: 226 | h.FirstBlock.Compressed = true 227 | h.FirstBlock.DecompressedSize = cSize 228 | h.FirstBlock.CompressedSize = 1 229 | case blockTypeCompressed: 230 | h.FirstBlock.Compressed = true 231 | h.FirstBlock.CompressedSize = cSize 232 | case blockTypeRaw: 233 | h.FirstBlock.DecompressedSize = cSize 234 | h.FirstBlock.CompressedSize = cSize 235 | default: 236 | panic("Invalid block type") 237 | } 238 | 239 | h.FirstBlock.OK = true 240 | return in, nil 241 | } 242 | 243 | // AppendTo will append the encoded header to the dst slice. 244 | // There is no error checking performed on the header values. 245 | func (h *Header) AppendTo(dst []byte) ([]byte, error) { 246 | if h.Skippable { 247 | magic := [4]byte{0x50, 0x2a, 0x4d, 0x18} 248 | magic[0] |= byte(h.SkippableID & 0xf) 249 | dst = append(dst, magic[:]...) 250 | f := h.SkippableSize 251 | return append(dst, uint8(f), uint8(f>>8), uint8(f>>16), uint8(f>>24)), nil 252 | } 253 | f := frameHeader{ 254 | ContentSize: h.FrameContentSize, 255 | WindowSize: uint32(h.WindowSize), 256 | SingleSegment: h.SingleSegment, 257 | Checksum: h.HasCheckSum, 258 | DictID: h.DictionaryID, 259 | } 260 | return f.appendTo(dst), nil 261 | } 262 | -------------------------------------------------------------------------------- /compress/zstd/decoder_options.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019+ Klaus Post. All rights reserved. 2 | // License information can be found in the LICENSE file. 3 | // Based on work by Yann Collet, released under BSD License. 4 | 5 | package zstd 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "math/bits" 11 | "runtime" 12 | ) 13 | 14 | // DOption is an option for creating a decoder. 15 | type DOption func(*decoderOptions) error 16 | 17 | // options retains accumulated state of multiple options. 18 | type decoderOptions struct { 19 | lowMem bool 20 | concurrent int 21 | maxDecodedSize uint64 22 | maxWindowSize uint64 23 | dicts []*dict 24 | ignoreChecksum bool 25 | limitToCap bool 26 | decodeBufsBelow int 27 | } 28 | 29 | func (o *decoderOptions) setDefault() { 30 | *o = decoderOptions{ 31 | // use less ram: true for now, but may change. 32 | lowMem: true, 33 | concurrent: runtime.GOMAXPROCS(0), 34 | maxWindowSize: MaxWindowSize, 35 | decodeBufsBelow: 128 << 10, 36 | } 37 | if o.concurrent > 4 { 38 | o.concurrent = 4 39 | } 40 | o.maxDecodedSize = 64 << 30 41 | } 42 | 43 | // WithDecoderLowmem will set whether to use a lower amount of memory, 44 | // but possibly have to allocate more while running. 45 | func WithDecoderLowmem(b bool) DOption { 46 | return func(o *decoderOptions) error { o.lowMem = b; return nil } 47 | } 48 | 49 | // WithDecoderConcurrency sets the number of created decoders. 50 | // When decoding block with DecodeAll, this will limit the number 51 | // of possible concurrently running decodes. 52 | // When decoding streams, this will limit the number of 53 | // inflight blocks. 54 | // When decoding streams and setting maximum to 1, 55 | // no async decoding will be done. 56 | // When a value of 0 is provided GOMAXPROCS will be used. 57 | // By default this will be set to 4 or GOMAXPROCS, whatever is lower. 58 | func WithDecoderConcurrency(n int) DOption { 59 | return func(o *decoderOptions) error { 60 | if n < 0 { 61 | return errors.New("concurrency must be at least 1") 62 | } 63 | if n == 0 { 64 | o.concurrent = runtime.GOMAXPROCS(0) 65 | } else { 66 | o.concurrent = n 67 | } 68 | return nil 69 | } 70 | } 71 | 72 | // WithDecoderMaxMemory allows to set a maximum decoded size for in-memory 73 | // non-streaming operations or maximum window size for streaming operations. 74 | // This can be used to control memory usage of potentially hostile content. 75 | // Maximum is 1 << 63 bytes. Default is 64GiB. 76 | func WithDecoderMaxMemory(n uint64) DOption { 77 | return func(o *decoderOptions) error { 78 | if n == 0 { 79 | return errors.New("WithDecoderMaxMemory must be at least 1") 80 | } 81 | if n > 1<<63 { 82 | return errors.New("WithDecoderMaxmemory must be less than 1 << 63") 83 | } 84 | o.maxDecodedSize = n 85 | return nil 86 | } 87 | } 88 | 89 | // WithDecoderDicts allows to register one or more dictionaries for the decoder. 90 | // 91 | // Each slice in dict must be in the [dictionary format] produced by 92 | // "zstd --train" from the Zstandard reference implementation. 93 | // 94 | // If several dictionaries with the same ID are provided, the last one will be used. 95 | // 96 | // [dictionary format]: https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary-format 97 | func WithDecoderDicts(dicts ...[]byte) DOption { 98 | return func(o *decoderOptions) error { 99 | for _, b := range dicts { 100 | d, err := loadDict(b) 101 | if err != nil { 102 | return err 103 | } 104 | o.dicts = append(o.dicts, d) 105 | } 106 | return nil 107 | } 108 | } 109 | 110 | // WithDecoderDictRaw registers a dictionary that may be used by the decoder. 111 | // The slice content can be arbitrary data. 112 | func WithDecoderDictRaw(id uint32, content []byte) DOption { 113 | return func(o *decoderOptions) error { 114 | if bits.UintSize > 32 && uint(len(content)) > dictMaxLength { 115 | return fmt.Errorf("dictionary of size %d > 2GiB too large", len(content)) 116 | } 117 | o.dicts = append(o.dicts, &dict{id: id, content: content, offsets: [3]int{1, 4, 8}}) 118 | return nil 119 | } 120 | } 121 | 122 | // WithDecoderMaxWindow allows to set a maximum window size for decodes. 123 | // This allows rejecting packets that will cause big memory usage. 124 | // The Decoder will likely allocate more memory based on the WithDecoderLowmem setting. 125 | // If WithDecoderMaxMemory is set to a lower value, that will be used. 126 | // Default is 512MB, Maximum is ~3.75 TB as per zstandard spec. 127 | func WithDecoderMaxWindow(size uint64) DOption { 128 | return func(o *decoderOptions) error { 129 | if size < MinWindowSize { 130 | return errors.New("WithMaxWindowSize must be at least 1KB, 1024 bytes") 131 | } 132 | if size > (1<<41)+7*(1<<38) { 133 | return errors.New("WithMaxWindowSize must be less than (1<<41) + 7*(1<<38) ~ 3.75TB") 134 | } 135 | o.maxWindowSize = size 136 | return nil 137 | } 138 | } 139 | 140 | // WithDecodeAllCapLimit will limit DecodeAll to decoding cap(dst)-len(dst) bytes, 141 | // or any size set in WithDecoderMaxMemory. 142 | // This can be used to limit decoding to a specific maximum output size. 143 | // Disabled by default. 144 | func WithDecodeAllCapLimit(b bool) DOption { 145 | return func(o *decoderOptions) error { 146 | o.limitToCap = b 147 | return nil 148 | } 149 | } 150 | 151 | // WithDecodeBuffersBelow will fully decode readers that have a 152 | // `Bytes() []byte` and `Len() int` interface similar to bytes.Buffer. 153 | // This typically uses less allocations but will have the full decompressed object in memory. 154 | // Note that DecodeAllCapLimit will disable this, as well as giving a size of 0 or less. 155 | // Default is 128KiB. 156 | func WithDecodeBuffersBelow(size int) DOption { 157 | return func(o *decoderOptions) error { 158 | o.decodeBufsBelow = size 159 | return nil 160 | } 161 | } 162 | 163 | // IgnoreChecksum allows to forcibly ignore checksum checking. 164 | func IgnoreChecksum(b bool) DOption { 165 | return func(o *decoderOptions) error { 166 | o.ignoreChecksum = b 167 | return nil 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /compress/zstd/enc_base.go: -------------------------------------------------------------------------------- 1 | package zstd 2 | 3 | import ( 4 | "fmt" 5 | "math/bits" 6 | 7 | "github.com/klauspost/compress/zstd/internal/xxhash" 8 | ) 9 | 10 | const ( 11 | dictShardBits = 7 12 | ) 13 | 14 | type fastBase struct { 15 | // cur is the offset at the start of hist 16 | cur int32 17 | // maximum offset. Should be at least 2x block size. 18 | maxMatchOff int32 19 | bufferReset int32 20 | hist []byte 21 | crc *xxhash.Digest 22 | tmp [8]byte 23 | blk *blockEnc 24 | lastDictID uint32 25 | lowMem bool 26 | } 27 | 28 | // CRC returns the underlying CRC writer. 29 | func (e *fastBase) CRC() *xxhash.Digest { 30 | return e.crc 31 | } 32 | 33 | // AppendCRC will append the CRC to the destination slice and return it. 34 | func (e *fastBase) AppendCRC(dst []byte) []byte { 35 | crc := e.crc.Sum(e.tmp[:0]) 36 | dst = append(dst, crc[7], crc[6], crc[5], crc[4]) 37 | return dst 38 | } 39 | 40 | // WindowSize returns the window size of the encoder, 41 | // or a window size small enough to contain the input size, if > 0. 42 | func (e *fastBase) WindowSize(size int64) int32 { 43 | if size > 0 && size < int64(e.maxMatchOff) { 44 | b := int32(1) << uint(bits.Len(uint(size))) 45 | // Keep minimum window. 46 | if b < 1024 { 47 | b = 1024 48 | } 49 | return b 50 | } 51 | return e.maxMatchOff 52 | } 53 | 54 | // Block returns the current block. 55 | func (e *fastBase) Block() *blockEnc { 56 | return e.blk 57 | } 58 | 59 | func (e *fastBase) addBlock(src []byte) int32 { 60 | if debugAsserts && e.cur > e.bufferReset { 61 | panic(fmt.Sprintf("ecur (%d) > buffer reset (%d)", e.cur, e.bufferReset)) 62 | } 63 | // check if we have space already 64 | if len(e.hist)+len(src) > cap(e.hist) { 65 | if cap(e.hist) == 0 { 66 | e.ensureHist(len(src)) 67 | } else { 68 | if cap(e.hist) < int(e.maxMatchOff+maxCompressedBlockSize) { 69 | panic(fmt.Errorf("unexpected buffer cap %d, want at least %d with window %d", cap(e.hist), e.maxMatchOff+maxCompressedBlockSize, e.maxMatchOff)) 70 | } 71 | // Move down 72 | offset := int32(len(e.hist)) - e.maxMatchOff 73 | copy(e.hist[0:e.maxMatchOff], e.hist[offset:]) 74 | e.cur += offset 75 | e.hist = e.hist[:e.maxMatchOff] 76 | } 77 | } 78 | s := int32(len(e.hist)) 79 | e.hist = append(e.hist, src...) 80 | return s 81 | } 82 | 83 | // ensureHist will ensure that history can keep at least this many bytes. 84 | func (e *fastBase) ensureHist(n int) { 85 | if cap(e.hist) >= n { 86 | return 87 | } 88 | l := e.maxMatchOff 89 | if (e.lowMem && e.maxMatchOff > maxCompressedBlockSize) || e.maxMatchOff <= maxCompressedBlockSize { 90 | l += maxCompressedBlockSize 91 | } else { 92 | l += e.maxMatchOff 93 | } 94 | // Make it at least 1MB. 95 | if l < 1<<20 && !e.lowMem { 96 | l = 1 << 20 97 | } 98 | // Make it at least the requested size. 99 | if l < int32(n) { 100 | l = int32(n) 101 | } 102 | e.hist = make([]byte, 0, l) 103 | } 104 | 105 | // useBlock will replace the block with the provided one, 106 | // but transfer recent offsets from the previous. 107 | func (e *fastBase) UseBlock(enc *blockEnc) { 108 | enc.reset(e.blk) 109 | e.blk = enc 110 | } 111 | 112 | func (e *fastBase) matchlen(s, t int32, src []byte) int32 { 113 | if debugAsserts { 114 | if s < 0 { 115 | err := fmt.Sprintf("s (%d) < 0", s) 116 | panic(err) 117 | } 118 | if t < 0 { 119 | err := fmt.Sprintf("t (%d) < 0", t) 120 | panic(err) 121 | } 122 | if s-t > e.maxMatchOff { 123 | err := fmt.Sprintf("s (%d) - t (%d) > maxMatchOff (%d)", s, t, e.maxMatchOff) 124 | panic(err) 125 | } 126 | if len(src)-int(s) > maxCompressedBlockSize { 127 | panic(fmt.Sprintf("len(src)-s (%d) > maxCompressedBlockSize (%d)", len(src)-int(s), maxCompressedBlockSize)) 128 | } 129 | } 130 | return int32(matchLen(src[s:], src[t:])) 131 | } 132 | 133 | // Reset the encoding table. 134 | func (e *fastBase) resetBase(d *dict, singleBlock bool) { 135 | if e.blk == nil { 136 | e.blk = &blockEnc{lowMem: e.lowMem} 137 | e.blk.init() 138 | } else { 139 | e.blk.reset(nil) 140 | } 141 | e.blk.initNewEncode() 142 | if e.crc == nil { 143 | e.crc = xxhash.New() 144 | } else { 145 | e.crc.Reset() 146 | } 147 | e.blk.dictLitEnc = nil 148 | if d != nil { 149 | low := e.lowMem 150 | if singleBlock { 151 | e.lowMem = true 152 | } 153 | e.ensureHist(d.ContentSize() + maxCompressedBlockSize) 154 | e.lowMem = low 155 | } 156 | 157 | // We offset current position so everything will be out of reach. 158 | // If above reset line, history will be purged. 159 | if e.cur < e.bufferReset { 160 | e.cur += e.maxMatchOff + int32(len(e.hist)) 161 | } 162 | e.hist = e.hist[:0] 163 | if d != nil { 164 | // Set offsets (currently not used) 165 | for i, off := range d.offsets { 166 | e.blk.recentOffsets[i] = uint32(off) 167 | e.blk.prevRecentOffsets[i] = e.blk.recentOffsets[i] 168 | } 169 | // Transfer litenc. 170 | e.blk.dictLitEnc = d.litEnc 171 | e.hist = append(e.hist, d.content...) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /compress/zstd/frameenc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019+ Klaus Post. All rights reserved. 2 | // License information can be found in the LICENSE file. 3 | // Based on work by Yann Collet, released under BSD License. 4 | 5 | package zstd 6 | 7 | import ( 8 | "encoding/binary" 9 | "fmt" 10 | "io" 11 | "math" 12 | "math/bits" 13 | ) 14 | 15 | type frameHeader struct { 16 | ContentSize uint64 17 | WindowSize uint32 18 | SingleSegment bool 19 | Checksum bool 20 | DictID uint32 21 | } 22 | 23 | const maxHeaderSize = 14 24 | 25 | func (f frameHeader) appendTo(dst []byte) []byte { 26 | dst = append(dst, frameMagic...) 27 | var fhd uint8 28 | if f.Checksum { 29 | fhd |= 1 << 2 30 | } 31 | if f.SingleSegment { 32 | fhd |= 1 << 5 33 | } 34 | 35 | var dictIDContent []byte 36 | if f.DictID > 0 { 37 | var tmp [4]byte 38 | if f.DictID < 256 { 39 | fhd |= 1 40 | tmp[0] = uint8(f.DictID) 41 | dictIDContent = tmp[:1] 42 | } else if f.DictID < 1<<16 { 43 | fhd |= 2 44 | binary.LittleEndian.PutUint16(tmp[:2], uint16(f.DictID)) 45 | dictIDContent = tmp[:2] 46 | } else { 47 | fhd |= 3 48 | binary.LittleEndian.PutUint32(tmp[:4], f.DictID) 49 | dictIDContent = tmp[:4] 50 | } 51 | } 52 | var fcs uint8 53 | if f.ContentSize >= 256 { 54 | fcs++ 55 | } 56 | if f.ContentSize >= 65536+256 { 57 | fcs++ 58 | } 59 | if f.ContentSize >= 0xffffffff { 60 | fcs++ 61 | } 62 | 63 | fhd |= fcs << 6 64 | 65 | dst = append(dst, fhd) 66 | if !f.SingleSegment { 67 | const winLogMin = 10 68 | windowLog := (bits.Len32(f.WindowSize-1) - winLogMin) << 3 69 | dst = append(dst, uint8(windowLog)) 70 | } 71 | if f.DictID > 0 { 72 | dst = append(dst, dictIDContent...) 73 | } 74 | switch fcs { 75 | case 0: 76 | if f.SingleSegment { 77 | dst = append(dst, uint8(f.ContentSize)) 78 | } 79 | // Unless SingleSegment is set, framessizes < 256 are not stored. 80 | case 1: 81 | f.ContentSize -= 256 82 | dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8)) 83 | case 2: 84 | dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8), uint8(f.ContentSize>>16), uint8(f.ContentSize>>24)) 85 | case 3: 86 | dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8), uint8(f.ContentSize>>16), uint8(f.ContentSize>>24), 87 | uint8(f.ContentSize>>32), uint8(f.ContentSize>>40), uint8(f.ContentSize>>48), uint8(f.ContentSize>>56)) 88 | default: 89 | panic("invalid fcs") 90 | } 91 | return dst 92 | } 93 | 94 | const skippableFrameHeader = 4 + 4 95 | 96 | // calcSkippableFrame will return a total size to be added for written 97 | // to be divisible by multiple. 98 | // The value will always be > skippableFrameHeader. 99 | // The function will panic if written < 0 or wantMultiple <= 0. 100 | func calcSkippableFrame(written, wantMultiple int64) int { 101 | if wantMultiple <= 0 { 102 | panic("wantMultiple <= 0") 103 | } 104 | if written < 0 { 105 | panic("written < 0") 106 | } 107 | leftOver := written % wantMultiple 108 | if leftOver == 0 { 109 | return 0 110 | } 111 | toAdd := wantMultiple - leftOver 112 | for toAdd < skippableFrameHeader { 113 | toAdd += wantMultiple 114 | } 115 | return int(toAdd) 116 | } 117 | 118 | // skippableFrame will add a skippable frame with a total size of bytes. 119 | // total should be >= skippableFrameHeader and < math.MaxUint32. 120 | func skippableFrame(dst []byte, total int, r io.Reader) ([]byte, error) { 121 | if total == 0 { 122 | return dst, nil 123 | } 124 | if total < skippableFrameHeader { 125 | return dst, fmt.Errorf("requested skippable frame (%d) < 8", total) 126 | } 127 | if int64(total) > math.MaxUint32 { 128 | return dst, fmt.Errorf("requested skippable frame (%d) > max uint32", total) 129 | } 130 | dst = append(dst, 0x50, 0x2a, 0x4d, 0x18) 131 | f := uint32(total - skippableFrameHeader) 132 | dst = append(dst, uint8(f), uint8(f>>8), uint8(f>>16), uint8(f>>24)) 133 | start := len(dst) 134 | dst = append(dst, make([]byte, f)...) 135 | _, err := io.ReadFull(r, dst[start:]) 136 | return dst, err 137 | } 138 | -------------------------------------------------------------------------------- /compress/zstd/fse_decoder.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019+ Klaus Post. All rights reserved. 2 | // License information can be found in the LICENSE file. 3 | // Based on work by Yann Collet, released under BSD License. 4 | 5 | package zstd 6 | 7 | import ( 8 | "encoding/binary" 9 | "errors" 10 | "fmt" 11 | "io" 12 | ) 13 | 14 | const ( 15 | tablelogAbsoluteMax = 9 16 | ) 17 | 18 | const ( 19 | /*!MEMORY_USAGE : 20 | * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) 21 | * Increasing memory usage improves compression ratio 22 | * Reduced memory usage can improve speed, due to cache effect 23 | * Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ 24 | maxMemoryUsage = tablelogAbsoluteMax + 2 25 | 26 | maxTableLog = maxMemoryUsage - 2 27 | maxTablesize = 1 << maxTableLog 28 | maxTableMask = (1 << maxTableLog) - 1 29 | minTablelog = 5 30 | maxSymbolValue = 255 31 | ) 32 | 33 | // fseDecoder provides temporary storage for compression and decompression. 34 | type fseDecoder struct { 35 | dt [maxTablesize]decSymbol // Decompression table. 36 | symbolLen uint16 // Length of active part of the symbol table. 37 | actualTableLog uint8 // Selected tablelog. 38 | maxBits uint8 // Maximum number of additional bits 39 | 40 | // used for table creation to avoid allocations. 41 | stateTable [256]uint16 42 | norm [maxSymbolValue + 1]int16 43 | preDefined bool 44 | } 45 | 46 | // tableStep returns the next table index. 47 | func tableStep(tableSize uint32) uint32 { 48 | return (tableSize >> 1) + (tableSize >> 3) + 3 49 | } 50 | 51 | // readNCount will read the symbol distribution so decoding tables can be constructed. 52 | func (s *fseDecoder) readNCount(b *byteReader, maxSymbol uint16) error { 53 | var ( 54 | charnum uint16 55 | previous0 bool 56 | ) 57 | if b.remain() < 4 { 58 | return errors.New("input too small") 59 | } 60 | bitStream := b.Uint32NC() 61 | nbBits := uint((bitStream & 0xF) + minTablelog) // extract tableLog 62 | if nbBits > tablelogAbsoluteMax { 63 | println("Invalid tablelog:", nbBits) 64 | return errors.New("tableLog too large") 65 | } 66 | bitStream >>= 4 67 | bitCount := uint(4) 68 | 69 | s.actualTableLog = uint8(nbBits) 70 | remaining := int32((1 << nbBits) + 1) 71 | threshold := int32(1 << nbBits) 72 | gotTotal := int32(0) 73 | nbBits++ 74 | 75 | for remaining > 1 && charnum <= maxSymbol { 76 | if previous0 { 77 | //println("prev0") 78 | n0 := charnum 79 | for (bitStream & 0xFFFF) == 0xFFFF { 80 | //println("24 x 0") 81 | n0 += 24 82 | if r := b.remain(); r > 5 { 83 | b.advance(2) 84 | // The check above should make sure we can read 32 bits 85 | bitStream = b.Uint32NC() >> bitCount 86 | } else { 87 | // end of bit stream 88 | bitStream >>= 16 89 | bitCount += 16 90 | } 91 | } 92 | //printf("bitstream: %d, 0b%b", bitStream&3, bitStream) 93 | for (bitStream & 3) == 3 { 94 | n0 += 3 95 | bitStream >>= 2 96 | bitCount += 2 97 | } 98 | n0 += uint16(bitStream & 3) 99 | bitCount += 2 100 | 101 | if n0 > maxSymbolValue { 102 | return errors.New("maxSymbolValue too small") 103 | } 104 | //println("inserting ", n0-charnum, "zeroes from idx", charnum, "ending before", n0) 105 | for charnum < n0 { 106 | s.norm[uint8(charnum)] = 0 107 | charnum++ 108 | } 109 | 110 | if r := b.remain(); r >= 7 || r-int(bitCount>>3) >= 4 { 111 | b.advance(bitCount >> 3) 112 | bitCount &= 7 113 | // The check above should make sure we can read 32 bits 114 | bitStream = b.Uint32NC() >> bitCount 115 | } else { 116 | bitStream >>= 2 117 | } 118 | } 119 | 120 | max := (2*threshold - 1) - remaining 121 | var count int32 122 | 123 | if int32(bitStream)&(threshold-1) < max { 124 | count = int32(bitStream) & (threshold - 1) 125 | if debugAsserts && nbBits < 1 { 126 | panic("nbBits underflow") 127 | } 128 | bitCount += nbBits - 1 129 | } else { 130 | count = int32(bitStream) & (2*threshold - 1) 131 | if count >= threshold { 132 | count -= max 133 | } 134 | bitCount += nbBits 135 | } 136 | 137 | // extra accuracy 138 | count-- 139 | if count < 0 { 140 | // -1 means +1 141 | remaining += count 142 | gotTotal -= count 143 | } else { 144 | remaining -= count 145 | gotTotal += count 146 | } 147 | s.norm[charnum&0xff] = int16(count) 148 | charnum++ 149 | previous0 = count == 0 150 | for remaining < threshold { 151 | nbBits-- 152 | threshold >>= 1 153 | } 154 | 155 | if r := b.remain(); r >= 7 || r-int(bitCount>>3) >= 4 { 156 | b.advance(bitCount >> 3) 157 | bitCount &= 7 158 | // The check above should make sure we can read 32 bits 159 | bitStream = b.Uint32NC() >> (bitCount & 31) 160 | } else { 161 | bitCount -= (uint)(8 * (len(b.b) - 4 - b.off)) 162 | b.off = len(b.b) - 4 163 | bitStream = b.Uint32() >> (bitCount & 31) 164 | } 165 | } 166 | s.symbolLen = charnum 167 | if s.symbolLen <= 1 { 168 | return fmt.Errorf("symbolLen (%d) too small", s.symbolLen) 169 | } 170 | if s.symbolLen > maxSymbolValue+1 { 171 | return fmt.Errorf("symbolLen (%d) too big", s.symbolLen) 172 | } 173 | if remaining != 1 { 174 | return fmt.Errorf("corruption detected (remaining %d != 1)", remaining) 175 | } 176 | if bitCount > 32 { 177 | return fmt.Errorf("corruption detected (bitCount %d > 32)", bitCount) 178 | } 179 | if gotTotal != 1<> 3) 183 | return s.buildDtable() 184 | } 185 | 186 | func (s *fseDecoder) mustReadFrom(r io.Reader) { 187 | fatalErr := func(err error) { 188 | if err != nil { 189 | panic(err) 190 | } 191 | } 192 | // dt [maxTablesize]decSymbol // Decompression table. 193 | // symbolLen uint16 // Length of active part of the symbol table. 194 | // actualTableLog uint8 // Selected tablelog. 195 | // maxBits uint8 // Maximum number of additional bits 196 | // // used for table creation to avoid allocations. 197 | // stateTable [256]uint16 198 | // norm [maxSymbolValue + 1]int16 199 | // preDefined bool 200 | fatalErr(binary.Read(r, binary.LittleEndian, &s.dt)) 201 | fatalErr(binary.Read(r, binary.LittleEndian, &s.symbolLen)) 202 | fatalErr(binary.Read(r, binary.LittleEndian, &s.actualTableLog)) 203 | fatalErr(binary.Read(r, binary.LittleEndian, &s.maxBits)) 204 | fatalErr(binary.Read(r, binary.LittleEndian, &s.stateTable)) 205 | fatalErr(binary.Read(r, binary.LittleEndian, &s.norm)) 206 | fatalErr(binary.Read(r, binary.LittleEndian, &s.preDefined)) 207 | } 208 | 209 | // decSymbol contains information about a state entry, 210 | // Including the state offset base, the output symbol and 211 | // the number of bits to read for the low part of the destination state. 212 | // Using a composite uint64 is faster than a struct with separate members. 213 | type decSymbol uint64 214 | 215 | func newDecSymbol(nbits, addBits uint8, newState uint16, baseline uint32) decSymbol { 216 | return decSymbol(nbits) | (decSymbol(addBits) << 8) | (decSymbol(newState) << 16) | (decSymbol(baseline) << 32) 217 | } 218 | 219 | func (d decSymbol) nbBits() uint8 { 220 | return uint8(d) 221 | } 222 | 223 | func (d decSymbol) addBits() uint8 { 224 | return uint8(d >> 8) 225 | } 226 | 227 | func (d decSymbol) newState() uint16 { 228 | return uint16(d >> 16) 229 | } 230 | 231 | func (d decSymbol) baselineInt() int { 232 | return int(d >> 32) 233 | } 234 | 235 | func (d *decSymbol) setNBits(nBits uint8) { 236 | const mask = 0xffffffffffffff00 237 | *d = (*d & mask) | decSymbol(nBits) 238 | } 239 | 240 | func (d *decSymbol) setAddBits(addBits uint8) { 241 | const mask = 0xffffffffffff00ff 242 | *d = (*d & mask) | (decSymbol(addBits) << 8) 243 | } 244 | 245 | func (d *decSymbol) setNewState(state uint16) { 246 | const mask = 0xffffffff0000ffff 247 | *d = (*d & mask) | decSymbol(state)<<16 248 | } 249 | 250 | func (d *decSymbol) setExt(addBits uint8, baseline uint32) { 251 | const mask = 0xffff00ff 252 | *d = (*d & mask) | (decSymbol(addBits) << 8) | (decSymbol(baseline) << 32) 253 | } 254 | 255 | // decSymbolValue returns the transformed decSymbol for the given symbol. 256 | func decSymbolValue(symb uint8, t []baseOffset) (decSymbol, error) { 257 | if int(symb) >= len(t) { 258 | return 0, fmt.Errorf("rle symbol %d >= max %d", symb, len(t)) 259 | } 260 | lu := t[symb] 261 | return newDecSymbol(0, lu.addBits, 0, lu.baseLine), nil 262 | } 263 | 264 | // setRLE will set the decoder til RLE mode. 265 | func (s *fseDecoder) setRLE(symbol decSymbol) { 266 | s.actualTableLog = 0 267 | s.maxBits = symbol.addBits() 268 | s.dt[0] = symbol 269 | } 270 | 271 | // transform will transform the decoder table into a table usable for 272 | // decoding without having to apply the transformation while decoding. 273 | // The state will contain the base value and the number of bits to read. 274 | func (s *fseDecoder) transform(t []baseOffset) error { 275 | tableSize := uint16(1 << s.actualTableLog) 276 | s.maxBits = 0 277 | for i, v := range s.dt[:tableSize] { 278 | add := v.addBits() 279 | if int(add) >= len(t) { 280 | return fmt.Errorf("invalid decoding table entry %d, symbol %d >= max (%d)", i, v.addBits(), len(t)) 281 | } 282 | lu := t[add] 283 | if lu.addBits > s.maxBits { 284 | s.maxBits = lu.addBits 285 | } 286 | v.setExt(lu.addBits, lu.baseLine) 287 | s.dt[i] = v 288 | } 289 | return nil 290 | } 291 | 292 | type fseState struct { 293 | dt []decSymbol 294 | state decSymbol 295 | } 296 | 297 | // Initialize and decodeAsync first state and symbol. 298 | func (s *fseState) init(br *bitReader, tableLog uint8, dt []decSymbol) { 299 | s.dt = dt 300 | br.fill() 301 | s.state = dt[br.getBits(tableLog)] 302 | } 303 | 304 | // final returns the current state symbol without decoding the next. 305 | func (s decSymbol) final() (int, uint8) { 306 | return s.baselineInt(), s.addBits() 307 | } 308 | -------------------------------------------------------------------------------- /compress/zstd/fse_decoder_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build amd64 && !appengine && !noasm && gc 2 | // +build amd64,!appengine,!noasm,gc 3 | 4 | package zstd 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | type buildDtableAsmContext struct { 11 | // inputs 12 | stateTable *uint16 13 | norm *int16 14 | dt *uint64 15 | 16 | // outputs --- set by the procedure in the case of error; 17 | // for interpretation please see the error handling part below 18 | errParam1 uint64 19 | errParam2 uint64 20 | } 21 | 22 | // buildDtable_asm is an x86 assembly implementation of fseDecoder.buildDtable. 23 | // Function returns non-zero exit code on error. 24 | // 25 | //go:noescape 26 | func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int 27 | 28 | // please keep in sync with _generate/gen_fse.go 29 | const ( 30 | errorCorruptedNormalizedCounter = 1 31 | errorNewStateTooBig = 2 32 | errorNewStateNoBits = 3 33 | ) 34 | 35 | // buildDtable will build the decoding table. 36 | func (s *fseDecoder) buildDtable() error { 37 | ctx := buildDtableAsmContext{ 38 | stateTable: &s.stateTable[0], 39 | norm: &s.norm[0], 40 | dt: (*uint64)(&s.dt[0]), 41 | } 42 | code := buildDtable_asm(s, &ctx) 43 | 44 | if code != 0 { 45 | switch code { 46 | case errorCorruptedNormalizedCounter: 47 | position := ctx.errParam1 48 | return fmt.Errorf("corrupted input (position=%d, expected 0)", position) 49 | 50 | case errorNewStateTooBig: 51 | newState := decSymbol(ctx.errParam1) 52 | size := ctx.errParam2 53 | return fmt.Errorf("newState (%d) outside table size (%d)", newState, size) 54 | 55 | case errorNewStateNoBits: 56 | newState := decSymbol(ctx.errParam1) 57 | oldState := decSymbol(ctx.errParam2) 58 | return fmt.Errorf("newState (%d) == oldState (%d) and no bits", newState, oldState) 59 | 60 | default: 61 | return fmt.Errorf("buildDtable_asm returned unhandled nonzero code = %d", code) 62 | } 63 | } 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /compress/zstd/fse_decoder_amd64.s: -------------------------------------------------------------------------------- 1 | // Code generated by command: go run gen_fse.go -out ../fse_decoder_amd64.s -pkg=zstd. DO NOT EDIT. 2 | 3 | //go:build !appengine && !noasm && gc && !noasm 4 | 5 | // func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int 6 | TEXT ·buildDtable_asm(SB), $0-24 7 | MOVQ ctx+8(FP), CX 8 | MOVQ s+0(FP), DI 9 | 10 | // Load values 11 | MOVBQZX 4098(DI), DX 12 | XORQ AX, AX 13 | BTSQ DX, AX 14 | MOVQ (CX), BX 15 | MOVQ 16(CX), SI 16 | LEAQ -1(AX), R8 17 | MOVQ 8(CX), CX 18 | MOVWQZX 4096(DI), DI 19 | 20 | // End load values 21 | // Init, lay down lowprob symbols 22 | XORQ R9, R9 23 | JMP init_main_loop_condition 24 | 25 | init_main_loop: 26 | MOVWQSX (CX)(R9*2), R10 27 | CMPW R10, $-1 28 | JNE do_not_update_high_threshold 29 | MOVB R9, 1(SI)(R8*8) 30 | DECQ R8 31 | MOVQ $0x0000000000000001, R10 32 | 33 | do_not_update_high_threshold: 34 | MOVW R10, (BX)(R9*2) 35 | INCQ R9 36 | 37 | init_main_loop_condition: 38 | CMPQ R9, DI 39 | JL init_main_loop 40 | 41 | // Spread symbols 42 | // Calculate table step 43 | MOVQ AX, R9 44 | SHRQ $0x01, R9 45 | MOVQ AX, R10 46 | SHRQ $0x03, R10 47 | LEAQ 3(R9)(R10*1), R9 48 | 49 | // Fill add bits values 50 | LEAQ -1(AX), R10 51 | XORQ R11, R11 52 | XORQ R12, R12 53 | JMP spread_main_loop_condition 54 | 55 | spread_main_loop: 56 | XORQ R13, R13 57 | MOVWQSX (CX)(R12*2), R14 58 | JMP spread_inner_loop_condition 59 | 60 | spread_inner_loop: 61 | MOVB R12, 1(SI)(R11*8) 62 | 63 | adjust_position: 64 | ADDQ R9, R11 65 | ANDQ R10, R11 66 | CMPQ R11, R8 67 | JG adjust_position 68 | INCQ R13 69 | 70 | spread_inner_loop_condition: 71 | CMPQ R13, R14 72 | JL spread_inner_loop 73 | INCQ R12 74 | 75 | spread_main_loop_condition: 76 | CMPQ R12, DI 77 | JL spread_main_loop 78 | TESTQ R11, R11 79 | JZ spread_check_ok 80 | MOVQ ctx+8(FP), AX 81 | MOVQ R11, 24(AX) 82 | MOVQ $+1, ret+16(FP) 83 | RET 84 | 85 | spread_check_ok: 86 | // Build Decoding table 87 | XORQ DI, DI 88 | 89 | build_table_main_table: 90 | MOVBQZX 1(SI)(DI*8), CX 91 | MOVWQZX (BX)(CX*2), R8 92 | LEAQ 1(R8), R9 93 | MOVW R9, (BX)(CX*2) 94 | MOVQ R8, R9 95 | BSRQ R9, R9 96 | MOVQ DX, CX 97 | SUBQ R9, CX 98 | SHLQ CL, R8 99 | SUBQ AX, R8 100 | MOVB CL, (SI)(DI*8) 101 | MOVW R8, 2(SI)(DI*8) 102 | CMPQ R8, AX 103 | JLE build_table_check1_ok 104 | MOVQ ctx+8(FP), CX 105 | MOVQ R8, 24(CX) 106 | MOVQ AX, 32(CX) 107 | MOVQ $+2, ret+16(FP) 108 | RET 109 | 110 | build_table_check1_ok: 111 | TESTB CL, CL 112 | JNZ build_table_check2_ok 113 | CMPW R8, DI 114 | JNE build_table_check2_ok 115 | MOVQ ctx+8(FP), AX 116 | MOVQ R8, 24(AX) 117 | MOVQ DI, 32(AX) 118 | MOVQ $+3, ret+16(FP) 119 | RET 120 | 121 | build_table_check2_ok: 122 | INCQ DI 123 | CMPQ DI, AX 124 | JL build_table_main_table 125 | MOVQ $+0, ret+16(FP) 126 | RET 127 | -------------------------------------------------------------------------------- /compress/zstd/fse_decoder_generic.go: -------------------------------------------------------------------------------- 1 | //go:build !amd64 || appengine || !gc || noasm 2 | // +build !amd64 appengine !gc noasm 3 | 4 | package zstd 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | ) 10 | 11 | // buildDtable will build the decoding table. 12 | func (s *fseDecoder) buildDtable() error { 13 | tableSize := uint32(1 << s.actualTableLog) 14 | highThreshold := tableSize - 1 15 | symbolNext := s.stateTable[:256] 16 | 17 | // Init, lay down lowprob symbols 18 | { 19 | for i, v := range s.norm[:s.symbolLen] { 20 | if v == -1 { 21 | s.dt[highThreshold].setAddBits(uint8(i)) 22 | highThreshold-- 23 | v = 1 24 | } 25 | symbolNext[i] = uint16(v) 26 | } 27 | } 28 | 29 | // Spread symbols 30 | { 31 | tableMask := tableSize - 1 32 | step := tableStep(tableSize) 33 | position := uint32(0) 34 | for ss, v := range s.norm[:s.symbolLen] { 35 | for i := 0; i < int(v); i++ { 36 | s.dt[position].setAddBits(uint8(ss)) 37 | for { 38 | // lowprob area 39 | position = (position + step) & tableMask 40 | if position <= highThreshold { 41 | break 42 | } 43 | } 44 | } 45 | } 46 | if position != 0 { 47 | // position must reach all cells once, otherwise normalizedCounter is incorrect 48 | return errors.New("corrupted input (position != 0)") 49 | } 50 | } 51 | 52 | // Build Decoding table 53 | { 54 | tableSize := uint16(1 << s.actualTableLog) 55 | for u, v := range s.dt[:tableSize] { 56 | symbol := v.addBits() 57 | nextState := symbolNext[symbol] 58 | symbolNext[symbol] = nextState + 1 59 | nBits := s.actualTableLog - byte(highBits(uint32(nextState))) 60 | s.dt[u&maxTableMask].setNBits(nBits) 61 | newState := (nextState << nBits) - tableSize 62 | if newState > tableSize { 63 | return fmt.Errorf("newState (%d) outside table size (%d)", newState, tableSize) 64 | } 65 | if newState == uint16(u) && nBits == 0 { 66 | // Seems weird that this is possible with nbits > 0. 67 | return fmt.Errorf("newState (%d) == oldState (%d) and no bits", newState, u) 68 | } 69 | s.dt[u&maxTableMask].setNewState(newState) 70 | } 71 | } 72 | return nil 73 | } 74 | -------------------------------------------------------------------------------- /compress/zstd/fse_predefined.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019+ Klaus Post. All rights reserved. 2 | // License information can be found in the LICENSE file. 3 | // Based on work by Yann Collet, released under BSD License. 4 | 5 | package zstd 6 | 7 | import ( 8 | "fmt" 9 | "math" 10 | "sync" 11 | ) 12 | 13 | var ( 14 | // fsePredef are the predefined fse tables as defined here: 15 | // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#default-distributions 16 | // These values are already transformed. 17 | fsePredef [3]fseDecoder 18 | 19 | // fsePredefEnc are the predefined encoder based on fse tables as defined here: 20 | // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#default-distributions 21 | // These values are already transformed. 22 | fsePredefEnc [3]fseEncoder 23 | 24 | // symbolTableX contain the transformations needed for each type as defined in 25 | // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#the-codes-for-literals-lengths-match-lengths-and-offsets 26 | symbolTableX [3][]baseOffset 27 | 28 | // maxTableSymbol is the biggest supported symbol for each table type 29 | // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#the-codes-for-literals-lengths-match-lengths-and-offsets 30 | maxTableSymbol = [3]uint8{tableLiteralLengths: maxLiteralLengthSymbol, tableOffsets: maxOffsetLengthSymbol, tableMatchLengths: maxMatchLengthSymbol} 31 | 32 | // bitTables is the bits table for each table. 33 | bitTables = [3][]byte{tableLiteralLengths: llBitsTable[:], tableOffsets: nil, tableMatchLengths: mlBitsTable[:]} 34 | ) 35 | 36 | type tableIndex uint8 37 | 38 | const ( 39 | // indexes for fsePredef and symbolTableX 40 | tableLiteralLengths tableIndex = 0 41 | tableOffsets tableIndex = 1 42 | tableMatchLengths tableIndex = 2 43 | 44 | maxLiteralLengthSymbol = 35 45 | maxOffsetLengthSymbol = 30 46 | maxMatchLengthSymbol = 52 47 | ) 48 | 49 | // baseOffset is used for calculating transformations. 50 | type baseOffset struct { 51 | baseLine uint32 52 | addBits uint8 53 | } 54 | 55 | // fillBase will precalculate base offsets with the given bit distributions. 56 | func fillBase(dst []baseOffset, base uint32, bits ...uint8) { 57 | if len(bits) != len(dst) { 58 | panic(fmt.Sprintf("len(dst) (%d) != len(bits) (%d)", len(dst), len(bits))) 59 | } 60 | for i, bit := range bits { 61 | if base > math.MaxInt32 { 62 | panic("invalid decoding table, base overflows int32") 63 | } 64 | 65 | dst[i] = baseOffset{ 66 | baseLine: base, 67 | addBits: bit, 68 | } 69 | base += 1 << bit 70 | } 71 | } 72 | 73 | var predef sync.Once 74 | 75 | func initPredefined() { 76 | predef.Do(func() { 77 | // Literals length codes 78 | tmp := make([]baseOffset, 36) 79 | for i := range tmp[:16] { 80 | tmp[i] = baseOffset{ 81 | baseLine: uint32(i), 82 | addBits: 0, 83 | } 84 | } 85 | fillBase(tmp[16:], 16, 1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) 86 | symbolTableX[tableLiteralLengths] = tmp 87 | 88 | // Match length codes 89 | tmp = make([]baseOffset, 53) 90 | for i := range tmp[:32] { 91 | tmp[i] = baseOffset{ 92 | // The transformation adds the 3 length. 93 | baseLine: uint32(i) + 3, 94 | addBits: 0, 95 | } 96 | } 97 | fillBase(tmp[32:], 35, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) 98 | symbolTableX[tableMatchLengths] = tmp 99 | 100 | // Offset codes 101 | tmp = make([]baseOffset, maxOffsetBits+1) 102 | tmp[1] = baseOffset{ 103 | baseLine: 1, 104 | addBits: 1, 105 | } 106 | fillBase(tmp[2:], 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) 107 | symbolTableX[tableOffsets] = tmp 108 | 109 | // Fill predefined tables and transform them. 110 | // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#default-distributions 111 | for i := range fsePredef[:] { 112 | f := &fsePredef[i] 113 | switch tableIndex(i) { 114 | case tableLiteralLengths: 115 | // https://github.com/facebook/zstd/blob/ededcfca57366461021c922720878c81a5854a0a/lib/decompress/zstd_decompress_block.c#L243 116 | f.actualTableLog = 6 117 | copy(f.norm[:], []int16{4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 118 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1, 119 | -1, -1, -1, -1}) 120 | f.symbolLen = 36 121 | case tableOffsets: 122 | // https://github.com/facebook/zstd/blob/ededcfca57366461021c922720878c81a5854a0a/lib/decompress/zstd_decompress_block.c#L281 123 | f.actualTableLog = 5 124 | copy(f.norm[:], []int16{ 125 | 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 126 | 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1}) 127 | f.symbolLen = 29 128 | case tableMatchLengths: 129 | //https://github.com/facebook/zstd/blob/ededcfca57366461021c922720878c81a5854a0a/lib/decompress/zstd_decompress_block.c#L304 130 | f.actualTableLog = 6 131 | copy(f.norm[:], []int16{ 132 | 1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 133 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 134 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 135 | -1, -1, -1, -1, -1}) 136 | f.symbolLen = 53 137 | } 138 | if err := f.buildDtable(); err != nil { 139 | panic(fmt.Errorf("building table %v: %v", tableIndex(i), err)) 140 | } 141 | if err := f.transform(symbolTableX[i]); err != nil { 142 | panic(fmt.Errorf("building table %v: %v", tableIndex(i), err)) 143 | } 144 | f.preDefined = true 145 | 146 | // Create encoder as well 147 | enc := &fsePredefEnc[i] 148 | copy(enc.norm[:], f.norm[:]) 149 | enc.symbolLen = f.symbolLen 150 | enc.actualTableLog = f.actualTableLog 151 | if err := enc.buildCTable(); err != nil { 152 | panic(fmt.Errorf("building encoding table %v: %v", tableIndex(i), err)) 153 | } 154 | enc.setBits(bitTables[i]) 155 | enc.preDefined = true 156 | } 157 | }) 158 | } 159 | -------------------------------------------------------------------------------- /compress/zstd/hash.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019+ Klaus Post. All rights reserved. 2 | // License information can be found in the LICENSE file. 3 | // Based on work by Yann Collet, released under BSD License. 4 | 5 | package zstd 6 | 7 | const ( 8 | prime3bytes = 506832829 9 | prime4bytes = 2654435761 10 | prime5bytes = 889523592379 11 | prime6bytes = 227718039650203 12 | prime7bytes = 58295818150454627 13 | prime8bytes = 0xcf1bbcdcb7a56463 14 | ) 15 | 16 | // hashLen returns a hash of the lowest mls bytes of with length output bits. 17 | // mls must be >=3 and <=8. Any other value will return hash for 4 bytes. 18 | // length should always be < 32. 19 | // Preferably length and mls should be a constant for inlining. 20 | func hashLen(u uint64, length, mls uint8) uint32 { 21 | switch mls { 22 | case 3: 23 | return (uint32(u<<8) * prime3bytes) >> (32 - length) 24 | case 5: 25 | return uint32(((u << (64 - 40)) * prime5bytes) >> (64 - length)) 26 | case 6: 27 | return uint32(((u << (64 - 48)) * prime6bytes) >> (64 - length)) 28 | case 7: 29 | return uint32(((u << (64 - 56)) * prime7bytes) >> (64 - length)) 30 | case 8: 31 | return uint32((u * prime8bytes) >> (64 - length)) 32 | default: 33 | return (uint32(u) * prime4bytes) >> (32 - length) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /compress/zstd/history.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019+ Klaus Post. All rights reserved. 2 | // License information can be found in the LICENSE file. 3 | // Based on work by Yann Collet, released under BSD License. 4 | 5 | package zstd 6 | 7 | import ( 8 | "github.com/klauspost/compress/huff0" 9 | ) 10 | 11 | // history contains the information transferred between blocks. 12 | type history struct { 13 | // Literal decompression 14 | huffTree *huff0.Scratch 15 | 16 | // Sequence decompression 17 | decoders sequenceDecs 18 | recentOffsets [3]int 19 | 20 | // History buffer... 21 | b []byte 22 | 23 | // ignoreBuffer is meant to ignore a number of bytes 24 | // when checking for matches in history 25 | ignoreBuffer int 26 | 27 | windowSize int 28 | allocFrameBuffer int // needed? 29 | error bool 30 | dict *dict 31 | } 32 | 33 | // reset will reset the history to initial state of a frame. 34 | // The history must already have been initialized to the desired size. 35 | func (h *history) reset() { 36 | h.b = h.b[:0] 37 | h.ignoreBuffer = 0 38 | h.error = false 39 | h.recentOffsets = [3]int{1, 4, 8} 40 | h.decoders.freeDecoders() 41 | h.decoders = sequenceDecs{br: h.decoders.br} 42 | h.freeHuffDecoder() 43 | h.huffTree = nil 44 | h.dict = nil 45 | //printf("history created: %+v (l: %d, c: %d)", *h, len(h.b), cap(h.b)) 46 | } 47 | 48 | func (h *history) freeHuffDecoder() { 49 | if h.huffTree != nil { 50 | if h.dict == nil || h.dict.litEnc != h.huffTree { 51 | huffDecoderPool.Put(h.huffTree) 52 | h.huffTree = nil 53 | } 54 | } 55 | } 56 | 57 | func (h *history) setDict(dict *dict) { 58 | if dict == nil { 59 | return 60 | } 61 | h.dict = dict 62 | h.decoders.litLengths = dict.llDec 63 | h.decoders.offsets = dict.ofDec 64 | h.decoders.matchLengths = dict.mlDec 65 | h.decoders.dict = dict.content 66 | h.recentOffsets = dict.offsets 67 | h.huffTree = dict.litEnc 68 | } 69 | 70 | // append bytes to history. 71 | // This function will make sure there is space for it, 72 | // if the buffer has been allocated with enough extra space. 73 | func (h *history) append(b []byte) { 74 | if len(b) >= h.windowSize { 75 | // Discard all history by simply overwriting 76 | h.b = h.b[:h.windowSize] 77 | copy(h.b, b[len(b)-h.windowSize:]) 78 | return 79 | } 80 | 81 | // If there is space, append it. 82 | if len(b) < cap(h.b)-len(h.b) { 83 | h.b = append(h.b, b...) 84 | return 85 | } 86 | 87 | // Move data down so we only have window size left. 88 | // We know we have less than window size in b at this point. 89 | discard := len(b) + len(h.b) - h.windowSize 90 | copy(h.b, h.b[discard:]) 91 | h.b = h.b[:h.windowSize] 92 | copy(h.b[h.windowSize-len(b):], b) 93 | } 94 | 95 | // ensureBlock will ensure there is space for at least one block... 96 | func (h *history) ensureBlock() { 97 | if cap(h.b) < h.allocFrameBuffer { 98 | h.b = make([]byte, 0, h.allocFrameBuffer) 99 | return 100 | } 101 | 102 | avail := cap(h.b) - len(h.b) 103 | if avail >= h.windowSize || avail > maxCompressedBlockSize { 104 | return 105 | } 106 | // Move data down so we only have window size left. 107 | // We know we have less than window size in b at this point. 108 | discard := len(h.b) - h.windowSize 109 | copy(h.b, h.b[discard:]) 110 | h.b = h.b[:h.windowSize] 111 | } 112 | 113 | // append bytes to history without ever discarding anything. 114 | func (h *history) appendKeep(b []byte) { 115 | h.b = append(h.b, b...) 116 | } 117 | -------------------------------------------------------------------------------- /compress/zstd/internal/xxhash/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Caleb Spare 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /compress/zstd/internal/xxhash/xxhash.go: -------------------------------------------------------------------------------- 1 | // Package xxhash implements the 64-bit variant of xxHash (XXH64) as described 2 | // at http://cyan4973.github.io/xxHash/. 3 | // THIS IS VENDORED: Go to github.com/cespare/xxhash for original package. 4 | 5 | package xxhash 6 | 7 | import ( 8 | "encoding/binary" 9 | "errors" 10 | "math/bits" 11 | ) 12 | 13 | const ( 14 | prime1 uint64 = 11400714785074694791 15 | prime2 uint64 = 14029467366897019727 16 | prime3 uint64 = 1609587929392839161 17 | prime4 uint64 = 9650029242287828579 18 | prime5 uint64 = 2870177450012600261 19 | ) 20 | 21 | // Store the primes in an array as well. 22 | // 23 | // The consts are used when possible in Go code to avoid MOVs but we need a 24 | // contiguous array of the assembly code. 25 | var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5} 26 | 27 | // Digest implements hash.Hash64. 28 | type Digest struct { 29 | v1 uint64 30 | v2 uint64 31 | v3 uint64 32 | v4 uint64 33 | total uint64 34 | mem [32]byte 35 | n int // how much of mem is used 36 | } 37 | 38 | // New creates a new Digest that computes the 64-bit xxHash algorithm. 39 | func New() *Digest { 40 | var d Digest 41 | d.Reset() 42 | return &d 43 | } 44 | 45 | // Reset clears the Digest's state so that it can be reused. 46 | func (d *Digest) Reset() { 47 | d.v1 = primes[0] + prime2 48 | d.v2 = prime2 49 | d.v3 = 0 50 | d.v4 = -primes[0] 51 | d.total = 0 52 | d.n = 0 53 | } 54 | 55 | // Size always returns 8 bytes. 56 | func (d *Digest) Size() int { return 8 } 57 | 58 | // BlockSize always returns 32 bytes. 59 | func (d *Digest) BlockSize() int { return 32 } 60 | 61 | // Write adds more data to d. It always returns len(b), nil. 62 | func (d *Digest) Write(b []byte) (n int, err error) { 63 | n = len(b) 64 | d.total += uint64(n) 65 | 66 | memleft := d.mem[d.n&(len(d.mem)-1):] 67 | 68 | if d.n+n < 32 { 69 | // This new data doesn't even fill the current block. 70 | copy(memleft, b) 71 | d.n += n 72 | return 73 | } 74 | 75 | if d.n > 0 { 76 | // Finish off the partial block. 77 | c := copy(memleft, b) 78 | d.v1 = round(d.v1, u64(d.mem[0:8])) 79 | d.v2 = round(d.v2, u64(d.mem[8:16])) 80 | d.v3 = round(d.v3, u64(d.mem[16:24])) 81 | d.v4 = round(d.v4, u64(d.mem[24:32])) 82 | b = b[c:] 83 | d.n = 0 84 | } 85 | 86 | if len(b) >= 32 { 87 | // One or more full blocks left. 88 | nw := writeBlocks(d, b) 89 | b = b[nw:] 90 | } 91 | 92 | // Store any remaining partial block. 93 | copy(d.mem[:], b) 94 | d.n = len(b) 95 | 96 | return 97 | } 98 | 99 | // Sum appends the current hash to b and returns the resulting slice. 100 | func (d *Digest) Sum(b []byte) []byte { 101 | s := d.Sum64() 102 | return append( 103 | b, 104 | byte(s>>56), 105 | byte(s>>48), 106 | byte(s>>40), 107 | byte(s>>32), 108 | byte(s>>24), 109 | byte(s>>16), 110 | byte(s>>8), 111 | byte(s), 112 | ) 113 | } 114 | 115 | // Sum64 returns the current hash. 116 | func (d *Digest) Sum64() uint64 { 117 | var h uint64 118 | 119 | if d.total >= 32 { 120 | v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 121 | h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) 122 | h = mergeRound(h, v1) 123 | h = mergeRound(h, v2) 124 | h = mergeRound(h, v3) 125 | h = mergeRound(h, v4) 126 | } else { 127 | h = d.v3 + prime5 128 | } 129 | 130 | h += d.total 131 | 132 | b := d.mem[:d.n&(len(d.mem)-1)] 133 | for ; len(b) >= 8; b = b[8:] { 134 | k1 := round(0, u64(b[:8])) 135 | h ^= k1 136 | h = rol27(h)*prime1 + prime4 137 | } 138 | if len(b) >= 4 { 139 | h ^= uint64(u32(b[:4])) * prime1 140 | h = rol23(h)*prime2 + prime3 141 | b = b[4:] 142 | } 143 | for ; len(b) > 0; b = b[1:] { 144 | h ^= uint64(b[0]) * prime5 145 | h = rol11(h) * prime1 146 | } 147 | 148 | h ^= h >> 33 149 | h *= prime2 150 | h ^= h >> 29 151 | h *= prime3 152 | h ^= h >> 32 153 | 154 | return h 155 | } 156 | 157 | const ( 158 | magic = "xxh\x06" 159 | marshaledSize = len(magic) + 8*5 + 32 160 | ) 161 | 162 | // MarshalBinary implements the encoding.BinaryMarshaler interface. 163 | func (d *Digest) MarshalBinary() ([]byte, error) { 164 | b := make([]byte, 0, marshaledSize) 165 | b = append(b, magic...) 166 | b = appendUint64(b, d.v1) 167 | b = appendUint64(b, d.v2) 168 | b = appendUint64(b, d.v3) 169 | b = appendUint64(b, d.v4) 170 | b = appendUint64(b, d.total) 171 | b = append(b, d.mem[:d.n]...) 172 | b = b[:len(b)+len(d.mem)-d.n] 173 | return b, nil 174 | } 175 | 176 | // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. 177 | func (d *Digest) UnmarshalBinary(b []byte) error { 178 | if len(b) < len(magic) || string(b[:len(magic)]) != magic { 179 | return errors.New("xxhash: invalid hash state identifier") 180 | } 181 | if len(b) != marshaledSize { 182 | return errors.New("xxhash: invalid hash state size") 183 | } 184 | b = b[len(magic):] 185 | b, d.v1 = consumeUint64(b) 186 | b, d.v2 = consumeUint64(b) 187 | b, d.v3 = consumeUint64(b) 188 | b, d.v4 = consumeUint64(b) 189 | b, d.total = consumeUint64(b) 190 | copy(d.mem[:], b) 191 | d.n = int(d.total % uint64(len(d.mem))) 192 | return nil 193 | } 194 | 195 | func appendUint64(b []byte, x uint64) []byte { 196 | var a [8]byte 197 | binary.LittleEndian.PutUint64(a[:], x) 198 | return append(b, a[:]...) 199 | } 200 | 201 | func consumeUint64(b []byte) ([]byte, uint64) { 202 | x := u64(b) 203 | return b[8:], x 204 | } 205 | 206 | func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) } 207 | func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } 208 | 209 | func round(acc, input uint64) uint64 { 210 | acc += input * prime2 211 | acc = rol31(acc) 212 | acc *= prime1 213 | return acc 214 | } 215 | 216 | func mergeRound(acc, val uint64) uint64 { 217 | val = round(0, val) 218 | acc ^= val 219 | acc = acc*prime1 + prime4 220 | return acc 221 | } 222 | 223 | func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) } 224 | func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) } 225 | func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) } 226 | func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) } 227 | func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) } 228 | func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) } 229 | func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) } 230 | func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) } 231 | -------------------------------------------------------------------------------- /compress/zstd/internal/xxhash/xxhash_amd64.s: -------------------------------------------------------------------------------- 1 | //go:build !appengine && gc && !purego && !noasm 2 | // +build !appengine 3 | // +build gc 4 | // +build !purego 5 | // +build !noasm 6 | 7 | #include "textflag.h" 8 | 9 | // Registers: 10 | #define h AX 11 | #define d AX 12 | #define p SI // pointer to advance through b 13 | #define n DX 14 | #define end BX // loop end 15 | #define v1 R8 16 | #define v2 R9 17 | #define v3 R10 18 | #define v4 R11 19 | #define x R12 20 | #define prime1 R13 21 | #define prime2 R14 22 | #define prime4 DI 23 | 24 | #define round(acc, x) \ 25 | IMULQ prime2, x \ 26 | ADDQ x, acc \ 27 | ROLQ $31, acc \ 28 | IMULQ prime1, acc 29 | 30 | // round0 performs the operation x = round(0, x). 31 | #define round0(x) \ 32 | IMULQ prime2, x \ 33 | ROLQ $31, x \ 34 | IMULQ prime1, x 35 | 36 | // mergeRound applies a merge round on the two registers acc and x. 37 | // It assumes that prime1, prime2, and prime4 have been loaded. 38 | #define mergeRound(acc, x) \ 39 | round0(x) \ 40 | XORQ x, acc \ 41 | IMULQ prime1, acc \ 42 | ADDQ prime4, acc 43 | 44 | // blockLoop processes as many 32-byte blocks as possible, 45 | // updating v1, v2, v3, and v4. It assumes that there is at least one block 46 | // to process. 47 | #define blockLoop() \ 48 | loop: \ 49 | MOVQ +0(p), x \ 50 | round(v1, x) \ 51 | MOVQ +8(p), x \ 52 | round(v2, x) \ 53 | MOVQ +16(p), x \ 54 | round(v3, x) \ 55 | MOVQ +24(p), x \ 56 | round(v4, x) \ 57 | ADDQ $32, p \ 58 | CMPQ p, end \ 59 | JLE loop 60 | 61 | // func Sum64(b []byte) uint64 62 | TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 63 | // Load fixed primes. 64 | MOVQ ·primes+0(SB), prime1 65 | MOVQ ·primes+8(SB), prime2 66 | MOVQ ·primes+24(SB), prime4 67 | 68 | // Load slice. 69 | MOVQ b_base+0(FP), p 70 | MOVQ b_len+8(FP), n 71 | LEAQ (p)(n*1), end 72 | 73 | // The first loop limit will be len(b)-32. 74 | SUBQ $32, end 75 | 76 | // Check whether we have at least one block. 77 | CMPQ n, $32 78 | JLT noBlocks 79 | 80 | // Set up initial state (v1, v2, v3, v4). 81 | MOVQ prime1, v1 82 | ADDQ prime2, v1 83 | MOVQ prime2, v2 84 | XORQ v3, v3 85 | XORQ v4, v4 86 | SUBQ prime1, v4 87 | 88 | blockLoop() 89 | 90 | MOVQ v1, h 91 | ROLQ $1, h 92 | MOVQ v2, x 93 | ROLQ $7, x 94 | ADDQ x, h 95 | MOVQ v3, x 96 | ROLQ $12, x 97 | ADDQ x, h 98 | MOVQ v4, x 99 | ROLQ $18, x 100 | ADDQ x, h 101 | 102 | mergeRound(h, v1) 103 | mergeRound(h, v2) 104 | mergeRound(h, v3) 105 | mergeRound(h, v4) 106 | 107 | JMP afterBlocks 108 | 109 | noBlocks: 110 | MOVQ ·primes+32(SB), h 111 | 112 | afterBlocks: 113 | ADDQ n, h 114 | 115 | ADDQ $24, end 116 | CMPQ p, end 117 | JG try4 118 | 119 | loop8: 120 | MOVQ (p), x 121 | ADDQ $8, p 122 | round0(x) 123 | XORQ x, h 124 | ROLQ $27, h 125 | IMULQ prime1, h 126 | ADDQ prime4, h 127 | 128 | CMPQ p, end 129 | JLE loop8 130 | 131 | try4: 132 | ADDQ $4, end 133 | CMPQ p, end 134 | JG try1 135 | 136 | MOVL (p), x 137 | ADDQ $4, p 138 | IMULQ prime1, x 139 | XORQ x, h 140 | 141 | ROLQ $23, h 142 | IMULQ prime2, h 143 | ADDQ ·primes+16(SB), h 144 | 145 | try1: 146 | ADDQ $4, end 147 | CMPQ p, end 148 | JGE finalize 149 | 150 | loop1: 151 | MOVBQZX (p), x 152 | ADDQ $1, p 153 | IMULQ ·primes+32(SB), x 154 | XORQ x, h 155 | ROLQ $11, h 156 | IMULQ prime1, h 157 | 158 | CMPQ p, end 159 | JL loop1 160 | 161 | finalize: 162 | MOVQ h, x 163 | SHRQ $33, x 164 | XORQ x, h 165 | IMULQ prime2, h 166 | MOVQ h, x 167 | SHRQ $29, x 168 | XORQ x, h 169 | IMULQ ·primes+16(SB), h 170 | MOVQ h, x 171 | SHRQ $32, x 172 | XORQ x, h 173 | 174 | MOVQ h, ret+24(FP) 175 | RET 176 | 177 | // func writeBlocks(d *Digest, b []byte) int 178 | TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 179 | // Load fixed primes needed for round. 180 | MOVQ ·primes+0(SB), prime1 181 | MOVQ ·primes+8(SB), prime2 182 | 183 | // Load slice. 184 | MOVQ b_base+8(FP), p 185 | MOVQ b_len+16(FP), n 186 | LEAQ (p)(n*1), end 187 | SUBQ $32, end 188 | 189 | // Load vN from d. 190 | MOVQ s+0(FP), d 191 | MOVQ 0(d), v1 192 | MOVQ 8(d), v2 193 | MOVQ 16(d), v3 194 | MOVQ 24(d), v4 195 | 196 | // We don't need to check the loop condition here; this function is 197 | // always called with at least one block of data to process. 198 | blockLoop() 199 | 200 | // Copy vN back to d. 201 | MOVQ v1, 0(d) 202 | MOVQ v2, 8(d) 203 | MOVQ v3, 16(d) 204 | MOVQ v4, 24(d) 205 | 206 | // The number of bytes written is p minus the old base pointer. 207 | SUBQ b_base+8(FP), p 208 | MOVQ p, ret+32(FP) 209 | 210 | RET 211 | -------------------------------------------------------------------------------- /compress/zstd/internal/xxhash/xxhash_arm64.s: -------------------------------------------------------------------------------- 1 | //go:build !appengine && gc && !purego && !noasm 2 | // +build !appengine 3 | // +build gc 4 | // +build !purego 5 | // +build !noasm 6 | 7 | #include "textflag.h" 8 | 9 | // Registers: 10 | #define digest R1 11 | #define h R2 // return value 12 | #define p R3 // input pointer 13 | #define n R4 // input length 14 | #define nblocks R5 // n / 32 15 | #define prime1 R7 16 | #define prime2 R8 17 | #define prime3 R9 18 | #define prime4 R10 19 | #define prime5 R11 20 | #define v1 R12 21 | #define v2 R13 22 | #define v3 R14 23 | #define v4 R15 24 | #define x1 R20 25 | #define x2 R21 26 | #define x3 R22 27 | #define x4 R23 28 | 29 | #define round(acc, x) \ 30 | MADD prime2, acc, x, acc \ 31 | ROR $64-31, acc \ 32 | MUL prime1, acc 33 | 34 | // round0 performs the operation x = round(0, x). 35 | #define round0(x) \ 36 | MUL prime2, x \ 37 | ROR $64-31, x \ 38 | MUL prime1, x 39 | 40 | #define mergeRound(acc, x) \ 41 | round0(x) \ 42 | EOR x, acc \ 43 | MADD acc, prime4, prime1, acc 44 | 45 | // blockLoop processes as many 32-byte blocks as possible, 46 | // updating v1, v2, v3, and v4. It assumes that n >= 32. 47 | #define blockLoop() \ 48 | LSR $5, n, nblocks \ 49 | PCALIGN $16 \ 50 | loop: \ 51 | LDP.P 16(p), (x1, x2) \ 52 | LDP.P 16(p), (x3, x4) \ 53 | round(v1, x1) \ 54 | round(v2, x2) \ 55 | round(v3, x3) \ 56 | round(v4, x4) \ 57 | SUB $1, nblocks \ 58 | CBNZ nblocks, loop 59 | 60 | // func Sum64(b []byte) uint64 61 | TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 62 | LDP b_base+0(FP), (p, n) 63 | 64 | LDP ·primes+0(SB), (prime1, prime2) 65 | LDP ·primes+16(SB), (prime3, prime4) 66 | MOVD ·primes+32(SB), prime5 67 | 68 | CMP $32, n 69 | CSEL LT, prime5, ZR, h // if n < 32 { h = prime5 } else { h = 0 } 70 | BLT afterLoop 71 | 72 | ADD prime1, prime2, v1 73 | MOVD prime2, v2 74 | MOVD $0, v3 75 | NEG prime1, v4 76 | 77 | blockLoop() 78 | 79 | ROR $64-1, v1, x1 80 | ROR $64-7, v2, x2 81 | ADD x1, x2 82 | ROR $64-12, v3, x3 83 | ROR $64-18, v4, x4 84 | ADD x3, x4 85 | ADD x2, x4, h 86 | 87 | mergeRound(h, v1) 88 | mergeRound(h, v2) 89 | mergeRound(h, v3) 90 | mergeRound(h, v4) 91 | 92 | afterLoop: 93 | ADD n, h 94 | 95 | TBZ $4, n, try8 96 | LDP.P 16(p), (x1, x2) 97 | 98 | round0(x1) 99 | 100 | // NOTE: here and below, sequencing the EOR after the ROR (using a 101 | // rotated register) is worth a small but measurable speedup for small 102 | // inputs. 103 | ROR $64-27, h 104 | EOR x1 @> 64-27, h, h 105 | MADD h, prime4, prime1, h 106 | 107 | round0(x2) 108 | ROR $64-27, h 109 | EOR x2 @> 64-27, h, h 110 | MADD h, prime4, prime1, h 111 | 112 | try8: 113 | TBZ $3, n, try4 114 | MOVD.P 8(p), x1 115 | 116 | round0(x1) 117 | ROR $64-27, h 118 | EOR x1 @> 64-27, h, h 119 | MADD h, prime4, prime1, h 120 | 121 | try4: 122 | TBZ $2, n, try2 123 | MOVWU.P 4(p), x2 124 | 125 | MUL prime1, x2 126 | ROR $64-23, h 127 | EOR x2 @> 64-23, h, h 128 | MADD h, prime3, prime2, h 129 | 130 | try2: 131 | TBZ $1, n, try1 132 | MOVHU.P 2(p), x3 133 | AND $255, x3, x1 134 | LSR $8, x3, x2 135 | 136 | MUL prime5, x1 137 | ROR $64-11, h 138 | EOR x1 @> 64-11, h, h 139 | MUL prime1, h 140 | 141 | MUL prime5, x2 142 | ROR $64-11, h 143 | EOR x2 @> 64-11, h, h 144 | MUL prime1, h 145 | 146 | try1: 147 | TBZ $0, n, finalize 148 | MOVBU (p), x4 149 | 150 | MUL prime5, x4 151 | ROR $64-11, h 152 | EOR x4 @> 64-11, h, h 153 | MUL prime1, h 154 | 155 | finalize: 156 | EOR h >> 33, h 157 | MUL prime2, h 158 | EOR h >> 29, h 159 | MUL prime3, h 160 | EOR h >> 32, h 161 | 162 | MOVD h, ret+24(FP) 163 | RET 164 | 165 | // func writeBlocks(s *Digest, b []byte) int 166 | TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 167 | LDP ·primes+0(SB), (prime1, prime2) 168 | 169 | // Load state. Assume v[1-4] are stored contiguously. 170 | MOVD s+0(FP), digest 171 | LDP 0(digest), (v1, v2) 172 | LDP 16(digest), (v3, v4) 173 | 174 | LDP b_base+8(FP), (p, n) 175 | 176 | blockLoop() 177 | 178 | // Store updated state. 179 | STP (v1, v2), 0(digest) 180 | STP (v3, v4), 16(digest) 181 | 182 | BIC $31, n 183 | MOVD n, ret+32(FP) 184 | RET 185 | -------------------------------------------------------------------------------- /compress/zstd/internal/xxhash/xxhash_asm.go: -------------------------------------------------------------------------------- 1 | //go:build (amd64 || arm64) && !appengine && gc && !purego && !noasm 2 | // +build amd64 arm64 3 | // +build !appengine 4 | // +build gc 5 | // +build !purego 6 | // +build !noasm 7 | 8 | package xxhash 9 | 10 | // Sum64 computes the 64-bit xxHash digest of b. 11 | // 12 | //go:noescape 13 | func Sum64(b []byte) uint64 14 | 15 | //go:noescape 16 | func writeBlocks(s *Digest, b []byte) int 17 | -------------------------------------------------------------------------------- /compress/zstd/internal/xxhash/xxhash_other.go: -------------------------------------------------------------------------------- 1 | //go:build (!amd64 && !arm64) || appengine || !gc || purego || noasm 2 | // +build !amd64,!arm64 appengine !gc purego noasm 3 | 4 | package xxhash 5 | 6 | // Sum64 computes the 64-bit xxHash digest of b. 7 | func Sum64(b []byte) uint64 { 8 | // A simpler version would be 9 | // d := New() 10 | // d.Write(b) 11 | // return d.Sum64() 12 | // but this is faster, particularly for small inputs. 13 | 14 | n := len(b) 15 | var h uint64 16 | 17 | if n >= 32 { 18 | v1 := primes[0] + prime2 19 | v2 := prime2 20 | v3 := uint64(0) 21 | v4 := -primes[0] 22 | for len(b) >= 32 { 23 | v1 = round(v1, u64(b[0:8:len(b)])) 24 | v2 = round(v2, u64(b[8:16:len(b)])) 25 | v3 = round(v3, u64(b[16:24:len(b)])) 26 | v4 = round(v4, u64(b[24:32:len(b)])) 27 | b = b[32:len(b):len(b)] 28 | } 29 | h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) 30 | h = mergeRound(h, v1) 31 | h = mergeRound(h, v2) 32 | h = mergeRound(h, v3) 33 | h = mergeRound(h, v4) 34 | } else { 35 | h = prime5 36 | } 37 | 38 | h += uint64(n) 39 | 40 | for ; len(b) >= 8; b = b[8:] { 41 | k1 := round(0, u64(b[:8])) 42 | h ^= k1 43 | h = rol27(h)*prime1 + prime4 44 | } 45 | if len(b) >= 4 { 46 | h ^= uint64(u32(b[:4])) * prime1 47 | h = rol23(h)*prime2 + prime3 48 | b = b[4:] 49 | } 50 | for ; len(b) > 0; b = b[1:] { 51 | h ^= uint64(b[0]) * prime5 52 | h = rol11(h) * prime1 53 | } 54 | 55 | h ^= h >> 33 56 | h *= prime2 57 | h ^= h >> 29 58 | h *= prime3 59 | h ^= h >> 32 60 | 61 | return h 62 | } 63 | 64 | func writeBlocks(d *Digest, b []byte) int { 65 | v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 66 | n := len(b) 67 | for len(b) >= 32 { 68 | v1 = round(v1, u64(b[0:8:len(b)])) 69 | v2 = round(v2, u64(b[8:16:len(b)])) 70 | v3 = round(v3, u64(b[16:24:len(b)])) 71 | v4 = round(v4, u64(b[24:32:len(b)])) 72 | b = b[32:len(b):len(b)] 73 | } 74 | d.v1, d.v2, d.v3, d.v4 = v1, v2, v3, v4 75 | return n - len(b) 76 | } 77 | -------------------------------------------------------------------------------- /compress/zstd/internal/xxhash/xxhash_safe.go: -------------------------------------------------------------------------------- 1 | package xxhash 2 | 3 | // Sum64String computes the 64-bit xxHash digest of s. 4 | func Sum64String(s string) uint64 { 5 | return Sum64([]byte(s)) 6 | } 7 | 8 | // WriteString adds more data to d. It always returns len(s), nil. 9 | func (d *Digest) WriteString(s string) (n int, err error) { 10 | return d.Write([]byte(s)) 11 | } 12 | -------------------------------------------------------------------------------- /compress/zstd/matchlen_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build amd64 && !appengine && !noasm && gc 2 | // +build amd64,!appengine,!noasm,gc 3 | 4 | // Copyright 2019+ Klaus Post. All rights reserved. 5 | // License information can be found in the LICENSE file. 6 | 7 | package zstd 8 | 9 | // matchLen returns how many bytes match in a and b 10 | // 11 | // It assumes that: 12 | // 13 | // len(a) <= len(b) and len(a) > 0 14 | // 15 | //go:noescape 16 | func matchLen(a []byte, b []byte) int 17 | -------------------------------------------------------------------------------- /compress/zstd/matchlen_amd64.s: -------------------------------------------------------------------------------- 1 | // Copied from S2 implementation. 2 | 3 | //go:build !appengine && !noasm && gc && !noasm 4 | 5 | #include "textflag.h" 6 | 7 | // func matchLen(a []byte, b []byte) int 8 | TEXT ·matchLen(SB), NOSPLIT, $0-56 9 | MOVQ a_base+0(FP), AX 10 | MOVQ b_base+24(FP), CX 11 | MOVQ a_len+8(FP), DX 12 | 13 | // matchLen 14 | XORL SI, SI 15 | CMPL DX, $0x08 16 | JB matchlen_match4_standalone 17 | 18 | matchlen_loopback_standalone: 19 | MOVQ (AX)(SI*1), BX 20 | XORQ (CX)(SI*1), BX 21 | JZ matchlen_loop_standalone 22 | 23 | #ifdef GOAMD64_v3 24 | TZCNTQ BX, BX 25 | #else 26 | BSFQ BX, BX 27 | #endif 28 | SHRL $0x03, BX 29 | LEAL (SI)(BX*1), SI 30 | JMP gen_match_len_end 31 | 32 | matchlen_loop_standalone: 33 | LEAL -8(DX), DX 34 | LEAL 8(SI), SI 35 | CMPL DX, $0x08 36 | JAE matchlen_loopback_standalone 37 | 38 | matchlen_match4_standalone: 39 | CMPL DX, $0x04 40 | JB matchlen_match2_standalone 41 | MOVL (AX)(SI*1), BX 42 | CMPL (CX)(SI*1), BX 43 | JNE matchlen_match2_standalone 44 | LEAL -4(DX), DX 45 | LEAL 4(SI), SI 46 | 47 | matchlen_match2_standalone: 48 | CMPL DX, $0x02 49 | JB matchlen_match1_standalone 50 | MOVW (AX)(SI*1), BX 51 | CMPW (CX)(SI*1), BX 52 | JNE matchlen_match1_standalone 53 | LEAL -2(DX), DX 54 | LEAL 2(SI), SI 55 | 56 | matchlen_match1_standalone: 57 | CMPL DX, $0x01 58 | JB gen_match_len_end 59 | MOVB (AX)(SI*1), BL 60 | CMPB (CX)(SI*1), BL 61 | JNE gen_match_len_end 62 | INCL SI 63 | 64 | gen_match_len_end: 65 | MOVQ SI, ret+48(FP) 66 | RET 67 | -------------------------------------------------------------------------------- /compress/zstd/matchlen_generic.go: -------------------------------------------------------------------------------- 1 | //go:build !amd64 || appengine || !gc || noasm 2 | // +build !amd64 appengine !gc noasm 3 | 4 | // Copyright 2019+ Klaus Post. All rights reserved. 5 | // License information can be found in the LICENSE file. 6 | 7 | package zstd 8 | 9 | import ( 10 | "math/bits" 11 | 12 | "github.com/klauspost/compress/internal/le" 13 | ) 14 | 15 | // matchLen returns the maximum common prefix length of a and b. 16 | // a must be the shortest of the two. 17 | func matchLen(a, b []byte) (n int) { 18 | left := len(a) 19 | for left >= 8 { 20 | diff := le.Load64(a, n) ^ le.Load64(b, n) 21 | if diff != 0 { 22 | return n + bits.TrailingZeros64(diff)>>3 23 | } 24 | n += 8 25 | left -= 8 26 | } 27 | a = a[n:] 28 | b = b[n:] 29 | 30 | for i := range a { 31 | if a[i] != b[i] { 32 | break 33 | } 34 | n++ 35 | } 36 | return n 37 | 38 | } 39 | -------------------------------------------------------------------------------- /compress/zstd/seqdec_generic.go: -------------------------------------------------------------------------------- 1 | //go:build !amd64 || appengine || !gc || noasm 2 | // +build !amd64 appengine !gc noasm 3 | 4 | package zstd 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | ) 10 | 11 | // decode sequences from the stream with the provided history but without dictionary. 12 | func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { 13 | return false, nil 14 | } 15 | 16 | // decode sequences from the stream without the provided history. 17 | func (s *sequenceDecs) decode(seqs []seqVals) error { 18 | br := s.br 19 | 20 | // Grab full sizes tables, to avoid bounds checks. 21 | llTable, mlTable, ofTable := s.litLengths.fse.dt[:maxTablesize], s.matchLengths.fse.dt[:maxTablesize], s.offsets.fse.dt[:maxTablesize] 22 | llState, mlState, ofState := s.litLengths.state.state, s.matchLengths.state.state, s.offsets.state.state 23 | s.seqSize = 0 24 | litRemain := len(s.literals) 25 | 26 | maxBlockSize := maxCompressedBlockSize 27 | if s.windowSize < maxBlockSize { 28 | maxBlockSize = s.windowSize 29 | } 30 | for i := range seqs { 31 | var ll, mo, ml int 32 | if br.cursor > 4+((maxOffsetBits+16+16)>>3) { 33 | // inlined function: 34 | // ll, mo, ml = s.nextFast(br, llState, mlState, ofState) 35 | 36 | // Final will not read from stream. 37 | var llB, mlB, moB uint8 38 | ll, llB = llState.final() 39 | ml, mlB = mlState.final() 40 | mo, moB = ofState.final() 41 | 42 | // extra bits are stored in reverse order. 43 | br.fillFast() 44 | mo += br.getBits(moB) 45 | if s.maxBits > 32 { 46 | br.fillFast() 47 | } 48 | ml += br.getBits(mlB) 49 | ll += br.getBits(llB) 50 | 51 | if moB > 1 { 52 | s.prevOffset[2] = s.prevOffset[1] 53 | s.prevOffset[1] = s.prevOffset[0] 54 | s.prevOffset[0] = mo 55 | } else { 56 | // mo = s.adjustOffset(mo, ll, moB) 57 | // Inlined for rather big speedup 58 | if ll == 0 { 59 | // There is an exception though, when current sequence's literals_length = 0. 60 | // In this case, repeated offsets are shifted by one, so an offset_value of 1 means Repeated_Offset2, 61 | // an offset_value of 2 means Repeated_Offset3, and an offset_value of 3 means Repeated_Offset1 - 1_byte. 62 | mo++ 63 | } 64 | 65 | if mo == 0 { 66 | mo = s.prevOffset[0] 67 | } else { 68 | var temp int 69 | if mo == 3 { 70 | temp = s.prevOffset[0] - 1 71 | } else { 72 | temp = s.prevOffset[mo] 73 | } 74 | 75 | if temp == 0 { 76 | // 0 is not valid; input is corrupted; force offset to 1 77 | println("WARNING: temp was 0") 78 | temp = 1 79 | } 80 | 81 | if mo != 1 { 82 | s.prevOffset[2] = s.prevOffset[1] 83 | } 84 | s.prevOffset[1] = s.prevOffset[0] 85 | s.prevOffset[0] = temp 86 | mo = temp 87 | } 88 | } 89 | br.fillFast() 90 | } else { 91 | if br.overread() { 92 | if debugDecoder { 93 | printf("reading sequence %d, exceeded available data\n", i) 94 | } 95 | return io.ErrUnexpectedEOF 96 | } 97 | ll, mo, ml = s.next(br, llState, mlState, ofState) 98 | br.fill() 99 | } 100 | 101 | if debugSequences { 102 | println("Seq", i, "Litlen:", ll, "mo:", mo, "(abs) ml:", ml) 103 | } 104 | // Evaluate. 105 | // We might be doing this async, so do it early. 106 | if mo == 0 && ml > 0 { 107 | return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) 108 | } 109 | if ml > maxMatchLen { 110 | return fmt.Errorf("match len (%d) bigger than max allowed length", ml) 111 | } 112 | s.seqSize += ll + ml 113 | if s.seqSize > maxBlockSize { 114 | return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) 115 | } 116 | litRemain -= ll 117 | if litRemain < 0 { 118 | return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, litRemain+ll) 119 | } 120 | seqs[i] = seqVals{ 121 | ll: ll, 122 | ml: ml, 123 | mo: mo, 124 | } 125 | if i == len(seqs)-1 { 126 | // This is the last sequence, so we shouldn't update state. 127 | break 128 | } 129 | 130 | // Manually inlined, ~ 5-20% faster 131 | // Update all 3 states at once. Approx 20% faster. 132 | nBits := llState.nbBits() + mlState.nbBits() + ofState.nbBits() 133 | if nBits == 0 { 134 | llState = llTable[llState.newState()&maxTableMask] 135 | mlState = mlTable[mlState.newState()&maxTableMask] 136 | ofState = ofTable[ofState.newState()&maxTableMask] 137 | } else { 138 | bits := br.get32BitsFast(nBits) 139 | lowBits := uint16(bits >> ((ofState.nbBits() + mlState.nbBits()) & 31)) 140 | llState = llTable[(llState.newState()+lowBits)&maxTableMask] 141 | 142 | lowBits = uint16(bits >> (ofState.nbBits() & 31)) 143 | lowBits &= bitMask[mlState.nbBits()&15] 144 | mlState = mlTable[(mlState.newState()+lowBits)&maxTableMask] 145 | 146 | lowBits = uint16(bits) & bitMask[ofState.nbBits()&15] 147 | ofState = ofTable[(ofState.newState()+lowBits)&maxTableMask] 148 | } 149 | } 150 | s.seqSize += litRemain 151 | if s.seqSize > maxBlockSize { 152 | return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) 153 | } 154 | err := br.close() 155 | if err != nil { 156 | printf("Closing sequences: %v, %+v\n", err, *br) 157 | } 158 | return err 159 | } 160 | 161 | // executeSimple handles cases when a dictionary is not used. 162 | func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error { 163 | // Ensure we have enough output size... 164 | if len(s.out)+s.seqSize > cap(s.out) { 165 | addBytes := s.seqSize + len(s.out) 166 | s.out = append(s.out, make([]byte, addBytes)...) 167 | s.out = s.out[:len(s.out)-addBytes] 168 | } 169 | 170 | if debugDecoder { 171 | printf("Execute %d seqs with literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize) 172 | } 173 | 174 | var t = len(s.out) 175 | out := s.out[:t+s.seqSize] 176 | 177 | for _, seq := range seqs { 178 | // Add literals 179 | copy(out[t:], s.literals[:seq.ll]) 180 | t += seq.ll 181 | s.literals = s.literals[seq.ll:] 182 | 183 | // Malformed input 184 | if seq.mo > t+len(hist) || seq.mo > s.windowSize { 185 | return fmt.Errorf("match offset (%d) bigger than current history (%d)", seq.mo, t+len(hist)) 186 | } 187 | 188 | // Copy from history. 189 | if v := seq.mo - t; v > 0 { 190 | // v is the start position in history from end. 191 | start := len(hist) - v 192 | if seq.ml > v { 193 | // Some goes into the current block. 194 | // Copy remainder of history 195 | copy(out[t:], hist[start:]) 196 | t += v 197 | seq.ml -= v 198 | } else { 199 | copy(out[t:], hist[start:start+seq.ml]) 200 | t += seq.ml 201 | continue 202 | } 203 | } 204 | 205 | // We must be in the current buffer now 206 | if seq.ml > 0 { 207 | start := t - seq.mo 208 | if seq.ml <= t-start { 209 | // No overlap 210 | copy(out[t:], out[start:start+seq.ml]) 211 | t += seq.ml 212 | } else { 213 | // Overlapping copy 214 | // Extend destination slice and copy one byte at the time. 215 | src := out[start : start+seq.ml] 216 | dst := out[t:] 217 | dst = dst[:len(src)] 218 | t += len(src) 219 | // Destination is the space we just added. 220 | for i := range src { 221 | dst[i] = src[i] 222 | } 223 | } 224 | } 225 | } 226 | // Add final literals 227 | copy(out[t:], s.literals) 228 | if debugDecoder { 229 | t += len(s.literals) 230 | if t != len(out) { 231 | panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize)) 232 | } 233 | } 234 | s.out = out 235 | 236 | return nil 237 | } 238 | -------------------------------------------------------------------------------- /compress/zstd/seqenc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019+ Klaus Post. All rights reserved. 2 | // License information can be found in the LICENSE file. 3 | // Based on work by Yann Collet, released under BSD License. 4 | 5 | package zstd 6 | 7 | import "math/bits" 8 | 9 | type seqCoders struct { 10 | llEnc, ofEnc, mlEnc *fseEncoder 11 | llPrev, ofPrev, mlPrev *fseEncoder 12 | } 13 | 14 | // swap coders with another (block). 15 | func (s *seqCoders) swap(other *seqCoders) { 16 | *s, *other = *other, *s 17 | } 18 | 19 | // setPrev will update the previous encoders to the actually used ones 20 | // and make sure a fresh one is in the main slot. 21 | func (s *seqCoders) setPrev(ll, ml, of *fseEncoder) { 22 | compareSwap := func(used *fseEncoder, current, prev **fseEncoder) { 23 | // We used the new one, more current to history and reuse the previous history 24 | if *current == used { 25 | *prev, *current = *current, *prev 26 | c := *current 27 | p := *prev 28 | c.reUsed = false 29 | p.reUsed = true 30 | return 31 | } 32 | if used == *prev { 33 | return 34 | } 35 | // Ensure we cannot reuse by accident 36 | prevEnc := *prev 37 | prevEnc.symbolLen = 0 38 | } 39 | compareSwap(ll, &s.llEnc, &s.llPrev) 40 | compareSwap(ml, &s.mlEnc, &s.mlPrev) 41 | compareSwap(of, &s.ofEnc, &s.ofPrev) 42 | } 43 | 44 | func highBit(val uint32) (n uint32) { 45 | return uint32(bits.Len32(val) - 1) 46 | } 47 | 48 | var llCodeTable = [64]byte{0, 1, 2, 3, 4, 5, 6, 7, 49 | 8, 9, 10, 11, 12, 13, 14, 15, 50 | 16, 16, 17, 17, 18, 18, 19, 19, 51 | 20, 20, 20, 20, 21, 21, 21, 21, 52 | 22, 22, 22, 22, 22, 22, 22, 22, 53 | 23, 23, 23, 23, 23, 23, 23, 23, 54 | 24, 24, 24, 24, 24, 24, 24, 24, 55 | 24, 24, 24, 24, 24, 24, 24, 24} 56 | 57 | // Up to 6 bits 58 | const maxLLCode = 35 59 | 60 | // llBitsTable translates from ll code to number of bits. 61 | var llBitsTable = [maxLLCode + 1]byte{ 62 | 0, 0, 0, 0, 0, 0, 0, 0, 63 | 0, 0, 0, 0, 0, 0, 0, 0, 64 | 1, 1, 1, 1, 2, 2, 3, 3, 65 | 4, 6, 7, 8, 9, 10, 11, 12, 66 | 13, 14, 15, 16} 67 | 68 | // llCode returns the code that represents the literal length requested. 69 | func llCode(litLength uint32) uint8 { 70 | const llDeltaCode = 19 71 | if litLength <= 63 { 72 | return llCodeTable[litLength&63] 73 | } 74 | return uint8(highBit(litLength)) + llDeltaCode 75 | } 76 | 77 | var mlCodeTable = [128]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 78 | 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 79 | 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 80 | 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 81 | 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 82 | 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 83 | 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 84 | 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42} 85 | 86 | // Up to 6 bits 87 | const maxMLCode = 52 88 | 89 | // mlBitsTable translates from ml code to number of bits. 90 | var mlBitsTable = [maxMLCode + 1]byte{ 91 | 0, 0, 0, 0, 0, 0, 0, 0, 92 | 0, 0, 0, 0, 0, 0, 0, 0, 93 | 0, 0, 0, 0, 0, 0, 0, 0, 94 | 0, 0, 0, 0, 0, 0, 0, 0, 95 | 1, 1, 1, 1, 2, 2, 3, 3, 96 | 4, 4, 5, 7, 8, 9, 10, 11, 97 | 12, 13, 14, 15, 16} 98 | 99 | // note : mlBase = matchLength - MINMATCH; 100 | // because it's the format it's stored in seqStore->sequences 101 | func mlCode(mlBase uint32) uint8 { 102 | const mlDeltaCode = 36 103 | if mlBase <= 127 { 104 | return mlCodeTable[mlBase&127] 105 | } 106 | return uint8(highBit(mlBase)) + mlDeltaCode 107 | } 108 | 109 | func ofCode(offset uint32) uint8 { 110 | // A valid offset will always be > 0. 111 | return uint8(bits.Len32(offset) - 1) 112 | } 113 | -------------------------------------------------------------------------------- /compress/zstd/zip.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019+ Klaus Post. All rights reserved. 2 | // License information can be found in the LICENSE file. 3 | 4 | package zstd 5 | 6 | import ( 7 | "errors" 8 | "io" 9 | "sync" 10 | ) 11 | 12 | // ZipMethodWinZip is the method for Zstandard compressed data inside Zip files for WinZip. 13 | // See https://www.winzip.com/win/en/comp_info.html 14 | const ZipMethodWinZip = 93 15 | 16 | // ZipMethodPKWare is the original method number used by PKWARE to indicate Zstandard compression. 17 | // Deprecated: This has been deprecated by PKWARE, use ZipMethodWinZip instead for compression. 18 | // See https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.9.TXT 19 | const ZipMethodPKWare = 20 20 | 21 | // zipReaderPool is the default reader pool. 22 | var zipReaderPool = sync.Pool{New: func() interface{} { 23 | z, err := NewReader(nil, WithDecoderLowmem(true), WithDecoderMaxWindow(128<<20), WithDecoderConcurrency(1)) 24 | if err != nil { 25 | panic(err) 26 | } 27 | return z 28 | }} 29 | 30 | // newZipReader creates a pooled zip decompressor. 31 | func newZipReader(opts ...DOption) func(r io.Reader) io.ReadCloser { 32 | pool := &zipReaderPool 33 | if len(opts) > 0 { 34 | opts = append([]DOption{WithDecoderLowmem(true), WithDecoderMaxWindow(128 << 20)}, opts...) 35 | // Force concurrency 1 36 | opts = append(opts, WithDecoderConcurrency(1)) 37 | // Create our own pool 38 | pool = &sync.Pool{} 39 | } 40 | return func(r io.Reader) io.ReadCloser { 41 | dec, ok := pool.Get().(*Decoder) 42 | if ok { 43 | dec.Reset(r) 44 | } else { 45 | d, err := NewReader(r, opts...) 46 | if err != nil { 47 | panic(err) 48 | } 49 | dec = d 50 | } 51 | return &pooledZipReader{dec: dec, pool: pool} 52 | } 53 | } 54 | 55 | type pooledZipReader struct { 56 | mu sync.Mutex // guards Close and Read 57 | pool *sync.Pool 58 | dec *Decoder 59 | } 60 | 61 | func (r *pooledZipReader) Read(p []byte) (n int, err error) { 62 | r.mu.Lock() 63 | defer r.mu.Unlock() 64 | if r.dec == nil { 65 | return 0, errors.New("read after close or EOF") 66 | } 67 | dec, err := r.dec.Read(p) 68 | if err == io.EOF { 69 | r.dec.Reset(nil) 70 | r.pool.Put(r.dec) 71 | r.dec = nil 72 | } 73 | return dec, err 74 | } 75 | 76 | func (r *pooledZipReader) Close() error { 77 | r.mu.Lock() 78 | defer r.mu.Unlock() 79 | var err error 80 | if r.dec != nil { 81 | err = r.dec.Reset(nil) 82 | r.pool.Put(r.dec) 83 | r.dec = nil 84 | } 85 | return err 86 | } 87 | 88 | type pooledZipWriter struct { 89 | mu sync.Mutex // guards Close and Read 90 | enc *Encoder 91 | pool *sync.Pool 92 | } 93 | 94 | func (w *pooledZipWriter) Write(p []byte) (n int, err error) { 95 | w.mu.Lock() 96 | defer w.mu.Unlock() 97 | if w.enc == nil { 98 | return 0, errors.New("Write after Close") 99 | } 100 | return w.enc.Write(p) 101 | } 102 | 103 | func (w *pooledZipWriter) Close() error { 104 | w.mu.Lock() 105 | defer w.mu.Unlock() 106 | var err error 107 | if w.enc != nil { 108 | err = w.enc.Close() 109 | w.pool.Put(w.enc) 110 | w.enc = nil 111 | } 112 | return err 113 | } 114 | 115 | // ZipCompressor returns a compressor that can be registered with zip libraries. 116 | // The provided encoder options will be used on all encodes. 117 | func ZipCompressor(opts ...EOption) func(w io.Writer) (io.WriteCloser, error) { 118 | var pool sync.Pool 119 | return func(w io.Writer) (io.WriteCloser, error) { 120 | enc, ok := pool.Get().(*Encoder) 121 | if ok { 122 | enc.Reset(w) 123 | } else { 124 | var err error 125 | enc, err = NewWriter(w, opts...) 126 | if err != nil { 127 | return nil, err 128 | } 129 | } 130 | return &pooledZipWriter{enc: enc, pool: &pool}, nil 131 | } 132 | } 133 | 134 | // ZipDecompressor returns a decompressor that can be registered with zip libraries. 135 | // See ZipCompressor for example. 136 | // Options can be specified. WithDecoderConcurrency(1) is forced, 137 | // and by default a 128MB maximum decompression window is specified. 138 | // The window size can be overridden if required. 139 | func ZipDecompressor(opts ...DOption) func(r io.Reader) io.ReadCloser { 140 | return newZipReader(opts...) 141 | } 142 | -------------------------------------------------------------------------------- /compress/zstd/zstd.go: -------------------------------------------------------------------------------- 1 | // Package zstd provides decompression of zstandard files. 2 | // 3 | // For advanced usage and examples, go to the README: https://github.com/klauspost/compress/tree/master/zstd#zstd 4 | package zstd 5 | 6 | import ( 7 | "bytes" 8 | "errors" 9 | "log" 10 | "math" 11 | 12 | "github.com/klauspost/compress/internal/le" 13 | ) 14 | 15 | // enable debug printing 16 | const debug = false 17 | 18 | // enable encoding debug printing 19 | const debugEncoder = debug 20 | 21 | // enable decoding debug printing 22 | const debugDecoder = debug 23 | 24 | // Enable extra assertions. 25 | const debugAsserts = debug || false 26 | 27 | // print sequence details 28 | const debugSequences = false 29 | 30 | // print detailed matching information 31 | const debugMatches = false 32 | 33 | // force encoder to use predefined tables. 34 | const forcePreDef = false 35 | 36 | // zstdMinMatch is the minimum zstd match length. 37 | const zstdMinMatch = 3 38 | 39 | // fcsUnknown is used for unknown frame content size. 40 | const fcsUnknown = math.MaxUint64 41 | 42 | var ( 43 | // ErrReservedBlockType is returned when a reserved block type is found. 44 | // Typically this indicates wrong or corrupted input. 45 | ErrReservedBlockType = errors.New("invalid input: reserved block type encountered") 46 | 47 | // ErrCompressedSizeTooBig is returned when a block is bigger than allowed. 48 | // Typically this indicates wrong or corrupted input. 49 | ErrCompressedSizeTooBig = errors.New("invalid input: compressed size too big") 50 | 51 | // ErrBlockTooSmall is returned when a block is too small to be decoded. 52 | // Typically returned on invalid input. 53 | ErrBlockTooSmall = errors.New("block too small") 54 | 55 | // ErrUnexpectedBlockSize is returned when a block has unexpected size. 56 | // Typically returned on invalid input. 57 | ErrUnexpectedBlockSize = errors.New("unexpected block size") 58 | 59 | // ErrMagicMismatch is returned when a "magic" number isn't what is expected. 60 | // Typically this indicates wrong or corrupted input. 61 | ErrMagicMismatch = errors.New("invalid input: magic number mismatch") 62 | 63 | // ErrWindowSizeExceeded is returned when a reference exceeds the valid window size. 64 | // Typically this indicates wrong or corrupted input. 65 | ErrWindowSizeExceeded = errors.New("window size exceeded") 66 | 67 | // ErrWindowSizeTooSmall is returned when no window size is specified. 68 | // Typically this indicates wrong or corrupted input. 69 | ErrWindowSizeTooSmall = errors.New("invalid input: window size was too small") 70 | 71 | // ErrDecoderSizeExceeded is returned if decompressed size exceeds the configured limit. 72 | ErrDecoderSizeExceeded = errors.New("decompressed size exceeds configured limit") 73 | 74 | // ErrUnknownDictionary is returned if the dictionary ID is unknown. 75 | ErrUnknownDictionary = errors.New("unknown dictionary") 76 | 77 | // ErrFrameSizeExceeded is returned if the stated frame size is exceeded. 78 | // This is only returned if SingleSegment is specified on the frame. 79 | ErrFrameSizeExceeded = errors.New("frame size exceeded") 80 | 81 | // ErrFrameSizeMismatch is returned if the stated frame size does not match the expected size. 82 | // This is only returned if SingleSegment is specified on the frame. 83 | ErrFrameSizeMismatch = errors.New("frame size does not match size on stream") 84 | 85 | // ErrCRCMismatch is returned if CRC mismatches. 86 | ErrCRCMismatch = errors.New("CRC check failed") 87 | 88 | // ErrDecoderClosed will be returned if the Decoder was used after 89 | // Close has been called. 90 | ErrDecoderClosed = errors.New("decoder used after Close") 91 | 92 | // ErrEncoderClosed will be returned if the Encoder was used after 93 | // Close has been called. 94 | ErrEncoderClosed = errors.New("encoder used after Close") 95 | 96 | // ErrDecoderNilInput is returned when a nil Reader was provided 97 | // and an operation other than Reset/DecodeAll/Close was attempted. 98 | ErrDecoderNilInput = errors.New("nil input provided as reader") 99 | ) 100 | 101 | func println(a ...interface{}) { 102 | if debug || debugDecoder || debugEncoder { 103 | log.Println(a...) 104 | } 105 | } 106 | 107 | func printf(format string, a ...interface{}) { 108 | if debug || debugDecoder || debugEncoder { 109 | log.Printf(format, a...) 110 | } 111 | } 112 | 113 | func load3232(b []byte, i int32) uint32 { 114 | return le.Load32(b, i) 115 | } 116 | 117 | func load6432(b []byte, i int32) uint64 { 118 | return le.Load64(b, i) 119 | } 120 | 121 | type byter interface { 122 | Bytes() []byte 123 | Len() int 124 | } 125 | 126 | var _ byter = &bytes.Buffer{} 127 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module run 2 | 3 | go 1.23.5 4 | 5 | require github.com/klauspost/compress v1.18.0 6 | 7 | replace github.com/klauspost/compress => ./compress 8 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "archive/tar" 5 | "embed" 6 | "fmt" 7 | "io" 8 | "os" 9 | "os/exec" 10 | "path/filepath" 11 | "strconv" 12 | "strings" 13 | "bytes" 14 | "syscall" 15 | "github.com/klauspost/compress/zstd" 16 | ) 17 | 18 | //go:embed res.tar.zst 19 | var embeddedRes embed.FS 20 | 21 | //go:embed available_commands.txt 22 | var availableCommands []byte 23 | 24 | var ( 25 | executableDir string 26 | executableName string 27 | androidLinker string 28 | ) 29 | 30 | func main() { 31 | cmd := cmdBuilder(os.Args) 32 | if cmd == nil { 33 | cleanup(1) 34 | } 35 | 36 | cmd.Stdout = os.Stdout 37 | cmd.Stderr = os.Stderr 38 | cmd.Stdin = os.Stdin 39 | 40 | err := cmd.Run() 41 | cleanup(getExitCode(err)) 42 | } 43 | 44 | func init() { 45 | getExecutableNameAndDir() 46 | checkAndroidLinker() 47 | getTmpDir() 48 | os.Setenv("LD_LIBRARY_PATH", filepath.Join(os.TempDir(), "bin", "lib")) 49 | 50 | if !extractBin() { 51 | fmt.Fprintln(os.Stderr, "Error during extraction.") 52 | cleanup(1) 53 | } 54 | } 55 | 56 | func getExitCode(err error) int { 57 | if exitErr, ok := err.(*exec.ExitError); ok { 58 | if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { 59 | if status.Signaled() { 60 | return 128 + int(status.Signal()) 61 | } 62 | return status.ExitStatus() 63 | } 64 | } 65 | return 0 66 | } 67 | 68 | func cleanup(exitCode int) { 69 | os.RemoveAll(os.TempDir()) 70 | os.Exit(exitCode) 71 | } 72 | 73 | func checkAndroidLinker() { 74 | cmd := exec.Command("getprop", "ro.build.version.sdk") 75 | output, err := cmd.Output() 76 | if err != nil { 77 | fmt.Fprintln(os.Stderr, "Error getting Android API level:", err) 78 | os.Exit(1) 79 | } 80 | 81 | apiLevel, err := strconv.Atoi(strings.TrimSpace(string(output))) 82 | if err != nil { 83 | fmt.Fprintln(os.Stderr, "Error converting API level:", err) 84 | os.Exit(1) 85 | } 86 | 87 | if apiLevel > 28 { 88 | switch 32 << (^uint(0) >> 63) { 89 | case 64: 90 | androidLinker = "/system/bin/linker64" 91 | case 32: 92 | androidLinker = "/system/bin/linker" 93 | } 94 | } 95 | } 96 | 97 | func getExecutableNameAndDir() { 98 | exePath, err := os.Executable() 99 | if err != nil { 100 | fmt.Fprintln(os.Stderr, "Error:", err) 101 | os.Exit(1) 102 | } 103 | absPath, _ := filepath.EvalSymlinks(exePath) 104 | 105 | linkerPaths := []string{ 106 | "/apex/com.android.runtime/bin/linker64", 107 | "/system/bin/linker64", 108 | "/apex/com.android.runtime/bin/linker", 109 | "/system/bin/linker", 110 | } 111 | 112 | for _, path := range linkerPaths { 113 | if absPath == path { 114 | executableName = filepath.Base(os.Args[1]) 115 | executableDir = filepath.Dir(os.Args[1]) 116 | return 117 | } 118 | } 119 | executableName = filepath.Base(exePath) 120 | executableDir = filepath.Dir(exePath) 121 | } 122 | 123 | func getTmpDir() { 124 | tmpDir := filepath.Join(os.TempDir(), ".tmp_run_dir_"+strconv.Itoa(os.Getpid())) 125 | os.RemoveAll(tmpDir) 126 | if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil { 127 | tmpDir = filepath.Join(executableDir, ".tmp_run_dir_"+strconv.Itoa(os.Getpid())) 128 | if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil { 129 | fmt.Fprintln(os.Stderr, "Temporary directory not available. Specify it using TMPDIR environment variable") 130 | os.Exit(1) 131 | } 132 | } 133 | os.Setenv("TMPDIR", tmpDir) 134 | } 135 | 136 | func extractBin() bool { 137 | extractionPath := os.TempDir() 138 | 139 | data, err := embeddedRes.ReadFile("res.tar.zst") 140 | if err != nil { 141 | fmt.Fprintln(os.Stderr, "Failed to read embedded resource:", err) 142 | return false 143 | } 144 | 145 | if err := extractTarZst(data, extractionPath); err != nil { 146 | fmt.Fprintln(os.Stderr, "Extraction failed:", err) 147 | return false 148 | } 149 | 150 | return true 151 | } 152 | 153 | func extractTarZst(data []byte, destDir string) error { 154 | zstReader, err := zstd.NewReader(bytes.NewReader(data)) 155 | if err != nil { 156 | return err 157 | } 158 | defer zstReader.Close() 159 | 160 | tarReader := tar.NewReader(zstReader) 161 | 162 | for { 163 | header, err := tarReader.Next() 164 | if err == io.EOF { 165 | return nil 166 | } else if err != nil { 167 | return err 168 | } 169 | 170 | targetPath := filepath.Join(destDir, header.Name) 171 | 172 | switch header.Typeflag { 173 | case tar.TypeDir: 174 | err = os.MkdirAll(targetPath, os.ModePerm) 175 | case tar.TypeReg: 176 | err = func() error { 177 | outFile, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(header.Mode)) 178 | if err != nil { 179 | return err 180 | } 181 | defer outFile.Close() 182 | _, err = io.Copy(outFile, tarReader) 183 | return err 184 | }() 185 | } 186 | 187 | if err != nil { 188 | return err 189 | } 190 | } 191 | } 192 | 193 | func cmdBuilder(args []string) *exec.Cmd { 194 | absPath, _ := filepath.EvalSymlinks(args[0]) 195 | 196 | args = args[1:] 197 | 198 | linkerPaths := []string{ 199 | "/apex/com.android.runtime/bin/linker64", 200 | "/system/bin/linker64", 201 | "/apex/com.android.runtime/bin/linker", 202 | "/system/bin/linker", 203 | } 204 | 205 | for _, path := range linkerPaths { 206 | if absPath == path { 207 | args = args[1:] 208 | } 209 | } 210 | 211 | if len(args) == 0 { 212 | fmt.Fprintln(os.Stderr, "Available commands: "+string(availableCommands)) 213 | return nil 214 | } 215 | 216 | binDir := filepath.Join(os.TempDir(), "bin") 217 | 218 | if androidLinker == "" { 219 | if len(args) > 1 { 220 | return exec.Command(filepath.Join(binDir, args[0]), args[1:]...) 221 | } else { 222 | return exec.Command(filepath.Join(binDir, args[0])) 223 | } 224 | } else { 225 | if len(args) > 1 { 226 | return exec.Command(androidLinker, append([]string{filepath.Join(binDir, args[0])}, args[1:]...)...) 227 | } else { 228 | return exec.Command(androidLinker, []string{filepath.Join(binDir, args[0])}...) 229 | } 230 | } 231 | } 232 | --------------------------------------------------------------------------------