{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
module Blockchain.VM.Memory (
Memory(..),
getSizeInBytes,
getSizeInWords,
getShow,
mLoad,
mLoad8,
mLoadByteString,
mStore,
mStore8,
mStoreByteString
) where
import Control.Monad
import qualified Data.Vector.Unboxed.Mutable as V
import qualified Data.ByteString as B
import qualified Data.ByteString.Base16 as B16
import Data.Functor
import Data.IORef
import Data.Word
--import Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
import Blockchain.ExtWord
import Blockchain.Util
import Blockchain.VM.VMState
getSizeInWords::Memory->IO Word256
getSizeInWords (Memory _ size) = (ceiling . (/ (32::Double)) . fromIntegral) <$> readIORef size
getSizeInBytes::Memory->IO Word256
getSizeInBytes (Memory _ size) = (1+) <$> readIORef size
--In this function I use the words "size" and "length" to mean 2 different things....
--"size" is the highest memory location used (as described in the yellowpaper).
--"length" is the IOVector length, which will often be larger than the size.
--Basically, to avoid resizing the vector constantly (which could be expensive),
--I keep something larger around until it fills up, then reallocate (something even
--larger).
setNewMaxSize::VMState->Word256->IO VMState
setNewMaxSize state newSize = do
oldSize <- readIORef (mSize $ memory state)
when (newSize > oldSize) $ do
writeIORef (mSize $ memory state) newSize
let gasCharge =
if newSize > oldSize
then fromInteger $ (ceiling $ fromIntegral newSize/(32::Double)) - (ceiling $ fromIntegral oldSize/(32::Double))
else 0
let oldLength = fromIntegral $ V.length (mVector $ memory state)
state' <-
if newSize > oldLength
then do
arr' <- V.grow (mVector $ memory state) $ fromIntegral $ 2*newSize
forM_ [oldLength-1..2*oldLength-1] $ \p -> V.write arr' (fromIntegral p) 0
return $ state{memory=(memory state){mVector = arr'}}
else return state
return state'{vmGasRemaining=vmGasRemaining state - gasCharge}
getShow::Memory->IO String
getShow (Memory arr sizeRef) = do
msize <- readIORef sizeRef
--fmap (show . B16.encode . B.pack) $ sequence $ V.read arr <$> fromIntegral <$> [0..fromIntegral msize-1]
fmap (show . B16.encode . B.pack) $ sequence $ V.read arr <$> [0..fromIntegral msize-1]
mLoad::VMState->Word256->IO [Word8]
mLoad state p = do
--setNewMaxSize m (p+31)
sequence $ V.read (mVector $ memory state) <$> fromIntegral <$> [p..p+31]
mLoad8::VMState->Word256->IO Word8
mLoad8 state p = do
--setNewMaxSize m p
V.read (mVector $ memory state) (fromIntegral p)
mLoadByteString::VMState->Word256->Word256->IO B.ByteString
mLoadByteString state p size = do
--setNewMaxSize m (p+size)
fmap B.pack $ sequence $ V.read (mVector $ memory state) <$> fromIntegral <$> [p..p+size-1]
mStore::VMState->Word256->Word256->IO VMState
mStore state p val = do
state' <- setNewMaxSize state (p+32)
sequence_ $ uncurry (V.write $ mVector $ memory state') <$> zip [fromIntegral p..] (word256ToBytes val)
return state'
mStore8::VMState->Word256->Word8->IO VMState
mStore8 state p val = do
state' <- setNewMaxSize state p
V.write (mVector $ memory state') (fromIntegral p) val
return state'
mStoreByteString::VMState->Word256->B.ByteString->IO VMState
mStoreByteString state p theData = do
state' <- setNewMaxSize state (p + fromIntegral (B.length theData))
sequence_ $ uncurry (V.write $ mVector $ memory state') <$> zip (fromIntegral <$> [p..p+fromIntegral (B.length theData)]) (B.unpack theData)
return state'