diff options
Diffstat (limited to 'winpr/libwinpr/utils/stream.c')
-rw-r--r-- | winpr/libwinpr/utils/stream.c | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/winpr/libwinpr/utils/stream.c b/winpr/libwinpr/utils/stream.c new file mode 100644 index 0000000..4c7696f --- /dev/null +++ b/winpr/libwinpr/utils/stream.c @@ -0,0 +1,523 @@ +/* + * WinPR: Windows Portable Runtime + * Stream Utils + * + * Copyright 2011 Vic Lee + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/config.h> + +#include <winpr/assert.h> +#include <winpr/crt.h> +#include <winpr/stream.h> + +#include "stream.h" +#include "../log.h" + +#define STREAM_TAG WINPR_TAG("wStream") + +#define STREAM_ASSERT(cond) \ + do \ + { \ + if (!(cond)) \ + { \ + WLog_FATAL(STREAM_TAG, "%s [%s:%s:%" PRIuz "]", #cond, __FILE__, __func__, \ + (size_t)__LINE__); \ + winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20); \ + abort(); \ + } \ + } while (0) + +BOOL Stream_EnsureCapacity(wStream* s, size_t size) +{ + WINPR_ASSERT(s); + if (s->capacity < size) + { + size_t position = 0; + size_t old_capacity = 0; + size_t new_capacity = 0; + BYTE* new_buf = NULL; + + old_capacity = s->capacity; + new_capacity = old_capacity; + + do + { + new_capacity *= 2; + } while (new_capacity < size); + + position = Stream_GetPosition(s); + + if (!s->isOwner) + { + new_buf = (BYTE*)malloc(new_capacity); + CopyMemory(new_buf, s->buffer, s->capacity); + s->isOwner = TRUE; + } + else + { + new_buf = (BYTE*)realloc(s->buffer, new_capacity); + } + + if (!new_buf) + return FALSE; + s->buffer = new_buf; + s->capacity = new_capacity; + s->length = new_capacity; + ZeroMemory(&s->buffer[old_capacity], s->capacity - old_capacity); + + Stream_SetPosition(s, position); + } + return TRUE; +} + +BOOL Stream_EnsureRemainingCapacity(wStream* s, size_t size) +{ + if (Stream_GetPosition(s) + size > Stream_Capacity(s)) + return Stream_EnsureCapacity(s, Stream_Capacity(s) + size); + return TRUE; +} + +wStream* Stream_New(BYTE* buffer, size_t size) +{ + wStream* s = NULL; + + if (!buffer && !size) + return NULL; + + s = malloc(sizeof(wStream)); + if (!s) + return NULL; + + if (buffer) + s->buffer = buffer; + else + s->buffer = (BYTE*)malloc(size); + + if (!s->buffer) + { + free(s); + return NULL; + } + + s->pointer = s->buffer; + s->capacity = size; + s->length = size; + + s->pool = NULL; + s->count = 0; + s->isAllocatedStream = TRUE; + s->isOwner = TRUE; + return s; +} + +wStream* Stream_StaticConstInit(wStream* s, const BYTE* buffer, size_t size) +{ + union + { + BYTE* b; + const BYTE* cb; + } cnv; + + cnv.cb = buffer; + return Stream_StaticInit(s, cnv.b, size); +} + +wStream* Stream_StaticInit(wStream* s, BYTE* buffer, size_t size) +{ + const wStream empty = { 0 }; + + WINPR_ASSERT(s); + WINPR_ASSERT(buffer); + + *s = empty; + s->buffer = s->pointer = buffer; + s->capacity = s->length = size; + s->pool = NULL; + s->count = 0; + s->isAllocatedStream = FALSE; + s->isOwner = FALSE; + return s; +} + +void Stream_EnsureValidity(wStream* s) +{ + size_t cur = 0; + + STREAM_ASSERT(s); + STREAM_ASSERT(s->pointer >= s->buffer); + + cur = (size_t)(s->pointer - s->buffer); + STREAM_ASSERT(cur <= s->capacity); + STREAM_ASSERT(s->length <= s->capacity); +} + +void Stream_Free(wStream* s, BOOL bFreeBuffer) +{ + if (s) + { + Stream_EnsureValidity(s); + if (bFreeBuffer && s->isOwner) + free(s->buffer); + + if (s->isAllocatedStream) + free(s); + } +} + +BOOL Stream_SetLength(wStream* _s, size_t _l) +{ + if ((_l) > Stream_Capacity(_s)) + { + _s->length = 0; + return FALSE; + } + _s->length = _l; + return TRUE; +} + +BOOL Stream_SetPosition(wStream* _s, size_t _p) +{ + if ((_p) > Stream_Capacity(_s)) + { + _s->pointer = _s->buffer; + return FALSE; + } + _s->pointer = _s->buffer + (_p); + return TRUE; +} + +void Stream_SealLength(wStream* _s) +{ + size_t cur = 0; + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->buffer <= _s->pointer); + cur = (size_t)(_s->pointer - _s->buffer); + WINPR_ASSERT(cur <= _s->capacity); + if (cur <= _s->capacity) + _s->length = cur; + else + { + WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was written out of bounds"); + winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20); + _s->length = 0; + } +} + +#if defined(WITH_WINPR_DEPRECATED) +BOOL Stream_SetPointer(wStream* _s, BYTE* _p) +{ + WINPR_ASSERT(_s); + if (!_p || (_s->buffer > _p) || (_s->buffer + _s->capacity < _p)) + { + _s->pointer = _s->buffer; + return FALSE; + } + _s->pointer = _p; + return TRUE; +} + +BOOL Stream_SetBuffer(wStream* _s, BYTE* _b) +{ + WINPR_ASSERT(_s); + WINPR_ASSERT(_b); + + _s->buffer = _b; + _s->pointer = _b; + return _s->buffer != NULL; +} + +void Stream_SetCapacity(wStream* _s, size_t _c) +{ + WINPR_ASSERT(_s); + _s->capacity = _c; +} + +#endif + +size_t Stream_GetRemainingCapacity(const wStream* _s) +{ + size_t cur = 0; + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->buffer <= _s->pointer); + cur = (size_t)(_s->pointer - _s->buffer); + WINPR_ASSERT(cur <= _s->capacity); + if (cur > _s->capacity) + { + WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was written out of bounds"); + winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20); + return 0; + } + return (_s->capacity - cur); +} + +size_t Stream_GetRemainingLength(const wStream* _s) +{ + size_t cur = 0; + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->buffer <= _s->pointer); + WINPR_ASSERT(_s->length <= _s->capacity); + cur = (size_t)(_s->pointer - _s->buffer); + WINPR_ASSERT(cur <= _s->length); + if (cur > _s->length) + { + WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was read out of bounds"); + winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20); + return 0; + } + return (_s->length - cur); +} + +BOOL Stream_Write_UTF16_String(wStream* s, const WCHAR* src, size_t length) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(src || (length == 0)); + if (!s || !src) + return FALSE; + + if (!Stream_CheckAndLogRequiredCapacityOfSize(STREAM_TAG, (s), length, sizeof(WCHAR))) + return FALSE; + + for (size_t x = 0; x < length; x++) + Stream_Write_UINT16(s, src[x]); + + return TRUE; +} + +BOOL Stream_Read_UTF16_String(wStream* s, WCHAR* dst, size_t length) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(dst); + + if (!Stream_CheckAndLogRequiredLengthOfSize(STREAM_TAG, s, length, sizeof(WCHAR))) + return FALSE; + + for (size_t x = 0; x < length; x++) + Stream_Read_UINT16(s, dst[x]); + + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredCapacityEx(const char* tag, DWORD level, wStream* s, size_t nmemb, + size_t size, const char* fmt, ...) +{ + WINPR_ASSERT(size != 0); + const size_t actual = Stream_GetRemainingCapacity(s) / size; + + if (actual < nmemb) + { + va_list args; + + va_start(args, fmt); + Stream_CheckAndLogRequiredCapacityExVa(tag, level, s, nmemb, size, fmt, args); + va_end(args); + + return FALSE; + } + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredCapacityExVa(const char* tag, DWORD level, wStream* s, size_t nmemb, + size_t size, const char* fmt, va_list args) +{ + WINPR_ASSERT(size != 0); + const size_t actual = Stream_GetRemainingCapacity(s) / size; + + if (actual < nmemb) + return Stream_CheckAndLogRequiredCapacityWLogExVa(WLog_Get(tag), level, s, nmemb, size, fmt, + args); + return TRUE; +} + +WINPR_ATTR_FORMAT_ARG(6, 0) +BOOL Stream_CheckAndLogRequiredCapacityWLogExVa(wLog* log, DWORD level, wStream* s, size_t nmemb, + size_t size, WINPR_FORMAT_ARG const char* fmt, + va_list args) +{ + + WINPR_ASSERT(size != 0); + const size_t actual = Stream_GetRemainingCapacity(s) / size; + + if (actual < nmemb) + { + char prefix[1024] = { 0 }; + + vsnprintf(prefix, sizeof(prefix), fmt, args); + + WLog_Print(log, level, + "[%s] invalid remaining capacity, got %" PRIuz ", require at least %" PRIu64 + " [element size=%" PRIuz "]", + prefix, actual, nmemb, size); + winpr_log_backtrace_ex(log, level, 20); + return FALSE; + } + return TRUE; +} + +WINPR_ATTR_FORMAT_ARG(6, 7) +BOOL Stream_CheckAndLogRequiredCapacityWLogEx(wLog* log, DWORD level, wStream* s, size_t nmemb, + size_t size, WINPR_FORMAT_ARG const char* fmt, ...) +{ + + WINPR_ASSERT(size != 0); + const size_t actual = Stream_GetRemainingCapacity(s) / size; + + if (actual < nmemb) + { + va_list args; + + va_start(args, fmt); + Stream_CheckAndLogRequiredCapacityWLogExVa(log, level, s, nmemb, size, fmt, args); + va_end(args); + + return FALSE; + } + return TRUE; +} + +WINPR_ATTR_FORMAT_ARG(6, 7) +BOOL Stream_CheckAndLogRequiredLengthEx(const char* tag, DWORD level, wStream* s, size_t nmemb, + size_t size, WINPR_FORMAT_ARG const char* fmt, ...) +{ + WINPR_ASSERT(size > 0); + const size_t actual = Stream_GetRemainingLength(s) / size; + + if (actual < nmemb) + { + va_list args; + + va_start(args, fmt); + Stream_CheckAndLogRequiredLengthExVa(tag, level, s, nmemb, size, fmt, args); + va_end(args); + + return FALSE; + } + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredLengthExVa(const char* tag, DWORD level, wStream* s, size_t nmemb, + size_t size, const char* fmt, va_list args) +{ + WINPR_ASSERT(size > 0); + const size_t actual = Stream_GetRemainingLength(s) / size; + + if (actual < nmemb) + return Stream_CheckAndLogRequiredLengthWLogExVa(WLog_Get(tag), level, s, nmemb, size, fmt, + args); + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredLengthWLogEx(wLog* log, DWORD level, wStream* s, size_t nmemb, + size_t size, const char* fmt, ...) +{ + WINPR_ASSERT(size > 0); + const size_t actual = Stream_GetRemainingLength(s) / size; + + if (actual < nmemb) + { + va_list args; + + va_start(args, fmt); + Stream_CheckAndLogRequiredLengthWLogExVa(log, level, s, nmemb, size, fmt, args); + va_end(args); + + return FALSE; + } + return TRUE; +} + +WINPR_ATTR_FORMAT_ARG(6, 0) +BOOL Stream_CheckAndLogRequiredLengthWLogExVa(wLog* log, DWORD level, wStream* s, size_t nmemb, + size_t size, WINPR_FORMAT_ARG const char* fmt, + va_list args) +{ + WINPR_ASSERT(size > 0); + const size_t actual = Stream_GetRemainingLength(s) / size; + + if (actual < nmemb) + { + char prefix[1024] = { 0 }; + + vsnprintf(prefix, sizeof(prefix), fmt, args); + + WLog_Print(log, level, + "[%s] invalid length, got %" PRIuz ", require at least %" PRIuz + " [element size=%" PRIuz "]", + prefix, actual, nmemb, size); + winpr_log_backtrace_ex(log, level, 20); + return FALSE; + } + return TRUE; +} + +SSIZE_T Stream_Write_UTF16_String_From_UTF8(wStream* s, size_t dlen, const char* src, size_t length, + BOOL fill) +{ + WCHAR* str = Stream_PointerAs(s, WCHAR); + + if (length == 0) + return 0; + + if (!Stream_CheckAndLogRequiredCapacityOfSize(STREAM_TAG, s, dlen, sizeof(WCHAR))) + return -1; + + SSIZE_T rc = ConvertUtf8NToWChar(src, length, str, dlen); + if (rc < 0) + return -1; + + Stream_Seek(s, (size_t)rc * sizeof(WCHAR)); + + if (fill) + Stream_Zero(s, (dlen - (size_t)rc) * sizeof(WCHAR)); + return rc; +} + +char* Stream_Read_UTF16_String_As_UTF8(wStream* s, size_t dlen, size_t* psize) +{ + const WCHAR* str = Stream_ConstPointer(s); + if (dlen > SIZE_MAX / sizeof(WCHAR)) + return NULL; + + if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, dlen * sizeof(WCHAR))) + return NULL; + + Stream_Seek(s, dlen * sizeof(WCHAR)); + return ConvertWCharNToUtf8Alloc(str, dlen, psize); +} + +SSIZE_T Stream_Read_UTF16_String_As_UTF8_Buffer(wStream* s, size_t wcharLength, char* utfBuffer, + size_t utfBufferCharLength) +{ + const WCHAR* ptr = Stream_ConstPointer(s); + if (wcharLength > SIZE_MAX / sizeof(WCHAR)) + return -1; + + if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, wcharLength * sizeof(WCHAR))) + return -1; + + Stream_Seek(s, wcharLength * sizeof(WCHAR)); + return ConvertWCharNToUtf8(ptr, wcharLength, utfBuffer, utfBufferCharLength); +} + +BOOL Stream_SafeSeekEx(wStream* s, size_t size, const char* file, size_t line, const char* fkt) +{ + if (!Stream_CheckAndLogRequiredLengthEx(STREAM_TAG, WLOG_WARN, s, size, 1, "%s(%s:%" PRIuz ")", + fkt, file, line)) + return FALSE; + + Stream_Seek(s, size); + return TRUE; +} |