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

HDT3213 / rdb / 17526273087

07 Sep 2025 08:42AM UTC coverage: 66.434% (-4.6%) from 71.063%
17526273087

push

github

HDT3213
fix intsize calculation error in intset encoding

2 of 2 new or added lines in 1 file covered. (100.0%)

321 existing lines in 3 files now uncovered.

1617 of 2434 relevant lines covered (66.43%)

0.72 hits per line

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

67.5
/core/decoder.go
1
// Package core is RDB core core
2
package core
3

4
import (
5
        "bufio"
6
        "bytes"
7
        "encoding/binary"
8
        "errors"
9
        "fmt"
10
        "io"
11
        "strconv"
12
        "time"
13

14
        "github.com/hdt3213/rdb/memprofiler"
15
        "github.com/hdt3213/rdb/model"
16
)
17

18
// Decoder is an instance of rdb parsing process
19
type Decoder struct {
20
        input     *bufio.Reader
21
        readCount int
22
        buffer    []byte
23

24
        withSpecialOpCode bool
25
        withSpecialTypes  map[string]ModuleTypeHandleFunc
26
}
27

28
// NewDecoder creates a new RDB decoder
29
func NewDecoder(reader io.Reader) *Decoder {
1✔
30
        parser := new(Decoder)
1✔
31
        parser.input = bufio.NewReader(reader)
1✔
32
        parser.buffer = make([]byte, 8)
1✔
33
        parser.withSpecialTypes = make(map[string]ModuleTypeHandleFunc)
1✔
34
        return parser
1✔
35
}
1✔
36

37
// WithSpecialOpCode enables returning model.AuxObject to callback
38
func (dec *Decoder) WithSpecialOpCode() *Decoder {
1✔
39
        dec.withSpecialOpCode = true
1✔
40
        return dec
1✔
41
}
1✔
42

43
// WithSpecialType enables returning redis module data structure to callback
44
func (dec *Decoder) WithSpecialType(moduleType string, f ModuleTypeHandleFunc) *Decoder {
1✔
45
        dec.withSpecialTypes[moduleType] = f
1✔
46
        return dec
1✔
47
}
1✔
48

49
var magicNumber = []byte("REDIS")
50

51
const (
52
        minVersion = 1
53
        maxVersion = 12
54
)
55

56
const (
57
        opCodeModuleAux    = 247 /* Module auxiliary data. */
58
        opCodeIdle         = 248 /* LRU idle time. */
59
        opCodeFreq         = 249 /* LFU frequency. */
60
        opCodeAux          = 250 /* RDB aux field. */
61
        opCodeResizeDB     = 251 /* Hash table resize hint. */
62
        opCodeExpireTimeMs = 252 /* Expire time in milliseconds. */
63
        opCodeExpireTime   = 253 /* Old expire time in seconds. */
64
        opCodeSelectDB     = 254 /* DB number of the following keys. */
65
        opCodeEOF          = 255
66
)
67

68
const (
69
        typeString = iota
70
        typeList
71
        typeSet
72
        typeZset
73
        typeHash
74
        typeZset2 /* ZSET version 2 with doubles stored in binary. */
75
        typeModule
76
        typeModule2 // Module value parser should be registered with Decoder.WithSpecialType
77
        _
78
        typeHashZipMap
79
        typeListZipList
80
        typeSetIntSet
81
        typeZsetZipList
82
        typeHashZipList
83
        typeListQuickList
84
        typeStreamListPacks
85
        typeHashListPack
86
        typeZsetListPack
87
        typeListQuickList2
88
        typeStreamListPacks2
89
        typeSetListPack
90
        typeStreamListPacks3
91
        typeHashWithHfeRc         // rdb 12 (only redis 7.4 rc)
92
        typeHashListPackWithHfeRc // rdb 12 (only redis 7.4 rc)
93
        typeHashWithHfe           // since rdb 12 (redis 7.4)
94
        typeHashListPackWithHfe   // since rdb 12 (redis 7.4)
95
)
96

97
const (
98
        EB_EXPIRE_TIME_MAX     int64 = 0x0000FFFFFFFFFFFF
99
        EB_EXPIRE_TIME_INVALID int64 = EB_EXPIRE_TIME_MAX + 1
100
        HFE_MAX_ABS_TIME_MSEC  int64 = EB_EXPIRE_TIME_MAX >> 2
101
)
102

103
var encodingMap = map[int]string{
104
        typeString:                model.StringEncoding,
105
        typeList:                  model.ListEncoding,
106
        typeSet:                   model.SetEncoding,
107
        typeZset:                  model.ZSetEncoding,
108
        typeHash:                  model.HashEncoding,
109
        typeZset2:                 model.ZSet2Encoding,
110
        typeHashZipMap:            model.ZipMapEncoding,
111
        typeListZipList:           model.ZipListEncoding,
112
        typeSetIntSet:             model.IntSetEncoding,
113
        typeZsetZipList:           model.ZipListEncoding,
114
        typeHashZipList:           model.ZipListEncoding,
115
        typeListQuickList:         model.QuickListEncoding,
116
        typeStreamListPacks:       model.ListPackEncoding,
117
        typeStreamListPacks2:      model.ListPackEncoding,
118
        typeHashListPack:          model.ListPackEncoding,
119
        typeZsetListPack:          model.ListPackEncoding,
120
        typeListQuickList2:        model.QuickList2Encoding,
121
        typeSetListPack:           model.ListPackEncoding,
122
        typeHashWithHfeRc:         model.HashExEncoding,
123
        typeHashListPackWithHfeRc: model.ListPackExEncoding,
124
        typeHashWithHfe:           model.HashExEncoding,
125
        typeHashListPackWithHfe:   model.ListPackExEncoding,
126
}
127

128
// checkHeader checks whether input has valid RDB file header
129
func (dec *Decoder) checkHeader() error {
1✔
130
        header := make([]byte, 9)
1✔
131
        err := dec.readFull(header)
1✔
132
        if err == io.EOF {
1✔
UNCOV
133
                return errors.New("empty file")
×
UNCOV
134
        }
×
135
        if err != nil {
1✔
UNCOV
136
                return fmt.Errorf("io error: %v", err)
×
UNCOV
137
        }
×
138
        if !bytes.Equal(header[0:5], magicNumber) {
1✔
UNCOV
139
                return errors.New("file is not a RDB file")
×
UNCOV
140
        }
×
141
        version, err := strconv.Atoi(string(header[5:]))
1✔
142
        if err != nil {
1✔
143
                return fmt.Errorf("%s is not valid version number", string(header[5:]))
×
UNCOV
144
        }
×
145
        if version < minVersion || version > maxVersion {
1✔
UNCOV
146
                return fmt.Errorf("cannot parse version: %d", version)
×
UNCOV
147
        }
×
148
        return nil
1✔
149
}
150

151
func (dec *Decoder) readObject(flag byte, base *model.BaseObject) (model.RedisObject, error) {
1✔
152
        base.Encoding = encodingMap[int(flag)]
1✔
153
        switch flag {
1✔
154
        case typeString:
1✔
155
                bs, err := dec.readString()
1✔
156
                if err != nil {
1✔
UNCOV
157
                        return nil, err
×
UNCOV
158
                }
×
159
                return &model.StringObject{
1✔
160
                        BaseObject: base,
1✔
161
                        Value:      bs,
1✔
162
                }, nil
1✔
163
        case typeList:
1✔
164
                list, err := dec.readList()
1✔
165
                if err != nil {
1✔
UNCOV
166
                        return nil, err
×
UNCOV
167
                }
×
168
                return &model.ListObject{
1✔
169
                        BaseObject: base,
1✔
170
                        Values:     list,
1✔
171
                }, nil
1✔
172
        case typeSet:
1✔
173
                set, err := dec.readSet()
1✔
174
                if err != nil {
1✔
UNCOV
175
                        return nil, err
×
UNCOV
176
                }
×
177
                return &model.SetObject{
1✔
178
                        BaseObject: base,
1✔
179
                        Members:    set,
1✔
180
                }, nil
1✔
181
        case typeSetIntSet:
1✔
182
                set, extra, err := dec.readIntSet()
1✔
183
                if err != nil {
1✔
UNCOV
184
                        return nil, err
×
UNCOV
185
                }
×
186
                base.Extra = extra
1✔
187
                return &model.SetObject{
1✔
188
                        BaseObject: base,
1✔
189
                        Members:    set,
1✔
190
                }, nil
1✔
191
        case typeHash:
1✔
192
                hash, err := dec.readHashMap()
1✔
193
                if err != nil {
1✔
UNCOV
194
                        return nil, err
×
UNCOV
195
                }
×
196
                return &model.HashObject{
1✔
197
                        BaseObject: base,
1✔
198
                        Hash:       hash,
1✔
199
                }, nil
1✔
200
        case typeListZipList:
1✔
201
                list, err := dec.readZipList()
1✔
202
                if err != nil {
1✔
UNCOV
203
                        return nil, err
×
UNCOV
204
                }
×
205
                return &model.ListObject{
1✔
206
                        BaseObject: base,
1✔
207
                        Values:     list,
1✔
208
                }, nil
1✔
209
        case typeListQuickList:
1✔
210
                list, extra, err := dec.readQuickList()
1✔
211
                if err != nil {
1✔
UNCOV
212
                        return nil, err
×
UNCOV
213
                }
×
214
                base.Extra = extra
1✔
215
                return &model.ListObject{
1✔
216
                        BaseObject: base,
1✔
217
                        Values:     list,
1✔
218
                }, nil
1✔
219
        case typeListQuickList2:
1✔
220
                list, extra, err := dec.readQuickList2()
1✔
221
                if err != nil {
1✔
UNCOV
222
                        return nil, err
×
UNCOV
223
                }
×
224
                base.Extra = extra
1✔
225
                return &model.ListObject{
1✔
226
                        BaseObject: base,
1✔
227
                        Values:     list,
1✔
228
                }, nil
1✔
229
        case typeHashZipMap:
1✔
230
                m, err := dec.readZipMapHash()
1✔
231
                if err != nil {
1✔
UNCOV
232
                        return nil, err
×
UNCOV
233
                }
×
234
                return &model.HashObject{
1✔
235
                        BaseObject: base,
1✔
236
                        Hash:       m,
1✔
237
                }, nil
1✔
238
        case typeHashZipList:
1✔
239
                m, extra, err := dec.readZipListHash()
1✔
240
                if err != nil {
1✔
UNCOV
241
                        return nil, err
×
UNCOV
242
                }
×
243
                base.Extra = extra
1✔
244
                return &model.HashObject{
1✔
245
                        BaseObject: base,
1✔
246
                        Hash:       m,
1✔
247
                }, nil
1✔
248
        case typeHashListPack:
1✔
249
                m, extra, err := dec.readListPackHash()
1✔
250
                if err != nil {
1✔
UNCOV
251
                        return nil, err
×
UNCOV
252
                }
×
253
                base.Extra = extra
1✔
254
                return &model.HashObject{
1✔
255
                        BaseObject: base,
1✔
256
                        Hash:       m,
1✔
257
                }, nil
1✔
258
        case typeZset:
1✔
259
                entries, err := dec.readZSet(false)
1✔
260
                if err != nil {
1✔
UNCOV
261
                        return nil, err
×
UNCOV
262
                }
×
263
                return &model.ZSetObject{
1✔
264
                        BaseObject: base,
1✔
265
                        Entries:    entries,
1✔
266
                }, nil
1✔
267
        case typeZset2:
1✔
268
                entries, err := dec.readZSet(true)
1✔
269
                if err != nil {
1✔
UNCOV
270
                        return nil, err
×
UNCOV
271
                }
×
272
                return &model.ZSetObject{
1✔
273
                        BaseObject: base,
1✔
274
                        Entries:    entries,
1✔
275
                }, nil
1✔
276
        case typeZsetZipList:
1✔
277
                entries, extra, err := dec.readZipListZSet()
1✔
278
                if err != nil {
1✔
UNCOV
279
                        return nil, err
×
UNCOV
280
                }
×
281
                base.Extra = extra
1✔
282
                return &model.ZSetObject{
1✔
283
                        BaseObject: base,
1✔
284
                        Entries:    entries,
1✔
285
                }, nil
1✔
286
        case typeZsetListPack:
1✔
287
                entries, extra, err := dec.readListPackZSet()
1✔
288
                if err != nil {
1✔
UNCOV
289
                        return nil, err
×
290
                }
×
291
                base.Extra = extra
1✔
292
                return &model.ZSetObject{
1✔
293
                        BaseObject: base,
1✔
294
                        Entries:    entries,
1✔
295
                }, nil
1✔
296
        case typeStreamListPacks, typeStreamListPacks2, typeStreamListPacks3:
1✔
297
                var version uint = 1
1✔
298
                if flag == typeStreamListPacks2 {
2✔
299
                        version = 2
1✔
300
                } else if flag == typeStreamListPacks3 {
3✔
301
                        version = 3
1✔
302
                }
1✔
303
                stream, err := dec.readStreamListPacks(version)
1✔
304
                if err != nil {
1✔
UNCOV
305
                        return nil, err
×
UNCOV
306
                }
×
307
                stream.BaseObject = base
1✔
308
                return stream, nil
1✔
309
        case typeModule2:
1✔
310
                moduleType, val, err := dec.readModuleType()
1✔
311
                if err != nil {
1✔
UNCOV
312
                        return nil, err
×
UNCOV
313
                }
×
314
                return &model.ModuleTypeObject{
1✔
315
                        BaseObject: base,
1✔
316
                        ModuleType: moduleType,
1✔
317
                        Value:      val,
1✔
318
                }, nil
1✔
319
        case typeSetListPack:
1✔
320
                set, extra, err := dec.readListPackSet()
1✔
321
                if err != nil {
1✔
UNCOV
322
                        return nil, err
×
UNCOV
323
                }
×
324
                base.Extra = extra
1✔
325
                return &model.SetObject{
1✔
326
                        BaseObject: base,
1✔
327
                        Members:    set,
1✔
328
                }, nil
1✔
UNCOV
329
        case typeHashWithHfe, typeHashWithHfeRc:
×
UNCOV
330
                hash, expire, err := dec.readHashMapEx(func() bool { return flag == typeHashWithHfeRc }())
×
331
                if err != nil {
×
332
                        return nil, err
×
UNCOV
333
                }
×
UNCOV
334
                return &model.HashObject{
×
UNCOV
335
                        BaseObject:       base,
×
336
                        Hash:             hash,
×
337
                        FieldExpirations: expire,
×
338
                }, nil
×
339
        case typeHashListPackWithHfe, typeHashListPackWithHfeRc:
×
340
                m, e, extra, err := dec.readListPackHashEx(func() bool { return flag == typeHashListPackWithHfeRc }())
×
341
                if err != nil {
×
UNCOV
342
                        return nil, err
×
UNCOV
343
                }
×
UNCOV
344
                base.Extra = extra
×
345
                return &model.HashObject{
×
346
                        BaseObject:       base,
×
UNCOV
347
                        Hash:             m,
×
UNCOV
348
                        FieldExpirations: e,
×
UNCOV
349
                }, nil
×
350
        }
UNCOV
351
        return nil, fmt.Errorf("unknown type flag: %b", flag)
×
352
}
353

354
func (dec *Decoder) parse(cb func(object model.RedisObject) bool) error {
1✔
355
        var dbIndex int
1✔
356
        var expireMs int64
1✔
357
        for {
2✔
358
                b, err := dec.readByte()
1✔
359
                if err != nil {
1✔
UNCOV
360
                        return err
×
UNCOV
361
                }
×
362
                if b == opCodeEOF {
2✔
363
                        break
1✔
364
                } else if b == opCodeSelectDB {
2✔
365
                        dbIndex64, _, err := dec.readLength()
1✔
366
                        if err != nil {
1✔
UNCOV
367
                                return err
×
368
                        }
×
369
                        dbIndex = int(dbIndex64)
1✔
370
                        continue
1✔
371
                } else if b == opCodeExpireTime {
1✔
UNCOV
372
                        err = dec.readFull(dec.buffer[:4])
×
UNCOV
373
                        if err != nil {
×
UNCOV
374
                                return err
×
375
                        }
×
376
                        expireMs = int64(binary.LittleEndian.Uint32(dec.buffer)) * 1000
×
UNCOV
377
                        continue
×
378
                } else if b == opCodeExpireTimeMs {
2✔
379
                        err = dec.readFull(dec.buffer)
1✔
380
                        if err != nil {
1✔
UNCOV
381
                                return err
×
UNCOV
382
                        }
×
383
                        expireMs = int64(binary.LittleEndian.Uint64(dec.buffer))
1✔
384
                        continue
1✔
385
                } else if b == opCodeResizeDB {
2✔
386
                        keyCount, _, err := dec.readLength()
1✔
387
                        if err != nil {
1✔
UNCOV
388
                                return err
×
UNCOV
389
                        }
×
390
                        ttlCount, _, err := dec.readLength()
1✔
391
                        if err != nil {
1✔
UNCOV
392
                                err = errors.New("Parse Aux value failed: " + err.Error())
×
UNCOV
393
                                break
×
394
                        }
395
                        if dec.withSpecialOpCode {
2✔
396
                                obj := &model.DBSizeObject{
1✔
397
                                        BaseObject: &model.BaseObject{},
1✔
398
                                }
1✔
399
                                obj.DB = dbIndex
1✔
400
                                obj.KeyCount = keyCount
1✔
401
                                obj.TTLCount = ttlCount
1✔
402
                                tbc := cb(obj)
1✔
403
                                if !tbc {
1✔
404
                                        break
×
405
                                }
406
                        }
407
                        continue
1✔
408
                } else if b == opCodeAux {
2✔
409
                        key, err := dec.readString()
1✔
410
                        if err != nil {
1✔
411
                                return err
×
UNCOV
412
                        }
×
413
                        value, err := dec.readString()
1✔
414
                        if err != nil {
1✔
415
                                err = errors.New("Parse Aux value failed: " + err.Error())
×
416
                                break
×
417
                        }
418
                        if dec.withSpecialOpCode {
2✔
419
                                obj := &model.AuxObject{
1✔
420
                                        BaseObject: &model.BaseObject{},
1✔
421
                                }
1✔
422
                                obj.Key = unsafeBytes2Str(key)
1✔
423
                                obj.Value = unsafeBytes2Str(value)
1✔
424
                                tbc := cb(obj)
1✔
425
                                if !tbc {
1✔
UNCOV
426
                                        break
×
427
                                }
428
                        }
429
                        continue
1✔
430
                } else if b == opCodeFreq {
1✔
UNCOV
431
                        _, err = dec.readByte()
×
UNCOV
432
                        if err != nil {
×
UNCOV
433
                                return err
×
434
                        }
×
UNCOV
435
                        continue
×
436
                } else if b == opCodeIdle {
1✔
UNCOV
437
                        _, _, err = dec.readLength()
×
UNCOV
438
                        if err != nil {
×
UNCOV
439
                                return err
×
UNCOV
440
                        }
×
UNCOV
441
                        continue
×
442
                } else if b == opCodeModuleAux {
1✔
UNCOV
443
                        _, _, err = dec.readModuleType()
×
UNCOV
444
                        if err != nil {
×
UNCOV
445
                                return err
×
UNCOV
446
                        }
×
447
                        continue
×
448
                }
449
                key, err := dec.readString()
1✔
450
                if err != nil {
1✔
UNCOV
451
                        return err
×
452
                }
×
453
                base := &model.BaseObject{
1✔
454
                        DB:  dbIndex,
1✔
455
                        Key: unsafeBytes2Str(key),
1✔
456
                }
1✔
457
                if expireMs > 0 {
2✔
458
                        expiration := time.Unix(0, expireMs*int64(time.Millisecond))
1✔
459
                        base.Expiration = &expiration
1✔
460
                        expireMs = 0 // reset expire ms
1✔
461
                }
1✔
462
                obj, err := dec.readObject(b, base)
1✔
463
                if err != nil {
1✔
UNCOV
464
                        return err
×
UNCOV
465
                }
×
466
                base.Size = memprofiler.SizeOfObject(obj)
1✔
467
                base.Type = obj.GetType()
1✔
468
                tbc := cb(obj)
1✔
469
                if !tbc {
1✔
UNCOV
470
                        break
×
471
                }
472
        }
473
        // read crc64 at the end
474
        _ = dec.readFull(dec.buffer)
1✔
475
        return nil
1✔
476
}
477

478
// Parse parses rdb and callback
479
// cb returns true to continue, returns false to stop the iteration
480
func (dec *Decoder) Parse(cb func(object model.RedisObject) bool) (err error) {
1✔
481
        defer func() {
2✔
482
                if err2 := recover(); err2 != nil {
1✔
UNCOV
483
                        err = fmt.Errorf("panic: %v", err2)
×
UNCOV
484
                }
×
485
        }()
486
        err = dec.checkHeader()
1✔
487
        if err != nil {
1✔
UNCOV
488
                return err
×
UNCOV
489
        }
×
490
        return dec.parse(cb)
1✔
491
}
492

UNCOV
493
func (dec *Decoder) GetReadCount() int {
×
UNCOV
494
        return dec.readCount
×
UNCOV
495
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc