annotate projects/heavy/circularBuffer/HvMessage.c @ 163:20b52283c7b4 heavy-updated

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