Current section

Files

Jump to
alcove c_src term decode cstruct.c
Raw

c_src/term/decode/cstruct.c

/* Copyright (c) 2014-2017, Michael Santos <michael.santos@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "alcove.h"
static void *alcove_malloc(ssize_t);
int
alcove_decode_cstruct(const char *arg, size_t len, int *index,
char *res, size_t *rlen, alcove_alloc_t **ptr, ssize_t *nptr)
{
int type = 0;
int arity = 0;
size_t tmp_size = 0;
long size = 0;
int tmp_index = 0;
int tmp_arity = 0;
char tmp[MAXMSGLEN] = {0};
unsigned long val = 0;
int i = 0;
size_t n = 0;
size_t total = 0;
*nptr = 0;
if (alcove_decode_list_header(arg, len, index, &arity) < 0)
return -1;
/* C: 5.2.4.1 Translation limits
*
* The implementation shall be able to translate and execute at
* least one program that contains at least one instance of every one
* of the following limits:
*
* - 1023 members in a single structure or union
*
*/
if (arity == 0 || arity > 1023)
return -1;
tmp_index = *index;
tmp_arity = arity;
/* Calculate the size required */
for (i = 0; i < arity; i++) {
if (alcove_get_type(arg, len, &tmp_index, &type, &tmp_arity) < 0)
return -1;
switch (type) {
case ERL_BINARY_EXT:
if (tmp_arity > sizeof(tmp))
return -1;
if (alcove_decode_binary(arg, len, &tmp_index, tmp, &tmp_size) < 0)
return -1;
if (tmp_size > INT32_MAX)
return -1;
n += tmp_size;
total += tmp_size;
break;
case ERL_SMALL_TUPLE_EXT:
if (tmp_arity != 2)
return -1;
if (alcove_decode_tuple_header(arg, len, &tmp_index, &tmp_arity) < 0)
return -1;
if (alcove_decode_atom(arg, len, &tmp_index, tmp) < 0)
return -1;
if (strcmp(tmp, "ptr") != 0)
return -1;
if (alcove_get_type(arg, len, &tmp_index, &type, &tmp_arity) < 0)
return -1;
switch (type) {
case ERL_SMALL_INTEGER_EXT:
case ERL_INTEGER_EXT:
if (alcove_decode_ulong(arg, len, &tmp_index, &val) < 0)
return -1;
n += sizeof(void *);
total += val;
break;
case ERL_BINARY_EXT:
if (alcove_decode_binary(arg, len, &tmp_index, tmp, &tmp_size) < 0)
return -1;
if (tmp_size > INT32_MAX)
return -1;
n += sizeof(void *);
total += tmp_size;
break;
default:
return -1;
}
break;
default:
return -1;
}
}
/* n = number of bytes written to the buffer
* total = number of bytes to be allocated
*
* total may be smaller than n, e.g., pointer to 1 byte
*/
if (n > *rlen || total > *rlen)
return -1;
*rlen = n;
*ptr = alcove_malloc(arity * sizeof(alcove_alloc_t));
*nptr = arity;
/* Copy the list contents */
for (i = 0; i < arity; i++) {
(void)ei_get_type(arg, index, &type, &tmp_arity);
switch (type) {
case ERL_BINARY_EXT:
(void)ei_decode_binary(arg, index, res, &size);
res += size;
(*ptr)[i].p = NULL;
(*ptr)[i].len = size;
break;
case ERL_SMALL_TUPLE_EXT:
(void)ei_decode_tuple_header(arg, index, &tmp_arity);
(void)ei_decode_atom(arg, index, tmp);
(void)ei_get_type(arg, index, &type, &tmp_arity);
switch (type) {
case ERL_SMALL_INTEGER_EXT:
case ERL_INTEGER_EXT: {
char *p = NULL;
(void)ei_decode_ulong(arg, index, &val);
if (val > 0) {
p = calloc(val, 1);
if (p == NULL)
exit(errno);
(*ptr)[i].len = val;
}
else {
/* NULL pointer: return a binary */
(*ptr)[i].len = sizeof(void *);
}
(void)memcpy(res, &p, sizeof(void *));
res += sizeof(void *);
(*ptr)[i].p = p;
}
break;
case ERL_BINARY_EXT: {
char *p = NULL;
(void)ei_decode_binary(arg, index, tmp, &size);
if (size > 0) {
p = alcove_malloc(size);
(void)memcpy(p, tmp, size);
(*ptr)[i].len = size;
}
else {
/* NULL pointer: return a binary */
(*ptr)[i].len = sizeof(void *);
}
(void)memcpy(res, &p, sizeof(void *));
res += sizeof(void *);
(*ptr)[i].p = p;
}
break;
}
}
}
return 0;
}
static void *
alcove_malloc(ssize_t size)
{
void *buf = NULL;
if (size <= 0 || size >= INT32_MAX)
exit(ENOMEM);
buf = malloc(size);
if (buf == NULL)
exit(ENOMEM);
return buf;
}