content: module EVM.Concrete where
  
  import Prelude hiding (Word)
  import EVM.RLP
  import EVM.Types
  
  import Data.ByteString (ByteString)
  import Data.ByteString qualified as BS
  import Witch (unsafeInto, into)
  
  byteStringSliceWithDefaultZeroes :: Int -> Int -> ByteString -> ByteString
  byteStringSliceWithDefaultZeroes offset size bs =
    if size == 0
    then ""
    else
      let bs' = BS.take size (BS.drop offset bs)
      in bs' <> BS.replicate (size - BS.length bs') 0
  
  sliceMemory :: W256 -> W256 -> ByteString -> ByteString
  sliceMemory o s =
    byteStringSliceWithDefaultZeroes (unsafeInto o) (unsafeInto s)
  
  writeMemory :: ByteString -> W256 -> W256 -> W256 -> ByteString -> ByteString
  writeMemory bs1 n src dst bs0 =
    let
      (a, b) = BS.splitAt (unsafeInto dst) bs0
      a'     = BS.replicate (unsafeInto dst - BS.length a) 0
      -- sliceMemory should work for both cases, but we are using 256 bit
      -- words, whereas ByteString is only defined up to 64 bit. For large n,
      -- src, dst this will cause problems (often in GeneralStateTests).
      -- Later we could reimplement ByteString for 256 bit arguments.
      c      = if src > unsafeInto (BS.length bs1)
               then BS.replicate (unsafeInto n) 0
               else sliceMemory src n bs1
      b'     = BS.drop (unsafeInto n) b
    in
      a <> a' <> c <> b'
  
  createAddress :: Addr -> W64 -> Expr EAddr
  createAddress a n = LitAddr . unsafeInto . keccak' . rlpList $ [rlpAddrFull a, rlpWord256 (into n)]
  
  create2Address :: Addr -> W256 -> ByteString -> Expr EAddr
  create2Address a s b = LitAddr $ unsafeInto $ keccak' $ mconcat
    [BS.singleton 0xff, word160Bytes a, word256Bytes s, word256Bytes $ keccak' b]
  