blob: 7baf66044e522adefbdcab1efa671584637bcd27 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file defines a GIDL-like DSL in C++ to help with defining FIDL bytes.
#ifndef SRC_TESTS_FIDL_DYNSUITE_CHANNEL_UTIL_BYTES_H_
#define SRC_TESTS_FIDL_DYNSUITE_CHANNEL_UTIL_BYTES_H_
#include <lib/fidl/cpp/natural_types.h>
#include <lib/fidl/cpp/transaction_header.h>
#include <zircon/assert.h>
#include <zircon/fidl.h>
#include <zircon/types.h>
#include <utility>
#include <vector>
namespace channel_util {
// Bytes is a wrapper around std::vector<uint8_t> that provides list
// initialization which automatically flattens nested Bytes values.
class Bytes {
public:
explicit Bytes(std::vector<uint8_t> data) : data_(std::move(data)) {}
Bytes(std::initializer_list<uint8_t> data) : data_(data) {}
Bytes(std::initializer_list<Bytes> bytes) {
for (auto& b : bytes) {
data_.insert(data_.end(), b.data_.begin(), b.data_.end());
}
}
size_t size() const { return data_.size(); }
const uint8_t* data() const { return data_.data(); }
bool operator==(const Bytes& rhs) const { return data_ == rhs.data_; }
bool operator!=(const Bytes& rhs) const { return data_ != rhs.data_; }
// Returns the first four bytes interpreted as a txid.
zx_txid_t& txid() {
ZX_ASSERT(data_.size() >= sizeof(zx_txid_t));
return *reinterpret_cast<zx_txid_t*>(data_.data());
}
private:
std::vector<uint8_t> data_;
};
template <typename T>
inline Bytes as_bytes(const T& value) {
static_assert(std::has_unique_object_representations_v<T>,
"objects with internal padding disallowed as the padding bytes are undefined");
std::vector<uint8_t> vec(sizeof(T), 0);
memcpy(vec.data(), &value, sizeof(T));
return Bytes(std::move(vec));
}
// Like fidl_message_header_t but with convenient defaults and implicit conversion to Bytes.
struct Header {
zx_txid_t txid;
uint8_t at_rest_flags[2] = {0x02, 0x00}; // v2 wire format
uint8_t dynamic_flags = 0x00;
uint8_t magic_number = 1; // FIDL 2023 magic number
uint64_t ordinal;
// NOLINTNEXTLINE(google-explicit-constructor)
operator Bytes() const { return as_bytes(*this); }
};
// We don't use these values directly, to keep tests independent of what they're
// testing. But we expect them to be the same, so statically assert that.
static_assert(sizeof(Header) == sizeof(fidl_message_header_t));
static_assert(Header{}.magic_number == kFidlWireFormatMagicNumberInitial);
static_assert(Header{}.at_rest_flags[0] == FIDL_MESSAGE_HEADER_AT_REST_FLAGS_0_USE_VERSION_V2);
static_assert(Header{}.at_rest_flags[1] == 0);
// A placeholder to indicate that the txid is unknown when using
// Channel::read_and_check_unknown_txid.
const zx_txid_t kTxidNotKnown = 0;
// An arbitrary nonzero txid to use for two-way methods in tests.
const zx_txid_t kTwoWayTxid = 0x174d3b6a;
// The dynamic flag indicating a method is flexible, not strict.
const uint8_t kDynamicFlagsFlexible = 0x80;
// An invalid magic number.
const uint8_t kBadMagicNumber = 0x5a;
inline Bytes uint8(uint8_t value) { return as_bytes(value); }
inline Bytes uint16(uint16_t value) { return as_bytes(value); }
inline Bytes uint32(uint32_t value) { return as_bytes(value); }
inline Bytes uint64(uint64_t value) { return as_bytes(value); }
inline Bytes int8(int8_t value) { return as_bytes(value); }
inline Bytes int16(int16_t value) { return as_bytes(value); }
inline Bytes int32(int32_t value) { return as_bytes(value); }
inline Bytes int64(int64_t value) { return as_bytes(value); }
struct RepeatOp {
uint8_t byte;
Bytes times(size_t count) const { return Bytes(std::vector<uint8_t>(count, byte)); }
};
inline RepeatOp repeat(uint8_t byte) { return RepeatOp{byte}; }
inline Bytes padding(size_t count) { return repeat(0).times(count); }
template <typename FidlType>
inline Bytes encode(FidlType message) {
auto result = fidl::StandaloneEncode(message);
ZX_ASSERT_MSG(result.message().ok(), "encode failed");
ZX_ASSERT_MSG(result.message().handle_actual() == 0u, "message contained handles");
auto copied_bytes = result.message().CopyBytes();
std::vector<uint8_t> vector(copied_bytes.data(), copied_bytes.data() + copied_bytes.size());
return Bytes(vector);
}
inline Bytes handle_present() { return repeat(0xff).times(4); }
inline Bytes handle_absent() { return repeat(0x00).times(4); }
inline Bytes pointer_present() { return repeat(0xff).times(8); }
inline Bytes pointer_absent() { return repeat(0x00).times(8); }
inline Bytes union_ordinal(fidl_xunion_tag_t ordinal) { return uint64(ordinal); }
inline Bytes table_max_ordinal(uint64_t ordinal) { return uint64(ordinal); }
const fidl_xunion_tag_t kResultUnionSuccess = 1;
const fidl_xunion_tag_t kResultUnionDomainError = 2;
const fidl_xunion_tag_t kResultUnionFrameworkError = 3;
inline Bytes string_length(uint64_t length) { return uint64(length); }
inline Bytes vector_length(uint64_t length) { return uint64(length); }
inline Bytes framework_err_unknown_method() { return int32(ZX_ERR_NOT_SUPPORTED); }
inline Bytes string_header(uint64_t length) { return {string_length(length), pointer_present()}; }
inline Bytes vector_header(uint64_t length) { return {vector_length(length), pointer_present()}; }
inline Bytes out_of_line_envelope(uint32_t num_bytes, uint8_t num_handles = 0) {
return {uint32(num_bytes), uint16(num_handles), uint16(0)};
}
inline Bytes inline_envelope(const Bytes& value, bool has_handles = false) {
ZX_ASSERT_MSG(value.size() <= 4, "inline envelope values must be <= 4 bytes");
return {value, padding(4 - value.size()), uint16(has_handles), uint16(1)};
}
} // namespace channel_util
#endif // SRC_TESTS_FIDL_DYNSUITE_CHANNEL_UTIL_BYTES_H_