// Copyright 2017 Pilosa Corp. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. 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 } } func (sc *sliceContainers) PutContainerValues(key uint64, typ byte, n int, mapped bool) { i := search64(sc.keys, key) if i < 0 { c := NewContainer() c.setTyp(typ) c.setN(int32(n)) c.setMapped(mapped) sc.insertAt(key, c, -i-1) } else { // if the container already exists, and is frozen, this may // result in copying its data, which is sort of pointless // because PutContainerValues almost always gets called // because we're reading new data from a file -- but also // that means this case probably never happens. c := sc.containers[i].Thaw() c.setTyp(typ) c.setN(int32(n)) c.setMapped(mapped) sc.containers[i] = c } } func (sc *sliceContainers) Remove(key uint64) { statsHit("sliceContainers/Remove") i := search64(sc.keys, key) if i < 0 { return } if key == sc.lastKey { sc.lastKey = ^uint64(0) sc.lastContainer = nil } 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 { 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.lastContainer = nil sc.lastKey = 0 } 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.lastContainer = nil sc.lastKey = 0 } 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}, found } func (sc *sliceContainers) Repair() { for _, c := range sc.containers { c.Repair() } } // 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-1) } } } // 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 } } } type sliceIterator struct { e *sliceContainers i int key uint64 value *Container } 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] si.i++ if si.value != nil { return true } } return false } func (si *sliceIterator) Value() (uint64, *Container) { return si.key, si.value }