github.com/grbit/go-json@v0.11.0/internal/decoder/slice.go (about)

     1  package decoder
     2  
     3  import (
     4  	"reflect"
     5  	"sync"
     6  	"unsafe"
     7  
     8  	"github.com/grbit/go-json/internal/errors"
     9  	"github.com/grbit/go-json/internal/runtime"
    10  )
    11  
    12  var (
    13  	sliceType = runtime.Type2RType(
    14  		reflect.TypeOf((*sliceHeader)(nil)).Elem(),
    15  	)
    16  	nilSlice = unsafe.Pointer(&sliceHeader{})
    17  )
    18  
    19  type sliceDecoder struct {
    20  	elemType          *runtime.Type
    21  	isElemPointerType bool
    22  	valueDecoder      Decoder
    23  	size              uintptr
    24  	arrayPool         sync.Pool
    25  	structName        string
    26  	fieldName         string
    27  }
    28  
    29  // If use reflect.SliceHeader, data type is uintptr.
    30  // In this case, Go compiler cannot trace reference created by newArray().
    31  // So, define using unsafe.Pointer as data type
    32  type sliceHeader struct {
    33  	data unsafe.Pointer
    34  	len  int
    35  	cap  int
    36  }
    37  
    38  const (
    39  	defaultSliceCapacity = 2
    40  )
    41  
    42  func newSliceDecoder(dec Decoder, elemType *runtime.Type, size uintptr, structName, fieldName string) *sliceDecoder {
    43  	return &sliceDecoder{
    44  		valueDecoder:      dec,
    45  		elemType:          elemType,
    46  		isElemPointerType: elemType.Kind() == reflect.Ptr || elemType.Kind() == reflect.Map,
    47  		size:              size,
    48  		arrayPool: sync.Pool{
    49  			New: func() interface{} {
    50  				return &sliceHeader{
    51  					data: newArray(elemType, defaultSliceCapacity),
    52  					len:  0,
    53  					cap:  defaultSliceCapacity,
    54  				}
    55  			},
    56  		},
    57  		structName: structName,
    58  		fieldName:  fieldName,
    59  	}
    60  }
    61  
    62  func (d *sliceDecoder) newSlice(src *sliceHeader) *sliceHeader {
    63  	slice := d.arrayPool.Get().(*sliceHeader)
    64  	if src.len > 0 {
    65  		// copy original elem
    66  		if slice.cap < src.cap {
    67  			data := newArray(d.elemType, src.cap)
    68  			slice = &sliceHeader{data: data, len: src.len, cap: src.cap}
    69  		} else {
    70  			slice.len = src.len
    71  		}
    72  		copySlice(d.elemType, *slice, *src)
    73  	} else {
    74  		slice.len = 0
    75  	}
    76  	return slice
    77  }
    78  
    79  func (d *sliceDecoder) releaseSlice(p *sliceHeader) {
    80  	d.arrayPool.Put(p)
    81  }
    82  
    83  //go:linkname copySlice reflect.typedslicecopy
    84  func copySlice(elemType *runtime.Type, dst, src sliceHeader) int
    85  
    86  //go:linkname newArray reflect.unsafe_NewArray
    87  func newArray(*runtime.Type, int) unsafe.Pointer
    88  
    89  //go:linkname typedmemmove reflect.typedmemmove
    90  func typedmemmove(t *runtime.Type, dst, src unsafe.Pointer)
    91  
    92  func (d *sliceDecoder) errNumber(offset int64) *errors.UnmarshalTypeError {
    93  	return &errors.UnmarshalTypeError{
    94  		Value:  "number",
    95  		Type:   reflect.SliceOf(runtime.RType2Type(d.elemType)),
    96  		Struct: d.structName,
    97  		Field:  d.fieldName,
    98  		Offset: offset,
    99  	}
   100  }
   101  
   102  func (d *sliceDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
   103  	depth++
   104  	if depth > maxDecodeNestingDepth {
   105  		return errors.ErrExceededMaxDepth(s.char(), s.cursor)
   106  	}
   107  
   108  	for {
   109  		switch s.char() {
   110  		case ' ', '\n', '\t', '\r':
   111  			s.cursor++
   112  			continue
   113  		case 'n':
   114  			if err := nullBytes(s); err != nil {
   115  				return err
   116  			}
   117  			typedmemmove(sliceType, p, nilSlice)
   118  			return nil
   119  		case '[':
   120  			s.cursor++
   121  			if s.skipWhiteSpace() == ']' {
   122  				dst := (*sliceHeader)(p)
   123  				if dst.data == nil {
   124  					dst.data = newArray(d.elemType, 0)
   125  				} else {
   126  					dst.len = 0
   127  				}
   128  				s.cursor++
   129  				return nil
   130  			}
   131  			idx := 0
   132  			slice := d.newSlice((*sliceHeader)(p))
   133  			srcLen := slice.len
   134  			capacity := slice.cap
   135  			data := slice.data
   136  			for {
   137  				if capacity <= idx {
   138  					src := sliceHeader{data: data, len: idx, cap: capacity}
   139  					capacity *= 2
   140  					data = newArray(d.elemType, capacity)
   141  					dst := sliceHeader{data: data, len: idx, cap: capacity}
   142  					copySlice(d.elemType, dst, src)
   143  				}
   144  				ep := unsafe.Pointer(uintptr(data) + uintptr(idx)*d.size)
   145  
   146  				// if srcLen is greater than idx, keep the original reference
   147  				if srcLen <= idx {
   148  					if d.isElemPointerType {
   149  						**(**unsafe.Pointer)(unsafe.Pointer(&ep)) = nil // initialize elem pointer
   150  					} else {
   151  						// assign new element to the slice
   152  						typedmemmove(d.elemType, ep, unsafe_New(d.elemType))
   153  					}
   154  				}
   155  
   156  				if err := d.valueDecoder.DecodeStream(s, depth, ep); err != nil {
   157  					return err
   158  				}
   159  				s.skipWhiteSpace()
   160  			RETRY:
   161  				switch s.char() {
   162  				case ']':
   163  					slice.cap = capacity
   164  					slice.len = idx + 1
   165  					slice.data = data
   166  					dst := (*sliceHeader)(p)
   167  					dst.len = idx + 1
   168  					if dst.len > dst.cap {
   169  						dst.data = newArray(d.elemType, dst.len)
   170  						dst.cap = dst.len
   171  					}
   172  					copySlice(d.elemType, *dst, *slice)
   173  					d.releaseSlice(slice)
   174  					s.cursor++
   175  					return nil
   176  				case ',':
   177  					idx++
   178  				case nul:
   179  					if s.read() {
   180  						goto RETRY
   181  					}
   182  					slice.cap = capacity
   183  					slice.data = data
   184  					d.releaseSlice(slice)
   185  					goto ERROR
   186  				default:
   187  					slice.cap = capacity
   188  					slice.data = data
   189  					d.releaseSlice(slice)
   190  					goto ERROR
   191  				}
   192  				s.cursor++
   193  			}
   194  		case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
   195  			return d.errNumber(s.totalOffset())
   196  		case nul:
   197  			if s.read() {
   198  				continue
   199  			}
   200  			goto ERROR
   201  		default:
   202  			goto ERROR
   203  		}
   204  	}
   205  ERROR:
   206  	return errors.ErrUnexpectedEndOfJSON("slice", s.totalOffset())
   207  }
   208  
   209  func (d *sliceDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
   210  	buf := ctx.Buf
   211  	depth++
   212  	if depth > maxDecodeNestingDepth {
   213  		return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
   214  	}
   215  
   216  	for {
   217  		switch buf[cursor] {
   218  		case ' ', '\n', '\t', '\r':
   219  			cursor++
   220  			continue
   221  		case 'n':
   222  			if err := validateNull(buf, cursor); err != nil {
   223  				return 0, err
   224  			}
   225  			cursor += 4
   226  			typedmemmove(sliceType, p, nilSlice)
   227  			return cursor, nil
   228  		case '[':
   229  			cursor++
   230  			cursor = skipWhiteSpace(buf, cursor)
   231  			if buf[cursor] == ']' {
   232  				dst := (*sliceHeader)(p)
   233  				if dst.data == nil {
   234  					dst.data = newArray(d.elemType, 0)
   235  				} else {
   236  					dst.len = 0
   237  				}
   238  				cursor++
   239  				return cursor, nil
   240  			}
   241  			idx := 0
   242  			slice := d.newSlice((*sliceHeader)(p))
   243  			srcLen := slice.len
   244  			capacity := slice.cap
   245  			data := slice.data
   246  			for {
   247  				if capacity <= idx {
   248  					src := sliceHeader{data: data, len: idx, cap: capacity}
   249  					capacity *= 2
   250  					data = newArray(d.elemType, capacity)
   251  					dst := sliceHeader{data: data, len: idx, cap: capacity}
   252  					copySlice(d.elemType, dst, src)
   253  				}
   254  				ep := unsafe.Pointer(uintptr(data) + uintptr(idx)*d.size)
   255  				// if srcLen is greater than idx, keep the original reference
   256  				if srcLen <= idx {
   257  					if d.isElemPointerType {
   258  						**(**unsafe.Pointer)(unsafe.Pointer(&ep)) = nil // initialize elem pointer
   259  					} else {
   260  						// assign new element to the slice
   261  						typedmemmove(d.elemType, ep, unsafe_New(d.elemType))
   262  					}
   263  				}
   264  				c, err := d.valueDecoder.Decode(ctx, cursor, depth, ep)
   265  				if err != nil {
   266  					return 0, err
   267  				}
   268  				cursor = c
   269  				cursor = skipWhiteSpace(buf, cursor)
   270  				switch buf[cursor] {
   271  				case ']':
   272  					slice.cap = capacity
   273  					slice.len = idx + 1
   274  					slice.data = data
   275  					dst := (*sliceHeader)(p)
   276  					dst.len = idx + 1
   277  					if dst.len > dst.cap {
   278  						dst.data = newArray(d.elemType, dst.len)
   279  						dst.cap = dst.len
   280  					}
   281  					copySlice(d.elemType, *dst, *slice)
   282  					d.releaseSlice(slice)
   283  					cursor++
   284  					return cursor, nil
   285  				case ',':
   286  					idx++
   287  				default:
   288  					slice.cap = capacity
   289  					slice.data = data
   290  					d.releaseSlice(slice)
   291  					return 0, errors.ErrInvalidCharacter(buf[cursor], "slice", cursor)
   292  				}
   293  				cursor++
   294  			}
   295  		case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
   296  			return 0, d.errNumber(cursor)
   297  		default:
   298  			return 0, errors.ErrUnexpectedEndOfJSON("slice", cursor)
   299  		}
   300  	}
   301  }
   302  
   303  func (d *sliceDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
   304  	buf := ctx.Buf
   305  	depth++
   306  	if depth > maxDecodeNestingDepth {
   307  		return nil, 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
   308  	}
   309  
   310  	ret := [][]byte{}
   311  	for {
   312  		switch buf[cursor] {
   313  		case ' ', '\n', '\t', '\r':
   314  			cursor++
   315  			continue
   316  		case 'n':
   317  			if err := validateNull(buf, cursor); err != nil {
   318  				return nil, 0, err
   319  			}
   320  			cursor += 4
   321  			return [][]byte{nullbytes}, cursor, nil
   322  		case '[':
   323  			cursor++
   324  			cursor = skipWhiteSpace(buf, cursor)
   325  			if buf[cursor] == ']' {
   326  				cursor++
   327  				return ret, cursor, nil
   328  			}
   329  			idx := 0
   330  			for {
   331  				child, found, err := ctx.Option.Path.node.Index(idx)
   332  				if err != nil {
   333  					return nil, 0, err
   334  				}
   335  				if found {
   336  					if child != nil {
   337  						oldPath := ctx.Option.Path.node
   338  						ctx.Option.Path.node = child
   339  						paths, c, err := d.valueDecoder.DecodePath(ctx, cursor, depth)
   340  						if err != nil {
   341  							return nil, 0, err
   342  						}
   343  						ctx.Option.Path.node = oldPath
   344  						ret = append(ret, paths...)
   345  						cursor = c
   346  					} else {
   347  						start := cursor
   348  						end, err := skipValue(buf, cursor, depth)
   349  						if err != nil {
   350  							return nil, 0, err
   351  						}
   352  						ret = append(ret, buf[start:end])
   353  						cursor = end
   354  					}
   355  				} else {
   356  					c, err := skipValue(buf, cursor, depth)
   357  					if err != nil {
   358  						return nil, 0, err
   359  					}
   360  					cursor = c
   361  				}
   362  				cursor = skipWhiteSpace(buf, cursor)
   363  				switch buf[cursor] {
   364  				case ']':
   365  					cursor++
   366  					return ret, cursor, nil
   367  				case ',':
   368  					idx++
   369  				default:
   370  					return nil, 0, errors.ErrInvalidCharacter(buf[cursor], "slice", cursor)
   371  				}
   372  				cursor++
   373  			}
   374  		case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
   375  			return nil, 0, d.errNumber(cursor)
   376  		default:
   377  			return nil, 0, errors.ErrUnexpectedEndOfJSON("slice", cursor)
   378  		}
   379  	}
   380  }