1package orderedmap
2
3import (
4 "bytes"
5 "encoding"
6 "encoding/json"
7 "fmt"
8 "reflect"
9 "unicode/utf8"
10
11 "github.com/buger/jsonparser"
12 "github.com/mailru/easyjson/jwriter"
13)
14
15var (
16 _ json.Marshaler = &OrderedMap[int, any]{}
17 _ json.Unmarshaler = &OrderedMap[int, any]{}
18)
19
20// MarshalJSON implements the json.Marshaler interface.
21func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) { //nolint:funlen
22 if om == nil || om.list == nil {
23 return []byte("null"), nil
24 }
25
26 writer := jwriter.Writer{}
27 writer.RawByte('{')
28
29 for pair, firstIteration := om.Oldest(), true; pair != nil; pair = pair.Next() {
30 if firstIteration {
31 firstIteration = false
32 } else {
33 writer.RawByte(',')
34 }
35
36 switch key := any(pair.Key).(type) {
37 case string:
38 writer.String(key)
39 case encoding.TextMarshaler:
40 writer.RawByte('"')
41 writer.Raw(key.MarshalText())
42 writer.RawByte('"')
43 case int:
44 writer.IntStr(key)
45 case int8:
46 writer.Int8Str(key)
47 case int16:
48 writer.Int16Str(key)
49 case int32:
50 writer.Int32Str(key)
51 case int64:
52 writer.Int64Str(key)
53 case uint:
54 writer.UintStr(key)
55 case uint8:
56 writer.Uint8Str(key)
57 case uint16:
58 writer.Uint16Str(key)
59 case uint32:
60 writer.Uint32Str(key)
61 case uint64:
62 writer.Uint64Str(key)
63 default:
64
65 // this switch takes care of wrapper types around primitive types, such as
66 // type myType string
67 switch keyValue := reflect.ValueOf(key); keyValue.Type().Kind() {
68 case reflect.String:
69 writer.String(keyValue.String())
70 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
71 writer.Int64Str(keyValue.Int())
72 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
73 writer.Uint64Str(keyValue.Uint())
74 default:
75 return nil, fmt.Errorf("unsupported key type: %T", key)
76 }
77 }
78
79 writer.RawByte(':')
80 // the error is checked at the end of the function
81 writer.Raw(json.Marshal(pair.Value)) //nolint:errchkjson
82 }
83
84 writer.RawByte('}')
85
86 return dumpWriter(&writer)
87}
88
89func dumpWriter(writer *jwriter.Writer) ([]byte, error) {
90 if writer.Error != nil {
91 return nil, writer.Error
92 }
93
94 var buf bytes.Buffer
95 buf.Grow(writer.Size())
96 if _, err := writer.DumpTo(&buf); err != nil {
97 return nil, err
98 }
99
100 return buf.Bytes(), nil
101}
102
103// UnmarshalJSON implements the json.Unmarshaler interface.
104func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error {
105 if om.list == nil {
106 om.initialize(0)
107 }
108
109 return jsonparser.ObjectEach(
110 data,
111 func(keyData []byte, valueData []byte, dataType jsonparser.ValueType, offset int) error {
112 if dataType == jsonparser.String {
113 // jsonparser removes the enclosing quotes; we need to restore them to make a valid JSON
114 valueData = data[offset-len(valueData)-2 : offset]
115 }
116
117 var key K
118 var value V
119
120 switch typedKey := any(&key).(type) {
121 case *string:
122 s, err := decodeUTF8(keyData)
123 if err != nil {
124 return err
125 }
126 *typedKey = s
127 case encoding.TextUnmarshaler:
128 if err := typedKey.UnmarshalText(keyData); err != nil {
129 return err
130 }
131 case *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64:
132 if err := json.Unmarshal(keyData, typedKey); err != nil {
133 return err
134 }
135 default:
136 // this switch takes care of wrapper types around primitive types, such as
137 // type myType string
138 switch reflect.TypeOf(key).Kind() {
139 case reflect.String:
140 s, err := decodeUTF8(keyData)
141 if err != nil {
142 return err
143 }
144
145 convertedKeyData := reflect.ValueOf(s).Convert(reflect.TypeOf(key))
146 reflect.ValueOf(&key).Elem().Set(convertedKeyData)
147 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
148 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
149 if err := json.Unmarshal(keyData, &key); err != nil {
150 return err
151 }
152 default:
153 return fmt.Errorf("unsupported key type: %T", key)
154 }
155 }
156
157 if err := json.Unmarshal(valueData, &value); err != nil {
158 return err
159 }
160
161 om.Set(key, value)
162 return nil
163 })
164}
165
166func decodeUTF8(input []byte) (string, error) {
167 remaining, offset := input, 0
168 runes := make([]rune, 0, len(remaining))
169
170 for len(remaining) > 0 {
171 r, size := utf8.DecodeRune(remaining)
172 if r == utf8.RuneError && size <= 1 {
173 return "", fmt.Errorf("not a valid UTF-8 string (at position %d): %s", offset, string(input))
174 }
175
176 runes = append(runes, r)
177 remaining = remaining[size:]
178 offset += size
179 }
180
181 return string(runes), nil
182}