ua_history_data_backend_memory.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  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 <open62541/plugin/historydata/history_data_backend_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. if (item->storeEnd == 0)
  235. return 0;
  236. return item->storeEnd - 1;
  237. }
  238. static size_t
  239. firstIndex_backend_memory(UA_Server *server,
  240. void *context,
  241. const UA_NodeId *sessionId,
  242. void *sessionContext,
  243. const UA_NodeId * nodeId) {
  244. return 0;
  245. }
  246. static UA_Boolean
  247. boundSupported_backend_memory(UA_Server *server,
  248. void *context,
  249. const UA_NodeId *sessionId,
  250. void *sessionContext,
  251. const UA_NodeId * nodeId) {
  252. return true;
  253. }
  254. static UA_Boolean
  255. timestampsToReturnSupported_backend_memory(UA_Server *server,
  256. void *context,
  257. const UA_NodeId *sessionId,
  258. void *sessionContext,
  259. const UA_NodeId *nodeId,
  260. const UA_TimestampsToReturn timestampsToReturn) {
  261. const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);;
  262. if (item->storeEnd == 0) {
  263. return true;
  264. }
  265. if (timestampsToReturn == UA_TIMESTAMPSTORETURN_NEITHER
  266. || timestampsToReturn == UA_TIMESTAMPSTORETURN_INVALID
  267. || (timestampsToReturn == UA_TIMESTAMPSTORETURN_SERVER
  268. && !item->dataStore[0]->value.hasServerTimestamp)
  269. || (timestampsToReturn == UA_TIMESTAMPSTORETURN_SOURCE
  270. && !item->dataStore[0]->value.hasSourceTimestamp)
  271. || (timestampsToReturn == UA_TIMESTAMPSTORETURN_BOTH
  272. && !(item->dataStore[0]->value.hasSourceTimestamp
  273. && item->dataStore[0]->value.hasServerTimestamp))) {
  274. return false;
  275. }
  276. return true;
  277. }
  278. static const UA_DataValue*
  279. getDataValue_backend_memory(UA_Server *server,
  280. void *context,
  281. const UA_NodeId *sessionId,
  282. void *sessionContext,
  283. const UA_NodeId * nodeId, size_t index) {
  284. const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);;
  285. return &item->dataStore[index]->value;
  286. }
  287. static UA_StatusCode
  288. UA_DataValue_backend_copyRange(const UA_DataValue *src, UA_DataValue *dst,
  289. const UA_NumericRange range)
  290. {
  291. memcpy(dst, src, sizeof(UA_DataValue));
  292. if (src->hasValue)
  293. return UA_Variant_copyRange(&src->value, &dst->value, range);
  294. return UA_STATUSCODE_BADDATAUNAVAILABLE;
  295. }
  296. static UA_StatusCode
  297. copyDataValues_backend_memory(UA_Server *server,
  298. void *context,
  299. const UA_NodeId *sessionId,
  300. void *sessionContext,
  301. const UA_NodeId * nodeId,
  302. size_t startIndex,
  303. size_t endIndex,
  304. UA_Boolean reverse,
  305. size_t maxValues,
  306. UA_NumericRange range,
  307. UA_Boolean releaseContinuationPoints,
  308. const UA_ByteString *continuationPoint,
  309. UA_ByteString *outContinuationPoint,
  310. size_t * providedValues,
  311. UA_DataValue * values)
  312. {
  313. size_t skip = 0;
  314. if (continuationPoint->length > 0) {
  315. if (continuationPoint->length == sizeof(size_t)) {
  316. skip = *((size_t*)(continuationPoint->data));
  317. } else {
  318. return UA_STATUSCODE_BADCONTINUATIONPOINTINVALID;
  319. }
  320. }
  321. const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId);;
  322. size_t index = startIndex;
  323. size_t counter = 0;
  324. size_t skipedValues = 0;
  325. if (reverse) {
  326. while (index >= endIndex && index < item->storeEnd && counter < maxValues) {
  327. if (skipedValues++ >= skip) {
  328. if (range.dimensionsSize > 0) {
  329. UA_DataValue_backend_copyRange(&item->dataStore[index]->value, &values[counter], range);
  330. } else {
  331. UA_DataValue_copy(&item->dataStore[index]->value, &values[counter]);
  332. }
  333. ++counter;
  334. }
  335. --index;
  336. }
  337. } else {
  338. while (index <= endIndex && counter < maxValues) {
  339. if (skipedValues++ >= skip) {
  340. if (range.dimensionsSize > 0) {
  341. UA_DataValue_backend_copyRange(&item->dataStore[index]->value, &values[counter], range);
  342. } else {
  343. UA_DataValue_copy(&item->dataStore[index]->value, &values[counter]);
  344. }
  345. ++counter;
  346. }
  347. ++index;
  348. }
  349. }
  350. if (providedValues)
  351. *providedValues = counter;
  352. if ((!reverse && (endIndex-startIndex-skip+1) > counter) || (reverse && (startIndex-endIndex-skip+1) > counter)) {
  353. outContinuationPoint->length = sizeof(size_t);
  354. size_t t = sizeof(size_t);
  355. outContinuationPoint->data = (UA_Byte*)UA_malloc(t);
  356. *((size_t*)(outContinuationPoint->data)) = skip + counter;
  357. }
  358. return UA_STATUSCODE_GOOD;
  359. }
  360. static UA_StatusCode
  361. insertDataValue_backend_memory(UA_Server *server,
  362. void *hdbContext,
  363. const UA_NodeId *sessionId,
  364. void *sessionContext,
  365. const UA_NodeId *nodeId,
  366. const UA_DataValue *value)
  367. {
  368. if (!value->hasSourceTimestamp && !value->hasServerTimestamp)
  369. return UA_STATUSCODE_BADINVALIDTIMESTAMP;
  370. const UA_DateTime timestamp = value->hasSourceTimestamp ? value->sourceTimestamp : value->serverTimestamp;
  371. UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)hdbContext, server, nodeId);
  372. size_t index = getDateTimeMatch_backend_memory(server,
  373. hdbContext,
  374. sessionId,
  375. sessionContext,
  376. nodeId,
  377. timestamp,
  378. MATCH_EQUAL_OR_AFTER);
  379. if (item->storeEnd != index && item->dataStore[index]->timestamp == timestamp)
  380. return UA_STATUSCODE_BADENTRYEXISTS;
  381. if (item->storeEnd >= item->storeSize) {
  382. size_t newStoreSize = item->storeSize == 0 ? INITIAL_MEMORY_STORE_SIZE : item->storeSize * 2;
  383. item->dataStore = (UA_DataValueMemoryStoreItem **)UA_realloc(item->dataStore, (newStoreSize * sizeof(UA_DataValueMemoryStoreItem*)));
  384. if (!item->dataStore) {
  385. item->storeSize = 0;
  386. return UA_STATUSCODE_BADOUTOFMEMORY;
  387. }
  388. item->storeSize = newStoreSize;
  389. }
  390. UA_DataValueMemoryStoreItem *newItem = (UA_DataValueMemoryStoreItem *)UA_calloc(1, sizeof(UA_DataValueMemoryStoreItem));
  391. newItem->timestamp = timestamp;
  392. UA_DataValue_copy(value, &newItem->value);
  393. if (item->storeEnd > 0 && index < item->storeEnd) {
  394. memmove(&item->dataStore[index+1], &item->dataStore[index], sizeof(UA_DataValueMemoryStoreItem*) * (item->storeEnd - index));
  395. }
  396. item->dataStore[index] = newItem;
  397. ++item->storeEnd;
  398. return UA_STATUSCODE_GOOD;
  399. }
  400. static UA_StatusCode
  401. replaceDataValue_backend_memory(UA_Server *server,
  402. void *hdbContext,
  403. const UA_NodeId *sessionId,
  404. void *sessionContext,
  405. const UA_NodeId *nodeId,
  406. const UA_DataValue *value)
  407. {
  408. if (!value->hasSourceTimestamp && !value->hasServerTimestamp)
  409. return UA_STATUSCODE_BADINVALIDTIMESTAMP;
  410. const UA_DateTime timestamp = value->hasSourceTimestamp ? value->sourceTimestamp : value->serverTimestamp;
  411. UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)hdbContext, server, nodeId);
  412. size_t index = getDateTimeMatch_backend_memory(server,
  413. hdbContext,
  414. sessionId,
  415. sessionContext,
  416. nodeId,
  417. timestamp,
  418. MATCH_EQUAL);
  419. if (index == item->storeEnd)
  420. return UA_STATUSCODE_BADNOENTRYEXISTS;
  421. UA_DataValue_deleteMembers(&item->dataStore[index]->value);
  422. UA_DataValue_copy(value, &item->dataStore[index]->value);
  423. return UA_STATUSCODE_GOOD;
  424. }
  425. static UA_StatusCode
  426. updateDataValue_backend_memory(UA_Server *server,
  427. void *hdbContext,
  428. const UA_NodeId *sessionId,
  429. void *sessionContext,
  430. const UA_NodeId *nodeId,
  431. const UA_DataValue *value)
  432. {
  433. // we first try to replace, because it is cheap
  434. UA_StatusCode ret = replaceDataValue_backend_memory(server,
  435. hdbContext,
  436. sessionId,
  437. sessionContext,
  438. nodeId,
  439. value);
  440. if (ret == UA_STATUSCODE_GOOD)
  441. return UA_STATUSCODE_GOODENTRYREPLACED;
  442. ret = insertDataValue_backend_memory(server,
  443. hdbContext,
  444. sessionId,
  445. sessionContext,
  446. nodeId,
  447. value);
  448. if (ret == UA_STATUSCODE_GOOD)
  449. return UA_STATUSCODE_GOODENTRYINSERTED;
  450. return ret;
  451. }
  452. static UA_StatusCode
  453. removeDataValue_backend_memory(UA_Server *server,
  454. void *hdbContext,
  455. const UA_NodeId *sessionId,
  456. void *sessionContext,
  457. const UA_NodeId *nodeId,
  458. UA_DateTime startTimestamp,
  459. UA_DateTime endTimestamp)
  460. {
  461. UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)hdbContext, server, nodeId);
  462. size_t storeEnd = item->storeEnd;
  463. // The first index which will be deleted
  464. size_t index1;
  465. // the first index which is not deleted
  466. size_t index2;
  467. if (startTimestamp > endTimestamp) {
  468. return UA_STATUSCODE_BADTIMESTAMPNOTSUPPORTED;
  469. }
  470. if (startTimestamp == endTimestamp) {
  471. index1 = getDateTimeMatch_backend_memory(server,
  472. hdbContext,
  473. sessionId,
  474. sessionContext,
  475. nodeId,
  476. startTimestamp,
  477. MATCH_EQUAL);
  478. if (index1 == storeEnd)
  479. return UA_STATUSCODE_BADNODATA;
  480. index2 = index1 + 1;
  481. } else {
  482. index1 = getDateTimeMatch_backend_memory(server,
  483. hdbContext,
  484. sessionId,
  485. sessionContext,
  486. nodeId,
  487. startTimestamp,
  488. MATCH_EQUAL_OR_AFTER);
  489. index2 = getDateTimeMatch_backend_memory(server,
  490. hdbContext,
  491. sessionId,
  492. sessionContext,
  493. nodeId,
  494. endTimestamp,
  495. MATCH_BEFORE);
  496. if (index2 == storeEnd || index1 == storeEnd || index1 > index2 )
  497. return UA_STATUSCODE_BADNODATA;
  498. ++index2;
  499. }
  500. #ifndef __clang_analyzer__
  501. for (size_t i = index1; i < index2; ++i) {
  502. UA_DataValueMemoryStoreItem_deleteMembers(item->dataStore[i]);
  503. UA_free(item->dataStore[i]);
  504. }
  505. memmove(&item->dataStore[index1], &item->dataStore[index2], sizeof(UA_DataValueMemoryStoreItem*) * (item->storeEnd - index2));
  506. item->storeEnd -= index2 - index1;
  507. #else
  508. (void)index1;
  509. (void)index2;
  510. #endif
  511. return UA_STATUSCODE_GOOD;
  512. }
  513. static void
  514. deleteMembers_backend_memory(UA_HistoryDataBackend *backend)
  515. {
  516. if (backend == NULL || backend->context == NULL)
  517. return;
  518. UA_MemoryStoreContext_deleteMembers((UA_MemoryStoreContext*)backend->context);
  519. }
  520. UA_HistoryDataBackend
  521. UA_HistoryDataBackend_Memory(size_t initialNodeIdStoreSize, size_t initialDataStoreSize) {
  522. if (initialNodeIdStoreSize == 0)
  523. initialNodeIdStoreSize = 1;
  524. if (initialDataStoreSize == 0)
  525. initialDataStoreSize = 1;
  526. UA_HistoryDataBackend result;
  527. memset(&result, 0, sizeof(UA_HistoryDataBackend));
  528. UA_MemoryStoreContext *ctx = (UA_MemoryStoreContext *)UA_calloc(1, sizeof(UA_MemoryStoreContext));
  529. if (!ctx)
  530. return result;
  531. ctx->dataStore = (UA_NodeIdStoreContextItem_backend_memory*)UA_calloc(initialNodeIdStoreSize, sizeof(UA_NodeIdStoreContextItem_backend_memory));
  532. ctx->initialStoreSize = initialDataStoreSize;
  533. ctx->storeSize = initialNodeIdStoreSize;
  534. ctx->storeEnd = 0;
  535. result.serverSetHistoryData = &serverSetHistoryData_backend_memory;
  536. result.resultSize = &resultSize_backend_memory;
  537. result.getEnd = &getEnd_backend_memory;
  538. result.lastIndex = &lastIndex_backend_memory;
  539. result.firstIndex = &firstIndex_backend_memory;
  540. result.getDateTimeMatch = &getDateTimeMatch_backend_memory;
  541. result.copyDataValues = &copyDataValues_backend_memory;
  542. result.getDataValue = &getDataValue_backend_memory;
  543. result.boundSupported = &boundSupported_backend_memory;
  544. result.timestampsToReturnSupported = &timestampsToReturnSupported_backend_memory;
  545. result.insertDataValue = &insertDataValue_backend_memory;
  546. result.updateDataValue = &updateDataValue_backend_memory;
  547. result.replaceDataValue = &replaceDataValue_backend_memory;
  548. result.removeDataValue = &removeDataValue_backend_memory;
  549. result.deleteMembers = &deleteMembers_backend_memory;
  550. result.getHistoryData = NULL;
  551. result.context = ctx;
  552. return result;
  553. }
  554. void
  555. UA_HistoryDataBackend_Memory_deleteMembers(UA_HistoryDataBackend *backend)
  556. {
  557. UA_MemoryStoreContext *ctx = (UA_MemoryStoreContext*)backend->context;
  558. UA_MemoryStoreContext_delete(ctx);
  559. memset(backend, 0, sizeof(UA_HistoryDataBackend));
  560. }