github.com/fjl/memsize@v0.0.2/bitmap.go (about)

     1  package memsize
     2  
     3  import (
     4  	"math/bits"
     5  )
     6  
     7  const (
     8  	uintptrBits  = 32 << (uint64(^uintptr(0)) >> 63)
     9  	uintptrBytes = uintptrBits / 8
    10  	bmBlockRange = 1 * 1024 * 1024 // bytes covered by bmBlock
    11  	bmBlockWords = bmBlockRange / uintptrBits
    12  )
    13  
    14  // bitmap is a sparse bitmap.
    15  type bitmap struct {
    16  	blocks map[uintptr]*bmBlock
    17  }
    18  
    19  func newBitmap() *bitmap {
    20  	return &bitmap{make(map[uintptr]*bmBlock)}
    21  }
    22  
    23  // markRange sets n consecutive bits starting at addr.
    24  func (b *bitmap) markRange(addr, n uintptr) {
    25  	for end := addr + n; addr < end; {
    26  		block, baddr := b.block(addr)
    27  		for i := baddr; i < bmBlockRange && addr < end; i++ {
    28  			block.mark(i)
    29  			addr++
    30  		}
    31  	}
    32  }
    33  
    34  // isMarked returns the value of the bit at the given address.
    35  func (b *bitmap) isMarked(addr uintptr) bool {
    36  	block, baddr := b.block(addr)
    37  	return block.isMarked(baddr)
    38  }
    39  
    40  // countRange returns the number of set bits in the range [addr, addr+n].
    41  func (b *bitmap) countRange(addr, n uintptr) uintptr {
    42  	c := uintptr(0)
    43  	for end := addr + n; addr < end; {
    44  		block, baddr := b.block(addr)
    45  		bend := uintptr(bmBlockRange - 1)
    46  		if baddr+(end-addr) < bmBlockRange {
    47  			bend = baddr + (end - addr)
    48  		}
    49  		c += uintptr(block.count(baddr, bend))
    50  		// Move addr to next block.
    51  		addr += bmBlockRange - baddr
    52  	}
    53  	return c
    54  }
    55  
    56  // block finds the block corresponding to the given memory address.
    57  // It also returns the block's starting address.
    58  func (b *bitmap) block(addr uintptr) (*bmBlock, uintptr) {
    59  	index := addr / bmBlockRange
    60  	block := b.blocks[index]
    61  	if block == nil {
    62  		block = new(bmBlock)
    63  		b.blocks[index] = block
    64  	}
    65  	return block, addr % bmBlockRange
    66  }
    67  
    68  // size returns the sum of the byte sizes of all blocks.
    69  func (b *bitmap) size() uintptr {
    70  	return uintptr(len(b.blocks)) * bmBlockWords * uintptrBytes
    71  }
    72  
    73  // utilization returns the mean percentage of one bits across all blocks.
    74  func (b *bitmap) utilization() float32 {
    75  	var avg float32
    76  	for _, block := range b.blocks {
    77  		avg += float32(block.count(0, bmBlockRange-1)) / float32(bmBlockRange)
    78  	}
    79  	return avg / float32(len(b.blocks))
    80  }
    81  
    82  // bmBlock is a bitmap block.
    83  type bmBlock [bmBlockWords]uintptr
    84  
    85  // mark sets the i'th bit to one.
    86  func (b *bmBlock) mark(i uintptr) {
    87  	b[i/uintptrBits] |= 1 << (i % uintptrBits)
    88  }
    89  
    90  // isMarked returns the value of the i'th bit.
    91  func (b *bmBlock) isMarked(i uintptr) bool {
    92  	return (b[i/uintptrBits] & (1 << (i % uintptrBits))) != 0
    93  }
    94  
    95  // count returns the number of set bits in the range [start, end].
    96  func (b *bmBlock) count(start, end uintptr) (count int) {
    97  	br := b[start/uintptrBits : end/uintptrBits+1]
    98  	for i, w := range br {
    99  		if i == 0 {
   100  			w &= blockmask(start)
   101  		}
   102  		if i == len(br)-1 {
   103  			w &^= blockmask(end)
   104  		}
   105  		count += onesCountPtr(w)
   106  	}
   107  	return count
   108  }
   109  
   110  func blockmask(x uintptr) uintptr {
   111  	return ^uintptr(0) << (x % uintptrBits)
   112  }
   113  
   114  func onesCountPtr(x uintptr) int {
   115  	if uintptrBits == 64 {
   116  		return bits.OnesCount64(uint64(x))
   117  	}
   118  	return bits.OnesCount32(uint32(x))
   119  }