1 /*
2  * PROJECT:     ReactOS Application compatibility module
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Shim database string table builder
5  * COPYRIGHT:   Copyright 2016-2019 Mark Jansen (mark.jansen@reactos.org)
6  */
7 
8 #if !defined(SDBWRITE_HOSTTOOL)
9 #define WIN32_NO_STATUS
10 #include "windows.h"
11 #include <appcompat/sdbtypes.h>
12 #include "sdbpapi.h"
13 #else /* !defined(SDBWRITE_HOSTTOOL) */
14 #include <typedefs.h>
15 #include <guiddef.h>
16 #include "sdbtypes.h"
17 #include "sdbpapi.h"
18 #endif /* !defined(SDBWRITE_HOSTTOOL) */
19 
20 #include "sdbstringtable.h"
21 
22 #if !defined(offsetof)
23 #if defined(__GNUC__)
24 #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
25 #else
26 #define offsetof(TYPE, MEMBER) ((size_t)&(((TYPE *)0)->MEMBER))
27 #endif
28 #endif // !defined(offsetof)
29 
30 #define DEFAULT_TABLE_SIZE    0x100
31 
32 typedef struct SdbHashEntry
33 {
34     struct SdbHashEntry* Next;
35     TAGID Tagid;
36     WCHAR Name[1];
37 } SdbHashEntry;
38 
39 struct SdbStringHashTable
40 {
41     DWORD Size;
42     struct SdbHashEntry** Entries;
43 };
44 
45 
46 static struct SdbStringHashTable* HashCreate(void)
47 {
48     struct SdbStringHashTable* tab = SdbAlloc(sizeof(*tab));
49     if (!tab)
50     {
51         SHIM_ERR("Failed to allocate 8 bytes.\r\n");
52         return tab;
53     }
54     tab->Size = DEFAULT_TABLE_SIZE;
55     tab->Entries = SdbAlloc(tab->Size * sizeof(*tab->Entries));
56     return tab;
57 }
58 
59 
60 void SdbpTableDestroy(struct SdbStringHashTable** pTable)
61 {
62     struct SdbStringHashTable* table = *pTable;
63     struct SdbHashEntry* entry, *next;
64     DWORD n, depth = 0, once = 1;
65 
66     *pTable = NULL;
67     for (n = 0; n < table->Size; ++n)
68     {
69         depth = 0;
70         entry = next = table->Entries[n];
71         while (entry)
72         {
73             next = entry->Next;
74             SdbFree(entry);
75             entry = next;
76             depth++;
77         }
78         if (once && depth > 3)
79         {
80             // warn
81             once = 0;
82         }
83     }
84     SdbFree(table->Entries);
85     SdbFree(table);
86 }
87 
88 /* Based on RtlHashUnicodeString */
89 static DWORD StringHash(const WCHAR* str)
90 {
91     DWORD hash = 0;
92     for (; *str; str++)
93     {
94         hash = ((65599 * hash) + (ULONG)(*str));
95     }
96     return hash;
97 }
98 
99 int Sdbwcscmp(const WCHAR* s1, const WCHAR* s2)
100 {
101     while (*s1 == *s2)
102     {
103         if (*s1 == 0)
104             return 0;
105         s1++;
106         s2++;
107     }
108     return *s1 - *s2;
109 }
110 
111 
112 // implementation taken from reactos/sdk/lib/crt/string/wcs.c
113 INT Sdbwcscpy(WCHAR* wcDest, size_t numElement, const WCHAR *wcSrc)
114 {
115     size_t size = 0;
116     if(!wcDest || !numElement)
117         return 22;  /* EINVAL */
118 
119     wcDest[0] = 0;
120 
121     if(!wcSrc)
122         return 22;  /* EINVAL */
123 
124     size = SdbpStrlen(wcSrc) + 1;
125 
126     if(size > numElement)
127         return 34;  /* ERANGE */
128 
129     memcpy(wcDest, wcSrc, size * sizeof(WCHAR));
130 
131     return 0;
132 }
133 
134 static struct SdbHashEntry** TableFindPtr(struct SdbStringHashTable* table, const WCHAR* str)
135 {
136     DWORD hash = StringHash(str);
137     struct SdbHashEntry** entry = &table->Entries[hash % table->Size];
138     while (*entry)
139     {
140         if (!Sdbwcscmp((*entry)->Name, str))
141             return entry;
142         entry = &(*entry)->Next;
143     }
144     return entry;
145 }
146 
147 static BOOL HashAddString(struct SdbStringHashTable* table, struct SdbHashEntry** position, const WCHAR* str, TAGID tagid)
148 {
149     struct SdbHashEntry* entry;
150     SIZE_T size, len;
151 
152     if (!position)
153         position = TableFindPtr(table, str);
154 
155     len = SdbpStrlen(str) + 1;
156     size = offsetof(struct SdbHashEntry, Name[len]);
157     entry = (*position) = SdbAlloc(size);
158     if (!entry)
159     {
160         SHIM_ERR("Failed to allocate %u bytes.\n", size);
161         return FALSE;
162     }
163     entry->Tagid = tagid;
164     Sdbwcscpy(entry->Name, len, str);
165     return TRUE;
166 }
167 
168 
169 BOOL SdbpAddStringToTable(struct SdbStringHashTable** table, const WCHAR* str, TAGID* tagid)
170 {
171     struct SdbHashEntry** entry;
172 
173     if (!*table)
174     {
175         *table = HashCreate();
176         if (!*table)
177         {
178             SHIM_ERR("Error creating hash table\n");
179             return FALSE;
180         }
181     }
182 
183     entry = TableFindPtr(*table, str);
184     if (*entry)
185     {
186         *tagid = (*entry)->Tagid;
187         return FALSE;
188     }
189     return HashAddString(*table, entry, str, *tagid);
190 }
191 
192