period_repr.ml
(*****************************************************************************)
(* *)
(* Open Source License *)
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.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. *)
(* *)
(*****************************************************************************)
(* `Permanent *)
type error += Malformed_period | Invalid_arg | Period_overflow
let () =
let open Data_encoding in
(* Malformed period *)
register_error_kind
`Permanent
~id:"malformed_period"
~title:"Malformed period"
~description:"Period is negative."
~pp:(fun ppf () -> Format.fprintf ppf "Malformed period")
empty
(function Malformed_period -> Some () | _ -> None)
(fun () -> Malformed_period) ;
(* Invalid arg *)
register_error_kind
`Permanent
~id:"invalid_arg"
~title:"Invalid arg"
~description:"Negative multiple of periods are not allowed."
~pp:(fun ppf () -> Format.fprintf ppf "Invalid arg")
empty
(function Invalid_arg -> Some () | _ -> None)
(fun () -> Invalid_arg) ;
let title = "Period overflow" in
register_error_kind
`Permanent
~id:"period_overflow"
~title
~description:"Last operation generated an integer overflow."
~pp:(fun ppf () -> Format.fprintf ppf "%s" title)
empty
(function Period_overflow -> Some () | _ -> None)
(fun () -> Period_overflow)
(* Internal module implementing natural numbers using int64. These are different
from usual (wrapping up) unsigned integers in that if one overflows the
representation bounds for int64 through [add] or [mul], a [None] value is
returned *)
module Internal : sig
type t = private int64
val create : int64 -> t option
val zero : t
val one : t
val mul : t -> t -> t option
val add : t -> t -> t option
val encoding : t Data_encoding.t
val rpc_arg : t RPC_arg.arg
val pp : Format.formatter -> t -> unit
include Compare.S with type t := t
end = struct
type t = Int64.t
let encoding = Data_encoding.int64
let rpc_arg = RPC_arg.int64
let pp ppf v = Format.fprintf ppf "%Ld" v
include (Compare.Int64 : Compare.S with type t := t)
let zero = 0L
let one = 1L
let create t = if t >= zero then Some t else None
(* The create function is not used in the [mul] and [add] below to not add
extra Some | None pattern matching to handle since the overflow checks are
generic and apply as well to negative as positive integers .
To handle overflows, both [add] and [mul] return option types. [None] is
returned on detected overflow, [Some value] when everything went well. *)
let mul a b =
if a <> zero then
let res = Int64.mul a b in
if Int64.div res a <> b then None else Some res
else Some zero
let add a b =
let res = Int64.add a b in
if res < a || res < b then None else Some res
end
include Internal
type period = Internal.t
let to_seconds (t : Internal.t) = (t :> int64)
let of_seconds secs =
match Internal.create secs with
| Some v ->
ok v
| None ->
error Malformed_period
let of_seconds_exn t =
match Internal.create t with
| Some t ->
t
| None ->
invalid_arg "Period.of_seconds_exn"
let mult i p =
match Internal.create (Int64.of_int32 i) with
| None ->
error Invalid_arg
| Some iper -> (
match Internal.mul iper p with
| None ->
error Period_overflow
| Some res ->
ok res )
let add p1 p2 =
match Internal.add p1 p2 with
| None ->
error Period_overflow
| Some res ->
ok res
let ( +? ) = add
let one_second = Internal.one
let one_minute = of_seconds_exn 60L
let one_hour = of_seconds_exn 3600L