blob: 1f8ec9695a314ab93efb36584ee1d79b9b45c203 [file] [log] [blame]
//===-- 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 "hdr/types/FILE.h"
#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
#include "src/__support/File/file.h"
#endif
#if defined(LIBC_TARGET_ARCH_IS_GPU)
#include "src/stdio/getc.h"
#include "src/stdio/ungetc.h"
#endif
#include "src/__support/macros/attributes.h" // For LIBC_INLINE
#include "src/__support/macros/config.h"
#include <stddef.h>
namespace LIBC_NAMESPACE_DECL {
namespace scanf_core {
// We use the name "reader_internal" over "internal" because
// "internal" causes name lookups in files that include the current header to be
// ambigious i.e. `internal::foo` in those files, will try to lookup in
// `LIBC_NAMESPACE::scanf_core::internal` over `LIBC_NAMESPACE::internal` for
// e.g., `internal::ArgList` in `libc/src/stdio/scanf_core/scanf_main.h`
namespace reader_internal {
#if defined(LIBC_TARGET_ARCH_IS_GPU)
// The GPU build provides FILE access through the host operating system's
// library. So here we simply use the public entrypoints like in the SYSTEM_FILE
// interface. Entrypoints should normally not call others, this is an exception.
// FIXME: We do not acquire any locks here, so this is not thread safe.
LIBC_INLINE int getc(void *f) {
return LIBC_NAMESPACE::getc(reinterpret_cast<::FILE *>(f));
}
LIBC_INLINE void ungetc(int c, void *f) {
LIBC_NAMESPACE::ungetc(c, reinterpret_cast<::FILE *>(f));
}
#elif !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
LIBC_INLINE int getc(void *f) {
unsigned char c;
auto result =
reinterpret_cast<LIBC_NAMESPACE::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<LIBC_NAMESPACE::File *>(f)->ungetc_unlocked(c);
}
#else // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
// Since ungetc_unlocked isn't always available, we don't acquire the lock for
// system files.
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));
}
#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
} // namespace reader_internal
// This is intended to be either a raw string or a buffer syncronized with the
// file's internal buffer.
struct ReadBuffer {
const char *buffer;
size_t buff_len;
size_t buff_cur = 0;
};
class Reader {
ReadBuffer *rb;
void *input_stream = nullptr;
size_t cur_chars_read = 0;
public:
// TODO: Set buff_len with a proper constant
LIBC_INLINE Reader(ReadBuffer *string_buffer) : rb(string_buffer) {}
LIBC_INLINE Reader(void *stream, ReadBuffer *stream_buffer = nullptr)
: rb(stream_buffer), input_stream(stream) {}
// 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.
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>(reader_internal::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.
LIBC_INLINE void ungetc(char c) {
--cur_chars_read;
if (rb != nullptr && rb->buff_cur > 0) {
// While technically c should be written back to the buffer, in scanf we
// always write the character that was already there. Additionally, the
// buffer is most likely to contain a string that isn't part of a file,
// which may not be writable.
--(rb->buff_cur);
return;
}
reader_internal::ungetc(static_cast<int>(c), input_stream);
}
LIBC_INLINE size_t chars_read() { return cur_chars_read; }
};
} // namespace scanf_core
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC_STDIO_SCANF_CORE_READER_H