Raw paste - Back to the pastebin
  1. /*
  2. This software is released into public domain.
  3. It is provided "as is", without warranties or conditions of any kind.
  4. Anyone is free to use, modify, redistribute and do anything with this software.
  5. All echo functions in here work as if the parameter -e was provided.
  6. */
  7. #define _POSIX_SOURCE /* putchar_unlocked */
  8. #include <inttypes.h>
  9. #include <fcntl.h>
  10. #include <stdbool.h>
  11. #include <stdint.h>
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <sys/time.h>
  16. #include <unistd.h>
  17. #ifndef NITER
  18. #define NITER 1000000
  19. #endif
  20. typedef void (*func)(void);
  21. typedef void (*echo_func)(int argc, char **argv);
  22. int64_t bench(func f, int niter);
  23. void test_init(int argc, char **argv);
  24. void test_free(void);
  25. void test_echo(echo_func echo);
  26. void test_null(void);
  27. void test_buf(void);
  28. void test_inplace(void);
  29. void test_finplace(void);
  30. void test_putchar(void);
  31. void test_putcharu(void);
  32. void test_write(void);
  33. void echo_null(int argc, char **argv);
  34. void echo_buf(int argc, char **argv);
  35. void echo_inplace(int argc, char **argv);
  36. void echo_finplace(int argc, char **argv);
  37. void echo_putchar(int argc, char **argv);
  38. void echo_putcharu(int argc, char **argv);
  39. void echo_write(int argc, char **argv);
  40. size_t echo_unescape(char *dst, const char *src);
  41. int argc_orig;
  42. char **argv_orig;
  43. char **argv_cpy;
  44. char **argv_echo;
  45. int64_t bench(func f, int niter)
  46. {
  47. struct timeval start, end;
  48. gettimeofday(&start, NULL);
  49. while (niter--) {
  50. f();
  51. }
  52. gettimeofday(&end, NULL);
  53. return (end.tv_sec-start.tv_sec)*1000000 + (end.tv_usec-start.tv_usec);
  54. }
  55. void test_init(int argc, char **argv)
  56. {
  57. int i;
  58. argc_orig = argc;
  59. argv_orig = argv;
  60. argv_cpy = malloc(sizeof(*argv) * (argc+1));
  61. if (!argv_cpy) {
  62. abort();
  63. }
  64. argv_cpy[argc] = NULL;
  65. argv_echo = malloc(sizeof(*argv) * (argc+1));
  66. if (!argv_echo) {
  67. abort();
  68. }
  69. argv_echo[argc] = NULL;
  70. for (i=0; i<argc; ++i) {
  71. argv_cpy[i] = malloc(strlen(argv[i])+1);
  72. if (!argv_cpy[i]) {
  73. abort();
  74. }
  75. strcpy(argv_cpy[i], argv[i]);
  76. argv_echo[i] = argv_cpy[i];
  77. }
  78. }
  79. void test_free(void)
  80. {
  81. int i;
  82. for (i=0; i<argc_orig; ++i) {
  83. free(argv_cpy[i]);
  84. }
  85. free(argv_cpy);
  86. free(argv_echo);
  87. }
  88. void test_echo(echo_func echo)
  89. {
  90. int argc = argc_orig;
  91. echo(argc, argv_echo);
  92. while (argc--) {
  93. argv_echo[argc] = strcpy(argv_cpy[argc], argv_orig[argc]);
  94. }
  95. }
  96. void test_null(void) {
  97. test_echo(echo_null);
  98. }
  99. void test_buf(void) {
  100. test_echo(echo_buf);
  101. }
  102. void test_inplace(void) {
  103. test_echo(echo_inplace);
  104. }
  105. void test_finplace(void) {
  106. test_echo(echo_finplace);
  107. }
  108. void test_putchar(void) {
  109. test_echo(echo_putchar);
  110. }
  111. void test_putcharu(void) {
  112. test_echo(echo_putcharu);
  113. }
  114. void test_write(void) {
  115. test_echo(echo_write);
  116. }
  117. /* do nothing to see how long benchmarking itself takes */
  118. void echo_null(int argc, char **argv) {}
  119. /* allocate a buffer, format args into it, use single write() call */
  120. void echo_buf(int argc, char **argv)
  121. {
  122. size_t len, n;
  123. int i;
  124. char *buf;
  125. len = 0;
  126. for (i=0; i<argc; ++i) {
  127. len += strlen(argv[i]) + 1;
  128. }
  129. buf = malloc(len);
  130. if (!buf) {
  131. abort();
  132. }
  133. n = 0;
  134. for (i=0; i<argc; ++i) {
  135. n += echo_unescape(buf+n, argv[i]);
  136. buf[n++] = ' ';
  137. }
  138. buf[n-1] = '\n';
  139. write(1, buf, n);
  140. free(buf);
  141. }
  142. /* does in-place formatting of arguments, write()s each one separately */
  143. void echo_inplace(int argc, char **argv)
  144. {
  145. size_t n;
  146. while (*argv) {
  147. n = echo_unescape(*argv, *argv);
  148. (*argv)[n++] = argv[1] ? ' ' : '\n';
  149. write(1, *argv++, n);
  150. }
  151. }
  152. /* does in-place formatting of arguments, fwrite each one separately */
  153. void echo_finplace(int argc, char **argv)
  154. {
  155. size_t n;
  156. while (*argv) {
  157. n = echo_unescape(*argv, *argv);
  158. (*argv)[n++] = argv[1] ? ' ' : '\n';
  159. fwrite(*argv++, 1, n, stdout);
  160. }
  161. fflush(stdout);
  162. }
  163. /* uses putchar() for each character */
  164. void echo_putchar(int argc, char **argv)
  165. {
  166. bool escape;
  167. escape = false;
  168. while (*argv) {
  169. while (**argv) {
  170. if (escape) {
  171. switch (**argv) {
  172. case 'a':
  173. putchar('\a');
  174. break;
  175. case 'b':
  176. putchar('\b');
  177. break;
  178. case 'f':
  179. putchar('\f');
  180. break;
  181. case 'n':
  182. putchar('\n');
  183. break;
  184. case 'r':
  185. putchar('\r');
  186. break;
  187. case 't':
  188. putchar('\t');
  189. break;
  190. case 'v':
  191. putchar('\v');
  192. break;
  193. /* \xHH and \0ooo would come here */
  194. default:
  195. putchar(**argv);
  196. break;
  197. }
  198. escape = false;
  199. } else {
  200. if (**argv == '\\') {
  201. escape = true;
  202. } else {
  203. putchar(**argv);
  204. }
  205. }
  206. ++*argv;
  207. }
  208. putchar(argv[1] ? ' ' : '\n');
  209. ++argv;
  210. }
  211. fflush(stdout);
  212. }
  213. /* uses putchar_unlocked() for each character */
  214. void echo_putcharu(int argc, char **argv)
  215. {
  216. bool escape;
  217. escape = false;
  218. while (*argv) {
  219. while (**argv) {
  220. if (escape) {
  221. switch (**argv) {
  222. case 'a':
  223. putchar_unlocked('\a');
  224. break;
  225. case 'b':
  226. putchar_unlocked('\b');
  227. break;
  228. case 'f':
  229. putchar_unlocked('\f');
  230. break;
  231. case 'n':
  232. putchar_unlocked('\n');
  233. break;
  234. case 'r':
  235. putchar_unlocked('\r');
  236. break;
  237. case 't':
  238. putchar_unlocked('\t');
  239. break;
  240. case 'v':
  241. putchar_unlocked('\v');
  242. break;
  243. /* \xHH and \0ooo would come here */
  244. default:
  245. putchar_unlocked(**argv);
  246. break;
  247. }
  248. escape = false;
  249. } else {
  250. if (**argv == '\\') {
  251. escape = true;
  252. } else {
  253. putchar_unlocked(**argv);
  254. }
  255. }
  256. ++*argv;
  257. }
  258. putchar_unlocked(argv[1] ? ' ' : '\n');
  259. ++argv;
  260. }
  261. fflush(stdout);
  262. }
  263. /* uses write() for each character */
  264. void echo_write(int argc, char **argv)
  265. {
  266. bool escape;
  267. char c;
  268. escape = false;
  269. while (*argv) {
  270. while (**argv) {
  271. c = **argv;
  272. if (escape) {
  273. switch (c) {
  274. case 'a':
  275. c = '\a';
  276. break;
  277. case 'b':
  278. c = '\b';
  279. break;
  280. case 'f':
  281. c = '\f';
  282. break;
  283. case 'n':
  284. c = '\n';
  285. break;
  286. case 'r':
  287. c = '\r';
  288. break;
  289. case 't':
  290. c = '\t';
  291. break;
  292. case 'v':
  293. c = '\v';
  294. break;
  295. /* \xHH and \0ooo would come here */
  296. }
  297. escape = false;
  298. write(1, &c, 1);
  299. } else {
  300. if (c == '\\') {
  301. escape = true;
  302. } else {
  303. write(1, &c, 1);
  304. }
  305. }
  306. ++*argv;
  307. }
  308. c = argv[1] ? ' ' : '\n';
  309. write(1, &c, 1);
  310. ++argv;
  311. }
  312. }
  313. /* unescapes src into dst (may be the same), returns strlen(dst) */
  314. size_t echo_unescape(char *dst, const char *src) {
  315. bool escape;
  316. char c;
  317. size_t n;
  318. n = 0;
  319. escape = false;
  320. while (*src) {
  321. c = *src;
  322. if (escape) {
  323. switch (c) {
  324. case 'a':
  325. c = '\a';
  326. break;
  327. case 'b':
  328. c = '\b';
  329. break;
  330. case 'f':
  331. c = '\f';
  332. break;
  333. case 'n':
  334. c = '\n';
  335. break;
  336. case 'r':
  337. c = '\r';
  338. break;
  339. case 't':
  340. c = '\t';
  341. break;
  342. case 'v':
  343. c = '\v';
  344. break;
  345. /* \xHH and \0ooo would come here */
  346. }
  347. escape = false;
  348. *dst++ = c;
  349. ++n;
  350. } else {
  351. if (c == '\\') {
  352. escape = true;
  353. } else {
  354. *dst++ = c;
  355. ++n;
  356. }
  357. }
  358. ++src;
  359. }
  360. *dst = '\0';
  361. return n;
  362. }
  363. int main(int argc, char **argv) {
  364. int devnull;
  365. devnull = open("/dev/null", O_WRONLY, 0644);
  366. if (devnull < 0) {
  367. abort();
  368. }
  369. dup2(1, 2);
  370. dup2(devnull, 1);
  371. test_init(argc-1, argv+1);
  372. fprintf(stderr, "echo_null (%d runs): %" PRId64 "us\n", NITER, bench(test_null, NITER));
  373. fprintf(stderr, "echo_buf (%d runs): %" PRId64 "us\n", NITER, bench(test_buf, NITER));
  374. fprintf(stderr, "echo_inplace (%d runs): %" PRId64 "us\n", NITER, bench(test_inplace, NITER));
  375. fprintf(stderr, "echo_finplace (%d runs): %" PRId64 "us\n", NITER, bench(test_finplace, NITER));
  376. fprintf(stderr, "echo_putchar (%d runs): %" PRId64 "us\n", NITER, bench(test_putchar, NITER));
  377. fprintf(stderr, "echo_putcharu (%d runs): %" PRId64 "us\n", NITER, bench(test_putcharu, NITER));
  378. fprintf(stderr, "echo_write (%d runs): %" PRId64 "us\n", NITER, bench(test_write, NITER));
  379. test_free();
  380. close(devnull);
  381. return 0;
  382. }