forked from mirrors/gecko-dev
		
	Put them behind a MOZ_HASH_TABLE_CHECKS_ENABLED define, which right now is only defined in DEBUG builds, preserving behavior. MakeImmutable becomes an empty inline function when disabled, which should be zero-cost. Differential Revision: https://phabricator.services.mozilla.com/D50493 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			180 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			180 lines
		
	
	
	
		
			5.8 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/. */
 | 
						|
 | 
						|
/* Class to manage lookup of static names in a table. */
 | 
						|
 | 
						|
#include "mozilla/HashFunctions.h"
 | 
						|
#include "mozilla/TextUtils.h"
 | 
						|
 | 
						|
#include "nsCRT.h"
 | 
						|
 | 
						|
#include "nscore.h"
 | 
						|
#include "nsISupportsImpl.h"
 | 
						|
 | 
						|
#include "nsStaticNameTable.h"
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
 | 
						|
struct NameTableKey {
 | 
						|
  NameTableKey(const nsDependentCString aNameArray[], const nsCString* aKeyStr)
 | 
						|
      : mNameArray(aNameArray), mIsUnichar(false) {
 | 
						|
    mKeyStr.m1b = aKeyStr;
 | 
						|
  }
 | 
						|
 | 
						|
  NameTableKey(const nsDependentCString aNameArray[], const nsString* aKeyStr)
 | 
						|
      : mNameArray(aNameArray), mIsUnichar(true) {
 | 
						|
    mKeyStr.m2b = aKeyStr;
 | 
						|
  }
 | 
						|
 | 
						|
  const nsDependentCString* mNameArray;
 | 
						|
  union {
 | 
						|
    const nsCString* m1b;
 | 
						|
    const nsString* m2b;
 | 
						|
  } mKeyStr;
 | 
						|
  bool mIsUnichar;
 | 
						|
};
 | 
						|
 | 
						|
struct NameTableEntry : public PLDHashEntryHdr {
 | 
						|
  int32_t mIndex;
 | 
						|
};
 | 
						|
 | 
						|
static bool matchNameKeysCaseInsensitive(const PLDHashEntryHdr* aHdr,
 | 
						|
                                         const void* aVoidKey) {
 | 
						|
  auto entry = static_cast<const NameTableEntry*>(aHdr);
 | 
						|
  auto key = static_cast<const NameTableKey*>(aVoidKey);
 | 
						|
  const nsDependentCString* name = &key->mNameArray[entry->mIndex];
 | 
						|
 | 
						|
  return key->mIsUnichar ? key->mKeyStr.m2b->LowerCaseEqualsASCII(
 | 
						|
                               name->get(), name->Length())
 | 
						|
                         : key->mKeyStr.m1b->LowerCaseEqualsASCII(
 | 
						|
                               name->get(), name->Length());
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * caseInsensitiveHashKey is just like PLDHashTable::HashStringKey except it
 | 
						|
 * uses (*s & ~0x20) instead of simply *s.  This means that "aFOO" and
 | 
						|
 * "afoo" and "aFoo" will all hash to the same thing.  It also means
 | 
						|
 * that some strings that aren't case-insensensitively equal will hash
 | 
						|
 * to the same value, but it's just a hash function so it doesn't
 | 
						|
 * matter.
 | 
						|
 */
 | 
						|
static PLDHashNumber caseInsensitiveStringHashKey(const void* aKey) {
 | 
						|
  PLDHashNumber h = 0;
 | 
						|
  const NameTableKey* tableKey = static_cast<const NameTableKey*>(aKey);
 | 
						|
  if (tableKey->mIsUnichar) {
 | 
						|
    for (const char16_t* s = tableKey->mKeyStr.m2b->get(); *s != '\0'; s++) {
 | 
						|
      h = AddToHash(h, *s & ~0x20);
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    for (const unsigned char* s = reinterpret_cast<const unsigned char*>(
 | 
						|
             tableKey->mKeyStr.m1b->get());
 | 
						|
         *s != '\0'; s++) {
 | 
						|
      h = AddToHash(h, *s & ~0x20);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return h;
 | 
						|
}
 | 
						|
 | 
						|
static const struct PLDHashTableOps nametable_CaseInsensitiveHashTableOps = {
 | 
						|
    caseInsensitiveStringHashKey,
 | 
						|
    matchNameKeysCaseInsensitive,
 | 
						|
    PLDHashTable::MoveEntryStub,
 | 
						|
    PLDHashTable::ClearEntryStub,
 | 
						|
    nullptr,
 | 
						|
};
 | 
						|
 | 
						|
nsStaticCaseInsensitiveNameTable::nsStaticCaseInsensitiveNameTable(
 | 
						|
    const char* const aNames[], int32_t aLength)
 | 
						|
    : mNameArray(nullptr),
 | 
						|
      mNameTable(&nametable_CaseInsensitiveHashTableOps, sizeof(NameTableEntry),
 | 
						|
                 aLength),
 | 
						|
      mNullStr("") {
 | 
						|
  MOZ_COUNT_CTOR(nsStaticCaseInsensitiveNameTable);
 | 
						|
 | 
						|
  MOZ_ASSERT(aNames, "null name table");
 | 
						|
  MOZ_ASSERT(aLength, "0 length");
 | 
						|
 | 
						|
  mNameArray =
 | 
						|
      (nsDependentCString*)moz_xmalloc(aLength * sizeof(nsDependentCString));
 | 
						|
 | 
						|
  for (int32_t index = 0; index < aLength; ++index) {
 | 
						|
    const char* raw = aNames[index];
 | 
						|
#ifdef DEBUG
 | 
						|
    {
 | 
						|
      // verify invariants of contents
 | 
						|
      nsAutoCString temp1(raw);
 | 
						|
      nsDependentCString temp2(raw);
 | 
						|
      ToLowerCase(temp1);
 | 
						|
      MOZ_ASSERT(temp1.Equals(temp2), "upper case char in table");
 | 
						|
      MOZ_ASSERT(IsAsciiNullTerminated(raw),
 | 
						|
                 "non-ascii string in table -- "
 | 
						|
                 "case-insensitive matching won't work right");
 | 
						|
    }
 | 
						|
#endif
 | 
						|
    // use placement-new to initialize the string object
 | 
						|
    nsDependentCString* strPtr = &mNameArray[index];
 | 
						|
    new (strPtr) nsDependentCString(raw);
 | 
						|
 | 
						|
    NameTableKey key(mNameArray, strPtr);
 | 
						|
 | 
						|
    auto entry = static_cast<NameTableEntry*>(mNameTable.Add(&key, fallible));
 | 
						|
    if (!entry) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // If the entry already exists it's unlikely but possible that its index is
 | 
						|
    // zero, in which case this assertion won't fail. But if the index is
 | 
						|
    // non-zero (highly likely) then it will fail. In other words, this
 | 
						|
    // assertion is likely but not guaranteed to detect if an entry is already
 | 
						|
    // used.
 | 
						|
    MOZ_ASSERT(entry->mIndex == 0, "Entry already exists!");
 | 
						|
 | 
						|
    entry->mIndex = index;
 | 
						|
  }
 | 
						|
  mNameTable.MarkImmutable();
 | 
						|
}
 | 
						|
 | 
						|
nsStaticCaseInsensitiveNameTable::~nsStaticCaseInsensitiveNameTable() {
 | 
						|
  // manually call the destructor on placement-new'ed objects
 | 
						|
  for (uint32_t index = 0; index < mNameTable.EntryCount(); index++) {
 | 
						|
    mNameArray[index].~nsDependentCString();
 | 
						|
  }
 | 
						|
  free((void*)mNameArray);
 | 
						|
  MOZ_COUNT_DTOR(nsStaticCaseInsensitiveNameTable);
 | 
						|
}
 | 
						|
 | 
						|
int32_t nsStaticCaseInsensitiveNameTable::Lookup(
 | 
						|
    const nsACString& aName) const {
 | 
						|
  NS_ASSERTION(mNameArray, "not inited");
 | 
						|
 | 
						|
  const nsCString& str = PromiseFlatCString(aName);
 | 
						|
 | 
						|
  NameTableKey key(mNameArray, &str);
 | 
						|
  auto entry = static_cast<NameTableEntry*>(mNameTable.Search(&key));
 | 
						|
 | 
						|
  return entry ? entry->mIndex : nsStaticCaseInsensitiveNameTable::NOT_FOUND;
 | 
						|
}
 | 
						|
 | 
						|
int32_t nsStaticCaseInsensitiveNameTable::Lookup(const nsAString& aName) const {
 | 
						|
  NS_ASSERTION(mNameArray, "not inited");
 | 
						|
 | 
						|
  const nsString& str = PromiseFlatString(aName);
 | 
						|
 | 
						|
  NameTableKey key(mNameArray, &str);
 | 
						|
  auto entry = static_cast<NameTableEntry*>(mNameTable.Search(&key));
 | 
						|
 | 
						|
  return entry ? entry->mIndex : nsStaticCaseInsensitiveNameTable::NOT_FOUND;
 | 
						|
}
 | 
						|
 | 
						|
const nsCString& nsStaticCaseInsensitiveNameTable::GetStringValue(
 | 
						|
    int32_t aIndex) {
 | 
						|
  NS_ASSERTION(mNameArray, "not inited");
 | 
						|
 | 
						|
  if ((NOT_FOUND < aIndex) && ((uint32_t)aIndex < mNameTable.EntryCount())) {
 | 
						|
    return mNameArray[aIndex];
 | 
						|
  }
 | 
						|
  return mNullStr;
 | 
						|
}
 |