Raw File
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2022 Nomadic Labs <contact@nomadic-labs.com>                *)
(*                                                                           *)
(* Permission is hereby granted, free of charge, to any person obtaining a   *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense,  *)
(* and/or sell copies of the Software, and to permit persons to whom the     *)
(* Software is furnished to do so, subject to the following conditions:      *)
(*                                                                           *)
(* The above copyright notice and this permission notice shall be included   *)
(* in all copies or substantial portions of the Software.                    *)
(*                                                                           *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL   *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING   *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER       *)
(* DEALINGS IN THE SOFTWARE.                                                 *)
(*                                                                           *)
(*****************************************************************************)

(** In transaction rollups, some values can be replaced by indexes in
    the messages sent from the layer-1 to the layer-2.

    This module provides various type-safe helpers to manipulate these
    particular values. *)

type value_only = Value_only

type index_only = Index_only

type unknown = Unknown

(** An indexable value is a value which can be replaced by an
    integer. The first type parameter determines whether or not this
    replacement has happened already. *)
type (_, 'a) t = private
  | Value : 'a -> (value_only, 'a) t
  | Hidden_value : 'a -> (unknown, 'a) t
  | Index : int32 -> (index_only, 'a) t
  | Hidden_index : int32 -> (unknown, 'a) t

(** The type of indexable values identified as not being indexes. *)
type 'a value = (value_only, 'a) t

(** The type of indexable values identified as being indexes. *)
type 'a index = (index_only, 'a) t

(** The type of indexable values whose content is still unknown. *)
type 'a either = (unknown, 'a) t

(** [value v] wraps [v] into an indexable value identified as not
    being an index. *)
val value : 'a -> 'a value

(** [from_value v] wraps [v] into an indexable value, but forget about
    the nature of the content of the result. *)
val from_value : 'a -> 'a either

(** [index i] wraps [i] into an indexable value identified as being an
    index.

    Returns the error [Index_cannot_be_negative] iff [i <= 0l]. *)
val index : int32 -> 'a index tzresult

(** [from_index i] wraps [i] into an indexable value, but forget about the
    nature of the content of the result.

    Returns the error [Index_cannot_be_negative] iff [i <= 0l]. *)
val from_index : int32 -> 'a either tzresult

(** [index_exn i] wraps [i] into an indexable value identified as
    being an index.

    @raise Invalid_argument iff [i <= 0l]. *)
val index_exn : int32 -> 'a index

(** [from_index_exn i] wraps [i] into an indexable value, but forget
    about the nature of the content of the result.

    @raise Invalid_argument iff [i <= 0l]. *)
val from_index_exn : int32 -> 'a either

(** [compact val_encoding] is a combinator to derive a compact
    encoding for an indexable value of type ['a] from an encoding for
    ['a]. It uses two bits in the shared tag. [00] is used for indexes
    fitting in one byte, [01] for indexes fitting in two bytes, [10]
    for indexes fitting in four bytes, and [11] for the values of type
    ['a]. *)
val compact : 'a Data_encoding.t -> (unknown, 'a) t Data_encoding.Compact.t

val encoding : 'a Data_encoding.t -> (unknown, 'a) t Data_encoding.t

val pp :
  (Format.formatter -> 'a -> unit) -> Format.formatter -> ('state, 'a) t -> unit

(** [destruct x] returns either the index or the (unwrapped) value
    contained in [x].

    {b Note:} If you want to manipulate a value of type ['a value],
    you can use {!value}. *)
val destruct : ('state, 'a) t -> ('a index, 'a) Either.t

(** [forget x] returns an indexable value whose kind of contents has
    been forgotten. *)
val forget : ('state, 'a) t -> (unknown, 'a) t

(** [to_int32 x] unwraps and returns the integer behind [x]. *)
val to_int32 : 'a index -> int32

(** [to_value x] unwraps and returns the value behind [x]. *)
val to_value : 'a value -> 'a

(** [is_value_e err x] unwraps and returns the value behind [x], and
    throws an [err] if [x] is an index. *)
val is_value_e : error:'trace -> ('state, 'a) t -> ('a, 'trace) result

(** [in_memory_size a] returns the number of bytes allocated in RAM for [a]. *)
val in_memory_size :
  ('a -> Cache_memory_helpers.sint) ->
  ('state, 'a) t ->
  Cache_memory_helpers.sint

(** [size a] returns the number of bytes allocated in an inbox to store [a]. *)
val size : ('a -> int) -> ('state, 'a) t -> int

(** [compare f x y] is a total order on indexable values, which
    proceeds as follows.

    {ul {li If both [x] and [y] are a value, then use [f] to compare them.}
        {li If both [x] and [y] are indexes, then uses the
            [Int32.compare] function to compare them.}
        {li Finally, if [x] and [y] have not the same kind, the logic
            is that indexes are smaller than values.}}

    {b Note:} This can be dangerous, as you may end up comparing two
    things that are equivalent (a value and its index) but declare
    they are not equal. *)
val compare : ('a -> 'a -> int) -> ('state, 'a) t -> ('state', 'a) t -> int

(** [compare_values f x y] compares the value [x] and [y] using [f],
    and relies on the type system of OCaml to ensure that [x] and [y]
    are indeed both values. *)
val compare_values : ('a -> 'a -> int) -> 'a value -> 'a value -> int

(** [compare_indexes x y] compares the indexes [x] and [y], and relies
    on the type system of OCaml to ensure that [x] and [y] are indeed
    both indexes. *)
val compare_indexes : 'a index -> 'a index -> int

module type VALUE = sig
  type t

  val encoding : t Data_encoding.t

  val compare : t -> t -> int

  val pp : Format.formatter -> t -> unit
end

module Make (V : VALUE) : sig
  type nonrec 'state t = ('state, V.t) t

  type nonrec index = V.t index

  type nonrec value = V.t value

  type nonrec either = V.t either

  val value : V.t -> value

  val index : int32 -> index tzresult

  val index_exn : int32 -> index

  val compact : either Data_encoding.Compact.t

  val encoding : either Data_encoding.t

  val index_encoding : index Data_encoding.t

  val value_encoding : value Data_encoding.t

  val compare : 'state t -> 'state' t -> int

  val compare_values : value -> value -> int

  val compare_indexes : index -> index -> int

  val pp : Format.formatter -> 'state t -> unit
end

type error += Index_cannot_be_negative of int32
back to top