From 457c3262d7057b2f8031876eddfbe5bea284c855 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 4 Sep 2013 12:35:13 -0700 Subject: [PATCH] Fix String from number conversions --- Builds/VisualStudio2012/beast.vcxproj | 3 +- Builds/VisualStudio2012/beast.vcxproj.filters | 6 + modules/beast_core/beast_core.h | 2 + .../beast_core/text/StringCharPointerType.h | 54 ++++++ modules/beast_core/text/StringFromNumber.h | 159 ++++++++++++++++ modules/beast_core/text/beast_String.cpp | 172 ++---------------- modules/beast_core/text/beast_String.h | 54 +++--- 7 files changed, 268 insertions(+), 182 deletions(-) create mode 100644 modules/beast_core/text/StringCharPointerType.h create mode 100644 modules/beast_core/text/StringFromNumber.h diff --git a/Builds/VisualStudio2012/beast.vcxproj b/Builds/VisualStudio2012/beast.vcxproj index 9082fd54f..98652f38e 100644 --- a/Builds/VisualStudio2012/beast.vcxproj +++ b/Builds/VisualStudio2012/beast.vcxproj @@ -129,7 +129,6 @@ - @@ -237,6 +236,8 @@ + + diff --git a/Builds/VisualStudio2012/beast.vcxproj.filters b/Builds/VisualStudio2012/beast.vcxproj.filters index 2a04302ff..00d3da713 100644 --- a/Builds/VisualStudio2012/beast.vcxproj.filters +++ b/Builds/VisualStudio2012/beast.vcxproj.filters @@ -935,6 +935,12 @@ beast_core\containers\detail + + beast_core\text + + + beast_core\text + diff --git a/modules/beast_core/beast_core.h b/modules/beast_core/beast_core.h index 2ac12c9fd..babc3c9e7 100644 --- a/modules/beast_core/beast_core.h +++ b/modules/beast_core/beast_core.h @@ -271,6 +271,8 @@ extern BEAST_API void BEAST_CALLTYPE logAssertion (char const* file, int line) n #include "containers/beast_LockFreeStack.h" #include "threads/beast_SpinDelay.h" #include "memory/beast_StaticObject.h" +# include "text/StringCharPointerType.h" +# include "text/StringFromNumber.h" #include "text/beast_String.h" #include "memory/beast_MemoryAlignment.h" #include "memory/beast_CacheLine.h" diff --git a/modules/beast_core/text/StringCharPointerType.h b/modules/beast_core/text/StringCharPointerType.h new file mode 100644 index 000000000..37e82b7b2 --- /dev/null +++ b/modules/beast_core/text/StringCharPointerType.h @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Portions of this file are from JUCE. + Copyright (c) 2013 - Raw Material Software Ltd. + Please visit http://www.juce.com + + 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_STRINGCHARPOINTERTYPE_H_INCLUDED +#define BEAST_STRINGCHARPOINTERTYPE_H_INCLUDED + +/** This is the character encoding type used internally to store the string. + + By setting the value of BEAST_STRING_UTF_TYPE to 8, 16, or 32, you can change the + internal storage format of the String class. UTF-8 uses the least space (if your strings + contain few extended characters), but call operator[] involves iterating the string to find + the required index. UTF-32 provides instant random access to its characters, but uses 4 bytes + per character to store them. UTF-16 uses more space than UTF-8 and is also slow to index, + but is the native wchar_t format used in Windows. + + It doesn't matter too much which format you pick, because the toUTF8(), toUTF16() and + toUTF32() methods let you access the string's content in any of the other formats. +*/ +#if (BEAST_STRING_UTF_TYPE == 32) +typedef CharPointer_UTF32 StringCharPointerType; + +#elif (BEAST_STRING_UTF_TYPE == 16) +typedef CharPointer_UTF16 StringCharPointerType; + +#elif (BEAST_STRING_UTF_TYPE == 8) +typedef CharPointer_UTF8 StringCharPointerType; + +#else +#error "You must set the value of BEAST_STRING_UTF_TYPE to be either 8, 16, or 32!" + +#endif + +#endif + diff --git a/modules/beast_core/text/StringFromNumber.h b/modules/beast_core/text/StringFromNumber.h new file mode 100644 index 000000000..b5f58fca8 --- /dev/null +++ b/modules/beast_core/text/StringFromNumber.h @@ -0,0 +1,159 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Portions of this file are from JUCE. + Copyright (c) 2013 - Raw Material Software Ltd. + Please visit http://www.beast.com + + 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_CORE_STRINGFROMNUMBER_H_INCLUDED +#define BEAST_CORE_STRINGFROMNUMBER_H_INCLUDED + +// This is private! +// +class NumberToStringConverters +{ +public: + enum + { + charsNeededForInt = 32, + charsNeededForDouble = 48 + }; + + // pass in a pointer to the END of a buffer.. + template + static char* printDigits (char* t, Type v) noexcept + { + *--t = 0; + do + { + *--t = '0' + (char) (v % 10); + v /= 10; + } + while (v > 0); + return t; + } + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 4127) // conditional expression is constant +#pragma warning (disable: 4146) // unary minus operator applied to unsigned type, result still unsigned +#endif + // pass in a pointer to the END of a buffer.. + template + static char* numberToString (char* t, IntegerType const n) noexcept + { + if (std::numeric_limits ::is_signed) + { + if (n >= 0) + return printDigits (t, static_cast (n)); + + // NB: this needs to be careful not to call + // -std::numeric_limits::min(), + // which has undefined behaviour + // + t = printDigits (t, static_cast (-(n + 1)) + 1); + *--t = '-'; + return t; + } + return printDigits (t, n); + } +#ifdef _MSC_VER +#pragma warning (pop) +#endif + + struct StackArrayStream : public std::basic_streambuf > + { + explicit StackArrayStream (char* d) + { + imbue (std::locale::classic()); + setp (d, d + charsNeededForDouble); + } + + size_t writeDouble (double n, int numDecPlaces) + { + { + std::ostream o (this); + + if (numDecPlaces > 0) + o.precision ((std::streamsize) numDecPlaces); + + o << n; + } + + return (size_t) (pptr() - pbase()); + } + }; + + static char* doubleToString (char* buffer, + const int numChars, double n, int numDecPlaces, size_t& len) noexcept + { + if (numDecPlaces > 0 && numDecPlaces < 7 && n > -1.0e20 && n < 1.0e20) + { + char* const end = buffer + numChars; + char* t = end; + int64 v = (int64) (pow (10.0, numDecPlaces) * std::abs (n) + 0.5); + *--t = (char) 0; + + while (numDecPlaces >= 0 || v > 0) + { + if (numDecPlaces == 0) + *--t = '.'; + + *--t = (char) ('0' + (v % 10)); + + v /= 10; + --numDecPlaces; + } + + if (n < 0) + *--t = '-'; + + len = (size_t) (end - t - 1); + return t; + } + + StackArrayStream strm (buffer); + len = strm.writeDouble (n, numDecPlaces); + bassert (len <= charsNeededForDouble); + return buffer; + } + + static StringCharPointerType createFromFixedLength ( + const char* const src, const size_t numChars); + + template + static StringCharPointerType createFromInteger (const IntegerType number) + { + char buffer [charsNeededForInt]; + char* const end = buffer + numElementsInArray (buffer); + char* const start = numberToString (end, number); + return createFromFixedLength (start, (size_t) (end - start - 1)); + } + + static StringCharPointerType createFromDouble ( + const double number, const int numberOfDecimalPlaces) + { + char buffer [charsNeededForDouble]; + size_t len; + char* const start = doubleToString (buffer, numElementsInArray (buffer), (double) number, numberOfDecimalPlaces, len); + return createFromFixedLength (start, len); + } +}; + +#endif diff --git a/modules/beast_core/text/beast_String.cpp b/modules/beast_core/text/beast_String.cpp index ac3c46a56..dbaa2f0b5 100644 --- a/modules/beast_core/text/beast_String.cpp +++ b/modules/beast_core/text/beast_String.cpp @@ -235,13 +235,24 @@ private: StringHolder StringHolder::empty; const String String::empty; -//============================================================================== +//------------------------------------------------------------------------------ + +StringCharPointerType NumberToStringConverters::createFromFixedLength ( + const char* const src, const size_t numChars) +{ + return StringHolder::createFromFixedLength (src, numChars); +} + +//------------------------------------------------------------------------------ + void String::preallocateBytes (const size_t numBytesNeeded) { - text = StringHolder::makeUniqueWithByteSize (text, numBytesNeeded + sizeof (CharPointerType::CharType)); + text = StringHolder::makeUniqueWithByteSize ( + text, numBytesNeeded + sizeof (CharPointerType::CharType)); } //============================================================================== + String::String() noexcept : text (StringHolder::getEmpty()) { } @@ -356,130 +367,7 @@ String String::charToString (const beast_wchar character) } //============================================================================== -namespace NumberToStringConverters -{ - enum - { - charsNeededForInt = 32, - charsNeededForDouble = 48 - }; - template - static char* printDigits (char* t, Type v) noexcept - { - *--t = 0; - - do - { - *--t = '0' + (char) (v % 10); - v /= 10; - - } while (v > 0); - - return t; - } - -#ifdef _MSC_VER -#pragma warning (push) -#pragma warning (disable: 4127) // conditional expression is constant -#pragma warning (disable: 4146) // unary minus operator applied to unsigned type, result still unsigned -#endif - // pass in a pointer to the END of a buffer.. - template - static char* numberToString (char* t, IntegerType const n) noexcept - { - if (std::numeric_limits ::is_signed) - { - if (n >= 0) - return printDigits (t, static_cast (n)); - - // NB: this needs to be careful not to call -std::numeric_limits::min(), - // which has undefined behaviour - t = printDigits (t, static_cast (-(n + 1)) + 1); - *--t = '-'; - return t; - } - return printDigits (t, n); - } -#ifdef _MSC_VER -#pragma warning (pop) -#endif - - struct StackArrayStream : public std::basic_streambuf > - { - explicit StackArrayStream (char* d) - { - imbue (std::locale::classic()); - setp (d, d + charsNeededForDouble); - } - - size_t writeDouble (double n, int numDecPlaces) - { - { - std::ostream o (this); - - if (numDecPlaces > 0) - o.precision ((std::streamsize) numDecPlaces); - - o << n; - } - - return (size_t) (pptr() - pbase()); - } - }; - - static char* doubleToString (char* buffer, const int numChars, double n, int numDecPlaces, size_t& len) noexcept - { - if (numDecPlaces > 0 && numDecPlaces < 7 && n > -1.0e20 && n < 1.0e20) - { - char* const end = buffer + numChars; - char* t = end; - int64 v = (int64) (pow (10.0, numDecPlaces) * std::abs (n) + 0.5); - *--t = (char) 0; - - while (numDecPlaces >= 0 || v > 0) - { - if (numDecPlaces == 0) - *--t = '.'; - - *--t = (char) ('0' + (v % 10)); - - v /= 10; - --numDecPlaces; - } - - if (n < 0) - *--t = '-'; - - len = (size_t) (end - t - 1); - return t; - } - - StackArrayStream strm (buffer); - len = strm.writeDouble (n, numDecPlaces); - bassert (len <= charsNeededForDouble); - return buffer; - } - - template - static String::CharPointerType createFromInteger (const IntegerType number) - { - char buffer [charsNeededForInt]; - char* const end = buffer + numElementsInArray (buffer); - char* const start = numberToString (end, number); - return StringHolder::createFromFixedLength (start, (size_t) (end - start - 1)); - } - - static String::CharPointerType createFromDouble (const double number, const int numberOfDecimalPlaces) - { - char buffer [charsNeededForDouble]; - size_t len; - char* const start = doubleToString (buffer, numElementsInArray (buffer), (double) number, numberOfDecimalPlaces, len); - return StringHolder::createFromFixedLength (start, len); - } -} - -//============================================================================== String::String (const int number) : text (NumberToStringConverters::createFromInteger (number)) {} String::String (const unsigned int number) : text (NumberToStringConverters::createFromInteger (number)) {} String::String (const short number) : text (NumberToStringConverters::createFromInteger ((int) number)) {} @@ -492,40 +380,6 @@ String::String (const double number) : text (NumberToStringConverters::c String::String (const float number, const int numberOfDecimalPlaces) : text (NumberToStringConverters::createFromDouble ((double) number, numberOfDecimalPlaces)) {} String::String (const double number, const int numberOfDecimalPlaces) : text (NumberToStringConverters::createFromDouble (number, numberOfDecimalPlaces)) {} -template -String String::fromNumber (Number number, int) -{ - return String (NumberToStringConverters::createFromInteger (number), FromNumber ()); -} - -template <> -String String::fromNumber (float number, int numberOfDecimalPlaces) -{ - if (numberOfDecimalPlaces == 0) - number = std::floor (number); - - return String (NumberToStringConverters::createFromDouble ( - number, numberOfDecimalPlaces)); -} - -template <> -String String::fromNumber (double number, int numberOfDecimalPlaces) -{ - if (numberOfDecimalPlaces == 0) - number = std::floor (number); - - return String (NumberToStringConverters::createFromDouble ( - number, numberOfDecimalPlaces)); -} - -template String String::fromNumber (int16, int); -template String String::fromNumber (int32, int); -template String String::fromNumber (int64, int); -template String String::fromNumber (uint16, int); -template String String::fromNumber (uint32, int); -template String String::fromNumber (uint64, int); -template String String::fromNumber (std::size_t, int); - //============================================================================== int String::length() const noexcept { diff --git a/modules/beast_core/text/beast_String.h b/modules/beast_core/text/beast_String.h index 980ea4cd5..aeff8329f 100644 --- a/modules/beast_core/text/beast_String.h +++ b/modules/beast_core/text/beast_String.h @@ -153,27 +153,8 @@ public: */ static const String empty; - /** This is the character encoding type used internally to store the string. - - By setting the value of BEAST_STRING_UTF_TYPE to 8, 16, or 32, you can change the - internal storage format of the String class. UTF-8 uses the least space (if your strings - contain few extended characters), but call operator[] involves iterating the string to find - the required index. UTF-32 provides instant random access to its characters, but uses 4 bytes - per character to store them. UTF-16 uses more space than UTF-8 and is also slow to index, - but is the native wchar_t format used in Windows. - - It doesn't matter too much which format you pick, because the toUTF8(), toUTF16() and - toUTF32() methods let you access the string's content in any of the other formats. - */ - #if (BEAST_STRING_UTF_TYPE == 32) - typedef CharPointer_UTF32 CharPointerType; - #elif (BEAST_STRING_UTF_TYPE == 16) - typedef CharPointer_UTF16 CharPointerType; - #elif (BEAST_STRING_UTF_TYPE == 8) - typedef CharPointer_UTF8 CharPointerType; - #else - #error "You must set the value of BEAST_STRING_UTF_TYPE to be either 8, 16, or 32!" - #endif + /** This is the character encoding type used internally to store the string. */ + typedef StringCharPointerType CharPointerType; //============================================================================== /** Generates a probably-unique 32-bit hashcode from this string. */ @@ -1215,7 +1196,36 @@ private: operator bool() const noexcept { return false; } }; -//============================================================================== +//------------------------------------------------------------------------------ + +template +inline String String::fromNumber (Number number, int) +{ + return String (NumberToStringConverters::createFromInteger + (number), FromNumber ()); +} + +template <> +inline String String::fromNumber (float number, int numberOfDecimalPlaces) +{ + if (numberOfDecimalPlaces == 0) + number = std::floor (number); + return String (NumberToStringConverters::createFromDouble ( + number, numberOfDecimalPlaces)); +} + +template <> +inline String String::fromNumber (double number, int numberOfDecimalPlaces) +{ + if (numberOfDecimalPlaces == 0) + number = std::floor (number); + + return String (NumberToStringConverters::createFromDouble ( + number, numberOfDecimalPlaces)); +} + +//------------------------------------------------------------------------------ + /** Concatenates two strings. */ BEAST_API String BEAST_CALLTYPE operator+ (const char* string1, const String& string2); /** Concatenates two strings. */