Hachoir internals
=================

When a field is really created?
-------------------------------

A field is created when someone ask to access it, or when another field is
asked and the field is before it. So if you use a field in your field set
constructor, one or more fields will be created. Example:

>>> from hachoir.field import Parser, Int8
>>> from hachoir.endian import BIG_ENDIAN
>>> class Point(Parser):
...     endian = BIG_ENDIAN
...     tags = {"description": "A point", "min_size": 4*8}
...     def __init__(self, stream):
...         Parser.__init__(self, stream)
...         if self["color"].value == -1:
...             self._description += " (no color)"
...
...     def createFields(self):
...         yield Int8(self, "color", "Point color (-1 for none)")
...         yield Int8(self, "use_3d", "Does it use Z axis?")
...         yield Int8(self, "x", "X axis value")
...         yield Int8(self, "y", "Y axis value")
...         if self["use_3d"] == 1:
...             yield Int8(self, "z", "Z axis value")
...

In the constructor, the field "color" is asked. So the field list will
contains one field (color):

>>> from hachoir.stream import StringInputStream
>>> stream = StringInputStream("\x2A\x00\x04\x05")
>>> p = Point(stream)
>>> p.current_length
1

.. WARNING::
   Only access 'fields' attribute if you know what you are doing.

If you access another field, the field list will grow up until the requested
field is reached:

>>> x = p["x"].value
>>> p.current_length
3

Some field set methods which create new fields:

* ``__getitem__()``: feed field list until requested field is reached
  (or raise MissingField exception) ;
* ``__len__()``: create all fields ;
* ``__iter__()``: begin to iterate in existing fields, and the iterate in new
  fields until all fields are created ;
* ``__contains__()``: feed field list until requested field is reached, may
  create all fields if the field doesn't exist.

The size attribute also interact with field list creation, but it's mechanism
is little bit more complex. By default, the whole field list have to be built
before size value can be read. But you can specify field list size:

* if field list is not dynamic (eg. don't depends on flag), use class
  attribute ``static_size`` ;
* otherwise you can set _size instance attribute in the constructor.

Two examples:

>>> from hachoir.field import Parser
>>> class FourBytes(Parser):
...     tags = {"description": "Example of fixed size", "min_size": 32}
...     endian = BIG_ENDIAN
...     static_size = 32
...     def createFields(self):
...         yield Integer(self, "four", "uint32")
...
>>> class DynamicSize(Parser):
...     tags = {"description": "Example with dynamic computed size", "min_size": 32}
...     endian = BIG_ENDIAN
...     def __init__(self, stream, nb_items):
...         Parser.__init__(self, stream)
...         assert 0 < nb_items
...         self.nb_items = nb_items
...         self._size = nb_items * 32   # 32 is the size of one item
...
...     def createFields(self):
...         for index in range(self.nb_items):
...             yield Integer(self, "item[]", "uint32")
...
>>> a = FourBytes(stream)
>>> b = DynamicSize(stream, 1)
>>> a.size, b.size
(32, 32)
>>> # Check that nothing is really read from stream
... a.current_length, b.current_length
(0, 0)

When the value of a field is read?
----------------------------------

When a field is created, the value of the field doesn't exist (equals to
None). The value is really read when you read the field value using 'value'
or 'display' field attributes. The value is then stored in the field.

Details about field name
------------------------

The name of a field have to be unique in a field set because it is used as
key in the field list. The argument 'name' of the Field constructor can be
changed in the the constructor, but should not (and can no) be changed after
that.

For arrays, you can use the 'magic' prefix « [] » (eg. "item[]") which will
be replaced by « [index] » where the number index is a counter starting a
zero.

Endian
------

The "endian" is the way in which ''bytes'' are stored. There are two important
orders:

* « Big endian » in which *most* significant byte (*big* number) are
  written first (PowerPC / Motorola CPUs). It's the order choosed in network ;
* « Little endian » in which *least* significant byte (*little* number)
  are written first (Intel x86 CPUs).

The number 0x1020 is stored "0x10 0x20" in big endian and "0x20 0x10" in little
endian.

The endian is global to a FieldSet and is a class attribute. Allowed values:
* BIG_ENDIAN (default) ;
* NETWORK_ENDIAN (alias of BIG_ENDIAN) ;
* LITTLE_ENDIAN ;
* PDP_ENDIAN (not suppported yet).

Example to set endian:

>>> from hachoir.endian import LITTLE_ENDIAN
>>> class UseLittleEndian(Parser):
...     endian = LITTLE_ENDIAN
...

Explore a field set using it's path
-----------------------------------

Fields are stored in a tree. To explore the tree you have differents tools:

* attribute *root* of a field which go to tree root ;
* attribute *parent* go to field parent (is None for tree root) ;
* and you can specify a path in *__getitem__()* argument.

There are differents valid syntax of a path:

* path to a child of current node: ``field["content"]`` ;
* path to a child of the parent: ``field["../brother"]`` ;
* path from the root: ``field["/header/key"]``.

Event handling
--------------

Code is not finished yet ...

Functions:

* ``FieldSet.connectEvent(event_name, handler)``: connect an handler to an
  event. It's possible to catch an event from any field set of the document,
  use optionnal argument local=False for that ;
* ``FieldSet.raiseEvent(event_name,  arg1, arg2, ...)``: raise an event.

Events:

* "field-value-changed": A field value has changed.
  Prototype: func(field) ;
* "field-resized": A field size has changed.
  Prototype: func(field) ;
* "field-replaced": A field has been replaced by a new one.
  Prototype: func(old_field, new_field).

Some other events are used internally:

* "set-field-value": Ask field set to set field value.
  Prototype: func(field, new_value).

