feat: vendor bodgit/sevenzip package to remove go4.org dependency

Vendored the sevenzip package to eliminate dependency chain:
- sevenzip -> go4.org -> 25+ Google/Cloud/telemetry packages

Changes:
- Added internal/sevenzip/ with full package source
- Inlined go4.org/readerutil into multireaderat.go
- Updated all internal imports to use bookhoard/internal/sevenzip
- Preserved .cb7 comic archive support

This reduces bloat by ~4.9 MB and removes unused telemetry
dependencies while maintaining all functionality.
This commit is contained in:
2026-02-22 16:29:21 -05:00
parent c6cf038c8e
commit 46ae45ee92
26 changed files with 4063 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
package aes7z
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"fmt"
"sync"
lru "github.com/hashicorp/golang-lru/v2"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
)
type cacheKey struct {
password string
cycles int
salt string // []byte isn't comparable
}
const cacheSize = 10
//nolint:gochecknoglobals
var once = sync.OnceValues(func() (*lru.Cache[cacheKey, []byte], error) {
return lru.New[cacheKey, []byte](cacheSize)
})
func calculateKey(password string, cycles int, salt []byte) ([]byte, error) {
cache, err := once()
if err != nil {
return nil, fmt.Errorf("aes7z: error creating cache: %w", err)
}
ck := cacheKey{
password: password,
cycles: cycles,
salt: hex.EncodeToString(salt),
}
if key, ok := cache.Get(ck); ok {
return key, nil
}
b := bytes.NewBuffer(salt)
// Convert password to UTF-16LE
utf16le := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)
t := transform.NewWriter(b, utf16le.NewEncoder())
_, _ = t.Write([]byte(password))
key := make([]byte, sha256.Size)
if cycles == 0x3f {
copy(key, b.Bytes())
} else {
h := sha256.New()
for i := uint64(0); i < 1<<cycles; i++ {
// These will never error
_, _ = h.Write(b.Bytes())
_ = binary.Write(h, binary.LittleEndian, i)
}
copy(key, h.Sum(nil))
}
_ = cache.Add(ck, key)
return key, nil
}
+126
View File
@@ -0,0 +1,126 @@
// Package aes7z implements the 7-zip AES decryption.
package aes7z
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"errors"
"fmt"
"io"
)
var (
errAlreadyClosed = errors.New("aes7z: already closed")
errNeedOneReader = errors.New("aes7z: need exactly one reader")
errInsufficientProperties = errors.New("aes7z: not enough properties")
errNoPasswordSet = errors.New("aes7z: no password set")
errUnsupportedMethod = errors.New("aes7z: unsupported compression method")
)
type readCloser struct {
rc io.ReadCloser
salt, iv []byte
cycles int
cbc cipher.BlockMode
buf bytes.Buffer
}
func (rc *readCloser) Close() error {
if rc.rc == nil {
return errAlreadyClosed
}
if err := rc.rc.Close(); err != nil {
return fmt.Errorf("aes7z: error closing: %w", err)
}
rc.rc = nil
return nil
}
func (rc *readCloser) Password(p string) error {
key, err := calculateKey(p, rc.cycles, rc.salt)
if err != nil {
return err
}
block, err := aes.NewCipher(key)
if err != nil {
return fmt.Errorf("aes7z: error creating cipher: %w", err)
}
rc.cbc = cipher.NewCBCDecrypter(block, rc.iv)
return nil
}
func (rc *readCloser) Read(p []byte) (int, error) {
if rc.rc == nil {
return 0, errAlreadyClosed
}
if rc.cbc == nil {
return 0, errNoPasswordSet
}
var block [aes.BlockSize]byte
for rc.buf.Len() < len(p) {
if _, err := io.ReadFull(rc.rc, block[:]); err != nil {
if errors.Is(err, io.EOF) {
break
}
return 0, fmt.Errorf("aes7z: error reading block: %w", err)
}
rc.cbc.CryptBlocks(block[:], block[:])
_, _ = rc.buf.Write(block[:])
}
n, err := rc.buf.Read(p)
if err != nil && !errors.Is(err, io.EOF) {
err = fmt.Errorf("aes7z: error reading: %w", err)
}
return n, err
}
// NewReader returns a new AES-256-CBC & SHA-256 io.ReadCloser. The Password
// method must be called before attempting to call Read so that the block
// cipher is correctly initialised.
func NewReader(p []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
if len(readers) != 1 {
return nil, errNeedOneReader
}
// Need at least two bytes initially
if len(p) < 2 {
return nil, errInsufficientProperties
}
if p[0]&0xc0 == 0 {
return nil, errUnsupportedMethod
}
rc := new(readCloser)
salt := p[0]>>7&1 + p[1]>>4
iv := p[0]>>6&1 + p[1]&0x0f
if len(p) != int(2+salt+iv) {
return nil, errInsufficientProperties
}
rc.salt = p[2 : 2+salt]
rc.iv = make([]byte, aes.BlockSize)
copy(rc.iv, p[2+salt:])
rc.cycles = int(p[0] & 0x3f)
rc.rc = readers[0]
return rc, nil
}
+232
View File
@@ -0,0 +1,232 @@
// Package bcj2 implements the BCJ2 filter for x86 binaries.
package bcj2
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"bookhoard/internal/sevenzip/internal/util"
)
type readCloser struct {
main util.ReadCloser
call io.ReadCloser
jump io.ReadCloser
rd util.ReadCloser
nrange uint
code uint
sd [256 + 2]uint
previous byte
written uint32
buf *bytes.Buffer
}
const (
numMoveBits = 5
numbitModelTotalBits = 11
bitModelTotal uint = 1 << numbitModelTotalBits
numTopBits = 24
topValue uint = 1 << numTopBits
)
var (
errAlreadyClosed = errors.New("bcj2: already closed")
errNeedFourReaders = errors.New("bcj2: need exactly four readers")
)
func isJcc(b0, b1 byte) bool {
return b0 == 0x0f && (b1&0xf0) == 0x80
}
func isJ(b0, b1 byte) bool {
return (b1&0xfe) == 0xe8 || isJcc(b0, b1)
}
func index(b0, b1 byte) int {
switch b1 {
case 0xe8:
return int(b0)
case 0xe9:
return 256
default:
return 257
}
}
// NewReader returns a new BCJ2 io.ReadCloser.
func NewReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
if len(readers) != 4 {
return nil, errNeedFourReaders
}
rc := &readCloser{
main: util.ByteReadCloser(readers[0]),
call: readers[1],
jump: readers[2],
rd: util.ByteReadCloser(readers[3]),
nrange: 0xffffffff,
buf: new(bytes.Buffer),
}
rc.buf.Grow(1 << 16)
b := make([]byte, 5)
if _, err := io.ReadFull(rc.rd, b); err != nil {
if !errors.Is(err, io.EOF) {
err = fmt.Errorf("bcj2: error reading initial state: %w", err)
}
return nil, err
}
for _, x := range b {
rc.code = (rc.code << 8) | uint(x)
}
for i := range rc.sd {
rc.sd[i] = bitModelTotal >> 1
}
return rc, nil
}
func (rc *readCloser) Close() error {
if rc.main == nil || rc.call == nil || rc.jump == nil || rc.rd == nil {
return errAlreadyClosed
}
if err := errors.Join(rc.main.Close(), rc.call.Close(), rc.jump.Close(), rc.rd.Close()); err != nil {
return fmt.Errorf("bcj2: error closing: %w", err)
}
rc.main, rc.call, rc.jump, rc.rd = nil, nil, nil, nil
return nil
}
func (rc *readCloser) Read(p []byte) (int, error) {
if rc.main == nil || rc.call == nil || rc.jump == nil || rc.rd == nil {
return 0, errAlreadyClosed
}
if err := rc.read(); err != nil && !errors.Is(err, io.EOF) {
return 0, err
}
n, err := rc.buf.Read(p)
if err != nil && !errors.Is(err, io.EOF) {
err = fmt.Errorf("bcj2: error reading: %w", err)
}
return n, err
}
func (rc *readCloser) update() error {
if rc.nrange < topValue {
b, err := rc.rd.ReadByte()
if err != nil && !errors.Is(err, io.EOF) {
return fmt.Errorf("bcj2: error reading byte: %w", err)
}
rc.code = (rc.code << 8) | uint(b)
rc.nrange <<= 8
}
return nil
}
func (rc *readCloser) decode(i int) (bool, error) {
newBound := (rc.nrange >> numbitModelTotalBits) * rc.sd[i]
if rc.code < newBound {
rc.nrange = newBound
rc.sd[i] += (bitModelTotal - rc.sd[i]) >> numMoveBits
if err := rc.update(); err != nil {
return false, err
}
return false, nil
}
rc.nrange -= newBound
rc.code -= newBound
rc.sd[i] -= rc.sd[i] >> numMoveBits
if err := rc.update(); err != nil {
return false, err
}
return true, nil
}
//nolint:cyclop,funlen
func (rc *readCloser) read() error {
var (
b byte
err error
)
for {
if b, err = rc.main.ReadByte(); err != nil {
if !errors.Is(err, io.EOF) {
err = fmt.Errorf("bcj2: error reading byte: %w", err)
}
return err
}
rc.written++
_ = rc.buf.WriteByte(b)
if isJ(rc.previous, b) {
break
}
rc.previous = b
if rc.buf.Len() == rc.buf.Cap() {
return nil
}
}
bit, err := rc.decode(index(rc.previous, b))
if err != nil {
return err
}
//nolint:nestif
if bit {
var r io.Reader
if b == 0xe8 {
r = rc.call
} else {
r = rc.jump
}
var dest uint32
if err = binary.Read(r, binary.BigEndian, &dest); err != nil {
if !errors.Is(err, io.EOF) {
err = fmt.Errorf("bcj2: error reading uint32: %w", err)
}
return err
}
dest -= rc.written + 4
_ = binary.Write(rc.buf, binary.LittleEndian, dest)
rc.previous = byte(dest >> 24)
rc.written += 4
} else {
rc.previous = b
}
return nil
}
+55
View File
@@ -0,0 +1,55 @@
package bra
import (
"encoding/binary"
"io"
)
const armAlignment = 4
type arm struct {
ip uint32
}
func (c *arm) Size() int { return armAlignment }
func (c *arm) Convert(b []byte, encoding bool) int {
if len(b) < c.Size() {
return 0
}
if c.ip == 0 {
c.ip += armAlignment
}
var i int
for i = 0; i < len(b) & ^(armAlignment-1); i += armAlignment {
v := binary.LittleEndian.Uint32(b[i:])
c.ip += uint32(armAlignment)
if b[i+3] == 0xeb {
v <<= 2
if encoding {
v += c.ip
} else {
v -= c.ip
}
v >>= 2
v &= 0x00ffffff
v |= 0xeb000000
}
binary.LittleEndian.PutUint32(b[i:], v)
}
return i
}
// NewARMReader returns a new ARM io.ReadCloser.
func NewARMReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
return newReader(readers, new(arm))
}
+104
View File
@@ -0,0 +1,104 @@
package bra
import (
"encoding/binary"
"io"
)
const bcjLookAhead = 4
type bcj struct {
ip, state uint32
}
func (c *bcj) Size() int { return bcjLookAhead + 1 }
func test86MSByte(b byte) bool {
return (b+1)&0xfe == 0
}
//nolint:cyclop,funlen,gocognit
func (c *bcj) Convert(b []byte, encoding bool) int {
if len(b) < c.Size() {
return 0
}
var (
pos uint32
mask = c.state & 7
)
for {
p := pos
for ; int(p) < len(b)-bcjLookAhead; p++ {
if b[p]&0xfe == 0xe8 {
break
}
}
d := p - pos
pos = p
if int(p) >= len(b)-bcjLookAhead {
if d > 2 {
c.state = 0
} else {
c.state = mask >> d
}
c.ip += pos
return int(pos)
}
if d > 2 {
mask = 0
} else {
mask >>= d
if mask != 0 && (mask > 4 || mask == 3 || test86MSByte(b[p+(mask>>1)+1])) {
mask = (mask >> 1) | 4
pos++
continue
}
}
//nolint:nestif
if test86MSByte(b[p+4]) {
v := binary.LittleEndian.Uint32(b[p+1:])
cur := c.ip + uint32(c.Size()) + pos //nolint:gosec
pos += uint32(c.Size()) //nolint:gosec
if encoding {
v += cur
} else {
v -= cur
}
if mask != 0 {
sh := mask & 6 << 2
if test86MSByte(byte(v >> sh)) {
v ^= (uint32(0x100) << sh) - 1
if encoding {
v += cur
} else {
v -= cur
}
}
mask = 0
}
binary.LittleEndian.PutUint32(b[p+1:], v)
b[p+4] = 0 - b[p+4]&1
} else {
mask = (mask >> 1) | 4
pos++
}
}
}
// NewBCJReader returns a new BCJ io.ReadCloser.
func NewBCJReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
return newReader(readers, new(bcj))
}
+7
View File
@@ -0,0 +1,7 @@
// Package bra implements the branch rewriting filter for binaries.
package bra
type converter interface {
Size() int
Convert(b []byte, encoding bool) int
}
+48
View File
@@ -0,0 +1,48 @@
package bra
import (
"encoding/binary"
"io"
)
const ppcAlignment = 4
type ppc struct {
ip uint32
}
func (c *ppc) Size() int { return ppcAlignment }
func (c *ppc) Convert(b []byte, encoding bool) int {
if len(b) < c.Size() {
return 0
}
var i int
for i = 0; i < len(b) & ^(ppcAlignment-1); i += ppcAlignment {
v := binary.BigEndian.Uint32(b[i:])
if b[i+0]&0xfc == 0x48 && b[i+3]&3 == 1 {
if encoding {
v += c.ip
} else {
v -= c.ip
}
v &= 0x03ffffff
v |= 0x48000000
}
c.ip += uint32(ppcAlignment)
binary.BigEndian.PutUint32(b[i:], v)
}
return i
}
// NewPPCReader returns a new PPC io.ReadCloser.
func NewPPCReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
return newReader(readers, new(ppc))
}
+72
View File
@@ -0,0 +1,72 @@
package bra
import (
"bytes"
"errors"
"fmt"
"io"
)
type readCloser struct {
rc io.ReadCloser
buf bytes.Buffer
n int
conv converter
}
var (
errAlreadyClosed = errors.New("bra: already closed")
errNeedOneReader = errors.New("bra: need exactly one reader")
)
func (rc *readCloser) Close() error {
if rc.rc == nil {
return errAlreadyClosed
}
if err := rc.rc.Close(); err != nil {
return fmt.Errorf("bra: error closing: %w", err)
}
rc.rc = nil
return nil
}
func (rc *readCloser) Read(p []byte) (int, error) {
if rc.rc == nil {
return 0, errAlreadyClosed
}
if _, err := io.CopyN(&rc.buf, rc.rc, int64(max(len(p), rc.conv.Size())-rc.buf.Len())); err != nil {
if !errors.Is(err, io.EOF) {
return 0, fmt.Errorf("bra: error buffering: %w", err)
}
if rc.buf.Len() < rc.conv.Size() {
rc.n = rc.buf.Len()
}
}
rc.n += rc.conv.Convert(rc.buf.Bytes()[rc.n:], false)
n, err := rc.buf.Read(p[:min(rc.n, len(p))])
if err != nil && !errors.Is(err, io.EOF) {
err = fmt.Errorf("bra: error reading: %w", err)
}
rc.n -= n
return n, err
}
func newReader(readers []io.ReadCloser, conv converter) (io.ReadCloser, error) {
if len(readers) != 1 {
return nil, errNeedOneReader
}
return &readCloser{
rc: readers[0],
conv: conv,
}, nil
}
+53
View File
@@ -0,0 +1,53 @@
package bra
import (
"encoding/binary"
"io"
)
const sparcAlignment = 4
type sparc struct {
ip uint32
}
func (c *sparc) Size() int { return sparcAlignment }
func (c *sparc) Convert(b []byte, encoding bool) int {
if len(b) < c.Size() {
return 0
}
var i int
for i = 0; i < len(b) & ^(sparcAlignment-1); i += sparcAlignment {
v := binary.BigEndian.Uint32(b[i:])
if (b[i+0] == 0x40 && b[i+1]&0xc0 == 0) || (b[i+0] == 0x7f && b[i+1] >= 0xc0) {
v <<= 2
if encoding {
v += c.ip
} else {
v -= c.ip
}
v &= 0x01ffffff
v -= uint32(1) << 24
v ^= 0xff000000
v >>= 2
v |= 0x40000000
}
c.ip += uint32(sparcAlignment)
binary.BigEndian.PutUint32(b[i:], v)
}
return i
}
// NewSPARCReader returns a new SPARC io.ReadCloser.
func NewSPARCReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
return newReader(readers, new(sparc))
}
+113
View File
@@ -0,0 +1,113 @@
// Package brotli implements the Brotli decompressor.
package brotli
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"sync"
"github.com/andybalholm/brotli"
"github.com/bodgit/plumbing"
)
type readCloser struct {
c io.Closer
r *brotli.Reader
}
const (
frameMagic uint32 = 0x184d2a50
frameSize uint32 = 8
brotliMagic uint16 = 0x5242 // 'B', 'R'
)
var (
//nolint:gochecknoglobals
brotliReaderPool sync.Pool
errAlreadyClosed = errors.New("brotli: already closed")
errNeedOneReader = errors.New("brotli: need exactly one reader")
)
// This isn't part of the Brotli format but is prepended by the 7-zip implementation.
type headerFrame struct {
FrameMagic uint32
FrameSize uint32
CompressedSize uint32
BrotliMagic uint16
UncompressedSize uint16 // * 64 KB
}
func (rc *readCloser) Close() error {
if rc.c == nil || rc.r == nil {
return errAlreadyClosed
}
if err := rc.c.Close(); err != nil {
return fmt.Errorf("brotli: error closing: %w", err)
}
brotliReaderPool.Put(rc.r)
rc.c, rc.r = nil, nil
return nil
}
func (rc *readCloser) Read(p []byte) (int, error) {
if rc.r == nil {
return 0, errAlreadyClosed
}
n, err := rc.r.Read(p)
if err != nil && !errors.Is(err, io.EOF) {
err = fmt.Errorf("brotli: error reading: %w", err)
}
return n, err
}
// NewReader returns a new Brotli io.ReadCloser.
func NewReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
if len(readers) != 1 {
return nil, errNeedOneReader
}
hr, b := new(headerFrame), new(bytes.Buffer)
b.Grow(binary.Size(hr))
// The 7-Zip Brotli compressor adds a 16 byte frame to the beginning of
// the data which will confuse a pure Brotli implementation. Read it
// but keep a copy so we can add it back if it doesn't look right
if err := binary.Read(io.TeeReader(readers[0], b), binary.LittleEndian, hr); err != nil {
if !errors.Is(err, io.EOF) {
err = fmt.Errorf("brotli: error reading frame: %w", err)
}
return nil, err
}
var reader io.ReadCloser
// If the header looks right, continue reading from that point
// onwards, otherwise prepend it again and hope for the best
if hr.FrameMagic == frameMagic && hr.FrameSize == frameSize && hr.BrotliMagic == brotliMagic {
reader = readers[0]
} else {
reader = plumbing.MultiReadCloser(io.NopCloser(b), readers[0])
}
r, ok := brotliReaderPool.Get().(*brotli.Reader)
if ok {
_ = r.Reset(reader)
} else {
r = brotli.NewReader(reader)
}
return &readCloser{
c: readers[0],
r: r,
}, nil
}
@@ -0,0 +1,58 @@
// Package bzip2 implements the Bzip2 decompressor.
package bzip2
import (
"compress/bzip2"
"errors"
"fmt"
"io"
)
type readCloser struct {
c io.Closer
r io.Reader
}
var (
errAlreadyClosed = errors.New("bzip2: already closed")
errNeedOneReader = errors.New("bzip2: need exactly one reader")
)
func (rc *readCloser) Close() error {
if rc.c == nil || rc.r == nil {
return errAlreadyClosed
}
if err := rc.c.Close(); err != nil {
return fmt.Errorf("bzip2: error closing: %w", err)
}
rc.c, rc.r = nil, nil
return nil
}
func (rc *readCloser) Read(p []byte) (int, error) {
if rc.r == nil {
return 0, errAlreadyClosed
}
n, err := rc.r.Read(p)
if err != nil && !errors.Is(err, io.EOF) {
err = fmt.Errorf("bzip2: error reading: %w", err)
}
return n, err
}
// NewReader returns a new bzip2 io.ReadCloser.
func NewReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
if len(readers) != 1 {
return nil, errNeedOneReader
}
return &readCloser{
c: readers[0],
r: bzip2.NewReader(readers[0]),
}, nil
}
@@ -0,0 +1,77 @@
// Package deflate implements the Deflate decompressor.
package deflate
import (
"errors"
"fmt"
"io"
"sync"
"bookhoard/internal/sevenzip/internal/util"
"github.com/klauspost/compress/flate"
)
type readCloser struct {
c io.Closer
fr io.ReadCloser
}
var (
//nolint:gochecknoglobals
flateReaderPool sync.Pool
errAlreadyClosed = errors.New("deflate: already closed")
errNeedOneReader = errors.New("deflate: need exactly one reader")
)
func (rc *readCloser) Close() error {
if rc.c == nil || rc.fr == nil {
return errAlreadyClosed
}
if err := errors.Join(rc.fr.Close(), rc.c.Close()); err != nil {
return fmt.Errorf("deflate: error closing: %w", err)
}
flateReaderPool.Put(rc.fr)
rc.c, rc.fr = nil, nil
return nil
}
func (rc *readCloser) Read(p []byte) (int, error) {
if rc.c == nil || rc.fr == nil {
return 0, errAlreadyClosed
}
n, err := rc.fr.Read(p)
if err != nil && !errors.Is(err, io.EOF) {
err = fmt.Errorf("deflate: error reading: %w", err)
}
return n, err
}
// NewReader returns a new DEFLATE io.ReadCloser.
func NewReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
if len(readers) != 1 {
return nil, errNeedOneReader
}
fr, ok := flateReaderPool.Get().(io.ReadCloser)
if ok {
frf, ok := fr.(flate.Resetter)
if ok {
if err := frf.Reset(util.ByteReadCloser(readers[0]), nil); err != nil {
return nil, fmt.Errorf("deflate: error resetting: %w", err)
}
}
} else {
fr = flate.NewReader(util.ByteReadCloser(readers[0]))
}
return &readCloser{
c: readers[0],
fr: fr,
}, nil
}
@@ -0,0 +1,89 @@
// Package delta implements the Delta filter.
package delta
import (
"errors"
"fmt"
"io"
)
type readCloser struct {
rc io.ReadCloser
state [stateSize]byte
delta int
}
const (
stateSize = 256
)
var (
errAlreadyClosed = errors.New("delta: already closed")
errNeedOneReader = errors.New("delta: need exactly one reader")
errInsufficientProperties = errors.New("delta: not enough properties")
)
func (rc *readCloser) Close() error {
if rc.rc == nil {
return errAlreadyClosed
}
if err := rc.rc.Close(); err != nil {
return fmt.Errorf("delta: error closing: %w", err)
}
rc.rc = nil
return nil
}
func (rc *readCloser) Read(p []byte) (int, error) {
if rc.rc == nil {
return 0, errAlreadyClosed
}
n, err := rc.rc.Read(p)
if err != nil && !errors.Is(err, io.EOF) {
return n, fmt.Errorf("delta: error reading: %w", err)
}
var (
buffer [stateSize]byte
j int
)
copy(buffer[:], rc.state[:rc.delta])
for i := 0; i < n; {
for j = 0; j < rc.delta && i < n; i++ {
p[i] = buffer[j] + p[i]
buffer[j] = p[i]
j++
}
}
if j == rc.delta {
j = 0
}
copy(rc.state[:], buffer[j:rc.delta])
copy(rc.state[rc.delta-j:], buffer[:j])
return n, nil
}
// NewReader returns a new Delta io.ReadCloser.
func NewReader(p []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
if len(readers) != 1 {
return nil, errNeedOneReader
}
if len(p) != 1 {
return nil, errInsufficientProperties
}
return &readCloser{
rc: readers[0],
delta: int(p[0] + 1),
}, nil
}
+71
View File
@@ -0,0 +1,71 @@
// Package lz4 implements the LZ4 decompressor.
package lz4
import (
"errors"
"fmt"
"io"
"sync"
lz4 "github.com/pierrec/lz4/v4"
)
type readCloser struct {
c io.Closer
r *lz4.Reader
}
var (
//nolint:gochecknoglobals
lz4ReaderPool sync.Pool
errAlreadyClosed = errors.New("lz4: already closed")
errNeedOneReader = errors.New("lz4: need exactly one reader")
)
func (rc *readCloser) Close() error {
if rc.c == nil || rc.r == nil {
return errAlreadyClosed
}
if err := rc.c.Close(); err != nil {
return fmt.Errorf("lz4: error closing: %w", err)
}
lz4ReaderPool.Put(rc.r)
rc.c, rc.r = nil, nil
return nil
}
func (rc *readCloser) Read(p []byte) (int, error) {
if rc.r == nil {
return 0, errAlreadyClosed
}
n, err := rc.r.Read(p)
if err != nil && !errors.Is(err, io.EOF) {
err = fmt.Errorf("lz4: error reading: %w", err)
}
return n, err
}
// NewReader returns a new LZ4 io.ReadCloser.
func NewReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
if len(readers) != 1 {
return nil, errNeedOneReader
}
r, ok := lz4ReaderPool.Get().(*lz4.Reader)
if ok {
r.Reset(readers[0])
} else {
r = lz4.NewReader(readers[0])
}
return &readCloser{
c: readers[0],
r: r,
}, nil
}
+112
View File
@@ -0,0 +1,112 @@
// Package lzma implements the LZMA decompressor.
package lzma
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"github.com/ulikunitz/xz/lzma"
)
type readCloser struct {
c io.Closer
r io.Reader
}
var (
errAlreadyClosed = errors.New("lzma: already closed")
errNeedOneReader = errors.New("lzma: need exactly one reader")
)
func (rc *readCloser) Close() error {
if rc.c == nil || rc.r == nil {
return errAlreadyClosed
}
if err := rc.c.Close(); err != nil {
return fmt.Errorf("lzma: error closing: %w", err)
}
rc.c, rc.r = nil, nil
return nil
}
func (rc *readCloser) Read(p []byte) (int, error) {
if rc.r == nil {
return 0, errAlreadyClosed
}
n, err := rc.r.Read(p)
if err != nil && !errors.Is(err, io.EOF) {
err = fmt.Errorf("lzma: error reading: %w", err)
}
return n, err
}
// NewReader returns a new LZMA io.ReadCloser.
func NewReader(p []byte, s uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
if len(readers) != 1 {
return nil, errNeedOneReader
}
h := bytes.NewBuffer(p)
_ = binary.Write(h, binary.LittleEndian, s)
lr, err := lzma.NewReader(multiReader(h, readers[0]))
if err != nil {
return nil, fmt.Errorf("lzma: error creating reader: %w", err)
}
return &readCloser{
c: readers[0],
r: lr,
}, nil
}
func multiReader(b *bytes.Buffer, rc io.ReadCloser) io.Reader {
mr := io.MultiReader(b, rc)
if br, ok := rc.(io.ByteReader); ok {
return &multiByteReader{
b: b,
br: br,
mr: mr,
}
}
return mr
}
type multiByteReader struct {
b *bytes.Buffer
br io.ByteReader
mr io.Reader
}
func (m *multiByteReader) ReadByte() (b byte, err error) {
if m.b.Len() > 0 {
b, err = m.b.ReadByte()
} else {
b, err = m.br.ReadByte()
}
if err != nil {
err = fmt.Errorf("lzma: error multi byte reading: %w", err)
}
return b, err
}
func (m *multiByteReader) Read(p []byte) (int, error) {
n, err := m.mr.Read(p)
if err != nil {
err = fmt.Errorf("lzma: error multi reading: %w", err)
}
return n, err
}
@@ -0,0 +1,77 @@
// Package lzma2 implements the LZMA2 decompressor.
package lzma2
import (
"errors"
"fmt"
"io"
"github.com/ulikunitz/xz/lzma"
)
type readCloser struct {
c io.Closer
r io.Reader
}
var (
errAlreadyClosed = errors.New("lzma2: already closed")
errNeedOneReader = errors.New("lzma2: need exactly one reader")
errInsufficientProperties = errors.New("lzma2: not enough properties")
)
func (rc *readCloser) Close() error {
if rc.c == nil || rc.r == nil {
return errAlreadyClosed
}
if err := rc.c.Close(); err != nil {
return fmt.Errorf("lzma2: error closing: %w", err)
}
rc.c, rc.r = nil, nil
return nil
}
func (rc *readCloser) Read(p []byte) (int, error) {
if rc.r == nil {
return 0, errAlreadyClosed
}
n, err := rc.r.Read(p)
if err != nil && !errors.Is(err, io.EOF) {
err = fmt.Errorf("lzma2: error reading: %w", err)
}
return n, err
}
// NewReader returns a new LZMA2 io.ReadCloser.
func NewReader(p []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
if len(readers) != 1 {
return nil, errNeedOneReader
}
if len(p) != 1 {
return nil, errInsufficientProperties
}
config := lzma.Reader2Config{
DictCap: (2 | (int(p[0]) & 1)) << (p[0]/2 + 11), // This gem came from Lzma2Dec.c
}
if err := config.Verify(); err != nil {
return nil, fmt.Errorf("lzma2: error verifying config: %w", err)
}
lr, err := config.NewReader2(readers[0])
if err != nil {
return nil, fmt.Errorf("lzma2: error creating reader: %w", err)
}
return &readCloser{
c: readers[0],
r: lr,
}, nil
}
+138
View File
@@ -0,0 +1,138 @@
// Package pool implements the reader pooling.
package pool
import (
"container/list"
"runtime"
"sort"
"sync"
"bookhoard/internal/sevenzip/internal/util"
)
// Pooler is the interface implemented by a pool.
type Pooler interface {
Get(offset int64) (util.SizeReadSeekCloser, bool)
Put(offset int64, rc util.SizeReadSeekCloser) (bool, error)
}
// Constructor is the function prototype used to instantiate a pool.
type Constructor func() (Pooler, error)
type noopPool struct{}
// NewNoopPool returns a Pooler that doesn't actually pool anything.
func NewNoopPool() (Pooler, error) {
return new(noopPool), nil
}
func (noopPool) Get(_ int64) (util.SizeReadSeekCloser, bool) {
return nil, false
}
func (noopPool) Put(_ int64, rc util.SizeReadSeekCloser) (bool, error) {
return false, rc.Close() //nolint:wrapcheck
}
type pool struct {
mutex sync.Mutex
size int
evictList *list.List
items map[int64]*list.Element
}
type entry struct {
key int64
value util.SizeReadSeekCloser
}
// NewPool returns a Pooler that uses a LRU strategy to maintain a fixed pool
// of util.SizeReadSeekCloser's keyed by their stream offset.
func NewPool() (Pooler, error) {
return &pool{
size: runtime.NumCPU(),
evictList: list.New(),
items: make(map[int64]*list.Element),
}, nil
}
func (p *pool) Get(offset int64) (util.SizeReadSeekCloser, bool) {
p.mutex.Lock()
defer p.mutex.Unlock()
if ent, ok := p.items[offset]; ok {
_ = p.removeElement(ent, false)
return ent.Value.(*entry).value, true //nolint:forcetypeassert
}
// Sort keys in descending order
keys := p.keys()
sort.Slice(keys, func(i, j int) bool { return keys[i] > keys[j] })
for _, k := range keys {
// First key less than offset is the closest
if k < offset {
ent := p.items[k]
_ = p.removeElement(ent, false)
return ent.Value.(*entry).value, true //nolint:forcetypeassert
}
}
return nil, false
}
func (p *pool) Put(offset int64, rc util.SizeReadSeekCloser) (bool, error) {
p.mutex.Lock()
defer p.mutex.Unlock()
if _, ok := p.items[offset]; ok {
return false, nil
}
ent := &entry{offset, rc}
entry := p.evictList.PushFront(ent)
p.items[offset] = entry
var err error
evict := p.evictList.Len() > p.size
if evict {
err = p.removeOldest()
}
return evict, err
}
func (p *pool) keys() []int64 {
keys := make([]int64, len(p.items))
i := 0
for ent := p.evictList.Back(); ent != nil; ent = ent.Prev() {
keys[i] = ent.Value.(*entry).key //nolint:forcetypeassert
i++
}
return keys
}
func (p *pool) removeOldest() error {
if ent := p.evictList.Back(); ent != nil {
return p.removeElement(ent, true)
}
return nil
}
func (p *pool) removeElement(e *list.Element, cb bool) error {
p.evictList.Remove(e)
kv := e.Value.(*entry) //nolint:forcetypeassert
delete(p.items, kv.key)
if cb {
return kv.value.Close() //nolint:wrapcheck
}
return nil
}
@@ -0,0 +1,8 @@
package util
import "bytes"
// CRC32Equal compares CRC32 checksums.
func CRC32Equal(b []byte, c uint32) bool {
return bytes.Equal(b, []byte{byte(0xff & (c >> 24)), byte(0xff & (c >> 16)), byte(0xff & (c >> 8)), byte(0xff & c)})
}
+68
View File
@@ -0,0 +1,68 @@
// Package util implements various utility types and interfaces.
package util
import "io"
// SizeReadSeekCloser is an io.Reader, io.Seeker, and io.Closer with a Size
// method.
type SizeReadSeekCloser interface {
io.Reader
io.Seeker
io.Closer
Size() int64
}
// Reader is both an io.Reader and io.ByteReader.
type Reader interface {
io.Reader
io.ByteReader
}
// ReadCloser is a Reader that is also an io.Closer.
type ReadCloser interface {
Reader
io.Closer
}
type nopCloser struct {
Reader
}
func (nopCloser) Close() error {
return nil
}
// NopCloser returns a ReadCloser with a no-op Close method wrapping the
// provided Reader r.
func NopCloser(r Reader) ReadCloser {
return &nopCloser{r}
}
type byteReadCloser struct {
io.ReadCloser
}
func (rc *byteReadCloser) ReadByte() (byte, error) {
var b [1]byte
n, err := rc.Read(b[:])
if err != nil {
return 0, err //nolint:wrapcheck
}
if n == 0 {
return 0, io.ErrNoProgress
}
return b[0], nil
}
// ByteReadCloser returns a ReadCloser either by returning the io.ReadCloser
// r if it implements the interface, or wrapping it with a ReadByte method.
func ByteReadCloser(r io.ReadCloser) ReadCloser {
if rc, ok := r.(ReadCloser); ok {
return rc
}
return &byteReadCloser{r}
}
+80
View File
@@ -0,0 +1,80 @@
// Package zstd implements the Zstandard decompressor.
package zstd
import (
"errors"
"fmt"
"io"
"runtime"
"sync"
"github.com/klauspost/compress/zstd"
)
type readCloser struct {
c io.Closer
r *zstd.Decoder
}
var (
//nolint:gochecknoglobals
zstdReaderPool sync.Pool
errAlreadyClosed = errors.New("zstd: already closed")
errNeedOneReader = errors.New("zstd: need exactly one reader")
)
func (rc *readCloser) Close() error {
if rc.c == nil {
return errAlreadyClosed
}
if err := rc.c.Close(); err != nil {
return fmt.Errorf("zstd: error closing: %w", err)
}
zstdReaderPool.Put(rc.r)
rc.c, rc.r = nil, nil
return nil
}
func (rc *readCloser) Read(p []byte) (int, error) {
if rc.r == nil {
return 0, errAlreadyClosed
}
n, err := rc.r.Read(p)
if err != nil && !errors.Is(err, io.EOF) {
err = fmt.Errorf("zstd: error reading: %w", err)
}
return n, err
}
// NewReader returns a new Zstandard io.ReadCloser.
func NewReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
if len(readers) != 1 {
return nil, errNeedOneReader
}
var err error
r, ok := zstdReaderPool.Get().(*zstd.Decoder)
if ok {
if err = r.Reset(readers[0]); err != nil {
return nil, fmt.Errorf("zstd: error resetting: %w", err)
}
} else {
if r, err = zstd.NewReader(readers[0]); err != nil {
return nil, fmt.Errorf("zstd: error creating reader: %w", err)
}
runtime.SetFinalizer(r, (*zstd.Decoder).Close)
}
return &readCloser{
c: readers[0],
r: r,
}, nil
}