diff --git a/webrtc/modules/interface/module_common_types.h b/webrtc/modules/interface/module_common_types.h index c8205ee60b..b371e47d05 100644 --- a/webrtc/modules/interface/module_common_types.h +++ b/webrtc/modules/interface/module_common_types.h @@ -765,6 +765,46 @@ inline uint32_t LatestTimestamp(uint32_t timestamp1, uint32_t 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(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 #endif // MODULE_COMMON_TYPES_H diff --git a/webrtc/modules/module_common_types_unittest.cc b/webrtc/modules/module_common_types_unittest.cc index 3e7f594166..bc0b7a1a5b 100644 --- a/webrtc/modules/module_common_types_unittest.cc +++ b/webrtc/modules/module_common_types_unittest.cc @@ -122,4 +122,63 @@ TEST(ClampToInt16, TestCases) { 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(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(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(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(seq & 0xFFFF)); + EXPECT_EQ(seq, unwrapped); + } +} + } // namespace webrtc