[libc] add scanf reader

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
This commit is contained in:
Michael Jones 2022-10-19 13:33:05 -07:00
parent 6a6101958a
commit 0c8db885f6
9 changed files with 324 additions and 0 deletions

View 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
)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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
)

View File

@ -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());
}
}