// Include our header file #include "bowl.h" // Runtime includes #include #include #include #include #define RDB_REC_DEF_SIZE 2 #define RDB_REC_MULTIPLY 2 #define ORIGINAL (short) 0 #define SHALLOW (short) 1 #define DEEP (short) 2 /*** Forward declared functions ***/ int list_search(struct bowl**, struct bowl*, struct bowl*); void list_print(struct bowl *data, const char *offset); /******/ dt_err bowl_malloc(struct bowl *(*data)) { (*data) = (struct bowl*) malloc(sizeof(struct bowl)); if(*data == NULL) { printf("Creating bowl object FAILED"); return MALLOC_FAILED; } memset(*data, 0, sizeof(struct bowl)); (*data)->type = UNSET; return SUCCESS; } dt_err bowl_resettype(struct bowl *data) { if(data->type == LITERAL) { if(data->payload.literal) free(data->payload.literal); } else if(data->type == LIST || data->type == PAIR) { /* Iterate over all children and clear them */ int i; dt_err err; for(i = 0; i < data->size; i++) { err = bowl_free(data->payload.list[i]); if(err) return err; } } /* Set the data type to unset */ data->type = UNSET; data->encset = 0; data->size = 0; data->used = 0; /* Forcibly clean union memory to avoid bleeding data */ memset(&data->payload, 0, sizeof(data->payload)); return SUCCESS; } dt_err bowl_addliteral(struct bowl *data, const char *literal) { /* Make sure we are a literal or unset data object */ if(data->type != UNSET) if(data->type != LITERAL) return INVALID_PAYLOAD; size_t length = REAL_STRLEN(literal); /* Get rid of previous data */ if(data->payload.literal) free(data->payload.literal); /* Allocate space for the data */ char *tmp = (char *) malloc(sizeof(char) * length); if(tmp == NULL) { printf("Allocating space for literal data FAILED"); return MALLOC_FAILED; } /* Copy the string over and store it in the union */ strcpy(tmp, literal); data->payload.literal = tmp; data->type = LITERAL; data->size = length; data->used = length; return SUCCESS; } dt_err bowl_addpointer(struct bowl *data, void *ptr) { if(data->type != UNSET) if(data->type != POINTER) return INVALID_PAYLOAD; data->payload.pointer = ptr; data->type = POINTER; data->size = sizeof(ptr); data->used = sizeof(ptr); return SUCCESS; } dt_err bowl_addnumeral(struct bowl *data, long numeral) { /* Make sure we are a literal or unset data object */ if(data->type != UNSET) if(data->type != NUMERIC) return INVALID_PAYLOAD; data->payload.numeral = numeral; data->type = NUMERIC; data->size = sizeof(int); data->used = sizeof(int); return SUCCESS; } dt_err bowl_addboolean(struct bowl *data, bool b) { /* Make sure we are a literal or unset data object */ if(data->type != UNSET) if(data->type != BOOLEAN) return INVALID_PAYLOAD; data->payload.boolean = b; data->type = BOOLEAN; data->size = sizeof(bool); data->used = sizeof(bool); return SUCCESS; } dt_err bowl_addlist(struct bowl *data, struct bowl *(*new_data)) { /* Make sure we are a literal or unset data object */ if(data->type != UNSET) if(data->type != LIST) return INVALID_PAYLOAD; dt_err err; /* This means elements already exist */ if(data->size > 0) { /* Used should never > size */ if(data->used >= data->size) { data->size += RDB_REC_MULTIPLY; // TODO Use Realloc struct bowl **tmp = (struct bowl**) malloc(sizeof(struct bowl*) * data->size); memcpy(tmp, data->payload.list, sizeof(struct bowl*) * data->used); /* Free the list WITHOUT the children! */ free(data->payload.list); data->payload.list = tmp; } /* This means the data object is new */ } else { struct bowl **tmp = (struct bowl**) malloc(sizeof(struct bowl*) * RDB_REC_DEF_SIZE); data->payload.list = tmp; data->type = LIST; data->used = 0; data->size = RDB_REC_DEF_SIZE; } err = bowl_malloc(new_data); if(err) return err; /* Reference the slot, assign it, then move our ctr */ data->payload.list[data->used++] = *new_data; return SUCCESS; } dt_err bowl_addpair(struct bowl *data, struct bowl *(*key), struct bowl *(*value)) { /* Make sure we are a literal or unset data object */ if(data->type != UNSET) return INVALID_PAYLOAD; dt_err err; /* Malloc two nodes */ err = bowl_malloc(key); if(err) goto cleanup; err = bowl_malloc(value); if(err) goto cleanup; /** Malloc space for PAIR */ data->size = 2; struct bowl **tmp = (struct bowl**) malloc(sizeof(struct bowl*) * data->size); if(!tmp) goto cleanup; data->payload.list = tmp; { /* Assign data to new array */ data->payload.list[data->used] = *key; data->used++; data->payload.list[data->used] = *value; data->used++; } /* Assign our new type and return */ data->type = PAIR; return SUCCESS; /* Code we run when we can't allocate structs anymore */ cleanup: free(*key); free(*value); free(tmp); return MALLOC_FAILED; } dt_err bowl_split_trees(struct bowl *data, struct bowl *sp) { /* Make sure we are a literal or unset data object */ if(data->type == UNSET) return INVALID_PAYLOAD; /* Check that sp is really a child of data */ struct bowl *dp; int ret = list_search(&dp, data, sp); if(ret != 0) return DATA_NOT_RELATED; if(dp == NULL) return NODE_NOT_FOUND; /* Find the exact list reference and remove it */ int i; for(i = 0; i < dp->used; i++) { if(dp->payload.list[i] == NULL) continue; /* Manually remove the entry */ if(dp->payload.list[i] == sp) { dp->used--; dp->payload.list[i] = NULL; } } return SUCCESS; } dt_err bowl_merge_trees(struct bowl *data, struct bowl *merge) { /* REALLY make sure the type is correct */ if(data->type == UNSET) return INVALID_PARAMS; if(!(data->type == LIST || data->type == PAIR)) return INVALID_PAYLOAD; /* This means elements already exist */ if(data->size > 0) { /* Used should never > size */ if(data->used >= data->size) { data->size += RDB_REC_MULTIPLY; struct bowl **tmp = (struct bowl**) malloc(sizeof(struct bowl*) * data->size); memcpy(tmp, data->payload.list, sizeof(struct bowl*) * data->used); /* Free the list WITHOUT the children! */ free(data->payload.list); data->payload.list = tmp; } /* This means the data object is new */ } else { struct bowl **tmp = (struct bowl**) malloc(sizeof(struct bowl*) * data->size); data->payload.list = tmp; data->type = LIST; data->used = 0; data->size = RDB_REC_DEF_SIZE; } /* Reference the slot, assign it, then move our ctr */ data->payload.list[data->used] = merge; data->used++; return SUCCESS; } dt_err bowl_copy_deep(struct bowl *data, struct bowl *(*copy)) { if(data == NULL) return INVALID_PARAMS; dt_err err = SUCCESS; int it_type = -1; bowl_t type = data->type; /* Check if we're the first call */ if((*copy) == NULL) bowl_malloc(copy); (*copy)->copy = DEEP; switch(type) { case LITERAL: bowl_addliteral(*copy, data->payload.literal); break; case NUMERIC: bowl_addnumeral(*copy, data->payload.numeral); break; case BOOLEAN: bowl_addboolean(*copy, data->payload.boolean); break; case LIST: { int i; int num = (int) data->used; for(i = 0; i < num; i++) { struct bowl *node = data->payload.list[i]; struct bowl *new; bowl_addlist(*copy, &new); bowl_copy_deep(node, &new); } break; } case PAIR: { struct bowl *key, *val; bowl_addpair(*copy, &key, &val); struct bowl *orig_key = data->payload.list[0]; struct bowl *orig_val = data->payload.list[1]; bowl_copy_deep(orig_key, &key); bowl_copy_deep(orig_val, &val); break; } case POINTER: bowl_addpointer(*copy, data->payload.pointer); break; default: err = INVALID_PAYLOAD; break; } return err; } dt_err bowl_parent(struct bowl *root, struct bowl *data, struct bowl **parent) { if(root == NULL || data == NULL) return INVALID_PARAMS; /* Blank the search pointer for easy error checking */ (*parent) = NULL; switch(root->type) { /* Dead-end data stores automatically return @{NODE_NOT_FOUND} */ case POINTER: case LITERAL: case BOOLEAN: case NUMERIC: return NODE_NOT_FOUND; case PAIR: case LIST: { int i; for(i = 0; i < root->used; i++) { /* Check if the node we're looking at is what we're searching for */ if(root->payload.list[i] == data) { (*parent) = root; return SUCCESS; } dt_err err = bowl_parent(root->payload.list[i], data, parent); if(err == SUCCESS) return SUCCESS; } } break; default: return INVALID_PAYLOAD; } return NODE_NOT_FOUND; } dt_err bowl_copy(struct bowl *data, struct bowl *(*copy)) { if(data == NULL) return INVALID_PARAMS; dt_err err = SUCCESS; /* Allocate a new node */ err = bowl_malloc(copy); if(err) goto exit; /* Mark as shallow copy */ (*copy)->copy = SHALLOW; /* Find out how to handle specific payloads */ switch(data->type) { case LITERAL: err = bowl_addliteral(*copy, data->payload.literal); break; case NUMERIC: err = bowl_addnumeral(*copy, data->payload.numeral); break; case BOOLEAN: err = bowl_addboolean(*copy, data->payload.boolean); break; case LIST: (*copy)->type = LIST; (*copy)->payload.list = (struct bowl**) malloc(sizeof(struct bowl*) * data->size); memcpy((*copy)->payload.list, data->payload.list, sizeof(struct bowl*) * data->used); break; case PAIR: (*copy)->type = PAIR; (*copy)->payload.list = (struct bowl**) malloc(sizeof(struct bowl*) * data->size); memcpy((*copy)->payload.list, data->payload.list, sizeof(struct bowl*) * data->used); break; case POINTER: (*copy)->type = POINTER; memcpy((*copy)->payload.pointer, data->payload.pointer, sizeof(void*)); break; default: return INVALID_PAYLOAD; } exit: return err; } dt_err bowl_search_payload(struct bowl *data, struct bowl *(*found), void *payload, bowl_t type) { if(data == NULL) return INVALID_PARAMS; /* Make sure our pointer is clean */ *found = NULL; if(data->type == LIST|| data->type == PAIR) { int i; for(i = 0; i < data->used; i++) { dt_err err = bowl_search_payload(data->payload.list[i], found, payload, type); if(err == SUCCESS) return SUCCESS; } } else { /* Check the type aligns */ if(data->type != type) return NODE_NOT_FOUND; switch(type) { case LITERAL: if(strcmp(data->payload.literal, (char*) payload) == 0) *found = data; break; case NUMERIC: if(data->payload.numeral == (long) payload) *found = data; break; case BOOLEAN: if(data->payload.boolean == (bool) payload) *found = data; break; case POINTER: if(data->payload.pointer == payload) *found = data; break; default: return NODE_NOT_FOUND; } } return (*found == NULL) ? NODE_NOT_FOUND : SUCCESS; } // FIXME: This is horrible. Do via context? static int reached = 0; dt_err bowl_search_keypayload(struct bowl *data, struct bowl *(*found), void *payload, bowl_t type, int depth) { if(data == NULL) return INVALID_PARAMS; if(reached++ >= depth) return QUERY_TOO_DEEP; /* Make sure our pointer is clean */ *found = NULL; /* We can only search LISTed values or PAIRs */ if(data->type == PAIR) { struct bowl *key = data->payload.list[0]; bowl_t tt; int hit = -1; if(strcmp(key->payload.literal, (char*) payload) == 0) { tt = LITERAL; hit = 0; } if(key->payload.numeral == (long) payload) { tt = NUMERIC; hit = 0; } if(key->payload.boolean == (bool) payload) { tt = BOOLEAN; hit = 0; } if(hit == 0) *found = data->payload.list[1]; } else if(data->type == LIST) { int i; for(i = 0; i < data->used; i++) { bowl_search_keypayload(data->payload.list[i], found, payload, type, depth); } } else { } if(data->type == LIST|| data->type == PAIR) { int i; for(i = 0; i < data->used; i++) { dt_err err = bowl_search_payload(data->payload.list[i], found, payload, type); if(err == SUCCESS) return SUCCESS; } } else { /* Check the type aligns */ if(data->type != type) return NODE_NOT_FOUND; switch(type) { case LITERAL: if(strcmp(data->payload.literal, (char*) payload) == 0) *found = data; break; case NUMERIC: if(data->payload.numeral == (long) payload) *found = data; break; case BOOLEAN: if(data->payload.boolean == (bool) payload) *found = data; case POINTER: if(data->payload.pointer == payload) *found = data; break; default: return NODE_NOT_FOUND; } } return (*found == NULL) ? NODE_NOT_FOUND : SUCCESS; } void list_print(struct bowl *data, const char *offset) { bowl_t type = data->type; switch(type) { case UNSET: printf("[NULL]\n"); break; case LITERAL: printf("%s['%s']\n", offset, data->payload.literal); break; case NUMERIC: printf("%s[%lu]\n", offset, data->payload.numeral); break; case BOOLEAN: printf("%s['%s']\n", offset, (data->payload.boolean) ? "TRUE" : "FALSE"); break; case PAIR: { bowl_t k_type = data->payload.list[0]->type; bowl_t v_type = data->payload.list[1]->type; if(k_type == LITERAL) printf("%s['%s']", offset, data->payload.list[0]->payload.literal); if(k_type == NUMERIC) printf("%s[%lu]", offset, data->payload.list[0]->payload.numeral); if(k_type == BOOLEAN) printf("%s[%s]", offset, (data->payload.list[0]->payload.boolean) ? "TRUE" : "FALSE"); char new_offset[REAL_STRLEN(offset) + 2]; strcpy(new_offset, offset); strcat(new_offset, " "); if(k_type == LIST || k_type == PAIR) list_print(data->payload.list[0], new_offset); /* Print the value now */ if(v_type == LITERAL) printf(" => ['%s']\n", data->payload.list[1]->payload.literal); if(v_type== NUMERIC) printf(" => [%lu]\n", data->payload.list[1]->payload.numeral); if(v_type == BOOLEAN) printf(" => [%s]\n", (data->payload.list[1]->payload.boolean) ? "TRUE" : "FALSE"); if(v_type == LIST || k_type == PAIR) list_print(data->payload.list[1], new_offset); break; } case LIST: { int i; printf("%s[LIST]\n", offset); for(i = 0; i < data->used; i++) { bowl_t t = data->payload.list[i]->type; /* Calculate the new offset */ char new_offset[REAL_STRLEN(offset) + 2]; strcpy(new_offset, offset); strcat(new_offset, " "); switch(t) { case LITERAL: case BOOLEAN: case NUMERIC: list_print(data->payload.list[i], new_offset); continue; case LIST: list_print(data->payload.list[i], new_offset); continue; case PAIR: printf("%s[PAIR] <==> ", new_offset); list_print(data->payload.list[i], new_offset); continue; default: break; } } break; } default: break; } } void bowl_print(struct bowl *data) { list_print(data, ""); } dt_err bowl_get(struct bowl *data, void *(*val)) { if(data->type == LITERAL) *val = data->payload.literal; if(data->type == NUMERIC) *val = &data->payload.numeral; if(data->type == BOOLEAN) *val = &data->payload.boolean; if(data->type == LIST || data->type == PAIR) *val = (struct bowl*) data->payload.list; return SUCCESS; } dt_err bowl_free(struct bowl *data) { if(data == NULL) return SUCCESS; if(data->copy == SHALLOW) return NODE_NOT_ORIGINAL; if(data->type == LITERAL) { if(data->payload.literal) free(data->payload.literal); } else if(data->type == LIST || data->type == PAIR) { int i; dt_err err; for(i = 0; i < data->used; i++) { err = bowl_free(data->payload.list[i]); if(err) return err; } free(data->payload.list); } else if(data->type == POINTER) { if(data->copy != SHALLOW && data->payload.pointer) free(data->payload.pointer); } free(data); return SUCCESS; } dt_err bowl_free_shallow(struct bowl *data) { if(data == NULL) return SUCCESS; if(data->type == LITERAL) { if(data->payload.literal) free(data->payload.literal); } else if(data->type == LIST || data->type == PAIR) { int i; dt_err err; for(i = 0; i < data->size; i++) { err = bowl_free(data->payload.list[i]); if(err) return err; } free(data->payload.list); } free(data); return SUCCESS; } const char *bowl_dtype(struct bowl *data) { switch(data->type) { case LITERAL: return "Literal"; case NUMERIC: return "Numeric"; case BOOLEAN: return "Boolean"; case LIST: return "List"; case PAIR: return "Pair"; case POINTER: return "Pointer"; default: return "Unknown"; } } /**************** PRIVATE UTILITY FUNCTIONS ******************/ /** * Steps down the list hirarchy of a dyntree node to * find a sub-child target. Returns 0 if it can be found. * * @param data * @param target * @return */ int list_search(struct bowl **direct_parent, struct bowl *data, struct bowl *target) { /* Check if data is actually valid */ if(data == NULL) return 1; /* Compare the pointers :) */ if(data == target) return 0; int res = 1; if(data->type == LIST || data->type == PAIR) { int i; for(i = 0; i < data->used; i++) { res = list_search(direct_parent, data->payload.list[i], target); if(res == 0) { /* Save the node that contains our child for later */ (*direct_parent) = data; return res; } } } return res; } /** * Small utility function that checks if a datablock is valid to write into. * Potentially releases previously owned memory to prevent memory leaks * * @param data The bowl object to check * @return */ dt_err data_check(struct bowl *data) { /* Check if the data block has children */ if(data->type == LIST) { printf("Won't override heap payload with data!"); return INVALID_PAYLOAD; } /* Free the existing string */ if(data->type == LITERAL) { if(data->payload.literal) free(data->payload.literal); } return SUCCESS; }