open Types
open Values

type size = int32
type index = int32
type count = int32
type table = { mutable ty : table_type; mutable content : ref_ array }
type t = table

exception Type
exception Bounds
exception SizeOverflow
exception SizeLimit
exception OutOfMemory

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

let create size r =
  try Lib.Array32.make size r
  with Out_of_memory | Invalid_argument _ -> raise OutOfMemory

let alloc (TableType (lim, _) as ty) r =
  if not (valid_limits lim) then raise Type;
  { ty; content = create lim.min r }

let size tab = Lib.Array32.length tab.content
let type_of tab = tab.ty

let grow tab delta r =
  let (TableType (lim, t)) = tab.ty in
  assert (lim.min = size tab);
  let old_size = lim.min in
  let new_size = Int32.add old_size delta in
  if I32.gt_u old_size new_size then raise SizeOverflow
  else
    let lim' = { lim with min = new_size } in
    if not (valid_limits lim') then raise SizeLimit
    else
      let after = create new_size r in
      Array.blit tab.content 0 after 0 (Array.length tab.content);
      tab.ty <- TableType (lim', t);
      tab.content <- after

let load tab i =
  try Lib.Array32.get tab.content i with Invalid_argument _ -> raise Bounds

let store tab i r =
  let (TableType (lim, t)) = tab.ty in
  if type_of_ref r <> t then raise Type;
  try Lib.Array32.set tab.content i r with Invalid_argument _ -> raise Bounds

let blit tab offset rs =
  let data = Array.of_list rs in
  try Lib.Array32.blit data 0l tab.content offset (Lib.Array32.length data)
  with Invalid_argument _ -> raise Bounds

open Types
open Values

type size = int32
type index = int32
type count = int32
type table = { mutable ty : table_type; mutable content : ref_ array }
type t = table

exception Type
exception Bounds
exception SizeOverflow
exception SizeLimit
exception OutOfMemory

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

let create size r =
  try Lib.Array32.make size r
  with Out_of_memory | Invalid_argument _ -> raise OutOfMemory

let alloc (TableType (lim, _) as ty) r =
  if not (valid_limits lim) then raise Type;
  { ty; content = create lim.min r }

let size tab = Lib.Array32.length tab.content
let type_of tab = tab.ty

let grow tab delta r =
  let (TableType (lim, t)) = tab.ty in
  assert (lim.min = size tab);
  let old_size = lim.min in
  let new_size = Int32.add old_size delta in
  if I32.gt_u old_size new_size then raise SizeOverflow
  else
    let lim' = { lim with min = new_size } in
    if not (valid_limits lim') then raise SizeLimit
    else
      let after = create new_size r in
      Array.blit tab.content 0 after 0 (Array.length tab.content);
      tab.ty <- TableType (lim', t);
      tab.content <- after

let load tab i =
  try Lib.Array32.get tab.content i with Invalid_argument _ -> raise Bounds

let store tab i r =
  let (TableType (lim, t)) = tab.ty in
  if type_of_ref r <> t then raise Type;
  try Lib.Array32.set tab.content i r with Invalid_argument _ -> raise Bounds

let blit tab offset rs =
  let data = Array.of_list rs in
  try Lib.Array32.blit data 0l tab.content offset (Lib.Array32.length data)
  with Invalid_argument _ -> raise Bounds
