1 //===--- A platform independent file data structure -------------*- C++ -*-===//
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 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC___SUPPORT_FILE_FILE_H
10 #define LLVM_LIBC_SRC___SUPPORT_FILE_FILE_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/error_or.h"
16 #include "src/__support/macros/config.h"
17 #include "src/__support/macros/properties/architectures.h"
18 #include "src/__support/threads/mutex.h"
23 namespace LIBC_NAMESPACE_DECL
{
29 constexpr FileIOResult(size_t val
) : value(val
), error(0) {}
30 constexpr FileIOResult(size_t val
, int error
) : value(val
), error(error
) {}
32 constexpr bool has_error() { return error
!= 0; }
34 constexpr operator size_t() { return value
; }
37 // This a generic base class to encapsulate a platform independent file data
38 // structure. Platform specific specializations should create a subclass as
39 // suitable for their platform.
42 static constexpr size_t DEFAULT_BUFFER_SIZE
= 1024;
44 using LockFunc
= void(File
*);
45 using UnlockFunc
= void(File
*);
47 using WriteFunc
= FileIOResult(File
*, const void *, size_t);
48 using ReadFunc
= FileIOResult(File
*, void *, size_t);
49 // The SeekFunc is expected to return the current offset of the external
50 // file position indicator.
51 using SeekFunc
= ErrorOr
<off_t
>(File
*, off_t
, int);
52 using CloseFunc
= int(File
*);
54 using ModeFlags
= uint32_t;
56 // The three different types of flags below are to be used with '|' operator.
57 // Their values correspond to mutually exclusive bits in a 32-bit unsigned
58 // integer value. A flag set can include both READ and WRITE if the file
59 // is opened in update mode (ie. if the file was opened with a '+' the mode
61 enum class OpenMode
: ModeFlags
{
68 // Denotes a file opened in binary mode (which is specified by including
69 // the 'b' character in teh mode string.)
70 enum class ContentType
: ModeFlags
{
74 // Denotes a file to be created for writing.
75 enum class CreateType
: ModeFlags
{
80 enum class FileOp
: uint8_t { NONE
, READ
, WRITE
, SEEK
};
82 // Platform specific functions which create new file objects should initialize
83 // these fields suitably via the constructor. Typically, they should be simple
84 // syscall wrappers for the corresponding functionality.
85 WriteFunc
*platform_write
;
86 ReadFunc
*platform_read
;
87 SeekFunc
*platform_seek
;
88 CloseFunc
*platform_close
;
92 // For files which are readable, we should be able to support one ungetc
93 // operation even if |buf| is nullptr. So, in the constructor of File, we
94 // set |buf| to point to this buffer character.
97 uint8_t *buf
; // Pointer to the stream buffer for buffered streams
98 size_t bufsize
; // Size of the buffer pointed to by |buf|.
100 // Buffering mode to used to buffer.
103 // If own_buf is true, the |buf| is owned by the stream and will be
104 // free-ed when close method is called on the stream.
107 // The mode in which the file was opened.
110 // Current read or write pointer.
113 // Represents the previous operation that was performed.
116 // When the buffer is used as a read buffer, read_limit is the upper limit
117 // of the index to which the buffer can be read until.
123 // This is a convenience RAII class to lock and unlock file objects.
128 explicit FileLock(File
*f
) : file(f
) { file
->lock(); }
130 ~FileLock() { file
->unlock(); }
132 FileLock(const FileLock
&) = delete;
133 FileLock(FileLock
&&) = delete;
137 constexpr bool write_allowed() const {
138 return mode
& (static_cast<ModeFlags
>(OpenMode::WRITE
) |
139 static_cast<ModeFlags
>(OpenMode::APPEND
) |
140 static_cast<ModeFlags
>(OpenMode::PLUS
));
143 constexpr bool read_allowed() const {
144 return mode
& (static_cast<ModeFlags
>(OpenMode::READ
) |
145 static_cast<ModeFlags
>(OpenMode::PLUS
));
149 // We want this constructor to be constexpr so that global file objects
150 // like stdout do not require invocation of the constructor which can
151 // potentially lead to static initialization order fiasco. Consequently,
152 // we will assume that the |buffer| and |buffer_size| argument are
153 // meaningful - that is, |buffer| is nullptr if and only if |buffer_size|
154 // is zero. This way, we will not have to employ the semantics of
155 // the set_buffer method and allocate a buffer.
156 constexpr File(WriteFunc
*wf
, ReadFunc
*rf
, SeekFunc
*sf
, CloseFunc
*cf
,
157 uint8_t *buffer
, size_t buffer_size
, int buffer_mode
,
158 bool owned
, ModeFlags modeflags
)
159 : platform_write(wf
), platform_read(rf
), platform_seek(sf
),
160 platform_close(cf
), mutex(/*timed=*/false, /*recursive=*/false,
161 /*robust=*/false, /*pshared=*/false),
162 ungetc_buf(0), buf(buffer
), bufsize(buffer_size
), bufmode(buffer_mode
),
163 own_buf(owned
), mode(modeflags
), pos(0), prev_op(FileOp::NONE
),
164 read_limit(0), eof(false), err(false) {
168 // Buffered write of |len| bytes from |data| without the file lock.
169 FileIOResult
write_unlocked(const void *data
, size_t len
);
171 // Buffered write of |len| bytes from |data| under the file lock.
172 FileIOResult
write(const void *data
, size_t len
) {
174 return write_unlocked(data
, len
);
177 // Buffered read of |len| bytes into |data| without the file lock.
178 FileIOResult
read_unlocked(void *data
, size_t len
);
180 // Buffered read of |len| bytes into |data| under the file lock.
181 FileIOResult
read(void *data
, size_t len
) {
183 return read_unlocked(data
, len
);
186 ErrorOr
<int> seek(off_t offset
, int whence
);
188 ErrorOr
<off_t
> tell();
190 // If buffer has data written to it, flush it out. Does nothing if the
191 // buffer is currently being used as a read buffer.
194 return flush_unlocked();
197 int flush_unlocked();
199 // Returns EOF on error and keeps the file unchanged.
200 int ungetc_unlocked(int c
);
204 return ungetc_unlocked(c
);
207 // Does the following:
208 // 1. If in write mode, Write out any data present in the buffer.
209 // 2. Call platform_close.
210 // platform_close is expected to cleanup the complete file object.
214 if (prev_op
== FileOp::WRITE
&& pos
> 0) {
215 auto buf_result
= platform_write(this, buf
, pos
);
216 if (buf_result
.has_error() || buf_result
.value
< pos
) {
218 return buf_result
.error
;
223 // If we own the buffer, delete it before calling the platform close
224 // implementation. The platform close should not need to access the buffer
225 // and we need to clean it up before the entire structure is removed.
229 // Platform close is expected to cleanup the file data structure which
230 // includes the file mutex. Hence, we call platform_close after releasing
231 // the file lock. Another thread doing file operations while a thread is
232 // closing the file is undefined behavior as per POSIX.
233 return platform_close(this);
236 // Sets the internal buffer to |buffer| with buffering mode |mode|.
237 // |size| is the size of |buffer|. If |size| is non-zero, but |buffer|
238 // is nullptr, then a buffer owned by this file will be allocated.
239 // Else, |buffer| will not be owned by this file.
241 // Will return zero on success, or an error value on failure. Will fail
243 // 1. |buffer| is not a nullptr but |size| is zero.
244 // 2. |buffer_mode| is not one of _IOLBF, IOFBF or _IONBF.
245 // 3. If an allocation was required but the allocation failed.
246 // For cases 1 and 2, the error returned in EINVAL. For case 3, error returned
248 int set_buffer(void *buffer
, size_t size
, int buffer_mode
);
250 void lock() { mutex
.lock(); }
251 void unlock() { mutex
.unlock(); }
253 bool error_unlocked() const { return err
; }
257 return error_unlocked();
260 void clearerr_unlocked() { err
= false; }
267 bool iseof_unlocked() { return eof
; }
271 return iseof_unlocked();
274 // Returns an bit map of flags corresponding to enumerations of
275 // OpenMode, ContentType and CreateType.
276 static ModeFlags
mode_flags(const char *mode
);
279 FileIOResult
write_unlocked_lbf(const uint8_t *data
, size_t len
);
280 FileIOResult
write_unlocked_fbf(const uint8_t *data
, size_t len
);
281 FileIOResult
write_unlocked_nbf(const uint8_t *data
, size_t len
);
283 constexpr void adjust_buf() {
284 if (read_allowed() && (buf
== nullptr || bufsize
== 0)) {
285 // We should allow atleast one ungetc operation.
286 // This might give an impression that a buffer will be used even when
287 // the user does not want a buffer. But, that will not be the case.
288 // For reading, the buffering does not come into play. For writing, let
289 // us take up the three different kinds of buffering separately:
290 // 1. If user wants _IOFBF but gives a zero buffer, buffering still
291 // happens in the OS layer until the user flushes. So, from the user's
292 // point of view, this single byte buffer does not affect their
294 // 2. If user wants _IOLBF but gives a zero buffer, the reasoning is
295 // very similar to the _IOFBF case.
296 // 3. If user wants _IONBF, then the buffer is ignored for writing.
297 // So, all of the above cases, having a single ungetc buffer does not
298 // affect the behavior experienced by the user.
301 own_buf
= false; // We shouldn't call free on |buf| when closing the file.
306 // The implementaiton of this function is provided by the platform_file
308 ErrorOr
<File
*> openfile(const char *path
, const char *mode
);
310 // The platform_file library should implement it if it relevant for that
312 int get_fileno(File
*f
);
318 } // namespace LIBC_NAMESPACE_DECL
320 #endif // LLVM_LIBC_SRC___SUPPORT_FILE_FILE_H