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