gecko-dev/modules/libpref/SharedPrefMap.cpp

251 lines
7.5 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SharedPrefMap.h"
#include "mozilla/dom/ipc/MemMapSnapshot.h"
#include "mozilla/BinarySearch.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/Try.h"
#include "mozilla/ipc/FileDescriptor.h"
namespace mozilla {
using namespace ipc;
static inline size_t GetAlignmentOffset(size_t aOffset, size_t aAlign) {
auto mod = aOffset % aAlign;
return mod ? aAlign - mod : 0;
}
SharedPrefMap::SharedPrefMap(const SharedMemoryHandle& aMapHandle,
size_t aMapSize) {
auto map = MakeRefPtr<SharedMemory>();
{
auto result = map->SetHandle(SharedMemory::CloneHandle(aMapHandle),
SharedMemory::OpenRights::RightsReadOnly);
MOZ_RELEASE_ASSERT(result);
}
{
auto result = map->Map(aMapSize);
MOZ_RELEASE_ASSERT(result);
}
// We return literal nsCStrings pointing to the mapped data for preference
// names and string values, which means that we may still have references to
// the mapped data even after this instance is destroyed. That means that we
// need to keep the mapping alive until process shutdown, in order to be safe.
mMappedMemory = map->TakeMapping();
mHandle = map->TakeHandle();
}
SharedPrefMap::SharedPrefMap(SharedPrefMapBuilder&& aBuilder) {
RefPtr<SharedMemory> map;
auto result = aBuilder.Finalize(map);
MOZ_RELEASE_ASSERT(result.isOk() && map);
mMappedMemory = map->TakeMapping();
mHandle = map->TakeHandle();
}
mozilla::ipc::SharedMemoryHandle SharedPrefMap::CloneHandle() const {
return SharedMemory::CloneHandle(mHandle);
}
bool SharedPrefMap::Has(const char* aKey) const {
size_t index;
return Find(aKey, &index);
}
Maybe<const SharedPrefMap::Pref> SharedPrefMap::Get(const char* aKey) const {
Maybe<const Pref> result;
size_t index;
if (Find(aKey, &index)) {
result.emplace(Pref{this, &Entries()[index]});
}
return result;
}
bool SharedPrefMap::Find(const char* aKey, size_t* aIndex) const {
const auto& keys = KeyTable();
return BinarySearchIf(
Entries(), 0, EntryCount(),
[&](const Entry& aEntry) {
return strcmp(aKey, keys.GetBare(aEntry.mKey));
},
aIndex);
}
void SharedPrefMapBuilder::Add(const nsCString& aKey, const Flags& aFlags,
bool aDefaultValue, bool aUserValue) {
mEntries.AppendElement(Entry{
aKey.get(),
mKeyTable.Add(aKey),
{aDefaultValue, aUserValue},
uint8_t(PrefType::Bool),
aFlags.mHasDefaultValue,
aFlags.mHasUserValue,
aFlags.mIsSticky,
aFlags.mIsLocked,
aFlags.mIsSanitized,
aFlags.mIsSkippedByIteration,
});
}
void SharedPrefMapBuilder::Add(const nsCString& aKey, const Flags& aFlags,
int32_t aDefaultValue, int32_t aUserValue) {
ValueIdx index;
if (aFlags.mHasUserValue) {
index = mIntValueTable.Add(aDefaultValue, aUserValue);
} else {
index = mIntValueTable.Add(aDefaultValue);
}
mEntries.AppendElement(Entry{
aKey.get(),
mKeyTable.Add(aKey),
{index},
uint8_t(PrefType::Int),
aFlags.mHasDefaultValue,
aFlags.mHasUserValue,
aFlags.mIsSticky,
aFlags.mIsLocked,
aFlags.mIsSanitized,
aFlags.mIsSkippedByIteration,
});
}
void SharedPrefMapBuilder::Add(const nsCString& aKey, const Flags& aFlags,
const nsCString& aDefaultValue,
const nsCString& aUserValue) {
ValueIdx index;
StringTableEntry defaultVal = mValueStringTable.Add(aDefaultValue);
if (aFlags.mHasUserValue) {
StringTableEntry userVal = mValueStringTable.Add(aUserValue);
index = mStringValueTable.Add(defaultVal, userVal);
} else {
index = mStringValueTable.Add(defaultVal);
}
mEntries.AppendElement(Entry{
aKey.get(),
mKeyTable.Add(aKey),
{index},
uint8_t(PrefType::String),
aFlags.mHasDefaultValue,
aFlags.mHasUserValue,
aFlags.mIsSticky,
aFlags.mIsLocked,
aFlags.mIsSanitized,
aFlags.mIsSkippedByIteration,
});
}
Result<Ok, nsresult> SharedPrefMapBuilder::Finalize(
RefPtr<SharedMemory>& aMap) {
using Header = SharedPrefMap::Header;
// Create an array of entry pointers for the entry array, and sort it by
// preference name prior to serialization, so that entries can be looked up
// using binary search.
nsTArray<Entry*> entries(mEntries.Length());
for (auto& entry : mEntries) {
entries.AppendElement(&entry);
}
entries.Sort([](const Entry* aA, const Entry* aB) {
return strcmp(aA->mKeyString, aB->mKeyString);
});
Header header = {uint32_t(entries.Length())};
size_t offset = sizeof(header);
offset += GetAlignmentOffset(offset, alignof(Header));
offset += entries.Length() * sizeof(SharedPrefMap::Entry);
header.mKeyStrings.mOffset = offset;
header.mKeyStrings.mSize = mKeyTable.Size();
offset += header.mKeyStrings.mSize;
offset += GetAlignmentOffset(offset, mIntValueTable.Alignment());
header.mUserIntValues.mOffset = offset;
header.mUserIntValues.mSize = mIntValueTable.UserSize();
offset += header.mUserIntValues.mSize;
offset += GetAlignmentOffset(offset, mIntValueTable.Alignment());
header.mDefaultIntValues.mOffset = offset;
header.mDefaultIntValues.mSize = mIntValueTable.DefaultSize();
offset += header.mDefaultIntValues.mSize;
offset += GetAlignmentOffset(offset, mStringValueTable.Alignment());
header.mUserStringValues.mOffset = offset;
header.mUserStringValues.mSize = mStringValueTable.UserSize();
offset += header.mUserStringValues.mSize;
offset += GetAlignmentOffset(offset, mStringValueTable.Alignment());
header.mDefaultStringValues.mOffset = offset;
header.mDefaultStringValues.mSize = mStringValueTable.DefaultSize();
offset += header.mDefaultStringValues.mSize;
header.mValueStrings.mOffset = offset;
header.mValueStrings.mSize = mValueStringTable.Size();
offset += header.mValueStrings.mSize;
MemMapSnapshot mem;
MOZ_TRY(mem.Init(offset));
auto headerPtr = mem.Get<Header>();
headerPtr[0] = header;
auto* entryPtr = reinterpret_cast<SharedPrefMap::Entry*>(&headerPtr[1]);
for (auto* entry : entries) {
*entryPtr = {
entry->mKey,
GetValue(*entry),
entry->mType,
entry->mHasDefaultValue,
entry->mHasUserValue,
entry->mIsSticky,
entry->mIsLocked,
entry->mIsSanitized,
entry->mIsSkippedByIteration,
};
entryPtr++;
}
auto ptr = mem.Get<uint8_t>();
mKeyTable.Write({&ptr[header.mKeyStrings.mOffset], header.mKeyStrings.mSize});
mValueStringTable.Write(
{&ptr[header.mValueStrings.mOffset], header.mValueStrings.mSize});
mIntValueTable.WriteDefaultValues(
{&ptr[header.mDefaultIntValues.mOffset], header.mDefaultIntValues.mSize});
mIntValueTable.WriteUserValues(
{&ptr[header.mUserIntValues.mOffset], header.mUserIntValues.mSize});
mStringValueTable.WriteDefaultValues(
{&ptr[header.mDefaultStringValues.mOffset],
header.mDefaultStringValues.mSize});
mStringValueTable.WriteUserValues(
{&ptr[header.mUserStringValues.mOffset], header.mUserStringValues.mSize});
mKeyTable.Clear();
mValueStringTable.Clear();
mIntValueTable.Clear();
mStringValueTable.Clear();
mEntries.Clear();
return mem.Finalize(aMap);
}
} // namespace mozilla