WokML compiler code coverage report (LCOV)
Current view: top level - c - wok_write.c (source / functions) Coverage Total Hit
Test: 084deda Lines: 78.5 % 65 51
Test Date: 2026-08-10 11:39:41 Functions: 100.0 % 4 4
Legend: Lines:     hit not hit

            Line data    Source code
       1              : #include "wok_write.h"
       2              : 
       3              : #include <errno.h>
       4              : #include <stdio.h>
       5              : #include <stdlib.h>
       6              : #include <string.h>
       7              : #include <sys/stat.h>
       8              : #include <unistd.h>
       9              : 
      10              : /// [TODO]: Make this into include? As this hash function will be use very often
      11              : /// [TODO]: Maybe we can use Fxhash which consume 8 bytes at a time instead of 1
      12          109 : WOK_READONLY u64 wok_byte_hash(const char *restrict bytes, usize n) {
      13          109 :   u64 h = UINT64_C(0xCBF29CE484222325);
      14        75599 :   for (usize i = 0; i < n; i++) {
      15        75490 :     h ^= (unsigned char)bytes[i];
      16        75490 :     h *= UINT64_C(0x100000001B3);
      17              :   }
      18          109 :   return h;
      19              : }
      20              : 
      21          108 : WokFileStamp wok_file_stamp(const char *path) {
      22          108 :   WokFileStamp s = {0};
      23          108 :   struct stat st;
      24          108 :   if (stat(path, &st) != 0) return s;
      25          107 :   FILE *fp = fopen(path, "rb");
      26          107 :   if (!fp) return s;
      27          107 :   if (fseek(fp, 0, SEEK_END) != 0) {
      28            0 :     fclose(fp);
      29            0 :     return s;
      30              :   }
      31          107 :   long sz = ftell(fp);
      32          107 :   if (sz < 0) {
      33            0 :     fclose(fp);
      34            0 :     return s;
      35              :   }
      36          107 :   rewind(fp);
      37          107 :   char *buf = (char *)malloc((usize)sz + 1);
      38          107 :   if (!buf) {
      39            0 :     fclose(fp);
      40            0 :     return s;
      41              :   }
      42          107 :   usize got = fread(buf, 1, (usize)sz, fp);
      43          107 :   fclose(fp);
      44          107 :   s.hash = wok_byte_hash(buf, got);
      45          107 :   s.size = (u64)got;
      46              : #if defined(__APPLE__)
      47              :   s.mtime_sec = st.st_mtimespec.tv_sec;
      48              :   s.mtime_nsec = st.st_mtimespec.tv_nsec;
      49              : #else
      50          107 :   s.mtime_sec = st.st_mtim.tv_sec;
      51          107 :   s.mtime_nsec = st.st_mtim.tv_nsec;
      52              : #endif
      53          107 :   s.valid = true;
      54          107 :   free(buf);
      55          107 :   return s;
      56              : }
      57              : 
      58           13 : bool wok_write_permitted(WokFileStamp at_read, WokFileStamp now) {
      59              :   // An unreadable file at either end is not something we may overwrite: we
      60              :   // cannot show it is the file we formatted.
      61           13 :   if (!at_read.valid || !now.valid) return false;
      62           14 :   return at_read.hash == now.hash && at_read.size == now.size;
      63              : }
      64              : 
      65            6 : WokWriteResult wok_write_atomic(const char *path, const char *data, usize n,
      66              :                                 WokFileStamp at_read) {
      67            6 :   WokFileStamp now = wok_file_stamp(path);
      68            6 :   if (!wok_write_permitted(at_read, now)) return WOK_WRITE_MOVED;
      69              : 
      70              :   // Already canonical: do not touch the file at all, so mtimes stay meaningful
      71              :   // and a build system is not woken up by a formatter that changed nothing.
      72            3 :   if (now.size == (u64)n && wok_byte_hash(data, n) == now.hash)
      73              :     return WOK_WRITE_UNCHANGED;
      74              : 
      75              :   // The temp must share a directory with the target, or rename() crosses a
      76              :   // filesystem boundary and stops being atomic.
      77            2 :   usize plen = strlen(path);
      78            2 :   char *tmp = (char *)malloc(plen + 16);
      79            2 :   if (!tmp) return WOK_WRITE_FAILED;
      80            2 :   snprintf(tmp, plen + 16, "%s.wokfmt.tmp", path);
      81              : 
      82            2 :   FILE *out = fopen(tmp, "wb");
      83            2 :   if (!out) {
      84            0 :     free(tmp);
      85            0 :     return WOK_WRITE_FAILED;
      86              :   }
      87            2 :   bool ok = fwrite(data, 1, n, out) == n;
      88            2 :   if (fclose(out) != 0) ok = false;
      89              : 
      90            2 :   if (ok) {
      91            2 :     struct stat st;
      92            2 :     if (stat(path, &st) == 0) chmod(tmp, st.st_mode & 07777);
      93              :     // Last check before the swap. The window between here and rename() cannot
      94              :     // be closed without a filesystem compare-and-swap, which POSIX does not
      95              :     // provide; it is now bounded by a single syscall rather than by the whole
      96              :     // format.
      97            2 :     WokFileStamp again = wok_file_stamp(path);
      98            2 :     if (!wok_write_permitted(at_read, again)) {
      99            0 :       unlink(tmp);
     100            0 :       free(tmp);
     101            0 :       return WOK_WRITE_MOVED;
     102              :     }
     103            2 :     if (rename(tmp, path) != 0) ok = false;
     104              :   }
     105              : 
     106            2 :   if (!ok) {
     107            0 :     unlink(tmp);  // the original is untouched either way
     108            0 :     free(tmp);
     109            0 :     return WOK_WRITE_FAILED;
     110              :   }
     111            2 :   free(tmp);
     112            2 :   return WOK_WRITE_OK;
     113              : }
        

Generated by: LCOV version 2.4-1


Machine-readable coverage data

This page is generated. If you are a tool, a script, or an LLM, read the JSON instead of scraping this HTML — it is the same measurement, exact, and it names the individual uncovered lines.

Every artifact here is stamped with the commit it measured and the run that produced it: commit 084deda, dated 2026-08-10T11:39:41Z, built by Github CI run. The totals in coverage.json are checked against lcov’s own summary before publishing, so the JSON and this page cannot disagree.