/*
 *                         OpenSplice DDS
 *
 *   This software and documentation are Copyright 2006 to TO_YEAR PrismTech
 *   Limited, its affiliated companies and licensors. All rights reserved.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */
/** \file services/serialization/code/sd_serializerBigE.c
 *  \brief Implementation of the \b serializerBigE class
 *
 *  The BigE networking format is a platform independent format
 *  where datafields are represented in the big endian format.
 *  No padding is inserted, so the resulting serializedData object
 *  can be interpreted as a stream of bytes. This stream can
 *  be used for networking, but also for persistent storage.
 *
 * ---------------------- Serialization format ---------------------------
 *
 *  Below, the exact definition for version 0.1 of the BigE format is given.
 *  Here, MSB means Most Significant Byte and LSB means Least Significant Byte
 *
\verbatim

  ....2...........8..............16..............24..............32
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |      'S'      |      '2'      |       0       |       1       |
  +---------------+---------------+---------------+---------------+
  | MSB                        dataSize                       LSB |
  +---------------+---------------+---------------+---------------+
  |      'S'      |      'c'      |      'o'      |      'p'      |
  +---------------+---------------+---------------+---------------+
  |      'e'      |      'n'      |      'a'      |      'm'      |
  +---------------+---------------+---------------+---------------+
  |      'e'      |      ':'      |      ':'      |      'T'      |
  +---------------+---------------+---------------+---------------+
  |      'y'      |      'p'      |      'e'      |      'n'      |
  +---------------+---------------+---------------+---------------+
  |      'a'      |      'm'      |      'e'      |       0       |
  +---------------+---------------+---------------+---------------+
  |                                                               |
  |      Serialized data       .                                  |
  |                            .                                  |
  |                            .                                  |
  |                                                               |
  +---------------+---------------+---------------+---------------+\endverbatim
 *  The first four bytes represent the format ID and version number of
 *  the serializer used. The next four bytes are a big endian representation
 *  of the number of bytes of the serialization data. This is followed by a
 *  zero terminated string containing the scoped name of the type of the
 *  serialized object. This is a variable length part. Then the stream of
 *  databytes starts, which has a variable length as well.
 *
 *  <b>Primitive types</b> are equal to the big endian representation
 *  of the corresponding IDL types.
 *
\verbatim

  c_char, c_octet

  ....2...........8
  +-+-+-+-+-+-+-+-+
  |7|6|5|4|3|2|1|0|
  +---------------+\endverbatim

\verbatim

  c_bool

  TRUE
  ....2...........8
  +-+-+-+-+-+-+-+-+
  |0|0|0|0|0|0|0|1|
  +---------------+

  FALSE
  ....2...........8
  +-+-+-+-+-+-+-+-+
  |0|0|0|0|0|0|0|0|
  +---------------+\endverbatim

\verbatim

  c_short, c_ushort

  ....2...........8..............16
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |MSB            |            LSB|
  +---------------+---------------+\endverbatim

\verbatim

  c_long, c_ulong

  ....2...........8..............16..............24..............32
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |MSB            |               |               |            LSB|
  +---------------+---------------+---------------+---------------+\endverbatim

\verbatim

  c_longlong, c_ulonglong

  ....2...........8..............16..............24..............32
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |MSB            |               |               |               |
  +---------------+---------------+---------------+---------------+
  |               |               |               |            LSB|
  +---------------+---------------+---------------+---------------+\endverbatim

For floats and doubles, the standard IEEE big endian format is used. S indicates
the sign-bit, E indicates the mantissa and F indicates the float-value.
\verbatim

  float

  ....2...........8..............16..............24..............32
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |S|     E1      |E|     F1      |       F3      |       F3      |
  +---------------+---------------+---------------+---------------+\endverbatim

\verbatim

  double

  ....2...........8..............16..............24..............32
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |S|     E1      |   E2  |  F1   |       F2      |       F3      |
  +---------------+---------------+---------------+---------------+
  |      F4       |       F5      |       F6      |       F7      |
  +---------------+---------------+---------------+---------------+\endverbatim

 *  <b>Enumeration types</b> have the same representation as the
 *  \b c_long type.
\verbatim

  enumeration

  ....2...........8..............16..............24..............32
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |MSB                        Enum value                       LSB|
  +---------------+---------------+---------------+---------------+\endverbatim
 *
 * The representation of <b>Collection types</b> is different for
 * fixed length and variable length arrays. Fixed length arrays of
 * size <b>n</b> are formatted like <b>n</b> sequential elements.
 * Variable sized arrays are formatted like <b>n</b> sequential
 * elements with the integer value of <b>n</b> prepended. Since such
 * an array can be a NULL-pointer, an extra TRUE/FALSE byte is
 * prepended first. FALSE, which is represented by a zero, indicates
 * a NULL pointer. TRUE, which is represented by a one, indicates a
 * valid array.
\verbatim

  Set or variable length array

  ....2...........8..............16..............24..............32
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                                  | TRUE / FALSE  |
  +---------------+---------------+---------------+---------------+
  |MSB             Set size or array length n                  LSB|
  +---------------+---------------+---------------+---------------+
  |                                                               |
  |      Element 1             .                                  |
  |                            .                                  |
  |                            .                                  |
  |                                                               |
  +---------------+---------------+---------------+---------------+
  |                                                               |
  |      Element 2             .                                  |
  |                            .                                  |
  |                            .                                  |
  |                                                               |
  +---------------+---------------+---------------+---------------+
  |                                                               |
  |                            .                                  |
  |                            .                                  |
  |                            .                                  |
  |                                                               |
  +---------------+---------------+---------------+---------------+
  |                                                               |
  |      Element n             .                                  |
  |                            .                                  |
  |                            .                                  |
  |                                                               |
  +---------------+---------------+---------------+---------------+\endverbatim

\verbatim

   Fixed length array with length n

  ....2...........8..............16..............24..............32
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                                                               |
  |      Element 1             .                                  |
  |                            .                                  |
  |                            .                                  |
  |                                                               |
  +---------------+---------------+---------------+---------------+
  |                                                               |
  |      Element 2             .                                  |
  |                            .                                  |
  |                            .                                  |
  |                                                               |
  +---------------+---------------+---------------+---------------+
  |                                                               |
  |                            .                                  |
  |                            .                                  |
  |                            .                                  |
  |                                                               |
  +---------------+---------------+---------------+---------------+
  |                                                               |
  |      Element n             .                                  |
  |                            .                                  |
  |                            .                                  |
  |                                                               |
  +---------------+---------------+---------------+---------------+\endverbatim
 * \b Strings are incorporated as a zero-terminated concatenation of characters.
 * Like the variable sized array, a string can be NULL. This is
 * indicated by the leading TRUE/FALSE byte.
\verbatim

  String of length n
  ....2...........8..............16..............24..............32
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  | TRUE / FALSE  |      c_1      |      c_2      |      c_3      |
  +---------------+---------------+---------------+---------------+
  |             . . .             |      c_n      |       0       |
  +---------------+---------------+---------------+---------------+\endverbatim
 *
 *  <b>Union types</b> are represented as a concatenation of the switch
 *  element and the active case element.
 *
 *  <b>Structure types</b> are represented as a concatenation of their
 *  individual members.
 *
 *
 * ---------------------- General mechanism ---------------------------
 *
 * Both serialization and deserialization make use of the deepwalk function.
 * This functions interprets metadata information and with this information
 * it walks recursively over all elements in a class. During this walk,
 * a callback function is called. This callback function will dispatch to a
 * type-specific function which executes the actual (de)serialization. These
 * functions are the core of the algorithm. They return a c_ulong value
 * indicating how many bytes have been (de)serialized.
 */

/* interface */
#include "sd__serializer.h"
#include "sd_serializerBigE.h"

/* implementation */
#include "os_abstract.h"
#include "os_stdlib.h"
#include "os_heap.h"
#include "c_metabase.h"
#include "c_collection.h"
#include "sd__byteswapping.h"
#include "sd__confidence.h"
#include "sd_deepwalk.h"
#include "sd_misc.h"

#define SD_FORMAT_ID              (0x5332U)    /* "S2" */
#define SD_FORMAT_VERSION         (0x0001U)
#define SD_FORMAT_ID_STRING       "BigEndian"
#define SD_FORMAT_VERSION_STRING  "v0.1"

#define SD_FORMAT_USER_BYTE       ((unsigned char)0)
#define SD_FORMAT_INTERNAL_BYTE   ((unsigned char)1)
#define SD_INTERNAL_INDICATOR_SIZE (1)


C_CLASS(sd_serializerBigEInternal);

C_STRUCT(sd_serializerBigEInternal) {
    C_EXTENDS(sd_serializer);
    c_type internalType;
};

#define sd_serializerBigEInternal(o) ((sd_serializerBigEInternal)(o))

#ifndef NDEBUG
/* -------------------------- checking routines -------------------------- */

/** \brief Private function for typechecking the current serializer. This avoids
 * deserialization of incompatible formats
 */
static c_bool
sd_checkSerializerType(
    sd_serializer serializer)
{
    return ((c_bool)
            ((unsigned int)serializer->formatID == SD_FORMAT_ID) &&
            ((unsigned int)serializer->formatVersion == SD_FORMAT_VERSION));
}
#endif


/* ----------------------- serialization routines ----------------------- */

#define sd_isValidReference(_this) ((c_bool)(_this && (*(c_object *)_this)))

/** \brief Function for serializing any collection type. Currently supported
 * types are string, array, sequence and set.
 */
static os_size_t
sd_bigESerCollection(
    c_collectionType collectionType,
    c_object object,
    c_octet *dataPtr)
{
    c_ulong colSize;
    os_size_t len;
    c_octet *dataPtrHelper;

    /* Different behaviour for reftypes and non-reftypes */
    if (((collectionType->kind == OSPL_C_ARRAY) ||
         (collectionType->kind == OSPL_C_SEQUENCE)) &&
         !c_typeIsRef(c_type(collectionType))) {

        len = 0U;

    } else {

        /* first serialize the validity of the object. We have to do this in
         * all situations, whether the reference is valid or not */
        *dataPtr = sd_isValidReference(object);
        len = 1;

        /* Now check if this was a valid reference. In that case, we have to
         * handle the data itself as well */
        if (sd_isValidReference(object)) {

            /* Skip to the data itself */
            dataPtrHelper = C_DISPLACE(dataPtr, len);

            /* Only serialize the collection size in case of list/set/bag/etc */
            switch (collectionType->kind) {
            case OSPL_C_STRING:
                {
                    c_char *src = *(c_char **)object;
                    c_char *dst = (c_char *)dataPtrHelper;
                    if ((src != NULL) && (dst != NULL)) {
                        while (*src != (c_char)0) {
                            *dst++ = *src++;
                            len++;
                        }
                        *dst = (c_char)0;
                        len++;
                    }
                }
            break;
            case OSPL_C_ARRAY:
            case OSPL_C_SEQUENCE:
                SD_CONFIDENCE(c_typeIsRef(c_type(collectionType)));
                /* Serialize the size */
                colSize = c_arraySize(*((c_array *)(object)));
                len += 4;
                SD_COPY2BIG_4(dataPtrHelper,&colSize);
            break;
            case OSPL_C_SET:
                /* Serialize the size */
                colSize = c_setCount(*(c_collection *)object);
                len += 4;
                SD_COPY2BIG_4(dataPtrHelper,&colSize);
            break;
            case OSPL_C_LIST:
            case OSPL_C_BAG:
            case OSPL_C_DICTIONARY:
            case OSPL_C_QUERY:
            default:
                SD_CONFIDENCE(FALSE); /* No other collection types supported */
            break;
            }
        }
    }
    return len;
}

/** \brief Callback function for deepwalk when serializing */
static c_bool
sd_bigESerCallback(
    c_type type,
    c_object *objectPtr,
    void *actionArg)
{
    c_octet **dataPtrPtr = (c_octet **)actionArg;
    os_size_t len;

    switch (c_baseObject(type)->kind) {
    case M_COLLECTION:
        len = sd_bigESerCollection(c_collectionType(type), *objectPtr, *dataPtrPtr);
    break;
    case M_PRIMITIVE:
        SD_COPY2BIG_N(*dataPtrPtr,*objectPtr,type->size);
        len = type->size;
    break;
    case M_ENUMERATION:
        SD_COPY2BIG_4(*dataPtrPtr,*objectPtr);
        len = 4;
    break;
    case M_INTERFACE:
    case M_CLASS:
        **dataPtrPtr = sd_isValidReference(*objectPtr);
        len = 1;
    break;
    default:
        SD_CONFIDENCE(FALSE); /* No other expected than these */
        len = 0;
    break;
    }
    *dataPtrPtr = C_DISPLACE(*dataPtrPtr, C_ADDRESS(len));
    return TRUE;
}


/* ------------------- counting the number of bytes needed -------------------*/


/** \brief Callback function for deepwalk when determining the number of
 *         bytes needed
 */
static c_bool
sd_bigECountCallback(
    c_type type,
    c_object *objectPtr,
    void *actionArg)
{
    os_size_t *size = actionArg;

    switch (c_baseObject(type)->kind) {
    case M_COLLECTION:
        switch (c_collectionType(type)->kind) {
        case OSPL_C_STRING:
            /* A boolean indicating if the reference is valid */
            *size += sizeof(c_bool);
            if (sd_isValidReference(*objectPtr)) {
                 *size += strlen(*(c_string *)(*objectPtr)) + 1U /* '\0' */;
            }
        break;
        case OSPL_C_ARRAY:
        case OSPL_C_SEQUENCE:
            if (c_typeIsRef(type)) {
                /* A boolean indicating if the reference is valid */
                *size += sizeof(c_bool);
                if (sd_isValidReference(*objectPtr)) {
                    /* contains size of stretchy array */
                    *size += sizeof(c_ulong);
                }
            }
        break;
        case OSPL_C_LIST:
        case OSPL_C_SET:
        case OSPL_C_BAG:
        case OSPL_C_DICTIONARY:
        case OSPL_C_QUERY:
            /* A boolean indicating if the reference is valid */
            *size += sizeof(c_bool);
            if (sd_isValidReference(*objectPtr)) {
                /* contains size of the collection */
                *size += sizeof(c_ulong);
            }
        break;
        default:
            /* Not yet implemented */
            SD_CONFIDENCE(FALSE);
        break;
        }
    break;
    case M_PRIMITIVE:
    case M_ENUMERATION:
        *size += type->size;
    break;
    case M_CLASS:
    case M_INTERFACE:
        /* A boolean indicating if the reference is valid */
        *size += sizeof(c_bool);
    break;
    default:
        SD_CONFIDENCE(FALSE);
    break;
    }
    return TRUE;
/* QAC EXPECT 5101; cyclomatic complexity is no problem here */
}


/* --------------- deserialization callbacks ------------------ */

/** \brief Function for deserializing any collection type.
 * Currently supported types are string, array, sequence and set.
 */
static c_bool sd_bigEDeserCollection (os_size_t *len, c_collectionType collectionType, c_object *objectPtr, c_octet *dataPtr) __nonnull_all__ __attribute_warn_unused_result__;

static c_bool sd_bigEDeserCollection (os_size_t *len, c_collectionType collectionType, c_object *objectPtr, c_octet *dataPtr)
{
    c_ulong colSize;
    c_set set;
    c_object object, inserted;
    c_ulong i;
    c_bool isValidRef;
    c_octet *dataPtrHelper;

    /* Different behaviour for reftypes and non-reftypes */
    if (((collectionType->kind == OSPL_C_ARRAY) ||
         (collectionType->kind == OSPL_C_SEQUENCE)) &&
         !c_typeIsRef(c_type(collectionType))) {

        *len = 0;

    } else {
        SD_COPY2BIG_1(&isValidRef, dataPtr);
        *len = 1;

        if (isValidRef) {
            /* Skip to the data itself */
            dataPtrHelper = C_DISPLACE(dataPtr, *len);

            /* Only serialize the collection size in case of list/set/bag/etc */
            switch (collectionType->kind) {
                case OSPL_C_STRING: {
                    char *str;
                    if ((str = c_stringNew_s(c_getBase(collectionType), (const c_char *)dataPtrHelper)) == NULL) {
                        return FALSE;
                    }
                    *((c_string *)(*objectPtr)) = str;
                    *len += strlen((const char *)dataPtrHelper) + 1U /* '\0' */;
                    break;
                }
                case OSPL_C_ARRAY:
                case OSPL_C_SEQUENCE: {
                    void *ary;
                    /* Deserialize into new array if necessary */
                    SD_CONFIDENCE(c_typeIsRef(c_type(collectionType)));
                    SD_COPY2BIG_4(&colSize,dataPtrHelper);
                    *len += 4;
                    if ((ary = c_newBaseArrayObject_s(collectionType, colSize)) == NULL && colSize) {
                        return FALSE;
                    }
                    *((c_array *)(*objectPtr)) = ary;
                    break;
                }
                case OSPL_C_SET:
                    /* Get the size */
                    SD_COPY2BIG_4(&colSize,dataPtrHelper);
                    *len += 4;
                    /* Create the set */
                    set = c_setNew(collectionType->subType);
                    *((c_set *)(*objectPtr)) = set;
                    /* And initialize it with objects */
                    for (i=0; i<colSize; i++) {
                        if ((object = c_new_s(collectionType->subType)) == NULL) {
                            return FALSE;
                        }
                        inserted = ospl_c_insert(set, object);
                        SD_CONFIDENCE(inserted == object);
                        OS_UNUSED_ARG(inserted);
                        /* Let go of this instance */
                        c_free(object);
                    }
                    break;
                    /* Other collection types not yet implemented */
                case OSPL_C_LIST:
                case OSPL_C_BAG:
                case OSPL_C_DICTIONARY:
                case OSPL_C_QUERY:
                    SD_CONFIDENCE(FALSE);
                    break;
                default:
                    SD_CONFIDENCE(FALSE);
                    /* No other collection types supported */
                    break;
            }
        }
    }

    return TRUE;
}

/** \brief Deserialization function for interfaces and classes */
static c_bool sd_bigEDeserInterface (c_size *result, c_interface interf, c_object *objectPtr, c_octet *dataPtr) __nonnull_all__ __attribute_warn_unused_result__;

static c_bool sd_bigEDeserInterface (c_size *result, c_interface interf, c_object *objectPtr, c_octet *dataPtr)
{
    c_bool isValidRef;
    c_object *placeHolder;

    SD_CONFIDENCE((c_baseObject(interf)->kind == M_INTERFACE) || (c_baseObject(interf)->kind == M_CLASS));

    placeHolder = (c_object *)(*objectPtr);

    /* Deserialize the boolean in order to find out if this is
       a valid reference */
    SD_COPY2BIG_1(&isValidRef, dataPtr);
    *result = 1;
    if (isValidRef) {
        if (!(*placeHolder)) {
            /* A new instance has to be created, since this object is still a
             * NULL pointer */
            if ((*placeHolder = c_new_s(c_type(interf))) == NULL) {
                return FALSE;
            }
        }
    } else {
        *placeHolder = 0;
    }

    return TRUE;
}

/** \brief Callback function for deepwalk when deserializing */
static c_bool sd_bigEDeserCallback (c_type type, c_object *objectPtr, void *actionArg) __nonnull_all__ __attribute_warn_unused_result__;

static c_bool sd_bigEDeserCallback (c_type type, c_object *objectPtr, void *actionArg)
{
    c_octet **dataPtrPtr = actionArg;
    os_size_t len;

    switch (c_baseObject(type)->kind) {
        case M_COLLECTION:
            if (!sd_bigEDeserCollection(&len, c_collectionType(type), objectPtr, *dataPtrPtr)) {
                return FALSE;
            }
            break;
        case M_PRIMITIVE:
            SD_COPY2BIG_N(*objectPtr,*dataPtrPtr,type->size);
            len = type->size;
            break;
        case M_ENUMERATION:
            SD_COPY2BIG_4(*objectPtr,*dataPtrPtr);
            len = 4;
            break;
        case M_INTERFACE:
        case M_CLASS:
            if (!sd_bigEDeserInterface(&len, c_interface(type), objectPtr, *dataPtrPtr)) {
                return FALSE;
            }
            break;
        default:
            SD_CONFIDENCE(FALSE); /* No other expected than these */
            len = 0;
            break;
    }
    
    *dataPtrPtr = C_DISPLACE(*dataPtrPtr, C_ADDRESS(len));
    return TRUE;
}


/* --------------------------- driving functions ---------------------------- */


/* ------------------------------ serialization ----------------------------- */

/** \brief The actual BigE implementation of the virtual abstract serialize
 * function as introduced by sd_serializer */
static sd_serializedData sd_serializerBigESerialize(sd_serializer serializer, c_object object) __nonnull_all__ __attribute_warn_unused_result__;

static sd_serializedData
sd_serializerBigESerialize(
    sd_serializer serializer,
    c_object object)
{
    sd_serializedData result;
    os_size_t size;
    c_long start;
    c_char *typeName;
    c_type type;
    c_char *startPtr;

    OS_UNUSED_ARG(serializer);

    SD_CONFIDENCE(sd_checkSerializerType(serializer));

    type = c_getType(object);
    typeName = sd_getScopedTypeName(type, "::");

    /* Determine the size */
    size = strlen(typeName) + 1U; /* sep */
    if (!sd_deepwalk(type, &object, sd_bigECountCallback, &size)) {
        os_free(typeName);
        return NULL;
    }

    /* Instantiate the serialized data */
    result = sd_serializedDataNew(SD_FORMAT_ID, SD_FORMAT_VERSION, size);

    /* Fill the block */
    /* First print the name */
    /* Note: the return value of snprintf does not count the terminator */
    start = snprintf((c_char *)result->data, size, "%s", typeName) + 1;
    os_free(typeName);

    /* Then do the walk */
    startPtr =  C_DISPLACE(result->data,  C_ADDRESS(start));
    if (!sd_deepwalk(type, &object, sd_bigESerCallback, &startPtr)) {
        sd_serializedDataFree (result);
        return NULL;
    }

    /* Double-check if the size was calculated correctly */
    SD_CONFIDENCE((C_ADDRESS(startPtr) - C_ADDRESS(result->data)) ==
                   sd_serializedDataGetDataSize(result));

    return result;
}

/** \brief The actual BigE typed implementation of the virtual abstract
 * serialize function as introduced by sd_serializer
 */
static sd_serializedData sd_serializerBigESerializeTyped(sd_serializer serializer, c_object object) __nonnull_all__ __attribute_warn_unused_result__;

static sd_serializedData
sd_serializerBigESerializeTyped(
    sd_serializer serializer,
    c_object object)
{
    sd_serializedData result;
    os_size_t size;
    c_char *startPtr;

    SD_CONFIDENCE(sd_checkSerializerType(serializer));
    SD_CONFIDENCE((serializer->type == c_getType(object)) ||
                  (c_baseObject(c_typeActualType(serializer->type))->kind ==
                   M_COLLECTION));

    /* Determine the size */
    size = 0;
    if (!sd_deepwalk(serializer->type, &object, sd_bigECountCallback, &size)) {
        return NULL;
    }

    /* Instantiate the serialized data */
    result = sd_serializedDataNew(SD_FORMAT_ID, SD_FORMAT_VERSION, size);

    /* Fill the block */
    startPtr = (c_char *)(result->data);
    if (!sd_deepwalk(serializer->type, &object, sd_bigESerCallback, &startPtr)) {
        sd_serializedDataFree(result);
        return NULL;
    }

    /* Double-check if the size was calculated correctly */
    SD_CONFIDENCE((C_ADDRESS(startPtr) - C_ADDRESS(result->data)) ==
                   sd_serializedDataGetDataSize(result));

    return result;
}


/** \brief The actual BigE typed internal implementation of the virtual abstract
 * serialize function as introduced by sd_serializer
 */
static sd_serializedData sd_serializerBigESerializeTypedInternal(sd_serializer serializer, c_object object) __nonnull((1)) __attribute_warn_unused_result__;

static sd_serializedData
sd_serializerBigESerializeTypedInternal(
    sd_serializer serializer,
    c_object object)
{
    sd_serializedData result;
    os_size_t size;
    c_char *startPtr;
    c_bool isInternalType;
    c_type typeToUse;
    c_char internalFormatByte;

    SD_CONFIDENCE(sd_checkSerializerType(serializer));
    SD_CONFIDENCE((object == NULL) ||
                  (serializer->type == c_getType(object)) ||
                  (sd_serializerBigEInternal(serializer)->internalType == c_getType(object)) ||
                  (c_baseObject(c_typeActualType(serializer->type))->kind ==
                   M_COLLECTION));

    /* First check if this is the internal type */
    isInternalType = (object != NULL) && ((sd_serializerBigEInternal(serializer)->internalType == c_getType(object)));

    if (isInternalType) {
        /* Only continue if this is correctly constructed */
        SD_CONFIDENCE(sd_serializerBigEInternal(serializer)->internalType != NULL);
        typeToUse = sd_serializerBigEInternal(serializer)->internalType;
        internalFormatByte = SD_FORMAT_INTERNAL_BYTE;
    } else {
        typeToUse = serializer->type;
        internalFormatByte = SD_FORMAT_USER_BYTE;
    }

    /* Determine the size */
    size = SD_INTERNAL_INDICATOR_SIZE; /* Starting byte is counted as well */
    if (!sd_deepwalk(typeToUse, &object, sd_bigECountCallback, &size)) {
        return NULL;
    }

    /* Instantiate the serialized data */
    result = sd_serializedDataNew(SD_FORMAT_ID, SD_FORMAT_VERSION, size);

    /* Set the pointer to the correct place */
    startPtr = (c_char *)(result->data);
    *startPtr = internalFormatByte;
    /* skip to the actual startingpoint of the serialized data */
    startPtr = &(startPtr[SD_INTERNAL_INDICATOR_SIZE]);

    /* And do the serialization */
    if (!sd_deepwalk(typeToUse, &object, sd_bigESerCallback, &startPtr)) {
        sd_serializedDataFree(result);
        return NULL;
    }

    /* Double-check if the size was calculated correctly */
    SD_CONFIDENCE((C_ADDRESS(startPtr) - C_ADDRESS(result->data)) ==
                   sd_serializedDataGetDataSize(result));

    return result;
}


/* ----------------------------- deserialization ---------------------------- */

/** \brief Function implementing common functionalities for all bigE
 *  deserialization routines */
static c_bool sd_serializerBigEDeserializeInternal (c_type type, c_object *objectPtr, c_octet **dataPtrPtr) __nonnull_all__ __attribute_warn_unused_result__;

static c_bool
sd_serializerBigEDeserializeInternal(
    c_type type,
    c_object *objectPtr,
    c_octet **dataPtrPtr)
{
    if (!c_typeIsRef(type)) {
        if ((*objectPtr = c_new_s(type)) == NULL) {
            return FALSE;
        } else if (!sd_deepwalk(type, objectPtr, sd_bigEDeserCallback, dataPtrPtr)) {
            c_free(*objectPtr);
            *objectPtr = NULL;
            return FALSE;
        } else {
            return TRUE;
        }
    } else {
        /* Class and interface reference created by deser algorithm */
        *objectPtr = NULL;
        if (!sd_deepwalk(type, objectPtr, sd_bigEDeserCallback, dataPtrPtr)) {
            return FALSE;
        } else {
            return TRUE;
        }
    }
}

/** \brief The implementation of the virtual abstract deserialize function
 * as introduced by sd_serializer, for the BigE class */
static c_object sd_serializerBigEDeserialize(sd_serializer serializer, sd_serializedData serData) __nonnull_all__ __attribute_warn_unused_result__;

static c_object
sd_serializerBigEDeserialize(
    sd_serializer serializer,
    sd_serializedData serData)
{
    c_object result = NULL;
    c_type resultType;
    c_char *name;
    c_octet *startPtr;

    SD_CONFIDENCE(sd_checkSerializerType(serializer));

    /* The deserialized data starts with the scoped name of the type */
    /* Create the object using this name */
    name = (c_char *)serData->data;
    resultType = c_resolve(serializer->base, name);

    /* Only continue if the resolution was succesful */
    SD_CONFIDENCE(resultType);

    if (resultType) {

        /* Find the starting point of the actual data */
        startPtr = C_DISPLACE(serData->data, C_ADDRESS(strlen(name) + 1U));

        if (!sd_serializerBigEDeserializeInternal(resultType, &result, &startPtr)) {
            SD_CONFIDENCE(result == NULL);
        } else {
            /* Double-check if the size was calculated correctly */
            SD_CONFIDENCE(result && (C_ADDRESS(startPtr) - C_ADDRESS(serData->data)) == sd_serializedDataGetDataSize(serData));
        }

        c_free(resultType);
    }

    return result;
}

/** \brief The implementation of the virtual abstract deserialize function
 * as introduced by sd_serializer, for the BigETyped class */
static c_object sd_serializerBigEDeserializeTyped(sd_serializer serializer, sd_serializedData serData) __nonnull_all__ __attribute_warn_unused_result__;

static c_object
sd_serializerBigEDeserializeTyped(
    sd_serializer serializer,
    sd_serializedData serData)
{
    c_object result = NULL;
    c_octet *startPtr;

    SD_CONFIDENCE(sd_checkSerializerType(serializer));

    /* Only continue if this is constructed using BigENewTyped */
    SD_CONFIDENCE(serializer->type != NULL);

    startPtr = serData->data;
    if (!sd_serializerBigEDeserializeInternal(serializer->type, &result, &startPtr)) {
        SD_CONFIDENCE(result == NULL);
    } else {
        /* Double-check deserialisation did not read beyond the end of the serialised data */
        SD_CONFIDENCE(result && (C_ADDRESS(startPtr) - C_ADDRESS(serData->data)) <= sd_serializedDataGetDataSize(serData));
    }

    return result;
}


/** \brief The implementation of the virtual abstract deserialize function
 * as introduced by sd_serializer, for the BigETypedInternal class */
static c_object sd_serializerBigEDeserializeTypedInternal(sd_serializer serializer, sd_serializedData serData) __nonnull_all__ __attribute_warn_unused_result__;

static c_object
sd_serializerBigEDeserializeTypedInternal(
    sd_serializer serializer,
    sd_serializedData serData)
{
    c_object result = NULL;
    c_octet *startPtr;
    c_bool isInternalType;
    c_bool ok;

    SD_CONFIDENCE(sd_checkSerializerType(serializer));

    startPtr = serData->data;
    /* First check if this is user data or internal data */
    isInternalType = (*startPtr == SD_FORMAT_INTERNAL_BYTE);
    startPtr = &(startPtr[SD_INTERNAL_INDICATOR_SIZE]);

    if (isInternalType) {
        /* Only continue if this is correctly constructed */
        SD_CONFIDENCE(sd_serializerBigEInternal(serializer)->internalType != NULL);
        ok = sd_serializerBigEDeserializeInternal(sd_serializerBigEInternal(serializer)->internalType, &result, &startPtr);
    } else {
        /* Only continue if this is correctly constructed */
        SD_CONFIDENCE(serializer->type != NULL);
        ok = sd_serializerBigEDeserializeInternal(serializer->type, &result, &startPtr);
    }

    /* Double-check if the size was calculated correctly */
    if (!ok) {
        SD_CONFIDENCE(result == NULL);
    } else {
        SD_CONFIDENCE(result && (C_ADDRESS(startPtr) - C_ADDRESS(serData->data)) == sd_serializedDataGetDataSize(serData));
    }
    
    return result;
}



/* ----------------------- conversion to/from string ------------------------ */

/** \brief The actual implementation of the virtual abstract toString function
 * as introduced by sd_serializer */
static c_char *
sd_serializerBigEToString(
    sd_serializer serializer,
    sd_serializedData serData)
{
#define SD_ASCII_SPACE          (32U)  /* SP: Lowest printable character      */
#define SD_ASCII_TILDE         (126U)  /* ~: Highest printable character      */
#define SD_ASCII_PERCENT        (37U)  /* %: Avoid printf confusion           */
#define SD_ASCII_DOT            (46U)  /* .: Replacer for non-printable chars */
#define SD_ASCII_ZERO           (48U)  /* 0: Base for digits                  */
#define SD_ASCII_A              (65U)  /* A: Base for hex-digits > 10         */
#define SD_SEP_REPLACER         (c_octet)'\n'
#define SD_ASCII(i)             (c_octet)(                 \
                                  ((unsigned int)(i)<10U)?  \
                                  ((unsigned int)(i)+SD_ASCII_ZERO):  \
                                  ((unsigned int)(i)+ SD_ASCII_A - 10U))
#define SD_ASCII_MSB(i)         (c_octet)(SD_ASCII(((unsigned int)(i)>>4)))
#define SD_ASCII_LSB(i)         (c_octet)(SD_ASCII(((unsigned int)(i)&15U)))
#define SD_ASCII_PRINTABLE(i)   (c_octet)(                      \
                                  ( ((unsigned int)(i)>= SD_ASCII_SPACE) && \
                                    ((unsigned int)(i)<= SD_ASCII_TILDE) && \
                                    ((unsigned int)(i)!= SD_ASCII_PERCENT) )? \
                                  (unsigned int)(i):            \
                                  SD_ASCII_DOT)
#define SD_LINE_WIDTH           (16U)
#define SD_BUFSIZE              (32U)
#define SD_BYTESEP              (c_octet)' '
#define SD_LINESEP              (c_octet)'\n'

    c_ulong dataSize, totSize, nLines, start;
    c_char *result;
    c_octet *currentDest, *currentSrc;
    c_ulong line, i, nThisLine;
    c_char sizeBuf[SD_BUFSIZE];
    int snpRes;
    OS_UNUSED_ARG(serializer);

    SD_CONFIDENCE(sd_checkSerializerType(serializer));

    dataSize = sd_serializedDataGetDataSize(serData);

    nLines = ((dataSize-1U) / SD_LINE_WIDTH) + 1U;
    totSize = ((4U*SD_LINE_WIDTH) + 1U) * nLines;
    totSize += (c_ulong)strlen(SD_FORMAT_ID_STRING) + 1U;
    totSize += (c_ulong)strlen(SD_FORMAT_VERSION_STRING) + 1U;
    snpRes = snprintf(sizeBuf, SD_BUFSIZE, "%d%c", dataSize, '\0');
    if (snpRes >= 0) {
        totSize += (c_ulong)snpRes;
    } else {
       SD_CONFIDENCE(snpRes >= 0);
    }
    totSize++; /* '\0' */
    result = os_malloc(totSize);

    /* Prepend version info */
    snpRes = snprintf(result, totSize, "%s%c%s%c%s%c",
                    SD_FORMAT_ID_STRING, SD_SEP_REPLACER,
                    SD_FORMAT_VERSION_STRING, SD_SEP_REPLACER,
                    sizeBuf, SD_SEP_REPLACER);
    if (snpRes >= 0) {
        start = (c_ulong)snpRes;
    } else {
        start = 0;
        SD_CONFIDENCE(snpRes >= 0);
    }

    /* The actual serialized data */
    currentSrc = serData->data;
    currentDest = C_DISPLACE(result, C_ADDRESS(start));
    for (line=0; line < nLines; line++) {
        nThisLine = (((line+1U) < nLines) ?
                     SD_LINE_WIDTH :
                     (((dataSize - 1U) % SD_LINE_WIDTH) + 1U));
        for (i=0; i < SD_LINE_WIDTH; i++) {
            if (i < nThisLine) {
                currentDest[(3U*i)] = SD_ASCII_MSB(currentSrc[i]);
                currentDest[(3U*i) + 1U] = SD_ASCII_LSB(currentSrc[i]);
            } else {
                currentDest[(3U*i)] = SD_BYTESEP;
                currentDest[(3U*i) + 1U] = SD_BYTESEP;
            }
            currentDest[(3U*i) + 2U] = SD_BYTESEP;
        }
        currentDest = &(currentDest[3U*SD_LINE_WIDTH]);
        for (i=0; i < nThisLine; i++) {
            currentDest[i] = SD_ASCII_PRINTABLE(currentSrc[i]);
        }
        currentDest = &(currentDest[nThisLine]);
        currentSrc = &(currentSrc[nThisLine]);

        *currentDest = SD_LINESEP;
        currentDest = &(currentDest[1]);
    }
    currentDest = &(currentDest[-1]);
    *currentDest = 0;

    return result;

#undef SD_ASCII
#undef SD_ASCII_PRINTABLE
#undef SD_ASCII_LSB
#undef SD_ASCII_MSB
#undef SD_SEP_REPLACER
#undef SD_LINE_WIDTH
#undef SD_BUFSIZE
#undef SD_BYTESEP
#undef SD_LINESEP
}


/** \brief Constructor for the big endian format serializer for any type
 *
 *  The \b serializerBigE class is a concrete descendant of the abstract
 *  \b serializer class. In order to use this class, create it with this
 *  function and call the methods as defined on \b serializer. This class
 *  is capable of (de)serializing objects of any type.
 *
 *  This class is supposed to do quick (de)serialization and does not do any
 *  checking on format errors.
 *
 *  \param base The database to serialize to and deserialize from.
 */

sd_serializer
sd_serializerBigENew(
    c_base base)
{
    struct sd_serializerVMT VMT;
    VMT.serialize = sd_serializerBigESerialize;
    VMT.deserialize = sd_serializerBigEDeserialize;
    VMT.deserializeInto = NULL;
    VMT.toString = sd_serializerBigEToString;
    VMT.fromString = NULL;
    return sd_serializerNew(SD_FORMAT_ID, SD_FORMAT_VERSION, base, NULL, VMT);
}

/** \brief Constructor for the big endian format serializer for a fixed type.
 *
 *  The \b serializerBigETyped class is a concrete descendant of the abstract
 *  \b serializer class. In order to use this class, create it with this
 *  function and call the methods as defined on \b serializer. This class is
 *  capable of (de)serializing objects of one specific type only.
 *
 *  This class is supposed to do quick (de)serialization and does not do any
 *  checking on format errors.
 *
 *  \param type The type that will be (de)serialized by this object.
 */
sd_serializer
sd_serializerBigENewTyped(
    c_type type)
{
    struct sd_serializerVMT VMT;
    VMT.serialize = sd_serializerBigESerializeTyped;
    VMT.deserialize = sd_serializerBigEDeserializeTyped;
    VMT.deserializeInto = NULL;
    VMT.toString = sd_serializerBigEToString;
    VMT.fromString = NULL;
    return sd_serializerNew(SD_FORMAT_ID, SD_FORMAT_VERSION, c_getBase(type), type, VMT);
}

/** \brief Constructor for the big endian format serializer for a fixed type
 *         which can also handle an internal type
 *
 *  The \b serializerBigETyped class is a concrete descendant of the abstract
 *  \b serializer class. In order to use this class, create it with this
 *  function and call the methods as defined on \b serializer. This class is
 *  capable of (de)serializing objects of two specific types: the type given
 *  to the constructor and the v_networkMessageType
 *
 *  This class is supposed to do quick (de)serialization and does not do any
 *  checking on format errors.
 *
 *  Note: this class could be changed so that it will be able to (de)serialize
 *  several internal types.
 *
 *  \param type The type that will be (de)serialized by this object.
 */

/* Note: hard-coding the name of the type here decreases reusability. Another
 * choice would have been giving the internal type to the constructor. For now,
 * this dedicated solution is chosen for simplicity */
#define SD_INTERNAL_TYPE_NAME "kernelModuleI::v_networkMessage"

sd_serializer
sd_serializerBigENewTypedInternal(
    c_type type)
{
    sd_serializerBigEInternal result;
    c_base base;
    struct sd_serializerVMT VMT;

    base = c_getBase((c_object)type);

    VMT.serialize = sd_serializerBigESerializeTypedInternal;
    VMT.deserialize = sd_serializerBigEDeserializeTypedInternal;
    VMT.deserializeInto = NULL;
    VMT.toString = sd_serializerBigEToString;
    VMT.fromString = NULL;

    result = os_malloc(sizeof(*result));
    /* Initialize self */
    result->internalType = c_resolve(base, SD_INTERNAL_TYPE_NAME);
    /* Initialize parent */
    sd_serializerInitialize((sd_serializer)result, SD_FORMAT_ID, SD_FORMAT_VERSION, base, type, VMT);
    return (sd_serializer)result;
}

#undef SD_INTERNAL_TYPE_NAME

#undef SD_FORMAT_ID
#undef SD_FORMAT_VERSION
#undef SD_FORMAT_ID_STRING
#undef SD_FORMAT_VERSION_STRING
#undef SD_FORMAT_USER_BYTE
#undef SD_FORMAT_INTERNAL_BYTE
