Standard setup for writing C inspired by Casey Muratori, Ryan Fleury, Mr. 4th Programmer, and others in the handmade community.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

495 lignes
14 KiB

  1. #define STB_SPRINTF_IMPLEMENTATION
  2. #include "core.h"
  3. void *pushSize(Arena *arena, size_t bytes) {
  4. if (arena->capacity - arena->head >= bytes) {
  5. void *ptr = (char *)arena->memory + arena->head;
  6. arena->head += bytes;
  7. return ptr;
  8. }
  9. return 0;
  10. }
  11. Arena *arenaAlloc(size_t capacity) {
  12. #if OS_WINDOWS
  13. Arena *result = (Arena *)VirtualAlloc(NULL, sizeof(Arena) + capacity, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  14. #elif OS_LINUX
  15. Arena *result = (Arena *)mmap(0, capacity, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  16. #endif
  17. result->memory = result + sizeof(Arena);
  18. result->capacity = capacity;
  19. result->head = 0;
  20. return result;
  21. }
  22. void arenaFree(Arena *arena) {
  23. #if OS_WINDOWS
  24. VirtualFree(arena, NULL, MEM_RELEASE);
  25. #elif OS_LINUX
  26. // TODO(dledda): implement this for Linux
  27. Assert(false);
  28. #endif
  29. }
  30. void arenaFreeFrom(Arena *arena, size_t position) {
  31. arena->head = position;
  32. }
  33. Arena *scratchArenas[2];
  34. void initialiseCore() {
  35. for (EachInArray(scratchArenas, i)) {
  36. scratchArenas[i] = arenaAlloc(Megabytes(64));
  37. }
  38. }
  39. Scratch scratchStart(Arena **conflicts, size_t conflictCount) {
  40. Scratch scratch = {0};
  41. for (size_t i = 0; i < ArrayCount(scratchArenas); i += 1) {
  42. bool conflicted = false;
  43. for (Arena **conflict = conflicts; conflict < conflicts + conflictCount; conflict += 1) {
  44. if (*conflict == scratchArenas[i]) {
  45. conflicted = true;
  46. break;
  47. }
  48. }
  49. if (conflicted == false) {
  50. scratch.arena = scratchArenas[i];
  51. scratch.start = scratch.arena->head;
  52. break;
  53. }
  54. }
  55. return scratch;
  56. }
  57. void scratchEnd(Scratch scratch) {
  58. arenaFreeFrom(scratch.arena, scratch.start);
  59. }
  60. template <typename T>
  61. T *appendList(list<T> *list, T element) {
  62. if (list->head < list->length) {
  63. list->data[list->head] = element;
  64. list->head++;
  65. return &(list->data[list->head - 1]);
  66. } else {
  67. return 0;
  68. }
  69. }
  70. template <typename T>
  71. void zeroListFull(list<T> *list) {
  72. memset(list->data, 0, list->head * sizeof(T));
  73. }
  74. template <typename T>
  75. void zeroList(list<T> *list) {
  76. list->head = 0;
  77. memset(list->data, 0, list->head * sizeof(T));
  78. }
  79. const char *cstring(Arena *arena, list<char> buf) {
  80. char *arr = PushArray(arena, char, buf.length + 1);
  81. memmove(arr, buf.data, buf.length);
  82. arr[buf.length] = '\0';
  83. return arr;
  84. }
  85. const char *cstring(Arena *arena, string str) {
  86. char *arr = PushArray(arena, char, str.length + 1);
  87. memmove(arr, str.str, str.length);
  88. arr[str.length] = '\0';
  89. return arr;
  90. }
  91. bool strEql(string s1, string s2) {
  92. if (s1.length != s2.length) {
  93. return false;
  94. }
  95. for (size_t i = 0; i < s1.length; i++) {
  96. if (s1.str[i] != s2.str[i]) {
  97. return false;
  98. }
  99. }
  100. return true;
  101. }
  102. size_t calcStringLen(const char *str) {
  103. size_t size = 0;
  104. if (str == NULL) {
  105. return size;
  106. }
  107. while (str[size] != '\0') {
  108. size++;
  109. }
  110. return size;
  111. }
  112. string strFromCString(Arena *arena, const char *str) {
  113. string result = PushString(arena, calcStringLen(str));
  114. memcpy(result.str, str, result.length);
  115. return result;
  116. }
  117. string strReverse(Arena *arena, string str) {
  118. string reversed = PushString(arena, str.length);
  119. for (
  120. size_t mainIndex = str.length - 1, reversedIndex = 0;
  121. mainIndex < str.length;
  122. mainIndex--, reversedIndex++
  123. ) {
  124. reversed.str[reversedIndex] = str.str[mainIndex];
  125. }
  126. return reversed;
  127. }
  128. string strPrintfv(Arena *arena, const char *fmt, va_list args) {
  129. string result = {0};
  130. va_list argsCopy;
  131. va_copy(argsCopy, args);
  132. uint64 bufSize = stb_vsnprintf(0, 0, fmt, args) + 1;
  133. result.str = PushArray(arena, char, bufSize);
  134. result.length = bufSize - 1;
  135. stb_vsnprintf((char *)result.str, (int)bufSize, fmt, argsCopy);
  136. return result;
  137. }
  138. string strPrintf(Arena *arena, const char *fmt, ...) {
  139. string result = {0};
  140. va_list args;
  141. va_start(args, fmt);
  142. result = strPrintfv(arena, fmt, args);
  143. va_end(args);
  144. return result;
  145. }
  146. template <typename T>
  147. list<T> listSlice(list<T> l, size_t start, size_t stop) {
  148. if (stop == 0) {
  149. stop = l.head;
  150. }
  151. // TODO(dledda): maybe assert instead
  152. if (stop > l.head || start > stop) {
  153. return {0};
  154. }
  155. return {
  156. l.data + start,
  157. stop - start,
  158. stop - start,
  159. };
  160. }
  161. string strSlice(string str, size_t start, size_t stop) {
  162. if (stop == 0) {
  163. stop = str.length;
  164. }
  165. // TODO(dledda): maybe assert instead
  166. if (stop > str.length || start > stop) {
  167. return {0};
  168. }
  169. return {
  170. str.str + start,
  171. stop - start,
  172. };
  173. }
  174. string strSlice(char *data, size_t start, size_t stop) {
  175. return {
  176. data + start,
  177. stop - start,
  178. };
  179. }
  180. bool stringContains(string str, char c) {
  181. for (size_t i = 0; i < str.length; i++) {
  182. if (str.str[i] == c) {
  183. return true;
  184. }
  185. }
  186. return false;
  187. }
  188. const char NUMERIC_CHARS[] = "0123456789";
  189. inline bool isNumeric(char c) {
  190. return stringContains(strlit(NUMERIC_CHARS), c);
  191. }
  192. list<string> strSplit(Arena *arena, string splitStr, string inputStr) {
  193. list<string> result = {0};
  194. if (inputStr.length > 0) {
  195. size_t splitCount = 0;
  196. size_t c = 0;
  197. size_t start = 0;
  198. void *beginning = (char *)arena->memory + arena->head;
  199. while (c < inputStr.length - splitStr.length) {
  200. if (strEql(strSlice(inputStr, c, splitStr.length), splitStr)) {
  201. string *splitString = PushStruct(arena, string);
  202. splitString->str = inputStr.str + start;
  203. splitString->length = c - start;
  204. splitCount++;
  205. start = c + 1;
  206. }
  207. c++;
  208. }
  209. string *splitString = PushStruct(arena, string);
  210. splitString->str = inputStr.str + start;
  211. splitString->length = inputStr.length - start;
  212. splitCount++;
  213. result.data = (string *)beginning,
  214. result.head = splitCount,
  215. result.length = splitCount;
  216. }
  217. return result;
  218. }
  219. int8 parsePositiveInt(string str, size_t *lengthPointer) {
  220. size_t numEnd = 0;
  221. char currChar = str.str[numEnd];
  222. while (numEnd < str.length && isNumeric(currChar)) {
  223. currChar = str.str[++numEnd];
  224. *lengthPointer += 1;
  225. }
  226. *lengthPointer -= 1;
  227. if (numEnd > 0) {
  228. uint8 result = 0;
  229. for (size_t i = 0; i < numEnd; i++) {
  230. result *= 10;
  231. result += str.str[i] - '0';
  232. }
  233. return result;
  234. } else {
  235. return -1;
  236. }
  237. }
  238. real32 parsePositiveReal32(string str, size_t *lengthPointer) {
  239. real32 result = NAN;
  240. string wholePartStr = string{0};
  241. string fractionalPartStr = string{0};
  242. bool split = false;
  243. size_t c = 0;
  244. while (c < str.length) {
  245. if (str.str[c] == '.') {
  246. wholePartStr.str = str.str;
  247. wholePartStr.length = c;
  248. fractionalPartStr.str = str.str + c + 1;
  249. fractionalPartStr.length = str.length - c - 1;
  250. split = true;
  251. break;
  252. }
  253. c++;
  254. }
  255. if (split) {
  256. int wholePart = parsePositiveInt(wholePartStr, lengthPointer);
  257. *lengthPointer += 1;
  258. int fractionalPart = parsePositiveInt(fractionalPartStr, lengthPointer);
  259. if (wholePart >= 0 && fractionalPart >= 0) {
  260. real32 fractionalPartMultiplier = 1.0f / powf(10.0f, (real32)fractionalPartStr.length);
  261. result = (real32)wholePart + (real32)fractionalPart * (real32)fractionalPartMultiplier;
  262. }
  263. } else if (c > 0) {
  264. result = (real32)parsePositiveInt(str, lengthPointer);
  265. }
  266. return result;
  267. }
  268. string readEntireFile(Arena *arena, string filename) {
  269. #if OS_WINDOWS
  270. string result = {0};
  271. HANDLE fileHandle = CreateFileA(cstring(arena, filename), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
  272. if (fileHandle != INVALID_HANDLE_VALUE) {
  273. LARGE_INTEGER fileSize;
  274. if (GetFileSizeEx(fileHandle, &fileSize)) {
  275. string readfile = PushString(arena, (size_t)fileSize.QuadPart);
  276. if (readfile.str) {
  277. DWORD bytesRead;
  278. if (ReadFile(fileHandle, readfile.str, (DWORD)fileSize.QuadPart, &bytesRead, NULL) && (fileSize.QuadPart == bytesRead)) {
  279. result = readfile;
  280. }
  281. }
  282. }
  283. CloseHandle(fileHandle);
  284. }
  285. return result;
  286. #elif OS_LINUX
  287. FILE *input = fopen((char *)file.str, "r");
  288. struct stat st;
  289. stat((char *)file.str, &st);
  290. size_t fsize = st.st_size;
  291. string readBuffer = PushString(arena, filesize);
  292. readBuffer.length = filesize;
  293. fread(readBuffer.str, sizeof(byte), filesize, input);
  294. fclose(input);
  295. return readBuffer;
  296. #endif
  297. }
  298. bool writeEntireFile(Arena *arena, string filename, const byte *contents, size_t contentsLength) {
  299. #if OS_WINDOWS
  300. bool result = false;
  301. HANDLE fileHandle = CreateFileA(cstring(arena, filename), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, NULL, NULL);
  302. if (fileHandle != INVALID_HANDLE_VALUE) {
  303. DWORD bytesWritten;
  304. if (WriteFile(fileHandle, contents, (DWORD)contentsLength, &bytesWritten, NULL)) {
  305. // file written successfully
  306. result = bytesWritten == contentsLength;
  307. }
  308. CloseHandle(fileHandle);
  309. }
  310. return result;
  311. #elif OS_LINUX
  312. Assert(false);
  313. #endif
  314. }
  315. bool fileAppend(Arena *arena, string filename, const byte *contents, size_t contentsLength) {
  316. #if OS_WINDOWS
  317. bool result = false;
  318. HANDLE fileHandle = CreateFileA(cstring(arena, filename), FILE_APPEND_DATA | FILE_GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  319. if (fileHandle != INVALID_HANDLE_VALUE) {
  320. DWORD bytesWritten;
  321. DWORD position = SetFilePointer(fileHandle, 0, NULL, FILE_END);
  322. if (WriteFile(fileHandle, contents, (DWORD)contentsLength, &bytesWritten, NULL)) {
  323. // file written successfully
  324. result = bytesWritten == contentsLength;
  325. }
  326. CloseHandle(fileHandle);
  327. }
  328. return result;
  329. #elif OS_LINUX
  330. Assert(false);
  331. #endif
  332. }
  333. list<string> getArgs(Arena *arena, int argc, char **argv) {
  334. list<string> args = PushList(arena, string, (size_t)argc);
  335. for (int i = 1; i < argc; i++) {
  336. appendList(&args, strFromCString(arena, argv[i]));
  337. }
  338. return args;
  339. }
  340. UnixTimestamp getSystemUnixTime() {
  341. time_t now;
  342. time(&now);
  343. return (UnixTimestamp)now;
  344. }
  345. Timestamp timestampFromUnixTime(UnixTimestamp *unixTimestamp) {
  346. tm *timestamp = gmtime((time_t *)&time);
  347. return *timestamp;
  348. }
  349. string formatTimeHms(Arena *arena, UnixTimestamp time) {
  350. local_persist const string format = strlit("HH-MM-SS");
  351. string buf = PushString(arena, format.length);
  352. tm *timestamp = gmtime((time_t *)&time);
  353. strftime(buf.str, buf.length + 1, "%T", timestamp);
  354. return buf;
  355. }
  356. string formatTimeHms(Arena *arena, Timestamp *time) {
  357. local_persist const string format = strlit("HH-MM-SS");
  358. string buf = PushString(arena, format.length);
  359. strftime(buf.str, buf.length + 1, "%T", (tm *)time);
  360. return buf;
  361. }
  362. string formatTimeYmd(Arena *arena, UnixTimestamp time) {
  363. local_persist const string format = strlit("YYYY-mm-dd");
  364. string buf = PushString(arena, format.length);
  365. tm *timestamp = gmtime((time_t *)&time);
  366. strftime(buf.str, buf.length + 1, "%Y-%m-%d", timestamp);
  367. return buf;
  368. }
  369. string formatTimeYmd(Arena *arena, Timestamp *time) {
  370. local_persist const string format = strlit("YYYY-mm-dd");
  371. string buf = PushString(arena, format.length);
  372. strftime(buf.str, buf.length + 1, "%Y-%m-%d", (tm *)time);
  373. return buf;
  374. }
  375. function void __core_log(LogTarget target, const char *fmt, va_list argList) {
  376. Scratch scratch = scratchStart(0, 0);
  377. string result = strPrintfv(scratch.arena, fmt, argList);
  378. #if OS_WINDOWS
  379. DWORD done;
  380. HANDLE stdHandle;
  381. switch (target) {
  382. case LogTarget_stdin:
  383. stdHandle = GetStdHandle(STD_INPUT_HANDLE);
  384. break;
  385. case LogTarget_stdout:
  386. stdHandle = GetStdHandle(STD_ERROR_HANDLE);
  387. break;
  388. case LogTarget_stderr:
  389. stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
  390. break;
  391. default:
  392. stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
  393. break;
  394. }
  395. WriteFile(stdHandle, result.str, (DWORD)result.length, &done, 0);
  396. #elif OS_LINUX
  397. // TODO(dledda): implementation
  398. Assert(false);
  399. #endif
  400. scratchEnd(scratch);
  401. }
  402. void logErr(const char *fmt, ...) {
  403. va_list argList;
  404. va_start(argList, fmt);
  405. __core_log(LogTarget_stdout, fmt, argList);
  406. va_end(argList);
  407. }
  408. function void logStdout(const char *fmt, ...) {
  409. va_list argList;
  410. va_start(argList, fmt);
  411. __core_log(LogTarget_stdout, fmt, argList);
  412. va_end(argList);
  413. }
  414. void log(const char *fmt, ...) {
  415. va_list argList;
  416. va_start(argList, fmt);
  417. __core_log(LogTarget_stdout, fmt, argList);
  418. va_end(argList);
  419. }
  420. void log(list<int> l, LogTarget target) {
  421. void (*logFn)(const char *fmt, ...) = target == LogTarget_stdout ? &logStdout : &logErr;
  422. logFn("{ ");
  423. for (size_t i = 0; i < l.length; i++) {
  424. if (i != 0) {
  425. logFn(", ");
  426. }
  427. logFn("%i", l.data[i]);
  428. }
  429. logFn(" } length: %zu, head: %zu\n", l.length, l.head);
  430. }
  431. void log(list<string> l, LogTarget target) {
  432. void (*logFn)(const char *fmt, ...) = target == LogTarget_stdout ? &logStdout : &logErr;
  433. logFn("{ ");
  434. for (size_t i = 0; i < l.length; i++) {
  435. if (i != 0) {
  436. logFn(", ");
  437. }
  438. logFn("\"%S\"", l.data[i]);
  439. }
  440. logFn(" } length: %zu, head: %zu\n", l.length, l.head);
  441. }
  442. int intCompare(const void *a, const void *b) {
  443. int *x = (int *)a;
  444. int *y = (int *)b;
  445. return (*x > *y) - (*x < *y);
  446. }