github.com/stffabi/git-lfs@v2.3.5-0.20180214015214-8eeaa8d88902+incompatible/git/odb/pack/index_decode.go (about)

     1  package pack
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"io"
     7  
     8  	"github.com/git-lfs/git-lfs/errors"
     9  )
    10  
    11  const (
    12  	// indexMagicWidth is the width of the magic header of packfiles version
    13  	// 2 and newer.
    14  	indexMagicWidth = 4
    15  	// indexVersionWidth is the width of the version following the magic
    16  	// header.
    17  	indexVersionWidth = 4
    18  	// indexV2Width is the total width of the header in V2.
    19  	indexV2Width = indexMagicWidth + indexVersionWidth
    20  	// indexV1Width is the total width of the header in V1.
    21  	indexV1Width = 0
    22  
    23  	// indexFanoutEntries is the number of entries in the fanout table.
    24  	indexFanoutEntries = 256
    25  	// indexFanoutEntryWidth is the width of each entry in the fanout table.
    26  	indexFanoutEntryWidth = 4
    27  	// indexFanoutWidth is the width of the entire fanout table.
    28  	indexFanoutWidth = indexFanoutEntries * indexFanoutEntryWidth
    29  
    30  	// indexOffsetV1Start is the location of the first object outside of the
    31  	// V1 header.
    32  	indexOffsetV1Start = indexV1Width + indexFanoutWidth
    33  	// indexOffsetV2Start is the location of the first object outside of the
    34  	// V2 header.
    35  	indexOffsetV2Start = indexV2Width + indexFanoutWidth
    36  
    37  	// indexObjectNameWidth is the width of a SHA1 object name.
    38  	indexObjectNameWidth = 20
    39  	// indexObjectCRCWidth is the width of the CRC accompanying each object
    40  	// in V2.
    41  	indexObjectCRCWidth = 4
    42  	// indexObjectSmallOffsetWidth is the width of the small offset encoded
    43  	// into each object.
    44  	indexObjectSmallOffsetWidth = 4
    45  	// indexObjectLargeOffsetWidth is the width of the optional large offset
    46  	// encoded into the small offset.
    47  	indexObjectLargeOffsetWidth = 8
    48  
    49  	// indexObjectEntryV1Width is the width of one contiguous object entry
    50  	// in V1.
    51  	indexObjectEntryV1Width = indexObjectNameWidth + indexObjectSmallOffsetWidth
    52  	// indexObjectEntryV2Width is the width of one non-contiguous object
    53  	// entry in V2.
    54  	indexObjectEntryV2Width = indexObjectNameWidth + indexObjectCRCWidth + indexObjectSmallOffsetWidth
    55  )
    56  
    57  var (
    58  	// ErrShortFanout is an error representing situations where the entire
    59  	// fanout table could not be read, and is thus too short.
    60  	ErrShortFanout = errors.New("git/odb/pack: too short fanout table")
    61  
    62  	// indexHeader is the first four "magic" bytes of index files version 2
    63  	// or newer.
    64  	indexHeader = []byte{0xff, 0x74, 0x4f, 0x63}
    65  )
    66  
    67  // DecodeIndex decodes an index whose underlying data is supplied by "r".
    68  //
    69  // DecodeIndex reads only the header and fanout table, and does not eagerly
    70  // parse index entries.
    71  //
    72  // If there was an error parsing, it will be returned immediately.
    73  func DecodeIndex(r io.ReaderAt) (*Index, error) {
    74  	version, err := decodeIndexHeader(r)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	fanout, err := decodeIndexFanout(r, version.Width())
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	return &Index{
    85  		version: version,
    86  		fanout:  fanout,
    87  
    88  		r: r,
    89  	}, nil
    90  }
    91  
    92  // decodeIndexHeader determines which version the index given by "r" is.
    93  func decodeIndexHeader(r io.ReaderAt) (IndexVersion, error) {
    94  	hdr := make([]byte, 4)
    95  	if _, err := r.ReadAt(hdr, 0); err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	if bytes.Equal(hdr, indexHeader) {
   100  		vb := make([]byte, 4)
   101  		if _, err := r.ReadAt(vb, 4); err != nil {
   102  			return nil, err
   103  		}
   104  
   105  		version := binary.BigEndian.Uint32(vb)
   106  		switch version {
   107  		case 1:
   108  			return new(V1), nil
   109  		case 2:
   110  			return new(V2), nil
   111  		}
   112  
   113  		return nil, &UnsupportedVersionErr{uint32(version)}
   114  	}
   115  	return new(V1), nil
   116  }
   117  
   118  // decodeIndexFanout decodes the fanout table given by "r" and beginning at the
   119  // given offset.
   120  func decodeIndexFanout(r io.ReaderAt, offset int64) ([]uint32, error) {
   121  	b := make([]byte, 256*4)
   122  	if _, err := r.ReadAt(b, offset); err != nil {
   123  		if err == io.EOF {
   124  			return nil, ErrShortFanout
   125  		}
   126  		return nil, err
   127  	}
   128  
   129  	fanout := make([]uint32, 256)
   130  	for i, _ := range fanout {
   131  		fanout[i] = binary.BigEndian.Uint32(b[(i * 4):])
   132  	}
   133  
   134  	return fanout, nil
   135  }