0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00
mongodb/util/goodies.h

612 lines
16 KiB
C++

// @file goodies.h
// miscellaneous
/* Copyright 2009 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "../bson/util/misc.h"
#include "concurrency/mutex.h"
namespace mongo {
/* @return a dump of the buffer as hex byte ascii output */
string hexdump(const char *data, unsigned len);
/**
* @return if this name has an increasing counter associated, return the value
* otherwise 0
*/
unsigned setThreadName(const char * name);
string getThreadName();
template<class T>
inline string ToString(const T& t) {
stringstream s;
s << t;
return s.str();
}
#if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) && !defined(__openbsd__) && !defined(__sun__)
} // namespace mongo
#include <pthread.h>
#include <execinfo.h>
namespace mongo {
inline pthread_t GetCurrentThreadId() {
return pthread_self();
}
/* use "addr2line -CFe <exe>" to parse. */
inline void printStackTrace( ostream &o = cout ) {
void *b[20];
int size = backtrace(b, 20);
for (int i = 0; i < size; i++)
o << hex << b[i] << dec << ' ';
o << endl;
char **strings;
strings = backtrace_symbols(b, size);
for (int i = 0; i < size; i++)
o << ' ' << strings[i] << '\n';
o.flush();
free (strings);
}
#else
inline void printStackTrace( ostream &o = cout ) { }
#endif
bool isPrime(int n);
int nextPrime(int n);
inline void dumpmemory(const char *data, int len) {
if ( len > 1024 )
len = 1024;
try {
const char *q = data;
const char *p = q;
while ( len > 0 ) {
for ( int i = 0; i < 16; i++ ) {
if ( *p >= 32 && *p <= 126 )
cout << *p;
else
cout << '.';
p++;
}
cout << " ";
p -= 16;
for ( int i = 0; i < 16; i++ )
cout << (unsigned) ((unsigned char)*p++) << ' ';
cout << endl;
len -= 16;
}
}
catch (...) {
}
}
// PRINT(2+2); prints "2+2: 4"
#define MONGO_PRINT(x) cout << #x ": " << (x) << endl
#define PRINT MONGO_PRINT
// PRINTFL; prints file:line
#define MONGO_PRINTFL cout << __FILE__ ":" << __LINE__ << endl
#define PRINTFL MONGO_PRINTFL
#undef assert
#define assert MONGO_assert
struct WrappingInt {
WrappingInt() {
x = 0;
}
WrappingInt(unsigned z) : x(z) { }
unsigned x;
operator unsigned() const {
return x;
}
static int diff(unsigned a, unsigned b) {
return a-b;
}
bool operator<=(WrappingInt r) {
// platform dependent
int df = (r.x - x);
return df >= 0;
}
bool operator>(WrappingInt r) {
return !(r<=*this);
}
};
/*
class DebugMutex : boost::noncopyable {
friend class lock;
mongo::mutex m;
int locked;
public:
DebugMutex() : locked(0); { }
bool isLocked() { return locked; }
};
*/
//typedef scoped_lock lock;
inline bool startsWith(const char *str, const char *prefix) {
size_t l = strlen(prefix);
if ( strlen(str) < l ) return false;
return strncmp(str, prefix, l) == 0;
}
inline bool startsWith(string s, string p) { return startsWith(s.c_str(), p.c_str()); }
inline bool endsWith(const char *p, const char *suffix) {
size_t a = strlen(p);
size_t b = strlen(suffix);
if ( b > a ) return false;
return strcmp(p + a - b, suffix) == 0;
}
inline unsigned long swapEndian(unsigned long x) {
return
((x & 0xff) << 24) |
((x & 0xff00) << 8) |
((x & 0xff0000) >> 8) |
((x & 0xff000000) >> 24);
}
#if defined(BOOST_LITTLE_ENDIAN)
inline unsigned long fixEndian(unsigned long x) {
return x;
}
#else
inline unsigned long fixEndian(unsigned long x) {
return swapEndian(x);
}
#endif
#if !defined(_WIN32)
typedef int HANDLE;
inline void strcpy_s(char *dst, unsigned len, const char *src) {
assert( strlen(src) < len );
strcpy(dst, src);
}
#else
typedef void *HANDLE;
#endif
/* thread local "value" rather than a pointer
good for things which have copy constructors (and the copy constructor is fast enough)
e.g.
ThreadLocalValue<int> myint;
*/
template<class T>
class ThreadLocalValue {
public:
ThreadLocalValue( T def = 0 ) : _default( def ) { }
T get() const {
T * val = _val.get();
if ( val )
return *val;
return _default;
}
void set( const T& i ) {
T *v = _val.get();
if( v ) {
*v = i;
return;
}
v = new T(i);
_val.reset( v );
}
private:
boost::thread_specific_ptr<T> _val;
const T _default;
};
class ProgressMeter : boost::noncopyable {
public:
ProgressMeter( unsigned long long total , int secondsBetween = 3 , int checkInterval = 100 ) {
reset( total , secondsBetween , checkInterval );
}
ProgressMeter() {
_active = 0;
}
// typically you do ProgressMeterHolder
void reset( unsigned long long total , int secondsBetween = 3 , int checkInterval = 100 ) {
_total = total;
_secondsBetween = secondsBetween;
_checkInterval = checkInterval;
_done = 0;
_hits = 0;
_lastTime = (int)time(0);
_active = 1;
}
void finished() {
_active = 0;
}
bool isActive() {
return _active;
}
/**
* @param n how far along we are relative to the total # we set in CurOp::setMessage
* @return if row was printed
*/
bool hit( int n = 1 ) {
if ( ! _active ) {
cout << "warning: hit on in-active ProgressMeter" << endl;
return false;
}
_done += n;
_hits++;
if ( _hits % _checkInterval )
return false;
int t = (int) time(0);
if ( t - _lastTime < _secondsBetween )
return false;
if ( _total > 0 ) {
int per = (int)( ( (double)_done * 100.0 ) / (double)_total );
cout << "\t\t" << _done << "/" << _total << "\t" << per << "%" << endl;
}
_lastTime = t;
return true;
}
void setTotalWhileRunning( unsigned long long total ) {
_total = total;
}
unsigned long long done() const { return _done; }
unsigned long long hits() const { return _hits; }
unsigned long long total() const { return _total; }
string toString() const {
if ( ! _active )
return "";
stringstream buf;
buf << _done << "/" << _total << " " << (_done*100)/_total << "%";
return buf.str();
}
bool operator==( const ProgressMeter& other ) const {
return this == &other;
}
private:
bool _active;
unsigned long long _total;
int _secondsBetween;
int _checkInterval;
unsigned long long _done;
unsigned long long _hits;
int _lastTime;
};
// e.g.:
// CurOp * op = cc().curop();
// ProgressMeterHolder pm( op->setMessage( "index: (1/3) external sort" , d->stats.nrecords , 10 ) );
// loop { pm.hit(); }
class ProgressMeterHolder : boost::noncopyable {
public:
ProgressMeterHolder( ProgressMeter& pm )
: _pm( pm ) {
}
~ProgressMeterHolder() {
_pm.finished();
}
ProgressMeter* operator->() {
return &_pm;
}
bool hit( int n = 1 ) {
return _pm.hit( n );
}
void finished() {
_pm.finished();
}
bool operator==( const ProgressMeter& other ) {
return _pm == other;
}
private:
ProgressMeter& _pm;
};
class TicketHolder {
public:
TicketHolder( int num ) : _mutex("TicketHolder") {
_outof = num;
_num = num;
}
bool tryAcquire() {
scoped_lock lk( _mutex );
if ( _num <= 0 ) {
if ( _num < 0 ) {
cerr << "DISASTER! in TicketHolder" << endl;
}
return false;
}
_num--;
return true;
}
void release() {
scoped_lock lk( _mutex );
_num++;
}
void resize( int newSize ) {
scoped_lock lk( _mutex );
int used = _outof - _num;
if ( used > newSize ) {
cout << "ERROR: can't resize since we're using (" << used << ") more than newSize(" << newSize << ")" << endl;
return;
}
_outof = newSize;
_num = _outof - used;
}
int available() const {
return _num;
}
int used() const {
return _outof - _num;
}
int outof() const { return _outof; }
private:
int _outof;
int _num;
mongo::mutex _mutex;
};
class TicketHolderReleaser {
public:
TicketHolderReleaser( TicketHolder * holder ) {
_holder = holder;
}
~TicketHolderReleaser() {
_holder->release();
}
private:
TicketHolder * _holder;
};
/**
* this is a thread safe string
* you will never get a bad pointer, though data may be mungedd
*/
class ThreadSafeString {
public:
ThreadSafeString( size_t size=256 )
: _size( size ) , _buf( new char[size] ) {
memset( _buf , 0 , _size );
}
ThreadSafeString( const ThreadSafeString& other )
: _size( other._size ) , _buf( new char[_size] ) {
strncpy( _buf , other._buf , _size );
}
~ThreadSafeString() {
delete[] _buf;
_buf = 0;
}
string toString() const {
string s = _buf;
return s;
}
ThreadSafeString& operator=( const char * str ) {
size_t s = strlen(str);
if ( s >= _size - 2 )
s = _size - 2;
strncpy( _buf , str , s );
_buf[s] = 0;
return *this;
}
bool operator==( const ThreadSafeString& other ) const {
return strcmp( _buf , other._buf ) == 0;
}
bool operator==( const char * str ) const {
return strcmp( _buf , str ) == 0;
}
bool operator!=( const char * str ) const {
return strcmp( _buf , str ) != 0;
}
bool empty() const {
return _buf == 0 || _buf[0] == 0;
}
private:
size_t _size;
char * _buf;
};
ostream& operator<<( ostream &s, const ThreadSafeString &o );
inline bool isNumber( char c ) {
return c >= '0' && c <= '9';
}
inline unsigned stringToNum(const char *str) {
unsigned x = 0;
const char *p = str;
while( 1 ) {
if( !isNumber(*p) ) {
if( *p == 0 && p != str )
break;
throw 0;
}
x = x * 10 + *p++ - '0';
}
return x;
}
// for convenience, '{' is greater than anything and stops number parsing
inline int lexNumCmp( const char *s1, const char *s2 ) {
//cout << "START : " << s1 << "\t" << s2 << endl;
while( *s1 && *s2 ) {
bool p1 = ( *s1 == (char)255 );
bool p2 = ( *s2 == (char)255 );
//cout << "\t\t " << p1 << "\t" << p2 << endl;
if ( p1 && !p2 )
return 1;
if ( p2 && !p1 )
return -1;
bool n1 = isNumber( *s1 );
bool n2 = isNumber( *s2 );
if ( n1 && n2 ) {
// get rid of leading 0s
while ( *s1 == '0' ) s1++;
while ( *s2 == '0' ) s2++;
char * e1 = (char*)s1;
char * e2 = (char*)s2;
// find length
// if end of string, will break immediately ('\0')
while ( isNumber (*e1) ) e1++;
while ( isNumber (*e2) ) e2++;
int len1 = (int)(e1-s1);
int len2 = (int)(e2-s2);
int result;
// if one is longer than the other, return
if ( len1 > len2 ) {
return 1;
}
else if ( len2 > len1 ) {
return -1;
}
// if the lengths are equal, just strcmp
else if ( (result = strncmp(s1, s2, len1)) != 0 ) {
return result;
}
// otherwise, the numbers are equal
s1 = e1;
s2 = e2;
continue;
}
if ( n1 )
return 1;
if ( n2 )
return -1;
if ( *s1 > *s2 )
return 1;
if ( *s2 > *s1 )
return -1;
s1++; s2++;
}
if ( *s1 )
return 1;
if ( *s2 )
return -1;
return 0;
}
/** A generic pointer type for function arguments.
* It will convert from any pointer type except auto_ptr.
* Semantics are the same as passing the pointer returned from get()
* const ptr<T> => T * const
* ptr<const T> => T const * or const T*
*/
template <typename T>
struct ptr {
ptr() : _p(NULL) {}
// convert to ptr<T>
ptr(T* p) : _p(p) {} // needed for NULL
template<typename U> ptr(U* p) : _p(p) {}
template<typename U> ptr(const ptr<U>& p) : _p(p) {}
template<typename U> ptr(const boost::shared_ptr<U>& p) : _p(p.get()) {}
template<typename U> ptr(const boost::scoped_ptr<U>& p) : _p(p.get()) {}
//template<typename U> ptr(const auto_ptr<U>& p) : _p(p.get()) {}
// assign to ptr<T>
ptr& operator= (T* p) { _p = p; return *this; } // needed for NULL
template<typename U> ptr& operator= (U* p) { _p = p; return *this; }
template<typename U> ptr& operator= (const ptr<U>& p) { _p = p; return *this; }
template<typename U> ptr& operator= (const boost::shared_ptr<U>& p) { _p = p.get(); return *this; }
template<typename U> ptr& operator= (const boost::scoped_ptr<U>& p) { _p = p.get(); return *this; }
//template<typename U> ptr& operator= (const auto_ptr<U>& p) { _p = p.get(); return *this; }
// use
T* operator->() const { return _p; }
T& operator*() const { return *_p; }
// convert from ptr<T>
operator T* () const { return _p; }
private:
T* _p;
};
/** Hmmmm */
using namespace boost;
} // namespace mongo