@@ -1085,7 +1085,11 @@ func (d *ReflectionDecoder) decodeStructWithFields(
10851085 }
10861086 // The string() does not create a copy due to this compiler
10871087 // optimization: https://github.com/golang/go/issues/3512
1088- fieldInfo , ok := fields .namedFields [string (key )]
1088+ fingerprint := fieldKeyFingerprint (key )
1089+ fieldInfo , ok := fields .fieldForFingerprint (fingerprint )
1090+ if ok && (fieldInfo == nil || fieldInfo .name != string (key )) {
1091+ fieldInfo , ok = fields .namedFields [string (key )]
1092+ }
10891093 if ! ok {
10901094 offset , err = d .nextValueOffset (offset , 1 )
10911095 if err != nil {
@@ -1373,8 +1377,69 @@ type fieldInfo struct {
13731377}
13741378
13751379type fieldsType struct {
1376- namedFields map [string ]* fieldInfo // Map from field name to field info
1377- validationErr error
1380+ validationErr error
1381+ namedFields map [string ]* fieldInfo // Map from field name to field info
1382+ fingerprintFields []fingerprintField
1383+ }
1384+
1385+ type fingerprintField struct {
1386+ field * fieldInfo
1387+ fingerprint uint64
1388+ }
1389+
1390+ func (fs * fieldsType ) fieldForFingerprint (fingerprint uint64 ) (* fieldInfo , bool ) {
1391+ mask := uint64 (len (fs .fingerprintFields ) - 1 )
1392+ index := (fingerprint ^ (fingerprint >> 16 ) ^ (fingerprint >> 32 )) & mask
1393+ for {
1394+ entry := fs .fingerprintFields [index ]
1395+ if entry .fingerprint == 0 {
1396+ return nil , false
1397+ }
1398+ if entry .fingerprint == fingerprint {
1399+ return entry .field , true
1400+ }
1401+ index = (index + 1 ) & mask
1402+ }
1403+ }
1404+
1405+ func fieldKeyFingerprint (key []byte ) uint64 {
1406+ // Length plus the first and last two bytes distinguish ordinary MMDB
1407+ // field names cheaply. Collisions fall back to the full string map.
1408+ n := len (key )
1409+ fingerprint := uint64 (n ) << 32
1410+ if n > 0 {
1411+ fingerprint |= uint64 (key [0 ]) << 24
1412+ fingerprint |= uint64 (key [n - 1 ]) << 16
1413+ }
1414+ if n > 1 {
1415+ fingerprint |= uint64 (key [1 ]) << 8
1416+ fingerprint |= uint64 (key [n - 2 ])
1417+ }
1418+ return fingerprint
1419+ }
1420+
1421+ func makeFingerprintFields (namedFields map [string ]* fieldInfo ) []fingerprintField {
1422+ tableSize := 1
1423+ for tableSize < len (namedFields )* 2 {
1424+ tableSize *= 2
1425+ }
1426+ fingerprintFields := make ([]fingerprintField , tableSize )
1427+ mask := uint64 (tableSize - 1 )
1428+ for _ , field := range namedFields {
1429+ fingerprint := fieldKeyFingerprint ([]byte (field .name ))
1430+ index := (fingerprint ^ (fingerprint >> 16 ) ^ (fingerprint >> 32 )) & mask
1431+ for fingerprintFields [index ].fingerprint != 0 &&
1432+ fingerprintFields [index ].fingerprint != fingerprint {
1433+ index = (index + 1 ) & mask
1434+ }
1435+ entry := & fingerprintFields [index ]
1436+ if entry .fingerprint != 0 {
1437+ entry .field = nil
1438+ continue
1439+ }
1440+ * entry = fingerprintField {fingerprint : fingerprint , field : field }
1441+ }
1442+ return fingerprintFields
13781443}
13791444
13801445type queueEntry struct {
@@ -1648,8 +1713,9 @@ func makeStructFieldsWithStack(
16481713 }
16491714
16501715 fields := & fieldsType {
1651- namedFields : namedFields ,
1652- validationErr : validationErr ,
1716+ namedFields : namedFields ,
1717+ fingerprintFields : makeFingerprintFields (namedFields ),
1718+ validationErr : validationErr ,
16531719 }
16541720
16551721 // Reindex all fields for optimized access
0 commit comments