Coverage Report

Created: 2025-10-09 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/re2/re2/sparse_array.h
Line
Count
Source
1
// Copyright 2006 The RE2 Authors.  All Rights Reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
4
5
#ifndef RE2_SPARSE_ARRAY_H_
6
#define RE2_SPARSE_ARRAY_H_
7
8
// DESCRIPTION
9
//
10
// SparseArray<T>(m) is a map from integers in [0, m) to T values.
11
// It requires (sizeof(T)+sizeof(int))*m memory, but it provides
12
// fast iteration through the elements in the array and fast clearing
13
// of the array.  The array has a concept of certain elements being
14
// uninitialized (having no value).
15
//
16
// Insertion and deletion are constant time operations.
17
//
18
// Allocating the array is a constant time operation
19
// when memory allocation is a constant time operation.
20
//
21
// Clearing the array is a constant time operation (unusual!).
22
//
23
// Iterating through the array is an O(n) operation, where n
24
// is the number of items in the array (not O(m)).
25
//
26
// The array iterator visits entries in the order they were first
27
// inserted into the array.  It is safe to add items to the array while
28
// using an iterator: the iterator will visit indices added to the array
29
// during the iteration, but will not re-visit indices whose values
30
// change after visiting.  Thus SparseArray can be a convenient
31
// implementation of a work queue.
32
//
33
// The SparseArray implementation is NOT thread-safe.  It is up to the
34
// caller to make sure only one thread is accessing the array.  (Typically
35
// these arrays are temporary values and used in situations where speed is
36
// important.)
37
//
38
// The SparseArray interface does not present all the usual STL bells and
39
// whistles.
40
//
41
// Implemented with reference to Briggs & Torczon, An Efficient
42
// Representation for Sparse Sets, ACM Letters on Programming Languages
43
// and Systems, Volume 2, Issue 1-4 (March-Dec.  1993), pp.  59-69.
44
//
45
// Briggs & Torczon popularized this technique, but it had been known
46
// long before their paper.  They point out that Aho, Hopcroft, and
47
// Ullman's 1974 Design and Analysis of Computer Algorithms and Bentley's
48
// 1986 Programming Pearls both hint at the technique in exercises to the
49
// reader (in Aho & Hopcroft, exercise 2.12; in Bentley, column 1
50
// exercise 8).
51
//
52
// Briggs & Torczon describe a sparse set implementation.  I have
53
// trivially generalized it to create a sparse array (actually the original
54
// target of the AHU and Bentley exercises).
55
56
// IMPLEMENTATION
57
//
58
// SparseArray is an array dense_ and an array sparse_ of identical size.
59
// At any point, the number of elements in the sparse array is size_.
60
//
61
// The array dense_ contains the size_ elements in the sparse array (with
62
// their indices),
63
// in the order that the elements were first inserted.  This array is dense:
64
// the size_ pairs are dense_[0] through dense_[size_-1].
65
//
66
// The array sparse_ maps from indices in [0,m) to indices in [0,size_).
67
// For indices present in the array, dense_[sparse_[i]].index_ == i.
68
// For indices not present in the array, sparse_ can contain any value at all,
69
// perhaps outside the range [0, size_) but perhaps not.
70
//
71
// The lax requirement on sparse_ values makes clearing the array very easy:
72
// set size_ to 0.  Lookups are slightly more complicated.
73
// An index i has a value in the array if and only if:
74
//   sparse_[i] is in [0, size_) AND
75
//   dense_[sparse_[i]].index_ == i.
76
// If both these properties hold, only then it is safe to refer to
77
//   dense_[sparse_[i]].value_
78
// as the value associated with index i.
79
//
80
// To insert a new entry, set sparse_[i] to size_,
81
// initialize dense_[size_], and then increment size_.
82
//
83
// To make the sparse array as efficient as possible for non-primitive types,
84
// elements may or may not be destroyed when they are deleted from the sparse
85
// array through a call to resize(). They immediately become inaccessible, but
86
// they are only guaranteed to be destroyed when the SparseArray destructor is
87
// called.
88
//
89
// A moved-from SparseArray will be empty.
90
91
#include <assert.h>
92
#include <stdint.h>
93
94
#include <algorithm>
95
#include <memory>
96
#include <utility>
97
98
#include "re2/pod_array.h"
99
100
// Doing this simplifies the logic below.
101
#ifndef __has_feature
102
#define __has_feature(x) 0
103
#endif
104
105
#if __has_feature(memory_sanitizer)
106
#include <sanitizer/msan_interface.h>
107
#endif
108
109
namespace re2 {
110
111
template<typename Value>
112
class SparseArray {
113
 public:
114
  SparseArray();
115
  explicit SparseArray(int max_size);
116
  ~SparseArray();
117
118
  // IndexValue pairs: exposed in SparseArray::iterator.
119
  class IndexValue;
120
121
  typedef IndexValue* iterator;
122
  typedef const IndexValue* const_iterator;
123
124
  SparseArray(const SparseArray& src);
125
  SparseArray(SparseArray&& src);
126
127
  SparseArray& operator=(const SparseArray& src);
128
  SparseArray& operator=(SparseArray&& src);
129
130
  // Return the number of entries in the array.
131
11.0M
  int size() const {
132
11.0M
    return size_;
133
11.0M
  }
re2::SparseArray<re2::NFA::Thread*>::size() const
Line
Count
Source
131
37.2k
  int size() const {
132
37.2k
    return size_;
133
37.2k
  }
re2::SparseArray<int>::size() const
Line
Count
Source
131
11.0M
  int size() const {
132
11.0M
    return size_;
133
11.0M
  }
134
135
  // Indicate whether the array is empty.
136
  int empty() const {
137
    return size_ == 0;
138
  }
139
140
  // Iterate over the array.
141
9.87M
  iterator begin() {
142
9.87M
    return dense_.data();
143
9.87M
  }
re2::SparseArray<int>::begin()
Line
Count
Source
141
9.83M
  iterator begin() {
142
9.83M
    return dense_.data();
143
9.83M
  }
re2::SparseArray<re2::NFA::Thread*>::begin()
Line
Count
Source
141
41.5k
  iterator begin() {
142
41.5k
    return dense_.data();
143
41.5k
  }
144
50.3M
  iterator end() {
145
50.3M
    return dense_.data() + size_;
146
50.3M
  }
re2::SparseArray<int>::end()
Line
Count
Source
144
19.0M
  iterator end() {
145
19.0M
    return dense_.data() + size_;
146
19.0M
  }
re2::SparseArray<re2::NFA::Thread*>::end()
Line
Count
Source
144
31.3M
  iterator end() {
145
31.3M
    return dense_.data() + size_;
146
31.3M
  }
147
148
  const_iterator begin() const {
149
    return dense_.data();
150
  }
151
  const_iterator end() const {
152
    return dense_.data() + size_;
153
  }
154
155
  // Change the maximum size of the array.
156
  // Invalidates all iterators.
157
  void resize(int new_max_size);
158
159
  // Return the maximum size of the array.
160
  // Indices can be in the range [0, max_size).
161
2.21G
  int max_size() const {
162
2.21G
    if (dense_.data() != NULL)
163
2.21G
      return dense_.size();
164
5.82k
    else
165
5.82k
      return 0;
166
2.21G
  }
re2::SparseArray<int>::max_size() const
Line
Count
Source
161
2.14G
  int max_size() const {
162
2.14G
    if (dense_.data() != NULL)
163
2.14G
      return dense_.size();
164
0
    else
165
0
      return 0;
166
2.14G
  }
re2::SparseArray<re2::NFA::Thread*>::max_size() const
Line
Count
Source
161
73.3M
  int max_size() const {
162
73.3M
    if (dense_.data() != NULL)
163
73.3M
      return dense_.size();
164
5.82k
    else
165
5.82k
      return 0;
166
73.3M
  }
167
168
  // Clear the array.
169
138k
  void clear() {
170
138k
    size_ = 0;
171
138k
  }
re2::SparseArray<re2::NFA::Thread*>::clear()
Line
Count
Source
169
118k
  void clear() {
170
118k
    size_ = 0;
171
118k
  }
re2::SparseArray<int>::clear()
Line
Count
Source
169
19.3k
  void clear() {
170
19.3k
    size_ = 0;
171
19.3k
  }
172
173
  // Check whether index i is in the array.
174
  bool has_index(int i) const;
175
176
  // Comparison function for sorting.
177
  // Can sort the sparse array so that future iterations
178
  // will visit indices in increasing order using
179
  // std::sort(arr.begin(), arr.end(), arr.less);
180
  static bool less(const IndexValue& a, const IndexValue& b);
181
182
 public:
183
  // Set the value at index i to v.
184
235k
  iterator set(int i, const Value& v) {
185
235k
    return SetInternal(true, i, v);
186
235k
  }
187
188
  // Set the value at new index i to v.
189
  // Fast but unsafe: only use if has_index(i) is false.
190
68.5M
  iterator set_new(int i, const Value& v) {
191
68.5M
    return SetInternal(false, i, v);
192
68.5M
  }
re2::SparseArray<int>::set_new(int, int const&)
Line
Count
Source
190
37.2M
  iterator set_new(int i, const Value& v) {
191
37.2M
    return SetInternal(false, i, v);
192
37.2M
  }
re2::SparseArray<re2::NFA::Thread*>::set_new(int, re2::NFA::Thread* const&)
Line
Count
Source
190
31.2M
  iterator set_new(int i, const Value& v) {
191
31.2M
    return SetInternal(false, i, v);
192
31.2M
  }
193
194
  // Set the value at index i to v.
195
  // Fast but unsafe: only use if has_index(i) is true.
196
13.7k
  iterator set_existing(int i, const Value& v) {
197
13.7k
    return SetExistingInternal(i, v);
198
13.7k
  }
199
200
  // Get the value at index i.
201
  // Fast but unsafe: only use if has_index(i) is true.
202
1.04G
  Value& get_existing(int i) {
203
1.04G
    assert(has_index(i));
204
1.04G
    return dense_[sparse_[i]].value_;
205
1.04G
  }
re2::SparseArray<int>::get_existing(int)
Line
Count
Source
202
1.01G
  Value& get_existing(int i) {
203
    assert(has_index(i));
204
1.01G
    return dense_[sparse_[i]].value_;
205
1.01G
  }
re2::SparseArray<re2::NFA::Thread*>::get_existing(int)
Line
Count
Source
202
31.2M
  Value& get_existing(int i) {
203
    assert(has_index(i));
204
31.2M
    return dense_[sparse_[i]].value_;
205
31.2M
  }
206
  const Value& get_existing(int i) const {
207
    assert(has_index(i));
208
    return dense_[sparse_[i]].value_;
209
  }
210
211
 private:
212
68.7M
  iterator SetInternal(bool allow_existing, int i, const Value& v) {
213
68.7M
    DebugCheckInvariants();
214
68.7M
    if (static_cast<uint32_t>(i) >= static_cast<uint32_t>(max_size())) {
215
0
      assert(false && "illegal index");
216
      // Semantically, end() would be better here, but we already know
217
      // the user did something stupid, so begin() insulates them from
218
      // dereferencing an invalid pointer.
219
0
      return begin();
220
0
    }
221
68.7M
    if (!allow_existing) {
222
68.5M
      assert(!has_index(i));
223
68.5M
      create_index(i);
224
68.5M
    } else {
225
235k
      if (!has_index(i))
226
133k
        create_index(i);
227
235k
    }
228
68.7M
    return SetExistingInternal(i, v);
229
68.7M
  }
re2::SparseArray<int>::SetInternal(bool, int, int const&)
Line
Count
Source
212
37.4M
  iterator SetInternal(bool allow_existing, int i, const Value& v) {
213
37.4M
    DebugCheckInvariants();
214
37.4M
    if (static_cast<uint32_t>(i) >= static_cast<uint32_t>(max_size())) {
215
0
      assert(false && "illegal index");
216
      // Semantically, end() would be better here, but we already know
217
      // the user did something stupid, so begin() insulates them from
218
      // dereferencing an invalid pointer.
219
0
      return begin();
220
0
    }
221
37.4M
    if (!allow_existing) {
222
37.2M
      assert(!has_index(i));
223
37.2M
      create_index(i);
224
37.2M
    } else {
225
235k
      if (!has_index(i))
226
133k
        create_index(i);
227
235k
    }
228
37.4M
    return SetExistingInternal(i, v);
229
37.4M
  }
re2::SparseArray<re2::NFA::Thread*>::SetInternal(bool, int, re2::NFA::Thread* const&)
Line
Count
Source
212
31.2M
  iterator SetInternal(bool allow_existing, int i, const Value& v) {
213
31.2M
    DebugCheckInvariants();
214
31.2M
    if (static_cast<uint32_t>(i) >= static_cast<uint32_t>(max_size())) {
215
0
      assert(false && "illegal index");
216
      // Semantically, end() would be better here, but we already know
217
      // the user did something stupid, so begin() insulates them from
218
      // dereferencing an invalid pointer.
219
0
      return begin();
220
0
    }
221
31.2M
    if (!allow_existing) {
222
31.2M
      assert(!has_index(i));
223
31.2M
      create_index(i);
224
31.2M
    } else {
225
0
      if (!has_index(i))
226
0
        create_index(i);
227
0
    }
228
31.2M
    return SetExistingInternal(i, v);
229
31.2M
  }
230
231
68.7M
  iterator SetExistingInternal(int i, const Value& v) {
232
68.7M
    DebugCheckInvariants();
233
68.7M
    assert(has_index(i));
234
68.7M
    dense_[sparse_[i]].value_ = v;
235
68.7M
    DebugCheckInvariants();
236
68.7M
    return dense_.data() + sparse_[i];
237
68.7M
  }
re2::SparseArray<int>::SetExistingInternal(int, int const&)
Line
Count
Source
231
37.4M
  iterator SetExistingInternal(int i, const Value& v) {
232
37.4M
    DebugCheckInvariants();
233
    assert(has_index(i));
234
37.4M
    dense_[sparse_[i]].value_ = v;
235
37.4M
    DebugCheckInvariants();
236
37.4M
    return dense_.data() + sparse_[i];
237
37.4M
  }
re2::SparseArray<re2::NFA::Thread*>::SetExistingInternal(int, re2::NFA::Thread* const&)
Line
Count
Source
231
31.2M
  iterator SetExistingInternal(int i, const Value& v) {
232
31.2M
    DebugCheckInvariants();
233
    assert(has_index(i));
234
31.2M
    dense_[sparse_[i]].value_ = v;
235
31.2M
    DebugCheckInvariants();
236
31.2M
    return dense_.data() + sparse_[i];
237
31.2M
  }
238
239
  // Add the index i to the array.
240
  // Only use if has_index(i) is known to be false.
241
  // Since it doesn't set the value associated with i,
242
  // this function is private, only intended as a helper
243
  // for other methods.
244
  void create_index(int i);
245
246
  // In debug mode, verify that some invariant properties of the class
247
  // are being maintained. This is called at the end of the constructor
248
  // and at the beginning and end of all public non-const member functions.
249
  void DebugCheckInvariants() const;
250
251
  // Initializes memory for elements [min, max).
252
131k
  void MaybeInitializeMemory(int min, int max) {
253
#if __has_feature(memory_sanitizer)
254
    __msan_unpoison(sparse_.data() + min, (max - min) * sizeof sparse_[0]);
255
#elif defined(RE2_ON_VALGRIND)
256
    for (int i = min; i < max; i++) {
257
      sparse_[i] = 0xababababU;
258
    }
259
#endif
260
131k
  }
re2::SparseArray<int>::MaybeInitializeMemory(int, int)
Line
Count
Source
252
128k
  void MaybeInitializeMemory(int min, int max) {
253
#if __has_feature(memory_sanitizer)
254
    __msan_unpoison(sparse_.data() + min, (max - min) * sizeof sparse_[0]);
255
#elif defined(RE2_ON_VALGRIND)
256
    for (int i = min; i < max; i++) {
257
      sparse_[i] = 0xababababU;
258
    }
259
#endif
260
128k
  }
re2::SparseArray<re2::NFA::Thread*>::MaybeInitializeMemory(int, int)
Line
Count
Source
252
2.91k
  void MaybeInitializeMemory(int min, int max) {
253
#if __has_feature(memory_sanitizer)
254
    __msan_unpoison(sparse_.data() + min, (max - min) * sizeof sparse_[0]);
255
#elif defined(RE2_ON_VALGRIND)
256
    for (int i = min; i < max; i++) {
257
      sparse_[i] = 0xababababU;
258
    }
259
#endif
260
2.91k
  }
261
262
  int size_ = 0;
263
  PODArray<int> sparse_;
264
  PODArray<IndexValue> dense_;
265
};
266
267
template<typename Value>
268
2.91k
SparseArray<Value>::SparseArray() = default;
269
270
template<typename Value>
271
SparseArray<Value>::SparseArray(const SparseArray& src)
272
40.1k
    : size_(src.size_),
273
40.1k
      sparse_(src.max_size()),
274
40.1k
      dense_(src.max_size()) {
275
40.1k
  std::copy_n(src.sparse_.data(), src.max_size(), sparse_.data());
276
40.1k
  std::copy_n(src.dense_.data(), src.max_size(), dense_.data());
277
40.1k
}
278
279
template<typename Value>
280
SparseArray<Value>::SparseArray(SparseArray&& src)
281
    : size_(src.size_),
282
      sparse_(std::move(src.sparse_)),
283
      dense_(std::move(src.dense_)) {
284
  src.size_ = 0;
285
}
286
287
template<typename Value>
288
SparseArray<Value>& SparseArray<Value>::operator=(const SparseArray& src) {
289
  // Construct these first for exception safety.
290
  PODArray<int> a(src.max_size());
291
  PODArray<IndexValue> b(src.max_size());
292
293
  size_ = src.size_;
294
  sparse_ = std::move(a);
295
  dense_ = std::move(b);
296
  std::copy_n(src.sparse_.data(), src.max_size(), sparse_.data());
297
  std::copy_n(src.dense_.data(), src.max_size(), dense_.data());
298
  return *this;
299
}
300
301
template<typename Value>
302
SparseArray<Value>& SparseArray<Value>::operator=(SparseArray&& src) {
303
  size_ = src.size_;
304
  sparse_ = std::move(src.sparse_);
305
  dense_ = std::move(src.dense_);
306
  src.size_ = 0;
307
  return *this;
308
}
309
310
// IndexValue pairs: exposed in SparseArray::iterator.
311
template<typename Value>
312
class SparseArray<Value>::IndexValue {
313
 public:
314
62.4M
  int index() const { return index_; }
re2::SparseArray<int>::IndexValue::index() const
Line
Count
Source
314
43.8M
  int index() const { return index_; }
re2::SparseArray<re2::NFA::Thread*>::IndexValue::index() const
Line
Count
Source
314
18.6M
  int index() const { return index_; }
315
42.8M
  Value& value() { return value_; }
re2::SparseArray<int>::IndexValue::value()
Line
Count
Source
315
11.5M
  Value& value() { return value_; }
re2::SparseArray<re2::NFA::Thread*>::IndexValue::value()
Line
Count
Source
315
31.2M
  Value& value() { return value_; }
316
22.0M
  const Value& value() const { return value_; }
317
318
 private:
319
  friend class SparseArray;
320
  int index_;
321
  Value value_;
322
};
323
324
// Change the maximum size of the array.
325
// Invalidates all iterators.
326
template<typename Value>
327
2.91k
void SparseArray<Value>::resize(int new_max_size) {
328
2.91k
  DebugCheckInvariants();
329
2.91k
  if (new_max_size > max_size()) {
330
2.91k
    const int old_max_size = max_size();
331
332
    // Construct these first for exception safety.
333
2.91k
    PODArray<int> a(new_max_size);
334
2.91k
    PODArray<IndexValue> b(new_max_size);
335
336
2.91k
    std::copy_n(sparse_.data(), old_max_size, a.data());
337
2.91k
    std::copy_n(dense_.data(), old_max_size, b.data());
338
339
2.91k
    sparse_ = std::move(a);
340
2.91k
    dense_ = std::move(b);
341
342
2.91k
    MaybeInitializeMemory(old_max_size, new_max_size);
343
2.91k
  }
344
2.91k
  if (size_ > new_max_size)
345
0
    size_ = new_max_size;
346
2.91k
  DebugCheckInvariants();
347
2.91k
}
348
349
// Check whether index i is in the array.
350
template<typename Value>
351
2.14G
bool SparseArray<Value>::has_index(int i) const {
352
2.14G
  assert(i >= 0);
353
2.14G
  assert(i < max_size());
354
2.14G
  if (static_cast<uint32_t>(i) >= static_cast<uint32_t>(max_size())) {
355
0
    return false;
356
0
  }
357
  // Unsigned comparison avoids checking sparse_[i] < 0.
358
2.14G
  return (uint32_t)sparse_[i] < (uint32_t)size_ &&
359
1.69G
         dense_[sparse_[i]].index_ == i;
360
2.14G
}
re2::SparseArray<int>::has_index(int) const
Line
Count
Source
351
2.10G
bool SparseArray<Value>::has_index(int i) const {
352
2.10G
  assert(i >= 0);
353
2.10G
  assert(i < max_size());
354
2.10G
  if (static_cast<uint32_t>(i) >= static_cast<uint32_t>(max_size())) {
355
0
    return false;
356
0
  }
357
  // Unsigned comparison avoids checking sparse_[i] < 0.
358
2.10G
  return (uint32_t)sparse_[i] < (uint32_t)size_ &&
359
1.68G
         dense_[sparse_[i]].index_ == i;
360
2.10G
}
re2::SparseArray<re2::NFA::Thread*>::has_index(int) const
Line
Count
Source
351
42.1M
bool SparseArray<Value>::has_index(int i) const {
352
42.1M
  assert(i >= 0);
353
42.1M
  assert(i < max_size());
354
42.1M
  if (static_cast<uint32_t>(i) >= static_cast<uint32_t>(max_size())) {
355
0
    return false;
356
0
  }
357
  // Unsigned comparison avoids checking sparse_[i] < 0.
358
42.1M
  return (uint32_t)sparse_[i] < (uint32_t)size_ &&
359
13.6M
         dense_[sparse_[i]].index_ == i;
360
42.1M
}
361
362
template<typename Value>
363
68.6M
void SparseArray<Value>::create_index(int i) {
364
68.6M
  assert(!has_index(i));
365
68.6M
  assert(size_ < max_size());
366
68.6M
  sparse_[i] = size_;
367
68.6M
  dense_[size_].index_ = i;
368
68.6M
  size_++;
369
68.6M
}
re2::SparseArray<int>::create_index(int)
Line
Count
Source
363
37.3M
void SparseArray<Value>::create_index(int i) {
364
37.3M
  assert(!has_index(i));
365
  assert(size_ < max_size());
366
37.3M
  sparse_[i] = size_;
367
37.3M
  dense_[size_].index_ = i;
368
37.3M
  size_++;
369
37.3M
}
re2::SparseArray<re2::NFA::Thread*>::create_index(int)
Line
Count
Source
363
31.2M
void SparseArray<Value>::create_index(int i) {
364
31.2M
  assert(!has_index(i));
365
  assert(size_ < max_size());
366
31.2M
  sparse_[i] = size_;
367
31.2M
  dense_[size_].index_ = i;
368
31.2M
  size_++;
369
31.2M
}
370
371
template<typename Value> SparseArray<Value>::SparseArray(int max_size) :
372
128k
    sparse_(max_size), dense_(max_size) {
373
128k
  MaybeInitializeMemory(size_, max_size);
374
128k
  DebugCheckInvariants();
375
128k
}
376
377
171k
template<typename Value> SparseArray<Value>::~SparseArray() {
378
171k
  DebugCheckInvariants();
379
171k
}
re2::SparseArray<int>::~SparseArray()
Line
Count
Source
377
168k
template<typename Value> SparseArray<Value>::~SparseArray() {
378
168k
  DebugCheckInvariants();
379
168k
}
re2::SparseArray<re2::NFA::Thread*>::~SparseArray()
Line
Count
Source
377
2.91k
template<typename Value> SparseArray<Value>::~SparseArray() {
378
2.91k
  DebugCheckInvariants();
379
2.91k
}
380
381
206M
template<typename Value> void SparseArray<Value>::DebugCheckInvariants() const {
382
206M
  assert(0 <= size_);
383
206M
  assert(size_ <= max_size());
384
206M
}
re2::SparseArray<int>::DebugCheckInvariants() const
Line
Count
Source
381
112M
template<typename Value> void SparseArray<Value>::DebugCheckInvariants() const {
382
112M
  assert(0 <= size_);
383
  assert(size_ <= max_size());
384
112M
}
re2::SparseArray<re2::NFA::Thread*>::DebugCheckInvariants() const
Line
Count
Source
381
93.8M
template<typename Value> void SparseArray<Value>::DebugCheckInvariants() const {
382
93.8M
  assert(0 <= size_);
383
  assert(size_ <= max_size());
384
93.8M
}
385
386
// Comparison function for sorting.
387
template<typename Value> bool SparseArray<Value>::less(const IndexValue& a,
388
124M
                                                       const IndexValue& b) {
389
124M
  return a.index_ < b.index_;
390
124M
}
391
392
}  // namespace re2
393
394
#endif  // RE2_SPARSE_ARRAY_H_