CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
DataMessage.cpp
Go to the documentation of this file.
1
6#include "DataMessage.h"
7#include "PsyTime.h"
8#include "UnitTestFramework.h"
9
10namespace cmlabs {
11
12/*
13 IMPLEMENTATION NOTES - binary layout
14 ------------------------------------
15 A DataMessage is ONE contiguous heap block (malloc/realloc, never new[])
16 pointed to by 'data':
17
18 [DataMessageHeader][entry][entry][entry]... total bytes = data->size
19
20 The header starts with { uint32 size; uint32 cid; ... }, so the first
21 4 bytes of any serialised message are its total size and the next 4 the
22 class id (DATAMESSAGEID). Because the block is already flat, "serialise"
23 is simply "send data->size bytes starting at data" - there is no separate
24 marshalling step and no per-field byte swapping: all integers are stored
25 in HOST byte order. All supported platforms (x86/x64, ARM/ARM64 in the
26 default configs) are little-endian, so blocks are wire-compatible between
27 nodes; only the transport-level frame length (see NetworkProtocols.cpp)
28 uses network byte order.
29
30 Each user entry is a PAIR of tagged records laid out back to back:
31
32 [DataMessageEntryHeader{size,cid=CONSTCHARID}][key chars + NUL]
33 [DataMessageEntryHeader{size,cid=<value type>}][value bytes]
34
35 where entry.size is the full record size INCLUDING its own header, so a
36 walker advances with 'src += ((DataMessageEntryHeader*)src)->size'. Value
37 cids are INTID (int64), DOUBLEID (float64), TIMEID (uint64 ms),
38 CONSTCHARID (NUL-terminated text), CHARDATAID (raw bytes), DATAMESSAGEID
39 (a nested, fully serialised message) and the *INFOID variants which hold
40 only an int64 byte count as a placeholder for content that was stripped
41 (see the "draft" constructors below).
42
43 Arrays and maps are NOT separate structures: they are encoded purely in
44 the key string. Element i of array "foo" is stored under the mangled key
45 "foo_-|i|-_" and map entry "bar" of map "m" under "m_-[bar]-_". All the
46 array/map getters below therefore do a linear scan matching the key
47 prefix/suffix; there is no index. Lookup of single keys is a linear scan
48 too (findEntry() in the header), assisted by a one-slot find cache that
49 must be invalidated (invalidateFindKeyCache()) whenever the block is
50 modified or reallocated.
51*/
52
53std::string DataMessageHeader::toJSON(std::map<uint16, std::string>* subtypes, std::map<uint16, std::string>* subcontexts, std::map<uint32, std::string>* compNames) {
54 if (cid != DATAMESSAGEID) return 0;
55 if (!subtypes && !subcontexts && !compNames)
56 return utils::StringFormat("{ \"size\": %u, \"memid\": \"%llu\", \"time\": \"%llu\", \"time__desc\": \"%s\", \"sendtime\": \"%llu\", \"sendtime__desc\": \"%s\", \"recvtime\": \"%llu\", \"recvtime__desc\": \"%s\", "
57 "\"modulecputime\": %u, \"modulewalltime\": %u, \"chaincputime\": %u, \"chainwalltime\": %u, \"chaincount\": %u, "
58 "\"origin\": %u, \"destination\": %u, \"ttl\": \"%llu\", \"eol__desc\": \"%s\", \"priority\": %u, \"policy\": %u, \"from\": %u, \"to\": %u, \"type\": \"%s\", \"tag\": \"%u\", \"status\": %u, \"reference\": \"%llu\", \"serial\": \"%llu\", \"contextchange\": \"%s\", \"userdatacount\": %u, \"userdatasize\": %u }",
59 size, memid,
60 time, time ? PrintTimeString(time).c_str() : "",
64 origin, destination, ttl, ttl ? PrintTimeString(time + ttl).c_str() : "",
65 priority, policy, from, to, type.toString().c_str(), tag, status, reference, serial,
66 contextchange.isValid() ? contextchange.toString().c_str() : "",
68
69 std::string typeStr, contextStr, fromStr, toStr;
70 typeStr = type.toString(subtypes);
71 contextStr = contextchange.toString(subcontexts);
72 if (compNames) {
73 fromStr = (*compNames)[from];
74 toStr = (*compNames)[to];
75 }
76 if (!fromStr.length()) {
77 if (from)
78 fromStr = utils::StringFormat("Component %u", from);
79 }
80 if (!toStr.length()) {
81 if (to)
82 toStr = utils::StringFormat("Component %u", to);
83 }
84 return utils::StringFormat("{ \"size\": %u, \"memid\": \"%llu\", \"time\": \"%llu\", \"time__desc\": \"%s\", \"sendtime\": \"%llu\", \"sendtime__desc\": \"%s\", \"recvtime\": \"%llu\", \"recvtime__desc\": \"%s\", "
85 "\"modulecputime\": %u, \"modulewalltime\": %u, \"chaincputime\": %u, \"chainwalltime\": %u, \"chaincount\": %u, "
86 "\"origin\": %u, \"destination\": %u, \"ttl\": \"%llu\", \"eol__desc\": \"%s\", \"priority\": %u, \"policy\": %u, \"from\": \"%s\", \"to\": \"%s\", \"type\": \"%s\", \"tag\": \"%u\", \"status\": %u, \"reference\": \"%llu\", \"serial\": \"%llu\", \"contextchange\": \"%s\", \"userdatacount\": %u, \"userdatasize\": %u }",
87 size, memid,
88 time, time ? PrintTimeString(time).c_str() : "",
92 origin, destination, ttl, ttl ? PrintTimeString(time + ttl).c_str() : "",
94 fromStr.c_str(),
95 toStr.c_str(),
96 typeStr.c_str(), tag, status, reference, serial,
97 contextStr.c_str(), userCount, userSize);
98
99}
100
101std::string DataMessageHeader::toXML(std::map<uint16, std::string>* subtypes, std::map<uint16, std::string>* subcontexts, std::map<uint32, std::string>* compNames) {
102 if (cid != DATAMESSAGEID) return 0;
103
104 if (!subtypes && !subcontexts && !compNames)
105 return utils::StringFormat("<datamessage size=\"%u\" memid=\"%llu\" time=\"%llu\" timetext=\"%s\" sendtime=\"%llu\" sendtimetext=\"%s\" recvtime=\"%llu\" recvtimetext=\"%s\" "
106 "modulecputime=\"%u\" modulewalltime=\"%u\" chaincputime=\"%u\" chainwalltime=\"%u\" chaincount=\"%u\" "
107 "origin=\"%u\" destination=\"%u\" ttl=\"%llu\" eoltext=\"%s\" priority=\"%u\" policy=\"%u\" from=\"%u\" to=\"%u\" type=\"%s\" tag=\"%u\" status=\"%u\" reference=\"%llu\" serial=\"%llu\" contextchange=\"%s\" userdatacount=\"%u\" userdatasize=\"%u\" />\n",
108 size, memid,
109 time, time ? PrintTimeString(time).c_str() : "",
110 sendtime, sendtime ? PrintTimeString(sendtime).c_str() : "",
111 recvtime, recvtime ? PrintTimeString(recvtime).c_str() : "",
113 origin, destination, ttl, ttl ? PrintTimeString(time + ttl).c_str() : "", priority, policy, from, to, type.toString().c_str(), tag, status, reference, serial, contextchange.isValid() ? contextchange.toString().c_str() : "", userCount, userSize);
114
115 std::string typeStr, contextStr, fromStr, toStr;
116 typeStr = type.toString(subtypes);
117 contextStr = contextchange.toString(subcontexts);
118 if (compNames) {
119 fromStr = (*compNames)[from];
120 toStr = (*compNames)[to];
121 }
122 if (!fromStr.length()) {
123 if (from)
124 fromStr = utils::StringFormat("Component %u", from);
125 }
126 if (!toStr.length()) {
127 if (to)
128 toStr = utils::StringFormat("Component %u", to);
129 }
130
131 return utils::StringFormat("<datamessage size=\"%u\" memid=\"%llu\" time=\"%llu\" timetext=\"%s\" sendtime=\"%llu\" sendtimetext=\"%s\" recvtime=\"%llu\" recvtimetext=\"%s\" "
132 "modulecputime=\"%u\" modulewalltime=\"%u\" chaincputime=\"%u\" chainwalltime=\"%u\" chaincount=\"%u\" "
133 "origin=\"%u\" destination=\"%u\" ttl=\"%llu\" eoltext=\"%s\" priority=\"%u\" policy=\"%u\" from=\"%s\" to=\"%s\" type=\"%s\" tag=\"%u\" status=\"%u\" reference=\"%llu\" serial=\"%llu\" contextchange=\"%s\" userdatacount=\"%u\" userdatasize=\"%u\" />\n",
134 size, memid,
135 time, time ? PrintTimeString(time).c_str() : "",
136 sendtime, sendtime ? PrintTimeString(sendtime).c_str() : "",
137 recvtime, recvtime ? PrintTimeString(recvtime).c_str() : "",
140 fromStr.c_str(),
141 toStr.c_str(),
142 typeStr.c_str(), tag, status, reference, serial, contextStr.c_str(), userCount, userSize);
143}
144
145
146// Bulk-import every user entry from another message by walking its flat
147// block directly: each key record (CONSTCHARID) is followed by its value
148// record, which is re-inserted here via setRawData() (deep copy, preserving
149// the original value cid). Returns the number of entries copied.
151 if ((data->cid != DATAMESSAGEID) || !msg || (msg->data->cid != DATAMESSAGEID))
152 return 0;
153
154 if (!msg->data->userCount)
155 return 0;
156
157 uint32 count = 0;
158 const char* key;
159 // Walk [header][entry][entry]... of the source block; srcEnd guards the walk.
160 char* src = (char*)(msg->data) + sizeof(DataMessageHeader);
161 char* srcEnd = (char*)(msg->data) + msg->data->size;
162 while (src < srcEnd) {
163 // A CONSTCHARID record here is a key; the value record follows it directly.
164 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
165 key = src + sizeof(DataMessageEntryHeader);
166 src += sizeof(DataMessageEntryHeader) + strlen(key) + 1;
167 if (setRawData(key, src + sizeof(DataMessageEntryHeader), ((DataMessageEntryHeader*)src)->size - sizeof(DataMessageEntryHeader), ((DataMessageEntryHeader*)src)->cid))
168 count++;
169 }
170 src += ((DataMessageEntryHeader*)src)->size;
171 }
172 return count;
173}
174
175namespace {
176 // Scratch records used while grouping mangled array/map keys during the
177 // text/JSON/XML dumps: they remember where each element's value record
178 // lives inside the flat block so it can be printed under its parent key.
179 struct ArrayElem { int64 index; char* entryPtr; uint32 cid; };
180 struct MapElem { std::string index; char* entryPtr; uint32 cid; };
181}
182
183// Human-readable dump of all user entries. Single pass over the flat block:
184// plain keys are printed immediately; keys carrying the array ("_-|i|-_")
185// or map ("_-[k]-_") mangling are collected into userArrays/userMaps first
186// and printed grouped (indented) after the scan. Nested DATAMESSAGEID
187// entries recurse via a temporary DataMessage wrapper.
189 if (data->cid != DATAMESSAGEID) return 0;
190
191 if (!data->userCount)
192 return "";
193
194 std::map< std::string, std::list<ArrayElem> > userArrays;
195 std::map< std::string, std::list<MapElem> > userMaps;
196
197 DataMessage* msg;
198 const char* key;
199 const char* mapStart, *mapEnd;
200 std::string userData;
201 userData.reserve(data->userSize + data->userCount * 64);
202 char* src = (char*)data + sizeof(DataMessageHeader);
203 char* srcEnd = (char*)data + data->size;
204 while (src < srcEnd) {
205 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
206 key = src + sizeof(DataMessageEntryHeader);
207 src += sizeof(DataMessageEntryHeader) + strlen(key) + 1;
208 if ((mapStart = strstr(key, "_-|")) && (mapEnd = strstr(key, "|-_"))) {
209 ArrayElem e;
210 e.index = utils::Ascii2Int64(std::string(mapStart + 3, mapEnd - mapStart - 3).c_str());
211 e.entryPtr = src;
212 e.cid = ((DataMessageEntryHeader*)src)->cid;
213 userArrays[std::string(key, mapStart - key)].push_back(e);
214 src += ((DataMessageEntryHeader*)src)->size;
215 continue;
216 }
217 else if ((mapStart = strstr(key, "_-[")) && (mapEnd = strstr(key, "]-_"))) {
218 MapElem e;
219 e.index = std::string(mapStart + 3, mapEnd - mapStart - 3);
220 e.entryPtr = src;
221 e.cid = ((DataMessageEntryHeader*)src)->cid;
222 userMaps[std::string(key, mapStart - key)].push_back(e);
223 src += ((DataMessageEntryHeader*)src)->size;
224 continue;
225 }
226 switch (((DataMessageEntryHeader*)src)->cid) {
227 case CONSTCHARID:
228 userData += utils::StringFormat("'%s' : '%s' (string)\n", key, src + sizeof(DataMessageEntryHeader));
229 break;
230 case TIMEID:
231 userData += utils::StringFormat("'%s' : %s (time)\n",
232 key,
233 *(uint64*)(src + sizeof(DataMessageEntryHeader)) ? PrintTimeString(*(uint64*)(src + sizeof(DataMessageEntryHeader))).c_str() : ""
234 );
235 break;
236 case CHARDATAID:
237 userData += utils::StringFormat("'%s' : size %u (binary)\n", key, ((DataMessageEntryHeader*)src)->size - sizeof(DataMessageEntryHeader));
238 break;
239 case CONSTCHARINFOID:
240 userData += utils::StringFormat("'%s' : (string of size %lld)\n", key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
241 break;
242 case CHARDATAINFOID:
243 userData += utils::StringFormat("'%s' : (binary of size %lld)\n", key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
244 break;
246 userData += utils::StringFormat("'%s' : (message of size %lld)\n", key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
247 break;
248 case INTID:
249 userData += utils::StringFormat("'%s' : %lld (integer)\n", key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
250 break;
251 case DOUBLEID:
252 userData += utils::StringFormat("'%s' : %f (float)\n", key, *(float64*)(src + sizeof(DataMessageEntryHeader)));
253 break;
254 case DATAMESSAGEID:
255 msg = new DataMessage(src + sizeof(DataMessageEntryHeader));
256 if (msg) {
257 if (userData.length()) userData += ", ";
258 userData += utils::StringFormat("'%s' : (message)\n%s\n", key, utils::TextIndent(msg->getUserEntriesAsString().c_str(), " -").c_str());
259 }
260 delete(msg);
261 break;
263 // ignore
264 default:
265 break;
266 }
267 }
268 // else cannot generate JSON for this entry
269 src += ((DataMessageEntryHeader*)src)->size;
270 }
271
272 std::string subUserData;
273
274 std::list<ArrayElem>::iterator entryII, entryEE;
275 std::map< std::string, std::list<ArrayElem> >::iterator keyII = userArrays.begin(), keyEE = userArrays.end();
276 while (keyII != keyEE) {
277 subUserData.clear();
278 entryII = keyII->second.begin();
279 entryEE = keyII->second.end();
280 while (entryII != entryEE) {
281 char* ep = entryII->entryPtr;
282 char* val = ep + sizeof(DataMessageEntryHeader);
283 uint32 cid = entryII->cid;
284 switch (cid) {
285 case CONSTCHARID:
286 subUserData += utils::StringFormat("%lld : '%s' (string)\n", entryII->index, val);
287 break;
288 case TIMEID:
289 subUserData += utils::StringFormat("%lld : %s (time)\n",
290 entryII->index,
291 *(uint64*)val ? PrintTimeString(*(uint64*)val).c_str() : ""
292 );
293 break;
294 case CHARDATAID:
295 subUserData += utils::StringFormat("%lld : size %u (binary)\n", entryII->index, ((DataMessageEntryHeader*)ep)->size - sizeof(DataMessageEntryHeader));
296 break;
297 case DATAMESSAGEID:
298 msg = new DataMessage(val);
299 if (msg) {
300 if (subUserData.length()) subUserData += ", ";
301 subUserData += utils::StringFormat("%lld : (message)\n%s\n", entryII->index, utils::TextIndent(msg->getUserEntriesAsString().c_str(), " -").c_str());
302 }
303 delete(msg);
304 break;
305 case INTID:
306 subUserData += utils::StringFormat("%lld : %lld (integer)\n", entryII->index, *(int64*)val);
307 break;
308 case DOUBLEID:
309 subUserData += utils::StringFormat("%lld : %f (float)\n", entryII->index, *(float64*)val);
310 break;
311 case CONSTCHARINFOID:
312 subUserData += utils::StringFormat("%lld : (string of size %lld)\n", entryII->index, *(int64*)val);
313 break;
314 case CHARDATAINFOID:
315 subUserData += utils::StringFormat("%lld : (binary of size %lld)\n", entryII->index, *(int64*)val);
316 break;
318 subUserData += utils::StringFormat("%lld : (message of size %lld)\n", entryII->index, *(int64*)val);
319 break;
320 default:
321 break;
322 }
323 entryII++;
324 }
325 userData += utils::StringFormat("'%s' : (array)\n%s", keyII->first.c_str(), utils::TextIndent(subUserData.c_str(), " -").c_str());
326 keyII++;
327 }
328
329 std::list<MapElem>::iterator entryI, entryE;
330 std::map< std::string, std::list<MapElem> >::iterator keyI = userMaps.begin(), keyE = userMaps.end();
331 while (keyI != keyE) {
332 subUserData.clear();
333 entryI = keyI->second.begin();
334 entryE = keyI->second.end();
335 while (entryI != entryE) {
336 char* ep = entryI->entryPtr;
337 char* val = ep + sizeof(DataMessageEntryHeader);
338 uint32 cid = entryI->cid;
339 switch (cid) {
340 case CONSTCHARID:
341 subUserData += utils::StringFormat("'%s' : '%s' (string)\n", entryI->index.c_str(), val);
342 break;
343 case TIMEID:
344 subUserData += utils::StringFormat("'%s' : %s (time)\n",
345 entryI->index.c_str(),
346 *(uint64*)val ? PrintTimeString(*(uint64*)val).c_str() : ""
347 );
348 break;
349 case CHARDATAID:
350 subUserData += utils::StringFormat("'%s' : size %u (binary)\n", entryI->index.c_str(), ((DataMessageEntryHeader*)ep)->size - sizeof(DataMessageEntryHeader));
351 break;
352 case DATAMESSAGEID:
353 msg = new DataMessage(val);
354 if (msg) {
355 if (subUserData.length()) subUserData += ", ";
356 subUserData += utils::StringFormat("'%s' : (message)\n%s\n", entryI->index.c_str(), utils::TextIndent(msg->getUserEntriesAsString().c_str(), " -").c_str());
357 }
358 delete(msg);
359 break;
360 case INTID:
361 subUserData += utils::StringFormat("'%s' : %lld (integer)\n", entryI->index.c_str(), *(int64*)val);
362 break;
363 case DOUBLEID:
364 subUserData += utils::StringFormat("'%s' : %f (float)\n", entryI->index.c_str(), *(float64*)val);
365 break;
366 case CONSTCHARINFOID:
367 subUserData += utils::StringFormat("'%s' : (string of size %lld)\n", entryI->index.c_str(), *(int64*)val);
368 break;
369 case CHARDATAINFOID:
370 subUserData += utils::StringFormat("'%s' : (binary of size %lld)\n", entryI->index.c_str(), *(int64*)val);
371 break;
373 subUserData += utils::StringFormat("'%s' : (message of size %lld)\n", entryI->index.c_str(), *(int64*)val);
374 break;
375 default:
376 break;
377 }
378 entryI++;
379 }
380 userData += utils::StringFormat("'%s' : (map)\n%s", keyI->first.c_str(), utils::TextIndent(subUserData.c_str(), " -").c_str());
381 keyI++;
382 }
383
384 return userData;
385}
386
387// JSON dump of all user entries; same two-phase walk as
388// getUserEntriesAsString() (plain keys inline, mangled array/map keys
389// grouped afterwards). With asText=true values are emitted as bare JSON
390// scalars; otherwise each value becomes a {"type", "value"} object so the
391// receiver can distinguish text/time/binary/etc.
392std::string DataMessage::getUserEntriesAsJSON(bool asText) {
393 if (data->cid != DATAMESSAGEID) return "{}";
394
395 if (!data->userCount)
396 return "{}";
397
398 std::map< std::string, std::list<ArrayElem> > userArrays;
399 std::map< std::string, std::list<MapElem> > userMaps;
400
401 const char* oldData = NULL;
402 DataMessage* msg = new DataMessage();
403 const char* key;
404 const char* mapStart, *mapEnd;
405 std::string userData;
406 userData.reserve(data->userSize + data->userCount * 64);
407 char* src = (char*)data + sizeof(DataMessageHeader);
408 char* srcEnd = (char*)data + data->size;
409 while (src < srcEnd) {
410 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
411 key = src + sizeof(DataMessageEntryHeader);
412 src += sizeof(DataMessageEntryHeader) + strlen(key) + 1;
413 if ((mapStart = strstr(key, "_-|")) && (mapEnd = strstr(key, "|-_"))) {
414 ArrayElem e;
415 e.index = utils::Ascii2Int64(std::string(mapStart + 3, mapEnd - mapStart - 3).c_str());
416 e.entryPtr = src;
417 e.cid = ((DataMessageEntryHeader*)src)->cid;
418 userArrays[std::string(key, mapStart - key)].push_back(e);
419 src += ((DataMessageEntryHeader*)src)->size;
420 continue;
421 }
422 else if ((mapStart = strstr(key, "_-[")) && (mapEnd = strstr(key, "]-_"))) {
423 MapElem e;
424 e.index = std::string(mapStart + 3, mapEnd - mapStart - 3);
425 e.entryPtr = src;
426 e.cid = ((DataMessageEntryHeader*)src)->cid;
427 userMaps[std::string(key, mapStart - key)].push_back(e);
428 src += ((DataMessageEntryHeader*)src)->size;
429 continue;
430 }
431 switch (((DataMessageEntryHeader*)src)->cid) {
432 case CONSTCHARID:
433 if (userData.length()) userData += ", ";
434 if (!asText)
435 userData += utils::StringFormat("\"%s\": {\"type\": \"text\", \"value\": \"%s\" }\n", key, src + sizeof(DataMessageEntryHeader));
436 else
437 userData += utils::StringFormat("\"%s\": \"%s\"\n", key, src + sizeof(DataMessageEntryHeader));
438 break;
439 case TIMEID:
440 if (userData.length()) userData += ", ";
441 if (!asText)
442 userData += utils::StringFormat("\"%s\": {\"type\": \"time\", \"value\": %llu, \"text\": \"%s\" }\n",
443 key, *(uint64*)(src + sizeof(DataMessageEntryHeader)),
444 *(uint64*)(src + sizeof(DataMessageEntryHeader)) ? PrintTimeString(*(uint64*)(src + sizeof(DataMessageEntryHeader))).c_str() : ""
445 );
446 else
447 userData += utils::StringFormat("\"%s\": \"%s\"\n",
448 key,
449 *(uint64*)(src + sizeof(DataMessageEntryHeader)) ? PrintTimeString(*(uint64*)(src + sizeof(DataMessageEntryHeader))).c_str() : ""
450 );
451 break;
452 case CHARDATAID:
453 if (userData.length()) userData += ", ";
454 if (!asText)
455 userData += utils::StringFormat("\"%s\": {\"type\": \"binary\", \"size\": %u }\n", key, ((DataMessageEntryHeader*)src)->size - sizeof(DataMessageEntryHeader));
456 break;
457 case INTID:
458 if (userData.length()) userData += ", ";
459 if (!asText)
460 userData += utils::StringFormat("\"%s\": {\"type\": \"integer\", \"value\": %lld }\n", key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
461 else
462 userData += utils::StringFormat("\"%s\": \"%lld\"\n", key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
463 break;
464 case DOUBLEID:
465 if (userData.length()) userData += ", ";
466 if (!asText)
467 userData += utils::StringFormat("\"%s\": {\"type\": \"double\", \"value\": %f }\n", key, *(float64*)(src + sizeof(DataMessageEntryHeader)));
468 else
469 userData += utils::StringFormat("\"%s\": \"%f\"\n", key, *(float64*)(src + sizeof(DataMessageEntryHeader)));
470 break;
471 case DATAMESSAGEID:
472 if (((DataMessageHeader*)(src + sizeof(DataMessageEntryHeader)))->cid == DATAMESSAGEID) {
473 if (!oldData)
474 oldData = msg->swapMessageData(src + sizeof(DataMessageEntryHeader));
475 else
476 msg->swapMessageData(src + sizeof(DataMessageEntryHeader));
477 //msg = new DataMessage(src + sizeof(DataMessageEntryHeader));
478 //if (msg) {
479 if (userData.length()) userData += ", ";
480 if (!asText)
481 userData += utils::StringFormat("\"%s\": {\"type\": \"message\", \"value\": %s }\n", key, msg->toJSON().c_str());
482 else
483 userData += utils::StringFormat("\"%s\": %s\n", key, msg->getUserEntriesAsJSON(true).c_str());
484 //}
485 //delete(msg);
486 }
487 break;
488 case CONSTCHARINFOID:
489 if (!asText) {
490 if (userData.length()) userData += ", ";
491 userData += utils::StringFormat("\"%s\": {\"type\": \"textinfo\", \"size\": %u }\n", key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
492 }
493 break;
494 case CHARDATAINFOID:
495 if (!asText) {
496 if (userData.length()) userData += ", ";
497 userData += utils::StringFormat("\"%s\": {\"type\": \"binary\", \"size\": %u }\n", key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
498 }
499 break;
501 if (!asText) {
502 if (userData.length()) userData += ", ";
503 userData += utils::StringFormat("\"%s\": {\"type\": \"messageinfo\", \"size\": %u }\n", key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
504 }
505 break;
507 default:
508 break;
509 }
510 }
511 // else cannot generate JSON for this entry
512 src += ((DataMessageEntryHeader*)src)->size;
513 }
514
515 if (oldData)
516 msg->swapMessageData(oldData);
517 delete(msg);
518
519 std::string subUserData;
520
521 std::list<ArrayElem>::iterator entryII, entryEE;
522 std::map< std::string, std::list<ArrayElem> >::iterator keyII = userArrays.begin(), keyEE = userArrays.end();
523 while (keyII != keyEE) {
524 subUserData.clear();
525 entryII = keyII->second.begin();
526 entryEE = keyII->second.end();
527 while (entryII != entryEE) {
528 char* ep = entryII->entryPtr;
529 char* val = ep + sizeof(DataMessageEntryHeader);
530 uint32 cid = entryII->cid;
531 if (subUserData.length()) subUserData += ", ";
532 switch (cid) {
533 case CONSTCHARID:
534 if (!asText)
535 subUserData += utils::StringFormat("%lld: {\"type\": \"text\", \"value\": \"%s\" }\n", entryII->index, val);
536 else
537 subUserData += utils::StringFormat("%lld: \"%s\"\n", entryII->index, val);
538 break;
539 case TIMEID:
540 if (!asText)
541 subUserData += utils::StringFormat("%lld: {\"type\": \"time\", \"value\": %llu, \"text\": \"%s\" }\n",
542 entryII->index, *(uint64*)val,
543 *(uint64*)val ? PrintTimeString(*(uint64*)val).c_str() : ""
544 );
545 else
546 subUserData += utils::StringFormat("%lld: \"%s\"\n", entryII->index, *(uint64*)val ? PrintTimeString(*(uint64*)val).c_str() : "");
547 break;
548 case CHARDATAID:
549 if (!asText)
550 subUserData += utils::StringFormat("%lld: {\"type\": \"binary\", \"size\": %u }\n", entryII->index, ((DataMessageEntryHeader*)ep)->size - sizeof(DataMessageEntryHeader));
551 break;
552 case DATAMESSAGEID:
553 msg = new DataMessage(val);
554 if (msg) {
555 if (subUserData.length()) subUserData += ", ";
556 if (!asText)
557 subUserData += utils::StringFormat("%lld: {\"type\": \"message\", \"value\": %s }\n", entryII->index, msg->toJSON().c_str());
558 else
559 subUserData += utils::StringFormat("%lld: %s }\n", entryII->index, msg->getUserEntriesAsJSON(true).c_str());
560 }
561 delete(msg);
562 break;
563 case INTID:
564 if (!asText)
565 subUserData += utils::StringFormat("%lld: {\"type\": \"integer\", \"value\": %lld }\n", entryII->index, *(int64*)val);
566 else
567 subUserData += utils::StringFormat("%lld: %lld\n", entryII->index, *(int64*)val);
568 break;
569 case DOUBLEID:
570 if (!asText)
571 subUserData += utils::StringFormat("%lld: {\"type\": \"double\", \"value\": %f }\n", entryII->index, *(float64*)val);
572 else
573 subUserData += utils::StringFormat("%lld: \"%f\" }\n", entryII->index, *(float64*)val);
574 break;
575 case CONSTCHARINFOID:
576 if (!asText)
577 subUserData += utils::StringFormat("%lld: {\"type\": \"textinfo\", \"size\": %lld }", entryII->index, *(int64*)val);
578 break;
579 case CHARDATAINFOID:
580 if (!asText)
581 subUserData += utils::StringFormat("%lld: {\"type\": \"binary\", \"size\": %lld }", entryII->index, *(int64*)val);
582 break;
584 if (!asText)
585 subUserData += utils::StringFormat("%lld: {\"type\": \"messageinfo\", \"size\": %lld }", entryII->index, *(int64*)val);
586 break;
587 default:
588 break;
589 }
590 entryII++;
591 }
592 if (userData.length()) userData += ", ";
593 if (!asText)
594 userData += utils::StringFormat("\"%s\": {\"type\": \"array\", \"value\":\n{%s} }\n", keyII->first.c_str(), subUserData.c_str());
595 else
596 userData += utils::StringFormat("\"%s\": {%s}\n", keyII->first.c_str(), subUserData.c_str());
597 keyII++;
598 }
599
600
601 std::list<MapElem>::iterator entryI, entryE;
602 std::map< std::string, std::list<MapElem> >::iterator keyI = userMaps.begin(), keyE = userMaps.end();
603 while (keyI != keyE) {
604 subUserData.clear();
605 entryI = keyI->second.begin();
606 entryE = keyI->second.end();
607 while (entryI != entryE) {
608 char* ep = entryI->entryPtr;
609 char* val = ep + sizeof(DataMessageEntryHeader);
610 uint32 cid = entryI->cid;
611 if (subUserData.length()) subUserData += ", ";
612 switch (cid) {
613 case CONSTCHARID:
614 if (!asText)
615 subUserData += utils::StringFormat("\"%s\": {\"type\": \"text\", \"value\": \"%s\" }\n", entryI->index.c_str(), val);
616 else
617 subUserData += utils::StringFormat("\"%s\": \"%s\"\n", entryI->index.c_str(), val);
618 break;
619 case TIMEID:
620 if (!asText)
621 subUserData += utils::StringFormat("\"%s\": {\"type\": \"time\", \"value\": %llu, \"text\": \"%s\" }\n",
622 entryI->index.c_str(), *(uint64*)val,
623 *(uint64*)val ? PrintTimeString(*(uint64*)val).c_str() : ""
624 );
625 else
626 subUserData += utils::StringFormat("\"%s\": \"%s\"\n", entryI->index.c_str(), *(uint64*)val ? PrintTimeString(*(uint64*)val).c_str() : "");
627 break;
628 case CHARDATAID:
629 if (!asText)
630 subUserData += utils::StringFormat("\"%s\": {\"type\": \"binary\", \"size\": %u }\n", entryI->index.c_str(), ((DataMessageEntryHeader*)ep)->size - sizeof(DataMessageEntryHeader));
631 break;
632 case DATAMESSAGEID:
633 msg = new DataMessage(val);
634 if (msg) {
635 if (subUserData.length()) subUserData += ", ";
636 if (!asText)
637 subUserData += utils::StringFormat("\"%s\": {\"type\": \"message\", \"value\": %s }\n", entryI->index.c_str(), msg->toJSON().c_str());
638 else
639 subUserData += utils::StringFormat("\"%s\": %s\n", entryI->index.c_str(), msg->getUserEntriesAsJSON(true).c_str());
640 }
641 delete(msg);
642 break;
643 case INTID:
644 if (!asText)
645 subUserData += utils::StringFormat("\"%s\": {\"type\": \"integer\", \"value\": %lld }\n", entryI->index.c_str(), *(int64*)val);
646 else
647 subUserData += utils::StringFormat("\"%s\": \"%lld\"\n", entryI->index.c_str(), *(int64*)val);
648 break;
649 case DOUBLEID:
650 if (!asText)
651 subUserData += utils::StringFormat("\"%s\": {\"type\": \"double\", \"value\": %f }\n", entryI->index.c_str(), *(float64*)val);
652 else
653 subUserData += utils::StringFormat("\"%s\": \"%f\" }\n", entryI->index.c_str(), *(float64*)val);
654 break;
655 case CONSTCHARINFOID:
656 if (!asText)
657 subUserData += utils::StringFormat("\"%s\": {\"type\": \"textinfo\", \"size\": %lld }", entryI->index.c_str(), *(int64*)val);
658 break;
659 case CHARDATAINFOID:
660 if (!asText)
661 subUserData += utils::StringFormat("\"%s\": {\"type\": \"binary\", \"size\": %lld }", entryI->index.c_str(), *(int64*)val);
662 break;
664 if (!asText)
665 subUserData += utils::StringFormat("\"%s\": {\"type\": \"messageinfo\", \"size\": %lld }", entryI->index.c_str(), *(int64*)val);
666 break;
667 default:
668 break;
669 }
670 entryI++;
671 }
672 if (userData.length()) userData += ", ";
673 if (!asText)
674 userData += utils::StringFormat("\"%s\": {\"type\": \"map\", \"value\":\n{%s} }\n", keyI->first.c_str(), subUserData.c_str());
675 else
676 userData += utils::StringFormat("\"%s\": {%s}\n", keyI->first.c_str(), subUserData.c_str());
677 keyI++;
678 }
679
680 return utils::StringFormat("{\n%s}\n", userData.c_str());
681}
682
683
684std::string DataMessage::toJSON(std::map<uint16, std::string>* subtypes, std::map<uint16, std::string>* subcontexts, std::map<uint32, std::string>* compNames) {
685 if (data->cid != DATAMESSAGEID) return "";
686
687 if (!data->userCount)
688 return data->toJSON(subtypes, subcontexts, compNames);
689
690 std::string userData = getUserEntriesAsJSON(false);
691
692 std::map< std::string, std::list<std::string> > userMaps;
693 std::map< std::string, std::list<int64> > userArrays;
694
695 uint32 draftSize = 0;
696 uint32 draftUserSize = 0;
697 uint32 draftUserCount = 0;
698
699 const char* key;
700 char* src = (char*)data + sizeof(DataMessageHeader);
701 char* srcEnd = (char*)data + data->size;
702 while (src < srcEnd) {
703 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
704 key = src + sizeof(DataMessageEntryHeader);
705 src += sizeof(DataMessageEntryHeader) + strlen(key) + 1;
706 switch (((DataMessageEntryHeader*)src)->cid) {
708 if (strcmp(key, "OS") == 0)
709 draftSize = (uint32)(*(int64*)(src + sizeof(DataMessageEntryHeader)));
710 else if (strcmp(key, "US") == 0)
711 draftUserSize = (uint32)(*(int64*)(src + sizeof(DataMessageEntryHeader)));
712 else if (strcmp(key, "UC") == 0)
713 draftUserCount = (uint32)(*(int64*)(src + sizeof(DataMessageEntryHeader)));
714 default:
715 break;
716 }
717 }
718 src += ((DataMessageEntryHeader*)src)->size;
719 }
720
721 if (draftSize) {
722 return utils::StringFormat("{ \"draft\":\"yes\", \"size\": %u, \"memid\": \"%llu\", \"time\": \"%llu\", \"time__text\": \"%s\", \"sendtime\": \"%llu\", \"sendtime__text\": \"%s\", \"recvtime\": \"%llu\", \"recvtime__text\": \"%s\", \"origin\": %u, \"destination\": %u, \"ttl\": \"%llu\", \"eol__text\": \"%s\", \"priority\": %u, \"policy\": %u, \"from\": %u, \"to\": %u, \"type\": \"%s\", \"tag\": \"%u\", \"status\": %u, \"reference\": \"%llu\", \"serial\": \"%llu\", \"contextchange\": \"%s\", \"userdatacount\": %u, \"userdatasize\": %u, \"userdata\":\n%s }\n",
723 draftSize, data->memid,
724 data->time, data->time ? PrintTimeString(data->time).c_str() : "",
725 data->sendtime, data->sendtime ? PrintTimeString(data->sendtime).c_str() : "",
726 data->recvtime, data->recvtime ? PrintTimeString(data->recvtime).c_str() : "",
727 data->origin, data->destination, data->ttl, data->ttl ? PrintTimeString(data->time + data->ttl).c_str() : "",
728 data->priority, data->policy, data->from, data->to, data->type.toString().c_str(), data->tag, data->status, data->reference, data->serial,
729 data->contextchange.isValid() ? data->contextchange.toString().c_str() : "",
730 draftUserCount, draftUserSize, userData.c_str());
731 }
732 else {
733 return utils::StringFormat("{ \"size\": %u, \"memid\": \"%llu\", \"time\": \"%llu\", \"time__text\": \"%s\", \"sendtime\": \"%llu\", \"sendtime__text\": \"%s\", \"recvtime\": \"%llu\", \"recvtime__text\": \"%s\", \"origin\": %u, \"destination\": %u, \"ttl\": \"%llu\", \"eol__text\": \"%s\", \"priority\": %u, \"policy\": %u, \"from\": %u, \"to\": %u, \"type\": \"%s\", \"tag\": \"%u\", \"status\": %u, \"reference\": \"%llu\", \"serial\": \"%llu\", \"contextchange\": \"%s\", \"userdatacount\": %u, \"userdatasize\": %u, \"userdata\":\n%s }\n",
734 data->size, data->memid,
735 data->time, data->time ? PrintTimeString(data->time).c_str() : "",
736 data->sendtime, data->sendtime ? PrintTimeString(data->sendtime).c_str() : "",
737 data->recvtime, data->recvtime ? PrintTimeString(data->recvtime).c_str() : "",
738 data->origin, data->destination, data->ttl, data->ttl ? PrintTimeString(data->time + data->ttl).c_str() : "",
739 data->priority, data->policy, data->from, data->to, data->type.toString().c_str(), data->tag, data->status, data->reference, data->serial,
740 data->contextchange.isValid() ? data->contextchange.toString().c_str() : "",
741 data->userCount, data->userSize, userData.c_str());
742 }
743}
744
745std::string DataMessage::toCSV(const char* separator, const char* preample, std::map<uint16, std::string>* subtypes, std::map<uint16, std::string>* subcontexts, std::map<uint32, std::string>* compNames) {
746 // triggername,type,created,sent,received,modulecputime,modulewalltime,chaincputime,chainwalltime,chaincount,size,priority,ttl,from,to,tag,serial,contextchange,userdatacount,userdatasize,userdata
747
748 char sep[64];
749 uint32 len;
750 if (separator && (len = (uint32)strlen(separator)))
751 memcpy(sep, separator, len+1);
752 else
753 memcpy(sep, ",", 2);
754
755 std::string userData;
756 if (data->userCount) {
757 userData = this->getUserEntriesAsString();
758 utils::StringSingleReplace(userData, sep, "*", false);
759 utils::StringSingleReplace(userData, "\n", "||", false);
760 }
761
762 std::string typeStr, contextStr, fromStr, toStr;
763 typeStr = data->type.toString(subtypes);
764 contextStr = data->contextchange.toString(subcontexts);
765 if (compNames) {
766 fromStr = (*compNames)[data->from];
767 toStr = (*compNames)[data->to];
768 }
769 if (!fromStr.length()) {
770 if (data->from)
771 fromStr = utils::StringFormat("Component %u", data->from);
772 }
773 if (!toStr.length()) {
774 if (data->to)
775 toStr = utils::StringFormat("Component %u", data->to);
776 }
777
778 if (preample && strlen(preample)) {
779 return utils::StringFormat("%s%s%s%s%s%s%s%s%s%s%u%s%u%s%u%s%u%s%u%s%u%s%u%s%llu%s%s%s%s%s%u%s%llu%s%s%s%u%s%u%s%s\n",
780 preample, sep,
781 typeStr.c_str(), sep,
782 data->time ? PrintTimeString(data->time).c_str() : "", sep,
783 data->sendtime ? PrintTimeString(data->sendtime).c_str() : "", sep,
784 data->recvtime ? PrintTimeString(data->recvtime).c_str() : "", sep,
785 data->cyclecputime, sep,
786 data->cyclewalltime, sep,
787 data->chaincputime, sep,
788 data->chainwalltime, sep,
789 data->chaincount, sep,
790 data->size, sep,
791 data->priority, sep,
792 data->ttl, sep,
793 fromStr.c_str(), sep,
794 toStr.c_str(), sep,
795 data->tag, sep,
796 data->serial, sep,
797 contextStr.c_str(), sep,
798 data->userCount, sep,
799 data->userSize, sep,
800 userData.c_str());
801 }
802 else {
803 return utils::StringFormat("%s%s%s%s%s%s%s%s%u%s%u%s%u%s%u%s%u%s%u%s%u%s%llu%s%s%s%s%s%u%s%llu%s%s%s%u%s%u%s%s\n",
804 typeStr.c_str(), sep,
805 data->time ? PrintTimeString(data->time).c_str() : "", sep,
806 data->sendtime ? PrintTimeString(data->sendtime).c_str() : "", sep,
807 data->recvtime ? PrintTimeString(data->recvtime).c_str() : "", sep,
808 data->cyclecputime, sep,
809 data->cyclewalltime, sep,
810 data->chaincputime, sep,
811 data->chainwalltime, sep,
812 data->chaincount, sep,
813 data->size, sep,
814 data->priority, sep,
815 data->ttl, sep,
816 fromStr.c_str(), sep,
817 toStr.c_str(), sep,
818 data->tag, sep,
819 data->serial, sep,
820 contextStr.c_str(), sep,
821 data->userCount, sep,
822 data->userSize, sep,
823 userData.c_str());
824 }
825}
826
827std::string DataMessage::GetCSVHeader(const char* separator, const char* preample) {
828 // triggername,type,created,sent,received,modulecputime,modulewalltime,chaincputime,chainwalltime,chaincount,size,priority,ttl,from,to,tag,serial,contextchange,userdatacount,userdatasize,userdata
829
830 char sep[64];
831 uint32 len;
832 if (separator && (len = (uint32)strlen(separator)))
833 memcpy(sep, separator, len + 1);
834 else
835 memcpy(sep, ",", 2);
836
837 std::string header;
838 if (preample && strlen(preample))
839 header = utils::StringFormat("%s,type,created,sent,received,modulecputime,modulewalltime,chaincputime,chainwalltime,chaincount,size,priority,ttl,from,to,tag,serial,contextchange,userdatacount,userdatasize,userdata\n", preample);
840 else
841 header = "type,created,sent,received,modulecputime,modulewalltime,chaincputime,chainwalltime,chaincount,size,priority,ttl,from,to,tag,serial,contextchange,userdatacount,userdatasize,userdata\n";
842
843 if (stricmp(sep, ",") != 0)
844 utils::StringSingleReplace(header, ",", sep, false);
845
846 return header;
847}
848
849
850std::string DataMessage::toXML(std::map<uint16, std::string>* subtypes, std::map<uint16, std::string>* subcontexts, std::map<uint32, std::string>* compNames) {
851 if (data->cid != DATAMESSAGEID) return "";
852
853 if (!data->userCount)
854 return data->toXML(subtypes, subcontexts, compNames);
855
856 std::map< std::string, std::list<ArrayElem> > userArrays;
857 std::map< std::string, std::list<MapElem> > userMaps;
858 uint32 draftSize = 0;
859 uint32 draftUserSize = 0;
860 uint32 draftUserCount = 0;
861
862 const char* oldData = NULL;
863 DataMessage* msg = new DataMessage();
864 const char* key;
865 const char* mapStart, *mapEnd;
866 std::string userData;
867 userData.reserve(data->userSize + data->userCount * 64);
868 char* src = (char*)data + sizeof(DataMessageHeader);
869 char* srcEnd = (char*)data + data->size;
870 while (src < srcEnd) {
871 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
872 key = src + sizeof(DataMessageEntryHeader);
873 src += sizeof(DataMessageEntryHeader) + strlen(key) + 1;
874 if ((mapStart = strstr(key, "_-|")) && (mapEnd = strstr(key, "|-_"))) {
875 ArrayElem e;
876 e.index = utils::Ascii2Int64(std::string(mapStart + 3, mapEnd - mapStart - 3).c_str());
877 e.entryPtr = src;
878 e.cid = ((DataMessageEntryHeader*)src)->cid;
879 userArrays[std::string(key, mapStart - key)].push_back(e);
880 src += ((DataMessageEntryHeader*)src)->size;
881 continue;
882 }
883 else if ((mapStart = strstr(key, "_-[")) && (mapEnd = strstr(key, "]-_"))) {
884 MapElem e;
885 e.index = std::string(mapStart + 3, mapEnd - mapStart - 3);
886 e.entryPtr = src;
887 e.cid = ((DataMessageEntryHeader*)src)->cid;
888 userMaps[std::string(key, mapStart - key)].push_back(e);
889 src += ((DataMessageEntryHeader*)src)->size;
890 continue;
891 }
892 switch (((DataMessageEntryHeader*)src)->cid) {
893 case CONSTCHARID:
894 userData += utils::StringFormat("<data name=\"%s\" type=\"text\" value=\"%s\" />\n", key, src + sizeof(DataMessageEntryHeader));
895 break;
896 case TIMEID:
897 userData += utils::StringFormat("<data name=\"%s\" type=\"time\" value=\"%llu\" text=\"%s\" />\n",
898 key,
899 *(uint64*)(src + sizeof(DataMessageEntryHeader)),
900 *(uint64*)(src + sizeof(DataMessageEntryHeader)) ?
901 PrintTimeString(*(uint64*)(src + sizeof(DataMessageEntryHeader))).c_str() : ""
902 );
903 break;
904 case CHARDATAID:
905 userData += utils::StringFormat("<data name=\"%s\" type=\"binary\" size=\"%u\" />\n", key, ((DataMessageEntryHeader*)src)->size - sizeof(DataMessageEntryHeader));
906 break;
907 case INTID:
908 userData += utils::StringFormat("<data name=\"%s\" type=\"integer\" value=\"%lld\" />\n", key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
909 break;
910 case DOUBLEID:
911 userData += utils::StringFormat("<data name=\"%s\" type=\"float\" value=\"%f\" />\n", key, *(float64*)(src + sizeof(DataMessageEntryHeader)));
912 break;
913 case DATAMESSAGEID:
914 if (((DataMessageHeader*)(src + sizeof(DataMessageEntryHeader)))->cid == DATAMESSAGEID) {
915 if (!oldData)
916 oldData = msg->swapMessageData(src + sizeof(DataMessageEntryHeader));
917 else
918 msg->swapMessageData(src + sizeof(DataMessageEntryHeader));
919 //msg = new DataMessage(src + sizeof(DataMessageEntryHeader));
920 //if (msg)
921 userData += utils::StringFormat("<data name=\"%s\" type=\"datamessage\">\n%s</data>", key, msg->toXML().c_str());
922 //delete(msg);
923 }
924 break;
925 case CONSTCHARINFOID:
926 userData += utils::StringFormat("<data name=\"%s\" type=\"textinfo\" size=\"%u\" />\n", key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
927 break;
928 case CHARDATAINFOID:
929 userData += utils::StringFormat("<data name=\"%s\" type=\"binary\" size=\"%u\" />\n", key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
930 break;
932 userData += utils::StringFormat("<data name=\"%s\" type=\"messageinfo\" size=\"%u\" />\n", key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
933 break;
935 if (strcmp(key, "OS") == 0)
936 draftSize = (uint32)(*(int64*)(src + sizeof(DataMessageEntryHeader)));
937 else if (strcmp(key, "US") == 0)
938 draftUserSize = (uint32)(*(int64*)(src + sizeof(DataMessageEntryHeader)));
939 else if (strcmp(key, "UC") == 0)
940 draftUserCount = (uint32)(*(int64*)(src + sizeof(DataMessageEntryHeader)));
941 default:
942 break;
943 }
944 }
945 // else cannot generate JSON for this entry
946 src += ((DataMessageEntryHeader*)src)->size;
947 }
948
949 if (oldData)
950 msg->swapMessageData(oldData);
951 delete(msg);
952
953 std::string subUserData;
954
955 std::list<ArrayElem>::iterator entryII, entryEE;
956 std::map< std::string, std::list<ArrayElem> >::iterator keyII = userArrays.begin(), keyEE = userArrays.end();
957 while (keyII != keyEE) {
958 subUserData.clear();
959 entryII = keyII->second.begin();
960 entryEE = keyII->second.end();
961 while (entryII != entryEE) {
962 char* ep = entryII->entryPtr;
963 char* val = ep + sizeof(DataMessageEntryHeader);
964 uint32 cid = entryII->cid;
965 switch (cid) {
966 case CONSTCHARID:
967 subUserData += utils::StringFormat("<data name=\"%lld\" type=\"text\" value=\"%s\" />\n", entryII->index, val);
968 break;
969 case TIMEID:
970 subUserData += utils::StringFormat("<data name=\"%lld\" type=\"time\" value=\"%llu\" text=\"%s\" />\n",
971 entryII->index, *(uint64*)val,
972 *(uint64*)val ? PrintTimeString(*(uint64*)val).c_str() : ""
973 );
974 break;
975 case CHARDATAID:
976 subUserData += utils::StringFormat("<data name=\"%lld\" type=\"binary\" size=\"%u\" />\n", entryII->index, ((DataMessageEntryHeader*)ep)->size - sizeof(DataMessageEntryHeader));
977 break;
978 case INTID:
979 subUserData += utils::StringFormat("<data name=\"%lld\" type=\"integer\" value=\"%lld\" />\n", entryII->index, *(int64*)val);
980 break;
981 case DOUBLEID:
982 subUserData += utils::StringFormat("<data name=\"%lld\" type=\"float\" value=\"%f\" />\n", entryII->index, *(float64*)val);
983 break;
984 case DATAMESSAGEID:
985 msg = new DataMessage(val);
986 if (msg) {
987 subUserData += utils::StringFormat("<data name=\"%lld\" type=\"datamessage\">\n%s</data>", entryII->index, msg->toXML().c_str());
988 delete(msg);
989 }
990 break;
991 case CONSTCHARINFOID:
992 subUserData += utils::StringFormat("<data name=\"%lld\" type=\"textinfo\" size=\"%lld\" />\n", entryII->index, *(int64*)val);
993 break;
994 case CHARDATAINFOID:
995 subUserData += utils::StringFormat("<data name=\"%lld\" type=\"binary\" size=\"%lld\" />\n", entryII->index, *(int64*)val);
996 break;
998 subUserData += utils::StringFormat("<data name=\"%lld\" type=\"messageinfo\" size=\"%lld\" />\n", entryII->index, *(int64*)val);
999 break;
1000 default:
1001 break;
1002 }
1003 entryII++;
1004 }
1005 userData += utils::StringFormat("<array name=\"%s\">\n%s</array>\n", keyII->first.c_str(), subUserData.c_str());
1006 keyII++;
1007 }
1008
1009 std::list<MapElem>::iterator entryI, entryE;
1010 std::map< std::string, std::list<MapElem> >::iterator keyI = userMaps.begin(), keyE = userMaps.end();
1011 while (keyI != keyE) {
1012 subUserData.clear();
1013 entryI = keyI->second.begin();
1014 entryE = keyI->second.end();
1015 while (entryI != entryE) {
1016 char* ep = entryI->entryPtr;
1017 char* val = ep + sizeof(DataMessageEntryHeader);
1018 uint32 cid = entryI->cid;
1019 switch (cid) {
1020 case CONSTCHARID:
1021 subUserData += utils::StringFormat("<data name=\"%s\" type=\"text\" value=\"%s\" />\n", entryI->index.c_str(), val);
1022 break;
1023 case TIMEID:
1024 subUserData += utils::StringFormat("<data name=\"%s\" type=\"time\" value=\"%llu\" text=\"%s\" />\n",
1025 entryI->index.c_str(), *(uint64*)val,
1026 *(uint64*)val ? PrintTimeString(*(uint64*)val).c_str() : ""
1027 );
1028 break;
1029 case CHARDATAID:
1030 subUserData += utils::StringFormat("<data name=\"%s\" type=\"binary\" size=\"%u\" />\n", entryI->index.c_str(), ((DataMessageEntryHeader*)ep)->size - sizeof(DataMessageEntryHeader));
1031 break;
1032 case INTID:
1033 subUserData += utils::StringFormat("<data name=\"%s\" type=\"integer\" value=\"%lld\" />\n", entryI->index.c_str(), *(int64*)val);
1034 break;
1035 case DOUBLEID:
1036 subUserData += utils::StringFormat("<data name=\"%s\" type=\"float\" value=\"%f\" />\n", entryI->index.c_str(), *(float64*)val);
1037 break;
1038 case DATAMESSAGEID:
1039 msg = new DataMessage(val);
1040 if (msg) {
1041 subUserData += utils::StringFormat("<data name=\"%s\" type=\"datamessage\">\n%s</data>", entryI->index.c_str(), msg->toXML().c_str());
1042 delete(msg);
1043 }
1044 break;
1045 case CONSTCHARINFOID:
1046 subUserData += utils::StringFormat("<data name=\"%s\" type=\"textinfo\" size=\"%lld\" />\n", entryI->index.c_str(), *(int64*)val);
1047 break;
1048 case CHARDATAINFOID:
1049 subUserData += utils::StringFormat("<data name=\"%s\" type=\"binary\" size=\"%lld\" />\n", entryI->index.c_str(), *(int64*)val);
1050 break;
1051 case DATAMESSAGEINFOID:
1052 subUserData += utils::StringFormat("<data name=\"%s\" type=\"messageinfo\" size=\"%lld\" />\n", entryI->index.c_str(), *(int64*)val);
1053 break;
1054 default:
1055 break;
1056 }
1057 entryI++;
1058 }
1059 userData += utils::StringFormat("<map name=\"%s\">\n%s</map>\n", keyI->first.c_str(), subUserData.c_str());
1060 keyI++;
1061 }
1062
1063 if (draftSize) {
1064 return utils::StringFormat("<datamessage draft=\"yes\" size=\"%u\" memid=\"%llu\" time=\"%llu\" timetext=\"%s\" sendtime=\"%llu\" sendtimetext=\"%s\" recvtime=\"%llu\" recvtimetext=\"%s\" origin=\"%u\" destination=\"%u\" ttl=\"%llu\" eoltext=\"%s\" priority=\"%u\" policy=\"%u\" from=\"%u\" to=\"%u\" type=\"%s\" tag=\"%u\" status=\"%u\" reference=\"%llu\" serial=\"%llu\" contextchange=\"%s\" userdatacount=\"%u\" userdatasize=\"%u\">\n%s</datamessage>\n",
1065 draftSize, data->memid,
1066 data->time, data->time ? PrintTimeString(data->time).c_str() : "",
1067 data->sendtime, data->sendtime ? PrintTimeString(data->sendtime).c_str() : "",
1068 data->recvtime, data->recvtime ? PrintTimeString(data->recvtime).c_str() : "",
1069 data->origin, data->destination, data->ttl, data->ttl ? PrintTimeString(data->time + data->ttl).c_str() : "",
1070 data->priority, data->policy, data->from, data->to, data->type.toString().c_str(), data->tag, data->status, data->reference, data->serial, data->contextchange.isValid() ? data->contextchange.toString().c_str() : "", draftUserCount, draftUserSize, userData.c_str());
1071 }
1072 else {
1073 return utils::StringFormat("<datamessage size=\"%u\" memid=\"%llu\" time=\"%llu\" timetext=\"%s\" sendtime=\"%llu\" sendtimetext=\"%s\" recvtime=\"%llu\" recvtimetext=\"%s\" origin=\"%u\" destination=\"%u\" ttl=\"%llu\" eoltext=\"%s\" priority=\"%u\" policy=\"%u\" from=\"%u\" to=\"%u\" type=\"%s\" tag=\"%u\" status=\"%u\" reference=\"%llu\" serial=\"%llu\" contextchange=\"%s\" userdatacount=\"%u\" userdatasize=\"%u\">\n%s</datamessage>\n",
1074 data->size, data->memid,
1075 data->time, data->time ? PrintTimeString(data->time).c_str() : "",
1076 data->sendtime, data->sendtime ? PrintTimeString(data->sendtime).c_str() : "",
1077 data->recvtime, data->recvtime ? PrintTimeString(data->recvtime).c_str() : "",
1078 data->origin, data->destination, data->ttl, data->ttl ? PrintTimeString(data->time + data->ttl).c_str() : "",
1079 data->priority, data->policy, data->from, data->to, data->type.toString().c_str(), data->tag, data->status, data->reference, data->serial, data->contextchange.isValid() ? data->contextchange.toString().c_str() : "", data->userCount, data->userSize, userData.c_str());
1080 }
1081}
1082
1083
1084
1085
1086// ################# Constructors #################
1087
1088// ---------------------------------------------------------------------------
1089// Constructors. All of them establish the invariant described at the top of
1090// this file: 'data' is a single malloc'ed block whose header fields size,
1091// cid (DATAMESSAGEID) and cver (CURRENTDATAMESSAGEVERSION) are always valid.
1092// Header-only construction is a plain malloc+memset; entries are appended
1093// later by setRawData() growing the block with realloc.
1094// ---------------------------------------------------------------------------
1096 this->data = (DataMessageHeader*) malloc(sizeof(DataMessageHeader));
1097 memset((char*)this->data, 0, sizeof(DataMessageHeader));
1098 data->size = sizeof(DataMessageHeader);
1099 data->cid = DATAMESSAGEID;
1101 data->time = GetTimeNow();
1103}
1104
1106 this->data = (DataMessageHeader*) malloc(sizeof(DataMessageHeader));
1107 memset((char*)this->data, 0, sizeof(DataMessageHeader));
1108 data->size = sizeof(DataMessageHeader);
1109 data->cid = DATAMESSAGEID;
1111 data->type = type;
1112 data->time = GetTimeNow();
1113 data->from = from;
1115}
1116
1117DataMessage::DataMessage(PsyType type, uint32 from, uint32 to, uint64 ttl, uint16 priority) {
1118 this->data = (DataMessageHeader*) malloc(sizeof(DataMessageHeader));
1119 memset((char*)this->data, 0, sizeof(DataMessageHeader));
1120 data->size = sizeof(DataMessageHeader);
1121 data->cid = DATAMESSAGEID;
1123 data->type = type;
1124 data->time = GetTimeNow();
1125 data->from = from;
1126 data->to = to;
1127 data->ttl = ttl;
1128 data->priority = priority;
1130}
1131
1132DataMessage::DataMessage(PsyType type, uint32 from, uint32 to, uint32 tag, uint64 time, uint64 ttl, uint16 priority) {
1133 this->data = (DataMessageHeader*) malloc(sizeof(DataMessageHeader));
1134 memset((char*)this->data, 0, sizeof(DataMessageHeader));
1135 data->size = sizeof(DataMessageHeader);
1136 data->cid = DATAMESSAGEID;
1138 data->type = type;
1139 data->time = GetTimeNow();
1140 data->from = from;
1141 data->to = to;
1142 data->tag = tag;
1143 data->time = time;
1144 data->ttl = ttl;
1145 data->priority = priority;
1147}
1148
1149// Adopting/copying constructor for raw serialised bytes (e.g. straight off a
1150// socket). Handles three cases:
1151// - old-format block (DATAMESSAGEOLDID): converted into a fresh block via
1152// ConvertDataFromOlderMessageFormat(); the input is delete[]'d when
1153// copy==false because ownership was transferred to us.
1154// - current-format block: adopted in place (copy==false, zero-copy - the
1155// caller must NOT free it afterwards) or duplicated (copy==true; the
1156// total size is read from the leading uint32).
1157// - anything else / failed conversion: falls through to an empty message so
1158// the object is always usable.
1159// The "check cpu numbers" fixup repairs cycle/chain CPU counters that were
1160// written as wrapped-around (MAXVALUINT32 - x) values by older senders.
1162 char* newData = NULL;
1163 if (GetObjID(data) == DATAMESSAGEOLDID) {
1164 if (ConvertDataFromOlderMessageFormat(data, &newData) && newData) {
1165 this->data = (DataMessageHeader*)newData;
1166 if (!copy)
1167 delete[] data;
1169 return;
1170 }
1171 else
1172 delete[] newData;
1173 }
1174 else if (GetObjID(data) == DATAMESSAGEID) {
1175 // check cpu numbers
1176 if (((DataMessageHeader*)data)->cyclecputime > 10000 * (((DataMessageHeader*)data)->cyclewalltime)) {
1177 ((DataMessageHeader*)data)->cyclecputime = MAXVALUINT32 - (((DataMessageHeader*)data)->cyclecputime);
1178 ((DataMessageHeader*)data)->chaincputime = MAXVALUINT32 - (((DataMessageHeader*)data)->chaincputime);
1179 }
1181 if (!copy)
1182 this->data = (DataMessageHeader*)data;
1183 else {
1184 uint32 size = *(uint32*)data;
1185 this->data = (DataMessageHeader*)malloc(size);
1186 memcpy(this->data, data, size);
1187 }
1189 return;
1190 }
1191 else {
1192 if (ConvertDataFromOlderMessageFormat(data, &newData) && newData) {
1193 this->data = (DataMessageHeader*)newData;
1194 if (!copy)
1195 delete[] data;
1197 return;
1198 }
1199 else
1200 delete[] newData;
1201 }
1202 }
1203
1204 // If we get here, everything else failed, just create empty message
1205 this->data = (DataMessageHeader*) malloc(sizeof(DataMessageHeader));
1206 memset((char*)this->data, 0, sizeof(DataMessageHeader));
1207 this->data->size = sizeof(DataMessageHeader);
1208 this->data->cid = DATAMESSAGEID;
1209 this->data->cver = CURRENTDATAMESSAGEVERSION;
1211 return;
1212}
1213
1214// Const-input variant of the above: never adopts, always converts or copies
1215// (the source bytes are left untouched apart from the CPU-counter fixup).
1217 char* newData = NULL;
1218 if (GetObjID(data) == DATAMESSAGEOLDID) {
1219 if (ConvertDataFromOlderMessageFormat(data, &newData) && newData) {
1220 this->data = (DataMessageHeader*)newData;
1222 return;
1223 }
1224 else
1225 delete[] newData;
1226 }
1227 else if (GetObjID(data) == DATAMESSAGEID) {
1228 // check cpu numbers
1229 if (((DataMessageHeader*)data)->cyclecputime > 10 * (((DataMessageHeader*)data)->cyclewalltime)) {
1230 ((DataMessageHeader*)data)->cyclecputime = MAXVALUINT32 - (((DataMessageHeader*)data)->cyclecputime);
1231 ((DataMessageHeader*)data)->chaincputime = MAXVALUINT32 - (((DataMessageHeader*)data)->chaincputime);
1232 }
1234 uint32 size = *(uint32*)data;
1235 this->data = (DataMessageHeader*)malloc(size);
1236 memcpy(this->data, data, size);
1238 return;
1239 }
1240 else {
1241 if (ConvertDataFromOlderMessageFormat(data, &newData) && newData) {
1242 this->data = (DataMessageHeader*)newData;
1244 return;
1245 }
1246 else
1247 delete[] newData;
1248 }
1249 }
1250
1251 // If we get here, everything else failed, just create empty message
1252 this->data = (DataMessageHeader*)malloc(sizeof(DataMessageHeader));
1253 memset((char*)this->data, 0, sizeof(DataMessageHeader));
1254 this->data->size = sizeof(DataMessageHeader);
1255 this->data->cid = DATAMESSAGEID;
1256 this->data->cver = CURRENTDATAMESSAGEVERSION;
1258 return;
1259}
1260
1261// Deep-copy constructor: one malloc + one memcpy of the whole flat block.
1263 if (GetObjID(msg.data) != DATAMESSAGEID) {
1264 this->data = (DataMessageHeader*) malloc(sizeof(DataMessageHeader));
1265 memset((char*)this->data, 0, sizeof(DataMessageHeader));
1266 this->data->size = sizeof(DataMessageHeader);
1267 this->data->cid = DATAMESSAGEID;
1268 this->data->cver = CURRENTDATAMESSAGEVERSION;
1270 return;
1271 }
1272 this->data = (DataMessageHeader*)malloc(msg.data->size);
1273 memcpy(this->data, msg.data, msg.data->size);
1275}
1276
1277// "Draft" constructor: build a size-capped preview of a (possibly huge)
1278// message. If the source fits within maxDraftSize it is copied verbatim;
1279// otherwise only the header is copied and fillInDraftUserDataFrom() adds as
1280// many entries as the budget allows, replacing oversized content with
1281// *INFOID size placeholders. Old-format input is first converted via a
1282// temporary full message.
1283DataMessage::DataMessage(const char* data, uint32 maxDraftSize) {
1284 if ((GetObjID(data) == DATAMESSAGEOLDID) ||
1286 DataMessage* correctMsg = new DataMessage(data, true);
1287 const char* newData = (const char*)correctMsg->data;
1288 uint32 size = *(uint32*)newData;
1289 if (size <= maxDraftSize) {
1290 this->data = (DataMessageHeader*)malloc(size);
1291 memcpy(this->data, newData, size);
1292 }
1293 else {
1294 this->data = (DataMessageHeader*)malloc(maxDraftSize);
1295 memcpy(this->data, newData, sizeof(DataMessageHeader));
1296 fillInDraftUserDataFrom(newData, maxDraftSize);
1297 }
1298 delete(correctMsg);
1300 return;
1301 }
1302 else if (GetObjID(data) != DATAMESSAGEID) {
1303 this->data = (DataMessageHeader*)malloc(sizeof(DataMessageHeader));
1304 memset((char*)this->data, 0, sizeof(DataMessageHeader));
1305 this->data->size = sizeof(DataMessageHeader);
1306 this->data->cid = DATAMESSAGEID;
1307 this->data->cver = CURRENTDATAMESSAGEVERSION;
1309 return;
1310 }
1311
1312 uint32 size = *(uint32*)data;
1313 if (size <= maxDraftSize) {
1314 this->data = (DataMessageHeader*)malloc(size);
1315 memcpy(this->data, data, size);
1316 }
1317 else {
1318 this->data = (DataMessageHeader*)malloc(maxDraftSize);
1319 memcpy(this->data, data, sizeof(DataMessageHeader));
1320 fillInDraftUserDataFrom(data, maxDraftSize);
1321 }
1323}
1324
1325// Draft copy of an existing message; same budget logic as above.
1326DataMessage::DataMessage(const DataMessage& msg, uint32 maxDraftSize) {
1327 if (GetObjID(msg.data) != DATAMESSAGEID) {
1328 this->data = (DataMessageHeader*)malloc(sizeof(DataMessageHeader));
1329 memset((char*)this->data, 0, sizeof(DataMessageHeader));
1330 this->data->size = sizeof(DataMessageHeader);
1331 this->data->cid = DATAMESSAGEID;
1332 this->data->cver = CURRENTDATAMESSAGEVERSION;
1334 return;
1335 }
1336 uint32 size = *(uint32*)msg.data;
1337 if (size <= maxDraftSize) {
1338 this->data = (DataMessageHeader*)malloc(size);
1339 memcpy(this->data, msg.data, size);
1340 }
1341 else {
1342 this->data = (DataMessageHeader*)malloc(maxDraftSize);
1343 memcpy(this->data, msg.data, sizeof(DataMessageHeader));
1344 fillInDraftUserDataFrom((char*)msg.data, maxDraftSize);
1345 }
1347}
1348
1349// Populate a draft message from a full serialised block without exceeding
1350// maxDraftSize. Strategy:
1351// 0. record the original totals as DATAMESSAGEDRAFTID entries "OS"
1352// (original size), "US" (user bytes) and "UC" (user count) so a reader
1353// can tell what was elided;
1354// 1. first pass copies all SMALL values (time/int/double) verbatim and
1355// replaces binary blobs and nested messages by int64 size placeholders
1356// (*INFOID cids);
1357// 2. second pass adds full strings while room remains, then falls back to
1358// CONSTCHARINFOID size placeholders.
1359// Entries that do not even fit as placeholders are merely counted into
1360// userCount/userSize so the header still reflects the full message.
1361bool DataMessage::fillInDraftUserDataFrom(const char* rawdata, uint32 maxDraftSize) {
1363 if (data->cid != DATAMESSAGEID) return false;
1364
1365 if (!data->userCount)
1366 return true;
1367
1368 this->data->size = sizeof(DataMessageHeader);
1369 uint32 draftInfoSize = (uint32)((2 * sizeof(DataMessageEntryHeader)) + 3 + sizeof(int64));
1370 if (this->data->size + (3 * draftInfoSize) > maxDraftSize)
1371 return false;
1372
1373 this->data->userCount = 0;
1374 this->data->userSize = 0;
1375
1376 int64 size;
1377 uint32 itemSize;
1378
1379 size = data->size;
1380 setRawData("OS", (char*)&size, sizeof(int64), DATAMESSAGEDRAFTID);
1381 size = data->userSize;
1382 setRawData("US", (char*)&size, sizeof(int64), DATAMESSAGEDRAFTID);
1383 size = data->userCount;
1384 setRawData("UC", (char*)&size, sizeof(int64), DATAMESSAGEDRAFTID);
1385
1386 std::map< std::string, std::list<std::string> > userMaps;
1387
1388 const char* key;
1389 std::string userData;
1390
1391 // First try to add all the small data info (int, float, time and info about binary, messages)
1392 char* src = (char*)data + sizeof(DataMessageHeader);
1393 char* srcEnd = (char*)data + data->size;
1394 while (src < srcEnd) {
1395 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
1396 key = src + sizeof(DataMessageEntryHeader);
1397 itemSize = (uint32)(sizeof(DataMessageEntryHeader) + strlen(key) + 1);
1398 src += itemSize;
1399 switch (((DataMessageEntryHeader*)src)->cid) {
1400 case TIMEID:
1401 itemSize += sizeof(DataMessageEntryHeader) + sizeof(uint64);
1402 if (this->data->size + itemSize < maxDraftSize) {
1403 setTime(key, *(uint64*)(src + sizeof(DataMessageEntryHeader)));
1404 }
1405 else {
1406 this->data->userCount++;
1407 this->data->userSize += itemSize;
1408 }
1409 break;
1410 case INTID:
1411 itemSize += sizeof(DataMessageEntryHeader) + sizeof(int64);
1412 if (this->data->size + itemSize < maxDraftSize) {
1413 setInt(key, *(int64*)(src + sizeof(DataMessageEntryHeader)));
1414 }
1415 else {
1416 this->data->userCount++;
1417 this->data->userSize += itemSize;
1418 }
1419 break;
1420 case DOUBLEID:
1421 itemSize += sizeof(DataMessageEntryHeader) + sizeof(float64);
1422 if (this->data->size + itemSize < maxDraftSize) {
1423 setDouble(key, (double)*(float64*)(src + sizeof(DataMessageEntryHeader)));
1424 }
1425 else {
1426 this->data->userCount++;
1427 this->data->userSize += itemSize;
1428 }
1429 break;
1430 case CHARDATAID:
1431 size = ((DataMessageEntryHeader*)src)->size - sizeof(DataMessageEntryHeader);
1432 itemSize += sizeof(DataMessageEntryHeader) + sizeof(int64);
1433 if (this->data->size + itemSize < maxDraftSize) {
1434 setRawData(key, (char*)&size, sizeof(int64), CHARDATAINFOID);
1435 }
1436 else {
1437 this->data->userCount++;
1438 this->data->userSize += itemSize - sizeof(int64) + (uint32)size;
1439 }
1440 break;
1441 case DATAMESSAGEID:
1442 size = ((DataMessageEntryHeader*)src)->size - sizeof(DataMessageEntryHeader);
1443 itemSize += sizeof(DataMessageEntryHeader) + sizeof(int64);
1444 if (this->data->size + itemSize < maxDraftSize) {
1445 setRawData(key, (char*)&size, sizeof(int64), DATAMESSAGEID);
1446 }
1447 else {
1448 this->data->userCount++;
1449 this->data->userSize += itemSize - sizeof(int64) + (uint32)size;
1450 }
1451 break;
1452
1453 case CONSTCHARINFOID:
1454 size = (*(int64*)(src + sizeof(DataMessageEntryHeader)));
1455 itemSize += sizeof(DataMessageEntryHeader) + sizeof(int64);
1456 if (this->data->size + itemSize < maxDraftSize) {
1457 setRawData(key, (char*)&size, sizeof(int64), CONSTCHARINFOID);
1458 }
1459 else {
1460 this->data->userCount++;
1461 this->data->userSize += itemSize - sizeof(int64) + (uint32)size;
1462 }
1463 break;
1464 case CHARDATAINFOID:
1465 size = (*(int64*)(src + sizeof(DataMessageEntryHeader)));
1466 itemSize += sizeof(DataMessageEntryHeader) + sizeof(int64);
1467 if (this->data->size + itemSize < maxDraftSize) {
1468 setRawData(key, (char*)&size, sizeof(int64), CHARDATAINFOID);
1469 }
1470 else {
1471 this->data->userCount++;
1472 this->data->userSize += itemSize - sizeof(int64) + (uint32)size;
1473 }
1474 break;
1475 case DATAMESSAGEINFOID:
1476 size = (*(int64*)(src + sizeof(DataMessageEntryHeader)));
1477 itemSize += sizeof(DataMessageEntryHeader) + sizeof(int64);
1478 if (this->data->size + itemSize < maxDraftSize) {
1479 setRawData(key, (char*)&size, sizeof(int64), DATAMESSAGEINFOID);
1480 }
1481 else {
1482 this->data->userCount++;
1483 this->data->userSize += itemSize - sizeof(int64) + (uint32)size;
1484 }
1485 break;
1486 // Ignore these for now
1487 case CONSTCHARID:
1488 default:
1489 break;
1490 }
1491 }
1492 src += ((DataMessageEntryHeader*)src)->size;
1493 }
1494
1495 // Now try to see how many strings we have room for
1496 src = (char*)data + sizeof(DataMessageHeader);
1497 srcEnd = (char*)data + data->size;
1498 while (src < srcEnd) {
1499 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
1500 key = src + sizeof(DataMessageEntryHeader);
1501 itemSize = (uint32)(sizeof(DataMessageEntryHeader) + strlen(key) + 1);
1502 src += itemSize;
1503 switch (((DataMessageEntryHeader*)src)->cid) {
1504 case CONSTCHARID:
1505 size = ((DataMessageEntryHeader*)src)->size - sizeof(DataMessageEntryHeader);
1506 itemSize += sizeof(DataMessageEntryHeader);
1507 if (this->data->size + itemSize + size < maxDraftSize) {
1508 // add the whole thing
1509 setString(key, (src + sizeof(DataMessageEntryHeader)));
1510 }
1511 else if (this->data->size + itemSize + sizeof(int64) < maxDraftSize) {
1512 setRawData(key, (char*)&size, sizeof(int64), CONSTCHARINFOID);
1513 }
1514 else {
1515 this->data->userCount++;
1516 this->data->userSize += itemSize + (uint32)size;
1517 }
1518 break;
1519 case TIMEID:
1520 case INTID:
1521 case DOUBLEID:
1522 case CHARDATAID:
1523 case DATAMESSAGEID:
1524 case CONSTCHARINFOID:
1525 case CHARDATAINFOID:
1526 case DATAMESSAGEINFOID:
1527 default:
1528 break;
1529 }
1530 }
1531 src += ((DataMessageEntryHeader*)src)->size;
1532 }
1533
1534 return true;
1535}
1536
1537
1538// The block was allocated with malloc/realloc, hence free() (not delete).
1540 //printf("Deleting msg: %u.%u.%u (%p)...\n", data->type[0], data->type[1], data->type[2], this);
1541 free(data);
1542 data = NULL;
1543}
1544
1545// Swap the underlying flat block for another one WITHOUT copying; returns
1546// the old block so the caller can free or re-adopt it. Used by memory-pool
1547// style callers to re-point a wrapper at shared memory. The find cache must
1548// be dropped because every cached pointer refers to the old block.
1549const char* DataMessage::swapMessageData(const char* data) {
1551 const char* oldData = (const char*)this->data;
1552 this->data = (DataMessageHeader*)data;
1553 return oldData;
1554}
1555
1556
1557// ################# User #################
1558
1559int64 DataMessage::getAsInt(const char* key) {
1560 return utils::Ascii2Int64(getAsString(key).c_str());
1561}
1562
1563uint64 DataMessage::getAsTime(const char* key) {
1564 return utils::Ascii2Uint64(getAsString(key).c_str());
1565}
1566
1567float64 DataMessage::getAsFloat(const char* key) {
1568 return utils::Ascii2Float64(getAsString(key).c_str());
1569}
1570
1571int64 DataMessage::getAsInt(int64 i, const char* key) {
1572 return utils::Ascii2Int64(getAsString(i, key).c_str());
1573}
1574
1575uint64 DataMessage::getAsTime(int64 i, const char* key) {
1576 return utils::Ascii2Uint64(getAsString(i, key).c_str());
1577}
1578
1579float64 DataMessage::getAsFloat(int64 i, const char* key) {
1580 return utils::Ascii2Float64(getAsString(i, key).c_str());
1581}
1582
1583int64 DataMessage::getAsInt(const char* idx, const char* key) {
1584 return utils::Ascii2Int64(getAsString(idx, key).c_str());
1585}
1586
1587uint64 DataMessage::getAsTime(const char* idx, const char* key) {
1588 return utils::Ascii2Uint64(getAsString(idx, key).c_str());
1589}
1590
1591float64 DataMessage::getAsFloat(const char* idx, const char* key) {
1592 return utils::Ascii2Float64(getAsString(idx, key).c_str());
1593}
1594
1595
1596bool DataMessage::getAsBool(const char* key) {
1597
1598 char* src = findEntry(key);
1599 if (src == NULL)
1600 return false;
1601
1602 uint32 cid = ((DataMessageEntryHeader*)src)->cid;
1603 src += sizeof(DataMessageEntryHeader);
1604
1605 switch (cid) {
1606 case CONSTCHARID:
1607 return ( (stricmp(src, "yes") == 0) || (stricmp(src, "true") == 0) || (stricmp(src, "1") == 0));
1608 case TIMEID:
1609 return (*(uint64*)src != 0);
1610 case CHARDATAID:
1611 return true;
1612 case INTID:
1613 return (*(int64*)src != 0);
1614 case DOUBLEID:
1615 return (((int64)(*(float64*)src)) != 0);
1616 case DATAMESSAGEID:
1617 return true;
1618 case CONSTCHARINFOID:
1619 return false;
1620 case CHARDATAINFOID:
1621 return false;
1622 case DATAMESSAGEINFOID:
1623 return false;
1624 default:
1625 return false;
1626 }
1627}
1628
1629std::string DataMessage::getAsString(const char* key) {
1630 char* src = findEntry(key);
1631 if (src == NULL) {
1632 if (stricmp(key, "size") == 0)
1633 return utils::StringFormat("%u", data->size);
1634 else if (stricmp(key, "cid") == 0)
1635 return utils::StringFormat("%llu", data->cid);
1636 else if (stricmp(key, "memid") == 0)
1637 return utils::StringFormat("%llu", data->memid);
1638 else if (stricmp(key, "time") == 0)
1639 return utils::StringFormat("%llu", data->time);
1640 else if (stricmp(key, "sendtime") == 0)
1641 return utils::StringFormat("%llu", data->sendtime);
1642 else if (stricmp(key, "recvtime") == 0)
1643 return utils::StringFormat("%llu", data->recvtime);
1644 else if (stricmp(key, "timetext") == 0)
1645 return data->time ? PrintTimeString(data->time) : "";
1646 else if (stricmp(key, "sendtimetext") == 0)
1647 return data->sendtime ? PrintTimeString(data->sendtime) : "";
1648 else if (stricmp(key, "recvtimetext") == 0)
1649 return data->recvtime ? PrintTimeString(data->recvtime) : "";
1650 else if (stricmp(key, "origin") == 0)
1651 return utils::StringFormat("%u", data->origin);
1652 else if (stricmp(key, "destination") == 0)
1653 return utils::StringFormat("%u", data->destination);
1654 else if (stricmp(key, "ttl") == 0)
1655 return utils::StringFormat("%llu", data->ttl);
1656 else if (stricmp(key, "eol") == 0)
1657 return data->ttl ? utils::StringFormat("%llu", data->time + data->ttl) : "0";
1658 else if (stricmp(key, "eoltext") == 0)
1659 return data->ttl ? PrintTimeString(data->time + data->ttl) : "";
1660 else if (stricmp(key, "priority") == 0)
1661 return utils::StringFormat("%u", data->priority);
1662 else if (stricmp(key, "policy") == 0)
1663 return utils::StringFormat("%u", data->policy);
1664 else if (stricmp(key, "from") == 0)
1665 return utils::StringFormat("%u", data->from);
1666 else if (stricmp(key, "to") == 0)
1667 return utils::StringFormat("%u", data->to);
1668 else if (stricmp(key, "type") == 0)
1669 return data->type.toString();
1670 else if (stricmp(key, "tag") == 0)
1671 return utils::StringFormat("%u", data->tag);
1672 else if (stricmp(key, "status") == 0)
1673 return utils::StringFormat("%u", data->status);
1674 else if (stricmp(key, "reference") == 0)
1675 return utils::StringFormat("%llu", data->reference);
1676 else if (stricmp(key, "serial") == 0)
1677 return utils::StringFormat("%llu", data->serial);
1678 else if (stricmp(key, "contextchange") == 0)
1679 return data->contextchange.isValid() ? data->contextchange.toString() : "";
1680 else if (stricmp(key, "userSize") == 0)
1681 return utils::StringFormat("%u", data->userSize);
1682 else if (stricmp(key, "userCount") == 0)
1683 return utils::StringFormat("%u", data->userCount);
1684 else
1685 return "";
1686 }
1687
1688 switch (((DataMessageEntryHeader*)src)->cid) {
1689 case CONSTCHARID:
1690 return src + sizeof(DataMessageEntryHeader);
1691 case TIMEID:
1692 return PrintTimeString(*(uint64*)(src + sizeof(DataMessageEntryHeader)));
1693 case CHARDATAID:
1694 return utils::StringFormat("BinaryData [%ub]", ((DataMessageEntryHeader*)src)->size);
1695 case INTID:
1696 return utils::StringFormat("%lld", *(int64*)(src + sizeof(DataMessageEntryHeader)));
1697 case DOUBLEID:
1698 return utils::StringFormat("%f", *(float64*)(src + sizeof(DataMessageEntryHeader)));
1699 case DATAMESSAGEID:
1700 return utils::StringFormat("DataMessage [%ub]", ((DataMessageEntryHeader*)src)->size);
1701 case CONSTCHARINFOID:
1702 return utils::StringFormat("(%lld)", *(int64*)(src + sizeof(DataMessageEntryHeader)));
1703 case CHARDATAINFOID:
1704 return utils::StringFormat("(%lld)", *(int64*)(src + sizeof(DataMessageEntryHeader)));
1705 case DATAMESSAGEINFOID:
1706 return utils::StringFormat("(%lld)", *(int64*)(src + sizeof(DataMessageEntryHeader)));
1707 default:
1708 return "";
1709 }
1710}
1711
1712const char* DataMessage::getString(const char* key) {
1713 bool success = false;
1714 return getString(key, success);
1715}
1716
1717const char* DataMessage::getString(const char* key, uint32 &size) {
1718 bool success = false;
1719 return getString(key, size, success);
1720}
1721
1722uint64 DataMessage::getTime(const char* key) {
1723 bool success = false;
1724 return getTime(key, success);
1725}
1726
1727bool DataMessage::getInt(const char* key, int64& value) {
1728 bool success = false;
1729 value = getInt(key, success);
1730 return success;
1731}
1732
1733bool DataMessage::getDouble(const char* key, double& value) {
1734 bool success = false;
1735 value = getDouble(key, success);
1736 return success;
1737}
1738
1739bool DataMessage::getFloat(const char* key, float64& value) {
1740 bool success = false;
1741 value = getFloat(key, success);
1742 return success;
1743}
1744
1745const char* DataMessage::getData(const char* key, uint32& size) {
1746 bool success = false;
1747 return getData(key, size, success);
1748}
1749
1750char* DataMessage::getDataCopy(const char* key, uint32& size) {
1751 bool success = false;
1752 return getDataCopy(key, size, success);
1753}
1754
1756 bool success = false;
1757 return getAttachedMessageCopy(key, success);
1758}
1759
1760
1761
1762
1763
1764// ################# User with success flag #################
1765
1766const char* DataMessage::getString(const char* key, bool& success) {
1767 success = false;
1768 char* src = findEntry(key);
1769 if (src == NULL)
1770 return NULL;
1771
1772 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
1773 success = true;
1774 return src+sizeof(DataMessageEntryHeader);
1775 }
1776
1777 return NULL;
1778}
1779
1780const char* DataMessage::getString(const char* key, uint32 &size, bool& success) {
1781 size = 0;
1782 success = false;
1783 char* src = findEntry(key);
1784 if (src == NULL)
1785 return NULL;
1786
1787 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
1788 success = true;
1789 size = ((DataMessageEntryHeader*)src)->size - sizeof(DataMessageEntryHeader) - 1;
1790 return src+sizeof(DataMessageEntryHeader);
1791 }
1792
1793 return NULL;
1794}
1795
1796uint32 DataMessage::getContentType(const char* key) {
1797 char* src = findEntry(key);
1798 if (src == NULL) return 0;
1799 return ((DataMessageEntryHeader*)src)->cid;
1800}
1801
1802uint32 DataMessage::getContentSize(const char* key) {
1803 char* src = findEntry(key);
1804 if (src == NULL) return 0;
1805 return ((DataMessageEntryHeader*)src)->size;
1806}
1807
1808
1809uint64 DataMessage::getTime(const char* key, bool& success) {
1810 success = false;
1811 char* src = findEntry(key);
1812 if (src == NULL) return 0;
1813
1814 if (((DataMessageEntryHeader*)src)->cid == TIMEID) {
1815 success = true;
1816 return *(uint64*)(src+sizeof(DataMessageEntryHeader));
1817 }
1818
1819 return 0;
1820}
1821
1822int64 DataMessage::getInt(const char* key, bool& success) {
1823 success = false;
1824 char* src = findEntry(key);
1825 if (src == NULL) return 0;
1826
1827 if (((DataMessageEntryHeader*)src)->cid == INTID) {
1828 success = true;
1829 return *(int64*)(src+sizeof(DataMessageEntryHeader));
1830 }
1831 return 0;
1832}
1833
1834int64 DataMessage::getInt(const char* key) {
1835 bool success = false;
1836 return getInt(key, success);
1837}
1838
1839double DataMessage::getDouble(const char* key, bool& success) {
1840 success = false;
1841 char* src = findEntry(key);
1842 if (src == NULL) return false;
1843
1844 if (((DataMessageEntryHeader*)src)->cid == DOUBLEID) {
1845 success = true;
1846 return *(float64*)(src + sizeof(DataMessageEntryHeader));
1847 }
1848 return false;
1849}
1850
1851float64 DataMessage::getFloat(const char* key, bool& success) {
1852 success = false;
1853 char* src = findEntry(key);
1854 if (src == NULL) return false;
1855
1856 if (((DataMessageEntryHeader*)src)->cid == DOUBLEID) {
1857 success = true;
1858 return *(float64*)(src + sizeof(DataMessageEntryHeader));
1859 }
1860 return false;
1861}
1862
1863double DataMessage::getDouble(const char* key) {
1864 bool success = false;
1865 return getDouble(key, success);
1866}
1867
1868double DataMessage::getFloat(const char* key) {
1869 bool success = false;
1870 return getFloat(key, success);
1871}
1872
1873const char* DataMessage::getData(const char* key, uint32& size, bool& success) {
1874 success = false;
1875 char* src = findEntry(key);
1876 if (src == NULL) return NULL;
1877
1878 if (((DataMessageEntryHeader*)src)->cid == CHARDATAID) {
1879 success = true;
1880 size = ((DataMessageEntryHeader*)src)->size - sizeof(DataMessageEntryHeader);
1881 return src+sizeof(DataMessageEntryHeader);
1882 }
1883
1884 return NULL;
1885}
1886
1887char* DataMessage::getDataCopy(const char* key, uint32& size, bool& success) {
1888 success = false;
1889 char* src = findEntry(key);
1890 if (src == NULL) return NULL;
1891
1892 if (((DataMessageEntryHeader*)src)->cid == CHARDATAID) {
1893 success = true;
1894 size = ((DataMessageEntryHeader*)src)->size - sizeof(DataMessageEntryHeader);
1895 char* data = new char[size];
1896 memcpy(data, src+sizeof(DataMessageEntryHeader), size);
1897 return data;
1898 }
1899
1900 return NULL;
1901}
1902
1903DataMessage* DataMessage::getAttachedMessageCopy(const char* key, bool& success) {
1904 success = false;
1905 char* src = findEntry(key);
1906 if (src == NULL) return NULL;
1907
1908 if (((DataMessageEntryHeader*)src)->cid == DATAMESSAGEID) {
1909 success = true;
1910 return new DataMessage(src+sizeof(DataMessageEntryHeader), true);
1911 }
1912 return NULL;
1913}
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927bool DataMessage::setString(const char* key, const char* value) {
1928 if (!key || !value)
1929 return false;
1930 return setRawData(key, value, (uint32)strlen(value)+1, CONSTCHARID);
1931}
1932
1933bool DataMessage::setTime(const char* key, uint64 value) {
1934 return setRawData(key, (char*)&value, sizeof(uint64), TIMEID);
1935}
1936
1937bool DataMessage::setInt(const char* key, int64 value) {
1938 return setRawData(key, (char*)&value, sizeof(int64), INTID);
1939}
1940
1941bool DataMessage::setDouble(const char* key, double value) {
1942 float64 val = value;
1943 return setRawData(key, (char*)&val, sizeof(float64), DOUBLEID);
1944}
1945
1946bool DataMessage::setFloat(const char* key, float64 value) {
1947 return setRawData(key, (char*)&value, sizeof(float64), DOUBLEID);
1948}
1949
1950bool DataMessage::setData(const char* key, const char* value, uint32 size) {
1951 return setRawData(key, value, size, CHARDATAID);
1952}
1953
1955 if (!msg->isValid())
1956 return false;
1957 return setRawData(key, (char*)msg->data, msg->data->size, DATAMESSAGEID);
1958}
1959
1960bool DataMessage::setRawData(const char* key, const char* value, uint32 size, uint32 datatype) {
1962 char* src = findEntry(key);
1963
1964 uint32 spaceReq = sizeof(DataMessageEntryHeader) + size;
1965 uint32 fullSpaceReq = 2 * sizeof(DataMessageEntryHeader) + (uint32)strlen(key) + 1 + size;
1966
1967 if (src != NULL) { // overwrite?
1968 if (((DataMessageEntryHeader*)src)->size >= spaceReq) {
1969 ((DataMessageEntryHeader*)src)->cid = datatype;
1970 // Record the new value's own size, so a smaller value (or one of another type)
1971 // written into a larger slot reads back with its correct size. The slot itself
1972 // (key header size) is unchanged, so entry iteration is unaffected.
1973 ((DataMessageEntryHeader*)src)->size = spaceReq;
1974 memcpy(src+sizeof(DataMessageEntryHeader), value, size);
1975 //data->userCount++;
1976 //data->userSize += fullSpaceReq;
1978 return true;
1979 }
1980 else {
1981 removeEntry(key);
1982 // and act as if not found, ie. continue
1983 }
1984 }
1985
1986 src = findSpace(fullSpaceReq);
1987 if (src == NULL) {
1988 // Space not found, create a new one
1989 // if (!ownsData) return false;
1990
1991 uint32 oldSize = ((DataMessageHeader*)data)->size;
1992 data = (DataMessageHeader*)realloc(data, oldSize + fullSpaceReq);
1993 data->size = oldSize + fullSpaceReq;
1995
1996 src = (char*)data + oldSize;
1997 ((DataMessageEntryHeader*)src)->size = fullSpaceReq;
1998 }
1999
2000 ((DataMessageEntryHeader*)src)->cid = CONSTCHARID;
2001 utils::strcpyavail(src+sizeof(DataMessageEntryHeader), key, (uint32)strlen(key)+1, true);
2002
2003 src += sizeof(DataMessageEntryHeader) + strlen(key) + 1;
2004 ((DataMessageEntryHeader*)src)->cid = datatype;
2005 ((DataMessageEntryHeader*)src)->size = sizeof(DataMessageEntryHeader) + size;
2006 memcpy(src+sizeof(DataMessageEntryHeader), value, size);
2007 data->userCount++;
2008 data->userSize += fullSpaceReq;
2009
2010 // findEntry(key) above cached "not found" for this key. If the entry was written into a
2011 // reused (previously deleted) slot, nothing else clears that cache, and the key would read
2012 // back as missing (delete + re-add bug, fixed 26 Oct 2026). Always invalidate.
2014 return true;
2015}
2016
2017
2018
2019
2020
2021
2022
2023// Arrays
2024
2025bool DataMessage::isArrayContent(const char* key) {
2026 uint32 size;
2027 char* keyStart = utils::StringFormat(size, "%s_-|", key);
2028 if (data->cid != DATAMESSAGEID) return false;
2029 char* src = (char*)data + sizeof(DataMessageHeader);
2030 char* srcEnd = (char*)data + data->size;
2031 while (src < srcEnd) {
2032 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2033 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false) &&
2034 utils::TextEndsWith(src + sizeof(DataMessageEntryHeader), "|-_", false))
2035 return true;
2036 }
2037 src += ((DataMessageEntryHeader*)src)->size;
2038 }
2039 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2040 return false;
2041}
2042
2043bool DataMessage::isMapContent(const char* key) {
2044 uint32 size;
2045 char* keyStart = utils::StringFormat(size, "%s_-[", key);
2046 if (data->cid != DATAMESSAGEID) return false;
2047 char* src = (char*)data + sizeof(DataMessageHeader);
2048 char* srcEnd = (char*)data + data->size;
2049 while (src < srcEnd) {
2050 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2051 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false) &&
2052 utils::TextEndsWith(src + sizeof(DataMessageEntryHeader), "]-_", false) )
2053 return true;
2054 }
2055 src += ((DataMessageEntryHeader*)src)->size;
2056 }
2057 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2058 return false;
2059}
2060
2062 if (data->cid != DATAMESSAGEID) return NONE;
2063 uint32 size;
2064 // StringFormat returns vasprintf-malloc'd memory: release with free(), never
2065 // delete[] (ASan alloc-dealloc-mismatch), and on EVERY return path - the early
2066 // SINGLE/ARRAY/MAP returns used to leak both strings.
2067 char* arrayKeyStart = utils::StringFormat(size, "%s_-|", key);
2068 char* mapKeyStart = utils::StringFormat(size, "%s_-[", key);
2069 KeyType type = NONE;
2070 char* src = (char*)data + sizeof(DataMessageHeader);
2071 char* srcEnd = (char*)data + data->size;
2072 while (src < srcEnd) {
2073 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2074 if (stricmp(key, src + sizeof(DataMessageEntryHeader)) == 0) {
2075 type = SINGLE; break; }
2076 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), arrayKeyStart, false) &&
2077 utils::TextEndsWith(src + sizeof(DataMessageEntryHeader), "|-_", false)) {
2078 type = ARRAY; break; }
2079 else if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), mapKeyStart, false) &&
2080 utils::TextEndsWith(src + sizeof(DataMessageEntryHeader), "]-_", false)) {
2081 type = MAP; break; }
2082 }
2083 src += ((DataMessageEntryHeader*)src)->size;
2084 }
2085 free(arrayKeyStart);
2086 free(mapKeyStart);
2087 return type;
2088}
2089
2090
2091
2092
2093std::map<std::string, DataMessage::KeyType> DataMessage::getAllKeys() {
2094 std::map<std::string, KeyType> keys;
2095 if (data->cid != DATAMESSAGEID) return keys;
2096 char* src = (char*)data + sizeof(DataMessageHeader);
2097 char* srcEnd = (char*)data + data->size;
2098 while (src < srcEnd) {
2099 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2100 std::string k = src + sizeof(DataMessageEntryHeader);
2101 size_t p;
2102 if (k.size() > 6 && k.compare(k.size() - 3, 3, "|-_") == 0 && (p = k.find("_-|")) != std::string::npos)
2103 keys[k.substr(0, p)] = ARRAY;
2104 else if (k.size() > 6 && k.compare(k.size() - 3, 3, "]-_") == 0 && (p = k.find("_-[")) != std::string::npos)
2105 keys[k.substr(0, p)] = MAP;
2106 else
2107 keys[k] = SINGLE;
2108 }
2109 if (((DataMessageEntryHeader*)src)->size == 0) break; // corrupt block guard
2110 src += ((DataMessageEntryHeader*)src)->size;
2111 }
2112 return keys;
2113}
2114
2116 if (data->cid != DATAMESSAGEID) return 0;
2117 std::string arrayStart = std::string(key) + "_-|";
2118 std::string mapStart = std::string(key) + "_-[";
2119 uint32 count = 0;
2120 char* src = (char*)data + sizeof(DataMessageHeader);
2121 char* srcEnd = (char*)data + data->size;
2122 while (src < srcEnd) {
2123 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2124 const char* k = src + sizeof(DataMessageEntryHeader);
2125 if ((utils::TextStartsWith(k, arrayStart.c_str(), false) && utils::TextEndsWith(k, "|-_", false)) ||
2126 (utils::TextStartsWith(k, mapStart.c_str(), false) && utils::TextEndsWith(k, "]-_", false))) {
2127 ((DataMessageEntryHeader*)src)->cid = UNUSEDID;
2128 data->userCount--;
2129 data->userSize -= ((DataMessageEntryHeader*)src)->size;
2130 count++;
2131 }
2132 }
2133 if (((DataMessageEntryHeader*)src)->size == 0) break;
2134 src += ((DataMessageEntryHeader*)src)->size;
2135 }
2136 if (count) invalidateFindKeyCache();
2137 return count;
2138}
2139
2140bool DataMessage::hasKey(const char* key) {
2141 return (findEntry(key) != NULL);
2142}
2143
2144bool DataMessage::hasString(const char* key) {
2145 char* src = findEntry(key);
2146 if (src == NULL) return 0;
2147 return (((DataMessageEntryHeader*)src)->cid == CONSTCHARID);
2148}
2149
2150bool DataMessage::hasInteger(const char* key) {
2151 char* src = findEntry(key);
2152 if (src == NULL) return 0;
2153 return (((DataMessageEntryHeader*)src)->cid == INTID);
2154}
2155
2156bool DataMessage::hasFloat(const char* key) {
2157 char* src = findEntry(key);
2158 if (src == NULL) return 0;
2159 return (((DataMessageEntryHeader*)src)->cid == DOUBLEID);
2160}
2161
2162bool DataMessage::hasTime(const char* key) {
2163 char* src = findEntry(key);
2164 if (src == NULL) return 0;
2165 return (((DataMessageEntryHeader*)src)->cid == TIMEID);
2166}
2167
2168bool DataMessage::hasData(const char* key) {
2169 char* src = findEntry(key);
2170 if (src == NULL) return 0;
2171 return (((DataMessageEntryHeader*)src)->cid == CHARDATAID);
2172}
2173
2174bool DataMessage::hasMessage(const char* key) {
2175 char* src = findEntry(key);
2176 if (src == NULL) return 0;
2177 return (((DataMessageEntryHeader*)src)->cid == DATAMESSAGEID);
2178}
2179
2180
2181bool DataMessage::isSingleValue(const char* key) {
2182 return (findEntry(key) != NULL);
2183}
2184
2185uint32 DataMessage::getContentType(int64 i, const char* key) {
2186 char buf[256];
2187 int n = snprintf(buf, sizeof(buf), "%s_-|%lld|-_", key, i);
2188 if (n < 0 || (size_t)n >= sizeof(buf))
2189 return getContentType(utils::StringFormat("%s_-|%lld|-_", key, i).c_str());
2190 return getContentType(buf);
2191}
2192
2193uint32 DataMessage::getContentSize(int64 i, const char* key) {
2194 return getContentSize(utils::StringFormat("%s_-|%lld|-_", key, i).c_str());
2195}
2196
2197uint64 DataMessage::getTime(int64 i, const char* key) {
2198 return getTime(utils::StringFormat("%s_-|%lld|-_", key, i).c_str());
2199}
2200
2201std::string DataMessage::getAsString(int64 i, const char* key) {
2202 return getAsString(utils::StringFormat("%s_-|%lld|-_", key, i).c_str());
2203}
2204
2205const char* DataMessage::getString(int64 i, const char* key) {
2206 return getString(utils::StringFormat("%s_-|%lld|-_", key, i).c_str());
2207}
2208
2209const char* DataMessage::getString(int64 i, const char* key, uint32 &size) {
2210 return getString(utils::StringFormat("%s_-|%lld|-_", key, i).c_str());
2211}
2212
2213bool DataMessage::getInt(int64 i, const char* key, int64& value) {
2214 return getInt(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), value);
2215}
2216
2217bool DataMessage::getDouble(int64 i, const char* key, double& value) {
2218 return getDouble(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), value);
2219}
2220
2221bool DataMessage::getFloat(int64 i, const char* key, float64& value) {
2222 return getFloat(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), value);
2223}
2224
2225int64 DataMessage::getInt(int64 i, const char* key, bool& success) {
2226 return getInt(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), success);
2227}
2228
2229double DataMessage::getDouble(int64 i, const char* key, bool& success) {
2230 return getDouble(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), success);
2231}
2232
2233float64 DataMessage::getFloat(int64 i, const char* key, bool& success) {
2234 return getFloat(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), success);
2235}
2236
2237int64 DataMessage::getInt(int64 i, const char* key) {
2238 bool success;
2239 return getInt(i, key, success);
2240}
2241
2242double DataMessage::getDouble(int64 i, const char* key) {
2243 bool success;
2244 return getDouble(i, key, success);
2245}
2246
2247float64 DataMessage::getFloat(int64 i, const char* key) {
2248 bool success;
2249 return getFloat(i, key, success);
2250}
2251
2252int64 DataMessage::getInt(const char* idx, const char* key) {
2253 bool success;
2254 return getInt(idx, key, success);
2255}
2256
2257double DataMessage::getDouble(const char* idx, const char* key) {
2258 bool success;
2259 return getDouble(idx, key, success);
2260}
2261
2262float64 DataMessage::getFloat(const char* idx, const char* key) {
2263 bool success;
2264 return getFloat(idx, key, success);
2265}
2266
2267
2268const char* DataMessage::getData(int64 i, const char* key, uint32& size) {
2269 return getData(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), size);
2270}
2271
2272char* DataMessage::getDataCopy(int64 i, const char* key, uint32& size) {
2273 return getDataCopy(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), size);
2274}
2275
2277 return getAttachedMessageCopy(utils::StringFormat("%s_-|%lld|-_", key, i).c_str());
2278}
2279
2280
2281bool DataMessage::setTime(int64 i, const char* key, uint64 value) {
2282 return setTime(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), value);
2283}
2284
2285bool DataMessage::setString(int64 i, const char* key, const char* value) {
2286 return setString(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), value);
2287}
2288
2289bool DataMessage::setInt(int64 i, const char* key, int64 value) {
2290 return setInt(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), value);
2291}
2292
2293bool DataMessage::setDouble(int64 i, const char* key, double value) {
2294 return setDouble(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), value);
2295}
2296
2297bool DataMessage::setFloat(int64 i, const char* key, float64 value) {
2298 return setFloat(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), value);
2299}
2300
2301bool DataMessage::setData(int64 i, const char* key, const char* value, uint32 size) {
2302 return setData(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), value, size);
2303}
2304
2305bool DataMessage::setAttachedMessage(int64 i, const char* key, DataMessage* msg) {
2306 return setAttachedMessage(utils::StringFormat("%s_-|%lld|-_", key, i).c_str(), msg);
2307}
2308
2309
2310// Maps
2311uint32 DataMessage::getContentType(const char* idx, const char* key) {
2312 char buf[256];
2313 int n = snprintf(buf, sizeof(buf), "%s_-[%s]-_", key, idx);
2314 if (n < 0 || (size_t)n >= sizeof(buf))
2315 return getContentType(utils::StringFormat("%s_-[%s]-_", key, idx).c_str());
2316 return getContentType(buf);
2317}
2318
2319uint32 DataMessage::getContentSize(const char* idx, const char* key) {
2320 return getContentSize(utils::StringFormat("%s_-[%s]-_", key, idx).c_str());
2321}
2322
2323uint64 DataMessage::getTime(const char* idx, const char* key) {
2324 return getTime(utils::StringFormat("%s_-[%s]-_", key, idx).c_str());
2325}
2326
2327std::string DataMessage::getAsString(const char* idx, const char* key) {
2328 return getAsString(utils::StringFormat("%s_-[%s]-_", key, idx).c_str());
2329}
2330
2331const char* DataMessage::getString(const char* idx, const char* key) {
2332 return getString(utils::StringFormat("%s_-[%s]-_", key, idx).c_str());
2333}
2334
2335const char* DataMessage::getString(const char* idx, const char* key, uint32 &size) {
2336 return getString(utils::StringFormat("%s_-[%s]-_", key, idx).c_str());
2337}
2338
2339bool DataMessage::getInt(const char* idx, const char* key, int64& value) {
2340 return getInt(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), value);
2341}
2342
2343bool DataMessage::getDouble(const char* idx, const char* key, double& value) {
2344 return getDouble(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), value);
2345}
2346
2347bool DataMessage::getFloat(const char* idx, const char* key, float64& value) {
2348 return getFloat(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), value);
2349}
2350
2351int64 DataMessage::getInt(const char* idx, const char* key, bool& success) {
2352 return getInt(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), success);
2353}
2354
2355double DataMessage::getDouble(const char* idx, const char* key, bool& success) {
2356 return getDouble(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), success);
2357}
2358
2359float64 DataMessage::getFloat(const char* idx, const char* key, bool& success) {
2360 return getFloat(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), success);
2361}
2362
2363const char* DataMessage::getData(const char* idx, const char* key, uint32& size) {
2364 return getData(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), size);
2365}
2366
2367char* DataMessage::getDataCopy(const char* idx, const char* key, uint32& size) {
2368 return getDataCopy(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), size);
2369}
2370
2371DataMessage* DataMessage::getAttachedMessageCopy(const char* idx, const char* key) {
2372 return getAttachedMessageCopy(utils::StringFormat("%s_-[%s]-_", key, idx).c_str());
2373}
2374
2375
2376bool DataMessage::setTime(const char* idx, const char* key, uint64 value) {
2377 return setTime(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), value);
2378}
2379
2380bool DataMessage::setString(const char* idx, const char* key, const char* value) {
2381 return setString(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), value);
2382}
2383
2384bool DataMessage::setInt(const char* idx, const char* key, int64 value) {
2385 return setInt(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), value);
2386}
2387
2388bool DataMessage::setDouble(const char* idx, const char* key, double value) {
2389 return setDouble(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), value);
2390}
2391
2392bool DataMessage::setFloat(const char* idx, const char* key, float64 value) {
2393 return setFloat(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), value);
2394}
2395
2396bool DataMessage::setData(const char* idx, const char* key, const char* value, uint32 size) {
2397 return setData(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), value, size);
2398}
2399
2400bool DataMessage::setAttachedMessage(const char* idx, const char* key, DataMessage* msg) {
2401 return setAttachedMessage(utils::StringFormat("%s_-[%s]-_", key, idx).c_str(), msg);
2402}
2403
2404
2405
2406
2407
2408
2409
2410
2411std::map<int64, uint64> DataMessage::getTimeArray(const char* key) {
2412 std::map<int64, uint64> map;
2413 int64 i;
2414 uint32 size;
2415 char* keyStart = utils::StringFormat(size, "%s_-|", key);
2416 if (data->cid != DATAMESSAGEID) return map;
2417 char* entry;
2418 char* src = (char*)data + sizeof(DataMessageHeader);
2419 char* srcEnd = (char*)data + data->size;
2420 while (src < srcEnd) {
2421 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2422 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2423 if (((DataMessageEntryHeader*)entry)->cid == TIMEID) {
2424 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
2425 //i = utils::Ascii2Int64(std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3).c_str());
2426 i = utils::Ascii2Int64(src + sizeof(DataMessageEntryHeader) + size);
2427 map[i] = *(uint64*)(entry + sizeof(DataMessageEntryHeader));
2428 }
2429 }
2430 }
2431 src += ((DataMessageEntryHeader*)src)->size;
2432 }
2433 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2434 return map;
2435}
2436
2437std::map<int64, int64> DataMessage::getAsIntArray(const char* key) {
2438 std::map<int64, int64> map;
2439 int64 i;
2440 uint32 size;
2441 char* keyStart = utils::StringFormat(size, "%s_-|", key);
2442 if (data->cid != DATAMESSAGEID) return map;
2443 char* entry;
2444 char* src = (char*)data + sizeof(DataMessageHeader);
2445 char* srcEnd = (char*)data + data->size;
2446 while (src < srcEnd) {
2447 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2448 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
2449 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2450
2451 //i = utils::Ascii2Int64(std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3).c_str());
2452 i = utils::Ascii2Int64(src + sizeof(DataMessageEntryHeader) + size);
2453 switch (((DataMessageEntryHeader*)entry)->cid) {
2454 case TIMEID:
2455 map[i] = (int64)(*(uint64*)(entry + sizeof(DataMessageEntryHeader)));
2456 break;
2457 case CONSTCHARID:
2458 map[i] = utils::Ascii2Int64(entry + sizeof(DataMessageEntryHeader));
2459 break;
2460 case CHARDATAID:
2461 map[i] = (int64)((DataMessageEntryHeader*)entry)->size;
2462 break;
2463 case INTID:
2464 map[i] = *(int64*)(entry + sizeof(DataMessageEntryHeader));
2465 break;
2466 case DOUBLEID:
2467 map[i] = (int64)(*(float64*)(entry + sizeof(DataMessageEntryHeader)));
2468 break;
2469 case DATAMESSAGEID:
2470 map[i] = (int64)((DataMessageEntryHeader*)entry)->size;
2471 break;
2472 default:
2473 break;
2474 }
2475 }
2476
2477 }
2478 src += ((DataMessageEntryHeader*)src)->size;
2479 }
2480 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2481 return map;
2482}
2483
2484std::map<int64, float64> DataMessage::getAsFloatArray(const char* key) {
2485 std::map<int64, float64> map;
2486 int64 i;
2487 uint32 size;
2488 char* keyStart = utils::StringFormat(size, "%s_-|", key);
2489 if (data->cid != DATAMESSAGEID) return map;
2490 char* entry;
2491 char* src = (char*)data + sizeof(DataMessageHeader);
2492 char* srcEnd = (char*)data + data->size;
2493 while (src < srcEnd) {
2494 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2495 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
2496 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2497
2498 //i = utils::Ascii2Int64(std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3).c_str());
2499 i = utils::Ascii2Int64(src + sizeof(DataMessageEntryHeader) + size);
2500 switch (((DataMessageEntryHeader*)entry)->cid) {
2501 case TIMEID:
2502 map[i] = (float64)(*(uint64*)(entry + sizeof(DataMessageEntryHeader)));
2503 break;
2504 case CONSTCHARID:
2505 map[i] = utils::Ascii2Float64(entry + sizeof(DataMessageEntryHeader));
2506 break;
2507 case CHARDATAID:
2508 map[i] = (float64)((DataMessageEntryHeader*)entry)->size;
2509 break;
2510 case INTID:
2511 map[i] = (float64)(*(int64*)(entry + sizeof(DataMessageEntryHeader)));
2512 break;
2513 case DOUBLEID:
2514 map[i] = *(float64*)(entry + sizeof(DataMessageEntryHeader));
2515 break;
2516 case DATAMESSAGEID:
2517 map[i] = (float64)((DataMessageEntryHeader*)entry)->size;
2518 break;
2519 default:
2520 break;
2521 }
2522 }
2523
2524 }
2525 src += ((DataMessageEntryHeader*)src)->size;
2526 }
2527 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2528 return map;
2529}
2530
2531std::map<int64, std::string> DataMessage::getAsStringArray(const char* key) {
2532 std::map<int64, std::string> map;
2533 int64 i;
2534 uint32 size;
2535 char* keyStart = utils::StringFormat(size, "%s_-|", key);
2536 if (data->cid != DATAMESSAGEID) return map;
2537 char* entry;
2538 char* src = (char*)data + sizeof(DataMessageHeader);
2539 char* srcEnd = (char*)data + data->size;
2540 while (src < srcEnd) {
2541 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2542 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
2543 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2544
2545 //i = utils::Ascii2Int64(std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3).c_str());
2546 i = utils::Ascii2Int64(src + sizeof(DataMessageEntryHeader) + size);
2547 switch (((DataMessageEntryHeader*)entry)->cid) {
2548 case TIMEID:
2549 map[i] = PrintTimeString(*(uint64*)(entry + sizeof(DataMessageEntryHeader)));
2550 break;
2551 case CONSTCHARID:
2552 map[i] = entry + sizeof(DataMessageEntryHeader);
2553 break;
2554 case CHARDATAID:
2555 map[i] = utils::StringFormat("BinaryData [%ub]", ((DataMessageEntryHeader*)entry)->size);
2556 break;
2557 case INTID:
2558 map[i] = utils::StringFormat("%lld", *(int64*)(entry + sizeof(DataMessageEntryHeader)));
2559 break;
2560 case DOUBLEID:
2561 map[i] = utils::StringFormat("%f", *(float64*)(entry + sizeof(DataMessageEntryHeader)));
2562 break;
2563 case DATAMESSAGEID:
2564 map[i] = utils::StringFormat("DataMessage [%ub]", ((DataMessageEntryHeader*)entry)->size);
2565 break;
2566 default:
2567 break;
2568 }
2569 }
2570
2571 }
2572 src += ((DataMessageEntryHeader*)src)->size;
2573 }
2574 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2575 return map;
2576}
2577
2578std::map<int64, std::string> DataMessage::getStringArray(const char* key) {
2579 std::map<int64, std::string> map;
2580 int64 i;
2581 uint32 size;
2582 char* keyStart = utils::StringFormat(size, "%s_-|", key);
2583 if (data->cid != DATAMESSAGEID) return map;
2584 char* entry;
2585 char* src = (char*)data + sizeof(DataMessageHeader);
2586 char* srcEnd = (char*)data + data->size;
2587 while (src < srcEnd) {
2588 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2589 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2590 if (((DataMessageEntryHeader*)entry)->cid == CONSTCHARID) {
2591 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
2592 //i = utils::Ascii2Int64(std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3).c_str());
2593 i = utils::Ascii2Int64(src + sizeof(DataMessageEntryHeader) + size);
2594 map[i] = entry + sizeof(DataMessageEntryHeader);
2595 }
2596 }
2597 }
2598 src += ((DataMessageEntryHeader*)src)->size;
2599 }
2600 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2601 return map;
2602}
2603
2604
2605std::map<int64, int64> DataMessage::getIntArray(const char* key) {
2606 std::map<int64, int64> map;
2607 int64 i;
2608 uint32 size;
2609 char* keyStart = utils::StringFormat(size, "%s_-|", key);
2610 if (data->cid != DATAMESSAGEID) return map;
2611 char* entry;
2612 char* src = (char*)data + sizeof(DataMessageHeader);
2613 char* srcEnd = (char*)data + data->size;
2614 while (src < srcEnd) {
2615 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2616 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2617 if (((DataMessageEntryHeader*)entry)->cid == INTID) {
2618 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
2619 //i = utils::Ascii2Int64(std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3).c_str());
2620 i = utils::Ascii2Int64(src + sizeof(DataMessageEntryHeader) + size);
2621 map[i] = *(int64*)(entry + sizeof(DataMessageEntryHeader));
2622 }
2623 }
2624 }
2625 src += ((DataMessageEntryHeader*)src)->size;
2626 }
2627 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2628 return map;
2629}
2630
2631std::map<int64, double> DataMessage::getDoubleArray(const char* key) {
2632 std::map<int64, double> map;
2633 int64 i;
2634 uint32 size;
2635 char* keyStart = utils::StringFormat(size, "%s_-|", key);
2636 if (data->cid != DATAMESSAGEID) return map;
2637 char* entry;
2638 char* src = (char*)data + sizeof(DataMessageHeader);
2639 char* srcEnd = (char*)data + data->size;
2640 while (src < srcEnd) {
2641 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2642 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2643 if (((DataMessageEntryHeader*)entry)->cid == DOUBLEID) {
2644 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
2645 //i = utils::Ascii2Int64(std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3).c_str());
2646 i = utils::Ascii2Int64(src + sizeof(DataMessageEntryHeader) + size);
2647 map[i] = *(float64*)(entry + sizeof(DataMessageEntryHeader));
2648 }
2649 }
2650 }
2651 src += ((DataMessageEntryHeader*)src)->size;
2652 }
2653 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2654 return map;
2655}
2656
2657std::map<int64, float64> DataMessage::getFloatArray(const char* key) {
2658 std::map<int64, float64> map;
2659 int64 i;
2660 uint32 size;
2661 char* keyStart = utils::StringFormat(size, "%s_-|", key);
2662 if (data->cid != DATAMESSAGEID) return map;
2663 char* entry;
2664 char* src = (char*)data + sizeof(DataMessageHeader);
2665 char* srcEnd = (char*)data + data->size;
2666 while (src < srcEnd) {
2667 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2668 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2669 if (((DataMessageEntryHeader*)entry)->cid == DOUBLEID) {
2670 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
2671 //i = utils::Ascii2Int64(std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3).c_str());
2672 i = utils::Ascii2Int64(src + sizeof(DataMessageEntryHeader) + size);
2673 map[i] = *(float64*)(entry + sizeof(DataMessageEntryHeader));
2674 }
2675 }
2676 }
2677 src += ((DataMessageEntryHeader*)src)->size;
2678 }
2679 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2680 return map;
2681}
2682
2683std::map<int64, DataMessage*> DataMessage::getAttachedMessageArray(const char* key) {
2684 std::map<int64, DataMessage*> map;
2685 int64 i;
2686 uint32 size;
2687 char* keyStart = utils::StringFormat(size, "%s_-|", key);
2688 if (data->cid != DATAMESSAGEID) return map;
2689 char* entry;
2690 char* src = (char*)data + sizeof(DataMessageHeader);
2691 char* srcEnd = (char*)data + data->size;
2692 while (src < srcEnd) {
2693 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2694 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2695 if (((DataMessageEntryHeader*)entry)->cid == DATAMESSAGEID) {
2696 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
2697 //i = utils::Ascii2Int64(std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3).c_str());
2698 i = utils::Ascii2Int64(src + sizeof(DataMessageEntryHeader) + size);
2699 map[i] = new DataMessage(entry + sizeof(DataMessageEntryHeader), true);
2700 }
2701 }
2702 }
2703 src += ((DataMessageEntryHeader*)src)->size;
2704 }
2705 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2706 return map;
2707}
2708
2709bool DataMessage::setTimeArray(const char* key, std::map<int64, uint64>& map) {
2710 std::map<int64, uint64>::iterator i = map.begin(), e = map.end();
2711 while (i != e) {
2712 if (!setTime(i->first, key, i->second))
2713 return false;
2714 i++;
2715 }
2716 return true;
2717}
2718
2719bool DataMessage::setStringArray(const char* key, std::map<int64, std::string>& map) {
2720 std::map<int64, std::string>::iterator i = map.begin(), e = map.end();
2721 while (i != e) {
2722 if (!setString(i->first, key, i->second.c_str()))
2723 return false;
2724 i++;
2725 }
2726 return true;
2727}
2728
2729bool DataMessage::setIntArray(const char* key, std::map<int64, int64>& map) {
2730 std::map<int64, int64>::iterator i = map.begin(), e = map.end();
2731 while (i != e) {
2732 if (!setInt(i->first, key, i->second))
2733 return false;
2734 i++;
2735 }
2736 return true;
2737}
2738
2739bool DataMessage::setDoubleArray(const char* key, std::map<int64, double>& map) {
2740 std::map<int64, double>::iterator i = map.begin(), e = map.end();
2741 while (i != e) {
2742 if (!setDouble(i->first, key, i->second))
2743 return false;
2744 i++;
2745 }
2746 return true;
2747}
2748
2749bool DataMessage::setFloatArray(const char* key, std::map<int64, float64>& map) {
2750 std::map<int64, float64>::iterator i = map.begin(), e = map.end();
2751 while (i != e) {
2752 if (!setFloat(i->first, key, i->second))
2753 return false;
2754 i++;
2755 }
2756 return true;
2757}
2758
2759bool DataMessage::setAttachedMessageArray(const char* key, std::map<int64, DataMessage*>& map) {
2760 std::map<int64, DataMessage*>::iterator i = map.begin(), e = map.end();
2761 while (i != e) {
2762 if (!setAttachedMessage(i->first, key, i->second))
2763 return false;
2764 i++;
2765 }
2766 return true;
2767}
2768
2769
2770
2771
2772
2773
2774
2775
2776uint32 DataMessage::getArraySize(const char* key) {
2777 uint32 c = 0;
2778 uint32 size;
2779 char* keyStart = utils::StringFormat(size, "%s_-|", key);
2780 if (data->cid != DATAMESSAGEID) return c;
2781 char* entry;
2782 char* src = (char*)data + sizeof(DataMessageHeader);
2783 char* srcEnd = (char*)data + data->size;
2784 while (src < srcEnd) {
2785 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2786 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2787 if (((DataMessageEntryHeader*)entry)->cid == CONSTCHARID) {
2788 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false))
2789 c++;
2790 }
2791 }
2792 src += ((DataMessageEntryHeader*)src)->size;
2793 }
2794 // free(), not delete[]: StringFormat returns a vasprintf/malloc'd buffer
2795 // (Utils.cpp:7644), so delete[] is an alloc-dealloc mismatch - undefined
2796 // behaviour, reported by ASan.
2797 free(keyStart);
2798 return c;
2799}
2800
2801uint32 DataMessage::getArrayCount(const char* key) {
2802 return getArraySize(key);
2803}
2804
2805uint32 DataMessage::getMapSize(const char* key) {
2806 uint32 c = 0;
2807 uint32 size;
2808 char* keyStart = utils::StringFormat(size, "%s_-[", key);
2809 if (data->cid != DATAMESSAGEID) return c;
2810 char* entry;
2811 char* src = (char*)data + sizeof(DataMessageHeader);
2812 char* srcEnd = (char*)data + data->size;
2813 while (src < srcEnd) {
2814 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2815 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2816 if (((DataMessageEntryHeader*)entry)->cid == CONSTCHARID) {
2817 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false))
2818 c++;
2819 }
2820 }
2821 src += ((DataMessageEntryHeader*)src)->size;
2822 }
2823 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2824 return c;
2825}
2826
2827uint32 DataMessage::getMapCount(const char* key) {
2828 return getMapSize(key);
2829}
2830
2831
2832
2833
2834
2835
2836
2837std::map<std::string, uint64> DataMessage::getTimeMap(const char* key) {
2838 std::map<std::string, uint64> map;
2839 std::string str;
2840 uint32 size;
2841 char* keyStart = utils::StringFormat(size, "%s_-[", key);
2842 if (data->cid != DATAMESSAGEID) return map;
2843 char* entry;
2844 char* src = (char*)data + sizeof(DataMessageHeader);
2845 char* srcEnd = (char*)data + data->size;
2846 while (src < srcEnd) {
2847 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2848 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2849 if (((DataMessageEntryHeader*)entry)->cid == TIMEID) {
2850 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
2851 str = std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3);
2852 map[str] = *(uint64*)(entry + sizeof(DataMessageEntryHeader));
2853 }
2854 }
2855 }
2856 src += ((DataMessageEntryHeader*)src)->size;
2857 }
2858 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2859 return map;
2860}
2861
2862std::map<std::string, int64> DataMessage::getAsIntMap(const char* key) {
2863 std::map<std::string, int64> map;
2864 std::string str;
2865 uint32 size;
2866 char* keyStart = utils::StringFormat(size, "%s_-[", key);
2867 if (data->cid != DATAMESSAGEID) return map;
2868 char* entry;
2869 char* src = (char*)data + sizeof(DataMessageHeader);
2870 char* srcEnd = (char*)data + data->size;
2871 while (src < srcEnd) {
2872 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2873 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
2874 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2875 str = std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3).c_str();
2876 switch (((DataMessageEntryHeader*)entry)->cid) {
2877 case TIMEID:
2878 map[str] = (int64)(*(uint64*)(entry + sizeof(DataMessageEntryHeader)));
2879 break;
2880 case CONSTCHARID:
2881 map[str] = utils::Ascii2Int64(entry + sizeof(DataMessageEntryHeader));
2882 break;
2883 case CHARDATAID:
2884 map[str] = (int64)((DataMessageEntryHeader*)entry)->size;
2885 break;
2886 case INTID:
2887 map[str] = *(int64*)(entry + sizeof(DataMessageEntryHeader));
2888 break;
2889 case DOUBLEID:
2890 map[str] = (int64)(*(float64*)(entry + sizeof(DataMessageEntryHeader)));
2891 break;
2892 case DATAMESSAGEID:
2893 map[str] = (int64)((DataMessageEntryHeader*)entry)->size;
2894 break;
2895 default:
2896 break;
2897 }
2898 }
2899 }
2900 src += ((DataMessageEntryHeader*)src)->size;
2901 }
2902 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2903 return map;
2904}
2905
2906std::map<std::string, float64> DataMessage::getAsFloatMap(const char* key) {
2907 std::map<std::string, float64> map;
2908 std::string str;
2909 uint32 size;
2910 char* keyStart = utils::StringFormat(size, "%s_-[", key);
2911 if (data->cid != DATAMESSAGEID) return map;
2912 char* entry;
2913 char* src = (char*)data + sizeof(DataMessageHeader);
2914 char* srcEnd = (char*)data + data->size;
2915 while (src < srcEnd) {
2916 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2917 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
2918 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2919 str = std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3).c_str();
2920 switch (((DataMessageEntryHeader*)entry)->cid) {
2921 case TIMEID:
2922 map[str] = (float64)(*(uint64*)(entry + sizeof(DataMessageEntryHeader)));
2923 break;
2924 case CONSTCHARID:
2925 map[str] = utils::Ascii2Float64(entry + sizeof(DataMessageEntryHeader));
2926 break;
2927 case CHARDATAID:
2928 map[str] = (float64)((DataMessageEntryHeader*)entry)->size;
2929 break;
2930 case INTID:
2931 map[str] = (float64)(*(int64*)(entry + sizeof(DataMessageEntryHeader)));
2932 break;
2933 case DOUBLEID:
2934 map[str] = *(float64*)(entry + sizeof(DataMessageEntryHeader));
2935 break;
2936 case DATAMESSAGEID:
2937 map[str] = (float64)((DataMessageEntryHeader*)entry)->size;
2938 break;
2939 default:
2940 break;
2941 }
2942 }
2943 }
2944 src += ((DataMessageEntryHeader*)src)->size;
2945 }
2946 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2947 return map;
2948}
2949
2950std::map<std::string, std::string> DataMessage::getAsStringMap(const char* key) {
2951 std::map<std::string, std::string> map;
2952 std::string str;
2953 uint32 size;
2954 char* keyStart = utils::StringFormat(size, "%s_-[", key);
2955 if (data->cid != DATAMESSAGEID) return map;
2956 char* entry;
2957 char* src = (char*)data + sizeof(DataMessageHeader);
2958 char* srcEnd = (char*)data + data->size;
2959 while (src < srcEnd) {
2960 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
2961 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
2962 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
2963 str = std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3).c_str();
2964 switch (((DataMessageEntryHeader*)entry)->cid) {
2965 case TIMEID:
2966 map[str] = PrintTimeString(*(uint64*)(entry + sizeof(DataMessageEntryHeader)));
2967 break;
2968 case CONSTCHARID:
2969 map[str] = entry + sizeof(DataMessageEntryHeader);
2970 break;
2971 case CHARDATAID:
2972 map[str] = utils::StringFormat("BinaryData [%ub]", ((DataMessageEntryHeader*)entry)->size);
2973 break;
2974 case INTID:
2975 map[str] = utils::StringFormat("%lld", *(int64*)(entry + sizeof(DataMessageEntryHeader)));
2976 break;
2977 case DOUBLEID:
2978 map[str] = utils::StringFormat("%f", *(float64*)(entry + sizeof(DataMessageEntryHeader)));
2979 break;
2980 case DATAMESSAGEID:
2981 map[str] = utils::StringFormat("DataMessage [%ub]", ((DataMessageEntryHeader*)entry)->size);
2982 break;
2983 default:
2984 break;
2985 }
2986 }
2987 }
2988 src += ((DataMessageEntryHeader*)src)->size;
2989 }
2990 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
2991 return map;
2992}
2993
2994std::map<std::string, std::string> DataMessage::getStringMap(const char* key) {
2995 std::map<std::string, std::string> map;
2996 std::string str;
2997 uint32 size;
2998 char* keyStart = utils::StringFormat(size, "%s_-[", key);
2999 if (data->cid != DATAMESSAGEID) return map;
3000 char* entry;
3001 char* src = (char*)data + sizeof(DataMessageHeader);
3002 char* srcEnd = (char*)data + data->size;
3003 while (src < srcEnd) {
3004 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
3005 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
3006 if (((DataMessageEntryHeader*)entry)->cid == CONSTCHARID) {
3007 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
3008 str = std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3);
3009 map[str] = entry + sizeof(DataMessageEntryHeader);
3010 }
3011 }
3012 }
3013 src += ((DataMessageEntryHeader*)src)->size;
3014 }
3015 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
3016 return map;
3017}
3018
3019std::map<std::string, int64> DataMessage::getIntMap(const char* key) {
3020 std::map<std::string, int64> map;
3021 std::string str;
3022 uint32 size;
3023 char* keyStart = utils::StringFormat(size, "%s_-[", key);
3024 if (data->cid != DATAMESSAGEID) return map;
3025 char* entry;
3026 char* src = (char*)data + sizeof(DataMessageHeader);
3027 char* srcEnd = (char*)data + data->size;
3028 while (src < srcEnd) {
3029 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
3030 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
3031 if (((DataMessageEntryHeader*)entry)->cid == INTID) {
3032 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
3033 str = std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3);
3034 map[str] = *(int64*)(entry + sizeof(DataMessageEntryHeader));
3035 }
3036 }
3037 }
3038 src += ((DataMessageEntryHeader*)src)->size;
3039 }
3040 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
3041 return map;
3042}
3043
3044std::map<std::string, double> DataMessage::getDoubleMap(const char* key) {
3045 std::map<std::string, double> map;
3046 std::string str;
3047 uint32 size;
3048 char* keyStart = utils::StringFormat(size, "%s_-[", key);
3049 if (data->cid != DATAMESSAGEID) return map;
3050 char* entry;
3051 char* src = (char*)data + sizeof(DataMessageHeader);
3052 char* srcEnd = (char*)data + data->size;
3053 while (src < srcEnd) {
3054 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
3055 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
3056 if (((DataMessageEntryHeader*)entry)->cid == DOUBLEID) {
3057 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
3058 str = std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3);
3059 map[str] = *(float64*)(entry + sizeof(DataMessageEntryHeader));
3060 }
3061 }
3062 }
3063 src += ((DataMessageEntryHeader*)src)->size;
3064 }
3065 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
3066 return map;
3067}
3068
3069std::map<std::string, float64> DataMessage::getFloatMap(const char* key) {
3070 std::map<std::string, float64> map;
3071 std::string str;
3072 uint32 size;
3073 char* keyStart = utils::StringFormat(size, "%s_-[", key);
3074 if (data->cid != DATAMESSAGEID) return map;
3075 char* entry;
3076 char* src = (char*)data + sizeof(DataMessageHeader);
3077 char* srcEnd = (char*)data + data->size;
3078 while (src < srcEnd) {
3079 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
3080 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
3081 if (((DataMessageEntryHeader*)entry)->cid == DOUBLEID) {
3082 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
3083 str = std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3);
3084 map[str] = *(float64*)(entry + sizeof(DataMessageEntryHeader));
3085 }
3086 }
3087 }
3088 src += ((DataMessageEntryHeader*)src)->size;
3089 }
3090 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
3091 return map;
3092}
3093
3094std::map<std::string, DataMessage*> DataMessage::getAttachedMessageMap(const char* key) {
3095 std::map<std::string, DataMessage*> map;
3096 std::string str;
3097 uint32 size;
3098 char* keyStart = utils::StringFormat(size, "%s_-[", key);
3099 if (data->cid != DATAMESSAGEID) return map;
3100 char* entry;
3101 char* src = (char*)data + sizeof(DataMessageHeader);
3102 char* srcEnd = (char*)data + data->size;
3103 while (src < srcEnd) {
3104 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) {
3105 entry = src + sizeof(DataMessageEntryHeader) + strlen(src + sizeof(DataMessageEntryHeader)) + 1;
3106 if (((DataMessageEntryHeader*)entry)->cid == DATAMESSAGEID) {
3107 if (utils::TextStartsWith(src + sizeof(DataMessageEntryHeader), keyStart, false)) {
3108 str = std::string(src + sizeof(DataMessageEntryHeader) + size, strlen(src + sizeof(DataMessageEntryHeader)) - size - 3);
3109 map[str] = new DataMessage(entry + sizeof(DataMessageEntryHeader), true);
3110 }
3111 }
3112 }
3113 src += ((DataMessageEntryHeader*)src)->size;
3114 }
3115 free(keyStart); // StringFormat returns vasprintf-malloc'd memory (Utils.cpp:7644)
3116 return map;
3117}
3118
3119bool DataMessage::setTimeMap(const char* key, std::map<std::string, uint64>& map) {
3120 std::map<std::string, uint64>::iterator i = map.begin(), e = map.end();
3121 while (i != e) {
3122 if (!setTime(i->first.c_str(), key, i->second))
3123 return false;
3124 i++;
3125 }
3126 return true;
3127}
3128
3129bool DataMessage::setStringMap(const char* key, std::map<std::string, std::string>& map) {
3130 std::map<std::string, std::string>::iterator i = map.begin(), e = map.end();
3131 while (i != e) {
3132 if (!setString(i->first.c_str(), key, i->second.c_str()))
3133 return false;
3134 i++;
3135 }
3136 return true;
3137}
3138
3139bool DataMessage::setIntMap(const char* key, std::map<std::string, int64>& map) {
3140 std::map<std::string, int64>::iterator i = map.begin(), e = map.end();
3141 while (i != e) {
3142 if (!setInt(i->first.c_str(), key, i->second))
3143 return false;
3144 i++;
3145 }
3146 return true;
3147}
3148
3149bool DataMessage::setDoubleMap(const char* key, std::map<std::string, double>& map) {
3150 std::map<std::string, double>::iterator i = map.begin(), e = map.end();
3151 while (i != e) {
3152 if (!setDouble(i->first.c_str(), key, i->second))
3153 return false;
3154 i++;
3155 }
3156 return true;
3157}
3158
3159bool DataMessage::setFloatMap(const char* key, std::map<std::string, float64>& map) {
3160 std::map<std::string, float64>::iterator i = map.begin(), e = map.end();
3161 while (i != e) {
3162 if (!setFloat(i->first.c_str(), key, i->second))
3163 return false;
3164 i++;
3165 }
3166 return true;
3167}
3168
3169bool DataMessage::setAttachedMessageMap(const char* key, std::map<std::string, DataMessage*>& map) {
3170 std::map<std::string, DataMessage*>::iterator i = map.begin(), e = map.end();
3171 while (i != e) {
3172 if (!setAttachedMessage(i->first.c_str(), key, i->second))
3173 return false;
3174 i++;
3175 }
3176 return true;
3177}
3178
3179
3180DataMessage* DataMessage::FindClosestMessage(uint64 t, std::map<uint64, DataMessage*> &messages) {
3181 uint64 bestCandidateDiff = 0, diff;
3182 DataMessage* bestCandidate = NULL;
3183 std::map<uint64, DataMessage*>::reverse_iterator i = messages.rbegin(), e = messages.rend();
3184 while (i != e) {
3185 if (!bestCandidate) {
3186 bestCandidate = i->second;
3187 bestCandidateDiff = (i->first > t) ? i->first - t : t - i->first;
3188 }
3189 else {
3190 diff = (i->first > t) ? i->first - t : t - i->first;
3191 if (diff < bestCandidateDiff) {
3192 bestCandidate = i->second;
3193 bestCandidateDiff = diff;
3194 }
3195 }
3196 i++;
3197 }
3198 return bestCandidate;
3199}
3200
3201bool DataMessage::addTimeUsage(uint32 cycleCPUTime, uint32 cycleWallTime, uint32 chainCPUTime, uint32 chainWallTime, uint32 chainCount) {
3202 if (data->cid != DATAMESSAGEID) return false;
3203
3204 data->chaincputime = chainCPUTime + cycleCPUTime;
3205 data->chainwalltime = chainWallTime + cycleWallTime;
3206 data->chaincount = chainCount;
3207
3208 data->cyclecputime = cycleCPUTime;
3209 data->cyclewalltime = cycleWallTime;
3210 return true;
3211}
3212
3213
3214bool DataMessage::ConvertDataFromOlderMessageFormat(const char* data, char **newData) {
3215 *newData = NULL;
3216 if (!data) return false;
3217 const DataMessageHeader* header = (DataMessageHeader*)data;
3218 if (header->cid == DATAMESSAGEOLDID) {
3220 if (oldHeader->size < sizeof(DataMessageHeader_Old))
3221 return false;
3222
3223 // Data Message Version History
3224 // Initial version:
3225 // - data->cid = DATAMESSAGEOLDID
3226 // - does not contain cver, cyclecputime, cyclewalltime, chaincputime, chainwalltime
3227 // Version 10:
3228 // - data->cid = DATAMESSAGEID
3229 // - now contains cver, cenc, cyclecputime, cyclewalltime, chaincputime, chainwalltime
3230 // we added 18 bytes of space
3231 uint32 newSize = sizeof(DataMessageHeader) + oldHeader->userSize;
3232 *newData = new char[newSize];
3233 // reset buffer
3234 memset(*newData, 0, newSize);
3235 // set new size
3236 ((DataMessageHeader*)*newData)->size = newSize;
3237 // set new object id type
3238 ((DataMessageHeader*)*newData)->cid = DATAMESSAGEID;
3239 ((DataMessageHeader*)*newData)->cver = CURRENTDATAMESSAGEVERSION;
3240 // cenc already set to 0
3241 // copy the rest of the header
3242 ((DataMessageHeader*)*newData)->memid = oldHeader->memid;
3243 ((DataMessageHeader*)*newData)->time = oldHeader->time;
3244 ((DataMessageHeader*)*newData)->sendtime = oldHeader->sendtime;
3245 ((DataMessageHeader*)*newData)->recvtime = oldHeader->recvtime;
3246 ((DataMessageHeader*)*newData)->origin = oldHeader->origin;
3247 ((DataMessageHeader*)*newData)->destination = oldHeader->destination;
3248 ((DataMessageHeader*)*newData)->ttl = oldHeader->ttl;
3249 ((DataMessageHeader*)*newData)->priority = oldHeader->priority;
3250 ((DataMessageHeader*)*newData)->policy = oldHeader->policy;
3251 ((DataMessageHeader*)*newData)->from = oldHeader->from;
3252 ((DataMessageHeader*)*newData)->to = oldHeader->to;
3253 ((DataMessageHeader*)*newData)->type = oldHeader->type;
3254 ((DataMessageHeader*)*newData)->tag = oldHeader->tag;
3255 ((DataMessageHeader*)*newData)->status = oldHeader->status;
3256 ((DataMessageHeader*)*newData)->reference = oldHeader->reference;
3257 ((DataMessageHeader*)*newData)->serial = oldHeader->serial;
3258 ((DataMessageHeader*)*newData)->contextchange = oldHeader->contextchange;
3259 ((DataMessageHeader*)*newData)->userSize = oldHeader->userSize;
3260 ((DataMessageHeader*)*newData)->userCount = oldHeader->userCount;
3261 // cyclecputime, cyclewalltime, chaincputime, chainwalltime already set to 0
3262 if (oldHeader->userSize > 0)
3263 memcpy((*newData) + sizeof(DataMessageHeader), data + sizeof(DataMessageHeader_Old), oldHeader->userSize);
3264 return true;
3265 }
3266 else if (header->cid == DATAMESSAGEID) {
3267 if (header->cver == CURRENTDATAMESSAGEVERSION) {
3268 *newData = new char[header->size];
3269 memcpy(*newData, data, header->size);
3270 return true;
3271 }
3272 switch (header->cver) {
3273 case 10:
3274 *newData = new char[header->size];
3275 memcpy(*newData, data, header->size);
3276 return true;
3277 default:
3278 LogPrint(0, 0, 0, "*** DataMessage format version %u is not supported, only up to version %u ***", header->cver, CURRENTDATAMESSAGEVERSION);
3279 return false;
3280 }
3281 }
3282 return false;
3283}
3284
3286
3287 // 1. Construction and validity
3288 unittest::progress(5, "construction and validity");
3289 {
3290 DataMessage empty;
3291 if (!empty.isValid()) {
3292 unittest::fail("DataMessage test: default constructor invalid\n");
3293 return false;
3294 }
3295 if (empty.getUserCount() != 0) {
3296 unittest::fail("DataMessage test: empty getUserCount != 0\n");
3297 return false;
3298 }
3299
3300 DataMessage withType(CTRL_TEST, 1);
3301 if (!withType.isValid() || withType.getUserCount() != 0) {
3302 unittest::fail("DataMessage test: DataMessage(type, from) invalid\n");
3303 return false;
3304 }
3305
3306 DataMessage full(CTRL_TEST, 1, 2, static_cast<uint64>(1000), static_cast<uint16>(5));
3307 if (!full.isValid()) {
3308 unittest::fail("DataMessage test: DataMessage(type, from, to, ttl, priority) invalid\n");
3309 return false;
3310 }
3311 }
3312
3313 // 2. Single-value write and read
3314 unittest::progress(20, "single-value read/write");
3315 {
3316 DataMessage msg(CTRL_TEST, 0);
3317 const uint64 t = 1234567890ULL;
3318 if (!msg.setTime("T", t)) { unittest::fail("DataMessage test: setTime failed\n"); return false; }
3319 if (!msg.setString("S", "hello")) { unittest::fail("DataMessage test: setString failed\n"); return false; }
3320 if (!msg.setInt("I", -42)) { unittest::fail("DataMessage test: setInt failed\n"); return false; }
3321 if (!msg.setDouble("D", 3.14)) { unittest::fail("DataMessage test: setDouble failed\n"); return false; }
3322 if (!msg.setFloat("F", 2.5f)) { unittest::fail("DataMessage test: setFloat failed\n"); return false; }
3323 const char bin[] = { 'x', 'y', 'z' };
3324 if (!msg.setData("B", bin, 3)) { unittest::fail("DataMessage test: setData failed\n"); return false; }
3325
3326 if (msg.getTime("T") != t) { unittest::fail("DataMessage test: getTime mismatch\n"); return false; }
3327 const char* s = msg.getString("S");
3328 if (!s || strcmp(s, "hello") != 0) { unittest::fail("DataMessage test: getString mismatch\n"); return false; }
3329 int64 i = 0;
3330 if (!msg.getInt("I", i) || i != -42) { unittest::fail("DataMessage test: getInt mismatch\n"); return false; }
3331 double d = 0;
3332 if (!msg.getDouble("D", d) || d < 3.13 || d > 3.15) { unittest::fail("DataMessage test: getDouble mismatch\n"); return false; }
3333 float64 f = 0;
3334 if (!msg.getFloat("F", f) || f < 2.4f || f > 2.6f) { unittest::fail("DataMessage test: getFloat mismatch\n"); return false; }
3335 uint32 bsize = 0;
3336 const char* b = msg.getData("B", bsize);
3337 if (!b || bsize != 3 || b[0] != 'x' || b[1] != 'y' || b[2] != 'z') {
3338 unittest::fail("DataMessage test: getData mismatch\n"); return false;
3339 }
3340
3341 if (msg.getContentType("T") != TIMEID) { unittest::fail("DataMessage test: getContentType T != TIMEID\n"); return false; }
3342 if (msg.getContentType("S") != CONSTCHARID) { unittest::fail("DataMessage test: getContentType S != CONSTCHARID\n"); return false; }
3343 if (msg.getContentType("I") != INTID) { unittest::fail("DataMessage test: getContentType I != INTID\n"); return false; }
3344 if (msg.getContentType("D") != DOUBLEID) { unittest::fail("DataMessage test: getContentType D != DOUBLEID\n"); return false; }
3345 if (msg.getContentType("B") != CHARDATAID) { unittest::fail("DataMessage test: getContentType B != CHARDATAID\n"); return false; }
3346 if (!msg.hasKey("S") || !msg.hasString("S") || !msg.hasInteger("I") || !msg.hasFloat("D") || !msg.hasTime("T") || !msg.hasData("B")) {
3347 unittest::fail("DataMessage test: hasKey/hasString etc failed\n"); return false;
3348 }
3349 if (msg.getKeyType("S") != DataMessage::SINGLE || !msg.isSingleValue("S")) {
3350 unittest::fail("DataMessage test: getKeyType/isSingleValue failed\n"); return false;
3351 }
3352 if (msg.getAsString("I") != "-42" || msg.getAsInt("S") != 0) { /* getAsInt on string may return 0 */ }
3353 if (msg.getAsBool("I") != true) { unittest::fail("DataMessage test: getAsBool failed\n"); return false; }
3354
3355 // getDataCopy
3356 uint32 csize = 0;
3357 char* cpy = msg.getDataCopy("B", csize);
3358 if (!cpy || csize != 3) { unittest::fail("DataMessage test: getDataCopy failed\n"); return false; }
3359 delete[] cpy;
3360
3361 // Attached message
3362 DataMessage* nested = new DataMessage(CTRL_TEST, 99);
3363 nested->setString("nested", "value");
3364 if (!msg.setAttachedMessage("M", nested)) { unittest::fail("DataMessage test: setAttachedMessage failed\n"); delete nested; return false; }
3365 DataMessage* got = msg.getAttachedMessageCopy("M");
3366 if (!got || got->getUserCount() == 0) { unittest::fail("DataMessage test: getAttachedMessageCopy failed\n"); delete got; return false; }
3367 if (got->getAsString("nested") != "value") { unittest::fail("DataMessage test: attached message content mismatch\n"); delete got; return false; }
3368 if (msg.getContentType("M") != DATAMESSAGEID) { unittest::fail("DataMessage test: getContentType M != DATAMESSAGEID\n"); delete got; return false; }
3369 if (!msg.hasMessage("M")) { unittest::fail("DataMessage test: hasMessage failed\n"); delete got; return false; }
3370 delete got;
3371 delete nested;
3372 }
3373
3374 // 3. Array access
3375 unittest::progress(40, "array access");
3376 {
3377 DataMessage msg(CTRL_TEST, 0);
3378 if (!msg.setTime(static_cast<int64>(0), "arr", 100ULL)) { unittest::fail("DataMessage test: setTime array failed\n"); return false; }
3379 if (!msg.setString(1, "arr", "one")) { unittest::fail("DataMessage test: setString array failed\n"); return false; }
3380 if (!msg.setInt(2, "arr", 999)) { unittest::fail("DataMessage test: setInt array failed\n"); return false; }
3381 if (msg.getTime(static_cast<int64>(0), "arr") != 100ULL) { unittest::fail("DataMessage test: getTime array mismatch\n"); return false; }
3382 if (msg.getAsString(1, "arr") != "one") { unittest::fail("DataMessage test: getAsString array mismatch\n"); return false; }
3383 int64 vi = 0;
3384 if (!msg.getInt(2, "arr", vi) || vi != 999) { unittest::fail("DataMessage test: getInt array mismatch\n"); return false; }
3385 if (msg.getKeyType("arr") != DataMessage::ARRAY || !msg.isArrayContent("arr")) {
3386 unittest::fail("DataMessage test: array key type failed\n"); return false;
3387 }
3388 // getArraySize counts only CONSTCHARID value entries; arr has time, string, int so size may be 1
3389 if (msg.getArraySize("arr") < 1) {
3390 unittest::fail("DataMessage test: getArraySize failed\n"); return false;
3391 }
3392
3393 std::map<int64, uint64> tmap; tmap[0] = 10; tmap[1] = 20;
3394 if (!msg.setTimeArray("tarr", tmap)) { unittest::fail("DataMessage test: setTimeArray failed\n"); return false; }
3395 std::map<int64, std::string> smap; smap[0] = "a"; smap[1] = "b";
3396 if (!msg.setStringArray("sarr", smap)) { unittest::fail("DataMessage test: setStringArray failed\n"); return false; }
3397 std::map<int64, int64> imap; imap[0] = 1; imap[1] = 2;
3398 if (!msg.setIntArray("iarr", imap)) { unittest::fail("DataMessage test: setIntArray failed\n"); return false; }
3399 std::map<int64, double> dmap; dmap[0] = 1.1; dmap[1] = 2.2;
3400 if (!msg.setDoubleArray("darr", dmap)) { unittest::fail("DataMessage test: setDoubleArray failed\n"); return false; }
3401 std::map<int64, uint64> gotT = msg.getTimeArray("tarr");
3402 if (gotT.size() != 2 || gotT[0] != 10 || gotT[1] != 20) { unittest::fail("DataMessage test: getTimeArray mismatch\n"); return false; }
3403 std::map<int64, std::string> gotS = msg.getAsStringArray("sarr");
3404 if (gotS.size() != 2 || gotS[0] != "a" || gotS[1] != "b") { unittest::fail("DataMessage test: getAsStringArray mismatch\n"); return false; }
3405 std::map<int64, int64> gotI = msg.getIntArray("iarr");
3406 if (gotI.size() != 2 || gotI[0] != 1 || gotI[1] != 2) { unittest::fail("DataMessage test: getIntArray mismatch\n"); return false; }
3407 std::map<int64, double> gotD = msg.getDoubleArray("darr");
3408 if (gotD.size() != 2 || gotD[0] < 1.0 || gotD[0] > 1.2 || gotD[1] < 2.1 || gotD[1] > 2.3) { unittest::fail("DataMessage test: getDoubleArray mismatch\n"); return false; }
3409 }
3410
3411 // 4. Map access (string index)
3412 unittest::progress(55, "map access");
3413 {
3414 DataMessage msg(CTRL_TEST, 0);
3415 if (!msg.setTime("x", "m", 200ULL)) { unittest::fail("DataMessage test: setTime map failed\n"); return false; }
3416 if (!msg.setString("y", "m", "mapval")) { unittest::fail("DataMessage test: setString map failed\n"); return false; }
3417 if (!msg.setInt("z", "m", -7)) { unittest::fail("DataMessage test: setInt map failed\n"); return false; }
3418 if (msg.getTime("x", "m") != 200ULL) { unittest::fail("DataMessage test: getTime map mismatch\n"); return false; }
3419 if (msg.getAsString("y", "m") != "mapval") { unittest::fail("DataMessage test: getAsString map mismatch\n"); return false; }
3420 int64 vi = 0;
3421 if (!msg.getInt("z", "m", vi) || vi != -7) { unittest::fail("DataMessage test: getInt map mismatch\n"); return false; }
3422 if (msg.getKeyType("m") != DataMessage::MAP || !msg.isMapContent("m")) {
3423 unittest::fail("DataMessage test: map key type failed\n"); return false;
3424 }
3425 // getMapSize counts only CONSTCHARID value entries; m has time, string, int so size may be 1
3426 if (msg.getMapSize("m") < 1) {
3427 unittest::fail("DataMessage test: getMapSize failed\n"); return false;
3428 }
3429
3430 std::map<std::string, uint64> tmap; tmap["a"] = 1; tmap["b"] = 2;
3431 if (!msg.setTimeMap("tm", tmap)) { unittest::fail("DataMessage test: setTimeMap failed\n"); return false; }
3432 std::map<std::string, std::string> smap; smap["k1"] = "v1"; smap["k2"] = "v2";
3433 if (!msg.setStringMap("sm", smap)) { unittest::fail("DataMessage test: setStringMap failed\n"); return false; }
3434 std::map<std::string, int64> imap; imap["i1"] = 10; imap["i2"] = 20;
3435 if (!msg.setIntMap("im", imap)) { unittest::fail("DataMessage test: setIntMap failed\n"); return false; }
3436 std::map<std::string, uint64> gotT = msg.getTimeMap("tm");
3437 if (gotT.size() != 2 || gotT["a"] != 1 || gotT["b"] != 2) { unittest::fail("DataMessage test: getTimeMap mismatch\n"); return false; }
3438 std::map<std::string, std::string> gotS = msg.getAsStringMap("sm");
3439 if (gotS.size() != 2 || gotS["k1"] != "v1" || gotS["k2"] != "v2") { unittest::fail("DataMessage test: getAsStringMap mismatch\n"); return false; }
3440 std::map<std::string, int64> gotI = msg.getIntMap("im");
3441 if (gotI.size() != 2 || gotI["i1"] != 10 || gotI["i2"] != 20) { unittest::fail("DataMessage test: getIntMap mismatch\n"); return false; }
3442 }
3443
3444 // 5. Binary serialisation round-trip
3445 unittest::progress(70, "binary serialisation round-trip");
3446 {
3447 DataMessage orig(CTRL_TEST, 0);
3448 orig.setString("k", "roundtrip");
3449 orig.setInt("n", 77);
3450 orig.setDouble("x", 1.5);
3451 std::map<int64, std::string> amap; amap[0] = "elem0";
3452 orig.setStringArray("arr", amap);
3453 std::map<std::string, int64> mmap; mmap["idx"] = 42;
3454 orig.setIntMap("m", mmap);
3455 char* raw = orig.getRawData();
3456 uint32 sz = orig.getSize();
3457 if (!raw || sz == 0) { unittest::fail("DataMessage test: getRawData/getSize failed\n"); return false; }
3458 DataMessage decoded((const char*)raw);
3459 if (!decoded.isValid()) { unittest::fail("DataMessage test: decoded message invalid\n"); return false; }
3460 if (decoded.getUserCount() != orig.getUserCount()) {
3461 unittest::fail("DataMessage test: round-trip getUserCount mismatch\n"); return false;
3462 }
3463 if (decoded.getAsString("k") != "roundtrip") { unittest::fail("DataMessage test: round-trip string mismatch\n"); return false; }
3464 int64 nval = 0;
3465 if (!decoded.getInt("n", nval) || nval != 77) { unittest::fail("DataMessage test: round-trip int mismatch\n"); return false; }
3466 double xval = 0;
3467 if (!decoded.getDouble("x", xval) || xval < 1.4 || xval > 1.6) { unittest::fail("DataMessage test: round-trip double mismatch\n"); return false; }
3468 if (decoded.getAsStringArray("arr")[0] != "elem0") { unittest::fail("DataMessage test: round-trip array mismatch\n"); return false; }
3469 if (decoded.getIntMap("m")["idx"] != 42) { unittest::fail("DataMessage test: round-trip map mismatch\n"); return false; }
3470 }
3471
3472 // 6. Text / structured serialisation
3473 unittest::progress(82, "text / structured serialisation");
3474 {
3475 DataMessage msg(CTRL_TEST, 0);
3476 msg.setString("foo", "bar");
3477 std::string ustr = msg.getUserEntriesAsString();
3478 if (ustr.empty()) { unittest::fail("DataMessage test: getUserEntriesAsString empty\n"); return false; }
3479 std::string ujson = msg.getUserEntriesAsJSON(false);
3480 if (ujson.empty()) { unittest::fail("DataMessage test: getUserEntriesAsJSON empty\n"); return false; }
3481 msg.getUserEntriesAsJSON(true);
3482 std::string json = msg.toJSON();
3483 if (json.empty()) { unittest::fail("DataMessage test: toJSON empty\n"); return false; }
3484 std::string xml = msg.toXML();
3485 if (xml.empty()) { unittest::fail("DataMessage test: toXML empty\n"); return false; }
3486 std::string csv = msg.toCSV();
3487 if (csv.empty()) { unittest::fail("DataMessage test: toCSV empty\n"); return false; }
3488 std::string hdr = DataMessage::GetCSVHeader();
3489 if (hdr.empty()) { unittest::fail("DataMessage test: GetCSVHeader empty\n"); return false; }
3490 }
3491
3492 // 7. Copy and remove
3493 unittest::progress(90, "copy and remove");
3494 {
3495 DataMessage src(CTRL_TEST, 0);
3496 src.setString("a", "1");
3497 src.setInt("b", 2);
3498 DataMessage dst(CTRL_TEST, 0);
3499 if (dst.copyUserEntriesFromMessage(&src) != 2) {
3500 unittest::fail("DataMessage test: copyUserEntriesFromMessage count\n"); return false;
3501 }
3502 if (dst.getAsString("a") != "1" || dst.getAsInt("b") != 2) {
3503 unittest::fail("DataMessage test: copyUserEntriesFromMessage content\n"); return false;
3504 }
3505 uint32 cnt = dst.getUserCount();
3506 if (!dst.removeEntry("a")) { unittest::fail("DataMessage test: removeEntry failed\n"); return false; }
3507 if (dst.hasKey("a")) { unittest::fail("DataMessage test: removeEntry key still present\n"); return false; }
3508 if (dst.getUserCount() != cnt - 1) { unittest::fail("DataMessage test: removeEntry count\n"); return false; }
3509 }
3510
3511 // 7b. getAllKeys, delete + re-add with any type, deleted keys unreadable (26 Oct 2026)
3512 unittest::progress(93, "getAllKeys / delete + re-add");
3513 {
3514 DataMessage msg(CTRL_TEST, 0);
3515 msg.setInt("i", 5);
3516 msg.setTime("t", 123456789ULL);
3517 msg.setString("s", "hello");
3518 msg.setInt((int64)0, "arr", 10); msg.setInt((int64)1, "arr", 11);
3519 msg.setTime("a", "map", 1000ULL); msg.setTime("b", "map", 2000ULL);
3520 std::map<std::string, KeyType> keys = msg.getAllKeys();
3521 if (keys.size() != 5) { unittest::fail("DataMessage test: getAllKeys count %u != 5\n", (uint32)keys.size()); return false; }
3522 if (keys["i"] != SINGLE || keys["t"] != SINGLE || keys["s"] != SINGLE || keys["arr"] != ARRAY || keys["map"] != MAP) {
3523 unittest::fail("DataMessage test: getAllKeys types\n"); return false;
3524 }
3525 if (DataMessage().getAllKeys().size() != 0) { unittest::fail("DataMessage test: getAllKeys on empty\n"); return false; }
3526
3527 // Deleted keys cannot be read again, by any getter, and are gone from getAllKeys/getKeyType
3528 uint32 cnt = msg.getUserCount();
3529 if (!msg.deleteEntry("i") || !msg.deleteEntry("t") || !msg.deleteEntry("s")) { unittest::fail("DataMessage test: deleteEntry failed\n"); return false; }
3530 int64 iv = -1; bool ok = true;
3531 if (msg.hasKey("i") || msg.getInt("i", iv) || msg.getTime("t") != 0 || msg.hasTime("t") || msg.getString("s") != NULL ||
3532 msg.getKeyType("i") != NONE || msg.getAllKeys().count("s")) {
3533 unittest::fail("DataMessage test: deleted key still readable\n"); return false;
3534 }
3535 if (msg.getUserCount() != cnt - 3) { unittest::fail("DataMessage test: deleteEntry count\n"); return false; }
3536
3537 // Deleting an array or map name removes all its items
3538 msg.deleteEntry("arr"); msg.deleteEntry("map");
3539 if (msg.getArrayCount("arr") != 0 || msg.getTimeMap("map").size() != 0 || msg.getKeyType("map") != NONE || msg.getAllKeys().size() != 0) {
3540 unittest::fail("DataMessage test: deleted array/map still readable\n"); return false;
3541 }
3542 if (msg.getUserCount() != 0) { unittest::fail("DataMessage test: count after deleting all %u\n", msg.getUserCount()); return false; }
3543
3544 // Delete + re-add, same size (reuses the freed slot: the original bug) — int, time
3545 DataMessage m2(CTRL_TEST, 0);
3546 m2.setInt("k", 5); m2.deleteEntry("k");
3547 if (!m2.setInt("k", 7) || !m2.getInt("k", iv) || iv != 7) { unittest::fail("DataMessage test: delete + re-add int (same slot)\n"); return false; }
3548 m2.deleteEntry("k");
3549 if (!m2.setTime("k", 99ULL) || m2.getTime("k") != 99ULL) { unittest::fail("DataMessage test: delete + re-add as time (same slot)\n"); return false; }
3550 // ... and of another type and size
3551 m2.deleteEntry("k");
3552 if (!m2.setString("k", "now a longer string") || m2.getString("k") == NULL || strcmp(m2.getString("k"), "now a longer string") != 0) {
3553 unittest::fail("DataMessage test: delete + re-add as string\n"); return false;
3554 }
3555 m2.deleteEntry("k");
3556 if (!m2.setDouble("k", 2.5) || m2.getDouble("k") != 2.5) { unittest::fail("DataMessage test: delete + re-add as double\n"); return false; }
3557 m2.deleteEntry("k");
3558 const char blob[3] = { 1, 2, 3 }; uint32 bsz = 0;
3559 if (!m2.setData("k", blob, 3) || m2.getData("k", bsz) == NULL || bsz != 3) { unittest::fail("DataMessage test: delete + re-add as data\n"); return false; }
3560 if (m2.getUserCount() != 1 || m2.getAllKeys().size() != 1) { unittest::fail("DataMessage test: count after re-adds\n"); return false; }
3561 // Overwrite in place with a smaller value keeps the right size
3562 m2.setData("d", "abcdefgh", 8); m2.setData("d", "xy", 2);
3563 if (m2.getData("d", bsz) == NULL || bsz != 2) { unittest::fail("DataMessage test: overwrite smaller data size %u\n", bsz); return false; }
3564 // Replace a map entirely: delete it, then write a new one
3565 m2.setTime("a", "trail", 1ULL); m2.setTime("b", "trail", 2ULL);
3566 m2.deleteEntry("trail");
3567 m2.setTime("c", "trail", 3ULL);
3568 std::map<std::string, uint64> tm = m2.getTimeMap("trail");
3569 if (tm.size() != 1 || tm["c"] != 3ULL) { unittest::fail("DataMessage test: map replace\n"); return false; }
3570 // Survives a raw round trip
3571 DataMessage copy((const char*)m2.getRawData());
3572 if (copy.getTimeMap("trail").size() != 1 || copy.getAllKeys().size() != 3 || copy.hasKey("i")) {
3573 unittest::fail("DataMessage test: round trip after delete/re-add\n"); return false;
3574 }
3575 }
3576
3577 // 8. Edge / robustness
3578 unittest::progress(96, "edge / robustness");
3579 {
3580 DataMessage msg(CTRL_TEST, 0);
3581 if (msg.hasKey("nonexistent")) { unittest::fail("DataMessage test: hasKey nonexistent true\n"); return false; }
3582 if (msg.getString("nonexistent") != NULL) { unittest::fail("DataMessage test: getString nonexistent non-NULL\n"); return false; }
3583 if (msg.getTime("nonexistent") != 0) { unittest::fail("DataMessage test: getTime nonexistent non-zero\n"); return false; }
3584 DataMessage empty;
3585 if (empty.getUserCount() != 0) { unittest::fail("DataMessage test: empty getUserCount\n"); return false; }
3586 empty.getUserEntriesAsString();
3587 empty.toJSON();
3588 }
3589
3590 // 9. Packed wire form
3591 unittest::progress(98, "packed wire form");
3592 {
3593 #define PKFAIL(what) { unittest::fail("DataMessage test: packed: " what "\n"); return false; }
3594 DataMessage m(CTRL_TEST, 7, 9, 11, 63960700000000000ULL, 5000000, 3);
3595 m.setSerial(4242);
3596 m.setString("PsyDevice", "phone-1234abcd");
3597 m.setInt("seq", 123456);
3598 m.setInt("neg", -7);
3599 m.setInt("big", (int64)0x7FFFFFFFFFFFFFFFLL);
3600 m.setInt("min", (int64)(-0x7FFFFFFFFFFFFFFFLL - 1));
3601 m.setDouble("gain", 0.75);
3602 m.setTime("tcap", 63960700000123456ULL);
3603 char audio[80];
3604 for (int i = 0; i < 80; i++) audio[i] = (char)(i * 31);
3605 m.setData("audio", audio, 80);
3606 m.setString("empty", "");
3607 DataMessage inner(CTRL_TEST, 1);
3608 inner.setString("Text", "nested");
3609 m.setAttachedMessage("att", &inner);
3610 uint32 n = 0;
3611 char* pk = m.toPacked(n);
3612 if (!pk || n < 20 || !DataMessage::IsPacked(pk, n) || DataMessage::IsPacked((const char*)m.data, m.data->size)) PKFAIL("encode / IsPacked");
3613 if (n * 2 > m.data->size) PKFAIL("not at least 2x smaller");
3615 if (!d) PKFAIL("decode");
3616 if (!(d->data->cenc & DATAMESSAGE_CENC_PACKED)) PKFAIL("cenc flag not set");
3618 // same content and the same order -> the regular block is byte-identical
3619 if (d->data->size != m.data->size || memcmp(d->data, m.data, m.data->size) != 0) PKFAIL("decoded block differs from the original");
3620 uint32 sz = 0;
3621 const char* av = d->getData("audio", sz);
3622 if (sz != 80 || memcmp(av, audio, 80) != 0 || d->getInt("min") != (int64)(-0x7FFFFFFFFFFFFFFFLL - 1)) PKFAIL("values");
3623 DataMessage* att = d->getAttachedMessageCopy("att");
3624 if (!att || !att->getString("Text") || strcmp(att->getString("Text"), "nested") != 0) PKFAIL("attached message");
3625 delete(att);
3626 // re-encoding a decoded message gives the same packed bytes (the cenc flag is not sent)
3628 uint32 n2 = 0;
3629 char* pk2 = d->toPacked(n2);
3630 if (!pk2 || n2 != n || memcmp(pk, pk2, n) != 0) PKFAIL("re-encode differs");
3631 free(pk2);
3632 delete(d);
3633 // every truncation and a corrupted size are rejected, never read past the end
3634 for (uint32 cut = 0; cut < n; cut++) {
3635 char* t = (char*)malloc(n);
3636 memcpy(t, pk, n);
3637 memcpy(t, &cut, 4);
3638 DataMessage* bad = DataMessage::FromPacked(t, cut);
3639 free(t);
3640 if (bad) { delete(bad); PKFAIL("accepted a truncated message"); }
3641 }
3642 pk[9] = (char)0xFF; pk[10] = (char)0xFF; pk[11] = (char)0xFF; pk[12] = (char)0xFF; // field mask out of range
3643 DataMessage* bad2 = DataMessage::FromPacked(pk, n);
3644 if (bad2) { delete(bad2); PKFAIL("accepted a corrupt field mask"); }
3645 free(pk);
3646 // deleted slots are dropped; an empty message packs to a few bytes
3647 DataMessage e2;
3648 e2.setInt("a", 1); e2.setInt("b", 2); e2.removeEntry("a");
3649 char* pk3 = e2.toPacked(n);
3650 DataMessage* d3 = pk3 ? DataMessage::FromPacked(pk3, n) : NULL;
3651 if (!d3 || d3->getUserCount() != 1 || d3->getInt("b") != 2 || d3->hasKey("a")) PKFAIL("deleted slot");
3652 delete(d3); free(pk3);
3653 #undef PKFAIL
3654 }
3655
3656 unittest::progress(100, "done");
3657 return true;
3658}
3659
3662 "Data message encode/decode, arrays, maps, serialisation", "core");
3663}
3664
3665} // namespace cmlabs
3666
3668// Packed wire form (see DataMessage.h, "Packed wire form")
3670
3671namespace cmlabs {
3672
3673namespace {
3674 enum { PK_RAW = 0, PK_TEXT = 1, PK_INT = 2, PK_DOUBLE = 3, PK_TIME = 4, PK_DATA = 5, PK_MESSAGE = 6 };
3675 enum { PK_FIELDS = 24 };
3676 const uint32 PK_MAX_DEPTH = 16; // attached messages nest at most this deep
3677
3678 void PkVarint(std::string& o, uint64 v) {
3679 while (v >= 0x80) { o += (char)((v & 0x7F) | 0x80); v >>= 7; }
3680 o += (char)v;
3681 }
3682 void PkBytes(std::string& o, const char* d, uint32 n) { PkVarint(o, n); o.append(d, n); }
3683 void PkLevels(std::string& o, const uint16* lv, uint32 n) {
3684 while (n && !lv[n - 1]) n--;
3685 PkVarint(o, n);
3686 for (uint32 i = 0; i < n; i++) PkVarint(o, lv[i]);
3687 }
3688 // The header fields in mask-bit order, as unsigned 64-bit values (PsyType/PsyContext separate).
3689 void PkFields(const DataMessageHeader* h, uint64* f) {
3690 f[0] = h->cenc & ~DATAMESSAGE_CENC_PACKED; f[1] = h->memid; f[2] = h->time; f[3] = h->sendtime;
3691 f[4] = h->recvtime; f[5] = h->origin; f[6] = h->destination; f[7] = h->ttl; f[8] = h->priority;
3692 f[9] = h->policy; f[10] = h->from; f[11] = h->to; f[12] = 0; f[13] = h->tag; f[14] = h->status;
3693 f[15] = h->reference; f[16] = h->serial; f[17] = 0; f[18] = h->cyclecputime; f[19] = h->cyclewalltime;
3694 f[20] = h->chaincputime; f[21] = h->chainwalltime; f[22] = h->chaincount; f[23] = h->systemid;
3695 }
3696
3697 bool PkEncode(const DataMessageHeader* h, std::string& o, uint32 depth) {
3698 if (!h || h->cid != DATAMESSAGEID || h->size < sizeof(DataMessageHeader) || depth > PK_MAX_DEPTH)
3699 return false;
3700 size_t start = o.size();
3701 o.append(8, '\0'); // size + cid, filled in below
3702 o += (char)CURRENTDATAMESSAGEVERSION;
3703 uint64 f[PK_FIELDS];
3704 PkFields(h, f);
3705 bool hasType = false, hasCtx = false;
3706 for (uint32 i = 0; i < PSYTYPELEVELS; i++) hasType |= (h->type.levels[i] != 0);
3707 for (uint32 i = 0; i < PSYCONTEXTLEVELS; i++) hasCtx |= (h->contextchange.levels[i] != 0);
3708 uint64 mask = 0;
3709 for (uint32 i = 0; i < PK_FIELDS; i++)
3710 if (f[i] || (i == 12 && hasType) || (i == 17 && hasCtx)) mask |= (1ULL << i);
3711 PkVarint(o, mask);
3712 for (uint32 i = 0; i < PK_FIELDS; i++) {
3713 if (!(mask & (1ULL << i))) continue;
3714 if (i == 12) PkLevels(o, h->type.levels, PSYTYPELEVELS);
3715 else if (i == 17) PkLevels(o, h->contextchange.levels, PSYCONTEXTLEVELS);
3716 else PkVarint(o, f[i]);
3717 }
3718 // entries: count first, so walk twice
3719 const char* base = (const char*)h;
3720 const char* end = base + h->size;
3721 uint32 count = 0;
3722 for (const char* src = base + sizeof(DataMessageHeader); src + sizeof(DataMessageEntryHeader) <= end; ) {
3723 uint32 slot = ((DataMessageEntryHeader*)src)->size;
3724 if (slot < sizeof(DataMessageEntryHeader) || src + slot > end) return false;
3725 if (((DataMessageEntryHeader*)src)->cid == CONSTCHARID) count++;
3726 src += slot;
3727 }
3728 PkVarint(o, count);
3729 for (const char* src = base + sizeof(DataMessageHeader); src + sizeof(DataMessageEntryHeader) <= end; ) {
3730 const DataMessageEntryHeader* kh = (const DataMessageEntryHeader*)src;
3731 const char* slotEnd = src + kh->size;
3732 if (kh->cid == CONSTCHARID) {
3733 const char* key = src + sizeof(DataMessageEntryHeader);
3734 const char* k = key;
3735 while (k < slotEnd && *k) k++;
3736 if (k >= slotEnd) return false;
3737 const DataMessageEntryHeader* vh = (const DataMessageEntryHeader*)(k + 1);
3738 if ((const char*)vh + sizeof(DataMessageEntryHeader) > slotEnd || vh->size < sizeof(DataMessageEntryHeader)
3739 || (const char*)vh + vh->size > slotEnd)
3740 return false;
3741 const char* v = (const char*)vh + sizeof(DataMessageEntryHeader);
3742 uint32 vn = vh->size - sizeof(DataMessageEntryHeader);
3743 PkBytes(o, key, (uint32)(k - key));
3744 switch (vh->cid) {
3745 case CONSTCHARID: {
3746 o += (char)PK_TEXT;
3747 uint32 n = 0;
3748 while (n < vn && v[n]) n++; // stored NUL-terminated
3749 PkBytes(o, v, n);
3750 break; }
3751 case INTID: {
3752 if (vn != 8) return false;
3753 int64 x; memcpy(&x, v, 8);
3754 o += (char)PK_INT;
3755 PkVarint(o, ((uint64)x << 1) ^ (uint64)(x >> 63));
3756 break; }
3757 case DOUBLEID:
3758 if (vn != 8) return false;
3759 o += (char)PK_DOUBLE; o.append(v, 8);
3760 break;
3761 case TIMEID: {
3762 if (vn != 8) return false;
3763 uint64 x; memcpy(&x, v, 8);
3764 o += (char)PK_TIME; PkVarint(o, x);
3765 break; }
3766 case CHARDATAID:
3767 o += (char)PK_DATA; PkBytes(o, v, vn);
3768 break;
3769 case DATAMESSAGEID: {
3770 o += (char)PK_MESSAGE;
3771 std::string nested;
3772 if (!PkEncode((const DataMessageHeader*)v, nested, depth + 1)) return false;
3773 PkBytes(o, nested.data(), (uint32)nested.size());
3774 break; }
3775 default:
3776 o += (char)PK_RAW; PkVarint(o, vh->cid); PkBytes(o, v, vn);
3777 break;
3778 }
3779 }
3780 src = slotEnd; // UNUSEDID (deleted) slots are dropped
3781 }
3782 uint32 size = (uint32)(o.size() - start), cid = PACKEDDATAMESSAGEID;
3783 memcpy(&o[start], &size, 4);
3784 memcpy(&o[start + 4], &cid, 4);
3785 return true;
3786 }
3787
3788 struct PkReader {
3789 const uint8* p; const uint8* e;
3790 bool ok;
3791 PkReader(const char* d, uint32 n) : p((const uint8*)d), e((const uint8*)d + n), ok(true) {}
3792 uint64 varint() {
3793 uint64 v = 0;
3794 for (uint32 s = 0; s < 64; s += 7) {
3795 if (p >= e) { ok = false; return 0; }
3796 uint8 b = *p++;
3797 v |= (uint64)(b & 0x7F) << s;
3798 if (!(b & 0x80)) return v;
3799 }
3800 ok = false;
3801 return 0;
3802 }
3803 uint8 byte() { if (p >= e) { ok = false; return 0; } return *p++; }
3804 const char* bytes(uint64 n) {
3805 if (!ok || n > (uint64)(e - p)) { ok = false; return NULL; }
3806 const char* r = (const char*)p; p += n; return r;
3807 }
3808 bool levels(uint16* lv, uint32 max) {
3809 uint64 n = varint();
3810 if (!ok || n > max) { ok = false; return false; }
3811 for (uint32 i = 0; i < n; i++) { uint64 x = varint(); if (x > 0xFFFF) ok = false; lv[i] = (uint16)x; }
3812 return ok;
3813 }
3814 };
3815
3816 void PkEntry(std::string& o, const char* key, uint32 kn, uint32 cid, const char* v, uint32 vn, bool nul) {
3818 vh.size = (uint32)sizeof(DataMessageEntryHeader) + vn + (nul ? 1 : 0);
3819 vh.cid = cid;
3820 kh.size = (uint32)sizeof(DataMessageEntryHeader) + kn + 1 + vh.size;
3821 kh.cid = CONSTCHARID;
3822 o.append((const char*)&kh, sizeof(kh)); o.append(key, kn); o += '\0';
3823 o.append((const char*)&vh, sizeof(vh)); o.append(v, vn); if (nul) o += '\0';
3824 }
3825
3826 // Decode one packed message (exactly n bytes) into a regular block appended to o.
3827 bool PkDecode(const char* d, uint32 n, std::string& o, uint32 depth) {
3828 if (n < 9 || depth > PK_MAX_DEPTH) return false;
3829 uint32 size, cid;
3830 memcpy(&size, d, 4); memcpy(&cid, d + 4, 4);
3831 if (size != n || cid != PACKEDDATAMESSAGEID || (uint8)d[8] != CURRENTDATAMESSAGEVERSION) return false;
3832 PkReader r(d + 9, n - 9);
3834 memset(&h, 0, sizeof(h));
3835 uint64 mask = r.varint();
3836 if (!r.ok || (mask >> PK_FIELDS)) return false;
3837 uint64 f[PK_FIELDS];
3838 memset(f, 0, sizeof(f));
3839 for (uint32 i = 0; i < PK_FIELDS && r.ok; i++) {
3840 if (!(mask & (1ULL << i))) continue;
3841 if (i == 12) r.levels(h.type.levels, PSYTYPELEVELS);
3842 else if (i == 17) r.levels(h.contextchange.levels, PSYCONTEXTLEVELS);
3843 else f[i] = r.varint();
3844 }
3845 if (!r.ok) return false;
3846 h.cid = DATAMESSAGEID; h.cver = CURRENTDATAMESSAGEVERSION;
3847 h.cenc = (uint16)f[0]; h.memid = f[1]; h.time = f[2]; h.sendtime = f[3]; h.recvtime = f[4];
3848 h.origin = (uint16)f[5]; h.destination = (uint16)f[6]; h.ttl = f[7]; h.priority = (uint16)f[8];
3849 h.policy = (uint8)f[9]; h.from = (uint32)f[10]; h.to = (uint32)f[11]; h.tag = (uint32)f[13];
3850 h.status = (uint16)f[14]; h.reference = f[15]; h.serial = f[16]; h.cyclecputime = (uint32)f[18];
3851 h.cyclewalltime = (uint32)f[19]; h.chaincputime = (uint32)f[20]; h.chainwalltime = (uint32)f[21];
3852 h.chaincount = (uint32)f[22]; h.systemid = (uint32)f[23];
3853 size_t start = o.size();
3854 o.append((const char*)&h, sizeof(h));
3855 uint64 count = r.varint();
3856 if (!r.ok || count > n) return false;
3857 for (uint64 i = 0; i < count; i++) {
3858 uint64 kn = r.varint();
3859 const char* key = r.bytes(kn);
3860 uint8 kind = r.byte();
3861 if (!r.ok || !kn || memchr(key, 0, (size_t)kn)) return false;
3862 switch (kind) {
3863 case PK_TEXT: { uint64 vn = r.varint(); const char* v = r.bytes(vn); if (!r.ok) return false;
3864 PkEntry(o, key, (uint32)kn, CONSTCHARID, v, (uint32)vn, true); break; }
3865 case PK_INT: { uint64 z = r.varint(); if (!r.ok) return false;
3866 int64 x = (int64)(z >> 1) ^ -(int64)(z & 1);
3867 PkEntry(o, key, (uint32)kn, INTID, (const char*)&x, 8, false); break; }
3868 case PK_DOUBLE: { const char* v = r.bytes(8); if (!r.ok) return false;
3869 PkEntry(o, key, (uint32)kn, DOUBLEID, v, 8, false); break; }
3870 case PK_TIME: { uint64 x = r.varint(); if (!r.ok) return false;
3871 PkEntry(o, key, (uint32)kn, TIMEID, (const char*)&x, 8, false); break; }
3872 case PK_DATA: { uint64 vn = r.varint(); const char* v = r.bytes(vn); if (!r.ok) return false;
3873 PkEntry(o, key, (uint32)kn, CHARDATAID, v, (uint32)vn, false); break; }
3874 case PK_MESSAGE: { uint64 vn = r.varint(); const char* v = r.bytes(vn); if (!r.ok) return false;
3875 std::string nested;
3876 if (!PkDecode(v, (uint32)vn, nested, depth + 1)) return false;
3877 PkEntry(o, key, (uint32)kn, DATAMESSAGEID, nested.data(), (uint32)nested.size(), false); break; }
3878 case PK_RAW: { uint64 c = r.varint(); uint64 vn = r.varint(); const char* v = r.bytes(vn);
3879 if (!r.ok || c > 0xFFFFFFFFULL || c == CONSTCHARID || c == UNUSEDID) return false;
3880 PkEntry(o, key, (uint32)kn, (uint32)c, v, (uint32)vn, false); break; }
3881 default:
3882 return false;
3883 }
3884 }
3885 if (r.p != r.e) return false; // trailing bytes: not what the sender wrote
3886 DataMessageHeader* out = (DataMessageHeader*)&o[start];
3887 out->size = (uint32)(o.size() - start);
3888 out->userSize = out->size - (uint32)sizeof(DataMessageHeader);
3889 out->userCount = (uint32)count;
3890 return true;
3891 }
3892}
3893
3894char* DataMessage::toPacked(uint32& size) const {
3895 size = 0;
3896 std::string o;
3897 if (!data || !PkEncode(data, o, 0))
3898 return NULL;
3899 char* buf = (char*)malloc(o.size());
3900 if (!buf)
3901 return NULL;
3902 memcpy(buf, o.data(), o.size());
3903 size = (uint32)o.size();
3904 return buf;
3905}
3906
3907bool DataMessage::IsPacked(const char* data, uint32 size) {
3908 if (!data || size < 2 * sizeof(uint32))
3909 return false;
3910 uint32 cid;
3911 memcpy(&cid, data + sizeof(uint32), sizeof(uint32));
3912 return cid == PACKEDDATAMESSAGEID;
3913}
3914
3915DataMessage* DataMessage::FromPacked(const char* data, uint32 size) {
3916 std::string o;
3917 if (!IsPacked(data, size) || !PkDecode(data, size, o, 0))
3918 return NULL;
3919 ((DataMessageHeader*)&o[0])->cenc |= DATAMESSAGE_CENC_PACKED;
3920 char* buf = (char*)malloc(o.size());
3921 if (!buf)
3922 return NULL;
3923 memcpy(buf, o.data(), o.size());
3924 return new DataMessage(buf); // adopts the block
3925}
3926
3927} // namespace cmlabs
#define PKFAIL(what)
The binary DataMessage container — the central data-exchange object of Psyclone/CMSDK.
#define DATAMESSAGE_CENC_PACKED
cenc flag: this message arrived in the packed wire form (DataMessage::FromPacked() sets it).
#define CURRENTDATAMESSAGEVERSION
#define CONSTCHARID
Definition ObjectIDs.h:38
#define DATAMESSAGEOLDID
Definition ObjectIDs.h:53
#define CHARDATAID
Definition ObjectIDs.h:40
#define CONSTCHARINFOID
Definition ObjectIDs.h:43
#define DOUBLEID
Definition ObjectIDs.h:42
#define DATAMESSAGEINFOID
Definition ObjectIDs.h:69
#define PACKEDDATAMESSAGEID
Packed (compact) wire form of a DataMessage, see DataMessage::toPacked().
Definition ObjectIDs.h:77
#define DATAMESSAGEID
Definition ObjectIDs.h:75
#define INTID
Definition ObjectIDs.h:41
#define GetObjID(data)
Extract the cid field from a binary object block: the uint32 at byte offset 4 (after the leading size...
Definition ObjectIDs.h:26
#define UNUSEDID
Definition ObjectIDs.h:37
#define CHARDATAINFOID
Definition ObjectIDs.h:44
#define TIMEID
Definition ObjectIDs.h:39
#define DATAMESSAGEDRAFTID
Definition ObjectIDs.h:70
CMSDK time: µs-resolution 64-bit timestamps and the Time Mapping Constant (TMC).
#define PSYCONTEXTLEVELS
Number of hierarchy levels in a PsyContext.
Definition Types.h:267
#define MAXVALUINT32
Definition Types.h:87
#define PSYTYPELEVELS
Number of hierarchy levels in a PsyType.
Definition Types.h:100
Small, dependency-free unit test harness used by all CMSDK object tests.
#define stricmp
Definition Utils.h:132
#define LogPrint
Definition Utils.h:313
bool setTime(const char *key, uint64 value)
setTime(const char* key, uint64 value)
std::map< int64, float64 > getAsFloatArray(const char *key)
getAsFloatArray(const char* key)
bool setFloatArray(const char *key, std::map< int64, float64 > &map)
setFloatArray(const char* key, std::map<int64, float64>& map) Sets the whole float array named key in...
static bool UnitTest()
Run the built-in self test for the DataMessage implementation.
DataMessage()
DataMessage() Creates an empty DataMessage.
bool getDouble(const char *key, double &value)
getDouble(const char* key, double& value)
~DataMessage()
~DataMessage() Message destructor
std::string toXML(std::map< uint16, std::string > *subtypes=NULL, std::map< uint16, std::string > *subcontexts=NULL, std::map< uint32, std::string > *compNames=NULL)
toXML()
bool deleteEntry(const char *key)
deleteEntry(const char* key)
uint32 removeCollectionEntries(const char *key)
removeEntry(const char* key)
static DataMessage * FindClosestMessage(uint64 t, std::map< uint64, DataMessage * > &messages)
FindClosestMessage(uint64 t, std::map<uint64, DataMessage*> &messages) In a map of time -> DataMessag...
bool setFloat(const char *key, float64 value)
setFloat(const char* key, float64 value)
std::map< int64, int64 > getIntArray(const char *key)
getIntArray(const char* key)
bool addTimeUsage(uint32 cycleCPUTime, uint32 cycleWallTime, uint32 chainCPUTime=0, uint32 chainWallTime=0, uint32 chainCount=0)
addTimeUsage()
std::map< std::string, double > getDoubleMap(const char *key)
returns the user double entry with this key
bool setString(const char *key, const char *value)
setString(const char* key, const char* value)
bool getInt(const char *key, int64 &value)
getInt(const char* key, int64& value)
bool setInt(const char *key, int64 value)
setInt(const char* key, int64 value)
bool isSingleValue(const char *key)
isSingleValue(const char* key)
bool hasData(const char *key)
Check if key variable exists and is a data entry.
uint32 getMapCount(const char *key)
getMapCount(const char* key) Returns the number of elements in map with name '<key>'
std::map< std::string, uint64 > getTimeMap(const char *key)
returns the user time entry with this key
std::map< std::string, int64 > getIntMap(const char *key)
returns the user integer entry with this key
uint32 getArraySize(const char *key)
getArraySize(const char* key) Returns the number of elements in array with name '<key>'
char * getDataCopy(const char *key, uint32 &size)
getDataCopy(const char* key, uint32& size)
bool setDoubleMap(const char *key, std::map< std::string, double > &map)
sets the user double entry with this key to the value provided
static bool IsPacked(const char *data, uint32 size)
True if data (at least 8 bytes) starts a packed message.
std::string getAsString(const char *key)
getAsString(const char* key)
DataMessageHeader * data
Pointer to the message's flat memory block (header + user entries).
std::string toJSON(std::map< uint16, std::string > *subtypes=NULL, std::map< uint16, std::string > *subcontexts=NULL, std::map< uint32, std::string > *compNames=NULL)
toJSON()
std::map< int64, float64 > getFloatArray(const char *key)
getFloatArray(const char* key)
std::string toCSV(const char *separator=NULL, const char *preample=NULL, std::map< uint16, std::string > *subtypes=NULL, std::map< uint16, std::string > *subcontexts=NULL, std::map< uint32, std::string > *compNames=NULL)
toCSV()
bool hasFloat(const char *key)
Check if key variable exists and is a float.
bool setIntMap(const char *key, std::map< std::string, int64 > &map)
sets the user integer entry with this key
char * findEntry(const char *key)
findEntry(const char* key)
const char * getData(const char *key, uint32 &size)
getData(const char* key, uint32& size)
bool getAsBool(const char *key)
getAsBool(const char* key)
uint32 getUserCount()
getUserCount()
uint32 getSize()
getSize() Get message size Many types of data of any size can be put into a message as user entries; ...
static bool ConvertDataFromOlderMessageFormat(const char *data, char **newData)
Convert a binary block in the legacy (pre-version-10, DATAMESSAGEOLDID) format to the current DataMes...
uint32 copyUserEntriesFromMessage(DataMessage *msg)
copyUserEntriesFromMessage(DataMessage* msg) Copies all user data from a message into this message,...
std::string getUserEntriesAsJSON(bool asText)
getUserEntriesAsJSON(bool asText) Returns the JSON representation of the full message including all u...
bool setAttachedMessage(const char *key, DataMessage *msg)
setAttachedMessage(const char* key, DataMessage* msg)
bool setTimeArray(const char *key, std::map< int64, uint64 > &map)
setTimeArray(const char* key, std::map<int64, uint64>& map) Sets the whole time array named key in on...
std::map< std::string, std::string > getStringMap(const char *key)
returns the user string entry with this key
std::map< std::string, KeyType > getAllKeys()
getAllKeys()
char * toPacked(uint32 &size) const
Encode this message in the packed wire form.
std::map< int64, std::string > getStringArray(const char *key)
getStringArray(const char* key)
bool setAttachedMessageArray(const char *key, std::map< int64, DataMessage * > &map)
setAttachedMessageArray(const char* key, std::map<int64, DataMessage*>& map) Sets the whole array of ...
bool removeEntry(const char *key)
bool hasTime(const char *key)
Check if key variable exists and is a time.
std::map< int64, std::string > getAsStringArray(const char *key)
getAsStringArray(const char* key)
bool setIntArray(const char *key, std::map< int64, int64 > &map)
setIntArray(const char* key, std::map<int64, int64>& map) Sets the whole integer array named key in o...
std::map< std::string, DataMessage * > getAttachedMessageMap(const char *key)
returns a copy of the user message entry with this key
bool setTimeMap(const char *key, std::map< std::string, uint64 > &map)
sets the user time entry with this key
uint32 getContentSize(const char *key)
getContentSize(const char* key)
void invalidateFindKeyCache()
Internal: clears the single-entry key-lookup cache.
uint32 getArrayCount(const char *key)
getArrayCount(const char* key) Returns the number of elements in array with name '<key>'
std::map< std::string, int64 > getAsIntMap(const char *key)
returns the user entry as an integer map regardless of what type it is
bool setFloatMap(const char *key, std::map< std::string, float64 > &map)
sets the user float entry with this key to the value provided
uint64 getAsTime(const char *key)
getAsTime(const char* key)
bool isMapContent(const char *key)
isMapContent(const char* key)
bool hasKey(const char *key)
Check if key variable exists.
uint64 getTime(const char *key)
getTime(const char* key)
bool getFloat(const char *key, float64 &value)
getFloat(const char* key, float64& value)
bool setRawData(const char *key, const char *value, uint32 size, uint32 datatype)
Internal: low-level entry writer used by all typed set...() overloads.
bool setDouble(const char *key, double value)
setDouble(const char* key, double value)
std::map< std::string, std::string > getAsStringMap(const char *key)
returns the user entry as a string regardless of what type it is
int64 getAsInt(const char *key)
getAsInt(const char* key)
bool setDoubleArray(const char *key, std::map< int64, double > &map)
setDoubleArray(const char* key, std::map<int64, double>& map) Sets the whole double array named key i...
bool setStringMap(const char *key, std::map< std::string, std::string > &map)
sets the user string entry with this key
bool isArrayContent(const char *key)
isArrayContent(const char* key)
DataMessage * getAttachedMessageCopy(const char *key)
getAttachedMessageCopy(const char* key)
bool isValid()
isValid() Checks that the message memory block exists and carries the current-format object id (DATAM...
bool hasMessage(const char *key)
Check if key variable exists and is a message.
char * getRawData()
getRawData() Retrieve a pointer to the raw binary block of the message (header + entries).
const char * swapMessageData(const char *data)
swapMessageData(const char* data)
std::map< int64, uint64 > getTimeArray(const char *key)
uint32 getMapSize(const char *key)
getMapSize(const char* key) Returns the number of elements in map with name '<key>'
std::map< int64, double > getDoubleArray(const char *key)
getDoubleArray(const char* key)
bool setData(const char *key, const char *value, uint32 size)
setData(const char* key, const char* value, uint32 size)
bool hasString(const char *key)
Check if key variable exists and is a string.
std::map< int64, DataMessage * > getAttachedMessageArray(const char *key)
getAttachedMessageArray(const char* key)
const char * getString(const char *key)
getString(const char* key)
char * findSpace(uint32 space)
findSpace(uint32 space)
static std::string GetCSVHeader(const char *separator=NULL, const char *preample=NULL)
toCSVHeader()
bool fillInDraftUserDataFrom(const char *data, uint32 maxDraftSize)
fillInDraftUserDataFrom(const char* data, uint32 maxDraftSize)
KeyType getKeyType(const char *key)
getKeyType(const char* key)
uint32 getContentType(const char *key)
getContentType(const char* key)
bool setSerial(uint64 serial)
setSerial(uint64 serial)
bool setStringArray(const char *key, std::map< int64, std::string > &map)
setStringArray(const char* key, std::map<int64, std::string>& map) Sets the whole string array named ...
DataMessage * copy()
copy() Creates a full deep copy of the message and returns it.
bool hasInteger(const char *key)
Check if key variable exists and is an integer.
std::map< int64, int64 > getAsIntArray(const char *key)
getAsIntArray(const char* key)
bool setAttachedMessageMap(const char *key, std::map< std::string, DataMessage * > &map)
sets the user message entry with this key to the provided message
static DataMessage * FromPacked(const char *data, uint32 size)
Decode a packed message (as written by toPacked() or the Kotlin codec).
std::string getUserEntriesAsString()
getUserEntriesAsString() Returns the textual representation of the full message including all user en...
float64 getAsFloat(const char *key)
getAsFloat(const char* key)
std::map< std::string, float64 > getFloatMap(const char *key)
returns the user float entry with this key
std::map< std::string, float64 > getAsFloatMap(const char *key)
returns the user entry as a float map regardless of what type it is
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
std::string PrintTimeString(uint64 t, bool local=true, bool us=true, bool ms=true)
Definition PsyTime.cpp:676
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
Definition PsyTime.cpp:69
bool TextStartsWith(const char *str, const char *start, bool caseSensitive=true)
Test whether str starts with start.
Definition Utils.cpp:8694
bool TextEndsWith(const char *str, const char *end, bool caseSensitive=true)
Test whether str ends with end.
Definition Utils.cpp:8687
uint32 strcpyavail(char *dst, const char *src, uint32 maxlen, bool copyAvailable)
Bounded strcpy that always NUL-terminates.
Definition Utils.cpp:7510
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void progress(int percent, const char *action)
Report progress with a short description of the current action.
std::string TextIndent(const char *text, const char *indent)
Prefix every line of text with indent.
Definition Utils.cpp:7573
uint32 StringSingleReplace(std::string &text, std::string key, std::string value, bool onlyFirst)
Replace occurrences of key with value in text.
Definition Utils.cpp:8098
std::string StringFormat(const char *format,...)
printf into a std::string.
Definition Utils.cpp:8080
uint64 Ascii2Uint64(const char *ascii, uint32 start=0, uint32 end=0)
Parse an unsigned 64-bit decimal integer from a substring.
Definition Utils.cpp:8934
int64 Ascii2Int64(const char *ascii, uint32 start=0, uint32 end=0)
Parse a signed 64-bit decimal integer from a substring.
Definition Utils.cpp:8916
float64 Ascii2Float64(const char *ascii, uint32 start=0, uint32 end=0)
Parse a 64-bit float from a substring (decimal point, not locale dependent).
Definition Utils.cpp:9000
void Register_DataMessage_Tests()
static struct PsyType CTRL_TEST
Definition ObjectIDs.h:84
Hierarchical message type identifier — the key used for publish/subscribe matching in Psyclone.
Definition Types.h:123
Header preceding every user entry inside a DataMessage's flat memory block.
uint32 size
Total size of this entry in bytes, including this header, the key and the payload.
Legacy (pre-version-10) DataMessage wire header, kept only for format conversion.
The current (version 10) DataMessage wire/shared-memory header.
uint64 sendtime
Time the message was posted/sent, in microseconds.
std::string toJSON(std::map< uint16, std::string > *subtypes=NULL, std::map< uint16, std::string > *subcontexts=NULL, std::map< uint32, std::string > *compNames=NULL)
Render the header fields as a JSON object (header only, no user entries).
uint64 reference
Reference id linking this message to another (e.g.
uint16 destination
NodeID of the target node (0 = local/any).
uint32 chaincputime
Accumulated CPU time across the whole causal message chain, in microseconds.
uint64 serial
Serial number, e.g.
uint64 time
Creation time in microseconds since epoch (see PsyTime).
uint64 memid
Shared-memory id assigned when the message is stored in a memory map.
uint32 size
Total size of the whole message block in bytes (header + all entries).
uint32 userCount
Number of user entries currently in use.
uint16 status
User/application status code.
PsyType type
Hierarchical message type (e.g.
uint16 priority
Scheduling priority; higher values are processed first by whiteboards and modules.
uint32 userSize
Total size in bytes of all user entries currently in use.
uint8 policy
Delivery policy flags, e.g.
uint32 cyclewalltime
Wall-clock time spent in this cycle, in microseconds (profiling).
uint32 cid
Object id, always DATAMESSAGEID for a valid current-format message; used by isValid().
uint16 cver
Message format version (CURRENTDATAMESSAGEVERSION).
uint32 chainwalltime
Accumulated wall time across the whole causal message chain, in microseconds.
uint64 ttl
Time-to-live in microseconds; 0 means deliver-and-forget (not retained in memory).
std::string toXML(std::map< uint16, std::string > *subtypes=NULL, std::map< uint16, std::string > *subcontexts=NULL, std::map< uint32, std::string > *compNames=NULL)
Render the header fields as an XML fragment (header only, no user entries).
PsyContext contextchange
Context switch this message announces (NOCONTEXT if none).
uint32 cyclecputime
CPU time spent processing the triggering message in this cycle, in microseconds (profiling).
uint32 to
Component id of the addressee; 0 for publish/subscribe distribution.
uint16 origin
NodeID of the node the message originated on.
uint32 chaincount
Number of messages in the causal chain leading to (and including) this one.
uint32 from
Component id of the sender.
uint64 recvtime
Time the message was received by the current component, in microseconds.
uint16 cenc
Encoding flags (reserved for compression/encryption of the payload).
uint32 tag
User tag id for temporal/semantic grouping of related messages.