pick up wakaama files from https://github.com/eclipse/wakaama

core/tlv.c

Committer:
terencez
Date:
2017-04-19
Revision:
0:c2dff8cbb91a

File content as of revision 0:c2dff8cbb91a:

/*******************************************************************************
 *
 * Copyright (c) 2013, 2014 Intel Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 *
 * The Eclipse Public License is available at
 *    http://www.eclipse.org/legal/epl-v10.html
 * The Eclipse Distribution License is available at
 *    http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *    David Navarro, Intel Corporation - initial API and implementation
 *    Fabien Fleutot - Please refer to git log
 *    Bosch Software Innovations GmbH - Please refer to git log
 *    
 *******************************************************************************/

#include "internals.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <inttypes.h>
#include <float.h>

#ifndef LWM2M_BIG_ENDIAN
#ifndef LWM2M_LITTLE_ENDIAN
#error Please define LWM2M_BIG_ENDIAN or LWM2M_LITTLE_ENDIAN
#endif
#endif

#define _PRV_TLV_TYPE_MASK 0xC0
#define _PRV_TLV_HEADER_MAX_LENGTH 6

#define _PRV_TLV_TYPE_UNKNOWN           (uint8_t)0xFF
#define _PRV_TLV_TYPE_OBJECT            (uint8_t)0x10
#define _PRV_TLV_TYPE_OBJECT_INSTANCE   (uint8_t)0x00
#define _PRV_TLV_TYPE_RESOURCE          (uint8_t)0xC0
#define _PRV_TLV_TYPE_MULTIPLE_RESOURCE (uint8_t)0x80
#define _PRV_TLV_TYPE_RESOURCE_INSTANCE (uint8_t)0x40


static uint8_t prv_getHeaderType(lwm2m_data_type_t type)
{
    switch (type)
    {
    case LWM2M_TYPE_OBJECT:
        return _PRV_TLV_TYPE_OBJECT;

    case LWM2M_TYPE_OBJECT_INSTANCE:
        return _PRV_TLV_TYPE_OBJECT_INSTANCE;

    case LWM2M_TYPE_MULTIPLE_RESOURCE:
        return _PRV_TLV_TYPE_MULTIPLE_RESOURCE;


    case LWM2M_TYPE_STRING:
    case LWM2M_TYPE_INTEGER:
    case LWM2M_TYPE_FLOAT:
    case LWM2M_TYPE_BOOLEAN:
    case LWM2M_TYPE_OPAQUE:
    case LWM2M_TYPE_OBJECT_LINK:
        return _PRV_TLV_TYPE_RESOURCE;

    case LWM2M_TYPE_UNDEFINED:
    default:
        return _PRV_TLV_TYPE_UNKNOWN;
    }
}

static lwm2m_data_type_t prv_getDataType(uint8_t type)
{
    switch (type)
    {
    case _PRV_TLV_TYPE_OBJECT:
        return LWM2M_TYPE_OBJECT;

    case _PRV_TLV_TYPE_OBJECT_INSTANCE:
        return LWM2M_TYPE_OBJECT_INSTANCE;

    case _PRV_TLV_TYPE_MULTIPLE_RESOURCE:
        return LWM2M_TYPE_MULTIPLE_RESOURCE;

    case _PRV_TLV_TYPE_RESOURCE:
    case _PRV_TLV_TYPE_RESOURCE_INSTANCE:
        return LWM2M_TYPE_OPAQUE;

    default:
        return LWM2M_TYPE_UNDEFINED;
    }
}

static int prv_getHeaderLength(uint16_t id,
                               size_t dataLen)
{
    int length;

    length = 2;

    if (id > 0xFF)
    {
        length += 1;
    }

    if (dataLen > 0xFFFF)
    {
        length += 3;
    }
    else if (dataLen > 0xFF)
    {
        length += 2;
    }
    else if (dataLen > 7)
    {
        length += 1;
    }

    return length;
}

static int prv_createHeader(uint8_t * header,
                            bool isInstance,
                            lwm2m_data_type_t type,
                            uint16_t id,
                            size_t data_len)
{
    int header_len;
    int offset;
    uint8_t hdrType;

    header_len = prv_getHeaderLength(id, data_len);
    if (isInstance == true)
    {
        hdrType = _PRV_TLV_TYPE_RESOURCE_INSTANCE;
    }
    else
    {
        hdrType = prv_getHeaderType(type);
    }

    header[0] = 0;
    header[0] |= hdrType&_PRV_TLV_TYPE_MASK;

    if (id > 0xFF)
    {
        header[0] |= 0x20;
        header[1] = (id >> 8) & 0XFF;
        header[2] = id & 0XFF;
        offset = 3;
    }
    else
    {
        header[1] = id;
        offset = 2;
    }
    if (data_len <= 7)
    {
        header[0] += data_len;
    }
    else if (data_len <= 0xFF)
    {
        header[0] |= 0x08;
        header[offset] = data_len;
    }
    else if (data_len <= 0xFFFF)
    {
        header[0] |= 0x10;
        header[offset] = (data_len >> 8) & 0XFF;
        header[offset + 1] = data_len & 0XFF;
    }
    else if (data_len <= 0xFFFFFF)
    {
        header[0] |= 0x18;
        header[offset] = (data_len >> 16) & 0XFF;
        header[offset + 1] = (data_len >> 8) & 0XFF;
        header[offset + 2] = data_len & 0XFF;
    }

    return header_len;
}

int lwm2m_decode_TLV(const uint8_t * buffer,
                    size_t buffer_len,
                    lwm2m_data_type_t * oType,
                    uint16_t * oID,
                    size_t * oDataIndex,
                    size_t * oDataLen)
{

    LOG_ARG("buffer_len: %d", buffer_len);
        ;
    if (buffer_len < 2) return 0;

    *oDataIndex = 2;

    *oType = prv_getDataType(buffer[0]&_PRV_TLV_TYPE_MASK);

    if ((buffer[0]&0x20) == 0x20)
    {
        // id is 16 bits long
        if (buffer_len < 3) return 0;
        *oDataIndex += 1;
        *oID = (buffer[1]<<8) + buffer[2];
    }
    else
    {
        // id is 8 bits long
        *oID = buffer[1];
    }

    switch (buffer[0]&0x18)
    {
    case 0x00:
        // no length field
        *oDataLen = buffer[0]&0x07;
        break;
    case 0x08:
        // length field is 8 bits long
        if (buffer_len < *oDataIndex + 1) return 0;
        *oDataLen = buffer[*oDataIndex];
        *oDataIndex += 1;
        break;
    case 0x10:
        // length field is 16 bits long
        if (buffer_len < *oDataIndex + 2) return 0;
        *oDataLen = (buffer[*oDataIndex]<<8) + buffer[*oDataIndex+1];
        *oDataIndex += 2;
        break;
    case 0x18:
        // length field is 24 bits long
        if (buffer_len < *oDataIndex + 3) return 0;
        *oDataLen = (buffer[*oDataIndex]<<16) + (buffer[*oDataIndex+1]<<8) + buffer[*oDataIndex+2];
        *oDataIndex += 3;
        break;
    default:
        // can't happen
        return 0;
    }

    if (*oDataIndex + *oDataLen > buffer_len) return 0;

    return *oDataIndex + *oDataLen;
}


int tlv_parse(uint8_t * buffer,
              size_t bufferLen,
              lwm2m_data_t ** dataP)
{
    lwm2m_data_type_t type;
    uint16_t id;
    size_t dataIndex;
    size_t dataLen;
    int index = 0;
    int result;
    int size = 0;

    LOG_ARG("bufferLen: %d", bufferLen);

    *dataP = NULL;

    while (0 != (result = lwm2m_decode_TLV((uint8_t*)buffer + index, bufferLen - index, &type, &id, &dataIndex, &dataLen)))
    {
        lwm2m_data_t * newTlvP;

        newTlvP = lwm2m_data_new(size + 1);
        if (size >= 1)
        {
            if (newTlvP == NULL)
            {
                lwm2m_data_free(size, *dataP);
                return 0;
            }
            else
            {
                memcpy(newTlvP, *dataP, size * sizeof(lwm2m_data_t));
                lwm2m_free(*dataP);
            }
        }
        *dataP = newTlvP;

        (*dataP)[size].type = type;
        (*dataP)[size].id = id;
        if (type == LWM2M_TYPE_OBJECT_INSTANCE || type == LWM2M_TYPE_MULTIPLE_RESOURCE)
        {
            (*dataP)[size].value.asChildren.count = tlv_parse(buffer + index + dataIndex,
                                                          dataLen,
                                                          &((*dataP)[size].value.asChildren.array));
            if ((*dataP)[size].value.asChildren.count == 0)
            {
                lwm2m_data_free(size + 1, *dataP);
                return 0;
            }
        }
        else
        {
            lwm2m_data_encode_opaque(buffer + index + dataIndex, dataLen, (*dataP) + size);
        }
        size++;
        index += result;
    }

    return size;
}


static int prv_getLength(int size,
                         lwm2m_data_t * dataP)
{
    int length;
    int i;

    length = 0;

    for (i = 0 ; i < size && length != -1 ; i++)
    {
        switch (dataP[i].type)
        {
        case LWM2M_TYPE_OBJECT_INSTANCE:
        case LWM2M_TYPE_MULTIPLE_RESOURCE:
            {
                int subLength;

                subLength = prv_getLength(dataP[i].value.asChildren.count, dataP[i].value.asChildren.array);
                if (subLength == -1)
                {
                    length = -1;
                }
                else
                {
                    length += prv_getHeaderLength(dataP[i].id, subLength) + subLength;
                }
            }
            break;

        case LWM2M_TYPE_STRING:
        case LWM2M_TYPE_OPAQUE:
            length += prv_getHeaderLength(dataP[i].id, dataP[i].value.asBuffer.length) + dataP[i].value.asBuffer.length;
            break;

        case LWM2M_TYPE_INTEGER:
            {
                size_t data_len;
                uint8_t unused_buffer[_PRV_64BIT_BUFFER_SIZE];

                data_len = utils_encodeInt(dataP[i].value.asInteger, unused_buffer);
                length += prv_getHeaderLength(dataP[i].id, data_len) + data_len;
            }
            break;

        case LWM2M_TYPE_FLOAT:
            {
                size_t data_len;

                if ((dataP[i].value.asFloat < 0.0 - (double)FLT_MAX)
                    || (dataP[i].value.asFloat >(double)FLT_MAX))
                {
                    data_len = 8;
                }
                else
                {
                    data_len = 4;
                }

                length += prv_getHeaderLength(dataP[i].id, data_len) + data_len;
            }
            break;

        case LWM2M_TYPE_BOOLEAN:
            // Booleans are always encoded on one byte
            length += prv_getHeaderLength(dataP[i].id, 1) + 1;
            break;

        case LWM2M_TYPE_OBJECT_LINK:
            // Object Link are always encoded on four bytes
            length += prv_getHeaderLength(dataP[i].id, 4) + 4;
            break;

        default:
            length = -1;
            break;
        }
    }

    return length;
}


int tlv_serialize(bool isResourceInstance, 
                  int size,
                  lwm2m_data_t * dataP,
                  uint8_t ** bufferP)
{
    int length;
    int index;
    int i;

    LOG_ARG("isResourceInstance: %s, size: %d", isResourceInstance?"true":"false", size);

    *bufferP = NULL;
    length = prv_getLength(size, dataP);
    if (length <= 0) return length;

    *bufferP = (uint8_t *)lwm2m_malloc(length);
    if (*bufferP == NULL) return 0;

    index = 0;
    for (i = 0 ; i < size && length != 0 ; i++)
    {
        int headerLen;
        bool isInstance;

        isInstance = isResourceInstance;
        switch (dataP[i].type)
        {
        case LWM2M_TYPE_MULTIPLE_RESOURCE:
            isInstance = true;
            // fall through
        case LWM2M_TYPE_OBJECT_INSTANCE:
            {
                uint8_t * tmpBuffer;
                int res;

                res = tlv_serialize(isInstance, dataP[i].value.asChildren.count, dataP[i].value.asChildren.array, &tmpBuffer);
                if (res < 0)
                {
                    length = -1;
                }
                else
                {
                    size_t tmpLength;

                    tmpLength = (size_t)res;
                    headerLen = prv_createHeader(*bufferP + index, false, dataP[i].type, dataP[i].id, tmpLength);
                    index += headerLen;
                    if (tmpLength > 0)
                    {
                        memcpy(*bufferP + index, tmpBuffer, tmpLength);
                        index += tmpLength;
                        lwm2m_free(tmpBuffer);
                    }
                }
            }
            break;

        case LWM2M_TYPE_OBJECT_LINK:
            {
                int k;
                uint8_t buf[4];
                uint32_t v = dataP[i].value.asObjLink.objectId;
                v <<= 16;
                v |= dataP[i].value.asObjLink.objectInstanceId;
                for (k = 3; k >= 0; --k) {
                    buf[k] = (uint8_t)(v & 0xFF);
                    v >>= 8;
                }
                // keep encoding as buffer
                headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, 4);
                index += headerLen;
                memcpy(*bufferP + index, buf, 4);
                index += 4;
            }
            break;

        case LWM2M_TYPE_STRING:
        case LWM2M_TYPE_OPAQUE:
            headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, dataP[i].value.asBuffer.length);
            index += headerLen;
            memcpy(*bufferP + index, dataP[i].value.asBuffer.buffer, dataP[i].value.asBuffer.length);
            index += dataP[i].value.asBuffer.length;
            break;

        case LWM2M_TYPE_INTEGER:
            {
                size_t data_len;
                uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE];

                data_len = utils_encodeInt(dataP[i].value.asInteger, data_buffer);
                headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, data_len);
                index += headerLen;
                memcpy(*bufferP + index, data_buffer, data_len);
                index += data_len;
            }
            break;

        case LWM2M_TYPE_FLOAT:
            {
                size_t data_len;
                uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE];

                data_len = utils_encodeFloat(dataP[i].value.asFloat, data_buffer);
                headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, data_len);
                index += headerLen;
                memcpy(*bufferP + index, data_buffer, data_len);
                index += data_len;
            }
            break;

        case LWM2M_TYPE_BOOLEAN:
            headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, 1);
            index += headerLen;
            (*bufferP)[index] = dataP[i].value.asBoolean ? 1 : 0;
            index += 1;
            break;

        default:
            length = -1;
            break;
        }
    }

    if (length < 0)
    {
        lwm2m_free(*bufferP);
        *bufferP = NULL;
    }

    LOG_ARG("returning %u", length);

    return length;
}