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 cpp::ArrayRef<uint8_t> dataref(data, len); 29 cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize); 30 31 const size_t used = pos; 32 const size_t bufspace = bufsize - pos; 33 const size_t write_size = bufspace > len ? len : bufspace; 34 // TODO: Replace the for loop below with a call to internal memcpy. 35 for (size_t i = 0; i < write_size; ++i) 36 bufref[pos + i] = dataref[i]; 37 pos += write_size; 38 if (len < bufspace) 39 return len; 40 41 // If the control reaches beyond this point, it means that |data| 42 // is more than what can be accomodated in the buffer. So, we first 43 // flush out the buffer. 44 size_t bytes_written = platform_write(this, buf, bufsize); 45 pos = 0; // Buffer is now empty so reset pos to the beginning. 46 if (bytes_written < bufsize) { 47 err = true; 48 // If less bytes were written than expected, then there are two 49 // possibilities. 50 // 1. None of the bytes from |data| were flushed out. 51 if (bytes_written <= used) 52 return 0; 53 // 2. Some of the bytes from |data| were written 54 return bytes_written - used; 55 } 56 57 // If the remaining bytes from |data| can fit in the buffer, write 58 // into it. Else, write it directly to the platform stream. 59 size_t remaining = len - write_size; 60 if (remaining <= len) { 61 // TODO: Replace the for loop below with a call to internal memcpy. 62 for (size_t i = 0; i < remaining; ++i) 63 bufref[i] = dataref[i]; 64 pos += remaining; 65 return len; 66 } 67 68 size_t transferred = 69 platform_write(this, dataref.data() + write_size, remaining); 70 if (transferred < remaining) { 71 err = true; 72 return write_size + transferred; 73 } 74 return len; 75 } 76 77 size_t File::read_unlocked(void *data, size_t len) { 78 if (!read_allowed()) { 79 errno = EBADF; 80 err = true; 81 return 0; 82 } 83 84 prev_op = FileOp::READ; 85 86 cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize); 87 cpp::MutableArrayRef<uint8_t> dataref(data, len); 88 89 // Because read_limit is always greater than equal to pos, 90 // available_data is never a wrapped around value. 91 size_t available_data = read_limit - pos; 92 if (len <= available_data) { 93 // TODO: Replace the for loop below with a call to internal memcpy. 94 for (size_t i = 0; i < len; ++i) 95 dataref[i] = bufref[i + pos]; 96 pos += len; 97 return len; 98 } 99 100 // Copy all of the available data. 101 // TODO: Replace the for loop with a call to internal memcpy. 102 for (size_t i = 0; i < available_data; ++i) 103 dataref[i] = bufref[i + pos]; 104 read_limit = pos = 0; // Reset the pointers. 105 106 size_t to_fetch = len - available_data; 107 if (to_fetch > bufsize) { 108 size_t fetched_size = platform_read(this, data, to_fetch); 109 if (fetched_size < to_fetch) { 110 if (errno == 0) 111 eof = true; 112 else 113 err = true; 114 return available_data + fetched_size; 115 } 116 return len; 117 } 118 119 // Fetch and buffer another buffer worth of data. 120 size_t fetched_size = platform_read(this, buf, bufsize); 121 read_limit += fetched_size; 122 size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size; 123 for (size_t i = 0; i < transfer_size; ++i) 124 dataref[i] = bufref[i]; 125 pos += transfer_size; 126 if (fetched_size < to_fetch) { 127 if (errno == 0) 128 eof = true; 129 else 130 err = true; 131 } 132 return transfer_size + available_data; 133 } 134 135 int File::seek(long offset, int whence) { 136 FileLock lock(this); 137 if (prev_op == FileOp::WRITE && pos > 0) { 138 size_t transferred_size = platform_write(this, buf, pos); 139 if (transferred_size < pos) { 140 err = true; 141 return -1; 142 } 143 } else if (prev_op == FileOp::READ && whence == SEEK_CUR) { 144 // More data could have been read out from the platform file than was 145 // required. So, we have to adjust the offset we pass to platform seek 146 // function. Note that read_limit >= pos is always true. 147 offset -= (read_limit - pos); 148 } 149 pos = read_limit = 0; 150 prev_op = FileOp::SEEK; 151 // Reset the eof flag as a seek might move the file positon to some place 152 // readable. 153 eof = false; 154 return platform_seek(this, offset, whence); 155 } 156 157 int File::flush() { 158 FileLock lock(this); 159 if (prev_op == FileOp::WRITE && pos > 0) { 160 size_t transferred_size = platform_write(this, buf, pos); 161 if (transferred_size < pos) { 162 err = true; 163 return -1; 164 } 165 pos = 0; 166 return platform_flush(this); 167 } 168 // TODO: Add POSIX behavior for input streams. 169 return 0; 170 } 171 172 int File::close() { 173 { 174 FileLock lock(this); 175 if (prev_op == FileOp::WRITE && pos > 0) { 176 size_t transferred_size = platform_write(this, buf, pos); 177 if (transferred_size < pos) { 178 err = true; 179 return -1; 180 } 181 } 182 if (platform_close(this) != 0) 183 return -1; 184 if (own_buf) 185 free(buf); 186 } 187 free(this); 188 return 0; 189 } 190 191 void File::set_buffer(void *buffer, size_t size, bool owned) { 192 if (own_buf) 193 free(buf); 194 buf = buffer; 195 bufsize = size; 196 own_buf = owned; 197 } 198 199 File::ModeFlags File::mode_flags(const char *mode) { 200 // First character in |mode| should be 'a', 'r' or 'w'. 201 if (*mode != 'a' && *mode != 'r' && *mode != 'w') 202 return 0; 203 204 // There should be exaclty one main mode ('a', 'r' or 'w') character. 205 // If there are more than one main mode characters listed, then 206 // we will consider |mode| as incorrect and return 0; 207 int main_mode_count = 0; 208 209 ModeFlags flags = 0; 210 for (; *mode != '\0'; ++mode) { 211 switch (*mode) { 212 case 'r': 213 flags |= static_cast<ModeFlags>(OpenMode::READ); 214 ++main_mode_count; 215 break; 216 case 'w': 217 flags |= static_cast<ModeFlags>(OpenMode::WRITE); 218 ++main_mode_count; 219 break; 220 case '+': 221 flags |= static_cast<ModeFlags>(OpenMode::PLUS); 222 break; 223 case 'b': 224 flags |= static_cast<ModeFlags>(ContentType::BINARY); 225 break; 226 case 'a': 227 flags |= static_cast<ModeFlags>(OpenMode::APPEND); 228 ++main_mode_count; 229 break; 230 case 'x': 231 flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE); 232 break; 233 default: 234 return 0; 235 } 236 } 237 238 if (main_mode_count != 1) 239 return 0; 240 241 return flags; 242 } 243 244 } // namespace __llvm_libc 245