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 "src/__support/CPP/new.h"
12 #include "src/__support/CPP/span.h"
13 #include "src/errno/libc_errno.h" // For error macros
18 namespace __llvm_libc
{
20 FileIOResult
File::write_unlocked(const void *data
, size_t len
) {
21 if (!write_allowed()) {
26 prev_op
= FileOp::WRITE
;
28 if (bufmode
== _IOFBF
) { // fully buffered
29 return write_unlocked_fbf(static_cast<const uint8_t *>(data
), len
);
30 } else if (bufmode
== _IOLBF
) { // line buffered
31 return write_unlocked_lbf(static_cast<const uint8_t *>(data
), len
);
32 } else /*if (bufmode == _IONBF) */ { // unbuffered
34 write_unlocked_nbf(static_cast<const uint8_t *>(data
), len
);
40 FileIOResult
File::write_unlocked_nbf(const uint8_t *data
, size_t len
) {
41 if (pos
> 0) { // If the buffer is not empty
43 const size_t write_size
= pos
;
44 auto write_result
= platform_write(this, buf
, write_size
);
45 pos
= 0; // Buffer is now empty so reset pos to the beginning.
46 // If less bytes were written than expected, then an error occurred.
47 if (write_result
< write_size
) {
49 // No bytes from data were written, so return 0.
50 return {0, write_result
.error
};
54 auto write_result
= platform_write(this, data
, len
);
55 if (write_result
< len
)
60 FileIOResult
File::write_unlocked_fbf(const uint8_t *data
, size_t len
) {
61 const size_t init_pos
= pos
;
62 const size_t bufspace
= bufsize
- pos
;
64 // If data is too large to be buffered at all, then just write it unbuffered.
65 if (len
> bufspace
+ bufsize
)
66 return write_unlocked_nbf(data
, len
);
68 // we split |data| (conceptually) using the split point. Then we handle the
69 // two pieces separately.
70 const size_t split_point
= len
< bufspace
? len
: bufspace
;
72 // The primary piece is the piece of |data| we want to write to the buffer
73 // before flushing. It will always fit into the buffer, since the split point
74 // is defined as being min(len, bufspace), and it will always exist if len is
76 cpp::span
<const uint8_t> primary(data
, split_point
);
78 // The second piece is the remainder of |data|. It is written to the buffer if
79 // it fits, or written directly to the output if it doesn't. If the primary
80 // piece fits entirely in the buffer, the remainder may be nothing.
81 cpp::span
<const uint8_t> remainder(
82 static_cast<const uint8_t *>(data
) + split_point
, len
- split_point
);
84 cpp::span
<uint8_t> bufref(static_cast<uint8_t *>(buf
), bufsize
);
86 // Copy the first piece into the buffer.
87 // TODO: Replace the for loop below with a call to internal memcpy.
88 for (size_t i
= 0; i
< primary
.size(); ++i
)
89 bufref
[pos
+ i
] = primary
[i
];
90 pos
+= primary
.size();
92 // If there is no remainder, we can return early, since the first piece has
93 // fit completely into the buffer.
94 if (remainder
.size() == 0)
97 // We need to flush the buffer now, since there is still data and the buffer
99 const size_t write_size
= pos
;
101 auto buf_result
= platform_write(this, buf
, write_size
);
102 size_t bytes_written
= buf_result
.value
;
104 pos
= 0; // Buffer is now empty so reset pos to the beginning.
105 // If less bytes were written than expected, then an error occurred. Return
106 // the number of bytes that have been written from |data|.
107 if (buf_result
.has_error() || bytes_written
< write_size
) {
109 return {bytes_written
<= init_pos
? 0 : bytes_written
- init_pos
,
113 // The second piece is handled basically the same as the first, although we
114 // know that if the second piece has data in it then the buffer has been
115 // flushed, meaning that pos is always 0.
116 if (remainder
.size() < bufsize
) {
117 // TODO: Replace the for loop below with a call to internal memcpy.
118 for (size_t i
= 0; i
< remainder
.size(); ++i
)
119 bufref
[i
] = remainder
[i
];
120 pos
= remainder
.size();
123 auto result
= platform_write(this, remainder
.data(), remainder
.size());
124 size_t bytes_written
= buf_result
.value
;
126 // If less bytes were written than expected, then an error occurred. Return
127 // the number of bytes that have been written from |data|.
128 if (result
.has_error() || bytes_written
< remainder
.size()) {
130 return {primary
.size() + bytes_written
, result
.error
};
137 FileIOResult
File::write_unlocked_lbf(const uint8_t *data
, size_t len
) {
138 constexpr uint8_t NEWLINE_CHAR
= '\n';
139 size_t last_newline
= len
;
140 for (size_t i
= len
; i
>= 1; --i
) {
141 if (data
[i
- 1] == NEWLINE_CHAR
) {
142 last_newline
= i
- 1;
147 // If there is no newline, treat this as fully buffered.
148 if (last_newline
== len
) {
149 return write_unlocked_fbf(data
, len
);
152 // we split |data| (conceptually) using the split point. Then we handle the
153 // two pieces separately.
154 const size_t split_point
= last_newline
+ 1;
156 // The primary piece is everything in |data| up to the newline. It's written
157 // unbuffered to the output.
158 cpp::span
<const uint8_t> primary(data
, split_point
);
160 // The second piece is the remainder of |data|. It is written fully buffered,
161 // meaning it may stay in the buffer if it fits.
162 cpp::span
<const uint8_t> remainder(
163 static_cast<const uint8_t *>(data
) + split_point
, len
- split_point
);
167 written
= write_unlocked_nbf(primary
.data(), primary
.size());
168 if (written
< primary
.size()) {
175 written
+= write_unlocked_fbf(remainder
.data(), remainder
.size());
184 FileIOResult
File::read_unlocked(void *data
, size_t len
) {
185 if (!read_allowed()) {
190 prev_op
= FileOp::READ
;
192 cpp::span
<uint8_t> bufref(static_cast<uint8_t *>(buf
), bufsize
);
193 cpp::span
<uint8_t> dataref(static_cast<uint8_t *>(data
), len
);
195 // Because read_limit is always greater than equal to pos,
196 // available_data is never a wrapped around value.
197 size_t available_data
= read_limit
- pos
;
198 if (len
<= available_data
) {
199 // TODO: Replace the for loop below with a call to internal memcpy.
200 for (size_t i
= 0; i
< len
; ++i
)
201 dataref
[i
] = bufref
[i
+ pos
];
206 // Copy all of the available data.
207 // TODO: Replace the for loop with a call to internal memcpy.
208 for (size_t i
= 0; i
< available_data
; ++i
)
209 dataref
[i
] = bufref
[i
+ pos
];
210 read_limit
= pos
= 0; // Reset the pointers.
211 // Update the dataref to reflect that fact that we have already
212 // copied |available_data| into |data|.
213 dataref
= cpp::span
<uint8_t>(dataref
.data() + available_data
,
214 dataref
.size() - available_data
);
216 size_t to_fetch
= len
- available_data
;
217 if (to_fetch
> bufsize
) {
218 auto result
= platform_read(this, dataref
.data(), to_fetch
);
219 size_t fetched_size
= result
.value
;
220 if (result
.has_error() || fetched_size
< to_fetch
) {
221 if (!result
.has_error())
225 return {available_data
+ fetched_size
, result
.has_error()};
230 // Fetch and buffer another buffer worth of data.
231 auto result
= platform_read(this, buf
, bufsize
);
232 size_t fetched_size
= result
.value
;
233 read_limit
+= fetched_size
;
234 size_t transfer_size
= fetched_size
>= to_fetch
? to_fetch
: fetched_size
;
235 for (size_t i
= 0; i
< transfer_size
; ++i
)
236 dataref
[i
] = bufref
[i
];
237 pos
+= transfer_size
;
238 if (result
.has_error() || fetched_size
< to_fetch
) {
239 if (!result
.has_error())
244 return {transfer_size
+ available_data
, result
.error
};
247 int File::ungetc_unlocked(int c
) {
248 // There is no meaning to unget if:
249 // 1. You are trying to push back EOF.
250 // 2. Read operations are not allowed on this file.
251 // 3. The previous operation was a write operation.
252 if (c
== EOF
|| !read_allowed() || (prev_op
== FileOp::WRITE
))
255 cpp::span
<uint8_t> bufref(static_cast<uint8_t *>(buf
), bufsize
);
256 if (read_limit
== 0) {
257 // If |read_limit| is zero, it can mean three things:
258 // a. This file was just created.
259 // b. The previous operation was a seek operation.
260 // c. The previous operation was a read operation which emptied
262 // For all the above cases, we simply write |c| at the beginning
263 // of the buffer and bump |read_limit|. Note that |pos| will also
264 // be zero in this case, so we don't need to adjust it.
265 bufref
[0] = static_cast<unsigned char>(c
);
268 // If |read_limit| is non-zero, it means that there is data in the buffer
269 // from a previous read operation. Which would also mean that |pos| is not
270 // zero. So, we decrement |pos| and write |c| in to the buffer at the new
271 // |pos|. If too many ungetc operations are performed without reads, it
272 // can lead to (pos == 0 but read_limit != 0). We will just error out in
277 bufref
[pos
] = static_cast<unsigned char>(c
);
280 eof
= false; // There is atleast one character that can be read now.
281 err
= false; // This operation was a success.
285 ErrorOr
<int> File::seek(long offset
, int whence
) {
287 if (prev_op
== FileOp::WRITE
&& pos
> 0) {
289 auto buf_result
= platform_write(this, buf
, pos
);
290 if (buf_result
.has_error() || buf_result
.value
< pos
) {
292 return Error(buf_result
.error
);
294 } else if (prev_op
== FileOp::READ
&& whence
== SEEK_CUR
) {
295 // More data could have been read out from the platform file than was
296 // required. So, we have to adjust the offset we pass to platform seek
297 // function. Note that read_limit >= pos is always true.
298 offset
-= (read_limit
- pos
);
300 pos
= read_limit
= 0;
301 prev_op
= FileOp::SEEK
;
302 // Reset the eof flag as a seek might move the file positon to some place
305 auto result
= platform_seek(this, offset
, whence
);
306 if (!result
.has_value())
307 return Error(result
.error());
312 ErrorOr
<long> 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 long platform_offset
= result
.value();
319 if (prev_op
== FileOp::READ
)
320 return platform_offset
- (read_limit
- pos
);
321 else if (prev_op
== FileOp::WRITE
)
322 return platform_offset
+ pos
;
324 return platform_offset
;
327 int File::flush_unlocked() {
328 if (prev_op
== FileOp::WRITE
&& pos
> 0) {
329 auto buf_result
= platform_write(this, buf
, pos
);
330 if (buf_result
.has_error() || buf_result
.value
< pos
) {
332 return buf_result
.error
;
335 return platform_flush(this);
337 // TODO: Add POSIX behavior for input streams.
341 int File::set_buffer(void *buffer
, size_t size
, int buffer_mode
) {
342 // We do not need to lock the file as this method should be called before
343 // other operations are performed on the file.
345 if (buffer
!= nullptr && size
== 0)
348 switch (buffer_mode
) {
357 if (buffer
== nullptr && size
!= 0 && buffer_mode
!= _IONBF
) {
358 // We exclude the case of buffer_mode == _IONBF in this branch
359 // because we don't need to allocate buffer in such a case.
361 // This is one of the places where use a C allocation functon
362 // as C++ does not have an equivalent of realloc.
363 buf
= reinterpret_cast<uint8_t *>(realloc(buf
, size
));
368 buf
= new (ac
) uint8_t[size
];
374 // TODO: Handle allocation failures.
378 if (buffer_mode
!= _IONBF
) {
379 buf
= static_cast<uint8_t *>(buffer
);
382 // We don't need any buffer.
388 bufmode
= buffer_mode
;
393 File::ModeFlags
File::mode_flags(const char *mode
) {
394 // First character in |mode| should be 'a', 'r' or 'w'.
395 if (*mode
!= 'a' && *mode
!= 'r' && *mode
!= 'w')
398 // There should be exaclty one main mode ('a', 'r' or 'w') character.
399 // If there are more than one main mode characters listed, then
400 // we will consider |mode| as incorrect and return 0;
401 int main_mode_count
= 0;
404 for (; *mode
!= '\0'; ++mode
) {
407 flags
|= static_cast<ModeFlags
>(OpenMode::READ
);
411 flags
|= static_cast<ModeFlags
>(OpenMode::WRITE
);
415 flags
|= static_cast<ModeFlags
>(OpenMode::PLUS
);
418 flags
|= static_cast<ModeFlags
>(ContentType::BINARY
);
421 flags
|= static_cast<ModeFlags
>(OpenMode::APPEND
);
425 flags
|= static_cast<ModeFlags
>(CreateType::EXCLUSIVE
);
432 if (main_mode_count
!= 1)
438 } // namespace __llvm_libc