github.com/trim21/go-phpserialize@v0.0.22-0.20240301204449-2fca0319b3f0/internal/encoder/map.go (about)

     1  package encoder
     2  
     3  import (
     4  	"reflect"
     5  
     6  	"github.com/trim21/go-phpserialize/internal/runtime"
     7  )
     8  
     9  // !!! not safe to use in reflect case !!!
    10  func compileMap(rt *runtime.Type, seen seenMap) (encoder, error) {
    11  	// for map[int]string, keyType is int, valueType is string
    12  	keyType := rt.Key()
    13  	valueType := rt.Elem()
    14  
    15  	switch keyType.Kind() {
    16  	case reflect.String,
    17  		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
    18  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    19  	default:
    20  		return nil, &UnsupportedTypeAsMapKeyError{Type: runtime.RType2Type(keyType)}
    21  	}
    22  
    23  	keyEncoder, err := compileMapKey(keyType)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	var valueEncoder encoder
    29  
    30  	// need special take care
    31  	// fmt.Println(runtime.IfaceIndir(rt), runtime.IfaceIndir(valueType), rt.String())
    32  	if valueType.Kind() == reflect.Map {
    33  		enc, err := compileMap(valueType, seen)
    34  		if err != nil {
    35  			return nil, err
    36  		}
    37  		valueEncoder = deRefNilEncoder(enc)
    38  	} else {
    39  		valueEncoder, err = compile(valueType, seen)
    40  		if err != nil {
    41  			return nil, err
    42  		}
    43  	}
    44  
    45  	return func(ctx *Ctx, b []byte, p uintptr) ([]byte, error) {
    46  		if p == 0 {
    47  			// nil
    48  			return appendNull(b), nil
    49  		}
    50  
    51  		ptr := ptrToUnsafePtr(p)
    52  
    53  		mapLen := runtime.MapLen(ptr)
    54  		if mapLen == 0 {
    55  			return appendEmptyArray(b), nil
    56  		}
    57  
    58  		b = appendArrayBegin(b, int64(mapLen))
    59  
    60  		var mapCtx = newMapCtx()
    61  		defer freeMapCtx(mapCtx)
    62  
    63  		runtime.MapIterInit(rt, ptr, &mapCtx.Iter)
    64  		var err error // create a new error value, so shadow compiler's error
    65  		for i := 0; i < mapLen; i++ {
    66  			b, err = keyEncoder(ctx, b, runtime.MapIterKey(&mapCtx.Iter))
    67  			if err != nil {
    68  				return b, err
    69  			}
    70  
    71  			b, err = valueEncoder(ctx, b, runtime.MapIterValue(&mapCtx.Iter))
    72  			if err != nil {
    73  				return b, err
    74  			}
    75  
    76  			runtime.MapIterNext(&mapCtx.Iter)
    77  		}
    78  		return append(b, '}'), nil
    79  	}, nil
    80  }