first commit

This commit is contained in:
Dwight 2007-10-19 19:35:48 -04:00
commit e73188b551
30 changed files with 2163 additions and 0 deletions

11
.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
*~
*.o
*.aps
*.tar.gz
*.suo
*.ncb
*.idb
*.obj
*.opt
*.pch
db/Debug

BIN
db/db Normal file

Binary file not shown.

269
db/db.cpp Normal file
View File

@ -0,0 +1,269 @@
// db.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "db.h"
#include "../grid/message.h"
#include "../util/mmap.h"
#include "../util/hashtab.h"
#include "pdfile.h"
#include "jsobj.h"
#include "query.h"
struct MyStartupTests {
MyStartupTests() {
assert( sizeof(OID) == 12 );
}
} mystartupdbcpp;
/* example for
var zz = { x: 3, y: "foo", v: { z:5, arr: [1, 2] } }
zz.v.arr.prop = "zoo";
*/
void quicktest() {
cout << "quicktest\n";
MemoryMappedFile mmf;
char *m = (char *) mmf.map("/tmp/abc", 16384);
// cout << "mmf reads: " << m << endl;
strcpy_s(m, 1000, "hello worldz");
}
MessagingPort dbMsgPort;
void pdfileInit();
class DbMessage {
public:
DbMessage(Message& _m) : m(_m) {
int *r = (int *) _m.data;
reserved = *r;
r++;
data = (const char *) r;
nextjsobj = data;
}
const char * getns() { return data; }
void getns(Namespace& ns) {
ns = data;
}
OID* getOID() {
return (OID *) (data + strlen(data) + 1); // skip namespace
}
void getQueryStuff(const char *&query, int& ntoreturn) {
int *i = (int *) (data + strlen(data) + 1);
ntoreturn = *i;
i++;
query = (const char *) i;
}
/* for insert and update msgs */
bool moreJSObjs() { return nextjsobj != 0; }
JSObj nextJsObj() {
if( nextjsobj == data )
nextjsobj += strlen(data) + 1; // skip namespace
JSObj js(nextjsobj);
if( js.size <= 4 )
nextjsobj = null;
else
nextjsobj += js.size;
return js;
}
private:
Message& m;
int reserved;
const char *data;
const char *nextjsobj;
};
Record* findByOID(const char *ns, OID *oid) {
// temp implementation
Cursor c = theDataFileMgr.findAll(ns);
while( c.ok() ) {
Record *r = c.current();
JSObj js(r);
OID *i = js.getOID();
if( i && *oid == *i )
return r;
c.advance();
}
return 0;
}
void updateByOID(const char *ns, char *objdata, int objsize, OID *oid) {
Record *r = findByOID(ns, oid);
if( r == 0 ) {
cout << "updateByOID: no such record " << ns << endl;
return;
}
if( objsize > r->netLength() ) {
cout << "ERROR: updateByOID: growing records not implemented yet." << endl;
return;
}
/* note: need to be smarter if it gets a lot smaller??? */
/* this really dumb for now as it gets smaller but doesn't allow regrowth
to the original size! */
memcpy(r->data, objdata, objsize);
r->setNewLength(objsize);
}
#pragma pack(push)
#pragma pack(1)
struct EmptyObject {
EmptyObject() { len = 5; jstype = EOO; }
int len;
char jstype;
} emptyObject;
#pragma pack(pop)
void query(Message& m) {
DbMessage d(m);
const char *query;
int ntoreturn;
d.getQueryStuff(query, ntoreturn);
QueryResult* msgdata = runQuery(d.getns(), query, ntoreturn);
Message resp;
resp.setData(msgdata, true);
dbMsgPort.reply(m, resp);
}
void getbyoid(Message& m) {
DbMessage d(m);
Record *r = findByOID(d.getns(), d.getOID());
Message resp;
if( r == 0 )
resp.setData(opReply, (char *) &emptyObject, emptyObject.len);
else
resp.setData(opReply, r->data, r->netLength());
dbMsgPort.reply(m, resp);
}
void dbinsert(Message& m) {
DbMessage d(m);
while( d.moreJSObjs() ) {
JSObj js = d.nextJsObj();
if( m.data->operation == dbInsert ) {
theDataFileMgr.insert(d.getns(), (void*) js.objdata(), js.objsize());
} else {
// update
OID *oid = js.getOID();
if( oid == null )
cout << "error: no oid on update -- that isn't coded yet" << endl;
else
updateByOID(d.getns(), (char *) js.objdata(), js.objsize(), oid);
}
}
}
void run() {
dbMsgPort.init(MessagingPort::DBPort);
pdfileInit();
theDataFileMgr.insert("sys.unittest.pdfile", "hello world", 12);
cout << "findAll:\n";
Cursor c = theDataFileMgr.findAll("sys.unittest.pdfile");
while( c.ok() ) {
Record* r = c.current();
cout << " gotrec " << r->netLength() << ' ' <<
r->data << '\n';
c.advance();
}
cout << endl;
Message m;
while( 1 ) {
cout << "waiting for msg..." << endl;
m.reset();
if( !dbMsgPort.recv(m) ) {
cout << "recv() returned false" << endl;
break;
}
cout << " got msg" << endl;
cout << " op:" << m.data->operation << " len:" << m.data->len << endl;
if( m.data->operation == dbMsg ) {
bool end = strcmp("end", m.data->_data) == 0;
Message resp;
resp.setData(opReply, "i am fine");
dbMsgPort.reply(m, resp);
if( end ) {
cout << " end msg" << endl;
break;
}
}
else if( m.data->operation == dbUpdate || dbInsert ) {
dbinsert(m);
}
else if( m.data->operation == dbGetByOID ) {
getbyoid(m);
}
else if( m.data->operation == dbQuery ) {
query(m);
}
else if( m.data->operation == dbGetMore ) {
cout << "dbGetMore: not implemented!" << endl;
}
else {
cout << " operation isn't supported (???)" << endl;
}
}
}
void msg(const char *m) {
MessagingPort p;
p.init(29999);
SockAddr db("127.0.0.1", MessagingPort::DBPort);
Message send;
Message response;
send.setData(1000, m);
cout << "contacting DB..." << endl;
bool ok = p.call(db, send, response);
cout << "ok: " << ok << endl;
cout << " " << response.data->id << endl;
cout << " " << response.data->len << endl;
cout << " " << response.data->operation << endl;
cout << " " << response.data->reserved << endl;
cout << " " << response.data->responseTo << endl;
cout << " " << response.data->_data << endl;
}
int main(int argc, char* argv[], char *envp[] )
{
quicktest();
if( argc >= 2 ) {
if( strcmp(argv[1], "quicktest") == 0 )
return 0;
if( strcmp(argv[1], "msg") == 0 ) {
msg(argc >= 3 ? argv[2] : "ping");
return 0;
}
if( strcmp(argv[1], "run") == 0 ) {
run();
return 0;
}
}
cout << "usage:\n";
cout << " quicktest just check basic assertions and exit" << endl;
cout << " msg [msg] send a request to the db server" << endl;
cout << " msg end shut down" << endl;
cout << " run run db" << endl;
return 0;
}
//#if !defined(_WIN32)
//int main( int argc, char *argv[], char *envp[] ) {
// return _tmain(argc, 0);
//}
//#endif

2
db/db.h Normal file
View File

@ -0,0 +1,2 @@
#include "../stdafx.h"

61
db/db.rc Normal file
View File

@ -0,0 +1,61 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE 9, 1
#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

20
db/db.sln Normal file
View File

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual C++ Express 2008
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "db", "db.vcproj", "{215B2D68-0A70-4D10-8E75-B31010C62A91}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{215B2D68-0A70-4D10-8E75-B31010C62A91}.Debug|Win32.ActiveCfg = Debug|Win32
{215B2D68-0A70-4D10-8E75-B31010C62A91}.Debug|Win32.Build.0 = Debug|Win32
{215B2D68-0A70-4D10-8E75-B31010C62A91}.Release|Win32.ActiveCfg = Release|Win32
{215B2D68-0A70-4D10-8E75-B31010C62A91}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

290
db/db.vcproj Normal file
View File

@ -0,0 +1,290 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="db"
ProjectGUID="{215B2D68-0A70-4D10-8E75-B31010C62A91}"
RootNamespace="db"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="2"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\db.cpp"
>
</File>
<File
RelativePath="..\grid\message.cpp"
>
</File>
<File
RelativePath="..\util\mmap.cpp"
>
</File>
<File
RelativePath=".\pdfile.cpp"
>
</File>
<File
RelativePath=".\query.cpp"
>
</File>
<File
RelativePath="..\util\sock.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\util\builder.h"
>
</File>
<File
RelativePath=".\db.h"
>
</File>
<File
RelativePath="..\util\hashtab.h"
>
</File>
<File
RelativePath=".\jsobj.h"
>
</File>
<File
RelativePath="..\grid\message.h"
>
</File>
<File
RelativePath="..\util\mmap.h"
>
</File>
<File
RelativePath=".\pdfile.h"
>
</File>
<File
RelativePath=".\query.h"
>
</File>
<File
RelativePath=".\resource.h"
>
</File>
<File
RelativePath="..\util\sock.h"
>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
<File
RelativePath=".\storage.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
<File
RelativePath=".\db.rc"
>
</File>
<File
RelativePath="..\targetver.h"
>
</File>
</Filter>
<Filter
Name="misc"
>
<File
RelativePath=".\makefile"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioUserFile
ProjectType="Visual C++"
Version="9.00"
ShowAllFiles="false"
>
<Configurations>
<Configuration
Name="Debug|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments="run"
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DWIGHT-PC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor="0"
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
<Configuration
Name="Release|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DWIGHT-PC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
</Configurations>
</VisualStudioUserFile>

118
db/jsobj.h Normal file
View File

@ -0,0 +1,118 @@
// jsobj.h
#include "../stdafx.h"
#include "pdfile.h"
#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 };
/* 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><string FieldName><Data>}* EOO
totalSize includes itself.
Data:
EOO: nothing follows
Undefined: nothing follows
OID: an OID object
Number: <double>
String: <unsigned strlen><string>
Object: a nested object, which terminates with EOO.
Array:
<unsigned length>
{Object}[length]
a nested object, which is the object properties of the array
BinData:
<byte subtype>
<unsigned len>
<byte[len] data>
*/
/* db operation message format
unsigned opid; // arbitary; will be echoed back
byte operation;
Update:
int reserved;
string collection; // name of the collection (namespace)
a series of JSObjects terminated with a null object (i.e., just EOO)
Insert:
int reserved;
string collection;
a series of JSObjects terminated with a null object (i.e., just EOO)
GetByOID:
int reserved;
string collection;
OID oid;
Query:
int reserved;
string collection;
unsigned nToReturn; // how many you want back as the beginning of the cursor data
string query;
GetMore:
int reserved;
unsigned cursorID;
unsigned nToReturn;
byte EOM
*/
/* db response format
GetByOID operation:
marshalled JSObject returned. always specified, even if an error.
Query or GetMore: see query.h
int reserved;
unsigned cursorID;
unsigned startOfs;
unsigned nReturned;
list of marshalled JSObjects;
*/
#pragma pack(pop)
class JSObj {
public:
JSObj(const char *_data) : data(_data) {
size = *((int*) data);
}
JSObj(Record *r) {
size = r->netLength();
data = r->data;
}
const char *objdata() { return data + 4; } // skip the length field.
int objsize() { return size - 4; }
OID* getOID() {
const char *p = objdata();
if( *p != jstOID )
return 0;
return (OID *) ++p;
}
int size;
const char *data;
};

20
db/makefile Normal file
View File

@ -0,0 +1,20 @@
# makefile for our db project
FLAGS=-I ..
OBJS=../stdafx.o ../util/sock.o ../grid/message.o ../util/mmap.o pdfile.o
.cpp.o:
g++ -c $(FLAGS) $< -o $@
# Our convention is that passing 'quicktest' on the command line means run
# fast regressions and then immediately exit. That way you know immediately if you
# broke something horribly.
db: $(OBJS) db.o
g++ $(FLAGS) -o $@ $(OBJS) db.o
./db quicktest
clean:
-rm -f $(OBJS) db.o
-rm -f db

0
db/makefile.txt Normal file
View File

166
db/pdfile.cpp Normal file
View File

@ -0,0 +1,166 @@
// pdfile.cpp
#include "stdafx.h"
#include "pdfile.h"
#include "db.h"
#include "../util/mmap.h"
#include "../util/hashtab.h"
#include <map>
#include <string>
DataFileMgr theDataFileMgr;
/* just temporary */
const int ExtentSize = 1024 * 1024;
/*---------------------------------------------------------------------*/
class NamespaceIndex {
public:
NamespaceIndex() { }
void init() {
const int LEN = 16 * 1024 * 1024;
void *p = f.map("/data/namespace.idx", LEN);
ht = new HashTable<Namespace,DiskLoc>(p, LEN, "namespace index");
}
void add(const char *ns, DiskLoc& loc) {
Namespace n(ns);
ht->put(n, loc);
}
bool find(const char *ns, DiskLoc& loc) {
Namespace n(ns);
DiskLoc *l = ht->get(n);
if( l ) {
loc = *l;
return true;
}
return false;
}
private:
MemoryMappedFile f;
HashTable<Namespace,DiskLoc> *ht;
} namespaceIndex;
/*---------------------------------------------------------------------*/
void PhysicalDataFile::open(const char *filename, int length) {
header = (PDFHeader *) mmf.map(filename, length);
assert(header);
header->init(length);
}
Extent* PhysicalDataFile::newExtent(const char *ns, DiskLoc& loc) {
int left = header->unusedLength - ExtentSize;
if( left < 0 ) {
cout << "ERROR: newExtent: no more room for extents. write more code" << endl;
assert(false);
exit(2);
}
int offset = header->unused.getOfs();
header->unused.setOfs( offset + ExtentSize );
header->unusedLength -= ExtentSize;
loc.setOfs(offset);
Extent *e = _getExtent(loc);
e->init(ns, ExtentSize, offset);
return e;
}
/*---------------------------------------------------------------------*/
/* assumes already zeroed -- insufficient for block 'reuse' perhaps */
void Extent::init(const char *nsname, int _length, int _offset) {
magic = 0x41424344;
myLoc.setOfs(_offset);
ns = nsname;
length = _length;
firstRecord.Null(); lastRecord.Null();
firstEmptyRegion = myLoc;
firstEmptyRegion.inc( (extentData-(char*)this) );
Record *empty1 = (Record *) extentData;
Record *empty = getRecord(firstEmptyRegion);
assert( empty == empty1 );
empty->lengthWithHeaders = _length - (extentData - (char *) this);
empty->next.Null();
}
Record* Extent::newRecord(int len) {
if( firstEmptyRegion.isNull() )
return 0;
assert(len > 0);
int newRecSize = len + Record::HeaderSize;
DiskLoc newRecordLoc = firstEmptyRegion;
Record *r = getRecord(newRecordLoc);
int left = r->netLength() - len;
if( left < 0 ) {
/* this might be wasteful if huge variance in record sizes in a namespace */
firstEmptyRegion.Null();
return 0;
}
DiskLoc nextEmpty = r->next;
r->lengthWithHeaders = newRecSize;
r->next.Null();
if( !lastRecord.isNull() ) {
assert(getRecord(lastRecord)->next.isNull());
getRecord(lastRecord)->next = newRecordLoc;
}
lastRecord = newRecordLoc;
if( firstRecord.isNull() )
firstRecord = newRecordLoc;
if( left < Record::HeaderSize + 32 ) {
firstEmptyRegion.Null();
}
else {
firstEmptyRegion.inc(newRecSize);
Record *empty = getRecord(firstEmptyRegion);
empty->next = nextEmpty;
empty->lengthWithHeaders = left;
}
return r;
}
/*---------------------------------------------------------------------*/
Cursor DataFileMgr::findAll(const char *ns) {
DiskLoc loc;
bool found = namespaceIndex.find(ns, loc);
if( !found ) {
cout << "info: findAll() namespace does not exist: " << ns << endl;
return Cursor(DiskLoc());
}
Extent *e = temp.getExtent(loc);
return Cursor( e->firstRecord );
}
void DataFileMgr::insert(const char *ns, void *buf, int len) {
DiskLoc loc;
bool found = namespaceIndex.find(ns, loc);
if( !found ) {
cout << "New namespace: " << ns << endl;
temp.newExtent(ns, loc);
namespaceIndex.add(ns, loc);
}
Extent *e = temp.getExtent(loc);
Record *r = e->newRecord(len); /*todo: if zero returned, need new extent */
memcpy(r->data, buf, len);
}
void DataFileMgr::init() {
temp.open("/data/temp.dat", 64 * 1024 * 1024);
}
void pdfileInit() {
namespaceIndex.init();
theDataFileMgr.init();
}

197
db/pdfile.h Normal file
View File

@ -0,0 +1,197 @@
// pdfile.h
#pragma once
#include "../stdafx.h"
#include "../util/mmap.h"
#include "storage.h"
struct PDFHeader;
struct Extent;
struct Record;
/*---------------------------------------------------------------------*/
struct Namespace {
Namespace(const char *ns) {
*this = ns;
}
Namespace& operator=(const char *ns) {
memset(buf, 0, 128); /* this is just to keep stuff clean in the files for easy dumping and reading */
strcpy_s(buf, 128, ns); return *this;
}
bool operator==(const Namespace& r) { return strcmp(buf, r.buf) == 0; }
int hash() const {
unsigned x = 0;
const char *p = buf;
while( *p ) {
x = x * 131 + *p;
p++;
}
return (x & 0x7fffffff) | 0x8000000; // must be > 0
}
char buf[128];
};
/*---------------------------------------------------------------------*/
#pragma pack(push)
#pragma pack(1)
struct Record {
enum { HeaderSize = 12 };
DiskLoc next;
int lengthWithHeaders;
char data[4];
bool haveNext() { return !next.isNull(); }
int netLength() { return lengthWithHeaders - HeaderSize; }
void setNewLength(int netlen) { lengthWithHeaders = netlen + HeaderSize; }
};
/* extents are regions where all the records within the region
belong to the same namespace.
*/
struct Extent {
unsigned magic;
DiskLoc myLoc;
Namespace ns; /* which namespace this extent is for. this is just for troubleshooting really */
int length; /* size of the extent, including these fields */
DiskLoc firstEmptyRegion;
DiskLoc firstRecord, lastRecord;
char extentData[4];
/* assumes already zeroed -- insufficient for block 'reuse' perhaps */
void init(const char *nsname, int _length, int _offset);
void assertOk() { assert(magic == 0x41424344); }
Record* newRecord(int len);
Record* getRecord(DiskLoc dl) {
assert( !dl.isNull() );
assert( dl.sameFile(myLoc) );
int x = dl.getOfs() - myLoc.getOfs();
assert( x > 0 );
return (Record *) (((char *) this) + x);
}
};
/*
----------------------
Header
----------------------
Extent (for a particular namespace)
Record
...
Record (some chained for unused space)
----------------------
more Extents...
----------------------
*/
/* data file header */
struct PDFHeader {
int version;
int versionMinor;
int fileLength;
DiskLoc unused; /* unused is the portion of the file that doesn't belong to any allocated extents. -1 = no more */
int unusedLength;
int reserved[8192 - 4*4];
char data[4];
static int headerSize() { return sizeof(PDFHeader) - 4; }
bool uninitialized() { if( version == 0 ) return true; assert(version == 3); return false; }
Record* getRecord(DiskLoc dl) {
int ofs = dl.getOfs();
assert( ofs >= headerSize() );
return (Record*) (((char *) this) + ofs);
}
void init(int filelength) {
if( uninitialized() ) {
assert(filelength > 32768 );
fileLength = filelength;
version = 3;
versionMinor = 0;
unused.setOfs( headerSize() );
assert( (data-(char*)this) == headerSize() );
unusedLength = fileLength - headerSize() - 16;
memcpy(data+unusedLength, " \nthe end\n", 16);
}
}
};
#pragma pack(pop)
class PhysicalDataFile {
friend class DataFileMgr;
friend class Cursor;
public:
void open(const char *filename, int length = 64 * 1024 * 1024);
private:
Extent* newExtent(const char *ns, DiskLoc& loc);
Extent* getExtent(DiskLoc loc);
Extent* _getExtent(DiskLoc loc);
Record* recordAt(DiskLoc dl) { return header->getRecord(dl); }
MemoryMappedFile mmf;
PDFHeader *header;
int length;
};
inline Extent* PhysicalDataFile::_getExtent(DiskLoc loc) {
loc.assertOk();
Extent *e = (Extent *) (((char *)header) + loc.getOfs());
return e;
}
inline Extent* PhysicalDataFile::getExtent(DiskLoc loc) {
Extent *e = _getExtent(loc);
return e;
}
class Cursor;
class DataFileMgr {
friend class Cursor;
public:
void init();
void insert(const char *ns, void *buf, int len);
Cursor findAll(const char *ns);
private:
PhysicalDataFile temp;
};
extern DataFileMgr theDataFileMgr;
class Cursor {
public:
bool ok() { return !curr.isNull(); }
bool eof() { return !ok(); }
Record* current() {
assert( ok() );
return theDataFileMgr.temp.recordAt(curr);
}
bool advance() {
if( eof() )
return false;
Record *r = current();
curr = r->next;
return ok();
}
Cursor(DiskLoc dl) : curr(dl) { }
Cursor() { }
private:
DiskLoc curr;
};

48
db/query.cpp Normal file
View File

@ -0,0 +1,48 @@
// query.cpp
#include "stdafx.h"
#include "query.h"
#include "pdfile.h"
#include "jsobj.h"
#include "../util/builder.h"
int nextCursorId = 1;
QueryResult* runQuery(const char *ns, const char *query, int ntoreturn) {
/* temp implementation -- just returns everything! */
BufBuilder b;
QueryResult *qr = 0;
b.skip(qr->data - ((char *)qr));
int n = 0;
Cursor c = theDataFileMgr.findAll(ns);
while( c.ok() ) {
Record *r = c.current();
JSObj js(r);
// check criteria here.
b.append(r->netLength()+4);
b.append(r->data, r->netLength());
n++;
if( n >= ntoreturn )
break;
c.advance();
}
qr = (QueryResult *) b.buf();
qr->len = b.len();
qr->reserved = 0;
qr->operation = opReply;
qr->cursorId = nextCursorId++;
qr->startOfs = 0;
qr->nReturned = n;
b.decouple();
return qr;
}

25
db/query.h Normal file
View File

@ -0,0 +1,25 @@
// query.h
#pragma once
#include "../stdafx.h"
#include "../grid/message.h"
/*
Query or GetMore:
int reserved;
unsigned cursorID;
unsigned startOfs;
unsigned nReturned;
list of marshalled JSObjects;
*/
struct QueryResult : public MsgData {
int cursorId;
int startOfs;
int nReturned;
char data[4];
};
QueryResult* runQuery(const char *ns, const char *query, int ntoreturn);

14
db/resource.h Normal file
View File

@ -0,0 +1,14 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by db.rc
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

BIN
db/send Normal file

Binary file not shown.

37
db/storage.h Normal file
View File

@ -0,0 +1,37 @@
/* storage.h
Storage subsystem management.
Lays out our datafiles on disk, manages disk space.
*/
#pragma once
#pragma pack(push)
#pragma pack(1)
class DiskLoc {
int reserved; /* this will be volume, file #, etc. */
int ofs;
public:
DiskLoc() { reserved = -1; ofs = -1; }
bool isNull() { return ofs == -1; }
void Null() { reserved = -1; ofs = -1; }
void assertOk() { assert(!isNull()); }
int getOfs() { return ofs; }
void setOfs(int _ofs) {
reserved = -2;
ofs = _ofs;
}
void inc(int amt) {
assert( !isNull() );
ofs += amt;
}
bool sameFile(DiskLoc b) { return reserved == b.reserved; /* not really done...*/ }
};
#pragma pack(pop)

153
grid/message.cpp Normal file
View File

@ -0,0 +1,153 @@
/* message
todo: authenticate; encrypt?
*/
#include "stdafx.h"
#include "message.h"
const int FragMax = 1480;
const int MSS = FragMax - 8;
#pragma pack(push)
#pragma pack(1)
struct Fragment {
enum { MinFragmentLen = 8 + 1 };
int msgId;
short fragmentLen;
short fragmentNo;
char data[1];
int fragmentDataLen() { return fragmentLen - 8; }
bool ok(int nRead) {
if( nRead < MinFragmentLen || fragmentLen > nRead || fragmentLen < MinFragmentLen ) {
cout << "recv: fragment bad. fragmentLen:" << fragmentLen << " nRead:" << nRead << endl;
return false;
}
if( fragmentNo == 0 && fragmentLen < MinFragmentLen + MsgDataHeaderSize ) {
cout << "recv: bad first fragment. fragmentLen:" << fragmentLen << endl;
return false;
}
return true;
}
MsgData* startOfMsgData() { assert(fragmentNo == 0); return (MsgData *) data; }
};
#pragma pack(pop)
int NextMsgId = -1000;
struct MsgStart {
MsgStart() {
srand(3);
NextMsgId = rand();
assert(MsgDataHeaderSize == 20);
assert(sizeof(Fragment) == 9);
}
} msgstart;
MessagingPort::MessagingPort() {
}
MessagingPort::~MessagingPort() {
}
void MessagingPort::init(int myUdpPort) {
SockAddr me(myUdpPort);
if( !conn.init(me) ) {
cout << "conn init failure in MessagingPort::init " << myUdpPort << endl;
exit(2);
}
}
/* this is a temp implementation. it will only work if there is a single entity messaging the receiver! */
bool MessagingPort::recv(Message& m) {
int n = conn.recvfrom(buf, BufSize, m.from);
Fragment *ff = (Fragment *) buf;
if( !ff->ok(n) )
return false;
MsgData *somd = ff->startOfMsgData();
int totalLen = somd->len;
if( ff->fragmentDataLen() >= totalLen ) {
// it's a short message, we got it all in one packet
m.setData(somd, false);
return true;
}
/* we'll need to read more */
char *msgData = (char *) malloc(totalLen);
char *p = msgData;
memcpy(p, somd, ff->fragmentDataLen());
int sofar = ff->fragmentDataLen();
int wanted = totalLen;
p += ff->fragmentDataLen();
wanted -= ff->fragmentDataLen();
/* note out of order, retransmits not done. just get us going on localhost */
int msgid = ff->msgId;
int expectedFragmentNo = 1;
SockAddr from;
while( 1 ) {
char b[FragMax];
int n = conn.recvfrom(b, sizeof(b), from);
Fragment *f = (Fragment *) b;
if( !f->ok(n) )
return false;
if( f->msgId != msgid || f->fragmentNo != expectedFragmentNo ) {
cout << "bad fragment" << endl;
return false;
}
if( from != m.from ) {
cout << "wrong sender? impl not done for multiple 'clients'" << endl;
assert(false);
return false;
}
memcpy(p, f->startOfMsgData(), f->fragmentDataLen());
p += f->fragmentDataLen();
wanted -= f->fragmentDataLen();
expectedFragmentNo++;
if( wanted <= 0 ) {
assert( wanted == 0 );
break;
}
}
return true;
}
void MessagingPort::reply(Message& received, Message& response) {
say(received.from, response, received.data->id);
}
bool MessagingPort::call(SockAddr& to, Message& toSend, Message& response) {
say(to, toSend);
bool ok = recv(response);
if( !ok )
return false;
assert( response.data->responseTo == toSend.data->id);
return true;
}
void MessagingPort::say(SockAddr& to, Message& toSend, int responseTo) {
toSend.data->reserved = 0;
toSend.data->id = NextMsgId++;
toSend.data->responseTo = responseTo;
int left = toSend.data->len;
assert( left > 0 && left <= 16 * 1024 * 1024 );
Fragment *f = (Fragment *) buf;
f->msgId = toSend.data->id;
f->fragmentNo = 0;
char *p = (char *) toSend.data;
while( left > 0 ) {
int l = left > MSS ? MSS : left;
f->fragmentLen = l + 8;
memcpy(f->data, p, l);
p += l;
left -= l;
conn.sendto(buf, l+8, to);
}
}

98
grid/message.h Normal file
View File

@ -0,0 +1,98 @@
// message.h
#pragma once
#include "../util/sock.h"
class Message;
class MessagingPort {
public:
enum { DBPort = 27017 };
MessagingPort();
~MessagingPort();
void init(int myUdpPort);
/* it's assumed if you reuse a message object, that it doesn't cross MessagingPort's.
also, the Message data will go out of scope on the subsequent recv call.
*/
bool recv(Message& m);
void reply(Message& received, Message& response);
bool call(SockAddr& to, Message& toSend, Message& response);
void say(SockAddr& to, Message& toSend, int responseTo = -1);
private:
UDPConnection conn;
enum { BufSize = 64 * 1024 };
char buf[BufSize];
};
#pragma pack(push)
#pragma pack(1)
enum Operations {
opReply = 1, /* reply. responseTo is set. */
dbMsg = 1000, /* generic msg command followed by a string */
dbUpdate = 2001, /* update object */
dbInsert = 2002,
dbGetByOID = 2003,
dbQuery = 2004,
dbGetMore = 2005
};
struct MsgData {
int len; /* len of the msg, including this field */
int reserved;
int id; /* request/reply id's match... */
int responseTo; /* id of the message we are responding to */
int operation;
char _data[4];
int dataLen();
};
const int MsgDataHeaderSize = sizeof(MsgData) - 4;
inline int MsgData::dataLen() { return len - MsgDataHeaderSize; }
#pragma pack(pop)
class Message {
public:
Message() { data = 0; }
~Message() { reset(); }
SockAddr from;
MsgData *data;
void reset() {
if( freeIt && data )
free(data);
data = 0;
}
void setData(MsgData *d, bool _freeIt) {
assert( data == 0 );
freeIt = _freeIt;
data = d;
}
void setData(int operation, const char *msgtxt) {
setData(operation, msgtxt, strlen(msgtxt)+1);
}
void setData(int operation, const char *msgdata, int len) {
assert(data == 0);
int dataLen = len + sizeof(MsgData) - 4;
MsgData *d = (MsgData *) malloc(dataLen);
memcpy(d->_data, msgdata, len);
d->len = dataLen;
d->operation = operation;
freeIt= true;
data = d;
}
private:
bool freeIt;
};

14
stdafx.cpp Normal file
View File

@ -0,0 +1,14 @@
// stdafx.cpp : source file that includes just the standard includes
// db.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file
struct MyAsserts {
MyAsserts() {
}
} myassertsstdafx;

43
stdafx.h Normal file
View File

@ -0,0 +1,43 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#include "targetver.h"
#include "assert.h"
#include <stdio.h>
#include <sstream>
//#if defined(_WIN32)
//#include <tchar.h>
//#else
typedef char _TCHAR;
//#endif
#include <iostream>
#include <fstream>
using namespace std;
#if !defined(_WIN32)
typedef int HANDLE;
inline void strcpy_s(char *dst, unsigned len, const char *src) { strcpy(dst, src); }
#else
typedef void *HANDLE;
#endif
//#if defined(CHAR)
//#error CHAR already defined?
//#endif
//#if defined(_WIN32_WINNT)
//typedef wchar_t CHAR;
//#else
// more to be done...linux unicode is 32 bit.
//typedef unsigned short CHAR; // 16 bit unicode
//#endif
#define null (0)

BIN
stdafx.h.gch Normal file

Binary file not shown.

5
targetver.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#ifndef _WIN32_WINNT // Allow use of features specific to Windows Vista or later.
#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows.
#endif

15
util/builder.h Normal file
View File

@ -0,0 +1,15 @@
/* builder.h
*/
#include "../stdafx.h"
class BufBuilder {
public:
void skip(int n) { }
char* buf() { return 0; }
void decouple() { }
void append(int) { }
void append(void *, int len) { }
int len() { return 0; }
};

96
util/hashtab.h Normal file
View File

@ -0,0 +1,96 @@
/* hashtab.h
Simple, fixed size hash table. Darn simple.
Uses a contiguous block of memory, so you can put it in a memory mapped file very easily.
*/
#include "../stdafx.h"
#include <map>
#pragma pack(push)
#pragma pack(1)
/* you should define:
int Key::hash() return > 0 always.
*/
template <
class Key,
class Type
>
class HashTable {
private:
const char *name;
struct Node {
int hash;
Key k;
Type value;
} *nodes;
int n;
int _find(const Key& k, bool& found) {
found = false;
int h = k.hash();
int i = h % n;
int start = i;
int chain = 0;
while( 1 ) {
if( nodes[i].hash == 0 ) {
return i;
}
if( nodes[i].hash == h && nodes[i].k == k ) {
found = true;
return i;
}
chain++;
i = (i+1) % n;
if( i == start ) {
cout << "warning: hashtable is full " << name << endl;
return -1;
}
if( chain == 200 )
cout << "warning: hashtable long chain " << name << endl;
}
}
public:
/* buf must be all zeroes on initialization. */
HashTable(void *buf, int buflen, const char *_name) : name(_name) {
int m = sizeof(Node);
n = buflen / m;
if( (n & 1) == 0 )
n--;
nodes = (Node *) buf;
assert(nodes[n-1].hash == 0);
assert(nodes[0].hash == 0);
}
Type* get(const Key& k) {
bool found;
int i = _find(k, found);
if( found )
return &nodes[i].value;
return 0;
}
void put(const Key& k, const Type& value) {
bool found;
int i = _find(k, found);
if( i < 0 )
return;
if( !found ) {
nodes[i].k = k;
nodes[i].hash = k.hash();
}
else {
assert( nodes[i].hash == k.hash() );
}
nodes[i].value = value;
}
};
#pragma pack(pop)

128
util/mmap.cpp Normal file
View File

@ -0,0 +1,128 @@
// mmap.cpp
#include "stdafx.h"
#include "mmap.h"
#if defined(_WIN32)
#include "windows.h"
MemoryMappedFile::MemoryMappedFile() {
fd = 0; maphandle = 0; view = 0;
}
MemoryMappedFile::~MemoryMappedFile() {
if( view )
UnmapViewOfFile(view);
view = 0;
if( maphandle )
CloseHandle(maphandle);
maphandle = 0;
if( fd )
CloseHandle(fd);
fd = 0;
}
std::wstring toWideString(const char *s) {
//const std::basic_string<TCHAR> s) {
std::basic_ostringstream<TCHAR> buf;
buf << s;
return buf.str();
}
void* MemoryMappedFile::map(const char *filename, int length) {
std::wstring filenamew = toWideString(filename);
fd = CreateFile(
filenamew.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if( fd == INVALID_HANDLE_VALUE ) {
cout << "CreateFile failed " << filename << endl;
return 0;
}
maphandle = CreateFileMapping(fd, NULL, PAGE_READWRITE, 0, length, NULL);
if( maphandle == NULL ) {
cout << "CreateFileMapping failed " << filename << endl;
return 0;
}
view = MapViewOfFile(maphandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if( view == 0 )
cout << "MapViewOfFile failed " << filename << endl;
return view;
}
void MemoryMappedFile::flush(bool) {
}
#else
#include <errno.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
MemoryMappedFile::MemoryMappedFile() {
fd = 0; maphandle = 0; view = 0; len = 0;
}
MemoryMappedFile::~MemoryMappedFile() {
if( view )
munmap(view, len);
view = 0;
if( fd )
close(fd);
fd = 0;
}
void* MemoryMappedFile::map(const char *filename, int length) {
len = length;
fd = open(filename, O_CREAT | O_RDWR | O_NOATIME, S_IRUSR | S_IWUSR);
if( !fd ) {
cout << "couldn't open " << filename << ' ' << errno << endl;
return 0;
}
/* make sure the file is the full desired length */
off_t filelen = lseek(fd, 0, SEEK_END);
if( filelen < length ) {
cout << "map: file length=" << filelen << " want:" << length << endl;
if( filelen != 0 ) {
cout << " failing mapping" << endl;
return 0;
}
cout << " writing file to full length with zeroes..." << endl;
int z = 8192;
char buf[z];
memset(buf, 0, z);
int left = length;
while( 1 ) {
if( left <= z ) {
write(fd, buf, left);
break;
}
write(fd, buf, z);
left -= z;
}
cout << " done" << endl;
}
lseek(fd, length, SEEK_SET);
write(fd, "", 1);
view = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
return view;
}
void MemoryMappedFile::flush(bool sync) {
if( msync(view, len, sync ? MS_SYNC : MS_ASYNC) )
cout << "msync error " << errno << endl;
}
#endif

22
util/mmap.h Normal file
View File

@ -0,0 +1,22 @@
// mmap.h
#pragma once
class MemoryMappedFile {
public:
MemoryMappedFile();
~MemoryMappedFile();
/* only smart enough right now to deal with files of a fixed length.
creates if DNE
*/
void* map(const char *filename, int length);
void flush(bool sync);
private:
HANDLE fd;
HANDLE maphandle;
void *view;
int len;
};

158
util/sock.cpp Normal file
View File

@ -0,0 +1,158 @@
// sock.cpp
#include "stdafx.h"
#include "sock.h"
void sendtest() {
cout << "sendtest\n";
SockAddr me(27016);
SockAddr dest("127.0.0.1", 27015);
UDPConnection c;
if( c.init(me) ) {
char buf[256];
cout << "sendto: ";
cout << c.sendto(buf, sizeof(buf), dest) << " errno:" << h_errno << endl;
}
cout << "end\n";
}
void listentest() {
cout << "listentest\n";
SockAddr me(27015);
SockAddr sender;
UDPConnection c;
if( c.init(me) ) {
char buf[256];
cout << "recvfrom: ";
cout << c.recvfrom(buf, sizeof(buf), sender) << " errno:" << h_errno << endl;
}
cout << "end listentest\n";
}
void xmain();
struct SockStartupTests {
SockStartupTests() {
#if defined(_WIN32)
WSADATA d;
if( WSAStartup(MAKEWORD(2,2), &d) != 0 ) {
cout << "ERROR: wsastartup failed " << errno << endl;
exit(1);
}
#endif
//cout << "ntohl:" << ntohl(256) << endl;
//sendtest();
//listentest();
}
} sstests;
#if 0
void smain() {
WSADATA wsaData;
SOCKET RecvSocket;
sockaddr_in RecvAddr;
int Port = 27015;
char RecvBuf[1024];
int BufLen = 1024;
sockaddr_in SenderAddr;
int SenderAddrSize = sizeof(SenderAddr);
//-----------------------------------------------
// Initialize Winsock
WSAStartup(MAKEWORD(2,2), &wsaData);
//-----------------------------------------------
// Create a receiver socket to receive datagrams
RecvSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
//-----------------------------------------------
// Bind the socket to any address and the specified port.
RecvAddr.sin_family = AF_INET;
RecvAddr.sin_port = htons(Port);
RecvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(RecvSocket, (SOCKADDR *) &RecvAddr, sizeof(RecvAddr));
//-----------------------------------------------
// Call the recvfrom function to receive datagrams
// on the bound socket.
printf("Receiving datagrams...\n");
recvfrom(RecvSocket,
RecvBuf,
BufLen,
0,
(SOCKADDR *)&SenderAddr,
&SenderAddrSize);
//-----------------------------------------------
// Close the socket when finished receiving datagrams
printf("Finished receiving. Closing socket.\n");
closesocket(RecvSocket);
//-----------------------------------------------
// Clean up and exit.
printf("Exiting.\n");
WSACleanup();
return;
}
void xmain() {
WSADATA wsaData;
SOCKET RecvSocket;
sockaddr_in RecvAddr;
int Port = 27015;
char RecvBuf[1024];
int BufLen = 1024;
sockaddr_in SenderAddr;
int SenderAddrSize = sizeof(SenderAddr);
//-----------------------------------------------
// Initialize Winsock
WSAStartup(MAKEWORD(2,2), &wsaData);
//-----------------------------------------------
// Create a receiver socket to receive datagrams
RecvSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
//-----------------------------------------------
// Bind the socket to any address and the specified port.
RecvAddr.sin_family = AF_INET;
RecvAddr.sin_port = htons(Port);
RecvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
SockAddr a(Port);
bind(RecvSocket, (SOCKADDR *) &a.sa, a.addressSize);
// bind(RecvSocket, (SOCKADDR *) &RecvAddr, sizeof(RecvAddr));
SockAddr b;
//-----------------------------------------------
// Call the recvfrom function to receive datagrams
// on the bound socket.
printf("Receiving datagrams...\n");
recvfrom(RecvSocket,
RecvBuf,
BufLen,
0,
(SOCKADDR *) &b.sa, &b.addressSize);
// (SOCKADDR *)&SenderAddr,
// &SenderAddrSize);
//-----------------------------------------------
// Close the socket when finished receiving datagrams
printf("Finished receiving. Closing socket.\n");
closesocket(RecvSocket);
//-----------------------------------------------
// Clean up and exit.
printf("Exiting.\n");
WSACleanup();
return;
}
#endif

88
util/sock.h Normal file
View File

@ -0,0 +1,88 @@
// sock.h
#pragma once
//#include "socket.h"
#if defined(_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
typedef int socklen_t;
#else
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
inline void closesocket(int s) { close(s); }
const int INVALID_SOCKET = -1;
typedef int SOCKET;
#define h_errno errno
#endif
struct SockAddr {
SockAddr() { addressSize = sizeof(sockaddr_in); }
SockAddr(int sourcePort); /* source side */
SockAddr(const char *ip, int port); /* dest (remote) side, or if you want to specify which interface locally */
struct sockaddr_in sa;
socklen_t addressSize;
bool operator==(const SockAddr& r) const {
return sa.sin_addr.s_addr == r.sa.sin_addr.s_addr &&
sa.sin_port == r.sa.sin_port;
}
bool operator!=(const SockAddr& r) const { return !(*this == r); }
};
class UDPConnection {
public:
UDPConnection() { sock = 0; }
~UDPConnection() { if( sock ) { closesocket(sock); sock = 0; } }
bool init(const SockAddr& myAddr);
int recvfrom(char *buf, int len, SockAddr& sender);
int sendto(char *buf, int len, const SockAddr& dest);
SOCKET sock;
};
inline int UDPConnection::recvfrom(char *buf, int len, SockAddr& sender) {
return ::recvfrom(sock, buf, len, 0, (sockaddr *) &sender.sa, &sender.addressSize);
}
inline int UDPConnection::sendto(char *buf, int len, const SockAddr& dest) {
return ::sendto(sock, buf, len, 0, (sockaddr *) &dest.sa, dest.addressSize);
}
inline bool UDPConnection::init(const SockAddr& myAddr) {
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if( sock == INVALID_SOCKET ) {
cout << "invalid socket? " << errno << endl;
return false;
}
cout << sizeof(sockaddr_in) << ' ' << myAddr.addressSize << endl;
if( bind(sock, (sockaddr *) &myAddr.sa, myAddr.addressSize) != 0 ) {
cout << "udp init failed" << endl;
closesocket(sock);
sock = 0;
return false;
}
return true;
}
inline SockAddr::SockAddr(int sourcePort) {
memset(sa.sin_zero, 0, sizeof(sa.sin_zero));
sa.sin_family = AF_INET;
sa.sin_port = htons(sourcePort);
sa.sin_addr.s_addr = htonl(INADDR_ANY);
addressSize = sizeof(sa);
}
inline SockAddr::SockAddr(const char *ip, int port) {
memset(sa.sin_zero, 0, sizeof(sa.sin_zero));
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = inet_addr(ip);
addressSize = sizeof(sa);
}