blob: 3b23fcee06bd483bebaaf2a2f03b315a0863fe3b [file] [log] [blame]
// Copyright 2018 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.
#include "garnet/lib/overnet/protocol/ack_frame.h"
#include <assert.h>
#include "garnet/lib/overnet/protocol/varint.h"
namespace overnet {
AckFrame::Writer::Writer(const AckFrame* ack_frame)
: ack_frame_(ack_frame), wire_length_(ack_frame_->WrittenLength()) {}
uint64_t AckFrame::WrittenLength() const {
uint64_t wire_length =
varint::WireSizeFor(ack_to_seq_) + varint::WireSizeFor(DelayAndFlags());
for (const auto block : blocks_) {
wire_length += varint::WireSizeFor(block.acks);
wire_length += varint::WireSizeFor(block.nacks);
}
return wire_length;
}
uint8_t* AckFrame::Writer::Write(uint8_t* out) const {
uint8_t* p = out;
p = varint::Write(ack_frame_->ack_to_seq_, p);
p = varint::Write(ack_frame_->DelayAndFlags(), p);
for (const auto block : ack_frame_->blocks_) {
p = varint::Write(block.acks, p);
p = varint::Write(block.nacks, p);
}
assert(p == out + wire_length_);
return p;
}
uint64_t AckFrame::DelayAndFlags() const {
uint64_t delay_part =
(ack_delay_us_ >> 63) ? 0xffff'ffff'ffff'fffe : (ack_delay_us_ << 1);
uint64_t partial_part = partial_ ? 1 : 0;
return delay_part | partial_part;
}
StatusOr<AckFrame> AckFrame::Parse(Slice slice) {
const uint8_t* bytes = slice.begin();
const uint8_t* end = slice.end();
uint64_t ack_to_seq;
if (!varint::Read(&bytes, end, &ack_to_seq)) {
return StatusOr<AckFrame>(StatusCode::INVALID_ARGUMENT,
"Failed to parse ack_to_seq from ack frame");
}
if (ack_to_seq == 0) {
return StatusOr<AckFrame>(StatusCode::INVALID_ARGUMENT,
"Ack frame cannot ack_to_seq 0");
}
uint64_t delay_and_flags;
if (!varint::Read(&bytes, end, &delay_and_flags)) {
return StatusOr<AckFrame>(StatusCode::INVALID_ARGUMENT,
"Failed to parse delay_and_flags from ack frame");
}
bool is_partial = (delay_and_flags & 1) != 0;
uint64_t ack_delay_us = delay_and_flags >> 1;
AckFrame frame(ack_to_seq, ack_delay_us);
frame.partial_ = is_partial;
uint64_t base = ack_to_seq;
while (bytes != end) {
uint64_t acks, nacks;
if (!varint::Read(&bytes, end, &acks)) {
return StatusOr<AckFrame>(StatusCode::INVALID_ARGUMENT,
"Failed to read ack count from ack frame");
}
if (!varint::Read(&bytes, end, &nacks)) {
return StatusOr<AckFrame>(StatusCode::INVALID_ARGUMENT,
"Failed to read nack count from ack frame");
}
if (acks >= base) {
return StatusOr<AckFrame>(StatusCode::INVALID_ARGUMENT,
"Failed to read nack (too many acks)");
}
if (nacks > base - acks) {
return StatusOr<AckFrame>(StatusCode::INVALID_ARGUMENT,
"Failed to read nack (too many nacks)");
}
if (nacks == 0) {
return StatusOr<AckFrame>(StatusCode::INVALID_ARGUMENT,
"Nack count cannot be zero");
}
base -= acks;
base -= nacks;
frame.blocks_.push_back(Block{acks, nacks});
frame.last_nack_ = base + 1;
}
return StatusOr<AckFrame>(std::move(frame));
}
std::ostream& operator<<(std::ostream& out, const AckFrame& ack_frame) {
out << "ACK{to:" << ack_frame.ack_to_seq()
<< ", delay:" << ack_frame.ack_delay_us()
<< "us, partial:" << (ack_frame.partial() ? "yes" : "no") << ", nack=[";
for (auto n : ack_frame.nack_seqs()) {
out << n << ",";
}
return out << "]}";
}
} // namespace overnet