diff --git a/libc/src/stdio/scanf_core/CMakeLists.txt b/libc/src/stdio/scanf_core/CMakeLists.txt --- a/libc/src/stdio/scanf_core/CMakeLists.txt +++ b/libc/src/stdio/scanf_core/CMakeLists.txt @@ -23,3 +23,32 @@ libc.src.__support.CPP.bitset libc.src.__support.CPP.string_view ) + +add_object_library( + string_reader + SRCS + string_reader.cpp + HDRS + string_reader.h +) + +add_object_library( + file_reader + SRCS + file_reader.cpp + HDRS + file_reader.h + DEPENDS + libc.src.__support.File.file +) + +add_object_library( + reader + SRCS + reader.cpp + HDRS + reader.h + DEPENDS + .string_reader + .file_reader +) diff --git a/libc/src/stdio/scanf_core/file_reader.h b/libc/src/stdio/scanf_core/file_reader.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/scanf_core/file_reader.h @@ -0,0 +1,38 @@ +//===-- FILE Reader definition for scanf ------------------------*- 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_SRC_STDIO_SCANF_CORE_FILE_READER_H +#define LLVM_LIBC_SRC_STDIO_SCANF_CORE_FILE_READER_H + +#include "src/__support/File/file.h" + +#include +#include + +namespace __llvm_libc { +namespace scanf_core { + +class FileReader { + __llvm_libc::File *file; + +public: + FileReader(::FILE *init_file) { + file = reinterpret_cast<__llvm_libc::File *>(init_file); + file->lock(); + } + + ~FileReader() { file->unlock(); } + + char get_char(); + void unget_char(); +}; + +} // namespace scanf_core +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_SCANF_CORE_FILE_READER_H diff --git a/libc/src/stdio/scanf_core/file_reader.cpp b/libc/src/stdio/scanf_core/file_reader.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/scanf_core/file_reader.cpp @@ -0,0 +1,28 @@ +//===-- FILE Reader implementation for scanf --------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/scanf_core/file_reader.h" +#include "src/__support/File/file.h" +#include + +namespace __llvm_libc { +namespace scanf_core { + +char FileReader::get_char() { + char tiny_buff = 0; + if (file->read_unlocked(&tiny_buff, 1) != 1) + return 0; + return tiny_buff; +} + +void FileReader::unget_char() { + // file->ungetc_unlocked(); +} + +} // namespace scanf_core +} // namespace __llvm_libc diff --git a/libc/src/stdio/scanf_core/reader.h b/libc/src/stdio/scanf_core/reader.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/scanf_core/reader.h @@ -0,0 +1,49 @@ +//===-- Reader definition for scanf -----------------------------*- 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_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 + +namespace __llvm_libc { +namespace scanf_core { + +enum class ReaderType { String, File }; + +class Reader final { + union { + StringReader *string_reader; + FileReader *file_reader; + }; + + const ReaderType reader_type; + +public: + Reader(StringReader *init_string_reader) + : string_reader(init_string_reader), reader_type(ReaderType::String) {} + + Reader(FileReader *init_file_reader) + : file_reader(init_file_reader), reader_type(ReaderType::File) {} + + // This returns the next character from the input and advances it by one + // character. If the file reaches EOF this function will return '\0'. + char getc(); + + // This moves the input back by one character. If getc returned a non-zero + // value, then calling ungetc and then getc again should return the same + // value. + void ungetc(); +}; + +} // namespace scanf_core +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_SCANF_CORE_READER_H diff --git a/libc/src/stdio/scanf_core/reader.cpp b/libc/src/stdio/scanf_core/reader.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/scanf_core/reader.cpp @@ -0,0 +1,32 @@ +//===-- Reader definition for scanf -----------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/scanf_core/reader.h" +#include + +namespace __llvm_libc { +namespace scanf_core { + +char Reader::getc() { + if (reader_type == ReaderType::String) { + return string_reader->get_char(); + } else { + return file_reader->get_char(); + } +} + +void Reader::ungetc() { + if (reader_type == ReaderType::String) { + return string_reader->unget_char(); + } else { + return file_reader->unget_char(); + } +} + +} // namespace scanf_core +} // namespace __llvm_libc diff --git a/libc/src/stdio/scanf_core/string_reader.h b/libc/src/stdio/scanf_core/string_reader.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/scanf_core/string_reader.h @@ -0,0 +1,33 @@ +//===-- String Reader definition for scanf ----------------------*- 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_SRC_STDIO_SCANF_CORE_STRING_READER_H +#define LLVM_LIBC_SRC_STDIO_SCANF_CORE_STRING_READER_H + +#include + +namespace __llvm_libc { +namespace scanf_core { + +class StringReader { + const char *string; + size_t cur_index = 0; + +public: + StringReader(const char *init_string) { string = init_string; } + + ~StringReader() {} + + char get_char(); + void unget_char(); +}; + +} // namespace scanf_core +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_SCANF_CORE_STRING_READER_H diff --git a/libc/src/stdio/scanf_core/string_reader.cpp b/libc/src/stdio/scanf_core/string_reader.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/scanf_core/string_reader.cpp @@ -0,0 +1,24 @@ +//===-- String Reader implementation for scanf ------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/scanf_core/string_reader.h" +#include + +namespace __llvm_libc { +namespace scanf_core { + +char StringReader::get_char() { + char cur_char = string[cur_index]; + ++cur_index; + return cur_char; +} + +void StringReader::unget_char() { --cur_index; } + +} // namespace scanf_core +} // namespace __llvm_libc diff --git a/libc/test/src/stdio/scanf_core/CMakeLists.txt b/libc/test/src/stdio/scanf_core/CMakeLists.txt --- a/libc/test/src/stdio/scanf_core/CMakeLists.txt +++ b/libc/test/src/stdio/scanf_core/CMakeLists.txt @@ -12,3 +12,15 @@ libc.src.__support.CPP.string_view libc.src.__support.arg_list ) + +add_libc_unittest( + string_reader_test + SUITE + libc_stdio_unittests + SRCS + string_reader_test.cpp + DEPENDS + libc.src.stdio.scanf_core.reader + libc.src.stdio.scanf_core.string_reader + libc.src.__support.CPP.string_view +) diff --git a/libc/test/src/stdio/scanf_core/string_reader_test.cpp b/libc/test/src/stdio/scanf_core/string_reader_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/scanf_core/string_reader_test.cpp @@ -0,0 +1,66 @@ +//===-- Unittests for the scanf String Reader -----------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/string_view.h" +#include "src/stdio/scanf_core/reader.h" +#include "src/stdio/scanf_core/string_reader.h" + +#include "utils/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); +} + +TEST(LlvmLibcScanfStringReaderTest, SimpleRead) { + const char *str = "abc"; + __llvm_libc::scanf_core::StringReader str_reader(str); + __llvm_libc::scanf_core::Reader reader(&str_reader); + + for (size_t i = 0; i < sizeof(str); ++i) { + ASSERT_EQ(str[i], reader.getc()); + } +} + +TEST(LlvmLibcScanfStringReaderTest, ReadAndReverse) { + const char *str = "abcDEF123"; + __llvm_libc::scanf_core::StringReader str_reader(str); + __llvm_libc::scanf_core::Reader reader(&str_reader); + + for (size_t i = 0; i < 5; ++i) { + ASSERT_EQ(str[i], reader.getc()); + } + + // Move back by 3, cursor should now be on 2 + reader.ungetc(); + reader.ungetc(); + reader.ungetc(); + + for (size_t i = 2; i < 7; ++i) { + ASSERT_EQ(str[i], reader.getc()); + } + + // Move back by 2, cursor should now be on 5 + reader.ungetc(); + reader.ungetc(); + + for (size_t i = 5; i < 10; ++i) { + ASSERT_EQ(str[i], reader.getc()); + } + + // Move back by 10, which should be back to the start. + for (size_t i = 0; i < 10; ++i) { + reader.ungetc(); + } + + // Check the whole string. + for (size_t i = 0; i < sizeof(str); ++i) { + ASSERT_EQ(str[i], reader.getc()); + } +}