You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

522 lines
16 KiB

  1. #pragma once
  2. #define _CRT_SECURE_NO_WARNINGS
  3. #include <stdarg.h>
  4. #include <stdbool.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include "platform.hpp"
  9. #ifdef FTB_WINDOWS
  10. #include <Windows.h>
  11. #endif
  12. #include "hashmap.hpp"
  13. #include "hooks.hpp"
  14. FILE* ftb_stdout = stdout;
  15. #define str_eq(s1, s2) (strcmp(s1, s2) == 0)
  16. #define console_normal "\x1B[0m"
  17. #define console_red "\x1B[31m"
  18. #define console_green "\x1B[32m"
  19. #define console_cyan "\x1B[36m"
  20. typedef const char* static_string;
  21. typedef int (*printer_function_32b)(FILE*, u32);
  22. typedef int (*printer_function_64b)(FILE*, u64);
  23. typedef int (*printer_function_flt)(FILE*, double);
  24. typedef int (*printer_function_ptr)(FILE*, void*);
  25. typedef int (*printer_function_void)(FILE*);
  26. enum struct Printer_Function_Type {
  27. unknown,
  28. _32b,
  29. _64b,
  30. _flt,
  31. _ptr,
  32. _void
  33. };
  34. Array_List<char*> color_stack = {0};
  35. Hash_Map<char*, printer_function_ptr> printer_map = {0};
  36. Hash_Map<char*, int> type_map = {0};
  37. #define register_printer(spec, fun, type) \
  38. register_printer_ptr(spec, (printer_function_ptr)fun, type)
  39. void register_printer_ptr(const char* spec, printer_function_ptr fun, Printer_Function_Type type) {
  40. printer_map.set_object((char*)spec, fun);
  41. type_map.set_object((char*)spec, (int)type);
  42. }
  43. int maybe_special_print(FILE* file, static_string format, int* pos, va_list* arg_list) {
  44. if(format[*pos] != '{')
  45. return 0;
  46. int end_pos = (*pos)+1;
  47. while (format[end_pos] != '}' &&
  48. format[end_pos] != ',' &&
  49. format[end_pos] != '[' &&
  50. format[end_pos] != 0)
  51. ++end_pos;
  52. if (format[end_pos] == 0)
  53. return 0;
  54. char* spec = (char*)ftb_alloca(end_pos - (*pos));
  55. strncpy(spec, format+(*pos)+1, end_pos - (*pos));
  56. spec[end_pos - (*pos)-1] = '\0';
  57. u64 spec_hash = hm_hash(spec);
  58. Printer_Function_Type type = (Printer_Function_Type)type_map.get_object(spec, spec_hash);
  59. union {
  60. printer_function_32b printer_32b;
  61. printer_function_64b printer_64b;
  62. printer_function_ptr printer_ptr;
  63. printer_function_flt printer_flt;
  64. printer_function_void printer_void;
  65. } printer;
  66. // just grab it, it will have the correct type
  67. printer.printer_ptr = printer_map.get_object(spec, spec_hash);
  68. if (type == Printer_Function_Type::unknown) {
  69. printf("ERROR: %s printer not found\n", spec);
  70. ftb_free(spec);
  71. return 0;
  72. }
  73. if (format[end_pos] == ',') {
  74. int element_count;
  75. ++end_pos;
  76. sscanf(format+end_pos, "%d", &element_count);
  77. while (format[end_pos] != '}' &&
  78. format[end_pos] != 0)
  79. ++end_pos;
  80. if (format[end_pos] == 0)
  81. return 0;
  82. // both brackets already included:
  83. int written_length = 2;
  84. fputs("[", file);
  85. for (int i = 0; i < element_count - 1; ++i) {
  86. if (type == Printer_Function_Type::_32b) written_length += printer.printer_32b(file, va_arg(*arg_list, u32));
  87. else if (type == Printer_Function_Type::_64b) written_length += printer.printer_64b(file, va_arg(*arg_list, u64));
  88. else if (type == Printer_Function_Type::_flt) written_length += printer.printer_flt(file, va_arg(*arg_list, double));
  89. else if (type == Printer_Function_Type::_ptr) written_length += printer.printer_ptr(file, va_arg(*arg_list, void*));
  90. else written_length += printer.printer_void(file);
  91. written_length += 2;
  92. fputs(", ", file);
  93. }
  94. if (element_count > 0) {
  95. if (type == Printer_Function_Type::_32b) written_length += printer.printer_32b(file, va_arg(*arg_list, u32));
  96. else if (type == Printer_Function_Type::_64b) written_length += printer.printer_64b(file, va_arg(*arg_list, u64));
  97. else if (type == Printer_Function_Type::_flt) written_length += printer.printer_flt(file, va_arg(*arg_list, double));
  98. else if (type == Printer_Function_Type::_ptr) written_length += printer.printer_ptr(file, va_arg(*arg_list, void*));
  99. else written_length += printer.printer_void(file);
  100. }
  101. fputs("]", file);
  102. *pos = end_pos;
  103. return written_length;
  104. } else if (format[end_pos] == '[') {
  105. end_pos++;
  106. u32 element_count;
  107. union {
  108. u32* arr_32b;
  109. u64* arr_64b;
  110. f32* arr_flt;
  111. void** arr_ptr;
  112. } arr;
  113. if (type == Printer_Function_Type::_32b) arr.arr_32b = va_arg(*arg_list, u32*);
  114. else if (type == Printer_Function_Type::_64b) arr.arr_64b = va_arg(*arg_list, u64*);
  115. else if (type == Printer_Function_Type::_flt) arr.arr_flt = va_arg(*arg_list, f32*);
  116. else arr.arr_ptr = va_arg(*arg_list, void**);
  117. if (format[end_pos] == '*') {
  118. element_count = va_arg(*arg_list, u32);
  119. } else {
  120. sscanf(format+end_pos, "%d", &element_count);
  121. }
  122. // skip to next ']'
  123. while (format[end_pos] != ']' &&
  124. format[end_pos] != 0)
  125. ++end_pos;
  126. if (format[end_pos] == 0)
  127. return 0;
  128. // skip to next '}'
  129. while (format[end_pos] != '}' &&
  130. format[end_pos] != 0)
  131. ++end_pos;
  132. if (format[end_pos] == 0)
  133. return 0;
  134. // both brackets already included:
  135. int written_length = 2;
  136. fputs("[", file);
  137. for (u32 i = 0; i < element_count - 1; ++i) {
  138. if (type == Printer_Function_Type::_32b) written_length += printer.printer_32b(file, arr.arr_32b[i]);
  139. else if (type == Printer_Function_Type::_64b) written_length += printer.printer_64b(file, arr.arr_64b[i]);
  140. else if (type == Printer_Function_Type::_flt) written_length += printer.printer_flt(file, arr.arr_flt[i]);
  141. else if (type == Printer_Function_Type::_ptr) written_length += printer.printer_ptr(file, arr.arr_ptr[i]);
  142. else written_length += printer.printer_void(file);
  143. written_length += 2;
  144. fputs(", ", file);
  145. }
  146. if (element_count > 0) {
  147. if (type == Printer_Function_Type::_32b) written_length += printer.printer_32b(file, arr.arr_32b[element_count - 1]);
  148. else if (type == Printer_Function_Type::_64b) written_length += printer.printer_64b(file, arr.arr_64b[element_count - 1]);
  149. else if (type == Printer_Function_Type::_flt) written_length += printer.printer_flt(file, arr.arr_flt[element_count - 1]);
  150. else if (type == Printer_Function_Type::_ptr) written_length += printer.printer_ptr(file, arr.arr_ptr[element_count - 1]);
  151. else written_length += printer.printer_void(file);
  152. }
  153. fputs("]", file);
  154. *pos = end_pos;
  155. return written_length;
  156. } else {
  157. *pos = end_pos;
  158. if (type == Printer_Function_Type::_32b) return printer.printer_32b(file, va_arg(*arg_list, u32));
  159. else if (type == Printer_Function_Type::_64b) return printer.printer_64b(file, va_arg(*arg_list, u64));
  160. else if (type == Printer_Function_Type::_flt) return printer.printer_flt(file, va_arg(*arg_list, double));
  161. else if (type == Printer_Function_Type::_ptr) return printer.printer_ptr(file, va_arg(*arg_list, void*));
  162. else return printer.printer_void(file);
  163. }
  164. return 0;
  165. }
  166. int maybe_fprintf(FILE* file, static_string format, int* pos, va_list* arg_list) {
  167. // %[flags][width][.precision][length]specifier
  168. // flags ::= [+- #0]
  169. // width ::= [<number>+ \*]
  170. // precision ::= \.[<number>+ \*]
  171. // length ::= [h l L]
  172. // specifier ::= [c d i e E f g G o s u x X p n %]
  173. int end_pos = *pos;
  174. int writen_len = 0;
  175. int used_arg_values = 1;
  176. // overstep flags:
  177. while(format[end_pos] == '+' ||
  178. format[end_pos] == '-' ||
  179. format[end_pos] == ' ' ||
  180. format[end_pos] == '#' ||
  181. format[end_pos] == '0')
  182. ++end_pos;
  183. // overstep width
  184. if (format[end_pos] == '*') {
  185. ++used_arg_values;
  186. ++end_pos;
  187. }
  188. else {
  189. while(format[end_pos] >= '0' && format[end_pos] <= '9')
  190. ++end_pos;
  191. }
  192. // overstep precision
  193. if (format[end_pos] == '.') {
  194. ++end_pos;
  195. if (format[end_pos] == '*') {
  196. ++used_arg_values;
  197. ++end_pos;
  198. }
  199. else {
  200. while(format[end_pos] >= '0' && format[end_pos] <= '9')
  201. ++end_pos;
  202. }
  203. }
  204. // overstep length:
  205. while(format[end_pos] == 'h' ||
  206. format[end_pos] == 'l' ||
  207. format[end_pos] == 'L')
  208. ++end_pos;
  209. // check for actual built_in specifier
  210. if(format[end_pos] == 'c' ||
  211. format[end_pos] == 'd' ||
  212. format[end_pos] == 'i' ||
  213. format[end_pos] == 'e' ||
  214. format[end_pos] == 'E' ||
  215. format[end_pos] == 'f' ||
  216. format[end_pos] == 'g' ||
  217. format[end_pos] == 'G' ||
  218. format[end_pos] == 'o' ||
  219. format[end_pos] == 's' ||
  220. format[end_pos] == 'u' ||
  221. format[end_pos] == 'x' ||
  222. format[end_pos] == 'X' ||
  223. format[end_pos] == 'p' ||
  224. format[end_pos] == 'n' ||
  225. format[end_pos] == '%')
  226. {
  227. writen_len = end_pos - *pos + 2;
  228. char* temp = (char*)alloca((writen_len+1)* sizeof(char));
  229. temp[0] = '%';
  230. temp[1] = 0;
  231. strncpy(temp+1, format+*pos, writen_len);
  232. temp[writen_len] = 0;
  233. // printf("\ntest:: len(%s) = %d\n", temp, writen_len+1);
  234. /// NOTE(Felix): Somehow we have to pass a copy of the list to vfprintf
  235. // because otherwise it destroys it on some platforms :(
  236. va_list arg_list_copy;
  237. va_copy(arg_list_copy, *arg_list);
  238. writen_len = vfprintf(file, temp, arg_list_copy);
  239. va_end(arg_list_copy);
  240. // NOTE(Felix): maually overstep the args that vfprintf will have used
  241. for (int i = 0; i < used_arg_values; ++i) {
  242. va_arg(*arg_list, void*);
  243. }
  244. *pos = end_pos;
  245. }
  246. return writen_len;
  247. }
  248. int print_va_args_to_file(FILE* file, static_string format, va_list* arg_list) {
  249. int printed_chars = 0;
  250. char c;
  251. int pos = -1;
  252. while ((c = format[++pos])) {
  253. if (c != '%') {
  254. putc(c, file);
  255. ++printed_chars;
  256. } else {
  257. c = format[++pos];
  258. int move = maybe_special_print(file, format, &pos, arg_list);
  259. if (move == 0) {
  260. move = maybe_fprintf(file, format, &pos, arg_list);
  261. if (move == -1) {
  262. fputc('%', file);
  263. fputc(c, file);
  264. move = 1;
  265. }
  266. }
  267. printed_chars += move;
  268. }
  269. }
  270. return printed_chars;
  271. }
  272. int print_va_args_to_string(char** out, static_string format, va_list* arg_list) {
  273. FILE* t_file = tmpfile();
  274. if (!t_file) {
  275. return 0;
  276. }
  277. int num_printed_chars = print_va_args_to_file(t_file, format, arg_list);
  278. *out = (char*)ftb_malloc(sizeof(char) * (num_printed_chars+1));
  279. rewind(t_file);
  280. fread(*out, sizeof(char), num_printed_chars, t_file);
  281. (*out)[num_printed_chars] = '\0';
  282. fclose(t_file);
  283. return num_printed_chars;
  284. }
  285. int print_va_args(static_string format, va_list* arg_list) {
  286. return print_va_args_to_file(stdout, format, arg_list);
  287. }
  288. int print_to_string(char** out, static_string format, ...) {
  289. va_list arg_list;
  290. va_start(arg_list, format);
  291. FILE* t_file = tmpfile();
  292. if (!t_file) {
  293. return 0;
  294. }
  295. int num_printed_chars = print_va_args_to_file(t_file, format, &arg_list);
  296. va_end(arg_list);
  297. *out = (char*)ftb_malloc(sizeof(char) * (num_printed_chars+1));
  298. rewind(t_file);
  299. fread(*out, sizeof(char), num_printed_chars, t_file);
  300. (*out)[num_printed_chars] = '\0';
  301. fclose(t_file);
  302. return num_printed_chars;
  303. }
  304. int print_to_file(FILE* file, static_string format, ...) {
  305. va_list arg_list;
  306. va_start(arg_list, format);
  307. int num_printed_chars = print_va_args_to_file(file, format, &arg_list);
  308. va_end(arg_list);
  309. return num_printed_chars;
  310. }
  311. int print(static_string format, ...) {
  312. va_list arg_list;
  313. va_start(arg_list, format);
  314. int num_printed_chars = print_va_args_to_file(ftb_stdout, format, &arg_list);
  315. va_end(arg_list);
  316. return num_printed_chars;
  317. }
  318. int println(static_string format, ...) {
  319. va_list arg_list;
  320. va_start(arg_list, format);
  321. int num_printed_chars = print_va_args_to_file(ftb_stdout, format, &arg_list);
  322. num_printed_chars += print("\n");
  323. fflush(stdout);
  324. va_end(arg_list);
  325. return num_printed_chars;
  326. }
  327. int print_bool(FILE* f, u32 val) {
  328. return print_to_file(f, val ? "true" : "false");
  329. }
  330. int print_u32(FILE* f, u32 num) {
  331. return print_to_file(f, "%u", num);
  332. }
  333. int print_spaces(FILE* f, s32 num) {
  334. int sum = 0;
  335. while (num >= 8) {
  336. // println("%d", 8);
  337. sum += print_to_file(f, " ");
  338. num -= 8;
  339. }
  340. while (num >= 4) {
  341. // println("%d", 4);
  342. sum += print_to_file(f, " ");
  343. num -= 4;
  344. }
  345. while (num --> 0) {
  346. // println("%d", 1);
  347. sum += print_to_file(f, " ");
  348. num--;
  349. }
  350. return sum;
  351. }
  352. int print_u64(FILE* f, u64 num) {
  353. return print_to_file(f, "%llu", num);
  354. }
  355. int print_s32(FILE* f, s32 num) {
  356. return print_to_file(f, "%d", num);
  357. }
  358. int print_s64(FILE* f, s64 num) {
  359. return print_to_file(f, "%lld", num);
  360. }
  361. int print_flt(FILE* f, double arg) {
  362. return print_to_file(f, "%f", arg);
  363. }
  364. int print_str(FILE* f, char* str) {
  365. return print_to_file(f, "%s", str);
  366. }
  367. int print_color_start(FILE* f, char* str) {
  368. color_stack.append(str);
  369. return print_to_file(f, "%s", str);
  370. }
  371. int print_color_end(FILE* f) {
  372. --color_stack.count;
  373. if (color_stack.count == 0) {
  374. return print_to_file(f, "%s", console_normal);
  375. } else {
  376. return print_to_file(f, "%s", color_stack[color_stack.count-1]);
  377. }
  378. }
  379. int print_ptr(FILE* f, void* ptr) {
  380. if (ptr)
  381. return print_to_file(f, "%#0*X", sizeof(void*)*2+2, ptr);
  382. return print_to_file(f, "nullptr");
  383. }
  384. auto print_Str(FILE* f, String* str) -> s32 {
  385. return print_to_file(f, "%.*s", str->length, str->data);
  386. }
  387. auto print_str_line(FILE* f, char* str) -> s32 {
  388. u32 length = 0;
  389. while (str[length] != '\0') {
  390. if (str[length] == '\n')
  391. break;
  392. length++;
  393. }
  394. return print_to_file(f, "%.*s", length, str);
  395. }
  396. void init_printer() {
  397. #ifdef FTB_WINDOWS
  398. // enable colored terminal output for windows
  399. HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
  400. DWORD dwMode = 0;
  401. GetConsoleMode(hOut, &dwMode);
  402. dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
  403. SetConsoleMode(hOut, dwMode);
  404. #endif
  405. color_stack.alloc();
  406. printer_map.alloc();
  407. type_map.alloc();
  408. register_printer("spaces", print_spaces, Printer_Function_Type::_32b);
  409. register_printer("u32", print_u32, Printer_Function_Type::_32b);
  410. register_printer("u64", print_u64, Printer_Function_Type::_64b);
  411. register_printer("bool", print_bool, Printer_Function_Type::_32b);
  412. register_printer("s64", print_s64, Printer_Function_Type::_64b);
  413. register_printer("s32", print_s32, Printer_Function_Type::_32b);
  414. register_printer("f32", print_flt, Printer_Function_Type::_flt);
  415. register_printer("f64", print_flt, Printer_Function_Type::_flt);
  416. register_printer("->char", print_str, Printer_Function_Type::_ptr);
  417. register_printer("->", print_ptr, Printer_Function_Type::_ptr);
  418. register_printer("color<", print_color_start, Printer_Function_Type::_ptr);
  419. register_printer(">color", print_color_end, Printer_Function_Type::_void);
  420. register_printer("->Str", print_Str, Printer_Function_Type::_ptr);
  421. register_printer("->char_line", print_str_line, Printer_Function_Type::_ptr);
  422. }
  423. void deinit_printer() {
  424. color_stack.dealloc();
  425. printer_map.dealloc();
  426. type_map.dealloc();
  427. }