github.com/trim21/go-phpserialize@v0.0.22-0.20240301204449-2fca0319b3f0/internal/encoder/map_reflect.go (about) 1 package encoder 2 3 import ( 4 "reflect" 5 "unsafe" 6 7 "github.com/trim21/go-phpserialize/internal/runtime" 8 ) 9 10 // fast array for map reflect 11 var mapKeyEncoder = [25]encoder{ 12 reflect.String: encodeString, 13 reflect.Int: encodeInt, 14 reflect.Int8: encodeInt8, 15 reflect.Int16: encodeInt16, 16 reflect.Int32: encodeInt32, 17 reflect.Int64: encodeInt64, 18 reflect.Uint: encodeUint, 19 reflect.Uint8: encodeUint8, 20 reflect.Uint16: encodeUint16, 21 reflect.Uint32: encodeUint32, 22 reflect.Uint64: encodeUint64, 23 } 24 25 func reflectMap(ctx *Ctx, b []byte, rv reflect.Value) ([]byte, error) { 26 rt := rv.Type() 27 keyType := rt.Key() 28 29 switch keyType.Kind() { 30 case reflect.String, 31 reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 32 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 33 default: 34 return nil, &UnsupportedTypeAsMapKeyError{Type: rv.Type().Key()} 35 } 36 37 if rv.IsNil() { 38 return appendNull(b), nil 39 } 40 41 // iter keys and values 42 // non interface value type, fast path with uintptr 43 if rt.Elem().Kind() != reflect.Interface { 44 return reflectConcreteMap(ctx, b, rt, rv, keyType) 45 } 46 47 mapLen := rv.Len() 48 if mapLen == 0 { 49 return appendEmptyArray(b), nil 50 } 51 52 b = appendArrayBegin(b, int64(mapLen)) 53 54 keyEncoder := mapKeyEncoder[keyType.Kind()] 55 56 var mr = newMapCtx() 57 defer freeMapCtx(mr) 58 59 valueEncoder, err := compileInterface(runtime.Type2RType(rt.Elem())) 60 if err != nil { 61 return b, err 62 } 63 64 runtime.MapIterInit(runtime.Type2RType(rt), unsafe.Pointer(rv.Pointer()), &mr.Iter) 65 for i := 0; i < mapLen; i++ { 66 b, err = keyEncoder(ctx, b, runtime.MapIterKey(&mr.Iter)) 67 if err != nil { 68 return b, err 69 } 70 71 b, err = valueEncoder(ctx, b, runtime.MapIterValue(&mr.Iter)) 72 if err != nil { 73 return b, err 74 } 75 76 runtime.MapIterNext(&mr.Iter) 77 } 78 79 b = append(b, '}') 80 81 return b, nil 82 } 83 84 func reflectConcreteMap(ctx *Ctx, b []byte, rt reflect.Type, rv reflect.Value, keyType reflect.Type) ([]byte, error) { 85 mapLen := rv.Len() 86 if mapLen == 0 { 87 return appendEmptyArray(b), nil 88 } 89 90 b = appendArrayBegin(b, int64(mapLen)) 91 92 // map has a different reflect.Value{}.flag. 93 // map's address may be direct or indirect address 94 var valueEncoder encoder 95 var err error 96 var valueType = rt.Elem() 97 98 valueEncoder, err = compileWithCache(runtime.Type2RType(valueType)) 99 if err != nil { 100 return nil, err 101 } 102 103 if rt.Elem().Kind() == reflect.Map { 104 originValueEncoder := valueEncoder 105 valueEncoder = func(ctx *Ctx, b []byte, p uintptr) ([]byte, error) { 106 return originValueEncoder(ctx, b, PtrDeRef(p)) 107 } 108 } 109 110 keyEncoder := mapKeyEncoder[keyType.Kind()] 111 112 var mr = newMapCtx() 113 defer freeMapCtx(mr) 114 115 runtime.MapIterInit(runtime.Type2RType(rt), unsafe.Pointer(rv.Pointer()), &mr.Iter) 116 for i := 0; i < mapLen; i++ { 117 b, err = keyEncoder(ctx, b, runtime.MapIterKey(&mr.Iter)) 118 if err != nil { 119 return b, err 120 } 121 122 b, err = valueEncoder(ctx, b, runtime.MapIterValue(&mr.Iter)) 123 if err != nil { 124 return b, err 125 } 126 127 runtime.MapIterNext(&mr.Iter) 128 } 129 130 return append(b, '}'), nil 131 }