diff options
| author | 2026-01-29 17:41:23 +0100 | |
|---|---|---|
| committer | 2026-01-29 17:43:52 +0100 | |
| commit | 17c9aee0e781026353ead4ac749a3ae89c83d007 (patch) | |
| tree | fc681ebc99fdcd21339265a7e0fcfd4fe7a17d67 /packed_write_pack.go | |
| parent | README: Various updates (diff) | |
| signature | No signature | |
packed: Write packs with deltas
Diffstat (limited to 'packed_write_pack.go')
| -rw-r--r-- | packed_write_pack.go | 123 |
1 files changed, 110 insertions, 13 deletions
diff --git a/packed_write_pack.go b/packed_write_pack.go index 435c9edb..0bcaf9ca 100644 --- a/packed_write_pack.go +++ b/packed_write_pack.go @@ -115,19 +115,84 @@ func (pw *packWriter) WriteObject(ty ObjectType, body []byte) error { } func (pw *packWriter) WriteOfsDelta(baseOffset uint64, baseSize, resultSize int, delta []byte) error { - _ = baseOffset - _ = baseSize - _ = resultSize - _ = delta - return errPackDeltaUnimplemented + if pw == nil || !pw.wroteHeader { + return ErrInvalidObject + } + if baseSize < 0 || resultSize < 0 { + return ErrInvalidObject + } + if delta == nil { + delta = []byte{} + } + deltaSize := len(delta) + if deltaSize <= 0 { + return ErrInvalidObject + } + currentOffset := pw.bytesWritten + if baseOffset >= currentOffset { + return ErrInvalidObject + } + dist := currentOffset - baseOffset + + hdr, err := packHeaderEncode(ObjectTypeOfsDelta, deltaSize) + if err != nil { + return err + } + if err := pw.writePacked(hdr); err != nil { + return err + } + ofs, err := packOfsEncode(dist) + if err != nil { + return err + } + if err := pw.writePacked(ofs); err != nil { + return err + } + + zw := zlib.NewWriter(&packHashWriter{pw: pw}) + if _, err := zw.Write(delta); err != nil { + _ = zw.Close() + return err + } + return zw.Close() } func (pw *packWriter) WriteRefDelta(base Hash, baseSize, resultSize int, delta []byte) error { - _ = base - _ = baseSize - _ = resultSize - _ = delta - return errPackDeltaUnimplemented + if pw == nil || !pw.wroteHeader { + return ErrInvalidObject + } + if baseSize < 0 || resultSize < 0 { + return ErrInvalidObject + } + if delta == nil { + delta = []byte{} + } + deltaSize := len(delta) + if deltaSize <= 0 { + return ErrInvalidObject + } + baseBytes := base.Bytes() + if len(baseBytes) == 0 { + return ErrInvalidObject + } + + hdr, err := packHeaderEncode(ObjectTypeRefDelta, deltaSize) + if err != nil { + return err + } + if err := pw.writePacked(hdr); err != nil { + return err + } + if err := pw.writePacked(baseBytes); err != nil { + return err + } + + zw := zlib.NewWriter(&packHashWriter{pw: pw}) + if _, err := zw.Write(delta); err != nil { + _ = zw.Close() + return err + } + return zw.Close() } func (pw *packWriter) Close() (Hash, error) { @@ -237,7 +302,7 @@ func (repo *Repository) packWrite(w io.Writer, objects []Hash, opts packWriteOpt if repo == nil { return Hash{}, ErrInvalidObject } - if opts.EnableDeltas || opts.EnableThinPack { + if opts.EnableThinPack { return Hash{}, errPackDeltaUnimplemented } if len(objects) > int(^uint32(0)) { @@ -252,13 +317,45 @@ func (repo *Repository) packWrite(w io.Writer, objects []Hash, opts packWriteOpt return Hash{}, err } + var dctx deltaContext + if opts.EnableDeltas { + dctx.window = defaultDeltaWindow + } + deltaSeed := uint32(0) + for _, id := range objects { ty, body, err := repo.ReadObjectTypeRaw(id) if err != nil { return Hash{}, err } - if err := pw.WriteObject(ty, body); err != nil { - return Hash{}, err + obj := &objectToPack{ + id: id, + ty: ty, + body: body, + } + startOffset := pw.bytesWritten + wroteDelta := false + + if opts.EnableDeltas && ty == ObjectTypeBlob { + base, delta := pickDeltaBase(&dctx, obj, deltaSeed, opts.MinDeltaSavings, opts.MaxDeltaDepth) + if base != nil && delta != nil { + if err := pw.WriteOfsDelta(base.offset, len(base.body), len(body), delta); err != nil { + return Hash{}, err + } + wroteDelta = true + obj.deltaDepth = base.deltaDepth + 1 + } + } + if !wroteDelta { + if err := pw.WriteObject(ty, body); err != nil { + return Hash{}, err + } + obj.deltaDepth = 0 + } + obj.offset = startOffset + + if opts.EnableDeltas && ty == ObjectTypeBlob { + dctx.addCandidate(obj) } } |
