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