Line data Source code
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/. */
6 :
7 : /* Class to manage lookup of static names in a table. */
8 :
9 : #include "nsCRT.h"
10 :
11 : #include "nscore.h"
12 : #include "mozilla/HashFunctions.h"
13 : #include "nsISupportsImpl.h"
14 :
15 : #include "nsStaticNameTable.h"
16 :
17 : using namespace mozilla;
18 :
19 : struct NameTableKey
20 : {
21 4395 : NameTableKey(const nsDependentCString aNameArray[],
22 : const nsCString* aKeyStr)
23 4395 : : mNameArray(aNameArray)
24 4395 : , mIsUnichar(false)
25 : {
26 4395 : mKeyStr.m1b = aKeyStr;
27 4395 : }
28 :
29 13306 : NameTableKey(const nsDependentCString aNameArray[],
30 : const nsString* aKeyStr)
31 13306 : : mNameArray(aNameArray)
32 13306 : , mIsUnichar(true)
33 : {
34 13306 : mKeyStr.m2b = aKeyStr;
35 13306 : }
36 :
37 : const nsDependentCString* mNameArray;
38 : union
39 : {
40 : const nsCString* m1b;
41 : const nsString* m2b;
42 : } mKeyStr;
43 : bool mIsUnichar;
44 : };
45 :
46 : struct NameTableEntry : public PLDHashEntryHdr
47 : {
48 : int32_t mIndex;
49 : };
50 :
51 : static bool
52 12141 : matchNameKeysCaseInsensitive(const PLDHashEntryHdr* aHdr, const void* aVoidKey)
53 : {
54 12141 : auto entry = static_cast<const NameTableEntry*>(aHdr);
55 12141 : auto key = static_cast<const NameTableKey*>(aVoidKey);
56 12141 : const nsDependentCString* name = &key->mNameArray[entry->mIndex];
57 :
58 12141 : return key->mIsUnichar
59 12141 : ? key->mKeyStr.m2b->LowerCaseEqualsASCII(name->get(), name->Length())
60 12141 : : key->mKeyStr.m1b->LowerCaseEqualsASCII(name->get(), name->Length());
61 : }
62 :
63 : /*
64 : * caseInsensitiveHashKey is just like PLDHashTable::HashStringKey except it
65 : * uses (*s & ~0x20) instead of simply *s. This means that "aFOO" and
66 : * "afoo" and "aFoo" will all hash to the same thing. It also means
67 : * that some strings that aren't case-insensensitively equal will hash
68 : * to the same value, but it's just a hash function so it doesn't
69 : * matter.
70 : */
71 : static PLDHashNumber
72 17701 : caseInsensitiveStringHashKey(const void* aKey)
73 : {
74 17701 : PLDHashNumber h = 0;
75 17701 : const NameTableKey* tableKey = static_cast<const NameTableKey*>(aKey);
76 17701 : if (tableKey->mIsUnichar) {
77 146753 : for (const char16_t* s = tableKey->mKeyStr.m2b->get();
78 146753 : *s != '\0';
79 : s++) {
80 133447 : h = AddToHash(h, *s & ~0x20);
81 : }
82 : } else {
83 54582 : for (const unsigned char* s = reinterpret_cast<const unsigned char*>(
84 4395 : tableKey->mKeyStr.m1b->get());
85 58977 : *s != '\0';
86 : s++) {
87 54582 : h = AddToHash(h, *s & ~0x20);
88 : }
89 : }
90 17701 : return h;
91 : }
92 :
93 : static const struct PLDHashTableOps nametable_CaseInsensitiveHashTableOps = {
94 : caseInsensitiveStringHashKey,
95 : matchNameKeysCaseInsensitive,
96 : PLDHashTable::MoveEntryStub,
97 : PLDHashTable::ClearEntryStub,
98 : nullptr,
99 : };
100 :
101 18 : nsStaticCaseInsensitiveNameTable::nsStaticCaseInsensitiveNameTable(
102 18 : const char* const aNames[], int32_t aLength)
103 : : mNameArray(nullptr)
104 : , mNameTable(&nametable_CaseInsensitiveHashTableOps,
105 : sizeof(NameTableEntry), aLength)
106 18 : , mNullStr("")
107 : {
108 18 : MOZ_COUNT_CTOR(nsStaticCaseInsensitiveNameTable);
109 :
110 18 : MOZ_ASSERT(aNames, "null name table");
111 18 : MOZ_ASSERT(aLength, "0 length");
112 :
113 18 : mNameArray = (nsDependentCString*)
114 18 : moz_xmalloc(aLength * sizeof(nsDependentCString));
115 :
116 4413 : for (int32_t index = 0; index < aLength; ++index) {
117 4395 : const char* raw = aNames[index];
118 : #ifdef DEBUG
119 : {
120 : // verify invariants of contents
121 8790 : nsAutoCString temp1(raw);
122 8790 : nsDependentCString temp2(raw);
123 4395 : ToLowerCase(temp1);
124 4395 : MOZ_ASSERT(temp1.Equals(temp2), "upper case char in table");
125 4395 : MOZ_ASSERT(nsCRT::IsAscii(raw),
126 : "non-ascii string in table -- "
127 : "case-insensitive matching won't work right");
128 : }
129 : #endif
130 : // use placement-new to initialize the string object
131 4395 : nsDependentCString* strPtr = &mNameArray[index];
132 4395 : new (strPtr) nsDependentCString(raw);
133 :
134 4395 : NameTableKey key(mNameArray, strPtr);
135 :
136 4395 : auto entry = static_cast<NameTableEntry*>(mNameTable.Add(&key, fallible));
137 4395 : if (!entry) {
138 0 : continue;
139 : }
140 :
141 : // If the entry already exists it's unlikely but possible that its index is
142 : // zero, in which case this assertion won't fail. But if the index is
143 : // non-zero (highly likely) then it will fail. In other words, this
144 : // assertion is likely but not guaranteed to detect if an entry is already
145 : // used.
146 4395 : MOZ_ASSERT(entry->mIndex == 0, "Entry already exists!");
147 :
148 4395 : entry->mIndex = index;
149 : }
150 : #ifdef DEBUG
151 18 : mNameTable.MarkImmutable();
152 : #endif
153 18 : }
154 :
155 0 : nsStaticCaseInsensitiveNameTable::~nsStaticCaseInsensitiveNameTable()
156 : {
157 : // manually call the destructor on placement-new'ed objects
158 0 : for (uint32_t index = 0; index < mNameTable.EntryCount(); index++) {
159 0 : mNameArray[index].~nsDependentCString();
160 : }
161 0 : free((void*)mNameArray);
162 0 : MOZ_COUNT_DTOR(nsStaticCaseInsensitiveNameTable);
163 0 : }
164 :
165 : int32_t
166 0 : nsStaticCaseInsensitiveNameTable::Lookup(const nsACString& aName)
167 : {
168 0 : NS_ASSERTION(mNameArray, "not inited");
169 :
170 0 : const nsCString& str = PromiseFlatCString(aName);
171 :
172 0 : NameTableKey key(mNameArray, &str);
173 0 : auto entry = static_cast<NameTableEntry*>(mNameTable.Search(&key));
174 :
175 0 : return entry ? entry->mIndex : nsStaticCaseInsensitiveNameTable::NOT_FOUND;
176 : }
177 :
178 : int32_t
179 13306 : nsStaticCaseInsensitiveNameTable::Lookup(const nsAString& aName)
180 : {
181 13306 : NS_ASSERTION(mNameArray, "not inited");
182 :
183 26612 : const nsString& str = PromiseFlatString(aName);
184 :
185 13306 : NameTableKey key(mNameArray, &str);
186 13306 : auto entry = static_cast<NameTableEntry*>(mNameTable.Search(&key));
187 :
188 26612 : return entry ? entry->mIndex : nsStaticCaseInsensitiveNameTable::NOT_FOUND;
189 : }
190 :
191 : const nsCString&
192 11 : nsStaticCaseInsensitiveNameTable::GetStringValue(int32_t aIndex)
193 : {
194 11 : NS_ASSERTION(mNameArray, "not inited");
195 :
196 11 : if ((NOT_FOUND < aIndex) && ((uint32_t)aIndex < mNameTable.EntryCount())) {
197 11 : return mNameArray[aIndex];
198 : }
199 0 : return mNullStr;
200 : }
|