/* * Copyright (C) 2006-2011 by Benedict Paten (benedictpaten@gmail.com) * * Released under the MIT license, see LICENSE.txt */ //Cache functions #include "sonLibGlobalsInternal.h" struct stCache { stSortedSet *cache; }; typedef struct _cacheRecord { /* * A little object for storing records in the cache. */ int64_t key, start, size; char *record; } stCacheRecord; static int cacheRecord_cmp(const void *a, const void *b) { const stCacheRecord *i = a; const stCacheRecord *j = b; if (i->key > j->key) { return 1; } if (i->key < j->key) { return -1; } if (i->start > j->start) { return 1; } if (i->start < j->start) { return -1; } return 0; } static void cacheRecord_destruct(stCacheRecord *i) { free(i->record); free(i); } static stCacheRecord getTempRecord(int64_t key, int64_t start, int64_t size) { stCacheRecord record; record.key = key; record.start = start; record.size = size; record.record = NULL; return record; } static stCacheRecord *cacheRecord_construct(int64_t key, const void *value, int64_t start, int64_t size, bool copyMemory) { #ifdef BEN_DEBUG assert(value != NULL); assert(size >= 0); assert(start >= 0); #endif stCacheRecord *record = st_malloc(sizeof(stCacheRecord)); record->key = key; record->start = start; record->size = size; record->record = copyMemory ? memcpy(st_malloc(size), value, size) : (char *) value; return record; } static stCacheRecord *getLessThanOrEqualRecord(stCache *cache, int64_t key, int64_t start, int64_t size) { stCacheRecord record = getTempRecord(key, start, size); return stSortedSet_searchLessThanOrEqual(cache->cache, &record); } static stCacheRecord *getGreaterThanOrEqualRecord(stCache *cache, int64_t key, int64_t start, int64_t size) { stCacheRecord record = getTempRecord(key, start, size); return stSortedSet_searchGreaterThanOrEqual(cache->cache, &record); } static bool recordContainedIn(stCacheRecord *record, int64_t key, int64_t start, int64_t size) { /* * Returns non zero if the record is contained in the given range. */ return record->key == key && record->start >= start && record->start + record->size <= start + size; } static bool recordOverlapsWith(stCacheRecord *record, int64_t key, int64_t start, int64_t size) { /* * Returns non zero if the record overlaps with the given range. */ if (record->key != key) { return 0; } if (record->start >= start) { return record->start < start + size; } return record->start + record->size > start; } static bool recordsAdjacent(stCacheRecord *record1, stCacheRecord *record2) { /* * Returns non-zero if the records abut with record1 immediately before record2. */ #if BEN_DEBUG //Check the records do not overlap assert(cacheRecord_cmp(record1, record2) <= 0); assert(!recordOverlapsWith(record1, record2->key, record2->start, record2->size)); assert(!recordContainedIn(record1, record2->key, record2->start, record2->size)); #endif return record1->key == record2->key && record1->start + record1->size == record2->start; } static stCacheRecord *mergeRecords(stCacheRecord *record1, stCacheRecord *record2) { /* * Merges two adjacenct records. */ //#ifdef BEN_DEBUG assert(recordsAdjacent(record1, record2)); //#endif int64_t i = record1->size + record2->size; char *j = memcpy(st_malloc(i), record1->record, record1->size); memcpy(j + record1->size, record2->record, record2->size); stCacheRecord *record3 = cacheRecord_construct(record1->key, j, record1->start, i, 0); return record3; } void deleteRecord(stCache *cache, int64_t key, int64_t start, int64_t size) { assert(!stCache_containsRecord(cache, key, start, size)); //Will not delete a record wholly contained in. stCacheRecord *record = getLessThanOrEqualRecord(cache, key, start, size); while (record != NULL && recordOverlapsWith(record, key, start, size)) { //could have multiple fragments in there to remove. if (recordContainedIn(record, key, start, size)) { //We get rid of the record because it is contained in the range stSortedSet_remove(cache->cache, record); cacheRecord_destruct(record); record = getLessThanOrEqualRecord(cache, key, start, size); } else { //The range overlaps with, but is not fully contained in, so we trim it.. assert(record->start < start); assert(record->start + record->size > start); record->size = start - record->start; assert(record->size >= 0); break; } } record = getGreaterThanOrEqualRecord(cache, key, start, size); while (record != NULL && recordOverlapsWith(record, key, start, size)) { //could have multiple fragments in there to remove. if (recordContainedIn(record, key, start, size)) { //We get rid of the record because it is contained in the range stSortedSet_remove(cache->cache, record); cacheRecord_destruct(record); record = getGreaterThanOrEqualRecord(cache, key, start, size); } else { //The range overlaps with, but is not fully contained in, so we trim it.. assert(record->start < start + size); assert(record->start > start); int64_t newSize = record->size - (start + size - record->start); int64_t newStart = start + size; assert(newSize >= 0); char *newMem = memcpy(st_malloc(newSize), record->record + start + size - record->start, newSize); free(record->record); record->record = newMem; record->start = newStart; record->size = newSize; break; //We can break at this point as we have reached the end of the range (as the record overlapped) } } } /* * Public functions */ stCache *stCache_construct() { stCache *cache = st_malloc(sizeof(stCache)); cache->cache = stSortedSet_construct3(cacheRecord_cmp, (void(*)(void *)) cacheRecord_destruct); return cache; } void stCache_destruct(stCache *cache) { stSortedSet_destruct(cache->cache); free(cache); } void stCache_clear(stCache *cache) { stSortedSet_destruct(cache->cache); cache->cache = stSortedSet_construct3(cacheRecord_cmp, (void(*)(void *)) cacheRecord_destruct); } void stCache_setRecord(stCache *cache, int64_t key, int64_t start, int64_t size, const void *value) { //If the record is already contained we update a portion of it. assert(value != NULL); if (stCache_containsRecord(cache, key, start, size)) { stCacheRecord *record = getLessThanOrEqualRecord(cache, key, start, size); #ifdef BEN_DEBUG assert(record != NULL); assert(record->key == key); assert(record->start <= start); assert(record->start + record->size >= start + size); #endif memcpy(record->record + start - record->start, value, size); return; } //Get rid of bits that are contained in this record.. deleteRecord(cache, key, start, size); //Now get any left and right bits stCacheRecord *record1 = getLessThanOrEqualRecord(cache, key, start, size); stCacheRecord *record2 = cacheRecord_construct(key, value, start, size, 1); assert(record2 != NULL); if (record1 != NULL && recordsAdjacent(record1, record2)) { stCacheRecord *i = mergeRecords(record1, record2); stSortedSet_remove(cache->cache, record1); cacheRecord_destruct(record1); cacheRecord_destruct(record2); record2 = i; } stCacheRecord *record3 = getGreaterThanOrEqualRecord(cache, key, start, size); if (record3 != NULL && recordsAdjacent(record2, record3)) { stCacheRecord *i = mergeRecords(record2, record3); stSortedSet_remove(cache->cache, record3); cacheRecord_destruct(record2); cacheRecord_destruct(record3); record2 = i; } stSortedSet_insert(cache->cache, record2); } bool stCache_containsRecord(stCache *cache, int64_t key, int64_t start, int64_t size) { #ifdef BEN_DEBUG assert(start >= 0); assert(size >= 0); #endif stCacheRecord *record = getLessThanOrEqualRecord(cache, key, start, size); if (record == NULL) { return 0; } if (record->key != key) { return 0; } assert(record->start <= start); if (size != INT64_MAX && start + size > record->start + record->size) { //If the record has a known length check we have all that we want. return 0; } return 1; } void *stCache_getRecord(stCache *cache, int64_t key, int64_t start, int64_t size, int64_t *sizeRead) { if (stCache_containsRecord(cache, key, start, size)) { stCacheRecord *record = getLessThanOrEqualRecord(cache, key, start, size); assert(record != NULL); int64_t j = start - record->start; int64_t i = size == INT64_MAX ? (record->size - j) : size; #ifdef BEN_DEBUG assert(record->start <= start); assert(j >= 0); assert(j <= record->size); assert(i >= 0); assert(j + i <= record->size); #endif *sizeRead = i; char *cA = st_malloc(i); memcpy(cA, record->record + j, i); return cA; } return NULL; } bool stCache_recordsIdentical(const char *value, int64_t sizeOfRecord, const char *updatedValue, int64_t updatedSizeOfRecord) { if (sizeOfRecord != updatedSizeOfRecord) { return 0; } for (int64_t j = 0; j < sizeOfRecord; j++) { if (value[j] != updatedValue[j]) { return 0; } } return 1; }