99#ifndef LLVM_LIBC_SRC_STDIO_SCANF_CORE_READER_H
1010#define LLVM_LIBC_SRC_STDIO_SCANF_CORE_READER_H
1111
12+ #include " hdr/types/FILE.h"
13+
14+ #ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
15+ #include " src/__support/File/file.h"
16+ #endif
17+
1218#include " src/__support/macros/attributes.h" // For LIBC_INLINE
1319#include " src/__support/macros/config.h"
20+
1421#include < stddef.h>
1522
1623namespace LIBC_NAMESPACE_DECL {
1724namespace scanf_core {
18-
19- using StreamGetc = int (*)(void *);
20- using StreamUngetc = void (*)(int , void *);
25+ // We use the name "reader_internal" over "internal" because
26+ // "internal" causes name lookups in files that include the current header to be
27+ // ambigious i.e. `internal::foo` in those files, will try to lookup in
28+ // `LIBC_NAMESPACE::scanf_core::internal` over `LIBC_NAMESPACE::internal` for
29+ // e.g., `internal::ArgList` in `libc/src/stdio/scanf_core/scanf_main.h`
30+ namespace reader_internal {
31+
32+ #if defined(LIBC_TARGET_ARCH_IS_GPU)
33+ // The GPU build provides FILE access through the host operating system's
34+ // library. So here we simply use the public entrypoints like in the SYSTEM_FILE
35+ // interface. Entrypoints should normally not call others, this is an exception.
36+ // FIXME: We do not acquire any locks here, so this is not thread safe.
37+ LIBC_INLINE int getc (void *f) {
38+ return LIBC_NAMESPACE::getc (reinterpret_cast <::FILE *>(f));
39+ }
40+
41+ LIBC_INLINE void ungetc (int c, void *f) {
42+ LIBC_NAMESPACE::ungetc (c, reinterpret_cast <::FILE *>(f));
43+ }
44+
45+ #elif !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
46+
47+ LIBC_INLINE int getc (void *f) {
48+ unsigned char c;
49+ auto result =
50+ reinterpret_cast <LIBC_NAMESPACE::File *>(f)->read_unlocked (&c, 1 );
51+ size_t r = result.value ;
52+ if (result.has_error () || r != 1 )
53+ return ' \0 ' ;
54+
55+ return c;
56+ }
57+
58+ LIBC_INLINE void ungetc (int c, void *f) {
59+ reinterpret_cast <LIBC_NAMESPACE::File *>(f)->ungetc_unlocked (c);
60+ }
61+
62+ #else // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
63+
64+ // Since ungetc_unlocked isn't always available, we don't acquire the lock for
65+ // system files.
66+ LIBC_INLINE int getc (void *f) { return ::getc (reinterpret_cast <::FILE *>(f)); }
67+
68+ LIBC_INLINE void ungetc (int c, void *f) {
69+ ::ungetc (c, reinterpret_cast <::FILE *>(f));
70+ }
71+ #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
72+
73+ } // namespace reader_internal
2174
2275// This is intended to be either a raw string or a buffer syncronized with the
2376// file's internal buffer.
@@ -29,24 +82,15 @@ struct ReadBuffer {
2982
3083class Reader {
3184 ReadBuffer *rb;
32-
3385 void *input_stream = nullptr ;
34-
35- // TODO: Remove these unnecessary function pointers
36- StreamGetc stream_getc = nullptr ;
37- StreamUngetc stream_ungetc = nullptr ;
38-
3986 size_t cur_chars_read = 0 ;
4087
4188public:
4289 // TODO: Set buff_len with a proper constant
4390 LIBC_INLINE Reader (ReadBuffer *string_buffer) : rb(string_buffer) {}
4491
45- LIBC_INLINE Reader (void *stream, StreamGetc stream_getc_in,
46- StreamUngetc stream_ungetc_in,
47- ReadBuffer *stream_buffer = nullptr )
48- : rb(stream_buffer), input_stream(stream), stream_getc(stream_getc_in),
49- stream_ungetc(stream_ungetc_in) {}
92+ LIBC_INLINE Reader (void *stream, ReadBuffer *stream_buffer = nullptr )
93+ : rb(stream_buffer), input_stream(stream) {}
5094
5195 // This returns the next character from the input and advances it by one
5296 // character. When it hits the end of the string or file it returns '\0' to
@@ -59,12 +103,23 @@ class Reader {
59103 return output;
60104 }
61105 // This should reset the buffer if applicable.
62- return static_cast <char >(stream_getc (input_stream));
106+ return static_cast <char >(reader_internal::getc (input_stream));
63107 }
64108
65109 // This moves the input back by one character, placing c into the buffer if
66110 // this is a file reader, else c is ignored.
67- void ungetc (char c);
111+ LIBC_INLINE void ungetc (char c) {
112+ --cur_chars_read;
113+ if (rb != nullptr && rb->buff_cur > 0 ) {
114+ // While technically c should be written back to the buffer, in scanf we
115+ // always write the character that was already there. Additionally, the
116+ // buffer is most likely to contain a string that isn't part of a file,
117+ // which may not be writable.
118+ --(rb->buff_cur );
119+ return ;
120+ }
121+ reader_internal::ungetc (static_cast <int >(c), input_stream);
122+ }
68123
69124 LIBC_INLINE size_t chars_read () { return cur_chars_read; }
70125};
0 commit comments