Skip to content

Commit

Permalink
Add message encoding/decoding utility to FML. (flutter#6017)
Browse files Browse the repository at this point in the history
  • Loading branch information
chinmaygarde authored Aug 15, 2018
1 parent e3687f7 commit 7e39999
Show file tree
Hide file tree
Showing 5 changed files with 385 additions and 0 deletions.
3 changes: 3 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions fml/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
108 changes: 108 additions & 0 deletions fml/message.cc
Original file line number Diff line number Diff line change
@@ -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 changes: 204 additions & 0 deletions fml/message.h
Original file line number Diff line number Diff line change
@@ -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_
Loading

0 comments on commit 7e39999

Please sign in to comment.