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}