[NFC][Py Reformat] Added more commits to .git-blame-ignore-revs
[llvm-project.git] / libc / src / __support / File / file.h
blob3c035cdd5fc6313f19ae9489ec06bd73da3d53d9
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/threads/mutex.h"
16 #include <stddef.h>
17 #include <stdint.h>
19 namespace __llvm_libc {
21 struct FileIOResult {
22 size_t value;
23 int error;
25 constexpr FileIOResult(size_t val) : value(val), error(0) {}
26 constexpr FileIOResult(size_t val, int error) : value(val), error(error) {}
28 constexpr bool has_error() { return error != 0; }
30 constexpr operator size_t() { return value; }
33 // This a generic base class to encapsulate a platform independent file data
34 // structure. Platform specific specializations should create a subclass as
35 // suitable for their platform.
36 class File {
37 public:
38 static constexpr size_t DEFAULT_BUFFER_SIZE = 1024;
40 using LockFunc = void(File *);
41 using UnlockFunc = void(File *);
43 using WriteFunc = FileIOResult(File *, const void *, size_t);
44 using ReadFunc = FileIOResult(File *, void *, size_t);
45 // The SeekFunc is expected to return the current offset of the external
46 // file position indicator.
47 using SeekFunc = ErrorOr<long>(File *, long, int);
48 using CloseFunc = int(File *);
49 using FlushFunc = int(File *);
50 // CleanupFunc is a function which does the equivalent of this:
52 // void my_file_cleanup(File *f) {
53 // MyFile *file = reinterpret_cast<MyFile *>(f);
54 // delete file;
55 // }
57 // Essentially, it a function which calls the delete operator on the
58 // platform file object to cleanup resources held by it.
59 using CleanupFunc = void(File *);
61 using ModeFlags = uint32_t;
63 // The three different types of flags below are to be used with '|' operator.
64 // Their values correspond to mutually exclusive bits in a 32-bit unsigned
65 // integer value. A flag set can include both READ and WRITE if the file
66 // is opened in update mode (ie. if the file was opened with a '+' the mode
67 // string.)
68 enum class OpenMode : ModeFlags {
69 READ = 0x1,
70 WRITE = 0x2,
71 APPEND = 0x4,
72 PLUS = 0x8,
75 // Denotes a file opened in binary mode (which is specified by including
76 // the 'b' character in teh mode string.)
77 enum class ContentType : ModeFlags {
78 BINARY = 0x10,
81 // Denotes a file to be created for writing.
82 enum class CreateType : ModeFlags {
83 EXCLUSIVE = 0x100,
86 private:
87 enum class FileOp : uint8_t { NONE, READ, WRITE, SEEK };
89 // Platfrom specific functions which create new file objects should initialize
90 // these fields suitably via the constructor. Typically, they should be simple
91 // syscall wrappers for the corresponding functionality.
92 WriteFunc *platform_write;
93 ReadFunc *platform_read;
94 SeekFunc *platform_seek;
95 CloseFunc *platform_close;
96 FlushFunc *platform_flush;
97 CleanupFunc *platform_cleanup;
99 Mutex mutex;
101 // For files which are readable, we should be able to support one ungetc
102 // operation even if |buf| is nullptr. So, in the constructor of File, we
103 // set |buf| to point to this buffer character.
104 uint8_t ungetc_buf;
106 uint8_t *buf; // Pointer to the stream buffer for buffered streams
107 size_t bufsize; // Size of the buffer pointed to by |buf|.
109 // Buffering mode to used to buffer.
110 int bufmode;
112 // If own_buf is true, the |buf| is owned by the stream and will be
113 // free-ed when close method is called on the stream.
114 bool own_buf;
116 // The mode in which the file was opened.
117 ModeFlags mode;
119 // Current read or write pointer.
120 size_t pos;
122 // Represents the previous operation that was performed.
123 FileOp prev_op;
125 // When the buffer is used as a read buffer, read_limit is the upper limit
126 // of the index to which the buffer can be read until.
127 size_t read_limit;
129 bool eof;
130 bool err;
132 // This is a convenience RAII class to lock and unlock file objects.
133 class FileLock {
134 File *file;
136 public:
137 explicit FileLock(File *f) : file(f) { file->lock(); }
139 ~FileLock() { file->unlock(); }
141 FileLock(const FileLock &) = delete;
142 FileLock(FileLock &&) = delete;
145 // This is private function and is not to be called by the users of
146 // File and its derived classes. The correct way to close a file is
147 // to call the File::cleanup function.
148 int close() {
150 FileLock lock(this);
151 if (prev_op == FileOp::WRITE && pos > 0) {
152 auto buf_result = platform_write(this, buf, pos);
153 if (buf_result.has_error() || buf_result.value < pos) {
154 err = true;
155 return buf_result.error;
158 int result = platform_close(this);
159 if (result != 0)
160 return result;
162 return 0;
165 protected:
166 constexpr bool write_allowed() const {
167 return mode & (static_cast<ModeFlags>(OpenMode::WRITE) |
168 static_cast<ModeFlags>(OpenMode::APPEND) |
169 static_cast<ModeFlags>(OpenMode::PLUS));
172 constexpr bool read_allowed() const {
173 return mode & (static_cast<ModeFlags>(OpenMode::READ) |
174 static_cast<ModeFlags>(OpenMode::PLUS));
177 ~File() {
178 if (own_buf)
179 delete buf;
182 public:
183 // We want this constructor to be constexpr so that global file objects
184 // like stdout do not require invocation of the constructor which can
185 // potentially lead to static initialization order fiasco. Consequently,
186 // we will assume that the |buffer| and |buffer_size| argument are
187 // meaningful - that is, |buffer| is nullptr if and only if |buffer_size|
188 // is zero. This way, we will not have to employ the semantics of
189 // the set_buffer method and allocate a buffer.
190 constexpr File(WriteFunc *wf, ReadFunc *rf, SeekFunc *sf, CloseFunc *cf,
191 FlushFunc *ff, CleanupFunc *clf, uint8_t *buffer,
192 size_t buffer_size, int buffer_mode, bool owned,
193 ModeFlags modeflags)
194 : platform_write(wf), platform_read(rf), platform_seek(sf),
195 platform_close(cf), platform_flush(ff), platform_cleanup(clf),
196 mutex(false, false, false), ungetc_buf(0), buf(buffer),
197 bufsize(buffer_size), bufmode(buffer_mode), own_buf(owned),
198 mode(modeflags), pos(0), prev_op(FileOp::NONE), read_limit(0),
199 eof(false), err(false) {
200 adjust_buf();
203 // Close |f| and cleanup resources held by it.
204 // Returns the non-zero error value if an error occurs when closing the
205 // file.
206 static int cleanup(File *f) {
207 int close_result = f->close();
208 if (close_result != 0)
209 return close_result;
210 f->platform_cleanup(f);
211 return 0;
214 // Buffered write of |len| bytes from |data| without the file lock.
215 FileIOResult write_unlocked(const void *data, size_t len);
217 // Buffered write of |len| bytes from |data| under the file lock.
218 FileIOResult write(const void *data, size_t len) {
219 FileLock l(this);
220 return write_unlocked(data, len);
223 // Buffered read of |len| bytes into |data| without the file lock.
224 FileIOResult read_unlocked(void *data, size_t len);
226 // Buffered read of |len| bytes into |data| under the file lock.
227 FileIOResult read(void *data, size_t len) {
228 FileLock l(this);
229 return read_unlocked(data, len);
232 ErrorOr<int> seek(long offset, int whence);
234 ErrorOr<long> tell();
236 // If buffer has data written to it, flush it out. Does nothing if the
237 // buffer is currently being used as a read buffer.
238 int flush() {
239 FileLock lock(this);
240 return flush_unlocked();
243 int flush_unlocked();
245 // Returns EOF on error and keeps the file unchanged.
246 int ungetc_unlocked(int c);
248 int ungetc(int c) {
249 FileLock lock(this);
250 return ungetc_unlocked(c);
253 // Sets the internal buffer to |buffer| with buffering mode |mode|.
254 // |size| is the size of |buffer|. If |size| is non-zero, but |buffer|
255 // is nullptr, then a buffer owned by this file will be allocated.
256 // Else, |buffer| will not be owned by this file.
258 // Will return zero on success, or an error value on failure. Will fail
259 // if:
260 // 1. |buffer| is not a nullptr but |size| is zero.
261 // 2. |buffer_mode| is not one of _IOLBF, IOFBF or _IONBF.
262 // 3. If an allocation was required but the allocation failed.
263 // For cases 1 and 2, the error returned in EINVAL. For case 3, error returned
264 // is ENOMEM.
265 int set_buffer(void *buffer, size_t size, int buffer_mode);
267 void lock() { mutex.lock(); }
268 void unlock() { mutex.unlock(); }
270 bool error_unlocked() const { return err; }
272 bool error() {
273 FileLock l(this);
274 return error_unlocked();
277 void clearerr_unlocked() { err = false; }
279 void clearerr() {
280 FileLock l(this);
281 clearerr_unlocked();
284 bool iseof_unlocked() { return eof; }
286 bool iseof() {
287 FileLock l(this);
288 return iseof_unlocked();
291 // Returns an bit map of flags corresponding to enumerations of
292 // OpenMode, ContentType and CreateType.
293 static ModeFlags mode_flags(const char *mode);
295 private:
296 FileIOResult write_unlocked_lbf(const uint8_t *data, size_t len);
297 FileIOResult write_unlocked_fbf(const uint8_t *data, size_t len);
298 FileIOResult write_unlocked_nbf(const uint8_t *data, size_t len);
300 constexpr void adjust_buf() {
301 if (read_allowed() && (buf == nullptr || bufsize == 0)) {
302 // We should allow atleast one ungetc operation.
303 // This might give an impression that a buffer will be used even when
304 // the user does not want a buffer. But, that will not be the case.
305 // For reading, the buffering does not come into play. For writing, let
306 // us take up the three different kinds of buffering separately:
307 // 1. If user wants _IOFBF but gives a zero buffer, buffering still
308 // happens in the OS layer until the user flushes. So, from the user's
309 // point of view, this single byte buffer does not affect their
310 // experience.
311 // 2. If user wants _IOLBF but gives a zero buffer, the reasoning is
312 // very similar to the _IOFBF case.
313 // 3. If user wants _IONBF, then the buffer is ignored for writing.
314 // So, all of the above cases, having a single ungetc buffer does not
315 // affect the behavior experienced by the user.
316 buf = &ungetc_buf;
317 bufsize = 1;
318 own_buf = false; // We shouldn't call free on |buf| when closing the file.
323 // Platform specific file implementations can simply pass a pointer to a
324 // a specialization of this function as the CleanupFunc argument to the
325 // File constructor. The template type argument FileType should replaced
326 // with the type of the platform specific file implementation.
327 template <typename FileType> void cleanup_file(File *f) {
328 auto *file = reinterpret_cast<FileType *>(f);
329 delete file;
332 // The implementaiton of this function is provided by the platfrom_file
333 // library.
334 ErrorOr<File *> openfile(const char *path, const char *mode);
336 // The platform_file library should implement it if it relevant for that
337 // platform.
338 int get_fileno(File *f);
340 extern File *stdin;
341 extern File *stdout;
342 extern File *stderr;
344 } // namespace __llvm_libc
346 #endif // LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H