154 lines
4.8 KiB
Go
154 lines
4.8 KiB
Go
package mem
|
|
|
|
import (
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
Kilobyte = 1 << (10 * (iota + 1))
|
|
Megabyte
|
|
Gigabyte
|
|
Terabyte
|
|
)
|
|
|
|
// Sizeof returns the size (in bytes) of the given type.
|
|
//
|
|
// Not to be confused with [unsafe.Sizeof] which returns the size of a type via an expression.
|
|
func Sizeof[T any]() uintptr {
|
|
return unsafe.Sizeof(*(*T)(nil))
|
|
}
|
|
|
|
// Alignof returns the alignment (in bytes) of the given type.
|
|
//
|
|
// Not to be confused with [unsafe.AlignOf] which returns the alignment of a type via an expression.
|
|
func Alignof[T any]() uintptr {
|
|
return unsafe.Alignof(*(*T)(nil))
|
|
}
|
|
|
|
// ZeroValue returns the zero value of a given type.
|
|
func ZeroValue[T any]() (_ T) {
|
|
return
|
|
}
|
|
|
|
// BitCast performs a bit conversion between two types of the same size.
|
|
//
|
|
// BitCast panics if the sizes of the types differ.
|
|
func BitCast[TOut any, TIn any](value *TIn) TOut {
|
|
if Sizeof[TOut]() != Sizeof[TIn]() {
|
|
panic("bitcast: sizes of types must match")
|
|
}
|
|
return *((*TOut)(unsafe.Pointer(value)))
|
|
}
|
|
|
|
// BitCastValue performs a bit conversion between two types of the same size.
|
|
//
|
|
// BitCastValue panics if the sizes of the types differ.
|
|
func BitCastValue[TOut any, TIn any](value TIn) TOut {
|
|
if Sizeof[TOut]() != Sizeof[TIn]() {
|
|
panic("bitcast: sizes of types must match")
|
|
}
|
|
return *((*TOut)(unsafe.Pointer(&value)))
|
|
}
|
|
|
|
// UnsafeCast performs a bit conversion between two types without checking if their sizes match.
|
|
func UnsafeCast[TOut any, TIn any](value *TIn) TOut {
|
|
return *((*TOut)(unsafe.Pointer(value)))
|
|
}
|
|
|
|
// UnsafeCastValue performs a bit conversion between two types without checking if their sizes match.
|
|
func UnsafeCastValue[TOut any, TIn any](value TIn) TOut {
|
|
return *((*TOut)(unsafe.Pointer(&value)))
|
|
}
|
|
|
|
// Copy copies size number of bytes from src into dst.
|
|
//
|
|
// Returns dst.
|
|
func Copy(dst, src unsafe.Pointer, size uintptr) unsafe.Pointer {
|
|
copy(unsafe.Slice((*byte)(dst), size), unsafe.Slice((*byte)(src), size))
|
|
return dst
|
|
}
|
|
|
|
// Clear overwrites 'count' number of bytes in 'dst' with a particular value.
|
|
//
|
|
// Returns dst.
|
|
func Clear(dst unsafe.Pointer, value byte, count uintptr) unsafe.Pointer {
|
|
b := (*byte)(dst)
|
|
for range count { // @todo: loop unroll/maybe use asm?
|
|
*b = value
|
|
b = (*byte)(unsafe.Add(dst, 1))
|
|
}
|
|
return dst
|
|
}
|
|
|
|
// Zero overwrites 'count' number of bytes in 'dst' with zeros.
|
|
//
|
|
// Returns dst.
|
|
func Zero(dst unsafe.Pointer, count uintptr) unsafe.Pointer {
|
|
return Clear(dst, 0, count)
|
|
}
|
|
|
|
// AlignForward returns an address align to the next power-of-two alignment.
|
|
func AlignForward(address uintptr, alignment uintptr) uintptr {
|
|
if alignment == 0 || (alignment&(alignment-1)) != 0 {
|
|
panic("alignforward: alignment must be a power of two")
|
|
}
|
|
return (address + alignment - 1) &^ (alignment - 1)
|
|
}
|
|
|
|
// AlignBackward returns an address align to the previous power-of-two alignment.
|
|
func AlignBackward(address uintptr, alignment uintptr) uintptr {
|
|
if alignment == 0 || (alignment&(alignment-1)) != 0 {
|
|
panic("alignbackward: alignment must be a power of two")
|
|
}
|
|
return address &^ (alignment - 1)
|
|
}
|
|
|
|
// Aligned returns if the address is aligned to the given power-of-two alignment.
|
|
func Aligned(address uintptr, alignment uintptr) bool {
|
|
if alignment == 0 || (alignment&(alignment-1)) != 0 {
|
|
panic("aligned: alignment must be a power of two")
|
|
}
|
|
return address&(alignment-1) == 0
|
|
}
|
|
|
|
// ExtendSlice returns a copy of the given slice, increasing its length, but leaving the capacity intact.
|
|
func ExtendSlice[T any](slice []T, amount uintptr) []T {
|
|
if amount+uintptr(len(slice)) > uintptr(cap(slice)) {
|
|
panic("extendslice: cannot extend slice past its capacity")
|
|
}
|
|
|
|
return slice[: uintptr(len(slice))+amount : cap(slice)]
|
|
}
|
|
|
|
// Access describes memory access permissions.
|
|
type Access int
|
|
|
|
const (
|
|
AccessNone Access = 1 << iota
|
|
AccessRead
|
|
AccessWrite
|
|
AccessExecute
|
|
)
|
|
|
|
// Reserve returns a slice of bytes pointing to uncommitted virtual memory.
|
|
// The length and capacity of the slice will be total_address_space bytes.
|
|
//
|
|
// The underlying memory of the slice must be comitted to phyiscal memory before being accessed (see: Commit).
|
|
//
|
|
// Use Release to return the virtual address space back to the operating system.
|
|
func Reserve(total_address_space uintptr) ([]byte, error) { return reserve(total_address_space) }
|
|
|
|
// Release returns reserved virtual address space back to the operating system.
|
|
//
|
|
// Note: Any committed memory within its address space will be freed as well.
|
|
func Release(reserved []byte) error { return release(reserved) }
|
|
|
|
// Commit maps virtual memory to physical memory.
|
|
func Commit(reserved []byte, access Access) error { return commit(reserved, access) }
|
|
|
|
// Decommit unmaps committed memory, leaving the underlying addresss space intact.
|
|
//
|
|
// Decommitted memory can be re-committed at a later time using Commit.
|
|
//
|
|
// Note: Accessing the memory after calling Decommit is unsafe and may cause a panic.
|
|
func Decommit(committed []byte) (err error) { return decommit(committed) }
|