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.
 
 
 
 

512 rindas
14 KiB

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