diff --git a/examples/gno.land/p/demo/json/LICENSE b/examples/gno.land/p/demo/json/LICENSE new file mode 100644 index 00000000000..954b7ef406d --- /dev/null +++ b/examples/gno.land/p/demo/json/LICENSE @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2019 Pyzhov Stepan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/gno.land/p/demo/json/README.md b/examples/gno.land/p/demo/json/README.md new file mode 100644 index 00000000000..86bc9928194 --- /dev/null +++ b/examples/gno.land/p/demo/json/README.md @@ -0,0 +1,170 @@ +# JSON Parser + +The JSON parser is a package that provides functionality for parsing and processing JSON strings. This package accepts JSON strings as byte slices. + +Currently, gno does not [support the `reflect` package](https://docs.gno.land/concepts/effective-gno#reflection-is-never-clear), so it cannot retrieve type information at runtime. Therefore, it is designed to infer and handle type information when parsing JSON strings using a state machine approach. + +After passing through the state machine, JSON strings are represented as the `Node` type. The `Node` type represents nodes for JSON data, including various types such as `ObjectNode`, `ArrayNode`, `StringNode`, `NumberNode`, `BoolNode`, and `NullNode`. + +This package provides methods for manipulating, searching, and extracting the Node type. + +## State Machine + +To parse JSON strings, a [finite state machine](https://en.wikipedia.org/wiki/Finite-state_machine) approach is used. The state machine transitions to the next state based on the current state and the input character while parsing the JSON string. Through this method, type information can be inferred and processed without reflect, and the amount of parser code can be significantly reduced. + +The image below shows the state transitions of the state machine according to the states and input characters. + +```mermaid +stateDiagram-v2 + [*] --> __: Start + __ --> ST: String + __ --> MI: Number + __ --> ZE: Zero + __ --> IN: Integer + __ --> T1: Boolean (true) + __ --> F1: Boolean (false) + __ --> N1: Null + __ --> ec: Empty Object End + __ --> cc: Object End + __ --> bc: Array End + __ --> co: Object Begin + __ --> bo: Array Begin + __ --> cm: Comma + __ --> cl: Colon + __ --> OK: Success/End + ST --> OK: String Complete + MI --> OK: Number Complete + ZE --> OK: Zero Complete + IN --> OK: Integer Complete + T1 --> OK: True Complete + F1 --> OK: False Complete + N1 --> OK: Null Complete + ec --> OK: Empty Object Complete + cc --> OK: Object Complete + bc --> OK: Array Complete + co --> OB: Inside Object + bo --> AR: Inside Array + cm --> KE: Expecting New Key + cm --> VA: Expecting New Value + cl --> VA: Expecting Value + OB --> ST: String in Object (Key) + OB --> ec: Empty Object + OB --> cc: End Object + AR --> ST: String in Array + AR --> bc: End Array + KE --> ST: String as Key + VA --> ST: String as Value + VA --> MI: Number as Value + VA --> T1: True as Value + VA --> F1: False as Value + VA --> N1: Null as Value + OK --> [*]: End +``` + +## Examples + +This package provides parsing functionality along with encoding and decoding functionality. The following examples demonstrate how to use this package. + +### Decoding + +Decoding (or Unmarshaling) is the functionality that converts an input byte slice JSON string into a `Node` type. + +The converted `Node` type allows you to modify the JSON data or search and extract data that meets specific conditions. + +```go +package main + +import ( + "fmt" + "gno.land/p/demo/json" + "gno.land/p/demo/ufmt" +) + +func main() { + node, err := json.Unmarshal([]byte(`{"foo": "var"}`)) + if err != nil { + ufmt.Errorf("error: %v", err) + } + + ufmt.Sprintf("node: %v", node) +} +``` + +### Encoding + +Encoding (or Marshaling) is the functionality that converts JSON data represented as a Node type into a byte slice JSON string. + +> ⚠️ Caution: Converting a large `Node` type into a JSON string may _impact performance_. or might be cause _unexpected behavior_. + +```go +package main + +import ( + "fmt" + "gno.land/p/demo/json" + "gno.land/p/demo/ufmt" +) + +func main() { + node := ObjectNode("", map[string]*Node{ + "foo": StringNode("foo", "bar"), + "baz": NumberNode("baz", 100500), + "qux": NullNode("qux"), + }) + + b, err := json.Marshal(node) + if err != nil { + ufmt.Errorf("error: %v", err) + } + + ufmt.Sprintf("json: %s", string(b)) +} +``` + +### Searching + +Once the JSON data converted into a `Node` type, you can **search** and **extract** data that satisfy specific conditions. For example, you can find data with a specific type or data with a specific key. + +To use this functionality, you can use methods in the `GetXXX` prefixed methods. The `MustXXX` methods also provide the same functionality as the former methods, but they will **panic** if data doesn't satisfies the condition. + +Here is an example of finding data with a specific key. For more examples, please refer to the [node.gno](node.gno) file. + +```go +package main + +import ( + "fmt" + "gno.land/p/demo/json" + "gno.land/p/demo/ufmt" +) + +func main() { + root, err := Unmarshal([]byte(`{"foo": true, "bar": null}`)) + if err != nil { + ufmt.Errorf("error: %v", err) + } + + value, err := root.GetKey("foo") + if err != nil { + ufmt.Errorf("error occurred while getting key, %s", err) + } + + if value.MustBool() != true { + ufmt.Errorf("value is not true") + } + + value, err = root.GetKey("bar") + if err != nil { + t.Errorf("error occurred while getting key, %s", err) + } + + _, err = root.GetKey("baz") + if err == nil { + t.Errorf("key baz is not exist. must be failed") + } +} +``` + +## Contributing + +Please submit any issues or pull requests for this package through the GitHub repository at [gnolang/gno](). diff --git a/examples/gno.land/p/demo/json/buffer.gno b/examples/gno.land/p/demo/json/buffer.gno new file mode 100644 index 00000000000..23fb53fb0ea --- /dev/null +++ b/examples/gno.land/p/demo/json/buffer.gno @@ -0,0 +1,485 @@ +package json + +import ( + "errors" + "io" + "strings" + + "gno.land/p/demo/ufmt" +) + +type buffer struct { + data []byte + length int + index int + + last States + state States + class Classes +} + +// newBuffer creates a new buffer with the given data +func newBuffer(data []byte) *buffer { + return &buffer{ + data: data, + length: len(data), + last: GO, + state: GO, + } +} + +// first retrieves the first non-whitespace (or other escaped) character in the buffer. +func (b *buffer) first() (byte, error) { + for ; b.index < b.length; b.index++ { + c := b.data[b.index] + + if !(c == whiteSpace || c == carriageReturn || c == newLine || c == tab) { + return c, nil + } + } + + return 0, io.EOF +} + +// current returns the byte of the current index. +func (b *buffer) current() (byte, error) { + if b.index >= b.length { + return 0, io.EOF + } + + return b.data[b.index], nil +} + +// next moves to the next byte and returns it. +func (b *buffer) next() (byte, error) { + b.index++ + return b.current() +} + +// step just moves to the next position. +func (b *buffer) step() error { + _, err := b.next() + return err +} + +// move moves the index by the given position. +func (b *buffer) move(pos int) error { + newIndex := b.index + pos + + if newIndex > b.length { + return io.EOF + } + + b.index = newIndex + + return nil +} + +// slice returns the slice from the current index to the given position. +func (b *buffer) slice(pos int) ([]byte, error) { + end := b.index + pos + + if end > b.length { + return nil, io.EOF + } + + return b.data[b.index:end], nil +} + +// sliceFromIndices returns a slice of the buffer's data starting from 'start' up to (but not including) 'stop'. +func (b *buffer) sliceFromIndices(start, stop int) []byte { + if start > b.length { + start = b.length + } + + if stop > b.length { + stop = b.length + } + + return b.data[start:stop] +} + +// skip moves the index to skip the given byte. +func (b *buffer) skip(bs byte) error { + for b.index < b.length { + if b.data[b.index] == bs && !b.backslash() { + return nil + } + + b.index++ + } + + return io.EOF +} + +// skipAny moves the index until it encounters one of the given set of bytes. +func (b *buffer) skipAny(endTokens map[byte]bool) error { + for b.index < b.length { + if _, exists := endTokens[b.data[b.index]]; exists { + return nil + } + + b.index++ + } + + // build error message + var tokens []string + for token := range endTokens { + tokens = append(tokens, string(token)) + } + + return ufmt.Errorf( + "EOF reached before encountering one of the expected tokens: %s", + strings.Join(tokens, ", "), + ) +} + +// skipAndReturnIndex moves the buffer index forward by one and returns the new index. +func (b *buffer) skipAndReturnIndex() (int, error) { + err := b.step() + if err != nil { + return 0, err + } + + return b.index, nil +} + +// skipUntil moves the buffer index forward until it encounters a byte contained in the endTokens set. +func (b *buffer) skipUntil(endTokens map[byte]bool) (int, error) { + for b.index < b.length { + currentByte, err := b.current() + if err != nil { + return b.index, err + } + + // Check if the current byte is in the set of end tokens. + if _, exists := endTokens[currentByte]; exists { + return b.index, nil + } + + b.index++ + } + + return b.index, io.EOF +} + +// significantTokens is a map where the keys are the significant characters in a JSON path. +// The values in the map are all true, which allows us to use the map as a set for quick lookups. +var significantTokens = map[byte]bool{ + dot: true, // access properties of an object + dollarSign: true, // root object + atSign: true, // current object + bracketOpen: true, // start of an array index or filter expression + bracketClose: true, // end of an array index or filter expression +} + +// filterTokens stores the filter expression tokens. +var filterTokens = map[byte]bool{ + aesterisk: true, // wildcard + andSign: true, + orSign: true, +} + +// skipToNextSignificantToken advances the buffer index to the next significant character. +// Significant characters are defined based on the JSON path syntax. +func (b *buffer) skipToNextSignificantToken() { + for b.index < b.length { + current := b.data[b.index] + + if _, ok := significantTokens[current]; ok { + break + } + + b.index++ + } +} + +// backslash checks to see if the number of backslashes before the current index is odd. +// +// This is used to check if the current character is escaped. However, unlike the "unescape" function, +// "backslash" only serves to check the number of backslashes. +func (b *buffer) backslash() bool { + if b.index == 0 { + return false + } + + count := 0 + for i := b.index - 1; ; i-- { + if i >= b.length || b.data[i] != backSlash { + break + } + + count++ + + if i == 0 { + break + } + } + + return count%2 != 0 +} + +// numIndex holds a map of valid numeric characters +var numIndex = map[byte]bool{ + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + '.': true, + 'e': true, + 'E': true, +} + +// pathToken checks if the current token is a valid JSON path token. +func (b *buffer) pathToken() error { + var stack []byte + + inToken := false + inNumber := false + first := b.index + + for b.index < b.length { + c := b.data[b.index] + + switch { + case c == doubleQuote || c == singleQuote: + inToken = true + if err := b.step(); err != nil { + return errors.New("error stepping through buffer") + } + + if err := b.skip(c); err != nil { + return errors.New("unmatched quote in path") + } + + if b.index >= b.length { + return errors.New("unmatched quote in path") + } + + case c == bracketOpen || c == parenOpen: + inToken = true + stack = append(stack, c) + + case c == bracketClose || c == parenClose: + inToken = true + if len(stack) == 0 || (c == bracketClose && stack[len(stack)-1] != bracketOpen) || (c == parenClose && stack[len(stack)-1] != parenOpen) { + return errors.New("mismatched bracket or parenthesis") + } + + stack = stack[:len(stack)-1] + + case pathStateContainsValidPathToken(c): + inToken = true + + case c == plus || c == minus: + if inNumber || (b.index > 0 && numIndex[b.data[b.index-1]]) { + inToken = true + } else if !inToken && (b.index+1 < b.length && numIndex[b.data[b.index+1]]) { + inToken = true + inNumber = true + } else if !inToken { + return errors.New("unexpected operator at start of token") + } + + default: + if len(stack) != 0 || inToken { + inToken = true + } else { + goto end + } + } + + b.index++ + } + +end: + if len(stack) != 0 { + return errors.New("unclosed bracket or parenthesis at end of path") + } + + if first == b.index { + return errors.New("no token found") + } + + if inNumber && !numIndex[b.data[b.index-1]] { + inNumber = false + } + + return nil +} + +func pathStateContainsValidPathToken(c byte) bool { + if _, ok := significantTokens[c]; ok { + return true + } + + if _, ok := filterTokens[c]; ok { + return true + } + + if _, ok := numIndex[c]; ok { + return true + } + + if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' { + return true + } + + return false +} + +func (b *buffer) numeric(token bool) error { + if token { + b.last = GO + } + + for ; b.index < b.length; b.index++ { + b.class = b.getClasses(doubleQuote) + if b.class == __ { + return errors.New("invalid token found while parsing path") + } + + b.state = StateTransitionTable[b.last][b.class] + if b.state == __ { + if token { + break + } + + return errors.New("invalid token found while parsing path") + } + + if b.state < __ { + return nil + } + + if b.state < MI || b.state > E3 { + return nil + } + + b.last = b.state + } + + if b.last != ZE && b.last != IN && b.last != FR && b.last != E3 { + return errors.New("invalid token found while parsing path") + } + + return nil +} + +func (b *buffer) getClasses(c byte) Classes { + if b.data[b.index] >= 128 { + return C_ETC + } + + if c == singleQuote { + return QuoteAsciiClasses[b.data[b.index]] + } + + return AsciiClasses[b.data[b.index]] +} + +func (b *buffer) getState() States { + b.last = b.state + + b.class = b.getClasses(doubleQuote) + if b.class == __ { + return __ + } + + b.state = StateTransitionTable[b.last][b.class] + + return b.state +} + +// string parses a string token from the buffer. +func (b *buffer) string(search byte, token bool) error { + if token { + b.last = GO + } + + for ; b.index < b.length; b.index++ { + b.class = b.getClasses(search) + + if b.class == __ { + return errors.New("invalid token found while parsing path") + } + + b.state = StateTransitionTable[b.last][b.class] + if b.state == __ { + return errors.New("invalid token found while parsing path") + } + + if b.state < __ { + break + } + + b.last = b.state + } + + return nil +} + +func (b *buffer) word(bs []byte) error { + var c byte + + max := len(bs) + index := 0 + + for ; b.index < b.length; b.index++ { + c = b.data[b.index] + + if c != bs[index] { + return errors.New("invalid token found while parsing path") + } + + index++ + if index >= max { + break + } + } + + if index != max { + return errors.New("invalid token found while parsing path") + } + + return nil +} + +func numberKind2f64(value interface{}) (result float64, err error) { + switch typed := value.(type) { + case float64: + result = typed + case float32: + result = float64(typed) + case int: + result = float64(typed) + case int8: + result = float64(typed) + case int16: + result = float64(typed) + case int32: + result = float64(typed) + case int64: + result = float64(typed) + case uint: + result = float64(typed) + case uint8: + result = float64(typed) + case uint16: + result = float64(typed) + case uint32: + result = float64(typed) + case uint64: + result = float64(typed) + default: + err = ufmt.Errorf("invalid number type: %T", value) + } + + return +} diff --git a/examples/gno.land/p/demo/json/buffer_test.gno b/examples/gno.land/p/demo/json/buffer_test.gno new file mode 100644 index 00000000000..a1acce4eba0 --- /dev/null +++ b/examples/gno.land/p/demo/json/buffer_test.gno @@ -0,0 +1,624 @@ +package json + +import ( + "testing" +) + +func TestBufferCurrent(t *testing.T) { + tests := []struct { + name string + buffer *buffer + expected byte + wantErr bool + }{ + { + name: "Valid current byte", + buffer: &buffer{ + data: []byte("test"), + length: 4, + index: 1, + }, + expected: 'e', + wantErr: false, + }, + { + name: "EOF", + buffer: &buffer{ + data: []byte("test"), + length: 4, + index: 4, + }, + expected: 0, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.buffer.current() + if (err != nil) != tt.wantErr { + t.Errorf("buffer.current() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.expected { + t.Errorf("buffer.current() = %v, want %v", got, tt.expected) + } + }) + } +} + +func TestBufferStep(t *testing.T) { + tests := []struct { + name string + buffer *buffer + wantErr bool + }{ + { + name: "Valid step", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + wantErr: false, + }, + { + name: "EOF error", + buffer: &buffer{data: []byte("test"), length: 4, index: 3}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.buffer.step() + if (err != nil) != tt.wantErr { + t.Errorf("buffer.step() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestBufferNext(t *testing.T) { + tests := []struct { + name string + buffer *buffer + want byte + wantErr bool + }{ + { + name: "Valid next byte", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + want: 'e', + wantErr: false, + }, + { + name: "EOF error", + buffer: &buffer{data: []byte("test"), length: 4, index: 3}, + want: 0, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.buffer.next() + if (err != nil) != tt.wantErr { + t.Errorf("buffer.next() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("buffer.next() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestBufferSlice(t *testing.T) { + tests := []struct { + name string + buffer *buffer + pos int + want []byte + wantErr bool + }{ + { + name: "Valid slice -- 0 characters", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + pos: 0, + want: nil, + wantErr: false, + }, + { + name: "Valid slice -- 1 character", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + pos: 1, + want: []byte("t"), + wantErr: false, + }, + { + name: "Valid slice -- 2 characters", + buffer: &buffer{data: []byte("test"), length: 4, index: 1}, + pos: 2, + want: []byte("es"), + wantErr: false, + }, + { + name: "Valid slice -- 3 characters", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + pos: 3, + want: []byte("tes"), + wantErr: false, + }, + { + name: "Valid slice -- 4 characters", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + pos: 4, + want: []byte("test"), + wantErr: false, + }, + { + name: "EOF error", + buffer: &buffer{data: []byte("test"), length: 4, index: 3}, + pos: 2, + want: nil, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.buffer.slice(tt.pos) + if (err != nil) != tt.wantErr { + t.Errorf("buffer.slice() error = %v, wantErr %v", err, tt.wantErr) + return + } + if string(got) != string(tt.want) { + t.Errorf("buffer.slice() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestBufferMove(t *testing.T) { + tests := []struct { + name string + buffer *buffer + pos int + wantErr bool + wantIdx int + }{ + { + name: "Valid move", + buffer: &buffer{data: []byte("test"), length: 4, index: 1}, + pos: 2, + wantErr: false, + wantIdx: 3, + }, + { + name: "Move beyond length", + buffer: &buffer{data: []byte("test"), length: 4, index: 1}, + pos: 4, + wantErr: true, + wantIdx: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.buffer.move(tt.pos) + if (err != nil) != tt.wantErr { + t.Errorf("buffer.move() error = %v, wantErr %v", err, tt.wantErr) + } + if tt.buffer.index != tt.wantIdx { + t.Errorf("buffer.move() index = %v, want %v", tt.buffer.index, tt.wantIdx) + } + }) + } +} + +func TestBufferSkip(t *testing.T) { + tests := []struct { + name string + buffer *buffer + b byte + wantErr bool + }{ + { + name: "Skip byte", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + b: 'e', + wantErr: false, + }, + { + name: "Skip to EOF", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + b: 'x', + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.buffer.skip(tt.b) + if (err != nil) != tt.wantErr { + t.Errorf("buffer.skip() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestBufferSkipAny(t *testing.T) { + tests := []struct { + name string + buffer *buffer + s map[byte]bool + wantErr bool + }{ + { + name: "Skip any valid byte", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + s: map[byte]bool{'e': true, 'o': true}, + wantErr: false, + }, + { + name: "Skip any to EOF", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + s: map[byte]bool{'x': true, 'y': true}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.buffer.skipAny(tt.s) + if (err != nil) != tt.wantErr { + t.Errorf("buffer.skipAny() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestSkipToNextSignificantToken(t *testing.T) { + tests := []struct { + name string + input []byte + expected int + }{ + {"No significant chars", []byte("abc"), 3}, + {"One significant char at start", []byte(".abc"), 0}, + {"Significant char in middle", []byte("ab.c"), 2}, + {"Multiple significant chars", []byte("a$.c"), 1}, + {"Significant char at end", []byte("abc$"), 3}, + {"Only significant chars", []byte("$."), 0}, + {"Empty string", []byte(""), 0}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := newBuffer(tt.input) + b.skipToNextSignificantToken() + if b.index != tt.expected { + t.Errorf("after skipToNextSignificantToken(), got index = %v, want %v", b.index, tt.expected) + } + }) + } +} + +func mockBuffer(s string) *buffer { + return newBuffer([]byte(s)) +} + +func TestSkipAndReturnIndex(t *testing.T) { + tests := []struct { + name string + input string + expected int + }{ + {"StartOfString", "", 0}, + {"MiddleOfString", "abcdef", 1}, + {"EndOfString", "abc", 1}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := mockBuffer(tt.input) + got, err := buf.skipAndReturnIndex() + if err != nil && tt.input != "" { // Expect no error unless input is empty + t.Errorf("skipAndReturnIndex() error = %v", err) + } + if got != tt.expected { + t.Errorf("skipAndReturnIndex() = %v, want %v", got, tt.expected) + } + }) + } +} + +func TestSkipUntil(t *testing.T) { + tests := []struct { + name string + input string + tokens map[byte]bool + expected int + }{ + {"SkipToToken", "abcdefg", map[byte]bool{'c': true}, 2}, + {"SkipToEnd", "abcdefg", map[byte]bool{'h': true}, 7}, + {"SkipNone", "abcdefg", map[byte]bool{'a': true}, 0}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := mockBuffer(tt.input) + got, err := buf.skipUntil(tt.tokens) + if err != nil && got != len(tt.input) { // Expect error only if reached end without finding token + t.Errorf("skipUntil() error = %v", err) + } + if got != tt.expected { + t.Errorf("skipUntil() = %v, want %v", got, tt.expected) + } + }) + } +} + +func TestSliceFromIndices(t *testing.T) { + tests := []struct { + name string + input string + start int + end int + expected string + }{ + {"FullString", "abcdefg", 0, 7, "abcdefg"}, + {"Substring", "abcdefg", 2, 5, "cde"}, + {"OutOfBounds", "abcdefg", 5, 10, "fg"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := mockBuffer(tt.input) + got := buf.sliceFromIndices(tt.start, tt.end) + if string(got) != tt.expected { + t.Errorf("sliceFromIndices() = %v, want %v", string(got), tt.expected) + } + }) + } +} + +func TestBufferToken(t *testing.T) { + tests := []struct { + name string + path string + index int + isErr bool + }{ + { + name: "Simple valid path", + path: "@.length", + index: 8, + isErr: false, + }, + { + name: "Path with array expr", + path: "@['foo'].0.bar", + index: 14, + isErr: false, + }, + { + name: "Path with array expr and simple fomula", + path: "@['foo'].[(@.length - 1)].*", + index: 27, + isErr: false, + }, + { + name: "Path with filter expr", + path: "@['foo'].[?(@.bar == 1 & @.baz < @.length)].*", + index: 45, + isErr: false, + }, + { + name: "addition of foo and bar", + path: "@.foo+@.bar", + index: 11, + isErr: false, + }, + { + name: "logical AND of foo and bar", + path: "@.foo && @.bar", + index: 14, + isErr: false, + }, + { + name: "logical OR of foo and bar", + path: "@.foo || @.bar", + index: 14, + isErr: false, + }, + { + name: "accessing third element of foo", + path: "@.foo,3", + index: 7, + isErr: false, + }, + { + name: "accessing last element of array", + path: "@.length-1", + index: 10, + isErr: false, + }, + { + name: "number 1", + path: "1", + index: 1, + isErr: false, + }, + { + name: "float", + path: "3.1e4", + index: 5, + isErr: false, + }, + { + name: "float with minus", + path: "3.1e-4", + index: 6, + isErr: false, + }, + { + name: "float with plus", + path: "3.1e+4", + index: 6, + isErr: false, + }, + { + name: "negative number", + path: "-12345", + index: 6, + isErr: false, + }, + { + name: "negative float", + path: "-3.1e4", + index: 6, + isErr: false, + }, + { + name: "negative float with minus", + path: "-3.1e-4", + index: 7, + isErr: false, + }, + { + name: "negative float with plus", + path: "-3.1e+4", + index: 7, + isErr: false, + }, + { + name: "string number", + path: "'12345'", + index: 7, + isErr: false, + }, + { + name: "string with backslash", + path: "'foo \\'bar '", + index: 12, + isErr: false, + }, + { + name: "string with inner double quotes", + path: "'foo \"bar \"'", + index: 12, + isErr: false, + }, + { + name: "parenthesis 1", + path: "(@abc)", + index: 6, + isErr: false, + }, + { + name: "parenthesis 2", + path: "[()]", + index: 4, + isErr: false, + }, + { + name: "parenthesis mismatch", + path: "[(])", + index: 2, + isErr: true, + }, + { + name: "parenthesis mismatch 2", + path: "(", + index: 1, + isErr: true, + }, + { + name: "parenthesis mismatch 3", + path: "())]", + index: 2, + isErr: true, + }, + { + name: "bracket mismatch", + path: "[()", + index: 3, + isErr: true, + }, + { + name: "bracket mismatch 2", + path: "()]", + index: 2, + isErr: true, + }, + { + name: "path does not close bracket", + path: "@.foo[)", + index: 6, + isErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := newBuffer([]byte(tt.path)) + + err := buf.pathToken() + if tt.isErr { + if err == nil { + t.Errorf("Expected an error for path `%s`, but got none", tt.path) + } + } + + if err == nil && tt.isErr { + t.Errorf("Expected an error for path `%s`, but got none", tt.path) + } + + if buf.index != tt.index { + t.Errorf("Expected final index %d, got %d (token: `%s`) for path `%s`", tt.index, buf.index, string(buf.data[buf.index]), tt.path) + } + }) + } +} + +func TestBufferFirst(t *testing.T) { + tests := []struct { + name string + data []byte + expected byte + }{ + { + name: "Valid first byte", + data: []byte("test"), + expected: 't', + }, + { + name: "Empty buffer", + data: []byte(""), + expected: 0, + }, + { + name: "Whitespace buffer", + data: []byte(" "), + expected: 0, + }, + { + name: "whitespace in middle", + data: []byte("hello world"), + expected: 'h', + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := newBuffer(tt.data) + + got, err := b.first() + if err != nil && tt.expected != 0 { + t.Errorf("Unexpected error: %v", err) + } + + if got != tt.expected { + t.Errorf("Expected first byte to be %q, got %q", tt.expected, got) + } + }) + } +} diff --git a/examples/gno.land/p/demo/json/decode.gno b/examples/gno.land/p/demo/json/decode.gno new file mode 100644 index 00000000000..8a6e2b99846 --- /dev/null +++ b/examples/gno.land/p/demo/json/decode.gno @@ -0,0 +1,370 @@ +// ref: https://github.com/spyzhov/ajson/blob/master/decode.go + +package json + +import ( + "errors" + "io" + + "gno.land/p/demo/ufmt" +) + +// This limits the max nesting depth to prevent stack overflow. +// This is permitted by https://tools.ietf.org/html/rfc7159#section-9 +const maxNestingDepth = 10000 + +// Unmarshal parses the JSON-encoded data and returns a Node. +// The data must be a valid JSON-encoded value. +// +// Usage: +// +// node, err := json.Unmarshal([]byte(`{"key": "value"}`)) +// if err != nil { +// ufmt.Println(err) +// } +// println(node) // {"key": "value"} +func Unmarshal(data []byte) (*Node, error) { + buf := newBuffer(data) + + var ( + state States + key *string + current *Node + nesting int + useKey = func() **string { + tmp := cptrs(key) + key = nil + return &tmp + } + err error + ) + + if _, err = buf.first(); err != nil { + return nil, io.EOF + } + + for { + state = buf.getState() + if state == __ { + return nil, unexpectedTokenError(buf.data, buf.index) + } + + // region state machine + if state >= GO { + switch buf.state { + case ST: // string + if current != nil && current.IsObject() && key == nil { + // key detected + if key, err = getString(buf); err != nil { + return nil, err + } + + buf.state = CO + } else { + current, nesting, err = createNestedNode(current, buf, String, nesting, useKey()) + if err != nil { + return nil, err + } + + err = buf.string(doubleQuote, false) + if err != nil { + return nil, err + } + + current, nesting = updateNode(current, buf, nesting, true) + buf.state = OK + } + + case MI, ZE, IN: // number + current, err = processNumericNode(current, buf, useKey()) + if err != nil { + return nil, err + } + + case T1, F1: // boolean + literal := falseLiteral + if buf.state == T1 { + literal = trueLiteral + } + + current, nesting, err = processLiteralNode(current, buf, Boolean, literal, useKey(), nesting) + if err != nil { + return nil, err + } + + case N1: // null + current, nesting, err = processLiteralNode(current, buf, Null, nullLiteral, useKey(), nesting) + if err != nil { + return nil, err + } + } + } else { + // region action + switch state { + case ec, cc: // } + if key != nil { + return nil, unexpectedTokenError(buf.data, buf.index) + } + + current, nesting, err = updateNodeAndSetBufferState(current, buf, nesting, Object) + if err != nil { + return nil, err + } + + case bc: // ] + current, nesting, err = updateNodeAndSetBufferState(current, buf, nesting, Array) + if err != nil { + return nil, err + } + + case co, bo: // { [ + valTyp, bState := Object, OB + if state == bo { + valTyp, bState = Array, AR + } + + current, nesting, err = createNestedNode(current, buf, valTyp, nesting, useKey()) + if err != nil { + return nil, err + } + + buf.state = bState + + case cm: // , + if current == nil { + return nil, unexpectedTokenError(buf.data, buf.index) + } + + if !current.isContainer() { + return nil, unexpectedTokenError(buf.data, buf.index) + } + + if current.IsObject() { + buf.state = KE // key expected + } else { + buf.state = VA // value expected + } + + case cl: // : + if current == nil || !current.IsObject() || key == nil { + return nil, unexpectedTokenError(buf.data, buf.index) + } + + buf.state = VA + + default: + return nil, unexpectedTokenError(buf.data, buf.index) + } + } + + if buf.step() != nil { + break + } + + if _, err = buf.first(); err != nil { + err = nil + break + } + } + + if current == nil || buf.state != OK { + return nil, io.EOF + } + + root := current.root() + if !root.ready() { + return nil, io.EOF + } + + return root, err +} + +// UnmarshalSafe parses the JSON-encoded data and returns a Node. +func UnmarshalSafe(data []byte) (*Node, error) { + var safe []byte + safe = append(safe, data...) + return Unmarshal(safe) +} + +// processNumericNode creates a new node, processes a numeric value, +// sets the node's borders, and moves to the previous node. +func processNumericNode(current *Node, buf *buffer, key **string) (*Node, error) { + var err error + current, err = createNode(current, buf, Number, key) + if err != nil { + return nil, err + } + + if err = buf.numeric(false); err != nil { + return nil, err + } + + current.borders[1] = buf.index + if current.prev != nil { + current = current.prev + } + + buf.index -= 1 + buf.state = OK + + return current, nil +} + +// processLiteralNode creates a new node, processes a literal value, +// sets the node's borders, and moves to the previous node. +func processLiteralNode( + current *Node, + buf *buffer, + literalType ValueType, + literalValue []byte, + useKey **string, + nesting int, +) (*Node, int, error) { + var err error + current, nesting, err = createLiteralNode(current, buf, literalType, literalValue, useKey, nesting) + if err != nil { + return nil, nesting, err + } + return current, nesting, nil +} + +// isValidContainerType checks if the current node is a valid container (object or array). +// The container must satisfy the following conditions: +// 1. The current node must not be nil. +// 2. The current node must be an object or array. +// 3. The current node must not be ready. +func isValidContainerType(current *Node, nodeType ValueType) bool { + switch nodeType { + case Object: + return current != nil && current.IsObject() && !current.ready() + case Array: + return current != nil && current.IsArray() && !current.ready() + default: + return false + } +} + +// getString extracts a string from the buffer and advances the buffer index past the string. +func getString(b *buffer) (*string, error) { + start := b.index + if err := b.string(doubleQuote, false); err != nil { + return nil, err + } + + value, ok := Unquote(b.data[start:b.index+1], doubleQuote) + if !ok { + return nil, unexpectedTokenError(b.data, start) + } + + return &value, nil +} + +// createNode creates a new node and sets the key if it is not nil. +func createNode( + current *Node, + buf *buffer, + nodeType ValueType, + key **string, +) (*Node, error) { + var err error + current, err = NewNode(current, buf, nodeType, key) + if err != nil { + return nil, err + } + + return current, nil +} + +// createNestedNode creates a new nested node (array or object) and sets the key if it is not nil. +func createNestedNode( + current *Node, + buf *buffer, + nodeType ValueType, + nesting int, + key **string, +) (*Node, int, error) { + var err error + if nesting, err = checkNestingDepth(nesting); err != nil { + return nil, nesting, err + } + + if current, err = createNode(current, buf, nodeType, key); err != nil { + return nil, nesting, err + } + + return current, nesting, nil +} + +// createLiteralNode creates a new literal node and sets the key if it is not nil. +// The literal is a byte slice that represents a boolean or null value. +func createLiteralNode( + current *Node, + buf *buffer, + literalType ValueType, + literal []byte, + useKey **string, + nesting int, +) (*Node, int, error) { + var err error + if current, err = createNode(current, buf, literalType, useKey); err != nil { + return nil, 0, err + } + + if err = buf.word(literal); err != nil { + return nil, 0, err + } + + current, nesting = updateNode(current, buf, nesting, false) + buf.state = OK + + return current, nesting, nil +} + +// updateNode updates the current node and returns the previous node. +func updateNode( + current *Node, buf *buffer, nesting int, decreaseLevel bool, +) (*Node, int) { + current.borders[1] = buf.index + 1 + + prev := current.prev + if prev == nil { + return current, nesting + } + + current = prev + if decreaseLevel { + nesting-- + } + + return current, nesting +} + +// updateNodeAndSetBufferState updates the current node and sets the buffer state to OK. +func updateNodeAndSetBufferState( + current *Node, + buf *buffer, + nesting int, + typ ValueType, +) (*Node, int, error) { + if !isValidContainerType(current, typ) { + return nil, nesting, unexpectedTokenError(buf.data, buf.index) + } + + current, nesting = updateNode(current, buf, nesting, true) + buf.state = OK + + return current, nesting, nil +} + +// checkNestingDepth checks if the nesting depth is within the maximum allowed depth. +func checkNestingDepth(nesting int) (int, error) { + if nesting >= maxNestingDepth { + return nesting, errors.New("maximum nesting depth exceeded") + } + + return nesting + 1, nil +} + +func unexpectedTokenError(data []byte, index int) error { + return ufmt.Errorf("unexpected token at index %d. data %b", index, data) +} diff --git a/examples/gno.land/p/demo/json/decode_test.gno b/examples/gno.land/p/demo/json/decode_test.gno new file mode 100644 index 00000000000..5a04846224e --- /dev/null +++ b/examples/gno.land/p/demo/json/decode_test.gno @@ -0,0 +1,555 @@ +package json + +import ( + "bytes" + "encoding/json" + "testing" +) + +type testNode struct { + name string + input []byte + _type ValueType + value []byte +} + +func simpleValid(test *testNode, t *testing.T) { + root, err := Unmarshal(test.input) + if err != nil { + t.Errorf("Error on Unmarshal(%s): %s", test.input, err.Error()) + } else if root == nil { + t.Errorf("Error on Unmarshal(%s): root is nil", test.name) + } else if root.nodeType != test._type { + t.Errorf("Error on Unmarshal(%s): wrong type", test.name) + } else if !bytes.Equal(root.source(), test.value) { + t.Errorf("Error on Unmarshal(%s): %s != %s", test.name, root.source(), test.value) + } +} + +func simpleInvalid(test *testNode, t *testing.T) { + root, err := Unmarshal(test.input) + if err == nil { + t.Errorf("Error on Unmarshal(%s): error expected, got '%s'", test.name, root.source()) + } else if root != nil { + t.Errorf("Error on Unmarshal(%s): root is not nil", test.name) + } +} + +func simpleCorrupted(name string) *testNode { + return &testNode{name: name, input: []byte(name)} +} + +func TestUnmarshal_StringSimpleSuccess(t *testing.T) { + tests := []*testNode{ + {name: "blank", input: []byte("\"\""), _type: String, value: []byte("\"\"")}, + {name: "char", input: []byte("\"c\""), _type: String, value: []byte("\"c\"")}, + {name: "word", input: []byte("\"cat\""), _type: String, value: []byte("\"cat\"")}, + {name: "spaces", input: []byte(" \"good cat or dog\"\r\n "), _type: String, value: []byte("\"good cat or dog\"")}, + {name: "backslash", input: []byte("\"good \\\"cat\\\"\""), _type: String, value: []byte("\"good \\\"cat\\\"\"")}, + {name: "backslash 2", input: []byte("\"good \\\\\\\"cat\\\"\""), _type: String, value: []byte("\"good \\\\\\\"cat\\\"\"")}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleValid(test, t) + }) + } +} + +func TestUnmarshal_NumericSimpleSuccess(t *testing.T) { + tests := []*testNode{ + {name: "1", input: []byte("1"), _type: Number, value: []byte("1")}, + {name: "-1", input: []byte("-1"), _type: Number, value: []byte("-1")}, + + {name: "1234567890", input: []byte("1234567890"), _type: Number, value: []byte("1234567890")}, + {name: "-123", input: []byte("-123"), _type: Number, value: []byte("-123")}, + + {name: "123.456", input: []byte("123.456"), _type: Number, value: []byte("123.456")}, + {name: "-123.456", input: []byte("-123.456"), _type: Number, value: []byte("-123.456")}, + + {name: "1e3", input: []byte("1e3"), _type: Number, value: []byte("1e3")}, + {name: "1e+3", input: []byte("1e+3"), _type: Number, value: []byte("1e+3")}, + {name: "1e-3", input: []byte("1e-3"), _type: Number, value: []byte("1e-3")}, + {name: "-1e3", input: []byte("-1e3"), _type: Number, value: []byte("-1e3")}, + {name: "-1e-3", input: []byte("-1e-3"), _type: Number, value: []byte("-1e-3")}, + + {name: "1.123e3456", input: []byte("1.123e3456"), _type: Number, value: []byte("1.123e3456")}, + {name: "1.123e-3456", input: []byte("1.123e-3456"), _type: Number, value: []byte("1.123e-3456")}, + {name: "-1.123e3456", input: []byte("-1.123e3456"), _type: Number, value: []byte("-1.123e3456")}, + {name: "-1.123e-3456", input: []byte("-1.123e-3456"), _type: Number, value: []byte("-1.123e-3456")}, + + {name: "1E3", input: []byte("1E3"), _type: Number, value: []byte("1E3")}, + {name: "1E-3", input: []byte("1E-3"), _type: Number, value: []byte("1E-3")}, + {name: "-1E3", input: []byte("-1E3"), _type: Number, value: []byte("-1E3")}, + {name: "-1E-3", input: []byte("-1E-3"), _type: Number, value: []byte("-1E-3")}, + + {name: "1.123E3456", input: []byte("1.123E3456"), _type: Number, value: []byte("1.123E3456")}, + {name: "1.123E-3456", input: []byte("1.123E-3456"), _type: Number, value: []byte("1.123E-3456")}, + {name: "-1.123E3456", input: []byte("-1.123E3456"), _type: Number, value: []byte("-1.123E3456")}, + {name: "-1.123E-3456", input: []byte("-1.123E-3456"), _type: Number, value: []byte("-1.123E-3456")}, + + {name: "-1.123E-3456 with spaces", input: []byte(" \r -1.123E-3456 \t\n"), _type: Number, value: []byte("-1.123E-3456")}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + root, err := Unmarshal(test.input) + if err != nil { + t.Errorf("Error on Unmarshal(%s): %s", test.name, err.Error()) + } else if root == nil { + t.Errorf("Error on Unmarshal(%s): root is nil", test.name) + } else if root.nodeType != test._type { + t.Errorf("Error on Unmarshal(%s): wrong type", test.name) + } else if !bytes.Equal(root.source(), test.value) { + t.Errorf("Error on Unmarshal(%s): %s != %s", test.name, root.source(), test.value) + } + }) + } +} + +func TestUnmarshal_StringSimpleCorrupted(t *testing.T) { + tests := []*testNode{ + {name: "white NL", input: []byte("\"foo\nbar\"")}, + {name: "white R", input: []byte("\"foo\rbar\"")}, + {name: "white Tab", input: []byte("\"foo\tbar\"")}, + {name: "wrong quotes", input: []byte("'cat'")}, + {name: "double string", input: []byte("\"Hello\" \"World\"")}, + {name: "quotes in quotes", input: []byte("\"good \"cat\"\"")}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleInvalid(test, t) + }) + } +} + +func TestUnmarshal_ObjectSimpleSuccess(t *testing.T) { + tests := []*testNode{ + {name: "{}", input: []byte("{}"), _type: Object, value: []byte("{}")}, + {name: `{ \r\n }`, input: []byte("{ \r\n }"), _type: Object, value: []byte("{ \r\n }")}, + {name: `{"key":1}`, input: []byte(`{"key":1}`), _type: Object, value: []byte(`{"key":1}`)}, + {name: `{"key":true}`, input: []byte(`{"key":true}`), _type: Object, value: []byte(`{"key":true}`)}, + {name: `{"key":"value"}`, input: []byte(`{"key":"value"}`), _type: Object, value: []byte(`{"key":"value"}`)}, + {name: `{"foo":"bar","baz":"foo"}`, input: []byte(`{"foo":"bar", "baz":"foo"}`), _type: Object, value: []byte(`{"foo":"bar", "baz":"foo"}`)}, + {name: "spaces", input: []byte(` { "foo" : "bar" , "baz" : "foo" } `), _type: Object, value: []byte(`{ "foo" : "bar" , "baz" : "foo" }`)}, + {name: "nested", input: []byte(`{"foo":{"bar":{"baz":{}}}}`), _type: Object, value: []byte(`{"foo":{"bar":{"baz":{}}}}`)}, + {name: "array", input: []byte(`{"array":[{},{},{"foo":[{"bar":["baz"]}]}]}`), _type: Object, value: []byte(`{"array":[{},{},{"foo":[{"bar":["baz"]}]}]}`)}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleValid(test, t) + }) + } +} + +func TestUnmarshal_ObjectSimpleCorrupted(t *testing.T) { + tests := []*testNode{ + simpleCorrupted("{{{\"key\": \"foo\"{{{{"), + simpleCorrupted("}"), + simpleCorrupted("{ }}}}}}}"), + simpleCorrupted(" }"), + simpleCorrupted("{,}"), + simpleCorrupted("{:}"), + simpleCorrupted("{100000}"), + simpleCorrupted("{1:1}"), + simpleCorrupted("{'1:2,3:4'}"), + simpleCorrupted(`{"d"}`), + simpleCorrupted(`{"foo"}`), + simpleCorrupted(`{"foo":}`), + simpleCorrupted(`{:"foo"}`), + simpleCorrupted(`{"foo":bar}`), + simpleCorrupted(`{"foo":"bar",}`), + simpleCorrupted(`{}{}`), + simpleCorrupted(`{},{}`), + simpleCorrupted(`{[},{]}`), + simpleCorrupted(`{[,]}`), + simpleCorrupted(`{[]}`), + simpleCorrupted(`{}1`), + simpleCorrupted(`1{}`), + simpleCorrupted(`{"x"::1}`), + simpleCorrupted(`{null:null}`), + simpleCorrupted(`{"foo:"bar"}`), + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleInvalid(test, t) + }) + } +} + +func TestUnmarshal_NullSimpleCorrupted(t *testing.T) { + tests := []*testNode{ + {name: "nul", input: []byte("nul")}, + {name: "nil", input: []byte("nil")}, + {name: "nill", input: []byte("nill")}, + {name: "NILL", input: []byte("NILL")}, + {name: "Null", input: []byte("Null")}, + {name: "NULL", input: []byte("NULL")}, + {name: "spaces", input: []byte("Nu ll")}, + {name: "null1", input: []byte("null1")}, + {name: "double", input: []byte("null null")}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleInvalid(test, t) + }) + } +} + +func TestUnmarshal_BoolSimpleSuccess(t *testing.T) { + tests := []*testNode{ + {name: "lower true", input: []byte("true"), _type: Boolean, value: []byte("true")}, + {name: "lower false", input: []byte("false"), _type: Boolean, value: []byte("false")}, + {name: "spaces true", input: []byte(" true\r\n "), _type: Boolean, value: []byte("true")}, + {name: "spaces false", input: []byte(" false\r\n "), _type: Boolean, value: []byte("false")}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleValid(test, t) + }) + } +} + +func TestUnmarshal_BoolSimpleCorrupted(t *testing.T) { + tests := []*testNode{ + simpleCorrupted("tru"), + simpleCorrupted("fals"), + simpleCorrupted("tre"), + simpleCorrupted("fal se"), + simpleCorrupted("true false"), + simpleCorrupted("True"), + simpleCorrupted("TRUE"), + simpleCorrupted("False"), + simpleCorrupted("FALSE"), + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleInvalid(test, t) + }) + } +} + +func TestUnmarshal_ArraySimpleSuccess(t *testing.T) { + tests := []*testNode{ + {name: "[]", input: []byte("[]"), _type: Array, value: []byte("[]")}, + {name: "[1]", input: []byte("[1]"), _type: Array, value: []byte("[1]")}, + {name: "[1,2,3]", input: []byte("[1,2,3]"), _type: Array, value: []byte("[1,2,3]")}, + {name: "[1, 2, 3]", input: []byte("[1, 2, 3]"), _type: Array, value: []byte("[1, 2, 3]")}, + {name: "[1,[2],3]", input: []byte("[1,[2],3]"), _type: Array, value: []byte("[1,[2],3]")}, + {name: "[[],[],[]]", input: []byte("[[],[],[]]"), _type: Array, value: []byte("[[],[],[]]")}, + {name: "[[[[[]]]]]", input: []byte("[[[[[]]]]]"), _type: Array, value: []byte("[[[[[]]]]]")}, + {name: "[true,null,1,\"foo\",[]]", input: []byte("[true,null,1,\"foo\",[]]"), _type: Array, value: []byte("[true,null,1,\"foo\",[]]")}, + {name: "spaces", input: []byte("\n\r [\n1\n ]\r\n"), _type: Array, value: []byte("[\n1\n ]")}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleValid(test, t) + }) + } +} + +func TestUnmarshal_ArraySimpleCorrupted(t *testing.T) { + tests := []*testNode{ + simpleCorrupted("[,]"), + simpleCorrupted("[]\\"), + simpleCorrupted("[1,]"), + simpleCorrupted("[[]"), + simpleCorrupted("[]]"), + simpleCorrupted("1[]"), + simpleCorrupted("[]1"), + simpleCorrupted("[[]1]"), + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleInvalid(test, t) + }) + } +} + +// Examples from https://json.org/example.html +func TestUnmarshal(t *testing.T) { + tests := []struct { + name string + value string + }{ + { + name: "glossary", + value: `{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } + }`, + }, + { + name: "menu", + value: `{"menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [ + {"value": "New", "onclick": "CreateNewDoc()"}, + {"value": "Open", "onclick": "OpenDoc()"}, + {"value": "Close", "onclick": "CloseDoc()"} + ] + } + }}`, + }, + { + name: "widget", + value: `{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } + }} `, + }, + { + name: "web-app", + value: `{"web-app": { + "servlet": [ + { + "servlet-name": "cofaxCDS", + "servlet-class": "org.cofax.cds.CDSServlet", + "init-param": { + "configGlossary:installationAt": "Philadelphia, PA", + "configGlossary:adminEmail": "ksm@pobox.com", + "configGlossary:poweredBy": "Cofax", + "configGlossary:poweredByIcon": "/images/cofax.gif", + "configGlossary:staticPath": "/content/static", + "templateProcessorClass": "org.cofax.WysiwygTemplate", + "templateLoaderClass": "org.cofax.FilesTemplateLoader", + "templatePath": "templates", + "templateOverridePath": "", + "defaultListTemplate": "listTemplate.htm", + "defaultFileTemplate": "articleTemplate.htm", + "useJSP": false, + "jspListTemplate": "listTemplate.jsp", + "jspFileTemplate": "articleTemplate.jsp", + "cachePackageTagsTrack": 200, + "cachePackageTagsStore": 200, + "cachePackageTagsRefresh": 60, + "cacheTemplatesTrack": 100, + "cacheTemplatesStore": 50, + "cacheTemplatesRefresh": 15, + "cachePagesTrack": 200, + "cachePagesStore": 100, + "cachePagesRefresh": 10, + "cachePagesDirtyRead": 10, + "searchEngineListTemplate": "forSearchEnginesList.htm", + "searchEngineFileTemplate": "forSearchEngines.htm", + "searchEngineRobotsDb": "WEB-INF/robots.db", + "useDataStore": true, + "dataStoreClass": "org.cofax.SqlDataStore", + "redirectionClass": "org.cofax.SqlRedirection", + "dataStoreName": "cofax", + "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", + "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", + "dataStoreUser": "sa", + "dataStorePassword": "dataStoreTestQuery", + "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", + "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", + "dataStoreInitConns": 10, + "dataStoreMaxConns": 100, + "dataStoreConnUsageLimit": 100, + "dataStoreLogLevel": "debug", + "maxUrlLength": 500}}, + { + "servlet-name": "cofaxEmail", + "servlet-class": "org.cofax.cds.EmailServlet", + "init-param": { + "mailHost": "mail1", + "mailHostOverride": "mail2"}}, + { + "servlet-name": "cofaxAdmin", + "servlet-class": "org.cofax.cds.AdminServlet"}, + + { + "servlet-name": "fileServlet", + "servlet-class": "org.cofax.cds.FileServlet"}, + { + "servlet-name": "cofaxTools", + "servlet-class": "org.cofax.cms.CofaxToolsServlet", + "init-param": { + "templatePath": "toolstemplates/", + "log": 1, + "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", + "logMaxSize": "", + "dataLog": 1, + "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", + "dataLogMaxSize": "", + "removePageCache": "/content/admin/remove?cache=pages&id=", + "removeTemplateCache": "/content/admin/remove?cache=templates&id=", + "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", + "lookInContext": 1, + "adminGroupID": 4, + "betaServer": true}}], + "servlet-mapping": { + "cofaxCDS": "/", + "cofaxEmail": "/cofaxutil/aemail/*", + "cofaxAdmin": "/admin/*", + "fileServlet": "/static/*", + "cofaxTools": "/tools/*"}, + + "taglib": { + "taglib-uri": "cofax.tld", + "taglib-location": "/WEB-INF/tlds/cofax.tld"}}}`, + }, + { + name: "SVG Viewer", + value: `{"menu": { + "header": "SVG Viewer", + "items": [ + {"id": "Open"}, + {"id": "OpenNew", "label": "Open New"}, + null, + {"id": "ZoomIn", "label": "Zoom In"}, + {"id": "ZoomOut", "label": "Zoom Out"}, + {"id": "OriginalView", "label": "Original View"}, + null, + {"id": "Quality"}, + {"id": "Pause"}, + {"id": "Mute"}, + null, + {"id": "Find", "label": "Find..."}, + {"id": "FindAgain", "label": "Find Again"}, + {"id": "Copy"}, + {"id": "CopyAgain", "label": "Copy Again"}, + {"id": "CopySVG", "label": "Copy SVG"}, + {"id": "ViewSVG", "label": "View SVG"}, + {"id": "ViewSource", "label": "View Source"}, + {"id": "SaveAs", "label": "Save As"}, + null, + {"id": "Help"}, + {"id": "About", "label": "About Adobe CVG Viewer..."} + ] + }}`, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, err := Unmarshal([]byte(test.value)) + if err != nil { + t.Errorf("Error on Unmarshal: %s", err.Error()) + } + }) + } +} + +func TestUnmarshalSafe(t *testing.T) { + json := []byte(`{ "store": { + "book": [ + { "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95 + }, + { "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99 + }, + { "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99 + }, + { "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99 + } + ], + "bicycle": { + "color": "red", + "price": 19.95 + } + } + }`) + safe, err := UnmarshalSafe(json) + if err != nil { + t.Errorf("Error on Unmarshal: %s", err.Error()) + } else if safe == nil { + t.Errorf("Error on Unmarshal: safe is nil") + } else { + root, err := Unmarshal(json) + if err != nil { + t.Errorf("Error on Unmarshal: %s", err.Error()) + } else if root == nil { + t.Errorf("Error on Unmarshal: root is nil") + } else if !bytes.Equal(root.source(), safe.source()) { + t.Errorf("Error on UnmarshalSafe: values not same") + } + } +} + +// BenchmarkGoStdUnmarshal-8 61698 19350 ns/op 288 B/op 6 allocs/op +// BenchmarkUnmarshal-8 45620 26165 ns/op 21889 B/op 367 allocs/op +// +// type bench struct { +// Name string `json:"name"` +// Value int `json:"value"` +// } + +// func BenchmarkGoStdUnmarshal(b *testing.B) { +// data := []byte(webApp) +// for i := 0; i < b.N; i++ { +// err := json.Unmarshal(data, &bench{}) +// if err != nil { +// b.Fatal(err) +// } +// } +// } + +// func BenchmarkUnmarshal(b *testing.B) { +// data := []byte(webApp) +// for i := 0; i < b.N; i++ { +// _, err := Unmarshal(data) +// if err != nil { +// b.Fatal(err) +// } +// } +// } diff --git a/examples/gno.land/p/demo/json/eisel_lemire/eisel_lemire.gno b/examples/gno.land/p/demo/json/eisel_lemire/eisel_lemire.gno new file mode 100644 index 00000000000..6a29f7f1350 --- /dev/null +++ b/examples/gno.land/p/demo/json/eisel_lemire/eisel_lemire.gno @@ -0,0 +1,839 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package eisel_lemire + +// This file implements the Eisel-Lemire ParseFloat algorithm, published in +// 2020 and discussed extensively at +// https://nigeltao.github.io/blog/2020/eisel-lemire.html +// +// The original C++ implementation is at +// https://github.com/lemire/fast_double_parser/blob/644bef4306059d3be01a04e77d3cc84b379c596f/include/fast_double_parser.h#L840 +// +// This Go re-implementation closely follows the C re-implementation at +// https://github.com/google/wuffs/blob/ba3818cb6b473a2ed0b38ecfc07dbbd3a97e8ae7/internal/cgen/base/floatconv-submodule-code.c#L990 +// +// Additional testing (on over several million test strings) is done by +// https://github.com/nigeltao/parse-number-fxx-test-data/blob/5280dcfccf6d0b02a65ae282dad0b6d9de50e039/script/test-go-strconv.go + +import ( + "math" + "math/bits" +) + +const ( + float32ExponentBias = 127 + float64ExponentBias = 1023 +) + +// eiselLemire64 parses a floating-point number from its mantissa and exponent representation. +// This implementation is based on the Eisel-Lemire ParseFloat algorithm, which is efficient +// and precise for converting strings to floating-point numbers. +// +// Arguments: +// man (uint64): The mantissa part of the floating-point number. +// exp10 (int): The exponent part, representing the power of 10. +// neg (bool): Indicates if the number is negative. +// +// Returns: +// f (float64): The parsed floating-point number. +// ok (bool): Indicates whether the parsing was successful. +// +// The function starts by handling special cases, such as zero mantissa. +// It then checks if the exponent is within the allowed range. +// After that, it normalizes the mantissa by left-shifting it to fill +// the leading zeros. This is followed by the main algorithm logic that +// converts the normalized mantissa and exponent into a 64-bit floating-point number. +// The function returns this number along with a boolean indicating the success of the operation. +func EiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) { + // The terse comments in this function body refer to sections of the + // https://nigeltao.github.io/blog/2020/eisel-lemire.html blog post. + + // Exp10 Range. + if man == 0 { + if neg { + f = math.Float64frombits(0x80000000_00000000) // Negative zero. + } + + return f, true + } + + if exp10 < detailedPowersOfTenMinExp10 || detailedPowersOfTenMaxExp10 < exp10 { + return 0, false + } + + // Normalization. + clz := bits.LeadingZeros64(man) + man <<= uint(clz) + retExp2 := uint64(217706*exp10>>16+64+float64ExponentBias) - uint64(clz) + + // Multiplication. + xHi, xLo := bits.Mul64(man, detailedPowersOfTen[exp10-detailedPowersOfTenMinExp10][1]) + + // Wider Approximation. + if xHi&0x1FF == 0x1FF && xLo+man < man { + yHi, yLo := bits.Mul64(man, detailedPowersOfTen[exp10-detailedPowersOfTenMinExp10][0]) + mergedHi, mergedLo := xHi, xLo+yHi + if mergedLo < xLo { + mergedHi++ + } + + if mergedHi&0x1FF == 0x1FF && mergedLo+1 == 0 && yLo+man < man { + return 0, false + } + + xHi, xLo = mergedHi, mergedLo + } + + // Shifting to 54 Bits. + msb := xHi >> 63 + retMantissa := xHi >> (msb + 9) + retExp2 -= 1 ^ msb + + // Half-way Ambiguity. + if xLo == 0 && xHi&0x1FF == 0 && retMantissa&3 == 1 { + return 0, false + } + + // From 54 to 53 Bits. + retMantissa += retMantissa & 1 + retMantissa >>= 1 + if retMantissa>>53 > 0 { + retMantissa >>= 1 + retExp2 += 1 + } + + // retExp2 is a uint64. Zero or underflow means that we're in subnormal + // float64 space. 0x7FF or above means that we're in Inf/NaN float64 space. + // + // The if block is equivalent to (but has fewer branches than): + // if retExp2 <= 0 || retExp2 >= 0x7FF { etc } + if retExp2-1 >= 0x7FF-1 { + return 0, false + } + + retBits := retExp2<<52 | retMantissa&0x000FFFFF_FFFFFFFF + if neg { + retBits |= 0x80000000_00000000 + } + + return math.Float64frombits(retBits), true +} + +// detailedPowersOfTen{Min,Max}Exp10 is the power of 10 represented by the +// first and last rows of detailedPowersOfTen. Both bounds are inclusive. +const ( + detailedPowersOfTenMinExp10 = -348 + detailedPowersOfTenMaxExp10 = +347 +) + +// detailedPowersOfTen contains 128-bit mantissa approximations (rounded down) +// to the powers of 10. For example: +// +// - 1e43 ≈ (0xE596B7B0_C643C719 * (2 ** 79)) +// - 1e43 = (0xE596B7B0_C643C719_6D9CCD05_D0000000 * (2 ** 15)) +// +// The mantissas are explicitly listed. The exponents are implied by a linear +// expression with slope 217706.0/65536.0 ≈ log(10)/log(2). +// +// The table was generated by +// https://github.com/google/wuffs/blob/ba3818cb6b473a2ed0b38ecfc07dbbd3a97e8ae7/script/print-mpb-powers-of-10.go +var detailedPowersOfTen = [...][2]uint64{ + {0x1732C869CD60E453, 0xFA8FD5A0081C0288}, // 1e-348 + {0x0E7FBD42205C8EB4, 0x9C99E58405118195}, // 1e-347 + {0x521FAC92A873B261, 0xC3C05EE50655E1FA}, // 1e-346 + {0xE6A797B752909EF9, 0xF4B0769E47EB5A78}, // 1e-345 + {0x9028BED2939A635C, 0x98EE4A22ECF3188B}, // 1e-344 + {0x7432EE873880FC33, 0xBF29DCABA82FDEAE}, // 1e-343 + {0x113FAA2906A13B3F, 0xEEF453D6923BD65A}, // 1e-342 + {0x4AC7CA59A424C507, 0x9558B4661B6565F8}, // 1e-341 + {0x5D79BCF00D2DF649, 0xBAAEE17FA23EBF76}, // 1e-340 + {0xF4D82C2C107973DC, 0xE95A99DF8ACE6F53}, // 1e-339 + {0x79071B9B8A4BE869, 0x91D8A02BB6C10594}, // 1e-338 + {0x9748E2826CDEE284, 0xB64EC836A47146F9}, // 1e-337 + {0xFD1B1B2308169B25, 0xE3E27A444D8D98B7}, // 1e-336 + {0xFE30F0F5E50E20F7, 0x8E6D8C6AB0787F72}, // 1e-335 + {0xBDBD2D335E51A935, 0xB208EF855C969F4F}, // 1e-334 + {0xAD2C788035E61382, 0xDE8B2B66B3BC4723}, // 1e-333 + {0x4C3BCB5021AFCC31, 0x8B16FB203055AC76}, // 1e-332 + {0xDF4ABE242A1BBF3D, 0xADDCB9E83C6B1793}, // 1e-331 + {0xD71D6DAD34A2AF0D, 0xD953E8624B85DD78}, // 1e-330 + {0x8672648C40E5AD68, 0x87D4713D6F33AA6B}, // 1e-329 + {0x680EFDAF511F18C2, 0xA9C98D8CCB009506}, // 1e-328 + {0x0212BD1B2566DEF2, 0xD43BF0EFFDC0BA48}, // 1e-327 + {0x014BB630F7604B57, 0x84A57695FE98746D}, // 1e-326 + {0x419EA3BD35385E2D, 0xA5CED43B7E3E9188}, // 1e-325 + {0x52064CAC828675B9, 0xCF42894A5DCE35EA}, // 1e-324 + {0x7343EFEBD1940993, 0x818995CE7AA0E1B2}, // 1e-323 + {0x1014EBE6C5F90BF8, 0xA1EBFB4219491A1F}, // 1e-322 + {0xD41A26E077774EF6, 0xCA66FA129F9B60A6}, // 1e-321 + {0x8920B098955522B4, 0xFD00B897478238D0}, // 1e-320 + {0x55B46E5F5D5535B0, 0x9E20735E8CB16382}, // 1e-319 + {0xEB2189F734AA831D, 0xC5A890362FDDBC62}, // 1e-318 + {0xA5E9EC7501D523E4, 0xF712B443BBD52B7B}, // 1e-317 + {0x47B233C92125366E, 0x9A6BB0AA55653B2D}, // 1e-316 + {0x999EC0BB696E840A, 0xC1069CD4EABE89F8}, // 1e-315 + {0xC00670EA43CA250D, 0xF148440A256E2C76}, // 1e-314 + {0x380406926A5E5728, 0x96CD2A865764DBCA}, // 1e-313 + {0xC605083704F5ECF2, 0xBC807527ED3E12BC}, // 1e-312 + {0xF7864A44C633682E, 0xEBA09271E88D976B}, // 1e-311 + {0x7AB3EE6AFBE0211D, 0x93445B8731587EA3}, // 1e-310 + {0x5960EA05BAD82964, 0xB8157268FDAE9E4C}, // 1e-309 + {0x6FB92487298E33BD, 0xE61ACF033D1A45DF}, // 1e-308 + {0xA5D3B6D479F8E056, 0x8FD0C16206306BAB}, // 1e-307 + {0x8F48A4899877186C, 0xB3C4F1BA87BC8696}, // 1e-306 + {0x331ACDABFE94DE87, 0xE0B62E2929ABA83C}, // 1e-305 + {0x9FF0C08B7F1D0B14, 0x8C71DCD9BA0B4925}, // 1e-304 + {0x07ECF0AE5EE44DD9, 0xAF8E5410288E1B6F}, // 1e-303 + {0xC9E82CD9F69D6150, 0xDB71E91432B1A24A}, // 1e-302 + {0xBE311C083A225CD2, 0x892731AC9FAF056E}, // 1e-301 + {0x6DBD630A48AAF406, 0xAB70FE17C79AC6CA}, // 1e-300 + {0x092CBBCCDAD5B108, 0xD64D3D9DB981787D}, // 1e-299 + {0x25BBF56008C58EA5, 0x85F0468293F0EB4E}, // 1e-298 + {0xAF2AF2B80AF6F24E, 0xA76C582338ED2621}, // 1e-297 + {0x1AF5AF660DB4AEE1, 0xD1476E2C07286FAA}, // 1e-296 + {0x50D98D9FC890ED4D, 0x82CCA4DB847945CA}, // 1e-295 + {0xE50FF107BAB528A0, 0xA37FCE126597973C}, // 1e-294 + {0x1E53ED49A96272C8, 0xCC5FC196FEFD7D0C}, // 1e-293 + {0x25E8E89C13BB0F7A, 0xFF77B1FCBEBCDC4F}, // 1e-292 + {0x77B191618C54E9AC, 0x9FAACF3DF73609B1}, // 1e-291 + {0xD59DF5B9EF6A2417, 0xC795830D75038C1D}, // 1e-290 + {0x4B0573286B44AD1D, 0xF97AE3D0D2446F25}, // 1e-289 + {0x4EE367F9430AEC32, 0x9BECCE62836AC577}, // 1e-288 + {0x229C41F793CDA73F, 0xC2E801FB244576D5}, // 1e-287 + {0x6B43527578C1110F, 0xF3A20279ED56D48A}, // 1e-286 + {0x830A13896B78AAA9, 0x9845418C345644D6}, // 1e-285 + {0x23CC986BC656D553, 0xBE5691EF416BD60C}, // 1e-284 + {0x2CBFBE86B7EC8AA8, 0xEDEC366B11C6CB8F}, // 1e-283 + {0x7BF7D71432F3D6A9, 0x94B3A202EB1C3F39}, // 1e-282 + {0xDAF5CCD93FB0CC53, 0xB9E08A83A5E34F07}, // 1e-281 + {0xD1B3400F8F9CFF68, 0xE858AD248F5C22C9}, // 1e-280 + {0x23100809B9C21FA1, 0x91376C36D99995BE}, // 1e-279 + {0xABD40A0C2832A78A, 0xB58547448FFFFB2D}, // 1e-278 + {0x16C90C8F323F516C, 0xE2E69915B3FFF9F9}, // 1e-277 + {0xAE3DA7D97F6792E3, 0x8DD01FAD907FFC3B}, // 1e-276 + {0x99CD11CFDF41779C, 0xB1442798F49FFB4A}, // 1e-275 + {0x40405643D711D583, 0xDD95317F31C7FA1D}, // 1e-274 + {0x482835EA666B2572, 0x8A7D3EEF7F1CFC52}, // 1e-273 + {0xDA3243650005EECF, 0xAD1C8EAB5EE43B66}, // 1e-272 + {0x90BED43E40076A82, 0xD863B256369D4A40}, // 1e-271 + {0x5A7744A6E804A291, 0x873E4F75E2224E68}, // 1e-270 + {0x711515D0A205CB36, 0xA90DE3535AAAE202}, // 1e-269 + {0x0D5A5B44CA873E03, 0xD3515C2831559A83}, // 1e-268 + {0xE858790AFE9486C2, 0x8412D9991ED58091}, // 1e-267 + {0x626E974DBE39A872, 0xA5178FFF668AE0B6}, // 1e-266 + {0xFB0A3D212DC8128F, 0xCE5D73FF402D98E3}, // 1e-265 + {0x7CE66634BC9D0B99, 0x80FA687F881C7F8E}, // 1e-264 + {0x1C1FFFC1EBC44E80, 0xA139029F6A239F72}, // 1e-263 + {0xA327FFB266B56220, 0xC987434744AC874E}, // 1e-262 + {0x4BF1FF9F0062BAA8, 0xFBE9141915D7A922}, // 1e-261 + {0x6F773FC3603DB4A9, 0x9D71AC8FADA6C9B5}, // 1e-260 + {0xCB550FB4384D21D3, 0xC4CE17B399107C22}, // 1e-259 + {0x7E2A53A146606A48, 0xF6019DA07F549B2B}, // 1e-258 + {0x2EDA7444CBFC426D, 0x99C102844F94E0FB}, // 1e-257 + {0xFA911155FEFB5308, 0xC0314325637A1939}, // 1e-256 + {0x793555AB7EBA27CA, 0xF03D93EEBC589F88}, // 1e-255 + {0x4BC1558B2F3458DE, 0x96267C7535B763B5}, // 1e-254 + {0x9EB1AAEDFB016F16, 0xBBB01B9283253CA2}, // 1e-253 + {0x465E15A979C1CADC, 0xEA9C227723EE8BCB}, // 1e-252 + {0x0BFACD89EC191EC9, 0x92A1958A7675175F}, // 1e-251 + {0xCEF980EC671F667B, 0xB749FAED14125D36}, // 1e-250 + {0x82B7E12780E7401A, 0xE51C79A85916F484}, // 1e-249 + {0xD1B2ECB8B0908810, 0x8F31CC0937AE58D2}, // 1e-248 + {0x861FA7E6DCB4AA15, 0xB2FE3F0B8599EF07}, // 1e-247 + {0x67A791E093E1D49A, 0xDFBDCECE67006AC9}, // 1e-246 + {0xE0C8BB2C5C6D24E0, 0x8BD6A141006042BD}, // 1e-245 + {0x58FAE9F773886E18, 0xAECC49914078536D}, // 1e-244 + {0xAF39A475506A899E, 0xDA7F5BF590966848}, // 1e-243 + {0x6D8406C952429603, 0x888F99797A5E012D}, // 1e-242 + {0xC8E5087BA6D33B83, 0xAAB37FD7D8F58178}, // 1e-241 + {0xFB1E4A9A90880A64, 0xD5605FCDCF32E1D6}, // 1e-240 + {0x5CF2EEA09A55067F, 0x855C3BE0A17FCD26}, // 1e-239 + {0xF42FAA48C0EA481E, 0xA6B34AD8C9DFC06F}, // 1e-238 + {0xF13B94DAF124DA26, 0xD0601D8EFC57B08B}, // 1e-237 + {0x76C53D08D6B70858, 0x823C12795DB6CE57}, // 1e-236 + {0x54768C4B0C64CA6E, 0xA2CB1717B52481ED}, // 1e-235 + {0xA9942F5DCF7DFD09, 0xCB7DDCDDA26DA268}, // 1e-234 + {0xD3F93B35435D7C4C, 0xFE5D54150B090B02}, // 1e-233 + {0xC47BC5014A1A6DAF, 0x9EFA548D26E5A6E1}, // 1e-232 + {0x359AB6419CA1091B, 0xC6B8E9B0709F109A}, // 1e-231 + {0xC30163D203C94B62, 0xF867241C8CC6D4C0}, // 1e-230 + {0x79E0DE63425DCF1D, 0x9B407691D7FC44F8}, // 1e-229 + {0x985915FC12F542E4, 0xC21094364DFB5636}, // 1e-228 + {0x3E6F5B7B17B2939D, 0xF294B943E17A2BC4}, // 1e-227 + {0xA705992CEECF9C42, 0x979CF3CA6CEC5B5A}, // 1e-226 + {0x50C6FF782A838353, 0xBD8430BD08277231}, // 1e-225 + {0xA4F8BF5635246428, 0xECE53CEC4A314EBD}, // 1e-224 + {0x871B7795E136BE99, 0x940F4613AE5ED136}, // 1e-223 + {0x28E2557B59846E3F, 0xB913179899F68584}, // 1e-222 + {0x331AEADA2FE589CF, 0xE757DD7EC07426E5}, // 1e-221 + {0x3FF0D2C85DEF7621, 0x9096EA6F3848984F}, // 1e-220 + {0x0FED077A756B53A9, 0xB4BCA50B065ABE63}, // 1e-219 + {0xD3E8495912C62894, 0xE1EBCE4DC7F16DFB}, // 1e-218 + {0x64712DD7ABBBD95C, 0x8D3360F09CF6E4BD}, // 1e-217 + {0xBD8D794D96AACFB3, 0xB080392CC4349DEC}, // 1e-216 + {0xECF0D7A0FC5583A0, 0xDCA04777F541C567}, // 1e-215 + {0xF41686C49DB57244, 0x89E42CAAF9491B60}, // 1e-214 + {0x311C2875C522CED5, 0xAC5D37D5B79B6239}, // 1e-213 + {0x7D633293366B828B, 0xD77485CB25823AC7}, // 1e-212 + {0xAE5DFF9C02033197, 0x86A8D39EF77164BC}, // 1e-211 + {0xD9F57F830283FDFC, 0xA8530886B54DBDEB}, // 1e-210 + {0xD072DF63C324FD7B, 0xD267CAA862A12D66}, // 1e-209 + {0x4247CB9E59F71E6D, 0x8380DEA93DA4BC60}, // 1e-208 + {0x52D9BE85F074E608, 0xA46116538D0DEB78}, // 1e-207 + {0x67902E276C921F8B, 0xCD795BE870516656}, // 1e-206 + {0x00BA1CD8A3DB53B6, 0x806BD9714632DFF6}, // 1e-205 + {0x80E8A40ECCD228A4, 0xA086CFCD97BF97F3}, // 1e-204 + {0x6122CD128006B2CD, 0xC8A883C0FDAF7DF0}, // 1e-203 + {0x796B805720085F81, 0xFAD2A4B13D1B5D6C}, // 1e-202 + {0xCBE3303674053BB0, 0x9CC3A6EEC6311A63}, // 1e-201 + {0xBEDBFC4411068A9C, 0xC3F490AA77BD60FC}, // 1e-200 + {0xEE92FB5515482D44, 0xF4F1B4D515ACB93B}, // 1e-199 + {0x751BDD152D4D1C4A, 0x991711052D8BF3C5}, // 1e-198 + {0xD262D45A78A0635D, 0xBF5CD54678EEF0B6}, // 1e-197 + {0x86FB897116C87C34, 0xEF340A98172AACE4}, // 1e-196 + {0xD45D35E6AE3D4DA0, 0x9580869F0E7AAC0E}, // 1e-195 + {0x8974836059CCA109, 0xBAE0A846D2195712}, // 1e-194 + {0x2BD1A438703FC94B, 0xE998D258869FACD7}, // 1e-193 + {0x7B6306A34627DDCF, 0x91FF83775423CC06}, // 1e-192 + {0x1A3BC84C17B1D542, 0xB67F6455292CBF08}, // 1e-191 + {0x20CABA5F1D9E4A93, 0xE41F3D6A7377EECA}, // 1e-190 + {0x547EB47B7282EE9C, 0x8E938662882AF53E}, // 1e-189 + {0xE99E619A4F23AA43, 0xB23867FB2A35B28D}, // 1e-188 + {0x6405FA00E2EC94D4, 0xDEC681F9F4C31F31}, // 1e-187 + {0xDE83BC408DD3DD04, 0x8B3C113C38F9F37E}, // 1e-186 + {0x9624AB50B148D445, 0xAE0B158B4738705E}, // 1e-185 + {0x3BADD624DD9B0957, 0xD98DDAEE19068C76}, // 1e-184 + {0xE54CA5D70A80E5D6, 0x87F8A8D4CFA417C9}, // 1e-183 + {0x5E9FCF4CCD211F4C, 0xA9F6D30A038D1DBC}, // 1e-182 + {0x7647C3200069671F, 0xD47487CC8470652B}, // 1e-181 + {0x29ECD9F40041E073, 0x84C8D4DFD2C63F3B}, // 1e-180 + {0xF468107100525890, 0xA5FB0A17C777CF09}, // 1e-179 + {0x7182148D4066EEB4, 0xCF79CC9DB955C2CC}, // 1e-178 + {0xC6F14CD848405530, 0x81AC1FE293D599BF}, // 1e-177 + {0xB8ADA00E5A506A7C, 0xA21727DB38CB002F}, // 1e-176 + {0xA6D90811F0E4851C, 0xCA9CF1D206FDC03B}, // 1e-175 + {0x908F4A166D1DA663, 0xFD442E4688BD304A}, // 1e-174 + {0x9A598E4E043287FE, 0x9E4A9CEC15763E2E}, // 1e-173 + {0x40EFF1E1853F29FD, 0xC5DD44271AD3CDBA}, // 1e-172 + {0xD12BEE59E68EF47C, 0xF7549530E188C128}, // 1e-171 + {0x82BB74F8301958CE, 0x9A94DD3E8CF578B9}, // 1e-170 + {0xE36A52363C1FAF01, 0xC13A148E3032D6E7}, // 1e-169 + {0xDC44E6C3CB279AC1, 0xF18899B1BC3F8CA1}, // 1e-168 + {0x29AB103A5EF8C0B9, 0x96F5600F15A7B7E5}, // 1e-167 + {0x7415D448F6B6F0E7, 0xBCB2B812DB11A5DE}, // 1e-166 + {0x111B495B3464AD21, 0xEBDF661791D60F56}, // 1e-165 + {0xCAB10DD900BEEC34, 0x936B9FCEBB25C995}, // 1e-164 + {0x3D5D514F40EEA742, 0xB84687C269EF3BFB}, // 1e-163 + {0x0CB4A5A3112A5112, 0xE65829B3046B0AFA}, // 1e-162 + {0x47F0E785EABA72AB, 0x8FF71A0FE2C2E6DC}, // 1e-161 + {0x59ED216765690F56, 0xB3F4E093DB73A093}, // 1e-160 + {0x306869C13EC3532C, 0xE0F218B8D25088B8}, // 1e-159 + {0x1E414218C73A13FB, 0x8C974F7383725573}, // 1e-158 + {0xE5D1929EF90898FA, 0xAFBD2350644EEACF}, // 1e-157 + {0xDF45F746B74ABF39, 0xDBAC6C247D62A583}, // 1e-156 + {0x6B8BBA8C328EB783, 0x894BC396CE5DA772}, // 1e-155 + {0x066EA92F3F326564, 0xAB9EB47C81F5114F}, // 1e-154 + {0xC80A537B0EFEFEBD, 0xD686619BA27255A2}, // 1e-153 + {0xBD06742CE95F5F36, 0x8613FD0145877585}, // 1e-152 + {0x2C48113823B73704, 0xA798FC4196E952E7}, // 1e-151 + {0xF75A15862CA504C5, 0xD17F3B51FCA3A7A0}, // 1e-150 + {0x9A984D73DBE722FB, 0x82EF85133DE648C4}, // 1e-149 + {0xC13E60D0D2E0EBBA, 0xA3AB66580D5FDAF5}, // 1e-148 + {0x318DF905079926A8, 0xCC963FEE10B7D1B3}, // 1e-147 + {0xFDF17746497F7052, 0xFFBBCFE994E5C61F}, // 1e-146 + {0xFEB6EA8BEDEFA633, 0x9FD561F1FD0F9BD3}, // 1e-145 + {0xFE64A52EE96B8FC0, 0xC7CABA6E7C5382C8}, // 1e-144 + {0x3DFDCE7AA3C673B0, 0xF9BD690A1B68637B}, // 1e-143 + {0x06BEA10CA65C084E, 0x9C1661A651213E2D}, // 1e-142 + {0x486E494FCFF30A62, 0xC31BFA0FE5698DB8}, // 1e-141 + {0x5A89DBA3C3EFCCFA, 0xF3E2F893DEC3F126}, // 1e-140 + {0xF89629465A75E01C, 0x986DDB5C6B3A76B7}, // 1e-139 + {0xF6BBB397F1135823, 0xBE89523386091465}, // 1e-138 + {0x746AA07DED582E2C, 0xEE2BA6C0678B597F}, // 1e-137 + {0xA8C2A44EB4571CDC, 0x94DB483840B717EF}, // 1e-136 + {0x92F34D62616CE413, 0xBA121A4650E4DDEB}, // 1e-135 + {0x77B020BAF9C81D17, 0xE896A0D7E51E1566}, // 1e-134 + {0x0ACE1474DC1D122E, 0x915E2486EF32CD60}, // 1e-133 + {0x0D819992132456BA, 0xB5B5ADA8AAFF80B8}, // 1e-132 + {0x10E1FFF697ED6C69, 0xE3231912D5BF60E6}, // 1e-131 + {0xCA8D3FFA1EF463C1, 0x8DF5EFABC5979C8F}, // 1e-130 + {0xBD308FF8A6B17CB2, 0xB1736B96B6FD83B3}, // 1e-129 + {0xAC7CB3F6D05DDBDE, 0xDDD0467C64BCE4A0}, // 1e-128 + {0x6BCDF07A423AA96B, 0x8AA22C0DBEF60EE4}, // 1e-127 + {0x86C16C98D2C953C6, 0xAD4AB7112EB3929D}, // 1e-126 + {0xE871C7BF077BA8B7, 0xD89D64D57A607744}, // 1e-125 + {0x11471CD764AD4972, 0x87625F056C7C4A8B}, // 1e-124 + {0xD598E40D3DD89BCF, 0xA93AF6C6C79B5D2D}, // 1e-123 + {0x4AFF1D108D4EC2C3, 0xD389B47879823479}, // 1e-122 + {0xCEDF722A585139BA, 0x843610CB4BF160CB}, // 1e-121 + {0xC2974EB4EE658828, 0xA54394FE1EEDB8FE}, // 1e-120 + {0x733D226229FEEA32, 0xCE947A3DA6A9273E}, // 1e-119 + {0x0806357D5A3F525F, 0x811CCC668829B887}, // 1e-118 + {0xCA07C2DCB0CF26F7, 0xA163FF802A3426A8}, // 1e-117 + {0xFC89B393DD02F0B5, 0xC9BCFF6034C13052}, // 1e-116 + {0xBBAC2078D443ACE2, 0xFC2C3F3841F17C67}, // 1e-115 + {0xD54B944B84AA4C0D, 0x9D9BA7832936EDC0}, // 1e-114 + {0x0A9E795E65D4DF11, 0xC5029163F384A931}, // 1e-113 + {0x4D4617B5FF4A16D5, 0xF64335BCF065D37D}, // 1e-112 + {0x504BCED1BF8E4E45, 0x99EA0196163FA42E}, // 1e-111 + {0xE45EC2862F71E1D6, 0xC06481FB9BCF8D39}, // 1e-110 + {0x5D767327BB4E5A4C, 0xF07DA27A82C37088}, // 1e-109 + {0x3A6A07F8D510F86F, 0x964E858C91BA2655}, // 1e-108 + {0x890489F70A55368B, 0xBBE226EFB628AFEA}, // 1e-107 + {0x2B45AC74CCEA842E, 0xEADAB0ABA3B2DBE5}, // 1e-106 + {0x3B0B8BC90012929D, 0x92C8AE6B464FC96F}, // 1e-105 + {0x09CE6EBB40173744, 0xB77ADA0617E3BBCB}, // 1e-104 + {0xCC420A6A101D0515, 0xE55990879DDCAABD}, // 1e-103 + {0x9FA946824A12232D, 0x8F57FA54C2A9EAB6}, // 1e-102 + {0x47939822DC96ABF9, 0xB32DF8E9F3546564}, // 1e-101 + {0x59787E2B93BC56F7, 0xDFF9772470297EBD}, // 1e-100 + {0x57EB4EDB3C55B65A, 0x8BFBEA76C619EF36}, // 1e-99 + {0xEDE622920B6B23F1, 0xAEFAE51477A06B03}, // 1e-98 + {0xE95FAB368E45ECED, 0xDAB99E59958885C4}, // 1e-97 + {0x11DBCB0218EBB414, 0x88B402F7FD75539B}, // 1e-96 + {0xD652BDC29F26A119, 0xAAE103B5FCD2A881}, // 1e-95 + {0x4BE76D3346F0495F, 0xD59944A37C0752A2}, // 1e-94 + {0x6F70A4400C562DDB, 0x857FCAE62D8493A5}, // 1e-93 + {0xCB4CCD500F6BB952, 0xA6DFBD9FB8E5B88E}, // 1e-92 + {0x7E2000A41346A7A7, 0xD097AD07A71F26B2}, // 1e-91 + {0x8ED400668C0C28C8, 0x825ECC24C873782F}, // 1e-90 + {0x728900802F0F32FA, 0xA2F67F2DFA90563B}, // 1e-89 + {0x4F2B40A03AD2FFB9, 0xCBB41EF979346BCA}, // 1e-88 + {0xE2F610C84987BFA8, 0xFEA126B7D78186BC}, // 1e-87 + {0x0DD9CA7D2DF4D7C9, 0x9F24B832E6B0F436}, // 1e-86 + {0x91503D1C79720DBB, 0xC6EDE63FA05D3143}, // 1e-85 + {0x75A44C6397CE912A, 0xF8A95FCF88747D94}, // 1e-84 + {0xC986AFBE3EE11ABA, 0x9B69DBE1B548CE7C}, // 1e-83 + {0xFBE85BADCE996168, 0xC24452DA229B021B}, // 1e-82 + {0xFAE27299423FB9C3, 0xF2D56790AB41C2A2}, // 1e-81 + {0xDCCD879FC967D41A, 0x97C560BA6B0919A5}, // 1e-80 + {0x5400E987BBC1C920, 0xBDB6B8E905CB600F}, // 1e-79 + {0x290123E9AAB23B68, 0xED246723473E3813}, // 1e-78 + {0xF9A0B6720AAF6521, 0x9436C0760C86E30B}, // 1e-77 + {0xF808E40E8D5B3E69, 0xB94470938FA89BCE}, // 1e-76 + {0xB60B1D1230B20E04, 0xE7958CB87392C2C2}, // 1e-75 + {0xB1C6F22B5E6F48C2, 0x90BD77F3483BB9B9}, // 1e-74 + {0x1E38AEB6360B1AF3, 0xB4ECD5F01A4AA828}, // 1e-73 + {0x25C6DA63C38DE1B0, 0xE2280B6C20DD5232}, // 1e-72 + {0x579C487E5A38AD0E, 0x8D590723948A535F}, // 1e-71 + {0x2D835A9DF0C6D851, 0xB0AF48EC79ACE837}, // 1e-70 + {0xF8E431456CF88E65, 0xDCDB1B2798182244}, // 1e-69 + {0x1B8E9ECB641B58FF, 0x8A08F0F8BF0F156B}, // 1e-68 + {0xE272467E3D222F3F, 0xAC8B2D36EED2DAC5}, // 1e-67 + {0x5B0ED81DCC6ABB0F, 0xD7ADF884AA879177}, // 1e-66 + {0x98E947129FC2B4E9, 0x86CCBB52EA94BAEA}, // 1e-65 + {0x3F2398D747B36224, 0xA87FEA27A539E9A5}, // 1e-64 + {0x8EEC7F0D19A03AAD, 0xD29FE4B18E88640E}, // 1e-63 + {0x1953CF68300424AC, 0x83A3EEEEF9153E89}, // 1e-62 + {0x5FA8C3423C052DD7, 0xA48CEAAAB75A8E2B}, // 1e-61 + {0x3792F412CB06794D, 0xCDB02555653131B6}, // 1e-60 + {0xE2BBD88BBEE40BD0, 0x808E17555F3EBF11}, // 1e-59 + {0x5B6ACEAEAE9D0EC4, 0xA0B19D2AB70E6ED6}, // 1e-58 + {0xF245825A5A445275, 0xC8DE047564D20A8B}, // 1e-57 + {0xEED6E2F0F0D56712, 0xFB158592BE068D2E}, // 1e-56 + {0x55464DD69685606B, 0x9CED737BB6C4183D}, // 1e-55 + {0xAA97E14C3C26B886, 0xC428D05AA4751E4C}, // 1e-54 + {0xD53DD99F4B3066A8, 0xF53304714D9265DF}, // 1e-53 + {0xE546A8038EFE4029, 0x993FE2C6D07B7FAB}, // 1e-52 + {0xDE98520472BDD033, 0xBF8FDB78849A5F96}, // 1e-51 + {0x963E66858F6D4440, 0xEF73D256A5C0F77C}, // 1e-50 + {0xDDE7001379A44AA8, 0x95A8637627989AAD}, // 1e-49 + {0x5560C018580D5D52, 0xBB127C53B17EC159}, // 1e-48 + {0xAAB8F01E6E10B4A6, 0xE9D71B689DDE71AF}, // 1e-47 + {0xCAB3961304CA70E8, 0x9226712162AB070D}, // 1e-46 + {0x3D607B97C5FD0D22, 0xB6B00D69BB55C8D1}, // 1e-45 + {0x8CB89A7DB77C506A, 0xE45C10C42A2B3B05}, // 1e-44 + {0x77F3608E92ADB242, 0x8EB98A7A9A5B04E3}, // 1e-43 + {0x55F038B237591ED3, 0xB267ED1940F1C61C}, // 1e-42 + {0x6B6C46DEC52F6688, 0xDF01E85F912E37A3}, // 1e-41 + {0x2323AC4B3B3DA015, 0x8B61313BBABCE2C6}, // 1e-40 + {0xABEC975E0A0D081A, 0xAE397D8AA96C1B77}, // 1e-39 + {0x96E7BD358C904A21, 0xD9C7DCED53C72255}, // 1e-38 + {0x7E50D64177DA2E54, 0x881CEA14545C7575}, // 1e-37 + {0xDDE50BD1D5D0B9E9, 0xAA242499697392D2}, // 1e-36 + {0x955E4EC64B44E864, 0xD4AD2DBFC3D07787}, // 1e-35 + {0xBD5AF13BEF0B113E, 0x84EC3C97DA624AB4}, // 1e-34 + {0xECB1AD8AEACDD58E, 0xA6274BBDD0FADD61}, // 1e-33 + {0x67DE18EDA5814AF2, 0xCFB11EAD453994BA}, // 1e-32 + {0x80EACF948770CED7, 0x81CEB32C4B43FCF4}, // 1e-31 + {0xA1258379A94D028D, 0xA2425FF75E14FC31}, // 1e-30 + {0x096EE45813A04330, 0xCAD2F7F5359A3B3E}, // 1e-29 + {0x8BCA9D6E188853FC, 0xFD87B5F28300CA0D}, // 1e-28 + {0x775EA264CF55347D, 0x9E74D1B791E07E48}, // 1e-27 + {0x95364AFE032A819D, 0xC612062576589DDA}, // 1e-26 + {0x3A83DDBD83F52204, 0xF79687AED3EEC551}, // 1e-25 + {0xC4926A9672793542, 0x9ABE14CD44753B52}, // 1e-24 + {0x75B7053C0F178293, 0xC16D9A0095928A27}, // 1e-23 + {0x5324C68B12DD6338, 0xF1C90080BAF72CB1}, // 1e-22 + {0xD3F6FC16EBCA5E03, 0x971DA05074DA7BEE}, // 1e-21 + {0x88F4BB1CA6BCF584, 0xBCE5086492111AEA}, // 1e-20 + {0x2B31E9E3D06C32E5, 0xEC1E4A7DB69561A5}, // 1e-19 + {0x3AFF322E62439FCF, 0x9392EE8E921D5D07}, // 1e-18 + {0x09BEFEB9FAD487C2, 0xB877AA3236A4B449}, // 1e-17 + {0x4C2EBE687989A9B3, 0xE69594BEC44DE15B}, // 1e-16 + {0x0F9D37014BF60A10, 0x901D7CF73AB0ACD9}, // 1e-15 + {0x538484C19EF38C94, 0xB424DC35095CD80F}, // 1e-14 + {0x2865A5F206B06FB9, 0xE12E13424BB40E13}, // 1e-13 + {0xF93F87B7442E45D3, 0x8CBCCC096F5088CB}, // 1e-12 + {0xF78F69A51539D748, 0xAFEBFF0BCB24AAFE}, // 1e-11 + {0xB573440E5A884D1B, 0xDBE6FECEBDEDD5BE}, // 1e-10 + {0x31680A88F8953030, 0x89705F4136B4A597}, // 1e-9 + {0xFDC20D2B36BA7C3D, 0xABCC77118461CEFC}, // 1e-8 + {0x3D32907604691B4C, 0xD6BF94D5E57A42BC}, // 1e-7 + {0xA63F9A49C2C1B10F, 0x8637BD05AF6C69B5}, // 1e-6 + {0x0FCF80DC33721D53, 0xA7C5AC471B478423}, // 1e-5 + {0xD3C36113404EA4A8, 0xD1B71758E219652B}, // 1e-4 + {0x645A1CAC083126E9, 0x83126E978D4FDF3B}, // 1e-3 + {0x3D70A3D70A3D70A3, 0xA3D70A3D70A3D70A}, // 1e-2 + {0xCCCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCC}, // 1e-1 + {0x0000000000000000, 0x8000000000000000}, // 1e0 + {0x0000000000000000, 0xA000000000000000}, // 1e1 + {0x0000000000000000, 0xC800000000000000}, // 1e2 + {0x0000000000000000, 0xFA00000000000000}, // 1e3 + {0x0000000000000000, 0x9C40000000000000}, // 1e4 + {0x0000000000000000, 0xC350000000000000}, // 1e5 + {0x0000000000000000, 0xF424000000000000}, // 1e6 + {0x0000000000000000, 0x9896800000000000}, // 1e7 + {0x0000000000000000, 0xBEBC200000000000}, // 1e8 + {0x0000000000000000, 0xEE6B280000000000}, // 1e9 + {0x0000000000000000, 0x9502F90000000000}, // 1e10 + {0x0000000000000000, 0xBA43B74000000000}, // 1e11 + {0x0000000000000000, 0xE8D4A51000000000}, // 1e12 + {0x0000000000000000, 0x9184E72A00000000}, // 1e13 + {0x0000000000000000, 0xB5E620F480000000}, // 1e14 + {0x0000000000000000, 0xE35FA931A0000000}, // 1e15 + {0x0000000000000000, 0x8E1BC9BF04000000}, // 1e16 + {0x0000000000000000, 0xB1A2BC2EC5000000}, // 1e17 + {0x0000000000000000, 0xDE0B6B3A76400000}, // 1e18 + {0x0000000000000000, 0x8AC7230489E80000}, // 1e19 + {0x0000000000000000, 0xAD78EBC5AC620000}, // 1e20 + {0x0000000000000000, 0xD8D726B7177A8000}, // 1e21 + {0x0000000000000000, 0x878678326EAC9000}, // 1e22 + {0x0000000000000000, 0xA968163F0A57B400}, // 1e23 + {0x0000000000000000, 0xD3C21BCECCEDA100}, // 1e24 + {0x0000000000000000, 0x84595161401484A0}, // 1e25 + {0x0000000000000000, 0xA56FA5B99019A5C8}, // 1e26 + {0x0000000000000000, 0xCECB8F27F4200F3A}, // 1e27 + {0x4000000000000000, 0x813F3978F8940984}, // 1e28 + {0x5000000000000000, 0xA18F07D736B90BE5}, // 1e29 + {0xA400000000000000, 0xC9F2C9CD04674EDE}, // 1e30 + {0x4D00000000000000, 0xFC6F7C4045812296}, // 1e31 + {0xF020000000000000, 0x9DC5ADA82B70B59D}, // 1e32 + {0x6C28000000000000, 0xC5371912364CE305}, // 1e33 + {0xC732000000000000, 0xF684DF56C3E01BC6}, // 1e34 + {0x3C7F400000000000, 0x9A130B963A6C115C}, // 1e35 + {0x4B9F100000000000, 0xC097CE7BC90715B3}, // 1e36 + {0x1E86D40000000000, 0xF0BDC21ABB48DB20}, // 1e37 + {0x1314448000000000, 0x96769950B50D88F4}, // 1e38 + {0x17D955A000000000, 0xBC143FA4E250EB31}, // 1e39 + {0x5DCFAB0800000000, 0xEB194F8E1AE525FD}, // 1e40 + {0x5AA1CAE500000000, 0x92EFD1B8D0CF37BE}, // 1e41 + {0xF14A3D9E40000000, 0xB7ABC627050305AD}, // 1e42 + {0x6D9CCD05D0000000, 0xE596B7B0C643C719}, // 1e43 + {0xE4820023A2000000, 0x8F7E32CE7BEA5C6F}, // 1e44 + {0xDDA2802C8A800000, 0xB35DBF821AE4F38B}, // 1e45 + {0xD50B2037AD200000, 0xE0352F62A19E306E}, // 1e46 + {0x4526F422CC340000, 0x8C213D9DA502DE45}, // 1e47 + {0x9670B12B7F410000, 0xAF298D050E4395D6}, // 1e48 + {0x3C0CDD765F114000, 0xDAF3F04651D47B4C}, // 1e49 + {0xA5880A69FB6AC800, 0x88D8762BF324CD0F}, // 1e50 + {0x8EEA0D047A457A00, 0xAB0E93B6EFEE0053}, // 1e51 + {0x72A4904598D6D880, 0xD5D238A4ABE98068}, // 1e52 + {0x47A6DA2B7F864750, 0x85A36366EB71F041}, // 1e53 + {0x999090B65F67D924, 0xA70C3C40A64E6C51}, // 1e54 + {0xFFF4B4E3F741CF6D, 0xD0CF4B50CFE20765}, // 1e55 + {0xBFF8F10E7A8921A4, 0x82818F1281ED449F}, // 1e56 + {0xAFF72D52192B6A0D, 0xA321F2D7226895C7}, // 1e57 + {0x9BF4F8A69F764490, 0xCBEA6F8CEB02BB39}, // 1e58 + {0x02F236D04753D5B4, 0xFEE50B7025C36A08}, // 1e59 + {0x01D762422C946590, 0x9F4F2726179A2245}, // 1e60 + {0x424D3AD2B7B97EF5, 0xC722F0EF9D80AAD6}, // 1e61 + {0xD2E0898765A7DEB2, 0xF8EBAD2B84E0D58B}, // 1e62 + {0x63CC55F49F88EB2F, 0x9B934C3B330C8577}, // 1e63 + {0x3CBF6B71C76B25FB, 0xC2781F49FFCFA6D5}, // 1e64 + {0x8BEF464E3945EF7A, 0xF316271C7FC3908A}, // 1e65 + {0x97758BF0E3CBB5AC, 0x97EDD871CFDA3A56}, // 1e66 + {0x3D52EEED1CBEA317, 0xBDE94E8E43D0C8EC}, // 1e67 + {0x4CA7AAA863EE4BDD, 0xED63A231D4C4FB27}, // 1e68 + {0x8FE8CAA93E74EF6A, 0x945E455F24FB1CF8}, // 1e69 + {0xB3E2FD538E122B44, 0xB975D6B6EE39E436}, // 1e70 + {0x60DBBCA87196B616, 0xE7D34C64A9C85D44}, // 1e71 + {0xBC8955E946FE31CD, 0x90E40FBEEA1D3A4A}, // 1e72 + {0x6BABAB6398BDBE41, 0xB51D13AEA4A488DD}, // 1e73 + {0xC696963C7EED2DD1, 0xE264589A4DCDAB14}, // 1e74 + {0xFC1E1DE5CF543CA2, 0x8D7EB76070A08AEC}, // 1e75 + {0x3B25A55F43294BCB, 0xB0DE65388CC8ADA8}, // 1e76 + {0x49EF0EB713F39EBE, 0xDD15FE86AFFAD912}, // 1e77 + {0x6E3569326C784337, 0x8A2DBF142DFCC7AB}, // 1e78 + {0x49C2C37F07965404, 0xACB92ED9397BF996}, // 1e79 + {0xDC33745EC97BE906, 0xD7E77A8F87DAF7FB}, // 1e80 + {0x69A028BB3DED71A3, 0x86F0AC99B4E8DAFD}, // 1e81 + {0xC40832EA0D68CE0C, 0xA8ACD7C0222311BC}, // 1e82 + {0xF50A3FA490C30190, 0xD2D80DB02AABD62B}, // 1e83 + {0x792667C6DA79E0FA, 0x83C7088E1AAB65DB}, // 1e84 + {0x577001B891185938, 0xA4B8CAB1A1563F52}, // 1e85 + {0xED4C0226B55E6F86, 0xCDE6FD5E09ABCF26}, // 1e86 + {0x544F8158315B05B4, 0x80B05E5AC60B6178}, // 1e87 + {0x696361AE3DB1C721, 0xA0DC75F1778E39D6}, // 1e88 + {0x03BC3A19CD1E38E9, 0xC913936DD571C84C}, // 1e89 + {0x04AB48A04065C723, 0xFB5878494ACE3A5F}, // 1e90 + {0x62EB0D64283F9C76, 0x9D174B2DCEC0E47B}, // 1e91 + {0x3BA5D0BD324F8394, 0xC45D1DF942711D9A}, // 1e92 + {0xCA8F44EC7EE36479, 0xF5746577930D6500}, // 1e93 + {0x7E998B13CF4E1ECB, 0x9968BF6ABBE85F20}, // 1e94 + {0x9E3FEDD8C321A67E, 0xBFC2EF456AE276E8}, // 1e95 + {0xC5CFE94EF3EA101E, 0xEFB3AB16C59B14A2}, // 1e96 + {0xBBA1F1D158724A12, 0x95D04AEE3B80ECE5}, // 1e97 + {0x2A8A6E45AE8EDC97, 0xBB445DA9CA61281F}, // 1e98 + {0xF52D09D71A3293BD, 0xEA1575143CF97226}, // 1e99 + {0x593C2626705F9C56, 0x924D692CA61BE758}, // 1e100 + {0x6F8B2FB00C77836C, 0xB6E0C377CFA2E12E}, // 1e101 + {0x0B6DFB9C0F956447, 0xE498F455C38B997A}, // 1e102 + {0x4724BD4189BD5EAC, 0x8EDF98B59A373FEC}, // 1e103 + {0x58EDEC91EC2CB657, 0xB2977EE300C50FE7}, // 1e104 + {0x2F2967B66737E3ED, 0xDF3D5E9BC0F653E1}, // 1e105 + {0xBD79E0D20082EE74, 0x8B865B215899F46C}, // 1e106 + {0xECD8590680A3AA11, 0xAE67F1E9AEC07187}, // 1e107 + {0xE80E6F4820CC9495, 0xDA01EE641A708DE9}, // 1e108 + {0x3109058D147FDCDD, 0x884134FE908658B2}, // 1e109 + {0xBD4B46F0599FD415, 0xAA51823E34A7EEDE}, // 1e110 + {0x6C9E18AC7007C91A, 0xD4E5E2CDC1D1EA96}, // 1e111 + {0x03E2CF6BC604DDB0, 0x850FADC09923329E}, // 1e112 + {0x84DB8346B786151C, 0xA6539930BF6BFF45}, // 1e113 + {0xE612641865679A63, 0xCFE87F7CEF46FF16}, // 1e114 + {0x4FCB7E8F3F60C07E, 0x81F14FAE158C5F6E}, // 1e115 + {0xE3BE5E330F38F09D, 0xA26DA3999AEF7749}, // 1e116 + {0x5CADF5BFD3072CC5, 0xCB090C8001AB551C}, // 1e117 + {0x73D9732FC7C8F7F6, 0xFDCB4FA002162A63}, // 1e118 + {0x2867E7FDDCDD9AFA, 0x9E9F11C4014DDA7E}, // 1e119 + {0xB281E1FD541501B8, 0xC646D63501A1511D}, // 1e120 + {0x1F225A7CA91A4226, 0xF7D88BC24209A565}, // 1e121 + {0x3375788DE9B06958, 0x9AE757596946075F}, // 1e122 + {0x0052D6B1641C83AE, 0xC1A12D2FC3978937}, // 1e123 + {0xC0678C5DBD23A49A, 0xF209787BB47D6B84}, // 1e124 + {0xF840B7BA963646E0, 0x9745EB4D50CE6332}, // 1e125 + {0xB650E5A93BC3D898, 0xBD176620A501FBFF}, // 1e126 + {0xA3E51F138AB4CEBE, 0xEC5D3FA8CE427AFF}, // 1e127 + {0xC66F336C36B10137, 0x93BA47C980E98CDF}, // 1e128 + {0xB80B0047445D4184, 0xB8A8D9BBE123F017}, // 1e129 + {0xA60DC059157491E5, 0xE6D3102AD96CEC1D}, // 1e130 + {0x87C89837AD68DB2F, 0x9043EA1AC7E41392}, // 1e131 + {0x29BABE4598C311FB, 0xB454E4A179DD1877}, // 1e132 + {0xF4296DD6FEF3D67A, 0xE16A1DC9D8545E94}, // 1e133 + {0x1899E4A65F58660C, 0x8CE2529E2734BB1D}, // 1e134 + {0x5EC05DCFF72E7F8F, 0xB01AE745B101E9E4}, // 1e135 + {0x76707543F4FA1F73, 0xDC21A1171D42645D}, // 1e136 + {0x6A06494A791C53A8, 0x899504AE72497EBA}, // 1e137 + {0x0487DB9D17636892, 0xABFA45DA0EDBDE69}, // 1e138 + {0x45A9D2845D3C42B6, 0xD6F8D7509292D603}, // 1e139 + {0x0B8A2392BA45A9B2, 0x865B86925B9BC5C2}, // 1e140 + {0x8E6CAC7768D7141E, 0xA7F26836F282B732}, // 1e141 + {0x3207D795430CD926, 0xD1EF0244AF2364FF}, // 1e142 + {0x7F44E6BD49E807B8, 0x8335616AED761F1F}, // 1e143 + {0x5F16206C9C6209A6, 0xA402B9C5A8D3A6E7}, // 1e144 + {0x36DBA887C37A8C0F, 0xCD036837130890A1}, // 1e145 + {0xC2494954DA2C9789, 0x802221226BE55A64}, // 1e146 + {0xF2DB9BAA10B7BD6C, 0xA02AA96B06DEB0FD}, // 1e147 + {0x6F92829494E5ACC7, 0xC83553C5C8965D3D}, // 1e148 + {0xCB772339BA1F17F9, 0xFA42A8B73ABBF48C}, // 1e149 + {0xFF2A760414536EFB, 0x9C69A97284B578D7}, // 1e150 + {0xFEF5138519684ABA, 0xC38413CF25E2D70D}, // 1e151 + {0x7EB258665FC25D69, 0xF46518C2EF5B8CD1}, // 1e152 + {0xEF2F773FFBD97A61, 0x98BF2F79D5993802}, // 1e153 + {0xAAFB550FFACFD8FA, 0xBEEEFB584AFF8603}, // 1e154 + {0x95BA2A53F983CF38, 0xEEAABA2E5DBF6784}, // 1e155 + {0xDD945A747BF26183, 0x952AB45CFA97A0B2}, // 1e156 + {0x94F971119AEEF9E4, 0xBA756174393D88DF}, // 1e157 + {0x7A37CD5601AAB85D, 0xE912B9D1478CEB17}, // 1e158 + {0xAC62E055C10AB33A, 0x91ABB422CCB812EE}, // 1e159 + {0x577B986B314D6009, 0xB616A12B7FE617AA}, // 1e160 + {0xED5A7E85FDA0B80B, 0xE39C49765FDF9D94}, // 1e161 + {0x14588F13BE847307, 0x8E41ADE9FBEBC27D}, // 1e162 + {0x596EB2D8AE258FC8, 0xB1D219647AE6B31C}, // 1e163 + {0x6FCA5F8ED9AEF3BB, 0xDE469FBD99A05FE3}, // 1e164 + {0x25DE7BB9480D5854, 0x8AEC23D680043BEE}, // 1e165 + {0xAF561AA79A10AE6A, 0xADA72CCC20054AE9}, // 1e166 + {0x1B2BA1518094DA04, 0xD910F7FF28069DA4}, // 1e167 + {0x90FB44D2F05D0842, 0x87AA9AFF79042286}, // 1e168 + {0x353A1607AC744A53, 0xA99541BF57452B28}, // 1e169 + {0x42889B8997915CE8, 0xD3FA922F2D1675F2}, // 1e170 + {0x69956135FEBADA11, 0x847C9B5D7C2E09B7}, // 1e171 + {0x43FAB9837E699095, 0xA59BC234DB398C25}, // 1e172 + {0x94F967E45E03F4BB, 0xCF02B2C21207EF2E}, // 1e173 + {0x1D1BE0EEBAC278F5, 0x8161AFB94B44F57D}, // 1e174 + {0x6462D92A69731732, 0xA1BA1BA79E1632DC}, // 1e175 + {0x7D7B8F7503CFDCFE, 0xCA28A291859BBF93}, // 1e176 + {0x5CDA735244C3D43E, 0xFCB2CB35E702AF78}, // 1e177 + {0x3A0888136AFA64A7, 0x9DEFBF01B061ADAB}, // 1e178 + {0x088AAA1845B8FDD0, 0xC56BAEC21C7A1916}, // 1e179 + {0x8AAD549E57273D45, 0xF6C69A72A3989F5B}, // 1e180 + {0x36AC54E2F678864B, 0x9A3C2087A63F6399}, // 1e181 + {0x84576A1BB416A7DD, 0xC0CB28A98FCF3C7F}, // 1e182 + {0x656D44A2A11C51D5, 0xF0FDF2D3F3C30B9F}, // 1e183 + {0x9F644AE5A4B1B325, 0x969EB7C47859E743}, // 1e184 + {0x873D5D9F0DDE1FEE, 0xBC4665B596706114}, // 1e185 + {0xA90CB506D155A7EA, 0xEB57FF22FC0C7959}, // 1e186 + {0x09A7F12442D588F2, 0x9316FF75DD87CBD8}, // 1e187 + {0x0C11ED6D538AEB2F, 0xB7DCBF5354E9BECE}, // 1e188 + {0x8F1668C8A86DA5FA, 0xE5D3EF282A242E81}, // 1e189 + {0xF96E017D694487BC, 0x8FA475791A569D10}, // 1e190 + {0x37C981DCC395A9AC, 0xB38D92D760EC4455}, // 1e191 + {0x85BBE253F47B1417, 0xE070F78D3927556A}, // 1e192 + {0x93956D7478CCEC8E, 0x8C469AB843B89562}, // 1e193 + {0x387AC8D1970027B2, 0xAF58416654A6BABB}, // 1e194 + {0x06997B05FCC0319E, 0xDB2E51BFE9D0696A}, // 1e195 + {0x441FECE3BDF81F03, 0x88FCF317F22241E2}, // 1e196 + {0xD527E81CAD7626C3, 0xAB3C2FDDEEAAD25A}, // 1e197 + {0x8A71E223D8D3B074, 0xD60B3BD56A5586F1}, // 1e198 + {0xF6872D5667844E49, 0x85C7056562757456}, // 1e199 + {0xB428F8AC016561DB, 0xA738C6BEBB12D16C}, // 1e200 + {0xE13336D701BEBA52, 0xD106F86E69D785C7}, // 1e201 + {0xECC0024661173473, 0x82A45B450226B39C}, // 1e202 + {0x27F002D7F95D0190, 0xA34D721642B06084}, // 1e203 + {0x31EC038DF7B441F4, 0xCC20CE9BD35C78A5}, // 1e204 + {0x7E67047175A15271, 0xFF290242C83396CE}, // 1e205 + {0x0F0062C6E984D386, 0x9F79A169BD203E41}, // 1e206 + {0x52C07B78A3E60868, 0xC75809C42C684DD1}, // 1e207 + {0xA7709A56CCDF8A82, 0xF92E0C3537826145}, // 1e208 + {0x88A66076400BB691, 0x9BBCC7A142B17CCB}, // 1e209 + {0x6ACFF893D00EA435, 0xC2ABF989935DDBFE}, // 1e210 + {0x0583F6B8C4124D43, 0xF356F7EBF83552FE}, // 1e211 + {0xC3727A337A8B704A, 0x98165AF37B2153DE}, // 1e212 + {0x744F18C0592E4C5C, 0xBE1BF1B059E9A8D6}, // 1e213 + {0x1162DEF06F79DF73, 0xEDA2EE1C7064130C}, // 1e214 + {0x8ADDCB5645AC2BA8, 0x9485D4D1C63E8BE7}, // 1e215 + {0x6D953E2BD7173692, 0xB9A74A0637CE2EE1}, // 1e216 + {0xC8FA8DB6CCDD0437, 0xE8111C87C5C1BA99}, // 1e217 + {0x1D9C9892400A22A2, 0x910AB1D4DB9914A0}, // 1e218 + {0x2503BEB6D00CAB4B, 0xB54D5E4A127F59C8}, // 1e219 + {0x2E44AE64840FD61D, 0xE2A0B5DC971F303A}, // 1e220 + {0x5CEAECFED289E5D2, 0x8DA471A9DE737E24}, // 1e221 + {0x7425A83E872C5F47, 0xB10D8E1456105DAD}, // 1e222 + {0xD12F124E28F77719, 0xDD50F1996B947518}, // 1e223 + {0x82BD6B70D99AAA6F, 0x8A5296FFE33CC92F}, // 1e224 + {0x636CC64D1001550B, 0xACE73CBFDC0BFB7B}, // 1e225 + {0x3C47F7E05401AA4E, 0xD8210BEFD30EFA5A}, // 1e226 + {0x65ACFAEC34810A71, 0x8714A775E3E95C78}, // 1e227 + {0x7F1839A741A14D0D, 0xA8D9D1535CE3B396}, // 1e228 + {0x1EDE48111209A050, 0xD31045A8341CA07C}, // 1e229 + {0x934AED0AAB460432, 0x83EA2B892091E44D}, // 1e230 + {0xF81DA84D5617853F, 0xA4E4B66B68B65D60}, // 1e231 + {0x36251260AB9D668E, 0xCE1DE40642E3F4B9}, // 1e232 + {0xC1D72B7C6B426019, 0x80D2AE83E9CE78F3}, // 1e233 + {0xB24CF65B8612F81F, 0xA1075A24E4421730}, // 1e234 + {0xDEE033F26797B627, 0xC94930AE1D529CFC}, // 1e235 + {0x169840EF017DA3B1, 0xFB9B7CD9A4A7443C}, // 1e236 + {0x8E1F289560EE864E, 0x9D412E0806E88AA5}, // 1e237 + {0xF1A6F2BAB92A27E2, 0xC491798A08A2AD4E}, // 1e238 + {0xAE10AF696774B1DB, 0xF5B5D7EC8ACB58A2}, // 1e239 + {0xACCA6DA1E0A8EF29, 0x9991A6F3D6BF1765}, // 1e240 + {0x17FD090A58D32AF3, 0xBFF610B0CC6EDD3F}, // 1e241 + {0xDDFC4B4CEF07F5B0, 0xEFF394DCFF8A948E}, // 1e242 + {0x4ABDAF101564F98E, 0x95F83D0A1FB69CD9}, // 1e243 + {0x9D6D1AD41ABE37F1, 0xBB764C4CA7A4440F}, // 1e244 + {0x84C86189216DC5ED, 0xEA53DF5FD18D5513}, // 1e245 + {0x32FD3CF5B4E49BB4, 0x92746B9BE2F8552C}, // 1e246 + {0x3FBC8C33221DC2A1, 0xB7118682DBB66A77}, // 1e247 + {0x0FABAF3FEAA5334A, 0xE4D5E82392A40515}, // 1e248 + {0x29CB4D87F2A7400E, 0x8F05B1163BA6832D}, // 1e249 + {0x743E20E9EF511012, 0xB2C71D5BCA9023F8}, // 1e250 + {0x914DA9246B255416, 0xDF78E4B2BD342CF6}, // 1e251 + {0x1AD089B6C2F7548E, 0x8BAB8EEFB6409C1A}, // 1e252 + {0xA184AC2473B529B1, 0xAE9672ABA3D0C320}, // 1e253 + {0xC9E5D72D90A2741E, 0xDA3C0F568CC4F3E8}, // 1e254 + {0x7E2FA67C7A658892, 0x8865899617FB1871}, // 1e255 + {0xDDBB901B98FEEAB7, 0xAA7EEBFB9DF9DE8D}, // 1e256 + {0x552A74227F3EA565, 0xD51EA6FA85785631}, // 1e257 + {0xD53A88958F87275F, 0x8533285C936B35DE}, // 1e258 + {0x8A892ABAF368F137, 0xA67FF273B8460356}, // 1e259 + {0x2D2B7569B0432D85, 0xD01FEF10A657842C}, // 1e260 + {0x9C3B29620E29FC73, 0x8213F56A67F6B29B}, // 1e261 + {0x8349F3BA91B47B8F, 0xA298F2C501F45F42}, // 1e262 + {0x241C70A936219A73, 0xCB3F2F7642717713}, // 1e263 + {0xED238CD383AA0110, 0xFE0EFB53D30DD4D7}, // 1e264 + {0xF4363804324A40AA, 0x9EC95D1463E8A506}, // 1e265 + {0xB143C6053EDCD0D5, 0xC67BB4597CE2CE48}, // 1e266 + {0xDD94B7868E94050A, 0xF81AA16FDC1B81DA}, // 1e267 + {0xCA7CF2B4191C8326, 0x9B10A4E5E9913128}, // 1e268 + {0xFD1C2F611F63A3F0, 0xC1D4CE1F63F57D72}, // 1e269 + {0xBC633B39673C8CEC, 0xF24A01A73CF2DCCF}, // 1e270 + {0xD5BE0503E085D813, 0x976E41088617CA01}, // 1e271 + {0x4B2D8644D8A74E18, 0xBD49D14AA79DBC82}, // 1e272 + {0xDDF8E7D60ED1219E, 0xEC9C459D51852BA2}, // 1e273 + {0xCABB90E5C942B503, 0x93E1AB8252F33B45}, // 1e274 + {0x3D6A751F3B936243, 0xB8DA1662E7B00A17}, // 1e275 + {0x0CC512670A783AD4, 0xE7109BFBA19C0C9D}, // 1e276 + {0x27FB2B80668B24C5, 0x906A617D450187E2}, // 1e277 + {0xB1F9F660802DEDF6, 0xB484F9DC9641E9DA}, // 1e278 + {0x5E7873F8A0396973, 0xE1A63853BBD26451}, // 1e279 + {0xDB0B487B6423E1E8, 0x8D07E33455637EB2}, // 1e280 + {0x91CE1A9A3D2CDA62, 0xB049DC016ABC5E5F}, // 1e281 + {0x7641A140CC7810FB, 0xDC5C5301C56B75F7}, // 1e282 + {0xA9E904C87FCB0A9D, 0x89B9B3E11B6329BA}, // 1e283 + {0x546345FA9FBDCD44, 0xAC2820D9623BF429}, // 1e284 + {0xA97C177947AD4095, 0xD732290FBACAF133}, // 1e285 + {0x49ED8EABCCCC485D, 0x867F59A9D4BED6C0}, // 1e286 + {0x5C68F256BFFF5A74, 0xA81F301449EE8C70}, // 1e287 + {0x73832EEC6FFF3111, 0xD226FC195C6A2F8C}, // 1e288 + {0xC831FD53C5FF7EAB, 0x83585D8FD9C25DB7}, // 1e289 + {0xBA3E7CA8B77F5E55, 0xA42E74F3D032F525}, // 1e290 + {0x28CE1BD2E55F35EB, 0xCD3A1230C43FB26F}, // 1e291 + {0x7980D163CF5B81B3, 0x80444B5E7AA7CF85}, // 1e292 + {0xD7E105BCC332621F, 0xA0555E361951C366}, // 1e293 + {0x8DD9472BF3FEFAA7, 0xC86AB5C39FA63440}, // 1e294 + {0xB14F98F6F0FEB951, 0xFA856334878FC150}, // 1e295 + {0x6ED1BF9A569F33D3, 0x9C935E00D4B9D8D2}, // 1e296 + {0x0A862F80EC4700C8, 0xC3B8358109E84F07}, // 1e297 + {0xCD27BB612758C0FA, 0xF4A642E14C6262C8}, // 1e298 + {0x8038D51CB897789C, 0x98E7E9CCCFBD7DBD}, // 1e299 + {0xE0470A63E6BD56C3, 0xBF21E44003ACDD2C}, // 1e300 + {0x1858CCFCE06CAC74, 0xEEEA5D5004981478}, // 1e301 + {0x0F37801E0C43EBC8, 0x95527A5202DF0CCB}, // 1e302 + {0xD30560258F54E6BA, 0xBAA718E68396CFFD}, // 1e303 + {0x47C6B82EF32A2069, 0xE950DF20247C83FD}, // 1e304 + {0x4CDC331D57FA5441, 0x91D28B7416CDD27E}, // 1e305 + {0xE0133FE4ADF8E952, 0xB6472E511C81471D}, // 1e306 + {0x58180FDDD97723A6, 0xE3D8F9E563A198E5}, // 1e307 + {0x570F09EAA7EA7648, 0x8E679C2F5E44FF8F}, // 1e308 + {0x2CD2CC6551E513DA, 0xB201833B35D63F73}, // 1e309 + {0xF8077F7EA65E58D1, 0xDE81E40A034BCF4F}, // 1e310 + {0xFB04AFAF27FAF782, 0x8B112E86420F6191}, // 1e311 + {0x79C5DB9AF1F9B563, 0xADD57A27D29339F6}, // 1e312 + {0x18375281AE7822BC, 0xD94AD8B1C7380874}, // 1e313 + {0x8F2293910D0B15B5, 0x87CEC76F1C830548}, // 1e314 + {0xB2EB3875504DDB22, 0xA9C2794AE3A3C69A}, // 1e315 + {0x5FA60692A46151EB, 0xD433179D9C8CB841}, // 1e316 + {0xDBC7C41BA6BCD333, 0x849FEEC281D7F328}, // 1e317 + {0x12B9B522906C0800, 0xA5C7EA73224DEFF3}, // 1e318 + {0xD768226B34870A00, 0xCF39E50FEAE16BEF}, // 1e319 + {0xE6A1158300D46640, 0x81842F29F2CCE375}, // 1e320 + {0x60495AE3C1097FD0, 0xA1E53AF46F801C53}, // 1e321 + {0x385BB19CB14BDFC4, 0xCA5E89B18B602368}, // 1e322 + {0x46729E03DD9ED7B5, 0xFCF62C1DEE382C42}, // 1e323 + {0x6C07A2C26A8346D1, 0x9E19DB92B4E31BA9}, // 1e324 + {0xC7098B7305241885, 0xC5A05277621BE293}, // 1e325 + {0xB8CBEE4FC66D1EA7, 0xF70867153AA2DB38}, // 1e326 + {0x737F74F1DC043328, 0x9A65406D44A5C903}, // 1e327 + {0x505F522E53053FF2, 0xC0FE908895CF3B44}, // 1e328 + {0x647726B9E7C68FEF, 0xF13E34AABB430A15}, // 1e329 + {0x5ECA783430DC19F5, 0x96C6E0EAB509E64D}, // 1e330 + {0xB67D16413D132072, 0xBC789925624C5FE0}, // 1e331 + {0xE41C5BD18C57E88F, 0xEB96BF6EBADF77D8}, // 1e332 + {0x8E91B962F7B6F159, 0x933E37A534CBAAE7}, // 1e333 + {0x723627BBB5A4ADB0, 0xB80DC58E81FE95A1}, // 1e334 + {0xCEC3B1AAA30DD91C, 0xE61136F2227E3B09}, // 1e335 + {0x213A4F0AA5E8A7B1, 0x8FCAC257558EE4E6}, // 1e336 + {0xA988E2CD4F62D19D, 0xB3BD72ED2AF29E1F}, // 1e337 + {0x93EB1B80A33B8605, 0xE0ACCFA875AF45A7}, // 1e338 + {0xBC72F130660533C3, 0x8C6C01C9498D8B88}, // 1e339 + {0xEB8FAD7C7F8680B4, 0xAF87023B9BF0EE6A}, // 1e340 + {0xA67398DB9F6820E1, 0xDB68C2CA82ED2A05}, // 1e341 + {0x88083F8943A1148C, 0x892179BE91D43A43}, // 1e342 + {0x6A0A4F6B948959B0, 0xAB69D82E364948D4}, // 1e343 + {0x848CE34679ABB01C, 0xD6444E39C3DB9B09}, // 1e344 + {0xF2D80E0C0C0B4E11, 0x85EAB0E41A6940E5}, // 1e345 + {0x6F8E118F0F0E2195, 0xA7655D1D2103911F}, // 1e346 + {0x4B7195F2D2D1A9FB, 0xD13EB46469447567}, // 1e347 +} diff --git a/examples/gno.land/p/demo/json/eisel_lemire/gno.mod b/examples/gno.land/p/demo/json/eisel_lemire/gno.mod new file mode 100644 index 00000000000..d6670de82e2 --- /dev/null +++ b/examples/gno.land/p/demo/json/eisel_lemire/gno.mod @@ -0,0 +1 @@ +module gno.land/p/demo/json/eisel_lemire diff --git a/examples/gno.land/p/demo/json/encode.gno b/examples/gno.land/p/demo/json/encode.gno new file mode 100644 index 00000000000..be90d7aa73d --- /dev/null +++ b/examples/gno.land/p/demo/json/encode.gno @@ -0,0 +1,128 @@ +package json + +import ( + "bytes" + "errors" + "math" + "strconv" + + "gno.land/p/demo/json/ryu" + "gno.land/p/demo/ufmt" +) + +// Marshal returns the JSON encoding of a Node. +func Marshal(node *Node) ([]byte, error) { + var ( + buf bytes.Buffer + sVal string + bVal bool + nVal float64 + oVal []byte + err error + ) + + if node == nil { + return nil, errors.New("node is nil") + } + + if !node.modified && !node.ready() { + return nil, errors.New("node is not ready") + } + + if !node.modified && node.ready() { + buf.Write(node.source()) + } + + if node.modified { + switch node.nodeType { + case Null: + buf.Write(nullLiteral) + + case Number: + nVal, err = node.GetNumeric() + if err != nil { + return nil, err + } + + // ufmt does not support %g. by doing so, we need to check if the number is an integer + // after then, apply the correct format for each float and integer numbers. + if math.Mod(nVal, 1.0) == 0 { + // must convert float to integer. otherwise it will be overflowed. + num := ufmt.Sprintf("%d", int(nVal)) + buf.WriteString(num) + } else { + // use ryu algorithm to convert float to string + num := ryu.FormatFloat64(nVal) + buf.WriteString(num) + } + + case String: + sVal, err = node.GetString() + if err != nil { + return nil, err + } + + quoted := ufmt.Sprintf("%s", strconv.Quote(sVal)) + buf.WriteString(quoted) + + case Boolean: + bVal, err = node.GetBool() + if err != nil { + return nil, err + } + + bStr := ufmt.Sprintf("%t", bVal) + buf.WriteString(bStr) + + case Array: + buf.WriteByte(bracketOpen) + + for i := 0; i < len(node.next); i++ { + if i != 0 { + buf.WriteByte(comma) + } + + elem, ok := node.next[strconv.Itoa(i)] + if !ok { + return nil, ufmt.Errorf("array element %d is not found", i) + } + + oVal, err = Marshal(elem) + if err != nil { + return nil, err + } + + buf.Write(oVal) + } + + buf.WriteByte(bracketClose) + + case Object: + buf.WriteByte(curlyOpen) + + bVal = false + for k, v := range node.next { + if bVal { + buf.WriteByte(comma) + } else { + bVal = true + } + + key := ufmt.Sprintf("%s", strconv.Quote(k)) + buf.WriteString(key) + buf.WriteByte(colon) + + oVal, err = Marshal(v) + if err != nil { + return nil, err + } + + buf.Write(oVal) + } + + buf.WriteByte(curlyClose) + } + } + + return buf.Bytes(), nil +} diff --git a/examples/gno.land/p/demo/json/encode_test.gno b/examples/gno.land/p/demo/json/encode_test.gno new file mode 100644 index 00000000000..33a1fae3d4e --- /dev/null +++ b/examples/gno.land/p/demo/json/encode_test.gno @@ -0,0 +1,257 @@ +package json + +import ( + "testing" +) + +func TestMarshal_Primitive(t *testing.T) { + tests := []struct { + name string + node *Node + }{ + { + name: "null", + node: NullNode(""), + }, + { + name: "true", + node: BoolNode("", true), + }, + { + name: "false", + node: BoolNode("", false), + }, + { + name: `"string"`, + node: StringNode("", "string"), + }, + { + name: `"one \"encoded\" string"`, + node: StringNode("", `one "encoded" string`), + }, + { + name: `{"foo":"bar"}`, + node: ObjectNode("", map[string]*Node{ + "foo": StringNode("foo", "bar"), + }), + }, + { + name: "42", + node: NumberNode("", 42), + }, + // TODO: fix output for not to use scientific notation + { + name: "1.005e+02", + node: NumberNode("", 100.5), + }, + { + name: `[1,2,3]`, + node: ArrayNode("", []*Node{ + NumberNode("0", 1), + NumberNode("2", 2), + NumberNode("3", 3), + }), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + value, err := Marshal(test.node) + if err != nil { + t.Errorf("unexpected error: %s", err) + } else if string(value) != test.name { + t.Errorf("wrong result: '%s', expected '%s'", value, test.name) + } + }) + } +} + +func TestMarshal_Object(t *testing.T) { + node := ObjectNode("", map[string]*Node{ + "foo": StringNode("foo", "bar"), + "baz": NumberNode("baz", 100500), + "qux": NullNode("qux"), + }) + + mustKey := []string{"foo", "baz", "qux"} + + value, err := Marshal(node) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + + // the order of keys in the map is not guaranteed + // so we need to unmarshal the result and check the keys + decoded, err := Unmarshal(value) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + + for _, key := range mustKey { + if node, err := decoded.GetKey(key); err != nil { + t.Errorf("unexpected error: %s", err) + } else { + if node == nil { + t.Errorf("node is nil") + } else if node.key == nil { + t.Errorf("key is nil") + } else if *node.key != key { + t.Errorf("wrong key: '%s', expected '%s'", *node.key, key) + } + } + } +} + +func valueNode(prev *Node, key string, typ ValueType, val interface{}) *Node { + curr := &Node{ + prev: prev, + data: nil, + key: &key, + borders: [2]int{0, 0}, + value: val, + modified: true, + } + + if val != nil { + curr.nodeType = typ + } + + return curr +} + +func TestMarshal_Errors(t *testing.T) { + tests := []struct { + name string + node func() (node *Node) + }{ + { + name: "nil", + node: func() (node *Node) { + return + }, + }, + { + name: "broken", + node: func() (node *Node) { + node = Must(Unmarshal([]byte(`{}`))) + node.borders[1] = 0 + return + }, + }, + { + name: "Numeric", + node: func() (node *Node) { + return valueNode(nil, "", Number, false) + }, + }, + { + name: "String", + node: func() (node *Node) { + return valueNode(nil, "", String, false) + }, + }, + { + name: "Bool", + node: func() (node *Node) { + return valueNode(nil, "", Boolean, 1) + }, + }, + { + name: "Array_1", + node: func() (node *Node) { + node = ArrayNode("", nil) + node.next["1"] = NullNode("1") + return + }, + }, + { + name: "Array_2", + node: func() (node *Node) { + return ArrayNode("", []*Node{valueNode(nil, "", Boolean, 1)}) + }, + }, + { + name: "Object", + node: func() (node *Node) { + return ObjectNode("", map[string]*Node{"key": valueNode(nil, "key", Boolean, 1)}) + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + value, err := Marshal(test.node()) + if err == nil { + t.Errorf("expected error") + } else if len(value) != 0 { + t.Errorf("wrong result") + } + }) + } +} + +func TestMarshal_Nil(t *testing.T) { + _, err := Marshal(nil) + if err == nil { + t.Error("Expected error for nil node, but got nil") + } +} + +func TestMarshal_NotModified(t *testing.T) { + node := &Node{} + _, err := Marshal(node) + if err == nil { + t.Error("Expected error for not modified node, but got nil") + } +} + +func TestMarshalCycleReference(t *testing.T) { + node1 := &Node{ + key: stringPtr("node1"), + nodeType: String, + next: map[string]*Node{ + "next": nil, + }, + } + + node2 := &Node{ + key: stringPtr("node2"), + nodeType: String, + prev: node1, + } + + node1.next["next"] = node2 + + _, err := Marshal(node1) + if err == nil { + t.Error("Expected error for cycle reference, but got nil") + } +} + +func TestMarshalNoCycleReference(t *testing.T) { + node1 := &Node{ + key: stringPtr("node1"), + nodeType: String, + value: "value1", + modified: true, + } + + node2 := &Node{ + key: stringPtr("node2"), + nodeType: String, + value: "value2", + modified: true, + } + + _, err := Marshal(node1) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + _, err = Marshal(node2) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } +} + +func stringPtr(s string) *string { + return &s +} diff --git a/examples/gno.land/p/demo/json/escape.gno b/examples/gno.land/p/demo/json/escape.gno new file mode 100644 index 00000000000..5a834068127 --- /dev/null +++ b/examples/gno.land/p/demo/json/escape.gno @@ -0,0 +1,300 @@ +package json + +import ( + "bytes" + "errors" + "unicode/utf8" +) + +const ( + supplementalPlanesOffset = 0x10000 + highSurrogateOffset = 0xD800 + lowSurrogateOffset = 0xDC00 + surrogateEnd = 0xDFFF + basicMultilingualPlaneOffset = 0xFFFF + badHex = -1 +) + +var hexLookupTable = [256]int{ + '0': 0x0, '1': 0x1, '2': 0x2, '3': 0x3, '4': 0x4, + '5': 0x5, '6': 0x6, '7': 0x7, '8': 0x8, '9': 0x9, + 'A': 0xA, 'B': 0xB, 'C': 0xC, 'D': 0xD, 'E': 0xE, 'F': 0xF, + 'a': 0xA, 'b': 0xB, 'c': 0xC, 'd': 0xD, 'e': 0xE, 'f': 0xF, + // Fill unspecified index-value pairs with key and value of -1 + 'G': -1, 'H': -1, 'I': -1, 'J': -1, + 'K': -1, 'L': -1, 'M': -1, 'N': -1, + 'O': -1, 'P': -1, 'Q': -1, 'R': -1, + 'S': -1, 'T': -1, 'U': -1, 'V': -1, + 'W': -1, 'X': -1, 'Y': -1, 'Z': -1, + 'g': -1, 'h': -1, 'i': -1, 'j': -1, + 'k': -1, 'l': -1, 'm': -1, 'n': -1, + 'o': -1, 'p': -1, 'q': -1, 'r': -1, + 's': -1, 't': -1, 'u': -1, 'v': -1, + 'w': -1, 'x': -1, 'y': -1, 'z': -1, +} + +func h2i(c byte) int { + return hexLookupTable[c] +} + +// Unescape takes an input byte slice, processes it to Unescape certain characters, +// and writes the result into an output byte slice. +// +// it returns the processed slice and any error encountered during the Unescape operation. +func Unescape(input, output []byte) ([]byte, error) { + // find the index of the first backslash in the input slice. + firstBackslash := bytes.IndexByte(input, backSlash) + if firstBackslash == -1 { + return input, nil + } + + // ensure the output slice has enough capacity to hold the result. + inputLen := len(input) + if cap(output) < inputLen { + output = make([]byte, inputLen) + } + + output = output[:inputLen] + copy(output, input[:firstBackslash]) + + input = input[firstBackslash:] + buf := output[firstBackslash:] + + for len(input) > 0 { + inLen, bufLen, err := processEscapedUTF8(input, buf) + if err != nil { + return nil, err + } + + input = input[inLen:] // the number of bytes consumed in the input + buf = buf[bufLen:] // the number of bytes written to buf + + // find the next backslash in the remaining input + nextBackslash := bytes.IndexByte(input, backSlash) + if nextBackslash == -1 { + copy(buf, input) + buf = buf[len(input):] + break + } + + copy(buf, input[:nextBackslash]) + + input = input[nextBackslash:] + buf = buf[nextBackslash:] + } + + return output[:len(output)-len(buf)], nil +} + +// isSurrogatePair returns true if the rune is a surrogate pair. +// +// A surrogate pairs are used in UTF-16 encoding to encode characters +// outside the Basic Multilingual Plane (BMP). +func isSurrogatePair(r rune) bool { + return highSurrogateOffset <= r && r <= surrogateEnd +} + +// combineSurrogates reconstruct the original unicode code points in the +// supplemental plane by combinin the high and low surrogate. +// +// The hight surrogate in the range from U+D800 to U+DBFF, +// and the low surrogate in the range from U+DC00 to U+DFFF. +// +// The formula to combine the surrogates is: +// (high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000 +func combineSurrogates(high, low rune) rune { + return ((high - highSurrogateOffset) << 10) + (low - lowSurrogateOffset) + supplementalPlanesOffset +} + +// deocdeSingleUnicodeEscape decodes a unicode escape sequence (e.g., \uXXXX) into a rune. +func decodeSingleUnicodeEscape(b []byte) (rune, bool) { + if len(b) < 6 { + return utf8.RuneError, false + } + + // convert hex to decimal + h1, h2, h3, h4 := h2i(b[2]), h2i(b[3]), h2i(b[4]), h2i(b[5]) + if h1 == badHex || h2 == badHex || h3 == badHex || h4 == badHex { + return utf8.RuneError, false + } + + return rune(h1<<12 + h2<<8 + h3<<4 + h4), true +} + +// decodeUnicodeEscape decodes a Unicode escape sequence from a byte slice. +func decodeUnicodeEscape(b []byte) (rune, int) { + r, ok := decodeSingleUnicodeEscape(b) + if !ok { + return utf8.RuneError, -1 + } + + // determine valid unicode escapes within the BMP + if r <= basicMultilingualPlaneOffset && !isSurrogatePair(r) { + return r, 6 + } + + // Decode the following escape sequence to verify a UTF-16 susergate pair. + r2, ok := decodeSingleUnicodeEscape(b[6:]) + if !ok { + return utf8.RuneError, -1 + } + + if r2 < lowSurrogateOffset { + return utf8.RuneError, -1 + } + + return combineSurrogates(r, r2), 12 +} + +var escapeByteSet = [256]byte{ + '"': doubleQuote, + '\\': backSlash, + '/': slash, + 'b': backSpace, + 'f': formFeed, + 'n': newLine, + 'r': carriageReturn, + 't': tab, +} + +// Unquote takes a byte slice and unquotes it by removing +// the surrounding quotes and unescaping the contents. +func Unquote(s []byte, border byte) (string, bool) { + s, ok := unquoteBytes(s, border) + return string(s), ok +} + +// unquoteBytes takes a byte slice and unquotes it by removing +// TODO: consider to move this function to the strconv package. +func unquoteBytes(s []byte, border byte) ([]byte, bool) { + if len(s) < 2 || s[0] != border || s[len(s)-1] != border { + return nil, false + } + + s = s[1 : len(s)-1] + + r := 0 + for r < len(s) { + c := s[r] + + if c == backSlash || c == border || c < 0x20 { + break + } + + if c < utf8.RuneSelf { + r++ + continue + } + + rr, size := utf8.DecodeRune(s[r:]) + if rr == utf8.RuneError && size == 1 { + break + } + + r += size + } + + if r == len(s) { + return s, true + } + + utfDoubleMax := utf8.UTFMax * 2 + b := make([]byte, len(s)+utfDoubleMax) + w := copy(b, s[0:r]) + + for r < len(s) { + if w >= len(b)-utf8.UTFMax { + nb := make([]byte, utfDoubleMax+(2*len(b))) + copy(nb, b) + b = nb + } + + c := s[r] + if c == backSlash { + r++ + if r >= len(s) { + return nil, false + } + + if s[r] == 'u' { + rr, res := decodeUnicodeEscape(s[r-1:]) + if res < 0 { + return nil, false + } + + w += utf8.EncodeRune(b[w:], rr) + r += 5 + } else { + decode := escapeByteSet[s[r]] + if decode == 0 { + return nil, false + } + + if decode == doubleQuote || decode == backSlash || decode == slash { + decode = s[r] + } + + b[w] = decode + r++ + w++ + } + } else if c == border || c < 0x20 { + return nil, false + } else if c < utf8.RuneSelf { + b[w] = c + r++ + w++ + } else { + rr, size := utf8.DecodeRune(s[r:]) + + if rr == utf8.RuneError && size == 1 { + return nil, false + } + + r += size + w += utf8.EncodeRune(b[w:], rr) + } + } + + return b[:w], true +} + +// processEscapedUTF8 processes the escape sequence in the given byte slice and +// and converts them to UTF-8 characters. The function returns the length of the processed input and output. +// +// The input 'in' must contain the escape sequence to be processed, +// and 'out' provides a space to store the converted characters. +// +// The function returns (input length, output length) if the escape sequence is correct. +// Unicode escape sequences (e.g. \uXXXX) are decoded to UTF-8, other default escape sequences are +// converted to their corresponding special characters (e.g. \n -> newline). +// +// If the escape sequence is invalid, or if 'in' does not completely enclose the escape sequence, +// function returns (-1, -1) to indicate an error. +func processEscapedUTF8(in, out []byte) (int, int, error) { + if len(in) < 2 || in[0] != backSlash { + return -1, -1, errors.New("invalid escape sequence") + } + + escapeSeqLen := 2 + escapeChar := in[1] + + if escapeChar != 'u' { + val := escapeByteSet[escapeChar] + if val == 0 { + return -1, -1, errors.New("invalid escape sequence") + } + + out[0] = val + return escapeSeqLen, 1, nil + } + + r, size := decodeUnicodeEscape(in) + if size == -1 { + return -1, -1, errors.New("invalid escape sequence") + } + + outLen := utf8.EncodeRune(out, r) + + return size, outLen, nil +} diff --git a/examples/gno.land/p/demo/json/escape_test.gno b/examples/gno.land/p/demo/json/escape_test.gno new file mode 100644 index 00000000000..40c118d93ce --- /dev/null +++ b/examples/gno.land/p/demo/json/escape_test.gno @@ -0,0 +1,222 @@ +package json + +import ( + "bytes" + "testing" + "unicode/utf8" +) + +func TestHexToInt(t *testing.T) { + tests := []struct { + name string + c byte + want int + }{ + {"Digit 0", '0', 0}, + {"Digit 9", '9', 9}, + {"Uppercase A", 'A', 10}, + {"Uppercase F", 'F', 15}, + {"Lowercase a", 'a', 10}, + {"Lowercase f", 'f', 15}, + {"Invalid character1", 'g', badHex}, + {"Invalid character2", 'G', badHex}, + {"Invalid character3", 'z', badHex}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := h2i(tt.c); got != tt.want { + t.Errorf("h2i() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestIsSurrogatePair(t *testing.T) { + testCases := []struct { + name string + r rune + expected bool + }{ + {"high surrogate start", 0xD800, true}, + {"high surrogate end", 0xDBFF, true}, + {"low surrogate start", 0xDC00, true}, + {"low surrogate end", 0xDFFF, true}, + {"Non-surrogate", 0x0000, false}, + {"Non-surrogate 2", 0xE000, false}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if got := isSurrogatePair(tc.r); got != tc.expected { + t.Errorf("isSurrogate() = %v, want %v", got, tc.expected) + } + }) + } +} + +func TestCombineSurrogates(t *testing.T) { + testCases := []struct { + high, low rune + expected rune + }{ + {0xD83D, 0xDC36, 0x1F436}, // 🐶 U+1F436 DOG FACE + {0xD83D, 0xDE00, 0x1F600}, // 😀 U+1F600 GRINNING FACE + {0xD83C, 0xDF03, 0x1F303}, // 🌃 U+1F303 NIGHT WITH STARS + } + + for _, tc := range testCases { + result := combineSurrogates(tc.high, tc.low) + if result != tc.expected { + t.Errorf("combineSurrogates(%U, %U) = %U; want %U", tc.high, tc.low, result, tc.expected) + } + } +} + +func TestDecodeSingleUnicodeEscape(t *testing.T) { + testCases := []struct { + input []byte + expected rune + isValid bool + }{ + // valid unicode escape sequences + {[]byte(`\u0041`), 'A', true}, + {[]byte(`\u03B1`), 'α', true}, + {[]byte(`\u00E9`), 'é', true}, // valid non-English character + {[]byte(`\u0021`), '!', true}, // valid special character + {[]byte(`\uFF11`), '1', true}, + {[]byte(`\uD83D`), 0xD83D, true}, + {[]byte(`\uDE03`), 0xDE03, true}, + + // invalid unicode escape sequences + {[]byte(`\u004`), utf8.RuneError, false}, // too short + {[]byte(`\uXYZW`), utf8.RuneError, false}, // invalid hex + {[]byte(`\u00G1`), utf8.RuneError, false}, // non-hex character + } + + for _, tc := range testCases { + result, isValid := decodeSingleUnicodeEscape(tc.input) + if result != tc.expected || isValid != tc.isValid { + t.Errorf("decodeSingleUnicodeEscape(%s) = (%U, %v); want (%U, %v)", tc.input, result, isValid, tc.expected, tc.isValid) + } + } +} + +func TestDecodeUnicodeEscape(t *testing.T) { + testCases := []struct { + input string + expected rune + size int + }{ + {"\\u0041", 'A', 6}, + {"\\u03B1", 'α', 6}, + {"\\u1F600", 0x1F60, 6}, + {"\\uD830\\uDE03", 0x1C203, 12}, + {"\\uD800\\uDC00", 0x00010000, 12}, + + {"\\u004", utf8.RuneError, -1}, + {"\\uXYZW", utf8.RuneError, -1}, + {"\\uD83D\\u0041", utf8.RuneError, -1}, + } + + for _, tc := range testCases { + r, size := decodeUnicodeEscape([]byte(tc.input)) + if r != tc.expected || size != tc.size { + t.Errorf("decodeUnicodeEscape(%q) = (%U, %d); want (%U, %d)", tc.input, r, size, tc.expected, tc.size) + } + } +} + +func TestUnescapeToUTF8(t *testing.T) { + testCases := []struct { + input []byte + expectedIn int + expectedOut int + isError bool + }{ + // valid escape sequences + {[]byte(`\n`), 2, 1, false}, + {[]byte(`\t`), 2, 1, false}, + {[]byte(`\u0041`), 6, 1, false}, + {[]byte(`\u03B1`), 6, 2, false}, + {[]byte(`\uD830\uDE03`), 12, 4, false}, + + // invalid escape sequences + {[]byte(`\`), -1, -1, true}, // incomplete escape sequence + {[]byte(`\x`), -1, -1, true}, // invalid escape character + {[]byte(`\u`), -1, -1, true}, // incomplete unicode escape sequence + {[]byte(`\u004`), -1, -1, true}, // invalid unicode escape sequence + {[]byte(`\uXYZW`), -1, -1, true}, // invalid unicode escape sequence + {[]byte(`\uD83D\u0041`), -1, -1, true}, // invalid unicode escape sequence + } + + for _, tc := range testCases { + input := make([]byte, len(tc.input)) + copy(input, tc.input) + output := make([]byte, utf8.UTFMax) + inLen, outLen, err := processEscapedUTF8(input, output) + if (err != nil) != tc.isError { + t.Errorf("processEscapedUTF8(%q) = %v; want %v", tc.input, err, tc.isError) + } + + if inLen != tc.expectedIn || outLen != tc.expectedOut { + t.Errorf("processEscapedUTF8(%q) = (%d, %d); want (%d, %d)", tc.input, inLen, outLen, tc.expectedIn, tc.expectedOut) + } + } +} + +func TestUnescape(t *testing.T) { + testCases := []struct { + name string + input []byte + expected []byte + }{ + {"NoEscape", []byte("hello world"), []byte("hello world")}, + {"SingleEscape", []byte("hello\\nworld"), []byte("hello\nworld")}, + {"MultipleEscapes", []byte("line1\\nline2\\r\\nline3"), []byte("line1\nline2\r\nline3")}, + {"UnicodeEscape", []byte("snowman:\\u2603"), []byte("snowman:\u2603")}, + {"Complex", []byte("tc\\n\\u2603\\r\\nend"), []byte("tc\n\u2603\r\nend")}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + output, _ := Unescape(tc.input, make([]byte, len(tc.input)+10)) + if !bytes.Equal(output, tc.expected) { + t.Errorf("unescape(%q) = %q; want %q", tc.input, output, tc.expected) + } + }) + } +} + +func TestUnquoteBytes(t *testing.T) { + tests := []struct { + input []byte + border byte + expected []byte + ok bool + }{ + {[]byte("\"hello\""), '"', []byte("hello"), true}, + {[]byte("'hello'"), '\'', []byte("hello"), true}, + {[]byte("\"hello"), '"', nil, false}, + {[]byte("hello\""), '"', nil, false}, + {[]byte("\"he\\\"llo\""), '"', []byte("he\"llo"), true}, + {[]byte("\"he\\nllo\""), '"', []byte("he\nllo"), true}, + {[]byte("\"\""), '"', []byte(""), true}, + {[]byte("''"), '\'', []byte(""), true}, + {[]byte("\"\\u0041\""), '"', []byte("A"), true}, + {[]byte(`"Hello, 世界"`), '"', []byte("Hello, 世界"), true}, + {[]byte(`"Hello, \x80"`), '"', nil, false}, + } + + for _, tc := range tests { + result, pass := unquoteBytes(tc.input, tc.border) + + if pass != tc.ok { + t.Errorf("unquoteBytes(%q) = %v; want %v", tc.input, pass, tc.ok) + } + + if !bytes.Equal(result, tc.expected) { + t.Errorf("unquoteBytes(%q) = %q; want %q", tc.input, result, tc.expected) + } + } +} diff --git a/examples/gno.land/p/demo/json/gno.mod b/examples/gno.land/p/demo/json/gno.mod new file mode 100644 index 00000000000..8a380644acc --- /dev/null +++ b/examples/gno.land/p/demo/json/gno.mod @@ -0,0 +1,7 @@ +module gno.land/p/demo/json + +require ( + gno.land/p/demo/json/eisel_lemire v0.0.0-latest + gno.land/p/demo/json/ryu v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest +) diff --git a/examples/gno.land/p/demo/json/indent.gno b/examples/gno.land/p/demo/json/indent.gno new file mode 100644 index 00000000000..cdcfd4524ee --- /dev/null +++ b/examples/gno.land/p/demo/json/indent.gno @@ -0,0 +1,144 @@ +package json + +import ( + "bytes" + "strings" +) + +// indentGrowthFactor specifies the growth factor of indenting JSON input. +// A factor no higher than 2 ensures that wasted space never exceeds 50%. +const indentGrowthFactor = 2 + +// IndentJSON takes a JSON byte slice and a string for indentation, +// then formats the JSON according to the specified indent string. +// This function applies indentation rules as follows: +// +// 1. For top-level arrays and objects, no additional indentation is applied. +// +// 2. For nested structures like arrays within arrays or objects, indentation increases. +// +// 3. Indentation is applied after opening brackets ('[' or '{') and before closing brackets (']' or '}'). +// +// 4. Commas and colons are handled appropriately to maintain valid JSON format. +// +// 5. Nested arrays within objects or arrays receive new lines and indentation based on their depth level. +// +// The function returns the formatted JSON as a byte slice and an error if any issues occurred during formatting. +func Indent(data []byte, indent string) ([]byte, error) { + var ( + out bytes.Buffer + level int + inArray bool + arrayDepth int + ) + + for i := 0; i < len(data); i++ { + c := data[i] // current character + + switch c { + case bracketOpen: + arrayDepth++ + if arrayDepth > 1 { + level++ // increase the level if it's nested array + inArray = true + + if err := out.WriteByte(c); err != nil { + return nil, err + } + + if err := writeNewlineAndIndent(&out, level, indent); err != nil { + return nil, err + } + } else { + // case of the top-level array + inArray = true + if err := out.WriteByte(c); err != nil { + return nil, err + } + } + + case bracketClose: + if inArray && arrayDepth > 1 { // nested array + level-- + if err := writeNewlineAndIndent(&out, level, indent); err != nil { + return nil, err + } + } + + arrayDepth-- + if arrayDepth == 0 { + inArray = false + } + + if err := out.WriteByte(c); err != nil { + return nil, err + } + + case curlyOpen: + // check if the empty object or array + // we don't need to apply the indent when it's empty containers. + if i+1 < len(data) && data[i+1] == curlyClose { + if err := out.WriteByte(c); err != nil { + return nil, err + } + + i++ // skip next character + if err := out.WriteByte(data[i]); err != nil { + return nil, err + } + } else { + if err := out.WriteByte(c); err != nil { + return nil, err + } + + level++ + if err := writeNewlineAndIndent(&out, level, indent); err != nil { + return nil, err + } + } + + case curlyClose: + level-- + if err := writeNewlineAndIndent(&out, level, indent); err != nil { + return nil, err + } + if err := out.WriteByte(c); err != nil { + return nil, err + } + + case comma, colon: + if err := out.WriteByte(c); err != nil { + return nil, err + } + if inArray && arrayDepth > 1 { // nested array + if err := writeNewlineAndIndent(&out, level, indent); err != nil { + return nil, err + } + } else if c == colon { + if err := out.WriteByte(' '); err != nil { + return nil, err + } + } + + default: + if err := out.WriteByte(c); err != nil { + return nil, err + } + } + } + + return out.Bytes(), nil +} + +func writeNewlineAndIndent(out *bytes.Buffer, level int, indent string) error { + if err := out.WriteByte('\n'); err != nil { + return err + } + + idt := strings.Repeat(indent, level*indentGrowthFactor) + if _, err := out.WriteString(idt); err != nil { + return err + } + + return nil +} diff --git a/examples/gno.land/p/demo/json/indent_test.gno b/examples/gno.land/p/demo/json/indent_test.gno new file mode 100644 index 00000000000..bc57449b12d --- /dev/null +++ b/examples/gno.land/p/demo/json/indent_test.gno @@ -0,0 +1,84 @@ +package json + +import ( + "bytes" + "testing" +) + +func TestIndentJSON(t *testing.T) { + tests := []struct { + name string + input []byte + indent string + expected []byte + }{ + { + name: "empty object", + input: []byte(`{}`), + indent: " ", + expected: []byte(`{}`), + }, + { + name: "empty array", + input: []byte(`[]`), + indent: " ", + expected: []byte(`[]`), + }, + { + name: "nested object", + input: []byte(`{{}}`), + indent: "\t", + expected: []byte("{\n\t\t{}\n}"), + }, + { + name: "nested array", + input: []byte(`[[[]]]`), + indent: "\t", + expected: []byte("[[\n\t\t[\n\t\t\t\t\n\t\t]\n]]"), + }, + { + name: "top-level array", + input: []byte(`["apple","banana","cherry"]`), + indent: "\t", + expected: []byte(`["apple","banana","cherry"]`), + }, + { + name: "array of arrays", + input: []byte(`["apple",["banana","cherry"],"date"]`), + indent: " ", + expected: []byte("[\"apple\",[\n \"banana\",\n \"cherry\"\n],\"date\"]"), + }, + + { + name: "nested array in object", + input: []byte(`{"fruits":["apple",["banana","cherry"],"date"]}`), + indent: " ", + expected: []byte("{\n \"fruits\": [\"apple\",[\n \"banana\",\n \"cherry\"\n ],\"date\"]\n}"), + }, + { + name: "complex nested structure", + input: []byte(`{"data":{"array":[1,2,3],"bool":true,"nestedArray":[["a","b"],"c"]}}`), + indent: " ", + expected: []byte("{\n \"data\": {\n \"array\": [1,2,3],\"bool\": true,\"nestedArray\": [[\n \"a\",\n \"b\"\n ],\"c\"]\n }\n}"), + }, + { + name: "custom ident character", + input: []byte(`{"fruits":["apple",["banana","cherry"],"date"]}`), + indent: "*", + expected: []byte("{\n**\"fruits\": [\"apple\",[\n****\"banana\",\n****\"cherry\"\n**],\"date\"]\n}"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual, err := Indent(tt.input, tt.indent) + if err != nil { + t.Errorf("IndentJSON() error = %v", err) + return + } + if !bytes.Equal(actual, tt.expected) { + t.Errorf("IndentJSON() = %q, want %q", actual, tt.expected) + } + }) + } +} diff --git a/examples/gno.land/p/demo/json/internal.gno b/examples/gno.land/p/demo/json/internal.gno new file mode 100644 index 00000000000..cae7fbaba7a --- /dev/null +++ b/examples/gno.land/p/demo/json/internal.gno @@ -0,0 +1,198 @@ +package json + +// Reference: https://github.com/freddierice/php_source/blob/467ed5d6edff72219afd3e644516f131118ef48e/ext/json/JSON_parser.c +// Copyright (c) 2005 JSON.org + +// Go implementation is taken from: https://github.com/spyzhov/ajson/blob/master/internal/state.go + +type ( + States int8 // possible states of the parser + Classes int8 // JSON string character types +) + +const __ = -1 + +// enum classes +const ( + C_SPACE Classes = iota /* space */ + C_WHITE /* other whitespace */ + C_LCURB /* { */ + C_RCURB /* } */ + C_LSQRB /* [ */ + C_RSQRB /* ] */ + C_COLON /* : */ + C_COMMA /* , */ + C_QUOTE /* " */ + C_BACKS /* \ */ + C_SLASH /* / */ + C_PLUS /* + */ + C_MINUS /* - */ + C_POINT /* . */ + C_ZERO /* 0 */ + C_DIGIT /* 123456789 */ + C_LOW_A /* a */ + C_LOW_B /* b */ + C_LOW_C /* c */ + C_LOW_D /* d */ + C_LOW_E /* e */ + C_LOW_F /* f */ + C_LOW_L /* l */ + C_LOW_N /* n */ + C_LOW_R /* r */ + C_LOW_S /* s */ + C_LOW_T /* t */ + C_LOW_U /* u */ + C_ABCDF /* ABCDF */ + C_E /* E */ + C_ETC /* everything else */ +) + +// AsciiClasses array maps the 128 ASCII characters into character classes. +var AsciiClasses = [128]Classes{ + /* + This array maps the 128 ASCII characters into character classes. + The remaining Unicode characters should be mapped to C_ETC. + Non-whitespace control characters are errors. + */ + __, __, __, __, __, __, __, __, + __, C_WHITE, C_WHITE, __, __, C_WHITE, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + + C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH, + C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, + C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + + C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC, + + C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC, + C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC, +} + +// QuoteAsciiClasses is a HACK for single quote from AsciiClasses +var QuoteAsciiClasses = [128]Classes{ + /* + This array maps the 128 ASCII characters into character classes. + The remaining Unicode characters should be mapped to C_ETC. + Non-whitespace control characters are errors. + */ + __, __, __, __, __, __, __, __, + __, C_WHITE, C_WHITE, __, __, C_WHITE, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + + C_SPACE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_QUOTE, + C_ETC, C_ETC, C_ETC, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH, + C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, + C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + + C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC, + + C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC, + C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC, +} + +/* +The state codes. +*/ +const ( + GO States = iota /* start */ + OK /* ok */ + OB /* object */ + KE /* key */ + CO /* colon */ + VA /* value */ + AR /* array */ + ST /* string */ + ES /* escape */ + U1 /* u1 */ + U2 /* u2 */ + U3 /* u3 */ + U4 /* u4 */ + MI /* minus */ + ZE /* zero */ + IN /* integer */ + DT /* dot */ + FR /* fraction */ + E1 /* e */ + E2 /* ex */ + E3 /* exp */ + T1 /* tr */ + T2 /* tru */ + T3 /* true */ + F1 /* fa */ + F2 /* fal */ + F3 /* fals */ + F4 /* false */ + N1 /* nu */ + N2 /* nul */ + N3 /* null */ +) + +// List of action codes. +// these constants are defining an action that should be performed under certain conditions. +const ( + cl States = -2 /* colon */ + cm States = -3 /* comma */ + qt States = -4 /* quote */ + bo States = -5 /* bracket open */ + co States = -6 /* curly bracket open */ + bc States = -7 /* bracket close */ + cc States = -8 /* curly bracket close */ + ec States = -9 /* curly bracket empty */ +) + +// StateTransitionTable is the state transition table takes the current state and the current symbol, and returns either +// a new state or an action. An action is represented as a negative number. A JSON text is accepted if at the end of the +// text the state is OK and if the mode is DONE. +var StateTransitionTable = [31][31]States{ + /* + The state transition table takes the current state and the current symbol, + and returns either a new state or an action. An action is represented as a + negative number. A JSON text is accepted if at the end of the text the + state is OK and if the mode is DONE. + white 1-9 ABCDF etc + space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E |*/ + /*start GO*/ {GO, GO, co, __, bo, __, __, __, ST, __, __, __, MI, __, ZE, IN, __, __, __, __, __, F1, __, N1, __, __, T1, __, __, __, __}, + /*ok OK*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*object OB*/ {OB, OB, __, ec, __, __, __, __, ST, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*key KE*/ {KE, KE, __, __, __, __, __, __, ST, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*colon CO*/ {CO, CO, __, __, __, __, cl, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*value VA*/ {VA, VA, co, __, bo, __, __, __, ST, __, __, __, MI, __, ZE, IN, __, __, __, __, __, F1, __, N1, __, __, T1, __, __, __, __}, + /*array AR*/ {AR, AR, co, __, bo, bc, __, __, ST, __, __, __, MI, __, ZE, IN, __, __, __, __, __, F1, __, N1, __, __, T1, __, __, __, __}, + /*string ST*/ {ST, __, ST, ST, ST, ST, ST, ST, qt, ES, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST}, + /*escape ES*/ {__, __, __, __, __, __, __, __, ST, ST, ST, __, __, __, __, __, __, ST, __, __, __, ST, __, ST, ST, __, ST, U1, __, __, __}, + /*u1 U1*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, U2, U2, U2, U2, U2, U2, U2, U2, __, __, __, __, __, __, U2, U2, __}, + /*u2 U2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, U3, U3, U3, U3, U3, U3, U3, U3, __, __, __, __, __, __, U3, U3, __}, + /*u3 U3*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, U4, U4, U4, U4, U4, U4, U4, U4, __, __, __, __, __, __, U4, U4, __}, + /*u4 U4*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, ST, ST, ST, ST, ST, ST, ST, ST, __, __, __, __, __, __, ST, ST, __}, + /*minus MI*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, ZE, IN, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*zero ZE*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, DT, __, __, __, __, __, __, E1, __, __, __, __, __, __, __, __, E1, __}, + /*int IN*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, DT, IN, IN, __, __, __, __, E1, __, __, __, __, __, __, __, __, E1, __}, + /*dot DT*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, FR, FR, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*frac FR*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, __, FR, FR, __, __, __, __, E1, __, __, __, __, __, __, __, __, E1, __}, + /*e E1*/ {__, __, __, __, __, __, __, __, __, __, __, E2, E2, __, E3, E3, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*ex E2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, E3, E3, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*exp E3*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, __, E3, E3, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*tr T1*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, T2, __, __, __, __, __, __}, + /*tru T2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, T3, __, __, __}, + /*true T3*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, OK, __, __, __, __, __, __, __, __, __, __}, + /*fa F1*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, F2, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*fal F2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, F3, __, __, __, __, __, __, __, __}, + /*fals F3*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, F4, __, __, __, __, __}, + /*false F4*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, OK, __, __, __, __, __, __, __, __, __, __}, + /*nu N1*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, N2, __, __, __}, + /*nul N2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, N3, __, __, __, __, __, __, __, __}, + /*null N3*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, OK, __, __, __, __, __, __, __, __}, +} diff --git a/examples/gno.land/p/demo/json/node.gno b/examples/gno.land/p/demo/json/node.gno new file mode 100644 index 00000000000..1e71a101e62 --- /dev/null +++ b/examples/gno.land/p/demo/json/node.gno @@ -0,0 +1,1083 @@ +package json + +import ( + "errors" + "strconv" + "strings" + + "gno.land/p/demo/ufmt" +) + +// Node represents a JSON node. +type Node struct { + prev *Node // prev is the parent node of the current node. + next map[string]*Node // next is the child nodes of the current node. + key *string // key holds the key of the current node in the parent node. + data []byte // byte slice of JSON data + value interface{} // value holds the value of the current node. + nodeType ValueType // NodeType holds the type of the current node. (Object, Array, String, Number, Boolean, Null) + index *int // index holds the index of the current node in the parent array node. + borders [2]int // borders stores the start and end index of the current node in the data. + modified bool // modified indicates the current node is changed or not. +} + +// NewNode creates a new node instance with the given parent node, buffer, type, and key. +func NewNode(prev *Node, b *buffer, typ ValueType, key **string) (*Node, error) { + curr := &Node{ + prev: prev, + data: b.data, + borders: [2]int{b.index, 0}, + key: *key, + nodeType: typ, + modified: false, + } + + if typ == Object || typ == Array { + curr.next = make(map[string]*Node) + } + + if prev != nil { + if prev.IsArray() { + size := len(prev.next) + curr.index = &size + + prev.next[strconv.Itoa(size)] = curr + } else if prev.IsObject() { + if key == nil { + return nil, errors.New("key is required for object") + } + + prev.next[**key] = curr + } else { + return nil, errors.New("invalid parent type") + } + } + + return curr, nil +} + +// load retrieves the value of the current node. +func (n *Node) load() interface{} { + return n.value +} + +// Changed checks the current node is changed or not. +func (n *Node) Changed() bool { + return n.modified +} + +// Key returns the key of the current node. +func (n *Node) Key() string { + if n == nil || n.key == nil { + return "" + } + + return *n.key +} + +// HasKey checks the current node has the given key or not. +func (n *Node) HasKey(key string) bool { + if n == nil { + return false + } + + _, ok := n.next[key] + return ok +} + +// GetKey returns the value of the given key from the current object node. +func (n *Node) GetKey(key string) (*Node, error) { + if n == nil { + return nil, errors.New("node is nil") + } + + if n.Type() != Object { + return nil, ufmt.Errorf("target node is not object type. got: %s", n.Type().String()) + } + + value, ok := n.next[key] + if !ok { + return nil, ufmt.Errorf("key not found: %s", key) + } + + return value, nil +} + +// MustKey returns the value of the given key from the current object node. +func (n *Node) MustKey(key string) *Node { + val, err := n.GetKey(key) + if err != nil { + panic(err) + } + + return val +} + +// UniqueKeyLists traverses the current JSON nodes and collects all the unique keys. +func (n *Node) UniqueKeyLists() []string { + var collectKeys func(*Node) []string + collectKeys = func(node *Node) []string { + if node == nil || !node.IsObject() { + return nil + } + + result := make(map[string]bool) + for key, childNode := range node.next { + result[key] = true + childKeys := collectKeys(childNode) + for _, childKey := range childKeys { + result[childKey] = true + } + } + + keys := make([]string, 0, len(result)) + for key := range result { + keys = append(keys, key) + } + return keys + } + + return collectKeys(n) +} + +// Empty returns true if the current node is empty. +func (n *Node) Empty() bool { + if n == nil { + return false + } + + return len(n.next) == 0 +} + +// Type returns the type (ValueType) of the current node. +func (n *Node) Type() ValueType { + return n.nodeType +} + +// Value returns the value of the current node. +// +// Usage: +// +// root := Unmarshal([]byte(`{"key": "value"}`)) +// val, err := root.MustKey("key").Value() +// if err != nil { +// t.Errorf("Value returns error: %v", err) +// } +// +// result: "value" +func (n *Node) Value() (value interface{}, err error) { + value = n.load() + + if value == nil { + switch n.nodeType { + case Null: + return nil, nil + + case Number: + value, err = ParseFloatLiteral(n.source()) + if err != nil { + return nil, err + } + + n.value = value + + case String: + var ok bool + value, ok = Unquote(n.source(), doubleQuote) + if !ok { + return "", errors.New("invalid string value") + } + + n.value = value + + case Boolean: + if len(n.source()) == 0 { + return nil, errors.New("empty boolean value") + } + + b := n.source()[0] + value = b == 't' || b == 'T' + n.value = value + + case Array: + elems := make([]*Node, len(n.next)) + + for _, e := range n.next { + elems[*e.index] = e + } + + value = elems + n.value = value + + case Object: + obj := make(map[string]*Node, len(n.next)) + + for k, v := range n.next { + obj[k] = v + } + + value = obj + n.value = value + } + } + + return value, nil +} + +// Delete removes the current node from the parent node. +// +// Usage: +// +// root := Unmarshal([]byte(`{"key": "value"}`)) +// if err := root.MustKey("key").Delete(); err != nil { +// t.Errorf("Delete returns error: %v", err) +// } +// +// result: {} (empty object) +func (n *Node) Delete() error { + if n == nil { + return errors.New("can't delete nil node") + } + + if n.prev == nil { + return nil + } + + return n.prev.remove(n) +} + +// Size returns the size (length) of the current array node. +// +// Usage: +// +// root := ArrayNode("", []*Node{StringNode("", "foo"), NumberNode("", 1)}) +// if root == nil { +// t.Errorf("ArrayNode returns nil") +// } +// +// if root.Size() != 2 { +// t.Errorf("ArrayNode returns wrong size: %d", root.Size()) +// } +func (n *Node) Size() int { + if n == nil { + return 0 + } + + return len(n.next) +} + +// Index returns the index of the current node in the parent array node. +// +// Usage: +// +// root := ArrayNode("", []*Node{StringNode("", "foo"), NumberNode("", 1)}) +// if root == nil { +// t.Errorf("ArrayNode returns nil") +// } +// +// if root.MustIndex(1).Index() != 1 { +// t.Errorf("Index returns wrong index: %d", root.MustIndex(1).Index()) +// } +// +// We can also use the index to the byte slice of the JSON data directly. +// +// Example: +// +// root := Unmarshal([]byte(`["foo", 1]`)) +// if root == nil { +// t.Errorf("Unmarshal returns nil") +// } +// +// if string(root.MustIndex(1).source()) != "1" { +// t.Errorf("source returns wrong result: %s", root.MustIndex(1).source()) +// } +func (n *Node) Index() int { + if n == nil || n.index == nil { + return -1 + } + + return *n.index +} + +// MustIndex returns the array element at the given index. +// +// If the index is negative, it returns the index is from the end of the array. +// Also, it panics if the index is not found. +// +// check the Index method for detailed usage. +func (n *Node) MustIndex(expectIdx int) *Node { + val, err := n.GetIndex(expectIdx) + if err != nil { + panic(err) + } + + return val +} + +// GetIndex returns the array element at the given index. +// +// if the index is negative, it returns the index is from the end of the array. +func (n *Node) GetIndex(idx int) (*Node, error) { + if n == nil { + return nil, errors.New("node is nil") + } + + if !n.IsArray() { + return nil, errors.New("node is not array") + } + + if idx > n.Size() { + return nil, errors.New("input index exceeds the array size") + } + + if idx < 0 { + idx += len(n.next) + } + + child, ok := n.next[strconv.Itoa(idx)] + if !ok { + return nil, errors.New("index not found") + } + + return child, nil +} + +// DeleteIndex removes the array element at the given index. +func (n *Node) DeleteIndex(idx int) error { + node, err := n.GetIndex(idx) + if err != nil { + return err + } + + return n.remove(node) +} + +// NullNode creates a new null type node. +// +// Usage: +// +// _ := NullNode("") +func NullNode(key string) *Node { + return &Node{ + key: &key, + value: nil, + nodeType: Null, + modified: true, + } +} + +// NumberNode creates a new number type node. +// +// Usage: +// +// root := NumberNode("", 1) +// if root == nil { +// t.Errorf("NumberNode returns nil") +// } +func NumberNode(key string, value float64) *Node { + return &Node{ + key: &key, + value: value, + nodeType: Number, + modified: true, + } +} + +// StringNode creates a new string type node. +// +// Usage: +// +// root := StringNode("", "foo") +// if root == nil { +// t.Errorf("StringNode returns nil") +// } +func StringNode(key string, value string) *Node { + return &Node{ + key: &key, + value: value, + nodeType: String, + modified: true, + } +} + +// BoolNode creates a new given boolean value node. +// +// Usage: +// +// root := BoolNode("", true) +// if root == nil { +// t.Errorf("BoolNode returns nil") +// } +func BoolNode(key string, value bool) *Node { + return &Node{ + key: &key, + value: value, + nodeType: Boolean, + modified: true, + } +} + +// ArrayNode creates a new array type node. +// +// If the given value is nil, it creates an empty array node. +// +// Usage: +// +// root := ArrayNode("", []*Node{StringNode("", "foo"), NumberNode("", 1)}) +// if root == nil { +// t.Errorf("ArrayNode returns nil") +// } +func ArrayNode(key string, value []*Node) *Node { + curr := &Node{ + key: &key, + nodeType: Array, + modified: true, + } + + curr.next = make(map[string]*Node, len(value)) + if value != nil { + curr.value = value + + for i, v := range value { + idx := i + curr.next[strconv.Itoa(i)] = v + + v.prev = curr + v.index = &idx + } + } + + return curr +} + +// ObjectNode creates a new object type node. +// +// If the given value is nil, it creates an empty object node. +// +// next is a map of key and value pairs of the object. +func ObjectNode(key string, value map[string]*Node) *Node { + curr := &Node{ + nodeType: Object, + key: &key, + next: value, + modified: true, + } + + if value != nil { + curr.value = value + + for key, val := range value { + vkey := key + val.prev = curr + val.key = &vkey + } + } else { + curr.next = make(map[string]*Node) + } + + return curr +} + +// IsArray returns true if the current node is array type. +func (n *Node) IsArray() bool { + return n.nodeType == Array +} + +// IsObject returns true if the current node is object type. +func (n *Node) IsObject() bool { + return n.nodeType == Object +} + +// IsNull returns true if the current node is null type. +func (n *Node) IsNull() bool { + return n.nodeType == Null +} + +// IsBool returns true if the current node is boolean type. +func (n *Node) IsBool() bool { + return n.nodeType == Boolean +} + +// IsString returns true if the current node is string type. +func (n *Node) IsString() bool { + return n.nodeType == String +} + +// IsNumber returns true if the current node is number type. +func (n *Node) IsNumber() bool { + return n.nodeType == Number +} + +// ready checks the current node is ready or not. +// +// the meaning of ready is the current node is parsed and has a valid value. +func (n *Node) ready() bool { + return n.borders[1] != 0 +} + +// source returns the source of the current node. +func (n *Node) source() []byte { + if n == nil { + return nil + } + + if n.ready() && !n.modified && n.data != nil { + return (n.data)[n.borders[0]:n.borders[1]] + } + + return nil +} + +// root returns the root node of the current node. +func (n *Node) root() *Node { + if n == nil { + return nil + } + + curr := n + for curr.prev != nil { + curr = curr.prev + } + + return curr +} + +// GetNull returns the null value if current node is null type. +// +// Usage: +// +// root := Unmarshal([]byte("null")) +// val, err := root.GetNull() +// if err != nil { +// t.Errorf("GetNull returns error: %v", err) +// } +// if val != nil { +// t.Errorf("GetNull returns wrong result: %v", val) +// } +func (n *Node) GetNull() (interface{}, error) { + if n == nil { + return nil, errors.New("node is nil") + } + + if !n.IsNull() { + return nil, errors.New("node is not null") + } + + return nil, nil +} + +// MustNull returns the null value if current node is null type. +// +// It panics if the current node is not null type. +func (n *Node) MustNull() interface{} { + v, err := n.GetNull() + if err != nil { + panic(err) + } + + return v +} + +// GetNumeric returns the numeric (int/float) value if current node is number type. +// +// Usage: +// +// root := Unmarshal([]byte("10.5")) +// val, err := root.GetNumeric() +// if err != nil { +// t.Errorf("GetNumeric returns error: %v", err) +// } +// println(val) // 10.5 +func (n *Node) GetNumeric() (float64, error) { + if n == nil { + return 0, errors.New("node is nil") + } + + if n.nodeType != Number { + return 0, errors.New("node is not number") + } + + val, err := n.Value() + if err != nil { + return 0, err + } + + v, ok := val.(float64) + if !ok { + return 0, errors.New("node is not number") + } + + return v, nil +} + +// MustNumeric returns the numeric (int/float) value if current node is number type. +// +// It panics if the current node is not number type. +func (n *Node) MustNumeric() float64 { + v, err := n.GetNumeric() + if err != nil { + panic(err) + } + + return v +} + +// GetString returns the string value if current node is string type. +// +// Usage: +// +// root, err := Unmarshal([]byte("foo")) +// if err != nil { +// t.Errorf("Error on Unmarshal(): %s", err) +// } +// +// str, err := root.GetString() +// if err != nil { +// t.Errorf("should retrieve string value: %s", err) +// } +// +// println(str) // "foo" +func (n *Node) GetString() (string, error) { + if n == nil { + return "", errors.New("string node is empty") + } + + if !n.IsString() { + return "", errors.New("node type is not string") + } + + val, err := n.Value() + if err != nil { + return "", err + } + + v, ok := val.(string) + if !ok { + return "", errors.New("node is not string") + } + + return v, nil +} + +// MustString returns the string value if current node is string type. +// +// It panics if the current node is not string type. +func (n *Node) MustString() string { + v, err := n.GetString() + if err != nil { + panic(err) + } + + return v +} + +// GetBool returns the boolean value if current node is boolean type. +// +// Usage: +// +// root := Unmarshal([]byte("true")) +// val, err := root.GetBool() +// if err != nil { +// t.Errorf("GetBool returns error: %v", err) +// } +// println(val) // true +func (n *Node) GetBool() (bool, error) { + if n == nil { + return false, errors.New("node is nil") + } + + if n.nodeType != Boolean { + return false, errors.New("node is not boolean") + } + + val, err := n.Value() + if err != nil { + return false, err + } + + v, ok := val.(bool) + if !ok { + return false, errors.New("node is not boolean") + } + + return v, nil +} + +// MustBool returns the boolean value if current node is boolean type. +// +// It panics if the current node is not boolean type. +func (n *Node) MustBool() bool { + v, err := n.GetBool() + if err != nil { + panic(err) + } + + return v +} + +// GetArray returns the array value if current node is array type. +// +// Usage: +// +// root := Must(Unmarshal([]byte(`["foo", 1]`))) +// arr, err := root.GetArray() +// if err != nil { +// t.Errorf("GetArray returns error: %v", err) +// } +// +// for _, val := range arr { +// println(val) +// } +// +// result: "foo", 1 +func (n *Node) GetArray() ([]*Node, error) { + if n == nil { + return nil, errors.New("node is nil") + } + + if n.nodeType != Array { + return nil, errors.New("node is not array") + } + + val, err := n.Value() + if err != nil { + return nil, err + } + + v, ok := val.([]*Node) + if !ok { + return nil, errors.New("node is not array") + } + + return v, nil +} + +// MustArray returns the array value if current node is array type. +// +// It panics if the current node is not array type. +func (n *Node) MustArray() []*Node { + v, err := n.GetArray() + if err != nil { + panic(err) + } + + return v +} + +// AppendArray appends the given values to the current array node. +// +// If the current node is not array type, it returns an error. +// +// Example 1: +// +// root := Must(Unmarshal([]byte(`[{"foo":"bar"}]`))) +// if err := root.AppendArray(NullNode("")); err != nil { +// t.Errorf("should not return error: %s", err) +// } +// +// result: [{"foo":"bar"}, null] +// +// Example 2: +// +// root := Must(Unmarshal([]byte(`["bar", "baz"]`))) +// err := root.AppendArray(NumberNode("", 1), StringNode("", "foo")) +// if err != nil { +// t.Errorf("AppendArray returns error: %v", err) +// } +// +// result: ["bar", "baz", 1, "foo"] +func (n *Node) AppendArray(value ...*Node) error { + if !n.IsArray() { + return errors.New("can't append value to non-array node") + } + + for _, val := range value { + if err := n.append(nil, val); err != nil { + return err + } + } + + n.mark() + return nil +} + +// ArrayEach executes the callback for each element in the JSON array. +// +// Usage: +// +// jsonArrayNode.ArrayEach(func(i int, valueNode *Node) { +// ufmt.Println(i, valueNode) +// }) +func (n *Node) ArrayEach(callback func(i int, target *Node)) { + if n == nil || !n.IsArray() { + return + } + + for idx := 0; idx < len(n.next); idx++ { + element, err := n.GetIndex(idx) + if err != nil { + continue + } + + callback(idx, element) + } +} + +// GetObject returns the object value if current node is object type. +// +// Usage: +// +// root := Must(Unmarshal([]byte(`{"key": "value"}`))) +// obj, err := root.GetObject() +// if err != nil { +// t.Errorf("GetObject returns error: %v", err) +// } +// +// result: map[string]*Node{"key": StringNode("key", "value")} +func (n *Node) GetObject() (map[string]*Node, error) { + if n == nil { + return nil, errors.New("node is nil") + } + + if !n.IsObject() { + return nil, errors.New("node is not object") + } + + val, err := n.Value() + if err != nil { + return nil, err + } + + v, ok := val.(map[string]*Node) + if !ok { + return nil, errors.New("node is not object") + } + + return v, nil +} + +// MustObject returns the object value if current node is object type. +// +// It panics if the current node is not object type. +func (n *Node) MustObject() map[string]*Node { + v, err := n.GetObject() + if err != nil { + panic(err) + } + + return v +} + +// AppendObject appends the given key and value to the current object node. +// +// If the current node is not object type, it returns an error. +func (n *Node) AppendObject(key string, value *Node) error { + if !n.IsObject() { + return errors.New("can't append value to non-object node") + } + + if err := n.append(&key, value); err != nil { + return err + } + + n.mark() + return nil +} + +// ObjectEach executes the callback for each key-value pair in the JSON object. +// +// Usage: +// +// jsonObjectNode.ObjectEach(func(key string, valueNode *Node) { +// ufmt.Println(key, valueNode) +// }) +func (n *Node) ObjectEach(callback func(key string, value *Node)) { + if n == nil || !n.IsObject() { + return + } + + for key, child := range n.next { + callback(key, child) + } +} + +// String converts the node to a string representation. +func (n *Node) String() string { + if n == nil { + return "" + } + + if n.ready() && !n.modified { + return string(n.source()) + } + + val, err := Marshal(n) + if err != nil { + return "error: " + err.Error() + } + + return string(val) +} + +// Path builds the path of the current node. +// +// For example: +// +// { "key": { "sub": [ "val1", "val2" ] }} +// +// The path of "val2" is: $.key.sub[1] +func (n *Node) Path() string { + if n == nil { + return "" + } + + var sb strings.Builder + + if n.prev == nil { + sb.WriteString("$") + } else { + sb.WriteString(n.prev.Path()) + + if n.key != nil { + sb.WriteString("['" + n.Key() + "']") + } else { + sb.WriteString("[" + strconv.Itoa(n.Index()) + "]") + } + } + + return sb.String() +} + +// mark marks the current node as modified. +func (n *Node) mark() { + node := n + for node != nil && !node.modified { + node.modified = true + node = node.prev + } +} + +// isContainer checks the current node type is array or object. +func (n *Node) isContainer() bool { + return n.IsArray() || n.IsObject() +} + +// remove removes the value from the current container type node. +func (n *Node) remove(v *Node) error { + if !n.isContainer() { + return ufmt.Errorf( + "can't remove value from non-array or non-object node. got=%s", + n.Type().String(), + ) + } + + if v.prev != n { + return errors.New("invalid parent node") + } + + n.mark() + if n.IsArray() { + delete(n.next, strconv.Itoa(*v.index)) + n.dropIndex(*v.index) + } else { + delete(n.next, *v.key) + } + + v.prev = nil + return nil +} + +// dropIndex rebase the index of current array node values. +func (n *Node) dropIndex(idx int) { + for i := idx + 1; i <= len(n.next); i++ { + prv := i - 1 + if curr, ok := n.next[strconv.Itoa(i)]; ok { + curr.index = &prv + n.next[strconv.Itoa(prv)] = curr + } + + delete(n.next, strconv.Itoa(i)) + } +} + +// append is a helper function to append the given value to the current container type node. +func (n *Node) append(key *string, val *Node) error { + if n.isSameOrParentNode(val) { + return errors.New("can't append same or parent node") + } + + if val.prev != nil { + if err := val.prev.remove(val); err != nil { + return err + } + } + + val.prev = n + val.key = key + + if key == nil { + size := len(n.next) + val.index = &size + n.next[strconv.Itoa(size)] = val + } else { + if old, ok := n.next[*key]; ok { + if err := n.remove(old); err != nil { + return err + } + } + n.next[*key] = val + } + + return nil +} + +func (n *Node) isSameOrParentNode(nd *Node) bool { + return n == nd || n.isParentNode(nd) +} + +func (n *Node) isParentNode(nd *Node) bool { + if n == nil { + return false + } + + for curr := nd.prev; curr != nil; curr = curr.prev { + if curr == n { + return true + } + } + + return false +} + +// cptrs returns the pointer of the given string value. +func cptrs(cpy *string) *string { + if cpy == nil { + return nil + } + + val := *cpy + + return &val +} + +// cptri returns the pointer of the given integer value. +func cptri(i *int) *int { + if i == nil { + return nil + } + + val := *i + return &val +} + +// Must panics if the given node is not fulfilled the expectation. +// Usage: +// +// node := Must(Unmarshal([]byte(`{"key": "value"}`)) +func Must(root *Node, expect error) *Node { + if expect != nil { + panic(expect) + } + + return root +} diff --git a/examples/gno.land/p/demo/json/node_test.gno b/examples/gno.land/p/demo/json/node_test.gno new file mode 100644 index 00000000000..dbc82369f68 --- /dev/null +++ b/examples/gno.land/p/demo/json/node_test.gno @@ -0,0 +1,1392 @@ +package json + +import ( + "bytes" + "sort" + "strconv" + "strings" + "testing" + + "gno.land/p/demo/ufmt" +) + +var ( + nilKey *string + dummyKey = "key" +) + +type _args struct { + prev *Node + buf *buffer + typ ValueType + key **string +} + +type simpleNode struct { + name string + node *Node +} + +func TestNode_CreateNewNode(t *testing.T) { + rel := &dummyKey + + tests := []struct { + name string + args _args + expectCurr *Node + expectErr bool + expectPanic bool + }{ + { + name: "child for non container type", + args: _args{ + prev: BoolNode("", true), + buf: newBuffer(make([]byte, 10)), + typ: Boolean, + key: &rel, + }, + expectCurr: nil, + expectErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + if tt.expectPanic { + return + } + t.Errorf("%s panic occurred when not expected: %v", tt.name, r) + } else if tt.expectPanic { + t.Errorf("%s expected panic but didn't occur", tt.name) + } + }() + + got, err := NewNode(tt.args.prev, tt.args.buf, tt.args.typ, tt.args.key) + if (err != nil) != tt.expectErr { + t.Errorf("%s error = %v, expect error %v", tt.name, err, tt.expectErr) + return + } + + if tt.expectErr { + return + } + + if !compareNodes(got, tt.expectCurr) { + t.Errorf("%s got = %v, want %v", tt.name, got, tt.expectCurr) + } + }) + } +} + +func TestNode_Value(t *testing.T) { + tests := []struct { + name string + data []byte + _type ValueType + expected interface{} + errExpected bool + }{ + {name: "null", data: []byte("null"), _type: Null, expected: nil}, + {name: "1", data: []byte("1"), _type: Number, expected: float64(1)}, + {name: ".1", data: []byte(".1"), _type: Number, expected: float64(.1)}, + {name: "-.1e1", data: []byte("-.1e1"), _type: Number, expected: float64(-1)}, + {name: "string", data: []byte("\"foo\""), _type: String, expected: "foo"}, + {name: "space", data: []byte("\"foo bar\""), _type: String, expected: "foo bar"}, + {name: "true", data: []byte("true"), _type: Boolean, expected: true}, + {name: "invalid true", data: []byte("tru"), _type: Unknown, errExpected: true}, + {name: "invalid false", data: []byte("fals"), _type: Unknown, errExpected: true}, + {name: "false", data: []byte("false"), _type: Boolean, expected: false}, + {name: "e1", data: []byte("e1"), _type: Unknown, errExpected: true}, + {name: "1a", data: []byte("1a"), _type: Unknown, errExpected: true}, + {name: "string error", data: []byte("\"foo\nbar\""), _type: String, errExpected: true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + curr := &Node{ + data: tt.data, + nodeType: tt._type, + borders: [2]int{0, len(tt.data)}, + } + + got, err := curr.Value() + if err != nil { + if !tt.errExpected { + t.Errorf("%s error = %v, expect error %v", tt.name, err, tt.errExpected) + } + return + } + + if got != tt.expected { + t.Errorf("%s got = %v, want %v", tt.name, got, tt.expected) + } + }) + } +} + +func TestNode_Delete(t *testing.T) { + root := Must(Unmarshal([]byte(`{"foo":"bar"}`))) + if err := root.Delete(); err != nil { + t.Errorf("Delete returns error: %v", err) + } + + if value, err := Marshal(root); err != nil { + t.Errorf("Marshal returns error: %v", err) + } else if string(value) != `{"foo":"bar"}` { + t.Errorf("Marshal returns wrong value: %s", string(value)) + } + + foo := root.MustKey("foo") + if err := foo.Delete(); err != nil { + t.Errorf("Delete returns error while handling foo: %v", err) + } + + if value, err := Marshal(root); err != nil { + t.Errorf("Marshal returns error: %v", err) + } else if string(value) != `{}` { + t.Errorf("Marshal returns wrong value: %s", string(value)) + } + + if value, err := Marshal(foo); err != nil { + t.Errorf("Marshal returns error: %v", err) + } else if string(value) != `"bar"` { + t.Errorf("Marshal returns wrong value: %s", string(value)) + } + + if foo.prev != nil { + t.Errorf("foo.prev should be nil") + } +} + +func TestNode_ObjectNode(t *testing.T) { + objs := map[string]*Node{ + "key1": NullNode("null"), + "key2": NumberNode("answer", 42), + "key3": StringNode("string", "foobar"), + "key4": BoolNode("bool", true), + } + + node := ObjectNode("test", objs) + + if len(node.next) != len(objs) { + t.Errorf("ObjectNode: want %v got %v", len(objs), len(node.next)) + } + + for k, v := range objs { + if node.next[k] == nil { + t.Errorf("ObjectNode: want %v got %v", v, node.next[k]) + } + } +} + +func TestNode_AppendObject(t *testing.T) { + if err := Must(Unmarshal([]byte(`{"foo":"bar","baz":null}`))).AppendObject("biz", NullNode("")); err != nil { + t.Errorf("AppendArray should return error") + } + + root := Must(Unmarshal([]byte(`{"foo":"bar"}`))) + if err := root.AppendObject("baz", NullNode("")); err != nil { + t.Errorf("AppendObject should not return error: %s", err) + } + + if value, err := Marshal(root); err != nil { + t.Errorf("Marshal returns error: %v", err) + } else if isSameObject(string(value), `"{"foo":"bar","baz":null}"`) { + t.Errorf("Marshal returns wrong value: %s", string(value)) + } + + // FIXME: this may fail if execute test in more than 3 times in a row. + if err := root.AppendObject("biz", NumberNode("", 42)); err != nil { + t.Errorf("AppendObject returns error: %v", err) + } + + val, err := Marshal(root) + if err != nil { + t.Errorf("Marshal returns error: %v", err) + } + + // FIXME: this may fail if execute test in more than 3 times in a row. + if isSameObject(string(val), `"{"foo":"bar","baz":null,"biz":42}"`) { + t.Errorf("Marshal returns wrong value: %s", string(val)) + } +} + +func TestNode_ArrayNode(t *testing.T) { + arr := []*Node{ + NullNode("nil"), + NumberNode("num", 42), + StringNode("str", "foobar"), + BoolNode("bool", true), + } + + node := ArrayNode("test", arr) + + if len(node.next) != len(arr) { + t.Errorf("ArrayNode: want %v got %v", len(arr), len(node.next)) + } + + for i, v := range arr { + if node.next[strconv.Itoa(i)] == nil { + t.Errorf("ArrayNode: want %v got %v", v, node.next[strconv.Itoa(i)]) + } + } +} + +func TestNode_AppendArray(t *testing.T) { + if err := Must(Unmarshal([]byte(`[{"foo":"bar"}]`))).AppendArray(NullNode("")); err != nil { + t.Errorf("should return error") + } + + root := Must(Unmarshal([]byte(`[{"foo":"bar"}]`))) + if err := root.AppendArray(NullNode("")); err != nil { + t.Errorf("should not return error: %s", err) + } + + if value, err := Marshal(root); err != nil { + t.Errorf("Marshal returns error: %v", err) + } else if string(value) != `[{"foo":"bar"},null]` { + t.Errorf("Marshal returns wrong value: %s", string(value)) + } + + if err := root.AppendArray( + NumberNode("", 1), + StringNode("", "foo"), + Must(Unmarshal([]byte(`[0,1,null,true,"example"]`))), + Must(Unmarshal([]byte(`{"foo": true, "bar": null, "baz": 123}`))), + ); err != nil { + t.Errorf("AppendArray returns error: %v", err) + } + + if value, err := Marshal(root); err != nil { + t.Errorf("Marshal returns error: %v", err) + } else if string(value) != `[{"foo":"bar"},null,1,"foo",[0,1,null,true,"example"],{"foo": true, "bar": null, "baz": 123}]` { + t.Errorf("Marshal returns wrong value: %s", string(value)) + } +} + +/******** value getter ********/ + +func TestNode_GetBool(t *testing.T) { + root, err := Unmarshal([]byte(`true`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + return + } + + value, err := root.GetBool() + if err != nil { + t.Errorf("Error on root.GetBool(): %s", err.Error()) + } + + if !value { + t.Errorf("root.GetBool() is corrupted") + } +} + +func TestNode_GetBool_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"literally null node", NullNode("")}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetBool(); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_IsBool(t *testing.T) { + tests := []simpleNode{ + {"true", BoolNode("", true)}, + {"false", BoolNode("", false)}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if !tt.node.IsBool() { + t.Errorf("%s should be a bool", tt.name) + } + }) + } +} + +func TestNode_IsBool_With_Unmarshal(t *testing.T) { + tests := []struct { + name string + json []byte + want bool + }{ + {"true", []byte("true"), true}, + {"false", []byte("false"), true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + root, err := Unmarshal(tt.json) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + } + + if root.IsBool() != tt.want { + t.Errorf("%s should be a bool", tt.name) + } + }) + } +} + +var nullJson = []byte(`null`) + +func TestNode_GetNull(t *testing.T) { + root, err := Unmarshal(nullJson) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + } + + value, err := root.GetNull() + if err != nil { + t.Errorf("error occurred while getting null, %s", err) + } + + if value != nil { + t.Errorf("value is not matched. expected: nil, got: %v", value) + } +} + +func TestNode_GetNull_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"number node is null", NumberNode("", 42)}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetNull(); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_MustNull(t *testing.T) { + root, err := Unmarshal(nullJson) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + } + + value := root.MustNull() + if value != nil { + t.Errorf("value is not matched. expected: nil, got: %v", value) + } +} + +func TestNode_GetNumeric_Float(t *testing.T) { + root, err := Unmarshal([]byte(`123.456`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + return + } + + value, err := root.GetNumeric() + if err != nil { + t.Errorf("Error on root.GetNumeric(): %s", err) + } + + if value != float64(123.456) { + t.Errorf(ufmt.Sprintf("value is not matched. expected: 123.456, got: %v", value)) + } +} + +func TestNode_GetNumeric_Scientific_Notation(t *testing.T) { + root, err := Unmarshal([]byte(`1e3`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + return + } + + value, err := root.GetNumeric() + if err != nil { + t.Errorf("Error on root.GetNumeric(): %s", err) + } + + if value != float64(1000) { + t.Errorf(ufmt.Sprintf("value is not matched. expected: 1000, got: %v", value)) + } +} + +func TestNode_GetNumeric_With_Unmarshal(t *testing.T) { + root, err := Unmarshal([]byte(`123`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + return + } + + value, err := root.GetNumeric() + if err != nil { + t.Errorf("Error on root.GetNumeric(): %s", err) + } + + if value != float64(123) { + t.Errorf(ufmt.Sprintf("value is not matched. expected: 123, got: %v", value)) + } +} + +func TestNode_GetNumeric_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"null node", NullNode("")}, + {"string node", StringNode("", "123")}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetNumeric(); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_GetString(t *testing.T) { + root, err := Unmarshal([]byte(`"123foobar 3456"`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + } + + value, err := root.GetString() + if err != nil { + t.Errorf("Error on root.GetString(): %s", err) + } + + if value != "123foobar 3456" { + t.Errorf(ufmt.Sprintf("value is not matched. expected: 123, got: %s", value)) + } +} + +func TestNode_GetString_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"null node", NullNode("")}, + {"number node", NumberNode("", 123)}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetString(); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_MustString(t *testing.T) { + tests := []struct { + name string + data []byte + }{ + {"foo", []byte(`"foo"`)}, + {"foo bar", []byte(`"foo bar"`)}, + {"", []byte(`""`)}, + {"안녕하세요", []byte(`"안녕하세요"`)}, + {"こんにちは", []byte(`"こんにちは"`)}, + {"你好", []byte(`"你好"`)}, + {"one \"encoded\" string", []byte(`"one \"encoded\" string"`)}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + root, err := Unmarshal(tt.data) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + } + + value := root.MustString() + if value != tt.name { + t.Errorf("value is not matched. expected: %s, got: %s", tt.name, value) + } + }) + } +} + +func TestUnmarshal_Array(t *testing.T) { + root, err := Unmarshal([]byte(" [1,[\"1\",[1,[1,2,3]]]]\r\n")) + if err != nil { + t.Errorf("Error on Unmarshal: %s", err.Error()) + } + + if root == nil { + t.Errorf("Error on Unmarshal: root is nil") + } + + if root.Type() != Array { + t.Errorf("Error on Unmarshal: wrong type") + } + + array, err := root.GetArray() + if err != nil { + t.Errorf("error occurred while getting array, %s", err) + } else if len(array) != 2 { + t.Errorf("expected 2 elements, got %d", len(array)) + } else if val, err := array[0].GetNumeric(); err != nil { + t.Errorf("value of array[0] is not numeric. got: %v", array[0].value) + } else if val != 1 { + t.Errorf("Error on array[0].GetNumeric(): expected to be '1', got: %v", val) + } else if val, err := array[1].GetArray(); err != nil { + t.Errorf("error occurred while getting array, %s", err.Error()) + } else if len(val) != 2 { + t.Errorf("Error on array[1].GetArray(): expected 2 elements, got %d", len(val)) + } else if el, err := val[0].GetString(); err != nil { + t.Errorf("error occurred while getting string, %s", err.Error()) + } else if el != "1" { + t.Errorf("Error on val[0].GetString(): expected to be '1', got: %s", el) + } +} + +var sampleArr = []byte(`[-1, 2, 3, 4, 5, 6]`) + +func TestNode_GetArray(t *testing.T) { + root, err := Unmarshal(sampleArr) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + return + } + + array, err := root.GetArray() + if err != nil { + t.Errorf("Error on root.GetArray(): %s", err) + } + + if len(array) != 6 { + t.Errorf(ufmt.Sprintf("length is not matched. expected: 3, got: %d", len(array))) + } + + for i, node := range array { + for j, val := range []int{-1, 2, 3, 4, 5, 6} { + if i == j { + if v, err := node.GetNumeric(); err != nil { + t.Errorf(ufmt.Sprintf("Error on node.GetNumeric(): %s", err)) + } else if v != float64(val) { + t.Errorf(ufmt.Sprintf("value is not matched. expected: %d, got: %v", val, v)) + } + } + } + } +} + +func TestNode_GetArray_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"null node", NullNode("")}, + {"number node", NumberNode("", 123)}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetArray(); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_IsArray(t *testing.T) { + root, err := Unmarshal(sampleArr) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + return + } + + if root.Type() != Array { + t.Errorf(ufmt.Sprintf("Must be an array. got: %s", root.Type().String())) + } +} + +func TestNode_ArrayEach(t *testing.T) { + tests := []struct { + name string + json string + expected []int + }{ + { + name: "empty array", + json: `[]`, + expected: []int{}, + }, + { + name: "single element", + json: `[42]`, + expected: []int{42}, + }, + { + name: "multiple elements", + json: `[1, 2, 3, 4, 5]`, + expected: []int{1, 2, 3, 4, 5}, + }, + { + name: "multiple elements but all values are same", + json: `[1, 1, 1, 1, 1]`, + expected: []int{1, 1, 1, 1, 1}, + }, + { + name: "multiple elements with non-numeric values", + json: `["a", "b", "c", "d", "e"]`, + expected: []int{}, + }, + { + name: "non-array node", + json: `{"not": "an array"}`, + expected: []int{}, + }, + { + name: "array containing numeric and non-numeric elements", + json: `["1", 2, 3, "4", 5, "6"]`, + expected: []int{2, 3, 5}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + root, err := Unmarshal([]byte(tc.json)) + if err != nil { + t.Fatalf("Unmarshal failed: %v", err) + } + + var result []int // callback result + root.ArrayEach(func(index int, element *Node) { + if val, err := strconv.Atoi(element.String()); err == nil { + result = append(result, val) + } + }) + + if len(result) != len(tc.expected) { + t.Errorf("%s: expected %d elements, got %d", tc.name, len(tc.expected), len(result)) + return + } + + for i, val := range result { + if val != tc.expected[i] { + t.Errorf("%s: expected value at index %d to be %d, got %d", tc.name, i, tc.expected[i], val) + } + } + }) + } +} + +func TestNode_Key(t *testing.T) { + root, err := Unmarshal([]byte(`{"foo": true, "bar": null, "baz": 123, "biz": [1,2,3]}`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + } + + obj := root.MustObject() + for key, node := range obj { + if key != node.Key() { + t.Errorf("Key() = %v, want %v", node.Key(), key) + } + } + + keys := []string{"foo", "bar", "baz", "biz"} + for _, key := range keys { + if obj[key].Key() != key { + t.Errorf("Key() = %v, want %v", obj[key].Key(), key) + } + } + + // TODO: resolve stack overflow + // if root.MustKey("foo").Clone().Key() != "" { + // t.Errorf("wrong key found for cloned key") + // } + + if (*Node)(nil).Key() != "" { + t.Errorf("wrong key found for nil node") + } +} + +func TestNode_Size(t *testing.T) { + root, err := Unmarshal(sampleArr) + if err != nil { + t.Errorf("error occurred while unmarshal") + } + + size := root.Size() + if size != 6 { + t.Errorf(ufmt.Sprintf("Size() must be 6. got: %v", size)) + } + + if (*Node)(nil).Size() != 0 { + t.Errorf(ufmt.Sprintf("Size() must be 0. got: %v", (*Node)(nil).Size())) + } +} + +func TestNode_Index(t *testing.T) { + root, err := Unmarshal([]byte(`[1, 2, 3, 4, 5, 6]`)) + if err != nil { + t.Error("error occurred while unmarshal") + } + + arr := root.MustArray() + for i, node := range arr { + if i != node.Index() { + t.Errorf(ufmt.Sprintf("Index() must be nil. got: %v", i)) + } + } +} + +func TestNode_Index_Fail(t *testing.T) { + tests := []struct { + name string + node *Node + want int + }{ + {"nil node", (*Node)(nil), -1}, + {"null node", NullNode(""), -1}, + {"object node", ObjectNode("", nil), -1}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.node.Index(); got != tt.want { + t.Errorf("Index() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNode_GetIndex(t *testing.T) { + root := Must(Unmarshal([]byte(`[1, 2, 3, 4, 5, 6]`))) + expected := []int{1, 2, 3, 4, 5, 6} + + if len(expected) != root.Size() { + t.Errorf("length is not matched. expected: %d, got: %d", len(expected), root.Size()) + } + + // TODO: if length exceeds, stack overflow occurs. need to fix + for i, v := range expected { + val, err := root.GetIndex(i) + if err != nil { + t.Errorf("error occurred while getting index %d, %s", i, err) + } + + if val.MustNumeric() != float64(v) { + t.Errorf("value is not matched. expected: %d, got: %v", v, val.MustNumeric()) + } + } +} + +func TestNode_GetIndex_InputIndex_Exceed_Original_Node_Index(t *testing.T) { + root, err := Unmarshal([]byte(`[1, 2, 3, 4, 5, 6]`)) + if err != nil { + t.Errorf("error occurred while unmarshal") + } + + _, err = root.GetIndex(10) + if err == nil { + t.Errorf("GetIndex should return error") + } +} + +func TestNode_DeleteIndex(t *testing.T) { + tests := []struct { + name string + expected string + index int + ok bool + }{ + {`null`, ``, 0, false}, + {`1`, ``, 0, false}, + {`{}`, ``, 0, false}, + {`{"foo":"bar"}`, ``, 0, false}, + {`true`, ``, 0, false}, + {`[]`, ``, 0, false}, + {`[]`, ``, -1, false}, + {`[1]`, `[]`, 0, true}, + {`[{}]`, `[]`, 0, true}, + {`[{}, [], 42]`, `[{}, []]`, -1, true}, + {`[{}, [], 42]`, `[[], 42]`, 0, true}, + {`[{}, [], 42]`, `[{}, 42]`, 1, true}, + {`[{}, [], 42]`, `[{}, []]`, 2, true}, + {`[{}, [], 42]`, ``, 10, false}, + {`[{}, [], 42]`, ``, -10, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + root := Must(Unmarshal([]byte(tt.name))) + err := root.DeleteIndex(tt.index) + if err != nil && tt.ok { + t.Errorf("DeleteIndex returns error: %v", err) + } + }) + } +} + +func TestNode_GetKey(t *testing.T) { + root, err := Unmarshal([]byte(`{"foo": true, "bar": null}`)) + if err != nil { + t.Error("error occurred while unmarshal") + } + + value, err := root.GetKey("foo") + if err != nil { + t.Errorf("error occurred while getting key, %s", err) + } + + if value.MustBool() != true { + t.Errorf("value is not matched. expected: true, got: %v", value.MustBool()) + } + + value, err = root.GetKey("bar") + if err != nil { + t.Errorf("error occurred while getting key, %s", err) + } + + _, err = root.GetKey("baz") + if err == nil { + t.Errorf("key baz is not exist. must be failed") + } + + if value.MustNull() != nil { + t.Errorf("value is not matched. expected: nil, got: %v", value.MustNull()) + } +} + +func TestNode_GetKey_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"null node", NullNode("")}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetKey(""); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_GetUniqueKeyList(t *testing.T) { + tests := []struct { + name string + json string + expected []string + }{ + { + name: "simple foo/bar", + json: `{"foo": true, "bar": null}`, + expected: []string{"foo", "bar"}, + }, + { + name: "empty object", + json: `{}`, + expected: []string{}, + }, + { + name: "nested object", + json: `{ + "outer": { + "inner": { + "key": "value" + }, + "array": [1, 2, 3] + }, + "another": "item" + }`, + expected: []string{"outer", "inner", "key", "array", "another"}, + }, + { + name: "complex object", + json: `{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated": false, + "IDs": [116, 943, 234, 38793] + } + }`, + expected: []string{"Image", "Width", "Height", "Title", "Thumbnail", "Url", "Animated", "IDs"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + root, err := Unmarshal([]byte(tt.json)) + if err != nil { + t.Errorf("error occurred while unmarshal") + } + + value := root.UniqueKeyLists() + if len(value) != len(tt.expected) { + t.Errorf("%s length must be %v. got: %v. retrieved keys: %s", tt.name, len(tt.expected), len(value), value) + } + + for _, key := range value { + if !contains(tt.expected, key) { + t.Errorf("EachKey() must be in %v. got: %v", tt.expected, key) + } + } + }) + } +} + +// TODO: resolve stack overflow +func TestNode_IsEmpty(t *testing.T) { + tests := []struct { + name string + node *Node + expected bool + }{ + {"nil node", (*Node)(nil), false}, // nil node is not empty. + // {"null node", NullNode(""), true}, + {"empty object", ObjectNode("", nil), true}, + {"empty array", ArrayNode("", nil), true}, + {"non-empty object", ObjectNode("", map[string]*Node{"foo": BoolNode("foo", true)}), false}, + {"non-empty array", ArrayNode("", []*Node{BoolNode("0", true)}), false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.node.Empty(); got != tt.expected { + t.Errorf("%s = %v, want %v", tt.name, got, tt.expected) + } + }) + } +} + +func TestNode_Index_EmptyList(t *testing.T) { + root, err := Unmarshal([]byte(`[]`)) + if err != nil { + t.Errorf("error occurred while unmarshal") + } + + array := root.MustArray() + for i, node := range array { + if i != node.Index() { + t.Errorf(ufmt.Sprintf("Index() must be nil. got: %v", i)) + } + } +} + +func TestNode_GetObject(t *testing.T) { + root, err := Unmarshal([]byte(`{"foo": true,"bar": null}`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + return + } + + value, err := root.GetObject() + if err != nil { + t.Errorf("Error on root.GetObject(): %s", err.Error()) + } + + if _, ok := value["foo"]; !ok { + t.Errorf("root.GetObject() is corrupted: foo") + } + + if _, ok := value["bar"]; !ok { + t.Errorf("root.GetObject() is corrupted: bar") + } +} + +func TestNode_GetObject_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"get object from null node", NullNode("")}, + {"not object node", NumberNode("", 123)}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetObject(); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_ObjectEach(t *testing.T) { + tests := []struct { + name string + json string + expected map[string]int + }{ + { + name: "empty object", + json: `{}`, + expected: make(map[string]int), + }, + { + name: "single key-value pair", + json: `{"key": 42}`, + expected: map[string]int{"key": 42}, + }, + { + name: "multiple key-value pairs", + json: `{"one": 1, "two": 2, "three": 3}`, + expected: map[string]int{"one": 1, "two": 2, "three": 3}, + }, + { + name: "multiple key-value pairs with some non-numeric values", + json: `{"one": 1, "two": "2", "three": 3, "four": "4"}`, + expected: map[string]int{"one": 1, "three": 3}, + }, + { + name: "non-object node", + json: `["not", "an", "object"]`, + expected: make(map[string]int), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + root, err := Unmarshal([]byte(tc.json)) + if err != nil { + t.Fatalf("Unmarshal failed: %v", err) + } + + result := make(map[string]int) + root.ObjectEach(func(key string, value *Node) { + // extract integer values from the object + if val, err := strconv.Atoi(value.String()); err == nil { + result[key] = val + } + }) + + if len(result) != len(tc.expected) { + t.Errorf("%s: expected %d key-value pairs, got %d", tc.name, len(tc.expected), len(result)) + return + } + + for key, val := range tc.expected { + if result[key] != val { + t.Errorf("%s: expected value for key %s to be %d, got %d", tc.name, key, val, result[key]) + } + } + }) + } +} + +func TestNode_ExampleMust(t *testing.T) { + data := []byte(`{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated" : false, + "IDs": [116, 943, 234, 38793] + } + }`) + + root := Must(Unmarshal(data)) + if root.Size() != 1 { + t.Errorf("root.Size() must be 1. got: %v", root.Size()) + } + + ufmt.Sprintf("Object has %d inheritors inside", root.Size()) + // Output: + // Object has 1 inheritors inside +} + +// Calculate AVG price from different types of objects, JSON from: https://goessner.net/articles/JsonPath/index.html#e3 +func TestExampleUnmarshal(t *testing.T) { + data := []byte(`{ "store": { + "book": [ + { "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95 + }, + { "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99 + }, + { "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99 + }, + { "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99 + } + ], + "bicycle": { "color": "red", + "price": 19.95 + }, + "tools": null + } +}`) + + root, err := Unmarshal(data) + if err != nil { + t.Errorf("error occurred when unmarshal") + } + + store := root.MustKey("store").MustObject() + + var prices float64 + size := 0 + for _, objects := range store { + if objects.IsArray() && objects.Size() > 0 { + size += objects.Size() + for _, object := range objects.MustArray() { + prices += object.MustKey("price").MustNumeric() + } + } else if objects.IsObject() && objects.HasKey("price") { + size++ + prices += objects.MustKey("price").MustNumeric() + } + } + + result := int(prices / float64(size)) + ufmt.Sprintf("AVG price: %d", result) +} + +func TestNode_ExampleMust_panic(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } + }() + data := []byte(`{]`) + root := Must(Unmarshal(data)) + ufmt.Sprintf("Object has %d inheritors inside", root.Size()) +} + +func TestNode_Path(t *testing.T) { + data := []byte(`{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated" : false, + "IDs": [116, 943, 234, 38793] + } + }`) + + root, err := Unmarshal(data) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + return + } + + if root.Path() != "$" { + t.Errorf("Wrong root.Path()") + } + + element := root.MustKey("Image").MustKey("Thumbnail").MustKey("Url") + if element.Path() != "$['Image']['Thumbnail']['Url']" { + t.Errorf("Wrong path found: %s", element.Path()) + } + + if (*Node)(nil).Path() != "" { + t.Errorf("Wrong (nil).Path()") + } +} + +func TestNode_Path2(t *testing.T) { + tests := []struct { + name string + node *Node + want string + }{ + { + name: "Node with key", + node: &Node{ + prev: &Node{}, + key: func() *string { s := "key"; return &s }(), + }, + want: "$['key']", + }, + { + name: "Node with index", + node: &Node{ + prev: &Node{}, + index: func() *int { i := 1; return &i }(), + }, + want: "$[1]", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.node.Path(); got != tt.want { + t.Errorf("Path() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNode_Root(t *testing.T) { + root := &Node{} + child := &Node{prev: root} + grandChild := &Node{prev: child} + + tests := []struct { + name string + node *Node + want *Node + }{ + { + name: "Root node", + node: root, + want: root, + }, + { + name: "Child node", + node: child, + want: root, + }, + { + name: "Grandchild node", + node: grandChild, + want: root, + }, + { + name: "Node is nil", + node: nil, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.node.root(); got != tt.want { + t.Errorf("root() = %v, want %v", got, tt.want) + } + }) + } +} + +func contains(slice []string, item string) bool { + for _, a := range slice { + if a == item { + return true + } + } + + return false +} + +// ignore the sequence of keys by ordering them. +// need to avoid import encoding/json and reflect package. +// because gno does not support them for now. +// TODO: use encoding/json to compare the result after if possible in gno. +func isSameObject(a, b string) bool { + aPairs := strings.Split(strings.Trim(a, "{}"), ",") + bPairs := strings.Split(strings.Trim(b, "{}"), ",") + + aMap := make(map[string]string) + bMap := make(map[string]string) + for _, pair := range aPairs { + kv := strings.Split(pair, ":") + key := strings.Trim(kv[0], `"`) + value := strings.Trim(kv[1], `"`) + aMap[key] = value + } + for _, pair := range bPairs { + kv := strings.Split(pair, ":") + key := strings.Trim(kv[0], `"`) + value := strings.Trim(kv[1], `"`) + bMap[key] = value + } + + aKeys := make([]string, 0, len(aMap)) + bKeys := make([]string, 0, len(bMap)) + for k := range aMap { + aKeys = append(aKeys, k) + } + + for k := range bMap { + bKeys = append(bKeys, k) + } + + sort.Strings(aKeys) + sort.Strings(bKeys) + + if len(aKeys) != len(bKeys) { + return false + } + + for i := range aKeys { + if aKeys[i] != bKeys[i] { + return false + } + + if aMap[aKeys[i]] != bMap[bKeys[i]] { + return false + } + } + + return true +} + +func compareNodes(n1, n2 *Node) bool { + if n1 == nil || n2 == nil { + return n1 == n2 + } + + if n1.key != n2.key { + return false + } + + if !bytes.Equal(n1.data, n2.data) { + return false + } + + if n1.index != n2.index { + return false + } + + if n1.borders != n2.borders { + return false + } + + if n1.modified != n2.modified { + return false + } + + if n1.nodeType != n2.nodeType { + return false + } + + if !compareNodes(n1.prev, n2.prev) { + return false + } + + if len(n1.next) != len(n2.next) { + return false + } + + for k, v := range n1.next { + if !compareNodes(v, n2.next[k]) { + return false + } + } + + return true +} diff --git a/examples/gno.land/p/demo/json/parser.gno b/examples/gno.land/p/demo/json/parser.gno new file mode 100644 index 00000000000..9a2c3a8c817 --- /dev/null +++ b/examples/gno.land/p/demo/json/parser.gno @@ -0,0 +1,185 @@ +package json + +import ( + "bytes" + "errors" + "strconv" + + el "gno.land/p/demo/json/eisel_lemire" +) + +const ( + absMinInt64 = 1 << 63 + maxInt64 = absMinInt64 - 1 + maxUint64 = 1<<64 - 1 +) + +const unescapeStackBufSize = 64 + +// PaseStringLiteral parses a string from the given byte slice. +func ParseStringLiteral(data []byte) (string, error) { + var buf [unescapeStackBufSize]byte + + bf, err := Unescape(data, buf[:]) + if err != nil { + return "", errors.New("invalid string input found while parsing string value") + } + + return string(bf), nil +} + +// ParseBoolLiteral parses a boolean value from the given byte slice. +func ParseBoolLiteral(data []byte) (bool, error) { + switch { + case bytes.Equal(data, trueLiteral): + return true, nil + case bytes.Equal(data, falseLiteral): + return false, nil + default: + return false, errors.New("JSON Error: malformed boolean value found while parsing boolean value") + } +} + +// PaseFloatLiteral parses a float64 from the given byte slice. +// +// It utilizes double-precision (64-bit) floating-point format as defined +// by the IEEE 754 standard, providing a decimal precision of approximately 15 digits. +func ParseFloatLiteral(bytes []byte) (float64, error) { + if len(bytes) == 0 { + return -1, errors.New("JSON Error: empty byte slice found while parsing float value") + } + + neg, bytes := trimNegativeSign(bytes) + + var exponentPart []byte + for i, c := range bytes { + if lower(c) == 'e' { + exponentPart = bytes[i+1:] + bytes = bytes[:i] + break + } + } + + man, exp10, err := extractMantissaAndExp10(bytes) + if err != nil { + return -1, err + } + + if len(exponentPart) > 0 { + exp, err := strconv.Atoi(string(exponentPart)) + if err != nil { + return -1, errors.New("JSON Error: invalid exponent value found while parsing float value") + } + exp10 += exp + } + + // for fast float64 conversion + f, success := el.EiselLemire64(man, exp10, neg) + if !success { + return 0, nil + } + + return f, nil +} + +func ParseIntLiteral(bytes []byte) (int64, error) { + if len(bytes) == 0 { + return 0, errors.New("JSON Error: empty byte slice found while parsing integer value") + } + + neg, bytes := trimNegativeSign(bytes) + + var n uint64 = 0 + for _, c := range bytes { + if notDigit(c) { + return 0, errors.New("JSON Error: non-digit characters found while parsing integer value") + } + + if n > maxUint64/10 { + return 0, errors.New("JSON Error: numeric value exceeds the range limit") + } + + n *= 10 + + n1 := n + uint64(c-'0') + if n1 < n { + return 0, errors.New("JSON Error: numeric value exceeds the range limit") + } + + n = n1 + } + + if n > maxInt64 { + if neg && n == absMinInt64 { + return -absMinInt64, nil + } + + return 0, errors.New("JSON Error: numeric value exceeds the range limit") + } + + if neg { + return -int64(n), nil + } + + return int64(n), nil +} + +// extractMantissaAndExp10 parses a byte slice representing a decimal number and extracts the mantissa and the exponent of its base-10 representation. +// It iterates through the bytes, constructing the mantissa by treating each byte as a digit. +// If a decimal point is encountered, the function keeps track of the position of the decimal point to calculate the exponent. +// The function ensures that: +// - The number contains at most one decimal point. +// - All characters in the byte slice are digits or a single decimal point. +// - The resulting mantissa does not overflow a uint64. +func extractMantissaAndExp10(bytes []byte) (uint64, int, error) { + var ( + man uint64 + exp10 int + decimalFound bool + ) + + for _, c := range bytes { + if c == dot { + if decimalFound { + return 0, 0, errors.New("JSON Error: multiple decimal points found while parsing float value") + } + decimalFound = true + continue + } + + if notDigit(c) { + return 0, 0, errors.New("JSON Error: non-digit characters found while parsing integer value") + } + + digit := uint64(c - '0') + + if man > (maxUint64-digit)/10 { + return 0, 0, errors.New("JSON Error: numeric value exceeds the range limit") + } + + man = man*10 + digit + + if decimalFound { + exp10-- + } + } + + return man, exp10, nil +} + +func trimNegativeSign(bytes []byte) (bool, []byte) { + if bytes[0] == minus { + return true, bytes[1:] + } + + return false, bytes +} + +func notDigit(c byte) bool { + return (c & 0xF0) != 0x30 +} + +// lower converts a byte to lower case if it is an uppercase letter. +func lower(c byte) byte { + return c | 0x20 +} diff --git a/examples/gno.land/p/demo/json/parser_test.gno b/examples/gno.land/p/demo/json/parser_test.gno new file mode 100644 index 00000000000..44a2fee6404 --- /dev/null +++ b/examples/gno.land/p/demo/json/parser_test.gno @@ -0,0 +1,191 @@ +package json + +import ( + "strconv" + "testing" +) + +func TestParseStringLiteral(t *testing.T) { + tests := []struct { + input string + expected string + isError bool + }{ + {`"Hello, World!"`, "\"Hello, World!\"", false}, + {`\uFF11`, "\uFF11", false}, + {`\uFFFF`, "\uFFFF", false}, + {`true`, "true", false}, + {`false`, "false", false}, + {`\uDF00`, "", true}, + } + + for i, tt := range tests { + s, err := ParseStringLiteral([]byte(tt.input)) + + if !tt.isError && err != nil { + t.Errorf("%d. unexpected error: %s", i, err) + } + + if tt.isError && err == nil { + t.Errorf("%d. expected error, but not error", i) + } + + if s != tt.expected { + t.Errorf("%d. expected=%s, but actual=%s", i, tt.expected, s) + } + } +} + +func TestParseBoolLiteral(t *testing.T) { + tests := []struct { + input string + expected bool + isError bool + }{ + {`true`, true, false}, + {`false`, false, false}, + {`TRUE`, false, true}, + {`FALSE`, false, true}, + {`foo`, false, true}, + {`"true"`, false, true}, + {`"false"`, false, true}, + } + + for i, tt := range tests { + b, err := ParseBoolLiteral([]byte(tt.input)) + + if !tt.isError && err != nil { + t.Errorf("%d. unexpected error: %s", i, err) + } + + if tt.isError && err == nil { + t.Errorf("%d. expected error, but not error", i) + } + + if b != tt.expected { + t.Errorf("%d. expected=%t, but actual=%t", i, tt.expected, b) + } + } +} + +func TestParseFloatLiteral(t *testing.T) { + tests := []struct { + input string + expected float64 + }{ + {"123", 123}, + {"-123", -123}, + {"123.456", 123.456}, + {"-123.456", -123.456}, + {"12345678.1234567890", 12345678.1234567890}, + {"-12345678.09123456789", -12345678.09123456789}, + {"0.123", 0.123}, + {"-0.123", -0.123}, + {"", -1}, + {"abc", -1}, + {"123.45.6", -1}, + {"999999999999999999999", -1}, + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + got, _ := ParseFloatLiteral([]byte(tt.input)) + if got != tt.expected { + t.Errorf("ParseFloatLiteral(%s): got %v, want %v", tt.input, got, tt.expected) + } + }) + } +} + +func TestParseFloatWithScientificNotation(t *testing.T) { + tests := []struct { + input string + expected float64 + }{ + {"1e6", 1000000}, + {"1E6", 1000000}, + {"1.23e10", 1.23e10}, + {"1.23E10", 1.23e10}, + {"-1.23e10", -1.23e10}, + {"-1.23E10", -1.23e10}, + {"2.45e-8", 2.45e-8}, + {"2.45E-8", 2.45e-8}, + {"-2.45e-8", -2.45e-8}, + {"-2.45E-8", -2.45e-8}, + {"5e0", 5}, + {"-5e0", -5}, + {"5E+0", 5}, + {"5e+1", 50}, + {"1e-1", 0.1}, + {"1E-1", 0.1}, + {"-1e-1", -0.1}, + {"-1E-1", -0.1}, + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + got, err := ParseFloatLiteral([]byte(tt.input)) + if got != tt.expected { + t.Errorf("ParseFloatLiteral(%s): got %v, want %v", tt.input, got, tt.expected) + } + + if err != nil { + t.Errorf("ParseFloatLiteral(%s): got error %v", tt.input, err) + } + }) + } +} + +func TestParseFloat_May_Interoperability_Problem(t *testing.T) { + tests := []struct { + input string + shouldErr bool + }{ + {"3.141592653589793238462643383279", true}, + {"1E400", false}, // TODO: should error + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + _, err := ParseFloatLiteral([]byte(tt.input)) + if tt.shouldErr && err == nil { + t.Errorf("ParseFloatLiteral(%s): expected error, but not error", tt.input) + } + }) + } +} + +func TestParseIntLiteral(t *testing.T) { + tests := []struct { + input string + expected int64 + }{ + {"0", 0}, + {"1", 1}, + {"-1", -1}, + {"12345", 12345}, + {"-12345", -12345}, + {"9223372036854775807", 9223372036854775807}, + {"-9223372036854775808", -9223372036854775808}, + {"-92233720368547758081", 0}, + {"18446744073709551616", 0}, + {"9223372036854775808", 0}, + {"-9223372036854775809", 0}, + {"", 0}, + {"abc", 0}, + {"12345x", 0}, + {"123e5", 0}, + {"9223372036854775807x", 0}, + {"27670116110564327410", 0}, + {"-27670116110564327410", 0}, + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + got, _ := ParseIntLiteral([]byte(tt.input)) + if got != tt.expected { + t.Errorf("ParseIntLiteral(%s): got %v, want %v", tt.input, got, tt.expected) + } + }) + } +} diff --git a/examples/gno.land/p/demo/json/path.gno b/examples/gno.land/p/demo/json/path.gno new file mode 100644 index 00000000000..31f7e04633f --- /dev/null +++ b/examples/gno.land/p/demo/json/path.gno @@ -0,0 +1,78 @@ +package json + +import ( + "errors" +) + +// ParsePath takes a JSONPath string and returns a slice of strings representing the path segments. +func ParsePath(path string) ([]string, error) { + buf := newBuffer([]byte(path)) + result := make([]string, 0) + + for { + b, err := buf.current() + if err != nil { + break + } + + switch { + case b == dollarSign || b == atSign: + result = append(result, string(b)) + buf.step() + + case b == dot: + buf.step() + + if next, _ := buf.current(); next == dot { + buf.step() + result = append(result, "..") + + extractNextSegment(buf, &result) + } else { + extractNextSegment(buf, &result) + } + + case b == bracketOpen: + start := buf.index + buf.step() + + for { + if buf.index >= buf.length || buf.data[buf.index] == bracketClose { + break + } + + buf.step() + } + + if buf.index >= buf.length { + return nil, errors.New("unexpected end of path") + } + + segment := string(buf.sliceFromIndices(start+1, buf.index)) + result = append(result, segment) + + buf.step() + + default: + buf.step() + } + } + + return result, nil +} + +// extractNextSegment extracts the segment from the current index +// to the next significant character and adds it to the resulting slice. +func extractNextSegment(buf *buffer, result *[]string) { + start := buf.index + buf.skipToNextSignificantToken() + + if buf.index <= start { + return + } + + segment := string(buf.sliceFromIndices(start, buf.index)) + if segment != "" { + *result = append(*result, segment) + } +} diff --git a/examples/gno.land/p/demo/json/path_test.gno b/examples/gno.land/p/demo/json/path_test.gno new file mode 100644 index 00000000000..dd242849f03 --- /dev/null +++ b/examples/gno.land/p/demo/json/path_test.gno @@ -0,0 +1,64 @@ +package json + +import ( + "testing" +) + +func TestParseJSONPath(t *testing.T) { + tests := []struct { + name string + path string + expected []string + }{ + {name: "Empty string path", path: "", expected: []string{}}, + {name: "Root only path", path: "$", expected: []string{"$"}}, + {name: "Root with dot path", path: "$.", expected: []string{"$"}}, + {name: "All objects in path", path: "$..", expected: []string{"$", ".."}}, + {name: "Only children in path", path: "$.*", expected: []string{"$", "*"}}, + {name: "All objects' children in path", path: "$..*", expected: []string{"$", "..", "*"}}, + {name: "Simple dot notation path", path: "$.root.element", expected: []string{"$", "root", "element"}}, + {name: "Complex dot notation path with wildcard", path: "$.root.*.element", expected: []string{"$", "root", "*", "element"}}, + {name: "Path with array wildcard", path: "$.phoneNumbers[*].type", expected: []string{"$", "phoneNumbers", "*", "type"}}, + {name: "Path with filter expression", path: "$.store.book[?(@.price < 10)].title", expected: []string{"$", "store", "book", "?(@.price < 10)", "title"}}, + {name: "Path with formula", path: "$..phoneNumbers..('ty' + 'pe')", expected: []string{"$", "..", "phoneNumbers", "..", "('ty' + 'pe')"}}, + {name: "Simple bracket notation path", path: "$['root']['element']", expected: []string{"$", "'root'", "'element'"}}, + {name: "Complex bracket notation path with wildcard", path: "$['root'][*]['element']", expected: []string{"$", "'root'", "*", "'element'"}}, + {name: "Bracket notation path with integer index", path: "$['store']['book'][0]['title']", expected: []string{"$", "'store'", "'book'", "0", "'title'"}}, + {name: "Complex path with wildcard in bracket notation", path: "$['root'].*['element']", expected: []string{"$", "'root'", "*", "'element'"}}, + {name: "Mixed notation path with dot after bracket", path: "$.['root'].*.['element']", expected: []string{"$", "'root'", "*", "'element'"}}, + {name: "Mixed notation path with dot before bracket", path: "$['root'].*.['element']", expected: []string{"$", "'root'", "*", "'element'"}}, + {name: "Single character path with root", path: "$.a", expected: []string{"$", "a"}}, + {name: "Multiple characters path with root", path: "$.abc", expected: []string{"$", "abc"}}, + {name: "Multiple segments path with root", path: "$.a.b.c", expected: []string{"$", "a", "b", "c"}}, + {name: "Multiple segments path with wildcard and root", path: "$.a.*.c", expected: []string{"$", "a", "*", "c"}}, + {name: "Multiple segments path with filter and root", path: "$.a[?(@.b == 'c')].d", expected: []string{"$", "a", "?(@.b == 'c')", "d"}}, + {name: "Complex path with multiple filters", path: "$.a[?(@.b == 'c')].d[?(@.e == 'f')].g", expected: []string{"$", "a", "?(@.b == 'c')", "d", "?(@.e == 'f')", "g"}}, + {name: "Complex path with multiple filters and wildcards", path: "$.a[?(@.b == 'c')].*.d[?(@.e == 'f')].g", expected: []string{"$", "a", "?(@.b == 'c')", "*", "d", "?(@.e == 'f')", "g"}}, + {name: "Path with array index and root", path: "$.a[0].b", expected: []string{"$", "a", "0", "b"}}, + {name: "Path with multiple array indices and root", path: "$.a[0].b[1].c", expected: []string{"$", "a", "0", "b", "1", "c"}}, + {name: "Path with array index, wildcard and root", path: "$.a[0].*.c", expected: []string{"$", "a", "0", "*", "c"}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + reult, _ := ParsePath(tt.path) + if !isEqualSlice(reult, tt.expected) { + t.Errorf("ParsePath(%s) expected: %v, got: %v", tt.path, tt.expected, reult) + } + }) + } +} + +func isEqualSlice(a, b []string) bool { + if len(a) != len(b) { + return false + } + + for i, v := range a { + if v != b[i] { + return false + } + } + + return true +} diff --git a/examples/gno.land/p/demo/json/ryu/License b/examples/gno.land/p/demo/json/ryu/License new file mode 100644 index 00000000000..55beeadce54 --- /dev/null +++ b/examples/gno.land/p/demo/json/ryu/License @@ -0,0 +1,21 @@ +# Apache License + +Copyright 2018 Ulf Adams +Modifications copyright 2019 Caleb Spare + +The contents of this file may be used under the terms of the Apache License, +Version 2.0. + + (See accompanying file LICENSE or copy at + ) + +Unless required by applicable law or agreed to in writing, this software +is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. + +The code in this file is part of a Go translation of the C code originally written by +Ulf Adams, which can be found at . The original source +code is licensed under the Apache License 2.0. This code is a derivative work thereof, +adapted and modified to meet the specifications of the Gno language project. + +Please note that the modifications are also under the Apache License 2.0 unless otherwise specified. diff --git a/examples/gno.land/p/demo/json/ryu/floatconv.gno b/examples/gno.land/p/demo/json/ryu/floatconv.gno new file mode 100644 index 00000000000..617141d2734 --- /dev/null +++ b/examples/gno.land/p/demo/json/ryu/floatconv.gno @@ -0,0 +1,143 @@ +// Copyright 2018 Ulf Adams +// Modifications copyright 2019 Caleb Spare +// +// The contents of this file may be used under the terms of the Apache License, +// Version 2.0. +// +// (See accompanying file LICENSE or copy at +// http://www.apache.org/licenses/LICENSE-2.0) +// +// Unless required by applicable law or agreed to in writing, this software +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. +// +// The code in this file is part of a Go translation of the C code originally written by +// Ulf Adams, which can be found at https://github.com/ulfjack/ryu. The original source +// code is licensed under the Apache License 2.0. This code is a derivative work thereof, +// adapted and modified to meet the specifications of the Gno language project. +// +// original Go implementation can be found at https://github.com/cespare/ryu. +// +// Please note that the modifications are also under the Apache License 2.0 unless +// otherwise specified. + +// Package ryu implements the Ryu algorithm for quickly converting floating +// point numbers into strings. +package ryu + +import ( + "math" +) + +const ( + mantBits32 = 23 + expBits32 = 8 + bias32 = 127 + + mantBits64 = 52 + expBits64 = 11 + bias64 = 1023 +) + +// FormatFloat64 converts a 64-bit floating point number f to a string. +// It behaves like strconv.FormatFloat(f, 'e', -1, 64). +func FormatFloat64(f float64) string { + b := make([]byte, 0, 24) + b = AppendFloat64(b, f) + return string(b) +} + +// AppendFloat64 appends the string form of the 64-bit floating point number f, +// as generated by FormatFloat64, to b and returns the extended buffer. +func AppendFloat64(b []byte, f float64) []byte { + // Step 1: Decode the floating-point number. + // Unify normalized and subnormal cases. + u := math.Float64bits(f) + neg := u>>(mantBits64+expBits64) != 0 + mant := u & (uint64(1)<> mantBits64) & (uint64(1)<= 0, "e >= 0") + assert(e <= 1650, "e <= 1650") + return (uint32(e) * 78913) >> 18 +} + +// log10Pow5 returns floor(log_10(5^e)). +func log10Pow5(e int32) uint32 { + // The first value this approximation fails for is 5^2621 + // which is just greater than 10^1832. + assert(e >= 0, "e >= 0") + assert(e <= 2620, "e <= 2620") + return (uint32(e) * 732923) >> 20 +} + +// pow5Bits returns ceil(log_2(5^e)), or else 1 if e==0. +func pow5Bits(e int32) int32 { + // This approximation works up to the point that the multiplication + // overflows at e = 3529. If the multiplication were done in 64 bits, + // it would fail at 5^4004 which is just greater than 2^9297. + assert(e >= 0, "e >= 0") + assert(e <= 3528, "e <= 3528") + return int32((uint32(e)*1217359)>>19 + 1) +} diff --git a/examples/gno.land/p/demo/json/ryu/floatconv_test.gno b/examples/gno.land/p/demo/json/ryu/floatconv_test.gno new file mode 100644 index 00000000000..7f01d4034f7 --- /dev/null +++ b/examples/gno.land/p/demo/json/ryu/floatconv_test.gno @@ -0,0 +1,33 @@ +package ryu + +import ( + "math" + "testing" +) + +func TestFormatFloat64(t *testing.T) { + tests := []struct { + name string + value float64 + expected string + }{ + {"positive infinity", math.Inf(1), "+Inf"}, + {"negative infinity", math.Inf(-1), "-Inf"}, + {"NaN", math.NaN(), "NaN"}, + {"zero", 0.0, "0e+00"}, + {"negative zero", -0.0, "0e+00"}, + {"positive number", 3.14159, "3.14159e+00"}, + {"negative number", -2.71828, "-2.71828e+00"}, + {"very small number", 1.23e-20, "1.23e-20"}, + {"very large number", 1.23e+20, "1.23e+20"}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := FormatFloat64(test.value) + if result != test.expected { + t.Errorf("FormatFloat64(%v) = %q, expected %q", test.value, result, test.expected) + } + }) + } +} diff --git a/examples/gno.land/p/demo/json/ryu/gno.mod b/examples/gno.land/p/demo/json/ryu/gno.mod new file mode 100644 index 00000000000..86a1988b052 --- /dev/null +++ b/examples/gno.land/p/demo/json/ryu/gno.mod @@ -0,0 +1 @@ +module gno.land/p/demo/json/ryu diff --git a/examples/gno.land/p/demo/json/ryu/ryu64.gno b/examples/gno.land/p/demo/json/ryu/ryu64.gno new file mode 100644 index 00000000000..249e3d0f526 --- /dev/null +++ b/examples/gno.land/p/demo/json/ryu/ryu64.gno @@ -0,0 +1,344 @@ +package ryu + +import ( + "math/bits" +) + +type uint128 struct { + lo uint64 + hi uint64 +} + +// dec64 is a floating decimal type representing m * 10^e. +type dec64 struct { + m uint64 + e int32 +} + +func (d dec64) append(b []byte, neg bool) []byte { + // Step 5: Print the decimal representation. + if neg { + b = append(b, '-') + } + + out := d.m + outLen := decimalLen64(out) + bufLen := outLen + if bufLen > 1 { + bufLen++ // extra space for '.' + } + + // Print the decimal digits. + n := len(b) + if cap(b)-len(b) >= bufLen { + // Avoid function call in the common case. + b = b[:len(b)+bufLen] + } else { + b = append(b, make([]byte, bufLen)...) + } + + // Avoid expensive 64-bit divisions. + // We have at most 17 digits, and uint32 can store 9 digits. + // If the output doesn't fit into a uint32, cut off 8 digits + // so the rest will fit into a uint32. + var i int + if out>>32 > 0 { + var out32 uint32 + out, out32 = out/1e8, uint32(out%1e8) + for ; i < 8; i++ { + b[n+outLen-i] = '0' + byte(out32%10) + out32 /= 10 + } + } + out32 := uint32(out) + for ; i < outLen-1; i++ { + b[n+outLen-i] = '0' + byte(out32%10) + out32 /= 10 + } + b[n] = '0' + byte(out32%10) + + // Print the '.' if needed. + if outLen > 1 { + b[n+1] = '.' + } + + // Print the exponent. + b = append(b, 'e') + exp := d.e + int32(outLen) - 1 + if exp < 0 { + b = append(b, '-') + exp = -exp + } else { + // Unconditionally print a + here to match strconv's formatting. + b = append(b, '+') + } + // Always print at least two digits to match strconv's formatting. + d2 := exp % 10 + exp /= 10 + d1 := exp % 10 + d0 := exp / 10 + if d0 > 0 { + b = append(b, '0'+byte(d0)) + } + b = append(b, '0'+byte(d1), '0'+byte(d2)) + + return b +} + +func float64ToDecimalExactInt(mant, exp uint64) (d dec64, ok bool) { + e := exp - bias64 + if e > mantBits64 { + return d, false + } + shift := mantBits64 - e + mant |= 1 << mantBits64 // implicit 1 + d.m = mant >> shift + if d.m<= 0 { + // This expression is slightly faster than max(0, log10Pow2(e2) - 1). + q := log10Pow2(e2) - boolToUint32(e2 > 3) + e10 = int32(q) + k := pow5InvNumBits64 + pow5Bits(int32(q)) - 1 + i := -e2 + int32(q) + k + mul := pow5InvSplit64[q] + vr = mulShift64(4*m2, mul, i) + vp = mulShift64(4*m2+2, mul, i) + vm = mulShift64(4*m2-1-mmShift, mul, i) + if q <= 21 { + // This should use q <= 22, but I think 21 is also safe. + // Smaller values may still be safe, but it's more + // difficult to reason about them. Only one of mp, mv, + // and mm can be a multiple of 5, if any. + if mv%5 == 0 { + vrIsTrailingZeros = multipleOfPowerOfFive64(mv, q) + } else if acceptBounds { + // Same as min(e2 + (^mm & 1), pow5Factor64(mm)) >= q + // <=> e2 + (^mm & 1) >= q && pow5Factor64(mm) >= q + // <=> true && pow5Factor64(mm) >= q, since e2 >= q. + vmIsTrailingZeros = multipleOfPowerOfFive64(mv-1-mmShift, q) + } else if multipleOfPowerOfFive64(mv+2, q) { + vp-- + } + } + } else { + // This expression is slightly faster than max(0, log10Pow5(-e2) - 1). + q := log10Pow5(-e2) - boolToUint32(-e2 > 1) + e10 = int32(q) + e2 + i := -e2 - int32(q) + k := pow5Bits(i) - pow5NumBits64 + j := int32(q) - k + mul := pow5Split64[i] + vr = mulShift64(4*m2, mul, j) + vp = mulShift64(4*m2+2, mul, j) + vm = mulShift64(4*m2-1-mmShift, mul, j) + if q <= 1 { + // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits. + // mv = 4 * m2, so it always has at least two trailing 0 bits. + vrIsTrailingZeros = true + if acceptBounds { + // mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1. + vmIsTrailingZeros = mmShift == 1 + } else { + // mp = mv + 2, so it always has at least one trailing 0 bit. + vp-- + } + } else if q < 63 { // TODO(ulfjack/cespare): Use a tighter bound here. + // We need to compute min(ntz(mv), pow5Factor64(mv) - e2) >= q - 1 + // <=> ntz(mv) >= q - 1 && pow5Factor64(mv) - e2 >= q - 1 + // <=> ntz(mv) >= q - 1 (e2 is negative and -e2 >= q) + // <=> (mv & ((1 << (q - 1)) - 1)) == 0 + // We also need to make sure that the left shift does not overflow. + vrIsTrailingZeros = multipleOfPowerOfTwo64(mv, q-1) + } + } + + // Step 4: Find the shortest decimal representation + // in the interval of valid representations. + var removed int32 + var lastRemovedDigit uint8 + var out uint64 + // On average, we remove ~2 digits. + if vmIsTrailingZeros || vrIsTrailingZeros { + // General case, which happens rarely (~0.7%). + for { + vpDiv10 := vp / 10 + vmDiv10 := vm / 10 + if vpDiv10 <= vmDiv10 { + break + } + vmMod10 := vm % 10 + vrDiv10 := vr / 10 + vrMod10 := vr % 10 + vmIsTrailingZeros = vmIsTrailingZeros && vmMod10 == 0 + vrIsTrailingZeros = vrIsTrailingZeros && lastRemovedDigit == 0 + lastRemovedDigit = uint8(vrMod10) + vr = vrDiv10 + vp = vpDiv10 + vm = vmDiv10 + removed++ + } + if vmIsTrailingZeros { + for { + vmDiv10 := vm / 10 + vmMod10 := vm % 10 + if vmMod10 != 0 { + break + } + vpDiv10 := vp / 10 + vrDiv10 := vr / 10 + vrMod10 := vr % 10 + vrIsTrailingZeros = vrIsTrailingZeros && lastRemovedDigit == 0 + lastRemovedDigit = uint8(vrMod10) + vr = vrDiv10 + vp = vpDiv10 + vm = vmDiv10 + removed++ + } + } + if vrIsTrailingZeros && lastRemovedDigit == 5 && vr%2 == 0 { + // Round even if the exact number is .....50..0. + lastRemovedDigit = 4 + } + out = vr + // We need to take vr + 1 if vr is outside bounds + // or we need to round up. + if (vr == vm && (!acceptBounds || !vmIsTrailingZeros)) || lastRemovedDigit >= 5 { + out++ + } + } else { + // Specialized for the common case (~99.3%). + // Percentages below are relative to this. + roundUp := false + for vp/100 > vm/100 { + // Optimization: remove two digits at a time (~86.2%). + roundUp = vr%100 >= 50 + vr /= 100 + vp /= 100 + vm /= 100 + removed += 2 + } + // Loop iterations below (approximately), without optimization above: + // 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02% + // Loop iterations below (approximately), with optimization above: + // 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02% + for vp/10 > vm/10 { + roundUp = vr%10 >= 5 + vr /= 10 + vp /= 10 + vm /= 10 + removed++ + } + // We need to take vr + 1 if vr is outside bounds + // or we need to round up. + out = vr + boolToUint64(vr == vm || roundUp) + } + + return dec64{m: out, e: e10 + removed} +} + +var powersOf10 = [...]uint64{ + 1e0, + 1e1, + 1e2, + 1e3, + 1e4, + 1e5, + 1e6, + 1e7, + 1e8, + 1e9, + 1e10, + 1e11, + 1e12, + 1e13, + 1e14, + 1e15, + 1e16, + 1e17, + // We only need to find the length of at most 17 digit numbers. +} + +func decimalLen64(u uint64) int { + // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + log2 := 64 - bits.LeadingZeros64(u) - 1 + t := (log2 + 1) * 1233 >> 12 + return t - boolToInt(u < powersOf10[t]) + 1 +} + +func mulShift64(m uint64, mul uint128, shift int32) uint64 { + hihi, hilo := bits.Mul64(m, mul.hi) + lohi, _ := bits.Mul64(m, mul.lo) + sum := uint128{hi: hihi, lo: lohi + hilo} + if sum.lo < lohi { + sum.hi++ // overflow + } + return shiftRight128(sum, shift-64) +} + +func shiftRight128(v uint128, shift int32) uint64 { + // The shift value is always modulo 64. + // In the current implementation of the 64-bit version + // of Ryu, the shift value is always < 64. + // (It is in the range [2, 59].) + // Check this here in case a future change requires larger shift + // values. In this case this function needs to be adjusted. + assert(shift < 64, "shift < 64") + return (v.hi << uint64(64-shift)) | (v.lo >> uint(shift)) +} + +func pow5Factor64(v uint64) uint32 { + for n := uint32(0); ; n++ { + q, r := v/5, v%5 + if r != 0 { + return n + } + v = q + } +} + +func multipleOfPowerOfFive64(v uint64, p uint32) bool { + return pow5Factor64(v) >= p +} + +func multipleOfPowerOfTwo64(v uint64, p uint32) bool { + return uint32(bits.TrailingZeros64(v)) >= p +} diff --git a/examples/gno.land/p/demo/json/ryu/table.gno b/examples/gno.land/p/demo/json/ryu/table.gno new file mode 100644 index 00000000000..fe33ad90a57 --- /dev/null +++ b/examples/gno.land/p/demo/json/ryu/table.gno @@ -0,0 +1,678 @@ +// Code generated by running "go generate". DO NOT EDIT. + +// Copyright 2018 Ulf Adams +// Modifications copyright 2019 Caleb Spare +// +// The contents of this file may be used under the terms of the Apache License, +// Version 2.0. +// +// (See accompanying file LICENSE or copy at +// http://www.apache.org/licenses/LICENSE-2.0) +// +// Unless required by applicable law or agreed to in writing, this software +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. +// +// The code in this file is part of a Go translation of the C code written by +// Ulf Adams which may be found at https://github.com/ulfjack/ryu. That source +// code is licensed under Apache 2.0 and this code is derivative work thereof. + +package ryu + +const pow5NumBits32 = 61 + +var pow5Split32 = [...]uint64{ + 1152921504606846976, 1441151880758558720, 1801439850948198400, 2251799813685248000, + 1407374883553280000, 1759218604441600000, 2199023255552000000, 1374389534720000000, + 1717986918400000000, 2147483648000000000, 1342177280000000000, 1677721600000000000, + 2097152000000000000, 1310720000000000000, 1638400000000000000, 2048000000000000000, + 1280000000000000000, 1600000000000000000, 2000000000000000000, 1250000000000000000, + 1562500000000000000, 1953125000000000000, 1220703125000000000, 1525878906250000000, + 1907348632812500000, 1192092895507812500, 1490116119384765625, 1862645149230957031, + 1164153218269348144, 1455191522836685180, 1818989403545856475, 2273736754432320594, + 1421085471520200371, 1776356839400250464, 2220446049250313080, 1387778780781445675, + 1734723475976807094, 2168404344971008868, 1355252715606880542, 1694065894508600678, + 2117582368135750847, 1323488980084844279, 1654361225106055349, 2067951531382569187, + 1292469707114105741, 1615587133892632177, 2019483917365790221, +} + +const pow5InvNumBits32 = 59 + +var pow5InvSplit32 = [...]uint64{ + 576460752303423489, 461168601842738791, 368934881474191033, 295147905179352826, + 472236648286964522, 377789318629571618, 302231454903657294, 483570327845851670, + 386856262276681336, 309485009821345069, 495176015714152110, 396140812571321688, + 316912650057057351, 507060240091291761, 405648192073033409, 324518553658426727, + 519229685853482763, 415383748682786211, 332306998946228969, 531691198313966350, + 425352958651173080, 340282366920938464, 544451787073501542, 435561429658801234, + 348449143727040987, 557518629963265579, 446014903970612463, 356811923176489971, + 570899077082383953, 456719261665907162, 365375409332725730, +} + +const pow5NumBits64 = 121 + +var pow5Split64 = [...]uint128{ + {0, 72057594037927936}, + {0, 90071992547409920}, + {0, 112589990684262400}, + {0, 140737488355328000}, + {0, 87960930222080000}, + {0, 109951162777600000}, + {0, 137438953472000000}, + {0, 85899345920000000}, + {0, 107374182400000000}, + {0, 134217728000000000}, + {0, 83886080000000000}, + {0, 104857600000000000}, + {0, 131072000000000000}, + {0, 81920000000000000}, + {0, 102400000000000000}, + {0, 128000000000000000}, + {0, 80000000000000000}, + {0, 100000000000000000}, + {0, 125000000000000000}, + {0, 78125000000000000}, + {0, 97656250000000000}, + {0, 122070312500000000}, + {0, 76293945312500000}, + {0, 95367431640625000}, + {0, 119209289550781250}, + {4611686018427387904, 74505805969238281}, + {10376293541461622784, 93132257461547851}, + {8358680908399640576, 116415321826934814}, + {612489549322387456, 72759576141834259}, + {14600669991935148032, 90949470177292823}, + {13639151471491547136, 113686837721616029}, + {3213881284082270208, 142108547152020037}, + {4314518811765112832, 88817841970012523}, + {781462496279003136, 111022302462515654}, + {10200200157203529728, 138777878078144567}, + {13292654125893287936, 86736173798840354}, + {7392445620511834112, 108420217248550443}, + {4628871007212404736, 135525271560688054}, + {16728102434789916672, 84703294725430033}, + {7075069988205232128, 105879118406787542}, + {18067209522111315968, 132348898008484427}, + {8986162942105878528, 82718061255302767}, + {6621017659204960256, 103397576569128459}, + {3664586055578812416, 129246970711410574}, + {16125424340018921472, 80779356694631608}, + {1710036351314100224, 100974195868289511}, + {15972603494424788992, 126217744835361888}, + {9982877184015493120, 78886090522101180}, + {12478596480019366400, 98607613152626475}, + {10986559581596820096, 123259516440783094}, + {2254913720070624656, 77037197775489434}, + {12042014186943056628, 96296497219361792}, + {15052517733678820785, 120370621524202240}, + {9407823583549262990, 75231638452626400}, + {11759779479436578738, 94039548065783000}, + {14699724349295723422, 117549435082228750}, + {4575641699882439235, 73468396926392969}, + {10331238143280436948, 91835496157991211}, + {8302361660673158281, 114794370197489014}, + {1154580038986672043, 143492962746861268}, + {9944984561221445835, 89683101716788292}, + {12431230701526807293, 112103877145985365}, + {1703980321626345405, 140129846432481707}, + {17205888765512323542, 87581154020301066}, + {12283988920035628619, 109476442525376333}, + {1519928094762372062, 136845553156720417}, + {12479170105294952299, 85528470722950260}, + {15598962631618690374, 106910588403687825}, + {5663645234241199255, 133638235504609782}, + {17374836326682913246, 83523897190381113}, + {7883487353071477846, 104404871487976392}, + {9854359191339347308, 130506089359970490}, + {10770660513014479971, 81566305849981556}, + {13463325641268099964, 101957882312476945}, + {2994098996302961243, 127447352890596182}, + {15706369927971514489, 79654595556622613}, + {5797904354682229399, 99568244445778267}, + {2635694424925398845, 124460305557222834}, + {6258995034005762182, 77787690973264271}, + {3212057774079814824, 97234613716580339}, + {17850130272881932242, 121543267145725423}, + {18073860448192289507, 75964541966078389}, + {8757267504958198172, 94955677457597987}, + {6334898362770359811, 118694596821997484}, + {13182683513586250689, 74184123013748427}, + {11866668373555425458, 92730153767185534}, + {5609963430089506015, 115912692208981918}, + {17341285199088104971, 72445432630613698}, + {12453234462005355406, 90556790788267123}, + {10954857059079306353, 113195988485333904}, + {13693571323849132942, 141494985606667380}, + {17781854114260483896, 88434366004167112}, + {3780573569116053255, 110542957505208891}, + {114030942967678664, 138178696881511114}, + {4682955357782187069, 86361685550944446}, + {15077066234082509644, 107952106938680557}, + {5011274737320973344, 134940133673350697}, + {14661261756894078100, 84337583545844185}, + {4491519140835433913, 105421979432305232}, + {5614398926044292391, 131777474290381540}, + {12732371365632458552, 82360921431488462}, + {6692092170185797382, 102951151789360578}, + {17588487249587022536, 128688939736700722}, + {15604490549419276989, 80430587335437951}, + {14893927168346708332, 100538234169297439}, + {14005722942005997511, 125672792711621799}, + {15671105866394830300, 78545495444763624}, + {1142138259283986260, 98181869305954531}, + {15262730879387146537, 122727336632443163}, + {7233363790403272633, 76704585395276977}, + {13653390756431478696, 95880731744096221}, + {3231680390257184658, 119850914680120277}, + {4325643253124434363, 74906821675075173}, + {10018740084832930858, 93633527093843966}, + {3300053069186387764, 117041908867304958}, + {15897591223523656064, 73151193042065598}, + {10648616992549794273, 91438991302581998}, + {4087399203832467033, 114298739128227498}, + {14332621041645359599, 142873423910284372}, + {18181260187883125557, 89295889943927732}, + {4279831161144355331, 111619862429909666}, + {14573160988285219972, 139524828037387082}, + {13719911636105650386, 87203017523366926}, + {7926517508277287175, 109003771904208658}, + {684774848491833161, 136254714880260823}, + {7345513307948477581, 85159196800163014}, + {18405263671790372785, 106448996000203767}, + {18394893571310578077, 133061245000254709}, + {13802651491282805250, 83163278125159193}, + {3418256308821342851, 103954097656448992}, + {4272820386026678563, 129942622070561240}, + {2670512741266674102, 81214138794100775}, + {17173198981865506339, 101517673492625968}, + {3019754653622331308, 126897091865782461}, + {4193189667727651020, 79310682416114038}, + {14464859121514339583, 99138353020142547}, + {13469387883465536574, 123922941275178184}, + {8418367427165960359, 77451838296986365}, + {15134645302384838353, 96814797871232956}, + {471562554271496325, 121018497339041196}, + {9518098633274461011, 75636560836900747}, + {7285937273165688360, 94545701046125934}, + {18330793628311886258, 118182126307657417}, + {4539216990053847055, 73863828942285886}, + {14897393274422084627, 92329786177857357}, + {4786683537745442072, 115412232722321697}, + {14520892257159371055, 72132645451451060}, + {18151115321449213818, 90165806814313825}, + {8853836096529353561, 112707258517892282}, + {1843923083806916143, 140884073147365353}, + {12681666973447792349, 88052545717103345}, + {2017025661527576725, 110065682146379182}, + {11744654113764246714, 137582102682973977}, + {422879793461572340, 85988814176858736}, + {528599741826965425, 107486017721073420}, + {660749677283706782, 134357522151341775}, + {7330497575943398595, 83973451344588609}, + {13774807988356636147, 104966814180735761}, + {3383451930163631472, 131208517725919702}, + {15949715511634433382, 82005323578699813}, + {6102086334260878016, 102506654473374767}, + {3015921899398709616, 128133318091718459}, + {18025852251620051174, 80083323807324036}, + {4085571240815512351, 100104154759155046}, + {14330336087874166247, 125130193448943807}, + {15873989082562435760, 78206370905589879}, + {15230800334775656796, 97757963631987349}, + {5203442363187407284, 122197454539984187}, + {946308467778435600, 76373409087490117}, + {5794571603150432404, 95466761359362646}, + {16466586540792816313, 119333451699203307}, + {7985773578781816244, 74583407312002067}, + {5370530955049882401, 93229259140002584}, + {6713163693812353001, 116536573925003230}, + {18030785363914884337, 72835358703127018}, + {13315109668038829614, 91044198378908773}, + {2808829029766373305, 113805247973635967}, + {17346094342490130344, 142256559967044958}, + {6229622945628943561, 88910349979403099}, + {3175342663608791547, 111137937474253874}, + {13192550366365765242, 138922421842817342}, + {3633657960551215372, 86826513651760839}, + {18377130505971182927, 108533142064701048}, + {4524669058754427043, 135666427580876311}, + {9745447189362598758, 84791517238047694}, + {2958436949848472639, 105989396547559618}, + {12921418224165366607, 132486745684449522}, + {12687572408530742033, 82804216052780951}, + {11247779492236039638, 103505270065976189}, + {224666310012885835, 129381587582470237}, + {2446259452971747599, 80863492239043898}, + {12281196353069460307, 101079365298804872}, + {15351495441336825384, 126349206623506090}, + {14206370669262903769, 78968254139691306}, + {8534591299723853903, 98710317674614133}, + {15279925143082205283, 123387897093267666}, + {14161639232853766206, 77117435683292291}, + {13090363022639819853, 96396794604115364}, + {16362953778299774816, 120495993255144205}, + {12532689120651053212, 75309995784465128}, + {15665861400813816515, 94137494730581410}, + {10358954714162494836, 117671868413226763}, + {4168503687137865320, 73544917758266727}, + {598943590494943747, 91931147197833409}, + {5360365506546067587, 114913933997291761}, + {11312142901609972388, 143642417496614701}, + {9375932322719926695, 89776510935384188}, + {11719915403399908368, 112220638669230235}, + {10038208235822497557, 140275798336537794}, + {10885566165816448877, 87672373960336121}, + {18218643725697949000, 109590467450420151}, + {18161618638695048346, 136988084313025189}, + {13656854658398099168, 85617552695640743}, + {12459382304570236056, 107021940869550929}, + {1739169825430631358, 133777426086938662}, + {14922039196176308311, 83610891304336663}, + {14040862976792997485, 104513614130420829}, + {3716020665709083144, 130642017663026037}, + {4628355925281870917, 81651261039391273}, + {10397130925029726550, 102064076299239091}, + {8384727637859770284, 127580095374048864}, + {5240454773662356427, 79737559608780540}, + {6550568467077945534, 99671949510975675}, + {3576524565420044014, 124589936888719594}, + {6847013871814915412, 77868710555449746}, + {17782139376623420074, 97335888194312182}, + {13004302183924499284, 121669860242890228}, + {17351060901807587860, 76043662651806392}, + {3242082053549933210, 95054578314757991}, + {17887660622219580224, 118818222893447488}, + {11179787888887237640, 74261389308404680}, + {13974734861109047050, 92826736635505850}, + {8245046539531533005, 116033420794382313}, + {16682369133275677888, 72520887996488945}, + {7017903361312433648, 90651109995611182}, + {17995751238495317868, 113313887494513977}, + {8659630992836983623, 141642359368142472}, + {5412269370523114764, 88526474605089045}, + {11377022731581281359, 110658093256361306}, + {4997906377621825891, 138322616570451633}, + {14652906532082110942, 86451635356532270}, + {9092761128247862869, 108064544195665338}, + {2142579373455052779, 135080680244581673}, + {12868327154477877747, 84425425152863545}, + {2250350887815183471, 105531781441079432}, + {2812938609768979339, 131914726801349290}, + {6369772649532999991, 82446704250843306}, + {17185587848771025797, 103058380313554132}, + {3035240737254230630, 128822975391942666}, + {6508711479211282048, 80514359619964166}, + {17359261385868878368, 100642949524955207}, + {17087390713908710056, 125803686906194009}, + {3762090168551861929, 78627304316371256}, + {4702612710689827411, 98284130395464070}, + {15101637925217060072, 122855162994330087}, + {16356052730901744401, 76784476871456304}, + {1998321839917628885, 95980596089320381}, + {7109588318324424010, 119975745111650476}, + {13666864735807540814, 74984840694781547}, + {12471894901332038114, 93731050868476934}, + {6366496589810271835, 117163813585596168}, + {3979060368631419896, 73227383490997605}, + {9585511479216662775, 91534229363747006}, + {2758517312166052660, 114417786704683758}, + {12671518677062341634, 143022233380854697}, + {1002170145522881665, 89388895863034186}, + {10476084718758377889, 111736119828792732}, + {13095105898447972362, 139670149785990915}, + {5878598177316288774, 87293843616244322}, + {16571619758500136775, 109117304520305402}, + {11491152661270395161, 136396630650381753}, + {264441385652915120, 85247894156488596}, + {330551732066143900, 106559867695610745}, + {5024875683510067779, 133199834619513431}, + {10058076329834874218, 83249896637195894}, + {3349223375438816964, 104062370796494868}, + {4186529219298521205, 130077963495618585}, + {14145795808130045513, 81298727184761615}, + {13070558741735168987, 101623408980952019}, + {11726512408741573330, 127029261226190024}, + {7329070255463483331, 79393288266368765}, + {13773023837756742068, 99241610332960956}, + {17216279797195927585, 124052012916201195}, + {8454331864033760789, 77532508072625747}, + {5956228811614813082, 96915635090782184}, + {7445286014518516353, 121144543863477730}, + {9264989777501460624, 75715339914673581}, + {16192923240304213684, 94644174893341976}, + {1794409976670715490, 118305218616677471}, + {8039035263060279037, 73940761635423419}, + {5437108060397960892, 92425952044279274}, + {16019757112352226923, 115532440055349092}, + {788976158365366019, 72207775034593183}, + {14821278253238871236, 90259718793241478}, + {9303225779693813237, 112824648491551848}, + {11629032224617266546, 141030810614439810}, + {11879831158813179495, 88144256634024881}, + {1014730893234310657, 110180320792531102}, + {10491785653397664129, 137725400990663877}, + {8863209042587234033, 86078375619164923}, + {6467325284806654637, 107597969523956154}, + {17307528642863094104, 134497461904945192}, + {10817205401789433815, 84060913690590745}, + {18133192770664180173, 105076142113238431}, + {18054804944902837312, 131345177641548039}, + {18201782118205355176, 82090736025967524}, + {4305483574047142354, 102613420032459406}, + {14605226504413703751, 128266775040574257}, + {2210737537617482988, 80166734400358911}, + {16598479977304017447, 100208418000448638}, + {11524727934775246001, 125260522500560798}, + {2591268940807140847, 78287826562850499}, + {17074144231291089770, 97859783203563123}, + {16730994270686474309, 122324729004453904}, + {10456871419179046443, 76452955627783690}, + {3847717237119032246, 95566194534729613}, + {9421332564826178211, 119457743168412016}, + {5888332853016361382, 74661089480257510}, + {16583788103125227536, 93326361850321887}, + {16118049110479146516, 116657952312902359}, + {16991309721690548428, 72911220195563974}, + {12015765115258409727, 91139025244454968}, + {15019706394073012159, 113923781555568710}, + {9551260955736489391, 142404726944460888}, + {5969538097335305869, 89002954340288055}, + {2850236603241744433, 111253692925360069}, +} + +const pow5InvNumBits64 = 122 + +var pow5InvSplit64 = [...]uint128{ + {1, 288230376151711744}, + {3689348814741910324, 230584300921369395}, + {2951479051793528259, 184467440737095516}, + {17118578500402463900, 147573952589676412}, + {12632330341676300947, 236118324143482260}, + {10105864273341040758, 188894659314785808}, + {15463389048156653253, 151115727451828646}, + {17362724847566824558, 241785163922925834}, + {17579528692795369969, 193428131138340667}, + {6684925324752475329, 154742504910672534}, + {18074578149087781173, 247588007857076054}, + {18149011334012135262, 198070406285660843}, + {3451162622983977240, 158456325028528675}, + {5521860196774363583, 253530120045645880}, + {4417488157419490867, 202824096036516704}, + {7223339340677503017, 162259276829213363}, + {7867994130342094503, 259614842926741381}, + {2605046489531765280, 207691874341393105}, + {2084037191625412224, 166153499473114484}, + {10713157136084480204, 265845599156983174}, + {12259874523609494487, 212676479325586539}, + {13497248433629505913, 170141183460469231}, + {14216899864323388813, 272225893536750770}, + {11373519891458711051, 217780714829400616}, + {5409467098425058518, 174224571863520493}, + {4965798542738183305, 278759314981632789}, + {7661987648932456967, 223007451985306231}, + {2440241304404055250, 178405961588244985}, + {3904386087046488400, 285449538541191976}, + {17880904128604832013, 228359630832953580}, + {14304723302883865611, 182687704666362864}, + {15133127457049002812, 146150163733090291}, + {16834306301794583852, 233840261972944466}, + {9778096226693756759, 187072209578355573}, + {15201174610838826053, 149657767662684458}, + {2185786488890659746, 239452428260295134}, + {5437978005854438120, 191561942608236107}, + {15418428848909281466, 153249554086588885}, + {6222742084545298729, 245199286538542217}, + {16046240111861969953, 196159429230833773}, + {1768945645263844993, 156927543384667019}, + {10209010661905972635, 251084069415467230}, + {8167208529524778108, 200867255532373784}, + {10223115638361732810, 160693804425899027}, + {1599589762411131202, 257110087081438444}, + {4969020624670815285, 205688069665150755}, + {3975216499736652228, 164550455732120604}, + {13739044029062464211, 263280729171392966}, + {7301886408508061046, 210624583337114373}, + {13220206756290269483, 168499666669691498}, + {17462981995322520850, 269599466671506397}, + {6591687966774196033, 215679573337205118}, + {12652048002903177473, 172543658669764094}, + {9175230360419352987, 276069853871622551}, + {3650835473593572067, 220855883097298041}, + {17678063637842498946, 176684706477838432}, + {13527506561580357021, 282695530364541492}, + {3443307619780464970, 226156424291633194}, + {6443994910566282300, 180925139433306555}, + {5155195928453025840, 144740111546645244}, + {15627011115008661990, 231584178474632390}, + {12501608892006929592, 185267342779705912}, + {2622589484121723027, 148213874223764730}, + {4196143174594756843, 237142198758023568}, + {10735612169159626121, 189713759006418854}, + {12277838550069611220, 151771007205135083}, + {15955192865369467629, 242833611528216133}, + {1696107848069843133, 194266889222572907}, + {12424932722681605476, 155413511378058325}, + {1433148282581017146, 248661618204893321}, + {15903913885032455010, 198929294563914656}, + {9033782293284053685, 159143435651131725}, + {14454051669254485895, 254629497041810760}, + {11563241335403588716, 203703597633448608}, + {16629290697806691620, 162962878106758886}, + {781423413297334329, 260740604970814219}, + {4314487545379777786, 208592483976651375}, + {3451590036303822229, 166873987181321100}, + {5522544058086115566, 266998379490113760}, + {4418035246468892453, 213598703592091008}, + {10913125826658934609, 170878962873672806}, + {10082303693170474728, 273406340597876490}, + {8065842954536379782, 218725072478301192}, + {17520720807854834795, 174980057982640953}, + {5897060404116273733, 279968092772225526}, + {1028299508551108663, 223974474217780421}, + {15580034865808528224, 179179579374224336}, + {17549358155809824511, 286687326998758938}, + {2971440080422128639, 229349861599007151}, + {17134547323305344204, 183479889279205720}, + {13707637858644275364, 146783911423364576}, + {14553522944347019935, 234854258277383322}, + {4264120725993795302, 187883406621906658}, + {10789994210278856888, 150306725297525326}, + {9885293106962350374, 240490760476040522}, + {529536856086059653, 192392608380832418}, + {7802327114352668369, 153914086704665934}, + {1415676938738538420, 246262538727465495}, + {1132541550990830736, 197010030981972396}, + {15663428499760305882, 157608024785577916}, + {17682787970132668764, 252172839656924666}, + {10456881561364224688, 201738271725539733}, + {15744202878575200397, 161390617380431786}, + {17812026976236499989, 258224987808690858}, + {3181575136763469022, 206579990246952687}, + {13613306553636506187, 165263992197562149}, + {10713244041592678929, 264422387516099439}, + {12259944048016053467, 211537910012879551}, + {6118606423670932450, 169230328010303641}, + {2411072648389671274, 270768524816485826}, + {16686253377679378312, 216614819853188660}, + {13349002702143502650, 173291855882550928}, + {17669055508687693916, 277266969412081485}, + {14135244406950155133, 221813575529665188}, + {240149081334393137, 177450860423732151}, + {11452284974360759988, 283921376677971441}, + {5472479164746697667, 227137101342377153}, + {11756680961281178780, 181709681073901722}, + {2026647139541122378, 145367744859121378}, + {18000030682233437097, 232588391774594204}, + {18089373360528660001, 186070713419675363}, + {3403452244197197031, 148856570735740291}, + {16513570034941246220, 238170513177184465}, + {13210856027952996976, 190536410541747572}, + {3189987192878576934, 152429128433398058}, + {1414630693863812771, 243886605493436893}, + {8510402184574870864, 195109284394749514}, + {10497670562401807014, 156087427515799611}, + {9417575270359070576, 249739884025279378}, + {14912757845771077107, 199791907220223502}, + {4551508647133041040, 159833525776178802}, + {10971762650154775986, 255733641241886083}, + {16156107749607641435, 204586912993508866}, + {9235537384944202825, 163669530394807093}, + {11087511001168814197, 261871248631691349}, + {12559357615676961681, 209496998905353079}, + {13736834907283479668, 167597599124282463}, + {18289587036911657145, 268156158598851941}, + {10942320814787415393, 214524926879081553}, + {16132554281313752961, 171619941503265242}, + {11054691591134363444, 274591906405224388}, + {16222450902391311402, 219673525124179510}, + {12977960721913049122, 175738820099343608}, + {17075388340318968271, 281182112158949773}, + {2592264228029443648, 224945689727159819}, + {5763160197165465241, 179956551781727855}, + {9221056315464744386, 287930482850764568}, + {14755542681855616155, 230344386280611654}, + {15493782960226403247, 184275509024489323}, + {1326979923955391628, 147420407219591459}, + {9501865507812447252, 235872651551346334}, + {11290841220991868125, 188698121241077067}, + {1653975347309673853, 150958496992861654}, + {10025058185179298811, 241533595188578646}, + {4330697733401528726, 193226876150862917}, + {14532604630946953951, 154581500920690333}, + {1116074521063664381, 247330401473104534}, + {4582208431592841828, 197864321178483627}, + {14733813189500004432, 158291456942786901}, + {16195403473716186445, 253266331108459042}, + {5577625149489128510, 202613064886767234}, + {8151448934333213131, 162090451909413787}, + {16731667109675051333, 259344723055062059}, + {17074682502481951390, 207475778444049647}, + {6281048372501740465, 165980622755239718}, + {6360328581260874421, 265568996408383549}, + {8777611679750609860, 212455197126706839}, + {10711438158542398211, 169964157701365471}, + {9759603424184016492, 271942652322184754}, + {11497031554089123517, 217554121857747803}, + {16576322872755119460, 174043297486198242}, + {11764721337440549842, 278469275977917188}, + {16790474699436260520, 222775420782333750}, + {13432379759549008416, 178220336625867000}, + {3045063541568861850, 285152538601387201}, + {17193446092222730773, 228122030881109760}, + {13754756873778184618, 182497624704887808}, + {18382503128506368341, 145998099763910246}, + {3586563302416817083, 233596959622256395}, + {2869250641933453667, 186877567697805116}, + {17052795772514404226, 149502054158244092}, + {12527077977055405469, 239203286653190548}, + {17400360011128145022, 191362629322552438}, + {2852241564676785048, 153090103458041951}, + {15631632947708587046, 244944165532867121}, + {8815957543424959314, 195955332426293697}, + {18120812478965698421, 156764265941034957}, + {14235904707377476180, 250822825505655932}, + {4010026136418160298, 200658260404524746}, + {17965416168102169531, 160526608323619796}, + {2919224165770098987, 256842573317791675}, + {2335379332616079190, 205474058654233340}, + {1868303466092863352, 164379246923386672}, + {6678634360490491686, 263006795077418675}, + {5342907488392393349, 210405436061934940}, + {4274325990713914679, 168324348849547952}, + {10528270399884173809, 269318958159276723}, + {15801313949391159694, 215455166527421378}, + {1573004715287196786, 172364133221937103}, + {17274202803427156150, 275782613155099364}, + {17508711057483635243, 220626090524079491}, + {10317620031244997871, 176500872419263593}, + {12818843235250086271, 282401395870821749}, + {13944423402941979340, 225921116696657399}, + {14844887537095493795, 180736893357325919}, + {15565258844418305359, 144589514685860735}, + {6457670077359736959, 231343223497377177}, + {16234182506113520537, 185074578797901741}, + {9297997190148906106, 148059663038321393}, + {11187446689496339446, 236895460861314229}, + {12639306166338981880, 189516368689051383}, + {17490142562555006151, 151613094951241106}, + {2158786396894637579, 242580951921985771}, + {16484424376483351356, 194064761537588616}, + {9498190686444770762, 155251809230070893}, + {11507756283569722895, 248402894768113429}, + {12895553841597688639, 198722315814490743}, + {17695140702761971558, 158977852651592594}, + {17244178680193423523, 254364564242548151}, + {10105994129412828495, 203491651394038521}, + {4395446488788352473, 162793321115230817}, + {10722063196803274280, 260469313784369307}, + {1198952927958798777, 208375451027495446}, + {15716557601334680315, 166700360821996356}, + {17767794532651667857, 266720577315194170}, + {14214235626121334286, 213376461852155336}, + {7682039686155157106, 170701169481724269}, + {1223217053622520399, 273121871170758831}, + {15735968901865657612, 218497496936607064}, + {16278123936234436413, 174797997549285651}, + {219556594781725998, 279676796078857043}, + {7554342905309201445, 223741436863085634}, + {9732823138989271479, 178993149490468507}, + {815121763415193074, 286389039184749612}, + {11720143854957885429, 229111231347799689}, + {13065463898708218666, 183288985078239751}, + {6763022304224664610, 146631188062591801}, + {3442138057275642729, 234609900900146882}, + {13821756890046245153, 187687920720117505}, + {11057405512036996122, 150150336576094004}, + {6623802375033462826, 240240538521750407}, + {16367088344252501231, 192192430817400325}, + {13093670675402000985, 153753944653920260}, + {2503129006933649959, 246006311446272417}, + {13070549649772650937, 196805049157017933}, + {17835137349301941396, 157444039325614346}, + {2710778055689733971, 251910462920982955}, + {2168622444551787177, 201528370336786364}, + {5424246770383340065, 161222696269429091}, + {1300097203129523457, 257956314031086546}, + {15797473021471260058, 206365051224869236}, + {8948629602435097724, 165092040979895389}, + {3249760919670425388, 264147265567832623}, + {9978506365220160957, 211317812454266098}, + {15361502721659949412, 169054249963412878}, + {2442311466204457120, 270486799941460606}, + {16711244431931206989, 216389439953168484}, + {17058344360286875914, 173111551962534787}, + {12535955717491360170, 276978483140055660}, + {10028764573993088136, 221582786512044528}, + {15401709288678291155, 177266229209635622}, + {9885339602917624555, 283625966735416996}, + {4218922867592189321, 226900773388333597}, + {14443184738299482427, 181520618710666877}, + {4175850161155765295, 145216494968533502}, + {10370709072591134795, 232346391949653603}, + {15675264887556728482, 185877113559722882}, + {5161514280561562140, 148701690847778306}, + {879725219414678777, 237922705356445290}, + {703780175531743021, 190338164285156232}, + {11631070584651125387, 152270531428124985}, + {162968861732249003, 243632850284999977}, + {11198421533611530172, 194906280227999981}, + {5269388412147313814, 155925024182399985}, + {8431021459435702103, 249480038691839976}, + {3055468352806651359, 199584030953471981}, + {17201769941212962380, 159667224762777584}, + {16454785461715008838, 255467559620444135}, + {13163828369372007071, 204374047696355308}, + {17909760324981426303, 163499238157084246}, + {2830174816776909822, 261598781051334795}, + {2264139853421527858, 209279024841067836}, + {16568707141704863579, 167423219872854268}, + {4373838538276319787, 267877151796566830}, + {3499070830621055830, 214301721437253464}, + {6488605479238754987, 171441377149802771}, + {3003071137298187333, 274306203439684434}, + {6091805724580460189, 219444962751747547}, + {15941491023890099121, 175555970201398037}, + {10748990379256517301, 280889552322236860}, + {8599192303405213841, 224711641857789488}, + {14258051472207991719, 179769313486231590}, +} diff --git a/examples/gno.land/p/demo/json/token.gno b/examples/gno.land/p/demo/json/token.gno new file mode 100644 index 00000000000..4791850bf46 --- /dev/null +++ b/examples/gno.land/p/demo/json/token.gno @@ -0,0 +1,76 @@ +package json + +const ( + bracketOpen = '[' + bracketClose = ']' + parenOpen = '(' + parenClose = ')' + curlyOpen = '{' + curlyClose = '}' + comma = ',' + dot = '.' + colon = ':' + backTick = '`' + singleQuote = '\'' + doubleQuote = '"' + emptyString = "" + whiteSpace = ' ' + plus = '+' + minus = '-' + aesterisk = '*' + bang = '!' + question = '?' + newLine = '\n' + tab = '\t' + carriageReturn = '\r' + formFeed = '\f' + backSpace = '\b' + slash = '/' + backSlash = '\\' + underScore = '_' + dollarSign = '$' + atSign = '@' + andSign = '&' + orSign = '|' +) + +var ( + trueLiteral = []byte("true") + falseLiteral = []byte("false") + nullLiteral = []byte("null") +) + +type ValueType int + +const ( + NotExist ValueType = iota + String + Number + Float + Object + Array + Boolean + Null + Unknown +) + +func (v ValueType) String() string { + switch v { + case NotExist: + return "not-exist" + case String: + return "string" + case Number: + return "number" + case Object: + return "object" + case Array: + return "array" + case Boolean: + return "boolean" + case Null: + return "null" + default: + return "unknown" + } +}