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