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  }