Serenity Operating System
1/*
2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/ByteBuffer.h>
9#include <AK/Format.h>
10#include <AK/Stream.h>
11
12namespace AK {
13
14ErrorOr<void> Stream::read_until_filled(Bytes buffer)
15{
16 size_t nread = 0;
17 while (nread < buffer.size()) {
18 if (is_eof())
19 return Error::from_string_view_or_print_error_and_return_errno("Reached end-of-file before filling the entire buffer"sv, EIO);
20
21 auto result = read_some(buffer.slice(nread));
22 if (result.is_error()) {
23 if (result.error().is_errno() && result.error().code() == EINTR) {
24 continue;
25 }
26
27 return result.release_error();
28 }
29
30 nread += result.value().size();
31 }
32
33 return {};
34}
35
36ErrorOr<ByteBuffer> Stream::read_until_eof(size_t block_size)
37{
38 return read_until_eof_impl(block_size);
39}
40
41ErrorOr<ByteBuffer> Stream::read_until_eof_impl(size_t block_size, size_t expected_file_size)
42{
43 ByteBuffer data;
44 data.ensure_capacity(expected_file_size);
45
46 size_t total_read = 0;
47 Bytes buffer;
48 while (!is_eof()) {
49 if (buffer.is_empty()) {
50 buffer = TRY(data.get_bytes_for_writing(block_size));
51 }
52
53 auto nread = TRY(read_some(buffer)).size();
54 total_read += nread;
55 buffer = buffer.slice(nread);
56 }
57
58 data.resize(total_read);
59 return data;
60}
61
62ErrorOr<void> Stream::discard(size_t discarded_bytes)
63{
64 // Note: This was chosen arbitrarily.
65 // Note: This can't be PAGE_SIZE because it is defined to sysconf() on Lagom.
66 constexpr size_t continuous_read_size = 4096;
67
68 Array<u8, continuous_read_size> buffer;
69
70 while (discarded_bytes > 0) {
71 if (is_eof())
72 return Error::from_string_view_or_print_error_and_return_errno("Reached end-of-file before reading all discarded bytes"sv, EIO);
73
74 auto slice = TRY(read_some(buffer.span().slice(0, min(discarded_bytes, continuous_read_size))));
75 discarded_bytes -= slice.size();
76 }
77
78 return {};
79}
80
81ErrorOr<void> Stream::write_until_depleted(ReadonlyBytes buffer)
82{
83 size_t nwritten = 0;
84 while (nwritten < buffer.size()) {
85 auto result = write_some(buffer.slice(nwritten));
86 if (result.is_error()) {
87 if (result.error().is_errno() && result.error().code() == EINTR) {
88 continue;
89 }
90
91 return result.release_error();
92 }
93
94 nwritten += result.value();
95 }
96
97 return {};
98}
99
100ErrorOr<size_t> SeekableStream::tell() const
101{
102 // Seek with 0 and SEEK_CUR does not modify anything despite the const_cast,
103 // so it's safe to do this.
104 return const_cast<SeekableStream*>(this)->seek(0, SeekMode::FromCurrentPosition);
105}
106
107ErrorOr<size_t> SeekableStream::size()
108{
109 auto original_position = TRY(tell());
110
111 auto seek_result = seek(0, SeekMode::FromEndPosition);
112 if (seek_result.is_error()) {
113 // Let's try to restore the original position, just in case.
114 auto restore_result = seek(original_position, SeekMode::SetPosition);
115 if (restore_result.is_error()) {
116 dbgln("SeekableStream::size: Couldn't restore initial position, stream might have incorrect position now!");
117 }
118
119 return seek_result.release_error();
120 }
121
122 TRY(seek(original_position, SeekMode::SetPosition));
123 return seek_result.value();
124}
125
126ErrorOr<void> SeekableStream::discard(size_t discarded_bytes)
127{
128 TRY(seek(discarded_bytes, SeekMode::FromCurrentPosition));
129 return {};
130}
131
132}