blob: a619c932a4e4cd2e4d64e0d4e2d623d8bf50ec0a [file] [log] [blame]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "messagepack.h"
#include <base/compiler_specific.h>
#include <base/logging.h>
// Macro for telling -Wimplicit-fallthrough that a fallthrough is intentional.
// This is needed for android nyc builds
#if __ANDROID__
#if !defined(FALLTHROUGH) && defined(__clang__)
#define FALLTHROUGH [[clang::fallthrough]]
#endif
#endif
namespace huddly {
namespace messagepack {
constexpr int kObjectPrintBufferSize = 100 * 1024;
Object::Object() {
object_.type = MSGPACK_OBJECT_NIL;
}
bool Object::IsNil() const {
return object_.type == MSGPACK_OBJECT_NIL;
}
template <>
bool Object::Is<bool>() const {
return object_.type == MSGPACK_OBJECT_BOOLEAN;
}
template <>
bool Object::Is<double>() const {
return (object_.type == MSGPACK_OBJECT_FLOAT ||
object_.type == MSGPACK_OBJECT_FLOAT32);
}
template <>
bool Object::Is<int64_t>() const {
// The term NEGATIVE_INTEGER used in msgpack-c is somewhat of a misnomer.
// Everywhere in the source code, it refers to the value accessed via the i64
// member, so it would probably better be called SIGNED_INTEGER.
return (object_.type == MSGPACK_OBJECT_NEGATIVE_INTEGER);
}
template <>
bool Object::Is<uint64_t>() const {
// The term POSITIVE_INTEGER used in msgpack-c is somewhat of a misnomer.
// Everywhere in the source code, it refers to the value accessed via the u64
// member, so it would probably better be called UNSIGNED_INTEGER.
return (object_.type == MSGPACK_OBJECT_POSITIVE_INTEGER);
}
template <>
bool Object::Is<std::string>() const {
return object_.type == MSGPACK_OBJECT_STR;
}
template <>
bool Object::Is<Map>() const {
return object_.type == MSGPACK_OBJECT_MAP;
}
template <>
bool Object::Is<Array>() const {
return object_.type == MSGPACK_OBJECT_ARRAY;
}
template <>
bool Object::Is<Bin>() const {
return object_.type == MSGPACK_OBJECT_BIN;
}
template <>
bool Object::Get<bool>(bool* out) const {
if (!Is<bool>())
return false;
*out = object_.via.boolean;
return true;
}
template <>
bool Object::Get<double>(double* out) const {
if (!Is<double>())
return false;
*out = object_.via.f64;
return true;
}
template <>
bool Object::Get<int64_t>(int64_t* out) const {
if (!Is<int64_t>())
return false;
*out = object_.via.i64;
return true;
}
template <>
bool Object::Get<uint64_t>(uint64_t* out) const {
if (!Is<uint64_t>())
return false;
*out = object_.via.u64;
return true;
}
template <>
bool Object::Get<std::string>(std::string* out) const {
if (!Is<std::string>())
return false;
*out = std::string(object_.via.str.ptr, object_.via.str.size);
return true;
}
template <>
bool Object::Get<Map>(Map* out) const {
if (!Is<Map>())
return false;
*out = Map(object_.via.map);
return true;
}
template <>
bool Object::Get<Array>(Array* out) const {
if (!Is<Array>())
return false;
*out = Array(object_.via.array);
return true;
}
template <>
bool Object::Get<Bin>(Bin* out) const {
if (!Is<Bin>())
return false;
*out = Bin(object_.via.bin);
return true;
}
template <>
bool Object::GetAs<int64_t>(int64_t* out) const {
switch (object_.type) {
case MSGPACK_OBJECT_POSITIVE_INTEGER:
*out = static_cast<int64_t>(object_.via.u64);
return true;
case MSGPACK_OBJECT_NEGATIVE_INTEGER:
*out = object_.via.i64;
return true;
case MSGPACK_OBJECT_FLOAT32:
FALLTHROUGH;
case MSGPACK_OBJECT_FLOAT:
FALLTHROUGH;
case MSGPACK_OBJECT_BOOLEAN:
FALLTHROUGH;
case MSGPACK_OBJECT_NIL:
FALLTHROUGH;
case MSGPACK_OBJECT_STR:
FALLTHROUGH;
case MSGPACK_OBJECT_ARRAY:
FALLTHROUGH;
case MSGPACK_OBJECT_MAP:
FALLTHROUGH;
case MSGPACK_OBJECT_BIN:
FALLTHROUGH;
case MSGPACK_OBJECT_EXT:
return false;
}
}
template <>
bool Object::GetAs<uint64_t>(uint64_t* out) const {
switch (object_.type) {
case MSGPACK_OBJECT_POSITIVE_INTEGER:
*out = object_.via.u64;
return true;
case MSGPACK_OBJECT_NEGATIVE_INTEGER:
*out = static_cast<uint64_t>(object_.via.i64);
return true;
case MSGPACK_OBJECT_FLOAT32:
FALLTHROUGH;
case MSGPACK_OBJECT_FLOAT:
FALLTHROUGH;
case MSGPACK_OBJECT_BOOLEAN:
FALLTHROUGH;
case MSGPACK_OBJECT_NIL:
FALLTHROUGH;
case MSGPACK_OBJECT_STR:
FALLTHROUGH;
case MSGPACK_OBJECT_ARRAY:
FALLTHROUGH;
case MSGPACK_OBJECT_MAP:
FALLTHROUGH;
case MSGPACK_OBJECT_BIN:
FALLTHROUGH;
case MSGPACK_OBJECT_EXT:
return false;
}
}
template <>
bool Object::GetAs<double>(double* out) const {
switch (object_.type) {
case MSGPACK_OBJECT_POSITIVE_INTEGER:
*out = static_cast<double>(object_.via.u64);
return true;
case MSGPACK_OBJECT_NEGATIVE_INTEGER:
*out = static_cast<double>(object_.via.i64);
return true;
case MSGPACK_OBJECT_FLOAT32:
FALLTHROUGH;
case MSGPACK_OBJECT_FLOAT:
*out = object_.via.f64;
return true;
case MSGPACK_OBJECT_BOOLEAN:
FALLTHROUGH;
case MSGPACK_OBJECT_NIL:
FALLTHROUGH;
case MSGPACK_OBJECT_STR:
FALLTHROUGH;
case MSGPACK_OBJECT_ARRAY:
FALLTHROUGH;
case MSGPACK_OBJECT_MAP:
FALLTHROUGH;
case MSGPACK_OBJECT_BIN:
FALLTHROUGH;
case MSGPACK_OBJECT_EXT:
return false;
}
}
template <>
bool Object::GetAs<std::string>(std::string* out) const {
if (Is<std::string>()) {
return Get<std::string>(out);
}
char buffer[kObjectPrintBufferSize];
int string_length =
msgpack_object_print_buffer(buffer, sizeof(buffer), object_);
if (string_length <= 0 || string_length >= sizeof(buffer)) {
LOG(ERROR) << "Failed to convert object to string";
return false;
}
if (string_length < 0) {
LOG(ERROR) << "Unexpected return value: " << string_length;
return false;
}
*out = std::string(buffer, string_length);
return true;
}
std::string Object::ToString() const {
std::string out;
if (!GetAs<std::string>(&out)) {
out = "";
}
return out;
}
std::string Object::TypeName() const {
switch (object_.type) {
case MSGPACK_OBJECT_NIL:
return "Nil";
case MSGPACK_OBJECT_BOOLEAN:
return "Boolean";
case MSGPACK_OBJECT_POSITIVE_INTEGER:
return "Int";
case MSGPACK_OBJECT_NEGATIVE_INTEGER:
return "Uint";
case MSGPACK_OBJECT_FLOAT32:
return "Float32";
case MSGPACK_OBJECT_FLOAT64:
return "Float64";
case MSGPACK_OBJECT_STR:
return "String";
case MSGPACK_OBJECT_ARRAY:
return "Array";
case MSGPACK_OBJECT_MAP:
return "Map";
case MSGPACK_OBJECT_BIN:
return "Bin";
case MSGPACK_OBJECT_EXT:
return "Object ext";
default:
return "Unknown type";
}
}
Map::Map() {
map_.size = 0;
map_.ptr = nullptr;
}
int Map::Size() const {
return map_.size;
}
bool Map::GetValueObject(const std::string& key, Object* out) const {
msgpack_object_kv key_value;
if (!FindKeyValue(key, &key_value)) {
return false;
}
*out = Object(key_value.val);
return true;
}
bool Map::FindKeyValue(const std::string& key,
msgpack_object_kv* key_value) const {
const auto map_begin = map_.ptr;
const auto map_end = map_begin + map_.size;
auto find_it = std::find_if(
map_begin, map_end, [&key](const msgpack_object_kv& key_value) {
Object candidate_key_object(key_value.key);
std::string candidate_key;
if (!candidate_key_object.Get<std::string>(&candidate_key))
return false;
return key == candidate_key;
});
if (find_it == map_end)
return false;
*key_value = *find_it;
return true;
}
std::string Map::ToString() const {
msgpack_object obj;
obj.type = MSGPACK_OBJECT_MAP;
obj.via.map = map_;
return Object(obj).ToString();
}
Array::Array() {
array_.size = 0;
array_.ptr = nullptr;
}
int Array::Size() const {
return array_.size;
}
bool Array::GetValueObjects(std::vector<Object>* out) const {
for (auto i = 0; i < Size(); i++) {
out->push_back(Object(array_.ptr[i]));
}
return true;
}
std::string Array::ToString() const {
msgpack_object obj;
obj.type = MSGPACK_OBJECT_ARRAY;
obj.via.array = array_;
return Object(obj).ToString();
}
Bin::Bin() {
bin_.size = 0;
bin_.ptr = nullptr;
}
int Bin::Size() const {
return bin_.size;
}
std::string Bin::ToString() const {
msgpack_object obj;
obj.type = MSGPACK_OBJECT_BIN;
obj.via.bin = bin_;
return Object(obj).ToString();
}
Unpacker::Unpacker(const std::vector<uint8_t>& packed) : packed_(packed) {
msgpack_unpacked_init(&unpacked_);
}
std::unique_ptr<Unpacker> Unpacker::Create(const std::vector<uint8_t>& packed) {
auto unpacker = std::unique_ptr<Unpacker>(new Unpacker(packed));
if (packed.empty()) {
return nullptr;
}
size_t offset = 0;
auto status = msgpack_unpack_next(
&unpacker->unpacked_,
reinterpret_cast<const char*>(unpacker->packed_.data()), packed.size(),
&offset);
if (status != MSGPACK_UNPACK_SUCCESS) {
return nullptr;
}
return unpacker;
}
Unpacker::~Unpacker() {
msgpack_unpacked_destroy(&unpacked_);
}
Object Unpacker::GetRootObject() const {
return Object(unpacked_.data);
}
} // namespace messagepack
} // namespace huddly