diff options
Diffstat (limited to 'vendor/github.com/RoaringBitmap/roaring/serialization_littleendian.go')
-rw-r--r-- | vendor/github.com/RoaringBitmap/roaring/serialization_littleendian.go | 99 |
1 files changed, 60 insertions, 39 deletions
diff --git a/vendor/github.com/RoaringBitmap/roaring/serialization_littleendian.go b/vendor/github.com/RoaringBitmap/roaring/serialization_littleendian.go index c1d3ad3046..818a06c80b 100644 --- a/vendor/github.com/RoaringBitmap/roaring/serialization_littleendian.go +++ b/vendor/github.com/RoaringBitmap/roaring/serialization_littleendian.go @@ -3,8 +3,10 @@ package roaring import ( + "errors" "io" "reflect" + "runtime" "unsafe" ) @@ -14,26 +16,13 @@ func (ac *arrayContainer) writeTo(stream io.Writer) (int, error) { } func (bc *bitmapContainer) writeTo(stream io.Writer) (int, error) { + if bc.cardinality <= arrayDefaultMaxSize { + return 0, errors.New("refusing to write bitmap container with cardinality of array container") + } buf := uint64SliceAsByteSlice(bc.bitmap) return stream.Write(buf) } -// readFrom reads an arrayContainer from stream. -// PRE-REQUISITE: you must size the arrayContainer correctly (allocate b.content) -// *before* you call readFrom. We can't guess the size in the stream -// by this point. -func (ac *arrayContainer) readFrom(stream io.Reader) (int, error) { - buf := uint16SliceAsByteSlice(ac.content) - return io.ReadFull(stream, buf) -} - -func (bc *bitmapContainer) readFrom(stream io.Reader) (int, error) { - buf := uint64SliceAsByteSlice(bc.bitmap) - n, err := io.ReadFull(stream, buf) - bc.computeCardinality() - return n, err -} - func uint64SliceAsByteSlice(slice []uint64) []byte { // make a new slice header header := *(*reflect.SliceHeader)(unsafe.Pointer(&slice)) @@ -42,8 +31,12 @@ func uint64SliceAsByteSlice(slice []uint64) []byte { header.Len *= 8 header.Cap *= 8 + // instantiate result and use KeepAlive so data isn't unmapped. + result := *(*[]byte)(unsafe.Pointer(&header)) + runtime.KeepAlive(&slice) + // return it - return *(*[]byte)(unsafe.Pointer(&header)) + return result } func uint16SliceAsByteSlice(slice []uint16) []byte { @@ -54,8 +47,12 @@ func uint16SliceAsByteSlice(slice []uint16) []byte { header.Len *= 2 header.Cap *= 2 + // instantiate result and use KeepAlive so data isn't unmapped. + result := *(*[]byte)(unsafe.Pointer(&header)) + runtime.KeepAlive(&slice) + // return it - return *(*[]byte)(unsafe.Pointer(&header)) + return result } func (bc *bitmapContainer) asLittleEndianByteSlice() []byte { @@ -64,50 +61,74 @@ func (bc *bitmapContainer) asLittleEndianByteSlice() []byte { // Deserialization code follows -func byteSliceAsUint16Slice(slice []byte) []uint16 { +//// +// These methods (byteSliceAsUint16Slice,...) do not make copies, +// they are pointer-based (unsafe). The caller is responsible to +// ensure that the input slice does not get garbage collected, deleted +// or modified while you hold the returned slince. +//// +func byteSliceAsUint16Slice(slice []byte) (result []uint16) { // here we create a new slice holder if len(slice)%2 != 0 { panic("Slice size should be divisible by 2") } + // reference: https://go101.org/article/unsafe.html // make a new slice header - header := *(*reflect.SliceHeader)(unsafe.Pointer(&slice)) + bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice)) + rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result)) - // update its capacity and length - header.Len /= 2 - header.Cap /= 2 + // transfer the data from the given slice to a new variable (our result) + rHeader.Data = bHeader.Data + rHeader.Len = bHeader.Len / 2 + rHeader.Cap = bHeader.Cap / 2 - // return it - return *(*[]uint16)(unsafe.Pointer(&header)) + // instantiate result and use KeepAlive so data isn't unmapped. + runtime.KeepAlive(&slice) // it is still crucial, GC can free it) + + // return result + return } -func byteSliceAsUint64Slice(slice []byte) []uint64 { +func byteSliceAsUint64Slice(slice []byte) (result []uint64) { if len(slice)%8 != 0 { panic("Slice size should be divisible by 8") } + // reference: https://go101.org/article/unsafe.html // make a new slice header - header := *(*reflect.SliceHeader)(unsafe.Pointer(&slice)) + bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice)) + rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result)) - // update its capacity and length - header.Len /= 8 - header.Cap /= 8 + // transfer the data from the given slice to a new variable (our result) + rHeader.Data = bHeader.Data + rHeader.Len = bHeader.Len / 8 + rHeader.Cap = bHeader.Cap / 8 - // return it - return *(*[]uint64)(unsafe.Pointer(&header)) + // instantiate result and use KeepAlive so data isn't unmapped. + runtime.KeepAlive(&slice) // it is still crucial, GC can free it) + + // return result + return } -func byteSliceAsInterval16Slice(slice []byte) []interval16 { +func byteSliceAsInterval16Slice(slice []byte) (result []interval16) { if len(slice)%4 != 0 { panic("Slice size should be divisible by 4") } + // reference: https://go101.org/article/unsafe.html // make a new slice header - header := *(*reflect.SliceHeader)(unsafe.Pointer(&slice)) + bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice)) + rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result)) - // update its capacity and length - header.Len /= 4 - header.Cap /= 4 + // transfer the data from the given slice to a new variable (our result) + rHeader.Data = bHeader.Data + rHeader.Len = bHeader.Len / 4 + rHeader.Cap = bHeader.Cap / 4 - // return it - return *(*[]interval16)(unsafe.Pointer(&header)) + // instantiate result and use KeepAlive so data isn't unmapped. + runtime.KeepAlive(&slice) // it is still crucial, GC can free it) + + // return result + return } |