/* Reads the file from the current position to the end into a string. */ static MVMString * slurp(MVMThreadContext *tc, MVMOSHandle *h) { MVMIOFileData *data = (MVMIOFileData *)h->body.data; uv_fs_t req; ensure_decode_stream(tc, data); /* Typically we're slurping an entire file, so just request the bytes * until the end; repeat to ensure we get 'em all. */ if (uv_fs_fstat(tc->loop, &req, data->fd, NULL) < 0) { MVM_exception_throw_adhoc(tc, "slurp from filehandle failed: %s", uv_strerror(req.result)); } /* Sometimes - usually for special files like those in /proc - the file * size comes up 0, even though the S_ISREG test succeeds. So in that case * we try a small read and switch to a "read chunks until EOF" impl. * Otherwise we just read the exact size of the file. */ if (req.statbuf.st_size == 0) { if (read_to_buffer(tc, data, 32) > 0) { while (read_to_buffer(tc, data, 4096) > 0) ; } } else { while (read_to_buffer(tc, data, req.statbuf.st_size) > 0) ; } return MVM_string_decodestream_get_all(tc, data->ds); }
MVMString * MVM_string_decodestream_get_chars(MVMThreadContext *tc, MVMDecodeStream *ds, MVMint32 chars, MVMint64 eof) { MVMint32 missing; /* If we request nothing, give empty string. */ if (chars == 0) return tc->instance->str_consts.empty; /* If we don't already have enough chars, try and decode more. */ missing = missing_chars(tc, ds, chars); ds->result_size_guess = missing; if (missing) run_decode(tc, ds, &missing, NULL, DECODE_NOT_EOF); /* If we've got enough, assemble a string. Otherwise, flag EOF and retry, * falling back to returning what's available. */ if (missing_chars(tc, ds, chars) == 0) { return take_chars(tc, ds, chars, 0); } else if (eof) { reached_eof(tc, ds); return missing_chars(tc, ds, chars) == 0 ? take_chars(tc, ds, chars, 0) : MVM_string_decodestream_get_all(tc, ds); } else { return NULL; } }
/* Reads the stream from the current position to the end into a string, * fetching as much data is available. */ MVMString * MVM_io_syncstream_slurp(MVMThreadContext *tc, MVMOSHandle *h) { MVMIOSyncStreamData *data = (MVMIOSyncStreamData *)h->body.data; ensure_decode_stream(tc, data); /* Fetch as much data as we can (XXX this can be more efficient, by * passing on down that we want to get many buffers from libuv). */ while (read_to_buffer(tc, data, CHUNK_SIZE)) ; return MVM_string_decodestream_get_all(tc, data->ds); }
/* Gets the specified number of characters from the file. */ static MVMString * read_chars(MVMThreadContext *tc, MVMOSHandle *h, MVMint64 chars) { MVMIOFileData *data = (MVMIOFileData *)h->body.data; ensure_decode_stream(tc, data); /* Pull data until we can read the chars we want. */ do { MVMString *result = MVM_string_decodestream_get_chars(tc, data->ds, chars); if (result != NULL) return result; } while (read_to_buffer(tc, data, CHUNK_SIZE) > 0); /* Reached end of file, so just take what we have. */ return MVM_string_decodestream_get_all(tc, data->ds); }
/* Reads the file from the current position to the end into a string. */ static MVMString * slurp(MVMThreadContext *tc, MVMOSHandle *h) { MVMIOFileData *data = (MVMIOFileData *)h->body.data; uv_fs_t req; ensure_decode_stream(tc, data); /* Typically we're slurping an entire file, so just request the bytes * until the end; repeat to ensure we get 'em all. */ if (uv_fs_fstat(tc->loop, &req, data->fd, NULL) < 0) { MVM_exception_throw_adhoc(tc, "slurp from filehandle failed: %s", uv_strerror(req.result)); } while (read_to_buffer(tc, data, req.statbuf.st_size) > 0) ; return MVM_string_decodestream_get_all(tc, data->ds); }
/* Reads a single line from the file handle. May serve it from a buffer, if we * already read enough data. */ static MVMString * read_line(MVMThreadContext *tc, MVMOSHandle *h) { MVMIOFileData *data = (MVMIOFileData *)h->body.data; ensure_decode_stream(tc, data); /* Pull data until we can read a line. */ do { MVMString *line = MVM_string_decodestream_get_until_sep(tc, data->ds, data->sep); if (line != NULL) return line; } while (read_to_buffer(tc, data, CHUNK_SIZE) > 0); /* Reached end of file, or last (non-termianted) line. */ return MVM_string_decodestream_get_all(tc, data->ds); }
/* Variant of MVM_string_decodestream_get_until_sep that is called when we * reach EOF. Trims the final separator if there is one, or returns the last * line without the EOF marker. */ MVMString * MVM_string_decodestream_get_until_sep_eof(MVMThreadContext *tc, MVMDecodeStream *ds, MVMDecodeStreamSeparators *sep_spec, MVMint32 chomp) { MVMint32 sep_loc, sep_length; /* Decode anything remaining and flush normalization buffer. */ reached_eof(tc, ds); /* Look for separator, which should by now be at the end, and chomp it * off if needed. */ sep_loc = find_separator(tc, ds, sep_spec, &sep_length); if (sep_loc) return take_chars(tc, ds, sep_loc, chomp ? sep_length : 0); /* Otherwise, take all remaining chars. */ return MVM_string_decodestream_get_all(tc, ds); }
/* Gets the specified number of characters from the stream. */ MVMString * MVM_io_syncstream_read_chars(MVMThreadContext *tc, MVMOSHandle *h, MVMint64 chars) { MVMIOSyncStreamData *data = (MVMIOSyncStreamData *)h->body.data; MVMString *result; ensure_decode_stream(tc, data); /* Do we already have the chars available? */ result = MVM_string_decodestream_get_chars(tc, data->ds, chars); if (result) { return result; } else { /* No; read and try again. */ read_to_buffer(tc, data, CHUNK_SIZE); result = MVM_string_decodestream_get_chars(tc, data->ds, chars); if (result != NULL) return result; } /* Fetched all we immediately can, so just take what we have. */ return MVM_string_decodestream_get_all(tc, data->ds); }
/* Read handler. */ static void on_read(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) { ReadInfo *ri = (ReadInfo *)handle->data; MVMThreadContext *tc = ri->tc; MVMObject *arr = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray); MVMAsyncTask *t = (MVMAsyncTask *)MVM_repr_at_pos_o(tc, tc->instance->event_loop_active, ri->work_idx); MVM_repr_push_o(tc, arr, t->body.schedulee); if (nread > 0) { MVMROOT(tc, t, { MVMROOT(tc, arr, { /* Push the sequence number. */ MVMObject *seq_boxed = MVM_repr_box_int(tc, tc->instance->boot_types.BOOTInt, ri->seq_number++); MVM_repr_push_o(tc, arr, seq_boxed); /* Either need to produce a buffer or decode characters. */ if (ri->ds) { MVMString *str; MVMObject *boxed_str; MVM_string_decodestream_add_bytes(tc, ri->ds, buf->base, nread); str = MVM_string_decodestream_get_all(tc, ri->ds); boxed_str = MVM_repr_box_str(tc, tc->instance->boot_types.BOOTStr, str); MVM_repr_push_o(tc, arr, boxed_str); } else { MVMArray *res_buf = (MVMArray *)MVM_repr_alloc_init(tc, ri->buf_type); res_buf->body.slots.i8 = buf->base; res_buf->body.start = 0; res_buf->body.ssize = nread; res_buf->body.elems = nread; MVM_repr_push_o(tc, arr, (MVMObject *)res_buf); } /* Finally, no error. */ MVM_repr_push_o(tc, arr, tc->instance->boot_types.BOOTStr); }); });