Add message encoding/decoding utility to FML. (flutter/engine#6017)
This commit is contained in:
@@ -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",
|
||||
|
||||
108
engine/src/flutter/fml/message.cc
Normal file
108
engine/src/flutter/fml/message.cc
Normal file
@@ -0,0 +1,108 @@
|
||||
// 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
|
||||
204
engine/src/flutter/fml/message.h
Normal file
204
engine/src/flutter/fml/message.h
Normal file
@@ -0,0 +1,204 @@
|
||||
// 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_
|
||||
67
engine/src/flutter/fml/message_unittests.cc
Normal file
67
engine/src/flutter/fml/message_unittests.cc
Normal file
@@ -0,0 +1,67 @@
|
||||
// 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
|
||||
Reference in New Issue
Block a user