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 }