浏览代码

Removing dependecy on CRT.

Branimir Karadžić 9 年之前
父节点
当前提交
b31f3507f2
共有 6 个文件被更改,包括 225 次插入85 次删除
  1. 37 7
      include/bx/string.h
  2. 3 3
      src/commandline.cpp
  3. 124 69
      src/string.cpp
  4. 6 6
      tests/handle_bench.cpp
  5. 23 0
      tests/string_test.cpp
  6. 32 0
      tests/vsnprintf_test.cpp

+ 37 - 7
include/bx/string.h

@@ -88,27 +88,57 @@ namespace bx
 		void clear();
 	};
 
+	///
+	bool isSpace(char _ch);
+
+	///
+	bool isUpper(char _ch);
+
+	///
+	bool isLower(char _ch);
+
+	///
+	bool isAlpha(char _ch);
+
+	///
+	bool isNumeric(char _ch);
+
+	///
+	bool isAlphaNum(char _ch);
+
+	///
+	char toLower(char _ch);
+
+	///
+	char toUpper(char _ch);
+
 	///
 	bool toBool(const char* _str);
 
+	/// String compare.
+	int32_t strncmp(const char* _lhs, const char* _rhs, size_t _max = -1);
+
 	/// Case insensitive string compare.
-	int32_t stricmp(const char* _a, const char* _b);
+	int32_t strincmp(const char* _lhs, const char* _rhs, size_t _max = -1);
 
 	///
-	size_t strnlen(const char* _str, size_t _max);
+	size_t strnlen(const char* _str, size_t _max = -1);
 
 	/// Copy _num characters from string _src to _dst buffer of maximum _dstSize capacity
 	/// including zero terminator. Copy will be terminated with '\0'.
 	size_t strlncpy(char* _dst, size_t _dstSize, const char* _src, size_t _num = -1);
 
-	/// Find substring in string. Limit search to _size.
-	const char* strnstr(const char* _str, const char* _find, size_t _size);
+	///
+	const char* strnchr(const char* _str, char _ch, size_t _max = -1);
 
-	/// Find substring in string. Case insensitive.
-	const char* stristr(const char* _str, const char* _find);
+	///
+	const char* strnrchr(const char* _str, char _ch, size_t _max = -1);
+
+	/// Find substring in string. Limit search to _size.
+	const char* strnstr(const char* _str, const char* _find, size_t _max = -1);
 
 	/// Find substring in string. Case insensitive. Limit search to _max.
-	const char* stristr(const char* _str, const char* _find, size_t _max);
+	const char* stristr(const char* _str, const char* _find, size_t _max = -1);
 
 	/// Find new line. Returns pointer after new line terminator.
 	const char* strnl(const char* _str);

+ 3 - 3
src/commandline.cpp

@@ -246,11 +246,11 @@ namespace bx
 		const char* arg = findOption(_short, _long, 1);
 		if (NULL != arg)
 		{
-			if ('0' == *arg || (0 == stricmp(arg, "false") ) )
+			if ('0' == *arg || (0 == strincmp(arg, "false") ) )
 			{
 				_value = false;
 			}
-			else if ('0' != *arg || (0 == stricmp(arg, "true") ) )
+			else if ('0' != *arg || (0 == strincmp(arg, "true") ) )
 			{
 				_value = true;
 			}
@@ -294,7 +294,7 @@ namespace bx
 				}
 				else if (NULL != _long
 						&&  '-' == *arg
-						&&  0 == stricmp(arg+1, _long) )
+						&&  0 == strincmp(arg+1, _long) )
 				{
 					if (0 == _skip)
 					{

+ 124 - 69
src/string.cpp

@@ -4,11 +4,8 @@
  */
 
 #include <alloca.h>
-#include <ctype.h>  // tolower
 #include <stdarg.h> // va_list
 #include <stdio.h>  // vsnprintf, vsnwprintf
-#include <string.h>
-#include <wchar.h>  // wchar_t
 
 #include <bx/string.h>
 
@@ -17,19 +14,90 @@
 
 namespace bx
 {
+	bool isSpace(char _ch)
+	{
+		return ' '  == _ch
+			|| '\t' == _ch
+			|| '\n' == _ch
+			|| '\v' == _ch
+			|| '\f' == _ch
+			|| '\r' == _ch
+			;
+	}
+
+	bool isUpper(char _ch)
+	{
+		return _ch >= 'A' && _ch <= 'Z';
+	}
+
+	bool isLower(char _ch)
+	{
+		return _ch >= 'a' && _ch <= 'z';
+	}
+
+	bool isAlpha(char _ch)
+	{
+		return isLower(_ch) || isUpper(_ch);
+	}
+
+	bool isNumeric(char _ch)
+	{
+		return _ch >= '0' && _ch <= '9';
+	}
+
+	bool isAlphaNum(char _ch)
+	{
+		return isAlpha(_ch) || isNumeric(_ch);
+	}
+
+	char toLower(char _ch)
+	{
+		return _ch - (isUpper(_ch) ? 0x20 : 0);
+	}
+
+	char toUpper(char _ch)
+	{
+		return _ch + (isLower(_ch) ? 0x20 : 0);
+	}
+
 	bool toBool(const char* _str)
 	{
-		char ch = (char)::tolower(_str[0]);
+		char ch = (char)toLower(_str[0]);
 		return ch == 't' ||  ch == '1';
 	}
 
-	int32_t stricmp(const char* _a, const char* _b)
+	int32_t strncmp(const char* _lhs, const char* _rhs, size_t _max)
 	{
-#if BX_CRT_MSVC
-		return ::_stricmp(_a, _b);
-#else
-		return ::strcasecmp(_a, _b);
-#endif // BX_COMPILER_
+		for (
+			; 0 < _max && *_lhs == *_rhs
+			; ++_lhs, ++_rhs, --_max
+			)
+		{
+			if (*_lhs != '\0'
+			||  *_rhs != '\0')
+			{
+				break;
+			}
+		}
+
+		return *_lhs - *_rhs;
+	}
+
+	int32_t strincmp(const char* _lhs, const char* _rhs, size_t _max)
+	{
+		for (
+			; 0 < _max && toLower(*_lhs) == toLower(*_rhs)
+			; ++_lhs, ++_rhs, --_max
+			)
+		{
+			if (*_lhs != '\0'
+			||  *_rhs != '\0')
+			{
+				break;
+			}
+		}
+
+		return *_lhs - *_rhs;
 	}
 
 	size_t strnlen(const char* _str, size_t _max)
@@ -48,13 +116,39 @@ namespace bx
 		const size_t len = strnlen(_src, _num);
 		const size_t max = _dstSize-1;
 		const size_t num = (len < max ? len : max);
-		strncpy(_dst, _src, num);
+		memcpy(_dst, _src, num);
 		_dst[num] = '\0';
 
 		return num;
 	}
 
-	const char* strnstr(const char* _str, const char* _find, size_t _size)
+	const char* strnchr(const char* _str, char _ch, size_t _max)
+	{
+		for (size_t ii = 0, len = strnlen(_str, _max); ii < len; ++ii)
+		{
+			if (_str[ii] == _ch)
+			{
+				return &_str[ii];
+			}
+		}
+
+		return NULL;
+	}
+
+	const char* strnrchr(const char* _str, char _ch, size_t _max)
+	{
+		for (size_t ii = strnlen(_str, _max); 0 < ii; --ii)
+		{
+			if (_str[ii] == _ch)
+			{
+				return &_str[ii];
+			}
+		}
+
+		return NULL;
+	}
+
+	const char* strnstr(const char* _str, const char* _find, size_t _max)
 	{
 		char first = *_find;
 		if ('\0' == first)
@@ -63,10 +157,10 @@ namespace bx
 		}
 
 		const char* cmp = _find + 1;
-		size_t len = strlen(cmp);
+		size_t len = strnlen(cmp);
 		do
 		{
-			for (char match = *_str++; match != first && 0 < _size; match = *_str++, --_size)
+			for (char match = *_str++; match != first && 0 < _max; match = *_str++, --_max)
 			{
 				if ('\0' == match)
 				{
@@ -74,7 +168,7 @@ namespace bx
 				}
 			}
 
-			if (0 == _size)
+			if (0 == _max)
 			{
 				return NULL;
 			}
@@ -84,56 +178,17 @@ namespace bx
 		return --_str;
 	}
 
-	const char* stristr(const char* _str, const char* _find)
-	{
-		const char* ptr = _str;
-
-		for (size_t len = strlen(_str), searchLen = strlen(_find)
-		    ; len >= searchLen
-		    ; ++ptr, --len)
-		{
-			// Find start of the string.
-			while (tolower(*ptr) != tolower(*_find) )
-			{
-				++ptr;
-				--len;
-
-				// Search pattern lenght can't be longer than the string.
-				if (searchLen > len)
-				{
-					return NULL;
-				}
-			}
-
-			// Set pointers.
-			const char* string = ptr;
-			const char* search = _find;
-
-			// Start comparing.
-			while (tolower(*string++) == tolower(*search++) )
-			{
-				// If end of the 'search' string is reached, all characters match.
-				if ('\0' == *search)
-				{
-					return ptr;
-				}
-			}
-		}
-
-		return NULL;
-	}
-
 	const char* stristr(const char* _str, const char* _find, size_t _max)
 	{
 		const char* ptr = _str;
 
 		size_t       stringLen = strnlen(_str, _max);
-		const size_t findLen   = strlen(_find);
+		const size_t findLen   = strnlen(_find);
 
 		for (; stringLen >= findLen; ++ptr, --stringLen)
 		{
 			// Find start of the string.
-			while (tolower(*ptr) != tolower(*_find) )
+			while (toLower(*ptr) != toLower(*_find) )
 			{
 				++ptr;
 				--stringLen;
@@ -150,7 +205,7 @@ namespace bx
 			const char* search = _find;
 
 			// Start comparing.
-			while (tolower(*string++) == tolower(*search++) )
+			while (toLower(*string++) == toLower(*search++) )
 			{
 				// If end of the 'search' string is reached, all characters match.
 				if ('\0' == *search)
@@ -205,19 +260,19 @@ namespace bx
 
 	const char* strws(const char* _str)
 	{
-		for (; isspace(*_str); ++_str) {};
+		for (; isSpace(*_str); ++_str) {};
 		return _str;
 	}
 
 	const char* strnws(const char* _str)
 	{
-		for (; !isspace(*_str); ++_str) {};
+		for (; !isSpace(*_str); ++_str) {};
 		return _str;
 	}
 
 	const char* strword(const char* _str)
 	{
-		for (char ch = *_str++; isalnum(ch) || '_' == ch; ch = *_str++) {};
+		for (char ch = *_str++; isAlphaNum(ch) || '_' == ch; ch = *_str++) {};
 		return _str-1;
 	}
 
@@ -262,21 +317,21 @@ namespace bx
 
 	const char* findIdentifierMatch(const char* _str, const char* _word)
 	{
-		size_t len = strlen(_word);
-		const char* ptr = strstr(_str, _word);
-		for (; NULL != ptr; ptr = strstr(ptr + len, _word) )
+		size_t len = strnlen(_word);
+		const char* ptr = strnstr(_str, _word);
+		for (; NULL != ptr; ptr = strnstr(ptr + len, _word) )
 		{
 			if (ptr != _str)
 			{
 				char ch = *(ptr - 1);
-				if (isalnum(ch) || '_' == ch)
+				if (isAlphaNum(ch) || '_' == ch)
 				{
 					continue;
 				}
 			}
 
 			char ch = ptr[len];
-			if (isalnum(ch) || '_' == ch)
+			if (isAlphaNum(ch) || '_' == ch)
 			{
 				continue;
 			}
@@ -357,10 +412,10 @@ namespace bx
 
 	const char* baseName(const char* _filePath)
 	{
-		const char* bs       = strrchr(_filePath, '\\');
-		const char* fs       = strrchr(_filePath, '/');
+		const char* bs       = strnrchr(_filePath, '\\');
+		const char* fs       = strnrchr(_filePath, '/');
 		const char* slash    = (bs > fs ? bs : fs);
-		const char* colon    = strrchr(_filePath, ':');
+		const char* colon    = strnrchr(_filePath, ':');
 		const char* basename = slash > colon ? slash : colon;
 		if (NULL != basename)
 		{
@@ -452,7 +507,7 @@ namespace bx
 
 		if (nn == 0)
 		{
-			return(dlen + strlen(s));
+			return(dlen + strnlen(s));
 		}
 
 		while (*s != '\0')

+ 6 - 6
tests/handle_bench.cpp

@@ -28,13 +28,13 @@ int main()
 			for (uint32_t jj = 0; jj < numElements; ++jj)
 			{
 				tinystl::pair<TinyStlUnorderedMap::iterator, bool> ok = map.insert(tinystl::make_pair(uint64_t(jj), uint16_t(jj) ) );
-				assert(ok.second);
+				assert(ok.second); BX_UNUSED(ok);
 			}
 
 			for (uint32_t jj = 0; jj < numElements; ++jj)
 			{
 				bool ok = bx::mapRemove(map, uint64_t(jj) );
-				assert(ok);
+				assert(ok); BX_UNUSED(ok);
 			}
 
 			assert(map.size() == 0);
@@ -56,13 +56,13 @@ int main()
 			for (uint32_t jj = 0; jj < numElements; ++jj)
 			{
 				std::pair<StdUnorderedMap::iterator, bool> ok = map.insert(std::make_pair(uint64_t(jj), uint16_t(jj) ) );
-				assert(ok.second);
+				assert(ok.second); BX_UNUSED(ok);
 			}
 
 			for (uint32_t jj = 0; jj < numElements; ++jj)
 			{
 				bool ok = bx::mapRemove(map, uint64_t(jj) );
-				assert(ok);
+				assert(ok); BX_UNUSED(ok);
 			}
 
 			assert(map.size() == 0);
@@ -83,13 +83,13 @@ int main()
 			for (uint32_t jj = 0; jj < numElements; ++jj)
 			{
 				bool ok = map.insert(jj, uint16_t(jj) );
-				assert(ok);
+				assert(ok); BX_UNUSED(ok);
 			}
 
 			for (uint32_t jj = 0; jj < numElements; ++jj)
 			{
 				bool ok = map.removeByKey(uint64_t(jj) );
-				assert(ok);
+				assert(ok); BX_UNUSED(ok);
 			}
 
 			assert(map.getNumElements() == 0);

+ 23 - 0
tests/string_test.cpp

@@ -40,6 +40,29 @@ TEST_CASE("strlncpy", "")
 	REQUIRE(num == 4);
 }
 
+TEST_CASE("strincmp", "")
+{
+	REQUIRE(0 == bx::strincmp("test", "test") );
+	REQUIRE(0 == bx::strincmp("test", "testestes", 4) );
+	REQUIRE(0 == bx::strincmp("testestes", "test", 4) );
+}
+
+TEST_CASE("strnchr", "")
+{
+	const char* test = "test";
+	REQUIRE(NULL == bx::strnchr(test, 's', 0) );
+	REQUIRE(NULL == bx::strnchr(test, 's', 2) );
+	REQUIRE(&test[2] == bx::strnchr(test, 's') );
+}
+
+TEST_CASE("strnrchr", "")
+{
+	const char* test = "test";
+	REQUIRE(NULL == bx::strnrchr(test, 's', 0) );
+	REQUIRE(NULL == bx::strnrchr(test, 's', 1) );
+	REQUIRE(&test[2] == bx::strnrchr(test, 's') );
+}
+
 TEST_CASE("StringView", "")
 {
 	bx::StringView sv("test");

+ 32 - 0
tests/vsnprintf_test.cpp

@@ -20,3 +20,35 @@ TEST_CASE("vsnprintf truncated", "Truncated output buffer.")
 	REQUIRE(10 == bx::snprintf(buffer, BX_COUNTOF(buffer), "Ten chars!") );
 	REQUIRE(0  == strcmp(buffer, "Ten ch") );
 }
+
+static bool test(const char* _expected, const char* _format, ...)
+{
+	int32_t max = (int32_t)strlen(_expected) + 1;
+	char* temp = (char*)alloca(max);
+
+	va_list argList;
+	va_start(argList, _format);
+	int32_t len = bx::vsnprintf(temp, max, _format, argList);
+	va_end(argList);
+
+	bool result = true
+		&& len == max-1
+		&& 0   == strcmp(_expected, temp)
+		;
+
+	if (!result)
+	{
+		printf("result (%d) %s, expected (%d) %s\n", len, temp, max-1, _expected);
+	}
+
+	return result;
+}
+
+TEST_CASE("vsnprintf f", "")
+{
+	REQUIRE(test("1.337", "%0.3f", 1.337) );
+	REQUIRE(test("  13.370", "%8.3f", 13.37) );
+	REQUIRE(test("  13.370", "%*.*f", 8, 3, 13.37) );
+	REQUIRE(test("13.370  ", "%-8.3f", 13.37) );
+	REQUIRE(test("13.370  ", "%*.*f", -8, 3, 13.37) );
+}