github.com/stffabi/git-lfs@v2.3.5-0.20180214015214-8eeaa8d88902+incompatible/git/odb/object_reader.go (about) 1 package odb 2 3 import ( 4 "bufio" 5 "compress/zlib" 6 "io" 7 "io/ioutil" 8 "strconv" 9 "strings" 10 11 "github.com/pkg/errors" 12 ) 13 14 // ObjectReader provides an io.Reader implementation that can read Git object 15 // headers, as well as provide an uncompressed view into the object contents 16 // itself. 17 type ObjectReader struct { 18 // header is the object header type 19 header *struct { 20 // typ is the ObjectType encoded in the header pointed at by 21 // this reader. 22 typ ObjectType 23 // size is the number of uncompressed bytes following the header 24 // that encodes the object. 25 size int64 26 } 27 // r is the underling uncompressed reader. 28 r *bufio.Reader 29 30 // closeFn supplies an optional function that, when called, frees an 31 // resources (open files, memory, etc) held by this instance of the 32 // *ObjectReader. 33 // 34 // closeFn returns any error encountered when closing/freeing resources 35 // held. 36 // 37 // It is allowed to be nil. 38 closeFn func() error 39 } 40 41 // NewObjectReader takes a given io.Reader that yields zlib-compressed data, and 42 // returns an *ObjectReader wrapping it, or an error if one occurred during 43 // construction time. 44 func NewObjectReader(r io.Reader) (*ObjectReader, error) { 45 return NewObjectReadCloser(ioutil.NopCloser(r)) 46 } 47 48 // NewObjectReader takes a given io.Reader that yields uncompressed data and 49 // returns an *ObjectReader wrapping it, or an error if one occurred during 50 // construction time. 51 func NewUncompressedObjectReader(r io.Reader) (*ObjectReader, error) { 52 return NewUncompressedObjectReadCloser(ioutil.NopCloser(r)) 53 } 54 55 // NewObjectReadCloser takes a given io.Reader that yields zlib-compressed data, and 56 // returns an *ObjectReader wrapping it, or an error if one occurred during 57 // construction time. 58 // 59 // It also calls the Close() function given by the implementation "r" of the 60 // type io.Closer. 61 func NewObjectReadCloser(r io.ReadCloser) (*ObjectReader, error) { 62 zr, err := zlib.NewReader(r) 63 if err != nil { 64 return nil, err 65 } 66 67 return &ObjectReader{ 68 r: bufio.NewReader(zr), 69 closeFn: func() error { 70 if err := zr.Close(); err != nil { 71 return err 72 } 73 if err := r.Close(); err != nil { 74 return err 75 } 76 return nil 77 }, 78 }, nil 79 } 80 81 // NewUncompressObjectReadCloser takes a given io.Reader that yields 82 // uncompressed data, and returns an *ObjectReader wrapping it, or an error if 83 // one occurred during construction time. 84 // 85 // It also calls the Close() function given by the implementation "r" of the 86 // type io.Closer. 87 func NewUncompressedObjectReadCloser(r io.ReadCloser) (*ObjectReader, error) { 88 return &ObjectReader{ 89 r: bufio.NewReader(r), 90 closeFn: r.Close, 91 }, nil 92 } 93 94 // Header returns information about the Object's header, or an error if one 95 // occurred while reading the data. 96 // 97 // Header information is cached, so this function is safe to call at any point 98 // during the object read, and can be called more than once. 99 func (r *ObjectReader) Header() (typ ObjectType, size int64, err error) { 100 if r.header != nil { 101 return r.header.typ, r.header.size, nil 102 } 103 104 typs, err := r.r.ReadString(' ') 105 if err != nil { 106 return UnknownObjectType, 0, err 107 } 108 if len(typs) == 0 { 109 return UnknownObjectType, 0, errors.Errorf( 110 "git/odb: object type must not be empty", 111 ) 112 } 113 typs = strings.TrimSuffix(typs, " ") 114 115 sizeStr, err := r.r.ReadString('\x00') 116 if err != nil { 117 return UnknownObjectType, 0, err 118 } 119 sizeStr = strings.TrimSuffix(sizeStr, "\x00") 120 121 size, err = strconv.ParseInt(sizeStr, 10, 64) 122 if err != nil { 123 return UnknownObjectType, 0, err 124 } 125 126 r.header = &struct { 127 typ ObjectType 128 size int64 129 }{ 130 ObjectTypeFromString(typs), 131 size, 132 } 133 134 return r.header.typ, r.header.size, nil 135 } 136 137 // Read reads uncompressed bytes into the buffer "p", and returns the number of 138 // uncompressed bytes read. Otherwise, it returns any error encountered along 139 // the way. 140 // 141 // This function is safe to call before reading the Header information, as any 142 // call to Read() will ensure that read has been called at least once. 143 func (r *ObjectReader) Read(p []byte) (n int, err error) { 144 if _, _, err = r.Header(); err != nil { 145 return 0, err 146 } 147 return r.r.Read(p) 148 } 149 150 // Close frees any resources held by the ObjectReader and must be called before 151 // disposing of this instance. 152 // 153 // It returns any error encountered by the *ObjectReader during close. 154 func (r *ObjectReader) Close() error { 155 if r.closeFn == nil { 156 return nil 157 } 158 return r.closeFn() 159 }