49 changes: 33 additions & 16 deletions libc/src/stdio/scanf_core/reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,61 @@
#ifndef LLVM_LIBC_SRC_STDIO_SCANF_CORE_READER_H
#define LLVM_LIBC_SRC_STDIO_SCANF_CORE_READER_H

#include "src/stdio/scanf_core/file_reader.h"
#include "src/stdio/scanf_core/string_reader.h"
#include "src/__support/macros/attributes.h" // For LIBC_INLINE
#include <stddef.h>

namespace __llvm_libc {
namespace scanf_core {

enum class ReaderType { String, File };
using StreamGetc = int (*)(void *);
using StreamUngetc = void (*)(int, void *);

class Reader final {
union {
StringReader *string_reader;
FileReader *file_reader;
};
// This is intended to be either a raw string or a buffer syncronized with the
// file's internal buffer.
struct ReadBuffer {
char *buffer;
size_t buff_len;
size_t buff_cur = 0;
};

class Reader {
ReadBuffer *rb;

const ReaderType reader_type;
void *input_stream = nullptr;

StreamGetc stream_getc = nullptr;
StreamUngetc stream_ungetc = nullptr;

size_t cur_chars_read = 0;

public:
Reader(StringReader *init_string_reader)
: string_reader(init_string_reader), reader_type(ReaderType::String) {}
// TODO: Set buff_len with a proper constant
Reader(ReadBuffer *string_buffer) : rb(string_buffer) {}

Reader(FileReader *init_file_reader)
: file_reader(init_file_reader), reader_type(ReaderType::File) {}
Reader(void *stream, StreamGetc stream_getc_in, StreamUngetc stream_ungetc_in,
ReadBuffer *stream_buffer = nullptr)
: rb(stream_buffer), input_stream(stream), stream_getc(stream_getc_in),
stream_ungetc(stream_ungetc_in) {}

// This returns the next character from the input and advances it by one
// character. When it hits the end of the string or file it returns '\0' to
// signal to stop parsing.
char getc();
LIBC_INLINE char getc() {
++cur_chars_read;
if (rb != nullptr) {
char output = rb->buffer[rb->buff_cur];
++(rb->buff_cur);
return output;
}
// This should reset the buffer if applicable.
return static_cast<char>(stream_getc(input_stream));
}

// This moves the input back by one character, placing c into the buffer if
// this is a file reader, else c is ignored.
void ungetc(char c);

size_t chars_read() { return cur_chars_read; }

bool has_error();
};

} // namespace scanf_core
Expand Down
5 changes: 0 additions & 5 deletions libc/src/stdio/scanf_core/scanf_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,6 @@ int scanf_main(Reader *reader, const char *__restrict str,
}
}

if (conversions == 0 && reader->has_error()) {
// This is intended to be converted to EOF in the client call to avoid
// including stdio.h in this internal file.
return -1;
}
return conversions;
}

Expand Down
24 changes: 0 additions & 24 deletions libc/src/stdio/scanf_core/string_reader.cpp

This file was deleted.

33 changes: 0 additions & 33 deletions libc/src/stdio/scanf_core/string_reader.h

This file was deleted.

29 changes: 0 additions & 29 deletions libc/src/stdio/scanf_core/vfscanf_internal.cpp

This file was deleted.

52 changes: 50 additions & 2 deletions libc/src/stdio/scanf_core/vfscanf_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,63 @@
#ifndef LLVM_LIBC_SRC_STDIO_SCANF_CORE_VFSCANF_INTERNAL_H
#define LLVM_LIBC_SRC_STDIO_SCANF_CORE_VFSCANF_INTERNAL_H

#include "src/__support/File/file.h"
#include "src/__support/arg_list.h"
#include "src/stdio/scanf_core/reader.h"
#include "src/stdio/scanf_core/scanf_main.h"

#include <stdio.h>

namespace __llvm_libc {

namespace internal {

#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE

LIBC_INLINE int getc(void *f) {
unsigned char c;
auto result = reinterpret_cast<__llvm_libc::File *>(f)->read_unlocked(&c, 1);
size_t r = result.value;
if (result.has_error() || r != 1)
return '\0';

return c;
}

LIBC_INLINE void ungetc(int c, void *f) {
reinterpret_cast<__llvm_libc::File *>(f)->ungetc(c);
}

LIBC_INLINE int ferror_unlocked(FILE *f) {
return reinterpret_cast<__llvm_libc::File *>(f)->error_unlocked();
}

#else // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)

LIBC_INLINE int getc(void *f) { return ::getc(reinterpret_cast<::FILE *>(f)); }

LIBC_INLINE void ungetc(int c, void *f) {
::ungetc(c, reinterpret_cast<::FILE *>(f));
}

LIBC_INLINE int ferror_unlocked(::FILE *f) { return ::ferror_unlocked(f); }

#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE

} // namespace internal

namespace scanf_core {

int vfscanf_internal(::FILE *__restrict stream, const char *__restrict format,
internal::ArgList &args);
LIBC_INLINE int vfscanf_internal(::FILE *__restrict stream,
const char *__restrict format,
internal::ArgList &args) {
scanf_core::Reader reader(stream, &internal::getc, internal::ungetc);
int retval = scanf_core::scanf_main(&reader, format, args);
if (retval == 0 && internal::ferror_unlocked(stream))
return EOF;

return retval;
}
} // namespace scanf_core
} // namespace __llvm_libc

Expand Down
7 changes: 4 additions & 3 deletions libc/src/stdio/sscanf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

#include "src/stdio/sscanf.h"

#include "src/__support/CPP/limits.h"
#include "src/__support/arg_list.h"
#include "src/stdio/scanf_core/reader.h"
#include "src/stdio/scanf_core/scanf_main.h"
#include "src/stdio/scanf_core/string_reader.h"

#include <stdarg.h>
#include <stdio.h>
Expand All @@ -27,8 +27,9 @@ LLVM_LIBC_FUNCTION(int, sscanf,
// and pointer semantics, as well as handling
// destruction automatically.
va_end(vlist);
scanf_core::StringReader string_reader(buffer);
scanf_core::Reader reader(&string_reader);
scanf_core::ReadBuffer rb{const_cast<char *>(buffer),
cpp::numeric_limits<size_t>::max()};
scanf_core::Reader reader(&rb);
int ret_val = scanf_core::scanf_main(&reader, format, args);
// This is done to avoid including stdio.h in the internals. On most systems
// EOF is -1, so this will be transformed into just "return ret_val".
Expand Down
6 changes: 3 additions & 3 deletions libc/src/stdio/vprintf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
#include <stdarg.h>
#include <stdio.h>

#ifndef LIBC_COPT_PRINTF_USE_SYSTEM_FILE
#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
#define PRINTF_STDOUT __llvm_libc::stdout
#else // LIBC_COPT_PRINTF_USE_SYSTEM_FILE
#else // LIBC_COPT_STDIO_USE_SYSTEM_FILE
#define PRINTF_STDOUT ::stdout
#endif // LIBC_COPT_PRINTF_USE_SYSTEM_FILE
#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE

namespace __llvm_libc {

Expand Down
24 changes: 17 additions & 7 deletions libc/test/src/stdio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ if(LLVM_LIBC_FULL_BUILD)
set(hermetic_test_only HERMETIC_TEST_ONLY)
else()
# Else in overlay mode they use the system's FILE.
set(fprintf_test_copts "-DLIBC_COPT_PRINTF_USE_SYSTEM_FILE")
set(use_system_file "-DLIBC_COPT_STDIO_USE_SYSTEM_FILE")
endif()

add_libc_unittest(
Expand All @@ -173,7 +173,7 @@ add_libc_unittest(
libc.src.stdio.fprintf
${fprintf_test_deps}
COMPILE_OPTIONS
${fprintf_test_copts}
${use_system_file}
)

add_libc_test(
Expand Down Expand Up @@ -218,7 +218,7 @@ add_libc_unittest(
libc.src.stdio.vfprintf
${fprintf_test_deps}
COMPILE_OPTIONS
${fprintf_test_copts}
${use_system_file}
)

add_libc_test(
Expand All @@ -232,6 +232,17 @@ add_libc_test(
libc.src.stdio.vprintf
)


if(LLVM_LIBC_FULL_BUILD)
# In fullbuild mode, fscanf's tests use the internal FILE for other functions.
list(APPEND fscanf_test_deps
libc.src.stdio.fclose
libc.src.stdio.ferror
libc.src.stdio.fopen
libc.src.stdio.fwrite
)
endif()

add_libc_unittest(
fscanf_test
SUITE
Expand All @@ -240,11 +251,10 @@ add_libc_unittest(
fscanf_test.cpp
DEPENDS
libc.src.stdio.fscanf
libc.src.stdio.fclose
libc.src.stdio.ferror
libc.src.stdio.fopen
libc.src.stdio.fwrite
${fscanf_test_deps}
libc.src.__support.CPP.string_view
COMPILE_OPTIONS
${use_system_file}
)

add_libc_unittest(
Expand Down
10 changes: 5 additions & 5 deletions libc/test/src/stdio/fprintf_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
//
//===----------------------------------------------------------------------===//

#ifndef LIBC_COPT_PRINTF_USE_SYSTEM_FILE
#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
#include "src/stdio/fclose.h"
#include "src/stdio/ferror.h"
#include "src/stdio/fopen.h"
#include "src/stdio/fread.h"
#endif // LIBC_COPT_PRINTF_USE_SYSTEM_FILE
#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE

#include "src/stdio/fprintf.h"

Expand All @@ -20,17 +20,17 @@
#include <stdio.h>

namespace printf_test {
#ifndef LIBC_COPT_PRINTF_USE_SYSTEM_FILE
#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
using __llvm_libc::fclose;
using __llvm_libc::ferror;
using __llvm_libc::fopen;
using __llvm_libc::fread;
#else // defined(LIBC_COPT_PRINTF_USE_SYSTEM_FILE)
#else // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
using ::fclose;
using ::ferror;
using ::fopen;
using ::fread;
#endif // LIBC_COPT_PRINTF_USE_SYSTEM_FILE
#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
} // namespace printf_test

TEST(LlvmLibcFPrintfTest, WriteToFile) {
Expand Down
38 changes: 28 additions & 10 deletions libc/test/src/stdio/fscanf_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,65 @@
//===----------------------------------------------------------------------===//

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

#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
#include "src/stdio/fclose.h"
#include "src/stdio/ferror.h"
#include "src/stdio/fopen.h"
#include "src/stdio/fwrite.h"
#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE

#include "src/stdio/fscanf.h"

#include "test/UnitTest/Test.h"

#include <stdio.h>

namespace scanf_test {
#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
using __llvm_libc::fclose;
using __llvm_libc::ferror;
using __llvm_libc::fopen;
using __llvm_libc::fwrite;
#else // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
using ::fclose;
using ::ferror;
using ::fopen;
using ::fwrite;
#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
} // namespace scanf_test

TEST(LlvmLibcFScanfTest, WriteToFile) {
constexpr char FILENAME[] = "testdata/fscanf_output.test";
::FILE *file = __llvm_libc::fopen(FILENAME, "w");
const char *FILENAME = "fscanf_output.test";
auto FILE_PATH = libc_make_test_file_path(FILENAME);
::FILE *file = scanf_test::fopen(FILE_PATH, "w");
ASSERT_FALSE(file == nullptr);

int read;

constexpr char simple[] = "A simple string with no conversions.\n";

ASSERT_EQ(sizeof(simple) - 1,
__llvm_libc::fwrite(simple, 1, sizeof(simple) - 1, file));
scanf_test::fwrite(simple, 1, sizeof(simple) - 1, file));

constexpr char numbers[] = "1234567890\n";

ASSERT_EQ(sizeof(numbers) - 1,
__llvm_libc::fwrite(numbers, 1, sizeof(numbers) - 1, file));
scanf_test::fwrite(numbers, 1, sizeof(numbers) - 1, file));

constexpr char numbers_and_more[] = "1234 and more\n";

ASSERT_EQ(sizeof(numbers_and_more) - 1,
__llvm_libc::fwrite(numbers_and_more, 1,
sizeof(numbers_and_more) - 1, file));
scanf_test::fwrite(numbers_and_more, 1,
sizeof(numbers_and_more) - 1, file));

read =
__llvm_libc::fscanf(file, "Reading from a write-only file should fail.");
EXPECT_LT(read, 0);

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

file = __llvm_libc::fopen(FILENAME, "r");
file = scanf_test::fopen(FILE_PATH, "r");
ASSERT_FALSE(file == nullptr);

char data[50];
Expand All @@ -67,6 +85,6 @@ TEST(LlvmLibcFScanfTest, WriteToFile) {
ASSERT_EQ(__llvm_libc::cpp::string_view(numbers_and_more),
__llvm_libc::cpp::string_view(data, sizeof(numbers_and_more) - 1));

ASSERT_EQ(__llvm_libc::ferror(file), 0);
ASSERT_EQ(__llvm_libc::fclose(file), 0);
ASSERT_EQ(scanf_test::ferror(file), 0);
ASSERT_EQ(scanf_test::fclose(file), 0);
}
17 changes: 8 additions & 9 deletions libc/test/src/stdio/scanf_core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,23 @@ add_libc_unittest(
libc.src.__support.arg_list
)

if(NOT (TARGET libc.src.__support.File.file))
# Not all platforms have a file implementation. If file is unvailable,
# then we must skip all the parts that need file.
return()
endif()

add_libc_unittest(
string_reader_test
reader_test
SUITE
libc_stdio_unittests
SRCS
string_reader_test.cpp
reader_test.cpp
DEPENDS
libc.src.stdio.scanf_core.reader
libc.src.stdio.scanf_core.string_reader
libc.src.__support.CPP.string_view
)

if(NOT (TARGET libc.src.__support.File.file))
# Not all platforms have a file implementation. If file is unvailable,
# then we must skip all the parts that need file.
return()
endif()

add_libc_unittest(
converter_test
SUITE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@

#include "src/__support/CPP/string_view.h"
#include "src/stdio/scanf_core/reader.h"
#include "src/stdio/scanf_core/string_reader.h"

#include "test/UnitTest/Test.h"

TEST(LlvmLibcScanfStringReaderTest, Constructor) {
char str[10];
__llvm_libc::scanf_core::StringReader str_reader(str);
__llvm_libc::scanf_core::Reader reader(&str_reader);
// buff_len justneeds to be a big number. The specific value isn't important
// in the real world.
__llvm_libc::scanf_core::ReadBuffer rb{const_cast<char *>(str), 1000000};
__llvm_libc::scanf_core::Reader reader(&rb);
}

TEST(LlvmLibcScanfStringReaderTest, SimpleRead) {
const char *str = "abc";
__llvm_libc::scanf_core::StringReader str_reader(str);
__llvm_libc::scanf_core::Reader reader(&str_reader);
__llvm_libc::scanf_core::ReadBuffer rb{const_cast<char *>(str), 1000000};
__llvm_libc::scanf_core::Reader reader(&rb);

for (size_t i = 0; i < sizeof("abc"); ++i) {
ASSERT_EQ(str[i], reader.getc());
Expand All @@ -30,8 +31,8 @@ TEST(LlvmLibcScanfStringReaderTest, SimpleRead) {

TEST(LlvmLibcScanfStringReaderTest, ReadAndReverse) {
const char *str = "abcDEF123";
__llvm_libc::scanf_core::StringReader str_reader(str);
__llvm_libc::scanf_core::Reader reader(&str_reader);
__llvm_libc::scanf_core::ReadBuffer rb{const_cast<char *>(str), 1000000};
__llvm_libc::scanf_core::Reader reader(&rb);

for (size_t i = 0; i < 5; ++i) {
ASSERT_EQ(str[i], reader.getc());
Expand Down
10 changes: 5 additions & 5 deletions libc/test/src/stdio/vfprintf_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
// because these functions are identical in every way except for how the varargs
// are passed.

#ifndef LIBC_COPT_PRINTF_USE_SYSTEM_FILE
#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
#include "src/stdio/fclose.h"
#include "src/stdio/ferror.h"
#include "src/stdio/fopen.h"
#include "src/stdio/fread.h"
#endif // LIBC_COPT_PRINTF_USE_SYSTEM_FILE
#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE

#include "src/stdio/vfprintf.h"

Expand All @@ -24,17 +24,17 @@
#include <stdio.h>

namespace printf_test {
#ifndef LIBC_COPT_PRINTF_USE_SYSTEM_FILE
#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
using __llvm_libc::fclose;
using __llvm_libc::ferror;
using __llvm_libc::fopen;
using __llvm_libc::fread;
#else // defined(LIBC_COPT_PRINTF_USE_SYSTEM_FILE)
#else // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
using ::fclose;
using ::ferror;
using ::fopen;
using ::fread;
#endif // LIBC_COPT_PRINTF_USE_SYSTEM_FILE
#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
} // namespace printf_test

int call_vfprintf(::FILE *__restrict stream, const char *__restrict format,
Expand Down
2 changes: 1 addition & 1 deletion utils/bazel/llvm-project-overlay/libc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ package(
licenses(["notice"])

PRINTF_COPTS = [
"LIBC_COPT_PRINTF_USE_SYSTEM_FILE",
"LIBC_COPT_STDIO_USE_SYSTEM_FILE",
"LIBC_COPT_PRINTF_DISABLE_INDEX_MODE",
"LIBC_COPT_PRINTF_DISABLE_WRITE_INT",
]
Expand Down