Skip to content

Commit

Permalink
fix(aarch64): support parse map that key is float (#748)
Browse files Browse the repository at this point in the history
  • Loading branch information
liuq19 authored Feb 21, 2025
1 parent 51ef605 commit 28755a2
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 8 deletions.
7 changes: 6 additions & 1 deletion internal/decoder/optdec/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ func tryCompileKeyUnmarshaler(vt reflect.Type) decKey {
return decodeKeyTextUnmarshaler
}

/* not support map key with `json.Unmarshaler` */
/* NOTE: encoding/json not support map key with `json.Unmarshaler` */
return nil
}

Expand All @@ -413,6 +413,11 @@ func (c *compiler) compileMapKey(vt reflect.Type) decKey {
return decodeKeyU8
case reflect.Uint16:
return decodeKeyU16
// NOTE: actually, encoding/json can't use float as map key
case reflect.Float32:
return decodeFloat32Key
case reflect.Float64:
return decodeFloat64Key
default:
panic(&json.UnmarshalTypeError{Type: vt})
}
Expand Down
27 changes: 20 additions & 7 deletions internal/decoder/optdec/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package optdec

import (
"encoding"
"encoding/json"
"math"
"reflect"
"unsafe"
Expand Down Expand Up @@ -354,26 +353,40 @@ func decodeKeyI16(dec *mapDecoder, raw string, ctx *context) (interface{}, error
return int16(ret), nil
}

func decodeKeyJSONUnmarshaler(dec *mapDecoder, raw string, _ *context) (interface{}, error) {
func decodeKeyTextUnmarshaler(dec *mapDecoder, raw string, ctx *context) (interface{}, error) {
key, err := Unquote(raw)
if err != nil {
return nil, err
}
ret := reflect.New(dec.mapType.Key.Pack()).Interface()
err := ret.(json.Unmarshaler).UnmarshalJSON([]byte(raw))
err = ret.(encoding.TextUnmarshaler).UnmarshalText(rt.Str2Mem(key))
if err != nil {
return nil, err
}
return ret, nil
}

func decodeKeyTextUnmarshaler(dec *mapDecoder, raw string, ctx *context) (interface{}, error) {
func decodeFloat32Key(dec *mapDecoder, raw string, _ *context) (interface{}, error) {
key, err := Unquote(raw)
if err != nil {
return nil, err
}
ret := reflect.New(dec.mapType.Key.Pack()).Interface()
err = ret.(encoding.TextUnmarshaler).UnmarshalText([]byte(key))
ret, err := ParseF64(key)
if err != nil {
return nil, err
}
return ret, nil
if ret > math.MaxFloat32 || ret < -math.MaxFloat32 {
return nil, error_value(key, dec.mapType.Key.Pack())
}
return float32(ret), nil
}

func decodeFloat64Key(dec *mapDecoder, raw string, _ *context) (interface{}, error) {
key, err := Unquote(raw)
if err != nil {
return nil, err
}
return ParseF64(key)
}

type mapDecoder struct {
Expand Down
14 changes: 14 additions & 0 deletions issue_test/issue739_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
// Copyright 2025 CloudWeGo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package issue_test

import (
Expand Down
14 changes: 14 additions & 0 deletions issue_test/issue744_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
// Copyright 2025 CloudWeGo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package issue_test

import (
Expand Down
63 changes: 63 additions & 0 deletions issue_test/issue747_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2025 CloudWeGo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package issue_test

import (
"testing"

"encoding/json"
"github.com/bytedance/sonic"
"github.com/stretchr/testify/assert"
)

func TestIssue747(t *testing.T) {
tests := []struct {
name string
input string
expected interface{}
newfn func() interface{}
}{
{
name: "unmarshal map key float64",
input: `{"1.2":1.8}`,
expected: &map[float64]float64{
1.2: 1.8,
},
newfn: func() interface{} { return new(map[float64]float64) },
},
{
name: "unmarshal map key float32",
input: `{"1.2":1.8}`,
expected: &map[float32]float32{
1.2: 1.8,
},
newfn: func() interface{} { return new(map[float32]float32) },
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sv, jv := tt.newfn(), tt.newfn()
serr := sonic.Unmarshal([]byte(tt.input), &sv)
assert.Equal(t, tt.expected, sv)
assert.NoError(t, serr)

// Note: it is different from encoding/json
jerr := json.Unmarshal([]byte(tt.input), &jv)
assert.NotEqual(t, jerr == nil, serr == nil)
assert.NotEqual(t, jv, sv)
})
}
}

0 comments on commit 28755a2

Please sign in to comment.