Add utility class for unwrapping 16 bit sequence numbers

Unwrap uint16_t to int64_t, based on delta and last sequence number.
This can make application logic, putting packets in maps etc, much
simpler.

BUG=

Review URL: https://codereview.webrtc.org/1209623002

Cr-Commit-Position: refs/heads/master@{#9887}
This commit is contained in:
sprang
2015-09-08 03:43:17 -07:00
committed by Commit bot
parent caa5f4b3d2
commit c3aa12d5f2
2 changed files with 99 additions and 0 deletions

View File

@ -765,6 +765,46 @@ inline uint32_t LatestTimestamp(uint32_t timestamp1, uint32_t timestamp2) {
return IsNewerTimestamp(timestamp1, timestamp2) ? timestamp1 : timestamp2; return IsNewerTimestamp(timestamp1, timestamp2) ? timestamp1 : timestamp2;
} }
// Utility class to unwrap a sequence number to a larger type, for easier
// handling large ranges. Note that sequence numbers will never be unwrapped
// to a negative value.
class SequenceNumberUnwrapper {
public:
SequenceNumberUnwrapper() : last_seq_(-1) {}
// Get the unwrapped sequence, but don't update the internal state.
int64_t UnwrapWithoutUpdate(uint16_t sequence_number) {
if (last_seq_ == -1)
return sequence_number;
uint16_t cropped_last = static_cast<uint16_t>(last_seq_);
int64_t delta = sequence_number - cropped_last;
if (IsNewerSequenceNumber(sequence_number, cropped_last)) {
if (delta < 0)
delta += (1 << 16); // Wrap forwards.
} else if (delta > 0 && (last_seq_ + delta - (1 << 16)) >= 0) {
// If sequence_number is older but delta is positive, this is a backwards
// wrap-around. However, don't wrap backwards past 0 (unwrapped).
delta -= (1 << 16);
}
return last_seq_ + delta;
}
// Only update the internal state to the specified last (unwrapped) sequence.
void UpdateLast(int64_t last_sequence) { last_seq_ = last_sequence; }
// Unwrap the sequence number and update the internal state.
int64_t Unwrap(uint16_t sequence_number) {
int64_t unwrapped = UnwrapWithoutUpdate(sequence_number);
UpdateLast(unwrapped);
return unwrapped;
}
private:
int64_t last_seq_;
};
} // namespace webrtc } // namespace webrtc
#endif // MODULE_COMMON_TYPES_H #endif // MODULE_COMMON_TYPES_H

View File

@ -122,4 +122,63 @@ TEST(ClampToInt16, TestCases) {
EXPECT_EQ(-0x8000, ClampToInt16(-0x7FFFFFFF)); EXPECT_EQ(-0x8000, ClampToInt16(-0x7FFFFFFF));
} }
TEST(SequenceNumberUnwrapper, Limits) {
SequenceNumberUnwrapper unwrapper;
EXPECT_EQ(0, unwrapper.Unwrap(0));
EXPECT_EQ(0x8000, unwrapper.Unwrap(0x8000));
// Delta is exactly 0x8000 but current is lower than input, wrap backwards.
EXPECT_EQ(0x0, unwrapper.Unwrap(0x0000));
EXPECT_EQ(0x8000, unwrapper.Unwrap(0x8000));
EXPECT_EQ(0xFFFF, unwrapper.Unwrap(0xFFFF));
EXPECT_EQ(0x10000, unwrapper.Unwrap(0x0000));
EXPECT_EQ(0xFFFF, unwrapper.Unwrap(0xFFFF));
EXPECT_EQ(0x8000, unwrapper.Unwrap(0x8000));
EXPECT_EQ(0, unwrapper.Unwrap(0));
// Don't allow negative values.
EXPECT_EQ(0xFFFF, unwrapper.Unwrap(0xFFFF));
}
TEST(SequenceNumberUnwrapper, ForwardWraps) {
int64_t seq = 0;
SequenceNumberUnwrapper unwrapper;
const int kMaxIncrease = 0x8000 - 1;
const int kNumWraps = 4;
for (int i = 0; i < kNumWraps * 2; ++i) {
int64_t unwrapped = unwrapper.Unwrap(static_cast<uint16_t>(seq & 0xFFFF));
EXPECT_EQ(seq, unwrapped);
seq += kMaxIncrease;
}
unwrapper.UpdateLast(0);
for (int seq = 0; seq < kNumWraps * 0xFFFF; ++seq) {
int64_t unwrapped = unwrapper.Unwrap(static_cast<uint16_t>(seq & 0xFFFF));
EXPECT_EQ(seq, unwrapped);
}
}
TEST(SequenceNumberUnwrapper, BackwardWraps) {
SequenceNumberUnwrapper unwrapper;
const int kMaxDecrease = 0x8000 - 1;
const int kNumWraps = 4;
int64_t seq = kNumWraps * 2 * kMaxDecrease;
unwrapper.UpdateLast(seq);
for (int i = kNumWraps * 2; i >= 0; --i) {
int64_t unwrapped = unwrapper.Unwrap(static_cast<uint16_t>(seq & 0xFFFF));
EXPECT_EQ(seq, unwrapped);
seq -= kMaxDecrease;
}
seq = kNumWraps * 0xFFFF;
unwrapper.UpdateLast(seq);
for (; seq >= 0; --seq) {
int64_t unwrapped = unwrapper.Unwrap(static_cast<uint16_t>(seq & 0xFFFF));
EXPECT_EQ(seq, unwrapped);
}
}
} // namespace webrtc } // namespace webrtc