diff --git a/CMakeLists.txt b/CMakeLists.txt index b37a69a83e1..bb315916b4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,8 +142,6 @@ else() message(STATUS "Can't find ccache — consider installing ccache to improve recompilation performance") endif() -include(cmake/codecvt.cmake) - if(NOT EXISTS ${CMAKE_SOURCE_DIR}/platform/${MBGL_PLATFORM}/config.cmake) message(ERROR "Can't find config.cmake file for platform ${MBGL_PLATFORM}") endif() diff --git a/bin/offline.cpp b/bin/offline.cpp index 5123e7b9261..0d0fe57281f 100644 --- a/bin/offline.cpp +++ b/bin/offline.cpp @@ -53,6 +53,27 @@ mapbox::geometry::geometry parseGeometry(const std::string& json) { }); } +std::ostream& operator<<(std::ostream& os, mbgl::Response::Error::Reason r) { + switch (r) { + case mbgl::Response::Error::Reason::Success: + return os << "Response::Error::Reason::Success"; + case mbgl::Response::Error::Reason::NotFound: + return os << "Response::Error::Reason::NotFound"; + case mbgl::Response::Error::Reason::Server: + return os << "Response::Error::Reason::Server"; + case mbgl::Response::Error::Reason::Connection: + return os << "Response::Error::Reason::Connection"; + case mbgl::Response::Error::Reason::RateLimit: + return os << "Response::Error::Reason::RateLimit"; + case mbgl::Response::Error::Reason::Other: + return os << "Response::Error::Reason::Other"; + } + + // The above switch is exhaustive, but placate GCC nonetheless: + assert(false); + return os; +} + int main(int argc, char *argv[]) { args::ArgumentParser argumentParser("Mapbox GL offline tool"); args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", {'h', "help"}); diff --git a/cmake/codecvt.cmake b/cmake/codecvt.cmake deleted file mode 100644 index 8228a6df6bd..00000000000 --- a/cmake/codecvt.cmake +++ /dev/null @@ -1,19 +0,0 @@ -add_library(codecvt INTERFACE) - -# Determine if the STL has codecvt -file(WRITE "${CMAKE_BINARY_DIR}/features/codecvt/main.cpp" "#include \nint main() {}") -try_compile(STL_SUPPORTS_CODECVT - "${CMAKE_BINARY_DIR}/features/codecvt" - SOURCES "${CMAKE_BINARY_DIR}/features/codecvt/main.cpp" - CMAKE_FLAGS "-DCMAKE_MACOSX_BUNDLE:STRING=YES" "-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED:STRING=NO" - COMPILE_DEFINITIONS "-std=c++14" # CXX_STANDARD wasn't added to try_compile until CMake 3.8 - OUTPUT_VARIABLE CODECVT_TEST_OUTPUT -) - -if (NOT STL_SUPPORTS_CODECVT) - if ($ENV{V}) - message("codecvt support not detected: ${CODECVT_TEST_OUTPUT}") - endif() - target_include_directories(codecvt INTERFACE platform/default/codecvt) - target_add_mason_package(codecvt INTERFACE boost) -endif() diff --git a/cmake/core-files.txt b/cmake/core-files.txt index 7c52e08ccf4..e7fd7d81c21 100644 --- a/cmake/core-files.txt +++ b/cmake/core-files.txt @@ -726,6 +726,7 @@ src/mbgl/util/geo.cpp src/mbgl/util/geojson_impl.cpp src/mbgl/util/grid_index.cpp src/mbgl/util/grid_index.hpp +src/mbgl/util/hash.hpp src/mbgl/util/http_header.cpp src/mbgl/util/http_header.hpp src/mbgl/util/http_timeout.cpp @@ -751,6 +752,7 @@ src/mbgl/util/math.hpp src/mbgl/util/offscreen_texture.cpp src/mbgl/util/offscreen_texture.hpp src/mbgl/util/premultiply.cpp +src/mbgl/util/rapidjson.cpp src/mbgl/util/rapidjson.hpp src/mbgl/util/rect.hpp src/mbgl/util/std.hpp diff --git a/cmake/core.cmake b/cmake/core.cmake index 1b29b4fb084..32e77f5d555 100644 --- a/cmake/core.cmake +++ b/cmake/core.cmake @@ -7,8 +7,6 @@ target_include_directories(mbgl-core PRIVATE src ) -target_link_libraries(mbgl-core PRIVATE codecvt) - target_add_mason_package(mbgl-core PUBLIC geometry) target_add_mason_package(mbgl-core PUBLIC variant) target_add_mason_package(mbgl-core PRIVATE unique_resource) diff --git a/cmake/mason-dependencies.cmake b/cmake/mason-dependencies.cmake index 160060a6fa4..79d343ac8b1 100644 --- a/cmake/mason-dependencies.cmake +++ b/cmake/mason-dependencies.cmake @@ -19,7 +19,7 @@ mason_use(cheap-ruler VERSION 2.5.3 HEADER_ONLY) mason_use(vector-tile VERSION 1.0.2 HEADER_ONLY) if(MBGL_PLATFORM STREQUAL "android") - mason_use(jni.hpp VERSION 4.0.0 HEADER_ONLY) + mason_use(jni.hpp VERSION 4.0.1 HEADER_ONLY) elseif(MBGL_PLATFORM STREQUAL "ios") # noop elseif(MBGL_PLATFORM STREQUAL "linux") diff --git a/cmake/test-files.txt b/cmake/test-files.txt index 89283e2aea7..4c7029f9f04 100644 --- a/cmake/test-files.txt +++ b/cmake/test-files.txt @@ -144,6 +144,7 @@ test/util/peer.test.cpp test/util/position.test.cpp test/util/projection.test.cpp test/util/run_loop.test.cpp +test/util/string.test.cpp test/util/text_conversions.test.cpp test/util/thread.test.cpp test/util/thread_local.test.cpp diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp index 508400141b3..07b059e4356 100644 --- a/include/mbgl/storage/response.hpp +++ b/include/mbgl/storage/response.hpp @@ -69,6 +69,4 @@ class Response::Error { Error(Reason, std::string = "", optional = {}); }; -std::ostream& operator<<(std::ostream&, Response::Error::Reason); - } // namespace mbgl diff --git a/include/mbgl/util/string.hpp b/include/mbgl/util/string.hpp index 13498ccb923..418e1872f3a 100644 --- a/include/mbgl/util/string.hpp +++ b/include/mbgl/util/string.hpp @@ -1,25 +1,14 @@ #pragma once -#include #include -#include #include -#include +#include // Polyfill needed by Qt when building for Android with GCC #if defined(__ANDROID__) && defined(__GLIBCXX__) namespace std { -template -std::string to_string(T value) -{ - std::ostringstream oss; - oss << value; - - return oss.str(); -} - inline int stoi(const std::string &str) { return atoi(str.c_str()); @@ -36,42 +25,70 @@ inline float stof(const std::string &str) { namespace mbgl { namespace util { -template -inline std::string toString(T t) { - return std::to_string(t); +std::string toString(int64_t); +std::string toString(uint64_t); +std::string toString(int32_t); +std::string toString(uint32_t); +std::string toString(double, bool decimal = false); + +inline std::string toString(int16_t t) { + return toString(static_cast(t)); } -inline std::string toString(int8_t num) { - return std::to_string(int(num)); +inline std::string toString(uint16_t t) { + return toString(static_cast(t)); } -inline std::string toString(uint8_t num) { - return std::to_string(unsigned(num)); +inline std::string toString(int8_t t) { + return toString(static_cast(t)); } -std::string toString(float); -std::string toString(double); -std::string toString(long double); +inline std::string toString(uint8_t t) { + return toString(static_cast(t)); +} -inline std::string toString(std::exception_ptr error) { - assert(error); +template ::value>> +inline std::string toString(unsigned long t) { + return toString(static_cast(t)); +} - if (!error) { - return "(null)"; - } +template ::value>> +inline std::string toString(unsigned long long t) { + return toString(static_cast(t)); +} - try { - std::rethrow_exception(error); - } catch (const std::exception& ex) { - return ex.what(); - } catch (...) { - return "Unknown exception type"; - } +inline std::string toString(float t, bool decimal = false) { + return toString(static_cast(t), decimal); } +inline std::string toString(long double t, bool decimal = false) { + return toString(static_cast(t), decimal); +} + +std::string toString(std::exception_ptr); + +template +std::string toString(T) = delete; + +std::string toHex(size_t); + inline float stof(const std::string& str) { return std::stof(str); } } // namespace util } // namespace mbgl + +// Android's libstdc++ doesn't have std::to_string() +#if defined(__ANDROID__) && defined(__GLIBCXX__) + +namespace std { + +template +inline std::string to_string(T value) { + return mbgl::util::toString(value); +} + +} // namespace std + +#endif diff --git a/platform/android/config.cmake b/platform/android/config.cmake index cc44c2585cb..ec64b239f84 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -69,7 +69,6 @@ macro(mbgl_filesource) target_add_mason_package(mbgl-filesource PUBLIC jni.hpp) target_link_libraries(mbgl-filesource - PRIVATE codecvt PUBLIC sqlite PUBLIC -llog PUBLIC -landroid @@ -84,8 +83,11 @@ add_library(mapbox-gl SHARED platform/android/src/main.cpp ) +target_include_directories(mapbox-gl + PRIVATE src +) + target_link_libraries(mapbox-gl - PRIVATE codecvt PRIVATE mbgl-core PRIVATE mbgl-filesource ) @@ -106,7 +108,6 @@ macro(mbgl_platform_test) ) target_link_libraries(mbgl-test - PRIVATE codecvt PRIVATE mbgl-core PRIVATE mbgl-filesource ) diff --git a/platform/android/src/conversion/constant.cpp b/platform/android/src/conversion/constant.cpp index eaff434dbcc..a609dc7c60b 100644 --- a/platform/android/src/conversion/constant.cpp +++ b/platform/android/src/conversion/constant.cpp @@ -1,7 +1,7 @@ #include "constant.hpp" #include "collection.hpp" -#include +#include namespace mbgl { namespace android { @@ -24,9 +24,18 @@ Result>> Converter>, std::stri } Result>> Converter>, Color>::operator()(jni::JNIEnv& env, const Color& value) const { - std::stringstream sstream; - sstream << "rgba(" << value.r << ", " << value.g << ", " << value.b << ", " << value.a << ")"; - return jni::Make(env, sstream.str()); + std::string result; + result.reserve(32); + result += "rgba("; + result += util::toString(value.r); + result += ", "; + result += util::toString(value.g); + result += ", "; + result += util::toString(value.b); + result += ", "; + result += util::toString(value.a); + result += ")"; + return jni::Make(env, result); } Result>> Converter>, style::expression::Formatted>::operator()(jni::JNIEnv& env, const style::expression::Formatted& value) const { diff --git a/platform/android/src/geojson/feature.cpp b/platform/android/src/geojson/feature.cpp index f0ed097842a..767b145a898 100644 --- a/platform/android/src/geojson/feature.cpp +++ b/platform/android/src/geojson/feature.cpp @@ -33,7 +33,7 @@ class FeatureIdVisitor { public: template std::string operator()(const T& i) const { - return std::to_string(i); + return util::toString(i); } std::string operator()(const std::string& i) const { diff --git a/platform/android/src/http_file_source.cpp b/platform/android/src/http_file_source.cpp index e1b3493f0fe..bb6fc90e45b 100644 --- a/platform/android/src/http_file_source.cpp +++ b/platform/android/src/http_file_source.cpp @@ -151,9 +151,9 @@ void HTTPRequest::onResponse(jni::JNIEnv& env, int code, } response.error = std::make_unique(Error::Reason::RateLimit, "HTTP status code 429", http::parseRetryHeaders(retryAfter, xRateLimitReset)); } else if (code >= 500 && code < 600) { - response.error = std::make_unique(Error::Reason::Server, std::string{ "HTTP status code " } + std::to_string(code)); + response.error = std::make_unique(Error::Reason::Server, std::string{ "HTTP status code " } + util::toString(code)); } else { - response.error = std::make_unique(Error::Reason::Other, std::string{ "HTTP status code " } + std::to_string(code)); + response.error = std::make_unique(Error::Reason::Other, std::string{ "HTTP status code " } + util::toString(code)); } async.send(); diff --git a/platform/default/codecvt/codecvt b/platform/default/codecvt/codecvt deleted file mode 100644 index 8d21e823483..00000000000 --- a/platform/default/codecvt/codecvt +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -// This is a minimal polyfill that'll only work exactly for how we use codecvt - -#include -#include - -namespace std { - -template -class wstring_convert { -public: - static_assert(std::is_same::value, "type mismatch"); - - inline std::basic_string from_bytes(const string& str) { - return boost::locale::conv::utf_to_utf(str); - } - - inline string to_bytes(const std::basic_string& str) { - return boost::locale::conv::utf_to_utf(str); - } -}; - -template -class codecvt_utf8_utf16 { -public: - using Elem = E; -}; - -} // namespace std diff --git a/platform/default/jni/string_conversion.hpp b/platform/default/jni/string_conversion.hpp new file mode 100644 index 00000000000..66df539f4e0 --- /dev/null +++ b/platform/default/jni/string_conversion.hpp @@ -0,0 +1,17 @@ +#pragma once + +// This file replaces the default implementation in jni.hpp. + +#include + +namespace jni { + +inline std::u16string convertUTF8ToUTF16(const std::string& str) { + return mbgl::util::convertUTF8ToUTF16(str); +} + +inline std::string convertUTF16ToUTF8(const std::u16string& str) { + return mbgl::util::convertUTF16ToUTF8(str); +} + +} // namespace jni diff --git a/platform/default/utf.cpp b/platform/default/utf.cpp index 8bc8ea73149..f0f9d3e67a0 100644 --- a/platform/default/utf.cpp +++ b/platform/default/utf.cpp @@ -1,13 +1,16 @@ #include -#include -#include +#include namespace mbgl { namespace util { -std::u16string utf8_to_utf16::convert(const std::string& utf8) { - return std::wstring_convert, char16_t>().from_bytes(utf8); +std::u16string convertUTF8ToUTF16(const std::string& str) { + return boost::locale::conv::utf_to_utf(str); +} + +std::string convertUTF16ToUTF8(const std::u16string& str) { + return boost::locale::conv::utf_to_utf(str); } } // namespace util diff --git a/platform/node/test/js/map.test.js b/platform/node/test/js/map.test.js index 4a71d72046e..b21c1519e3a 100644 --- a/platform/node/test/js/map.test.js +++ b/platform/node/test/js/map.test.js @@ -317,7 +317,7 @@ test('Map', function(t) { t.throws(function() { map.load('foo bar'); - }, /Failed to parse style: 1 - Invalid value./); + }, /Failed to parse style: Invalid value. at offset 1/); t.throws(function() { map.load('""'); @@ -349,7 +349,7 @@ test('Map', function(t) { t.throws(function() { map.load('invalid'); - }, /Failed to parse style: 0 - Invalid value./); + }, /Failed to parse style: Invalid value. at offset 0/); }); t.test('accepts an empty stylesheet string', function(t) { diff --git a/platform/qt/src/utf.cpp b/platform/qt/src/utf.cpp index d8bf2ca0b3a..069c7bca390 100644 --- a/platform/qt/src/utf.cpp +++ b/platform/qt/src/utf.cpp @@ -5,7 +5,7 @@ namespace mbgl { namespace util { -std::u16string utf8_to_utf16::convert(std::string const& utf8) { +std::u16string convertUTF8ToUTF16(std::string const& utf8) { auto utf16 = QString::fromUtf8(utf8.data(), utf8.length()); // Newers Qt have QString::toStdU16String(), but this is how it is diff --git a/src/mbgl/algorithm/generate_clip_ids.cpp b/src/mbgl/algorithm/generate_clip_ids.cpp index 287d2a408e2..aefa55b929a 100644 --- a/src/mbgl/algorithm/generate_clip_ids.cpp +++ b/src/mbgl/algorithm/generate_clip_ids.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include diff --git a/src/mbgl/geometry/line_atlas.cpp b/src/mbgl/geometry/line_atlas.cpp index 1129bd0b200..1bd6f987e13 100644 --- a/src/mbgl/geometry/line_atlas.cpp +++ b/src/mbgl/geometry/line_atlas.cpp @@ -2,10 +2,8 @@ #include #include #include +#include -#include - -#include #include namespace mbgl { @@ -22,7 +20,7 @@ LinePatternPos LineAtlas::getDashPosition(const std::vector& dasharray, size_t key = patternCap == LinePatternCap::Round ? std::numeric_limits::min() : std::numeric_limits::max(); for (const float part : dasharray) { - boost::hash_combine(key, part); + util::hash_combine(key, part); } // Note: We're not handling hash collisions here. diff --git a/src/mbgl/layout/merge_lines.cpp b/src/mbgl/layout/merge_lines.cpp index 2a3afa42b2e..616a8a3ff56 100644 --- a/src/mbgl/layout/merge_lines.cpp +++ b/src/mbgl/layout/merge_lines.cpp @@ -1,7 +1,6 @@ #include #include - -#include +#include namespace mbgl { namespace util { @@ -43,10 +42,7 @@ size_t mergeFromLeft(std::vector& features, } size_t getKey(const std::u16string& text, const GeometryCoordinate& coord) { - auto hash = std::hash()(text); - boost::hash_combine(hash, coord.x); - boost::hash_combine(hash, coord.y); - return hash; + return util::hash(text, coord.x, coord.y); } void mergeLines(std::vector& features) { diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index f7b6d949bdf..6d20afeb8d4 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -123,7 +123,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, u8string = platform::lowercase(u8string); } - ft.formattedText->addSection(applyArabicShaping(util::utf8_to_utf16::convert(u8string)), + ft.formattedText->addSection(applyArabicShaping(util::convertUTF8ToUTF16(u8string)), section.fontScale ? *section.fontScale : 1.0, section.fontStack ? FontStackHasher()(*section.fontStack) : baseFontStackHash); diff --git a/src/mbgl/programs/program_parameters.cpp b/src/mbgl/programs/program_parameters.cpp index e76ec4be713..6b6c2bb2fe4 100644 --- a/src/mbgl/programs/program_parameters.cpp +++ b/src/mbgl/programs/program_parameters.cpp @@ -1,7 +1,5 @@ #include - -#include -#include +#include namespace mbgl { @@ -9,14 +7,15 @@ ProgramParameters::ProgramParameters(const float pixelRatio, const bool overdraw, optional cacheDir_) : defines([&] { - std::ostringstream ss; - ss.imbue(std::locale("C")); - ss.setf(std::ios_base::showpoint); - ss << "#define DEVICE_PIXEL_RATIO " << pixelRatio << std::endl; + std::string result; + result.reserve(32); + result += "#define DEVICE_PIXEL_RATIO "; + result += util::toString(pixelRatio, true); + result += '\n'; if (overdraw) { - ss << "#define OVERDRAW_INSPECTOR" << std::endl; + result += "#define OVERDRAW_INSPECTOR\n"; } - return ss.str(); + return result; }()), cacheDir(std::move(cacheDir_)) { } @@ -29,10 +28,15 @@ optional ProgramParameters::cachePath(const char* name) const { if (!cacheDir) { return {}; } else { - std::ostringstream ss; - ss << *cacheDir << "/com.mapbox.gl.shader." << name << "." << std::setfill('0') - << std::setw(sizeof(size_t) * 2) << std::hex << std::hash()(defines) << ".pbf"; - return ss.str(); + std::string result; + result.reserve(cacheDir->length() + 64); + result += *cacheDir; + result += "/com.mapbox.gl.shader."; + result += name; + result += '.'; + result += util::toHex(std::hash()(defines)); + result += ".pbf"; + return result; } } diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.cpp b/src/mbgl/renderer/sources/render_raster_dem_source.cpp index fbf2c09d197..2283a0e788e 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.cpp @@ -4,7 +4,6 @@ #include #include #include -#include namespace mbgl { diff --git a/src/mbgl/shaders/shaders.cpp b/src/mbgl/shaders/shaders.cpp index 2e5a318024c..c4f49a1a8cf 100644 --- a/src/mbgl/shaders/shaders.cpp +++ b/src/mbgl/shaders/shaders.cpp @@ -1,10 +1,9 @@ #include #include #include +#include #include -#include -#include namespace mbgl { namespace shaders { @@ -18,12 +17,12 @@ std::string vertexSource(const ProgramParameters& parameters, const char* vertex } std::string programIdentifier(const std::string& vertexSource, const std::string& fragmentSource) { - std::ostringstream ss; - ss << std::setfill('0') << std::setw(sizeof(size_t) * 2) << std::hex; - ss << std::hash()(vertexSource); - ss << std::hash()(fragmentSource); - ss << "v2"; - return ss.str(); + std::string result; + result.reserve((sizeof(size_t) * 2) * 2 + 2); // 2 size_t hex values + "v2" + result += util::toHex(std::hash()(vertexSource)); + result += util::toHex(std::hash()(fragmentSource)); + result += "v2"; + return result; } } // namespace shaders diff --git a/src/mbgl/sprite/sprite_parser.cpp b/src/mbgl/sprite/sprite_parser.cpp index 1a36e3e9902..99e2b0c8ca7 100644 --- a/src/mbgl/sprite/sprite_parser.cpp +++ b/src/mbgl/sprite/sprite_parser.cpp @@ -9,7 +9,6 @@ #include #include -#include namespace mbgl { @@ -91,9 +90,7 @@ std::vector> parseSprite(const std::string& encode JSDocument doc; doc.Parse<0>(json.c_str()); if (doc.HasParseError()) { - std::stringstream message; - message << "Failed to parse JSON: " << rapidjson::GetParseError_En(doc.GetParseError()) << " at offset " << doc.GetErrorOffset(); - throw std::runtime_error(message.str()); + throw std::runtime_error("Failed to parse JSON: " + formatJSONParseError(doc)); } else if (!doc.IsObject()) { throw std::runtime_error("Sprite JSON root must be an object"); } else { diff --git a/src/mbgl/storage/response.cpp b/src/mbgl/storage/response.cpp index 222f55db84b..ba075c32625 100644 --- a/src/mbgl/storage/response.cpp +++ b/src/mbgl/storage/response.cpp @@ -1,8 +1,4 @@ #include -#include - -#include -#include namespace mbgl { @@ -26,25 +22,4 @@ Response::Error::Error(Reason reason_, std::string message_, optional : reason(reason_), message(std::move(message_)), retryAfter(std::move(retryAfter_)) { } -std::ostream& operator<<(std::ostream& os, Response::Error::Reason r) { - switch (r) { - case Response::Error::Reason::Success: - return os << "Response::Error::Reason::Success"; - case Response::Error::Reason::NotFound: - return os << "Response::Error::Reason::NotFound"; - case Response::Error::Reason::Server: - return os << "Response::Error::Reason::Server"; - case Response::Error::Reason::Connection: - return os << "Response::Error::Reason::Connection"; - case Response::Error::Reason::RateLimit: - return os << "Response::Error::Reason::RateLimit"; - case Response::Error::Reason::Other: - return os << "Response::Error::Reason::Other"; - } - - // The above switch is exhaustive, but placate GCC nonetheless: - assert(false); - return os; -} - } // namespace mbgl diff --git a/src/mbgl/style/conversion/json.hpp b/src/mbgl/style/conversion/json.hpp index 3a7bf2b557c..503f60a3821 100644 --- a/src/mbgl/style/conversion/json.hpp +++ b/src/mbgl/style/conversion/json.hpp @@ -4,7 +4,6 @@ #include #include -#include namespace mbgl { namespace style { @@ -16,9 +15,7 @@ optional convertJSON(const std::string& json, Error& error, Args&&...args) { document.Parse<0>(json.c_str()); if (document.HasParseError()) { - std::stringstream message; - message << document.GetErrorOffset() << " - " << rapidjson::GetParseError_En(document.GetParseError()); - error = { message.str() }; + error = { formatJSONParseError(document) }; return {}; } diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp index 114a666f088..77cf23bad0e 100644 --- a/src/mbgl/style/parser.cpp +++ b/src/mbgl/style/parser.cpp @@ -18,7 +18,6 @@ #include #include -#include namespace mbgl { namespace style { @@ -30,11 +29,7 @@ StyleParseResult Parser::parse(const std::string& json) { document.Parse<0>(json.c_str()); if (document.HasParseError()) { - std::stringstream message; - message << document.GetErrorOffset() << " - " - << rapidjson::GetParseError_En(document.GetParseError()); - - return std::make_exception_ptr(std::runtime_error(message.str())); + return std::make_exception_ptr(std::runtime_error(formatJSONParseError(document))); } if (!document.IsObject()) { diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp index 55cd50fd9b6..034784dc243 100644 --- a/src/mbgl/text/glyph.hpp +++ b/src/mbgl/text/glyph.hpp @@ -115,7 +115,7 @@ MBGL_CONSTEXPR WritingModeType operator~(WritingModeType value) { return WritingModeType(~mbgl::underlying_type(value)); } -using GlyphDependencies = std::map; -using GlyphRangeDependencies = std::map; +using GlyphDependencies = std::map; +using GlyphRangeDependencies = std::map>; } // end namespace mbgl diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp index c4a7a2de661..b947ef72c87 100644 --- a/src/mbgl/text/glyph_manager.cpp +++ b/src/mbgl/text/glyph_manager.cpp @@ -31,7 +31,7 @@ void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphD Entry& entry = entries[fontStack]; const GlyphIDs& glyphIDs = dependency.second; - GlyphRangeSet ranges; + std::unordered_set ranges; for (const auto& glyphID : glyphIDs) { if (localGlyphRasterizer->canRasterizeGlyph(fontStack, glyphID)) { if (entry.glyphs.find(glyphID) == entry.glyphs.end()) { diff --git a/src/mbgl/text/glyph_range.hpp b/src/mbgl/text/glyph_range.hpp index 74afb73dfcc..35a1f74b94f 100644 --- a/src/mbgl/text/glyph_range.hpp +++ b/src/mbgl/text/glyph_range.hpp @@ -3,21 +3,24 @@ #include #include #include -#include +#include namespace mbgl { using GlyphRange = std::pair; -struct GlyphRangeHash { - std::size_t operator()(const GlyphRange& glyphRange) const { - return boost::hash_value(glyphRange); - } -}; - -using GlyphRangeSet = std::unordered_set; - constexpr uint32_t GLYPHS_PER_GLYPH_RANGE = 256; constexpr uint32_t GLYPH_RANGES_PER_FONT_STACK = 256; } // end namespace mbgl + +namespace std { + +template <> +struct hash { + std::size_t operator()(const mbgl::GlyphRange& range) const { + return mbgl::util::hash(range.first, range.second); + } +}; + +} // namespace std diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index d6cbb2a4f8d..3a6335955b0 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -4,9 +4,8 @@ #include #include -#include - #include +#include #include namespace mbgl { @@ -247,7 +246,7 @@ std::set determineLineBreaks(const TaggedString& logicalInput, continue; } auto it = glyphs->second.find(codePoint); - if (it != glyphs->second.end() && it->second && !boost::algorithm::is_any_of(u" \t\n\v\f\r")(codePoint)) { + if (it != glyphs->second.end() && it->second && !util::i18n::isWhitespace(codePoint)) { currentX += (*it->second)->metrics.advance * section.scale + spacing; } diff --git a/src/mbgl/text/tagged_string.cpp b/src/mbgl/text/tagged_string.cpp index e199a7c962b..851e011c4f8 100644 --- a/src/mbgl/text/tagged_string.cpp +++ b/src/mbgl/text/tagged_string.cpp @@ -1,8 +1,6 @@ #include #include -#include - namespace mbgl { void TaggedString::addSection(const std::u16string& sectionText, double scale, FontStackHash fontStack) { @@ -12,7 +10,6 @@ void TaggedString::addSection(const std::u16string& sectionText, double scale, F } void TaggedString::trim() { - auto whiteSpace = boost::algorithm::is_any_of(u" \t\n\v\f\r"); std::size_t beginningWhitespace = styledText.first.find_first_not_of(u" \t\n\v\f\r"); if (beginningWhitespace == std::u16string::npos) { // Entirely whitespace diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index 9743cd3f7d8..80b3e970bd6 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -19,8 +19,6 @@ #include #include -#include - namespace mbgl { using namespace style; diff --git a/src/mbgl/tile/tile_id_hash.cpp b/src/mbgl/tile/tile_id_hash.cpp index 4a1f1858172..d4a7d880772 100644 --- a/src/mbgl/tile/tile_id_hash.cpp +++ b/src/mbgl/tile/tile_id_hash.cpp @@ -1,28 +1,27 @@ #include - -#include +#include namespace std { size_t hash::operator()(const mbgl::CanonicalTileID& id) const { std::size_t seed = 0; - boost::hash_combine(seed, id.x); - boost::hash_combine(seed, id.y); - boost::hash_combine(seed, id.z); + mbgl::util::hash_combine(seed, id.x); + mbgl::util::hash_combine(seed, id.y); + mbgl::util::hash_combine(seed, id.z); return seed; } size_t hash::operator()(const mbgl::UnwrappedTileID& id) const { std::size_t seed = 0; - boost::hash_combine(seed, std::hash{}(id.canonical)); - boost::hash_combine(seed, id.wrap); + mbgl::util::hash_combine(seed, std::hash{}(id.canonical)); + mbgl::util::hash_combine(seed, id.wrap); return seed; } size_t hash::operator()(const mbgl::OverscaledTileID& id) const { std::size_t seed = 0; - boost::hash_combine(seed, std::hash{}(id.canonical)); - boost::hash_combine(seed, id.overscaledZ); + mbgl::util::hash_combine(seed, std::hash{}(id.canonical)); + mbgl::util::hash_combine(seed, id.overscaledZ); return seed; } diff --git a/src/mbgl/util/dtoa.cpp b/src/mbgl/util/dtoa.cpp index 0e3aef61171..844d50b7452 100644 --- a/src/mbgl/util/dtoa.cpp +++ b/src/mbgl/util/dtoa.cpp @@ -1,121 +1,22 @@ #include "dtoa.hpp" -// Clang/C2 on Windows 64-bits can't parse rapidjson's dtoa -// and it was causing the compiler to crash. -#if !defined(_WINDOWS) #include -#endif - -#include namespace mbgl { namespace util { -#if !defined(_WINDOWS) - -namespace { - -// From https://github.com/miloyip/rapidjson/blob/master/include/rapidjson/internal/dtoa.h - -char* Prettify(char* buffer, int length, int k) { - constexpr int maxDecimalPlaces = 324; - const int kk = length + k; // 10^(kk-1) <= v < 10^kk - - if (0 <= k && kk <= 21) { - // 1234e7 -> 12340000000 - for (int i = length; i < kk; i++) - buffer[i] = '0'; - return &buffer[kk]; - } - else if (0 < kk && kk <= 21) { - // 1234e-2 -> 12.34 - std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); - buffer[kk] = '.'; - if (0 > k + maxDecimalPlaces) { - // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 - // Remove extra trailing zeros (at least one) after truncation. - for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) - if (buffer[i] != '0') - return &buffer[i + 1]; - return &buffer[kk + 2]; // Reserve one zero - } - else - return &buffer[length + 1]; - } - else if (-6 < kk && kk <= 0) { - // 1234e-6 -> 0.001234 - const int offset = 2 - kk; - std::memmove(&buffer[offset], &buffer[0], static_cast(length)); - buffer[0] = '0'; - buffer[1] = '.'; - for (int i = 2; i < offset; i++) - buffer[i] = '0'; - if (length - kk > maxDecimalPlaces) { - // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 - // Remove extra trailing zeros (at least one) after truncation. - for (int i = maxDecimalPlaces + 1; i > 2; i--) - if (buffer[i] != '0') - return &buffer[i + 1]; - return &buffer[3]; // Reserve one zero - } - else - return &buffer[length + offset]; - } - else if (kk < -maxDecimalPlaces) { - // Truncate to zero - buffer[0] = '0'; - return &buffer[1]; - } - else if (length == 1) { - // 1e30 - buffer[1] = 'e'; - return rapidjson::internal::WriteExponent(kk - 1, &buffer[2]); - } - else { - // 1234e30 -> 1.234e33 - std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); - buffer[1] = '.'; - buffer[length + 1] = 'e'; - return rapidjson::internal::WriteExponent(kk - 1, &buffer[0 + length + 2]); - } -} - -} // namespace - -char* dtoa(double value, char* buffer) { - rapidjson::internal::Double d(value); - if (d.IsZero()) { - if (d.Sign()) - *buffer++ = '-'; // -0.0, Issue #289 - buffer[0] = '0'; - return &buffer[1]; - } - else { - if (value < 0) { - *buffer++ = '-'; - value = -value; - } - int length, K; - rapidjson::internal::Grisu2(value, buffer, &length, &K); - return Prettify(buffer, length, K); - } -} - -std::string dtoa(double value) { +std::string dtoa(double value, bool decimal) { std::string data; data.resize(25); - auto end = dtoa(value, const_cast(data.data())); - data.resize(end - data.data()); + auto end = rapidjson::internal::dtoa(value, const_cast(data.data())); + auto length = end - data.data(); + if (!decimal && length >= 3 && end[-1] == '0' && end[-2] == '.') { + // Remove trailing ".0" for integers + length -= 2; + } + data.resize(length); return data; } -#else - -std::string dtoa(double value) { - return std::to_string(value); -} - -#endif - } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/dtoa.hpp b/src/mbgl/util/dtoa.hpp index e6b1659aa2e..22aa9256758 100644 --- a/src/mbgl/util/dtoa.hpp +++ b/src/mbgl/util/dtoa.hpp @@ -6,7 +6,7 @@ namespace mbgl { namespace util { -std::string dtoa(double value); +std::string dtoa(double value, bool decimal = false); inline double stod(const std::string& str) { return ::strtod(str.c_str(), nullptr); diff --git a/src/mbgl/util/font_stack.cpp b/src/mbgl/util/font_stack.cpp index fb1b716fb64..4093a217935 100644 --- a/src/mbgl/util/font_stack.cpp +++ b/src/mbgl/util/font_stack.cpp @@ -1,8 +1,8 @@ #include #include #include +#include -#include #include namespace mbgl { @@ -14,7 +14,11 @@ std::string fontStackToString(const FontStack& fontStack) { } FontStackHash FontStackHasher::operator()(const FontStack& fontStack) const { - return boost::hash_range(fontStack.begin(), fontStack.end()); + std::size_t seed = 0; + for (const auto& font : fontStack) { + util::hash_combine(seed, font); + } + return seed; } std::set fontStacks(const std::vector>& layers) { diff --git a/src/mbgl/util/hash.hpp b/src/mbgl/util/hash.hpp new file mode 100644 index 00000000000..89aba00f72f --- /dev/null +++ b/src/mbgl/util/hash.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace util { + +template +void hash_combine(std::size_t& seed, const T& v) { + seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +template +std::size_t hash(Args&&... args) { + std::size_t seed = 0; + ignore({ (hash_combine(seed, args), 0)... }); + return seed; +} + +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/i18n.cpp b/src/mbgl/util/i18n.cpp index 0fa61226739..d9b0c20796b 100644 --- a/src/mbgl/util/i18n.cpp +++ b/src/mbgl/util/i18n.cpp @@ -609,7 +609,7 @@ bool charInSupportedScript(char16_t chr) { } bool isStringInSupportedScript(const std::string& input) { - auto u16string = util::utf8_to_utf16::convert(input); + auto u16string = util::convertUTF8ToUTF16(input); for (char16_t chr : u16string) { if (!charInSupportedScript(chr)) { return false; @@ -618,6 +618,10 @@ bool isStringInSupportedScript(const std::string& input) { return true; } +bool isWhitespace(char16_t chr) { + return chr == u' ' || chr == u'\t' || chr == u'\n' || chr == u'\v' || chr == u'\f' || chr == u'\r'; +} + } // namespace i18n } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/i18n.hpp b/src/mbgl/util/i18n.hpp index a74215a1341..c7544f443b2 100644 --- a/src/mbgl/util/i18n.hpp +++ b/src/mbgl/util/i18n.hpp @@ -75,6 +75,8 @@ char16_t verticalizePunctuation(char16_t chr); bool isStringInSupportedScript(const std::string& input); +bool isWhitespace(char16_t chr); + } // namespace i18n } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/mapbox.cpp b/src/mbgl/util/mapbox.cpp index b823b727a7c..6c36c152cd8 100644 --- a/src/mbgl/util/mapbox.cpp +++ b/src/mbgl/util/mapbox.cpp @@ -6,7 +6,6 @@ #include #include -#include #include namespace { diff --git a/src/mbgl/util/rapidjson.cpp b/src/mbgl/util/rapidjson.cpp new file mode 100644 index 00000000000..32a3c0b929e --- /dev/null +++ b/src/mbgl/util/rapidjson.cpp @@ -0,0 +1,13 @@ +#include +#include + +namespace mbgl { + +std::string formatJSONParseError(const JSDocument& doc) { + return std::string{ rapidjson::GetParseError_En(doc.GetParseError()) } + " at offset " + + util::toString(doc.GetErrorOffset()); +} + +} // namespace mbgl + + diff --git a/src/mbgl/util/rapidjson.hpp b/src/mbgl/util/rapidjson.hpp index 7262e8545c9..2fb2a07c9fd 100644 --- a/src/mbgl/util/rapidjson.hpp +++ b/src/mbgl/util/rapidjson.hpp @@ -8,4 +8,6 @@ namespace mbgl { using JSDocument = rapidjson::GenericDocument, rapidjson::CrtAllocator>; using JSValue = rapidjson::GenericValue, rapidjson::CrtAllocator>; +std::string formatJSONParseError(const JSDocument&); + } // namespace mbgl diff --git a/src/mbgl/util/stopwatch.cpp b/src/mbgl/util/stopwatch.cpp index 3883d8535fd..6db9da07e1a 100644 --- a/src/mbgl/util/stopwatch.cpp +++ b/src/mbgl/util/stopwatch.cpp @@ -4,7 +4,6 @@ #include #include -#include #include namespace mbgl { diff --git a/src/mbgl/util/stopwatch.hpp b/src/mbgl/util/stopwatch.hpp index 0c91342a57e..daa313f7024 100644 --- a/src/mbgl/util/stopwatch.hpp +++ b/src/mbgl/util/stopwatch.hpp @@ -4,7 +4,10 @@ #include #include + +#ifdef MBGL_TIMING #include +#endif namespace mbgl { namespace util { diff --git a/src/mbgl/util/string.cpp b/src/mbgl/util/string.cpp index 47382093829..2f737e74360 100644 --- a/src/mbgl/util/string.cpp +++ b/src/mbgl/util/string.cpp @@ -1,19 +1,87 @@ #include -#include + +#include +#include + +#include namespace mbgl { namespace util { -std::string toString(float num) { - return dtoa(num); +std::string toString(int32_t t) { + rapidjson::StringBuffer s; + rapidjson::Writer writer(s); + writer.Int(t); + return s.GetString(); +} + +std::string toString(uint32_t t) { + rapidjson::StringBuffer s; + rapidjson::Writer writer(s); + writer.Uint(t); + return s.GetString(); +} + +std::string toString(int64_t t) { + rapidjson::StringBuffer s; + rapidjson::Writer writer(s); + writer.Int64(t); + return s.GetString(); +} + +std::string toString(uint64_t t) { + rapidjson::StringBuffer s; + rapidjson::Writer writer(s); + writer.Uint64(t); + return s.GetString(); +} + +std::string toString(double t, bool decimal) { + rapidjson::StringBuffer s; + rapidjson::Writer writer(s); + writer.Double(t); + std::string data = s.GetString(); + if (!decimal && data.length() >= 3 && data[data.length() - 1] == '0' && data[data.length() - 2] == '.') { + // Remove trailing ".0" for integers + data.resize(data.length() - 2); + } + return data; +} + +std::string toString(std::exception_ptr error) { + assert(error); + + if (!error) { + return "(null)"; + } + + try { + std::rethrow_exception(error); + } catch (const std::exception& ex) { + return ex.what(); + } catch (...) { + return "Unknown exception type"; + } +} + +namespace { + +template +std::string toPaddedHex(T x) { + std::string result; + result.resize(sizeof(T) * 2); + for (int index = sizeof(T) * 2 - 1; index >= 0; index--) { + const int digit = x & 0x0F; + result[index] = '0' + digit + (digit > 9 ? 39 : 0); + x >>= 4; + } + return result; } -std::string toString(double num) { - return dtoa(num); } -std::string toString(long double num) { - return dtoa(num); +std::string toHex(size_t value) { + return toPaddedHex(value); } } // namespace util diff --git a/src/mbgl/util/url.cpp b/src/mbgl/util/url.cpp index a4263502ef3..37a70007cae 100644 --- a/src/mbgl/util/url.cpp +++ b/src/mbgl/util/url.cpp @@ -1,8 +1,6 @@ #include #include -#include -#include #include #include #include @@ -24,26 +22,30 @@ inline bool isSchemeCharacter(char c) { return isAlphaNumericCharacter(c) || c == '-' || c == '+' || c == '.'; } +inline char toLowerHex(char c) { + c &= 0x0F; + return '0' + c + (c > 9 ? 7 : 0); +} + } // namespace namespace mbgl { namespace util { std::string percentEncode(const std::string& input) { - std::ostringstream encoded; - - encoded.fill('0'); - encoded << std::hex; + std::string encoded; for (auto c : input) { if (isAlphaNumericCharacter(c) || c == '-' || c == '_' || c == '.' || c == '~') { - encoded << c; + encoded += c; } else { - encoded << '%' << std::setw(2) << int(c); + encoded += '%'; + encoded += toLowerHex(c >> 4); + encoded += toLowerHex(c); } } - return encoded.str(); + return encoded; } std::string percentDecode(const std::string& input) { diff --git a/src/mbgl/util/utf.hpp b/src/mbgl/util/utf.hpp index c13b0943719..d870fb93315 100644 --- a/src/mbgl/util/utf.hpp +++ b/src/mbgl/util/utf.hpp @@ -5,10 +5,8 @@ namespace mbgl { namespace util { -class utf8_to_utf16 { -public: - static std::u16string convert(std::string const&); -}; +std::u16string convertUTF8ToUTF16(const std::string&); +std::string convertUTF16ToUTF8(const std::u16string&); } // namespace util } // namespace mbgl diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index cd15c5c2781..a21f9c7e75d 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -237,7 +237,7 @@ TEST(Map, SetStyleInvalidJSON) { auto observer = Log::removeObserver(); auto flo = static_cast(observer.get()); EXPECT_EQ(1u, flo->count({ EventSeverity::Error, Event::ParseStyle, -1, - "Failed to parse style: 0 - Invalid value." })); + "Failed to parse style: Invalid value. at offset 0" })); auto unchecked = flo->unchecked(); EXPECT_TRUE(unchecked.empty()) << unchecked; } diff --git a/test/style/source.test.cpp b/test/style/source.test.cpp index 6cb01146f62..abd43502314 100644 --- a/test/style/source.test.cpp +++ b/test/style/source.test.cpp @@ -127,7 +127,7 @@ TEST(Source, LoadingCorrupt) { test.styleObserver.sourceError = [&] (Source& source, std::exception_ptr error) { EXPECT_EQ("source", source.getID()); - EXPECT_EQ("0 - Invalid value.", util::toString(error)); + EXPECT_EQ("Invalid value. at offset 0", util::toString(error)); test.end(); }; diff --git a/test/util/string.test.cpp b/test/util/string.test.cpp new file mode 100644 index 00000000000..91e1b936856 --- /dev/null +++ b/test/util/string.test.cpp @@ -0,0 +1,29 @@ +#include + +#include + +#include + +using namespace mbgl; + +TEST(ToString, FloatingPoint) { + EXPECT_EQ("0", util::toString(0.0)); + EXPECT_EQ("0.0", util::toString(0.0, true)); + EXPECT_EQ("1.33", util::toString(1.33)); + EXPECT_EQ("1.33", util::toString(1.33, true)); + EXPECT_EQ("-1", util::toString(-1.0)); + EXPECT_EQ("-1.0", util::toString(-1.0, true)); + EXPECT_EQ("12340000000", util::toString(12340000000.0)); + EXPECT_EQ("12340000000.0", util::toString(12340000000.0, true)); + EXPECT_EQ("12340000000", util::toString(12340000000.0)); + EXPECT_EQ("12340000000.0", util::toString(12340000000.0, true)); +} + +TEST(ToHex, SIZE_T) { +#if INTPTR_MAX == INT32_MAX + EXPECT_EQ("a715b247", util::toHex((size_t)0xa715b247)); +#elif INTPTR_MAX == INT64_MAX + EXPECT_EQ("a715b247df38cc29", util::toHex((size_t)0xa715b247df38cc29)); +#endif + +} diff --git a/test/util/url.test.cpp b/test/util/url.test.cpp index 55d8af2811b..24697c42fa1 100644 --- a/test/util/url.test.cpp +++ b/test/util/url.test.cpp @@ -6,6 +6,14 @@ using namespace mbgl::util; +TEST(URL, percentEncode) { + EXPECT_EQ("%22%C3%A9nc%C3%B8%C3%B0ing%22", percentEncode("\"éncøðing\"")); +} + +TEST(URL, percentDecode) { + EXPECT_EQ("\"éncøðing\"", percentDecode("%22%C3%A9nc%C3%B8%C3%B0ing%22")); +} + TEST(URL, Scheme) { EXPECT_EQ(URL::Segment({ 0, 4 }), URL("http://example.com/test?query=foo").scheme); EXPECT_EQ(URL::Segment({ 0, 4 }), URL("http://127.0.0.1:8080/test?query=foo").scheme);