LibMedia+Tests: Add a method to remove data from incremental streams

This will later be used to test updating of buffered time ranges when
data is removed. It will also be needed when data eviction is
implemented.
This commit is contained in:
Zaggy1024 2026-05-15 18:35:51 -05:00 committed by Gregory Bertilson
parent 054d6d1b17
commit fea33aa02f
3 changed files with 112 additions and 0 deletions

View file

@ -95,6 +95,35 @@ void IncrementallyPopulatedStream::add_chunk_at(u64 offset, ReadonlyBytes data)
m_state_changed.broadcast();
}
void IncrementallyPopulatedStream::remove_byte_range(u64 start, u64 end)
{
VERIFY(start < end);
Sync::MutexLocker locker { m_mutex };
auto chunk_iterator = m_chunks.find_largest_not_above_iterator(start);
if (chunk_iterator.is_end() || chunk_iterator->end() <= start)
chunk_iterator = m_chunks.find_smallest_not_below_iterator(start);
while (!chunk_iterator.is_end() && chunk_iterator->offset() < end) {
auto chunk = m_chunks.remove_and_advance(chunk_iterator);
auto chunk_start = chunk.offset();
auto chunk_end = chunk.end();
if (chunk_start < start) {
auto left_size = start - chunk_start;
m_chunks.insert(chunk_start, DataChunk { chunk_start, MUST(ByteBuffer::copy(chunk.data().bytes().trim(left_size))) });
}
if (chunk_end > end) {
auto right_offset_in_chunk = end - chunk_start;
m_chunks.insert(end, DataChunk { end, MUST(ByteBuffer::copy(chunk.data().bytes().slice(right_offset_in_chunk))) });
}
}
m_state_changed.broadcast();
}
Vector<MediaStream::ByteRange> IncrementallyPopulatedStream::available_byte_ranges() const
{
Sync::MutexLocker locker { m_mutex };

View file

@ -39,6 +39,7 @@ public:
void set_data_request_callback(DataRequestCallback);
void add_chunk_at(u64 offset, ReadonlyBytes);
void remove_byte_range(u64 start, u64 end);
u64 next_chunk_start() const { return m_last_chunk_end; }
void close();

View file

@ -273,6 +273,88 @@ TEST_CASE(redundant_chunk_within_existing_chunk_at_nonzero_offset)
EXPECT_EQ(buffer[i], static_cast<u8>(100 + i));
}
TEST_CASE(remove_byte_range_splits_chunk)
{
auto data = make_test_data(100);
auto stream = Media::IncrementallyPopulatedStream::create_from_data(data.bytes());
stream->remove_byte_range(30, 70);
auto ranges = stream->available_byte_ranges();
EXPECT_EQ(ranges.size(), 2u);
EXPECT_EQ(ranges[0].start, 0u);
EXPECT_EQ(ranges[0].end, 30u);
EXPECT_EQ(ranges[1].start, 70u);
EXPECT_EQ(ranges[1].end, 100u);
auto cursor = stream->create_cursor();
Array<u8, 30> buffer;
auto bytes_read = MUST(cursor->read_into(buffer));
EXPECT_EQ(bytes_read, 30u);
for (size_t i = 0; i < 30; i++)
EXPECT_EQ(buffer[i], static_cast<u8>(i));
MUST(cursor->seek(70, SeekMode::SetPosition));
bytes_read = MUST(cursor->read_into(buffer));
EXPECT_EQ(bytes_read, 30u);
for (size_t i = 0; i < 30; i++)
EXPECT_EQ(buffer[i], static_cast<u8>(70 + i));
}
TEST_CASE(remove_byte_range_trims_chunk)
{
auto data = make_test_data(100);
auto stream = Media::IncrementallyPopulatedStream::create_from_data(data.bytes());
stream->remove_byte_range(0, 20);
stream->remove_byte_range(80, 100);
auto ranges = stream->available_byte_ranges();
EXPECT_EQ(ranges.size(), 1u);
EXPECT_EQ(ranges[0].start, 20u);
EXPECT_EQ(ranges[0].end, 80u);
auto cursor = stream->create_cursor();
MUST(cursor->seek(20, SeekMode::SetPosition));
Array<u8, 60> buffer;
auto bytes_read = MUST(cursor->read_into(buffer));
EXPECT_EQ(bytes_read, 60u);
for (size_t i = 0; i < 60; i++)
EXPECT_EQ(buffer[i], static_cast<u8>(20 + i));
}
TEST_CASE(remove_byte_range_across_disjoint_chunks)
{
auto stream = Media::IncrementallyPopulatedStream::create_empty();
auto data = make_test_data(100);
stream->add_chunk_at(0, data.bytes().trim(20));
stream->add_chunk_at(40, data.bytes().slice(40, 20));
stream->add_chunk_at(80, data.bytes().slice(80));
stream->remove_byte_range(10, 90);
auto ranges = stream->available_byte_ranges();
EXPECT_EQ(ranges.size(), 2u);
EXPECT_EQ(ranges[0].start, 0u);
EXPECT_EQ(ranges[0].end, 10u);
EXPECT_EQ(ranges[1].start, 90u);
EXPECT_EQ(ranges[1].end, 100u);
auto cursor = stream->create_cursor();
Array<u8, 10> buffer;
auto bytes_read = MUST(cursor->read_into(buffer));
EXPECT_EQ(bytes_read, 10u);
for (size_t i = 0; i < 10; i++)
EXPECT_EQ(buffer[i], static_cast<u8>(i));
MUST(cursor->seek(90, SeekMode::SetPosition));
bytes_read = MUST(cursor->read_into(buffer));
EXPECT_EQ(bytes_read, 10u);
for (size_t i = 0; i < 10; i++)
EXPECT_EQ(buffer[i], static_cast<u8>(90 + i));
}
TEST_CASE(add_touching_chunks_forward)
{
auto stream = Media::IncrementallyPopulatedStream::create_empty();