CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
gen_fixtures.cpp
Go to the documentation of this file.
1// Golden DataMessage fixtures from the C++ CMSDK: the reference encoder for non-C++ codecs.
2//
3// The Kotlin codec's tests decode every fixture and check header fields and entries against
4// expected.json; for the 'compact' fixtures (no deleted or shrunk slots) they also re-encode and
5// require byte-for-byte equality with these files. Regenerate after any DataMessage change:
6//
7// make -C kotlin/tools (from CMSDK/, after `make` / `make ssl`)
8//
9// Writes kotlin/fixtures/<name>.bin, expected.json and layout.json (via the layout tool).
10#include "DataMessage.h"
11#include "ObjectIDs.h"
12#include "PsyTime.h"
13#include <cstdio>
14#include <string>
15#include <vector>
16
17using namespace cmlabs;
18
19static const uint64 T0 = 63960700000000000ULL; // fixed PsyTime (us since year 0): reproducible files
20
21struct Entry { std::string key, kind, json; };
22struct Fixture { std::string name; DataMessage* msg; bool compact; std::vector<Entry> entries; };
23
24static std::string Esc(const std::string& s) {
25 std::string o;
26 for (unsigned char c : s) {
27 if (c == '"' || c == '\\') { o += '\\'; o += (char)c; }
28 else if (c < 0x20) { char b[8]; snprintf(b, sizeof(b), "\\u%04x", c); o += b; }
29 else o += (char)c;
30 }
31 return o;
32}
33static std::string Hex(const char* d, uint32 n) {
34 static const char* h = "0123456789abcdef";
35 std::string o;
36 for (uint32 i = 0; i < n; i++) { o += h[((uint8)d[i]) >> 4]; o += h[((uint8)d[i]) & 15]; }
37 return o;
38}
39static DataMessage* Base() {
40 DataMessage* m = new DataMessage();
42 m->setSendTime(T0 + 10);
43 m->setRecvTime(T0 + 20);
44 return m;
45}
46static Entry S(const char* k, const char* v) { return Entry{ k, "string", "\"" + Esc(v) + "\"" }; }
47static Entry I(const char* k, int64 v) { return Entry{ k, "int", std::to_string((long long)v) }; }
48
49int main(int argc, char** argv) {
50 std::string out = argc > 1 ? argv[1] : "kotlin/fixtures";
51 std::vector<Fixture> fx;
52
53 fx.push_back({ "empty", Base(), true, {} });
54
55 DataMessage* m = Base();
56 m->setString("Text", "hello");
57 m->setInt("Serial", 4242);
58 m->setInt("Negative", -7);
59 m->setDouble("Gain", 0.75);
60 m->setTime("TCap", T0 + 123456);
61 fx.push_back({ "scalars", m, true, { S("Text", "hello"), I("Serial", 4242), I("Negative", -7),
62 Entry{ "Gain", "double", "0.75" }, Entry{ "TCap", "time", std::to_string((unsigned long long)(T0 + 123456)) } } });
63
64 m = Base();
65 char pcm[320];
66 for (int i = 0; i < 320; i++) pcm[i] = (char)(i < 256 ? i : 0); // includes NULs on purpose
67 m->setString("PsyPost", "AudioFrame");
68 m->setString("PsyDevice", "phone-s25");
69 m->setData("pcm", pcm, 320);
70 m->setString("Unicode", "M\xC3\xADmir \xE2\x80\x93 \xC3\xA6\xC3\xB8\xC3\xA5 \xE2\x9C\x93");
71 fx.push_back({ "audio_frame", m, true, { S("PsyPost", "AudioFrame"), S("PsyDevice", "phone-s25"),
72 Entry{ "pcm", "data", "\"" + Hex(pcm, 320) + "\"" },
73 S("Unicode", "M\xC3\xADmir \xE2\x80\x93 \xC3\xA6\xC3\xB8\xC3\xA5 \xE2\x9C\x93") } });
74
75 m = Base();
76 PsyType t; t.fromString("16.34.35");
77 m->setType(t);
78 PsyContext c; c.fromString("3.7");
79 m->setContextChange(c);
80 m->setTag(77);
81 m->setSerial(9001);
82 m->setReference(123456789012ULL);
83 m->setTTL(3000000);
84 m->setPriority(9);
85 m->setPolicy(1);
86 m->setFrom(12);
87 m->setTo(34);
88 m->setStatus(5);
89 // Every remaining header field gets a distinct non-zero value through the struct itself, so a
90 // codec with a wrong offset reads a wrong value (zeros would hide it).
91 m->data->cenc = 0x0102;
92 m->data->memid = 0x1112131415161718ULL;
93 m->data->origin = 0x2122;
94 m->data->destination = 0x2324;
95 m->data->cyclecputime = 0x31323334;
96 m->data->cyclewalltime = 0x35363738;
97 m->data->chaincputime = 0x41424344;
98 m->data->chainwalltime = 0x45464748;
99 m->data->chaincount = 0x51525354;
100 m->data->systemid = 0x61626364;
101 m->setString("k", "v");
102 fx.push_back({ "header_fields", m, true, { S("k", "v") } });
103
104 DataMessage* inner = Base();
105 inner->setString("Inner", "yes");
106 inner->setInt("N", 3);
107 m = Base();
108 m->setAttachedMessage("Attached", inner);
109 m->setString("After", "tail");
110 fx.push_back({ "attached", m, true, { Entry{ "Attached", "message", "{\"Inner\":\"yes\",\"N\":3}" }, S("After", "tail") } });
111 delete inner;
112
113 // Not compact: a shrinking overwrite (slot keeps its old size) and a deleted slot (UNUSEDID).
114 // Decoders must step by the KEY header's size and skip UNUSED slots.
115 m = Base();
116 m->setString("Long", "a fairly long original value");
117 m->setString("Long", "short");
118 m->setString("Gone", "to be deleted");
119 m->deleteEntry("Gone");
120 m->setInt("Kept", 1);
121 fx.push_back({ "overwrite_delete", m, false, { S("Long", "short"), I("Kept", 1) } });
122
123 std::string js = "{\n \"generator\": \"kotlin/tools/gen_fixtures.cpp (C++ CMSDK)\",\n \"fixtures\": [\n";
124 for (size_t i = 0; i < fx.size(); i++) {
125 DataMessage* d = fx[i].msg;
126 std::string path = out + "/" + fx[i].name + ".bin";
127 FILE* f = fopen(path.c_str(), "wb");
128 if (!f) { fprintf(stderr, "cannot write %s\n", path.c_str()); return 1; }
129 fwrite(d->data, 1, d->data->size, f);
130 fclose(f);
131 DataMessageHeader* h = d->data;
132 char hdr[2048];
133 snprintf(hdr, sizeof(hdr),
134 "\"header\": {\"size\":%u,\"time\":%llu,\"sendtime\":%llu,\"recvtime\":%llu,\"ttl\":%llu,"
135 "\"priority\":%u,\"policy\":%u,\"from\":%u,\"to\":%u,\"tag\":%u,\"status\":%u,\"reference\":%llu,"
136 "\"serial\":%llu,\"type\":\"%s\",\"contextchange\":\"%s\",\"userCount\":%u,\"userSize\":%u,"
137 "\"cenc\":%u,\"memid\":%llu,\"origin\":%u,\"destination\":%u,\"cyclecputime\":%u,\"cyclewalltime\":%u,"
138 "\"chaincputime\":%u,\"chainwalltime\":%u,\"chaincount\":%u,\"systemid\":%u}",
139 h->size, (unsigned long long)h->time, (unsigned long long)h->sendtime, (unsigned long long)h->recvtime,
140 (unsigned long long)h->ttl, h->priority, h->policy, h->from, h->to, h->tag, h->status,
141 (unsigned long long)h->reference, (unsigned long long)h->serial, h->type.toString().c_str(),
142 h->contextchange.toString().c_str(), h->userCount, h->userSize,
143 h->cenc, (unsigned long long)h->memid, h->origin, h->destination, h->cyclecputime, h->cyclewalltime,
145 js += " {\"name\":\"" + fx[i].name + "\",\"compact\":" + (fx[i].compact ? "true" : "false") + ",\n " + hdr + ",\n \"entries\": [";
146 for (size_t e = 0; e < fx[i].entries.size(); e++)
147 js += (e ? "," : "") + std::string("\n {\"key\":\"") + fx[i].entries[e].key + "\",\"kind\":\"" +
148 fx[i].entries[e].kind + "\",\"value\":" + fx[i].entries[e].json + "}";
149 js += "]}" + std::string(i + 1 < fx.size() ? ",\n" : "\n");
150 printf("%-18s %5u bytes compact=%s\n", fx[i].name.c_str(), h->size, fx[i].compact ? "yes" : "no");
151 delete d;
152 }
153 js += " ]\n}\n";
154 FILE* f = fopen((out + "/expected.json").c_str(), "wb");
155 if (!f) return 1;
156 fwrite(js.data(), 1, js.size(), f);
157 fclose(f);
158 return 0;
159}
The binary DataMessage container — the central data-exchange object of Psyclone/CMSDK.
Object type ids used to tag and verify every binary structure in CMSDK memory.
CMSDK time: µs-resolution 64-bit timestamps and the Time Mapping Constant (TMC).
The central Psyclone data container: a self-contained binary message with typed, named user entries.
bool setTime(const char *key, uint64 value)
setTime(const char* key, uint64 value)
bool setTTL(uint64 ttl)
setTTL(uint64 ttl)
bool deleteEntry(const char *key)
deleteEntry(const char* key)
bool setPriority(uint16 priority)
setPriority(uint16 priority)
bool setString(const char *key, const char *value)
setString(const char* key, const char* value)
bool setInt(const char *key, int64 value)
setInt(const char* key, int64 value)
bool setTag(uint32 tag)
setTag(uint32 tag)
DataMessageHeader * data
Pointer to the message's flat memory block (header + user entries).
bool setStatus(uint16 status)
setStatus(uint16 status)
bool setAttachedMessage(const char *key, DataMessage *msg)
setAttachedMessage(const char* key, DataMessage* msg)
bool setRecvTime(uint64 time)
setRecvTime(uint64 time)
bool setFrom(uint32 from)
setFrom(uint32 from)
bool setTo(uint32 to)
setTo(uint32 to)
bool setCreatedTime(uint64 t)
setCreatedTime(uint64 t)
bool setReference(uint64 ref)
setReference(uint64 ref) Set message reference
bool setType(PsyType &type)
setType(PsyType &type)
bool setDouble(const char *key, double value)
setDouble(const char* key, double value)
bool setPolicy(uint8 policy)
setPolicy(uint8 policy)
bool setData(const char *key, const char *value, uint32 size)
setData(const char* key, const char* value, uint32 size)
bool setSerial(uint64 serial)
setSerial(uint64 serial)
bool setContextChange(PsyContext &context)
setContextChange(PsyContext& context)
bool setSendTime(uint64 time)
setSendTime(uint64 time)
static Entry S(const char *k, const char *v)
static Entry I(const char *k, int64 v)
static const uint64 T0
static DataMessage * Base()
static std::string Esc(const std::string &s)
static std::string Hex(const char *d, uint32 n)
int main()
Definition layout.cpp:23
std::string json
std::string key
std::string kind
std::vector< Entry > entries
std::string name
DataMessage * msg
Hierarchical execution-context identifier.
Definition Types.h:284
bool fromString(const char *text)
Definition Types.h:374
std::string toString()
Definition Types.h:341
Hierarchical message type identifier — the key used for publish/subscribe matching in Psyclone.
Definition Types.h:123
bool fromString(const char *text)
Parse dotted decimal notation (e.g.
Definition Types.h:233
std::string toString()
Render as dotted decimal notation, e.g. "2.201.3"; empty string if invalid.
Definition Types.h:195
The current (version 10) DataMessage wire/shared-memory header.
uint64 sendtime
Time the message was posted/sent, in microseconds.
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 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).
PsyContext contextchange
Context switch this message announces (NOCONTEXT if none).
uint32 systemid
Id of the Psyclone system instance that sent the message.
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.