Pseudo-code

1. DocumentType to Document

2. Document to DocumentType

3. Document decoder

4. Document encoder

FUNCTION Document readDocument(Input in, DocumentType docType)

    Document doc = Document.new()

    FOR EACH field IN docType.fields THEN

        # read number of occurences
        Int nbOcc = readNbOccurence()

        IF nbOcc != 0 THEN

            # read field value doc type if undefined
            DocumentType fieldDocType = field.docType
            IF field.type = 'document' AND field.docType = null THEN
                Document encodedDocType = readDocument(in, SCHEMA_DOCTYPE)
                fieldDocType = toDocumentType(encodedDocType)
            END IF

            # read field values
            IF nbOcc = 1 AND field.maxOcc = 1 THEN
                # read a single value

                doc.setFieldValue(field.id, readValue(in, field))

            ELSE IF nbOcc = -1 THEN
                # read a streamed collection

                List values = List.new()                
                WHILE in.readByte() != 0
                    values.add(readValue(in, field))
                END WHILE

                doc.setFieldValue(field.id, values)
            ELSE
                # read a defined size collection

                List values = List.new(nbOcc)
                WHILE in.readByte() != 0
                    values.add(readValue(in, field))
                END WHILE

                doc.setFieldValue(field.id, values)
            END IF

        END IF

    END FOR

    RETURN doc

END FUNCTION



FUNCTION Int readNbOccurence(Input in, Field field)

    Int nbOcc = field.max_occ
    IF field.minOcc = 0 AND field.maxOcc = 1 THEN
        IF in.readBit() == 0 THEN
            nbOcc = 0
        ELSE
            nbOcc = 1
        END IF
    ELSE IF field.minOcc != field.maxOcc THEN
        Int n = in.readVarUInt()
        IF n = 0 THEN
            nbOcc = -1
        ELSE
            nbOcc = field.minOcc + n - 1
        END IF
    END IF

    RETURN nbOcc

END FUNCTION



FUNCTION Object readValue(Input in, Field field)

    # read array size
    Int[] arraySize = field.arraySize
    IF arraySize != null THEN
        FOR i = 0 TO arraySize.length
            IF arraySize[i] <=0
                arraySize[i] = in.readVarUInt()
            END IF
        END FOR
    END IF

    # field values
    IF arraySize != null THEN
        RETURN readArrayValues(in, field, arraySize, 0)
    ELSE
        RETURN readSingleValue(in, field)
    END IF

END FUNCTION


FUNCTION Object readArrayValues(Input in, Field field, Int[] arraySize, int depth)

    Object[] values = new Object[array[depth]]

    FOR i = 0 TO values.length
        IF depth = array.length-1 THEN
            values[i] = readSingleValue(in, valueType)
        ELSE
            values[i] = readArrayValues(in, valueType, arraySize, depth+1)
        END IF
    END FOR

    RETURN values

END FUNCTION


FUNCTION Object readSingleValue(Input in, Field field)

    SWITCH field.type
        CASE 'Bit'       : RETURN in.readBit() != 0
        CASE 'ByteBool'  : RETURN in.readByte() != 0
        CASE 'Int8'      : RETURN in.readByte()
        CASE 'UInt8'     : RETURN in.readUByte()
        CASE 'Int16_BE'  : RETURN in.readShortBE()
        CASE 'UInt16_BE' : RETURN in.readUShortBE()
        CASE 'Int32_BE'  : RETURN in.readIntBE()
        CASE 'UInt32_BE' : RETURN in.readUIntBE()
        CASE 'Int64_BE'  : RETURN in.readLongBE()
        CASE 'UInt64_BE' : RETURN in.readULongBE()
        CASE 'Float_BE'  : RETURN in.readFloatBE()
        CASE 'Double_BE' : RETURN in.readDoubleBE()
        CASE 'Int16_LE'  : RETURN in.readShortLE()
        CASE 'UInt16_LE' : RETURN in.readUShortLE()
        CASE 'Int32_LE'  : RETURN in.readIntLE()
        CASE 'UInt32_LE' : RETURN in.readUIntLE()
        CASE 'Int64_LE'  : RETURN in.readLongLE()
        CASE 'UInt64_LE' : RETURN in.readULongLE()
        CASE 'Float_LE'  : RETURN in.readFloatLE()
        CASE 'Double_LE' : RETURN in.readDoubleLE()
        CASE 'VarUInt'   : RETURN in.readVarUInt();
        CASE 'VarBits'   : RETURN in.readBits(field.nbBits);
        CASE 'Text'      : RETURN Chars.new(in.readBytes(in.readVarUInt()), field.charEncoding)
        CASE 'Document'  : RETURN readDocument(in, field.docType, field.inline)
        CASE 'Variable'  : 
            Document typeDoc = readDocument(in, SCHEMA_FIELDVALUETYPE)
            FieldValueType type = toFieldValueType(typeDoc)
            RETURN readValue(in, type)
    END SWITCH

END FUNCTION