wasmedge-hs-0.17.1.0: src/WasmEdge/Memory.hs
{- |
Module : WasmEdge.Memory
Description : WasmEdge linear memory contexts
Copyright : (c) 2026 Tushar Adhatrao
License : MIT
Stability : stable
Provides types and functions for inspecting and manipulating WebAssembly
linear memory instances:
* 'MemoryType' — describes the page limits of a linear memory
* 'MemoryInstance' — a runtime linear memory instance with byte-level read/write
-}
module WasmEdge.Memory
( -- * Memory type
MemoryType
, createMemoryType
, deleteMemoryType
, withMemoryType
, getMemoryTypeLimit
-- * Memory instance
, MemoryInstance
, createMemory
, deleteMemory
, withMemory
, getMemoryType
, getMemoryData
, setMemoryData
, getMemoryPointer
, getMemoryPointerConst
, getMemoryPageSize
, growMemoryPages
-- * Internal / borrowed pointers
, unsafeMemoryPtr
, borrowMemory
, unsafeMemoryTypePtr
, borrowMemoryType
, disarmMemory
) where
import Control.Exception (bracket, throwIO)
import Data.ByteString (ByteString)
import Data.ByteString qualified as BS
import Data.ByteString.Unsafe qualified as UBS
import Data.Word (Word32, Word8)
import Foreign.C (CBool (..))
import Foreign.Marshal.Alloc (alloca, allocaBytes)
import Foreign.Ptr (FunPtr, Ptr, castPtr, nullPtr)
import Foreign.Storable (peek)
import WasmEdge.Error
import WasmEdge.Internal.FFI.Instance
import WasmEdge.Internal.Ownership (OwnedCtx, disarmOwned, finalizeOwned, mkOwned, withOwned)
import WasmEdge.Internal.Storable (allocaCResult)
import WasmEdge.Internal.Types (MemoryInstanceTag, MemoryTypeTag)
import WasmEdge.Internal.Utils (checkResult)
import WasmEdge.Types
-- ---------------------------------------------------------------------------
-- MemoryType
-- ---------------------------------------------------------------------------
-- | A WebAssembly linear memory type, specifying min/max page limits.
newtype MemoryType = MemoryType (OwnedCtx MemoryTypeTag)
{- | Create a 'MemoryType' with the given 'Limit'.
1 page = 64 KiB (65,536 bytes).
-}
createMemoryType :: Limit -> IO MemoryType
createMemoryType lim = do
let hasMax = CBool (case limitMax lim of Just _ -> 1; Nothing -> 0)
minPage = fromIntegral (limitMin lim)
maxPage = maybe 0 fromIntegral (limitMax lim)
shared = CBool (if limitShared lim then 1 else 0)
is64 = CBool 0
p <- c_MemoryTypeCreate hasMax minPage maxPage shared is64
if p == nullPtr
then throwIO $ WasmEdgeError ErrCategory_WASM 0 "WasmEdge_MemoryTypeCreate returned NULL"
else MemoryType <$> mkOwned (appFunPtr c_MemoryTypeDelete) p
-- | Delete a 'MemoryType'.
deleteMemoryType :: MemoryType -> IO ()
deleteMemoryType (MemoryType o) = finalizeOwned o
-- | Scoped 'MemoryType'.
withMemoryType :: Limit -> (MemoryType -> IO a) -> IO a
withMemoryType lim = bracket (createMemoryType lim) deleteMemoryType
-- | Get the 'Limit' of a 'MemoryType'.
getMemoryTypeLimit :: MemoryType -> IO Limit
getMemoryTypeLimit (MemoryType o) =
withOwned o $ \p -> do
limPtr <- c_MemoryTypeGetLimit p
if limPtr == nullPtr
then pure (Limit 0 Nothing False)
else alloca $ \hasMaxPtr ->
alloca $ \minPtr ->
alloca $ \maxPtr ->
alloca $ \sharedPtr ->
alloca $ \is64Ptr -> do
c_LimitGetInfo limPtr hasMaxPtr minPtr maxPtr sharedPtr is64Ptr
CBool hasMax <- peek hasMaxPtr
minVal <- peek minPtr
maxVal <- peek maxPtr
CBool shared <- peek sharedPtr
pure
Limit
{ limitMin = fromIntegral minVal
, limitMax = if hasMax /= 0 then Just (fromIntegral maxVal) else Nothing
, limitShared = shared /= 0
}
-- ---------------------------------------------------------------------------
-- MemoryInstance
-- ---------------------------------------------------------------------------
-- | A runtime WebAssembly linear memory instance.
newtype MemoryInstance = MemoryInstance (OwnedCtx MemoryInstanceTag)
-- | Create a 'MemoryInstance' from a 'MemoryType'.
createMemory :: MemoryType -> IO MemoryInstance
createMemory (MemoryType mtOwned) =
withOwned mtOwned $ \mtPtr -> do
p <- c_MemoryInstanceCreate mtPtr
if p == nullPtr
then throwIO $ WasmEdgeError ErrCategory_WASM 0 "WasmEdge_MemoryInstanceCreate returned NULL"
else MemoryInstance <$> mkOwned (appFunPtr c_MemoryInstanceDelete) p
-- | Delete a 'MemoryInstance'.
deleteMemory :: MemoryInstance -> IO ()
deleteMemory (MemoryInstance o) = finalizeOwned o
-- | Scoped 'MemoryInstance'.
withMemory :: MemoryType -> (MemoryInstance -> IO a) -> IO a
withMemory mt = bracket (createMemory mt) deleteMemory
-- | Get the 'MemoryType' of a 'MemoryInstance'.
getMemoryType :: MemoryInstance -> IO MemoryType
getMemoryType (MemoryInstance o) =
withOwned o $ \p -> do
tp <- c_MemoryInstanceGetMemoryType p
if tp == nullPtr
then throwIO $ WasmEdgeError ErrCategory_WASM 0 "WasmEdge_MemoryInstanceGetMemoryType returned NULL"
else borrowMemoryType tp
-- | Read @length@ bytes from linear memory starting at @offset@.
getMemoryData :: MemoryInstance -> Word32 -> Word32 -> IO (WasmResult ByteString)
getMemoryData (MemoryInstance o) offset len =
withOwned o $ \p ->
allocaBytes (fromIntegral len) $ \buf ->
allocaCResult $ \resPtr -> do
c_MemoryInstanceGetData resPtr p (castPtr buf) offset len
res <- checkResult resPtr
case res of
Left err -> pure (Left err)
Right () -> Right <$> BS.packCStringLen (buf, fromIntegral len)
-- | Write a 'ByteString' into linear memory starting at @offset@.
setMemoryData :: MemoryInstance -> Word32 -> ByteString -> IO (WasmResult ())
setMemoryData (MemoryInstance o) offset bs =
withOwned o $ \p ->
UBS.unsafeUseAsCStringLen bs $ \(buf, len) ->
allocaCResult $ \resPtr -> do
c_MemoryInstanceSetData resPtr p (castPtr buf) offset (fromIntegral len)
checkResult resPtr
{- | Get a direct pointer to linear memory at @offset@ for @length@ bytes.
The pointer is valid only as long as the memory is not grown and the instance is alive.
-}
getMemoryPointer :: MemoryInstance -> Word32 -> Word32 -> IO (Ptr Word8)
getMemoryPointer (MemoryInstance o) offset len =
withOwned o $ \p -> c_MemoryInstanceGetPointer p offset len
-- | Const variant of 'getMemoryPointer'.
getMemoryPointerConst :: MemoryInstance -> Word32 -> Word32 -> IO (Ptr Word8)
getMemoryPointerConst (MemoryInstance o) offset len =
withOwned o $ \p -> c_MemoryInstanceGetPointerConst p offset len
-- | Get the current size in pages (1 page = 64 KiB).
getMemoryPageSize :: MemoryInstance -> IO Word32
getMemoryPageSize (MemoryInstance o) =
withOwned o c_MemoryInstanceGetPageSize
-- | Grow linear memory by @pageCount@ pages.
growMemoryPages :: MemoryInstance -> Word32 -> IO (WasmResult ())
growMemoryPages (MemoryInstance o) pageCount =
withOwned o $ \p ->
allocaCResult $ \resPtr -> do
c_MemoryInstanceGrowPage resPtr p pageCount
checkResult resPtr
-- ---------------------------------------------------------------------------
-- Unsafe / internal pointer helpers
-- ---------------------------------------------------------------------------
-- | Extract the raw pointer of a 'MemoryInstance'.
unsafeMemoryPtr :: MemoryInstance -> IO (Ptr MemoryInstanceTag)
unsafeMemoryPtr (MemoryInstance o) = withOwned o pure
-- | Create a borrowed 'MemoryInstance' from a raw pointer (does not finalize).
borrowMemory :: Ptr MemoryInstanceTag -> IO MemoryInstance
borrowMemory p = MemoryInstance <$> mkOwned (\_ -> pure ()) p
-- | Extract the raw pointer of a 'MemoryType'.
unsafeMemoryTypePtr :: MemoryType -> IO (Ptr MemoryTypeTag)
unsafeMemoryTypePtr (MemoryType o) = withOwned o pure
-- | Create a borrowed 'MemoryType' from a raw pointer (does not finalize).
borrowMemoryType :: Ptr MemoryTypeTag -> IO MemoryType
borrowMemoryType p = MemoryType <$> mkOwned (\_ -> pure ()) p
-- | Disarm finalizer when ownership is transferred to a module instance.
disarmMemory :: MemoryInstance -> IO ()
disarmMemory (MemoryInstance o) = disarmOwned o
-- ---------------------------------------------------------------------------
-- Internal helper
-- ---------------------------------------------------------------------------
appFunPtr :: FunPtr (Ptr a -> IO ()) -> Ptr a -> IO ()
appFunPtr = mkDeleter
foreign import ccall "dynamic"
mkDeleter :: FunPtr (Ptr a -> IO ()) -> Ptr a -> IO ()