342 lines
7.7 KiB
C++
342 lines
7.7 KiB
C++
|
/* ---------------------------------------------------------------------------
|
||
|
Nullsoft Database Engine
|
||
|
--------------------
|
||
|
codename: Near Death Experience
|
||
|
--------------------------------------------------------------------------- */
|
||
|
|
||
|
/* ---------------------------------------------------------------------------
|
||
|
|
||
|
StringField Class
|
||
|
Windows specific version
|
||
|
|
||
|
Field data layout:
|
||
|
[2 bytes] string length (bytes)
|
||
|
[length bytes] String data. UTF-16 data will start with a BOM
|
||
|
--------------------------------------------------------------------------- */
|
||
|
|
||
|
#include "../nde.h"
|
||
|
#include "StringField.h"
|
||
|
#include "../../nu/AutoChar.h"
|
||
|
#include "../../nu/AutoWide.h"
|
||
|
|
||
|
static wchar_t CharSwap(wchar_t value)
|
||
|
{
|
||
|
return (value >> 8) | (value << 8);
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
StringField::StringField(const wchar_t *Str, int strkind)
|
||
|
{
|
||
|
InitField();
|
||
|
Type = FIELD_STRING;
|
||
|
if (Str)
|
||
|
{
|
||
|
if (strkind == STRING_IS_WCHAR)
|
||
|
StringW = ndestring_wcsdup(Str);
|
||
|
else
|
||
|
{
|
||
|
StringW = const_cast<wchar_t *>(Str);
|
||
|
ndestring_retain(StringW);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
void StringField::InitField(void)
|
||
|
{
|
||
|
Type = FIELD_STRING;
|
||
|
StringW = NULL;
|
||
|
optimized_the = 0;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
StringField::StringField()
|
||
|
{
|
||
|
InitField();
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
StringField::~StringField()
|
||
|
{
|
||
|
ndestring_release(StringW);
|
||
|
StringW=0;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
void StringField::ReadTypedData(const uint8_t *data, size_t len)
|
||
|
{
|
||
|
unsigned short c;
|
||
|
|
||
|
CHECK_SHORT(len);
|
||
|
c = (unsigned short)(data[0]|(data[1]<<8));
|
||
|
data+=2;
|
||
|
if (c)
|
||
|
{
|
||
|
bool unicode=false;
|
||
|
bool reverseEndian=false;
|
||
|
if (c >= 2 // enough room for BOM
|
||
|
&& (c % 2) == 0) // can't be unicode if it's not an even multiple of 2
|
||
|
{
|
||
|
wchar_t BOM=0;
|
||
|
memcpy(&BOM, data, 2);
|
||
|
if (BOM == 0xFEFF)
|
||
|
{
|
||
|
data+=2;
|
||
|
c-=2;
|
||
|
unicode=true;
|
||
|
}
|
||
|
else if (BOM == 0xFFFE)
|
||
|
{
|
||
|
data+=2;
|
||
|
c-=2;
|
||
|
unicode=true;
|
||
|
reverseEndian=true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CHECK_BIN(len, c);
|
||
|
if (unicode)
|
||
|
{
|
||
|
ndestring_release(StringW);
|
||
|
StringW = ndestring_malloc(c+sizeof(wchar_t));
|
||
|
|
||
|
memcpy(StringW, data, c);
|
||
|
StringW[c/2]=0;
|
||
|
if (reverseEndian)
|
||
|
{
|
||
|
for (unsigned short i=0;i<c;i++)
|
||
|
StringW[i]=CharSwap(StringW[i]);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)data, c, 0, 0);
|
||
|
StringW = ndestring_malloc((len+1)*sizeof(wchar_t));
|
||
|
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)data, c, StringW, len);
|
||
|
StringW[len]=0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
void StringField::WriteTypedData(uint8_t *data, size_t len)
|
||
|
{
|
||
|
int pos=0;
|
||
|
|
||
|
if (StringW)
|
||
|
{
|
||
|
unsigned short c = (unsigned short)wcslen(StringW) * sizeof(wchar_t) + 2 /* for BOM */;
|
||
|
// write size
|
||
|
CHECK_SHORT(len);
|
||
|
PUT_SHORT(c); pos+=2;
|
||
|
|
||
|
// write byte order mark
|
||
|
CHECK_BIN(len, 2);
|
||
|
wchar_t BOM = 0xFEFF;
|
||
|
PUT_BINARY(data, (uint8_t *)&BOM, 2, pos);
|
||
|
pos+=2;
|
||
|
c-=2;
|
||
|
|
||
|
// write string
|
||
|
CHECK_BIN(len, c);
|
||
|
PUT_BINARY(data, (uint8_t *)StringW, c, pos);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CHECK_SHORT(len);
|
||
|
PUT_SHORT(0); /*pos+=2;*/
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
wchar_t *StringField::GetStringW(void)
|
||
|
{
|
||
|
return StringW;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
void StringField::SetStringW(const wchar_t *Str)
|
||
|
{
|
||
|
if (!Str) return;
|
||
|
|
||
|
ndestring_release(StringW);
|
||
|
StringW = NULL;
|
||
|
StringW = ndestring_wcsdup(Str);
|
||
|
optimized_the=0;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
void StringField::SetNDEString(wchar_t *Str)
|
||
|
{
|
||
|
if (!Str) return;
|
||
|
|
||
|
// copy and then release, just in case we're copying into ourselves
|
||
|
wchar_t *oldStr = StringW;
|
||
|
StringW = Str;
|
||
|
ndestring_retain(StringW);
|
||
|
ndestring_release(oldStr);
|
||
|
optimized_the=0;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
size_t StringField::GetDataSize(void)
|
||
|
{
|
||
|
if (StringW)
|
||
|
{
|
||
|
return wcslen(StringW)*2 +2 /*for BOM*/ + 2 /*for byte length*/;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int StringField::Compare(Field *Entry)
|
||
|
{
|
||
|
if (!Entry) return -1;
|
||
|
if (Entry->GetType() != GetType()) return 0;
|
||
|
return mywcsicmp(GetStringW(), ((StringField*)Entry)->GetStringW());
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int StringField::Starts(Field *Entry)
|
||
|
{
|
||
|
if (!Entry) return -1;
|
||
|
if (Entry->GetType() != GetType()) return 0;
|
||
|
const wchar_t *p = ((StringField*)Entry)->GetStringW();
|
||
|
const wchar_t *d = GetStringW();
|
||
|
if (!d || !p) return 0;
|
||
|
return nde_wcsbegins(d, p);
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int StringField::Contains(Field *Entry)
|
||
|
{
|
||
|
if (!Entry) return -1;
|
||
|
if (Entry->GetType() != GetType()) return 0;
|
||
|
const wchar_t *p = ((StringField*)Entry)->GetStringW();
|
||
|
const wchar_t *d = GetStringW();
|
||
|
if (!d || !p) return 0;
|
||
|
return nde_wcscontains(GetStringW(), ((StringField*)Entry)->GetStringW());
|
||
|
}
|
||
|
|
||
|
Field *StringField::Clone(Table *pTable)
|
||
|
{
|
||
|
StringField *clone = new StringField(StringW, STRING_IS_NDESTRING);
|
||
|
clone->Pos = FIELD_CLONE;
|
||
|
clone->ID = ID;
|
||
|
clone->MaxSizeOnDisk = (uint32_t)GetDataSize();
|
||
|
return clone;
|
||
|
}
|
||
|
|
||
|
// todo: make configurable words to skip, as well as trailing whitespace removal
|
||
|
#define IsCharSpaceW(c) (c == L' ' || c == L'\t')
|
||
|
inline bool IsTheW(const wchar_t *str) { if (str && (str[0] == L't' || str[0] == L'T') && (str[1] == L'h' || str[1] == L'H') && (str[2] == L'e' || str[2] == L'E') && (str[3] == L' ')) return true; else return false; }
|
||
|
#define SKIP_THE_AND_WHITESPACEW(x) { wchar_t *save##x=(wchar_t*)x; while (IsCharSpaceW(*x) && *x) x++; if (IsTheW(x)) x+=4; while (IsCharSpaceW(*x)) x++; if (!*x) x=save##x; }
|
||
|
|
||
|
bool StringField::ApplyFilter(Field *Data, int op)
|
||
|
{
|
||
|
// TODO: maybe do this?
|
||
|
|
||
|
if (op == FILTER_ISEMPTY || op == FILTER_ISNOTEMPTY)
|
||
|
{
|
||
|
bool r = (op == FILTER_ISEMPTY);
|
||
|
if (!StringW)
|
||
|
return r;
|
||
|
|
||
|
if (StringW && StringW[0] == 0)
|
||
|
return r;
|
||
|
|
||
|
return !r;
|
||
|
}
|
||
|
//
|
||
|
bool r;
|
||
|
StringField *compField = (StringField *)Data;
|
||
|
|
||
|
const wchar_t *p = compField->GetStringW();
|
||
|
const wchar_t *d = GetStringW();
|
||
|
if (!p)
|
||
|
p = L"";
|
||
|
if (!d)
|
||
|
d = L"";
|
||
|
|
||
|
switch (op)
|
||
|
{
|
||
|
case FILTER_EQUALS:
|
||
|
r = !nde_wcsicmp(d, p);
|
||
|
break;
|
||
|
case FILTER_NOTEQUALS:
|
||
|
r = !!nde_wcsicmp(d, p);
|
||
|
break;
|
||
|
case FILTER_CONTAINS:
|
||
|
r = nde_wcscontains(d, p);
|
||
|
break;
|
||
|
case FILTER_NOTCONTAINS:
|
||
|
r = !nde_wcscontains(d, p);
|
||
|
break;
|
||
|
case FILTER_ABOVE:
|
||
|
r = (bool)(nde_wcsicmp(d, p) > 0);
|
||
|
break;
|
||
|
case FILTER_ABOVEOREQUAL:
|
||
|
r = (bool)(nde_wcsicmp(d, p) >= 0);
|
||
|
break;
|
||
|
case FILTER_BELOW:
|
||
|
r = (bool)(nde_wcsicmp(d, p) < 0);
|
||
|
break;
|
||
|
case FILTER_BELOWOREQUAL:
|
||
|
r = (bool)(nde_wcsicmp(d, p) <= 0);
|
||
|
break;
|
||
|
case FILTER_BEGINS:
|
||
|
r = nde_wcsbegins(d, p);
|
||
|
break;
|
||
|
case FILTER_ENDS:
|
||
|
r = nde_wcsends(d, p);
|
||
|
break;
|
||
|
case FILTER_LIKE:
|
||
|
|
||
|
if (compField->optimized_the)
|
||
|
p = compField->optimized_the;
|
||
|
else
|
||
|
{
|
||
|
SKIP_THE_AND_WHITESPACEW(p);
|
||
|
compField->optimized_the = p;
|
||
|
}
|
||
|
|
||
|
if (optimized_the)
|
||
|
d = optimized_the;
|
||
|
else
|
||
|
{
|
||
|
SKIP_THE_AND_WHITESPACEW(d);
|
||
|
optimized_the=d;
|
||
|
}
|
||
|
|
||
|
r = (bool)(nde_wcsicmp(d, p) == 0);
|
||
|
break;
|
||
|
case FILTER_BEGINSLIKE:
|
||
|
|
||
|
if (compField->optimized_the)
|
||
|
p = compField->optimized_the;
|
||
|
else
|
||
|
{
|
||
|
SKIP_THE_AND_WHITESPACEW(p);
|
||
|
compField->optimized_the = p;
|
||
|
}
|
||
|
|
||
|
if (optimized_the)
|
||
|
d = optimized_the;
|
||
|
else
|
||
|
{
|
||
|
SKIP_THE_AND_WHITESPACEW(d);
|
||
|
optimized_the=d;
|
||
|
}
|
||
|
|
||
|
r = nde_wcsbegins(d, p);
|
||
|
break;
|
||
|
default:
|
||
|
r = true;
|
||
|
break;
|
||
|
}
|
||
|
return r;
|
||
|
}
|