github.com/stffabi/git-lfs@v2.3.5-0.20180214015214-8eeaa8d88902+incompatible/git/odb/pack/chain_delta.go (about) 1 package pack 2 3 import ( 4 "github.com/git-lfs/git-lfs/errors" 5 ) 6 7 // ChainDelta represents a "delta" component of a delta-base chain. 8 type ChainDelta struct { 9 // Base is the base delta-base chain that this delta should be applied 10 // to. It can be a ChainBase in the simple case, or it can itself be a 11 // ChainDelta, which resolves against another ChainBase, when the 12 // delta-base chain is of length greater than 2. 13 base Chain 14 // delta is the set of copy/add instructions to apply on top of the 15 // base. 16 delta []byte 17 } 18 19 // Unpack applies the delta operation to the previous delta-base chain, "base". 20 // 21 // If any of the delta-base instructions were invalid, an error will be 22 // returned. 23 func (d *ChainDelta) Unpack() ([]byte, error) { 24 base, err := d.base.Unpack() 25 if err != nil { 26 return nil, err 27 } 28 29 return patch(base, d.delta) 30 } 31 32 // Type returns the type of the base of the delta-base chain. 33 func (d *ChainDelta) Type() PackedObjectType { 34 return d.base.Type() 35 } 36 37 // patch applies the delta instructions in "delta" to the base given as "base". 38 // It returns the result of applying those patch instructions to base, but does 39 // not modify base itself. 40 // 41 // If any of the delta instructions were malformed, or otherwise could not be 42 // applied to the given base, an error will returned, along with an empty set of 43 // data. 44 func patch(base, delta []byte) ([]byte, error) { 45 srcSize, pos := patchDeltaHeader(delta, 0) 46 if srcSize != int64(len(base)) { 47 // The header of the delta gives the size of the source contents 48 // that it is a patch over. 49 // 50 // If this does not match with the srcSize, return an error 51 // early so as to avoid a possible bounds error below. 52 return nil, errors.New("git/odb/pack: invalid delta data") 53 } 54 55 // The remainder of the delta header contains the destination size, and 56 // moves the "pos" offset to the correct position to begin the set of 57 // delta instructions. 58 destSize, pos := patchDeltaHeader(delta, pos) 59 60 dest := make([]byte, 0, destSize) 61 62 for pos < len(delta) { 63 c := int(delta[pos]) 64 pos += 1 65 66 if c&0x80 != 0 { 67 // If the most significant bit (MSB, at position 0x80) 68 // is set, this is a copy instruction. Advance the 69 // position one byte backwards, and initialize variables 70 // for the copy offset and size instructions. 71 pos -= 1 72 73 var co, cs int 74 75 // The lower-half of "c" (0000 1111) defines a "bitmask" 76 // for the copy offset. 77 if c&0x1 != 0 { 78 pos += 1 79 co = int(delta[pos]) 80 } 81 if c&0x2 != 0 { 82 pos += 1 83 co |= (int(delta[pos]) << 8) 84 } 85 if c&0x4 != 0 { 86 pos += 1 87 co |= (int(delta[pos]) << 16) 88 } 89 if c&0x8 != 0 { 90 pos += 1 91 co |= (int(delta[pos]) << 24) 92 } 93 94 // The upper-half of "c" (1111 0000) defines a "bitmask" 95 // for the size of the copy instruction. 96 if c&0x10 != 0 { 97 pos += 1 98 cs = int(delta[pos]) 99 } 100 if c&0x20 != 0 { 101 pos += 1 102 cs |= (int(delta[pos]) << 8) 103 } 104 if c&0x40 != 0 { 105 pos += 1 106 cs |= (int(delta[pos]) << 16) 107 } 108 109 if cs == 0 { 110 // If the copy size is zero, we assume that it 111 // is the next whole number after the max uint32 112 // value. 113 cs = 0x10000 114 } 115 pos += 1 116 117 // Once we have the copy offset and length defined, copy 118 // that number of bytes from the base into the 119 // destination. Since we are copying from the base and 120 // not the delta, the position into the delta ("pos") 121 // need not be updated. 122 dest = append(dest, base[co:co+cs]...) 123 } else if c != 0 { 124 // If the most significant bit (MSB) is _not_ set, we 125 // instead process a copy instruction, where "c" is the 126 // number of successive bytes in the delta patch to add 127 // to the output. 128 // 129 // Copy the bytes and increment the read pointer 130 // forward. 131 dest = append(dest, delta[pos:int(pos)+c]...) 132 133 pos += int(c) 134 } else { 135 // Otherwise, "c" is 0, and is an invalid delta 136 // instruction. 137 // 138 // Return immediately. 139 return nil, errors.New( 140 "git/odb/pack: invalid delta data") 141 } 142 } 143 144 if destSize != int64(len(dest)) { 145 // If after patching the delta against the base, the destination 146 // size is different than the expected destination size, we have 147 // an invalid set of patch instructions. 148 // 149 // Return immediately. 150 return nil, errors.New("git/odb/pack: invalid delta data") 151 } 152 return dest, nil 153 } 154 155 // patchDeltaHeader examines the header within delta at the given offset, and 156 // returns the size encoded within it, as well as the ending offset where begins 157 // the next header, or the patch instructions. 158 func patchDeltaHeader(delta []byte, pos int) (size int64, end int) { 159 var shift uint 160 var c int64 161 162 for shift == 0 || c&0x80 != 0 { 163 if len(delta) <= pos { 164 panic("git/odb/pack: invalid delta header") 165 } 166 167 c = int64(delta[pos]) 168 169 pos++ 170 size |= (c & 0x7f) << shift 171 shift += 7 172 } 173 174 return size, pos 175 }