Skip to content

Commit 77b99f8

Browse files
committed
Use Go 1.25 reflect.TypeAssert for custom unmarshalers
Optimize custom unmarshaler type assertion by conditionally using Go 1.25's reflect.TypeAssert function when available, which avoids allocations compared to Interface().(Type) pattern.
1 parent 5aca7cc commit 77b99f8

File tree

5 files changed

+41
-1
lines changed

5 files changed

+41
-1
lines changed

.golangci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ linters:
1919
- gosmopolitan
2020
- inamedparam
2121
- interfacebloat
22+
# Seems unstable. It will sometimes fire and other times not.
23+
- ireturn
2224
- lll
2325
- mnd
2426
- nlreturn

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
networks whose data is an empty map or empty array. This is useful for
1515
databases that store empty maps or arrays for records without meaningful
1616
data. GitHub #172.
17+
- Optimized custom unmarshaler type assertion to use Go 1.25's
18+
`reflect.TypeAssert` when available, reducing allocations in reflection code
19+
paths.
1720

1821
## 2.0.0-beta.8 - 2025-07-15
1922

internal/decoder/reflection.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ func (d *ReflectionDecoder) decodeValue(
318318

319319
// Check if the value implements Unmarshaler interface using type assertion
320320
if result.CanAddr() {
321-
if unmarshaler, ok := result.Addr().Interface().(Unmarshaler); ok {
321+
if unmarshaler, ok := tryTypeAssert(result.Addr()); ok {
322322
decoder := NewDecoder(d.DataDecoder, offset)
323323
if err := unmarshaler.UnmarshalMaxMindDB(decoder); err != nil {
324324
return 0, err
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//go:build go1.25
2+
3+
package decoder
4+
5+
import (
6+
"reflect"
7+
)
8+
9+
// tryTypeAssert attempts to type assert a reflect.Value to the Unmarshaler interface.
10+
// In Go 1.25+, this uses reflect.TypeAssert which avoids allocations compared to
11+
// the traditional Interface().(Type) approach. The value should be the address of the
12+
// struct since UnmarshalMaxMindDB implementations use pointer receivers.
13+
//
14+
//go:inline
15+
func tryTypeAssert(v reflect.Value) (Unmarshaler, bool) {
16+
return reflect.TypeAssert[Unmarshaler](v)
17+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//go:build !go1.25
2+
3+
package decoder
4+
5+
import (
6+
"reflect"
7+
)
8+
9+
// tryTypeAssert attempts to type assert a reflect.Value to the Unmarshaler interface.
10+
// For Go versions before 1.25, this uses the traditional Interface().(Type) approach.
11+
// The value should be the address of the struct since UnmarshalMaxMindDB implementations
12+
// use pointer receivers.
13+
//
14+
//go:inline
15+
func tryTypeAssert(v reflect.Value) (Unmarshaler, bool) {
16+
unmarshaler, ok := v.Interface().(Unmarshaler)
17+
return unmarshaler, ok
18+
}

0 commit comments

Comments
 (0)