Skip to content

Commit 054d629

Browse files
committed
Use Marshal and Unmarshal functionality
Change up the structure to use Marshal and Unmarshal functions instead of the Read Write functions. Small difference, the Unmarshal function returns a BitArray, instead of taking a pointer to a BitArray and populating it, like the encoding/json library.
1 parent ef1ae27 commit 054d629

File tree

6 files changed

+353
-189
lines changed

6 files changed

+353
-189
lines changed

bitarray/bitarray.go

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,6 @@ efficient way. This is *NOT* a threadsafe package.
2020
*/
2121
package bitarray
2222

23-
import (
24-
"encoding/binary"
25-
"io"
26-
)
27-
2823
// bitArray is a struct that maintains state of a bit array.
2924
type bitArray struct {
3025
blocks []block
@@ -270,70 +265,6 @@ func (ba *bitArray) copy() BitArray {
270265
}
271266
}
272267

273-
// Write serializes the bitArray and its data and sends it to the writer.
274-
func Write(w io.Writer, ba *bitArray) error {
275-
err := binary.Write(w, binary.LittleEndian, ba.lowest)
276-
if err != nil {
277-
return err
278-
}
279-
err = binary.Write(w, binary.LittleEndian, ba.highest)
280-
if err != nil {
281-
return err
282-
}
283-
284-
var encodedanyset uint8
285-
if ba.anyset {
286-
encodedanyset = 1
287-
} else {
288-
encodedanyset = 0
289-
}
290-
err = binary.Write(w, binary.LittleEndian, encodedanyset)
291-
if err != nil {
292-
return err
293-
}
294-
295-
err = binary.Write(w, binary.LittleEndian, ba.blocks)
296-
return err
297-
}
298-
299-
// Read takes a reader of a serialized bitArray created by the Write function,
300-
// and returns a bitArray object.
301-
func Read(r io.Reader) (*bitArray, error) {
302-
ret := &bitArray{}
303-
304-
err := binary.Read(r, binary.LittleEndian, &ret.lowest)
305-
if err != nil {
306-
return nil, err
307-
}
308-
309-
err = binary.Read(r, binary.LittleEndian, &ret.highest)
310-
if err != nil {
311-
return nil, err
312-
}
313-
314-
var encodedanyset uint8
315-
err = binary.Read(r, binary.LittleEndian, &encodedanyset)
316-
if err != nil {
317-
return nil, err
318-
}
319-
320-
// anyset defaults to false so we don't need an else statement
321-
if encodedanyset == 1 {
322-
ret.anyset = true
323-
}
324-
325-
var nextblock block
326-
err = binary.Read(r, binary.LittleEndian, &nextblock)
327-
for err == nil {
328-
ret.blocks = append(ret.blocks, nextblock)
329-
err = binary.Read(r, binary.LittleEndian, &nextblock)
330-
}
331-
if err != io.EOF {
332-
return nil, err
333-
}
334-
return ret, nil
335-
}
336-
337268
// newBitArray returns a new dense BitArray at the specified size. This is a
338269
// separate private constructor so unit tests don't have to constantly cast the
339270
// BitArray interface to the concrete type.

bitarray/bitarray_test.go

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717
package bitarray
1818

1919
import (
20-
"bytes"
2120
"testing"
2221

2322
"github.com/stretchr/testify/assert"
@@ -473,30 +472,3 @@ func BenchmarkBitArrayToNums(b *testing.B) {
473472
ba.ToNums()
474473
}
475474
}
476-
477-
func TestBitArrayReadWrite(t *testing.T) {
478-
numItems := uint64(1280)
479-
input := newBitArray(numItems)
480-
481-
for i := uint64(0); i < numItems; i++ {
482-
if i%3 == 0 {
483-
input.SetBit(i)
484-
}
485-
}
486-
487-
writebuf := new(bytes.Buffer)
488-
err := Write(writebuf, input)
489-
assert.Equal(t, err, nil)
490-
491-
// 1280 bits = 20 blocks = 160 bytes, plus lowest and highest at
492-
// 128 bits = 16 bytes plus 1 byte for the anyset param
493-
assert.Equal(t, len(writebuf.Bytes()), 177)
494-
495-
expected := []byte{0, 0, 0, 0, 0, 0, 0, 0, 254}
496-
assert.Equal(t, expected, writebuf.Bytes()[:9])
497-
498-
readbuf := bytes.NewReader(writebuf.Bytes())
499-
output, err := Read(readbuf)
500-
assert.Equal(t, err, nil)
501-
assert.True(t, input.Equals(output))
502-
}

bitarray/encoding.go

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/*
2+
Copyright 2014 Workiva, LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package bitarray
18+
19+
import (
20+
"bytes"
21+
"encoding/binary"
22+
"errors"
23+
"io"
24+
)
25+
26+
// Marshal takes a dense or sparse bit array and serializes it to a
27+
// byte slice.
28+
func Marshal(ba BitArray) ([]byte, error) {
29+
if eba, ok := ba.(*bitArray); ok {
30+
return eba.Serialize()
31+
} else if sba, ok := ba.(*sparseBitArray); ok {
32+
return sba.Serialize()
33+
} else {
34+
return nil, errors.New("not a valid BitArray")
35+
}
36+
}
37+
38+
// Unmarshal takes a byte slice, of the same format produced by Marshal,
39+
// and returns a BitArray.
40+
func Unmarshal(input []byte) (BitArray, error) {
41+
if len(input) == 0 {
42+
return nil, errors.New("no data in input")
43+
}
44+
if input[0] == 'B' {
45+
ret := newBitArray(0)
46+
err := ret.Deserialize(input)
47+
if err != nil {
48+
return nil, err
49+
}
50+
return ret, nil
51+
} else if input[0] == 'S' {
52+
ret := newSparseBitArray()
53+
err := ret.Deserialize(input)
54+
if err != nil {
55+
return nil, err
56+
}
57+
return ret, nil
58+
} else {
59+
return nil, errors.New("unrecognized encoding")
60+
}
61+
}
62+
63+
// Serialize converts the sparseBitArray to a byte slice
64+
func (ba *sparseBitArray) Serialize() ([]byte, error) {
65+
w := new(bytes.Buffer)
66+
67+
var identifier uint8 = 'S'
68+
err := binary.Write(w, binary.LittleEndian, identifier)
69+
if err != nil {
70+
return nil, err
71+
}
72+
73+
blocksLen := uint64(len(ba.blocks))
74+
indexLen := uint64(len(ba.indices))
75+
76+
err = binary.Write(w, binary.LittleEndian, blocksLen)
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
err = binary.Write(w, binary.LittleEndian, ba.blocks)
82+
if err != nil {
83+
return nil, err
84+
}
85+
86+
err = binary.Write(w, binary.LittleEndian, indexLen)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
err = binary.Write(w, binary.LittleEndian, ba.indices)
92+
if err != nil {
93+
return nil, err
94+
}
95+
return w.Bytes(), nil
96+
}
97+
98+
// Deserialize takes the incoming byte slice, and populates the sparseBitArray
99+
// with data in the bytes. Note that this will overwrite any capacity
100+
// specified when creating the sparseBitArray. Also note that if an error
101+
// is returned, the sparseBitArray this is called on might be populated
102+
// with partial data.
103+
func (ret *sparseBitArray) Deserialize(incoming []byte) error {
104+
r := bytes.NewReader(incoming[1:]) // Discard identifier
105+
106+
var intsToRead uint64
107+
err := binary.Read(r, binary.LittleEndian, &intsToRead)
108+
if err != nil {
109+
return err
110+
}
111+
112+
var nextblock block
113+
for i := intsToRead; i > uint64(0); i-- {
114+
err = binary.Read(r, binary.LittleEndian, &nextblock)
115+
if err != nil {
116+
return err
117+
}
118+
ret.blocks = append(ret.blocks, nextblock)
119+
}
120+
121+
err = binary.Read(r, binary.LittleEndian, &intsToRead)
122+
if err != nil {
123+
return err
124+
}
125+
126+
var nextuint uint64
127+
for i := intsToRead; i > uint64(0); i-- {
128+
err = binary.Read(r, binary.LittleEndian, &nextuint)
129+
if err != nil {
130+
return err
131+
}
132+
ret.indices = append(ret.indices, nextuint)
133+
}
134+
135+
return nil
136+
}
137+
138+
// Serialize converts the bitArray to a byte slice.
139+
func (ba *bitArray) Serialize() ([]byte, error) {
140+
w := new(bytes.Buffer)
141+
142+
var identifier uint8 = 'B'
143+
err := binary.Write(w, binary.LittleEndian, identifier)
144+
if err != nil {
145+
return nil, err
146+
}
147+
148+
err = binary.Write(w, binary.LittleEndian, ba.lowest)
149+
if err != nil {
150+
return nil, err
151+
}
152+
err = binary.Write(w, binary.LittleEndian, ba.highest)
153+
if err != nil {
154+
return nil, err
155+
}
156+
157+
var encodedanyset uint8
158+
if ba.anyset {
159+
encodedanyset = 1
160+
} else {
161+
encodedanyset = 0
162+
}
163+
err = binary.Write(w, binary.LittleEndian, encodedanyset)
164+
if err != nil {
165+
return nil, err
166+
}
167+
168+
err = binary.Write(w, binary.LittleEndian, ba.blocks)
169+
if err != nil {
170+
return nil, err
171+
}
172+
return w.Bytes(), nil
173+
}
174+
175+
// Deserialize takes the incoming byte slice, and populates the bitArray
176+
// with data in the bytes. Note that this will overwrite any capacity
177+
// specified when creating the bitArray. Also note that if an error is returned,
178+
// the bitArray this is called on might be populated with partial data.
179+
func (ret *bitArray) Deserialize(incoming []byte) error {
180+
r := bytes.NewReader(incoming[1:]) // Discard identifier
181+
182+
err := binary.Read(r, binary.LittleEndian, &ret.lowest)
183+
if err != nil {
184+
return err
185+
}
186+
187+
err = binary.Read(r, binary.LittleEndian, &ret.highest)
188+
if err != nil {
189+
return err
190+
}
191+
192+
var encodedanyset uint8
193+
err = binary.Read(r, binary.LittleEndian, &encodedanyset)
194+
if err != nil {
195+
return err
196+
}
197+
198+
// anyset defaults to false so we don't need an else statement
199+
if encodedanyset == 1 {
200+
ret.anyset = true
201+
}
202+
203+
var nextblock block
204+
err = binary.Read(r, binary.LittleEndian, &nextblock)
205+
for err == nil {
206+
ret.blocks = append(ret.blocks, nextblock)
207+
err = binary.Read(r, binary.LittleEndian, &nextblock)
208+
}
209+
if err != io.EOF {
210+
return err
211+
}
212+
return nil
213+
}

0 commit comments

Comments
 (0)