2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-05-31 09:20:52 +00:00

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> 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 * Add QIC.hh, containing integer type converters that do range
checking. checking.

View File

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

View File

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

View File

@ -17,12 +17,17 @@ one
compare okay compare okay
-2147483648 to int: PASSED -2147483648 to int: PASSED
2147483647 to int: PASSED 2147483647 to int: PASSED
2147483648 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: 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: PASSED 9999999999999999999999999 to int threw (overflow/underflow converting 9999999999999999999999999 to 64-bit integer): PASSED
2147483648 to int: PASSED 2147483648 to int: PASSED
-2147483649 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 ---- os wrapper
before remove before remove
exception: remove file: No such file or directory 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 threw = false;
bool worked = false; bool worked = false;
int_T result = 0; int_T result = 0;
std::string msg;
try try
{ {
result = fn(str); result = fn(str);
worked = (wanted == result); worked = (wanted == result);
} }
catch (std::runtime_error const&) catch (std::runtime_error const& e)
{ {
threw = true; threw = true;
msg = e.what();
} }
if (threw) if (threw)
{ {
if (error) if (error)
{ {
std::cout << str << " to int threw: PASSED" << std::endl; std::cout << str << " to int threw (" << msg << "): PASSED" << std::endl;
} }
else 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); 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() void string_conversion_test()
{ {
std::cout << QUtil::int_to_string(16059) << std::endl 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; 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_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 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_min_str.c_str(), INT_MIN, false);
test_to_int(int_max_str.c_str(), INT_MAX, false); test_to_int(int_max_str.c_str(), INT_MAX, false);
test_to_int(int_max_plus_1_str.c_str(), 0, true); 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_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(int_min_minus_1_str.c_str(), int_min_minus_1, false);
test_to_ll("99999999999999999999999999999999999999999999999999", 0, true); 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() void os_wrapper_test()
@ -159,7 +178,7 @@ void getenv_test()
static void print_utf8(unsigned long val) static void print_utf8(unsigned long val)
{ {
std::string result = QUtil::toUTF8(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) if (val < 0xfffe)
{ {
std::cout << " " << result; std::cout << " " << result;
@ -199,7 +218,7 @@ void to_utf8_test()
static void print_utf16(unsigned long val) static void print_utf16(unsigned long val)
{ {
std::string result = QUtil::toUTF16(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(); for (std::string::iterator iter = result.begin();
iter != result.end(); ++iter) iter != result.end(); ++iter)
{ {
@ -249,7 +268,7 @@ void transcoding_test(std::string (*to_utf8)(std::string const&),
std::string back; std::string back;
for (int i = 128; i <= last; ++i) 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); out = (*to_utf8)(in);
std::string wanted = (out == "\xef\xbf\xbd") ? unknown : in; std::string wanted = (out == "\xef\xbf\xbd") ? unknown : in;
back = (*from_utf8)(out, '?'); back = (*from_utf8)(out, '?');