1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
7 /* Class to manage lookup of static names in a table. */
9 #include "mozilla/HashFunctions.h"
10 #include "mozilla/TextUtils.h"
15 #include "nsISupportsImpl.h"
17 #include "nsStaticNameTable.h"
19 using namespace mozilla
;
22 NameTableKey(const nsDependentCString aNameArray
[], const nsCString
* aKeyStr
)
23 : mNameArray(aNameArray
), mIsUnichar(false) {
24 mKeyStr
.m1b
= aKeyStr
;
27 NameTableKey(const nsDependentCString aNameArray
[], const nsString
* aKeyStr
)
28 : mNameArray(aNameArray
), mIsUnichar(true) {
29 mKeyStr
.m2b
= aKeyStr
;
32 const nsDependentCString
* mNameArray
;
40 struct NameTableEntry
: public PLDHashEntryHdr
{
44 static bool matchNameKeysCaseInsensitive(const PLDHashEntryHdr
* aHdr
,
45 const void* aVoidKey
) {
46 auto entry
= static_cast<const NameTableEntry
*>(aHdr
);
47 auto key
= static_cast<const NameTableKey
*>(aVoidKey
);
48 const nsDependentCString
* name
= &key
->mNameArray
[entry
->mIndex
];
50 return key
->mIsUnichar
? key
->mKeyStr
.m2b
->LowerCaseEqualsASCII(
51 name
->get(), name
->Length())
52 : key
->mKeyStr
.m1b
->LowerCaseEqualsASCII(
53 name
->get(), name
->Length());
57 * caseInsensitiveHashKey is just like PLDHashTable::HashStringKey except it
58 * uses (*s & ~0x20) instead of simply *s. This means that "aFOO" and
59 * "afoo" and "aFoo" will all hash to the same thing. It also means
60 * that some strings that aren't case-insensensitively equal will hash
61 * to the same value, but it's just a hash function so it doesn't
64 static PLDHashNumber
caseInsensitiveStringHashKey(const void* aKey
) {
66 const NameTableKey
* tableKey
= static_cast<const NameTableKey
*>(aKey
);
67 if (tableKey
->mIsUnichar
) {
68 for (const char16_t
* s
= tableKey
->mKeyStr
.m2b
->get(); *s
!= '\0'; s
++) {
69 h
= AddToHash(h
, *s
& ~0x20);
72 for (const unsigned char* s
= reinterpret_cast<const unsigned char*>(
73 tableKey
->mKeyStr
.m1b
->get());
75 h
= AddToHash(h
, *s
& ~0x20);
81 static const struct PLDHashTableOps nametable_CaseInsensitiveHashTableOps
= {
82 caseInsensitiveStringHashKey
,
83 matchNameKeysCaseInsensitive
,
84 PLDHashTable::MoveEntryStub
,
85 PLDHashTable::ClearEntryStub
,
89 nsStaticCaseInsensitiveNameTable::nsStaticCaseInsensitiveNameTable(
90 const char* const aNames
[], int32_t aLength
)
91 : mNameArray(nullptr),
92 mNameTable(&nametable_CaseInsensitiveHashTableOps
, sizeof(NameTableEntry
),
95 MOZ_COUNT_CTOR(nsStaticCaseInsensitiveNameTable
);
97 MOZ_ASSERT(aNames
, "null name table");
98 MOZ_ASSERT(aLength
, "0 length");
101 (nsDependentCString
*)moz_xmalloc(aLength
* sizeof(nsDependentCString
));
103 for (int32_t index
= 0; index
< aLength
; ++index
) {
104 const char* raw
= aNames
[index
];
107 // verify invariants of contents
108 nsAutoCString
temp1(raw
);
109 nsDependentCString
temp2(raw
);
111 MOZ_ASSERT(temp1
.Equals(temp2
), "upper case char in table");
112 MOZ_ASSERT(IsAsciiNullTerminated(raw
),
113 "non-ascii string in table -- "
114 "case-insensitive matching won't work right");
117 // use placement-new to initialize the string object
118 nsDependentCString
* strPtr
= &mNameArray
[index
];
119 new (strPtr
) nsDependentCString(raw
);
121 NameTableKey
key(mNameArray
, strPtr
);
123 auto entry
= static_cast<NameTableEntry
*>(mNameTable
.Add(&key
, fallible
));
128 // If the entry already exists it's unlikely but possible that its index is
129 // zero, in which case this assertion won't fail. But if the index is
130 // non-zero (highly likely) then it will fail. In other words, this
131 // assertion is likely but not guaranteed to detect if an entry is already
133 MOZ_ASSERT(entry
->mIndex
== 0, "Entry already exists!");
135 entry
->mIndex
= index
;
137 mNameTable
.MarkImmutable();
140 nsStaticCaseInsensitiveNameTable::~nsStaticCaseInsensitiveNameTable() {
141 // manually call the destructor on placement-new'ed objects
142 for (uint32_t index
= 0; index
< mNameTable
.EntryCount(); index
++) {
143 mNameArray
[index
].~nsDependentCString();
145 free((void*)mNameArray
);
146 MOZ_COUNT_DTOR(nsStaticCaseInsensitiveNameTable
);
149 int32_t nsStaticCaseInsensitiveNameTable::Lookup(
150 const nsACString
& aName
) const {
151 NS_ASSERTION(mNameArray
, "not inited");
153 const nsCString
& str
= PromiseFlatCString(aName
);
155 NameTableKey
key(mNameArray
, &str
);
156 auto entry
= static_cast<NameTableEntry
*>(mNameTable
.Search(&key
));
158 return entry
? entry
->mIndex
: nsStaticCaseInsensitiveNameTable::NOT_FOUND
;
161 int32_t nsStaticCaseInsensitiveNameTable::Lookup(const nsAString
& aName
) const {
162 NS_ASSERTION(mNameArray
, "not inited");
164 const nsString
& str
= PromiseFlatString(aName
);
166 NameTableKey
key(mNameArray
, &str
);
167 auto entry
= static_cast<NameTableEntry
*>(mNameTable
.Search(&key
));
169 return entry
? entry
->mIndex
: nsStaticCaseInsensitiveNameTable::NOT_FOUND
;
172 const nsCString
& nsStaticCaseInsensitiveNameTable::GetStringValue(
174 NS_ASSERTION(mNameArray
, "not inited");
176 if ((NOT_FOUND
< aIndex
) && ((uint32_t)aIndex
< mNameTable
.EntryCount())) {
177 return mNameArray
[aIndex
];