#if HAVE_CRT
#define _CRTDBG_MAP_ALLOC 
#include <stdlib.h> 
#include <crtdbg.h>
#endif //HAVE_CRT
/*
* Copyright (C) 2017, University of the Basque Country (UPV/EHU)
* Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com>
*
* The original file was part of Open Source Doubango Framework
* Copyright (C) 2010-2011 Mamadou Diop.
* Copyright (C) 2012 Doubango Telecom <http://doubango.org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/

#include "tinybfcp/tbfcp_attr.h"

#include "tnet_endianness.h"

#include "tsk_memory.h"
#include "tsk_debug.h"

#define kWithoutPadding		tsk_false
#define kWithPadding		tsk_true

#define ALIGN_ON_32BITS(size_in_octes) if (((size_in_octes) & 3)) (size_in_octes) += (4 - ((size_in_octes) & 3));
#define ALIGN_ON_32BITS_AND_SET_PADDING_ZEROS(p_buffer, size_in_octes) \
	if (((size_in_octes) & 3)) { \
		int c = (4 - ((size_in_octes) & 3)); \
		memset(p_buffer, 0, c); \
		(size_in_octes) += c; \
	}


static enum tbfcp_attribute_format_e _tbfcp_attr_get_format(enum tbfcp_attribute_type_e type)
{
    switch (type) {
    case tbfcp_attribute_type_BENEFICIARY_ID:
        case tbfcp_attribute_type_FLOOR_ID:
            case tbfcp_attribute_type_FLOOR_REQUEST_ID:
                    return tbfcp_attribute_format_Unsigned16;
    case tbfcp_attribute_type_PRIORITY:
    case tbfcp_attribute_type_REQUEST_STATUS:
        return tbfcp_attribute_format_OctetString16;
    case tbfcp_attribute_type_ERROR_CODE:
    case tbfcp_attribute_type_ERROR_INFO:
    case tbfcp_attribute_type_PARTICIPANT_PROVIDED_INFO:
    case tbfcp_attribute_type_STATUS_INFO:
    case tbfcp_attribute_type_SUPPORTED_ATTRIBUTES:
    case tbfcp_attribute_type_SUPPORTED_PRIMITIVES:
    case tbfcp_attribute_type_USER_DISPLAY_NAME:
    case tbfcp_attribute_type_USER_URI:
        return tbfcp_attribute_format_OctetString;
    case tbfcp_attribute_type_BENEFICIARY_INFORMATION:
    case tbfcp_attribute_type_FLOOR_REQUEST_INFORMATION:
    case tbfcp_attribute_type_REQUESTED_BY_INFORMATION:
    case tbfcp_attribute_type_FLOOR_REQUEST_STATUS:
    case tbfcp_attribute_type_OVERALL_REQUEST_STATUS:
        return tbfcp_attribute_format_Grouped;
    default:
        return tbfcp_attribute_format_Unknown;

    }
}

static int _tbfcp_attr_get_size_in_octetunits(const tbfcp_attr_t* pc_self, tsk_bool_t with_padding, tsk_size_t* p_size)
{
    if (!pc_self || !p_size) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }
    switch (pc_self->format) {
    case tbfcp_attribute_format_Unsigned16:
    case tbfcp_attribute_format_OctetString16: {
        *p_size = (TBFCP_ATTR_HDR_SIZE_IN_OCTETS + 2);
        if (with_padding) {
            ALIGN_ON_32BITS(*p_size);
        }
        return 0;
    }
    case tbfcp_attribute_format_OctetString: {
        *p_size = (TBFCP_ATTR_HDR_SIZE_IN_OCTETS + ((const tbfcp_attr_octetstring_t*)pc_self)->OctetStringLength);
		if (with_padding) {
			ALIGN_ON_32BITS(*p_size);
		}
        return 0;
    }
    case tbfcp_attribute_format_Grouped: {
        int ret;
        tsk_size_t n_size;
        const tbfcp_attr_grouped_t* _pc_self = (const tbfcp_attr_grouped_t*)pc_self;
        const tsk_list_item_t* pc_item;
        const tbfcp_attr_t* pc_attr;
        *p_size = TBFCP_ATTR_HDR_SIZE_IN_OCTETS + _pc_self->extra_hdr_size_in_octets;
        tsk_list_foreach(pc_item, _pc_self->p_list_attrs) {
            if ((pc_attr = (const tbfcp_attr_t*)pc_item->data)) {
                if ((ret = tbfcp_attr_get_size_in_octetunits_without_padding(pc_attr, &n_size))) {
                    return ret;
                }
                *p_size += n_size;
            }
        }
        if (with_padding) {
            ALIGN_ON_32BITS(*p_size);
        }
        return 0;
    }
    default: {
        TSK_DEBUG_WARN("Attribute format=%d is unknown. Don't be surprised if something goes wrong.", pc_self->format);
        *p_size = (TBFCP_ATTR_HDR_SIZE_IN_OCTETS + pc_self->hdr.length);
        if (with_padding) {
            ALIGN_ON_32BITS(*p_size);
        }
        return 0;
    }
    }
}

int tbfcp_attr_get_size_in_octetunits_without_padding(const tbfcp_attr_t* pc_self, tsk_size_t* p_size)
{
    return _tbfcp_attr_get_size_in_octetunits(pc_self, kWithoutPadding, p_size);
}

int tbfcp_attr_get_size_in_octetunits_with_padding(const tbfcp_attr_t* pc_self, tsk_size_t* p_size)
{
    return _tbfcp_attr_get_size_in_octetunits(pc_self, kWithPadding, p_size);
}

static int _tbfcp_attr_write(const tbfcp_attr_t* pc_self, uint8_t* p_buff_ptr, tsk_size_t n_buff_size, tsk_bool_t with_padding, tsk_size_t *p_written)
{
    tsk_size_t n_min_req_size;
    int ret;
    if (!pc_self || !p_buff_ptr || !n_buff_size || !p_written) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }
    if ((ret = _tbfcp_attr_get_size_in_octetunits(pc_self, with_padding, &n_min_req_size))) {
        return ret;
    }
    if (n_min_req_size > n_buff_size) {
        TSK_DEBUG_ERROR("Buffer too short %u<%u", n_buff_size, n_min_req_size);
        return -2;
    }

    p_buff_ptr[0] = ((uint8_t)pc_self->hdr.type) << 1;
    p_buff_ptr[0] |= (pc_self->hdr.M & 0x01);

    switch (pc_self->format) {
    case tbfcp_attribute_format_Unsigned16:
    case tbfcp_attribute_format_OctetString16: {
        *p_written = TBFCP_ATTR_HDR_SIZE_IN_OCTETS + 2;
        p_buff_ptr[1] = (uint8_t)*p_written;
        if (pc_self->format == tbfcp_attribute_format_Unsigned16) {
            *((uint16_t*)&p_buff_ptr[2]) = tnet_htons(((const tbfcp_attr_unsigned16_t*)pc_self)->Unsigned16);
        }
        else {
            p_buff_ptr[2] = ((const tbfcp_attr_octetstring16_t*)pc_self)->OctetString16[0];
            p_buff_ptr[3] = ((const tbfcp_attr_octetstring16_t*)pc_self)->OctetString16[1];
        }
        if (with_padding) {
            ALIGN_ON_32BITS_AND_SET_PADDING_ZEROS(&p_buff_ptr[*p_written], *p_written);
        }
        return 0;
    }
    case tbfcp_attribute_format_OctetString: {
        const tbfcp_attr_octetstring_t* _pc_self = (const tbfcp_attr_octetstring_t*)pc_self;
        *p_written = TBFCP_ATTR_HDR_SIZE_IN_OCTETS + ((_pc_self->OctetStringLength && _pc_self->OctetString) ? _pc_self->OctetStringLength : 0);
        p_buff_ptr[1] = (uint8_t)*p_written;
        if (_pc_self->OctetStringLength && _pc_self->OctetString) {
            memcpy(&p_buff_ptr[2], _pc_self->OctetString, _pc_self->OctetStringLength);
        }
        if (with_padding) {
            ALIGN_ON_32BITS_AND_SET_PADDING_ZEROS(&p_buff_ptr[*p_written], *p_written);
        }
        return 0;
    }
    case tbfcp_attribute_format_Grouped: {
        int ret;
        tsk_size_t n_written;
        const tbfcp_attr_grouped_t* _pc_self = (const tbfcp_attr_grouped_t*)pc_self;
        const tsk_list_item_t* pc_item;
        const tbfcp_attr_t* pc_attr;
        if (_pc_self->extra_hdr_size_in_octets && _pc_self->extra_hdr_size_in_octets != 2) {
            TSK_DEBUG_ERROR("extra_hdr_size_in_octets=%u not valid", _pc_self->extra_hdr_size_in_octets); // for now only 2byte extra values is supported
            return -2;
        }
        if ((ret = tbfcp_attr_get_size_in_octetunits_without_padding(pc_self, p_written))) {
            return ret;
        }
        p_buff_ptr[1] = (uint8_t)*p_written;
        p_buff_ptr += 2;
        n_buff_size -= 2;
        if (_pc_self->extra_hdr_size_in_octets) {
            *((uint16_t*)p_buff_ptr) = tnet_htons_2(&_pc_self->extra_hdr);
            p_buff_ptr += _pc_self->extra_hdr_size_in_octets;
            n_buff_size -= _pc_self->extra_hdr_size_in_octets;
        }
        tsk_list_foreach(pc_item, _pc_self->p_list_attrs) {
            if ((pc_attr = (const tbfcp_attr_t*)pc_item->data)) {
                if ((ret = _tbfcp_attr_write(pc_attr, p_buff_ptr, n_buff_size, kWithoutPadding, &n_written))) {
                    return ret;
                }
                p_buff_ptr += n_written;
                n_buff_size -= n_written;
            }
        }
        if (with_padding) {
            ALIGN_ON_32BITS_AND_SET_PADDING_ZEROS(&p_buff_ptr[*p_written], *p_written);
        }
        return 0;
    }
    default: {
        TSK_DEBUG_ERROR("Attribute format=%d is unknown.", pc_self->format);
        return -2;
    }
    }
}

int tbfcp_attr_write_without_padding(const tbfcp_attr_t* pc_self, uint8_t* p_buff_ptr, tsk_size_t n_buff_size, tsk_size_t *p_written)
{
    return _tbfcp_attr_write(pc_self, p_buff_ptr, n_buff_size, kWithoutPadding, p_written);
}

int tbfcp_attr_write_with_padding(const tbfcp_attr_t* pc_self, uint8_t* p_buff_ptr, tsk_size_t n_buff_size, tsk_size_t *p_written)
{
    return _tbfcp_attr_write(pc_self, p_buff_ptr, n_buff_size, kWithPadding, p_written);
}

int tbfcp_attr_read(const uint8_t* pc_buff_ptr, tsk_size_t n_buff_size, tsk_size_t *p_consumed_octets, tbfcp_attr_t** pp_attr)
{
    uint8_t M, Length, PadLength;
    tbfcp_attribute_type_t Type;
    tbfcp_attribute_format_t Format;
    int ret;
    if (!pc_buff_ptr || !n_buff_size || !pp_attr || !p_consumed_octets) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }
    if (n_buff_size < TBFCP_ATTR_HDR_SIZE_IN_OCTETS) {
        TSK_DEBUG_ERROR("Buffer too short(%u)", n_buff_size);
        return -2;
    }

    Length = pc_buff_ptr[1];
    if (Length > n_buff_size) {
        TSK_DEBUG_ERROR("Buffer too short(%u). Length=%u", n_buff_size, Length);
        return -3;
    }

    PadLength = (Length & 0x03) ? (4 - (Length & 0x03)) : 0;

    *pp_attr = tsk_null;
    *p_consumed_octets = Length + PadLength;

    Type = (pc_buff_ptr[0] >> 1) & 0x7F;
    M = (pc_buff_ptr[0] & 0x01);
    Format = _tbfcp_attr_get_format(Type);
    if (Format == tbfcp_attribute_format_Unknown) {
        return 0;
    }

    if (Format == tbfcp_attribute_format_Unsigned16) {
        uint16_t Unsigned16 = tnet_ntohs_2(&pc_buff_ptr[2]);
        if ((ret = tbfcp_attr_unsigned16_create(Type, M, Unsigned16, (tbfcp_attr_unsigned16_t**)pp_attr))) {
            return ret;
        }
    }
    else if (Format == tbfcp_attribute_format_OctetString16) {
        uint8_t OctetString16[2];
        OctetString16[0] = pc_buff_ptr[2];
        OctetString16[1] = pc_buff_ptr[3];
        if ((ret = tbfcp_attr_octetstring16_create(Type, M, OctetString16, (tbfcp_attr_octetstring16_t**)pp_attr))) {
            return ret;
        }
    }
    else if (Format == tbfcp_attribute_format_OctetString) {
        const uint8_t *OctetString = &pc_buff_ptr[TBFCP_ATTR_HDR_SIZE_IN_OCTETS];
        uint8_t OctetStringLength = (Length - TBFCP_ATTR_HDR_SIZE_IN_OCTETS);
        if ((ret = tbfcp_attr_octetstring_create(Type, M, OctetString, OctetStringLength, (tbfcp_attr_octetstring_t**)pp_attr))) {
            return ret;
        }
    }
    else if (Format == tbfcp_attribute_format_Grouped) {
        tbfcp_attr_grouped_t* p_attr;
        if ((ret = tbfcp_attr_grouped_create(Type, M, &p_attr))) {
            return ret;
        }
        *pp_attr = (tbfcp_attr_t*)p_attr;
        switch (Type) {
        case tbfcp_attribute_type_BENEFICIARY_INFORMATION: {
            p_attr->extra_hdr_size_in_octets = 2;
            p_attr->extra_hdr.BeneficiaryID = tnet_ntohs_2(&pc_buff_ptr[2]);
            break;
        }
        case tbfcp_attribute_type_FLOOR_REQUEST_INFORMATION: {
            p_attr->extra_hdr_size_in_octets = 2;
            p_attr->extra_hdr.FloorRequestID = tnet_ntohs_2(&pc_buff_ptr[2]);
            break;
        }
        case tbfcp_attribute_type_REQUESTED_BY_INFORMATION: {
            p_attr->extra_hdr_size_in_octets = 2;
            p_attr->extra_hdr.RequestedbyID = tnet_ntohs_2(&pc_buff_ptr[2]);
            break;
        }
        case tbfcp_attribute_type_FLOOR_REQUEST_STATUS: {
            p_attr->extra_hdr_size_in_octets = 2;
            p_attr->extra_hdr.FloorID = tnet_ntohs_2(&pc_buff_ptr[2]);
            break;
        }
        case tbfcp_attribute_type_OVERALL_REQUEST_STATUS: {
            p_attr->extra_hdr_size_in_octets = 2;
            p_attr->extra_hdr.FloorRequestID = tnet_ntohs_2(&pc_buff_ptr[2]);
            break;
        }
        default: {
            return 0;
        }
        }
        if ((TBFCP_ATTR_HDR_SIZE_IN_OCTETS + p_attr->extra_hdr_size_in_octets) < Length) {
            tsk_size_t n_consumed_octets, PayloadLength = Length;
            PayloadLength -= TBFCP_ATTR_HDR_SIZE_IN_OCTETS + p_attr->extra_hdr_size_in_octets;
            pc_buff_ptr += TBFCP_ATTR_HDR_SIZE_IN_OCTETS + p_attr->extra_hdr_size_in_octets;
            if (PayloadLength >= TBFCP_ATTR_HDR_SIZE_IN_OCTETS) {
                do {
                    if ((ret = tbfcp_attr_read(pc_buff_ptr, PayloadLength, &n_consumed_octets, (tbfcp_attr_t**)&p_attr))) {
                        break;
                    }
                    if ((ret = tbfcp_attr_grouped_add_attr((tbfcp_attr_grouped_t*)(*pp_attr), (tbfcp_attr_t**)&p_attr))) {
                        TSK_OBJECT_SAFE_FREE(p_attr);
                        break;
                    }
                    pc_buff_ptr += n_consumed_octets;
                    PayloadLength -= n_consumed_octets;
                }
                while (PayloadLength >= TBFCP_ATTR_HDR_SIZE_IN_OCTETS);
            }
        }
    }
    else {
        TSK_DEBUG_ERROR("%d not valid attribute format", Format);
        return -4;
    }
    return 0;
}


static int _tbfcp_attr_init(tbfcp_attr_t* p_self, tbfcp_attribute_type_t type, unsigned M, uint8_t length)
{
    if (!p_self) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }
    if (!p_self->pc_base) {
        p_self->pc_base = p_self;
    }
    p_self->hdr.type = type;
    p_self->hdr.M = M;
    p_self->hdr.length = length;
    if ((p_self->format = _tbfcp_attr_get_format(type)) == tbfcp_attribute_format_Unknown) {
        TSK_DEBUG_WARN("Attribute type=%d is unknown...setting its format to UNKNOWN. Don't be surprised if something goes wrong.", type);
    }

    return 0;
}


/*************** tbfcp_attr_unsigned16 *******************/
int tbfcp_attr_unsigned16_create(tbfcp_attribute_type_t type, unsigned M, uint16_t Unsigned16, tbfcp_attr_unsigned16_t** pp_self)
{
    extern const tsk_object_def_t *tbfcp_attr_unsigned16_def_t;
    tbfcp_attr_unsigned16_t* p_self;
    int ret;
    if (!pp_self) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }
    if (!(p_self = tsk_object_new(tbfcp_attr_unsigned16_def_t))) {
        TSK_DEBUG_ERROR("Failed to create 'tbfcp_attr_unsigned16_def_t' object");
        return -2;
    }
    if ((ret = _tbfcp_attr_init(TBFCP_ATTR(p_self), type, M, TBFCP_ATTR_HDR_SIZE_IN_OCTETS + 2))) {
        TSK_OBJECT_SAFE_FREE(p_self);
        return -3;
    }
    if (TBFCP_ATTR(p_self)->format != tbfcp_attribute_format_Unsigned16) {
        TSK_DEBUG_ERROR("Format mismatch");
        TSK_OBJECT_SAFE_FREE(p_self);
        return -4;
    }
    p_self->Unsigned16 = Unsigned16;
    *pp_self = p_self;
    return 0;
}

static tsk_object_t* tbfcp_attr_unsigned16_ctor(tsk_object_t * self, va_list * app)
{
    tbfcp_attr_unsigned16_t *p_u16 = (tbfcp_attr_unsigned16_t *)self;
    if (p_u16) {
    }
    return self;
}
static tsk_object_t* tbfcp_attr_unsigned16_dtor(tsk_object_t * self)
{
    tbfcp_attr_unsigned16_t *p_u16 = (tbfcp_attr_unsigned16_t *)self;
    if (p_u16) {
        TSK_DEBUG_INFO("*** BFCP Attribute(Unsigned16) destroyed ***");

    }
    return self;
}
static int tbfcp_attr_unsigned16_cmp(const tsk_object_t *_att1, const tsk_object_t *_att2)
{
    const tbfcp_attr_unsigned16_t *pc_att1 = (const tbfcp_attr_unsigned16_t *)_att1;
    const tbfcp_attr_unsigned16_t *pc_att2 = (const tbfcp_attr_unsigned16_t *)_att2;

    return (int)(pc_att1-pc_att2);
}
static const tsk_object_def_t tbfcp_attr_unsigned16_def_s = {
    sizeof(tbfcp_attr_unsigned16_t),
    tbfcp_attr_unsigned16_ctor,
    tbfcp_attr_unsigned16_dtor,
    tbfcp_attr_unsigned16_cmp,
};
const tsk_object_def_t *tbfcp_attr_unsigned16_def_t = &tbfcp_attr_unsigned16_def_s;




/*************** tbfcp_attr_octetstring16 *******************/
int tbfcp_attr_octetstring16_create(tbfcp_attribute_type_t type, unsigned M, uint8_t OctetString16[2], tbfcp_attr_octetstring16_t** pp_self)
{
    extern const tsk_object_def_t *tbfcp_attr_octetstring16_def_t;
    tbfcp_attr_octetstring16_t* p_self;
    int ret;
    if (!pp_self) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }
    if (!(p_self = tsk_object_new(tbfcp_attr_octetstring16_def_t))) {
        TSK_DEBUG_ERROR("Failed to create 'tbfcp_attr_octetstring16_def_t' object");
        return -2;
    }
    if ((ret = _tbfcp_attr_init(TBFCP_ATTR(p_self), type, M, TBFCP_ATTR_HDR_SIZE_IN_OCTETS + 2))) {
        TSK_OBJECT_SAFE_FREE(p_self);
        return -3;
    }
    if (TBFCP_ATTR(p_self)->format != tbfcp_attribute_format_OctetString16) {
        TSK_DEBUG_ERROR("Format mismatch");
        TSK_OBJECT_SAFE_FREE(p_self);
        return -4;
    }
    p_self->OctetString16[0] = OctetString16[0];
    p_self->OctetString16[1] = OctetString16[1];
    *pp_self = p_self;
    return 0;
}

static tsk_object_t* tbfcp_attr_octetstring16_ctor(tsk_object_t * self, va_list * app)
{
    tbfcp_attr_octetstring16_t *p_os16 = (tbfcp_attr_octetstring16_t *)self;
    if (p_os16) {
    }
    return self;
}
static tsk_object_t* tbfcp_attr_octetstring16_dtor(tsk_object_t * self)
{
    tbfcp_attr_octetstring16_t *p_os16 = (tbfcp_attr_octetstring16_t *)self;
    if (p_os16) {
        TSK_DEBUG_INFO("*** BFCP Attribute(OctetString16) destroyed ***");

    }
    return self;
}
static int tbfcp_attr_octetstring16_cmp(const tsk_object_t *_att1, const tsk_object_t *_att2)
{
    const tbfcp_attr_octetstring16_t *pc_att1 = (const tbfcp_attr_octetstring16_t *)_att1;
    const tbfcp_attr_octetstring16_t *pc_att2 = (const tbfcp_attr_octetstring16_t *)_att2;

    return (int)(pc_att1-pc_att2);
}
static const tsk_object_def_t tbfcp_attr_octetstring16_def_s = {
    sizeof(tbfcp_attr_octetstring16_t),
    tbfcp_attr_octetstring16_ctor,
    tbfcp_attr_octetstring16_dtor,
    tbfcp_attr_octetstring16_cmp,
};
const tsk_object_def_t *tbfcp_attr_octetstring16_def_t = &tbfcp_attr_octetstring16_def_s;


/*************** tbfcp_attr_octetstring *******************/
int tbfcp_attr_octetstring_create(tbfcp_attribute_type_t type, unsigned M, const uint8_t *OctetString, uint8_t OctetStringLength, tbfcp_attr_octetstring_t** pp_self)
{
    extern const tsk_object_def_t *tbfcp_attr_octetstring_def_t;
    tbfcp_attr_octetstring_t* p_self;
    int ret;
    if (!pp_self) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }
    if (!(p_self = tsk_object_new(tbfcp_attr_octetstring_def_t))) {
        TSK_DEBUG_ERROR("Failed to create 'tbfcp_attr_octetstring_def_t' object");
        return -2;
    }
    if ((ret = _tbfcp_attr_init(TBFCP_ATTR(p_self), type, M, (TBFCP_ATTR_HDR_SIZE_IN_OCTETS + OctetStringLength)))) {
        TSK_OBJECT_SAFE_FREE(p_self);
        return -3;
    }
    if (TBFCP_ATTR(p_self)->format != tbfcp_attribute_format_OctetString) {
        TSK_DEBUG_ERROR("Format mismatch");
        TSK_OBJECT_SAFE_FREE(p_self);
        return -4;
    }
    if (OctetStringLength) {
		#if HAVE_CRT //Debug memory
        if (!(p_self->OctetString = malloc(OctetStringLength))) {
		
		#else
        if (!(p_self->OctetString = tsk_malloc(OctetStringLength))) {
		
		#endif //HAVE_CRT
            TSK_DEBUG_ERROR("Failed to alloc %u octets", OctetStringLength);
            TSK_OBJECT_SAFE_FREE(p_self);
            return -5;
        }
        if (OctetString) {
            memcpy(p_self->OctetString, OctetString, OctetStringLength);
        }
        p_self->OctetStringLength = OctetStringLength;
    }
    else {
        TBFCP_ATTR(p_self)->hdr.length = TBFCP_ATTR_HDR_SIZE_IN_OCTETS;
        p_self->OctetStringLength = 0;
    }
    *pp_self = p_self;
    return 0;
}

static tsk_object_t* tbfcp_attr_octetstring_ctor(tsk_object_t * self, va_list * app)
{
    tbfcp_attr_octetstring_t *p_os = (tbfcp_attr_octetstring_t *)self;
    if (p_os) {
    }
    return self;
}
static tsk_object_t* tbfcp_attr_octetstring_dtor(tsk_object_t * self)
{
    tbfcp_attr_octetstring_t *p_os = (tbfcp_attr_octetstring_t *)self;
    if (p_os) {
        TSK_DEBUG_INFO("*** BFCP Attribute(OctetString) destroyed ***");
        TSK_SAFE_FREE(p_os->OctetString);
    }
    return self;
}
static int tbfcp_attr_octetstring_cmp(const tsk_object_t *_att1, const tsk_object_t *_att2)
{
    const tbfcp_attr_octetstring_t *pc_att1 = (const tbfcp_attr_octetstring_t *)_att1;
    const tbfcp_attr_octetstring_t *pc_att2 = (const tbfcp_attr_octetstring_t *)_att2;

    return (int)(pc_att1-pc_att2);
}
static const tsk_object_def_t tbfcp_attr_octetstring_def_s = {
    sizeof(tbfcp_attr_octetstring_t),
    tbfcp_attr_octetstring_ctor,
    tbfcp_attr_octetstring_dtor,
    tbfcp_attr_octetstring_cmp,
};
const tsk_object_def_t *tbfcp_attr_octetstring_def_t = &tbfcp_attr_octetstring_def_s;



/*************** tbfcp_attr_grouped *******************/
int tbfcp_attr_grouped_create(tbfcp_attribute_type_t type, unsigned M, tbfcp_attr_grouped_t** pp_self)
{
    extern const tsk_object_def_t *tbfcp_attr_grouped_def_t;
    tbfcp_attr_grouped_t* p_self;
    int ret;
    if (!pp_self) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }
    if (!(p_self = tsk_object_new(tbfcp_attr_grouped_def_t))) {
        TSK_DEBUG_ERROR("Failed to create 'tbfcp_attr_grouped_def_t' object");
        return -2;
    }
    if ((ret = _tbfcp_attr_init(TBFCP_ATTR(p_self), type, M, TBFCP_ATTR_HDR_SIZE_IN_OCTETS + 0))) {
        TSK_OBJECT_SAFE_FREE(p_self);
        return -3;
    }
    if (TBFCP_ATTR(p_self)->format != tbfcp_attribute_format_Grouped) {
        TSK_DEBUG_ERROR("Format mismatch");
        TSK_OBJECT_SAFE_FREE(p_self);
        return -4;
    }
    if (!(p_self->p_list_attrs = tsk_list_create())) {
        TSK_DEBUG_ERROR("Failed to create empty list");
        TSK_OBJECT_SAFE_FREE(p_self);
        return -5;
    }

    *pp_self = p_self;
    return 0;
}

int tbfcp_attr_grouped_create_u16(tbfcp_attribute_type_t type, unsigned M, uint16_t extra_hdr_u16_val, tbfcp_attr_grouped_t** pp_self)
{
    int ret;
    if ((ret = tbfcp_attr_grouped_create(type, M, pp_self))) {
        return ret;
    }
    *((uint16_t*)&(*pp_self)->extra_hdr) = extra_hdr_u16_val;
    (*pp_self)->extra_hdr_size_in_octets = 2;
    return 0;
}

int tbfcp_attr_grouped_add_attr(tbfcp_attr_grouped_t* p_self, tbfcp_attr_t** p_attr)
{
    if (!p_self || !p_attr) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }
    tsk_list_push_back_data(p_self->p_list_attrs, (void**)p_attr);
    return 0;
}

int tbfcp_attr_grouped_find_at(const struct tbfcp_attr_grouped_s* pc_self, enum tbfcp_attribute_format_e e_format, tsk_size_t u_index, const struct tbfcp_attr_s** ppc_attr)
{
	tsk_size_t _u_index = 0;
	const tsk_list_item_t *pc_item;
	const struct tbfcp_attr_s* pc_attr;
	if (!pc_self || !ppc_attr) {
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}
	*ppc_attr = tsk_null;
	tsk_list_foreach(pc_item, pc_self->p_list_attrs) {
		pc_attr = (const struct tbfcp_attr_s*)pc_item->data;
		if (!pc_attr || pc_attr->format != e_format) {
			continue;
		}
		if (_u_index++ >= u_index) {
			*ppc_attr = pc_attr;
			break;
		}
	}
	return 0;
}

static tsk_object_t* tbfcp_attr_grouped_ctor(tsk_object_t * self, va_list * app)
{
    tbfcp_attr_grouped_t *p_gr = (tbfcp_attr_grouped_t *)self;
    if (p_gr) {
    }
    return self;
}
static tsk_object_t* tbfcp_attr_grouped_dtor(tsk_object_t * self)
{
    tbfcp_attr_grouped_t *p_gr = (tbfcp_attr_grouped_t *)self;
    if (p_gr) {
        TSK_DEBUG_INFO("*** BFCP Attribute(Grouped) destroyed ***");
        TSK_OBJECT_SAFE_FREE(p_gr->p_list_attrs);
    }
    return self;
}
static int tbfcp_attr_grouped_cmp(const tsk_object_t *_att1, const tsk_object_t *_att2)
{
    const tbfcp_attr_grouped_t *pc_att1 = (const tbfcp_attr_grouped_t *)_att1;
    const tbfcp_attr_grouped_t *pc_att2 = (const tbfcp_attr_grouped_t *)_att2;

    return (int)(pc_att1-pc_att2);
}
static const tsk_object_def_t tbfcp_attr_grouped_def_s = {
    sizeof(tbfcp_attr_grouped_t),
    tbfcp_attr_grouped_ctor,
    tbfcp_attr_grouped_dtor,
    tbfcp_attr_grouped_cmp,
};
const tsk_object_def_t *tbfcp_attr_grouped_def_t = &tbfcp_attr_grouped_def_s;