diff --git a/quic/state/StreamData.h b/quic/state/StreamData.h index 215b42368..f4ef256cc 100644 --- a/quic/state/StreamData.h +++ b/quic/state/StreamData.h @@ -553,6 +553,53 @@ struct QuicStreamState : public QuicStreamLike { return readBuffer.size() > 0; } + void removeFromWriteBufMetaAfterOffset(uint64_t removalOffset) { + if (removalOffset < writeBufMeta.offset) { + writeBufMeta.length = 0; + return; + } + + if (removalOffset >= writeBufMeta.offset && + removalOffset < writeBufMeta.offset + writeBufMeta.length) { + writeBufMeta.length = uint32_t(removalOffset - writeBufMeta.offset + 1); + } + } + + void removeFromRetransmissionBufMetasAfterOffset(uint64_t removalOffset) { + folly::F14FastSet offsetsToRemove; + + for (auto& [offset, buf] : retransmissionBufMetas) { + if (offset > removalOffset) { + offsetsToRemove.insert(offset); + } else if (offset + buf.length > removalOffset) { + buf.length = uint32_t(removalOffset - offset + 1); + } + } + + for (auto offset : offsetsToRemove) { + retransmissionBufMetas.erase(offset); + } + } + + void removeFromLossBufMetasAfterOffset(uint64_t removalOffset) { + if (lossBufMetas.empty()) { + // Nothing to do. + return; + } + + while (!lossBufMetas.empty()) { + auto& lastElement = lossBufMetas.back(); + if (lastElement.offset > removalOffset) { + lossBufMetas.pop_back(); + } else if (lastElement.offset + lastElement.length > removalOffset) { + lastElement.length = uint32_t(removalOffset - lastElement.offset + 1); + return; + } else { + return; + } + } + } + std::unique_ptr dsrSender; // BufferMeta that has been written to the QUIC layer. diff --git a/quic/state/test/StreamDataTest.cpp b/quic/state/test/StreamDataTest.cpp index 13a707284..4e546815f 100644 --- a/quic/state/test/StreamDataTest.cpp +++ b/quic/state/test/StreamDataTest.cpp @@ -7,6 +7,7 @@ #include #include +#include #include using namespace quic; @@ -281,4 +282,307 @@ TEST(StreamDataTest, PendingWritesRemovalNoChange) { EXPECT_EQ(state.pendingWrites.chainLength(), 8); } +TEST(StreamDataTest, LossBufferMetaRemovalAll) { + QuicConnectionStateBase qcsb(QuicNodeType::Client); + QuicStreamState state(0, qcsb); + + // [1, 2] [5, 12] [17, 19] + WriteBufferMeta wbm1 = WriteBufferMeta::Builder() + .setOffset(1) + .setLength(2) + .setEOF(false) + .build(); + WriteBufferMeta wbm2 = WriteBufferMeta::Builder() + .setOffset(5) + .setLength(8) + .setEOF(false) + .build(); + WriteBufferMeta wbm3 = WriteBufferMeta::Builder() + .setOffset(17) + .setLength(3) + .setEOF(false) + .build(); + state.insertIntoLossBufMeta(wbm1); + state.insertIntoLossBufMeta(wbm2); + state.insertIntoLossBufMeta(wbm3); + + state.removeFromLossBufMetasAfterOffset(0); + + EXPECT_EQ(state.lossBufMetas.size(), 0); +} + +TEST(StreamDataTest, LossBufferMetaRemovalExactMatch) { + QuicConnectionStateBase qcsb(QuicNodeType::Client); + QuicStreamState state(0, qcsb); + + // [1, 2] [5, 12] [17, 19] + WriteBufferMeta wbm1 = WriteBufferMeta::Builder() + .setOffset(1) + .setLength(2) + .setEOF(false) + .build(); + WriteBufferMeta wbm2 = WriteBufferMeta::Builder() + .setOffset(5) + .setLength(8) + .setEOF(false) + .build(); + WriteBufferMeta wbm3 = WriteBufferMeta::Builder() + .setOffset(17) + .setLength(3) + .setEOF(false) + .build(); + state.insertIntoLossBufMeta(wbm1); + state.insertIntoLossBufMeta(wbm2); + state.insertIntoLossBufMeta(wbm3); + + state.removeFromLossBufMetasAfterOffset(4); + EXPECT_EQ(state.lossBufMetas.size(), 1); + EXPECT_EQ(state.lossBufMetas[0].offset, 1); + EXPECT_EQ(state.lossBufMetas[0].length, 2); +} + +TEST(StreamDataTest, LossBufferMetaRemovalPartialMatch) { + QuicConnectionStateBase qcsb(QuicNodeType::Client); + QuicStreamState state(0, qcsb); + + // [1, 2] [5, 12] [17, 19] + WriteBufferMeta wbm1 = WriteBufferMeta::Builder() + .setOffset(1) + .setLength(2) + .setEOF(false) + .build(); + WriteBufferMeta wbm2 = WriteBufferMeta::Builder() + .setOffset(5) + .setLength(8) + .setEOF(false) + .build(); + WriteBufferMeta wbm3 = WriteBufferMeta::Builder() + .setOffset(17) + .setLength(3) + .setEOF(false) + .build(); + state.insertIntoLossBufMeta(wbm1); + state.insertIntoLossBufMeta(wbm2); + state.insertIntoLossBufMeta(wbm3); + + state.removeFromLossBufMetasAfterOffset(5); + EXPECT_EQ(state.lossBufMetas.size(), 2); + + EXPECT_EQ(state.lossBufMetas[0].offset, 1); + EXPECT_EQ(state.lossBufMetas[0].length, 2); + + EXPECT_EQ(state.lossBufMetas[1].offset, 5); + EXPECT_EQ(state.lossBufMetas[1].length, 1); +} + +TEST(StreamDataTest, LossBufferMetaRemovalNoMatch) { + QuicConnectionStateBase qcsb(QuicNodeType::Client); + QuicStreamState state(0, qcsb); + + // [1, 2] [5, 12] [17, 19] + WriteBufferMeta wbm1 = WriteBufferMeta::Builder() + .setOffset(1) + .setLength(2) + .setEOF(false) + .build(); + WriteBufferMeta wbm2 = WriteBufferMeta::Builder() + .setOffset(5) + .setLength(8) + .setEOF(false) + .build(); + WriteBufferMeta wbm3 = WriteBufferMeta::Builder() + .setOffset(17) + .setLength(3) + .setEOF(false) + .build(); + state.insertIntoLossBufMeta(wbm1); + state.insertIntoLossBufMeta(wbm2); + state.insertIntoLossBufMeta(wbm3); + + state.removeFromLossBufAfterOffset(20); + EXPECT_EQ(state.lossBufMetas.size(), 3); + + EXPECT_EQ(state.lossBufMetas[0].offset, 1); + EXPECT_EQ(state.lossBufMetas[0].length, 2); + + EXPECT_EQ(state.lossBufMetas[1].offset, 5); + EXPECT_EQ(state.lossBufMetas[1].length, 8); + + EXPECT_EQ(state.lossBufMetas[2].offset, 17); + EXPECT_EQ(state.lossBufMetas[2].length, 3); +} + +TEST(StreamDataTest, RetxBufferMetaRemovalAll) { + QuicConnectionStateBase qcsb(QuicNodeType::Client); + QuicStreamState state(0, qcsb); + + // [1, 2] [5, 12] [17, 19] + WriteBufferMeta wbm1 = WriteBufferMeta::Builder() + .setOffset(1) + .setLength(2) + .setEOF(false) + .build(); + WriteBufferMeta wbm2 = WriteBufferMeta::Builder() + .setOffset(5) + .setLength(8) + .setEOF(false) + .build(); + WriteBufferMeta wbm3 = WriteBufferMeta::Builder() + .setOffset(17) + .setLength(3) + .setEOF(false) + .build(); + state.retransmissionBufMetas.emplace(1, wbm1); + state.retransmissionBufMetas.emplace(5, wbm2); + state.retransmissionBufMetas.emplace(17, wbm3); + + state.removeFromRetransmissionBufMetasAfterOffset(0); + EXPECT_EQ(state.retransmissionBufMetas.size(), 0); +} + +TEST(StreamDataTest, RetxBufferMetaRemovalExactMatch) { + QuicConnectionStateBase qcsb(QuicNodeType::Client); + QuicStreamState state(0, qcsb); + + // [1, 2] [5, 12] [17, 19] + WriteBufferMeta wbm1 = WriteBufferMeta::Builder() + .setOffset(1) + .setLength(2) + .setEOF(false) + .build(); + WriteBufferMeta wbm2 = WriteBufferMeta::Builder() + .setOffset(5) + .setLength(8) + .setEOF(false) + .build(); + WriteBufferMeta wbm3 = WriteBufferMeta::Builder() + .setOffset(17) + .setLength(3) + .setEOF(false) + .build(); + state.retransmissionBufMetas.emplace(1, wbm1); + state.retransmissionBufMetas.emplace(5, wbm2); + state.retransmissionBufMetas.emplace(17, wbm3); + + state.removeFromRetransmissionBufMetasAfterOffset(16); + EXPECT_EQ(state.retransmissionBufMetas.size(), 2); + + EXPECT_EQ(state.retransmissionBufMetas[1].offset, 1); + EXPECT_EQ(state.retransmissionBufMetas[1].length, 2); + + EXPECT_EQ(state.retransmissionBufMetas[5].offset, 5); + EXPECT_EQ(state.retransmissionBufMetas[5].length, 8); +} + +TEST(StreamDataTest, RetxBufferMetaRemovalPartialMatch) { + QuicConnectionStateBase qcsb(QuicNodeType::Client); + QuicStreamState state(0, qcsb); + + // [1, 2] [5, 12] [17, 19] + WriteBufferMeta wbm1 = WriteBufferMeta::Builder() + .setOffset(1) + .setLength(2) + .setEOF(false) + .build(); + WriteBufferMeta wbm2 = WriteBufferMeta::Builder() + .setOffset(5) + .setLength(8) + .setEOF(false) + .build(); + WriteBufferMeta wbm3 = WriteBufferMeta::Builder() + .setOffset(17) + .setLength(3) + .setEOF(false) + .build(); + state.retransmissionBufMetas.emplace(1, wbm1); + state.retransmissionBufMetas.emplace(5, wbm2); + state.retransmissionBufMetas.emplace(17, wbm3); + + state.removeFromRetransmissionBufMetasAfterOffset(5); + EXPECT_EQ(state.retransmissionBufMetas.size(), 2); + + EXPECT_EQ(state.retransmissionBufMetas[1].offset, 1); + EXPECT_EQ(state.retransmissionBufMetas[1].length, 2); + + EXPECT_EQ(state.retransmissionBufMetas[5].offset, 5); + EXPECT_EQ(state.retransmissionBufMetas[5].length, 1); +} + +TEST(StreamDataTest, RetxBufferMetaRemovalNoMatch) { + QuicConnectionStateBase qcsb(QuicNodeType::Client); + QuicStreamState state(0, qcsb); + + // [1, 2] [5, 12] [17, 19] + WriteBufferMeta wbm1 = WriteBufferMeta::Builder() + .setOffset(1) + .setLength(2) + .setEOF(false) + .build(); + WriteBufferMeta wbm2 = WriteBufferMeta::Builder() + .setOffset(5) + .setLength(8) + .setEOF(false) + .build(); + WriteBufferMeta wbm3 = WriteBufferMeta::Builder() + .setOffset(17) + .setLength(3) + .setEOF(false) + .build(); + state.retransmissionBufMetas.emplace(1, wbm1); + state.retransmissionBufMetas.emplace(5, wbm2); + state.retransmissionBufMetas.emplace(17, wbm3); + + state.removeFromRetransmissionBufMetasAfterOffset(19); + EXPECT_EQ(state.retransmissionBufMetas.size(), 3); + + EXPECT_EQ(state.retransmissionBufMetas[1].offset, 1); + EXPECT_EQ(state.retransmissionBufMetas[1].length, 2); + + EXPECT_EQ(state.retransmissionBufMetas[5].offset, 5); + EXPECT_EQ(state.retransmissionBufMetas[5].length, 8); + + EXPECT_EQ(state.retransmissionBufMetas[17].offset, 17); + EXPECT_EQ(state.retransmissionBufMetas[17].length, 3); +} + +TEST(StreamDataTest, WriteBufferMetaRemovalAll) { + QuicConnectionStateBase qcsb(QuicNodeType::Client); + QuicStreamState state(0, qcsb); + + // [5, 16] + state.writeBufMeta.offset = 5; + state.writeBufMeta.length = 12; + + state.removeFromWriteBufMetaAfterOffset(0); + EXPECT_EQ(state.writeBufMeta.length, 0); +} + +TEST(StreamDataTest, WriteBufferMetaRemoval) { + QuicConnectionStateBase qcsb(QuicNodeType::Client); + QuicStreamState state(0, qcsb); + + state.writeBufferStartOffset = 5; + + // [5, 16] + state.writeBufMeta.offset = 5; + state.writeBufMeta.length = 12; + + state.removeFromWriteBufMetaAfterOffset(5); + EXPECT_EQ(state.writeBufMeta.length, 1); +} + +TEST(StreamDataTest, WriteBufferMetaRemovalNoChange) { + QuicConnectionStateBase qcsb(QuicNodeType::Client); + QuicStreamState state(0, qcsb); + + state.writeBufferStartOffset = 5; + + // [5, 16] + state.writeBufMeta.offset = 5; + state.writeBufMeta.length = 12; + + state.removeFromWriteBufMetaAfterOffset(16); + EXPECT_EQ(state.writeBufMeta.length, 12); +} + } // namespace quic::test