Reviewed By: ngorogiannis Differential Revision: D20199999 fbshipit-source-id: 93d26b822master
parent
db821846d7
commit
9dbc3981cc
@ -0,0 +1,94 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
module F = Format
|
||||
module Hashtbl = Caml.Hashtbl
|
||||
|
||||
module type S = sig
|
||||
type key
|
||||
|
||||
type 'a t
|
||||
|
||||
val create : initial_size:int -> max_size:int -> 'a t
|
||||
|
||||
val find_opt : 'a t -> key -> 'a option
|
||||
|
||||
val replace : 'a t -> key -> 'a -> unit
|
||||
|
||||
val clear : 'a t -> unit
|
||||
|
||||
val pp :
|
||||
pp_key:(F.formatter -> key -> unit)
|
||||
-> pp_v:(F.formatter -> 'a -> unit)
|
||||
-> F.formatter
|
||||
-> 'a t
|
||||
-> unit
|
||||
|
||||
val bindings : 'a t -> (key * 'a) list
|
||||
end
|
||||
|
||||
module Make (Key : Hashtbl.HashedType) = struct
|
||||
type key = Key.t
|
||||
|
||||
module Hash = Hashtbl.Make (Key)
|
||||
|
||||
module LRU = struct
|
||||
type t = {list: Key.t Doubly_linked.t; max: int}
|
||||
|
||||
let create max =
|
||||
assert (max > 0) ;
|
||||
{list= Doubly_linked.create (); max}
|
||||
|
||||
|
||||
let insert_first {list; max} k =
|
||||
let new_node = Doubly_linked.insert_first list k in
|
||||
let removed_key =
|
||||
if Doubly_linked.length list > max then Doubly_linked.remove_last list else None
|
||||
in
|
||||
(new_node, removed_key)
|
||||
|
||||
|
||||
let use {list} n = Doubly_linked.move_to_front list n
|
||||
|
||||
let clear {list} = Doubly_linked.clear list
|
||||
end
|
||||
|
||||
type 'a t = {map: ('a * key Doubly_linked.Elt.t) Hash.t; lru: LRU.t}
|
||||
|
||||
let create ~initial_size ~max_size = {map= Hash.create initial_size; lru= LRU.create max_size}
|
||||
|
||||
let find_opt {map; lru} k =
|
||||
match Hash.find_opt map k with None -> None | Some (v, e) -> LRU.use lru e ; Some v
|
||||
|
||||
|
||||
let replace {map; lru} k v =
|
||||
let n =
|
||||
match Hash.find_opt map k with
|
||||
| None ->
|
||||
let n, removed_key = LRU.insert_first lru k in
|
||||
Option.iter removed_key ~f:(Hash.remove map) ;
|
||||
n
|
||||
| Some (_, n) ->
|
||||
LRU.use lru n ; n
|
||||
in
|
||||
Hash.replace map k (v, n)
|
||||
|
||||
|
||||
let clear {map; lru} = Hash.clear map ; LRU.clear lru
|
||||
|
||||
let pp ~pp_key ~pp_v f {map} =
|
||||
let is_first = ref true in
|
||||
let pp_key_v key (v, _node) =
|
||||
if !is_first then is_first := false else F.pp_print_string f ", " ;
|
||||
F.fprintf f "%a->%a" pp_key key pp_v v
|
||||
in
|
||||
F.pp_print_string f "{" ; Hash.iter pp_key_v map ; F.pp_print_string f "}"
|
||||
|
||||
|
||||
let bindings {map} = Seq.fold_left (fun acc (k, (v, _node)) -> (k, v) :: acc) [] (Hash.to_seq map)
|
||||
end
|
@ -0,0 +1,35 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
|
||||
(** Hash table the size of which is limited by LRU *)
|
||||
|
||||
module type S = sig
|
||||
type key
|
||||
|
||||
type 'a t
|
||||
|
||||
val create : initial_size:int -> max_size:int -> 'a t
|
||||
|
||||
val find_opt : 'a t -> key -> 'a option
|
||||
|
||||
val replace : 'a t -> key -> 'a -> unit
|
||||
|
||||
val clear : 'a t -> unit
|
||||
|
||||
val pp :
|
||||
pp_key:(Format.formatter -> key -> unit)
|
||||
-> pp_v:(Format.formatter -> 'a -> unit)
|
||||
-> Format.formatter
|
||||
-> 'a t
|
||||
-> unit
|
||||
|
||||
val bindings : 'a t -> (key * 'a) list
|
||||
end
|
||||
|
||||
module Make (Key : Caml.Hashtbl.HashedType) : S with type key = Key.t
|
@ -0,0 +1,58 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
open OUnit2
|
||||
module LRUHash = LRUHashtbl.Make (Int)
|
||||
|
||||
let inputs =
|
||||
[ ("empty", (fun () -> LRUHash.create ~initial_size:5 ~max_size:3), [])
|
||||
; ( "singleton"
|
||||
, (fun () ->
|
||||
let map = LRUHash.create ~initial_size:5 ~max_size:3 in
|
||||
LRUHash.replace map 0 10 ; map )
|
||||
, [(0, 10)] )
|
||||
; ( "LRU1"
|
||||
, (fun () ->
|
||||
let map = LRUHash.create ~initial_size:5 ~max_size:3 in
|
||||
LRUHash.replace map 0 10 ;
|
||||
LRUHash.replace map 1 10 ;
|
||||
LRUHash.replace map 2 10 ;
|
||||
let (_ : int option) = LRUHash.find_opt map 1 in
|
||||
LRUHash.replace map 3 10 ; LRUHash.replace map 4 10 ; map )
|
||||
, [(1, 10); (3, 10); (4, 10)] )
|
||||
; ( "LRU2"
|
||||
, (fun () ->
|
||||
let map = LRUHash.create ~initial_size:5 ~max_size:3 in
|
||||
LRUHash.replace map 0 10 ;
|
||||
LRUHash.replace map 1 10 ;
|
||||
LRUHash.replace map 2 10 ;
|
||||
LRUHash.replace map 0 20 ;
|
||||
LRUHash.replace map 3 10 ;
|
||||
map )
|
||||
, [(0, 20); (2, 10); (3, 10)] )
|
||||
; ( "clear"
|
||||
, (fun () ->
|
||||
let map = LRUHash.create ~initial_size:5 ~max_size:3 in
|
||||
LRUHash.replace map 0 10 ;
|
||||
LRUHash.replace map 1 10 ;
|
||||
LRUHash.replace map 2 10 ;
|
||||
LRUHash.clear map ;
|
||||
map )
|
||||
, [] ) ]
|
||||
|
||||
|
||||
let tests =
|
||||
let compare (k1, v1) (k2, v2) =
|
||||
let c = k1 - k2 in
|
||||
if c <> 0 then c else v1 - v2
|
||||
in
|
||||
"LRUHashtble"
|
||||
>::: List.map inputs ~f:(fun (name, input, expected) ->
|
||||
name
|
||||
>:: fun _ -> assert_equal (input () |> LRUHash.bindings |> List.sort ~compare) expected
|
||||
)
|
Loading…
Reference in new issue