Standard setup for writing C inspired by Casey Muratori, Ryan Fleury, Mr. 4th Programmer, and others in the handmade community.
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 

518 rindas
14 KiB

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