quorum/vendor/github.com/fjl/memsize/type.go

120 lines
2.7 KiB
Go

package memsize
import (
"fmt"
"reflect"
)
// address is a memory location.
//
// Code dealing with uintptr is oblivious to the zero address.
// Code dealing with address is not: it treats the zero address
// as invalid. Offsetting an invalid address doesn't do anything.
//
// This distinction is useful because there are objects that we can't
// get the pointer to.
type address uintptr
const invalidAddr = address(0)
func (a address) valid() bool {
return a != 0
}
func (a address) addOffset(off uintptr) address {
if !a.valid() {
return invalidAddr
}
return a + address(off)
}
func (a address) String() string {
if uintptrBits == 32 {
return fmt.Sprintf("%#0.8x", uintptr(a))
}
return fmt.Sprintf("%#0.16x", uintptr(a))
}
type typCache map[reflect.Type]typInfo
type typInfo struct {
isPointer bool
needScan bool
}
// isPointer returns true for pointer-ish values. The notion of
// pointer includes everything but plain values, i.e. slices, maps
// channels, interfaces are 'pointer', too.
func (tc *typCache) isPointer(typ reflect.Type) bool {
return tc.info(typ).isPointer
}
// needScan reports whether a value of the type needs to be scanned
// recursively because it may contain pointers.
func (tc *typCache) needScan(typ reflect.Type) bool {
return tc.info(typ).needScan
}
func (tc *typCache) info(typ reflect.Type) typInfo {
info, found := (*tc)[typ]
switch {
case found:
return info
case isPointer(typ):
info = typInfo{true, true}
default:
info = typInfo{false, tc.checkNeedScan(typ)}
}
(*tc)[typ] = info
return info
}
func (tc *typCache) checkNeedScan(typ reflect.Type) bool {
switch k := typ.Kind(); k {
case reflect.Struct:
// Structs don't need scan if none of their fields need it.
for i := 0; i < typ.NumField(); i++ {
if tc.needScan(typ.Field(i).Type) {
return true
}
}
case reflect.Array:
// Arrays don't need scan if their element type doesn't.
return tc.needScan(typ.Elem())
}
return false
}
func isPointer(typ reflect.Type) bool {
k := typ.Kind()
switch {
case k <= reflect.Complex128:
return false
case k == reflect.Array:
return false
case k >= reflect.Chan && k <= reflect.String:
return true
case k == reflect.Struct || k == reflect.UnsafePointer:
return false
default:
unhandledKind(k)
return false
}
}
func unhandledKind(k reflect.Kind) {
panic("unhandled kind " + k.String())
}
// HumanSize formats the given number of bytes as a readable string.
func HumanSize(bytes uintptr) string {
switch {
case bytes < 1024:
return fmt.Sprintf("%d B", bytes)
case bytes < 1024*1024:
return fmt.Sprintf("%.3f KB", float64(bytes)/1024)
default:
return fmt.Sprintf("%.3f MB", float64(bytes)/1024/1024)
}
}