diff --git a/source/lib/string_s.cpp b/source/lib/string_s.cpp new file mode 100644 index 0000000000..dee868fe5e --- /dev/null +++ b/source/lib/string_s.cpp @@ -0,0 +1,356 @@ +// implementation of proposed CRT safe string functions +// +// Copyright (c) 2005 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + +#include "precompiled.h" + +#include "lib.h" +#include "posix.h" // SIZE_MAX +#include "timer.h" + +#include + + +// these are already shipped with VC2005 +#if _MSC_VER < 1400 + + +// written against http://std.dkuug.dk/jtc1/sc22/wg14/www/docs/n1031.pdf . +// optimized for size - e.g. strcpy calls strncpy with n = SIZE_MAX. + +// since char and wide versions of these functions are basically the same, +// this source file implements generic versions and bridges the differences +// with these macros. wstring_s.cpp #defines WSTRING_S and includes this file. +#ifdef WSTRING_S +# define tchar wchar_t +# define T(string_literal) L ## string_literal +# define tlen_s wcslen_s +# define tncpy_s wcsncpy_s +# define tcpy_s wcscpy_s +# define tncat_s wcsncat_s +# define tcat_s wcscat_s +# define tcmp wcscmp +# define tcpy wcscpy +#else +# define tchar char +# define T(string_literal) string_literal +# define tlen_s strlen_s +# define tncpy_s strncpy_s +# define tcpy_s strcpy_s +# define tncat_s strncat_s +# define tcat_s strcat_s +# define tcmp strcmp +# define tcpy strcpy +#endif + + +// return and raise an assertion if doesn't hold. +// usable as a statement. +#define ENFORCE(condition, retval) STMT(\ + if(!(condition)) \ + { \ + /*assert2(condition);*/ \ + return retval; \ + } \ +) + + +// return length [in characters] of a string, not including the trailing +// null character. to protect against access violations, only the +// first characters are examined; if the null character is +// not encountered by then, is returned. +size_t tlen_s(const tchar* str, size_t max_len) +{ + // note: we can't bail - what would the return value be? + assert(str != 0); + + size_t len; + for(len = 0; len < max_len; len++) + if(*str++ == '\0') + break; + + return len; +} + + +// copy at most (not including trailing null) from +// into , which must not overlap. +// if thereby (including null) would be exceeded, +// is set to the empty string and ERANGE returned; otherwise, +// 0 is returned to indicate success and that is null-terminated. +// +// note: padding with zeroes is not called for by NG1031. +int tncpy_s(tchar* dst, size_t max_dst_chars, const tchar* src, size_t max_src_chars) +{ + // the MS implementation returns EINVAL and allows dst = 0 if + // max_dst_chars = max_src_chars = 0. no mention of this in + // NG1031 3.6.2.1.1, so don't emulate that behavior. + ENFORCE(dst != 0, EINVAL); + ENFORCE(max_dst_chars != 0, ERANGE); + *dst = '\0'; + ENFORCE(src != 0, EINVAL); + + // copy string until null character encountered or limit reached. + // optimized for size (less comparisons than MS impl) and + // speed (due to well-predicted jumps; we don't bother unrolling). + tchar* p = dst; + size_t chars_left = MIN(max_dst_chars, max_src_chars); +copy_loop: + if(chars_left == 0) + goto hit_limit; + if((*p++ = *src++) == '\0') + return 0; + chars_left--; + goto copy_loop; + +hit_limit: + // which limit was responsible? + // .. dest, and last character wasn't null: overflow. + if(max_dst_chars <= max_src_chars) + { + *dst = '\0'; + ENFORCE(0 && "Buffer too small", ERANGE); + } + // .. source: success, but still need to null-terminate the destination. + *p = '\0'; + return 0; +} + + +// copy (including trailing null) into , which must not overlap. +// if thereby (including null) would be exceeded, +// is set to the empty string and ERANGE returned; otherwise, +// 0 is returned to indicate success and that is null-terminated. +int tcpy_s(tchar* dst, size_t max_dst_chars, const tchar* src) +{ + return tncpy_s(dst, max_dst_chars, src, SIZE_MAX); +} + + +// append to , which must not overlap. +// if thereby (including null) would be exceeded, +// is set to the empty string and ERANGE returned; otherwise, +// 0 is returned to indicate success and that is null-terminated. +int tncat_s(tchar* dst, size_t max_dst_chars, const tchar* src, size_t max_src_chars) +{ + ENFORCE(dst != 0, EINVAL); + ENFORCE(max_dst_chars != 0, ERANGE); + // src is checked in tncpy_s + + const size_t dst_len = tlen_s(dst, max_dst_chars); + if(dst_len == max_dst_chars) + { + *dst = '\0'; + ENFORCE(0 && "Destination string not null-terminated", ERANGE); + } + + tchar* const end = dst+dst_len; + const size_t chars_left = max_dst_chars-dst_len; + int ret = tncpy_s(end, chars_left, src, max_src_chars); + // if tncpy_s overflowed, we need to clear the start of our string + // (not just the appended part). can't do that by default, because + // the beginning of dst is not changed in normal operation. + if(ret != 0) + *dst = '\0'; + return ret; +} + + +int tcat_s(tchar* dst, size_t max_chars, const tchar* src) +{ + return tncat_s(dst, max_chars, src, SIZE_MAX); +} + + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// built-in self test +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef NDEBUG + +static int test() +{ +debug_out("%s\n", __FILE__); +TIMER(stringtest); + + const tchar* s0 = T(""); + const tchar* s1 = T("a"); + const tchar* s5 = T("abcde"); + const tchar* s10 = T("abcdefghij"); + + tchar d1[1]; + tchar d2[2]; + tchar d3[3]; + tchar d5[5]; + tchar d6[6]; + tchar d10[10]; + tchar d11[11]; + + tchar no_null[] = { 'n','o','_','n','u','l','l'}; + + + // + // len + // + +#define TEST_LEN(string, limit, expected)\ +STMT(\ + assert2(tlen_s((string), (limit)) == (expected));\ +) + + TEST_LEN(s0, 0 , 0 ); + TEST_LEN(s0, 1 , 0 ); + TEST_LEN(s0, 50, 0 ); + TEST_LEN(s1, 0 , 0 ); + TEST_LEN(s1, 1 , 1 ); + TEST_LEN(s1, 50, 1 ); + TEST_LEN(s5, 0 , 0 ); + TEST_LEN(s5, 1 , 1 ); + TEST_LEN(s5, 50, 5 ); + TEST_LEN(s10,9 , 9 ); + TEST_LEN(s10,10, 10); + TEST_LEN(s10,11, 10); + + + // + // cpy + // + +#define TEST_CPY(dst, dst_max, src, expected_ret, expected_dst) \ +STMT( \ + int ret = tcpy_s((dst), dst_max, (src)); \ + assert2(ret == expected_ret); \ + if(dst != 0) \ + assert2(!tcmp(dst, T(expected_dst))); \ +) +#define TEST_CPY2(dst, src, expected_ret, expected_dst) \ +STMT( \ + int ret = tcpy_s((dst), ARRAY_SIZE(dst), (src)); \ + assert2(ret == expected_ret); \ + if(dst != 0) \ + assert2(!tcmp(dst, T(expected_dst))); \ +) +#define TEST_NCPY(dst, src, max_src_chars, expected_ret, expected_dst) \ +STMT( \ + int ret = tncpy_s((dst), ARRAY_SIZE(dst), (src), (max_src_chars)); \ + assert2(ret == expected_ret); \ + if(dst != 0) \ + assert2(!tcmp(dst, T(expected_dst))); \ +) + + + TEST_CPY(0 ,0,0 , EINVAL,""); // all invalid + TEST_CPY(0 ,0,s1, EINVAL,""); // dst = 0, max = 0 + TEST_CPY(0 ,1,s1, EINVAL,""); // dst = 0, max > 0 + TEST_CPY(d1,1,0 , EINVAL,""); // src = 0 + TEST_CPY(d1,0,s1, ERANGE,""); // max_dst_chars = 0 + + TEST_CPY2(d1 ,s1, ERANGE,""); + TEST_CPY2(d1 ,s5, ERANGE,""); + TEST_CPY2(d5 ,s5, ERANGE,""); + TEST_CPY2(d2 ,s1, 0,"a"); + TEST_CPY2(d6 ,s5, 0,"abcde"); + TEST_CPY2(d11,s5, 0,"abcde"); + + TEST_NCPY(d1 ,s1,1, ERANGE,""); + TEST_NCPY(d1 ,s5,1, ERANGE,""); + TEST_NCPY(d5 ,s5,5, ERANGE,""); + TEST_NCPY(d2 ,s1,1, 0,"a"); + TEST_NCPY(d6 ,s5,5, 0,"abcde"); + TEST_NCPY(d11,s5,5, 0,"abcde"); + + tcpy(d5, T("----")); + TEST_NCPY(d5,s5,0 , 0,""); // specified behavior! see 3.6.2.1.1 #4 + TEST_NCPY(d5,s5,1 , 0,"a"); + TEST_NCPY(d5,s5,4 , 0,"abcd"); + TEST_NCPY(d6,s5,5 , 0,"abcde"); + TEST_NCPY(d6,s5,10, 0,"abcde"); + + + // + // cat + // + +#define TEST_CAT(dst, dst_max, src, expected_ret, expected_dst) \ +STMT( \ + int ret = tcat_s((dst), dst_max, (src)); \ + assert2(ret == expected_ret); \ + if(dst != 0) \ + assert2(!tcmp(dst, expected_dst)); \ +) +#define TEST_CAT2(dst, dst_val, src, expected_ret, expected_dst) \ +STMT( \ + tcpy(dst, T(dst_val)); \ + int ret = tcat_s((dst), ARRAY_SIZE(dst), (src)); \ + assert2(ret == expected_ret); \ + if(dst != 0) \ + assert2(!tcmp(dst, T(expected_dst))); \ +) +#define TEST_NCAT(dst, dst_val, src, max_src_chars, expected_ret, expected_dst)\ +STMT( \ + tcpy(dst, T(dst_val)); \ + int ret = tncat_s((dst), ARRAY_SIZE(dst), (src), (max_src_chars)); \ + assert2(ret == expected_ret); \ + if(dst != 0) \ + assert2(!tcmp(dst, T(expected_dst))); \ +) + + TEST_CAT(0 ,0,0 , EINVAL,""); // all invalid + TEST_CAT(0 ,0,s1, EINVAL,""); // dst = 0, max = 0 + TEST_CAT(0 ,1,s1, EINVAL,""); // dst = 0, max > 0 + TEST_CAT(d1,1,0 , EINVAL,""); // src = 0 + TEST_CAT(d1,0,s1, ERANGE,""); // max_dst_chars = 0 + TEST_CAT(no_null,5,s1, ERANGE,T("")); // dst not terminated + + TEST_CAT2(d1 ,"" ,s1, ERANGE,""); + TEST_CAT2(d1 ,"" ,s5, ERANGE,""); + TEST_CAT2(d3 ,"1",s1, 0,"1a"); + TEST_CAT2(d5 ,"1",s1, 0,"1a"); + TEST_CAT2(d6 ,"" ,s5, 0,"abcde"); + TEST_CAT2(d10,"" ,s5, 0,"abcde"); + TEST_CAT2(d10,"" ,s10, ERANGE,""); // empty, total overflow + TEST_CAT2(d10,"12345",s5 , ERANGE,""); // not empty, overflow + TEST_CAT2(d10,"12345",s10, ERANGE,""); // not empty, total overflow + TEST_CAT2(d10,"1234" ,s5 , 0,"1234abcde"); + + TEST_NCAT(d1 ,"" ,s1,1, ERANGE,""); + TEST_NCAT(d1 ,"" ,s5,5, ERANGE,""); + TEST_NCAT(d3 ,"1",s1,1, 0,"1a"); + TEST_NCAT(d5 ,"1",s1,1, 0,"1a"); + TEST_NCAT(d6 ,"" ,s5,5, 0,"abcde"); + TEST_NCAT(d10,"" ,s5,5, 0,"abcde"); + TEST_NCAT(d10,"" ,s10,10, ERANGE,""); // empty, total overflow + TEST_NCAT(d10,"12345",s5 ,5 , ERANGE,""); // not empty, overflow + TEST_NCAT(d10,"12345",s10,10, ERANGE,""); // not empty, total overflow + TEST_NCAT(d10,"1234" ,s5 ,5 , 0,"1234abcde"); + + TEST_NCAT(d5,"----",s5,0 , 0,"----"); + TEST_NCAT(d5,"",s5,1 , 0,"a"); + TEST_NCAT(d5,"",s5,4 , 0,"abcd"); + TEST_NCAT(d5,"12",s5,2 , 0,"12ab"); + TEST_NCAT(d6,"",s5,10, 0,"abcde"); + + return 0; +} + +static int dummy = test(); + +#endif // #ifndef NDEBUG diff --git a/source/lib/wstring_s.cpp b/source/lib/wstring_s.cpp new file mode 100644 index 0000000000..469217a353 --- /dev/null +++ b/source/lib/wstring_s.cpp @@ -0,0 +1,4 @@ +#include "precompiled.h" + +#define WSTRING_S +#include "string_s.cpp"