From 58a3abcb4c35ba63acc69da38c033e4e8e9a7db0 Mon Sep 17 00:00:00 2001 From: MBourgoin Date: Mon, 19 Nov 2018 15:53:09 +0100 Subject: [PATCH] StdLib: introduce a WeakRingTable based on ephemerons for bounded cache/memoization purposes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: MBourgoin Co-authored-by: Raphaƫl Proust Co-authored-by: Pietro Abate --- src/lib_stdlib/weakRingTable.ml | 55 +++++++++++++++++++++++++++++++ src/lib_stdlib/weakRingTable.mli | 56 ++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 src/lib_stdlib/weakRingTable.ml create mode 100644 src/lib_stdlib/weakRingTable.mli diff --git a/src/lib_stdlib/weakRingTable.ml b/src/lib_stdlib/weakRingTable.ml new file mode 100644 index 000000000..e0bd2a72f --- /dev/null +++ b/src/lib_stdlib/weakRingTable.ml @@ -0,0 +1,55 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Nomadic Labs, Inc. *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +module Make(M: Hashtbl.HashedType) = struct + + module Table = Ephemeron.K1.Make(M) + module Ring = Ring.MakeTable(M) + + type 'a t = { + table : 'a Table.t ; + ring : Ring.t ; + } + + let create n = { table = Table.create n ; ring = Ring.create n } + + (* IDEA: + * - `create` takes an additional `v -> k` function which is used to + * generate the key (typically `HashTbl.hash` or `SomeModule.hash`) + * - `add` returns that key so that the caller can either + * - keep it to gain some control over the lifetime of the table entry + * - throw it away to let the GC do its thing + * *) + let add { ring ; table } k v = + Ring.add ring k ; + Table.replace table k v + + let find_opt { table ; _ } k = + Table.find_opt table k + + let fold f { table ; _ } acc = + Table.fold f table acc + +end diff --git a/src/lib_stdlib/weakRingTable.mli b/src/lib_stdlib/weakRingTable.mli new file mode 100644 index 000000000..932603050 --- /dev/null +++ b/src/lib_stdlib/weakRingTable.mli @@ -0,0 +1,56 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Nomadic Labs, Inc. *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +module Make (K: Hashtbl.HashedType): sig + + (** A bounded table which optimistically cheats on the bound and sometimes + counts wrong. + Specifically, the table retains a bounded number of elements. It will also + retain more if given more than that, but it will always drop back to the + bound if the garbage collector intervenes. + In addition, repeated bindings on the same key use several slots when + counting the number of elements even though only the last binding is ever + accessible. *) + type 'a t + + (** [create n] is a table with at most [n] elements except when it has more. *) + val create: int -> 'a t + + (** [add t k v] adds a mapping from key [k] to value [v] in the table. + NOTE: when n values are bound to the same key, it may count as up to n + elements. + However, NOTE: when n values are bound to the same key, only the last + binding can be found with [find_opt] or traversed with [fold]. *) + val add: 'a t -> K.t -> 'a -> unit + + (** [fold f t acc] folds over the bindings in [t] starting with [acc]. *) + val fold: (K.t -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b + + (** [find_opt t k] is [Some v] if [k] is bound to [v] in [t] and [None] + otherwise. A key [k] is bound to a value [v] in [t] if [add t k v] has been + called and not too many other bindings have been added since then. *) + val find_opt: 'a t -> K.t -> 'a option + +end