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(b, 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) }