aboutsummaryrefslogtreecommitdiff
path: root/packed_write_pack.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-01-29 17:41:23 +0100
committerGravatar Runxi Yu2026-01-29 17:43:52 +0100
commit17c9aee0e781026353ead4ac749a3ae89c83d007 (patch)
treefc681ebc99fdcd21339265a7e0fcfd4fe7a17d67 /packed_write_pack.go
parentREADME: Various updates (diff)
signatureNo signature
packed: Write packs with deltas
Diffstat (limited to 'packed_write_pack.go')
-rw-r--r--packed_write_pack.go123
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)
}
}