From 2e402ba654fd0f9869a9b1194658f113689ebbd6 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sun, 28 Jul 2013 16:41:34 -0700 Subject: [PATCH] Add lexicalCast --- Builds/VisualStudio2012/beast.vcxproj | 7 + Builds/VisualStudio2012/beast.vcxproj.filters | 6 + TODO.txt | 2 + modules/beast_core/beast_core.cpp | 2 +- modules/beast_core/beast_core.h | 11 +- modules/beast_core/diagnostic/beast_Throw.h | 2 +- modules/beast_core/text/beast_LexicalCast.cpp | 123 +++++++ modules/beast_core/text/beast_LexicalCast.h | 303 ++++++++++++++++++ 8 files changed, 450 insertions(+), 6 deletions(-) create mode 100644 modules/beast_core/text/beast_LexicalCast.cpp create mode 100644 modules/beast_core/text/beast_LexicalCast.h diff --git a/Builds/VisualStudio2012/beast.vcxproj b/Builds/VisualStudio2012/beast.vcxproj index b19179f45..a7f7a585f 100644 --- a/Builds/VisualStudio2012/beast.vcxproj +++ b/Builds/VisualStudio2012/beast.vcxproj @@ -192,6 +192,7 @@ + @@ -748,6 +749,12 @@ true true + + true + true + true + true + true true diff --git a/Builds/VisualStudio2012/beast.vcxproj.filters b/Builds/VisualStudio2012/beast.vcxproj.filters index 11104f9c8..5954681eb 100644 --- a/Builds/VisualStudio2012/beast.vcxproj.filters +++ b/Builds/VisualStudio2012/beast.vcxproj.filters @@ -725,6 +725,9 @@ beast_core\misc + + beast_core\text + @@ -1129,6 +1132,9 @@ beast_core\misc + + beast_core\text + diff --git a/TODO.txt b/TODO.txt index d94a75a07..4d11e8dd2 100644 --- a/TODO.txt +++ b/TODO.txt @@ -2,6 +2,8 @@ BEAST TODO -------------------------------------------------------------------------------- +- add expectThrow() to UnitTest, where it expects an exception + - Remove timeout forced-kill from Thread::stopThread () - Import secp256k1 from sipa diff --git a/modules/beast_core/beast_core.cpp b/modules/beast_core/beast_core.cpp index 5cc387290..becc39afd 100644 --- a/modules/beast_core/beast_core.cpp +++ b/modules/beast_core/beast_core.cpp @@ -193,7 +193,7 @@ namespace beast #include "system/beast_SystemStats.cpp" #include "text/beast_CharacterFunctions.cpp" - +#include "text/beast_LexicalCast.cpp" #include "text/beast_Identifier.cpp" #include "text/beast_LocalisedStrings.cpp" #include "text/beast_String.cpp" diff --git a/modules/beast_core/beast_core.h b/modules/beast_core/beast_core.h index d562f943a..08eceb44c 100644 --- a/modules/beast_core/beast_core.h +++ b/modules/beast_core/beast_core.h @@ -212,22 +212,25 @@ namespace beast #include "memory/beast_Memory.h" #include "text/beast_String.h" + +#include "diagnostic/beast_SafeBool.h" +#include "diagnostic/beast_Error.h" +#include "diagnostic/beast_Debug.h" +#include "diagnostic/beast_Throw.h" + #include "text/beast_CharacterFunctions.h" #include "text/beast_CharPointer_ASCII.h" #include "text/beast_CharPointer_UTF16.h" #include "text/beast_CharPointer_UTF32.h" #include "text/beast_CharPointer_UTF8.h" +#include "text/beast_LexicalCast.h" #include "time/beast_PerformedAtExit.h" #include "diagnostic/beast_LeakChecked.h" #include "memory/beast_ByteOrder.h" #include "logging/beast_Logger.h" #include "threads/beast_Thread.h" -#include "diagnostic/beast_Debug.h" -#include "diagnostic/beast_SafeBool.h" -#include "diagnostic/beast_Error.h" #include "diagnostic/beast_FPUFlags.h" -#include "diagnostic/beast_Throw.h" #include "diagnostic/beast_ProtectedCall.h" #include "containers/beast_AbstractFifo.h" #include "containers/beast_Array.h" diff --git a/modules/beast_core/diagnostic/beast_Throw.h b/modules/beast_core/diagnostic/beast_Throw.h index a488193fe..58b6e8ae7 100644 --- a/modules/beast_core/diagnostic/beast_Throw.h +++ b/modules/beast_core/diagnostic/beast_Throw.h @@ -26,7 +26,7 @@ the stack is unwound. */ template -inline void Throw (Exception const& e) +inline void Throw (Exception const& e, char const* = "", int = 0) { Debug::breakPoint (); diff --git a/modules/beast_core/text/beast_LexicalCast.cpp b/modules/beast_core/text/beast_LexicalCast.cpp new file mode 100644 index 000000000..76165bca5 --- /dev/null +++ b/modules/beast_core/text/beast_LexicalCast.cpp @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +unsigned char const LexicalCastUtilities::s_digitTable [256] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xFF - 0x07 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x08 - 0x0F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x10 - 0x17 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x18 - 0x1F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x20 - 0x27 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x28 - 0x2F + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x30 - 0x37 + 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x38 - 0x3F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x40 - 0x47 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x48 - 0x4F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x50 - 0x57 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x58 - 0x5F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x60 - 0x67 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x68 - 0x6F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x70 - 0x77 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x78 - 0x7F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x80 - 0x87 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x88 - 0x8F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x90 - 0x97 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x98 - 0x9F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA0 - 0xA7 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA8 - 0xAF + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB0 - 0xB7 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB8 - 0xBF + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC0 - 0xC7 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC8 - 0xCF + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD0 - 0xD7 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD8 - 0xDF + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE0 - 0xE7 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE8 - 0xEF + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xF0 - 0xF7 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 0xF8 - 0xFF +}; + +//------------------------------------------------------------------------------ + +class LexicalCastTests : public UnitTest +{ +public: + LexicalCastTests () : UnitTest ("LexicalCast", "beast") + { + } + + template + static IntType nextRandomInt (Random& r) + { + return static_cast (r.nextInt64 ()); + } + + template + void testInteger (IntType in) + { + String s; + IntType out; + + expect (lexicalCastChecked (s, in)); + expect (lexicalCastChecked (out, s)); + expect (out == in); + } + + template + void testIntegers (Random& r) + { + { + String s; + s << "random " << typeid (IntType).name (); + beginTestCase (s); + + for (int i = 0; i < 1000; ++i) + { + IntType const value (nextRandomInt (r)); + testInteger (value); + } + } + + { + String s; + s << "numeric_limits <" << typeid (IntType).name () << ">"; + beginTestCase (s); + + testInteger (std::numeric_limits ::min ()); + testInteger (std::numeric_limits ::max ()); + } + } + + void runTest () + { + int64 const seedValue = 50; + + Random r (seedValue); + + testIntegers (r); + testIntegers (r); + testIntegers (r); + testIntegers (r); + testIntegers (r); + testIntegers (r); + testIntegers (r); + testIntegers (r); + } +}; + +static LexicalCastTests lexicalCastTests; diff --git a/modules/beast_core/text/beast_LexicalCast.h b/modules/beast_core/text/beast_LexicalCast.h new file mode 100644 index 000000000..20ac2e58a --- /dev/null +++ b/modules/beast_core/text/beast_LexicalCast.h @@ -0,0 +1,303 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_LEXICALCAST_H_INCLUDED +#define BEAST_LEXICALCAST_H_INCLUDED + +// Base class with utility functions +struct LexicalCastUtilities +{ + static unsigned char const s_digitTable [256]; + + // strict string to integer parser + template + static inline bool parseSigned (IntType& result, InputIterator begin, InputIterator end) + { + if (0 == std::distance (begin, end)) + return false; + + uint64 accum = 0; + InputIterator it = begin; + + // process sign + bool negative = false; + if ('+' == *it) + { + ++it; + } + else if ('-' == *it) + { + ++it; + negative = true; + } + if (end == it) + return false; + + // calc max of abs value + uint64 max; + if (negative) + max = static_cast ( + -(static_cast (std::numeric_limits ::min ()))); + else + max = std::numeric_limits ::max (); + + // process digits + while (end != it) + { + uint64 const digit = static_cast ( + s_digitTable [static_cast (*it++)]); + + if (0xFF == digit) + return false; + + uint64 const overflow = (max - digit) / 10; + + if (accum > overflow) + return false; + + accum = (10 * accum) + digit; + } + + if (negative) + { + result = -static_cast (accum); + } + else + { + result = static_cast (accum); + } + + return true; + } + + template + static inline bool parseUnsigned (IntType& result, InputIterator begin, InputIterator end) + { + if (0 == std::distance (begin, end)) + return false; + + uint64 accum = 0; + InputIterator it = begin; + uint64 const max = std::numeric_limits ::max (); + + // process digits + while (end != it) + { + uint64 const digit = static_cast ( + s_digitTable [static_cast (*it++)]); + + if (0xFF == digit) + return false; + + uint64 const overflow = (max - digit) / 10; + + if (accum > overflow) + return false; + + accum = (10 * accum) + digit; + } + + result = static_cast (accum); + + return true; + } +}; + +//------------------------------------------------------------------------------ + +/** This is thrown when a conversion is not possible. + Only used in the throw variants of lexicalCast. +*/ +struct BadLexicalCast : public std::bad_cast +{ +}; + +// These specializatons get called by the non-member functions to do the work +template +struct LexicalCast; + +// conversion to String +template +struct LexicalCast +{ + bool operator() (String& out, int in) const { out = String (in); return true; } + bool operator() (String& out, unsigned int in) const { out = String (in); return true; } + bool operator() (String& out, short in) const { out = String (in); return true; } + bool operator() (String& out, unsigned short in) const { out = String (in); return true; } + bool operator() (String& out, int64 in) const { out = String (in); return true; } + bool operator() (String& out, uint64 in) const { out = String (in); return true; } + bool operator() (String& out, float in) const { out = String (in); return true; } + bool operator() (String& out, double in) const { out = String (in); return true; } +}; + +// Parse String to number +template +struct LexicalCast +{ + bool operator() (int& out, String const& in) const { std::string const& s (in.toStdString ()); return LexicalCastUtilities::parseSigned (out, s.begin (), s.end ()); } + bool operator() (short& out, String const& in) const { std::string const& s (in.toStdString ()); return LexicalCastUtilities::parseSigned (out, s.begin (), s.end ()); } + bool operator() (int64& out, String const& in) const { std::string const& s (in.toStdString ()); return LexicalCastUtilities::parseSigned (out, s.begin (), s.end ()); } + bool operator() (unsigned int& out, String const& in) const { std::string const& s (in.toStdString ()); return LexicalCastUtilities::parseUnsigned (out, s.begin (), s.end ()); } + bool operator() (unsigned short& out, String const& in) const { std::string const& s (in.toStdString ()); return LexicalCastUtilities::parseUnsigned (out, s.begin (), s.end ()); } + bool operator() (uint64& out, String const& in) const { std::string const& s (in.toStdString ()); return LexicalCastUtilities::parseUnsigned (out, s.begin (), s.end ()); } + bool operator() (float& out, String const& in) const { bassertfalse; return false; /* UNIMPLEMENTED! */ } + bool operator() (double& out, String const& in) const { bassertfalse; return false; /* UNIMPLEMENTED! */ } + + bool operator () (bool& out, String const& in) const + { + // boost::lexical_cast is very strict, it + // throws on anything but "1" or "0" + // + if (in == "1") + { + out = true; + return true; + } + else if (in == "0") + { + out = false; + return true; + } + + return false; + } +}; + +//------------------------------------------------------------------------------ + +// Conversion to std::string +template +struct LexicalCast +{ + bool operator() (std::string& out, In in) const + { + String s; + + if (LexicalCast () (s, in)) + { + out = s.toStdString (); + return true; + } + + return false; + } +}; + +// Conversion from std::string +template +struct LexicalCast +{ + bool operator() (Out& out, std::string const& in) const + { + Out result; + + if (LexicalCast () (result, String (in.c_str ()))) + { + out = result; + return true; + } + + return false; + } +}; + +// Conversion from null terminated char const* +template +struct LexicalCast +{ + bool operator() (Out& out, char const* in) const + { + Out result; + + if (LexicalCast () (result, String (in))) + { + out = result; + return true; + } + + return false; + } +}; + +// Conversion from null terminated char* +// The string is not modified. +template +struct LexicalCast +{ + bool operator() (Out& out, char* in) const + { + Out result; + + if (LexicalCast () (result, in)) + { + out = result; + return true; + } + + return false; + } +}; + +//------------------------------------------------------------------------------ + +/** Intelligently convert from one type to another. + @return `false` if there was a parsing or range error +*/ +template +bool lexicalCastChecked (Out& out, In in) +{ + return LexicalCast () (out, in); +} + +/** Convert from one type to another, throw on error + + An exception of type BadLexicalCast is thrown if the conversion fails. + + @return The new type. +*/ +template +Out lexicalCastThrow (In in) +{ + Out out; + + if (lexicalCastChecked (out, in)) + return out; + + Throw (BadLexicalCast (), __FILE__, __LINE__); + + return Out (); +} + +/** Convert from one type to another. + + @param defaultValue The value returned if parsing fails + @return The new type. +*/ +template +Out lexicalCast (In in, Out defaultValue = Out ()) +{ + Out out; + + if (lexicalCastChecked (out, in)) + return out; + + return defaultValue; +} + +#endif