#ifndef DISFLUID_DISFLUID_CACHE_ENTRY_INCLUDED # define DISFLUID_DISFLUID_CACHE_ENTRY_INCLUDED # include # include "string-desc.h" # include MAYBE_UNUSED static int ao_cache_entry_read (int fd, size_t offset, struct timespec *request_date, struct timespec *response_date, bool *invalidated, bool read_key, bool read_response_header, bool read_response_body, string_desc_t * key, string_desc_t * response_header, string_desc_t * response_body); MAYBE_UNUSED static int ao_cache_entry_push (int fd, size_t *offset, const struct timespec *request_date, const struct timespec *response_date, bool invalidated, const string_desc_t key, const string_desc_t response_header, const string_desc_t response_body); # include "disfluid-append-only-file.h" # include "safe-alloc.h" /* The cache entry file is organized as follows: - on 8 bytes, the request date (timestamp in seconds), - on 4 bytes, the request date (number of nanoseconds), - on 8 bytes, the response date (timestamp in seconds), - on 4 bytes, the response date (number of nanoseconds), - on 8 bytes, the size of the key, - on 8 bytes, the offset of the key, - on 8 bytes, the size of the header, - on 8 bytes, the offset of the header, - on 8 bytes, the size of the body, - on 8 bytes, the offset of the body, - 80 if the entry has been artificially invalidated, 00 otherwise. - 7 bytes reserved. */ struct cache_entry_header { struct timespec request_date; struct timespec response_date; size_t key_size; size_t key_offset; size_t header_size; size_t header_offset; size_t body_size; size_t body_offset; bool invalidated; }; static int ao_cache_entry_timespec_from_bytes (const uint8_t * bytes, struct timespec *date) { date->tv_sec = 0; date->tv_nsec = 0; for (size_t i = 0; i < 8; i++) { date->tv_sec *= 256; date->tv_sec += bytes[i]; } for (size_t j = 8; j < 12; j++) { date->tv_nsec *= 256; date->tv_nsec += bytes[j]; } if (date->tv_nsec >= 1000000000) { return -1; } return 0; } static int ao_cache_entry_timespec_to_bytes (const struct timespec *date, uint8_t * bytes) { if (date->tv_nsec >= 1000000000) { return -1; } uint64_t date_sec = date->tv_sec; uint32_t date_nsec = date->tv_nsec; for (size_t i = 8; i-- > 0;) { bytes[i] = date_sec % 256; date_sec /= 256; } for (size_t i = 12; i-- > 8;) { bytes[i] = date_nsec % 256; date_nsec /= 256; } return 0; } static int ao_cache_entry_offset_from_bytes (const uint8_t * bytes, size_t *length, size_t *offset) { *length = 0; *offset = 0; for (size_t i = 0; i < 8; i++) { *length *= 256; *length += bytes[i]; } for (size_t j = 8; j < 16; j++) { *offset *= 256; *offset += bytes[j]; } return 0; } static int ao_cache_entry_offset_to_bytes (size_t length, size_t offset, uint8_t * bytes) { for (size_t i = 8; i-- > 0;) { bytes[i] = length % 256; length /= 256; } for (size_t i = 16; i-- > 8;) { bytes[i] = offset % 256; offset /= 256; } return 0; } static int ao_cache_entry_from_bytes (const uint8_t * bytes, struct cache_entry_header *entry) { if (ao_cache_entry_timespec_from_bytes (bytes, &(entry->request_date)) < 0) { return -1; } bytes += 12; if (ao_cache_entry_timespec_from_bytes (bytes, &(entry->response_date)) < 0) { return -1; } bytes += 12; if (ao_cache_entry_offset_from_bytes (bytes, &(entry->key_size), &(entry->key_offset)) < 0) { return -1; } bytes += 16; if (ao_cache_entry_offset_from_bytes (bytes, &(entry->header_size), &(entry->header_offset)) < 0) { return -1; } bytes += 16; if (ao_cache_entry_offset_from_bytes (bytes, &(entry->body_size), &(entry->body_offset)) < 0) { return -1; } bytes += 16; if (*bytes >= 128) { entry->invalidated = true; } else { entry->invalidated = false; } return 0; } static int ao_cache_entry_to_bytes (const struct cache_entry_header *header, uint8_t * dest) { if (ao_cache_entry_timespec_to_bytes (&(header->request_date), dest) < 0) { return -1; } dest += 12; if (ao_cache_entry_timespec_to_bytes (&(header->response_date), dest) < 0) { return -1; } dest += 12; if (ao_cache_entry_offset_to_bytes (header->key_size, header->key_offset, dest) < 0) { return -1; } dest += 16; if (ao_cache_entry_offset_to_bytes (header->header_size, header->header_offset, dest) < 0) { return -1; } dest += 16; if (ao_cache_entry_offset_to_bytes (header->body_size, header->body_offset, dest) < 0) { return -1; } dest += 16; *dest = 0; if (header->invalidated) { *dest = 128; } dest += 1; for (size_t i = 0; i < 7; i++) { dest[i] = 0; } return 0; } static int ao_cache_entry_read (int fd, size_t offset, struct timespec *request_date, struct timespec *response_date, bool *invalidated, bool read_key, bool read_response_header, bool read_response_body, string_desc_t * key, string_desc_t * response_header, string_desc_t * response_body) { struct cache_entry_header header; uint8_t header_bytes[12 * 2 + 16 * 3 + 1 + 7] = { 0 }; string_desc_t data = {._nbytes = sizeof (header_bytes),._data = header_bytes }; key->_data = NULL; response_header->_data = NULL; response_body->_data = NULL; *invalidated = false; request_date->tv_sec = request_date->tv_nsec = 0; response_date->tv_sec = response_date->tv_nsec = 0; if (ao_file_read (fd, offset, data) < 0) { return -1; } if (ao_cache_entry_from_bytes (header_bytes, &header) < 0) { return -1; } request_date->tv_sec = header.request_date.tv_sec; request_date->tv_nsec = header.request_date.tv_nsec; response_date->tv_sec = header.response_date.tv_sec; response_date->tv_nsec = header.response_date.tv_nsec; *invalidated = header.invalidated; key->_nbytes = header.key_size; response_header->_nbytes = header.header_size; response_body->_nbytes = header.body_size; if (read_key) { if (ALLOC_N (key->_data, key->_nbytes) < 0) { goto on_error; } if (ao_file_read (fd, header.key_offset, *key) < 0) { goto on_error; } } if (read_response_header) { if (ALLOC_N (response_header->_data, response_header->_nbytes) < 0) { goto on_error; } if (ao_file_read (fd, header.header_offset, *response_header) < 0) { goto on_error; } } if (read_response_body) { if (ALLOC_N (response_body->_data, response_body->_nbytes) < 0) { goto on_error; } if (ao_file_read (fd, header.body_offset, *response_body) < 0) { goto on_error; } } return 0; /* Unsuccessful exit: */ on_error: FREE (key->_data); FREE (response_header->_data); FREE (response_body->_data); return -1; } static int ao_cache_entry_push (int fd, size_t *offset, const struct timespec *request_date, const struct timespec *response_date, bool invalidated, const string_desc_t key, const string_desc_t response_header, const string_desc_t response_body) { struct cache_entry_header header = { .request_date = { .tv_sec = request_date->tv_sec, .tv_nsec = request_date->tv_nsec}, .response_date = { .tv_sec = response_date->tv_sec, .tv_nsec = response_date->tv_nsec}, .key_size = key._nbytes, .header_size = response_header._nbytes, .body_size = response_body._nbytes, .invalidated = invalidated }; if (ao_file_push_data (fd, key, &(header.key_offset)) < 0) { return -1; } if (ao_file_push_data (fd, response_header, &(header.header_offset)) < 0) { return -1; } if (ao_file_push_data (fd, response_body, &(header.body_offset)) < 0) { return -1; } uint8_t header_bytes[12 * 2 + 16 * 3 + 1 + 7] = { 0 }; if (ao_cache_entry_to_bytes (&header, header_bytes) < 0) { return -1; } string_desc_t header_str = { ._nbytes = sizeof (header_bytes), ._data = header_bytes }; if (ao_file_push_data (fd, header_str, offset) < 0) { return -1; } return 0; } #endif /* DISFLUID_DISFLUID_CACHE_ENTRY_INCLUDED */