EFL IO Programming Guide

EFL offers some facilities for handling classic input/output (I/O) operations such as reading and writing files, Unix file descriptors and network sockets.

These facilities reside in the Efl.Io namespace and are built around a common set of interfaces. This guide lists these interfaces and their purpose. It also describes the available I/O classes and how to use them.

Network communication classes are built on top of Efl.Io and grouped in the Efl.Net namespace (Not documented in this guide).

Prerequisites

I/O Interfaces

The sub-sections below describe Eolian interfaces. You cannot instantiate them directly. Instead, they are implemented by the Efl.Io classes listed in Classes section. Through understanding these interfaces you will learn most of the methods exposed by the EFL I/O classes which implement them.

Efl.Io.Reader

Read the entire documentation in Efl.Io.Reader API Reference guide.

This interface provides a method to read (input) data into a pre-allocated memory buffer (an Eina_Rw_Slice). It also provides properties to know if more data is available and whether the end of the stream (EOS) has been reached. It also emits events for asynchronous notification of changes in these properties.

  • efl_io_reader_read() method: allocate an Eina_Rw_Slice of any size you wish and efl_io_reader_read() will fill it up to that size (maybe less). Whether this call blocks or not depends on the class implementing it. Read more about Eina Memory Slices.

  • efl_io_reader_can_read_get() property: when TRUE it guarantees that efl_io_reader_read() will not block.

  • efl_io_reader_eos_get() property: when TRUE the stream has ended and no more data is to be expected.

  • EFL_IO_READER_EVENT_CAN_READ_CHANGED event: the can_read property has changed.

  • EFL_IO_READER_EVENT_EOS event: the eos property has changed.

Efl.Io.Writer

Read the entire documentation in Efl.Io.Writer API Reference guide.

In juxtaposition to Efl.Io.Reader, this interface provides a method to write (output) data from a pre-allocated memory buffer (an Eina_Rw_Slice) and a property to know if the output is ready to receive more. It also emits an event for asynchronous notification of changes in this property.

  • efl_io_writer_write() method: You need to allocate an Eina_Rw_Slice of any size you want and efl_io_writer_write() will output up to that size (maybe less). Whether this call blocks or not depend on the class implementing it. Read more about Eina Memory Slices.

  • efl_io_writer_can_write_get() property: when TRUE, it guarantees that efl_io_writer_write() will not block.

  • EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED event: the can_write property has changed.

Efl.Io.Closer

Read the entire documentation in Efl.Io.Closer API Reference guide.

This interface provides a method to close a stream (be it an input or output stream) and convenient properties to close it automatically.

  • efl_io_closer_close() method: Closes the stream, deallocating all related resources.

  • efl_io_closer_close_on_destructor_get/set() property: when TRUE, the stream will be automatically closed when the object is destroyed.

  • efl_io_closer_close_on_exec_get/set() property: when TRUE, the stream will be automatically closed on exec() calls.

  • efl_io_closer_closed_get() property: whether the stream has been already closed or not.

  • EFL_IO_CLOSER_EVENT_CLOSED event: the stream has just been closed.

Efl.Io.Positioner

Read the entire documentation in Efl.Io.Positioner API Reference guide.

This interface provides methods, properties and events to track the current position inside a data stream.

  • efl_io_positioner_seek() method: moves the current position in the stream. Akin to the seek() C method.

  • efl_io_positioner_position_get() property: contains the current position the stream.

  • EFL_IO_POSITIONER_EVENT_POSITION_CHANGED event: the current position in the stream has changed.

Efl.Io.Sizer

Read the entire documentation in Efl.Io.Sizer API Reference guide.

This interface provides methods, properties and events to track the size of a data stream.

  • efl_io_sizer_resize() method: changes the size of the stream. This is useful for files, for instance.

  • efl_io_sizer_size_get() property: returns the current size of the stream.

  • EFL_IO_SIZER_EVENT_SIZE_CHANGED event: the size of the stream has changed.

Classes

This sections details a set of classes which you can instantiate to perform miscellaneous I/O tasks and the interfaces they implement. You can use the interface methods listed above on the classes that support them. The most relevant class-specific methods are given below but for a complete list of methods use the API Reference guide. A direct link to the appropriate page is given for each class.

Efl.Io.File

Read the entire documentation in Efl.Io.File API Reference guide. It implements the Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Closer, Efl.Io.Positioner and Efl.Io.Sizer interfaces.

You can use this object in a similar way as you would a plain FILE * in C in that you create it, give it a filename, perform some read or write operations and then destroy the object.

Here's an example from the EFL examples repository: reference/c/core/src/core_io.c

   Eina_Slice content = EINA_SLICE_STR("### This is a sample string for the file io test ###");
   Efl_Io_File *file;                        
 
   file = efl_add_ref(EFL_IO_FILE_CLASS, NULL,
                      efl_file_set(efl_added, filename, NULL),
                      efl_io_file_flags_set(efl_added, O_WRONLY | O_CREAT),
                      efl_io_file_mode_set(efl_added, 0644),
                      efl_io_closer_close_on_destructor_set(efl_added, EINA_TRUE));
 
   if (efl_io_writer_write(file, &content, NULL) != EINA_ERROR_NO_ERROR)
     fprintf(stderr, "  Failed to write test file\n");
   else          
     {
        char *string = eina_slice_strdup(content);
        printf("  Wrote content: %s\n", string);
        free(string);
     }
 
   efl_unref(file);

Efl.Io.Queue

Read the entire documentation in Efl.Io.Queue API Reference guide. It implements the Efl.Io.Reader, Efl.Io.Writer and Efl.Io.Closer interfaces.

This is a first-in first-out (FIFO) memory queue where data can be enqueued using the Efl.Io.Writer interface and dequeued using the Efl.Io.Reader interface.

It's convenient, for example, to decouple two processes as reads and writes can happen independently. The queue automatically shrinks and grows to adapt to its content.

Its maximum size can be bounded through the limit property. Any attempted writes to a full queue or reads from an empty queue will fail.

The most representative methods of the Efl.Io.Queue are:

  • efl_io_queue_clear() empties the queue.

  • efl_io_queue_discard() discards the given number of bytes.

  • efl_io_queue_eos_mark() marks the end-of-stream. Future writes will fail and the reader process will receive the EOS message once the queue is empty.

  • efl_io_queue_limit_get/set() sets the upper bound for the size of the queue.

  • efl_io_queue_usage_get() retrieves the number of bytes currently in the queue.

Example:

Eo *send_queue;
send_queue = efl_add_ref(EFL_IO_QUEUE_CLASS, NULL,
                         efl_name_set(efl_added, "send_queue"),
                         efl_io_queue_limit_set(efl_added, buffer_limit));
efl_io_writer_write(send_queue, &slice, NULL);
efl_io_queue_eos_mark(send_queue);
[...]
efl_io_reader_read(send_queue, &slice);
efl_io_closer_close(send_queue);
efl_unref(send_queue);

More usage examples can be found in the EFL examples repository: reference/c/net/src/net_io.c

Efl.Io.Buffer

Read the entire documentation in the Efl.Io.Buffer API Reference guide. It implements the Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Closer, Efl.Io.Positioner and Efl.Io.Sizer interfaces.

Buffers are similar to Queues in that they act as in-memory storage for data but they allow independent handling of the read and write pointers. This effectively provides complete access to the whole memory block.

If only the Efl.Io.Reader and Efl.Io.Writer interfaces are used, it acts as a FIFO exactly like Efl.Io.Queue. Random access to any portion of the memory block can be gained however through the position_read and position_write properties, which move the read and write pointers independently.

Its maximum size can be set through the limit property. Attempted writes to a full queue or reads from an empty queue will fail.

The most representative methods of the Efl.Io.Buffer are:

  • efl_io_buffer_position_read_get/set() sets the position to read from.

  • efl_io_buffer_position_write_get/set() sets the position to write to.

  • efl_io_buffer_limit_get/set() sets the upper bound for the size of the buffer.

Efl.Io.Copier

Read the entire documentation in the Efl.Io.Copier API Reference guide. It implements the Efl.Io.Closer interface.

This is a convenient object which copies data asynchronously from any source object implementing the Efl.Io.Reader interface into any destination object implementing the Efl.Io.Writer interface until the End-of-Stream event is received or an error occurs.

Usage scenarios include downloading a URL to memory or to a file, copying files around the hard drive or copying data from/to any file descriptor.

To use the Efl.Io.Copier class you only need to instantiate it. Next set the source and destination properties then register to receive the EVENT_DONE or EVENT_ERROR callbacks.

Data can be copied in blocks of fixed size (controlled through the read_chunk_size property) or of variable size, using a delimiter (configured through the line_delimiter property).

The most representative methods of Efl.Io.Copier are:

  • efl_io_copier_source_get/set() sets the source object which must implement the Efl.Io.Reader interface.

  • efl_io_copier_destination_get/set() sets the destination object which must implement the Efl.Io.Writer interface.

  • efl_io_copier_done_get() returns TRUE when the copy has finished. Use the EFL_IO_COPIER_EVENT_DONE event for asynchronous operation.

  • efl_io_copier_line_delimiter_get/set() sets the delimiter to use.

  • efl_io_copier_progress_get() returns the amount of bytes read and written and the total, if known. Use the EFL_IO_COPIER_EVENT_PROGRESS for asynchronous operation.

  • efl_io_copier_read_chunk_size_get/set() sets the amount of bytes to fetch in each read operation, if no delimiter is being used.

  • efl_io_copier_timeout_inactivity_get/set() sets the amount of seconds of inactivity to wait before aborting the operation with a timeout error.

  • EFL_IO_COPIER_EVENT_DATA is emitted every time new data is read.

  • EFL_IO_COPIER_EVENT_DONE is emitted when all data has been copied from source to destination.

  • EFL_IO_COPIER_EVENT_ERROR is emitted when an error occurs.

  • EFL_IO_COPIER_EVENT_LINE is emitted when a line (block of data separated by the configured delimiter) is read.

  • EFL_IO_COPIER_EVENT_PROGRESS is emitted when the amount of bytes read, written or total changes.

Here's an usage example extracted from the EFL examples repository reference/c/core/src/core_io.c. It copies data from stdin to stdout until Ctrl+D is pressed:

EFL_CALLBACKS_ARRAY_DEFINE(copier_cbs,
                           { EFL_IO_COPIER_EVENT_DONE, _copier_done },
                           { EFL_IO_COPIER_EVENT_ERROR, _copier_error });
 
   [...]
 
   Eo *input, *output;
   Eo *copier = NULL;
 
   printf("TEST 2: Efl.Io.Copier\n");
   // set up our objects to copy, use stdin and stdout
   input = efl_add(EFL_IO_STDIN_CLASS, loop);
   output = efl_add(EFL_IO_STDOUT_CLASS, loop);
 
   // copier: set up a copy from input to output
   copier = efl_add(EFL_IO_COPIER_CLASS, loop,
                    efl_name_set(efl_added, "Copier"),
                    efl_io_copier_source_set(efl_added, input),
                    efl_io_copier_destination_set(efl_added, output),
                    efl_event_callback_array_add(efl_added, copier_cbs(), NULL));

To discover how different events are registered through a single callback array read the Events Programming Guide.

A more complex usage example can be found in the EFL examples repository reference/c/net/src/net_io.c. It sends commands to an HTTP server using an Efl.Io.Copier which reads from an Efl.Io.Queue. It then receives answers using a second Efl.Io.Copier and another Efl.Io.Queue.

Efl.Io.Buffered_Stream

Read the entire documentation in the Efl.Io.Buffered_Stream API Reference guide. It implements the Efl.Io.Reader, Efl.Io.Writer and Efl.Io.Closer interfaces.

This is a convenient object which adds buffered input and output to an existing object (called the inner object) implementing the Efl.Io.Reader and/or Efl.Io.Writer interfaces.

The purpose of Efl.Io.Buffered_Stream is to overcome the following limitations of these interfaces: The basic writer interface might write less data than actually provided and the user is then responsible for retrying later on when the interface is ready. Similarly the basic reader interface might provide less data than requested and the user must wait and retry until all data is read.

Efl.Io.Buffered_Stream uses internal Queues and Copier objects so that you can simply "write and forget". You only need to write all your data in one operation and the Buffered_Stream object will take care of retrying and guarantee that all your data is sent. You can also simply "read when ready": Incoming data will be automatically accumulated and you can perform your read when enough has been received.

To achieve this the writer interface of the Buffered_Stream object is connected to an output Efl.Io.Queue and an Efl.Io.Copier connects this queue to the writer interface of the inner object. Simultaneously, another Efl.Io.Copier connects the reader interface of the inner object to an input Efl.Io.Queue which is accessible through the Buffered_Stream reader interface.

The most commonly used methods of Efl.Io.Buffered_Stream are:

  • efl_io_buffered_stream_inner_io_get/set() sets the inner object to wrap. You must set it at least once for the Buffered_Stream object to work.

  • efl_io_buffered_stream_clear() clears all data from the input queue.

  • efl_io_buffered_stream_discard() discards the given number of bytes from the input queue.

  • efl_io_buffered_stream_eos_mark() marks the end-of-stream. Future writes will fail and EOS marker will be sent once the output queue becomes empty.

  • efl_io_buffered_stream_read_chunk_size_get() and efl_io_buffered_stream_line_delimiter_get/set() control whether data is read in blocks of fixed or variable size (bounded by this delimiter). See Efl.Io.Copier for more information.

  • efl_io_buffered_stream_max_queue_size_input_get/set() and efl_io_buffered_stream_max_queue_size_output_get/set() set the maximum number of bytes to store in the input and output queues, respectively.

  • efl_io_buffered_stream_progress_get() returns the amount of bytes written to and read from the inner object.

  • efl_io_buffered_stream_pending_read_get() and efl_io_buffered_stream_pending_write_get() return the amount of bytes in the input and output queues respectively.

  • EFL_IO_BUFFERED_STREAM_EVENT_ERROR is emitted when an error occurred.

  • EFL_IO_BUFFERED_STREAM_EVENT_FINISHED is emitted when all pending read and write operations are finished.

  • EFL_IO_BUFFERED_STREAM_EVENT_LINE is emitted when a line is received (bounded by the configured delimiter).

  • EFL_IO_BUFFERED_STREAM_EVENT_PROGRESS is emitted when the progress property has changed. You can also read the pending_write or pending_read properties to check the status of these operations.

  • EFL_IO_BUFFERED_STREAM_EVENT_READ_FINISHED is emitted when the end-of-stream marker is received from the inner object, indicating that no more data is to be expected.

  • EFL_IO_BUFFERED_STREAM_EVENT_WRITE_FINISHED is emitted when efl_io_buffered_stream_eos_mark() has been used and all data has already been written into the inner object.

Efl.Io.Stdin, Efl.Io.Stdout and Efl.Io.Stderr

EFL provides wrappers around the standard input, output and error file descriptors available on most operating systems. Just instantiate an EFL_IO_STDIN_CLASS or EFL_IO_STDERR_CLASS and you can output to the console using the Efl.Io.Writer interface. To read from the console instantiate an EFL_IO_STDOUT_CLASS and use the Efl.Io.Reader interface on it.

See the Efl.Io.Copier section above for an usage example.

Further Reading

Introduction to Eo
Explains how to create and destroy EFL objects.
Multiple Inheritance with Eolian
Explains what are Eolian interfaces.
Events Programming Guide
Explains how events and event callbacks are handled in EFL.
EFL API Reference guide
Detailed documentation on the EFL API.