github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/jsoni/extra/fuzzy_decoder.go (about) 1 package extra 2 3 import ( 4 "context" 5 "encoding/json" 6 "io" 7 "math" 8 "reflect" 9 "strings" 10 "unsafe" 11 12 "github.com/bingoohuang/gg/pkg/jsoni" 13 "github.com/modern-go/reflect2" 14 ) 15 16 const ( 17 maxUint = ^uint(0) 18 maxInt = int(maxUint >> 1) 19 minInt = -maxInt - 1 20 ) 21 22 // RegisterFuzzyDecoders decode input from PHP with tolerance. 23 // It will handle string/number auto conversation, and treat empty [] as empty struct. 24 func RegisterFuzzyDecoders() { 25 jsoni.RegisterExtension(&tolerateEmptyArrayExtension{}) 26 jsoni.RegisterTypeDecoder("string", &fuzzyStringDecoder{}) 27 jsoni.RegisterTypeDecoder("float32", &fuzzyFloat32Decoder{}) 28 jsoni.RegisterTypeDecoder("float64", &fuzzyFloat64Decoder{}) 29 jsoni.RegisterTypeDecoder("int", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoni.Iterator) { 30 if isFloat { 31 val := iter.ReadFloat64() 32 if val > float64(maxInt) || val < float64(minInt) { 33 iter.ReportError("fuzzy decode int", "exceed range") 34 return 35 } 36 *((*int)(ptr)) = int(val) 37 } else { 38 *((*int)(ptr)) = iter.ReadInt() 39 } 40 }}) 41 jsoni.RegisterTypeDecoder("uint", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoni.Iterator) { 42 if isFloat { 43 val := iter.ReadFloat64() 44 if val > float64(maxUint) || val < 0 { 45 iter.ReportError("fuzzy decode uint", "exceed range") 46 return 47 } 48 *((*uint)(ptr)) = uint(val) 49 } else { 50 *((*uint)(ptr)) = iter.ReadUint() 51 } 52 }}) 53 jsoni.RegisterTypeDecoder("int8", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoni.Iterator) { 54 if isFloat { 55 val := iter.ReadFloat64() 56 if val > float64(math.MaxInt8) || val < float64(math.MinInt8) { 57 iter.ReportError("fuzzy decode int8", "exceed range") 58 return 59 } 60 *((*int8)(ptr)) = int8(val) 61 } else { 62 *((*int8)(ptr)) = iter.ReadInt8() 63 } 64 }}) 65 jsoni.RegisterTypeDecoder("uint8", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoni.Iterator) { 66 if isFloat { 67 val := iter.ReadFloat64() 68 if val > float64(math.MaxUint8) || val < 0 { 69 iter.ReportError("fuzzy decode uint8", "exceed range") 70 return 71 } 72 *((*uint8)(ptr)) = uint8(val) 73 } else { 74 *((*uint8)(ptr)) = iter.ReadUint8() 75 } 76 }}) 77 jsoni.RegisterTypeDecoder("int16", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoni.Iterator) { 78 if isFloat { 79 val := iter.ReadFloat64() 80 if val > float64(math.MaxInt16) || val < float64(math.MinInt16) { 81 iter.ReportError("fuzzy decode int16", "exceed range") 82 return 83 } 84 *((*int16)(ptr)) = int16(val) 85 } else { 86 *((*int16)(ptr)) = iter.ReadInt16() 87 } 88 }}) 89 jsoni.RegisterTypeDecoder("uint16", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoni.Iterator) { 90 if isFloat { 91 val := iter.ReadFloat64() 92 if val > float64(math.MaxUint16) || val < 0 { 93 iter.ReportError("fuzzy decode uint16", "exceed range") 94 return 95 } 96 *((*uint16)(ptr)) = uint16(val) 97 } else { 98 *((*uint16)(ptr)) = iter.ReadUint16() 99 } 100 }}) 101 jsoni.RegisterTypeDecoder("int32", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoni.Iterator) { 102 if isFloat { 103 val := iter.ReadFloat64() 104 if val > float64(math.MaxInt32) || val < float64(math.MinInt32) { 105 iter.ReportError("fuzzy decode int32", "exceed range") 106 return 107 } 108 *((*int32)(ptr)) = int32(val) 109 } else { 110 *((*int32)(ptr)) = iter.ReadInt32() 111 } 112 }}) 113 jsoni.RegisterTypeDecoder("uint32", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoni.Iterator) { 114 if isFloat { 115 val := iter.ReadFloat64() 116 if val > float64(math.MaxUint32) || val < 0 { 117 iter.ReportError("fuzzy decode uint32", "exceed range") 118 return 119 } 120 *((*uint32)(ptr)) = uint32(val) 121 } else { 122 *((*uint32)(ptr)) = iter.ReadUint32() 123 } 124 }}) 125 jsoni.RegisterTypeDecoder("int64", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoni.Iterator) { 126 if isFloat { 127 val := iter.ReadFloat64() 128 if val > float64(math.MaxInt64) || val < float64(math.MinInt64) { 129 iter.ReportError("fuzzy decode int64", "exceed range") 130 return 131 } 132 *((*int64)(ptr)) = int64(val) 133 } else { 134 *((*int64)(ptr)) = iter.ReadInt64() 135 } 136 }}) 137 jsoni.RegisterTypeDecoder("uint64", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoni.Iterator) { 138 if isFloat { 139 val := iter.ReadFloat64() 140 if val > float64(math.MaxUint64) || val < 0 { 141 iter.ReportError("fuzzy decode uint64", "exceed range") 142 return 143 } 144 *((*uint64)(ptr)) = uint64(val) 145 } else { 146 *((*uint64)(ptr)) = iter.ReadUint64() 147 } 148 }}) 149 } 150 151 type tolerateEmptyArrayExtension struct { 152 jsoni.DummyExtension 153 } 154 155 func (extension *tolerateEmptyArrayExtension) DecorateDecoder(typ reflect2.Type, decoder jsoni.ValDecoder) jsoni.ValDecoder { 156 if typ.Kind() == reflect.Struct || typ.Kind() == reflect.Map { 157 return &tolerateEmptyArrayDecoder{decoder} 158 } 159 return decoder 160 } 161 162 type tolerateEmptyArrayDecoder struct { 163 valDecoder jsoni.ValDecoder 164 } 165 166 func (decoder *tolerateEmptyArrayDecoder) Decode(ctx context.Context, ptr unsafe.Pointer, iter *jsoni.Iterator) { 167 if iter.WhatIsNext() == jsoni.ArrayValue { 168 iter.Skip() 169 newIter := iter.Pool().BorrowIterator([]byte("{}")) 170 defer iter.Pool().ReturnIterator(newIter) 171 decoder.valDecoder.Decode(ctx, ptr, newIter) 172 } else { 173 decoder.valDecoder.Decode(ctx, ptr, iter) 174 } 175 } 176 177 type fuzzyStringDecoder struct{} 178 179 func (decoder *fuzzyStringDecoder) Decode(ctx context.Context, ptr unsafe.Pointer, iter *jsoni.Iterator) { 180 valueType := iter.WhatIsNext() 181 switch valueType { 182 case jsoni.NumberValue: 183 var number json.Number 184 iter.ReadVal(ctx, &number) 185 *((*string)(ptr)) = string(number) 186 case jsoni.StringValue: 187 *((*string)(ptr)) = iter.ReadString() 188 case jsoni.NilValue: 189 iter.Skip() 190 *((*string)(ptr)) = "" 191 default: 192 iter.ReportError("fuzzyStringDecoder", "not number or string") 193 } 194 } 195 196 type fuzzyIntegerDecoder struct { 197 fun func(isFloat bool, ptr unsafe.Pointer, iter *jsoni.Iterator) 198 } 199 200 func (decoder *fuzzyIntegerDecoder) Decode(ctx context.Context, ptr unsafe.Pointer, iter *jsoni.Iterator) { 201 valueType := iter.WhatIsNext() 202 var str string 203 switch valueType { 204 case jsoni.NumberValue: 205 var number json.Number 206 iter.ReadVal(ctx, &number) 207 str = string(number) 208 case jsoni.StringValue: 209 str = iter.ReadString() 210 case jsoni.BoolValue: 211 if iter.ReadBool() { 212 str = "1" 213 } else { 214 str = "0" 215 } 216 case jsoni.NilValue: 217 iter.Skip() 218 str = "0" 219 default: 220 iter.ReportError("fuzzyIntegerDecoder", "not number or string") 221 } 222 if len(str) == 0 { 223 str = "0" 224 } 225 newIter := iter.Pool().BorrowIterator([]byte(str)) 226 defer iter.Pool().ReturnIterator(newIter) 227 isFloat := strings.IndexByte(str, '.') != -1 228 decoder.fun(isFloat, ptr, newIter) 229 if newIter.Error != nil && newIter.Error != io.EOF { 230 iter.Error = newIter.Error 231 } 232 } 233 234 type fuzzyFloat32Decoder struct{} 235 236 func (decoder *fuzzyFloat32Decoder) Decode(ctx context.Context, ptr unsafe.Pointer, iter *jsoni.Iterator) { 237 valueType := iter.WhatIsNext() 238 var str string 239 switch valueType { 240 case jsoni.NumberValue: 241 *((*float32)(ptr)) = iter.ReadFloat32() 242 case jsoni.StringValue: 243 str = iter.ReadString() 244 newIter := iter.Pool().BorrowIterator([]byte(str)) 245 defer iter.Pool().ReturnIterator(newIter) 246 *((*float32)(ptr)) = newIter.ReadFloat32() 247 if newIter.Error != nil && newIter.Error != io.EOF { 248 iter.Error = newIter.Error 249 } 250 case jsoni.BoolValue: 251 // support bool to float32 252 if iter.ReadBool() { 253 *((*float32)(ptr)) = 1 254 } else { 255 *((*float32)(ptr)) = 0 256 } 257 case jsoni.NilValue: 258 iter.Skip() 259 *((*float32)(ptr)) = 0 260 default: 261 iter.ReportError("fuzzyFloat32Decoder", "not number or string") 262 } 263 } 264 265 type fuzzyFloat64Decoder struct{} 266 267 func (decoder *fuzzyFloat64Decoder) Decode(ctx context.Context, ptr unsafe.Pointer, iter *jsoni.Iterator) { 268 valueType := iter.WhatIsNext() 269 var str string 270 switch valueType { 271 case jsoni.NumberValue: 272 *((*float64)(ptr)) = iter.ReadFloat64() 273 case jsoni.StringValue: 274 str = iter.ReadString() 275 newIter := iter.Pool().BorrowIterator([]byte(str)) 276 defer iter.Pool().ReturnIterator(newIter) 277 *((*float64)(ptr)) = newIter.ReadFloat64() 278 if newIter.Error != nil && newIter.Error != io.EOF { 279 iter.Error = newIter.Error 280 } 281 case jsoni.BoolValue: 282 // support bool to float64 283 if iter.ReadBool() { 284 *((*float64)(ptr)) = 1 285 } else { 286 *((*float64)(ptr)) = 0 287 } 288 case jsoni.NilValue: 289 iter.Skip() 290 *((*float64)(ptr)) = 0 291 default: 292 iter.ReportError("fuzzyFloat64Decoder", "not number or string") 293 } 294 }