Skip to content

Commit 58b8530

Browse files
authored
Merge pull request #186 from oschwald/greg/perf
Minor performance improvements
2 parents 7002e02 + 4a9f0b0 commit 58b8530

File tree

3 files changed

+93
-29
lines changed

3 files changed

+93
-29
lines changed

internal/decoder/data_decoder.go

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -424,28 +424,36 @@ func (d *DataDecoder) decodeKey(offset uint) ([]byte, uint, error) {
424424
// the one at the offset passed in. The size bits have different meanings for
425425
// different data types.
426426
func (d *DataDecoder) nextValueOffset(offset, numberToSkip uint) (uint, error) {
427-
if numberToSkip == 0 {
428-
return offset, nil
429-
}
430-
kindNum, size, offset, err := d.decodeCtrlData(offset)
431-
if err != nil {
432-
return 0, err
433-
}
434-
switch kindNum {
435-
case KindPointer:
436-
_, offset, err = d.decodePointer(size, offset)
427+
for numberToSkip > 0 {
428+
kindNum, size, newOffset, err := d.decodeCtrlData(offset)
437429
if err != nil {
438430
return 0, err
439431
}
440-
case KindMap:
441-
numberToSkip += 2 * size
442-
case KindSlice:
443-
numberToSkip += size
444-
case KindBool:
445-
default:
446-
offset += size
432+
433+
switch kindNum {
434+
case KindPointer:
435+
// A pointer value is represented by its pointer token only.
436+
// To skip it, just move past the pointer bytes; do NOT follow
437+
// the pointer target here.
438+
_, ptrEndOffset, err2 := d.decodePointer(size, newOffset)
439+
if err2 != nil {
440+
return 0, err2
441+
}
442+
newOffset = ptrEndOffset
443+
case KindMap:
444+
numberToSkip += 2 * size
445+
case KindSlice:
446+
numberToSkip += size
447+
case KindBool:
448+
// size encodes the boolean; nothing else to skip
449+
default:
450+
newOffset += size
451+
}
452+
453+
offset = newOffset
454+
numberToSkip--
447455
}
448-
return d.nextValueOffset(offset, numberToSkip-1)
456+
return offset, nil
449457
}
450458

451459
func (d *DataDecoder) sizeFromCtrlByte(

reader.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,67 @@ func readNodeBySize(buffer []byte, offset, bit, recordSize uint) (uint, error) {
515515
}
516516
}
517517

518+
// readNodePairBySize reads both left (bit=0) and right (bit=1) child pointers
519+
// for a node at the given base offset according to the record size. This reduces
520+
// duplicate bound checks and byte fetches when both children are needed.
521+
func readNodePairBySize(buffer []byte, baseOffset, recordSize uint) (left, right uint, err error) {
522+
bufferLen := uint(len(buffer))
523+
switch recordSize {
524+
case 24:
525+
// Each child is 3 bytes; total 6 bytes starting at baseOffset
526+
if baseOffset > bufferLen-6 {
527+
return 0, 0, mmdberrors.NewInvalidDatabaseError(
528+
"bounds check failed: insufficient buffer for 24-bit node pair read",
529+
)
530+
}
531+
o := baseOffset
532+
left = (uint(buffer[o]) << 16) | (uint(buffer[o+1]) << 8) | uint(buffer[o+2])
533+
o += 3
534+
right = (uint(buffer[o]) << 16) | (uint(buffer[o+1]) << 8) | uint(buffer[o+2])
535+
return left, right, nil
536+
case 28:
537+
// Left uses high nibble of shared byte, right uses low nibble.
538+
// Layout: [A B C S][D E F] where S provides 4 shared bits for each child
539+
if baseOffset > bufferLen-7 {
540+
return 0, 0, mmdberrors.NewInvalidDatabaseError(
541+
"bounds check failed: insufficient buffer for 28-bit node pair read",
542+
)
543+
}
544+
// Left child (bit=0): uses high nibble of shared byte
545+
shared := uint(buffer[baseOffset+3])
546+
left = ((shared & 0xF0) << 20) |
547+
(uint(buffer[baseOffset]) << 16) |
548+
(uint(buffer[baseOffset+1]) << 8) |
549+
uint(buffer[baseOffset+2])
550+
// Right child (bit=1): uses low nibble of shared byte, next 3 bytes
551+
right = ((shared & 0x0F) << 24) |
552+
(uint(buffer[baseOffset+4]) << 16) |
553+
(uint(buffer[baseOffset+5]) << 8) |
554+
uint(buffer[baseOffset+6])
555+
return left, right, nil
556+
case 32:
557+
// Each child is 4 bytes; total 8 bytes
558+
if baseOffset > bufferLen-8 {
559+
return 0, 0, mmdberrors.NewInvalidDatabaseError(
560+
"bounds check failed: insufficient buffer for 32-bit node pair read",
561+
)
562+
}
563+
o := baseOffset
564+
left = (uint(buffer[o]) << 24) |
565+
(uint(buffer[o+1]) << 16) |
566+
(uint(buffer[o+2]) << 8) |
567+
uint(buffer[o+3])
568+
o += 4
569+
right = (uint(buffer[o]) << 24) |
570+
(uint(buffer[o+1]) << 16) |
571+
(uint(buffer[o+2]) << 8) |
572+
uint(buffer[o+3])
573+
return left, right, nil
574+
default:
575+
return 0, 0, mmdberrors.NewInvalidDatabaseError("unsupported record size")
576+
}
577+
}
578+
518579
func (r *Reader) traverseTree(ip netip.Addr, node uint, stopBit int) (uint, int, error) {
519580
switch r.Metadata.RecordSize {
520581
case 24:

traverse.go

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,12 @@ func (r *Reader) NetworksWithin(prefix netip.Prefix, options ...NetworksOption)
223223
}
224224
ipRight[node.bit>>3] |= 1 << (7 - (node.bit % 8))
225225

226-
offset := node.pointer * r.nodeOffsetMult
227-
rightPointer, err := readNodeBySize(r.buffer, offset, 1, r.Metadata.RecordSize)
226+
baseOffset := node.pointer * r.nodeOffsetMult
227+
leftPointer, rightPointer, err := readNodePairBySize(
228+
r.buffer,
229+
baseOffset,
230+
r.Metadata.RecordSize,
231+
)
228232
if err != nil {
229233
yield(Result{
230234
ip: mappedIP(node.ip),
@@ -241,15 +245,6 @@ func (r *Reader) NetworksWithin(prefix netip.Prefix, options ...NetworksOption)
241245
bit: node.bit,
242246
})
243247

244-
leftPointer, err := readNodeBySize(r.buffer, offset, 0, r.Metadata.RecordSize)
245-
if err != nil {
246-
yield(Result{
247-
ip: mappedIP(node.ip),
248-
prefixLen: uint8(node.bit),
249-
err: err,
250-
})
251-
return
252-
}
253248
node.pointer = leftPointer
254249
}
255250
}

0 commit comments

Comments
 (0)