Skip to content

Commit

Permalink
Add new IO vtables to set, get and advance the current in-memory curs…
Browse files Browse the repository at this point in the history
…or position. Update file position on read and write operations. For read/write handles, make sure to sync buffers so position stays consistent. Several codestd fixes. Add comments. Several tests still failing
  • Loading branch information
Whiteknight committed Jun 9, 2012
1 parent 0192161 commit 1f03c5b
Show file tree
Hide file tree
Showing 10 changed files with 465 additions and 69 deletions.
56 changes: 35 additions & 21 deletions include/parrot/io.h
Expand Up @@ -130,36 +130,40 @@ typedef INTVAL (*io_vtable_write_b) (PARROT_INTERP, PMC *handle, ARGIN
typedef INTVAL (*io_vtable_flush) (PARROT_INTERP, PMC *handle);
typedef INTVAL (*io_vtable_is_eof) (PARROT_INTERP, PMC *handle);
typedef PIOOFF_T (*io_vtable_tell) (PARROT_INTERP, PMC *handle);
typedef INTVAL (*io_vtable_seek) (PARROT_INTERP, PMC *handle, PIOOFF_T offset, INTVAL whence);
typedef PIOOFF_T (*io_vtable_seek) (PARROT_INTERP, PMC *handle, PIOOFF_T offset, INTVAL whence);
typedef void (*io_vtable_adv_position) (PARROT_INTERP, PMC *handle, size_t len);
typedef void (*io_vtable_set_position) (PARROT_INTERP, PMC *handle, PIOOFF_T pos);
typedef PIOOFF_T (*io_vtable_get_position) (PARROT_INTERP, PMC *handle);
typedef INTVAL (*io_vtable_open) (PARROT_INTERP, PMC *handle, ARGIN(STRING *path), INTVAL flags, ARGIN(STRING *mode));
typedef INTVAL (*io_vtable_is_open) (PARROT_INTERP, PMC *handle);
typedef INTVAL (*io_vtable_close) (PARROT_INTERP, PMC *handle);
typedef void (*io_vtable_set_flags) (PARROT_INTERP, PMC *handle, INTVAL flags);
typedef INTVAL (*io_vtable_get_flags) (PARROT_INTERP, PMC *handle);
typedef void (*io_vtable_ensure_buffer)(PARROT_INTERP, PMC *handle, INTVAL buffer_no, size_t size, INTVAL flags);
typedef size_t (*io_vtable_total_size) (PARROT_INTERP, PMC *handle);
typedef PIOHANDLE (*io_vtable_get_piohandle)(PARROT_INTERP, PMC *handle);
typedef const STR_VTABLE *(*io_vtable_get_encoding) (PARROT_INTERP, PMC *handle);

typedef struct _io_vtable {
const char * name;
INTVAL number;
INTVAL flags;
io_vtable_read_b read_b;
io_vtable_write_b write_b;
io_vtable_flush flush;
io_vtable_is_eof is_eof;
io_vtable_open open;
io_vtable_is_open is_open;
io_vtable_close close;
io_vtable_tell tell;
io_vtable_seek seek;
io_vtable_get_flags get_flags;
io_vtable_set_flags set_flags;
io_vtable_get_encoding get_encoding;
io_vtable_ensure_buffer ensure_buffer;
io_vtable_total_size total_size;
io_vtable_get_piohandle get_piohandle;
const char * name; /* Name of this vtable type */
INTVAL number; /* Index number of this vtable */
INTVAL flags; /* Flags for this type */
io_vtable_read_b read_b; /* Read bytes from the handle */
io_vtable_write_b write_b; /* Write bytes to the handle */
io_vtable_flush flush; /* Flush the handle */
io_vtable_is_eof is_eof; /* Determine if at end-of-file */
io_vtable_open open; /* Open the handle */
io_vtable_is_open is_open; /* Determine if the handle is open */
io_vtable_close close; /* Close the handle */
io_vtable_tell tell; /* Get on-disk file position */
io_vtable_seek seek; /* Seek to new position */
io_vtable_adv_position adv_position; /* Advance handle (in-mem) position */
io_vtable_set_position set_position; /* Set handle (in-mem) position */
io_vtable_get_position get_position; /* Get handle (in-mem) position */
io_vtable_get_flags get_flags; /* Get the flags */
io_vtable_set_flags set_flags; /* Set the flags */
io_vtable_get_encoding get_encoding; /* Get the handle encoding */
io_vtable_total_size total_size; /* Get the total size, if possible */
io_vtable_get_piohandle get_piohandle; /* Get the raw file PIOHANDLE */
} IO_VTABLE;

#define IO_VTABLE_FILEHANDLE 0
Expand Down Expand Up @@ -561,7 +565,7 @@ PIOOFF_T Parrot_io_tell_handle(PARROT_INTERP, ARGMOD(PMC *handle))

PARROT_EXPORT
PARROT_WARN_UNUSED_RESULT
INTVAL Parrot_io_write_b(PARROT_INTERP,
size_t Parrot_io_write_b(PARROT_INTERP,
ARGMOD(PMC *handle),
ARGIN(const void *buffer),
size_t byte_length)
Expand Down Expand Up @@ -601,6 +605,7 @@ PIOHANDLE Parrot_io_get_os_handle(PARROT_INTERP, ARGIN(PMC *handle))
__attribute__nonnull__(2);

PARROT_WARN_UNUSED_RESULT
PARROT_CAN_RETURN_NULL
IO_VTABLE * Parrot_io_get_vtable(PARROT_INTERP,
INTVAL idx,
ARGIN_NULLOK(const char * name))
Expand Down Expand Up @@ -851,6 +856,12 @@ void Parrot_io_buffer_add_to_handle(PARROT_INTERP,
__attribute__nonnull__(2)
FUNC_MODIFIES(*handle);

void Parrot_io_buffer_advance_position(PARROT_INTERP,
ARGMOD_NULLOK(IO_BUFFER *buffer),
size_t len)
__attribute__nonnull__(1)
FUNC_MODIFIES(*buffer);

PARROT_CANNOT_RETURN_NULL
PARROT_WARN_UNUSED_RESULT
IO_BUFFER * Parrot_io_buffer_allocate(PARROT_INTERP,
Expand Down Expand Up @@ -972,6 +983,9 @@ size_t Parrot_io_buffer_write_b(PARROT_INTERP,
__attribute__unused__ int _ASSERT_ARGS_CHECK = (\
PARROT_ASSERT_ARG(interp) \
, PARROT_ASSERT_ARG(handle))
#define ASSERT_ARGS_Parrot_io_buffer_advance_position \
__attribute__unused__ int _ASSERT_ARGS_CHECK = (\
PARROT_ASSERT_ARG(interp))
#define ASSERT_ARGS_Parrot_io_buffer_allocate __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
PARROT_ASSERT_ARG(interp) \
, PARROT_ASSERT_ARG(owner))
Expand Down
73 changes: 57 additions & 16 deletions src/io/api.c
Expand Up @@ -136,6 +136,7 @@ Parrot_io_allocate_new_vtable(PARROT_INTERP, ARGIN(const char *name))
}

PARROT_WARN_UNUSED_RESULT
PARROT_CAN_RETURN_NULL
IO_VTABLE *
Parrot_io_get_vtable(PARROT_INTERP, INTVAL idx, ARGIN_NULLOK(const char * name))
{
Expand Down Expand Up @@ -186,7 +187,6 @@ Parrot_io_finish(PARROT_INTERP)

}


/*
=item C<void Parrot_IOData_mark(PARROT_INTERP, ParrotIOData *piodata)>
Expand All @@ -213,7 +213,6 @@ Parrot_io_mark(PARROT_INTERP, ARGIN(ParrotIOData *piodata))
}
}


/*
=head2 Generic I/O interface
Expand Down Expand Up @@ -316,13 +315,10 @@ Parrot_io_open(PARROT_INTERP, ARGIN(PMC *pmc), ARGIN(STRING *path),

/* If this type uses buffers by default, set them up, and if we're
in an acceptable mode, set up buffers. */
if ((flags & (PIO_F_READ | PIO_F_WRITE)) != (PIO_F_READ | PIO_F_WRITE) &&
(vtable->flags & PIO_VF_DEFAULT_BUFFERS)) {
if (flags & PIO_F_READ)
Parrot_io_buffer_add_to_handle(interp, handle, IO_PTR_IDX_READ_BUFFER, BUFFER_SIZE_ANY, PIO_BF_BLKBUF);
if (flags & PIO_F_WRITE)
Parrot_io_buffer_add_to_handle(interp, handle, IO_PTR_IDX_WRITE_BUFFER, BUFFER_SIZE_ANY, PIO_BF_BLKBUF);
}
if (flags & PIO_F_READ)
Parrot_io_buffer_add_to_handle(interp, handle, IO_PTR_IDX_READ_BUFFER, BUFFER_SIZE_ANY, PIO_BF_BLKBUF);
if (flags & PIO_F_WRITE)
Parrot_io_buffer_add_to_handle(interp, handle, IO_PTR_IDX_WRITE_BUFFER, BUFFER_SIZE_ANY, PIO_BF_BLKBUF);
}

return handle;
Expand Down Expand Up @@ -495,8 +491,11 @@ Parrot_io_close(PARROT_INTERP, ARGMOD(PMC *handle), INTVAL autoflush)
else {
IO_VTABLE * const vtable = IO_GET_VTABLE(interp, handle);
IO_BUFFER * const write_buffer = IO_GET_WRITE_BUFFER(interp, handle);
IO_BUFFER * const read_buffer = IO_GET_READ_BUFFER(interp, handle);
if (write_buffer)
Parrot_io_buffer_flush(interp, write_buffer, handle, vtable);
if (read_buffer)
Parrot_io_buffer_clear(interp, read_buffer);

if (autoflush == -1)
autoflush == (vtable->flags && PIO_VF_FLUSH_ON_CLOSE) ? 1 : 0;
Expand Down Expand Up @@ -561,6 +560,10 @@ Parrot_io_flush(PARROT_INTERP, ARGMOD(PMC *handle))
if (PMC_IS_NULL(handle))
return 0;

if (Parrot_io_is_closed(interp, handle))
Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_PIO_ERROR,
"Cannot flush a closed handle");

else {
IO_VTABLE * const vtable = IO_GET_VTABLE(interp, handle);
IO_BUFFER * const write_buffer = IO_GET_WRITE_BUFFER(interp, handle);
Expand Down Expand Up @@ -609,12 +612,20 @@ Parrot_io_read_s(PARROT_INTERP, ARGMOD(PMC *handle), size_t length)
{
IO_VTABLE * const vtable = IO_GET_VTABLE(interp, handle);
IO_BUFFER * read_buffer = IO_GET_READ_BUFFER(interp, handle);
IO_BUFFER * const write_buffer = IO_GET_WRITE_BUFFER(interp, handle);
const STR_VTABLE * encoding = vtable->get_encoding(interp, handle);

/* read_s requires us to read in a whole number of characters, which
might be multi-byte. This requires a read buffer. */
/* TODO: If we have a fixed8 encoding or similar, we should be able to
avoid using a read_buffer here. Detect that case and don't assign
a buffer if not needed. */
if (read_buffer == NULL)
read_buffer = io_verify_has_read_buffer(interp, handle, vtable, BUFFER_SIZE_ANY);
io_verify_is_open_for(interp, handle, vtable, PIO_F_READ);

io_sync_buffers_for_read(interp, handle, vtable, read_buffer, write_buffer);

return io_read_encoded_string(interp, handle, vtable, read_buffer, encoding, length);
}
}
Expand Down Expand Up @@ -650,13 +661,15 @@ Parrot_io_readall_s(PARROT_INTERP, ARGMOD(PMC *handle))
"Attempt to read from null or invalid PMC");
{
IO_VTABLE * const vtable = IO_GET_VTABLE(interp, handle);
IO_BUFFER * const write_buffer = IO_GET_WRITE_BUFFER(interp, handle);

const STR_VTABLE * const encoding = io_get_encoding(interp, handle, vtable, PIO_F_READ);
size_t total_size = vtable->total_size(interp, handle);
if (total_size == 0)
return Parrot_str_new_init(interp, "", 0, encoding, 0);
if (total_size == PIO_UNKNOWN_SIZE) {
IO_BUFFER * const read_buffer = io_verify_has_read_buffer(interp, handle, vtable, BUFFER_FLAGS_ANY);
io_sync_buffers_for_read(interp, handle, vtable, read_buffer, write_buffer);
size_t available_bytes = Parrot_io_buffer_fill(interp, read_buffer, handle, vtable);
STRING * const s = io_get_new_empty_string(interp, encoding, -1, PIO_STRING_BUFFER_MINSIZE);
while (available_bytes > 0) {
Expand All @@ -666,6 +679,7 @@ Parrot_io_readall_s(PARROT_INTERP, ARGMOD(PMC *handle))
return s;
} else {
IO_BUFFER * const read_buffer = IO_GET_READ_BUFFER(interp, handle);
io_sync_buffers_for_read(interp, handle, vtable, read_buffer, write_buffer);
STRING * const s = io_get_new_empty_string(interp, encoding, -1, total_size);
io_read_chars_append_string(interp, s, handle, vtable, read_buffer, total_size);
return s;
Expand Down Expand Up @@ -715,13 +729,17 @@ Parrot_io_read_byte_buffer_pmc(PARROT_INTERP, ARGMOD(PMC *handle),
char * content = VTABLE_get_pointer(interp, buffer);
IO_VTABLE * const vtable = IO_GET_VTABLE(interp, handle);
IO_BUFFER * const read_buffer = IO_GET_READ_BUFFER(interp, handle);
IO_BUFFER * const write_buffer = IO_GET_WRITE_BUFFER(interp, handle);
size_t bytes_read;

io_verify_is_open_for(interp, handle, vtable, PIO_F_READ);

io_sync_buffers_for_read(interp, handle, vtable, read_buffer, write_buffer);

bytes_read = Parrot_io_buffer_read_b(interp, read_buffer, handle, vtable, content, byte_length);
if (bytes_read != byte_length)
VTABLE_set_integer_native(interp, buffer, byte_length);
vtable->adv_position(interp, handle, bytes_read);
return buffer;
}
}
Expand Down Expand Up @@ -785,9 +803,12 @@ Parrot_io_readline_s(PARROT_INTERP, ARGMOD(PMC *handle), INTVAL terminator)
{
IO_VTABLE * const vtable = IO_GET_VTABLE(interp, handle);
IO_BUFFER * read_buffer = IO_GET_READ_BUFFER(interp, handle);
IO_BUFFER * const write_buffer = IO_GET_WRITE_BUFFER(interp, handle);
size_t bytes_read;
STRING *result;

io_sync_buffers_for_read(interp, handle, vtable, read_buffer, write_buffer);

io_verify_is_open_for(interp, handle, vtable, PIO_F_READ);
if (read_buffer == NULL)
read_buffer = io_verify_has_read_buffer(interp, handle, vtable, BUFFER_SIZE_ANY);
Expand All @@ -808,26 +829,34 @@ Writes C<len> bytes from C<*buffer> to C<*pmc>.

PARROT_EXPORT
PARROT_WARN_UNUSED_RESULT
INTVAL
size_t
Parrot_io_write_b(PARROT_INTERP, ARGMOD(PMC *handle), ARGIN(const void *buffer),
size_t byte_length)
{
ASSERT_ARGS(Parrot_io_write_b)

if (PMC_IS_NULL(handle))
Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_PIO_ERROR,
"Attempt to read bytes from a null or invalid PMC");
"Attempt to write bytes to a null or invalid PMC");

if (!byte_length)
return 0;

{
IO_VTABLE * const vtable = IO_GET_VTABLE(interp, handle);
IO_BUFFER * const write_buffer = IO_GET_WRITE_BUFFER(interp, handle);
IO_BUFFER * const read_buffer = IO_GET_READ_BUFFER(interp, handle);
size_t bytes_written;

io_verify_is_open_for(interp, handle, vtable, PIO_F_WRITE);
return Parrot_io_buffer_write_b(interp, write_buffer, handle, vtable,
(char *)buffer, byte_length);
io_sync_buffers_for_write(interp, handle, vtable, read_buffer, write_buffer);
bytes_written = Parrot_io_buffer_write_b(interp, write_buffer, handle, vtable,
(char *)buffer, byte_length);
vtable->adv_position(interp, handle, bytes_written);
/* If we are writing to a r/w handle, advance the pointer in the
associated read-buffer since we're overwriting those characters. */
Parrot_io_buffer_advance_position(interp, read_buffer, bytes_written);
return bytes_written;
}
}

Expand All @@ -848,12 +877,21 @@ Parrot_io_write_s(PARROT_INTERP, ARGMOD(PMC *handle), ARGIN(STRING *s))
{
IO_VTABLE * const vtable = IO_GET_VTABLE(interp, handle);
IO_BUFFER * const write_buffer = IO_GET_WRITE_BUFFER(interp, handle);
IO_BUFFER * const read_buffer = IO_GET_READ_BUFFER(interp, handle);
STRING *out_s;
size_t bytes_written;

io_verify_is_open_for(interp, handle, vtable, PIO_F_WRITE);
io_sync_buffers_for_write(interp, handle, vtable, read_buffer, write_buffer);
out_s = io_verify_string_encoding(interp, handle, vtable, s, PIO_F_WRITE);

return Parrot_io_buffer_write_b(interp, write_buffer, handle, vtable, out_s->strstart, out_s->bufused);
bytes_written = Parrot_io_buffer_write_b(interp, write_buffer, handle,
vtable, out_s->strstart, out_s->bufused);
vtable->adv_position(interp, handle, bytes_written);
/* If we are writing to a r/w handle, advance the pointer in the
associated read-buffer since we're overwriting those characters. */
Parrot_io_buffer_advance_position(interp, read_buffer, bytes_written);
return bytes_written;
}
}

Expand Down Expand Up @@ -906,8 +944,11 @@ Parrot_io_seek(PARROT_INTERP, ARGMOD(PMC *handle), PIOOFF_T offset, INTVAL w)
if (write_buffer)
Parrot_io_buffer_flush(interp, write_buffer, handle, vtable);

if (read_buffer && w != SEEK_END)
return Parrot_io_buffer_seek(interp, read_buffer, handle, vtable, offset, w);
if (read_buffer && w != SEEK_END) {
PIOOFF_T new_offset = Parrot_io_buffer_seek(interp, read_buffer, handle, vtable, offset, w);
vtable->set_position(interp, handle, new_offset);
return new_offset;
}

return vtable->seek(interp, handle, offset, w);
}
Expand Down
43 changes: 39 additions & 4 deletions src/io/buffer.c
Expand Up @@ -431,6 +431,20 @@ Parrot_io_buffer_content_size(SHIM_INTERP, ARGIN(IO_BUFFER *buffer))
return BUFFER_USED_SIZE(buffer);
}

void
Parrot_io_buffer_advance_position(PARROT_INTERP, ARGMOD_NULLOK(IO_BUFFER *buffer), size_t len)
{
ASSERT_ARGS(Parrot_io_buffer_advance_position)
if (!buffer || BUFFER_IS_EMPTY(buffer))
return;
if (BUFFER_USED_SIZE(buffer) <= len) {
Parrot_io_buffer_clear(interp, buffer);
return;
}
buffer->buffer_start += len;
io_buffer_normalize(interp, buffer);
}

/*
=item C<size_t io_buffer_find_string_marker(PARROT_INTERP, IO_BUFFER *buffer,
Expand Down Expand Up @@ -523,11 +537,32 @@ Parrot_io_buffer_seek(PARROT_INTERP, ARGMOD(IO_BUFFER *buffer),
INTVAL w)
{
ASSERT_ARGS(Parrot_io_buffer_seek)
PIOOFF_T cur_pos = vtable->get_position(interp, handle);
PIOOFF_T pos_diff;

/* TODO: Try to seek inside the read buffer */
offset -= BUFFER_USED_SIZE(buffer);
Parrot_io_buffer_clear(interp, buffer);
return vtable->seek(interp, handle, offset, w);
PARROT_ASSERT(w == SEEK_SET);

if (cur_pos == offset)
return;
if (offset < cur_pos) {
/* write buffers are flushed before seeking, so this is a read buffer.
if we're not seeking to a position inside the buffer just clear it.
*/
Parrot_io_buffer_clear(interp, buffer);
return vtable->seek(interp, handle, offset, w);
}
pos_diff = offset - cur_pos;
PARROT_ASSERT(pos_diff > 0);

if (pos_diff > BUFFER_USED_SIZE(buffer)) {
Parrot_io_buffer_clear(interp, buffer);
return vtable->seek(interp, handle, offset, w);
}

/* If we're here, we can seek inside this buffer */
buffer->buffer_start += (size_t)pos_diff;
io_buffer_normalize(interp, buffer);
return offset;
}

/*
Expand Down

0 comments on commit 1f03c5b

Please sign in to comment.