From 30fd24abd4e2f4223d75313909cb0c10a9c8e738 Mon Sep 17 00:00:00 2001 From: herman ten brugge Date: Sun, 25 Jun 2023 20:23:58 +0200 Subject: [PATCH] Allow different names for pragma once pragma once can now be used with test.h ./test.h and other references to the same file just like gcc/clang. On linux we can use stat and st_ino to check for the same file. On windows the st_ino does not work so we calculate a file hash if the size of the file differs and then compare the hash. --- tcc.h | 5 +++ tccpp.c | 74 ++++++++++++++++++++++++++++------ tests/tests2/18_include.c | 4 ++ tests/tests2/18_include.expect | 1 + tests/tests2/18_include2.h | 2 + 5 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 tests/tests2/18_include2.h diff --git a/tcc.h b/tcc.h index 00d80879..78b0174a 100644 --- a/tcc.h +++ b/tcc.h @@ -38,6 +38,7 @@ #include #include #include +#include #ifndef _WIN32 # include @@ -716,6 +717,10 @@ typedef struct InlineFunc { /* include file cache, used to find files faster and also to eliminate inclusion if the include file is protected by #ifndef ... #endif */ typedef struct CachedInclude { + struct stat st; +#ifdef _WIN32 + unsigned long long hash; +#endif int ifndef_macro; int once; int hash_next; /* -1 if none */ diff --git a/tccpp.c b/tccpp.c index a76654e3..a2fafddf 100644 --- a/tccpp.c +++ b/tccpp.c @@ -1607,38 +1607,88 @@ bad_twosharp: define_push(v, t, tok_str_dup(&tokstr_buf), first); } +#ifdef _WIN32 +static unsigned long long calc_file_hash(const char *filename) +{ + unsigned long long hash = 14695981039346656037ull; // FNV_offset_basis; + FILE *fp = fopen (filename, "rb"); + + if (fp == NULL) + return 0; + for (;;) { + unsigned char temp[IO_BUF_SIZE]; + int i, n = fread(temp, 1, sizeof(temp), fp); + + if (n <= 0) + break; + for (i = 0; i < n; i++) + hash = hash * 1099511628211ull + temp[i]; // FNV_prime + } + fclose(fp); + if (hash == 0) + hash = 1; + return hash; +} +#endif + static CachedInclude *search_cached_include(TCCState *s1, const char *filename, int add) { - const unsigned char *s; - unsigned int h; + unsigned int h = 0; CachedInclude *e; int i; - - h = TOK_HASH_INIT; - s = (unsigned char *) filename; - while (*s) { + struct stat st; #ifdef _WIN32 - h = TOK_HASH_FUNC(h, toup(*s)); -#else - h = TOK_HASH_FUNC(h, *s); + unsigned long long hash = 0; #endif - s++; + + /* This is needed for #pragmae once + * We cannot use stat on windows because st_ino is not set correctly + * so we calculate a hash of file contents. + * This also works for hard/soft links as in gcc/clang. + */ + memset (&st, 0, sizeof(st)); + if (stat (filename, &st)) + goto skip; + h = st.st_size & (CACHED_INCLUDES_HASH_SIZE - 1); +#ifdef _WIN32 + /* Only calculate file hash if file size same. */ + i = s1->cached_includes_hash[h]; + for(;;) { + if (i == 0) + break; + e = s1->cached_includes[i - 1]; + if (e->st.st_size == st.st_size) { + if (e->hash == 0) + e->hash = calc_file_hash(e->filename); + hash = calc_file_hash(filename); + break; + } + i = e->hash_next; } - h &= (CACHED_INCLUDES_HASH_SIZE - 1); +#endif i = s1->cached_includes_hash[h]; for(;;) { if (i == 0) break; e = s1->cached_includes[i - 1]; - if (0 == PATHCMP(e->filename, filename)) +#ifdef _WIN32 + if (e->st.st_size == st.st_size && e->hash == hash) +#else + if (st.st_dev == e->st.st_dev && st.st_ino == e->st.st_ino) +#endif return e; i = e->hash_next; } +skip: if (!add) return NULL; e = tcc_malloc(sizeof(CachedInclude) + strlen(filename)); + e->st = st; +#ifdef _WIN32 + e->hash = hash; +#endif strcpy(e->filename, filename); e->ifndef_macro = e->once = 0; dynarray_add(&s1->cached_includes, &s1->nb_cached_includes, e); diff --git a/tests/tests2/18_include.c b/tests/tests2/18_include.c index a85327e5..9ff60c96 100644 --- a/tests/tests2/18_include.c +++ b/tests/tests2/18_include.c @@ -37,6 +37,10 @@ int main() #endif #endif +#include "18_include2.h" +#include "./18_include2.h" +#include "../tests2/18_include2.h" + return 0; } diff --git a/tests/tests2/18_include.expect b/tests/tests2/18_include.expect index 15c96c07..fbc6d134 100644 --- a/tests/tests2/18_include.expect +++ b/tests/tests2/18_include.expect @@ -5,3 +5,4 @@ has_include has_include has_include_next has_include_next +counter 0 diff --git a/tests/tests2/18_include2.h b/tests/tests2/18_include2.h new file mode 100644 index 00000000..2a89c3d1 --- /dev/null +++ b/tests/tests2/18_include2.h @@ -0,0 +1,2 @@ +#pragma once +printf ("counter %d\n", __COUNTER__);