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  }