1 /* -*- mode: c -*-
2  *
3  * Alternatve version of the strcat test using the vstr string library ...
4  *    http://www.and.org/vstr/
5  * gcc -Wall -W -O2 -o tst tst.c `pkg-config --cflags --libs vstr`
6  *
7  * http://www.bagley.org/~doug/shootout/
8  */
9 /* #define VSTR_COMPILE_INLINE 0 */
10 #include <vstr.h>
11 
12 #include <errno.h>
13 #include <err.h>
14 #include <assert.h>
15 
16 #include "ex_perf.h"
17 
18 #define HAVE_IOV 0
19 #define FMT_ALL 0
20 
21 #define MCPY_TYPE MCPY_GCC
22 
23 static const char *mcpy_type_map[3] = {
24 #define MCPY_LIBC 0
25  "MCPY_LIBC",
26 #define MCPY_GCC  1
27  "MCPY_GCC",
28 #define MCPY_VSTR 2
29  "MCPY_VSTR",
30 };
31 
32 #if   MCPY_TYPE == MCPY_LIBC
33 # define MCPY(x, y, z) memcpy(x, y, z)
34 #elif MCPY_TYPE == MCPY_GCC
35 # define MCPY(x, y, z) __builtin_memcpy(x, y, z)
36 #elif MCPY_TYPE == MCPY_VSTR
37 # define MCPY(x, y, z) vstr_wrap_memcpy(x, y, z)
38 #else
39 # error "Not a valid MCPY_TYPE"
40 #endif
41 
42 #define BUF_SZ (4 * 1024) /* size of node */
43 
44 #define STUFF "hello\n" /* size of data to append each time -- from shootout */
45 
46 #define YES_NO(x) ((x) ? "yes" : "no")
47 
hand_inline(Vstr_base * tst,unsigned int num)48 static void hand_inline(Vstr_base *tst, unsigned int num)
49 {
50   unsigned int scan = 0;
51 
52   while (scan < num)
53   {
54     size_t len = tst->end ? (BUF_SZ - tst->end->len) : 0;
55 
56     if (len >= strlen(STUFF))
57     { /* hand inline of vstr_add_cstr_buf()...
58        *   massive hack -- DO NOT rely on this working this is just so we can
59        *   mesure the best possible inline perf. */
60       unsigned int orig = scan;
61       char *buf = ((Vstr_node_buf *)tst->end)->buf;
62 
63       buf += tst->end->len;
64       while (len >= strlen(STUFF))
65       {
66         MCPY(buf, STUFF, strlen(STUFF));
67         buf += strlen(STUFF);
68         len -= strlen(STUFF);
69 
70         if (++scan == num)
71           break;
72       }
73 
74       len = (scan - orig) * strlen(STUFF);
75       tst->len      += len;
76       tst->end->len += len;
77       if (tst->iovec_upto_date)
78       {
79         unsigned int num_off = tst->num + VSTR__CACHE(tst)->vec->off - 1;
80         VSTR__CACHE(tst)->vec->v[num_off].iov_len += len;
81       }
82     }
83 
84     vstr_add_cstr_buf(tst, tst->len, STUFF);
85     ++scan;
86   }
87 }
88 
hand_iov(Vstr_base * tst,unsigned int num)89 static void hand_iov(Vstr_base *tst, unsigned int num)
90 {
91   unsigned int scan = 0;
92 
93   while (scan < num)
94   {
95     struct iovec *iov = NULL;
96     unsigned int iov_num = 0;
97     size_t len = 0;
98 
99     if (!vstr_add_iovec_buf_beg(tst, tst->len, 1, 2, &iov, &iov_num))
100       errno = ENOMEM, err(EXIT_FAILURE, "iovec_buf_beg");
101 
102     assert(iov_num);
103 
104     len = iov[0].iov_len;
105 
106     if (len < strlen(STUFF))
107       vstr_add_cstr_buf(tst, tst->len, STUFF);
108     else
109     { /* hand inline using iovectors */
110       unsigned int orig = scan;
111       char *buf = iov[0].iov_base;
112 
113       while (len >= strlen(STUFF))
114       {
115         MCPY(buf, STUFF, strlen(STUFF));
116         buf += strlen(STUFF);
117         len -= strlen(STUFF);
118 
119         if (++scan >= num)
120           break;
121       }
122       assert(scan <= num);
123 
124       len = (scan - orig) * strlen(STUFF);
125       vstr_add_iovec_buf_end(tst, tst->len, len);
126     }
127 
128     ++scan;
129   }
130 }
131 
del(Vstr_base * tst)132 static void del(Vstr_base *tst)
133 {
134   vstr_del(tst, 1, tst->len);
135 }
del_alloc(Vstr_base * tst)136 static void del_alloc(Vstr_base *tst)
137 {
138   unsigned int num = tst->num;
139   vstr_del(tst, 1, tst->len);
140   vstr_free_spare_nodes(tst->conf, VSTR_TYPE_NODE_BUF, num);
141 }
142 
main(int argc,char * argv[])143 int main(int argc, char *argv[])
144 {
145   unsigned int num = ((argc == 2) ? atoi(argv[1]) : 1);
146   Vstr_base *tst = NULL;
147   Vstr_base *out = NULL;
148 
149   if (!vstr_init())
150     errno = ENOMEM, err(EXIT_FAILURE, "vstr_init");
151 
152   vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_NUM_BUF_SZ, BUF_SZ);
153 
154   if (!(tst = vstr_make_base(NULL)))
155     errno = ENOMEM, err(EXIT_FAILURE, "vstr_make_base");
156   if (!(out = vstr_make_base(NULL)))
157     errno = ENOMEM, err(EXIT_FAILURE, "vstr_make_base");
158 
159   vstr_cntl_conf(out->conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '$');
160 
161   if (FMT_ALL)
162     vstr_sc_fmt_add_all(out->conf);
163   else
164   {
165     vstr_sc_fmt_add_buf(out->conf,     "{buf:%s%zu}");
166     vstr_sc_fmt_add_rep_chr(out->conf, "{rep_chr:%c%zu}"); /* for TST_HDR */
167   }
168 
169   vstr_cntl_conf(out->conf, VSTR_CNTL_CONF_SET_LOC_CSTR_THOU_SEP, "_");
170   vstr_cntl_conf(out->conf, VSTR_CNTL_CONF_SET_LOC_CSTR_THOU_GRP, "\3");
171 
172   if (HAVE_IOV)
173   {
174     vstr_add_rep_chr(tst, 0, '-', strlen(STUFF) * num);
175     vstr_export_iovec_ptr_all(tst, NULL, NULL);
176   }
177   vstr_del(tst, 1, tst->len);
178   vstr_free_spare_nodes(tst->conf, VSTR_TYPE_NODE_BUF, 1000 * 1000 * 1000);
179 
180   TST_HDR_BEG();
181 
182   if (0) {
183   del_alloc(tst); TST_BEG(1, 1);
184   hand_inline(tst, num);
185   TST_END("hand inline (alloc)");
186   del(tst);       TST_BEG(1, 1);
187   hand_inline(tst, num);
188   TST_END("hand inline");
189 
190   del_alloc(tst); TST_BEG(1, 1);
191   hand_iov(tst, num);
192   TST_END("iov inline (alloc)");
193   del(tst);       TST_BEG(1, 1);
194   hand_iov(tst, num);
195   TST_END("iov inline");
196 
197   del_alloc(tst); TST_BEG(1, num);
198   vstr_add_cstr_buf(tst, tst->len, STUFF);
199   TST_END("add_cstr_buf (alloc)");
200   del(tst);       TST_BEG(1, num);
201   vstr_add_cstr_buf(tst, tst->len, STUFF);
202   TST_END("add_cstr_buf");
203   }
204 
205   del_alloc(tst); TST_BEG(1, num);
206   vstr_add_fmt(tst, tst->len, "%s", STUFF);
207   TST_END("add_fmt(%s) (alloc)");
208   del(tst);       TST_BEG(1, num);
209   vstr_add_fmt(tst, tst->len, "%s", STUFF);
210   TST_END("add_fmt(%s)");
211 
212   del_alloc(tst); TST_BEG(1, num);
213   vstr_add_fmt(tst, tst->len, "${buf:%s%zu}", STUFF, strlen(STUFF));
214   TST_END("add_fmt(${buf}) (alloc)");
215   del(tst);       TST_BEG(1, num);
216   vstr_add_fmt(tst, tst->len, "${buf:%s%zu}", STUFF, strlen(STUFF));
217   TST_END("add_fmt(${buf})");
218 
219   TST_HDR_END();
220 
221   vstr_add_fmt(out, out->len, "data        = %c%s%c\n", '"', STUFF, '"');
222   vstr_add_fmt(out, out->len, "iter        = %'13u\n", num);
223   vstr_add_fmt(out, out->len, "len         = %'13zu\n", tst->len);
224   vstr_add_fmt(out, out->len, "num         = %'13u\n", tst->num);
225   vstr_add_fmt(out, out->len, "hand inline = %s\n", mcpy_type_map[MCPY_TYPE]);
226   vstr_add_fmt(out, out->len, "have iov    = %s\n", YES_NO(HAVE_IOV));
227   vstr_add_fmt(out, out->len, "vstr inline = %s\n",
228                YES_NO(VSTR_COMPILE_INLINE));
229 
230   if (out->conf->malloc_bad)
231     errno = ENOMEM, err(EXIT_FAILURE, "tst");
232 
233   while (out->len)
234     if (!vstr_sc_write_fd(out, 1, out->len, 1, NULL))
235       err(EXIT_FAILURE, "write");
236 
237   vstr_free_base(tst);
238   vstr_free_base(out);
239   vstr_exit();
240 
241   exit (EXIT_SUCCESS);
242 }
243