module Pool_intf:sig..end
    A pool stores a bounded-size set of tuples, where client code is responsible for
    explicitly controlling when the pool allocates and frees tuples.  One creates a pool
    of a certain capacity, which returns an empty pool that can hold that many tuples.
    One then uses new to allocate a tuple, which returns a Pointer.t to the tuple.
    One then uses get and set along with the pointer to get and set slots of the
    tuple.  Finally, one free's a pointer to the pool's memory for a tuple, making the
    memory available for subsequent reuse.
    The point of Pool is to allocate a single long-lived block of memory (the pool) that
    lives in the OCaml major heap, and then to reuse the block, rather than continually
    allocating blocks on the minor heap.
In typical usage, one wraps up a pool with an abstract interface, giving nice names to the tuple slots, and only exposing mutation where desired.
All the usual problems with manual memory allocation are present with pools:
Pool.Debug and Pool.Error_check, that are useful for
    building pools to help debug incorrect pointer usage.
    A pool stores a bounded-size set of tuples, where client code is responsible for
    explicitly controlling when the pool allocates and frees tuples.  One creates a pool
    of a certain capacity, which returns an empty pool that can hold that many tuples.
    One then uses new to allocate a tuple, which returns a Pointer.t to the tuple.
    One then uses get and set along with the pointer to get and set slots of the
    tuple.  Finally, one free's a pointer to the pool's memory for a tuple, making the
    memory available for subsequent reuse.
    The point of Pool is to allocate a single long-lived block of memory (the pool) that
    lives in the OCaml major heap, and then to reuse the block, rather than continually
    allocating blocks on the minor heap.
In typical usage, one wraps up a pool with an abstract interface, giving nice names to the tuple slots, and only exposing mutation where desired.
All the usual problems with manual memory allocation are present with pools:
Pool.Debug and Pool.Error_check, that are useful for
    building pools to help debug incorrect pointer usage.module type S =sig..end
S is the module type for a pool.
module type Pool =sig..end
    A pool stores a bounded-size set of tuples, where client code is responsible for
    explicitly controlling when the pool allocates and frees tuples.  One creates a pool
    of a certain capacity, which returns an empty pool that can hold that many tuples.
    One then uses new to allocate a tuple, which returns a Pointer.t to the tuple.
    One then uses get and set along with the pointer to get and set slots of the
    tuple.  Finally, one free's a pointer to the pool's memory for a tuple, making the
    memory available for subsequent reuse.
    The point of Pool is to allocate a single long-lived block of memory (the pool) that
    lives in the OCaml major heap, and then to reuse the block, rather than continually
    allocating blocks on the minor heap.
In typical usage, one wraps up a pool with an abstract interface, giving nice names to the tuple slots, and only exposing mutation where desired.
All the usual problems with manual memory allocation are present with pools:
Pool.Debug and Pool.Error_check, that are useful for
    building pools to help debug incorrect pointer usage.S is the module type for a pool.'slots will look like ('a1, ..., 'an)
        Slots.tn, and the tuples have type 'a1 * ... * 'an.null pointer is a distinct pointer that does not correspond to a tuple in
        the pool.  It is a function to prevent problems due to the value restriction.'slots will look like ('a1, ..., 'an) Slots.tn, and the pool holds
      tuples of type 'a1 * ... * 'an.pointer_is_valid t pointer returns true iff pointer points to a live tuple in
      t, i.e. pointer is not null, not free, and is in the range of t.
      A pointer might not be in the range of a pool if it comes from another pool for
      example.  In this case unsafe_get/set functions would cause a segfault.
id_of_pointer t pointer returns an id that is unique for the lifetime of
      pointer's tuple.  When the tuple is freed, the id is no longer valid, and
      pointer_of_id_exn will fail on it.  Pointer.null () has a distinct id from all
      non-null pointers.
pointer_of_id_exn t id returns the pointer corresponding to id.  It fails if the
      tuple corresponding to id was already freed.
      pointer_of_id_exn_is_supported says whether the implementation supports
      pointer_of_id_exn; if not, it will always raise.  We can not use the usual idiom
      of making pointer_of_id_exn be an Or_error.t due to problems with the value
      restriction.
create slots ~capacity ~dummy creates an empty pool that can hold up to capacity
      N-tuples.  The slots of dummy are stored in free tuples.  create raises if
      capacity < 0.
capacity returns the maximum number of tuples that the pool can hold.
length returns the number of tuples currently in the pool.
        0 <= length t <= capacity t
      grow t ~capacity returns a new pool t' with the supplied capacity.  The new pool
      is to be used as a replacement for t.  All live tuples in t are now live in
      t', and valid pointers to tuples in t are now valid pointers to the identical
      tuple in t'.  It is an error to use t after calling grow t.
      grow raises if the supplied capacity isn't larger than capacity t.
default is 2 * capacity t
is_full t returns true if no more tuples can be allocated in t.
free t pointer frees the tuple pointed to by pointer from t.
new<N> t a0 ... a<N-1> returns a new tuple from the pool, with the tuple's
      slots initialized to a0 ... a<N-1>.  new raises if is_full t.
get_tuple t pointer allocates an OCaml tuple isomorphic to the pool t's tuple
      pointed to by pointer. The tuple gets copied, but its slots do not.
get t pointer slot gets slot of the tuple pointed to by pointer in
      pool t.
      set t pointer slot a sets to a the slot of the tuple pointed to by pointer
      in pool t.
      In get and set, it is an error to refer to a pointer that has been freed.  It
      is also an error to use a pointer with any pool other than the one the pointer was
      new'd from or grown to.  These errors will lead to undefined behavior, but will
      not segfault.
      unsafe_get is comparable in speed to get for immediate values, and 5%-10% faster
      for pointers.
      unsafe_get and unsafe_set skip bounds checking, and can thus segfault.
This uses an Obj_array.t to implement the pool.  We expose that Pointer.t is an
      int so that OCaml can avoid the write barrier, due to knowing that Pointer.t
      isn't an OCaml pointer.
An Unsafe pool is like an ordinary pool, except that the create function does
      not require an initial element.  The pool stores Obj.magic () as the dummy value
      for each slot.  Such a pool is only safe if one never accesses a slot from a freed
      tuple.
      It makes sense to use Unsafe if one has a small constrained chunk of code where
      one can prove that one never accesses a freed tuple, and one needs a pool where
      it is difficult to construct a dummy value.
create slots ~capacity creates an empty pool that can hold up to capacity
        N-tuples.  The elements of a free tuple may contain stale and/or invalid values
        for their types, and as such any access to a free tuple from this pool is
        unsafe.
Debug builds a pool in which every function can run invariant on its pool
      argument(s) and/or print a debug message to stderr, as determined by
      !check_invariant and !show_messages, which are initially both true.
      The performance of the pool resulting from Debug is much worse than that of the
      input Pool, even with all the controls set to false.
Error_check builds a pool that has additional error checking for pointers, in
      particular to detect using a freed pointer or multiply freeing a pointer.
      Error_check has a significant performance cost, but less than that of Debug.
      One can compose Debug and Error_check, e.g:
        module M = Debug (Error_check (Obj_array))