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  }