github.com/scottcagno/storage@v1.8.0/pkg/util/memory.go (about)

     1  package util
     2  
     3  import (
     4  	"reflect"
     5  	"sync"
     6  )
     7  
     8  var processingPool = sync.Pool{
     9  	New: func() interface{} {
    10  		return []reflect.Value{}
    11  	},
    12  }
    13  
    14  func isNativeType(k reflect.Kind) bool {
    15  	switch k {
    16  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
    17  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
    18  		reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
    19  		return true
    20  	}
    21  	return false
    22  }
    23  
    24  // Sizeof returns the estimated memory usage of object(s) not just the size of the type.
    25  // On 64bit Sizeof("test") == 12 (8 = sizeof(StringHeader) + 4 bytes).
    26  func Sizeof(objs ...interface{}) (sz uint64) {
    27  	refmap := make(map[uintptr]bool)
    28  	processing := processingPool.Get().([]reflect.Value)[:0]
    29  	for i := range objs {
    30  		processing = append(processing, reflect.ValueOf(objs[i]))
    31  	}
    32  
    33  	for len(processing) > 0 {
    34  		var val reflect.Value
    35  		val, processing = processing[len(processing)-1], processing[:len(processing)-1]
    36  		if !val.IsValid() {
    37  			continue
    38  		}
    39  
    40  		if val.CanAddr() {
    41  			refmap[val.Addr().Pointer()] = true
    42  		}
    43  
    44  		typ := val.Type()
    45  
    46  		sz += uint64(typ.Size())
    47  
    48  		switch val.Kind() {
    49  		case reflect.Ptr:
    50  			if val.IsNil() {
    51  				break
    52  			}
    53  			if refmap[val.Pointer()] {
    54  				break
    55  			}
    56  
    57  			fallthrough
    58  		case reflect.Interface:
    59  			processing = append(processing, val.Elem())
    60  
    61  		case reflect.Struct:
    62  			sz -= uint64(typ.Size())
    63  			for i := 0; i < val.NumField(); i++ {
    64  				processing = append(processing, val.Field(i))
    65  			}
    66  
    67  		case reflect.Array:
    68  			if isNativeType(typ.Elem().Kind()) {
    69  				break
    70  			}
    71  			sz -= uint64(typ.Size())
    72  			for i := 0; i < val.Len(); i++ {
    73  				processing = append(processing, val.Index(i))
    74  			}
    75  		case reflect.Slice:
    76  			el := typ.Elem()
    77  			if isNativeType(el.Kind()) {
    78  				sz += uint64(val.Len()) * uint64(el.Size())
    79  				break
    80  			}
    81  			for i := 0; i < val.Len(); i++ {
    82  				processing = append(processing, val.Index(i))
    83  			}
    84  		case reflect.Map:
    85  			if val.IsNil() {
    86  				break
    87  			}
    88  			kel, vel := typ.Key(), typ.Elem()
    89  			if isNativeType(kel.Kind()) && isNativeType(vel.Kind()) {
    90  				sz += uint64(kel.Size()+vel.Size()) * uint64(val.Len())
    91  				break
    92  			}
    93  			keys := val.MapKeys()
    94  			for i := 0; i < len(keys); i++ {
    95  				processing = append(processing, keys[i])
    96  				processing = append(processing, val.MapIndex(keys[i]))
    97  			}
    98  		case reflect.String:
    99  			sz += uint64(val.Len())
   100  		}
   101  	}
   102  	processingPool.Put(processing)
   103  	return
   104  }