From 329e6b4557dd90405d0560edcba2ce0440633de0 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Wed, 25 Mar 2026 14:32:04 +0000 Subject: *: delta -> packfile/delta --- delta/apply/apply.go | 160 ----------------------------- delta/apply/header.go | 47 --------- delta/doc.go | 2 - object/store/packed/delta_resolve_chain.go | 2 +- object/store/packed/delta_size.go | 2 +- packfile/delta/apply/apply.go | 160 +++++++++++++++++++++++++++++ packfile/delta/apply/header.go | 47 +++++++++ packfile/delta/doc.go | 2 + packfile/ingest/delta_header.go | 2 +- packfile/ingest/record_delta.go | 2 +- 10 files changed, 213 insertions(+), 213 deletions(-) delete mode 100644 delta/apply/apply.go delete mode 100644 delta/apply/header.go delete mode 100644 delta/doc.go create mode 100644 packfile/delta/apply/apply.go create mode 100644 packfile/delta/apply/header.go create mode 100644 packfile/delta/doc.go diff --git a/delta/apply/apply.go b/delta/apply/apply.go deleted file mode 100644 index f5006e3c..00000000 --- a/delta/apply/apply.go +++ /dev/null @@ -1,160 +0,0 @@ -// Package apply applies Git delta instruction streams. -package apply - -import "fmt" - -// Apply applies one Git delta instruction stream to base. -func Apply(base, delta []byte) ([]byte, error) { - pos := 0 - - srcSize, err := readVarint(delta, &pos) - if err != nil { - return nil, err - } - - dstSize, err := readVarint(delta, &pos) - if err != nil { - return nil, err - } - - if srcSize != len(base) { - return nil, fmt.Errorf("delta/apply: delta source size mismatch: got %d want %d", srcSize, len(base)) - } - - out := make([]byte, dstSize) - outPos := 0 - - for pos < len(delta) { - op := delta[pos] - pos++ - - //nolint:nestif - if op&0x80 != 0 { - off := 0 - - if op&0x01 != 0 { - if pos >= len(delta) { - return nil, fmt.Errorf("delta/apply: malformed delta copy offset") - } - - off |= int(delta[pos]) - pos++ - } - - if op&0x02 != 0 { - if pos >= len(delta) { - return nil, fmt.Errorf("delta/apply: malformed delta copy offset") - } - - off |= int(delta[pos]) << 8 - pos++ - } - - if op&0x04 != 0 { - if pos >= len(delta) { - return nil, fmt.Errorf("delta/apply: malformed delta copy offset") - } - - off |= int(delta[pos]) << 16 - pos++ - } - - if op&0x08 != 0 { - if pos >= len(delta) { - return nil, fmt.Errorf("delta/apply: malformed delta copy offset") - } - - off |= int(delta[pos]) << 24 - pos++ - } - - n := 0 - - if op&0x10 != 0 { - if pos >= len(delta) { - return nil, fmt.Errorf("delta/apply: malformed delta copy size") - } - - n |= int(delta[pos]) - pos++ - } - - if op&0x20 != 0 { - if pos >= len(delta) { - return nil, fmt.Errorf("delta/apply: malformed delta copy size") - } - - n |= int(delta[pos]) << 8 - pos++ - } - - if op&0x40 != 0 { - if pos >= len(delta) { - return nil, fmt.Errorf("delta/apply: malformed delta copy size") - } - - n |= int(delta[pos]) << 16 - pos++ - } - - if n == 0 { - n = 0x10000 - } - - if off < 0 || n < 0 || off+n > len(base) || outPos+n > len(out) { - return nil, fmt.Errorf("delta/apply: delta copy out of bounds") - } - - copy(out[outPos:outPos+n], base[off:off+n]) - outPos += n - - continue - } - - if op == 0 { - return nil, fmt.Errorf("delta/apply: invalid delta opcode 0") - } - - n := int(op) - if pos+n > len(delta) || outPos+n > len(out) { - return nil, fmt.Errorf("delta/apply: delta insert out of bounds") - } - - copy(out[outPos:outPos+n], delta[pos:pos+n]) - outPos += n - pos += n - } - - if outPos != len(out) { - return nil, fmt.Errorf("delta/apply: delta output size mismatch: got %d want %d", outPos, len(out)) - } - - return out, nil -} - -// readVarint parses one Git delta varint and advances pos. -func readVarint(buf []byte, pos *int) (int, error) { - value := 0 - shift := uint(0) - - for { - if *pos >= len(buf) { - return 0, fmt.Errorf("delta/apply: malformed delta varint") - } - - b := buf[*pos] - *pos++ - - value |= int(b&0x7f) << shift - if b&0x80 == 0 { - break - } - - shift += 7 - if shift > 63 { - return 0, fmt.Errorf("delta/apply: delta varint overflow") - } - } - - return value, nil -} diff --git a/delta/apply/header.go b/delta/apply/header.go deleted file mode 100644 index 69c9659a..00000000 --- a/delta/apply/header.go +++ /dev/null @@ -1,47 +0,0 @@ -package apply - -import ( - "fmt" - "io" -) - -// ReadHeaderSizes reads the first two varints in one inflated delta stream. -// -// Callers that continue reading the same stream should pass their own buffered -// byte reader and keep using that same reader afterwards. -func ReadHeaderSizes(reader io.ByteReader) (int, int, error) { - srcSize, err := readVarintFromByteReader(reader) - if err != nil { - return 0, 0, err - } - - dstSize, err := readVarintFromByteReader(reader) - if err != nil { - return 0, 0, err - } - - return srcSize, dstSize, nil -} - -// readVarintFromByteReader parses one Git delta varint from reader. -func readVarintFromByteReader(reader io.ByteReader) (int, error) { - value := 0 - shift := uint(0) - - for { - b, err := reader.ReadByte() - if err != nil { - return 0, fmt.Errorf("delta/apply: malformed delta varint: %w", err) - } - - value |= int(b&0x7f) << shift - if b&0x80 == 0 { - return value, nil - } - - shift += 7 - if shift > 63 { - return 0, fmt.Errorf("delta/apply: delta varint overflow") - } - } -} diff --git a/delta/doc.go b/delta/doc.go deleted file mode 100644 index f63c96a8..00000000 --- a/delta/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package delta provides various routines to handle Git delta compression. -package delta diff --git a/object/store/packed/delta_resolve_chain.go b/object/store/packed/delta_resolve_chain.go index f988a9fb..47992ceb 100644 --- a/object/store/packed/delta_resolve_chain.go +++ b/object/store/packed/delta_resolve_chain.go @@ -3,8 +3,8 @@ package packed import ( "fmt" - deltaapply "codeberg.org/lindenii/furgit/delta/apply" objecttype "codeberg.org/lindenii/furgit/object/type" + deltaapply "codeberg.org/lindenii/furgit/packfile/delta/apply" ) // deltaResolveChain resolves one object chain into content bytes. diff --git a/object/store/packed/delta_size.go b/object/store/packed/delta_size.go index 4c89733c..6896c939 100644 --- a/object/store/packed/delta_size.go +++ b/object/store/packed/delta_size.go @@ -3,7 +3,7 @@ package packed import ( "bufio" - deltaapply "codeberg.org/lindenii/furgit/delta/apply" + deltaapply "codeberg.org/lindenii/furgit/packfile/delta/apply" ) // deltaDeclaredSizeAt returns the resolved object size declared by one delta diff --git a/packfile/delta/apply/apply.go b/packfile/delta/apply/apply.go new file mode 100644 index 00000000..f5006e3c --- /dev/null +++ b/packfile/delta/apply/apply.go @@ -0,0 +1,160 @@ +// Package apply applies Git delta instruction streams. +package apply + +import "fmt" + +// Apply applies one Git delta instruction stream to base. +func Apply(base, delta []byte) ([]byte, error) { + pos := 0 + + srcSize, err := readVarint(delta, &pos) + if err != nil { + return nil, err + } + + dstSize, err := readVarint(delta, &pos) + if err != nil { + return nil, err + } + + if srcSize != len(base) { + return nil, fmt.Errorf("delta/apply: delta source size mismatch: got %d want %d", srcSize, len(base)) + } + + out := make([]byte, dstSize) + outPos := 0 + + for pos < len(delta) { + op := delta[pos] + pos++ + + //nolint:nestif + if op&0x80 != 0 { + off := 0 + + if op&0x01 != 0 { + if pos >= len(delta) { + return nil, fmt.Errorf("delta/apply: malformed delta copy offset") + } + + off |= int(delta[pos]) + pos++ + } + + if op&0x02 != 0 { + if pos >= len(delta) { + return nil, fmt.Errorf("delta/apply: malformed delta copy offset") + } + + off |= int(delta[pos]) << 8 + pos++ + } + + if op&0x04 != 0 { + if pos >= len(delta) { + return nil, fmt.Errorf("delta/apply: malformed delta copy offset") + } + + off |= int(delta[pos]) << 16 + pos++ + } + + if op&0x08 != 0 { + if pos >= len(delta) { + return nil, fmt.Errorf("delta/apply: malformed delta copy offset") + } + + off |= int(delta[pos]) << 24 + pos++ + } + + n := 0 + + if op&0x10 != 0 { + if pos >= len(delta) { + return nil, fmt.Errorf("delta/apply: malformed delta copy size") + } + + n |= int(delta[pos]) + pos++ + } + + if op&0x20 != 0 { + if pos >= len(delta) { + return nil, fmt.Errorf("delta/apply: malformed delta copy size") + } + + n |= int(delta[pos]) << 8 + pos++ + } + + if op&0x40 != 0 { + if pos >= len(delta) { + return nil, fmt.Errorf("delta/apply: malformed delta copy size") + } + + n |= int(delta[pos]) << 16 + pos++ + } + + if n == 0 { + n = 0x10000 + } + + if off < 0 || n < 0 || off+n > len(base) || outPos+n > len(out) { + return nil, fmt.Errorf("delta/apply: delta copy out of bounds") + } + + copy(out[outPos:outPos+n], base[off:off+n]) + outPos += n + + continue + } + + if op == 0 { + return nil, fmt.Errorf("delta/apply: invalid delta opcode 0") + } + + n := int(op) + if pos+n > len(delta) || outPos+n > len(out) { + return nil, fmt.Errorf("delta/apply: delta insert out of bounds") + } + + copy(out[outPos:outPos+n], delta[pos:pos+n]) + outPos += n + pos += n + } + + if outPos != len(out) { + return nil, fmt.Errorf("delta/apply: delta output size mismatch: got %d want %d", outPos, len(out)) + } + + return out, nil +} + +// readVarint parses one Git delta varint and advances pos. +func readVarint(buf []byte, pos *int) (int, error) { + value := 0 + shift := uint(0) + + for { + if *pos >= len(buf) { + return 0, fmt.Errorf("delta/apply: malformed delta varint") + } + + b := buf[*pos] + *pos++ + + value |= int(b&0x7f) << shift + if b&0x80 == 0 { + break + } + + shift += 7 + if shift > 63 { + return 0, fmt.Errorf("delta/apply: delta varint overflow") + } + } + + return value, nil +} diff --git a/packfile/delta/apply/header.go b/packfile/delta/apply/header.go new file mode 100644 index 00000000..69c9659a --- /dev/null +++ b/packfile/delta/apply/header.go @@ -0,0 +1,47 @@ +package apply + +import ( + "fmt" + "io" +) + +// ReadHeaderSizes reads the first two varints in one inflated delta stream. +// +// Callers that continue reading the same stream should pass their own buffered +// byte reader and keep using that same reader afterwards. +func ReadHeaderSizes(reader io.ByteReader) (int, int, error) { + srcSize, err := readVarintFromByteReader(reader) + if err != nil { + return 0, 0, err + } + + dstSize, err := readVarintFromByteReader(reader) + if err != nil { + return 0, 0, err + } + + return srcSize, dstSize, nil +} + +// readVarintFromByteReader parses one Git delta varint from reader. +func readVarintFromByteReader(reader io.ByteReader) (int, error) { + value := 0 + shift := uint(0) + + for { + b, err := reader.ReadByte() + if err != nil { + return 0, fmt.Errorf("delta/apply: malformed delta varint: %w", err) + } + + value |= int(b&0x7f) << shift + if b&0x80 == 0 { + return value, nil + } + + shift += 7 + if shift > 63 { + return 0, fmt.Errorf("delta/apply: delta varint overflow") + } + } +} diff --git a/packfile/delta/doc.go b/packfile/delta/doc.go new file mode 100644 index 00000000..f63c96a8 --- /dev/null +++ b/packfile/delta/doc.go @@ -0,0 +1,2 @@ +// Package delta provides various routines to handle Git delta compression. +package delta diff --git a/packfile/ingest/delta_header.go b/packfile/ingest/delta_header.go index 76e90172..63fda066 100644 --- a/packfile/ingest/delta_header.go +++ b/packfile/ingest/delta_header.go @@ -1,6 +1,6 @@ package ingest -import deltaapply "codeberg.org/lindenii/furgit/delta/apply" +import deltaapply "codeberg.org/lindenii/furgit/packfile/delta/apply" // finalizeStreamPackHash consumes trailer bytes and verifies stream integrity. // readDeltaHeaderSizes reads source and destination sizes from one delta payload. diff --git a/packfile/ingest/record_delta.go b/packfile/ingest/record_delta.go index f1637f5c..69cb8524 100644 --- a/packfile/ingest/record_delta.go +++ b/packfile/ingest/record_delta.go @@ -3,8 +3,8 @@ package ingest import ( "fmt" - deltaapply "codeberg.org/lindenii/furgit/delta/apply" objecttype "codeberg.org/lindenii/furgit/object/type" + deltaapply "codeberg.org/lindenii/furgit/packfile/delta/apply" ) // applyDeltaRecord applies one delta record onto base content. -- cgit v1.3.1-10-gc9f91