IO Interface And Operations
We need IO abstraction layer for porting and extension.
See Vnode/VFS interface (vnode(9)) on IO abstraction.
struct vnode {
...
voff_t v_size; /* size of file */
int v_numoutput; /* num pending writes */
long v_writecount; /* ref count of writers */
...
int (**v_op)(void *); /* vnode ops vector */
...
void *v_data; /* private data for fs */
};
vop_open(),
vop_read(),
vop_getattr(), ...
are defined over v_op.
**v_op (vnode operation vector) corresponds to a method of object
oriented programming. &fml8; provides IO::Adapter class as IO
abstraction layer.
Fundamentals Of IO::Adapter
IO::Adapter
is most fundamental in &fml8; architecture.
It is well considered and implemented.
It provides enough primitive methods.
IO::Adapter
class abstracts
KEY => VALUE
or
KEY => [ VALUE, VALUE2, VALUE3 ]
type data structure.
It is similar to RDBMS theory like this.
KEY1 VALUE1-1 "" ""
KEY2 VALUE2-1 VALUE2-2 VALUE2-3
KEY3 VALUE3-1 VALUE3-2 VALUE3-3
KEY4 VALUE4-1 VALUE4-2 VALUE4-3
To maintain address list, least fundamental methods of IO::Adapter are
open()
close()
and IO operations to the object
add(KEY, ARGV) (ARGV is class dependent)
delete(KEY)
find(KEY or REGEXP)
get_next_key()
methods.
At least, enough to write user management codes.
Methods / Operation Vector
Mentioned above, the fundamental methods of IO::Adapter are as
follows:
open()
close()
add(KEY, ARGV) (ARGV = class dependent)
delete(KEY)
find(KEY or REGEXP)
get_next_key()
Other than these methods, new() as a constructor and destructor() are
needed.
The constructor is new() methond.
For example, use like this;
$obj = new IO::Adapter MAP;
The argument is a map which calls map dependent constructor.
open()
For a file, call open(2), for RDBMS, connect to the specified SQL
server.
close()
Reverse of open().
add(KEY, ARGV)
add KEY (primary key) or KEY and the associated VALUE into the object.
ARGV is class dependent.
We assume an object is composed of a form.
It is similar to RDBMS.
The primary key is a mail address. This assumption is correct in the
case of mailing list driver. This is basic data structure.
delete(KEY)
Delete KEY and the associated VALUE.
find(KEY) / find(REGEXP)
Search data corresponding with the primary key within an object.
The target is specified as regular expression.
Regular expression search is useful.
The return value is STR of ARRAY_REF (KEY => [ VALUE, VALUE2, VALUE3 ]).
get_next_key()
Return the list of primary keys like this:
while ($obj->get_next_key()) { ... }
This operation corresponds to FIRST_KEY() and NEXT_KEY() of perl hash.
Discussion
What ARRAY_REF return ?
What expected in using ARRAY_REF as the return value ?
A list of PRIMARY KEY ?
How To Get All Primary Keys ?
We need to call get_next_keys() again and again to retrieve all
keys. It may be useful to implement special method to return a list of
keys but it is not implemented.
get_primary_keys() ?
find('*', { all => 1 }) is used for this purpose.
It is of no use to implement specific method for this.
HASH_REF As Return Value ?
What expected ?
RETURN VALUE = {
KEY_1 => VALUE_1,
KEY_2 => VALUE_2,
}
If a mail address has several attributes, this method is useful.
For example, consider digest delivery:
ADDRESS => {
internal => 3 hours,
compression => no,
format => mime/multipart,
};
Abstracted Cache IO layer is this type such as:
FML::Error -> FML::Error::Cache -> Tie::JournaledDir
But ... dependent on Tie::* hmm ..