--- a/src/utils/circache.cpp
+++ b/src/utils/circache.cpp
@@ -1,3 +1,19 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
#ifndef lint
static char rcsid[] = "@(#$Id: $ (C) 2009 J.F.Dockes";
#endif
@@ -17,19 +33,20 @@
#include "circache.h"
#include "conftree.h"
+#include "debuglog.h"
using namespace std;
/*
* File structure:
- * - Starts with a 1-KB header block, with a param dictionary, ascii-space
- * filled.
- * - Stored items follow. Each item has 2 segments for the metadata and the
- * data. The segment sizes are stored in an ascii header/marker.
+ * - Starts with a 1-KB header block, with a param dictionary.
+ * - Stored items follow. Each item has a header and 2 segments for
+ * the metadata and the data.
+ * The segment sizes are stored in the ascii header/marker:
* circacheSizes = xxx yyy zzz
* xxx bytes of metadata
* yyy bytes of data
- * zzz bytes of padding up to next object
+ * zzz bytes of padding up to next object (only one entry has non zero)
*
* There is a write position, which can be at eof while
* the file is growing, or inside the file if we are recycling. This is stored
@@ -40,7 +57,7 @@
* pad it with neutral data and store the size in the new header.
*/
-// First block in file.
+// First block size
#define CIRCACHE_FIRSTBLOCK_SIZE 1024
// Entry header.
@@ -111,7 +128,7 @@
// Name for the cache file
string datafn(const string& d)
{
- return path_cat(d, "circache");
+ return path_cat(d, "circache.crch");
}
bool writefirstblock()
@@ -228,17 +245,33 @@
return true;
}
- CCScanHook::status scan(off_t startoffset, CCScanHook *user)
+ CCScanHook::status scan(off_t startoffset, CCScanHook *user,
+ bool fold = false)
{
assert(m_fd >= 0);
+ off_t so0 = startoffset;
+ bool already_folded = false;
+
while (true) {
+ if (already_folded && startoffset == so0)
+ return CCScanHook::Eof;
+
EntryHeaderData d;
CCScanHook::status st;
- if ((st = readentryheader(startoffset, d)) !=
- CCScanHook::Continue) {
+ switch ((st = readentryheader(startoffset, d))) {
+ case CCScanHook::Continue: break;
+ case CCScanHook::Eof:
+ if (fold && !already_folded) {
+ already_folded = true;
+ startoffset = CIRCACHE_FIRSTBLOCK_SIZE;
+ continue;
+ }
+ /* FALLTHROUGH */
+ default:
return st;
}
+
char *bf;
if ((bf = buf(d.dicsize+1)) == 0) {
return CCScanHook::Error;
@@ -296,8 +329,8 @@
struct stat st;
if (stat(m_dir.c_str(), &st) < 0) {
if (mkdir(m_dir.c_str(), 0777) < 0) {
- m_d->m_reason << "CirCache::create: mkdir(" << m_dir << ") failed" <<
- " errno " << errno;
+ m_d->m_reason << "CirCache::create: mkdir(" << m_dir <<
+ ") failed" << " errno " << errno;
return false;
}
}
@@ -317,10 +350,26 @@
memset(buf, 0, CIRCACHE_FIRSTBLOCK_SIZE);
if (::write(m_d->m_fd, buf, CIRCACHE_FIRSTBLOCK_SIZE) !=
CIRCACHE_FIRSTBLOCK_SIZE) {
- m_d->m_reason << "CirCache::create: write header failed, errno " << errno;
+ m_d->m_reason << "CirCache::create: write header failed, errno "
+ << errno;
return false;
}
return m_d->writefirstblock();
+}
+
+bool CirCache::open(OpMode mode)
+{
+ assert(m_d != 0);
+ if (m_d->m_fd >= 0)
+ ::close(m_d->m_fd);
+
+ if ((m_d->m_fd = ::open(m_d->datafn(m_dir).c_str(),
+ mode == CC_OPREAD ? O_RDONLY : O_RDWR)) < 0) {
+ m_d->m_reason << "CirCache::open: open(" << m_d->datafn(m_dir) <<
+ ") failed " << "errno " << errno;
+ return false;
+ }
+ return m_d->readfirstblock();
}
class CCScanHookDump : public CCScanHook {
@@ -328,49 +377,113 @@
virtual status takeone(off_t offs, const string& udi, unsigned int dicsize,
unsigned int datasize, unsigned int padsize)
{
- cout << "udi [" << udi << "] dicsize " << dicsize << " datasize "
- << datasize << " padsize " << padsize << endl;
+ cout << "Scan: offs " << offs << " dicsize " << dicsize
+ << " datasize " << datasize << " padsize " << padsize <<
+ " udi [" << udi << "]" << endl;
return Continue;
}
};
-bool CirCache::open(OpMode mode)
+bool CirCache::dump()
+{
+ CCScanHookDump dumper;
+ off_t start = m_d->m_nheadoffs > CIRCACHE_FIRSTBLOCK_SIZE ?
+ m_d->m_nheadoffs : CIRCACHE_FIRSTBLOCK_SIZE;
+ switch (m_d->scan(start, &dumper, true)) {
+ case CCScanHook::Stop:
+ cout << "Scan returns Stop??" << endl;
+ return false;
+ case CCScanHook::Continue:
+ cout << "Scan returns Continue ?? " << CCScanHook::Continue << " " <<
+ getReason() << endl;
+ return false;
+ case CCScanHook::Error:
+ cout << "Scan returns Error: " << getReason() << endl;
+ return false;
+ case CCScanHook::Eof:
+ cout << "Scan returns Eof" << endl;
+ return true;
+ default:
+ cout << "Scan returns Unknown ??" << endl;
+ return false;
+ }
+}
+
+class CCScanHookGetter : public CCScanHook {
+public:
+ string m_udi;
+ int m_targinstance;
+ int m_instance;
+ off_t m_offs;
+ EntryHeaderData m_hd;
+
+ CCScanHookGetter(const string &udi, int ti)
+ : m_udi(udi), m_targinstance(ti), m_instance(0), m_offs(0){}
+
+ virtual status takeone(off_t offs, const string& udi, unsigned int dicsize,
+ unsigned int datasize, unsigned int padsize)
+ {
+ cerr << "offs " << offs << " udi [" << udi << "] dicsize " << dicsize
+ << " datasize " << datasize << " padsize " << padsize << endl;
+ if (!m_udi.compare(udi)) {
+ m_instance++;
+ m_offs = offs;
+ m_hd.dicsize = dicsize;
+ m_hd.datasize = datasize;
+ m_hd.padsize = padsize;
+ if (m_instance == m_targinstance)
+ return Stop;
+ }
+ return Continue;
+ }
+};
+
+// instance == -1 means get latest. Otherwise specify from 1+
+bool CirCache::get(const string& udi, string& dict, string& data, int instance)
{
assert(m_d != 0);
- if (m_d->m_fd >= 0)
- ::close(m_d->m_fd);
-
- if ((m_d->m_fd = ::open(m_d->datafn(m_dir).c_str(),
- mode == CC_OPREAD ? O_RDONLY : O_RDWR)) < 0) {
- m_d->m_reason << "CirCache::open: open(" << m_d->datafn(m_dir) <<
- ") failed " << "errno " << errno;
- return false;
- }
- bool ret = m_d->readfirstblock();
-
- if (mode == CC_OPREAD) {
- CCScanHookDump dumper;
- switch (m_d->scan(CIRCACHE_FIRSTBLOCK_SIZE, &dumper)) {
- case CCScanHook::Stop:
- cerr << "Scan returns Stop" << endl;
- break;
- case CCScanHook::Continue:
- cerr << "Scan returns Continue ?? " << CCScanHook::Continue << " " <<
- getReason() << endl;
- break;
- case CCScanHook::Error:
- cerr << "Scan returns Error: " << getReason() << endl;
- break;
- case CCScanHook::Eof:
- cerr << "Scan returns Eof" << endl;
- break;
- }
- }
- return ret;
-}
-
-bool CirCache::get(const string& udi, string dic, string data)
-{
+ if (m_d->m_fd < 0) {
+ m_d->m_reason << "CirCache::get: not open";
+ return false;
+ }
+
+ LOGDEB(("CirCache::get: udi [%s], instance\n", udi.c_str(), instance));
+
+ CCScanHookGetter getter(udi, instance);
+ off_t start = m_d->m_nheadoffs > CIRCACHE_FIRSTBLOCK_SIZE ?
+ m_d->m_nheadoffs : CIRCACHE_FIRSTBLOCK_SIZE;
+
+ CCScanHook::status ret = m_d->scan(start, &getter, true);
+ if (ret == CCScanHook::Eof) {
+ if (getter.m_instance == 0)
+ return false;
+ } else if (ret != CCScanHook::Stop) {
+ return false;
+ }
+ off_t offs = getter.m_offs + CIRCACHE_HEADER_SIZE;
+ if (lseek(m_d->m_fd, offs, 0) != offs) {
+ m_d->m_reason << "CirCache::get: lseek(" << offs << ") failed: " <<
+ errno;
+ return false;
+ }
+ char *bf = m_d->buf(getter.m_hd.dicsize);
+ if (bf == 0)
+ return false;
+ if (read(m_d->m_fd, bf, getter.m_hd.dicsize) != int(getter.m_hd.dicsize)) {
+ m_d->m_reason << "CirCache::get: read() failed: errno " << errno;
+ return false;
+ }
+ dict.assign(bf, getter.m_hd.dicsize);
+
+ bf = m_d->buf(getter.m_hd.datasize);
+ if (bf == 0)
+ return false;
+ if (read(m_d->m_fd, bf, getter.m_hd.datasize) != int(getter.m_hd.datasize)){
+ m_d->m_reason << "CirCache::get: read() failed: errno " << errno;
+ return false;
+ }
+ data.assign(bf, getter.m_hd.datasize);
+
return true;
}
@@ -385,8 +498,8 @@
virtual status takeone(off_t offs, const string& udi, unsigned int dicsize,
unsigned int datasize, unsigned int padsize)
{
- cout << "udi [" << udi << "] dicsize " << dicsize << " datasize "
- << datasize << " padsize " << padsize << endl;
+ LOGDEB(("ScanSpacer: offs %u dicsz %u datasz %u padsz %u udi[%s]\n",
+ (unsigned int)offs, dicsize, datasize, padsize, udi.c_str()));
sizeseen += CIRCACHE_HEADER_SIZE + dicsize + datasize + padsize;
if (sizeseen >= sizewanted)
return Stop;
@@ -425,8 +538,8 @@
int npadsize = 0;
bool extending = false;
- cerr << "CirCache::PUT: nsize " << nsize <<
- " oheadoffs " << m_d->m_oheadoffs << endl;
+ LOGDEB2(("CirCache::put: nsize %d oheadoffs %d\n",
+ nsize, m_d->m_oheadoffs));
if (st.st_size < m_d->m_maxsize) {
// If we are still growing the file, things are simple
@@ -450,8 +563,8 @@
return false;
}
assert(int(pd.padsize) == m_d->m_npadsize);
- cerr << "CirCache::put: recovering previous padsize " <<
- pd.padsize << endl;
+ LOGDEB2(("CirCache::put: recovering previous padsize %d\n",
+ pd.padsize));
pd.padsize = 0;
if (!m_d->writeentryheader(m_d->m_nheadoffs, pd)) {
return false;
@@ -463,19 +576,20 @@
if (nsize <= recovpadsize) {
// If the new entry fits entirely in the pad area from the
// latest one, no need to recycle the oldest entries.
- cerr << "CirCache::put: new fits in old padsize " <<
- recovpadsize << endl;
+ LOGDEB2(("CirCache::put: new fits in old padsize %d\n,"
+ recovpadsize));
npadsize = recovpadsize - nsize;
} else {
// Scan the file until we have enough space for the new entry,
// and determine the pad size up to the 1st preserved entry
int scansize = nsize - recovpadsize;
- cerr << "CirCache::put: scanning for size " << scansize <<
- " from offset " << m_d->m_oheadoffs << endl;
+ LOGDEB2(("CirCache::put: scanning for size %d from offs %u\n",
+ scansize, (unsigned int)m_d->m_oheadoffs));
CCScanHookSpacer spacer(scansize);
switch (m_d->scan(m_d->m_oheadoffs, &spacer)) {
case CCScanHook::Stop:
- cerr << "put: Scan ok, sizeseen " << spacer.sizeseen << endl;
+ LOGDEB2(("CirCache::put: Scan ok, sizeseen %d\n",
+ spacer.sizeseen));
npadsize = spacer.sizeseen - scansize;
break;
case CCScanHook::Eof:
@@ -489,8 +603,8 @@
}
}
- cerr << "CirCache::put: writing " << nsize << " at " << nwriteoffs <<
- " padsize " << npadsize << endl;
+ LOGDEB2(("CirCache::put: writing %d at %d padsize %d\n",
+ nsize, nwriteoffs, npadsize));
if (lseek(m_d->m_fd, nwriteoffs, 0) != nwriteoffs) {
m_d->m_reason << "CirCache::put: lseek failed: " << errno;
return false;
@@ -551,6 +665,8 @@
static char usage [] =
" -c <dirname> : create\n"
" -p <dirname> <apath> [apath ...] : put files\n"
+" -d <dirname> : dump\n"
+" -g [-i instance] <dirname> <udi>: get\n"
;
static void
Usage(FILE *fp = stderr)
@@ -562,14 +678,15 @@
static int op_flags;
#define OPT_MOINS 0x1
#define OPT_c 0x2
-#define OPT_b 0x4
#define OPT_p 0x8
#define OPT_g 0x10
+#define OPT_d 0x20
+#define OPT_i 0x40
int main(int argc, char **argv)
{
- int count = 10;
-
+ int instance = -1;
+
thisprog = argv[0];
argc--; argv++;
@@ -583,8 +700,9 @@
case 'c': op_flags |= OPT_c; break;
case 'p': op_flags |= OPT_p; break;
case 'g': op_flags |= OPT_g; break;
- case 'b': op_flags |= OPT_b; if (argc < 2) Usage();
- if ((sscanf(*(++argv), "%d", &count)) != 1)
+ case 'd': op_flags |= OPT_d; break;
+ case 'i': op_flags |= OPT_i; if (argc < 2) Usage();
+ if ((sscanf(*(++argv), "%d", &instance)) != 1)
Usage();
argc--;
goto b1;
@@ -633,10 +751,23 @@
}
cc.open(CirCache::CC_OPREAD);
} else if (op_flags & OPT_g) {
+ string udi = *argv++;argc--;
if (!cc.open(CirCache::CC_OPREAD)) {
cerr << "Open failed: " << cc.getReason() << endl;
exit(1);
}
+ string dic, data;
+ if (!cc.get(udi, dic, data, instance)) {
+ cerr << "Get failed: " << cc.getReason() << endl;
+ exit(1);
+ }
+ cout << "Dict: [" << dic << "]" << endl;
+ } else if (op_flags & OPT_d) {
+ if (!cc.open(CirCache::CC_OPREAD)) {
+ cerr << "Open failed: " << cc.getReason() << endl;
+ exit(1);
+ }
+ cc.dump();
} else
Usage();