summaryrefslogtreecommitdiff
path: root/vendor/github.com/goccy/go-json/internal/decoder/unmarshal_text.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/goccy/go-json/internal/decoder/unmarshal_text.go')
-rw-r--r--vendor/github.com/goccy/go-json/internal/decoder/unmarshal_text.go280
1 files changed, 280 insertions, 0 deletions
diff --git a/vendor/github.com/goccy/go-json/internal/decoder/unmarshal_text.go b/vendor/github.com/goccy/go-json/internal/decoder/unmarshal_text.go
new file mode 100644
index 000000000..1ef287782
--- /dev/null
+++ b/vendor/github.com/goccy/go-json/internal/decoder/unmarshal_text.go
@@ -0,0 +1,280 @@
+package decoder
+
+import (
+ "bytes"
+ "encoding"
+ "unicode"
+ "unicode/utf16"
+ "unicode/utf8"
+ "unsafe"
+
+ "github.com/goccy/go-json/internal/errors"
+ "github.com/goccy/go-json/internal/runtime"
+)
+
+type unmarshalTextDecoder struct {
+ typ *runtime.Type
+ structName string
+ fieldName string
+}
+
+func newUnmarshalTextDecoder(typ *runtime.Type, structName, fieldName string) *unmarshalTextDecoder {
+ return &unmarshalTextDecoder{
+ typ: typ,
+ structName: structName,
+ fieldName: fieldName,
+ }
+}
+
+func (d *unmarshalTextDecoder) annotateError(cursor int64, err error) {
+ switch e := err.(type) {
+ case *errors.UnmarshalTypeError:
+ e.Struct = d.structName
+ e.Field = d.fieldName
+ case *errors.SyntaxError:
+ e.Offset = cursor
+ }
+}
+
+var (
+ nullbytes = []byte(`null`)
+)
+
+func (d *unmarshalTextDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
+ s.skipWhiteSpace()
+ start := s.cursor
+ if err := s.skipValue(depth); err != nil {
+ return err
+ }
+ src := s.buf[start:s.cursor]
+ if len(src) > 0 {
+ switch src[0] {
+ case '[':
+ return &errors.UnmarshalTypeError{
+ Value: "array",
+ Type: runtime.RType2Type(d.typ),
+ Offset: s.totalOffset(),
+ }
+ case '{':
+ return &errors.UnmarshalTypeError{
+ Value: "object",
+ Type: runtime.RType2Type(d.typ),
+ Offset: s.totalOffset(),
+ }
+ case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return &errors.UnmarshalTypeError{
+ Value: "number",
+ Type: runtime.RType2Type(d.typ),
+ Offset: s.totalOffset(),
+ }
+ case 'n':
+ if bytes.Equal(src, nullbytes) {
+ *(*unsafe.Pointer)(p) = nil
+ return nil
+ }
+ }
+ }
+ dst := make([]byte, len(src))
+ copy(dst, src)
+
+ if b, ok := unquoteBytes(dst); ok {
+ dst = b
+ }
+ v := *(*interface{})(unsafe.Pointer(&emptyInterface{
+ typ: d.typ,
+ ptr: p,
+ }))
+ if err := v.(encoding.TextUnmarshaler).UnmarshalText(dst); err != nil {
+ d.annotateError(s.cursor, err)
+ return err
+ }
+ return nil
+}
+
+func (d *unmarshalTextDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
+ buf := ctx.Buf
+ cursor = skipWhiteSpace(buf, cursor)
+ start := cursor
+ end, err := skipValue(buf, cursor, depth)
+ if err != nil {
+ return 0, err
+ }
+ src := buf[start:end]
+ if len(src) > 0 {
+ switch src[0] {
+ case '[':
+ return 0, &errors.UnmarshalTypeError{
+ Value: "array",
+ Type: runtime.RType2Type(d.typ),
+ Offset: start,
+ }
+ case '{':
+ return 0, &errors.UnmarshalTypeError{
+ Value: "object",
+ Type: runtime.RType2Type(d.typ),
+ Offset: start,
+ }
+ case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return 0, &errors.UnmarshalTypeError{
+ Value: "number",
+ Type: runtime.RType2Type(d.typ),
+ Offset: start,
+ }
+ case 'n':
+ if bytes.Equal(src, nullbytes) {
+ *(*unsafe.Pointer)(p) = nil
+ return end, nil
+ }
+ }
+ }
+
+ if s, ok := unquoteBytes(src); ok {
+ src = s
+ }
+ v := *(*interface{})(unsafe.Pointer(&emptyInterface{
+ typ: d.typ,
+ ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)),
+ }))
+ if err := v.(encoding.TextUnmarshaler).UnmarshalText(src); err != nil {
+ d.annotateError(cursor, err)
+ return 0, err
+ }
+ return end, nil
+}
+
+func unquoteBytes(s []byte) (t []byte, ok bool) {
+ length := len(s)
+ if length < 2 || s[0] != '"' || s[length-1] != '"' {
+ return
+ }
+ s = s[1 : length-1]
+ length -= 2
+
+ // Check for unusual characters. If there are none,
+ // then no unquoting is needed, so return a slice of the
+ // original bytes.
+ r := 0
+ for r < length {
+ c := s[r]
+ if c == '\\' || c == '"' || c < ' ' {
+ break
+ }
+ if c < utf8.RuneSelf {
+ r++
+ continue
+ }
+ rr, size := utf8.DecodeRune(s[r:])
+ if rr == utf8.RuneError && size == 1 {
+ break
+ }
+ r += size
+ }
+ if r == length {
+ return s, true
+ }
+
+ b := make([]byte, length+2*utf8.UTFMax)
+ w := copy(b, s[0:r])
+ for r < length {
+ // Out of room? Can only happen if s is full of
+ // malformed UTF-8 and we're replacing each
+ // byte with RuneError.
+ if w >= len(b)-2*utf8.UTFMax {
+ nb := make([]byte, (len(b)+utf8.UTFMax)*2)
+ copy(nb, b[0:w])
+ b = nb
+ }
+ switch c := s[r]; {
+ case c == '\\':
+ r++
+ if r >= length {
+ return
+ }
+ switch s[r] {
+ default:
+ return
+ case '"', '\\', '/', '\'':
+ b[w] = s[r]
+ r++
+ w++
+ case 'b':
+ b[w] = '\b'
+ r++
+ w++
+ case 'f':
+ b[w] = '\f'
+ r++
+ w++
+ case 'n':
+ b[w] = '\n'
+ r++
+ w++
+ case 'r':
+ b[w] = '\r'
+ r++
+ w++
+ case 't':
+ b[w] = '\t'
+ r++
+ w++
+ case 'u':
+ r--
+ rr := getu4(s[r:])
+ if rr < 0 {
+ return
+ }
+ r += 6
+ if utf16.IsSurrogate(rr) {
+ rr1 := getu4(s[r:])
+ if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar {
+ // A valid pair; consume.
+ r += 6
+ w += utf8.EncodeRune(b[w:], dec)
+ break
+ }
+ // Invalid surrogate; fall back to replacement rune.
+ rr = unicode.ReplacementChar
+ }
+ w += utf8.EncodeRune(b[w:], rr)
+ }
+
+ // Quote, control characters are invalid.
+ case c == '"', c < ' ':
+ return
+
+ // ASCII
+ case c < utf8.RuneSelf:
+ b[w] = c
+ r++
+ w++
+
+ // Coerce to well-formed UTF-8.
+ default:
+ rr, size := utf8.DecodeRune(s[r:])
+ r += size
+ w += utf8.EncodeRune(b[w:], rr)
+ }
+ }
+ return b[0:w], true
+}
+
+func getu4(s []byte) rune {
+ if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
+ return -1
+ }
+ var r rune
+ for _, c := range s[2:6] {
+ switch {
+ case '0' <= c && c <= '9':
+ c = c - '0'
+ case 'a' <= c && c <= 'f':
+ c = c - 'a' + 10
+ case 'A' <= c && c <= 'F':
+ c = c - 'A' + 10
+ default:
+ return -1
+ }
+ r = r*16 + rune(c)
+ }
+ return r
+}