Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function to turn all items into definite length equivalents #51

Open
PJK opened this issue Jan 14, 2017 · 0 comments
Open

Function to turn all items into definite length equivalents #51

PJK opened this issue Jan 14, 2017 · 0 comments
Labels

Comments

@PJK
Copy link
Owner

PJK commented Jan 14, 2017

More constrained clients benefit from knowing the sizes upfront.

Jacob Teplitsky has contributed the following snippet. It should be quite easy to integrate it.

#include <string.h>
#include "platform.h"
#include "cbor.h"
#include "cbor/internal/builder_callbacks.h"
#include "cbor/internal/loaders.h"
#include "cJSON.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#ifndef CBOR_LOAD_CALLBACK_T_DEFINED
typedef struct cbor_decoder_result(*cbor_load_callback_t)(void *, size_t,  const struct cbor_callbacks *, void *);
#endif


cbor_item_t *cjson_cbor_load(void *source,
                             size_t source_size,
                             struct cbor_load_result *result,
                             cbor_load_callback_t *cbor_load_callback)
{
	/* Context stack */
	static struct cbor_callbacks callbacks = {
		.uint8 = &cbor_builder_uint8_callback,
		.uint16 = &cbor_builder_uint16_callback,
		.uint32 = &cbor_builder_uint32_callback,
		.uint64 = &cbor_builder_uint64_callback,

		.negint8 = &cbor_builder_negint8_callback,
		.negint16 = &cbor_builder_negint16_callback,
		.negint32 = &cbor_builder_negint32_callback,
		.negint64 = &cbor_builder_negint64_callback,

		.byte_string = &cbor_builder_byte_string_callback,
		.byte_string_start = &cbor_builder_byte_string_start_callback,

		.string = &cbor_builder_string_callback,
		.string_start = &cbor_builder_string_start_callback,

		.array_start = &cbor_builder_array_start_callback,
		.indef_array_start = &cbor_builder_indef_array_start_callback,

		.map_start = &cbor_builder_map_start_callback,
		.indef_map_start = &cbor_builder_indef_map_start_callback,

		.tag = &cbor_builder_tag_callback,

		.null = &cbor_builder_null_callback,
		.undefined = &cbor_builder_undefined_callback,
		.boolean = &cbor_builder_boolean_callback,
		.float2 = &cbor_builder_float2_callback,
		.float4 = &cbor_builder_float4_callback,
		.float8 = &cbor_builder_float8_callback,
		.indef_break = &cbor_builder_indef_break_callback
	};

	if (source_size == 0) {
		result->error.code = CBOR_ERR_NODATA;
		return NULL;
	}
	struct _cbor_stack stack = _cbor_stack_init();

	/* Target for callbacks */
	struct _cbor_decoder_context context = (struct _cbor_decoder_context) {
		.stack = &stack,
		.creation_failed = false,
		.syntax_error = false
	};
	struct cbor_decoder_result decode_result;
	*result = (struct cbor_load_result) {.read = 0, .error = {.code = CBOR_ERR_NONE}};

	do {
		if (source_size > result->read) { /* Check for overflows */
			decode_result = (*cbor_load_callback)(
				source, 24,
				&callbacks,
				&context);
		} else {
			result->error = (struct cbor_error) {
				.code = CBOR_ERR_NOTENOUGHDATA,
				.position = result->read
			};
			goto error;
		}

		switch (decode_result.status) {
		case CBOR_DECODER_FINISHED:
			/* Everything OK */
		{
			result->read += decode_result.read;
			break;
		}
		case CBOR_DECODER_NEDATA:
			/* Data length doesn't match MTB expectation */
		{
			result->error.code = CBOR_ERR_NOTENOUGHDATA;
			goto error;
		}
		case CBOR_DECODER_EBUFFER:
			/* Fallthrough */
		case CBOR_DECODER_ERROR:
			/* Reserved/malformated item */
		{
			result->error.code = CBOR_ERR_MALFORMATED;
			goto error;
		}
		}

		if (context.creation_failed) {
			/* Most likely unsuccessful allocation - our callback has failed */
			result->error.code = CBOR_ERR_MEMERROR;
			goto error;
		} else if (context.syntax_error) {
			result->error.code = CBOR_ERR_SYNTAXERROR;
			goto error;
		}
	} while (stack.size > 0);

	/* Move the result before free */
	cbor_item_t *result_item = context.root;
	return result_item;

	error:
	result->error.position = result->read;
	/*debug_print("Failed with decoder error %d at %d\n",
     * result->error.code, result->error.position); */
	/* cbor_describe(stack.top->item, stdout); */
	/* Free the stack */
	while (stack.size > 0) {
		cbor_decref(&stack.top->item);
		_cbor_stack_pop(&stack);
	}
	return NULL;
}

struct cbor_decoder_result cjson_cbor_stream_decode(void *source_void, size_t source_size,
                                                    const struct cbor_callbacks *callbacks,
                                                    void *context)
{
    cJSON *source = source_void;
	/* If we have no data, we cannot read even the MTB */
	if (source_size < 1) {
		return (struct cbor_decoder_result) {0, CBOR_DECODER_EBUFFER};
	}

	/* If we have a byte, assume it's the MTB */
	struct cbor_decoder_result result = {1, CBOR_DECODER_FINISHED};

    const char *name = source->string;
    if (name) {
        callbacks->string(context, (unsigned char *)name, strlen(name));
    }
	switch (source->type) {
      case cJSON_False:
        {
		callbacks->boolean(context, false);
		return result;
        }
      case cJSON_True:
        {
            callbacks->boolean(context, false);
            return result;
        }
        /* case cJSON_NULL: */
      case cJSON_Number:
        {
			callbacks->uint32(context, source->valueint);
            return result;
        }
      case cJSON_String:
        {
            int                 res;
            struct sockaddr_in  sw_addr;
            size_t              len = 0;
            const char         *str;

            str = source->valuestring;
            res = inet_pton(AF_INET, str, &sw_addr.sin_addr);
            if (res > 0) {
                len = 4;
            } else {
                res = inet_pton(AF_INET6, str, &sw_addr.sin_addr);
                if (res > 0) {
                    len = 16;
                }
            }
            if (len == 0) {
                callbacks->string(context, (unsigned char *)source->valuestring, strlen(source->valuestring));
            } else {
                callbacks->byte_string(context, (unsigned char *)&sw_addr.sin_addr, len);
            }
            return result;
        }
      case cJSON_Array:
        {
            callbacks->array_start(context, cJSON_GetArraySize(source));
            cJSON *item;
            cJSON_Foreach_Array_Item_Begin(source, item)
            {
                cjson_cbor_stream_decode(item , 2, callbacks, context);

            }
            cJSON_Foreach_Array_Item_End(source, item)
            return result;
        }
      case cJSON_Object:
        {
			callbacks->map_start(context, cJSON_GetArraySize(source));
            cJSON *item;
            cJSON_Foreach_Object_Item_Begin(source, item)
            {
                cjson_cbor_stream_decode(item , 2, callbacks, context);

            }
            cJSON_Foreach_Object_Item_End(source, item)
            return result;
        }
      default: /* Never happens - this shuts up the compiler */
        {
            return result;
        }
	}
}

cbor_item_t *
cjson_to_cbor(cJSON *json)
{
    struct cbor_load_result result;
    cbor_load_callback_t call = &cjson_cbor_stream_decode;
    return (cjson_cbor_load((void *)json, 10, &result, &call));
}

static cbor_item_t * _cbor_copy_int(cbor_item_t * item, bool negative)
{
	cbor_item_t * res;
	switch (cbor_int_get_width(item)) {
	case CBOR_INT_8: res = cbor_build_uint8(cbor_get_uint8(item)); break;
	case CBOR_INT_16: res = cbor_build_uint16(cbor_get_uint16(item)); break;
	case CBOR_INT_32: res = cbor_build_uint32(cbor_get_uint32(item)); break;
	case CBOR_INT_64: res = cbor_build_uint64(cbor_get_uint64(item)); break;
	default: return NULL;
	}

	if (negative)
		cbor_mark_negint(res);

	return res;
}

static cbor_item_t * _cbor_copy_float_ctrl(cbor_item_t * item)
{
	switch (cbor_float_get_width(item)) {
	case CBOR_FLOAT_0:
		return cbor_build_ctrl(cbor_ctrl_value(item));
	case CBOR_FLOAT_16:
		return cbor_build_float2(cbor_float_get_float2(item));
	case CBOR_FLOAT_32:
		return cbor_build_float4(cbor_float_get_float4(item));
	case CBOR_FLOAT_64:
		return cbor_build_float8(cbor_float_get_float8(item));
	}

	return NULL;
}

cbor_item_t *cbor_copy_definite(cbor_item_t * item)
{
    if (item == NULL) {
        return (NULL);
    }
	switch (cbor_typeof(item)) {
	case CBOR_TYPE_UINT:
		return _cbor_copy_int(item, false);
	case CBOR_TYPE_NEGINT:
		return _cbor_copy_int(item, true);
	case CBOR_TYPE_BYTESTRING:
		if (cbor_bytestring_is_definite(item)) {
			return cbor_build_bytestring(cbor_bytestring_handle(item), cbor_bytestring_length(item));
		} else {
            size_t i;
			cbor_item_t * res = cbor_new_indefinite_bytestring();
			for (i = 0; i < cbor_bytestring_chunk_count(item); i++)
				cbor_bytestring_add_chunk(
					res,
					cbor_move(
						cbor_copy_definite(cbor_bytestring_chunks_handle(item)[i])
					)
				);
			return res;
		}
	case CBOR_TYPE_STRING:
		if (cbor_string_is_definite(item)) {
			return cbor_build_stringn((const char *) cbor_string_handle(item), cbor_string_length(item));
		} else {
            size_t i;
			cbor_item_t * res = cbor_new_indefinite_string();
			for (i = 0; i < cbor_string_chunk_count(item); i++)
				cbor_string_add_chunk(
					res,
					cbor_move(
						cbor_copy_definite(cbor_string_chunks_handle(item)[i])
					)
				);
			return res;
		}
	case CBOR_TYPE_ARRAY: {
		cbor_item_t * res;
        size_t i;

        if (cbor_array_size(item) == 0) {
            res = cbor_new_null();
        } else {
            res = cbor_new_definite_array(cbor_array_size(item));
            for (i = 0; i < cbor_array_size(item); i++)
                cbor_array_push(
                    res,
                    cbor_move(cbor_copy_definite(cbor_move(cbor_array_get(item, i))))
                    );
        }
		return res;
	}
	case CBOR_TYPE_MAP: {
		cbor_item_t * res;
        size_t i;

        res = cbor_new_definite_map(cbor_map_size(item));

		struct cbor_pair * it = cbor_map_handle(item);
		for (i = 0; i < cbor_map_size(item); i++)
			cbor_map_add(res, (struct cbor_pair) {
				.key = cbor_move(cbor_copy_definite(it[i].key)),
				.value = cbor_move(cbor_copy_definite(it[i].value))
			});
		return res;
	}
	case CBOR_TYPE_TAG:
		return cbor_build_tag(
			cbor_tag_value(item),
			cbor_move(cbor_copy(cbor_tag_item(item)))
		);
	case CBOR_TYPE_FLOAT_CTRL:
		return _cbor_copy_float_ctrl(item);
	}

	return NULL;
}
@PJK PJK added the Feature label Jan 14, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant