github.com/moontrade/nogc@v0.1.7/alloc/tlsf/heap.go (about)

     1  package tlsf
     2  
     3  import (
     4  	"math"
     5  	"math/bits"
     6  	"unsafe"
     7  )
     8  
     9  // Heap === Heap (Two-Level Segregate Fit) memory allocator ===
    10  //
    11  // Heap is a general purpose dynamic memory allocator specifically designed to meet
    12  // real-time requirements:
    13  //
    14  // 		Bounded Response Time - The worst-case execution time (WCET) of memory allocation
    15  //								and deallocation Has got to be known in advance and be
    16  //								independent of application data. Allocator Has a constant
    17  //								cost O(1).
    18  //
    19  //						 Fast - Additionally to a bounded cost, the allocator Has to be
    20  //								efficient and fast enough. Allocator executes a maximum
    21  //								of 168 processor instructions in a x86 architecture.
    22  //								Depending on the compiler version and optimisation flags,
    23  //								it can be slightly lower or higher.
    24  //
    25  // 		Efficient Memory Use - 	Traditionally, real-time systems run for long periods of
    26  //								time and some (embedded applications), have strong constraints
    27  //								of memory size. Fragmentation can have a significant impact on
    28  //								such systems. It can increase  dramatically, and degrade the
    29  //								system performance. A way to measure this efficiency is the
    30  //								memory fragmentation incurred by the allocator. Allocator has
    31  //								been tested in hundreds of different loads (real-time tasks,
    32  //								general purpose applications, etc.) obtaining an average
    33  //								fragmentation lower than 15 %. The maximum fragmentation
    34  //								measured is lower than 25%.
    35  //
    36  // Memory can be added on demand and is a multiple of 64kb pages. Grow is used to allocate new
    37  // memory to be added to the allocator. Each Grow must provide a contiguous chunk of memory.
    38  // However, the allocator may be comprised of many contiguous chunks which are not contiguous
    39  // of each other. There is not a mechanism for shrinking the memory. Supplied Grow function
    40  // can effectively limit how big the allocator can get. If a zero pointer is returned it will
    41  // cause an out-of-memory situation which is propagated as a nil pointer being returned from
    42  // Alloc. It's up to the application to decide how to handle such scenarios.
    43  //
    44  // see: http://www.gii.upv.es/tlsf/
    45  // see: https://github.com/AssemblyScript/assemblyscript
    46  //
    47  // - `ffs(x)` is equivalent to `ctz(x)` with x != 0
    48  // - `fls(x)` is equivalent to `sizeof(x) * 8 - clz(x) - 1`
    49  //
    50  // ╒══════════════ Block size interpretation (32-bit) ═════════════╕
    51  //    3                   2                   1
    52  //  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0  bits
    53  // ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┴─┴─┴─╫─┴─┴─┴─┤
    54  // │ |                    FL                       │ SB = SL + AL  │ ◄─ usize
    55  // └───────────────────────────────────────────────┴───────╨───────┘
    56  // FL: first level, SL: second level, AL: alignment, SB: small block
    57  type Heap struct {
    58  	root      *root
    59  	HeapStart uintptr
    60  	HeapEnd   uintptr
    61  	arena     uintptr
    62  	Grow      Grow
    63  	Slot      uint8
    64  	Stats
    65  }
    66  
    67  // Stats provides the metrics of an Allocator
    68  type Stats struct {
    69  	HeapSize        int64
    70  	AllocSize       int64
    71  	PeakAllocSize   int64
    72  	FreeSize        int64
    73  	Allocs          int32
    74  	InitialPages    int32
    75  	ConsecutiveLow  int32
    76  	ConsecutiveHigh int32
    77  	Pages           int32
    78  	Grows           int32
    79  	fragmentation   float32
    80  }
    81  
    82  func (s *Stats) Fragmentation() float32 {
    83  	if s.HeapSize == 0 || s.PeakAllocSize == 0 {
    84  		return 0
    85  	}
    86  	pct := float64(s.HeapSize-s.PeakAllocSize) / float64(s.HeapSize)
    87  	s.fragmentation = float32(math.Floor(pct*100) / 100)
    88  	return s.fragmentation
    89  }
    90  
    91  // Grow provides the ability to Grow the heap and allocate a contiguous
    92  // chunk of system memory to add to the allocator.
    93  type Grow func(pagesBefore, pagesNeeded int32, minSize uintptr) (pagesAdded int32, start, end uintptr)
    94  
    95  const (
    96  	PageSize = uintptr(64 * 1024)
    97  
    98  	_TLSFAlignU32 = 2
    99  	// All allocation sizes and addresses are aligned to 4 or 8 bytes.
   100  	// 32bit = 2
   101  	// 64bit = 3
   102  	// <expr> = bits.UintSize / 8 / 4 + 1
   103  	_TLSFAlignSizeLog2 uintptr = ((32 << (^uint(0) >> 63)) / 8 / 4) + 1
   104  	_TLSFSizeofPointer         = unsafe.Sizeof(uintptr(0))
   105  
   106  	ALBits uint32  = 4 // 16 bytes to fit up to v128
   107  	ALSize uintptr = 1 << uintptr(ALBits)
   108  	ALMask         = ALSize - 1
   109  
   110  	// Overhead of a memory manager block.
   111  	BlockOverhead = unsafe.Sizeof(BLOCK{})
   112  	// Block constants. A block must have a minimum size of three pointers so it can hold `prev`,
   113  	// `prev` and `back` if free.
   114  	BlockMinSize = ((3*_TLSFSizeofPointer + BlockOverhead + ALMask) & ^ALMask) - BlockOverhead
   115  	// Maximum size of a memory manager block's payload.
   116  	BlockMaxSize = (1 << 30) - BlockOverhead
   117  	//BlockMaxSize = (1 << ((_TLSFAlignSizeLog2 + 1)*10)) - BlockOverhead
   118  
   119  	_TLSFDebug = false
   120  
   121  	_TLSFSLBits uint32 = 4
   122  	_TLSFSLSize uint32 = 1 << _TLSFSLBits
   123  	_TLSFSBBits        = _TLSFSLBits + ALBits
   124  	_TLSFSBSize uint32 = 1 << _TLSFSBBits
   125  	_TLSFFLBits        = 31 - _TLSFSBBits
   126  
   127  	// [00]: < 256B (SB)  [12]: < 1M
   128  	// [01]: < 512B       [13]: < 2M
   129  	// [02]: < 1K         [14]: < 4M
   130  	// [03]: < 2K         [15]: < 8M
   131  	// [04]: < 4K         [16]: < 16M
   132  	// [05]: < 8K         [17]: < 32M
   133  	// [06]: < 16K        [18]: < 64M
   134  	// [07]: < 32K        [19]: < 128M
   135  	// [08]: < 64K        [20]: < 256M
   136  	// [09]: < 128K       [21]: < 512M
   137  	// [10]: < 256K       [22]: <= 1G - OVERHEAD
   138  	// [11]: < 512K
   139  	// WASM VMs limit to 2GB total (currently), making one 1G block max
   140  	// (or three 512M etc.) due to block overhead
   141  
   142  	// Tags stored in otherwise unused alignment bits
   143  	_TLSFFREE     uintptr = 1 << 0
   144  	_TLSFLEFTFREE uintptr = 1 << 1
   145  	TagsMask              = _TLSFFREE | _TLSFLEFTFREE
   146  )
   147  
   148  // Alloc allocates a block of memory that fits the size provided
   149  //goland:noinspection GoVetUnsafePointer
   150  func (a *Heap) Alloc(size uintptr) uintptr {
   151  	if a == nil {
   152  		panic("nil")
   153  	}
   154  	p := uintptr(unsafe.Pointer(a.allocateBlock(size)))
   155  	if p == 0 {
   156  		return 0
   157  	}
   158  	p = p + BlockOverhead
   159  	return p
   160  }
   161  
   162  // AllocZeroed allocates a block of memory that fits the size provided
   163  //goland:noinspection GoVetUnsafePointer
   164  func (a *Heap) AllocZeroed(size uintptr) uintptr {
   165  	p := uintptr(unsafe.Pointer(a.allocateBlock(size)))
   166  	if p == 0 {
   167  		return 0
   168  	}
   169  	p = p + BlockOverhead
   170  	Zero(unsafe.Pointer(p), size)
   171  	return p
   172  }
   173  
   174  // Realloc determines the best way to resize an allocation.
   175  func (a *Heap) Realloc(ptr uintptr, size uintptr) uintptr {
   176  	p := uintptr(unsafe.Pointer(a.moveBlock(checkUsedBlock(ptr), size)))
   177  	if p == 0 {
   178  		return 0
   179  	}
   180  	return p + BlockOverhead
   181  }
   182  
   183  // Free release the allocation back into the free list.
   184  //goland:noinspection GoVetUnsafePointer
   185  func (a *Heap) Free(ptr uintptr) {
   186  	//println("Free", uint(ptr))
   187  	//a.freeBlock((*tlsfBlock)(unsafe.Pointer(ptr - BlockOverhead)))
   188  	a.freeBlock(checkUsedBlock(ptr))
   189  }
   190  
   191  //goland:noinspection GoVetUnsafePointer
   192  func SizeOf(ptr uintptr) uintptr {
   193  	return ((*tlsfBlock)(unsafe.Pointer(ptr - BlockOverhead))).MMInfo & ^TagsMask
   194  }
   195  
   196  // Bootstrap bootstraps the Allocator with the initial block of contiguous memory
   197  // that at least fits the minimum required to fit the bitmap.
   198  //goland:noinspection GoVetUnsafePointer
   199  func Bootstrap(start, end uintptr, pages int32, grow Grow) *Heap {
   200  	start = (start + unsafe.Alignof(unsafe.Pointer(nil)) - 1) &^ (unsafe.Alignof(unsafe.Pointer(nil)) - 1)
   201  
   202  	//if a.T {
   203  	//println("Bootstrap", "pages", pages, uint(start), uint(end), uint(end-start))
   204  	//}
   205  	// init allocator
   206  	a := (*Heap)(unsafe.Pointer(start))
   207  	*a = Heap{
   208  		HeapStart: start,
   209  		HeapEnd:   end,
   210  		Stats: Stats{
   211  			InitialPages: pages,
   212  			Pages:        pages,
   213  		},
   214  		Grow: grow,
   215  	}
   216  
   217  	// init root
   218  	rootOffset := unsafe.Sizeof(Heap{}) + ((start + ALMask) & ^ALMask)
   219  	a.root = (*root)(unsafe.Pointer(rootOffset))
   220  	a.root.init()
   221  
   222  	// add initial memory
   223  	a.addMemory(rootOffset+RootSize, end)
   224  	return a
   225  }
   226  
   227  // Memory manager
   228  
   229  // ╒════════════ Memory manager block layout (32-bit) ═════════════╕
   230  //    3                   2                   1
   231  //  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0  bits
   232  // ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤
   233  // │                           MM info                             │ -4
   234  // ╞>ptr═══════════════════════════════════════════════════════════╡
   235  // │                              ...                              │
   236  type BLOCK struct {
   237  	MMInfo uintptr
   238  }
   239  
   240  // ╒════════════════════ Block layout (32-bit) ════════════════════╕
   241  //    3                   2                   1
   242  //  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0  bits
   243  // ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┼─┤            ┐
   244  // │                          size                             │L│F│ ◄─┐ info   overhead
   245  // ╞>ptr═══════════════════════════════════════════════════════╧═╧═╡   │        ┘
   246  // │                        if free: ◄ prev                        │ ◄─┤ usize
   247  // ├───────────────────────────────────────────────────────────────┤   │
   248  // │                        if free: next ►                        │ ◄─┤
   249  // ├───────────────────────────────────────────────────────────────┤   │
   250  // │                             ...                               │   │ >= 0
   251  // ├───────────────────────────────────────────────────────────────┤   │
   252  // │                        if free: back ▲                        │ ◄─┘
   253  // └───────────────────────────────────────────────────────────────┘ >= MIN SIZE
   254  // F: FREE, L: LEFTFREE
   255  type tlsfBlock struct {
   256  	BLOCK
   257  	// Previous free block, if any. Only valid if free, otherwise part of payload.
   258  	//prev *Block
   259  	prev uintptr
   260  	// Next free block, if any. Only valid if free, otherwise part of payload.
   261  	//next *Block
   262  	next uintptr
   263  
   264  	// If the block is free, there is a 'back'reference at its end pointing at its start.
   265  }
   266  
   267  // Gets the left block of a block. Only valid if the left block is free.
   268  func (block *tlsfBlock) getFreeLeft() *tlsfBlock {
   269  	return *(**tlsfBlock)(unsafe.Pointer(uintptr(unsafe.Pointer(block)) - _TLSFSizeofPointer))
   270  }
   271  
   272  // Gets the right block of a block by advancing to the right by its size.
   273  func (block *tlsfBlock) getRight() *tlsfBlock {
   274  	return (*tlsfBlock)(unsafe.Pointer(uintptr(unsafe.Pointer(block)) + BlockOverhead + (block.MMInfo & ^TagsMask)))
   275  }
   276  
   277  // ╒═════════════════════ Root layout (32-bit) ════════════════════╕
   278  //    3                   2                   1
   279  //  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0  bits
   280  // ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤          ┐
   281  // │        0        |           flMap                            S│ ◄────┐
   282  // ╞═══════════════════════════════════════════════════════════════╡      │
   283  // │                           slMap[0] S                          │ ◄─┐  │
   284  // ├───────────────────────────────────────────────────────────────┤   │  │
   285  // │                           slMap[1]                            │ ◄─┤  │
   286  // ├───────────────────────────────────────────────────────────────┤  uint32 │
   287  // │                           slMap[22]                           │ ◄─┘  │
   288  // ╞═══════════════════════════════════════════════════════════════╡    usize
   289  // │                            head[0]                            │ ◄────┤
   290  // ├───────────────────────────────────────────────────────────────┤      │
   291  // │                              ...                              │ ◄────┤
   292  // ├───────────────────────────────────────────────────────────────┤      │
   293  // │                           head[367]                           │ ◄────┤
   294  // ╞═══════════════════════════════════════════════════════════════╡      │
   295  // │                             tail                              │ ◄────┘
   296  // └───────────────────────────────────────────────────────────────┘   SIZE   ┘
   297  // S: Small blocks map
   298  type root struct {
   299  	flMap uintptr
   300  }
   301  
   302  func (r *root) init() {
   303  	r.flMap = 0
   304  	r.setTail(nil)
   305  	for fl := uintptr(0); fl < uintptr(_TLSFFLBits); fl++ {
   306  		r.setSL(fl, 0)
   307  		for sl := uint32(0); sl < _TLSFSLSize; sl++ {
   308  			r.setHead(fl, sl, nil)
   309  		}
   310  	}
   311  }
   312  
   313  const (
   314  	SLStart  = _TLSFSizeofPointer
   315  	SLEnd    = SLStart + (uintptr(_TLSFFLBits) << _TLSFAlignU32)
   316  	HLStart  = (SLEnd + ALMask) &^ ALMask
   317  	HLEnd    = HLStart + uintptr(_TLSFFLBits)*uintptr(_TLSFSLSize)*_TLSFSizeofPointer
   318  	RootSize = HLEnd + _TLSFSizeofPointer
   319  )
   320  
   321  // Gets the second level map of the specified first level.
   322  func (r *root) getSL(fl uintptr) uint32 {
   323  	return *(*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(r)) + (fl << _TLSFAlignU32) + SLStart))
   324  }
   325  
   326  // Sets the second level map of the specified first level.
   327  func (r *root) setSL(fl uintptr, slMap uint32) {
   328  	*(*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(r)) + (fl << _TLSFAlignU32) + SLStart)) = slMap
   329  }
   330  
   331  // Gets the head of the free list for the specified combination of first and second level.
   332  func (r *root) getHead(fl uintptr, sl uint32) *tlsfBlock {
   333  	return *(**tlsfBlock)(unsafe.Pointer(uintptr(unsafe.Pointer(r)) + HLStart +
   334  		(((fl << _TLSFSLBits) + uintptr(sl)) << _TLSFAlignSizeLog2)))
   335  }
   336  
   337  // Sets the head of the free list for the specified combination of first and second level.
   338  func (r *root) setHead(fl uintptr, sl uint32, head *tlsfBlock) {
   339  	*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(r)) + HLStart +
   340  		(((fl << _TLSFSLBits) + uintptr(sl)) << _TLSFAlignSizeLog2))) = uintptr(unsafe.Pointer(head))
   341  }
   342  
   343  // Gets the tail block.
   344  func (r *root) getTail() *tlsfBlock {
   345  	return *(**tlsfBlock)(unsafe.Pointer(uintptr(unsafe.Pointer(r)) + HLEnd))
   346  }
   347  
   348  // Sets the tail block.
   349  func (r *root) setTail(tail *tlsfBlock) {
   350  	*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(r)) + HLEnd)) = uintptr(unsafe.Pointer(tail))
   351  }
   352  
   353  // Inserts a previously used block back into the free list.
   354  func (a *Heap) insertBlock(block *tlsfBlock) {
   355  	var (
   356  		r         = a.root
   357  		blockInfo = block.MMInfo
   358  		right     = block.getRight()
   359  		rightInfo = right.MMInfo
   360  	)
   361  	//(blockInfo & FREE)
   362  
   363  	// merge with right block if also free
   364  	if rightInfo&_TLSFFREE != 0 {
   365  		a.removeBlock(right)
   366  		blockInfo = blockInfo + BlockOverhead + (rightInfo & ^TagsMask) // keep block tags
   367  		block.MMInfo = blockInfo
   368  		right = block.getRight()
   369  		rightInfo = right.MMInfo
   370  		// 'back' is Add below
   371  	}
   372  
   373  	// merge with left block if also free
   374  	if blockInfo&_TLSFLEFTFREE != 0 {
   375  		left := block.getFreeLeft()
   376  		leftInfo := left.MMInfo
   377  		if _TLSFDebug {
   378  			assert(leftInfo&_TLSFFREE != 0, "must be free according to right tags")
   379  		}
   380  		a.removeBlock(left)
   381  		block = left
   382  		blockInfo = leftInfo + BlockOverhead + (blockInfo & ^TagsMask) // keep left tags
   383  		block.MMInfo = blockInfo
   384  		// 'back' is Add below
   385  	}
   386  
   387  	right.MMInfo = rightInfo | _TLSFLEFTFREE
   388  	// reference to right is no longer used now, hence rightInfo is not synced
   389  
   390  	// we now know the size of the block
   391  	size := blockInfo & ^TagsMask
   392  
   393  	// Add 'back' to itself at the end of block
   394  	*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(right)) - _TLSFSizeofPointer)) = uintptr(unsafe.Pointer(block))
   395  
   396  	// mapping_insert
   397  	var (
   398  		fl uintptr
   399  		sl uint32
   400  	)
   401  	if size < uintptr(_TLSFSBSize) {
   402  		fl = 0
   403  		sl = uint32(size >> ALBits)
   404  	} else {
   405  		const inv = _TLSFSizeofPointer*8 - 1
   406  		boundedSize := min(size, BlockMaxSize)
   407  		fl = inv - clz(boundedSize)
   408  		sl = uint32((boundedSize >> (fl - uintptr(_TLSFSLBits))) ^ (1 << _TLSFSLBits))
   409  		fl -= uintptr(_TLSFSBBits) - 1
   410  	}
   411  
   412  	// perform insertion
   413  	head := r.getHead(fl, sl)
   414  	block.prev = 0
   415  	block.next = uintptr(unsafe.Pointer(head))
   416  	if head != nil {
   417  		head.prev = uintptr(unsafe.Pointer(block))
   418  	}
   419  	r.setHead(fl, sl, block)
   420  
   421  	// update first and second level maps
   422  	r.flMap |= 1 << fl
   423  	r.setSL(fl, r.getSL(fl)|(1<<sl))
   424  }
   425  
   426  //goland:noinspection GoVetUnsafePointer
   427  func (a *Heap) removeBlock(block *tlsfBlock) {
   428  	r := a.root
   429  	blockInfo := block.MMInfo
   430  	if _TLSFDebug {
   431  		assert(blockInfo&_TLSFFREE != 0, "must be free")
   432  	}
   433  	size := blockInfo & ^TagsMask
   434  	if _TLSFDebug {
   435  		assert(size >= BlockMinSize, "must be valid")
   436  	}
   437  
   438  	// mapping_insert
   439  	var (
   440  		fl uintptr
   441  		sl uint32
   442  	)
   443  	if size < uintptr(_TLSFSBSize) {
   444  		fl = 0
   445  		sl = uint32(size >> ALBits)
   446  	} else {
   447  		const inv = _TLSFSizeofPointer*8 - 1
   448  		boundedSize := min(size, BlockMaxSize)
   449  		fl = inv - clz(boundedSize)
   450  		sl = uint32((boundedSize >> (fl - uintptr(_TLSFSLBits))) ^ (1 << uintptr(_TLSFSLBits)))
   451  		fl -= uintptr(_TLSFSBBits) - 1
   452  	}
   453  	if _TLSFDebug {
   454  		assert(fl < uintptr(_TLSFFLBits) && sl < _TLSFSLSize, "fl/sl out of range")
   455  	}
   456  
   457  	// link previous and prev free block
   458  	var (
   459  		prev = block.prev
   460  		next = block.next
   461  	)
   462  	if prev != 0 {
   463  		(*tlsfBlock)(unsafe.Pointer(prev)).next = next
   464  	}
   465  	if next != 0 {
   466  		(*tlsfBlock)(unsafe.Pointer(next)).prev = prev
   467  	}
   468  
   469  	// update head if we are removing it
   470  	if block == r.getHead(fl, sl) {
   471  		r.setHead(fl, sl, (*tlsfBlock)(unsafe.Pointer(next)))
   472  
   473  		// clear second level map if head is empty now
   474  		if next == 0 {
   475  			slMap := r.getSL(fl)
   476  			slMap &= ^(1 << sl)
   477  			r.setSL(fl, slMap)
   478  
   479  			// clear first level map if second level is empty now
   480  			if slMap == 0 {
   481  				r.flMap &= ^(1 << fl)
   482  			}
   483  		}
   484  	}
   485  	// note: does not alter left/back because it is likely that splitting
   486  	// is performed afterwards, invalidating those changes. so, the caller
   487  	// must perform those updates.
   488  }
   489  
   490  // Searches for a free block of at least the specified size.
   491  func (a *Heap) searchBlock(size uintptr) *tlsfBlock {
   492  	// mapping_search
   493  	var (
   494  		fl uintptr
   495  		sl uint32
   496  		r  = a.root
   497  	)
   498  	if size < uintptr(_TLSFSBSize) {
   499  		fl = 0
   500  		sl = uint32(size >> ALBits)
   501  	} else {
   502  		const (
   503  			halfMaxSize = BlockMaxSize >> 1 // don't round last fl
   504  			inv         = _TLSFSizeofPointer*8 - 1
   505  			invRound    = inv - uintptr(_TLSFSLBits)
   506  		)
   507  
   508  		var requestSize uintptr
   509  		if size < halfMaxSize {
   510  			requestSize = size + (1 << (invRound - clz(size))) - 1
   511  		} else {
   512  			requestSize = size
   513  		}
   514  
   515  		fl = inv - clz(requestSize)
   516  		sl = uint32((requestSize >> (fl - uintptr(_TLSFSLBits))) ^ (1 << _TLSFSLBits))
   517  		fl -= uintptr(_TLSFSBBits) - 1
   518  	}
   519  	if _TLSFDebug {
   520  		assert(fl < uintptr(_TLSFFLBits) && sl < _TLSFSLSize, "fl/sl out of range")
   521  	}
   522  
   523  	// search second level
   524  	var (
   525  		slMap = r.getSL(fl) & (^uint32(0) << sl)
   526  		head  *tlsfBlock
   527  	)
   528  	if slMap == 0 {
   529  		// search prev larger first level
   530  		flMap := r.flMap & (^uintptr(0) << (fl + 1))
   531  		if flMap == 0 {
   532  			head = nil
   533  		} else {
   534  			fl = ctz(flMap)
   535  			slMap = r.getSL(fl)
   536  			if _TLSFDebug {
   537  				assert(slMap != 0, "can't be zero if fl points here")
   538  			}
   539  			head = r.getHead(fl, ctz32(slMap))
   540  		}
   541  	} else {
   542  		head = r.getHead(fl, ctz32(slMap))
   543  	}
   544  
   545  	return head
   546  }
   547  
   548  func (a *Heap) prepareBlock(block *tlsfBlock, size uintptr) {
   549  	blockInfo := block.MMInfo
   550  	if _TLSFDebug {
   551  		assert(((size+BlockOverhead)&ALMask) == 0,
   552  			"size must be aligned so the New block is")
   553  	}
   554  	// split if the block can hold another MINSIZE block incl. overhead
   555  	remaining := (blockInfo & ^TagsMask) - size
   556  	if remaining >= BlockOverhead+BlockMinSize {
   557  		block.MMInfo = size | (blockInfo & _TLSFLEFTFREE) // also discards FREE
   558  
   559  		spare := (*tlsfBlock)(unsafe.Pointer(uintptr(unsafe.Pointer(block)) + BlockOverhead + size))
   560  		spare.MMInfo = (remaining - BlockOverhead) | _TLSFFREE // not LEFTFREE
   561  		a.insertBlock(spare)                                   // also sets 'back'
   562  
   563  		// otherwise tag block as no longer FREE and right as no longer LEFTFREE
   564  	} else {
   565  		block.MMInfo = blockInfo & ^_TLSFFREE
   566  		block.getRight().MMInfo &= ^_TLSFLEFTFREE
   567  	}
   568  }
   569  
   570  // growMemory grows the pool by a number of 64kb pages to fit the required size
   571  func (a *Heap) growMemory(size uintptr) bool {
   572  	if a.Grow == nil {
   573  		return false
   574  	}
   575  	// Here, both rounding performed in searchBlock ...
   576  	const halfMaxSize = BlockMaxSize >> 1
   577  	if size < halfMaxSize { // don't round last fl
   578  		const invRound = (_TLSFSizeofPointer*8 - 1) - uintptr(_TLSFSLBits)
   579  		size += (1 << (invRound - clz(size))) - 1
   580  	}
   581  	// and additional BLOCK_OVERHEAD must be taken into account. If we are going
   582  	// to merge with the tail block, that's one time, otherwise it's two times.
   583  	var (
   584  		pagesBefore         = a.Pages
   585  		offset      uintptr = 0
   586  	)
   587  	if BlockOverhead != uintptr(unsafe.Pointer(a.root.getTail())) {
   588  		offset = 1
   589  	}
   590  	size += BlockOverhead << ((uintptr(pagesBefore) << 16) - offset)
   591  	pagesNeeded := ((int32(size) + 0xffff) & ^0xffff) >> 16
   592  
   593  	addedPages, start, end := a.Grow(pagesBefore, pagesNeeded, size)
   594  	if start == 0 || end == 0 {
   595  		return false
   596  	}
   597  	if addedPages == 0 {
   598  		addedPages = int32((end - start) / PageSize)
   599  		if (end-start)%PageSize > 0 {
   600  			addedPages++
   601  		}
   602  	}
   603  	a.Pages += addedPages
   604  	a.HeapEnd = end
   605  	a.addMemory(start, end)
   606  	return true
   607  }
   608  
   609  // addMemory adds the newly allocated memory to the Allocator bitmaps
   610  //goland:noinspection GoVetUnsafePointer
   611  func (a *Heap) addMemory(start, end uintptr) bool {
   612  	if _TLSFDebug {
   613  		assert(start <= end, "start must be <= end")
   614  	}
   615  	start = ((start + BlockOverhead + ALMask) & ^ALMask) - BlockOverhead
   616  	end &= ^ALMask
   617  
   618  	var tail = a.root.getTail()
   619  	var tailInfo uintptr = 0
   620  	if tail != nil { // more memory
   621  		if _TLSFDebug {
   622  			assert(start >= uintptr(unsafe.Pointer(tail))+BlockOverhead, "out of bounds")
   623  		}
   624  
   625  		// merge with current tail if adjacent
   626  		const offsetToTail = ALSize
   627  		if start-offsetToTail == uintptr(unsafe.Pointer(tail)) {
   628  			start -= offsetToTail
   629  			tailInfo = tail.MMInfo
   630  		} else {
   631  			// We don't do this, but a user might `memory.Grow` manually
   632  			// leading to non-adjacent pages managed by Allocator.
   633  		}
   634  	} else if _TLSFDebug { // first memory
   635  		assert(start >= uintptr(unsafe.Pointer(a.root))+RootSize, "starts after root")
   636  	}
   637  
   638  	// check if size is large enough for a free block and the tail block
   639  	var size = end - start
   640  	if size < BlockOverhead+BlockMinSize+BlockOverhead {
   641  		return false
   642  	}
   643  
   644  	// left size is total minus its own and the zero-length tail's header
   645  	var (
   646  		leftSize = size - 2*BlockOverhead
   647  		left     = (*tlsfBlock)(unsafe.Pointer(start))
   648  	)
   649  	left.MMInfo = leftSize | _TLSFFREE | (tailInfo & _TLSFLEFTFREE)
   650  	left.prev = 0
   651  	left.next = 0
   652  
   653  	// tail is a zero-length used block
   654  	tail = (*tlsfBlock)(unsafe.Pointer(start + BlockOverhead + leftSize))
   655  	tail.MMInfo = 0 | _TLSFLEFTFREE
   656  	a.root.setTail(tail)
   657  
   658  	a.FreeSize += int64(leftSize)
   659  	a.HeapSize += int64(end - start)
   660  
   661  	// also merges with free left before tail / sets 'back'
   662  	a.insertBlock(left)
   663  
   664  	return true
   665  }
   666  
   667  // Computes the size (excl. header) of a block.
   668  func computeSize(size uintptr) uintptr {
   669  	// Size must be large enough and aligned minus preceeding overhead
   670  	if size <= BlockMinSize {
   671  		return BlockMinSize
   672  	} else {
   673  		return ((size + BlockOverhead + ALMask) & ^ALMask) - BlockOverhead
   674  	}
   675  }
   676  
   677  // Prepares and checks an allocation size.
   678  func prepareSize(size uintptr) uintptr {
   679  	if size > BlockMaxSize {
   680  		panic("allocation too large")
   681  	}
   682  	return computeSize(size)
   683  }
   684  
   685  // Allocates a block of the specified size.
   686  func (a *Heap) allocateBlock(size uintptr) *tlsfBlock {
   687  	var payloadSize = prepareSize(size)
   688  	var block = a.searchBlock(payloadSize)
   689  	if block == nil {
   690  		if !a.growMemory(payloadSize) {
   691  			return nil
   692  		}
   693  		block = a.searchBlock(payloadSize)
   694  		if _TLSFDebug {
   695  			assert(block != nil, "block must be found now")
   696  		}
   697  		if block == nil {
   698  			return nil
   699  		}
   700  	}
   701  	if _TLSFDebug {
   702  		assert((block.MMInfo & ^TagsMask) >= payloadSize, "must fit")
   703  	}
   704  
   705  	a.removeBlock(block)
   706  	a.prepareBlock(block, payloadSize)
   707  
   708  	// update stats
   709  	payloadSize = block.MMInfo & ^TagsMask
   710  	a.AllocSize += int64(payloadSize)
   711  	if a.AllocSize > a.PeakAllocSize {
   712  		a.PeakAllocSize = a.AllocSize
   713  	}
   714  	a.FreeSize -= int64(payloadSize)
   715  	a.Allocs++
   716  
   717  	// return block
   718  	return block
   719  }
   720  
   721  func (a *Heap) reallocateBlock(block *tlsfBlock, size uintptr) *tlsfBlock {
   722  	var (
   723  		payloadSize = prepareSize(size)
   724  		blockInfo   = block.MMInfo
   725  		blockSize   = blockInfo & ^TagsMask
   726  	)
   727  
   728  	// possibly split and update runtime size if it still fits
   729  	if payloadSize <= blockSize {
   730  		a.prepareBlock(block, payloadSize)
   731  		//if (isDefined(ASC_RTRACE)) {
   732  		//	if (payloadSize != blockSize) onresize(block, BLOCK_OVERHEAD + blockSize);
   733  		//}
   734  		return block
   735  	}
   736  
   737  	// merge with right free block if merger is large enough
   738  	var (
   739  		right     = block.getRight()
   740  		rightInfo = right.MMInfo
   741  	)
   742  	if rightInfo&_TLSFFREE != 0 {
   743  		mergeSize := blockSize + BlockOverhead + (rightInfo & ^TagsMask)
   744  		if mergeSize >= payloadSize {
   745  			a.removeBlock(right)
   746  			block.MMInfo = (blockInfo & TagsMask) | mergeSize
   747  			a.prepareBlock(block, payloadSize)
   748  			//if (isDefined(ASC_RTRACE)) onresize(block, BLOCK_OVERHEAD + blockSize);
   749  			return block
   750  		}
   751  	}
   752  
   753  	// otherwise, move the block
   754  	return a.moveBlock(block, size)
   755  }
   756  
   757  func (a *Heap) moveBlock(block *tlsfBlock, newSize uintptr) *tlsfBlock {
   758  	newBlock := a.allocateBlock(newSize)
   759  	if newBlock == nil {
   760  		return nil
   761  	}
   762  
   763  	Copy(unsafe.Pointer(uintptr(unsafe.Pointer(newBlock))+BlockOverhead),
   764  		unsafe.Pointer(uintptr(unsafe.Pointer(block))+BlockOverhead),
   765  		block.MMInfo & ^TagsMask)
   766  
   767  	a.freeBlock(block)
   768  	//maybeFreeBlock(a, block)
   769  
   770  	return newBlock
   771  }
   772  
   773  func (a *Heap) freeBlock(block *tlsfBlock) {
   774  	size := block.MMInfo & ^TagsMask
   775  	a.FreeSize += int64(size)
   776  	a.AllocSize -= int64(size)
   777  	a.Allocs--
   778  
   779  	block.MMInfo = block.MMInfo | _TLSFFREE
   780  	a.insertBlock(block)
   781  }
   782  
   783  func min(l, r uintptr) uintptr {
   784  	if l < r {
   785  		return l
   786  	}
   787  	return r
   788  }
   789  
   790  func clz(value uintptr) uintptr {
   791  	return uintptr(bits.LeadingZeros(uint(value)))
   792  }
   793  
   794  func ctz(value uintptr) uintptr {
   795  	return uintptr(bits.TrailingZeros(uint(value)))
   796  }
   797  
   798  func ctz32(value uint32) uint32 {
   799  	return uint32(bits.TrailingZeros32(value))
   800  }
   801  
   802  //goland:noinspection GoVetUnsafePointer
   803  func checkUsedBlock(ptr uintptr) *tlsfBlock {
   804  	block := (*tlsfBlock)(unsafe.Pointer(ptr - BlockOverhead))
   805  	if !(ptr != 0 && ((ptr & ALMask) == 0) && ((block.MMInfo & _TLSFFREE) == 0)) {
   806  		panic("used block is not valid to be freed or reallocated")
   807  	}
   808  	return block
   809  }
   810  
   811  func PrintDebugInfo() {
   812  	println("ALIGNOF_U32		", int64(_TLSFAlignU32))
   813  	println("ALIGN_SIZE_LOG2	", int64(_TLSFAlignSizeLog2))
   814  	println("U32_MAX			", ^uint32(0))
   815  	println("PTR_MAX			", ^uintptr(0))
   816  	println("AL_BITS			", int64(ALBits))
   817  	println("AL_SIZE			", int64(ALSize))
   818  	println("AL_MASK			", int64(ALMask))
   819  	println("BLOCK_OVERHEAD	", int64(BlockOverhead))
   820  	println("BLOCK_MAXSIZE	", int64(BlockMaxSize))
   821  	println("SL_BITS			", int64(_TLSFSLBits))
   822  	println("SL_SIZE			", int64(_TLSFSLSize))
   823  	println("SB_BITS			", int64(_TLSFSBBits))
   824  	println("SB_SIZE			", int64(_TLSFSBSize))
   825  	println("FL_BITS			", int64(_TLSFFLBits))
   826  	println("FREE			", int64(_TLSFFREE))
   827  	println("LEFTFREE		", int64(_TLSFLEFTFREE))
   828  	println("TAGS_MASK		", int64(TagsMask))
   829  	println("BLOCK_MINSIZE	", int64(BlockMinSize))
   830  	println("SL_START		", int64(SLStart))
   831  	println("SL_END			", int64(SLEnd))
   832  	println("HL_START		", int64(HLStart))
   833  	println("HL_END			", int64(HLEnd))
   834  	println("ROOT_SIZE		", int64(RootSize))
   835  }