@@ -295,6 +295,8 @@ func (t Int32) WriteTo(w writer) (int64, error) {
295295}
296296
297297// Map is the MaxMind DB map type.
298+ //
299+ //nolint:recvcheck // preexisting/interface
298300type Map map [String ]DataType
299301
300302var _ DataType = Map (nil )
@@ -341,6 +343,11 @@ func (t Map) typeNum() typeNum {
341343
342344// UnmarshalMaxMindDB implements the mmdbdata.Unmarshaler interface.
343345func (t * Map ) UnmarshalMaxMindDB (decoder * mmdbdata.Decoder ) error {
346+ return t .unmarshalMaxMindDB (decoder , nil )
347+ }
348+
349+ // unmarshalMaxMindDB is the internal implementation that supports caching.
350+ func (t * Map ) unmarshalMaxMindDB (decoder * mmdbdata.Decoder , cache map [uint ]DataType ) error {
344351 iter , size , err := decoder .ReadMap ()
345352 if err != nil {
346353 return fmt .Errorf ("reading Map: %w" , err )
@@ -352,7 +359,7 @@ func (t *Map) UnmarshalMaxMindDB(decoder *mmdbdata.Decoder) error {
352359 return iterErr
353360 }
354361
355- value , err := decodeDataTypeValue (decoder )
362+ value , err := decodeDataTypeValue (decoder , cache )
356363 if err != nil {
357364 return err
358365 }
@@ -511,6 +518,8 @@ func (t Pointer) WriteTo(w writer) (int64, error) {
511518}
512519
513520// Slice is the MaxMind DB array type.
521+ //
522+ //nolint:recvcheck // preexisting/interface
514523type Slice []DataType
515524
516525var _ DataType = Slice (nil )
@@ -557,6 +566,11 @@ func (t Slice) typeNum() typeNum {
557566
558567// UnmarshalMaxMindDB implements the mmdbdata.Unmarshaler interface.
559568func (t * Slice ) UnmarshalMaxMindDB (decoder * mmdbdata.Decoder ) error {
569+ return t .unmarshalMaxMindDB (decoder , nil )
570+ }
571+
572+ // unmarshalMaxMindDB is the internal implementation that supports caching.
573+ func (t * Slice ) unmarshalMaxMindDB (decoder * mmdbdata.Decoder , cache map [uint ]DataType ) error {
560574 iter , size , err := decoder .ReadSlice ()
561575 if err != nil {
562576 return fmt .Errorf ("reading Slice: %w" , err )
@@ -568,7 +582,7 @@ func (t *Slice) UnmarshalMaxMindDB(decoder *mmdbdata.Decoder) error {
568582 return iterErr
569583 }
570584
571- value , err := decodeDataTypeValue (decoder )
585+ value , err := decodeDataTypeValue (decoder , cache )
572586 if err != nil {
573587 return err
574588 }
@@ -939,63 +953,98 @@ func writeCtrlByte(w writer, t DataType) (int64, error) {
939953 return numBytes , nil
940954}
941955
956+ // isCacheableKind returns true if the given kind is worth caching. Currently,
957+ // we have primarily found a benefit with containers, not scalar values.
958+ // Potentially, longer strings may also benefit from caching, although it
959+ // may be even better to just intern them, either here or directly in
960+ // the maxminddb reader.
961+ func isCacheableKind (kind mmdbdata.Kind ) bool {
962+ return kind == mmdbdata .KindMap || kind == mmdbdata .KindSlice
963+ }
964+
942965// decodeDataTypeValue decodes a value from the decoder and returns the appropriate DataType.
943- func decodeDataTypeValue (decoder * mmdbdata.Decoder ) (DataType , error ) {
966+ // If cache is provided (non-nil), it will check for cached values at the current decoder offset
967+ // and store newly decoded container types (Map, Slice) in the cache. Simple scalar types
968+ // are not cached as they are cheap to decode and caching them would waste memory.
969+ func decodeDataTypeValue (decoder * mmdbdata.Decoder , cache map [uint ]DataType ) (DataType , error ) {
944970 kind , err := decoder .PeekKind ()
945971 if err != nil {
946972 return nil , fmt .Errorf ("peeking kind: %w" , err )
947973 }
948974
975+ // Only check cache if provided and the type is worth caching
976+ // This avoids unnecessary map lookups for scalar types in tight loops
977+ useCache := cache != nil && isCacheableKind (kind )
978+ var offset uint
979+ if useCache {
980+ offset = decoder .Offset ()
981+ if cached , ok := cache [offset ]; ok {
982+ return cached , nil
983+ }
984+ }
985+
986+ var value DataType
949987 switch kind {
950988 case mmdbdata .KindString :
951- var value String
952- err := value .UnmarshalMaxMindDB (decoder )
953- return value , err
989+ var v String
990+ err = v .UnmarshalMaxMindDB (decoder )
991+ value = v
954992 case mmdbdata .KindFloat64 :
955- var value Float64
956- err := value .UnmarshalMaxMindDB (decoder )
957- return value , err
993+ var v Float64
994+ err = v .UnmarshalMaxMindDB (decoder )
995+ value = v
958996 case mmdbdata .KindBytes :
959- var value Bytes
960- err := value .UnmarshalMaxMindDB (decoder )
961- return value , err
997+ var v Bytes
998+ err = v .UnmarshalMaxMindDB (decoder )
999+ value = v
9621000 case mmdbdata .KindUint16 :
963- var value Uint16
964- err := value .UnmarshalMaxMindDB (decoder )
965- return value , err
1001+ var v Uint16
1002+ err = v .UnmarshalMaxMindDB (decoder )
1003+ value = v
9661004 case mmdbdata .KindUint32 :
967- var value Uint32
968- err := value .UnmarshalMaxMindDB (decoder )
969- return value , err
1005+ var v Uint32
1006+ err = v .UnmarshalMaxMindDB (decoder )
1007+ value = v
9701008 case mmdbdata .KindInt32 :
971- var value Int32
972- err := value .UnmarshalMaxMindDB (decoder )
973- return value , err
1009+ var v Int32
1010+ err = v .UnmarshalMaxMindDB (decoder )
1011+ value = v
9741012 case mmdbdata .KindUint64 :
975- var value Uint64
976- err := value .UnmarshalMaxMindDB (decoder )
977- return value , err
1013+ var v Uint64
1014+ err = v .UnmarshalMaxMindDB (decoder )
1015+ value = v
9781016 case mmdbdata .KindUint128 :
979- var value Uint128
980- err := value .UnmarshalMaxMindDB (decoder )
981- return & value , err // Return pointer for Uint128
1017+ var v Uint128
1018+ err = v .UnmarshalMaxMindDB (decoder )
1019+ value = & v // Return pointer for Uint128
9821020 case mmdbdata .KindBool :
983- var value Bool
984- err := value .UnmarshalMaxMindDB (decoder )
985- return value , err
1021+ var v Bool
1022+ err = v .UnmarshalMaxMindDB (decoder )
1023+ value = v
9861024 case mmdbdata .KindFloat32 :
987- var value Float32
988- err := value .UnmarshalMaxMindDB (decoder )
989- return value , err
1025+ var v Float32
1026+ err = v .UnmarshalMaxMindDB (decoder )
1027+ value = v
9901028 case mmdbdata .KindMap :
991- var value Map
992- err := value . UnmarshalMaxMindDB (decoder )
993- return value , err
1029+ var v Map
1030+ err = v . unmarshalMaxMindDB (decoder , cache )
1031+ value = v
9941032 case mmdbdata .KindSlice :
995- var value Slice
996- err := value . UnmarshalMaxMindDB (decoder )
997- return value , err
1033+ var v Slice
1034+ err = v . unmarshalMaxMindDB (decoder , cache )
1035+ value = v
9981036 default :
9991037 return nil , fmt .Errorf ("unsupported data type: %v" , kind )
10001038 }
1039+
1040+ if err != nil {
1041+ return nil , err
1042+ }
1043+
1044+ // Store the decoded value in cache.
1045+ if useCache {
1046+ cache [offset ] = value
1047+ }
1048+
1049+ return value , nil
10011050}
0 commit comments