#include <string.h>
#include <wchar.h>
#include "windows.h"
#include "winerror.h"
#include "hstring.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(winstring);
#define HSTRING_REFERENCE_FLAG 1
struct hstring_header
{
UINT32 flags;
UINT32 length;
UINT32 padding1;
UINT32 padding2;
const WCHAR *str;
};
struct hstring_private
{
struct hstring_header header;
LONG refcount;
WCHAR buffer[1];
};
static const WCHAR empty[1];
C_ASSERT(sizeof(struct hstring_header) <= sizeof(HSTRING_HEADER));
static inline struct hstring_private *impl_from_HSTRING(HSTRING string)
{
return (struct hstring_private *)string;
}
static inline struct hstring_private *impl_from_HSTRING_HEADER(HSTRING_HEADER *header)
{
return CONTAINING_RECORD(header, struct hstring_private, header);
}
static inline struct hstring_private *impl_from_HSTRING_BUFFER(HSTRING_BUFFER buffer)
{
return CONTAINING_RECORD(buffer, struct hstring_private, buffer);
}
static BOOL alloc_string(UINT32 len, HSTRING *out)
{
struct hstring_private *priv;
priv = malloc(offsetof(struct hstring_private, buffer[len+1]));
if (!priv)
return FALSE;
priv->header.flags = 0;
priv->header.length = len;
priv->header.str = priv->buffer;
priv->refcount = 1;
priv->buffer[len] = '\0';
*out = (HSTRING)priv;
return TRUE;
}
HRESULT WINAPI WindowsCreateString(LPCWSTR ptr, UINT32 len,
HSTRING *out)
{
struct hstring_private *priv;
TRACE("(%s, %u, %p)\n", debugstr_wn(ptr, len), len, out);
if (out == NULL)
return E_INVALIDARG;
if (len == 0)
{
*out = NULL;
return S_OK;
}
if (ptr == NULL)
return E_POINTER;
if (!alloc_string(len, out))
return E_OUTOFMEMORY;
priv = impl_from_HSTRING(*out);
memcpy(priv->buffer, ptr, len * sizeof(*priv->buffer));
return S_OK;
}
HRESULT WINAPI WindowsCreateStringReference(LPCWSTR ptr, UINT32 len,
HSTRING_HEADER *header, HSTRING *out)
{
struct hstring_private *priv = impl_from_HSTRING_HEADER(header);
TRACE("(%s, %u, %p, %p)\n", debugstr_wn(ptr, len), len, header, out);
if (out == NULL || header == NULL)
return E_INVALIDARG;
if (ptr != NULL && ptr[len] != '\0')
return E_INVALIDARG;
if (len == 0)
{
*out = NULL;
return S_OK;
}
if (ptr == NULL)
return E_POINTER;
priv->header.str = ptr;
priv->header.length = len;
priv->header.flags = HSTRING_REFERENCE_FLAG;
*out = (HSTRING)priv;
return S_OK;
}
HRESULT WINAPI WindowsDeleteString(HSTRING str)
{
struct hstring_private *priv = impl_from_HSTRING(str);
TRACE("(%p)\n", str);
if (str == NULL)
return S_OK;
if (priv->header.flags & HSTRING_REFERENCE_FLAG)
return S_OK;
if (InterlockedDecrement(&priv->refcount) == 0)
free(priv);
return S_OK;
}
HRESULT WINAPI WindowsDuplicateString(HSTRING str, HSTRING *out)
{
struct hstring_private *priv = impl_from_HSTRING(str);
TRACE("(%p, %p)\n", str, out);
if (out == NULL)
return E_INVALIDARG;
if (str == NULL)
{
*out = NULL;
return S_OK;
}
if (priv->header.flags & HSTRING_REFERENCE_FLAG)
return WindowsCreateString(priv->header.str, priv->header.length, out);
InterlockedIncrement(&priv->refcount);
*out = str;
return S_OK;
}
HRESULT WINAPI WindowsPreallocateStringBuffer(UINT32 len, WCHAR **outptr,
HSTRING_BUFFER *out)
{
struct hstring_private *priv;
HSTRING str;
TRACE("(%u, %p, %p)\n", len, outptr, out);
if (outptr == NULL || out == NULL)
return E_POINTER;
if (len == 0)
{
*outptr = (LPWSTR)empty;
*out = NULL;
return S_OK;
}
if (!alloc_string(len, &str))
return E_OUTOFMEMORY;
priv = impl_from_HSTRING(str);
*outptr = priv->buffer;
*out = (HSTRING_BUFFER)&priv->buffer;
return S_OK;
}
HRESULT WINAPI WindowsDeleteStringBuffer(HSTRING_BUFFER buf)
{
struct hstring_private *priv = NULL;
TRACE("(%p)\n", buf);
if(buf)
priv = impl_from_HSTRING_BUFFER(buf);
return WindowsDeleteString((HSTRING)priv);
}
HRESULT WINAPI WindowsPromoteStringBuffer(HSTRING_BUFFER buf, HSTRING *out)
{
struct hstring_private *priv = impl_from_HSTRING_BUFFER(buf);
TRACE("(%p, %p)\n", buf, out);
if (out == NULL)
return E_POINTER;
if (buf == NULL)
{
*out = NULL;
return S_OK;
}
if (priv->buffer[priv->header.length] != 0 || priv->header.flags & HSTRING_REFERENCE_FLAG || priv->refcount != 1)
return E_INVALIDARG;
*out = (HSTRING)priv;
return S_OK;
}
UINT32 WINAPI WindowsGetStringLen(HSTRING str)
{
struct hstring_private *priv = impl_from_HSTRING(str);
TRACE("(%p)\n", str);
if (str == NULL)
return 0;
return priv->header.length;
}
LPCWSTR WINAPI WindowsGetStringRawBuffer(HSTRING str, UINT32 *len)
{
struct hstring_private *priv = impl_from_HSTRING(str);
TRACE("(%p, %p)\n", str, len);
if (str == NULL)
{
if (len)
*len = 0;
return empty;
}
if (len)
*len = priv->header.length;
return priv->header.str;
}
HRESULT WINAPI WindowsStringHasEmbeddedNull(HSTRING str, BOOL *out)
{
UINT32 i;
struct hstring_private *priv = impl_from_HSTRING(str);
TRACE("(%p, %p)\n", str, out);
if (out == NULL)
return E_INVALIDARG;
if (str == NULL)
{
*out = FALSE;
return S_OK;
}
for (i = 0; i < priv->header.length; i++)
{
if (priv->header.str[i] == '\0')
{
*out = TRUE;
return S_OK;
}
}
*out = FALSE;
return S_OK;
}
HRESULT WINAPI WindowsSubstring(HSTRING str, UINT32 start, HSTRING *out)
{
struct hstring_private *priv = impl_from_HSTRING(str);
UINT32 len = WindowsGetStringLen(str);
TRACE("(%p, %u, %p)\n", str, start, out);
if (out == NULL)
return E_INVALIDARG;
if (start > len)
return E_BOUNDS;
if (start == len)
{
*out = NULL;
return S_OK;
}
return WindowsCreateString(&priv->header.str[start], len - start, out);
}
HRESULT WINAPI WindowsSubstringWithSpecifiedLength(HSTRING str, UINT32 start, UINT32 len, HSTRING *out)
{
struct hstring_private *priv = impl_from_HSTRING(str);
TRACE("(%p, %u, %u, %p)\n", str, start, len, out);
if (out == NULL)
return E_INVALIDARG;
if (start + len < start ||
start + len > WindowsGetStringLen(str))
return E_BOUNDS;
if (len == 0)
{
*out = NULL;
return S_OK;
}
return WindowsCreateString(&priv->header.str[start], len, out);
}
HRESULT WINAPI WindowsConcatString(HSTRING str1, HSTRING str2, HSTRING *out)
{
struct hstring_private *priv1 = impl_from_HSTRING(str1);
struct hstring_private *priv2 = impl_from_HSTRING(str2);
struct hstring_private *priv;
TRACE("(%p, %p, %p)\n", str1, str2, out);
if (out == NULL)
return E_INVALIDARG;
if (str1 == NULL)
return WindowsDuplicateString(str2, out);
if (str2 == NULL)
return WindowsDuplicateString(str1, out);
if (!priv1->header.length && !priv2->header.length)
{
*out = NULL;
return S_OK;
}
if (!alloc_string(priv1->header.length + priv2->header.length, out))
return E_OUTOFMEMORY;
priv = impl_from_HSTRING(*out);
memcpy(priv->buffer, priv1->header.str, priv1->header.length * sizeof(*priv1->buffer));
memcpy(priv->buffer + priv1->header.length, priv2->header.str, priv2->header.length * sizeof(*priv2->buffer));
return S_OK;
}
BOOL WINAPI WindowsIsStringEmpty(HSTRING str)
{
struct hstring_private *priv = impl_from_HSTRING(str);
TRACE("(%p)\n", str);
if (str == NULL)
return TRUE;
return priv->header.length == 0;
}
HRESULT WINAPI WindowsCompareStringOrdinal(HSTRING str1, HSTRING str2, INT32 *res)
{
struct hstring_private *priv1 = impl_from_HSTRING(str1);
struct hstring_private *priv2 = impl_from_HSTRING(str2);
const WCHAR *buf1 = empty, *buf2 = empty;
UINT32 len1 = 0, len2 = 0;
TRACE("(%p, %p, %p)\n", str1, str2, res);
if (res == NULL)
return E_INVALIDARG;
if (str1 == str2)
{
*res = 0;
return S_OK;
}
if (str1)
{
buf1 = priv1->header.str;
len1 = priv1->header.length;
}
if (str2)
{
buf2 = priv2->header.str;
len2 = priv2->header.length;
}
*res = CompareStringOrdinal(buf1, len1, buf2, len2, FALSE) - CSTR_EQUAL;
return S_OK;
}
HRESULT WINAPI WindowsTrimStringStart(HSTRING str1, HSTRING str2, HSTRING *out)
{
struct hstring_private *priv1 = impl_from_HSTRING(str1);
struct hstring_private *priv2 = impl_from_HSTRING(str2);
UINT32 start;
TRACE("(%p, %p, %p)\n", str1, str2, out);
if (!out || !str2 || !priv2->header.length)
return E_INVALIDARG;
if (!str1)
{
*out = NULL;
return S_OK;
}
for (start = 0; start < priv1->header.length; start++)
{
if (!wmemchr(priv2->header.str, priv1->header.str[start], priv2->header.length))
break;
}
return start ? WindowsCreateString(&priv1->header.str[start], priv1->header.length - start, out) :
WindowsDuplicateString(str1, out);
}
HRESULT WINAPI WindowsTrimStringEnd(HSTRING str1, HSTRING str2, HSTRING *out)
{
struct hstring_private *priv1 = impl_from_HSTRING(str1);
struct hstring_private *priv2 = impl_from_HSTRING(str2);
UINT32 len;
TRACE("(%p, %p, %p)\n", str1, str2, out);
if (!out || !str2 || !priv2->header.length)
return E_INVALIDARG;
if (!str1)
{
*out = NULL;
return S_OK;
}
for (len = priv1->header.length; len > 0; len--)
{
if (!wmemchr(priv2->header.str, priv1->header.str[len - 1], priv2->header.length))
break;
}
return (len < priv1->header.length) ? WindowsCreateString(priv1->header.str, len, out) :
WindowsDuplicateString(str1, out);
}