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

     1  package decoder
     2  
     3  import (
     4  	"reflect"
     5  	"unsafe"
     6  
     7  	"github.com/trim21/go-phpserialize/internal/errors"
     8  	"github.com/trim21/go-phpserialize/internal/runtime"
     9  )
    10  
    11  type mapDecoder struct {
    12  	mapType                 *runtime.Type
    13  	keyType                 *runtime.Type
    14  	valueType               *runtime.Type
    15  	canUseAssignFaststrType bool
    16  	keyDecoder              Decoder
    17  	valueDecoder            Decoder
    18  	structName              string
    19  	fieldName               string
    20  }
    21  
    22  func newMapDecoder(mapType *runtime.Type, keyType *runtime.Type, keyDec Decoder, valueType *runtime.Type, valueDec Decoder, structName, fieldName string) *mapDecoder {
    23  	return &mapDecoder{
    24  		mapType:                 mapType,
    25  		keyDecoder:              keyDec,
    26  		keyType:                 keyType,
    27  		canUseAssignFaststrType: canUseAssignFaststrType(keyType, valueType),
    28  		valueType:               valueType,
    29  		valueDecoder:            valueDec,
    30  		structName:              structName,
    31  		fieldName:               fieldName,
    32  	}
    33  }
    34  
    35  const (
    36  	mapMaxElemSize = 128
    37  )
    38  
    39  // See detail: https://github.com/goccy/go-json/pull/283
    40  func canUseAssignFaststrType(key *runtime.Type, value *runtime.Type) bool {
    41  	indirectElem := value.Size() > mapMaxElemSize
    42  	if indirectElem {
    43  		return false
    44  	}
    45  	return key.Kind() == reflect.String
    46  }
    47  
    48  func (d *mapDecoder) mapassign(t *runtime.Type, m, k, v unsafe.Pointer) {
    49  	if d.canUseAssignFaststrType {
    50  		mapV := runtime.MapAssignFastStr(t, m, *(*string)(k))
    51  		typedmemmove(d.valueType, mapV, v)
    52  	} else {
    53  		runtime.MapAssign(t, m, k, v)
    54  	}
    55  }
    56  
    57  func (d *mapDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
    58  	buf := ctx.Buf
    59  	depth++
    60  	if depth > maxDecodeNestingDepth {
    61  		return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
    62  	}
    63  
    64  	buflen := int64(len(buf))
    65  	if buflen < 2 {
    66  		return 0, errors.ErrExpected("{} for map", cursor)
    67  	}
    68  	switch buf[cursor] {
    69  	case 'N':
    70  		if err := validateNull(buf, cursor); err != nil {
    71  			return 0, err
    72  		}
    73  		cursor += 2
    74  		**(**unsafe.Pointer)(unsafe.Pointer(&p)) = nil
    75  		return cursor, nil
    76  	case 'O':
    77  		// O:8:"stdClass":1:{s:1:"a";s:1:"q";}
    78  		end, err := skipClassName(buf, cursor)
    79  		if err != nil {
    80  			return cursor, err
    81  		}
    82  		cursor = end
    83  		fallthrough
    84  	case 'a':
    85  		// array case
    86  		cursor++
    87  	default:
    88  		return 0, errors.ErrUnexpectedStart("map", buf, cursor)
    89  	}
    90  
    91  	l, end, err := readLength(buf, cursor)
    92  	if err != nil {
    93  		return 0, err
    94  	}
    95  
    96  	cursor = end
    97  	if buf[cursor] != '{' {
    98  		return 0, errors.ErrExpected("{ character for map value", cursor)
    99  	}
   100  
   101  	mapValue := *(*unsafe.Pointer)(p)
   102  	if mapValue == nil {
   103  		mapValue = runtime.MakeMap(d.mapType, int(l))
   104  	}
   105  
   106  	cursor++
   107  	if buf[cursor] == '}' {
   108  		**(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue
   109  		cursor++
   110  		return cursor, nil
   111  	}
   112  
   113  	for {
   114  		k := unsafe_New(d.keyType)
   115  		keyCursor, err := d.keyDecoder.Decode(ctx, cursor, depth, k)
   116  		if err != nil {
   117  			return 0, err
   118  		}
   119  		cursor = keyCursor
   120  		v := unsafe_New(d.valueType)
   121  		valueCursor, err := d.valueDecoder.Decode(ctx, cursor, depth, v)
   122  		if err != nil {
   123  			return 0, err
   124  		}
   125  
   126  		d.mapassign(d.mapType, mapValue, k, v)
   127  		cursor = valueCursor
   128  		if buf[cursor] == '}' {
   129  			**(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue
   130  			cursor++
   131  			return cursor, nil
   132  		}
   133  	}
   134  }