summaryrefslogtreecommitdiff
path: root/src/mbgl/util
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2014-12-04 18:29:42 +0100
committerKonstantin Käfer <mail@kkaefer.com>2014-12-04 20:02:50 +0100
commitabafb52f37beb5659efc2105ccd1568e1f754898 (patch)
tree6a60636d3497560ca61e5aae5f6d7061c4f18553 /src/mbgl/util
parentbff6aeb4da41dee1f5f1cfa0be81b6c257257253 (diff)
downloadqtlocation-mapboxgl-abafb52f37beb5659efc2105ccd1568e1f754898.tar.gz
make most headers private
Diffstat (limited to 'src/mbgl/util')
-rw-r--r--src/mbgl/util/box.hpp15
-rw-r--r--src/mbgl/util/clip_ids.cpp96
-rw-r--r--src/mbgl/util/clip_ids.hpp38
-rw-r--r--src/mbgl/util/compression.cpp81
-rw-r--r--src/mbgl/util/compression.hpp15
-rw-r--r--src/mbgl/util/constants.cpp27
-rw-r--r--src/mbgl/util/constants.hpp31
-rw-r--r--src/mbgl/util/error.hpp20
-rw-r--r--src/mbgl/util/interpolate.hpp27
-rw-r--r--src/mbgl/util/io.cpp34
-rw-r--r--src/mbgl/util/io.hpp15
-rw-r--r--src/mbgl/util/mapbox.cpp44
-rw-r--r--src/mbgl/util/mapbox.hpp17
-rw-r--r--src/mbgl/util/mat3.cpp95
-rw-r--r--src/mbgl/util/mat3.hpp42
-rw-r--r--src/mbgl/util/mat4.cpp198
-rw-r--r--src/mbgl/util/math.cpp25
-rw-r--r--src/mbgl/util/optional.hpp69
-rw-r--r--src/mbgl/util/parsedate.c689
-rw-r--r--src/mbgl/util/pbf.hpp184
-rw-r--r--src/mbgl/util/queue.h92
-rw-r--r--src/mbgl/util/raster.cpp106
-rw-r--r--src/mbgl/util/raster.hpp74
-rw-r--r--src/mbgl/util/rect.hpp22
-rw-r--r--src/mbgl/util/sqlite3.cpp165
-rw-r--r--src/mbgl/util/sqlite3.hpp74
-rw-r--r--src/mbgl/util/stopwatch.cpp36
-rw-r--r--src/mbgl/util/stopwatch.hpp40
-rw-r--r--src/mbgl/util/texture_pool.cpp58
-rw-r--r--src/mbgl/util/texture_pool.hpp25
-rw-r--r--src/mbgl/util/time.cpp25
-rw-r--r--src/mbgl/util/token.hpp50
-rw-r--r--src/mbgl/util/transition.cpp26
-rw-r--r--src/mbgl/util/transition.hpp77
-rw-r--r--src/mbgl/util/unitbezier.hpp121
-rw-r--r--src/mbgl/util/url.cpp51
-rw-r--r--src/mbgl/util/url.hpp15
-rw-r--r--src/mbgl/util/utf.hpp24
-rw-r--r--src/mbgl/util/uv-channel.c69
-rw-r--r--src/mbgl/util/uv-channel.h29
-rw-r--r--src/mbgl/util/uv-messenger.c86
-rw-r--r--src/mbgl/util/uv-worker.c170
-rw-r--r--src/mbgl/util/uv-worker.h41
-rw-r--r--src/mbgl/util/uv.cpp25
-rw-r--r--src/mbgl/util/uv_detail.hpp177
-rw-r--r--src/mbgl/util/vec.hpp15
46 files changed, 3455 insertions, 0 deletions
diff --git a/src/mbgl/util/box.hpp b/src/mbgl/util/box.hpp
new file mode 100644
index 0000000000..55a5d46fbc
--- /dev/null
+++ b/src/mbgl/util/box.hpp
@@ -0,0 +1,15 @@
+#ifndef MBGL_UTIL_BOX
+#define MBGL_UTIL_BOX
+
+#include <mbgl/util/vec.hpp>
+
+namespace mbgl {
+
+struct box {
+ vec2<double> tl, tr, bl, br;
+ vec2<double> center;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/util/clip_ids.cpp b/src/mbgl/util/clip_ids.cpp
new file mode 100644
index 0000000000..9c391c38ad
--- /dev/null
+++ b/src/mbgl/util/clip_ids.cpp
@@ -0,0 +1,96 @@
+#include <mbgl/util/clip_ids.hpp>
+#include <mbgl/map/tile.hpp>
+
+#include <mbgl/util/math.hpp>
+
+#include <list>
+#include <vector>
+#include <bitset>
+#include <cassert>
+#include <iostream>
+#include <algorithm>
+
+namespace mbgl {
+
+ClipIDGenerator::Leaf::Leaf(Tile &tile_) : tile(tile_) {}
+
+void ClipIDGenerator::Leaf::add(const Tile::ID &p) {
+ if (p.isChildOf(tile.id)) {
+ // Ensure that no already present child is a parent of the new p.
+ for (const Tile::ID &child : children) {
+ if (p.isChildOf(child))
+ return;
+ }
+ children.push_front(p);
+ }
+}
+
+bool ClipIDGenerator::Leaf::operator==(const Leaf &other) const {
+ return tile.id == other.tile.id && children == other.children;
+}
+
+bool ClipIDGenerator::reuseExisting(Leaf &leaf) {
+ for (const std::vector<Leaf> &pool : pools) {
+ auto existing = std::find(pool.begin(), pool.end(), leaf);
+ if (existing != pool.end()) {
+ leaf.tile.clip = existing->tile.clip;
+ return true;
+ }
+ }
+ return false;
+}
+
+void ClipIDGenerator::update(std::forward_list<Tile *> tiles) {
+ Pool pool;
+
+ tiles.sort([](const Tile *a, const Tile *b) {
+ return a->id < b->id;
+ });
+
+ const auto end = tiles.end();
+ for (auto it = tiles.begin(); it != end; it++) {
+ if (!*it) {
+ // Handle null pointers.
+ continue;
+ }
+
+ Tile &tile = **it;
+ Leaf clip { tile };
+
+ // Try to add all remaining ids as children. We sorted the tile list
+ // by z earlier, so all preceding items cannot be children of the current
+ // tile.
+ for (auto child_it = std::next(it); child_it != end; child_it++) {
+ clip.add((*child_it)->id);
+ }
+ clip.children.sort();
+
+ // Loop through all existing pools and try to find a matching ClipID.
+ if (!reuseExisting(clip)) {
+ // We haven't found an existing clip ID
+ pool.push_back(std::move(clip));
+ }
+ }
+
+ if (pool.size()) {
+ const uint32_t bit_count = util::ceil_log2(pool.size() + 1);
+ const std::bitset<8> mask = uint64_t(((1 << bit_count) - 1) << bit_offset);
+
+ // We are starting our count with 1 since we need at least 1 bit set to distinguish between
+ // areas without any tiles whatsoever and the current area.
+ uint8_t count = 1;
+ for (Leaf &leaf : pool) {
+ leaf.tile.clip.mask = mask;
+ leaf.tile.clip.reference = count++ << bit_offset;
+ }
+
+ bit_offset += bit_count;
+ pools.push_front(std::move(pool));
+ }
+
+ if (bit_offset > 8) {
+ fprintf(stderr, "stencil mask overflow\n");
+ }
+}
+
+}
diff --git a/src/mbgl/util/clip_ids.hpp b/src/mbgl/util/clip_ids.hpp
new file mode 100644
index 0000000000..5855b16af7
--- /dev/null
+++ b/src/mbgl/util/clip_ids.hpp
@@ -0,0 +1,38 @@
+#ifndef MBGL_UTIL_CLIP_IDS
+#define MBGL_UTIL_CLIP_IDS
+
+#include <mbgl/map/tile.hpp>
+#include <list>
+#include <set>
+#include <vector>
+#include <forward_list>
+#include <map>
+
+namespace mbgl {
+
+class ClipIDGenerator {
+private:
+ struct Leaf {
+ Leaf(Tile &tile);
+ void add(const Tile::ID &p);
+ bool operator==(const Leaf &other) const;
+
+ Tile &tile;
+ std::forward_list<Tile::ID> children;
+ };
+
+ typedef std::vector<Leaf> Pool;
+ std::forward_list<Pool> pools;
+ uint8_t bit_offset = 0;
+
+private:
+ bool reuseExisting(Leaf &leaf);
+
+public:
+ void update(std::forward_list<Tile *> tiles);
+};
+
+
+}
+
+#endif
diff --git a/src/mbgl/util/compression.cpp b/src/mbgl/util/compression.cpp
new file mode 100644
index 0000000000..d6d6370546
--- /dev/null
+++ b/src/mbgl/util/compression.cpp
@@ -0,0 +1,81 @@
+#include <mbgl/util/compression.hpp>
+
+#include <zlib.h>
+
+#include <cstring>
+#include <stdexcept>
+
+namespace mbgl {
+namespace util {
+
+std::string compress(const std::string &raw) {
+ z_stream deflate_stream;
+ memset(&deflate_stream, 0, sizeof(deflate_stream));
+
+ // TODO: reuse z_streams
+ if (deflateInit(&deflate_stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
+ throw std::runtime_error("failed to initialize deflate");
+ }
+
+ deflate_stream.next_in = (Bytef *)raw.data();
+ deflate_stream.avail_in = uInt(raw.size());
+
+ std::string result;
+ char out[16384];
+
+ int code;
+ do {
+ deflate_stream.next_out = reinterpret_cast<Bytef *>(out);
+ deflate_stream.avail_out = sizeof(out);
+ code = deflate(&deflate_stream, Z_FINISH);
+ if (result.size() < deflate_stream.total_out) {
+ // append the block to the output string
+ result.append(out, deflate_stream.total_out - result.size());
+ }
+ } while (code == Z_OK);
+
+ deflateEnd(&deflate_stream);
+
+ if (code != Z_STREAM_END) {
+ throw std::runtime_error(deflate_stream.msg);
+ }
+
+ return result;
+}
+
+std::string decompress(const std::string &raw) {
+ z_stream inflate_stream;
+ memset(&inflate_stream, 0, sizeof(inflate_stream));
+
+ // TODO: reuse z_streams
+ if (inflateInit(&inflate_stream) != Z_OK) {
+ throw std::runtime_error("failed to initialize inflate");
+ }
+
+ inflate_stream.next_in = (Bytef *)raw.data();
+ inflate_stream.avail_in = uInt(raw.size());
+
+ std::string result;
+ char out[15384];
+
+ int code;
+ do {
+ inflate_stream.next_out = reinterpret_cast<Bytef *>(out);
+ inflate_stream.avail_out = sizeof(out);
+ code = inflate(&inflate_stream, 0);
+ // result.append(out, sizeof(out) - inflate_stream.avail_out);
+ if (result.size() < inflate_stream.total_out) {
+ result.append(out, inflate_stream.total_out - result.size());
+ }
+ } while (code == Z_OK);
+
+ inflateEnd(&inflate_stream);
+
+ if (code != Z_STREAM_END) {
+ throw std::runtime_error(inflate_stream.msg);
+ }
+
+ return result;
+}
+}
+}
diff --git a/src/mbgl/util/compression.hpp b/src/mbgl/util/compression.hpp
new file mode 100644
index 0000000000..a33b2476a7
--- /dev/null
+++ b/src/mbgl/util/compression.hpp
@@ -0,0 +1,15 @@
+#ifndef MBGL_UTIL_COMPRESSION
+#define MBGL_UTIL_COMPRESSION
+
+#include <string>
+
+namespace mbgl {
+namespace util {
+
+std::string compress(const std::string &raw);
+std::string decompress(const std::string &raw);
+
+}
+}
+
+#endif
diff --git a/src/mbgl/util/constants.cpp b/src/mbgl/util/constants.cpp
new file mode 100644
index 0000000000..3d1422e6c7
--- /dev/null
+++ b/src/mbgl/util/constants.cpp
@@ -0,0 +1,27 @@
+#include <mbgl/util/constants.hpp>
+
+const float mbgl::util::tileSize = 512.0f;
+
+#if defined(DEBUG)
+const bool mbgl::debug::tileParseWarnings = false;
+const bool mbgl::debug::styleParseWarnings = false;
+const bool mbgl::debug::spriteWarnings = false;
+const bool mbgl::debug::renderWarnings = false;
+const bool mbgl::debug::renderTree = false;
+const bool mbgl::debug::labelTextMissingWarning = true;
+const bool mbgl::debug::missingFontStackWarning = true;
+const bool mbgl::debug::missingFontFaceWarning = true;
+const bool mbgl::debug::glyphWarning = true;
+const bool mbgl::debug::shapingWarning = true;
+#else
+const bool mbgl::debug::tileParseWarnings = false;
+const bool mbgl::debug::styleParseWarnings = false;
+const bool mbgl::debug::spriteWarnings = false;
+const bool mbgl::debug::renderWarnings = false;
+const bool mbgl::debug::renderTree = false;
+const bool mbgl::debug::labelTextMissingWarning = false;
+const bool mbgl::debug::missingFontStackWarning = false;
+const bool mbgl::debug::missingFontFaceWarning = false;
+const bool mbgl::debug::glyphWarning = false;
+const bool mbgl::debug::shapingWarning = false;
+#endif
diff --git a/src/mbgl/util/constants.hpp b/src/mbgl/util/constants.hpp
new file mode 100644
index 0000000000..98365e0f32
--- /dev/null
+++ b/src/mbgl/util/constants.hpp
@@ -0,0 +1,31 @@
+#ifndef MBGL_UTIL_CONSTANTS
+#define MBGL_UTIL_CONSTANTS
+
+#include <cmath>
+
+namespace mbgl {
+
+namespace util {
+
+extern const float tileSize;
+
+}
+
+namespace debug {
+
+extern const bool tileParseWarnings;
+extern const bool styleParseWarnings;
+extern const bool spriteWarnings;
+extern const bool renderWarnings;
+extern const bool renderTree;
+extern const bool labelTextMissingWarning;
+extern const bool missingFontStackWarning;
+extern const bool missingFontFaceWarning;
+extern const bool glyphWarning;
+extern const bool shapingWarning;
+
+}
+
+}
+
+#endif
diff --git a/src/mbgl/util/error.hpp b/src/mbgl/util/error.hpp
new file mode 100644
index 0000000000..09fa8d3e21
--- /dev/null
+++ b/src/mbgl/util/error.hpp
@@ -0,0 +1,20 @@
+#ifndef MBGL_UTIL_ERROR
+#define MBGL_UTIL_ERROR
+
+#include <stdexcept>
+#include <string>
+
+namespace mbgl {
+namespace error {
+
+struct style_parse : std::exception {
+ inline style_parse(size_t offset_, const char *msg_) : offset(offset_), msg(msg_) {}
+ inline const char* what() const noexcept { return msg.c_str(); }
+ const size_t offset;
+ const std::string msg;
+};
+}
+
+}
+
+#endif \ No newline at end of file
diff --git a/src/mbgl/util/interpolate.hpp b/src/mbgl/util/interpolate.hpp
new file mode 100644
index 0000000000..c9232db4eb
--- /dev/null
+++ b/src/mbgl/util/interpolate.hpp
@@ -0,0 +1,27 @@
+#ifndef MBGL_UTIL_INTERPOLATE
+#define MBGL_UTIL_INTERPOLATE
+
+#include <array>
+
+namespace mbgl {
+namespace util {
+
+template <typename T>
+T interpolate(const T a, const T b, const double t) {
+ return a * (1.0 - t) + b * t;
+}
+
+template <typename T>
+inline std::array<T, 4> interpolate(const std::array<T, 4>& a, const std::array<T, 4>& b, const double t) {
+ return {{
+ interpolate(a[0], b[0], t),
+ interpolate(a[1], b[1], t),
+ interpolate(a[2], b[2], t),
+ interpolate(a[3], b[3], t)
+ }};
+}
+
+}
+}
+
+#endif
diff --git a/src/mbgl/util/io.cpp b/src/mbgl/util/io.cpp
new file mode 100644
index 0000000000..76f7c35ade
--- /dev/null
+++ b/src/mbgl/util/io.cpp
@@ -0,0 +1,34 @@
+#include <mbgl/util/io.hpp>
+
+#include <cstdio>
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <stdexcept>
+
+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);
+ if (file.good()) {
+ std::stringstream data;
+ data << file.rdbuf();
+ return data.str();
+ } else {
+ throw std::runtime_error(std::string("Cannot read file ") + filename);
+ }
+}
+
+}
+}
diff --git a/src/mbgl/util/io.hpp b/src/mbgl/util/io.hpp
new file mode 100644
index 0000000000..e95fc16d9d
--- /dev/null
+++ b/src/mbgl/util/io.hpp
@@ -0,0 +1,15 @@
+#ifndef MBGL_UTIL_IO
+#define MBGL_UTIL_IO
+
+#include <string>
+
+namespace mbgl {
+namespace util {
+
+void write_file(const std::string &filename, const std::string &data);
+std::string read_file(const std::string &filename);
+
+}
+}
+
+#endif
diff --git a/src/mbgl/util/mapbox.cpp b/src/mbgl/util/mapbox.cpp
new file mode 100644
index 0000000000..277b647f34
--- /dev/null
+++ b/src/mbgl/util/mapbox.cpp
@@ -0,0 +1,44 @@
+#include <mbgl/util/mapbox.hpp>
+
+#include <stdexcept>
+
+namespace mbgl {
+namespace util {
+namespace mapbox {
+
+const std::string mapbox = "mapbox://";
+
+std::string normalizeURL(const std::string& url, const std::string& accessToken) {
+ if (accessToken.empty())
+ throw std::runtime_error("You must provide a Mapbox API access token for Mapbox tile sources");
+
+ return std::string("https://api.tiles.mapbox.com/v4/")
+ + url.substr(mapbox.length())
+ + "?access_token="
+ + accessToken;
+}
+
+std::string normalizeSourceURL(const std::string& url, const std::string& accessToken) {
+ if (url.compare(0, mapbox.length(), mapbox) != 0)
+ return url;
+
+ std::string result = normalizeURL(url + ".json", accessToken);
+
+ // TileJSON requests need a secure flag appended to their URLs so
+ // that the server knows to send SSL-ified resource references.
+ if (url.compare(0, 5, "https") == 0)
+ result += "&secure";
+
+ return result;
+}
+
+std::string normalizeGlyphsURL(const std::string& url, const std::string& accessToken) {
+ if (url.compare(0, mapbox.length(), mapbox) != 0)
+ return url;
+
+ return normalizeURL(url, accessToken);
+}
+
+}
+}
+}
diff --git a/src/mbgl/util/mapbox.hpp b/src/mbgl/util/mapbox.hpp
new file mode 100644
index 0000000000..0fbb9a91ed
--- /dev/null
+++ b/src/mbgl/util/mapbox.hpp
@@ -0,0 +1,17 @@
+#ifndef MBGL_UTIL_MAPBOX
+#define MBGL_UTIL_MAPBOX
+
+#include <string>
+
+namespace mbgl {
+namespace util {
+namespace mapbox {
+
+std::string normalizeSourceURL(const std::string& url, const std::string& accessToken);
+std::string normalizeGlyphsURL(const std::string& url, const std::string& accessToken);
+
+}
+}
+}
+
+#endif
diff --git a/src/mbgl/util/mat3.cpp b/src/mbgl/util/mat3.cpp
new file mode 100644
index 0000000000..263768ee41
--- /dev/null
+++ b/src/mbgl/util/mat3.cpp
@@ -0,0 +1,95 @@
+// This is an incomplete port of http://glmatrix.net/
+//
+// Copyright (c) 2013 Brandon Jones, Colin MacKenzie IV
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from the
+// use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not claim
+// that you wrote the original software. If you use this software in a
+// product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+
+#include <mbgl/util/mat3.hpp>
+
+#include <cmath>
+
+using namespace mbgl;
+
+void matrix::identity(mat3& out) {
+ out[0] = 1.0f;
+ out[1] = 0.0f;
+ out[2] = 0.0f;
+ out[3] = 0.0f;
+ out[4] = 1.0f;
+ out[5] = 0.0f;
+ out[6] = 0.0f;
+ out[7] = 0.0f;
+ out[8] = 1.0f;
+}
+
+void matrix::translate(mat3& out, const mat3& a, float x, float y) {
+ float a00 = a[0], a01 = a[1], a02 = a[2],
+ a10 = a[3], a11 = a[4], a12 = a[5],
+ a20 = a[6], a21 = a[7], a22 = a[8];
+
+ out[0] = a00;
+ out[1] = a01;
+ out[2] = a02;
+
+ out[3] = a10;
+ out[4] = a11;
+ out[5] = a12;
+
+ out[6] = x * a00 + y * a10 + a20;
+ out[7] = x * a01 + y * a11 + a21;
+ out[8] = x * a02 + y * a12 + a22;
+}
+
+void matrix::rotate(mat3& out, const mat3& a, float rad) {
+ float s = std::sin(rad),
+ c = std::cos(rad),
+ a00 = a[0],
+ a01 = a[1],
+ a02 = a[2],
+ a10 = a[3],
+ a11 = a[4],
+ a12 = a[5],
+ a20 = a[6],
+ a21 = a[7],
+ a22 = a[8];
+
+ out[0] = c * a00 + s * a10;
+ out[1] = c * a01 + s * a11;
+ out[2] = c * a02 + s * a12;
+
+ out[3] = c * a10 - s * a00;
+ out[4] = c * a11 - s * a01;
+ out[5] = c * a12 - s * a02;
+
+ out[6] = a20;
+ out[7] = a21;
+ out[8] = a22;
+};
+
+void matrix::scale(mat3& out, const mat3& a, float x, float y) {
+ out[0] = x * a[0];
+ out[1] = x * a[1];
+ out[2] = x * a[2];
+ out[3] = y * a[3];
+ out[4] = y * a[4];
+ out[5] = y * a[5];
+ out[6] = a[6];
+ out[7] = a[7];
+ out[8] = a[8];
+}
diff --git a/src/mbgl/util/mat3.hpp b/src/mbgl/util/mat3.hpp
new file mode 100644
index 0000000000..fa40751764
--- /dev/null
+++ b/src/mbgl/util/mat3.hpp
@@ -0,0 +1,42 @@
+// This is an incomplete port of http://glmatrix.net/
+//
+// Copyright (c) 2013 Brandon Jones, Colin MacKenzie IV
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from the
+// use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not claim
+// that you wrote the original software. If you use this software in a
+// product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+
+#ifndef MBGL_UTIL_MAT3
+#define MBGL_UTIL_MAT3
+
+#include <array>
+
+namespace mbgl {
+
+typedef std::array<float, 9> mat3;
+
+namespace matrix {
+
+void identity(mat3& out);
+void translate(mat3& out, const mat3& a, float x, float y);
+void rotate(mat3& out, const mat3& a, float rad);
+void scale(mat3& out, const mat3& a, float x, float y);
+
+}
+}
+
+#endif
diff --git a/src/mbgl/util/mat4.cpp b/src/mbgl/util/mat4.cpp
new file mode 100644
index 0000000000..50270d9217
--- /dev/null
+++ b/src/mbgl/util/mat4.cpp
@@ -0,0 +1,198 @@
+// This is an incomplete port of http://glmatrix.net/
+//
+// Copyright (c) 2013 Brandon Jones, Colin MacKenzie IV
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from the
+// use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not claim
+// that you wrote the original software. If you use this software in a
+// product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+
+#include <mbgl/util/mat4.hpp>
+
+#include <cmath>
+
+using namespace mbgl;
+
+void matrix::identity(mat4& out) {
+ out[0] = 1.0f;
+ out[1] = 0.0f;
+ out[2] = 0.0f;
+ out[3] = 0.0f;
+ out[4] = 0.0f;
+ out[5] = 1.0f;
+ out[6] = 0.0f;
+ out[7] = 0.0f;
+ out[8] = 0.0f;
+ out[9] = 0.0f;
+ out[10] = 1.0f;
+ out[11] = 0.0f;
+ out[12] = 0.0f;
+ out[13] = 0.0f;
+ out[14] = 0.0f;
+ out[15] = 1.0f;
+}
+
+void matrix::ortho(mat4& out, float left, float right, float bottom, float top, float near, float far) {
+ float lr = 1.0f / (left - right),
+ bt = 1.0f / (bottom - top),
+ nf = 1.0f / (near - far);
+ out[0] = -2.0f * lr;
+ out[1] = 0.0f;
+ out[2] = 0.0f;
+ out[3] = 0.0f;
+ out[4] = 0.0f;
+ out[5] = -2.0f * bt;
+ out[6] = 0.0f;
+ out[7] = 0.0f;
+ out[8] = 0.0f;
+ out[9] = 0.0f;
+ out[10] = 2.0f * nf;
+ out[11] = 0.0f;
+ out[12] = (left + right) * lr;
+ out[13] = (top + bottom) * bt;
+ out[14] = (far + near) * nf;
+ out[15] = 1.0f;
+}
+
+void matrix::copy(mat4& out, const mat4& a) {
+ out[0] = a[0];
+ out[1] = a[1];
+ out[2] = a[2];
+ out[3] = a[3];
+ out[4] = a[4];
+ out[5] = a[5];
+ out[6] = a[6];
+ out[7] = a[7];
+ out[8] = a[8];
+ out[9] = a[9];
+ out[10] = a[10];
+ out[11] = a[11];
+ out[12] = a[12];
+ out[13] = a[13];
+ out[14] = a[14];
+ out[15] = a[15];
+}
+
+void matrix::translate(mat4& out, const mat4& a, float x, float y, float z) {
+ if (a == out) {
+ out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
+ out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
+ out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
+ out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
+ } else {
+ float a00, a01, a02, a03,
+ a10, a11, a12, a13,
+ a20, a21, a22, a23;
+
+ a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
+ a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
+ a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
+
+ out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03;
+ out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13;
+ out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23;
+
+ out[12] = a00 * x + a10 * y + a20 * z + a[12];
+ out[13] = a01 * x + a11 * y + a21 * z + a[13];
+ out[14] = a02 * x + a12 * y + a22 * z + a[14];
+ out[15] = a03 * x + a13 * y + a23 * z + a[15];
+ }
+}
+
+void matrix::rotate_z(mat4& out, const mat4& a, float rad) {
+ float s = std::sin(rad),
+ c = std::cos(rad),
+ a00 = a[0],
+ a01 = a[1],
+ a02 = a[2],
+ a03 = a[3],
+ a10 = a[4],
+ a11 = a[5],
+ a12 = a[6],
+ a13 = a[7];
+
+ if (a != out) { // If the source and destination differ, copy the unchanged last row
+ out[8] = a[8];
+ out[9] = a[9];
+ out[10] = a[10];
+ out[11] = a[11];
+ out[12] = a[12];
+ out[13] = a[13];
+ out[14] = a[14];
+ out[15] = a[15];
+ }
+
+ // Perform axis-specific matrix multiplication
+ out[0] = a00 * c + a10 * s;
+ out[1] = a01 * c + a11 * s;
+ out[2] = a02 * c + a12 * s;
+ out[3] = a03 * c + a13 * s;
+ out[4] = a10 * c - a00 * s;
+ out[5] = a11 * c - a01 * s;
+ out[6] = a12 * c - a02 * s;
+ out[7] = a13 * c - a03 * s;
+}
+
+void matrix::scale(mat4& out, const mat4& a, float x, float y, float z) {
+ out[0] = a[0] * x;
+ out[1] = a[1] * x;
+ out[2] = a[2] * x;
+ out[3] = a[3] * x;
+ out[4] = a[4] * y;
+ out[5] = a[5] * y;
+ out[6] = a[6] * y;
+ out[7] = a[7] * y;
+ out[8] = a[8] * z;
+ out[9] = a[9] * z;
+ out[10] = a[10] * z;
+ out[11] = a[11] * z;
+ out[12] = a[12];
+ out[13] = a[13];
+ out[14] = a[14];
+ out[15] = a[15];
+}
+
+void matrix::multiply(mat4& out, const mat4& a, const mat4& b) {
+ float a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
+ a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
+ a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
+ a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
+
+ // Cache only the current line of the second matrix
+ float b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
+ out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
+ out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
+ out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
+ out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
+
+ b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7];
+ out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
+ out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
+ out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
+ out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
+
+ b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11];
+ out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
+ out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
+ out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
+ out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
+
+ b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15];
+ out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
+ out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
+ out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
+ out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
+}
diff --git a/src/mbgl/util/math.cpp b/src/mbgl/util/math.cpp
new file mode 100644
index 0000000000..a7eab2d771
--- /dev/null
+++ b/src/mbgl/util/math.cpp
@@ -0,0 +1,25 @@
+#include <mbgl/util/math.hpp>
+
+namespace mbgl {
+namespace util {
+
+// From http://stackoverflow.com/questions/3272424/compute-fast-log-base-2-ceiling
+uint32_t ceil_log2(uint64_t x) {
+ static const uint64_t t[6] = {0xFFFFFFFF00000000, 0x00000000FFFF0000,
+ 0x000000000000FF00, 0x00000000000000F0,
+ 0x000000000000000C, 0x0000000000000002};
+ uint32_t y = (((x & (x - 1)) == 0) ? 0 : 1);
+ uint32_t j = 32;
+
+ for (int32_t i = 0; i < 6; i++) {
+ const uint32_t k = (((x & t[i]) == 0) ? 0 : j);
+ y += k;
+ x >>= k;
+ j >>= 1;
+ }
+
+ return y;
+}
+
+}
+} \ No newline at end of file
diff --git a/src/mbgl/util/optional.hpp b/src/mbgl/util/optional.hpp
new file mode 100644
index 0000000000..8d46eae857
--- /dev/null
+++ b/src/mbgl/util/optional.hpp
@@ -0,0 +1,69 @@
+#ifndef MAPBOX_UTIL_OPTIONAL_HPP
+#define MAPBOX_UTIL_OPTIONAL_HPP
+
+#include <type_traits>
+
+#include <mbgl/util/variant.hpp>
+
+namespace mapbox
+{
+namespace util
+{
+
+template <typename T> class optional
+{
+ static_assert(!std::is_reference<T>::value, "optional doesn't support references");
+
+ struct none_type
+ {
+ };
+
+ variant<none_type, T> variant_;
+
+ public:
+ optional() = default;
+
+ optional(optional const &rhs)
+ {
+ if (this != &rhs)
+ { // protect against invalid self-assignment
+ variant_ = rhs.variant_;
+ }
+ }
+
+ optional(T const &v) { variant_ = v; }
+
+ explicit operator bool() const noexcept { return variant_.template is<T>(); }
+
+ T const &get() const { return variant_.template get<T>(); }
+ T &get() { return variant_.template get<T>(); }
+
+ T const &operator*() const { return this->get(); }
+ T operator*() { return this->get(); }
+
+ optional &operator=(T const &v)
+ {
+ variant_ = v;
+ return *this;
+ }
+
+ optional &operator=(optional const &rhs)
+ {
+ if (this != &rhs)
+ {
+ variant_ = rhs.variant_;
+ }
+ return *this;
+ }
+
+ template <typename... Args> void emplace(Args &&... args)
+ {
+ variant_ = T{std::forward<Args>(args)...};
+ }
+
+ void reset() { variant_ = none_type{}; }
+};
+}
+}
+
+#endif
diff --git a/src/mbgl/util/parsedate.c b/src/mbgl/util/parsedate.c
new file mode 100644
index 0000000000..f888def853
--- /dev/null
+++ b/src/mbgl/util/parsedate.c
@@ -0,0 +1,689 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+/*
+ A brief summary of the date string formats this parser groks:
+
+ RFC 2616 3.3.1
+
+ Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+
+ we support dates without week day name:
+
+ 06 Nov 1994 08:49:37 GMT
+ 06-Nov-94 08:49:37 GMT
+ Nov 6 08:49:37 1994
+
+ without the time zone:
+
+ 06 Nov 1994 08:49:37
+ 06-Nov-94 08:49:37
+
+ weird order:
+
+ 1994 Nov 6 08:49:37 (GNU date fails)
+ GMT 08:49:37 06-Nov-94 Sunday
+ 94 6 Nov 08:49:37 (GNU date fails)
+
+ time left out:
+
+ 1994 Nov 6
+ 06-Nov-94
+ Sun Nov 6 94
+
+ unusual separators:
+
+ 1994.Nov.6
+ Sun/Nov/6/94/GMT
+
+ commonly used time zone names:
+
+ Sun, 06 Nov 1994 08:49:37 CET
+ 06 Nov 1994 08:49:37 EST
+
+ time zones specified using RFC822 style:
+
+ Sun, 12 Sep 2004 15:05:58 -0700
+ Sat, 11 Sep 2004 21:32:11 +0200
+
+ compact numerical date strings:
+
+ 20040912 15:05:58 -0700
+ 20040911 +0200
+
+*/
+
+#include <mbgl/util/parsedate.h>
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <limits.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+
+#define ERRNO (errno)
+#define SET_ERRNO(x) (errno = (x))
+
+
+/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because
+ its behavior is altered by the current locale. */
+char raw_toupper(char in)
+{
+ switch (in) {
+ case 'a':
+ return 'A';
+ case 'b':
+ return 'B';
+ case 'c':
+ return 'C';
+ case 'd':
+ return 'D';
+ case 'e':
+ return 'E';
+ case 'f':
+ return 'F';
+ case 'g':
+ return 'G';
+ case 'h':
+ return 'H';
+ case 'i':
+ return 'I';
+ case 'j':
+ return 'J';
+ case 'k':
+ return 'K';
+ case 'l':
+ return 'L';
+ case 'm':
+ return 'M';
+ case 'n':
+ return 'N';
+ case 'o':
+ return 'O';
+ case 'p':
+ return 'P';
+ case 'q':
+ return 'Q';
+ case 'r':
+ return 'R';
+ case 's':
+ return 'S';
+ case 't':
+ return 'T';
+ case 'u':
+ return 'U';
+ case 'v':
+ return 'V';
+ case 'w':
+ return 'W';
+ case 'x':
+ return 'X';
+ case 'y':
+ return 'Y';
+ case 'z':
+ return 'Z';
+ }
+ return in;
+}
+
+/*
+ * raw_equal() is for doing "raw" case insensitive strings. This is meant
+ * to be locale independent and only compare strings we know are safe for
+ * this. See http://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for
+ * some further explanation to why this function is necessary.
+ *
+ * The function is capable of comparing a-z case insensitively even for
+ * non-ascii.
+ */
+
+int raw_equal(const char *first, const char *second)
+{
+ while(*first && *second) {
+ if(raw_toupper(*first) != raw_toupper(*second))
+ /* get out of the loop as soon as they don't match */
+ break;
+ first++;
+ second++;
+ }
+ /* we do the comparison here (possibly again), just to make sure that if the
+ loop above is skipped because one of the strings reached zero, we must not
+ return this as a successful match */
+ return (raw_toupper(*first) == raw_toupper(*second));
+}
+
+#define ISSPACE(x) (isspace((int) ((unsigned char)x)))
+#define ISDIGIT(x) (isdigit((int) ((unsigned char)x)))
+#define ISALNUM(x) (isalnum((int) ((unsigned char)x)))
+#define ISALPHA(x) (isalpha((int) ((unsigned char)x)))
+
+
+/*
+ * Redefine TRUE and FALSE too, to catch current use. With this
+ * change, 'bool found = 1' will give a warning on MIPSPro, but
+ * 'bool found = TRUE' will not. Change tested on IRIX/MIPSPro,
+ * AIX 5.1/Xlc, Tru64 5.1/cc, w/make test too.
+ */
+
+#ifndef TRUE
+#define TRUE true
+#endif
+#ifndef FALSE
+#define FALSE false
+#endif
+
+
+
+/*
+** signed long to signed int
+*/
+
+int clamp_to_int(long slnum)
+{
+ return slnum > INT_MAX ? INT_MAX : slnum < INT_MIN ? INT_MIN : (int)slnum;
+}
+
+
+const char * const wkday[] =
+{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
+static const char * const weekday[] =
+{ "Monday", "Tuesday", "Wednesday", "Thursday",
+ "Friday", "Saturday", "Sunday" };
+const char * const month[]=
+{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+struct tzinfo {
+ char name[5];
+ int offset; /* +/- in minutes */
+};
+
+/*
+ * parsedate()
+ *
+ * Returns:
+ *
+ * PARSEDATE_OK - a fine conversion
+ * PARSEDATE_FAIL - failed to convert
+ * PARSEDATE_LATER - time overflow at the far end of time_t
+ * PARSEDATE_SOONER - time underflow at the low end of time_t
+ */
+
+static int parsedate(const char *date, time_t *output);
+
+#define PARSEDATE_OK 0
+#define PARSEDATE_FAIL -1
+#define PARSEDATE_LATER 1
+#define PARSEDATE_SOONER 2
+
+/* Here's a bunch of frequently used time zone names. These were supported
+ by the old getdate parser. */
+#define tDAYZONE -60 /* offset for daylight savings time */
+static const struct tzinfo tz[]= {
+ {"GMT", 0}, /* Greenwich Mean */
+ {"UTC", 0}, /* Universal (Coordinated) */
+ {"WET", 0}, /* Western European */
+ {"BST", 0 tDAYZONE}, /* British Summer */
+ {"WAT", 60}, /* West Africa */
+ {"AST", 240}, /* Atlantic Standard */
+ {"ADT", 240 tDAYZONE}, /* Atlantic Daylight */
+ {"EST", 300}, /* Eastern Standard */
+ {"EDT", 300 tDAYZONE}, /* Eastern Daylight */
+ {"CST", 360}, /* Central Standard */
+ {"CDT", 360 tDAYZONE}, /* Central Daylight */
+ {"MST", 420}, /* Mountain Standard */
+ {"MDT", 420 tDAYZONE}, /* Mountain Daylight */
+ {"PST", 480}, /* Pacific Standard */
+ {"PDT", 480 tDAYZONE}, /* Pacific Daylight */
+ {"YST", 540}, /* Yukon Standard */
+ {"YDT", 540 tDAYZONE}, /* Yukon Daylight */
+ {"HST", 600}, /* Hawaii Standard */
+ {"HDT", 600 tDAYZONE}, /* Hawaii Daylight */
+ {"CAT", 600}, /* Central Alaska */
+ {"AHST", 600}, /* Alaska-Hawaii Standard */
+ {"NT", 660}, /* Nome */
+ {"IDLW", 720}, /* International Date Line West */
+ {"CET", -60}, /* Central European */
+ {"MET", -60}, /* Middle European */
+ {"MEWT", -60}, /* Middle European Winter */
+ {"MEST", -60 tDAYZONE}, /* Middle European Summer */
+ {"CEST", -60 tDAYZONE}, /* Central European Summer */
+ {"MESZ", -60 tDAYZONE}, /* Middle European Summer */
+ {"FWT", -60}, /* French Winter */
+ {"FST", -60 tDAYZONE}, /* French Summer */
+ {"EET", -120}, /* Eastern Europe, USSR Zone 1 */
+ {"WAST", -420}, /* West Australian Standard */
+ {"WADT", -420 tDAYZONE}, /* West Australian Daylight */
+ {"CCT", -480}, /* China Coast, USSR Zone 7 */
+ {"JST", -540}, /* Japan Standard, USSR Zone 8 */
+ {"EAST", -600}, /* Eastern Australian Standard */
+ {"EADT", -600 tDAYZONE}, /* Eastern Australian Daylight */
+ {"GST", -600}, /* Guam Standard, USSR Zone 9 */
+ {"NZT", -720}, /* New Zealand */
+ {"NZST", -720}, /* New Zealand Standard */
+ {"NZDT", -720 tDAYZONE}, /* New Zealand Daylight */
+ {"IDLE", -720}, /* International Date Line East */
+ /* Next up: Military timezone names. RFC822 allowed these, but (as noted in
+ RFC 1123) had their signs wrong. Here we use the correct signs to match
+ actual military usage.
+ */
+ {"A", +1 * 60}, /* Alpha */
+ {"B", +2 * 60}, /* Bravo */
+ {"C", +3 * 60}, /* Charlie */
+ {"D", +4 * 60}, /* Delta */
+ {"E", +5 * 60}, /* Echo */
+ {"F", +6 * 60}, /* Foxtrot */
+ {"G", +7 * 60}, /* Golf */
+ {"H", +8 * 60}, /* Hotel */
+ {"I", +9 * 60}, /* India */
+ /* "J", Juliet is not used as a timezone, to indicate the observer's local
+ time */
+ {"K", +10 * 60}, /* Kilo */
+ {"L", +11 * 60}, /* Lima */
+ {"M", +12 * 60}, /* Mike */
+ {"N", -1 * 60}, /* November */
+ {"O", -2 * 60}, /* Oscar */
+ {"P", -3 * 60}, /* Papa */
+ {"Q", -4 * 60}, /* Quebec */
+ {"R", -5 * 60}, /* Romeo */
+ {"S", -6 * 60}, /* Sierra */
+ {"T", -7 * 60}, /* Tango */
+ {"U", -8 * 60}, /* Uniform */
+ {"V", -9 * 60}, /* Victor */
+ {"W", -10 * 60}, /* Whiskey */
+ {"X", -11 * 60}, /* X-ray */
+ {"Y", -12 * 60}, /* Yankee */
+ {"Z", 0}, /* Zulu, zero meridian, a.k.a. UTC */
+};
+
+/* returns:
+ -1 no day
+ 0 monday - 6 sunday
+*/
+
+static int checkday(const char *check, size_t len)
+{
+ int i;
+ const char * const *what;
+ bool found= FALSE;
+ if(len > 3)
+ what = &weekday[0];
+ else
+ what = &wkday[0];
+ for(i=0; i<7; i++) {
+ if(raw_equal(check, what[0])) {
+ found=TRUE;
+ break;
+ }
+ what++;
+ }
+ return found?i:-1;
+}
+
+static int checkmonth(const char *check)
+{
+ int i;
+ const char * const *what;
+ bool found= FALSE;
+
+ what = &month[0];
+ for(i=0; i<12; i++) {
+ if(raw_equal(check, what[0])) {
+ found=TRUE;
+ break;
+ }
+ what++;
+ }
+ return found?i:-1; /* return the offset or -1, no real offset is -1 */
+}
+
+/* return the time zone offset between GMT and the input one, in number
+ of seconds or -1 if the timezone wasn't found/legal */
+
+static int checktz(const char *check)
+{
+ unsigned int i;
+ const struct tzinfo *what;
+ bool found= FALSE;
+
+ what = tz;
+ for(i=0; i< sizeof(tz)/sizeof(tz[0]); i++) {
+ if(raw_equal(check, what->name)) {
+ found=TRUE;
+ break;
+ }
+ what++;
+ }
+ return found?what->offset*60:-1;
+}
+
+static void skip(const char **date)
+{
+ /* skip everything that aren't letters or digits */
+ while(**date && !ISALNUM(**date))
+ (*date)++;
+}
+
+enum assume {
+ DATE_MDAY,
+ DATE_YEAR,
+ DATE_TIME
+};
+
+/* this is a clone of 'struct tm' but with all fields we don't need or use
+ cut out */
+struct my_tm {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+};
+
+/* struct tm to time since epoch in GMT time zone.
+ * This is similar to the standard mktime function but for GMT only, and
+ * doesn't suffer from the various bugs and portability problems that
+ * some systems' implementations have.
+ */
+static time_t my_timegm(struct my_tm *tm)
+{
+ static const int month_days_cumulative [12] =
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+ int month, year, leap_days;
+
+ if(tm->tm_year < 70)
+ /* we don't support years before 1970 as they will cause this function
+ to return a negative value */
+ return -1;
+
+ year = tm->tm_year + 1900;
+ month = tm->tm_mon;
+ if(month < 0) {
+ year += (11 - month) / 12;
+ month = 11 - (11 - month) % 12;
+ }
+ else if(month >= 12) {
+ year -= month / 12;
+ month = month % 12;
+ }
+
+ leap_days = year - (tm->tm_mon <= 1);
+ leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400)
+ - (1969 / 4) + (1969 / 100) - (1969 / 400));
+
+ return ((((time_t) (year - 1970) * 365
+ + leap_days + month_days_cumulative [month] + tm->tm_mday - 1) * 24
+ + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;
+}
+
+/*
+ * parsedate()
+ *
+ * Returns:
+ *
+ * PARSEDATE_OK - a fine conversion
+ * PARSEDATE_FAIL - failed to convert
+ * PARSEDATE_LATER - time overflow at the far end of time_t
+ * PARSEDATE_SOONER - time underflow at the low end of time_t
+ */
+
+static int parsedate(const char *date, time_t *output)
+{
+ time_t t = 0;
+ int wdaynum=-1; /* day of the week number, 0-6 (mon-sun) */
+ int monnum=-1; /* month of the year number, 0-11 */
+ int mdaynum=-1; /* day of month, 1 - 31 */
+ int hournum=-1;
+ int minnum=-1;
+ int secnum=-1;
+ int yearnum=-1;
+ int tzoff=-1;
+ struct my_tm tm;
+ enum assume dignext = DATE_MDAY;
+ const char *indate = date; /* save the original pointer */
+ int part = 0; /* max 6 parts */
+
+ while(*date && (part < 6)) {
+ bool found=FALSE;
+
+ skip(&date);
+
+ if(ISALPHA(*date)) {
+ /* a name coming up */
+ char buf[32]="";
+ size_t len;
+ if(sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz]", buf))
+ len = strlen(buf);
+ else
+ len = 0;
+
+ if(wdaynum == -1) {
+ wdaynum = checkday(buf, len);
+ if(wdaynum != -1)
+ found = TRUE;
+ }
+ if(!found && (monnum == -1)) {
+ monnum = checkmonth(buf);
+ if(monnum != -1)
+ found = TRUE;
+ }
+
+ if(!found && (tzoff == -1)) {
+ /* this just must be a time zone string */
+ tzoff = checktz(buf);
+ if(tzoff != -1)
+ found = TRUE;
+ }
+
+ if(!found)
+ return PARSEDATE_FAIL; /* bad string */
+
+ date += len;
+ }
+ else if(ISDIGIT(*date)) {
+ /* a digit */
+ int val;
+ char *end;
+ if((secnum == -1) &&
+ (3 == sscanf(date, "%02d:%02d:%02d", &hournum, &minnum, &secnum))) {
+ /* time stamp! */
+ date += 8;
+ }
+ else if((secnum == -1) &&
+ (2 == sscanf(date, "%02d:%02d", &hournum, &minnum))) {
+ /* time stamp without seconds */
+ date += 5;
+ secnum = 0;
+ }
+ else {
+ long lval;
+ int error;
+ int old_errno;
+
+ old_errno = ERRNO;
+ SET_ERRNO(0);
+ lval = strtol(date, &end, 10);
+ error = ERRNO;
+ if(error != old_errno)
+ SET_ERRNO(old_errno);
+
+ if(error)
+ return PARSEDATE_FAIL;
+
+#if LONG_MAX != INT_MAX
+ if((lval > (long)INT_MAX) || (lval < (long)INT_MIN))
+ return PARSEDATE_FAIL;
+#endif
+
+ val = clamp_to_int(lval);
+
+ if((tzoff == -1) &&
+ ((end - date) == 4) &&
+ (val <= 1400) &&
+ (indate< date) &&
+ ((date[-1] == '+' || date[-1] == '-'))) {
+ /* four digits and a value less than or equal to 1400 (to take into
+ account all sorts of funny time zone diffs) and it is preceded
+ with a plus or minus. This is a time zone indication. 1400 is
+ picked since +1300 is frequently used and +1400 is mentioned as
+ an edge number in the document "ISO C 200X Proposal: Timezone
+ Functions" at http://david.tribble.com/text/c0xtimezone.html If
+ anyone has a more authoritative source for the exact maximum time
+ zone offsets, please speak up! */
+ found = TRUE;
+ tzoff = (val/100 * 60 + val%100)*60;
+
+ /* the + and - prefix indicates the local time compared to GMT,
+ this we need ther reversed math to get what we want */
+ tzoff = date[-1]=='+'?-tzoff:tzoff;
+ }
+
+ if(((end - date) == 8) &&
+ (yearnum == -1) &&
+ (monnum == -1) &&
+ (mdaynum == -1)) {
+ /* 8 digits, no year, month or day yet. This is YYYYMMDD */
+ found = TRUE;
+ yearnum = val/10000;
+ monnum = (val%10000)/100-1; /* month is 0 - 11 */
+ mdaynum = val%100;
+ }
+
+ if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) {
+ if((val > 0) && (val<32)) {
+ mdaynum = val;
+ found = TRUE;
+ }
+ dignext = DATE_YEAR;
+ }
+
+ if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) {
+ yearnum = val;
+ found = TRUE;
+ if(yearnum < 1900) {
+ if(yearnum > 70)
+ yearnum += 1900;
+ else
+ yearnum += 2000;
+ }
+ if(mdaynum == -1)
+ dignext = DATE_MDAY;
+ }
+
+ if(!found)
+ return PARSEDATE_FAIL;
+
+ date = end;
+ }
+ }
+
+ part++;
+ }
+
+ if(-1 == secnum)
+ secnum = minnum = hournum = 0; /* no time, make it zero */
+
+ if((-1 == mdaynum) ||
+ (-1 == monnum) ||
+ (-1 == yearnum))
+ /* lacks vital info, fail */
+ return PARSEDATE_FAIL;
+
+#if SIZEOF_TIME_T < 5
+ /* 32 bit time_t can only hold dates to the beginning of 2038 */
+ if(yearnum > 2037) {
+ *output = 0x7fffffff;
+ return PARSEDATE_LATER;
+ }
+#endif
+
+ if(yearnum < 1970) {
+ *output = 0;
+ return PARSEDATE_SOONER;
+ }
+
+ if((mdaynum > 31) || (monnum > 11) ||
+ (hournum > 23) || (minnum > 59) || (secnum > 60))
+ return PARSEDATE_FAIL; /* clearly an illegal date */
+
+ tm.tm_sec = secnum;
+ tm.tm_min = minnum;
+ tm.tm_hour = hournum;
+ tm.tm_mday = mdaynum;
+ tm.tm_mon = monnum;
+ tm.tm_year = yearnum - 1900;
+
+ /* my_timegm() returns a time_t. time_t is often 32 bits, even on many
+ architectures that feature 64 bit 'long'.
+
+ Some systems have 64 bit time_t and deal with years beyond 2038. However,
+ even on some of the systems with 64 bit time_t mktime() returns -1 for
+ dates beyond 03:14:07 UTC, January 19, 2038. (Such as AIX 5100-06)
+ */
+ t = my_timegm(&tm);
+
+ /* time zone adjust (cast t to int to compare to negative one) */
+ if(-1 != (int)t) {
+
+ /* Add the time zone diff between local time zone and GMT. */
+ long delta = (long)(tzoff!=-1?tzoff:0);
+
+ if((delta>0) && (t > LONG_MAX - delta))
+ return -1; /* time_t overflow */
+
+ t += delta;
+ }
+
+ *output = t;
+
+ return PARSEDATE_OK;
+}
+
+time_t parse_date(const char *p)
+{
+ time_t parsed;
+ int rc = parsedate(p, &parsed);
+
+ switch(rc) {
+ case PARSEDATE_OK:
+ case PARSEDATE_LATER:
+ case PARSEDATE_SOONER:
+ return parsed;
+ }
+ /* everything else is fail */
+ return -1;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/mbgl/util/pbf.hpp b/src/mbgl/util/pbf.hpp
new file mode 100644
index 0000000000..d017219a52
--- /dev/null
+++ b/src/mbgl/util/pbf.hpp
@@ -0,0 +1,184 @@
+#ifndef MBGL_UTIL_PBF
+#define MBGL_UTIL_PBF
+
+/*
+ * Some parts are from upb - a minimalist implementation of protocol buffers.
+ *
+ * Copyright (c) 2008-2011 Google Inc. See LICENSE for details.
+ * Author: Josh Haberman <jhaberman@gmail.com>
+ */
+
+#include <string>
+#include <cstring>
+
+namespace mbgl {
+
+struct pbf {
+ struct exception : std::exception { const char *what() const noexcept { return "pbf exception"; } };
+ struct unterminated_varint_exception : exception { const char *what() const noexcept { return "pbf unterminated varint exception"; } };
+ struct varint_too_long_exception : exception { const char *what() const noexcept { return "pbf varint too long exception"; } };
+ struct unknown_field_type_exception : exception { const char *what() const noexcept { return "pbf unknown field type exception"; } };
+ struct end_of_buffer_exception : exception { const char *what() const noexcept { return "pbf end of buffer exception"; } };
+
+ inline pbf(const unsigned char *data, size_t length);
+ inline pbf();
+
+ inline operator bool() const;
+
+ inline bool next();
+ inline bool next(uint32_t tag);
+ template <typename T = uint32_t> inline T varint();
+ template <typename T = uint32_t> inline T svarint();
+
+ template <typename T = uint32_t, int bytes = 4> inline T fixed();
+ inline float float32();
+ inline double float64();
+
+ inline std::string string();
+ inline bool boolean();
+
+ inline pbf message();
+
+ inline void skip();
+ inline void skipValue(uint32_t val);
+ inline void skipBytes(uint32_t bytes);
+
+ const uint8_t *data = nullptr;
+ const uint8_t *end = nullptr;
+ uint32_t value = 0;
+ uint32_t tag = 0;
+};
+
+pbf::pbf(const unsigned char *data_, size_t length)
+ : data(data_),
+ end(data_ + length),
+ value(0),
+ tag(0) {
+}
+
+pbf::pbf()
+ : data(nullptr),
+ end(nullptr),
+ value(0),
+ tag(0) {
+}
+
+
+pbf::operator bool() const {
+ return data < end;
+}
+
+bool pbf::next() {
+ if (data < end) {
+ value = static_cast<uint32_t>(varint());
+ tag = value >> 3;
+ return true;
+ }
+ return false;
+}
+
+bool pbf::next(uint32_t requested_tag) {
+ while (next()) {
+ if (tag == requested_tag) {
+ return true;
+ } else {
+ skip();
+ }
+ }
+ return false;
+}
+
+template <typename T>
+T pbf::varint() {
+ uint8_t byte = 0x80;
+ T result = 0;
+ int bitpos;
+ for (bitpos = 0; bitpos < 70 && (byte & 0x80); bitpos += 7) {
+ if (data >= end) {
+ throw unterminated_varint_exception();
+ }
+ result |= ((T)(byte = *data) & 0x7F) << bitpos;
+
+ data++;
+ }
+ if (bitpos == 70 && (byte & 0x80)) {
+ throw varint_too_long_exception();
+ }
+
+ return result;
+}
+
+template <typename T>
+T pbf::svarint() {
+ T n = varint<T>();
+ return (n >> 1) ^ -(T)(n & 1);
+}
+
+template <typename T, int bytes>
+T pbf::fixed() {
+ skipBytes(bytes);
+ T result;
+ memcpy(&result, data - bytes, bytes);
+ return result;
+}
+
+float pbf::float32() {
+ return fixed<float, 4>();
+}
+
+double pbf::float64() {
+ return fixed<double, 8>();
+}
+
+std::string pbf::string() {
+ uint32_t bytes = static_cast<uint32_t>(varint());
+ const char *string_data = reinterpret_cast<const char*>(data);
+ skipBytes(bytes);
+ return std::string(string_data, bytes);
+}
+
+bool pbf::boolean() {
+ skipBytes(1);
+ return *(bool *)(data - 1);
+}
+
+pbf pbf::message() {
+ uint32_t bytes = static_cast<uint32_t>(varint());
+ const uint8_t *pos = data;
+ skipBytes(bytes);
+ return pbf(pos, bytes);
+}
+
+void pbf::skip() {
+ skipValue(value);
+}
+
+void pbf::skipValue(uint32_t val) {
+ switch (val & 0x7) {
+ case 0: // varint
+ varint();
+ break;
+ case 1: // 64 bit
+ skipBytes(8);
+ break;
+ case 2: // string/message
+ skipBytes(static_cast<uint32_t>(varint()));
+ break;
+ case 5: // 32 bit
+ skipBytes(4);
+ break;
+ default:
+ throw unknown_field_type_exception();
+ }
+}
+
+void pbf::skipBytes(uint32_t bytes) {
+ if (data + bytes > end) {
+ throw end_of_buffer_exception();
+ }
+ data += bytes;
+}
+
+} // end namespace mbgl
+
+#endif
diff --git a/src/mbgl/util/queue.h b/src/mbgl/util/queue.h
new file mode 100644
index 0000000000..fe02b454ea
--- /dev/null
+++ b/src/mbgl/util/queue.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef QUEUE_H_
+#define QUEUE_H_
+
+typedef void *QUEUE[2];
+
+/* Private macros. */
+#define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0]))
+#define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1]))
+#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q)))
+#define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q)))
+
+/* Public macros. */
+#define QUEUE_DATA(ptr, type, field) \
+ ((type *) ((char *) (ptr) - ((char *) &((type *) 0)->field)))
+
+#define QUEUE_FOREACH(q, h) \
+ for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q))
+
+#define QUEUE_EMPTY(q) \
+ ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q))
+
+#define QUEUE_HEAD(q) \
+ (QUEUE_NEXT(q))
+
+#define QUEUE_INIT(q) \
+ do { \
+ QUEUE_NEXT(q) = (q); \
+ QUEUE_PREV(q) = (q); \
+ } \
+ while (0)
+
+#define QUEUE_ADD(h, n) \
+ do { \
+ QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \
+ QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \
+ QUEUE_PREV(h) = QUEUE_PREV(n); \
+ QUEUE_PREV_NEXT(h) = (h); \
+ } \
+ while (0)
+
+#define QUEUE_SPLIT(h, q, n) \
+ do { \
+ QUEUE_PREV(n) = QUEUE_PREV(h); \
+ QUEUE_PREV_NEXT(n) = (n); \
+ QUEUE_NEXT(n) = (q); \
+ QUEUE_PREV(h) = QUEUE_PREV(q); \
+ QUEUE_PREV_NEXT(h) = (h); \
+ QUEUE_PREV(q) = (n); \
+ } \
+ while (0)
+
+#define QUEUE_INSERT_HEAD(h, q) \
+ do { \
+ QUEUE_NEXT(q) = QUEUE_NEXT(h); \
+ QUEUE_PREV(q) = (h); \
+ QUEUE_NEXT_PREV(q) = (q); \
+ QUEUE_NEXT(h) = (q); \
+ } \
+ while (0)
+
+#define QUEUE_INSERT_TAIL(h, q) \
+ do { \
+ QUEUE_NEXT(q) = (h); \
+ QUEUE_PREV(q) = QUEUE_PREV(h); \
+ QUEUE_PREV_NEXT(q) = (q); \
+ QUEUE_PREV(h) = (q); \
+ } \
+ while (0)
+
+#define QUEUE_REMOVE(q) \
+ do { \
+ QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \
+ QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \
+ } \
+ while (0)
+
+#endif /* QUEUE_H_ */
diff --git a/src/mbgl/util/raster.cpp b/src/mbgl/util/raster.cpp
new file mode 100644
index 0000000000..56461aec5f
--- /dev/null
+++ b/src/mbgl/util/raster.cpp
@@ -0,0 +1,106 @@
+#include <mbgl/platform/platform.hpp>
+#include <mbgl/platform/gl.hpp>
+
+#include <mbgl/util/raster.hpp>
+#include <mbgl/util/time.hpp>
+#include <mbgl/util/uv_detail.hpp>
+#include <mbgl/util/std.hpp>
+
+#include <cassert>
+#include <cstring>
+
+using namespace mbgl;
+
+Raster::Raster(TexturePool& texturePool_)
+ : texturePool(texturePool_)
+{}
+
+Raster::~Raster() {
+ if (textured) {
+ texturePool.removeTextureID(texture);
+ }
+}
+
+bool Raster::isLoaded() const {
+ std::lock_guard<std::mutex> lock(mtx);
+ return loaded;
+}
+
+bool Raster::load(const std::string &data) {
+ img = util::make_unique<util::Image>(data);
+ width = img->getWidth();
+ height = img->getHeight();
+
+ std::lock_guard<std::mutex> lock(mtx);
+ if (img->getData()) {
+ loaded = true;
+ }
+ return loaded;
+}
+
+
+void Raster::bind(bool linear) {
+ if (!width || !height) {
+ fprintf(stderr, "trying to bind texture without dimension\n");
+ return;
+ }
+
+ if (img && !textured) {
+ texture = texturePool.getTextureID();
+ glBindTexture(GL_TEXTURE_2D, texture);
+#ifndef GL_ES_VERSION_2_0
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+#endif
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img->getData());
+ img.reset();
+ textured = true;
+ } else if (textured) {
+ glBindTexture(GL_TEXTURE_2D, texture);
+ }
+
+ GLuint new_filter = linear ? GL_LINEAR : GL_NEAREST;
+ if (new_filter != this->filter) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, new_filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, new_filter);
+ filter = new_filter;
+ }
+}
+
+// overload ::bind for prerendered raster textures
+void Raster::bind(const GLuint custom_texture) {
+ if (img && !textured) {
+ glBindTexture(GL_TEXTURE_2D, custom_texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img->getData());
+ img.reset();
+ textured = true;
+ } else if (textured) {
+ glBindTexture(GL_TEXTURE_2D, custom_texture);
+ }
+
+ GLuint new_filter = GL_LINEAR;
+ if (new_filter != this->filter) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, new_filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, new_filter);
+ filter = new_filter;
+ }
+
+}
+
+void Raster::beginFadeInTransition() {
+ timestamp start = util::now();
+ fade_transition = std::make_shared<util::ease_transition<double>>(opacity, 1.0, opacity, start, 250_milliseconds);
+}
+
+bool Raster::needsTransition() const {
+ return fade_transition != nullptr;
+}
+
+void Raster::updateTransitions(timestamp now) {
+ if (fade_transition->update(now) == util::transition::complete) {
+ fade_transition = nullptr;
+ }
+}
diff --git a/src/mbgl/util/raster.hpp b/src/mbgl/util/raster.hpp
new file mode 100644
index 0000000000..ff27f509f4
--- /dev/null
+++ b/src/mbgl/util/raster.hpp
@@ -0,0 +1,74 @@
+#ifndef MBGL_UTIL_RASTER
+#define MBGL_UTIL_RASTER
+
+#include <mbgl/util/transition.hpp>
+#include <mbgl/util/texture_pool.hpp>
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/ptr.hpp>
+#include <mbgl/renderer/prerendered_texture.hpp>
+
+#include <string>
+#include <mutex>
+
+typedef struct uv_loop_s uv_loop_t;
+
+namespace mbgl {
+
+class Raster : public std::enable_shared_from_this<Raster> {
+
+public:
+ Raster(TexturePool&);
+ ~Raster();
+
+ // load image data
+ bool load(const std::string &img);
+
+ // bind current texture
+ void bind(bool linear = false);
+
+ // bind prerendered texture
+ void bind(const GLuint texture);
+
+ // loaded status
+ bool isLoaded() const;
+
+ // transitions
+ void beginFadeInTransition();
+ bool needsTransition() const;
+ void updateTransitions(timestamp now);
+
+public:
+ // loaded image dimensions
+ uint32_t width = 0, height = 0;
+
+ // has been uploaded to texture
+ bool textured = false;
+
+ // the uploaded texture
+ uint32_t texture = 0;
+
+ // texture opacity
+ double opacity = 0;
+
+private:
+ mutable std::mutex mtx;
+
+ // raw pixels have been loaded
+ bool loaded = false;
+
+ // shared texture pool
+ TexturePool& texturePool;
+
+ // min/mag filter
+ uint32_t filter = 0;
+
+ // the raw pixels
+ std::unique_ptr<util::Image> img;
+
+ // fade in transition
+ util::ptr<util::transition> fade_transition = nullptr;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/util/rect.hpp b/src/mbgl/util/rect.hpp
new file mode 100644
index 0000000000..f5c77f93d1
--- /dev/null
+++ b/src/mbgl/util/rect.hpp
@@ -0,0 +1,22 @@
+#ifndef MBGL_UTIL_RECT
+#define MBGL_UTIL_RECT
+
+namespace mbgl {
+
+template <typename T>
+struct Rect {
+ inline Rect() {}
+ inline Rect(T x_, T y_, T w_, T h_) : x(x_), y(y_), w(w_), h(h_) {}
+ T x = 0, y = 0;
+ T w = 0, h = 0;
+
+ template <typename Number>
+ Rect operator *(Number value) const {
+ return Rect(x * value, y * value, w * value, h * value);
+ }
+
+ operator bool() const { return w != 0 && h != 0; }
+};
+}
+
+#endif
diff --git a/src/mbgl/util/sqlite3.cpp b/src/mbgl/util/sqlite3.cpp
new file mode 100644
index 0000000000..787db83992
--- /dev/null
+++ b/src/mbgl/util/sqlite3.cpp
@@ -0,0 +1,165 @@
+#include <mbgl/util/sqlite3.hpp>
+#include <sqlite3.h>
+
+#include <cassert>
+
+namespace mapbox {
+namespace sqlite {
+
+Database::Database(const std::string &filename, int flags) {
+ const int err = sqlite3_open_v2(filename.c_str(), &db, flags, nullptr);
+ if (err != SQLITE_OK) {
+ Exception ex { err, sqlite3_errmsg(db) };
+ db = nullptr;
+ throw ex;
+ }
+}
+
+Database::Database(Database &&other)
+ : db(std::move(other.db)) {}
+
+Database &Database::operator=(Database &&other) {
+ std::swap(db, other.db);
+ return *this;
+}
+
+Database::~Database() {
+ if (db) {
+ const int err = sqlite3_close(db);
+ if (err != SQLITE_OK) {
+ throw Exception { err, sqlite3_errmsg(db) };
+ }
+ }
+}
+
+Database::operator bool() const {
+ return db != nullptr;
+}
+
+void Database::exec(const std::string &sql) {
+ assert(db);
+ char *msg = nullptr;
+ const int err = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &msg);
+ if (msg) {
+ Exception ex { err, msg };
+ sqlite3_free(msg);
+ throw ex;
+ } else if (err != SQLITE_OK) {
+ throw Exception { err, sqlite3_errmsg(db) };
+ }
+}
+
+Statement Database::prepare(const char *query) {
+ assert(db);
+ return std::move(Statement(db, query));
+}
+
+Statement::Statement(sqlite3 *db, const char *sql) {
+ const int err = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
+ if (err != SQLITE_OK) {
+ stmt = nullptr;
+ throw Exception { err, sqlite3_errmsg(db) };
+ }
+}
+
+#define CHECK_SQLITE_OK(err) \
+ if (err != SQLITE_OK) { \
+ throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt)) }; \
+ }
+
+Statement::Statement(Statement &&other) {
+ *this = std::move(other);
+}
+
+Statement &Statement::operator=(Statement &&other) {
+ std::swap(stmt, other.stmt);
+ return *this;
+}
+
+Statement::~Statement() {
+ if (stmt) {
+ const int err = sqlite3_finalize(stmt);
+ CHECK_SQLITE_OK(err)
+ }
+}
+
+Statement::operator bool() const {
+ return stmt != nullptr;
+}
+
+#define BIND_3(type, value) \
+ assert(stmt); \
+ const int err = sqlite3_bind_##type(stmt, offset, value); \
+ CHECK_SQLITE_OK(err)
+
+#define BIND_5(type, value, length, param) \
+ assert(stmt); \
+ const int err = sqlite3_bind_##type(stmt, offset, value, length, param); \
+ CHECK_SQLITE_OK(err)
+
+template <> void Statement::bind(int offset, int value) {
+ BIND_3(int, value)
+}
+
+template <> void Statement::bind(int offset, int64_t value) {
+ BIND_3(int64, value)
+}
+
+template <> void Statement::bind(int offset, double value) {
+ BIND_3(double, value)
+}
+
+template <> void Statement::bind(int offset, bool value) {
+ BIND_3(int, value)
+}
+
+template <> void Statement::bind(int offset, const char *value) {
+ BIND_5(text, value, -1, nullptr)
+}
+
+void Statement::bind(int offset, const std::string &value, bool retain) {
+ BIND_5(blob, value.data(), int(value.size()), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)
+}
+
+bool Statement::run() {
+ assert(stmt);
+ const int err = sqlite3_step(stmt);
+ if (err == SQLITE_DONE) {
+ return false;
+ } else if (err == SQLITE_ROW) {
+ return true;
+ } else {
+ throw std::runtime_error("failed to run statement");
+ }
+}
+
+template <> int Statement::get(int offset) {
+ assert(stmt);
+ return sqlite3_column_int(stmt, offset);
+}
+
+template <> int64_t Statement::get(int offset) {
+ assert(stmt);
+ return sqlite3_column_int64(stmt, offset);
+}
+
+template <> double Statement::get(int offset) {
+ assert(stmt);
+ return sqlite3_column_double(stmt, offset);
+}
+
+template <> std::string Statement::get(int offset) {
+ assert(stmt);
+ return {
+ reinterpret_cast<const char *>(sqlite3_column_blob(stmt, offset)),
+ size_t(sqlite3_column_bytes(stmt, offset))
+ };
+}
+
+void Statement::reset() {
+ assert(stmt);
+ sqlite3_reset(stmt);
+}
+
+}
+}
diff --git a/src/mbgl/util/sqlite3.hpp b/src/mbgl/util/sqlite3.hpp
new file mode 100644
index 0000000000..3e324f7ce1
--- /dev/null
+++ b/src/mbgl/util/sqlite3.hpp
@@ -0,0 +1,74 @@
+#pragma once
+
+#include <string>
+#include <stdexcept>
+
+typedef struct sqlite3 sqlite3;
+typedef struct sqlite3_stmt sqlite3_stmt;
+
+namespace mapbox {
+namespace sqlite {
+
+enum OpenFlag : int {
+ ReadOnly = 0x00000001,
+ ReadWrite = 0x00000002,
+ Create = 0x00000004,
+ NoMutex = 0x00008000,
+ FullMutex = 0x00010000,
+ SharedCache = 0x00020000,
+ PrivateCache = 0x00040000,
+};
+
+struct Exception : std::runtime_error {
+ inline Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {}
+ const int code = 0;
+};
+
+class Statement;
+
+class Database {
+private:
+ Database(const Database &) = delete;
+ Database &operator=(const Database &) = delete;
+
+public:
+ Database(const std::string &filename, int flags = 0);
+ Database(Database &&);
+ ~Database();
+ Database &operator=(Database &&);
+
+ operator bool() const;
+
+ void exec(const std::string &sql);
+ Statement prepare(const char *query);
+
+private:
+ sqlite3 *db = nullptr;
+};
+
+class Statement {
+private:
+ Statement(const Statement &) = delete;
+ Statement &operator=(const Statement &) = delete;
+
+public:
+ Statement(sqlite3 *db, const char *sql);
+ Statement(Statement &&);
+ ~Statement();
+ Statement &operator=(Statement &&);
+
+ operator bool() const;
+
+ template <typename T> void bind(int offset, T value);
+ void bind(int offset, const std::string &value, bool retain = true);
+ template <typename T> T get(int offset);
+
+ bool run();
+ void reset();
+
+private:
+ sqlite3_stmt *stmt = nullptr;
+};
+
+}
+}
diff --git a/src/mbgl/util/stopwatch.cpp b/src/mbgl/util/stopwatch.cpp
new file mode 100644
index 0000000000..14b4f4018b
--- /dev/null
+++ b/src/mbgl/util/stopwatch.cpp
@@ -0,0 +1,36 @@
+#ifndef DISABLE_STOPWATCH
+#include <mbgl/util/stopwatch.hpp>
+#include <mbgl/util/time.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/platform/log.hpp>
+
+#include <iostream>
+#include <atomic>
+
+using namespace mbgl::util;
+
+stopwatch::stopwatch(Event event_)
+ : event(event_), start(now()) {}
+
+stopwatch::stopwatch(EventSeverity severity_, Event event_)
+ : severity(severity_), event(event_), start(now()) {}
+
+stopwatch::stopwatch(const std::string &name_, Event event_)
+ : name(name_), event(event_), start(now()) {}
+
+stopwatch::stopwatch(const std::string &name_, EventSeverity severity_, Event event_)
+ : name(name_), severity(severity_), event(event_), start(now()) {}
+
+void stopwatch::report(const std::string &name_) {
+ timestamp duration = now() - start;
+
+ Log::Record(severity, event, name_ + ": " + util::toString(double(duration) / 1_millisecond) + "ms");
+ start += duration;
+}
+
+stopwatch::~stopwatch() {
+ if (name.size()) {
+ report(name);
+ }
+}
+#endif
diff --git a/src/mbgl/util/stopwatch.hpp b/src/mbgl/util/stopwatch.hpp
new file mode 100644
index 0000000000..663bbb6fc7
--- /dev/null
+++ b/src/mbgl/util/stopwatch.hpp
@@ -0,0 +1,40 @@
+#ifndef MBGL_UTIL_STOPWATCH
+#define MBGL_UTIL_STOPWATCH
+
+#include <mbgl/platform/event.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace util {
+
+#ifndef DISABLE_STOPWATCH
+class stopwatch {
+public:
+ stopwatch(Event event = Event::General);
+ stopwatch(EventSeverity severity, Event event = Event::General);
+ stopwatch(const std::string &name, Event event = Event::General);
+ stopwatch(const std::string &name, EventSeverity severity, Event event = Event::General);
+ void report(const std::string &name);
+ ~stopwatch();
+
+private:
+ const std::string name;
+ EventSeverity severity = EventSeverity::Debug;
+ Event event = Event::General;
+ uint64_t start;
+};
+#else
+class stopwatch {
+ inline stopwatch(Event event = Event::General);
+ inline stopwatch(EventSeverity severity, Event event = Event::General);
+ inline stopwatch(const std::string &name, Event event = Event::General);
+ inline stopwatch(const std::string &name, EventSeverity severity, Event event = Event::General);
+ inline void report(const std::string &name) {}
+ inline ~stopwatch() {}
+};
+#endif
+}
+}
+
+#endif
diff --git a/src/mbgl/util/texture_pool.cpp b/src/mbgl/util/texture_pool.cpp
new file mode 100644
index 0000000000..9c8c24b085
--- /dev/null
+++ b/src/mbgl/util/texture_pool.cpp
@@ -0,0 +1,58 @@
+#include <mbgl/util/texture_pool.hpp>
+
+#include <vector>
+
+const int TextureMax = 64;
+
+using namespace mbgl;
+
+GLuint TexturePool::getTextureID() {
+ if (texture_ids.empty()) {
+ GLuint new_texture_ids[TextureMax];
+ glGenTextures(TextureMax, new_texture_ids);
+ for (uint32_t id = 0; id < TextureMax; id++) {
+ texture_ids.insert(new_texture_ids[id]);
+ }
+ }
+
+ GLuint id = 0;
+
+ if (!texture_ids.empty()) {
+ std::set<GLuint>::iterator id_iterator = texture_ids.begin();
+ id = *id_iterator;
+ texture_ids.erase(id_iterator);
+ }
+
+ return id;
+}
+
+void TexturePool::removeTextureID(GLuint texture_id) {
+ bool needs_clear = false;
+
+ texture_ids.insert(texture_id);
+
+ if (texture_ids.size() > TextureMax) {
+ needs_clear = true;
+ }
+
+ if (needs_clear) {
+ // TODO: We need to find a better way to deal with texture pool cleanup
+// clearTextureIDs();
+ }
+}
+
+void TexturePool::clearTextureIDs() {
+ std::vector<GLuint> ids_to_remove;
+ ids_to_remove.reserve(texture_ids.size());
+
+ for (std::set<GLuint>::iterator id_iterator = texture_ids.begin();
+ id_iterator != texture_ids.end(); ++id_iterator) {
+ ids_to_remove.push_back(*id_iterator);
+ }
+
+ if (!ids_to_remove.empty()) {
+ glDeleteTextures((GLsizei)ids_to_remove.size(), &ids_to_remove[0]);
+ }
+
+ texture_ids.clear();
+}
diff --git a/src/mbgl/util/texture_pool.hpp b/src/mbgl/util/texture_pool.hpp
new file mode 100644
index 0000000000..95d918c237
--- /dev/null
+++ b/src/mbgl/util/texture_pool.hpp
@@ -0,0 +1,25 @@
+#ifndef MBGL_UTIL_TEXTUREPOOL
+#define MBGL_UTIL_TEXTUREPOOL
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/platform/gl.hpp>
+
+#include <set>
+#include <mutex>
+
+namespace mbgl {
+
+class TexturePool : private util::noncopyable {
+
+public:
+ GLuint getTextureID();
+ void removeTextureID(GLuint texture_id);
+ void clearTextureIDs();
+
+private:
+ std::set<GLuint> texture_ids;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/util/time.cpp b/src/mbgl/util/time.cpp
new file mode 100644
index 0000000000..1953975b18
--- /dev/null
+++ b/src/mbgl/util/time.cpp
@@ -0,0 +1,25 @@
+#include <mbgl/util/time.hpp>
+#include <mbgl/util/uv_detail.hpp>
+
+namespace mbgl {
+namespace util {
+
+timestamp now() {
+ return uv_hrtime();
+}
+
+static const char *week[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+std::string rfc1123(std::time_t time) {
+ std::tm info;
+ gmtime_r(&time, &info);
+ char buffer[30];
+ snprintf(buffer, 30, "%s, %02d %s %4d %02d:%02d:%02d GMT", week[info.tm_wday], info.tm_mday,
+ months[info.tm_mon], 1900 + info.tm_year, info.tm_hour, info.tm_min, info.tm_sec);
+ return buffer;
+}
+
+}
+}
diff --git a/src/mbgl/util/token.hpp b/src/mbgl/util/token.hpp
new file mode 100644
index 0000000000..64192a99f9
--- /dev/null
+++ b/src/mbgl/util/token.hpp
@@ -0,0 +1,50 @@
+#ifndef MBGL_UTIL_TOKEN
+#define MBGL_UTIL_TOKEN
+
+#include <map>
+#include <string>
+#include <algorithm>
+
+namespace mbgl {
+namespace util {
+
+// Replaces {tokens} in a string by calling the lookup function.
+template <typename Lookup>
+std::string replaceTokens(const std::string &source, const Lookup &lookup) {
+ std::string result;
+ result.reserve(source.size());
+
+ auto pos = source.begin();
+ const auto end = source.end();
+
+ while (pos != end) {
+ auto brace = std::find(pos, end, '{');
+ result.append(pos, brace);
+ pos = brace;
+ if (pos != end) {
+ for (brace++; brace != end && (std::isalnum(*brace) || *brace == '_'); brace++);
+ if (brace != end && *brace == '}') {
+ result.append(lookup({ pos + 1, brace }));
+ pos = brace + 1;
+ } else {
+ result.append(pos, brace);
+ pos = brace;
+ }
+ }
+ }
+
+ return result;
+}
+
+template <typename T>
+inline std::string replaceTokens(const std::string &source, const std::map<std::string, T> &properties) {
+ return replaceTokens(source, [&properties](const std::string &token) -> std::string {
+ const auto it_prop = properties.find(token);
+ return it_prop != properties.end() ? toString(it_prop->second) : "";
+ });
+}
+
+} // end namespace util
+} // end namespace mbgl
+
+#endif
diff --git a/src/mbgl/util/transition.cpp b/src/mbgl/util/transition.cpp
new file mode 100644
index 0000000000..e63a5c311f
--- /dev/null
+++ b/src/mbgl/util/transition.cpp
@@ -0,0 +1,26 @@
+#include <mbgl/util/transition.hpp>
+#include <mbgl/util/unitbezier.hpp>
+#include <mbgl/util/interpolate.hpp>
+#include <mbgl/platform/platform.hpp>
+
+namespace mbgl { namespace util {
+
+UnitBezier ease(0, 0, 0.25, 1);
+
+transition::~transition() {}
+
+template <typename T>
+transition::state ease_transition<T>::update(timestamp now) const {
+ float t = progress(now);
+ if (t >= 1) {
+ value = to;
+ return complete;
+ } else {
+ value = interpolate(from, to, ease.solve(t, 0.001));
+ return running;
+ }
+}
+
+template class ease_transition<double>;
+
+}}
diff --git a/src/mbgl/util/transition.hpp b/src/mbgl/util/transition.hpp
new file mode 100644
index 0000000000..d6bbc9eba0
--- /dev/null
+++ b/src/mbgl/util/transition.hpp
@@ -0,0 +1,77 @@
+#ifndef MBGL_UTIL_TRANSITION
+#define MBGL_UTIL_TRANSITION
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/time.hpp>
+
+namespace mbgl {
+namespace util {
+
+class transition : private noncopyable {
+public:
+ enum state {
+ running,
+ complete
+ };
+
+ inline transition(timestamp start_, timestamp duration_)
+ : start(start_),
+ duration(duration_) {}
+
+ inline float progress(timestamp now) const {
+ if (duration == 0) return 1;
+ if (start > now) return 0;
+
+ return (float)(now - start) / duration;
+ }
+
+ virtual state update(timestamp now) const = 0;
+ virtual ~transition();
+
+protected:
+ const timestamp start, duration;
+};
+
+template <typename T>
+class ease_transition : public transition {
+public:
+ ease_transition(T from_, T to_, T& value_, timestamp start_, timestamp duration_)
+ : transition(start_, duration_),
+ from(from_),
+ to(to_),
+ value(value_) {}
+
+ state update(timestamp now) const;
+
+private:
+ const T from, to;
+ T& value;
+
+};
+
+template <typename T>
+class timeout : public transition {
+public:
+ timeout(T final_value_, T& value_, timestamp start_, timestamp duration_)
+ : transition(start_, duration_),
+ final_value(final_value_),
+ value(value_) {}
+
+ state update(timestamp now) const {
+ if (progress(now) >= 1) {
+ value = final_value;
+ return complete;
+ } else {
+ return running;
+ }
+ }
+
+private:
+ const T final_value;
+ T& value;
+};
+
+}
+}
+
+#endif
diff --git a/src/mbgl/util/unitbezier.hpp b/src/mbgl/util/unitbezier.hpp
new file mode 100644
index 0000000000..095e15f809
--- /dev/null
+++ b/src/mbgl/util/unitbezier.hpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MBGL_UTIL_UNITBEZIER
+#define MBGL_UTIL_UNITBEZIER
+
+#include <cmath>
+
+namespace mbgl {
+namespace util {
+
+struct UnitBezier {
+ UnitBezier(double p1x, double p1y, double p2x, double p2y) {
+ // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
+ cx = 3.0 * p1x;
+ bx = 3.0 * (p2x - p1x) - cx;
+ ax = 1.0 - cx - bx;
+
+ cy = 3.0 * p1y;
+ by = 3.0 * (p2y - p1y) - cy;
+ ay = 1.0 - cy - by;
+ }
+
+ double sampleCurveX(double t) {
+ // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
+ return ((ax * t + bx) * t + cx) * t;
+ }
+
+ double sampleCurveY(double t) {
+ return ((ay * t + by) * t + cy) * t;
+ }
+
+ double sampleCurveDerivativeX(double t) {
+ return (3.0 * ax * t + 2.0 * bx) * t + cx;
+ }
+
+ // Given an x value, find a parametric value it came from.
+ double solveCurveX(double x, double epsilon) {
+ double t0;
+ double t1;
+ double t2;
+ double x2;
+ double d2;
+ int i;
+
+ // First try a few iterations of Newton's method -- normally very fast.
+ for (t2 = x, i = 0; i < 8; ++i) {
+ x2 = sampleCurveX(t2) - x;
+ if (fabs (x2) < epsilon)
+ return t2;
+ d2 = sampleCurveDerivativeX(t2);
+ if (fabs(d2) < 1e-6)
+ break;
+ t2 = t2 - x2 / d2;
+ }
+
+ // Fall back to the bisection method for reliability.
+ t0 = 0.0;
+ t1 = 1.0;
+ t2 = x;
+
+ if (t2 < t0)
+ return t0;
+ if (t2 > t1)
+ return t1;
+
+ while (t0 < t1) {
+ x2 = sampleCurveX(t2);
+ if (fabs(x2 - x) < epsilon)
+ return t2;
+ if (x > x2)
+ t0 = t2;
+ else
+ t1 = t2;
+ t2 = (t1 - t0) * .5 + t0;
+ }
+
+ // Failure.
+ return t2;
+ }
+
+ double solve(double x, double epsilon) {
+ return sampleCurveY(solveCurveX(x, epsilon));
+ }
+
+private:
+ double ax;
+ double bx;
+ double cx;
+
+ double ay;
+ double by;
+ double cy;
+};
+
+}
+}
+
+#endif
diff --git a/src/mbgl/util/url.cpp b/src/mbgl/util/url.cpp
new file mode 100644
index 0000000000..e9b9672109
--- /dev/null
+++ b/src/mbgl/util/url.cpp
@@ -0,0 +1,51 @@
+#include <mbgl/util/url.hpp>
+
+#include <cctype>
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <cstdlib>
+#include <algorithm>
+
+namespace mbgl {
+namespace util {
+
+std::string percentEncode(const std::string& input) {
+ std::ostringstream encoded;
+
+ encoded.fill('0');
+ encoded << std::hex;
+
+ for (auto c : input) {
+ if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
+ encoded << c;
+ } else {
+ encoded << '%' << std::setw(2) << int(c);
+ }
+ }
+
+ return encoded.str();
+}
+
+std::string percentDecode(const std::string& input) {
+ std::string decoded;
+
+ auto it = input.begin();
+ const auto end = input.end();
+ char hex[3] = "00";
+
+ while (it != end) {
+ auto cur = std::find(it, end, '%');
+ decoded.append(it, cur);
+ it = cur;
+ if (cur != end) {
+ it += input.copy(hex, 2, cur - input.begin() + 1) + 1;
+ decoded += static_cast<char>(std::strtoul(hex, nullptr, 16));
+ }
+ }
+
+ return decoded;
+}
+
+}
+}
diff --git a/src/mbgl/util/url.hpp b/src/mbgl/util/url.hpp
new file mode 100644
index 0000000000..a7e5291ec5
--- /dev/null
+++ b/src/mbgl/util/url.hpp
@@ -0,0 +1,15 @@
+#ifndef MBGL_UTIL_URL
+#define MBGL_UTIL_URL
+
+#include <string>
+
+namespace mbgl {
+namespace util {
+
+std::string percentEncode(const std::string&);
+std::string percentDecode(const std::string&);
+
+}
+}
+
+#endif
diff --git a/src/mbgl/util/utf.hpp b/src/mbgl/util/utf.hpp
new file mode 100644
index 0000000000..d6ba2a1f2f
--- /dev/null
+++ b/src/mbgl/util/utf.hpp
@@ -0,0 +1,24 @@
+#ifndef MBGL_UTIL_UTF
+#define MBGL_UTIL_UTF
+
+#include <memory>
+
+#include <boost/regex/pending/unicode_iterator.hpp>
+
+namespace mbgl {
+
+namespace util {
+
+class utf8_to_utf32 {
+ public:
+ static std::u32string convert(std::string const& utf8)
+ {
+ boost::u8_to_u32_iterator<std::string::const_iterator> begin(utf8.begin());
+ boost::u8_to_u32_iterator<std::string::const_iterator> end(utf8.end());
+ return std::u32string(begin,end);
+ }
+};
+
+}}
+
+#endif
diff --git a/src/mbgl/util/uv-channel.c b/src/mbgl/util/uv-channel.c
new file mode 100644
index 0000000000..4e3b9fa5ff
--- /dev/null
+++ b/src/mbgl/util/uv-channel.c
@@ -0,0 +1,69 @@
+#include <mbgl/util/uv-channel.h>
+#include <mbgl/util/queue.h>
+
+#include <stdlib.h>
+
+// Taken from http://navaneeth.github.io/blog/2013/08/02/channels-in-libuv/
+
+typedef struct {
+ void *data;
+ void *active_queue[2];
+} uv__chan_item_t;
+
+int uv_chan_init(uv_chan_t *chan) {
+ int r = uv_mutex_init(&chan->mutex);
+ if (r == -1)
+ return r;
+
+ QUEUE_INIT(&chan->q);
+
+ return uv_cond_init(&chan->cond);
+}
+
+void uv_chan_send(uv_chan_t *chan, void *data) {
+ uv__chan_item_t *item = (uv__chan_item_t *)malloc(sizeof(uv__chan_item_t));
+ item->data = data;
+
+ uv_mutex_lock(&chan->mutex);
+ QUEUE_INSERT_TAIL(&chan->q, &item->active_queue);
+ uv_cond_signal(&chan->cond);
+ uv_mutex_unlock(&chan->mutex);
+}
+
+void *uv_chan_receive(uv_chan_t *chan) {
+ uv__chan_item_t *item;
+ QUEUE *head;
+ void *data = NULL;
+
+ uv_mutex_lock(&chan->mutex);
+ while (QUEUE_EMPTY(&chan->q)) {
+ uv_cond_wait(&chan->cond, &chan->mutex);
+ }
+
+ head = QUEUE_HEAD(&chan->q);
+ item = QUEUE_DATA(head, uv__chan_item_t, active_queue);
+ data = item->data;
+ QUEUE_REMOVE(head);
+ free(item);
+ uv_mutex_unlock(&chan->mutex);
+ return data;
+}
+
+void uv_chan_clear(uv_chan_t *chan) {
+ uv_mutex_lock(&chan->mutex);
+ uv__chan_item_t *item = NULL;
+ QUEUE *head = NULL;
+ while (!QUEUE_EMPTY(&chan->q)) {
+ head = QUEUE_HEAD(&chan->q);
+ item = QUEUE_DATA(head, uv__chan_item_t, active_queue);
+ QUEUE_REMOVE(head);
+ free(item);
+ }
+ uv_mutex_unlock(&chan->mutex);
+}
+
+void uv_chan_destroy(uv_chan_t *chan) {
+ uv_chan_clear(chan);
+ uv_cond_destroy(&chan->cond);
+ uv_mutex_destroy(&chan->mutex);
+}
diff --git a/src/mbgl/util/uv-channel.h b/src/mbgl/util/uv-channel.h
new file mode 100644
index 0000000000..ea5c279f65
--- /dev/null
+++ b/src/mbgl/util/uv-channel.h
@@ -0,0 +1,29 @@
+#ifndef MBGL_UTIL_UV_CHANNEL
+#define MBGL_UTIL_UV_CHANNEL
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <uv.h>
+
+// Taken from http://navaneeth.github.io/blog/2013/08/02/channels-in-libuv/
+
+typedef struct uv_chan_s uv_chan_t;
+
+struct uv_chan_s {
+ uv_mutex_t mutex;
+ uv_cond_t cond;
+ void *q[2];
+};
+
+int uv_chan_init(uv_chan_t *chan);
+void uv_chan_send(uv_chan_t *chan, void *data);
+void *uv_chan_receive(uv_chan_t *chan);
+void uv_chan_destroy(uv_chan_t *chan);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/mbgl/util/uv-messenger.c b/src/mbgl/util/uv-messenger.c
new file mode 100644
index 0000000000..935b6f1c41
--- /dev/null
+++ b/src/mbgl/util/uv-messenger.c
@@ -0,0 +1,86 @@
+#include <mbgl/util/uv-messenger.h>
+#include <mbgl/util/queue.h>
+
+#include <stdlib.h>
+#include <assert.h>
+
+typedef struct {
+ void *data;
+ void *queue[2];
+} uv__messenger_item_t;
+
+#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+void uv__messenger_callback(uv_async_t *async, int status) {
+#pragma clang diagnostic pop
+#else
+void uv__messenger_callback(uv_async_t *async) {
+#endif
+ uv_messenger_t *msgr = (uv_messenger_t *)async->data;
+
+ uv__messenger_item_t *item;
+ QUEUE *head;
+
+ while (1) {
+ uv_mutex_lock(&msgr->mutex);
+ if (QUEUE_EMPTY(&msgr->queue)) {
+ uv_mutex_unlock(&msgr->mutex);
+ break;
+ }
+
+ head = QUEUE_HEAD(&msgr->queue);
+ item = QUEUE_DATA(head, uv__messenger_item_t, queue);
+ QUEUE_REMOVE(head);
+ uv_mutex_unlock(&msgr->mutex);
+
+ msgr->callback(item->data);
+
+ free(item);
+ }
+}
+
+int uv_messenger_init(uv_loop_t *loop, uv_messenger_t *msgr, uv_messenger_cb callback) {
+ int ret = uv_mutex_init(&msgr->mutex);
+ if (ret < 0) {
+ return ret;
+ }
+
+ msgr->callback = callback;
+ msgr->stop_callback = NULL;
+
+ QUEUE_INIT(&msgr->queue);
+
+ msgr->async.data = msgr;
+ return uv_async_init(loop, &msgr->async, uv__messenger_callback);
+}
+
+void uv_messenger_send(uv_messenger_t *msgr, void *data) {
+ uv__messenger_item_t *item = (uv__messenger_item_t *)malloc(sizeof(uv__messenger_item_t));
+ item->data = data;
+
+ uv_mutex_lock(&msgr->mutex);
+ QUEUE_INSERT_TAIL(&msgr->queue, &item->queue);
+ uv_mutex_unlock(&msgr->mutex);
+
+ uv_async_send(&msgr->async);
+}
+
+void uv_messenger_ref(uv_messenger_t *msgr) {
+ uv_ref((uv_handle_t *)&msgr->async);
+}
+
+void uv_messenger_unref(uv_messenger_t *msgr) {
+ uv_unref((uv_handle_t *)&msgr->async);
+}
+
+void uv__messenger_stop_callback(uv_handle_t *handle) {
+ uv_messenger_t *msgr = (uv_messenger_t *)handle->data;
+ msgr->stop_callback(msgr);
+}
+
+void uv_messenger_stop(uv_messenger_t *msgr, uv_messenger_stop_cb stop_callback) {
+ assert(!msgr->stop_callback);
+ msgr->stop_callback = stop_callback;
+ uv_close((uv_handle_t *)&msgr->async, uv__messenger_stop_callback);
+}
diff --git a/src/mbgl/util/uv-worker.c b/src/mbgl/util/uv-worker.c
new file mode 100644
index 0000000000..d2aa908019
--- /dev/null
+++ b/src/mbgl/util/uv-worker.c
@@ -0,0 +1,170 @@
+#include <mbgl/util/uv-worker.h>
+#include <mbgl/util/uv-messenger.h>
+
+#include <stdio.h>
+#include <assert.h>
+
+typedef struct uv__worker_item_s uv__worker_item_t;
+struct uv__worker_item_s {
+ uv_worker_t *worker;
+ void *data;
+ uv_worker_cb work_cb;
+ uv_worker_after_cb after_work_cb;
+};
+
+typedef struct uv__worker_thread_s uv__worker_thread_t;
+struct uv__worker_thread_s {
+ uv_worker_t *worker;
+ uv_thread_t thread;
+};
+
+void uv__worker_free_messenger(uv_messenger_t *msgr) {
+ free(msgr);
+}
+
+void uv__worker_thread_finished(uv__worker_thread_t *worker_thread) {
+ uv_worker_t *worker = worker_thread->worker;
+
+#ifndef NDEBUG
+ assert(uv_thread_self() == worker->thread_id);
+#endif
+
+ // This should at most block very briefly. We are sending the termination
+ // notification as the last thing in the worker thread, so by now the thread
+ // has probably terminated already. If not, the waiting time should be
+ // extremely short.
+ uv_thread_join(&worker_thread->thread);
+
+ assert(worker->count > 0);
+ worker->count--;
+ if (worker->count == 0) {
+ uv_chan_destroy(&worker->chan);
+ uv_messenger_stop(worker->msgr, uv__worker_free_messenger);
+ if (worker->close_cb) {
+ worker->close_cb(worker);
+ }
+ }
+}
+
+void uv__worker_after(void *ptr) {
+ uv__worker_item_t *item = (uv__worker_item_t *)ptr;
+
+ if (item->work_cb) {
+ // We are finishing a regular work request.
+ if (item->after_work_cb) {
+ assert(item->after_work_cb);
+ item->after_work_cb(item->data);
+ }
+ uv_worker_t *worker = item->worker;
+ assert(worker->active_items > 0);
+ if (--worker->active_items == 0) {
+ uv_messenger_unref(worker->msgr);
+ }
+ } else {
+ // This is a worker thread termination.
+ uv__worker_thread_t *worker_thread = (uv__worker_thread_t *)item->data;
+ uv__worker_thread_finished(worker_thread);
+ free(worker_thread);
+ }
+
+ free(item);
+}
+
+void uv__worker_thread_loop(void *ptr) {
+ uv__worker_thread_t *worker_thread = (uv__worker_thread_t *)ptr;
+ uv_worker_t *worker = worker_thread->worker;
+
+#ifdef __APPLE__
+ if (worker->name) {
+ pthread_setname_np(worker->name);
+ }
+#endif
+
+ uv__worker_item_t *item = NULL;
+ while ((item = (uv__worker_item_t *)uv_chan_receive(&worker->chan)) != NULL) {
+ assert(item->work_cb);
+ item->work_cb(item->data);
+
+ // Trigger the after callback in the main thread.
+ uv_messenger_send(worker->msgr, item);
+ }
+
+ // Make sure to close all other workers too.
+ uv_chan_send(&worker->chan, NULL);
+
+ // Create a new worker item that acts as a terminate flag for this thread.
+ item = (uv__worker_item_t *)malloc(sizeof(uv__worker_item_t));
+ item->data = worker_thread;
+ item->work_cb = NULL;
+ item->after_work_cb = NULL;
+ uv_messenger_send(worker->msgr, item);
+}
+
+int uv_worker_init(uv_worker_t *worker, uv_loop_t *loop, int count, const char *name) {
+#ifndef NDEBUG
+ worker->thread_id = uv_thread_self();
+#endif
+ worker->loop = loop;
+ worker->name = name;
+ worker->count = 0;
+ worker->close_cb = NULL;
+ worker->active_items = 0;
+ worker->msgr = (uv_messenger_t *)malloc(sizeof(uv_messenger_t));
+ int ret = uv_messenger_init(loop, worker->msgr, uv__worker_after);
+ if (ret < 0) {
+ free(worker->msgr);
+ return ret;
+ }
+ uv_messenger_unref(worker->msgr);
+ ret = uv_chan_init(&worker->chan);
+ if (ret < 0) return ret;
+
+ // Initialize all worker threads.
+ int i;
+ for (i = 0; i < count; i++) {
+ uv__worker_thread_t *worker_thread = (uv__worker_thread_t *)malloc(sizeof(uv__worker_thread_t));
+ worker_thread->worker = worker;
+ ret = uv_thread_create(&worker_thread->thread, uv__worker_thread_loop, worker_thread);
+ if (ret < 0) return ret;
+ worker->count++;
+ }
+
+ return 0;
+}
+
+void uv_worker_send(uv_worker_t *worker, void *data, uv_worker_cb work_cb,
+ uv_worker_after_cb after_work_cb) {
+#ifndef NDEBUG
+ assert(uv_thread_self() == worker->thread_id);
+#endif
+
+ // It doesn't make sense to not provide a work callback. On the other hand, the after_work_cb
+ // may be NULL. In that case, there will be no callback called in the current thread and the
+ // worker item will instead be freed in the worker thread.
+ assert(work_cb);
+
+ uv__worker_item_t *item = (uv__worker_item_t *)malloc(sizeof(uv__worker_item_t));
+ item->worker = worker;
+ item->work_cb = work_cb;
+ item->after_work_cb = after_work_cb;
+ item->data = data;
+ uv_chan_send(&worker->chan, item);
+ if (worker->active_items++ == 0) {
+ uv_messenger_ref(worker->msgr);
+ }
+}
+
+void uv_worker_close(uv_worker_t *worker, uv_worker_close_cb close_cb) {
+#ifndef NDEBUG
+ assert(uv_thread_self() == worker->thread_id);
+#endif
+
+ // Prevent double calling.
+ assert(worker->close_cb == NULL);
+
+ worker->close_cb = close_cb;
+ uv_chan_send(&worker->chan, NULL);
+ if (worker->active_items++ == 0) {
+ uv_messenger_ref(worker->msgr);
+ }
+}
diff --git a/src/mbgl/util/uv-worker.h b/src/mbgl/util/uv-worker.h
new file mode 100644
index 0000000000..cb2075d1c3
--- /dev/null
+++ b/src/mbgl/util/uv-worker.h
@@ -0,0 +1,41 @@
+#ifndef MBGL_UTIL_UV_WORKER
+#define MBGL_UTIL_UV_WORKER
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <mbgl/util/uv-messenger.h>
+#include <mbgl/util/uv-channel.h>
+
+#include <stdlib.h>
+
+typedef struct uv_worker_s uv_worker_t;
+
+typedef void (*uv_worker_cb)(void *data);
+typedef void (*uv_worker_after_cb)(void *data);
+typedef void (*uv_worker_close_cb)(uv_worker_t *worker);
+
+struct uv_worker_s {
+#ifndef NDEBUG
+ unsigned long thread_id;
+#endif
+ uv_loop_t *loop;
+ uv_messenger_t *msgr;
+ uv_chan_t chan;
+ const char *name;
+ int count;
+ uv_worker_close_cb close_cb;
+ unsigned int active_items;
+};
+
+int uv_worker_init(uv_worker_t *worker, uv_loop_t *loop, int count, const char *name);
+void uv_worker_send(uv_worker_t *worker, void *data, uv_worker_cb work_cb,
+ uv_worker_after_cb after_work_cb);
+void uv_worker_close(uv_worker_t *worker, uv_worker_close_cb close_cb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/mbgl/util/uv.cpp b/src/mbgl/util/uv.cpp
new file mode 100644
index 0000000000..7aa5bad0cf
--- /dev/null
+++ b/src/mbgl/util/uv.cpp
@@ -0,0 +1,25 @@
+#include <mbgl/util/uv.hpp>
+
+#include <uv.h>
+
+namespace uv {
+
+std::string cwd() {
+#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
+ char dir[512];
+ uv_cwd(dir, 512);
+ return dir;
+#else
+ size_t max = 0;
+ std::string dir;
+ do {
+ max += 256;
+ dir.resize(max);
+ uv_cwd(const_cast<char *>(dir.data()), &max);
+ } while (max == dir.size());
+ dir.resize(max - 1);
+ return dir;
+#endif
+}
+
+}
diff --git a/src/mbgl/util/uv_detail.hpp b/src/mbgl/util/uv_detail.hpp
new file mode 100644
index 0000000000..99f5edc145
--- /dev/null
+++ b/src/mbgl/util/uv_detail.hpp
@@ -0,0 +1,177 @@
+#ifndef MBGL_UTIL_UV_DETAIL
+#define MBGL_UTIL_UV_DETAIL
+
+#include <mbgl/util/uv-worker.h>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <uv.h>
+
+#include <functional>
+#include <cassert>
+#include <memory>
+#include <string>
+
+namespace uv {
+
+template <class T>
+void close(std::unique_ptr<T> ptr) {
+ uv_close(reinterpret_cast<uv_handle_t*>(ptr.release()), [](uv_handle_t* handle) {
+ delete reinterpret_cast<T*>(handle);
+ });
+}
+
+class loop : public mbgl::util::noncopyable {
+public:
+ inline loop() {
+#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
+ l = uv_loop_new();
+ if (l == nullptr) {
+#else
+ l = new uv_loop_t;
+ if (uv_loop_init(l) != 0) {
+#endif
+ throw std::runtime_error("failed to initialize loop");
+ }
+ }
+
+ inline ~loop() {
+#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
+ uv_loop_delete(l);
+#else
+ uv_loop_close(l);
+ delete l;
+#endif
+
+ }
+
+ inline uv_loop_t *operator*() { return l; }
+
+private:
+ uv_loop_t *l = nullptr;
+};
+
+class async : public mbgl::util::noncopyable {
+public:
+ inline async(uv_loop_t* loop, std::function<void ()> fn_)
+ : a(new uv_async_t)
+ , fn(fn_)
+ {
+ a->data = this;
+ if (uv_async_init(loop, a.get(), async_cb) != 0) {
+ throw std::runtime_error("failed to initialize async");
+ }
+ }
+
+ inline ~async() {
+ close(std::move(a));
+ }
+
+ inline void send() {
+ if (uv_async_send(a.get()) != 0) {
+ throw std::runtime_error("failed to async send");
+ }
+ }
+
+private:
+#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
+ static void async_cb(uv_async_t* a, int) {
+#else
+ static void async_cb(uv_async_t* a) {
+#endif
+ reinterpret_cast<async*>(a->data)->fn();
+ }
+
+ std::unique_ptr<uv_async_t> a;
+ std::function<void ()> fn;
+};
+
+class rwlock : public mbgl::util::noncopyable {
+public:
+ inline rwlock() {
+ if (uv_rwlock_init(&mtx) != 0) {
+ throw std::runtime_error("failed to initialize read-write lock");
+ }
+ }
+ inline ~rwlock() { uv_rwlock_destroy(&mtx); }
+ inline void rdlock() { uv_rwlock_rdlock(&mtx); }
+ inline void wrlock() { uv_rwlock_wrlock(&mtx); }
+ inline void rdunlock() { uv_rwlock_rdunlock(&mtx); }
+ inline void wrunlock() { uv_rwlock_wrunlock(&mtx); }
+
+private:
+ uv_rwlock_t mtx;
+};
+
+class readlock : public mbgl::util::noncopyable {
+public:
+ inline readlock(rwlock &mtx_) : mtx(mtx_) { mtx.rdlock(); }
+ inline readlock(const std::unique_ptr<rwlock> &mtx_) : mtx(*mtx_) { mtx.rdlock(); }
+ inline ~readlock() { mtx.rdunlock(); }
+
+private:
+ rwlock &mtx;
+};
+
+class writelock : public mbgl::util::noncopyable {
+public:
+ inline writelock(rwlock &mtx_) : mtx(mtx_) { mtx.wrlock(); }
+ inline writelock(const std::unique_ptr<rwlock> &mtx_) : mtx(*mtx_) { mtx.wrlock(); }
+ inline ~writelock() { mtx.wrunlock(); }
+
+private:
+ rwlock &mtx;
+};
+
+class worker : public mbgl::util::noncopyable {
+public:
+ inline worker(uv_loop_t *loop, unsigned int count, const char *name = nullptr) : w(new uv_worker_t) {
+ uv_worker_init(w, loop, count, name);
+ }
+ inline ~worker() {
+ uv_worker_close(w, [](uv_worker_t *worker_) {
+ delete worker_;
+ });
+ }
+ inline void add(void *data, uv_worker_cb work_cb, uv_worker_after_cb after_work_cb) {
+ uv_worker_send(w, data, work_cb, after_work_cb);
+ }
+
+private:
+ uv_worker_t *w;
+};
+
+template <typename T>
+class work : public mbgl::util::noncopyable {
+public:
+ typedef std::function<void (T&)> work_callback;
+ typedef std::function<void (T&)> after_work_callback;
+
+ template<typename... Args>
+ work(worker &worker, work_callback work_cb_, after_work_callback after_work_cb_, Args&&... args)
+ : data(std::forward<Args>(args)...),
+ work_cb(work_cb_),
+ after_work_cb(after_work_cb_) {
+ worker.add(this, do_work, after_work);
+ }
+
+private:
+ static void do_work(void *data) {
+ work<T> *w = reinterpret_cast<work<T> *>(data);
+ w->work_cb(w->data);
+ }
+
+ static void after_work(void *data) {
+ work<T> *w = reinterpret_cast<work<T> *>(data);
+ w->after_work_cb(w->data);
+ delete w;
+ }
+
+private:
+ T data;
+ work_callback work_cb;
+ after_work_callback after_work_cb;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/util/vec.hpp b/src/mbgl/util/vec.hpp
new file mode 100644
index 0000000000..60065b4fc3
--- /dev/null
+++ b/src/mbgl/util/vec.hpp
@@ -0,0 +1,15 @@
+#ifndef MBGL_UTIL_VEC
+#define MBGL_UTIL_VEC
+
+#include <mbgl/util/vec.hpp>
+
+namespace mbgl {
+
+struct box {
+ vec2<double> tl, tr, bl, br;
+ vec2<double> center;
+};
+
+}
+
+#endif