(** Each address has not one, but two permissions associated with it.  The first is the current permission.  It governs whether operations (load, store, free, etc) over this address succeed or not.  The other is the maximal permission.  It is always at least as strong as the current permission.  Once a block is allocated, the maximal permission of an address within this block can only decrease, as a result of [free] or [drop_perm] operations, or of external calls.  In contrast, the current permission of an address can be temporarily lowered by an external call, then raised again by another external call. *)
Inductive perm_kind: Type :=
| Max: perm_kind
| Cur: perm_kind.
Module Type MEM.
(** The abstract type of memory states. *)
Parameter mem: Type.
(** * Operations on memory states *)
(** [empty] is the initial memory state. *)
Parameter empty: mem.
(** [alloc m lo hi] allocates a fresh block of size [hi - lo] bytes. Valid offsets in this block are between [lo] included and [hi] excluded. These offsets are writable in the returned memory state. This block is not initialized: its contents are initially undefined. Returns a pair [(m', b)] of the updated memory state [m'] and the identifier [b] of the newly-allocated block. Note that [alloc] never fails: we are modeling an infinite memory. *)
Parameter alloc: forall (m: mem) (lo hi: Z), mem * block.
(** [free m b lo hi] frees (deallocates) the range of offsets from [lo] included to [hi] excluded in block [b].  Returns the updated memory state, or [None] if the freed addresses are not writable. *)
Parameter free: forall (m: mem) (b: block) (lo hi: Z), option mem.
(** [load chunk m b ofs] reads a memory quantity [chunk] from addresses [b, ofs] to [b, ofs + size_chunk chunk - 1] in memory state [m].  Returns the value read, or [None] if the accessed addresses are not readable. *)
Parameter load: forall (chunk: memory_chunk) (m: mem) (b: block) (ofs: Z), option val.
(** [store chunk m b ofs v] writes value [v] as memory quantity [chunk] from addresses [b, ofs] to [b, ofs + size_chunk chunk - 1] in memory state [m].  Returns the updated memory state, or [None] if the accessed addresses are not writable. *)
Parameter store: forall (chunk: memory_chunk) (m: mem) (b: block) (ofs: Z) (v: val), option mem.
(** [loadv] and [storev] are variants of [load] and [store] where the address being accessed is passed as a value (of the [Vptr] kind). *)
Definition loadv (chunk: memory_chunk) (m: mem) (addr: val) : option val :=
match addr with
| Vptr b ofs => load chunk m b (Ptrofs.unsigned ofs)
| _ => None
end.
Definition storev (chunk: memory_chunk) (m: mem) (addr v: val) : option mem :=
match addr with
| Vptr b ofs => store chunk m b (Ptrofs.unsigned ofs) v
| _ => None
end.
(** [loadbytes m b ofs n] reads and returns the byte-level representation of the values contained at offsets [ofs] to [ofs + n - 1] within block [b] in memory state [m]. [None] is returned if the accessed addresses are not readable. *)
Parameter loadbytes: forall (m: mem) (b: block) (ofs n: Z), option (list memval).
(** [storebytes m b ofs bytes] stores the given list of bytes [bytes] starting at location [(b, ofs)].  Returns updated memory state or [None] if the accessed locations are not writable. *)
Parameter storebytes: forall (m: mem) (b: block) (ofs: Z) (bytes: list memval), option mem.
(** [free_list] frees all the given (block, lo, hi) triples. *)
Fixpoint free_list (m: mem) (l: list (block * Z * Z)) {struct l}: option mem :=
match l with
| nil => Some m
| (b, lo, hi) :: l' =>
match free m b lo hi with
| None => None
| Some m' => free_list m' l'
end
end.
(** [drop_perm m b lo hi p] sets the permissions of the byte range [(b, lo) ... (b, hi - 1)] to [p].  These bytes must have [Freeable] permissions in the initial memory state [m]. Returns updated memory state, or [None] if insufficient permissions. *)
Parameter drop_perm: forall (m: mem) (b: block) (lo hi: Z) (p: permission), option mem.
(** * Permissions, block validity, access validity, and bounds *)
(** The next block of a memory state is the block identifier for the next allocation.  It increases by one at each allocation. Block identifiers below [nextblock] are said to be valid, meaning that they have been allocated previously.  Block identifiers above [nextblock] are fresh or invalid, i.e. not yet allocated.  Note that a block identifier remains valid after a [free] operation over this block. *)
Parameter nextblock: mem -> block.
Definition valid_block (m: mem) (b: block) := Plt b (nextblock m).

Axiom valid_not_valid_diff:
forall m b b', valid_block m b -> ~(valid_block m b') -> b <> b'.