/*------------------------------------------------------------------------- The hierarchical parameter database code. For more information, see hipadaba.h copyright: GPL Mark Koennecke, June 2006 ---------------------------------------------------------------------------*/ #include #include #include #include #include "hipadaba.h" #define ABS(x) (x < 0 ? -(x) : (x)) #define HDBMAGICK 77119900 /*================== Message Stuff ========================================*/ static char set[] = { "set" }; static char get[] = { "get" }; static char update[] = { "update" }; static char treeChange[] = { "treeChange" }; static char dataSearch[] = { "dataSearch" }; static char killNode[] = { "killNode" }; /*------------------------------------------------------------------------*/ pHdbDataMessage GetHdbSetMessage(pHdbMessage toTest) { if (toTest->type == set) { return (pHdbDataMessage) toTest; } return NULL; } /*------------------------------------------------------------------------*/ pHdbDataMessage GetHdbGetMessage(pHdbMessage toTest) { if (toTest->type == get) { return (pHdbDataMessage) toTest; } return NULL; } /*------------------------------------------------------------------------*/ pHdbDataMessage GetHdbUpdateMessage(pHdbMessage toTest) { if (toTest->type == update) { return (pHdbDataMessage) toTest; } return NULL; } /*-------------------------------------------------------------------------*/ pHdbTreeChangeMessage GetHdbTreeChangeMessage(pHdbMessage toTest) { if (toTest->type == treeChange) { return (pHdbTreeChangeMessage) toTest; } return NULL; } /*-------------------------------------------------------------------------*/ pHdbDataSearch GetHdbDataSearchMessage(pHdbMessage toTest) { if (toTest->type == dataSearch) { return (pHdbDataSearch) toTest; } return NULL; } /*-------------------------------------------------------------------------*/ pHdbMessage GetHdbKillNodeMessage(pHdbMessage toTest) { if (toTest->type == killNode) { return toTest; } return NULL; } /*================== internal functions ===================================*/ void DeleteCallbackChain(pHdb node) { pHdbCallback current = NULL, thisEntry; hdbMessage killNodeMsg; killNodeMsg.type = killNode; InvokeCallbackChain(node, &killNodeMsg); current = node->callBackChain; node->callBackChain = NULL; while (current != NULL) { if (current->killFunc != NULL) { current->killFunc(current->userData); } thisEntry = current; current = (pHdbCallback) current->next; free(thisEntry); } } /*----------------------------------------------------------------------*/ void RecurseCallbackChains(pHdb node, pHdbMessage message) { pHdb current = NULL; InvokeCallbackChain(node, message); current = node->child; while (current != NULL) { RecurseCallbackChains(current, message); current = current->next; } } /*-----------------------------------------------------------------------*/ void DeleteNodeData(pHdb node) { pHdb tmp = NULL, next = NULL; if (node == NULL) { return; } DeleteCallbackChain(node); if (node->properties != NULL) { DeleteStringDict(node->properties); } if (node->name != NULL) { free(node->name); } if (node->path != NULL) { free(node->path); } ReleaseHdbValue(&node->value); node->magic = 000000; while (node->child != NULL) { tmp = node->child; next = node->child->next; DeleteNodeData(tmp); node->child = next; } free(node); } /*------------------------------------------------------------------------*/ static pHdbCallback CleanCallbackChain(pHdbCallback head) { pHdbCallback current = head; pHdbCallback next; pHdbCallback *ptr2last = &head; while (current != NULL) { if (current->killFlag == 1) { next = current->next; /* * unlink */ *ptr2last = next; /* * delete */ if (current->killFunc != NULL) { current->killFunc(current->userData); } free(current); /* * move on */ current = next; } else { ptr2last = ¤t->next; current = current->next; } } return head; } /*-------------------------------------------------------------------------*/ int InvokeCallbackChain(pHdb node, pHdbMessage message) { pHdbCallback current = node->callBackChain; hdbCallbackReturn status; int killFlag = 0; while (current != NULL) { status = current->userCallback(node, current->userData, message); switch (status) { case hdbAbort: return 0; break; case hdbKill: current->killFlag = 1; killFlag = 1; break; case hdbContinue: break; } current = current->next; } if (killFlag == 1) { node->callBackChain = CleanCallbackChain(node->callBackChain); } return 1; } /*-----------------------------------------------------------------------*/ void SendTreeChangeMessage(pHdb node, void *callData) { hdbTreeChangeMessage treeChangeMes; treeChangeMes.type = treeChange; treeChangeMes.callData = callData; InvokeCallbackChain(node, (pHdbMessage) & treeChangeMes); } /*------------------------------------------------------------------------*/ void RemoveHdbNodeFromParent(pHdb node, void *callData) { pHdb parent = NULL; pHdb current = NULL; hdbTreeChangeMessage treeChangeMes; parent = node->mama; if (parent != NULL) { if (parent->child == node) { parent->child = node->next; } else { current = parent->child; while (current->next != node) { current = current->next; } current->next = current->next->next; } SendTreeChangeMessage(parent, callData); node->mama = NULL; } } /*----------------------------------------------------------------------*/ int CountHdbChildren(pHdb node) { int count = 0; pHdb current = NULL; current = node->child; while (current != NULL) { current = current->next; count++; } return count; } /*----------------------------------------------------------------------*/ char *hdbTrim(char *str) { char *ibuf = str, *obuf = str; int i = 0, cnt = 0; /* ** Trap NULL */ if (str) { /* ** Remove leading spaces (from RMLEAD.C) */ for (ibuf = str; *ibuf && isspace(*ibuf); ++ibuf); if (str != ibuf) memmove(str, ibuf, ibuf - str); /* ** Collapse embedded spaces (from LV1WS.C) */ while (*ibuf) { if (isspace(*ibuf) && cnt) ibuf++; else { if (!isspace(*ibuf)) cnt = 0; else { *ibuf = ' '; cnt = 1; } obuf[i++] = *ibuf++; } } obuf[i] = '\0'; /* ** Remove trailing spaces (from RMTRAIL.C) */ while (--i >= 0) { if (!isspace(obuf[i])) break; } obuf[++i] = '\0'; } return str; } /*------------------------------------------------------------------------*/ static pHdb locateChild(pHdb root, char *name) { pHdb current = NULL; current = root->child; while (current != NULL) { if (strcmp(current->name, name) == 0) { return current; } current = current->next; } return NULL; } /*================= data functions ========================================*/ hdbValue makeHdbValue(int datatype, int length) { hdbValue val; memset(&val, 0, sizeof(hdbValue)); val.dataType = datatype; val.doNotFree = 0; switch (datatype) { case HIPINTAR: case HIPINTVARAR: val.arrayLength = length; val.v.intArray = malloc(length * sizeof(int)); if (val.v.intArray != NULL) { memset(val.v.intArray, 0, length * sizeof(int)); } break; case HIPFLOATAR: case HIPFLOATVARAR: val.arrayLength = length; val.v.floatArray = malloc(length * sizeof(double)); if (val.v.floatArray != NULL) { memset(val.v.floatArray, 0, length * sizeof(double)); } break; case HIPTEXT: val.v.text = strdup("UNKNOWN"); val.arrayLength = length; break; } return val; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbInt(int initValue) { hdbValue result; result.dataType = HIPINT; result.arrayLength = 1; result.v.intValue = initValue; result.doNotFree = 0; return result; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbFloat(double initValue) { hdbValue result; result.dataType = HIPFLOAT; result.arrayLength = 1; result.v.doubleValue = initValue; result.doNotFree = 0; return result; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbText(char *initText) { hdbValue result; result.dataType = HIPTEXT; result.v.text = initText; /* no strdup here ! */ result.arrayLength = strlen(initText); result.doNotFree = 0; return result; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbIntArray(int length, int *data) { hdbValue result; result.dataType = HIPINTAR; result.arrayLength = length; result.v.intArray = data; result.doNotFree = 0; return result; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbFloatArray(int length, double *data) { hdbValue result; result.dataType = HIPFLOATAR; result.arrayLength = length; result.v.floatArray = data; result.doNotFree = 0; return result; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbFunc(voidFunc * func) { hdbValue result; result.dataType = HIPFUNC; result.v.func = func; result.doNotFree = 0; return result; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbObj(void *obj) { hdbValue result; result.dataType = HIPOBJ; result.v.obj = obj; result.doNotFree = 0; return result; } /*-------------------------------------------------------------------------*/ void ReleaseHdbValue(hdbValue * v) { if (v->doNotFree == 1) { return; } switch (v->dataType) { case HIPTEXT: if (v->v.text != NULL) { free(v->v.text); } break; case HIPINTAR: case HIPINTVARAR: if (v->v.intArray != NULL) { free(v->v.intArray); } break; case HIPFLOATAR: case HIPFLOATVARAR: if (v->v.floatArray != NULL) { free(v->v.floatArray); } break; } } /*-----------------------------------------------------------------------------*/ static unsigned short fletcher16( char *data, size_t len) { unsigned short sum1 = 0xff, sum2 = 0xff, result; unsigned char checkA, checkB; if(data == NULL){ return 0; } while (len) { size_t tlen = len > 21 ? 21 : len; len -= tlen; do { sum1 += *data++; sum2 += sum1; } while (--tlen); sum1 = (sum1 & 0xff) + (sum1 >> 8); sum2 = (sum2 & 0xff) + (sum2 >> 8); } /* Second reduction step to reduce sums to 8 bits */ sum1 = (sum1 & 0xff) + (sum1 >> 8); sum2 = (sum2 & 0xff) + (sum2 >> 8); checkA = (unsigned char)sum1; checkB = (unsigned char)sum2; result = checkA; result = result << 8 | checkB; return result ; } /*------------------------------------------------------------------------*/ #define MAXLEN 65536 unsigned short getHdbCheckSum(hdbValue *val) { char *data; size_t len; len = getHdbValueLength(*val); /* if(len > MAXLEN){ len = MAXLEN; } */ switch (val->dataType) { case HIPNONE: return 0; break; case HIPINT: data = (char *)&val->v.intValue; return fletcher16(data,len); break; case HIPFLOAT: data = (char *)&val->v.doubleValue; return fletcher16(data,len); break; case HIPTEXT: data = val->v.text; return fletcher16(data,len); break; case HIPINTAR: case HIPINTVARAR: data = (char *)val->v.intArray; return fletcher16(data,len); break; case HIPFLOATAR: case HIPFLOATVARAR: data = (char *)val->v.floatArray; return fletcher16(data,len); break; case HIPOBJ: data = (char *)val->v.obj; return fletcher16(data,len); break; case HIPFUNC: data = (char *)val->v.func; return fletcher16(data,len); break; default: assert(0); break; } return 0; } /*------------------------------------------------------------------------*/ int compareHdbValue(hdbValue v1, hdbValue v2) { int i; if (v1.dataType != v2.dataType) { return 0; } if((v1.doNotFree == 1 && v1.v.obj == NULL) || (v2.doNotFree == 1 && v2.v.obj == NULL)){ return 0; } switch (v1.dataType) { case HIPNONE: return 0; break; case HIPINT: if (v1.v.intValue == v2.v.intValue) { return 1; } else { return 0; } break; case HIPFLOAT: if (ABS(v1.v.doubleValue - v2.v.doubleValue) < .0001) { /* DFC */ return 1; } else { return 0; } break; case HIPTEXT: if (v1.v.text == NULL || v2.v.text == NULL) { return 0; } if (strcmp(v1.v.text, v2.v.text) == 0) { return 1; } else { return 0; } break; case HIPINTAR: case HIPINTVARAR: if (v1.arrayLength != v2.arrayLength) { return 0; } if (v1.v.intArray == NULL || v2.v.intArray == NULL) { return 0; } for (i = 0; i < v1.arrayLength; i++) { if (v1.v.intArray[i] != v2.v.intArray[i]) { return 0; } } return 1; break; case HIPFLOATAR: case HIPFLOATVARAR: if (v1.arrayLength != v2.arrayLength) { return 0; } if (v1.v.floatArray == NULL || v2.v.floatArray == NULL) { return 0; } for (i = 0; i < v1.arrayLength; i++) { if (ABS(v1.v.floatArray[i] - v2.v.floatArray[i]) > .01) { return 0; } } return 1; break; case HIPOBJ: if (v2.v.obj == v1.v.obj) { return 1; } else { return 0; } break; case HIPFUNC: if (v2.v.func == v1.v.func) { return 1; } else { return 0; } break; default: assert(0); break; } return 0; } /*-------------------------------------------------------------------------*/ int cloneHdbValue(hdbValue * source, hdbValue * clone) { memset(clone, 0, sizeof(hdbValue)); clone->v.text = NULL; /* this sets all pointers in the union to NULL */ clone->dataType = source->dataType; return copyHdbValue(source, clone); } /*-------------------------------------------------------------------------*/ int getHdbValueLength(hdbValue v) { int length = 0; switch (v.dataType) { case HIPNONE: break; case HIPINT: length = sizeof(int); break; case HIPFLOAT: length = sizeof(double); break; case HIPINTAR: case HIPINTVARAR: length = v.arrayLength * sizeof(int); break; case HIPFLOATAR: case HIPFLOATVARAR: length = v.arrayLength * sizeof(double); break; case HIPTEXT: length = strlen(v.v.text); break; case HIPOBJ: length = sizeof(void *); break; case HIPFUNC: length = sizeof(voidFunc *); break; } return length; } /*================= node functions ========================================*/ pHdb MakeHipadabaNode(char *name, int datatype, int length) { pHdb pNew = NULL; pNew = malloc(sizeof(Hdb)); if (pNew == NULL) { return NULL; } memset(pNew, 0, sizeof(Hdb)); pNew->magic = HDBMAGICK; pNew->name = strdup(name); pNew->value.dataType = datatype; pNew->properties = CreateStringDict(); if (pNew->properties == NULL || pNew->name == NULL) { return NULL; } switch (datatype) { case HIPINTAR: case HIPINTVARAR: pNew->value.arrayLength = length; pNew->value.v.intArray = malloc(length * sizeof(int)); if (pNew->value.v.intArray == NULL) { return NULL; } memset(pNew->value.v.intArray, 0, length * sizeof(int)); break; case HIPFLOATAR: case HIPFLOATVARAR: pNew->value.arrayLength = length; pNew->value.v.floatArray = malloc(length * sizeof(double)); if (pNew->value.v.floatArray == NULL) { return NULL; } memset(pNew->value.v.floatArray, 0, length * sizeof(double)); break; case HIPTEXT: pNew->value.arrayLength = length; pNew->value.v.text = strdup("UNKNOWN"); break; } return pNew; } /*-------------------------------------------------------------------------*/ void AddHipadabaChild(pHdb parent, pHdb child, void *callData) { pHdb current = NULL, prev = NULL; assert(parent != NULL); if (child == NULL) { return; } current = parent->child; child->mama = parent; if (current == NULL) { parent->child = child; child->next = NULL; } else { /* * step to end of child chain */ while (current != NULL) { assert(strcmp(current->name, child->name) != 0); prev = current; current = current->next; } child->next = NULL; prev->next = child; } SendTreeChangeMessage(parent, callData); } /*--------------------------------------------------------------------------*/ void DeleteHipadabaNode(pHdb node, void *callData) { pHdb current = NULL, tmp = NULL; if (node == NULL) { return; } RemoveHdbNodeFromParent(node, callData); DeleteNodeData(node); } /*--------------------------------------------------------------------------*/ int isHdbNodeValid(pHdb node) { if (node == NULL) { return 0; } if (node->magic == HDBMAGICK) { return 1; } else { return 0; } } /*--------------------------------------------------------------------------*/ pHdb GetHipadabaNode(pHdb root, char *puth) { pHdb resultNode = NULL; char *separator = NULL; char *path = NULL, *pathData; /* * I need to make a copy in order to get the path in writable memory. * Otherwise we SEGFAULT in hdbTrim when this function is called with * a string constant in puth */ pathData = strdup(puth); path = pathData; if (path == NULL) { return NULL; } path = hdbTrim(path); if (strcmp(path, "/") == 0 || strlen(path) == 0) { free(pathData); return root; } if (path[0] == '/') { path++; } separator = strchr(path, '/'); if (separator == NULL) { resultNode = locateChild(root, path); free(pathData); return resultNode; } else { *separator = '\0'; resultNode = locateChild(root, path); if (resultNode == NULL) { free(pathData); return NULL; } else { separator++; resultNode = GetHipadabaNode(resultNode, separator); free(pathData); return resultNode; } } } /*--------------------------------------------------------------------------*/ char *GetHipadabaPath(pHdb node) { pHdb nodeStack[64]; int depth = 0, length = 0, i; pHdb current = NULL; char *pPtr = NULL; /** * build a nodestack and find out required string length for path */ current = node; while (current != NULL) { length += strlen(current->name) + 1; nodeStack[depth] = current; depth++; assert(depth < 64); current = current->mama; } pPtr = malloc(length * sizeof(char)); if (pPtr == NULL) { return NULL; } memset(pPtr, 0, length * sizeof(char)); /* * we wish to decremement by one because above loop * increments one to many and we wish to ignore the * root node */ for (i = depth - 2; i >= 0; i--) { strcat(pPtr, "/"); strcat(pPtr, nodeStack[i]->name); } node->path = pPtr; return pPtr; } /*==================== Callback Functions ==================================*/ pHdbCallback MakeHipadabaCallback(hdbCallbackFunction func, void *userData, killUserData killFunc) { pHdbCallback pNew = NULL; assert(func != NULL); pNew = malloc(sizeof(hdbCallback)); if (pNew == NULL) { return NULL; } memset(pNew, 0, sizeof(hdbCallback)); pNew->userCallback = func; pNew->userData = userData; pNew->killFunc = killFunc; return pNew; } /*-------------------------------------------------------------------*/ void AppendHipadabaCallback(pHdb node, pHdbCallback newCB) { pHdbCallback current = NULL; assert(node); current = node->callBackChain; newCB->next = NULL; if (current == NULL) { node->callBackChain = newCB; return; } while (current->next != NULL) { current = (pHdbCallback) current->next; } current->next = newCB; } /*---------------------------------------------------------------------------*/ void PrependHipadabaCallback(pHdb node, pHdbCallback newCB) { assert(node != NULL); newCB->next = node->callBackChain; node->callBackChain = newCB; } /*----------------------------------------------------------------------------*/ void *FindHdbCallbackData(pHdb node, void *userPtr) { hdbDataSearch dsm; dsm.type = dataSearch; dsm.testPtr = userPtr; dsm.result = NULL; InvokeCallbackChain(node, (pHdbMessage) & dsm); return dsm.result; } /*=================== parameter interface ====================================*/ static int canCopy(hdbValue * source, hdbValue * target) { if (target->dataType == HIPINTVARAR) { if (source->dataType == HIPINTAR || source->dataType == HIPINTVARAR) { return 1; } } if (target->dataType == HIPFLOATVARAR) { if (source->dataType == HIPFLOATAR || source->dataType == HIPFLOATVARAR) { return 1; } } if (source->dataType != target->dataType) { return 0; } else { return 1; } } /*----------------------------------------------------------------------------*/ int copyHdbValue(hdbValue * source, hdbValue * target) { int i; if (!canCopy(source, target)) { return 0; } switch (source->dataType) { case HIPNONE: break; case HIPINT: target->v.intValue = source->v.intValue; break; case HIPFLOAT: target->v.doubleValue = source->v.doubleValue; break; case HIPTEXT: if (target->v.text != NULL) { free(target->v.text); } target->v.text = strdup(source->v.text); break; case HIPINTAR: case HIPINTVARAR: if (target->arrayLength != source->arrayLength || target->v.intArray == NULL) { if (target->v.intArray != NULL) { free(target->v.intArray); } target->v.intArray = malloc(source->arrayLength * sizeof(int)); if (target->v.intArray == NULL) { return 0; } memset(target->v.intArray, 0, source->arrayLength * sizeof(int)); target->arrayLength = source->arrayLength; } if (source->v.intArray != NULL) { /* for (i = 0; i < source->arrayLength; i++) { target->v.intArray[i] = source->v.intArray[i]; } */ memcpy(target->v.intArray,source->v.intArray,source->arrayLength*sizeof(int)); } break; case HIPFLOATAR: case HIPFLOATVARAR: if (target->arrayLength != source->arrayLength || target->v.floatArray == NULL) { if (target->v.floatArray != NULL) { free(target->v.floatArray); } target->v.floatArray = malloc(source->arrayLength * sizeof(double)); if (target->v.floatArray == NULL) { return 0; } memset(target->v.floatArray, 0, source->arrayLength * sizeof(double)); target->arrayLength = source->arrayLength; } if (source->v.floatArray != NULL) { for (i = 0; i < source->arrayLength; i++) { target->v.floatArray[i] = source->v.floatArray[i]; } } break; case HIPOBJ: target->v.obj = source->v.obj; break; case HIPFUNC: target->v.func = source->v.func; break; default: /* * unknown data type */ assert(0); break; } return 1; } /*---------------------------------------------------------------------------*/ static int SendDataMessage(pHdb node, char *type, hdbValue *v, void *callData) { hdbDataMessage dataMes; assert(type == set || type == get || type == update); dataMes.type = type; dataMes.v = v; dataMes.callData = callData; return InvokeCallbackChain(node, (pHdbMessage) & dataMes); } /*----------------------------------------------------------------------------*/ int SetHipadabaPar(pHdb node, hdbValue v, void *callData) { return SendDataMessage(node, set, &v, callData); } /*-----------------------------------------------------------------------------*/ int UpdateHipadabaPar(pHdb node, hdbValue v, void *callData) { int status; status = SendDataMessage(node, update, &v, callData); if (status == 1) { copyHdbValue(&v, &node->value); } return status; } /*-----------------------------------------------------------------------------*/ int NotifyHipadabaPar(pHdb node, void *callData) { SendDataMessage(node, update, &node->value, callData); return 1; } /*-----------------------------------------------------------------------------*/ int GetHipadabaPar(pHdb node, hdbValue *v, void *callData) { int status; unsigned short checksum; memset(v,0,sizeof(hdbValue)); v->dataType = node->value.dataType; checksum = getHdbCheckSum(&node->value); status = SendDataMessage(node, get, v, callData); copyHdbValue(&node->value, v); if(getHdbCheckSum(&node->value) != checksum){ NotifyHipadabaPar(node, callData); } if (status != 1) { return status; } return 1; } /*----------------------------------------------------------------------------*/ static int calcDataLength(pHdb node, int testLength) { int length = 0; length = getHdbValueLength(node->value); if (node->value.dataType == HIPFLOATVARAR || node->value.dataType == HIPINTVARAR || node->value.dataType == HIPTEXT) { length = testLength; } return length; } /*============================= Property Functions ==========================*/ void SetHdbProperty(pHdb node, char *key, char *value) { if (node != NULL && key != NULL && node->properties != NULL) { if (value == NULL) { StringDictDelete(node->properties, key); } else if (StringDictExists(node->properties, key)) { StringDictUpdate(node->properties, key, value); } else { StringDictAddPair(node->properties, key, value); } } } /*---------------------------------------------------------------------------*/ int HasHdbProperty(pHdb node, char *key) { if (node != NULL && node->properties != NULL) { return StringDictExists(node->properties, key); } else { return 0; } } /*---------------------------------------------------------------------------*/ int GetHdbProperty(pHdb node, char *key, char *value, int len) { if (node != NULL && node->properties != NULL) { return StringDictGet(node->properties, key, value, len); } else { return 0; } } /*---------------------------------------------------------------------------*/ char *GetHdbProp(pHdb node, char *key) { if (node != NULL && node->properties != NULL) { return StringDictGetShort(node->properties, key); } else { return NULL; } } /*---------------------------------------------------------------------------*/ void InitHdbPropertySearch(pHdb node) { if (node != NULL && node->properties != NULL) { StringDictKillScan(node->properties); } } /*--------------------------------------------------------------------------*/ const char *GetNextHdbProperty(pHdb node, char *value, int len) { if (node != NULL && node->properties != NULL) { return StringDictGetNext(node->properties, value, len); } else { return NULL; } }