1 //===--- Implementation of a platform independent file data structure -----===// 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 #include "file.h" 10 11 #include "src/__support/CPP/ArrayRef.h" 12 13 #include <errno.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 17 namespace __llvm_libc { 18 19 size_t File::write_unlocked(const void *data, size_t len) { 20 if (!write_allowed()) { 21 errno = EBADF; 22 err = true; 23 return 0; 24 } 25 26 prev_op = FileOp::WRITE; 27 28 if (bufmode == _IOFBF) { // fully buffered 29 return write_unlocked_fbf(data, len); 30 } else if (bufmode == _IOLBF) { // line buffered 31 return write_unlocked_lbf(data, len); 32 } else /*if (bufmode == _IONBF) */ { // unbuffered 33 size_t ret_val = write_unlocked_nbf(data, len); 34 flush_unlocked(); 35 return ret_val; 36 } 37 } 38 39 size_t File::write_unlocked_nbf(const void *data, size_t len) { 40 if (pos > 0) { // If the buffer is not empty 41 // Flush the buffer 42 const size_t write_size = pos; 43 size_t bytes_written = platform_write(this, buf, write_size); 44 pos = 0; // Buffer is now empty so reset pos to the beginning. 45 // If less bytes were written than expected, then an error occurred. 46 if (bytes_written < write_size) { 47 err = true; 48 return 0; // No bytes from data were written, so return 0. 49 } 50 } 51 52 size_t written = platform_write(this, data, len); 53 if (written < len) 54 err = true; 55 return written; 56 } 57 58 size_t File::write_unlocked_fbf(const void *data, size_t len) { 59 const size_t init_pos = pos; 60 const size_t bufspace = bufsize - pos; 61 62 // If data is too large to be buffered at all, then just write it unbuffered. 63 if (len > bufspace + bufsize) 64 return write_unlocked_nbf(data, len); 65 66 // we split |data| (conceptually) using the split point. Then we handle the 67 // two pieces separately. 68 const size_t split_point = len < bufspace ? len : bufspace; 69 70 // The primary piece is the piece of |data| we want to write to the buffer 71 // before flushing. It will always fit into the buffer, since the split point 72 // is defined as being min(len, bufspace), and it will always exist if len is 73 // non-zero. 74 cpp::ArrayRef<uint8_t> primary(data, split_point); 75 76 // The second piece is the remainder of |data|. It is written to the buffer if 77 // it fits, or written directly to the output if it doesn't. If the primary 78 // piece fits entirely in the buffer, the remainder may be nothing. 79 cpp::ArrayRef<uint8_t> remainder( 80 static_cast<const uint8_t *>(data) + split_point, len - split_point); 81 82 cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize); 83 84 // Copy the first piece into the buffer. 85 // TODO: Replace the for loop below with a call to internal memcpy. 86 for (size_t i = 0; i < primary.size(); ++i) 87 bufref[pos + i] = primary[i]; 88 pos += primary.size(); 89 90 // If there is no remainder, we can return early, since the first piece has 91 // fit completely into the buffer. 92 if (remainder.size() == 0) 93 return len; 94 95 // We need to flush the buffer now, since there is still data and the buffer 96 // is full. 97 const size_t write_size = pos; 98 size_t bytes_written = platform_write(this, buf, write_size); 99 pos = 0; // Buffer is now empty so reset pos to the beginning. 100 // If less bytes were written than expected, then an error occurred. Return 101 // the number of bytes that have been written from |data|. 102 if (bytes_written < write_size) { 103 err = true; 104 return bytes_written <= init_pos ? 0 : bytes_written - init_pos; 105 } 106 107 // The second piece is handled basically the same as the first, although we 108 // know that if the second piece has data in it then the buffer has been 109 // flushed, meaning that pos is always 0. 110 if (remainder.size() < bufsize) { 111 // TODO: Replace the for loop below with a call to internal memcpy. 112 for (size_t i = 0; i < remainder.size(); ++i) 113 bufref[i] = remainder[i]; 114 pos = remainder.size(); 115 } else { 116 size_t bytes_written = 117 platform_write(this, remainder.data(), remainder.size()); 118 119 // If less bytes were written than expected, then an error occurred. Return 120 // the number of bytes that have been written from |data|. 121 if (bytes_written < remainder.size()) { 122 err = true; 123 return primary.size() + bytes_written; 124 } 125 } 126 127 return len; 128 } 129 130 size_t File::write_unlocked_lbf(const void *data, size_t len) { 131 const size_t init_pos = pos; 132 const size_t bufspace = bufsize - pos; 133 134 constexpr char NEWLINE_CHAR = '\n'; 135 size_t last_newline = len; 136 for (size_t i = len - 1; i > 0; --i) { 137 if (static_cast<const char *>(data)[i] == NEWLINE_CHAR) { 138 last_newline = i; 139 break; 140 } 141 } 142 143 // If there is no newline, treat this as fully buffered. 144 if (last_newline == len) { 145 return write_unlocked_fbf(data, len); 146 } 147 148 // we split |data| (conceptually) using the split point. Then we handle the 149 // two pieces separately. 150 const size_t split_point = last_newline + 1; 151 152 // The primary piece is everything in |data| up to the newline. It's written 153 // unbuffered to the output. 154 cpp::ArrayRef<uint8_t> primary(data, split_point); 155 156 // The second piece is the remainder of |data|. It is written fully buffered, 157 // meaning it may stay in the buffer if it fits. 158 cpp::ArrayRef<uint8_t> remainder( 159 static_cast<const uint8_t *>(data) + split_point, len - split_point); 160 161 size_t written = 0; 162 163 written = write_unlocked_nbf(primary.data(), primary.size()); 164 if (written < primary.size()) { 165 err = true; 166 return written; 167 } 168 169 flush_unlocked(); 170 171 written += write_unlocked_fbf(remainder.data(), remainder.size()); 172 if (written < primary.size() + remainder.size()) { 173 err = true; 174 return written; 175 } 176 177 return len; 178 } 179 180 size_t File::read_unlocked(void *data, size_t len) { 181 if (!read_allowed()) { 182 errno = EBADF; 183 err = true; 184 return 0; 185 } 186 187 prev_op = FileOp::READ; 188 189 cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize); 190 cpp::MutableArrayRef<uint8_t> dataref(data, len); 191 192 // Because read_limit is always greater than equal to pos, 193 // available_data is never a wrapped around value. 194 size_t available_data = read_limit - pos; 195 if (len <= available_data) { 196 // TODO: Replace the for loop below with a call to internal memcpy. 197 for (size_t i = 0; i < len; ++i) 198 dataref[i] = bufref[i + pos]; 199 pos += len; 200 return len; 201 } 202 203 // Copy all of the available data. 204 // TODO: Replace the for loop with a call to internal memcpy. 205 for (size_t i = 0; i < available_data; ++i) 206 dataref[i] = bufref[i + pos]; 207 read_limit = pos = 0; // Reset the pointers. 208 209 size_t to_fetch = len - available_data; 210 if (to_fetch > bufsize) { 211 size_t fetched_size = platform_read(this, data, to_fetch); 212 if (fetched_size < to_fetch) { 213 if (errno == 0) 214 eof = true; 215 else 216 err = true; 217 return available_data + fetched_size; 218 } 219 return len; 220 } 221 222 // Fetch and buffer another buffer worth of data. 223 size_t fetched_size = platform_read(this, buf, bufsize); 224 read_limit += fetched_size; 225 size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size; 226 for (size_t i = 0; i < transfer_size; ++i) 227 dataref[i] = bufref[i]; 228 pos += transfer_size; 229 if (fetched_size < to_fetch) { 230 if (errno == 0) 231 eof = true; 232 else 233 err = true; 234 } 235 return transfer_size + available_data; 236 } 237 238 int File::seek(long offset, int whence) { 239 FileLock lock(this); 240 if (prev_op == FileOp::WRITE && pos > 0) { 241 size_t transferred_size = platform_write(this, buf, pos); 242 if (transferred_size < pos) { 243 err = true; 244 return -1; 245 } 246 } else if (prev_op == FileOp::READ && whence == SEEK_CUR) { 247 // More data could have been read out from the platform file than was 248 // required. So, we have to adjust the offset we pass to platform seek 249 // function. Note that read_limit >= pos is always true. 250 offset -= (read_limit - pos); 251 } 252 pos = read_limit = 0; 253 prev_op = FileOp::SEEK; 254 // Reset the eof flag as a seek might move the file positon to some place 255 // readable. 256 eof = false; 257 return platform_seek(this, offset, whence); 258 } 259 260 int File::flush_unlocked() { 261 if (prev_op == FileOp::WRITE && pos > 0) { 262 size_t transferred_size = platform_write(this, buf, pos); 263 if (transferred_size < pos) { 264 err = true; 265 return -1; 266 } 267 pos = 0; 268 return platform_flush(this); 269 } 270 // TODO: Add POSIX behavior for input streams. 271 return 0; 272 } 273 274 int File::close() { 275 { 276 FileLock lock(this); 277 if (prev_op == FileOp::WRITE && pos > 0) { 278 size_t transferred_size = platform_write(this, buf, pos); 279 if (transferred_size < pos) { 280 err = true; 281 return -1; 282 } 283 } 284 if (platform_close(this) != 0) 285 return -1; 286 if (own_buf) 287 free(buf); 288 } 289 free(this); 290 return 0; 291 } 292 293 void File::set_buffer(void *buffer, size_t size, bool owned) { 294 if (own_buf) 295 free(buf); 296 buf = buffer; 297 bufsize = size; 298 own_buf = owned; 299 } 300 301 File::ModeFlags File::mode_flags(const char *mode) { 302 // First character in |mode| should be 'a', 'r' or 'w'. 303 if (*mode != 'a' && *mode != 'r' && *mode != 'w') 304 return 0; 305 306 // There should be exaclty one main mode ('a', 'r' or 'w') character. 307 // If there are more than one main mode characters listed, then 308 // we will consider |mode| as incorrect and return 0; 309 int main_mode_count = 0; 310 311 ModeFlags flags = 0; 312 for (; *mode != '\0'; ++mode) { 313 switch (*mode) { 314 case 'r': 315 flags |= static_cast<ModeFlags>(OpenMode::READ); 316 ++main_mode_count; 317 break; 318 case 'w': 319 flags |= static_cast<ModeFlags>(OpenMode::WRITE); 320 ++main_mode_count; 321 break; 322 case '+': 323 flags |= static_cast<ModeFlags>(OpenMode::PLUS); 324 break; 325 case 'b': 326 flags |= static_cast<ModeFlags>(ContentType::BINARY); 327 break; 328 case 'a': 329 flags |= static_cast<ModeFlags>(OpenMode::APPEND); 330 ++main_mode_count; 331 break; 332 case 'x': 333 flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE); 334 break; 335 default: 336 return 0; 337 } 338 } 339 340 if (main_mode_count != 1) 341 return 0; 342 343 return flags; 344 } 345 346 } // namespace __llvm_libc 347