You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
309 lines
9.6 KiB
309 lines
9.6 KiB
// Copyright (c) 2012 The Chromium 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 "dbus/values_util.h"
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "base/json/json_writer.h"
|
|
#include "base/logging.h"
|
|
#include "base/values.h"
|
|
#include "dbus/message.h"
|
|
|
|
namespace dbus {
|
|
|
|
namespace {
|
|
|
|
// Returns whether |value| is exactly representable by double or not.
|
|
template<typename T>
|
|
bool IsExactlyRepresentableByDouble(T value) {
|
|
return value == static_cast<T>(static_cast<double>(value));
|
|
}
|
|
|
|
// Pops values from |reader| and appends them to |list_value|.
|
|
bool PopListElements(MessageReader* reader, base::ListValue* list_value) {
|
|
while (reader->HasMoreData()) {
|
|
std::unique_ptr<base::Value> element_value = PopDataAsValue(reader);
|
|
if (!element_value)
|
|
return false;
|
|
list_value->Append(std::move(element_value));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Pops dict-entries from |reader| and sets them to |dictionary_value|
|
|
bool PopDictionaryEntries(MessageReader* reader,
|
|
base::DictionaryValue* dictionary_value) {
|
|
while (reader->HasMoreData()) {
|
|
DCHECK_EQ(Message::DICT_ENTRY, reader->GetDataType());
|
|
MessageReader entry_reader(nullptr);
|
|
if (!reader->PopDictEntry(&entry_reader))
|
|
return false;
|
|
// Get key as a string.
|
|
std::string key_string;
|
|
if (entry_reader.GetDataType() == Message::STRING) {
|
|
// If the type of keys is STRING, pop it directly.
|
|
if (!entry_reader.PopString(&key_string))
|
|
return false;
|
|
} else {
|
|
// If the type of keys is not STRING, convert it to string.
|
|
std::unique_ptr<base::Value> key(PopDataAsValue(&entry_reader));
|
|
if (!key)
|
|
return false;
|
|
// Use JSONWriter to convert an arbitrary value to a string.
|
|
base::JSONWriter::Write(*key, &key_string);
|
|
}
|
|
// Get the value and set the key-value pair.
|
|
std::unique_ptr<base::Value> value = PopDataAsValue(&entry_reader);
|
|
if (!value)
|
|
return false;
|
|
dictionary_value->SetWithoutPathExpansion(key_string, std::move(value));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Gets the D-Bus type signature for the value.
|
|
std::string GetTypeSignature(const base::Value& value) {
|
|
switch (value.type()) {
|
|
case base::Value::Type::BOOLEAN:
|
|
return "b";
|
|
case base::Value::Type::INTEGER:
|
|
return "i";
|
|
case base::Value::Type::DOUBLE:
|
|
return "d";
|
|
case base::Value::Type::STRING:
|
|
return "s";
|
|
case base::Value::Type::BINARY:
|
|
return "ay";
|
|
case base::Value::Type::DICTIONARY:
|
|
return "a{sv}";
|
|
case base::Value::Type::LIST:
|
|
return "av";
|
|
default:
|
|
DLOG(ERROR) << "Unexpected type " << value.type();
|
|
return std::string();
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::unique_ptr<base::Value> PopDataAsValue(MessageReader* reader) {
|
|
std::unique_ptr<base::Value> result;
|
|
switch (reader->GetDataType()) {
|
|
case Message::INVALID_DATA:
|
|
// Do nothing.
|
|
break;
|
|
case Message::BYTE: {
|
|
uint8_t value = 0;
|
|
if (reader->PopByte(&value))
|
|
result = std::make_unique<base::Value>(value);
|
|
break;
|
|
}
|
|
case Message::BOOL: {
|
|
bool value = false;
|
|
if (reader->PopBool(&value))
|
|
result = std::make_unique<base::Value>(value);
|
|
break;
|
|
}
|
|
case Message::INT16: {
|
|
int16_t value = 0;
|
|
if (reader->PopInt16(&value))
|
|
result = std::make_unique<base::Value>(value);
|
|
break;
|
|
}
|
|
case Message::UINT16: {
|
|
uint16_t value = 0;
|
|
if (reader->PopUint16(&value))
|
|
result = std::make_unique<base::Value>(value);
|
|
break;
|
|
}
|
|
case Message::INT32: {
|
|
int32_t value = 0;
|
|
if (reader->PopInt32(&value))
|
|
result = std::make_unique<base::Value>(value);
|
|
break;
|
|
}
|
|
case Message::UINT32: {
|
|
uint32_t value = 0;
|
|
if (reader->PopUint32(&value)) {
|
|
result = std::make_unique<base::Value>(static_cast<double>(value));
|
|
}
|
|
break;
|
|
}
|
|
case Message::INT64: {
|
|
int64_t value = 0;
|
|
if (reader->PopInt64(&value)) {
|
|
DLOG_IF(WARNING, !IsExactlyRepresentableByDouble(value)) <<
|
|
value << " is not exactly representable by double";
|
|
result = std::make_unique<base::Value>(static_cast<double>(value));
|
|
}
|
|
break;
|
|
}
|
|
case Message::UINT64: {
|
|
uint64_t value = 0;
|
|
if (reader->PopUint64(&value)) {
|
|
DLOG_IF(WARNING, !IsExactlyRepresentableByDouble(value)) <<
|
|
value << " is not exactly representable by double";
|
|
result = std::make_unique<base::Value>(static_cast<double>(value));
|
|
}
|
|
break;
|
|
}
|
|
case Message::DOUBLE: {
|
|
double value = 0;
|
|
if (reader->PopDouble(&value))
|
|
result = std::make_unique<base::Value>(value);
|
|
break;
|
|
}
|
|
case Message::STRING: {
|
|
std::string value;
|
|
if (reader->PopString(&value))
|
|
result = std::make_unique<base::Value>(value);
|
|
break;
|
|
}
|
|
case Message::OBJECT_PATH: {
|
|
ObjectPath value;
|
|
if (reader->PopObjectPath(&value))
|
|
result = std::make_unique<base::Value>(value.value());
|
|
break;
|
|
}
|
|
case Message::UNIX_FD: {
|
|
// Cannot distinguish a file descriptor from an int
|
|
NOTREACHED();
|
|
break;
|
|
}
|
|
case Message::ARRAY: {
|
|
MessageReader sub_reader(nullptr);
|
|
if (reader->PopArray(&sub_reader)) {
|
|
// If the type of the array's element is DICT_ENTRY, create a
|
|
// DictionaryValue, otherwise create a ListValue.
|
|
if (sub_reader.GetDataType() == Message::DICT_ENTRY) {
|
|
std::unique_ptr<base::DictionaryValue> dictionary_value(
|
|
new base::DictionaryValue);
|
|
if (PopDictionaryEntries(&sub_reader, dictionary_value.get()))
|
|
result = std::move(dictionary_value);
|
|
} else {
|
|
std::unique_ptr<base::ListValue> list_value(new base::ListValue);
|
|
if (PopListElements(&sub_reader, list_value.get()))
|
|
result = std::move(list_value);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Message::STRUCT: {
|
|
MessageReader sub_reader(nullptr);
|
|
if (reader->PopStruct(&sub_reader)) {
|
|
std::unique_ptr<base::ListValue> list_value(new base::ListValue);
|
|
if (PopListElements(&sub_reader, list_value.get()))
|
|
result = std::move(list_value);
|
|
}
|
|
break;
|
|
}
|
|
case Message::DICT_ENTRY:
|
|
// DICT_ENTRY must be popped as an element of an array.
|
|
NOTREACHED();
|
|
break;
|
|
case Message::VARIANT: {
|
|
MessageReader sub_reader(nullptr);
|
|
if (reader->PopVariant(&sub_reader))
|
|
result = PopDataAsValue(&sub_reader);
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void AppendBasicTypeValueData(MessageWriter* writer, const base::Value& value) {
|
|
switch (value.type()) {
|
|
case base::Value::Type::BOOLEAN: {
|
|
bool bool_value = false;
|
|
bool success = value.GetAsBoolean(&bool_value);
|
|
DCHECK(success);
|
|
writer->AppendBool(bool_value);
|
|
break;
|
|
}
|
|
case base::Value::Type::INTEGER: {
|
|
int int_value = 0;
|
|
bool success = value.GetAsInteger(&int_value);
|
|
DCHECK(success);
|
|
writer->AppendInt32(int_value);
|
|
break;
|
|
}
|
|
case base::Value::Type::DOUBLE: {
|
|
double double_value = 0;
|
|
bool success = value.GetAsDouble(&double_value);
|
|
DCHECK(success);
|
|
writer->AppendDouble(double_value);
|
|
break;
|
|
}
|
|
case base::Value::Type::STRING: {
|
|
std::string string_value;
|
|
bool success = value.GetAsString(&string_value);
|
|
DCHECK(success);
|
|
writer->AppendString(string_value);
|
|
break;
|
|
}
|
|
default:
|
|
DLOG(ERROR) << "Unexpected type " << value.type();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AppendBasicTypeValueDataAsVariant(MessageWriter* writer,
|
|
const base::Value& value) {
|
|
MessageWriter sub_writer(nullptr);
|
|
writer->OpenVariant(GetTypeSignature(value), &sub_writer);
|
|
AppendBasicTypeValueData(&sub_writer, value);
|
|
writer->CloseContainer(&sub_writer);
|
|
}
|
|
|
|
void AppendValueData(MessageWriter* writer, const base::Value& value) {
|
|
switch (value.type()) {
|
|
case base::Value::Type::DICTIONARY: {
|
|
const base::DictionaryValue* dictionary = nullptr;
|
|
value.GetAsDictionary(&dictionary);
|
|
dbus::MessageWriter array_writer(nullptr);
|
|
writer->OpenArray("{sv}", &array_writer);
|
|
for (base::DictionaryValue::Iterator iter(*dictionary);
|
|
!iter.IsAtEnd(); iter.Advance()) {
|
|
dbus::MessageWriter dict_entry_writer(nullptr);
|
|
array_writer.OpenDictEntry(&dict_entry_writer);
|
|
dict_entry_writer.AppendString(iter.key());
|
|
AppendValueDataAsVariant(&dict_entry_writer, iter.value());
|
|
array_writer.CloseContainer(&dict_entry_writer);
|
|
}
|
|
writer->CloseContainer(&array_writer);
|
|
break;
|
|
}
|
|
case base::Value::Type::LIST: {
|
|
const base::ListValue* list = nullptr;
|
|
value.GetAsList(&list);
|
|
dbus::MessageWriter array_writer(nullptr);
|
|
writer->OpenArray("v", &array_writer);
|
|
for (const auto& value : *list) {
|
|
AppendValueDataAsVariant(&array_writer, value);
|
|
}
|
|
writer->CloseContainer(&array_writer);
|
|
break;
|
|
}
|
|
case base::Value::Type::BOOLEAN:
|
|
case base::Value::Type::INTEGER:
|
|
case base::Value::Type::DOUBLE:
|
|
case base::Value::Type::STRING:
|
|
AppendBasicTypeValueData(writer, value);
|
|
break;
|
|
default:
|
|
DLOG(ERROR) << "Unexpected type: " << value.type();
|
|
}
|
|
}
|
|
|
|
void AppendValueDataAsVariant(MessageWriter* writer, const base::Value& value) {
|
|
MessageWriter variant_writer(nullptr);
|
|
writer->OpenVariant(GetTypeSignature(value), &variant_writer);
|
|
AppendValueData(&variant_writer, value);
|
|
writer->CloseContainer(&variant_writer);
|
|
}
|
|
|
|
} // namespace dbus
|