windows-nt/Source/XPSP1/NT/base/pnp/setupapi/sputils/strtab.c
2020-09-26 16:20:57 +08:00

2418 lines
67 KiB
C

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
strtab.c
Abstract:
String table functions for Windows NT Setup API dll
A string table is a block of memory that contains a bunch of strings.
Hashing is used, and each hash table entry points to a linked list
of strings within the string table. Strings within each linked list
are sorted in ascending order. A node in the linked list consists of
a pointer to the next node, followed by the string itself. Nodes
are manually aligned to start on DWORD boundaries so we don't have to
resort to using unaligned pointers.
Author:
Ted Miller (tedm) Jan-11-1995
Revision History:
Jamie Hunter (JamieHun) Jan-15-1997 fixed minor bug regarding use of STRTAB_NEW_EXTRADATA
Jamie Hunter (JamieHun) Feb-8-2000 improved string table growth algorithm
Jamie Hunter (JamieHun) Jun-27-2000 moved to sputils static library
--*/
#include "precomp.h"
#pragma hdrstop
//
// Values used for the initial and growth size
// of the string table data area
//
// We start out with 6K, but remember that this includes the hash buckets.
// After you subtract their part of the buffer, you're left with ~4K bytes.
// STRING_TABLE_NEW_SIZE_ADJUST - determines approximate increase
// STRING_TABLE_NEW_SIZE - will increase oldsize by at least STRING_TABLE_GROWTH_SIZE
// and a multiple of STRING_TABLE_GROWTH_SIZE
// if string table gets very big, we limit growth to STRING_TABLE_GROWTH_CAP bytes.
//
#define STRING_TABLE_INITIAL_SIZE 6144
#define STRING_TABLE_GROWTH_SIZE 2048
#define STRING_TABLE_GROWTH_CAP 0x100000
#define STRING_TABLE_NEW_SIZE_ADJUST(oldsize) ((oldsize)/3*2)
#define STRING_TABLE_NEW_SIZE(oldsize) \
(oldsize+min((((DWORD)(STRING_TABLE_NEW_SIZE_ADJUST(oldsize)/STRING_TABLE_GROWTH_SIZE)+1)*STRING_TABLE_GROWTH_SIZE),STRING_TABLE_GROWTH_CAP))
//
// WARNING:
//
// Don't change this structure, various file formats depend upon it
//
#include "pshpack1.h"
#ifdef SPUTILSW
//
// name mangling so the names don't conflict with any in sputilsa.lib
//
#define _STRING_NODE _STRING_NODE_W
#define STRING_NODE STRING_NODE_W
#define PSTRING_NODE PSTRING_NODE_W
#define _STRING_TABLE _STRING_TABLE_W
#define STRING_TABLE STRING_TABLE_W
#define PSTRING_TABLE PSTRING_TABLE_W
#endif // SPUTILSW
typedef struct _STRING_NODE {
//
// This is stored as an offset instead of a pointer
// because the table can move as it's built
// The offset is from the beginning of the table
//
LONG NextOffset;
//
// This field must be last
//
TCHAR String[ANYSIZE_ARRAY];
} STRING_NODE, *PSTRING_NODE;
#include "poppack.h"
//
// in-memory details about the string table
//
typedef struct _STRING_TABLE {
PUCHAR Data; // First HASH_BUCKET_COUNT DWORDS are StringNodeOffset array.
DWORD DataSize;
DWORD BufferSize;
MYLOCK Lock;
UINT ExtraDataSize;
LCID Locale;
} STRING_TABLE, *PSTRING_TABLE;
#define LockTable(table) BeginSynchronizedAccess(&((table)->Lock))
#define UnlockTable(table) EndSynchronizedAccess(&((table)->Lock))
#ifdef UNICODE
#define FixedCompareString CompareString
#else
#include <locale.h>
#include <mbctype.h>
static
INT
FixedCompareString (
IN LCID Locale,
IN DWORD Flags,
IN PCSTR FirstString,
IN INT Count1,
IN PCSTR SecondString,
IN INT Count2
)
{
LCID OldLocale;
INT Result = 0;
//
// This routine uses the C runtime to compare the strings, because
// the Win32 APIs are broken on some versions of Win95
//
OldLocale = GetThreadLocale();
if (OldLocale != Locale) {
SetThreadLocale (Locale);
setlocale(LC_ALL,"");
}
__try {
if (Count1 == -1) {
Count1 = strlen (FirstString);
}
if (Count2 == -1) {
Count2 = strlen (SecondString);
}
//
// The C runtime compares strings differently than the CompareString
// API. Most importantly, the C runtime considers uppercase to be
// less than lowercase; the CompareString API is the opposite.
//
if (Flags & NORM_IGNORECASE) {
Result = _mbsnbicmp (FirstString, SecondString, min (Count1, Count2));
} else {
Result = _mbsnbcmp (FirstString, SecondString, min (Count1, Count2));
}
//
// We now convert the C runtime result into the CompareString result.
// This means making the comparison a Z to A ordering, with lowercase
// coming before uppercase. The length comparison does not get reversed.
//
if(Result == _NLSCMPERROR) {
Result = 0; // zero returned if _mbsnbicmp could not compare
} else if (Result < 0) {
Result = CSTR_GREATER_THAN;
} else if (Result == 0) {
if (Count1 < Count2) {
Result = CSTR_LESS_THAN; // first string shorter than second
} else if (Count1 > Count2) {
Result = CSTR_GREATER_THAN; // first string longer than second
} else {
Result = CSTR_EQUAL;
}
} else {
Result = CSTR_LESS_THAN;
}
}
__except (TRUE) {
Result = 0;
}
if (OldLocale != Locale) {
SetThreadLocale (OldLocale);
setlocale(LC_ALL,"");
}
return Result;
}
#endif
static
DWORD
_StringTableCheckFlags(
IN DWORD FlagsIn
)
/*++
Routine Description:
Pre-process flags, called by exported routines we want to handle the
combination of CASE_INSENSITIVE, CASE_SENSITIVE and BUFFER_WRITEABLE
and keep all other flags as is.
Arguments:
FlagsIn - flags as supplied
Return Value:
Flags out
--*/
{
DWORD FlagsOut;
DWORD FlagsSpecial;
//
// we're just interested in these flags for the switch
//
FlagsSpecial = FlagsIn & (STRTAB_CASE_SENSITIVE | STRTAB_BUFFER_WRITEABLE);
//
// strip these off FlagsIn to create initial FlagsOut
//
FlagsOut = FlagsIn ^ FlagsSpecial;
switch (FlagsSpecial) {
case STRTAB_CASE_INSENSITIVE :
case STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE :
//
// these cases ok
//
FlagsOut |= FlagsSpecial;
break;
default :
//
// any other combination is treated as STRTAB_CASE_SENSITIVE (and so
// WRITEABLE doesn't matter)
//
FlagsOut |= STRTAB_CASE_SENSITIVE;
}
return FlagsOut;
}
static
VOID
_ComputeHashValue(
IN PTSTR String,
OUT PDWORD StringLength,
IN DWORD Flags,
OUT PDWORD HashValue
)
/*++
Routine Description:
Compute a hash value for a given string.
The algorithm simply adds up the unicode values for each
character in the string and then takes the result mod the
number of hash buckets.
Arguments:
String - supplies the string for which a hash value is desired.
StringLength - receives the number of characters in the string,
not including the terminating nul.
Flags - supplies flags controlling how the hashing is to be done. May be
a combination of the following values (all other bits ignored):
STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
the string look-up. Specifying this flag improves the
performance of this API for case-insensitive string
additions. This flag is ignored for case-sensitive
string additions.
STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
all lower-case (e.g., by calling CharLower), and
therefore doesn't need to be lower-cased in the
hashing routine. If this flag is supplied, then
STRTAB_BUFFER_WRITEABLE is ignored, since modifying
the caller's buffer is not required.
HashValue - receives the hash value.
Return Value:
None.
--*/
{
DWORD Length;
DWORD Value = 0;
PCTSTR p, q;
DWORD Char;
try {
if((Flags & (STRTAB_BUFFER_WRITEABLE | STRTAB_ALREADY_LOWERCASE)) == STRTAB_BUFFER_WRITEABLE) {
//
// Then the buffer is writeable, but isn't yet lower-case. Take care of that right now.
//
#ifndef UNICODE
_mbslwr (String);
#else
CharLower(String);
#endif
Flags |= STRTAB_ALREADY_LOWERCASE;
}
//
// Define a macro to ensure we don't get sign-extension when adding up character values.
//
#ifdef UNICODE
#define DWORD_FROM_TCHAR(x) ((DWORD)((WCHAR)(x)))
#else
#define DWORD_FROM_TCHAR(x) ((DWORD)((UCHAR)(x)))
#endif
p = String;
if(Flags & STRTAB_ALREADY_LOWERCASE) {
while (*p) {
Value += DWORD_FROM_TCHAR (*p);
p++;
}
} else {
//
// Make sure we don't get sign-extension on extended chars
// in String -- otherwise we get values like 0xffffffe4 passed
// to CharLower(), which thinks it's a pointer and faults.
//
#ifdef UNICODE
//
// The WCHAR case is trivial
//
while (*p) {
Value += DWORD_FROM_TCHAR(CharLower((PWSTR)(WORD) (*p)));
p++;
}
#else
//
// The DBCS case is a mess because of the possibility of CharLower
// altering a two-byte character
// Standardize to use _mbslwr as that is used elsewhere
// ie, if we did _mbslwr, & called this function with
// flag set to say "already lower", vs we called function
// with buffer writable, vs calling with neither
// we should ensure we get same hash in each case
// it may fail, but at least it will fail *universally* and
// generate the same hash
//
PTSTR copy = pSetupDuplicateString(String);
if(copy) {
//
// do conversion on copied string
//
_mbslwr(copy);
p = copy;
while (*p) {
Value += DWORD_FROM_TCHAR (*p);
p++;
}
pSetupFree(copy);
p = String+lstrlen(String);
} else {
//
// we had a memory failure
//
*HashValue = 0;
*StringLength = 0;
leave;
}
#endif
}
*HashValue = Value % HASH_BUCKET_COUNT;
*StringLength = (DWORD)(p - String);
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// Inbound string was bogus
//
*HashValue = 0;
*StringLength = 0;
MYASSERT(FALSE);
}
}
BOOL
_pSpUtilsStringTableLock(
IN PVOID StringTable
)
/*++
Routine Description:
This routine acquires the lock for the specified string table (it's
implemented as a function call for uses in setupapi where locking needs to
be explicitly controlled).
Arguments:
StringTable - supplies handle to string table to be locked.
Return Value:
If the lock was successfully acquired, the return value is TRUE.
Otherwise, the return value is FALSE.
--*/
{
return LockTable((PSTRING_TABLE)StringTable);
}
VOID
_pSpUtilsStringTableUnlock(
IN PVOID StringTable
)
/*++
Routine Description:
This routine releases the lock (previously acquired via
_pSpUtilsStringTableLock) for the specified string table.
Arguments:
StringTable - supplies handle to string table to be unlocked.
Return Value:
None.
--*/
{
UnlockTable((PSTRING_TABLE)StringTable);
}
LONG
_pSpUtilsStringTableLookUpString(
IN PVOID StringTable,
IN OUT PTSTR String,
OUT PDWORD StringLength,
OUT PDWORD HashValue, OPTIONAL
OUT PVOID *FindContext, OPTIONAL
IN DWORD Flags,
OUT PVOID ExtraData, OPTIONAL
IN UINT ExtraDataBufferSize OPTIONAL
)
/*++
Routine Description:
Locates a string in the string table, if present.
If the string is not present, this routine may optionally tell its
caller where the search stopped. This is useful for maintaining a
sorted order for the strings.
Arguments:
StringTable - supplies handle to string table to be searched
for the string
String - supplies the string to be looked up
StringLength - receives number of characters in the string, not
including the terminating nul.
HashValue - Optionally, receives hash value for the string.
FindContext - Optionally, receives the context at which the search was
terminated.
(NOTE: This is actually a PSTRING_NODE pointer, that is used
during new string addition. Since this routine has wider exposure
than just internal string table usage, this parameter is made into
a PVOID, so no one else has to have access to string table-internal
structures.
On return, this variable receives a pointer to the string node of
the node where the search stopped. If the string was found, then
this is a pointer to the string's node. If the string was not found,
then this is a pointer to the last string node whose string is
'less' (based on lstrcmpi) than the string we're looking for.
Note that this value may be NULL.)
Flags - supplies flags controlling how the string is to be located. May be
a combination of the following values:
STRTAB_CASE_INSENSITIVE - Search for the string case-insensitively.
STRTAB_CASE_SENSITIVE - Search for the string case-sensitively. This flag
overrides the STRTAB_CASE_INSENSITIVE flag.
STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
the string look-up. Specifying this flag improves the
performance of this API for case-insensitive string
additions. This flag is ignored for case-sensitive
string additions.
In addition to the above public flags, the following private flag is also
allowed:
STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
all lower-case (e.g., by calling CharLower), and
therefore doesn't need to be lower-cased in the
hashing routine. If this flag is supplied, then
STRTAB_BUFFER_WRITEABLE is ignored, since modifying
the caller's buffer is not required.
ExtraData - if specified, receives extra data associated with the string
if the string is found.
ExtraDataBufferSize - if ExtraData is specified, then this parameter
specifies the size of the buffer, in bytes. As much extra data as will fit
is stored here.
Return Value:
The return value is a value that uniquely identifies the string
within the string table, namely the offset of the string node
within the string table.
If the string could not be found the value is -1.
--*/
{
PSTRING_NODE node,prev;
int i;
PSTRING_TABLE stringTable = StringTable;
DWORD hashValue;
PSTRING_NODE FinalNode;
LONG rc = -1;
LCID Locale;
DWORD CompareFlags;
BOOL CollateEnded = FALSE;
//
// If this is a case-sensitive lookup, then we want to reset the STRTAB_BUFFER_WRITEABLE
// flag, if present, since otherwise the string will get replaced with its all-lowercase
// counterpart.
//
if(Flags & STRTAB_CASE_SENSITIVE) {
Flags &= ~STRTAB_BUFFER_WRITEABLE;
}
//
// Compute hash value
//
_ComputeHashValue(String,StringLength,Flags,&hashValue);
if(((PLONG)(stringTable->Data))[hashValue] == -1) {
//
// The string table contains no strings at the computed hash value.
//
FinalNode = NULL;
goto clean0;
}
//
// We know there's at least one string in the table with the computed
// hash value, so go find it. There's no previous node yet.
//
node = (PSTRING_NODE)(stringTable->Data + ((PLONG)(stringTable->Data))[hashValue]);
prev = NULL;
//
// Go looking through the string nodes for that hash value,
// looking through the string.
//
Locale = stringTable->Locale;
CompareFlags = (Flags & STRTAB_CASE_SENSITIVE) ? 0 : NORM_IGNORECASE;
while(1) {
if(i = FixedCompareString(Locale,CompareFlags,String,-1,node->String,-1)) {
i -= 2;
} else {
//
// Failure, try system default locale
//
if(i = FixedCompareString(LOCALE_SYSTEM_DEFAULT,CompareFlags,String,-1,node->String,-1)) {
i -= 2;
} else {
//
// Failure, just use CRTs
//
// This could give wrong collation?? If it does, we're stuck with it now.
//
i = (Flags & STRTAB_CASE_SENSITIVE)
? _tcscmp(String,node->String)
: _tcsicmp(String,node->String);
}
}
if(i == 0) {
FinalNode = node;
rc = (LONG)((PUCHAR)node - stringTable->Data);
break;
}
//
// If the string we are looking for is 'less' than the current
// string, mark it's position so we can insert a new string before here
// (ANSI) but keep searching (UNICODE) we can abort - old behaviour
//
if((i < 0) && !CollateEnded) {
CollateEnded = TRUE;
FinalNode = prev;
#if UNICODE
break;
#endif
}
//
// The string we are looking for is 'greater' than the current string.
// Keep looking, unless we've reached the end of the table.
//
if(node->NextOffset == -1) {
if(!CollateEnded)
{
//
// unless we found a more ideal position
// return the end of the list
//
FinalNode = node;
}
break;
} else {
prev = node;
node = (PSTRING_NODE)(stringTable->Data + node->NextOffset);
}
}
clean0:
if((rc != -1) && ExtraData) {
//
// Extra data is stored immediately following the string.
//
CopyMemory(
ExtraData,
FinalNode->String + *StringLength + 1,
min(ExtraDataBufferSize,stringTable->ExtraDataSize)
);
}
if(HashValue) {
*HashValue = hashValue;
}
if(FindContext) {
*FindContext = FinalNode;
}
return rc;
}
LONG
pSetupStringTableLookUpString(
IN PVOID StringTable,
IN OUT PTSTR String,
IN DWORD Flags
)
/*++
Routine Description:
Locates a string in the string table, if present.
Arguments:
StringTable - supplies handle to string table to be searched
for the string
String - supplies the string to be looked up. If STRTAB_BUFFER_WRITEABLE is
specified and a case-insensitive lookup is requested, then this buffer
will be all lower-case upon return.
Flags - supplies flags controlling how the string is to be located. May be
a combination of the following values:
STRTAB_CASE_INSENSITIVE - Search for the string case-insensitively.
STRTAB_CASE_SENSITIVE - Search for the string case-sensitively. This flag
overrides the STRTAB_CASE_INSENSITIVE flag.
STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
the string look-up. Specifying this flag improves the
performance of this API for case-insensitive string
additions. This flag is ignored for case-sensitive
string additions.
In addition to the above public flags, the following private flag is also
allowed:
STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
all lower-case (e.g., by calling CharLower), and
therefore doesn't need to be lower-cased in the
hashing routine. If this flag is supplied, then
STRTAB_BUFFER_WRITEABLE is ignored, since modifying
the caller's buffer is not required.
Return Value:
The return value is a value that uniquely identifies the string
within the string table.
If the string could not be found the value is -1.
--*/
{
DWORD StringLength, PrivateFlags, AlreadyLcFlag;
LONG rc = -1;
BOOL locked = FALSE;
try {
if (!LockTable((PSTRING_TABLE)StringTable)) {
leave;
}
locked = TRUE;
PrivateFlags = _StringTableCheckFlags(Flags);
rc = _pSpUtilsStringTableLookUpString(
StringTable,
String,
&StringLength,
NULL,
NULL,
PrivateFlags,
NULL,
0
);
} except (EXCEPTION_EXECUTE_HANDLER) {
rc = -1;
}
if (locked) {
UnlockTable((PSTRING_TABLE)StringTable);
}
return (rc);
}
LONG
pSetupStringTableLookUpStringEx(
IN PVOID StringTable,
IN OUT PTSTR String,
IN DWORD Flags,
OUT PVOID ExtraData, OPTIONAL
IN UINT ExtraDataBufferSize OPTIONAL
)
/*++
Routine Description:
Locates a string in the string table, if present.
Arguments:
StringTable - supplies handle to string table to be searched
for the string
String - supplies the string to be looked up. If STRTAB_BUFFER_WRITEABLE is
specified and a case-insensitive lookup is requested, then this buffer
will be all lower-case upon return.
Flags - supplies flags controlling how the string is to be located. May be
a combination of the following values:
STRTAB_CASE_INSENSITIVE - Search for the string case-insensitively.
STRTAB_CASE_SENSITIVE - Search for the string case-sensitively. This flag
overrides the STRTAB_CASE_INSENSITIVE flag.
STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
the string look-up. Specifying this flag improves the
performance of this API for case-insensitive string
additions. This flag is ignored for case-sensitive
string additions.
In addition to the above public flags, the following private flag is also
allowed:
STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
all lower-case (e.g., by calling CharLower), and
therefore doesn't need to be lower-cased in the
hashing routine. If this flag is supplied, then
STRTAB_BUFFER_WRITEABLE is ignored, since modifying
the caller's buffer is not required.
ExtraData - if specified, receives extra data associated with the string
if the string is found.
ExtraDataBufferSize - if ExtraData is specified, then this parameter
specifies the size of the buffer, in bytes. As much extra data as will fit
is stored here.
Return Value:
The return value is a value that uniquely identifies the string
within the string table.
If the string could not be found the value is -1.
--*/
{
DWORD StringLength, PrivateFlags, AlreadyLcFlag;
LONG rc = -1;
BOOL locked = FALSE;
try {
if(!LockTable((PSTRING_TABLE)StringTable)) {
leave;
}
locked = TRUE;
PrivateFlags = _StringTableCheckFlags(Flags);
rc = _pSpUtilsStringTableLookUpString(
StringTable,
String,
&StringLength,
NULL,
NULL,
PrivateFlags,
ExtraData,
ExtraDataBufferSize
);
} except (EXCEPTION_EXECUTE_HANDLER) {
rc = -1;
}
if (locked) {
UnlockTable((PSTRING_TABLE)StringTable);
}
return (rc);
}
BOOL
pSetupStringTableGetExtraData(
IN PVOID StringTable,
IN LONG StringId,
OUT PVOID ExtraData,
IN UINT ExtraDataBufferSize
)
/*++
Routine Description:
Get arbitrary data associated with a string table entry.
Arguments:
StringTable - supplies handle to string table containing the string
whose associated data is to be returned.
String - supplies the id of the string whose associated data is to be returned.
ExtraData - receives the data associated with the string. Data is truncated
to fit, if necessary.
ExtraDataBufferSize - supplies the size in bytes of the buffer specified
by ExtraData. If this value is smaller than the extra data size for
the string table, data is truncated to fit.
Return Value:
Boolean value indicating outcome.
--*/
{
BOOL b = FALSE;
BOOL locked = FALSE;
try {
if(!LockTable((PSTRING_TABLE)StringTable)) {
leave;
}
locked = TRUE;
b = _pSpUtilsStringTableGetExtraData(StringTable,StringId,ExtraData,ExtraDataBufferSize);
} except (EXCEPTION_EXECUTE_HANDLER) {
b = FALSE;
}
if (locked) {
UnlockTable((PSTRING_TABLE)StringTable);
}
return(b);
}
BOOL
_pSpUtilsStringTableGetExtraData(
IN PVOID StringTable,
IN LONG StringId,
OUT PVOID ExtraData,
IN UINT ExtraDataBufferSize
)
/*++
Routine Description:
Get arbitrary data associated with a string table entry.
THIS ROUTINE DOES NOT DO LOCKING and IT DOES NOT HANDLE EXCEPTIONS!
Arguments:
StringTable - supplies handle to string table containing the string
whose associated data is to be returned.
String - supplies the id of the string whose associated data is to be returned.
ExtraData - receives the data associated with the string. Data is truncated
to fit, if necessary.
ExtraDataBufferSize - supplies the size in bytes of the buffer specified
by ExtraData. If this value is smaller than the extra data size for
the string table, data is truncated to fit.
Return Value:
Boolean value indicating outcome.
--*/
{
PSTRING_TABLE stringTable = StringTable;
PSTRING_NODE stringNode;
PVOID p;
stringNode = (PSTRING_NODE)(stringTable->Data + StringId);
p = stringNode->String + lstrlen(stringNode->String) + 1;
CopyMemory(ExtraData,p,min(ExtraDataBufferSize,stringTable->ExtraDataSize));
return(TRUE);
}
BOOL
pSetupStringTableSetExtraData(
IN PVOID StringTable,
IN LONG StringId,
IN PVOID ExtraData,
IN UINT ExtraDataSize
)
/*++
Routine Description:
Associate arbitrary data with a string table entry.
Arguments:
StringTable - supplies handle to string table containing the string
with which the data is to be associated.
String - supplies the id of the string with which the data is to be associated.
ExtraData - supplies the data to be associated with the string.
ExtraDataSize - specifies the size in bytes of the data. If the data is
larger than the extra data size for this string table, then the routine fails.
Return Value:
Boolean value indicating outcome.
--*/
{
BOOL b = FALSE;
BOOL locked = FALSE;
try {
if(!LockTable((PSTRING_TABLE)StringTable)) {
leave;
}
locked = TRUE;
b = _pSpUtilsStringTableSetExtraData(StringTable,StringId,ExtraData,ExtraDataSize);
} except (EXCEPTION_EXECUTE_HANDLER) {
b = FALSE;
}
if (locked) {
UnlockTable((PSTRING_TABLE)StringTable);
}
return(b);
}
BOOL
_pSpUtilsStringTableSetExtraData(
IN PVOID StringTable,
IN LONG StringId,
IN PVOID ExtraData,
IN UINT ExtraDataSize
)
/*++
Routine Description:
Associate arbitrary data with a string table entry.
Arguments:
StringTable - supplies handle to string table containing the string
with which the data is to be associated.
String - supplies the id of the string with which the data is to be associated.
ExtraData - supplies the data to be associated with the string.
ExtraDataSize - specifies the size in bytes of the data. If the data is
larger than the extra data size for this string table, then the routine fails.
Return Value:
Boolean value indicating outcome.
--*/
{
PSTRING_TABLE stringTable = StringTable;
PSTRING_NODE stringNode;
BOOL b;
PVOID p;
if(ExtraDataSize <= stringTable->ExtraDataSize) {
stringNode = (PSTRING_NODE)(stringTable->Data + StringId);
p = stringNode->String + lstrlen(stringNode->String) + 1;
ZeroMemory(p,stringTable->ExtraDataSize);
CopyMemory(p,ExtraData,ExtraDataSize);
b = TRUE;
} else {
b = FALSE;
}
return(b);
}
LONG
_pSpUtilsStringTableAddString(
IN PVOID StringTable,
IN OUT PTSTR String,
IN DWORD Flags,
IN PVOID ExtraData, OPTIONAL
IN UINT ExtraDataSize OPTIONAL
)
/*++
Routine Description:
Adds a string to the string table if the string is not already
in the string table. (Does not do locking!)
If the string is to be added case-insensitively, then it is
lower-cased, and added case-sensitively. Since lower-case characters
are 'less than' lower case ones (according to lstrcmp), this ensures that
a case-insensitive string will always appear in front of any of its
case-sensitive counterparts. This ensures that we always find the correct
string ID for things like section names.
Arguments:
StringTable - supplies handle to string table to be searched
for the string
String - supplies the string to be added
Flags - supplies flags controlling how the string is to be added, and
whether the caller-supplied buffer may be modified. May be a combination
of the following values:
STRTAB_CASE_INSENSITIVE - Add the string case-insensitively. The
specified string will be added to the string
table as all lower-case. This flag is overridden
if STRTAB_CASE_SENSITIVE is specified.
STRTAB_CASE_SENSITIVE - Add the string case-sensitively. This flag
overrides the STRTAB_CASE_INSENSITIVE flag.
STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
the string-addition process. Specifying this flag
improves the performance of this API for case-
insensitive string additions. This flag is ignored
for case-sensitive string additions.
STRTAB_NEW_EXTRADATA - if the string already exists in the table
and ExtraData is specified (see below) then
the new ExtraData overwrites any existing extra data.
Otherwise any existing extra data is left alone.
In addition to the above public flags, the following private flag is also
allowed:
STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
all lower-case (e.g., by calling CharLower), and
therefore doesn't need to be lower-cased in the
hashing routine. If this flag is supplied, then
STRTAB_BUFFER_WRITEABLE is ignored, since modifying
the caller's buffer is not required.
ExtraData - if supplied, specifies extra data to be associated with the string
in the string table. If the string already exists in the table, the Flags
field controls whether the new data overwrites existing data already
associated with the string.
ExtraDataSize - if ExtraData is supplied, then this value supplies the size
in bytes of the buffer pointed to by ExtraData. If the data is larger than
the extra data size for the string table, the routine fails.
Return Value:
The return value uniquely identifes the string within the string table.
It is -1 if the string was not in the string table but could not be added
(out of memory).
--*/
{
LONG rc;
PSTRING_TABLE stringTable = StringTable;
DWORD StringLength;
DWORD HashValue;
PSTRING_NODE PreviousNode,NewNode;
DWORD SpaceRequired;
PTSTR TempString = String;
BOOL FreeTempString = FALSE;
PVOID p;
DWORD sz;
if (!(Flags & STRTAB_CASE_SENSITIVE)) {
//
// not case sensitive ( = insensitive)
//
if (!(Flags & STRTAB_ALREADY_LOWERCASE)) {
//
// not already lowercase
//
if (!(Flags & STRTAB_BUFFER_WRITEABLE)) {
//
// not writable
//
//
// Then the string is to be added case-insensitively, but the caller
// doesn't want us to write to their buffer. Allocate one of our own.
//
if (TempString = pSetupDuplicateString(String)) {
FreeTempString = TRUE;
} else {
//
// We couldn't allocate space for our duplicated string. Since we'll
// only consider exact matches (where the strings are all lower-case),
// we're stuck, since we can't lower-case the buffer in place.
//
return -1;
}
}
//
// Lower-case the buffer.
//
#ifndef UNICODE
_mbslwr (TempString);
#else
CharLower(TempString);
#endif
}
//
// we know that the string is now lower-case
// we no longer need "Writable" flag
// searches will be case sensitive
//
Flags &= ~ (STRTAB_BUFFER_WRITEABLE | STRTAB_CASE_INSENSITIVE);
Flags |= STRTAB_CASE_SENSITIVE | STRTAB_ALREADY_LOWERCASE;
}
try {
if (ExtraData && (ExtraDataSize > stringTable->ExtraDataSize)) {
//
// Force us into the exception handler -- sort of a non-local goto.
//
RaiseException(0,0,0,NULL);
}
//
// The string might already be in there.
//
rc = _pSpUtilsStringTableLookUpString(
StringTable,
TempString,
&StringLength,
&HashValue,
&PreviousNode,
Flags,
NULL,
0
);
if (rc != -1) {
if (ExtraData && (Flags & STRTAB_NEW_EXTRADATA)) {
//
// Overwrite extra data. We know the data is small enough to fit
// because we checked for this above.
//
p = PreviousNode->String + StringLength + 1;
ZeroMemory(p,stringTable->ExtraDataSize);
CopyMemory(p,ExtraData,ExtraDataSize);
}
if (FreeTempString) {
pSetupFree(TempString);
}
return (rc);
}
//
// Figure out how much space is required to hold this entry.
// This is the size of a STRING_NODE plus the length of the string
// plus space for extra per-element data.
//
SpaceRequired = offsetof(STRING_NODE,String)
+ ((StringLength+1)*sizeof(TCHAR))
+ stringTable->ExtraDataSize;
//
// Make sure things stay aligned within the table
//
if (SpaceRequired % sizeof(DWORD)) {
SpaceRequired += sizeof(DWORD) - (SpaceRequired % sizeof(DWORD));
}
while(stringTable->DataSize + SpaceRequired > stringTable->BufferSize) {
//
// Grow the string table.
// do this exponentially so that tables with a lot of items
// added won't cause lots of reallocs
//
sz = STRING_TABLE_NEW_SIZE(stringTable->BufferSize);
if (sz < stringTable->BufferSize) {
sz = stringTable->DataSize + SpaceRequired;
}
p = pSetupReallocWithTag(stringTable->Data,sz,MEMTAG_STRINGDATA);
if (!p) {
//
// we've run out of room, this could be because we asked
// for too big of a re-alloc
// if we're in this state, we're probably going to
// have problems later anyway, but for now, let's
// try and proceed with exactly what we need
//
sz = stringTable->DataSize + SpaceRequired;
p = pSetupReallocWithTag(stringTable->Data,sz,MEMTAG_STRINGDATA);
if (!p) {
//
// nope, this didn't help
//
if (FreeTempString) {
pSetupFree(TempString);
}
return (-1);
}
}
//
// Adjust previous node pointer.
//
if (PreviousNode) {
PreviousNode = (PSTRING_NODE)((PUCHAR)p + ((PUCHAR)PreviousNode-(PUCHAR)stringTable->Data));
}
stringTable->Data = p;
stringTable->BufferSize = sz;
}
//
// Stick the string and extra data, if any, in the string table buffer.
//
NewNode = (PSTRING_NODE)(stringTable->Data + stringTable->DataSize);
if (PreviousNode) {
NewNode->NextOffset = PreviousNode->NextOffset;
PreviousNode->NextOffset = (LONG)((LONG_PTR)NewNode - (LONG_PTR)stringTable->Data);
} else {
NewNode->NextOffset = ((PLONG)(stringTable->Data))[HashValue];
((PLONG)(stringTable->Data))[HashValue] = (LONG)((LONG_PTR)NewNode - (LONG_PTR)stringTable->Data);
}
lstrcpy(NewNode->String,TempString);
p = NewNode->String + StringLength + 1;
ZeroMemory(p,stringTable->ExtraDataSize);
if (ExtraData) {
CopyMemory(p,ExtraData,ExtraDataSize);
}
stringTable->DataSize += SpaceRequired;
rc = (LONG)((LONG_PTR)NewNode - (LONG_PTR)stringTable->Data);
}except(EXCEPTION_EXECUTE_HANDLER) {
rc = -1;
}
if (FreeTempString) {
pSetupFree(TempString);
}
return rc;
}
LONG
pSetupStringTableAddString(
IN PVOID StringTable,
IN PTSTR String,
IN DWORD Flags
)
/*++
Routine Description:
Adds a string to the string table if the string is not already
in the string table.
Arguments:
StringTable - supplies handle to string table to be searched
for the string
String - supplies the string to be added
Flags - supplies flags controlling how the string is to be added, and
whether the caller-supplied buffer may be modified. May be a combination
of the following values:
STRTAB_CASE_INSENSITIVE - Add the string case-insensitively. The
specified string will be added to the string
table as all lower-case. This flag is overridden
if STRTAB_CASE_SENSITIVE is specified.
STRTAB_CASE_SENSITIVE - Add the string case-sensitively. This flag
overrides the STRTAB_CASE_INSENSITIVE flag.
STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
the string-addition process. Specifying this flag
improves the performance of this API for case-
insensitive string additions. This flag is ignored
for case-sensitive string additions.
In addition to the above public flags, the following private flag is also
allowed:
STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
all lower-case (e.g., by calling CharLower), and
therefore doesn't need to be lower-cased in the
hashing routine. If this flag is supplied, then
STRTAB_BUFFER_WRITEABLE is ignored, since modifying
the caller's buffer is not required.
Return Value:
The return value uniquely identifes the string within the string table.
It is -1 if the string was not in the string table but could not be added
(out of memory).
--*/
{
LONG rc = -1;
BOOL locked = FALSE;
DWORD PrivateFlags;
try {
if(!LockTable((PSTRING_TABLE)StringTable)) {
leave;
}
locked = TRUE;
PrivateFlags = _StringTableCheckFlags(Flags);
rc = _pSpUtilsStringTableAddString(StringTable, String, PrivateFlags, NULL, 0);
} except (EXCEPTION_EXECUTE_HANDLER) {
rc = -1;
}
if (locked) {
UnlockTable((PSTRING_TABLE)StringTable);
}
return(rc);
}
LONG
pSetupStringTableAddStringEx(
IN PVOID StringTable,
IN PTSTR String,
IN DWORD Flags,
IN PVOID ExtraData, OPTIONAL
IN UINT ExtraDataSize OPTIONAL
)
/*++
Routine Description:
Adds a string to the string table if the string is not already
in the string table.
Arguments:
StringTable - supplies handle to string table to be searched
for the string
String - supplies the string to be added
Flags - supplies flags controlling how the string is to be added, and
whether the caller-supplied buffer may be modified. May be a combination
of the following values:
STRTAB_CASE_INSENSITIVE - Add the string case-insensitively. The
specified string will be added to the string
table as all lower-case. This flag is overridden
if STRTAB_CASE_SENSITIVE is specified.
STRTAB_CASE_SENSITIVE - Add the string case-sensitively. This flag
overrides the STRTAB_CASE_INSENSITIVE flag.
STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
the string-addition process. Specifying this flag
improves the performance of this API for case-
insensitive string additions. This flag is ignored
for case-sensitive string additions.
STRTAB_NEW_EXTRADATA - If the string already exists in the table
and ExtraData is specified (see below) then
the new ExtraData overwrites any existing extra data.
Otherwise any existing extra data is left alone.
In addition to the above public flags, the following private flag is also
allowed:
STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
all lower-case (e.g., by calling CharLower), and
therefore doesn't need to be lower-cased in the
hashing routine. If this flag is supplied, then
STRTAB_BUFFER_WRITEABLE is ignored, since modifying
the caller's buffer is not required.
ExtraData - if supplied, specifies extra data to be associated with the string
in the string table. If the string already exists in the table, the Flags
field controls whether the new data overwrites existing data already
associated with the string.
ExtraDataSize - if ExtraData is supplied, then this value supplies the size
in bytes of the buffer pointed to by ExtraData. If the data is larger than
the extra data size for the string table, the routine fails.
Return Value:
The return value uniquely identifes the string within the string table.
It is -1 if the string was not in the string table but could not be added
(out of memory).
--*/
{
LONG rc = -1;
BOOL locked = FALSE;
DWORD PrivateFlags;
try {
if(!LockTable((PSTRING_TABLE)StringTable)) {
leave;
}
locked = TRUE;
PrivateFlags = _StringTableCheckFlags(Flags);
rc = _pSpUtilsStringTableAddString(StringTable, String, PrivateFlags, ExtraData, ExtraDataSize);
} except (EXCEPTION_EXECUTE_HANDLER) {
rc = -1;
}
if (locked) {
UnlockTable((PSTRING_TABLE)StringTable);
}
return (rc);
}
BOOL
pSetupStringTableEnum(
IN PVOID StringTable,
OUT PVOID ExtraDataBuffer, OPTIONAL
IN UINT ExtraDataBufferSize, OPTIONAL
IN PSTRTAB_ENUM_ROUTINE Callback,
IN LPARAM lParam OPTIONAL
)
/*++
Routine Description:
For every string in a string table, inform a callback routine of
the stirng's id, it's value, and any associated data.
Arguments:
StringTable - supplies a pointer to the string table to be enumerated.
ExtraDataBuffer - supplies the address of a buffer to be passed to
the callback routine for each string, which will be filled in
with the associated data of each string.
ExtraDataBufferSize - if ExtraDataBuffer is specified then this
supplies the size of that buffer in bytes. If this value is
smaller than the size of the extra data for the string table,
the enumeration fails.
Callback - supplies the routine to be notified of each string.
lParam - supplies an optional parameter meaningful to the caller
which is passed on to the callback unchanged.
Return Value:
Boolean value indicating outcome. TRUE unless ExtraDataBufferSize
is too small.
--*/
{
BOOL b = FALSE;
BOOL locked = FALSE;
try {
if(!LockTable((PSTRING_TABLE)StringTable)) {
leave;
}
locked = TRUE;
b = _pSpUtilsStringTableEnum(StringTable,ExtraDataBuffer,ExtraDataBufferSize,Callback,lParam);
} except (EXCEPTION_EXECUTE_HANDLER) {
b = FALSE;
}
if (locked) {
UnlockTable((PSTRING_TABLE)StringTable);
}
return(b);
}
BOOL
_pSpUtilsStringTableEnum(
IN PVOID StringTable,
OUT PVOID ExtraDataBuffer, OPTIONAL
IN UINT ExtraDataBufferSize, OPTIONAL
IN PSTRTAB_ENUM_ROUTINE Callback,
IN LPARAM lParam OPTIONAL
)
/*++
Routine Description:
For every string in a string table, inform a callback routine of
the stirng's id, its value, and any associated data.
THIS ROUTINE DOES NOT DO LOCKING.
Arguments:
StringTable - supplies a pointer to the string table to be enumerated.
ExtraDataBuffer - supplies the address of a buffer to be passed to
the callback routine for each string, which will be filled in
with the associated data of each string.
ExtraDataBufferSize - if ExtraDataBuffer is specified then this
supplies the size of that buffer in bytes. If this value is
smaller than the size of the extra data for the string table,
the enumeration fails.
Callback - supplies the routine to be notified of each string.
lParam - supplies an optional parameter meaningful to the caller
which is passed on to the callback unchanged.
Return Value:
Boolean value indicating outcome. TRUE unless ExtraDataBufferSize
is too small.
--*/
{
UINT u;
PSTRING_TABLE stringTable = StringTable;
PSTRING_NODE stringNode;
LONG FirstOffset;
BOOL b;
//
// Validate buffer size.
//
if(ExtraDataBuffer && (ExtraDataBufferSize < stringTable->ExtraDataSize)) {
return(FALSE);
}
for(b=TRUE,u=0; b && (u<HASH_BUCKET_COUNT); u++) {
FirstOffset = ((PLONG)stringTable->Data)[u];
if(FirstOffset == -1) {
continue;
}
stringNode = (PSTRING_NODE)(stringTable->Data + FirstOffset);
do {
if(ExtraDataBuffer) {
CopyMemory(
ExtraDataBuffer,
stringNode->String + lstrlen(stringNode->String) + 1,
stringTable->ExtraDataSize
);
}
b = Callback(
StringTable,
(LONG)((PUCHAR)stringNode - stringTable->Data),
stringNode->String,
ExtraDataBuffer,
ExtraDataBuffer ? stringTable->ExtraDataSize : 0,
lParam
);
stringNode = (stringNode->NextOffset == -1)
? NULL
: (PSTRING_NODE)(stringTable->Data + stringNode->NextOffset);
} while(b && stringNode);
}
return(TRUE);
}
PTSTR
_pSpUtilsStringTableStringFromId(
IN PVOID StringTable,
IN LONG StringId
)
/*++
Routine Description:
Given a string ID returned when a string was added or looked up,
return a pointer to the actual string. (This is exactly the same
as pSetupStringTableStringFromId, except that it doesn't do locking.)
Arguments:
StringTable - supplies a pointer to the string table containing the
string to be retrieved.
StringId - supplies a string id returned from pSetupStringTableAddString
or pSetupStringTableLookUpString.
Return Value:
Pointer to string data. The caller must not write into or otherwise
alter the string.
--*/
{
return ((PSTRING_NODE)(((PSTRING_TABLE)StringTable)->Data + StringId))->String;
}
PTSTR
pSetupStringTableStringFromId(
IN PVOID StringTable,
IN LONG StringId
)
/*++
Routine Description:
Given a string ID returned when a string was added or looked up,
return a pointer to the actual string.
Arguments:
StringTable - supplies a pointer to the string table containing the
string to be retrieved.
StringId - supplies a string id returned from pSetupStringTableAddString
or pSetupStringTableLookUpString.
Return Value:
Pointer to string data. The caller must not write into or otherwise
alter the string.
This function is fundamentally not thread-safe
since the ptr we return could be modified by another thread
if string table is accessed by more than one thread
Hence new API below pSetupStringTableStringFromIdEx
--*/
{
PTSTR p = NULL;
BOOL locked = FALSE;
try {
if(!LockTable((PSTRING_TABLE)StringTable)) {
leave;
}
locked = TRUE;
p = ((PSTRING_NODE)(((PSTRING_TABLE)StringTable)->Data + StringId))->String;
} except (EXCEPTION_EXECUTE_HANDLER) {
p = NULL;
//
// Reference the following variable so the compiler will respect
// statement ordering w.r.t. its assignment.
//
locked = locked;
}
if (locked) {
UnlockTable((PSTRING_TABLE)StringTable);
}
return(p);
}
BOOL
pSetupStringTableStringFromIdEx(
IN PVOID StringTable,
IN LONG StringId,
IN OUT PTSTR pBuffer,
IN OUT PULONG pBufSize
)
/*++
Routine Description:
Given a string ID returned when a string was added or looked up,
return a pointer to the actual string.
Arguments:
StringTable - supplies a pointer to the string table containing the
string to be retrieved.
StringId - supplies a string id returned from pSetupStringTableAddString
or pSetupStringTableLookUpString.
pBuffer - points to a buffer that will be filled out with the string
to be retrieved
pBufSize - supplies a pointer to an input/output parameter that contains
the size of the buffer on entry, and the number of chars. written on
exit.
Return Value:
TRUE if the string was written. pBufSize contains the length of the string
FALSE if the buffer was invalid, or the string ID was invalid, or the buffer
wasn't big enough. If pBufSize non-zero, then it is the required bufsize.
currently caller can tell the difference between invalid param and buffer
size by checking pBufSize
--*/
{
PTSTR p;
ULONG len;
PSTRING_TABLE stringTable = (PSTRING_TABLE)StringTable;
DWORD status = ERROR_INVALID_DATA;
BOOL locked = FALSE;
try {
if (!pBufSize) {
status = ERROR_INVALID_PARAMETER;
leave;
}
if(!LockTable(stringTable)) {
if (pBuffer != NULL && *pBufSize > 0) {
pBuffer[0]=0;
}
*pBufSize = 0;
status = ERROR_INVALID_HANDLE;
leave;
}
locked = TRUE;
//
// CFGMGR calls this with an ID passed by it's caller
// we have to check bounds here (while table is locked)
// the check has to do:
//
// (1) StringId must be > 0 (0 is hash-bucket 0)
// (2) StringId must be < size of string table
// This should catch common errors but isn't perfect.
//
// the check is here since Id validity requires access to Opaque pointer
//
if(StringId <= 0 || StringId >= (LONG)(stringTable->DataSize)) {
if (pBuffer != NULL && *pBufSize > 0) {
pBuffer[0]=0;
}
*pBufSize = 0;
status = ERROR_INVALID_PARAMETER;
leave;
}
len = lstrlen( ((PSTRING_NODE)(stringTable->Data + StringId))->String);
len ++; // account for terminating NULL
if (len > *pBufSize || pBuffer == NULL) {
if (pBuffer != NULL && *pBufSize > 0) {
pBuffer[0]=0;
}
*pBufSize = len;
status = ERROR_INSUFFICIENT_BUFFER;
leave;
}
lstrcpy (pBuffer,((PSTRING_NODE)(stringTable->Data + StringId))->String);
*pBufSize = len;
status = NO_ERROR;
} except (EXCEPTION_EXECUTE_HANDLER) {
status = ERROR_INVALID_DATA;
}
if (locked) {
UnlockTable((PSTRING_TABLE)StringTable);
}
if(status == NO_ERROR) {
//
// if success, return TRUE without modifying error code
//
return TRUE;
}
//
// if error, we may be interested in cause
//
// SetLastError(status); // left disabled till I know this is safe to do
return FALSE;
}
VOID
_pSpUtilsStringTableTrim(
IN PVOID StringTable
)
/*++
Routine Description:
Free any memory currently allocated for the string table
but not currently used.
This is useful after all strings have been added to a string table
because the string table grows by a fixed block size as it's being built.
THIS ROUTINE DOES NOT DO LOCKING!
Arguments:
StringTable - supplies a string table handle returned from
a call to pSetupStringTableInitialize().
Return Value:
None.
--*/
{
PSTRING_TABLE stringTable = StringTable;
PVOID p;
//
// If the realloc failed the original block is not freed,
// so we don't really care.
//
if(p = pSetupReallocWithTag(stringTable->Data, stringTable->DataSize, MEMTAG_STRINGDATA)) {
stringTable->Data = p;
stringTable->BufferSize = stringTable->DataSize;
}
}
PVOID
pSetupStringTableInitialize(
VOID
)
/*++
Routine Description:
Create and initialize a string table.
Arguments:
None.
Return Value:
NULL if the string table could not be created (out of memory).
Otherwise returns an opaque value that references the string
table in other StringTable calls.
Remarks:
This routine returns a string table with synchronization locks
required by all public StringTable APIs. If the string table
is to be enclosed in a structure that has its own locking
(e.g., HINF, HDEVINFO), then the private version of this API
may be called, which will not create locks for the string table.
--*/
{
PSTRING_TABLE StringTable;
if(StringTable = (PSTRING_TABLE)_pSpUtilsStringTableInitialize(0)) {
if(InitializeSynchronizedAccess(&StringTable->Lock)) {
return StringTable;
}
_pSpUtilsStringTableDestroy(StringTable);
}
return NULL;
}
PVOID
pSetupStringTableInitializeEx(
IN UINT ExtraDataSize, OPTIONAL
IN UINT Reserved
)
/*++
Routine Description:
Create and initialize a string table, where each string can have
some arbitrary data associated with it.
Arguments:
ExtraDataSize - supplies maximum size of arbitrary data that can be
associated with strings in the string table that will be created.
Reserved - unused, must be 0.
Return Value:
NULL if the string table could not be created (out of memory).
Otherwise returns an opaque value that references the string
table in other StringTable calls.
Remarks:
This routine returns a string table with synchronization locks
required by all public StringTable APIs. If the string table
is to be enclosed in a structure that has its own locking
(e.g., HINF, HDEVINFO), then the private version of this API
may be called, which will not create locks for the string table.
--*/
{
PSTRING_TABLE StringTable;
if(Reserved) {
return(NULL);
}
if(StringTable = (PSTRING_TABLE)_pSpUtilsStringTableInitialize(ExtraDataSize)) {
if(InitializeSynchronizedAccess(&StringTable->Lock)) {
return StringTable;
}
_pSpUtilsStringTableDestroy(StringTable);
}
return NULL;
}
PVOID
_pSpUtilsStringTableInitialize(
IN UINT ExtraDataSize OPTIONAL
)
/*++
Routine Description:
Create and initialize a string table. Each string can optionally have
some arbitrary data associated with it.
THIS ROUTINE DOES NOT INITIALIZE STRING TABLE SYNCHRONIZATION LOCKS!
Arguments:
ExtraDataSize - supplies maximum size of arbitrary data that can be
associated with strings in the string table that will be created.
Return Value:
NULL if the string table could not be created (out of memory).
Otherwise returns an opaque value that references the string
table in other StringTable calls.
Remarks:
The string table returned from this API may not be used as-is with the
public StringTable APIs--it must have its synchronization locks initialized
by the public form of this API.
--*/
{
UINT u;
PSTRING_TABLE stringTable;
LCID locale;
//
// Allocate a string table
//
if(stringTable = pSetupMallocWithTag(sizeof(STRING_TABLE),MEMTAG_STRINGTABLE)) {
ZeroMemory(stringTable,sizeof(STRING_TABLE));
stringTable->ExtraDataSize = ExtraDataSize;
locale = GetThreadLocale();
//
// changes here may need to be reflected in _pSpUtilsStringTableInitializeFromMemoryMappedFile
//
if(PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_TURKISH) {
//
// Turkish has a problem with i and dotted i's.
// Do comparison in English (default sort)
//
stringTable->Locale = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT);
} else {
//
// string tables always use default sorting algorithm
//
stringTable->Locale = MAKELCID(LANGIDFROMLCID(locale),SORT_DEFAULT);
}
//
// Allocate space for the string table data.
//
if(stringTable->Data = pSetupMallocWithTag(STRING_TABLE_INITIAL_SIZE,MEMTAG_STRINGDATA)) {
stringTable->BufferSize = STRING_TABLE_INITIAL_SIZE;
//
// Initialize the hash table
//
for(u=0; u<HASH_BUCKET_COUNT; u++) {
((PLONG)(stringTable->Data))[u] = -1;
}
//
// Set the DataSize to the size of the StringNodeOffset list, so
// we'll start adding new strings after it.
//
stringTable->DataSize = HASH_BUCKET_COUNT * sizeof(LONG);
return(stringTable);
}
pSetupFreeWithTag(stringTable,MEMTAG_STRINGTABLE);
}
return(NULL);
}
VOID
pSetupStringTableDestroy(
IN PVOID StringTable
)
/*++
Routine Description:
Destroy a string table, freeing all resources it uses.
Arguments:
StringTable - supplies a string table handle returned from
a call to pSetupStringTableInitialize().
Return Value:
None.
--*/
{
try {
if(!LockTable((PSTRING_TABLE)StringTable)) {
leave;
}
DestroySynchronizedAccess(&(((PSTRING_TABLE)StringTable)->Lock));
_pSpUtilsStringTableDestroy(StringTable);
} except (EXCEPTION_EXECUTE_HANDLER) {
//
}
}
VOID
_pSpUtilsStringTableDestroy(
IN PVOID StringTable
)
/*++
Routine Description:
Destroy a string table, freeing all resources it uses.
THIS ROUTINE DOES NOT DO LOCKING!
Arguments:
StringTable - supplies a string table handle returned from
a call to pSetupStringTableInitialize() or _pSpUtilsStringTableInitializeFromMemoryMappedFile
Return Value:
None.
--*/
{
if (((PSTRING_TABLE)StringTable)->BufferSize) {
pSetupFreeWithTag(((PSTRING_TABLE)StringTable)->Data,MEMTAG_STRINGDATA);
pSetupFreeWithTag(StringTable,MEMTAG_STRINGTABLE);
} else {
pSetupFreeWithTag(StringTable,MEMTAG_STATICSTRINGTABLE);
}
}
PVOID
pSetupStringTableDuplicate(
IN PVOID StringTable
)
/*++
Routine Description:
Create an independent duplicate of a string table.
Arguments:
StringTable - supplies a string table handle of string table to duplicate.
Return Value:
Handle for new string table, NULL if out of memory.
--*/
{
PSTRING_TABLE New = NULL;
BOOL locked = FALSE;
try {
if(!LockTable((PSTRING_TABLE)StringTable)) {
leave;
}
locked = TRUE;
if(New = (PSTRING_TABLE)_pSpUtilsStringTableDuplicate(StringTable)) {
if(!InitializeSynchronizedAccess(&New->Lock)) {
_pSpUtilsStringTableDestroy(New);
New = NULL;
}
}
} except (EXCEPTION_EXECUTE_HANDLER) {
New = NULL;
}
if (locked) {
UnlockTable((PSTRING_TABLE)StringTable);
}
return New;
}
PVOID
_pSpUtilsStringTableDuplicate(
IN PVOID StringTable
)
/*++
Routine Description:
Create an independent duplicate of a string table.
THIS ROUTINE DOES NOT DO LOCKING!
Arguments:
StringTable - supplies a string table handle of string table to duplicate.
Return Value:
Handle for new string table, NULL if out of memory or buffer copy failure.
Remarks:
This routine does not initialize synchronization locks for the duplicate--these
fields are initialized to NULL.
--*/
{
PSTRING_TABLE New;
PSTRING_TABLE stringTable = StringTable;
BOOL Success;
if(New = pSetupMallocWithTag(sizeof(STRING_TABLE),MEMTAG_STRINGTABLE)) {
CopyMemory(New,StringTable,sizeof(STRING_TABLE));
//
// Allocate space for the string table data.
//
if(New->Data = pSetupMallocWithTag(stringTable->DataSize,MEMTAG_STRINGDATA)) {
//
// Surround memory copy in try/except, since we may be dealing with
// a string table contained in a PNF, in which case the buffer is
// in a memory-mapped file.
//
Success = TRUE; // assume success unless we get an inpage error...
try {
CopyMemory(New->Data, stringTable->Data, stringTable->DataSize);
} except(EXCEPTION_EXECUTE_HANDLER) {
Success = FALSE;
}
if(Success) {
New->BufferSize = New->DataSize;
ZeroMemory(&New->Lock, sizeof(MYLOCK));
return New;
}
pSetupFreeWithTag(New->Data,MEMTAG_STRINGDATA);
}
pSetupFreeWithTag(New,MEMTAG_STRINGTABLE);
}
return NULL;
}
PVOID
_pSpUtilsStringTableInitializeFromMemoryMappedFile(
IN PVOID DataBlock,
IN DWORD DataBlockSize,
IN LCID Locale,
IN UINT ExtraDataSize
)
{
PSTRING_TABLE StringTable;
BOOL WasLoaded = TRUE;
//
// Allocate a string table
//
if(!(StringTable = pSetupMallocWithTag(sizeof(STRING_TABLE),MEMTAG_STATICSTRINGTABLE))) {
return NULL;
}
try {
StringTable->Data = (PUCHAR)DataBlock;
StringTable->DataSize = DataBlockSize;
StringTable->BufferSize = 0; // no allocated buffer
//
// Clear the Lock structure, because mem-mapped string tables can only be accessed
// internally
//
StringTable->Lock.Handles[0] = StringTable->Lock.Handles[1] = NULL;
StringTable->ExtraDataSize = ExtraDataSize;
if(PRIMARYLANGID(LANGIDFROMLCID(Locale)) == LANG_TURKISH) {
//
// Turkish has a problem with i and dotted i's.
// Do comparison in English.
//
StringTable->Locale = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT);
} else {
StringTable->Locale = MAKELCID(LANGIDFROMLCID(Locale),SORT_DEFAULT);
}
} except(EXCEPTION_EXECUTE_HANDLER) {
WasLoaded = FALSE;
}
if(WasLoaded) {
return StringTable;
} else {
pSetupFreeWithTag(StringTable,MEMTAG_STATICSTRINGTABLE);
return NULL;
}
}
DWORD
_pSpUtilsStringTableGetDataBlock(
IN PVOID StringTable,
OUT PVOID *StringTableBlock
)
{
*StringTableBlock = (PVOID)(((PSTRING_TABLE)StringTable)->Data);
return ((PSTRING_TABLE)StringTable)->DataSize;
}