mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 16:22:43 +00:00 
			
		
		
		
	AK+LibCore: Make output buffered stream seekable
Just like with input buffered streams, we don't currently have a use case for output buffered streams which aren't seekable, since the main application are files.
This commit is contained in:
		
							parent
							
								
									c2d9d0277f
								
							
						
					
					
						commit
						001ea22917
					
				
					 3 changed files with 46 additions and 10 deletions
				
			
		|  | @ -310,10 +310,10 @@ private: | ||||||
|     BufferedHelper<T> m_helper; |     BufferedHelper<T> m_helper; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template<StreamLike T> | template<SeekableStreamLike T> | ||||||
| class OutputBufferedStream final : public Stream { | class OutputBufferedSeekable : public SeekableStream { | ||||||
| public: | public: | ||||||
|     static ErrorOr<NonnullOwnPtr<OutputBufferedStream<T>>> create(NonnullOwnPtr<T> stream, size_t buffer_size = 16 * KiB) |     static ErrorOr<NonnullOwnPtr<OutputBufferedSeekable<T>>> create(NonnullOwnPtr<T> stream, size_t buffer_size = 16 * KiB) | ||||||
|     { |     { | ||||||
|         if (buffer_size == 0) |         if (buffer_size == 0) | ||||||
|             return Error::from_errno(EINVAL); |             return Error::from_errno(EINVAL); | ||||||
|  | @ -322,11 +322,11 @@ public: | ||||||
| 
 | 
 | ||||||
|         auto buffer = TRY(CircularBuffer::create_empty(buffer_size)); |         auto buffer = TRY(CircularBuffer::create_empty(buffer_size)); | ||||||
| 
 | 
 | ||||||
|         return adopt_nonnull_own_or_enomem(new OutputBufferedStream<T>(move(stream), move(buffer))); |         return adopt_nonnull_own_or_enomem(new OutputBufferedSeekable<T>(move(stream), move(buffer))); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     OutputBufferedStream(OutputBufferedStream&& other) = default; |     OutputBufferedSeekable(OutputBufferedSeekable&& other) = default; | ||||||
|     OutputBufferedStream& operator=(OutputBufferedStream&& other) = default; |     OutputBufferedSeekable& operator=(OutputBufferedSeekable&& other) = default; | ||||||
| 
 | 
 | ||||||
|     virtual ErrorOr<Bytes> read_some(Bytes buffer) override |     virtual ErrorOr<Bytes> read_some(Bytes buffer) override | ||||||
|     { |     { | ||||||
|  | @ -368,13 +368,31 @@ public: | ||||||
|         return {}; |         return {}; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     virtual ~OutputBufferedStream() override |     // Since tell() doesn't involve moving the write offset, we can skip flushing the buffer here.
 | ||||||
|  |     virtual ErrorOr<size_t> tell() const override | ||||||
|  |     { | ||||||
|  |         return TRY(m_stream->tell()) + m_buffer.used_space(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     virtual ErrorOr<size_t> seek(i64 offset, SeekMode mode) override | ||||||
|  |     { | ||||||
|  |         TRY(flush_buffer()); | ||||||
|  |         return m_stream->seek(offset, mode); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     virtual ErrorOr<void> truncate(size_t length) override | ||||||
|  |     { | ||||||
|  |         TRY(flush_buffer()); | ||||||
|  |         return m_stream->truncate(length); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     virtual ~OutputBufferedSeekable() override | ||||||
|     { |     { | ||||||
|         MUST(flush_buffer()); |         MUST(flush_buffer()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     OutputBufferedStream(NonnullOwnPtr<T> stream, CircularBuffer buffer) |     OutputBufferedSeekable(NonnullOwnPtr<T> stream, CircularBuffer buffer) | ||||||
|         : m_stream(move(stream)) |         : m_stream(move(stream)) | ||||||
|         , m_buffer(move(buffer)) |         , m_buffer(move(buffer)) | ||||||
|     { |     { | ||||||
|  | @ -389,5 +407,5 @@ private: | ||||||
| #if USING_AK_GLOBALLY | #if USING_AK_GLOBALLY | ||||||
| using AK::BufferedHelper; | using AK::BufferedHelper; | ||||||
| using AK::InputBufferedSeekable; | using AK::InputBufferedSeekable; | ||||||
| using AK::OutputBufferedStream; | using AK::OutputBufferedSeekable; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -111,6 +111,24 @@ BENCHMARK_CASE(file_tell) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | TEST_CASE(file_buffered_write_and_seek) | ||||||
|  | { | ||||||
|  |     auto file = TRY_OR_FAIL(Core::OutputBufferedFile::create(TRY_OR_FAIL(Core::File::open("/tmp/file-buffered-write-test.txt"sv, Core::File::OpenMode::Truncate | Core::File::OpenMode::ReadWrite)))); | ||||||
|  | 
 | ||||||
|  |     TRY_OR_FAIL(file->write_some("0123456789"sv.bytes())); | ||||||
|  |     EXPECT_EQ(file->tell().release_value(), 10ul); | ||||||
|  | 
 | ||||||
|  |     // Reads don't go through the buffer, so after we seek, the data must be available from the underlying file.
 | ||||||
|  |     TRY_OR_FAIL(file->seek(0, AK::SeekMode::SetPosition)); | ||||||
|  |     auto first_byte = TRY_OR_FAIL(file->read_value<u8>()); | ||||||
|  |     EXPECT_EQ(first_byte, static_cast<u8>('0')); | ||||||
|  | 
 | ||||||
|  |     TRY_OR_FAIL(file->seek(9, AK::SeekMode::SetPosition)); | ||||||
|  |     auto last_byte = TRY_OR_FAIL(file->read_value<u8>()); | ||||||
|  |     EXPECT_EQ(last_byte, static_cast<u8>('9')); | ||||||
|  |     EXPECT_EQ(file->tell().release_value(), 10ul); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| TEST_CASE(file_adopt_fd) | TEST_CASE(file_adopt_fd) | ||||||
| { | { | ||||||
|     int rc = ::open("/usr/Tests/LibCore/long_lines.txt", O_RDONLY); |     int rc = ::open("/usr/Tests/LibCore/long_lines.txt", O_RDONLY); | ||||||
|  |  | ||||||
|  | @ -117,6 +117,6 @@ private: | ||||||
| AK_ENUM_BITWISE_OPERATORS(File::OpenMode) | AK_ENUM_BITWISE_OPERATORS(File::OpenMode) | ||||||
| 
 | 
 | ||||||
| using InputBufferedFile = InputBufferedSeekable<File>; | using InputBufferedFile = InputBufferedSeekable<File>; | ||||||
| using OutputBufferedFile = OutputBufferedStream<File>; | using OutputBufferedFile = OutputBufferedSeekable<File>; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 kleines Filmröllchen
						kleines Filmröllchen