1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
// 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\0";
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
|