123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * Copyright 2018 (c) basysKom GmbH <opensource@basyskom.com> (Author: Peter Rustler)
- */
- #include "ua_historydatabackend_memory.h"
- #include <limits.h>
- #include <string.h>
- typedef struct {
- UA_DateTime timestamp;
- UA_DataValue value;
- } UA_DataValueMemoryStoreItem;
- static void
- UA_DataValueMemoryStoreItem_deleteMembers(UA_DataValueMemoryStoreItem* item) {
- UA_DateTime_deleteMembers(&item->timestamp);
- UA_DataValue_deleteMembers(&item->value);
- }
- typedef struct {
- UA_NodeId nodeId;
- UA_DataValueMemoryStoreItem **dataStore;
- size_t storeEnd;
- size_t storeSize;
- } UA_NodeIdStoreContextItem_backend_memory;
- static void
- UA_NodeIdStoreContextItem_deleteMembers(UA_NodeIdStoreContextItem_backend_memory* item) {
- UA_NodeId_deleteMembers(&item->nodeId);
- for (size_t i = 0; i < item->storeEnd; ++i) {
- UA_DataValueMemoryStoreItem_deleteMembers(item->dataStore[i]);
- UA_free(item->dataStore[i]);
- }
- UA_free(item->dataStore);
- }
- typedef struct {
- UA_NodeIdStoreContextItem_backend_memory *dataStore;
- size_t storeEnd;
- size_t storeSize;
- size_t initialStoreSize;
- } UA_MemoryStoreContext;
- static void
- UA_MemoryStoreContext_deleteMembers(UA_MemoryStoreContext* ctx) {
- for (size_t i = 0; i < ctx->storeEnd; ++i) {
- UA_NodeIdStoreContextItem_deleteMembers(&ctx->dataStore[i]);
- }
- UA_free(ctx->dataStore);
- memset(ctx, 0, sizeof(UA_MemoryStoreContext));
- }
- static UA_NodeIdStoreContextItem_backend_memory *
- getNewNodeIdContext_backend_memory(UA_MemoryStoreContext* context,
- UA_Server *server,
- const UA_NodeId *nodeId) {
- UA_MemoryStoreContext *ctx = (UA_MemoryStoreContext*)context;
- if (ctx->storeEnd >= ctx->storeSize) {
- size_t newStoreSize = ctx->storeSize * 2;
- if (newStoreSize == 0)
- return NULL;
- ctx->dataStore = (UA_NodeIdStoreContextItem_backend_memory*)UA_realloc(ctx->dataStore, (newStoreSize * sizeof(UA_NodeIdStoreContextItem_backend_memory)));
- if (!ctx->dataStore) {
- ctx->storeSize = 0;
- return NULL;
- }
- ctx->storeSize = newStoreSize;
- }
- UA_NodeIdStoreContextItem_backend_memory *item = &ctx->dataStore[ctx->storeEnd];
- UA_NodeId_copy(nodeId, &item->nodeId);
- UA_DataValueMemoryStoreItem ** store = (UA_DataValueMemoryStoreItem **)UA_calloc(ctx->initialStoreSize, sizeof(UA_DataValueMemoryStoreItem*));
- if (!store) {
- UA_NodeIdStoreContextItem_deleteMembers(item);
- return NULL;
- }
- item->dataStore = store;
- item->storeSize = ctx->initialStoreSize;
- item->storeEnd = 0;
- ++ctx->storeEnd;
- return item;
- }
- static UA_NodeIdStoreContextItem_backend_memory *
- getNodeIdStoreContextItem_backend_memory(UA_MemoryStoreContext* context,
- UA_Server *server,
- const UA_NodeId *nodeId)
- {
- for (size_t i = 0; i < context->storeEnd; ++i) {
- if (UA_NodeId_equal(nodeId, &context->dataStore[i].nodeId)) {
- return &context->dataStore[i];
- }
- }
- return getNewNodeIdContext_backend_memory(context, server, nodeId);
- }
- static UA_Boolean
- binarySearch_backend_memory(const UA_NodeIdStoreContextItem_backend_memory* item,
- const UA_DateTime timestamp,
- size_t *index) {
- if (item->storeEnd == 0) {
- *index = item->storeEnd;
- return false;
- }
- size_t min = 0;
- size_t max = item->storeEnd - 1;
- while (min <= max) {
- *index = (min + max) / 2;
- if (item->dataStore[*index]->timestamp == timestamp) {
- return true;
- } else if (item->dataStore[*index]->timestamp < timestamp) {
- if (*index == item->storeEnd - 1) {
- *index = item->storeEnd;
- return false;
- }
- min = *index + 1;
- } else {
- if (*index == 0)
- return false;
- max = *index - 1;
- }
- }
- *index = min;
- return false;
- }
- static size_t
- resultSize_backend_memory(UA_Server *server,
- void *context,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId * nodeId,
- size_t startIndex,
- size_t endIndex) {
- const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);
- if (item->storeEnd == 0
- || startIndex == item->storeEnd
- || endIndex == item->storeEnd)
- return 0;
- return endIndex - startIndex + 1;
- }
- static size_t
- getDateTimeMatch_backend_memory(UA_Server *server,
- void *context,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId * nodeId,
- const UA_DateTime timestamp,
- const MatchStrategy strategy) {
- const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);
- size_t current;
- UA_Boolean retval = binarySearch_backend_memory(item, timestamp, ¤t);
- if ((strategy == MATCH_EQUAL
- || strategy == MATCH_EQUAL_OR_AFTER
- || strategy == MATCH_EQUAL_OR_BEFORE)
- && retval)
- return current;
- switch (strategy) {
- case MATCH_AFTER:
- if (retval)
- return current+1;
- return current;
- case MATCH_EQUAL_OR_AFTER:
- return current;
- case MATCH_EQUAL_OR_BEFORE:
- // retval == true aka "equal" is handled before
- // Fall through if !retval
- case MATCH_BEFORE:
- if (current > 0)
- return current-1;
- else
- return item->storeEnd;
- default:
- break;
- }
- return item->storeEnd;
- }
- static UA_StatusCode
- serverSetHistoryData_backend_memory(UA_Server *server,
- void *context,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId * nodeId,
- UA_Boolean historizing,
- const UA_DataValue *value)
- {
- UA_NodeIdStoreContextItem_backend_memory *item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);
- if (item->storeEnd >= item->storeSize) {
- size_t newStoreSize = item->storeSize == 0 ? INITIAL_MEMORY_STORE_SIZE : item->storeSize * 2;
- item->dataStore = (UA_DataValueMemoryStoreItem **)UA_realloc(item->dataStore, (newStoreSize * sizeof(UA_DataValueMemoryStoreItem*)));
- if (!item->dataStore) {
- item->storeSize = 0;
- return UA_STATUSCODE_BADOUTOFMEMORY;
- }
- item->storeSize = newStoreSize;
- }
- UA_DateTime timestamp = 0;
- if (value->hasSourceTimestamp) {
- timestamp = value->sourceTimestamp;
- } else if (value->hasServerTimestamp) {
- timestamp = value->serverTimestamp;
- } else {
- timestamp = UA_DateTime_now();
- }
- UA_DataValueMemoryStoreItem *newItem = (UA_DataValueMemoryStoreItem *)UA_calloc(1, sizeof(UA_DataValueMemoryStoreItem));
- newItem->timestamp = timestamp;
- UA_DataValue_copy(value, &newItem->value);
- size_t index = getDateTimeMatch_backend_memory(server,
- context,
- NULL,
- NULL,
- nodeId,
- timestamp,
- MATCH_EQUAL_OR_AFTER);
- if (item->storeEnd > 0 && index < item->storeEnd) {
- memmove(&item->dataStore[index+1], &item->dataStore[index], sizeof(UA_DataValueMemoryStoreItem*) * (item->storeEnd - index));
- }
- item->dataStore[index] = newItem;
- ++item->storeEnd;
- return UA_STATUSCODE_GOOD;
- }
- static void
- UA_MemoryStoreContext_delete(UA_MemoryStoreContext* ctx) {
- UA_MemoryStoreContext_deleteMembers(ctx);
- UA_free(ctx);
- }
- static size_t
- getEnd_backend_memory(UA_Server *server,
- void *context,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId * nodeId) {
- const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);;
- return item->storeEnd;
- }
- static size_t
- lastIndex_backend_memory(UA_Server *server,
- void *context,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId * nodeId) {
- const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);;
- if (item->storeEnd == 0)
- return 0;
- return item->storeEnd - 1;
- }
- static size_t
- firstIndex_backend_memory(UA_Server *server,
- void *context,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId * nodeId) {
- return 0;
- }
- static UA_Boolean
- boundSupported_backend_memory(UA_Server *server,
- void *context,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId * nodeId) {
- return true;
- }
- static UA_Boolean
- timestampsToReturnSupported_backend_memory(UA_Server *server,
- void *context,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId *nodeId,
- const UA_TimestampsToReturn timestampsToReturn) {
- const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);;
- if (item->storeEnd == 0) {
- return true;
- }
- if (timestampsToReturn == UA_TIMESTAMPSTORETURN_NEITHER
- || timestampsToReturn == UA_TIMESTAMPSTORETURN_INVALID
- || (timestampsToReturn == UA_TIMESTAMPSTORETURN_SERVER
- && !item->dataStore[0]->value.hasServerTimestamp)
- || (timestampsToReturn == UA_TIMESTAMPSTORETURN_SOURCE
- && !item->dataStore[0]->value.hasSourceTimestamp)
- || (timestampsToReturn == UA_TIMESTAMPSTORETURN_BOTH
- && !(item->dataStore[0]->value.hasSourceTimestamp
- && item->dataStore[0]->value.hasServerTimestamp))) {
- return false;
- }
- return true;
- }
- static const UA_DataValue*
- getDataValue_backend_memory(UA_Server *server,
- void *context,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId * nodeId, size_t index) {
- const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);;
- return &item->dataStore[index]->value;
- }
- static UA_StatusCode
- UA_DataValue_backend_copyRange(const UA_DataValue *src, UA_DataValue *dst,
- const UA_NumericRange range)
- {
- memcpy(dst, src, sizeof(UA_DataValue));
- if (src->hasValue)
- return UA_Variant_copyRange(&src->value, &dst->value, range);
- return UA_STATUSCODE_BADDATAUNAVAILABLE;
- }
- static UA_StatusCode
- copyDataValues_backend_memory(UA_Server *server,
- void *context,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId * nodeId,
- size_t startIndex,
- size_t endIndex,
- UA_Boolean reverse,
- size_t maxValues,
- UA_NumericRange range,
- UA_Boolean releaseContinuationPoints,
- const UA_ByteString *continuationPoint,
- UA_ByteString *outContinuationPoint,
- size_t * providedValues,
- UA_DataValue * values)
- {
- size_t skip = 0;
- if (continuationPoint->length > 0) {
- if (continuationPoint->length == sizeof(size_t)) {
- skip = *((size_t*)(continuationPoint->data));
- } else {
- return UA_STATUSCODE_BADCONTINUATIONPOINTINVALID;
- }
- }
- const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);;
- size_t index = startIndex;
- size_t counter = 0;
- size_t skipedValues = 0;
- if (reverse) {
- while (index >= endIndex && index < item->storeEnd && counter < maxValues) {
- if (skipedValues++ >= skip) {
- if (range.dimensionsSize > 0) {
- UA_DataValue_backend_copyRange(&item->dataStore[index]->value, &values[counter], range);
- } else {
- UA_DataValue_copy(&item->dataStore[index]->value, &values[counter]);
- }
- ++counter;
- }
- --index;
- }
- } else {
- while (index <= endIndex && counter < maxValues) {
- if (skipedValues++ >= skip) {
- if (range.dimensionsSize > 0) {
- UA_DataValue_backend_copyRange(&item->dataStore[index]->value, &values[counter], range);
- } else {
- UA_DataValue_copy(&item->dataStore[index]->value, &values[counter]);
- }
- ++counter;
- }
- ++index;
- }
- }
- if (providedValues)
- *providedValues = counter;
- if ((!reverse && (endIndex-startIndex-skip+1) > counter) || (reverse && (startIndex-endIndex-skip+1) > counter)) {
- outContinuationPoint->length = sizeof(size_t);
- size_t t = sizeof(size_t);
- outContinuationPoint->data = (UA_Byte*)UA_malloc(t);
- *((size_t*)(outContinuationPoint->data)) = skip + counter;
- }
- return UA_STATUSCODE_GOOD;
- }
- static UA_StatusCode
- insertDataValue_backend_memory(UA_Server *server,
- void *hdbContext,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId *nodeId,
- const UA_DataValue *value)
- {
- if (!value->hasSourceTimestamp && !value->hasServerTimestamp)
- return UA_STATUSCODE_BADINVALIDTIMESTAMP;
- const UA_DateTime timestamp = value->hasSourceTimestamp ? value->sourceTimestamp : value->serverTimestamp;
- UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)hdbContext, server, nodeId);
- size_t index = getDateTimeMatch_backend_memory(server,
- hdbContext,
- sessionId,
- sessionContext,
- nodeId,
- timestamp,
- MATCH_EQUAL_OR_AFTER);
- if (item->storeEnd != index && item->dataStore[index]->timestamp == timestamp)
- return UA_STATUSCODE_BADENTRYEXISTS;
- if (item->storeEnd >= item->storeSize) {
- size_t newStoreSize = item->storeSize == 0 ? INITIAL_MEMORY_STORE_SIZE : item->storeSize * 2;
- item->dataStore = (UA_DataValueMemoryStoreItem **)UA_realloc(item->dataStore, (newStoreSize * sizeof(UA_DataValueMemoryStoreItem*)));
- if (!item->dataStore) {
- item->storeSize = 0;
- return UA_STATUSCODE_BADOUTOFMEMORY;
- }
- item->storeSize = newStoreSize;
- }
- UA_DataValueMemoryStoreItem *newItem = (UA_DataValueMemoryStoreItem *)UA_calloc(1, sizeof(UA_DataValueMemoryStoreItem));
- newItem->timestamp = timestamp;
- UA_DataValue_copy(value, &newItem->value);
- if (item->storeEnd > 0 && index < item->storeEnd) {
- memmove(&item->dataStore[index+1], &item->dataStore[index], sizeof(UA_DataValueMemoryStoreItem*) * (item->storeEnd - index));
- }
- item->dataStore[index] = newItem;
- ++item->storeEnd;
- return UA_STATUSCODE_GOOD;
- }
- static UA_StatusCode
- replaceDataValue_backend_memory(UA_Server *server,
- void *hdbContext,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId *nodeId,
- const UA_DataValue *value)
- {
- if (!value->hasSourceTimestamp && !value->hasServerTimestamp)
- return UA_STATUSCODE_BADINVALIDTIMESTAMP;
- const UA_DateTime timestamp = value->hasSourceTimestamp ? value->sourceTimestamp : value->serverTimestamp;
- UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)hdbContext, server, nodeId);
- size_t index = getDateTimeMatch_backend_memory(server,
- hdbContext,
- sessionId,
- sessionContext,
- nodeId,
- timestamp,
- MATCH_EQUAL);
- if (index == item->storeEnd)
- return UA_STATUSCODE_BADNOENTRYEXISTS;
- UA_DataValue_deleteMembers(&item->dataStore[index]->value);
- UA_DataValue_copy(value, &item->dataStore[index]->value);
- return UA_STATUSCODE_GOOD;
- }
- static UA_StatusCode
- updateDataValue_backend_memory(UA_Server *server,
- void *hdbContext,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId *nodeId,
- const UA_DataValue *value)
- {
- // we first try to replace, because it is cheap
- UA_StatusCode ret = replaceDataValue_backend_memory(server,
- hdbContext,
- sessionId,
- sessionContext,
- nodeId,
- value);
- if (ret == UA_STATUSCODE_GOOD)
- return UA_STATUSCODE_GOODENTRYREPLACED;
- ret = insertDataValue_backend_memory(server,
- hdbContext,
- sessionId,
- sessionContext,
- nodeId,
- value);
- if (ret == UA_STATUSCODE_GOOD)
- return UA_STATUSCODE_GOODENTRYINSERTED;
- return ret;
- }
- static UA_StatusCode
- removeDataValue_backend_memory(UA_Server *server,
- void *hdbContext,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId *nodeId,
- UA_DateTime startTimestamp,
- UA_DateTime endTimestamp)
- {
- UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)hdbContext, server, nodeId);
- size_t storeEnd = item->storeEnd;
- // The first index which will be deleted
- size_t index1;
- // the first index which is not deleted
- size_t index2;
- if (startTimestamp > endTimestamp) {
- return UA_STATUSCODE_BADTIMESTAMPNOTSUPPORTED;
- }
- if (startTimestamp == endTimestamp) {
- index1 = getDateTimeMatch_backend_memory(server,
- hdbContext,
- sessionId,
- sessionContext,
- nodeId,
- startTimestamp,
- MATCH_EQUAL);
- if (index1 == storeEnd)
- return UA_STATUSCODE_BADNODATA;
- index2 = index1 + 1;
- } else {
- index1 = getDateTimeMatch_backend_memory(server,
- hdbContext,
- sessionId,
- sessionContext,
- nodeId,
- startTimestamp,
- MATCH_EQUAL_OR_AFTER);
- index2 = getDateTimeMatch_backend_memory(server,
- hdbContext,
- sessionId,
- sessionContext,
- nodeId,
- endTimestamp,
- MATCH_BEFORE);
- if (index2 == storeEnd || index1 == storeEnd || index1 > index2 )
- return UA_STATUSCODE_BADNODATA;
- ++index2;
- }
- #ifndef __clang_analyzer__
- for (size_t i = index1; i < index2; ++i) {
- UA_DataValueMemoryStoreItem_deleteMembers(item->dataStore[i]);
- UA_free(item->dataStore[i]);
- }
- memmove(&item->dataStore[index1], &item->dataStore[index2], sizeof(UA_DataValueMemoryStoreItem*) * (item->storeEnd - index2));
- item->storeEnd -= index2 - index1;
- #else
- (void)index1;
- (void)index2;
- #endif
- return UA_STATUSCODE_GOOD;
- }
- static void
- deleteMembers_backend_memory(UA_HistoryDataBackend *backend)
- {
- if (backend == NULL || backend->context == NULL)
- return;
- UA_MemoryStoreContext_deleteMembers((UA_MemoryStoreContext*)backend->context);
- }
- UA_HistoryDataBackend
- UA_HistoryDataBackend_Memory(size_t initialNodeIdStoreSize, size_t initialDataStoreSize) {
- if (initialNodeIdStoreSize == 0)
- initialNodeIdStoreSize = 1;
- if (initialDataStoreSize == 0)
- initialDataStoreSize = 1;
- UA_HistoryDataBackend result;
- memset(&result, 0, sizeof(UA_HistoryDataBackend));
- UA_MemoryStoreContext *ctx = (UA_MemoryStoreContext *)UA_calloc(1, sizeof(UA_MemoryStoreContext));
- if (!ctx)
- return result;
- ctx->dataStore = (UA_NodeIdStoreContextItem_backend_memory*)UA_calloc(initialNodeIdStoreSize, sizeof(UA_NodeIdStoreContextItem_backend_memory));
- ctx->initialStoreSize = initialDataStoreSize;
- ctx->storeSize = initialNodeIdStoreSize;
- ctx->storeEnd = 0;
- result.serverSetHistoryData = &serverSetHistoryData_backend_memory;
- result.resultSize = &resultSize_backend_memory;
- result.getEnd = &getEnd_backend_memory;
- result.lastIndex = &lastIndex_backend_memory;
- result.firstIndex = &firstIndex_backend_memory;
- result.getDateTimeMatch = &getDateTimeMatch_backend_memory;
- result.copyDataValues = ©DataValues_backend_memory;
- result.getDataValue = &getDataValue_backend_memory;
- result.boundSupported = &boundSupported_backend_memory;
- result.timestampsToReturnSupported = ×tampsToReturnSupported_backend_memory;
- result.insertDataValue = &insertDataValue_backend_memory;
- result.updateDataValue = &updateDataValue_backend_memory;
- result.replaceDataValue = &replaceDataValue_backend_memory;
- result.removeDataValue = &removeDataValue_backend_memory;
- result.deleteMembers = &deleteMembers_backend_memory;
- result.getHistoryData = NULL;
- result.context = ctx;
- return result;
- }
- void
- UA_HistoryDataBackend_Memory_deleteMembers(UA_HistoryDataBackend *backend)
- {
- UA_MemoryStoreContext *ctx = (UA_MemoryStoreContext*)backend->context;
- UA_MemoryStoreContext_delete(ctx);
- memset(backend, 0, sizeof(UA_HistoryDataBackend));
- }
|