xref: /aosp_15_r20/external/llvm-libc/src/__support/File/file.h (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
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 "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"
19 
20 #include <stddef.h>
21 #include <stdint.h>
22 
23 namespace LIBC_NAMESPACE_DECL {
24 
25 struct FileIOResult {
26   size_t value;
27   int error;
28 
FileIOResultFileIOResult29   constexpr FileIOResult(size_t val) : value(val), error(0) {}
FileIOResultFileIOResult30   constexpr FileIOResult(size_t val, int error) : value(val), error(error) {}
31 
has_errorFileIOResult32   constexpr bool has_error() { return error != 0; }
33 
size_tFileIOResult34   constexpr operator size_t() { return value; }
35 };
36 
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.
40 class File {
41 public:
42   static constexpr size_t DEFAULT_BUFFER_SIZE = 1024;
43 
44   using LockFunc = void(File *);
45   using UnlockFunc = void(File *);
46 
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 *);
53 
54   using ModeFlags = uint32_t;
55 
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
60   // string.)
61   enum class OpenMode : ModeFlags {
62     READ = 0x1,
63     WRITE = 0x2,
64     APPEND = 0x4,
65     PLUS = 0x8,
66   };
67 
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 {
71     BINARY = 0x10,
72   };
73 
74   // Denotes a file to be created for writing.
75   enum class CreateType : ModeFlags {
76     EXCLUSIVE = 0x100,
77   };
78 
79 private:
80   enum class FileOp : uint8_t { NONE, READ, WRITE, SEEK };
81 
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;
89 
90   Mutex mutex;
91 
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.
95   uint8_t ungetc_buf;
96 
97   uint8_t *buf;   // Pointer to the stream buffer for buffered streams
98   size_t bufsize; // Size of the buffer pointed to by |buf|.
99 
100   // Buffering mode to used to buffer.
101   int bufmode;
102 
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.
105   bool own_buf;
106 
107   // The mode in which the file was opened.
108   ModeFlags mode;
109 
110   // Current read or write pointer.
111   size_t pos;
112 
113   // Represents the previous operation that was performed.
114   FileOp prev_op;
115 
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.
118   size_t read_limit;
119 
120   bool eof;
121   bool err;
122 
123   // This is a convenience RAII class to lock and unlock file objects.
124   class FileLock {
125     File *file;
126 
127   public:
FileLock(File * f)128     explicit FileLock(File *f) : file(f) { file->lock(); }
129 
~FileLock()130     ~FileLock() { file->unlock(); }
131 
132     FileLock(const FileLock &) = delete;
133     FileLock(FileLock &&) = delete;
134   };
135 
136 protected:
write_allowed()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));
141   }
142 
read_allowed()143   constexpr bool read_allowed() const {
144     return mode & (static_cast<ModeFlags>(OpenMode::READ) |
145                    static_cast<ModeFlags>(OpenMode::PLUS));
146   }
147 
148 public:
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.
File(WriteFunc * wf,ReadFunc * rf,SeekFunc * sf,CloseFunc * cf,uint8_t * buffer,size_t buffer_size,int buffer_mode,bool owned,ModeFlags modeflags)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) {
165     adjust_buf();
166   }
167 
168   // Buffered write of |len| bytes from |data| without the file lock.
169   FileIOResult write_unlocked(const void *data, size_t len);
170 
171   // Buffered write of |len| bytes from |data| under the file lock.
write(const void * data,size_t len)172   FileIOResult write(const void *data, size_t len) {
173     FileLock l(this);
174     return write_unlocked(data, len);
175   }
176 
177   // Buffered read of |len| bytes into |data| without the file lock.
178   FileIOResult read_unlocked(void *data, size_t len);
179 
180   // Buffered read of |len| bytes into |data| under the file lock.
read(void * data,size_t len)181   FileIOResult read(void *data, size_t len) {
182     FileLock l(this);
183     return read_unlocked(data, len);
184   }
185 
186   ErrorOr<int> seek(off_t offset, int whence);
187 
188   ErrorOr<off_t> tell();
189 
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.
flush()192   int flush() {
193     FileLock lock(this);
194     return flush_unlocked();
195   }
196 
197   int flush_unlocked();
198 
199   // Returns EOF on error and keeps the file unchanged.
200   int ungetc_unlocked(int c);
201 
ungetc(int c)202   int ungetc(int c) {
203     FileLock lock(this);
204     return ungetc_unlocked(c);
205   }
206 
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.
close()211   int close() {
212     {
213       FileLock lock(this);
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) {
217           err = true;
218           return buf_result.error;
219         }
220       }
221     }
222 
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.
226     if (own_buf)
227       delete buf;
228 
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);
234   }
235 
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.
240   //
241   // Will return zero on success, or an error value on failure. Will fail
242   // if:
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
247   // is ENOMEM.
248   int set_buffer(void *buffer, size_t size, int buffer_mode);
249 
lock()250   void lock() { mutex.lock(); }
unlock()251   void unlock() { mutex.unlock(); }
252 
error_unlocked()253   bool error_unlocked() const { return err; }
254 
error()255   bool error() {
256     FileLock l(this);
257     return error_unlocked();
258   }
259 
clearerr_unlocked()260   void clearerr_unlocked() { err = false; }
261 
clearerr()262   void clearerr() {
263     FileLock l(this);
264     clearerr_unlocked();
265   }
266 
iseof_unlocked()267   bool iseof_unlocked() { return eof; }
268 
iseof()269   bool iseof() {
270     FileLock l(this);
271     return iseof_unlocked();
272   }
273 
274   // Returns an bit map of flags corresponding to enumerations of
275   // OpenMode, ContentType and CreateType.
276   static ModeFlags mode_flags(const char *mode);
277 
278 private:
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);
282 
adjust_buf()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
293       //    experience.
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.
299       buf = &ungetc_buf;
300       bufsize = 1;
301       own_buf = false; // We shouldn't call free on |buf| when closing the file.
302     }
303   }
304 };
305 
306 // The implementaiton of this function is provided by the platform_file
307 // library.
308 ErrorOr<File *> openfile(const char *path, const char *mode);
309 
310 // The platform_file library should implement it if it relevant for that
311 // platform.
312 int get_fileno(File *f);
313 
314 extern File *stdin;
315 extern File *stdout;
316 extern File *stderr;
317 
318 } // namespace LIBC_NAMESPACE_DECL
319 
320 #endif // LLVM_LIBC_SRC___SUPPORT_FILE_FILE_H
321