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}