/* 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" #include #include #include "xstd/h/os_std.h" #include "xstd/h/process.h" /* for _getpid() on W2K */ #include "xstd/h/iostream.h" #include "xstd/h/sstream.h" #include "xstd/h/iomanip.h" #include "base/ILog.h" #include "base/OLog.h" #include "base/BStream.h" #include "base/UniqId.h" #include "xstd/gadgets.h" static UniqId TheSessionId; static int TheSpaceId = 0; void UniqId::Space(int id) { // we must be called before any ids are generated (weak check) Assert(TheSessionId.theCnt <= 0); Assert(id >= 0); TheSpaceId = id; } UniqId::UniqId(): theSecs(0), theMix(0), theCnt(0) { } UniqId::UniqId(int aSecs, int aMix, int aCnt): theSecs(aSecs), theMix(aMix), theCnt(aCnt) { Assert(aCnt <= 0 || (aSecs > 0 && aMix > 0)); } UniqId &UniqId::create() { if (TheSessionId.theCnt <= 0) RefreshSessionId(); *this = TheSessionId; TheSessionId.theCnt += 2; return *this; } int UniqId::hash() const { return abs(theMix + (theCnt ^ theSecs)); } ostream &UniqId::print(ostream &os) const { const ios_fmtflags flags = os.flags(); const char fill = os.fill('0'); os << hex << setw(8) << theSecs << '.' << setw(8) << theMix << ':' << setw(8) << theCnt; os.fill(fill); os.flags(flags); return os; } OLog &UniqId::store(OLog &ol) const { return ol << theSecs << theMix << theCnt; } ILog &UniqId::load(ILog &il) { return il >> theSecs >> theMix >> theCnt; } OBStream &UniqId::store(OBStream &os) const { return os << theSecs << theMix << theCnt; } IBStream &UniqId::load(IBStream &is) { return is >> theSecs >> theMix >> theCnt; } String UniqId::str() const { char buf[64]; ofixedstream os(buf, sizeof(buf)); print(os) << ends; return buf; } UniqId UniqId::FromStr(const Area &area) { int buf[3] = { 0, 0, 0 }; memcpy(&buf, area.data(), Min(area.size(), (int)sizeof(buf))); // make secs and mix positive buf[0] = buf[0] <= 0 ? buf[0] + INT_MAX : buf[0]; buf[1] = buf[1] <= 0 ? buf[1] + INT_MAX : buf[1]; buf[2] = Min(buf[2] <= 0 ? buf[2] + INT_MAX : buf[2], INT_MAX - 2); // make theCnt even (and still positive) buf[2] /= 2; buf[2] *= 2; buf[2] += 2; return UniqId(buf[0], buf[1], buf[2]); } // it would be better to use hostid in a mix, but gethostid() is not portable // so we use tm.usec instead void UniqId::RefreshSessionId() { if (!TheSpaceId) { // use random factors to reset "session id" static unsigned pid = (unsigned) getpid(); Time tm(Time::Now()); TheSessionId = UniqId( 0x7fffffff & (int)(tm.sec() - 912345678), 0x7fffffff & (int)((tm.usec() << 16) | (pid & 0xffff)), 2); } else { // stay within the specified space TheSessionId = UniqId( TheSpaceId, TheSessionId.theMix+1, 2); } }