nutcracker 0.3.0.12 → 0.4.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. checksums.yaml +5 -13
  2. data/README.md +3 -3
  3. data/Rakefile +12 -10
  4. data/ext/nutcracker/Makefile.in +215 -162
  5. data/ext/nutcracker/README.md +16 -4
  6. data/ext/nutcracker/aclocal.m4 +432 -254
  7. data/ext/nutcracker/{contrib/yaml-0.1.4/configure → autom4te.cache/output.0} +11367 -4545
  8. data/ext/nutcracker/autom4te.cache/output.1 +19907 -0
  9. data/ext/nutcracker/autom4te.cache/output.2 +19907 -0
  10. data/ext/nutcracker/autom4te.cache/requests +518 -0
  11. data/ext/nutcracker/autom4te.cache/traces.0 +2715 -0
  12. data/ext/nutcracker/autom4te.cache/traces.1 +967 -0
  13. data/ext/nutcracker/autom4te.cache/traces.2 +2715 -0
  14. data/ext/nutcracker/config/compile +347 -0
  15. data/ext/nutcracker/config/config.guess +116 -78
  16. data/ext/nutcracker/config/config.sub +65 -45
  17. data/ext/nutcracker/config/depcomp +295 -192
  18. data/ext/nutcracker/config/install-sh +7 -7
  19. data/ext/nutcracker/config/ltmain.sh +15 -20
  20. data/ext/nutcracker/config/missing +149 -265
  21. data/ext/nutcracker/configure +493 -367
  22. data/ext/nutcracker/contrib/Makefile.in +158 -116
  23. data/ext/nutcracker/extconf.rb +0 -1
  24. data/ext/nutcracker/m4/libtool.m4 +4 -23
  25. data/ext/nutcracker/m4/ltoptions.m4 +0 -0
  26. data/ext/nutcracker/m4/ltsugar.m4 +0 -0
  27. data/ext/nutcracker/m4/ltversion.m4 +0 -0
  28. data/ext/nutcracker/m4/lt~obsolete.m4 +0 -0
  29. data/ext/nutcracker/notes/recommendation.md +1 -1
  30. data/ext/nutcracker/notes/redis.md +35 -3
  31. data/ext/nutcracker/scripts/benchmark-mget.py +43 -0
  32. data/ext/nutcracker/scripts/nutcracker.spec +61 -3
  33. data/ext/nutcracker/scripts/redis-check.sh +43 -0
  34. data/ext/nutcracker/src/Makefile.in +205 -142
  35. data/ext/nutcracker/src/event/Makefile.in +164 -66
  36. data/ext/nutcracker/src/hashkit/Makefile.in +164 -66
  37. data/ext/nutcracker/src/nc_conf.c +2 -0
  38. data/ext/nutcracker/src/nc_connection.c +31 -0
  39. data/ext/nutcracker/src/nc_connection.h +3 -0
  40. data/ext/nutcracker/src/nc_core.c +38 -2
  41. data/ext/nutcracker/src/nc_core.h +11 -0
  42. data/ext/nutcracker/src/nc_log.c +90 -12
  43. data/ext/nutcracker/src/nc_log.h +11 -0
  44. data/ext/nutcracker/src/nc_mbuf.h +1 -1
  45. data/ext/nutcracker/src/nc_message.c +162 -116
  46. data/ext/nutcracker/src/nc_message.h +161 -129
  47. data/ext/nutcracker/src/nc_proxy.c +34 -4
  48. data/ext/nutcracker/src/nc_request.c +158 -32
  49. data/ext/nutcracker/src/nc_server.c +59 -5
  50. data/ext/nutcracker/src/nc_server.h +1 -0
  51. data/ext/nutcracker/src/nc_signal.c +2 -2
  52. data/ext/nutcracker/src/nc_stats.c +21 -0
  53. data/ext/nutcracker/src/nc_stats.h +28 -26
  54. data/ext/nutcracker/src/nc_string.c +176 -1
  55. data/ext/nutcracker/src/nc_string.h +26 -0
  56. data/ext/nutcracker/src/nc_util.c +12 -0
  57. data/ext/nutcracker/src/nc_util.h +1 -0
  58. data/ext/nutcracker/src/proto/Makefile.in +164 -66
  59. data/ext/nutcracker/src/proto/nc_memcache.c +279 -88
  60. data/ext/nutcracker/src/proto/nc_proto.h +3 -4
  61. data/ext/nutcracker/src/proto/nc_redis.c +561 -134
  62. data/lib/nutcracker/version.rb +1 -1
  63. metadata +31 -67
  64. data/ext/nutcracker/contrib/yaml-0.1.4/LICENSE +0 -19
  65. data/ext/nutcracker/contrib/yaml-0.1.4/Makefile.am +0 -20
  66. data/ext/nutcracker/contrib/yaml-0.1.4/Makefile.in +0 -736
  67. data/ext/nutcracker/contrib/yaml-0.1.4/README +0 -27
  68. data/ext/nutcracker/contrib/yaml-0.1.4/aclocal.m4 +0 -956
  69. data/ext/nutcracker/contrib/yaml-0.1.4/config.h.in +0 -80
  70. data/ext/nutcracker/contrib/yaml-0.1.4/config/config.guess +0 -1561
  71. data/ext/nutcracker/contrib/yaml-0.1.4/config/config.sub +0 -1686
  72. data/ext/nutcracker/contrib/yaml-0.1.4/config/depcomp +0 -630
  73. data/ext/nutcracker/contrib/yaml-0.1.4/config/install-sh +0 -520
  74. data/ext/nutcracker/contrib/yaml-0.1.4/config/ltmain.sh +0 -8406
  75. data/ext/nutcracker/contrib/yaml-0.1.4/config/missing +0 -376
  76. data/ext/nutcracker/contrib/yaml-0.1.4/configure.ac +0 -75
  77. data/ext/nutcracker/contrib/yaml-0.1.4/doc/doxygen.cfg +0 -222
  78. data/ext/nutcracker/contrib/yaml-0.1.4/include/yaml.h +0 -1971
  79. data/ext/nutcracker/contrib/yaml-0.1.4/m4/libtool.m4 +0 -7357
  80. data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltoptions.m4 +0 -368
  81. data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltsugar.m4 +0 -123
  82. data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltversion.m4 +0 -23
  83. data/ext/nutcracker/contrib/yaml-0.1.4/m4/lt~obsolete.m4 +0 -92
  84. data/ext/nutcracker/contrib/yaml-0.1.4/src/Makefile.am +0 -4
  85. data/ext/nutcracker/contrib/yaml-0.1.4/src/Makefile.in +0 -484
  86. data/ext/nutcracker/contrib/yaml-0.1.4/src/api.c +0 -1392
  87. data/ext/nutcracker/contrib/yaml-0.1.4/src/dumper.c +0 -394
  88. data/ext/nutcracker/contrib/yaml-0.1.4/src/emitter.c +0 -2329
  89. data/ext/nutcracker/contrib/yaml-0.1.4/src/loader.c +0 -432
  90. data/ext/nutcracker/contrib/yaml-0.1.4/src/parser.c +0 -1374
  91. data/ext/nutcracker/contrib/yaml-0.1.4/src/reader.c +0 -465
  92. data/ext/nutcracker/contrib/yaml-0.1.4/src/scanner.c +0 -3570
  93. data/ext/nutcracker/contrib/yaml-0.1.4/src/writer.c +0 -141
  94. data/ext/nutcracker/contrib/yaml-0.1.4/src/yaml_private.h +0 -640
  95. data/ext/nutcracker/contrib/yaml-0.1.4/tests/Makefile.am +0 -8
  96. data/ext/nutcracker/contrib/yaml-0.1.4/tests/Makefile.in +0 -675
  97. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-deconstructor-alt.c +0 -800
  98. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-deconstructor.c +0 -1130
  99. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-reformatter-alt.c +0 -217
  100. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-reformatter.c +0 -202
  101. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-dumper.c +0 -311
  102. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-emitter.c +0 -327
  103. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-loader.c +0 -63
  104. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-parser.c +0 -63
  105. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-scanner.c +0 -63
  106. data/ext/nutcracker/contrib/yaml-0.1.4/tests/test-reader.c +0 -354
  107. data/ext/nutcracker/contrib/yaml-0.1.4/tests/test-version.c +0 -29
@@ -368,6 +368,10 @@ server_close(struct context *ctx, struct conn *conn)
368
368
  msg->error = 1;
369
369
  msg->err = conn->err;
370
370
 
371
+ if (msg->frag_owner != NULL) {
372
+ msg->frag_owner->nfrag_done++;
373
+ }
374
+
371
375
  if (req_done(c_conn, TAILQ_FIRST(&c_conn->omsg_q))) {
372
376
  event_add_out(ctx->evb, msg->owner);
373
377
  }
@@ -397,6 +401,9 @@ server_close(struct context *ctx, struct conn *conn)
397
401
  msg->done = 1;
398
402
  msg->error = 1;
399
403
  msg->err = conn->err;
404
+ if (msg->frag_owner != NULL) {
405
+ msg->frag_owner->nfrag_done++;
406
+ }
400
407
 
401
408
  if (req_done(c_conn, TAILQ_FIRST(&c_conn->omsg_q))) {
402
409
  event_add_out(ctx->evb, msg->owner);
@@ -464,7 +471,7 @@ server_connect(struct context *ctx, struct server *server, struct conn *conn)
464
471
  status = nc_set_nonblocking(conn->sd);
465
472
  if (status != NC_OK) {
466
473
  log_error("set nonblock on s %d for server '%.*s' failed: %s",
467
- conn->sd, server->pname.len, server->pname.data,
474
+ conn->sd, server->pname.len, server->pname.data,
468
475
  strerror(errno));
469
476
  goto error;
470
477
  }
@@ -608,15 +615,33 @@ server_pool_hash(struct server_pool *pool, uint8_t *key, uint32_t keylen)
608
615
  return pool->key_hash((char *)key, keylen);
609
616
  }
610
617
 
611
- static struct server *
612
- server_pool_server(struct server_pool *pool, uint8_t *key, uint32_t keylen)
618
+ uint32_t
619
+ server_pool_idx(struct server_pool *pool, uint8_t *key, uint32_t keylen)
613
620
  {
614
- struct server *server;
615
621
  uint32_t hash, idx;
616
622
 
617
623
  ASSERT(array_n(&pool->server) != 0);
618
624
  ASSERT(key != NULL && keylen != 0);
619
625
 
626
+ /*
627
+ * If hash_tag: is configured for this server pool, we use the part of
628
+ * the key within the hash tag as an input to the distributor. Otherwise
629
+ * we use the full key
630
+ */
631
+ if (!string_empty(&pool->hash_tag)) {
632
+ struct string *tag = &pool->hash_tag;
633
+ uint8_t *tag_start, *tag_end;
634
+
635
+ tag_start = nc_strchr(key, key + keylen, tag->data[0]);
636
+ if (tag_start != NULL) {
637
+ tag_end = nc_strchr(tag_start + 1, key + keylen, tag->data[1]);
638
+ if ((tag_end != NULL) && (tag_end - tag_start > 1)) {
639
+ key = tag_start + 1;
640
+ keylen = (uint32_t)(tag_end - key);
641
+ }
642
+ }
643
+ }
644
+
620
645
  switch (pool->dist_type) {
621
646
  case DIST_KETAMA:
622
647
  hash = server_pool_hash(pool, key, keylen);
@@ -634,10 +659,19 @@ server_pool_server(struct server_pool *pool, uint8_t *key, uint32_t keylen)
634
659
 
635
660
  default:
636
661
  NOT_REACHED();
637
- return NULL;
662
+ return 0;
638
663
  }
639
664
  ASSERT(idx < array_n(&pool->server));
665
+ return idx;
666
+ }
640
667
 
668
+ static struct server *
669
+ server_pool_server(struct server_pool *pool, uint8_t *key, uint32_t keylen)
670
+ {
671
+ struct server *server;
672
+ uint32_t idx;
673
+
674
+ idx = server_pool_idx(pool, key, keylen);
641
675
  server = array_get(&pool->server, idx);
642
676
 
643
677
  log_debug(LOG_VERB, "key '%.*s' on dist %d maps to server '%.*s'", keylen,
@@ -742,6 +776,18 @@ server_pool_each_set_owner(void *elem, void *data)
742
776
  return NC_OK;
743
777
  }
744
778
 
779
+ static rstatus_t
780
+ server_pool_each_calc_connections(void *elem, void *data)
781
+ {
782
+ struct server_pool *sp = elem;
783
+ struct context *ctx = data;
784
+
785
+ ctx->max_nsconn += sp->server_connections * array_n(&sp->server);
786
+ ctx->max_nsconn += 1; /* pool listening socket */
787
+
788
+ return NC_OK;
789
+ }
790
+
745
791
  rstatus_t
746
792
  server_pool_run(struct server_pool *pool)
747
793
  {
@@ -802,6 +848,14 @@ server_pool_init(struct array *server_pool, struct array *conf_pool,
802
848
  return status;
803
849
  }
804
850
 
851
+ /* compute max server connections */
852
+ ctx->max_nsconn = 0;
853
+ status = array_each(server_pool, server_pool_each_calc_connections, ctx);
854
+ if (status != NC_OK) {
855
+ server_pool_deinit(server_pool);
856
+ return status;
857
+ }
858
+
805
859
  /* update server pool continuum */
806
860
  status = array_each(server_pool, server_pool_each_run, NULL);
807
861
  if (status != NC_OK) {
@@ -133,6 +133,7 @@ void server_close(struct context *ctx, struct conn *conn);
133
133
  void server_connected(struct context *ctx, struct conn *conn);
134
134
  void server_ok(struct context *ctx, struct conn *conn);
135
135
 
136
+ uint32_t server_pool_idx(struct server_pool *pool, uint8_t *key, uint32_t keylen);
136
137
  struct conn *server_pool_conn(struct context *ctx, struct server_pool *pool, uint8_t *key, uint32_t keylen);
137
138
  rstatus_t server_pool_run(struct server_pool *pool);
138
139
  rstatus_t server_pool_preconnect(struct context *ctx);
@@ -110,7 +110,7 @@ signal_handler(int signo)
110
110
  break;
111
111
 
112
112
  case SIGSEGV:
113
- nc_stacktrace(1);
113
+ log_stacktrace();
114
114
  actionstr = ", core dumping";
115
115
  raise(SIGSEGV);
116
116
  break;
@@ -119,7 +119,7 @@ signal_handler(int signo)
119
119
  NOT_REACHED();
120
120
  }
121
121
 
122
- loga("signal %d (%s) received%s", signo, sig->signame, actionstr);
122
+ log_safe("signal %d (%s) received%s", signo, sig->signame, actionstr);
123
123
 
124
124
  if (action != NULL) {
125
125
  action();
@@ -361,6 +361,14 @@ stats_create_buf(struct stats *st)
361
361
  size += int64_max_digits;
362
362
  size += key_value_extra;
363
363
 
364
+ size += st->ntotal_conn_str.len;
365
+ size += int64_max_digits;
366
+ size += key_value_extra;
367
+
368
+ size += st->ncurr_conn_str.len;
369
+ size += int64_max_digits;
370
+ size += key_value_extra;
371
+
364
372
  /* server pools */
365
373
  for (i = 0; i < array_n(&st->sum); i++) {
366
374
  struct stats_pool *stp = array_get(&st->sum, i);
@@ -508,6 +516,16 @@ stats_add_header(struct stats *st)
508
516
  return status;
509
517
  }
510
518
 
519
+ status = stats_add_num(st, &st->ntotal_conn_str, conn_ntotal_conn());
520
+ if (status != NC_OK) {
521
+ return status;
522
+ }
523
+
524
+ status = stats_add_num(st, &st->ncurr_conn_str, conn_ncurr_conn());
525
+ if (status != NC_OK) {
526
+ return status;
527
+ }
528
+
511
529
  return NC_OK;
512
530
  }
513
531
 
@@ -909,6 +927,9 @@ stats_create(uint16_t stats_port, char *stats_ip, int stats_interval,
909
927
  string_set_text(&st->uptime_str, "uptime");
910
928
  string_set_text(&st->timestamp_str, "timestamp");
911
929
 
930
+ string_set_text(&st->ntotal_conn_str, "total_connections");
931
+ string_set_text(&st->ncurr_conn_str, "curr_connections");
932
+
912
933
  st->updated = 0;
913
934
  st->aggregate = 0;
914
935
 
@@ -41,7 +41,7 @@
41
41
  /* data behavior */ \
42
42
  ACTION( requests, STATS_COUNTER, "# requests") \
43
43
  ACTION( request_bytes, STATS_COUNTER, "total request bytes") \
44
- ACTION( responses, STATS_COUNTER, "# respones") \
44
+ ACTION( responses, STATS_COUNTER, "# responses") \
45
45
  ACTION( response_bytes, STATS_COUNTER, "total response bytes") \
46
46
  ACTION( in_queue, STATS_GAUGE, "# requests in incoming queue") \
47
47
  ACTION( in_queue_bytes, STATS_GAUGE, "current request bytes in incoming queue") \
@@ -87,31 +87,33 @@ struct stats_buffer {
87
87
  };
88
88
 
89
89
  struct stats {
90
- uint16_t port; /* stats monitoring port */
91
- int interval; /* stats aggregation interval */
92
- struct string addr; /* stats monitoring address */
93
-
94
- int64_t start_ts; /* start timestamp of nutcracker */
95
- struct stats_buffer buf; /* output buffer */
96
-
97
- struct array current; /* stats_pool[] (a) */
98
- struct array shadow; /* stats_pool[] (b) */
99
- struct array sum; /* stats_pool[] (c = a + b) */
100
-
101
- pthread_t tid; /* stats aggregator thread */
102
- int sd; /* stats descriptor */
103
-
104
- struct string service_str; /* service string */
105
- struct string service; /* service */
106
- struct string source_str; /* source string */
107
- struct string source; /* source */
108
- struct string version_str; /* version string */
109
- struct string version; /* version */
110
- struct string uptime_str; /* uptime string */
111
- struct string timestamp_str; /* timestamp string */
112
-
113
- volatile int aggregate; /* shadow (b) aggregate? */
114
- volatile int updated; /* current (a) updated? */
90
+ uint16_t port; /* stats monitoring port */
91
+ int interval; /* stats aggregation interval */
92
+ struct string addr; /* stats monitoring address */
93
+
94
+ int64_t start_ts; /* start timestamp of nutcracker */
95
+ struct stats_buffer buf; /* output buffer */
96
+
97
+ struct array current; /* stats_pool[] (a) */
98
+ struct array shadow; /* stats_pool[] (b) */
99
+ struct array sum; /* stats_pool[] (c = a + b) */
100
+
101
+ pthread_t tid; /* stats aggregator thread */
102
+ int sd; /* stats descriptor */
103
+
104
+ struct string service_str; /* service string */
105
+ struct string service; /* service */
106
+ struct string source_str; /* source string */
107
+ struct string source; /* source */
108
+ struct string version_str; /* version string */
109
+ struct string version; /* version */
110
+ struct string uptime_str; /* uptime string */
111
+ struct string timestamp_str; /* timestamp string */
112
+ struct string ntotal_conn_str; /* total connections string */
113
+ struct string ncurr_conn_str; /* curr connections string */
114
+
115
+ volatile int aggregate; /* shadow (b) aggregate? */
116
+ volatile int updated; /* current (a) updated? */
115
117
  };
116
118
 
117
119
  #define DEFINE_ACTION(_name, _type, _desc) STATS_POOL_##_name,
@@ -102,8 +102,183 @@ int
102
102
  string_compare(const struct string *s1, const struct string *s2)
103
103
  {
104
104
  if (s1->len != s2->len) {
105
- return s1->len - s2->len > 0 ? 1 : -1;
105
+ return s1->len > s2->len ? 1 : -1;
106
106
  }
107
107
 
108
108
  return nc_strncmp(s1->data, s2->data, s1->len);
109
109
  }
110
+
111
+ static char *
112
+ _safe_utoa(int _base, uint64_t val, char *buf)
113
+ {
114
+ char hex[] = "0123456789abcdef";
115
+ uint32_t base = (uint32_t) _base;
116
+ *buf-- = 0;
117
+ do {
118
+ *buf-- = hex[val % base];
119
+ } while ((val /= base) != 0);
120
+ return buf + 1;
121
+ }
122
+
123
+ static char *
124
+ _safe_itoa(int base, int64_t val, char *buf)
125
+ {
126
+ char hex[] = "0123456789abcdef";
127
+ char *orig_buf = buf;
128
+ const int32_t is_neg = (val < 0);
129
+ *buf-- = 0;
130
+
131
+ if (is_neg) {
132
+ val = -val;
133
+ }
134
+ if (is_neg && base == 16) {
135
+ int ix;
136
+ val -= 1;
137
+ for (ix = 0; ix < 16; ++ix)
138
+ buf[-ix] = '0';
139
+ }
140
+
141
+ do {
142
+ *buf-- = hex[val % base];
143
+ } while ((val /= base) != 0);
144
+
145
+ if (is_neg && base == 10) {
146
+ *buf-- = '-';
147
+ }
148
+
149
+ if (is_neg && base == 16) {
150
+ int ix;
151
+ buf = orig_buf - 1;
152
+ for (ix = 0; ix < 16; ++ix, --buf) {
153
+ /* *INDENT-OFF* */
154
+ switch (*buf) {
155
+ case '0': *buf = 'f'; break;
156
+ case '1': *buf = 'e'; break;
157
+ case '2': *buf = 'd'; break;
158
+ case '3': *buf = 'c'; break;
159
+ case '4': *buf = 'b'; break;
160
+ case '5': *buf = 'a'; break;
161
+ case '6': *buf = '9'; break;
162
+ case '7': *buf = '8'; break;
163
+ case '8': *buf = '7'; break;
164
+ case '9': *buf = '6'; break;
165
+ case 'a': *buf = '5'; break;
166
+ case 'b': *buf = '4'; break;
167
+ case 'c': *buf = '3'; break;
168
+ case 'd': *buf = '2'; break;
169
+ case 'e': *buf = '1'; break;
170
+ case 'f': *buf = '0'; break;
171
+ }
172
+ /* *INDENT-ON* */
173
+ }
174
+ }
175
+ return buf + 1;
176
+ }
177
+
178
+ static const char *
179
+ _safe_check_longlong(const char *fmt, int32_t * have_longlong)
180
+ {
181
+ *have_longlong = false;
182
+ if (*fmt == 'l') {
183
+ fmt++;
184
+ if (*fmt != 'l') {
185
+ *have_longlong = (sizeof(long) == sizeof(int64_t));
186
+ } else {
187
+ fmt++;
188
+ *have_longlong = true;
189
+ }
190
+ }
191
+ return fmt;
192
+ }
193
+
194
+ int
195
+ _safe_vsnprintf(char *to, size_t size, const char *format, va_list ap)
196
+ {
197
+ char *start = to;
198
+ char *end = start + size - 1;
199
+ for (; *format; ++format) {
200
+ int32_t have_longlong = false;
201
+ if (*format != '%') {
202
+ if (to == end) { /* end of buffer */
203
+ break;
204
+ }
205
+ *to++ = *format; /* copy ordinary char */
206
+ continue;
207
+ }
208
+ ++format; /* skip '%' */
209
+
210
+ format = _safe_check_longlong(format, &have_longlong);
211
+
212
+ switch (*format) {
213
+ case 'd':
214
+ case 'i':
215
+ case 'u':
216
+ case 'x':
217
+ case 'p':
218
+ {
219
+ int64_t ival = 0;
220
+ uint64_t uval = 0;
221
+ if (*format == 'p')
222
+ have_longlong = (sizeof(void *) == sizeof(uint64_t));
223
+ if (have_longlong) {
224
+ if (*format == 'u') {
225
+ uval = va_arg(ap, uint64_t);
226
+ } else {
227
+ ival = va_arg(ap, int64_t);
228
+ }
229
+ } else {
230
+ if (*format == 'u') {
231
+ uval = va_arg(ap, uint32_t);
232
+ } else {
233
+ ival = va_arg(ap, int32_t);
234
+ }
235
+ }
236
+
237
+ {
238
+ char buff[22];
239
+ const int base = (*format == 'x' || *format == 'p') ? 16 : 10;
240
+
241
+ /* *INDENT-OFF* */
242
+ char *val_as_str = (*format == 'u') ?
243
+ _safe_utoa(base, uval, &buff[sizeof(buff) - 1]) :
244
+ _safe_itoa(base, ival, &buff[sizeof(buff) - 1]);
245
+ /* *INDENT-ON* */
246
+
247
+ /* Strip off "ffffffff" if we have 'x' format without 'll' */
248
+ if (*format == 'x' && !have_longlong && ival < 0) {
249
+ val_as_str += 8;
250
+ }
251
+
252
+ while (*val_as_str && to < end) {
253
+ *to++ = *val_as_str++;
254
+ }
255
+ continue;
256
+ }
257
+ }
258
+ case 's':
259
+ {
260
+ const char *val = va_arg(ap, char *);
261
+ if (!val) {
262
+ val = "(null)";
263
+ }
264
+ while (*val && to < end) {
265
+ *to++ = *val++;
266
+ }
267
+ continue;
268
+ }
269
+ }
270
+ }
271
+ *to = 0;
272
+ return (int)(to - start);
273
+ }
274
+
275
+ int
276
+ _safe_snprintf(char *to, size_t n, const char *fmt, ...)
277
+ {
278
+ int result;
279
+ va_list args;
280
+ va_start(args, fmt);
281
+ result = _safe_vsnprintf(to, n, fmt, args);
282
+ va_end(args);
283
+ return result;
284
+ }
@@ -19,6 +19,9 @@
19
19
  #define _NC_STRING_H_
20
20
 
21
21
  #include <string.h>
22
+ #include <sys/types.h>
23
+ #include <stdarg.h>
24
+
22
25
  #include <nc_core.h>
23
26
 
24
27
  struct string {
@@ -83,6 +86,29 @@ int string_compare(const struct string *s1, const struct string *s2);
83
86
  #define nc_vscnprintf(_s, _n, _f, _a) \
84
87
  _vscnprintf((char *)(_s), (size_t)(_n), _f, _a)
85
88
 
89
+ #define nc_strftime(_s, _n, fmt, tm) \
90
+ (int)strftime((char *)(_s), (size_t)(_n), fmt, tm)
91
+
92
+ /*
93
+ * A (very) limited version of snprintf
94
+ * @param to Destination buffer
95
+ * @param n Size of destination buffer
96
+ * @param fmt printf() style format string
97
+ * @returns Number of bytes written, including terminating '\0'
98
+ * Supports 'd' 'i' 'u' 'x' 'p' 's' conversion
99
+ * Supports 'l' and 'll' modifiers for integral types
100
+ * Does not support any width/precision
101
+ * Implemented with simplicity, and async-signal-safety in mind
102
+ */
103
+ int _safe_vsnprintf(char *to, size_t size, const char *format, va_list ap);
104
+ int _safe_snprintf(char *to, size_t n, const char *fmt, ...);
105
+
106
+ #define nc_safe_snprintf(_s, _n, ...) \
107
+ _safe_snprintf((char *)(_s), (size_t)(_n), __VA_ARGS__)
108
+
109
+ #define nc_safe_vsnprintf(_s, _n, _f, _a) \
110
+ _safe_vsnprintf((char *)(_s), (size_t)(_n), _f, _a)
111
+
86
112
  static inline uint8_t *
87
113
  _nc_strchr(uint8_t *p, uint8_t *last, uint8_t c)
88
114
  {