summaryrefslogtreecommitdiff
path: root/ext/librethinkdbxx/src/datum.cc
diff options
context:
space:
mode:
authorAdam Ierymenko <adam.ierymenko@zerotier.com>2018-04-25 06:39:02 -0700
committerGitHub <noreply@github.com>2018-04-25 06:39:02 -0700
commit42ec780a6f6eedef4d8b1d8218bd72fc6ed75cc0 (patch)
tree7bf86c4d92d6a0f77eced79bfc33313c62c7b6dd /ext/librethinkdbxx/src/datum.cc
parent18c9dc8a0649c866eff9f299f20fa5b19c502e52 (diff)
parent4608880fb06700822d01e9e5d6729fcdeb82b64b (diff)
downloadinfinitytier-42ec780a6f6eedef4d8b1d8218bd72fc6ed75cc0.tar.gz
infinitytier-42ec780a6f6eedef4d8b1d8218bd72fc6ed75cc0.zip
Merge branch 'dev' into netbsd-support
Diffstat (limited to 'ext/librethinkdbxx/src/datum.cc')
-rw-r--r--ext/librethinkdbxx/src/datum.cc449
1 files changed, 449 insertions, 0 deletions
diff --git a/ext/librethinkdbxx/src/datum.cc b/ext/librethinkdbxx/src/datum.cc
new file mode 100644
index 00000000..e4dbc8dc
--- /dev/null
+++ b/ext/librethinkdbxx/src/datum.cc
@@ -0,0 +1,449 @@
+#include <float.h>
+#include <cmath>
+
+#include "datum.h"
+#include "json_p.h"
+#include "utils.h"
+#include "cursor.h"
+
+#include "rapidjson-config.h"
+#include "rapidjson/prettywriter.h"
+#include "rapidjson/stringbuffer.h"
+
+namespace RethinkDB {
+
+using TT = Protocol::Term::TermType;
+
+bool Datum::is_nil() const {
+ return type == Type::NIL;
+}
+
+bool Datum::is_boolean() const {
+ return type == Type::BOOLEAN;
+}
+
+bool Datum::is_number() const {
+ return type == Type::NUMBER;
+}
+
+bool Datum::is_string() const {
+ return type == Type::STRING;
+}
+
+bool Datum::is_object() const {
+ return type == Type::OBJECT;
+}
+
+bool Datum::is_array() const {
+ return type == Type::ARRAY;
+}
+
+bool Datum::is_binary() const {
+ return type == Type::BINARY;
+}
+
+bool Datum::is_time() const {
+ return type == Type::TIME;
+}
+
+bool* Datum::get_boolean() {
+ if (type == Type::BOOLEAN) {
+ return &value.boolean;
+ } else {
+ return NULL;
+ }
+}
+
+const bool* Datum::get_boolean() const {
+ if (type == Type::BOOLEAN) {
+ return &value.boolean;
+ } else {
+ return NULL;
+ }
+}
+
+double* Datum::get_number() {
+ if (type == Type::NUMBER) {
+ return &value.number;
+ } else {
+ return NULL;
+ }
+}
+
+const double* Datum::get_number() const {
+ if (type == Type::NUMBER) {
+ return &value.number;
+ } else {
+ return NULL;
+ }
+}
+
+std::string* Datum::get_string() {
+ if (type == Type::STRING) {
+ return &value.string;
+ } else {
+ return NULL;
+ }
+}
+
+const std::string* Datum::get_string() const {
+ if (type == Type::STRING) {
+ return &value.string;
+ } else {
+ return NULL;
+ }
+}
+
+Datum* Datum::get_field(std::string key) {
+ if (type != Type::OBJECT) {
+ return NULL;
+ }
+ auto it = value.object.find(key);
+ if (it == value.object.end()) {
+ return NULL;
+ }
+ return &it->second;
+}
+
+const Datum* Datum::get_field(std::string key) const {
+ if (type != Type::OBJECT) {
+ return NULL;
+ }
+ auto it = value.object.find(key);
+ if (it == value.object.end()) {
+ return NULL;
+ }
+ return &it->second;
+}
+
+Datum* Datum::get_nth(size_t i) {
+ if (type != Type::ARRAY) {
+ return NULL;
+ }
+ if (i >= value.array.size()) {
+ return NULL;
+ }
+ return &value.array[i];
+}
+
+const Datum* Datum::get_nth(size_t i) const {
+ if (type != Type::ARRAY) {
+ return NULL;
+ }
+ if (i >= value.array.size()) {
+ return NULL;
+ }
+ return &value.array[i];
+}
+
+Object* Datum::get_object() {
+ if (type == Type::OBJECT) {
+ return &value.object;
+ } else {
+ return NULL;
+ }
+}
+
+const Object* Datum::get_object() const {
+ if (type == Type::OBJECT) {
+ return &value.object;
+ } else {
+ return NULL;
+ }
+}
+
+Array* Datum::get_array() {
+ if (type == Type::ARRAY) {
+ return &value.array;
+ } else {
+ return NULL;
+ }
+}
+
+const Array* Datum::get_array() const {
+ if (type == Type::ARRAY) {
+ return &value.array;
+ } else {
+ return NULL;
+ }
+}
+
+Binary* Datum::get_binary() {
+ if (type == Type::BINARY) {
+ return &value.binary;
+ } else {
+ return NULL;
+ }
+}
+
+const Binary* Datum::get_binary() const {
+ if (type == Type::BINARY) {
+ return &value.binary;
+ } else {
+ return NULL;
+ }
+}
+
+Time* Datum::get_time() {
+ if (type == Type::TIME) {
+ return &value.time;
+ } else {
+ return NULL;
+ }
+}
+
+const Time* Datum::get_time() const {
+ if (type == Type::TIME) {
+ return &value.time;
+ } else {
+ return NULL;
+ }
+}
+
+bool& Datum::extract_boolean() {
+ if (type != Type::BOOLEAN) {
+ throw Error("extract_bool: Not a boolean");
+ }
+ return value.boolean;
+}
+
+double& Datum::extract_number() {
+ if (type != Type::NUMBER) {
+ throw Error("extract_number: Not a number: %s", write_datum(*this).c_str());
+ }
+ return value.number;
+}
+
+std::string& Datum::extract_string() {
+ if (type != Type::STRING) {
+ throw Error("extract_string: Not a string");
+ }
+ return value.string;
+}
+
+Object& Datum::extract_object() {
+ if (type != Type::OBJECT) {
+ throw Error("extract_object: Not an object");
+ }
+ return value.object;
+}
+
+Datum& Datum::extract_field(std::string key) {
+ if (type != Type::OBJECT) {
+ throw Error("extract_field: Not an object");
+ }
+ auto it = value.object.find(key);
+ if (it == value.object.end()) {
+ throw Error("extract_field: No such key in object");
+ }
+ return it->second;
+}
+
+Datum& Datum::extract_nth(size_t i) {
+ if (type != Type::ARRAY) {
+ throw Error("extract_nth: Not an array");
+ }
+ if (i >= value.array.size()) {
+ throw Error("extract_nth: index too large");
+ }
+ return value.array[i];
+}
+
+Array& Datum::extract_array() {
+ if (type != Type::ARRAY) {
+ throw Error("get_array: Not an array");
+ }
+ return value.array;
+}
+
+Binary& Datum::extract_binary() {
+ if (type != Type::BINARY) {
+ throw Error("get_binary: Not a binary");
+ }
+ return value.binary;
+}
+
+Time& Datum::extract_time() {
+ if (type != Type::TIME) {
+ throw Error("get_time: Not a time");
+ }
+ return value.time;
+}
+
+int Datum::compare(const Datum& other) const {
+#define COMPARE(a, b) do { \
+ if (a < b) { return -1; } \
+ if (a > b) { return 1; } } while(0)
+#define COMPARE_OTHER(x) COMPARE(x, other.x)
+
+ COMPARE_OTHER(type);
+ int c;
+ switch (type) {
+ case Type::NIL: case Type::INVALID: break;
+ case Type::BOOLEAN: COMPARE_OTHER(value.boolean); break;
+ case Type::NUMBER: COMPARE_OTHER(value.number); break;
+ case Type::STRING:
+ c = value.string.compare(other.value.string);
+ COMPARE(c, 0);
+ break;
+ case Type::BINARY:
+ c = value.binary.data.compare(other.value.binary.data);
+ COMPARE(c, 0);
+ break;
+ case Type::TIME:
+ COMPARE(value.time.epoch_time, other.value.time.epoch_time);
+ COMPARE(value.time.utc_offset, other.value.time.utc_offset);
+ break;
+ case Type::ARRAY:
+ COMPARE_OTHER(value.array.size());
+ for (size_t i = 0; i < value.array.size(); i++) {
+ c = value.array[i].compare(other.value.array[i]);
+ COMPARE(c, 0);
+ }
+ break;
+ case Type::OBJECT:
+ COMPARE_OTHER(value.object.size());
+ for (Object::const_iterator l = value.object.begin(),
+ r = other.value.object.begin();
+ l != value.object.end();
+ ++l, ++r) {
+ COMPARE(l->first, r->first);
+ c = l->second.compare(r->second);
+ COMPARE(c, 0);
+ }
+ break;
+ default:
+ throw Error("cannot compare invalid datum");
+ }
+ return 0;
+#undef COMPARE_OTHER
+#undef COMPARE
+}
+
+bool Datum::operator== (const Datum& other) const {
+ return compare(other) == 0;
+}
+
+Datum Datum::from_raw() const {
+ do {
+ const Datum* type_field = get_field("$reql_type$");
+ if (!type_field) break;
+ const std::string* type = type_field->get_string();
+ if (!type) break;;
+ if (!strcmp(type->c_str(), "BINARY")) {
+ const Datum* data_field = get_field("data");
+ if (!data_field) break;
+ const std::string* encoded_data = data_field->get_string();
+ if (!encoded_data) break;
+ Binary binary("");
+ if (base64_decode(*encoded_data, binary.data)) {
+ return binary;
+ }
+ } else if (!strcmp(type->c_str(), "TIME")) {
+ const Datum* epoch_field = get_field("epoch_time");
+ if (!epoch_field) break;
+ const Datum* tz_field = get_field("timezone");
+ if (!tz_field) break;
+ const double* epoch_time = epoch_field->get_number();
+ if (!epoch_time) break;
+ const std::string* tz = tz_field->get_string();
+ if (!tz) break;
+ double offset;
+ if (!Time::parse_utc_offset(*tz, &offset)) break;
+ return Time(*epoch_time, offset);
+ }
+ } while (0);
+ return *this;
+}
+
+Datum Datum::to_raw() const {
+ if (type == Type::BINARY) {
+ return Object{
+ {"$reql_type$", "BINARY"},
+ {"data", base64_encode(value.binary.data)}};
+ } else if (type == Type::TIME) {
+ return Object{
+ {"$reql_type$", "TIME"},
+ {"epoch_time", value.time.epoch_time},
+ {"timezone", Time::utc_offset_string(value.time.utc_offset)}};
+ }
+ return *this;
+}
+
+Datum::Datum(Cursor&& cursor) : Datum(cursor.to_datum()) { }
+Datum::Datum(const Cursor& cursor) : Datum(cursor.to_datum()) { }
+
+static const double max_dbl_int = 0x1LL << DBL_MANT_DIG;
+static const double min_dbl_int = max_dbl_int * -1;
+bool number_as_integer(double d, int64_t *i_out) {
+ static_assert(DBL_MANT_DIG == 53, "Doubles are wrong size.");
+
+ if (min_dbl_int <= d && d <= max_dbl_int) {
+ int64_t i = d;
+ if (static_cast<double>(i) == d) {
+ *i_out = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+template void Datum::write_json(
+ rapidjson::Writer<rapidjson::StringBuffer> *writer) const;
+template void Datum::write_json(
+ rapidjson::PrettyWriter<rapidjson::StringBuffer> *writer) const;
+
+template <class json_writer_t>
+void Datum::write_json(json_writer_t *writer) const {
+ switch (type) {
+ case Type::NIL: writer->Null(); break;
+ case Type::BOOLEAN: writer->Bool(value.boolean); break;
+ case Type::NUMBER: {
+ const double d = value.number;
+ // Always print -0.0 as a double since integers cannot represent -0.
+ // Otherwise check if the number is an integer and print it as such.
+ int64_t i;
+ if (!(d == 0.0 && std::signbit(d)) && number_as_integer(d, &i)) {
+ writer->Int64(i);
+ } else {
+ writer->Double(d);
+ }
+ } break;
+ case Type::STRING: writer->String(value.string.data(), value.string.size()); break;
+ case Type::ARRAY: {
+ writer->StartArray();
+ for (auto it : value.array) {
+ it.write_json(writer);
+ }
+ writer->EndArray();
+ } break;
+ case Type::OBJECT: {
+ writer->StartObject();
+ for (auto it : value.object) {
+ writer->Key(it.first.data(), it.first.size());
+ it.second.write_json(writer);
+ }
+ writer->EndObject();
+ } break;
+
+ case Type::BINARY:
+ case Type::TIME:
+ to_raw().write_json(writer);
+ break;
+ default:
+ throw Error("cannot write invalid datum");
+ }
+}
+
+std::string Datum::as_json() const {
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ write_json(&writer);
+ return std::string(buffer.GetString(), buffer.GetSize());
+}
+
+Datum Datum::from_json(const std::string& json) {
+ return read_datum(json);
+}
+
+} // namespace RethinkDB