9
9
#ifndef LLVM_LIBC_SRC_STDIO_SCANF_CORE_READER_H
10
10
#define LLVM_LIBC_SRC_STDIO_SCANF_CORE_READER_H
11
11
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
+
12
18
#include " src/__support/macros/attributes.h" // For LIBC_INLINE
13
19
#include " src/__support/macros/config.h"
20
+
14
21
#include < stddef.h>
15
22
16
23
namespace LIBC_NAMESPACE_DECL {
17
24
namespace 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
21
74
22
75
// This is intended to be either a raw string or a buffer syncronized with the
23
76
// file's internal buffer.
@@ -29,24 +82,15 @@ struct ReadBuffer {
29
82
30
83
class Reader {
31
84
ReadBuffer *rb;
32
-
33
85
void *input_stream = nullptr ;
34
-
35
- // TODO: Remove these unnecessary function pointers
36
- StreamGetc stream_getc = nullptr ;
37
- StreamUngetc stream_ungetc = nullptr ;
38
-
39
86
size_t cur_chars_read = 0 ;
40
87
41
88
public:
42
89
// TODO: Set buff_len with a proper constant
43
90
LIBC_INLINE Reader (ReadBuffer *string_buffer) : rb(string_buffer) {}
44
91
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) {}
50
94
51
95
// This returns the next character from the input and advances it by one
52
96
// character. When it hits the end of the string or file it returns '\0' to
@@ -59,12 +103,23 @@ class Reader {
59
103
return output;
60
104
}
61
105
// 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));
63
107
}
64
108
65
109
// This moves the input back by one character, placing c into the buffer if
66
110
// 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
+ }
68
123
69
124
LIBC_INLINE size_t chars_read () { return cur_chars_read; }
70
125
};
0 commit comments