1#include "include/pw.h"
2#include "include/pwlib/string_io.h"
3#include "src/pw_interfaces_internal.h"
4#include "src/types/string/string_internal.h"
5
6PW_STRUCT(_PwStringIO) {
7 // line reader iterator data
8 _PwValue line;
9 _PwValue pushback;
10 unsigned line_number;
11 unsigned line_position;
12};
13
14
15/****************************************************************
16 * Basic interface
17 */
18
19uint16_t PwTypeId_StringIO = 0;
20
21static bool stringio_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
22{
23 PwStringIOCtorArgs* args = pw_this_ctor_args();
24
25 pw_assert(pw_is_string(args->string));
26
27 if (!pw_super(mthis, result, ctor_args)) {
28 return false;
29 }
30 _PwStringIO* sio = pw_this_data(result);
31 pw_clone2(&sio->line, args->string);
32 sio->pushback = PwNull();
33 return true;
34}
35
36static bool stringio_destroy(PwMethod_Basic_destroy* mthis, PwValuePtr self, _PwCompoundChain* tail)
37{
38 PwValuePtr value_seen = _pw_on_chain(self, tail);
39 if (value_seen) {
40 return true;
41 }
42 _PwStringIO* sio = pw_this_data(self);
43 pw_destroy(&sio->line);
44 pw_destroy(&sio->pushback);
45 return pw_super(mthis, self, tail);
46}
47
48static bool stringio_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
49{
50 _pw_hash_uint64(ctx, PwTypeId_StringIO);
51
52 _PwStringIO* sio = pw_this_data(self);
53 PwValuePtr line = &sio->line;
54 _pw_call_hash(line, ctx, nullptr);
55 return true;
56}
57
58static bool stringio_deepcopy(PwMethod_Basic_deepcopy* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
59{
60 _PwStringIO* sio = pw_this_data(self);
61
62 return pw_create_string_io(result, &sio->line);
63}
64
65static bool stringio_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
66{
67 if (!pw_super(mthis, self, fp, indent, tail)) {
68 return false;
69 }
70
71 _PwStringIO* sio = pw_this_data(self);
72
73 _pw_print_indent(fp, indent);
74 _pw_string_dump_data(fp, &sio->line, indent);
75
76 _pw_print_indent(fp, indent);
77 fprintf(fp, "Current position: %u\n", sio->line_position);
78
79 _pw_print_indent(fp, indent);
80 if (pw_is_null(&sio->pushback)) {
81 fputs("Pushback: none\n", fp);
82 } else if (!pw_is_string(&sio->pushback)) {
83 fputs("WARNING: bad pushback:\n", fp);
84 pw_dump(fp, &sio->pushback);
85 } else {
86 fputs("Pushback:\n", fp);
87 _pw_string_dump_data(fp, &sio->pushback, indent);
88 }
89 return true;
90}
91
92static bool stringio_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
93{
94 _PwStringIO* sio = pw_this_data(self);
95 pw_clone2(result, &sio->line);
96 return true;
97}
98
99static bool stringio_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
100{
101 _PwStringIO* sio = pw_this_data(self);
102 return pw_is_true(&sio->line);
103}
104
105static bool stringio_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
106{
107 _PwStringIO* sio_self = pw_this_data(self);
108 _PwStringIO* sio_other = _pw_get_struct_ptr(other, PwTypeId_StringIO);
109
110 if (sio_other) {
111 return pw_equal(&sio_self->line, &sio_other->line);
112 } else {
113 return false;
114 }
115}
116
117// inherited methods:
118#define stringio_clone nullptr
119#define stringio_decref nullptr
120#define stringio_is_immutable nullptr
121#define stringio_iter_children nullptr
122
123static PwInterface_Basic basic_interface = {
124#define X(name, ...) .name = { .func = stringio_##name } __VA_OPT__(,)
125 PW_BASIC_INTERFACE_METHODS
126#undef X
127};
128
129/****************************************************************
130 * LineReader interface
131 */
132
133static bool stringio_start(PwMethod_LineReader_start* mthis, PwValuePtr self)
134{
135 _PwStringIO* sio = pw_this_data(self);
136 sio->line_position = 0;
137 sio->line_number = 0;
138 pw_destroy(&sio->pushback);
139 return true;
140}
141
142static bool do_read_line_inplace(_PwStringIO* sio, PwValuePtr self, PwValuePtr line)
143{
144 if (!pw_string_truncate(line, 0)) {
145 return false;
146 }
147 if (pw_is_string(&sio->pushback)) {
148 if (!pw_string_append(line, &sio->pushback)) {
149 return false;
150 }
151 pw_destroy(&sio->pushback);
152 sio->line_number++;
153 return true;
154 }
155 if (!pw_string_index_valid(&sio->line, sio->line_position)) {
156 pw_set_status(PwStatus(PweEOF));
157 return false;
158 }
159
160 unsigned lf_pos;
161 if (!pw_strchr(&sio->line, '\n', sio->line_position, &lf_pos)) {
162 lf_pos = pw_strlen(&sio->line) - 1;
163 }
164 if (!pw_string_append_substring(line, &sio->line, sio->line_position, lf_pos + 1)) {
165 return false;
166 }
167 sio->line_position = lf_pos + 1;
168 sio->line_number++;
169 return true;
170}
171
172static bool stringio_read_line(PwMethod_LineReader_read_line* mthis, PwValuePtr self, PwValuePtr result)
173{
174 pw_destroy(result);
175 *result = PwString();
176 return do_read_line_inplace(pw_this_data(self), self, result);
177}
178
179static bool stringio_read_line_inplace(PwMethod_LineReader_read_line_inplace* mthis, PwValuePtr self, PwValuePtr line)
180{
181 return do_read_line_inplace(pw_this_data(self), self, line);
182}
183
184static bool stringio_unread_line(PwMethod_LineReader_unread_line* mthis, PwValuePtr self, PwValuePtr line)
185{
186 _PwStringIO* sio = pw_this_data(self);
187
188 if (pw_is_null(&sio->pushback)) {
189 __pw_clone(&sio->pushback, line); // puchback is already Null, so use __pw_clone here
190 sio->line_number--;
191 return true;
192 } else {
193 return false;
194 }
195}
196
197static bool stringio_get_line_number(PwMethod_LineReader_get_line_number* mthis, PwValuePtr self, unsigned* result)
198{
199 _PwStringIO* sio = pw_this_data(self);
200 *result = sio->line_number;
201 return true;
202}
203
204static bool stringio_stop(PwMethod_LineReader_stop* mthis, PwValuePtr self)
205{
206 _PwStringIO* sio = pw_this_data(self);
207 pw_destroy(&sio->pushback);
208 return true;
209}
210
211static PwInterface_LineReader line_reader_interface = {
212#define X(name, ...) .name = { .func = stringio_##name } __VA_OPT__(,)
213 PW_LINE_READER_INTERFACE_METHODS
214#undef X
215};
216
217
218/****************************************************************
219 * Initialization
220 */
221
222[[gnu::constructor]]
223static void init_stringio_type()
224{
225 if (PwTypeId_StringIO) {
226 return;
227 }
228
229 _pw_init_types();
230
231 PwTypeId_StringIO = pw_add_type2(
232 "StringIO", _PwStringIO,
233 PW_PARENTS,
234 PwTypeId_Struct,
235 PW_INTERFACES,
236 PwInterfaceId_Basic, &basic_interface,
237 PwInterfaceId_LineReader, &line_reader_interface
238 );
239}