upubsub.h 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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 botm = 0;
  72. int top = nel;
  73. res.low = 0;
  74. res.high = nel;
  75. while (res.low <= top) {
  76. int mid = res.low + (top - res.low) / 2;
  77. here = (char*)base + (mid*width);
  78. sign = cmp(key, here);
  79. if (sign <= 0) {
  80. top = mid - 1;
  81. } else {
  82. res.low = mid + 1;
  83. }
  84. }
  85. botm = res.low ;
  86. while (botm <= res.high) {
  87. int mid = botm + (res.high - botm) / 2;
  88. here = (char*)base + (mid*width);
  89. sign = cmp(key, here);
  90. if (sign < 0) {
  91. res.high = mid - 1;
  92. } else {
  93. botm = mid + 1;
  94. }
  95. }
  96. return res;
  97. }
  98. int upubsub_listener_cmp_topic_id(const void * v1, const void * v2) {
  99. int res;
  100. struct upubsub_listener *l1, *l2;
  101. l1 = (struct upubsub_listener *)(v1);
  102. l2 = (struct upubsub_listener *)(v2);
  103. /* Sort with null topic as last, as these ones are unused. */
  104. if ((!l1->topic) && (l2->topic)) { return 1; }
  105. if ((!l2->topic) && (l1->topic)) { return -1; }
  106. if ((!l2->topic) && (!l1->topic)) { return 0; }
  107. /* Otherwise compare topics. */
  108. res = strcmp(l1->topic, l2->topic);
  109. /* If the same sort by id to try to keep the sort stable. */
  110. if (!res) {
  111. return l1->id - l2->id;
  112. }
  113. return res;
  114. }
  115. int upubsub_listener_cmp_topic(const void * v1, const void * v2) {
  116. struct upubsub_listener *l1, *l2;
  117. l1 = (struct upubsub_listener *)(v1);
  118. l2 = (struct upubsub_listener *)(v2);
  119. /* Sort with null topic as last, as these ones are unused. */
  120. if ((!l1->topic) && (l2->topic)) { return 1; }
  121. if ((!l2->topic) && (l1->topic)) { return -1; }
  122. if ((!l2->topic) && (!l1->topic)) { return 0; }
  123. /* Otherwise compare topics. */
  124. return strcmp(l1->topic, l2->topic);
  125. }
  126. int upubsub_listener_cmp_id(const void * v1, const void * v2) {
  127. struct upubsub_listener *l1, *l2;
  128. l1 = (struct upubsub_listener *)(v1);
  129. l2 = (struct upubsub_listener *)(v2);
  130. /* Sort with null topic as last, as these ones are unused. */
  131. if ((!l1->topic) && (l2->topic)) { return 1; }
  132. if ((!l2->topic) && (l1->topic)) { return -1; }
  133. if ((!l2->topic) && (!l1->topic)) { return 0; }
  134. /* Compare by id to find the exact listener. */
  135. return l1->id - l2->id;
  136. }
  137. struct upubsub_listener * upubsub_subscribe_listener(struct upubsub * u, struct upubsub_listener l) {
  138. struct upubsub_listener * res;
  139. if ( u->size >= UPUBSUB_LISTENER_AMOUNT ) {
  140. return NULL;
  141. }
  142. u->last_id++;
  143. l.id = u->last_id;
  144. l.upubsub = u;
  145. u->listeners[u->size] = l;
  146. res = u->listeners + u->size;
  147. u->size ++;
  148. qsort(u->listeners, u->size, sizeof(struct upubsub_listener), upubsub_listener_cmp_topic_id);
  149. return res;
  150. }
  151. struct upubsub_listener* upubsub_subscribe(struct upubsub * u, const char * topic, void * data, upubsub_listen_func * listen) {
  152. struct upubsub_listener l;
  153. l.topic = topic;
  154. l.data = data;
  155. l.listen = listen;
  156. return upubsub_subscribe_listener(u, l);
  157. }
  158. int upubsub_publish_message(struct upubsub u, struct upubsub_message m) {
  159. int sent = 0;
  160. size_t index;
  161. struct upubsub_listener * found = NULL;
  162. struct upubsub_listener key;
  163. struct bsearch_range_result ran;
  164. key.topic = m.topic;
  165. key.id = 1;
  166. ran = bsearch_range(&key, u.listeners, u.size, sizeof(struct upubsub_listener), upubsub_listener_cmp_topic);
  167. if (ran.high < ran.low) { /* Not found, bail out */
  168. return sent;
  169. }
  170. /* Broadcast to all in range. */
  171. for (index = ran.low; index <= ran.high; index ++) {
  172. found = u.listeners + index;
  173. found->listen(found, m);
  174. sent ++;
  175. }
  176. return sent;
  177. }
  178. int upubsub_publish_data(struct upubsub u, const char * topic, void * data, size_t size) {
  179. struct upubsub_message m = { 0 };
  180. m.data = data;
  181. m.size = size;
  182. m.copy_ok = 0;
  183. if (m.size < UPUBSUB_MESSAGE_SIZE) {
  184. memcpy(m.copy, data, m.size);
  185. m.copy_ok = 1;
  186. }
  187. m.topic = topic;
  188. return upubsub_publish_message(u, m);
  189. }
  190. int upubsub_publish_str(struct upubsub u, const char * topic, char * str) {
  191. return upubsub_publish_data(u, topic, str, strlen(str));
  192. }
  193. int upubsub_unsubscribe_listener(struct upubsub * u, struct upubsub_listener * l) {
  194. struct upubsub_listener * found = NULL;
  195. found = bsearch(l, u->listeners, u->size, sizeof(struct upubsub_listener), upubsub_listener_cmp_id);
  196. if (!found) {
  197. return -1;
  198. }
  199. /* Set to unused. */
  200. found->id = 0;
  201. found->topic = NULL;
  202. /* Unused will be sorted to the back. */
  203. qsort(u->listeners, u->size, sizeof(struct upubsub_listener), upubsub_listener_cmp_topic_id);
  204. u->size--;
  205. return 0;
  206. }
  207. int upubsub_unsubscribe(struct upubsub_listener * l) {
  208. return upubsub_unsubscribe_listener(l->upubsub, l);
  209. }
  210. #endif /* UPUBSUB_IMPLEMENTATION */
  211. #ifdef UPUBSUB_TEST
  212. #include <stdio.h>
  213. int listen_print_str(struct upubsub_listener *l, struct upubsub_message m) {
  214. printf("received on topic %s: data %s, %s\n", l->topic, (char*)(l->data), (char *)(m.data));
  215. return 0;
  216. }
  217. int compare_int(const void * key, const void * mem) {
  218. int * i1 = (int*)key;
  219. int * i2 = (int*)mem;
  220. return (*i1) - (*i2);
  221. }
  222. int main(void) {
  223. struct upubsub pusu = { 0 };
  224. struct upubsub_listener *l[10];
  225. int arr[] = { 7, 8 , 8, 9, 9, 9, 10, 11, 12, 20, 20, 32 };
  226. int low, high;
  227. int key = 9;
  228. struct bsearch_range_result ran;
  229. ran = bsearch_range(&key, arr, 12, sizeof(int), compare_int);
  230. printf("Low, high: %d -> %d...\n ", ran.low, ran.high);
  231. if ((ran.low >= 0) && (ran.low < 12)) {
  232. printf("Low: %d\n", arr[ran.low]);
  233. }
  234. if ((ran.high >= 0) && (ran.high < 12)) {
  235. printf("High: %d\n", arr[ran.high]);
  236. }
  237. for (key = 0; key < 40; key ++) {
  238. ran = bsearch_range(&key, arr, 12, sizeof(int), compare_int);
  239. printf("Key: %d; Low, high: %d -> %d:", key, ran.low, ran.high);
  240. if ((ran.low >= 0) && (ran.low < 12)) {
  241. printf(" Low: %d", arr[low]);
  242. }
  243. if ((ran.high >= 0) && (ran.high < 12)) {
  244. printf(" High: %d", arr[ran.high]);
  245. }
  246. printf("\n");
  247. }
  248. printf("Subscribing...\n");
  249. l[0] = upubsub_subscribe(&pusu, "POWER" , "SYST", listen_print_str);
  250. l[1] = upubsub_subscribe(&pusu, "POWER" , "APP1", listen_print_str);
  251. l[2] = upubsub_subscribe(&pusu, "SYSTEM", "APP2", listen_print_str);
  252. l[3] = upubsub_subscribe(&pusu, "SYSTEM", "WIND", listen_print_str);
  253. l[2] = upubsub_subscribe(&pusu, "SYSTEM", "APP3", listen_print_str);
  254. l[3] = upubsub_subscribe(&pusu, "POWER", "APP4", listen_print_str);
  255. printf("Publishing...\n");
  256. upubsub_publish_str(pusu, "POWER", "DOWN");
  257. upubsub_publish_str(pusu, "POWER", "UP");
  258. upubsub_publish_str(pusu, "SYSTEM", "CLICK(7,8)");
  259. upubsub_publish_str(pusu, "SYSTEM", "ROLL(9,5)");
  260. upubsub_publish_str(pusu, "NULL", "IGNORE");
  261. printf("Removing topics...\n");
  262. upubsub_unsubscribe(l[1]);
  263. upubsub_unsubscribe(l[3]);
  264. printf("Publishing...\n");
  265. upubsub_publish_str(pusu, "POWER", "DOWN 2");
  266. upubsub_publish_str(pusu, "POWER", "UP 2");
  267. upubsub_publish_str(pusu, "SYSTEM", "CLICK(7,8) 2");
  268. upubsub_publish_str(pusu, "SYSTEM", "ROLL(9,5) 2");
  269. upubsub_publish_str(pusu, "NULL", "IGNORE");
  270. return 0;
  271. }
  272. #endif /* UPUBSUB_TEST */