1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
(* SPDX-License-Identifier: AGPL-3.0-or-later *)
(* Copyright © 2021-2024 OCamlPro *)
(* Written by the Owi programmers *)

let page_size = 65_536

type t =
  { mutable limits : Binary.Mem.Type.limits
  ; mutable data : bytes
  }

let get_min : Binary.Mem.Type.limits -> int = function
  | I32 { min; _ } -> Int32.to_int min
  | I64 { min; _ } -> min

let set_min (limits : Binary.Mem.Type.limits) new_min : Binary.Mem.Type.limits =
  match limits with
  | I32 l -> I32 { l with min = Int32.of_int new_min }
  | I64 l -> I64 { l with min = new_min }

let init limits : t =
  (* TODO: overflow check? (page_size * limits.min) / page_size = limits.min? *)
  let data = Bytes.make (page_size * get_min limits) '\x00' in
  { limits; data }

let update_memory mem data =
  let limits =
    set_min mem.limits
      (max (get_min mem.limits) (Bytes.length data / page_size))
  in
  mem.limits <- limits;
  mem.data <- data

let grow mem delta =
  let delta = Int32.to_int delta in
  let old_size = Bytes.length mem.data in
  let new_mem = Bytes.extend mem.data 0 delta in
  Bytes.unsafe_fill new_mem old_size delta (Char.chr 0);
  update_memory mem new_mem;
  Ok ()

let fill mem ~pos ~len c =
  let pos = Int32.to_int pos in
  let len = Int32.to_int len in
  Bytes.unsafe_fill mem.data pos len c;
  Ok ()

let blit ~src ~src_idx ~dst ~dst_idx ~len =
  let src_idx = Int32.to_int src_idx in
  let dst_idx = Int32.to_int dst_idx in
  let len = Int32.to_int len in
  Bytes.unsafe_blit src.data src_idx dst.data dst_idx len;
  Ok ()

let blit_string mem str ~src ~dst ~len =
  let src = Int32.to_int src in
  let dst = Int32.to_int dst in
  let len = Int32.to_int len in
  Bytes.unsafe_blit_string str src mem.data dst len;
  Ok ()

let get_limit_max { limits; _ } =
  match limits with
  | I32 { max; _ } -> Option.map (fun maxv -> Int32.to_int maxv) max
  | I64 { max; _ } -> max

let get_limits { limits; _ } = limits

let store_8 mem ~addr n =
  let addr = Int32.to_int addr in
  let n = Int32.to_int n in
  Ok (Bytes.set_int8 mem.data addr n)

let store_16 mem ~addr n =
  let addr = Int32.to_int addr in
  let n = Int32.to_int n in
  Ok (Bytes.set_int16_le mem.data addr n)

let store_32 mem ~addr n =
  let addr = Int32.to_int addr in
  Ok (Bytes.set_int32_le mem.data addr n)

let store_64 mem ~addr n =
  let addr = Int32.to_int addr in
  Ok (Bytes.set_int64_le mem.data addr n)

let load_8_s mem addr =
  let addr = Int32.to_int addr in
  Ok (Int32.of_int @@ Bytes.get_int8 mem.data addr)

let load_8_u mem addr =
  let addr = Int32.to_int addr in
  Ok (Int32.of_int @@ Bytes.get_uint8 mem.data addr)

let load_16_s mem addr =
  let addr = Int32.to_int addr in
  Ok (Int32.of_int @@ Bytes.get_int16_le mem.data addr)

let load_16_u mem addr =
  let addr = Int32.to_int addr in
  Ok (Int32.of_int @@ Bytes.get_uint16_le mem.data addr)

let load_32 mem addr =
  let addr = Int32.to_int addr in
  Ok (Bytes.get_int32_le mem.data addr)

let load_64 mem addr =
  let addr = Int32.to_int addr in
  Ok (Bytes.get_int64_le mem.data addr)

let size_in_pages mem = Int32.of_int @@ (Bytes.length mem.data / page_size)

let size mem = Int32.of_int @@ Bytes.length mem.data