upubsub.h 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /* upubsub: a minimal publish/subscribe 1 header file library for pure ANSI C. */
  2. #ifndef UPUBSUB_H_INCLUDED
  3. #define UPUBSUB_H_INCLUDED
  4. #ifndef UPUBSUB_LISTENER_AMOUNT
  5. #define UPUBSUB_LISTENER_AMOUNT 64
  6. #endif /* UPUBSUB_LISTENER_AMOUNT */
  7. #ifndef UPUBSUB_MESSAGE_SIZE
  8. #define UPUBSUB_MESSAGE_SIZE 64
  9. #endif /* UPUBSUB_MESSAGE_SIZE */
  10. #ifndef UPUBSUB_TOPIC_AMOUNT
  11. #define UPUBSUB_TOPIC_AMOUNT 32
  12. #endif /* UPUBSUB_TOPIC_AMOUNT */
  13. #ifdef __cplusplus
  14. extern "C" {
  15. #endif
  16. #include <stdlib.h>
  17. #include <stdint.h>
  18. #include <string.h>
  19. struct upubsub_message {
  20. const char * topic;
  21. size_t size;
  22. uint8_t copy[UPUBSUB_MESSAGE_SIZE];
  23. int copy_ok;
  24. void * data;
  25. };
  26. struct upubsub_listener;
  27. struct upubsub;
  28. typedef int (upubsub_listen_func)(struct upubsub_listener *l, struct upubsub_message m);
  29. struct upubsub_listener {
  30. struct upubsub * upubsub;
  31. unsigned int id;
  32. const char * topic;
  33. void * data;
  34. upubsub_listen_func * listen;
  35. };
  36. struct upubsub {
  37. size_t size;
  38. unsigned int last_id;
  39. struct upubsub_listener listeners[UPUBSUB_LISTENER_AMOUNT];
  40. size_t topics_size;
  41. };
  42. int upubsub_listener_cmp_topic_id(const void * v1, const void * v2);
  43. int upubsub_listener_cmp_topic(const void * v1, const void * v2);
  44. int upubsub_listener_cmp_id(const void * v1, const void * v2);
  45. struct upubsub_listener * upubsub_subscribe_listener(struct upubsub * u, struct upubsub_listener l);
  46. struct upubsub_listener* upubsub_subscribe(struct upubsub * u, const char * topic, void * data, upubsub_listen_func * listen);
  47. int upubsub_publish_message(struct upubsub u, struct upubsub_message m);
  48. int upubsub_publish_data(struct upubsub u, const char * topic, void * data, size_t size);
  49. int upubsub_publish_str(struct upubsub u, const char * topic, char * str);
  50. int upubsub_unsubscribe_listener(struct upubsub * u, struct upubsub_listener * l);
  51. #ifdef __cplusplus
  52. }
  53. #endif
  54. #endif /* UPUBSUB_H_INCLUDED */
  55. #ifdef UPUBSUB_TEST
  56. #define UPUBSUB_IMPLEMENTATION
  57. #endif
  58. #ifdef UPUBSUB_IMPLEMENTATION
  59. #ifdef __cplusplus
  60. #error This is not C++. Please compile this with a C compiler.
  61. #endif
  62. /** Result of searching with brange_search. If high < low, nothing was found. */
  63. struct bsearch_range_result {
  64. int low;
  65. int high;
  66. };
  67. struct bsearch_range_result bsearch_range (const void *key, const void *base, size_t nel, size_t width, int (*cmp)(const void *, const void *)) {
  68. struct bsearch_range_result res;
  69. void *here;
  70. int sign;
  71. int found;
  72. int botm = 0;
  73. int top = nel;
  74. res.low = 0;
  75. res.high = nel;
  76. while (res.low <= top) {
  77. int mid = res.low + (top - res.low) / 2;
  78. here = (char*)base + (mid*width);
  79. sign = cmp(key, here);
  80. if (sign <= 0) {
  81. top = mid - 1;
  82. } else {
  83. res.low = mid + 1;
  84. }
  85. }
  86. botm = res.low ;
  87. while (botm <= res.high) {
  88. int mid = botm + (res.high - botm) / 2;
  89. here = (char*)base + (mid*width);
  90. sign = cmp(key, here);
  91. if (sign < 0) {
  92. res.high = mid - 1;
  93. } else {
  94. botm = mid + 1;
  95. }
  96. }
  97. return res;
  98. }
  99. int upubsub_listener_cmp_topic_id(const void * v1, const void * v2) {
  100. int res;
  101. struct upubsub_listener *l1, *l2;
  102. l1 = (struct upubsub_listener *)(v1);
  103. l2 = (struct upubsub_listener *)(v2);
  104. /* Sort with null topic as last, as these ones are unused. */
  105. if ((!l1->topic) && (l2->topic)) { return 1; }
  106. if ((!l2->topic) && (l1->topic)) { return -1; }
  107. if ((!l2->topic) && (!l1->topic)) { return 0; }
  108. /* Otherwise compare topics. */
  109. res = strcmp(l1->topic, l2->topic);
  110. /* If the same sort by id to try to keep the sort stable. */
  111. if (!res) {
  112. return l1->id - l2->id;
  113. }
  114. return res;
  115. }
  116. int upubsub_listener_cmp_topic(const void * v1, const void * v2) {
  117. struct upubsub_listener *l1, *l2;
  118. l1 = (struct upubsub_listener *)(v1);
  119. l2 = (struct upubsub_listener *)(v2);
  120. /* Sort with null topic as last, as these ones are unused. */
  121. if ((!l1->topic) && (l2->topic)) { return 1; }
  122. if ((!l2->topic) && (l1->topic)) { return -1; }
  123. if ((!l2->topic) && (!l1->topic)) { return 0; }
  124. /* Otherwise compare topics. */
  125. return strcmp(l1->topic, l2->topic);
  126. }
  127. int upubsub_listener_cmp_id(const void * v1, const void * v2) {
  128. struct upubsub_listener *l1, *l2;
  129. l1 = (struct upubsub_listener *)(v1);
  130. l2 = (struct upubsub_listener *)(v2);
  131. /* Sort with null topic as last, as these ones are unused. */
  132. if ((!l1->topic) && (l2->topic)) { return 1; }
  133. if ((!l2->topic) && (l1->topic)) { return -1; }
  134. if ((!l2->topic) && (!l1->topic)) { return 0; }
  135. /* Compare by id to find the exact listener. */
  136. return l1->id - l2->id;
  137. }
  138. struct upubsub_listener * upubsub_subscribe_listener(struct upubsub * u, struct upubsub_listener l) {
  139. struct upubsub_listener * res;
  140. if ( u->size >= UPUBSUB_LISTENER_AMOUNT ) {
  141. return NULL;
  142. }
  143. u->last_id++;
  144. l.id = u->last_id;
  145. l.upubsub = u;
  146. u->listeners[u->size] = l;
  147. res = u->listeners + u->size;
  148. u->size ++;
  149. qsort(u->listeners, u->size, sizeof(struct upubsub_listener), upubsub_listener_cmp_topic_id);
  150. return res;
  151. }
  152. struct upubsub_listener* upubsub_subscribe(struct upubsub * u, const char * topic, void * data, upubsub_listen_func * listen) {
  153. struct upubsub_listener l;
  154. l.topic = topic;
  155. l.data = data;
  156. l.listen = listen;
  157. return upubsub_subscribe_listener(u, l);
  158. }
  159. int upubsub_publish_message(struct upubsub u, struct upubsub_message m) {
  160. int sent = 0;
  161. size_t start, index;
  162. struct upubsub_listener * found = NULL;
  163. struct upubsub_listener key;
  164. struct bsearch_range_result ran;
  165. key.topic = m.topic;
  166. key.id = 1;
  167. ran = bsearch_range(&key, u.listeners, u.size, sizeof(struct upubsub_listener), upubsub_listener_cmp_topic);
  168. if (ran.high < ran.low) { /* Not found, bail out */
  169. return sent;
  170. }
  171. /* Broadcast to all in range. */
  172. for (index = ran.low; index <= ran.high; index ++) {
  173. found = u.listeners + index;
  174. found->listen(found, m);
  175. sent ++;
  176. }
  177. return sent;
  178. }
  179. int upubsub_publish_data(struct upubsub u, const char * topic, void * data, size_t size) {
  180. struct upubsub_message m = { 0 };
  181. m.data = data;
  182. m.size = size;
  183. m.copy_ok = 0;
  184. if (m.size < UPUBSUB_MESSAGE_SIZE) {
  185. memcpy(m.copy, data, m.size);
  186. m.copy_ok = 1;
  187. }
  188. m.topic = topic;
  189. return upubsub_publish_message(u, m);
  190. }
  191. int upubsub_publish_str(struct upubsub u, const char * topic, char * str) {
  192. return upubsub_publish_data(u, topic, str, strlen(str));
  193. }
  194. int upubsub_unsubscribe_listener(struct upubsub * u, struct upubsub_listener * l) {
  195. struct upubsub_listener * found = NULL;
  196. found = bsearch(l, u->listeners, u->size, sizeof(struct upubsub_listener), upubsub_listener_cmp_id);
  197. if (!found) {
  198. return -1;
  199. }
  200. /* Set to unused. */
  201. found->id = 0;
  202. found->topic = NULL;
  203. /* Unused will be sorted to the back. */
  204. qsort(u->listeners, u->size, sizeof(struct upubsub_listener), upubsub_listener_cmp_topic_id);
  205. u->size--;
  206. return 0;
  207. }
  208. int upubsub_unsubscribe(struct upubsub_listener * l) {
  209. return upubsub_unsubscribe_listener(l->upubsub, l);
  210. }
  211. #endif /* UPUBSUB_IMPLEMENTATION */
  212. #ifdef UPUBSUB_TEST
  213. #include <stdio.h>
  214. int listen_print_str(struct upubsub_listener *l, struct upubsub_message m) {
  215. printf("received on topic %s: data %s, %s\n", l->topic, (char*)(l->data), (char *)(m.data));
  216. return 0;
  217. }
  218. int compare_int(const void * key, const void * mem) {
  219. int * i1 = (int*)key;
  220. int * i2 = (int*)mem;
  221. return (*i1) - (*i2);
  222. }
  223. int main(void) {
  224. struct upubsub pusu = { 0 };
  225. struct upubsub_listener *l[10];
  226. int arr[] = { 7, 8 , 8, 9, 9, 9, 10, 11, 12, 20, 20, 32 };
  227. int low, high;
  228. int key = 9;
  229. struct bsearch_range_result ran;
  230. ran = bsearch_range(&key, arr, 12, sizeof(int), compare_int);
  231. printf("Low, high: %d -> %d...\n ", ran.low, ran.high);
  232. if ((ran.low >= 0) && (ran.low < 12)) {
  233. printf("Low: %d\n", arr[ran.low]);
  234. }
  235. if ((ran.high >= 0) && (ran.high < 12)) {
  236. printf("High: %d\n", arr[ran.high]);
  237. }
  238. for (key = 0; key < 40; key ++) {
  239. ran = bsearch_range(&key, arr, 12, sizeof(int), compare_int);
  240. printf("Key: %d; Low, high: %d -> %d:", key, ran.low, ran.high);
  241. if ((ran.low >= 0) && (ran.low < 12)) {
  242. printf(" Low: %d", arr[low]);
  243. }
  244. if ((ran.high >= 0) && (ran.high < 12)) {
  245. printf(" High: %d", arr[ran.high]);
  246. }
  247. printf("\n");
  248. }
  249. printf("Subscribing...\n");
  250. l[0] = upubsub_subscribe(&pusu, "POWER" , "SYST", listen_print_str);
  251. l[1] = upubsub_subscribe(&pusu, "POWER" , "APP1", listen_print_str);
  252. l[2] = upubsub_subscribe(&pusu, "SYSTEM", "APP2", listen_print_str);
  253. l[3] = upubsub_subscribe(&pusu, "SYSTEM", "WIND", listen_print_str);
  254. l[2] = upubsub_subscribe(&pusu, "SYSTEM", "APP3", listen_print_str);
  255. l[3] = upubsub_subscribe(&pusu, "POWER", "APP4", listen_print_str);
  256. printf("Publishing...\n");
  257. upubsub_publish_str(pusu, "POWER", "DOWN");
  258. upubsub_publish_str(pusu, "POWER", "UP");
  259. upubsub_publish_str(pusu, "SYSTEM", "CLICK(7,8)");
  260. upubsub_publish_str(pusu, "SYSTEM", "ROLL(9,5)");
  261. upubsub_publish_str(pusu, "NULL", "IGNORE");
  262. printf("Removing topics...\n");
  263. upubsub_unsubscribe(l[1]);
  264. upubsub_unsubscribe(l[3]);
  265. printf("Publishing...\n");
  266. upubsub_publish_str(pusu, "POWER", "DOWN 2");
  267. upubsub_publish_str(pusu, "POWER", "UP 2");
  268. upubsub_publish_str(pusu, "SYSTEM", "CLICK(7,8) 2");
  269. upubsub_publish_str(pusu, "SYSTEM", "ROLL(9,5) 2");
  270. upubsub_publish_str(pusu, "NULL", "IGNORE");
  271. return 0;
  272. }
  273. #endif /* UPUBSUB_TEST */