futhark-0.26.2: rts/c/cache.h
// Start of cache.h
#define CACHE_HASH_SIZE 8 // In 32-bit words.
struct cache_hash {
uint32_t hash[CACHE_HASH_SIZE];
};
// Initialise a blank cache.
static void cache_hash_init(struct cache_hash *c);
// Hash some bytes and add them to the accumulated hash.
static void cache_hash(struct cache_hash *out, const char *in, size_t n);
// Try to restore cache contents from a file with the given name.
// Assumes the cache is invalid if it contains the given hash.
// Allocates memory and reads the cache conents, which is returned in
// *buf with size *buflen. If the cache is successfully loaded, this
// function returns 0. Otherwise it returns nonzero. Errno is set if
// the failure to load the cache is due to anything except invalid
// cache conents. Note that failing to restore the cache is not
// necessarily a problem: it might just be invalid or not created yet.
static int cache_restore(const char *fname, const struct cache_hash *hash,
unsigned char **buf, size_t *buflen);
// Store cache contents in the given file, with the given hash.
static int cache_store(const char *fname, const struct cache_hash *hash,
const unsigned char *buf, size_t buflen);
// Now for the implementation.
static void cache_hash_init(struct cache_hash *c) {
memset(c->hash, 0, CACHE_HASH_SIZE * sizeof(uint32_t));
}
static void cache_hash(struct cache_hash *out, const char *in, size_t n) {
// Adaptation of djb2 for larger output size by storing intermediate
// states.
uint32_t hash = 5381;
for (size_t i = 0; i < n; i++) {
hash = ((hash << 5) + hash) + in[i];
out->hash[i % CACHE_HASH_SIZE] ^= hash;
}
}
#define CACHE_HEADER_SIZE 8
static const char cache_header[CACHE_HEADER_SIZE] = "FUTHARK";
static int cache_restore(const char *fname, const struct cache_hash *hash,
unsigned char **buf, size_t *buflen) {
FILE *f = fopen(fname, "rb");
if (f == NULL) {
return 1;
}
char f_header[CACHE_HEADER_SIZE];
if (fread(f_header, sizeof(char), CACHE_HEADER_SIZE, f) != CACHE_HEADER_SIZE) {
goto error;
}
if (memcmp(f_header, cache_header, CACHE_HEADER_SIZE) != 0) {
goto error;
}
if (fseek(f, 0, SEEK_END) != 0) {
goto error;
}
int64_t f_size = (int64_t)ftell(f);
if (fseek(f, CACHE_HEADER_SIZE, SEEK_SET) != 0) {
goto error;
}
int64_t expected_size;
if (fread(&expected_size, sizeof(int64_t), 1, f) != 1) {
goto error;
}
if (f_size != expected_size) {
errno = 0;
goto error;
}
int32_t f_hash[CACHE_HASH_SIZE];
if (fread(f_hash, sizeof(int32_t), CACHE_HASH_SIZE, f) != CACHE_HASH_SIZE) {
goto error;
}
if (memcmp(f_hash, hash->hash, CACHE_HASH_SIZE) != 0) {
errno = 0;
goto error;
}
*buflen = f_size - CACHE_HEADER_SIZE - sizeof(int64_t) - CACHE_HASH_SIZE*sizeof(int32_t);
*buf = malloc(*buflen);
if (fread(*buf, sizeof(char), *buflen, f) != *buflen) {
free(*buf);
goto error;
}
fclose(f);
return 0;
error:
fclose(f);
return 1;
}
static int cache_store(const char *fname, const struct cache_hash *hash,
const unsigned char *buf, size_t buflen) {
FILE *f = fopen(fname, "wb");
if (f == NULL) {
return 1;
}
if (fwrite(cache_header, CACHE_HEADER_SIZE, 1, f) != 1) {
goto error;
}
int64_t size = CACHE_HEADER_SIZE + sizeof(int64_t) + CACHE_HASH_SIZE*sizeof(int32_t) + buflen;
if (fwrite(&size, sizeof(size), 1, f) != 1) {
goto error;
}
if (fwrite(hash->hash, sizeof(int32_t), CACHE_HASH_SIZE, f) != CACHE_HASH_SIZE) {
goto error;
}
if (fwrite(buf, sizeof(unsigned char), buflen, f) != buflen) {
goto error;
}
fclose(f);
return 0;
error:
fclose(f);
return 1;
}
// End of cache.h