Bug 1874684 - Part 4: Prefer const references instead of copying Instant values....
[gecko.git] / modules / libpref / SharedPrefMap.h
blob7c949e137d808bfa0639a7a12ec2f6866e73afda
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/. */
7 #ifndef dom_ipc_SharedPrefMap_h
8 #define dom_ipc_SharedPrefMap_h
10 #include "mozilla/AutoMemMap.h"
11 #include "mozilla/HashFunctions.h"
12 #include "mozilla/Preferences.h"
13 #include "mozilla/Result.h"
14 #include "mozilla/dom/ipc/StringTable.h"
15 #include "nsTHashMap.h"
17 namespace mozilla {
19 // The approximate number of preferences expected to be in an ordinary
20 // preferences database.
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;
28 class SharedPrefMapBuilder;
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.
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:
41 // nsCString prefName(pref.NameString());
43 // whereas if we returned a nsDependentCString or a dynamically allocated
44 // nsCString, it would.
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.
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 using FileDescriptor = mozilla::ipc::FileDescriptor;
57 friend class SharedPrefMapBuilder;
59 // Describes a block of memory within the shared memory region.
60 struct DataBlock {
61 // The byte offset from the start of the shared memory region to the start
62 // of the block.
63 size_t mOffset;
64 // The size of the block, in bytes. This is typically used only for bounds
65 // checking in debug builds.
66 size_t mSize;
69 // Describes the contents of the shared memory region, which is laid-out as
70 // follows:
72 // - The Header struct
74 // - An array of Entry structs with mEntryCount elements, lexicographically
75 // sorted by preference name.
77 // - A set of data blocks, with offsets and sizes described by the DataBlock
78 // entries in the header, described below.
80 // Each entry stores its name string and values as indices into these blocks,
81 // as documented in the Entry struct, but with some important optimizations:
83 // - Boolean values are always stored inline. Both the default and user
84 // values can be retrieved directly from the entry. Other types have only
85 // one value index, and their values appear at the same indices in the
86 // default and user value arrays.
88 // Aside from reducing our memory footprint, this space-efficiency means
89 // that we can fit more entries in the CPU cache at once, and reduces the
90 // number of likely cache misses during lookups.
92 // - Key strings are stored in a separate string table from value strings. As
93 // above, this makes it more likely that the strings we need will be
94 // available in the CPU cache during lookups by not interleaving them with
95 // extraneous data.
97 // - Default and user values are stored in separate arrays. Entries with user
98 // values always appear before entries with default values in the value
99 // arrays, and entries without user values do not have entries in the user
100 // array at all. Since the same index is used for both arrays, this means
101 // that entries with a default value but no user value do not allocate any
102 // space to store their user value.
104 // - For preferences with no user value, the entries in the default value are
105 // de-duplicated. All preferences with the same default value (and no user
106 // value) point to the same index in the default value array.
109 // For example, a preference database containing:
111 // +---------+-------------------------------+-------------------------------+
112 // | Name | Default Value | User Value | |
113 // +---------+---------------+---------------+-------------------------------+
114 // | string1 | "meh" | "hem" | |
115 // | string2 | | "b" | |
116 // | string3 | "a" | | |
117 // | string4 | "foo" | | |
118 // | string5 | "foo" | | |
119 // | string6 | "meh" | | |
120 // +---------+---------------+---------------+-------------------------------+
121 // | bool1 | false | true | |
122 // | bool2 | | false | |
123 // | bool3 | true | | |
124 // +---------+---------------+---------------+-------------------------------+
125 // | int1 | 18 | 16 | |
126 // | int2 | | 24 | |
127 // | int3 | 42 | | |
128 // | int4 | 12 | | |
129 // | int5 | 12 | | |
130 // | int6 | 18 | | |
131 // +---------+---------------+---------------+-------------------------------+
133 // Results in a database that looks like:
135 // +-------------------------------------------------------------------------+
136 // | Header: |
137 // +-------------------------------------------------------------------------+
138 // | mEntryCount = 15 |
139 // | ... |
140 // +-------------------------------------------------------------------------+
142 // +-------------------------------------------------------------------------+
143 // | Key strings: |
144 // +--------+----------------------------------------------------------------+
145 // | Offset | Value |
146 // +--------+----------------------------------------------------------------+
147 // | 0 | string1\0 |
148 // | 8 | string2\0 |
149 // | 16 | string3\0 |
150 // | 24 | string4\0 |
151 // | 32 | string5\0 |
152 // | 40 | string6\0 |
153 // | 48 | bool1\0 |
154 // | 54 | bool2\0 |
155 // | 60 | bool3\0 |
156 // | 66 | int1\0 |
157 // | 71 | int2\0 |
158 // | 76 | int3\0 |
159 // | 81 | int4\0 |
160 // | 86 | int6\0 |
161 // | 91 | int6\0 |
162 // +--------+----------------------------------------------------------------+
164 // +-------------------------------------------------------------------------+
165 // | Entries: |
166 // +---------------------+------+------------+------------+------------------+
167 // | Key[1] | Type | HasDefault | HasUser | Value |
168 // +---------------------+------+------------+------------+------------------+
169 // | K["bool1", 48, 5] | 3 | true | true | { false, true } |
170 // | K["bool2", 54, 5] | 3 | false | true | { 0, false } |
171 // | K["bool3", 60, 5] | 3 | true | false | { true, 0 } |
172 // | K["int1", 66, 4] | 2 | true | true | 0 |
173 // | K["int2", 71, 4] | 2 | false | true | 1 |
174 // | K["int3", 76, 4] | 2 | true | false | 2 |
175 // | K["int4", 81, 4] | 2 | true | false | 3 |
176 // | K["int5", 86, 4] | 2 | true | false | 3 |
177 // | K["int6", 91, 4] | 2 | true | false | 4 |
178 // | K["string1", 0, 6] | 1 | true | true | 0 |
179 // | K["string2", 8, 6] | 1 | false | true | 1 |
180 // | K["string3", 16, 6] | 1 | true | false | 2 |
181 // | K["string4", 24, 6] | 1 | true | false | 3 |
182 // | K["string5", 32, 6] | 1 | true | false | 3 |
183 // | K["string6", 40, 6] | 1 | true | false | 4 |
184 // +---------------------+------+------------+------------+------------------+
185 // | [1]: Encoded as an offset into the key table and a length. Specified |
186 // | as K[string, offset, length] for clarity. |
187 // +-------------------------------------------------------------------------+
189 // +------------------------------------+------------------------------------+
190 // | User integer values | Default integer values |
191 // +-------+----------------------------+-------+----------------------------+
192 // | Index | Contents | Index | Contents |
193 // +-------+----------------------------+-------+----------------------------+
194 // | 0 | 16 | 0 | 18 |
195 // | 1 | 24 | 1 | |
196 // | | | 2 | 42 |
197 // | | | 3 | 12 |
198 // | | | 4 | 18 |
199 // +-------+----------------------------+-------+----------------------------+
200 // | * Note: Tables are laid out sequentially in memory, but displayed |
201 // | here side-by-side for clarity. |
202 // +-------------------------------------------------------------------------+
204 // +------------------------------------+------------------------------------+
205 // | User string values | Default string values |
206 // +-------+----------------------------+-------+----------------------------+
207 // | Index | Contents[1] | Index | Contents[1] |
208 // +-------+----------------------------+-------+----------------------------+
209 // | 0 | V["hem", 0, 3] | 0 | V["meh", 4, 3] |
210 // | 1 | V["b", 8, 1] | 1 | |
211 // | | | 2 | V["a", 10, 1] |
212 // | | | 3 | V["foo", 12, 3] |
213 // | | | 4 | V["meh", 4, 3] |
214 // |-------+----------------------------+-------+----------------------------+
215 // | [1]: Encoded as an offset into the value table and a length. Specified |
216 // | as V[string, offset, length] for clarity. |
217 // +-------------------------------------------------------------------------+
218 // | * Note: Tables are laid out sequentially in memory, but displayed |
219 // | here side-by-side for clarity. |
220 // +-------------------------------------------------------------------------+
222 // +-------------------------------------------------------------------------+
223 // | Value strings: |
224 // +--------+----------------------------------------------------------------+
225 // | Offset | Value |
226 // +--------+----------------------------------------------------------------+
227 // | 0 | hem\0 |
228 // | 4 | meh\0 |
229 // | 8 | b\0 |
230 // | 10 | a\0 |
231 // | 12 | foo\0 |
232 // +--------+----------------------------------------------------------------+
233 struct Header {
234 // The number of entries in this map.
235 uint32_t mEntryCount;
237 // The StringTable data block for preference name strings, which act as keys
238 // in the map.
239 DataBlock mKeyStrings;
241 // The int32_t arrays of user and default int preference values. Entries in
242 // the map store their values as indices into these arrays.
243 DataBlock mUserIntValues;
244 DataBlock mDefaultIntValues;
246 // The StringTableEntry arrays of user and default string preference values.
248 // Strings are stored as StringTableEntry structs with character offsets
249 // into the mValueStrings string table and their corresponding lengths.
251 // Entries in the map, likewise, store their string values as indices into
252 // these arrays.
253 DataBlock mUserStringValues;
254 DataBlock mDefaultStringValues;
256 // The StringTable data block for string preference values, referenced by
257 // the above two data blocks.
258 DataBlock mValueStrings;
261 using StringTableEntry = mozilla::dom::ipc::StringTableEntry;
263 // Represents a preference value, as either a pair of boolean values, or an
264 // index into one of the above value arrays.
265 union Value {
266 Value(bool aDefaultValue, bool aUserValue)
267 : mDefaultBool(aDefaultValue), mUserBool(aUserValue) {}
269 MOZ_IMPLICIT Value(uint16_t aIndex) : mIndex(aIndex) {}
271 // The index of this entry in the value arrays.
273 // User and default preference values have the same indices in their
274 // respective arrays. However, entries without a user value are not
275 // guaranteed to have space allocated for them in the user value array, and
276 // likewise for preferences without default values in the default value
277 // array. This means that callers must only access value entries for entries
278 // which claim to have a value of that type.
279 uint16_t mIndex;
280 struct {
281 bool mDefaultBool;
282 bool mUserBool;
286 // Represents a preference entry in the map, containing its name, type info,
287 // flags, and a reference to its value.
288 struct Entry {
289 // A pointer to the preference name in the KeyTable string table.
290 StringTableEntry mKey;
292 // The preference's value, either as a pair of booleans, or an index into
293 // the value arrays. Please see the documentation for the Value struct
294 // above.
295 Value mValue;
297 // The preference's type, as a PrefType enum value. This must *never* be
298 // PrefType::None for values in a shared array.
299 uint8_t mType : 2;
300 // True if the preference has a default value. Callers must not attempt to
301 // access the entry's default value if this is false.
302 uint8_t mHasDefaultValue : 1;
303 // True if the preference has a user value. Callers must not attempt to
304 // access the entry's user value if this is false.
305 uint8_t mHasUserValue : 1;
306 // True if the preference is sticky, as defined by the preference service.
307 uint8_t mIsSticky : 1;
308 // True if the preference is locked, as defined by the preference service.
309 uint8_t mIsLocked : 1;
310 // True if the preference is sanitized, as defined by the preference
311 // service.
312 uint8_t mIsSanitized : 1;
313 // True if the preference should be skipped while iterating over the
314 // SharedPrefMap. This is used to internally store Once StaticPrefs.
315 // This property is not visible to users the way sticky and locked are.
316 uint8_t mIsSkippedByIteration : 1;
319 public:
320 NS_INLINE_DECL_REFCOUNTING(SharedPrefMap)
322 // A temporary wrapper class for accessing entries in the array. Instances of
323 // this class are valid as long as SharedPrefMap instance is alive, but
324 // generally should not be stored long term, or allocated on the heap.
326 // The class is implemented as two pointers, one to the SharedPrefMap
327 // instance, and one to the Entry that corresponds to the preference, and is
328 // meant to be cheaply returned by value from preference lookups and
329 // iterators. All property accessors lazily fetch the appropriate values from
330 // the shared memory region.
331 class MOZ_STACK_CLASS Pref final {
332 public:
333 const char* Name() const { return mMap->KeyTable().GetBare(mEntry->mKey); }
335 nsCString NameString() const { return mMap->KeyTable().Get(mEntry->mKey); }
337 PrefType Type() const {
338 MOZ_ASSERT(PrefType(mEntry->mType) != PrefType::None);
339 return PrefType(mEntry->mType);
342 bool HasDefaultValue() const { return mEntry->mHasDefaultValue; }
343 bool HasUserValue() const { return mEntry->mHasUserValue; }
344 bool IsLocked() const { return mEntry->mIsLocked; }
345 bool IsSanitized() const { return mEntry->mIsSanitized; }
346 bool IsSticky() const { return mEntry->mIsSticky; }
347 bool IsSkippedByIteration() const { return mEntry->mIsSkippedByIteration; }
349 bool GetBoolValue(PrefValueKind aKind = PrefValueKind::User) const {
350 MOZ_ASSERT(Type() == PrefType::Bool);
351 MOZ_ASSERT(aKind == PrefValueKind::Default ? HasDefaultValue()
352 : HasUserValue());
354 return aKind == PrefValueKind::Default ? mEntry->mValue.mDefaultBool
355 : mEntry->mValue.mUserBool;
358 int32_t GetIntValue(PrefValueKind aKind = PrefValueKind::User) const {
359 MOZ_ASSERT(Type() == PrefType::Int);
360 MOZ_ASSERT(aKind == PrefValueKind::Default ? HasDefaultValue()
361 : HasUserValue());
363 return aKind == PrefValueKind::Default
364 ? mMap->DefaultIntValues()[mEntry->mValue.mIndex]
365 : mMap->UserIntValues()[mEntry->mValue.mIndex];
368 private:
369 const StringTableEntry& GetStringEntry(PrefValueKind aKind) const {
370 MOZ_ASSERT(Type() == PrefType::String);
371 MOZ_ASSERT(aKind == PrefValueKind::Default ? HasDefaultValue()
372 : HasUserValue());
374 return aKind == PrefValueKind::Default
375 ? mMap->DefaultStringValues()[mEntry->mValue.mIndex]
376 : mMap->UserStringValues()[mEntry->mValue.mIndex];
379 public:
380 nsCString GetStringValue(PrefValueKind aKind = PrefValueKind::User) const {
381 return mMap->ValueTable().Get(GetStringEntry(aKind));
384 const char* GetBareStringValue(
385 PrefValueKind aKind = PrefValueKind::User) const {
386 return mMap->ValueTable().GetBare(GetStringEntry(aKind));
389 // Returns the entry's index in the map, as understood by GetKeyAt() and
390 // GetValueAt().
391 size_t Index() const { return mEntry - mMap->Entries().get(); }
393 bool operator==(const Pref& aPref) const { return mEntry == aPref.mEntry; }
394 bool operator!=(const Pref& aPref) const { return !(*this == aPref); }
396 // This is odd, but necessary in order for the C++ range iterator protocol
397 // to work here.
398 Pref& operator*() { return *this; }
400 // Updates this wrapper to point to the next visible entry in the map. This
401 // should not be attempted unless Index() is less than the map's Count().
402 Pref& operator++() {
403 do {
404 mEntry++;
405 } while (mEntry->mIsSkippedByIteration && Index() < mMap->Count());
406 return *this;
409 Pref(const Pref& aPref) = default;
411 protected:
412 friend class SharedPrefMap;
414 Pref(const SharedPrefMap* aPrefMap, const Entry* aEntry)
415 : mMap(aPrefMap), mEntry(aEntry) {}
417 private:
418 const SharedPrefMap* const mMap;
419 const Entry* mEntry;
422 // Note: These constructors are infallible, because the preference database is
423 // critical to platform functionality, and we cannot operate without it.
424 SharedPrefMap(const FileDescriptor&, size_t);
425 explicit SharedPrefMap(SharedPrefMapBuilder&&);
427 // Searches for the given preference in the map, and returns true if it
428 // exists.
429 bool Has(const char* aKey) const;
431 bool Has(const nsCString& aKey) const { return Has(aKey.get()); }
433 // Searches for the given preference in the map, and if it exists, returns
434 // a Some<Pref> containing its details.
435 Maybe<const Pref> Get(const char* aKey) const;
437 Maybe<const Pref> Get(const nsCString& aKey) const { return Get(aKey.get()); }
439 private:
440 // Searches for an entry for the given key. If found, returns true, and
441 // places its index in the entry array in aIndex.
442 bool Find(const char* aKey, size_t* aIndex) const;
444 public:
445 // Returns the number of entries in the map.
446 uint32_t Count() const { return EntryCount(); }
448 // Returns the string entry at the given index. Keys are guaranteed to be
449 // sorted lexicographically.
451 // The given index *must* be less than the value returned by Count().
453 // The returned value is a literal string which references the mapped memory
454 // region.
455 nsCString GetKeyAt(uint32_t aIndex) const {
456 MOZ_ASSERT(aIndex < Count());
457 return KeyTable().Get(Entries()[aIndex].mKey);
460 // Returns the value for the entry at the given index.
462 // The given index *must* be less than the value returned by Count().
464 // The returned value is valid for the lifetime of this map instance.
465 const Pref GetValueAt(uint32_t aIndex) const {
466 MOZ_ASSERT(aIndex < Count());
467 return UncheckedGetValueAt(aIndex);
470 private:
471 // Returns a wrapper with a pointer to an entry without checking its bounds.
472 // This should only be used by range iterators, to check their end positions.
474 // Note: In debug builds, the RangePtr returned by entries will still assert
475 // that aIndex is no more than 1 past the last element in the array, since it
476 // also takes into account the ranged iteration use case.
477 Pref UncheckedGetValueAt(uint32_t aIndex) const {
478 return {this, (Entries() + aIndex).get()};
481 public:
482 // C++ range iterator protocol. begin() and end() return references to the
483 // first (non-skippable) and last entries in the array. The begin wrapper
484 // can be incremented until it matches the last element in the array, at which
485 // point it becomes invalid and the iteration is over.
486 Pref begin() const {
487 for (uint32_t aIndex = 0; aIndex < Count(); aIndex++) {
488 Pref pref = UncheckedGetValueAt(aIndex);
489 if (!pref.IsSkippedByIteration()) {
490 return pref;
493 return end();
495 Pref end() const { return UncheckedGetValueAt(Count()); }
497 // A cosmetic helper for range iteration. Returns a reference value from a
498 // pointer to this instance so that its .begin() and .end() methods can be
499 // accessed in a ranged for loop. `map->Iter()` is equivalent to `*map`, but
500 // makes its purpose slightly clearer.
501 const SharedPrefMap& Iter() const { return *this; }
503 // Returns a copy of the read-only file descriptor which backs the shared
504 // memory region for this map. The file descriptor may be passed between
505 // processes, and used to construct new instances of SharedPrefMap with
506 // the same data as this instance.
507 FileDescriptor CloneFileDescriptor() const;
509 // Returns the size of the mapped memory region. This size must be passed to
510 // the constructor when mapping the shared region in another process.
511 size_t MapSize() const { return mMap.size(); }
513 protected:
514 ~SharedPrefMap() = default;
516 private:
517 template <typename T>
518 using StringTable = mozilla::dom::ipc::StringTable<T>;
520 // Type-safe getters for values in the shared memory region:
521 const Header& GetHeader() const { return mMap.get<Header>()[0]; }
523 RangedPtr<const Entry> Entries() const {
524 return {reinterpret_cast<const Entry*>(&GetHeader() + 1), EntryCount()};
527 uint32_t EntryCount() const { return GetHeader().mEntryCount; }
529 template <typename T>
530 RangedPtr<const T> GetBlock(const DataBlock& aBlock) const {
531 return RangedPtr<uint8_t>(&mMap.get<uint8_t>()[aBlock.mOffset],
532 aBlock.mSize)
533 .ReinterpretCast<const T>();
536 RangedPtr<const int32_t> DefaultIntValues() const {
537 return GetBlock<int32_t>(GetHeader().mDefaultIntValues);
539 RangedPtr<const int32_t> UserIntValues() const {
540 return GetBlock<int32_t>(GetHeader().mUserIntValues);
543 RangedPtr<const StringTableEntry> DefaultStringValues() const {
544 return GetBlock<StringTableEntry>(GetHeader().mDefaultStringValues);
546 RangedPtr<const StringTableEntry> UserStringValues() const {
547 return GetBlock<StringTableEntry>(GetHeader().mUserStringValues);
550 StringTable<nsCString> KeyTable() const {
551 auto& block = GetHeader().mKeyStrings;
552 return {{&mMap.get<uint8_t>()[block.mOffset], block.mSize}};
555 StringTable<nsCString> ValueTable() const {
556 auto& block = GetHeader().mValueStrings;
557 return {{&mMap.get<uint8_t>()[block.mOffset], block.mSize}};
560 loader::AutoMemMap mMap;
563 // A helper class which builds the contiguous look-up table used by
564 // SharedPrefMap. Each preference in the final map is added to the builder,
565 // before it is finalized and transformed into a read-only snapshot.
566 class MOZ_RAII SharedPrefMapBuilder {
567 public:
568 SharedPrefMapBuilder() = default;
570 // The set of flags for the preference, as documented in SharedPrefMap::Entry.
571 struct Flags {
572 uint8_t mHasDefaultValue : 1;
573 uint8_t mHasUserValue : 1;
574 uint8_t mIsSticky : 1;
575 uint8_t mIsLocked : 1;
576 uint8_t mIsSanitized : 1;
577 uint8_t mIsSkippedByIteration : 1;
580 void Add(const nsCString& aKey, const Flags& aFlags, bool aDefaultValue,
581 bool aUserValue);
583 void Add(const nsCString& aKey, const Flags& aFlags, int32_t aDefaultValue,
584 int32_t aUserValue);
586 void Add(const nsCString& aKey, const Flags& aFlags,
587 const nsCString& aDefaultValue, const nsCString& aUserValue);
589 // Finalizes the binary representation of the map, writes it to a shared
590 // memory region, and then initializes the given AutoMemMap with a reference
591 // to the read-only copy of it.
593 // This should generally not be used directly by callers. The
594 // SharedPrefMapBuilder instance should instead be passed to the SharedPrefMap
595 // constructor as a move reference.
596 Result<Ok, nsresult> Finalize(loader::AutoMemMap& aMap);
598 private:
599 using StringTableEntry = mozilla::dom::ipc::StringTableEntry;
600 template <typename T, typename U>
601 using StringTableBuilder = mozilla::dom::ipc::StringTableBuilder<T, U>;
603 // An opaque descriptor of the index of a preference entry in a value array,
604 // which can be converted numeric index after the ValueTableBuilder is
605 // finalized.
606 struct ValueIdx {
607 // The relative index of the entry, based on its class. Entries for
608 // preferences with user values appear at the value arrays. Entries with
609 // only default values begin after the last entry with a user value.
610 uint16_t mIndex;
611 bool mHasUserValue;
614 // A helper class for building default and user value arrays for preferences.
616 // As described in the SharedPrefMap class, this helper optimizes the way that
617 // it builds its value arrays, in that:
619 // - It stores value entries for all preferences with user values before
620 // entries for preferences with only default values, and allocates no
621 // entries for preferences with only default values in the user value array.
622 // Since most preferences have only default values, this dramatically
623 // reduces the space required for value storage.
625 // - For preferences with only default values, it de-duplicates value entries,
626 // and returns the same indices for all preferences with the same value.
628 // One important complication of this approach is that it means we cannot know
629 // the final index of any entry with only a default value until all entries
630 // have been added to the builder, since it depends on the final number of
631 // user entries in the output.
633 // To deal with this, when entries are added, we return an opaque ValueIndex
634 // struct, from which we can calculate the final index after the map has been
635 // finalized.
636 template <typename HashKey, typename ValueType_>
637 class ValueTableBuilder {
638 public:
639 using ValueType = ValueType_;
641 // Adds an entry for a preference with only a default value to the array,
642 // and returns an opaque descriptor for its index.
643 ValueIdx Add(const ValueType& aDefaultValue) {
644 auto index = uint16_t(mDefaultEntries.Count());
646 return mDefaultEntries.WithEntryHandle(aDefaultValue, [&](auto&& entry) {
647 entry.OrInsertWith([&] { return Entry{index, false, aDefaultValue}; });
649 return ValueIdx{entry->mIndex, false};
653 // Adds an entry for a preference with a user value to the array. Regardless
654 // of whether the preference has a default value, space must be allocated
655 // for it. For preferences with no default value, the actual value which
656 // appears in the array at its value index is ignored.
657 ValueIdx Add(const ValueType& aDefaultValue, const ValueType& aUserValue) {
658 auto index = uint16_t(mUserEntries.Length());
660 mUserEntries.AppendElement(Entry{index, true, aDefaultValue, aUserValue});
662 return {index, true};
665 // Returns the final index for an entry based on its opaque index
666 // descriptor. This must only be called after the caller has finished adding
667 // entries to the builder.
668 uint16_t GetIndex(const ValueIdx& aIndex) const {
669 uint16_t base = aIndex.mHasUserValue ? 0 : UserCount();
670 return base + aIndex.mIndex;
673 // Writes out the array of default values at the block beginning at the
674 // given pointer. The block must be at least as large as the value returned
675 // by DefaultSize().
676 void WriteDefaultValues(const RangedPtr<uint8_t>& aBuffer) const {
677 auto buffer = aBuffer.ReinterpretCast<ValueType>();
679 for (const auto& entry : mUserEntries) {
680 buffer[entry.mIndex] = entry.mDefaultValue;
683 size_t defaultsOffset = UserCount();
684 for (const auto& data : mDefaultEntries.Values()) {
685 buffer[defaultsOffset + data.mIndex] = data.mDefaultValue;
689 // Writes out the array of user values at the block beginning at the
690 // given pointer. The block must be at least as large as the value returned
691 // by UserSize().
692 void WriteUserValues(const RangedPtr<uint8_t>& aBuffer) const {
693 auto buffer = aBuffer.ReinterpretCast<ValueType>();
695 for (const auto& entry : mUserEntries) {
696 buffer[entry.mIndex] = entry.mUserValue;
700 // These return the number of entries in the default and user value arrays,
701 // respectively.
702 uint32_t DefaultCount() const {
703 return UserCount() + mDefaultEntries.Count();
705 uint32_t UserCount() const { return mUserEntries.Length(); }
707 // These return the byte sizes of the default and user value arrays,
708 // respectively.
709 uint32_t DefaultSize() const { return DefaultCount() * sizeof(ValueType); }
710 uint32_t UserSize() const { return UserCount() * sizeof(ValueType); }
712 void Clear() {
713 mUserEntries.Clear();
714 mDefaultEntries.Clear();
717 static constexpr size_t Alignment() { return alignof(ValueType); }
719 private:
720 struct Entry {
721 uint16_t mIndex;
722 bool mHasUserValue;
723 ValueType mDefaultValue;
724 ValueType mUserValue{};
727 AutoTArray<Entry, 256> mUserEntries;
729 nsTHashMap<HashKey, Entry> mDefaultEntries;
732 // A special-purpose string table builder for keys which are already
733 // guaranteed to be unique. Duplicate values will not be detected or
734 // de-duplicated.
735 template <typename CharType>
736 class UniqueStringTableBuilder {
737 public:
738 using ElemType = CharType;
740 explicit UniqueStringTableBuilder(size_t aCapacity) : mEntries(aCapacity) {}
742 StringTableEntry Add(const nsTString<CharType>& aKey) {
743 auto entry = mEntries.AppendElement(
744 Entry{mSize, uint32_t(aKey.Length()), aKey.get()});
746 mSize += entry->mLength + 1;
748 return {entry->mOffset, entry->mLength};
751 void Write(const RangedPtr<uint8_t>& aBuffer) {
752 auto buffer = aBuffer.ReinterpretCast<ElemType>();
754 for (auto& entry : mEntries) {
755 memcpy(&buffer[entry.mOffset], entry.mValue,
756 sizeof(ElemType) * (entry.mLength + 1));
760 uint32_t Count() const { return mEntries.Length(); }
762 uint32_t Size() const { return mSize * sizeof(ElemType); }
764 void Clear() { mEntries.Clear(); }
766 static constexpr size_t Alignment() { return alignof(ElemType); }
768 private:
769 struct Entry {
770 uint32_t mOffset;
771 uint32_t mLength;
772 const CharType* mValue;
775 nsTArray<Entry> mEntries;
776 uint32_t mSize = 0;
779 // A preference value entry, roughly corresponding to the
780 // SharedPrefMap::Value struct, but with a temporary place-holder value rather
781 // than a final value index.
782 union Value {
783 Value(bool aDefaultValue, bool aUserValue)
784 : mDefaultBool(aDefaultValue), mUserBool(aUserValue) {}
786 MOZ_IMPLICIT Value(const ValueIdx& aIndex) : mIndex(aIndex) {}
788 // For Bool preferences, their default and user bool values.
789 struct {
790 bool mDefaultBool;
791 bool mUserBool;
793 // For Int and String preferences, an opaque descriptor for their entries in
794 // their value arrays. This must be passed to the appropriate
795 // ValueTableBuilder to obtain the final index when the entry is serialized.
796 ValueIdx mIndex;
799 // A preference entry, to be converted to a SharedPrefMap::Entry struct during
800 // serialization.
801 struct Entry {
802 // The entry's preference name, as passed to Add(). The caller is
803 // responsible for keeping this pointer alive until the builder is
804 // finalized.
805 const char* mKeyString;
806 // The entry in mKeyTable corresponding to mKeyString.
807 StringTableEntry mKey;
808 Value mValue;
810 uint8_t mType : 2;
811 uint8_t mHasDefaultValue : 1;
812 uint8_t mHasUserValue : 1;
813 uint8_t mIsSticky : 1;
814 uint8_t mIsLocked : 1;
815 uint8_t mIsSanitized : 1;
816 uint8_t mIsSkippedByIteration : 1;
819 // Converts a builder Value struct to a SharedPrefMap::Value struct for
820 // serialization. This must not be called before callers have finished adding
821 // entries to the value array builders.
822 SharedPrefMap::Value GetValue(const Entry& aEntry) const {
823 switch (PrefType(aEntry.mType)) {
824 case PrefType::Bool:
825 return {aEntry.mValue.mDefaultBool, aEntry.mValue.mUserBool};
826 case PrefType::Int:
827 return {mIntValueTable.GetIndex(aEntry.mValue.mIndex)};
828 case PrefType::String:
829 return {mStringValueTable.GetIndex(aEntry.mValue.mIndex)};
830 default:
831 MOZ_ASSERT_UNREACHABLE("Invalid pref type");
832 return {false, false};
836 UniqueStringTableBuilder<char> mKeyTable{kExpectedPrefCount};
837 StringTableBuilder<nsCStringHashKey, nsCString> mValueStringTable;
839 ValueTableBuilder<nsUint32HashKey, uint32_t> mIntValueTable;
840 ValueTableBuilder<nsGenericHashKey<StringTableEntry>, StringTableEntry>
841 mStringValueTable;
843 nsTArray<Entry> mEntries{kExpectedPrefCount};
846 } // namespace mozilla
848 #endif // dom_ipc_SharedPrefMap_h