90 lines
2.5 KiB
Go
90 lines
2.5 KiB
Go
package main
|
|
|
|
import "unsafe"
|
|
|
|
// ============================================================================
|
|
// Memory Management
|
|
// ============================================================================
|
|
|
|
// heapSize is the fixed size of the pre-allocated heap.
|
|
// This must be large enough to handle all allocations during the plugin lifetime.
|
|
// The heap is never reallocated to ensure all pointers remain valid.
|
|
const heapSize = 4 * 1024 * 1024 // 4MB fixed heap
|
|
|
|
// heapBase is a fixed-size buffer that is never reallocated.
|
|
// All allocations come from this buffer to ensure pointer stability.
|
|
var heapBase []byte
|
|
|
|
// heapOffset tracks the next available position in heapBase.
|
|
var heapOffset uint32 = 0
|
|
|
|
// heapBasePtr caches the base pointer of heapBase for efficient offset-to-pointer conversion.
|
|
var heapBasePtr uintptr
|
|
|
|
func init() {
|
|
// Pre-allocate the fixed heap once at startup.
|
|
// This ensures heapBase is never reallocated after pointers are handed out.
|
|
heapBase = make([]byte, heapSize)
|
|
heapBasePtr = uintptr(unsafe.Pointer(&heapBase[0]))
|
|
}
|
|
|
|
//export plugin_malloc
|
|
func plugin_malloc(size uint32) uint32 {
|
|
if size == 0 {
|
|
return 0
|
|
}
|
|
// Align to 8-byte boundary
|
|
alignedSize := (size + 7) &^ 7
|
|
// Check if we have enough space (no reallocation allowed)
|
|
if heapOffset+alignedSize > uint32(len(heapBase)) {
|
|
// Allocation failure - heap exhausted
|
|
// Return 0 to indicate failure rather than reallocating
|
|
return 0
|
|
}
|
|
// Return pointer to the allocated region
|
|
ptr := uint32(heapBasePtr + uintptr(heapOffset))
|
|
heapOffset += alignedSize
|
|
return ptr
|
|
}
|
|
|
|
//export plugin_free
|
|
func plugin_free(ptr uint32) {
|
|
// No-op: we use a simple bump allocator without individual frees.
|
|
// Memory is reclaimed when the plugin is unloaded.
|
|
}
|
|
|
|
// plugin_reset resets the heap allocator, allowing memory to be reused.
|
|
// This should only be called when no allocated memory is in use.
|
|
//
|
|
//export plugin_reset
|
|
func plugin_reset() {
|
|
heapOffset = 0
|
|
}
|
|
|
|
func packResult(ptr uint32, length uint32) uint64 {
|
|
return (uint64(ptr) << 32) | uint64(length)
|
|
}
|
|
|
|
func writeBytes(data []byte) uint64 {
|
|
if len(data) == 0 {
|
|
return 0
|
|
}
|
|
// Allocate from the stable heap
|
|
ptr := plugin_malloc(uint32(len(data)))
|
|
if ptr == 0 {
|
|
// Allocation failed
|
|
return 0
|
|
}
|
|
// Copy data into the allocated region
|
|
offset := ptr - uint32(heapBasePtr)
|
|
copy(heapBase[offset:offset+uint32(len(data))], data)
|
|
return packResult(ptr, uint32(len(data)))
|
|
}
|
|
|
|
func readInput(ptr, length uint32) []byte {
|
|
if length == 0 {
|
|
return nil
|
|
}
|
|
return unsafe.Slice((*byte)(unsafe.Pointer(uintptr(ptr))), length)
|
|
}
|