import eina_types; class @beta Efl.Io.Copier extends Efl.Loop_Consumer implements 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 releasing them on destruction. By default the read-write process is done based on fixed-size chunks (@.read_chunk_size) but if @.line_delimiter is set, the behavior changes to wait for said delimiter or until 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 as data when it's read from @.source). - The "line" event is only emitted when @.line_delimiter is set and not empty. It's similar 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 is read from @.source). - The "error" event is reported if the @Efl.Io.Reader.read, @Efl.Io.Writer.write or some other internal error occurs, such as being out of memory. Another common error is ETIMEDOUT if @.timeout_inactivity is set. A copier is handy for simplifying common I/O use cases, such as: - Reading a file or download content to memory: provide only the source and wait for "done" event, calling @.binbuf_steal afterwards. - Downloading content to disk: provide a network socket as source and use @Efl.Io.File as destination, then wait for "done" event. - Linking 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 is 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_invalidate are respected and applied to both source and destination. Both default to $true. ]] 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 $true a chunk (limited by @.read_chunk_size) is read using @Efl.Io.Reader.read into an intermediate storage buffer which can grow up to the @.buffer_limit. If there's 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 (i.e: missing trailing newline in files) or if the @.buffer_limit is 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's 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/ It also offers hints to enhance performance such as 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_invalidate 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 the "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 an @.line_delimiter set then copier will use it in order to attempt to write one 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 is reached. If @.source is flagged "eos" and all data is written to the destination then the "done" event is emitted. If the destination is not set (ie: NULL) then data is kept in a internal @Eina.Binbuf, which can be stolen with @.binbuf_steal once "data" or "line" events are emitted. It exists as a useful shortcut to easily drain readers and store all data in memory with no need to use an @Efl.Io.Buffer or @Efl.Io.Copier -- a source and copier are enough. If both source and destination object implements @Efl.Io.Sizer, then the destination will be resized to match the source size. It also offers hints to enhance performance such as 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_invalidate 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, 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 the 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 system memory can be exhausted by flooding the copier. In these 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 perform any reads or writes 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 is 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 returns 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 which case you should wait for "done", "data" or "line" events and then call it to retrieve and own the data. ]] return: ptr(Eina.Binbuf) @move @no_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 queries 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 can be read from source (ie: not EOS) or not all data is written to destination, then $false is returned. To forcefully drain source and write all contents to destination, use in a loop until it returns $true. The return value matches the "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 are complete! This is bad for usability as user interface or other operations may freeze. A better approach is to operate asynchronously and wait for the "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: void; [[All available data was copied from source to destination]] error: Eina.Error; [[An error happened and the copy stopped]] progress: void; [[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.finalize; Efl.Object.invalidate; Efl.Object.destructor; Efl.Io.Closer.close; Efl.Io.Closer.closed { get; } Efl.Io.Closer.close_on_exec { get; set; } Efl.Io.Closer.close_on_invalidate { get; set; } } }