// Copyright 2022 Molecula Corp. (DBA FeatureBase). // SPDX-License-Identifier: Apache-2.0 package roaring type sliceContainers struct { keys []uint64 containers []*Container lastKey uint64 lastContainer *Container } func newSliceContainers() *sliceContainers { return &sliceContainers{} } func (sc *sliceContainers) Get(key uint64) *Container { i := search64(sc.keys, key) if i < 0 { return nil } return sc.containers[i] } func (sc *sliceContainers) Put(key uint64, c *Container) { i := search64(sc.keys, key) // If index is negative then there's not an exact match // and a container needs to be added. if i < 0 { sc.insertAt(key, c, -i-1) } else { sc.containers[i] = c } sc.lastKey = key sc.lastContainer = c } func (sc *sliceContainers) Remove(key uint64) { statsHit("sliceContainers/Remove") i := search64(sc.keys, key) if i < 0 { return } if key == sc.lastKey { sc.invalidateCache() } sc.keys = append(sc.keys[:i], sc.keys[i+1:]...) sc.containers = append(sc.containers[:i], sc.containers[i+1:]...) } func (sc *sliceContainers) insertAt(key uint64, c *Container, i int) { statsHit("sliceContainers/insertAt") sc.keys = append(sc.keys, 0) copy(sc.keys[i+1:], sc.keys[i:]) sc.keys[i] = key sc.containers = append(sc.containers, nil) copy(sc.containers[i+1:], sc.containers[i:]) sc.containers[i] = c } func (sc *sliceContainers) GetOrCreate(key uint64) *Container { // Check the last* cache for same container. if key == sc.lastKey && sc.lastContainer != nil { return sc.lastContainer } sc.lastKey = key i := search64(sc.keys, key) if i < 0 { c := NewContainer() sc.insertAt(key, c, -i-1) sc.lastContainer = c return c } sc.lastContainer = sc.containers[i] return sc.lastContainer } func (sc *sliceContainers) Clone() Containers { other := newSliceContainers() other.keys = make([]uint64, len(sc.keys)) other.containers = make([]*Container, len(sc.containers)) copy(other.keys, sc.keys) for i, c := range sc.containers { if c == nil { other.containers[i] = nil continue } other.containers[i] = c.Clone() } return other } func (sc *sliceContainers) Freeze() Containers { other := newSliceContainers() other.keys = make([]uint64, len(sc.keys)) other.containers = make([]*Container, len(sc.containers)) copy(other.keys, sc.keys) for i, c := range sc.containers { other.containers[i] = c.Freeze() } return other } func (sc *sliceContainers) Last() (key uint64, c *Container) { if len(sc.keys) == 0 { return 0, nil } return sc.keys[len(sc.keys)-1], sc.containers[len(sc.keys)-1] } func (sc *sliceContainers) Size() int { return len(sc.keys) } func (sc *sliceContainers) Count() uint64 { n := uint64(0) for i := range sc.containers { n += uint64(sc.containers[i].N()) } return n } func (sc *sliceContainers) Reset() { sc.keys = sc.keys[:0] sc.containers = sc.containers[:0] sc.invalidateCache() } func (sc *sliceContainers) ResetN(n int) { if cap(sc.keys) < n { sc.keys = make([]uint64, 0, n) sc.containers = make([]*Container, 0, n) } else { sc.keys = sc.keys[:0] sc.containers = sc.containers[:0] } sc.invalidateCache() } func (sc *sliceContainers) seek(key uint64) (int, bool) { i := search64(sc.keys, key) found := true if i < 0 { found = false i = -i - 1 } return i, found } func (sc *sliceContainers) Iterator(key uint64) (citer ContainerIterator, found bool) { i, found := sc.seek(key) return &sliceIterator{e: sc, i: i, index: i}, found } // Repair tries to repair all containers, // results in nil and empty containers getting dropped from the slice. // For instance, that has to happen for writing the roaring format, // which can't represent an empty container (c.N() == 0). func (sc *sliceContainers) Repair() { n := 0 for i, c := range sc.containers { if c == nil { continue } c.Repair() sc.containers[n] = c sc.keys[n] = sc.keys[i] n++ } sc.containers = sc.containers[:n] sc.keys = sc.keys[:n] sc.invalidateCache() } // Update calls fn (existing-container, existed), and expects // (new-container, write). If write is true, the container is used to // replace the given container. func (sc *sliceContainers) Update(key uint64, fn func(*Container, bool) (*Container, bool)) { i, found := sc.seek(key) var nc *Container var write bool if found { nc, write = fn(sc.containers[i], true) if write { sc.containers[i] = nc } } else { nc, write = fn(nil, false) // don't expand the slice just to add a nil container, we // could return that anyway if write && nc != nil { sc.insertAt(key, nc, i) } } sc.invalidateCache() } // UpdateEvery calls fn (existing-container, existed), and expects // (new-container, write). If write is true, the container is used to // replace the given container. func (sc *sliceContainers) UpdateEvery(fn func(uint64, *Container, bool) (*Container, bool)) { for i, c := range sc.containers { nc, write := fn(sc.keys[i], c, true) if write { sc.containers[i] = nc } } sc.invalidateCache() } func (sc *sliceContainers) invalidateCache() { sc.lastKey = ^uint64(0) sc.lastContainer = nil } type sliceIterator struct { e *sliceContainers i int // next e's index to get key, value index int // current e's index of key, value key uint64 // current key value *Container // current value } func (si *sliceIterator) Close() {} func (si *sliceIterator) Next() bool { if si.e == nil { return false } // discard nil containers from iteration. we don't always // actually remove them because copying is expensive. for si.i < len(si.e.keys) { si.key = si.e.keys[si.i] si.value = si.e.containers[si.i] // keep the current index of key, value si.index = si.i si.i++ if si.value != nil { return true } } return false } func (si *sliceIterator) Value() (uint64, *Container) { return si.key, si.value }