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