blob: ebd519275380f12c76032fe6ba46299c7005fd58 [file]
// Copyright 2023 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "iree/io/memory_stream.h"
#include <array>
#include <string>
#include <string_view>
#include "iree/base/api.h"
#include "iree/testing/gtest.h"
#include "iree/testing/status_matchers.h"
namespace {
using iree::Status;
using iree::StatusCode;
using iree::testing::status::StatusIs;
using testing::ElementsAre;
using testing::ElementsAreArray;
using testing::Eq;
TEST(MemoryStreamTest, Wrap) {
uint8_t data[5] = {0, 1, 2, 3, 4};
iree_io_stream_t* stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_READABLE, iree_make_byte_span(data, sizeof(data)),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&stream));
EXPECT_EQ(iree_io_stream_mode(stream), IREE_IO_STREAM_MODE_READABLE);
EXPECT_EQ(iree_io_stream_offset(stream), 0);
EXPECT_EQ(iree_io_stream_length(stream), sizeof(data));
EXPECT_FALSE(iree_io_stream_is_eos(stream));
iree_io_stream_release(stream);
}
TEST(MemoryStreamTest, WrapEmpty) {
uint8_t data[1] = {0};
iree_io_stream_t* stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_READABLE, iree_make_byte_span(data, 0),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&stream));
EXPECT_EQ(iree_io_stream_mode(stream), IREE_IO_STREAM_MODE_READABLE);
EXPECT_EQ(iree_io_stream_offset(stream), 0);
EXPECT_EQ(iree_io_stream_length(stream), 0);
EXPECT_TRUE(iree_io_stream_is_eos(stream));
iree_io_stream_release(stream);
}
TEST(MemoryStreamTest, WrapReleaseCallback) {
int callback_count = 0;
iree_io_memory_stream_release_callback_t release_callback = {
+[](void* user_data, iree_io_stream_t* stream) {
int* callback_count = (int*)user_data;
++(*callback_count);
},
&callback_count,
};
ASSERT_EQ(callback_count, 0);
uint8_t data[1] = {0};
iree_io_stream_t* stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_READABLE, iree_make_byte_span(data, sizeof(data)),
release_callback, iree_allocator_system(), &stream));
ASSERT_EQ(callback_count, 0);
iree_io_stream_release(stream);
ASSERT_EQ(callback_count, 1);
}
TEST(MemoryStreamTest, SeekSet) {
uint8_t data[5] = {0, 1, 2, 3, 4};
iree_io_stream_t* stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_READABLE, iree_make_byte_span(data, sizeof(data)),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&stream));
// Streams start at origin 0.
EXPECT_EQ(iree_io_stream_offset(stream), 0);
EXPECT_EQ(iree_io_stream_length(stream), sizeof(data));
EXPECT_FALSE(iree_io_stream_is_eos(stream));
// No-op seek to origin.
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 0));
EXPECT_EQ(iree_io_stream_offset(stream), 0);
EXPECT_FALSE(iree_io_stream_is_eos(stream));
// Seek to end-of-stream.
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET,
iree_io_stream_length(stream)));
EXPECT_EQ(iree_io_stream_offset(stream), iree_io_stream_length(stream));
EXPECT_TRUE(iree_io_stream_is_eos(stream));
// Seek to absolute offset 1.
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 1));
EXPECT_EQ(iree_io_stream_offset(stream), 1);
EXPECT_FALSE(iree_io_stream_is_eos(stream));
// Seek to absolute offset length-1 (last valid byte).
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 4));
EXPECT_EQ(iree_io_stream_offset(stream), 4);
EXPECT_FALSE(iree_io_stream_is_eos(stream));
// Try seeking out of bounds.
EXPECT_THAT(Status(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, -1)),
StatusIs(StatusCode::kOutOfRange));
EXPECT_THAT(Status(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 6)),
StatusIs(StatusCode::kOutOfRange));
iree_io_stream_release(stream);
}
TEST(MemoryStreamTest, SeekFromCurrent) {
uint8_t data[5] = {0, 1, 2, 3, 4};
iree_io_stream_t* stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_READABLE, iree_make_byte_span(data, sizeof(data)),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&stream));
// Streams start at origin 0.
EXPECT_EQ(iree_io_stream_offset(stream), 0);
EXPECT_EQ(iree_io_stream_length(stream), sizeof(data));
EXPECT_FALSE(iree_io_stream_is_eos(stream));
// Seek to end-of-stream by jumping the full length.
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_CURRENT,
iree_io_stream_length(stream)));
EXPECT_EQ(iree_io_stream_offset(stream), iree_io_stream_length(stream));
EXPECT_TRUE(iree_io_stream_is_eos(stream));
// Reset back to origin by seeking back the full length.
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_CURRENT,
-iree_io_stream_length(stream)));
EXPECT_EQ(iree_io_stream_offset(stream), 0);
EXPECT_FALSE(iree_io_stream_is_eos(stream));
// Seek forward to absolute position 1.
IREE_EXPECT_OK(
iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_CURRENT, 1));
EXPECT_EQ(iree_io_stream_offset(stream), 1);
EXPECT_FALSE(iree_io_stream_is_eos(stream));
// No-op seek to current location (absolute 1).
IREE_EXPECT_OK(
iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_CURRENT, 0));
EXPECT_EQ(iree_io_stream_offset(stream), 1);
EXPECT_FALSE(iree_io_stream_is_eos(stream));
// Seek to absolute offset length-1 (last valid byte) - here (5-1) - 1 = 3.
IREE_EXPECT_OK(
iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_CURRENT, 3));
EXPECT_EQ(iree_io_stream_offset(stream), 4);
EXPECT_FALSE(iree_io_stream_is_eos(stream));
// Seek forward 1 to absolute end-of-stream.
IREE_EXPECT_OK(
iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_CURRENT, 1));
EXPECT_EQ(iree_io_stream_offset(stream), iree_io_stream_length(stream));
EXPECT_TRUE(iree_io_stream_is_eos(stream));
// Reset back to origin.
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 0));
// Try seeking out of bounds.
EXPECT_THAT(Status(iree_io_stream_seek(
stream, IREE_IO_STREAM_SEEK_FROM_CURRENT, -100)),
StatusIs(StatusCode::kOutOfRange));
EXPECT_THAT(Status(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 600)),
StatusIs(StatusCode::kOutOfRange));
iree_io_stream_release(stream);
}
TEST(MemoryStreamTest, SeekFromEnd) {
uint8_t data[5] = {0, 1, 2, 3, 4};
iree_io_stream_t* stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_READABLE, iree_make_byte_span(data, sizeof(data)),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&stream));
// Streams start at origin 0.
EXPECT_EQ(iree_io_stream_offset(stream), 0);
EXPECT_EQ(iree_io_stream_length(stream), sizeof(data));
EXPECT_FALSE(iree_io_stream_is_eos(stream));
// Jump to end-of-stream.
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, 0));
EXPECT_EQ(iree_io_stream_offset(stream), iree_io_stream_length(stream));
EXPECT_TRUE(iree_io_stream_is_eos(stream));
// Reset back to origin by seeking back the full length.
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END,
-iree_io_stream_length(stream)));
EXPECT_EQ(iree_io_stream_offset(stream), 0);
EXPECT_FALSE(iree_io_stream_is_eos(stream));
// Seek to absolute offset length-1 (last valid byte) - here 5 - 1 = 4.
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, -1));
EXPECT_EQ(iree_io_stream_offset(stream), 4);
EXPECT_FALSE(iree_io_stream_is_eos(stream));
// Reset back to origin.
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 0));
// Try seeking out of bounds.
EXPECT_THAT(
Status(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, -100)),
StatusIs(StatusCode::kOutOfRange));
EXPECT_THAT(
Status(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, 600)),
StatusIs(StatusCode::kOutOfRange));
iree_io_stream_release(stream);
}
TEST(MemoryStreamTest, SeekToAlignment) {
uint8_t data[5] = {0, 1, 2, 3, 4};
iree_io_stream_t* stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_READABLE, iree_make_byte_span(data, sizeof(data)),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&stream));
// Streams start at origin 0.
EXPECT_EQ(iree_io_stream_offset(stream), 0);
EXPECT_EQ(iree_io_stream_length(stream), sizeof(data));
EXPECT_FALSE(iree_io_stream_is_eos(stream));
// Alignment must be a power of two.
EXPECT_THAT(Status(iree_io_stream_seek_to_alignment(stream, 3)),
StatusIs(StatusCode::kInvalidArgument));
EXPECT_THAT(Status(iree_io_stream_seek_to_alignment(stream, 63)),
StatusIs(StatusCode::kInvalidArgument));
EXPECT_THAT(Status(iree_io_stream_seek_to_alignment(stream, -2)),
StatusIs(StatusCode::kInvalidArgument));
// Alignment at 0 should always be ok.
IREE_EXPECT_OK(iree_io_stream_seek_to_alignment(stream, 0));
EXPECT_EQ(iree_io_stream_offset(stream), 0);
IREE_EXPECT_OK(iree_io_stream_seek_to_alignment(stream, 1));
EXPECT_EQ(iree_io_stream_offset(stream), 0);
IREE_EXPECT_OK(iree_io_stream_seek_to_alignment(stream, 2));
EXPECT_EQ(iree_io_stream_offset(stream), 0);
// Seek forward to an unaligned absolute offset 1.
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 1));
EXPECT_EQ(iree_io_stream_offset(stream), 1);
// Seek forward to alignment 2, which should be absolute offset 2.
IREE_EXPECT_OK(iree_io_stream_seek_to_alignment(stream, 2));
EXPECT_EQ(iree_io_stream_offset(stream), 2);
// Alignment that matches the current offset (2) should be a no-op.
IREE_EXPECT_OK(iree_io_stream_seek_to_alignment(stream, 2));
EXPECT_EQ(iree_io_stream_offset(stream), 2);
// Align up from an aligned value.
IREE_EXPECT_OK(iree_io_stream_seek_to_alignment(stream, 4));
EXPECT_EQ(iree_io_stream_offset(stream), 4);
// Try aligning off the end of the stream.
EXPECT_THAT(Status(iree_io_stream_seek_to_alignment(stream, 16)),
StatusIs(StatusCode::kOutOfRange));
iree_io_stream_release(stream);
}
TEST(MemoryStreamTest, ReadUpTo) {
uint8_t data[5] = {0, 1, 2, 3, 4};
iree_io_stream_t* stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_READABLE, iree_make_byte_span(data, sizeof(data)),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&stream));
// Streams start at origin 0.
EXPECT_EQ(iree_io_stream_offset(stream), 0);
EXPECT_EQ(iree_io_stream_length(stream), sizeof(data));
EXPECT_FALSE(iree_io_stream_is_eos(stream));
uint8_t read_buffer[64] = {0xDD};
iree_host_size_t read_length = 0;
// Reads of zero length should no-op.
IREE_EXPECT_OK(iree_io_stream_read(stream, 0, read_buffer, &read_length));
EXPECT_EQ(read_length, 0);
EXPECT_EQ(iree_io_stream_offset(stream), 0);
// Reads should advance the stream offset.
memset(read_buffer, 0xDD, sizeof(read_buffer));
IREE_EXPECT_OK(iree_io_stream_read(stream, 1, read_buffer, &read_length));
EXPECT_EQ(read_length, 1);
EXPECT_EQ(iree_io_stream_offset(stream), 1);
EXPECT_EQ(read_buffer[0], 0);
EXPECT_EQ(read_buffer[1], 0xDD);
// Read another chunk of 2 bytes.
memset(read_buffer, 0xDD, sizeof(read_buffer));
IREE_EXPECT_OK(iree_io_stream_read(stream, 2, read_buffer, &read_length));
EXPECT_EQ(read_length, 2);
EXPECT_EQ(iree_io_stream_offset(stream), 3);
EXPECT_EQ(read_buffer[0], 1);
EXPECT_EQ(read_buffer[1], 2);
EXPECT_EQ(read_buffer[2], 0xDD);
// Read up to the end of the stream (2 bytes remaining) by reading over.
memset(read_buffer, 0xDD, sizeof(read_buffer));
IREE_EXPECT_OK(iree_io_stream_read(stream, sizeof(read_buffer), read_buffer,
&read_length));
EXPECT_EQ(read_length, 2);
EXPECT_EQ(iree_io_stream_offset(stream), iree_io_stream_length(stream));
EXPECT_TRUE(iree_io_stream_is_eos(stream));
EXPECT_EQ(read_buffer[0], 3);
EXPECT_EQ(read_buffer[1], 4);
EXPECT_EQ(read_buffer[2], 0xDD);
// Reading from the end of the stream should be a no-op.
memset(read_buffer, 0xDD, sizeof(read_buffer));
IREE_EXPECT_OK(iree_io_stream_read(stream, sizeof(read_buffer), read_buffer,
&read_length));
EXPECT_EQ(read_length, 0);
EXPECT_EQ(iree_io_stream_offset(stream), iree_io_stream_length(stream));
EXPECT_TRUE(iree_io_stream_is_eos(stream));
EXPECT_EQ(read_buffer[0], 0xDD);
iree_io_stream_release(stream);
}
TEST(MemoryStreamTest, ReadExact) {
uint8_t data[5] = {0, 1, 2, 3, 4};
iree_io_stream_t* stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_READABLE, iree_make_byte_span(data, sizeof(data)),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&stream));
// Streams start at origin 0.
EXPECT_EQ(iree_io_stream_offset(stream), 0);
EXPECT_EQ(iree_io_stream_length(stream), sizeof(data));
EXPECT_FALSE(iree_io_stream_is_eos(stream));
uint8_t read_buffer[64] = {0xDD};
// Reads of zero length should no-op.
IREE_EXPECT_OK(iree_io_stream_read(stream, 0, read_buffer, NULL));
EXPECT_EQ(iree_io_stream_offset(stream), 0);
// Reads should advance the stream offset.
memset(read_buffer, 0xDD, sizeof(read_buffer));
IREE_EXPECT_OK(iree_io_stream_read(stream, 1, read_buffer, NULL));
EXPECT_EQ(iree_io_stream_offset(stream), 1);
EXPECT_EQ(read_buffer[0], 0);
EXPECT_EQ(read_buffer[1], 0xDD);
// Read another chunk of 2 bytes.
memset(read_buffer, 0xDD, sizeof(read_buffer));
IREE_EXPECT_OK(iree_io_stream_read(stream, 2, read_buffer, NULL));
EXPECT_EQ(iree_io_stream_offset(stream), 3);
EXPECT_EQ(read_buffer[0], 1);
EXPECT_EQ(read_buffer[1], 2);
EXPECT_EQ(read_buffer[2], 0xDD);
// Read up to the end of the stream (2 bytes remaining) by reading over.
memset(read_buffer, 0xDD, sizeof(read_buffer));
IREE_EXPECT_OK(iree_io_stream_read(stream, 2, read_buffer, NULL));
EXPECT_EQ(iree_io_stream_offset(stream), iree_io_stream_length(stream));
EXPECT_TRUE(iree_io_stream_is_eos(stream));
EXPECT_EQ(read_buffer[0], 3);
EXPECT_EQ(read_buffer[1], 4);
EXPECT_EQ(read_buffer[2], 0xDD);
// Reading from the end of the stream fails with no read length arg.
memset(read_buffer, 0xDD, sizeof(read_buffer));
EXPECT_THAT(Status(iree_io_stream_read(stream, sizeof(read_buffer),
read_buffer, NULL)),
StatusIs(StatusCode::kOutOfRange));
// Reset back to the origin and try reading off the end.
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 0));
EXPECT_THAT(Status(iree_io_stream_read(stream, sizeof(read_buffer),
read_buffer, NULL)),
StatusIs(StatusCode::kOutOfRange));
EXPECT_EQ(iree_io_stream_offset(stream), 0);
iree_io_stream_release(stream);
}
TEST(MemoryStreamTest, Write) {
uint8_t data[5] = {0xDD};
iree_io_stream_t* stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_WRITABLE, iree_make_byte_span(data, sizeof(data)),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&stream));
const uint8_t write_buffer[8] = {0, 1, 2, 3, 4, 5, 6, 7};
// Writes of zero length should be a no-op.
memset(data, 0xDD, sizeof(data));
IREE_EXPECT_OK(iree_io_stream_write(stream, 0, write_buffer));
EXPECT_EQ(iree_io_stream_offset(stream), 0);
EXPECT_EQ(data[0], 0xDD);
// Writes should advance the stream.
memset(data, 0xDD, sizeof(data));
IREE_EXPECT_OK(iree_io_stream_write(stream, 1, write_buffer));
EXPECT_EQ(iree_io_stream_offset(stream), 1);
EXPECT_EQ(data[0], 0);
EXPECT_EQ(data[1], 0xDD);
// Write 2 more bytes and ensure only those are mutated.
memset(data, 0xDD, sizeof(data));
IREE_EXPECT_OK(iree_io_stream_write(stream, 2, &write_buffer[1]));
EXPECT_EQ(iree_io_stream_offset(stream), 1 + 2);
EXPECT_EQ(data[0], 0xDD);
EXPECT_EQ(data[1], 1);
EXPECT_EQ(data[2], 2);
EXPECT_EQ(data[3], 0xDD);
// Writes off the end of the stream should fail.
EXPECT_THAT(
Status(iree_io_stream_write(stream, sizeof(write_buffer), write_buffer)),
StatusIs(StatusCode::kOutOfRange));
// Seek to the end of the stream and try to write 0 bytes (should be a no-op).
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, 0));
EXPECT_TRUE(iree_io_stream_is_eos(stream));
IREE_EXPECT_OK(iree_io_stream_write(stream, 0, write_buffer));
EXPECT_TRUE(iree_io_stream_is_eos(stream));
// Writing off the end of the stream should fail.
EXPECT_THAT(Status(iree_io_stream_write(stream, 1, write_buffer)),
StatusIs(StatusCode::kOutOfRange));
// Overwrite the entire contents of the storage.
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 0));
IREE_EXPECT_OK(iree_io_stream_write(stream, sizeof(data), write_buffer));
EXPECT_THAT(data,
ElementsAre(write_buffer[0], write_buffer[1], write_buffer[2],
write_buffer[3], write_buffer[4]));
iree_io_stream_release(stream);
}
TEST(MemoryStreamTest, Fill) {
uint8_t data[16] = {0xDD};
iree_io_stream_t* stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_WRITABLE, iree_make_byte_span(data, sizeof(data)),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&stream));
uint8_t pattern[] = {0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0};
// Fill patterns must be 1,2,4,8 bytes.
EXPECT_THAT(Status(iree_io_stream_fill(stream, 1, pattern, 3)),
StatusIs(StatusCode::kInvalidArgument));
EXPECT_THAT(Status(iree_io_stream_fill(stream, 1, pattern, 9)),
StatusIs(StatusCode::kInvalidArgument));
// Fills are bounds checked.
EXPECT_THAT(Status(iree_io_stream_fill(stream, 100, pattern, 1)),
StatusIs(StatusCode::kOutOfRange));
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, 0));
EXPECT_THAT(Status(iree_io_stream_fill(stream, 1, pattern, 1)),
StatusIs(StatusCode::kOutOfRange));
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, -1));
EXPECT_THAT(Status(iree_io_stream_fill(stream, 2, pattern, 1)),
StatusIs(StatusCode::kOutOfRange));
// Fill with pattern size 1.
memset(data, 0xDD, sizeof(data));
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 1));
IREE_EXPECT_OK(iree_io_stream_fill(stream, 3, pattern, 1));
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, -2));
IREE_EXPECT_OK(iree_io_stream_fill(stream, 2, pattern, 1));
EXPECT_THAT(data,
ElementsAre(0xDD, 0x80, 0x80, 0x80, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0x80, 0x80));
// Fill with pattern size 2.
memset(data, 0xDD, sizeof(data));
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 1));
IREE_EXPECT_OK(iree_io_stream_fill(stream, 3, pattern, 2));
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, -4));
IREE_EXPECT_OK(iree_io_stream_fill(stream, 2, pattern, 2));
EXPECT_THAT(data,
ElementsAre(0xDD, 0x80, 0x90, 0x80, 0x90, 0x80, 0x90, 0xDD, 0xDD,
0xDD, 0xDD, 0xDD, 0x80, 0x90, 0x80, 0x90));
// Fill with pattern size 4.
memset(data, 0xDD, sizeof(data));
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 1));
IREE_EXPECT_OK(iree_io_stream_fill(stream, 2, pattern, 4));
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, -4));
IREE_EXPECT_OK(iree_io_stream_fill(stream, 1, pattern, 4));
EXPECT_THAT(data,
ElementsAre(0xDD, 0x80, 0x90, 0xA0, 0xB0, 0x80, 0x90, 0xA0, 0xB0,
0xDD, 0xDD, 0xDD, 0x80, 0x90, 0xA0, 0xB0));
// Fill with pattern size 8.
memset(data, 0xDD, sizeof(data));
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 1));
IREE_EXPECT_OK(iree_io_stream_fill(stream, 1, pattern, 8));
EXPECT_THAT(data,
ElementsAre(0xDD, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD));
memset(data, 0xDD, sizeof(data));
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, -8));
IREE_EXPECT_OK(iree_io_stream_fill(stream, 1, pattern, 8));
EXPECT_THAT(data,
ElementsAre(0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0x80,
0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0));
iree_io_stream_release(stream);
}
TEST(MemoryStreamTest, MapRead) {
uint8_t data[5] = {0xDD};
iree_io_stream_t* stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_READABLE | IREE_IO_STREAM_MODE_MAPPABLE,
iree_make_byte_span(data, sizeof(data)),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&stream));
iree_const_byte_span_t span = iree_const_byte_span_empty();
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 1));
IREE_EXPECT_OK(iree_io_stream_map_read(stream, 2, &span));
EXPECT_EQ(span.data, &data[1]);
EXPECT_EQ(span.data_length, 2);
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, -1));
IREE_EXPECT_OK(iree_io_stream_map_read(stream, 1, &span));
EXPECT_EQ(span.data, &data[4]);
EXPECT_EQ(span.data_length, 1);
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 0));
EXPECT_THAT(Status(iree_io_stream_map_read(stream, 100, &span)),
StatusIs(StatusCode::kOutOfRange));
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, 0));
EXPECT_THAT(Status(iree_io_stream_map_read(stream, 1, &span)),
StatusIs(StatusCode::kOutOfRange));
iree_io_stream_release(stream);
}
TEST(MemoryStreamTest, MapWrite) {
uint8_t data[5] = {0xDD};
iree_io_stream_t* stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_WRITABLE | IREE_IO_STREAM_MODE_MAPPABLE,
iree_make_byte_span(data, sizeof(data)),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&stream));
iree_byte_span_t span = iree_byte_span_empty();
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 1));
IREE_EXPECT_OK(iree_io_stream_map_write(stream, 2, &span));
EXPECT_EQ(span.data, &data[1]);
EXPECT_EQ(span.data_length, 2);
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, -1));
IREE_EXPECT_OK(iree_io_stream_map_write(stream, 1, &span));
EXPECT_EQ(span.data, &data[4]);
EXPECT_EQ(span.data_length, 1);
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_SET, 0));
EXPECT_THAT(Status(iree_io_stream_map_write(stream, 100, &span)),
StatusIs(StatusCode::kOutOfRange));
IREE_EXPECT_OK(iree_io_stream_seek(stream, IREE_IO_STREAM_SEEK_FROM_END, 0));
EXPECT_THAT(Status(iree_io_stream_map_write(stream, 1, &span)),
StatusIs(StatusCode::kOutOfRange));
iree_io_stream_release(stream);
}
// Tests copies that fit within a single block.
TEST(MemoryStreamTest, Copy) {
uint8_t source_data[5] = {0xA0, 0xB0, 0xC0, 0xD0, 0xE0};
iree_io_stream_t* source_stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_READABLE,
iree_make_byte_span(source_data, sizeof(source_data)),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&source_stream));
uint8_t target_data[5] = {0xDD};
iree_io_stream_t* target_stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_WRITABLE,
iree_make_byte_span(target_data, sizeof(target_data)),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&target_stream));
// Bounds checks length.
EXPECT_THAT(Status(iree_io_stream_copy(source_stream, target_stream, 100)),
StatusIs(StatusCode::kOutOfRange));
// Bounds check source.
IREE_EXPECT_OK(
iree_io_stream_seek(source_stream, IREE_IO_STREAM_SEEK_FROM_END, 0));
IREE_EXPECT_OK(
iree_io_stream_seek(target_stream, IREE_IO_STREAM_SEEK_SET, 0));
EXPECT_THAT(Status(iree_io_stream_copy(source_stream, target_stream, 1)),
StatusIs(StatusCode::kOutOfRange));
// Bounds check target.
IREE_EXPECT_OK(
iree_io_stream_seek(source_stream, IREE_IO_STREAM_SEEK_SET, 0));
IREE_EXPECT_OK(
iree_io_stream_seek(target_stream, IREE_IO_STREAM_SEEK_FROM_END, 0));
EXPECT_THAT(Status(iree_io_stream_copy(source_stream, target_stream, 1)),
StatusIs(StatusCode::kOutOfRange));
// Copy entire contents.
memset(target_data, 0xDD, sizeof(target_data));
IREE_EXPECT_OK(
iree_io_stream_seek(source_stream, IREE_IO_STREAM_SEEK_SET, 0));
IREE_EXPECT_OK(
iree_io_stream_seek(target_stream, IREE_IO_STREAM_SEEK_SET, 0));
IREE_EXPECT_OK(
iree_io_stream_copy(source_stream, target_stream, sizeof(target_data)));
EXPECT_THAT(target_data, ElementsAreArray(source_data));
// Copy an interior subrange.
memset(target_data, 0xDD, sizeof(target_data));
IREE_EXPECT_OK(
iree_io_stream_seek(source_stream, IREE_IO_STREAM_SEEK_SET, 0));
IREE_EXPECT_OK(
iree_io_stream_seek(target_stream, IREE_IO_STREAM_SEEK_SET, 1));
IREE_EXPECT_OK(iree_io_stream_copy(source_stream, target_stream, 2));
EXPECT_THAT(target_data, ElementsAre(0xDD, 0xA0, 0xB0, 0xDD, 0xDD));
// Copy to up to the end.
memset(target_data, 0xDD, sizeof(target_data));
IREE_EXPECT_OK(
iree_io_stream_seek(source_stream, IREE_IO_STREAM_SEEK_SET, 0));
IREE_EXPECT_OK(
iree_io_stream_seek(target_stream, IREE_IO_STREAM_SEEK_FROM_END, -2));
IREE_EXPECT_OK(iree_io_stream_copy(source_stream, target_stream, 2));
EXPECT_THAT(target_data, ElementsAre(0xDD, 0xDD, 0xDD, 0xA0, 0xB0));
iree_io_stream_release(source_stream);
iree_io_stream_release(target_stream);
}
// Tests a copy that should trip into the multi-block code path.
TEST(MemoryStreamTest, CopyLarge) {
std::vector<uint8_t> source_data(1 * 1024 * 1024);
for (size_t i = 0; i < source_data.size(); ++i) {
source_data[i] = (uint8_t)i;
}
iree_io_stream_t* source_stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_READABLE,
iree_make_byte_span(source_data.data(), source_data.size()),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&source_stream));
std::vector<uint8_t> target_data(1 * 1024 * 1024);
iree_io_stream_t* target_stream = NULL;
IREE_ASSERT_OK(iree_io_memory_stream_wrap(
IREE_IO_STREAM_MODE_WRITABLE,
iree_make_byte_span(target_data.data(), target_data.size()),
iree_io_memory_stream_release_callback_null(), iree_allocator_system(),
&target_stream));
// Copy an interior subrange.
memset(target_data.data(), 0xDD, target_data.size());
IREE_EXPECT_OK(
iree_io_stream_seek(source_stream, IREE_IO_STREAM_SEEK_SET, 1));
IREE_EXPECT_OK(
iree_io_stream_seek(target_stream, IREE_IO_STREAM_SEEK_SET, 2));
IREE_EXPECT_OK(iree_io_stream_copy(source_stream, target_stream,
source_data.size() - 10));
for (size_t i = 0; i < target_data.size(); ++i) {
if (i < 2) {
// Before the copy range.
EXPECT_EQ(target_data[i], 0xDD);
} else if (i < source_data.size() - 10 + 2) {
// In the copy range.
EXPECT_EQ(target_data[i], source_data[1 + i - 2]);
} else {
// After the copy range.
EXPECT_EQ(target_data[i], 0xDD);
}
}
iree_io_stream_release(source_stream);
iree_io_stream_release(target_stream);
}
} // namespace