github.com/v2pro/plz@v0.0.0-20221028024117-e5f9aec5b631/dump/test/map_test.go (about)

     1  package test
     2  
     3  import (
     4  	"testing"
     5  	"github.com/v2pro/plz/test"
     6  	"github.com/v2pro/plz/countlog"
     7  	"github.com/v2pro/plz/test/must"
     8  	"github.com/v2pro/plz/dump"
     9  	"reflect"
    10  	"fmt"
    11  	"unsafe"
    12  )
    13  
    14  type nameOff int32 // offset to a name
    15  type typeOff int32 // offset to an *rtype
    16  type textOff int32 // offset from top of text section
    17  
    18  // a copy of runtime.typeAlg
    19  type typeAlg struct {
    20  	// function for hashing objects of this type
    21  	// (ptr to object, seed) -> hash
    22  	hash func(unsafe.Pointer, uintptr) uintptr
    23  	// function for comparing objects of this type
    24  	// (ptr to object A, ptr to object B) -> ==?
    25  	equal func(unsafe.Pointer, unsafe.Pointer) bool
    26  }
    27  
    28  // tflag is used by an rtype to signal what extra type information is
    29  // available in the memory directly following the rtype value.
    30  //
    31  // tflag values must be kept in sync with copies in:
    32  //	cmd/compile/internal/gc/reflect.go
    33  //	cmd/link/internal/ld/decodesym.go
    34  //	runtime/type.go
    35  type tflag uint8
    36  
    37  // rtype is the common implementation of most values.
    38  // It is embedded in other, public struct types, but always
    39  // with a unique tag like `reflect:"array"` or `reflect:"ptr"`
    40  // so that code cannot convert from, say, *arrayType to *ptrType.
    41  //
    42  // rtype must be kept in sync with ../runtime/type.go:/^type._type.
    43  type rtype struct {
    44  	size       uintptr
    45  	ptrdata    uintptr  // number of bytes in the type that can contain pointers
    46  	hash       uint32   // hash of type; avoids computation in hash tables
    47  	tflag      tflag    // extra type information flags
    48  	align      uint8    // alignment of variable with this type
    49  	fieldAlign uint8    // alignment of struct field with this type
    50  	kind       uint8    // enumeration for C
    51  	alg        *typeAlg // algorithm table
    52  	gcdata     *byte    // garbage collection data
    53  	str        nameOff  // string form
    54  	ptrToThis  typeOff  // type for pointer to this type, may be zero
    55  }
    56  
    57  type iface struct {
    58  	itab unsafe.Pointer
    59  	data unsafe.Pointer
    60  }
    61  
    62  // mapType represents a map type.
    63  type mapType struct {
    64  	rtype         `reflect:"map"`
    65  	key           *rtype // map key type
    66  	elem          *rtype // map element (value) type
    67  	bucket        *rtype // internal bucket structure
    68  	hmap          *rtype // internal map header
    69  	keysize       uint8  // size of key slot
    70  	indirectkey   uint8  // store ptr to key instead of key itself
    71  	valuesize     uint8  // size of value slot
    72  	indirectvalue uint8  // store ptr to value instead of value itself
    73  	bucketsize    uint16 // size of bucket
    74  	reflexivekey  bool   // true if k==k for all keys
    75  	needkeyupdate bool   // true if we need to update key on an overwrite
    76  }
    77  
    78  func Test_map(t *testing.T) {
    79  	t.Run("map int to int", test.Case(func(ctx *countlog.Context) {
    80  		must.JsonEqual(`{
    81  		"__root__": {
    82  			"type": "map[int]int",
    83  			"data": {
    84  				"__ptr__": "{ptr1}"
    85  			}
    86  		},
    87  		"{ptr1}": {
    88  			"count": 2,
    89  			"flags": 0,
    90  			"B": 0,
    91  			"noverflow": 0,
    92  			"hash0": "{ANYTHING}",
    93  			"buckets": {"__ptr__":"{ptr2}"},
    94  			"oldbuckets": {"__ptr__":"0"},
    95  			"nevacuate": 0,
    96  			"extra": {"__ptr__":"0"}
    97  		},
    98  		"{ptr2}": [{
    99  			"tophash": "{ANYTHING}",
   100  			"keys": [9,8,0,0,0,0,0,0],
   101  			"elems": [7,6,0,0,0,0,0,0]
   102  		}]}`, dump.Var{map[int]int{
   103  			9: 7,
   104  			8: 6,
   105  		}}.String())
   106  	}))
   107  	t.Run("map string to string", test.Case(func(ctx *countlog.Context) {
   108  		must.JsonEqual(`{
   109  		"__root__": {
   110  			"type": "map[string]string",
   111  			"data": {
   112  				"__ptr__": "{ptr1}"
   113  			}
   114  		},
   115  		"{ptr1}": {
   116  			"count": 2,
   117  			"flags": 0,
   118  			"B": 0,
   119  			"noverflow": 0,
   120  			"hash0": "{ANYTHING}",
   121  			"buckets": {"__ptr__":"{ptr2}"},
   122  			"oldbuckets": {"__ptr__":"0"},
   123  			"nevacuate": 0,
   124  			"extra": {"__ptr__":"0"}
   125  		},
   126  		"{ptr2}": [{
   127  			"tophash": "{ANYTHING}",
   128  			"keys": [
   129  				{"data":{"__ptr__":"{key1}"},"len":1},
   130  				{"data":{"__ptr__":"{key2}"},"len":1},
   131  				{"data":{"__ptr__":"0"},"len":0},
   132  				{"data":{"__ptr__":"0"},"len":0},
   133  				{"data":{"__ptr__":"0"},"len":0},
   134  				{"data":{"__ptr__":"0"},"len":0},
   135  				{"data":{"__ptr__":"0"},"len":0},
   136  				{"data":{"__ptr__":"0"},"len":0}
   137  			],
   138  			"elems": [
   139  				{"data":{"__ptr__":"{elem1}"},"len":1},
   140  				{"data":{"__ptr__":"{elem2}"},"len":1},
   141  				{"data":{"__ptr__":"0"},"len":0},
   142  				{"data":{"__ptr__":"0"},"len":0},
   143  				{"data":{"__ptr__":"0"},"len":0},
   144  				{"data":{"__ptr__":"0"},"len":0},
   145  				{"data":{"__ptr__":"0"},"len":0},
   146  				{"data":{"__ptr__":"0"},"len":0}
   147  			]
   148  		}],
   149  		"{key1}":"a",
   150  		"{key2}":"c",
   151  		"{elem1}":"b",
   152  		"{elem2}":"d"
   153  		}`, dump.Var{map[string]string{
   154  			"a": "b",
   155  			"c": "d",
   156  		}}.String())
   157  	}))
   158  	t.Run("map type", test.Case(func(ctx *countlog.Context) {
   159  		m := map[int]*int{}
   160  		mType := reflect.TypeOf(m)
   161  		mIFace := (*iface)(unsafe.Pointer(&mType))
   162  		mapType := (*mapType)(mIFace.data)
   163  		fmt.Println(mapType.bucket.ptrdata)
   164  	}))
   165  }