github.com/quay/claircore@v1.5.28/rpm/ndb/index.go (about) 1 package ndb 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 ) 9 10 // Index is an index over an RPM tag. 11 type Index struct { 12 // SlotSpace reads the slot section of the Index. 13 slotSpace *io.SectionReader 14 // KeySpace reads the key section of the Index. 15 keySpace *io.SectionReader 16 // HMask is the mask for hash keys. 17 hMask uint32 18 19 indexHeader 20 } 21 22 // IndexHeader is the header for a tag index. It's meant to be embedded. 23 type indexHeader struct { 24 Generation uint32 25 NSlots uint32 26 UsedSlots uint32 27 DummySlots uint32 28 XMask uint32 29 KeyEnd uint32 30 KeyExcess uint32 31 } 32 33 // UnmarshalBinary implements encoding.BinaryUnmarshaler for an Index header. 34 func (i *indexHeader) UnmarshalBinary(b []byte) error { 35 const ( 36 magic = ('R' | 'p'<<8 | 'm'<<16 | 'I'<<24) 37 version = 0 38 39 offsetMagic = 0 40 offsetVersion = 4 41 offsetGeneration = 8 42 offsetNSlots = 12 43 offsetUsedSlots = 16 44 offsetDummySlots = 20 45 offsetXMask = 24 46 offsetKeyEnd = 28 47 offsetKeyExcess = 32 48 offsetObsolete = 36 49 ) 50 if len(b) < 64 { 51 return io.ErrShortBuffer 52 } 53 if le.Uint32(b[offsetMagic:]) != magic { 54 return errors.New("ndb: index: bad magic") 55 } 56 if le.Uint32(b[offsetVersion:]) != version { 57 return errors.New("ndb: index: bad version") 58 } 59 i.Generation = le.Uint32(b[offsetGeneration:]) 60 i.NSlots = le.Uint32(b[offsetNSlots:]) 61 i.UsedSlots = le.Uint32(b[offsetUsedSlots:]) 62 i.DummySlots = le.Uint32(b[offsetDummySlots:]) 63 i.XMask = le.Uint32(b[offsetXMask:]) 64 i.KeyEnd = le.Uint32(b[offsetKeyEnd:]) 65 i.KeyExcess = le.Uint32(b[offsetKeyExcess:]) 66 // 4 bytes "obsolete" 67 // 24 bytes padding 68 return nil 69 } 70 71 // IndexPair is the package index and data offset. 72 type IndexPair struct { 73 Package uint32 74 Data uint32 75 } 76 77 // Lookup returns the pair (if any) for the provided key. 78 func (i *Index) Lookup(s string) (pg []IndexPair, err error) { 79 // NOTE(hank) This is a pretty straight forward port of the C version. 80 const ( 81 slotSize = 8 82 skip = ^uint32(0) 83 84 offsetKey = 0 85 offsetOffset = 4 86 ) 87 var keyoff, x uint32 88 keyh := murmur(s) 89 b := make([]byte, slotSize) 90 Look: 91 for h, hh := keyh&i.hMask, uint32(7); ; h, hh = (h+hh)&i.hMask, hh+1 { 92 off := int64(8 * h) 93 if _, err := i.slotSpace.ReadAt(b, off); err != nil { 94 return pg, fmt.Errorf("ndb: index: failed to read slot@0x%08x: %w", off, err) 95 } 96 x = le.Uint32(b) 97 switch { 98 case x == 0: 99 break Look 100 case x == skip: 101 continue 102 } 103 if keyoff == 0 { 104 switch { 105 case ((x ^ keyh) & i.XMask) != 0: 106 continue 107 case !i.equalkey(x & ^i.XMask, s): 108 continue 109 } 110 keyoff = x 111 } 112 if keyoff != x { 113 continue 114 } 115 data := le.Uint32(b[offsetOffset:]) 116 var ovldata uint32 117 // If flagged for overflow, read the overflow segment: 118 if data&0x80000000 != 0 { 119 off += 4 * int64(h) 120 if _, err := i.slotSpace.ReadAt(b[:4], off); err != nil { 121 return pg, fmt.Errorf("ndb: index: failed to read overflow slot@0x%08x: %w", off, err) 122 } 123 ovldata = le.Uint32(b) 124 } 125 pg = append(pg, i.decodeData(data, ovldata)) 126 } 127 return pg, nil 128 } 129 130 func (i *Index) equalkey(keyoff uint32, s string) bool { 131 if int64(keyoff)+int64(len(s))+1 > i.keySpace.Size() { 132 return false 133 } 134 l := len(s) 135 var b []byte 136 switch { 137 case l < 255: 138 b = make([]byte, 1+l) 139 case l < 65535: 140 b = make([]byte, 3+l) 141 default: 142 b = make([]byte, 7+l) 143 } 144 n, _ := i.keySpace.ReadAt(b, int64(keyoff)) 145 b = b[:n] 146 switch { 147 case l < 255: 148 if b[0] != uint8(l) { 149 return false 150 } 151 b = b[1:] 152 case l < 65535: 153 if b[0] != 255 || le.Uint16(b[:1]) != uint16(l) { 154 return false 155 } 156 b = b[3:] 157 default: 158 if b[0] != 255 || b[1] != 255 || b[2] != 255 || le.Uint32(b[3:]) != uint32(l) { 159 return false 160 } 161 b = b[7:] 162 } 163 return bytes.Equal([]byte(s), b) 164 } 165 166 func (i *Index) decodeData(data, ovldata uint32) (t IndexPair) { 167 switch { 168 case (data & 0x80000000) != 0: 169 t.Data = data ^ 0x80000000 170 t.Package = ovldata 171 case (data & 0x40000000) != 0: 172 t.Data = (data ^ 0x40000000) >> 24 173 t.Package = data & 0xffffff 174 default: 175 t.Data = data >> 20 176 t.Package = data & 0xfffff 177 } 178 return t 179 } 180 181 func (i *Index) encodeData(pkgIdx, datIdx uint32) (data, ovldata uint32) { 182 switch { 183 case (pkgIdx < 0x100000 && datIdx < 0x400): 184 ovldata = 0 185 data = pkgIdx | datIdx<<20 186 case (pkgIdx < 0x1000000 && datIdx < 0x40): 187 ovldata = 0 188 data = pkgIdx | datIdx<<24 | 0x40000000 189 default: 190 ovldata = pkgIdx 191 data = datIdx | 0x80000000 192 } 193 return data, ovldata 194 } 195 196 // Parse closes over the provided [io.ReaderAt] and populates the provided Index. 197 func (i *Index) Parse(r io.ReaderAt) error { 198 const ( 199 indexSlotOffset = 64 200 indexKeyChunksize = 4096 201 ) 202 b := make([]byte, indexSlotOffset) 203 if _, err := r.ReadAt(b, 0); err != nil { 204 return fmt.Errorf("ndb: index: unable to read bytes: %w", err) 205 } 206 if err := i.indexHeader.UnmarshalBinary(b); err != nil { 207 return fmt.Errorf("ndb: index: unable to unmarshal header: %w", err) 208 } 209 210 i.hMask = i.NSlots - 1 211 i.slotSpace = io.NewSectionReader(r, indexSlotOffset, int64(i.NSlots)*12) 212 i.keySpace = io.NewSectionReader(r, indexSlotOffset+(int64(i.NSlots)*12), int64(i.KeyEnd)) 213 214 return nil 215 }