summaryrefslogtreecommitdiff
path: root/src/mongo/bson/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/bson/util')
-rw-r--r--src/mongo/bson/util/atomic_int.h131
-rw-r--r--src/mongo/bson/util/builder.h349
-rw-r--r--src/mongo/bson/util/misc.h129
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;
+ }
+
+}