Skip to content

Commit 2d93a7e

Browse files
authored
Merge pull request #504 from happygiraffe/master
Switch to builtin unsafe slice creation.
2 parents 80986ef + 7cbdda9 commit 2d93a7e

File tree

1 file changed

+72
-179
lines changed

1 file changed

+72
-179
lines changed

serialization_littleendian.go

Lines changed: 72 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ package roaring
66
import (
77
"encoding/binary"
88
"errors"
9+
"fmt"
910
"io"
10-
"reflect"
11-
"runtime"
1211
"unsafe"
1312
)
1413

@@ -26,51 +25,30 @@ func (bc *bitmapContainer) writeTo(stream io.Writer) (int, error) {
2625
}
2726

2827
func uint64SliceAsByteSlice(slice []uint64) []byte {
29-
// make a new slice header
30-
header := *(*reflect.SliceHeader)(unsafe.Pointer(&slice))
31-
32-
// update its capacity and length
33-
header.Len *= 8
34-
header.Cap *= 8
35-
36-
// instantiate result and use KeepAlive so data isn't unmapped.
37-
result := *(*[]byte)(unsafe.Pointer(&header))
38-
runtime.KeepAlive(&slice)
39-
40-
// return it
41-
return result
28+
ptr := unsafe.SliceData(slice)
29+
if ptr == nil {
30+
return nil
31+
}
32+
const size = unsafe.Sizeof(uint64(0))
33+
return unsafe.Slice(((*byte)(unsafe.Pointer(ptr))), int(size)*len(slice))
4234
}
4335

4436
func uint16SliceAsByteSlice(slice []uint16) []byte {
45-
// make a new slice header
46-
header := *(*reflect.SliceHeader)(unsafe.Pointer(&slice))
47-
48-
// update its capacity and length
49-
header.Len *= 2
50-
header.Cap *= 2
51-
52-
// instantiate result and use KeepAlive so data isn't unmapped.
53-
result := *(*[]byte)(unsafe.Pointer(&header))
54-
runtime.KeepAlive(&slice)
55-
56-
// return it
57-
return result
37+
ptr := unsafe.SliceData(slice)
38+
if ptr == nil {
39+
return nil
40+
}
41+
const size = unsafe.Sizeof(uint16(0))
42+
return unsafe.Slice(((*byte)(unsafe.Pointer(ptr))), int(size)*len(slice))
5843
}
5944

6045
func interval16SliceAsByteSlice(slice []interval16) []byte {
61-
// make a new slice header
62-
header := *(*reflect.SliceHeader)(unsafe.Pointer(&slice))
63-
64-
// update its capacity and length
65-
header.Len *= 4
66-
header.Cap *= 4
67-
68-
// instantiate result and use KeepAlive so data isn't unmapped.
69-
result := *(*[]byte)(unsafe.Pointer(&header))
70-
runtime.KeepAlive(&slice)
71-
72-
// return it
73-
return result
46+
ptr := unsafe.SliceData(slice)
47+
if ptr == nil {
48+
return nil
49+
}
50+
const size = unsafe.Sizeof(interval16{})
51+
return unsafe.Slice(((*byte)(unsafe.Pointer(ptr))), int(size)*len(slice))
7452
}
7553

7654
func (bc *bitmapContainer) asLittleEndianByteSlice() []byte {
@@ -86,69 +64,39 @@ func (bc *bitmapContainer) asLittleEndianByteSlice() []byte {
8664
// or modified while you hold the returned slince.
8765
// //
8866
func byteSliceAsUint16Slice(slice []byte) (result []uint16) { // here we create a new slice holder
89-
if len(slice)%2 != 0 {
90-
panic("Slice size should be divisible by 2")
67+
const sz = int(unsafe.Sizeof(uint16(0)))
68+
if len(slice)%sz != 0 {
69+
panic(fmt.Sprintf("Slice size should be divisible by %d", sz))
9170
}
92-
// reference: https://go101.org/article/unsafe.html
93-
94-
// make a new slice header
95-
bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
96-
rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
97-
98-
// transfer the data from the given slice to a new variable (our result)
99-
rHeader.Data = bHeader.Data
100-
rHeader.Len = bHeader.Len / 2
101-
rHeader.Cap = bHeader.Cap / 2
102-
103-
// instantiate result and use KeepAlive so data isn't unmapped.
104-
runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
105-
106-
// return result
107-
return
71+
ptr := unsafe.SliceData(slice)
72+
if ptr == nil {
73+
return nil
74+
}
75+
return unsafe.Slice((*uint16)(unsafe.Pointer(ptr)), len(slice)/sz)
10876
}
10977

11078
func byteSliceAsUint64Slice(slice []byte) (result []uint64) {
111-
if len(slice)%8 != 0 {
112-
panic("Slice size should be divisible by 8")
79+
const sz = int(unsafe.Sizeof(uint64(0)))
80+
if len(slice)%sz != 0 {
81+
panic(fmt.Sprintf("Slice size should be divisible by %d", sz))
11382
}
114-
// reference: https://go101.org/article/unsafe.html
115-
116-
// make a new slice header
117-
bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
118-
rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
119-
120-
// transfer the data from the given slice to a new variable (our result)
121-
rHeader.Data = bHeader.Data
122-
rHeader.Len = bHeader.Len / 8
123-
rHeader.Cap = bHeader.Cap / 8
124-
125-
// instantiate result and use KeepAlive so data isn't unmapped.
126-
runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
127-
128-
// return result
129-
return
83+
ptr := unsafe.SliceData(slice)
84+
if ptr == nil {
85+
return nil
86+
}
87+
return unsafe.Slice((*uint64)(unsafe.Pointer(ptr)), len(slice)/sz)
13088
}
13189

13290
func byteSliceAsInterval16Slice(slice []byte) (result []interval16) {
133-
if len(slice)%4 != 0 {
134-
panic("Slice size should be divisible by 4")
91+
const sz = int(unsafe.Sizeof(interval16{}))
92+
if len(slice)%sz != 0 {
93+
panic(fmt.Sprintf("Slice size should be divisible by %d", sz))
13594
}
136-
// reference: https://go101.org/article/unsafe.html
137-
138-
// make a new slice header
139-
bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
140-
rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
141-
142-
// transfer the data from the given slice to a new variable (our result)
143-
rHeader.Data = bHeader.Data
144-
rHeader.Len = bHeader.Len / 4
145-
rHeader.Cap = bHeader.Cap / 4
146-
147-
// instantiate result and use KeepAlive so data isn't unmapped.
148-
runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
149-
150-
// return result
151-
return
95+
ptr := unsafe.SliceData(slice)
96+
if ptr == nil {
97+
return nil
98+
}
99+
return unsafe.Slice((*interval16)(unsafe.Pointer(ptr)), len(slice)/sz)
152100
}
153101

154102
func byteSliceAsContainerSlice(slice []byte) (result []container) {
@@ -158,114 +106,59 @@ func byteSliceAsContainerSlice(slice []byte) (result []container) {
158106
if len(slice)%containerSize != 0 {
159107
panic("Slice size should be divisible by unsafe.Sizeof(container)")
160108
}
161-
// reference: https://go101.org/article/unsafe.html
162-
163-
// make a new slice header
164-
bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
165-
rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
166-
167-
// transfer the data from the given slice to a new variable (our result)
168-
rHeader.Data = bHeader.Data
169-
rHeader.Len = bHeader.Len / containerSize
170-
rHeader.Cap = bHeader.Cap / containerSize
171-
172-
// instantiate result and use KeepAlive so data isn't unmapped.
173-
runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
174-
175-
// return result
176-
return
109+
ptr := unsafe.SliceData(slice)
110+
if ptr == nil {
111+
return nil
112+
}
113+
return unsafe.Slice((*container)(unsafe.Pointer(ptr)), len(slice)/containerSize)
177114
}
178115

179116
func byteSliceAsBitsetSlice(slice []byte) (result []bitmapContainer) {
180-
bitsetSize := int(unsafe.Sizeof(bitmapContainer{}))
117+
const bitsetSize = int(unsafe.Sizeof(bitmapContainer{}))
181118
if len(slice)%bitsetSize != 0 {
182119
panic("Slice size should be divisible by unsafe.Sizeof(bitmapContainer)")
183120
}
184-
// reference: https://go101.org/article/unsafe.html
185-
186-
// make a new slice header
187-
bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
188-
rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
189-
190-
// transfer the data from the given slice to a new variable (our result)
191-
rHeader.Data = bHeader.Data
192-
rHeader.Len = bHeader.Len / bitsetSize
193-
rHeader.Cap = bHeader.Cap / bitsetSize
194-
195-
// instantiate result and use KeepAlive so data isn't unmapped.
196-
runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
197-
198-
// return result
199-
return
121+
ptr := unsafe.SliceData(slice)
122+
if ptr == nil {
123+
return nil
124+
}
125+
return unsafe.Slice((*bitmapContainer)(unsafe.Pointer(ptr)), len(slice)/bitsetSize)
200126
}
201127

202128
func byteSliceAsArraySlice(slice []byte) (result []arrayContainer) {
203-
arraySize := int(unsafe.Sizeof(arrayContainer{}))
129+
const arraySize = int(unsafe.Sizeof(arrayContainer{}))
204130
if len(slice)%arraySize != 0 {
205131
panic("Slice size should be divisible by unsafe.Sizeof(arrayContainer)")
206132
}
207-
// reference: https://go101.org/article/unsafe.html
208-
209-
// make a new slice header
210-
bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
211-
rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
212-
213-
// transfer the data from the given slice to a new variable (our result)
214-
rHeader.Data = bHeader.Data
215-
rHeader.Len = bHeader.Len / arraySize
216-
rHeader.Cap = bHeader.Cap / arraySize
217-
218-
// instantiate result and use KeepAlive so data isn't unmapped.
219-
runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
220-
221-
// return result
222-
return
133+
ptr := unsafe.SliceData(slice)
134+
if ptr == nil {
135+
return nil
136+
}
137+
return unsafe.Slice((*arrayContainer)(unsafe.Pointer(ptr)), len(slice)/arraySize)
223138
}
224139

225140
func byteSliceAsRun16Slice(slice []byte) (result []runContainer16) {
226-
run16Size := int(unsafe.Sizeof(runContainer16{}))
141+
const run16Size = int(unsafe.Sizeof(runContainer16{}))
227142
if len(slice)%run16Size != 0 {
228143
panic("Slice size should be divisible by unsafe.Sizeof(runContainer16)")
229144
}
230-
// reference: https://go101.org/article/unsafe.html
231-
232-
// make a new slice header
233-
bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
234-
rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
235-
236-
// transfer the data from the given slice to a new variable (our result)
237-
rHeader.Data = bHeader.Data
238-
rHeader.Len = bHeader.Len / run16Size
239-
rHeader.Cap = bHeader.Cap / run16Size
240-
241-
// instantiate result and use KeepAlive so data isn't unmapped.
242-
runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
243-
244-
// return result
245-
return
145+
ptr := unsafe.SliceData(slice)
146+
if ptr == nil {
147+
return nil
148+
}
149+
return unsafe.Slice((*runContainer16)(unsafe.Pointer(ptr)), len(slice)/run16Size)
246150
}
247151

248152
func byteSliceAsBoolSlice(slice []byte) (result []bool) {
249-
boolSize := int(unsafe.Sizeof(true))
153+
const boolSize = int(unsafe.Sizeof(true))
250154
if len(slice)%boolSize != 0 {
251155
panic("Slice size should be divisible by unsafe.Sizeof(bool)")
252156
}
253-
// reference: https://go101.org/article/unsafe.html
254-
255-
// make a new slice header
256-
bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
257-
rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
258-
259-
// transfer the data from the given slice to a new variable (our result)
260-
rHeader.Data = bHeader.Data
261-
rHeader.Len = bHeader.Len / boolSize
262-
rHeader.Cap = bHeader.Cap / boolSize
263-
264-
// instantiate result and use KeepAlive so data isn't unmapped.
265-
runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
266-
267-
// return result
268-
return
157+
ptr := unsafe.SliceData(slice)
158+
if ptr == nil {
159+
return nil
160+
}
161+
return unsafe.Slice((*bool)(unsafe.Pointer(ptr)), len(slice)/boolSize)
269162
}
270163

271164
// FrozenView creates a static view of a serialized bitmap stored in buf.

0 commit comments

Comments
 (0)