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  }