Skip to content

Commit

Permalink
Add Delta decompressor
Browse files Browse the repository at this point in the history
  • Loading branch information
bodgit committed Apr 29, 2022
1 parent 60312a6 commit b30a880
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ Current status:
* Handles password-protected versions of both of the above (`7za a -mhc=on|off -mhe=on -ppassword test.7z ...`).
* Handles archives split into multiple volumes, (`7za a -v100m test.7z ...`).
* Validates CRC values as it parses the file.
* Supports BCJ2, Bzip2, Deflate, Copy, LZMA and LZMA2 methods.
* Supports BCJ2, Bzip2, Copy, Deflate, Delta, LZMA and LZMA2 methods.

More examples of 7-zip archives are needed to test all of the different combinations/algorithms possible.
72 changes: 72 additions & 0 deletions internal/delta/reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package delta

import (
"errors"
"io"
)

const (
stateSize = 256
)

type readCloser struct {
rc io.ReadCloser
state [stateSize]byte
delta int
}

func (rc *readCloser) Close() (err error) {
if rc.rc != nil {
err = rc.rc.Close()
rc.rc = nil
}
return
}

func (rc *readCloser) Read(p []byte) (int, error) {
if rc.rc == nil {
return 0, errors.New("delta: Read after Close")
}

n, err := rc.rc.Read(p)
if err != nil {
return n, err
}

var buffer [stateSize]byte
copy(buffer[:], rc.state[:rc.delta])

var j int
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, errors.New("delta: need exactly one reader")
}

if len(p) != 1 {
return nil, errors.New("delta: not enough properties")
}

return &readCloser{
rc: readers[0],
delta: int(p[0] + 1),
}, nil
}
4 changes: 4 additions & 0 deletions reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ func BenchmarkDeflate(b *testing.B) {
benchmarkArchive("deflate.7z", b)
}

func BenchmarkDelta(b *testing.B) {
benchmarkArchive("delta.7z", b)
}

func BenchmarkLZMA(b *testing.B) {
benchmarkArchive("lzma.7z", b)
}
Expand Down
3 changes: 3 additions & 0 deletions register.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/bodgit/sevenzip/internal/bcj2"
"github.com/bodgit/sevenzip/internal/bzip2"
"github.com/bodgit/sevenzip/internal/deflate"
"github.com/bodgit/sevenzip/internal/delta"
"github.com/bodgit/sevenzip/internal/lzma"
"github.com/bodgit/sevenzip/internal/lzma2"
)
Expand All @@ -30,6 +31,8 @@ func init() {
}
return readers[0], nil
}))
// Delta
RegisterDecompressor([]byte{0x03}, Decompressor(delta.NewReader))
// LZMA
RegisterDecompressor([]byte{0x03, 0x01, 0x01}, Decompressor(lzma.NewReader))
// BCJ2
Expand Down
Binary file added testdata/delta.7z
Binary file not shown.

0 comments on commit b30a880

Please sign in to comment.