github.com/driusan/dgit@v0.0.0-20221118233547-f39f0c15edbb/git/packobjects.go (about) 1 package git 2 3 import ( 4 "bytes" 5 "io" 6 "log" 7 //"os" 8 //"runtime/pprof" 9 10 "compress/zlib" 11 "crypto/sha1" 12 "encoding/binary" 13 "index/suffixarray" 14 15 "github.com/driusan/dgit/git/delta" 16 ) 17 18 type PackObjectsOptions struct { 19 // The number of entries to use for the sliding window for delta 20 // calculations 21 Window int 22 23 // Use offset deltas instead of refdeltas when calculating delta 24 DeltaBaseOffset bool 25 } 26 27 // Used for keeping track of the previous window objects to encode 28 // their location with DeltaBaseOffset set 29 type packWindow struct { 30 oid Sha1 31 location int 32 typ PackEntryType 33 cache []byte 34 index *suffixarray.Index 35 } 36 37 // Writes a packfile to w of the objects objects from the client's 38 // GitDir. 39 func PackObjects(c *Client, opts PackObjectsOptions, w io.Writer, objects []Sha1) (trailer Sha1, err error) { 40 sha := sha1.New() 41 w = io.MultiWriter(w, sha) 42 n, err := w.Write([]byte{'P', 'A', 'C', 'K'}) 43 if n != 4 { 44 panic("Could not write signature") 45 } 46 if err != nil { 47 return Sha1{}, err 48 } 49 50 // Version 51 binary.Write(w, binary.BigEndian, uint32(2)) 52 // Size 53 binary.Write(w, binary.BigEndian, uint32(len(objects))) 54 var window []packWindow = make([]packWindow, 0, opts.Window) 55 56 pos := 12 // PACK + uint32 + uint32 57 for i, obj := range objects { 58 objcontent, err := c.GetObject(obj) 59 if err != nil { 60 return Sha1{}, err 61 } 62 objbytes := objcontent.GetContent() 63 best := objbytes 64 otyp := obj.PackEntryType(c) 65 otypreal := obj.PackEntryType(c) 66 written := 0 67 var ref *packWindow 68 69 // We don't bother trying to calculate how close the object 70 // is, we just blindly calculate a delta and calculate the 71 // size. 72 for _, tryobj := range window { 73 basebytes := tryobj.cache 74 if tryobj.typ != otypreal { 75 continue 76 } 77 78 var newdelta bytes.Buffer 79 if err := delta.CalculateWithIndex(tryobj.index, &newdelta, basebytes, objbytes, len(best)/2); err == nil { 80 81 if d := newdelta.Bytes(); len(d) < len(best) { 82 best = d 83 if opts.DeltaBaseOffset { 84 otyp = OBJ_OFS_DELTA 85 } else { 86 otyp = OBJ_REF_DELTA 87 } 88 ref = &tryobj 89 } 90 } else { 91 log.Println(err) 92 } 93 } 94 95 s := VariableLengthInt(len(best)) 96 97 if n, err := s.WriteVariable(w, otyp); err != nil { 98 return Sha1{}, err 99 } else { 100 written += n 101 } 102 103 if ref != nil { 104 if opts.DeltaBaseOffset { 105 offset := pos - ref.location 106 var buf [128]byte 107 n := binary.PutUvarint(buf[:], uint64(offset)) 108 n, err := w.Write(buf[:n]) 109 if err != nil { 110 return Sha1{}, err 111 } 112 written += n 113 114 } else { 115 if _, err := w.Write(ref.oid[:]); err != nil { 116 return Sha1{}, err 117 } 118 written += 20 119 } 120 } 121 122 // We write into a buffer so that we can calcualte the compressed 123 // size easily. the n from io.Writer is the uncompressed size. 124 var cbuf bytes.Buffer 125 zw := zlib.NewWriter(&cbuf) 126 if err != nil { 127 panic(err) 128 } 129 if _, err := zw.Write(best); err != nil { 130 return Sha1{}, err 131 } 132 zw.Close() 133 134 if _, err := w.Write(cbuf.Bytes()); err != nil { 135 return Sha1{}, err 136 } 137 138 written += cbuf.Len() 139 140 suffix := suffixarray.New(objbytes) 141 if opts.Window > 0 { 142 if i < opts.Window { 143 window = append(window, packWindow{ 144 oid: obj, 145 location: pos, 146 typ: otypreal, 147 cache: objbytes, 148 index: suffix, 149 }) 150 } else { 151 window[i%opts.Window] = packWindow{ 152 oid: obj, 153 location: pos, 154 typ: otypreal, 155 cache: objbytes, 156 index: suffix, 157 } 158 } 159 } 160 161 pos += written 162 } 163 trail := sha.Sum(nil) 164 w.Write(trail) 165 return Sha1FromSlice(trail) 166 }