1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/quic/quic_fec_group.h"
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "base/stl_util.h"
13 using base::StringPiece
;
14 using std::numeric_limits
;
19 QuicFecGroup::QuicFecGroup()
20 : min_protected_packet_(kInvalidPacketNumber
),
21 max_protected_packet_(kInvalidPacketNumber
),
22 payload_parity_len_(0),
23 effective_encryption_level_(NUM_ENCRYPTION_LEVELS
) {}
25 QuicFecGroup::~QuicFecGroup() {}
27 bool QuicFecGroup::Update(EncryptionLevel encryption_level
,
28 const QuicPacketHeader
& header
,
29 StringPiece decrypted_payload
) {
30 DCHECK_NE(kInvalidPacketNumber
, header
.packet_packet_number
);
31 if (ContainsKey(received_packets_
, header
.packet_packet_number
)) {
34 if (min_protected_packet_
!= kInvalidPacketNumber
&&
35 max_protected_packet_
!= kInvalidPacketNumber
&&
36 (header
.packet_packet_number
< min_protected_packet_
||
37 header
.packet_packet_number
> max_protected_packet_
)) {
38 DLOG(ERROR
) << "FEC group does not cover received packet: "
39 << header
.packet_packet_number
;
42 if (!UpdateParity(decrypted_payload
)) {
45 received_packets_
.insert(header
.packet_packet_number
);
46 if (encryption_level
< effective_encryption_level_
) {
47 effective_encryption_level_
= encryption_level
;
52 bool QuicFecGroup::UpdateFec(EncryptionLevel encryption_level
,
53 QuicPacketNumber fec_packet_packet_number
,
54 const QuicFecData
& fec
) {
55 DCHECK_NE(kInvalidPacketNumber
, fec_packet_packet_number
);
56 DCHECK_NE(kInvalidPacketNumber
, fec
.fec_group
);
57 if (min_protected_packet_
!= kInvalidPacketNumber
) {
60 PacketNumberSet::const_iterator it
= received_packets_
.begin();
61 while (it
!= received_packets_
.end()) {
62 if ((*it
< fec
.fec_group
) || (*it
>= fec_packet_packet_number
)) {
63 DLOG(ERROR
) << "FEC group does not cover received packet: " << *it
;
68 if (!UpdateParity(fec
.redundancy
)) {
71 min_protected_packet_
= fec
.fec_group
;
72 max_protected_packet_
= fec_packet_packet_number
- 1;
73 if (encryption_level
< effective_encryption_level_
) {
74 effective_encryption_level_
= encryption_level
;
79 bool QuicFecGroup::CanRevive() const {
80 // We can revive if we're missing exactly 1 packet.
81 return NumMissingPackets() == 1;
84 bool QuicFecGroup::IsFinished() const {
85 // We are finished if we are not missing any packets.
86 return NumMissingPackets() == 0;
89 size_t QuicFecGroup::Revive(QuicPacketHeader
* header
,
90 char* decrypted_payload
,
91 size_t decrypted_payload_len
) {
96 // Identify the packet number to be resurrected.
97 QuicPacketNumber missing
= kInvalidPacketNumber
;
98 for (QuicPacketNumber i
= min_protected_packet_
; i
<= max_protected_packet_
;
100 // Is this packet missing?
101 if (received_packets_
.count(i
) == 0) {
106 DCHECK_NE(kInvalidPacketNumber
, missing
);
108 DCHECK_LE(payload_parity_len_
, decrypted_payload_len
);
109 if (payload_parity_len_
> decrypted_payload_len
) {
112 for (size_t i
= 0; i
< payload_parity_len_
; ++i
) {
113 decrypted_payload
[i
] = payload_parity_
[i
];
116 header
->packet_packet_number
= missing
;
117 header
->entropy_flag
= false; // Unknown entropy.
119 received_packets_
.insert(missing
);
120 return payload_parity_len_
;
123 bool QuicFecGroup::ProtectsPacketsBefore(QuicPacketNumber num
) const {
124 if (max_protected_packet_
!= kInvalidPacketNumber
) {
125 return max_protected_packet_
< num
;
127 // Since we might not yet have received the FEC packet, we must check
128 // the packets we have received.
129 return *received_packets_
.begin() < num
;
132 bool QuicFecGroup::UpdateParity(StringPiece payload
) {
133 DCHECK_GE(kMaxPacketSize
, payload
.size());
134 if (payload
.size() > kMaxPacketSize
) {
135 DLOG(ERROR
) << "Illegal payload size: " << payload
.size();
138 if (payload_parity_len_
< payload
.size()) {
139 payload_parity_len_
= payload
.size();
141 if (received_packets_
.empty() &&
142 min_protected_packet_
== kInvalidPacketNumber
) {
143 // Initialize the parity to the value of this payload
144 memcpy(payload_parity_
, payload
.data(), payload
.size());
145 if (payload
.size() < kMaxPacketSize
) {
146 // TODO(rch): expand as needed.
147 memset(payload_parity_
+ payload
.size(), 0,
148 kMaxPacketSize
- payload
.size());
152 // Update the parity by XORing in the data (padding with 0s if necessary).
153 XorBuffers(payload
.data(), payload
.size(), payload_parity_
);
157 QuicPacketCount
QuicFecGroup::NumMissingPackets() const {
158 if (min_protected_packet_
== kInvalidPacketNumber
) {
159 return numeric_limits
<QuicPacketCount
>::max();
161 return static_cast<QuicPacketCount
>(
162 (max_protected_packet_
- min_protected_packet_
+ 1) -
163 received_packets_
.size());
166 void QuicFecGroup::XorBuffers(const char* input
,
167 size_t size_in_bytes
,
169 #if defined(__i386__) || defined(__x86_64__)
170 // On x86, alignment is not required and casting bytes to words is safe.
172 // size_t is a reasonable approximation of how large a general-purpose
173 // register is for the platforms and compilers Chrome is built on.
174 typedef size_t platform_word
;
175 const size_t size_in_words
= size_in_bytes
/ sizeof(platform_word
);
177 const platform_word
* input_words
=
178 reinterpret_cast<const platform_word
*>(input
);
179 platform_word
* output_words
= reinterpret_cast<platform_word
*>(output
);
181 // Handle word-sized part of the buffer.
182 size_t offset_in_words
= 0;
183 for (; offset_in_words
< size_in_words
; offset_in_words
++) {
184 output_words
[offset_in_words
] ^= input_words
[offset_in_words
];
187 // Handle the tail which does not fit into the word.
188 for (size_t offset_in_bytes
= offset_in_words
* sizeof(platform_word
);
189 offset_in_bytes
< size_in_bytes
; offset_in_bytes
++) {
190 output
[offset_in_bytes
] ^= input
[offset_in_bytes
];
193 // On ARM and most other plaforms, the code above could fail due to the
194 // alignment errors. Stick to byte-by-byte comparison.
195 for (size_t offset
= 0; offset
< size_in_bytes
; offset
++) {
196 output
[offset
] ^= input
[offset
];
198 #endif /* defined(__i386__) || defined(__x86_64__) */