package batteries

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file batHashcons.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
(*
 * Hashcons -- a hashconsing library
 * Copyright (C) 2011  Batteries Included Development Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version,
 * with the special exception on linking described in file LICENSE.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *)

(* Most of this code is lifted from J.-C. Fillâtre and S. Conchon's
   implementation:

   http://www.lri.fr/~filliatr/ftp/ocaml/ds/hashcons.ml
*)

module Int = BatInt
module Sys = BatSys
module Hashtbl = BatHashtbl
module Array = BatArray

##V>=5##module Pervasives = Stdlib

type 'a hobj = {
  obj   : 'a ;
  tag   : int ;
  hcode : int ;
}

type 'a t = 'a hobj

let compare ho1 ho2 = Int.compare ho1.tag ho2.tag

let gentag =
  let tags = ref 0 in
  fun () -> incr tags ; !tags

module type Table =
sig
  type key
  type t
  val create   : int -> t
  val clear    : t -> unit
  val hashcons : t -> key -> key hobj
  val iter     : (key hobj -> unit) -> t -> unit
  val fold     : (key hobj -> 'a -> 'a) -> t -> 'a -> 'a
  val count    : t -> int
end

module MakeTable (HT : Hashtbl.HashedType) : Table with type key = HT.t =
struct
  type key = HT.t

  type data = HT.t hobj

  type t = {
    mutable table : data Weak.t array ;
    mutable totsize : int ;             (* sum of the bucket sizes *)
    mutable limit : int ;               (* max ratio totsize/table length *)
  }

  let emptybucket = Weak.create 0

  let create sz =
    let sz = Pervasives.min (Pervasives.max sz 7) (Sys.max_array_length - 1) in
    { table = Array.make sz emptybucket
    ; totsize = 0 ; limit = 3 }

  let clear t =
    Array.modify (fun _ -> emptybucket) t.table ;
    t.totsize <- 0 ;
    t.limit <- 3

  let fold f t init =
    let rec fold_bucket i b accu =
      if i >= Weak.length b then accu else
        match Weak.get b i with
        | Some v -> fold_bucket (i + 1) b (f v accu)
        | None -> fold_bucket (i + 1) b accu
    in
    Array.fold_right (fold_bucket 0) t.table init

  let iter f t =
    let rec iter_bucket i b =
      if i >= Weak.length b then () else
        match Weak.get b i with
        | Some v -> f v ; iter_bucket (i + 1) b
        | None -> iter_bucket (i + 1) b
    in
    Array.iter (iter_bucket 0) t.table

  let count t =
    let rec count_bucket i b accu =
      if i >= Weak.length b then accu else
        count_bucket (i + 1) b (accu + (if Weak.check b i then 1 else 0))
    in
    Array.fold_right (count_bucket 0) t.table 0

  let next_sz n = Pervasives.min (3 * n / 2 + 3) (Sys.max_array_length - 1)

  let rec resize t =
    let oldlen = Array.length t.table in
    let newlen = next_sz oldlen in
    if newlen > oldlen then begin
      let newt = create newlen in
      newt.limit <- t.limit + 100 ;          (* prevent resizing of newt *)
      iter (add newt) t ;
      t.table <- newt.table ;
      t.limit <- t.limit + 2 ;
    end

  and add t d =
    let index = d.hcode mod (Array.length t.table) in
    let bucket = t.table.(index) in
    let sz = Weak.length bucket in
    let rec loop i =
      if i >= sz then begin
        let newsz = Pervasives.min (sz + 3) (Sys.max_array_length - 1) in
        if newsz <= sz then
          failwith "Hashcons.Make: hash bucket cannot grow more" ;
        let newbucket = Weak.create newsz in
        Weak.blit bucket 0 newbucket 0 sz ;
        Weak.set newbucket i (Some d) ;
        t.table.(index) <- newbucket ;
        t.totsize <- t.totsize + (newsz - sz) ;
        if t.totsize > t.limit * Array.length t.table then resize t ;
      end else begin
        if Weak.check bucket i
        then loop (i + 1)
        else Weak.set bucket i (Some d)
      end
    in
    loop 0

  let hashcons t d =
    let hcode = (HT.hash d) land Pervasives.max_int in
    let index = hcode mod (Array.length t.table) in
    let bucket = t.table.(index) in
    let sz = Weak.length bucket in
    let rec loop i =
      if i >= sz then begin
        let hdata = { hcode = hcode ; tag = gentag () ; obj = d } in
        add t hdata ;
        hdata
      end else begin
        match Weak.get_copy bucket i with
        | Some v when HT.equal v.obj d ->
          begin match Weak.get bucket i with
            | Some v -> v
            | None -> loop (i + 1)
          end
        | _ -> loop (i + 1)
      end
    in
    loop 0
end

module H = struct
  let hc0_ h = h
  let hc0 x = x.hcode
  let hc1_ x h = x + 19 * h
  let hc1  x = hc1_ x.hcode
end
OCaml

Innovation. Community. Security.