| /* | |
| * 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 = 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; | |
| } | |
| /*********************************************************************** | |
| * 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) | |
| free(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); | |
| } |