/* -*- 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/fossil-internal.h"
#include <assert.h>
bool fsl_acache_expire_oldest(fsl_acache * c){
uint16_t i;
fsl_int_t mnAge = c->nextAge;
uint16_t mn = 0xFFFF;
for(i=0; i<(fsl_int_t)c->used; i++){
if( c->list[i].age<mnAge ){
mnAge = c->list[i].age;
mn = i;
}
}
if( mn<0xFFFF ){
fsl_id_bag_remove(&c->inCache, c->list[mn].rid);
c->szTotal -= (fsl_int_t)c->list[mn].content.used;
fsl_buffer_clear(&c->list[mn].content);
--c->used;
c->list[mn] = c->list[c->used];
}
return (mn>=0) ? true : false;
}
int fsl_acache_insert(fsl_acache * c, fsl_id_t rid, fsl_buffer *pBlob){
static const fsl_size_t memLimit = 50000000 /* historical value */;
static const uint16_t countLimit = 500 /* historical value */;
fsl_acache_line *p;
if( c->used>countLimit || c->szTotal>memLimit ){
fsl_size_t szBefore;
do{
szBefore = c->szTotal;
fsl_acache_expire_oldest(c);
}while( c->szTotal>memLimit && c->szTotal<szBefore );
}
if( c->used>=c->capacity ){
uint16_t const cap = c->capacity ? (c->capacity*2) : 10;
void * remem = fsl_realloc(c->list, cap*sizeof(c->list[0]));
assert(cap>c->capacity && "Numeric overflow");
if(cap>c->capacity){
fsl_fatal(FSL_RC_RANGE,"Numeric overflow. Bump "
"fsl_acache::capacity to a larger int type.");
}
if(!remem){
fsl_buffer_clear(pBlob) /* for consistency */;
return FSL_RC_OOM;
}
c->capacity = cap;
c->list = (fsl_acache_line*)remem;
}
p = &c->list[c->used++];
p->rid = rid;
p->age = c->nextAge++;
c->szTotal += pBlob->used;
p->content = *pBlob /* Transfer ownership */;
*pBlob = fsl_buffer_empty;
return fsl_id_bag_insert(&c->inCache, rid);
}
void fsl_acache_clear(fsl_acache * c){
#if 0
while(fsl_acache_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_acache_empty;
}
int fsl_acache_check_available(fsl_cx * 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_acache * c = &f->cache.arty;
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");
}