efl/src/lib/ecore/efl_io_copier.eo

381 lines
16 KiB
Plaintext

import eina_types;
class Efl.Io.Copier (Efl.Loop_User, Efl.Io.Closer) {
[[Copy from an @Efl.Io.Reader source to @Efl.Io.Writer destination.
During usage it will keep reference to @.source and
@.destination objects, automatically relasing them on
destructor.
By default the read-write process is done based on fixed-size
chunks (@.read_chunk_size), however if @.line_delimiter is set,
the behavior changes to wait for such delimiter or a maximum
buffer limit is reached (@.buffer_limit).
While a @.source is mandatory for the copier to operate
properly, the @.destination is optional: if it's not provided,
all read data is stored in an internal buffer that can be
retrieved using @.binbuf_steal, usually called when one of
"data", "line" or "done" events are emitted.
Most important events:
- The "data" event is general and notifies some data was
written to @.destination (if any, otherwise it will be
reported for data when it's read from @.source).
- The "line" event is only emitted when @.line_delimiter is
set and not empty. It's simiar to "data".
- The "done" event is emitted if @.source is
@Efl.Io.Reader.eos $true and all data was written to
@.destination (if any, otherwise it will be reported when
all data was read from @.source).
- The "error" event is reported if the @Efl.Io.Reader.read,
@Efl.Io.Writer.write or some other internal error happened,
like out of memory. Another common error is ETIMEDOUT if
@.timeout_inactivity is set.
A copier is handful to simplify common I/O use cases, such as:
- Read a file or download content to memory: provide only the
source and wait for "done" event, calling @.binbuf_steal
afterwards.
- Download content to disk: provide a network socket as source
and use @Efl.Io.File as destination, then wait for "done"
event.
- Link two I/O streams: provide both source and destination
streams, such as @Efl.Io.Stdin and @Efl.Io.Stdout, or some
network socket. As data is received from source it will be
copied to destination in an endless (asynchronous) loop. You
may monitor for "done" if the source may be closed.
If @Efl.Io.Closer.close is called, then it will be called on
@.source and @.destination if they implement those interfaces.
@Efl.Io.Closer.close_on_exec and
@Efl.Io.Closer.close_on_destructor are respected and applied to
both source and destination. Both default to $true.
@since 1.19
]]
methods {
@property source {
[[Copier source object.
The source object must implement the @Efl.Io.Reader
interface and will provide data to the copier, thus it
must be provided in order for the copier to work.
The copier will monitor @Efl.Io.Reader.can_read property
and "can_read,changed" event. When it's $true, then a
chunk up to @.read_chunk_size is read using
@Efl.Io.Reader.read into an intermediate storage buffer
that can grow up to @.buffer_limit.
If there is a @.destination, the read data is written
there and afterwards "data" and "line" events are
dispatched. If no destination, it's accumulated in the
internal storage and "data" and "line" events are
dispatched immediately.
If there is a @.line_delimiter set, then copier will use
that in order to attempt to write a full line at a time,
including the delimiter. Exceptions may be if the
@.source object emitted "eos" and there is no trailing
delimiter (ie: missing trailing newline in files), or the
@.buffer_limit was reached.
Once @Efl.Io.Reader.eos is $true or "eos" event happen,
then the read process is over. If there is no
@.destination set, then the process is over and "done"
event is called. If there is a @.destination, then all
data must be flushed to it, draining the internal
intermediate storage, before "done" event is emitted.
If the source object implements @Efl.Io.Sizer interface,
then the @.progress will report the total size. If the
destination object also implements @Efl.Io.Sizer, then it
will be resized to match the source size, providing the
hint that may enhance performance (like pre-allocating
the total amount and avoid dynamic resize).
If the source object implements @Efl.Io.Closer and it's
not closed, it will be closed when the copier itself
is. This may happen, for example, when the copier is
deleted and @Efl.Io.Closer.close_on_destructor is $true
(the default).
Common source classes are @Efl.Io.Buffer (if fixed data
exists in memory), @Efl.Io.Queue (used to stream
in-memory data) and @Efl.Io.File (data is present in the
file system). Networking classes are also common source
objects.
]]
get {
}
set {
[[Constructor-only property to set where to read data from]]
}
values {
source: Efl.Io.Reader; [[@Efl.Io.Reader source]]
}
}
@property destination {
[[Copier destination object.
If set it must implement @Efl.Io.Writer interface and
will receive read data once @Efl.Io.Writer.can_write
reports $true, this is monitored using
"can_write,changed" event.
The copier will attempt to write all internal
intermediate storage data at once, however the
destination object may consume less. The actual data
written is emitted in the "data" and "line" events.
If there is a @.line_delimiter set, then copier will use
that in order to attempt to write a full line at a time,
including the delimiter. Exceptions may be if the
@.source object emitted "eos" and there is no trailing
delimiter (ie: missing trailing newline in files), or the
@.buffer_limit was reached.
If @.source is flagged "eos" and all data was written to
destination, then "done" event is emitted.
If destination is not set (ie: NULL), then data is kept
in a internal @Eina.Binbuf, that can be stolen with
@.binbuf_steal once "data" or "line" events are
emitted. It is allowed as a shortcut to easily drain
readers and store all data in memory, not requiring an
@Efl.Io.Buffer or @Efl.Io.Copier to be used -- a source
and a copier are enough.
If both source and destination object implements
@Efl.Io.Sizer, then destination will be resized to match
the source size, providing the hint that may enhance
performance (like pre-allocating the total amount and
avoid dynamic resize).
If the destination object implements @Efl.Io.Closer and it's
not closed, it will be closed when the copier itself
is. This may happen, for example, when the copier is
deleted and @Efl.Io.Closer.close_on_destructor is $true
(the default).
Common destination classes are @Efl.Io.Buffer (better to
wait for all data in memory), @Efl.Io.Queue (to handle
streaming protocols) and @Efl.Io.File (stores data to
disk). Networking classes are also common destination
objects.
]]
get {
}
set {
[[Constructor-only property to set where to write data to]]
}
values {
destination: Efl.Io.Writer; [[@Efl.Io.Writer destination]]
}
}
@property line_delimiter {
[[If there is a line delimiter, the reads will buffer/queue up to the line delimiter before calling @Efl.Io.Writer.write on the @.destination and the event line is emitted with current line. The line may include the delimiter, unless it's end-of-stream on @.source or @.buffer_limit was reached.]]
get { }
set {
[[Changes line delimiter to use. If empty, no delimiter is to be used]]
}
values {
slice: const(Eina.Slice); [[The contents may contain \0 and will be copied]]
}
}
@property buffer_limit {
[[Copier buffer limit property, in bytes.
During the read-write cycle, an intermediate storage
buffer is used. By default it's zero -- unlimited, and
will grow as needed if @.source provides data and
@.destination do not consume it (or if there is no
@.destination).
However when reading data from untrusted sources, like
network, one can exhaust the system memory by flooding
the copier. In such cases, using a buffer limit is
recommended.
When the buffer limit is reached, the copier will pause
reading data from @.source until @.destination consumes
it. If there is no @.destination set, user should call
@.binbuf_steal to consume data and reset buffer usage.
Setting a buffer limit smaller than current
@.read_chunk_size will automatically change
@.read_chunk_size to the new buffer limit.
]]
get {
}
set {
[[Constructor-only property to set buffer limit. 0 is unlimited]]
}
values {
size: size; [[Defines a maximum buffer limit, or 0 to allow unlimited amount of bytes]]
}
}
@property read_chunk_size {
[[Copier read chunk size property, in bytes.
When a @.source is flagged with @Efl.Io.Reader.can_read
$true, data will be read using @Efl.Io.Reader.read into
an intermediate buffer of this size.
Setting this value large enough may reduce number of
@Efl.Io.Reader.read, improving performance at the expense
of more memory consumption.
This value is bounded by @.buffer_limit if it's set.
By default it's 4096.
]]
get {
}
set {
[[Set chunk size for each basic @Efl.Io.Reader.read operation.]]
}
values {
size: size; [[This is the chunk size to use for read operations]]
}
}
@property progress {
[[Progress for read and write.
Reports value read from @.source, written to
@.destination and the total, if the source implements
@Efl.Io.Sizer.
]]
get {
}
values {
read: uint64 @optional; [[Amount of bytes read from source]]
written: uint64 @optional; [[Amount of bytes written to destination]]
total: uint64 @optional; [[If @.source is an Efl.Io.Sizer, its total size. Otherwise 0 to report unknown size]]
}
}
@property timeout_inactivity {
[[Terminates the copier with ETIMEDOUT if it becomes inactive for some time.
If the copier cannot do any read or write in the given
amount of seconds, then the copier will emit "error"
event with ETIMEDOUT value.
This is specified in seconds and is only active for
greater-than zero. Defaults to inactive.
]]
values {
seconds: double; [[Number inactive seconds to timeout this copier. If zero or less, it will be disabled.]]
}
}
@property done {
[[Reports if copier is done.
A copier is done if source reached "eos" and all data
was written to "destination".
The copier is also done when it's @Efl.Io.Closer.closed.
]]
get { }
set @protected { }
values {
done: bool; [[If $true, source is "eos" and all data was written to "destination". If $false, it's still pending some more copies]]
}
}
binbuf_steal {
[[Steals the internal binbuf and return it to caller.
The buffer is then owned by caller, which should call
eina_binbuf_free() when it's done.
Usually call this method when no @.destination is set, in
this case you should wait for "done", "data" or "line"
events and then call it to retrieve (and own!) the data.
]]
return: ptr(Eina.Binbuf) @owned @warn_unused; [[Binbuf]]
}
@property pending_size {
[[Returns the size of the pending buffer.
The pending buffer is an intermediate buffer where data
is read from @.source before it's written to
@.destination, if any.
This is the equivalent of the size of returned by
@.binbuf_steal, however it doesn't steal or modify the
buffer at all, just query its internal size.
]]
get { }
values {
size: size; [[The pending buffer size, in bytes.]]
}
}
flush {
[[Forces reading from source and writing to destination.
This executes a single read->write cycle, if more data
could be read from source (ie: not EOS) or not all data
was written to destination, then $false is
returned. Then to forcefully drain source and write all
contents to destination, use in a loop until it returns
$true.
The return value matches "done" event, that is, when
$true is returned, the "done" event is emitted.
This function may also emit "progress" and "error"
events.
\@note this function may block the main loop execution
until operations complete! This is bad for usability, as
user interface or other operations may freeze. A better
approach is to operate asynchronously and wait for
"done" event.
]]
params {
may_block: bool; [[If $true, then @Efl.Io.Reader.can_read and @Efl.Io.Writer.can_write are not checked and the call may block.]]
ignore_line_delimiter: bool; [[Forces flush ignoring line delimiters]]
}
return: bool(true); [[$true on success, $false otherwise]]
}
}
events {
done; [[All available data was copied from source to destination]]
error: Eina.Error; [[An error happened and the copy stopped]]
progress; [[Total size changed or Data was read/written]]
data: ptr(const(Eina.Slice)); [[When data is read to internal buffer, it's emitted in this event. The memory is only valid during event callback dispatched and should not be modified.]]
line: ptr(const(Eina.Slice)); [[If @.line_delimiter is set, will be emitted with current line. The memory is only valid during event callback dispatched and should not be modified.]]
}
implements {
Efl.Object.constructor;
Efl.Object.destructor;
Efl.Object.finalize;
Efl.Io.Closer.close;
Efl.Io.Closer.closed { get; }
Efl.Io.Closer.close_on_exec { get; set; }
Efl.Io.Closer.close_on_destructor { get; set; }
}
}