xref: /aosp_15_r20/external/icing/icing/legacy/index/icing-dynamic-trie.h (revision 8b6cd535a057e39b3b86660c4aa06c99747c2136)
1 // Copyright (C) 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Copyright 2011 Google Inc. All Rights Reserved.
16 // Author: [email protected] (Ulas Kirazci)
17 //
18 // Trie for word prefix lookups. Features:
19 //
20 // - Dynamic additions (but not deletions)
21 // - Low memory usage
22 // - Reasonable latency but not QPS
23 // - Revive from persistence is a disk read
24 // - Stores a 4-byte value associated with every key
25 //
26 // Associated with each value in the trie is a set of property ids.  For
27 // efficiency, property ids should start at 0 and be densely packed.  A value
28 // may have more than one id set.  There is an additional deleted property
29 // for each value, which is set only when all the property ids associated with a
30 // value have been cleared.  In the flash_index, property ids are used to track
31 // corpus ids.
32 //
33 // Not thread-safe.
34 
35 #ifndef ICING_LEGACY_INDEX_ICING_DYNAMIC_TRIE_H_
36 #define ICING_LEGACY_INDEX_ICING_DYNAMIC_TRIE_H_
37 
38 #include <cstdint>
39 #include <memory>
40 #include <string>
41 #include <string_view>
42 #include <unordered_map>
43 #include <vector>
44 
45 #include "icing/text_classifier/lib3/utils/base/status.h"
46 #include "icing/text_classifier/lib3/utils/base/statusor.h"
47 #include "icing/legacy/core/icing-compat.h"
48 #include "icing/legacy/core/icing-packed-pod.h"
49 #include "icing/legacy/index/icing-filesystem.h"
50 #include "icing/legacy/index/icing-mmapper.h"
51 #include "icing/legacy/index/icing-storage.h"
52 #include "icing/legacy/index/proto/icing-dynamic-trie-header.pb.h"
53 #include "icing/util/crc32.h"
54 #include "icing/util/i18n-utils.h"
55 #include "unicode/utf8.h"
56 
57 namespace icing {
58 namespace lib {
59 
60 class IcingFlashBitmap;
61 
62 class IcingDynamicTrie : public IIcingStorage {
63   class Dumper;
64   class IcingDynamicTrieStorage;
65 
66  public:
67   // Adjacent bit fields are usually packed automatically. However, that is
68   // implementation specific:
69   // http://en.cppreference.com/w/cpp/language/bit_field
70   // So we'll set packed to be explicit.
71   class Node {
72    public:
73     // This object is only ever used by an ArrayStorage, which allocates
74     // sizeof(Node) bytes, zeroes them out and then casts to a Node.
75     Node() = delete;
76 
next_index()77     uint32_t next_index() const { return next_index_; }
set_next_index(uint32_t next_index)78     void set_next_index(uint32_t next_index) { next_index_ = next_index; }
79 
is_leaf()80     bool is_leaf() const { return is_leaf_; }
set_is_leaf(bool is_leaf)81     void set_is_leaf(bool is_leaf) { is_leaf_ = is_leaf; }
82 
log2_num_children()83     uint8_t log2_num_children() const { return log2_num_children_; }
set_log2_num_children(uint8_t log2_num_children)84     void set_log2_num_children(uint8_t log2_num_children) {
85       log2_num_children_ = log2_num_children;
86     }
87 
88    private:
89     uint32_t next_index_ : 27;
90     uint32_t is_leaf_ : 1;
91     uint32_t log2_num_children_ : 4;
92   } __attribute__((packed));
93   static_assert(sizeof(Node) == 4, "");
94   static_assert(icing_is_packed_pod<Node>::value, "go/icing-ubsan");
95 
96   // Adjacent bit fields are usually packed automatically. However, that is
97   // implementation specific:
98   // http://en.cppreference.com/w/cpp/language/bit_field.
99   // So we'll set packed to be explicit.
100   union Next {
Next(uint8_t val,uint32_t node_index)101     Next(uint8_t val, uint32_t node_index) {
102       used.val = val;
103       used.node_index = node_index;
104     }
105 
val()106     uint8_t val() const { return used.val; }
set_val(uint8_t val)107     void set_val(uint8_t val) { used.val = val; }
108 
node_index()109     uint32_t node_index() const { return used.node_index; }
set_node_index(uint32_t node_index)110     void set_node_index(uint32_t node_index) { used.node_index = node_index; }
111 
next_index()112     uint32_t next_index() const { return freelink.next_index; }
set_next_index(uint32_t next_index)113     void set_next_index(uint32_t next_index) {
114       freelink.next_index = next_index;
115     }
116 
117     bool operator<(const Next &next2) const {
118       if (val() == next2.val()) {
119         return node_index() < next2.node_index();
120       }
121       return val() < next2.val();
122     }
123 
124    private:
125     // This object is only ever used by an ArrayStorage, which allocates
126     // sizeof(Node) bytes, zeroes them out and then casts to a Node.
127     Next() = default;
128 
129     struct {
130       uint32_t val : 8;
131       uint32_t node_index : 24;
132     } used;
133     struct {
134       uint32_t next_index : 32;
135     } freelink;
136   } __attribute__((packed));
137   static_assert(sizeof(Next) == 4, "");
138   static_assert(sizeof(Next) % alignof(Next) == 0, "");
139   static_assert(icing_is_packed_pod<Next>::value, "go/icing-ubsan");
140 
141   static const int kMaxNextArraySize = 256;
142   static const int kNumNextAllocationBuckets = 9;  // [log2(1), log2(256)]
143 
144   static const uint32_t kMaxPropertyId = (1 << 16) - 1;
145 
146   static const uint32_t kInvalidValueIndex = 0;
147 
148   static const uint32_t kNoCrc = 0;
149 
150   struct Stats {
151     uint32_t num_keys;
152 
153     // Node stats
154 
155     uint32_t num_nodes;
156     uint32_t max_nodes;
157     // Count of intermediate nodes.
158     uint32_t num_intermediates;
159     // Total and maximum number of children of intermediate nodes.
160     uint32_t sum_children, max_children;
161 
162     // Count of leaf nodes.
163     uint32_t num_leaves;
164     // Total and maximum depth of leaf nodes.
165     uint32_t sum_depth, max_depth;
166 
167     // Next stats
168 
169     uint32_t num_nexts;
170     uint32_t max_nexts;
171     // Count of next arrays by size.
172     uint32_t child_counts[kMaxNextArraySize];
173     // Wasted next array space per allocation bucket (in Nexts, not
174     // bytes).
175     uint32_t wasted[kNumNextAllocationBuckets];
176     // Sum of wasted array.
177     uint32_t total_wasted;
178 
179     // Suffix stats
180 
181     uint32_t suffixes_size;
182     uint32_t max_suffixes_size;
183     // Bytes actually used by suffixes.
184     uint32_t suffixes_used;
185     // Number of suffixes that are just empty strings.
186     uint32_t null_suffixes;
187 
188     // Next free-list stats
189     uint32_t num_free[kNumNextAllocationBuckets];
190     // Total Next nodes free (weighted sum of the above).
191     uint32_t total_free;
192 
193     // Dirty pages.
194     uint32_t dirty_pages_nodes;
195     uint32_t dirty_pages_nexts;
196     uint32_t dirty_pages_suffixes;
197 
198     // TODO(b/222349894) Convert the string output to a protocol buffer instead.
199     std::string DumpStats(int verbosity) const;
200   };
201 
202   // Options when creating the trie. Maximums for the node/next/suffix
203   // arrays must be specified in advance.
204   struct Options {
205     // Absolute maximums.
206     static const uint32_t kMaxNodes, kMaxNexts, kMaxSuffixesSize, kMaxValueSize;
207 
208     // The default takes 13MB of memory and can take about 1M English
209     // words.
OptionsOptions210     Options()
211         : max_nodes(1U << 20),
212           max_nexts(1U << 20),
213           max_suffixes_size(5U << 20),
214           value_size(sizeof(uint32_t)) {}
OptionsOptions215     Options(uint32_t max_nodes_in, uint32_t max_nexts_in,
216             uint32_t max_suffixes_size_in, uint32_t value_size_in)
217         : max_nodes(max_nodes_in),
218           max_nexts(max_nexts_in),
219           max_suffixes_size(max_suffixes_size_in),
220           value_size(value_size_in) {}
221 
222     uint32_t max_nodes;
223     uint32_t max_nexts;
224     uint32_t max_suffixes_size;
225     uint32_t value_size;
226 
227     // True if options do not exceed absolute maximums.
228     bool is_valid() const;
229   };
230 
231   // These can be supplied during runtime, as opposed to the persisted
232   // Options above.
233   struct RuntimeOptions {
234     enum StoragePolicy {
235       // Changes are reflected in the underlying file immediately but
236       // more vulnerable to corruption.
237       kMapSharedWithCrc,
238 
239       // Changes only applied during Flush. Smaller window of
240       // vulnerability to corruption.
241       kExplicitFlush,
242     };
243 
set_storage_policyRuntimeOptions244     RuntimeOptions &set_storage_policy(StoragePolicy sp) {
245       storage_policy = sp;
246       return *this;
247     }
248 
249     StoragePolicy storage_policy = kExplicitFlush;
250   };
251 
max_value_index(const Options & options)252   static uint32_t max_value_index(const Options &options) {
253     return options.max_suffixes_size;
254   }
255 
256   // Light-weight constructor. Real work happens in Create or Init.
257   IcingDynamicTrie(const std::string &filename_base,
258                    const RuntimeOptions &runtime_options,
259                    const IcingFilesystem *filesystem);
260   ~IcingDynamicTrie() override;
261 
is_initialized()262   bool is_initialized() const { return is_initialized_; }
263 
264   // Create, but do not Init, a new trie with options if the file does
265   // not already exist.
266   //
267   // Returns true if successfully created all files or files already
268   // exist. Does not do a complete sanity check for when files seem to
269   // exist. Cleans up files if creation fails midstream.
270   bool CreateIfNotExist(const Options &options);
271 
UpgradeTo(int new_version)272   bool UpgradeTo(int new_version) override { return true; }
273   bool Init() override;
274   void Close() override;
275   bool Remove() override;
276   uint64_t GetDiskUsage() const override;
277 
278   // Returns the size of the elements held in the trie. This excludes the size
279   // of any internal metadata of the trie, e.g. the trie's header.
280   uint64_t GetElementsSize() const;
281 
282   // REQUIRED: For all functions below is_initialized() == true.
283 
284   // Number of keys in trie.
285   uint32_t size() const;
286 
287   bool empty() const;
288 
289   // Collecting stats.
290   void CollectStats(Stats *stats) const;
291 
292   // Gets all of the contents of the trie for debugging purposes. Note: this
293   // stores the entire set of terms in memory.
294   //   pretty_print - The tree structure of the trie will be written to this.
295   //   keys - All keys in the trie are appended to this vector.
296   void DumpTrie(std::ostream *pretty_print,
297                 std::vector<std::string> *keys) const;
298 
299   // Empty out the trie without closing or removing.
300   void Clear();
301 
302   // Clears the suffix and value at the given index. Returns true on success.
303   bool ClearSuffixAndValue(uint32_t suffix_value_index);
304 
305   // Resets the next at the given index so that it points to no node.
306   // Returns true on success.
307   bool ResetNext(uint32_t next_index);
308 
309   // Sorts the next array of the node. Returns true on success.
310   bool SortNextArray(const Node *node);
311 
312   // Sync to disk.
313   bool Sync() override;
314 
315   // Tell kernel we will access the memory shortly.
316   void Warm() const;
317 
318   // Insert value at key. If key already exists and replace == true,
319   // replaces old value with value. We take a copy of value.
320   //
321   // If value_index is not NULL, returns a pointer to value in
322   // value_index. This can then be used with SetValueAtIndex
323   // below. value_index is not valid past a Clear/Read/Write.
324   //
325   // REQUIRES: value a buffer of size value_size()
326   //
327   // Returns:
328   //   OK on success
329   //   RESOURCE_EXHAUSTED if no disk space is available
330   //   INTERNAL_ERROR if there are inconsistencies in the dynamic trie.
Insert(std::string_view key,const void * value)331   libtextclassifier3::Status Insert(std::string_view key, const void *value) {
332     return Insert(key, value, nullptr, true, nullptr);
333   }
Insert(std::string_view key,const void * value,uint32_t * value_index,bool replace)334   libtextclassifier3::Status Insert(std::string_view key, const void *value,
335                                     uint32_t *value_index, bool replace) {
336     return Insert(key, value, value_index, replace, nullptr);
337   }
338   libtextclassifier3::Status Insert(std::string_view key, const void *value,
339                                     uint32_t *value_index, bool replace,
340                                     bool *pnew_key);
341 
342   // Get a value returned by Insert value_index. This points to the
343   // value in the trie. The pointer is immutable and always valid
344   // while the trie is alive.
345   const void *GetValueAtIndex(uint32_t value_index) const;
346 
347   // Set a value returned by Insert value_index. We take a copy of
348   // value.
349   //
350   // REQUIRES: value a buffer of size value_size()
351   void SetValueAtIndex(uint32_t value_index, const void *value);
352 
353   // Returns true if key is found and sets value. If value_index is
354   // not NULL, returns value_index (see Insert discussion above).
355   // If the key is not found, returns false and neither value nor
356   // value_index is modified.
357   //
358   // REQUIRES: value a buffer of size value_size()
Find(std::string_view key,void * value)359   bool Find(std::string_view key, void *value) const {
360     return Find(key, value, nullptr);
361   }
362   bool Find(std::string_view key, void *value, uint32_t *value_index) const;
363 
364   // Find the input key and all keys that are a variant of the input
365   // key according to a variant map. Currently supports
366   // transliteration. For example "a" is a variant for "à" or "á" so
367   // an "a" in the input key can match those characters in the trie in
368   // addition to itself.
369   //
370   // If prefix is set, also returns any prefix matches (so value_index
371   // will be invalid).
372   //
373   // REQUIRES: all terms in the lexicon to be valid utf8.
374   struct OriginalMatch {
375     uint32_t value_index;
376     std::string orig;
377 
OriginalMatchOriginalMatch378     OriginalMatch() : value_index(kInvalidValueIndex) {}
379 
is_full_matchOriginalMatch380     bool is_full_match() const { return value_index != kInvalidValueIndex; }
381   };
382 
383   static constexpr int kNoBranchFound = -1;
384   // Return prefix of any new branches created if key were inserted. If utf8 is
385   // true, does not cut key mid-utf8. Returns kNoBranchFound if no branches
386   // would be created.
387   int FindNewBranchingPrefixLength(std::string_view key, bool utf8) const;
388 
389   // Find all prefixes of key where the trie branches. Excludes the key
390   // itself. If utf8 is true, does not cut key mid-utf8.
391   std::vector<int> FindBranchingPrefixLengths(std::string_view key,
392                                               bool utf8) const;
393 
394   // Check if key is a branching term.
395   //
396   // key is a branching term, if and only if there exists terms s1 and s2 in the
397   // trie such that key is the maximum common prefix of s1 and s2, but s1 and s2
398   // are not prefixes of each other.
399   bool IsBranchingTerm(std::string_view key) const;
400 
401   void GetDebugInfo(int verbosity, std::string *out) const override;
402 
403   double min_free_fraction() const;
404 
405   uint32_t value_size() const;
406 
407   uint32_t max_value_index() const;
408 
409   // If in kMapSharedWithCrc mode, update crcs and return the master
410   // crc, else return kNoCrc. This crc includes both the trie files
411   // and property bitmaps.
412   Crc32 UpdateCrc() override;
413 
414   // If in kMapSharedWithCrc mode, calculates crcs and return the master
415   // crc, else return kNoCrc. This crc includes both the trie files
416   // and property bitmaps. Does NOT update any stored crcs.
417   Crc32 GetCrc() const;
418 
419   // Store dynamic properties for each value.  When a property is added to
420   // a value, the deleted flag is cleared for it (if it was previously set).
421   bool SetProperty(uint32_t value_index, uint32_t property_id);
422   bool ClearProperty(uint32_t value_index, uint32_t property_id);
423 
424   // Store deleted property for each value.
425   // This method is not the only way the deleted property can be set; the trie
426   // may set this property itself during other operations if it can determine a
427   // value becomes superfluous.
428   bool SetDeleted(uint32_t value_index);
429 
430   // Clears the deleted property for each value.
431   bool ClearDeleted(uint32_t value_index);
432 
433   // Deletes the entry associated with the key. Data can not be recovered after
434   // the deletion. Returns true on success.
435   bool Delete(std::string_view key);
436 
437   // Clear a specific property id from all values.  For each value that has this
438   // property cleared, also check to see if it was the only property set;  if
439   // so, set the deleted property for the value to indicate it no longer has any
440   // properties associated with it.
441   bool ClearPropertyForAllValues(uint32_t property_id);
442 
443   // Access properties. Usage:
444   //
445   // IcingDynamicTrie::PropertyReader reader(trie, 10);
446   // char value[SIZE];
447   // uint32_t value_index;
448   // if (trie.Find("abc", value, &value_index) &&
449   //     reader.HasProperty(value_index)) {
450   //     ...
451   // }
452   //
453   // Readers are valid as long as the underlying trie is open.
454   class PropertyReaderBase {
455    public:
456     // Whether underlying file exists.
457     bool Exists() const;
458 
459     // Returns false for all values if underlying file is missing.
460     bool HasProperty(uint32_t value_index) const;
461 
462    protected:
463     PropertyReaderBase(const IcingDynamicTrie &trie, bool deleted,
464                        uint32_t property_id);
465 
466     // Does not own.
467     const IcingFlashBitmap *bitmap_;
468     const IcingDynamicTrie &trie_;
469   };
470 
471   // Reader for a given property. It is invalidated when the underlying property
472   // is deleted, or the trie is closed.
473   class PropertyReader : public PropertyReaderBase {
474    public:
PropertyReader(const IcingDynamicTrie & trie,uint32_t property_id)475     PropertyReader(const IcingDynamicTrie &trie, uint32_t property_id)
476         : PropertyReaderBase(trie, false, property_id) {}
477   };
478 
479   // Reader for the deleted property. It is invalidated when the trie is closed.
480   class PropertyDeletedReader : public PropertyReaderBase {
481    public:
PropertyDeletedReader(const IcingDynamicTrie & trie)482     explicit PropertyDeletedReader(const IcingDynamicTrie &trie)
483         : PropertyReaderBase(trie, true, 0) {}
484   };
485 
486   // Reader for all properties (but not the deleted one). It is invalidated when
487   // the trie is closed.
488   class PropertyReadersAll {
489    public:
490     explicit PropertyReadersAll(const IcingDynamicTrie &trie);
491 
492     // Whether underlying file for property_id exists.
493     bool Exists(uint32_t property_id) const;
494 
495     // Returns false if underlying file or property doesn't exist.
496     bool HasProperty(uint32_t property_id, uint32_t value_index) const;
497 
498     // Returns true if the value at value_index is set for the only the supplied
499     // property_id, and none of the other properties.
500     bool IsPropertyUnique(uint32_t property_id, uint32_t value_index) const;
501 
502     // For iterating.
503     size_t size() const;
504 
505    private:
506     const IcingDynamicTrie &trie_;
507   };
508 
509   // Iterate through trie in lexicographic order.
510   //
511   // Not thread-safe.
512   //
513   // Change in underlying trie invalidates iterator.
514   //
515   // TODO(b/241784804): change IcingDynamicTrie::Iterator to follow the common
516   //                    iterator pattern in our codebase.
517   class Iterator {
518    public:
519     Iterator(const IcingDynamicTrie &trie, std::string prefix,
520              bool reverse = false);
521     void Reset();
522     bool Advance();
523 
524     // If !IsValid(), GetKey() will return a std::string_view object with
525     // data() == nullptr and size() == 0, and GetValue() will return nullptr.
526     bool IsValid() const;
527     std::string_view GetKey() const;
528     // This points directly to the underlying data and is valid while
529     // the trie is alive. We keep ownership of the pointer.
530     const void *GetValue() const;
531     uint32_t GetValueIndex() const;
532 
533    private:
534     Iterator();
535     // Copy is ok.
536 
537     enum class BranchType { kLeftMost = 0, kRightMost = 1 };
538     // Helper function that takes the left-most or the right-most branch down
539     // intermediate nodes to a leaf, based on branch_type.
540     void BranchToLeaf(uint32_t node_index, BranchType branch_type);
541 
542     std::string cur_key_;
543     const char *cur_suffix_;
544     int cur_suffix_len_;
545     struct Branch {
546       uint32_t node_idx;
547       int child_idx;
548 
BranchBranch549       explicit Branch(uint32_t node_index, int child_index)
550           : node_idx(node_index), child_idx(child_index) {}
551     };
552     std::vector<Branch> branch_stack_;
553     bool single_leaf_match_;
554     bool reverse_;
555 
556     const IcingDynamicTrie &trie_;
557   };
558 
559   // Represents a non-leaf node or a "virtual" trie node in the suffix
560   // region.
561   struct LogicalNode {
562     const Node *node;
563     int suffix_offset;
564 
LogicalNodeLogicalNode565     LogicalNode() : node(nullptr), suffix_offset(0) {}
LogicalNodeLogicalNode566     LogicalNode(const Node *node_in, int suffix_offset_in)
567         : node(node_in), suffix_offset(suffix_offset_in) {}
568   };
569 
570   // Iterate over all utf8 chars in the trie anchored at prefix (or
571   // node). If trie has invalid utf8 chars, behavior is undefined (but
572   // won't crash).
573   class Utf8Iterator {
574    public:
575     void Reset();
576     bool Advance();
577 
578     bool IsValid() const;
579 
580    private:
581     struct Branch {
582       const Node *node;
583       const Next *child;
584       const Next *child_end;
585 
586       bool IsFinished();
587     };
588 
589     Utf8Iterator();
590     // Copy is ok.
591 
592     void LeftBranchToUtf8End();
593     void InitBranch(Branch *branch, const Node *start, char key_char);
594     void GoIntoSuffix(const Node *node);
595 
596     char cur_[U8_MAX_LENGTH + 1];  // NULL-terminated
597     int cur_len_;
598     LogicalNode cur_logical_node_;
599 
600     Branch branch_stack_[U8_MAX_LENGTH];
601     Branch *branch_end_;
602 
603     const IcingDynamicTrie &trie_;
604     const Node *start_node_;
605   };
606 
607  private:
608   class CandidateSet;
609 
610   // For testing only.
611   friend class IcingDynamicTrieTest_TrieShouldRespectLimits_Test;
612   friend class IcingDynamicTrieTest_SyncErrorRecovery_Test;
613   friend class IcingDynamicTrieTest_BitmapsClosedWhenInitFails_Test;
614   void GetHeader(IcingDynamicTrieHeader *hdr) const;
615   void SetHeader(const IcingDynamicTrieHeader &new_hdr);
616 
617   static const uint32_t kInvalidSuffixIndex;
618 
619   // Stats helpers.
620   //
621   // REQUIRES: node is valid.
622   //   - Since we only invalidate Next to remove the edge from the trie and Node
623   //     is not invalidated after deletion, the caller should ensure that it
624   //     traverses correctly to a valid node according to the trie structure.
625   //     Calling this function with an invalid node is undefined behavior since
626   //     it could traverse into a deleted subtree, or invalid memory addresses.
627   //   - This also means storage_->empty() should be checked before calling this
628   //     function with the root node.
629   void CollectStatsRecursive(const Node &node, Stats *stats,
630                              uint32_t depth = 0) const;
631 
632   // Helpers for Find and Insert.
633   const Next *GetNextByChar(const Node *node, uint8_t key_char) const;
634   const Next *LowerBound(const Next *start, const Next *end, uint8_t key_char,
635                          uint32_t node_index = 0) const;
636   // Returns the number of valid nexts in the array.
637   int GetValidNextsSize(const IcingDynamicTrie::Next *next_array_start,
638                         int next_array_length) const;
639   void FindBestNode(std::string_view key, uint32_t *best_node_index,
640                     int *key_offset, bool prefix, bool utf8 = false) const;
641 
642   // For value properties.  This truncates the data by clearing it, but leaving
643   // the storage intact.
644   bool InitPropertyBitmaps();
645 
646   // Returns a pointer to a bitmap that is successfully opened.
647   static std::unique_ptr<IcingFlashBitmap> OpenAndInitBitmap(
648       const std::string &filename, bool verify,
649       const IcingFilesystem *filesystem);
650 
651   // Returns a pointer to a writable bitmap, creating it if necessary.  Returned
652   // pointer should not be freed, it will be maintained by property_bitmaps_.
653   // Returns null if bitmap failed to load.
654   IcingFlashBitmap *OpenOrCreatePropertyBitmap(uint32_t property_id);
655 
656   uint64_t ValueIndexToPropertyBitmapIndex(uint32_t value_index) const;
657 
658   const std::string filename_base_;
659   bool is_initialized_;
660   const RuntimeOptions runtime_options_;
661   std::unique_ptr<IcingDynamicTrieStorage> storage_;
662   const std::string property_bitmaps_prefix_;
663   std::vector<std::unique_ptr<IcingFlashBitmap>> property_bitmaps_;
664   const std::string deleted_bitmap_filename_;
665   std::unique_ptr<IcingFlashBitmap> deleted_bitmap_;
666   const IcingFilesystem *const filesystem_;
667 };
668 
669 }  // namespace lib
670 }  // namespace icing
671 
672 #endif  // ICING_LEGACY_INDEX_ICING_DYNAMIC_TRIE_H_
673