ua_historydatabackend_memory.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. *
  5. * Copyright 2018 (c) basysKom GmbH <opensource@basyskom.com> (Author: Peter Rustler)
  6. */
  7. #include "ua_historydatabackend_memory.h"
  8. #include <limits.h>
  9. #include <string.h>
  10. typedef struct {
  11. UA_DateTime timestamp;
  12. UA_DataValue value;
  13. } UA_DataValueMemoryStoreItem;
  14. static void
  15. UA_DataValueMemoryStoreItem_deleteMembers(UA_DataValueMemoryStoreItem* item) {
  16. UA_DateTime_deleteMembers(&item->timestamp);
  17. UA_DataValue_deleteMembers(&item->value);
  18. }
  19. typedef struct {
  20. UA_NodeId nodeId;
  21. UA_DataValueMemoryStoreItem **dataStore;
  22. size_t storeEnd;
  23. size_t storeSize;
  24. } UA_NodeIdStoreContextItem_backend_memory;
  25. static void
  26. UA_NodeIdStoreContextItem_deleteMembers(UA_NodeIdStoreContextItem_backend_memory* item) {
  27. UA_NodeId_deleteMembers(&item->nodeId);
  28. for (size_t i = 0; i < item->storeEnd; ++i) {
  29. UA_DataValueMemoryStoreItem_deleteMembers(item->dataStore[i]);
  30. UA_free(item->dataStore[i]);
  31. }
  32. UA_free(item->dataStore);
  33. }
  34. typedef struct {
  35. UA_NodeIdStoreContextItem_backend_memory *dataStore;
  36. size_t storeEnd;
  37. size_t storeSize;
  38. size_t initialStoreSize;
  39. } UA_MemoryStoreContext;
  40. static void
  41. UA_MemoryStoreContext_deleteMembers(UA_MemoryStoreContext* ctx) {
  42. for (size_t i = 0; i < ctx->storeEnd; ++i) {
  43. UA_NodeIdStoreContextItem_deleteMembers(&ctx->dataStore[i]);
  44. }
  45. UA_free(ctx->dataStore);
  46. memset(ctx, 0, sizeof(UA_MemoryStoreContext));
  47. }
  48. static UA_NodeIdStoreContextItem_backend_memory *
  49. getNewNodeIdContext_backend_memory(UA_MemoryStoreContext* context,
  50. UA_Server *server,
  51. const UA_NodeId *nodeId) {
  52. UA_MemoryStoreContext *ctx = (UA_MemoryStoreContext*)context;
  53. if (ctx->storeEnd >= ctx->storeSize) {
  54. size_t newStoreSize = ctx->storeSize * 2;
  55. if (newStoreSize == 0)
  56. return NULL;
  57. ctx->dataStore = (UA_NodeIdStoreContextItem_backend_memory*)UA_realloc(ctx->dataStore, (newStoreSize * sizeof(UA_NodeIdStoreContextItem_backend_memory)));
  58. if (!ctx->dataStore) {
  59. ctx->storeSize = 0;
  60. return NULL;
  61. }
  62. ctx->storeSize = newStoreSize;
  63. }
  64. UA_NodeIdStoreContextItem_backend_memory *item = &ctx->dataStore[ctx->storeEnd];
  65. UA_NodeId_copy(nodeId, &item->nodeId);
  66. UA_DataValueMemoryStoreItem ** store = (UA_DataValueMemoryStoreItem **)UA_calloc(ctx->initialStoreSize, sizeof(UA_DataValueMemoryStoreItem*));
  67. if (!store) {
  68. UA_NodeIdStoreContextItem_deleteMembers(item);
  69. return NULL;
  70. }
  71. item->dataStore = store;
  72. item->storeSize = ctx->initialStoreSize;
  73. item->storeEnd = 0;
  74. ++ctx->storeEnd;
  75. return item;
  76. }
  77. static UA_NodeIdStoreContextItem_backend_memory *
  78. getNodeIdStoreContextItem_backend_memory(UA_MemoryStoreContext* context,
  79. UA_Server *server,
  80. const UA_NodeId *nodeId)
  81. {
  82. for (size_t i = 0; i < context->storeEnd; ++i) {
  83. if (UA_NodeId_equal(nodeId, &context->dataStore[i].nodeId)) {
  84. return &context->dataStore[i];
  85. }
  86. }
  87. return getNewNodeIdContext_backend_memory(context, server, nodeId);
  88. }
  89. static UA_Boolean
  90. binarySearch_backend_memory(const UA_NodeIdStoreContextItem_backend_memory* item,
  91. const UA_DateTime timestamp,
  92. size_t *index) {
  93. if (item->storeEnd == 0) {
  94. *index = item->storeEnd;
  95. return false;
  96. }
  97. size_t min = 0;
  98. size_t max = item->storeEnd - 1;
  99. while (min <= max) {
  100. *index = (min + max) / 2;
  101. if (item->dataStore[*index]->timestamp == timestamp) {
  102. return true;
  103. } else if (item->dataStore[*index]->timestamp < timestamp) {
  104. if (*index == item->storeEnd - 1) {
  105. *index = item->storeEnd;
  106. return false;
  107. }
  108. min = *index + 1;
  109. } else {
  110. if (*index == 0)
  111. return false;
  112. max = *index - 1;
  113. }
  114. }
  115. *index = min;
  116. return false;
  117. }
  118. static size_t
  119. resultSize_backend_memory(UA_Server *server,
  120. void *context,
  121. const UA_NodeId *sessionId,
  122. void *sessionContext,
  123. const UA_NodeId * nodeId,
  124. size_t startIndex,
  125. size_t endIndex) {
  126. const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);
  127. if (item->storeEnd == 0
  128. || startIndex == item->storeEnd
  129. || endIndex == item->storeEnd)
  130. return 0;
  131. return endIndex - startIndex + 1;
  132. }
  133. static size_t
  134. getDateTimeMatch_backend_memory(UA_Server *server,
  135. void *context,
  136. const UA_NodeId *sessionId,
  137. void *sessionContext,
  138. const UA_NodeId * nodeId,
  139. const UA_DateTime timestamp,
  140. const MatchStrategy strategy) {
  141. const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);
  142. size_t current;
  143. UA_Boolean retval = binarySearch_backend_memory(item, timestamp, &current);
  144. if ((strategy == MATCH_EQUAL
  145. || strategy == MATCH_EQUAL_OR_AFTER
  146. || strategy == MATCH_EQUAL_OR_BEFORE)
  147. && retval)
  148. return current;
  149. switch (strategy) {
  150. case MATCH_AFTER:
  151. if (retval)
  152. return current+1;
  153. return current;
  154. case MATCH_EQUAL_OR_AFTER:
  155. return current;
  156. case MATCH_EQUAL_OR_BEFORE:
  157. // retval == true aka "equal" is handled before
  158. // Fall through if !retval
  159. case MATCH_BEFORE:
  160. if (current > 0)
  161. return current-1;
  162. else
  163. return item->storeEnd;
  164. default:
  165. break;
  166. }
  167. return item->storeEnd;
  168. }
  169. static UA_StatusCode
  170. serverSetHistoryData_backend_memory(UA_Server *server,
  171. void *context,
  172. const UA_NodeId *sessionId,
  173. void *sessionContext,
  174. const UA_NodeId * nodeId,
  175. UA_Boolean historizing,
  176. const UA_DataValue *value)
  177. {
  178. UA_NodeIdStoreContextItem_backend_memory *item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);
  179. if (item->storeEnd >= item->storeSize) {
  180. size_t newStoreSize = item->storeSize == 0 ? INITIAL_MEMORY_STORE_SIZE : item->storeSize * 2;
  181. item->dataStore = (UA_DataValueMemoryStoreItem **)UA_realloc(item->dataStore, (newStoreSize * sizeof(UA_DataValueMemoryStoreItem*)));
  182. if (!item->dataStore) {
  183. item->storeSize = 0;
  184. return UA_STATUSCODE_BADOUTOFMEMORY;
  185. }
  186. item->storeSize = newStoreSize;
  187. }
  188. UA_DateTime timestamp = 0;
  189. if (value->hasSourceTimestamp) {
  190. timestamp = value->sourceTimestamp;
  191. } else if (value->hasServerTimestamp) {
  192. timestamp = value->serverTimestamp;
  193. } else {
  194. timestamp = UA_DateTime_now();
  195. }
  196. UA_DataValueMemoryStoreItem *newItem = (UA_DataValueMemoryStoreItem *)UA_calloc(1, sizeof(UA_DataValueMemoryStoreItem));
  197. newItem->timestamp = timestamp;
  198. UA_DataValue_copy(value, &newItem->value);
  199. size_t index = getDateTimeMatch_backend_memory(server,
  200. context,
  201. NULL,
  202. NULL,
  203. nodeId,
  204. timestamp,
  205. MATCH_EQUAL_OR_AFTER);
  206. if (item->storeEnd > 0 && index < item->storeEnd) {
  207. memmove(&item->dataStore[index+1], &item->dataStore[index], sizeof(UA_DataValueMemoryStoreItem*) * (item->storeEnd - index));
  208. }
  209. item->dataStore[index] = newItem;
  210. ++item->storeEnd;
  211. return UA_STATUSCODE_GOOD;
  212. }
  213. static void
  214. UA_MemoryStoreContext_delete(UA_MemoryStoreContext* ctx) {
  215. UA_MemoryStoreContext_deleteMembers(ctx);
  216. UA_free(ctx);
  217. }
  218. static size_t
  219. getEnd_backend_memory(UA_Server *server,
  220. void *context,
  221. const UA_NodeId *sessionId,
  222. void *sessionContext,
  223. const UA_NodeId * nodeId) {
  224. const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);;
  225. return item->storeEnd;
  226. }
  227. static size_t
  228. lastIndex_backend_memory(UA_Server *server,
  229. void *context,
  230. const UA_NodeId *sessionId,
  231. void *sessionContext,
  232. const UA_NodeId * nodeId) {
  233. const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);;
  234. return item->storeEnd - 1;
  235. }
  236. static size_t
  237. firstIndex_backend_memory(UA_Server *server,
  238. void *context,
  239. const UA_NodeId *sessionId,
  240. void *sessionContext,
  241. const UA_NodeId * nodeId) {
  242. return 0;
  243. }
  244. static UA_Boolean
  245. boundSupported_backend_memory(UA_Server *server,
  246. void *context,
  247. const UA_NodeId *sessionId,
  248. void *sessionContext,
  249. const UA_NodeId * nodeId) {
  250. return true;
  251. }
  252. static UA_Boolean
  253. timestampsToReturnSupported_backend_memory(UA_Server *server,
  254. void *context,
  255. const UA_NodeId *sessionId,
  256. void *sessionContext,
  257. const UA_NodeId *nodeId,
  258. const UA_TimestampsToReturn timestampsToReturn) {
  259. const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);;
  260. if (item->storeEnd == 0) {
  261. return true;
  262. }
  263. if (timestampsToReturn == UA_TIMESTAMPSTORETURN_NEITHER
  264. || timestampsToReturn == UA_TIMESTAMPSTORETURN_INVALID
  265. || (timestampsToReturn == UA_TIMESTAMPSTORETURN_SERVER
  266. && !item->dataStore[0]->value.hasServerTimestamp)
  267. || (timestampsToReturn == UA_TIMESTAMPSTORETURN_SOURCE
  268. && !item->dataStore[0]->value.hasSourceTimestamp)
  269. || (timestampsToReturn == UA_TIMESTAMPSTORETURN_BOTH
  270. && !(item->dataStore[0]->value.hasSourceTimestamp
  271. && item->dataStore[0]->value.hasServerTimestamp))) {
  272. return false;
  273. }
  274. return true;
  275. }
  276. static const UA_DataValue*
  277. getDataValue_backend_memory(UA_Server *server,
  278. void *context,
  279. const UA_NodeId *sessionId,
  280. void *sessionContext,
  281. const UA_NodeId * nodeId, size_t index) {
  282. const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);;
  283. return &item->dataStore[index]->value;
  284. }
  285. static UA_StatusCode
  286. UA_DataValue_backend_copyRange(const UA_DataValue *src, UA_DataValue *dst,
  287. const UA_NumericRange range)
  288. {
  289. memcpy(dst, src, sizeof(UA_DataValue));
  290. if (src->hasValue)
  291. return UA_Variant_copyRange(&src->value, &dst->value, range);
  292. return UA_STATUSCODE_BADDATAUNAVAILABLE;
  293. }
  294. static UA_StatusCode
  295. copyDataValues_backend_memory(UA_Server *server,
  296. void *context,
  297. const UA_NodeId *sessionId,
  298. void *sessionContext,
  299. const UA_NodeId * nodeId,
  300. size_t startIndex,
  301. size_t endIndex,
  302. UA_Boolean reverse,
  303. size_t maxValues,
  304. UA_NumericRange range,
  305. UA_Boolean releaseContinuationPoints,
  306. const UA_ByteString *continuationPoint,
  307. UA_ByteString *outContinuationPoint,
  308. size_t * providedValues,
  309. UA_DataValue * values)
  310. {
  311. size_t skip = 0;
  312. if (continuationPoint->length > 0) {
  313. if (continuationPoint->length == sizeof(size_t)) {
  314. skip = *((size_t*)(continuationPoint->data));
  315. } else {
  316. return UA_STATUSCODE_BADCONTINUATIONPOINTINVALID;
  317. }
  318. }
  319. const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);;
  320. size_t index = startIndex;
  321. size_t counter = 0;
  322. size_t skipedValues = 0;
  323. if (reverse) {
  324. while (index >= endIndex && index < item->storeEnd && counter < maxValues) {
  325. if (skipedValues++ >= skip) {
  326. if (range.dimensionsSize > 0) {
  327. UA_DataValue_backend_copyRange(&item->dataStore[index]->value, &values[counter], range);
  328. } else {
  329. UA_DataValue_copy(&item->dataStore[index]->value, &values[counter]);
  330. }
  331. ++counter;
  332. }
  333. --index;
  334. }
  335. } else {
  336. while (index <= endIndex && counter < maxValues) {
  337. if (skipedValues++ >= skip) {
  338. if (range.dimensionsSize > 0) {
  339. UA_DataValue_backend_copyRange(&item->dataStore[index]->value, &values[counter], range);
  340. } else {
  341. UA_DataValue_copy(&item->dataStore[index]->value, &values[counter]);
  342. }
  343. ++counter;
  344. }
  345. ++index;
  346. }
  347. }
  348. if (providedValues)
  349. *providedValues = counter;
  350. if ((!reverse && (endIndex-startIndex-skip+1) > counter) || (reverse && (startIndex-endIndex-skip+1) > counter)) {
  351. outContinuationPoint->length = sizeof(size_t);
  352. size_t t = sizeof(size_t);
  353. outContinuationPoint->data = (UA_Byte*)UA_malloc(t);
  354. *((size_t*)(outContinuationPoint->data)) = skip + counter;
  355. }
  356. return UA_STATUSCODE_GOOD;
  357. }
  358. static UA_StatusCode
  359. insertDataValue_backend_memory(UA_Server *server,
  360. void *hdbContext,
  361. const UA_NodeId *sessionId,
  362. void *sessionContext,
  363. const UA_NodeId *nodeId,
  364. const UA_DataValue *value)
  365. {
  366. if (!value->hasSourceTimestamp && !value->hasServerTimestamp)
  367. return UA_STATUSCODE_BADINVALIDTIMESTAMP;
  368. const UA_DateTime timestamp = value->hasSourceTimestamp ? value->sourceTimestamp : value->serverTimestamp;
  369. UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)hdbContext, server, nodeId);
  370. size_t index = getDateTimeMatch_backend_memory(server,
  371. hdbContext,
  372. sessionId,
  373. sessionContext,
  374. nodeId,
  375. timestamp,
  376. MATCH_EQUAL_OR_AFTER);
  377. if (item->storeEnd != index && item->dataStore[index]->timestamp == timestamp)
  378. return UA_STATUSCODE_BADENTRYEXISTS;
  379. if (item->storeEnd >= item->storeSize) {
  380. size_t newStoreSize = item->storeSize == 0 ? INITIAL_MEMORY_STORE_SIZE : item->storeSize * 2;
  381. item->dataStore = (UA_DataValueMemoryStoreItem **)UA_realloc(item->dataStore, (newStoreSize * sizeof(UA_DataValueMemoryStoreItem*)));
  382. if (!item->dataStore) {
  383. item->storeSize = 0;
  384. return UA_STATUSCODE_BADOUTOFMEMORY;
  385. }
  386. item->storeSize = newStoreSize;
  387. }
  388. UA_DataValueMemoryStoreItem *newItem = (UA_DataValueMemoryStoreItem *)UA_calloc(1, sizeof(UA_DataValueMemoryStoreItem));
  389. newItem->timestamp = timestamp;
  390. UA_DataValue_copy(value, &newItem->value);
  391. if (item->storeEnd > 0 && index < item->storeEnd) {
  392. memmove(&item->dataStore[index+1], &item->dataStore[index], sizeof(UA_DataValueMemoryStoreItem*) * (item->storeEnd - index));
  393. }
  394. item->dataStore[index] = newItem;
  395. ++item->storeEnd;
  396. return UA_STATUSCODE_GOOD;
  397. }
  398. static UA_StatusCode
  399. replaceDataValue_backend_memory(UA_Server *server,
  400. void *hdbContext,
  401. const UA_NodeId *sessionId,
  402. void *sessionContext,
  403. const UA_NodeId *nodeId,
  404. const UA_DataValue *value)
  405. {
  406. if (!value->hasSourceTimestamp && !value->hasServerTimestamp)
  407. return UA_STATUSCODE_BADINVALIDTIMESTAMP;
  408. const UA_DateTime timestamp = value->hasSourceTimestamp ? value->sourceTimestamp : value->serverTimestamp;
  409. UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)hdbContext, server, nodeId);
  410. size_t index = getDateTimeMatch_backend_memory(server,
  411. hdbContext,
  412. sessionId,
  413. sessionContext,
  414. nodeId,
  415. timestamp,
  416. MATCH_EQUAL);
  417. if (index == item->storeEnd)
  418. return UA_STATUSCODE_BADNOENTRYEXISTS;
  419. UA_DataValue_deleteMembers(&item->dataStore[index]->value);
  420. UA_DataValue_copy(value, &item->dataStore[index]->value);
  421. return UA_STATUSCODE_GOOD;
  422. }
  423. static UA_StatusCode
  424. updateDataValue_backend_memory(UA_Server *server,
  425. void *hdbContext,
  426. const UA_NodeId *sessionId,
  427. void *sessionContext,
  428. const UA_NodeId *nodeId,
  429. const UA_DataValue *value)
  430. {
  431. // we first try to replace, because it is cheap
  432. UA_StatusCode ret = replaceDataValue_backend_memory(server,
  433. hdbContext,
  434. sessionId,
  435. sessionContext,
  436. nodeId,
  437. value);
  438. if (ret == UA_STATUSCODE_GOOD)
  439. return UA_STATUSCODE_GOODENTRYREPLACED;
  440. ret = insertDataValue_backend_memory(server,
  441. hdbContext,
  442. sessionId,
  443. sessionContext,
  444. nodeId,
  445. value);
  446. if (ret == UA_STATUSCODE_GOOD)
  447. return UA_STATUSCODE_GOODENTRYINSERTED;
  448. return ret;
  449. }
  450. static UA_StatusCode
  451. removeDataValue_backend_memory(UA_Server *server,
  452. void *hdbContext,
  453. const UA_NodeId *sessionId,
  454. void *sessionContext,
  455. const UA_NodeId *nodeId,
  456. UA_DateTime startTimestamp,
  457. UA_DateTime endTimestamp)
  458. {
  459. UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)hdbContext, server, nodeId);
  460. size_t storeEnd = item->storeEnd;
  461. // The first index which will be deleted
  462. size_t index1;
  463. // the first index which is not deleted
  464. size_t index2;
  465. if (startTimestamp > endTimestamp) {
  466. return UA_STATUSCODE_BADTIMESTAMPNOTSUPPORTED;
  467. }
  468. if (startTimestamp == endTimestamp) {
  469. index1 = getDateTimeMatch_backend_memory(server,
  470. hdbContext,
  471. sessionId,
  472. sessionContext,
  473. nodeId,
  474. startTimestamp,
  475. MATCH_EQUAL);
  476. if (index1 == storeEnd)
  477. return UA_STATUSCODE_BADNODATA;
  478. index2 = index1 + 1;
  479. } else {
  480. index1 = getDateTimeMatch_backend_memory(server,
  481. hdbContext,
  482. sessionId,
  483. sessionContext,
  484. nodeId,
  485. startTimestamp,
  486. MATCH_EQUAL_OR_AFTER);
  487. index2 = getDateTimeMatch_backend_memory(server,
  488. hdbContext,
  489. sessionId,
  490. sessionContext,
  491. nodeId,
  492. endTimestamp,
  493. MATCH_BEFORE);
  494. if (index2 == storeEnd || index1 == storeEnd || index1 > index2 )
  495. return UA_STATUSCODE_BADNODATA;
  496. ++index2;
  497. }
  498. #ifndef __clang_analyzer__
  499. for (size_t i = index1; i < index2; ++i) {
  500. UA_DataValueMemoryStoreItem_deleteMembers(item->dataStore[i]);
  501. UA_free(item->dataStore[i]);
  502. }
  503. memmove(&item->dataStore[index1], &item->dataStore[index2], sizeof(UA_DataValueMemoryStoreItem*) * (item->storeEnd - index2));
  504. item->storeEnd -= index2 - index1;
  505. #else
  506. (void)index1;
  507. (void)index2;
  508. #endif
  509. return UA_STATUSCODE_GOOD;
  510. }
  511. static void
  512. deleteMembers_backend_memory(UA_HistoryDataBackend *backend)
  513. {
  514. if (backend == NULL || backend->context == NULL)
  515. return;
  516. UA_MemoryStoreContext_deleteMembers((UA_MemoryStoreContext*)backend->context);
  517. }
  518. UA_HistoryDataBackend
  519. UA_HistoryDataBackend_Memory(size_t initialNodeIdStoreSize, size_t initialDataStoreSize) {
  520. if (initialNodeIdStoreSize == 0)
  521. initialNodeIdStoreSize = 1;
  522. if (initialDataStoreSize == 0)
  523. initialDataStoreSize = 1;
  524. UA_HistoryDataBackend result;
  525. memset(&result, 0, sizeof(UA_HistoryDataBackend));
  526. UA_MemoryStoreContext *ctx = (UA_MemoryStoreContext *)UA_calloc(1, sizeof(UA_MemoryStoreContext));
  527. if (!ctx)
  528. return result;
  529. ctx->dataStore = (UA_NodeIdStoreContextItem_backend_memory*)UA_calloc(initialNodeIdStoreSize, sizeof(UA_NodeIdStoreContextItem_backend_memory));
  530. ctx->initialStoreSize = initialDataStoreSize;
  531. ctx->storeSize = initialNodeIdStoreSize;
  532. ctx->storeEnd = 0;
  533. result.serverSetHistoryData = &serverSetHistoryData_backend_memory;
  534. result.resultSize = &resultSize_backend_memory;
  535. result.getEnd = &getEnd_backend_memory;
  536. result.lastIndex = &lastIndex_backend_memory;
  537. result.firstIndex = &firstIndex_backend_memory;
  538. result.getDateTimeMatch = &getDateTimeMatch_backend_memory;
  539. result.copyDataValues = &copyDataValues_backend_memory;
  540. result.getDataValue = &getDataValue_backend_memory;
  541. result.boundSupported = &boundSupported_backend_memory;
  542. result.timestampsToReturnSupported = &timestampsToReturnSupported_backend_memory;
  543. result.insertDataValue = &insertDataValue_backend_memory;
  544. result.updateDataValue = &updateDataValue_backend_memory;
  545. result.replaceDataValue = &replaceDataValue_backend_memory;
  546. result.removeDataValue = &removeDataValue_backend_memory;
  547. result.deleteMembers = &deleteMembers_backend_memory;
  548. result.getHistoryData = NULL;
  549. result.context = ctx;
  550. return result;
  551. }
  552. void
  553. UA_HistoryDataBackend_Memory_deleteMembers(UA_HistoryDataBackend *backend)
  554. {
  555. UA_MemoryStoreContext *ctx = (UA_MemoryStoreContext*)backend->context;
  556. UA_MemoryStoreContext_delete(ctx);
  557. memset(backend, 0, sizeof(UA_HistoryDataBackend));
  558. }