Bug 1418156 - Merge PrefTypeFlags into PrefHashEntry. r=glandium draft
authorNicholas Nethercote <nnethercote@mozilla.com>
Fri, 17 Nov 2017 12:15:51 +1100
changeset 699338 58e954057ce93e3a71a8ea7d72e9a59a6346c6f2
parent 699337 ad20b2a431756209f996d9300fc0567cc79ee359
child 699339 2fa444bacf2bcd7c941751ad0e9bb4a0f09bdd83
push id89536
push usernnethercote@mozilla.com
push dateFri, 17 Nov 2017 01:25:26 +0000
reviewersglandium
bugs1418156
milestone59.0a1
Bug 1418156 - Merge PrefTypeFlags into PrefHashEntry. r=glandium PrefTypeFlags is a class with a lot of smarts (i.e. methods). PrefHashEntry is a class with little smarts. This is silly, because PrefTypeFlags is essentially an internal implementation detail of PrefHashEntry. This patch merges PrefTypeFlags into PrefHashEntry, so that PrefHashEntry has all the smarts. This means lots of `pref->mPrefFlags.Foo()` calls become `pref->Foo()`. The patch also changes the representation of the type and flags within PrefHashEntry to use bitfields, which avoids the need for a Flags type and is much simpler than the old approach. MozReview-Commit-ID: 4Yt9OtBzh9e
modules/libpref/Preferences.cpp
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -155,120 +155,81 @@ PrefTypeToString(PrefType aType)
     case PrefType::Bool:
       return "bool";
     default:
       MOZ_CRASH("Unhandled enum value");
   }
 }
 #endif
 
-// Keep the type of the preference, as well as the flags guiding its behaviour.
-class PrefTypeFlags
+class PrefHashEntry : public PLDHashEntryHdr
 {
 public:
-  PrefTypeFlags()
-    : mValue(AsInt(PrefType::Invalid))
-  {
-  }
-
-  explicit PrefTypeFlags(PrefType aType)
-    : mValue(AsInt(aType))
-  {
-  }
-
-  bool IsTypeValid() const { return !IsPrefType(PrefType::Invalid); }
-  bool IsTypeString() const { return IsPrefType(PrefType::String); }
-  bool IsTypeInt() const { return IsPrefType(PrefType::Int); }
-  bool IsTypeBool() const { return IsPrefType(PrefType::Bool); }
-  bool IsPrefType(PrefType type) const { return GetPrefType() == type; }
-
-  void SetPrefType(PrefType aType)
-  {
-    mValue = mValue - AsInt(GetPrefType()) + AsInt(aType);
-  }
-
-  PrefType GetPrefType() const
-  {
-    return (PrefType)(mValue & (AsInt(PrefType::String) | AsInt(PrefType::Int) |
-                                AsInt(PrefType::Bool)));
-  }
-
-  bool HasDefaultValue() const { return mValue & PREF_FLAG_HAS_DEFAULT_VALUE; }
-
-  void SetHasDefaultValue(bool aSetOrUnset)
-  {
-    SetFlag(PREF_FLAG_HAS_DEFAULT_VALUE, aSetOrUnset);
-  }
-
-  bool IsSticky() const { return mValue & PREF_FLAG_STICKY; }
-
-  void SetIsSticky(bool aSetOrUnset)
-  {
-    SetFlag(PREF_FLAG_STICKY, aSetOrUnset);
-  }
-
-  bool IsLocked() const { return mValue & PREF_FLAG_LOCKED; }
-
-  void SetLocked(bool aSetOrUnset) { SetFlag(PREF_FLAG_LOCKED, aSetOrUnset); }
-
-  bool HasUserValue() const { return mValue & PREF_FLAG_HAS_USER_VALUE; }
-
-  void SetHasUserValue(bool aSetOrUnset)
-  {
-    SetFlag(PREF_FLAG_HAS_USER_VALUE, aSetOrUnset);
-  }
-
-private:
-  static uint16_t AsInt(PrefType aType) { return (uint16_t)aType; }
-
-  void SetFlag(uint16_t aFlag, bool aSetOrUnset)
-  {
-    mValue = aSetOrUnset ? mValue | aFlag : mValue & ~aFlag;
-  }
-
-  // We pack both the value of type (PrefType) and flags into the same int. The
-  // flag enum starts at 4 so that the PrefType can occupy the bottom two bits.
-  enum
-  {
-    PREF_FLAG_LOCKED = 4,
-    PREF_FLAG_HAS_USER_VALUE = 8,
-    PREF_FLAG_HAS_DEFAULT_VALUE = 16,
-    PREF_FLAG_STICKY = 32,
-  };
-  uint16_t mValue;
-};
-
-struct PrefHashEntry : PLDHashEntryHdr
-{
-  PrefTypeFlags mPrefFlags; // this field first to minimize 64-bit struct size
-  const char* mKey;
-  PrefValue mDefaultValue;
-  PrefValue mUserValue;
+  // Types.
+
+  PrefType Type() const { return static_cast<PrefType>(mType); }
+  void SetType(PrefType aType) { mType = static_cast<uint32_t>(aType); }
+
+  bool IsType(PrefType aType) const { return Type() == aType; }
+  bool IsTypeString() const { return IsType(PrefType::String); }
+  bool IsTypeInt() const { return IsType(PrefType::Int); }
+  bool IsTypeBool() const { return IsType(PrefType::Bool); }
+
+  // Other properties.
+
+  bool IsSticky() const { return mIsSticky; }
+  void SetIsSticky(bool aValue) { mIsSticky = aValue; }
+
+  bool IsLocked() const { return mIsLocked; }
+  void SetLocked(bool aValue) { mIsLocked = aValue; }
+
+  bool HasDefaultValue() const { return mHasDefaultValue; }
+  void SetHasDefaultValue(bool aValue) { mHasDefaultValue = aValue; }
+
+  bool HasUserValue() const { return mHasUserValue; }
+  void SetHasUserValue(bool aValue) { mHasUserValue = aValue; }
+
+  // Other operations.
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
   {
     // Note: mKey is allocated in gPrefNameArena, measured elsewhere.
     size_t n = 0;
-    if (mPrefFlags.IsTypeString()) {
-      if (mPrefFlags.HasDefaultValue()) {
+    if (IsTypeString()) {
+      if (HasDefaultValue()) {
         n += aMallocSizeOf(mDefaultValue.mStringVal);
       }
-      if (mPrefFlags.HasUserValue()) {
+      if (HasUserValue()) {
         n += aMallocSizeOf(mUserValue.mStringVal);
       }
     }
     return n;
   }
+
+private:
+  // These fields go first to minimize this struct's size on 64-bit. (Because
+  // this a subclass of PLDHashEntryHdr, there's a preceding 32-bit mKeyHash
+  // field.)
+  uint32_t mType : 2;
+  uint32_t mIsSticky : 1;
+  uint32_t mIsLocked : 1;
+  uint32_t mHasUserValue : 1;
+  uint32_t mHasDefaultValue : 1;
+
+public:
+  const char* mKey;
+  PrefValue mDefaultValue;
+  PrefValue mUserValue;
 };
 
 static void
 ClearPrefEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
 {
   auto pref = static_cast<PrefHashEntry*>(aEntry);
-  if (pref->mPrefFlags.IsTypeString()) {
+  if (pref->IsTypeString()) {
     free(const_cast<char*>(pref->mDefaultValue.mStringVal));
     free(const_cast<char*>(pref->mUserValue.mStringVal));
   }
 
   // Don't need to free this because it's allocated in memory owned by
   // gPrefNameArena.
   pref->mKey = nullptr;
   memset(aEntry, 0, aTable->EntrySize());
@@ -410,65 +371,63 @@ pref_savePrefs()
   PrefSaveData savedPrefs(gHashTable->EntryCount());
 
   for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
     auto pref = static_cast<PrefHashEntry*>(iter.Get());
 
     // where we're getting our pref from
     PrefValue* sourceValue;
 
-    if (pref->mPrefFlags.HasUserValue() &&
-        (pref_ValueChanged(pref->mDefaultValue,
-                           pref->mUserValue,
-                           pref->mPrefFlags.GetPrefType()) ||
-         !pref->mPrefFlags.HasDefaultValue() ||
-         pref->mPrefFlags.IsSticky())) {
+    if (pref->HasUserValue() &&
+        (pref_ValueChanged(
+           pref->mDefaultValue, pref->mUserValue, pref->Type()) ||
+         !pref->HasDefaultValue() || pref->IsSticky())) {
       sourceValue = &pref->mUserValue;
     } else {
       // do not save default prefs that haven't changed
       continue;
     }
 
     nsAutoCString prefName;
     StrEscape(pref->mKey, prefName);
 
     nsAutoCString prefValue;
-    if (pref->mPrefFlags.IsTypeString()) {
+    if (pref->IsTypeString()) {
       StrEscape(sourceValue->mStringVal, prefValue);
 
-    } else if (pref->mPrefFlags.IsTypeInt()) {
+    } else if (pref->IsTypeInt()) {
       prefValue.AppendInt(sourceValue->mIntVal);
 
-    } else if (pref->mPrefFlags.IsTypeBool()) {
+    } else if (pref->IsTypeBool()) {
       prefValue = sourceValue->mBoolVal ? "true" : "false";
     }
 
     nsPrintfCString str("user_pref(%s, %s);", prefName.get(), prefValue.get());
     savedPrefs.AppendElement(str);
   }
 
   return savedPrefs;
 }
 
 static bool
 pref_EntryHasAdvisablySizedValues(PrefHashEntry* aPref)
 {
-  if (aPref->mPrefFlags.GetPrefType() != PrefType::String) {
+  if (!aPref->IsTypeString()) {
     return true;
   }
 
   const char* stringVal;
-  if (aPref->mPrefFlags.HasDefaultValue()) {
+  if (aPref->HasDefaultValue()) {
     stringVal = aPref->mDefaultValue.mStringVal;
     if (strlen(stringVal) > MAX_ADVISABLE_PREF_LENGTH) {
       return false;
     }
   }
 
-  if (aPref->mPrefFlags.HasUserValue()) {
+  if (aPref->HasUserValue()) {
     stringVal = aPref->mUserValue.mStringVal;
     if (strlen(stringVal) > MAX_ADVISABLE_PREF_LENGTH) {
       return false;
     }
   }
 
   return true;
 }
@@ -485,17 +444,17 @@ GetPrefValueFromEntry(PrefHashEntry* aPr
     aSetting->userValue() = dom::PrefValue();
     settingValue = &aSetting->userValue().get_PrefValue();
   } else {
     value = &aPref->mDefaultValue;
     aSetting->defaultValue() = dom::PrefValue();
     settingValue = &aSetting->defaultValue().get_PrefValue();
   }
 
-  switch (aPref->mPrefFlags.GetPrefType()) {
+  switch (aPref->Type()) {
     case PrefType::String:
       *settingValue = nsDependentCString(value->mStringVal);
       return;
     case PrefType::Int:
       *settingValue = value->mIntVal;
       return;
     case PrefType::Bool:
       *settingValue = !!value->mBoolVal;
@@ -505,23 +464,23 @@ GetPrefValueFromEntry(PrefHashEntry* aPr
   }
 }
 
 static void
 pref_GetPrefFromEntry(PrefHashEntry* aPref, dom::PrefSetting* aSetting)
 {
   aSetting->name() = aPref->mKey;
 
-  if (aPref->mPrefFlags.HasDefaultValue()) {
+  if (aPref->HasDefaultValue()) {
     GetPrefValueFromEntry(aPref, aSetting, PrefValueKind::Default);
   } else {
     aSetting->defaultValue() = null_t();
   }
 
-  if (aPref->mPrefFlags.HasUserValue()) {
+  if (aPref->HasUserValue()) {
     GetPrefValueFromEntry(aPref, aSetting, PrefValueKind::User);
   } else {
     aSetting->userValue() = null_t();
   }
 
   MOZ_ASSERT(aSetting->defaultValue().type() == dom::MaybePrefValue::Tnull_t ||
              aSetting->userValue().type() == dom::MaybePrefValue::Tnull_t ||
              (aSetting->defaultValue().get_PrefValue().type() ==
@@ -531,32 +490,32 @@ pref_GetPrefFromEntry(PrefHashEntry* aPr
 static bool
 PREF_HasUserPref(const char* aPrefName)
 {
   if (!gHashTable) {
     return false;
   }
 
   PrefHashEntry* pref = pref_HashTableLookup(aPrefName);
-  return pref && pref->mPrefFlags.HasUserValue();
+  return pref && pref->HasUserValue();
 }
 
 // Clears the given pref (reverts it to its default value).
 static nsresult
 PREF_ClearUserPref(const char* aPrefName)
 {
   if (!gHashTable) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   PrefHashEntry* pref = pref_HashTableLookup(aPrefName);
-  if (pref && pref->mPrefFlags.HasUserValue()) {
-    pref->mPrefFlags.SetHasUserValue(false);
-
-    if (!pref->mPrefFlags.HasDefaultValue()) {
+  if (pref && pref->HasUserValue()) {
+    pref->SetHasUserValue(false);
+
+    if (!pref->HasDefaultValue()) {
       gHashTable->RemoveEntry(pref);
     }
 
     pref_DoCallback(aPrefName);
     Preferences::HandleDirty();
   }
   return NS_OK;
 }
@@ -570,23 +529,23 @@ PREF_ClearAllUserPrefs()
   if (!gHashTable) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   Vector<const char*> prefNames;
   for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
     auto pref = static_cast<PrefHashEntry*>(iter.Get());
 
-    if (pref->mPrefFlags.HasUserValue()) {
+    if (pref->HasUserValue()) {
       if (!prefNames.append(pref->mKey)) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
 
-      pref->mPrefFlags.SetHasUserValue(false);
-      if (!pref->mPrefFlags.HasDefaultValue()) {
+      pref->SetHasUserValue(false);
+      if (!pref->HasDefaultValue()) {
         iter.Remove();
       }
     }
   }
 
   for (const char* prefName : prefNames) {
     pref_DoCallback(prefName);
   }
@@ -605,23 +564,23 @@ PREF_LockPref(const char* aKey, bool aLo
   }
 
   PrefHashEntry* pref = pref_HashTableLookup(aKey);
   if (!pref) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (aLockIt) {
-    if (!pref->mPrefFlags.IsLocked()) {
-      pref->mPrefFlags.SetLocked(true);
+    if (!pref->IsLocked()) {
+      pref->SetLocked(true);
       gIsAnyPrefLocked = true;
       pref_DoCallback(aKey);
     }
-  } else if (pref->mPrefFlags.IsLocked()) {
-    pref->mPrefFlags.SetLocked(false);
+  } else if (pref->IsLocked()) {
+    pref->SetLocked(false);
     pref_DoCallback(aKey);
   }
 
   return NS_OK;
 }
 
 //
 // Hash table functions
@@ -757,77 +716,72 @@ pref_SetPref(const char* aKey,
 
   auto pref = static_cast<PrefHashEntry*>(gHashTable->Add(aKey, fallible));
   if (!pref) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (!pref->mKey) {
     // New (zeroed) entry. Initialize it.
-    pref->mPrefFlags.SetPrefType(aType);
+    pref->SetType(aType);
     pref->mKey = ArenaStrdup(aKey, gPrefNameArena);
 
-  } else if (pref->mPrefFlags.HasDefaultValue() &&
-             !pref->mPrefFlags.IsPrefType(aType)) {
+  } else if (pref->HasDefaultValue() && !pref->IsType(aType)) {
     NS_WARNING(
       nsPrintfCString(
         "Ignoring attempt to overwrite value of default pref %s (type %s) with "
         "the wrong type (%s)!",
         aKey,
-        PrefTypeToString(pref->mPrefFlags.GetPrefType()),
+        PrefTypeToString(pref->Type()),
         PrefTypeToString(aType))
         .get());
 
     return NS_ERROR_UNEXPECTED;
   }
 
   bool valueChanged = false;
   if (aFlags & kPrefSetDefault) {
-    if (!pref->mPrefFlags.IsLocked()) {
+    if (!pref->IsLocked()) {
       // ?? change of semantics?
       if (pref_ValueChanged(pref->mDefaultValue, aValue, aType) ||
-          !pref->mPrefFlags.HasDefaultValue()) {
-        pref_SetValue(
-          &pref->mDefaultValue, pref->mPrefFlags.GetPrefType(), aValue, aType);
-        pref->mPrefFlags.SetPrefType(aType);
-        pref->mPrefFlags.SetHasDefaultValue(true);
+          !pref->HasDefaultValue()) {
+        pref_SetValue(&pref->mDefaultValue, pref->Type(), aValue, aType);
+        pref->SetType(aType);
+        pref->SetHasDefaultValue(true);
         if (aFlags & kPrefSticky) {
-          pref->mPrefFlags.SetIsSticky(true);
+          pref->SetIsSticky(true);
         }
-        if (!pref->mPrefFlags.HasUserValue()) {
+        if (!pref->HasUserValue()) {
           valueChanged = true;
         }
       }
       // What if we change the default to be the same as the user value?
       // Should we clear the user value?
     }
   } else {
     // If new value is same as the default value and it's not a "sticky" pref,
     // then un-set the user value. Otherwise, set the user value only if it has
     // changed.
-    if ((pref->mPrefFlags.HasDefaultValue()) &&
-        !(pref->mPrefFlags.IsSticky()) &&
+    if (pref->HasDefaultValue() && !pref->IsSticky() &&
         !pref_ValueChanged(pref->mDefaultValue, aValue, aType) &&
         !(aFlags & kPrefForceSet)) {
-      if (pref->mPrefFlags.HasUserValue()) {
+      if (pref->HasUserValue()) {
         // XXX should we free a user-set string value if there is one?
-        pref->mPrefFlags.SetHasUserValue(false);
-        if (!pref->mPrefFlags.IsLocked()) {
+        pref->SetHasUserValue(false);
+        if (!pref->IsLocked()) {
           Preferences::HandleDirty();
           valueChanged = true;
         }
       }
-    } else if (!pref->mPrefFlags.HasUserValue() ||
-               !pref->mPrefFlags.IsPrefType(aType) ||
+    } else if (!pref->HasUserValue() || !pref->IsType(aType) ||
                pref_ValueChanged(pref->mUserValue, aValue, aType)) {
-      pref_SetValue(
-        &pref->mUserValue, pref->mPrefFlags.GetPrefType(), aValue, aType);
-      pref->mPrefFlags.SetPrefType(aType);
-      pref->mPrefFlags.SetHasUserValue(true);
-      if (!pref->mPrefFlags.IsLocked()) {
+      pref_SetValue(&pref->mUserValue, pref->Type(), aValue, aType);
+      pref->SetType(aType);
+      pref->SetHasUserValue(true);
+      if (!pref->IsLocked()) {
         Preferences::HandleDirty();
         valueChanged = true;
       }
     }
   }
 
   if (valueChanged) {
     return pref_DoCallback(aKey);
@@ -839,17 +793,17 @@ pref_SetPref(const char* aKey,
 // Bool function that returns whether or not the preference is locked and
 // therefore cannot be changed.
 static bool
 PREF_PrefIsLocked(const char* aPrefName)
 {
   bool result = false;
   if (gIsAnyPrefLocked && gHashTable) {
     PrefHashEntry* pref = pref_HashTableLookup(aPrefName);
-    if (pref && pref->mPrefFlags.IsLocked()) {
+    if (pref && pref->IsLocked()) {
       result = true;
     }
   }
 
   return result;
 }
 
 // Adds a node to the callback list; the position depends on aIsPriority. The
@@ -1996,17 +1950,17 @@ nsPrefBranch::GetPrefType(const char* aP
 {
   NS_ENSURE_ARG(aPrefName);
 
   const PrefName& prefName = GetPrefName(aPrefName);
   PrefType type = PrefType::Invalid;
   if (gHashTable) {
     PrefHashEntry* pref = pref_HashTableLookup(prefName.get());
     if (pref) {
-      type = pref->mPrefFlags.GetPrefType();
+      type = pref->Type();
     }
   }
 
   switch (type) {
     case PrefType::String:
       *aRetVal = PREF_STRING;
       break;
 
@@ -4488,25 +4442,25 @@ Preferences::InitInitialObjects()
 
 /* static */ nsresult
 Preferences::GetBool(const char* aPrefName, bool* aResult, PrefValueKind aKind)
 {
   NS_PRECONDITION(aResult, "aResult must not be NULL");
   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
 
   PrefHashEntry* pref = pref_HashTableLookup(aPrefName);
-  if (!pref || !pref->mPrefFlags.IsTypeBool()) {
+  if (!pref || !pref->IsTypeBool()) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  if (aKind == PrefValueKind::Default || pref->mPrefFlags.IsLocked() ||
-      !pref->mPrefFlags.HasUserValue()) {
+  if (aKind == PrefValueKind::Default || pref->IsLocked() ||
+      !pref->HasUserValue()) {
 
     // Do we have a default?
-    if (!pref->mPrefFlags.HasDefaultValue()) {
+    if (!pref->HasDefaultValue()) {
       return NS_ERROR_UNEXPECTED;
     }
     *aResult = pref->mDefaultValue.mBoolVal;
   } else {
     *aResult = pref->mUserValue.mBoolVal;
   }
 
   return NS_OK;
@@ -4516,25 +4470,25 @@ Preferences::GetBool(const char* aPrefNa
 Preferences::GetInt(const char* aPrefName,
                     int32_t* aResult,
                     PrefValueKind aKind)
 {
   NS_PRECONDITION(aResult, "aResult must not be NULL");
   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
 
   PrefHashEntry* pref = pref_HashTableLookup(aPrefName);
-  if (!pref || !pref->mPrefFlags.IsTypeInt()) {
+  if (!pref || !pref->IsTypeInt()) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  if (aKind == PrefValueKind::Default || pref->mPrefFlags.IsLocked() ||
-      !pref->mPrefFlags.HasUserValue()) {
+  if (aKind == PrefValueKind::Default || pref->IsLocked() ||
+      !pref->HasUserValue()) {
 
     // Do we have a default?
-    if (!pref->mPrefFlags.HasDefaultValue()) {
+    if (!pref->HasDefaultValue()) {
       return NS_ERROR_UNEXPECTED;
     }
     *aResult = pref->mDefaultValue.mIntVal;
   } else {
     *aResult = pref->mUserValue.mIntVal;
   }
 
   return NS_OK;
@@ -4560,26 +4514,26 @@ Preferences::GetCString(const char* aPre
                         nsACString& aResult,
                         PrefValueKind aKind)
 {
   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
 
   aResult.SetIsVoid(true);
 
   PrefHashEntry* pref = pref_HashTableLookup(aPrefName);
-  if (!pref || !pref->mPrefFlags.IsTypeString()) {
+  if (!pref || !pref->IsTypeString()) {
     return NS_ERROR_UNEXPECTED;
   }
 
   const char* stringVal = nullptr;
-  if (aKind == PrefValueKind::Default || pref->mPrefFlags.IsLocked() ||
-      !pref->mPrefFlags.HasUserValue()) {
+  if (aKind == PrefValueKind::Default || pref->IsLocked() ||
+      !pref->HasUserValue()) {
 
     // Do we have a default?
-    if (!pref->mPrefFlags.HasDefaultValue()) {
+    if (!pref->HasDefaultValue()) {
       return NS_ERROR_UNEXPECTED;
     }
     stringVal = pref->mDefaultValue.mStringVal;
   } else {
     stringVal = pref->mUserValue.mStringVal;
   }
 
   if (!stringVal) {