SharedPrefMap.h (37666B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef dom_ipc_SharedPrefMap_h 8 #define dom_ipc_SharedPrefMap_h 9 10 #include "mozilla/Preferences.h" 11 #include "mozilla/Result.h" 12 #include "mozilla/dom/ipc/StringTable.h" 13 #include "mozilla/ipc/SharedMemoryHandle.h" 14 #include "mozilla/ipc/SharedMemoryMapping.h" 15 #include "nsTHashMap.h" 16 17 namespace mozilla { 18 19 // The approximate number of preferences expected to be in an ordinary 20 // preferences database. 21 // 22 // This number is used to determine initial allocation sizes for data structures 23 // when building the shared preference map, and should be slightly higher than 24 // the expected number of preferences in an ordinary database to avoid 25 // unnecessary reallocations/rehashes. 26 constexpr size_t kExpectedPrefCount = 4000; 27 28 class SharedPrefMapBuilder; 29 30 // This class provides access to a compact, read-only copy of a preference 31 // database, backed by a shared memory buffer which can be shared between 32 // processes. All state data for the database is stored in the shared memory 33 // region, so individual instances require no dynamic memory allocation. 34 // 35 // Further, all strings returned from this API are nsLiteralCStrings with 36 // pointers into the shared memory region, which means that they can be copied 37 // into new nsCString instances without additional allocations. For instance, 38 // the following (where `pref` is a Pref object) will not cause any string 39 // copies, memory allocations, or atomic refcount changes: 40 // 41 // nsCString prefName(pref.NameString()); 42 // 43 // whereas if we returned a nsDependentCString or a dynamically allocated 44 // nsCString, it would. 45 // 46 // The set of entries is stored in sorted order by preference name, so look-ups 47 // are done by binary search. This means that look-ups have O(log n) complexity, 48 // rather than the O(1) complexity of a dynamic hashtable. Consumers should keep 49 // this in mind when planning their accesses. 50 // 51 // Important: The mapped memory created by this class is persistent. Once an 52 // instance has been initialized, the memory that it allocates can never be 53 // freed before process shutdown. Do not use it for short-lived mappings. 54 class SharedPrefMap { 55 friend class SharedPrefMapBuilder; 56 57 // Describes a block of memory within the shared memory region. 58 struct DataBlock { 59 // The byte offset from the start of the shared memory region to the start 60 // of the block. 61 size_t mOffset; 62 // The size of the block, in bytes. This is typically used only for bounds 63 // checking in debug builds. 64 size_t mSize; 65 }; 66 67 // Describes the contents of the shared memory region, which is laid-out as 68 // follows: 69 // 70 // - The Header struct 71 // 72 // - An array of Entry structs with mEntryCount elements, lexicographically 73 // sorted by preference name. 74 // 75 // - A set of data blocks, with offsets and sizes described by the DataBlock 76 // entries in the header, described below. 77 // 78 // Each entry stores its name string and values as indices into these blocks, 79 // as documented in the Entry struct, but with some important optimizations: 80 // 81 // - Boolean values are always stored inline. Both the default and user 82 // values can be retrieved directly from the entry. Other types have only 83 // one value index, and their values appear at the same indices in the 84 // default and user value arrays. 85 // 86 // Aside from reducing our memory footprint, this space-efficiency means 87 // that we can fit more entries in the CPU cache at once, and reduces the 88 // number of likely cache misses during lookups. 89 // 90 // - Key strings are stored in a separate string table from value strings. As 91 // above, this makes it more likely that the strings we need will be 92 // available in the CPU cache during lookups by not interleaving them with 93 // extraneous data. 94 // 95 // - Default and user values are stored in separate arrays. Entries with user 96 // values always appear before entries with default values in the value 97 // arrays, and entries without user values do not have entries in the user 98 // array at all. Since the same index is used for both arrays, this means 99 // that entries with a default value but no user value do not allocate any 100 // space to store their user value. 101 // 102 // - For preferences with no user value, the entries in the default value are 103 // de-duplicated. All preferences with the same default value (and no user 104 // value) point to the same index in the default value array. 105 // 106 // 107 // For example, a preference database containing: 108 // 109 // +---------+-------------------------------+-------------------------------+ 110 // | Name | Default Value | User Value | | 111 // +---------+---------------+---------------+-------------------------------+ 112 // | string1 | "meh" | "hem" | | 113 // | string2 | | "b" | | 114 // | string3 | "a" | | | 115 // | string4 | "foo" | | | 116 // | string5 | "foo" | | | 117 // | string6 | "meh" | | | 118 // +---------+---------------+---------------+-------------------------------+ 119 // | bool1 | false | true | | 120 // | bool2 | | false | | 121 // | bool3 | true | | | 122 // +---------+---------------+---------------+-------------------------------+ 123 // | int1 | 18 | 16 | | 124 // | int2 | | 24 | | 125 // | int3 | 42 | | | 126 // | int4 | 12 | | | 127 // | int5 | 12 | | | 128 // | int6 | 18 | | | 129 // +---------+---------------+---------------+-------------------------------+ 130 // 131 // Results in a database that looks like: 132 // 133 // +-------------------------------------------------------------------------+ 134 // | Header: | 135 // +-------------------------------------------------------------------------+ 136 // | mEntryCount = 15 | 137 // | ... | 138 // +-------------------------------------------------------------------------+ 139 // 140 // +-------------------------------------------------------------------------+ 141 // | Key strings: | 142 // +--------+----------------------------------------------------------------+ 143 // | Offset | Value | 144 // +--------+----------------------------------------------------------------+ 145 // | 0 | string1\0 | 146 // | 8 | string2\0 | 147 // | 16 | string3\0 | 148 // | 24 | string4\0 | 149 // | 32 | string5\0 | 150 // | 40 | string6\0 | 151 // | 48 | bool1\0 | 152 // | 54 | bool2\0 | 153 // | 60 | bool3\0 | 154 // | 66 | int1\0 | 155 // | 71 | int2\0 | 156 // | 76 | int3\0 | 157 // | 81 | int4\0 | 158 // | 86 | int6\0 | 159 // | 91 | int6\0 | 160 // +--------+----------------------------------------------------------------+ 161 // 162 // +-------------------------------------------------------------------------+ 163 // | Entries: | 164 // +---------------------+------+------------+------------+------------------+ 165 // | Key[1] | Type | HasDefault | HasUser | Value | 166 // +---------------------+------+------------+------------+------------------+ 167 // | K["bool1", 48, 5] | 3 | true | true | { false, true } | 168 // | K["bool2", 54, 5] | 3 | false | true | { 0, false } | 169 // | K["bool3", 60, 5] | 3 | true | false | { true, 0 } | 170 // | K["int1", 66, 4] | 2 | true | true | 0 | 171 // | K["int2", 71, 4] | 2 | false | true | 1 | 172 // | K["int3", 76, 4] | 2 | true | false | 2 | 173 // | K["int4", 81, 4] | 2 | true | false | 3 | 174 // | K["int5", 86, 4] | 2 | true | false | 3 | 175 // | K["int6", 91, 4] | 2 | true | false | 4 | 176 // | K["string1", 0, 6] | 1 | true | true | 0 | 177 // | K["string2", 8, 6] | 1 | false | true | 1 | 178 // | K["string3", 16, 6] | 1 | true | false | 2 | 179 // | K["string4", 24, 6] | 1 | true | false | 3 | 180 // | K["string5", 32, 6] | 1 | true | false | 3 | 181 // | K["string6", 40, 6] | 1 | true | false | 4 | 182 // +---------------------+------+------------+------------+------------------+ 183 // | [1]: Encoded as an offset into the key table and a length. Specified | 184 // | as K[string, offset, length] for clarity. | 185 // +-------------------------------------------------------------------------+ 186 // 187 // +------------------------------------+------------------------------------+ 188 // | User integer values | Default integer values | 189 // +-------+----------------------------+-------+----------------------------+ 190 // | Index | Contents | Index | Contents | 191 // +-------+----------------------------+-------+----------------------------+ 192 // | 0 | 16 | 0 | 18 | 193 // | 1 | 24 | 1 | | 194 // | | | 2 | 42 | 195 // | | | 3 | 12 | 196 // | | | 4 | 18 | 197 // +-------+----------------------------+-------+----------------------------+ 198 // | * Note: Tables are laid out sequentially in memory, but displayed | 199 // | here side-by-side for clarity. | 200 // +-------------------------------------------------------------------------+ 201 // 202 // +------------------------------------+------------------------------------+ 203 // | User string values | Default string values | 204 // +-------+----------------------------+-------+----------------------------+ 205 // | Index | Contents[1] | Index | Contents[1] | 206 // +-------+----------------------------+-------+----------------------------+ 207 // | 0 | V["hem", 0, 3] | 0 | V["meh", 4, 3] | 208 // | 1 | V["b", 8, 1] | 1 | | 209 // | | | 2 | V["a", 10, 1] | 210 // | | | 3 | V["foo", 12, 3] | 211 // | | | 4 | V["meh", 4, 3] | 212 // |-------+----------------------------+-------+----------------------------+ 213 // | [1]: Encoded as an offset into the value table and a length. Specified | 214 // | as V[string, offset, length] for clarity. | 215 // +-------------------------------------------------------------------------+ 216 // | * Note: Tables are laid out sequentially in memory, but displayed | 217 // | here side-by-side for clarity. | 218 // +-------------------------------------------------------------------------+ 219 // 220 // +-------------------------------------------------------------------------+ 221 // | Value strings: | 222 // +--------+----------------------------------------------------------------+ 223 // | Offset | Value | 224 // +--------+----------------------------------------------------------------+ 225 // | 0 | hem\0 | 226 // | 4 | meh\0 | 227 // | 8 | b\0 | 228 // | 10 | a\0 | 229 // | 12 | foo\0 | 230 // +--------+----------------------------------------------------------------+ 231 struct Header { 232 // The number of entries in this map. 233 uint32_t mEntryCount; 234 235 // The StringTable data block for preference name strings, which act as keys 236 // in the map. 237 DataBlock mKeyStrings; 238 239 // The int32_t arrays of user and default int preference values. Entries in 240 // the map store their values as indices into these arrays. 241 DataBlock mUserIntValues; 242 DataBlock mDefaultIntValues; 243 244 // The StringTableEntry arrays of user and default string preference values. 245 // 246 // Strings are stored as StringTableEntry structs with character offsets 247 // into the mValueStrings string table and their corresponding lengths. 248 // 249 // Entries in the map, likewise, store their string values as indices into 250 // these arrays. 251 DataBlock mUserStringValues; 252 DataBlock mDefaultStringValues; 253 254 // The StringTable data block for string preference values, referenced by 255 // the above two data blocks. 256 DataBlock mValueStrings; 257 }; 258 259 using StringTableEntry = mozilla::dom::ipc::StringTableEntry; 260 261 // Represents a preference value, as either a pair of boolean values, or an 262 // index into one of the above value arrays. 263 union Value { 264 Value(bool aDefaultValue, bool aUserValue) 265 : mDefaultBool(aDefaultValue), mUserBool(aUserValue) {} 266 267 MOZ_IMPLICIT Value(uint16_t aIndex) : mIndex(aIndex) {} 268 269 // The index of this entry in the value arrays. 270 // 271 // User and default preference values have the same indices in their 272 // respective arrays. However, entries without a user value are not 273 // guaranteed to have space allocated for them in the user value array, and 274 // likewise for preferences without default values in the default value 275 // array. This means that callers must only access value entries for entries 276 // which claim to have a value of that type. 277 uint16_t mIndex; 278 struct { 279 bool mDefaultBool; 280 bool mUserBool; 281 }; 282 }; 283 284 // Represents a preference entry in the map, containing its name, type info, 285 // flags, and a reference to its value. 286 struct Entry { 287 // A pointer to the preference name in the KeyTable string table. 288 StringTableEntry mKey; 289 290 // The preference's value, either as a pair of booleans, or an index into 291 // the value arrays. Please see the documentation for the Value struct 292 // above. 293 Value mValue; 294 295 // The preference's type, as a PrefType enum value. This must *never* be 296 // PrefType::None for values in a shared array. 297 uint8_t mType : 2; 298 // True if the preference has a default value. Callers must not attempt to 299 // access the entry's default value if this is false. 300 uint8_t mHasDefaultValue : 1; 301 // True if the preference has a user value. Callers must not attempt to 302 // access the entry's user value if this is false. 303 uint8_t mHasUserValue : 1; 304 // True if the preference is sticky, as defined by the preference service. 305 uint8_t mIsSticky : 1; 306 // True if the preference is locked, as defined by the preference service. 307 uint8_t mIsLocked : 1; 308 // True if the preference is sanitized, as defined by the preference 309 // service. 310 uint8_t mIsSanitized : 1; 311 // True if the preference should be skipped while iterating over the 312 // SharedPrefMap. This is used to internally store Once StaticPrefs. 313 // This property is not visible to users the way sticky and locked are. 314 uint8_t mIsSkippedByIteration : 1; 315 }; 316 317 public: 318 NS_INLINE_DECL_REFCOUNTING(SharedPrefMap) 319 320 // A temporary wrapper class for accessing entries in the array. Instances of 321 // this class are valid as long as SharedPrefMap instance is alive, but 322 // generally should not be stored long term, or allocated on the heap. 323 // 324 // The class is implemented as two pointers, one to the SharedPrefMap 325 // instance, and one to the Entry that corresponds to the preference, and is 326 // meant to be cheaply returned by value from preference lookups and 327 // iterators. All property accessors lazily fetch the appropriate values from 328 // the shared memory region. 329 class MOZ_STACK_CLASS Pref final { 330 public: 331 const char* Name() const { return mMap->KeyTable().GetBare(mEntry->mKey); } 332 333 nsCString NameString() const { return mMap->KeyTable().Get(mEntry->mKey); } 334 335 PrefType Type() const { 336 MOZ_ASSERT(PrefType(mEntry->mType) != PrefType::None); 337 return PrefType(mEntry->mType); 338 } 339 340 bool HasDefaultValue() const { return mEntry->mHasDefaultValue; } 341 bool HasUserValue() const { return mEntry->mHasUserValue; } 342 bool IsLocked() const { return mEntry->mIsLocked; } 343 bool IsSanitized() const { return mEntry->mIsSanitized; } 344 bool IsSticky() const { return mEntry->mIsSticky; } 345 bool IsSkippedByIteration() const { return mEntry->mIsSkippedByIteration; } 346 347 bool GetBoolValue(PrefValueKind aKind = PrefValueKind::User) const { 348 MOZ_ASSERT(Type() == PrefType::Bool); 349 MOZ_ASSERT(aKind == PrefValueKind::Default ? HasDefaultValue() 350 : HasUserValue()); 351 352 return aKind == PrefValueKind::Default ? mEntry->mValue.mDefaultBool 353 : mEntry->mValue.mUserBool; 354 } 355 356 int32_t GetIntValue(PrefValueKind aKind = PrefValueKind::User) const { 357 MOZ_ASSERT(Type() == PrefType::Int); 358 MOZ_ASSERT(aKind == PrefValueKind::Default ? HasDefaultValue() 359 : HasUserValue()); 360 361 return aKind == PrefValueKind::Default 362 ? mMap->DefaultIntValues()[mEntry->mValue.mIndex] 363 : mMap->UserIntValues()[mEntry->mValue.mIndex]; 364 } 365 366 private: 367 const StringTableEntry& GetStringEntry(PrefValueKind aKind) const { 368 MOZ_ASSERT(Type() == PrefType::String); 369 MOZ_ASSERT(aKind == PrefValueKind::Default ? HasDefaultValue() 370 : HasUserValue()); 371 372 return aKind == PrefValueKind::Default 373 ? mMap->DefaultStringValues()[mEntry->mValue.mIndex] 374 : mMap->UserStringValues()[mEntry->mValue.mIndex]; 375 } 376 377 public: 378 nsCString GetStringValue(PrefValueKind aKind = PrefValueKind::User) const { 379 return mMap->ValueTable().Get(GetStringEntry(aKind)); 380 } 381 382 const char* GetBareStringValue( 383 PrefValueKind aKind = PrefValueKind::User) const { 384 return mMap->ValueTable().GetBare(GetStringEntry(aKind)); 385 } 386 387 // Returns the entry's index in the map, as understood by GetKeyAt() and 388 // GetValueAt(). 389 size_t Index() const { return mEntry - mMap->Entries().get(); } 390 391 bool operator==(const Pref& aPref) const { return mEntry == aPref.mEntry; } 392 bool operator!=(const Pref& aPref) const { return !(*this == aPref); } 393 394 // This is odd, but necessary in order for the C++ range iterator protocol 395 // to work here. 396 Pref& operator*() { return *this; } 397 398 // Updates this wrapper to point to the next visible entry in the map. This 399 // should not be attempted unless Index() is less than the map's Count(). 400 Pref& operator++() { 401 do { 402 mEntry++; 403 } while (mEntry->mIsSkippedByIteration && Index() < mMap->Count()); 404 return *this; 405 } 406 407 Pref(const Pref& aPref) = default; 408 409 protected: 410 friend class SharedPrefMap; 411 412 Pref(const SharedPrefMap* aPrefMap, const Entry* aEntry) 413 : mMap(aPrefMap), mEntry(aEntry) {} 414 415 private: 416 const SharedPrefMap* const mMap; 417 const Entry* mEntry; 418 }; 419 420 // Note: These constructors are infallible, because the preference database is 421 // critical to platform functionality, and we cannot operate without it. 422 explicit SharedPrefMap(const mozilla::ipc::ReadOnlySharedMemoryHandle&); 423 explicit SharedPrefMap(SharedPrefMapBuilder&&); 424 425 // Searches for the given preference in the map, and returns true if it 426 // exists. 427 bool Has(const char* aKey) const; 428 429 bool Has(const nsCString& aKey) const { return Has(aKey.get()); } 430 431 // Searches for the given preference in the map, and if it exists, returns 432 // a Some<Pref> containing its details. 433 Maybe<const Pref> Get(const char* aKey) const; 434 435 Maybe<const Pref> Get(const nsCString& aKey) const { return Get(aKey.get()); } 436 437 private: 438 // Searches for an entry for the given key. If found, returns true, and 439 // places its index in the entry array in aIndex. 440 bool Find(const char* aKey, size_t* aIndex) const; 441 442 public: 443 // Returns the number of entries in the map. 444 uint32_t Count() const { return EntryCount(); } 445 446 // Returns the string entry at the given index. Keys are guaranteed to be 447 // sorted lexicographically. 448 // 449 // The given index *must* be less than the value returned by Count(). 450 // 451 // The returned value is a literal string which references the mapped memory 452 // region. 453 nsCString GetKeyAt(uint32_t aIndex) const { 454 MOZ_ASSERT(aIndex < Count()); 455 return KeyTable().Get(Entries()[aIndex].mKey); 456 } 457 458 // Returns the value for the entry at the given index. 459 // 460 // The given index *must* be less than the value returned by Count(). 461 // 462 // The returned value is valid for the lifetime of this map instance. 463 const Pref GetValueAt(uint32_t aIndex) const { 464 MOZ_ASSERT(aIndex < Count()); 465 return UncheckedGetValueAt(aIndex); 466 } 467 468 private: 469 // Returns a wrapper with a pointer to an entry without checking its bounds. 470 // This should only be used by range iterators, to check their end positions. 471 // 472 // Note: In debug builds, the RangePtr returned by entries will still assert 473 // that aIndex is no more than 1 past the last element in the array, since it 474 // also takes into account the ranged iteration use case. 475 Pref UncheckedGetValueAt(uint32_t aIndex) const { 476 return {this, (Entries() + aIndex).get()}; 477 } 478 479 public: 480 // C++ range iterator protocol. begin() and end() return references to the 481 // first (non-skippable) and last entries in the array. The begin wrapper 482 // can be incremented until it matches the last element in the array, at which 483 // point it becomes invalid and the iteration is over. 484 Pref begin() const { 485 for (uint32_t aIndex = 0; aIndex < Count(); aIndex++) { 486 Pref pref = UncheckedGetValueAt(aIndex); 487 if (!pref.IsSkippedByIteration()) { 488 return pref; 489 } 490 } 491 return end(); 492 } 493 Pref end() const { return UncheckedGetValueAt(Count()); } 494 495 // A cosmetic helper for range iteration. Returns a reference value from a 496 // pointer to this instance so that its .begin() and .end() methods can be 497 // accessed in a ranged for loop. `map->Iter()` is equivalent to `*map`, but 498 // makes its purpose slightly clearer. 499 const SharedPrefMap& Iter() const { return *this; } 500 501 // Returns a copy of the read-only shared memory handle which backs the shared 502 // memory region for this map. The handle may be passed between processes, and 503 // used to construct new instances of SharedPrefMap with the same data as this 504 // instance. 505 mozilla::ipc::ReadOnlySharedMemoryHandle CloneHandle() const; 506 507 // Returns the size of the mapped memory region. This size must be passed to 508 // the constructor when mapping the shared region in another process. 509 size_t MapSize() const { return mMappedMemory.size(); } 510 511 protected: 512 ~SharedPrefMap() = default; 513 514 private: 515 template <typename T> 516 using StringTable = mozilla::dom::ipc::StringTable<T>; 517 518 // Type-safe getters for values in the shared memory region: 519 const Header& GetHeader() const { 520 return *reinterpret_cast<const Header*>(mMappedMemory.data()); 521 } 522 523 RangedPtr<const Entry> Entries() const { 524 return {reinterpret_cast<const Entry*>(&GetHeader() + 1), EntryCount()}; 525 } 526 527 uint32_t EntryCount() const { return GetHeader().mEntryCount; } 528 529 template <typename T> 530 RangedPtr<const T> GetBlock(const DataBlock& aBlock) const { 531 return RangedPtr<const uint8_t>(&mMappedMemory.data()[aBlock.mOffset], 532 aBlock.mSize) 533 .ReinterpretCast<const T>(); 534 } 535 536 RangedPtr<const int32_t> DefaultIntValues() const { 537 return GetBlock<int32_t>(GetHeader().mDefaultIntValues); 538 } 539 RangedPtr<const int32_t> UserIntValues() const { 540 return GetBlock<int32_t>(GetHeader().mUserIntValues); 541 } 542 543 RangedPtr<const StringTableEntry> DefaultStringValues() const { 544 return GetBlock<StringTableEntry>(GetHeader().mDefaultStringValues); 545 } 546 RangedPtr<const StringTableEntry> UserStringValues() const { 547 return GetBlock<StringTableEntry>(GetHeader().mUserStringValues); 548 } 549 550 StringTable<nsCString> KeyTable() const { 551 auto& block = GetHeader().mKeyStrings; 552 return {{(uint8_t*)&mMappedMemory.data()[block.mOffset], block.mSize}}; 553 } 554 555 StringTable<nsCString> ValueTable() const { 556 auto& block = GetHeader().mValueStrings; 557 return {{(uint8_t*)&mMappedMemory.data()[block.mOffset], block.mSize}}; 558 } 559 560 mozilla::ipc::ReadOnlySharedMemoryHandle mHandle; 561 // This is a leaked shared memory mapping (see the constructor definition for 562 // an explanation). It replaces AutoMemMap::setPersistent behavior as part of 563 // bug 1454816. 564 mozilla::ipc::shared_memory::LeakedReadOnlyMapping mMappedMemory; 565 }; 566 567 // A helper class which builds the contiguous look-up table used by 568 // SharedPrefMap. Each preference in the final map is added to the builder, 569 // before it is finalized and transformed into a read-only snapshot. 570 class MOZ_RAII SharedPrefMapBuilder { 571 public: 572 SharedPrefMapBuilder() = default; 573 574 // The set of flags for the preference, as documented in SharedPrefMap::Entry. 575 struct Flags { 576 uint8_t mHasDefaultValue : 1; 577 uint8_t mHasUserValue : 1; 578 uint8_t mIsSticky : 1; 579 uint8_t mIsLocked : 1; 580 uint8_t mIsSanitized : 1; 581 uint8_t mIsSkippedByIteration : 1; 582 }; 583 584 void Add(const nsCString& aKey, const Flags& aFlags, bool aDefaultValue, 585 bool aUserValue); 586 587 void Add(const nsCString& aKey, const Flags& aFlags, int32_t aDefaultValue, 588 int32_t aUserValue); 589 590 void Add(const nsCString& aKey, const Flags& aFlags, 591 const nsCString& aDefaultValue, const nsCString& aUserValue); 592 593 // Finalizes the binary representation of the map, writes it to a shared 594 // memory region, and then initializes the given ReadOnlySharedMemoryMapping 595 // with a reference to the read-only copy of it. 596 // 597 // This should generally not be used directly by callers. The 598 // SharedPrefMapBuilder instance should instead be passed to the SharedPrefMap 599 // constructor as a move reference. 600 Result<mozilla::ipc::ReadOnlySharedMemoryHandle, nsresult> Finalize(); 601 602 private: 603 using StringTableEntry = mozilla::dom::ipc::StringTableEntry; 604 template <typename T, typename U> 605 using StringTableBuilder = mozilla::dom::ipc::StringTableBuilder<T, U>; 606 607 // An opaque descriptor of the index of a preference entry in a value array, 608 // which can be converted numeric index after the ValueTableBuilder is 609 // finalized. 610 struct ValueIdx { 611 // The relative index of the entry, based on its class. Entries for 612 // preferences with user values appear at the value arrays. Entries with 613 // only default values begin after the last entry with a user value. 614 uint16_t mIndex; 615 bool mHasUserValue; 616 }; 617 618 // A helper class for building default and user value arrays for preferences. 619 // 620 // As described in the SharedPrefMap class, this helper optimizes the way that 621 // it builds its value arrays, in that: 622 // 623 // - It stores value entries for all preferences with user values before 624 // entries for preferences with only default values, and allocates no 625 // entries for preferences with only default values in the user value array. 626 // Since most preferences have only default values, this dramatically 627 // reduces the space required for value storage. 628 // 629 // - For preferences with only default values, it de-duplicates value entries, 630 // and returns the same indices for all preferences with the same value. 631 // 632 // One important complication of this approach is that it means we cannot know 633 // the final index of any entry with only a default value until all entries 634 // have been added to the builder, since it depends on the final number of 635 // user entries in the output. 636 // 637 // To deal with this, when entries are added, we return an opaque ValueIndex 638 // struct, from which we can calculate the final index after the map has been 639 // finalized. 640 template <typename HashKey, typename ValueType_> 641 class ValueTableBuilder { 642 public: 643 using ValueType = ValueType_; 644 645 // Adds an entry for a preference with only a default value to the array, 646 // and returns an opaque descriptor for its index. 647 ValueIdx Add(const ValueType& aDefaultValue) { 648 auto index = uint16_t(mDefaultEntries.Count()); 649 650 return mDefaultEntries.WithEntryHandle(aDefaultValue, [&](auto&& entry) { 651 entry.OrInsertWith([&] { return Entry{index, false, aDefaultValue}; }); 652 653 return ValueIdx{entry->mIndex, false}; 654 }); 655 } 656 657 // Adds an entry for a preference with a user value to the array. Regardless 658 // of whether the preference has a default value, space must be allocated 659 // for it. For preferences with no default value, the actual value which 660 // appears in the array at its value index is ignored. 661 ValueIdx Add(const ValueType& aDefaultValue, const ValueType& aUserValue) { 662 auto index = uint16_t(mUserEntries.Length()); 663 664 mUserEntries.AppendElement(Entry{index, true, aDefaultValue, aUserValue}); 665 666 return {index, true}; 667 } 668 669 // Returns the final index for an entry based on its opaque index 670 // descriptor. This must only be called after the caller has finished adding 671 // entries to the builder. 672 uint16_t GetIndex(const ValueIdx& aIndex) const { 673 uint16_t base = aIndex.mHasUserValue ? 0 : UserCount(); 674 return base + aIndex.mIndex; 675 } 676 677 // Writes out the array of default values at the block beginning at the 678 // given pointer. The block must be at least as large as the value returned 679 // by DefaultSize(). 680 void WriteDefaultValues(const RangedPtr<uint8_t>& aBuffer) const { 681 auto buffer = aBuffer.ReinterpretCast<ValueType>(); 682 683 for (const auto& entry : mUserEntries) { 684 buffer[entry.mIndex] = entry.mDefaultValue; 685 } 686 687 size_t defaultsOffset = UserCount(); 688 for (const auto& data : mDefaultEntries.Values()) { 689 buffer[defaultsOffset + data.mIndex] = data.mDefaultValue; 690 } 691 } 692 693 // Writes out the array of user values at the block beginning at the 694 // given pointer. The block must be at least as large as the value returned 695 // by UserSize(). 696 void WriteUserValues(const RangedPtr<uint8_t>& aBuffer) const { 697 auto buffer = aBuffer.ReinterpretCast<ValueType>(); 698 699 for (const auto& entry : mUserEntries) { 700 buffer[entry.mIndex] = entry.mUserValue; 701 } 702 } 703 704 // These return the number of entries in the default and user value arrays, 705 // respectively. 706 uint32_t DefaultCount() const { 707 return UserCount() + mDefaultEntries.Count(); 708 } 709 uint32_t UserCount() const { return mUserEntries.Length(); } 710 711 // These return the byte sizes of the default and user value arrays, 712 // respectively. 713 uint32_t DefaultSize() const { return DefaultCount() * sizeof(ValueType); } 714 uint32_t UserSize() const { return UserCount() * sizeof(ValueType); } 715 716 void Clear() { 717 mUserEntries.Clear(); 718 mDefaultEntries.Clear(); 719 } 720 721 static constexpr size_t Alignment() { return alignof(ValueType); } 722 723 private: 724 struct Entry { 725 uint16_t mIndex; 726 bool mHasUserValue; 727 ValueType mDefaultValue; 728 ValueType mUserValue{}; 729 }; 730 731 AutoTArray<Entry, 256> mUserEntries; 732 733 nsTHashMap<HashKey, Entry> mDefaultEntries; 734 }; 735 736 // A special-purpose string table builder for keys which are already 737 // guaranteed to be unique. Duplicate values will not be detected or 738 // de-duplicated. 739 template <typename CharType> 740 class UniqueStringTableBuilder { 741 public: 742 using ElemType = CharType; 743 744 explicit UniqueStringTableBuilder(size_t aCapacity) : mEntries(aCapacity) {} 745 746 StringTableEntry Add(const nsTString<CharType>& aKey) { 747 auto entry = mEntries.AppendElement( 748 Entry{mSize, uint32_t(aKey.Length()), aKey.get()}); 749 750 mSize += entry->mLength + 1; 751 752 return {entry->mOffset, entry->mLength}; 753 } 754 755 void Write(const RangedPtr<uint8_t>& aBuffer) { 756 auto buffer = aBuffer.ReinterpretCast<ElemType>(); 757 758 for (auto& entry : mEntries) { 759 memcpy(&buffer[entry.mOffset], entry.mValue, 760 sizeof(ElemType) * (entry.mLength + 1)); 761 } 762 } 763 764 uint32_t Count() const { return mEntries.Length(); } 765 766 uint32_t Size() const { return mSize * sizeof(ElemType); } 767 768 void Clear() { mEntries.Clear(); } 769 770 static constexpr size_t Alignment() { return alignof(ElemType); } 771 772 private: 773 struct Entry { 774 uint32_t mOffset; 775 uint32_t mLength; 776 const CharType* mValue; 777 }; 778 779 nsTArray<Entry> mEntries; 780 uint32_t mSize = 0; 781 }; 782 783 // A preference value entry, roughly corresponding to the 784 // SharedPrefMap::Value struct, but with a temporary place-holder value rather 785 // than a final value index. 786 union Value { 787 Value(bool aDefaultValue, bool aUserValue) 788 : mDefaultBool(aDefaultValue), mUserBool(aUserValue) {} 789 790 MOZ_IMPLICIT Value(const ValueIdx& aIndex) : mIndex(aIndex) {} 791 792 // For Bool preferences, their default and user bool values. 793 struct { 794 bool mDefaultBool; 795 bool mUserBool; 796 }; 797 // For Int and String preferences, an opaque descriptor for their entries in 798 // their value arrays. This must be passed to the appropriate 799 // ValueTableBuilder to obtain the final index when the entry is serialized. 800 ValueIdx mIndex; 801 }; 802 803 // A preference entry, to be converted to a SharedPrefMap::Entry struct during 804 // serialization. 805 struct Entry { 806 // The entry's preference name, as passed to Add(). The caller is 807 // responsible for keeping this pointer alive until the builder is 808 // finalized. 809 const char* mKeyString; 810 // The entry in mKeyTable corresponding to mKeyString. 811 StringTableEntry mKey; 812 Value mValue; 813 814 uint8_t mType : 2; 815 uint8_t mHasDefaultValue : 1; 816 uint8_t mHasUserValue : 1; 817 uint8_t mIsSticky : 1; 818 uint8_t mIsLocked : 1; 819 uint8_t mIsSanitized : 1; 820 uint8_t mIsSkippedByIteration : 1; 821 }; 822 823 // Converts a builder Value struct to a SharedPrefMap::Value struct for 824 // serialization. This must not be called before callers have finished adding 825 // entries to the value array builders. 826 SharedPrefMap::Value GetValue(const Entry& aEntry) const { 827 switch (PrefType(aEntry.mType)) { 828 case PrefType::Bool: 829 return {aEntry.mValue.mDefaultBool, aEntry.mValue.mUserBool}; 830 case PrefType::Int: 831 return {mIntValueTable.GetIndex(aEntry.mValue.mIndex)}; 832 case PrefType::String: 833 return {mStringValueTable.GetIndex(aEntry.mValue.mIndex)}; 834 default: 835 MOZ_ASSERT_UNREACHABLE("Invalid pref type"); 836 return {false, false}; 837 } 838 } 839 840 UniqueStringTableBuilder<char> mKeyTable{kExpectedPrefCount}; 841 StringTableBuilder<nsCStringHashKey, nsCString> mValueStringTable; 842 843 ValueTableBuilder<nsUint32HashKey, uint32_t> mIntValueTable; 844 ValueTableBuilder<nsGenericHashKey<StringTableEntry>, StringTableEntry> 845 mStringValueTable; 846 847 nsTArray<Entry> mEntries{kExpectedPrefCount}; 848 }; 849 850 } // namespace mozilla 851 852 #endif // dom_ipc_SharedPrefMap_h