#define EFL_IO_READER_PROTECTED 1 #define EFL_IO_WRITER_PROTECTED 1 #include "config.h" #include "Efl.h" #define MY_CLASS EFL_IO_BUFFER_CLASS typedef struct _Efl_Io_Buffer_Data { uint8_t *bytes; size_t allocated; size_t used; size_t limit; size_t position_read; size_t position_write; Eina_Bool closed; Eina_Bool can_read; Eina_Bool can_write; Eina_Bool readonly; } Efl_Io_Buffer_Data; static Eina_Bool _efl_io_buffer_realloc(Eo *o, Efl_Io_Buffer_Data *pd, size_t size) { void *tmp; size_t limit = efl_io_buffer_limit_get(o); EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->readonly, EINA_FALSE); if ((limit > 0) && (size > limit)) size = limit; if (pd->allocated == size) return EINA_FALSE; if (efl_io_sizer_size_get(o) > size) { if (efl_io_buffer_position_read_get(o) > size) efl_io_buffer_position_read_set(o, size); if (efl_io_buffer_position_write_get(o) > size) efl_io_buffer_position_write_set(o, size); /* no efl_io_sizer_size_set() since it could recurse! */ pd->used = size; efl_event_callback_call(o, EFL_IO_SIZER_EVENT_SIZE_CHANGED, NULL); } if (size == 0) { free(pd->bytes); tmp = NULL; } else { tmp = realloc(pd->bytes, size); EINA_SAFETY_ON_NULL_RETURN_VAL(tmp, EINA_FALSE); } pd->bytes = tmp; pd->allocated = size; efl_event_callback_call(o, EFL_IO_BUFFER_EVENT_REALLOCATED, NULL); return EINA_TRUE; } static Eina_Bool _efl_io_buffer_realloc_rounded(Eo *o, Efl_Io_Buffer_Data *pd, size_t size) { if ((size > 0) && (size < 128)) size = ((size / 32) + 1) * 32; else if (size < 1024) size = ((size / 128) + 1) * 128; else if (size < 8192) size = ((size / 1024) + 1) * 1024; else size = ((size / 4096) + 1) * 4096; return _efl_io_buffer_realloc(o, pd, size); } EOLIAN static void _efl_io_buffer_preallocate(Eo *o, Efl_Io_Buffer_Data *pd, size_t size) { EINA_SAFETY_ON_TRUE_RETURN(pd->readonly); EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o)); if (pd->allocated < size) _efl_io_buffer_realloc_rounded(o, pd, size); } EOLIAN static void _efl_io_buffer_limit_set(Eo *o, Efl_Io_Buffer_Data *pd, size_t limit) { EINA_SAFETY_ON_TRUE_RETURN(pd->readonly); EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o)); if (pd->limit == limit) return; pd->limit = limit; if ((limit > 0) && (pd->allocated > limit)) _efl_io_buffer_realloc(o, pd, limit); if (pd->closed) return; efl_io_reader_can_read_set(o, efl_io_buffer_position_read_get(o) < efl_io_sizer_size_get(o)); if (pd->closed) return; efl_io_writer_can_write_set(o, (limit == 0) || (efl_io_buffer_position_write_get(o) < limit)); } EOLIAN static size_t _efl_io_buffer_limit_get(const Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd) { return pd->limit; } EOLIAN static Eina_Slice _efl_io_buffer_slice_get(const Eo *o, Efl_Io_Buffer_Data *pd) { Eina_Slice slice = { }; if (!efl_io_closer_closed_get(o)) { slice.mem = pd->bytes; slice.len = efl_io_sizer_size_get(o); } return slice; } EOLIAN static Eina_Binbuf * _efl_io_buffer_binbuf_steal(Eo *o, Efl_Io_Buffer_Data *pd) { Eina_Binbuf *ret; EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->readonly, NULL); EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), NULL); ret = eina_binbuf_manage_new(pd->bytes, efl_io_sizer_size_get(o), EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(ret, NULL); pd->bytes = NULL; pd->allocated = 0; efl_event_callback_call(o, EFL_IO_BUFFER_EVENT_REALLOCATED, NULL); efl_io_sizer_resize(o, 0); return ret; } EOLIAN static Efl_Object * _efl_io_buffer_efl_object_finalize(Eo *o, Efl_Io_Buffer_Data *pd EINA_UNUSED) { size_t limit; o = efl_finalize(efl_super(o, MY_CLASS)); if (!o) return NULL; efl_io_reader_can_read_set(o, efl_io_buffer_position_read_get(o) < efl_io_sizer_size_get(o)); if (pd->closed) return o; limit = efl_io_buffer_limit_get(o); efl_io_writer_can_write_set(o, (limit == 0) || (efl_io_buffer_position_write_get(o) < limit)); return o; } EOLIAN static void _efl_io_buffer_efl_object_destructor(Eo *o, Efl_Io_Buffer_Data *pd) { if (!efl_io_closer_closed_get(o)) { efl_event_freeze(o); efl_io_closer_close(o); efl_event_thaw(o); } efl_destructor(efl_super(o, MY_CLASS)); if (pd->bytes) { if (!pd->readonly) free(pd->bytes); pd->bytes = NULL; pd->allocated = 0; pd->used = 0; pd->position_read = 0; pd->position_write = 0; } } EOLIAN static Eina_Error _efl_io_buffer_efl_io_reader_read(Eo *o, Efl_Io_Buffer_Data *pd, Eina_Rw_Slice *rw_slice) { Eina_Slice ro_slice; size_t used, read_pos, available; EINA_SAFETY_ON_NULL_RETURN_VAL(rw_slice, EINVAL); EINA_SAFETY_ON_TRUE_GOTO(efl_io_closer_closed_get(o), error); used = efl_io_sizer_size_get(o); read_pos = efl_io_buffer_position_read_get(o); available = used - read_pos; if (rw_slice->len > available) { rw_slice->len = available; if (rw_slice->len == 0) return EAGAIN; } ro_slice.len = rw_slice->len; ro_slice.mem = pd->bytes + read_pos; *rw_slice = eina_rw_slice_copy(*rw_slice, ro_slice); efl_io_buffer_position_read_set(o, read_pos + ro_slice.len); return 0; error: rw_slice->len = 0; rw_slice->mem = NULL; efl_io_reader_can_read_set(o, EINA_FALSE); return EINVAL; } EOLIAN static Eina_Bool _efl_io_buffer_efl_io_reader_can_read_get(const Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd) { return pd->can_read; } EOLIAN static void _efl_io_buffer_efl_io_reader_can_read_set(Eo *o, Efl_Io_Buffer_Data *pd, Eina_Bool can_read) { EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o)); if (pd->can_read == can_read) return; pd->can_read = can_read; efl_event_callback_call(o, EFL_IO_READER_EVENT_CAN_READ_CHANGED, &can_read); } EOLIAN static Eina_Bool _efl_io_buffer_efl_io_reader_eos_get(const Eo *o, Efl_Io_Buffer_Data *pd EINA_UNUSED) { return efl_io_closer_closed_get(o) || efl_io_buffer_position_read_get(o) >= efl_io_sizer_size_get(o); } EOLIAN static void _efl_io_buffer_efl_io_reader_eos_set(Eo *o, Efl_Io_Buffer_Data *pd EINA_UNUSED, Eina_Bool is_eos) { EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o)); if (is_eos) efl_event_callback_call(o, EFL_IO_READER_EVENT_EOS, NULL); } EOLIAN static Eina_Error _efl_io_buffer_efl_io_writer_write(Eo *o, Efl_Io_Buffer_Data *pd, Eina_Slice *slice, Eina_Slice *remaining) { size_t available, todo, write_pos, limit; int err = EINVAL; EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->readonly, EPERM); EINA_SAFETY_ON_NULL_RETURN_VAL(slice, EINVAL); EINA_SAFETY_ON_TRUE_GOTO(efl_io_closer_closed_get(o), error); write_pos = efl_io_buffer_position_write_get(o); available = pd->allocated - write_pos; limit = efl_io_buffer_limit_get(o); err = ENOSPC; if (available >= slice->len) todo = slice->len; else if ((limit > 0) && (pd->allocated == limit)) goto error; else { _efl_io_buffer_realloc_rounded(o, pd, write_pos + slice->len); if (pd->allocated >= write_pos + slice->len) todo = slice->len; else todo = pd->allocated - write_pos; if (todo == 0) goto error; } memcpy(pd->bytes + write_pos, slice->mem, todo); if (remaining) { remaining->len = slice->len - todo; if (remaining->len) remaining->mem = slice->bytes + todo; else remaining->mem = NULL; } slice->len = todo; if (pd->used < write_pos + todo) { pd->used = write_pos + todo; efl_event_callback_call(o, EFL_IO_SIZER_EVENT_SIZE_CHANGED, NULL); if (pd->closed) return 0; efl_io_reader_can_read_set(o, pd->position_read < pd->used); if (pd->closed) return 0; } efl_io_buffer_position_write_set(o, write_pos + todo); return 0; error: if (remaining) *remaining = *slice; slice->len = 0; slice->mem = NULL; efl_io_writer_can_write_set(o, EINA_FALSE); return err; } EOLIAN static Eina_Bool _efl_io_buffer_efl_io_writer_can_write_get(const Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd) { return pd->can_write; } EOLIAN static void _efl_io_buffer_efl_io_writer_can_write_set(Eo *o, Efl_Io_Buffer_Data *pd, Eina_Bool can_write) { EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o)); if (pd->can_write == can_write) return; pd->can_write = can_write; efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, &can_write); } EOLIAN static Eina_Error _efl_io_buffer_efl_io_closer_close(Eo *o, Efl_Io_Buffer_Data *pd) { EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINVAL); efl_io_sizer_resize(o, 0); pd->closed = EINA_TRUE; efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL); return 0; } EOLIAN static Eina_Bool _efl_io_buffer_efl_io_closer_closed_get(const Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd) { return pd->closed; } EOLIAN static Eina_Bool _efl_io_buffer_efl_io_closer_close_on_exec_set(Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd EINA_UNUSED, Eina_Bool close_on_exec) { if (!close_on_exec) return EINA_FALSE; return EINA_TRUE; } EOLIAN static Eina_Bool _efl_io_buffer_efl_io_closer_close_on_exec_get(const Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd EINA_UNUSED) { return EINA_TRUE; } EOLIAN static void _efl_io_buffer_efl_io_closer_close_on_invalidate_set(Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd EINA_UNUSED, Eina_Bool close_on_invalidate EINA_UNUSED) { } EOLIAN static Eina_Bool _efl_io_buffer_efl_io_closer_close_on_invalidate_get(const Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd EINA_UNUSED) { return EINA_TRUE; } EOLIAN static Eina_Error _efl_io_buffer_efl_io_sizer_resize(Eo *o, Efl_Io_Buffer_Data *pd, uint64_t size) { Eina_Error ret = 0; Eina_Bool reallocated = EINA_FALSE; size_t old_size, pos_read, pos_write; EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINVAL); if (efl_io_sizer_size_get(o) == size) return 0; if (pd->readonly) { EINA_SAFETY_ON_TRUE_RETURN_VAL(size > pd->used, EPERM); pd->used = size; goto end; } old_size = pd->used; pd->used = size; efl_event_freeze(o); reallocated = _efl_io_buffer_realloc_rounded(o, pd, size); efl_event_thaw(o); if (size > pd->allocated) { pd->used = size = pd->allocated; ret = ENOSPC; } if (old_size < size) memset(pd->bytes + old_size, 0, size - old_size); end: pos_read = efl_io_buffer_position_read_get(o); if (pos_read > size) efl_io_buffer_position_read_set(o, size); else efl_io_reader_can_read_set(o, pos_read < size); if (pd->closed) return 0; pos_write = efl_io_buffer_position_write_get(o); if (pos_write > size) efl_io_buffer_position_write_set(o, size); else { size_t limit = efl_io_buffer_limit_get(o); efl_io_writer_can_write_set(o, (limit == 0) || (pos_write < limit)); if (pd->closed) return 0; } efl_event_callback_call(o, EFL_IO_SIZER_EVENT_SIZE_CHANGED, NULL); if (reallocated) efl_event_callback_call(o, EFL_IO_BUFFER_EVENT_REALLOCATED, NULL); return ret; } EOLIAN static uint64_t _efl_io_buffer_efl_io_sizer_size_get(const Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd) { return pd->used; } EOLIAN static Eina_Error _efl_io_buffer_efl_io_positioner_seek(Eo *o, Efl_Io_Buffer_Data *pd EINA_UNUSED, int64_t offset, Efl_Io_Positioner_Whence whence) { size_t size; EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINVAL); size = efl_io_sizer_size_get(o); if (whence == EFL_IO_POSITIONER_WHENCE_CURRENT) { whence = EFL_IO_POSITIONER_WHENCE_START; offset += efl_io_positioner_position_get(o); } else if (whence == EFL_IO_POSITIONER_WHENCE_END) { whence = EFL_IO_POSITIONER_WHENCE_START; offset += size; } EINA_SAFETY_ON_TRUE_RETURN_VAL(whence != EFL_IO_POSITIONER_WHENCE_START, EINVAL); EINA_SAFETY_ON_TRUE_RETURN_VAL(offset < 0, EINVAL); EINA_SAFETY_ON_TRUE_RETURN_VAL((size_t)offset > size, EINVAL); efl_io_buffer_position_read_set(o, offset); efl_io_buffer_position_write_set(o, offset); return 0; } EOLIAN static uint64_t _efl_io_buffer_efl_io_positioner_position_get(const Eo *o, Efl_Io_Buffer_Data *pd EINA_UNUSED) { uint64_t r = efl_io_buffer_position_read_get(o); uint64_t w = efl_io_buffer_position_write_get(o); /* if using Efl.Io.Positioner.position, on set it will do both * read/write to the same offset, however on Efl.Io.Reader.read it * will only update position_read (and similarly for * Efl.Io.Writer), thus on the next position.get we want the * greatest position. * * This allows the buffer to be used solely as reader or writer * without the need to know it have two internal offsets. */ if (r >= w) return r; return w; } EOLIAN static Eina_Bool _efl_io_buffer_position_read_set(Eo *o, Efl_Io_Buffer_Data *pd, uint64_t position) { size_t size; Eina_Bool changed; EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINA_FALSE); size = efl_io_sizer_size_get(o); EINA_SAFETY_ON_TRUE_RETURN_VAL(position > size, EINA_FALSE); if (pd->position_read == position) return EINA_TRUE; changed = efl_io_positioner_position_get(o) != position; pd->position_read = position; efl_event_callback_call(o, EFL_IO_BUFFER_EVENT_POSITION_READ_CHANGED, NULL); if (changed) efl_event_callback_call(o, EFL_IO_POSITIONER_EVENT_POSITION_CHANGED, NULL); efl_io_reader_can_read_set(o, position < size); if (pd->closed) return EINA_TRUE; efl_io_reader_eos_set(o, position >= size); return EINA_TRUE; } EOLIAN static uint64_t _efl_io_buffer_position_read_get(const Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd) { return pd->position_read; } EOLIAN static Eina_Bool _efl_io_buffer_position_write_set(Eo *o, Efl_Io_Buffer_Data *pd, uint64_t position) { size_t size; size_t limit; Eina_Bool changed; EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EINA_FALSE); size = efl_io_sizer_size_get(o); if (position < size) EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->readonly, EINA_FALSE); EINA_SAFETY_ON_TRUE_RETURN_VAL(position > size, EINA_FALSE); if (pd->position_write == position) return EINA_TRUE; changed = efl_io_positioner_position_get(o) != position; pd->position_write = position; efl_event_callback_call(o, EFL_IO_BUFFER_EVENT_POSITION_WRITE_CHANGED, NULL); if (changed) efl_event_callback_call(o, EFL_IO_POSITIONER_EVENT_POSITION_CHANGED, NULL); if (pd->closed) return 0; limit = efl_io_buffer_limit_get(o); efl_io_writer_can_write_set(o, (limit == 0) || (position < limit)); return EINA_TRUE; } EOLIAN static uint64_t _efl_io_buffer_position_write_get(const Eo *o EINA_UNUSED, Efl_Io_Buffer_Data *pd) { return pd->position_write; } EOLIAN static void _efl_io_buffer_adopt_readonly(Eo *o, Efl_Io_Buffer_Data *pd, const Eina_Slice slice) { Eina_Bool changed_size; EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o)); if (!pd->readonly) free(pd->bytes); pd->readonly = EINA_TRUE; pd->bytes = (uint8_t *)slice.bytes; pd->allocated = slice.len; changed_size = (pd->used != slice.len); pd->used = slice.len; efl_io_writer_can_write_set(o, EINA_FALSE); if (pd->closed) return; if (efl_io_buffer_position_read_get(o) > slice.len) { efl_io_buffer_position_read_set(o, slice.len); if (pd->closed) return; } efl_io_buffer_position_write_set(o, slice.len); if (pd->closed) return; if (changed_size) { efl_event_callback_call(o, EFL_IO_SIZER_EVENT_SIZE_CHANGED, NULL); if (pd->closed) return; } efl_event_callback_call(o, EFL_IO_BUFFER_EVENT_REALLOCATED, NULL); } EOLIAN static void _efl_io_buffer_adopt_readwrite(Eo *o, Efl_Io_Buffer_Data *pd, Eina_Rw_Slice slice) { Eina_Bool changed_size; EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o)); if (!pd->readonly) free(pd->bytes); pd->readonly = EINA_FALSE; pd->bytes = slice.bytes; pd->allocated = slice.len; changed_size = (pd->used != slice.len); pd->used = slice.len; efl_io_writer_can_write_set(o, (pd->limit == 0) || (efl_io_buffer_position_write_get(o) < pd->limit)); if (pd->closed) return; if (efl_io_buffer_position_read_get(o) > slice.len) { efl_io_buffer_position_read_set(o, slice.len); if (pd->closed) return; } if (efl_io_buffer_position_write_get(o) > slice.len) { efl_io_buffer_position_write_set(o, slice.len); if (pd->closed) return; } if (changed_size) { efl_event_callback_call(o, EFL_IO_SIZER_EVENT_SIZE_CHANGED, NULL); if (pd->closed) return; } efl_event_callback_call(o, EFL_IO_BUFFER_EVENT_REALLOCATED, NULL); } #include "interfaces/efl_io_buffer.eo.c"