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 }