mycpp

Coverage Report

Created: 2024-06-09 06:28

/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
9
List<T>* ListFromDictSlab(Slab<T>* slab, int n) {
38
9
  List<T>* result = Alloc<List<T>>();
39
9
  result->reserve(n);
40
41
51
  for (int i = 0; i < n; ++i) {
42
42
    result->append(slab->items_[i]);
43
42
  }
44
9
  return result;
45
9
}
_Z16ListFromDictSlabIP6BigStrEP4ListIT_EP4SlabIS3_Ei
Line
Count
Source
37
6
List<T>* ListFromDictSlab(Slab<T>* slab, int n) {
38
6
  List<T>* result = Alloc<List<T>>();
39
6
  result->reserve(n);
40
41
19
  for (int i = 0; i < n; ++i) {
42
13
    result->append(slab->items_[i]);
43
13
  }
44
6
  return result;
45
6
}
_Z16ListFromDictSlabIiEP4ListIT_EP4SlabIS1_Ei
Line
Count
Source
37
3
List<T>* ListFromDictSlab(Slab<T>* slab, int n) {
38
3
  List<T>* result = Alloc<List<T>>();
39
3
  result->reserve(n);
40
41
32
  for (int i = 0; i < n; ++i) {
42
29
    result->append(slab->items_[i]);
43
29
  }
44
3
  return result;
45
3
}
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
25
        values_(nullptr) {
87
25
  }
_ZN4DictIiP6BigStrEC2Ev
Line
Count
Source
86
4
        values_(nullptr) {
87
4
  }
_ZN4DictIP6BigStriEC2Ev
Line
Count
Source
86
8
        values_(nullptr) {
87
8
  }
_ZN4DictIP6BigStrS1_EC2Ev
Line
Count
Source
86
3
        values_(nullptr) {
87
3
  }
_ZN4DictIiiEC2Ev
Line
Count
Source
86
8
        values_(nullptr) {
87
8
  }
_ZN4DictIP6Tuple2IiiEiEC2Ev
Line
Count
Source
86
1
        values_(nullptr) {
87
1
  }
_ZN4DictIP6Tuple2IP6BigStriEiEC2Ev
Line
Count
Source
86
1
        values_(nullptr) {
87
1
  }
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
  }
_ZN4DictIiP6BigStrEC2ESt16initializer_listIiES3_IS1_E
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
  }
_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
  }
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
167
  static constexpr ObjHeader obj_header() {
144
167
    return ObjHeader::ClassFixed(field_mask(), sizeof(Dict));
145
167
  }
_ZN4DictIiP6BigStrE10obj_headerEv
Line
Count
Source
143
6
  static constexpr ObjHeader obj_header() {
144
6
    return ObjHeader::ClassFixed(field_mask(), sizeof(Dict));
145
6
  }
_ZN4DictIP6BigStriE10obj_headerEv
Line
Count
Source
143
10
  static constexpr ObjHeader obj_header() {
144
10
    return ObjHeader::ClassFixed(field_mask(), sizeof(Dict));
145
10
  }
_ZN4DictIP6BigStrS1_E10obj_headerEv
Line
Count
Source
143
4
  static constexpr ObjHeader obj_header() {
144
4
    return ObjHeader::ClassFixed(field_mask(), sizeof(Dict));
145
4
  }
_ZN4DictIiiE10obj_headerEv
Line
Count
Source
143
145
  static constexpr ObjHeader obj_header() {
144
145
    return ObjHeader::ClassFixed(field_mask(), sizeof(Dict));
145
145
  }
_ZN4DictIP6Tuple2IiiEiE10obj_headerEv
Line
Count
Source
143
1
  static constexpr ObjHeader obj_header() {
144
1
    return ObjHeader::ClassFixed(field_mask(), sizeof(Dict));
145
1
  }
_ZN4DictIP6Tuple2IP6BigStriEiE10obj_headerEv
Line
Count
Source
143
1
  static constexpr ObjHeader obj_header() {
144
1
    return ObjHeader::ClassFixed(field_mask(), sizeof(Dict));
145
1
  }
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
168
  static constexpr uint32_t field_mask() {
159
168
    return maskbit(offsetof(Dict, index_)) | maskbit(offsetof(Dict, keys_)) |
160
168
           maskbit(offsetof(Dict, values_));
161
168
  }
_ZN4DictIiP6BigStrE10field_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
  }
_ZN4DictIP6BigStriE10field_maskEv
Line
Count
Source
158
10
  static constexpr uint32_t field_mask() {
159
10
    return maskbit(offsetof(Dict, index_)) | maskbit(offsetof(Dict, keys_)) |
160
10
           maskbit(offsetof(Dict, values_));
161
10
  }
_ZN4DictIP6BigStrS1_E10field_maskEv
Line
Count
Source
158
4
  static constexpr uint32_t field_mask() {
159
4
    return maskbit(offsetof(Dict, index_)) | maskbit(offsetof(Dict, keys_)) |
160
4
           maskbit(offsetof(Dict, values_));
161
4
  }
_ZN4DictIiiE10field_maskEv
Line
Count
Source
158
146
  static constexpr uint32_t field_mask() {
159
146
    return maskbit(offsetof(Dict, index_)) | maskbit(offsetof(Dict, keys_)) |
160
146
           maskbit(offsetof(Dict, values_));
161
146
  }
_ZN4DictIP6Tuple2IiiEiE10field_maskEv
Line
Count
Source
158
1
  static constexpr uint32_t field_mask() {
159
1
    return maskbit(offsetof(Dict, index_)) | maskbit(offsetof(Dict, keys_)) |
160
1
           maskbit(offsetof(Dict, values_));
161
1
  }
_ZN4DictIP6Tuple2IP6BigStriEiE10field_maskEv
Line
Count
Source
158
1
  static constexpr uint32_t field_mask() {
159
1
    return maskbit(offsetof(Dict, index_)) | maskbit(offsetof(Dict, keys_)) |
160
1
           maskbit(offsetof(Dict, values_));
161
1
  }
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
43
  int HowManyPairs(int num_desired) {
188
    // See gc_list.h for comments on nearly identical logic
189
190
43
    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
17
    return RoundUp(num_desired + kHeaderFudge) - kHeaderFudge;
199
43
  }
_ZN4DictIiP6BigStrE12HowManyPairsEi
Line
Count
Source
187
6
  int HowManyPairs(int num_desired) {
188
    // See gc_list.h for comments on nearly identical logic
189
190
6
    if (num_desired <= kNumItems2) {  // use full cell in pool 2
191
5
      return kNumItems2;
192
5
    }
193
#if 0
194
    if (num_desired <= kMinItems2) {  // 48 -> 128, not 48 -> 64
195
      return kMinItems2;
196
    }
197
#endif
198
1
    return RoundUp(num_desired + kHeaderFudge) - kHeaderFudge;
199
6
  }
_ZN4DictIP6BigStriE12HowManyPairsEi
Line
Count
Source
187
16
  int HowManyPairs(int num_desired) {
188
    // See gc_list.h for comments on nearly identical logic
189
190
16
    if (num_desired <= kNumItems2) {  // use full cell in pool 2
191
9
      return kNumItems2;
192
9
    }
193
#if 0
194
    if (num_desired <= kMinItems2) {  // 48 -> 128, not 48 -> 64
195
      return kMinItems2;
196
    }
197
#endif
198
7
    return RoundUp(num_desired + kHeaderFudge) - kHeaderFudge;
199
16
  }
_ZN4DictIP6BigStrS1_E12HowManyPairsEi
Line
Count
Source
187
2
  int HowManyPairs(int num_desired) {
188
    // See gc_list.h for comments on nearly identical logic
189
190
2
    if (num_desired <= kNumItems2) {  // use full cell in pool 2
191
2
      return kNumItems2;
192
2
    }
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
2
  }
_ZN4DictIiiE12HowManyPairsEi
Line
Count
Source
187
17
  int HowManyPairs(int num_desired) {
188
    // See gc_list.h for comments on nearly identical logic
189
190
17
    if (num_desired <= kNumItems2) {  // use full cell in pool 2
191
8
      return kNumItems2;
192
8
    }
193
#if 0
194
    if (num_desired <= kMinItems2) {  // 48 -> 128, not 48 -> 64
195
      return kMinItems2;
196
    }
197
#endif
198
9
    return RoundUp(num_desired + kHeaderFudge) - kHeaderFudge;
199
17
  }
_ZN4DictIP6Tuple2IiiEiE12HowManyPairsEi
Line
Count
Source
187
1
  int HowManyPairs(int num_desired) {
188
    // See gc_list.h for comments on nearly identical logic
189
190
1
    if (num_desired <= kNumItems2) {  // use full cell in pool 2
191
1
      return kNumItems2;
192
1
    }
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
1
  }
_ZN4DictIP6Tuple2IP6BigStriEiE12HowManyPairsEi
Line
Count
Source
187
1
  int HowManyPairs(int num_desired) {
188
    // See gc_list.h for comments on nearly identical logic
189
190
1
    if (num_desired <= kNumItems2) {  // use full cell in pool 2
191
1
      return kNumItems2;
192
1
    }
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
1
  }
200
};
201
202
template <typename K, typename V>
203
9
inline bool dict_contains(const Dict<K, V>* haystack, K needle) {
204
9
  return haystack->find_kv_index(needle) != kNotFound;
205
9
}
_Z13dict_containsIiP6BigStrEbPK4DictIT_T0_ES3_
Line
Count
Source
203
2
inline bool dict_contains(const Dict<K, V>* haystack, K needle) {
204
2
  return haystack->find_kv_index(needle) != kNotFound;
205
2
}
_Z13dict_containsIP6BigStriEbPK4DictIT_T0_ES3_
Line
Count
Source
203
2
inline bool dict_contains(const Dict<K, V>* haystack, K needle) {
204
2
  return haystack->find_kv_index(needle) != kNotFound;
205
2
}
_Z13dict_containsIiiEbPK4DictIT_T0_ES1_
Line
Count
Source
203
2
inline bool dict_contains(const Dict<K, V>* haystack, K needle) {
204
2
  return haystack->find_kv_index(needle) != kNotFound;
205
2
}
_Z13dict_containsIP6BigStrS1_EbPK4DictIT_T0_ES3_
Line
Count
Source
203
3
inline bool dict_contains(const Dict<K, V>* haystack, K needle) {
204
3
  return haystack->find_kv_index(needle) != kNotFound;
205
3
}
206
207
template <typename K, typename V>
208
44
void Dict<K, V>::reserve(int num_desired) {
209
44
  if (capacity_ >= num_desired) {
210
1
    return;  // Don't do anything if there's already enough space.
211
1
  }
212
213
43
  int old_len = len_;
214
43
  Slab<K>* old_k = keys_;
215
43
  Slab<V>* old_v = values_;
216
217
  // Calculate the number of keys and values we should have
218
43
  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
43
  index_len_ = RoundUp((capacity_ + 1) * 5 / 4);
224
43
  DCHECK(index_len_ > capacity_);
225
226
0
  index_ = NewSlab<int>(index_len_);
227
1.38k
  for (int i = 0; i < index_len_; ++i) {
228
1.34k
    index_->items_[i] = kEmptyEntry;
229
1.34k
  }
230
231
  // These are DENSE, while index_ is sparse.
232
43
  keys_ = NewSlab<K>(capacity_);
233
43
  values_ = NewSlab<V>(capacity_);
234
235
43
  if (old_k != nullptr) {  // rehash if there were any entries
236
17
    len_ = 0;
237
252
    for (int i = 0; i < old_len; ++i) {
238
235
      set(old_k->items_[i], old_v->items_[i]);
239
235
    }
240
17
  }
241
43
}
_ZN4DictIiP6BigStrE7reserveEi
Line
Count
Source
208
6
void Dict<K, V>::reserve(int num_desired) {
209
6
  if (capacity_ >= num_desired) {
210
0
    return;  // Don't do anything if there's already enough space.
211
0
  }
212
213
6
  int old_len = len_;
214
6
  Slab<K>* old_k = keys_;
215
6
  Slab<V>* old_v = values_;
216
217
  // Calculate the number of keys and values we should have
218
6
  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
6
  index_len_ = RoundUp((capacity_ + 1) * 5 / 4);
224
6
  DCHECK(index_len_ > capacity_);
225
226
0
  index_ = NewSlab<int>(index_len_);
227
62
  for (int i = 0; i < index_len_; ++i) {
228
56
    index_->items_[i] = kEmptyEntry;
229
56
  }
230
231
  // These are DENSE, while index_ is sparse.
232
6
  keys_ = NewSlab<K>(capacity_);
233
6
  values_ = NewSlab<V>(capacity_);
234
235
6
  if (old_k != nullptr) {  // rehash if there were any entries
236
1
    len_ = 0;
237
6
    for (int i = 0; i < old_len; ++i) {
238
5
      set(old_k->items_[i], old_v->items_[i]);
239
5
    }
240
1
  }
241
6
}
_ZN4DictIP6BigStriE7reserveEi
Line
Count
Source
208
16
void Dict<K, V>::reserve(int num_desired) {
209
16
  if (capacity_ >= num_desired) {
210
0
    return;  // Don't do anything if there's already enough space.
211
0
  }
212
213
16
  int old_len = len_;
214
16
  Slab<K>* old_k = keys_;
215
16
  Slab<V>* old_v = values_;
216
217
  // Calculate the number of keys and values we should have
218
16
  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
16
  index_len_ = RoundUp((capacity_ + 1) * 5 / 4);
224
16
  DCHECK(index_len_ > capacity_);
225
226
0
  index_ = NewSlab<int>(index_len_);
227
440
  for (int i = 0; i < index_len_; ++i) {
228
424
    index_->items_[i] = kEmptyEntry;
229
424
  }
230
231
  // These are DENSE, while index_ is sparse.
232
16
  keys_ = NewSlab<K>(capacity_);
233
16
  values_ = NewSlab<V>(capacity_);
234
235
16
  if (old_k != nullptr) {  // rehash if there were any entries
236
7
    len_ = 0;
237
92
    for (int i = 0; i < old_len; ++i) {
238
85
      set(old_k->items_[i], old_v->items_[i]);
239
85
    }
240
7
  }
241
16
}
_ZN4DictIP6BigStrS1_E7reserveEi
Line
Count
Source
208
2
void Dict<K, V>::reserve(int num_desired) {
209
2
  if (capacity_ >= num_desired) {
210
0
    return;  // Don't do anything if there's already enough space.
211
0
  }
212
213
2
  int old_len = len_;
214
2
  Slab<K>* old_k = keys_;
215
2
  Slab<V>* old_v = values_;
216
217
  // Calculate the number of keys and values we should have
218
2
  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
2
  index_len_ = RoundUp((capacity_ + 1) * 5 / 4);
224
2
  DCHECK(index_len_ > capacity_);
225
226
0
  index_ = NewSlab<int>(index_len_);
227
18
  for (int i = 0; i < index_len_; ++i) {
228
16
    index_->items_[i] = kEmptyEntry;
229
16
  }
230
231
  // These are DENSE, while index_ is sparse.
232
2
  keys_ = NewSlab<K>(capacity_);
233
2
  values_ = NewSlab<V>(capacity_);
234
235
2
  if (old_k != nullptr) {  // rehash if there were any entries
236
0
    len_ = 0;
237
0
    for (int i = 0; i < old_len; ++i) {
238
0
      set(old_k->items_[i], old_v->items_[i]);
239
0
    }
240
0
  }
241
2
}
_ZN4DictIiiE7reserveEi
Line
Count
Source
208
18
void Dict<K, V>::reserve(int num_desired) {
209
18
  if (capacity_ >= num_desired) {
210
1
    return;  // Don't do anything if there's already enough space.
211
1
  }
212
213
17
  int old_len = len_;
214
17
  Slab<K>* old_k = keys_;
215
17
  Slab<V>* old_v = values_;
216
217
  // Calculate the number of keys and values we should have
218
17
  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
17
  index_len_ = RoundUp((capacity_ + 1) * 5 / 4);
224
17
  DCHECK(index_len_ > capacity_);
225
226
0
  index_ = NewSlab<int>(index_len_);
227
849
  for (int i = 0; i < index_len_; ++i) {
228
832
    index_->items_[i] = kEmptyEntry;
229
832
  }
230
231
  // These are DENSE, while index_ is sparse.
232
17
  keys_ = NewSlab<K>(capacity_);
233
17
  values_ = NewSlab<V>(capacity_);
234
235
17
  if (old_k != nullptr) {  // rehash if there were any entries
236
9
    len_ = 0;
237
154
    for (int i = 0; i < old_len; ++i) {
238
145
      set(old_k->items_[i], old_v->items_[i]);
239
145
    }
240
9
  }
241
17
}
_ZN4DictIP6Tuple2IiiEiE7reserveEi
Line
Count
Source
208
1
void Dict<K, V>::reserve(int num_desired) {
209
1
  if (capacity_ >= num_desired) {
210
0
    return;  // Don't do anything if there's already enough space.
211
0
  }
212
213
1
  int old_len = len_;
214
1
  Slab<K>* old_k = keys_;
215
1
  Slab<V>* old_v = values_;
216
217
  // Calculate the number of keys and values we should have
218
1
  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
1
  index_len_ = RoundUp((capacity_ + 1) * 5 / 4);
224
1
  DCHECK(index_len_ > capacity_);
225
226
0
  index_ = NewSlab<int>(index_len_);
227
9
  for (int i = 0; i < index_len_; ++i) {
228
8
    index_->items_[i] = kEmptyEntry;
229
8
  }
230
231
  // These are DENSE, while index_ is sparse.
232
1
  keys_ = NewSlab<K>(capacity_);
233
1
  values_ = NewSlab<V>(capacity_);
234
235
1
  if (old_k != nullptr) {  // rehash if there were any entries
236
0
    len_ = 0;
237
0
    for (int i = 0; i < old_len; ++i) {
238
0
      set(old_k->items_[i], old_v->items_[i]);
239
0
    }
240
0
  }
241
1
}
_ZN4DictIP6Tuple2IP6BigStriEiE7reserveEi
Line
Count
Source
208
1
void Dict<K, V>::reserve(int num_desired) {
209
1
  if (capacity_ >= num_desired) {
210
0
    return;  // Don't do anything if there's already enough space.
211
0
  }
212
213
1
  int old_len = len_;
214
1
  Slab<K>* old_k = keys_;
215
1
  Slab<V>* old_v = values_;
216
217
  // Calculate the number of keys and values we should have
218
1
  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
1
  index_len_ = RoundUp((capacity_ + 1) * 5 / 4);
224
1
  DCHECK(index_len_ > capacity_);
225
226
0
  index_ = NewSlab<int>(index_len_);
227
9
  for (int i = 0; i < index_len_; ++i) {
228
8
    index_->items_[i] = kEmptyEntry;
229
8
  }
230
231
  // These are DENSE, while index_ is sparse.
232
1
  keys_ = NewSlab<K>(capacity_);
233
1
  values_ = NewSlab<V>(capacity_);
234
235
1
  if (old_k != nullptr) {  // rehash if there were any entries
236
0
    len_ = 0;
237
0
    for (int i = 0; i < old_len; ++i) {
238
0
      set(old_k->items_[i], old_v->items_[i]);
239
0
    }
240
0
  }
241
1
}
242
243
template <typename K, typename V>
244
8.11k
V Dict<K, V>::at(K key) const {
245
8.11k
  int kv_index = find_kv_index(key);
246
8.11k
  if (kv_index == kNotFound) {
247
0
    throw Alloc<KeyError>();
248
8.11k
  } else {
249
8.11k
    return values_->items_[kv_index];
250
8.11k
  }
251
8.11k
}
_ZNK4DictIiP6BigStrE2atEi
Line
Count
Source
244
3
V Dict<K, V>::at(K key) const {
245
3
  int kv_index = find_kv_index(key);
246
3
  if (kv_index == kNotFound) {
247
0
    throw Alloc<KeyError>();
248
3
  } else {
249
3
    return values_->items_[kv_index];
250
3
  }
251
3
}
_ZNK4DictIP6BigStriE2atES1_
Line
Count
Source
244
7
V Dict<K, V>::at(K key) const {
245
7
  int kv_index = find_kv_index(key);
246
7
  if (kv_index == kNotFound) {
247
0
    throw Alloc<KeyError>();
248
7
  } else {
249
7
    return values_->items_[kv_index];
250
7
  }
251
7
}
_ZNK4DictIiiE2atEi
Line
Count
Source
244
8.09k
V Dict<K, V>::at(K key) const {
245
8.09k
  int kv_index = find_kv_index(key);
246
8.09k
  if (kv_index == kNotFound) {
247
0
    throw Alloc<KeyError>();
248
8.09k
  } else {
249
8.09k
    return values_->items_[kv_index];
250
8.09k
  }
251
8.09k
}
_ZNK4DictIP6BigStrS1_E2atES1_
Line
Count
Source
244
3
V Dict<K, V>::at(K key) const {
245
3
  int kv_index = find_kv_index(key);
246
3
  if (kv_index == kNotFound) {
247
0
    throw Alloc<KeyError>();
248
3
  } else {
249
3
    return values_->items_[kv_index];
250
3
  }
251
3
}
_ZNK4DictIP6Tuple2IiiEiE2atES2_
Line
Count
Source
244
2
V Dict<K, V>::at(K key) const {
245
2
  int kv_index = find_kv_index(key);
246
2
  if (kv_index == kNotFound) {
247
0
    throw Alloc<KeyError>();
248
2
  } else {
249
2
    return values_->items_[kv_index];
250
2
  }
251
2
}
_ZNK4DictIP6Tuple2IP6BigStriEiE2atES4_
Line
Count
Source
244
2
V Dict<K, V>::at(K key) const {
245
2
  int kv_index = find_kv_index(key);
246
2
  if (kv_index == kNotFound) {
247
0
    throw Alloc<KeyError>();
248
2
  } else {
249
2
    return values_->items_[kv_index];
250
2
  }
251
2
}
252
253
template <typename K, typename V>
254
3
V Dict<K, V>::get(K key) const {
255
3
  int kv_index = find_kv_index(key);
256
3
  if (kv_index == kNotFound) {
257
2
    return nullptr;
258
2
  } else {
259
1
    return values_->items_[kv_index];
260
1
  }
261
3
}
_ZNK4DictIiP6BigStrE3getEi
Line
Count
Source
254
2
V Dict<K, V>::get(K key) const {
255
2
  int kv_index = find_kv_index(key);
256
2
  if (kv_index == kNotFound) {
257
1
    return nullptr;
258
1
  } else {
259
1
    return values_->items_[kv_index];
260
1
  }
261
2
}
_ZNK4DictIP6BigStrS1_E3getES1_
Line
Count
Source
254
1
V Dict<K, V>::get(K key) const {
255
1
  int kv_index = find_kv_index(key);
256
1
  if (kv_index == kNotFound) {
257
1
    return nullptr;
258
1
  } else {
259
0
    return values_->items_[kv_index];
260
0
  }
261
1
}
262
263
template <typename K, typename V>
264
1
V Dict<K, V>::get(K key, V default_val) const {
265
1
  int kv_index = find_kv_index(key);
266
1
  if (kv_index == kNotFound) {
267
1
    return default_val;
268
1
  } else {
269
0
    return values_->items_[kv_index];
270
0
  }
271
1
}
_ZNK4DictIP6BigStrS1_E3getES1_S1_
Line
Count
Source
264
1
V Dict<K, V>::get(K key, V default_val) const {
265
1
  int kv_index = find_kv_index(key);
266
1
  if (kv_index == kNotFound) {
267
1
    return default_val;
268
1
  } else {
269
0
    return values_->items_[kv_index];
270
0
  }
271
1
}
Unexecuted instantiation: _ZNK4DictIiP6BigStrE3getEiS1_
Unexecuted instantiation: _ZNK4DictIP6BigStriE3getES1_i
272
273
template <typename K, typename V>
274
7
List<K>* Dict<K, V>::keys() const {
275
7
  return ListFromDictSlab<K>(keys_, len_);
276
7
}
_ZNK4DictIP6BigStriE4keysEv
Line
Count
Source
274
4
List<K>* Dict<K, V>::keys() const {
275
4
  return ListFromDictSlab<K>(keys_, len_);
276
4
}
_ZNK4DictIP6BigStrS1_E4keysEv
Line
Count
Source
274
1
List<K>* Dict<K, V>::keys() const {
275
1
  return ListFromDictSlab<K>(keys_, len_);
276
1
}
_ZNK4DictIiiE4keysEv
Line
Count
Source
274
2
List<K>* Dict<K, V>::keys() const {
275
2
  return ListFromDictSlab<K>(keys_, len_);
276
2
}
277
278
template <typename K, typename V>
279
2
List<V>* Dict<K, V>::values() const {
280
2
  return ListFromDictSlab<V>(values_, len_);
281
2
}
_ZNK4DictIP6BigStriE6valuesEv
Line
Count
Source
279
1
List<V>* Dict<K, V>::values() const {
280
1
  return ListFromDictSlab<V>(values_, len_);
281
1
}
_ZNK4DictIP6BigStrS1_E6valuesEv
Line
Count
Source
279
1
List<V>* Dict<K, V>::values() const {
280
1
  return ListFromDictSlab<V>(values_, len_);
281
1
}
282
283
template <typename K, typename V>
284
4
void Dict<K, V>::clear() {
285
  // Maintain invariant
286
396
  for (int i = 0; i < index_len_; ++i) {
287
392
    index_->items_[i] = kEmptyEntry;
288
392
  }
289
290
4
  if (keys_) {
291
3
    memset(keys_->items_, 0, len_ * sizeof(K));  // zero for GC scan
292
3
  }
293
4
  if (values_) {
294
3
    memset(values_->items_, 0, len_ * sizeof(V));  // zero for GC scan
295
3
  }
296
4
  len_ = 0;
297
4
}
_ZN4DictIiP6BigStrE5clearEv
Line
Count
Source
284
1
void Dict<K, V>::clear() {
285
  // Maintain invariant
286
1
  for (int i = 0; i < index_len_; ++i) {
287
0
    index_->items_[i] = kEmptyEntry;
288
0
  }
289
290
1
  if (keys_) {
291
0
    memset(keys_->items_, 0, len_ * sizeof(K));  // zero for GC scan
292
0
  }
293
1
  if (values_) {
294
0
    memset(values_->items_, 0, len_ * sizeof(V));  // zero for GC scan
295
0
  }
296
1
  len_ = 0;
297
1
}
_ZN4DictIP6BigStriE5clearEv
Line
Count
Source
284
1
void Dict<K, V>::clear() {
285
  // Maintain invariant
286
9
  for (int i = 0; i < index_len_; ++i) {
287
8
    index_->items_[i] = kEmptyEntry;
288
8
  }
289
290
1
  if (keys_) {
291
1
    memset(keys_->items_, 0, len_ * sizeof(K));  // zero for GC scan
292
1
  }
293
1
  if (values_) {
294
1
    memset(values_->items_, 0, len_ * sizeof(V));  // zero for GC scan
295
1
  }
296
1
  len_ = 0;
297
1
}
_ZN4DictIiiE5clearEv
Line
Count
Source
284
2
void Dict<K, V>::clear() {
285
  // Maintain invariant
286
386
  for (int i = 0; i < index_len_; ++i) {
287
384
    index_->items_[i] = kEmptyEntry;
288
384
  }
289
290
2
  if (keys_) {
291
2
    memset(keys_->items_, 0, len_ * sizeof(K));  // zero for GC scan
292
2
  }
293
2
  if (values_) {
294
2
    memset(values_->items_, 0, len_ * sizeof(V));  // zero for GC scan
295
2
  }
296
2
  len_ = 0;
297
2
}
298
299
// TODO:
300
// - Special case to intern BigStr* when it's hashed?  How?
301
//   - Should we have wrappers like:
302
//   - V GetAndIntern<V>(D, &string_key)
303
//   - SetAndIntern<V>(D, &string_key, value)
304
//   This will enable duplicate copies of the string to be garbage collected
305
template <typename K, typename V>
306
16.8k
int Dict<K, V>::hash_and_probe(K key) const {
307
16.8k
  if (capacity_ == 0) {
308
25
    return kTooSmall;
309
25
  }
310
311
  // Hash the key onto a slot in the index. If the first slot is occupied,
312
  // probe until an empty one is found.
313
16.8k
  unsigned h = hash_key(key);
314
  // faster % using & -- assuming index_len_ is power of 2
315
16.8k
  int init_bucket = h & (index_len_ - 1);
316
317
  // If we see a tombstone along the probing path, stash it.
318
16.8k
  int open_slot = -1;
319
320
16.9k
  for (int i = 0; i < index_len_; ++i) {
321
    // Start at init_bucket and wrap araound
322
323
    // faster % using & -- assuming index_len_ is power of 2
324
16.9k
    int slot = (i + init_bucket) & (index_len_ - 1);
325
326
16.9k
    int kv_index = index_->items_[slot];
327
16.9k
    DCHECK(kv_index < len_);
328
    // Optimistically this is the common case once the table has been populated.
329
16.9k
    if (kv_index >= 0) {
330
16.3k
      unsigned h2 = hash_key(keys_->items_[kv_index]);
331
16.3k
      if (h == h2 && keys_equal(keys_->items_[kv_index], key)) {
332
16.2k
        return slot;
333
16.2k
      }
334
16.3k
    }
335
336
700
    if (kv_index == kEmptyEntry) {
337
612
      if (open_slot != -1) {
338
3
        slot = open_slot;
339
3
      }
340
      // If there isn't room in the entry arrays, tell the caller to resize.
341
612
      return len_ < capacity_ ? slot : kTooSmall;
342
612
    }
343
344
    // Tombstone or collided keys unequal. Keep scanning.
345
88
    DCHECK(kv_index >= 0 || kv_index == kDeletedEntry);
346
88
    if (kv_index == kDeletedEntry && open_slot == -1) {
347
      // NOTE: We only record the open slot here. We DON'T return it. If we're
348
      // looking for a key that was writen before this tombstone was written to
349
      // the index we should continue probing until we get to that key. If we
350
      // get to an empty index slot or the end of the index then we know we are
351
      // dealing with a new key and can safely replace the tombstone without
352
      // disrupting any existing keys.
353
4
      open_slot = slot;
354
4
    }
355
88
  }
356
357
1
  if (open_slot != -1) {
358
1
    return len_ < capacity_ ? open_slot : kTooSmall;
359
1
  }
360
361
0
  return kTooSmall;
362
1
}
_ZNK4DictIiP6BigStrE14hash_and_probeEi
Line
Count
Source
306
29
int Dict<K, V>::hash_and_probe(K key) const {
307
29
  if (capacity_ == 0) {
308
5
    return kTooSmall;
309
5
  }
310
311
  // Hash the key onto a slot in the index. If the first slot is occupied,
312
  // probe until an empty one is found.
313
24
  unsigned h = hash_key(key);
314
  // faster % using & -- assuming index_len_ is power of 2
315
24
  int init_bucket = h & (index_len_ - 1);
316
317
  // If we see a tombstone along the probing path, stash it.
318
24
  int open_slot = -1;
319
320
27
  for (int i = 0; i < index_len_; ++i) {
321
    // Start at init_bucket and wrap araound
322
323
    // faster % using & -- assuming index_len_ is power of 2
324
27
    int slot = (i + init_bucket) & (index_len_ - 1);
325
326
27
    int kv_index = index_->items_[slot];
327
27
    DCHECK(kv_index < len_);
328
    // Optimistically this is the common case once the table has been populated.
329
27
    if (kv_index >= 0) {
330
8
      unsigned h2 = hash_key(keys_->items_[kv_index]);
331
8
      if (h == h2 && keys_equal(keys_->items_[kv_index], key)) {
332
5
        return slot;
333
5
      }
334
8
    }
335
336
22
    if (kv_index == kEmptyEntry) {
337
19
      if (open_slot != -1) {
338
0
        slot = open_slot;
339
0
      }
340
      // If there isn't room in the entry arrays, tell the caller to resize.
341
19
      return len_ < capacity_ ? slot : kTooSmall;
342
19
    }
343
344
    // Tombstone or collided keys unequal. Keep scanning.
345
3
    DCHECK(kv_index >= 0 || kv_index == kDeletedEntry);
346
3
    if (kv_index == kDeletedEntry && open_slot == -1) {
347
      // NOTE: We only record the open slot here. We DON'T return it. If we're
348
      // looking for a key that was writen before this tombstone was written to
349
      // the index we should continue probing until we get to that key. If we
350
      // get to an empty index slot or the end of the index then we know we are
351
      // dealing with a new key and can safely replace the tombstone without
352
      // disrupting any existing keys.
353
0
      open_slot = slot;
354
0
    }
355
3
  }
356
357
0
  if (open_slot != -1) {
358
0
    return len_ < capacity_ ? open_slot : kTooSmall;
359
0
  }
360
361
0
  return kTooSmall;
362
0
}
_ZNK4DictIP6BigStriE14hash_and_probeES1_
Line
Count
Source
306
184
int Dict<K, V>::hash_and_probe(K key) const {
307
184
  if (capacity_ == 0) {
308
9
    return kTooSmall;
309
9
  }
310
311
  // Hash the key onto a slot in the index. If the first slot is occupied,
312
  // probe until an empty one is found.
313
175
  unsigned h = hash_key(key);
314
  // faster % using & -- assuming index_len_ is power of 2
315
175
  int init_bucket = h & (index_len_ - 1);
316
317
  // If we see a tombstone along the probing path, stash it.
318
175
  int open_slot = -1;
319
320
230
  for (int i = 0; i < index_len_; ++i) {
321
    // Start at init_bucket and wrap araound
322
323
    // faster % using & -- assuming index_len_ is power of 2
324
230
    int slot = (i + init_bucket) & (index_len_ - 1);
325
326
230
    int kv_index = index_->items_[slot];
327
230
    DCHECK(kv_index < len_);
328
    // Optimistically this is the common case once the table has been populated.
329
230
    if (kv_index >= 0) {
330
63
      unsigned h2 = hash_key(keys_->items_[kv_index]);
331
63
      if (h == h2 && keys_equal(keys_->items_[kv_index], key)) {
332
9
        return slot;
333
9
      }
334
63
    }
335
336
221
    if (kv_index == kEmptyEntry) {
337
166
      if (open_slot != -1) {
338
1
        slot = open_slot;
339
1
      }
340
      // If there isn't room in the entry arrays, tell the caller to resize.
341
166
      return len_ < capacity_ ? slot : kTooSmall;
342
166
    }
343
344
    // Tombstone or collided keys unequal. Keep scanning.
345
55
    DCHECK(kv_index >= 0 || kv_index == kDeletedEntry);
346
55
    if (kv_index == kDeletedEntry && open_slot == -1) {
347
      // NOTE: We only record the open slot here. We DON'T return it. If we're
348
      // looking for a key that was writen before this tombstone was written to
349
      // the index we should continue probing until we get to that key. If we
350
      // get to an empty index slot or the end of the index then we know we are
351
      // dealing with a new key and can safely replace the tombstone without
352
      // disrupting any existing keys.
353
1
      open_slot = slot;
354
1
    }
355
55
  }
356
357
0
  if (open_slot != -1) {
358
0
    return len_ < capacity_ ? open_slot : kTooSmall;
359
0
  }
360
361
0
  return kTooSmall;
362
0
}
_ZNK4DictIP6BigStrS1_E14hash_and_probeES1_
Line
Count
Source
306
6
int Dict<K, V>::hash_and_probe(K key) const {
307
6
  if (capacity_ == 0) {
308
2
    return kTooSmall;
309
2
  }
310
311
  // Hash the key onto a slot in the index. If the first slot is occupied,
312
  // probe until an empty one is found.
313
4
  unsigned h = hash_key(key);
314
  // faster % using & -- assuming index_len_ is power of 2
315
4
  int init_bucket = h & (index_len_ - 1);
316
317
  // If we see a tombstone along the probing path, stash it.
318
4
  int open_slot = -1;
319
320
4
  for (int i = 0; i < index_len_; ++i) {
321
    // Start at init_bucket and wrap araound
322
323
    // faster % using & -- assuming index_len_ is power of 2
324
4
    int slot = (i + init_bucket) & (index_len_ - 1);
325
326
4
    int kv_index = index_->items_[slot];
327
4
    DCHECK(kv_index < len_);
328
    // Optimistically this is the common case once the table has been populated.
329
4
    if (kv_index >= 0) {
330
2
      unsigned h2 = hash_key(keys_->items_[kv_index]);
331
2
      if (h == h2 && keys_equal(keys_->items_[kv_index], key)) {
332
2
        return slot;
333
2
      }
334
2
    }
335
336
2
    if (kv_index == kEmptyEntry) {
337
2
      if (open_slot != -1) {
338
0
        slot = open_slot;
339
0
      }
340
      // If there isn't room in the entry arrays, tell the caller to resize.
341
2
      return len_ < capacity_ ? slot : kTooSmall;
342
2
    }
343
344
    // Tombstone or collided keys unequal. Keep scanning.
345
0
    DCHECK(kv_index >= 0 || kv_index == kDeletedEntry);
346
0
    if (kv_index == kDeletedEntry && open_slot == -1) {
347
      // NOTE: We only record the open slot here. We DON'T return it. If we're
348
      // looking for a key that was writen before this tombstone was written to
349
      // the index we should continue probing until we get to that key. If we
350
      // get to an empty index slot or the end of the index then we know we are
351
      // dealing with a new key and can safely replace the tombstone without
352
      // disrupting any existing keys.
353
0
      open_slot = slot;
354
0
    }
355
0
  }
356
357
0
  if (open_slot != -1) {
358
0
    return len_ < capacity_ ? open_slot : kTooSmall;
359
0
  }
360
361
0
  return kTooSmall;
362
0
}
_ZNK4DictIiiE14hash_and_probeEi
Line
Count
Source
306
16.6k
int Dict<K, V>::hash_and_probe(K key) const {
307
16.6k
  if (capacity_ == 0) {
308
7
    return kTooSmall;
309
7
  }
310
311
  // Hash the key onto a slot in the index. If the first slot is occupied,
312
  // probe until an empty one is found.
313
16.6k
  unsigned h = hash_key(key);
314
  // faster % using & -- assuming index_len_ is power of 2
315
16.6k
  int init_bucket = h & (index_len_ - 1);
316
317
  // If we see a tombstone along the probing path, stash it.
318
16.6k
  int open_slot = -1;
319
320
16.6k
  for (int i = 0; i < index_len_; ++i) {
321
    // Start at init_bucket and wrap araound
322
323
    // faster % using & -- assuming index_len_ is power of 2
324
16.6k
    int slot = (i + init_bucket) & (index_len_ - 1);
325
326
16.6k
    int kv_index = index_->items_[slot];
327
16.6k
    DCHECK(kv_index < len_);
328
    // Optimistically this is the common case once the table has been populated.
329
16.6k
    if (kv_index >= 0) {
330
16.2k
      unsigned h2 = hash_key(keys_->items_[kv_index]);
331
16.2k
      if (h == h2 && keys_equal(keys_->items_[kv_index], key)) {
332
16.2k
        return slot;
333
16.2k
      }
334
16.2k
    }
335
336
451
    if (kv_index == kEmptyEntry) {
337
421
      if (open_slot != -1) {
338
2
        slot = open_slot;
339
2
      }
340
      // If there isn't room in the entry arrays, tell the caller to resize.
341
421
      return len_ < capacity_ ? slot : kTooSmall;
342
421
    }
343
344
    // Tombstone or collided keys unequal. Keep scanning.
345
30
    DCHECK(kv_index >= 0 || kv_index == kDeletedEntry);
346
30
    if (kv_index == kDeletedEntry && open_slot == -1) {
347
      // NOTE: We only record the open slot here. We DON'T return it. If we're
348
      // looking for a key that was writen before this tombstone was written to
349
      // the index we should continue probing until we get to that key. If we
350
      // get to an empty index slot or the end of the index then we know we are
351
      // dealing with a new key and can safely replace the tombstone without
352
      // disrupting any existing keys.
353
3
      open_slot = slot;
354
3
    }
355
30
  }
356
357
1
  if (open_slot != -1) {
358
1
    return len_ < capacity_ ? open_slot : kTooSmall;
359
1
  }
360
361
0
  return kTooSmall;
362
1
}
_ZNK4DictIP6Tuple2IiiEiE14hash_and_probeES2_
Line
Count
Source
306
5
int Dict<K, V>::hash_and_probe(K key) const {
307
5
  if (capacity_ == 0) {
308
1
    return kTooSmall;
309
1
  }
310
311
  // Hash the key onto a slot in the index. If the first slot is occupied,
312
  // probe until an empty one is found.
313
4
  unsigned h = hash_key(key);
314
  // faster % using & -- assuming index_len_ is power of 2
315
4
  int init_bucket = h & (index_len_ - 1);
316
317
  // If we see a tombstone along the probing path, stash it.
318
4
  int open_slot = -1;
319
320
4
  for (int i = 0; i < index_len_; ++i) {
321
    // Start at init_bucket and wrap araound
322
323
    // faster % using & -- assuming index_len_ is power of 2
324
4
    int slot = (i + init_bucket) & (index_len_ - 1);
325
326
4
    int kv_index = index_->items_[slot];
327
4
    DCHECK(kv_index < len_);
328
    // Optimistically this is the common case once the table has been populated.
329
4
    if (kv_index >= 0) {
330
2
      unsigned h2 = hash_key(keys_->items_[kv_index]);
331
2
      if (h == h2 && keys_equal(keys_->items_[kv_index], key)) {
332
2
        return slot;
333
2
      }
334
2
    }
335
336
2
    if (kv_index == kEmptyEntry) {
337
2
      if (open_slot != -1) {
338
0
        slot = open_slot;
339
0
      }
340
      // If there isn't room in the entry arrays, tell the caller to resize.
341
2
      return len_ < capacity_ ? slot : kTooSmall;
342
2
    }
343
344
    // Tombstone or collided keys unequal. Keep scanning.
345
0
    DCHECK(kv_index >= 0 || kv_index == kDeletedEntry);
346
0
    if (kv_index == kDeletedEntry && open_slot == -1) {
347
      // NOTE: We only record the open slot here. We DON'T return it. If we're
348
      // looking for a key that was writen before this tombstone was written to
349
      // the index we should continue probing until we get to that key. If we
350
      // get to an empty index slot or the end of the index then we know we are
351
      // dealing with a new key and can safely replace the tombstone without
352
      // disrupting any existing keys.
353
0
      open_slot = slot;
354
0
    }
355
0
  }
356
357
0
  if (open_slot != -1) {
358
0
    return len_ < capacity_ ? open_slot : kTooSmall;
359
0
  }
360
361
0
  return kTooSmall;
362
0
}
_ZNK4DictIP6Tuple2IP6BigStriEiE14hash_and_probeES4_
Line
Count
Source
306
5
int Dict<K, V>::hash_and_probe(K key) const {
307
5
  if (capacity_ == 0) {
308
1
    return kTooSmall;
309
1
  }
310
311
  // Hash the key onto a slot in the index. If the first slot is occupied,
312
  // probe until an empty one is found.
313
4
  unsigned h = hash_key(key);
314
  // faster % using & -- assuming index_len_ is power of 2
315
4
  int init_bucket = h & (index_len_ - 1);
316
317
  // If we see a tombstone along the probing path, stash it.
318
4
  int open_slot = -1;
319
320
4
  for (int i = 0; i < index_len_; ++i) {
321
    // Start at init_bucket and wrap araound
322
323
    // faster % using & -- assuming index_len_ is power of 2
324
4
    int slot = (i + init_bucket) & (index_len_ - 1);
325
326
4
    int kv_index = index_->items_[slot];
327
4
    DCHECK(kv_index < len_);
328
    // Optimistically this is the common case once the table has been populated.
329
4
    if (kv_index >= 0) {
330
2
      unsigned h2 = hash_key(keys_->items_[kv_index]);
331
2
      if (h == h2 && keys_equal(keys_->items_[kv_index], key)) {
332
2
        return slot;
333
2
      }
334
2
    }
335
336
2
    if (kv_index == kEmptyEntry) {
337
2
      if (open_slot != -1) {
338
0
        slot = open_slot;
339
0
      }
340
      // If there isn't room in the entry arrays, tell the caller to resize.
341
2
      return len_ < capacity_ ? slot : kTooSmall;
342
2
    }
343
344
    // Tombstone or collided keys unequal. Keep scanning.
345
0
    DCHECK(kv_index >= 0 || kv_index == kDeletedEntry);
346
0
    if (kv_index == kDeletedEntry && open_slot == -1) {
347
      // NOTE: We only record the open slot here. We DON'T return it. If we're
348
      // looking for a key that was writen before this tombstone was written to
349
      // the index we should continue probing until we get to that key. If we
350
      // get to an empty index slot or the end of the index then we know we are
351
      // dealing with a new key and can safely replace the tombstone without
352
      // disrupting any existing keys.
353
0
      open_slot = slot;
354
0
    }
355
0
  }
356
357
0
  if (open_slot != -1) {
358
0
    return len_ < capacity_ ? open_slot : kTooSmall;
359
0
  }
360
361
0
  return kTooSmall;
362
0
}
363
364
template <typename K, typename V>
365
8.12k
int Dict<K, V>::find_kv_index(K key) const {
366
8.12k
  if (index_ != nullptr) {  // Common case
367
8.11k
    int pos = hash_and_probe(key);
368
8.11k
    if (pos == kTooSmall) {
369
0
      return kNotFound;
370
0
    }
371
8.11k
    DCHECK(pos >= 0);
372
0
    int kv_index = index_->items_[pos];
373
8.11k
    if (kv_index < 0) {
374
5
      return kNotFound;
375
5
    }
376
8.11k
    return kv_index;
377
8.11k
  }
378
379
  // Linear search on GlobalDict instances.
380
  // TODO: Should we populate and compare their hash values?
381
14
  for (int i = 0; i < len_; ++i) {
382
11
    if (keys_equal(keys_->items_[i], key)) {
383
6
      return i;
384
6
    }
385
11
  }
386
387
3
  return kNotFound;
388
9
}
_ZNK4DictIiP6BigStrE13find_kv_indexEi
Line
Count
Source
365
7
int Dict<K, V>::find_kv_index(K key) const {
366
7
  if (index_ != nullptr) {  // Common case
367
7
    int pos = hash_and_probe(key);
368
7
    if (pos == kTooSmall) {
369
0
      return kNotFound;
370
0
    }
371
7
    DCHECK(pos >= 0);
372
0
    int kv_index = index_->items_[pos];
373
7
    if (kv_index < 0) {
374
2
      return kNotFound;
375
2
    }
376
5
    return kv_index;
377
7
  }
378
379
  // Linear search on GlobalDict instances.
380
  // TODO: Should we populate and compare their hash values?
381
0
  for (int i = 0; i < len_; ++i) {
382
0
    if (keys_equal(keys_->items_[i], key)) {
383
0
      return i;
384
0
    }
385
0
  }
386
387
0
  return kNotFound;
388
0
}
_ZNK4DictIP6BigStriE13find_kv_indexES1_
Line
Count
Source
365
9
int Dict<K, V>::find_kv_index(K key) const {
366
9
  if (index_ != nullptr) {  // Common case
367
9
    int pos = hash_and_probe(key);
368
9
    if (pos == kTooSmall) {
369
0
      return kNotFound;
370
0
    }
371
9
    DCHECK(pos >= 0);
372
0
    int kv_index = index_->items_[pos];
373
9
    if (kv_index < 0) {
374
1
      return kNotFound;
375
1
    }
376
8
    return kv_index;
377
9
  }
378
379
  // Linear search on GlobalDict instances.
380
  // TODO: Should we populate and compare their hash values?
381
0
  for (int i = 0; i < len_; ++i) {
382
0
    if (keys_equal(keys_->items_[i], key)) {
383
0
      return i;
384
0
    }
385
0
  }
386
387
0
  return kNotFound;
388
0
}
_ZNK4DictIiiE13find_kv_indexEi
Line
Count
Source
365
8.09k
int Dict<K, V>::find_kv_index(K key) const {
366
8.09k
  if (index_ != nullptr) {  // Common case
367
8.09k
    int pos = hash_and_probe(key);
368
8.09k
    if (pos == kTooSmall) {
369
0
      return kNotFound;
370
0
    }
371
8.09k
    DCHECK(pos >= 0);
372
0
    int kv_index = index_->items_[pos];
373
8.09k
    if (kv_index < 0) {
374
2
      return kNotFound;
375
2
    }
376
8.09k
    return kv_index;
377
8.09k
  }
378
379
  // Linear search on GlobalDict instances.
380
  // TODO: Should we populate and compare their hash values?
381
3
  for (int i = 0; i < len_; ++i) {
382
3
    if (keys_equal(keys_->items_[i], key)) {
383
2
      return i;
384
2
    }
385
3
  }
386
387
0
  return kNotFound;
388
2
}
_ZNK4DictIP6BigStrS1_E13find_kv_indexES1_
Line
Count
Source
365
8
int Dict<K, V>::find_kv_index(K key) const {
366
8
  if (index_ != nullptr) {  // Common case
367
1
    int pos = hash_and_probe(key);
368
1
    if (pos == kTooSmall) {
369
0
      return kNotFound;
370
0
    }
371
1
    DCHECK(pos >= 0);
372
0
    int kv_index = index_->items_[pos];
373
1
    if (kv_index < 0) {
374
0
      return kNotFound;
375
0
    }
376
1
    return kv_index;
377
1
  }
378
379
  // Linear search on GlobalDict instances.
380
  // TODO: Should we populate and compare their hash values?
381
11
  for (int i = 0; i < len_; ++i) {
382
8
    if (keys_equal(keys_->items_[i], key)) {
383
4
      return i;
384
4
    }
385
8
  }
386
387
3
  return kNotFound;
388
7
}
_ZNK4DictIP6Tuple2IiiEiE13find_kv_indexES2_
Line
Count
Source
365
2
int Dict<K, V>::find_kv_index(K key) const {
366
2
  if (index_ != nullptr) {  // Common case
367
2
    int pos = hash_and_probe(key);
368
2
    if (pos == kTooSmall) {
369
0
      return kNotFound;
370
0
    }
371
2
    DCHECK(pos >= 0);
372
0
    int kv_index = index_->items_[pos];
373
2
    if (kv_index < 0) {
374
0
      return kNotFound;
375
0
    }
376
2
    return kv_index;
377
2
  }
378
379
  // Linear search on GlobalDict instances.
380
  // TODO: Should we populate and compare their hash values?
381
0
  for (int i = 0; i < len_; ++i) {
382
0
    if (keys_equal(keys_->items_[i], key)) {
383
0
      return i;
384
0
    }
385
0
  }
386
387
0
  return kNotFound;
388
0
}
_ZNK4DictIP6Tuple2IP6BigStriEiE13find_kv_indexES4_
Line
Count
Source
365
2
int Dict<K, V>::find_kv_index(K key) const {
366
2
  if (index_ != nullptr) {  // Common case
367
2
    int pos = hash_and_probe(key);
368
2
    if (pos == kTooSmall) {
369
0
      return kNotFound;
370
0
    }
371
2
    DCHECK(pos >= 0);
372
0
    int kv_index = index_->items_[pos];
373
2
    if (kv_index < 0) {
374
0
      return kNotFound;
375
0
    }
376
2
    return kv_index;
377
2
  }
378
379
  // Linear search on GlobalDict instances.
380
  // TODO: Should we populate and compare their hash values?
381
0
  for (int i = 0; i < len_; ++i) {
382
0
    if (keys_equal(keys_->items_[i], key)) {
383
0
      return i;
384
0
    }
385
0
  }
386
387
0
  return kNotFound;
388
0
}
389
390
template <typename K, typename V>
391
8.53k
void Dict<K, V>::set(K key, V val) {
392
8.53k
  DCHECK(obj_header().heap_tag != HeapTag::Global);
393
0
  int pos = hash_and_probe(key);
394
8.53k
  if (pos == kTooSmall) {
395
40
    reserve(len_ + 1);
396
40
    pos = hash_and_probe(key);
397
40
  }
398
8.53k
  DCHECK(pos >= 0);
399
400
0
  int kv_index = index_->items_[pos];
401
8.53k
  DCHECK(kv_index < len_);
402
8.53k
  if (kv_index < 0) {
403
    // Write new entries to the end of the k/v arrays. This allows us to recall
404
    // insertion order until the first deletion.
405
592
    keys_->items_[len_] = key;
406
592
    values_->items_[len_] = val;
407
592
    index_->items_[pos] = len_;
408
592
    len_++;
409
592
    DCHECK(len_ <= capacity_);
410
7.94k
  } else {
411
7.94k
    values_->items_[kv_index] = val;
412
7.94k
  }
413
8.53k
}
_ZN4DictIiP6BigStrE3setEiS1_
Line
Count
Source
391
15
void Dict<K, V>::set(K key, V val) {
392
15
  DCHECK(obj_header().heap_tag != HeapTag::Global);
393
0
  int pos = hash_and_probe(key);
394
15
  if (pos == kTooSmall) {
395
6
    reserve(len_ + 1);
396
6
    pos = hash_and_probe(key);
397
6
  }
398
15
  DCHECK(pos >= 0);
399
400
0
  int kv_index = index_->items_[pos];
401
15
  DCHECK(kv_index < len_);
402
15
  if (kv_index < 0) {
403
    // Write new entries to the end of the k/v arrays. This allows us to recall
404
    // insertion order until the first deletion.
405
15
    keys_->items_[len_] = key;
406
15
    values_->items_[len_] = val;
407
15
    index_->items_[pos] = len_;
408
15
    len_++;
409
15
    DCHECK(len_ <= capacity_);
410
15
  } else {
411
0
    values_->items_[kv_index] = val;
412
0
  }
413
15
}
_ZN4DictIP6BigStriE3setES1_i
Line
Count
Source
391
158
void Dict<K, V>::set(K key, V val) {
392
158
  DCHECK(obj_header().heap_tag != HeapTag::Global);
393
0
  int pos = hash_and_probe(key);
394
158
  if (pos == kTooSmall) {
395
16
    reserve(len_ + 1);
396
16
    pos = hash_and_probe(key);
397
16
  }
398
158
  DCHECK(pos >= 0);
399
400
0
  int kv_index = index_->items_[pos];
401
158
  DCHECK(kv_index < len_);
402
158
  if (kv_index < 0) {
403
    // Write new entries to the end of the k/v arrays. This allows us to recall
404
    // insertion order until the first deletion.
405
158
    keys_->items_[len_] = key;
406
158
    values_->items_[len_] = val;
407
158
    index_->items_[pos] = len_;
408
158
    len_++;
409
158
    DCHECK(len_ <= capacity_);
410
158
  } else {
411
0
    values_->items_[kv_index] = val;
412
0
  }
413
158
}
_ZN4DictIP6BigStrS1_E3setES1_S1_
Line
Count
Source
391
2
void Dict<K, V>::set(K key, V val) {
392
2
  DCHECK(obj_header().heap_tag != HeapTag::Global);
393
0
  int pos = hash_and_probe(key);
394
2
  if (pos == kTooSmall) {
395
2
    reserve(len_ + 1);
396
2
    pos = hash_and_probe(key);
397
2
  }
398
2
  DCHECK(pos >= 0);
399
400
0
  int kv_index = index_->items_[pos];
401
2
  DCHECK(kv_index < len_);
402
2
  if (kv_index < 0) {
403
    // Write new entries to the end of the k/v arrays. This allows us to recall
404
    // insertion order until the first deletion.
405
2
    keys_->items_[len_] = key;
406
2
    values_->items_[len_] = val;
407
2
    index_->items_[pos] = len_;
408
2
    len_++;
409
2
    DCHECK(len_ <= capacity_);
410
2
  } else {
411
0
    values_->items_[kv_index] = val;
412
0
  }
413
2
}
_ZN4DictIiiE3setEii
Line
Count
Source
391
8.35k
void Dict<K, V>::set(K key, V val) {
392
8.35k
  DCHECK(obj_header().heap_tag != HeapTag::Global);
393
0
  int pos = hash_and_probe(key);
394
8.35k
  if (pos == kTooSmall) {
395
14
    reserve(len_ + 1);
396
14
    pos = hash_and_probe(key);
397
14
  }
398
8.35k
  DCHECK(pos >= 0);
399
400
0
  int kv_index = index_->items_[pos];
401
8.35k
  DCHECK(kv_index < len_);
402
8.35k
  if (kv_index < 0) {
403
    // Write new entries to the end of the k/v arrays. This allows us to recall
404
    // insertion order until the first deletion.
405
413
    keys_->items_[len_] = key;
406
413
    values_->items_[len_] = val;
407
413
    index_->items_[pos] = len_;
408
413
    len_++;
409
413
    DCHECK(len_ <= capacity_);
410
7.94k
  } else {
411
7.94k
    values_->items_[kv_index] = val;
412
7.94k
  }
413
8.35k
}
_ZN4DictIP6Tuple2IiiEiE3setES2_i
Line
Count
Source
391
2
void Dict<K, V>::set(K key, V val) {
392
2
  DCHECK(obj_header().heap_tag != HeapTag::Global);
393
0
  int pos = hash_and_probe(key);
394
2
  if (pos == kTooSmall) {
395
1
    reserve(len_ + 1);
396
1
    pos = hash_and_probe(key);
397
1
  }
398
2
  DCHECK(pos >= 0);
399
400
0
  int kv_index = index_->items_[pos];
401
2
  DCHECK(kv_index < len_);
402
2
  if (kv_index < 0) {
403
    // Write new entries to the end of the k/v arrays. This allows us to recall
404
    // insertion order until the first deletion.
405
2
    keys_->items_[len_] = key;
406
2
    values_->items_[len_] = val;
407
2
    index_->items_[pos] = len_;
408
2
    len_++;
409
2
    DCHECK(len_ <= capacity_);
410
2
  } else {
411
0
    values_->items_[kv_index] = val;
412
0
  }
413
2
}
_ZN4DictIP6Tuple2IP6BigStriEiE3setES4_i
Line
Count
Source
391
2
void Dict<K, V>::set(K key, V val) {
392
2
  DCHECK(obj_header().heap_tag != HeapTag::Global);
393
0
  int pos = hash_and_probe(key);
394
2
  if (pos == kTooSmall) {
395
1
    reserve(len_ + 1);
396
1
    pos = hash_and_probe(key);
397
1
  }
398
2
  DCHECK(pos >= 0);
399
400
0
  int kv_index = index_->items_[pos];
401
2
  DCHECK(kv_index < len_);
402
2
  if (kv_index < 0) {
403
    // Write new entries to the end of the k/v arrays. This allows us to recall
404
    // insertion order until the first deletion.
405
2
    keys_->items_[len_] = key;
406
2
    values_->items_[len_] = val;
407
2
    index_->items_[pos] = len_;
408
2
    len_++;
409
2
    DCHECK(len_ <= capacity_);
410
2
  } else {
411
0
    values_->items_[kv_index] = val;
412
0
  }
413
2
}
414
415
template <typename K, typename V>
416
24
inline int len(const Dict<K, V>* d) {
417
24
  return d->len_;
418
24
}
_Z3lenIP6BigStriEiPK4DictIT_T0_E
Line
Count
Source
416
9
inline int len(const Dict<K, V>* d) {
417
9
  return d->len_;
418
9
}
_Z3lenIP6BigStrS1_EiPK4DictIT_T0_E
Line
Count
Source
416
5
inline int len(const Dict<K, V>* d) {
417
5
  return d->len_;
418
5
}
_Z3lenIiiEiPK4DictIT_T0_E
Line
Count
Source
416
7
inline int len(const Dict<K, V>* d) {
417
7
  return d->len_;
418
7
}
_Z3lenIiP6BigStrEiPK4DictIT_T0_E
Line
Count
Source
416
3
inline int len(const Dict<K, V>* d) {
417
3
  return d->len_;
418
3
}
419
420
template <class K, class V>
421
class DictIter {
422
 public:
423
4
  explicit DictIter(Dict<K, V>* D) : D_(D), pos_(0) {
424
4
  }
_ZN8DictIterIP6BigStriEC2EP4DictIS1_iE
Line
Count
Source
423
3
  explicit DictIter(Dict<K, V>* D) : D_(D), pos_(0) {
424
3
  }
_ZN8DictIterIP6BigStrS1_EC2EP4DictIS1_S1_E
Line
Count
Source
423
1
  explicit DictIter(Dict<K, V>* D) : D_(D), pos_(0) {
424
1
  }
425
4
  void Next() {
426
4
    pos_++;
427
4
  }
_ZN8DictIterIP6BigStriE4NextEv
Line
Count
Source
425
4
  void Next() {
426
4
    pos_++;
427
4
  }
Unexecuted instantiation: _ZN8DictIterIP6BigStrS1_E4NextEv
428
8
  bool Done() {
429
8
    return pos_ == D_->len_;
430
8
  }
_ZN8DictIterIP6BigStriE4DoneEv
Line
Count
Source
428
7
  bool Done() {
429
7
    return pos_ == D_->len_;
430
7
  }
_ZN8DictIterIP6BigStrS1_E4DoneEv
Line
Count
Source
428
1
  bool Done() {
429
1
    return pos_ == D_->len_;
430
1
  }
431
4
  K Key() {
432
4
    return D_->keys_->items_[pos_];
433
4
  }
_ZN8DictIterIP6BigStriE3KeyEv
Line
Count
Source
431
4
  K Key() {
432
4
    return D_->keys_->items_[pos_];
433
4
  }
Unexecuted instantiation: _ZN8DictIterIP6BigStrS1_E3KeyEv
434
2
  V Value() {
435
2
    return D_->values_->items_[pos_];
436
2
  }
437
438
 private:
439
  const Dict<K, V>* D_;
440
  int pos_;
441
};
442
443
// dict(mylist) converts a list of (k, v) tuples into a dict
444
template <typename K, typename V>
445
1
Dict<K, V>* dict(List<Tuple2<K, V>*>* l) {
446
1
  auto ret = Alloc<Dict<K, V>>();
447
1
  ret->reserve(len(l));
448
3
  for (ListIter<Tuple2<K, V>*> it(l); !it.Done(); it.Next()) {
449
2
    ret->set(it.Value()->at0(), it.Value()->at1());
450
2
  }
451
1
  return ret;
452
1
}
453
454
template <class K, class V>
455
1
void Dict<K, V>::update(List<Tuple2<K, V>*>* pairs) {
456
1
  reserve(len_ + len(pairs));
457
3
  for (ListIter<Tuple2<K, V>*> it(pairs); !it.Done(); it.Next()) {
458
2
    set(it.Value()->at0(), it.Value()->at1());
459
2
  }
460
1
}
461
462
template <class K, class V>
463
void Dict<K, V>::update(Dict<K, V>* other) {
464
  reserve(len_ + len(other));
465
  for (DictIter<K, V> it(other); !it.Done(); it.Next()) {
466
    set(it.Key(), it.Value());
467
  }
468
}
469
470
#endif  // MYCPP_GC_DICT_H