diff options
| author | 2026-02-20 19:06:13 +0800 | |
|---|---|---|
| committer | 2026-02-20 19:07:14 +0800 | |
| commit | aa513c069c1418734aea894dc944e27c6a78a3bb (patch) | |
| tree | 687f0a11bb550fa088fd82a98ceb8979bbc35f69 /internal | |
| parent | Comment on prior reverts removing the pack writing API (diff) | |
| signature | No signature | |
Delete everything, I'm redesigning this.
I'll stop using a flat package and make things much more modular.
And also experiment with streaming APIs so large blobs don't OOM us.
Diffstat (limited to 'internal')
31 files changed, 0 insertions, 3207 deletions
diff --git a/internal/adler32/LICENSE b/internal/adler32/LICENSE deleted file mode 100644 index 5cec357a..00000000 --- a/internal/adler32/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Copyright (c) 2024, Michal Hruby -Copyright (c) 2017 The Chromium Authors. All rights reserved. -Copyright (c) 1995-2024 Mark Adler -Copyright (c) 1995-2024 Jean-loup Gailly -Copyright (c) 2022 Adam Stylinski - -BSD 2-Clause License - - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/internal/adler32/LICENSE.ZLIB b/internal/adler32/LICENSE.ZLIB deleted file mode 100644 index c75c1568..00000000 --- a/internal/adler32/LICENSE.ZLIB +++ /dev/null @@ -1,17 +0,0 @@ -Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. diff --git a/internal/adler32/README b/internal/adler32/README deleted file mode 100644 index b80acd00..00000000 --- a/internal/adler32/README +++ /dev/null @@ -1 +0,0 @@ -This package was mostly copied from github.com/mhr3/adler32-simd. diff --git a/internal/adler32/adler32_amd64.go b/internal/adler32/adler32_amd64.go deleted file mode 100644 index 88a854ed..00000000 --- a/internal/adler32/adler32_amd64.go +++ /dev/null @@ -1,93 +0,0 @@ -//go:build amd64 && !purego - -package adler32 - -import ( - "encoding/binary" - "errors" - "hash" - "hash/adler32" - - "golang.org/x/sys/cpu" -) - -// The size of an Adler-32 checksum in bytes. -const Size = 4 - -var ( - hasSSE3 = cpu.X86.HasSSE3 - hasAVX2 = cpu.X86.HasAVX2 -) - -// digest represents the partial evaluation of a checksum. -// The low 16 bits are s1, the high 16 bits are s2. -type digest uint32 - -func (d *digest) Reset() { *d = 1 } - -// New returns a new hash.Hash32 computing the Adler-32 checksum. -func New() hash.Hash32 { - if !hasSSE3 { - return adler32.New() - } - d := new(digest) - d.Reset() - return d -} - -func (d *digest) MarshalBinary() ([]byte, error) { - b := make([]byte, 0, marshaledSize) - b = append(b, magic...) - b = binary.BigEndian.AppendUint32(b, uint32(*d)) - return b, nil -} - -func (d *digest) UnmarshalBinary(b []byte) error { - if len(b) < len(magic) || string(b[:len(magic)]) != magic { - return errors.New("hash/adler32: invalid hash state identifier") - } - if len(b) != marshaledSize { - return errors.New("hash/adler32: invalid hash state size") - } - *d = digest(binary.BigEndian.Uint32(b[len(magic):])) - return nil -} - -func (d *digest) Size() int { return Size } - -func (d *digest) BlockSize() int { return 4 } - -func (d *digest) Write(data []byte) (nn int, err error) { - if len(data) >= 64 { - var h uint32 - if hasAVX2 { - h = adler32_avx2(uint32(*d), data) - } else { - h = adler32_sse3(uint32(*d), data) - } - *d = digest(h) - } else { - h := update(uint32(*d), data) - *d = digest(h) - } - return len(data), nil -} - -func (d *digest) Sum32() uint32 { return uint32(*d) } - -func (d *digest) Sum(in []byte) []byte { - s := uint32(*d) - return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s)) -} - -// Checksum returns the Adler-32 checksum of data. -func Checksum(data []byte) uint32 { - if !hasSSE3 || len(data) < 64 { - return update(1, data) - } - - if hasAVX2 { - return adler32_avx2(1, data) - } - return adler32_sse3(1, data) -} diff --git a/internal/adler32/adler32_arm64.go b/internal/adler32/adler32_arm64.go deleted file mode 100644 index ddf9cb5e..00000000 --- a/internal/adler32/adler32_arm64.go +++ /dev/null @@ -1,73 +0,0 @@ -//go:build arm64 && !purego - -package adler32 - -import ( - "encoding/binary" - "errors" - "hash" -) - -// The size of an Adler-32 checksum in bytes. -const Size = 4 - -// digest represents the partial evaluation of a checksum. -// The low 16 bits are s1, the high 16 bits are s2. -type digest uint32 - -func (d *digest) Reset() { *d = 1 } - -// New returns a new hash.Hash32 computing the Adler-32 checksum. -func New() hash.Hash32 { - d := new(digest) - d.Reset() - return d -} - -func (d *digest) MarshalBinary() ([]byte, error) { - b := make([]byte, 0, marshaledSize) - b = append(b, magic...) - b = binary.BigEndian.AppendUint32(b, uint32(*d)) - return b, nil -} - -func (d *digest) UnmarshalBinary(b []byte) error { - if len(b) < len(magic) || string(b[:len(magic)]) != magic { - return errors.New("hash/adler32: invalid hash state identifier") - } - if len(b) != marshaledSize { - return errors.New("hash/adler32: invalid hash state size") - } - *d = digest(binary.BigEndian.Uint32(b[len(magic):])) - return nil -} - -func (d *digest) Size() int { return Size } - -func (d *digest) BlockSize() int { return 4 } - -func (d *digest) Write(data []byte) (nn int, err error) { - if len(data) >= 64 { - h := adler32_neon(uint32(*d), data) - *d = digest(h) - } else { - h := update(uint32(*d), data) - *d = digest(h) - } - return len(data), nil -} - -func (d *digest) Sum32() uint32 { return uint32(*d) } - -func (d *digest) Sum(in []byte) []byte { - s := uint32(*d) - return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s)) -} - -// Checksum returns the Adler-32 checksum of data. -func Checksum(data []byte) uint32 { - if len(data) >= 64 { - return adler32_neon(1, data) - } - return update(1, data) -} diff --git a/internal/adler32/adler32_avx2.go b/internal/adler32/adler32_avx2.go deleted file mode 100644 index 042812b8..00000000 --- a/internal/adler32/adler32_avx2.go +++ /dev/null @@ -1,6 +0,0 @@ -//go:build !purego && amd64 - -package adler32 - -//go:noescape -func adler32_avx2(in uint32, buf []byte) uint32 diff --git a/internal/adler32/adler32_avx2.s b/internal/adler32/adler32_avx2.s deleted file mode 100644 index 1b9a1c50..00000000 --- a/internal/adler32/adler32_avx2.s +++ /dev/null @@ -1,263 +0,0 @@ -//go:build !purego && amd64 - -#include "textflag.h" - -DATA weights_1_32<>+0x00(SB)/8, $0x191a1b1c1d1e1f20 -DATA weights_1_32<>+0x08(SB)/8, $0x1112131415161718 -DATA weights_1_32<>+0x10(SB)/8, $0x090a0b0c0d0e0f10 -DATA weights_1_32<>+0x18(SB)/8, $0x0102030405060708 -GLOBL weights_1_32<>(SB), (RODATA|NOPTR), $32 - -DATA ones_u16<>+0x00(SB)/8, $0x0001000100010001 -DATA ones_u16<>+0x08(SB)/8, $0x0001000100010001 -DATA ones_u16<>+0x10(SB)/8, $0x0001000100010001 -DATA ones_u16<>+0x18(SB)/8, $0x0001000100010001 -GLOBL ones_u16<>(SB), (RODATA|NOPTR), $32 - -DATA one_u16<>+0x00(SB)/2, $0x0001 -GLOBL one_u16<>(SB), (RODATA|NOPTR), $2 - -TEXT ·adler32_avx2(SB), NOSPLIT, $0-36 - MOVLQZX in+0(FP), DI - MOVQ buf_base+8(FP), SI - MOVQ buf_len+16(FP), DX - MOVQ buf_cap+24(FP), CX - WORD $0x8548; BYTE $0xf6 - JE return_one - WORD $0xf889 - WORD $0x8548; BYTE $0xd2 - JE return_result - NOP - NOP - NOP - WORD $0xc189 - WORD $0xe9c1; BYTE $0x10 - WORD $0xb70f; BYTE $0xc0 - CMPQ DX, $0x20 - JB tail16_check - LONG $0x078071bf; BYTE $0x80 - LONG $0xc0eff9c5 - VMOVDQA weights_1_32<>(SB), Y1 - VPBROADCASTW one_u16<>(SB), Y2 - JMP block_loop_setup - -block_accum_init: - LONG $0xf46ffdc5 - LONG $0xedefd1c5 - -block_reduce: - SUBQ AX, DX - LONG $0xf572ddc5; BYTE $0x05 - LONG $0xdbfeddc5 - LONG $0x397de3c4; WORD $0x01f4 - LONG $0xecc6c8c5; BYTE $0x88 - LONG $0xe470f9c5; BYTE $0x88 - LONG $0xe4fed1c5 - LONG $0xec70f9c5; BYTE $0x55 - LONG $0xe4fed1c5 - LONG $0xe07ef9c5 - MOVQ AX, CX - IMULQ DI, CX - SHRQ $0x2f, CX - LONG $0xfff1c969; WORD $0x0000 - WORD $0xc829 - LONG $0x397de3c4; WORD $0x01dc - LONG $0xdbfed9c5 - LONG $0xe370f9c5; BYTE $0xee - LONG $0xdcfee1c5 - LONG $0xe370f9c5; BYTE $0x55 - LONG $0xdbfed9c5 - LONG $0xd97ef9c5 - MOVQ CX, R8 - IMULQ DI, R8 - SHRQ $0x2f, R8 - LONG $0xf1c06945; WORD $0x00ff; BYTE $0x00 - WORD $0x2944; BYTE $0xc1 - CMPQ DX, $0x1f - JBE tail_check - -block_loop_setup: - LONG $0xe06ef9c5 - LONG $0xd96ef9c5 - CMPQ DX, $0x15b0 - LONG $0x15b0b841; WORD $0x0000 - LONG $0xc2420f4c - WORD $0x8944; BYTE $0xc0 - LONG $0x001fe025; BYTE $0x00 - JE block_accum_init - ADDQ $-0x20, R8 - LONG $0xedefd1c5 - LONG $0x20c0f641 - JNE block_loop_entry - LONG $0x2e6ffec5 - ADDQ $0x20, SI - LEAQ -0x20(AX), CX - LONG $0xf0f6d5c5 - LONG $0xf4fecdc5 - LONG $0x0455e2c4; BYTE $0xe9 - LONG $0xeaf5d5c5 - LONG $0xdbfed5c5 - LONG $0xec6ffdc5 - LONG $0xe66ffdc5 - CMPQ R8, $0x20 - JAE block_loop_64 - JMP block_reduce - -block_loop_entry: - MOVQ AX, CX - CMPQ R8, $0x20 - JB block_reduce - -block_loop_64: - LONG $0x366ffec5 - LONG $0x7e6ffec5; BYTE $0x20 - LONG $0xc0f64dc5 - LONG $0xc4fe3dc5 - LONG $0xecfed5c5 - LONG $0x044de2c4; BYTE $0xe1 - LONG $0xe2f5ddc5 - LONG $0xdbfeddc5 - ADDQ $0x40, SI - LONG $0xe0f6c5c5 - LONG $0xe4febdc5 - LONG $0xedfebdc5 - LONG $0x0445e2c4; BYTE $0xf1 - LONG $0xf2f5cdc5 - LONG $0xdbfecdc5 - ADDQ $-0x40, CX - JNE block_loop_64 - LONG $0xf46ffdc5 - JMP block_reduce - -return_one: - LONG $0x000001b8; BYTE $0x00 - -return_result: - MOVL AX, ret+32(FP) - RET - -tail_check: - WORD $0x8548; BYTE $0xd2 - JE return_no_tail - -tail16_check: - CMPQ DX, $0x10 - JB tail_bytes_setup - WORD $0xb60f; BYTE $0x3e - WORD $0xf801 - WORD $0xc101 - LONG $0x017eb60f - WORD $0xc701 - WORD $0xf901 - LONG $0x0246b60f - WORD $0xf801 - WORD $0xc101 - LONG $0x037eb60f - WORD $0xc701 - WORD $0xf901 - LONG $0x0446b60f - WORD $0xf801 - WORD $0xc101 - LONG $0x057eb60f - WORD $0xc701 - WORD $0xf901 - LONG $0x0646b60f - WORD $0xf801 - WORD $0xc101 - LONG $0x077eb60f - WORD $0xc701 - WORD $0xf901 - LONG $0x0846b60f - WORD $0xf801 - WORD $0xc101 - LONG $0x097eb60f - WORD $0xc701 - WORD $0xf901 - LONG $0x0a46b60f - WORD $0xf801 - WORD $0xc101 - LONG $0x0b7eb60f - WORD $0xc701 - WORD $0xf901 - LONG $0x0c46b60f - WORD $0xf801 - WORD $0xc101 - LONG $0x0d7eb60f - WORD $0xc701 - WORD $0xf901 - LONG $0x46b60f44; BYTE $0x0e - WORD $0x0141; BYTE $0xf8 - WORD $0x0144; BYTE $0xc1 - LONG $0x0f46b60f - WORD $0x0144; BYTE $0xc0 - WORD $0xc101 - ADDQ $-0x10, DX - JE final_reduce - ADDQ $0x10, SI - -tail_bytes_setup: - LEAQ -0x1(DX), DI - MOVQ DX, R9 - ANDQ $0x3, R9 - JE tail_dword_setup - XORL R8, R8 - -tail_byte_loop: - LONG $0x14b60f46; BYTE $0x06 - WORD $0x0144; BYTE $0xd0 - WORD $0xc101 - INCQ R8 - CMPQ R9, R8 - JNE tail_byte_loop - ADDQ R8, SI - SUBQ R8, DX - -tail_dword_setup: - CMPQ DI, $0x3 - JB final_reduce - XORL DI, DI - -tail_dword_loop: - LONG $0x04b60f44; BYTE $0x3e - WORD $0x0141; BYTE $0xc0 - WORD $0x0144; BYTE $0xc1 - LONG $0x3e44b60f; BYTE $0x01 - WORD $0x0144; BYTE $0xc0 - WORD $0xc101 - LONG $0x44b60f44; WORD $0x023e - WORD $0x0141; BYTE $0xc0 - WORD $0x0144; BYTE $0xc1 - LONG $0x3e44b60f; BYTE $0x03 - WORD $0x0144; BYTE $0xc0 - WORD $0xc101 - ADDQ $0x4, DI - CMPQ DX, DI - JNE tail_dword_loop - -final_reduce: - LONG $0x000f908d; WORD $0xffff - CMPL AX, $0xfff1 - WORD $0x420f; BYTE $0xd0 - WORD $0xc889 - LONG $0x078071be; BYTE $0x80 - IMULQ AX, SI - SHRQ $0x2f, SI - LONG $0xfff1c669; WORD $0x0000 - WORD $0xc129 - WORD $0xe1c1; BYTE $0x10 - WORD $0xd109 - WORD $0xc889 - NOP - NOP - VZEROUPPER - MOVL AX, ret+32(FP) - RET - -return_no_tail: - WORD $0xe1c1; BYTE $0x10 - WORD $0xc809 - NOP - NOP - VZEROUPPER - MOVL AX, ret+32(FP) - RET diff --git a/internal/adler32/adler32_fallback.go b/internal/adler32/adler32_fallback.go deleted file mode 100644 index c213c3c1..00000000 --- a/internal/adler32/adler32_fallback.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build (!arm64 && !amd64) || purego - -package adler32 - -import ( - "hash" - "hash/adler32" -) - -// The size of an Adler-32 checksum in bytes. -const Size = 4 - -// New returns a new hash.Hash32 computing the Adler-32 checksum. -func New() hash.Hash32 { - return adler32.New() -} - -// Checksum returns the Adler-32 checksum of data. -func Checksum(data []byte) uint32 { return adler32.Checksum(data) } diff --git a/internal/adler32/adler32_generic.go b/internal/adler32/adler32_generic.go deleted file mode 100644 index f33e0f9b..00000000 --- a/internal/adler32/adler32_generic.go +++ /dev/null @@ -1,45 +0,0 @@ -// Package adler32 implements the Adler-32 checksum. -package adler32 - -const ( - // mod is the largest prime that is less than 65536. - mod = 65521 - // nmax is the largest n such that - // 255 * n * (n+1) / 2 + (n+1) * (mod-1) <= 2^32-1. - // It is mentioned in RFC 1950 (search for "5552"). - nmax = 5552 - - // binary representation compatible with standard library. - magic = "adl\x01" - marshaledSize = len(magic) + 4 -) - -// Add p to the running checksum d. -func update(d uint32, p []byte) uint32 { - s1, s2 := d&0xffff, d>>16 - for len(p) > 0 { - var q []byte - if len(p) > nmax { - p, q = p[:nmax], p[nmax:] - } - for len(p) >= 4 { - s1 += uint32(p[0]) - s2 += s1 - s1 += uint32(p[1]) - s2 += s1 - s1 += uint32(p[2]) - s2 += s1 - s1 += uint32(p[3]) - s2 += s1 - p = p[4:] - } - for _, x := range p { - s1 += uint32(x) - s2 += s1 - } - s1 %= mod - s2 %= mod - p = q - } - return s2<<16 | s1 -} diff --git a/internal/adler32/adler32_neon.go b/internal/adler32/adler32_neon.go deleted file mode 100644 index 521b71e0..00000000 --- a/internal/adler32/adler32_neon.go +++ /dev/null @@ -1,6 +0,0 @@ -//go:build !purego && arm64 - -package adler32 - -//go:noescape -func adler32_neon(in uint32, buf []byte) uint32 diff --git a/internal/adler32/adler32_neon.s b/internal/adler32/adler32_neon.s deleted file mode 100644 index 08b170bd..00000000 --- a/internal/adler32/adler32_neon.s +++ /dev/null @@ -1,208 +0,0 @@ -//go:build !purego && arm64 - -#include "textflag.h" - -DATA mult_table<>+0x00(SB)/8, $0x001d001e001f0020 -DATA mult_table<>+0x08(SB)/8, $0x0019001a001b001c -DATA mult_table<>+0x10(SB)/8, $0x0015001600170018 -DATA mult_table<>+0x18(SB)/8, $0x0011001200130014 -DATA mult_table<>+0x20(SB)/8, $0x000d000e000f0010 -DATA mult_table<>+0x28(SB)/8, $0x0009000a000b000c -DATA mult_table<>+0x30(SB)/8, $0x0005000600070008 -DATA mult_table<>+0x38(SB)/8, $0x0001000200030004 -GLOBL mult_table<>(SB), (RODATA|NOPTR), $64 - -TEXT ·adler32_neon(SB), NOSPLIT, $0-36 - MOVW in+0(FP), R0 - MOVD buf_base+8(FP), R1 - MOVD buf_len+16(FP), R2 - MOVD buf_cap+24(FP), R3 - NOP - ANDS $15, R1, R10 - ANDW $65535, R0, R8 - LSRW $16, R0, R9 - NOP - BEQ vector_loop_setup - ADD $1, R1, R11 - MOVD R1, R12 - -align_loop: - WORD $0x3840158d - SUB $1, R2, R2 - TST $15, R11 - ADD $1, R11, R11 - ADDW R13, R8, R8 - ADDW R9, R8, R9 - BNE align_loop - MOVW $32881, R11 - MOVW $65521, R13 - MOVKW $(32775<<16), R11 - MOVW $4294901775, R12 - MOVW $65520, R14 - SUB R10, R1, R10 - UMULL R11, R9, R11 - ADDW R12, R8, R12 - CMPW R14, R8 - ADD $16, R10, R1 - LSR $47, R11, R11 - CSELW HI, R12, R8, R8 - MSUBW R13, R9, R11, R9 - -vector_loop_setup: - AND $31, R2, R10 - CMP $32, R2 - BCC tail_entry - MOVD $mult_table<>(SB), R11 - ADD $0, R11, R11 - MOVW $32881, R14 - MOVW $173, R12 - MOVD $137438953440, R13 - MOVKW $(32775<<16), R14 - VLD1 (R11), [V0.H8, V1.H8, V2.H8, V3.H8] - LSR $5, R2, R11 - MOVW $65521, R15 - VEXT $8, V0.B16, V0.B16, V4.B16 - VEXT $8, V1.B16, V1.B16, V5.B16 - VEXT $8, V2.B16, V2.B16, V6.B16 - VEXT $8, V3.B16, V3.B16, V7.B16 - -vector_outer_loop: - CMP $173, R11 - MOVD R1, R2 - CSEL LO, R11, R12, R16 - WORD $0x6f00e414 - MULW R16, R8, R0 - ADD R16<<5, R13, R17 - WORD $0x6f00e410 - AND $137438953440, R17, R17 - WORD $0x6f00e412 - WORD $0x6f00e413 - WORD $0x6f00e415 - VMOV R0, V20.S[3] - MOVW R16, R0 - WORD $0x6f00e411 - -vector_inner_loop: - WORD $0xacc15857 - SUBSW $1, R0, R0 - VADD V17.S4, V20.S4, V20.S4 - WORD $0x2e3712b5 - WORD $0x6e371273 - WORD $0x6e202ad8 - WORD $0x2e361252 - WORD $0x6e361210 - WORD $0x6e206af8 - WORD $0x6e606b11 - BNE vector_inner_loop - VSHL $5, V20.S4, V20.S4 - ADD R17, R1, R17 - SUBS R16, R11, R11 - ADD $32, R17, R1 - WORD $0x2e6082b4 - VEXT $8, V21.B16, V21.B16, V21.B16 - WORD $0x2e6482b4 - VEXT $8, V19.B16, V19.B16, V21.B16 - WORD $0x2e618274 - VEXT $8, V18.B16, V18.B16, V19.B16 - WORD $0x2e6582b4 - WORD $0x2e628254 - WORD $0x2e668274 - WORD $0x2e638214 - VEXT $8, V16.B16, V16.B16, V16.B16 - WORD $0x2e678214 - WORD $0x4eb1be30 - WORD $0x4eb4be91 - WORD $0x0eb1be10 - VMOV V16.S[1], R0 - FMOVS F16, R2 - ADDW R8, R2, R8 - ADDW R9, R0, R9 - UMULL R14, R8, R0 - UMULL R14, R9, R2 - LSR $47, R0, R0 - LSR $47, R2, R2 - MSUBW R15, R8, R0, R8 - MSUBW R15, R9, R2, R9 - BNE vector_outer_loop - -tail_entry: - CBZ R10, return_result - CMP $16, R10 - BCC tail_byte_loop - WORD $0x3940002b - SUBS $16, R10, R10 - WORD $0x3940042c - WORD $0x3940082d - ADDW R11, R8, R8 - WORD $0x39400c2b - ADDW R9, R8, R9 - ADDW R12, R8, R8 - WORD $0x3940102c - ADDW R8, R9, R9 - ADDW R13, R8, R8 - WORD $0x3940142d - ADDW R8, R9, R9 - ADDW R11, R8, R8 - WORD $0x3940182b - ADDW R8, R9, R9 - ADDW R12, R8, R8 - WORD $0x39401c2c - ADDW R8, R9, R9 - ADDW R13, R8, R8 - ADDW R8, R9, R9 - ADDW R11, R8, R8 - WORD $0x3940202b - ADDW R8, R9, R9 - ADDW R12, R8, R8 - WORD $0x3940242c - ADDW R8, R9, R9 - WORD $0x3940382d - ADDW R11, R8, R8 - WORD $0x3940282b - ADDW R8, R9, R9 - ADDW R12, R8, R8 - WORD $0x39402c2c - ADDW R8, R9, R9 - ADDW R11, R8, R8 - WORD $0x3940302b - ADDW R8, R9, R9 - ADDW R12, R8, R8 - WORD $0x3940342c - ADDW R8, R9, R9 - ADDW R11, R8, R8 - WORD $0x39403c2b - ADDW R8, R9, R9 - ADDW R12, R8, R8 - ADDW R8, R9, R9 - ADDW R13, R8, R8 - ADDW R8, R9, R9 - ADDW R11, R8, R8 - ADDW R8, R9, R9 - BEQ final_reduce - ADD $16, R1, R1 - -tail_byte_loop: - WORD $0x3840142b - SUBS $1, R10, R10 - ADDW R11, R8, R8 - ADDW R9, R8, R9 - BNE tail_byte_loop - -final_reduce: - MOVW $32881, R10 - MOVW $65521, R12 - MOVKW $(32775<<16), R10 - MOVW $4294901775, R11 - MOVW $65520, R13 - ADDW R11, R8, R11 - UMULL R10, R9, R10 - CMPW R13, R8 - CSELW HI, R11, R8, R8 - LSR $47, R10, R10 - MSUBW R12, R9, R10, R9 - -return_result: - ORRW R9<<16, R8, R0 - NOP - MOVW R0, ret+32(FP) - RET diff --git a/internal/adler32/adler32_sse3.go b/internal/adler32/adler32_sse3.go deleted file mode 100644 index 8e8c8a9b..00000000 --- a/internal/adler32/adler32_sse3.go +++ /dev/null @@ -1,6 +0,0 @@ -//go:build !purego && amd64 - -package adler32 - -//go:noescape -func adler32_sse3(in uint32, buf []byte) uint32 diff --git a/internal/adler32/adler32_sse3.s b/internal/adler32/adler32_sse3.s deleted file mode 100644 index 5880bab8..00000000 --- a/internal/adler32/adler32_sse3.s +++ /dev/null @@ -1,214 +0,0 @@ -//go:build !purego && amd64 - -#include "textflag.h" - -DATA weights_17_32<>+0x00(SB)/8, $0x191a1b1c1d1e1f20 -DATA weights_17_32<>+0x08(SB)/8, $0x1112131415161718 -GLOBL weights_17_32<>(SB), (RODATA|NOPTR), $16 - -DATA ones_u16<>+0x00(SB)/8, $0x0001000100010001 -DATA ones_u16<>+0x08(SB)/8, $0x0001000100010001 -GLOBL ones_u16<>(SB), (RODATA|NOPTR), $16 - -DATA weights_1_16<>+0x00(SB)/8, $0x090a0b0c0d0e0f10 -DATA weights_1_16<>+0x08(SB)/8, $0x0102030405060708 -GLOBL weights_1_16<>(SB), (RODATA|NOPTR), $16 - -TEXT ·adler32_sse3(SB), NOSPLIT, $0-36 - MOVLQZX in+0(FP), DI - MOVQ buf_base+8(FP), SI - MOVQ buf_len+16(FP), DX - MOVQ buf_cap+24(FP), CX - NOP - NOP - NOP - WORD $0xf889 - LONG $0xc8b70f44 - WORD $0xe8c1; BYTE $0x10 - WORD $0xd189 - WORD $0xe183; BYTE $0x1f - CMPQ DX, $0x20 - JAE block_loop_setup - WORD $0x8944; BYTE $0xcf - JMP tail_entry - -block_loop_setup: - SHRQ $0x5, DX - LONG $0xc0ef0f66 - MOVO weights_17_32<>(SB), X1 - MOVO ones_u16<>(SB), X2 - MOVO weights_1_16<>(SB), X3 - LONG $0x8071b841; WORD $0x8007 - -block_outer_loop: - CMPQ DX, $0xad - LONG $0x00adba41; WORD $0x0000 - LONG $0xd2420f4c - WORD $0x8944; BYTE $0xcf - LONG $0xfaaf0f41 - LONG $0xef6e0f66 - LONG $0xe06e0f66 - WORD $0x8944; BYTE $0xd0 - LONG $0xf6ef0f66 - -block_inner_loop: - LONG $0x3e6f0ff3 - LONG $0x6f0f4466; BYTE $0xc7 - LONG $0x04380f66; BYTE $0xf9 - LONG $0xfaf50f66 - LONG $0xfcfe0f66 - LONG $0x666f0ff3; BYTE $0x10 - LONG $0xeefe0f66 - LONG $0xf60f4466; BYTE $0xc0 - LONG $0xfe0f4466; BYTE $0xc6 - LONG $0xf46f0f66 - LONG $0xf0f60f66 - LONG $0xfe0f4166; BYTE $0xf0 - LONG $0x04380f66; BYTE $0xe3 - LONG $0xe2f50f66 - LONG $0xe7fe0f66 - ADDQ $0x20, SI - WORD $0xc8ff - JNE block_inner_loop - LONG $0xf5720f66; BYTE $0x05 - LONG $0xe5fe0f66 - LONG $0xee700f66; BYTE $0xb1 - LONG $0xeefe0f66 - LONG $0xf5700f66; BYTE $0xee - LONG $0xf5fe0f66 - LONG $0xf77e0f66 - WORD $0x0144; BYTE $0xcf - LONG $0xec700f66; BYTE $0xb1 - LONG $0xecfe0f66 - LONG $0xe5700f66; BYTE $0xee - LONG $0xe5fe0f66 - LONG $0xe07e0f66 - MOVQ DI, R9 - IMULQ R8, R9 - SHRQ $0x2f, R9 - LONG $0xf1c96945; WORD $0x00ff; BYTE $0x00 - WORD $0x2944; BYTE $0xcf - MOVQ AX, R9 - IMULQ R8, R9 - SHRQ $0x2f, R9 - LONG $0xf1c96945; WORD $0x00ff; BYTE $0x00 - WORD $0x2944; BYTE $0xc8 - WORD $0x8941; BYTE $0xf9 - SUBQ R10, DX - JNE block_outer_loop - -tail_entry: - WORD $0x8548; BYTE $0xc9 - JE return_result - CMPL CX, $0x10 - JB tail_bytes_setup - WORD $0xb60f; BYTE $0x16 - WORD $0xd701 - WORD $0xf801 - LONG $0x0156b60f - WORD $0xfa01 - WORD $0xd001 - LONG $0x027eb60f - WORD $0xd701 - WORD $0xf801 - LONG $0x0356b60f - WORD $0xfa01 - WORD $0xd001 - LONG $0x047eb60f - WORD $0xd701 - WORD $0xf801 - LONG $0x0556b60f - WORD $0xfa01 - WORD $0xd001 - LONG $0x067eb60f - WORD $0xd701 - WORD $0xf801 - LONG $0x0756b60f - WORD $0xfa01 - WORD $0xd001 - LONG $0x087eb60f - WORD $0xd701 - WORD $0xf801 - LONG $0x0956b60f - WORD $0xfa01 - WORD $0xd001 - LONG $0x0a7eb60f - WORD $0xd701 - WORD $0xf801 - LONG $0x0b56b60f - WORD $0xfa01 - WORD $0xd001 - LONG $0x0c7eb60f - WORD $0xd701 - WORD $0xf801 - LONG $0x0d56b60f - WORD $0xfa01 - WORD $0xd001 - LONG $0x46b60f44; BYTE $0x0e - WORD $0x0141; BYTE $0xd0 - WORD $0x0144; BYTE $0xc0 - LONG $0x0f7eb60f - WORD $0x0144; BYTE $0xc7 - WORD $0xf801 - ADDQ $-0x10, CX - JE final_reduce - ADDQ $0x10, SI - -tail_bytes_setup: - LEAQ -0x1(CX), DX - MOVQ CX, R9 - ANDQ $0x3, R9 - JE tail_dword_setup - XORL R8, R8 - -tail_byte_loop: - LONG $0x14b60f46; BYTE $0x06 - WORD $0x0144; BYTE $0xd7 - WORD $0xf801 - INCQ R8 - CMPQ R9, R8 - JNE tail_byte_loop - ADDQ R8, SI - SUBQ R8, CX - -tail_dword_setup: - CMPQ DX, $0x3 - JB final_reduce - XORL DX, DX - -tail_dword_loop: - LONG $0x04b60f44; BYTE $0x16 - WORD $0x0141; BYTE $0xf8 - WORD $0x0144; BYTE $0xc0 - LONG $0x167cb60f; BYTE $0x01 - WORD $0x0144; BYTE $0xc7 - WORD $0xf801 - LONG $0x44b60f44; WORD $0x0216 - WORD $0x0141; BYTE $0xf8 - WORD $0x0144; BYTE $0xc0 - LONG $0x167cb60f; BYTE $0x03 - WORD $0x0144; BYTE $0xc7 - WORD $0xf801 - ADDQ $0x4, DX - CMPQ CX, DX - JNE tail_dword_loop - -final_reduce: - LONG $0x000f8f8d; WORD $0xffff - CMPL DI, $0xfff1 - WORD $0x420f; BYTE $0xcf - WORD $0xc289 - LONG $0x078071be; BYTE $0x80 - IMULQ DX, SI - SHRQ $0x2f, SI - LONG $0xfff1d669; WORD $0x0000 - WORD $0xd029 - WORD $0xcf89 - -return_result: - WORD $0xe0c1; BYTE $0x10 - WORD $0xf809 - NOP - NOP - MOVL AX, ret+32(FP) - RET diff --git a/internal/adler32/bench_test.go b/internal/adler32/bench_test.go deleted file mode 100644 index 7744b903..00000000 --- a/internal/adler32/bench_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package adler32 - -import ( - "testing" -) - -const benchmarkSize = 64 * 1024 - -var data = make([]byte, benchmarkSize) - -func init() { - for i := range benchmarkSize { - data[i] = byte(i % 256) - } -} - -func BenchmarkChecksum(b *testing.B) { - b.ReportAllocs() - for range b.N { - Checksum(data) - } -} diff --git a/internal/bloom/bloom.go b/internal/bloom/bloom.go deleted file mode 100644 index c1a116d6..00000000 --- a/internal/bloom/bloom.go +++ /dev/null @@ -1,236 +0,0 @@ -// Package bloom provides a bloom filter implementation used for changed-path -// filters in Git commit graphs. -package bloom - -import "encoding/binary" - -const ( - // DataHeaderSize is the size of the BDAT header in commit-graph files. - DataHeaderSize = 3 * 4 - // DefaultMaxChange matches Git's default max-changed-paths behavior. - DefaultMaxChange = 512 -) - -// Settings describe the changed-paths Bloom filter parameters stored in -// commit-graph BDAT chunks. -// -// Obviously, they must match the repository's commit-graph settings to -// interpret filters correctly. -type Settings struct { - HashVersion uint32 - NumHashes uint32 - BitsPerEntry uint32 - MaxChangePaths uint32 -} - -// Filter represents a changed-paths Bloom filter associated with a commit. -// -// The filter encodes which paths changed between a commit and its first -// parent. Paths are expected to be in Git's slash-separated form and -// are queried using a path and its prefixes (e.g. "a/b/c", "a/b", "a"). -type Filter struct { - Data []byte - Version uint32 -} - -// ParseSettings reads Bloom filter settings from a BDAT chunk header. -func ParseSettings(bdat []byte) (*Settings, error) { - if len(bdat) < DataHeaderSize { - return nil, ErrInvalid - } - settings := &Settings{ - HashVersion: binary.BigEndian.Uint32(bdat[0:4]), - NumHashes: binary.BigEndian.Uint32(bdat[4:8]), - BitsPerEntry: binary.BigEndian.Uint32(bdat[8:12]), - MaxChangePaths: DefaultMaxChange, - } - return settings, nil -} - -// MightContain reports whether the Bloom filter may contain the given path. -// -// Evaluated against the full path and each of its directory prefixes. A true -// result indicates a possible match; false means the path definitely did not -// change. -func (f *Filter) MightContain(path []byte, settings *Settings) bool { - if f == nil || settings == nil { - return false - } - if len(f.Data) == 0 { - return false - } - keys := keyvec(path, settings) - for i := range keys { - if filterContainsKey(f, &keys[i], settings) { - return true - } - } - return false -} - -type key struct { - hashes []uint32 -} - -func keyvec(path []byte, settings *Settings) []key { - if len(path) == 0 { - return nil - } - count := 1 - for _, b := range path { - if b == '/' { - count++ - } - } - keys := make([]key, 0, count) - keys = append(keys, keyFill(path, settings)) - for i := len(path) - 1; i >= 0; i-- { - if path[i] == '/' { - keys = append(keys, keyFill(path[:i], settings)) - } - } - return keys -} - -func keyFill(path []byte, settings *Settings) key { - const seed0 = 0x293ae76f - const seed1 = 0x7e646e2c - var h0, h1 uint32 - if settings.HashVersion == 2 { - h0 = murmur3SeededV2(seed0, path) - h1 = murmur3SeededV2(seed1, path) - } else { - h0 = murmur3SeededV1(seed0, path) - h1 = murmur3SeededV1(seed1, path) - } - hashes := make([]uint32, settings.NumHashes) - for i := uint32(0); i < settings.NumHashes; i++ { - hashes[i] = h0 + i*h1 - } - return key{hashes: hashes} -} - -func filterContainsKey(filter *Filter, key *key, settings *Settings) bool { - if filter == nil || key == nil || settings == nil { - return false - } - if len(filter.Data) == 0 { - return false - } - mod := uint64(len(filter.Data)) * 8 - for _, h := range key.hashes { - idx := uint64(h) % mod - bytePos := idx / 8 - bit := byte(1 << (idx & 7)) - if filter.Data[bytePos]&bit == 0 { - return false - } - } - return true -} - -func murmur3SeededV2(seed uint32, data []byte) uint32 { - const ( - c1 = 0xcc9e2d51 - c2 = 0x1b873593 - r1 = 15 - r2 = 13 - m = 5 - n = 0xe6546b64 - ) - h := seed - nblocks := len(data) / 4 - for i := 0; i < nblocks; i++ { - k := uint32(data[4*i]) | - (uint32(data[4*i+1]) << 8) | - (uint32(data[4*i+2]) << 16) | - (uint32(data[4*i+3]) << 24) - k *= c1 - k = (k << r1) | (k >> (32 - r1)) - k *= c2 - - h ^= k - h = (h << r2) | (h >> (32 - r2)) - h = h*m + n - } - - var k1 uint32 - tail := data[nblocks*4:] - switch len(tail) & 3 { - case 3: - k1 ^= uint32(tail[2]) << 16 - fallthrough - case 2: - k1 ^= uint32(tail[1]) << 8 - fallthrough - case 1: - k1 ^= uint32(tail[0]) - k1 *= c1 - k1 = (k1 << r1) | (k1 >> (32 - r1)) - k1 *= c2 - h ^= k1 - } - - h ^= uint32(len(data)) - h ^= h >> 16 - h *= 0x85ebca6b - h ^= h >> 13 - h *= 0xc2b2ae35 - h ^= h >> 16 - return h -} - -func murmur3SeededV1(seed uint32, data []byte) uint32 { - const ( - c1 = 0xcc9e2d51 - c2 = 0x1b873593 - r1 = 15 - r2 = 13 - m = 5 - n = 0xe6546b64 - ) - h := seed - nblocks := len(data) / 4 - for i := 0; i < nblocks; i++ { - b0 := int8(data[4*i]) - b1 := int8(data[4*i+1]) - b2 := int8(data[4*i+2]) - b3 := int8(data[4*i+3]) - k := uint32(b0) | - (uint32(b1) << 8) | - (uint32(b2) << 16) | - (uint32(b3) << 24) - k *= c1 - k = (k << r1) | (k >> (32 - r1)) - k *= c2 - - h ^= k - h = (h << r2) | (h >> (32 - r2)) - h = h*m + n - } - - var k1 uint32 - tail := data[nblocks*4:] - switch len(tail) & 3 { - case 3: - k1 ^= uint32(int8(tail[2])) << 16 - fallthrough - case 2: - k1 ^= uint32(int8(tail[1])) << 8 - fallthrough - case 1: - k1 ^= uint32(int8(tail[0])) - k1 *= c1 - k1 = (k1 << r1) | (k1 >> (32 - r1)) - k1 *= c2 - h ^= k1 - } - - h ^= uint32(len(data)) - h ^= h >> 16 - h *= 0x85ebca6b - h ^= h >> 13 - h *= 0xc2b2ae35 - h ^= h >> 16 - return h -} diff --git a/internal/bloom/errors.go b/internal/bloom/errors.go deleted file mode 100644 index fe38d1bc..00000000 --- a/internal/bloom/errors.go +++ /dev/null @@ -1,5 +0,0 @@ -package bloom - -import "errors" - -var ErrInvalid = errors.New("bloom: invalid data") diff --git a/internal/bufpool/buffers.go b/internal/bufpool/buffers.go deleted file mode 100644 index 439e7e04..00000000 --- a/internal/bufpool/buffers.go +++ /dev/null @@ -1,189 +0,0 @@ -// Package bufpool provides a lightweight byte-buffer type with optional -// pooling. -package bufpool - -import "sync" - -const ( - // DefaultBufferCap is the minimum capacity a borrowed buffer will have. - // Borrow() will allocate or retrieve a buffer with at least this capacity. - DefaultBufferCap = 32 * 1024 - - // maxPooledBuffer defines the maximum capacity of a buffer that may be - // returned to the pool. Buffers larger than this will not be pooled to - // avoid unbounded memory usage. - maxPooledBuffer = 8 << 20 -) - -// Buffer is a growable byte container that optionally participates in a -// memory pool. A Buffer may be obtained through Borrow() or constructed -// directly from owned data via FromOwned(). -// -// A Buffer's underlying slice may grow as needed. When finished with a -// pooled buffer, the caller should invoke Release() to return it to the pool. -// -// Buffers must not be copied after first use; doing so can cause double-returns -// to the pool and data races. -// -//go:nocopy -type Buffer struct { - _ struct{} // for nocopy - buf []byte - pool poolIndex -} - -type poolIndex int8 - -const ( - unpooled poolIndex = -1 -) - -var sizeClasses = [...]int{ - DefaultBufferCap, - 64 << 10, - 128 << 10, - 256 << 10, - 512 << 10, - 1 << 20, - 2 << 20, - 4 << 20, - maxPooledBuffer, -} - -var bufferPools = func() []sync.Pool { - pools := make([]sync.Pool, len(sizeClasses)) - for i, classCap := range sizeClasses { - capCopy := classCap - pools[i].New = func() any { - buf := make([]byte, 0, capCopy) - return &buf - } - } - return pools -}() - -// Borrow retrieves a Buffer suitable for storing up to capHint bytes. -// The returned Buffer may come from an internal sync.Pool. -// -// If capHint is smaller than DefaultBufferCap, it is automatically raised -// to DefaultBufferCap. If no pooled buffer has sufficient capacity, a new -// unpooled buffer is allocated. -// -// The caller must call Release() when finished using the returned Buffer. -func Borrow(capHint int) Buffer { - if capHint < DefaultBufferCap { - capHint = DefaultBufferCap - } - classIdx, classCap, pooled := classFor(capHint) - if !pooled { - newBuf := make([]byte, 0, capHint) - return Buffer{buf: newBuf, pool: unpooled} - } - buf := bufferPools[classIdx].Get().(*[]byte) - if cap(*buf) < classCap { - *buf = make([]byte, 0, classCap) - } - slice := (*buf)[:0] - return Buffer{buf: slice, pool: poolIndex(classIdx)} -} - -// FromOwned constructs a Buffer from a caller-owned byte slice. The resulting -// Buffer does not participate in pooling and will never be returned to the -// internal pool when released. -func FromOwned(buf []byte) Buffer { - return Buffer{buf: buf, pool: unpooled} -} - -// Resize adjusts the length of the buffer to n bytes. If n exceeds the current -// capacity, the underlying storage is grown. If n is negative, it is treated -// as zero. -// -// The buffer's new contents beyond the previous length are undefined. -func (buf *Buffer) Resize(n int) { - if n < 0 { - n = 0 - } - buf.ensureCapacity(n) - buf.buf = buf.buf[:n] -} - -// Append copies the provided bytes onto the end of the buffer, growing its -// capacity if required. If src is empty, the method does nothing. -// -// The receiver retains ownership of the data; the caller may reuse src freely. -func (buf *Buffer) Append(src []byte) { - if len(src) == 0 { - return - } - start := len(buf.buf) - buf.ensureCapacity(start + len(src)) - buf.buf = buf.buf[:start+len(src)] - copy(buf.buf[start:], src) -} - -// Bytes returns the underlying byte slice that represents the current contents -// of the buffer. Modifying the returned slice modifies the Buffer itself. -func (buf *Buffer) Bytes() []byte { - return buf.buf -} - -// Release returns the buffer to the global pool if it originated from the -// pool and its capacity is no larger than maxPooledBuffer. After release, the -// Buffer becomes invalid and should not be used further. -// -// Releasing a non-pooled buffer has no effect beyond clearing its internal -// storage. -func (buf *Buffer) Release() { - if buf.buf == nil { - return - } - buf.returnToPool() - buf.buf = nil - buf.pool = unpooled -} - -// ensureCapacity grows the underlying buffer to accommodate the requested -// number of bytes. Growth doubles the capacity by default unless a larger -// expansion is needed. If the previous storage was pooled and not oversized, -// it is returned to the pool. -func (buf *Buffer) ensureCapacity(needed int) { - if cap(buf.buf) >= needed { - return - } - classIdx, classCap, pooled := classFor(needed) - var newBuf []byte - if pooled { - raw := bufferPools[classIdx].Get().(*[]byte) - if cap(*raw) < classCap { - *raw = make([]byte, 0, classCap) - } - newBuf = (*raw)[:len(buf.buf)] - } else { - newBuf = make([]byte, len(buf.buf), classCap) - } - copy(newBuf, buf.buf) - buf.returnToPool() - buf.buf = newBuf - if pooled { - buf.pool = poolIndex(classIdx) - } else { - buf.pool = unpooled - } -} - -func classFor(size int) (idx int, classCap int, ok bool) { - for i, class := range sizeClasses { - if size <= class { - return i, class, true - } - } - return -1, size, false -} - -func (buf *Buffer) returnToPool() { - if buf.pool == unpooled { - return - } - tmp := buf.buf[:0] - bufferPools[int(buf.pool)].Put(&tmp) -} diff --git a/internal/bufpool/buffers_test.go b/internal/bufpool/buffers_test.go deleted file mode 100644 index f5c006da..00000000 --- a/internal/bufpool/buffers_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package bufpool - -import "testing" - -func TestBorrowBufferResizeAndAppend(t *testing.T) { - b := Borrow(1) - defer b.Release() - - if cap(b.buf) < DefaultBufferCap { - t.Fatalf("expected capacity >= %d, got %d", DefaultBufferCap, cap(b.buf)) - } - - b.Append([]byte("alpha")) - b.Append([]byte("beta")) - if got := string(b.Bytes()); got != "alphabeta" { - t.Fatalf("unexpected contents: %q", got) - } - - b.Resize(3) - if got := string(b.Bytes()); got != "alp" { - t.Fatalf("resize shrink mismatch: %q", got) - } - - b.Resize(8) - if len(b.Bytes()) != 8 { - t.Fatalf("expected len 8 after grow, got %d", len(b.Bytes())) - } - if prefix := string(b.Bytes()[:3]); prefix != "alp" { - t.Fatalf("prefix lost after grow: %q", prefix) - } -} - -func TestBorrowBufferRelease(t *testing.T) { - b := Borrow(DefaultBufferCap / 2) - b.Append([]byte("data")) - b.Release() - if b.buf != nil { - t.Fatal("expected buffer cleared after release") - } -} - -func TestBorrowUsesLargerPools(t *testing.T) { - const request = DefaultBufferCap * 4 - - classIdx, classCap, pooled := classFor(request) - if !pooled { - t.Fatalf("expected %d to map to a pooled class", request) - } - - b := Borrow(request) - if b.pool != poolIndex(classIdx) { - t.Fatalf("expected pooled buffer in class %d, got %d", classIdx, b.pool) - } - if cap(b.buf) != classCap { - t.Fatalf("expected capacity %d, got %d", classCap, cap(b.buf)) - } - b.Release() - - b2 := Borrow(request) - defer b2.Release() - if b2.pool != poolIndex(classIdx) { - t.Fatalf("expected pooled buffer in class %d on reuse, got %d", classIdx, b2.pool) - } - if cap(b2.buf) != classCap { - t.Fatalf("expected capacity %d on reuse, got %d", classCap, cap(b2.buf)) - } -} - -func TestGrowingBufferStaysPooled(t *testing.T) { - b := Borrow(DefaultBufferCap) - defer b.Release() - - b.Append(make([]byte, DefaultBufferCap*3)) - if b.pool == unpooled { - t.Fatal("buffer should stay pooled after growth within limit") - } -} diff --git a/internal/flatex/LICENSE b/internal/flatex/LICENSE deleted file mode 100644 index 2a7cf70d..00000000 --- a/internal/flatex/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2009 The Go Authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google LLC nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/internal/flatex/decompress.go b/internal/flatex/decompress.go deleted file mode 100644 index 065e23f3..00000000 --- a/internal/flatex/decompress.go +++ /dev/null @@ -1,38 +0,0 @@ -package flatex - -import ( - "io" - - "codeberg.org/lindenii/furgit/internal/bufpool" -) - -func DecompressSized(src []byte, sizeHint int) (bufpool.Buffer, int, error) { - d := sliceInflaterPool.Get().(*sliceInflater) - defer sliceInflaterPool.Put(d) - - if err := d.reset(src); err != nil { - return bufpool.Buffer{}, 0, err - } - - out := bufpool.Borrow(sizeHint) - out.Resize(0) - - for { - if len(d.toRead) > 0 { - out.Append(d.toRead) - d.toRead = nil - continue - } - if d.err != nil { - if d.err == io.EOF { - return out, d.pos, nil - } - out.Release() - return bufpool.Buffer{}, 0, d.err - } - d.step(d) - if d.err != nil && len(d.toRead) == 0 { - d.toRead = d.window.readFlush() - } - } -} diff --git a/internal/flatex/decompress_test.go b/internal/flatex/decompress_test.go deleted file mode 100644 index e53a6581..00000000 --- a/internal/flatex/decompress_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package flatex - -import ( - "bytes" - stdflate "compress/flate" - "testing" -) - -func compressDeflate(t *testing.T, payload []byte) []byte { - t.Helper() - var buf bytes.Buffer - w, err := stdflate.NewWriter(&buf, stdflate.DefaultCompression) - if err != nil { - t.Fatalf("NewWriter: %v", err) - } - if _, err := w.Write(payload); err != nil { - t.Fatalf("Write: %v", err) - } - if err := w.Close(); err != nil { - t.Fatalf("Close: %v", err) - } - return buf.Bytes() -} - -func TestDecompressSized(t *testing.T) { - payload := bytes.Repeat([]byte("golang"), 32) - compressed := compressDeflate(t, payload) - - out, _, err := DecompressSized(compressed, 0) - if err != nil { - t.Fatalf("DecompressSized: %v", err) - } - defer out.Release() - - if !bytes.Equal(out.Bytes(), payload) { - t.Fatalf("unexpected payload: got %q", out.Bytes()) - } -} - -func TestDecompressSizedUsesHint(t *testing.T) { - payload := []byte("short") - compressed := compressDeflate(t, payload) - - const hint = 1 << 19 - out, _, err := DecompressSized(compressed, hint) - if err != nil { - t.Fatalf("DecompressSized: %v", err) - } - defer out.Release() - - if !bytes.Equal(out.Bytes(), payload) { - t.Fatalf("unexpected payload: got %q", out.Bytes()) - } - if cap(out.Bytes()) < hint { - t.Fatalf("expected capacity >= %d, got %d", hint, cap(out.Bytes())) - } -} diff --git a/internal/flatex/huffman.go b/internal/flatex/huffman.go deleted file mode 100644 index 32172dbb..00000000 --- a/internal/flatex/huffman.go +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package flatex implements the DEFLATE compressed data format, described in -// RFC 1951. The [compress/gzip] and [compress/zlib] packages implement access -// to DEFLATE-based file formats. -package flatex - -import ( - "math/bits" - "strconv" - "sync" -) - -const ( - // The special code used to mark the end of a block. - endBlockMarker = 256 - maxCodeLen = 16 // max length of Huffman code - maxMatchOffset = 1 << 15 // The largest match offset - // The next three numbers come from the RFC section 3.2.7, with the - // additional proviso in section 3.2.5 which implies that distance codes - // 30 and 31 should never occur in compressed data. - maxNumLit = 286 - maxNumDist = 30 - numCodes = 19 // number of codes in Huffman meta-code -) - -// A CorruptInputError reports the presence of corrupt input at a given offset. -type CorruptInputError int64 - -func (e CorruptInputError) Error() string { - return "flate: corrupt input before offset " + strconv.FormatInt(int64(e), 10) -} - -// The data structure for decoding Huffman tables is based on that of -// zlib. There is a lookup table of a fixed bit width (huffmanChunkBits), -// For codes smaller than the table width, there are multiple entries -// (each combination of trailing bits has the same value). For codes -// larger than the table width, the table contains a link to an overflow -// table. The width of each entry in the link table is the maximum code -// size minus the chunk width. -// -// Note that you can do a lookup in the table even without all bits -// filled. Since the extra bits are zero, and the DEFLATE Huffman codes -// have the property that shorter codes come before longer ones, the -// bit length estimate in the result is a lower bound on the actual -// number of bits. -// -// See the following: -// https://github.com/madler/zlib/raw/master/doc/algorithm.txt - -// chunk & 15 is number of bits -// chunk >> 4 is value, including table link - -const ( - huffmanChunkBits = 9 - huffmanNumChunks = 1 << huffmanChunkBits - huffmanCountMask = 15 - huffmanValueShift = 4 -) - -type huffmanDecoder struct { - min int // the minimum code length - chunks [huffmanNumChunks]uint32 // chunks as described above - links [][]uint32 // overflow links - linkMask uint32 // mask the width of the link table -} - -// Initialize Huffman decoding tables from array of code lengths. -// Following this function, h is guaranteed to be initialized into a complete -// tree (i.e., neither over-subscribed nor under-subscribed). The exception is a -// degenerate case where the tree has only a single symbol with length 1. Empty -// trees are permitted. -func (h *huffmanDecoder) init(lengths []int) bool { - // Sanity enables additional runtime tests during Huffman - // table construction. It's intended to be used during - // development to supplement the currently ad-hoc unit tests. - const sanity = false - - if h.min != 0 { - *h = huffmanDecoder{} - } - - // Count number of codes of each length, - // compute min and max length. - var count [maxCodeLen]int - var min, max int - for _, n := range lengths { - if n == 0 { - continue - } - if min == 0 || n < min { - min = n - } - if n > max { - max = n - } - count[n]++ - } - - // Empty tree. The decompressor.huffSym function will fail later if the tree - // is used. Technically, an empty tree is only valid for the HDIST tree and - // not the HCLEN and HLIT tree. However, a stream with an empty HCLEN tree - // is guaranteed to fail since it will attempt to use the tree to decode the - // codes for the HLIT and HDIST trees. Similarly, an empty HLIT tree is - // guaranteed to fail later since the compressed data section must be - // composed of at least one symbol (the end-of-block marker). - if max == 0 { - return true - } - - code := 0 - var nextcode [maxCodeLen]int - for i := min; i <= max; i++ { - code <<= 1 - nextcode[i] = code - code += count[i] - } - - // Check that the coding is complete (i.e., that we've - // assigned all 2-to-the-max possible bit sequences). - // Exception: To be compatible with zlib, we also need to - // accept degenerate single-code codings. See also - // TestDegenerateHuffmanCoding. - if code != 1<<uint(max) && (code != 1 || max != 1) { - return false - } - - h.min = min - if max > huffmanChunkBits { - numLinks := 1 << (uint(max) - huffmanChunkBits) - h.linkMask = uint32(numLinks - 1) - - // create link tables - link := nextcode[huffmanChunkBits+1] >> 1 - h.links = make([][]uint32, huffmanNumChunks-link) - for j := uint(link); j < huffmanNumChunks; j++ { - reverse := int(bits.Reverse16(uint16(j))) - reverse >>= uint(16 - huffmanChunkBits) - off := j - uint(link) - if sanity && h.chunks[reverse] != 0 { - panic("impossible: overwriting existing chunk") - } - h.chunks[reverse] = uint32(off<<huffmanValueShift | (huffmanChunkBits + 1)) - h.links[off] = make([]uint32, numLinks) - } - } - - for i, n := range lengths { - if n == 0 { - continue - } - code := nextcode[n] - nextcode[n]++ - chunk := uint32(i<<huffmanValueShift | n) - reverse := int(bits.Reverse16(uint16(code))) - reverse >>= uint(16 - n) - if n <= huffmanChunkBits { - for off := reverse; off < len(h.chunks); off += 1 << uint(n) { - // We should never need to overwrite - // an existing chunk. Also, 0 is - // never a valid chunk, because the - // lower 4 "count" bits should be - // between 1 and 15. - if sanity && h.chunks[off] != 0 { - panic("impossible: overwriting existing chunk") - } - h.chunks[off] = chunk - } - } else { - j := reverse & (huffmanNumChunks - 1) - if sanity && h.chunks[j]&huffmanCountMask != huffmanChunkBits+1 { - // Longer codes should have been - // associated with a link table above. - panic("impossible: not an indirect chunk") - } - value := h.chunks[j] >> huffmanValueShift - linktab := h.links[value] - reverse >>= huffmanChunkBits - for off := reverse; off < len(linktab); off += 1 << uint(n-huffmanChunkBits) { - if sanity && linktab[off] != 0 { - panic("impossible: overwriting existing chunk") - } - linktab[off] = chunk - } - } - } - - if sanity { - // Above we've sanity checked that we never overwrote - // an existing entry. Here we additionally check that - // we filled the tables completely. - for i, chunk := range h.chunks { - if chunk == 0 { - // As an exception, in the degenerate - // single-code case, we allow odd - // chunks to be missing. - if code == 1 && i%2 == 1 { - continue - } - panic("impossible: missing chunk") - } - } - for _, linktab := range h.links { - for _, chunk := range linktab { - if chunk == 0 { - panic("impossible: missing chunk") - } - } - } - } - - return true -} - -// RFC 1951 section 3.2.7. -// Compression with dynamic Huffman codes -var codeOrder = [...]int{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} - -var ( - // Initialize the fixedHuffmanDecoder only once upon first use. - fixedOnce sync.Once - fixedHuffmanDecoder huffmanDecoder -) - -func fixedHuffmanDecoderInit() { - fixedOnce.Do(func() { - // These come from the RFC section 3.2.6. - var bits [288]int - for i := 0; i < 144; i++ { - bits[i] = 8 - } - for i := 144; i < 256; i++ { - bits[i] = 9 - } - for i := 256; i < 280; i++ { - bits[i] = 7 - } - for i := 280; i < 288; i++ { - bits[i] = 8 - } - fixedHuffmanDecoder.init(bits[:]) - }) -} diff --git a/internal/flatex/slice_inflate.go b/internal/flatex/slice_inflate.go deleted file mode 100644 index f9120143..00000000 --- a/internal/flatex/slice_inflate.go +++ /dev/null @@ -1,472 +0,0 @@ -package flatex - -import ( - "io" - "math/bits" - "sync" -) - -// sliceInflater is a specialized DEFLATE decoder that reads directly from an -// in-memory byte slice. It mirrors the main decompressor but avoids the -// overhead of the Reader interfaces, enabling faster byte-slice decoding. -type sliceInflater struct { - input []byte - pos int - roffset int64 - - b uint32 - nb uint - - h1, h2 huffmanDecoder - - bits *[maxNumLit + maxNumDist]int - codebits *[numCodes]int - - window windowDecoder - - toRead []byte - step func(*sliceInflater) - stepState int - final bool - err error - hl, hd *huffmanDecoder - copyLen int - copyDist int -} - -var sliceInflaterPool = sync.Pool{ - New: func() any { - fixedHuffmanDecoderInit() - return &sliceInflater{ - bits: new([maxNumLit + maxNumDist]int), - codebits: new([numCodes]int), - } - }, -} - -func (f *sliceInflater) reset(src []byte) error { - bits := f.bits - codebits := f.codebits - windowState := f.window - *f = sliceInflater{ - input: src, - bits: bits, - codebits: codebits, - window: windowState, - step: (*sliceInflater).nextBlock, - } - f.window.init(maxMatchOffset) - return nil -} - -func (f *sliceInflater) nextBlock() { - for f.nb < 1+2 { - if err := f.moreBits(); err != nil { - f.err = err - return - } - } - f.final = f.b&1 == 1 - f.b >>= 1 - typ := f.b & 3 - f.b >>= 2 - f.nb -= 1 + 2 - switch typ { - case 0: - f.dataBlock() - case 1: - f.hl = &fixedHuffmanDecoder - f.hd = nil - f.huffmanBlock() - case 2: - if err := f.readHuffman(); err != nil { - f.err = err - return - } - f.hl = &f.h1 - f.hd = &f.h2 - f.huffmanBlock() - default: - f.err = CorruptInputError(f.roffset) - } -} - -func (f *sliceInflater) huffmanBlock() { - const ( - stateInit = iota - stateDict - ) - switch f.stepState { - case stateInit: - goto readLiteral - case stateDict: - goto copyHistory - } - -readLiteral: - { - v, err := f.huffSym(f.hl) - if err != nil { - f.err = err - return - } - var n uint - var length int - switch { - case v < 256: - f.window.writeByte(byte(v)) - if f.window.availWrite() == 0 { - f.toRead = f.window.readFlush() - f.step = (*sliceInflater).huffmanBlock - f.stepState = stateInit - return - } - goto readLiteral - case v == 256: - f.finishBlock() - return - case v < 265: - length = v - (257 - 3) - n = 0 - case v < 269: - length = v*2 - (265*2 - 11) - n = 1 - case v < 273: - length = v*4 - (269*4 - 19) - n = 2 - case v < 277: - length = v*8 - (273*8 - 35) - n = 3 - case v < 281: - length = v*16 - (277*16 - 67) - n = 4 - case v < 285: - length = v*32 - (281*32 - 131) - n = 5 - case v < maxNumLit: - length = 258 - n = 0 - default: - f.err = CorruptInputError(f.roffset) - return - } - if n > 0 { - for f.nb < n { - if err = f.moreBits(); err != nil { - f.err = err - return - } - } - length += int(f.b & uint32(1<<n-1)) - f.b >>= n - f.nb -= n - } - - var dist int - if f.hd == nil { - for f.nb < 5 { - if err = f.moreBits(); err != nil { - f.err = err - return - } - } - dist = int(bits.Reverse8(uint8(f.b & 0x1F << 3))) - f.b >>= 5 - f.nb -= 5 - } else { - if dist, err = f.huffSym(f.hd); err != nil { - f.err = err - return - } - } - - switch { - case dist < 4: - dist++ - case dist < maxNumDist: - nb := uint(dist-2) >> 1 - extra := (dist & 1) << nb - for f.nb < nb { - if err = f.moreBits(); err != nil { - f.err = err - return - } - } - extra |= int(f.b & uint32(1<<nb-1)) - f.b >>= nb - f.nb -= nb - dist = 1<<(nb+1) + 1 + extra - default: - f.err = CorruptInputError(f.roffset) - return - } - - if dist > f.window.histSize() { - f.err = CorruptInputError(f.roffset) - return - } - - f.copyLen, f.copyDist = length, dist - goto copyHistory - } - -copyHistory: - { - cnt := f.window.tryWriteCopy(f.copyDist, f.copyLen) - if cnt == 0 { - cnt = f.window.writeCopy(f.copyDist, f.copyLen) - } - f.copyLen -= cnt - - if f.window.availWrite() == 0 || f.copyLen > 0 { - f.toRead = f.window.readFlush() - f.step = (*sliceInflater).huffmanBlock - f.stepState = stateDict - return - } - goto readLiteral - } -} - -func (f *sliceInflater) dataBlock() { - f.nb = 0 - f.b = 0 - - if f.pos+4 > len(f.input) { - f.pos = len(f.input) - f.err = io.ErrUnexpectedEOF - return - } - hdr := f.input[f.pos : f.pos+4] - f.pos += 4 - f.roffset += 4 - n := int(hdr[0]) | int(hdr[1])<<8 - nn := int(hdr[2]) | int(hdr[3])<<8 - if uint16(nn) != uint16(^n) { - f.err = CorruptInputError(f.roffset) - return - } - - if n == 0 { - f.toRead = f.window.readFlush() - f.finishBlock() - return - } - - f.copyLen = n - f.copyData() -} - -func (f *sliceInflater) copyData() { - for { - if f.copyLen == 0 { - f.finishBlock() - return - } - buf := f.window.writeSlice() - if len(buf) == 0 { - f.toRead = f.window.readFlush() - f.step = (*sliceInflater).copyData - return - } - n := f.copyLen - if n > len(buf) { - n = len(buf) - } - if f.pos+n > len(f.input) { - f.err = io.ErrUnexpectedEOF - return - } - copy(buf[:n], f.input[f.pos:f.pos+n]) - f.pos += n - f.roffset += int64(n) - f.copyLen -= n - f.window.writeMark(n) - if f.window.availWrite() == 0 { - f.toRead = f.window.readFlush() - f.step = (*sliceInflater).copyData - return - } - } -} - -func (f *sliceInflater) finishBlock() { - if f.final { - if f.window.availRead() > 0 { - f.toRead = f.window.readFlush() - } - f.err = io.EOF - } - f.step = (*sliceInflater).nextBlock - f.stepState = 0 -} - -func (f *sliceInflater) moreBits() error { - if f.pos >= len(f.input) { - return io.ErrUnexpectedEOF - } - c := f.input[f.pos] - f.pos++ - f.roffset++ - f.b |= uint32(c) << (f.nb & 31) - f.nb += 8 - return nil -} - -func (f *sliceInflater) huffSym(h *huffmanDecoder) (int, error) { - n := uint(h.min) - nb, b := f.nb, f.b - for { - for nb < n { - if f.pos >= len(f.input) { - f.b = b - f.nb = nb - return 0, io.ErrUnexpectedEOF - } - c := f.input[f.pos] - f.pos++ - f.roffset++ - b |= uint32(c) << (nb & 31) - nb += 8 - } - chunk := h.chunks[b&(huffmanNumChunks-1)] - n = uint(chunk & huffmanCountMask) - if n > huffmanChunkBits { - chunk = h.links[chunk>>huffmanValueShift][(b>>huffmanChunkBits)&h.linkMask] - n = uint(chunk & huffmanCountMask) - } - if n <= nb { - if n == 0 { - f.b = b - f.nb = nb - f.err = CorruptInputError(f.roffset) - return 0, f.err - } - f.b = b >> (n & 31) - f.nb = nb - n - return int(chunk >> huffmanValueShift), nil - } - } -} - -func (f *sliceInflater) readHuffman() error { - for f.nb < 5+5+4 { - if err := f.moreBits(); err != nil { - return err - } - } - nlit := int(f.b&0x1F) + 257 - if nlit > maxNumLit { - return CorruptInputError(f.roffset) - } - f.b >>= 5 - ndist := int(f.b&0x1F) + 1 - if ndist > maxNumDist { - return CorruptInputError(f.roffset) - } - f.b >>= 5 - nclen := int(f.b&0xF) + 4 - f.b >>= 4 - f.nb -= 5 + 5 + 4 - codebits := f.codebits[:] - bits := f.bits[:] - clear(codebits) - clear(bits) - for i := 0; i < nclen; i++ { - for f.nb < 3 { - if err := f.moreBits(); err != nil { - return err - } - } - codebits[codeOrder[i]] = int(f.b & 0x7) - f.b >>= 3 - f.nb -= 3 - } - if !f.h1.init(codebits) { - return CorruptInputError(f.roffset) - } - for i := range bits { - bits[i] = 0 - } - i := 0 - for i < nlit+ndist { - x, err := f.huffSym(&f.h1) - if err != nil { - return err - } - switch { - case x < 16: - bits[i] = x - i++ - case x == 16: - if i == 0 { - return CorruptInputError(f.roffset) - } - repeat := 3 - for f.nb < 2 { - if err := f.moreBits(); err != nil { - return err - } - } - repeat += int(f.b & 0x3) - f.b >>= 2 - f.nb -= 2 - for repeat > 0 { - if i >= len(bits) { - return CorruptInputError(f.roffset) - } - bits[i] = bits[i-1] - i++ - repeat-- - } - case x == 17: - repeat := 3 - for f.nb < 3 { - if err := f.moreBits(); err != nil { - return err - } - } - repeat += int(f.b & 0x7) - f.b >>= 3 - f.nb -= 3 - for repeat > 0 { - if i >= len(bits) { - return CorruptInputError(f.roffset) - } - bits[i] = 0 - i++ - repeat-- - } - case x == 18: - repeat := 11 - for f.nb < 7 { - if err := f.moreBits(); err != nil { - return err - } - } - repeat += int(f.b & 0x7F) - f.b >>= 7 - f.nb -= 7 - for repeat > 0 { - if i >= len(bits) { - return CorruptInputError(f.roffset) - } - bits[i] = 0 - i++ - repeat-- - } - default: - return CorruptInputError(f.roffset) - } - } - if !f.h1.init(bits[:nlit]) { - return CorruptInputError(f.roffset) - } - if !f.h2.init(bits[nlit : nlit+ndist]) { - return CorruptInputError(f.roffset) - } - if f.h1.min < bits[endBlockMarker] { - f.h1.min = bits[endBlockMarker] - } - return nil -} diff --git a/internal/flatex/window_decoder.go b/internal/flatex/window_decoder.go deleted file mode 100644 index 492c6a96..00000000 --- a/internal/flatex/window_decoder.go +++ /dev/null @@ -1,101 +0,0 @@ -package flatex - -// windowDecoder implements the sliding window used in decompression. -type windowDecoder struct { - hist []byte - - wrPos int - rdPos int - full bool -} - -func (wd *windowDecoder) init(size int) { - *wd = windowDecoder{hist: wd.hist} - - if cap(wd.hist) < size { - wd.hist = make([]byte, size) - } - wd.hist = wd.hist[:size] - - wd.wrPos = 0 - wd.rdPos = 0 - wd.full = false -} - -func (wd *windowDecoder) histSize() int { - if wd.full { - return len(wd.hist) - } - return wd.wrPos -} - -func (wd *windowDecoder) availRead() int { - return wd.wrPos - wd.rdPos -} - -func (wd *windowDecoder) availWrite() int { - return len(wd.hist) - wd.wrPos -} - -func (wd *windowDecoder) writeSlice() []byte { - return wd.hist[wd.wrPos:] -} - -func (wd *windowDecoder) writeMark(cnt int) { - wd.wrPos += cnt -} - -func (wd *windowDecoder) writeByte(c byte) { - wd.hist[wd.wrPos] = c - wd.wrPos++ -} - -func (wd *windowDecoder) writeCopy(dist, length int) int { - dstBase := wd.wrPos - dstPos := dstBase - srcPos := dstPos - dist - endPos := dstPos + length - if endPos > len(wd.hist) { - endPos = len(wd.hist) - } - - if srcPos < 0 { - srcPos += len(wd.hist) - dstPos += copy(wd.hist[dstPos:endPos], wd.hist[srcPos:]) - srcPos = 0 - } - - for dstPos < endPos { - dstPos += copy(wd.hist[dstPos:endPos], wd.hist[srcPos:dstPos]) - } - - wd.wrPos = dstPos - return dstPos - dstBase -} - -func (wd *windowDecoder) tryWriteCopy(dist, length int) int { - dstPos := wd.wrPos - endPos := dstPos + length - if dstPos < dist || endPos > len(wd.hist) { - return 0 - } - dstBase := dstPos - srcPos := dstPos - dist - - for dstPos < endPos { - dstPos += copy(wd.hist[dstPos:endPos], wd.hist[srcPos:dstPos]) - } - - wd.wrPos = dstPos - return dstPos - dstBase -} - -func (wd *windowDecoder) readFlush() []byte { - toRead := wd.hist[wd.rdPos:wd.wrPos] - wd.rdPos = wd.wrPos - if wd.wrPos == len(wd.hist) { - wd.wrPos, wd.rdPos = 0, 0 - wd.full = true - } - return toRead -} diff --git a/internal/zlib/LICENSE b/internal/zlib/LICENSE deleted file mode 100644 index 2a7cf70d..00000000 --- a/internal/zlib/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2009 The Go Authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google LLC nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/internal/zlib/reader.go b/internal/zlib/reader.go deleted file mode 100644 index 2234e7e0..00000000 --- a/internal/zlib/reader.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package zlib implements reading and writing of zlib format compressed data, -as specified in RFC 1950. - -This package differs from the standard library's compress/zlib package -in that it pools readers and writers to reduce allocations. - -Note that closing a reader or writer causes it to be returned to a pool -for reuse. Therefore, the caller must not retain references to a -reader or writer after closing it; in the standard library's -compress/zlib package, it is legal to Reset a closed reader or writer -and continue using it; that is not allowed here, so there is simply no -Resetter interface. - -The implementation provides filters that uncompress during reading -and compress during writing. For example, to write compressed data -to a buffer: - - var b bytes.Buffer - w := zlib.NewWriter(&b) - w.Write([]byte("hello, world\n")) - w.Close() - -and to read that data back: - - r, err := zlib.NewReader(&b) - io.Copy(os.Stdout, r) - r.Close() -*/ -package zlib - -import ( - "bufio" - "compress/flate" - "encoding/binary" - "errors" - "hash" - "io" - "sync" - - "codeberg.org/lindenii/furgit/internal/adler32" -) - -const ( - zlibDeflate = 8 - zlibMaxWindow = 7 -) - -var ( - // ErrChecksum is returned when reading ZLIB data that has an invalid checksum. - ErrChecksum = errors.New("zlib: invalid checksum") - // ErrDictionary is returned when reading ZLIB data that has an invalid dictionary. - ErrDictionary = errors.New("zlib: invalid dictionary") - // ErrHeader is returned when reading ZLIB data that has an invalid header. - ErrHeader = errors.New("zlib: invalid header") -) - -var readerPool = sync.Pool{ - New: func() any { - r := new(reader) - return r - }, -} - -type reader struct { - r flate.Reader - decompressor io.ReadCloser - digest hash.Hash32 - err error - scratch [4]byte -} - -// NewReader creates a new ReadCloser. -// Reads from the returned ReadCloser read and decompress data from r. -// If r does not implement [io.ByteReader], the decompressor may read more -// data than necessary from r. -// It is the caller's responsibility to call Close on the ReadCloser when done. -func NewReader(r io.Reader) (io.ReadCloser, error) { - return NewReaderDict(r, nil) -} - -// NewReaderDict is like [NewReader] but uses a preset dictionary. -// NewReaderDict ignores the dictionary if the compressed data does not refer to it. -// If the compressed data refers to a different dictionary, NewReaderDict returns [ErrDictionary]. -func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, error) { - v := readerPool.Get() - z, ok := v.(*reader) - if !ok { - panic("zlib: pool returned unexpected type") - } - err := z.Reset(r, dict) - if err != nil { - return nil, err - } - return z, nil -} - -func (z *reader) Read(p []byte) (int, error) { - if z.err != nil { - return 0, z.err - } - - var n int - n, z.err = z.decompressor.Read(p) - z.digest.Write(p[0:n]) - if z.err != io.EOF { - // In the normal case we return here. - return n, z.err - } - - // Finished file; check checksum. - if _, err := io.ReadFull(z.r, z.scratch[0:4]); err != nil { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - z.err = err - return n, z.err - } - // ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952). - checksum := binary.BigEndian.Uint32(z.scratch[:4]) - if checksum != z.digest.Sum32() { - z.err = ErrChecksum - return n, z.err - } - return n, io.EOF -} - -// Calling Close does not close the wrapped [io.Reader] originally passed to [NewReader]. -// In order for the ZLIB checksum to be verified, the reader must be -// fully consumed until the [io.EOF]. -func (z *reader) Close() error { - if z.err != nil && z.err != io.EOF { - return z.err - } - z.err = z.decompressor.Close() - if z.err != nil { - return z.err - } - - readerPool.Put(z) - return nil -} - -func (z *reader) Reset(r io.Reader, dict []byte) error { - *z = reader{decompressor: z.decompressor} - if fr, ok := r.(flate.Reader); ok { - z.r = fr - } else { - z.r = bufio.NewReader(r) - } - - // Read the header (RFC 1950 section 2.2.). - _, z.err = io.ReadFull(z.r, z.scratch[0:2]) - if z.err != nil { - if z.err == io.EOF { - z.err = io.ErrUnexpectedEOF - } - return z.err - } - h := binary.BigEndian.Uint16(z.scratch[:2]) - if (z.scratch[0]&0x0f != zlibDeflate) || (z.scratch[0]>>4 > zlibMaxWindow) || (h%31 != 0) { - z.err = ErrHeader - return z.err - } - haveDict := z.scratch[1]&0x20 != 0 - if haveDict { - _, z.err = io.ReadFull(z.r, z.scratch[0:4]) - if z.err != nil { - if z.err == io.EOF { - z.err = io.ErrUnexpectedEOF - } - return z.err - } - checksum := binary.BigEndian.Uint32(z.scratch[:4]) - if checksum != adler32.Checksum(dict) { - z.err = ErrDictionary - return z.err - } - } - - if z.decompressor == nil { - if haveDict { - z.decompressor = flate.NewReaderDict(z.r, dict) - } else { - z.decompressor = flate.NewReader(z.r) - } - } else { - z.err = z.decompressor.(flate.Resetter).Reset(z.r, dict) - if z.err != nil { - return z.err - } - } - z.digest = adler32.New() - return nil -} diff --git a/internal/zlib/writer.go b/internal/zlib/writer.go deleted file mode 100644 index 81c57f55..00000000 --- a/internal/zlib/writer.go +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package zlib - -import ( - "compress/flate" - "encoding/binary" - "fmt" - "hash" - "io" - "sync" - - "codeberg.org/lindenii/furgit/internal/adler32" -) - -// These constants are copied from the [flate] package, so that code that imports -// [compress/zlib] does not also have to import [compress/flate]. -const ( - NoCompression = flate.NoCompression - BestSpeed = flate.BestSpeed - BestCompression = flate.BestCompression - DefaultCompression = flate.DefaultCompression - HuffmanOnly = flate.HuffmanOnly -) - -// A Writer takes data written to it and writes the compressed -// form of that data to an underlying writer (see [NewWriter]). -type Writer struct { - w io.Writer - level int - dict []byte - compressor *flate.Writer - digest hash.Hash32 - err error - scratch [4]byte - wroteHeader bool -} - -var writerPool = sync.Pool{ - New: func() any { - return new(Writer) - }, -} - -// NewWriter creates a new [Writer]. -// Writes to the returned Writer are compressed and written to w. -// -// It is the caller's responsibility to call Close on the Writer when done. -// Writes may be buffered and not flushed until Close. -func NewWriter(w io.Writer) *Writer { - z, _ := NewWriterLevelDict(w, DefaultCompression, nil) - return z -} - -// NewWriterLevel is like [NewWriter] but specifies the compression level instead -// of assuming [DefaultCompression]. -// -// The compression level can be [DefaultCompression], [NoCompression], [HuffmanOnly] -// or any integer value between [BestSpeed] and [BestCompression] inclusive. -// The error returned will be nil if the level is valid. -func NewWriterLevel(w io.Writer, level int) (*Writer, error) { - return NewWriterLevelDict(w, level, nil) -} - -// NewWriterLevelDict is like [NewWriterLevel] but specifies a dictionary to -// compress with. -// -// The dictionary may be nil. If not, its contents should not be modified until -// the Writer is closed. -func NewWriterLevelDict(w io.Writer, level int, dict []byte) (*Writer, error) { - if level < HuffmanOnly || level > BestCompression { - return nil, fmt.Errorf("zlib: invalid compression level: %d", level) - } - v := writerPool.Get() - z, ok := v.(*Writer) - if !ok { - panic("zlib: pool returned unexpected type") - } - - // flate.Writer can only be Reset with the same level/dictionary mode. - // Reuse it only when the configuration is unchanged and dictionary-free. - reuseCompressor := z.compressor != nil && z.level == level && z.dict == nil && dict == nil - if !reuseCompressor { - z.compressor = nil - } - if z.digest != nil { - z.digest.Reset() - } - - *z = Writer{ - w: w, - level: level, - dict: dict, - compressor: z.compressor, - digest: z.digest, - } - if z.compressor != nil { - z.compressor.Reset(w) - } - return z, nil -} - -// Reset clears the state of the [Writer] z such that it is equivalent to its -// initial state from [NewWriterLevel] or [NewWriterLevelDict], but instead writing -// to w. -func (z *Writer) Reset(w io.Writer) { - z.w = w - // z.level and z.dict left unchanged. - if z.compressor != nil { - z.compressor.Reset(w) - } - if z.digest != nil { - z.digest.Reset() - } - z.err = nil - z.scratch = [4]byte{} - z.wroteHeader = false -} - -// writeHeader writes the ZLIB header. -func (z *Writer) writeHeader() (err error) { - z.wroteHeader = true - // ZLIB has a two-byte header (as documented in RFC 1950). - // The first four bits is the CINFO (compression info), which is 7 for the default deflate window size. - // The next four bits is the CM (compression method), which is 8 for deflate. - z.scratch[0] = 0x78 - // The next two bits is the FLEVEL (compression level). The four values are: - // 0=fastest, 1=fast, 2=default, 3=best. - // The next bit, FDICT, is set if a dictionary is given. - // The final five FCHECK bits form a mod-31 checksum. - switch z.level { - case -2, 0, 1: - z.scratch[1] = 0 << 6 - case 2, 3, 4, 5: - z.scratch[1] = 1 << 6 - case 6, -1: - z.scratch[1] = 2 << 6 - case 7, 8, 9: - z.scratch[1] = 3 << 6 - default: - panic("unreachable") - } - if z.dict != nil { - z.scratch[1] |= 1 << 5 - } - z.scratch[1] += uint8(31 - binary.BigEndian.Uint16(z.scratch[:2])%31) - if _, err = z.w.Write(z.scratch[0:2]); err != nil { - return err - } - if z.dict != nil { - // The next four bytes are the Adler-32 checksum of the dictionary. - binary.BigEndian.PutUint32(z.scratch[:], adler32.Checksum(z.dict)) - if _, err = z.w.Write(z.scratch[0:4]); err != nil { - return err - } - } - if z.compressor == nil { - // Initialize deflater unless the Writer is being reused - // after a Reset call. - z.compressor, err = flate.NewWriterDict(z.w, z.level, z.dict) - if err != nil { - return err - } - z.digest = adler32.New() - } - return nil -} - -// Write writes a compressed form of p to the underlying [io.Writer]. The -// compressed bytes are not necessarily flushed until the [Writer] is closed or -// explicitly flushed. -func (z *Writer) Write(p []byte) (n int, err error) { - if !z.wroteHeader { - z.err = z.writeHeader() - } - if z.err != nil { - return 0, z.err - } - if len(p) == 0 { - return 0, nil - } - n, err = z.compressor.Write(p) - if err != nil { - z.err = err - return - } - z.digest.Write(p) - return -} - -// Flush flushes the Writer to its underlying [io.Writer]. -func (z *Writer) Flush() error { - if !z.wroteHeader { - z.err = z.writeHeader() - } - if z.err != nil { - return z.err - } - z.err = z.compressor.Flush() - return z.err -} - -// Close closes the Writer, flushing any unwritten data to the underlying -// [io.Writer], but does not close the underlying io.Writer. -func (z *Writer) Close() error { - if !z.wroteHeader { - z.err = z.writeHeader() - } - if z.err != nil { - return z.err - } - z.err = z.compressor.Close() - if z.err != nil { - return z.err - } - checksum := z.digest.Sum32() - // ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952). - binary.BigEndian.PutUint32(z.scratch[:], checksum) - _, z.err = z.w.Write(z.scratch[0:4]) - if z.err != nil { - return z.err - } - - writerPool.Put(z) - return nil -} diff --git a/internal/zlibx/LICENSE b/internal/zlibx/LICENSE deleted file mode 100644 index 2a7cf70d..00000000 --- a/internal/zlibx/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2009 The Go Authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google LLC nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/internal/zlibx/constants.go b/internal/zlibx/constants.go deleted file mode 100644 index 161e3458..00000000 --- a/internal/zlibx/constants.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package zlibx implements reading of zlib format compressed data, -as specified in RFC 1950. - -This package differs from the standard library's compress/zlib package -in that it pools readers to reduce allocations. Writing is unsupported. - -THis package will likely be refactorered much more for our specific -use case of only doing full decompressions to byte slices. - -Note that closing the reader causes it to be returned to a pool for -reuse. Therefore, the caller must not retain references to the -reader after closing it; in the standard library's compress/zlib package, -it is legal to Reset a closed reader and continue using it; that is -not allowed here, so there is simply no Resetter interface. - -The implementation provides filters that uncompress during reading -and compress during writing. For example, to write compressed data -to a buffer: - - var b bytes.Buffer - w := zlib.NewWriter(&b) - w.Write([]byte("hello, world\n")) - w.Close() - -and to read that data back: - - r, err := zlib.NewReader(&b) - io.Copy(os.Stdout, r) - r.Close() -*/ -package zlibx - -import ( - "errors" -) - -const ( - zlibDeflate = 8 - zlibMaxWindow = 7 -) - -var ( - // ErrChecksum is returned when reading ZLIB data that has an invalid checksum. - ErrChecksum = errors.New("zlib: invalid checksum") - // ErrHeader is returned when reading ZLIB data that has an invalid header. - ErrHeader = errors.New("zlib: invalid header") -) diff --git a/internal/zlibx/decompress.go b/internal/zlibx/decompress.go deleted file mode 100644 index 126c1fcb..00000000 --- a/internal/zlibx/decompress.go +++ /dev/null @@ -1,54 +0,0 @@ -package zlibx - -import ( - "encoding/binary" - "io" - - "codeberg.org/lindenii/furgit/internal/adler32" - "codeberg.org/lindenii/furgit/internal/bufpool" - "codeberg.org/lindenii/furgit/internal/flatex" -) - -func Decompress(src []byte) (bufpool.Buffer, error) { - out, _, err := DecompressSized(src, 0) - return out, err -} - -func DecompressSized(src []byte, sizeHint int) (buf bufpool.Buffer, consumed int, err error) { - if len(src) < 6 { - return bufpool.Buffer{}, 0, io.ErrUnexpectedEOF - } - - cmf := src[0] - flg := src[1] - if (cmf&0x0f != zlibDeflate) || (cmf>>4 > zlibMaxWindow) || (binary.BigEndian.Uint16(src[:2])%31 != 0) { - return bufpool.Buffer{}, 0, ErrHeader - } - - offset := 2 - if flg&0x20 != 0 { - return bufpool.Buffer{}, 0, ErrHeader - } - - if len(src[offset:]) < 4 { - return bufpool.Buffer{}, 0, io.ErrUnexpectedEOF - } - - deflateData := src[offset:] - out, consumed, err := flatex.DecompressSized(deflateData, sizeHint) - if err != nil { - return bufpool.Buffer{}, 0, err - } - - checksumPos := offset + consumed - if checksumPos+4 > len(src) { - out.Release() - return bufpool.Buffer{}, 0, io.ErrUnexpectedEOF - } - expected := binary.BigEndian.Uint32(src[checksumPos : checksumPos+4]) - if expected != adler32.Checksum(out.Bytes()) { - out.Release() - return bufpool.Buffer{}, 0, ErrChecksum - } - return out, checksumPos + 4, nil -} diff --git a/internal/zlibx/decompress_test.go b/internal/zlibx/decompress_test.go deleted file mode 100644 index bea348d2..00000000 --- a/internal/zlibx/decompress_test.go +++ /dev/null @@ -1,170 +0,0 @@ -package zlibx - -import ( - "bytes" - stdzlib "compress/zlib" - "crypto/rand" - "testing" -) - -func compressZlib(t *testing.T, payload []byte) []byte { - t.Helper() - var buf bytes.Buffer - w := stdzlib.NewWriter(&buf) - if _, err := w.Write(payload); err != nil { - t.Fatalf("Write: %v", err) - } - if err := w.Close(); err != nil { - t.Fatalf("Close: %v", err) - } - return buf.Bytes() -} - -func TestDecompress(t *testing.T) { - makeRand := func(n int) []byte { - b := make([]byte, n) - if _, err := rand.Read(b); err != nil { - t.Fatalf("rand.Read: %v", err) - } - return b - } - - type tc struct { - name string - payload []byte - } - - tests := []tc{ - { - name: "simple-hello", - payload: []byte("hello, zlib world!"), - }, - { - name: "empty", - payload: []byte{}, - }, - { - name: "single-byte", - payload: []byte{0x42}, - }, - { - name: "all-zero-1k", - payload: bytes.Repeat([]byte{0}, 1024), - }, - { - name: "all-FF-1k", - payload: bytes.Repeat([]byte{0xFF}, 1024), - }, - { - name: "ascii-repeated-pattern", - payload: bytes.Repeat([]byte("ABC123!"), 500), - }, - { - name: "binary-structured", - payload: []byte{ - 0x00, 0x01, 0x02, 0x03, - 0x10, 0x20, 0x30, 0x40, - 0xFF, 0xEE, 0xDD, 0xCC, - }, - }, - { - name: "1k-crypto-random", - payload: makeRand(1024), - }, - { - name: "32k-crypto-random", - payload: makeRand(32 * 1024), - }, - { - name: "256k-crypto-random", - payload: makeRand(256 * 1024), - }, - { - name: "highly-compressible-large", - payload: bytes.Repeat([]byte("AAAAAAAAAAAAAAAAAAAA"), 50_000), - }, - { - name: "json", - payload: []byte(`{"name":"test","values":[1,2,3,4],"deep":{"x":123,"y":"abc"}}`), - }, - { - name: "html", - payload: []byte("<html><body><h1>Title</h1><p>Paragraph</p></body></html>"), - }, - { - name: "alternating-binary-pattern", - payload: func() []byte { - b := make([]byte, 4096) - for i := 0; i < len(b); i++ { - if i%2 == 0 { - b[i] = 0xAA - } else { - b[i] = 0x55 - } - } - return b - }(), - }, - { - name: "large-repetitive-words", - payload: bytes.Repeat([]byte("the quick brown fox jumps over the lazy dog\n"), 4000), - }, - { - name: "unicode", - payload: []byte("我不知道该说点啥就随便打点字吧🤷♀️"), - }, - { - name: "multi-meg-random-2MB", - payload: makeRand(2 * 1024 * 1024), - }, - { - name: "multi-meg-random-16MB", - payload: makeRand(16 * 1024 * 1024), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - compressed := compressZlib(t, tt.payload) - - out, err := Decompress(compressed) - if err != nil { - t.Fatalf("Decompress: %v", err) - } - defer out.Release() - - if !bytes.Equal(out.Bytes(), tt.payload) { - t.Fatalf("payload mismatch: got %d bytes, want %d", len(out.Bytes()), len(tt.payload)) - } - }) - } -} - -func TestDecompressChecksumError(t *testing.T) { - payload := []byte("checksum check") - compressed := compressZlib(t, payload) - compressed[len(compressed)-1] ^= 0xff - - if _, err := Decompress(compressed); err != ErrChecksum { - t.Fatalf("expected ErrChecksum, got %v", err) - } -} - -func TestDecompressSizedUsesHint(t *testing.T) { - payload := []byte("tiny payload") - compressed := compressZlib(t, payload) - - const hint = 1 << 20 - out, _, err := DecompressSized(compressed, hint) - if err != nil { - t.Fatalf("DecompressSized: %v", err) - } - defer out.Release() - - if !bytes.Equal(out.Bytes(), payload) { - t.Fatalf("unexpected payload %q", out.Bytes()) - } - if cap(out.Bytes()) < hint { - t.Fatalf("expected capacity >= %d, got %d", hint, cap(out.Bytes())) - } -} |
