github.com/moontrade/unsafe@v0.9.1/memory/alloc_nocgo.go (about)

     1  //go:build !tinygo && !wasm && !wasi && !tinygo.wasm && !cgo
     2  
     3  package memory
     4  
     5  import (
     6  	"github.com/moontrade/unsafe/memory/tlsf"
     7  	"unsafe"
     8  )
     9  
    10  ////////////////////////////////////////////////////////////////////////////////////
    11  // Global Allocator
    12  ////////////////////////////////////////////////////////////////////////////////////
    13  
    14  var allocator = tlsf.NewHeap(1).ToSync()
    15  
    16  func HeapInstance() *tlsf.Sync {
    17  	return allocator
    18  }
    19  
    20  ////////////////////////////////////////////////////////////////////////////////////
    21  // Global allocator convenience
    22  ////////////////////////////////////////////////////////////////////////////////////
    23  
    24  // Alloc calls Alloc on the system allocator
    25  //
    26  //export malloc
    27  func Alloc(size uintptr) Pointer {
    28  	return Pointer(allocator.Alloc(size))
    29  }
    30  func AllocCap(size uintptr) (Pointer, uintptr) {
    31  	ptr := allocator.Alloc(size)
    32  	return Pointer(ptr), tlsf.SizeOf(ptr)
    33  }
    34  func AllocZeroed(size uintptr) Pointer {
    35  	return Pointer(allocator.AllocZeroed(size))
    36  }
    37  func AllocZeroedCap(size uintptr) (Pointer, uintptr) {
    38  	ptr := allocator.AllocZeroed(size)
    39  	return Pointer(ptr), tlsf.SizeOf(ptr)
    40  }
    41  
    42  // Alloc calls Alloc on the system allocator
    43  //
    44  //export calloc
    45  func Calloc(num, size uintptr) Pointer {
    46  	return Pointer(allocator.AllocZeroed(num * size))
    47  }
    48  func CallocCap(num, size uintptr) (Pointer, uintptr) {
    49  	ptr := allocator.AllocZeroed(num * size)
    50  	return Pointer(ptr), tlsf.SizeOf(ptr)
    51  }
    52  
    53  // Realloc calls Realloc on the system allocator
    54  //
    55  //export realloc
    56  func Realloc(p Pointer, size uintptr) Pointer {
    57  	return Pointer(allocator.Realloc(uintptr(p), size))
    58  }
    59  func ReallocCap(p Pointer, size uintptr) (Pointer, uintptr) {
    60  	newPtr := allocator.Realloc(uintptr(p), size)
    61  	return Pointer(newPtr), tlsf.SizeOf(newPtr)
    62  }
    63  
    64  // Free calls Free on the system allocator
    65  //
    66  //export free
    67  func Free(p Pointer) {
    68  	allocator.Free(uintptr(p))
    69  }
    70  
    71  // //export malloc_usable_size
    72  func Sizeof(p Pointer) uintptr {
    73  	return uintptr(tlsf.SizeOf(uintptr(p)))
    74  }
    75  
    76  func Scope(fn func(a AutoFree)) {
    77  	a := NewAuto(32)
    78  	defer a.Free()
    79  	fn(a)
    80  }
    81  
    82  //// Scope creates an AutoFree free list that automatically reclaims memory
    83  //// after callback finishes.
    84  //func (a *Heap) Scope(fn func(a AutoFree)) {
    85  //	auto := NewAuto(a.AsAllocator(), 32)
    86  //	fn(auto)
    87  //	auto.Free()
    88  //}
    89  
    90  ////////////////////////////////////////////////////////////////////////////////////
    91  // tinygo hooks
    92  ////////////////////////////////////////////////////////////////////////////////////
    93  
    94  const wasmPageSize = 64 * 1024
    95  
    96  func WASMMemorySize(index int32) int32 {
    97  	return wasm_memory_size(index)
    98  }
    99  
   100  func WASMMemorySizeBytes(index int32) int32 {
   101  	return wasm_memory_size(index) * wasmPageSize
   102  }
   103  
   104  //export llvm.wasm.memory.size.i32
   105  func wasm_memory_size(index int32) int32
   106  
   107  //export llvm.wasm.memory.grow.i32
   108  func wasm_memory_grow(index, pages int32) int32
   109  
   110  func growBy(pages int32) (uintptr, uintptr) {
   111  	before := wasm_memory_size(0)
   112  	wasm_memory_grow(0, pages)
   113  	after := wasm_memory_size(0)
   114  	if before == after {
   115  		return 0, 0
   116  	}
   117  	return uintptr(before * wasmPageSize), uintptr(after * wasmPageSize)
   118  }
   119  
   120  func allocatorPointer() uintptr {
   121  	return uintptr(unsafe.Pointer(allocator))
   122  }
   123  
   124  //go:linkname initAllocator runtime.initAllocator
   125  func initAllocator(heapStart, heapEnd uintptr) {
   126  	//println("initAllocator", uint(heapStart), uint(heapEnd))
   127  	//println("globals", uint(globalsStart), uint(globalsEnd))
   128  
   129  	// Did we get called twice?
   130  	if allocator != nil {
   131  		return
   132  	}
   133  
   134  	var (
   135  		pagesBefore = wasm_memory_size(0)
   136  		pagesAfter  = pagesBefore
   137  	)
   138  
   139  	// Need to add a page initially to fit minimum size required by allocator?
   140  	if heapStart == 0 || heapStart+unsafe.Sizeof(tlsf.Heap{})+tlsf.RootSize+tlsf.ALMask+16 >
   141  		uintptr(pagesBefore)*uintptr(wasmPageSize) {
   142  		// Just need a single page. Root size is much smaller than a single WASM page.
   143  		wasm_memory_grow(0, 1)
   144  		pagesAfter = wasm_memory_size(0)
   145  	}
   146  
   147  	// Bootstrap allocator which will take over all allocations from now on.
   148  	allocator = tlsf.Bootstrap(
   149  		heapStart,
   150  		uintptr(pagesAfter)*uintptr(wasmPageSize),
   151  		1,
   152  		GrowMin,
   153  	)
   154  }
   155  
   156  func SetGrow(grow tlsf.Grow) {
   157  	if allocator == nil {
   158  		initAllocator(0, 0)
   159  	}
   160  	if allocator != nil {
   161  		allocator.Grow = grow
   162  	}
   163  }
   164  
   165  // GrowByDouble will double the heap on each Grow
   166  func GrowByDouble(pagesBefore, pagesNeeded int32, minSize uintptr) (pagesAdded int32, start, end uintptr) {
   167  	if pagesBefore > pagesNeeded {
   168  		pagesAdded = pagesBefore
   169  	} else {
   170  		pagesAdded = pagesNeeded
   171  	}
   172  	start, end = growBy(pagesAdded)
   173  	if start == 0 {
   174  		pagesAdded = pagesNeeded
   175  		start, end = growBy(pagesAdded)
   176  		if start == 0 {
   177  			return 0, 0, 0
   178  		}
   179  	}
   180  	return
   181  }
   182  
   183  var (
   184  	growByPages int32 = 1
   185  )
   186  
   187  // GrowBy will Grow by the number of pages specified or by the minimum needed, whichever is greater.
   188  func GrowBy(pages int32) tlsf.Grow {
   189  	growByPages = pages
   190  	return doGrowByPages
   191  }
   192  
   193  func doGrowByPages(pagesBefore, pagesNeeded int32, minSize uintptr) (pagesAdded int32, start, end uintptr) {
   194  	if growByPages > pagesNeeded {
   195  		pagesAdded = growByPages
   196  	} else {
   197  		pagesAdded = pagesNeeded
   198  	}
   199  	start, end = growBy(pagesAdded)
   200  	if start == 0 {
   201  		pagesAdded = pagesNeeded
   202  		start, end = growBy(pagesAdded)
   203  		if start == 0 {
   204  			return 0, 0, 0
   205  		}
   206  	}
   207  	return
   208  }
   209  
   210  // GrowByMin will Grow by a single page or by the minimum needed, whichever is greater.
   211  func GrowMin(pagesBefore, pagesNeeded int32, minSize uintptr) (int32, uintptr, uintptr) {
   212  	start, end := growBy(pagesNeeded)
   213  	if start == 0 {
   214  		return 0, 0, 0
   215  	}
   216  	return pagesNeeded, start, end
   217  }