authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-09 18:07:11-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-09 18:38:41-04:00
log173fc842c4eb1da6ca07a4ab6026c4c62bc8c09b
treea1bd31418545430c0c60fdebf00238d0034ae05d
parentb4d5d4d1748d25efa6197914a36e276e93509f57
signaturelock-open Commit is signed but in an unrecognized format.

basic compiler id hash working


12 files changed, 918 insertions(+), 224 deletions(-)

CMakeLists.txt+1
......@@ -409,6 +409,7 @@ set(ZIG_SOURCES
409409 "${CMAKE_SOURCE_DIR}/src/bigint.cpp"
410410 "${CMAKE_SOURCE_DIR}/src/blake2b.cpp"
411411 "${CMAKE_SOURCE_DIR}/src/buffer.cpp"
412 "${CMAKE_SOURCE_DIR}/src/cache_hash.cpp"
412413 "${CMAKE_SOURCE_DIR}/src/c_tokenizer.cpp"
413414 "${CMAKE_SOURCE_DIR}/src/codegen.cpp"
414415 "${CMAKE_SOURCE_DIR}/src/errmsg.cpp"
src/buffer.hpp+4
......@@ -78,6 +78,10 @@ static inline Buf *buf_create_from_mem(const char *ptr, size_t len) {
7878 return buf;
7979}
8080
81static inline Buf *buf_create_from_slice(Slice<uint8_t> slice) {
82 return buf_create_from_mem((const char *)slice.ptr, slice.len);
83}
84
8185static inline Buf *buf_create_from_str(const char *str) {
8286 return buf_create_from_mem(str, strlen(str));
8387}
src/cache_hash.cpp created+407
......@@ -0,0 +1,407 @@
1/*
2 * Copyright (c) 2018 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#include "cache_hash.hpp"
9#include "buffer.hpp"
10#include "os.hpp"
11
12#include <stdio.h>
13
14void cache_init(CacheHash *ch, Buf *manifest_dir) {
15 int rc = blake2b_init(&ch->blake, 48);
16 assert(rc == 0);
17 ch->files = {};
18 ch->manifest_dir = manifest_dir;
19 ch->manifest_file_path = nullptr;
20 ch->manifest_dirty = false;
21}
22
23void cache_str(CacheHash *ch, const char *ptr) {
24 assert(ch->manifest_file_path == nullptr);
25 assert(ptr != nullptr);
26 // + 1 to include the null byte
27 blake2b_update(&ch->blake, ptr, strlen(ptr) + 1);
28}
29
30void cache_int(CacheHash *ch, int x) {
31 assert(ch->manifest_file_path == nullptr);
32 // + 1 to include the null byte
33 uint8_t buf[sizeof(int) + 1];
34 memcpy(buf, &x, sizeof(int));
35 buf[sizeof(int)] = 0;
36 blake2b_update(&ch->blake, buf, sizeof(int) + 1);
37}
38
39void cache_buf(CacheHash *ch, Buf *buf) {
40 assert(ch->manifest_file_path == nullptr);
41 assert(buf != nullptr);
42 // + 1 to include the null byte
43 blake2b_update(&ch->blake, buf_ptr(buf), buf_len(buf) + 1);
44}
45
46void cache_buf_opt(CacheHash *ch, Buf *buf) {
47 assert(ch->manifest_file_path == nullptr);
48 if (buf == nullptr) {
49 cache_str(ch, "");
50 cache_str(ch, "");
51 } else {
52 cache_buf(ch, buf);
53 }
54}
55
56void cache_list_of_link_lib(CacheHash *ch, LinkLib **ptr, size_t len) {
57 assert(ch->manifest_file_path == nullptr);
58 for (size_t i = 0; i < len; i += 1) {
59 LinkLib *lib = ptr[i];
60 if (lib->provided_explicitly) {
61 cache_buf(ch, lib->name);
62 }
63 }
64 cache_str(ch, "");
65}
66
67void cache_list_of_buf(CacheHash *ch, Buf **ptr, size_t len) {
68 assert(ch->manifest_file_path == nullptr);
69 for (size_t i = 0; i < len; i += 1) {
70 Buf *buf = ptr[i];
71 cache_buf(ch, buf);
72 }
73 cache_str(ch, "");
74}
75
76void cache_file(CacheHash *ch, Buf *file_path) {
77 assert(ch->manifest_file_path == nullptr);
78 assert(file_path != nullptr);
79 Buf *resolved_path = buf_alloc();
80 *resolved_path = os_path_resolve(&file_path, 1);
81 CacheHashFile *chf = ch->files.add_one();
82 chf->path = resolved_path;
83 cache_buf(ch, resolved_path);
84}
85
86void cache_file_opt(CacheHash *ch, Buf *file_path) {
87 assert(ch->manifest_file_path == nullptr);
88 if (file_path == nullptr) {
89 cache_str(ch, "");
90 cache_str(ch, "");
91 } else {
92 cache_file(ch, file_path);
93 }
94}
95
96// Ported from std/base64.zig
97static uint8_t base64_fs_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
98static void base64_encode(Slice<uint8_t> dest, Slice<uint8_t> source) {
99 size_t dest_len = ((source.len + 2) / 3) * 4;
100 assert(dest.len == dest_len);
101
102 size_t i = 0;
103 size_t out_index = 0;
104 for (; i + 2 < source.len; i += 3) {
105 dest.ptr[out_index] = base64_fs_alphabet[(source.ptr[i] >> 2) & 0x3f];
106 out_index += 1;
107
108 dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i] & 0x3) << 4) | ((source.ptr[i + 1] & 0xf0) >> 4)];
109 out_index += 1;
110
111 dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i + 1] & 0xf) << 2) | ((source.ptr[i + 2] & 0xc0) >> 6)];
112 out_index += 1;
113
114 dest.ptr[out_index] = base64_fs_alphabet[source.ptr[i + 2] & 0x3f];
115 out_index += 1;
116 }
117
118 // Assert that we never need pad characters.
119 assert(i == source.len);
120}
121
122// Ported from std/base64.zig
123static Error base64_decode(Slice<uint8_t> dest, Slice<uint8_t> source) {
124 assert(source.len % 4 == 0);
125 assert(dest.len == (source.len / 4) * 3);
126
127 // In Zig this is comptime computed. In C++ it's not worth it to do that.
128 uint8_t char_to_index[256];
129 bool char_in_alphabet[256] = {0};
130 for (size_t i = 0; i < 64; i += 1) {
131 uint8_t c = base64_fs_alphabet[i];
132 assert(!char_in_alphabet[c]);
133 char_in_alphabet[c] = true;
134 char_to_index[c] = i;
135 }
136
137 size_t src_cursor = 0;
138 size_t dest_cursor = 0;
139
140 for (;src_cursor < source.len; src_cursor += 4) {
141 if (!char_in_alphabet[source.ptr[src_cursor + 0]]) return ErrorInvalidFormat;
142 if (!char_in_alphabet[source.ptr[src_cursor + 1]]) return ErrorInvalidFormat;
143 if (!char_in_alphabet[source.ptr[src_cursor + 2]]) return ErrorInvalidFormat;
144 if (!char_in_alphabet[source.ptr[src_cursor + 3]]) return ErrorInvalidFormat;
145 dest.ptr[dest_cursor + 0] = (char_to_index[source.ptr[src_cursor + 0]] << 2) | (char_to_index[source.ptr[src_cursor + 1]] >> 4);
146 dest.ptr[dest_cursor + 1] = (char_to_index[source.ptr[src_cursor + 1]] << 4) | (char_to_index[source.ptr[src_cursor + 2]] >> 2);
147 dest.ptr[dest_cursor + 2] = (char_to_index[source.ptr[src_cursor + 2]] << 6) | (char_to_index[source.ptr[src_cursor + 3]]);
148 dest_cursor += 3;
149 }
150
151 assert(src_cursor == source.len);
152 assert(dest_cursor == dest.len);
153 return ErrorNone;
154}
155
156static Error hash_file(uint8_t *digest, OsFile handle) {
157 Error err;
158
159 blake2b_state blake;
160 int rc = blake2b_init(&blake, 48);
161 assert(rc == 0);
162
163 for (;;) {
164 uint8_t buf[4096];
165 size_t amt = 4096;
166 if ((err = os_file_read(handle, buf, &amt)))
167 return err;
168 if (amt == 0) {
169 rc = blake2b_final(&blake, digest, 48);
170 assert(rc == 0);
171 return ErrorNone;
172 }
173 blake2b_update(&blake, buf, amt);
174 }
175}
176
177static Error populate_file_hash(CacheHash *ch, CacheHashFile *chf) {
178 Error err;
179
180 assert(chf->path != nullptr);
181
182 OsFile this_file;
183 if ((err = os_file_open_r(chf->path, &this_file)))
184 return err;
185
186 if ((err = os_file_mtime(this_file, &chf->mtime))) {
187 os_file_close(this_file);
188 return err;
189 }
190
191 if ((err = hash_file(chf->bin_digest, this_file))) {
192 os_file_close(this_file);
193 return err;
194 }
195 os_file_close(this_file);
196
197 blake2b_update(&ch->blake, chf->bin_digest, 48);
198
199 return ErrorNone;
200}
201
202Error cache_hit(CacheHash *ch, Buf *out_digest) {
203 Error err;
204
205 uint8_t bin_digest[48];
206 int rc = blake2b_final(&ch->blake, bin_digest, 48);
207 assert(rc == 0);
208
209 Buf b64_digest = BUF_INIT;
210 buf_resize(&b64_digest, 64);
211 base64_encode(buf_to_slice(&b64_digest), {bin_digest, 48});
212
213 rc = blake2b_init(&ch->blake, 48);
214 assert(rc == 0);
215 blake2b_update(&ch->blake, bin_digest, 48);
216
217 ch->manifest_file_path = buf_alloc();
218 os_path_join(ch->manifest_dir, &b64_digest, ch->manifest_file_path);
219
220 buf_append_str(ch->manifest_file_path, ".txt");
221
222 if ((err = os_file_open_lock_rw(ch->manifest_file_path, &ch->manifest_file)))
223 return err;
224
225 Buf line_buf = BUF_INIT;
226 buf_resize(&line_buf, 512);
227 if ((err = os_file_read_all(ch->manifest_file, &line_buf))) {
228 os_file_close(ch->manifest_file);
229 return err;
230 }
231
232 size_t input_file_count = ch->files.length;
233 bool any_file_changed = false;
234 size_t file_i = 0;
235 SplitIterator line_it = memSplit(buf_to_slice(&line_buf), str("\n"));
236 for (;; file_i += 1) {
237 Optional<Slice<uint8_t>> opt_line = SplitIterator_next(&line_it);
238 if (!opt_line.is_some)
239 break;
240
241 CacheHashFile *chf;
242 if (file_i < input_file_count) {
243 chf = &ch->files.at(file_i);
244 } else if (any_file_changed) {
245 // cache miss.
246 // keep the the manifest file open with the rw lock
247 // reset the hash
248 rc = blake2b_init(&ch->blake, 48);
249 assert(rc == 0);
250 blake2b_update(&ch->blake, bin_digest, 48);
251 ch->files.resize(input_file_count);
252 // bring the hash up to the input file hashes
253 for (file_i = 0; file_i < input_file_count; file_i += 1) {
254 blake2b_update(&ch->blake, ch->files.at(file_i).bin_digest, 48);
255 }
256 // caller can notice that out_digest is unmodified.
257 return ErrorNone;
258 } else {
259 chf = ch->files.add_one();
260 chf->path = nullptr;
261 }
262
263 SplitIterator it = memSplit(opt_line.value, str(" "));
264
265 Optional<Slice<uint8_t>> opt_mtime_sec = SplitIterator_next(&it);
266 if (!opt_mtime_sec.is_some) {
267 os_file_close(ch->manifest_file);
268 return ErrorInvalidFormat;
269 }
270 chf->mtime.sec = strtoull((const char *)opt_mtime_sec.value.ptr, nullptr, 10);
271
272 Optional<Slice<uint8_t>> opt_mtime_nsec = SplitIterator_next(&it);
273 if (!opt_mtime_nsec.is_some) {
274 os_file_close(ch->manifest_file);
275 return ErrorInvalidFormat;
276 }
277 chf->mtime.nsec = strtoull((const char *)opt_mtime_nsec.value.ptr, nullptr, 10);
278
279 Optional<Slice<uint8_t>> opt_digest = SplitIterator_next(&it);
280 if (!opt_digest.is_some) {
281 os_file_close(ch->manifest_file);
282 return ErrorInvalidFormat;
283 }
284 if ((err = base64_decode({chf->bin_digest, 48}, opt_digest.value))) {
285 os_file_close(ch->manifest_file);
286 return ErrorInvalidFormat;
287 }
288
289 Optional<Slice<uint8_t>> opt_file_path = SplitIterator_next(&it);
290 if (!opt_file_path.is_some) {
291 os_file_close(ch->manifest_file);
292 return ErrorInvalidFormat;
293 }
294 Buf *this_path = buf_create_from_slice(opt_file_path.value);
295 if (chf->path != nullptr && !buf_eql_buf(this_path, chf->path)) {
296 os_file_close(ch->manifest_file);
297 return ErrorInvalidFormat;
298 }
299 chf->path = this_path;
300
301 // if the mtime matches we can trust the digest
302 OsFile this_file;
303 if ((err = os_file_open_r(chf->path, &this_file))) {
304 os_file_close(ch->manifest_file);
305 return err;
306 }
307 OsTimeStamp actual_mtime;
308 if ((err = os_file_mtime(this_file, &actual_mtime))) {
309 os_file_close(this_file);
310 os_file_close(ch->manifest_file);
311 return err;
312 }
313 if (chf->mtime.sec == actual_mtime.sec && chf->mtime.nsec == actual_mtime.nsec) {
314 os_file_close(this_file);
315 } else {
316 // we have to recompute the digest.
317 // later we'll rewrite the manifest with the new mtime/digest values
318 ch->manifest_dirty = true;
319 chf->mtime = actual_mtime;
320
321 uint8_t actual_digest[48];
322 if ((err = hash_file(actual_digest, this_file))) {
323 os_file_close(this_file);
324 os_file_close(ch->manifest_file);
325 return err;
326 }
327 os_file_close(this_file);
328 if (memcmp(chf->bin_digest, actual_digest, 48) != 0) {
329 memcpy(chf->bin_digest, actual_digest, 48);
330 // keep going until we have the input file digests
331 any_file_changed = true;
332 }
333 }
334 if (!any_file_changed) {
335 blake2b_update(&ch->blake, chf->bin_digest, 48);
336 }
337 }
338 if (file_i < input_file_count) {
339 // manifest file is empty or missing entries, so this is a cache miss
340 ch->manifest_dirty = true;
341 for (; file_i < input_file_count; file_i += 1) {
342 CacheHashFile *chf = &ch->files.at(file_i);
343 if ((err = populate_file_hash(ch, chf))) {
344 os_file_close(ch->manifest_file);
345 return err;
346 }
347 }
348 return ErrorNone;
349 }
350 // Cache Hit
351 return cache_final(ch, out_digest);
352}
353
354Error cache_add_file(CacheHash *ch, Buf *path) {
355 Error err;
356
357 assert(ch->manifest_file_path != nullptr);
358 CacheHashFile *chf = ch->files.add_one();
359 chf->path = path;
360 if ((err = populate_file_hash(ch, chf))) {
361 os_file_close(ch->manifest_file);
362 return err;
363 }
364
365 return ErrorNone;
366}
367
368static Error write_manifest_file(CacheHash *ch) {
369 Error err;
370 Buf contents = BUF_INIT;
371 buf_resize(&contents, 0);
372 uint8_t encoded_digest[65];
373 encoded_digest[64] = 0;
374 for (size_t i = 0; i < ch->files.length; i += 1) {
375 CacheHashFile *chf = &ch->files.at(i);
376 base64_encode({encoded_digest, 64}, {chf->bin_digest, 48});
377 buf_appendf(&contents, "%" ZIG_PRI_u64 " %" ZIG_PRI_u64 " %s %s\n",
378 chf->mtime.sec, chf->mtime.nsec, encoded_digest, buf_ptr(chf->path));
379 }
380 fprintf(stderr, "overwrite with\n%s\n", buf_ptr(&contents));
381 if ((err = os_file_overwrite(ch->manifest_file, &contents)))
382 return err;
383
384 return ErrorNone;
385}
386
387Error cache_final(CacheHash *ch, Buf *out_digest) {
388 Error err;
389
390 assert(ch->manifest_file_path != nullptr);
391
392 if (ch->manifest_dirty) {
393 if ((err = write_manifest_file(ch))) {
394 fprintf(stderr, "Warning: Unable to write cache file '%s': %s\n",
395 buf_ptr(ch->manifest_file_path), err_str(err));
396 }
397 }
398 os_file_close(ch->manifest_file);
399
400 uint8_t bin_digest[48];
401 int rc = blake2b_final(&ch->blake, bin_digest, 48);
402 assert(rc == 0);
403 buf_resize(out_digest, 64);
404 base64_encode(buf_to_slice(out_digest), {bin_digest, 48});
405
406 return ErrorNone;
407}
src/cache_hash.hpp created+56
......@@ -0,0 +1,56 @@
1/*
2 * Copyright (c) 2018 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#ifndef ZIG_CACHE_HASH_HPP
9#define ZIG_CACHE_HASH_HPP
10
11#include "all_types.hpp"
12#include "blake2.h"
13#include "os.hpp"
14
15struct CacheHashFile {
16 Buf *path;
17 OsTimeStamp mtime;
18 uint8_t bin_digest[48];
19};
20
21struct CacheHash {
22 blake2b_state blake;
23 ZigList<CacheHashFile> files;
24 Buf *manifest_dir;
25 Buf *manifest_file_path;
26 OsFile manifest_file;
27 bool manifest_dirty;
28};
29
30// Always call this first to set up.
31void cache_init(CacheHash *ch, Buf *manifest_dir);
32
33// Next, use the hash population functions to add the initial parameters.
34void cache_str(CacheHash *ch, const char *ptr);
35void cache_int(CacheHash *ch, int x);
36void cache_buf(CacheHash *ch, Buf *buf);
37void cache_buf_opt(CacheHash *ch, Buf *buf);
38void cache_list_of_link_lib(CacheHash *ch, LinkLib **ptr, size_t len);
39void cache_list_of_buf(CacheHash *ch, Buf **ptr, size_t len);
40void cache_file(CacheHash *ch, Buf *path);
41void cache_file_opt(CacheHash *ch, Buf *path);
42
43// Then call cache_hit when you're ready to see if you can skip the next step.
44// out_b64_digest will be left unchanged if it was a cache miss
45Error ATTRIBUTE_MUST_USE cache_hit(CacheHash *ch, Buf *out_b64_digest);
46
47// If you got a cache hit, the flow is done. No more functions to call.
48// Next call this function for every file that is depended on.
49Error ATTRIBUTE_MUST_USE cache_add_file(CacheHash *ch, Buf *path);
50
51// If you did not get a cache hit, use the hash population functions again
52// and do all the actual work. When done use cache_final to save the cache
53// for next time.
54Error ATTRIBUTE_MUST_USE cache_final(CacheHash *ch, Buf *out_digest);
55
56#endif
src/codegen.cpp+33-119
......@@ -19,7 +19,6 @@
1919#include "target.hpp"
2020#include "util.hpp"
2121#include "zig_llvm.h"
22#include "blake2.h"
2322
2423#include <stdio.h>
2524#include <errno.h>
......@@ -7675,130 +7674,45 @@ void codegen_add_time_event(CodeGen *g, const char *name) {
76757674 g->timing_events.append({os_get_time(), name});
76767675}
76777676
7678static void add_cache_str(blake2b_state *blake, const char *ptr) {
7679 assert(ptr != nullptr);
7680 // + 1 to include the null byte
7681 blake2b_update(blake, ptr, strlen(ptr) + 1);
7682}
7683
7684static void add_cache_int(blake2b_state *blake, int x) {
7685 // + 1 to include the null byte
7686 uint8_t buf[sizeof(int) + 1];
7687 memcpy(buf, &x, sizeof(int));
7688 buf[sizeof(int)] = 0;
7689 blake2b_update(blake, buf, sizeof(int) + 1);
7690}
76917677
7692static void add_cache_buf(blake2b_state *blake, Buf *buf) {
7693 assert(buf != nullptr);
7694 // + 1 to include the null byte
7695 blake2b_update(blake, buf_ptr(buf), buf_len(buf) + 1);
7696}
7697
7698static void add_cache_buf_opt(blake2b_state *blake, Buf *buf) {
7699 if (buf == nullptr) {
7700 add_cache_str(blake, "");
7701 add_cache_str(blake, "");
7702 } else {
7703 add_cache_buf(blake, buf);
7704 }
7705}
7706
7707static void add_cache_list_of_link_lib(blake2b_state *blake, LinkLib **ptr, size_t len) {
7708 for (size_t i = 0; i < len; i += 1) {
7709 LinkLib *lib = ptr[i];
7710 if (lib->provided_explicitly) {
7711 add_cache_buf(blake, lib->name);
7712 }
7713 }
7714 add_cache_str(blake, "");
7715}
7716
7717static void add_cache_list_of_buf(blake2b_state *blake, Buf **ptr, size_t len) {
7718 for (size_t i = 0; i < len; i += 1) {
7719 Buf *buf = ptr[i];
7720 add_cache_buf(blake, buf);
7721 }
7722 add_cache_str(blake, "");
7723}
7724
7725//static void add_cache_file(CodeGen *g, blake2b_state *blake, Buf *resolved_path) {
7726// assert(file_name != nullptr);
7727// g->cache_files.append(resolved_path);
7728//}
7678//// Called before init()
7679//static bool build_with_cache(CodeGen *g) {
7680// // TODO zig exe & dynamic libraries
7681// // should be in main.cpp I think. only needs to happen
7682// // once on startup.
7683//
7684// CacheHash comp;
7685// cache_init(&comp);
7686//
7687// add_cache_buf(&blake, g->root_out_name);
7688// add_cache_buf_opt(&blake, get_resolved_root_src_path(g)); // Root source file
7689// add_cache_list_of_link_lib(&blake, g->link_libs_list.items, g->link_libs_list.length);
7690// add_cache_list_of_buf(&blake, g->darwin_frameworks.items, g->darwin_frameworks.length);
7691// add_cache_list_of_buf(&blake, g->rpath_list.items, g->rpath_list.length);
7692// add_cache_int(&blake, g->emit_file_type);
7693// add_cache_int(&blake, g->build_mode);
7694// add_cache_int(&blake, g->out_type);
7695// // TODO the rest of the struct CodeGen fields
7696//
7697// uint8_t bin_digest[48];
7698// rc = blake2b_final(&blake, bin_digest, 48);
7699// assert(rc == 0);
7700//
7701// Buf b64_digest = BUF_INIT;
7702// buf_resize(&b64_digest, 64);
7703// base64_encode(buf_to_slice(&b64_digest), {bin_digest, 48});
77297704//
7730//static void add_cache_file_opt(CodeGen *g, blake2b_state *blake, Buf *resolved_path) {
7731// if (resolved_path == nullptr) {
7732// add_cache_str(blake, "");
7733// add_cache_str(blake, "");
7734// } else {
7735// add_cache_file(g, blake, resolved_path);
7736// }
7705// fprintf(stderr, "input params hash: %s\n", buf_ptr(&b64_digest));
7706// // TODO next look for a manifest file that has all the files from the input parameters
7707// // use that to construct the real hash, which looks up the output directory and another manifest file
7708//
7709// return false;
77377710//}
77387711
7739// Ported from std/base64.zig
7740static uint8_t base64_fs_alphabet[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
7741static void base64_encode(Slice<uint8_t> dest, Slice<uint8_t> source) {
7742 size_t dest_len = ((source.len + 2) / 3) * 4;
7743 assert(dest.len == dest_len);
7744
7745 size_t i = 0;
7746 size_t out_index = 0;
7747 for (; i + 2 < source.len; i += 3) {
7748 dest.ptr[out_index] = base64_fs_alphabet[(source.ptr[i] >> 2) & 0x3f];
7749 out_index += 1;
7750
7751 dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i] & 0x3) << 4) | ((source.ptr[i + 1] & 0xf0) >> 4)];
7752 out_index += 1;
7753
7754 dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i + 1] & 0xf) << 2) | ((source.ptr[i + 2] & 0xc0) >> 6)];
7755 out_index += 1;
7756
7757 dest.ptr[out_index] = base64_fs_alphabet[source.ptr[i + 2] & 0x3f];
7758 out_index += 1;
7759 }
7760
7761 // Assert that we never need pad characters.
7762 assert(i == source.len);
7763}
7764
7765// Called before init()
7766static bool build_with_cache(CodeGen *g) {
7767 blake2b_state blake;
7768 int rc = blake2b_init(&blake, 48);
7769 assert(rc == 0);
7770
7771 // TODO zig exe & dynamic libraries
7772
7773 add_cache_buf(&blake, g->root_out_name);
7774 add_cache_buf_opt(&blake, get_resolved_root_src_path(g)); // Root source file
7775 add_cache_list_of_link_lib(&blake, g->link_libs_list.items, g->link_libs_list.length);
7776 add_cache_list_of_buf(&blake, g->darwin_frameworks.items, g->darwin_frameworks.length);
7777 add_cache_list_of_buf(&blake, g->rpath_list.items, g->rpath_list.length);
7778 add_cache_int(&blake, g->emit_file_type);
7779 add_cache_int(&blake, g->build_mode);
7780 add_cache_int(&blake, g->out_type);
7781 // TODO the rest of the struct CodeGen fields
7782
7783 uint8_t bin_digest[48];
7784 rc = blake2b_final(&blake, bin_digest, 48);
7785 assert(rc == 0);
7786
7787 Buf b64_digest = BUF_INIT;
7788 buf_resize(&b64_digest, 64);
7789 base64_encode(buf_to_slice(&b64_digest), {bin_digest, 48});
7790
7791 fprintf(stderr, "input params hash: %s\n", buf_ptr(&b64_digest));
7792 // TODO next look for a manifest file that has all the files from the input parameters
7793 // use that to construct the real hash, which looks up the output directory and another manifest file
7794
7795 return false;
7796}
7797
77987712void codegen_build(CodeGen *g) {
77997713 assert(g->out_type != OutTypeUnknown);
7800 if (build_with_cache(g))
7801 return;
7714 //if (build_with_cache(g))
7715 // return;
78027716 init(g);
78037717
78047718 codegen_add_time_event(g, "Semantic Analysis");
src/error.cpp+2
......@@ -27,6 +27,8 @@ const char *err_str(int err) {
2727 case ErrorNegativeDenominator: return "negative denominator";
2828 case ErrorShiftedOutOneBits: return "exact shift shifted out one bits";
2929 case ErrorCCompileErrors: return "C compile errors";
30 case ErrorEndOfFile: return "end of file";
31 case ErrorIsDir: return "is directory";
3032 }
3133 return "(invalid error)";
3234}
src/error.hpp+2
......@@ -27,6 +27,8 @@ enum Error {
2727 ErrorNegativeDenominator,
2828 ErrorShiftedOutOneBits,
2929 ErrorCCompileErrors,
30 ErrorEndOfFile,
31 ErrorIsDir,
3032};
3133
3234const char *err_str(int err);
src/main.cpp+61
......@@ -13,6 +13,7 @@
1313#include "link.hpp"
1414#include "os.hpp"
1515#include "target.hpp"
16#include "cache_hash.hpp"
1617
1718#include <stdio.h>
1819
......@@ -270,6 +271,66 @@ int main(int argc, char **argv) {
270271 return 0;
271272 }
272273
274 if (argc == 2 && strcmp(argv[1], "TEST") == 0) {
275 Error err;
276 Buf app_data_dir = BUF_INIT;
277 if ((err = os_get_app_data_dir(&app_data_dir, "zig"))) {
278 fprintf(stderr, "get app dir: %s\n", err_str(err));
279 return 1;
280 }
281 Buf *stage1_dir = buf_alloc();
282 os_path_join(&app_data_dir, buf_create_from_str("stage1"), stage1_dir);
283 Buf *manifest_dir = buf_alloc();
284 os_path_join(stage1_dir, buf_create_from_str("exe"), manifest_dir);
285
286 if ((err = os_make_path(manifest_dir))) {
287 fprintf(stderr, "make path: %s\n", err_str(err));
288 return 1;
289 }
290 CacheHash cache_hash;
291 CacheHash *ch = &cache_hash;
292 cache_init(ch, manifest_dir);
293 Buf self_exe_path = BUF_INIT;
294 if ((err = os_self_exe_path(&self_exe_path))) {
295 fprintf(stderr, "self exe path: %s\n", err_str(err));
296 return 1;
297 }
298
299 cache_file(ch, &self_exe_path);
300
301 Buf exe_digest = BUF_INIT;
302 buf_resize(&exe_digest, 0);
303 if ((err = cache_hit(ch, &exe_digest))) {
304 fprintf(stderr, "cache hit error: %s\n", err_str(err));
305 return 1;
306 }
307 if (buf_len(&exe_digest) != 0) {
308 fprintf(stderr, "cache hit: %s\n", buf_ptr(&exe_digest));
309 return 0;
310 }
311 fprintf(stderr, "cache miss\n");
312 ZigList<Buf *> lib_paths = {};
313 if ((err = os_self_exe_shared_libs(lib_paths))) {
314 fprintf(stderr, "finding out shared libs: %s\n", err_str(err));
315 return 1;
316 }
317 for (size_t i = 0; i < lib_paths.length; i += 1) {
318 Buf *lib_path = lib_paths.at(i);
319 if ((err = cache_add_file(ch, lib_path))) {
320 fprintf(stderr, "cache add file %s: %s", buf_ptr(lib_path), err_str(err));
321 return 1;
322 }
323 }
324 if ((err = cache_final(ch, &exe_digest))) {
325 fprintf(stderr, "final: %s\n", err_str(err));
326 return 1;
327 }
328
329
330 fprintf(stderr, "computed2: %s\n", buf_ptr(&exe_digest));
331 return 0;
332 }
333
273334 os_init();
274335
275336 char *arg0 = argv[0];
src/os.cpp+243-71
......@@ -40,6 +40,10 @@ typedef SSIZE_T ssize_t;
4040
4141#endif
4242
43#if defined(ZIG_OS_LINUX)
44#include <link.h>
45#endif
46
4347
4448#if defined(__MACH__)
4549#include <mach/clock.h>
......@@ -57,54 +61,6 @@ static clock_serv_t cclock;
5761#include <errno.h>
5862#include <time.h>
5963
60// Ported from std/mem.zig.
61// Coordinate struct fields with memSplit function
62struct SplitIterator {
63 size_t index;
64 Slice<uint8_t> buffer;
65 Slice<uint8_t> split_bytes;
66};
67
68// Ported from std/mem.zig.
69static bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte) {
70 for (size_t i = 0; i < self->split_bytes.len; i += 1) {
71 if (byte == self->split_bytes.ptr[i]) {
72 return true;
73 }
74 }
75 return false;
76}
77
78// Ported from std/mem.zig.
79static Optional<Slice<uint8_t>> SplitIterator_next(SplitIterator *self) {
80 // move to beginning of token
81 while (self->index < self->buffer.len &&
82 SplitIterator_isSplitByte(self, self->buffer.ptr[self->index]))
83 {
84 self->index += 1;
85 }
86 size_t start = self->index;
87 if (start == self->buffer.len) {
88 return {};
89 }
90
91 // move to end of token
92 while (self->index < self->buffer.len &&
93 !SplitIterator_isSplitByte(self, self->buffer.ptr[self->index]))
94 {
95 self->index += 1;
96 }
97 size_t end = self->index;
98
99 return Optional<Slice<uint8_t>>::some(self->buffer.slice(start, end));
100}
101
102// Ported from std/mem.zig
103static SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes) {
104 return SplitIterator{0, buffer, split_bytes};
105}
106
107
10864#if defined(ZIG_OS_POSIX)
10965static void populate_termination(Termination *term, int status) {
11066 if (WIFEXITED(status)) {
......@@ -1368,16 +1324,16 @@ double os_get_time(void) {
13681324#endif
13691325}
13701326
1371int os_make_path(Buf *path) {
1327Error os_make_path(Buf *path) {
13721328 Buf resolved_path = os_path_resolve(&path, 1);
13731329
13741330 size_t end_index = buf_len(&resolved_path);
1375 int err;
1331 Error err;
13761332 while (true) {
13771333 if ((err = os_make_dir(buf_slice(&resolved_path, 0, end_index)))) {
13781334 if (err == ErrorPathAlreadyExists) {
13791335 if (end_index == buf_len(&resolved_path))
1380 return 0;
1336 return ErrorNone;
13811337 } else if (err == ErrorFileNotFound) {
13821338 // march end_index backward until next path component
13831339 while (true) {
......@@ -1391,7 +1347,7 @@ int os_make_path(Buf *path) {
13911347 }
13921348 }
13931349 if (end_index == buf_len(&resolved_path))
1394 return 0;
1350 return ErrorNone;
13951351 // march end_index forward until next path component
13961352 while (true) {
13971353 end_index += 1;
......@@ -1399,10 +1355,10 @@ int os_make_path(Buf *path) {
13991355 break;
14001356 }
14011357 }
1402 return 0;
1358 return ErrorNone;
14031359}
14041360
1405int os_make_dir(Buf *path) {
1361Error os_make_dir(Buf *path) {
14061362#if defined(ZIG_OS_WINDOWS)
14071363 if (!CreateDirectory(buf_ptr(path), NULL)) {
14081364 if (GetLastError() == ERROR_ALREADY_EXISTS)
......@@ -1413,7 +1369,7 @@ int os_make_dir(Buf *path) {
14131369 return ErrorAccess;
14141370 return ErrorUnexpected;
14151371 }
1416 return 0;
1372 return ErrorNone;
14171373#else
14181374 if (mkdir(buf_ptr(path), 0755) == -1) {
14191375 if (errno == EEXIST)
......@@ -1424,7 +1380,7 @@ int os_make_dir(Buf *path) {
14241380 return ErrorAccess;
14251381 return ErrorUnexpected;
14261382 }
1427 return 0;
1383 return ErrorNone;
14281384#endif
14291385}
14301386
......@@ -1447,7 +1403,7 @@ int os_init(void) {
14471403 return 0;
14481404}
14491405
1450int os_self_exe_path(Buf *out_path) {
1406Error os_self_exe_path(Buf *out_path) {
14511407#if defined(ZIG_OS_WINDOWS)
14521408 buf_resize(out_path, 256);
14531409 for (;;) {
......@@ -1480,27 +1436,21 @@ int os_self_exe_path(Buf *out_path) {
14801436 char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path));
14811437 if (!real_path) {
14821438 buf_init_from_buf(out_path, tmp);
1483 return 0;
1439 return ErrorNone;
14841440 }
14851441
14861442 // Resize out_path for the correct length.
14871443 buf_resize(out_path, strlen(buf_ptr(out_path)));
14881444
1489 return 0;
1445 return ErrorNone;
14901446#elif defined(ZIG_OS_LINUX)
1491 buf_resize(out_path, 256);
1492 for (;;) {
1493 ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path));
1494 if (amt == -1) {
1495 return ErrorUnexpected;
1496 }
1497 if (amt == (ssize_t)buf_len(out_path)) {
1498 buf_resize(out_path, buf_len(out_path) * 2);
1499 continue;
1500 }
1501 buf_resize(out_path, amt);
1502 return 0;
1447 buf_resize(out_path, PATH_MAX);
1448 ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path));
1449 if (amt == -1) {
1450 return ErrorUnexpected;
15031451 }
1452 buf_resize(out_path, amt);
1453 return ErrorNone;
15041454#endif
15051455 return ErrorFileNotFound;
15061456}
......@@ -1685,3 +1635,225 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
16851635 return ErrorFileNotFound;
16861636#endif
16871637}
1638
1639// Ported from std/os/get_app_data_dir.zig
1640Error os_get_app_data_dir(Buf *out_path, const char *appname) {
1641#if defined(ZIG_OS_WINDOWS)
1642#error "Unimplemented"
1643#elif defined(ZIG_OS_DARWIN)
1644 const char *home_dir = getenv("HOME");
1645 if (home_dir == nullptr) {
1646 // TODO use /etc/passwd
1647 return ErrorFileNotFound;
1648 }
1649 buf_resize(out_path, 0);
1650 buf_appendf(out_path, "%s/Library/Application Support/%s", home_dir, appname);
1651 return ErrorNone;
1652#elif defined(ZIG_OS_LINUX)
1653 const char *home_dir = getenv("HOME");
1654 if (home_dir == nullptr) {
1655 // TODO use /etc/passwd
1656 return ErrorFileNotFound;
1657 }
1658 buf_resize(out_path, 0);
1659 buf_appendf(out_path, "%s/.local/share/%s", home_dir, appname);
1660 return ErrorNone;
1661#endif
1662}
1663
1664
1665#if defined(ZIG_OS_LINUX)
1666static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, void *data) {
1667 ZigList<Buf *> *libs = reinterpret_cast< ZigList<Buf *> *>(data);
1668 if (info->dlpi_name[0] == '/') {
1669 libs->append(buf_create_from_str(info->dlpi_name));
1670 }
1671 return 0;
1672}
1673#endif
1674
1675Error os_self_exe_shared_libs(ZigList<Buf *> &paths) {
1676#if defined(ZIG_OS_LINUX)
1677 paths.resize(0);
1678 dl_iterate_phdr(self_exe_shared_libs_callback, &paths);
1679 return ErrorNone;
1680#else
1681#error "unimplemented"
1682#endif
1683}
1684
1685Error os_file_open_r(Buf *full_path, OsFile *out_file) {
1686#if defined(ZIG_OS_WINDOWS)
1687#error "unimplemented"
1688#else
1689 for (;;) {
1690 int fd = open(buf_ptr(full_path), O_RDONLY|O_CLOEXEC);
1691 if (fd == -1) {
1692 switch (errno) {
1693 case EINTR:
1694 continue;
1695 case EINVAL:
1696 zig_unreachable();
1697 case EFAULT:
1698 zig_unreachable();
1699 case EACCES:
1700 return ErrorAccess;
1701 case EISDIR:
1702 return ErrorIsDir;
1703 case ENOENT:
1704 return ErrorFileNotFound;
1705 default:
1706 return ErrorFileSystem;
1707 }
1708 }
1709 *out_file = fd;
1710 return ErrorNone;
1711 }
1712#endif
1713}
1714
1715Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) {
1716#if defined(ZIG_OS_WINDOWS)
1717#error "unimplemented"
1718#else
1719 int fd;
1720 for (;;) {
1721 fd = open(buf_ptr(full_path), O_RDWR|O_CLOEXEC|O_CREAT, 0666);
1722 if (fd == -1) {
1723 switch (errno) {
1724 case EINTR:
1725 continue;
1726 case EINVAL:
1727 zig_unreachable();
1728 case EFAULT:
1729 zig_unreachable();
1730 case EACCES:
1731 return ErrorAccess;
1732 case EISDIR:
1733 return ErrorIsDir;
1734 case ENOENT:
1735 return ErrorFileNotFound;
1736 default:
1737 return ErrorFileSystem;
1738 }
1739 }
1740 break;
1741 }
1742 for (;;) {
1743 struct flock lock;
1744 lock.l_type = F_WRLCK;
1745 lock.l_whence = SEEK_SET;
1746 lock.l_start = 0;
1747 lock.l_len = 0;
1748 if (fcntl(fd, F_SETLKW, &lock) == -1) {
1749 switch (errno) {
1750 case EINTR:
1751 continue;
1752 case EBADF:
1753 zig_unreachable();
1754 case EFAULT:
1755 zig_unreachable();
1756 case EINVAL:
1757 zig_unreachable();
1758 default:
1759 close(fd);
1760 return ErrorFileSystem;
1761 }
1762 }
1763 break;
1764 }
1765 *out_file = fd;
1766 return ErrorNone;
1767#endif
1768}
1769
1770Error os_file_mtime(OsFile file, OsTimeStamp *mtime) {
1771#if defined(ZIG_OS_WINDOWS)
1772#error unimplemented
1773#else
1774 struct stat statbuf;
1775 if (fstat(file, &statbuf) == -1)
1776 return ErrorFileSystem;
1777
1778 mtime->sec = statbuf.st_mtim.tv_sec;
1779 mtime->nsec = statbuf.st_mtim.tv_nsec;
1780 return ErrorNone;
1781#endif
1782}
1783
1784Error os_file_read(OsFile file, void *ptr, size_t *len) {
1785#if defined(ZIG_OS_WINDOWS)
1786#error unimplemented
1787#else
1788 for (;;) {
1789 ssize_t rc = read(file, ptr, *len);
1790 if (rc == -1) {
1791 switch (errno) {
1792 case EINTR:
1793 continue;
1794 case EBADF:
1795 zig_unreachable();
1796 case EFAULT:
1797 zig_unreachable();
1798 case EISDIR:
1799 zig_unreachable();
1800 default:
1801 return ErrorFileSystem;
1802 }
1803 }
1804 *len = rc;
1805 return ErrorNone;
1806 }
1807#endif
1808}
1809
1810Error os_file_read_all(OsFile file, Buf *contents) {
1811 Error err;
1812 size_t index = 0;
1813 for (;;) {
1814 size_t amt = buf_len(contents) - index;
1815
1816 if (amt < 512) {
1817 buf_resize(contents, buf_len(contents) + 512);
1818 amt += 512;
1819 }
1820
1821 if ((err = os_file_read(file, buf_ptr(contents) + index, &amt)))
1822 return err;
1823
1824 if (amt == 0) {
1825 buf_resize(contents, index);
1826 return ErrorNone;
1827 }
1828
1829 index += amt;
1830 }
1831}
1832
1833Error os_file_overwrite(OsFile file, Buf *contents) {
1834#if defined(ZIG_OS_WINDOWS)
1835#error unimplemented
1836#else
1837 if (lseek(file, 0, SEEK_SET) == -1)
1838 return ErrorFileSystem;
1839 for (;;) {
1840 if (write(file, buf_ptr(contents), buf_len(contents)) == -1) {
1841 switch (errno) {
1842 case EINTR:
1843 continue;
1844 case EINVAL:
1845 zig_unreachable();
1846 case EBADF:
1847 zig_unreachable();
1848 default:
1849 return ErrorFileSystem;
1850 }
1851 }
1852 return ErrorNone;
1853 }
1854#endif
1855}
1856
1857void os_file_close(OsFile file) {
1858 close(file);
1859}
src/os.hpp+58-34
......@@ -13,10 +13,43 @@
1313#include "error.hpp"
1414#include "zig_llvm.h"
1515#include "windows_sdk.h"
16#include "result.hpp"
1617
1718#include <stdio.h>
1819#include <inttypes.h>
1920
21#if defined(__APPLE__)
22#define ZIG_OS_DARWIN
23#elif defined(_WIN32)
24#define ZIG_OS_WINDOWS
25#elif defined(__linux__)
26#define ZIG_OS_LINUX
27#else
28#define ZIG_OS_UNKNOWN
29#endif
30
31#if defined(__x86_64__)
32#define ZIG_ARCH_X86_64
33#else
34#define ZIG_ARCH_UNKNOWN
35#endif
36
37#if defined(ZIG_OS_WINDOWS)
38#define ZIG_PRI_usize "I64u"
39#define ZIG_PRI_u64 "I64u"
40#define ZIG_PRI_llu "I64u"
41#define ZIG_PRI_x64 "I64x"
42#define OS_SEP "\\"
43#define ZIG_OS_SEP_CHAR '\\'
44#else
45#define ZIG_PRI_usize "zu"
46#define ZIG_PRI_u64 PRIu64
47#define ZIG_PRI_llu "llu"
48#define ZIG_PRI_x64 PRIx64
49#define OS_SEP "/"
50#define ZIG_OS_SEP_CHAR '/'
51#endif
52
2053enum TermColor {
2154 TermColorRed,
2255 TermColorGreen,
......@@ -38,6 +71,17 @@ struct Termination {
3871 int code;
3972};
4073
74#if defined(ZIG_OS_WINDOWS)
75#define OsFile (void *)
76#else
77#define OsFile int
78#endif
79
80struct OsTimeStamp {
81 uint64_t sec;
82 uint64_t nsec;
83};
84
4185int os_init(void);
4286
4387void os_spawn_process(const char *exe, ZigList<const char *> &args, Termination *term);
......@@ -54,8 +98,16 @@ bool os_path_is_absolute(Buf *path);
5498
5599int os_get_global_cache_directory(Buf *out_tmp_path);
56100
57int os_make_path(Buf *path);
58int os_make_dir(Buf *path);
101Error ATTRIBUTE_MUST_USE os_make_path(Buf *path);
102Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path);
103
104Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file);
105Error ATTRIBUTE_MUST_USE os_file_open_lock_rw(Buf *full_path, OsFile *out_file);
106Error ATTRIBUTE_MUST_USE os_file_mtime(OsFile file, OsTimeStamp *mtime);
107Error ATTRIBUTE_MUST_USE os_file_read(OsFile file, void *ptr, size_t *len);
108Error ATTRIBUTE_MUST_USE os_file_read_all(OsFile file, Buf *contents);
109Error ATTRIBUTE_MUST_USE os_file_overwrite(OsFile file, Buf *contents);
110void os_file_close(OsFile file);
59111
60112void os_write_file(Buf *full_path, Buf *contents);
61113int os_copy_file(Buf *src_path, Buf *dest_path);
......@@ -78,42 +130,14 @@ double os_get_time(void);
78130
79131bool os_is_sep(uint8_t c);
80132
81int os_self_exe_path(Buf *out_path);
133Error ATTRIBUTE_MUST_USE os_self_exe_path(Buf *out_path);
134
135Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname);
82136
83137int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf);
84138int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
85139int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
86140
87#if defined(__APPLE__)
88#define ZIG_OS_DARWIN
89#elif defined(_WIN32)
90#define ZIG_OS_WINDOWS
91#elif defined(__linux__)
92#define ZIG_OS_LINUX
93#else
94#define ZIG_OS_UNKNOWN
95#endif
96
97#if defined(__x86_64__)
98#define ZIG_ARCH_X86_64
99#else
100#define ZIG_ARCH_UNKNOWN
101#endif
102
103#if defined(ZIG_OS_WINDOWS)
104#define ZIG_PRI_usize "I64u"
105#define ZIG_PRI_u64 "I64u"
106#define ZIG_PRI_llu "I64u"
107#define ZIG_PRI_x64 "I64x"
108#define OS_SEP "\\"
109#define ZIG_OS_SEP_CHAR '\\'
110#else
111#define ZIG_PRI_usize "zu"
112#define ZIG_PRI_u64 PRIu64
113#define ZIG_PRI_llu "llu"
114#define ZIG_PRI_x64 PRIx64
115#define OS_SEP "/"
116#define ZIG_OS_SEP_CHAR '/'
117#endif
141Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList<Buf *> &paths);
118142
119143#endif
src/util.cpp+39
......@@ -43,3 +43,42 @@ uint32_t ptr_hash(const void *ptr) {
4343bool ptr_eq(const void *a, const void *b) {
4444 return a == b;
4545}
46
47// Ported from std/mem.zig.
48bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte) {
49 for (size_t i = 0; i < self->split_bytes.len; i += 1) {
50 if (byte == self->split_bytes.ptr[i]) {
51 return true;
52 }
53 }
54 return false;
55}
56
57// Ported from std/mem.zig.
58Optional<Slice<uint8_t>> SplitIterator_next(SplitIterator *self) {
59 // move to beginning of token
60 while (self->index < self->buffer.len &&
61 SplitIterator_isSplitByte(self, self->buffer.ptr[self->index]))
62 {
63 self->index += 1;
64 }
65 size_t start = self->index;
66 if (start == self->buffer.len) {
67 return {};
68 }
69
70 // move to end of token
71 while (self->index < self->buffer.len &&
72 !SplitIterator_isSplitByte(self, self->buffer.ptr[self->index]))
73 {
74 self->index += 1;
75 }
76 size_t end = self->index;
77
78 return Optional<Slice<uint8_t>>::some(self->buffer.slice(start, end));
79}
80
81// Ported from std/mem.zig
82SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes) {
83 return SplitIterator{0, buffer, split_bytes};
84}
src/util.hpp+12
......@@ -254,4 +254,16 @@ static inline void memCopy(Slice<T> dest, Slice<T> src) {
254254 memcpy(dest.ptr, src.ptr, src.len * sizeof(T));
255255}
256256
257// Ported from std/mem.zig.
258// Coordinate struct fields with memSplit function
259struct SplitIterator {
260 size_t index;
261 Slice<uint8_t> buffer;
262 Slice<uint8_t> split_bytes;
263};
264
265bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte);
266Optional<Slice<uint8_t>> SplitIterator_next(SplitIterator *self);
267SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes);
268
257269#endif