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 }