github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/json/parser.gno (about) 1 package json 2 3 import ( 4 "bytes" 5 "errors" 6 "strconv" 7 8 el "gno.land/p/demo/json/eisel_lemire" 9 ) 10 11 const ( 12 absMinInt64 = 1 << 63 13 maxInt64 = absMinInt64 - 1 14 maxUint64 = 1<<64 - 1 15 ) 16 17 const unescapeStackBufSize = 64 18 19 // PaseStringLiteral parses a string from the given byte slice. 20 func ParseStringLiteral(data []byte) (string, error) { 21 var buf [unescapeStackBufSize]byte 22 23 bf, err := Unescape(data, buf[:]) 24 if err != nil { 25 return "", errors.New("invalid string input found while parsing string value") 26 } 27 28 return string(bf), nil 29 } 30 31 // ParseBoolLiteral parses a boolean value from the given byte slice. 32 func ParseBoolLiteral(data []byte) (bool, error) { 33 switch { 34 case bytes.Equal(data, trueLiteral): 35 return true, nil 36 case bytes.Equal(data, falseLiteral): 37 return false, nil 38 default: 39 return false, errors.New("JSON Error: malformed boolean value found while parsing boolean value") 40 } 41 } 42 43 // PaseFloatLiteral parses a float64 from the given byte slice. 44 // 45 // It utilizes double-precision (64-bit) floating-point format as defined 46 // by the IEEE 754 standard, providing a decimal precision of approximately 15 digits. 47 func ParseFloatLiteral(bytes []byte) (float64, error) { 48 if len(bytes) == 0 { 49 return -1, errors.New("JSON Error: empty byte slice found while parsing float value") 50 } 51 52 neg, bytes := trimNegativeSign(bytes) 53 54 var exponentPart []byte 55 for i, c := range bytes { 56 if lower(c) == 'e' { 57 exponentPart = bytes[i+1:] 58 bytes = bytes[:i] 59 break 60 } 61 } 62 63 man, exp10, err := extractMantissaAndExp10(bytes) 64 if err != nil { 65 return -1, err 66 } 67 68 if len(exponentPart) > 0 { 69 exp, err := strconv.Atoi(string(exponentPart)) 70 if err != nil { 71 return -1, errors.New("JSON Error: invalid exponent value found while parsing float value") 72 } 73 exp10 += exp 74 } 75 76 // for fast float64 conversion 77 f, success := el.EiselLemire64(man, exp10, neg) 78 if !success { 79 return 0, nil 80 } 81 82 return f, nil 83 } 84 85 func ParseIntLiteral(bytes []byte) (int64, error) { 86 if len(bytes) == 0 { 87 return 0, errors.New("JSON Error: empty byte slice found while parsing integer value") 88 } 89 90 neg, bytes := trimNegativeSign(bytes) 91 92 var n uint64 = 0 93 for _, c := range bytes { 94 if notDigit(c) { 95 return 0, errors.New("JSON Error: non-digit characters found while parsing integer value") 96 } 97 98 if n > maxUint64/10 { 99 return 0, errors.New("JSON Error: numeric value exceeds the range limit") 100 } 101 102 n *= 10 103 104 n1 := n + uint64(c-'0') 105 if n1 < n { 106 return 0, errors.New("JSON Error: numeric value exceeds the range limit") 107 } 108 109 n = n1 110 } 111 112 if n > maxInt64 { 113 if neg && n == absMinInt64 { 114 return -absMinInt64, nil 115 } 116 117 return 0, errors.New("JSON Error: numeric value exceeds the range limit") 118 } 119 120 if neg { 121 return -int64(n), nil 122 } 123 124 return int64(n), nil 125 } 126 127 // extractMantissaAndExp10 parses a byte slice representing a decimal number and extracts the mantissa and the exponent of its base-10 representation. 128 // It iterates through the bytes, constructing the mantissa by treating each byte as a digit. 129 // If a decimal point is encountered, the function keeps track of the position of the decimal point to calculate the exponent. 130 // The function ensures that: 131 // - The number contains at most one decimal point. 132 // - All characters in the byte slice are digits or a single decimal point. 133 // - The resulting mantissa does not overflow a uint64. 134 func extractMantissaAndExp10(bytes []byte) (uint64, int, error) { 135 var ( 136 man uint64 137 exp10 int 138 decimalFound bool 139 ) 140 141 for _, c := range bytes { 142 if c == dot { 143 if decimalFound { 144 return 0, 0, errors.New("JSON Error: multiple decimal points found while parsing float value") 145 } 146 decimalFound = true 147 continue 148 } 149 150 if notDigit(c) { 151 return 0, 0, errors.New("JSON Error: non-digit characters found while parsing integer value") 152 } 153 154 digit := uint64(c - '0') 155 156 if man > (maxUint64-digit)/10 { 157 return 0, 0, errors.New("JSON Error: numeric value exceeds the range limit") 158 } 159 160 man = man*10 + digit 161 162 if decimalFound { 163 exp10-- 164 } 165 } 166 167 return man, exp10, nil 168 } 169 170 func trimNegativeSign(bytes []byte) (bool, []byte) { 171 if bytes[0] == minus { 172 return true, bytes[1:] 173 } 174 175 return false, bytes 176 } 177 178 func notDigit(c byte) bool { 179 return (c & 0xF0) != 0x30 180 } 181 182 // lower converts a byte to lower case if it is an uppercase letter. 183 func lower(c byte) byte { 184 return c | 0x20 185 }