Repository: celix Updated Branches: refs/heads/feature/CELIX-237_rsa-ffi 7f3383247 -> bdeba2bcb
http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/dyn_type.c ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/dyn_type.c b/remote_services/dynamic_function_interface/dyn_type.c new file mode 100644 index 0000000..5ad18f4 --- /dev/null +++ b/remote_services/dynamic_function_interface/dyn_type.c @@ -0,0 +1,822 @@ +/** + * Licensed under Apache License v2. See LICENSE for more information. + */ +#include "dyn_type.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <errno.h> + +static int dynType_createWithStream(FILE *stream, dyn_type *parent, dyn_type **type); +static void dynType_clear(dyn_type *type); +static void dynType_clearComplex(dyn_type *type); +static void dynType_clearSequence(dyn_type *type); +static void dynType_clearTypedPointer(dyn_type *type); + +static ffi_type * dynType_ffiTypeFor(int c); +static dyn_type * dynType_findType(dyn_type *type, char *name); +static int dynType_parse(FILE *stream, dyn_type *type); +static int dynType_parseComplex(FILE *stream, dyn_type *type); +static int dynType_parseName(FILE *stream, char **name); +static int dynType_parseNestedType(FILE *stream, dyn_type *type); +static int dynType_parseReference(FILE *stream, dyn_type *type); +static int dynType_parseRefByValue(FILE *stream, dyn_type *type); +static int dynType_parseSequence(FILE *stream, dyn_type *type); +static int dynType_parseSimple(int c, dyn_type *type); +static int dynType_parseTypedPointer(FILE *stream, dyn_type *type); +static void dynType_prepCif(ffi_type *type); +static unsigned short dynType_getOffset(dyn_type *type, int index); + +static void dynType_printAny(char *name, dyn_type *type, int depth); +static void dynType_printComplex(char *name, dyn_type *type, int depth); +static void dynType_printSequence(char *name, dyn_type *type, int depth); +static void dynType_printSimple(char *name, dyn_type *type, int depth); +static void dynType_printTypedPointer(char *name, dyn_type *type, int depth); +static void printDepth(int depth); + +static void dynType_printTypes(dyn_type *type); +static void dynType_printComplexType(dyn_type *type); +static void dynType_printSimpleType(dyn_type *type); + +struct generic_sequence { + uint32_t cap; + uint32_t len; + void *buf; +}; + +static const int DT_OK = 0; +static const int DT_ERROR = 1; +static const int DT_MEM_ERROR = 2; +static const int DT_PARSE_ERROR = 3; + +#define DT_LOG_ERROR 1 +#define DT_LOG_WARNING 2 +#define DT_LOG_INFO 3 +#define DT_LOG_DEBUG 4 +#define DT_LOG(lvl, msg, ...) if(g_logf != NULL) { \ + g_logf(g_logHandle, (lvl), __FILE__, __LINE__, (msg), ##__VA_ARGS__); \ +} + +static void (*g_logf)(void *handle, int level, const char *file, int line, const char *msgFormat, ...) = NULL; +static void *g_logHandle = NULL; + +void dynType_setupLogger(void (*logf)(void *handle, int level, const char *file, int line, const char *msgFormat, ...), void *handle) { + g_logHandle = handle; + g_logf = logf; +} + +int dynType_create(const char *descriptor, dyn_type **type) { + int status = DT_OK; + FILE *stream = fmemopen((char *)descriptor, strlen(descriptor), "r"); + if (stream != NULL) { + status = dynType_createWithStream(stream, NULL, type); + if (status == DT_OK) { + int c = fgetc(stream); + if (c != '\0' && c != EOF) { + status = DT_PARSE_ERROR; + DT_LOG(DT_LOG_ERROR, "Expected EOF got %c", c); + } + } + fclose(stream); + } else { + status = DT_ERROR; + DT_LOG(DT_LOG_ERROR, "Error creating mem stream for descriptor string. %s", strerror(errno)); + } + return status; +} + +static int dynType_createWithStream(FILE *stream, dyn_type *parent, dyn_type **result) { + int status = DT_OK; + dyn_type *type = calloc(1, sizeof(*type)); + if (type != NULL) { + type->parent = parent; + type->type = DYN_TYPE_INVALID; + TAILQ_INIT(&type->nestedTypesHead); + status = dynType_parse(stream, type); + if (status == DT_OK) { + *result = type; + } else { + dynType_destroy(type); + } + } else { + status = DT_MEM_ERROR; + DT_LOG(DT_LOG_ERROR, "Error allocating memory for type"); + } + return status; +} + +static int dynType_parse(FILE *stream, dyn_type *type) { + int status = DT_OK; + + int c = fgetc(stream); + switch(c) { + case 'T' : + status = dynType_parseNestedType(stream, type); + if (status == DT_OK) { + status = dynType_parse(stream, type); + } + break; + case 'L' : + status = dynType_parseReference(stream, type); + break; + case 'l' : + status = dynType_parseRefByValue(stream, type); + break; + case '{' : + status = dynType_parseComplex(stream, type); + break; + case '[' : + status = dynType_parseSequence(stream, type); + break; + case '*' : + status = dynType_parseTypedPointer(stream, type); + break; + default : + status = dynType_parseSimple(c, type); + break; + } + + return status; +} + +static int dynType_parseComplex(FILE *stream, dyn_type *type) { + int status = DT_OK; + type->type = DYN_TYPE_COMPLEX; + type->descriptor = '{'; + type->ffiType = &type->complex.structType; + TAILQ_INIT(&type->complex.entriesHead); + + int c = fgetc(stream); + struct complex_type_entry *entry = NULL; + while (c != ' ' && c != '}') { + ungetc(c,stream); + entry = calloc(1, sizeof(*entry)); + if (entry != NULL) { + entry->type.parent = type; + entry->type.type = DYN_TYPE_INVALID; + TAILQ_INIT(&entry->type.nestedTypesHead); + TAILQ_INSERT_TAIL(&type->complex.entriesHead, entry, entries); + status = dynType_parse(stream, &entry->type); + } else { + status = DT_MEM_ERROR; + DT_LOG(DT_LOG_ERROR, "Error allocating memory for type"); + } + c = fgetc(stream); + } + + entry = TAILQ_FIRST(&type->complex.entriesHead); + char *name = NULL; + while (c == ' ' && entry != NULL) { + status = dynType_parseName(stream, &name); + if (status == DT_OK) { + entry->name = name; + entry = TAILQ_NEXT(entry, entries); + } else { + break; + } + c = getc(stream); + } + + int count = 0; + TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { + count +=1; + } + + if (status == DT_OK) { + type->complex.structType.type = FFI_TYPE_STRUCT; + type->complex.structType.elements = calloc(count + 1, sizeof(ffi_type)); + type->complex.structType.elements[count] = NULL; + if (type->complex.structType.elements != NULL) { + int index = 0; + TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { + type->complex.structType.elements[index++] = entry->type.ffiType; + } + } else { + status = DT_MEM_ERROR; + //T\nODO log: error allocating memory + } + } + + if (status == DT_OK) { + type->complex.types = calloc(count, sizeof(dyn_type *)); + if (type != NULL) { + int index = 0; + TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { + type->complex.types[index++] = &entry->type; + } + } else { + status = DT_MEM_ERROR; + DT_LOG(DT_LOG_ERROR, "Error allocating memory for type") + } + } + + if (status == DT_OK) { + dynType_prepCif(type->ffiType); + } + + + return status; +} + +static int dynType_parseName(FILE *stream, char **result) { + int status = DT_OK; + + char *buf = NULL; + size_t size = 0; + FILE *name = open_memstream(&buf, &size); + + if (name != NULL) { + int c = getc(stream); + while (isalnum(c) || c == '_') { + fputc(c, name); + c = getc(stream); + } + fflush(name); + fclose(name); + *result = buf; + ungetc(c, stream); + } else { + status = DT_ERROR; + DT_LOG(DT_LOG_ERROR, "Error creating mem stream for name. %s", strerror(errno)); + } + + return status; +} + +static int dynType_parseNestedType(FILE *stream, dyn_type *type) { + int status = DT_OK; + char *name = NULL; + struct nested_entry *entry = NULL; + + entry = calloc(1, sizeof(*entry)); + if (entry != NULL) { + entry->type.parent = type; + entry->type.type = DYN_TYPE_INVALID; + TAILQ_INIT(&entry->type.nestedTypesHead); + TAILQ_INSERT_TAIL(&type->nestedTypesHead, entry, entries); + status = dynType_parseName(stream, &name); + entry->type.name = name; + } else { + status = DT_MEM_ERROR; + } + + if (status == DT_OK) { + int c = fgetc(stream); + if (c != '=') { + status = DT_PARSE_ERROR; + DT_LOG(DT_LOG_ERROR, "Error parsing nested type expected '=' got '%c'", c); + } + } + + if (status == DT_OK) { + status = dynType_parse(stream, &entry->type); + int c = fgetc(stream); + if (c != ';') { + status = DT_PARSE_ERROR; + DT_LOG(DT_LOG_ERROR, "Expected ';' got '%c'\n", c); + } + } + + return status; +} + +static int dynType_parseReference(FILE *stream, dyn_type *type) { + int status = DT_OK; + type->type = DYN_TYPE_TYPED_POINTER; + type->descriptor = '*'; + + type->ffiType = &ffi_type_pointer; + type->typedPointer.typedType = NULL; + + dyn_type *subType = calloc(1, sizeof(*subType)); + + if (subType != NULL) { + type->typedPointer.typedType = subType; + subType->parent = type; + subType->type = DYN_TYPE_INVALID; + TAILQ_INIT(&subType->nestedTypesHead); + status = dynType_parseRefByValue(stream, subType); + } else { + status = DT_MEM_ERROR; + DT_LOG(DT_LOG_ERROR, "Error allocating memory for subtype\n"); + } + + return status; +} + +static int dynType_parseRefByValue(FILE *stream, dyn_type *type) { + int status = DT_OK; + type->type = DYN_TYPE_REF; + type->descriptor = 'l'; + + char *name = NULL; + status = dynType_parseName(stream, &name); + if (status == DT_OK) { + dyn_type *ref = dynType_findType(type, name); + free(name); + if (ref != NULL) { + type->ref.ref = ref; + } else { + status = DT_PARSE_ERROR; + DT_LOG(DT_LOG_ERROR, "Error cannot find type '%s'", name); + } + } + + if (status == DT_OK) { + int c = fgetc(stream); + if (c != ';') { + status = DT_PARSE_ERROR; + DT_LOG(DT_LOG_ERROR, "Error expected ';' got '%c'", c); + } + } + + return status; +} + +static ffi_type *seq_types[] = {&ffi_type_uint32, &ffi_type_uint32, &ffi_type_pointer, NULL}; + +static int dynType_parseSequence(FILE *stream, dyn_type *type) { + int status = DT_OK; + type->type = DYN_TYPE_SEQUENCE; + type->descriptor = '['; + + type->sequence.seqType.elements = seq_types; + status = dynType_createWithStream(stream, type, &type->sequence.itemType); + + if (status == DT_OK) { + type->ffiType = &type->sequence.seqType; + dynType_prepCif(&type->sequence.seqType); + } + + return status; +} + +static int dynType_parseSimple(int c, dyn_type *type) { + int status = DT_OK; + ffi_type *ffiType = dynType_ffiTypeFor(c); + if (ffiType != NULL) { + type->type = DYN_TYPE_SIMPLE; + type->descriptor = c; + type->ffiType = ffiType; + } else { + status = DT_PARSE_ERROR; + DT_LOG(DT_LOG_ERROR, "Error unsupported type '%c'", c); + } + + return status; +} + +static int dynType_parseTypedPointer(FILE *stream, dyn_type *type) { + int status = DT_OK; + type->type = DYN_TYPE_TYPED_POINTER; + type->descriptor = '*'; + type->ffiType = &ffi_type_pointer; + + status = dynType_createWithStream(stream, type, &type->typedPointer.typedType); + + return status; +} + +static void dynType_prepCif(ffi_type *type) { + ffi_cif cif; + ffi_type *args[1]; + args[0] = type; + ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_uint, args); +} + +void dynType_destroy(dyn_type *type) { + if (type != NULL) { + dynType_clear(type); + free(type); + } +} + +static void dynType_clear(dyn_type *type) { + struct nested_entry *entry = TAILQ_FIRST(&type->nestedTypesHead); + struct nested_entry *tmp = NULL; + while (entry != NULL) { + tmp = entry; + entry = TAILQ_NEXT(entry, entries); + dynType_clear(&tmp->type); + free(tmp); + } + + switch (type->type) { + case DYN_TYPE_COMPLEX : + dynType_clearComplex(type); + break; + case DYN_TYPE_SEQUENCE : + dynType_clearSequence(type); + break; + case DYN_TYPE_TYPED_POINTER : + dynType_clearTypedPointer(type); + break; + } + + if (type->name != NULL) { + free(type->name); + } +} + +static void dynType_clearComplex(dyn_type *type) { + assert(type->type == DYN_TYPE_COMPLEX); + struct complex_type_entry *entry = TAILQ_FIRST(&type->complex.entriesHead); + struct complex_type_entry *tmp = NULL; + while (entry != NULL) { + dynType_clear(&entry->type); + if (entry->name != NULL) { + free(entry->name); + } + tmp = entry; + entry = TAILQ_NEXT(entry, entries); + free(tmp); + } + if (type->complex.types != NULL) { + free(type->complex.types); + } + if (type->complex.structType.elements != NULL) { + free(type->complex.structType.elements); + } +} + +static void dynType_clearSequence(dyn_type *type) { + assert(type->type == DYN_TYPE_SEQUENCE); + if (type->sequence.itemType != NULL) { + dynType_destroy(type->sequence.itemType); + } +} + +static void dynType_clearTypedPointer(dyn_type *type) { + assert(type->type == DYN_TYPE_TYPED_POINTER); + if (type->typedPointer.typedType != NULL) { + dynType_destroy(type->typedPointer.typedType); + } +} + +int dynType_alloc(dyn_type *type, void **bufLoc) { + int status = DT_OK; + void *inst = calloc(1, type->ffiType->size); + if (inst != NULL) { + *bufLoc = inst; + } else { + status = DT_MEM_ERROR; + //TODO log + } + return status; +} + + +int dynType_complex_indexForName(dyn_type *type, const char *name) { + printf("descriptor is %c\n", type->descriptor); + assert(type->type == DYN_TYPE_COMPLEX); + int i = 0; + int index = -1; + struct complex_type_entry *entry = NULL; + TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { + if (strcmp(name, entry->name) == 0) { + index = i; + } + i +=1; + } + return index; +} + +char dynType_complex_descriptorTypeAt(dyn_type *type, int index) { + assert(type->type == DYN_TYPE_COMPLEX); + + dyn_type *item = type->complex.types[index]; + return item->descriptor; +} + +int dynType_complex_dynTypeAt(dyn_type *type, int index, dyn_type **result) { + assert(type->type == DYN_TYPE_COMPLEX); + *result = type->complex.types[index]; + return 0; +} + +int dynType_complex_setValueAt(dyn_type *type, int index, void *start, void *in) { + assert(type->type == DYN_TYPE_COMPLEX); + char *loc = ((char *)start) + dynType_getOffset(type, index); + size_t size = type->complex.structType.elements[index]->size; + memcpy(loc, in, size); + return 0; +} + +int dynType_complex_valLocAt(dyn_type *type, int index, void *inst, void **result) { + assert(type->type == DYN_TYPE_COMPLEX); + char *l = (char *)inst; + void *loc = (void *)(l + dynType_getOffset(type, index)); + *result = loc; + return 0; +} + +//sequence +int dynType_sequence_alloc(dyn_type *type, void *inst, int cap, void **out) { + assert(type->type == DYN_TYPE_SEQUENCE); + int status = DT_OK; + struct generic_sequence *seq = inst; + if (seq != NULL) { + size_t size = dynType_size(type->sequence.itemType); + seq->buf = calloc(cap, size); + if (seq->buf != NULL) { + seq->cap = cap; + seq->len = 0; + *out = seq->buf; + } else { + seq->cap = 0; + status = DT_MEM_ERROR; + DT_LOG(DT_LOG_ERROR, "Error allocating memory for buf") + } + } else { + status = DT_MEM_ERROR; + DT_LOG(DT_LOG_ERROR, "Error allocating memory for seq") + } + return status; +} + +int dynType_sequence_append(dyn_type *type, void *inst, void *in) { + assert(type->type == DYN_TYPE_SEQUENCE); + int status = DT_OK; + int index = -1; + struct generic_sequence *seq = inst; + if (seq->len + 1 <= seq->cap) { + index = seq->len; + seq->len += 1; + char *buf = seq->buf; + size_t elSize = dynType_size(type->sequence.itemType); + size_t offset = (elSize * index); + memcpy(buf + offset, in, elSize); + } else { + status = DT_ERROR; + DT_LOG(DT_LOG_ERROR, "Sequence out of capacity") + } + return status; +} + +dyn_type * dynType_sequence_itemType(dyn_type *type) { + assert(type->type == DYN_TYPE_SEQUENCE); + return type->sequence.itemType; +} + +void dynType_simple_setValue(dyn_type *type, void *inst, void *in) { + size_t size = dynType_size(type); + memcpy(inst, in, size); +} + + +int dynType_descriptorType(dyn_type *type) { + return type->descriptor; +} + +static ffi_type * dynType_ffiTypeFor(int c) { + ffi_type *type = NULL; + switch (c) { + case 'F' : + type = &ffi_type_float; + break; + case 'D' : + type = &ffi_type_double; + break; + case 'B' : + type = &ffi_type_sint8; + break; + case 'b' : + type = &ffi_type_uint8; + break; + case 'S' : + type = &ffi_type_sint16; + break; + case 's' : + type = &ffi_type_uint16; + break; + case 'I' : + type = &ffi_type_sint32; + break; + case 'i' : + type = &ffi_type_uint32; + break; + case 'J' : + type = &ffi_type_sint64; + break; + case 'j' : + type = &ffi_type_sint64; + break; + case 'N' : + type = &ffi_type_sint; + break; + case 'P' : + type = &ffi_type_pointer; + break; + } + return type; +} + +static dyn_type * dynType_findType(dyn_type *type, char *name) { + dyn_type *result = NULL; + + struct nested_entry *entry = NULL; + TAILQ_FOREACH(entry, &type->nestedTypesHead, entries) { + if (strcmp(name, entry->type.name) == 0) { + result = &entry->type; + break; + } + } + + if (result == NULL && type->parent != NULL) { + result = dynType_findType(type->parent, name); + } + + return result; +} + +static unsigned short dynType_getOffset(dyn_type *type, int index) { + assert(type->type == DYN_TYPE_COMPLEX); + unsigned short offset = 0; + + ffi_type *ffiType = &type->complex.structType; + int i; + for (i = 0; i <= index && ffiType->elements[i] != NULL; i += 1) { + size_t size = ffiType->elements[i]->size; + unsigned short alignment = ffiType->elements[i]->alignment; + int alignment_diff = offset % alignment; + if (alignment_diff > 0) { + offset += (alignment - alignment_diff); + } + if (i < index) { + offset += size; + } + } + + return offset; +} + +size_t dynType_size(dyn_type *type) { + return type->ffiType->size; +} + +int dynType_type(dyn_type *type) { + return type->type; +} + +void dynType_print(dyn_type *type) { + if (type != NULL) { + dynType_printTypes(type); + + printf("main type:\n"); + dynType_printAny("root", type, 0); + } else { + printf("invalid type\n"); + } +} + +static void printDepth(int depth) { + int i; + for (i = 0; i < depth; i +=1 ) { + printf("\t"); + } +} + +static void dynType_printAny(char *name, dyn_type *type, int depth) { + dyn_type *toPrint = type; + if (toPrint->type == DYN_TYPE_REF) { + toPrint = toPrint->ref.ref; + } + switch(toPrint->type) { + case DYN_TYPE_COMPLEX : + dynType_printComplex(name, toPrint, depth); + break; + case DYN_TYPE_SIMPLE : + dynType_printSimple(name, toPrint, depth); + break; + case DYN_TYPE_SEQUENCE : + dynType_printSequence(name, toPrint, depth); + break; + case DYN_TYPE_TYPED_POINTER : + dynType_printTypedPointer(name, toPrint, depth); + break; + default : + printf("TODO Unsupported type %i\n", toPrint->type); + break; + } +} + +static void dynType_printComplex(char *name, dyn_type *type, int depth) { + if (type->name == NULL) { + printDepth(depth); + printf("%s: complex type (anon), size is %zu, alignment is %i, descriptor is '%c'. fields:\n", name, type->ffiType->size, type->ffiType->alignment, type->descriptor); + + struct complex_type_entry *entry = NULL; + TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { + dynType_printAny(entry->name, &entry->type, depth + 1); + } + + printDepth(depth); + printf("}\n"); + } else { + printDepth(depth); + printf("%s: complex type ('%s'), size is %zu, alignment is %i, descriptor is '%c'.\n", name, type->name, type->ffiType->size, type->ffiType->alignment, type->descriptor); + } +} + +static void dynType_printSequence(char *name, dyn_type *type, int depth) { + printDepth(depth); + printf("sequence, size is %zu, alignment is %i, descriptor is '%c'. fields:\n", type->ffiType->size, type->ffiType->alignment, type->descriptor); + + printDepth(depth + 1); + printf("cap: simple type, size is %zu, alignment is %i.\n", type->sequence.seqType.elements[0]->size, type->sequence.seqType.elements[0]->alignment); + + printDepth(depth + 1); + printf("len: simple type, size is %zu, alignment is %i.\n", type->sequence.seqType.elements[1]->size, type->sequence.seqType.elements[1]->alignment); + + printDepth(depth + 1); + printf("buf: array, size is %zu, alignment is %i. points to ->\n", type->sequence.seqType.elements[2]->size, type->sequence.seqType.elements[2]->alignment); + dynType_printAny("element", type->sequence.itemType, depth + 1); +} + +static void dynType_printSimple(char *name, dyn_type *type, int depth) { + printDepth(depth); + printf("%s: simple type, size is %zu, alignment is %i, descriptor is '%c'.\n", name, type->ffiType->size, type->ffiType->alignment, type->descriptor); +} + +static void dynType_printTypedPointer(char *name, dyn_type *type, int depth) { + printDepth(depth); + printf("%s: typed pointer, size is %zu, alignment is %i, points to ->\n", name, type->ffiType->size, type->ffiType->alignment); + char *subName = NULL; + if (name != NULL) { + char buf[128]; + snprintf(buf, 128, "*%s", name); + subName = buf; + } + dynType_printAny(subName, type->typedPointer.typedType, depth + 1); +} + +static void dynType_printTypes(dyn_type *type) { + + dyn_type *parent = type->parent; + struct nested_entry *pentry = NULL; + while (parent != NULL) { + TAILQ_FOREACH(pentry, &parent->nestedTypesHead, entries) { + if (&pentry->type == type) { + return; + } + } + parent = parent->parent; + } + + struct nested_entry *entry = NULL; + TAILQ_FOREACH(entry, &type->nestedTypesHead, entries) { + dyn_type *toPrint = &entry->type; + if (toPrint->type == DYN_TYPE_REF) { + toPrint = toPrint->ref.ref; + } + + switch(toPrint->type) { + case DYN_TYPE_COMPLEX : + dynType_printComplexType(toPrint); + break; + case DYN_TYPE_SIMPLE : + dynType_printSimpleType(toPrint); + break; + default : + printf("TODO Print Type\n"); + break; + } + } + + + struct complex_type_entry *centry = NULL; + switch(type->type) { + case DYN_TYPE_COMPLEX : + TAILQ_FOREACH(centry, &type->complex.entriesHead, entries) { + dynType_printTypes(¢ry->type); + } + break; + case DYN_TYPE_SEQUENCE : + dynType_printTypes(type->sequence.itemType); + break; + case DYN_TYPE_TYPED_POINTER : + dynType_printTypes(type->typedPointer.typedType); + break; + } +} + +static void dynType_printComplexType(dyn_type *type) { + printf("type '%s': complex type, size is %zu, alignment is %i, descriptor is '%c'. fields:\n", type->name, type->ffiType->size, type->ffiType->alignment, type->descriptor); + + struct complex_type_entry *entry = NULL; + TAILQ_FOREACH(entry, &type->complex.entriesHead, entries) { + dynType_printAny(entry->name, &entry->type, 2); + } + + printf("}\n"); +} + +static void dynType_printSimpleType(dyn_type *type) { + printf("\ttype '%s': simple type, size is %zu, alignment is %i, descriptor is '%c'\n", type->name, type->ffiType->size, type->ffiType->alignment, type->descriptor); +} http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/dyn_type.h ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/dyn_type.h b/remote_services/dynamic_function_interface/dyn_type.h new file mode 100644 index 0000000..2a3b445 --- /dev/null +++ b/remote_services/dynamic_function_interface/dyn_type.h @@ -0,0 +1,137 @@ +/** + * Licensed under Apache License v2. See LICENSE for more information. + */ + +#ifndef _DYN_TYPE_H_ +#define _DYN_TYPE_H_ + +#include <stdio.h> +#include <sys/queue.h> +#include <stdbool.h> + +#include <ffi.h> + +/* Description string + * + * Type = [TypeDef]* (SimpleType | ComplexType | SequenceType | TypedPointer | PointerReference ) [TypeDef]* + * Name = alpha[(alpha|numeric)*] + * SPACE = ' ' + * + * SimplesTypes (based on java bytecode method signatures) + * //Java based: + * B char + * C (not supported) + * D double + * F float + * I int //TODO use int32_t instead? + * J long //TODO use int64_t intead? + * S short //TODO use int16_t instead? + * V void + * Z boolean + * //Extended + * b unsigned char + * i unsigned int (see I) + * j unsigned long (see J) + * s unsigned short (see S) + * P pointer + * t char* string + * N native int + * + * + * ComplexTypes (Struct) + * {[Type]+ [(Name)(SPACE)]+} + * + * PointerReference + * L(Name); + * + * ReferenceByValue + * l(name); + * + * TypeDef + * T(Name)=Type; + * + * SequenceType + * [(Type) + * + * examples + * "{DDII a b c d}" -> struct { double a; double b; int c; int d; }; + * "{DD{FF c1 c2} a b c" -> struct { double a; double b; struct c { float c1; float c2; }; }; + * + * + */ + +#define DYN_TYPE_INVALID 0 +#define DYN_TYPE_SIMPLE 1 +#define DYN_TYPE_COMPLEX 2 +#define DYN_TYPE_SEQUENCE 3 +#define DYN_TYPE_TYPED_POINTER 4 +#define DYN_TYPE_REF 5 + +typedef struct _dyn_type dyn_type; + +struct _dyn_type { + char *name; + char descriptor; + int type; + ffi_type *ffiType; + dyn_type *parent; + TAILQ_HEAD(, nested_entry) nestedTypesHead; + union { + struct { + TAILQ_HEAD(, complex_type_entry) entriesHead; + ffi_type structType; //dyn_type.ffiType points to this + dyn_type **types; //based on entriesHead for fast access + } complex; + struct { + ffi_type seqType; //dyn_type.ffiType points to this + dyn_type *itemType; + } sequence; + struct { + dyn_type *typedType; + } typedPointer; + struct { + dyn_type *ref; + } ref; + }; +}; + +struct complex_type_entry { + dyn_type type; + char *name; + TAILQ_ENTRY(complex_type_entry) entries; +}; + +struct nested_entry { + dyn_type type; + TAILQ_ENTRY(nested_entry) entries; +}; + + +//logging +void dynType_setupLogger(void (*logf)(void *handle, int level, const char *file, int line, const char *msgFormat, ...), void *handle); + +//generic +int dynType_create(const char *descriptor, dyn_type **type); +void dynType_destroy(dyn_type *type); +int dynType_alloc(dyn_type *type, void **bufLoc); +void dynType_print(dyn_type *type); +size_t dynType_size(dyn_type *type); +int dynType_type(dyn_type *type); +int dynType_descriptorType(dyn_type *type); + +//complexType +int dynType_complex_indexForName(dyn_type *type, const char *name); +char dynType_complex_descriptorTypeAt(dyn_type *type, int index); +int dynType_complex_dynTypeAt(dyn_type *type, int index, dyn_type **subType); +int dynType_complex_setValueAt(dyn_type *type, int index, void *inst, void *in); +int dynType_complex_valLocAt(dyn_type *type, int index, void *inst, void **valLoc); + +//sequence +int dynType_sequence_alloc(dyn_type *type, void *inst, int cap, void **buf); +int dynType_sequence_append(dyn_type *type, void *seq, void *in); +dyn_type * dynType_sequence_itemType(dyn_type *type); + +//simple +void dynType_simple_setValue(dyn_type *type, void *inst, void *in); + +#endif http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/json_serializer.c ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/json_serializer.c b/remote_services/dynamic_function_interface/json_serializer.c new file mode 100644 index 0000000..628bec9 --- /dev/null +++ b/remote_services/dynamic_function_interface/json_serializer.c @@ -0,0 +1,151 @@ +/** + * Licensed under Apache License v2. See LICENSE for more information. + */ +#include "json_serializer.h" + +#include <jansson.h> +#include <assert.h> + +static int json_serializer_createObject(dyn_type *type, json_t *object, void **result); +static int json_serializer_writeObject(dyn_type *type, json_t *object, void *inst); +static void json_serializer_writeObjectMember(dyn_type *type, const char *name, json_t *val, void *inst); +static void json_serializer_writeSequence(json_t *array, dyn_type *seq, void *seqLoc); + +int json_deserialize(dyn_type *type, const char *input, void **result) { + assert(type->type == DYN_TYPE_COMPLEX); + int status = 0; + + json_error_t error; + json_t *root = json_loads(input, JSON_DECODE_ANY, &error); + + if (root != NULL) { + status = json_serializer_createObject(type, root, result); + json_decref(root); + } else { + status = 1; + printf("JSON_SERIALIZER: error parsing json input '%s'. Error is %s\n", input, error.text); + } + + return status; +} + +static int json_serializer_createObject(dyn_type *type, json_t *object, void **result) { + assert(object != NULL); + int status = 0; + + void *inst = NULL; + status = dynType_alloc(type, &inst); + + if (status == 0) { + assert(inst != NULL); + status = json_serializer_writeObject(type, object, inst); + } else { + //TODO destroy + } + + if (status == 0) { + *result = inst; + } + + return status; +} + +static int json_serializer_writeObject(dyn_type *type, json_t *object, void *inst) { + assert(object != NULL); + int status = 0; + json_t *value; + const char *key; + + if (status == 0) { + json_object_foreach(object, key, value) { + json_serializer_writeObjectMember(type, key, value, inst); + } + } else { + status = 1; + printf("JSON_SERIALIZER: Error allocating memory\n"); + } + + return status; +} + +static void json_serializer_writeObjectMember(dyn_type *type, const char *name, json_t *val, void *inst) { + //TODO rename to complex. write generic write + int status = 0; + int index = dynType_complex_indexForName(type, name); + char charType = dynType_complex_descriptorTypeAt(type, index); + void *valp = NULL; + status = dynType_complex_valLocAt(type, index, inst, &valp); + + //TODO check status + + float *f; + double *d; + char *c; + short *s; + int *i; + long *l; + dyn_type *nested; + + switch (charType) { + case 'F' : + f = valp; + *f = json_real_value(val); + break; + case 'D' : + d = valp; + *d = json_real_value(val); + break; + case 'B' : + c = valp; + *c = json_integer_value(val); + break; + case 'S' : + s = valp; + *s = json_integer_value(val); + break; + case 'I' : + i = valp; + *i = json_integer_value(val); + break; + case 'J' : + l = valp; + *l = json_integer_value(val); + break; + case '[' : + status = dynType_complex_dynTypeAt(type, index, &nested); + //TODO check status + //TODO json_serializer_writeSequence(val, val, valp); + break; + case '{' : + status = dynType_complex_dynTypeAt(type, index, &nested); + //TODO check status + status = json_serializer_writeObject(nested, val, valp); + //TODO check status + break; + default : + printf("JSON_SERIALIZER: error provided type '%c' not supported\n", charType); + printf("Skipping\n"); + } +} + +static void json_serializer_writeSequence(json_t *array, dyn_type *seq, void *seqLoc) { + assert(dynType_type(seq) == DYN_TYPE_SEQUENCE); + size_t size = json_array_size(array); + //char seqType = dynType_sequence_elementSchemaType(seq); + + void *buf = NULL; + dynType_sequence_alloc(seq, seqLoc, size, &buf); //seq is already allocated. only need to allocate the buf + + //assuming int + int32_t i; + int index; + json_t *val; + json_array_foreach(array, index, val) { + i = json_number_value(val); + dynType_sequence_append(seq, seqLoc, &i); + } +} + +int json_serialize(dyn_type *type, void *input, char **output, size_t *size) { + return 0; +} http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/json_serializer.h ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/json_serializer.h b/remote_services/dynamic_function_interface/json_serializer.h new file mode 100644 index 0000000..6b42244 --- /dev/null +++ b/remote_services/dynamic_function_interface/json_serializer.h @@ -0,0 +1,12 @@ +/** + * Licensed under Apache License v2. See LICENSE for more information. + */ +#ifndef __JSON_SERIALIZER_H_ +#define __JSON_SERIALIZER_H_ + +#include "dyn_type.h" + +int json_deserialize(dyn_type *type, const char *input, void **result); +int json_serialize(dyn_type *type, void *input, char **output, size_t *size); + +#endif http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/schemas/complex.avdl ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/schemas/complex.avdl b/remote_services/dynamic_function_interface/schemas/complex.avdl new file mode 100644 index 0000000..0490dcd --- /dev/null +++ b/remote_services/dynamic_function_interface/schemas/complex.avdl @@ -0,0 +1,11 @@ +protocol Complex { + + record StatResult { + double sum; + double min; + double max; + array<double> input; + } + + StatResult stats(array<double> input); +} http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/schemas/complex.avpr ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/schemas/complex.avpr b/remote_services/dynamic_function_interface/schemas/complex.avpr new file mode 100644 index 0000000..0577397 --- /dev/null +++ b/remote_services/dynamic_function_interface/schemas/complex.avpr @@ -0,0 +1,36 @@ +{ + "protocol" : "Complex", + "namespace" : null, + "types" : [ { + "type" : "record", + "name" : "StatResult", + "fields" : [ { + "name" : "sum", + "type" : "double" + }, { + "name" : "min", + "type" : "double" + }, { + "name" : "max", + "type" : "double" + }, { + "name" : "input", + "type" : { + "type" : "array", + "items" : "double" + } + } ] + } ], + "messages" : { + "stats" : { + "request" : [ { + "name" : "input", + "type" : { + "type" : "array", + "items" : "double" + } + } ], + "response" : "StatResult" + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/schemas/simple.avdl ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/schemas/simple.avdl b/remote_services/dynamic_function_interface/schemas/simple.avdl new file mode 100644 index 0000000..cd5cafe --- /dev/null +++ b/remote_services/dynamic_function_interface/schemas/simple.avdl @@ -0,0 +1,6 @@ +@namespace("org.apache.avro.test") +protocol Simple { + double sum(double a, double b); + double sub(double a, double b); + double sqrt(double a); +} http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/schemas/simple.avpr ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/schemas/simple.avpr b/remote_services/dynamic_function_interface/schemas/simple.avpr new file mode 100644 index 0000000..8a90bb2 --- /dev/null +++ b/remote_services/dynamic_function_interface/schemas/simple.avpr @@ -0,0 +1,33 @@ +{ + "protocol" : "Simple", + "types" : [ ], + "messages" : { + "sum" : { + "request" : [ { + "name" : "a", + "type" : "double" + }, { + "name" : "b", + "type" : "double" + } ], + "response" : "double" + }, + "sub" : { + "request" : [ { + "name" : "a", + "type" : "double" + }, { + "name" : "b", + "type" : "double" + } ], + "response" : "double" + }, + "sqrt" : { + "request" : [ { + "name" : "a", + "type" : "double" + } ], + "response" : "double" + } + } +} http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/schemas/simple_min.avpr ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/schemas/simple_min.avpr b/remote_services/dynamic_function_interface/schemas/simple_min.avpr new file mode 100644 index 0000000..c2bce19 --- /dev/null +++ b/remote_services/dynamic_function_interface/schemas/simple_min.avpr @@ -0,0 +1 @@ +{"protocol":"Simple","types":[],"messages":{"sum":{"request":[{"name":"a","type":"double"},{"name":"b","type":"double"}],"response":"double"},"sub":{"request":[{"name":"a","type":"double"},{"name":"b","type":"double"}],"response":"double"},"sqrt":{"request":[{"name":"a","type":"double"}],"response":"double"}}} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/tst/avro_descriptor_translator_tests.cpp ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/tst/avro_descriptor_translator_tests.cpp b/remote_services/dynamic_function_interface/tst/avro_descriptor_translator_tests.cpp new file mode 100644 index 0000000..4a63c16 --- /dev/null +++ b/remote_services/dynamic_function_interface/tst/avro_descriptor_translator_tests.cpp @@ -0,0 +1,67 @@ +/** + * Licensed under Apache License v2. See LICENSE for more information. + */ +#include <CppUTest/TestHarness.h> +#include "CppUTest/CommandLineTestRunner.h" + +extern "C" { + +#include <stdio.h> +#include <assert.h> +#include <string.h> + +#include "descriptor_translator.h" + +void test1(void) { + //TODO split up + size_t size = 0; + char *ptr = NULL; + + //first argument void *handle, last argument output pointer for result and return int with status for exception handling + //sum(DD)D -> sum(PDD*D)N + //sub(DD)D -> sub(PDD*D)N + //sqrt(D)D -> sqrt(PD*D)N + FILE *schema = fopen("schemas/simple.avpr", "r"); + FILE *stream = open_memstream(&ptr, &size); + + int c = fgetc(schema); + while (c != EOF ) { + fputc(c, stream); + c = fgetc(schema); + } + fclose(schema); + fclose(stream); + + assert(schema != NULL && stream != NULL); + interface_descriptor_type *ift= NULL; + int status = descriptorTranslator_create(ptr, &ift); + CHECK_EQUAL(0, status); + + int count = 0; + method_descriptor_type *mDesc = NULL; + TAILQ_FOREACH(mDesc, &ift->methodDescriptors, entries) { + count +=1; + } + CHECK_EQUAL(3, count); + + TAILQ_FOREACH(mDesc, &ift->methodDescriptors, entries) { + if (strcmp("sum", mDesc->name) == 0) { + STRCMP_EQUAL("sum(PDD*D)N", mDesc->descriptor); + } else if (strcmp("add", mDesc->name) == 0) { + STRCMP_EQUAL("add(PDD*D)N", mDesc->descriptor); + } else if (strcmp("sqrt", mDesc->name) == 0) { + STRCMP_EQUAL("sqrt(PD*D)N", mDesc->descriptor); + } + } +} + +} + +TEST_GROUP(AvroDescTranslatorTest) { + void setup() { + } +}; + +TEST(AvroDescTranslatorTest, Test1) { + test1(); +} http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/tst/dyn_closure_tests.cpp ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/tst/dyn_closure_tests.cpp b/remote_services/dynamic_function_interface/tst/dyn_closure_tests.cpp new file mode 100644 index 0000000..30b0b65 --- /dev/null +++ b/remote_services/dynamic_function_interface/tst/dyn_closure_tests.cpp @@ -0,0 +1,139 @@ +/* + * Licensed under Apache License v2. See LICENSE for more information. + */ +#include <CppUTest/TestHarness.h> +#include "CppUTest/CommandLineTestRunner.h" + +extern "C" { + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "dyn_function.h" + +static int g_count; + +static void stdLog(void *handle, int level, const char *file, int line, const char *msg, ...) { + va_list ap; + const char *levels[5] = {"NIL", "ERROR", "WARNING", "INFO", "DEBUG"}; + fprintf(stderr, "%s: FILE:%s, LINE:%i, MSG:",levels[level], file, line); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + fprintf(stderr, "\n"); +} + +#define EXAMPLE1_SCHEMA "example(III)I" +static void example1_binding(void *userData, void* args[], void *out) { + printf("example1 closure called\n"); + int32_t a = *((int32_t *)args[0]); + int32_t b = *((int32_t *)args[1]); + int32_t c = *((int32_t *)args[2]); + int32_t *ret = (int32_t *)out; + *ret = a + b + c; + g_count += 1; +} + +#define EXAMPLE2_SCHEMA "example(I{DDD val1 val2 val3}I)D" +struct example2_arg2 { + double val1; + double val2; + double val3; +}; +void example2_binding(void *userData, void* args[], void *out) { + printf("example2 closure called\n"); + int32_t a = *((int32_t *)args[0]); + struct example2_arg2 b = *((struct example2_arg2 *)args[1]); + int32_t c = *((int32_t *)args[2]); + int32_t *ret = (int32_t *)out; + *ret = a + b.val1 + b.val2 + b.val3 + c; + g_count += 1; +} + + +#define EXAMPLE3_SCHEMA "example(III){III sum max min}" +struct example3_ret { + int32_t sum; + int32_t max; + int32_t min; +}; + +static void example3_binding(void *userData, void* args[], void *out) { + printf("example closure called\n"); + int32_t a = *((int32_t *)args[0]); + int32_t b = *((int32_t *)args[1]); + int32_t c = *((int32_t *)args[2]); + struct example3_ret *result = (struct example3_ret *)calloc(1,sizeof(struct example3_ret)); + result->sum = a + b + c; + result->min = a <= b ? a : b; + result->max = a >= b ? a : b; + result->min = result->min <= c ? result->min : c; + result->max = result->max >= c ? result->max : c; + + struct example3_ret **ret = (struct example3_ret **)out; + (*ret) = result; + g_count += 1; +} + +static void tests() { + dyn_closure_type *dynClosure = NULL; + int rc; + + { + rc = dynClosure_create(EXAMPLE1_SCHEMA, example1_binding, NULL, &dynClosure); + CHECK_EQUAL(0, rc); + int32_t (*func)(int32_t a, int32_t b, int32_t c) = NULL; + int rc = dynClosure_getFnPointer(dynClosure, (void(**)(void))&func); + CHECK_EQUAL(0, rc); + int32_t ret = func(2,3,4); + printf("Return value for example1 is %i\n", ret); + CHECK_EQUAL(1, g_count); + dynClosure_destroy(dynClosure); + } + + { + dynClosure = NULL; + rc = dynClosure_create(EXAMPLE2_SCHEMA, example2_binding, NULL, &dynClosure); + CHECK_EQUAL(0, rc); + double (*func)(int32_t a, struct example2_arg2 b, int32_t c) = NULL; + rc = dynClosure_getFnPointer(dynClosure, (void(**)(void))&func); + CHECK_EQUAL(0, rc); + struct example2_arg2 b = { .val1 = 1.0, .val2 = 1.5, .val3 = 2.0 }; + double ret = func(2,b,4); + printf("Return value for example2 is %f\n", ret); + CHECK_EQUAL(2, g_count); + dynClosure_destroy(dynClosure); + } + + { + dynClosure = NULL; + rc = dynClosure_create(EXAMPLE3_SCHEMA, example3_binding, NULL, &dynClosure); + CHECK_EQUAL(0, rc); + struct example3_ret * (*func)(int32_t a, int32_t b, int32_t c) = NULL; + rc = dynClosure_getFnPointer(dynClosure, (void(**)(void))&func); + CHECK_EQUAL(0, rc); + struct example3_ret *ret = func(2,8,4); + printf("Return value for example3 is {sum:%i, max:%i, min:%i}\n", ret->sum, ret->max, ret->min); + CHECK_EQUAL(3, g_count); + dynClosure_destroy(dynClosure); + free(ret); + } +} + +} + + +TEST_GROUP(DynClosureTests) { + void setup() { + dynFunction_logSetup(stdLog, NULL, 4); + //TODO dynType_logSetup(stdLog, NULL, 4); + g_count = 0; + } +}; + +TEST(DynClosureTests, DynCLosureTest1) { + //TODO split up + tests(); +} http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/tst/dyn_function_tests.cpp ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/tst/dyn_function_tests.cpp b/remote_services/dynamic_function_interface/tst/dyn_function_tests.cpp new file mode 100644 index 0000000..a2b7f69 --- /dev/null +++ b/remote_services/dynamic_function_interface/tst/dyn_function_tests.cpp @@ -0,0 +1,109 @@ +/* + * Licensed under Apache License v2. See LICENSE for more information. + */ +#include <CppUTest/TestHarness.h> +#include "CppUTest/CommandLineTestRunner.h" + +extern "C" { + #include <stdio.h> + #include <stdint.h> + #include <stdlib.h> + #include <string.h> + #include <ctype.h> + + #include "dyn_function.h" + + static void stdLog(void *handle, int level, const char *file, int line, const char *msg, ...) { + va_list ap; + const char *levels[5] = {"NIL", "ERROR", "WARNING", "INFO", "DEBUG"}; + fprintf(stderr, "%s: FILE:%s, LINE:%i, MSG:",levels[level], file, line); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + fprintf(stderr, "\n"); + } + + #define EXAMPLE1_SCHEMA "example(III)I" + int32_t example1(int32_t a, int32_t b, int32_t c) { + CHECK_EQUAL(2, a); + CHECK_EQUAL(4, b); + CHECK_EQUAL(8, c); + return 1; + } + + void test_example1(void) { + dyn_function_type *dynFunc = NULL; + int rc; + void (*fp)(void) = (void (*)(void)) example1; + + rc = dynFunction_create(EXAMPLE1_SCHEMA, fp, &dynFunc); + CHECK_EQUAL(0, rc); + + int32_t a = 2; + int32_t b = 4; + int32_t c = 8; + void *values[3]; + int32_t rVal = 0; + values[0] = &a; + values[1] = &b; + values[2] = &c; + + rc = dynFunction_call(dynFunc, &rVal, values); + CHECK_EQUAL(0, rc); + CHECK_EQUAL(1, rVal); + dynFunction_destroy(dynFunc); + } + + #define EXAMPLE2_SCHEMA "example(I{IID val1 val2 val3}D)D" + struct example2_arg { + int32_t val1; + int32_t val2; + double val3; + }; + + double example2(int32_t arg1, struct example2_arg arg2, double arg3) { + CHECK_EQUAL(2, arg1); + CHECK_EQUAL(2, arg2.val1); + CHECK_EQUAL(3, arg2.val2); + CHECK_EQUAL(4.1, arg2.val3); + CHECK_EQUAL(8.1, arg3); + return 2.2; + } + + void test_example2(void) { + dyn_function_type *dynFunc = NULL; + int rc; + void (*fp)(void) = (void (*)(void)) example2; + + rc = dynFunction_create(EXAMPLE2_SCHEMA, fp, &dynFunc); + CHECK_EQUAL(0, rc); + + int32_t arg1 = 2; + struct example2_arg arg2 = { .val1 = 2, .val2 = 3, .val3 = 4.1 }; + double arg3 = 8.1; + double returnVal = 0; + void *values[3]; + values[0] = &arg1; + values[1] = &arg2; + values[2] = &arg3; + + rc = dynFunction_call(dynFunc, &returnVal, values); + CHECK_EQUAL(0, rc); + CHECK_EQUAL(2.2, returnVal); + dynFunction_destroy(dynFunc); + } +} + +TEST_GROUP(DynFunctionTests) { + void setup() { + dynFunction_logSetup(stdLog, NULL, 4); + //TODO dynType_logSetup(stdLog, NULL, 4); + } +}; + +TEST(DynFunctionTests, DynFuncTest1) { + test_example1(); +} + +TEST(DynFunctionTests, DynFuncTest2) { + test_example2(); +} http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/tst/dyn_type_tests.cpp ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/tst/dyn_type_tests.cpp b/remote_services/dynamic_function_interface/tst/dyn_type_tests.cpp new file mode 100644 index 0000000..07c419b --- /dev/null +++ b/remote_services/dynamic_function_interface/tst/dyn_type_tests.cpp @@ -0,0 +1,183 @@ +/* + * Licensed under Apache License v2. See LICENSE for more information. + */ +#include <CppUTest/TestHarness.h> +#include "CppUTest/CommandLineTestRunner.h" + +extern "C" { + #include <string.h> + #include <stdarg.h> + + #include "dyn_type.h" + + void runTest(const char *descriptorStr, const char *exName) { + dyn_type *type; + int i; + type = NULL; + printf("\n-- example %s with descriptor string '%s' --\n", exName, descriptorStr); + int status = dynType_create(descriptorStr, &type); + CHECK_EQUAL(0, status); + if (status == 0) { + dynType_print(type); + dynType_destroy(type); + } + printf("--\n\n"); + } +} + +TEST_GROUP(DynTypeTests) { +}; + +#define EX1 "{BbJjIiSsDFNN arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10 arg11 arg12}" +#define EX2 "{D{DD b_1 b_2}I a b c}" +#define EX3 "Tsub={DD b_1 b_2};{DLsub;I a b c}" +#define EX4 "{[I numbers}" +#define EX5 "[[{DD{iii val3_1 val3_2 val3_3} val1 val2 val3}" +#define EX6 "Tsample={DD vala valb};Lsample;" +#define EX7 "Tsample={DD vala valb};[Lsample;" +#define EX8 "[Tsample={DD a b};Lsample;" +#define EX9 "*D" +#define EX10 "Tsample={DD a b};******Lsample;" +#define EX11 "Tsample=D;Lsample;" +#define EX12 "Tnode={Lnode;Lnode; left right};{Lnode; head}" //note recursive example +#define EX13 "Ttype={DDDDD a b c d e};{ltype;Ltype;ltype;Ltype; byVal1 byRef1 byVal2 ByRef2}" +#define EX14 "{DD{FF{JJ}{II*{ss}}}}" //unnamed fields + +#define CREATE_EXAMPLES_TEST(DESC) \ + TEST(DynTypeTests, ParseTestExample ## DESC) { \ + runTest(DESC, #DESC); \ + } + +CREATE_EXAMPLES_TEST(EX1) +CREATE_EXAMPLES_TEST(EX2) +CREATE_EXAMPLES_TEST(EX3) +CREATE_EXAMPLES_TEST(EX4) +CREATE_EXAMPLES_TEST(EX5) +CREATE_EXAMPLES_TEST(EX6) +CREATE_EXAMPLES_TEST(EX7) +CREATE_EXAMPLES_TEST(EX8) +CREATE_EXAMPLES_TEST(EX9) +CREATE_EXAMPLES_TEST(EX10) +CREATE_EXAMPLES_TEST(EX11) +CREATE_EXAMPLES_TEST(EX12) +CREATE_EXAMPLES_TEST(EX13) +CREATE_EXAMPLES_TEST(EX14) + +TEST(DynTypeTests, ParseRandomGarbageTest) { + return; //TODO enable + unsigned int seed = 4148; + char *testRandom = getenv("DYN_TYPE_TEST_RANDOM"); + if (testRandom != NULL && strcmp("true", testRandom) == 0) { + seed = (unsigned int) time(NULL); + } + srandom(seed); + size_t nrOfTests = 10000; + + printf("\nStarting test with random seed %i and nrOfTests %zu.\n", seed, nrOfTests); + + int i; + int k; + int c; + int sucesses = 0; + char descriptorStr[32]; + descriptorStr[31] = '\0'; + for(i = 0; i < nrOfTests; i += 1) { + for(k = 0; k < 31; k += 1) { + do { + c = (char) (((random() * 128) / RAND_MAX) - 1); + } while (!isprint(c)); + descriptorStr[k] = c; + if (c == '\0') { + break; + } + } + + //printf("ParseRandomGarbageTest iteration %i with descriptor string '%s'\n", k, descriptorStr); + dyn_type *type = NULL; + int status = dynType_create(descriptorStr, &type); + if (status == 0) { + dynType_destroy(type); + } + } +} + +extern "C" { + static int logCount = 0; + static void logf_test(void *handle, int level, const char *file, int line, const char *msg, ...) { + logCount += 1; + + /* + va_list ap; + const char *levels[5] = {"NIL", "ERROR", "WARNING", "INFO", "DEBUG"}; + printf("%s: FILE:%s, LINE:%i, MSG:",levels[level], file, line); + va_start(ap, msg); + vprintf(msg, ap); + printf("\n"); + */ + } +} + +TEST(DynTypeTests, DynTypeLoggerTest) { + dyn_type *type = NULL; + dynType_setupLogger(logf_test, NULL); + dynType_create("*", &type); + CHECK_EQUAL(1, logCount); +} + +TEST(DynTypeTests, AssignTest1) { + struct ex1 { + int32_t a; + int32_t b; + int32_t c; + }; + struct ex1 inst; + const char *desc = "{III a b c}"; + dyn_type *type = NULL; + int status = dynType_create(desc, &type); + CHECK_EQUAL(0, status); + int32_t val1 = 2; + int32_t val2 = 4; + int32_t val3 = 8; + dynType_complex_setValueAt(type, 0, &inst, &val1); + CHECK_EQUAL(2, inst.a); + dynType_complex_setValueAt(type, 1, &inst, &val2); + CHECK_EQUAL(4, inst.b); + dynType_complex_setValueAt(type, 2, &inst, &val3); + CHECK_EQUAL(8, inst.c); +} + +TEST(DynTypeTests, AssignTest2) { + struct ex { + int32_t a; + struct { + double a; + double b; + } b; + }; + struct ex inst; + const char *desc = "{I{DD a b} a b}"; + dyn_type *type = NULL; + int status = dynType_create(desc, &type); + CHECK_EQUAL(0, status); + int32_t a = 2; + double b_a = 1.1; + double b_b = 1.2; + + dynType_complex_setValueAt(type, 0, &inst, &a); + CHECK_EQUAL(2, inst.a); + + void *loc = NULL; + dyn_type *subType = NULL; + dynType_complex_valLocAt(type, 1, (void *)&inst, &loc); + dynType_complex_dynTypeAt(type, 1, &subType); + + dynType_complex_setValueAt(subType, 0, &inst.b, &b_a); + CHECK_EQUAL(1.1, inst.b.a); + + dynType_complex_setValueAt(subType, 1, &inst.b, &b_b); + CHECK_EQUAL(1.2, inst.b.b); +} + +//TODO allocating / freeing + +//TODO sequences http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/tst/json_serializer_tests.cpp ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/tst/json_serializer_tests.cpp b/remote_services/dynamic_function_interface/tst/json_serializer_tests.cpp new file mode 100644 index 0000000..6cabe8a --- /dev/null +++ b/remote_services/dynamic_function_interface/tst/json_serializer_tests.cpp @@ -0,0 +1,182 @@ +/** + * Licensed under Apache License v2. See LICENSE for more information. + */ +#include <CppUTest/TestHarness.h> +#include "CppUTest/CommandLineTestRunner.h" + +extern "C" { +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include <ffi.h> + +#include "dyn_type.h" +#include "json_serializer.h" + +static void stdLog(void *handle, int level, const char *file, int line, const char *msg, ...) { + va_list ap; + const char *levels[5] = {"NIL", "ERROR", "WARNING", "INFO", "DEBUG"}; + fprintf(stderr, "%s: FILE:%s, LINE:%i, MSG:",levels[level], file, line); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + fprintf(stderr, "\n"); +} + +/*********** example 1 ************************/ +/** struct type ******************************/ +const char *example1_descriptor = "{DJISF a b c d e}"; + +const char *example1_input = "{ \ + \"a\" : 1.0, \ + \"b\" : 22, \ + \"c\" : 32, \ + \"d\" : 42, \ + \"e\" : 4.4 \ +}"; + +struct example1 { + double a; //0 + int64_t b; //1 + int32_t c; //2 + int16_t d; //3 + float e; //4 +}; + +static void print_example1(void *data) { + struct example1 *ex = (struct example1 *)data; + printf("example1: a:%f, b:%li, c:%i, d:%i, e:%f\n", ex->a, ex->b, ex->c, ex->d, ex->e); +} + +/*********** example 2 ************************/ +const char *example2_descriptor = "{BJJDFD byte long1 long2 double1 float1 double2}"; + +const char *example2_input = "{ \ + \"byte\" : 42, \ + \"long1\" : 232, \ + \"long2\" : 242, \ + \"double1\" : 4.2, \ + \"float1\" : 3.2, \ + \"double2\" : 4.4 \ +}"; + +struct example2 { + char byte; //0 + int64_t long1; //1 + int64_t long2; //2 + double double1; //3 + float float1; //4 + double double2; //5 +}; + +static void print_example2(void *data) { + struct example2 *ex = (struct example2 *)data; + printf("example2: byte:%i, long1:%li, long2:%li, double1:%f, float1:%f, double2:%f\n", ex->byte, ex->long1, ex->long2, ex->double1, ex->float1, ex->double2); +} + + +/*********** example 3 ************************/ +/** sequence with a simple type **************/ +const char *example3_descriptor = "{[I numbers}"; + +const char *example3_input = "{ \ + \"numbers\" : [22,32,42] \ +}"; + +struct example3 { + struct { + uint32_t _cap; + uint32_t _len; + int32_t *buf; + } numbers; +}; + +static void print_example3(void *data) { + struct example3 *ex = (struct example3 *)data; + printf("example3: numbers length is %u and cap is %u and pointer buf is %p\n", ex->numbers._len, ex->numbers._cap, ex->numbers.buf); + int i; + for (i = 0; i < ex->numbers._len; i += 1) { + printf("\telement %i : %i\n", i, ex->numbers.buf[i]); + } +} + +/*********** example 4 ************************/ +/** structs within a struct (by reference)*******/ +//TODO think about references in descriptor e.g "{Lleaf;Lleaf; left right}\nleaf{IDD index val1 val2}" +const char *example4_descriptor = "{{IDD index val1 val2}{IDD index val1 val2} left right}"; + +static const char *example4_input = "{ \ + \"left\" : {\"index\":1, \"val1\":1.0, \"val2\":2.0 }, \ + \"right\" : {\"index\":2, \"val1\":5.0, \"val2\":4.0 } \ +}"; + +struct leaf { + int32_t index; + double val1; + double val2; +}; + +struct example4 { + struct leaf left; + struct leaf right; +}; + +static void print_example4(void *data) { + struct example4 *ex = (struct example4 *)data; + printf("example4: left { index:%i, val1:%f, val2:%f }, right { index;%i, val1:%f, val2:%f }\n", ex->left.index, ex->left.val1, ex->left.val2, ex->right.index, ex->right.val1, ex->right.val2); +} + +static void tests() { + printf("Starting json serializer tests\n"); + dyn_type *type; + void *inst; + int rc; + + type = NULL; + inst = NULL; + rc = dynType_create(example1_descriptor, &type); + CHECK_EQUAL(0, rc); + rc = json_deserialize(type, example1_input, &inst); + CHECK_EQUAL(0, rc); + print_example1(inst); + printf("--\n\n"); + + + type = NULL; + inst = NULL; + rc = dynType_create(example2_descriptor, &type); + CHECK_EQUAL(0, rc); + rc = json_deserialize(type, example2_input, &inst); + CHECK_EQUAL(0, rc); + print_example2(inst); + printf("--\n\n"); + + type = NULL; + inst = NULL; + rc = dynType_create(example3_descriptor, &type); + CHECK_EQUAL(0, rc); + rc = json_deserialize(type, example3_input, &inst); + CHECK_EQUAL(0, rc); + print_example3(inst); + + type = NULL; + inst = NULL; + rc = dynType_create(example4_descriptor, &type); + CHECK_EQUAL(0, rc); + rc = json_deserialize(type, example4_input, &inst); + CHECK_EQUAL(0, rc); + print_example4(inst); +} +} + +TEST_GROUP(JsonSerializerTests) { + void setup() { + } +}; + +TEST(JsonSerializerTests, Test1) { + //TODO split up + tests(); +} http://git-wip-us.apache.org/repos/asf/celix/blob/bdeba2bc/remote_services/dynamic_function_interface/tst/run_tests.cpp ---------------------------------------------------------------------- diff --git a/remote_services/dynamic_function_interface/tst/run_tests.cpp b/remote_services/dynamic_function_interface/tst/run_tests.cpp new file mode 100644 index 0000000..f405c9f --- /dev/null +++ b/remote_services/dynamic_function_interface/tst/run_tests.cpp @@ -0,0 +1,9 @@ +/* + * Licensed under Apache License v2. See LICENSE for more information. + */ +#include <CppUTest/TestHarness.h> +#include "CppUTest/CommandLineTestRunner.h" + +int main(int argc, char** argv) { + return RUN_ALL_TESTS(argc, argv); +}