// @file namespacestring.h
/**
* Copyright (C) 2008 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
#pragma once
#include
#include "mongo/util/assert_util.h"
namespace mongo {
using std::string;
/* in the mongo source code, "client" means "database". */
const size_t MaxDatabaseNameLen = 128; // max str len for the db name, including null char
/* e.g.
NamespaceString ns("acme.orders");
cout << ns.coll; // "orders"
*/
class NamespaceString {
public:
string db;
string coll; // note collection names can have periods in them for organizing purposes (e.g. "system.indexes")
NamespaceString( const char * ns ) { init(ns); }
NamespaceString( const string& ns ) { init(ns.c_str()); }
string ns() const { return db + '.' + coll; }
bool isSystem() const { return strncmp(coll.c_str(), "system.", 7) == 0; }
bool isCommand() const { return coll == "$cmd"; }
bool isSystemDotIndexes() const { return strncmp(coll.c_str(), "system.indexes", 14) == 0; }
/**
* @return true if the namespace is valid. Special namespaces for internal use are considered as valid.
*/
bool isValid() const {
return validDBName( db ) && !coll.empty();
}
operator string() const { return ns(); }
bool operator==( const string& nsIn ) const { return nsIn == ns(); }
bool operator==( const char* nsIn ) const { return (string)nsIn == ns(); }
bool operator==( const NamespaceString& nsIn ) const { return nsIn.db == db && nsIn.coll == coll; }
bool operator!=( const string& nsIn ) const { return nsIn != ns(); }
bool operator!=( const char* nsIn ) const { return (string)nsIn != ns(); }
bool operator!=( const NamespaceString& nsIn ) const { return nsIn.db != db || nsIn.coll != coll; }
size_t size() const { return ns().size(); }
string toString() const { return ns(); }
/**
* @return true if ns is 'normal'. A "$" is used for namespaces holding index data,
* which do not contain BSON objects in their records. ("oplog.$main" is the exception)
*/
static bool normal(const char* ns) {
const char *p = strchr(ns, '$');
if( p == 0 )
return true;
return oplog(ns);
}
/**
* @return true if the ns is an oplog one, otherwise false.
*/
static bool oplog(const char* ns) {
return StringData(ns) == StringData("local.oplog.rs") || StringData(ns) == StringData("local.oplog.$main");
}
static bool special(const char *ns) {
return !normal(ns) || strstr(ns, ".system.");
}
/**
* samples:
* good:
* foo
* bar
* foo-bar
* bad:
* foo bar
* foo.bar
* foo"bar
*
* @param db - a possible database name
* @return if db is an allowed database name
*/
static bool validDBName( const string& db ) {
if ( db.size() == 0 || db.size() > 64 )
return false;
#ifdef _WIN32
// We prohibit all FAT32-disallowed characters on Windows
size_t good = strcspn( db.c_str() , "/\\. \"*<>:|?" );
#else
// For non-Windows platforms we are much more lenient
size_t good = strcspn( db.c_str() , "/\\. \"" );
#endif
return good == db.size();
}
/**
* samples:
* good:
* foo.bar
* bad:
* foo.
*
* @param dbcoll - a possible collection name of the form db.coll
* @return if db.coll is an allowed collection name
*/
static bool validCollectionName(const char* dbcoll){
const char *c = strchr( dbcoll, '.' );
return (c != NULL) && (c[1] != '\0') && normal(dbcoll);
}
private:
void init(const char *ns) {
const char *p = strchr(ns, '.');
if( p == 0 ) return;
db = string(ns, p - ns);
coll = p + 1;
}
};
// "database.a.b.c" -> "database"
inline StringData nsToDatabaseSubstring( const StringData& ns ) {
size_t i = ns.find( '.' );
if ( i == string::npos ) {
massert(10078, "nsToDatabase: ns too long", ns.size() < MaxDatabaseNameLen );
return ns;
}
massert(10088, "nsToDatabase: ns too long", i < static_cast(MaxDatabaseNameLen));
return ns.substr( 0, i );
}
// "database.a.b.c" -> "database"
inline void nsToDatabase(const StringData& ns, char *database) {
StringData db = nsToDatabaseSubstring( ns );
db.copyTo( database, true );
}
// TODO: make this return a StringData
inline string nsToDatabase(const StringData& ns) {
return nsToDatabaseSubstring( ns ).toString();
}
/**
* NamespaceDBHash and NamespaceDBEquals allow you to do something like
* unordered_map
* and use the full namespace for the string
* but comparisons are done only on the db piece
*/
/**
* this can change, do not store on disk
*/
inline int nsDBHash( const string& ns ) {
int hash = 7;
for ( size_t i = 0; i < ns.size(); i++ ) {
if ( ns[i] == '.' )
break;
hash += 11 * ( ns[i] );
hash *= 3;
}
return hash;
}
inline bool nsDBEquals( const string& a, const string& b ) {
for ( size_t i = 0; i < a.size(); i++ ) {
if ( a[i] == '.' ) {
// b has to either be done or a '.'
if ( b.size() == i )
return true;
if ( b[i] == '.' )
return true;
return false;
}
// a is another character
if ( b.size() == i )
return false;
if ( b[i] != a[i] )
return false;
}
// a is done
// make sure b is done
if ( b.size() == a.size() ||
b[a.size()] == '.' )
return true;
return false;
}
struct NamespaceDBHash {
int operator()( const string& ns ) const {
return nsDBHash( ns );
}
};
struct NamespaceDBEquals {
bool operator()( const string& a, const string& b ) const {
return nsDBEquals( a, b );
}
};
}