mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Update to last common bleve (#3986)
This commit is contained in:
		
				
					committed by
					
						
						Lunny Xiao
					
				
			
			
				
	
			
			
			
						parent
						
							1b7cd3d0b0
						
					
				
				
					commit
					917b9641ec
				
			
							
								
								
									
										893
									
								
								vendor/github.com/RoaringBitmap/roaring/roaringarray.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										893
									
								
								vendor/github.com/RoaringBitmap/roaring/roaringarray.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,893 @@
 | 
			
		||||
package roaring
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
 | 
			
		||||
	snappy "github.com/glycerine/go-unsnap-stream"
 | 
			
		||||
	"github.com/tinylib/msgp/msgp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
//go:generate msgp -unexported
 | 
			
		||||
 | 
			
		||||
type container interface {
 | 
			
		||||
	clone() container
 | 
			
		||||
	and(container) container
 | 
			
		||||
	andCardinality(container) int
 | 
			
		||||
	iand(container) container // i stands for inplace
 | 
			
		||||
	andNot(container) container
 | 
			
		||||
	iandNot(container) container // i stands for inplace
 | 
			
		||||
	getCardinality() int
 | 
			
		||||
	// rank returns the number of integers that are
 | 
			
		||||
	// smaller or equal to x. rank(infinity) would be getCardinality().
 | 
			
		||||
	rank(uint16) int
 | 
			
		||||
 | 
			
		||||
	iadd(x uint16) bool                   // inplace, returns true if x was new.
 | 
			
		||||
	iaddReturnMinimized(uint16) container // may change return type to minimize storage.
 | 
			
		||||
 | 
			
		||||
	//addRange(start, final int) container  // range is [firstOfRange,lastOfRange) (unused)
 | 
			
		||||
	iaddRange(start, endx int) container // i stands for inplace, range is [firstOfRange,endx)
 | 
			
		||||
 | 
			
		||||
	iremove(x uint16) bool                   // inplace, returns true if x was present.
 | 
			
		||||
	iremoveReturnMinimized(uint16) container // may change return type to minimize storage.
 | 
			
		||||
 | 
			
		||||
	not(start, final int) container        // range is [firstOfRange,lastOfRange)
 | 
			
		||||
	inot(firstOfRange, endx int) container // i stands for inplace, range is [firstOfRange,endx)
 | 
			
		||||
	xor(r container) container
 | 
			
		||||
	getShortIterator() shortIterable
 | 
			
		||||
	getManyIterator() manyIterable
 | 
			
		||||
	contains(i uint16) bool
 | 
			
		||||
	maximum() uint16
 | 
			
		||||
	minimum() uint16
 | 
			
		||||
 | 
			
		||||
	// equals is now logical equals; it does not require the
 | 
			
		||||
	// same underlying container types, but compares across
 | 
			
		||||
	// any of the implementations.
 | 
			
		||||
	equals(r container) bool
 | 
			
		||||
 | 
			
		||||
	fillLeastSignificant16bits(array []uint32, i int, mask uint32)
 | 
			
		||||
	or(r container) container
 | 
			
		||||
	orCardinality(r container) int
 | 
			
		||||
	isFull() bool
 | 
			
		||||
	ior(r container) container   // i stands for inplace
 | 
			
		||||
	intersects(r container) bool // whether the two containers intersect
 | 
			
		||||
	lazyOR(r container) container
 | 
			
		||||
	lazyIOR(r container) container
 | 
			
		||||
	getSizeInBytes() int
 | 
			
		||||
	//removeRange(start, final int) container  // range is [firstOfRange,lastOfRange) (unused)
 | 
			
		||||
	iremoveRange(start, final int) container // i stands for inplace, range is [firstOfRange,lastOfRange)
 | 
			
		||||
	selectInt(x uint16) int                  // selectInt returns the xth integer in the container
 | 
			
		||||
	serializedSizeInBytes() int
 | 
			
		||||
	readFrom(io.Reader) (int, error)
 | 
			
		||||
	writeTo(io.Writer) (int, error)
 | 
			
		||||
 | 
			
		||||
	numberOfRuns() int
 | 
			
		||||
	toEfficientContainer() container
 | 
			
		||||
	String() string
 | 
			
		||||
	containerType() contype
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type contype uint8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	bitmapContype contype = iota
 | 
			
		||||
	arrayContype
 | 
			
		||||
	run16Contype
 | 
			
		||||
	run32Contype
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// careful: range is [firstOfRange,lastOfRange]
 | 
			
		||||
func rangeOfOnes(start, last int) container {
 | 
			
		||||
	if start > MaxUint16 {
 | 
			
		||||
		panic("rangeOfOnes called with start > MaxUint16")
 | 
			
		||||
	}
 | 
			
		||||
	if last > MaxUint16 {
 | 
			
		||||
		panic("rangeOfOnes called with last > MaxUint16")
 | 
			
		||||
	}
 | 
			
		||||
	if start < 0 {
 | 
			
		||||
		panic("rangeOfOnes called with start < 0")
 | 
			
		||||
	}
 | 
			
		||||
	if last < 0 {
 | 
			
		||||
		panic("rangeOfOnes called with last < 0")
 | 
			
		||||
	}
 | 
			
		||||
	return newRunContainer16Range(uint16(start), uint16(last))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type roaringArray struct {
 | 
			
		||||
	keys            []uint16
 | 
			
		||||
	containers      []container `msg:"-"` // don't try to serialize directly.
 | 
			
		||||
	needCopyOnWrite []bool
 | 
			
		||||
	copyOnWrite     bool
 | 
			
		||||
 | 
			
		||||
	// conserz is used at serialization time
 | 
			
		||||
	// to serialize containers. Otherwise empty.
 | 
			
		||||
	conserz []containerSerz
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// containerSerz facilitates serializing container (tricky to
 | 
			
		||||
// serialize because it is an interface) by providing a
 | 
			
		||||
// light wrapper with a type identifier.
 | 
			
		||||
type containerSerz struct {
 | 
			
		||||
	t contype  `msg:"t"` // type
 | 
			
		||||
	r msgp.Raw `msg:"r"` // Raw msgpack of the actual container type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newRoaringArray() *roaringArray {
 | 
			
		||||
	return &roaringArray{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// runOptimize compresses the element containers to minimize space consumed.
 | 
			
		||||
// Q: how does this interact with copyOnWrite and needCopyOnWrite?
 | 
			
		||||
// A: since we aren't changing the logical content, just the representation,
 | 
			
		||||
//    we don't bother to check the needCopyOnWrite bits. We replace
 | 
			
		||||
//    (possibly all) elements of ra.containers in-place with space
 | 
			
		||||
//    optimized versions.
 | 
			
		||||
func (ra *roaringArray) runOptimize() {
 | 
			
		||||
	for i := range ra.containers {
 | 
			
		||||
		ra.containers[i] = ra.containers[i].toEfficientContainer()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) appendContainer(key uint16, value container, mustCopyOnWrite bool) {
 | 
			
		||||
	ra.keys = append(ra.keys, key)
 | 
			
		||||
	ra.containers = append(ra.containers, value)
 | 
			
		||||
	ra.needCopyOnWrite = append(ra.needCopyOnWrite, mustCopyOnWrite)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) appendWithoutCopy(sa roaringArray, startingindex int) {
 | 
			
		||||
	mustCopyOnWrite := sa.needCopyOnWrite[startingindex]
 | 
			
		||||
	ra.appendContainer(sa.keys[startingindex], sa.containers[startingindex], mustCopyOnWrite)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) appendCopy(sa roaringArray, startingindex int) {
 | 
			
		||||
	// cow only if the two request it, or if we already have a lightweight copy
 | 
			
		||||
	copyonwrite := (ra.copyOnWrite && sa.copyOnWrite) || sa.needsCopyOnWrite(startingindex)
 | 
			
		||||
	if !copyonwrite {
 | 
			
		||||
		// since there is no copy-on-write, we need to clone the container (this is important)
 | 
			
		||||
		ra.appendContainer(sa.keys[startingindex], sa.containers[startingindex].clone(), copyonwrite)
 | 
			
		||||
	} else {
 | 
			
		||||
		ra.appendContainer(sa.keys[startingindex], sa.containers[startingindex], copyonwrite)
 | 
			
		||||
		if !sa.needsCopyOnWrite(startingindex) {
 | 
			
		||||
			sa.setNeedsCopyOnWrite(startingindex)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) appendWithoutCopyMany(sa roaringArray, startingindex, end int) {
 | 
			
		||||
	for i := startingindex; i < end; i++ {
 | 
			
		||||
		ra.appendWithoutCopy(sa, i)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) appendCopyMany(sa roaringArray, startingindex, end int) {
 | 
			
		||||
	for i := startingindex; i < end; i++ {
 | 
			
		||||
		ra.appendCopy(sa, i)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) appendCopiesUntil(sa roaringArray, stoppingKey uint16) {
 | 
			
		||||
	// cow only if the two request it, or if we already have a lightweight copy
 | 
			
		||||
	copyonwrite := ra.copyOnWrite && sa.copyOnWrite
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < sa.size(); i++ {
 | 
			
		||||
		if sa.keys[i] >= stoppingKey {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		thiscopyonewrite := copyonwrite || sa.needsCopyOnWrite(i)
 | 
			
		||||
		if thiscopyonewrite {
 | 
			
		||||
			ra.appendContainer(sa.keys[i], sa.containers[i], thiscopyonewrite)
 | 
			
		||||
			if !sa.needsCopyOnWrite(i) {
 | 
			
		||||
				sa.setNeedsCopyOnWrite(i)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else {
 | 
			
		||||
			// since there is no copy-on-write, we need to clone the container (this is important)
 | 
			
		||||
			ra.appendContainer(sa.keys[i], sa.containers[i].clone(), thiscopyonewrite)
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) appendCopiesAfter(sa roaringArray, beforeStart uint16) {
 | 
			
		||||
	// cow only if the two request it, or if we already have a lightweight copy
 | 
			
		||||
	copyonwrite := ra.copyOnWrite && sa.copyOnWrite
 | 
			
		||||
 | 
			
		||||
	startLocation := sa.getIndex(beforeStart)
 | 
			
		||||
	if startLocation >= 0 {
 | 
			
		||||
		startLocation++
 | 
			
		||||
	} else {
 | 
			
		||||
		startLocation = -startLocation - 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := startLocation; i < sa.size(); i++ {
 | 
			
		||||
		thiscopyonewrite := copyonwrite || sa.needsCopyOnWrite(i)
 | 
			
		||||
		if thiscopyonewrite {
 | 
			
		||||
			ra.appendContainer(sa.keys[i], sa.containers[i], thiscopyonewrite)
 | 
			
		||||
			if !sa.needsCopyOnWrite(i) {
 | 
			
		||||
				sa.setNeedsCopyOnWrite(i)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// since there is no copy-on-write, we need to clone the container (this is important)
 | 
			
		||||
			ra.appendContainer(sa.keys[i], sa.containers[i].clone(), thiscopyonewrite)
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) removeIndexRange(begin, end int) {
 | 
			
		||||
	if end <= begin {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := end - begin
 | 
			
		||||
 | 
			
		||||
	copy(ra.keys[begin:], ra.keys[end:])
 | 
			
		||||
	copy(ra.containers[begin:], ra.containers[end:])
 | 
			
		||||
	copy(ra.needCopyOnWrite[begin:], ra.needCopyOnWrite[end:])
 | 
			
		||||
 | 
			
		||||
	ra.resize(len(ra.keys) - r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) resize(newsize int) {
 | 
			
		||||
	for k := newsize; k < len(ra.containers); k++ {
 | 
			
		||||
		ra.containers[k] = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ra.keys = ra.keys[:newsize]
 | 
			
		||||
	ra.containers = ra.containers[:newsize]
 | 
			
		||||
	ra.needCopyOnWrite = ra.needCopyOnWrite[:newsize]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) clear() {
 | 
			
		||||
	ra.resize(0)
 | 
			
		||||
	ra.copyOnWrite = false
 | 
			
		||||
	ra.conserz = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) clone() *roaringArray {
 | 
			
		||||
 | 
			
		||||
	sa := roaringArray{}
 | 
			
		||||
	sa.copyOnWrite = ra.copyOnWrite
 | 
			
		||||
 | 
			
		||||
	// this is where copyOnWrite is used.
 | 
			
		||||
	if ra.copyOnWrite {
 | 
			
		||||
		sa.keys = make([]uint16, len(ra.keys))
 | 
			
		||||
		copy(sa.keys, ra.keys)
 | 
			
		||||
		sa.containers = make([]container, len(ra.containers))
 | 
			
		||||
		copy(sa.containers, ra.containers)
 | 
			
		||||
		sa.needCopyOnWrite = make([]bool, len(ra.needCopyOnWrite))
 | 
			
		||||
 | 
			
		||||
		ra.markAllAsNeedingCopyOnWrite()
 | 
			
		||||
		sa.markAllAsNeedingCopyOnWrite()
 | 
			
		||||
 | 
			
		||||
		// sa.needCopyOnWrite is shared
 | 
			
		||||
	} else {
 | 
			
		||||
		// make a full copy
 | 
			
		||||
 | 
			
		||||
		sa.keys = make([]uint16, len(ra.keys))
 | 
			
		||||
		copy(sa.keys, ra.keys)
 | 
			
		||||
 | 
			
		||||
		sa.containers = make([]container, len(ra.containers))
 | 
			
		||||
		for i := range sa.containers {
 | 
			
		||||
			sa.containers[i] = ra.containers[i].clone()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sa.needCopyOnWrite = make([]bool, len(ra.needCopyOnWrite))
 | 
			
		||||
	}
 | 
			
		||||
	return &sa
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unused function:
 | 
			
		||||
//func (ra *roaringArray) containsKey(x uint16) bool {
 | 
			
		||||
//	return (ra.binarySearch(0, int64(len(ra.keys)), x) >= 0)
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) getContainer(x uint16) container {
 | 
			
		||||
	i := ra.binarySearch(0, int64(len(ra.keys)), x)
 | 
			
		||||
	if i < 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return ra.containers[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) getContainerAtIndex(i int) container {
 | 
			
		||||
	return ra.containers[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) getFastContainerAtIndex(i int, needsWriteable bool) container {
 | 
			
		||||
	c := ra.getContainerAtIndex(i)
 | 
			
		||||
	switch t := c.(type) {
 | 
			
		||||
	case *arrayContainer:
 | 
			
		||||
		c = t.toBitmapContainer()
 | 
			
		||||
	case *runContainer16:
 | 
			
		||||
		if !t.isFull() {
 | 
			
		||||
			c = t.toBitmapContainer()
 | 
			
		||||
		}
 | 
			
		||||
	case *bitmapContainer:
 | 
			
		||||
		if needsWriteable && ra.needCopyOnWrite[i] {
 | 
			
		||||
			c = ra.containers[i].clone()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) getWritableContainerAtIndex(i int) container {
 | 
			
		||||
	if ra.needCopyOnWrite[i] {
 | 
			
		||||
		ra.containers[i] = ra.containers[i].clone()
 | 
			
		||||
		ra.needCopyOnWrite[i] = false
 | 
			
		||||
	}
 | 
			
		||||
	return ra.containers[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) getIndex(x uint16) int {
 | 
			
		||||
	// before the binary search, we optimize for frequent cases
 | 
			
		||||
	size := len(ra.keys)
 | 
			
		||||
	if (size == 0) || (ra.keys[size-1] == x) {
 | 
			
		||||
		return size - 1
 | 
			
		||||
	}
 | 
			
		||||
	return ra.binarySearch(0, int64(size), x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) getKeyAtIndex(i int) uint16 {
 | 
			
		||||
	return ra.keys[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) insertNewKeyValueAt(i int, key uint16, value container) {
 | 
			
		||||
	ra.keys = append(ra.keys, 0)
 | 
			
		||||
	ra.containers = append(ra.containers, nil)
 | 
			
		||||
 | 
			
		||||
	copy(ra.keys[i+1:], ra.keys[i:])
 | 
			
		||||
	copy(ra.containers[i+1:], ra.containers[i:])
 | 
			
		||||
 | 
			
		||||
	ra.keys[i] = key
 | 
			
		||||
	ra.containers[i] = value
 | 
			
		||||
 | 
			
		||||
	ra.needCopyOnWrite = append(ra.needCopyOnWrite, false)
 | 
			
		||||
	copy(ra.needCopyOnWrite[i+1:], ra.needCopyOnWrite[i:])
 | 
			
		||||
	ra.needCopyOnWrite[i] = false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) remove(key uint16) bool {
 | 
			
		||||
	i := ra.binarySearch(0, int64(len(ra.keys)), key)
 | 
			
		||||
	if i >= 0 { // if a new key
 | 
			
		||||
		ra.removeAtIndex(i)
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) removeAtIndex(i int) {
 | 
			
		||||
	copy(ra.keys[i:], ra.keys[i+1:])
 | 
			
		||||
	copy(ra.containers[i:], ra.containers[i+1:])
 | 
			
		||||
 | 
			
		||||
	copy(ra.needCopyOnWrite[i:], ra.needCopyOnWrite[i+1:])
 | 
			
		||||
 | 
			
		||||
	ra.resize(len(ra.keys) - 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) setContainerAtIndex(i int, c container) {
 | 
			
		||||
	ra.containers[i] = c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) replaceKeyAndContainerAtIndex(i int, key uint16, c container, mustCopyOnWrite bool) {
 | 
			
		||||
	ra.keys[i] = key
 | 
			
		||||
	ra.containers[i] = c
 | 
			
		||||
	ra.needCopyOnWrite[i] = mustCopyOnWrite
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) size() int {
 | 
			
		||||
	return len(ra.keys)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) binarySearch(begin, end int64, ikey uint16) int {
 | 
			
		||||
	low := begin
 | 
			
		||||
	high := end - 1
 | 
			
		||||
	for low+16 <= high {
 | 
			
		||||
		middleIndex := low + (high-low)/2 // avoid overflow
 | 
			
		||||
		middleValue := ra.keys[middleIndex]
 | 
			
		||||
 | 
			
		||||
		if middleValue < ikey {
 | 
			
		||||
			low = middleIndex + 1
 | 
			
		||||
		} else if middleValue > ikey {
 | 
			
		||||
			high = middleIndex - 1
 | 
			
		||||
		} else {
 | 
			
		||||
			return int(middleIndex)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for ; low <= high; low++ {
 | 
			
		||||
		val := ra.keys[low]
 | 
			
		||||
		if val >= ikey {
 | 
			
		||||
			if val == ikey {
 | 
			
		||||
				return int(low)
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return -int(low + 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) equals(o interface{}) bool {
 | 
			
		||||
	srb, ok := o.(roaringArray)
 | 
			
		||||
	if ok {
 | 
			
		||||
 | 
			
		||||
		if srb.size() != ra.size() {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		for i, k := range ra.keys {
 | 
			
		||||
			if k != srb.keys[i] {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for i, c := range ra.containers {
 | 
			
		||||
			if !c.equals(srb.containers[i]) {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) headerSize() uint64 {
 | 
			
		||||
	size := uint64(len(ra.keys))
 | 
			
		||||
	if ra.hasRunCompression() {
 | 
			
		||||
		if size < noOffsetThreshold { // for small bitmaps, we omit the offsets
 | 
			
		||||
			return 4 + (size+7)/8 + 4*size
 | 
			
		||||
		}
 | 
			
		||||
		return 4 + (size+7)/8 + 8*size // - 4 because we pack the size with the cookie
 | 
			
		||||
	}
 | 
			
		||||
	return 4 + 4 + 8*size
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// should be dirt cheap
 | 
			
		||||
func (ra *roaringArray) serializedSizeInBytes() uint64 {
 | 
			
		||||
	answer := ra.headerSize()
 | 
			
		||||
	for _, c := range ra.containers {
 | 
			
		||||
		answer += uint64(c.serializedSizeInBytes())
 | 
			
		||||
	}
 | 
			
		||||
	return answer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// spec: https://github.com/RoaringBitmap/RoaringFormatSpec
 | 
			
		||||
//
 | 
			
		||||
func (ra *roaringArray) toBytes() ([]byte, error) {
 | 
			
		||||
	stream := &bytes.Buffer{}
 | 
			
		||||
	hasRun := ra.hasRunCompression()
 | 
			
		||||
	isRunSizeInBytes := 0
 | 
			
		||||
	cookieSize := 8
 | 
			
		||||
	if hasRun {
 | 
			
		||||
		cookieSize = 4
 | 
			
		||||
		isRunSizeInBytes = (len(ra.keys) + 7) / 8
 | 
			
		||||
	}
 | 
			
		||||
	descriptiveHeaderSize := 4 * len(ra.keys)
 | 
			
		||||
	preambleSize := cookieSize + isRunSizeInBytes + descriptiveHeaderSize
 | 
			
		||||
 | 
			
		||||
	buf := make([]byte, preambleSize+4*len(ra.keys))
 | 
			
		||||
 | 
			
		||||
	nw := 0
 | 
			
		||||
 | 
			
		||||
	if hasRun {
 | 
			
		||||
		binary.LittleEndian.PutUint16(buf[0:], uint16(serialCookie))
 | 
			
		||||
		nw += 2
 | 
			
		||||
		binary.LittleEndian.PutUint16(buf[2:], uint16(len(ra.keys)-1))
 | 
			
		||||
		nw += 2
 | 
			
		||||
 | 
			
		||||
		// compute isRun bitmap
 | 
			
		||||
		var ir []byte
 | 
			
		||||
 | 
			
		||||
		isRun := newBitmapContainer()
 | 
			
		||||
		for i, c := range ra.containers {
 | 
			
		||||
			switch c.(type) {
 | 
			
		||||
			case *runContainer16:
 | 
			
		||||
				isRun.iadd(uint16(i))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// convert to little endian
 | 
			
		||||
		ir = isRun.asLittleEndianByteSlice()[:isRunSizeInBytes]
 | 
			
		||||
		nw += copy(buf[nw:], ir)
 | 
			
		||||
	} else {
 | 
			
		||||
		binary.LittleEndian.PutUint32(buf[0:], uint32(serialCookieNoRunContainer))
 | 
			
		||||
		nw += 4
 | 
			
		||||
		binary.LittleEndian.PutUint32(buf[4:], uint32(len(ra.keys)))
 | 
			
		||||
		nw += 4
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// descriptive header
 | 
			
		||||
	for i, key := range ra.keys {
 | 
			
		||||
		binary.LittleEndian.PutUint16(buf[nw:], key)
 | 
			
		||||
		nw += 2
 | 
			
		||||
		c := ra.containers[i]
 | 
			
		||||
		binary.LittleEndian.PutUint16(buf[nw:], uint16(c.getCardinality()-1))
 | 
			
		||||
		nw += 2
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	startOffset := int64(preambleSize + 4*len(ra.keys))
 | 
			
		||||
	if !hasRun || (len(ra.keys) >= noOffsetThreshold) {
 | 
			
		||||
		// offset header
 | 
			
		||||
		for _, c := range ra.containers {
 | 
			
		||||
			binary.LittleEndian.PutUint32(buf[nw:], uint32(startOffset))
 | 
			
		||||
			nw += 4
 | 
			
		||||
			switch rc := c.(type) {
 | 
			
		||||
			case *runContainer16:
 | 
			
		||||
				startOffset += 2 + int64(len(rc.iv))*4
 | 
			
		||||
			default:
 | 
			
		||||
				startOffset += int64(getSizeInBytesFromCardinality(c.getCardinality()))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := stream.Write(buf[:nw])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	for i, c := range ra.containers {
 | 
			
		||||
		_ = i
 | 
			
		||||
		_, err := c.writeTo(stream)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return stream.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// spec: https://github.com/RoaringBitmap/RoaringFormatSpec
 | 
			
		||||
//
 | 
			
		||||
func (ra *roaringArray) writeTo(out io.Writer) (int64, error) {
 | 
			
		||||
	by, err := ra.toBytes()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	n, err := out.Write(by)
 | 
			
		||||
	if err == nil && n < len(by) {
 | 
			
		||||
		err = io.ErrShortWrite
 | 
			
		||||
	}
 | 
			
		||||
	return int64(n), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) fromBuffer(buf []byte) (int64, error) {
 | 
			
		||||
	pos := 0
 | 
			
		||||
	if len(buf) < 8 {
 | 
			
		||||
		return 0, fmt.Errorf("buffer too small, expecting at least 8 bytes, was %d", len(buf))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cookie := binary.LittleEndian.Uint32(buf)
 | 
			
		||||
	pos += 4
 | 
			
		||||
	var size uint32 // number of containers
 | 
			
		||||
	haveRunContainers := false
 | 
			
		||||
	var isRunBitmap []byte
 | 
			
		||||
 | 
			
		||||
	// cookie header
 | 
			
		||||
	if cookie&0x0000FFFF == serialCookie {
 | 
			
		||||
		haveRunContainers = true
 | 
			
		||||
		size = uint32(uint16(cookie>>16) + 1) // number of containers
 | 
			
		||||
 | 
			
		||||
		// create is-run-container bitmap
 | 
			
		||||
		isRunBitmapSize := (int(size) + 7) / 8
 | 
			
		||||
		if pos+isRunBitmapSize > len(buf) {
 | 
			
		||||
			return 0, fmt.Errorf("malformed bitmap, is-run bitmap overruns buffer at %d", pos+isRunBitmapSize)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		isRunBitmap = buf[pos : pos+isRunBitmapSize]
 | 
			
		||||
		pos += isRunBitmapSize
 | 
			
		||||
	} else if cookie == serialCookieNoRunContainer {
 | 
			
		||||
		size = binary.LittleEndian.Uint32(buf[pos:])
 | 
			
		||||
		pos += 4
 | 
			
		||||
	} else {
 | 
			
		||||
		return 0, fmt.Errorf("error in roaringArray.readFrom: did not find expected serialCookie in header")
 | 
			
		||||
	}
 | 
			
		||||
	if size > (1 << 16) {
 | 
			
		||||
		return 0, fmt.Errorf("It is logically impossible to have more than (1<<16) containers.")
 | 
			
		||||
	}
 | 
			
		||||
	// descriptive header
 | 
			
		||||
	// keycard - is {key, cardinality} tuple slice
 | 
			
		||||
	if pos+2*2*int(size) > len(buf) {
 | 
			
		||||
		return 0, fmt.Errorf("malfomred bitmap, key-cardinality slice overruns buffer at %d", pos+2*2*int(size))
 | 
			
		||||
	}
 | 
			
		||||
	keycard := byteSliceAsUint16Slice(buf[pos : pos+2*2*int(size)])
 | 
			
		||||
	pos += 2 * 2 * int(size)
 | 
			
		||||
 | 
			
		||||
	if !haveRunContainers || size >= noOffsetThreshold {
 | 
			
		||||
		pos += 4 * int(size)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Allocate slices upfront as number of containers is known
 | 
			
		||||
	if cap(ra.containers) >= int(size) {
 | 
			
		||||
		ra.containers = ra.containers[:size]
 | 
			
		||||
	} else {
 | 
			
		||||
		ra.containers = make([]container, size)
 | 
			
		||||
	}
 | 
			
		||||
	if cap(ra.keys) >= int(size) {
 | 
			
		||||
		ra.keys = ra.keys[:size]
 | 
			
		||||
	} else {
 | 
			
		||||
		ra.keys = make([]uint16, size)
 | 
			
		||||
	}
 | 
			
		||||
	if cap(ra.needCopyOnWrite) >= int(size) {
 | 
			
		||||
		ra.needCopyOnWrite = ra.needCopyOnWrite[:size]
 | 
			
		||||
	} else {
 | 
			
		||||
		ra.needCopyOnWrite = make([]bool, size)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := uint32(0); i < size; i++ {
 | 
			
		||||
		key := uint16(keycard[2*i])
 | 
			
		||||
		card := int(keycard[2*i+1]) + 1
 | 
			
		||||
		ra.keys[i] = key
 | 
			
		||||
		ra.needCopyOnWrite[i] = true
 | 
			
		||||
 | 
			
		||||
		if haveRunContainers && isRunBitmap[i/8]&(1<<(i%8)) != 0 {
 | 
			
		||||
			// run container
 | 
			
		||||
			nr := binary.LittleEndian.Uint16(buf[pos:])
 | 
			
		||||
			pos += 2
 | 
			
		||||
			if pos+int(nr)*4 > len(buf) {
 | 
			
		||||
				return 0, fmt.Errorf("malformed bitmap, a run container overruns buffer at %d:%d", pos, pos+int(nr)*4)
 | 
			
		||||
			}
 | 
			
		||||
			nb := runContainer16{
 | 
			
		||||
				iv:   byteSliceAsInterval16Slice(buf[pos : pos+int(nr)*4]),
 | 
			
		||||
				card: int64(card),
 | 
			
		||||
			}
 | 
			
		||||
			pos += int(nr) * 4
 | 
			
		||||
			ra.containers[i] = &nb
 | 
			
		||||
		} else if card > arrayDefaultMaxSize {
 | 
			
		||||
			// bitmap container
 | 
			
		||||
			nb := bitmapContainer{
 | 
			
		||||
				cardinality: card,
 | 
			
		||||
				bitmap:      byteSliceAsUint64Slice(buf[pos : pos+arrayDefaultMaxSize*2]),
 | 
			
		||||
			}
 | 
			
		||||
			pos += arrayDefaultMaxSize * 2
 | 
			
		||||
			ra.containers[i] = &nb
 | 
			
		||||
		} else {
 | 
			
		||||
			// array container
 | 
			
		||||
			nb := arrayContainer{
 | 
			
		||||
				byteSliceAsUint16Slice(buf[pos : pos+card*2]),
 | 
			
		||||
			}
 | 
			
		||||
			pos += card * 2
 | 
			
		||||
			ra.containers[i] = &nb
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return int64(pos), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) readFrom(stream io.Reader) (int64, error) {
 | 
			
		||||
	pos := 0
 | 
			
		||||
	var cookie uint32
 | 
			
		||||
	err := binary.Read(stream, binary.LittleEndian, &cookie)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, fmt.Errorf("error in roaringArray.readFrom: could not read initial cookie: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	pos += 4
 | 
			
		||||
	var size uint32
 | 
			
		||||
	haveRunContainers := false
 | 
			
		||||
	var isRun *bitmapContainer
 | 
			
		||||
	if cookie&0x0000FFFF == serialCookie {
 | 
			
		||||
		haveRunContainers = true
 | 
			
		||||
		size = uint32(uint16(cookie>>16) + 1)
 | 
			
		||||
		bytesToRead := (int(size) + 7) / 8
 | 
			
		||||
		numwords := (bytesToRead + 7) / 8
 | 
			
		||||
		by := make([]byte, bytesToRead, numwords*8)
 | 
			
		||||
		nr, err := io.ReadFull(stream, by)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 8 + int64(nr), fmt.Errorf("error in readFrom: could not read the "+
 | 
			
		||||
				"runContainer bit flags of length %v bytes: %v", bytesToRead, err)
 | 
			
		||||
		}
 | 
			
		||||
		pos += bytesToRead
 | 
			
		||||
		by = by[:cap(by)]
 | 
			
		||||
		isRun = newBitmapContainer()
 | 
			
		||||
		for i := 0; i < numwords; i++ {
 | 
			
		||||
			isRun.bitmap[i] = binary.LittleEndian.Uint64(by)
 | 
			
		||||
			by = by[8:]
 | 
			
		||||
		}
 | 
			
		||||
	} else if cookie == serialCookieNoRunContainer {
 | 
			
		||||
		err = binary.Read(stream, binary.LittleEndian, &size)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, fmt.Errorf("error in roaringArray.readFrom: when reading size, got: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		pos += 4
 | 
			
		||||
	} else {
 | 
			
		||||
		return 0, fmt.Errorf("error in roaringArray.readFrom: did not find expected serialCookie in header")
 | 
			
		||||
	}
 | 
			
		||||
	if size > (1 << 16) {
 | 
			
		||||
		return 0, fmt.Errorf("It is logically impossible to have more than (1<<16) containers.")
 | 
			
		||||
	}
 | 
			
		||||
	// descriptive header
 | 
			
		||||
	keycard := make([]uint16, 2*size, 2*size)
 | 
			
		||||
	err = binary.Read(stream, binary.LittleEndian, keycard)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	pos += 2 * 2 * int(size)
 | 
			
		||||
	// offset header
 | 
			
		||||
	if !haveRunContainers || size >= noOffsetThreshold {
 | 
			
		||||
		io.CopyN(ioutil.Discard, stream, 4*int64(size)) // we never skip ahead so this data can be ignored
 | 
			
		||||
		pos += 4 * int(size)
 | 
			
		||||
	}
 | 
			
		||||
	for i := uint32(0); i < size; i++ {
 | 
			
		||||
		key := int(keycard[2*i])
 | 
			
		||||
		card := int(keycard[2*i+1]) + 1
 | 
			
		||||
		if haveRunContainers && isRun.contains(uint16(i)) {
 | 
			
		||||
			nb := newRunContainer16()
 | 
			
		||||
			nr, err := nb.readFrom(stream)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
			pos += nr
 | 
			
		||||
			ra.appendContainer(uint16(key), nb, false)
 | 
			
		||||
		} else if card > arrayDefaultMaxSize {
 | 
			
		||||
			nb := newBitmapContainer()
 | 
			
		||||
			nr, err := nb.readFrom(stream)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
			nb.cardinality = card
 | 
			
		||||
			pos += nr
 | 
			
		||||
			ra.appendContainer(keycard[2*i], nb, false)
 | 
			
		||||
		} else {
 | 
			
		||||
			nb := newArrayContainerSize(card)
 | 
			
		||||
			nr, err := nb.readFrom(stream)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
			pos += nr
 | 
			
		||||
			ra.appendContainer(keycard[2*i], nb, false)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return int64(pos), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) hasRunCompression() bool {
 | 
			
		||||
	for _, c := range ra.containers {
 | 
			
		||||
		switch c.(type) {
 | 
			
		||||
		case *runContainer16:
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) writeToMsgpack(stream io.Writer) error {
 | 
			
		||||
 | 
			
		||||
	ra.conserz = make([]containerSerz, len(ra.containers))
 | 
			
		||||
	for i, v := range ra.containers {
 | 
			
		||||
		switch cn := v.(type) {
 | 
			
		||||
		case *bitmapContainer:
 | 
			
		||||
			bts, err := cn.MarshalMsg(nil)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			ra.conserz[i].t = bitmapContype
 | 
			
		||||
			ra.conserz[i].r = bts
 | 
			
		||||
		case *arrayContainer:
 | 
			
		||||
			bts, err := cn.MarshalMsg(nil)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			ra.conserz[i].t = arrayContype
 | 
			
		||||
			ra.conserz[i].r = bts
 | 
			
		||||
		case *runContainer16:
 | 
			
		||||
			bts, err := cn.MarshalMsg(nil)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			ra.conserz[i].t = run16Contype
 | 
			
		||||
			ra.conserz[i].r = bts
 | 
			
		||||
		default:
 | 
			
		||||
			panic(fmt.Errorf("Unrecognized container implementation: %T", cn))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	w := snappy.NewWriter(stream)
 | 
			
		||||
	err := msgp.Encode(w, ra)
 | 
			
		||||
	ra.conserz = nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) readFromMsgpack(stream io.Reader) error {
 | 
			
		||||
	r := snappy.NewReader(stream)
 | 
			
		||||
	err := msgp.Decode(r, ra)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(ra.containers) != len(ra.keys) {
 | 
			
		||||
		ra.containers = make([]container, len(ra.keys))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, v := range ra.conserz {
 | 
			
		||||
		switch v.t {
 | 
			
		||||
		case bitmapContype:
 | 
			
		||||
			c := &bitmapContainer{}
 | 
			
		||||
			_, err = c.UnmarshalMsg(v.r)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			ra.containers[i] = c
 | 
			
		||||
		case arrayContype:
 | 
			
		||||
			c := &arrayContainer{}
 | 
			
		||||
			_, err = c.UnmarshalMsg(v.r)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			ra.containers[i] = c
 | 
			
		||||
		case run16Contype:
 | 
			
		||||
			c := &runContainer16{}
 | 
			
		||||
			_, err = c.UnmarshalMsg(v.r)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			ra.containers[i] = c
 | 
			
		||||
		default:
 | 
			
		||||
			return fmt.Errorf("unrecognized contype serialization code: '%v'", v.t)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ra.conserz = nil
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) advanceUntil(min uint16, pos int) int {
 | 
			
		||||
	lower := pos + 1
 | 
			
		||||
 | 
			
		||||
	if lower >= len(ra.keys) || ra.keys[lower] >= min {
 | 
			
		||||
		return lower
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spansize := 1
 | 
			
		||||
 | 
			
		||||
	for lower+spansize < len(ra.keys) && ra.keys[lower+spansize] < min {
 | 
			
		||||
		spansize *= 2
 | 
			
		||||
	}
 | 
			
		||||
	var upper int
 | 
			
		||||
	if lower+spansize < len(ra.keys) {
 | 
			
		||||
		upper = lower + spansize
 | 
			
		||||
	} else {
 | 
			
		||||
		upper = len(ra.keys) - 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ra.keys[upper] == min {
 | 
			
		||||
		return upper
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ra.keys[upper] < min {
 | 
			
		||||
		// means
 | 
			
		||||
		// array
 | 
			
		||||
		// has no
 | 
			
		||||
		// item
 | 
			
		||||
		// >= min
 | 
			
		||||
		// pos = array.length;
 | 
			
		||||
		return len(ra.keys)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// we know that the next-smallest span was too small
 | 
			
		||||
	lower += (spansize >> 1)
 | 
			
		||||
 | 
			
		||||
	mid := 0
 | 
			
		||||
	for lower+1 != upper {
 | 
			
		||||
		mid = (lower + upper) >> 1
 | 
			
		||||
		if ra.keys[mid] == min {
 | 
			
		||||
			return mid
 | 
			
		||||
		} else if ra.keys[mid] < min {
 | 
			
		||||
			lower = mid
 | 
			
		||||
		} else {
 | 
			
		||||
			upper = mid
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return upper
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) markAllAsNeedingCopyOnWrite() {
 | 
			
		||||
	for i := range ra.needCopyOnWrite {
 | 
			
		||||
		ra.needCopyOnWrite[i] = true
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) needsCopyOnWrite(i int) bool {
 | 
			
		||||
	return ra.needCopyOnWrite[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *roaringArray) setNeedsCopyOnWrite(i int) {
 | 
			
		||||
	ra.needCopyOnWrite[i] = true
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user