200 lines
6.0 KiB
Go
200 lines
6.0 KiB
Go
/*
|
|
* Copyright 2021 ByteDance Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package encoder
|
|
|
|
import (
|
|
"encoding"
|
|
"reflect"
|
|
"sync"
|
|
"unsafe"
|
|
|
|
"github.com/bytedance/sonic/internal/native"
|
|
"github.com/bytedance/sonic/internal/rt"
|
|
)
|
|
|
|
type _MapPair struct {
|
|
k string // when the map key is integer, k is pointed to m
|
|
v unsafe.Pointer
|
|
m [32]byte
|
|
}
|
|
|
|
type _MapIterator struct {
|
|
it rt.GoMapIterator // must be the first field
|
|
kv rt.GoSlice // slice of _MapPair
|
|
ki int
|
|
}
|
|
|
|
var (
|
|
iteratorPool = sync.Pool{}
|
|
iteratorPair = rt.UnpackType(reflect.TypeOf(_MapPair{}))
|
|
)
|
|
|
|
func init() {
|
|
if unsafe.Offsetof(_MapIterator{}.it) != 0 {
|
|
panic("_MapIterator.it is not the first field")
|
|
}
|
|
}
|
|
|
|
|
|
func newIterator() *_MapIterator {
|
|
if v := iteratorPool.Get(); v == nil {
|
|
return new(_MapIterator)
|
|
} else {
|
|
return resetIterator(v.(*_MapIterator))
|
|
}
|
|
}
|
|
|
|
func resetIterator(p *_MapIterator) *_MapIterator {
|
|
p.ki = 0
|
|
p.it = rt.GoMapIterator{}
|
|
p.kv.Len = 0
|
|
return p
|
|
}
|
|
|
|
func (self *_MapIterator) at(i int) *_MapPair {
|
|
return (*_MapPair)(unsafe.Pointer(uintptr(self.kv.Ptr) + uintptr(i) * unsafe.Sizeof(_MapPair{})))
|
|
}
|
|
|
|
func (self *_MapIterator) add() (p *_MapPair) {
|
|
p = self.at(self.kv.Len)
|
|
self.kv.Len++
|
|
return
|
|
}
|
|
|
|
func (self *_MapIterator) data() (p []_MapPair) {
|
|
*(*rt.GoSlice)(unsafe.Pointer(&p)) = self.kv
|
|
return
|
|
}
|
|
|
|
func (self *_MapIterator) append(t *rt.GoType, k unsafe.Pointer, v unsafe.Pointer) (err error) {
|
|
p := self.add()
|
|
p.v = v
|
|
|
|
/* check for strings */
|
|
if tk := t.Kind(); tk != reflect.String {
|
|
return self.appendGeneric(p, t, tk, k)
|
|
}
|
|
|
|
/* fast path for strings */
|
|
p.k = *(*string)(k)
|
|
return nil
|
|
}
|
|
|
|
func (self *_MapIterator) appendGeneric(p *_MapPair, t *rt.GoType, v reflect.Kind, k unsafe.Pointer) error {
|
|
switch v {
|
|
case reflect.Int : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int)(k)))]) ; return nil
|
|
case reflect.Int8 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int8)(k)))]) ; return nil
|
|
case reflect.Int16 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int16)(k)))]) ; return nil
|
|
case reflect.Int32 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int32)(k)))]) ; return nil
|
|
case reflect.Int64 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], *(*int64)(k))]) ; return nil
|
|
case reflect.Uint : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint)(k)))]) ; return nil
|
|
case reflect.Uint8 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint8)(k)))]) ; return nil
|
|
case reflect.Uint16 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint16)(k)))]) ; return nil
|
|
case reflect.Uint32 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint32)(k)))]) ; return nil
|
|
case reflect.Uint64 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], *(*uint64)(k))]) ; return nil
|
|
case reflect.Uintptr : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uintptr)(k)))]) ; return nil
|
|
case reflect.Interface : return self.appendInterface(p, t, k)
|
|
case reflect.Struct, reflect.Ptr : return self.appendConcrete(p, t, k)
|
|
default : panic("unexpected map key type")
|
|
}
|
|
}
|
|
|
|
func (self *_MapIterator) appendConcrete(p *_MapPair, t *rt.GoType, k unsafe.Pointer) (err error) {
|
|
// compiler has already checked that the type implements the encoding.MarshalText interface
|
|
if !t.Indirect() {
|
|
k = *(*unsafe.Pointer)(k)
|
|
}
|
|
eface := rt.GoEface{Value: k, Type: t}.Pack()
|
|
out, err := eface.(encoding.TextMarshaler).MarshalText()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.k = rt.Mem2Str(out)
|
|
return
|
|
}
|
|
|
|
func (self *_MapIterator) appendInterface(p *_MapPair, t *rt.GoType, k unsafe.Pointer) (err error) {
|
|
if len(rt.IfaceType(t).Methods) == 0 {
|
|
panic("unexpected map key type")
|
|
} else if p.k, err = asText(k); err == nil {
|
|
return nil
|
|
} else {
|
|
return
|
|
}
|
|
}
|
|
|
|
func iteratorStop(p *_MapIterator) {
|
|
iteratorPool.Put(p)
|
|
}
|
|
|
|
func iteratorNext(p *_MapIterator) {
|
|
i := p.ki
|
|
t := &p.it
|
|
|
|
/* check for unordered iteration */
|
|
if i < 0 {
|
|
mapiternext(t)
|
|
return
|
|
}
|
|
|
|
/* check for end of iteration */
|
|
if p.ki >= p.kv.Len {
|
|
t.K = nil
|
|
t.V = nil
|
|
return
|
|
}
|
|
|
|
/* update the key-value pair, and increase the pointer */
|
|
t.K = unsafe.Pointer(&p.at(p.ki).k)
|
|
t.V = p.at(p.ki).v
|
|
p.ki++
|
|
}
|
|
|
|
func iteratorStart(t *rt.GoMapType, m *rt.GoMap, fv uint64) (*_MapIterator, error) {
|
|
it := newIterator()
|
|
mapiterinit(t, m, &it.it)
|
|
|
|
/* check for key-sorting, empty map don't need sorting */
|
|
if m.Count == 0 || (fv & uint64(SortMapKeys)) == 0 {
|
|
it.ki = -1
|
|
return it, nil
|
|
}
|
|
|
|
/* pre-allocate space if needed */
|
|
if m.Count > it.kv.Cap {
|
|
it.kv = growslice(iteratorPair, it.kv, m.Count)
|
|
}
|
|
|
|
/* dump all the key-value pairs */
|
|
for ; it.it.K != nil; mapiternext(&it.it) {
|
|
if err := it.append(t.Key, it.it.K, it.it.V); err != nil {
|
|
iteratorStop(it)
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
/* sort the keys, map with only 1 item don't need sorting */
|
|
if it.ki = 1; m.Count > 1 {
|
|
radixQsort(it.data(), 0, maxDepth(it.kv.Len))
|
|
}
|
|
|
|
/* load the first pair into iterator */
|
|
it.it.V = it.at(0).v
|
|
it.it.K = unsafe.Pointer(&it.at(0).k)
|
|
return it, nil
|
|
}
|