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