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