github.com/pidato/unsafe@v0.1.4/memory/alloc_nocgo.go (about)

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