未验证 提交 7e399991 编写于 作者: C Chinmay Garde 提交者: GitHub

Add message encoding/decoding utility to FML. (#6017)

上级 e3687f70
......@@ -579,6 +579,9 @@ TYPE: LicenseType.bsd
FILE: ../../../flutter/fml/file.h
FILE: ../../../flutter/fml/macros.h
FILE: ../../../flutter/fml/mapping.cc
FILE: ../../../flutter/fml/message.cc
FILE: ../../../flutter/fml/message.h
FILE: ../../../flutter/fml/message_unittests.cc
FILE: ../../../flutter/fml/native_library.h
FILE: ../../../flutter/fml/paths.cc
FILE: ../../../flutter/fml/platform/fuchsia/paths_fuchsia.cc
......
......@@ -32,6 +32,8 @@ source_set("fml") {
"memory/weak_ptr.h",
"memory/weak_ptr_internal.cc",
"memory/weak_ptr_internal.h",
"message.cc",
"message.h",
"message_loop.cc",
"message_loop.h",
"message_loop_impl.cc",
......@@ -163,6 +165,7 @@ executable("fml_unittests") {
"memory/ref_counted_unittest.cc",
"memory/weak_ptr_unittest.cc",
"message_loop_unittests.cc",
"message_unittests.cc",
"string_view_unittest.cc",
"synchronization/thread_annotations_unittest.cc",
"synchronization/thread_checker_unittest.cc",
......
// Copyright 2018 The Flutter 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 "flutter/fml/message.h"
#include "flutter/fml/logging.h"
namespace fml {
Message::Message() = default;
Message::~Message() = default;
static uint32_t NextPowerOfTwoSize(uint32_t x) {
if (x == 0) {
return 1;
}
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x + 1;
}
const uint8_t* Message::GetBuffer() const {
return buffer_;
}
size_t Message::GetBufferSize() const {
return buffer_length_;
}
size_t Message::GetDataLength() const {
return data_length_;
}
size_t Message::GetSizeRead() const {
return size_read_;
}
bool Message::Reserve(size_t size) {
if (buffer_length_ >= size) {
return true;
}
return Resize(NextPowerOfTwoSize(size));
}
bool Message::Resize(size_t size) {
if (buffer_ == nullptr) {
// This is the initial resize where we have no previous buffer.
FML_DCHECK(buffer_length_ == 0);
void* buffer = ::malloc(size);
const bool success = buffer != nullptr;
if (success) {
buffer_ = static_cast<uint8_t*>(buffer);
buffer_length_ = size;
}
return success;
}
FML_DCHECK(size > buffer_length_);
void* resized = ::realloc(buffer_, size);
const bool success = resized != nullptr;
// In case of failure, the input buffer to realloc is still valid.
if (success) {
buffer_ = static_cast<uint8_t*>(resized);
buffer_length_ = size;
}
return success;
}
uint8_t* Message::PrepareEncode(size_t size) {
if (!Reserve(data_length_ + size)) {
return nullptr;
}
auto old_length = data_length_;
data_length_ += size;
return buffer_ + old_length;
}
uint8_t* Message::PrepareDecode(size_t size) {
if ((size + size_read_) > buffer_length_) {
return nullptr;
}
auto buffer = buffer_ + size_read_;
size_read_ += size;
return buffer;
}
void Message::ResetRead() {
size_read_ = 0;
}
} // namespace fml
// Copyright 2018 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FML_MESSAGE_H_
#define FLUTTER_FML_MESSAGE_H_
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <memory>
#include <type_traits>
#include <utility>
#include "flutter/fml/compiler_specific.h"
#include "flutter/fml/macros.h"
namespace fml {
#define FML_SERIALIZE(message, value) \
if (!message.Encode(value)) { \
return false; \
}
#define FML_SERIALIZE_TRAITS(message, value, traits) \
if (!message.Encode<traits>(value)) { \
return false; \
}
#define FML_DESERIALIZE(message, value) \
if (!message.Decode(value)) { \
return false; \
}
#define FML_DESERIALIZE_TRAITS(message, value, traits) \
if (!message.Decode<traits>(value)) { \
return false; \
}
class Message;
class MessageSerializable {
public:
virtual ~MessageSerializable() = default;
virtual bool Serialize(Message& message) const = 0;
virtual bool Deserialize(Message& message) = 0;
virtual size_t GetSerializableTag() const { return 0; };
};
// The traits passed to the encode/decode calls that accept traits should be
// something like the following.
//
// class MessageSerializableTraits {
// static size_t GetSerializableTag(const T&);
// static std::unique_ptr<T> CreateForSerializableTag(size_t tag);
// };
template <class T>
struct Serializable : public std::integral_constant<
bool,
std::is_trivially_copyable<T>::value ||
std::is_base_of<MessageSerializable, T>::value> {
};
// Utility class to encode and decode |Serializable| types to and from a buffer.
// Elements have to be read back into the same order they were written.
class Message {
public:
Message();
~Message();
const uint8_t* GetBuffer() const;
size_t GetBufferSize() const;
size_t GetDataLength() const;
size_t GetSizeRead() const;
void ResetRead();
// Encoders.
template <typename T,
typename = std::enable_if_t<std::is_trivially_copyable<T>::value>>
FML_WARN_UNUSED_RESULT bool Encode(const T& value) {
if (auto buffer = PrepareEncode(sizeof(T))) {
::memcpy(buffer, &value, sizeof(T));
return true;
}
return false;
}
FML_WARN_UNUSED_RESULT bool Encode(const MessageSerializable& value) {
return value.Serialize(*this);
}
template <typename Traits,
typename T,
typename = std::enable_if_t<
std::is_base_of<MessageSerializable, T>::value>>
FML_WARN_UNUSED_RESULT bool Encode(const std::unique_ptr<T>& value) {
// Encode if null.
if (!Encode(static_cast<bool>(value))) {
return false;
}
if (!value) {
return true;
}
// Encode the type.
if (!Encode(Traits::GetSerializableTag(*value.get()))) {
return false;
}
// Encode the value.
if (!Encode(*value.get())) {
return false;
}
return true;
}
// Decoders.
template <typename T,
typename = std::enable_if_t<std::is_trivially_copyable<T>::value>>
FML_WARN_UNUSED_RESULT bool Decode(T& value) {
if (auto buffer = PrepareDecode(sizeof(T))) {
::memcpy(&value, buffer, sizeof(T));
return true;
}
return false;
}
FML_WARN_UNUSED_RESULT bool Decode(MessageSerializable& value) {
return value.Deserialize(*this);
}
template <typename Traits,
typename T,
typename = std::enable_if_t<
std::is_base_of<MessageSerializable, T>::value>>
FML_WARN_UNUSED_RESULT bool Decode(std::unique_ptr<T>& value) {
// Decode if null.
bool is_null = false;
if (!Decode(is_null)) {
return false;
}
if (is_null) {
return true;
}
// Decode type.
size_t tag = 0;
if (!Decode(tag)) {
return false;
}
std::unique_ptr<T> new_value = Traits::CreateForSerializableTag(tag);
if (!new_value) {
return false;
}
// Decode value.
if (!Decode(*new_value.get())) {
return false;
}
std::swap(value, new_value);
return true;
}
private:
uint8_t* buffer_ = nullptr;
size_t buffer_length_ = 0;
size_t data_length_ = 0;
size_t size_read_ = 0;
FML_WARN_UNUSED_RESULT
bool Reserve(size_t size);
FML_WARN_UNUSED_RESULT
bool Resize(size_t size);
FML_WARN_UNUSED_RESULT
uint8_t* PrepareEncode(size_t size);
FML_WARN_UNUSED_RESULT
uint8_t* PrepareDecode(size_t size);
FML_DISALLOW_COPY_AND_ASSIGN(Message);
};
} // namespace fml
#endif // FLUTTER_FML_MESSAGE_H_
// Copyright 2018 The Flutter 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 "flutter/fml/message.h"
#include "gtest/gtest.h"
namespace fml {
struct TestStruct {
int a = 12;
char b = 'x';
float c = 99.0f;
};
TEST(MessageTest, CanEncodeTriviallyCopyableTypes) {
Message message;
ASSERT_TRUE(message.Encode(12));
ASSERT_TRUE(message.Encode(11.0f));
ASSERT_TRUE(message.Encode('a'));
TestStruct s;
ASSERT_TRUE(message.Encode(s));
ASSERT_GE(message.GetDataLength(), 0u);
ASSERT_GE(message.GetBufferSize(), 0u);
ASSERT_EQ(message.GetSizeRead(), 0u);
}
TEST(MessageTest, CanDecodeTriviallyCopyableTypes) {
Message message;
ASSERT_TRUE(message.Encode(12));
ASSERT_TRUE(message.Encode(11.0f));
ASSERT_TRUE(message.Encode('a'));
TestStruct s;
s.a = 10;
s.b = 'y';
s.c = 11.1f;
ASSERT_TRUE(message.Encode(s));
ASSERT_GE(message.GetDataLength(), 0u);
ASSERT_GE(message.GetBufferSize(), 0u);
ASSERT_EQ(message.GetSizeRead(), 0u);
int int1 = 0;
ASSERT_TRUE(message.Decode(int1));
ASSERT_EQ(12, int1);
float float1 = 0.0f;
ASSERT_TRUE(message.Decode(float1));
ASSERT_EQ(float1, 11.0f);
char char1 = 'x';
ASSERT_TRUE(message.Decode(char1));
ASSERT_EQ(char1, 'a');
TestStruct s1;
ASSERT_TRUE(message.Decode(s1));
ASSERT_EQ(s1.a, 10);
ASSERT_EQ(s1.b, 'y');
ASSERT_EQ(s1.c, 11.1f);
ASSERT_NE(message.GetSizeRead(), 0u);
ASSERT_EQ(message.GetDataLength(), message.GetSizeRead());
}
} // namespace fml
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册