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:
parent
054d6d1b17
commit
fea33aa02f
3 changed files with 112 additions and 0 deletions
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue