From 89248e2874f9ee85a93b574b2f06f4866875aff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Sat, 4 Feb 2017 22:27:49 -0800 Subject: [PATCH] Added vsnprintf implementation. --- include/bx/bx.h | 35 ++-- src/crt.cpp | 362 +++++++++++++++++++++++++++++++++++++++ src/string.cpp | 47 ++++- tests/vsnprintf_test.cpp | 14 ++ 4 files changed, 429 insertions(+), 29 deletions(-) create mode 100644 src/crt.cpp diff --git a/include/bx/bx.h b/include/bx/bx.h index 6c1f7b4..43ccba3 100644 --- a/include/bx/bx.h +++ b/include/bx/bx.h @@ -9,7 +9,6 @@ #include // uint32_t #include // size_t #include // ptrdiff_t -#include // memcpy #include "config.h" #include "macros.h" @@ -46,31 +45,23 @@ namespace bx Ty tmp = _a; _a = _b; _b = tmp; } - /// Scatter/gather memcpy. - inline void memCopy(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _srcPitch, uint32_t _dstPitch) - { - const uint8_t* src = (const uint8_t*)_src; - uint8_t* dst = (uint8_t*)_dst; - - for (uint32_t ii = 0; ii < _num; ++ii) - { - memcpy(dst, src, _size); - src += _srcPitch; - dst += _dstPitch; - } - } + /// + void* memCopy(void* _dst, const void* _src, size_t _numBytes); /// - inline void gather(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _srcPitch) - { - memCopy(_dst, _src, _size, _num, _srcPitch, _size); - } + void memCopy(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _srcPitch, uint32_t _dstPitch); /// - inline void scatter(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _dstPitch) - { - memCopy(_dst, _src, _size, _num, _size, _dstPitch); - } + void gather(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _srcPitch); + + /// + void scatter(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _dstPitch); + + /// + void* memMove(void* _dst, const void* _src, size_t _numBytes); + + /// + void* memSet(void* _dst, uint8_t _ch, size_t _numBytes); } // namespace bx diff --git a/src/crt.cpp b/src/crt.cpp new file mode 100644 index 0000000..8686f03 --- /dev/null +++ b/src/crt.cpp @@ -0,0 +1,362 @@ +/* + * Copyright 2010-2017 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bx#license-bsd-2-clause + */ + +#include +#include +#include + +namespace bx +{ + void* memCopyRef(void* _dst, const void* _src, size_t _numBytes) + { + uint8_t* dst = (uint8_t*)_dst; + const uint8_t* end = dst + _numBytes; + const uint8_t* src = (const uint8_t*)_src; + while (dst != end) + { + *dst++ = *src++; + } + + return _dst; + } + + void* memCopy(void* _dst, const void* _src, size_t _numBytes) + { +#if BX_CRT_NONE + return memCopyRef(_dst, _src, _numBytes); +#else + return ::memcpy(_dst, _src, _numBytes); +#endif // BX_CRT_NONE + } + + void memCopy(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _srcPitch, uint32_t _dstPitch) + { + const uint8_t* src = (const uint8_t*)_src; + uint8_t* dst = (uint8_t*)_dst; + + for (uint32_t ii = 0; ii < _num; ++ii) + { + memCopy(dst, src, _size); + src += _srcPitch; + dst += _dstPitch; + } + } + + /// + void gather(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _srcPitch) + { + memCopy(_dst, _src, _size, _num, _srcPitch, _size); + } + + /// + void scatter(void* _dst, const void* _src, uint32_t _size, uint32_t _num, uint32_t _dstPitch) + { + memCopy(_dst, _src, _size, _num, _size, _dstPitch); + } + + void* memMoveRef(void* _dst, const void* _src, size_t _numBytes) + { + uint8_t* dst = (uint8_t*)_dst; + const uint8_t* src = (const uint8_t*)_src; + + if (_numBytes == 0 + || dst == src) + { + return dst; + } + + // if (src+_numBytes <= dst || end <= src) + if (dst < src) + { + return memcpy(_dst, _src, _numBytes); + } + + for (intptr_t ii = _numBytes-1; ii >= 0; --ii) + { + dst[ii] = src[ii]; + } + + return _dst; + } + + void* memMove(void* _dst, const void* _src, size_t _numBytes) + { +#if BX_CRT_NONE + return memMoveRef(_dst, _src, _numBytes); +#else + return ::memmove(_dst, _src, _numBytes); +#endif // BX_CRT_NONE + } + + void* memSetRef(void* _dst, uint8_t _ch, size_t _numBytes) + { + uint8_t* dst = (uint8_t*)_dst; + const uint8_t* end = dst + _numBytes; + while (dst != end) + { + *dst++ = char(_ch); + } + + return _dst; + } + + void* memSet(void* _dst, uint8_t _ch, size_t _numBytes) + { +#if BX_CRT_NONE + return memSetRef(_dst, _ch, _numBytes); +#else + return ::memset(_dst, _ch, _numBytes); +#endif // BX_CRT_NONE + } + + namespace + { + struct Param + { + int32_t width; + uint32_t base; + uint32_t prec; + char fill; + bool left; + }; + + static int32_t write(WriterI* _writer, const char* _str, int32_t _len, const Param& _param, Error* _err) + { + int32_t size = 0; + int32_t len = (int32_t)strnlen(_str, _len); + int32_t padding = _param.width > len ? _param.width - len : 0; + + if (!_param.left) + { + size += writeRep(_writer, _param.fill, padding, _err); + } + + size += write(_writer, _str, len, _err); + + if (_param.left) + { + size += writeRep(_writer, _param.fill, padding, _err); + } + + return size; + } + + static int32_t write(WriterI* _writer, const char* _str, const Param& _param, Error* _err) + { + return write(_writer, _str, INT32_MAX, _param, _err); + } + + static int32_t write(WriterI* _writer, int32_t _i, const Param& _param, Error* _err) + { + char str[33]; + int32_t len = toString(str, sizeof(str), _i, _param.base); + + if (len == 0) + { + return 0; + } + + return write(_writer, str, len, _param, _err); + } + + static int32_t write(WriterI* _writer, uint32_t _i, const Param& _param, Error* _err) + { + char str[33]; + int32_t len = toString(str, sizeof(str), _i, _param.base); + + if (len == 0) + { + return 0; + } + + return write(_writer, str, len, _param, _err); + } + + static int32_t write(WriterI* _writer, double _d, const Param& _param, Error* _err) + { + char str[1024]; + int32_t len = toString(str, sizeof(str), _d); + + if (len == 0) + { + return 0; + } + + const char* dot = strnchr(str, '.'); + const int32_t precLen = int32_t(dot + 1 + _param.prec - str); + if (precLen > len) + { + for (int32_t ii = len; ii < precLen; ++ii) + { + str[ii] = '0'; + } + str[precLen] = '\0'; + } + len = precLen; + + return write(_writer, str, len, _param, _err); + } + + static int32_t write(WriterI* _writer, const void* _ptr, const Param& _param, Error* _err) + { + char str[35] = "0x"; + int32_t len = toString(str + 2, sizeof(str) - 2, uint32_t(uintptr_t(_ptr) ), 16); + + if (len == 0) + { + return 0; + } + + len += 2; + return write(_writer, str, len, _param, _err); + } + } // anonymous namespace + + int32_t write(WriterI* _writer, const char* _format, va_list _argList, Error* _err) + { + MemoryReader reader(_format, strnlen(_format) ); + + int32_t size = 0; + + while (_err->isOk() ) + { + char ch = '\0'; + read(&reader, ch, _err); + + if (!_err->isOk() ) + { + break; + } + else if ('%' == ch) + { + // %[ -0][][.] + read(&reader, ch); + + Param param; + param.base = 10; + param.prec = 6; + param.left = false; + param.fill = ' '; + param.width = 0; + + while (' ' == ch + || '-' == ch + || '0' == ch) + { + switch (ch) + { + case '-': param.left = true; break; + case ' ': param.fill = ' '; break; + case '0': param.fill = '0'; break; + } + + if (param.left) + { + param.fill = ' '; + } + + read(&reader, ch); + } + + if ('*' == ch) + { + read(&reader, ch); + param.width = va_arg(_argList, int32_t); + + if (0 > param.width) + { + param.left = true; + param.width = -param.width; + } + + } + else + { + while (isNumeric(ch) ) + { + param.width = param.width * 10 + ch - '0'; + read(&reader, ch); + } + } + + if ('.' == ch) + { + read(&reader, ch); + + if ('*' == ch) + { + read(&reader, ch); + param.prec = va_arg(_argList, int32_t); + } + else + { + param.prec = 0; + while (isNumeric(ch) ) + { + param.prec = param.prec * 10 + ch - '0'; + read(&reader, ch); + } + } + } + + switch (toLower(ch) ) + { + case 'c': + size += write(_writer, char(va_arg(_argList, int32_t) ), _err); + break; + + case 's': + size += write(_writer, va_arg(_argList, const char*), param, _err); + break; + + case 'd': + param.base = 10; + size += write(_writer, va_arg(_argList, int32_t), param, _err); + break; + + case 'f': + size += write(_writer, va_arg(_argList, double), param, _err); + break; + + case 'p': + size += write(_writer, va_arg(_argList, void*), param, _err); + break; + + case 'x': + param.base = 16; + size += write(_writer, va_arg(_argList, uint32_t), param, _err); + break; + + case 'u': + param.base = 10; + size += write(_writer, va_arg(_argList, uint32_t), param, _err); + break; + + default: + size += write(_writer, ch, _err); + break; + } + } + else + { + size += write(_writer, ch, _err); + } + } + + size += write(_writer, '\0', _err); + + return size; + } + + int32_t write(WriterI* _writer, Error* _err, const char* _format, ...) + { + va_list argList; + va_start(argList, _format); + int32_t size = write(_writer, _format, argList, _err); + va_end(argList); + return size; + } + +} // namespace bx diff --git a/src/string.cpp b/src/string.cpp index 0206f65..c53a9de 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -3,9 +3,10 @@ * License: https://github.com/bkaradzic/bx#license-bsd-2-clause */ -#include #include #include +#include +#include #if !BX_CRT_NONE # include // vsnprintf, vsnwprintf @@ -381,10 +382,42 @@ namespace bx return NULL; } -#if !BX_CRT_NONE + int32_t write(WriterI* _writer, const char* _format, va_list _argList, Error* _err); + + int32_t vsnprintfRef(char* _out, size_t _max, const char* _format, va_list _argList) + { + if (1 < _max) + { + StaticMemoryBlockWriter writer(_out, _max-1); + _out[_max-1] = '\0'; + + Error err; + va_list argListCopy; + va_copy(argListCopy, _argList); + int32_t size = write(&writer, _format, argListCopy, &err); + va_end(argListCopy); + + if (err.isOk() ) + { + return size; + } + } + + Error err; + SizerWriter sizer; + va_list argListCopy; + va_copy(argListCopy, _argList); + int32_t size = write(&sizer, _format, argListCopy, &err); + va_end(argListCopy); + + return size - 1 /* size without '\0' terminator */; + } + int32_t vsnprintf(char* _out, size_t _max, const char* _format, va_list _argList) { -#if BX_CRT_MSVC +#if BX_CRT_NONE + return vsnprintfRef(_out, _max, _format, _argList); +#elif BX_CRT_MSVC int32_t len = -1; if (NULL != _out) { @@ -398,7 +431,6 @@ namespace bx return ::vsnprintf(_out, _max, _format, _argList); #endif // BX_COMPILER_MSVC } -#endif // !BX_CRT_NONE int32_t snprintf(char* _out, size_t _max, const char* _format, ...) { @@ -409,10 +441,12 @@ namespace bx return len; } -#if !BX_CRT_NONE int32_t vsnwprintf(wchar_t* _out, size_t _max, const wchar_t* _format, va_list _argList) { -#if BX_CRT_MSVC +#if BX_CRT_NONE + BX_UNUSED(_out, _max, _format, _argList); + return 0; +#elif BX_CRT_MSVC int32_t len = -1; if (NULL != _out) { @@ -437,7 +471,6 @@ namespace bx va_end(argList); return len; } -#endif // !BX_CRT_NONE const char* baseName(const char* _filePath) { diff --git a/tests/vsnprintf_test.cpp b/tests/vsnprintf_test.cpp index 04dcf16..db1fba6 100644 --- a/tests/vsnprintf_test.cpp +++ b/tests/vsnprintf_test.cpp @@ -52,3 +52,17 @@ TEST_CASE("vsnprintf f", "") REQUIRE(test("13.370 ", "%-8.3f", 13.37) ); REQUIRE(test("13.370 ", "%*.*f", -8, 3, 13.37) ); } + +TEST_CASE("vsnprintf d/u/x", "") +{ + REQUIRE(test("1337", "%d", 1337) ); + + REQUIRE(test("1337", "%x", 0x1337) ); +} + +TEST_CASE("vsnprintf", "") +{ + REQUIRE(test("x", "%c", 'x') ); + + REQUIRE(test("hello, world!", "%s, %s!", "hello", "world") ); +}