diff options
105 files changed, 723 insertions, 475 deletions
diff --git a/benchmark/api/query.benchmark.cpp b/benchmark/api/query.benchmark.cpp index 769458552..f9cbbff32 100644 --- a/benchmark/api/query.benchmark.cpp +++ b/benchmark/api/query.benchmark.cpp @@ -22,10 +22,10 @@ public: NetworkStatus::Set(NetworkStatus::Status::Offline); fileSource.setAccessToken("foobar"); - map.getStyle().loadJSON(util::read_file("benchmark/fixtures/api/style.json")); + map.getStyle().loadJSON(util::readFile("benchmark/fixtures/api/style.json")); map.setLatLngZoom({ 40.726989, -73.992857 }, 15); // Manhattan map.getStyle().addImage(std::make_unique<style::Image>("test-icon", - decodeImage(util::read_file("benchmark/fixtures/api/default_marker.png")), 1.0)); + decodeImage(util::readFile("benchmark/fixtures/api/default_marker.png")), 1.0)); frontend.render(map); } diff --git a/benchmark/api/render.benchmark.cpp b/benchmark/api/render.benchmark.cpp index a1b557777..8c783c6a0 100644 --- a/benchmark/api/render.benchmark.cpp +++ b/benchmark/api/render.benchmark.cpp @@ -29,11 +29,11 @@ public: ThreadPool threadPool { 4 }; }; -static void prepare(Map& map, optional<std::string> json = {}) { - map.getStyle().loadJSON(json ? *json : util::read_file("benchmark/fixtures/api/style.json")); +static void prepare(Map& map, Blob json = {}) { + map.getStyle().loadJSON(json ? json : util::readFile("benchmark/fixtures/api/style.json")); map.setLatLngZoom({ 40.726989, -73.992857 }, 15); // Manhattan map.getStyle().addImage(std::make_unique<style::Image>("test-icon", - decodeImage(util::read_file("benchmark/fixtures/api/default_marker.png")), 1.0)); + decodeImage(util::readFile("benchmark/fixtures/api/default_marker.png")), 1.0)); } } // end namespace @@ -55,7 +55,7 @@ static void API_renderStill_reuse_map_switch_styles(::benchmark::State& state) { Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), 1, bench.fileSource, bench.threadPool, MapMode::Static}; while (state.KeepRunning()) { - prepare(map, { "{}" }); + prepare(map, { "{}", false }); frontend.render(map); prepare(map); frontend.render(map); diff --git a/benchmark/parse/vector_tile.benchmark.cpp b/benchmark/parse/vector_tile.benchmark.cpp index 24623dbda..fab22ae38 100644 --- a/benchmark/parse/vector_tile.benchmark.cpp +++ b/benchmark/parse/vector_tile.benchmark.cpp @@ -6,7 +6,7 @@ using namespace mbgl; static void Parse_VectorTile(benchmark::State& state) { - auto data = std::make_shared<std::string>(util::read_file("test/fixtures/api/assets/streets/10-163-395.vector.pbf")); + auto data = util::readFile("test/fixtures/api/assets/streets/10-163-395.vector.pbf"); while (state.KeepRunning()) { std::size_t length = 0; diff --git a/bin/render.cpp b/bin/render.cpp index 6ceb32eb0..4b15ced9a 100644 --- a/bin/render.cpp +++ b/bin/render.cpp @@ -101,7 +101,7 @@ int main(int argc, char *argv[]) { try { std::ofstream out(output, std::ios::binary); - out << encodePNG(frontend.render(map)); + out << *encodePNG(frontend.render(map)).uncompressedData(); out.close(); } catch(std::exception& e) { std::cout << "Error: " << e.what() << std::endl; diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index f24482e30..a0523a6f4 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -656,6 +656,7 @@ set(MBGL_CORE_FILES # util include/mbgl/util/async_request.hpp include/mbgl/util/async_task.hpp + include/mbgl/util/blob.hpp include/mbgl/util/char_array_buffer.hpp include/mbgl/util/chrono.hpp include/mbgl/util/color.hpp @@ -698,6 +699,7 @@ set(MBGL_CORE_FILES include/mbgl/util/work_request.hpp include/mbgl/util/work_task.hpp include/mbgl/util/work_task_impl.hpp + src/mbgl/util/blob.cpp src/mbgl/util/chrono.cpp src/mbgl/util/clip_id.cpp src/mbgl/util/clip_id.hpp diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp index 318fa389f..d9da494e4 100644 --- a/include/mbgl/storage/resource.hpp +++ b/include/mbgl/storage/resource.hpp @@ -42,12 +42,23 @@ public: All = Cache | Network, }; + enum class Compression : bool { + // The data will be an uncompressed blob, even if it was obtained in compressed form. + Uncompressed = false, + + // The data will be returned compressed if it was already obtained in compressed form, + // and uncompressed otherwise. + PreferCompressed = true, + }; + Resource(Kind kind_, std::string url_, optional<TileData> tileData_ = {}, - LoadingMethod loadingMethod_ = LoadingMethod::All) + LoadingMethod loadingMethod_ = LoadingMethod::All, + Compression compression_ = Compression::PreferCompressed) : kind(kind_), loadingMethod(loadingMethod_), + compression(compression_), url(std::move(url_)), tileData(std::move(tileData_)) { } @@ -72,6 +83,7 @@ public: Kind kind; LoadingMethod loadingMethod; + Compression compression; std::string url; // Includes auxiliary data if this is a tile request. @@ -80,7 +92,7 @@ public: optional<Timestamp> priorModified = {}; optional<Timestamp> priorExpires = {}; optional<std::string> priorEtag = {}; - std::shared_ptr<const std::string> priorData; + Blob priorData; }; diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp index 508400141..81a84effb 100644 --- a/include/mbgl/storage/response.hpp +++ b/include/mbgl/storage/response.hpp @@ -2,6 +2,7 @@ #include <mbgl/util/chrono.hpp> #include <mbgl/util/optional.hpp> +#include <mbgl/util/blob.hpp> #include <string> #include <memory> @@ -31,7 +32,7 @@ public: bool mustRevalidate = false; // The actual data of the response. Present only for non-error, non-notModified responses. - std::shared_ptr<const std::string> data; + Blob data; optional<Timestamp> modified; optional<Timestamp> expires; diff --git a/include/mbgl/style/style.hpp b/include/mbgl/style/style.hpp index d6fdbd8f2..dffdbf99e 100644 --- a/include/mbgl/style/style.hpp +++ b/include/mbgl/style/style.hpp @@ -3,6 +3,7 @@ #include <mbgl/style/transition_options.hpp> #include <mbgl/map/camera.hpp> #include <mbgl/util/geo.hpp> +#include <mbgl/util/blob.hpp> #include <string> #include <vector> @@ -25,10 +26,10 @@ public: Style(Scheduler&, FileSource&, float pixelRatio); ~Style(); - void loadJSON(const std::string&); + void loadJSON(Blob); void loadURL(const std::string&); - std::string getJSON() const; + Blob getJSON() const; std::string getURL() const; // Defaults diff --git a/include/mbgl/util/blob.hpp b/include/mbgl/util/blob.hpp new file mode 100644 index 000000000..df8fddc77 --- /dev/null +++ b/include/mbgl/util/blob.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include <memory> +#include <string> + +namespace mbgl { + +class Blob { +public: + Blob(); + Blob(std::shared_ptr<const std::string> bytes, bool gzip); + Blob(std::string&& bytes, bool compressed); + + // Return uncompressed/compressed data. + std::shared_ptr<const std::string> uncompressedData() const; + std::shared_ptr<const std::string> compressedData() const; + + // Transform the blob to being uncompressed. + void uncompress(); + + bool isCompressed() const { + return compressed; + } + + explicit operator bool() const { + return (bool)bytes; + } + +private: + std::shared_ptr<const std::string> bytes; + bool compressed; +}; + +} // namespace mbgl diff --git a/include/mbgl/util/compression.hpp b/include/mbgl/util/compression.hpp index 93a3ddb8b..cbce76468 100644 --- a/include/mbgl/util/compression.hpp +++ b/include/mbgl/util/compression.hpp @@ -5,8 +5,15 @@ namespace mbgl { namespace util { +// Compresses data with the deflate algorithm. std::string compress(const std::string& raw); + +// Decompresses data that is in deflate format, optionally wrapped in a gzip container. std::string decompress(const std::string& raw); +// Returns true when there's a good chance that the string can be compressed. +// In particular, it returns false when the data is an already compressed image format. +bool isCompressible(const std::string& raw); + } // namespace util } // namespace mbgl diff --git a/include/mbgl/util/image.hpp b/include/mbgl/util/image.hpp index 4887058f7..1cb3cca8c 100644 --- a/include/mbgl/util/image.hpp +++ b/include/mbgl/util/image.hpp @@ -2,6 +2,7 @@ #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/geometry.hpp> +#include <mbgl/util/blob.hpp> #include <mbgl/util/size.hpp> #include <string> @@ -170,8 +171,7 @@ using UnassociatedImage = Image<ImageAlphaMode::Unassociated>; using PremultipliedImage = Image<ImageAlphaMode::Premultiplied>; using AlphaImage = Image<ImageAlphaMode::Exclusive>; -// TODO: don't use std::string for binary data. -PremultipliedImage decodeImage(const std::string&); -std::string encodePNG(const PremultipliedImage&); +PremultipliedImage decodeImage(Blob); +Blob encodePNG(const PremultipliedImage&); } // namespace mbgl diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/UnknownContentEncodingException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/UnknownContentEncodingException.java new file mode 100644 index 000000000..4debc33ee --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/UnknownContentEncodingException.java @@ -0,0 +1,12 @@ +package com.mapbox.mapboxsdk.exceptions; + +/** + * An UnknownContentEncodingException is thrown by HTTPRequest + * when there aren't enough LatLng to create a bounds. + */ +public class UnknownContentEncodingException extends RuntimeException { + + public UnknownContentEncodingException(String encoding) { + super("Unknown content encoding '" + encoding + "'"); + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java index caee493e6..942b88f1f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java @@ -9,6 +9,7 @@ import android.text.TextUtils; import com.mapbox.mapboxsdk.BuildConfig; import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.constants.MapboxConstants; +import com.mapbox.mapboxsdk.exceptions.UnknownContentEncodingException; import java.io.IOException; import java.io.InterruptedIOException; @@ -66,7 +67,7 @@ class HTTPRequest implements Callback { private native void nativeOnFailure(int type, String message); private native void nativeOnResponse(int code, String etag, String modified, String cacheControl, String expires, - String retryAfter, String xRateLimitReset, byte[] body); + String retryAfter, String xRateLimitReset, byte[] body, boolean gzip); private HTTPRequest(long nativePtr, String resourceUrl, String etag, String modified) { mNativePtr = nativePtr; @@ -93,6 +94,7 @@ class HTTPRequest implements Callback { Request.Builder builder = new Request.Builder() .url(resourceUrl) .tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE)) + .addHeader("Accept-Encoding", "gzip, deflate") .addHeader("User-Agent", getUserAgent()); if (etag.length() > 0) { builder = builder.addHeader("If-None-Match", etag); @@ -149,6 +151,18 @@ class HTTPRequest implements Callback { mLock.lock(); if (mNativePtr != 0) { + String encoding = response.header("Content-Encoding"); + boolean compressed = false; + if (encoding != null) { + if (encoding.equals("gzip") || encoding.equals("deflate")) { + compressed = true; + } else if (!encoding.equals("identity")) { + mLock.unlock(); + handleFailure(call, new UnknownContentEncodingException(encoding)); + return; + } + } + nativeOnResponse(response.code(), response.header("ETag"), response.header("Last-Modified"), @@ -156,7 +170,8 @@ class HTTPRequest implements Callback { response.header("Expires"), response.header("Retry-After"), response.header("x-rate-limit-reset"), - body); + body, + compressed); } mLock.unlock(); } diff --git a/platform/android/src/asset_manager_file_source.cpp b/platform/android/src/asset_manager_file_source.cpp index aa65e3ff4..404177ba9 100644 --- a/platform/android/src/asset_manager_file_source.cpp +++ b/platform/android/src/asset_manager_file_source.cpp @@ -23,8 +23,9 @@ public: Response response; if (AAsset* asset = AAssetManager_open(assetManager, path.c_str(), AASSET_MODE_BUFFER)) { - response.data = std::make_shared<std::string>( - reinterpret_cast<const char*>(AAsset_getBuffer(asset)), AAsset_getLength64(asset)); + response.data = { { reinterpret_cast<const char*>(AAsset_getBuffer(asset)), + static_cast<size_t>(AAsset_getLength64(asset)) }, + false }; AAsset_close(asset); } else { response.error = std::make_unique<Response::Error>(Response::Error::Reason::NotFound, diff --git a/platform/android/src/http_file_source.cpp b/platform/android/src/http_file_source.cpp index 8eb9416e9..7602c1914 100644 --- a/platform/android/src/http_file_source.cpp +++ b/platform/android/src/http_file_source.cpp @@ -30,7 +30,8 @@ public: jni::String etag, jni::String modified, jni::String cacheControl, jni::String expires, jni::String retryAfter, jni::String xRateLimitReset, - jni::Array<jni::jbyte> body); + jni::Array<jni::jbyte> body, + jni::jboolean compressed); static jni::Class<HTTPRequest> javaClass; jni::UniqueObject<HTTPRequest> javaRequest; @@ -104,7 +105,8 @@ void HTTPRequest::onResponse(jni::JNIEnv& env, int code, jni::String etag, jni::String modified, jni::String cacheControl, jni::String expires, jni::String jRetryAfter, jni::String jXRateLimitReset, - jni::Array<jni::jbyte> body) { + jni::Array<jni::jbyte> body, + jni::jboolean compressed) { using Error = Response::Error; @@ -128,11 +130,11 @@ void HTTPRequest::onResponse(jni::JNIEnv& env, int code, if (code == 200) { if (body) { - auto data = std::make_shared<std::string>(body.Length(env), char()); - jni::GetArrayRegion(env, *body, 0, data->size(), reinterpret_cast<jbyte*>(&(*data)[0])); - response.data = data; + std::string data(static_cast<size_t>(body.Length(env)), char()); + jni::GetArrayRegion(env, *body, 0, data.size(), reinterpret_cast<jbyte*>(&data[0])); + response.data = Blob{ std::move(data), static_cast<bool>(compressed) }; } else { - response.data = std::make_shared<std::string>(); + response.data = {}; } } else if (code == 204 || (code == 404 && resource.kind == Resource::Kind::Tile)) { response.noContent = true; diff --git a/platform/android/src/image.cpp b/platform/android/src/image.cpp index 2a33944b1..dd98a3a6c 100644 --- a/platform/android/src/image.cpp +++ b/platform/android/src/image.cpp @@ -8,14 +8,16 @@ namespace mbgl { -PremultipliedImage decodeImage(const std::string& string) { +PremultipliedImage decodeImage(Blob blob) { auto env{ android::AttachEnv() }; - auto array = jni::Array<jni::jbyte>::New(*env, string.size()); - jni::SetArrayRegion(*env, *array, 0, string.size(), - reinterpret_cast<const signed char*>(string.data())); + const auto string = blob.uncompressedData(); - auto bitmap = android::BitmapFactory::DecodeByteArray(*env, array, 0, string.size()); + auto array = jni::Array<jni::jbyte>::New(*env, string->size()); + jni::SetArrayRegion(*env, *array, 0, string->size(), + reinterpret_cast<const signed char*>(string->data())); + + auto bitmap = android::BitmapFactory::DecodeByteArray(*env, array, 0, string->size()); return android::Bitmap::GetImage(*env, bitmap); } diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 67fc13220..c52aad640 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -188,11 +188,11 @@ void NativeMapView::setStyleUrl(jni::JNIEnv& env, jni::String url) { } jni::String NativeMapView::getStyleJson(jni::JNIEnv& env) { - return jni::Make<jni::String>(env, map->getStyle().getJSON()); + return jni::Make<jni::String>(env, *map->getStyle().getJSON().uncompressedData()); } void NativeMapView::setStyleJson(jni::JNIEnv& env, jni::String json) { - map->getStyle().loadJSON(jni::Make<std::string>(env, json)); + map->getStyle().loadJSON(Blob{ jni::Make<std::string>(env, json), false }); } void NativeMapView::setLatLngBounds(jni::JNIEnv& env, jni::Object<mbgl::android::LatLngBounds> jBounds) { diff --git a/platform/darwin/src/http_file_source.mm b/platform/darwin/src/http_file_source.mm index 4a16ad82f..6840740c4 100644 --- a/platform/darwin/src/http_file_source.mm +++ b/platform/darwin/src/http_file_source.mm @@ -232,8 +232,7 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, if (error) { if (data) { - response.data = - std::make_shared<std::string>((const char*)[data bytes], [data length]); + response.data = Blob{ { (const char*)[data bytes], [data length] }, false }; } switch ([error code]) { @@ -287,7 +286,7 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, } if (responseCode == 200) { - response.data = std::make_shared<std::string>((const char *)[data bytes], [data length]); + response.data = { { (const char *)[data bytes], [data length] }, false }; } else if (responseCode == 204 || (responseCode == 404 && resource.kind == Resource::Kind::Tile)) { response.noContent = true; } else if (responseCode == 304) { diff --git a/platform/darwin/src/image.mm b/platform/darwin/src/image.mm index 3a5adcca0..f08fb9a25 100644 --- a/platform/darwin/src/image.mm +++ b/platform/darwin/src/image.mm @@ -71,9 +71,10 @@ mbgl::PremultipliedImage MGLPremultipliedImageFromCGImage(CGImageRef src) { namespace mbgl { -PremultipliedImage decodeImage(const std::string& source) { +PremultipliedImage decodeImage(Blob blob) { + const auto source = blob.uncompressedData(); CFDataHandle data(CFDataCreateWithBytesNoCopy( - kCFAllocatorDefault, reinterpret_cast<const unsigned char*>(source.data()), source.size(), + kCFAllocatorDefault, reinterpret_cast<const unsigned char*>(source->data()), source->size(), kCFAllocatorNull)); if (!data) { throw std::runtime_error("CFDataCreateWithBytesNoCopy failed"); diff --git a/platform/darwin/test/MGLOfflineStorageTests.mm b/platform/darwin/test/MGLOfflineStorageTests.mm index 28c663302..f0d54f81b 100644 --- a/platform/darwin/test/MGLOfflineStorageTests.mm +++ b/platform/darwin/test/MGLOfflineStorageTests.mm @@ -182,8 +182,8 @@ req = fs->request(resource, [&](mbgl::Response res) { req.reset(); XCTAssertFalse(res.error.get(), @"Request should not return an error"); - XCTAssertTrue(res.data.get(), @"Request should return data"); - XCTAssertEqual("{\"api\":\"mapbox\"}", *res.data, @"Request did not return expected data"); + XCTAssertTrue(res.data, @"Request should return data"); + XCTAssertEqual("{\"api\":\"mapbox\"}", *res.data.uncompressedData(), @"Request did not return expected data"); CFRunLoopStop(CFRunLoopGetCurrent()); }); diff --git a/platform/default/asset_file_source.cpp b/platform/default/asset_file_source.cpp index 3063bf88a..0b57a6dce 100644 --- a/platform/default/asset_file_source.cpp +++ b/platform/default/asset_file_source.cpp @@ -44,12 +44,10 @@ public: } else if (result == -1 && errno == ENOENT) { response.error = std::make_unique<Response::Error>(Response::Error::Reason::NotFound); } else { - try { - response.data = std::make_shared<std::string>(util::read_file(path)); - } catch (...) { + response.data = util::readFile(path); + if (!response.data) { response.error = std::make_unique<Response::Error>( - Response::Error::Reason::Other, - util::toString(std::current_exception())); + Response::Error::Reason::Other, "Cannot read file " + path); } } diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp index cb602995a..5dcd28fac 100644 --- a/platform/default/default_file_source.cpp +++ b/platform/default/default_file_source.cpp @@ -151,8 +151,23 @@ public: // Get from the online file source if (resource.hasLoadingMethod(Resource::LoadingMethod::Network)) { + // Always solicit a compressed response so that we can insert it into the database + // while still compressed to save on CPU time. + const auto compression = resource.compression; + resource.compression = Resource::Compression::PreferCompressed; tasks[req] = onlineFileSource.request(resource, [=] (Response onlineResponse) mutable { this->offlineDatabase->put(resource, onlineResponse); + // If the original request expects an uncompressed response, uncompress before + // handing it back. + if (onlineResponse.data && onlineResponse.data.isCompressed() && + compression == Resource::Compression::Uncompressed) { + try { + onlineResponse.data.uncompress(); + } catch (std::exception& ex) { + onlineResponse.error = std::make_unique<Response::Error>( + Response::Error::Reason::Other, ex.what()); + } + } callback(onlineResponse); }); } diff --git a/platform/default/http_file_source.cpp b/platform/default/http_file_source.cpp index a9c442c2d..1a0ae577b 100644 --- a/platform/default/http_file_source.cpp +++ b/platform/default/http_file_source.cpp @@ -371,9 +371,9 @@ void HTTPRequest::handleResult(CURLcode code) { if (responseCode == 200) { if (data) { - response->data = std::move(data); + response->data = Blob{ std::move(data), false }; } else { - response->data = std::make_shared<std::string>(); + response->data = Blob{ "", false }; } } else if (responseCode == 204 || (responseCode == 404 && resource.kind == Resource::Kind::Tile)) { response->noContent = true; diff --git a/platform/default/image.cpp b/platform/default/image.cpp index 447c6bcd6..4fde1898c 100644 --- a/platform/default/image.cpp +++ b/platform/default/image.cpp @@ -11,9 +11,10 @@ PremultipliedImage decodeWebP(const uint8_t*, size_t); PremultipliedImage decodePNG(const uint8_t*, size_t); PremultipliedImage decodeJPEG(const uint8_t*, size_t); -PremultipliedImage decodeImage(const std::string& string) { - const auto* data = reinterpret_cast<const uint8_t*>(string.data()); - const size_t size = string.size(); +PremultipliedImage decodeImage(Blob blob) { + const auto uncompressed = blob.uncompressedData(); + const auto* data = reinterpret_cast<const uint8_t*>(uncompressed->data()); + const size_t size = uncompressed->size(); #if !defined(__ANDROID__) && !defined(__APPLE__) if (size >= 12) { diff --git a/platform/default/local_file_source.cpp b/platform/default/local_file_source.cpp index 0635e86d8..9a8f5ae51 100644 --- a/platform/default/local_file_source.cpp +++ b/platform/default/local_file_source.cpp @@ -46,12 +46,10 @@ public: } else if (result == -1 && errno == ENOENT) { response.error = std::make_unique<Response::Error>(Response::Error::Reason::NotFound); } else { - try { - response.data = std::make_shared<std::string>(util::read_file(path)); - } catch (...) { + response.data = util::readFile(path); + if (!response.data) { response.error = std::make_unique<Response::Error>( - Response::Error::Reason::Other, - util::toString(std::current_exception())); + Response::Error::Reason::Other, "Cannot read file " + path); } } diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp index 65c209718..d7022f1c8 100644 --- a/platform/default/mbgl/storage/offline_database.cpp +++ b/platform/default/mbgl/storage/offline_database.cpp @@ -1,10 +1,10 @@ #include <mbgl/storage/offline_database.hpp> #include <mbgl/storage/response.hpp> -#include <mbgl/util/compression.hpp> #include <mbgl/util/io.hpp> #include <mbgl/util/string.hpp> #include <mbgl/util/chrono.hpp> #include <mbgl/util/logging.hpp> +#include <mbgl/util/compression.hpp> #include "sqlite3.hpp" @@ -153,7 +153,7 @@ optional<Response> OfflineDatabase::get(const Resource& resource) { optional<std::pair<Response, uint64_t>> OfflineDatabase::getInternal(const Resource& resource) { if (resource.kind == Resource::Kind::Tile) { assert(resource.tileData); - return getTile(*resource.tileData); + return getTile(resource); } else { return getResource(resource); } @@ -177,14 +177,31 @@ std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource, return { false, 0 }; } - std::string compressedData; + std::shared_ptr<const std::string> data; bool compressed = false; uint64_t size = 0; if (response.data) { - compressedData = util::compress(*response.data); - compressed = compressedData.size() < response.data->size(); - size = compressed ? compressedData.size() : response.data->size(); + if (response.data.isCompressed()) { + // The response is already compressed; don't try to compare it against the uncompressed size. + compressed = true; + data = response.data.compressedData(); + } else { + data = response.data.uncompressedData(); + + // Only try to compress the data when we have a good chance that the data can actually + // be considerably compressed. + if (util::isCompressible(*data)) { + const auto compressedData = response.data.compressedData(); + if (compressedData->size() < data->size()) { + compressed = true; + data = compressedData; + } + } + } + size = data->size(); + } else { + data = std::make_shared<const std::string>(); } if (evict_ && !evict(size)) { @@ -196,13 +213,9 @@ std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource, if (resource.kind == Resource::Kind::Tile) { assert(resource.tileData); - inserted = putTile(*resource.tileData, response, - compressed ? compressedData : response.data ? *response.data : "", - compressed); + inserted = putTile(*resource.tileData, response, *data, compressed); } else { - inserted = putResource(resource, response, - compressed ? compressedData : response.data ? *response.data : "", - compressed); + inserted = putResource(resource, response, *data, compressed); } return { inserted, size }; @@ -243,12 +256,20 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou optional<std::string> data = stmt->get<optional<std::string>>(4); if (!data) { response.noContent = true; - } else if (stmt->get<bool>(5)) { - response.data = std::make_shared<std::string>(util::decompress(*data)); - size = data->length(); } else { - response.data = std::make_shared<std::string>(*data); + response.data = { std::move(*data), stmt->get<bool>(5) }; size = data->length(); + + // Make sure the data is decompressed when the user explicitly requested uncompressed data. + if (response.data.isCompressed() && + resource.compression == Resource::Compression::Uncompressed) { + try { + response.data.uncompress(); + } catch (std::exception& ex) { + response.error = + std::make_unique<Response::Error>(Response::Error::Reason::Other, ex.what()); + } + } } return std::make_pair(response, size); @@ -359,7 +380,9 @@ bool OfflineDatabase::putResource(const Resource& resource, return true; } -optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource::TileData& tile) { +optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource& resource) { + const auto& tile = *resource.tileData; + // clang-format off Statement accessedStmt = getStatement( "UPDATE tiles " @@ -412,12 +435,20 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource: optional<std::string> data = stmt->get<optional<std::string>>(4); if (!data) { response.noContent = true; - } else if (stmt->get<bool>(5)) { - response.data = std::make_shared<std::string>(util::decompress(*data)); - size = data->length(); } else { - response.data = std::make_shared<std::string>(*data); + response.data = { std::move(*data), stmt->get<bool>(5) }; size = data->length(); + + // Make sure the data is decompressed when the user explicitly requested uncompressed data. + if (response.data.isCompressed() && + resource.compression == Resource::Compression::Uncompressed) { + try { + response.data.uncompress(); + } catch (std::exception& ex) { + response.error = + std::make_unique<Response::Error>(Response::Error::Reason::Other, ex.what()); + } + } } return std::make_pair(response, size); diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp index 91b544a9e..fba1a3c23 100644 --- a/platform/default/mbgl/storage/offline_database.hpp +++ b/platform/default/mbgl/storage/offline_database.hpp @@ -81,7 +81,7 @@ private: Statement getStatement(const char *); - optional<std::pair<Response, uint64_t>> getTile(const Resource::TileData&); + optional<std::pair<Response, uint64_t>> getTile(const Resource&); optional<int64_t> hasTile(const Resource::TileData&); bool putTile(const Resource::TileData&, const Response&, const std::string&, bool compressed); diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp index ba504c1f9..d8fe8c646 100644 --- a/platform/default/mbgl/storage/offline_download.cpp +++ b/platform/default/mbgl/storage/offline_download.cpp @@ -71,7 +71,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { } style::Parser parser; - parser.parse(*styleResponse->data); + parser.parse(*styleResponse->data.uncompressedData()); result.requiredResourceCountIsPrecise = true; @@ -88,7 +88,8 @@ OfflineRegionStatus OfflineDownload::getStatus() const { optional<Response> sourceResponse = offlineDatabase.get(Resource::source(url)); if (sourceResponse) { style::conversion::Error error; - optional<Tileset> tileset = style::conversion::convertJSON<Tileset>(*sourceResponse->data, error); + optional<Tileset> tileset = style::conversion::convertJSON<Tileset>( + *sourceResponse->data.uncompressedData(), error); if (tileset) { result.requiredResourceCount += definition.tileCount(type, tileSize, (*tileset).zoomRange); @@ -160,7 +161,7 @@ void OfflineDownload::activateDownload() { status.requiredResourceCountIsPrecise = true; style::Parser parser; - parser.parse(*styleResponse.data); + parser.parse(*styleResponse.data.uncompressedData()); for (const auto& source : parser.sources) { SourceType type = source->getType(); @@ -176,7 +177,8 @@ void OfflineDownload::activateDownload() { ensureResource(Resource::source(url), [=](Response sourceResponse) { style::conversion::Error error; - optional<Tileset> tileset = style::conversion::convertJSON<Tileset>(*sourceResponse.data, error); + optional<Tileset> tileset = style::conversion::convertJSON<Tileset>( + *sourceResponse.data.uncompressedData(), error); if (tileset) { util::mapbox::canonicalizeTileset(*tileset, url, type, tileSize); queueTiles(type, tileSize, *tileset); @@ -236,14 +238,18 @@ void OfflineDownload::activateDownload() { if (!parser.glyphURL.empty()) { for (const auto& fontStack : parser.fontStacks()) { for (char16_t i = 0; i < GLYPH_RANGES_PER_FONT_STACK; i++) { - queueResource(Resource::glyphs(parser.glyphURL, fontStack, getGlyphRange(i * GLYPHS_PER_GLYPH_RANGE))); + auto resource = Resource::glyphs(parser.glyphURL, fontStack, getGlyphRange(i * GLYPHS_PER_GLYPH_RANGE)); + resource.compression = Resource::Compression::PreferCompressed; + queueResource(resource); } } } if (!parser.spriteURL.empty()) { queueResource(Resource::spriteImage(parser.spriteURL, definition.pixelRatio)); - queueResource(Resource::spriteJSON(parser.spriteURL, definition.pixelRatio)); + auto spriteJSON = Resource::spriteJSON(parser.spriteURL, definition.pixelRatio); + spriteJSON.compression = Resource::Compression::PreferCompressed; + queueResource(spriteJSON); } continueDownload(); diff --git a/platform/default/online_file_source.cpp b/platform/default/online_file_source.cpp index d685109b9..fe77df8c0 100644 --- a/platform/default/online_file_source.cpp +++ b/platform/default/online_file_source.cpp @@ -374,6 +374,16 @@ void OnlineFileRequest::completed(Response response) { failedRequestReason = Response::Error::Reason::Success; } + // Make sure the data is decompressed when the user explicitly requested uncompressed data. + if (response.data && response.data.isCompressed() && + resource.compression == Resource::Compression::Uncompressed) { + try { + response.data.uncompress(); + } catch (std::exception& ex) { + response.error = std::make_unique<Response::Error>(Response::Error::Reason::Other, ex.what()); + } + } + schedule(response.expires); // Calling the callback may result in `this` being deleted. It needs to be done last, diff --git a/platform/default/png_writer.cpp b/platform/default/png_writer.cpp index 9ef905215..d3297b8eb 100644 --- a/platform/default/png_writer.cpp +++ b/platform/default/png_writer.cpp @@ -38,7 +38,7 @@ void addChunk(std::string& png, const char* type, const char* data = "", const u namespace mbgl { // Encode PNGs without libpng. -std::string encodePNG(const PremultipliedImage& pre) { +Blob encodePNG(const PremultipliedImage& pre) { // Make copy of the image so that we can unpremultiply it. const auto src = util::unpremultiply(pre.clone()); @@ -74,7 +74,7 @@ std::string encodePNG(const PremultipliedImage& pre) { addChunk(png, "IHDR", ihdr, 13); addChunk(png, "IDAT", idat.data(), static_cast<uint32_t>(idat.size())); addChunk(png, "IEND"); - return png; + return { std::move(png), false }; } } // namespace mbgl diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 07fae5945..c29c2ec89 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -211,6 +211,7 @@ 40F887701D7A1E58008ECB67 /* MGLShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */; }; 40F887711D7A1E59008ECB67 /* MGLShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */; }; 40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */; }; + 55434256203C7530002624EB /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 55434255203C7530002624EB /* libz.tbd */; }; 5549A0381EF1D86B00073113 /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5549A0371EF1D86B00073113 /* libmbgl-core.a */; }; 5549A0391EF2877100073113 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; }; 5549A03A1EF2877500073113 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; }; @@ -794,6 +795,7 @@ 40FDA7691CCAAA6800442548 /* MBXAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXAnnotationView.h; sourceTree = "<group>"; }; 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXAnnotationView.m; sourceTree = "<group>"; }; 554180411D2E97DE00012372 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; + 55434255203C7530002624EB /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 5549A0371EF1D86B00073113 /* libmbgl-core.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libmbgl-core.a"; path = "../../build/ios/Debug-iphonesimulator/libmbgl-core.a"; sourceTree = "<group>"; }; 556660C91E1BF3A900E2C41B /* MGLFoundation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLFoundation.h; sourceTree = "<group>"; }; 556660D71E1D085500E2C41B /* MGLVersionNumber.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLVersionNumber.m; path = ../../darwin/test/MGLVersionNumber.m; sourceTree = "<group>"; }; @@ -1145,6 +1147,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 55434256203C7530002624EB /* libz.tbd in Frameworks */, 5549A0381EF1D86B00073113 /* libmbgl-core.a in Frameworks */, DA2E88561CC036F400F24E7B /* Mapbox.framework in Frameworks */, ); @@ -1477,6 +1480,7 @@ DA1DC9921CB6DF24006E619F /* Frameworks */ = { isa = PBXGroup; children = ( + 55434255203C7530002624EB /* libz.tbd */, 55D120AD1F791018004B6D81 /* libmbgl-loop-darwin.a */, 55D120AB1F791015004B6D81 /* libmbgl-filesource.a */, 55D120A91F79100C004B6D81 /* libmbgl-filesource.a */, diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 432767091..58f29ed2a 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -80,6 +80,7 @@ 52B5D17F1E5E26DF00BBCB48 /* libmbgl-loop-darwin.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5548BE7B1D0ACBBD005DDE81 /* libmbgl-loop-darwin.a */; }; 52B5D1801E5E26DF00BBCB48 /* libmbgl-loop-darwin.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5548BE7B1D0ACBBD005DDE81 /* libmbgl-loop-darwin.a */; }; 5548BE781D09E718005DDE81 /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAE6C3451CC31D1200DB3429 /* libmbgl-core.a */; }; + 555CF3EC203DA46F004C828F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D9B4B01D005D3900C1CCE2 /* libz.tbd */; }; 556660C61E1BEA0100E2C41B /* MGLFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 556660C51E1BEA0100E2C41B /* MGLFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 556660D61E1D07E400E2C41B /* MGLVersionNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = 556660D51E1D07E400E2C41B /* MGLVersionNumber.m */; }; 558DE7A61E56161C00C7916D /* MGLFoundation_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 558DE7A41E56161C00C7916D /* MGLFoundation_Private.h */; }; @@ -659,6 +660,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 555CF3EC203DA46F004C828F /* libz.tbd in Frameworks */, DAE0DD7A1D5F015A005A6BB1 /* libmbgl-core.a in Frameworks */, 55D120A51F790A0C004B6D81 /* libmbgl-filesource.a in Frameworks */, DAE6C3321CC30DB200DB3429 /* Mapbox.framework in Frameworks */, diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index ac14df022..a4f6f6a9d 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -214,8 +214,8 @@ void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) { } try { - nodeMap->map->getStyle().loadJSON(style); - } catch (const std::exception &ex) { + nodeMap->map->getStyle().loadJSON(mbgl::Blob{ std::move(style), false }); + } catch (const std::exception& ex) { return Nan::ThrowError(ex.what()); } diff --git a/platform/node/src/node_request.cpp b/platform/node/src/node_request.cpp index de16710f7..7b2b57fd1 100644 --- a/platform/node/src/node_request.cpp +++ b/platform/node/src/node_request.cpp @@ -99,10 +99,8 @@ void NodeRequest::HandleCallback(const Nan::FunctionCallbackInfo<v8::Value>& inf if (Nan::Has(res, Nan::New("data").ToLocalChecked()).FromJust()) { auto data = Nan::Get(res, Nan::New("data").ToLocalChecked()).ToLocalChecked(); if (node::Buffer::HasInstance(data)) { - response.data = std::make_shared<std::string>( - node::Buffer::Data(data), - node::Buffer::Length(data) - ); + response.data = { std::string{ node::Buffer::Data(data), node::Buffer::Length(data), }, + false }; } else { return Nan::ThrowTypeError("Response data must be a Buffer"); } diff --git a/platform/qt/src/http_request.cpp b/platform/qt/src/http_request.cpp index ea3f388bd..4e2143ba8 100644 --- a/platform/qt/src/http_request.cpp +++ b/platform/qt/src/http_request.cpp @@ -99,9 +99,9 @@ void HTTPRequest::handleNetworkReply(QNetworkReply *reply, const QByteArray& dat switch(responseCode) { case 200: { if (data.isEmpty()) { - response.data = std::make_shared<std::string>(); + response.data = Blob{ "", false }; } else { - response.data = std::make_shared<std::string>(data.constData(), data.size()); + response.data = Blob{ std::string{ data.constData(), static_cast<size_t>(data.size()) }, false }; } break; } diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 414b65255..e054d8848 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -585,12 +585,12 @@ void QMapboxGL::cycleDebugOptions() */ QString QMapboxGL::styleJson() const { - return QString::fromStdString(d_ptr->mapObj->getStyle().getJSON()); + return QString::fromStdString(*d_ptr->mapObj->getStyle().getJSON().uncompressedData()); } void QMapboxGL::setStyleJson(const QString &style) { - d_ptr->mapObj->getStyle().loadJSON(style.toStdString()); + d_ptr->mapObj->getStyle().loadJSON(mbgl::Blob{ style.toStdString(), false }); } /*! diff --git a/platform/qt/src/qt_image.cpp b/platform/qt/src/qt_image.cpp index a5c92514c..ff45b02ef 100644 --- a/platform/qt/src/qt_image.cpp +++ b/platform/qt/src/qt_image.cpp @@ -6,7 +6,7 @@ namespace mbgl { -std::string encodePNG(const PremultipliedImage& pre) { +Blob encodePNG(const PremultipliedImage& pre) { QImage image(pre.data.get(), pre.size.width, pre.size.height, QImage::Format_ARGB32_Premultiplied); @@ -16,7 +16,7 @@ std::string encodePNG(const PremultipliedImage& pre) { buffer.open(QIODevice::WriteOnly); image.rgbSwapped().save(&buffer, "PNG"); - return std::string(array.constData(), array.size()); + return { std::string(array.constData(), array.size()), false }; } #if !defined(QT_IMAGE_DECODERS) @@ -24,9 +24,10 @@ PremultipliedImage decodeJPEG(const uint8_t*, size_t); PremultipliedImage decodeWebP(const uint8_t*, size_t); #endif -PremultipliedImage decodeImage(const std::string& string) { - const uint8_t* data = reinterpret_cast<const uint8_t*>(string.data()); - const size_t size = string.size(); +PremultipliedImage decodeImage(Blob blob) { + const auto uncompressed = blob.uncompressedData(); + const uint8_t* data = reinterpret_cast<const uint8_t*>(uncompressed->data()); + const size_t size = uncompressed->size(); #if !defined(QT_IMAGE_DECODERS) if (size >= 12) { diff --git a/platform/qt/test/qmapboxgl.test.cpp b/platform/qt/test/qmapboxgl.test.cpp index 2a56b346a..607cdd6ba 100644 --- a/platform/qt/test/qmapboxgl.test.cpp +++ b/platform/qt/test/qmapboxgl.test.cpp @@ -47,7 +47,7 @@ void QMapboxGLTest::onNeedsRendering() { TEST_F(QMapboxGLTest, TEST_DISABLED_ON_CI(styleJson)) { QString json = QString::fromStdString( - mbgl::util::read_file("test/fixtures/resources/style_vector.json")); + *mbgl::util::readFile("test/fixtures/resources/style_vector.json").uncompressedData()); map.setStyleJson(json); ASSERT_EQ(map.styleJson(), json); diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp index 3b54ec194..64e3a355d 100644 --- a/src/mbgl/gl/program.hpp +++ b/src/mbgl/gl/program.hpp @@ -62,7 +62,7 @@ public: try { if (auto cachedBinaryProgram = util::readFile(*cachePath)) { - const BinaryProgram binaryProgram(std::move(*cachedBinaryProgram)); + const BinaryProgram binaryProgram(cachedBinaryProgram); if (binaryProgram.identifier() == identifier) { return Program { context, binaryProgram }; } else { @@ -82,7 +82,7 @@ public: try { if (const auto binaryProgram = result.template get<BinaryProgram>(context, identifier)) { - util::write_file(*cachePath, binaryProgram->serialize()); + util::writeFile(*cachePath, binaryProgram->serialize()); Log::Warning(Event::OpenGL, "Caching program in: %s", (*cachePath).c_str()); } } catch (std::runtime_error& error) { diff --git a/src/mbgl/programs/binary_program.cpp b/src/mbgl/programs/binary_program.cpp index da629194b..e5943bb5d 100644 --- a/src/mbgl/programs/binary_program.cpp +++ b/src/mbgl/programs/binary_program.cpp @@ -32,9 +32,10 @@ static std::pair<const std::string, Binding> parseBinding(protozero::pbf_reader& namespace mbgl { -BinaryProgram::BinaryProgram(std::string&& data) { +BinaryProgram::BinaryProgram(Blob blob) { + const auto data = blob.uncompressedData(); bool hasFormat = false, hasCode = false; - protozero::pbf_reader pbf(data); + protozero::pbf_reader pbf(*data); while (pbf.next()) { switch (pbf.tag()) { case 1: // format @@ -76,7 +77,7 @@ BinaryProgram::BinaryProgram( uniforms(std::move(uniforms_)) { } -std::string BinaryProgram::serialize() const { +Blob BinaryProgram::serialize() const { std::string data; data.reserve(32 + binaryCode.size() + uniforms.size() * 32 + attributes.size() * 32); protozero::pbf_writer pbf(data); @@ -95,7 +96,7 @@ std::string BinaryProgram::serialize() const { if (!binaryIdentifier.empty()) { pbf.add_string(5 /* identifier */, binaryIdentifier); } - return data; + return { std::move(data), false }; } optional<gl::AttributeLocation> BinaryProgram::attributeLocation(const std::string& name) const { diff --git a/src/mbgl/programs/binary_program.hpp b/src/mbgl/programs/binary_program.hpp index 8690f3fd6..2d3ea45aa 100644 --- a/src/mbgl/programs/binary_program.hpp +++ b/src/mbgl/programs/binary_program.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/gl/types.hpp> +#include <mbgl/util/blob.hpp> #include <mbgl/util/optional.hpp> #include <string> @@ -11,7 +12,7 @@ namespace mbgl { class BinaryProgram { public: // Initialize a BinaryProgram object from a serialized represenation. - BinaryProgram(std::string&& data); + BinaryProgram(Blob data); BinaryProgram(gl::BinaryProgramFormat, std::string&& binaryCode, @@ -19,7 +20,7 @@ public: std::vector<std::pair<const std::string, gl::AttributeLocation>>&&, std::vector<std::pair<const std::string, gl::UniformLocation>>&&); - std::string serialize() const; + Blob serialize() const; gl::BinaryProgramFormat format() const { return binaryFormat; diff --git a/src/mbgl/sprite/sprite_loader.cpp b/src/mbgl/sprite/sprite_loader.cpp index 93d6dfd9a..3bc8add16 100644 --- a/src/mbgl/sprite/sprite_loader.cpp +++ b/src/mbgl/sprite/sprite_loader.cpp @@ -25,8 +25,8 @@ struct SpriteLoader::Loader { worker(scheduler, ActorRef<SpriteLoader>(imageManager, mailbox)) { } - std::shared_ptr<const std::string> image; - std::shared_ptr<const std::string> json; + Blob image; + Blob json; std::unique_ptr<AsyncRequest> jsonRequest; std::unique_ptr<AsyncRequest> spriteRequest; std::shared_ptr<Mailbox> mailbox; @@ -55,7 +55,7 @@ void SpriteLoader::load(const std::string& url, Scheduler& scheduler, FileSource } else if (res.notModified) { return; } else if (res.noContent) { - loader->json = std::make_shared<const std::string>(); + loader->json = {}; emitSpriteLoadedIfComplete(); } else { // Only trigger a sprite loaded event we got new data. @@ -70,7 +70,7 @@ void SpriteLoader::load(const std::string& url, Scheduler& scheduler, FileSource } else if (res.notModified) { return; } else if (res.noContent) { - loader->image = std::make_shared<const std::string>(); + loader->image = {}; emitSpriteLoadedIfComplete(); } else { loader->image = res.data; diff --git a/src/mbgl/sprite/sprite_loader_worker.cpp b/src/mbgl/sprite/sprite_loader_worker.cpp index 4bded33d5..a2e3f331e 100644 --- a/src/mbgl/sprite/sprite_loader_worker.cpp +++ b/src/mbgl/sprite/sprite_loader_worker.cpp @@ -8,8 +8,7 @@ SpriteLoaderWorker::SpriteLoaderWorker(ActorRef<SpriteLoaderWorker>, ActorRef<Sp : parent(std::move(parent_)) { } -void SpriteLoaderWorker::parse(std::shared_ptr<const std::string> image, - std::shared_ptr<const std::string> json) { +void SpriteLoaderWorker::parse(Blob image, Blob json) { try { if (!image) { // This shouldn't happen, since we always invoke it with a non-empty pointer. @@ -20,7 +19,7 @@ void SpriteLoaderWorker::parse(std::shared_ptr<const std::string> image, throw std::runtime_error("missing sprite metadata"); } - parent.invoke(&SpriteLoader::onParsed, parseSprite(*image, *json)); + parent.invoke(&SpriteLoader::onParsed, parseSprite(std::move(image), std::move(json))); } catch (...) { parent.invoke(&SpriteLoader::onError, std::current_exception()); } diff --git a/src/mbgl/sprite/sprite_loader_worker.hpp b/src/mbgl/sprite/sprite_loader_worker.hpp index d61e07d14..4f2e38be2 100644 --- a/src/mbgl/sprite/sprite_loader_worker.hpp +++ b/src/mbgl/sprite/sprite_loader_worker.hpp @@ -2,6 +2,7 @@ #include <mbgl/actor/actor_ref.hpp> #include <mbgl/sprite/sprite_parser.hpp> +#include <mbgl/storage/response.hpp> #include <memory> #include <string> @@ -14,7 +15,7 @@ class SpriteLoaderWorker { public: SpriteLoaderWorker(ActorRef<SpriteLoaderWorker>, ActorRef<SpriteLoader>); - void parse(std::shared_ptr<const std::string> image, std::shared_ptr<const std::string> json); + void parse(Blob image, Blob json); private: ActorRef<SpriteLoader> parent; diff --git a/src/mbgl/sprite/sprite_parser.cpp b/src/mbgl/sprite/sprite_parser.cpp index 1a36e3e99..c93d97ba7 100644 --- a/src/mbgl/sprite/sprite_parser.cpp +++ b/src/mbgl/sprite/sprite_parser.cpp @@ -85,11 +85,12 @@ bool getBoolean(const JSValue& value, const char* name, const bool def = false) } // namespace -std::vector<std::unique_ptr<style::Image>> parseSprite(const std::string& encodedImage, const std::string& json) { - const PremultipliedImage raster = decodeImage(encodedImage); +std::vector<std::unique_ptr<style::Image>> parseSprite(Blob imageBlob, Blob jsonBlob) { + const PremultipliedImage raster = decodeImage(imageBlob); + const auto json = jsonBlob.uncompressedData(); JSDocument doc; - doc.Parse<0>(json.c_str()); + 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(); diff --git a/src/mbgl/sprite/sprite_parser.hpp b/src/mbgl/sprite/sprite_parser.hpp index f602818d3..b17f4cc45 100644 --- a/src/mbgl/sprite/sprite_parser.hpp +++ b/src/mbgl/sprite/sprite_parser.hpp @@ -23,6 +23,6 @@ std::unique_ptr<style::Image> createStyleImage(const std::string& id, bool sdf); // Parses an image and an associated JSON file and returns the sprite objects. -std::vector<std::unique_ptr<style::Image>> parseSprite(const std::string& image, const std::string& json); +std::vector<std::unique_ptr<style::Image>> parseSprite(Blob image, Blob json); } // namespace mbgl diff --git a/src/mbgl/storage/resource.cpp b/src/mbgl/storage/resource.cpp index 207dd2ee6..9bc8495d5 100644 --- a/src/mbgl/storage/resource.cpp +++ b/src/mbgl/storage/resource.cpp @@ -42,14 +42,20 @@ static std::string getTileBBox(int32_t x, int32_t y, int8_t z) { Resource Resource::style(const std::string& url) { return Resource { Resource::Kind::Style, - url + url, + {}, + LoadingMethod::All, + Compression::Uncompressed }; } Resource Resource::source(const std::string& url) { return Resource { Resource::Kind::Source, - url + url, + {}, + LoadingMethod::All, + Compression::Uncompressed }; } @@ -87,7 +93,10 @@ Resource Resource::glyphs(const std::string& urlTemplate, const FontStack& fontS } else { return std::string(); } - }) + }), + {}, + LoadingMethod::All, + Compression::Uncompressed }; } @@ -133,7 +142,8 @@ Resource Resource::tile(const std::string& urlTemplate, y, z }, - loadingMethod + loadingMethod, + Compression::PreferCompressed }; } diff --git a/src/mbgl/style/sources/geojson_source.cpp b/src/mbgl/style/sources/geojson_source.cpp index 4e3478322..48308ccbc 100644 --- a/src/mbgl/style/sources/geojson_source.cpp +++ b/src/mbgl/style/sources/geojson_source.cpp @@ -61,7 +61,8 @@ void GeoJSONSource::loadDescription(FileSource& fileSource) { *this, std::make_exception_ptr(std::runtime_error("unexpectedly empty GeoJSON"))); } else { conversion::Error error; - optional<GeoJSON> geoJSON = conversion::convertJSON<GeoJSON>(*res.data, error); + optional<GeoJSON> geoJSON = + conversion::convertJSON<GeoJSON>(*res.data.uncompressedData(), error); if (!geoJSON) { Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s", error.message.c_str()); diff --git a/src/mbgl/style/sources/image_source.cpp b/src/mbgl/style/sources/image_source.cpp index fa268da0e..5be1b5441 100644 --- a/src/mbgl/style/sources/image_source.cpp +++ b/src/mbgl/style/sources/image_source.cpp @@ -70,7 +70,7 @@ void ImageSource::loadDescription(FileSource& fileSource) { observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty image url"))); } else { try { - baseImpl = makeMutable<Impl>(impl(), decodeImage(*res.data)); + baseImpl = makeMutable<Impl>(impl(), decodeImage(res.data)); } catch (...) { observer->onSourceError(*this, std::current_exception()); } diff --git a/src/mbgl/style/sources/raster_source.cpp b/src/mbgl/style/sources/raster_source.cpp index 53f29d660..c3c47567a 100644 --- a/src/mbgl/style/sources/raster_source.cpp +++ b/src/mbgl/style/sources/raster_source.cpp @@ -57,7 +57,7 @@ void RasterSource::loadDescription(FileSource& fileSource) { observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty TileJSON"))); } else { conversion::Error error; - optional<Tileset> tileset = conversion::convertJSON<Tileset>(*res.data, error); + optional<Tileset> tileset = conversion::convertJSON<Tileset>(*res.data.uncompressedData(), error); if (!tileset) { observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(error.message))); return; diff --git a/src/mbgl/style/sources/vector_source.cpp b/src/mbgl/style/sources/vector_source.cpp index ccdd453c7..d88622f1b 100644 --- a/src/mbgl/style/sources/vector_source.cpp +++ b/src/mbgl/style/sources/vector_source.cpp @@ -54,7 +54,8 @@ void VectorSource::loadDescription(FileSource& fileSource) { observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty TileJSON"))); } else { conversion::Error error; - optional<Tileset> tileset = conversion::convertJSON<Tileset>(*res.data, error); + optional<Tileset> tileset = + conversion::convertJSON<Tileset>(*res.data.uncompressedData(), error); if (!tileset) { observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(error.message))); return; diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index bd8631fc5..82f421ecd 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -14,7 +14,7 @@ Style::Style(Scheduler& scheduler, FileSource& fileSource, float pixelRatio) Style::~Style() = default; -void Style::loadJSON(const std::string& json) { +void Style::loadJSON(Blob json) { impl->loadJSON(json); } @@ -22,7 +22,7 @@ void Style::loadURL(const std::string& url) { impl->loadURL(url); } -std::string Style::getJSON() const { +Blob Style::getJSON() const { return impl->getJSON(); } diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp index d330b3120..a952ccb8c 100644 --- a/src/mbgl/style/style_impl.cpp +++ b/src/mbgl/style/style_impl.cpp @@ -39,7 +39,7 @@ Style::Impl::Impl(Scheduler& scheduler_, FileSource& fileSource_, float pixelRat Style::Impl::~Impl() = default; -void Style::Impl::loadJSON(const std::string& json_) { +void Style::Impl::loadJSON(Blob json_) { lastError = nullptr; observer->onStyleLoading(); @@ -73,15 +73,17 @@ void Style::Impl::loadURL(const std::string& url_) { } else if (res.notModified || res.noContent) { return; } else { - parse(*res.data); + parse(res.data); } }); } -void Style::Impl::parse(const std::string& json_) { +void Style::Impl::parse(Blob json_) { Parser parser; - if (auto error = parser.parse(json_)) { + const auto data = json_.uncompressedData(); + + if (auto error = parser.parse(*data)) { std::string message = "Failed to parse style: " + util::toString(error); Log::Error(Event::ParseStyle, message.c_str()); observer->onStyleError(std::make_exception_ptr(util::StyleParseException(message))); @@ -124,7 +126,7 @@ void Style::Impl::parse(const std::string& json_) { observer->onStyleLoaded(); } -std::string Style::Impl::getJSON() const { +Blob Style::Impl::getJSON() const { return json; } diff --git a/src/mbgl/style/style_impl.hpp b/src/mbgl/style/style_impl.hpp index 3dc222bfa..9dc270439 100644 --- a/src/mbgl/style/style_impl.hpp +++ b/src/mbgl/style/style_impl.hpp @@ -41,10 +41,10 @@ public: Impl(Scheduler&, FileSource&, float pixelRatio); ~Impl() override; - void loadJSON(const std::string&); + void loadJSON(Blob); void loadURL(const std::string&); - std::string getJSON() const; + Blob getJSON() const; std::string getURL() const; void setObserver(Observer*); @@ -96,13 +96,13 @@ public: bool spriteLoaded = false; private: - void parse(const std::string&); + void parse(Blob); Scheduler& scheduler; FileSource& fileSource; std::string url; - std::string json; + Blob json; std::unique_ptr<AsyncRequest> styleRequest; std::unique_ptr<SpriteLoader> spriteLoader; diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp index 313041890..37574eff1 100644 --- a/src/mbgl/text/glyph_manager.cpp +++ b/src/mbgl/text/glyph_manager.cpp @@ -91,7 +91,7 @@ void GlyphManager::processResponse(const Response& res, const FontStack& fontSta std::vector<Glyph> glyphs; try { - glyphs = parseGlyphPBF(range, *res.data); + glyphs = parseGlyphPBF(range, res.data); } catch (...) { observer->onGlyphsError(fontStack, range, std::current_exception()); return; diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp index cfaf803f7..77ea5ec2c 100644 --- a/src/mbgl/text/glyph_pbf.cpp +++ b/src/mbgl/text/glyph_pbf.cpp @@ -4,11 +4,12 @@ namespace mbgl { -std::vector<Glyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string& data) { +std::vector<Glyph> parseGlyphPBF(const GlyphRange& glyphRange, Blob blob) { + const auto data = blob.uncompressedData(); std::vector<Glyph> result; result.reserve(256); - protozero::pbf_reader glyphs_pbf(data); + protozero::pbf_reader glyphs_pbf(*data); while (glyphs_pbf.next(1)) { auto fontstack_pbf = glyphs_pbf.get_message(); diff --git a/src/mbgl/text/glyph_pbf.hpp b/src/mbgl/text/glyph_pbf.hpp index 28a28b411..0c82343ce 100644 --- a/src/mbgl/text/glyph_pbf.hpp +++ b/src/mbgl/text/glyph_pbf.hpp @@ -8,6 +8,6 @@ namespace mbgl { -std::vector<Glyph> parseGlyphPBF(const GlyphRange&, const std::string& data); +std::vector<Glyph> parseGlyphPBF(const GlyphRange&, Blob blob); } // namespace mbgl diff --git a/src/mbgl/tile/raster_dem_tile.cpp b/src/mbgl/tile/raster_dem_tile.cpp index 5db298cf4..10f22e378 100644 --- a/src/mbgl/tile/raster_dem_tile.cpp +++ b/src/mbgl/tile/raster_dem_tile.cpp @@ -45,7 +45,7 @@ void RasterDEMTile::setMetadata(optional<Timestamp> modified_, optional<Timestam expires = expires_; } -void RasterDEMTile::setData(std::shared_ptr<const std::string> data) { +void RasterDEMTile::setData(Blob data) { pending = true; ++correlationID; worker.invoke(&RasterDEMTileWorker::parse, data, correlationID, encoding); diff --git a/src/mbgl/tile/raster_dem_tile.hpp b/src/mbgl/tile/raster_dem_tile.hpp index 0c8dd7596..0c5b3e7bf 100644 --- a/src/mbgl/tile/raster_dem_tile.hpp +++ b/src/mbgl/tile/raster_dem_tile.hpp @@ -70,7 +70,7 @@ public: void setError(std::exception_ptr); void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires); - void setData(std::shared_ptr<const std::string> data); + void setData(Blob data); void upload(gl::Context&) override; Bucket* getBucket(const style::Layer::Impl&) const override; diff --git a/src/mbgl/tile/raster_dem_tile_worker.cpp b/src/mbgl/tile/raster_dem_tile_worker.cpp index 7338e578c..ec5aba380 100644 --- a/src/mbgl/tile/raster_dem_tile_worker.cpp +++ b/src/mbgl/tile/raster_dem_tile_worker.cpp @@ -10,14 +10,14 @@ RasterDEMTileWorker::RasterDEMTileWorker(ActorRef<RasterDEMTileWorker>, ActorRef : parent(std::move(parent_)) { } -void RasterDEMTileWorker::parse(std::shared_ptr<const std::string> data, uint64_t correlationID, Tileset::DEMEncoding encoding) { +void RasterDEMTileWorker::parse(Blob data, uint64_t correlationID, Tileset::DEMEncoding encoding) { if (!data) { parent.invoke(&RasterDEMTile::onParsed, nullptr, correlationID); // No data; empty tile. return; } try { - auto bucket = std::make_unique<HillshadeBucket>(decodeImage(*data), encoding); + auto bucket = std::make_unique<HillshadeBucket>(decodeImage(data), encoding); parent.invoke(&RasterDEMTile::onParsed, std::move(bucket), correlationID); } catch (...) { parent.invoke(&RasterDEMTile::onError, std::current_exception(), correlationID); diff --git a/src/mbgl/tile/raster_dem_tile_worker.hpp b/src/mbgl/tile/raster_dem_tile_worker.hpp index 5a8222bc2..5b9bce1a1 100644 --- a/src/mbgl/tile/raster_dem_tile_worker.hpp +++ b/src/mbgl/tile/raster_dem_tile_worker.hpp @@ -2,6 +2,7 @@ #include <mbgl/actor/actor_ref.hpp> #include <mbgl/util/tileset.hpp> +#include <mbgl/util/blob.hpp> #include <memory> #include <string> @@ -14,7 +15,7 @@ class RasterDEMTileWorker { public: RasterDEMTileWorker(ActorRef<RasterDEMTileWorker>, ActorRef<RasterDEMTile>); - void parse(std::shared_ptr<const std::string> data, uint64_t correlationID, Tileset::DEMEncoding encoding); + void parse(Blob data, uint64_t correlationID, Tileset::DEMEncoding encoding); private: ActorRef<RasterDEMTile> parent; diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp index ff23d4493..4bc9830fd 100644 --- a/src/mbgl/tile/raster_tile.cpp +++ b/src/mbgl/tile/raster_tile.cpp @@ -34,7 +34,7 @@ void RasterTile::setMetadata(optional<Timestamp> modified_, optional<Timestamp> expires = expires_; } -void RasterTile::setData(std::shared_ptr<const std::string> data) { +void RasterTile::setData(Blob data) { pending = true; ++correlationID; worker.invoke(&RasterTileWorker::parse, data, correlationID); diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp index e25329119..c1fa6caa9 100644 --- a/src/mbgl/tile/raster_tile.hpp +++ b/src/mbgl/tile/raster_tile.hpp @@ -26,7 +26,7 @@ public: void setError(std::exception_ptr); void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires); - void setData(std::shared_ptr<const std::string> data); + void setData(Blob data); void upload(gl::Context&) override; Bucket* getBucket(const style::Layer::Impl&) const override; diff --git a/src/mbgl/tile/raster_tile_worker.cpp b/src/mbgl/tile/raster_tile_worker.cpp index 4afa87642..d0dd8918a 100644 --- a/src/mbgl/tile/raster_tile_worker.cpp +++ b/src/mbgl/tile/raster_tile_worker.cpp @@ -10,14 +10,14 @@ RasterTileWorker::RasterTileWorker(ActorRef<RasterTileWorker>, ActorRef<RasterTi : parent(std::move(parent_)) { } -void RasterTileWorker::parse(std::shared_ptr<const std::string> data, uint64_t correlationID) { +void RasterTileWorker::parse(Blob data, uint64_t correlationID) { if (!data) { parent.invoke(&RasterTile::onParsed, nullptr, correlationID); // No data; empty tile. return; } try { - auto bucket = std::make_unique<RasterBucket>(decodeImage(*data)); + auto bucket = std::make_unique<RasterBucket>(decodeImage(data)); parent.invoke(&RasterTile::onParsed, std::move(bucket), correlationID); } catch (...) { parent.invoke(&RasterTile::onError, std::current_exception(), correlationID); diff --git a/src/mbgl/tile/raster_tile_worker.hpp b/src/mbgl/tile/raster_tile_worker.hpp index 520973c3c..c56da82d0 100644 --- a/src/mbgl/tile/raster_tile_worker.hpp +++ b/src/mbgl/tile/raster_tile_worker.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/actor/actor_ref.hpp> +#include <mbgl/util/blob.hpp> #include <memory> #include <string> @@ -13,7 +14,7 @@ class RasterTileWorker { public: RasterTileWorker(ActorRef<RasterTileWorker>, ActorRef<RasterTile>); - void parse(std::shared_ptr<const std::string> data, uint64_t correlationID); + void parse(Blob data, uint64_t correlationID); private: ActorRef<RasterTile> parent; diff --git a/src/mbgl/tile/tile_loader_impl.hpp b/src/mbgl/tile/tile_loader_impl.hpp index 1b2963826..48b3b7c87 100644 --- a/src/mbgl/tile/tile_loader_impl.hpp +++ b/src/mbgl/tile/tile_loader_impl.hpp @@ -106,7 +106,7 @@ void TileLoader<T>::loadedData(const Response& res) { resource.priorExpires = res.expires; resource.priorEtag = res.etag; tile.setMetadata(res.modified, res.expires); - tile.setData(res.noContent ? nullptr : res.data); + tile.setData(res.noContent ? Blob{} : res.data); } } diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp index 0756d3e52..a815f31e4 100644 --- a/src/mbgl/tile/vector_tile.cpp +++ b/src/mbgl/tile/vector_tile.cpp @@ -21,8 +21,8 @@ void VectorTile::setMetadata(optional<Timestamp> modified_, optional<Timestamp> expires = expires_; } -void VectorTile::setData(std::shared_ptr<const std::string> data_) { - GeometryTile::setData(data_ ? std::make_unique<VectorTileData>(data_) : nullptr); +void VectorTile::setData(Blob data_) { + GeometryTile::setData(data_ ? std::make_unique<VectorTileData>(std::move(data_)) : nullptr); } } // namespace mbgl diff --git a/src/mbgl/tile/vector_tile.hpp b/src/mbgl/tile/vector_tile.hpp index 7dae414fe..87abf2f59 100644 --- a/src/mbgl/tile/vector_tile.hpp +++ b/src/mbgl/tile/vector_tile.hpp @@ -17,7 +17,7 @@ public: void setNecessity(TileNecessity) final; void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires); - void setData(std::shared_ptr<const std::string> data); + void setData(Blob data); private: TileLoader<VectorTile> loader; diff --git a/src/mbgl/tile/vector_tile_data.cpp b/src/mbgl/tile/vector_tile_data.cpp index 2d4a01bda..eaa91dc9c 100644 --- a/src/mbgl/tile/vector_tile_data.cpp +++ b/src/mbgl/tile/vector_tile_data.cpp @@ -60,19 +60,24 @@ std::string VectorTileLayer::getName() const { return layer.getName(); } -VectorTileData::VectorTileData(std::shared_ptr<const std::string> data_) : data(std::move(data_)) { +VectorTileData::VectorTileData(Blob blob_) : blob(std::move(blob_)) { } std::unique_ptr<GeometryTileData> VectorTileData::clone() const { - return std::make_unique<VectorTileData>(data); + // Always pass on data that is uncompressed, if we have it. + return std::make_unique<VectorTileData>(data ? Blob{ data, false } : blob); } std::unique_ptr<GeometryTileLayer> VectorTileData::getLayer(const std::string& name) const { - if (!parsed) { + if (!blob) { + return nullptr; + } + + if (!data) { // We're parsing this lazily so that we can construct VectorTileData objects on the main - // thread without incurring the overhead of parsing immediately. + // thread without incurring the overhead of parsing and decompressing immediately. + data = blob.uncompressedData(); layers = mapbox::vector_tile::buffer(*data).getLayers(); - parsed = true; } auto it = layers.find(name); diff --git a/src/mbgl/tile/vector_tile_data.hpp b/src/mbgl/tile/vector_tile_data.hpp index 48beaf9d0..ce120e97c 100644 --- a/src/mbgl/tile/vector_tile_data.hpp +++ b/src/mbgl/tile/vector_tile_data.hpp @@ -1,5 +1,7 @@ #include <mbgl/tile/geometry_tile_data.hpp> +#include <mbgl/util/blob.hpp> + #include <mapbox/vector_tile.hpp> #include <protozero/pbf_reader.hpp> @@ -38,7 +40,8 @@ private: class VectorTileData : public GeometryTileData { public: - VectorTileData(std::shared_ptr<const std::string> data); + VectorTileData(const VectorTileData&); + VectorTileData(Blob blob); std::unique_ptr<GeometryTileData> clone() const override; std::unique_ptr<GeometryTileLayer> getLayer(const std::string& name) const override; @@ -46,8 +49,8 @@ public: std::vector<std::string> layerNames() const; private: - std::shared_ptr<const std::string> data; - mutable bool parsed = false; + Blob blob; + mutable std::shared_ptr<const std::string> data; mutable std::map<std::string, const protozero::data_view> layers; }; diff --git a/src/mbgl/util/blob.cpp b/src/mbgl/util/blob.cpp new file mode 100644 index 000000000..53d764400 --- /dev/null +++ b/src/mbgl/util/blob.cpp @@ -0,0 +1,46 @@ +#include <mbgl/util/blob.hpp> +#include <mbgl/util/compression.hpp> + +namespace mbgl { + +Blob::Blob() = default; + +Blob::Blob(std::shared_ptr<const std::string> bytes_, bool compressed_) + : bytes(std::move(bytes_)), compressed(compressed_) { +} + +Blob::Blob(std::string&& bytes_, bool compressed_) + : bytes(std::make_shared<const std::string>(std::move(bytes_))), compressed(compressed_) { +} + +std::shared_ptr<const std::string> Blob::uncompressedData() const { + if (!bytes) { + throw std::runtime_error("invalid blob"); + } + if (compressed) { + return std::make_shared<const std::string>(util::decompress(*bytes)); + } else { + return bytes; + } +} + +std::shared_ptr<const std::string> Blob::compressedData() const { + if (!bytes) { + throw std::runtime_error("invalid blob"); + } + if (compressed) { + return bytes; + } else { + return std::make_shared<const std::string>(util::compress(*bytes)); + } +} + +void Blob::uncompress() { + if (compressed) { + bytes = uncompressedData(); + compressed = false; + } +} + + +} // namespace mbgl diff --git a/src/mbgl/util/compression.cpp b/src/mbgl/util/compression.cpp index 30e813cbb..4d4e094f0 100644 --- a/src/mbgl/util/compression.cpp +++ b/src/mbgl/util/compression.cpp @@ -71,7 +71,8 @@ std::string decompress(const std::string &raw) { memset(&inflate_stream, 0, sizeof(inflate_stream)); // TODO: reuse z_streams - if (inflateInit(&inflate_stream) != Z_OK) { + // MAX_WBITS + allows decoding gzip in addition to zlib + if (inflateInit2(&inflate_stream, MAX_WBITS + 32) != Z_OK) { throw std::runtime_error("failed to initialize inflate"); } @@ -100,5 +101,39 @@ std::string decompress(const std::string &raw) { return result; } + +bool isCompressible(const std::string& raw) { + // WebP + if (raw.size() >= 12 && static_cast<uint8_t>(raw[0]) == 'R' && + static_cast<uint8_t>(raw[1]) == 'I' && static_cast<uint8_t>(raw[2]) == 'F' && + static_cast<uint8_t>(raw[3]) == 'F' && static_cast<uint8_t>(raw[8]) == 'W' && + static_cast<uint8_t>(raw[9]) == 'E' && static_cast<uint8_t>(raw[10]) == 'B' && + static_cast<uint8_t>(raw[11]) == 'P') { + // Note: the WebP container format allows uncompressed data as well, but we just assume that + // all WebP files are already compressed. + return false; + } + + // PNG + if (raw.size() >= 8 && static_cast<uint8_t>(raw[0]) == 0x89 && + static_cast<uint8_t>(raw[1]) == 'P' && static_cast<uint8_t>(raw[2]) == 'N' && + static_cast<uint8_t>(raw[3]) == 'G' && static_cast<uint8_t>(raw[4]) == '\r' && + static_cast<uint8_t>(raw[5]) == '\n' && static_cast<uint8_t>(raw[6]) == 0x1a && + static_cast<uint8_t>(raw[7]) == '\n') { + // Note: this assumes the PNG file itself is compressed. However, it is possible to create + // PNG files with uncompressed data in it (zlib compression 0), but they are exceedingly + // rare, so we don't care about them. + return false; + } + + // JPEG + if (raw.size() >= 3 && static_cast<uint8_t>(raw[0]) == 0xff && + static_cast<uint8_t>(raw[1]) == 0xd8 && static_cast<uint8_t>(raw[2]) == 0xff) { + return false; + } + + return true; +} + } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/io.cpp b/src/mbgl/util/io.cpp index 6a6ed7b25..2dc6cabf0 100644 --- a/src/mbgl/util/io.cpp +++ b/src/mbgl/util/io.cpp @@ -1,43 +1,29 @@ #include <mbgl/util/io.hpp> -#include <cstdio> #include <cerrno> #include <iostream> -#include <sstream> #include <fstream> namespace mbgl { namespace util { -void write_file(const std::string &filename, const std::string &data) { - FILE *fd = fopen(filename.c_str(), "wb"); - if (fd) { - fwrite(data.data(), sizeof(std::string::value_type), data.size(), fd); - fclose(fd); - } else { - throw std::runtime_error(std::string("Failed to open file ") + filename); - } -} - -std::string read_file(const std::string &filename) { - std::ifstream file(filename); +void writeFile(const std::string &filename, Blob blob) { + std::ofstream file(filename, std::ios::binary); if (file.good()) { - std::stringstream data; - data << file.rdbuf(); - return data.str(); + file << *blob.uncompressedData(); } else { - throw std::runtime_error(std::string("Cannot read file ") + filename); + throw IOException(errno, "failed to write file"); } } -optional<std::string> readFile(const std::string &filename) { - std::ifstream file(filename); +Blob readFile(const std::string &filename) { + std::ifstream file(filename, std::ios::binary); if (file.good()) { - std::stringstream data; - data << file.rdbuf(); - return data.str(); + return { { std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() }, + false }; + } else { + return {}; } - return {}; } void deleteFile(const std::string& filename) { diff --git a/src/mbgl/util/io.hpp b/src/mbgl/util/io.hpp index 847271acf..b74f89485 100644 --- a/src/mbgl/util/io.hpp +++ b/src/mbgl/util/io.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/util/optional.hpp> +#include <mbgl/util/blob.hpp> #include <string> #include <stdexcept> @@ -14,10 +15,8 @@ struct IOException : std::runtime_error { const int code = 0; }; -void write_file(const std::string &filename, const std::string &data); -std::string read_file(const std::string &filename); - -optional<std::string> readFile(const std::string &filename); +void writeFile(const std::string &filename, Blob blob); +Blob readFile(const std::string &filename); void deleteFile(const std::string& filename); } // namespace util diff --git a/test/api/annotations.test.cpp b/test/api/annotations.test.cpp index 07257851a..8b25fa482 100644 --- a/test/api/annotations.test.cpp +++ b/test/api/annotations.test.cpp @@ -17,7 +17,7 @@ using namespace mbgl; namespace { PremultipliedImage namedImage(const std::string& name) { - return decodeImage(util::read_file("test/fixtures/sprites/" + name + ".png")); + return decodeImage(util::readFile("test/fixtures/sprites/" + name + ".png")); } std::unique_ptr<style::Image> namedMarker(const std::string& name) { @@ -45,7 +45,7 @@ public: TEST(Annotations, SymbolAnnotation) { AnnotationTest test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotationImage(namedMarker("default_marker")); test.map.addAnnotation(SymbolAnnotation { Point<double>(0, 0), "default_marker" }); test.checkRendering("point_annotation"); @@ -67,7 +67,7 @@ TEST(Annotations, LineAnnotation) { annotation.color = Color::red(); annotation.width = { 5 }; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotation(annotation); test.checkRendering("line_annotation"); @@ -82,7 +82,7 @@ TEST(Annotations, FillAnnotation) { FillAnnotation annotation { polygon }; annotation.color = Color::red(); - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotation(annotation); test.checkRendering("fill_annotation"); @@ -95,7 +95,7 @@ TEST(Annotations, AntimeridianAnnotationSmall) { double antimeridian = 180; test.map.setLatLngZoom(mbgl::LatLng(0, antimeridian), 0); - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); LineString<double> line = {{ { antimeridian, 20 }, { antimeridian, -20 } }}; LineAnnotation lineAnnotation { line }; @@ -116,7 +116,7 @@ TEST(Annotations, AntimeridianAnnotationLarge) { double antimeridian = 180; test.map.setLatLngZoom(mbgl::LatLng(0, antimeridian), 0); - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); LineString<double> line = {{ { antimeridian, 20 }, { antimeridian, -20 } }}; LineAnnotation lineAnnotation { line }; @@ -141,7 +141,7 @@ TEST(Annotations, OverlappingFillAnnotation) { FillAnnotation overlaidAnnotation { polygon }; overlaidAnnotation.color = Color::red(); - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotation(underlaidAnnotation); test.map.addAnnotation(overlaidAnnotation); test.checkRendering("overlapping_fill_annotation"); @@ -150,7 +150,7 @@ TEST(Annotations, OverlappingFillAnnotation) { TEST(Annotations, AddMultiple) { AnnotationTest test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotationImage(namedMarker("default_marker")); test.map.addAnnotation(SymbolAnnotation { Point<double> { -10, 0 }, "default_marker" }); @@ -163,7 +163,7 @@ TEST(Annotations, AddMultiple) { TEST(Annotations, NonImmediateAdd) { AnnotationTest test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.frontend.render(test.map); Polygon<double> polygon = { {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} }; @@ -177,7 +177,7 @@ TEST(Annotations, NonImmediateAdd) { TEST(Annotations, UpdateSymbolAnnotationGeometry) { AnnotationTest test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotationImage(namedMarker("default_marker")); test.map.addAnnotationImage(namedMarker("flipped_marker")); AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); @@ -191,7 +191,7 @@ TEST(Annotations, UpdateSymbolAnnotationGeometry) { TEST(Annotations, UpdateSymbolAnnotationIcon) { AnnotationTest test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotationImage(namedMarker("default_marker")); test.map.addAnnotationImage(namedMarker("flipped_marker")); AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); @@ -209,7 +209,7 @@ TEST(Annotations, UpdateLineAnnotationGeometry) { annotation.color = Color::red(); annotation.width = { 5 }; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); AnnotationID line = test.map.addAnnotation(annotation); test.frontend.render(test.map); @@ -226,7 +226,7 @@ TEST(Annotations, UpdateLineAnnotationStyle) { annotation.color = Color::red(); annotation.width = { 5 }; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); AnnotationID line = test.map.addAnnotation(annotation); test.frontend.render(test.map); @@ -243,7 +243,7 @@ TEST(Annotations, UpdateFillAnnotationGeometry) { FillAnnotation annotation { Polygon<double> { {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} } }; annotation.color = Color::red(); - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); AnnotationID fill = test.map.addAnnotation(annotation); test.frontend.render(test.map); @@ -260,7 +260,7 @@ TEST(Annotations, UpdateFillAnnotationStyle) { FillAnnotation annotation { polygon }; annotation.color = Color::red(); - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); AnnotationID fill = test.map.addAnnotation(annotation); test.frontend.render(test.map); @@ -273,7 +273,7 @@ TEST(Annotations, UpdateFillAnnotationStyle) { TEST(Annotations, RemovePoint) { AnnotationTest test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotationImage(namedMarker("default_marker")); AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); @@ -291,7 +291,7 @@ TEST(Annotations, RemoveShape) { annotation.color = Color::red(); annotation.width = { 5 }; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); AnnotationID shape = test.map.addAnnotation(annotation); test.frontend.render(test.map); @@ -304,7 +304,7 @@ TEST(Annotations, ImmediateRemoveShape) { AnnotationTest test; test.map.removeAnnotation(test.map.addAnnotation(LineAnnotation { LineString<double>() })); - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.frontend.render(test.map); } @@ -312,20 +312,20 @@ TEST(Annotations, ImmediateRemoveShape) { TEST(Annotations, SwitchStyle) { AnnotationTest test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotationImage(namedMarker("default_marker")); test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); test.frontend.render(test.map); - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.checkRendering("switch_style"); } TEST(Annotations, ReaddImage) { AnnotationTest test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotationImage(namedMarker("default_marker")); test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); @@ -338,7 +338,7 @@ TEST(Annotations, ReaddImage) { TEST(Annotations, QueryRenderedFeatures) { AnnotationTest test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotationImage(namedMarker("default_marker")); test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 50 }, "default_marker" }); @@ -362,7 +362,7 @@ TEST(Annotations, QueryFractionalZoomLevels) { auto viewSize = test.frontend.getSize(); auto box = ScreenBox { {}, { double(viewSize.width), double(viewSize.height) } }; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotationImage(namedMarker("default_marker")); std::vector<mbgl::AnnotationID> ids; @@ -394,7 +394,7 @@ TEST(Annotations, VisibleFeatures) { auto viewSize = test.frontend.getSize(); auto box = ScreenBox { {}, { double(viewSize.width), double(viewSize.height) } }; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotationImage(namedMarker("default_marker")); test.map.setLatLngZoom({ 5, 5 }, 3); @@ -438,7 +438,7 @@ TEST(Annotations, DebugEmpty) { // should not render them. AnnotationTest test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.setDebug(MapDebugOptions::TileBorders); test.map.setZoom(1); @@ -451,7 +451,7 @@ TEST(Annotations, DebugSparse) { // tiles because they're all empty. AnnotationTest test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.setDebug(MapDebugOptions::TileBorders); test.map.setZoom(1); test.map.addAnnotationImage(namedMarker("default_marker")); @@ -469,7 +469,7 @@ TEST(Annotations, ChangeMaxZoom) { annotation.width = { 5 }; test.map.setMaxZoom(6); - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.map.addAnnotation(annotation); test.map.setMaxZoom(14); test.map.setZoom(test.map.getMaxZoom()); diff --git a/test/api/custom_geometry_source.test.cpp b/test/api/custom_geometry_source.test.cpp index 83d1543a0..3397bef9e 100644 --- a/test/api/custom_geometry_source.test.cpp +++ b/test/api/custom_geometry_source.test.cpp @@ -26,7 +26,7 @@ TEST(CustomGeometrySource, Grid) { HeadlessFrontend frontend { pixelRatio, fileSource, *threadPool }; Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource, *threadPool, MapMode::Static); - map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); + map.getStyle().loadJSON(util::readFile("test/fixtures/api/water.json")); map.setLatLngZoom({ 37.8, -122.5 }, 10); CustomGeometrySource::Options options; diff --git a/test/api/custom_layer.test.cpp b/test/api/custom_layer.test.cpp index eb1d7e0d3..5eaa73a37 100644 --- a/test/api/custom_layer.test.cpp +++ b/test/api/custom_layer.test.cpp @@ -91,7 +91,7 @@ TEST(CustomLayer, Basic) { HeadlessFrontend frontend { pixelRatio, fileSource, threadPool }; Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource, threadPool, MapMode::Static); - map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); + map.getStyle().loadJSON(util::readFile("test/fixtures/api/water.json")); map.setLatLngZoom({ 37.8, -122.5 }, 10); map.getStyle().addLayer(std::make_unique<CustomLayer>( "custom", diff --git a/test/api/query.test.cpp b/test/api/query.test.cpp index c67ff9064..43067dd57 100644 --- a/test/api/query.test.cpp +++ b/test/api/query.test.cpp @@ -19,9 +19,9 @@ namespace { class QueryTest { public: QueryTest() { - map.getStyle().loadJSON(util::read_file("test/fixtures/api/query_style.json")); + map.getStyle().loadJSON(util::readFile("test/fixtures/api/query_style.json")); map.getStyle().addImage(std::make_unique<style::Image>("test-icon", - decodeImage(util::read_file("test/fixtures/sprites/default_marker.png")), 1.0)); + decodeImage(util::readFile("test/fixtures/sprites/default_marker.png")), 1.0)); frontend.render(map); } diff --git a/test/api/recycle_map.cpp b/test/api/recycle_map.cpp index ca6abac8c..1055eda5a 100644 --- a/test/api/recycle_map.cpp +++ b/test/api/recycle_map.cpp @@ -40,10 +40,10 @@ TEST(API, RecycleMapUpdateImages) { auto layer = std::make_unique<SymbolLayer>("geometry", "geometry"); layer->setIconImage({ markerName }); - map->getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + map->getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); map->getStyle().addSource(std::move(source)); map->getStyle().addLayer(std::move(layer)); - map->getStyle().addImage(std::make_unique<style::Image>(markerName, decodeImage(util::read_file(markerPath)), 1.0)); + map->getStyle().addImage(std::make_unique<style::Image>(markerName, decodeImage(util::readFile(markerPath)), 1.0)); }; // default marker diff --git a/test/api/zoom_history.cpp b/test/api/zoom_history.cpp index df9b6ff2a..a16d5322d 100644 --- a/test/api/zoom_history.cpp +++ b/test/api/zoom_history.cpp @@ -34,7 +34,7 @@ TEST(API, ZoomHistory) { EXPECT_TRUE(map); - map->getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + map->getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); auto source = std::make_unique<GeoJSONSource>("source"); source->setGeoJSON({ LineString<double> { { 45, -45 }, { -45, 45 } } }); diff --git a/test/gl/context.test.cpp b/test/gl/context.test.cpp index 179ce5de5..157732d72 100644 --- a/test/gl/context.test.cpp +++ b/test/gl/context.test.cpp @@ -90,7 +90,7 @@ TEST(GLContextMode, Shared) { HeadlessFrontend frontend { pixelRatio, fileSource, threadPool, {}, GLContextMode::Shared }; Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource, threadPool, MapMode::Static); - map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); + map.getStyle().loadJSON(util::readFile("test/fixtures/api/water.json")); map.setLatLngZoom({ 37.8, -122.5 }, 10); // Set transparent background layer. diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index 9b34ea89b..bb0ff2a20 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -21,7 +21,6 @@ using namespace mbgl; using namespace mbgl::style; -using namespace std::literals::string_literals; class StubMapObserver : public MapObserver { public: @@ -162,7 +161,7 @@ TEST(Map, Offline) { auto expiredItem = [] (const std::string& path) { Response response; - response.data = std::make_shared<std::string>(util::read_file("test/fixtures/map/offline/"s + path)); + response.data = util::readFile("test/fixtures/map/offline/" + path); response.expires = Timestamp{ Seconds(0) }; return response; }; @@ -188,13 +187,13 @@ TEST(Map, Offline) { TEST(Map, SetStyleDefaultCamera) { MapTest<> test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); EXPECT_DOUBLE_EQ(test.map.getZoom(), 0.0); EXPECT_DOUBLE_EQ(test.map.getPitch(), 0.0); EXPECT_DOUBLE_EQ(test.map.getBearing(), 0.0); EXPECT_EQ(test.map.getLatLng(), LatLng {}); - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty-zoomed.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty-zoomed.json")); EXPECT_DOUBLE_EQ(test.map.getZoom(), 0.0); test.map.jumpTo(test.map.getStyle().getDefaultCamera()); @@ -211,7 +210,7 @@ TEST(Map, SetStyleInvalidJSON) { test.observer.didFailLoadingMapCallback = [&]() { fail = true; }; - test.map.getStyle().loadJSON("invalid"); + test.map.getStyle().loadJSON(Blob{ "invalid", false }); } EXPECT_TRUE(fail); @@ -247,8 +246,8 @@ TEST(Map, SetStyleInvalidURL) { TEST(Map, DoubleStyleLoad) { MapTest<> test; - test.map.getStyle().loadJSON(""); - test.map.getStyle().loadJSON(""); + test.map.getStyle().loadJSON(Blob{ "", false }); + test.map.getStyle().loadJSON(Blob{ "", false }); } TEST(Map, StyleFresh) { @@ -260,7 +259,7 @@ TEST(Map, StyleFresh) { EXPECT_EQ(1u, test.fileSource.requests.size()); Response response; - response.data = std::make_shared<std::string>(util::read_file("test/fixtures/api/empty.json")); + response.data = util::readFile("test/fixtures/api/empty.json"); response.expires = Timestamp::max(); test.fileSource.respond(Resource::Style, response); @@ -278,7 +277,7 @@ TEST(Map, StyleExpired) { EXPECT_EQ(1u, test.fileSource.requests.size()); Response response; - response.data = std::make_shared<std::string>(util::read_file("test/fixtures/api/empty.json")); + response.data = util::readFile("test/fixtures/api/empty.json"); response.expires = util::now() - 1h; test.fileSource.respond(Resource::Style, response); @@ -303,7 +302,7 @@ TEST(Map, StyleExpiredWithAnnotations) { EXPECT_EQ(1u, test.fileSource.requests.size()); Response response; - response.data = std::make_shared<std::string>(util::read_file("test/fixtures/api/empty.json")); + response.data = util::readFile("test/fixtures/api/empty.json"); response.expires = util::now() - 1h; test.fileSource.respond(Resource::Style, response); @@ -327,7 +326,7 @@ TEST(Map, StyleExpiredWithRender) { EXPECT_EQ(1u, test.fileSource.requests.size()); Response response; - response.data = std::make_shared<std::string>(util::read_file("test/fixtures/api/empty.json")); + response.data = util::readFile("test/fixtures/api/empty.json"); response.expires = util::now() - 1h; test.fileSource.respond(Resource::Style, response); @@ -349,7 +348,7 @@ TEST(Map, StyleEarlyMutation) { test.map.getStyle().addLayer(std::make_unique<style::BackgroundLayer>("bg")); Response response; - response.data = std::make_shared<std::string>(util::read_file("test/fixtures/api/water.json")); + response.data = util::readFile("test/fixtures/api/water.json"); test.fileSource.respond(Resource::Style, response); EXPECT_EQ(0u, test.fileSource.requests.size()); @@ -363,7 +362,7 @@ TEST(Map, MapLoadingSignal) { test.observer.onWillStartLoadingMapCallback = [&]() { emitted = true; }; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); EXPECT_TRUE(emitted); } @@ -374,7 +373,7 @@ TEST(Map, MapLoadedSignal) { test.runLoop.stop(); }; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); test.runLoop.run(); } @@ -386,12 +385,12 @@ TEST(Map, StyleLoadedSignal) { test.observer.didFinishLoadingStyleCallback = [&]() { emitted = true; }; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); EXPECT_TRUE(emitted); // But not when the style couldn't be parsed emitted = false; - test.map.getStyle().loadJSON("invalid"); + test.map.getStyle().loadJSON(Blob{ "invalid", false }); EXPECT_FALSE(emitted); } @@ -437,7 +436,7 @@ TEST(Map, TEST_REQUIRES_SERVER(StyleNotFound)) { TEST(Map, AddLayer) { MapTest<> test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); auto layer = std::make_unique<BackgroundLayer>("background"); layer->setBackgroundColor({ { 1, 0, 0, 1 } }); @@ -452,7 +451,7 @@ TEST(Map, WithoutVAOExtension) { BackendScope scope { *test.frontend.getBackend() }; test.frontend.getBackend()->getContext().disableVAOExtension = true; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/water.json")); test::checkImage("test/fixtures/map/no_vao", test.frontend.render(test.map), 0.002); } @@ -460,7 +459,7 @@ TEST(Map, WithoutVAOExtension) { TEST(Map, RemoveLayer) { MapTest<> test; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/api/empty.json")); auto layer = std::make_unique<BackgroundLayer>("background"); layer->setBackgroundColor({{ 1, 0, 0, 1 }}); @@ -477,8 +476,7 @@ TEST(Map, DisabledSources) { test.fileSource.response = [] (const Resource& res) -> optional<Response> { if (res.url == "asset://tile.png") { Response response; - response.data = std::make_shared<std::string>( - util::read_file("test/fixtures/map/disabled_layers/tile.png")); + response.data = util::readFile("test/fixtures/map/disabled_layers/tile.png"); return {std::move(response)}; } return {}; @@ -491,7 +489,7 @@ TEST(Map, DisabledSources) { // to an opacity of 0.5). Then, we are zooming back out to a zoom level of 0.5 and rerender. // The "raster1" layer should not be visible anymore since it has minzoom 1, while "raster2" // should still be there. Both layers have a distinct color through "raster-hue-rotate". - test.map.getStyle().loadJSON(R"STYLE( + test.map.getStyle().loadJSON(Blob{ R"STYLE( { "version": 8, "name": "Test", @@ -523,7 +521,7 @@ TEST(Map, DisabledSources) { } }] } -)STYLE"); +)STYLE", false }); test::checkImage("test/fixtures/map/disabled_layers/first", test.frontend.render(test.map)); test.map.setZoom(0.5); @@ -533,7 +531,7 @@ TEST(Map, DisabledSources) { TEST(Map, DontLoadUnneededTiles) { MapTest<> test; - test.map.getStyle().loadJSON(R"STYLE({ + test.map.getStyle().loadJSON(Blob{ R"STYLE({ "sources": { "a": { "type": "vector", "tiles": [ "a/{z}/{x}/{y}" ] } }, @@ -545,7 +543,7 @@ TEST(Map, DontLoadUnneededTiles) { "minzoom": 0.3, "maxzoom": 1.6 }] -})STYLE"); +})STYLE", false }); using Tiles = std::unordered_set<std::string>; Tiles tiles; @@ -606,7 +604,7 @@ TEST(Map, TEST_DISABLED_ON_CI(ContinuousRendering)) { }; Map map(frontend, observer, frontend.getSize(), pixelRatio, fileSource, threadPool, MapMode::Continuous); - map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); + map.getStyle().loadJSON(util::readFile("test/fixtures/api/water.json")); runLoop.run(); } @@ -624,7 +622,7 @@ TEST(Map, NoContentTiles) { Tileset::Scheme::XYZ), response); - test.map.getStyle().loadJSON(R"STYLE({ + test.map.getStyle().loadJSON(Blob{ R"STYLE({ "version": 8, "name": "Water", "sources": { @@ -645,7 +643,7 @@ TEST(Map, NoContentTiles) { "source": "mapbox", "source-layer": "water" }] - })STYLE"); + })STYLE", false }); test::checkImage("test/fixtures/map/nocontent", test.frontend.render(test.map), diff --git a/test/map/prefetch.test.cpp b/test/map/prefetch.test.cpp index 4c82b2c96..7cb8ea154 100644 --- a/test/map/prefetch.test.cpp +++ b/test/map/prefetch.test.cpp @@ -37,11 +37,9 @@ TEST(Map, PrefetchTiles) { // The end rendering result should be all green because the map is only // considered fully rendered when only ideal tiles are shown. if (zoom == int(map.getZoom()) + 1) { - response.data = std::make_shared<std::string>( - util::read_file("test/fixtures/map/prefetch/tile_green.png")); + response.data = util::readFile("test/fixtures/map/prefetch/tile_green.png"); } else { - response.data = std::make_shared<std::string>( - util::read_file("test/fixtures/map/prefetch/tile_red.png")); + response.data = util::readFile("test/fixtures/map/prefetch/tile_red.png"); } return { std::move(response) }; @@ -51,8 +49,8 @@ TEST(Map, PrefetchTiles) { tiles.clear(); // Force tile reloading. - map.getStyle().loadJSON(util::read_file("test/fixtures/map/prefetch/empty.json")); - map.getStyle().loadJSON(util::read_file("test/fixtures/map/prefetch/style.json")); + map.getStyle().loadJSON(util::readFile("test/fixtures/map/prefetch/empty.json")); + map.getStyle().loadJSON(util::readFile("test/fixtures/map/prefetch/style.json")); map.setLatLngZoom({ 40.726989, -73.992857 }, zoom); // Manhattan diff --git a/test/programs/binary_program.test.cpp b/test/programs/binary_program.test.cpp index a5cf7b6e3..7fc2acbf7 100644 --- a/test/programs/binary_program.test.cpp +++ b/test/programs/binary_program.test.cpp @@ -35,5 +35,5 @@ TEST(BinaryProgram, ObtainValues) { EXPECT_EQ(3, binaryProgram2.uniformLocation("u_ratio")); EXPECT_EQ(-1, binaryProgram2.uniformLocation("a_data")); - EXPECT_THROW(BinaryProgram(""), std::runtime_error); + EXPECT_THROW(BinaryProgram(Blob{ "", false }), std::runtime_error); } diff --git a/test/renderer/image_manager.test.cpp b/test/renderer/image_manager.test.cpp index ebe1bcd72..e1d9030d4 100644 --- a/test/renderer/image_manager.test.cpp +++ b/test/renderer/image_manager.test.cpp @@ -25,8 +25,8 @@ TEST(ImageManager, Basic) { FixtureLog log; ImageManager imageManager; - auto images = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"), - util::read_file("test/fixtures/annotations/emerald.json")); + auto images = parseSprite(util::readFile("test/fixtures/annotations/emerald.png"), + util::readFile("test/fixtures/annotations/emerald.json")); for (auto& image : images) { imageManager.addImage(image->baseImpl); } diff --git a/test/sprite/sprite_loader.test.cpp b/test/sprite/sprite_loader.test.cpp index 369157226..d773afd53 100644 --- a/test/sprite/sprite_loader.test.cpp +++ b/test/sprite/sprite_loader.test.cpp @@ -58,14 +58,14 @@ public: Response successfulSpriteImageResponse(const Resource& resource) { EXPECT_EQ("test/fixtures/resources/sprite.png", resource.url); Response response; - response.data = std::make_unique<std::string>(util::read_file(resource.url)); + response.data = util::readFile(resource.url); return response; } Response successfulSpriteJSONResponse(const Resource& resource) { EXPECT_EQ("test/fixtures/resources/sprite.json", resource.url); Response response; - response.data = std::make_unique<std::string>(util::read_file(resource.url)); + response.data = util::readFile(resource.url); return response; } @@ -79,7 +79,7 @@ Response failedSpriteResponse(const Resource&) { Response corruptSpriteResponse(const Resource&) { Response response; - response.data = std::make_unique<std::string>("CORRUPT"); + response.data = Blob{ "CORRUPT", false }; return response; } diff --git a/test/sprite/sprite_parser.test.cpp b/test/sprite/sprite_parser.test.cpp index 529e4c75e..14d5f6586 100644 --- a/test/sprite/sprite_parser.test.cpp +++ b/test/sprite/sprite_parser.test.cpp @@ -14,7 +14,7 @@ using namespace mbgl; namespace { auto readImage(const std::string& name) { - return decodeImage(util::read_file(name)); + return decodeImage(util::readFile(name)); } } // namespace @@ -22,7 +22,7 @@ auto readImage(const std::string& name) { TEST(Sprite, SpriteImageCreationInvalid) { FixtureLog log; - const PremultipliedImage image_1x = decodeImage(util::read_file("test/fixtures/annotations/emerald.png")); + const PremultipliedImage image_1x = decodeImage(util::readFile("test/fixtures/annotations/emerald.png")); ASSERT_EQ(200u, image_1x.size.width); ASSERT_EQ(299u, image_1x.size.height); @@ -135,7 +135,7 @@ TEST(Sprite, SpriteImageCreationInvalid) { } TEST(Sprite, SpriteImageCreation1x) { - const PremultipliedImage image_1x = decodeImage(util::read_file("test/fixtures/annotations/emerald.png")); + const PremultipliedImage image_1x = decodeImage(util::readFile("test/fixtures/annotations/emerald.png")); ASSERT_EQ(200u, image_1x.size.width); ASSERT_EQ(299u, image_1x.size.height); @@ -152,7 +152,7 @@ TEST(Sprite, SpriteImageCreation1x) { } TEST(Sprite, SpriteImageCreation2x) { - const PremultipliedImage image_2x = decodeImage(util::read_file("test/fixtures/annotations/emerald@2x.png")); + const PremultipliedImage image_2x = decodeImage(util::readFile("test/fixtures/annotations/emerald@2x.png")); // "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false} const auto sprite = createStyleImage("test", image_2x, 354, 374, 36, 36, 2, false); @@ -165,7 +165,7 @@ TEST(Sprite, SpriteImageCreation2x) { } TEST(Sprite, SpriteImageCreation1_5x) { - const PremultipliedImage image_2x = decodeImage(util::read_file("test/fixtures/annotations/emerald@2x.png")); + const PremultipliedImage image_2x = decodeImage(util::readFile("test/fixtures/annotations/emerald@2x.png")); // "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false} const auto sprite = createStyleImage("test", image_2x, 354, 374, 36, 36, 1.5, false); @@ -187,8 +187,8 @@ TEST(Sprite, SpriteImageCreation1_5x) { } TEST(Sprite, SpriteParsing) { - const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); - const auto json_1x = util::read_file("test/fixtures/annotations/emerald.json"); + const auto image_1x = util::readFile("test/fixtures/annotations/emerald.png"); + const auto json_1x = util::readFile("test/fixtures/annotations/emerald.json"); const auto images = parseSprite(image_1x, json_1x); @@ -281,8 +281,8 @@ TEST(Sprite, SpriteParsing) { } TEST(Sprite, SpriteParsingInvalidJSON) { - const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); - const auto json_1x = R"JSON({ "image": " })JSON"; + const auto image_1x = util::readFile("test/fixtures/annotations/emerald.png"); + const auto json_1x = Blob{ R"JSON({ "image": " })JSON", false }; try { parseSprite(image_1x, json_1x); @@ -297,8 +297,8 @@ TEST(Sprite, SpriteParsingInvalidJSON) { TEST(Sprite, SpriteParsingEmptyImage) { FixtureLog log; - const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); - const auto json_1x = R"JSON({ "image": {} })JSON"; + const auto image_1x = util::readFile("test/fixtures/annotations/emerald.png"); + const auto json_1x = Blob{ R"JSON({ "image": {} })JSON", false }; const auto images = parseSprite(image_1x, json_1x); EXPECT_EQ(0u, images.size()); @@ -314,8 +314,8 @@ TEST(Sprite, SpriteParsingEmptyImage) { TEST(Sprite, SpriteParsingSimpleWidthHeight) { FixtureLog log; - const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); - const auto json_1x = R"JSON({ "image": { "width": 32, "height": 32 } })JSON"; + const auto image_1x = util::readFile("test/fixtures/annotations/emerald.png"); + const auto json_1x = Blob{ R"JSON({ "image": { "width": 32, "height": 32 } })JSON", false }; const auto images = parseSprite(image_1x, json_1x); EXPECT_EQ(1u, images.size()); @@ -324,8 +324,8 @@ TEST(Sprite, SpriteParsingSimpleWidthHeight) { TEST(Sprite, SpriteParsingWidthTooBig) { FixtureLog log; - const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); - const auto json_1x = R"JSON({ "image": { "width": 65536, "height": 32 } })JSON"; + const auto image_1x = util::readFile("test/fixtures/annotations/emerald.png"); + const auto json_1x = Blob{ R"JSON({ "image": { "width": 65536, "height": 32 } })JSON", false }; const auto images = parseSprite(image_1x, json_1x); EXPECT_EQ(0u, images.size()); @@ -347,8 +347,8 @@ TEST(Sprite, SpriteParsingWidthTooBig) { TEST(Sprite, SpriteParsingNegativeWidth) { FixtureLog log; - const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); - const auto json_1x = R"JSON({ "image": { "width": -1, "height": 32 } })JSON"; + const auto image_1x = util::readFile("test/fixtures/annotations/emerald.png"); + const auto json_1x = Blob{ R"JSON({ "image": { "width": -1, "height": 32 } })JSON", false }; const auto images = parseSprite(image_1x, json_1x); EXPECT_EQ(0u, images.size()); @@ -370,8 +370,8 @@ TEST(Sprite, SpriteParsingNegativeWidth) { TEST(Sprite, SpriteParsingNullRatio) { FixtureLog log; - const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); - const auto json_1x = R"JSON({ "image": { "width": 32, "height": 32, "pixelRatio": 0 } })JSON"; + const auto image_1x = util::readFile("test/fixtures/annotations/emerald.png"); + const auto json_1x = Blob{ R"JSON({ "image": { "width": 32, "height": 32, "pixelRatio": 0 } })JSON", false }; const auto images = parseSprite(image_1x, json_1x); EXPECT_EQ(0u, images.size()); diff --git a/test/src/mbgl/test/util.cpp b/test/src/mbgl/test/util.cpp index 028a0a9d5..82d2af6cd 100644 --- a/test/src/mbgl/test/util.cpp +++ b/test/src/mbgl/test/util.cpp @@ -100,26 +100,26 @@ void checkImage(const std::string& base, double pixelThreshold) { #if !TEST_READ_ONLY if (getenv("UPDATE")) { - util::write_file(base + "/expected.png", encodePNG(actual)); + util::writeFile(base + "/expected.png", encodePNG(actual)); return; } #endif - std::string expected_image; + Blob expectedImage; try { - expected_image = util::read_file(base + "/expected.png"); + expectedImage = util::readFile(base + "/expected.png"); } catch (std::exception& ex) { Log::Error(Event::Setup, "Failed to load expected image %s: %s", (base + "/expected.png").c_str(), ex.what()); throw; } - PremultipliedImage expected = decodeImage(expected_image); + PremultipliedImage expected = decodeImage(expectedImage); PremultipliedImage diff { expected.size }; #if !TEST_READ_ONLY - util::write_file(base + "/actual.png", encodePNG(actual)); + util::writeFile(base + "/actual.png", encodePNG(actual)); #endif ASSERT_EQ(expected.size, actual.size); @@ -134,7 +134,7 @@ void checkImage(const std::string& base, EXPECT_LE(pixels / (expected.size.width * expected.size.height), imageThreshold); #if !TEST_READ_ONLY - util::write_file(base + "/diff.png", encodePNG(diff)); + util::writeFile(base + "/diff.png", encodePNG(diff)); #endif } diff --git a/test/storage/asset_file_source.test.cpp b/test/storage/asset_file_source.test.cpp index 978a41a30..694d56be6 100644 --- a/test/storage/asset_file_source.test.cpp +++ b/test/storage/asset_file_source.test.cpp @@ -33,8 +33,8 @@ TEST(AssetFileSource, Load) { requestCallback = [this, asset, endCallback](mbgl::Response res) { EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("content is here\n", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("content is here\n", *res.data.uncompressedData()); if (!--numRequests) { endCallback(); @@ -86,8 +86,8 @@ TEST(AssetFileSource, EmptyFile) { std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "asset://empty" }, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("", *res.data.uncompressedData()); loop.stop(); }); @@ -102,8 +102,8 @@ TEST(AssetFileSource, NonEmptyFile) { std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "asset://nonempty" }, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("content is here\n", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("content is here\n", *res.data.uncompressedData()); loop.stop(); }); @@ -119,7 +119,7 @@ TEST(AssetFileSource, NonExistentFile) { req.reset(); ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason); - ASSERT_FALSE(res.data.get()); + ASSERT_FALSE(res.data); // Do not assert on platform-specific error message. loop.stop(); }); @@ -137,7 +137,7 @@ TEST(AssetFileSource, InvalidURL) { ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::Other, res.error->reason); EXPECT_EQ("Invalid asset URL", res.error->message); - ASSERT_FALSE(res.data.get()); + ASSERT_FALSE(res.data); loop.stop(); }); @@ -153,7 +153,7 @@ TEST(AssetFileSource, ReadDirectory) { req.reset(); ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason); - ASSERT_FALSE(res.data.get()); + ASSERT_FALSE(res.data); // Do not assert on platform-specific error message. loop.stop(); }); @@ -169,8 +169,8 @@ TEST(AssetFileSource, URLEncoding) { std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "asset://%6eonempty" }, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("content is here\n", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("content is here\n", *res.data.uncompressedData()); loop.stop(); }); diff --git a/test/storage/default_file_source.test.cpp b/test/storage/default_file_source.test.cpp index c11d44227..f74a515d8 100644 --- a/test/storage/default_file_source.test.cpp +++ b/test/storage/default_file_source.test.cpp @@ -19,8 +19,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheResponse)) { req1 = fs.request(resource, [&](Response res) { req1.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Response 1", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Response 1", *res.data.uncompressedData()); EXPECT_TRUE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -32,8 +32,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheResponse)) { req2 = fs.request(resource, [&](Response res2) { req2.reset(); EXPECT_EQ(response.error, res2.error); - ASSERT_TRUE(res2.data.get()); - EXPECT_EQ(*response.data, *res2.data); + ASSERT_TRUE(res2.data); + EXPECT_EQ(*response.data.uncompressedData(), *res2.data.uncompressedData()); EXPECT_EQ(response.expires, res2.expires); EXPECT_EQ(response.mustRevalidate, res2.mustRevalidate); EXPECT_EQ(response.modified, res2.modified); @@ -61,8 +61,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateSame)) { EXPECT_EQ(nullptr, res.error); EXPECT_FALSE(res.notModified); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Response", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Response", *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_TRUE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -81,8 +81,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateSame)) { gotResponse = true; EXPECT_EQ(nullptr, res2.error); EXPECT_FALSE(res2.notModified); - ASSERT_TRUE(res2.data.get()); - EXPECT_EQ("Response", *res2.data); + ASSERT_TRUE(res2.data); + EXPECT_EQ("Response", *res2.data.uncompressedData()); EXPECT_TRUE(bool(res2.expires)); EXPECT_TRUE(res2.mustRevalidate); EXPECT_FALSE(bool(res2.modified)); @@ -95,7 +95,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateSame)) { req2.reset(); EXPECT_EQ(nullptr, res2.error); EXPECT_TRUE(res2.notModified); - EXPECT_FALSE(res2.data.get()); + EXPECT_FALSE(res2.data); EXPECT_TRUE(bool(res2.expires)); EXPECT_TRUE(res2.mustRevalidate); EXPECT_FALSE(bool(res2.modified)); @@ -124,8 +124,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateModified)) { EXPECT_EQ(nullptr, res.error); EXPECT_FALSE(res.notModified); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Response", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Response", *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_TRUE(res.mustRevalidate); EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res.modified); @@ -144,8 +144,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateModified)) { gotResponse = true; EXPECT_EQ(nullptr, res2.error); EXPECT_FALSE(res2.notModified); - ASSERT_TRUE(res2.data.get()); - EXPECT_EQ("Response", *res2.data); + ASSERT_TRUE(res2.data); + EXPECT_EQ("Response", *res2.data.uncompressedData()); EXPECT_TRUE(bool(res2.expires)); EXPECT_TRUE(res2.mustRevalidate); EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res2.modified); @@ -158,7 +158,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateModified)) { req2.reset(); EXPECT_EQ(nullptr, res2.error); EXPECT_TRUE(res2.notModified); - EXPECT_FALSE(res2.data.get()); + EXPECT_FALSE(res2.data); EXPECT_TRUE(bool(res2.expires)); EXPECT_TRUE(res2.mustRevalidate); EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res2.modified); @@ -184,8 +184,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateEtag)) { req1.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Response 1", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Response 1", *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_TRUE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -196,9 +196,9 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateEtag)) { req2.reset(); EXPECT_EQ(nullptr, res2.error); - ASSERT_TRUE(res2.data.get()); - EXPECT_NE(res.data, res2.data); - EXPECT_EQ("Response 2", *res2.data); + ASSERT_TRUE(res2.data); + EXPECT_FALSE(*res.data.uncompressedData() == *res2.data.uncompressedData()); + EXPECT_EQ("Response 2", *res2.data.uncompressedData()); EXPECT_FALSE(bool(res2.expires)); EXPECT_TRUE(res2.mustRevalidate); EXPECT_FALSE(bool(res2.modified)); @@ -235,8 +235,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(HTTPIssue1369)) { req = fs.request(resource, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Hello World!", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Hello World!", *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -256,7 +256,7 @@ TEST(DefaultFileSource, OptionalNonExpired) { using namespace std::chrono_literals; Response response; - response.data = std::make_shared<std::string>("Cached value"); + response.data = Blob{ "Cached value", false }; response.expires = util::now() + 1h; fs.put(optionalResource, response); @@ -264,8 +264,8 @@ TEST(DefaultFileSource, OptionalNonExpired) { req = fs.request(optionalResource, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Cached value", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Cached value", *res.data.uncompressedData()); ASSERT_TRUE(bool(res.expires)); EXPECT_EQ(*response.expires, *res.expires); EXPECT_FALSE(res.mustRevalidate); @@ -286,7 +286,7 @@ TEST(DefaultFileSource, OptionalExpired) { using namespace std::chrono_literals; Response response; - response.data = std::make_shared<std::string>("Cached value"); + response.data = Blob{ "Cached value", false }; response.expires = util::now() - 1h; fs.put(optionalResource, response); @@ -294,8 +294,8 @@ TEST(DefaultFileSource, OptionalExpired) { req = fs.request(optionalResource, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Cached value", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Cached value", *res.data.uncompressedData()); ASSERT_TRUE(bool(res.expires)); EXPECT_EQ(*response.expires, *res.expires); EXPECT_FALSE(res.mustRevalidate); @@ -361,7 +361,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshEtagNotModified)) { // Put a fake value into the cache to make sure we're not retrieving anything from the cache. Response response; - response.data = std::make_shared<std::string>("Cached value"); + response.data = Blob{ "Cached value", false }; response.expires = util::now() + 1h; fs.put(resource, response); @@ -370,7 +370,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshEtagNotModified)) { req.reset(); EXPECT_EQ(nullptr, res.error); EXPECT_TRUE(res.notModified); - EXPECT_FALSE(res.data.get()); + EXPECT_FALSE(res.data); ASSERT_TRUE(bool(res.expires)); EXPECT_LT(util::now(), *res.expires); EXPECT_TRUE(res.mustRevalidate); @@ -396,7 +396,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshEtagModified)) { // Put a fake value into the cache to make sure we're not retrieving anything from the cache. Response response; - response.data = std::make_shared<std::string>("Cached value"); + response.data = Blob{ "Cached value", false }; response.expires = util::now() + 1h; fs.put(resource, response); @@ -405,8 +405,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshEtagModified)) { req.reset(); EXPECT_EQ(nullptr, res.error); EXPECT_FALSE(res.notModified); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Response", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Response", *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_TRUE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -430,7 +430,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheFull)) { // Put a fake value into the cache to make sure we're not retrieving anything from the cache. Response response; - response.data = std::make_shared<std::string>("Cached value"); + response.data = Blob{ "Cached value", false }; response.expires = util::now() + 1h; fs.put(resource, response); @@ -439,8 +439,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheFull)) { req.reset(); EXPECT_EQ(nullptr, res.error); EXPECT_FALSE(res.notModified); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Response", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Response", *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_TRUE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -466,7 +466,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshModifiedNotModified)) // Put a fake value into the cache to make sure we're not retrieving anything from the cache. Response response; - response.data = std::make_shared<std::string>("Cached value"); + response.data = Blob{ "Cached value", false }; response.expires = util::now() + 1h; fs.put(resource, response); @@ -475,7 +475,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshModifiedNotModified)) req.reset(); EXPECT_EQ(nullptr, res.error); EXPECT_TRUE(res.notModified); - EXPECT_FALSE(res.data.get()); + EXPECT_FALSE(res.data); ASSERT_TRUE(bool(res.expires)); EXPECT_LT(util::now(), *res.expires); EXPECT_TRUE(res.mustRevalidate); @@ -502,7 +502,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshModifiedModified)) { // Put a fake value into the cache to make sure we're not retrieving anything from the cache. Response response; - response.data = std::make_shared<std::string>("Cached value"); + response.data = Blob{ "Cached value", false }; response.expires = util::now() + 1h; fs.put(resource, response); @@ -511,8 +511,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshModifiedModified)) { req.reset(); EXPECT_EQ(nullptr, res.error); EXPECT_FALSE(res.notModified); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Response", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Response", *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_TRUE(res.mustRevalidate); EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res.modified); @@ -543,8 +543,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(SetResourceTransform)) { req = fs.request(resource1, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Hello World!", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Hello World!", *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -560,8 +560,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(SetResourceTransform)) { req = fs.request(resource2, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Hello World!", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Hello World!", *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -584,7 +584,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(RespondToStaleMustRevalidate)) { // Put an existing value in the cache that has expired, and has must-revalidate set. Response response; - response.data = std::make_shared<std::string>("Cached value"); + response.data = Blob{ "Cached value", false }; response.modified = Timestamp(Seconds(1417392000)); // December 1, 2014 response.expires = Timestamp(Seconds(1417392000)); response.mustRevalidate = true; @@ -598,8 +598,8 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(RespondToStaleMustRevalidate)) { EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason); EXPECT_EQ("Cached resource is unusable", res.error->message); EXPECT_FALSE(res.notModified); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Cached value", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Cached value", *res.data.uncompressedData()); ASSERT_TRUE(res.expires); EXPECT_EQ(Timestamp{ Seconds(1417392000) }, *res.expires); EXPECT_TRUE(res.mustRevalidate); @@ -622,7 +622,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(RespondToStaleMustRevalidate)) { // request. We're replacing the data so that we can check that the DefaultFileSource doesn't // attempt another database access if we already have the value. resource.loadingMethod = Resource::LoadingMethod::NetworkOnly; - resource.priorData = std::make_shared<std::string>("Prior value"); + resource.priorData = Blob{ "Prior value", false }; req = fs.request(resource, [&](Response res) { req.reset(); @@ -632,11 +632,11 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(RespondToStaleMustRevalidate)) { // OnlineFileSource to ensure that requestors know that this is the first time they're // seeing this data. EXPECT_FALSE(res.notModified); - ASSERT_TRUE(res.data.get()); + ASSERT_TRUE(res.data); // Ensure that it's the value that we manually inserted into the cache rather than the value // the server returns, since we should be executing a revalidation request which doesn't // return new data, only a 304 Not Modified response. - EXPECT_EQ("Prior value", *res.data); + EXPECT_EQ("Prior value", *res.data.uncompressedData()); ASSERT_TRUE(res.expires); EXPECT_LE(util::now(), *res.expires); EXPECT_TRUE(res.mustRevalidate); diff --git a/test/storage/http_file_source.test.cpp b/test/storage/http_file_source.test.cpp index 006b7a0fb..093a5c1ce 100644 --- a/test/storage/http_file_source.test.cpp +++ b/test/storage/http_file_source.test.cpp @@ -23,8 +23,8 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP200)) { auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, [&](Response res) { EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Hello World!", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Hello World!", *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -43,7 +43,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP404)) { ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason); EXPECT_EQ("HTTP status code 404", res.error->message); - EXPECT_FALSE(bool(res.data)); + EXPECT_FALSE(res.data); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -61,7 +61,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTPTile404)) { auto req = fs.request({ Resource::Tile, "http://127.0.0.1:3000/doesnotexist" }, [&](Response res) { EXPECT_TRUE(res.noContent); EXPECT_FALSE(bool(res.error)); - EXPECT_FALSE(bool(res.data)); + EXPECT_FALSE(res.data); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -79,7 +79,8 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP200EmptyData)) { auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/empty-data" }, [&](Response res) { EXPECT_FALSE(res.noContent); EXPECT_FALSE(bool(res.error)); - EXPECT_EQ(*res.data, std::string()); + ASSERT_TRUE(res.data); + EXPECT_EQ(0u, res.data.uncompressedData()->size()); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -97,7 +98,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP204)) { auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/no-content" }, [&](Response res) { EXPECT_TRUE(res.noContent); EXPECT_FALSE(bool(res.error)); - EXPECT_FALSE(bool(res.data)); + EXPECT_FALSE(res.data); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -116,7 +117,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP500)) { ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::Server, res.error->reason); EXPECT_EQ("HTTP status code 500", res.error->message); - EXPECT_FALSE(bool(res.data)); + EXPECT_FALSE(res.data); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -134,8 +135,8 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(ExpiresParsing)) { auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?modified=1420794326&expires=1420797926&etag=foo" }, [&](Response res) { EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Hello World!", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Hello World!", *res.data.uncompressedData()); EXPECT_EQ(Timestamp{ Seconds(1420797926) }, res.expires); EXPECT_FALSE(res.mustRevalidate); EXPECT_EQ(Timestamp{ Seconds(1420794326) }, res.modified); @@ -152,8 +153,8 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(CacheControlParsing)) { auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" }, [&](Response res) { EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Hello World!", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Hello World!", *res.data.uncompressedData()); EXPECT_GT(Seconds(2), util::abs(*res.expires - util::now() - Seconds(120))) << "Expiration date isn't about 120 seconds in the future"; EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -181,8 +182,8 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(Load)) { [&, i, current](Response res) { reqs[i].reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ(std::string("Request ") + std::to_string(current), *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ(std::string("Request ") + std::to_string(current), *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); diff --git a/test/storage/local_file_source.test.cpp b/test/storage/local_file_source.test.cpp index e1756f8e7..76848f668 100644 --- a/test/storage/local_file_source.test.cpp +++ b/test/storage/local_file_source.test.cpp @@ -37,8 +37,8 @@ TEST(LocalFileSource, EmptyFile) { std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, toAbsoluteURL("empty") }, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("", *res.data.uncompressedData()); loop.stop(); }); @@ -53,8 +53,8 @@ TEST(LocalFileSource, NonEmptyFile) { std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, toAbsoluteURL("nonempty") }, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("content is here\n", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("content is here\n", *res.data.uncompressedData()); loop.stop(); }); @@ -70,7 +70,7 @@ TEST(LocalFileSource, NonExistentFile) { req.reset(); ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason); - ASSERT_FALSE(res.data.get()); + ASSERT_FALSE(res.data); // Do not assert on platform-specific error message. loop.stop(); }); @@ -88,7 +88,7 @@ TEST(LocalFileSource, InvalidURL) { ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::Other, res.error->reason); EXPECT_EQ("Invalid file URL", res.error->message); - ASSERT_FALSE(res.data.get()); + ASSERT_FALSE(res.data); loop.stop(); }); @@ -104,7 +104,7 @@ TEST(LocalFileSource, ReadDirectory) { req.reset(); ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason); - ASSERT_FALSE(res.data.get()); + ASSERT_FALSE(res.data); // Do not assert on platform-specific error message. loop.stop(); }); @@ -120,8 +120,8 @@ TEST(LocalFileSource, URLEncoding) { std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, toAbsoluteURL("%6eonempty") }, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("content is here\n", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("content is here\n", *res.data.uncompressedData()); loop.stop(); }); @@ -142,7 +142,7 @@ TEST(LocalFileSource, URLLimit) { req.reset(); ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::Other, res.error->reason); - ASSERT_FALSE(res.data.get()); + ASSERT_FALSE(res.data); loop.stop(); }); diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp index 94daf59c0..23e70448b 100644 --- a/test/storage/offline_database.test.cpp +++ b/test/storage/offline_database.test.cpp @@ -34,8 +34,8 @@ void deleteFile(const char* name) { } } -void writeFile(const char* name, const std::string& data) { - mbgl::util::write_file(name, data); +void writeFile(const char* name, mbgl::Blob&& data) { + mbgl::util::writeFile(name, data); } } // namespace @@ -79,7 +79,7 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(Invalid)) { createDir("test/fixtures/offline_database"); deleteFile("test/fixtures/offline_database/invalid.db"); - writeFile("test/fixtures/offline_database/invalid.db", "this is an invalid file"); + writeFile("test/fixtures/offline_database/invalid.db", Blob{ "this is an invalid file", false }); Log::setObserver(std::make_unique<FixtureLogObserver>()); @@ -124,23 +124,23 @@ TEST(OfflineDatabase, PutResource) { Resource resource { Resource::Style, "http://example.com/" }; Response response; - response.data = std::make_shared<std::string>("first"); + response.data = Blob{ "first", false }; auto insertPutResult = db.put(resource, response); EXPECT_TRUE(insertPutResult.first); EXPECT_EQ(5u, insertPutResult.second); auto insertGetResult = db.get(resource); EXPECT_EQ(nullptr, insertGetResult->error.get()); - EXPECT_EQ("first", *insertGetResult->data); + EXPECT_EQ("first", *insertGetResult->data.uncompressedData()); - response.data = std::make_shared<std::string>("second"); + response.data = Blob{ "second", false }; auto updatePutResult = db.put(resource, response); EXPECT_FALSE(updatePutResult.first); EXPECT_EQ(6u, updatePutResult.second); auto updateGetResult = db.get(resource); EXPECT_EQ(nullptr, updateGetResult->error.get()); - EXPECT_EQ("second", *updateGetResult->data); + EXPECT_EQ("second", *updateGetResult->data.uncompressedData()); } TEST(OfflineDatabase, PutTile) { @@ -158,23 +158,23 @@ TEST(OfflineDatabase, PutTile) { }; Response response; - response.data = std::make_shared<std::string>("first"); + response.data = Blob{ "first", false }; auto insertPutResult = db.put(resource, response); EXPECT_TRUE(insertPutResult.first); EXPECT_EQ(5u, insertPutResult.second); auto insertGetResult = db.get(resource); EXPECT_EQ(nullptr, insertGetResult->error.get()); - EXPECT_EQ("first", *insertGetResult->data); + EXPECT_EQ("first", *insertGetResult->data.uncompressedData()); - response.data = std::make_shared<std::string>("second"); + response.data = Blob{ "second", false }; auto updatePutResult = db.put(resource, response); EXPECT_FALSE(updatePutResult.first); EXPECT_EQ(6u, updatePutResult.second); auto updateGetResult = db.get(resource); EXPECT_EQ(nullptr, updateGetResult->error.get()); - EXPECT_EQ("second", *updateGetResult->data); + EXPECT_EQ("second", *updateGetResult->data.uncompressedData()); } TEST(OfflineDatabase, PutResourceNoContent) { @@ -190,7 +190,7 @@ TEST(OfflineDatabase, PutResourceNoContent) { auto res = db.get(resource); EXPECT_EQ(nullptr, res->error); EXPECT_TRUE(res->noContent); - EXPECT_FALSE(res->data.get()); + EXPECT_FALSE(res->data); } TEST(OfflineDatabase, PutTileNotFound) { @@ -213,7 +213,7 @@ TEST(OfflineDatabase, PutTileNotFound) { auto res = db.get(resource); EXPECT_EQ(nullptr, res->error); EXPECT_TRUE(res->noContent); - EXPECT_FALSE(res->data.get()); + EXPECT_FALSE(res->data); } TEST(OfflineDatabase, CreateRegion) { @@ -344,15 +344,15 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(ConcurrentUse)) { thread2.join(); } -static std::shared_ptr<std::string> randomString(size_t size) { - auto result = std::make_shared<std::string>(size, 0); +static mbgl::Blob randomBlob(size_t size) { + auto result = std::string(size, char()); std::mt19937 random; for (size_t i = 0; i < size; i++) { - (*result)[i] = random(); + result[i] = random(); } - return result; + return { std::move(result), false }; } TEST(OfflineDatabase, PutReturnsSize) { @@ -361,11 +361,11 @@ TEST(OfflineDatabase, PutReturnsSize) { OfflineDatabase db(":memory:"); Response compressible; - compressible.data = std::make_shared<std::string>(1024, 0); + compressible.data = Blob{ std::string(1024, char()), false }; EXPECT_EQ(17u, db.put(Resource::style("http://example.com/compressible"), compressible).second); Response incompressible; - incompressible.data = randomString(1024); + incompressible.data = randomBlob(1024); EXPECT_EQ(1024u, db.put(Resource::style("http://example.com/incompressible"), incompressible).second); Response noContent; @@ -379,7 +379,7 @@ TEST(OfflineDatabase, PutEvictsLeastRecentlyUsedResources) { OfflineDatabase db(":memory:", 1024 * 100); Response response; - response.data = randomString(1024); + response.data = randomBlob(1024); for (uint32_t i = 1; i <= 100; i++) { Resource resource = Resource::style("http://example.com/"s + util::toString(i)); @@ -398,7 +398,7 @@ TEST(OfflineDatabase, PutRegionResourceDoesNotEvict) { OfflineRegion region = db.createRegion(definition, OfflineRegionMetadata()); Response response; - response.data = randomString(1024); + response.data = randomBlob(1024); for (uint32_t i = 1; i <= 100; i++) { db.putRegionResource(region.getID(), Resource::style("http://example.com/"s + util::toString(i)), response); @@ -414,7 +414,7 @@ TEST(OfflineDatabase, PutFailsWhenEvictionInsuffices) { OfflineDatabase db(":memory:", 1024 * 100); Response big; - big.data = randomString(1024 * 100); + big.data = randomBlob(1024 * 100); EXPECT_FALSE(db.put(Resource::style("http://example.com/big"), big).first); EXPECT_FALSE(bool(db.get(Resource::style("http://example.com/big")))); @@ -435,7 +435,7 @@ TEST(OfflineDatabase, GetRegionCompletedStatus) { EXPECT_EQ(0u, status1.completedTileSize); Response response; - response.data = std::make_shared<std::string>("data"); + response.data = Blob{ "data", false }; uint64_t styleSize = db.putRegionResource(region.getID(), Resource::style("http://example.com/"), response); @@ -465,7 +465,7 @@ TEST(OfflineDatabase, HasRegionResource) { EXPECT_FALSE(bool(db.hasRegionResource(region.getID(), Resource::style("http://example.com/20")))); Response response; - response.data = randomString(1024); + response.data = randomBlob(1024); for (uint32_t i = 1; i <= 100; i++) { db.putRegionResource(region.getID(), Resource::style("http://example.com/"s + util::toString(i)), response); @@ -493,7 +493,7 @@ TEST(OfflineDatabase, HasRegionResourceTile) { }; Response response; - response.data = std::make_shared<std::string>("first"); + response.data = Blob{ "first", false }; EXPECT_FALSE(bool(db.hasRegionResource(region.getID(), resource))); db.putRegionResource(region.getID(), resource, response); @@ -522,7 +522,7 @@ TEST(OfflineDatabase, OfflineMapboxTileCount) { Resource mapboxTile2 = Resource::tile("mapbox://tiles/2", 1.0, 0, 0, 1, Tileset::Scheme::XYZ); Response response; - response.data = std::make_shared<std::string>("data"); + response.data = Blob{ "data", false }; // Count is initially zero. EXPECT_EQ(0u, db.getOfflineMapboxTileCount()); @@ -609,7 +609,7 @@ TEST(OfflineDatabase, MigrateFromV2Schema) { // v2.db is a v2 database containing a single offline region with a small number of resources. deleteFile("test/fixtures/offline_database/migrated.db"); - writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v2.db")); + writeFile("test/fixtures/offline_database/migrated.db", util::readFile("test/fixtures/offline_database/v2.db")); { OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0); @@ -630,7 +630,7 @@ TEST(OfflineDatabase, MigrateFromV3Schema) { // v3.db is a v3 database, migrated from v2. deleteFile("test/fixtures/offline_database/migrated.db"); - writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v3.db")); + writeFile("test/fixtures/offline_database/migrated.db", util::readFile("test/fixtures/offline_database/v3.db")); { OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0); @@ -649,7 +649,7 @@ TEST(OfflineDatabase, MigrateFromV4Schema) { // v4.db is a v4 database, migrated from v2 & v3. This database used `journal_mode = WAL` and `synchronous = NORMAL`. deleteFile("test/fixtures/offline_database/migrated.db"); - writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v4.db")); + writeFile("test/fixtures/offline_database/migrated.db", util::readFile("test/fixtures/offline_database/v4.db")); { OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0); @@ -675,7 +675,7 @@ TEST(OfflineDatabase, MigrateFromV5Schema) { // v5.db is a v5 database, migrated from v2, v3 & v4. deleteFile("test/fixtures/offline_database/migrated.db"); - writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v5.db")); + writeFile("test/fixtures/offline_database/migrated.db", util::readFile("test/fixtures/offline_database/v5.db")); { OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0); @@ -703,7 +703,7 @@ TEST(OfflineDatabase, DowngradeSchema) { // and recreated with the current schema. deleteFile("test/fixtures/offline_database/migrated.db"); - writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v999.db")); + writeFile("test/fixtures/offline_database/migrated.db", util::readFile("test/fixtures/offline_database/v999.db")); { OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0); diff --git a/test/storage/offline_download.test.cpp b/test/storage/offline_download.test.cpp index 57780eba4..f7528fb02 100644 --- a/test/storage/offline_download.test.cpp +++ b/test/storage/offline_download.test.cpp @@ -14,7 +14,6 @@ #include <iostream> using namespace mbgl; -using namespace std::literals::string_literals; class MockObserver : public OfflineRegionObserver { public: @@ -50,10 +49,11 @@ public: Response response(const std::string& path) { Response result; - result.data = std::make_shared<std::string>(util::read_file("test/fixtures/offline_download/"s + path)); - size_t uncompressed = result.data->size(); - size_t compressed = util::compress(*result.data).size(); - size += std::min(uncompressed, compressed); + result.data = util::readFile("test/fixtures/offline_download/" + path); + const auto data = util::isCompressible(*result.data.uncompressedData()) + ? result.data.compressedData() + : result.data.uncompressedData(); + size += data->size(); return result; } }; diff --git a/test/storage/online_file_source.test.cpp b/test/storage/online_file_source.test.cpp index 70bfe3ac9..9744c0a02 100644 --- a/test/storage/online_file_source.test.cpp +++ b/test/storage/online_file_source.test.cpp @@ -33,8 +33,8 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(CancelMultiple)) { std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Hello World!", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Hello World!", *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -61,7 +61,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(TemporaryError)) { ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::Server, res.error->reason); EXPECT_EQ("HTTP status code 500", res.error->message); - ASSERT_FALSE(bool(res.data)); + ASSERT_FALSE(res.data); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -72,8 +72,8 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(TemporaryError)) { EXPECT_LT(0.99, duration) << "Backoff timer didn't wait 1 second"; EXPECT_GT(1.2, duration) << "Backoff timer fired too late"; EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Hello World!", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Hello World!", *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -100,7 +100,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(ConnectionError)) { EXPECT_GT(wait + 0.2, duration) << "Backoff timer fired too late"; ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::Connection, res.error->reason); - ASSERT_FALSE(res.data.get()); + ASSERT_FALSE(res.data); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -127,8 +127,8 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(Timeout)) { std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) { counter++; EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Hello World!", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Hello World!", *res.data.uncompressedData()); EXPECT_TRUE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -244,8 +244,8 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(Load)) { [&, i, current](Response res) { reqs[i].reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ(std::string("Request ") + std::to_string(current), *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ(std::string("Request ") + std::to_string(current), *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -282,8 +282,8 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(NetworkStatusChange)) { std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); - EXPECT_EQ("Response", *res.data); + ASSERT_TRUE(res.data); + EXPECT_EQ("Response", *res.data.uncompressedData()); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -322,7 +322,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(NetworkStatusChangePreempt)) { } ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::Connection, res.error->reason); - ASSERT_FALSE(res.data.get()); + ASSERT_FALSE(res.data); EXPECT_FALSE(bool(res.expires)); EXPECT_FALSE(res.mustRevalidate); EXPECT_FALSE(bool(res.modified)); @@ -361,7 +361,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(NetworkStatusOnlineOffline)) { req.reset(); EXPECT_EQ(nullptr, res.error); - ASSERT_TRUE(res.data.get()); + ASSERT_TRUE(res.data); EXPECT_EQ(NetworkStatus::Get(), NetworkStatus::Status::Online) << "Triggered before set back to Online"; diff --git a/test/style/expression/expression.test.cpp b/test/style/expression/expression.test.cpp index 694569695..c40f3fb86 100644 --- a/test/style/expression/expression.test.cpp +++ b/test/style/expression/expression.test.cpp @@ -16,8 +16,9 @@ using namespace mbgl; using namespace mbgl::style; TEST(Expression, IsExpression) { + const auto file = util::readFile("mapbox-gl-js/src/style-spec/reference/v8.json").uncompressedData(); rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> spec; - spec.Parse<0>(util::read_file("mapbox-gl-js/src/style-spec/reference/v8.json").c_str()); + spec.Parse<0>(file->c_str()); ASSERT_FALSE(spec.HasParseError()); ASSERT_TRUE(spec.IsObject() && spec.HasMember("expression_name") && @@ -44,8 +45,9 @@ TEST_P(ExpressionEqualityTest, ExpressionEquality) { std::string error; auto parse = [&](std::string filename, std::string& error_) -> std::unique_ptr<expression::Expression> { + const auto file = util::readFile(filename).uncompressedData(); rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> document; - document.Parse<0>(util::read_file(filename).c_str()); + document.Parse<0>(file->c_str()); assert(!document.HasParseError()); const JSValue* expression = &document; expression::ParsingContext ctx; diff --git a/test/style/source.test.cpp b/test/style/source.test.cpp index 6cb01146f..94eb66a78 100644 --- a/test/style/source.test.cpp +++ b/test/style/source.test.cpp @@ -121,7 +121,7 @@ TEST(Source, LoadingCorrupt) { test.fileSource.sourceResponse = [&] (const Resource& resource) { EXPECT_EQ("url", resource.url); Response response; - response.data = std::make_unique<std::string>("CORRUPTED"); + response.data = Blob{ "CORRUPTED", false }; return response; }; @@ -375,7 +375,7 @@ TEST(Source, RasterTileCorrupt) { test.fileSource.tileResponse = [&] (const Resource&) { Response response; - response.data = std::make_unique<std::string>("CORRUPTED"); + response.data = Blob{ "CORRUPTED", false }; return response; }; @@ -412,7 +412,7 @@ TEST(Source, RasterDEMTileCorrupt) { test.fileSource.tileResponse = [&] (const Resource&) { Response response; - response.data = std::make_unique<std::string>("CORRUPTED"); + response.data = Blob{ "CORRUPTED", false }; return response; }; @@ -449,7 +449,7 @@ TEST(Source, VectorTileCorrupt) { test.fileSource.tileResponse = [&] (const Resource&) { Response response; - response.data = std::make_unique<std::string>("CORRUPTED"); + response.data = Blob{ "CORRUPTED", false }; return response; }; @@ -610,9 +610,10 @@ TEST(Source, RasterTileAttribution) { test.fileSource.sourceResponse = [&] (const Resource& resource) { EXPECT_EQ("url", resource.url); Response response; - response.data = std::make_unique<std::string>(R"TILEJSON({ "tilejson": "2.1.0", "attribution": ")TILEJSON" + - mapboxOSM + - R"TILEJSON(", "tiles": [ "tiles" ] })TILEJSON"); + response.data = + Blob{ R"TILEJSON({ "tilejson": "2.1.0", "attribution": ")TILEJSON" + mapboxOSM + + R"TILEJSON(", "tiles": [ "tiles" ] })TILEJSON", + false }; return response; }; @@ -653,9 +654,10 @@ TEST(Source, RasterDEMTileAttribution) { test.fileSource.sourceResponse = [&] (const Resource& resource) { EXPECT_EQ("url", resource.url); Response response; - response.data = std::make_unique<std::string>(R"TILEJSON({ "tilejson": "2.1.0", "attribution": ")TILEJSON" + - mapbox + - R"TILEJSON(", "tiles": [ "tiles" ] })TILEJSON"); + response.data = + Blob{ R"TILEJSON({ "tilejson": "2.1.0", "attribution": ")TILEJSON" + mapbox + + R"TILEJSON(", "tiles": [ "tiles" ] })TILEJSON", + false }; return response; }; @@ -684,7 +686,7 @@ TEST(Source, GeoJSonSourceUrlUpdate) { test.fileSource.sourceResponse = [&] (const Resource& resource) { EXPECT_EQ("url", resource.url); Response response; - response.data = std::make_unique<std::string>(R"({"geometry": {"type": "Point", "coordinates": [1.1, 1.1]}, "type": "Feature", "properties": {}})"); + response.data = Blob{ R"({"geometry": {"type": "Point", "coordinates": [1.1, 1.1]}, "type": "Feature", "properties": {}})", false }; return response; }; @@ -714,7 +716,7 @@ TEST(Source, ImageSourceImageUpdate) { test.fileSource.response = [&] (const Resource& resource) { EXPECT_EQ("http://url", resource.url); Response response; - response.data = std::make_unique<std::string>(util::read_file("test/fixtures/image/no_profile.png")); + response.data = util::readFile("test/fixtures/image/no_profile.png"); return response; }; test.styleObserver.sourceChanged = [&] (Source&) { diff --git a/test/style/style.test.cpp b/test/style/style.test.cpp index 9bdab37ac..91ecbe8d2 100644 --- a/test/style/style.test.cpp +++ b/test/style/style.test.cpp @@ -23,27 +23,27 @@ TEST(Style, Properties) { StubFileSource fileSource; Style::Impl style { threadPool, fileSource, 1.0 }; - style.loadJSON(R"STYLE({"name": "Test"})STYLE"); + style.loadJSON(Blob{ R"STYLE({"name": "Test"})STYLE", false }); ASSERT_EQ("Test", style.getName()); - style.loadJSON(R"STYLE({"center": [10, 20]})STYLE"); + style.loadJSON(Blob{ R"STYLE({"center": [10, 20]})STYLE", false }); ASSERT_EQ("", style.getName()); ASSERT_EQ((LatLng{20, 10}), *style.getDefaultCamera().center); - style.loadJSON(R"STYLE({"bearing": 24})STYLE"); + style.loadJSON(Blob{ R"STYLE({"bearing": 24})STYLE", false }); ASSERT_EQ("", style.getName()); ASSERT_EQ(LatLng {}, *style.getDefaultCamera().center); ASSERT_EQ(24, *style.getDefaultCamera().angle); - style.loadJSON(R"STYLE({"zoom": 13.3})STYLE"); + style.loadJSON(Blob{ R"STYLE({"zoom": 13.3})STYLE", false }); ASSERT_EQ("", style.getName()); ASSERT_EQ(13.3, *style.getDefaultCamera().zoom); - style.loadJSON(R"STYLE({"pitch": 60})STYLE"); + style.loadJSON(Blob{ R"STYLE({"pitch": 60})STYLE", false }); ASSERT_EQ("", style.getName()); ASSERT_EQ(60, *style.getDefaultCamera().pitch); - style.loadJSON(R"STYLE({"name": 23, "center": {}, "bearing": "north", "zoom": null, "pitch": "wide"})STYLE"); + style.loadJSON(Blob{ R"STYLE({"name": 23, "center": {}, "bearing": "north", "zoom": null, "pitch": "wide"})STYLE", false }); ASSERT_EQ("", style.getName()); ASSERT_EQ(LatLng {}, *style.getDefaultCamera().center); ASSERT_EQ(0, *style.getDefaultCamera().zoom); @@ -58,7 +58,7 @@ TEST(Style, DuplicateSource) { StubFileSource fileSource; Style::Impl style { threadPool, fileSource, 1.0 }; - style.loadJSON(util::read_file("test/fixtures/resources/style-unused-sources.json")); + style.loadJSON(util::readFile("test/fixtures/resources/style-unused-sources.json")); style.addSource(std::make_unique<VectorSource>("sourceId", "mapbox://mapbox.mapbox-terrain-v2")); @@ -80,7 +80,7 @@ TEST(Style, RemoveSourceInUse) { StubFileSource fileSource; Style::Impl style { threadPool, fileSource, 1.0 }; - style.loadJSON(util::read_file("test/fixtures/resources/style-unused-sources.json")); + style.loadJSON(util::readFile("test/fixtures/resources/style-unused-sources.json")); style.addSource(std::make_unique<VectorSource>("sourceId", "mapbox://mapbox.mapbox-terrain-v2")); style.addLayer(std::make_unique<LineLayer>("layerId", "sourceId")); diff --git a/test/style/style_layer.test.cpp b/test/style/style_layer.test.cpp index 77acca286..36fd7de3f 100644 --- a/test/style/style_layer.test.cpp +++ b/test/style/style_layer.test.cpp @@ -276,7 +276,7 @@ TEST(Layer, DuplicateLayer) { ThreadPool threadPool{ 1 }; StubFileSource fileSource; Style::Impl style { threadPool, fileSource, 1.0 }; - style.loadJSON(util::read_file("test/fixtures/resources/style-unused-sources.json")); + style.loadJSON(util::readFile("test/fixtures/resources/style-unused-sources.json")); // Add initial layer style.addLayer(std::make_unique<LineLayer>("line", "unusedsource")); diff --git a/test/style/style_parser.test.cpp b/test/style/style_parser.test.cpp index 43b982c3b..c6b78cf6d 100644 --- a/test/style/style_parser.test.cpp +++ b/test/style/style_parser.test.cpp @@ -24,16 +24,18 @@ class StyleParserTest : public ::testing::TestWithParam<std::string> {}; TEST_P(StyleParserTest, ParseStyle) { const std::string base = std::string("test/fixtures/style_parser/") + GetParam(); + const auto infoFile = util::readFile(base + ".info.json").uncompressedData(); rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> infoDoc; - infoDoc.Parse<0>(util::read_file(base + ".info.json").c_str()); + infoDoc.Parse<0>(infoFile->c_str()); ASSERT_FALSE(infoDoc.HasParseError()); ASSERT_TRUE(infoDoc.IsObject()); auto observer = new FixtureLogObserver(); Log::setObserver(std::unique_ptr<Log::Observer>(observer)); + const auto styleFile = util::readFile(base + ".style.json").uncompressedData(); style::Parser parser; - auto error = parser.parse(util::read_file(base + ".style.json")); + auto error = parser.parse(*styleFile); if (error) { Log::Error(Event::ParseStyle, "Failed to parse style: %s", util::toString(error).c_str()); @@ -95,7 +97,7 @@ INSTANTIATE_TEST_CASE_P(StyleParser, StyleParserTest, ::testing::ValuesIn([] { TEST(StyleParser, FontStacks) { style::Parser parser; - parser.parse(util::read_file("test/fixtures/style_parser/font_stacks.json")); + parser.parse(*util::readFile("test/fixtures/style_parser/font_stacks.json").uncompressedData()); auto result = parser.fontStacks(); ASSERT_EQ(3u, result.size()); ASSERT_EQ(FontStack({"a"}), result[0]); diff --git a/test/text/glyph_manager.test.cpp b/test/text/glyph_manager.test.cpp index a96e1b970..be1c13eef 100644 --- a/test/text/glyph_manager.test.cpp +++ b/test/text/glyph_manager.test.cpp @@ -92,7 +92,7 @@ TEST(GlyphManager, LoadingSuccess) { test.fileSource.glyphsResponse = [&] (const Resource& resource) { EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); Response response; - response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf")); + response.data = util::readFile("test/fixtures/resources/glyphs.pbf"); return response; }; @@ -163,7 +163,7 @@ TEST(GlyphManager, LoadingCorrupted) { test.fileSource.glyphsResponse = [&] (const Resource&) { Response response; - response.data = std::make_unique<std::string>("CORRUPTED"); + response.data = Blob{ "CORRUPTED", false }; return response; }; @@ -260,7 +260,7 @@ TEST(GlyphManager, LoadingInvalid) { test.fileSource.glyphsResponse = [&] (const Resource& resource) { EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); Response response; - response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/fake_glyphs-0-255.pbf")); + response.data = util::readFile("test/fixtures/resources/fake_glyphs-0-255.pbf"); return response; }; @@ -320,7 +320,7 @@ TEST(GlyphManager, ImmediateFileSource) { test.fileSource.glyphsResponse = [&] (const Resource&) { Response response; - response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf")); + response.data = util::readFile("test/fixtures/resources/glyphs.pbf"); return response; }; diff --git a/test/text/glyph_pbf.test.cpp b/test/text/glyph_pbf.test.cpp index c222ec1dd..8c6b8a202 100644 --- a/test/text/glyph_pbf.test.cpp +++ b/test/text/glyph_pbf.test.cpp @@ -7,7 +7,7 @@ using namespace mbgl; TEST(GlyphPBF, Parsing) { // The fake glyphs contain a number of invalid glyphs, which should be skipped by the parser. - auto sdfs = parseGlyphPBF(GlyphRange { 0, 255 }, util::read_file("test/fixtures/resources/fake_glyphs-0-255.pbf")); + auto sdfs = parseGlyphPBF(GlyphRange { 0, 255 }, util::readFile("test/fixtures/resources/fake_glyphs-0-255.pbf")); EXPECT_TRUE(sdfs.size() == 1); auto& sdf = sdfs[0]; diff --git a/test/text/local_glyph_rasterizer.test.cpp b/test/text/local_glyph_rasterizer.test.cpp index 84c685d66..49ef1e18e 100644 --- a/test/text/local_glyph_rasterizer.test.cpp +++ b/test/text/local_glyph_rasterizer.test.cpp @@ -60,10 +60,10 @@ TEST(LocalGlyphRasterizer, PingFang) { test.fileSource.glyphsResponse = [&] (const Resource& resource) { EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); Response response; - response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf")); + response.data = util::readFile("test/fixtures/resources/glyphs.pbf"); return response; }; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/local_glyphs/mixed.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/local_glyphs/mixed.json")); test.checkRendering("ping_fang"); } @@ -77,10 +77,10 @@ TEST(LocalGlyphRasterizer, NoLocal) { test.fileSource.glyphsResponse = [&] (const Resource& resource) { EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); Response response; - response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf")); + response.data = util::readFile("test/fixtures/resources/glyphs.pbf"); return response; }; - test.map.getStyle().loadJSON(util::read_file("test/fixtures/local_glyphs/mixed.json")); + test.map.getStyle().loadJSON(util::readFile("test/fixtures/local_glyphs/mixed.json")); test.checkRendering("no_local"); } diff --git a/test/util/image.test.cpp b/test/util/image.test.cpp index f4a647304..f9389a694 100644 --- a/test/util/image.test.cpp +++ b/test/util/image.test.cpp @@ -35,7 +35,7 @@ TEST(Image, PNGRoundTripAlpha) { } TEST(Image, PNGReadNoProfile) { - PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/no_profile.png")); + PremultipliedImage image = decodeImage(util::readFile("test/fixtures/image/no_profile.png")); EXPECT_EQ(128, image.data[0]); EXPECT_EQ(0, image.data[1]); EXPECT_EQ(0, image.data[2]); @@ -43,7 +43,7 @@ TEST(Image, PNGReadNoProfile) { } TEST(Image, PNGReadNoProfileAlpha) { - PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/no_profile_alpha.png")); + PremultipliedImage image = decodeImage(util::readFile("test/fixtures/image/no_profile_alpha.png")); EXPECT_EQ(64, image.data[0]); EXPECT_EQ(0, image.data[1]); EXPECT_EQ(0, image.data[2]); @@ -51,7 +51,7 @@ TEST(Image, PNGReadNoProfileAlpha) { } TEST(Image, PNGReadProfile) { - PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/profile.png")); + PremultipliedImage image = decodeImage(util::readFile("test/fixtures/image/profile.png")); EXPECT_EQ(128, image.data[0]); EXPECT_EQ(0, image.data[1]); EXPECT_EQ(0, image.data[2]); @@ -59,7 +59,7 @@ TEST(Image, PNGReadProfile) { } TEST(Image, PNGReadProfileAlpha) { - PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/profile_alpha.png")); + PremultipliedImage image = decodeImage(util::readFile("test/fixtures/image/profile_alpha.png")); EXPECT_EQ(64, image.data[0]); EXPECT_EQ(0, image.data[1]); EXPECT_EQ(0, image.data[2]); @@ -67,20 +67,20 @@ TEST(Image, PNGReadProfileAlpha) { } TEST(Image, PNGTile) { - PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/tile.png")); + PremultipliedImage image = decodeImage(util::readFile("test/fixtures/image/tile.png")); EXPECT_EQ(256u, image.size.width); EXPECT_EQ(256u, image.size.height); } TEST(Image, JPEGTile) { - PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/tile.jpeg")); + PremultipliedImage image = decodeImage(util::readFile("test/fixtures/image/tile.jpeg")); EXPECT_EQ(256u, image.size.width); EXPECT_EQ(256u, image.size.height); } #if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(QT_IMAGE_DECODERS) TEST(Image, WebPTile) { - PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/tile.webp")); + PremultipliedImage image = decodeImage(util::readFile("test/fixtures/image/tile.webp")); EXPECT_EQ(256u, image.size.width); EXPECT_EQ(256u, image.size.height); } diff --git a/test/util/memory.test.cpp b/test/util/memory.test.cpp index 6befb521f..4f14c762b 100644 --- a/test/util/memory.test.cpp +++ b/test/util/memory.test.cpp @@ -45,9 +45,7 @@ private: if (it != cache.end()) { result.data = it->second; } else { - auto data = std::make_shared<std::string>( - util::read_file("test/fixtures/resources/"s + path)); - + auto data = util::readFile("test/fixtures/resources/" + path); cache.insert(it, std::make_pair(path, data)); result.data = data; } @@ -63,7 +61,7 @@ private: } }; - std::unordered_map<std::string, std::shared_ptr<std::string>> cache; + std::unordered_map<std::string, Blob> cache; }; TEST(Memory, Vector) { |