summaryrefslogtreecommitdiff
path: root/include/mbgl/util/pbf.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/mbgl/util/pbf.hpp')
-rw-r--r--include/mbgl/util/pbf.hpp184
1 files changed, 184 insertions, 0 deletions
diff --git a/include/mbgl/util/pbf.hpp b/include/mbgl/util/pbf.hpp
new file mode 100644
index 0000000000..1f78a0072f
--- /dev/null
+++ b/include/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 = reinterpret_cast<const char*>(data);
+ skipBytes(bytes);
+ return std::string(string, 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