1 //===--- Implementation of a platform independent file data structure -----===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
11 #include "hdr/func/realloc.h"
12 #include "hdr/stdio_macros.h"
13 #include "hdr/types/off_t.h"
14 #include "src/__support/CPP/new.h"
15 #include "src/__support/CPP/span.h"
16 #include "src/__support/macros/config.h"
17 #include "src/errno/libc_errno.h" // For error macros
19 namespace LIBC_NAMESPACE_DECL
{
21 FileIOResult
File::write_unlocked(const void *data
, size_t len
) {
22 if (!write_allowed()) {
27 prev_op
= FileOp::WRITE
;
29 if (bufmode
== _IONBF
) { // unbuffered.
31 write_unlocked_nbf(static_cast<const uint8_t *>(data
), len
);
34 } else if (bufmode
== _IOFBF
) { // fully buffered
35 return write_unlocked_fbf(static_cast<const uint8_t *>(data
), len
);
36 } else /*if (bufmode == _IOLBF) */ { // line buffered
37 return write_unlocked_lbf(static_cast<const uint8_t *>(data
), len
);
41 FileIOResult
File::write_unlocked_nbf(const uint8_t *data
, size_t len
) {
42 if (pos
> 0) { // If the buffer is not empty
44 const size_t write_size
= pos
;
45 auto write_result
= platform_write(this, buf
, write_size
);
46 pos
= 0; // Buffer is now empty so reset pos to the beginning.
47 // If less bytes were written than expected, then an error occurred.
48 if (write_result
< write_size
) {
50 // No bytes from data were written, so return 0.
51 return {0, write_result
.error
};
55 auto write_result
= platform_write(this, data
, len
);
56 if (write_result
< len
)
61 FileIOResult
File::write_unlocked_fbf(const uint8_t *data
, size_t len
) {
62 const size_t init_pos
= pos
;
63 const size_t bufspace
= bufsize
- pos
;
65 // If data is too large to be buffered at all, then just write it unbuffered.
66 if (len
> bufspace
+ bufsize
)
67 return write_unlocked_nbf(data
, len
);
69 // we split |data| (conceptually) using the split point. Then we handle the
70 // two pieces separately.
71 const size_t split_point
= len
< bufspace
? len
: bufspace
;
73 // The primary piece is the piece of |data| we want to write to the buffer
74 // before flushing. It will always fit into the buffer, since the split point
75 // is defined as being min(len, bufspace), and it will always exist if len is
77 cpp::span
<const uint8_t> primary(data
, split_point
);
79 // The second piece is the remainder of |data|. It is written to the buffer if
80 // it fits, or written directly to the output if it doesn't. If the primary
81 // piece fits entirely in the buffer, the remainder may be nothing.
82 cpp::span
<const uint8_t> remainder(
83 static_cast<const uint8_t *>(data
) + split_point
, len
- split_point
);
85 cpp::span
<uint8_t> bufref(static_cast<uint8_t *>(buf
), bufsize
);
87 // Copy the first piece into the buffer.
88 // TODO: Replace the for loop below with a call to internal memcpy.
89 for (size_t i
= 0; i
< primary
.size(); ++i
)
90 bufref
[pos
+ i
] = primary
[i
];
91 pos
+= primary
.size();
93 // If there is no remainder, we can return early, since the first piece has
94 // fit completely into the buffer.
95 if (remainder
.size() == 0)
98 // We need to flush the buffer now, since there is still data and the buffer
100 const size_t write_size
= pos
;
102 auto buf_result
= platform_write(this, buf
, write_size
);
103 size_t bytes_written
= buf_result
.value
;
105 pos
= 0; // Buffer is now empty so reset pos to the beginning.
106 // If less bytes were written than expected, then an error occurred. Return
107 // the number of bytes that have been written from |data|.
108 if (buf_result
.has_error() || bytes_written
< write_size
) {
110 return {bytes_written
<= init_pos
? 0 : bytes_written
- init_pos
,
114 // The second piece is handled basically the same as the first, although we
115 // know that if the second piece has data in it then the buffer has been
116 // flushed, meaning that pos is always 0.
117 if (remainder
.size() < bufsize
) {
118 // TODO: Replace the for loop below with a call to internal memcpy.
119 for (size_t i
= 0; i
< remainder
.size(); ++i
)
120 bufref
[i
] = remainder
[i
];
121 pos
= remainder
.size();
124 auto result
= platform_write(this, remainder
.data(), remainder
.size());
125 size_t bytes_written
= buf_result
.value
;
127 // If less bytes were written than expected, then an error occurred. Return
128 // the number of bytes that have been written from |data|.
129 if (result
.has_error() || bytes_written
< remainder
.size()) {
131 return {primary
.size() + bytes_written
, result
.error
};
138 FileIOResult
File::write_unlocked_lbf(const uint8_t *data
, size_t len
) {
139 constexpr uint8_t NEWLINE_CHAR
= '\n';
140 size_t last_newline
= len
;
141 for (size_t i
= len
; i
>= 1; --i
) {
142 if (data
[i
- 1] == NEWLINE_CHAR
) {
143 last_newline
= i
- 1;
148 // If there is no newline, treat this as fully buffered.
149 if (last_newline
== len
) {
150 return write_unlocked_fbf(data
, len
);
153 // we split |data| (conceptually) using the split point. Then we handle the
154 // two pieces separately.
155 const size_t split_point
= last_newline
+ 1;
157 // The primary piece is everything in |data| up to the newline. It's written
158 // unbuffered to the output.
159 cpp::span
<const uint8_t> primary(data
, split_point
);
161 // The second piece is the remainder of |data|. It is written fully buffered,
162 // meaning it may stay in the buffer if it fits.
163 cpp::span
<const uint8_t> remainder(
164 static_cast<const uint8_t *>(data
) + split_point
, len
- split_point
);
168 written
= write_unlocked_nbf(primary
.data(), primary
.size());
169 if (written
< primary
.size()) {
176 written
+= write_unlocked_fbf(remainder
.data(), remainder
.size());
185 FileIOResult
File::read_unlocked(void *data
, size_t len
) {
186 if (!read_allowed()) {
191 prev_op
= FileOp::READ
;
193 cpp::span
<uint8_t> bufref(static_cast<uint8_t *>(buf
), bufsize
);
194 cpp::span
<uint8_t> dataref(static_cast<uint8_t *>(data
), len
);
196 // Because read_limit is always greater than equal to pos,
197 // available_data is never a wrapped around value.
198 size_t available_data
= read_limit
- pos
;
199 if (len
<= available_data
) {
200 // TODO: Replace the for loop below with a call to internal memcpy.
201 for (size_t i
= 0; i
< len
; ++i
)
202 dataref
[i
] = bufref
[i
+ pos
];
207 // Copy all of the available data.
208 // TODO: Replace the for loop with a call to internal memcpy.
209 for (size_t i
= 0; i
< available_data
; ++i
)
210 dataref
[i
] = bufref
[i
+ pos
];
211 read_limit
= pos
= 0; // Reset the pointers.
212 // Update the dataref to reflect that fact that we have already
213 // copied |available_data| into |data|.
214 dataref
= cpp::span
<uint8_t>(dataref
.data() + available_data
,
215 dataref
.size() - available_data
);
217 size_t to_fetch
= len
- available_data
;
218 if (to_fetch
> bufsize
) {
219 auto result
= platform_read(this, dataref
.data(), to_fetch
);
220 size_t fetched_size
= result
.value
;
221 if (result
.has_error() || fetched_size
< to_fetch
) {
222 if (!result
.has_error())
226 return {available_data
+ fetched_size
, result
.has_error()};
231 // Fetch and buffer another buffer worth of data.
232 auto result
= platform_read(this, buf
, bufsize
);
233 size_t fetched_size
= result
.value
;
234 read_limit
+= fetched_size
;
235 size_t transfer_size
= fetched_size
>= to_fetch
? to_fetch
: fetched_size
;
236 for (size_t i
= 0; i
< transfer_size
; ++i
)
237 dataref
[i
] = bufref
[i
];
238 pos
+= transfer_size
;
239 if (result
.has_error() || fetched_size
< to_fetch
) {
240 if (!result
.has_error())
245 return {transfer_size
+ available_data
, result
.error
};
248 int File::ungetc_unlocked(int c
) {
249 // There is no meaning to unget if:
250 // 1. You are trying to push back EOF.
251 // 2. Read operations are not allowed on this file.
252 // 3. The previous operation was a write operation.
253 if (c
== EOF
|| !read_allowed() || (prev_op
== FileOp::WRITE
))
256 cpp::span
<uint8_t> bufref(static_cast<uint8_t *>(buf
), bufsize
);
257 if (read_limit
== 0) {
258 // If |read_limit| is zero, it can mean three things:
259 // a. This file was just created.
260 // b. The previous operation was a seek operation.
261 // c. The previous operation was a read operation which emptied
263 // For all the above cases, we simply write |c| at the beginning
264 // of the buffer and bump |read_limit|. Note that |pos| will also
265 // be zero in this case, so we don't need to adjust it.
266 bufref
[0] = static_cast<unsigned char>(c
);
269 // If |read_limit| is non-zero, it means that there is data in the buffer
270 // from a previous read operation. Which would also mean that |pos| is not
271 // zero. So, we decrement |pos| and write |c| in to the buffer at the new
272 // |pos|. If too many ungetc operations are performed without reads, it
273 // can lead to (pos == 0 but read_limit != 0). We will just error out in
278 bufref
[pos
] = static_cast<unsigned char>(c
);
281 eof
= false; // There is atleast one character that can be read now.
282 err
= false; // This operation was a success.
286 ErrorOr
<int> File::seek(off_t offset
, int whence
) {
288 if (prev_op
== FileOp::WRITE
&& pos
> 0) {
290 auto buf_result
= platform_write(this, buf
, pos
);
291 if (buf_result
.has_error() || buf_result
.value
< pos
) {
293 return Error(buf_result
.error
);
295 } else if (prev_op
== FileOp::READ
&& whence
== SEEK_CUR
) {
296 // More data could have been read out from the platform file than was
297 // required. So, we have to adjust the offset we pass to platform seek
298 // function. Note that read_limit >= pos is always true.
299 offset
-= (read_limit
- pos
);
301 pos
= read_limit
= 0;
302 prev_op
= FileOp::SEEK
;
303 // Reset the eof flag as a seek might move the file positon to some place
306 auto result
= platform_seek(this, offset
, whence
);
307 if (!result
.has_value())
308 return Error(result
.error());
312 ErrorOr
<off_t
> File::tell() {
314 auto seek_target
= eof
? SEEK_END
: SEEK_CUR
;
315 auto result
= platform_seek(this, 0, seek_target
);
316 if (!result
.has_value() || result
.value() < 0)
317 return Error(result
.error());
318 off_t platform_offset
= result
.value();
319 if (prev_op
== FileOp::READ
)
320 return platform_offset
- (read_limit
- pos
);
321 if (prev_op
== FileOp::WRITE
)
322 return platform_offset
+ pos
;
323 return platform_offset
;
326 int File::flush_unlocked() {
327 if (prev_op
== FileOp::WRITE
&& pos
> 0) {
328 auto buf_result
= platform_write(this, buf
, pos
);
329 if (buf_result
.has_error() || buf_result
.value
< pos
) {
331 return buf_result
.error
;
335 // TODO: Add POSIX behavior for input streams.
339 int File::set_buffer(void *buffer
, size_t size
, int buffer_mode
) {
340 // We do not need to lock the file as this method should be called before
341 // other operations are performed on the file.
342 if (buffer
!= nullptr && size
== 0)
345 switch (buffer_mode
) {
354 if (buffer
== nullptr && size
!= 0 && buffer_mode
!= _IONBF
) {
355 // We exclude the case of buffer_mode == _IONBF in this branch
356 // because we don't need to allocate buffer in such a case.
358 // This is one of the places where use a C allocation functon
359 // as C++ does not have an equivalent of realloc.
360 buf
= reinterpret_cast<uint8_t *>(realloc(buf
, size
));
365 buf
= new (ac
) uint8_t[size
];
371 // TODO: Handle allocation failures.
375 if (buffer_mode
!= _IONBF
) {
376 buf
= static_cast<uint8_t *>(buffer
);
379 // We don't need any buffer.
385 bufmode
= buffer_mode
;
390 File::ModeFlags
File::mode_flags(const char *mode
) {
391 // First character in |mode| should be 'a', 'r' or 'w'.
392 if (*mode
!= 'a' && *mode
!= 'r' && *mode
!= 'w')
395 // There should be exaclty one main mode ('a', 'r' or 'w') character.
396 // If there are more than one main mode characters listed, then
397 // we will consider |mode| as incorrect and return 0;
398 int main_mode_count
= 0;
401 for (; *mode
!= '\0'; ++mode
) {
404 flags
|= static_cast<ModeFlags
>(OpenMode::READ
);
408 flags
|= static_cast<ModeFlags
>(OpenMode::WRITE
);
412 flags
|= static_cast<ModeFlags
>(OpenMode::PLUS
);
415 flags
|= static_cast<ModeFlags
>(ContentType::BINARY
);
418 flags
|= static_cast<ModeFlags
>(OpenMode::APPEND
);
422 flags
|= static_cast<ModeFlags
>(CreateType::EXCLUSIVE
);
429 if (main_mode_count
!= 1)
435 } // namespace LIBC_NAMESPACE_DECL