Commit | Line | Data |
---|---|---|
34e49164 C |
1 | open Common |
2 | ||
3 | open Oassoc | |
4 | ||
5 | open Oassocb | |
6 | open Osetb | |
7 | ||
8 | (* todo: limit number of entries, and erase all (then better do a ltu) | |
9 | * todo: another cache that behave as in lfs1, | |
10 | * every 100 operation do a flush | |
11 | * | |
12 | * todo: choose between oassocb and oassoch ? | |
91eba41f C |
13 | * |
14 | * Also take care that must often redefine all function in the original | |
15 | * oassoc.ml because if some methods are not redefined, for instance | |
16 | * #clear, then if do wrapper over a oassocdbm, then even if oassocdbm | |
17 | * redefine #clear, it will not be called, but instead the default | |
18 | * method will be called that internally will call another method. | |
19 | * So better delegate all the methods and override even the method | |
20 | * with a default definition. | |
21 | * | |
22 | * In the same way sometimes an exn can occur at wierd time. When | |
23 | * we add an element, sometimes this may raise an exn such as Out_of_memory, | |
24 | * but as we dont add directly but only at flush time, the exn | |
25 | * may happen far later the user added something in this oassoc. | |
26 | * Also in the case of Out_of_memory, even if the entry is not | |
27 | * added in the wrapped, it will still be present in the cache | |
28 | * and so the next flush will still generate an exn that again | |
29 | * may not be cached. So for the moment if Out_of_memory then | |
30 | * do something special and erase the entry in the cache. | |
34e49164 C |
31 | *) |
32 | ||
33 | (* !!take care!!: this class has side effect, not a pure oassoc *) | |
34 | (* can not make it pure, cos the assoc have side effect on the cache *) | |
35 | class ['a,'b] oassoc_buffer max cached = | |
36 | object(o) | |
37 | inherit ['a,'b] oassoc | |
38 | ||
39 | val counter = ref 0 | |
40 | val cache = ref (new oassocb []) | |
41 | val dirty = ref (new osetb Setb.empty) | |
42 | val wrapped = ref cached | |
43 | ||
44 | method private myflush = | |
91eba41f C |
45 | |
46 | let has_a_raised = ref false in | |
47 | ||
34e49164 | 48 | !dirty#iter (fun k -> |
91eba41f C |
49 | try |
50 | wrapped := !wrapped#add (k, !cache#assoc k) | |
51 | with Out_of_memory -> | |
52 | pr2 "PBBBBBB: Out_of_memory in oassoc_buffer, but still empty cache"; | |
53 | has_a_raised := true; | |
34e49164 C |
54 | ); |
55 | dirty := (new osetb Setb.empty); | |
56 | cache := (new oassocb []); | |
57 | counter := 0; | |
91eba41f C |
58 | if !has_a_raised then raise Out_of_memory |
59 | ||
34e49164 C |
60 | |
61 | method misc_op_hook2 = o#myflush | |
62 | ||
63 | method empty = | |
64 | raise Todo | |
91eba41f C |
65 | |
66 | (* what happens in k is already present ? or if add multiple times | |
67 | * the same k ? cache is a oassocb and so the previous binding is | |
68 | * still there, but dirty is a set, and in myflush we iter based | |
69 | * on dirty so we will flush only the last 'k' in the cache. | |
70 | *) | |
34e49164 C |
71 | method add (k,v) = |
72 | cache := !cache#add (k,v); | |
73 | dirty := !dirty#add k; | |
74 | incr counter; | |
75 | if !counter > max then o#myflush; | |
76 | o | |
77 | ||
78 | method iter f = | |
79 | o#myflush; (* bugfix: have to flush !!! *) | |
80 | !wrapped#iter f | |
81 | ||
82 | ||
91eba41f C |
83 | method keys = |
84 | o#myflush; (* bugfix: have to flush !!! *) | |
85 | !wrapped#keys | |
86 | ||
87 | method clear = | |
88 | o#myflush; (* bugfix: have to flush !!! *) | |
89 | !wrapped#clear | |
90 | ||
91 | ||
34e49164 C |
92 | method length = |
93 | o#myflush; | |
94 | !wrapped#length | |
95 | ||
96 | method view = | |
97 | raise Todo | |
98 | ||
99 | method del (k,v) = | |
100 | cache := !cache#del (k,v); | |
101 | (* TODO as for delkey, do a try over wrapped *) | |
102 | wrapped := !wrapped#del (k,v); | |
103 | dirty := !dirty#del k; | |
104 | o | |
105 | method mem e = raise Todo | |
106 | method null = raise Todo | |
107 | ||
108 | method assoc k = | |
109 | try !cache#assoc k | |
110 | with Not_found -> | |
111 | (* may launch Not_found, but this time, dont catch it *) | |
112 | let v = !wrapped#assoc k in | |
113 | begin | |
114 | cache := !cache#add (k,v); | |
115 | (* otherwise can use too much mem *) | |
116 | incr counter; | |
117 | if !counter > max then o#myflush; | |
118 | v | |
119 | end | |
120 | ||
121 | method delkey k = | |
122 | cache := !cache#delkey k; | |
123 | (* sometimes have not yet flushed, so may not be yet in, (could | |
124 | * also flush in place of doing try). | |
125 | * | |
126 | * TODO would be better to see if was in cache (in case mean that | |
127 | * perhaps not flushed and do try and in other case just cos del | |
128 | * (without try) cos forcement flushed ou was an error *) | |
129 | begin | |
130 | try wrapped := !wrapped#delkey k | |
131 | with Not_found -> () | |
132 | end; | |
133 | dirty := !dirty#del k; | |
134 | o | |
135 | ||
136 | end | |
137 | ||
138 | ||
139 | (* | |
140 | class ['a,'b] oassoc_cache cache cached max = | |
141 | object(o) | |
142 | inherit ['a,'b] oassoc | |
143 | ||
144 | val full = ref 0 | |
145 | val max = max | |
146 | val cache = cache | |
147 | val cached = cached | |
148 | val lru = TODO | |
149 | ||
150 | val data = Hashtbl.create 100 | |
151 | ||
152 | method empty = raise Todo | |
153 | method add (k,v) = (Hashtbl.add data k v; o) | |
154 | method iter f = cached#iter f | |
155 | method view = raise Todo | |
156 | ||
157 | method del (k,v) = (cache#del (k,v); cached#del (k,v); o) | |
158 | method mem e = raise Todo | |
159 | method null = raise Todo | |
160 | ||
161 | method assoc k = Hashtbl.find data k | |
162 | method delkey k = (cache#delkey (k,v); cached#del (k,v); o) | |
163 | end | |
164 | *) | |
165 | ||
166 |