examples

Coverage Report

Created: 2025-05-20 15:50

/home/uke/oil/mycpp/gc_dict.h
Line
Count
Source (jump to first uncovered line)
1
// Hash table based on CPython's "compact dict":
2
//
3
//   https://mail.python.org/pipermail/python-dev/2012-December/123028.html
4
//   https://code.activestate.com/recipes/578375/
5
//
6
// Main differences:
7
// - It's type-specialized in C++ -- Dict<K, V>.
8
// - It's integrated with our mark and sweep GC, using Slab<int>, Slab<K>, and
9
//   Slab<V>
10
// - We use linear probing, not the pseudo-random number generator
11
12
#ifndef MYCPP_GC_DICT_H
13
#define MYCPP_GC_DICT_H
14
15
#include "mycpp/comparators.h"
16
#include "mycpp/gc_builtins.h"
17
#include "mycpp/gc_list.h"
18
#include "mycpp/hash.h"
19
20
// Non-negative entries in index_ are array indices into keys_ and values_.
21
// There are two special negative entries:
22
23
// index_ value to say this Dict item was deleted (a tombstone).
24
const int kDeletedEntry = -1;
25
26
// index_ value to say this Dict entry is free.
27
const int kEmptyEntry = -2;
28
29
// Return value for find_kv_index(), not stored in index_.
30
const int kNotFound = -3;
31
32
// Return value for hash_and_probe(), not stored in index_.
33
const int kTooSmall = -4;
34
35
// Helper for keys() and values()
36
template <typename T>
37
List<T>* ListFromDictSlab(Slab<T>* slab, int n) {
38
  List<T>* result = Alloc<List<T>>();
39
  result->reserve(n);
40
41
  for (int i = 0; i < n; ++i) {
42
    result->append(slab->items_[i]);
43
  }
44
  return result;
45
}
46
47
// GlobalDict is layout-compatible with Dict (unit tests assert this), and it
48
// can be a true C global (incurs zero startup time)
49
50
template <typename K, typename V, int N>
51
class GlobalDict {
52
 public:
53
  int len_;
54
  int capacity_;
55
  int index_len_;
56
  GlobalSlab<int, N>* index_;
57
  GlobalSlab<K, N>* keys_;
58
  GlobalSlab<V, N>* values_;
59
};
60
61
#define GLOBAL_DICT(name, K, V, N, keys, vals)                                 \
62
  GcGlobal<GlobalSlab<K, N>> _keys_##name = {ObjHeader::Global(TypeTag::Slab), \
63
                                             {.items_ = keys}};                \
64
  GcGlobal<GlobalSlab<V, N>> _vals_##name = {ObjHeader::Global(TypeTag::Slab), \
65
                                             {.items_ = vals}};                \
66
  GcGlobal<GlobalDict<K, V, N>> _dict_##name = {                               \
67
      ObjHeader::Global(TypeTag::Dict),                                        \
68
      {.len_ = N,                                                              \
69
       .capacity_ = N,                                                         \
70
       .index_len_ = 0,                                                        \
71
       .index_ = nullptr,                                                      \
72
       .keys_ = &_keys_##name.obj,                                             \
73
       .values_ = &_vals_##name.obj},                                          \
74
  };                                                                           \
75
  Dict<K, V>* name = reinterpret_cast<Dict<K, V>*>(&_dict_##name.obj);
76
77
template <class K, class V>
78
class Dict {
79
 public:
80
  Dict()
81
      : len_(0),
82
        capacity_(0),
83
        index_len_(0),
84
        index_(nullptr),
85
        keys_(nullptr),
86
47
        values_(nullptr) {
87
47
  }
_ZN4DictIP6BigStriEC2Ev
Line
Count
Source
86
4
        values_(nullptr) {
87
4
  }
_ZN4DictIP6BigStrS1_EC2Ev
Line
Count
Source
86
5
        values_(nullptr) {
87
5
  }
_ZN4DictIibEC2Ev
Line
Count
Source
86
38
        values_(nullptr) {
87
38
  }
Unexecuted instantiation: _ZN4DictIiiEC2Ev
88
89
  Dict(std::initializer_list<K> keys, std::initializer_list<V> values)
90
      : len_(0),
91
        capacity_(0),
92
        index_len_(0),
93
        index_(nullptr),
94
        keys_(nullptr),
95
2
        values_(nullptr) {
96
2
    DCHECK(keys.size() == values.size());
97
0
    auto v = values.begin();  // This simulates a "zip" loop
98
3
    for (auto key : keys) {
99
      // note: calls reserve(), and may allocate
100
3
      this->set(key, *v);
101
3
      ++v;
102
3
    }
103
2
  }
_ZN4DictIP6BigStriEC2ESt16initializer_listIS1_ES3_IiE
Line
Count
Source
95
1
        values_(nullptr) {
96
1
    DCHECK(keys.size() == values.size());
97
0
    auto v = values.begin();  // This simulates a "zip" loop
98
2
    for (auto key : keys) {
99
      // note: calls reserve(), and may allocate
100
2
      this->set(key, *v);
101
2
      ++v;
102
2
    }
103
1
  }
_ZN4DictIP6BigStrS1_EC2ESt16initializer_listIS1_ES4_
Line
Count
Source
95
1
        values_(nullptr) {
96
1
    DCHECK(keys.size() == values.size());
97
0
    auto v = values.begin();  // This simulates a "zip" loop
98
1
    for (auto key : keys) {
99
      // note: calls reserve(), and may allocate
100
1
      this->set(key, *v);
101
1
      ++v;
102
1
    }
103
1
  }
104
105
  // Reserve enough space for at LEAST this many key-value pairs.
106
  void reserve(int num_desired);
107
108
  // d[key] in Python: raises KeyError if not found
109
  V at(K key) const;
110
111
  // d.get(key) in Python. (Can't use this if V isn't a pointer!)
112
  V get(K key) const;
113
114
  // Get a key, but return a default if not found.
115
  // expr_parse.py uses this with OTHER_BALANCE
116
  V get(K key, V default_val) const;
117
118
  // Implements d[k] = v.  May resize the dictionary.
119
  void set(K key, V val);
120
121
  void update(List<Tuple2<K, V>*>* pairs);
122
  void update(Dict<K, V>* other);
123
124
  List<K>* keys() const;
125
126
  List<V>* values() const;
127
128
  void clear();
129
130
  // Helper used by find_kv_index(), set(), mylib::dict_erase() in
131
  // gc_mylib.h
132
  // Returns either:
133
  // - the slot for an existing key, or an empty slot for a new key
134
  // - kTooSmall if the table is full
135
  int hash_and_probe(K key) const;
136
137
  // Helper used by at(), get(), dict_contains()
138
  // Given a key, returns either:
139
  // - an index into keys_ and values_
140
  // - kNotFound
141
  int find_kv_index(K key) const;
142
143
49
  static constexpr ObjHeader obj_header() {
144
49
    return ObjHeader::ClassFixed(field_mask(), sizeof(Dict));
145
49
  }
_ZN4DictIP6BigStriE10obj_headerEv
Line
Count
Source
143
5
  static constexpr ObjHeader obj_header() {
144
5
    return ObjHeader::ClassFixed(field_mask(), sizeof(Dict));
145
5
  }
_ZN4DictIP6BigStrS1_E10obj_headerEv
Line
Count
Source
143
6
  static constexpr ObjHeader obj_header() {
144
6
    return ObjHeader::ClassFixed(field_mask(), sizeof(Dict));
145
6
  }
_ZN4DictIibE10obj_headerEv
Line
Count
Source
143
38
  static constexpr ObjHeader obj_header() {
144
38
    return ObjHeader::ClassFixed(field_mask(), sizeof(Dict));
145
38
  }
Unexecuted instantiation: _ZN4DictIiiE10obj_headerEv
146
147
  int len_;        // number of entries (keys and values, almost dense)
148
  int capacity_;   // number of k/v slots
149
  int index_len_;  // number of index slots
150
151
  // These 3 slabs are resized at the same time.
152
  Slab<int>* index_;  // kEmptyEntry, kDeletedEntry, or a valid index into
153
                      // keys_ and values_
154
  Slab<K>* keys_;     // Dict<K, int>
155
  Slab<V>* values_;   // Dict<int, V>
156
157
  // A dict has 3 pointers the GC needs to follow.
158
49
  static constexpr uint32_t field_mask() {
159
49
    return maskbit(offsetof(Dict, index_)) | maskbit(offsetof(Dict, keys_)) |
160
49
           maskbit(offsetof(Dict, values_));
161
49
  }
_ZN4DictIP6BigStriE10field_maskEv
Line
Count
Source
158
5
  static constexpr uint32_t field_mask() {
159
5
    return maskbit(offsetof(Dict, index_)) | maskbit(offsetof(Dict, keys_)) |
160
5
           maskbit(offsetof(Dict, values_));
161
5
  }
_ZN4DictIP6BigStrS1_E10field_maskEv
Line
Count
Source
158
6
  static constexpr uint32_t field_mask() {
159
6
    return maskbit(offsetof(Dict, index_)) | maskbit(offsetof(Dict, keys_)) |
160
6
           maskbit(offsetof(Dict, values_));
161
6
  }
_ZN4DictIibE10field_maskEv
Line
Count
Source
158
38
  static constexpr uint32_t field_mask() {
159
38
    return maskbit(offsetof(Dict, index_)) | maskbit(offsetof(Dict, keys_)) |
160
38
           maskbit(offsetof(Dict, values_));
161
38
  }
Unexecuted instantiation: _ZN4DictIiiE10field_maskEv
162
163
  DISALLOW_COPY_AND_ASSIGN(Dict);
164
165
  // kItemSize is max of K and V size.  That is, on 64-bit machines, the RARE
166
  // Dict<int, int> is smaller than other dicts
167
  static constexpr int kItemSize = sizeof(K) > sizeof(V) ? sizeof(K)
168
                                                         : sizeof(V);
169
170
  // Matches mark_sweep_heap.h
171
  static constexpr int kPoolBytes2 = 48 - sizeof(ObjHeader);
172
  static_assert(kPoolBytes2 % kItemSize == 0,
173
                "An integral number of items should fit in second pool");
174
  static constexpr int kNumItems2 = kPoolBytes2 / kItemSize;
175
176
  static const int kHeaderFudge = sizeof(ObjHeader) / kItemSize;
177
  static_assert(sizeof(ObjHeader) % kItemSize == 0,
178
                "Slab header size should be multiple of key size");
179
180
#if 0
181
  static constexpr int kMinBytes2 = 128 - sizeof(ObjHeader);
182
  static_assert(kMinBytes2 % kItemSize == 0,
183
                "An integral number of items should fit");
184
  static constexpr int kMinItems2 = kMinBytes2 / kItemSize;
185
#endif
186
187
26
  int HowManyPairs(int num_desired) {
188
    // See gc_list.h for comments on nearly identical logic
189
190
26
    if (num_desired <= kNumItems2) {  // use full cell in pool 2
191
26
      return kNumItems2;
192
26
    }
193
#if 0
194
    if (num_desired <= kMinItems2) {  // 48 -> 128, not 48 -> 64
195
      return kMinItems2;
196
    }
197
#endif
198
0
    return RoundUp(num_desired + kHeaderFudge) - kHeaderFudge;
199
26
  }
_ZN4DictIP6BigStriE12HowManyPairsEi
Line
Count
Source
187
4
  int HowManyPairs(int num_desired) {
188
    // See gc_list.h for comments on nearly identical logic
189
190
4
    if (num_desired <= kNumItems2) {  // use full cell in pool 2
191
4
      return kNumItems2;
192
4
    }
193
#if 0
194
    if (num_desired <= kMinItems2) {  // 48 -> 128, not 48 -> 64
195
      return kMinItems2;
196
    }
197
#endif
198
0
    return RoundUp(num_desired + kHeaderFudge) - kHeaderFudge;
199
4
  }
_ZN4DictIP6BigStrS1_E12HowManyPairsEi
Line
Count
Source
187
3
  int HowManyPairs(int num_desired) {
188
    // See gc_list.h for comments on nearly identical logic
189
190
3
    if (num_desired <= kNumItems2) {  // use full cell in pool 2
191
3
      return kNumItems2;
192
3
    }
193
#if 0
194
    if (num_desired <= kMinItems2) {  // 48 -> 128, not 48 -> 64
195
      return kMinItems2;
196
    }
197
#endif
198
0
    return RoundUp(num_desired + kHeaderFudge) - kHeaderFudge;
199
3
  }
_ZN4DictIibE12HowManyPairsEi
Line
Count
Source
187
19
  int HowManyPairs(int num_desired) {
188
    // See gc_list.h for comments on nearly identical logic
189
190
19
    if (num_desired <= kNumItems2) {  // use full cell in pool 2
191
19
      return kNumItems2;
192
19
    }
193
#if 0
194
    if (num_desired <= kMinItems2) {  // 48 -> 128, not 48 -> 64
195
      return kMinItems2;
196
    }
197
#endif
198
0
    return RoundUp(num_desired + kHeaderFudge) - kHeaderFudge;
199
19
  }
200
};
201
202
template <typename K, typename V>
203
58
inline bool dict_contains(const Dict<K, V>* haystack, K needle) {
204
58
  return haystack->find_kv_index(needle) != kNotFound;
205
58
}
_Z13dict_containsIP6BigStriEbPK4DictIT_T0_ES3_
Line
Count
Source
203
1
inline bool dict_contains(const Dict<K, V>* haystack, K needle) {
204
1
  return haystack->find_kv_index(needle) != kNotFound;
205
1
}
_Z13dict_containsIibEbPK4DictIT_T0_ES1_
Line
Count
Source
203
57
inline bool dict_contains(const Dict<K, V>* haystack, K needle) {
204
57
  return haystack->find_kv_index(needle) != kNotFound;
205
57
}
206
207
template <typename K, typename V>
208
26
void Dict<K, V>::reserve(int num_desired) {
209
26
  if (capacity_ >= num_desired) {
210
0
    return;  // Don't do anything if there's already enough space.
211
0
  }
212
213
26
  int old_len = len_;
214
26
  Slab<K>* old_k = keys_;
215
26
  Slab<V>* old_v = values_;
216
217
  // Calculate the number of keys and values we should have
218
26
  capacity_ = HowManyPairs(num_desired);
219
220
  // 1) Ensure index len a power of 2, to avoid expensive modulus % operation
221
  // 2) Introduce hash table load factor.   Use capacity_+1 to simulate ceil()
222
  // div, not floor() div.
223
26
  index_len_ = RoundUp((capacity_ + 1) * 5 / 4);
224
26
  DCHECK(index_len_ > capacity_);
225
226
0
  index_ = NewSlab<int>(index_len_);
227
386
  for (int i = 0; i < index_len_; ++i) {
228
360
    index_->items_[i] = kEmptyEntry;
229
360
  }
230
231
  // These are DENSE, while index_ is sparse.
232
26
  keys_ = NewSlab<K>(capacity_);
233
26
  values_ = NewSlab<V>(capacity_);
234
235
26
  if (old_k != nullptr) {  // rehash if there were any entries
236
    // log("REHASH num_desired %d", num_desired);
237
0
    len_ = 0;
238
0
    for (int i = 0; i < old_len; ++i) {
239
0
      set(old_k->items_[i], old_v->items_[i]);
240
0
    }
241
0
  }
242
26
}
_ZN4DictIP6BigStriE7reserveEi
Line
Count
Source
208
4
void Dict<K, V>::reserve(int num_desired) {
209
4
  if (capacity_ >= num_desired) {
210
0
    return;  // Don't do anything if there's already enough space.
211
0
  }
212
213
4
  int old_len = len_;
214
4
  Slab<K>* old_k = keys_;
215
4
  Slab<V>* old_v = values_;
216
217
  // Calculate the number of keys and values we should have
218
4
  capacity_ = HowManyPairs(num_desired);
219
220
  // 1) Ensure index len a power of 2, to avoid expensive modulus % operation
221
  // 2) Introduce hash table load factor.   Use capacity_+1 to simulate ceil()
222
  // div, not floor() div.
223
4
  index_len_ = RoundUp((capacity_ + 1) * 5 / 4);
224
4
  DCHECK(index_len_ > capacity_);
225
226
0
  index_ = NewSlab<int>(index_len_);
227
36
  for (int i = 0; i < index_len_; ++i) {
228
32
    index_->items_[i] = kEmptyEntry;
229
32
  }
230
231
  // These are DENSE, while index_ is sparse.
232
4
  keys_ = NewSlab<K>(capacity_);
233
4
  values_ = NewSlab<V>(capacity_);
234
235
4
  if (old_k != nullptr) {  // rehash if there were any entries
236
    // log("REHASH num_desired %d", num_desired);
237
0
    len_ = 0;
238
0
    for (int i = 0; i < old_len; ++i) {
239
0
      set(old_k->items_[i], old_v->items_[i]);
240
0
    }
241
0
  }
242
4
}
_ZN4DictIP6BigStrS1_E7reserveEi
Line
Count
Source
208
3
void Dict<K, V>::reserve(int num_desired) {
209
3
  if (capacity_ >= num_desired) {
210
0
    return;  // Don't do anything if there's already enough space.
211
0
  }
212
213
3
  int old_len = len_;
214
3
  Slab<K>* old_k = keys_;
215
3
  Slab<V>* old_v = values_;
216
217
  // Calculate the number of keys and values we should have
218
3
  capacity_ = HowManyPairs(num_desired);
219
220
  // 1) Ensure index len a power of 2, to avoid expensive modulus % operation
221
  // 2) Introduce hash table load factor.   Use capacity_+1 to simulate ceil()
222
  // div, not floor() div.
223
3
  index_len_ = RoundUp((capacity_ + 1) * 5 / 4);
224
3
  DCHECK(index_len_ > capacity_);
225
226
0
  index_ = NewSlab<int>(index_len_);
227
27
  for (int i = 0; i < index_len_; ++i) {
228
24
    index_->items_[i] = kEmptyEntry;
229
24
  }
230
231
  // These are DENSE, while index_ is sparse.
232
3
  keys_ = NewSlab<K>(capacity_);
233
3
  values_ = NewSlab<V>(capacity_);
234
235
3
  if (old_k != nullptr) {  // rehash if there were any entries
236
    // log("REHASH num_desired %d", num_desired);
237
0
    len_ = 0;
238
0
    for (int i = 0; i < old_len; ++i) {
239
0
      set(old_k->items_[i], old_v->items_[i]);
240
0
    }
241
0
  }
242
3
}
_ZN4DictIibE7reserveEi
Line
Count
Source
208
19
void Dict<K, V>::reserve(int num_desired) {
209
19
  if (capacity_ >= num_desired) {
210
0
    return;  // Don't do anything if there's already enough space.
211
0
  }
212
213
19
  int old_len = len_;
214
19
  Slab<K>* old_k = keys_;
215
19
  Slab<V>* old_v = values_;
216
217
  // Calculate the number of keys and values we should have
218
19
  capacity_ = HowManyPairs(num_desired);
219
220
  // 1) Ensure index len a power of 2, to avoid expensive modulus % operation
221
  // 2) Introduce hash table load factor.   Use capacity_+1 to simulate ceil()
222
  // div, not floor() div.
223
19
  index_len_ = RoundUp((capacity_ + 1) * 5 / 4);
224
19
  DCHECK(index_len_ > capacity_);
225
226
0
  index_ = NewSlab<int>(index_len_);
227
323
  for (int i = 0; i < index_len_; ++i) {
228
304
    index_->items_[i] = kEmptyEntry;
229
304
  }
230
231
  // These are DENSE, while index_ is sparse.
232
19
  keys_ = NewSlab<K>(capacity_);
233
19
  values_ = NewSlab<V>(capacity_);
234
235
19
  if (old_k != nullptr) {  // rehash if there were any entries
236
    // log("REHASH num_desired %d", num_desired);
237
0
    len_ = 0;
238
0
    for (int i = 0; i < old_len; ++i) {
239
0
      set(old_k->items_[i], old_v->items_[i]);
240
0
    }
241
0
  }
242
19
}
243
244
template <typename K, typename V>
245
8
V Dict<K, V>::at(K key) const {
246
8
  int kv_index = find_kv_index(key);
247
8
  if (kv_index == kNotFound) {
248
0
    throw Alloc<KeyError>();
249
8
  } else {
250
8
    return values_->items_[kv_index];
251
8
  }
252
8
}
_ZNK4DictIP6BigStriE2atES1_
Line
Count
Source
245
5
V Dict<K, V>::at(K key) const {
246
5
  int kv_index = find_kv_index(key);
247
5
  if (kv_index == kNotFound) {
248
0
    throw Alloc<KeyError>();
249
5
  } else {
250
5
    return values_->items_[kv_index];
251
5
  }
252
5
}
_ZNK4DictIiP6BigStrE2atEi
Line
Count
Source
245
1
V Dict<K, V>::at(K key) const {
246
1
  int kv_index = find_kv_index(key);
247
1
  if (kv_index == kNotFound) {
248
0
    throw Alloc<KeyError>();
249
1
  } else {
250
1
    return values_->items_[kv_index];
251
1
  }
252
1
}
_ZNK4DictIP6BigStrS1_E2atES1_
Line
Count
Source
245
2
V Dict<K, V>::at(K key) const {
246
2
  int kv_index = find_kv_index(key);
247
2
  if (kv_index == kNotFound) {
248
0
    throw Alloc<KeyError>();
249
2
  } else {
250
2
    return values_->items_[kv_index];
251
2
  }
252
2
}
253
254
template <typename K, typename V>
255
1
V Dict<K, V>::get(K key) const {
256
1
  int kv_index = find_kv_index(key);
257
1
  if (kv_index == kNotFound) {
258
1
    return nullptr;
259
1
  } else {
260
0
    return values_->items_[kv_index];
261
0
  }
262
1
}
263
264
template <typename K, typename V>
265
V Dict<K, V>::get(K key, V default_val) const {
266
  int kv_index = find_kv_index(key);
267
  if (kv_index == kNotFound) {
268
    return default_val;
269
  } else {
270
    return values_->items_[kv_index];
271
  }
272
}
273
274
template <typename K, typename V>
275
List<K>* Dict<K, V>::keys() const {
276
  return ListFromDictSlab<K>(keys_, len_);
277
}
278
279
template <typename K, typename V>
280
List<V>* Dict<K, V>::values() const {
281
  return ListFromDictSlab<V>(values_, len_);
282
}
283
284
template <typename K, typename V>
285
19
void Dict<K, V>::clear() {
286
  // Maintain invariant
287
19
  for (int i = 0; i < index_len_; ++i) {
288
0
    index_->items_[i] = kEmptyEntry;
289
0
  }
290
291
19
  if (keys_) {
292
0
    memset(keys_->items_, 0, len_ * sizeof(K));  // zero for GC scan
293
0
  }
294
19
  if (values_) {
295
0
    memset(values_->items_, 0, len_ * sizeof(V));  // zero for GC scan
296
0
  }
297
19
  len_ = 0;
298
19
}
299
300
// TODO:
301
// - Special case to intern BigStr* when it's hashed?  How?
302
//   - Should we have wrappers like:
303
//   - V GetAndIntern<V>(D, &string_key)
304
//   - SetAndIntern<V>(D, &string_key, value)
305
//   This will enable duplicate copies of the string to be garbage collected
306
template <typename K, typename V>
307
141
int Dict<K, V>::hash_and_probe(K key) const {
308
141
  if (capacity_ == 0) {
309
26
    return kTooSmall;
310
26
  }
311
312
  // Hash the key onto a slot in the index. If the first slot is occupied,
313
  // probe until an empty one is found.
314
115
  unsigned h = hash_key(key);
315
  // faster % using & -- assuming index_len_ is power of 2
316
115
  int init_bucket = h & (index_len_ - 1);
317
318
  // If we see a tombstone along the probing path, stash it.
319
115
  int open_slot = -1;
320
321
123
  for (int i = 0; i < index_len_; ++i) {
322
    // Start at init_bucket and wrap araound
323
324
    // faster % using & -- assuming index_len_ is power of 2
325
123
    int slot = (i + init_bucket) & (index_len_ - 1);
326
327
123
    int kv_index = index_->items_[slot];
328
123
    DCHECK(kv_index < len_);
329
    // Optimistically this is the common case once the table has been populated.
330
123
    if (kv_index >= 0) {
331
15
      unsigned h2 = hash_key(keys_->items_[kv_index]);
332
15
      if (h == h2 && keys_equal(keys_->items_[kv_index], key)) {
333
7
        return slot;
334
7
      }
335
15
    }
336
337
116
    if (kv_index == kEmptyEntry) {
338
108
      if (open_slot != -1) {
339
0
        slot = open_slot;
340
0
      }
341
      // If there isn't room in the entry arrays, tell the caller to resize.
342
108
      return len_ < capacity_ ? slot : kTooSmall;
343
108
    }
344
345
    // Tombstone or collided keys unequal. Keep scanning.
346
8
    DCHECK(kv_index >= 0 || kv_index == kDeletedEntry);
347
8
    if (kv_index == kDeletedEntry && open_slot == -1) {
348
      // NOTE: We only record the open slot here. We DON'T return it. If we're
349
      // looking for a key that was writen before this tombstone was written to
350
      // the index we should continue probing until we get to that key. If we
351
      // get to an empty index slot or the end of the index then we know we are
352
      // dealing with a new key and can safely replace the tombstone without
353
      // disrupting any existing keys.
354
0
      open_slot = slot;
355
0
    }
356
8
  }
357
358
0
  if (open_slot != -1) {
359
0
    return len_ < capacity_ ? open_slot : kTooSmall;
360
0
  }
361
362
0
  return kTooSmall;
363
0
}
_ZNK4DictIP6BigStriE14hash_and_probeES1_
Line
Count
Source
307
19
int Dict<K, V>::hash_and_probe(K key) const {
308
19
  if (capacity_ == 0) {
309
4
    return kTooSmall;
310
4
  }
311
312
  // Hash the key onto a slot in the index. If the first slot is occupied,
313
  // probe until an empty one is found.
314
15
  unsigned h = hash_key(key);
315
  // faster % using & -- assuming index_len_ is power of 2
316
15
  int init_bucket = h & (index_len_ - 1);
317
318
  // If we see a tombstone along the probing path, stash it.
319
15
  int open_slot = -1;
320
321
21
  for (int i = 0; i < index_len_; ++i) {
322
    // Start at init_bucket and wrap araound
323
324
    // faster % using & -- assuming index_len_ is power of 2
325
21
    int slot = (i + init_bucket) & (index_len_ - 1);
326
327
21
    int kv_index = index_->items_[slot];
328
21
    DCHECK(kv_index < len_);
329
    // Optimistically this is the common case once the table has been populated.
330
21
    if (kv_index >= 0) {
331
12
      unsigned h2 = hash_key(keys_->items_[kv_index]);
332
12
      if (h == h2 && keys_equal(keys_->items_[kv_index], key)) {
333
6
        return slot;
334
6
      }
335
12
    }
336
337
15
    if (kv_index == kEmptyEntry) {
338
9
      if (open_slot != -1) {
339
0
        slot = open_slot;
340
0
      }
341
      // If there isn't room in the entry arrays, tell the caller to resize.
342
9
      return len_ < capacity_ ? slot : kTooSmall;
343
9
    }
344
345
    // Tombstone or collided keys unequal. Keep scanning.
346
6
    DCHECK(kv_index >= 0 || kv_index == kDeletedEntry);
347
6
    if (kv_index == kDeletedEntry && open_slot == -1) {
348
      // NOTE: We only record the open slot here. We DON'T return it. If we're
349
      // looking for a key that was writen before this tombstone was written to
350
      // the index we should continue probing until we get to that key. If we
351
      // get to an empty index slot or the end of the index then we know we are
352
      // dealing with a new key and can safely replace the tombstone without
353
      // disrupting any existing keys.
354
0
      open_slot = slot;
355
0
    }
356
6
  }
357
358
0
  if (open_slot != -1) {
359
0
    return len_ < capacity_ ? open_slot : kTooSmall;
360
0
  }
361
362
0
  return kTooSmall;
363
0
}
Unexecuted instantiation: _ZNK4DictIiP6BigStrE14hash_and_probeEi
_ZNK4DictIP6BigStrS1_E14hash_and_probeES1_
Line
Count
Source
307
8
int Dict<K, V>::hash_and_probe(K key) const {
308
8
  if (capacity_ == 0) {
309
3
    return kTooSmall;
310
3
  }
311
312
  // Hash the key onto a slot in the index. If the first slot is occupied,
313
  // probe until an empty one is found.
314
5
  unsigned h = hash_key(key);
315
  // faster % using & -- assuming index_len_ is power of 2
316
5
  int init_bucket = h & (index_len_ - 1);
317
318
  // If we see a tombstone along the probing path, stash it.
319
5
  int open_slot = -1;
320
321
5
  for (int i = 0; i < index_len_; ++i) {
322
    // Start at init_bucket and wrap araound
323
324
    // faster % using & -- assuming index_len_ is power of 2
325
5
    int slot = (i + init_bucket) & (index_len_ - 1);
326
327
5
    int kv_index = index_->items_[slot];
328
5
    DCHECK(kv_index < len_);
329
    // Optimistically this is the common case once the table has been populated.
330
5
    if (kv_index >= 0) {
331
1
      unsigned h2 = hash_key(keys_->items_[kv_index]);
332
1
      if (h == h2 && keys_equal(keys_->items_[kv_index], key)) {
333
1
        return slot;
334
1
      }
335
1
    }
336
337
4
    if (kv_index == kEmptyEntry) {
338
4
      if (open_slot != -1) {
339
0
        slot = open_slot;
340
0
      }
341
      // If there isn't room in the entry arrays, tell the caller to resize.
342
4
      return len_ < capacity_ ? slot : kTooSmall;
343
4
    }
344
345
    // Tombstone or collided keys unequal. Keep scanning.
346
0
    DCHECK(kv_index >= 0 || kv_index == kDeletedEntry);
347
0
    if (kv_index == kDeletedEntry && open_slot == -1) {
348
      // NOTE: We only record the open slot here. We DON'T return it. If we're
349
      // looking for a key that was writen before this tombstone was written to
350
      // the index we should continue probing until we get to that key. If we
351
      // get to an empty index slot or the end of the index then we know we are
352
      // dealing with a new key and can safely replace the tombstone without
353
      // disrupting any existing keys.
354
0
      open_slot = slot;
355
0
    }
356
0
  }
357
358
0
  if (open_slot != -1) {
359
0
    return len_ < capacity_ ? open_slot : kTooSmall;
360
0
  }
361
362
0
  return kTooSmall;
363
0
}
_ZNK4DictIibE14hash_and_probeEi
Line
Count
Source
307
114
int Dict<K, V>::hash_and_probe(K key) const {
308
114
  if (capacity_ == 0) {
309
19
    return kTooSmall;
310
19
  }
311
312
  // Hash the key onto a slot in the index. If the first slot is occupied,
313
  // probe until an empty one is found.
314
95
  unsigned h = hash_key(key);
315
  // faster % using & -- assuming index_len_ is power of 2
316
95
  int init_bucket = h & (index_len_ - 1);
317
318
  // If we see a tombstone along the probing path, stash it.
319
95
  int open_slot = -1;
320
321
97
  for (int i = 0; i < index_len_; ++i) {
322
    // Start at init_bucket and wrap araound
323
324
    // faster % using & -- assuming index_len_ is power of 2
325
97
    int slot = (i + init_bucket) & (index_len_ - 1);
326
327
97
    int kv_index = index_->items_[slot];
328
97
    DCHECK(kv_index < len_);
329
    // Optimistically this is the common case once the table has been populated.
330
97
    if (kv_index >= 0) {
331
2
      unsigned h2 = hash_key(keys_->items_[kv_index]);
332
2
      if (h == h2 && keys_equal(keys_->items_[kv_index], key)) {
333
0
        return slot;
334
0
      }
335
2
    }
336
337
97
    if (kv_index == kEmptyEntry) {
338
95
      if (open_slot != -1) {
339
0
        slot = open_slot;
340
0
      }
341
      // If there isn't room in the entry arrays, tell the caller to resize.
342
95
      return len_ < capacity_ ? slot : kTooSmall;
343
95
    }
344
345
    // Tombstone or collided keys unequal. Keep scanning.
346
2
    DCHECK(kv_index >= 0 || kv_index == kDeletedEntry);
347
2
    if (kv_index == kDeletedEntry && open_slot == -1) {
348
      // NOTE: We only record the open slot here. We DON'T return it. If we're
349
      // looking for a key that was writen before this tombstone was written to
350
      // the index we should continue probing until we get to that key. If we
351
      // get to an empty index slot or the end of the index then we know we are
352
      // dealing with a new key and can safely replace the tombstone without
353
      // disrupting any existing keys.
354
0
      open_slot = slot;
355
0
    }
356
2
  }
357
358
0
  if (open_slot != -1) {
359
0
    return len_ < capacity_ ? open_slot : kTooSmall;
360
0
  }
361
362
0
  return kTooSmall;
363
0
}
364
365
template <typename K, typename V>
366
67
int Dict<K, V>::find_kv_index(K key) const {
367
67
  if (index_ != nullptr) {  // Common case
368
44
    int pos = hash_and_probe(key);
369
44
    if (pos == kTooSmall) {
370
0
      return kNotFound;
371
0
    }
372
44
    DCHECK(pos >= 0);
373
0
    int kv_index = index_->items_[pos];
374
44
    if (kv_index < 0) {
375
38
      return kNotFound;
376
38
    }
377
6
    return kv_index;
378
44
  }
379
380
  // Linear search on GlobalDict instances.
381
  // TODO: Should we populate and compare their hash values?
382
23
  for (int i = 0; i < len_; ++i) {
383
3
    if (keys_equal(keys_->items_[i], key)) {
384
3
      return i;
385
3
    }
386
3
  }
387
388
20
  return kNotFound;
389
23
}
_ZNK4DictIP6BigStriE13find_kv_indexES1_
Line
Count
Source
366
6
int Dict<K, V>::find_kv_index(K key) const {
367
6
  if (index_ != nullptr) {  // Common case
368
5
    int pos = hash_and_probe(key);
369
5
    if (pos == kTooSmall) {
370
0
      return kNotFound;
371
0
    }
372
5
    DCHECK(pos >= 0);
373
0
    int kv_index = index_->items_[pos];
374
5
    if (kv_index < 0) {
375
0
      return kNotFound;
376
0
    }
377
5
    return kv_index;
378
5
  }
379
380
  // Linear search on GlobalDict instances.
381
  // TODO: Should we populate and compare their hash values?
382
1
  for (int i = 0; i < len_; ++i) {
383
1
    if (keys_equal(keys_->items_[i], key)) {
384
1
      return i;
385
1
    }
386
1
  }
387
388
0
  return kNotFound;
389
1
}
_ZNK4DictIiP6BigStrE13find_kv_indexEi
Line
Count
Source
366
1
int Dict<K, V>::find_kv_index(K key) const {
367
1
  if (index_ != nullptr) {  // Common case
368
0
    int pos = hash_and_probe(key);
369
0
    if (pos == kTooSmall) {
370
0
      return kNotFound;
371
0
    }
372
0
    DCHECK(pos >= 0);
373
0
    int kv_index = index_->items_[pos];
374
0
    if (kv_index < 0) {
375
0
      return kNotFound;
376
0
    }
377
0
    return kv_index;
378
0
  }
379
380
  // Linear search on GlobalDict instances.
381
  // TODO: Should we populate and compare their hash values?
382
1
  for (int i = 0; i < len_; ++i) {
383
1
    if (keys_equal(keys_->items_[i], key)) {
384
1
      return i;
385
1
    }
386
1
  }
387
388
0
  return kNotFound;
389
1
}
_ZNK4DictIP6BigStrS1_E13find_kv_indexES1_
Line
Count
Source
366
3
int Dict<K, V>::find_kv_index(K key) const {
367
3
  if (index_ != nullptr) {  // Common case
368
1
    int pos = hash_and_probe(key);
369
1
    if (pos == kTooSmall) {
370
0
      return kNotFound;
371
0
    }
372
1
    DCHECK(pos >= 0);
373
0
    int kv_index = index_->items_[pos];
374
1
    if (kv_index < 0) {
375
0
      return kNotFound;
376
0
    }
377
1
    return kv_index;
378
1
  }
379
380
  // Linear search on GlobalDict instances.
381
  // TODO: Should we populate and compare their hash values?
382
2
  for (int i = 0; i < len_; ++i) {
383
1
    if (keys_equal(keys_->items_[i], key)) {
384
1
      return i;
385
1
    }
386
1
  }
387
388
1
  return kNotFound;
389
2
}
_ZNK4DictIibE13find_kv_indexEi
Line
Count
Source
366
57
int Dict<K, V>::find_kv_index(K key) const {
367
57
  if (index_ != nullptr) {  // Common case
368
38
    int pos = hash_and_probe(key);
369
38
    if (pos == kTooSmall) {
370
0
      return kNotFound;
371
0
    }
372
38
    DCHECK(pos >= 0);
373
0
    int kv_index = index_->items_[pos];
374
38
    if (kv_index < 0) {
375
38
      return kNotFound;
376
38
    }
377
0
    return kv_index;
378
38
  }
379
380
  // Linear search on GlobalDict instances.
381
  // TODO: Should we populate and compare their hash values?
382
19
  for (int i = 0; i < len_; ++i) {
383
0
    if (keys_equal(keys_->items_[i], key)) {
384
0
      return i;
385
0
    }
386
0
  }
387
388
19
  return kNotFound;
389
19
}
390
391
template <typename K, typename V>
392
71
void Dict<K, V>::set(K key, V val) {
393
71
  DCHECK(obj_header().heap_tag != HeapTag::Global);
394
0
  int pos = hash_and_probe(key);
395
71
  if (pos == kTooSmall) {
396
26
    reserve(len_ + 1);
397
26
    pos = hash_and_probe(key);
398
26
  }
399
71
  DCHECK(pos >= 0);
400
401
0
  int kv_index = index_->items_[pos];
402
71
  DCHECK(kv_index < len_);
403
71
  if (kv_index < 0) {
404
    // Write new entries to the end of the k/v arrays. This allows us to recall
405
    // insertion order until the first deletion.
406
70
    keys_->items_[len_] = key;
407
70
    values_->items_[len_] = val;
408
70
    index_->items_[pos] = len_;
409
70
    len_++;
410
70
    DCHECK(len_ <= capacity_);
411
70
  } else {
412
1
    values_->items_[kv_index] = val;
413
1
  }
414
71
}
_ZN4DictIP6BigStriE3setES1_i
Line
Count
Source
392
10
void Dict<K, V>::set(K key, V val) {
393
10
  DCHECK(obj_header().heap_tag != HeapTag::Global);
394
0
  int pos = hash_and_probe(key);
395
10
  if (pos == kTooSmall) {
396
4
    reserve(len_ + 1);
397
4
    pos = hash_and_probe(key);
398
4
  }
399
10
  DCHECK(pos >= 0);
400
401
0
  int kv_index = index_->items_[pos];
402
10
  DCHECK(kv_index < len_);
403
10
  if (kv_index < 0) {
404
    // Write new entries to the end of the k/v arrays. This allows us to recall
405
    // insertion order until the first deletion.
406
9
    keys_->items_[len_] = key;
407
9
    values_->items_[len_] = val;
408
9
    index_->items_[pos] = len_;
409
9
    len_++;
410
9
    DCHECK(len_ <= capacity_);
411
9
  } else {
412
1
    values_->items_[kv_index] = val;
413
1
  }
414
10
}
_ZN4DictIP6BigStrS1_E3setES1_S1_
Line
Count
Source
392
4
void Dict<K, V>::set(K key, V val) {
393
4
  DCHECK(obj_header().heap_tag != HeapTag::Global);
394
0
  int pos = hash_and_probe(key);
395
4
  if (pos == kTooSmall) {
396
3
    reserve(len_ + 1);
397
3
    pos = hash_and_probe(key);
398
3
  }
399
4
  DCHECK(pos >= 0);
400
401
0
  int kv_index = index_->items_[pos];
402
4
  DCHECK(kv_index < len_);
403
4
  if (kv_index < 0) {
404
    // Write new entries to the end of the k/v arrays. This allows us to recall
405
    // insertion order until the first deletion.
406
4
    keys_->items_[len_] = key;
407
4
    values_->items_[len_] = val;
408
4
    index_->items_[pos] = len_;
409
4
    len_++;
410
4
    DCHECK(len_ <= capacity_);
411
4
  } else {
412
0
    values_->items_[kv_index] = val;
413
0
  }
414
4
}
_ZN4DictIibE3setEib
Line
Count
Source
392
57
void Dict<K, V>::set(K key, V val) {
393
57
  DCHECK(obj_header().heap_tag != HeapTag::Global);
394
0
  int pos = hash_and_probe(key);
395
57
  if (pos == kTooSmall) {
396
19
    reserve(len_ + 1);
397
19
    pos = hash_and_probe(key);
398
19
  }
399
57
  DCHECK(pos >= 0);
400
401
0
  int kv_index = index_->items_[pos];
402
57
  DCHECK(kv_index < len_);
403
57
  if (kv_index < 0) {
404
    // Write new entries to the end of the k/v arrays. This allows us to recall
405
    // insertion order until the first deletion.
406
57
    keys_->items_[len_] = key;
407
57
    values_->items_[len_] = val;
408
57
    index_->items_[pos] = len_;
409
57
    len_++;
410
57
    DCHECK(len_ <= capacity_);
411
57
  } else {
412
0
    values_->items_[kv_index] = val;
413
0
  }
414
57
}
415
416
template <typename K, typename V>
417
5
inline int len(const Dict<K, V>* d) {
418
5
  return d->len_;
419
5
}
_Z3lenIP6BigStrS1_EiPK4DictIT_T0_E
Line
Count
Source
417
3
inline int len(const Dict<K, V>* d) {
418
3
  return d->len_;
419
3
}
_Z3lenIP6BigStriEiPK4DictIT_T0_E
Line
Count
Source
417
1
inline int len(const Dict<K, V>* d) {
418
1
  return d->len_;
419
1
}
_Z3lenIiP6BigStrEiPK4DictIT_T0_E
Line
Count
Source
417
1
inline int len(const Dict<K, V>* d) {
418
1
  return d->len_;
419
1
}
420
421
template <class K, class V>
422
class DictIter {
423
 public:
424
3
  explicit DictIter(Dict<K, V>* D) : D_(D), pos_(0) {
425
3
  }
_ZN8DictIterIP6BigStriEC2EP4DictIS1_iE
Line
Count
Source
424
3
  explicit DictIter(Dict<K, V>* D) : D_(D), pos_(0) {
425
3
  }
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN10value_asdl7value_tEEC2EP4DictIS1_S4_E
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN12runtime_asdl7HayNodeEEC2EP4DictIS1_S4_E
Unexecuted instantiation: _ZN8DictIterIlP6BigStrEC2EP4DictIlS1_E
Unexecuted instantiation: _ZN8DictIterIP6BigStrS1_EC2EP4DictIS1_S1_E
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN12runtime_asdl4CellEEC2EP4DictIS1_S4_E
426
9
  void Next() {
427
9
    pos_++;
428
9
  }
_ZN8DictIterIP6BigStriE4NextEv
Line
Count
Source
426
9
  void Next() {
427
9
    pos_++;
428
9
  }
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN10value_asdl7value_tEE4NextEv
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN12runtime_asdl7HayNodeEE4NextEv
Unexecuted instantiation: _ZN8DictIterIlP6BigStrE4NextEv
Unexecuted instantiation: _ZN8DictIterIP6BigStrS1_E4NextEv
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN12runtime_asdl4CellEE4NextEv
429
12
  bool Done() {
430
12
    return pos_ == D_->len_;
431
12
  }
_ZN8DictIterIP6BigStriE4DoneEv
Line
Count
Source
429
12
  bool Done() {
430
12
    return pos_ == D_->len_;
431
12
  }
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN10value_asdl7value_tEE4DoneEv
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN12runtime_asdl7HayNodeEE4DoneEv
Unexecuted instantiation: _ZN8DictIterIlP6BigStrE4DoneEv
Unexecuted instantiation: _ZN8DictIterIP6BigStrS1_E4DoneEv
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN12runtime_asdl4CellEE4DoneEv
432
9
  K Key() {
433
9
    return D_->keys_->items_[pos_];
434
9
  }
_ZN8DictIterIP6BigStriE3KeyEv
Line
Count
Source
432
9
  K Key() {
433
9
    return D_->keys_->items_[pos_];
434
9
  }
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN10value_asdl7value_tEE3KeyEv
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN12runtime_asdl7HayNodeEE3KeyEv
Unexecuted instantiation: _ZN8DictIterIlP6BigStrE3KeyEv
Unexecuted instantiation: _ZN8DictIterIP6BigStrS1_E3KeyEv
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN12runtime_asdl4CellEE3KeyEv
435
6
  V Value() {
436
6
    return D_->values_->items_[pos_];
437
6
  }
_ZN8DictIterIP6BigStriE5ValueEv
Line
Count
Source
435
6
  V Value() {
436
6
    return D_->values_->items_[pos_];
437
6
  }
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN10value_asdl7value_tEE5ValueEv
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN12runtime_asdl7HayNodeEE5ValueEv
Unexecuted instantiation: _ZN8DictIterIlP6BigStrE5ValueEv
Unexecuted instantiation: _ZN8DictIterIP6BigStrS1_E5ValueEv
Unexecuted instantiation: _ZN8DictIterIP6BigStrPN12runtime_asdl4CellEE5ValueEv
438
439
 private:
440
  const Dict<K, V>* D_;
441
  int pos_;
442
};
443
444
// dict(mylist) converts a list of (k, v) tuples into a dict
445
template <typename K, typename V>
446
Dict<K, V>* dict(List<Tuple2<K, V>*>* l) {
447
  auto ret = Alloc<Dict<K, V>>();
448
  ret->reserve(len(l));
449
  for (ListIter<Tuple2<K, V>*> it(l); !it.Done(); it.Next()) {
450
    ret->set(it.Value()->at0(), it.Value()->at1());
451
  }
452
  return ret;
453
}
454
455
template <class K, class V>
456
void Dict<K, V>::update(List<Tuple2<K, V>*>* pairs) {
457
  reserve(len_ + len(pairs));
458
  for (ListIter<Tuple2<K, V>*> it(pairs); !it.Done(); it.Next()) {
459
    set(it.Value()->at0(), it.Value()->at1());
460
  }
461
}
462
463
template <class K, class V>
464
void Dict<K, V>::update(Dict<K, V>* other) {
465
  reserve(len_ + len(other));
466
  for (DictIter<K, V> it(other); !it.Done(); it.Next()) {
467
    set(it.Key(), it.Value());
468
  }
469
}
470
471
#endif  // MYCPP_GC_DICT_H