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 }