My working unpac repository
at opam/upstream/thread-table 164 lines 5.1 kB view raw
1type 'v bucket = Nil | Cons of int * 'v * 'v bucket 2 3type 'v t = { 4 mutable rehash : int; 5 mutable buckets : 'v bucket array; 6 mutable length : int; 7} 8 9let[@tail_mod_cons] rec remove_first removed k' = function 10 | Nil -> Nil 11 | Cons (k, v, kvs) -> 12 if k == k' then begin 13 removed := true; 14 kvs 15 end 16 else Cons (k, v, remove_first removed k' kvs) 17 18let[@inline] remove_first removed k' = function 19 | Nil -> Nil 20 | Cons (k, v, kvs) -> 21 if k == k' then begin 22 removed := true; 23 kvs 24 end 25 else Cons (k, v, remove_first removed k' kvs) 26 27let rec find k' = function 28 | Nil -> raise_notrace Not_found 29 | Cons (k, v, kvs) -> if k == k' then v else find k' kvs 30 31let[@tail_mod_cons] rec filter bit chk = function 32 | Nil -> Nil 33 | Cons (k, v, kvs) -> 34 if Mix.int k land bit = chk then Cons (k, v, filter bit chk kvs) 35 else filter bit chk kvs 36 37let[@inline] filter bit chk = function 38 | Nil -> Nil 39 | Cons (k, _, Nil) as kvs -> if Mix.int k land bit = chk then kvs else Nil 40 | Cons (k, v, kvs) -> 41 if Mix.int k land bit = chk then Cons (k, v, filter bit chk kvs) 42 else filter bit chk kvs 43 44let[@tail_mod_cons] rec append kvs tail = 45 match kvs with Nil -> tail | Cons (k, v, kvs) -> Cons (k, v, append kvs tail) 46 47let[@inline] append kvs tail = 48 match kvs with Nil -> tail | Cons (k, v, kvs) -> Cons (k, v, append kvs tail) 49 50let min_buckets = 4 51and max_buckets_div_2 = (Sys.max_array_length + 1) asr 1 52 53let create () = { rehash = 0; buckets = Array.make min_buckets Nil; length = 0 } 54let length t = t.length 55 56let find t k' = 57 let h = Mix.int k' in 58 let buckets = t.buckets in 59 let n = Array.length buckets in 60 let i = h land (n - 1) in 61 find k' (Array.unsafe_get buckets i) 62 63(* Below we use [@poll error] and [@inline never] to ensure that there are no 64 safe-points where thread switches might occur during critical sections. *) 65 66let[@poll error] [@inline never] update_buckets_atomically t old_buckets 67 new_buckets = 68 t.buckets == old_buckets 69 && begin 70 t.buckets <- new_buckets; 71 t.rehash <- 0; 72 true 73 end 74 75let rec maybe_rehash t = 76 let old_buckets = t.buckets in 77 let new_n = t.rehash in 78 if new_n <> 0 then 79 let old_n = Array.length old_buckets in 80 let new_buckets = Array.make new_n Nil in 81 if old_n * 2 = new_n then 82 let new_bit = new_n lsr 1 in 83 let rec loop i = 84 if t.buckets == old_buckets then 85 if old_n <= i then begin 86 if not (update_buckets_atomically t old_buckets new_buckets) then 87 maybe_rehash t 88 end 89 else begin 90 let kvs = Array.unsafe_get old_buckets i in 91 Array.unsafe_set new_buckets i (filter new_bit 0 kvs); 92 Array.unsafe_set new_buckets (i lor new_bit) 93 (filter new_bit new_bit kvs); 94 loop (i + 1) 95 end 96 else maybe_rehash t 97 in 98 loop 0 99 else if old_n = new_n * 2 then 100 let old_bit = old_n lsr 1 in 101 let rec loop i = 102 if t.buckets == old_buckets then 103 if new_n <= i then begin 104 if not (update_buckets_atomically t old_buckets new_buckets) then 105 maybe_rehash t 106 end 107 else begin 108 Array.unsafe_set new_buckets i 109 (append 110 (Array.unsafe_get old_buckets (i + old_bit)) 111 (Array.unsafe_get old_buckets i)); 112 loop (i + 1) 113 end 114 else maybe_rehash t 115 in 116 loop 0 117 else maybe_rehash t 118 119let[@inline] maybe_rehash t = if t.rehash <> 0 then maybe_rehash t 120 121let[@poll error] [@inline never] add_atomically t buckets n i before after = 122 t.rehash = 0 && buckets == t.buckets 123 && before == Array.unsafe_get buckets i 124 && begin 125 Array.unsafe_set buckets i after; 126 let length = t.length + 1 in 127 t.length <- length; 128 if n < length && n < max_buckets_div_2 then t.rehash <- n * 2; 129 true 130 end 131 132let rec add t k' v' = 133 let h = Mix.int k' in 134 maybe_rehash t; 135 let buckets = t.buckets in 136 let n = Array.length buckets in 137 let i = h land (n - 1) in 138 let before = Array.unsafe_get buckets i in 139 let after = Cons (k', v', before) in 140 if not (add_atomically t buckets n i before after) then add t k' v' 141 142let[@poll error] [@inline never] remove_atomically t buckets n i before after 143 removed = 144 t.rehash = 0 && buckets == t.buckets 145 && before == Array.unsafe_get buckets i 146 && ((not !removed) 147 || begin 148 Array.unsafe_set buckets i after; 149 let length = t.length - 1 in 150 t.length <- length; 151 if length * 4 < n && min_buckets < n then t.rehash <- n asr 1; 152 true 153 end) 154 155let rec remove t k' = 156 let h = Mix.int k' in 157 let removed = ref false in 158 maybe_rehash t; 159 let buckets = t.buckets in 160 let n = Array.length buckets in 161 let i = h land (n - 1) in 162 let before = Array.unsafe_get buckets i in 163 let after = remove_first removed k' before in 164 if not (remove_atomically t buckets n i before after removed) then remove t k'