[libc][NFC] Move aligned access implementations to separate header
[llvm-project.git] / libc / src / __support / File / file.h
blob97dc5ff66f1ca9a387a629b662e723689e312a23
1 //===--- A platform independent file data structure -------------*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H
10 #define LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H
12 #include "src/__support/CPP/new.h"
13 #include "src/__support/error_or.h"
14 #include "src/__support/macros/properties/architectures.h"
15 #include "src/__support/threads/mutex.h"
17 #include <stddef.h>
18 #include <stdint.h>
20 namespace __llvm_libc {
22 struct FileIOResult {
23 size_t value;
24 int error;
26 constexpr FileIOResult(size_t val) : value(val), error(0) {}
27 constexpr FileIOResult(size_t val, int error) : value(val), error(error) {}
29 constexpr bool has_error() { return error != 0; }
31 constexpr operator size_t() { return value; }
34 // This a generic base class to encapsulate a platform independent file data
35 // structure. Platform specific specializations should create a subclass as
36 // suitable for their platform.
37 class File {
38 public:
39 static constexpr size_t DEFAULT_BUFFER_SIZE = 1024;
41 // Some platforms like the GPU build cannot support buffering due to extra
42 // resource usage or hardware constraints. This function allows us to optimize
43 // out the buffering portions of the code in the general implementation.
44 #if defined(LIBC_TARGET_ARCH_IS_GPU)
45 static constexpr bool ENABLE_BUFFER = false;
46 #else
47 static constexpr bool ENABLE_BUFFER = true;
48 #endif
50 using LockFunc = void(File *);
51 using UnlockFunc = void(File *);
53 using WriteFunc = FileIOResult(File *, const void *, size_t);
54 using ReadFunc = FileIOResult(File *, void *, size_t);
55 // The SeekFunc is expected to return the current offset of the external
56 // file position indicator.
57 using SeekFunc = ErrorOr<long>(File *, long, int);
58 using CloseFunc = int(File *);
60 using ModeFlags = uint32_t;
62 // The three different types of flags below are to be used with '|' operator.
63 // Their values correspond to mutually exclusive bits in a 32-bit unsigned
64 // integer value. A flag set can include both READ and WRITE if the file
65 // is opened in update mode (ie. if the file was opened with a '+' the mode
66 // string.)
67 enum class OpenMode : ModeFlags {
68 READ = 0x1,
69 WRITE = 0x2,
70 APPEND = 0x4,
71 PLUS = 0x8,
74 // Denotes a file opened in binary mode (which is specified by including
75 // the 'b' character in teh mode string.)
76 enum class ContentType : ModeFlags {
77 BINARY = 0x10,
80 // Denotes a file to be created for writing.
81 enum class CreateType : ModeFlags {
82 EXCLUSIVE = 0x100,
85 private:
86 enum class FileOp : uint8_t { NONE, READ, WRITE, SEEK };
88 // Platfrom specific functions which create new file objects should initialize
89 // these fields suitably via the constructor. Typically, they should be simple
90 // syscall wrappers for the corresponding functionality.
91 WriteFunc *platform_write;
92 ReadFunc *platform_read;
93 SeekFunc *platform_seek;
94 CloseFunc *platform_close;
96 Mutex mutex;
98 // For files which are readable, we should be able to support one ungetc
99 // operation even if |buf| is nullptr. So, in the constructor of File, we
100 // set |buf| to point to this buffer character.
101 uint8_t ungetc_buf;
103 uint8_t *buf; // Pointer to the stream buffer for buffered streams
104 size_t bufsize; // Size of the buffer pointed to by |buf|.
106 // Buffering mode to used to buffer.
107 int bufmode;
109 // If own_buf is true, the |buf| is owned by the stream and will be
110 // free-ed when close method is called on the stream.
111 bool own_buf;
113 // The mode in which the file was opened.
114 ModeFlags mode;
116 // Current read or write pointer.
117 size_t pos;
119 // Represents the previous operation that was performed.
120 FileOp prev_op;
122 // When the buffer is used as a read buffer, read_limit is the upper limit
123 // of the index to which the buffer can be read until.
124 size_t read_limit;
126 bool eof;
127 bool err;
129 // This is a convenience RAII class to lock and unlock file objects.
130 class FileLock {
131 File *file;
133 public:
134 explicit FileLock(File *f) : file(f) { file->lock(); }
136 ~FileLock() { file->unlock(); }
138 FileLock(const FileLock &) = delete;
139 FileLock(FileLock &&) = delete;
142 protected:
143 constexpr bool write_allowed() const {
144 return mode & (static_cast<ModeFlags>(OpenMode::WRITE) |
145 static_cast<ModeFlags>(OpenMode::APPEND) |
146 static_cast<ModeFlags>(OpenMode::PLUS));
149 constexpr bool read_allowed() const {
150 return mode & (static_cast<ModeFlags>(OpenMode::READ) |
151 static_cast<ModeFlags>(OpenMode::PLUS));
154 // The GPU build should not emit a destructor because we do not support global
155 // destructors in all cases and it is unneccessary without buffering.
156 #if !defined(LIBC_TARGET_ARCH_IS_GPU)
157 ~File() {
158 if (own_buf)
159 delete buf;
161 #endif
163 public:
164 // We want this constructor to be constexpr so that global file objects
165 // like stdout do not require invocation of the constructor which can
166 // potentially lead to static initialization order fiasco. Consequently,
167 // we will assume that the |buffer| and |buffer_size| argument are
168 // meaningful - that is, |buffer| is nullptr if and only if |buffer_size|
169 // is zero. This way, we will not have to employ the semantics of
170 // the set_buffer method and allocate a buffer.
171 constexpr File(WriteFunc *wf, ReadFunc *rf, SeekFunc *sf, CloseFunc *cf,
172 uint8_t *buffer, size_t buffer_size, int buffer_mode,
173 bool owned, ModeFlags modeflags)
174 : platform_write(wf), platform_read(rf), platform_seek(sf),
175 platform_close(cf), mutex(false, false, false), ungetc_buf(0),
176 buf(buffer), bufsize(buffer_size), bufmode(buffer_mode), own_buf(owned),
177 mode(modeflags), pos(0), prev_op(FileOp::NONE), read_limit(0),
178 eof(false), err(false) {
179 if constexpr (ENABLE_BUFFER)
180 adjust_buf();
183 // Buffered write of |len| bytes from |data| without the file lock.
184 FileIOResult write_unlocked(const void *data, size_t len);
186 // Buffered write of |len| bytes from |data| under the file lock.
187 FileIOResult write(const void *data, size_t len) {
188 FileLock l(this);
189 return write_unlocked(data, len);
192 // Buffered read of |len| bytes into |data| without the file lock.
193 FileIOResult read_unlocked(void *data, size_t len);
195 // Buffered read of |len| bytes into |data| under the file lock.
196 FileIOResult read(void *data, size_t len) {
197 FileLock l(this);
198 return read_unlocked(data, len);
201 ErrorOr<int> seek(long offset, int whence);
203 ErrorOr<long> tell();
205 // If buffer has data written to it, flush it out. Does nothing if the
206 // buffer is currently being used as a read buffer.
207 int flush() {
208 FileLock lock(this);
209 return flush_unlocked();
212 int flush_unlocked();
214 // Returns EOF on error and keeps the file unchanged.
215 int ungetc_unlocked(int c);
217 int ungetc(int c) {
218 FileLock lock(this);
219 return ungetc_unlocked(c);
222 // Does the following:
223 // 1. If in write mode, Write out any data present in the buffer.
224 // 2. Call platform_close.
225 // platform_close is expected to cleanup the complete file object.
226 int close() {
228 FileLock lock(this);
229 if (prev_op == FileOp::WRITE && pos > 0) {
230 auto buf_result = platform_write(this, buf, pos);
231 if (buf_result.has_error() || buf_result.value < pos) {
232 err = true;
233 return buf_result.error;
237 // Platform close is expected to cleanup the file data structure which
238 // includes the file mutex. Hence, we call platform_close after releasing
239 // the file lock. Another thread doing file operations while a thread is
240 // closing the file is undefined behavior as per POSIX.
241 return platform_close(this);
244 // Sets the internal buffer to |buffer| with buffering mode |mode|.
245 // |size| is the size of |buffer|. If |size| is non-zero, but |buffer|
246 // is nullptr, then a buffer owned by this file will be allocated.
247 // Else, |buffer| will not be owned by this file.
249 // Will return zero on success, or an error value on failure. Will fail
250 // if:
251 // 1. |buffer| is not a nullptr but |size| is zero.
252 // 2. |buffer_mode| is not one of _IOLBF, IOFBF or _IONBF.
253 // 3. If an allocation was required but the allocation failed.
254 // For cases 1 and 2, the error returned in EINVAL. For case 3, error returned
255 // is ENOMEM.
256 int set_buffer(void *buffer, size_t size, int buffer_mode);
258 void lock() { mutex.lock(); }
259 void unlock() { mutex.unlock(); }
261 bool error_unlocked() const { return err; }
263 bool error() {
264 FileLock l(this);
265 return error_unlocked();
268 void clearerr_unlocked() { err = false; }
270 void clearerr() {
271 FileLock l(this);
272 clearerr_unlocked();
275 bool iseof_unlocked() { return eof; }
277 bool iseof() {
278 FileLock l(this);
279 return iseof_unlocked();
282 // Returns an bit map of flags corresponding to enumerations of
283 // OpenMode, ContentType and CreateType.
284 static ModeFlags mode_flags(const char *mode);
286 private:
287 FileIOResult write_unlocked_lbf(const uint8_t *data, size_t len);
288 FileIOResult write_unlocked_fbf(const uint8_t *data, size_t len);
289 FileIOResult write_unlocked_nbf(const uint8_t *data, size_t len);
291 constexpr void adjust_buf() {
292 if (read_allowed() && (buf == nullptr || bufsize == 0)) {
293 // We should allow atleast one ungetc operation.
294 // This might give an impression that a buffer will be used even when
295 // the user does not want a buffer. But, that will not be the case.
296 // For reading, the buffering does not come into play. For writing, let
297 // us take up the three different kinds of buffering separately:
298 // 1. If user wants _IOFBF but gives a zero buffer, buffering still
299 // happens in the OS layer until the user flushes. So, from the user's
300 // point of view, this single byte buffer does not affect their
301 // experience.
302 // 2. If user wants _IOLBF but gives a zero buffer, the reasoning is
303 // very similar to the _IOFBF case.
304 // 3. If user wants _IONBF, then the buffer is ignored for writing.
305 // So, all of the above cases, having a single ungetc buffer does not
306 // affect the behavior experienced by the user.
307 buf = &ungetc_buf;
308 bufsize = 1;
309 own_buf = false; // We shouldn't call free on |buf| when closing the file.
314 // The implementaiton of this function is provided by the platfrom_file
315 // library.
316 ErrorOr<File *> openfile(const char *path, const char *mode);
318 // The platform_file library should implement it if it relevant for that
319 // platform.
320 int get_fileno(File *f);
322 extern File *stdin;
323 extern File *stdout;
324 extern File *stderr;
326 } // namespace __llvm_libc
328 #endif // LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H