Browse Source

Added vsnprintf implementation.

Branimir Karadžić 9 năm trước cách đây
mục cha
commit
89248e2874
4 tập tin đã thay đổi với 429 bổ sung29 xóa
  1. 13 22
      include/bx/bx.h
  2. 362 0
      src/crt.cpp
  3. 40 7
      src/string.cpp
  4. 14 0
      tests/vsnprintf_test.cpp

+ 13 - 22
include/bx/bx.h

@@ -9,7 +9,6 @@
 #include <stdint.h> // uint32_t
 #include <stdlib.h> // size_t
 #include <stddef.h> // ptrdiff_t
-#include <string.h> // 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
 

+ 362 - 0
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 <bx/bx.h>
+#include <bx/readerwriter.h>
+#include <bx/debug.h>
+
+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][<width>][.<precision>]
+				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

+ 40 - 7
src/string.cpp

@@ -3,9 +3,10 @@
  * License: https://github.com/bkaradzic/bx#license-bsd-2-clause
  */
 
-#include <bx/string.h>
 #include <bx/allocator.h>
 #include <bx/hash.h>
+#include <bx/readerwriter.h>
+#include <bx/string.h>
 
 #if !BX_CRT_NONE
 #	include <stdio.h> // 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)
 	{

+ 14 - 0
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") );
+}