483 lines
13 KiB
C
483 lines
13 KiB
C
/*
|
|
* Copyright 2014 Martin Storsjo
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#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 = HeapAlloc(GetProcessHeap(), 0, 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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsCreateString (combase.@)
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsCreateStringReference (combase.@)
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsDeleteString (combase.@)
|
|
*/
|
|
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)
|
|
HeapFree(GetProcessHeap(), 0, priv);
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsDuplicateString (combase.@)
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsPreallocateStringBuffer (combase.@)
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsDeleteStringBuffer (combase.@)
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsPromoteStringBuffer (combase.@)
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsGetStringLen (combase.@)
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsGetStringRawBuffer (combase.@)
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsStringHasEmbeddedNull (combase.@)
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsSubstring (combase.@)
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsSubstringWithSpecifiedLength (combase.@)
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsConcatString (combase.@)
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsIsStringEmpty (combase.@)
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsCompareStringOrdinal (combase.@)
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsTrimStringStart (combase.@)
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WindowsTrimStringEnd (combase.@)
|
|
*/
|
|
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);
|
|
}
|