# Fixed string handling for Windows/Linux compatibility.
* vsnprintf2: Made compatible between GCC and MSVC - it now always null-terminates the buffer, and returns -1 on overflow. Fixes #158. Added tests. * MeshManager: Use shared_ptr.expired() instead of checking for bad_weak_ptr exception. * Xeromyces: Added tests for GetXMBPath, because it does unusual things in sscanf which MSVC's /analyze complains about. * ConfigDB, ScriptGlue: Replaced some asserts with return-on-failure, to avoid invalid array accesses when continuing after the assert (as complained about by /analyze). * CStr: Removed "using namespace std". Added tests for handling of invalid UTF-8. This was SVN commit r4625.
This commit is contained in:
parent
75f2f9fd5f
commit
a265a441fd
@ -16,19 +16,11 @@ CModelDefPtr CMeshManager::GetMesh(const char *filename)
|
||||
{
|
||||
CStr fn(filename);
|
||||
mesh_map::iterator iter = m_MeshMap.find(fn);
|
||||
if (iter != m_MeshMap.end())
|
||||
if (iter != m_MeshMap.end() && !iter->second.expired())
|
||||
{
|
||||
try
|
||||
{
|
||||
CModelDefPtr model (iter->second);
|
||||
//LOG(MESSAGE, "mesh", "Loading mesh '%s%' (cached)...", filename);
|
||||
return model;
|
||||
}
|
||||
// If the mesh has already been deleted, the weak_ptr -> shared_ptr
|
||||
// conversion will throw bad_weak_ptr (and we need to reload the mesh)
|
||||
catch (boost::bad_weak_ptr)
|
||||
{
|
||||
}
|
||||
CModelDefPtr model (iter->second);
|
||||
//LOG(MESSAGE, "mesh", "Loading mesh '%s%' (cached)...", filename);
|
||||
return model;
|
||||
}
|
||||
|
||||
try
|
||||
|
@ -1022,7 +1022,6 @@ void CGUI::ReportParseError(const char *str, ...)
|
||||
|
||||
va_start(argp, str);
|
||||
vsnprintf2(buffer, sizeof(buffer), str, argp);
|
||||
buffer[sizeof(buffer)-1] = '\0';
|
||||
va_end(argp);
|
||||
|
||||
// Print header
|
||||
|
@ -188,5 +188,6 @@ extern bool self_test_active;
|
||||
|
||||
#define TS_ASSERT_OK(expr) TS_ASSERT_EQUALS((expr), INFO::OK)
|
||||
#define TS_ASSERT_STR_EQUALS(str1, str2) TS_ASSERT_EQUALS(std::string(str1), std::string(str2))
|
||||
#define TS_ASSERT_WSTR_EQUALS(str1, str2) TS_ASSERT_EQUALS(std::wstring(str1), std::wstring(str2))
|
||||
|
||||
#endif // #ifndef SELF_TEST_H__
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "lib/debug.h" // ErrorReaction
|
||||
|
||||
#include <cmath> // see comments below about isfinite
|
||||
#include <cstdarg> // needed for vsnprintf2
|
||||
|
||||
// some functions among the sysdep API are implemented as macros
|
||||
// that redirect to the platform-dependent version. this is done where
|
||||
@ -82,13 +83,12 @@
|
||||
// C99 / SUSv3 emulation where needed
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// vsnprintf2: handles positional parameters and %lld.
|
||||
// already available on *nix, emulated on Win32.
|
||||
#if OS_WIN
|
||||
// vsnprintf2: doesn't quite follow the standard for vsnprintf, but works
|
||||
// better across compilers:
|
||||
// - handles positional parameters and %lld
|
||||
// - always null-terminates the buffer
|
||||
// - returns -1 on overflow (if the output string (including null) does not fit in the buffer)
|
||||
extern int vsnprintf2(char* buffer, size_t count, const char* format, va_list argptr);
|
||||
#else
|
||||
#define vsnprintf2 vsnprintf
|
||||
#endif
|
||||
|
||||
#if !MSC_VERSION
|
||||
#define stricmp strcasecmp
|
||||
|
73
source/lib/sysdep/tests/test_printf.h
Normal file
73
source/lib/sysdep/tests/test_printf.h
Normal file
@ -0,0 +1,73 @@
|
||||
#include "lib/lib.h"
|
||||
#include "lib/self_test.h"
|
||||
|
||||
class TestPrintf : public CxxTest::TestSuite
|
||||
{
|
||||
// Split some bits into separate functions, so we can get
|
||||
// a legitimate va_list to pass to vsnprintf2:
|
||||
|
||||
void _test_truncate(int buffer_size, char* expected_output, int expected_return, /* char* input_string */...)
|
||||
{
|
||||
char buf[17] = "................"; // fill with dots so null-termination is made obvious
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, expected_return);
|
||||
|
||||
int ret = vsnprintf2(buf, buffer_size, "%s", ap);
|
||||
|
||||
TS_ASSERT_STR_EQUALS(buf, expected_output);
|
||||
TS_ASSERT_EQUALS(ret, expected_return);
|
||||
|
||||
std::string past_buffer (buf + buffer_size);
|
||||
TS_ASSERT(past_buffer.find_first_not_of('.') == past_buffer.npos);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void _test_sprintf(char* expected_output, char* format, ...)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
vsnprintf2(buf, sizeof(buf), format, ap);
|
||||
TS_ASSERT_STR_EQUALS(buf, expected_output);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
public:
|
||||
void test_truncate()
|
||||
{
|
||||
_test_truncate(8, "1234", 4, "1234");
|
||||
_test_truncate(8, "1234567", 7, "1234567");
|
||||
_test_truncate(8, "1234567", -1, "12345678");
|
||||
_test_truncate(8, "1234567", -1, "123456789");
|
||||
_test_truncate(8, "1234567", -1, "123456789abcdef");
|
||||
}
|
||||
|
||||
void test_lld()
|
||||
{
|
||||
i64 z = 0;
|
||||
i64 n = 65536;
|
||||
_test_sprintf("0", "%lld", z);
|
||||
_test_sprintf("65536", "%lld", n);
|
||||
_test_sprintf("4294967296", "%lld", n*n);
|
||||
_test_sprintf("281474976710656", "%lld", n*n*n);
|
||||
_test_sprintf("-281474976710656", "%lld", -n*n*n);
|
||||
_test_sprintf("123 456 281474976710656 789", "%d %d %lld %d", 123, 456, n*n*n, 789);
|
||||
}
|
||||
|
||||
void test_pos()
|
||||
{
|
||||
_test_sprintf("a b", "%1$c %2$c", 'a', 'b');
|
||||
_test_sprintf("b a", "%2$c %1$c", 'a', 'b');
|
||||
}
|
||||
|
||||
void test_pos_lld()
|
||||
{
|
||||
_test_sprintf("1 2 3", "%1$d %2$lld %3$d", 1, (i64)2, 3);
|
||||
_test_sprintf("2 1 3", "%2$lld %1$d %3$d", 1, (i64)2, 3);
|
||||
}
|
||||
};
|
26
source/lib/sysdep/unix/printf.cpp
Normal file
26
source/lib/sysdep/unix/printf.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
|
||||
// See declaration in sysdep.h for explanation of need
|
||||
|
||||
int vsnprintf2(char* buffer, size_t count, const char* format, va_list argptr)
|
||||
{
|
||||
int ret = vsnprintf(buffer, count, format, argptr);
|
||||
|
||||
/*
|
||||
"The glibc implementation of the functions snprintf() and vsnprintf() conforms
|
||||
to the C99 standard ... since glibc version 2.1. Until glibc 2.0.6 they would
|
||||
return -1 when the output was truncated."
|
||||
- man printf
|
||||
|
||||
MSVC's _vsnprintf still returns -1, so we want this one to do the same (for
|
||||
compatibility), if the output (including the terminating null) is truncated.
|
||||
*/
|
||||
|
||||
if (ret >= (int)count)
|
||||
return -1;
|
||||
|
||||
return ret;
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
/*
|
||||
Added features (compared to MSVC's printf):
|
||||
Positional parameters (e.g. "%1$d", where '1' means '1st in the parameter list')
|
||||
%lld (equivalent to %I64d in MSVC)
|
||||
%lld (equivalent to %I64d in MSVC7.1, though it's supported natively by MSVC8)
|
||||
|
||||
Unsupported features (compared to a perfect implementation):
|
||||
' <-- because MSVC doesn't support it
|
||||
@ -32,6 +32,12 @@
|
||||
#include <sstream>
|
||||
#include <stdarg.h>
|
||||
|
||||
#if MSC_VERSION < 1400
|
||||
# define USE_I64_FORMAT 1
|
||||
#else
|
||||
# define USE_I64_FORMAT 0
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
SPECFLAG_THOUSANDS = 1, // '
|
||||
@ -363,8 +369,8 @@ finished_reading:
|
||||
if (s->length > 256)
|
||||
{
|
||||
if (s->length == 0x00006c6c)
|
||||
#if MSC_VERSION
|
||||
newformat += "I64"; // MSVC compatibility
|
||||
#if USE_I64_FORMAT
|
||||
newformat += "I64";
|
||||
#else
|
||||
newformat += "ll";
|
||||
#endif
|
||||
@ -464,7 +470,11 @@ finished_reading:
|
||||
|
||||
int ret = _vsnprintf(buffer, count, newformat.c_str(), (va_list)newstackptr);
|
||||
|
||||
// For consistency with GCC's vsnprintf, make sure the buffer is null-terminated
|
||||
// and return an error if that truncates the output
|
||||
buffer[count-1] = '\0';
|
||||
if (ret == (int)count)
|
||||
return -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
@ -591,7 +591,7 @@ void CConsole::SetBuffer(const wchar_t* szMessage, ...)
|
||||
m_iBufferPos = std::min(oldBufferPos, m_iBufferLength);
|
||||
}
|
||||
|
||||
void CConsole::UseHistoryFile( const CStr& filename, int max_history_lines )
|
||||
void CConsole::UseHistoryFile(const CStr& filename, int max_history_lines)
|
||||
{
|
||||
m_MaxHistoryLines = max_history_lines;
|
||||
|
||||
@ -599,7 +599,8 @@ void CConsole::UseHistoryFile( const CStr& filename, int max_history_lines )
|
||||
LoadHistory();
|
||||
}
|
||||
|
||||
void CConsole::ProcessBuffer(const wchar_t* szLine){
|
||||
void CConsole::ProcessBuffer(const wchar_t* szLine)
|
||||
{
|
||||
if (szLine == NULL) return;
|
||||
if (wcslen(szLine) <= 0) return;
|
||||
|
||||
@ -609,14 +610,15 @@ void CConsole::ProcessBuffer(const wchar_t* szLine){
|
||||
SaveHistory(); // Do this each line for the moment; if a script causes
|
||||
// a crash it's a useful record.
|
||||
|
||||
wchar_t szCommand[CONSOLE_BUFFER_SIZE];
|
||||
memset(szCommand, '\0', sizeof(wchar_t) * CONSOLE_BUFFER_SIZE);
|
||||
wchar_t szCommand[CONSOLE_BUFFER_SIZE] = { 0 };
|
||||
|
||||
std::map<std::wstring, fptr>::iterator Iter;
|
||||
|
||||
if (szLine[0] == '\\')
|
||||
{
|
||||
swscanf(szLine, L"\\%ls", szCommand);
|
||||
if (swscanf(szLine, L"\\%ls", szCommand) != 1)
|
||||
return;
|
||||
|
||||
Trim(szCommand);
|
||||
ToLower(szCommand);
|
||||
|
||||
|
@ -154,10 +154,8 @@ void CLogger::Log(ELogMethod method, const char* category, const char *fmt, ...)
|
||||
va_list argp;
|
||||
char buffer[512];
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
va_start(argp, fmt);
|
||||
if (vsnprintf2(buffer, sizeof(buffer)-1, fmt, argp) == -1)
|
||||
if (vsnprintf2(buffer, sizeof(buffer), fmt, argp) == -1)
|
||||
{
|
||||
// Buffer too small - ensure the string is nicely terminated
|
||||
strcpy(buffer+sizeof(buffer)-4, "..."); // safe
|
||||
@ -173,10 +171,8 @@ void CLogger::LogOnce(ELogMethod method, const char* category, const char *fmt,
|
||||
va_list argp;
|
||||
char buffer[512];
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
va_start(argp, fmt);
|
||||
if (vsnprintf2(buffer, sizeof(buffer)-1, fmt, argp) == -1)
|
||||
if (vsnprintf2(buffer, sizeof(buffer), fmt, argp) == -1)
|
||||
{
|
||||
// Buffer too small - ensure the string is nicely terminated
|
||||
strcpy(buffer+sizeof(buffer)-4, "..."); // safe
|
||||
|
@ -38,7 +38,7 @@ CStr8 CStrW::ToUTF8() const
|
||||
{
|
||||
CStr8 result;
|
||||
|
||||
for (size_t i = 0; i < Length(); ++i)
|
||||
for (size_t i = 0; i < length(); ++i)
|
||||
{
|
||||
unsigned short bytesToWrite;
|
||||
wchar_t ch = (*this)[i];
|
||||
@ -58,12 +58,14 @@ CStr8 CStrW::ToUTF8() const
|
||||
case 2: *--target = ((ch | 0x80) & 0xBF); ch >>= 6;
|
||||
case 1: *--target = (ch | firstByteMark[bytesToWrite]);
|
||||
}
|
||||
result += CStr(buf, bytesToWrite);
|
||||
result += CStr8(buf, bytesToWrite);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool isLegalUTF8(const unsigned char *source, int length) {
|
||||
static bool isLegalUTF8(const unsigned char *source, int length)
|
||||
{
|
||||
unsigned char a;
|
||||
const unsigned char *srcptr = source+length;
|
||||
|
||||
@ -92,7 +94,7 @@ CStrW CStr8::FromUTF8() const
|
||||
{
|
||||
CStrW result;
|
||||
|
||||
if(empty())
|
||||
if (empty())
|
||||
return result;
|
||||
|
||||
const unsigned char* source = (const unsigned char*)&*begin();
|
||||
@ -103,12 +105,12 @@ CStrW CStr8::FromUTF8() const
|
||||
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
|
||||
if (source + extraBytesToRead >= sourceEnd)
|
||||
{
|
||||
debug_warn("Invalid UTF-8 (fell off end)");
|
||||
//debug_warn("Invalid UTF-8 (fell off end)");
|
||||
return L"";
|
||||
}
|
||||
|
||||
if (! isLegalUTF8(source, extraBytesToRead+1)) {
|
||||
debug_warn("Invalid UTF-8 (illegal data)");
|
||||
//debug_warn("Invalid UTF-8 (illegal data)");
|
||||
return L"";
|
||||
}
|
||||
|
||||
@ -134,7 +136,6 @@ CStrW CStr8::FromUTF8() const
|
||||
// The following code is compiled twice, as CStrW then as CStr8:
|
||||
|
||||
#include "CStr.h"
|
||||
using namespace std;
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@ -282,7 +283,7 @@ long CStr::ReverseFind(const CStr& Str) const
|
||||
// Lowercase and uppercase
|
||||
CStr CStr::LowerCase() const
|
||||
{
|
||||
tstring NewString = *this;
|
||||
std::tstring NewString = *this;
|
||||
for (size_t i = 0; i < length(); i++)
|
||||
NewString[i] = (tchar)_totlower((*this)[i]);
|
||||
|
||||
@ -291,7 +292,7 @@ CStr CStr::LowerCase() const
|
||||
|
||||
CStr CStr::UpperCase() const
|
||||
{
|
||||
tstring NewString = *this;
|
||||
std::tstring NewString = *this;
|
||||
for (size_t i = 0; i < length(); i++)
|
||||
NewString[i] = (tchar)_totupper((*this)[i]);
|
||||
|
||||
@ -302,7 +303,7 @@ CStr CStr::UpperCase() const
|
||||
// code duplication because return by value overhead if they were merely an alias
|
||||
CStr CStr::LCase() const
|
||||
{
|
||||
tstring NewString = *this;
|
||||
std::tstring NewString = *this;
|
||||
for (size_t i = 0; i < length(); i++)
|
||||
NewString[i] = (tchar)_totlower((*this)[i]);
|
||||
|
||||
@ -311,7 +312,7 @@ CStr CStr::LCase() const
|
||||
|
||||
CStr CStr::UCase() const
|
||||
{
|
||||
tstring NewString = *this;
|
||||
std::tstring NewString = *this;
|
||||
for (size_t i = 0; i < length(); i++)
|
||||
NewString[i] = (tchar)_totupper((*this)[i]);
|
||||
|
||||
|
@ -105,10 +105,11 @@ public:
|
||||
CStrW(const CStr8 &asciiStr);
|
||||
#endif
|
||||
|
||||
// Conversion to/from UTF-8, encoded in a CStr8. Non-ASCII characters are
|
||||
// handled correctly.
|
||||
// May fail, if converting from invalid UTF-8 data; the empty string will
|
||||
// be returned.
|
||||
// Conversion to/from UTF-8, encoded in a CStr8.
|
||||
// Common non-ASCII characters are handled correctly.
|
||||
// Characters outside the BMP (above 0xFFFF) are *not* handled correctly.
|
||||
// FromUTF8 may fail, if converting from invalid UTF-8 data - the empty
|
||||
// string will be returned.
|
||||
#ifdef _UNICODE
|
||||
CStr8 ToUTF8() const;
|
||||
#else
|
||||
|
@ -200,7 +200,11 @@ CConfigValue *CConfigDB::GetValue(EConfigNamespace ns, const CStr& name)
|
||||
|
||||
CConfigValueSet *CConfigDB::GetValues(EConfigNamespace ns, const CStr& name )
|
||||
{
|
||||
debug_assert(ns < CFG_LAST && ns >= 0);
|
||||
if (ns < 0 || ns >= CFG_LAST)
|
||||
{
|
||||
debug_warn("CConfigDB: Invalid ns value");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TConfigMap::iterator it = m_Map[CFG_COMMAND].find( name );
|
||||
if( it != m_Map[CFG_COMMAND].end() )
|
||||
@ -218,7 +222,11 @@ CConfigValueSet *CConfigDB::GetValues(EConfigNamespace ns, const CStr& name )
|
||||
|
||||
CConfigValue *CConfigDB::CreateValue(EConfigNamespace ns, const CStr& name)
|
||||
{
|
||||
debug_assert(ns < CFG_LAST && ns >= 0);
|
||||
if (ns < 0 || ns >= CFG_LAST)
|
||||
{
|
||||
debug_warn("CConfigDB: Invalid ns value");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CConfigValue *ret=GetValue(ns, name);
|
||||
if (ret) return ret;
|
||||
@ -229,7 +237,11 @@ CConfigValue *CConfigDB::CreateValue(EConfigNamespace ns, const CStr& name)
|
||||
|
||||
void CConfigDB::SetConfigFile(EConfigNamespace ns, bool useVFS, const CStr& path)
|
||||
{
|
||||
debug_assert(ns < CFG_LAST && ns >= 0);
|
||||
if (ns < 0 || ns >= CFG_LAST)
|
||||
{
|
||||
debug_warn("CConfigDB: Invalid ns value");
|
||||
return;
|
||||
}
|
||||
|
||||
m_ConfigFile[ns]=path;
|
||||
m_UseVFS[ns]=useVFS;
|
||||
@ -328,7 +340,11 @@ bool CConfigDB::Reload(EConfigNamespace ns)
|
||||
|
||||
bool CConfigDB::WriteFile(EConfigNamespace ns, bool useVFS, const CStr& path)
|
||||
{
|
||||
debug_assert(ns >= 0 && ns < CFG_LAST);
|
||||
if (ns < 0 || ns >= CFG_LAST)
|
||||
{
|
||||
debug_warn("CConfigDB: Invalid ns value");
|
||||
return false;
|
||||
}
|
||||
|
||||
char realpath[PATH_MAX];
|
||||
char nativepath[PATH_MAX];
|
||||
|
@ -172,7 +172,7 @@ void CXeromyces::Terminate()
|
||||
|
||||
|
||||
// Find out write location of the XMB file corresponding to xmlFilename
|
||||
void CXeromyces::getXMBPath(const char* xmlFilename, const char* xmbFilename,
|
||||
void CXeromyces::GetXMBPath(const char* xmlFilename, const char* xmbFilename,
|
||||
char* xmbPath)
|
||||
{
|
||||
// rationale:
|
||||
@ -257,7 +257,7 @@ PSRETURN CXeromyces::Load(const char* filename)
|
||||
xmbFilename += buf;
|
||||
|
||||
char xmbPath[PATH_MAX];
|
||||
getXMBPath(filename, xmbFilename, xmbPath);
|
||||
GetXMBPath(filename, xmbFilename, xmbPath);
|
||||
|
||||
|
||||
// If the file exists, use it
|
||||
|
@ -19,6 +19,7 @@ ERROR_TYPE(Xeromyces, XMLParseError);
|
||||
|
||||
class CXeromyces : public XMBFile
|
||||
{
|
||||
friend class TestXeromyces;
|
||||
public:
|
||||
CXeromyces();
|
||||
~CXeromyces();
|
||||
@ -32,7 +33,7 @@ public:
|
||||
private:
|
||||
|
||||
// Find out write location of the XMB file corresponding to xmlFilename
|
||||
void getXMBPath(const char* xmlFilename, const char* xmbFilename,
|
||||
static void GetXMBPath(const char* xmlFilename, const char* xmbFilename,
|
||||
char* xmbPath);
|
||||
|
||||
bool ReadXMBFile(const char* filename);
|
||||
|
32
source/ps/XML/tests/test_Xeromyces.h
Normal file
32
source/ps/XML/tests/test_Xeromyces.h
Normal file
@ -0,0 +1,32 @@
|
||||
#include "lib/self_test.h"
|
||||
|
||||
#include "ps/XML/Xeromyces.h"
|
||||
#include "lib/res/file/vfs.h"
|
||||
#include "lib/res/file/path.h"
|
||||
#include "lib/res/file/trace.h"
|
||||
|
||||
class TestXeromyces : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_paths()
|
||||
{
|
||||
file_init();
|
||||
path_init();
|
||||
file_set_root_dir(0, "../data");
|
||||
vfs_init();
|
||||
|
||||
vfs_mount("", "mods/_tests", VFS_MOUNT_RECURSIVE);
|
||||
vfs_set_write_target("mods/_tests");
|
||||
|
||||
char xmbPath[PATH_MAX];
|
||||
|
||||
CXeromyces::GetXMBPath("test1.xml", "test1.xmb", xmbPath);
|
||||
TS_ASSERT_STR_EQUALS(xmbPath, "cache/mods/_tests/xmb/test1.xmb");
|
||||
|
||||
CXeromyces::GetXMBPath("a/b/test1.xml", "a/b/test1.xmb", xmbPath);
|
||||
TS_ASSERT_STR_EQUALS(xmbPath, "cache/mods/_tests/xmb/a/b/test1.xmb");
|
||||
|
||||
vfs_shutdown();
|
||||
path_reset_root_dir();
|
||||
}
|
||||
};
|
@ -8,12 +8,46 @@ class TestCStr : public CxxTest::TestSuite
|
||||
public:
|
||||
void test_utf8_utf16_conversion()
|
||||
{
|
||||
const wchar_t chr_utf16[] = { 0x12, 0xff, 0x1234, 0x3456, 0x5678, 0x7890, 0x9abc, 0xbcde, 0xfffe };
|
||||
const unsigned char chr_utf8[] = { 0x12, 0xc3, 0xbf, 0xe1, 0x88, 0xb4, 0xe3, 0x91, 0x96, 0xe5, 0x99, 0xb8, 0xe7, 0xa2, 0x90, 0xe9, 0xaa, 0xbc, 0xeb, 0xb3, 0x9e, 0xef, 0xbf, 0xbe };
|
||||
const wchar_t chr_utf16[] = {
|
||||
0x12,
|
||||
0xff,
|
||||
0x1234,
|
||||
0x3456,
|
||||
0x5678,
|
||||
0x7890,
|
||||
0x9abc,
|
||||
0xbcde,
|
||||
0xfffe
|
||||
};
|
||||
const unsigned char chr_utf8[] = {
|
||||
0x12,
|
||||
0xc3, 0xbf,
|
||||
0xe1, 0x88, 0xb4,
|
||||
0xe3, 0x91, 0x96,
|
||||
0xe5, 0x99, 0xb8,
|
||||
0xe7, 0xa2, 0x90,
|
||||
0xe9, 0xaa, 0xbc,
|
||||
0xeb, 0xb3, 0x9e,
|
||||
0xef, 0xbf, 0xbe
|
||||
};
|
||||
CStrW str_utf16 (chr_utf16, sizeof(chr_utf16)/sizeof(wchar_t));
|
||||
|
||||
CStr8 str_utf8 = str_utf16.ToUTF8();
|
||||
TS_ASSERT_EQUALS(str_utf8.length(), sizeof(chr_utf8));
|
||||
TS_ASSERT_SAME_DATA(str_utf8.data(), chr_utf8, sizeof(chr_utf8));
|
||||
TS_ASSERT_EQUALS(str_utf8.FromUTF8(), str_utf16);
|
||||
|
||||
CStrW str_utf16b = str_utf8.FromUTF8();
|
||||
TS_ASSERT_EQUALS(str_utf16b, str_utf16);
|
||||
}
|
||||
|
||||
void test_invalid_utf8()
|
||||
{
|
||||
const unsigned char chr_utf8_a[] = { 'a', 0xef };
|
||||
const unsigned char chr_utf8_b[] = { 'b', 0xef, 0xbf };
|
||||
const unsigned char chr_utf8_c[] = { 'c', 0xef, 0xbf, 0x01 };
|
||||
|
||||
TS_ASSERT_WSTR_EQUALS(CStr8((const char*)chr_utf8_a, sizeof(chr_utf8_a)).FromUTF8(), L"");
|
||||
TS_ASSERT_WSTR_EQUALS(CStr8((const char*)chr_utf8_b, sizeof(chr_utf8_b)).FromUTF8(), L"");
|
||||
TS_ASSERT_WSTR_EQUALS(CStr8((const char*)chr_utf8_c, sizeof(chr_utf8_c)).FromUTF8(), L"");
|
||||
}
|
||||
};
|
||||
|
@ -625,11 +625,12 @@ JSBool startXTimer(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval
|
||||
|
||||
JSU_REQUIRE_PARAMS(1);
|
||||
uint slot = ToPrimitive<uint>(argv[0]);
|
||||
debug_assert(slot < MAX_XTIMERS);
|
||||
if (slot >= MAX_XTIMERS)
|
||||
return JS_FALSE;
|
||||
|
||||
debug_assert(xstart_times[slot] == 0);
|
||||
xstart_times[slot] = xtimer_impl.get_timestamp();
|
||||
return( JS_TRUE );
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
|
||||
@ -637,13 +638,14 @@ JSBool stopXTimer(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
|
||||
{
|
||||
JSU_REQUIRE_PARAMS(1);
|
||||
uint slot = ToPrimitive<uint>(argv[0]);
|
||||
debug_assert(slot < MAX_XTIMERS);
|
||||
if (slot >= MAX_XTIMERS)
|
||||
return JS_FALSE;
|
||||
|
||||
debug_assert(xstart_times[slot] != 0);
|
||||
XTimerImpl::unit dt = xtimer_impl.get_timestamp() - xstart_times[slot] - xoverhead;
|
||||
xstart_times[slot] = 0;
|
||||
timer_bill_client(&xclients[slot], dt);
|
||||
return( JS_TRUE );
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
@ -4,6 +4,10 @@
|
||||
# define HAVE_PCH
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||
# pragma warning(disable: 6334)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PCH
|
||||
|
||||
// Exclude rarely-used stuff from Windows headers
|
||||
|
Loading…
Reference in New Issue
Block a user