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//===----------------------------------------------------------------------===//
8
9#ifndef LLVM_LIBC_SRC___SUPPORT_FILE_FILE_H
10#define LLVM_LIBC_SRC___SUPPORT_FILE_FILE_H
11
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"
16
17#include <stddef.h>
18#include <stdint.h>
19
20namespace LIBC_NAMESPACE {
21
22struct FileIOResult {
23 size_t value;
24 int error;
25
26 constexpr FileIOResult(size_t val) : value(val), error(0) {}
27 constexpr FileIOResult(size_t val, int error) : value(val), error(error) {}
28
29 constexpr bool has_error() { return error != 0; }
30
31 constexpr operator size_t() { return value; }
32};
33
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.
37class File {
38public:
39 static constexpr size_t DEFAULT_BUFFER_SIZE = 1024;
40
41 using LockFunc = void(File *);
42 using UnlockFunc = void(File *);
43
44 using WriteFunc = FileIOResult(File *, const void *, size_t);
45 using ReadFunc = FileIOResult(File *, void *, size_t);
46 // The SeekFunc is expected to return the current offset of the external
47 // file position indicator.
48 using SeekFunc = ErrorOr<long>(File *, long, int);
49 using CloseFunc = int(File *);
50
51 using ModeFlags = uint32_t;
52
53 // The three different types of flags below are to be used with '|' operator.
54 // Their values correspond to mutually exclusive bits in a 32-bit unsigned
55 // integer value. A flag set can include both READ and WRITE if the file
56 // is opened in update mode (ie. if the file was opened with a '+' the mode
57 // string.)
58 enum class OpenMode : ModeFlags {
59 READ = 0x1,
60 WRITE = 0x2,
61 APPEND = 0x4,
62 PLUS = 0x8,
63 };
64
65 // Denotes a file opened in binary mode (which is specified by including
66 // the 'b' character in teh mode string.)
67 enum class ContentType : ModeFlags {
68 BINARY = 0x10,
69 };
70
71 // Denotes a file to be created for writing.
72 enum class CreateType : ModeFlags {
73 EXCLUSIVE = 0x100,
74 };
75
76private:
77 enum class FileOp : uint8_t { NONE, READ, WRITE, SEEK };
78
79 // Platform specific functions which create new file objects should initialize
80 // these fields suitably via the constructor. Typically, they should be simple
81 // syscall wrappers for the corresponding functionality.
82 WriteFunc *platform_write;
83 ReadFunc *platform_read;
84 SeekFunc *platform_seek;
85 CloseFunc *platform_close;
86
87 Mutex mutex;
88
89 // For files which are readable, we should be able to support one ungetc
90 // operation even if |buf| is nullptr. So, in the constructor of File, we
91 // set |buf| to point to this buffer character.
92 uint8_t ungetc_buf;
93
94 uint8_t *buf; // Pointer to the stream buffer for buffered streams
95 size_t bufsize; // Size of the buffer pointed to by |buf|.
96
97 // Buffering mode to used to buffer.
98 int bufmode;
99
100 // If own_buf is true, the |buf| is owned by the stream and will be
101 // free-ed when close method is called on the stream.
102 bool own_buf;
103
104 // The mode in which the file was opened.
105 ModeFlags mode;
106
107 // Current read or write pointer.
108 size_t pos;
109
110 // Represents the previous operation that was performed.
111 FileOp prev_op;
112
113 // When the buffer is used as a read buffer, read_limit is the upper limit
114 // of the index to which the buffer can be read until.
115 size_t read_limit;
116
117 bool eof;
118 bool err;
119
120 // This is a convenience RAII class to lock and unlock file objects.
121 class FileLock {
122 File *file;
123
124 public:
125 explicit FileLock(File *f) : file(f) { file->lock(); }
126
127 ~FileLock() { file->unlock(); }
128
129 FileLock(const FileLock &) = delete;
130 FileLock(FileLock &&) = delete;
131 };
132
133protected:
134 constexpr bool write_allowed() const {
135 return mode & (static_cast<ModeFlags>(OpenMode::WRITE) |
136 static_cast<ModeFlags>(OpenMode::APPEND) |
137 static_cast<ModeFlags>(OpenMode::PLUS));
138 }
139
140 constexpr bool read_allowed() const {
141 return mode & (static_cast<ModeFlags>(OpenMode::READ) |
142 static_cast<ModeFlags>(OpenMode::PLUS));
143 }
144
145public:
146 // We want this constructor to be constexpr so that global file objects
147 // like stdout do not require invocation of the constructor which can
148 // potentially lead to static initialization order fiasco. Consequently,
149 // we will assume that the |buffer| and |buffer_size| argument are
150 // meaningful - that is, |buffer| is nullptr if and only if |buffer_size|
151 // is zero. This way, we will not have to employ the semantics of
152 // the set_buffer method and allocate a buffer.
153 constexpr File(WriteFunc *wf, ReadFunc *rf, SeekFunc *sf, CloseFunc *cf,
154 uint8_t *buffer, size_t buffer_size, int buffer_mode,
155 bool owned, ModeFlags modeflags)
156 : platform_write(wf), platform_read(rf), platform_seek(sf),
157 platform_close(cf), mutex(false, false, false), ungetc_buf(0),
158 buf(buffer), bufsize(buffer_size), bufmode(buffer_mode), own_buf(owned),
159 mode(modeflags), pos(0), prev_op(FileOp::NONE), read_limit(0),
160 eof(false), err(false) {
161 adjust_buf();
162 }
163
164 // Buffered write of |len| bytes from |data| without the file lock.
165 FileIOResult write_unlocked(const void *data, size_t len);
166
167 // Buffered write of |len| bytes from |data| under the file lock.
168 FileIOResult write(const void *data, size_t len) {
169 FileLock l(this);
170 return write_unlocked(data, len);
171 }
172
173 // Buffered read of |len| bytes into |data| without the file lock.
174 FileIOResult read_unlocked(void *data, size_t len);
175
176 // Buffered read of |len| bytes into |data| under the file lock.
177 FileIOResult read(void *data, size_t len) {
178 FileLock l(this);
179 return read_unlocked(data, len);
180 }
181
182 ErrorOr<int> seek(long offset, int whence);
183
184 ErrorOr<long> tell();
185
186 // If buffer has data written to it, flush it out. Does nothing if the
187 // buffer is currently being used as a read buffer.
188 int flush() {
189 FileLock lock(this);
190 return flush_unlocked();
191 }
192
193 int flush_unlocked();
194
195 // Returns EOF on error and keeps the file unchanged.
196 int ungetc_unlocked(int c);
197
198 int ungetc(int c) {
199 FileLock lock(this);
200 return ungetc_unlocked(c);
201 }
202
203 // Does the following:
204 // 1. If in write mode, Write out any data present in the buffer.
205 // 2. Call platform_close.
206 // platform_close is expected to cleanup the complete file object.
207 int close() {
208 {
209 FileLock lock(this);
210 if (prev_op == FileOp::WRITE && pos > 0) {
211 auto buf_result = platform_write(this, buf, pos);
212 if (buf_result.has_error() || buf_result.value < pos) {
213 err = true;
214 return buf_result.error;
215 }
216 }
217 }
218
219 // If we own the buffer, delete it before calling the platform close
220 // implementation. The platform close should not need to access the buffer
221 // and we need to clean it up before the entire structure is removed.
222 if (own_buf)
223 delete buf;
224
225 // Platform close is expected to cleanup the file data structure which
226 // includes the file mutex. Hence, we call platform_close after releasing
227 // the file lock. Another thread doing file operations while a thread is
228 // closing the file is undefined behavior as per POSIX.
229 return platform_close(this);
230 }
231
232 // Sets the internal buffer to |buffer| with buffering mode |mode|.
233 // |size| is the size of |buffer|. If |size| is non-zero, but |buffer|
234 // is nullptr, then a buffer owned by this file will be allocated.
235 // Else, |buffer| will not be owned by this file.
236 //
237 // Will return zero on success, or an error value on failure. Will fail
238 // if:
239 // 1. |buffer| is not a nullptr but |size| is zero.
240 // 2. |buffer_mode| is not one of _IOLBF, IOFBF or _IONBF.
241 // 3. If an allocation was required but the allocation failed.
242 // For cases 1 and 2, the error returned in EINVAL. For case 3, error returned
243 // is ENOMEM.
244 int set_buffer(void *buffer, size_t size, int buffer_mode);
245
246 void lock() { mutex.lock(); }
247 void unlock() { mutex.unlock(); }
248
249 bool error_unlocked() const { return err; }
250
251 bool error() {
252 FileLock l(this);
253 return error_unlocked();
254 }
255
256 void clearerr_unlocked() { err = false; }
257
258 void clearerr() {
259 FileLock l(this);
260 clearerr_unlocked();
261 }
262
263 bool iseof_unlocked() { return eof; }
264
265 bool iseof() {
266 FileLock l(this);
267 return iseof_unlocked();
268 }
269
270 // Returns an bit map of flags corresponding to enumerations of
271 // OpenMode, ContentType and CreateType.
272 static ModeFlags mode_flags(const char *mode);
273
274private:
275 FileIOResult write_unlocked_lbf(const uint8_t *data, size_t len);
276 FileIOResult write_unlocked_fbf(const uint8_t *data, size_t len);
277 FileIOResult write_unlocked_nbf(const uint8_t *data, size_t len);
278
279 constexpr void adjust_buf() {
280 if (read_allowed() && (buf == nullptr || bufsize == 0)) {
281 // We should allow atleast one ungetc operation.
282 // This might give an impression that a buffer will be used even when
283 // the user does not want a buffer. But, that will not be the case.
284 // For reading, the buffering does not come into play. For writing, let
285 // us take up the three different kinds of buffering separately:
286 // 1. If user wants _IOFBF but gives a zero buffer, buffering still
287 // happens in the OS layer until the user flushes. So, from the user's
288 // point of view, this single byte buffer does not affect their
289 // experience.
290 // 2. If user wants _IOLBF but gives a zero buffer, the reasoning is
291 // very similar to the _IOFBF case.
292 // 3. If user wants _IONBF, then the buffer is ignored for writing.
293 // So, all of the above cases, having a single ungetc buffer does not
294 // affect the behavior experienced by the user.
295 buf = &ungetc_buf;
296 bufsize = 1;
297 own_buf = false; // We shouldn't call free on |buf| when closing the file.
298 }
299 }
300};
301
302// The implementaiton of this function is provided by the platform_file
303// library.
304ErrorOr<File *> openfile(const char *path, const char *mode);
305
306// The platform_file library should implement it if it relevant for that
307// platform.
308int get_fileno(File *f);
309
310extern File *stdin;
311extern File *stdout;
312extern File *stderr;
313
314} // namespace LIBC_NAMESPACE
315
316#endif // LLVM_LIBC_SRC___SUPPORT_FILE_FILE_H
317

source code of libc/src/__support/File/file.h