| /* |
| * 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 "windows.h" |
| #include "winerror.h" |
| #include "hstring.h" |
| #include "wine/unicode.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(winstring); |
| |
| struct hstring_private |
| { |
| LPWSTR buffer; |
| UINT32 length; |
| BOOL reference; |
| LONG refcount; |
| }; |
| |
| static const WCHAR empty[1]; |
| |
| C_ASSERT(sizeof(struct hstring_private) <= 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 (struct hstring_private *)header; |
| } |
| |
| static inline struct hstring_private *impl_from_HSTRING_BUFFER(HSTRING_BUFFER buffer) |
| { |
| return (struct hstring_private *)buffer; |
| } |
| |
| static BOOL alloc_string(UINT32 len, HSTRING *out) |
| { |
| struct hstring_private *priv; |
| priv = HeapAlloc(GetProcessHeap(), 0, sizeof(*priv) + (len + 1) * sizeof(*priv->buffer)); |
| if (!priv) |
| return FALSE; |
| priv->buffer = (LPWSTR)(priv + 1); |
| priv->length = len; |
| priv->reference = FALSE; |
| 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->buffer = (LPWSTR)ptr; |
| priv->length = len; |
| priv->reference = TRUE; |
| *out = (HSTRING)header; |
| 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->reference) |
| 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->reference) |
| return WindowsCreateString(priv->buffer, priv->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)str; |
| return S_OK; |
| } |
| |
| /*********************************************************************** |
| * WindowsDeleteStringBuffer (combase.@) |
| */ |
| HRESULT WINAPI WindowsDeleteStringBuffer(HSTRING_BUFFER buf) |
| { |
| TRACE("(%p)\n", buf); |
| |
| return WindowsDeleteString((HSTRING)buf); |
| } |
| |
| /*********************************************************************** |
| * 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->length] != 0 || priv->reference || priv->refcount != 1) |
| return E_INVALIDARG; |
| *out = (HSTRING)buf; |
| 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->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->length; |
| return priv->buffer; |
| } |
| |
| /*********************************************************************** |
| * 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->length; i++) |
| { |
| if (priv->buffer[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->buffer[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->buffer[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->length && !priv2->length) |
| { |
| *out = NULL; |
| return S_OK; |
| } |
| if (!alloc_string(priv1->length + priv2->length, out)) |
| return E_OUTOFMEMORY; |
| priv = impl_from_HSTRING(*out); |
| memcpy(priv->buffer, priv1->buffer, priv1->length * sizeof(*priv1->buffer)); |
| memcpy(priv->buffer + priv1->length, priv2->buffer, priv2->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->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->buffer; |
| len1 = priv1->length; |
| } |
| if (str2) |
| { |
| buf2 = priv2->buffer; |
| len2 = priv2->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->length) |
| return E_INVALIDARG; |
| if (!str1) |
| { |
| *out = NULL; |
| return S_OK; |
| } |
| for (start = 0; start < priv1->length; start++) |
| { |
| if (!memchrW(priv2->buffer, priv1->buffer[start], priv2->length)) |
| break; |
| } |
| return start ? WindowsCreateString(&priv1->buffer[start], priv1->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->length) |
| return E_INVALIDARG; |
| if (!str1) |
| { |
| *out = NULL; |
| return S_OK; |
| } |
| for (len = priv1->length; len > 0; len--) |
| { |
| if (!memchrW(priv2->buffer, priv1->buffer[len - 1], priv2->length)) |
| break; |
| } |
| return (len < priv1->length) ? WindowsCreateString(priv1->buffer, len, out) : |
| WindowsDuplicateString(str1, out); |
| } |