QUtil: add unsigned int/string functions

This commit is contained in:
Jay Berkenbilt 2019-06-20 19:48:53 -04:00
parent a66828caff
commit 42306e2ff8
5 changed files with 119 additions and 35 deletions

View File

@ -1,5 +1,9 @@
2019-06-20 Jay Berkenbilt <ejb@ql.org>
* Add functions to QUtil to convert unsigned integers to strings,
avoiding implicit conversion between unsigned and signed integer
types.
* Add QIC.hh, containing integer type converters that do range
checking.

View File

@ -40,8 +40,12 @@ namespace QUtil
QPDF_DLL
std::string int_to_string(long long, int length = 0);
QPDF_DLL
std::string uint_to_string(unsigned long long, int length = 0);
QPDF_DLL
std::string int_to_string_base(long long, int base, int length = 0);
QPDF_DLL
std::string uint_to_string_base(unsigned long long, int base, int length = 0);
QPDF_DLL
std::string double_to_string(double, int decimal_places = 0);
// These string to number methods throw std::runtime_error on
@ -50,6 +54,10 @@ namespace QUtil
long long string_to_ll(char const* str);
QPDF_DLL
int string_to_int(char const* str);
QPDF_DLL
unsigned long long string_to_ull(char const* str);
QPDF_DLL
unsigned int string_to_uint(char const* str);
// Pipeline's write method wants unsigned char*, but we often have
// some other type of string. These methods do combinations of

View File

@ -9,6 +9,7 @@
#include <qpdf/SecureRandomDataProvider.hh>
#include <qpdf/QPDFSystemError.hh>
#include <qpdf/QTC.hh>
#include <qpdf/QIntC.hh>
#include <cmath>
#include <iomanip>
@ -233,14 +234,10 @@ static unsigned short mac_roman_to_unicode[] = {
0x02c7, // 0xff
};
template <typename T>
static
std::string
QUtil::int_to_string(long long num, int length)
{
return int_to_string_base(num, 10, length);
}
std::string
QUtil::int_to_string_base(long long num, int base, int length)
int_to_string_base_internal(T num, int base, int length)
{
// Backward compatibility -- int_to_string, which calls this
// function, used to use sprintf with %0*d, so we interpret length
@ -255,18 +252,42 @@ QUtil::int_to_string_base(long long num, int base, int length)
buf << std::setbase(base) << std::nouppercase << num;
std::string result;
if ((length > 0) &&
(buf.str().length() < static_cast<size_t>(length)))
(buf.str().length() < QIntC::to_size(length)))
{
result.append(length - buf.str().length(), '0');
}
result += buf.str();
if ((length < 0) && (buf.str().length() < static_cast<size_t>(-length)))
if ((length < 0) && (buf.str().length() < QIntC::to_size(-length)))
{
result.append(-length - buf.str().length(), ' ');
}
return result;
}
std::string
QUtil::int_to_string(long long num, int length)
{
return int_to_string_base(num, 10, length);
}
std::string
QUtil::uint_to_string(unsigned long long num, int length)
{
return int_to_string_base(num, 10, length);
}
std::string
QUtil::int_to_string_base(long long num, int base, int length)
{
return int_to_string_base_internal(num, base, length);
}
std::string
QUtil::uint_to_string_base(unsigned long long num, int base, int length)
{
return int_to_string_base_internal(num, base, length);
}
std::string
QUtil::double_to_string(double num, int decimal_places)
{
@ -294,7 +315,7 @@ QUtil::string_to_ll(char const* str)
#endif
if (errno == ERANGE)
{
throw std::runtime_error(
throw std::range_error(
std::string("overflow/underflow converting ") + str
+ " to 64-bit integer");
}
@ -304,24 +325,47 @@ QUtil::string_to_ll(char const* str)
int
QUtil::string_to_int(char const* str)
{
// QIntC::to_int does range checking
return QIntC::to_int(string_to_ll(str));
}
unsigned long long
QUtil::string_to_ull(char const* str)
{
char const* p = str;
while (*p && is_space(*p))
{
++p;
}
if (*p == '-')
{
throw std::runtime_error(
std::string("underflow converting ") + str
+ " to 64-bit unsigned integer");
}
errno = 0;
long long_val = strtol(str, 0, 10);
#ifdef _MSC_VER
unsigned long long result = _strtoui64(str, 0, 10);
#else
unsigned long long result = strtoull(str, 0, 10);
#endif
if (errno == ERANGE)
{
throw std::runtime_error(
std::string("overflow/underflow converting ") + str
+ " to long integer");
}
int result = static_cast<int>(long_val);
if (static_cast<long>(result) != long_val)
{
throw std::runtime_error(
std::string("overflow/underflow converting ") + str
+ " to integer");
std::string("overflow converting ") + str
+ " to 64-bit unsigned integer");
}
return result;
}
unsigned int
QUtil::string_to_uint(char const* str)
{
// QIntC::to_uint does range checking
return QIntC::to_uint(string_to_ull(str));
}
unsigned char*
QUtil::unsigned_char_pointer(std::string const& str)
{
@ -412,14 +456,18 @@ int
QUtil::seek(FILE* stream, qpdf_offset_t offset, int whence)
{
#if HAVE_FSEEKO
return fseeko(stream, static_cast<off_t>(offset), whence);
return fseeko(stream,
QIntC::IntConverter<qpdf_offset_t, off_t>::convert(offset),
whence);
#elif HAVE_FSEEKO64
return fseeko64(stream, offset, whence);
#else
# if defined _MSC_VER || defined __BORLANDC__
return _fseeki64(stream, offset, whence);
# else
return fseek(stream, static_cast<long>(offset), whence);
return fseek(stream,
QIntC::IntConverter<qpdf_offset_t, long>(offset),
whence);
# endif
#endif
}
@ -428,14 +476,14 @@ qpdf_offset_t
QUtil::tell(FILE* stream)
{
#if HAVE_FSEEKO
return static_cast<qpdf_offset_t>(ftello(stream));
return QIntC::to_offset(ftello(stream));
#elif HAVE_FSEEKO64
return static_cast<qpdf_offset_t>(ftello64(stream));
return QIntC::to_offset(ftello64(stream));
#else
# if defined _MSC_VER || defined __BORLANDC__
return _ftelli64(stream);
# else
return static_cast<qpdf_offset_t>(ftell(stream));
return QIntC::to_offset(ftell(stream));
# endif
#endif
}
@ -508,7 +556,7 @@ QUtil::hex_encode(std::string const& input)
for (unsigned int i = 0; i < input.length(); ++i)
{
result += QUtil::int_to_string_base(
static_cast<int>(static_cast<unsigned char>(input.at(i))), 16, 2);
QIntC::to_int(static_cast<unsigned char>(input.at(i))), 16, 2);
}
return result;
}

View File

@ -17,12 +17,17 @@ one
compare okay
-2147483648 to int: PASSED
2147483647 to int: PASSED
2147483648 to int threw: PASSED
-2147483649 to int threw: PASSED
9999999999999999999999999 to int threw: PASSED
2147483648 to int threw (integer out of range converting 2147483648 from a 8-byte signed type to a 4-byte signed type): PASSED
-2147483649 to int threw (integer out of range converting -2147483649 from a 8-byte signed type to a 4-byte signed type): PASSED
9999999999999999999999999 to int threw (overflow/underflow converting 9999999999999999999999999 to 64-bit integer): PASSED
2147483648 to int: PASSED
-2147483649 to int: PASSED
99999999999999999999999999999999999999999999999999 to int threw: PASSED
99999999999999999999999999999999999999999999999999 to int threw (overflow/underflow converting 99999999999999999999999999999999999999999999999999 to 64-bit integer): PASSED
16059 to int: PASSED
-16059 to int threw (underflow converting -16059 to 64-bit unsigned integer): PASSED
9999999999 to int threw (integer out of range converting 9999999999 from a 8-byte unsigned type to a 4-byte unsigned type): PASSED
16059 to int: PASSED
-16059 to int threw (underflow converting -16059 to 64-bit unsigned integer): PASSED
---- os wrapper
before remove
exception: remove file: No such file or directory

View File

@ -23,20 +23,22 @@ void test_to_number(char const* str, int_T wanted, bool error,
bool threw = false;
bool worked = false;
int_T result = 0;
std::string msg;
try
{
result = fn(str);
worked = (wanted == result);
}
catch (std::runtime_error const&)
catch (std::runtime_error const& e)
{
threw = true;
msg = e.what();
}
if (threw)
{
if (error)
{
std::cout << str << " to int threw: PASSED" << std::endl;
std::cout << str << " to int threw (" << msg << "): PASSED" << std::endl;
}
else
{
@ -67,6 +69,16 @@ void test_to_ll(char const* str, long long wanted, bool error)
test_to_number(str, wanted, error, QUtil::string_to_ll);
}
void test_to_uint(char const* str, unsigned int wanted, bool error)
{
test_to_number(str, wanted, error, QUtil::string_to_uint);
}
void test_to_ull(char const* str, unsigned long long wanted, bool error)
{
test_to_number(str, wanted, error, QUtil::string_to_ull);
}
void string_conversion_test()
{
std::cout << QUtil::int_to_string(16059) << std::endl
@ -105,6 +117,8 @@ void string_conversion_test()
long long int_min_minus_1 = static_cast<long long>(INT_MIN) - 1;
std::string int_max_plus_1_str = QUtil::int_to_string(int_max_plus_1);
std::string int_min_minus_1_str = QUtil::int_to_string(int_min_minus_1);
std::string small_positive = QUtil::uint_to_string(16059U);
std::string small_negative = QUtil::int_to_string(-16059);
test_to_int(int_min_str.c_str(), INT_MIN, false);
test_to_int(int_max_str.c_str(), INT_MAX, false);
test_to_int(int_max_plus_1_str.c_str(), 0, true);
@ -113,6 +127,11 @@ void string_conversion_test()
test_to_ll(int_max_plus_1_str.c_str(), int_max_plus_1, false);
test_to_ll(int_min_minus_1_str.c_str(), int_min_minus_1, false);
test_to_ll("99999999999999999999999999999999999999999999999999", 0, true);
test_to_uint(small_positive.c_str(), 16059U, false);
test_to_uint(small_negative.c_str(), 0, true);
test_to_uint("9999999999", 0, true);
test_to_ull(small_positive.c_str(), 16059U, false);
test_to_ull(small_negative.c_str(), 0, true);
}
void os_wrapper_test()
@ -159,7 +178,7 @@ void getenv_test()
static void print_utf8(unsigned long val)
{
std::string result = QUtil::toUTF8(val);
std::cout << "0x" << QUtil::int_to_string_base(val, 16) << " ->";
std::cout << "0x" << QUtil::uint_to_string_base(val, 16) << " ->";
if (val < 0xfffe)
{
std::cout << " " << result;
@ -199,7 +218,7 @@ void to_utf8_test()
static void print_utf16(unsigned long val)
{
std::string result = QUtil::toUTF16(val);
std::cout << "0x" << QUtil::int_to_string_base(val, 16) << " ->";
std::cout << "0x" << QUtil::uint_to_string_base(val, 16) << " ->";
for (std::string::iterator iter = result.begin();
iter != result.end(); ++iter)
{
@ -249,7 +268,7 @@ void transcoding_test(std::string (*to_utf8)(std::string const&),
std::string back;
for (int i = 128; i <= last; ++i)
{
in.at(0) = static_cast<unsigned char>(i);
in.at(0) = static_cast<char>(static_cast<unsigned char>(i));
out = (*to_utf8)(in);
std::string wanted = (out == "\xef\xbf\xbd") ? unknown : in;
back = (*from_utf8)(out, '?');