diff --git a/libc/src/stdio/scanf_core/CMakeLists.txt b/libc/src/stdio/scanf_core/CMakeLists.txt index 3941d40a838c7..91cf5e2ada907 100644 --- a/libc/src/stdio/scanf_core/CMakeLists.txt +++ b/libc/src/stdio/scanf_core/CMakeLists.txt @@ -23,3 +23,38 @@ add_object_library( libc.src.__support.CPP.bitset 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_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.cpp b/libc/src/stdio/scanf_core/file_reader.cpp new file mode 100644 index 0000000000000..f39c3b9ab8412 --- /dev/null +++ b/libc/src/stdio/scanf_core/file_reader.cpp @@ -0,0 +1,26 @@ +//===-- 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(char c) { file->ungetc_unlocked(c); } + +} // namespace scanf_core +} // namespace __llvm_libc diff --git a/libc/src/stdio/scanf_core/file_reader.h b/libc/src/stdio/scanf_core/file_reader.h new file mode 100644 index 0000000000000..5e97eb604e66b --- /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(char c); +}; + +} // namespace scanf_core +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_SCANF_CORE_FILE_READER_H diff --git a/libc/src/stdio/scanf_core/reader.cpp b/libc/src/stdio/scanf_core/reader.cpp new file mode 100644 index 0000000000000..23dcbd405505d --- /dev/null +++ b/libc/src/stdio/scanf_core/reader.cpp @@ -0,0 +1,35 @@ +//===-- 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(char c) { + if (reader_type == ReaderType::String) { + // The string reader ignores the char c passed to unget since it doesn't + // need to place anything back into a buffer, and modifying the source + // string would be dangerous. + return string_reader->unget_char(); + } else { + return file_reader->unget_char(c); + } +} + +} // 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 index 0000000000000..4d6ed06c00e7c --- /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. When it hits the end of the string or file it returns '\0' to + // signal to stop parsing. + char getc(); + + // 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); +}; + +} // namespace scanf_core +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_SCANF_CORE_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 index 0000000000000..1d728d2b9eb35 --- /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/src/stdio/scanf_core/string_reader.h b/libc/src/stdio/scanf_core/string_reader.h new file mode 100644 index 0000000000000..35550b16c3214 --- /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/test/src/stdio/scanf_core/CMakeLists.txt b/libc/test/src/stdio/scanf_core/CMakeLists.txt index 3235a0e53e010..fa4878ae5b15f 100644 --- a/libc/test/src/stdio/scanf_core/CMakeLists.txt +++ b/libc/test/src/stdio/scanf_core/CMakeLists.txt @@ -12,3 +12,21 @@ add_libc_unittest( libc.src.__support.CPP.string_view 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 + 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 index 0000000000000..43e65cc1bab6e --- /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(str[4]); + reader.ungetc(str[3]); + reader.ungetc(str[2]); + + 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(str[6]); + reader.ungetc(str[5]); + + 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(str[9 - i]); + } + + // Check the whole string. + for (size_t i = 0; i < sizeof(str); ++i) { + ASSERT_EQ(str[i], reader.getc()); + } +}