@ 261106.1
current
open Types
open Value
open Bigarray
open Lib.Bigarray

type size = int64  (* number of pages *)
type offset = address
type count = int32

type memory' = (int, int8_unsigned_elt, c_layout) Array1.t
type memory = {mutable ty : memorytype; mutable content : memory'}
type t = memory

exception Type = Value.Type
exception Bounds
exception SizeOverflow
exception SizeLimit
exception OutOfMemory

let page_size = 0x10000L (* 64 KiB *)

let valid_limits {min; max} =
  match max with
  | None -> true
  | Some m -> I64.le_u min m

let valid_size at i =
  match at with
  | I32AT -> I64.le_u i 0x1_0000L
  | I64AT -> I64.le_u i 0x1_0000_0000_0000L

let create n =
  if n > Int64.(div max_int page_size) then raise OutOfMemory else
  let size = Int64.(mul n page_size) in
  try
    let mem = Array1_64.create Int8_unsigned C_layout size in
    Array1.fill mem 0;
    mem
  with Out_of_memory | Invalid_argument _ -> raise OutOfMemory

let alloc (MemoryT (at, lim) as ty) =
  assert Free.((memorytype ty).types = Set.empty);
  if not (valid_size at lim.min) then raise SizeOverflow;
  if not (valid_limits lim) then raise Type;
  {ty; content = create lim.min}

let bound mem =
  Array1_64.dim mem.content

let size mem =
  Int64.(div (bound mem) page_size)

let type_of mem =
  mem.ty

let addrtype_of mem =
  let MemoryT (at, _) = type_of mem in at

let grow mem delta =
  let MemoryT (at, lim) = mem.ty in
  assert (lim.min = size mem);
  let old_size = lim.min in
  let new_size = Int64.add old_size delta in
  if I64.gt_u old_size new_size then raise SizeOverflow else
  let lim' = {lim with min = new_size} in
  if not (valid_size at new_size) then raise SizeOverflow else
  if not (valid_limits lim') then raise SizeLimit else
  let after = create new_size in
  let dim = Array1_64.dim mem.content in
  Array1.blit (Array1_64.sub mem.content 0L dim) (Array1_64.sub after 0L dim);
  mem.ty <- MemoryT (at, lim');
  mem.content <- after

let load_byte mem a =
  if a < 0L || a >= Array1_64.dim mem.content then raise Bounds;
  Array1_64.get mem.content a

let store_byte mem a b =
  if a < 0L || a >= Array1_64.dim mem.content then raise Bounds;
  Array1_64.set mem.content a b

let load_bytes mem a n =
  let buf = Buffer.create n in
  for i = 0 to n - 1 do
    Buffer.add_char buf (Char.chr (load_byte mem Int64.(add a (of_int i))))
  done;
  Buffer.contents buf

let store_bytes mem a bs =
  if a < 0L then raise Bounds;
  for i = String.length bs - 1 downto 0 do
    store_byte mem Int64.(add a (of_int i)) (Char.code bs.[i])
  done


(* Typed accessors *)

let effective_address a o =
  let ea = Int64.(add a o) in
  if I64.lt_u ea a then raise Bounds;
  ea

let load_num mem a o nt =
  let bs = load_bytes mem (effective_address a o) (Types.num_size nt) in
  Value.num_of_bits nt bs

let store_num mem a o n =
  let bs = Value.bits_of_num n in
  store_bytes mem (effective_address a o) bs

let load_num_packed sz sx mem a o nt =
  let bs = load_bytes mem (effective_address a o) (Pack.packed_size sz) in
  Value.num_of_packed_bits nt sz sx bs

let store_num_packed sz mem a o n =
  let bs = Value.packed_bits_of_num sz n in
  store_bytes mem (effective_address a o) bs

let load_vec mem a o vt =
  let bs = load_bytes mem (effective_address a o) (Types.vec_size vt) in
  Value.vec_of_bits vt bs

let store_vec mem a o v =
  let bs = Value.bits_of_vec v in
  store_bytes mem (effective_address a o) bs

let load_vec_packed sz vext mem a o t =
  let bs = load_bytes mem (effective_address a o) (Pack.packed_size sz) in
  Value.vec_of_packed_bits t sz vext bs

let load_val mem a o t =
  let bs = load_bytes mem (effective_address a o) (Types.val_size t) in
  Value.val_of_bits t bs

let store_val mem a o v =
  let bs = Value.bits_of_val v in
  store_bytes mem (effective_address a o) bs

let load_val_storage mem a o st =
  let bs = load_bytes mem (effective_address a o) (Types.storage_size st) in
  Value.val_of_storage_bits st bs

let store_val_storage mem a o st v =
  let bs = Value.storage_bits_of_val st v in
  store_bytes mem (effective_address a o) bs