par_shaders.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. // SHADERS :: https://github.com/prideout/par
  2. // String extraction and concatenation utilities designed for shaders.
  3. //
  4. // This little library extracts blocks of text from a memory blob,
  5. // then lets you retrieve them by name or dump them out to a C header.
  6. // It also makes it easy to glue together a sequence of blocks.
  7. //
  8. // Each block of text is assigned a name using a prefix line that starts with
  9. // two dash characters, such as "-- the_name" or "-- my.block".
  10. //
  11. // For example, the contents of your memory blob could look like this:
  12. //
  13. // -- my_vs
  14. // void main() { ... }
  15. //
  16. // -- my_decls
  17. // uniform vec4 resolution;
  18. // uniform vec4 color;
  19. //
  20. // You could then extract text blocks from the memory blob like so:
  21. //
  22. // parsh_context* ctx = parsh_create_context({});
  23. // parsh_add_blocks(ctx, pointer_to_blob, size_of_blob_in_bytes);
  24. // free(pointer_to_blob);
  25. // parsh_add_block(ctx, "prefix", "#version 330\n");
  26. // const char* concatenated = parsh_get_blocks(ctx, "prefix my_decls my_vs");
  27. // ...use "concatenated" here...
  28. // parsh_destroy_context(ctx);
  29. //
  30. // This library is similar to the string wrangling library described
  31. // in the following old post, but has been updated to be a no-dependency
  32. // single-file library in the style of the STB libraries.
  33. //
  34. // https://prideout.net/blog/old/blog/index.html@p=11.html
  35. //
  36. // Distributed under the MIT License, see bottom of file.
  37. #ifndef PAR_SHADERS_H
  38. #define PAR_SHADERS_H
  39. #ifdef __cplusplus
  40. extern "C" {
  41. #endif
  42. #include <stdbool.h>
  43. #include <stddef.h>
  44. #include <stdint.h>
  45. typedef struct {
  46. bool enable_line_directives;
  47. } parsh_config;
  48. // Opaque handle to a memory arena. All generated strings are owned by the
  49. // library, and freed when the context is destroyed.
  50. typedef struct parsh_context_s parsh_context;
  51. parsh_context* parsh_create_context(parsh_config);
  52. void parsh_destroy_context(parsh_context*);
  53. void parsh_add_blocks(parsh_context*, const char* buffer, size_t buffer_size);
  54. void parsh_add_block(parsh_context*, const char* name, const char* body);
  55. const char* parsh_get_blocks(parsh_context*, const char* block_names);
  56. typedef void (*parsh_write_line)(const char* line, void* userdata);
  57. void parsh_write_cstring(parsh_context*, parsh_write_line writefn, void* user);
  58. parsh_context* parsh_create_context_from_file(const char* filename);
  59. void parsh_add_blocks_from_file(parsh_context* context, const char* filename);
  60. #ifndef PARSH_MAX_NUM_BLOCKS
  61. #define PARSH_MAX_NUM_BLOCKS 128
  62. #endif
  63. #ifndef PARSH_MAX_NAME_LENGTH
  64. #define PARSH_MAX_NAME_LENGTH 256
  65. #endif
  66. #ifndef PARSH_MAX_LINE_LENGTH
  67. #define PARSH_MAX_LINE_LENGTH 256
  68. #endif
  69. #ifdef __cplusplus
  70. }
  71. #endif
  72. // -----------------------------------------------------------------------------
  73. // END PUBLIC API
  74. // -----------------------------------------------------------------------------
  75. #ifdef PAR_SHADERS_IMPLEMENTATION
  76. #include <assert.h>
  77. #include <ctype.h>
  78. #include <stdio.h>
  79. #include <stdlib.h>
  80. #include <string.h>
  81. #ifndef PAR_PI
  82. #define PAR_PI (3.14159265359)
  83. #define PAR_MIN(a, b) (a > b ? b : a)
  84. #define PAR_MAX(a, b) (a > b ? a : b)
  85. #define PAR_CLAMP(v, lo, hi) PAR_MAX(lo, PAR_MIN(hi, v))
  86. #define PAR_SWAP(T, A, B) { T tmp = B; B = A; A = tmp; }
  87. #define PAR_SQR(a) ((a) * (a))
  88. #endif
  89. typedef struct {
  90. size_t count;
  91. char* values[PARSH_MAX_NUM_BLOCKS];
  92. char* names[PARSH_MAX_NUM_BLOCKS];
  93. } parsh__list;
  94. struct parsh_context_s {
  95. parsh_config config;
  96. parsh__list blocks;
  97. parsh__list results;
  98. };
  99. static char* parsh__list_add(parsh__list*, const char* id,
  100. const char* value, size_t value_size, int line_number);
  101. static const char* parsh__list_get(parsh__list*, const char* id, size_t idlen);
  102. static void parsh__list_free(parsh__list* );
  103. parsh_context* parsh_create_context(parsh_config config) {
  104. parsh_context* context = (parsh_context*) calloc(1, sizeof(parsh_context));
  105. context->config = config;
  106. return context;
  107. }
  108. void parsh_destroy_context(parsh_context* context) {
  109. parsh__list_free(&context->blocks);
  110. parsh__list_free(&context->results);
  111. free(context);
  112. }
  113. void parsh_add_blocks(parsh_context* context, const char* blob,
  114. size_t buffer_size) {
  115. const char* previous_block = 0;
  116. char previous_name[PARSH_MAX_NAME_LENGTH];
  117. int line_number = 0;
  118. int block_line_number = 0;
  119. for (size_t i = 0; i < buffer_size - 3; i++) {
  120. if (blob[i] != '-' || blob[i + 1] != '-' || blob[i + 2] != ' ') {
  121. if (blob[i] == '\n') {
  122. line_number++;
  123. }
  124. continue;
  125. }
  126. if (previous_block) {
  127. parsh__list_add(&context->blocks, previous_name, previous_block,
  128. i - (previous_block - blob),
  129. context->config.enable_line_directives ? block_line_number : 0);
  130. }
  131. i += 3;
  132. const char* name = blob + i;
  133. const char* block_start = 0;
  134. for (; i < buffer_size; i++) {
  135. if (blob[i] == '\n') {
  136. line_number++;
  137. size_t name_length = i - (name - blob);
  138. memcpy(previous_name, name, name_length);
  139. block_line_number = line_number + 2;
  140. previous_name[name_length] = 0;
  141. block_start = blob + i + 1;
  142. break;
  143. }
  144. if (isspace(blob[i])) {
  145. size_t name_length = i - (name - blob);
  146. memcpy(previous_name, name, name_length);
  147. block_line_number = line_number + 2;
  148. previous_name[name_length] = 0;
  149. for (i++; i < buffer_size; i++) {
  150. if (blob[i] == '\n') {
  151. line_number++;
  152. block_start = blob + i + 1;
  153. break;
  154. }
  155. }
  156. break;
  157. }
  158. }
  159. if (block_start == 0) {
  160. return;
  161. }
  162. previous_block = block_start;
  163. }
  164. if (previous_block) {
  165. parsh__list_add(&context->blocks, previous_name, previous_block,
  166. buffer_size - (previous_block - blob),
  167. context->config.enable_line_directives ? block_line_number : 0);
  168. }
  169. }
  170. void parsh_add_block(parsh_context* context, const char* name,
  171. const char* body) {
  172. char* dup = (char*) malloc(strlen(body) + 1);
  173. memcpy(dup, body, 1 + strlen(body));
  174. parsh__list_add(&context->blocks, name, dup, 1 + strlen(body), 0);
  175. }
  176. const char* parsh_get_blocks(parsh_context* context, const char* block_names) {
  177. size_t len = strlen(block_names);
  178. const char* name = block_names;
  179. size_t name_length = 0;
  180. size_t result_length = 0;
  181. // First pass determines the amount of required memory.
  182. size_t num_names = 0;
  183. for (size_t i = 0; i < len; i++) {
  184. char c = block_names[i];
  185. if (isspace(c) || !c) {
  186. const char* block = parsh__list_get(&context->blocks, name,
  187. name_length);
  188. if (block) {
  189. result_length += strlen(block);
  190. num_names++;
  191. }
  192. name_length = 0;
  193. name = block_names + i + 1;
  194. } else {
  195. name_length++;
  196. }
  197. }
  198. const char* block = parsh__list_get(&context->blocks, name,
  199. name_length);
  200. if (block) {
  201. result_length += strlen(block);
  202. num_names++;
  203. }
  204. // If no concatenation is required, return early.
  205. if (num_names == 1) {
  206. return parsh__list_get(&context->blocks, name, name_length);
  207. }
  208. // Allocate storage for the result.
  209. char* result = parsh__list_add(&context->results, 0, 0, result_length, 0);
  210. char* cursor = result;
  211. // Second pass populates the result.
  212. name = block_names;
  213. name_length = 0;
  214. for (size_t i = 0; i < len; i++) {
  215. char c = block_names[i];
  216. if (isspace(c) || !c) {
  217. const char* block = parsh__list_get(&context->blocks, name,
  218. name_length);
  219. if (block) {
  220. memcpy(cursor, block, strlen(block));
  221. cursor += strlen(block);
  222. }
  223. name_length = 0;
  224. name = block_names + i + 1;
  225. } else {
  226. name_length++;
  227. }
  228. }
  229. block = parsh__list_get(&context->blocks, name, name_length);
  230. if (block) {
  231. memcpy(cursor, block, strlen(block));
  232. cursor += strlen(block);
  233. }
  234. return result;
  235. }
  236. void parsh_write_cstring(parsh_context* context, parsh_write_line writefn,
  237. void* userdata) {
  238. char line[PARSH_MAX_LINE_LENGTH + 4] = {0};
  239. for (size_t i = 0; i < context->blocks.count; i++) {
  240. sprintf(line, "\"-- %s\\n\"", context->blocks.names[i]);
  241. writefn(line, userdata);
  242. const char* cursor = context->blocks.values[i];
  243. const size_t blocklen = strlen(cursor);
  244. size_t previous = 0;
  245. for (size_t i = 0; i < blocklen; i++) {
  246. if (cursor[i] == '\n' || i == blocklen - 1) {
  247. size_t line_length = PAR_MIN(i - previous,
  248. PARSH_MAX_LINE_LENGTH);
  249. if (i == blocklen - 1) {
  250. line_length++;
  251. }
  252. line[0] = '\"';
  253. memcpy(line + 1, cursor + previous, line_length);
  254. line[1 + line_length] = '\\';
  255. line[2 + line_length] = 'n';
  256. line[3 + line_length] = '\"';
  257. line[4 + line_length] = 0;
  258. writefn(line, userdata);
  259. previous = i + 1;
  260. }
  261. }
  262. }
  263. }
  264. static char* parsh__list_add(parsh__list* list, const char* name,
  265. const char* value, size_t value_size, int line_number) {
  266. if (value_size == 0) {
  267. return 0;
  268. }
  269. if (list->count == PARSH_MAX_NUM_BLOCKS) {
  270. assert(false && "Please increase PARSH_MAX_NUM_BLOCKS.");
  271. return 0;
  272. }
  273. char* storage;
  274. char* cursor;
  275. if (line_number > 0) {
  276. char line_directive[16] = {0};
  277. size_t prefix_length =
  278. snprintf(line_directive, 16, "\n#line %d\n", line_number);
  279. storage = (char*) calloc(1, prefix_length + value_size + 1);
  280. memcpy(storage, line_directive, prefix_length);
  281. cursor = storage + prefix_length;
  282. } else {
  283. storage = cursor = (char*) calloc(1, value_size + 1);
  284. }
  285. if (value) {
  286. memcpy(cursor, value, value_size--);
  287. }
  288. while (isspace(cursor[value_size])) {
  289. cursor[value_size] = 0;
  290. value_size--;
  291. if (value_size == 0) {
  292. break;
  293. }
  294. }
  295. if (name) {
  296. char* dup = (char*) malloc(strlen(name) + 1);
  297. memcpy(dup, name, strlen(name) + 1);
  298. list->names[list->count] = dup;
  299. } else {
  300. list->names[list->count] = 0;
  301. }
  302. list->values[list->count] = storage;
  303. list->count++;
  304. return storage;
  305. }
  306. static const char* parsh__list_get(parsh__list* list, const char* name,
  307. size_t idlen) {
  308. for (size_t i = 0; i < list->count; i++) {
  309. if (strncmp(name, list->names[i], idlen) == 0) {
  310. return list->values[i];
  311. }
  312. }
  313. return 0;
  314. }
  315. static void parsh__list_free(parsh__list* list) {
  316. for (size_t i = 0; i < list->count; i++) {
  317. free(list->names[i]);
  318. free(list->values[i]);
  319. }
  320. list->count = 0;
  321. }
  322. #ifdef PARSH_ENABLE_STDIO
  323. parsh_context* parsh_create_context_from_file(const char* filename) {
  324. FILE* f = fopen(filename, "rb");
  325. if (!f) {
  326. return NULL;
  327. }
  328. fseek(f, 0, SEEK_END);
  329. size_t length = ftell(f);
  330. fseek(f, 0, SEEK_SET);
  331. char* buffer = (char*) malloc(length);
  332. fread(buffer, 1, length, f);
  333. fclose(f);
  334. parsh_context* shaders = parsh_create_context((parsh_config){
  335. .enable_line_directives = true
  336. });
  337. parsh_add_blocks(shaders, buffer, length);
  338. free(buffer);
  339. return shaders;
  340. }
  341. void parsh_add_blocks_from_file(parsh_context* context, const char* filename) {
  342. FILE* f = fopen(filename, "rb");
  343. if (!f) {
  344. fprintf(stderr, "Unable to open %s\n", filename);
  345. return;
  346. }
  347. fseek(f, 0, SEEK_END);
  348. size_t length = ftell(f);
  349. fseek(f, 0, SEEK_SET);
  350. char* buffer = (char*) malloc(length);
  351. fread(buffer, 1, length, f);
  352. fclose(f);
  353. parsh_add_blocks(context, buffer, length);
  354. free(buffer);
  355. }
  356. #endif
  357. #ifdef PARSH_ENABLE_MAIN
  358. void write_line(const char* ln, void* userdata) {
  359. FILE* outfile = (FILE*) userdata;
  360. fputs(ln, outfile);
  361. fputc('\n', outfile);
  362. }
  363. int main(int argc, char** argv) {
  364. if (argc != 4) {
  365. puts("Usage: parsh srcfile dstfile array_name");
  366. return 1;
  367. }
  368. const char* srcfile = argv[1];
  369. const char* dstfile = argv[2];
  370. const char* array_name = argv[3];
  371. FILE *f = fopen(srcfile, "rb");
  372. fseek(f, 0, SEEK_END);
  373. size_t length = ftell(f);
  374. fseek(f, 0, SEEK_SET);
  375. char* buffer = malloc (length);
  376. fread(buffer, 1, length, f);
  377. fclose(f);
  378. parsh_context* ctx = parsh_create_context((parsh_config){
  379. .enable_line_directives = true
  380. });
  381. parsh_add_blocks(ctx, buffer, length);
  382. free(buffer);
  383. FILE* outfile = fopen(dstfile, "wt");
  384. fprintf(outfile, "const char %s[] = \n", array_name);
  385. parsh_write_cstring(ctx, write_line, outfile);
  386. fprintf(outfile, ";\n");
  387. fclose(outfile);
  388. parsh_destroy_context(ctx);
  389. return 0;
  390. }
  391. #endif
  392. #endif // PAR_SHADERS_IMPLEMENTATION
  393. #endif // PAR_SHADERS_H
  394. // par_shaders is distributed under the MIT license:
  395. //
  396. // Copyright (c) 2019 Philip Rideout
  397. //
  398. // Permission is hereby granted, free of charge, to any person obtaining a copy
  399. // of this software and associated documentation files (the "Software"), to deal
  400. // in the Software without restriction, including without limitation the rights
  401. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  402. // copies of the Software, and to permit persons to whom the Software is
  403. // furnished to do so, subject to the following conditions:
  404. //
  405. // The above copyright notice and this permission notice shall be included in
  406. // all copies or substantial portions of the Software.
  407. //
  408. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  409. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  410. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  411. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  412. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  413. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  414. // SOFTWARE.