• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

apache / datasketches-go / 30273261446

27 Jul 2026 02:03PM UTC coverage: 86.673% (-0.06%) from 86.734%
30273261446

push

github

web-flow
Merge pull request #160 from proost/feat-req-sketch-encode-decode

feat: REQ sketch encoding/decoding

534 of 691 new or added lines in 4 files covered. (77.28%)

1 existing line in 1 file now uncovered.

23030 of 26571 relevant lines covered (86.67%)

0.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.42
/req/decoder.go
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17

18
package req
19

20
import (
21
        "encoding/binary"
22
        "fmt"
23
        "io"
24
        "math"
25

26
        "github.com/apache/datasketches-go/internal"
27
)
28

29
// Decoder is responsible for decoding sketches from binary format.
30
type Decoder struct{}
31

32
// NewDecoder creates a new instance of Decoder.
33
func NewDecoder() Decoder {
1✔
34
        return Decoder{}
1✔
35
}
1✔
36

37
// Decode decodes a sketch from the provided reader.
38
func (d *Decoder) Decode(r io.Reader) (*Sketch, error) {
1✔
39
        var preambleInt byte
1✔
40
        if err := binary.Read(r, binary.LittleEndian, &preambleInt); err != nil {
1✔
NEW
41
                return nil, err
×
NEW
42
        }
×
43

44
        var serVer byte
1✔
45
        if err := binary.Read(r, binary.LittleEndian, &serVer); err != nil {
1✔
NEW
46
                return nil, err
×
NEW
47
        }
×
48
        if serVer != serialVersion {
1✔
NEW
49
                return nil, fmt.Errorf("unsupported serialization version: %d", serVer)
×
NEW
50
        }
×
51

52
        var familyID byte
1✔
53
        if err := binary.Read(r, binary.LittleEndian, &familyID); err != nil {
1✔
NEW
54
                return nil, err
×
NEW
55
        }
×
56
        if int(familyID) != internal.FamilyEnum.REQ.Id {
1✔
NEW
57
                return nil, fmt.Errorf("invalid family id: %d", familyID)
×
NEW
58
        }
×
59

60
        var flags byte
1✔
61
        if err := binary.Read(r, binary.LittleEndian, &flags); err != nil {
1✔
NEW
62
                return nil, err
×
NEW
63
        }
×
64
        isEmpty := (flags & 4) > 0
1✔
65
        isHighRankAccuracyMode := (flags & 8) > 0
1✔
66
        isRawItemsSketch := (flags & 16) > 0
1✔
67
        isLevel0Sorted := (flags & 32) > 0
1✔
68

1✔
69
        var k uint16
1✔
70
        if err := binary.Read(r, binary.LittleEndian, &k); err != nil {
1✔
NEW
71
                return nil, err
×
NEW
72
        }
×
73

74
        var numCompactors byte
1✔
75
        if err := binary.Read(r, binary.LittleEndian, &numCompactors); err != nil {
1✔
NEW
76
                return nil, err
×
NEW
77
        }
×
78

79
        var numRawItems byte
1✔
80
        if err := binary.Read(r, binary.LittleEndian, &numRawItems); err != nil {
1✔
NEW
81
                return nil, err
×
NEW
82
        }
×
83

84
        format := inferEncodingFormat(isEmpty, isRawItemsSketch, int(numCompactors))
1✔
85
        switch format {
1✔
86
        case encodingFormatEmpty:
1✔
87
                if preambleInt != 2 {
1✔
NEW
88
                        return nil, fmt.Errorf("invalid preamble: %d", preambleInt)
×
NEW
89
                }
×
90
                return NewSketch(WithK(int(k)), WithHighRankAccuracyMode(isHighRankAccuracyMode))
1✔
91
        case encodingFormatRawItems:
1✔
92
                if preambleInt != 2 {
1✔
NEW
93
                        return nil, fmt.Errorf("invalid preamble: %d", preambleInt)
×
NEW
94
                }
×
95

96
                sk, err := NewSketch(WithK(int(k)), WithHighRankAccuracyMode(isHighRankAccuracyMode))
1✔
97
                if err != nil {
1✔
NEW
98
                        return nil, err
×
NEW
99
                }
×
100

101
                for i := byte(0); i < numRawItems; i++ {
2✔
102
                        var rawItem uint32
1✔
103
                        if err := binary.Read(r, binary.LittleEndian, &rawItem); err != nil {
1✔
NEW
104
                                return nil, err
×
NEW
105
                        }
×
106

107
                        if err := sk.Update(math.Float32frombits(rawItem)); err != nil {
1✔
NEW
108
                                return nil, err
×
NEW
109
                        }
×
110
                }
111
                return sk, nil
1✔
112
        case encodingFormatExact:
1✔
113
                if preambleInt != 2 {
1✔
NEW
114
                        return nil, fmt.Errorf("invalid preamble: %d", preambleInt)
×
NEW
115
                }
×
116

117
                decoder := newCompactorDecoder(isLevel0Sorted, isHighRankAccuracyMode)
1✔
118
                result, err := decoder.Decode(r)
1✔
119
                if err != nil {
1✔
NEW
120
                        return nil, err
×
NEW
121
                }
×
122

123
                sk := &Sketch{
1✔
124
                        n:                      result.n,
1✔
125
                        compactors:             []*compactor{result.compactor},
1✔
126
                        minItem:                result.minItem,
1✔
127
                        maxItem:                result.maxItem,
1✔
128
                        k:                      int(k),
1✔
129
                        isHighRankAccuracyMode: isHighRankAccuracyMode,
1✔
130
                }
1✔
131
                if err := sk.validateK(); err != nil {
1✔
NEW
132
                        return nil, err
×
NEW
133
                }
×
134
                sk.maxNomSize = sk.computeMaxNomSize()
1✔
135
                sk.numRetained = sk.computeRetainedItems()
1✔
136
                return sk, nil
1✔
137
        default: // Estimation.
1✔
138
                if preambleInt != 4 {
1✔
NEW
139
                        return nil, fmt.Errorf("invalid preamble: %d", preambleInt)
×
NEW
140
                }
×
141

142
                var n uint64
1✔
143
                if err := binary.Read(r, binary.LittleEndian, &n); err != nil {
1✔
NEW
144
                        return nil, err
×
NEW
145
                }
×
146

147
                var minItemRaw uint32
1✔
148
                if err := binary.Read(r, binary.LittleEndian, &minItemRaw); err != nil {
1✔
NEW
149
                        return nil, err
×
NEW
150
                }
×
151
                minItem := math.Float32frombits(minItemRaw)
1✔
152

1✔
153
                var maxItemRaw uint32
1✔
154
                if err := binary.Read(r, binary.LittleEndian, &maxItemRaw); err != nil {
1✔
NEW
155
                        return nil, err
×
NEW
156
                }
×
157
                maxItem := math.Float32frombits(maxItemRaw)
1✔
158

1✔
159
                compactors := make([]*compactor, 0, int(numCompactors))
1✔
160
                for i := 0; i < int(numCompactors); i++ {
2✔
161
                        if i == 0 {
2✔
162
                                decoder := newCompactorDecoder(isLevel0Sorted, isHighRankAccuracyMode)
1✔
163
                                result, err := decoder.Decode(r)
1✔
164
                                if err != nil {
1✔
NEW
165
                                        return nil, err
×
NEW
166
                                }
×
167

168
                                compactors = append(compactors, result.compactor)
1✔
169
                        } else {
1✔
170
                                decoder := newCompactorDecoder(true, isHighRankAccuracyMode)
1✔
171
                                result, err := decoder.Decode(r)
1✔
172
                                if err != nil {
1✔
NEW
173
                                        return nil, err
×
NEW
174
                                }
×
175

176
                                compactors = append(compactors, result.compactor)
1✔
177
                        }
178
                }
179

180
                sk := &Sketch{
1✔
181
                        k:                      int(k),
1✔
182
                        isHighRankAccuracyMode: isHighRankAccuracyMode,
1✔
183
                        n:                      int64(n),
1✔
184
                        minItem:                minItem,
1✔
185
                        maxItem:                maxItem,
1✔
186
                        compactors:             compactors,
1✔
187
                }
1✔
188
                if err := sk.validateK(); err != nil {
1✔
NEW
189
                        return nil, err
×
NEW
190
                }
×
191
                sk.maxNomSize = sk.computeMaxNomSize()
1✔
192
                sk.numRetained = sk.computeRetainedItems()
1✔
193
                return sk, nil
1✔
194
        }
195
}
196

197
// Decode decodes a sketch from the provided buffer.
198
// If the buffer is too short, returns io.ErrUnexpectedEOF.
199
func Decode(buf []byte) (*Sketch, error) {
1✔
200
        index := 0
1✔
201
        if err := validateBuffer(buf, index+1); err != nil {
1✔
NEW
202
                return nil, err
×
NEW
203
        }
×
204
        preambleInts := buf[index]
1✔
205
        index++
1✔
206

1✔
207
        if err := validateBuffer(buf, index+1); err != nil {
1✔
NEW
208
                return nil, err
×
NEW
209
        }
×
210
        serVer := buf[index]
1✔
211
        index++
1✔
212
        if serVer != serialVersion {
1✔
NEW
213
                return nil, fmt.Errorf("unsupported serialization version: %d", serVer)
×
NEW
214
        }
×
215

216
        if err := validateBuffer(buf, index+1); err != nil {
1✔
NEW
217
                return nil, err
×
NEW
218
        }
×
219
        familyID := buf[index]
1✔
220
        index++
1✔
221
        if int(familyID) != internal.FamilyEnum.REQ.Id {
1✔
NEW
222
                return nil, fmt.Errorf("invalid family id: %d", familyID)
×
NEW
223
        }
×
224

225
        // flags.
226
        if err := validateBuffer(buf, index+1); err != nil {
1✔
NEW
227
                return nil, err
×
NEW
228
        }
×
229
        flags := buf[index]
1✔
230
        index++
1✔
231
        isEmpty := (flags & 4) > 0
1✔
232
        isHighRankAccuracyMode := (flags & 8) > 0
1✔
233
        isRawItemsSketch := (flags & 16) > 0
1✔
234
        isLevel0Sorted := (flags & 32) > 0
1✔
235

1✔
236
        if err := validateBuffer(buf, index+2); err != nil {
1✔
NEW
237
                return nil, err
×
NEW
238
        }
×
239
        k := binary.LittleEndian.Uint16(buf[index : index+2])
1✔
240
        index += 2
1✔
241

1✔
242
        if err := validateBuffer(buf, index+1); err != nil {
1✔
NEW
243
                return nil, err
×
NEW
244
        }
×
245
        numCompactors := buf[index]
1✔
246
        index++
1✔
247

1✔
248
        if err := validateBuffer(buf, index+1); err != nil {
1✔
NEW
249
                return nil, err
×
NEW
250
        }
×
251
        numRawItems := buf[index]
1✔
252
        index++
1✔
253

1✔
254
        format := inferEncodingFormat(isEmpty, isRawItemsSketch, int(numCompactors))
1✔
255
        switch format {
1✔
256
        case encodingFormatEmpty:
1✔
257
                if preambleInts != 2 {
1✔
NEW
258
                        return nil, fmt.Errorf("invalid preamble ints for empty encoding format: %d", preambleInts)
×
NEW
259
                }
×
260
                return NewSketch(WithK(int(k)), WithHighRankAccuracyMode(isHighRankAccuracyMode))
1✔
261
        case encodingFormatRawItems:
1✔
262
                if preambleInts != 2 {
1✔
NEW
263
                        return nil, fmt.Errorf("invalid preamble ints for raw items encoding: %d", preambleInts)
×
NEW
264
                }
×
265

266
                sk, err := NewSketch(WithK(int(k)), WithHighRankAccuracyMode(isHighRankAccuracyMode))
1✔
267
                if err != nil {
1✔
NEW
268
                        return nil, err
×
NEW
269
                }
×
270

271
                for i := 0; i < int(numRawItems); i++ {
2✔
272
                        if err := validateBuffer(buf, index+4); err != nil {
1✔
NEW
273
                                return nil, err
×
NEW
274
                        }
×
275

276
                        item := math.Float32frombits(binary.LittleEndian.Uint32(buf[index : index+4]))
1✔
277

1✔
278
                        if err := sk.Update(item); err != nil {
1✔
NEW
279
                                return nil, err
×
NEW
280
                        }
×
281

282
                        index += 4
1✔
283
                }
284
                return sk, nil
1✔
285
        case encodingFormatExact:
1✔
286
                if preambleInts != 2 {
1✔
NEW
287
                        return nil, fmt.Errorf("invalid preamble ints for exact encoding: %d", preambleInts)
×
NEW
288
                }
×
289

290
                result, err := decodeCompactor(buf, index, isLevel0Sorted, isHighRankAccuracyMode)
1✔
291
                if err != nil {
1✔
NEW
292
                        return nil, err
×
NEW
293
                }
×
294

295
                sk := &Sketch{
1✔
296
                        n:                      result.n,
1✔
297
                        compactors:             []*compactor{result.compactor},
1✔
298
                        minItem:                result.minItem,
1✔
299
                        maxItem:                result.maxItem,
1✔
300
                        k:                      int(k),
1✔
301
                        isHighRankAccuracyMode: isHighRankAccuracyMode,
1✔
302
                }
1✔
303
                if err := sk.validateK(); err != nil {
1✔
NEW
304
                        return nil, err
×
NEW
305
                }
×
306
                sk.maxNomSize = sk.computeMaxNomSize()
1✔
307
                sk.numRetained = sk.computeRetainedItems()
1✔
308
                return sk, nil
1✔
309
        default: // Estimation.
1✔
310
                if err := validateBuffer(buf, index+8); err != nil {
1✔
NEW
311
                        return nil, err
×
NEW
312
                }
×
313
                n := binary.LittleEndian.Uint64(buf[index : index+8])
1✔
314
                index += 8
1✔
315

1✔
316
                if err := validateBuffer(buf, index+4); err != nil {
1✔
NEW
317
                        return nil, err
×
NEW
318
                }
×
319
                minItem := math.Float32frombits(binary.LittleEndian.Uint32(buf[index : index+4]))
1✔
320
                index += 4
1✔
321

1✔
322
                if err := validateBuffer(buf, index+4); err != nil {
1✔
NEW
323
                        return nil, err
×
NEW
324
                }
×
325
                maxItem := math.Float32frombits(binary.LittleEndian.Uint32(buf[index : index+4]))
1✔
326
                index += 4
1✔
327

1✔
328
                compactors := make([]*compactor, 0, int(numCompactors))
1✔
329
                for i := 0; i < int(numCompactors); i++ {
2✔
330
                        if i == 0 {
2✔
331
                                res, err := decodeCompactor(buf, index, isLevel0Sorted, isHighRankAccuracyMode)
1✔
332
                                if err != nil {
1✔
NEW
333
                                        return nil, err
×
NEW
334
                                }
×
335

336
                                compactors = append(compactors, res.compactor)
1✔
337
                                index = res.bufferEndIndex
1✔
338
                        } else {
1✔
339
                                res, err := decodeCompactor(buf, index, true, isHighRankAccuracyMode)
1✔
340
                                if err != nil {
1✔
NEW
341
                                        return nil, err
×
NEW
342
                                }
×
343

344
                                compactors = append(compactors, res.compactor)
1✔
345
                                index = res.bufferEndIndex
1✔
346
                        }
347
                }
348

349
                sk := &Sketch{
1✔
350
                        k:                      int(k),
1✔
351
                        isHighRankAccuracyMode: isHighRankAccuracyMode,
1✔
352
                        n:                      int64(n),
1✔
353
                        minItem:                minItem,
1✔
354
                        maxItem:                maxItem,
1✔
355
                        compactors:             compactors,
1✔
356
                }
1✔
357
                if err := sk.validateK(); err != nil {
1✔
NEW
358
                        return nil, err
×
NEW
359
                }
×
360
                sk.maxNomSize = sk.computeMaxNomSize()
1✔
361
                sk.numRetained = sk.computeRetainedItems()
1✔
362
                return sk, nil
1✔
363
        }
364
}
365

366
func validateBuffer(buf []byte, endIndex int) error {
1✔
367
        if len(buf) < endIndex {
1✔
NEW
368
                return io.ErrUnexpectedEOF
×
NEW
369
        }
×
370
        return nil
1✔
371
}
372

373
func inferEncodingFormat(isEmpty, isRawItemsSketch bool, numCompactors int) encodingFormat {
1✔
374
        if numCompactors <= 1 {
2✔
375
                if isEmpty {
2✔
376
                        return encodingFormatEmpty
1✔
377
                }
1✔
378
                if isRawItemsSketch {
2✔
379
                        return encodingFormatRawItems
1✔
380
                }
1✔
381
                return encodingFormatExact
1✔
382
        }
383
        return encodingFormatEstimation
1✔
384
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc