/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/*
Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt
SPDX-License-Identifier: BSD-2-Clause-FreeBSD
SPDX-FileCopyrightText: 2021 The Libfossil Authors
SPDX-ArtifactOfProjectName: Libfossil
SPDX-FileType: Code
Heavily indebted to the Fossil SCM project (https://fossil-scm.org).
*/
/*****************************************************************************
This file some of the caching-related APIs.
*/
#include "fossil-scm/internal.h"
#include <assert.h>
/* Only for debugging */
#include <stdio.h>
#define MARKER(pfexp) \
do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \
printf pfexp; \
} while(0)
bool fsl__bccache_expire_oldest(fsl__bccache * const c){
static uint16_t const sentinel = 0xFFFF;
uint16_t i;
fsl_uint_t mnAge = c->nextAge;
uint16_t mn = sentinel;
for(i=0; i<c->used; i++){
if( c->list[i].age<mnAge ){
mnAge = c->list[i].age;
mn = i;
}
}
if( mn<sentinel ){
fsl_id_bag_remove(&c->inCache, c->list[mn].rid);
c->szTotal -= (unsigned)c->list[mn].content.capacity;
fsl_buffer_clear(&c->list[mn].content);
--c->used;
c->list[mn] = c->list[c->used];
}
return sentinel!=mn;
}
int fsl__bccache_insert(fsl__bccache * const c, fsl_id_t rid, fsl_buffer * const pBlob){
fsl__bccache_line *p;
if( c->used>c->usedLimit || c->szTotal>c->szLimit ){
fsl_size_t szBefore;
do{
szBefore = c->szTotal;
fsl__bccache_expire_oldest(c);
}while( c->szTotal>c->szLimit && c->szTotal<szBefore );
}
if((!c->usedLimit || !c->szLimit)
|| (c->used+1 >= c->usedLimit)){
fsl_buffer_clear(pBlob);
return 0;
}
if( c->used>=c->capacity ){
uint16_t const cap = c->capacity ? (c->capacity*2) : 10;
void * remem = c->list
? fsl_realloc(c->list, cap*sizeof(c->list[0]))
: fsl_malloc( cap*sizeof(c->list[0]) );
assert((c->capacity && cap<c->capacity) ? !"Numeric overflow" : 1);
if(c->capacity && cap<c->capacity){
fsl__fatal(FSL_RC_RANGE,"Numeric overflow. Bump "
"fsl__bccache::capacity to a larger int type.");
}
if(!remem){
fsl_buffer_clear(pBlob) /* for consistency */;
return FSL_RC_OOM;
}
c->capacity = cap;
c->list = (fsl__bccache_line*)remem;
}
int const rc = fsl_id_bag_insert(&c->inCache, rid);
if(0==rc){
p = &c->list[c->used++];
p->rid = rid;
p->age = c->nextAge++;
c->szTotal += pBlob->capacity;
p->content = *pBlob /* Transfer ownership */;
*pBlob = fsl_buffer_empty;
}else{
fsl_buffer_clear(pBlob);
}
return rc;
}
void fsl__bccache_clear(fsl__bccache * const c){
#if 0
while(fsl__bccache_expire_oldest(c)){}
#else
fsl_size_t i;
for(i=0; i<c->used; ++i){
fsl_buffer_clear(&c->list[i].content);
}
#endif
fsl_free(c->list);
fsl_id_bag_clear(&c->missing);
fsl_id_bag_clear(&c->available);
fsl_id_bag_clear(&c->inCache);
*c = fsl__bccache_empty;
}
void fsl__bccache_reset(fsl__bccache * const c){
static const fsl__bccache_line line_empty = fsl__bccache_line_empty_m;
fsl_size_t i;
for(i=0; i<c->used; ++i){
fsl_buffer_clear(&c->list[i].content);
c->list[i] = line_empty;
}
c->used = 0;
c->szTotal = 0;
c->nextAge = 0;
fsl_id_bag_reset(&c->missing);
fsl_id_bag_reset(&c->available);
fsl_id_bag_reset(&c->inCache);
}
int fsl__bccache_check_available(fsl_cx * const f, fsl_id_t rid){
fsl_id_t srcid;
int depth = 0; /* Limit to recursion depth */
static const int limit = 10000000 /* historical value */;
int rc;
fsl__bccache * const c = &f->cache.blobContent;
assert(f);
assert(c);
assert(rid>0);
assert(fsl_cx_db_repo(f));
while( depth++ < limit ){
fsl_int_t cSize = -1;
if( fsl_id_bag_contains(&c->missing, rid) ){
return FSL_RC_NOT_FOUND;
}
else if( fsl_id_bag_contains(&c->available, rid) ){
return 0;
}
else if( (cSize=fsl_content_size(f, rid)) <0){
rc = fsl_id_bag_insert(&c->missing, rid);
return rc ? rc : FSL_RC_NOT_FOUND;
}
srcid = 0;
rc = fsl_delta_src_id(f, rid, &srcid);
if(rc) return rc;
else if( srcid==0 ){
rc = fsl_id_bag_insert(&c->available, rid);
return rc ? rc : 0;
}
rid = srcid;
}
assert(!"delta-loop in repository");
return fsl_cx_err_set(f, FSL_RC_CONSISTENCY,
"Serious problem: delta-loop in repository");
}
#undef MARKER