1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code, released
17 : * March 31, 1998.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Netscape Communications Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 1998
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /* Class to manage lookup of static names in a table. */
41 :
42 : #include "nsCRT.h"
43 :
44 : #include "nscore.h"
45 : #include "nsString.h"
46 : #include "nsReadableUtils.h"
47 : #include "prbit.h"
48 : #include "mozilla/HashFunctions.h"
49 :
50 : #define PL_ARENA_CONST_ALIGN_MASK 3
51 : #include "nsStaticNameTable.h"
52 :
53 : using namespace mozilla;
54 :
55 : struct NameTableKey
56 : {
57 1387152 : NameTableKey(const nsAFlatCString* aKeyStr)
58 1387152 : : mIsUnichar(false)
59 : {
60 1387152 : mKeyStr.m1b = aKeyStr;
61 1387152 : }
62 :
63 0 : NameTableKey(const nsAFlatString* aKeyStr)
64 0 : : mIsUnichar(true)
65 : {
66 0 : mKeyStr.m2b = aKeyStr;
67 0 : }
68 :
69 : bool mIsUnichar;
70 : union {
71 : const nsAFlatCString* m1b;
72 : const nsAFlatString* m2b;
73 : } mKeyStr;
74 : };
75 :
76 : struct NameTableEntry : public PLDHashEntryHdr
77 : {
78 : // no ownership here!
79 : const nsAFlatCString* mString;
80 : PRInt32 mIndex;
81 : };
82 :
83 : static bool
84 0 : matchNameKeysCaseInsensitive(PLDHashTable*, const PLDHashEntryHdr* aHdr,
85 : const void* key)
86 : {
87 : const NameTableEntry* entry =
88 0 : static_cast<const NameTableEntry *>(aHdr);
89 0 : const NameTableKey *keyValue = static_cast<const NameTableKey*>(key);
90 :
91 0 : const nsAFlatCString* entryKey = entry->mString;
92 :
93 0 : if (keyValue->mIsUnichar) {
94 : return keyValue->mKeyStr.m2b->
95 0 : LowerCaseEqualsASCII(entryKey->get(), entryKey->Length());
96 : }
97 :
98 : return keyValue->mKeyStr.m1b->
99 0 : LowerCaseEqualsASCII(entryKey->get(), entryKey->Length());
100 : }
101 :
102 : /*
103 : * caseInsensitiveHashKey is just like PL_DHashStringKey except it
104 : * uses (*s & ~0x20) instead of simply *s. This means that "aFOO" and
105 : * "afoo" and "aFoo" will all hash to the same thing. It also means
106 : * that some strings that aren't case-insensensitively equal will hash
107 : * to the same value, but it's just a hash function so it doesn't
108 : * matter.
109 : */
110 : static PLDHashNumber
111 1387152 : caseInsensitiveStringHashKey(PLDHashTable *table, const void *key)
112 : {
113 1387152 : PLDHashNumber h = 0;
114 1387152 : const NameTableKey* tableKey = static_cast<const NameTableKey*>(key);
115 1387152 : if (tableKey->mIsUnichar) {
116 0 : for (const PRUnichar* s = tableKey->mKeyStr.m2b->get();
117 : *s != '\0';
118 : s++)
119 0 : h = AddToHash(h, *s & ~0x20);
120 : } else {
121 18581940 : for (const unsigned char* s =
122 : reinterpret_cast<const unsigned char*>
123 1387152 : (tableKey->mKeyStr.m1b->get());
124 : *s != '\0';
125 : s++)
126 17194788 : h = AddToHash(h, *s & ~0x20);
127 : }
128 1387152 : return h;
129 : }
130 :
131 : static const struct PLDHashTableOps nametable_CaseInsensitiveHashTableOps = {
132 : PL_DHashAllocTable,
133 : PL_DHashFreeTable,
134 : caseInsensitiveStringHashKey,
135 : matchNameKeysCaseInsensitive,
136 : PL_DHashMoveEntryStub,
137 : PL_DHashClearEntryStub,
138 : PL_DHashFinalizeStub,
139 : nsnull,
140 : };
141 :
142 5616 : nsStaticCaseInsensitiveNameTable::nsStaticCaseInsensitiveNameTable()
143 5616 : : mNameArray(nsnull), mNullStr("")
144 : {
145 5616 : MOZ_COUNT_CTOR(nsStaticCaseInsensitiveNameTable);
146 5616 : mNameTable.ops = nsnull;
147 5616 : }
148 :
149 11224 : nsStaticCaseInsensitiveNameTable::~nsStaticCaseInsensitiveNameTable()
150 : {
151 5612 : if (mNameArray) {
152 : // manually call the destructor on placement-new'ed objects
153 1391776 : for (PRUint32 index = 0; index < mNameTable.entryCount; index++) {
154 1386164 : mNameArray[index].~nsDependentCString();
155 : }
156 5612 : nsMemory::Free((void*)mNameArray);
157 : }
158 5612 : if (mNameTable.ops)
159 5612 : PL_DHashTableFinish(&mNameTable);
160 5612 : MOZ_COUNT_DTOR(nsStaticCaseInsensitiveNameTable);
161 5612 : }
162 :
163 : bool
164 5616 : nsStaticCaseInsensitiveNameTable::Init(const char* const aNames[], PRInt32 Count)
165 : {
166 5616 : NS_ASSERTION(!mNameArray, "double Init");
167 5616 : NS_ASSERTION(!mNameTable.ops, "double Init");
168 5616 : NS_ASSERTION(aNames, "null name table");
169 5616 : NS_ASSERTION(Count, "0 count");
170 :
171 : mNameArray = (nsDependentCString*)
172 5616 : nsMemory::Alloc(Count * sizeof(nsDependentCString));
173 5616 : if (!mNameArray)
174 0 : return false;
175 :
176 5616 : if (!PL_DHashTableInit(&mNameTable,
177 : &nametable_CaseInsensitiveHashTableOps,
178 5616 : nsnull, sizeof(NameTableEntry), Count)) {
179 0 : mNameTable.ops = nsnull;
180 0 : return false;
181 : }
182 :
183 1392768 : for (PRInt32 index = 0; index < Count; ++index) {
184 1387152 : const char* raw = aNames[index];
185 : #ifdef DEBUG
186 : {
187 : // verify invariants of contents
188 2774304 : nsCAutoString temp1(raw);
189 2774304 : nsDependentCString temp2(raw);
190 1387152 : ToLowerCase(temp1);
191 1387152 : NS_ASSERTION(temp1.Equals(temp2), "upper case char in table");
192 1387152 : NS_ASSERTION(nsCRT::IsAscii(raw),
193 : "non-ascii string in table -- "
194 : "case-insensitive matching won't work right");
195 : }
196 : #endif
197 : // use placement-new to initialize the string object
198 1387152 : nsDependentCString* strPtr = &mNameArray[index];
199 1387152 : new (strPtr) nsDependentCString(raw);
200 :
201 1387152 : NameTableKey key(strPtr);
202 :
203 : NameTableEntry *entry =
204 : static_cast<NameTableEntry*>
205 : (PL_DHashTableOperate(&mNameTable, &key,
206 1387152 : PL_DHASH_ADD));
207 :
208 1387152 : if (!entry) continue;
209 :
210 1387152 : NS_ASSERTION(entry->mString == 0, "Entry already exists!");
211 :
212 1387152 : entry->mString = strPtr; // not owned!
213 1387152 : entry->mIndex = index;
214 : }
215 5616 : return true;
216 : }
217 :
218 : PRInt32
219 0 : nsStaticCaseInsensitiveNameTable::Lookup(const nsACString& aName)
220 : {
221 0 : NS_ASSERTION(mNameArray, "not inited");
222 0 : NS_ASSERTION(mNameTable.ops, "not inited");
223 :
224 0 : const nsAFlatCString& str = PromiseFlatCString(aName);
225 :
226 0 : NameTableKey key(&str);
227 : NameTableEntry *entry =
228 : static_cast<NameTableEntry*>
229 : (PL_DHashTableOperate(&mNameTable, &key,
230 0 : PL_DHASH_LOOKUP));
231 :
232 0 : if (PL_DHASH_ENTRY_IS_FREE(entry))
233 0 : return nsStaticCaseInsensitiveNameTable::NOT_FOUND;
234 :
235 0 : return entry->mIndex;
236 : }
237 :
238 : PRInt32
239 0 : nsStaticCaseInsensitiveNameTable::Lookup(const nsAString& aName)
240 : {
241 0 : NS_ASSERTION(mNameArray, "not inited");
242 0 : NS_ASSERTION(mNameTable.ops, "not inited");
243 :
244 0 : const nsAFlatString& str = PromiseFlatString(aName);
245 :
246 0 : NameTableKey key(&str);
247 : NameTableEntry *entry =
248 : static_cast<NameTableEntry*>
249 : (PL_DHashTableOperate(&mNameTable, &key,
250 0 : PL_DHASH_LOOKUP));
251 :
252 0 : if (PL_DHASH_ENTRY_IS_FREE(entry))
253 0 : return nsStaticCaseInsensitiveNameTable::NOT_FOUND;
254 :
255 0 : return entry->mIndex;
256 : }
257 :
258 : const nsAFlatCString&
259 0 : nsStaticCaseInsensitiveNameTable::GetStringValue(PRInt32 index)
260 : {
261 0 : NS_ASSERTION(mNameArray, "not inited");
262 0 : NS_ASSERTION(mNameTable.ops, "not inited");
263 :
264 0 : if ((NOT_FOUND < index) && ((PRUint32)index < mNameTable.entryCount)) {
265 0 : return mNameArray[index];
266 : }
267 0 : return mNullStr;
268 : }
|