// Copyright (c) 2012 The Chromium 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 "net/quic/quic_fec_group.h"
#include <limits>
#include "base/logging.h"
using base::StringPiece;
using std::numeric_limits;
using std::set;
namespace net {
namespace {
const QuicPacketSequenceNumber kNoSequenceNumber = kuint64max;
} // namespace
QuicFecGroup::QuicFecGroup()
: min_protected_packet_(kNoSequenceNumber),
max_protected_packet_(kNoSequenceNumber),
payload_parity_len_(0),
entropy_parity_(false) {
}
QuicFecGroup::~QuicFecGroup() {}
bool QuicFecGroup::Update(const QuicPacketHeader& header,
StringPiece decrypted_payload) {
if (received_packets_.count(header.packet_sequence_number) != 0) {
return false;
}
if (min_protected_packet_ != kNoSequenceNumber &&
max_protected_packet_ != kNoSequenceNumber &&
(header.packet_sequence_number < min_protected_packet_ ||
header.packet_sequence_number > max_protected_packet_)) {
DLOG(ERROR) << "FEC group does not cover received packet: "
<< header.packet_sequence_number;
return false;
}
if (!UpdateParity(decrypted_payload, header.entropy_flag)) {
return false;
}
received_packets_.insert(header.packet_sequence_number);
return true;
}
bool QuicFecGroup::UpdateFec(
QuicPacketSequenceNumber fec_packet_sequence_number,
bool fec_packet_entropy,
const QuicFecData& fec) {
if (min_protected_packet_ != kNoSequenceNumber) {
return false;
}
SequenceNumberSet::const_iterator it = received_packets_.begin();
while (it != received_packets_.end()) {
if ((*it < fec.fec_group) ||
(*it >= fec_packet_sequence_number)) {
DLOG(ERROR) << "FEC group does not cover received packet: " << *it;
return false;
}
++it;
}
if (!UpdateParity(fec.redundancy, fec_packet_entropy)) {
return false;
}
min_protected_packet_ = fec.fec_group;
max_protected_packet_ = fec_packet_sequence_number - 1;
return true;
}
bool QuicFecGroup::CanRevive() const {
// We can revive if we're missing exactly 1 packet.
return NumMissingPackets() == 1;
}
bool QuicFecGroup::IsFinished() const {
// We are finished if we are not missing any packets.
return NumMissingPackets() == 0;
}
size_t QuicFecGroup::Revive(QuicPacketHeader* header,
char* decrypted_payload,
size_t decrypted_payload_len) {
if (!CanRevive()) {
return 0;
}
// Identify the packet sequence number to be resurrected.
QuicPacketSequenceNumber missing = kNoSequenceNumber;
for (QuicPacketSequenceNumber i = min_protected_packet_;
i <= max_protected_packet_; ++i) {
// Is this packet missing?
if (received_packets_.count(i) == 0) {
missing = i;
break;
}
}
DCHECK_NE(kNoSequenceNumber, missing);
DCHECK_LE(payload_parity_len_, decrypted_payload_len);
if (payload_parity_len_ > decrypted_payload_len) {
return 0;
}
for (size_t i = 0; i < payload_parity_len_; ++i) {
decrypted_payload[i] = payload_parity_[i];
}
header->packet_sequence_number = missing;
header->entropy_flag = entropy_parity_;
received_packets_.insert(missing);
return payload_parity_len_;
}
bool QuicFecGroup::ProtectsPacketsBefore(QuicPacketSequenceNumber num) const {
if (max_protected_packet_ != kNoSequenceNumber) {
return max_protected_packet_ < num;
}
// Since we might not yet have recevied the FEC packet, we must check
// the packets we have received.
return *received_packets_.begin() < num;
}
bool QuicFecGroup::UpdateParity(StringPiece payload, bool entropy) {
DCHECK_LE(payload.size(), kMaxPacketSize);
if (payload.size() > kMaxPacketSize) {
DLOG(ERROR) << "Illegal payload size: " << payload.size();
return false;
}
if (payload_parity_len_ < payload.size()) {
payload_parity_len_ = payload.size();
}
DCHECK_LE(payload.size(), kMaxPacketSize);
if (received_packets_.empty() &&
min_protected_packet_ == kNoSequenceNumber) {
// Initialize the parity to the value of this payload
memcpy(payload_parity_, payload.data(), payload.size());
if (payload.size() < kMaxPacketSize) {
// TODO(rch): expand as needed.
memset(payload_parity_ + payload.size(), 0,
kMaxPacketSize - payload.size());
}
entropy_parity_ = entropy;
return true;
}
// Update the parity by XORing in the data (padding with 0s if necessary).
for (size_t i = 0; i < kMaxPacketSize; ++i) {
uint8 byte = i < payload.size() ? payload[i] : 0x00;
payload_parity_[i] ^= byte;
}
// xor of boolean values.
entropy_parity_ = (entropy_parity_ != entropy);
return true;
}
size_t QuicFecGroup::NumMissingPackets() const {
if (min_protected_packet_ == kNoSequenceNumber)
return numeric_limits<size_t>::max();
return (max_protected_packet_ - min_protected_packet_ + 1) -
received_packets_.size();
}
} // namespace net