diff options
Diffstat (limited to 'src/mongo/bson/util')
| -rw-r--r-- | src/mongo/bson/util/atomic_int.h | 131 | ||||
| -rw-r--r-- | src/mongo/bson/util/builder.h | 349 | ||||
| -rw-r--r-- | src/mongo/bson/util/misc.h | 129 |
3 files changed, 609 insertions, 0 deletions
diff --git a/src/mongo/bson/util/atomic_int.h b/src/mongo/bson/util/atomic_int.h new file mode 100644 index 00000000000..0b853635795 --- /dev/null +++ b/src/mongo/bson/util/atomic_int.h @@ -0,0 +1,131 @@ +// atomic_int.h +// atomic wrapper for unsigned + +/* 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 + +#if defined(_WIN32) +#include "mongo/platform/windows_basic.h" +#endif + +#include "mongo/platform/compiler.h" + +namespace mongo { + + /** + * An unsigned integer supporting atomic read-modify-write operations. + * + * Many operations on these types depend on natural alignment (4 byte alignment for 4-byte + * words, i.e.). + */ + struct MONGO_COMPILER_ALIGN_TYPE( 4 ) AtomicUInt { + AtomicUInt() : x(0) {} + AtomicUInt(unsigned z) : x(z) { } + + operator unsigned() const { return x; } + unsigned get() const { return x; } + inline void set(unsigned newX); + + inline AtomicUInt operator++(); // ++prefix + inline AtomicUInt operator++(int);// postfix++ + inline AtomicUInt operator--(); // --prefix + inline AtomicUInt operator--(int); // postfix-- + inline void signedAdd(int by); + inline void zero() { set(0); } + volatile unsigned x; + }; + +#if defined(_WIN32) + void AtomicUInt::set(unsigned newX) { + InterlockedExchange((volatile long *)&x, newX); + } + + AtomicUInt AtomicUInt::operator++() { + return InterlockedIncrement((volatile long*)&x); + } + AtomicUInt AtomicUInt::operator++(int) { + return InterlockedIncrement((volatile long*)&x)-1; + } + AtomicUInt AtomicUInt::operator--() { + return InterlockedDecrement((volatile long*)&x); + } + AtomicUInt AtomicUInt::operator--(int) { + return InterlockedDecrement((volatile long*)&x)+1; + } +# if defined(_WIN64) + // don't see an InterlockedAdd for _WIN32...hmmm + void AtomicUInt::signedAdd(int by) { + InterlockedAdd((volatile long *)&x,by); + } +# endif +#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) + // this is in GCC >= 4.1 + inline void AtomicUInt::set(unsigned newX) { __sync_synchronize(); x = newX; } + AtomicUInt AtomicUInt::operator++() { + return __sync_add_and_fetch(&x, 1); + } + AtomicUInt AtomicUInt::operator++(int) { + return __sync_fetch_and_add(&x, 1); + } + AtomicUInt AtomicUInt::operator--() { + return __sync_add_and_fetch(&x, -1); + } + AtomicUInt AtomicUInt::operator--(int) { + return __sync_fetch_and_add(&x, -1); + } + void AtomicUInt::signedAdd(int by) { + __sync_fetch_and_add(&x, by); + } +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + inline void AtomicUInt::set(unsigned newX) { + asm volatile("mfence" ::: "memory"); + x = newX; + } + + // from boost 1.39 interprocess/detail/atomic.hpp + inline unsigned atomic_int_helper(volatile unsigned *x, int val) { + int r; + asm volatile + ( + "lock\n\t" + "xadd %1, %0": + "+m"( *x ), "=r"( r ): // outputs (%0, %1) + "1"( val ): // inputs (%2 == %1) + "memory", "cc" // clobbers + ); + return r; + } + AtomicUInt AtomicUInt::operator++() { + return atomic_int_helper(&x, 1)+1; + } + AtomicUInt AtomicUInt::operator++(int) { + return atomic_int_helper(&x, 1); + } + AtomicUInt AtomicUInt::operator--() { + return atomic_int_helper(&x, -1)-1; + } + AtomicUInt AtomicUInt::operator--(int) { + return atomic_int_helper(&x, -1); + } + void AtomicUInt::signedAdd(int by) { + atomic_int_helper(&x, by); + } +#else +# error "unsupported compiler or platform" +#endif + +} // namespace mongo diff --git a/src/mongo/bson/util/builder.h b/src/mongo/bson/util/builder.h new file mode 100644 index 00000000000..b8027e561a2 --- /dev/null +++ b/src/mongo/bson/util/builder.h @@ -0,0 +1,349 @@ +/* builder.h */ + +/* 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 <cfloat> +#include <iostream> +#include <sstream> +#include <stdio.h> +#include <string> +#include <string.h> + +#include "mongo/bson/inline_decls.h" +#include "mongo/bson/stringdata.h" + +namespace mongo { + /* Accessing unaligned doubles on ARM generates an alignment trap and aborts with SIGBUS on Linux. + Wrapping the double in a packed struct forces gcc to generate code that works with unaligned values too. + The generated code for other architectures (which already allow unaligned accesses) is the same as if + there was a direct pointer access. + */ + struct PackedDouble { + double d; + } PACKED_DECL; + + + /* Note the limit here is rather arbitrary and is simply a standard. generally the code works + with any object that fits in ram. + + Also note that the server has some basic checks to enforce this limit but those checks are not exhaustive + for example need to check for size too big after + update $push (append) operation + various db.eval() type operations + */ + const int BSONObjMaxUserSize = 16 * 1024 * 1024; + + /* + Sometimes we need objects slightly larger - an object in the replication local.oplog + is slightly larger than a user object for example. + */ + const int BSONObjMaxInternalSize = BSONObjMaxUserSize + ( 16 * 1024 ); + + const int BufferMaxSize = 64 * 1024 * 1024; + + void msgasserted(int msgid, const char *msg); + + template <typename Allocator> + class StringBuilderImpl; + + class TrivialAllocator { + public: + void* Malloc(size_t sz) { return malloc(sz); } + void* Realloc(void *p, size_t sz) { return realloc(p, sz); } + void Free(void *p) { free(p); } + }; + + class StackAllocator { + public: + enum { SZ = 512 }; + void* Malloc(size_t sz) { + if( sz <= SZ ) return buf; + return malloc(sz); + } + void* Realloc(void *p, size_t sz) { + if( p == buf ) { + if( sz <= SZ ) return buf; + void *d = malloc(sz); + if ( d == 0 ) + msgasserted( 15912 , "out of memory StackAllocator::Realloc" ); + memcpy(d, p, SZ); + return d; + } + return realloc(p, sz); + } + void Free(void *p) { + if( p != buf ) + free(p); + } + private: + char buf[SZ]; + }; + + template< class Allocator > + class _BufBuilder { + // non-copyable, non-assignable + _BufBuilder( const _BufBuilder& ); + _BufBuilder& operator=( const _BufBuilder& ); + Allocator al; + public: + _BufBuilder(int initsize = 512) : size(initsize) { + if ( size > 0 ) { + data = (char *) al.Malloc(size); + if( data == 0 ) + msgasserted(10000, "out of memory BufBuilder"); + } + else { + data = 0; + } + l = 0; + } + ~_BufBuilder() { kill(); } + + void kill() { + if ( data ) { + al.Free(data); + data = 0; + } + } + + void reset() { + l = 0; + } + void reset( int maxSize ) { + l = 0; + if ( maxSize && size > maxSize ) { + al.Free(data); + data = (char*)al.Malloc(maxSize); + if ( data == 0 ) + msgasserted( 15913 , "out of memory BufBuilder::reset" ); + size = maxSize; + } + } + + /** leave room for some stuff later + @return point to region that was skipped. pointer may change later (on realloc), so for immediate use only + */ + char* skip(int n) { return grow(n); } + + /* note this may be deallocated (realloced) if you keep writing. */ + char* buf() { return data; } + const char* buf() const { return data; } + + /* assume ownership of the buffer - you must then free() it */ + void decouple() { data = 0; } + + void appendUChar(unsigned char j) { + *((unsigned char*)grow(sizeof(unsigned char))) = j; + } + void appendChar(char j) { + *((char*)grow(sizeof(char))) = j; + } + void appendNum(char j) { + *((char*)grow(sizeof(char))) = j; + } + void appendNum(short j) { + *((short*)grow(sizeof(short))) = j; + } + void appendNum(int j) { + *((int*)grow(sizeof(int))) = j; + } + void appendNum(unsigned j) { + *((unsigned*)grow(sizeof(unsigned))) = j; + } + void appendNum(bool j) { + *((bool*)grow(sizeof(bool))) = j; + } + void appendNum(double j) { + (reinterpret_cast< PackedDouble* >(grow(sizeof(double))))->d = j; + } + void appendNum(long long j) { + *((long long*)grow(sizeof(long long))) = j; + } + void appendNum(unsigned long long j) { + *((unsigned long long*)grow(sizeof(unsigned long long))) = j; + } + + void appendBuf(const void *src, size_t len) { + memcpy(grow((int) len), src, len); + } + + template<class T> + void appendStruct(const T& s) { + appendBuf(&s, sizeof(T)); + } + + void appendStr(const StringData &str , bool includeEndingNull = true ) { + const int len = str.size() + ( includeEndingNull ? 1 : 0 ); + memcpy(grow(len), str.data(), len); + } + + /** @return length of current string */ + int len() const { return l; } + void setlen( int newLen ) { l = newLen; } + /** @return size of the buffer */ + int getSize() const { return size; } + + /* returns the pre-grow write position */ + inline char* grow(int by) { + int oldlen = l; + l += by; + if ( l > size ) { + grow_reallocate(); + } + return data + oldlen; + } + + private: + /* "slow" portion of 'grow()' */ + void NOINLINE_DECL grow_reallocate() { + int a = 64; + while( a < l ) + a = a * 2; + if ( a > BufferMaxSize ) { + std::stringstream ss; + ss << "BufBuilder attempted to grow() to " << a << " bytes, past the 64MB limit."; + msgasserted(13548, ss.str().c_str()); + } + data = (char *) al.Realloc(data, a); + if ( data == NULL ) + msgasserted( 16070 , "out of memory BufBuilder::grow_reallocate" ); + size = a; + } + + char *data; + int l; + int size; + + friend class StringBuilderImpl<Allocator>; + }; + + typedef _BufBuilder<TrivialAllocator> BufBuilder; + + /** The StackBufBuilder builds smaller datasets on the stack instead of using malloc. + this can be significantly faster for small bufs. However, you can not decouple() the + buffer with StackBufBuilder. + While designed to be a variable on the stack, if you were to dynamically allocate one, + nothing bad would happen. In fact in some circumstances this might make sense, say, + embedded in some other object. + */ + class StackBufBuilder : public _BufBuilder<StackAllocator> { + public: + StackBufBuilder() : _BufBuilder<StackAllocator>(StackAllocator::SZ) { } + void decouple(); // not allowed. not implemented. + }; + + namespace { +#if defined(_WIN32) + int (*mongo_snprintf)(char *str, size_t size, const char *format, ...) = &sprintf_s; +#else + int (*mongo_snprintf)(char *str, size_t size, const char *format, ...) = &snprintf; +#endif + } + + /** stringstream deals with locale so this is a lot faster than std::stringstream for UTF8 */ + template <typename Allocator> + class StringBuilderImpl { + public: + static const size_t MONGO_DBL_SIZE = 3 + DBL_MANT_DIG - DBL_MIN_EXP; + static const size_t MONGO_S32_SIZE = 12; + static const size_t MONGO_U32_SIZE = 11; + static const size_t MONGO_S64_SIZE = 23; + static const size_t MONGO_U64_SIZE = 22; + static const size_t MONGO_S16_SIZE = 7; + + StringBuilderImpl() { } + + StringBuilderImpl& operator<<( double x ) { + return SBNUM( x , MONGO_DBL_SIZE , "%g" ); + } + StringBuilderImpl& operator<<( int x ) { + return SBNUM( x , MONGO_S32_SIZE , "%d" ); + } + StringBuilderImpl& operator<<( unsigned x ) { + return SBNUM( x , MONGO_U32_SIZE , "%u" ); + } + StringBuilderImpl& operator<<( long x ) { + return SBNUM( x , MONGO_S64_SIZE , "%ld" ); + } + StringBuilderImpl& operator<<( unsigned long x ) { + return SBNUM( x , MONGO_U64_SIZE , "%lu" ); + } + StringBuilderImpl& operator<<( long long x ) { + return SBNUM( x , MONGO_S64_SIZE , "%lld" ); + } + StringBuilderImpl& operator<<( unsigned long long x ) { + return SBNUM( x , MONGO_U64_SIZE , "%llu" ); + } + StringBuilderImpl& operator<<( short x ) { + return SBNUM( x , MONGO_S16_SIZE , "%hd" ); + } + StringBuilderImpl& operator<<( char c ) { + _buf.grow( 1 )[0] = c; + return *this; + } + + void appendDoubleNice( double x ) { + const int prev = _buf.l; + const int maxSize = 32; + char * start = _buf.grow( maxSize ); + int z = mongo_snprintf( start , maxSize , "%.16g" , x ); + verify( z >= 0 ); + verify( z < maxSize ); + _buf.l = prev + z; + if( strchr(start, '.') == 0 && strchr(start, 'E') == 0 && strchr(start, 'N') == 0 ) { + write( ".0" , 2 ); + } + } + + void write( const char* buf, int len) { memcpy( _buf.grow( len ) , buf , len ); } + + void append( const StringData& str ) { memcpy( _buf.grow( str.size() ) , str.data() , str.size() ); } + + StringBuilderImpl& operator<<( const StringData& str ) { + append( str ); + return *this; + } + + void reset( int maxSize = 0 ) { _buf.reset( maxSize ); } + + std::string str() const { return std::string(_buf.data, _buf.l); } + + int len() const { return _buf.l; } + + private: + _BufBuilder<Allocator> _buf; + + // non-copyable, non-assignable + StringBuilderImpl( const StringBuilderImpl& ); + StringBuilderImpl& operator=( const StringBuilderImpl& ); + + template <typename T> + StringBuilderImpl& SBNUM(T val,int maxSize,const char *macro) { + int prev = _buf.l; + int z = mongo_snprintf( _buf.grow(maxSize) , maxSize , macro , (val) ); + verify( z >= 0 ); + verify( z < maxSize ); + _buf.l = prev + z; + return *this; + } + }; + + typedef StringBuilderImpl<TrivialAllocator> StringBuilder; + typedef StringBuilderImpl<StackAllocator> StackStringBuilder; + +} // namespace mongo diff --git a/src/mongo/bson/util/misc.h b/src/mongo/bson/util/misc.h new file mode 100644 index 00000000000..02d68745c99 --- /dev/null +++ b/src/mongo/bson/util/misc.h @@ -0,0 +1,129 @@ +/* @file misc.h +*/ + +/* + * 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 <ctime> +#include <limits> +#include <string> + +#include "mongo/util/assert_util.h" + +namespace mongo { + + inline void time_t_to_String(time_t t, char *buf) { +#if defined(_WIN32) + ctime_s(buf, 32, &t); +#else + ctime_r(&t, buf); +#endif + buf[24] = 0; // don't want the \n + } + + inline std::string time_t_to_String(time_t t = time(0) ) { + char buf[64]; +#if defined(_WIN32) + ctime_s(buf, sizeof(buf), &t); +#else + ctime_r(&t, buf); +#endif + buf[24] = 0; // don't want the \n + return buf; + } + + inline std::string time_t_to_String_no_year(time_t t) { + char buf[64]; +#if defined(_WIN32) + ctime_s(buf, sizeof(buf), &t); +#else + ctime_r(&t, buf); +#endif + buf[19] = 0; + return buf; + } + + inline std::string time_t_to_String_short(time_t t) { + char buf[64]; +#if defined(_WIN32) + ctime_s(buf, sizeof(buf), &t); +#else + ctime_r(&t, buf); +#endif + buf[19] = 0; + if( buf[0] && buf[1] && buf[2] && buf[3] ) + return buf + 4; // skip day of week + return buf; + } + + struct Date_t { + // TODO: make signed (and look for related TODO's) + unsigned long long millis; + Date_t(): millis(0) {} + Date_t(unsigned long long m): millis(m) {} + operator unsigned long long&() { return millis; } + operator const unsigned long long&() const { return millis; } + void toTm (tm *buf) { + time_t dtime = toTimeT(); +#if defined(_WIN32) + gmtime_s(buf, &dtime); +#else + gmtime_r(&dtime, buf); +#endif + } + std::string toString() const { + char buf[64]; + time_t_to_String(toTimeT(), buf); + return buf; + } + time_t toTimeT() const { + // cant use uassert from bson/util + verify((long long)millis >= 0); // TODO when millis is signed, delete + verify(((long long)millis/1000) < (std::numeric_limits<time_t>::max)()); + return millis / 1000; + } + }; + + // Like strlen, but only scans up to n bytes. + // Returns -1 if no '0' found. + inline int strnlen( const char *s, int n ) { + for( int i = 0; i < n; ++i ) + if ( !s[ i ] ) + return i; + return -1; + } + + 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; + } + +} |
