blob: 53aa12fac5fc0bb86b724a8b10381bf58b924a21 [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.
#ifndef LIB_FIDL_LLCPP_INCLUDE_LIB_FIDL_LLCPP_WIRE_DECODER_H_
#define LIB_FIDL_LLCPP_INCLUDE_LIB_FIDL_LLCPP_WIRE_DECODER_H_
#include <lib/fidl/llcpp/coding_errors.h>
#include <lib/fidl/llcpp/internal/transport.h>
#include <lib/fidl/llcpp/wire_coding_common.h>
namespace fidl::internal {
class WireDecoder final {
public:
explicit WireDecoder(const CodingConfig* coding_config, uint8_t* bytes, size_t num_bytes,
fidl_handle_t* handles, fidl_handle_metadata_t* handle_metadata,
size_t num_handles);
~WireDecoder();
[[nodiscard]] bool Alloc(size_t size, WirePosition* position) {
if (size == 0) {
// While not standardized, the existing coding table C/C++ encoder expect zero-sized vectors
// to have a valid data pointer. Therefore, for compatibility with the existing encoder when
// re-encoding, it is necessary to output a pointer to the decode buffer.
*position = WirePosition(&bytes_[next_out_of_line_]);
return true;
}
size_t old = next_out_of_line_;
size_t next_unaligned = next_out_of_line_ + size;
size_t next = FIDL_ALIGN_64(next_unaligned);
if (unlikely(next > num_bytes_)) {
SetError(ZX_ERR_BUFFER_TOO_SMALL, kCodingErrorBackingBufferSizeExceeded);
return false;
}
uint64_t padding;
switch (next - next_unaligned) {
case 0:
padding = 0x0000000000000000;
break;
case 1:
padding = 0xff00000000000000;
break;
case 2:
padding = 0xffff000000000000;
break;
case 3:
padding = 0xffffff0000000000;
break;
case 4:
padding = 0xffffffff00000000;
break;
case 5:
padding = 0xffffffffff000000;
break;
case 6:
padding = 0xffffffffffff0000;
break;
case 7:
padding = 0xffffffffffffff00;
break;
default:
__builtin_unreachable();
}
if (unlikely(*reinterpret_cast<uint64_t*>(&bytes_[next - 8]) & padding)) {
SetError(kCodingErrorInvalidPaddingBytes);
return false;
}
next_out_of_line_ = next;
*position = WirePosition(&bytes_[old]);
return true;
}
void CloseNextNHandles(size_t count) {
if (unlikely(count > num_handles_ - handle_index_)) {
SetError(kCodingErrorInvalidNumHandlesSpecifiedInEnvelope);
return;
}
coding_config_->close_many(&handles_[handle_index_], count);
size_t end = handle_index_ + count;
for (; handle_index_ < end; handle_index_++) {
handles_[handle_index_] = FIDL_HANDLE_INVALID;
}
}
void DecodeHandle(WirePosition position, HandleAttributes attr, bool is_optional) {
zx_handle_t* handle = position.As<zx_handle_t>();
switch (*handle) {
case FIDL_HANDLE_PRESENT: {
if (unlikely(handle_index_ >= num_handles_)) {
SetError(kCodingErrorTooManyHandlesConsumed);
return;
}
zx_handle_t* body_handle = &handles_[handle_index_];
if (unlikely(*body_handle == FIDL_HANDLE_INVALID)) {
SetError(kCodingErrorInvalidHandleInInput);
return;
}
if (coding_config_->decode_process_handle) {
const char* error;
zx_status_t status = coding_config_->decode_process_handle(
body_handle, attr, handle_index_, handle_metadata_, &error);
if (unlikely(status != ZX_OK)) {
SetError(error);
return;
}
}
*handle = *body_handle;
++handle_index_;
return;
}
case FIDL_HANDLE_ABSENT: {
if (likely(!is_optional)) {
SetError(kCodingErrorAbsentNonNullableHandle);
}
return;
}
default: {
SetError(kCodingErrorInvalidPresenceIndicator);
return;
}
}
}
size_t CurrentLength() const { return next_out_of_line_; }
size_t CurrentHandleCount() const { return handle_index_; }
void SetError(const char* error) {
if (HasError()) {
return;
}
error_ = error;
}
void SetError(zx_status_t status, const char* error) {
if (HasError()) {
return;
}
error_status_ = status;
error_ = error;
}
bool HasError() const { return error_ != nullptr; }
fidl::Status Finish() {
if (HasError()) {
coding_config_->close_many(handles_, num_handles_);
return fidl::Status::DecodeError(error_status_, error_);
}
return fidl::Status::Ok();
}
private:
const CodingConfig* coding_config_;
uint8_t* bytes_;
size_t num_bytes_;
fidl_handle_t* handles_;
fidl_handle_metadata_t* handle_metadata_;
size_t num_handles_;
uint32_t handle_index_ = 0;
size_t next_out_of_line_ = 0;
zx_status_t error_status_ = ZX_ERR_INVALID_ARGS;
const char* error_ = nullptr;
};
} // namespace fidl::internal
#endif // LIB_FIDL_LLCPP_INCLUDE_LIB_FIDL_LLCPP_WIRE_DECODER_H_