github.com/fjl/memsize@v0.0.2/type.go (about) 1 package memsize 2 3 import ( 4 "fmt" 5 "reflect" 6 ) 7 8 // address is a memory location. 9 // 10 // Code dealing with uintptr is oblivious to the zero address. 11 // Code dealing with address is not: it treats the zero address 12 // as invalid. Offsetting an invalid address doesn't do anything. 13 // 14 // This distinction is useful because there are objects that we can't 15 // get the pointer to. 16 type address uintptr 17 18 const invalidAddr = address(0) 19 20 func (a address) valid() bool { 21 return a != 0 22 } 23 24 func (a address) addOffset(off uintptr) address { 25 if !a.valid() { 26 return invalidAddr 27 } 28 return a + address(off) 29 } 30 31 func (a address) String() string { 32 if uintptrBits == 32 { 33 return fmt.Sprintf("%#0.8x", uintptr(a)) 34 } 35 return fmt.Sprintf("%#0.16x", uintptr(a)) 36 } 37 38 type typCache map[reflect.Type]typInfo 39 40 type typInfo struct { 41 isPointer bool 42 needScan bool 43 } 44 45 // isPointer returns true for pointer-ish values. The notion of 46 // pointer includes everything but plain values, i.e. slices, maps 47 // channels, interfaces are 'pointer', too. 48 func (tc *typCache) isPointer(typ reflect.Type) bool { 49 return tc.info(typ).isPointer 50 } 51 52 // needScan reports whether a value of the type needs to be scanned 53 // recursively because it may contain pointers. 54 func (tc *typCache) needScan(typ reflect.Type) bool { 55 return tc.info(typ).needScan 56 } 57 58 func (tc *typCache) info(typ reflect.Type) typInfo { 59 info, found := (*tc)[typ] 60 switch { 61 case found: 62 return info 63 case isPointer(typ): 64 info = typInfo{true, true} 65 default: 66 info = typInfo{false, tc.checkNeedScan(typ)} 67 } 68 (*tc)[typ] = info 69 return info 70 } 71 72 func (tc *typCache) checkNeedScan(typ reflect.Type) bool { 73 switch k := typ.Kind(); k { 74 case reflect.Struct: 75 // Structs don't need scan if none of their fields need it. 76 for i := 0; i < typ.NumField(); i++ { 77 if tc.needScan(typ.Field(i).Type) { 78 return true 79 } 80 } 81 case reflect.Array: 82 // Arrays don't need scan if their element type doesn't. 83 return tc.needScan(typ.Elem()) 84 } 85 return false 86 } 87 88 func isPointer(typ reflect.Type) bool { 89 k := typ.Kind() 90 switch { 91 case k <= reflect.Complex128: 92 return false 93 case k == reflect.Array: 94 return false 95 case k >= reflect.Chan && k <= reflect.String: 96 return true 97 case k == reflect.Struct || k == reflect.UnsafePointer: 98 return false 99 default: 100 unhandledKind(k) 101 return false 102 } 103 } 104 105 func unhandledKind(k reflect.Kind) { 106 panic("unhandled kind " + k.String()) 107 } 108 109 // HumanSize formats the given number of bytes as a readable string. 110 func HumanSize(bytes uintptr) string { 111 switch { 112 case bytes < 1024: 113 return fmt.Sprintf("%d B", bytes) 114 case bytes < 1024*1024: 115 return fmt.Sprintf("%.3f KB", float64(bytes)/1024) 116 default: 117 return fmt.Sprintf("%.3f MB", float64(bytes)/1024/1024) 118 } 119 }