github.com/driusan/dgit@v0.0.0-20221118233547-f39f0c15edbb/git/packiterator.go (about) 1 package git 2 3 import ( 4 "bufio" 5 // "bytes" 6 "compress/flate" 7 "fmt" 8 "hash/crc32" 9 "io" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 14 "encoding/binary" 15 ) 16 17 type byteCounter struct { 18 io.Reader 19 n int64 20 } 21 22 func (r *byteCounter) Read(buf []byte) (int, error) { 23 n, err := r.Reader.Read(buf) 24 r.n += int64(n) 25 return n, err 26 } 27 28 type flateCounter struct { 29 flate.Reader 30 n int64 31 } 32 33 func (r *flateCounter) Read(buf []byte) (int, error) { 34 n, err := r.Reader.Read(buf) 35 r.n += int64(n) 36 return n, err 37 } 38 39 func (r *flateCounter) ReadByte() (byte, error) { 40 r.n += 1 41 return r.Reader.ReadByte() 42 } 43 44 type packIterator func(r io.ReaderAt, i, n int, loc int64, t PackEntryType, osz PackEntrySize, deltaref Sha1, deltaoffset ObjectOffset, rawdata []byte) error 45 46 func iteratePack(c *Client, r io.Reader, initcallback func(int), callback packIterator, trailerCB func(r io.ReaderAt, packn int, packtrailer Sha1) error, crc32cb func(i int, crc uint32) error) (*os.File, error) { 47 // if the reader is not a file, tee it into a temp file to resolve 48 // deltas from. 49 var pack *os.File 50 51 counter := &byteCounter{r, 0} 52 if f, ok := r.(*os.File); ok && f != os.Stdin { 53 pack = f 54 r = counter 55 } else { 56 var err error 57 pdir := filepath.Join(c.ObjectDir, "pack") 58 if !File(pdir).Exists() { 59 if err := os.MkdirAll(pdir, 0755); err != nil { 60 return nil, err 61 } 62 } 63 pack, err = ioutil.TempFile(pdir, ".tmppackfile") 64 if err != nil { 65 return nil, err 66 } 67 // Do not defer pack.Close, it's the caller's responsibility to close it. 68 69 // Only tee into the pack file if it's not a file 70 r = io.TeeReader(counter, pack) 71 } 72 73 var p PackfileHeader 74 if err := binary.Read(r, binary.BigEndian, &p); err != nil { 75 return nil, err 76 } 77 78 if p.Signature != [4]byte{'P', 'A', 'C', 'K'} { 79 return nil, fmt.Errorf("Invalid packfile: %+v", p.Signature) 80 } 81 if p.Version != 2 { 82 return nil, fmt.Errorf("Unsupported packfile version: %d", p.Version) 83 } 84 85 loc := counter.n 86 br := bufio.NewReader(r) 87 initcallback(int(p.Size)) 88 89 for i := uint32(0); i < p.Size; i += 1 { 90 r := br 91 t, sz, deltasha, deltaoff, rawheader := p.ReadHeaderSize(r) 92 93 datacounter := flateCounter{r, 0} 94 stream, err := p.dataStream(&datacounter) 95 if err != nil { 96 return nil, err 97 } 98 data, err := ioutil.ReadAll(stream) 99 if err != nil { 100 return nil, err 101 } 102 103 ocache.Add(ObjectOffset(loc), cachedObject{t, data, int(deltaoff), deltasha}) 104 105 if err := callback(pack, int(i), int(p.Size), loc, t, sz, deltasha, deltaoff, data); err != nil { 106 return nil, err 107 } 108 109 compsize := int64(len(rawheader)) + int64(datacounter.n) 110 111 crc := crc32.NewIEEE() 112 datareader := io.NewSectionReader(pack, loc, compsize) 113 if _, err := io.Copy(crc, datareader); err != nil { 114 return nil, err 115 } 116 crc32cb(int(i), crc.Sum32()) 117 118 loc += compsize 119 120 } 121 122 // We need to read the packfile trailer so that it gets tee'd into 123 // the temp file, or it won't be there for index-pack. 124 var trailer PackfileIndexV2 125 if err := binary.Read(br, binary.BigEndian, &trailer.Packfile); err != nil { 126 return nil, err 127 } 128 if err := trailerCB(pack, int(p.Size), trailer.Packfile); err != nil { 129 return nil, err 130 } 131 132 return pack, nil 133 }