ntdll: Switch back to the pthread %fs register in signal handlers.
[wine.git] / dlls / setupapi / stringtable.c
blobf1ce69ce5dc90d2787ead0ac42ae4751dfb1ee65
1 /*
2 * Setupapi string table functions
4 * Copyright 2005 Eric Kohl
5 * Copyright 2014 Nikolay Sivov for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "winreg.h"
28 #include "winnls.h"
29 #include "setupapi.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
35 DECLARE_HANDLE(HSTRING_TABLE);
37 struct stringtable {
38 char *data;
39 ULONG nextoffset;
40 ULONG allocated;
41 DWORD_PTR unk[2];
42 ULONG max_extra_size;
43 LCID lcid;
46 struct stringentry {
47 DWORD nextoffset;
48 WCHAR data[1];
51 #define BUCKET_COUNT 509
52 #define DEFAULT_ALLOC_SIZE 4096
55 String table details
57 Returned string table 'handle' is a pointer to 'struct stringtable' structure.
58 Data itself is allocated separately, pointer is stored in 'data' field.
60 Data starts with array of 509 DWORDs - lookup table. Initially all offsets in that
61 array are set to -1. Right after lookup table goes data itself, stored in linked lists.
62 Lookup table offset points to first record of 'struct stringentry' type. When more
63 than one record is present in a bucket, first record links to next one with 'nextoffset'
64 field. Last record has nextoffset == -1, same when there's only one record. String data
65 is placed right after offset, and is followed by extra data. Each record has reserved
66 'max_extra_size' bytes to store extra data, it's not compacted in any way.
68 A simple hash function is used to determine which bucket a given string belongs to (see below).
70 All offsets including returned string ids are relative to 'data' pointer. When table
71 needs to grow 'allocated' size is doubled, but offsets are always valid and preserved.
75 static inline DWORD get_string_hash(const WCHAR *str, BOOL case_sensitive)
77 DWORD hash = 0;
79 while (*str) {
80 WCHAR ch = case_sensitive ? *str : towlower(*str);
81 hash += ch;
82 if (ch & ~0xff)
83 hash |= 1;
84 str++;
87 return hash % BUCKET_COUNT;
90 static inline DWORD *get_bucket_ptr(struct stringtable *table, const WCHAR *string, BOOL case_sensitive)
92 DWORD hash = get_string_hash(string, case_sensitive);
93 return (DWORD*)(table->data + hash*sizeof(DWORD));
96 static inline WCHAR *get_string_ptr(struct stringtable *table, DWORD id)
98 return (WCHAR*)(table->data + id + sizeof(DWORD));
101 static inline char *get_extradata_ptr(struct stringtable *table, DWORD id)
103 WCHAR *ptrW = get_string_ptr(table, id);
104 /* skip string itself */
105 return (char*)(ptrW + lstrlenW(ptrW) + 1);
108 static inline BOOL is_valid_string_id(struct stringtable *table, DWORD id)
110 return (id >= BUCKET_COUNT*sizeof(DWORD)) && (id < table->allocated);
113 static inline int get_aligned16_size(int size)
115 return (size + 15) & ~15;
118 /**************************************************************************
119 * StringTableInitializeEx [SETUPAPI.@]
121 * Creates a new string table and initializes it.
123 * PARAMS
124 * max_extra_size [I] Maximum extra data size
125 * reserved [I] Unused
127 * RETURNS
128 * Success: Handle to the string table
129 * Failure: NULL
131 HSTRING_TABLE WINAPI StringTableInitializeEx(ULONG max_extra_size, DWORD reserved)
133 struct stringtable *table;
135 TRACE("(%d %x)\n", max_extra_size, reserved);
137 table = MyMalloc(sizeof(*table));
138 if (!table) return NULL;
140 table->allocated = get_aligned16_size(BUCKET_COUNT*sizeof(DWORD) + DEFAULT_ALLOC_SIZE);
141 table->data = MyMalloc(table->allocated);
142 if (!table->data) {
143 MyFree(table);
144 return NULL;
147 table->nextoffset = BUCKET_COUNT*sizeof(DWORD);
148 /* FIXME: actually these two are not zero */
149 table->unk[0] = table->unk[1] = 0;
150 table->max_extra_size = max_extra_size;
151 table->lcid = GetThreadLocale();
153 /* bucket area is filled with 0xff, actual string data area is zeroed */
154 memset(table->data, 0xff, table->nextoffset);
155 memset(table->data + table->nextoffset, 0, table->allocated - table->nextoffset);
157 return (HSTRING_TABLE)table;
160 /**************************************************************************
161 * StringTableInitialize [SETUPAPI.@]
163 * Creates a new string table and initializes it.
165 * PARAMS
166 * None
168 * RETURNS
169 * Success: Handle to the string table
170 * Failure: NULL
172 HSTRING_TABLE WINAPI StringTableInitialize(void)
174 return StringTableInitializeEx(0, 0);
177 /**************************************************************************
178 * StringTableDestroy [SETUPAPI.@]
180 * Destroys a string table.
182 * PARAMS
183 * hTable [I] Handle to the string table to be destroyed
185 * RETURNS
186 * None
188 void WINAPI StringTableDestroy(HSTRING_TABLE hTable)
190 struct stringtable *table = (struct stringtable*)hTable;
192 TRACE("%p\n", table);
194 if (!table)
195 return;
197 MyFree(table->data);
198 MyFree(table);
201 /**************************************************************************
202 * StringTableDuplicate [SETUPAPI.@]
204 * Duplicates a given string table.
206 * PARAMS
207 * hTable [I] Handle to the string table
209 * RETURNS
210 * Success: Handle to the duplicated string table
211 * Failure: NULL
214 HSTRING_TABLE WINAPI StringTableDuplicate(HSTRING_TABLE hTable)
216 struct stringtable *src = (struct stringtable*)hTable, *dest;
218 TRACE("%p\n", src);
220 if (!src)
221 return NULL;
223 dest = MyMalloc(sizeof(*dest));
224 if (!dest)
225 return NULL;
227 *dest = *src;
228 dest->data = MyMalloc(src->allocated);
229 if (!dest->data) {
230 MyFree(dest);
231 return NULL;
234 memcpy(dest->data, src->data, src->allocated);
235 return (HSTRING_TABLE)dest;
238 /**************************************************************************
239 * StringTableGetExtraData [SETUPAPI.@]
241 * Retrieves extra data from a given string table entry.
243 * PARAMS
244 * hTable [I] Handle to the string table
245 * id [I] String ID
246 * extra [I] Pointer a buffer that receives the extra data
247 * extra_size [I] Size of the buffer
249 * RETURNS
250 * Success: TRUE
251 * Failure: FALSE
253 BOOL WINAPI StringTableGetExtraData(HSTRING_TABLE hTable, ULONG id, void *extra, ULONG extra_size)
255 struct stringtable *table = (struct stringtable*)hTable;
256 char *extraptr;
258 TRACE("%p %u %p %u\n", table, id, extra, extra_size);
260 if (!table)
261 return FALSE;
263 if (!is_valid_string_id(table, id))
264 return FALSE;
266 if (table->max_extra_size > extra_size)
268 ERR("data size is too large\n");
269 return FALSE;
272 extraptr = get_extradata_ptr(table, id);
273 memcpy(extra, extraptr, extra_size);
274 return TRUE;
277 /**************************************************************************
278 * StringTableLookUpStringEx [SETUPAPI.@]
280 * Searches a string table and extra data for a given string.
282 * PARAMS
283 * hTable [I] Handle to the string table
284 * string [I] String to be searched for
285 * flags [I] Flags
286 * 1: case sensitive compare
287 * extra [O] Pointer to the buffer that receives the extra data
288 * extra_size [I/O] Unused
290 * RETURNS
291 * Success: String ID
292 * Failure: -1
294 DWORD WINAPI StringTableLookUpStringEx(HSTRING_TABLE hTable, LPWSTR string, DWORD flags,
295 void *extra, ULONG extra_size)
297 struct stringtable *table = (struct stringtable*)hTable;
298 BOOL case_sensitive = flags & 1;
299 struct stringentry *entry;
300 DWORD offset;
301 int cmp;
303 TRACE("%p->%p %s %x %p, %x\n", table, table->data, debugstr_w(string), flags, extra, extra_size);
305 if (!table)
306 return -1;
308 /* get corresponding offset */
309 offset = *get_bucket_ptr(table, string, case_sensitive);
310 if (offset == -1)
311 return -1;
313 /* now we're at correct bucket, do linear search for string */
314 while (1) {
315 entry = (struct stringentry*)(table->data + offset);
316 if (case_sensitive)
317 cmp = wcscmp(entry->data, string);
318 else
319 cmp = lstrcmpiW(entry->data, string);
320 if (!cmp) {
321 if (extra)
322 memcpy(extra, get_extradata_ptr(table, offset), extra_size);
323 return offset;
326 /* last entry */
327 if (entry->nextoffset == -1)
328 return -1;
330 offset = entry->nextoffset;
331 if (offset > table->allocated)
332 return -1;
336 /**************************************************************************
337 * StringTableLookUpString [SETUPAPI.@]
339 * Searches a string table for a given string.
341 * PARAMS
342 * hTable [I] Handle to the string table
343 * string [I] String to be searched for
344 * flags [I] Flags
345 * 1: case sensitive compare
347 * RETURNS
348 * Success: String ID
349 * Failure: -1
351 DWORD WINAPI StringTableLookUpString(HSTRING_TABLE hTable, LPWSTR string, DWORD flags)
353 return StringTableLookUpStringEx(hTable, string, flags, NULL, 0);
356 /**************************************************************************
357 * StringTableAddStringEx [SETUPAPI.@]
359 * Adds a new string plus extra data to the string table.
361 * PARAMS
362 * hTable [I] Handle to the string table
363 * string [I] String to be added to the string table
364 * flags [I] Flags
365 * 1: case sensitive compare
366 * extra [I] Pointer to the extra data
367 * extra_size [I] Size of the extra data
369 * RETURNS
370 * Success: String ID
371 * Failure: -1
373 * NOTES
374 * If the given string already exists in the string table it will not
375 * be added again. The ID of the existing string will be returned in
376 * this case.
378 DWORD WINAPI StringTableAddStringEx(HSTRING_TABLE hTable, LPWSTR string,
379 DWORD flags, void *extra, DWORD extra_size)
381 struct stringtable *table = (struct stringtable*)hTable;
382 BOOL case_sensitive = flags & 1;
383 struct stringentry *entry;
384 DWORD id, *offset;
385 WCHAR *ptrW;
386 int len;
388 TRACE("%p %s %x %p, %u\n", hTable, debugstr_w(string), flags, extra, extra_size);
390 if (!table)
391 return -1;
393 id = StringTableLookUpStringEx(hTable, string, flags, NULL, 0);
394 if (id != -1)
395 return id;
397 /* needed space for new record */
398 len = sizeof(DWORD) + (lstrlenW(string)+1)*sizeof(WCHAR) + table->max_extra_size;
399 if (table->nextoffset + len >= table->allocated) {
400 table->allocated <<= 1;
401 table->data = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, table->data, table->allocated);
404 /* hash string */
405 offset = get_bucket_ptr(table, string, case_sensitive);
406 if (*offset == -1)
407 /* bucket used for a very first time */
408 *offset = table->nextoffset;
409 else {
410 entry = (struct stringentry*)(table->data + *offset);
411 /* link existing last entry to newly added */
412 while (entry->nextoffset != -1)
413 entry = (struct stringentry*)(table->data + entry->nextoffset);
414 entry->nextoffset = table->nextoffset;
416 entry = (struct stringentry*)(table->data + table->nextoffset);
417 entry->nextoffset = -1;
418 id = table->nextoffset;
420 /* copy string */
421 ptrW = get_string_ptr(table, id);
422 lstrcpyW(ptrW, string);
423 if (!case_sensitive)
424 wcslwr(ptrW);
426 /* copy extra data */
427 if (extra)
428 memcpy(get_extradata_ptr(table, id), extra, extra_size);
430 table->nextoffset += len;
431 return id;
434 /**************************************************************************
435 * StringTableAddString [SETUPAPI.@]
437 * Adds a new string to the string table.
439 * PARAMS
440 * hTable [I] Handle to the string table
441 * string [I] String to be added to the string table
442 * flags [I] Flags
443 * 1: case sensitive compare
445 * RETURNS
446 * Success: String ID
447 * Failure: -1
449 * NOTES
450 * If the given string already exists in the string table it will not
451 * be added again. The ID of the existing string will be returned in
452 * this case.
454 DWORD WINAPI StringTableAddString(HSTRING_TABLE hTable, LPWSTR string, DWORD flags)
456 return StringTableAddStringEx(hTable, string, flags, NULL, 0);
459 /**************************************************************************
460 * StringTableSetExtraData [SETUPAPI.@]
462 * Sets extra data for a given string table entry.
464 * PARAMS
465 * hTable [I] Handle to the string table
466 * id [I] String ID
467 * extra [I] Pointer to the extra data
468 * extra_size [I] Size of the extra data
470 * RETURNS
471 * Success: TRUE
472 * Failure: FALSE
474 BOOL WINAPI StringTableSetExtraData(HSTRING_TABLE hTable, DWORD id, void *extra, ULONG extra_size)
476 struct stringtable *table = (struct stringtable*)hTable;
477 char *extraptr;
479 TRACE("%p %d %p %u\n", hTable, id, extra, extra_size);
481 if (!table)
482 return FALSE;
484 if (!is_valid_string_id(table, id))
485 return FALSE;
487 if (table->max_extra_size < extra_size)
489 ERR("data size is too large\n");
490 return FALSE;
493 extraptr = get_extradata_ptr(table, id);
494 memset(extraptr, 0, table->max_extra_size);
495 memcpy(extraptr, extra, extra_size);
497 return TRUE;
500 /**************************************************************************
501 * StringTableStringFromId [SETUPAPI.@]
503 * Returns a pointer to a string for the given string ID.
505 * PARAMS
506 * hTable [I] Handle to the string table.
507 * id [I] String ID
509 * RETURNS
510 * Success: Pointer to the string
511 * Failure: NULL
513 LPWSTR WINAPI StringTableStringFromId(HSTRING_TABLE hTable, ULONG id)
515 struct stringtable *table = (struct stringtable*)hTable;
516 static WCHAR empty[] = {0};
518 TRACE("%p %d\n", table, id);
520 if (!table)
521 return NULL;
523 if (!is_valid_string_id(table, id))
524 return empty;
526 return get_string_ptr(table, id);
529 /**************************************************************************
530 * StringTableStringFromIdEx [SETUPAPI.@]
532 * Returns a string for the given string ID.
534 * PARAMS
535 * hTable [I] Handle to the string table
536 * id [I] String ID
537 * buff [I] Pointer to string buffer
538 * buflen [I/O] Pointer to the size of the string buffer
540 * RETURNS
541 * Success: TRUE
542 * Failure: FALSE
544 BOOL WINAPI StringTableStringFromIdEx(HSTRING_TABLE hTable, ULONG id, LPWSTR buff, DWORD *buflen)
546 struct stringtable *table = (struct stringtable*)hTable;
547 BOOL ret = TRUE;
548 WCHAR *ptrW;
549 int len;
551 TRACE("%p %x %p %p\n", table, id, buff, buflen);
553 if (!table) {
554 *buflen = 0;
555 return FALSE;
558 if (!is_valid_string_id(table, id)) {
559 WARN("invalid string id\n");
560 *buflen = 0;
561 return FALSE;
564 ptrW = get_string_ptr(table, id);
565 len = (lstrlenW(ptrW) + 1)*sizeof(WCHAR);
566 if (len <= *buflen)
567 lstrcpyW(buff, ptrW);
568 else
569 ret = FALSE;
571 *buflen = len;
572 return ret;
575 /**************************************************************************
576 * StringTableTrim [SETUPAPI.@]
578 * ...
580 * PARAMS
581 * hTable [I] Handle to the string table
583 * RETURNS
584 * None
586 void WINAPI StringTableTrim(HSTRING_TABLE hTable)
588 FIXME("%p\n", hTable);