nutcracker 0.3.0.12 → 0.4.0.13

Sign up to get free protection for your applications and to get access to all the features.
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
  {