Coverage Report

Created: 2018-09-25 14:53

/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