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