/* Web Polygraph http://www.web-polygraph.org/
* (C) 2003-2006 The Measurement Factory
* Licensed under the Apache License, Version 2.0 */
#include "base/polygraph.h"
#ifdef HAVE_UNISTD_H
#include "xstd/h/os_std.h"
#endif
#include "xstd/h/string.h"
#include "base/OLog.h"
#include "xstd/gadgets.h"
OLog::OLog(): theStream(0), theBuf(0), theEntry(0), theEntryTag(-1) {
theCapacity = Size::KB(64); // default
theBuf = new char[theCapacity];
theSize = 0;
thePos = 0;
}
OLog::~OLog() {
if (theStream)
close();
delete[] theBuf;
}
void OLog::stream(const String &aName, ostream *aStream) {
Assert(!theStream && aStream);
theName = aName;
theStream = aStream;
putHeader();
}
void OLog::capacity(Size aCap) {
Assert(theCapacity);
Assert(aCap >= theCapacity); // cannot shrink
if (aCap > theCapacity) {
theCapacity = aCap;
// allocate new chunk and copy
char *buf = new char[theCapacity];
if (theSize)
memcpy(buf, theBuf, theSize);
if (theEntry)
theEntry = buf + (theEntry - theBuf);
delete[] theBuf;
theBuf = buf;
}
}
void OLog::resize(Size minCap) {
Assert(minCap >= theCapacity); // cannot shrink
// exponential growth
Size newCap = theCapacity;
while (newCap < minCap)
newCap *= 2;
capacity(newCap);
}
void OLog::close() {
if (theStream) {
// abort unfinished entry, if any
if (theEntry) {
const Size readySize = (Size)(theEntry - theBuf);
thePos -= theSize - readySize;
theSize = readySize;
theEntry = 0;
}
putTrailer();
flush();
delete theStream;
theStream = 0;
}
}
void OLog::flush(Size maxSize) {
if (!theStream)
return;
// do not flush current entry if any; its size is yet unknown
const Size readySz = Min(maxSize,
theEntry ? (Size)(theEntry - theBuf) : theSize);
if (readySz > 0) {
Should(theStream->write(theBuf, readySz));
if (theSize > readySz) {
// move leftovers to the beginning of a buffer
theSize -= readySz;
memmove(theBuf, theBuf + readySz, theSize);
} else {
Assert(theSize == readySz); // wrote everything
theSize = 0;
}
if (theEntry)
theEntry -= readySz;
}
}
void OLog::overflow(const void *buf, Size size) {
// buffer what fits
int space = theCapacity - theSize;
if (space > 0) {
put(buf, space);
buf = ((const char*)buf) + space;
size -= space;
}
flush();
// grow if does not fit
space = theCapacity - theSize;
if (space < size)
resize(theSize + size);
Assert(theSize + size <= theCapacity);
put(buf, size);
}
void OLog::begEntry(int tag) {
Assert(tag > 0);
Assert(!theEntry);
theEntry = theBuf + theSize;
theEntryTag = tag;
puti(0); // size placeholder
puti(tag);
}
void OLog::endEntry() {
Assert(theEntry);
Assert(theEntryTag > 0);
const int x = htonl((theBuf + theSize) - theEntry);
memcpy(theEntry, &x, sizeof(x)); // record actual entry size
// update directory if needed
if (theDir.count() > theEntryTag) { // old tag
if (!theDir[theEntryTag])
theDir[theEntryTag] = thePos;
else
if (theDir[theEntryTag] > 0)
theDir[theEntryTag] = -theDir[theEntryTag];
} else {
theDir.put(thePos, theEntryTag);
}
theEntryTag = -1;
theEntry = 0;
}
void OLog::putHeader() {
puti(11); // current version
puti(11); // required min version to support
puti(0); // size of extra headers
}
void OLog::putTrailer() {
puti(0); // mark end-of-log
puti(0); // reserved
const Size dirPos = thePos;
(*this) << theDir;
puti(thePos - dirPos); // dir size
}
void OLog::puti(const int *xs, int count) {
puti(count);
for (int i = 0; i < count; ++i)
puti(xs[i]);
}
syntax highlighted by Code2HTML, v. 0.9.1