#include "file.h"
#include "src/__support/CPP/ArrayRef.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
namespace __llvm_libc {
size_t File::write_unlocked(const void *data, size_t len) {
if (!write_allowed()) {
errno = EBADF;
err = true;
return 0;
}
prev_op = FileOp::WRITE;
if (bufmode == _IOFBF) {
return write_unlocked_fbf(data, len);
} else if (bufmode == _IOLBF) {
return write_unlocked_lbf(data, len);
} else {
size_t ret_val = write_unlocked_nbf(data, len);
flush_unlocked();
return ret_val;
}
}
size_t File::write_unlocked_nbf(const void *data, size_t len) {
if (pos > 0) {
const size_t write_size = pos;
size_t bytes_written = platform_write(this, buf, write_size);
pos = 0;
if (bytes_written < write_size) {
err = true;
return 0;
}
}
size_t written = platform_write(this, data, len);
if (written < len)
err = true;
return written;
}
size_t File::write_unlocked_fbf(const void *data, size_t len) {
const size_t init_pos = pos;
const size_t bufspace = bufsize - pos;
if (len > bufspace + bufsize)
return write_unlocked_nbf(data, len);
const size_t split_point = len < bufspace ? len : bufspace;
cpp::ArrayRef<uint8_t> primary(data, split_point);
cpp::ArrayRef<uint8_t> remainder(
static_cast<const uint8_t *>(data) + split_point, len - split_point);
cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize);
for (size_t i = 0; i < primary.size(); ++i)
bufref[pos + i] = primary[i];
pos += primary.size();
if (remainder.size() == 0)
return len;
const size_t write_size = pos;
size_t bytes_written = platform_write(this, buf, write_size);
pos = 0;
if (bytes_written < write_size) {
err = true;
return bytes_written <= init_pos ? 0 : bytes_written - init_pos;
}
if (remainder.size() < bufsize) {
for (size_t i = 0; i < remainder.size(); ++i)
bufref[i] = remainder[i];
pos = remainder.size();
} else {
size_t bytes_written =
platform_write(this, remainder.data(), remainder.size());
if (bytes_written < remainder.size()) {
err = true;
return primary.size() + bytes_written;
}
}
return len;
}
size_t File::write_unlocked_lbf(const void *data, size_t len) {
constexpr char NEWLINE_CHAR = '\n';
size_t last_newline = len;
for (size_t i = len; i > 1; --i) {
if (static_cast<const char *>(data)[i - 1] == NEWLINE_CHAR) {
last_newline = i - 1;
break;
}
}
if (last_newline == len) {
return write_unlocked_fbf(data, len);
}
const size_t split_point = last_newline + 1;
cpp::ArrayRef<uint8_t> primary(data, split_point);
cpp::ArrayRef<uint8_t> remainder(
static_cast<const uint8_t *>(data) + split_point, len - split_point);
size_t written = 0;
written = write_unlocked_nbf(primary.data(), primary.size());
if (written < primary.size()) {
err = true;
return written;
}
flush_unlocked();
written += write_unlocked_fbf(remainder.data(), remainder.size());
if (written < len) {
err = true;
return written;
}
return len;
}
size_t File::read_unlocked(void *data, size_t len) {
if (!read_allowed()) {
errno = EBADF;
err = true;
return 0;
}
prev_op = FileOp::READ;
cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize);
cpp::MutableArrayRef<uint8_t> dataref(data, len);
size_t available_data = read_limit - pos;
if (len <= available_data) {
for (size_t i = 0; i < len; ++i)
dataref[i] = bufref[i + pos];
pos += len;
return len;
}
for (size_t i = 0; i < available_data; ++i)
dataref[i] = bufref[i + pos];
read_limit = pos = 0;
size_t to_fetch = len - available_data;
if (to_fetch > bufsize) {
size_t fetched_size = platform_read(this, data, to_fetch);
if (fetched_size < to_fetch) {
if (errno == 0)
eof = true;
else
err = true;
return available_data + fetched_size;
}
return len;
}
size_t fetched_size = platform_read(this, buf, bufsize);
read_limit += fetched_size;
size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size;
for (size_t i = 0; i < transfer_size; ++i)
dataref[i] = bufref[i];
pos += transfer_size;
if (fetched_size < to_fetch) {
if (errno == 0)
eof = true;
else
err = true;
}
return transfer_size + available_data;
}
int File::seek(long offset, int whence) {
FileLock lock(this);
if (prev_op == FileOp::WRITE && pos > 0) {
size_t transferred_size = platform_write(this, buf, pos);
if (transferred_size < pos) {
err = true;
return -1;
}
} else if (prev_op == FileOp::READ && whence == SEEK_CUR) {
offset -= (read_limit - pos);
}
pos = read_limit = 0;
prev_op = FileOp::SEEK;
eof = false;
return platform_seek(this, offset, whence);
}
int File::flush_unlocked() {
if (prev_op == FileOp::WRITE && pos > 0) {
size_t transferred_size = platform_write(this, buf, pos);
if (transferred_size < pos) {
err = true;
return -1;
}
pos = 0;
return platform_flush(this);
}
return 0;
}
int File::close() {
{
FileLock lock(this);
if (prev_op == FileOp::WRITE && pos > 0) {
size_t transferred_size = platform_write(this, buf, pos);
if (transferred_size < pos) {
err = true;
return -1;
}
}
if (platform_close(this) != 0)
return -1;
if (own_buf)
free(buf);
}
free(this);
return 0;
}
void File::set_buffer(void *buffer, size_t size, bool owned) {
if (own_buf)
free(buf);
buf = buffer;
bufsize = size;
own_buf = owned;
}
File::ModeFlags File::mode_flags(const char *mode) {
if (*mode != 'a' && *mode != 'r' && *mode != 'w')
return 0;
int main_mode_count = 0;
ModeFlags flags = 0;
for (; *mode != '\0'; ++mode) {
switch (*mode) {
case 'r':
flags |= static_cast<ModeFlags>(OpenMode::READ);
++main_mode_count;
break;
case 'w':
flags |= static_cast<ModeFlags>(OpenMode::WRITE);
++main_mode_count;
break;
case '+':
flags |= static_cast<ModeFlags>(OpenMode::PLUS);
break;
case 'b':
flags |= static_cast<ModeFlags>(ContentType::BINARY);
break;
case 'a':
flags |= static_cast<ModeFlags>(OpenMode::APPEND);
++main_mode_count;
break;
case 'x':
flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE);
break;
default:
return 0;
}
}
if (main_mode_count != 1)
return 0;
return flags;
}
}