From 660a1e010983b9e0b3c13a45cf622bf728e18064 Mon Sep 17 00:00:00 2001 From: mrabine Date: Wed, 28 Oct 2026 19:23:16 +0100 Subject: [PATCH 1/2] Add join probes with string and stream support --- dconv/include/dconv/probe.hpp | 455 ++++++++++++++++++++++++++++-- dconv/tests/CMakeLists.txt | 4 + dconv/tests/stream_probe_test.cpp | 248 ++++++++++++++++ dconv/tests/string_probe_test.cpp | 86 ++++++ 4 files changed, 773 insertions(+), 20 deletions(-) create mode 100644 dconv/tests/stream_probe_test.cpp diff --git a/dconv/include/dconv/probe.hpp b/dconv/include/dconv/probe.hpp index cdaf23c..da31342 100644 --- a/dconv/include/dconv/probe.hpp +++ b/dconv/include/dconv/probe.hpp @@ -26,6 +26,8 @@ #define __DCONV_PROBE_HPP__ // C++. +#include +#include #include #include @@ -39,6 +41,43 @@ namespace dconv { + namespace details + { + struct alignas (64) EscapedTable + { + uint8_t data[256]; + + constexpr EscapedTable () + : data{} + { + for (unsigned i = 0; i < 0x20; ++i) + { + data[i] = 1; + } + data[static_cast ('"')] = 1; + data[static_cast ('\\')] = 1; + } + }; + + constexpr EscapedTable escapedLookup{}; + + struct alignas (64) WhitespaceTable + { + uint8_t data[256]; + + constexpr WhitespaceTable () + : data{} + { + data[static_cast ('\t')] = 1; + data[static_cast ('\n')] = 1; + data[static_cast ('\r')] = 1; + data[static_cast (' ')] = 1; + } + }; + + constexpr WhitespaceTable whitespaceLookup{}; + } + /** * @brief string probe. */ @@ -49,20 +88,20 @@ namespace dconv /** * @brief default constructor. - * @param s pointer to a character array. - * @param count number of characters in the sequence. + * @param in input string. + * @param count number of characters. */ - explicit constexpr StringProbe (const char* s, size_t count) - : _cur (s) - , _beg (s) - , _end (s ? s + count : s) + constexpr StringProbe (const char* in, size_t count) + : _cur (in) + , _beg (in) + , _end (in ? in + count : in) { } /** * @brief default constructor. - * @param first pointer to the first character of the sequence. - * @param last pointer to the last character of the sequence. + * @param first pointer to the first character of the string. + * @param last pointer to the last character of the string. */ constexpr StringProbe (const char* first, const char* last) : _cur (first) @@ -73,12 +112,12 @@ namespace dconv /** * @brief default constructor. - * @param s pointer to a character array. + * @param in input string. */ - explicit StringProbe (const char* s) - : _cur (s) - , _beg (s) - , _end (s ? s + std::char_traits::length (s) : s) + StringProbe (const char* in) + : _cur (in) + , _beg (in) + , _end (in ? in + std::char_traits::length (in) : in) { } @@ -86,27 +125,27 @@ namespace dconv * @brief copy constructor. * @param other object to copy. */ - StringProbe (const StringProbe& other) noexcept = default; + StringProbe (const StringProbe& other) = default; /** * @brief copy assignment. * @param other object to copy. * @return a reference of the current object. */ - StringProbe& operator= (const StringProbe& other) noexcept = default; + StringProbe& operator= (const StringProbe& other) = default; /** * @brief move constructor. * @param other object to move. */ - StringProbe (StringProbe&& other) noexcept = default; + StringProbe (StringProbe&& other) = default; /** * @brief move assignment. * @param other object to move. * @return a reference of the current object. */ - StringProbe& operator= (StringProbe&& other) noexcept = default; + StringProbe& operator= (StringProbe&& other) = default; /** * @brief destroy instance. @@ -140,7 +179,7 @@ namespace dconv } /** - * @brief extracts expected character (case sensitive). + * @brief extracts expected character. * @param expected expected character. * @return true if extracted, false otherwise. */ @@ -186,7 +225,7 @@ namespace dconv uint64_t word; while ((end - cur >= 8) && - (memcpy (&word, cur, 8), + (std::memcpy (&word, cur, 8), ((word & 0xF0F0F0F0F0F0F0F0ULL) | (((word + 0x0606060606060606ULL) & 0xF0F0F0F0F0F0F0F0ULL) >> 4)) == 0x3333333333333333ULL)) { @@ -207,6 +246,154 @@ namespace dconv return count; } + /** + * @brief read characters. + * @param buf output buffer. + * @param count number of characters to read. + * @return number of characters read. + */ + inline size_t read (char* buf, size_t count) noexcept + { + const size_t available = _end - _cur; + const size_t nread = (count < available) ? count : available; + std::memcpy (buf, _cur, nread); + _cur += nread; + return nread; + } + + /** + * @brief read characters until escaped, without copying them. + * @return characters read. + */ + inline std::string_view readUntilEscaped () noexcept + { + const char* beg = _cur; + const char* cur = _cur; + const char* end = _end; + + while (end - cur >= 8) + { + uint64_t word, quote, backslash; + std::memcpy (&word, cur, 8); + quote = word ^ 0x2222222222222222ULL; + backslash = word ^ 0x5C5C5C5C5C5C5C5CULL; + uint64_t mask = + (((quote - 0x0101010101010101ULL) & ~quote) | ((backslash - 0x0101010101010101ULL) & ~backslash) | + ((word - 0x2020202020202020ULL) & ~word)) & + 0x8080808080808080ULL; + if (mask) + { + cur += __builtin_ctzll (mask) >> 3; + break; + } + cur += 8; + } + + while (cur < end && !details::escapedLookup.data[static_cast (*cur)]) + { + ++cur; + } + + _cur = cur; + return std::string_view (beg, static_cast (cur - beg)); + } + + /** + * @brief read characters until escaped. + * @param out output buffer. + */ + inline void readUntilEscaped (std::string& out) noexcept + { + out.append (readUntilEscaped ()); + } + + /** + * @brief skip whitespaces. + * @return 0 on success, -1 otherwise. + */ + inline int skipWhitespaces () noexcept + { + const char* cur = _cur; + const char* end = _end; + + while (cur < end && details::whitespaceLookup.data[static_cast (*cur)]) + { + ++cur; + + uint64_t word; + while ((end - cur >= 8) && (std::memcpy (&word, cur, 8), word == 0x2020202020202020ULL)) + { + cur += 8; + } + } + + _cur = cur; + + return 0; + } + + /** + * @brief skip whitespaces and comments. + * @return 0 on success, -1 otherwise. + */ + inline int skipWhitespacesAndComments () noexcept + { + const char* cur = _cur; + const char* end = _end; + + while (cur < end) + { + while (cur < end && details::whitespaceLookup.data[static_cast (*cur)]) + { + ++cur; + } + + if (cur >= end || *cur != '/') + { + break; + } + + if (++cur >= end) + { + return -1; + } + + if (*cur == '*') + { + ++cur; + bool closed = false; + while (cur < end) + { + if (*cur == '*' && (cur + 1 < end) && *(cur + 1) == '/') + { + cur += 2; + closed = true; + break; + } + ++cur; + } + if (!closed) + { + return -1; + } + } + else if (*cur == '/') + { + ++cur; + const char* p = static_cast (memchr (cur, '\n', end - cur)); + cur = p ? p : end; + } + else + { + return -1; + } + } + + _cur = cur; + + return 0; + } + /** * @brief get input position indicator. * @return current position. @@ -243,9 +430,237 @@ namespace dconv /// beginning position. const char* _beg = nullptr; - /// end position. + /// ending position. const char* _end = nullptr; }; + + /** + * @brief stream probe. + */ + class StreamProbe + { + public: + /** + * @brief default constructor. + * @param in input stream. + */ + StreamProbe (std::istream& in) + : _in (in.rdbuf ()) + { + } + + /** + * @brief copy constructor. + * @param other object to copy. + */ + StreamProbe (const StreamProbe& other) = delete; + + /** + * @brief copy assignment. + * @param other object to copy. + * @return a reference of the current object. + */ + StreamProbe& operator= (const StreamProbe& other) = delete; + + /** + * @brief move constructor. + * @param other object to move. + */ + StreamProbe (StreamProbe&& other) = delete; + + /** + * @brief move assignment. + * @param other object to move. + * @return a reference of the current object. + */ + StreamProbe& operator= (StreamProbe&& other) = delete; + + /** + * @brief destroy instance. + */ + ~StreamProbe () = default; + + /** + * @brief get character without extracting it. + * @return extracted character. + */ + inline int peek () const noexcept + { + return _in->sgetc (); + } + + /** + * @brief extracts character. + * @return extracted character. + */ + inline int get () noexcept + { + return _in->sbumpc (); + } + + /** + * @brief extracts expected character. + * @param expected expected character. + * @return true if extracted, false otherwise. + */ + inline bool getIf (char expected) noexcept + { + if (_in->sgetc () == static_cast (static_cast (expected))) + { + _in->sbumpc (); + return true; + } + return false; + } + + /** + * @brief extracts expected character (case insensitive, ASCII-only). + * @param expected expected character. + * @return true if extracted, false otherwise. + */ + inline bool getIfNoCase (char expected) noexcept + { + const int c = _in->sgetc (); + if (DCONV_LIKELY (c != std::char_traits::eof ())) + { + if ((static_cast (c) | 32) == (expected | 32)) + { + _in->sbumpc (); + return true; + } + } + return false; + } + + /** + * @brief read consecutive digits. + * @param value value the digits are appended to. + * @param max maximum number of digits to read. + * @return number of digits read. + */ + inline size_t readDigits (uint64_t& value, size_t max = std::numeric_limits::max ()) noexcept + { + size_t count = 0; + while ((count < max) && (static_cast (peek () - '0') < 10)) + { + value = (value * 10) + (get () - '0'); + ++count; + } + return count; + } + + /** + * @brief read characters. + * @param buf output buffer. + * @param count number of characters to read. + * @return number of characters read. + */ + inline size_t read (char* buf, size_t count) noexcept + { + return _in->sgetn (buf, count); + } + + /** + * @brief read characters until escaped. + * @param out output buffer. + */ + inline void readUntilEscaped (std::string& out) noexcept + { + int c; + while ((c = _in->sgetc ()) != std::char_traits::eof () && + !details::escapedLookup.data[static_cast (c)]) + { + out.push_back (static_cast (c)); + _in->sbumpc (); + } + } + + /** + * @brief skip whitespaces. + * @return 0 on success, -1 otherwise. + */ + inline int skipWhitespaces () noexcept + { + int c; + while ((c = _in->sgetc ()) != std::char_traits::eof () && + details::whitespaceLookup.data[static_cast (c)]) + { + _in->sbumpc (); + } + + return 0; + } + + /** + * @brief skip whitespaces and comments. + * @return 0 on success, -1 otherwise. + */ + inline int skipWhitespacesAndComments () noexcept + { + int c; + + while ((c = _in->sgetc ()) != std::char_traits::eof ()) + { + while ((c = _in->sgetc ()) != std::char_traits::eof () && + details::whitespaceLookup.data[static_cast (c)]) + { + _in->sbumpc (); + } + + if (c != '/') + { + break; + } + + _in->sbumpc (); + c = _in->sgetc (); + + if (c == std::char_traits::eof ()) + { + return -1; + } + + if (c == '*') + { + _in->sbumpc (); + bool closed = false; + + while ((c = _in->sbumpc ()) != std::char_traits::eof ()) + { + if (c == '*' && _in->sgetc () == '/') + { + _in->sbumpc (); + closed = true; + break; + } + } + + if (!closed) + { + return -1; + } + } + else if (c == '/') + { + _in->sbumpc (); + + while ((c = _in->sbumpc ()) != std::char_traits::eof () && c != '\n') + { + } + } + else + { + return -1; + } + } + + return 0; + } + + private: + /// input stream buffer. + std::streambuf* _in; + }; } #endif diff --git a/dconv/tests/CMakeLists.txt b/dconv/tests/CMakeLists.txt index db50442..7dccca8 100644 --- a/dconv/tests/CMakeLists.txt +++ b/dconv/tests/CMakeLists.txt @@ -6,6 +6,10 @@ add_executable(string_probe.gtest string_probe_test.cpp) target_link_libraries(string_probe.gtest ${PROJECT_NAME} GTest::gtest_main pthread) gtest_discover_tests(string_probe.gtest) +add_executable(stream_probe.gtest stream_probe_test.cpp) +target_link_libraries(stream_probe.gtest ${PROJECT_NAME} GTest::gtest_main pthread) +gtest_discover_tests(stream_probe.gtest) + add_executable(atod.gtest atod_test.cpp) target_link_libraries(atod.gtest ${PROJECT_NAME} GTest::gtest_main pthread) gtest_discover_tests(atod.gtest) diff --git a/dconv/tests/stream_probe_test.cpp b/dconv/tests/stream_probe_test.cpp new file mode 100644 index 0000000..bf6825f --- /dev/null +++ b/dconv/tests/stream_probe_test.cpp @@ -0,0 +1,248 @@ +/** + * MIT License + * + * Copyright (c) 2021 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// dconv. +#include + +// libraries. +#include + +// C++. +#include +#include + +using dconv::StreamProbe; + +/** + * @brief create test. + */ +TEST (StreamProbe, create) +{ + std::stringstream msg ("hello world"); + StreamProbe probe (msg); + + ASSERT_NE (probe.peek (), std::char_traits::eof ()); +} + +/** + * @brief peek test. + */ +TEST (StreamProbe, peek) +{ + std::stringstream msg ("hello world"); + StreamProbe probe (msg); + + ASSERT_EQ (probe.peek (), 'h'); + ASSERT_EQ (probe.get (), 'h'); + ASSERT_EQ (probe.peek (), 'e'); + ASSERT_EQ (probe.get (), 'e'); + ASSERT_EQ (probe.peek (), 'l'); + ASSERT_EQ (probe.get (), 'l'); + ASSERT_EQ (probe.peek (), 'l'); + ASSERT_EQ (probe.get (), 'l'); + ASSERT_EQ (probe.peek (), 'o'); + ASSERT_EQ (probe.get (), 'o'); + ASSERT_EQ (probe.peek (), ' '); + ASSERT_EQ (probe.get (), ' '); + ASSERT_EQ (probe.peek (), 'w'); + ASSERT_EQ (probe.get (), 'w'); + ASSERT_EQ (probe.peek (), 'o'); + ASSERT_EQ (probe.get (), 'o'); + ASSERT_EQ (probe.peek (), 'r'); + ASSERT_EQ (probe.get (), 'r'); + ASSERT_EQ (probe.peek (), 'l'); + ASSERT_EQ (probe.get (), 'l'); + ASSERT_EQ (probe.peek (), 'd'); + ASSERT_EQ (probe.get (), 'd'); + ASSERT_EQ (probe.peek (), std::char_traits::eof ()); +} + +/** + * @brief get test. + */ +TEST (StreamProbe, get) +{ + std::stringstream msg ("hello world"); + StreamProbe probe (msg); + + ASSERT_EQ (probe.get (), 'h'); + ASSERT_EQ (probe.get (), 'e'); + ASSERT_EQ (probe.get (), 'l'); + ASSERT_EQ (probe.get (), 'l'); + ASSERT_EQ (probe.get (), 'o'); + ASSERT_EQ (probe.get (), ' '); + ASSERT_EQ (probe.get (), 'w'); + ASSERT_EQ (probe.get (), 'o'); + ASSERT_EQ (probe.get (), 'r'); + ASSERT_EQ (probe.get (), 'l'); + ASSERT_EQ (probe.get (), 'd'); + ASSERT_EQ (probe.get (), std::char_traits::eof ()); +} + +/** + * @brief getIf test. + */ +TEST (StreamProbe, getIf) +{ + std::stringstream msg ("hello world"); + StreamProbe probe (msg); + + ASSERT_FALSE (probe.getIf ('X')); + ASSERT_FALSE (probe.getIf ('x')); + ASSERT_FALSE (probe.getIf ('H')); + ASSERT_TRUE (probe.getIf ('h')); + ASSERT_FALSE (probe.getIf ('E')); + ASSERT_TRUE (probe.getIf ('e')); +} + +/** + * @brief getIfNoCase test. + */ +TEST (StreamProbe, getIfNoCase) +{ + std::stringstream msg ("hello world"); + StreamProbe probe (msg); + + ASSERT_FALSE (probe.getIfNoCase ('x')); + ASSERT_FALSE (probe.getIfNoCase ('X')); + ASSERT_TRUE (probe.getIfNoCase ('h')); + ASSERT_TRUE (probe.getIfNoCase ('E')); + ASSERT_TRUE (probe.getIfNoCase ('l')); + ASSERT_TRUE (probe.getIfNoCase ('L')); +} + +/** + * @brief readDigits test. + */ +TEST (StreamProbe, readDigits) +{ + std::stringstream msg ("1234567890123456789x"); + StreamProbe probe (msg); + uint64_t value = 0; + + ASSERT_EQ (probe.readDigits (value), 19); + ASSERT_EQ (value, 1234567890123456789ULL); + ASSERT_EQ (probe.peek (), 'x'); + + std::stringstream limited ("123456789012"); + StreamProbe probe2 (limited); + value = 7; + ASSERT_EQ (probe2.readDigits (value, 10), 10); + ASSERT_EQ (value, 71234567890ULL); + ASSERT_EQ (probe2.peek (), '1'); +} + +/** + * @brief read test. + */ +TEST (StreamProbe, read) +{ + std::stringstream msg ("hello world"); + StreamProbe probe (msg); + char buf[8] = {}; + + ASSERT_EQ (probe.read (buf, 5), 5); + ASSERT_EQ (probe.read (buf, 8), 6); + ASSERT_EQ (probe.read (buf, 8), 0); +} + +/** + * @brief readUntilEscaped test. + */ +TEST (StreamProbe, readUntilEscaped) +{ + std::stringstream msg ("hello\"world"); + StreamProbe probe (msg); + std::string out; + + probe.readUntilEscaped (out); + ASSERT_EQ (out, "hello"); + ASSERT_EQ (probe.peek (), '"'); +} + +/** + * @brief skipWhitespaces test. + */ +TEST (StreamProbe, skipWhitespaces) +{ + std::stringstream msg (" hello"); + StreamProbe probe (msg); + std::string out; + + probe.skipWhitespaces (); + ASSERT_EQ (probe.peek (), 'h'); +} + +/** + * @brief skipWhitespacesAndComments test. + */ +TEST (StreamProbe, skipWhitespacesAndComments) +{ + std::stringstream msg (" // comment\nhello"); + StreamProbe probe1 (msg); + ASSERT_EQ (probe1.skipWhitespacesAndComments (), 0); + ASSERT_EQ (probe1.peek (), 'h'); + + msg.str (" /* comment */hello"); + msg.clear (); + StreamProbe probe2 (msg); + ASSERT_EQ (probe2.skipWhitespacesAndComments (), 0); + ASSERT_EQ (probe2.peek (), 'h'); + + msg.str ("// comment"); + msg.clear (); + StreamProbe probe3 (msg); + ASSERT_EQ (probe3.skipWhitespacesAndComments (), 0); + ASSERT_EQ (probe3.peek (), std::char_traits::eof ()); + + msg.str ("/* comment */"); + msg.clear (); + StreamProbe probe4 (msg); + ASSERT_EQ (probe4.skipWhitespacesAndComments (), 0); + ASSERT_EQ (probe4.peek (), std::char_traits::eof ()); + + msg.str ("/"); + msg.clear (); + StreamProbe probe5 (msg); + ASSERT_EQ (probe5.skipWhitespacesAndComments (), -1); + + msg.str ("/*"); + msg.clear (); + StreamProbe probe6 (msg); + ASSERT_EQ (probe6.skipWhitespacesAndComments (), -1); + + msg.str ("/!"); + msg.clear (); + StreamProbe probe7 (msg); + ASSERT_EQ (probe7.skipWhitespacesAndComments (), -1); +} + +/** + * @brief main function. + */ +int main (int argc, char** argv) +{ + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} diff --git a/dconv/tests/string_probe_test.cpp b/dconv/tests/string_probe_test.cpp index bc22382..740d02d 100644 --- a/dconv/tests/string_probe_test.cpp +++ b/dconv/tests/string_probe_test.cpp @@ -28,6 +28,8 @@ // libraries. #include +// C++. +#include using dconv::StringProbe; @@ -173,6 +175,90 @@ TEST (StringProbe, readDigits) ASSERT_EQ (value, 0); } +/** + * @brief read test. + */ +TEST (StringProbe, read) +{ + StringProbe probe ("hello world"); + char buf[8] = {}; + + ASSERT_EQ (probe.read (buf, 5), 5); + ASSERT_EQ (probe.read (buf, 8), 6); + ASSERT_EQ (probe.read (buf, 8), 0); +} + +/** + * @brief readUntilEscaped test. + */ +TEST (StringProbe, readUntilEscaped) +{ + StringProbe probe ("hello\"world"); + std::string out; + + probe.readUntilEscaped (out); + ASSERT_EQ (out, "hello"); + ASSERT_EQ (probe.peek (), '"'); + + probe = "hello wonderful world\\"; + out.clear (); + probe.readUntilEscaped (out); + ASSERT_EQ (out, "hello wonderful world"); + ASSERT_EQ (probe.peek (), '\\'); + + probe = "abc"; + out.clear (); + probe.readUntilEscaped (out); + ASSERT_EQ (out, "abc"); + ASSERT_EQ (probe.peek (), std::char_traits::eof ()); +} + +/** + * @brief skipWhitespaces test. + */ +TEST (StringProbe, skipWhitespaces) +{ + StringProbe probe (" hello"); + + probe.skipWhitespaces (); + ASSERT_EQ (probe.peek (), 'h'); + + probe = "\t hello"; + probe.skipWhitespaces (); + ASSERT_EQ (probe.peek (), 'h'); +} + +/** + * @brief skipWhitespacesAndComments test. + */ +TEST (StringProbe, skipWhitespacesAndComments) +{ + StringProbe probe (" // comment\nhello"); + ASSERT_EQ (probe.skipWhitespacesAndComments (), 0); + ASSERT_EQ (probe.peek (), 'h'); + + probe = " /* comment */hello"; + ASSERT_EQ (probe.skipWhitespacesAndComments (), 0); + ASSERT_EQ (probe.peek (), 'h'); + + probe = "// comment"; + ASSERT_EQ (probe.skipWhitespacesAndComments (), 0); + ASSERT_EQ (probe.peek (), std::char_traits::eof ()); + + probe = "/* comment */"; + ASSERT_EQ (probe.skipWhitespacesAndComments (), 0); + ASSERT_EQ (probe.peek (), std::char_traits::eof ()); + + probe = "/"; + ASSERT_EQ (probe.skipWhitespacesAndComments (), -1); + + probe = "/*"; + ASSERT_EQ (probe.skipWhitespacesAndComments (), -1); + + probe = "/!"; + ASSERT_EQ (probe.skipWhitespacesAndComments (), -1); +} + /** * @brief tell test. */ From 958b3b9c02c7f637c5b9d0862b86ba5de4dde880 Mon Sep 17 00:00:00 2001 From: mrabine Date: Wed, 28 Oct 2026 19:28:33 +0100 Subject: [PATCH 2/2] fix codacy warning --- dconv/include/dconv/probe.hpp | 4 ++-- dconv/tests/string_probe_test.cpp | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/dconv/include/dconv/probe.hpp b/dconv/include/dconv/probe.hpp index da31342..c5da8fd 100644 --- a/dconv/include/dconv/probe.hpp +++ b/dconv/include/dconv/probe.hpp @@ -114,7 +114,7 @@ namespace dconv * @brief default constructor. * @param in input string. */ - StringProbe (const char* in) + explicit StringProbe (const char* in) : _cur (in) , _beg (in) , _end (in ? in + std::char_traits::length (in) : in) @@ -444,7 +444,7 @@ namespace dconv * @brief default constructor. * @param in input stream. */ - StreamProbe (std::istream& in) + explicit StreamProbe (std::istream& in) : _in (in.rdbuf ()) { } diff --git a/dconv/tests/string_probe_test.cpp b/dconv/tests/string_probe_test.cpp index 740d02d..169cce8 100644 --- a/dconv/tests/string_probe_test.cpp +++ b/dconv/tests/string_probe_test.cpp @@ -200,13 +200,13 @@ TEST (StringProbe, readUntilEscaped) ASSERT_EQ (out, "hello"); ASSERT_EQ (probe.peek (), '"'); - probe = "hello wonderful world\\"; + probe = StringProbe ("hello wonderful world\\"); out.clear (); probe.readUntilEscaped (out); ASSERT_EQ (out, "hello wonderful world"); ASSERT_EQ (probe.peek (), '\\'); - probe = "abc"; + probe = StringProbe ("abc"); out.clear (); probe.readUntilEscaped (out); ASSERT_EQ (out, "abc"); @@ -223,7 +223,7 @@ TEST (StringProbe, skipWhitespaces) probe.skipWhitespaces (); ASSERT_EQ (probe.peek (), 'h'); - probe = "\t hello"; + probe = StringProbe ("\t hello"); probe.skipWhitespaces (); ASSERT_EQ (probe.peek (), 'h'); } @@ -237,25 +237,25 @@ TEST (StringProbe, skipWhitespacesAndComments) ASSERT_EQ (probe.skipWhitespacesAndComments (), 0); ASSERT_EQ (probe.peek (), 'h'); - probe = " /* comment */hello"; + probe = StringProbe (" /* comment */hello"); ASSERT_EQ (probe.skipWhitespacesAndComments (), 0); ASSERT_EQ (probe.peek (), 'h'); - probe = "// comment"; + probe = StringProbe ("// comment"); ASSERT_EQ (probe.skipWhitespacesAndComments (), 0); ASSERT_EQ (probe.peek (), std::char_traits::eof ()); - probe = "/* comment */"; + probe = StringProbe ("/* comment */"); ASSERT_EQ (probe.skipWhitespacesAndComments (), 0); ASSERT_EQ (probe.peek (), std::char_traits::eof ()); - probe = "/"; + probe = StringProbe ("/"); ASSERT_EQ (probe.skipWhitespacesAndComments (), -1); - probe = "/*"; + probe = StringProbe ("/*"); ASSERT_EQ (probe.skipWhitespacesAndComments (), -1); - probe = "/!"; + probe = StringProbe ("/!"); ASSERT_EQ (probe.skipWhitespacesAndComments (), -1); }