chris@160
|
1 /**
|
chris@160
|
2 * Copyright (c) 2014, 2015, Enzien Audio Ltd.
|
chris@160
|
3 *
|
chris@160
|
4 * Permission to use, copy, modify, and/or distribute this software for any
|
chris@160
|
5 * purpose with or without fee is hereby granted, provided that the above
|
chris@160
|
6 * copyright notice and this permission notice appear in all copies.
|
chris@160
|
7 *
|
chris@160
|
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
chris@160
|
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
chris@160
|
10 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
chris@160
|
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
chris@160
|
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
chris@160
|
13 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
chris@160
|
14 * PERFORMANCE OF THIS SOFTWARE.
|
chris@160
|
15 */
|
chris@160
|
16
|
chris@160
|
17 #include "HvMessage.h"
|
chris@160
|
18
|
chris@160
|
19 HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp) {
|
chris@160
|
20 m->timestamp = timestamp;
|
chris@160
|
21 m->numElements = (hv_uint16_t) numElements;
|
chris@160
|
22 m->numBytes = (hv_uint16_t) msg_getByteSize(numElements);
|
chris@160
|
23 return m;
|
chris@160
|
24 }
|
chris@160
|
25
|
chris@160
|
26 HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f) {
|
chris@160
|
27 m->timestamp = timestamp;
|
chris@160
|
28 m->numElements = 1;
|
chris@160
|
29 m->numBytes = sizeof(HvMessage);
|
chris@160
|
30 msg_setFloat(m, 0, f);
|
chris@160
|
31 return m;
|
chris@160
|
32 }
|
chris@160
|
33
|
chris@160
|
34 HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp) {
|
chris@160
|
35 m->timestamp = timestamp;
|
chris@160
|
36 m->numElements = 1;
|
chris@160
|
37 m->numBytes = sizeof(HvMessage);
|
chris@160
|
38 msg_setBang(m, 0);
|
chris@160
|
39 return m;
|
chris@160
|
40 }
|
chris@160
|
41
|
chris@160
|
42 HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, char *s) {
|
chris@160
|
43 m->timestamp = timestamp;
|
chris@160
|
44 m->numElements = 1;
|
chris@160
|
45 m->numBytes = sizeof(HvMessage);
|
chris@160
|
46 msg_setSymbol(m, 0, s);
|
chris@160
|
47 return m;
|
chris@160
|
48 }
|
chris@160
|
49
|
chris@160
|
50 HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h) {
|
chris@160
|
51 m->timestamp = timestamp;
|
chris@160
|
52 m->numElements = 1;
|
chris@160
|
53 m->numBytes = sizeof(HvMessage);
|
chris@160
|
54 msg_setHash(m, 0, h);
|
chris@160
|
55 return m;
|
chris@160
|
56 }
|
chris@160
|
57
|
chris@160
|
58 HvMessage *msg_initV(HvMessage *const m, const hv_uint32_t timestamp, const char *format, ...) {
|
chris@160
|
59 va_list ap;
|
chris@160
|
60 va_start(ap, format);
|
chris@160
|
61
|
chris@160
|
62 const int numElem = (int) hv_strlen(format);
|
chris@160
|
63 msg_init(m, numElem, timestamp);
|
chris@160
|
64 for (int i = 0; i < numElem; i++) {
|
chris@160
|
65 switch (format[i]) {
|
chris@160
|
66 case 'b': msg_setBang(m,i); break;
|
chris@160
|
67 case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break;
|
chris@160
|
68 case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break;
|
chris@160
|
69 case 'h': // hash not supported
|
chris@160
|
70 default: break;
|
chris@160
|
71 }
|
chris@160
|
72 }
|
chris@160
|
73 va_end(ap);
|
chris@160
|
74
|
chris@160
|
75 return m;
|
chris@160
|
76 }
|
chris@160
|
77
|
chris@160
|
78 hv_size_t msg_getNumHeapBytes(const HvMessage *m) {
|
chris@160
|
79 // get the size of all symbol elements
|
chris@160
|
80 hv_size_t rsizeofsym = 0;
|
chris@160
|
81 for (int i = 0; i < msg_getNumElements(m); ++i) {
|
chris@160
|
82 if (msg_isSymbol(m,i)) {
|
chris@160
|
83 rsizeofsym += (hv_size_t) hv_strlen(msg_getSymbol(m,i)) + 1; // +1 to allow for trailing '\0'
|
chris@160
|
84 }
|
chris@160
|
85 }
|
chris@160
|
86
|
chris@160
|
87 // the total byte size on the heap
|
chris@160
|
88 return (msg_getByteSize(msg_getNumElements(m)) + rsizeofsym);
|
chris@160
|
89 }
|
chris@160
|
90
|
chris@160
|
91 void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len) {
|
chris@160
|
92 HvMessage *r = (HvMessage *) buffer;
|
chris@160
|
93
|
chris@160
|
94 // assert that the message is not already larger than the length of the buffer
|
chris@160
|
95 hv_assert(msg_getNumBytes(m) <= len);
|
chris@160
|
96
|
chris@160
|
97 // copy the basic message to the buffer
|
chris@160
|
98 hv_memcpy(r, m, msg_getNumBytes(m));
|
chris@160
|
99
|
chris@160
|
100 hv_size_t len_r = msg_getNumBytes(m);
|
chris@160
|
101
|
chris@160
|
102 char *p = buffer + msg_getByteSize(msg_getNumElements(m)); // points to the end of the base message
|
chris@160
|
103 for (int i = 0; i < msg_getNumElements(m); ++i) {
|
chris@160
|
104 if (msg_isSymbol(m,i)) {
|
chris@160
|
105 const hv_size_t symLen = (hv_size_t) hv_strlen(msg_getSymbol(m,i)) + 1; // include the trailing null char
|
chris@160
|
106 hv_assert(len_r + symLen <= len); // stay safe!
|
chris@160
|
107 hv_strncpy(p, msg_getSymbol(m,i), symLen);
|
chris@160
|
108 msg_setSymbol(r, i, p);
|
chris@160
|
109 p += symLen;
|
chris@160
|
110 len_r += symLen;
|
chris@160
|
111 }
|
chris@160
|
112 }
|
chris@160
|
113
|
chris@160
|
114 r->numBytes = (hv_uint16_t) len_r; // update the message size in memory
|
chris@160
|
115 }
|
chris@160
|
116
|
chris@160
|
117 // the message is serialised such that all symbol elements are placed in order at the end of the buffer
|
chris@160
|
118 HvMessage *msg_copy(const HvMessage *m) {
|
chris@160
|
119 const hv_size_t heapSize = msg_getNumHeapBytes(m);
|
chris@160
|
120 char *r = (char *) hv_malloc(heapSize);
|
chris@160
|
121 msg_copyToBuffer(m, r, heapSize);
|
chris@160
|
122 return (HvMessage *) r;
|
chris@160
|
123 }
|
chris@160
|
124
|
chris@160
|
125 void msg_free(HvMessage *m) {
|
chris@160
|
126 hv_free(m); // because heap messages are serialised in memory, a simple call to free releases the message
|
chris@160
|
127 }
|
chris@160
|
128
|
chris@160
|
129 bool msg_hasFormat(const HvMessage *m, const char *fmt) {
|
chris@160
|
130 if (fmt == NULL) return false;
|
chris@160
|
131 if (msg_getNumElements(m) != hv_strlen(fmt)) return false;
|
chris@160
|
132 for (int i = 0; i < msg_getNumElements(m); i++) {
|
chris@160
|
133 switch (fmt[i]) {
|
chris@160
|
134 case 'b': if (!msg_isBang(m, i)) return false; break;
|
chris@160
|
135 case 'f': if (!msg_isFloat(m, i)) return false; break;
|
chris@160
|
136 case 's': if (!msg_isSymbol(m, i)) return false; break;
|
chris@160
|
137 case 'h': if (!msg_isHash(m, i)) return false; break;
|
chris@160
|
138 default: return false;
|
chris@160
|
139 }
|
chris@160
|
140 }
|
chris@160
|
141 return true;
|
chris@160
|
142 }
|
chris@160
|
143
|
chris@160
|
144 bool msg_compareSymbol(const HvMessage *m, int i, const char *s) {
|
chris@160
|
145 switch (msg_getType(m,i)) {
|
chris@160
|
146 case SYMBOL: return !hv_strcmp(msg_getSymbol(m, i), s);
|
chris@160
|
147 case HASH: return (msg_getHash(m,i) == msg_symbolToHash(s));
|
chris@160
|
148 default: return false;
|
chris@160
|
149 }
|
chris@160
|
150 }
|
chris@160
|
151
|
chris@160
|
152 bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n) {
|
chris@160
|
153 if (i_m < msg_getNumElements(m) && i_n < msg_getNumElements(n)) {
|
chris@160
|
154 if (msg_getType(m, i_m) == msg_getType(n, i_n)) {
|
chris@160
|
155 switch (msg_getType(m, i_m)) {
|
chris@160
|
156 case BANG: return true;
|
chris@160
|
157 case FLOAT: return (msg_getFloat(m, i_m) == msg_getFloat(n, i_n));
|
chris@160
|
158 case SYMBOL: return msg_compareSymbol(m, i_m, msg_getSymbol(n, i_n));
|
chris@160
|
159 case HASH: return msg_getHash(m,i_m) == msg_getHash(n,i_n);
|
chris@160
|
160 default: break;
|
chris@160
|
161 }
|
chris@160
|
162 }
|
chris@160
|
163 }
|
chris@160
|
164 return false;
|
chris@160
|
165 }
|
chris@160
|
166
|
chris@160
|
167 void msg_setElementToFrom(HvMessage *n, int i_n, const HvMessage *const m, int i_m) {
|
chris@160
|
168 switch (msg_getType(m, i_m)) {
|
chris@160
|
169 case BANG: msg_setBang(n, i_n); break;
|
chris@160
|
170 case FLOAT: msg_setFloat(n, i_n, msg_getFloat(m, i_m)); break;
|
chris@160
|
171 case SYMBOL: msg_setSymbol(n, i_n, msg_getSymbol(m, i_m)); break;
|
chris@160
|
172 case HASH: msg_setHash(n, i_n, msg_getHash(m, i_m));
|
chris@160
|
173 default: break;
|
chris@160
|
174 }
|
chris@160
|
175 }
|
chris@160
|
176
|
chris@160
|
177 hv_uint32_t msg_symbolToHash(const char *s) {
|
chris@160
|
178 // this hash is based MurmurHash2
|
chris@160
|
179 // http://en.wikipedia.org/wiki/MurmurHash
|
chris@160
|
180 // https://sites.google.com/site/murmurhash/
|
chris@160
|
181 static const unsigned int n = 0x5bd1e995;
|
chris@160
|
182 static const int r = 24;
|
chris@160
|
183
|
chris@160
|
184 int len = (int) hv_strlen(s);
|
chris@160
|
185 hv_uint32_t x = (hv_uint32_t) (len); // seed (0) ^ len
|
chris@160
|
186
|
chris@160
|
187 while (len >= 4) {
|
chris@160
|
188 hv_uint32_t k = *((hv_uint32_t *)s);
|
chris@160
|
189 k *= n;
|
chris@160
|
190 k ^= k >> r;
|
chris@160
|
191 k *= n;
|
chris@160
|
192 x *= n;
|
chris@160
|
193 x ^= k;
|
chris@160
|
194 s += 4; len -= 4;
|
chris@160
|
195 }
|
chris@160
|
196
|
chris@160
|
197 switch(len) {
|
chris@160
|
198 case 3: x ^= s[2] << 16;
|
chris@160
|
199 case 2: x ^= s[1] << 8;
|
chris@160
|
200 case 1: x ^= s[0]; x *= n;
|
chris@160
|
201 default: break;
|
chris@160
|
202 }
|
chris@160
|
203
|
chris@160
|
204 x ^= x >> 13;
|
chris@160
|
205 x *= n;
|
chris@160
|
206 x ^= x >> 15;
|
chris@160
|
207
|
chris@160
|
208 return x;
|
chris@160
|
209 }
|
chris@160
|
210
|
chris@160
|
211 hv_uint32_t msg_getHash(const HvMessage *const m, int i) {
|
chris@160
|
212 hv_assert(i < msg_getNumElements(m)); // invalid index
|
chris@160
|
213 switch (msg_getType(m,i)) {
|
chris@160
|
214 case BANG: return 0xFFFFFFFF;
|
chris@160
|
215 case FLOAT: {
|
chris@160
|
216 float f = msg_getFloat(m,i);
|
chris@160
|
217 return *((hv_uint32_t *) &f);
|
chris@160
|
218 }
|
chris@160
|
219 case SYMBOL: return msg_symbolToHash(msg_getSymbol(m,i));
|
chris@160
|
220 case HASH: return (&(m->elem)+i)->data.h;
|
chris@160
|
221 default: return 0;
|
chris@160
|
222 }
|
chris@160
|
223 }
|
chris@160
|
224
|
chris@160
|
225 char *msg_toString(const HvMessage *m) {
|
chris@160
|
226 hv_assert(msg_getNumElements(m) > 0);
|
chris@160
|
227 int *len = (int *) hv_alloca(msg_getNumElements(m)*sizeof(int));
|
chris@160
|
228 int size = 0; // the total length of our final buffer
|
chris@160
|
229
|
chris@160
|
230 // loop through every element in our list of atoms
|
chris@160
|
231 // first loop figures out how long our buffer should be
|
chris@160
|
232 for (int i = 0; i < msg_getNumElements(m); i++) {
|
chris@160
|
233 // length of our string is each atom plus a space, or \0 on the end
|
chris@160
|
234 switch (msg_getType(m, i)) {
|
chris@160
|
235 case BANG: len[i] = hv_snprintf(NULL, 0, "%s", "bang") + 1; break;
|
chris@160
|
236 case FLOAT: len[i] = hv_snprintf(NULL, 0, "%g", msg_getFloat(m, i)) + 1; break;
|
chris@160
|
237 case SYMBOL: len[i] = hv_snprintf(NULL, 0, "%s", msg_getSymbol(m, i)) + 1; break;
|
chris@160
|
238 case HASH: len[i] = hv_snprintf(NULL, 0, "0x%X", msg_getHash(m, i)) + 1; break;
|
chris@160
|
239 default: break;
|
chris@160
|
240 }
|
chris@160
|
241 size += len[i];
|
chris@160
|
242 }
|
chris@160
|
243
|
chris@160
|
244 hv_assert(size > 0);
|
chris@160
|
245
|
chris@160
|
246 // now we do the piecewise concatenation into our final string
|
chris@160
|
247 // the final buffer we will pass back after concatenating all strings - user should free it
|
chris@160
|
248 char *finalString = (char *) hv_malloc(size*sizeof(char));
|
chris@160
|
249 int pos = 0;
|
chris@160
|
250 for (int i = 0; i < msg_getNumElements(m); i++) {
|
chris@160
|
251 // put a string representation of each atom into the final string
|
chris@160
|
252 switch (msg_getType(m, i)) {
|
chris@160
|
253 case BANG: hv_snprintf(finalString+pos, len[i], "%s", "bang"); break;
|
chris@160
|
254 case FLOAT: hv_snprintf(finalString+pos, len[i], "%g", msg_getFloat(m, i)); break;
|
chris@160
|
255 case SYMBOL: hv_snprintf(finalString+pos, len[i], "%s", msg_getSymbol(m, i)); break;
|
chris@160
|
256 case HASH: hv_snprintf(finalString+pos, len[i], "0x%X", msg_getHash(m, i)); break;
|
chris@160
|
257 default: break;
|
chris@160
|
258 }
|
chris@160
|
259 pos += len[i];
|
chris@160
|
260 finalString[pos-1] = 32; // ASCII space
|
chris@160
|
261 }
|
chris@160
|
262 finalString[size-1] = '\0'; // ensure that the string is null terminated
|
chris@160
|
263 return finalString;
|
chris@160
|
264 }
|
chris@160
|
265
|
chris@160
|
266 /*
|
chris@160
|
267 * TODO(mhroth): unnecessary for now
|
chris@160
|
268 bool msg_resolveDollarArguments(HvMessage *m, HvMessage *n, int z, char *buf, hv_size_t len, const char *args, ...) {
|
chris@160
|
269 va_list ap;
|
chris@160
|
270 va_start(ap, args);
|
chris@160
|
271
|
chris@160
|
272 hv_memset(buf, 0, len); // clear the buffer
|
chris@160
|
273 hv_size_t j = 0; // position in buffer
|
chris@160
|
274 const hv_size_t numArgs = hv_strlen(args); // the number of arguments
|
chris@160
|
275
|
chris@160
|
276 // if there is only one argument then the result has the chance of being a number, otherwise no
|
chris@160
|
277 bool isNumber = (numArgs == 1);
|
chris@160
|
278
|
chris@160
|
279 for (hv_size_t i = 0; i < numArgs; ++i) {
|
chris@160
|
280 switch (args[i]) {
|
chris@160
|
281 case 'i': { // a message index
|
chris@160
|
282 const int index = (int) va_arg(ap, int);
|
chris@160
|
283 if (index < 0) {
|
chris@160
|
284 // $0 always resolve to "0"
|
chris@160
|
285 const hv_size_t x = 1;
|
chris@160
|
286 if (x < len-j) { // always < in order to allow for trailing \0
|
chris@160
|
287 j += snprintf(buf+j, len-j, "0");
|
chris@160
|
288 }
|
chris@160
|
289 } else {
|
chris@160
|
290 switch (msg_getType(m, index)) {
|
chris@160
|
291 default:
|
chris@160
|
292 case BANG: break; // this case should never happen
|
chris@160
|
293 case FLOAT: {
|
chris@160
|
294 const hv_size_t x = snprintf(NULL, 0, "%g", msg_getFloat(m,index));
|
chris@160
|
295 if (x < len-j) { // ensure that the buffer is big enough
|
chris@160
|
296 j += snprintf(buf+j, len-j, "%g", msg_getFloat(m,index));
|
chris@160
|
297 }
|
chris@160
|
298 break;
|
chris@160
|
299 }
|
chris@160
|
300 case SYMBOL: {
|
chris@160
|
301 const hv_size_t x = snprintf(NULL, 0, "%s", msg_getSymbol(m,index));
|
chris@160
|
302 if (x < len-j) {
|
chris@160
|
303 j += snprintf(buf+j, len-j, "%s", msg_getSymbol(m,index));
|
chris@160
|
304 isNumber = false;
|
chris@160
|
305 }
|
chris@160
|
306 break;
|
chris@160
|
307 }
|
chris@160
|
308 }
|
chris@160
|
309 }
|
chris@160
|
310 break;
|
chris@160
|
311 }
|
chris@160
|
312 case 's': { // a string
|
chris@160
|
313 const char *s = (char *) va_arg(ap, char *);
|
chris@160
|
314 const hv_size_t x = snprintf(NULL, 0, "%s", s);
|
chris@160
|
315 if (x <= len-j) {
|
chris@160
|
316 j += snprintf(buf+j, len-j, "%s", s);
|
chris@160
|
317 isNumber = false;
|
chris@160
|
318 }
|
chris@160
|
319 break;
|
chris@160
|
320 }
|
chris@160
|
321 default: break;
|
chris@160
|
322 }
|
chris@160
|
323 }
|
chris@160
|
324
|
chris@160
|
325 if (isNumber) {
|
chris@160
|
326 msg_setFloat(n,z,(float) atof(buf));
|
chris@160
|
327 } else {
|
chris@160
|
328 msg_setSymbol(n,z,buf);
|
chris@160
|
329 }
|
chris@160
|
330
|
chris@160
|
331 va_end(ap);
|
chris@160
|
332
|
chris@160
|
333 return !isNumber;
|
chris@160
|
334 }
|
chris@160
|
335 */
|