[libc] add scanf reader
authorMichael Jones <michaelrj@google.com>
Wed, 19 Oct 2022 20:33:05 +0000 (13:33 -0700)
committerMichael Jones <michaelrj@google.com>
Mon, 7 Nov 2022 21:48:58 +0000 (13:48 -0800)
This is the interface that will be used to read from a file or string in
scanf. This patch also adds the string and file implementations of the
reader, although the file reader is not yet complete since ungetc has
not yet been implemented.

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D137117

libc/src/stdio/scanf_core/CMakeLists.txt
libc/src/stdio/scanf_core/file_reader.cpp [new file with mode: 0644]
libc/src/stdio/scanf_core/file_reader.h [new file with mode: 0644]
libc/src/stdio/scanf_core/reader.cpp [new file with mode: 0644]
libc/src/stdio/scanf_core/reader.h [new file with mode: 0644]
libc/src/stdio/scanf_core/string_reader.cpp [new file with mode: 0644]
libc/src/stdio/scanf_core/string_reader.h [new file with mode: 0644]
libc/test/src/stdio/scanf_core/CMakeLists.txt
libc/test/src/stdio/scanf_core/string_reader_test.cpp [new file with mode: 0644]

index 3941d40..91cf5e2 100644 (file)
@@ -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 (file)
index 0000000..f39c3b9
--- /dev/null
@@ -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 <stddef.h>
+
+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 (file)
index 0000000..5e97eb6
--- /dev/null
@@ -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 <stddef.h>
+#include <stdio.h>
+
+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 (file)
index 0000000..23dcbd4
--- /dev/null
@@ -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 <stddef.h>
+
+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 (file)
index 0000000..4d6ed06
--- /dev/null
@@ -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 <stddef.h>
+
+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 (file)
index 0000000..1d728d2
--- /dev/null
@@ -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 <stddef.h>
+
+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 (file)
index 0000000..35550b1
--- /dev/null
@@ -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 <stddef.h>
+
+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
index 3235a0e..fa4878a 100644 (file)
@@ -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 (file)
index 0000000..43e65cc
--- /dev/null
@@ -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());
+  }
+}