github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/internal/unsafeheader/unsafeheader_test.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package unsafeheader 6 7 import ( 8 "bytes" 9 "reflect" 10 "testing" 11 "unsafe" 12 ) 13 14 // TestTypeMatchesReflectType ensures that the name and layout of the 15 // unsafeheader types matches the corresponding Header types in the reflect 16 // package. 17 func TestTypeMatchesReflectType(t *testing.T) { 18 t.Run("SliceHeader", func(t *testing.T) { 19 testHeaderMatchesReflect(t, SliceHeader{}, reflect.SliceHeader{}) 20 }) 21 22 t.Run("StringHeader", func(t *testing.T) { 23 testHeaderMatchesReflect(t, StringHeader{}, reflect.StringHeader{}) 24 }) 25 } 26 27 func testHeaderMatchesReflect(t *testing.T, header, reflectHeader any) { 28 h := reflect.TypeOf(header) 29 rh := reflect.TypeOf(reflectHeader) 30 31 for i := 0; i < h.NumField(); i++ { 32 f := h.Field(i) 33 rf, ok := rh.FieldByName(f.Name) 34 if !ok { 35 t.Errorf("Field %d of %v is named %s, but no such field exists in %v", i, h, f.Name, rh) 36 continue 37 } 38 if !typeCompatible(f.Type, rf.Type) { 39 t.Errorf("%v.%s has type %v, but %v.%s has type %v", h, f.Name, f.Type, rh, rf.Name, rf.Type) 40 } 41 if f.Offset != rf.Offset { 42 t.Errorf("%v.%s has offset %d, but %v.%s has offset %d", h, f.Name, f.Offset, rh, rf.Name, rf.Offset) 43 } 44 } 45 46 if h.NumField() != rh.NumField() { 47 t.Errorf("%v has %d fields, but %v has %d", h, h.NumField(), rh, rh.NumField()) 48 } 49 if h.Align() != rh.Align() { 50 t.Errorf("%v has alignment %d, but %v has alignment %d", h, h.Align(), rh, rh.Align()) 51 } 52 } 53 54 var ( 55 unsafePointerType = reflect.TypeOf(unsafe.Pointer(nil)) 56 uintptrType = reflect.TypeOf(uintptr(0)) 57 ) 58 59 func typeCompatible(t, rt reflect.Type) bool { 60 return t == rt || (t == unsafePointerType && rt == uintptrType) 61 } 62 63 // TestWriteThroughHeader ensures that the headers in the unsafeheader package 64 // can successfully mutate variables of the corresponding built-in types. 65 // 66 // This test is expected to fail under -race (which implicitly enables 67 // -d=checkptr) if the runtime views the header types as incompatible with the 68 // underlying built-in types. 69 func TestWriteThroughHeader(t *testing.T) { 70 t.Run("SliceHeader", func(t *testing.T) { 71 s := []byte("Hello, checkptr!")[:5] 72 73 var alias []byte 74 hdr := (*SliceHeader)(unsafe.Pointer(&alias)) 75 hdr.Data = unsafe.Pointer(&s[0]) 76 hdr.Cap = cap(s) 77 hdr.Len = len(s) 78 79 if !bytes.Equal(alias, s) { 80 t.Errorf("alias of %T(%q) constructed via SliceHeader = %T(%q)", s, s, alias, alias) 81 } 82 if cap(alias) != cap(s) { 83 t.Errorf("alias of %T with cap %d has cap %d", s, cap(s), cap(alias)) 84 } 85 }) 86 87 t.Run("StringHeader", func(t *testing.T) { 88 s := "Hello, checkptr!" 89 90 var alias string 91 hdr := (*StringHeader)(unsafe.Pointer(&alias)) 92 hdr.Data = (*StringHeader)(unsafe.Pointer(&s)).Data 93 hdr.Len = len(s) 94 95 if alias != s { 96 t.Errorf("alias of %q constructed via StringHeader = %q", s, alias) 97 } 98 }) 99 }