| package extra |
| |
| import ( |
| "encoding/json" |
| "io" |
| "math" |
| "reflect" |
| "strings" |
| "unsafe" |
| |
| "github.com/json-iterator/go" |
| "github.com/modern-go/reflect2" |
| ) |
| |
| const maxUint = ^uint(0) |
| const maxInt = int(maxUint >> 1) |
| const minInt = -maxInt - 1 |
| |
| // RegisterFuzzyDecoders decode input from PHP with tolerance. |
| // It will handle string/number auto conversation, and treat empty [] as empty struct. |
| func RegisterFuzzyDecoders() { |
| jsoniter.RegisterExtension(&tolerateEmptyArrayExtension{}) |
| jsoniter.RegisterTypeDecoder("string", &fuzzyStringDecoder{}) |
| jsoniter.RegisterTypeDecoder("float32", &fuzzyFloat32Decoder{}) |
| jsoniter.RegisterTypeDecoder("float64", &fuzzyFloat64Decoder{}) |
| jsoniter.RegisterTypeDecoder("int", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| if isFloat { |
| val := iter.ReadFloat64() |
| if val > float64(maxInt) || val < float64(minInt) { |
| iter.ReportError("fuzzy decode int", "exceed range") |
| return |
| } |
| *((*int)(ptr)) = int(val) |
| } else { |
| *((*int)(ptr)) = iter.ReadInt() |
| } |
| }}) |
| jsoniter.RegisterTypeDecoder("uint", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| if isFloat { |
| val := iter.ReadFloat64() |
| if val > float64(maxUint) || val < 0 { |
| iter.ReportError("fuzzy decode uint", "exceed range") |
| return |
| } |
| *((*uint)(ptr)) = uint(val) |
| } else { |
| *((*uint)(ptr)) = iter.ReadUint() |
| } |
| }}) |
| jsoniter.RegisterTypeDecoder("int8", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| if isFloat { |
| val := iter.ReadFloat64() |
| if val > float64(math.MaxInt8) || val < float64(math.MinInt8) { |
| iter.ReportError("fuzzy decode int8", "exceed range") |
| return |
| } |
| *((*int8)(ptr)) = int8(val) |
| } else { |
| *((*int8)(ptr)) = iter.ReadInt8() |
| } |
| }}) |
| jsoniter.RegisterTypeDecoder("uint8", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| if isFloat { |
| val := iter.ReadFloat64() |
| if val > float64(math.MaxUint8) || val < 0 { |
| iter.ReportError("fuzzy decode uint8", "exceed range") |
| return |
| } |
| *((*uint8)(ptr)) = uint8(val) |
| } else { |
| *((*uint8)(ptr)) = iter.ReadUint8() |
| } |
| }}) |
| jsoniter.RegisterTypeDecoder("int16", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| if isFloat { |
| val := iter.ReadFloat64() |
| if val > float64(math.MaxInt16) || val < float64(math.MinInt16) { |
| iter.ReportError("fuzzy decode int16", "exceed range") |
| return |
| } |
| *((*int16)(ptr)) = int16(val) |
| } else { |
| *((*int16)(ptr)) = iter.ReadInt16() |
| } |
| }}) |
| jsoniter.RegisterTypeDecoder("uint16", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| if isFloat { |
| val := iter.ReadFloat64() |
| if val > float64(math.MaxUint16) || val < 0 { |
| iter.ReportError("fuzzy decode uint16", "exceed range") |
| return |
| } |
| *((*uint16)(ptr)) = uint16(val) |
| } else { |
| *((*uint16)(ptr)) = iter.ReadUint16() |
| } |
| }}) |
| jsoniter.RegisterTypeDecoder("int32", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| if isFloat { |
| val := iter.ReadFloat64() |
| if val > float64(math.MaxInt32) || val < float64(math.MinInt32) { |
| iter.ReportError("fuzzy decode int32", "exceed range") |
| return |
| } |
| *((*int32)(ptr)) = int32(val) |
| } else { |
| *((*int32)(ptr)) = iter.ReadInt32() |
| } |
| }}) |
| jsoniter.RegisterTypeDecoder("uint32", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| if isFloat { |
| val := iter.ReadFloat64() |
| if val > float64(math.MaxUint32) || val < 0 { |
| iter.ReportError("fuzzy decode uint32", "exceed range") |
| return |
| } |
| *((*uint32)(ptr)) = uint32(val) |
| } else { |
| *((*uint32)(ptr)) = iter.ReadUint32() |
| } |
| }}) |
| jsoniter.RegisterTypeDecoder("int64", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| if isFloat { |
| val := iter.ReadFloat64() |
| if val > float64(math.MaxInt64) || val < float64(math.MinInt64) { |
| iter.ReportError("fuzzy decode int64", "exceed range") |
| return |
| } |
| *((*int64)(ptr)) = int64(val) |
| } else { |
| *((*int64)(ptr)) = iter.ReadInt64() |
| } |
| }}) |
| jsoniter.RegisterTypeDecoder("uint64", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| if isFloat { |
| val := iter.ReadFloat64() |
| if val > float64(math.MaxUint64) || val < 0 { |
| iter.ReportError("fuzzy decode uint64", "exceed range") |
| return |
| } |
| *((*uint64)(ptr)) = uint64(val) |
| } else { |
| *((*uint64)(ptr)) = iter.ReadUint64() |
| } |
| }}) |
| } |
| |
| type tolerateEmptyArrayExtension struct { |
| jsoniter.DummyExtension |
| } |
| |
| func (extension *tolerateEmptyArrayExtension) DecorateDecoder(typ reflect2.Type, decoder jsoniter.ValDecoder) jsoniter.ValDecoder { |
| if typ.Kind() == reflect.Struct || typ.Kind() == reflect.Map { |
| return &tolerateEmptyArrayDecoder{decoder} |
| } |
| return decoder |
| } |
| |
| type tolerateEmptyArrayDecoder struct { |
| valDecoder jsoniter.ValDecoder |
| } |
| |
| func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| if iter.WhatIsNext() == jsoniter.ArrayValue { |
| iter.Skip() |
| newIter := iter.Pool().BorrowIterator([]byte("{}")) |
| defer iter.Pool().ReturnIterator(newIter) |
| decoder.valDecoder.Decode(ptr, newIter) |
| } else { |
| decoder.valDecoder.Decode(ptr, iter) |
| } |
| } |
| |
| type fuzzyStringDecoder struct { |
| } |
| |
| func (decoder *fuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| valueType := iter.WhatIsNext() |
| switch valueType { |
| case jsoniter.NumberValue: |
| var number json.Number |
| iter.ReadVal(&number) |
| *((*string)(ptr)) = string(number) |
| case jsoniter.StringValue: |
| *((*string)(ptr)) = iter.ReadString() |
| case jsoniter.NilValue: |
| iter.Skip() |
| *((*string)(ptr)) = "" |
| default: |
| iter.ReportError("fuzzyStringDecoder", "not number or string") |
| } |
| } |
| |
| type fuzzyIntegerDecoder struct { |
| fun func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) |
| } |
| |
| func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| valueType := iter.WhatIsNext() |
| var str string |
| switch valueType { |
| case jsoniter.NumberValue: |
| var number json.Number |
| iter.ReadVal(&number) |
| str = string(number) |
| case jsoniter.StringValue: |
| str = iter.ReadString() |
| case jsoniter.BoolValue: |
| if iter.ReadBool() { |
| str = "1" |
| } else { |
| str = "0" |
| } |
| case jsoniter.NilValue: |
| iter.Skip() |
| str = "0" |
| default: |
| iter.ReportError("fuzzyIntegerDecoder", "not number or string") |
| } |
| if len(str) == 0 { |
| str = "0" |
| } |
| newIter := iter.Pool().BorrowIterator([]byte(str)) |
| defer iter.Pool().ReturnIterator(newIter) |
| isFloat := strings.IndexByte(str, '.') != -1 |
| decoder.fun(isFloat, ptr, newIter) |
| if newIter.Error != nil && newIter.Error != io.EOF { |
| iter.Error = newIter.Error |
| } |
| } |
| |
| type fuzzyFloat32Decoder struct { |
| } |
| |
| func (decoder *fuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| valueType := iter.WhatIsNext() |
| var str string |
| switch valueType { |
| case jsoniter.NumberValue: |
| *((*float32)(ptr)) = iter.ReadFloat32() |
| case jsoniter.StringValue: |
| str = iter.ReadString() |
| newIter := iter.Pool().BorrowIterator([]byte(str)) |
| defer iter.Pool().ReturnIterator(newIter) |
| *((*float32)(ptr)) = newIter.ReadFloat32() |
| if newIter.Error != nil && newIter.Error != io.EOF { |
| iter.Error = newIter.Error |
| } |
| case jsoniter.BoolValue: |
| // support bool to float32 |
| if iter.ReadBool() { |
| *((*float32)(ptr)) = 1 |
| } else { |
| *((*float32)(ptr)) = 0 |
| } |
| case jsoniter.NilValue: |
| iter.Skip() |
| *((*float32)(ptr)) = 0 |
| default: |
| iter.ReportError("fuzzyFloat32Decoder", "not number or string") |
| } |
| } |
| |
| type fuzzyFloat64Decoder struct { |
| } |
| |
| func (decoder *fuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
| valueType := iter.WhatIsNext() |
| var str string |
| switch valueType { |
| case jsoniter.NumberValue: |
| *((*float64)(ptr)) = iter.ReadFloat64() |
| case jsoniter.StringValue: |
| str = iter.ReadString() |
| newIter := iter.Pool().BorrowIterator([]byte(str)) |
| defer iter.Pool().ReturnIterator(newIter) |
| *((*float64)(ptr)) = newIter.ReadFloat64() |
| if newIter.Error != nil && newIter.Error != io.EOF { |
| iter.Error = newIter.Error |
| } |
| case jsoniter.BoolValue: |
| // support bool to float64 |
| if iter.ReadBool() { |
| *((*float64)(ptr)) = 1 |
| } else { |
| *((*float64)(ptr)) = 0 |
| } |
| case jsoniter.NilValue: |
| iter.Skip() |
| *((*float64)(ptr)) = 0 |
| default: |
| iter.ReportError("fuzzyFloat64Decoder", "not number or string") |
| } |
| } |