Serenity Operating System
at master 158 lines 4.0 kB view raw
1/* 2 * Copyright (c) 2021, the SerenityOS developers. 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#pragma once 8 9#include <AK/Array.h> 10#include <AK/IntrusiveList.h> 11#include <AK/Types.h> 12#include <bits/FILE.h> 13#include <bits/pthread_integration.h> 14#include <bits/wchar.h> 15#include <pthread.h> 16#include <sys/types.h> 17 18struct FILE { 19public: 20 FILE(int fd, int mode) 21 : m_fd(fd) 22 , m_mode(mode) 23 { 24 pthread_mutexattr_t attr = { __PTHREAD_MUTEX_RECURSIVE }; 25 pthread_mutex_init(&m_mutex, &attr); 26 } 27 ~FILE(); 28 29 static FILE* create(int fd, int mode); 30 31 void setbuf(u8* data, int mode, size_t size) { m_buffer.setbuf(data, mode, size); } 32 33 bool flush(); 34 void purge(); 35 size_t pending(); 36 bool close(); 37 38 void lock(); 39 void unlock(); 40 41 int fileno() const { return m_fd; } 42 bool eof() const { return m_eof; } 43 int mode() const { return m_mode; } 44 u8 flags() const { return m_flags; } 45 46 int error() const { return m_error; } 47 void clear_err() { m_error = 0; } 48 void set_err() { m_error = 1; } 49 50 size_t read(u8*, size_t); 51 size_t write(u8 const*, size_t); 52 53 template<typename CharType> 54 bool gets(CharType*, size_t); 55 56 bool ungetc(u8 byte) { return m_buffer.enqueue_front(byte); } 57 58 int seek(off_t offset, int whence); 59 off_t tell(); 60 61 pid_t popen_child() { return m_popen_child; } 62 void set_popen_child(pid_t child_pid) { m_popen_child = child_pid; } 63 64 void reopen(int fd, int mode); 65 66 u8 const* readptr(size_t& available_size); 67 void readptr_increase(size_t increment); 68 69 enum Flags : u8 { 70 None = 0, 71 LastRead = 1, 72 LastWrite = 2, 73 }; 74 75private: 76 struct Buffer { 77 // A ringbuffer that also transparently implements ungetc(). 78 public: 79 ~Buffer(); 80 81 int mode() const { return m_mode; } 82 void setbuf(u8* data, int mode, size_t size); 83 // Make sure to call realize() before enqueuing any data. 84 // Dequeuing can be attempted without it. 85 void realize(int fd); 86 void drop(); 87 88 bool may_use() const; 89 bool is_not_empty() const { return m_ungotten || !m_empty; } 90 size_t buffered_size() const; 91 92 u8 const* begin_dequeue(size_t& available_size) const; 93 void did_dequeue(size_t actual_size); 94 95 u8* begin_enqueue(size_t& available_size) const; 96 void did_enqueue(size_t actual_size); 97 98 bool enqueue_front(u8 byte); 99 100 private: 101 constexpr static auto unget_buffer_size = MB_CUR_MAX; 102 constexpr static u32 ungotten_mask = ((u32)0xffffffff) >> (sizeof(u32) * 8 - unget_buffer_size); 103 104 // Note: the fields here are arranged this way 105 // to make sizeof(Buffer) smaller. 106 u8* m_data { nullptr }; 107 size_t m_capacity { BUFSIZ }; 108 size_t m_begin { 0 }; 109 size_t m_end { 0 }; 110 111 int m_mode { -1 }; 112 Array<u8, unget_buffer_size> m_unget_buffer { 0 }; 113 u32 m_ungotten : unget_buffer_size { 0 }; 114 bool m_data_is_malloced : 1 { false }; 115 // When m_begin == m_end, we want to distinguish whether 116 // the buffer is full or empty. 117 bool m_empty : 1 { true }; 118 }; 119 120 // Read or write using the underlying fd, bypassing the buffer. 121 ssize_t do_read(u8*, size_t); 122 ssize_t do_write(u8 const*, size_t); 123 124 // Read some data into the buffer. 125 bool read_into_buffer(); 126 // Flush *some* data from the buffer. 127 bool write_from_buffer(); 128 129 int m_fd { -1 }; 130 int m_mode { 0 }; 131 u8 m_flags { Flags::None }; 132 int m_error { 0 }; 133 bool m_eof { false }; 134 pid_t m_popen_child { -1 }; 135 Buffer m_buffer; 136 __pthread_mutex_t m_mutex; 137 IntrusiveListNode<FILE> m_list_node; 138 139public: 140 using List = IntrusiveList<&FILE::m_list_node>; 141}; 142 143class ScopedFileLock { 144public: 145 ScopedFileLock(FILE* file) 146 : m_file(file) 147 { 148 m_file->lock(); 149 } 150 151 ~ScopedFileLock() 152 { 153 m_file->unlock(); 154 } 155 156private: 157 FILE* m_file; 158};