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_OSUTIL_FILE_H
10 #define LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H
11 
12 #include "src/__support/threads/mutex.h"
13 
14 #include <stddef.h>
15 #include <stdint.h>
16 
17 namespace __llvm_libc {
18 
19 // This a generic base class to encapsulate a platform independent file data
20 // structure. Platform specific specializations should create a subclass as
21 // suitable for their platform.
22 class File {
23 public:
24   static constexpr size_t DEFAULT_BUFFER_SIZE = 1024;
25 
26   using LockFunc = void(File *);
27   using UnlockFunc = void(File *);
28 
29   using WriteFunc = size_t(File *, const void *, size_t);
30   using ReadFunc = size_t(File *, void *, size_t);
31   using SeekFunc = int(File *, long, int);
32   using CloseFunc = int(File *);
33   using FlushFunc = int(File *);
34 
35   using ModeFlags = uint32_t;
36 
37   // The three different types of flags below are to be used with '|' operator.
38   // Their values correspond to mutually exclusive bits in a 32-bit unsigned
39   // integer value. A flag set can include both READ and WRITE if the file
40   // is opened in update mode (ie. if the file was opened with a '+' the mode
41   // string.)
42   enum class OpenMode : ModeFlags {
43     READ = 0x1,
44     WRITE = 0x2,
45     APPEND = 0x4,
46     PLUS = 0x8,
47   };
48 
49   // Denotes a file opened in binary mode (which is specified by including
50   // the 'b' character in teh mode string.)
51   enum class ContentType : ModeFlags {
52     BINARY = 0x10,
53   };
54 
55   // Denotes a file to be created for writing.
56   enum class CreateType : ModeFlags {
57     EXCLUSIVE = 0x100,
58   };
59 
60 private:
61   enum class FileOp : uint8_t { NONE, READ, WRITE, SEEK };
62 
63   // Platfrom specific functions which create new file objects should initialize
64   // these fields suitably via the constructor. Typically, they should be simple
65   // syscall wrappers for the corresponding functionality.
66   WriteFunc *platform_write;
67   ReadFunc *platform_read;
68   SeekFunc *platform_seek;
69   CloseFunc *platform_close;
70   FlushFunc *platform_flush;
71 
72   Mutex mutex;
73 
74   void *buf;      // Pointer to the stream buffer for buffered streams
75   size_t bufsize; // Size of the buffer pointed to by |buf|.
76 
77   // Buffering mode to used to buffer.
78   int bufmode;
79 
80   // If own_buf is true, the |buf| is owned by the stream and will be
81   // free-ed when close method is called on the stream.
82   bool own_buf;
83 
84   // The mode in which the file was opened.
85   ModeFlags mode;
86 
87   // Current read or write pointer.
88   size_t pos;
89 
90   // Represents the previous operation that was performed.
91   FileOp prev_op;
92 
93   // When the buffer is used as a read buffer, read_limit is the upper limit
94   // of the index to which the buffer can be read until.
95   size_t read_limit;
96 
97   bool eof;
98   bool err;
99 
100   // This is a convenience RAII class to lock and unlock file objects.
101   class FileLock {
102     File *file;
103 
104   public:
FileLock(File * f)105     explicit FileLock(File *f) : file(f) { file->lock(); }
106 
~FileLock()107     ~FileLock() { file->unlock(); }
108 
109     FileLock(const FileLock &) = delete;
110     FileLock(FileLock &&) = delete;
111   };
112 
113 protected:
write_allowed()114   bool write_allowed() const {
115     return mode & (static_cast<ModeFlags>(OpenMode::WRITE) |
116                    static_cast<ModeFlags>(OpenMode::APPEND) |
117                    static_cast<ModeFlags>(OpenMode::PLUS));
118   }
119 
read_allowed()120   bool read_allowed() const {
121     return mode & (static_cast<ModeFlags>(OpenMode::READ) |
122                    static_cast<ModeFlags>(OpenMode::PLUS));
123   }
124 
125 public:
126   // We want this constructor to be constexpr so that global file objects
127   // like stdout do not require invocation of the constructor which can
128   // potentially lead to static initialization order fiasco.
File(WriteFunc * wf,ReadFunc * rf,SeekFunc * sf,CloseFunc * cf,FlushFunc * ff,void * buffer,size_t buffer_size,int buffer_mode,bool owned,ModeFlags modeflags)129   constexpr File(WriteFunc *wf, ReadFunc *rf, SeekFunc *sf, CloseFunc *cf,
130                  FlushFunc *ff, void *buffer, size_t buffer_size,
131                  int buffer_mode, bool owned, ModeFlags modeflags)
132       : platform_write(wf), platform_read(rf), platform_seek(sf),
133         platform_close(cf), platform_flush(ff), mutex(false, false, false),
134         buf(buffer), bufsize(buffer_size), bufmode(buffer_mode), own_buf(owned),
135         mode(modeflags), pos(0), prev_op(FileOp::NONE), read_limit(0),
136         eof(false), err(false) {}
137 
138   // This function helps initialize the various fields of the File data
139   // structure after a allocating memory for it via a call to malloc.
init(File * f,WriteFunc * wf,ReadFunc * rf,SeekFunc * sf,CloseFunc * cf,FlushFunc * ff,void * buffer,size_t buffer_size,int buffer_mode,bool owned,ModeFlags modeflags)140   static void init(File *f, WriteFunc *wf, ReadFunc *rf, SeekFunc *sf,
141                    CloseFunc *cf, FlushFunc *ff, void *buffer,
142                    size_t buffer_size, int buffer_mode, bool owned,
143                    ModeFlags modeflags) {
144     Mutex::init(&f->mutex, false, false, false);
145     f->platform_write = wf;
146     f->platform_read = rf;
147     f->platform_seek = sf;
148     f->platform_close = cf;
149     f->platform_flush = ff;
150     f->buf = reinterpret_cast<uint8_t *>(buffer);
151     f->bufsize = buffer_size;
152     f->bufmode = buffer_mode;
153     f->own_buf = owned;
154     f->mode = modeflags;
155 
156     f->prev_op = FileOp::NONE;
157     f->read_limit = f->pos = 0;
158     f->eof = f->err = false;
159   }
160 
161   // Buffered write of |len| bytes from |data| without the file lock.
162   size_t write_unlocked(const void *data, size_t len);
163 
164   // Buffered write of |len| bytes from |data| under the file lock.
write(const void * data,size_t len)165   size_t write(const void *data, size_t len) {
166     FileLock l(this);
167     return write_unlocked(data, len);
168   }
169 
170   // Buffered read of |len| bytes into |data| without the file lock.
171   size_t read_unlocked(void *data, size_t len);
172 
173   // Buffered read of |len| bytes into |data| under the file lock.
read(void * data,size_t len)174   size_t read(void *data, size_t len) {
175     FileLock l(this);
176     return read_unlocked(data, len);
177   }
178 
179   int seek(long offset, int whence);
180 
181   // If buffer has data written to it, flush it out. Does nothing if the
182   // buffer is currently being used as a read buffer.
flush()183   int flush() {
184     FileLock lock(this);
185     return flush_unlocked();
186   }
187 
188   int flush_unlocked();
189 
190   // Sets the internal buffer to |buffer| with buffering mode |mode|.
191   // |size| is the size of |buffer|. This new |buffer| is owned by the
192   // stream only if |owned| is true.
193   void set_buffer(void *buffer, size_t size, bool owned);
194 
195   // Closes the file stream and frees up all resources owned by it.
196   int close();
197 
lock()198   void lock() { mutex.lock(); }
unlock()199   void unlock() { mutex.unlock(); }
200 
error_unlocked()201   bool error_unlocked() const { return err; }
202 
error()203   bool error() {
204     FileLock l(this);
205     return error_unlocked();
206   }
207 
clearerr_unlocked()208   void clearerr_unlocked() { err = false; }
209 
clearerr()210   void clearerr() {
211     FileLock l(this);
212     clearerr_unlocked();
213   }
214 
iseof_unlocked()215   bool iseof_unlocked() { return eof; }
216 
iseof()217   bool iseof() {
218     FileLock l(this);
219     return iseof_unlocked();
220   }
221 
222   // Returns an bit map of flags corresponding to enumerations of
223   // OpenMode, ContentType and CreateType.
224   static ModeFlags mode_flags(const char *mode);
225 
226 private:
227   size_t write_unlocked_lbf(const void *data, size_t len);
228   size_t write_unlocked_fbf(const void *data, size_t len);
229   size_t write_unlocked_nbf(const void *data, size_t len);
230 };
231 
232 // The implementaiton of this function is provided by the platfrom_file
233 // library.
234 File *openfile(const char *path, const char *mode);
235 
236 extern File *stdout;
237 extern File *stderr;
238 
239 } // namespace __llvm_libc
240 
241 #endif // LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H
242