39 changes: 26 additions & 13 deletions libc/src/__support/File/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,26 @@
#ifndef LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H
#define LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H

#include "src/__support/error_or.h"
#include "src/__support/threads/mutex.h"

#include <stddef.h>
#include <stdint.h>

namespace __llvm_libc {

struct FileIOResult {
size_t value;
int error;

constexpr FileIOResult(size_t val) : value(val), error(0) {}
constexpr FileIOResult(size_t val, int error) : value(val), error(error) {}

constexpr bool has_error() { return error != 0; }

constexpr operator size_t() { return value; }
};

// This a generic base class to encapsulate a platform independent file data
// structure. Platform specific specializations should create a subclass as
// suitable for their platform.
Expand All @@ -26,11 +39,11 @@ class File {
using LockFunc = void(File *);
using UnlockFunc = void(File *);

using WriteFunc = size_t(File *, const void *, size_t);
using ReadFunc = size_t(File *, void *, size_t);
using WriteFunc = FileIOResult(File *, const void *, size_t);
using ReadFunc = FileIOResult(File *, void *, size_t);
// The SeekFunc is expected to return the current offset of the external
// file position indicator.
using SeekFunc = long(File *, long, int);
using SeekFunc = ErrorOr<long>(File *, long, int);
using CloseFunc = int(File *);
using FlushFunc = int(File *);

Expand Down Expand Up @@ -174,26 +187,26 @@ class File {
}

// Buffered write of |len| bytes from |data| without the file lock.
size_t write_unlocked(const void *data, size_t len);
FileIOResult write_unlocked(const void *data, size_t len);

// Buffered write of |len| bytes from |data| under the file lock.
size_t write(const void *data, size_t len) {
FileIOResult write(const void *data, size_t len) {
FileLock l(this);
return write_unlocked(data, len);
}

// Buffered read of |len| bytes into |data| without the file lock.
size_t read_unlocked(void *data, size_t len);
FileIOResult read_unlocked(void *data, size_t len);

// Buffered read of |len| bytes into |data| under the file lock.
size_t read(void *data, size_t len) {
FileIOResult read(void *data, size_t len) {
FileLock l(this);
return read_unlocked(data, len);
}

int seek(long offset, int whence);
ErrorOr<int> seek(long offset, int whence);

long tell();
ErrorOr<long> tell();

// If buffer has data written to it, flush it out. Does nothing if the
// buffer is currently being used as a read buffer.
Expand Down Expand Up @@ -256,9 +269,9 @@ class File {
static ModeFlags mode_flags(const char *mode);

private:
size_t write_unlocked_lbf(const uint8_t *data, size_t len);
size_t write_unlocked_fbf(const uint8_t *data, size_t len);
size_t write_unlocked_nbf(const uint8_t *data, size_t len);
FileIOResult write_unlocked_lbf(const uint8_t *data, size_t len);
FileIOResult write_unlocked_fbf(const uint8_t *data, size_t len);
FileIOResult write_unlocked_nbf(const uint8_t *data, size_t len);

constexpr void adjust_buf() {
if (read_allowed() && (buf == nullptr || bufsize == 0)) {
Expand All @@ -285,7 +298,7 @@ class File {

// The implementaiton of this function is provided by the platfrom_file
// library.
File *openfile(const char *path, const char *mode);
ErrorOr<File *> openfile(const char *path, const char *mode);

// The platform_file library should implement it if it relevant for that
// platform.
Expand Down
57 changes: 26 additions & 31 deletions libc/src/__support/File/linux_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

#include "src/__support/OSUtil/syscall.h" // For internal syscall function.

#include <errno.h>
#include <errno.h> // For error macros
#include <fcntl.h> // For mode_t and other flags to the open syscall
#include <stdio.h>
#include <stdlib.h> // For malloc
Expand All @@ -20,9 +20,9 @@ namespace __llvm_libc {

namespace {

size_t write_func(File *, const void *, size_t);
size_t read_func(File *, void *, size_t);
long seek_func(File *, long, int);
FileIOResult write_func(File *, const void *, size_t);
FileIOResult read_func(File *, void *, size_t);
ErrorOr<long> seek_func(File *, long, int);
int close_func(File *);
int flush_func(File *);

Expand Down Expand Up @@ -51,75 +51,70 @@ class LinuxFile : public File {

namespace {

size_t write_func(File *f, const void *data, size_t size) {
FileIOResult write_func(File *f, const void *data, size_t size) {
auto *lf = reinterpret_cast<LinuxFile *>(f);
long ret = __llvm_libc::syscall_impl(SYS_write, lf->get_fd(), data, size);
int ret = __llvm_libc::syscall_impl(SYS_write, lf->get_fd(), data, size);
if (ret < 0) {
errno = -ret;
return 0;
return {0, -ret};
}
return ret;
}

size_t read_func(File *f, void *buf, size_t size) {
FileIOResult read_func(File *f, void *buf, size_t size) {
auto *lf = reinterpret_cast<LinuxFile *>(f);
long ret = __llvm_libc::syscall_impl(SYS_read, lf->get_fd(), buf, size);
int ret = __llvm_libc::syscall_impl(SYS_read, lf->get_fd(), buf, size);
if (ret < 0) {
errno = -ret;
return 0;
return {0, -ret};
}
return ret;
}

long seek_func(File *f, long offset, int whence) {
ErrorOr<long> seek_func(File *f, long offset, int whence) {
auto *lf = reinterpret_cast<LinuxFile *>(f);
long result;
#ifdef SYS_lseek
long ret = __llvm_libc::syscall_impl(SYS_lseek, lf->get_fd(), offset, whence);
int ret = __llvm_libc::syscall_impl(SYS_lseek, lf->get_fd(), offset, whence);
result = ret;
#elif defined(SYS__llseek)
long result;
long ret = __llvm_libc::syscall_impl(SYS__llseek, lf->get_fd(), offset >> 32,
offset, &result, whence);
int ret = __llvm_libc::syscall_impl(SYS__llseek, lf->get_fd(), offset >> 32,
offset, &result, whence);
#else
#error "lseek and _llseek syscalls not available to perform a seek operation."
#endif

if (ret < 0) {
errno = -ret;
return -1;
}
if (ret < 0)
return Error(-ret);

return result;
}

int close_func(File *f) {
auto *lf = reinterpret_cast<LinuxFile *>(f);
long ret = __llvm_libc::syscall_impl(SYS_close, lf->get_fd());
int ret = __llvm_libc::syscall_impl(SYS_close, lf->get_fd());
if (ret < 0) {
errno = -ret;
return -1;
return -ret;
}
return 0;
}

int flush_func(File *f) {
auto *lf = reinterpret_cast<LinuxFile *>(f);
long ret = __llvm_libc::syscall_impl(SYS_fsync, lf->get_fd());
int ret = __llvm_libc::syscall_impl(SYS_fsync, lf->get_fd());
if (ret < 0) {
errno = -ret;
return -1;
return -ret;
}
return 0;
}

} // anonymous namespace

File *openfile(const char *path, const char *mode) {
ErrorOr<File *> openfile(const char *path, const char *mode) {
using ModeFlags = File::ModeFlags;
auto modeflags = File::mode_flags(mode);
if (modeflags == 0) {
errno = EINVAL;
return nullptr;
// return {nullptr, EINVAL};
return Error(EINVAL);
}
long open_flags = 0;
if (modeflags & ModeFlags(File::OpenMode::APPEND)) {
Expand Down Expand Up @@ -155,8 +150,8 @@ File *openfile(const char *path, const char *mode) {
#endif

if (fd < 0) {
errno = -fd;
return nullptr;
return Error(-fd);
// return {nullptr, -fd};
}

void *buffer = malloc(File::DEFAULT_BUFFER_SIZE);
Expand Down
39 changes: 39 additions & 0 deletions libc/src/__support/error_or.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//===-- A data structure for returning an error or a value ------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SUPPORT_ERROR_OR_RESULT_H
#define LLVM_LIBC_SUPPORT_ERROR_OR_RESULT_H

#include "src/__support/CPP/expected.h"

namespace __llvm_libc {

template <class T> using ErrorOr = cpp::expected<T, int>;

using Error = cpp::unexpected<int>;

// template <typename T> struct ErrorOr {
// union {
// T value;
// int error;
// };
// bool is_error;

// constexpr ErrorOr(T value) : value(value), is_error(false) {}
// constexpr ErrorOr(int error, bool is_error)
// : error(error), is_error(is_error) {}

// constexpr bool has_error() { return is_error; }

// constexpr operator bool() { return is_error; }
// constexpr operator T() { return value; }
// };

} // namespace __llvm_libc

#endif // LLVM_LIBC_SUPPORT_ERROR_OR_RESULT_H
2 changes: 1 addition & 1 deletion libc/src/__support/threads/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ add_object_library(
libc.config.linux.app_h
libc.include.sys_syscall
libc.src.__support.CPP.atomic
libc.src.__support.CPP.error
libc.src.__support.error_or
libc.src.__support.CPP.stringstream
libc.src.__support.CPP.string_view
libc.src.__support.threads.thread_common
Expand Down
17 changes: 8 additions & 9 deletions libc/src/__support/threads/linux/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

#include "src/__support/threads/thread.h"
#include "config/linux/app.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/error.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/CPP/stringstream.h"
#include "src/__support/OSUtil/syscall.h" // For syscall functions.
#include "src/__support/OSUtil/syscall.h" // For syscall functions.
#include "src/__support/error_or.h"
#include "src/__support/threads/linux/futex_word.h" // For FutexWordType

#ifdef LLVM_LIBC_ARCH_AARCH64
Expand Down Expand Up @@ -54,7 +54,7 @@ static constexpr unsigned CLONE_SYSCALL_FLAGS =
// wake the joining thread.
| CLONE_SETTLS; // Setup the thread pointer of the new thread.

static inline cpp::ErrorOr<void *> alloc_stack(size_t size) {
static inline ErrorOr<void *> alloc_stack(size_t size) {
long mmap_result =
__llvm_libc::syscall_impl(MMAP_SYSCALL_NUMBER,
0, // No special address
Expand All @@ -65,7 +65,7 @@ static inline cpp::ErrorOr<void *> alloc_stack(size_t size) {
0 // No offset
);
if (mmap_result < 0 && (uintptr_t(mmap_result) >= UINTPTR_MAX - size))
return cpp::Error{int(-mmap_result)};
return Error{int(-mmap_result)};
return reinterpret_cast<void *>(mmap_result);
}

Expand Down Expand Up @@ -113,8 +113,7 @@ __attribute__((always_inline)) inline uintptr_t get_start_args_addr() {
#endif
}

__attribute__((noinline))
static void start_thread() {
__attribute__((noinline)) static void start_thread() {
auto *start_args = reinterpret_cast<StartArgs *>(get_start_args_addr());
auto *attrib = start_args->thread_attrib;
self.attrib = attrib;
Expand All @@ -141,7 +140,7 @@ int Thread::run(ThreadStyle style, ThreadRunner runner, void *arg, void *stack,
size = DEFAULT_STACK_SIZE;
auto alloc = alloc_stack(size);
if (!alloc)
return alloc.error_code();
return alloc.error();
else
stack = alloc.value();
owned_stack = true;
Expand Down Expand Up @@ -374,7 +373,7 @@ void thread_exit(ThreadReturnValue retval, ThreadStyle style) {
// These callbacks could be the ones registered by the language runtimes,
// for example, the destructors of thread local objects. They can also
// be destructors of the TSS objects set using API like pthread_setspecific.
// NOTE: We cannot call the atexit callbacks as part of the
// NOTE: We cannot call the atexit callbacks as part of the
// cleanup_thread_resources function as that function can be called from a
// different thread. The destructors of thread local and TSS objects should
// be called by the thread which owns them.
Expand Down
1 change: 0 additions & 1 deletion libc/src/pthread/pthread_setname_np.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "pthread_setname_np.h"

#include "src/__support/CPP/string_view.h"
#include "src/__support/CPP/error.h"
#include "src/__support/common.h"
#include "src/__support/threads/thread.h"

Expand Down
18 changes: 18 additions & 0 deletions libc/src/stdio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ add_entrypoint_object(
fclose.h
DEPENDS
libc.include.stdio
libc.include.errno
libc.src.__support.File.file
libc.src.__support.File.platform_file
)
Expand Down Expand Up @@ -108,6 +109,7 @@ add_entrypoint_object(
HDRS
fgetc.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -120,6 +122,7 @@ add_entrypoint_object(
HDRS
fgetc_unlocked.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -132,6 +135,7 @@ add_entrypoint_object(
HDRS
getc.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -144,6 +148,7 @@ add_entrypoint_object(
HDRS
getc_unlocked.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -156,6 +161,7 @@ add_entrypoint_object(
HDRS
fgets.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -168,6 +174,7 @@ add_entrypoint_object(
HDRS
fflush.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand Down Expand Up @@ -204,6 +211,7 @@ add_entrypoint_object(
HDRS
fread_unlocked.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -216,6 +224,7 @@ add_entrypoint_object(
HDRS
fread.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -228,6 +237,7 @@ add_entrypoint_object(
HDRS
fwrite_unlocked.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -240,6 +250,7 @@ add_entrypoint_object(
HDRS
fwrite.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -252,6 +263,7 @@ add_entrypoint_object(
HDRS
fputc.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -264,6 +276,7 @@ add_entrypoint_object(
HDRS
putc.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -276,6 +289,7 @@ add_entrypoint_object(
HDRS
putchar.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -288,6 +302,7 @@ add_entrypoint_object(
HDRS
fputs.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -301,6 +316,7 @@ add_entrypoint_object(
HDRS
puts.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand All @@ -313,6 +329,7 @@ add_entrypoint_object(
HDRS
fseek.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand Down Expand Up @@ -494,6 +511,7 @@ add_entrypoint_object(
HDRS
ftell.h
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
Expand Down
8 changes: 7 additions & 1 deletion libc/src/stdio/fclose.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
#include "src/stdio/fclose.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, fclose, (::FILE * stream)) {
return reinterpret_cast<__llvm_libc::File *>(stream)->close();
int result = reinterpret_cast<__llvm_libc::File *>(stream)->close();
if (result != 0) {
errno = result;
return EOF;
}
return 0;
}

} // namespace __llvm_libc
8 changes: 7 additions & 1 deletion libc/src/stdio/fflush.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
#include "src/stdio/fflush.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, fflush, (::FILE * stream)) {
return reinterpret_cast<__llvm_libc::File *>(stream)->flush();
int result = reinterpret_cast<__llvm_libc::File *>(stream)->flush();
if (result != 0) {
errno = result;
return EOF;
}
return 0;
}

} // namespace __llvm_libc
7 changes: 6 additions & 1 deletion libc/src/stdio/fgetc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@
#include "src/stdio/fgetc.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, fgetc, (::FILE * stream)) {
unsigned char c;
size_t r = reinterpret_cast<__llvm_libc::File *>(stream)->read(&c, 1);
auto result = reinterpret_cast<__llvm_libc::File *>(stream)->read(&c, 1);
size_t r = result.value;
if (result.has_error())
errno = result.error;

if (r != 1)
return EOF;
return c;
Expand Down
6 changes: 5 additions & 1 deletion libc/src/stdio/fgetc_unlocked.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@
#include "src/stdio/fgetc_unlocked.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, fgetc_unlocked, (::FILE * stream)) {
unsigned char c;
size_t r =
auto result =
reinterpret_cast<__llvm_libc::File *>(stream)->read_unlocked(&c, 1);
size_t r = result.value;
if (result.has_error())
errno = result.error;
if (r != 1)
return EOF;
return c;
Expand Down
7 changes: 6 additions & 1 deletion libc/src/stdio/fgets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "src/stdio/fgets.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stddef.h>
#include <stdio.h>

Expand All @@ -28,7 +29,11 @@ LLVM_LIBC_FUNCTION(char *, fgets,
int i = 0;

for (; i < (count - 1) && c != '\n'; ++i) {
size_t r = stream->read_unlocked(&c, 1);
auto result = stream->read_unlocked(&c, 1);
size_t r = result.value;
if (result.has_error())
errno = result.error;

if (r != 1)
break;
str[i] = c;
Expand Down
8 changes: 7 additions & 1 deletion libc/src/stdio/fopen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@
#include "src/stdio/fopen.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(::FILE *, fopen,
(const char *__restrict name, const char *__restrict mode)) {
return reinterpret_cast<::FILE *>(__llvm_libc::openfile(name, mode));
auto result = __llvm_libc::openfile(name, mode);
if (!result.has_value()) {
errno = result.error();
return nullptr;
}
return reinterpret_cast<::FILE *>(result.value());
}

} // namespace __llvm_libc
9 changes: 4 additions & 5 deletions libc/src/stdio/fopencookie.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,26 @@ class CookieFile : public __llvm_libc::File {
cookie_io_functions_t ops;
};

size_t write_func(File *f, const void *data, size_t size) {
FileIOResult write_func(File *f, const void *data, size_t size) {
auto cookie_file = reinterpret_cast<CookieFile *>(f);
if (cookie_file->ops.write == nullptr)
return 0;
return cookie_file->ops.write(cookie_file->cookie,
reinterpret_cast<const char *>(data), size);
}

size_t read_func(File *f, void *data, size_t size) {
FileIOResult read_func(File *f, void *data, size_t size) {
auto cookie_file = reinterpret_cast<CookieFile *>(f);
if (cookie_file->ops.read == nullptr)
return 0;
return cookie_file->ops.read(cookie_file->cookie,
reinterpret_cast<char *>(data), size);
}

long seek_func(File *f, long offset, int whence) {
ErrorOr<long> seek_func(File *f, long offset, int whence) {
auto cookie_file = reinterpret_cast<CookieFile *>(f);
if (cookie_file->ops.seek == nullptr) {
errno = EINVAL;
return -1;
return Error(EINVAL);
}
off64_t offset64 = offset;
int result = cookie_file->ops.seek(cookie_file->cookie, &offset64, whence);
Expand Down
8 changes: 7 additions & 1 deletion libc/src/stdio/fputc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@
#include "src/stdio/fputc.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, fputc, (int c, ::FILE *stream)) {
unsigned char uc = static_cast<unsigned char>(c);
size_t written = reinterpret_cast<__llvm_libc::File *>(stream)->write(&uc, 1);

auto result = reinterpret_cast<__llvm_libc::File *>(stream)->write(&uc, 1);
if (result.has_error())
errno = result.error;
size_t written = result.value;

if (1 != written) {
// The stream should be in an error state in this case.
return EOF;
Expand Down
7 changes: 6 additions & 1 deletion libc/src/stdio/fputs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "src/__support/CPP/string_view.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {
Expand All @@ -18,8 +19,12 @@ LLVM_LIBC_FUNCTION(int, fputs,
(const char *__restrict str, ::FILE *__restrict stream)) {
cpp::string_view str_view(str);

size_t written = reinterpret_cast<__llvm_libc::File *>(stream)->write(
auto result = reinterpret_cast<__llvm_libc::File *>(stream)->write(
str, str_view.size());
if (result.has_error())
errno = result.error;
size_t written = result.value;

if (str_view.size() != written) {
// The stream should be in an error state in this case.
return EOF;
Expand Down
9 changes: 6 additions & 3 deletions libc/src/stdio/fread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "src/stdio/fread.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {
Expand All @@ -18,9 +19,11 @@ LLVM_LIBC_FUNCTION(size_t, fread,
::FILE *stream)) {
if (size == 0 || nmemb == 0)
return 0;
return reinterpret_cast<__llvm_libc::File *>(stream)->read(buffer,
size * nmemb) /
size;
auto result =
reinterpret_cast<__llvm_libc::File *>(stream)->read(buffer, size * nmemb);
if (result.has_error())
errno = result.error;
return result.value / size;
}

} // namespace __llvm_libc
8 changes: 7 additions & 1 deletion libc/src/stdio/fread_unlocked.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,21 @@
#include "src/stdio/fread_unlocked.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(size_t, fread_unlocked,
(void *__restrict buffer, size_t size, size_t nmemb,
::FILE *stream)) {
return reinterpret_cast<__llvm_libc::File *>(stream)->read_unlocked(
if (size == 0 || nmemb == 0)
return 0;
auto result = reinterpret_cast<__llvm_libc::File *>(stream)->read_unlocked(
buffer, size * nmemb);
if (result.has_error())
errno = result.error;
return result.value / size;
}

} // namespace __llvm_libc
9 changes: 8 additions & 1 deletion libc/src/stdio/fseek.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,19 @@
#include "src/stdio/fseek.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, fseek, (::FILE * stream, long offset, int whence)) {
return reinterpret_cast<__llvm_libc::File *>(stream)->seek(offset, whence);
auto result =
reinterpret_cast<__llvm_libc::File *>(stream)->seek(offset, whence);
if (!result.has_value()) {
errno = result.error();
return -1;
}
return 0;
}

} // namespace __llvm_libc
8 changes: 7 additions & 1 deletion libc/src/stdio/ftell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
#include "src/stdio/ftell.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(long, ftell, (::FILE * stream)) {
return reinterpret_cast<__llvm_libc::File *>(stream)->tell();
auto result = reinterpret_cast<__llvm_libc::File *>(stream)->tell();
if (!result.has_value()) {
errno = result.error();
return -1;
}
return result.value();
}

} // namespace __llvm_libc
10 changes: 7 additions & 3 deletions libc/src/stdio/fwrite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "src/stdio/fwrite.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {
Expand All @@ -18,9 +19,12 @@ LLVM_LIBC_FUNCTION(size_t, fwrite,
::FILE *stream)) {
if (size == 0 || nmemb == 0)
return 0;
return reinterpret_cast<__llvm_libc::File *>(stream)->write(buffer,
size * nmemb) /
size;
auto result = reinterpret_cast<__llvm_libc::File *>(stream)->write(
buffer, size * nmemb);
if (result.has_error())
errno = result.error;

return result.value / size;
}

} // namespace __llvm_libc
10 changes: 9 additions & 1 deletion libc/src/stdio/fwrite_unlocked.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,23 @@
#include "src/stdio/fwrite_unlocked.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(size_t, fwrite_unlocked,
(const void *__restrict buffer, size_t size, size_t nmemb,
::FILE *stream)) {
return reinterpret_cast<__llvm_libc::File *>(stream)->write_unlocked(

if (size == 0 || nmemb == 0)
return 0;
auto result = reinterpret_cast<__llvm_libc::File *>(stream)->write_unlocked(
buffer, size * nmemb);
if (result.has_error())
errno = result.error;

return result.value / size;
}

} // namespace __llvm_libc
7 changes: 6 additions & 1 deletion libc/src/stdio/getc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@
#include "src/stdio/getc.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, getc, (::FILE * stream)) {
unsigned char c;
size_t r = reinterpret_cast<__llvm_libc::File *>(stream)->read(&c, 1);
auto result = reinterpret_cast<__llvm_libc::File *>(stream)->read(&c, 1);
size_t r = result.value;
if (result.has_error())
errno = result.error;

if (r != 1)
return EOF;
return c;
Expand Down
7 changes: 6 additions & 1 deletion libc/src/stdio/getc_unlocked.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@
#include "src/stdio/getc_unlocked.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, getc_unlocked, (::FILE * stream)) {
unsigned char c;
size_t r =
auto result =
reinterpret_cast<__llvm_libc::File *>(stream)->read_unlocked(&c, 1);
size_t r = result.value;
if (result.has_error())
errno = result.error;

if (r != 1)
return EOF;
return c;
Expand Down
5 changes: 3 additions & 2 deletions libc/src/stdio/printf_core/file_writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ namespace __llvm_libc {
namespace printf_core {

int FileWriter::write(const char *__restrict to_write, size_t len) {
int written = file->write_unlocked(to_write, len);
if (written != static_cast<int>(len))
auto result = file->write_unlocked(to_write, len);
int written = result.value;
if (written != static_cast<int>(len) || result.has_error())
written = FILE_WRITE_ERROR;
if (file->error_unlocked())
written = FILE_STATUS_ERROR;
Expand Down
8 changes: 7 additions & 1 deletion libc/src/stdio/putc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@
#include "src/stdio/putc.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, putc, (int c, ::FILE *stream)) {
unsigned char uc = static_cast<unsigned char>(c);
size_t written = reinterpret_cast<__llvm_libc::File *>(stream)->write(&uc, 1);

auto result = reinterpret_cast<__llvm_libc::File *>(stream)->write(&uc, 1);
if (result.has_error())
errno = result.error;
size_t written = result.value;

if (1 != written) {
// The stream should be in an error state in this case.
return EOF;
Expand Down
8 changes: 7 additions & 1 deletion libc/src/stdio/putchar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@
#include "src/stdio/putchar.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, putchar, (int c)) {
unsigned char uc = static_cast<unsigned char>(c);
size_t written = __llvm_libc::stdout->write(&uc, 1);

auto result = __llvm_libc::stdout->write(&uc, 1);
if (result.has_error())
errno = result.error;
size_t written = result.value;

if (1 != written) {
// The stream should be in an error state in this case.
return EOF;
Expand Down
11 changes: 9 additions & 2 deletions libc/src/stdio/puts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,25 @@
#include "src/__support/CPP/string_view.h"
#include "src/__support/File/file.h"

#include <errno.h>
#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, puts, (const char *__restrict str)) {
cpp::string_view str_view(str);
size_t written = __llvm_libc::stdout->write(str, str_view.size());
auto result = __llvm_libc::stdout->write(str, str_view.size());
if (result.has_error())
errno = result.error;
size_t written = result.value;
if (str_view.size() != written) {
// The stream should be in an error state in this case.
return EOF;
}
written = __llvm_libc::stdout->write("\n", 1);
result = __llvm_libc::stdout->write("\n", 1);
if (result.has_error())
errno = result.error;
written = result.value;
if (1 != written) {
// The stream should be in an error state in this case.
return EOF;
Expand Down
3 changes: 2 additions & 1 deletion libc/src/stdio/scanf_core/file_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ namespace scanf_core {

char FileReader::get_char() {
char tiny_buff = 0;
if (file->read_unlocked(&tiny_buff, 1) != 1)
auto result = file->read_unlocked(&tiny_buff, 1);
if (result.value != 1 || result.has_error())
return 0;
return tiny_buff;
}
Expand Down
130 changes: 71 additions & 59 deletions libc/test/src/__support/File/file_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
//===----------------------------------------------------------------------===//

#include "src/__support/File/file.h"
#include "src/__support/error_or.h"
#include "utils/UnitTest/MemoryMatcher.h"
#include "utils/UnitTest/Test.h"

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

using ModeFlags = __llvm_libc::File::ModeFlags;
using MemoryView = __llvm_libc::memory::testing::MemoryView;
using __llvm_libc::ErrorOr;
using __llvm_libc::FileIOResult;

class StringFile : public __llvm_libc::File {
static constexpr size_t SIZE = 512;
Expand All @@ -24,9 +26,10 @@ class StringFile : public __llvm_libc::File {
size_t eof_marker;
bool write_append;

static size_t str_read(__llvm_libc::File *f, void *data, size_t len);
static size_t str_write(__llvm_libc::File *f, const void *data, size_t len);
static long str_seek(__llvm_libc::File *f, long offset, int whence);
static FileIOResult str_read(__llvm_libc::File *f, void *data, size_t len);
static FileIOResult str_write(__llvm_libc::File *f, const void *data,
size_t len);
static ErrorOr<long> str_seek(__llvm_libc::File *f, long offset, int whence);
static int str_close(__llvm_libc::File *f) { return 0; }
static int str_flush(__llvm_libc::File *f) { return 0; }

Expand Down Expand Up @@ -67,7 +70,8 @@ class StringFile : public __llvm_libc::File {
}
};

size_t StringFile::str_read(__llvm_libc::File *f, void *data, size_t len) {
FileIOResult StringFile::str_read(__llvm_libc::File *f, void *data,
size_t len) {
StringFile *sf = static_cast<StringFile *>(f);
if (sf->pos >= sf->eof_marker)
return 0;
Expand All @@ -78,8 +82,8 @@ size_t StringFile::str_read(__llvm_libc::File *f, void *data, size_t len) {
return i;
}

size_t StringFile::str_write(__llvm_libc::File *f, const void *data,
size_t len) {
FileIOResult StringFile::str_write(__llvm_libc::File *f, const void *data,
size_t len) {
StringFile *sf = static_cast<StringFile *>(f);
if (sf->write_append)
sf->pos = sf->eof_marker;
Expand All @@ -94,7 +98,8 @@ size_t StringFile::str_write(__llvm_libc::File *f, const void *data,
return i;
}

long StringFile::str_seek(__llvm_libc::File *f, long offset, int whence) {
ErrorOr<long> StringFile::str_seek(__llvm_libc::File *f, long offset,
int whence) {
StringFile *sf = static_cast<StringFile *>(f);
if (whence == SEEK_SET)
sf->pos = offset;
Expand All @@ -119,18 +124,18 @@ TEST(LlvmLibcFileTest, WriteOnly) {
StringFile *f =
new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w");

ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);
EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream
ASSERT_EQ(f->flush(), 0);
EXPECT_EQ(f->get_pos(), sizeof(data)); // Data should now be available
EXPECT_STREQ(f->get_str(), data);

f->reset();
ASSERT_EQ(f->get_pos(), size_t(0));
ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);
EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream
// The second write should trigger a buffer flush.
ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);
EXPECT_GE(f->get_pos(), size_t(0));
ASSERT_EQ(f->flush(), 0);
EXPECT_EQ(f->get_pos(), 2 * sizeof(data));
Expand All @@ -139,11 +144,13 @@ TEST(LlvmLibcFileTest, WriteOnly) {
EXPECT_MEM_EQ(src1, dst1);

char read_data[sizeof(data)];
// This is not a readable file.
EXPECT_EQ(f->read(read_data, sizeof(data)), size_t(0));
EXPECT_TRUE(f->error());
EXPECT_NE(errno, 0);
errno = 0;
{
// This is not a readable file.
auto result = f->read(read_data, sizeof(data));
EXPECT_EQ(result.value, size_t(0));
EXPECT_TRUE(f->error());
EXPECT_TRUE(result.has_error());
}

ASSERT_EQ(f->close(), 0);
}
Expand All @@ -162,8 +169,8 @@ TEST(LlvmLibcFileTest, WriteLineBuffered) {
StringFile *f_full =
new_string_file(file_buffer_full, FILE_BUFFER_SIZE, _IOFBF, false, "w");

ASSERT_EQ(sizeof(data), f_line->write(data, sizeof(data)));
ASSERT_EQ(sizeof(data), f_full->write(data, sizeof(data)));
ASSERT_EQ(sizeof(data), f_line->write(data, sizeof(data)).value);
ASSERT_EQ(sizeof(data), f_full->write(data, sizeof(data)).value);

EXPECT_EQ(f_line->get_pos(), size_t(6)); // buffer after the newline
EXPECT_EQ(f_full->get_pos(), size_t(0)); // buffer all of data
Expand All @@ -175,12 +182,12 @@ TEST(LlvmLibcFileTest, WriteLineBuffered) {

const char data2[] = "longer for an \n overflow";

ASSERT_EQ(sizeof(data2), f_line->write(data2, sizeof(data2)));
ASSERT_EQ(sizeof(data2), f_line->write(data2, sizeof(data2)).value);
// The line buffer's initial contents should be " file\0"
// Writing data2 should write up until the newline, even though that doesn't
// all fit in the buffer.

ASSERT_EQ(sizeof(data2), f_full->write(data2, sizeof(data2)));
ASSERT_EQ(sizeof(data2), f_full->write(data2, sizeof(data2)).value);
// The full buffer's initial contents should be "hello\n file\0"
// Writing data2 should cause a flush of the buffer, as well as the remainder
// to be written directly since it doesn't fit in the buffer.
Expand Down Expand Up @@ -215,7 +222,7 @@ TEST(LlvmLibcFileTest, WriteUnbuffered) {
StringFile *f =
new_string_file(file_buffer, FILE_BUFFER_SIZE, _IONBF, false, "w");

ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);
EXPECT_EQ(f->get_pos(),
sizeof(data)); // no buffering means this is written immediately.
EXPECT_STREQ(f->get_str(), data);
Expand All @@ -233,7 +240,7 @@ TEST(LlvmLibcFileTest, ReadOnly) {

constexpr size_t READ_SIZE = sizeof(initial_content) / 2;
char read_data[READ_SIZE];
ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).value);
EXPECT_FALSE(f->iseof());
// Reading less than file buffer worth will still read one
// full buffer worth of data.
Expand All @@ -246,30 +253,32 @@ TEST(LlvmLibcFileTest, ReadOnly) {

// Reading another buffer worth should read out everything in
// the file.
ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).value);
EXPECT_FALSE(f->iseof());
MemoryView src2(initial_content + READ_SIZE, READ_SIZE),
dst2(read_data, READ_SIZE);
EXPECT_MEM_EQ(src2, dst2);

// Another read should trigger an EOF.
ASSERT_GT(READ_SIZE, f->read(read_data, READ_SIZE));
ASSERT_GT(READ_SIZE, f->read(read_data, READ_SIZE).value);
EXPECT_TRUE(f->iseof());

// Reset the pos to the beginning of the file which should allow
// reading again.
for (size_t i = 0; i < READ_SIZE; ++i)
read_data[i] = 0;
f->seek(0, SEEK_SET);
ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).value);
MemoryView src3(initial_content, READ_SIZE), dst3(read_data, READ_SIZE);
EXPECT_MEM_EQ(src3, dst3);

// This is not a writable file.
EXPECT_EQ(f->write(initial_content, sizeof(initial_content)), size_t(0));
EXPECT_TRUE(f->error());
EXPECT_NE(errno, 0);
errno = 0;
{
// This is not a writable file.
auto result = f->write(initial_content, sizeof(initial_content));
EXPECT_EQ(result.value, size_t(0));
EXPECT_TRUE(f->error());
EXPECT_TRUE(result.has_error());
}

ASSERT_EQ(f->close(), 0);
}
Expand All @@ -285,13 +294,13 @@ TEST(LlvmLibcFileTest, ReadSeekCurAndRead) {
constexpr size_t READ_SIZE = 5;
char data[READ_SIZE];
data[READ_SIZE - 1] = '\0';
ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1);
ASSERT_EQ(f->read(data, READ_SIZE - 1).value, READ_SIZE - 1);
ASSERT_STREQ(data, "1234");
ASSERT_EQ(f->seek(5, SEEK_CUR), 0);
ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1);
ASSERT_EQ(f->seek(5, SEEK_CUR).value(), 0);
ASSERT_EQ(f->read(data, READ_SIZE - 1).value, READ_SIZE - 1);
ASSERT_STREQ(data, "0987");
ASSERT_EQ(f->seek(-5, SEEK_CUR), 0);
ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1);
ASSERT_EQ(f->seek(-5, SEEK_CUR).value(), 0);
ASSERT_EQ(f->read(data, READ_SIZE - 1).value, READ_SIZE - 1);
ASSERT_STREQ(data, "9098");
ASSERT_EQ(f->close(), 0);
}
Expand All @@ -307,14 +316,17 @@ TEST(LlvmLibcFileTest, AppendOnly) {

constexpr size_t READ_SIZE = 5;
char read_data[READ_SIZE];
// This is not a readable file.
ASSERT_EQ(f->read(read_data, READ_SIZE), size_t(0));
EXPECT_TRUE(f->error());
EXPECT_NE(errno, 0);
errno = 0;

{
// This is not a readable file.
auto result = f->read(read_data, READ_SIZE);
EXPECT_EQ(result.value, size_t(0));
EXPECT_TRUE(f->error());
EXPECT_TRUE(result.has_error());
}

// Write should succeed but will be buffered in the file stream.
ASSERT_EQ(f->write(write_data, sizeof(write_data)), sizeof(write_data));
ASSERT_EQ(f->write(write_data, sizeof(write_data)).value, sizeof(write_data));
EXPECT_EQ(f->get_pos(), size_t(0));
// Flushing will write to the file.
EXPECT_EQ(f->flush(), int(0));
Expand All @@ -330,14 +342,14 @@ TEST(LlvmLibcFileTest, WriteUpdate) {
StringFile *f =
new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w+");

ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);
EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream

ASSERT_EQ(f->seek(0, SEEK_SET), 0);
ASSERT_EQ(f->seek(0, SEEK_SET).value(), 0);

// Seek flushes the stream buffer so we can read the previously written data.
char read_data[sizeof(data)];
ASSERT_EQ(f->read(read_data, sizeof(data)), sizeof(data));
ASSERT_EQ(f->read(read_data, sizeof(data)).value, sizeof(data));
EXPECT_STREQ(read_data, data);

ASSERT_EQ(f->close(), 0);
Expand All @@ -353,7 +365,7 @@ TEST(LlvmLibcFileTest, ReadUpdate) {

constexpr size_t READ_SIZE = sizeof(initial_content) / 2;
char read_data[READ_SIZE];
ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).value);
EXPECT_FALSE(f->iseof());
// Reading less than file buffer worth will still read one
// full buffer worth of data.
Expand All @@ -364,9 +376,9 @@ TEST(LlvmLibcFileTest, ReadUpdate) {
MemoryView src1(initial_content, READ_SIZE), dst1(read_data, READ_SIZE);
EXPECT_MEM_EQ(src1, dst1);

ASSERT_EQ(f->seek(0, SEEK_SET), 0);
ASSERT_EQ(f->seek(0, SEEK_SET).value(), 0);
const char write_data[] = "hello, file";
ASSERT_EQ(sizeof(write_data), f->write(write_data, sizeof(write_data)));
ASSERT_EQ(sizeof(write_data), f->write(write_data, sizeof(write_data)).value);
EXPECT_STREQ(file_buffer, write_data);
ASSERT_EQ(f->flush(), 0);
MemoryView dst2(f->get_str(), sizeof(write_data)),
Expand All @@ -385,16 +397,16 @@ TEST(LlvmLibcFileTest, AppendUpdate) {
new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "a+");
f->reset_and_fill(initial_content, sizeof(initial_content));

ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);
EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream
ASSERT_EQ(f->flush(), 0);
// The flush should write |data| to the endof the file.
EXPECT_EQ(f->get_pos(), sizeof(data) + sizeof(initial_content));

ASSERT_EQ(f->seek(0, SEEK_SET), 0);
ASSERT_EQ(f->seek(0, SEEK_SET).value(), 0);
// Seeking to the beginning of the file should not affect the place
// where write happens.
ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);
ASSERT_EQ(f->flush(), 0);
EXPECT_EQ(f->get_pos(), sizeof(data) * 2 + sizeof(initial_content));
MemoryView src1(initial_content, sizeof(initial_content)),
Expand All @@ -408,10 +420,10 @@ TEST(LlvmLibcFileTest, AppendUpdate) {
EXPECT_MEM_EQ(src3, dst3);

// Reads can happen from any point.
ASSERT_EQ(f->seek(0, SEEK_SET), 0);
ASSERT_EQ(f->seek(0, SEEK_SET).value(), 0);
constexpr size_t READ_SIZE = 10;
char read_data[READ_SIZE];
ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).value);
MemoryView src4(initial_content, READ_SIZE), dst4(read_data, READ_SIZE);
EXPECT_MEM_EQ(src4, dst4);

Expand All @@ -426,7 +438,7 @@ TEST(LlvmLibcFileTest, SmallBuffer) {
StringFile *f =
new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w");

ASSERT_EQ(WRITE_SIZE, f->write(WRITE_DATA, WRITE_SIZE));
ASSERT_EQ(WRITE_SIZE, f->write(WRITE_DATA, WRITE_SIZE).value);
// Since data much larger than the buffer is being written, all of it should
// be available in the file without a flush operation.
EXPECT_EQ(f->get_pos(), sizeof(WRITE_DATA));
Expand All @@ -442,9 +454,9 @@ TEST(LlvmLibcFileTest, ZeroLengthBuffer) {
StringFile *f_lbf = new_string_file(nullptr, 0, _IOLBF, true, "w");
StringFile *f_nbf = new_string_file(nullptr, 0, _IONBF, true, "w");

ASSERT_EQ(WRITE_SIZE, f_fbf->write(WRITE_DATA, WRITE_SIZE));
ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE));
ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE));
ASSERT_EQ(WRITE_SIZE, f_fbf->write(WRITE_DATA, WRITE_SIZE).value);
ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE).value);
ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE).value);
// Since there is no buffer space, all of the written data should
// be available in the file without a flush operation.
EXPECT_EQ(f_fbf->get_pos(), sizeof(WRITE_DATA));
Expand Down Expand Up @@ -473,9 +485,9 @@ TEST(LlvmLibcFileTest, WriteNothing) {
StringFile *f_nbf =
new_string_file(file_buffer_nbf, FILE_BUFFER_SIZE, _IONBF, false, "w");

ASSERT_EQ(WRITE_SIZE, f_fbf->write(WRITE_DATA, WRITE_SIZE));
ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE));
ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE));
ASSERT_EQ(WRITE_SIZE, f_fbf->write(WRITE_DATA, WRITE_SIZE).value);
ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE).value);
ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE).value);

ASSERT_FALSE(f_fbf->error_unlocked());
ASSERT_FALSE(f_lbf->error_unlocked());
Expand Down
57 changes: 29 additions & 28 deletions libc/test/src/__support/File/platform_file_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ TEST(LlvmLibcPlatformFileTest, CreateWriteCloseAndReadBack) {
constexpr char FILENAME[] = "testdata/create_write_close_and_readback.test";
File *file = __llvm_libc::openfile(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
ASSERT_EQ(file->write(TEXT, TEXT_SIZE), TEXT_SIZE);
ASSERT_EQ(file->write(TEXT, TEXT_SIZE).value, TEXT_SIZE);
ASSERT_EQ(file->close(), 0);

file = __llvm_libc::openfile(FILENAME, "r");
ASSERT_FALSE(file == nullptr);
char data[sizeof(TEXT)];
ASSERT_EQ(file->read(data, TEXT_SIZE), TEXT_SIZE);
ASSERT_EQ(file->read(data, TEXT_SIZE).value, TEXT_SIZE);
data[TEXT_SIZE] = '\0';
ASSERT_STREQ(data, TEXT);

// Reading more data should trigger EOF.
ASSERT_EQ(file->read(data, TEXT_SIZE), size_t(0));
ASSERT_EQ(file->read(data, TEXT_SIZE).value, size_t(0));
ASSERT_TRUE(file->iseof());

ASSERT_EQ(file->close(), 0);
Expand All @@ -40,17 +40,17 @@ TEST(LlvmLibcPlatformFileTest, CreateWriteSeekAndReadBack) {
constexpr char FILENAME[] = "testdata/create_write_seek_and_readback.test";
File *file = __llvm_libc::openfile(FILENAME, "w+");
ASSERT_FALSE(file == nullptr);
ASSERT_EQ(file->write(TEXT, TEXT_SIZE), TEXT_SIZE);
ASSERT_EQ(file->write(TEXT, TEXT_SIZE).value, TEXT_SIZE);

ASSERT_EQ(file->seek(0, SEEK_SET), 0);
ASSERT_EQ(file->seek(0, SEEK_SET).value(), 0);

char data[sizeof(TEXT)];
ASSERT_EQ(file->read(data, TEXT_SIZE), TEXT_SIZE);
ASSERT_EQ(file->read(data, TEXT_SIZE).value, TEXT_SIZE);
data[TEXT_SIZE] = '\0';
ASSERT_STREQ(data, TEXT);

// Reading more data should trigger EOF.
ASSERT_EQ(file->read(data, TEXT_SIZE), size_t(0));
ASSERT_EQ(file->read(data, TEXT_SIZE).value, size_t(0));
ASSERT_TRUE(file->iseof());

ASSERT_EQ(file->close(), 0);
Expand All @@ -60,26 +60,26 @@ TEST(LlvmLibcPlatformFileTest, CreateAppendCloseAndReadBack) {
constexpr char FILENAME[] = "testdata/create_append_close_and_readback.test";
File *file = __llvm_libc::openfile(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
ASSERT_EQ(file->write(TEXT, TEXT_SIZE), TEXT_SIZE);
ASSERT_EQ(file->write(TEXT, TEXT_SIZE).value, TEXT_SIZE);
ASSERT_EQ(file->close(), 0);

file = __llvm_libc::openfile(FILENAME, "a");
ASSERT_FALSE(file == nullptr);
constexpr char APPEND_TEXT[] = " Append Text";
constexpr size_t APPEND_TEXT_SIZE = sizeof(APPEND_TEXT) - 1;
ASSERT_EQ(file->write(APPEND_TEXT, APPEND_TEXT_SIZE), APPEND_TEXT_SIZE);
ASSERT_EQ(file->write(APPEND_TEXT, APPEND_TEXT_SIZE).value, APPEND_TEXT_SIZE);
ASSERT_EQ(file->close(), 0);

file = __llvm_libc::openfile(FILENAME, "r");
ASSERT_FALSE(file == nullptr);
constexpr size_t READ_SIZE = TEXT_SIZE + APPEND_TEXT_SIZE;
char data[READ_SIZE + 1];
ASSERT_EQ(file->read(data, READ_SIZE), READ_SIZE);
ASSERT_EQ(file->read(data, READ_SIZE).value, READ_SIZE);
data[READ_SIZE] = '\0';
ASSERT_STREQ(data, "Hello, File Append Text");

// Reading more data should trigger EOF.
ASSERT_EQ(file->read(data, READ_SIZE), size_t(0));
ASSERT_EQ(file->read(data, READ_SIZE).value, size_t(0));
ASSERT_TRUE(file->iseof());

ASSERT_EQ(file->close(), 0);
Expand All @@ -89,23 +89,23 @@ TEST(LlvmLibcPlatformFileTest, CreateAppendSeekAndReadBack) {
constexpr char FILENAME[] = "testdata/create_append_seek_and_readback.test";
File *file = __llvm_libc::openfile(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
ASSERT_EQ(file->write(TEXT, TEXT_SIZE), TEXT_SIZE);
ASSERT_EQ(file->write(TEXT, TEXT_SIZE).value, TEXT_SIZE);
ASSERT_EQ(file->close(), 0);

file = __llvm_libc::openfile(FILENAME, "a+");
ASSERT_FALSE(file == nullptr);
constexpr char APPEND_TEXT[] = " Append Text";
constexpr size_t APPEND_TEXT_SIZE = sizeof(APPEND_TEXT) - 1;
ASSERT_EQ(file->write(APPEND_TEXT, APPEND_TEXT_SIZE), APPEND_TEXT_SIZE);
ASSERT_EQ(file->write(APPEND_TEXT, APPEND_TEXT_SIZE).value, APPEND_TEXT_SIZE);

ASSERT_EQ(file->seek(-APPEND_TEXT_SIZE, SEEK_END), 0);
ASSERT_EQ(file->seek(-APPEND_TEXT_SIZE, SEEK_END).value(), 0);
char data[APPEND_TEXT_SIZE + 1];
ASSERT_EQ(file->read(data, APPEND_TEXT_SIZE), APPEND_TEXT_SIZE);
ASSERT_EQ(file->read(data, APPEND_TEXT_SIZE).value, APPEND_TEXT_SIZE);
data[APPEND_TEXT_SIZE] = '\0';
ASSERT_STREQ(data, APPEND_TEXT);

// Reading more data should trigger EOF.
ASSERT_EQ(file->read(data, APPEND_TEXT_SIZE), size_t(0));
ASSERT_EQ(file->read(data, APPEND_TEXT_SIZE).value, size_t(0));
ASSERT_TRUE(file->iseof());

ASSERT_EQ(file->close(), 0);
Expand All @@ -124,21 +124,21 @@ TEST(LlvmLibcPlatformFileTest, LargeFile) {

constexpr int REPEAT = 5;
for (int i = 0; i < REPEAT; ++i) {
ASSERT_EQ(file->write(write_data, DATA_SIZE), DATA_SIZE);
ASSERT_EQ(file->write(write_data, DATA_SIZE).value, DATA_SIZE);
}
ASSERT_EQ(file->close(), 0);

file = __llvm_libc::openfile(FILENAME, "r");
ASSERT_FALSE(file == nullptr);
constexpr size_t READ_SIZE = DATA_SIZE * REPEAT;
char data[READ_SIZE] = {0};
ASSERT_EQ(file->read(data, READ_SIZE), READ_SIZE);
ASSERT_EQ(file->read(data, READ_SIZE).value, READ_SIZE);

for (size_t i = 0; i < READ_SIZE; ++i)
ASSERT_EQ(data[i], BYTE);

// Reading more data should trigger EOF.
ASSERT_EQ(file->read(data, 1), size_t(0));
ASSERT_EQ(file->read(data, 1).value, size_t(0));
ASSERT_TRUE(file->iseof());

ASSERT_EQ(file->close(), 0);
Expand All @@ -149,7 +149,8 @@ TEST(LlvmLibcPlatformFileTest, ReadSeekCurAndRead) {
File *file = __llvm_libc::openfile(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
constexpr char CONTENT[] = "1234567890987654321";
ASSERT_EQ(sizeof(CONTENT) - 1, file->write(CONTENT, sizeof(CONTENT) - 1));
ASSERT_EQ(sizeof(CONTENT) - 1,
file->write(CONTENT, sizeof(CONTENT) - 1).value);
ASSERT_EQ(0, file->close());

file = __llvm_libc::openfile(FILENAME, "r");
Expand All @@ -158,13 +159,13 @@ TEST(LlvmLibcPlatformFileTest, ReadSeekCurAndRead) {
constexpr size_t READ_SIZE = 5;
char data[READ_SIZE];
data[READ_SIZE - 1] = '\0';
ASSERT_EQ(file->read(data, READ_SIZE - 1), READ_SIZE - 1);
ASSERT_EQ(file->read(data, READ_SIZE - 1).value, READ_SIZE - 1);
ASSERT_STREQ(data, "1234");
ASSERT_EQ(file->seek(5, SEEK_CUR), 0);
ASSERT_EQ(file->read(data, READ_SIZE - 1), READ_SIZE - 1);
ASSERT_EQ(file->seek(5, SEEK_CUR).value(), 0);
ASSERT_EQ(file->read(data, READ_SIZE - 1).value, READ_SIZE - 1);
ASSERT_STREQ(data, "0987");
ASSERT_EQ(file->seek(-5, SEEK_CUR), 0);
ASSERT_EQ(file->read(data, READ_SIZE - 1), READ_SIZE - 1);
ASSERT_EQ(file->seek(-5, SEEK_CUR).value(), 0);
ASSERT_EQ(file->read(data, READ_SIZE - 1).value, READ_SIZE - 1);
ASSERT_STREQ(data, "9098");

ASSERT_EQ(file->close(), 0);
Expand All @@ -176,21 +177,21 @@ TEST(LlvmLibcPlatformFileTest, IncorrectOperation) {

File *file = __llvm_libc::openfile(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
ASSERT_EQ(file->read(data, 1), size_t(0)); // Cannot read
ASSERT_EQ(file->read(data, 1).value, size_t(0)); // Cannot read
ASSERT_FALSE(file->iseof());
ASSERT_TRUE(file->error());
ASSERT_EQ(file->close(), 0);

file = __llvm_libc::openfile(FILENAME, "r");
ASSERT_FALSE(file == nullptr);
ASSERT_EQ(file->write(data, 1), size_t(0)); // Cannot write
ASSERT_EQ(file->write(data, 1).value, size_t(0)); // Cannot write
ASSERT_FALSE(file->iseof());
ASSERT_TRUE(file->error());
ASSERT_EQ(file->close(), 0);

file = __llvm_libc::openfile(FILENAME, "a");
ASSERT_FALSE(file == nullptr);
ASSERT_EQ(file->read(data, 1), size_t(0)); // Cannot read
ASSERT_EQ(file->read(data, 1).value, size_t(0)); // Cannot read
ASSERT_FALSE(file->iseof());
ASSERT_TRUE(file->error());
ASSERT_EQ(file->close(), 0);
Expand Down
27 changes: 27 additions & 0 deletions libc/test/src/stdio/fileop_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ TEST(LlvmLibcFILETest, SimpleFileOperations) {
__llvm_libc::clearerr(file);
ASSERT_EQ(__llvm_libc::ferror(file), 0);

errno = 0;
ASSERT_EQ(__llvm_libc::fwrite("nothing", 1, 1, file), size_t(0));
ASSERT_NE(errno, 0);
errno = 0;

ASSERT_EQ(__llvm_libc::fclose(file), 0);

// Now try puts.
Expand All @@ -90,6 +95,12 @@ TEST(LlvmLibcFILETest, SimpleFileOperations) {
__llvm_libc::clearerr(file);
ASSERT_EQ(__llvm_libc::ferror(file), 0);

// This is not a readable file.
errno = 0;
ASSERT_EQ(__llvm_libc::fread(data, 1, 1, file), size_t(0));
ASSERT_NE(errno, 0);
errno = 0;

ASSERT_EQ(0, __llvm_libc::fclose(file));

file = __llvm_libc::fopen(FILENAME, "r");
Expand All @@ -100,6 +111,21 @@ TEST(LlvmLibcFILETest, SimpleFileOperations) {
read_data[sizeof(CONTENT) - 1] = '\0';
ASSERT_STREQ(read_data, CONTENT);
ASSERT_EQ(__llvm_libc::fclose(file), 0);

// Check that the other functions correctly set errno.

errno = 0;
ASSERT_NE(__llvm_libc::fseek(file, 0, SEEK_SET), 0);
EXPECT_NE(errno, 0);

errno = 0;
ASSERT_NE(__llvm_libc::fclose(file), 0);
EXPECT_NE(errno, 0);

errno = 0;
ASSERT_EQ(__llvm_libc::fopen("INVALID FILE NAME", "r"),
static_cast<FILE *>(nullptr));
EXPECT_NE(errno, 0);
}

TEST(LlvmLibcFILETest, FFlush) {
Expand Down Expand Up @@ -132,6 +158,7 @@ TEST(LlvmLibcFILETest, FOpenFWriteSizeGreaterThanOne) {
constexpr size_t WRITE_NMEMB = sizeof(WRITE_DATA) / sizeof(MyStruct);
constexpr char FILENAME[] = "testdata/fread_fwrite.test";

errno = 0;
FILE *file = __llvm_libc::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
ASSERT_EQ(size_t(0), __llvm_libc::fwrite(WRITE_DATA, 0, 1, file));
Expand Down
5 changes: 5 additions & 0 deletions libc/test/src/stdio/ftell_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "src/stdio/setvbuf.h"
#include "utils/UnitTest/Test.h"

#include <errno.h>
#include <stdio.h>

class LlvmLibcFTellTest : public __llvm_libc::testing::Test {
Expand Down Expand Up @@ -53,6 +54,10 @@ class LlvmLibcFTellTest : public __llvm_libc::testing::Test {
ASSERT_EQ(size_t(__llvm_libc::ftell(file)), READ_SIZE);

ASSERT_EQ(0, __llvm_libc::fclose(file));

errno = 0;
ASSERT_EQ(__llvm_libc::ftell(file), long(-1));
ASSERT_NE(errno, 0);
}
};

Expand Down