utils.h
View source code here on GitHub!
Includes
<direct.h>
(if compiled for Windows)<windows.h>
(if compiled for Windows)<libgen.h>
(if not compiled for Windows)<unistd.h>
(if not compiled for Windows)
-
char *get_data_file(const char *name)
Return a character array containing the whole contents of a file found in _data.
-
char *get_parent_directory(const char *name, const uint32_t levels)
-
struct Answer
The Answer struct represents the solution to a Project Euler problems. It can hold different types of values and an identifier.
-
union value
-
uint8_t int8
Represents an 8-bit signed integer.
-
uint16_t int16
Represents a 16-bit signed integer.
-
uint32_t int32
Represents a 32-bit signed integer.
-
uint64_t int64
Represents a 64-bit signed integer.
-
uint8_t uint8
Represents an 8-bit unsigned integer.
-
uint16_t uint16
Represents a 16-bit unsigned integer.
-
uint32_t uint32
Represents a 32-bit unsigned integer.
-
uint64_t uint64
Represents a 64-bit unsigned integer.
-
char *string
Represents a string value.
-
uint8_t int8
-
uint16_t id
Represents the problem ID.
-
AnswerType type
Specifies the type of value stored in the
value
union. It is of typeAnswerType
.
-
union value
-
enum AnswerType
The AnswerType enum defines various types of values that can be stored in an Answer.
-
enumerator ERRORT
Represents an error state.
-
enumerator INT8T
Represents an 8-bit signed integer.
-
enumerator INT16T
Represents a 16-bit signed integer.
-
enumerator INT32T
Represents a 32-bit signed integer.
-
enumerator INT64T
Represents a 64-bit signed integer.
-
enumerator UINT8T
Represents an 8-bit unsigned integer.
-
enumerator UINT16T
Represents a 16-bit unsigned integer.
-
enumerator UINT32T
Represents a 32-bit unsigned integer.
-
enumerator UINT64T
Represents a 64-bit unsigned integer.
-
enumerator STRINGT
Represents a string value.
-
enumerator ERRORT
1#pragma once
2#include <inttypes.h>
3#include <stdint.h>
4#include <stdlib.h>
5#include <string.h>
6#include <stdio.h>
7#include "macros.h"
8#ifdef _WIN32
9#include <direct.h>
10#include <windows.h>
11#define PATH_SEPARATOR "\\"
12#else
13#include <libgen.h>
14#include <unistd.h>
15#define PATH_SEPARATOR "/"
16#endif
17
18char* get_parent_directory(char* path, const uint32_t levels) {
19#ifdef _WIN32
20 static char drive[_MAX_DRIVE];
21 static char dir[_MAX_DIR];
22 _splitpath(path, drive, dir, NULL, NULL);
23 for (uint32_t i = 0; i < levels; ++i) {
24 size_t len = strlen(dir);
25 if (len > 1 && (dir[len - 1] == '\\' || dir[len - 1] == '/'))
26 dir[len - 1] = '\0';
27 char* last_slash = strrchr(dir, '\\');
28 if (!last_slash)
29 last_slash = strrchr(dir, '/');
30 if (last_slash)
31 *last_slash = '\0';
32 }
33 static char parent_dir[_MAX_PATH];
34 snprintf(parent_dir, sizeof(parent_dir), "%s%s", drive, dir);
35 return parent_dir;
36#else
37 char* dir = dirname(path);
38 for (uint32_t i = 0; i < levels; ++i) {
39 dir = dirname(dir);
40 if (strcmp(dir, "/") == 0 || strcmp(dir, ".") == 0)
41 break;
42 }
43 return dir;
44#endif
45}
46
47char *get_data_file(const char *name) {
48 const char* start = __FILE__;
49#ifdef _WIN32
50 char absolute_path[MAX_PATH];
51 if (!_fullpath(absolute_path, start, MAX_PATH)) {
52 perror("_fullpath");
53 return NULL;
54 }
55#else
56 char* absolute_path = realpath(start, NULL);
57 if (!absolute_path) {
58 perror("realpath");
59 return NULL;
60 }
61#endif
62 char* parents_dir = get_parent_directory(absolute_path, 3);
63 const size_t p_len = strlen(parents_dir),
64 name_len = strlen(name);
65 char *file_path = (char *)malloc(p_len + name_len + 8);
66 memcpy(file_path, parents_dir, p_len);
67 memcpy(file_path + p_len, PATH_SEPARATOR "_data" PATH_SEPARATOR, 7);
68 memcpy(file_path + p_len + 7, name, name_len);
69 file_path[p_len + name_len + 7] = 0;
70 FILE* file = fopen(file_path, "r");
71#ifndef _WIN32
72 free(absolute_path);
73#endif
74// free(parents_dir);
75 free(file_path);
76 if (!file) {
77 perror("fopen");
78 return NULL;
79 }
80
81 fseek(file, 0, SEEK_END);
82 size_t length = ftell(file);
83 fseek(file, 0, SEEK_SET);
84
85 char* buffer = (char*)malloc(length + 1);
86 if (!buffer) {
87 perror("malloc");
88 fclose(file);
89 return NULL;
90 }
91
92 const size_t ret_code = fread(buffer, 1, length, file);
93 if (ret_code != length) {
94 if (feof(file))
95 printf("Error reading %s: unexpected end of file, read %" PRIu64 " of %"PRIu64" bytes expected\n", name, (uint64_t)ret_code, (uint64_t)length);
96 else if (ferror(file))
97 perror("Error reading data file");
98 }
99
100 buffer[length] = 0;
101 fclose(file);
102
103 return buffer;
104}
105
106typedef enum {
107 ERRORT,
108 INT8T,
109 INT16T,
110 INT32T,
111 INT64T,
112 UINT8T,
113 UINT16T,
114 UINT32T,
115 UINT64T,
116 STRINGT,
117} AnswerType;
118
119typedef struct {
120 union {
121 uint8_t uint8;
122 uint16_t uint16;
123 uint32_t uint32;
124 uint64_t uint64;
125 int8_t int8;
126 int16_t int16;
127 int32_t int32;
128 int64_t int64;
129 char *string;
130 } value;
131 uint16_t id;
132 AnswerType type;
133} Answer;
134
135#ifdef _WIN32
136#define strtok_r strtok_s
137#endif
138
139Answer EMSCRIPTEN_KEEPALIVE get_answer(uint16_t id) {
140 Answer ret = {
141 .id = id,
142 };
143 char *answers = get_data_file("answers.tsv");
144 char *linepointer, *tabpointer;
145
146 if (!answers) {
147 fprintf(stderr, "Error: Unable to get data from file\n");
148 return ret;
149 }
150
151 char s_id[6];
152 snprintf(s_id, sizeof(s_id), "%" PRIu16, id);
153
154 char *line = strtok_r(answers, "\n", &linepointer); // skip header
155 while ((line = strtok_r(NULL, "\n", &linepointer)) != NULL) {
156 char *token = strtok_r(line, "\t", &tabpointer);
157 if (strcmp(token, s_id) != 0)
158 continue;
159
160 token = strtok_r(NULL, "\t", &tabpointer);
161 if (!token)
162 continue;
163
164 if (strcmp(token, "uint") == 0)
165 ret.type = UINT8T; // will adjust size later
166 else if (strcmp(token, "int") == 0)
167 ret.type = INT8T; // will adjust size later
168 else if (strcmp(token, "str") == 0)
169 ret.type = STRINGT;
170 else {
171 fprintf(stderr, "Error: Unknown type '%s'\n", token);
172 return ret;
173 }
174
175 token = strtok_r(NULL, "\t", &tabpointer);
176 if (!token)
177 continue;
178 size_t size = strtoull(token, NULL, 10);
179
180 token = strtok_r(NULL, "\t", &tabpointer);
181 if (!token)
182 continue;
183
184 switch (ret.type) {
185 case UINT8T:
186 case UINT16T:
187 case UINT32T:
188 case UINT64T:
189 switch (size) {
190 case 8:
191 ret.value.uint8 = (uint8_t)strtoul(token, NULL, 10);
192 break;
193 case 16:
194 ret.value.uint16 = (uint16_t)strtoul(token, NULL, 10);
195 ret.type = UINT16T;
196 break;
197 case 32:
198 ret.value.uint32 = strtoul(token, NULL, 10);
199 ret.type = UINT32T;
200 break;
201 case 64:
202 ret.value.uint64 = strtoull(token, NULL, 10);
203 ret.type = UINT64T;
204 break;
205 default:
206 fprintf(stderr, "Error: Unsupported uint size %" PRIu64 "\n", (uint64_t)size);
207 Answer err = {0};
208 return err;
209 }
210 break;
211 case INT8T:
212 case INT16T:
213 case INT32T:
214 case INT64T:
215 switch (size) {
216 case 8:
217 ret.value.int8 = (int8_t)strtol(token, NULL, 10);
218 break;
219 case 16:
220 ret.value.int16 = (int16_t)strtol(token, NULL, 10);
221 ret.type = INT16T;
222 break;
223 case 32:
224 ret.value.int32 = strtol(token, NULL, 10);
225 ret.type = INT32T;
226 break;
227 case 64:
228 ret.value.int64 = strtoll(token, NULL, 10);
229 ret.type = INT64T;
230 break;
231 default:
232 fprintf(stderr, "Error: Unsupported int size %" PRIu64 "\n", (uint64_t)size);
233 Answer err = {0};
234 return err;
235 }
236 break;
237 case STRINGT:
238 ret.value.string = (char *)malloc(size + 1);
239 if (ret.value.string) {
240 strncpy(ret.value.string, token, size);
241 ret.value.string[size] = 0;
242 } else {
243 fprintf(stderr, "Error: Memory allocation failed for string\n");
244 Answer err = {0};
245 return err;
246 }
247 break;
248 case ERRORT:
249 fprintf(stderr, "Error: Unknown type (should be unreachable)\n");
250 Answer err = {0};
251 return err;
252 }
253 break;
254 }
255
256 free(answers);
257 return ret;
258}
259
260#ifdef _WIN32
261#undef strtok_r
262#endif