0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00
mongodb/db/jsobj.h
2007-11-24 16:15:09 -05:00

390 lines
9.0 KiB
C++

// jsobj.h
#pragma once
#include "../stdafx.h"
#include "../util/builder.h"
#include <set>
class JSObj;
class Record;
class JSObjBuilder;
#pragma pack(push)
#pragma pack(1)
/* BinData = binary data types.
EOO = end of object
*/
enum JSType { EOO = 0, Number=1, String=2, Object=3, Array=4, BinData=5,
Undefined=6, jstOID=7, Bool=8, Date=9 , jstNULL=10, RegEx=11 ,
DBRef=12, MaxKey=127 };
/* subtypes of BinData.
bdtCustom and above are ones that the JS compiler understands, but are
opaque to the database.
*/
enum BinDataType { Function=1, ByteArray=2, bdtCustom=128 };
/* Object id's are optional for JSObjects.
When present they should be the first object member added.
*/
struct OID {
long long a;
unsigned b;
bool operator==(const OID& r) { return a==r.a&&b==r.b; }
};
/* marshalled js object format:
<unsigned totalSize> {<byte JSType><cstring FieldName><Data>}* EOO
totalSize includes itself.
Data:
Bool: <byte>
EOO: nothing follows
Undefined: nothing follows
OID: an OID object
Number: <double>
String: <unsigned32 strsizewithnull><cstring>
Date: <8bytes>
Regex: <cstring regex><cstring options>
Object: a nested object, leading with its entire size, which terminates with EOO.
Array: same as object
BinData:
<int len>
<byte subtype>
<byte[len] data>
*/
/* db operation message format
unsigned opid; // arbitary; will be echoed back
byte operation;
dbInsert:
int reserved;
string collection;
a series of JSObjects terminated with a null object (i.e., just EOO)
dbUpdate: see query.h
dbDelete: see query.h
dbQuery: see query.h
*/
#pragma pack(pop)
/* <type><fieldName ><value>
-------- size() ------------
-fieldNameSize-
value()
type()
*/
class Element {
friend class JSElemIter;
friend class JSObj;
public:
string toString();
JSType type() { return (JSType) *data; }
bool eoo() { return type() == EOO; }
int size();
// wrap this element up as a singleton object.
JSObj wrap();
const char * fieldName() { return data + 1; }
// raw data be careful:
const char * value() const { return (data + fieldNameSize + 1); }
int valuesize() { return size() - fieldNameSize - 1; }
bool boolean() { return *value() ? true : false; }
unsigned long long date() { return *((unsigned long long*) value()); }
double number() { return *((double *) value()); }
OID& oid() { return *((OID*) value()); }
// for strings
int valuestrsize() {
return *((int *) value());
}
// for objects the size *includes* the size of the size field
int objsize() {
return *((int *) value());
}
// for strings. also gives you start of the real data for an embedded object
const char * valuestr() { return value() + 4; }
JSObj embeddedObject();
const char *regex() { assert(type() == RegEx); return value(); }
const char *regexFlags() {
const char *p = regex();
return p + strlen(p) + 1;
}
bool operator==(Element& r) {
int sz = size();
return sz == r.size() &&
memcmp(data, r.data, sz) == 0;
}
/* like operator== but doesn't check the fieldname,
just the value.
*/
bool valuesEqual(Element& r) {
return valuesize() == r.valuesize() &&
memcmp(value(),r.value(),valuesize()) == 0;
}
const char * rawdata() { return data; }
Element();
private:
Element(const char *d) : data(d) {
fieldNameSize = eoo() ? 0 : strlen(fieldName()) + 1;
totalSize = -1;
}
const char *data;
int fieldNameSize;
int totalSize;
};
class JSObj {
friend class JSElemIter;
public:
explicit
JSObj(const char *msgdata, bool ifree = false) : iFree(ifree) {
_objdata = msgdata;
_objsize = *((int*) _objdata);
}
JSObj(Record *r);
JSObj() : _objsize(0), _objdata(0), iFree(false) { }
~JSObj() { if( iFree ) { free((void*)_objdata); _objdata=0; } }
void iWillFree() {
assert(!iFree); iFree = true;
}
string toString() const;
int addFields(JSObj& from, set<string>& fields); /* returns n added */
int getFieldNames(set<string>& fields);
Element getField(const char *name); /* return has eoo() true if no match */
const char * getStringField(const char *name);
JSObj getObjectField(const char *name);
/* makes a new JSObj with the fields specified in pattern.
fields returned in the order they appear in pattern.
if any field missing, you get back an empty object overall.
*/
JSObj extractFields(JSObj pattern, JSObjBuilder& b);
const char *objdata() const { return _objdata; }
int objsize() const { return _objsize; } // includes the embedded size field
bool isEmpty() const { return objsize() <= 5; }
/* this is broken if elements aren't in the same order. */
bool operator<(const JSObj& r) const { return woCompare(r) < 0; }
/* -1: l<r. 0:l==r. 1:l>r
wo='well ordered'. fields must be in same order in each object.
*/
int woCompare(const JSObj& r) const;
bool woEqual(const JSObj& r) const {
return _objsize==r._objsize && memcmp(_objdata,r._objdata,_objsize)==0;
}
Element firstElement() {
return Element(objdata() + 4);
}
OID* getOID() {
Element e = firstElement();
if( e.type() != jstOID )
return 0;
return &e.oid();
}
JSObj(const JSObj& r) {
_objsize = r._objsize;
_objdata = r._objdata;
iFree = r.iFree;
if( iFree ) {
((JSObj&)r)._objdata = 0;
((JSObj&)r).iFree = false;
}
}
JSObj& operator=(JSObj& r) {
if( iFree ) free((void*)_objdata);
_objsize = r._objsize;
_objdata = r._objdata;
iFree = r.iFree;
/* kind of like auto_ptrs here. note we leave objsize as it was
so you'll get notified if you try to use the object, instead of just
thinking it is empty.
*/
if( iFree ) { r._objdata = 0; r.iFree = false; }
return *this;
}
/* makes a copy of the object. Normally, a jsobj points to data "owned"
by something else. this is a useful way to get your own copy of the buffer
data (which is freed when the new jsobj destructs).
*/
JSObj copy() const;
bool iFree;
private:
int _objsize;
const char *_objdata;
};
class JSObjBuilder {
public:
JSObjBuilder() { b.skip(4); /*leave room for size field*/ }
void append(Element& e) { b.append((void*) e.rawdata(), e.size()); }
/* append an element but with a new name */
void appendAs(Element& e, const char *as) {
b.append((char) e.type());
b.append(as);
b.append((void *) e.value(), e.valuesize());
}
void append(const char *fieldName, double n) {
b.append((char) Number);
b.append(fieldName);
b.append(n);
}
void append(const char *fieldName, const char *str) {
b.append((char) String);
b.append(fieldName);
b.append((int) strlen(str)+1);
b.append(str);
}
JSObj doneAndDecouple() {
int l;
return JSObj(decouple(l));
}
JSObj done() {
return JSObj(_done());
}
/* assume ownership of the buffer - you must then free it (with free()) */
char* decouple(int& l) {
char *x = _done();
l = b.len();
b.decouple();
return x;
}
void decouple() { b.decouple(); } // post done() call version. be sure jsobj frees...
private:
char* _done() {
b.append((char) EOO);
char *data = b.buf();
*((int*)data) = b.len();
return data;
}
BufBuilder b;
};
class JSElemIter {
public:
JSElemIter(const JSObj& jso) {
pos = jso.objdata() + 4;
theend = jso.objdata() + jso.objsize();
}
bool more() { return pos < theend; }
Element next() {
Element e(pos);
pos += e.size();
return e;
}
private:
const char *pos;
const char *theend;
};
#include <pcrecpp.h>
class RegexMatcher {
public:
const char *fieldName;
pcrecpp::RE *re;
RegexMatcher() { re = 0; }
~RegexMatcher() { delete re; }
};
/* For when a js object is a pattern... */
class JSMatcher {
public:
JSMatcher(JSObj& pattern);
bool matches(JSObj& j, bool *deep = 0);
private:
JSObj& jsobj;
vector<Element> toMatch;
int n;
RegexMatcher regexs[4];
int nRegex;
};
extern JSObj maxKey;
/*- just for testing -- */
#pragma pack(push)
#pragma pack(1)
struct JSObj1 {
JSObj1() {
totsize=sizeof(JSObj1);
n = Number; strcpy_s(nname, 5, "abcd"); N = 3.1;
s = String; strcpy_s(sname, 7, "abcdef"); slen = 10;
strcpy_s(sval, 10, "123456789"); eoo = EOO;
}
unsigned totsize;
char n;
char nname[5];
double N;
char s;
char sname[7];
unsigned slen;
char sval[10];
char eoo;
};
#pragma pack(pop)
extern JSObj1 js1;
inline JSObj Element::embeddedObject() {
assert( type()==Object || type()==Array );
return JSObj(value());
}
inline JSObj JSObj::copy() const {
if( _objsize == 0 )
return *this;
char *p = (char*) malloc(_objsize);
memcpy(p, _objdata, _objsize);
return JSObj(p, true);
}
// wrap this element up as a singleton object.
inline JSObj Element::wrap() {
JSObjBuilder b;
b.append(*this);
return b.doneAndDecouple();
}