diff --git a/ChangeLog b/ChangeLog index 8cf93186..a194440a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-05-15 Jay Berkenbilt + + * Add QUtil::is_long_long to test whether a string can be + converted to a long long and back without loss of information. + 2022-05-04 Jay Berkenbilt * JSON: add a new "blob" type that takes a function to write data diff --git a/include/qpdf/QUtil.hh b/include/qpdf/QUtil.hh index 8b2b5ff8..5d33b2a5 100644 --- a/include/qpdf/QUtil.hh +++ b/include/qpdf/QUtil.hh @@ -65,6 +65,13 @@ namespace QUtil QPDF_DLL unsigned int string_to_uint(char const* str); + // Returns true if this exactly represents a long long. The + // determination is made by converting the string to a long long, + // then converting the result back to a string, and then comparing + // that result with the original string. + QPDF_DLL + bool is_long_long(char const* str); + // Pipeline's write method wants unsigned char*, but we often have // some other type of string. These methods do combinations of // const_cast and reinterpret_cast to give us an unsigned char*. diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc index 22962199..8edfadae 100644 --- a/libqpdf/QUtil.cc +++ b/libqpdf/QUtil.cc @@ -467,6 +467,19 @@ QUtil::string_to_uint(char const* str) return QIntC::to_uint(string_to_ull(str)); } +bool +QUtil::is_long_long(char const* str) +{ + try { + auto i1 = string_to_ll(str); + std::string s1 = int_to_string(i1); + return str == s1; + } catch (std::exception&) { + // overflow or other error + } + return false; +} + unsigned char* QUtil::unsigned_char_pointer(std::string const& str) { diff --git a/libtests/qtest/qutil/qutil.out b/libtests/qtest/qutil/qutil.out index e9c53170..d91acba9 100644 --- a/libtests/qtest/qutil/qutil.out +++ b/libtests/qtest/qutil/qutil.out @@ -129,3 +129,5 @@ delete file D:20210209144925-05'00' D:20210210011925+05'30' D:20210209191925Z +---- is_long_long +done diff --git a/libtests/qutil.cc b/libtests/qutil.cc index f79f9a3f..ea0aece7 100644 --- a/libtests/qutil.cc +++ b/libtests/qutil.cc @@ -676,6 +676,29 @@ timestamp_test() QUtil::qpdf_time_to_pdf_time(QUtil::get_current_qpdf_time()))); } +void +is_long_long_test() +{ + auto check = [](char const* s, bool v) { + if (QUtil::is_long_long(s) != v) { + std::cout << "failed: " << s << std::endl; + } + }; + check("12312312", true); + check("12312312.34", false); + check("-12312312", true); + check("-12312312.34", false); + check("1e2", false); + check("9223372036854775807", true); + check("9223372036854775808", false); + check("-9223372036854775808", true); + check("-9223372036854775809", false); + check("123123123123123123123123123123123123", false); + check("potato", false); + check("0123", false); + std::cout << "done" << std::endl; +} + int main(int argc, char* argv[]) { @@ -710,6 +733,8 @@ main(int argc, char* argv[]) rename_delete_test(); std::cout << "---- timestamp" << std::endl; timestamp_test(); + std::cout << "---- is_long_long" << std::endl; + is_long_long_test(); } catch (std::exception& e) { std::cout << "unexpected exception: " << e.what() << std::endl; }