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:
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)})
|
||||
}
|
||||
@@ -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}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user