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  }