From 469bbcf0701e1eb8a6670c23145b0da87357e178 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Sun, 25 Mar 2012 20:00:08 +0000 Subject: Code is all reorganized. We're about ready to release. I should write up a little explenation of the arrangement. --- src/experimental/cache.cpp | 8 + src/experimental/cache.h | 437 ++++++++++++++++++++++++++++++++++ src/experimental/cachecalc.cpp | 8 + src/experimental/cachecalc.h | 63 +++++ src/experimental/cachestore.cpp | 9 + src/experimental/cachestore.h | 46 ++++ src/experimental/cachestorefiles.cpp | 9 + src/experimental/cachestorefiles.h | 207 ++++++++++++++++ src/experimental/cachestoremyriad.cpp | 9 + src/experimental/cachestoremyriad.h | 158 ++++++++++++ src/experimental/fastcgi.cpp | 372 +++++++++++++++++++++++++++++ src/experimental/fastcgi.h | 133 +++++++++++ src/experimental/httpget.cpp | 126 ++++++++++ src/experimental/httpget.h | 66 +++++ src/experimental/lexer.cpp | 40 ++++ src/experimental/lexer.h | 58 +++++ src/experimental/parser.cpp | 311 ++++++++++++++++++++++++ src/experimental/parser.h | 135 +++++++++++ src/experimental/random.cpp | 0 src/experimental/random.h | 0 src/experimental/randombase.cpp | 0 src/experimental/randombase.h | 0 src/experimental/randombasic.cpp | 0 src/experimental/randombasic.h | 0 src/experimental/regex.cpp | 95 ++++++++ src/experimental/regex.h | 44 ++++ src/experimental/regexengine.cpp | 5 + src/experimental/regexengine.h | 142 +++++++++++ src/experimental/xmlreader.cpp | 173 ++++++++++++++ src/experimental/xmlreader.h | 64 +++++ 30 files changed, 2718 insertions(+) create mode 100644 src/experimental/cache.cpp create mode 100644 src/experimental/cache.h create mode 100644 src/experimental/cachecalc.cpp create mode 100644 src/experimental/cachecalc.h create mode 100644 src/experimental/cachestore.cpp create mode 100644 src/experimental/cachestore.h create mode 100644 src/experimental/cachestorefiles.cpp create mode 100644 src/experimental/cachestorefiles.h create mode 100644 src/experimental/cachestoremyriad.cpp create mode 100644 src/experimental/cachestoremyriad.h create mode 100644 src/experimental/fastcgi.cpp create mode 100644 src/experimental/fastcgi.h create mode 100644 src/experimental/httpget.cpp create mode 100644 src/experimental/httpget.h create mode 100644 src/experimental/lexer.cpp create mode 100644 src/experimental/lexer.h create mode 100644 src/experimental/parser.cpp create mode 100644 src/experimental/parser.h create mode 100644 src/experimental/random.cpp create mode 100644 src/experimental/random.h create mode 100644 src/experimental/randombase.cpp create mode 100644 src/experimental/randombase.h create mode 100644 src/experimental/randombasic.cpp create mode 100644 src/experimental/randombasic.h create mode 100644 src/experimental/regex.cpp create mode 100644 src/experimental/regex.h create mode 100644 src/experimental/regexengine.cpp create mode 100644 src/experimental/regexengine.h create mode 100644 src/experimental/xmlreader.cpp create mode 100644 src/experimental/xmlreader.h (limited to 'src/experimental') diff --git a/src/experimental/cache.cpp b/src/experimental/cache.cpp new file mode 100644 index 0000000..52bd9fc --- /dev/null +++ b/src/experimental/cache.cpp @@ -0,0 +1,8 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#include "bu/cache.h" diff --git a/src/experimental/cache.h b/src/experimental/cache.h new file mode 100644 index 0000000..455cf1b --- /dev/null +++ b/src/experimental/cache.h @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#ifndef BU_CACHE_H +#define BU_CACHE_H + +// #include "bu/cptr.h" +#include "bu/hash.h" +#include "bu/list.h" +#include "bu/cachestore.h" +#include "bu/cachecalc.h" + +#include "bu/trace.h" + +namespace Bu +{ +// template +// keytype __cacheGetKey( obtype *&pObj ); + template + keytype __cacheGetKey( const obtype *pObj ) + { + return pObj->getKey(); + } + + template + class Cache + { + public: + /** + * Cache Pointer - Provides access to data that is held within the + * cache. This provides safe, refcounting access to data stored in + * the cache, with support for lazy loading. + */ + class Ptr + { + friend class Bu::Cache; + private: + Ptr( Cache *pCache, obtype *pData, + const keytype &kId ) : + pCache( pCache ), + pData( pData ), + kId( kId ) + { + if( pCache ) + pCache->incRef( kId ); + } + + Ptr( Cache *pCache, const keytype &kId ) : + pCache( pCache ), + pData( NULL ), + kId( kId ) + { + } + + public: + Ptr( const Ptr &rSrc ) : + pCache( rSrc.pCache ), + pData( rSrc.pData ), + kId( rSrc.kId ) + { + if( pCache && pData ) + pCache->incRef( kId ); + } + + Ptr() : + pCache( 0 ), + pData( 0 ) + { + } + + virtual ~Ptr() + { + if( pCache && pData ) + pCache->decRef( kId ); + } + + obtype &operator*() + { + checkPtr(); + return *pData; + } + + const obtype &operator*() const + { + checkPtr(); + return *pData; + } + + obtype *operator->() + { + checkPtr(); + return pData; + } + + const obtype *operator->() const + { + checkPtr(); + return pData; + } + + bool isValid() const + { + return pCache != NULL; + } + + bool isBound() const + { + return pData != NULL; + } + + bool isSet() const + { + return pCache != NULL; + } + + const keytype &getKey() const + { + return kId; + } + + void unbind() + { + if( pCache && pData ) + pCache->decRef( kId ); + pData = NULL; + } + + void clear() + { + unbind(); + pCache = NULL; + } + + void unset() + { + clear(); + } + + Ptr &operator=( const Ptr &rRhs ) + { + if( pCache && pData ) + pCache->decRef( kId ); + pCache = rRhs.pCache; + pData = rRhs.pData; + kId = rRhs.kId; + if( pCache && pData ) + pCache->incRef( kId ); + return *this; + } + + bool operator==( const Ptr &rRhs ) const + { + return pCache == rRhs.pCache && kId == rRhs.kId; + } + + bool operator!=( const Ptr &rRhs ) const + { + return pCache != rRhs.pCache || kId != rRhs.kId; + } + + private: + void checkPtr() const + { + if( pCache && !pData ) + { + pData = pCache->getRaw( kId ); + pCache->incRef( kId ); + } + } + + private: + Bu::Cache *pCache; + mutable obtype *pData; + mutable keytype kId; + }; + + private: + typedef Bu::CacheStore Store; + typedef Bu::List StoreList; + typedef Bu::CacheCalc Calc; + + typedef struct CacheEntry + { + obtype *pData; + int iRefs; + time_t tLastSync; + } CacheEntry; + + typedef Bu::Hash CidHash; + + public: + typedef keytype Key; + Cache( Calc *pCalc, Store *pStore ) : + pCalc( pCalc ), + pStore( pStore ) + { + TRACE(); + pCalc->setCache( this ); + } + + virtual ~Cache() + { + TRACE(); + + // Better safe than sorry, better try a sync before anything + // else happens. + sync(); + + // Cycle through and unload all objects from the system. + for( typename CidHash::iterator i = hEnt.begin(); + i != hEnt.end(); i++ ) + { + if( i.getValue().iRefs > 0 ) + { + // TODO: Throw an error in this case? iRefs != 0 for an + // object when the Cache is destroyed. + throw Bu::ExceptionBase("iRefs not zero."); + } + pStore->unload( + i.getValue().pData, + i.getKey() + ); + } + delete pCalc; + delete pStore; + } + + Ptr insert( obtype *pData ) + { + TRACE( pData ); + if( pStore->has( __cacheGetKey( pData ) ) ) + throw Bu::ExceptionBase("Key already exists in cache."); + CacheEntry e = {pData, 0, 0}; + keytype k = pStore->create( pData ); + hEnt.insert( k, e ); + + pCalc->onLoad( pData, k ); + + pStore->sync(); + + return Ptr( this, pData, k ); + } + + bool has( const keytype &cId ) + { + return hEnt.has( cId ) || pStore->has( cId ); + } + + /** + * Retrieve an object from the cache and return a pointer to it. + * The object returned may be loaded from backend storage if needed, + * or the currently live object will be returned. + *@param cId The id of the object to load. + *@returns A pointer to the object. + */ + Ptr get( const keytype &cId ) + { + TRACE( cId ); + try { + return Ptr( this, hEnt.get( cId ).pData, cId ); + } + catch( Bu::HashException &e ) { + CacheEntry e = {pStore->load( cId ), 0, time( NULL )}; + pCalc->onLoad( e.pData, cId ); + hEnt.insert( cId, e ); + return Ptr( this, e.pData, cId ); + } + } + + /** + * Retrieve a handle to an object without loading it now. This function + * will return a pointer that has not yet been "realized" but can be + * used normally. Upon initial use in any way the object will be + * loaded from the cache, either linking against the already loaded + * object or loading it fresh from the backend storage. The advantage + * of this is that you recieve a usable handle to the data, but it + * does not count as a reference yet, meaning that the data is loaded + * when you need it, not before. + */ + Ptr getLazy( const keytype &cId ) + { + TRACE( cId ); + return Ptr( this, cId ); + } + + int getRefCount( const keytype &cId ) + { + TRACE( cId ); + return hEnt.get( cId ).iRefs; + } + + void unload( const keytype &cId ) + { + TRACE( cId ); + try { + if( hEnt.get( cId ).iRefs > 0 ) + { + printf("Shouldn't unload, references still exist!\n"); + return; + } + } + catch( Bu::HashException &e ) { + // It's not here? Eh, return. + return; + } + obtype *pObj = hEnt.get( cId ).pData; + pCalc->onUnload( pObj, cId ); + hEnt.erase( cId ); + + // The unload has to happen last just in case cId is a reference + // to data that is about to be deleted from memory by the unload. + pStore->unload( pObj, cId ); + } + + void erase( const keytype &cId ) + { + TRACE( cId ); + try { + if( hEnt.get( cId ).iRefs > 0 ) + { + printf("Shouldn't erase, references still exist!\n"); + return; + } + + obtype *pObj = hEnt.get( cId ).pData; + pCalc->onDestroy( pObj, cId ); + hEnt.erase( cId ); + + pStore->destroy( pObj, cId ); + pStore->sync(); + } + catch( Bu::HashException &e ) { + pCalc->onDestroy( cId ); + + if( hEnt.has( cId ) ) + { + // The object was loaded by onDestroy + erase( cId ); + } + else + { + pStore->destroy( cId ); + pStore->sync(); + } + } + } + + typedef Bu::List KeyList; + KeyList getKeys() + { + return pStore->getKeys(); + } + + KeyList getActiveKeys() + { + return hEnt.getKeys(); + } + + int getSize() + { + return pStore->getSize(); + } + + /** + * Make sure all currently loaded but not-in-use objects are synced to + * the store. + */ + void sync() + { + TRACE(); + int iSynced = 0; + for( typename CidHash::iterator i = hEnt.begin(); + i != hEnt.end(); i++ ) + { + if( i.getValue().iRefs == 0 ) + { + if( pCalc->shouldSync( + i.getValue().pData, + i.getKey(), + i.getValue().tLastSync + ) ) + { + pStore->sync( + i.getValue().pData, + i.getKey() + ); + iSynced++; + i.getValue().tLastSync = time( NULL ); + } + } + } + if( iSynced > 0 ) + { + pStore->sync(); + } + } + + private: + void incRef( const keytype &cId ) + { + TRACE( cId ); + hEnt.get( cId ).iRefs++; + } + + void decRef( const keytype &cId ) + { + TRACE( cId ); + CacheEntry &e = hEnt.get( cId ); + e.iRefs--; + } + + obtype *getRaw( const keytype &cId ) + { + TRACE( cId ); + try { + return hEnt.get( cId ).pData; + } + catch( Bu::HashException &e ) { + CacheEntry e = {pStore->load( cId ), 0, time( NULL )}; + pCalc->onLoad( e.pData, cId ); + hEnt.insert( cId, e ); + return e.pData; + } + } + + private: + CidHash hEnt; + Calc *pCalc; + Store *pStore; + }; +}; + +#endif diff --git a/src/experimental/cachecalc.cpp b/src/experimental/cachecalc.cpp new file mode 100644 index 0000000..7b8a10a --- /dev/null +++ b/src/experimental/cachecalc.cpp @@ -0,0 +1,8 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#include "bu/cachecalc.h" diff --git a/src/experimental/cachecalc.h b/src/experimental/cachecalc.h new file mode 100644 index 0000000..89cfadc --- /dev/null +++ b/src/experimental/cachecalc.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#ifndef BU_CACHE_CALC_H +#define BU_CACHE_CALC_H + +#include "bu/trace.h" + +#include + +namespace Bu +{ + template class Cache; + + template + class CacheCalc + { + friend class Cache; + private: + typedef Cache MyCache; + public: + CacheCalc() : + pCache( (MyCache *)0 ) + { + TRACE(); + } + + virtual ~CacheCalc() + { + TRACE(); + } + + virtual void onLoad( obtype *pSrc, const keytype &key )=0; + virtual void onUnload( obtype *pSrc, const keytype &key )=0; + virtual void onDestroy( obtype *pSrc, const keytype &key )=0; + virtual void onDestroy( const keytype &key )=0; + virtual bool shouldSync( obtype *pSrc, const keytype &key, + time_t tLastSync )=0; + virtual void onTick() { }; + + protected: + MyCache *getCache() + { + TRACE(); + return pCache; + } + + private: + void setCache( MyCache *pCache ) + { + TRACE(); + this->pCache = pCache; + } + + MyCache *pCache; + }; +}; + +#endif diff --git a/src/experimental/cachestore.cpp b/src/experimental/cachestore.cpp new file mode 100644 index 0000000..af49548 --- /dev/null +++ b/src/experimental/cachestore.cpp @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#include "bu/cachestore.h" + diff --git a/src/experimental/cachestore.h b/src/experimental/cachestore.h new file mode 100644 index 0000000..d0d91a2 --- /dev/null +++ b/src/experimental/cachestore.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#ifndef BU_CACHE_STORE_H +#define BU_CACHE_STORE_H + +#include "bu/list.h" + +namespace Bu +{ + /** + * Handles I/O for data in the cache. This also assigns ID's to the newly + * created objects that are requested through this system. + */ + template + class CacheStore + { + public: + CacheStore() + { + } + + virtual ~CacheStore() + { + } + + virtual obtype *load( const keytype &key )=0; + virtual void unload( obtype *pObj, const keytype &key )=0; + virtual keytype create( obtype *pSrc )=0; + virtual void sync()=0; + virtual void sync( obtype *pObj, const keytype &key )=0; + virtual void destroy( obtype *pObj, const keytype &key )=0; + virtual void destroy( const keytype &key )=0; + virtual bool has( const keytype &key )=0; + virtual Bu::List getKeys() { return Bu::List(); } + virtual int getSize() { return -1; } + + private: + }; +}; + +#endif diff --git a/src/experimental/cachestorefiles.cpp b/src/experimental/cachestorefiles.cpp new file mode 100644 index 0000000..66ce672 --- /dev/null +++ b/src/experimental/cachestorefiles.cpp @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#include "bu/cachestorefiles.h" + diff --git a/src/experimental/cachestorefiles.h b/src/experimental/cachestorefiles.h new file mode 100644 index 0000000..10b2c1b --- /dev/null +++ b/src/experimental/cachestorefiles.h @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#ifndef BU_CACHE_STORE_FILES_H +#define BU_CACHE_STORE_FILES_H + +#include "bu/string.h" +#include "bu/file.h" +#include "bu/cachestore.h" +#include "bu/archive.h" +#include "bu/membuf.h" +#include "bu/formatter.h" +#include "bu/sio.h" + +#include +#include +#include +#include + +namespace Bu +{ + template + keytype __cacheGetKey( const obtype *pObj ); + + template + obtype *__cacheStoreFilesAlloc( const keytype &key ) + { + return new obtype(); + } + + template + void __cacheStoreFilesStore( Bu::Stream &s, obtype &rObj, + const keytype & ) + { + Bu::Archive ar( s, Bu::Archive::save ); + ar << rObj; + } + + template + obtype *__cacheStoreFilesLoad( Bu::Stream &s, const keytype &key ) + { + obtype *pObj = __cacheStoreFilesAlloc( key ); + Bu::Archive ar( s, Bu::Archive::load ); + ar >> (*pObj); + return pObj; + } + + template + class CacheStoreFiles : public CacheStore + { + public: + CacheStoreFiles( const Bu::String &sPrefix ) : + sPrefix( sPrefix ) + { + if( access( sPrefix.getStr(), W_OK|R_OK|X_OK ) ) + { + mkdir( sPrefix.getStr(), 0755 ); + } + } + + virtual ~CacheStoreFiles() + { + } + + virtual obtype *load( const keytype &key ) + { +// try +// { + Bu::MemBuf mb; + Bu::Formatter f( mb ); + f << sPrefix << "/" << key; + Bu::File fIn( mb.getString(), Bu::File::Read ); + obtype *pOb = __cacheStoreFilesLoad( fIn, key ); + return pOb; +// } +// catch( std::exception &e ) +// { +// throw Bu::HashException( e.what() ); +// } + } + + virtual void unload( obtype *pObj, const keytype & ) + { + delete pObj; + } + + virtual keytype create( obtype *pSrc ) + { + keytype key = __cacheGetKey( pSrc ); + Bu::MemBuf mb; + Bu::Formatter f( mb ); + f << sPrefix << "/" << key; + + Bu::File fTouch( mb.getString(), Bu::File::WriteNew ); + + return key; + } + + virtual void sync() + { + } + + virtual void sync( obtype *pSrc, const keytype &key ) + { + Bu::MemBuf mb; + Bu::Formatter f( mb ); + f << sPrefix << "/" << key; + + Bu::File fOut( mb.getString(), Bu::File::WriteNew ); + __cacheStoreFilesStore( fOut, *pSrc, key ); + } + + virtual void destroy( obtype *pObj, const keytype &key ) + { + Bu::MemBuf mb; + Bu::Formatter f( mb ); + f << sPrefix << "/" << key; + + unlink( mb.getString().getStr() ); + delete pObj; + } + + virtual void destroy( const keytype &key ) + { + Bu::MemBuf mb; + Bu::Formatter f( mb ); + f << sPrefix << "/" << key; + + unlink( mb.getString().getStr() ); + } + + virtual bool has( const keytype &key ) + { + Bu::MemBuf mb; + Bu::Formatter f( mb ); + f << sPrefix << "/"; + Bu::String sBase = mb.getString(); + f << key; + + if( sBase == mb.getString() ) + return false; + + return access( mb.getString().getStr(), F_OK ) == 0; + } + + virtual Bu::List getKeys() + { + DIR *dir = opendir( sPrefix.getStr() ); + struct dirent *de; + Bu::List lKeys; + + while( (de = readdir( dir ) ) ) + { + if( de->d_type != DT_REG ) + continue; + + keytype tmp; + Bu::MemBuf mb( de->d_name ); + Bu::Formatter f( mb ); + try + { + Fmt fm; + fm.tokenize( false ); + f << fm; + f >> tmp; + } + catch( Bu::ExceptionBase &e ) + { + Bu::sio << "Parse error in dir-scan: " << e.what() + << Bu::sio.nl; + } + lKeys.append( tmp ); + } + closedir( dir ); + + return lKeys; + } + + virtual int getSize() + { + DIR *dir = opendir( sPrefix.getStr() ); + struct dirent *de; + int iCount = 0; + + while( (de = readdir( dir ) ) ) + { + if( de->d_type != DT_REG ) + continue; + + iCount++; + } + closedir( dir ); + + return iCount; + } + + private: + Bu::String sPrefix; + }; + +}; + +#endif diff --git a/src/experimental/cachestoremyriad.cpp b/src/experimental/cachestoremyriad.cpp new file mode 100644 index 0000000..9d00dce --- /dev/null +++ b/src/experimental/cachestoremyriad.cpp @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#include "bu/cachestoremyriad.h" + diff --git a/src/experimental/cachestoremyriad.h b/src/experimental/cachestoremyriad.h new file mode 100644 index 0000000..e632a82 --- /dev/null +++ b/src/experimental/cachestoremyriad.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#ifndef BU_CACHE_STORE_MYRIAD_H +#define BU_CACHE_STORE_MYRIAD_H + +#include "bu/string.h" +#include "bu/stream.h" +#include "bu/myriad.h" +#include "bu/cachestore.h" +#include "bu/myriadstream.h" + +#include "bu/archive.h" + +namespace Bu +{ + template + keytype __cacheGetKey( const obtype *pObj ); + + template + obtype *__cacheStoreMyriadAlloc( const keytype &key ) + { + return new obtype(); + } + + template + void __cacheStoreMyriadStore( Bu::Stream &s, obtype &rObj, + const keytype & ) + { + Bu::Archive ar( s, Bu::Archive::save ); + ar << rObj; + } + + template + obtype *__cacheStoreMyriadLoad( Bu::Stream &s, const keytype &key ) + { + obtype *pObj = __cacheStoreMyriadAlloc( key ); + Bu::Archive ar( s, Bu::Archive::load ); + ar >> (*pObj); + return pObj; + } + + template + class CacheStoreMyriad : public CacheStore + { + public: + CacheStoreMyriad( Bu::Stream &sArch, + int iBlockSize=512, int iPreAllocate=8 ) : + mStore( sArch, iBlockSize, iPreAllocate ) + { + try + { + MyriadStream ns = mStore.openStream( 1 ); + Bu::Archive ar( ns, Bu::Archive::load ); + ar >> hId; + } + catch( Bu::MyriadException &e ) + { + int iStream = mStore.createStream(); + if( iStream != 1 ) + throw Bu::ExceptionBase("That's...horrible...id = %d.\n\n", + iStream ); + MyriadStream ns = mStore.openStream( 1 ); + Bu::Archive ar( ns, Bu::Archive::save ); + ar << hId; + } + } + + virtual ~CacheStoreMyriad() + { + MyriadStream ns = mStore.openStream( 1 ); + Bu::Archive ar( ns, Bu::Archive::save ); + ar << hId; + } + + virtual obtype *load( const keytype &key ) + { + int iStream = hId.get( key ); + MyriadStream ns = mStore.openStream( iStream ); + obtype *pOb = __cacheStoreMyriadLoad( ns, key ); + return pOb; + } + + virtual void unload( obtype *pObj, const keytype & ) + { + delete pObj; + } + + virtual keytype create( obtype *pSrc ) + { + keytype key = __cacheGetKey( pSrc ); + int iStream = mStore.createStream(); + hId.insert( key, iStream ); + MyriadStream ns = mStore.openStream( iStream ); + __cacheStoreMyriadStore( ns, *pSrc, key ); + ns.setSize( ns.tell() ); + return key; + } + + virtual void sync() + { + MyriadStream ns = mStore.openStream( 1 ); + Bu::Archive ar( ns, Bu::Archive::save ); + ar << hId; + ns.setSize( ns.tell() ); + mStore.sync(); + } + + virtual void sync( obtype *pSrc, const keytype &key ) + { + int iStream = hId.get( key ); + MyriadStream ns = mStore.openStream( iStream ); + __cacheStoreMyriadStore( ns, *pSrc, key ); + ns.setSize( ns.tell() ); + } + + virtual void destroy( obtype *pObj, const keytype &key ) + { + int iStream = hId.get( key ); + mStore.deleteStream( iStream ); + hId.erase( key ); + delete pObj; + } + + virtual void destroy( const keytype &key ) + { + int iStream = hId.get( key ); + mStore.deleteStream( iStream ); + hId.erase( key ); + } + + virtual bool has( const keytype &key ) + { + return hId.has( key ); + } + + virtual Bu::List getKeys() + { + return hId.getKeys(); + } + + virtual int getSize() + { + return hId.getSize(); + } + + private: + Myriad mStore; + typedef Bu::Hash StreamHash; + StreamHash hId; + }; +}; + +#endif diff --git a/src/experimental/fastcgi.cpp b/src/experimental/fastcgi.cpp new file mode 100644 index 0000000..2f9161e --- /dev/null +++ b/src/experimental/fastcgi.cpp @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#include "bu/fastcgi.h" + +#ifndef WIN32 + #include +#endif + +#include +#include + +#include "bu/membuf.h" + +#include "bu/sio.h" +using Bu::sio; +using Bu::Fmt; + +Bu::FastCgi::FastCgi() : + pSrv( NULL ), + bRunning( true ) +{ + pSrv = new Bu::TcpServerSocket( STDIN_FILENO, false ); +} + +Bu::FastCgi::FastCgi( int iPort ) : + pSrv( NULL ), + bRunning( true ) +{ + pSrv = new Bu::TcpServerSocket( iPort ); +} + +Bu::FastCgi::~FastCgi() +{ +} + +bool Bu::FastCgi::isEmbedded() +{ +#ifndef WIN32 + struct sockaddr name; + socklen_t namelen = sizeof(name); + if( getpeername( STDIN_FILENO, &name, &namelen ) != 0 && + errno == ENOTCONN ) + { + sio << "errno = " << errno << " (" << strerror( errno ) << ")" << + sio.nl; + sio << "Socket found" << sio.nl; + return true; + } + else + { + sio << "errno = " << errno << " (" << strerror( errno ) << ")" << + sio.nl; + sio << "No socket detected, running in standalone mode" << sio.nl; + return false; + } +#else + #warning Bu::FastCgi::isEmbedded IS A STUB for WIN32!!!! + return false; +#endif +} + +void Bu::FastCgi::read( Bu::TcpSocket &s, Bu::FastCgi::Record &r ) +{ + int iRead = s.read( &r, sizeof(Record) ); + if( iRead != sizeof(Record) ) + throw Bu::TcpSocketException("Hey, the size %d is wrong for Record. (%s)", + iRead, strerror( errno ) ); + r.uRequestId = ntohs( r.uRequestId ); + r.uContentLength = ntohs( r.uContentLength ); +} + +void Bu::FastCgi::write( Bu::TcpSocket &s, Bu::FastCgi::Record r ) +{ +// sio << "Out -> " << r << sio.nl; + r.uRequestId = htons( r.uRequestId ); + r.uContentLength = htons( r.uContentLength ); + s.write( &r, sizeof(Record) ); +} + +void Bu::FastCgi::read( Bu::TcpSocket &s, Bu::FastCgi::BeginRequestBody &b ) +{ + s.read( &b, sizeof(BeginRequestBody) ); + b.uRole = ntohs( b.uRole ); +} + +void Bu::FastCgi::write( Bu::TcpSocket &s, Bu::FastCgi::EndRequestBody b ) +{ + b.uStatus = htonl( b.uStatus ); + s.write( &b, sizeof(b) ); +} + +uint32_t Bu::FastCgi::readLen( Bu::TcpSocket &s, uint16_t &uRead ) +{ + uint8_t uByte[4]; + s.read( uByte, 1 ); + uRead++; + if( uByte[0] >> 7 == 0 ) + return uByte[0]; + + s.read( uByte+1, 3 ); + uRead += 3; + return ((uByte[0]&0x7f)<<24)|(uByte[1]<<16)|(uByte[2]<<8)|(uByte[3]); +} + +void Bu::FastCgi::readPair( Bu::TcpSocket &s, StrHash &hParams, uint16_t &uRead ) +{ + uint32_t uName = readLen( s, uRead ); + uint32_t uValue = readLen( s, uRead ); + uRead += uName + uValue; + unsigned char *sName = new unsigned char[uName]; + s.read( sName, uName ); + Bu::String fsName( (char *)sName, uName ); + delete[] sName; + + if( uValue > 0 ) + { + unsigned char *sValue = new unsigned char[uValue]; + s.read( sValue, uValue ); + Bu::String fsValue( (char *)sValue, uValue ); + hParams.insert( fsName, fsValue ); + delete[] sValue; + } + else + { + hParams.insert( fsName, "" ); + } +} + +bool Bu::FastCgi::hasChannel( int iChan ) +{ + if( aChannel.getSize() < iChan ) + return false; + if( aChannel[iChan-1] == NULL ) + return false; + return true; +} + +Bu::Formatter &Bu::operator<<( Bu::Formatter &f, const Bu::FastCgi::Record &r ) +{ + f << "[Ver=" << (uint32_t)r.uVersion << + ", Type=" << (uint32_t)r.uType << + ", Req=" << (uint32_t)r.uRequestId << + ", clen=" << (uint32_t)r.uContentLength << + ", plen=" << (uint32_t)r.uPaddingLength << + ", resv=" << (uint32_t)r.uReserved << + "]"; + return f; +} + +void Bu::FastCgi::run() +{ +// sio << "sizeof(Bu::FastCgi::Record) = " << sizeof(Record) << sio.nl; + bRunning = true; + while( bRunning ) + { + int iSock = pSrv->accept( 5 ); + if( iSock < 0 ) + continue; + + Bu::TcpSocket s( iSock ); + s.setBlocking( true ); +// sio << "Got connection, blocking? " << s.isBlocking() << sio.nl; + try + { + for(;;) + { + Record r; + memset( &r, 0, sizeof(r) ); +// try +// { + read( s, r ); +// } +// catch( Bu::ExceptionBase &e ) +// { +// sio << "Error: " << e.what() << ", " << s.isOpen() << +// sio.nl; +// continue; +// } + Channel *pChan = NULL; + if( r.uRequestId > 0 ) + { + if( !hasChannel( r.uRequestId ) && + r.uType != typeBeginRequest ) + { + sio << "Error, stream data without stream." << sio.nl; + sio << r << sio.nl; + if( r.uContentLength > 0 ) + { + char *buf = new char[r.uContentLength]; + sio << " (read " << s.read( buf, r.uContentLength ) + << "/" << r.uContentLength << "):" << sio.nl; + sio.write( buf, r.uContentLength ); + sio << sio.nl << sio.nl; + } + } + while( aChannel.getSize() < r.uRequestId ) + aChannel.append( NULL ); + if( r.uRequestId > 0 ) + pChan = aChannel[r.uRequestId-1]; + } + +// sio << "Record (id=" << r.uRequestId << ", len=" << +// r.uContentLength << ", pad=" << +// (unsigned int)r.uPaddingLength << "): "; +// fflush( stdout ); + + switch( (RequestType)r.uType ) + { + case typeBeginRequest: +// sio << "Begin Request."; + { + BeginRequestBody b; + read( s, b ); + if( pChan != NULL ) + { + sio << "Error!!!" << sio.nl; + return; + } + pChan = aChannel[r.uRequestId-1] = new Channel(); + } + break; + + case typeParams: +// sio << "Params."; + if( r.uContentLength == 0 ) + { + pChan->uFlags |= chflgParamsDone; + } + else + { + uint16_t uUsed = 0; + while( uUsed < r.uContentLength ) + { + readPair( s, pChan->hParams, uUsed ); + } + } + break; + + case typeStdIn: +// sio << "StdIn."; + if( r.uContentLength == 0 ) + { + pChan->uFlags |= chflgStdInDone; + } + else + { + char *buf = new char[r.uContentLength]; + int iTotal = 0; + do + { + size_t iRead = s.read( + buf, r.uContentLength-iTotal ); + iTotal += iRead; +// sio << " (read " << iRead << " " << iTotal +// << "/" << r.uContentLength << ")"; + pChan->sStdIn.append( buf, iRead ); + } while( iTotal < r.uContentLength ); + delete[] buf; + } + break; + + case typeData: +// sio << "Data."; + if( r.uContentLength == 0 ) + { + pChan->uFlags |= chflgDataDone; + } + else + { + char *buf = new char[r.uContentLength]; + s.read( buf, r.uContentLength ); + pChan->sData.append( buf, r.uContentLength ); + delete[] buf; + } + break; + + case typeStdOut: + case typeStdErr: + case typeEndRequest: + case typeAbortRequest: + case typeGetValuesResult: +// sio << "Scary."; + // ??? we shouldn't get these. + break; + + case typeGetValues: + break; + } + +// sio << sio.nl; + + if( pChan ) + { + if( pChan->uFlags == chflgAllDone ) + { +// sio << "All done, generating output." << sio.nl; + Bu::MemBuf mStdOut, mStdErr; + int iRet = onRequest( + pChan->hParams, pChan->sStdIn, + mStdOut, mStdErr + ); + + Bu::String &sStdOut = mStdOut.getString(); + Bu::String &sStdErr = mStdErr.getString(); + + Record rOut; + memset( &rOut, 0, sizeof(rOut) ); + rOut.uVersion = 1; + rOut.uRequestId = r.uRequestId; + rOut.uPaddingLength = 0; + rOut.uType = typeStdOut; + if( sStdOut.getSize() > 0 ) + { + for( int iPos = 0; iPos < sStdOut.getSize(); + iPos += 65528 ) + { + int iSize = sStdOut.getSize()-iPos; + if( iSize > 65528 ) + iSize = 65528; + rOut.uContentLength = iSize; + write( s, rOut ); + s.write( sStdOut.getStr()+iPos, iSize ); + } + } + rOut.uContentLength = 0; + write( s, rOut ); + + rOut.uType = typeStdErr; + if( sStdErr.getSize() > 0 ) + { + for( int iPos = 0; iPos < sStdErr.getSize(); + iPos += 65528 ) + { + int iSize = sStdErr.getSize()-iPos; + if( iSize > 65528 ) + iSize = 65528; + rOut.uContentLength = iSize; + write( s, rOut ); + s.write( sStdErr.getStr()+iPos, iSize ); + } + } + rOut.uContentLength = 0; + write( s, rOut ); + + rOut.uType = typeEndRequest; + rOut.uContentLength = 8; + write( s, rOut ); + + EndRequestBody b; + memset( &b, 0, sizeof(b) ); + b.uStatus = iRet; + write( s, b ); + + delete pChan; + aChannel[r.uRequestId-1] = NULL; + } + } + } + } + catch( Bu::TcpSocketException &e ) + { +// sio << "Bu::SocketException: " << e.what() << sio.nl << +// "\tSocket open: " << s.isOpen() << sio.nl; + } + } +} + diff --git a/src/experimental/fastcgi.h b/src/experimental/fastcgi.h new file mode 100644 index 0000000..d290c40 --- /dev/null +++ b/src/experimental/fastcgi.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#ifndef BU_FAST_CGI_H +#define BU_FAST_CGI_H + +#include "bu/string.h" +#include "bu/hash.h" +#include "bu/array.h" +#include "bu/tcpsocket.h" +#include "bu/tcpserversocket.h" + +namespace Bu +{ + class Stream; + + class FastCgi + { + public: + FastCgi( int iPort ); + FastCgi(); + virtual ~FastCgi(); + + static bool isEmbedded(); + + typedef Bu::Hash StrHash; + enum RequestType + { + typeBeginRequest = 1, + typeAbortRequest = 2, + typeEndRequest = 3, + typeParams = 4, + typeStdIn = 5, + typeStdOut = 6, + typeStdErr = 7, + typeData = 8, + typeGetValues = 9, + typeGetValuesResult = 10 + }; + + enum Role + { + roleResponder = 1, + roleAuthorizer = 2, + roleFilter = 3 + }; + + enum Flags + { + flagsKeepConn = 1 + }; + + enum Status + { + statusRequestComplete = 0, + statusCantMpxConn = 1, + statusOverloaded = 2, + statusUnknownRole = 3 + }; + + typedef struct { + uint8_t uVersion; + uint8_t uType; + uint16_t uRequestId; + uint16_t uContentLength; + uint8_t uPaddingLength; + uint8_t uReserved; + } Record; + + typedef struct { + uint16_t uRole; + uint8_t uFlags; + uint8_t reserved[5]; + } BeginRequestBody; + + typedef struct { + uint32_t uStatus; + uint8_t uProtocolStatus; + uint8_t reserved[3]; + } EndRequestBody; + + typedef struct Channel { + Channel() : uFlags( 0 ) { } + StrHash hParams; + Bu::String sStdIn; + Bu::String sData; + uint8_t uFlags; + } Channel; + + enum ChannelFlags + { + chflgParamsDone = 0x01, + chflgStdInDone = 0x02, + chflgDataDone = 0x04, + + chflgAllDone = 0x03 + }; + + virtual void run(); + + void stopRunning() { bRunning = false; } + + virtual void onInit() { }; + virtual int onRequest( const StrHash &hParams, + const Bu::String &sStdIn, Bu::Stream &sStdOut, + Bu::Stream &sStdErr )=0; + virtual void onUninit() { }; + + private: + void read( Bu::TcpSocket &s, Record &r ); + void read( Bu::TcpSocket &s, BeginRequestBody &b ); + uint32_t readLen( Bu::TcpSocket &s, uint16_t &uUsed ); + void readPair( Bu::TcpSocket &s, StrHash &hParams, uint16_t &uUsed ); + + void write( Bu::TcpSocket &s, Record r ); + void write( Bu::TcpSocket &s, EndRequestBody b ); + + bool hasChannel( int iChan ); + + private: + Bu::TcpServerSocket *pSrv; + bool bRunning; + Bu::Array aChannel; + }; + + Bu::Formatter &operator<<( Bu::Formatter &f, const Bu::FastCgi::Record &r ); +}; + +#endif diff --git a/src/experimental/httpget.cpp b/src/experimental/httpget.cpp new file mode 100644 index 0000000..99492a2 --- /dev/null +++ b/src/experimental/httpget.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#include "bu/httpget.h" + +Bu::HttpGet::HttpGet( const Bu::Url &uSrc, const Bu::String &sMethod ) : + uSrc( uSrc ), + sMethod( sMethod ), + sSrv( uSrc.getHost(), uSrc.getPort() ) +{ + sSrv.write( sMethod + " " + uSrc.getFullPath() + " HTTP/1.1\r\n" ); +} + +Bu::HttpGet::~HttpGet() +{ +} + +void Bu::HttpGet::close() +{ +} + +void Bu::HttpGet::get() +{ + for( MimeHash::iterator i = hMimeOut.begin(); i; i++ ) + { + sSrv.write( i.getKey() + ": " + i.getValue() + "\r\n" ); + } + sSrv.write("\r\n", 2 ); + +// sSrv.read( +} + +Bu::size Bu::HttpGet::read( void * /*pBuf*/, Bu::size /*nBytes*/ ) +{ + return 0; +} + +Bu::size Bu::HttpGet::write( const void * /*pBuf*/, Bu::size /*nBytes*/ ) +{ + return 0; +} + +Bu::size Bu::HttpGet::tell() +{ + return 0; +} + +void Bu::HttpGet::seek( Bu::size ) +{ +} + +void Bu::HttpGet::setPos( Bu::size ) +{ +} + +void Bu::HttpGet::setPosEnd( Bu::size ) +{ +} + +bool Bu::HttpGet::isEos() +{ + return false; +} + +bool Bu::HttpGet::isOpen() +{ + return true; +} + +void Bu::HttpGet::flush() +{ +} + +bool Bu::HttpGet::canRead() +{ + return true; +} + +bool Bu::HttpGet::canWrite() +{ + return false; +} + +bool Bu::HttpGet::isReadable() +{ + return true; +} + +bool Bu::HttpGet::isWritable() +{ + return false; +} + +bool Bu::HttpGet::isSeekable() +{ + return false; +} + +bool Bu::HttpGet::isBlocking() +{ + return true; +} + +void Bu::HttpGet::setBlocking( bool /*bBlocking*/ ) +{ +} + +Bu::size Bu::HttpGet::getSize() const +{ + return 0; +} + +Bu::size Bu::HttpGet::getBlockSize() const +{ + return 0; +} + +Bu::String Bu::HttpGet::getLocation() const +{ + return uSrc.getUrl(); +} + diff --git a/src/experimental/httpget.h b/src/experimental/httpget.h new file mode 100644 index 0000000..a58e8ac --- /dev/null +++ b/src/experimental/httpget.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#ifndef BU_HTTP_GET_H +#define BU_HTTP_GET_H + +#include "bu/stream.h" +#include "bu/string.h" +#include "bu/url.h" +#include "bu/tcpsocket.h" +#include "bu/hash.h" + +namespace Bu +{ + class HttpGet : public Bu::Stream + { + public: + HttpGet( const Bu::Url &uSrc, const Bu::String &sMethod="GET" ); + virtual ~HttpGet(); + + void get(); + + // From Bu::Stream + virtual void close(); + virtual Bu::size read( void *pBuf, Bu::size nBytes ); + virtual Bu::size write( const void *pBuf, Bu::size nBytes ); + using Stream::write; + + virtual Bu::size tell(); + virtual void seek( Bu::size offset ); + virtual void setPos( Bu::size pos ); + virtual void setPosEnd( Bu::size pos ); + virtual bool isEos(); + virtual bool isOpen(); + + virtual void flush(); + + virtual bool canRead(); + virtual bool canWrite(); + + virtual bool isReadable(); + virtual bool isWritable(); + virtual bool isSeekable(); + + virtual bool isBlocking(); + virtual void setBlocking( bool bBlocking=true ); + + virtual size getSize() const; + virtual size getBlockSize() const; + virtual Bu::String getLocation() const; + + private: + Bu::Url uSrc; + Bu::String sMethod; + Bu::TcpSocket sSrv; + typedef Bu::Hash MimeHash; + MimeHash hMimeIn; + MimeHash hMimeOut; + }; +}; + +#endif diff --git a/src/experimental/lexer.cpp b/src/experimental/lexer.cpp new file mode 100644 index 0000000..185456a --- /dev/null +++ b/src/experimental/lexer.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#include "bu/lexer.h" +#include "bu/membuf.h" +#include "bu/formatter.h" + +Bu::Lexer::Lexer() +{ +} + +Bu::Lexer::~Lexer() +{ +} + +Bu::Lexer::Token::Token() : + iToken( -1 ) +{ +} + +Bu::Lexer::Token::Token( Bu::Lexer::TokenType iToken ) : + iToken( iToken ) +{ +} + +Bu::String Bu::Lexer::tokenToString( const Bu::Lexer::Token &t ) +{ + Bu::MemBuf mb; + Bu::Formatter f( mb ); + f << "<" << t.iToken << ">"; + if( t.vExtra.isSet() ) + f << " (" << t.vExtra << ")"; + + return mb.getString(); +} + diff --git a/src/experimental/lexer.h b/src/experimental/lexer.h new file mode 100644 index 0000000..b057692 --- /dev/null +++ b/src/experimental/lexer.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#ifndef BU_LEXER_H +#define BU_LEXER_H + +#include "bu/variant.h" + +namespace Bu +{ + class Stream; + + /** + * The base class for creating a lexical analyzer. This is designed to work + * in tandem with the Bu::Parser class, which uses this to tokenize textual + * input. It can be used by just about anything that cares about tokens + * more than raw input, though. + */ + class Lexer + { + public: + Lexer(); + virtual ~Lexer(); + + typedef int TokenType; + + class Token + { + public: + Token(); + Token( TokenType iToken ); + + template + Token( TokenType iToken, const t &v ) : + iToken( iToken )//, +// vExtra( v ) + { + vExtra = v; + } + TokenType iToken; + Bu::Variant vExtra; + int iStartCol; + int iStartRow; + int iEndCol; + int iEndRow; + }; + + virtual Token *nextToken()=0; + + virtual Bu::String tokenToString( const Token &t ); + }; +}; + +#endif diff --git a/src/experimental/parser.cpp b/src/experimental/parser.cpp new file mode 100644 index 0000000..4d9f793 --- /dev/null +++ b/src/experimental/parser.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#include "bu/parser.h" +#include "bu/lexer.h" + +#include "bu/sio.h" +using namespace Bu; + +Bu::Parser::Parser() +{ +} + +Bu::Parser::~Parser() +{ +} + +void Bu::Parser::pushLexer( Lexer *pLex ) +{ + sLexer.push( pLex ); +} + +void Bu::Parser::popLexer() +{ + delete sLexer.peekPop(); +} + +Lexer::Token *Bu::Parser::popToken() +{ + return sToken.peekPop(); +} + +void Bu::Parser::pushToken( Lexer::Token *pTok ) +{ + sToken.push( pTok ); +} + +void Bu::Parser::parse() +{ + int iCurNt = iRootNonTerminal; + Lexer::Token *ptCur = sLexer.peek()->nextToken(); + sio << "Token(a): " << sLexer.peek()->tokenToString( *ptCur ) << sio.nl; + selectProduction( iCurNt, ptCur ); + + while( !sState.isEmpty() ) + { + switch( (*sState.peek()).eType ) + { + case State::typeTerminal: + sio << "terminal: " << ptCur->iToken << " == " + << (*sState.peek()).iIndex << sio.nl; + if( ptCur->iToken == (*sState.peek()).iIndex ) + { + advanceState(); + delete ptCur; + ptCur = sLexer.peek()->nextToken(); + sio << "Token(b): " << sLexer.peek()->tokenToString( *ptCur ) << sio.nl; + } + else + { + throw Bu::ExceptionBase("Error parsing code."); + } + break; + + case State::typeTerminalPush: + sio << "terminalpush: " << ptCur->iToken << " == " + << (*sState.peek()).iIndex << sio.nl; + if( ptCur->iToken == (*sState.peek()).iIndex ) + { + advanceState(); + sToken.push( ptCur ); + + ptCur = sLexer.peek()->nextToken(); + sio << "Token(c): " << sLexer.peek()->tokenToString( *ptCur ) << sio.nl; + } + else + { + throw Bu::ExceptionBase("Error parsing code."); + } + break; + + case State::typeNonTerminal: + sio << "nonterminal: " << ptCur->iToken << " --> " + << (*sState.peek()).iIndex << sio.nl; + { + int iNt = (*sState.peek()).iIndex; + sio << "Current state: " << *sState.peek() << sio.nl; + if( !selectProduction( iNt, ptCur ) ) + { + throw Bu::ExceptionBase("Error parsing code."); + } + } + break; + + case State::typeReduction: + sio << "reduction" << sio.nl; + aReduction[(*sState.peek()).iIndex]( *this ); + advanceState(); + break; + } + } +} + +bool Bu::Parser::selectProduction( int iNt, Lexer::Token *ptCur ) +{ + NonTerminal &nt = aNonTerminal[iNt]; + int j = 0; + for( NonTerminal::ProductionList::iterator i = nt.lProduction.begin(); + i; i++,j++ ) + { + if( (*i).isEmpty() ) + continue; + sio << "-->(Attempting production " << iNt << ":" << j << ": " + << (*i).first() << ")" << sio.nl; + if( (*i).first().eType == State::typeTerminal || + (*i).first().eType == State::typeTerminalPush ) + { + if( (*i).first().iIndex == ptCur->iToken ) + { + sState.push( (*i).begin() ); + sio.incIndent(); + sio << "Pushing production " << j << " from nt " << iNt + << sio.nl; + return true; + } + } + else if( (*i).first().eType == State::typeNonTerminal ) + { + sState.push( (*i).begin() ); + sio.incIndent(); + sio << "Pushing production " << j << " from nt " << iNt + << " as test." << sio.nl; + if( !selectProduction( (*i).first().iIndex, ptCur ) ) + { + sio.decIndent(); + sState.pop(); + sio << "Production " << j << " from nt " << iNt + << " didn't work out." << sio.nl; + } + else + { + return true; + } + } + } + if( nt.bCanSkip ) + { + sio << "Nothing matches, skipping non-terminal." << sio.nl; + advanceState(); + return true; + } + sio << "-->(Found nothing)" << sio.nl; + return false; +} + +void Bu::Parser::advanceState() +{ + if( sState.isEmpty() ) + return; + + sState.peek()++; + if( !sState.peek() ) + { + sio.decIndent(); + sState.pop(); + sio << "State advanced, End of production." << sio.nl; + advanceState(); + return; + } + sio << "State advanced, now: " << *(sState.peek()) << sio.nl; +} + +void Bu::Parser::setRootNonTerminal( int iRoot ) +{ + iRootNonTerminal = iRoot; +} + +void Bu::Parser::setRootNonTerminal( const Bu::String &sRoot ) +{ + setRootNonTerminal( hNonTerminalName.get( sRoot ) ); +} + +int Bu::Parser::addNonTerminal( const Bu::String &sName, NonTerminal &nt ) +{ + int iId = aNonTerminal.getSize(); + aNonTerminal.append( nt ); + hNonTerminalName.insert( sName, iId ); + sio << "nt '" << sName << "' = " << iId << sio.nl; + return iId; +} + +int Bu::Parser::addNonTerminal( const Bu::String &sName ) +{ + int iId = aNonTerminal.getSize(); + aNonTerminal.append( NonTerminal() ); + hNonTerminalName.insert( sName, iId ); + sio << "nt '" << sName << "' = " << iId << sio.nl; + return iId; +} + +void Bu::Parser::setNonTerminal( const Bu::String &sName, NonTerminal &nt ) +{ + aNonTerminal[hNonTerminalName.get(sName)] = nt; +} + +int Bu::Parser::getNonTerminalId( const Bu::String &sName ) +{ + return hNonTerminalName.get( sName ); +} + +bool Bu::Parser::hasNonTerminal( const Bu::String &sName ) +{ + return hNonTerminalName.has( sName ); +} + +int Bu::Parser::addReduction( const Bu::String &sName, const Reduction &r ) +{ + int iId = aReduction.getSize(); + aReduction.append( r ); + hReductionName.insert( sName, iId ); + return iId; +} + +int Bu::Parser::addReduction( const Bu::String &sName ) +{ + int iId = aReduction.getSize(); + aReduction.append( Reduction() ); + hReductionName.insert( sName, iId ); + return iId; +} + +void Bu::Parser::setReduction( const Bu::String &sName, const Reduction &r ) +{ + aReduction[hReductionName.get(sName)] = r; +} + +int Bu::Parser::getReductionId( const Bu::String &sName ) +{ + return hReductionName.get( sName ); +} + +bool Bu::Parser::hasReduction( const Bu::String &sName ) +{ + return hReductionName.has( sName ); +} + +// +// Bu::Parser::State +// + +Bu::Parser::State::State( Bu::Parser::State::Type eType, int iIndex ) : + eType( eType ), + iIndex( iIndex ) +{ +} + +Bu::Parser::State::~State() +{ +} + +// +// Bu::Parser::NonTerminal +// + +Bu::Parser::NonTerminal::NonTerminal() : + bCanSkip( false ) +{ +} + +Bu::Parser::NonTerminal::~NonTerminal() +{ +} + +void Bu::Parser::NonTerminal::addProduction( Production p ) +{ + lProduction.append( p ); +} + +void Bu::Parser::NonTerminal::setCanSkip() +{ + bCanSkip = true; +} + +Bu::Formatter &Bu::operator<<( Bu::Formatter &f, Bu::Parser::State::Type t ) +{ + switch( t ) + { + case Bu::Parser::State::typeTerminal: + return f << "typeTerminal"; + + case Bu::Parser::State::typeTerminalPush: + return f << "typeTerminalPush"; + + case Bu::Parser::State::typeNonTerminal: + return f << "typeNonTerminal"; + + case Bu::Parser::State::typeReduction: + return f << "typeReduction"; + } + return f << "***error***"; +} + +Bu::Formatter &Bu::operator<<( Bu::Formatter &f, const Bu::Parser::State &s ) +{ + return f << "{" << s.eType << ": " << s.iIndex << "}"; +} + diff --git a/src/experimental/parser.h b/src/experimental/parser.h new file mode 100644 index 0000000..a168c7b --- /dev/null +++ b/src/experimental/parser.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#ifndef BU_PARSER_H +#define BU_PARSER_H + +#include "bu/string.h" +#include "bu/list.h" +#include "bu/array.h" +#include "bu/hash.h" +#include "bu/signals.h" + +#include "bu/lexer.h" + +namespace Bu +{ + /** + * The base framework for a LR(1) grammar parser. Provided a proper set of + * ParserStates this will prase any input the lexer can provide. + */ + class Parser + { + public: + Parser(); + virtual ~Parser(); + + /** + * When a Lexer is pushed onto the stack it becomes the source for + * future tokens read by the parser until it is popped off the stack. + * The Parser takes ownership of every Lexer pushed onto the stack, + * and will delete it when it is popped off the stack. + */ + void pushLexer( Lexer *pLex ); + + /** + * Pop a lexer off the stack, and delete it. + */ + void popLexer(); + + Lexer::Token *popToken(); + void pushToken( Lexer::Token *pTok ); + + /** + * Execute a parse. + */ + void parse(); + + void setRootNonTerminal( int iRoot ); + void setRootNonTerminal( const Bu::String &sRoot ); + + typedef Bu::Signal1 Reduction; + + /** + * Represents a possible state, either a terminal or non-terminal symbol + * in a Production. + */ + class State + { + public: + enum Type + { + typeTerminal, + typeTerminalPush, + typeNonTerminal, + typeReduction + }; + + State( Type eType, int iIndex ); + virtual ~State(); + + //private: + Type eType; + int iIndex; + }; + + typedef Bu::List Production; + + class NonTerminal + { + public: + NonTerminal(); + virtual ~NonTerminal(); + + void addProduction( Production p ); + void setCanSkip(); + +// private: + typedef Bu::List ProductionList; + ProductionList lProduction; + bool bCanSkip; + }; + + int addNonTerminal( const Bu::String &sName, NonTerminal &nt ); + int addNonTerminal( const Bu::String &sName ); + void setNonTerminal( const Bu::String &sName, NonTerminal &nt ); + int getNonTerminalId( const Bu::String &sName ); + bool hasNonTerminal( const Bu::String &sName ); + + int addReduction( const Bu::String &sName, const Reduction &r ); + int addReduction( const Bu::String &sName ); + void setReduction( const Bu::String &sName, const Reduction &r ); + int getReductionId( const Bu::String &sName ); + bool hasReduction( const Bu::String &sName ); + + private: + bool selectProduction( int iNt, Lexer::Token *ptCur ); + void advanceState(); + + private: + typedef Bu::List LexerStack; + typedef Bu::List TokenStack; + typedef Bu::List StateStack; + typedef Bu::Array ReductionArray; + typedef Bu::Hash NameIndexHash; + typedef Bu::Array NonTerminalArray; + + LexerStack sLexer; + TokenStack sToken; + StateStack sState; + ReductionArray aReduction; + NameIndexHash hReductionName; + NonTerminalArray aNonTerminal; + NameIndexHash hNonTerminalName; + int iRootNonTerminal; + }; +Bu::Formatter &operator<<( Bu::Formatter &f, Bu::Parser::State::Type t ); +Bu::Formatter &operator<<( Bu::Formatter &f, const Bu::Parser::State &s ); +}; + + +#endif diff --git a/src/experimental/random.cpp b/src/experimental/random.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/experimental/random.h b/src/experimental/random.h new file mode 100644 index 0000000..e69de29 diff --git a/src/experimental/randombase.cpp b/src/experimental/randombase.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/experimental/randombase.h b/src/experimental/randombase.h new file mode 100644 index 0000000..e69de29 diff --git a/src/experimental/randombasic.cpp b/src/experimental/randombasic.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/experimental/randombasic.h b/src/experimental/randombasic.h new file mode 100644 index 0000000..e69de29 diff --git a/src/experimental/regex.cpp b/src/experimental/regex.cpp new file mode 100644 index 0000000..af0d364 --- /dev/null +++ b/src/experimental/regex.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#include "bu/regex.h" + +#include // Please, please include the system regex.h file + +#define re ((regex_t *)pRegEx) +#define aSubStr ((regmatch_t *)paSubStr) + +Bu::RegEx::RegEx() : + pRegEx( NULL ), + bCompiled( false ), + paSubStr( NULL ) +{ +} + +Bu::RegEx::RegEx( const Bu::String &sSrc ) : + pRegEx( NULL ), + bCompiled( false ), + paSubStr( NULL ) +{ + compile( sSrc ); +} + +Bu::RegEx::~RegEx() +{ + if( bCompiled ) + { + regfree( re ); + delete re; + delete[] aSubStr; + } +} + +void Bu::RegEx::compile( const Bu::String &sSrc ) +{ + if( bCompiled ) + { + regfree( re ); + delete re; + delete[] aSubStr; + bCompiled = false; + } + pRegEx = (void *)(new regex_t); + + int nErr = regcomp( re, sSrc.getStr(), REG_EXTENDED|REG_NEWLINE ); + if( nErr ) + { + size_t length = regerror( nErr, re, NULL, 0 ); + char *buffer = new char[length]; + (void) regerror( nErr, re, buffer, length ); + Bu::String s( buffer ); + delete[] buffer; + throw "???"; // BuildException( s.getStr() ); + } + bCompiled = true; + this->sSrc = sSrc; + + nSubStr = re->re_nsub+1; + paSubStr = (void *)(new regmatch_t[nSubStr]); +} + +int Bu::RegEx::getNumSubStrings() +{ + return nSubStr; +} + +bool Bu::RegEx::execute( const Bu::String &sSrc ) +{ + sTest = sSrc; + if( regexec( re, sSrc.getStr(), nSubStr, aSubStr, 0 ) ) + return false; + return true; +} + +void Bu::RegEx::getSubStringRange( int nIndex, int &iStart, int &iEnd ) +{ + iStart = aSubStr[nIndex].rm_so; + iEnd = aSubStr[nIndex].rm_eo; +} + +Bu::String Bu::RegEx::getSubString( int nIndex ) +{ +// regmatch_t *Subs = aSubStr; + return Bu::String( + sTest.getStr()+aSubStr[nIndex].rm_so, + aSubStr[nIndex].rm_eo - aSubStr[nIndex].rm_so + ); +} + diff --git a/src/experimental/regex.h b/src/experimental/regex.h new file mode 100644 index 0000000..f0aa5d5 --- /dev/null +++ b/src/experimental/regex.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#ifndef BU_REG_EX_H +#define BU_REG_EX_H + +#include "bu/string.h" + +#include + +namespace Bu +{ + class RegEx + { + public: + RegEx(); + RegEx( const Bu::String &sSrc ); + virtual ~RegEx(); + + void compile( const Bu::String &sSrc ); + int getNumSubStrings(); + bool execute( const Bu::String &sSrc ); + void getSubStringRange( int nIndex, int &iStart, int &iEnd ); + Bu::String getSubString( int nIndex ); + const Bu::String &getSource() + { + return sSrc; + } + + private: + Bu::String sSrc; + Bu::String sTest; + void *pRegEx; + bool bCompiled; + int nSubStr; + void *paSubStr; + }; +}; + +#endif diff --git a/src/experimental/regexengine.cpp b/src/experimental/regexengine.cpp new file mode 100644 index 0000000..72bc381 --- /dev/null +++ b/src/experimental/regexengine.cpp @@ -0,0 +1,5 @@ +#include "bu/regexengine.h" +#include "bu/utfstring.h" + +template class Bu::RegExEngine; +template class Bu::RegExEngine; diff --git a/src/experimental/regexengine.h b/src/experimental/regexengine.h new file mode 100644 index 0000000..ec181c1 --- /dev/null +++ b/src/experimental/regexengine.h @@ -0,0 +1,142 @@ +#ifndef BU_REG_EX_ENGINE_H +#define BU_REG_EX_ENGINE_H + +#include "bu/sharedcore.h" +#include "bu/array.h" +#include "bu/sio.h" + +namespace Bu +{ + template class RegExEngine; + + template + class RegExEngineCore + { + friend class RegExEngine; + friend class SharedCore, RegExEngineCore >; + private: + RegExEngineCore() + { + } + + virtual ~RegExEngineCore() + { + } + + class Range + { + public: + Range( chr cLower, chr cUpper, int iTrgState ) : + cLower( cLower ), cUpper( cUpper ), iTrgState( iTrgState ) + { + } + + chr cLower; + chr cUpper; + int iTrgState; + }; + + class State + { + public: + Bu::Array aRange; + }; + + int addState() + { + aState.append( State() ); + return aState.getSize()-1; + } + + void addCompletion( int iState, chr cLower, chr cUpper, int iTrgState ) + { + aState[iState].aRange.append( Range( cLower, cUpper, iTrgState ) ); + } + + template + bool match( const str &sIn, int &iSize, int &iCompletion ) + { + bool bMatch; + int iState = 0; + iSize = 0; + for( typename str::const_iterator i = sIn.begin(); i; i++ ) + { + Bu::sio << "Finding char " << *i << " in state " << iState + << ":" << Bu::sio.nl; + bMatch = false; + for( typename Bu::Array::iterator j = + aState[iState].aRange.begin(); j; j++ ) + { + Bu::sio << " Testing range " << (*j).cLower << " - " << (*j).cUpper << Bu::sio.nl; + if( *i >= (*j).cLower && *i <= (*j).cUpper ) + { + iState = (*j).iTrgState; + bMatch = true; + iSize++; + if( iState < 0 ) + { + iCompletion = iState; + return true; + } + } + } + if( bMatch == false ) + { + return false; + } + } + + iCompletion = 0; + return true; + } + + typedef Bu::Array StateArray; + StateArray aState; + }; + + template + class RegExEngine : public SharedCore, + RegExEngineCore > + { + private: + typedef class RegExEngine MyType; + typedef class RegExEngineCore Core; + typedef class Core::Range Range; + typedef class Core::State State; + + protected: + using SharedCore::core; + using SharedCore::_hardCopy; + using SharedCore::_resetCore; + using SharedCore::_allocateCore; + + public: + RegExEngine() + { + } + + virtual ~RegExEngine() + { + } + + int addState() + { + return core->addState(); + } + + void addCompletion( int iState, chr cLower, chr cUpper, int iTrgState ) + { + core->addCompletion( iState, cLower, cUpper, iTrgState ); + } + + template + bool match( const str &sIn, int &iSize, int &iCompletion ) + { + return core->match( sIn, iSize, iCompletion ); + } + + private: + }; +}; + +#endif diff --git a/src/experimental/xmlreader.cpp b/src/experimental/xmlreader.cpp new file mode 100644 index 0000000..ba7fb3d --- /dev/null +++ b/src/experimental/xmlreader.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#include "bu/xmlreader.h" +#include "bu/stream.h" + +namespace Bu { subExceptionDef( XmlException ) } + +Bu::XmlReader::XmlReader( Stream &rInput ) : + rInput( rInput ), + iCurToken( 0 ), + iNextToken( 0 ), + bIgnoreWS( true ) +{ + nextToken(); + stDocument(); +} + +Bu::XmlReader::~XmlReader() +{ +} + +void Bu::XmlReader::fillBuffer() +{ + if( rInput.isEos() ) + return; + char buf[1024]; + int iSize = rInput.read( buf, 1024 ); + sBuf.append( buf, iSize ); +} + +void Bu::XmlReader::cleanupBuffer( int iUsed ) +{ + for( int j = 0; j < iUsed; j++ ) + { + if( sBuf[j] == '\n' ) + { + spNextToken.iLine++; + spNextToken.iChar = 1; + } + else + { + spNextToken.iChar++; + } + } + + printf("--Deleting %d bytes from front of buffer.\n", iUsed ); + sBuf.trimFront( iUsed ); +} + +int Bu::XmlReader::nextToken() +{ + fillBuffer(); + + int iUsed = 1; + + iCurToken = iNextToken; + spCurToken = spNextToken; + + switch( sBuf[0] ) + { + case '<': + if( !strncmp( sBuf.getStr(), "' ) + { + iNextToken = tokXmlDeclEnd; + iUsed = 2; + } + else + { + iNextToken = '?'; + } + break; + + case ' ': + case '\t': + case '\n': + case '\r': + for( int j = 1;; j++ ) + { + if( j == sBuf.getSize() ) + { + if( rInput.isEos() ) + error("Reached end of input while waiting for whitespace to end."); + + fillBuffer(); + } + if( sBuf[j] == ' ' || sBuf[j] == '\t' || + sBuf[j] == '\n' || sBuf[j] == '\r' ) + iUsed++; + else + break; + } + sStr.clear(); + sStr.append( sBuf, iUsed ); + iNextToken = tokWS; + break; + + case '=': + iNextToken = sBuf[0]; + break; + + default: + if( (sBuf[0] >= 'a' && sBuf[0] <= 'z') || + (sBuf[0] >= 'A' && sBuf[0] <= 'Z') ) + { + for( int j = 1;; j++ ) + { + if( j == sBuf.getSize() ) + { + if( rInput.isEos() ) + error("Reached end of input while waiting for a string to end."); + + fillBuffer(); + } + if( (sBuf[j] >= 'a' && sBuf[j] <= 'z') || + (sBuf[j] >= 'A' && sBuf[j] <= 'Z') ) + iUsed++; + else + break; + } + sStr.clear(); + sStr.append( sBuf, iUsed ); + iNextToken = tokIdent; + } + } + + cleanupBuffer( iUsed ); + + return iCurToken; +} + +void Bu::XmlReader::error( const char *sMessage ) +{ + throw Bu::XmlException("%d:%d: %s", + spCurToken.iLine, spCurToken.iChar, sMessage ); +} + +void Bu::XmlReader::stDocument() +{ + stProlog(); +} + +void Bu::XmlReader::stProlog() +{ + stXmlDecl(); +} + +void Bu::XmlReader::stXmlDecl() +{ + if( nextToken() != tokXmlDeclHead ) + error("You must begin your xml file with a declaration: "); + if( nextToken() != tokIdent ) + error("A version comes first!"); + if( sStr != "version" ) + error("No, a version!"); +} + diff --git a/src/experimental/xmlreader.h b/src/experimental/xmlreader.h new file mode 100644 index 0000000..7cb44c9 --- /dev/null +++ b/src/experimental/xmlreader.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2007-2011 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#ifndef BU_XML_READER_H +#define BU_XML_READER_H + +#include "bu/string.h" +#include "bu/exceptionbase.h" + +namespace Bu +{ + class Stream; + + subExceptionDecl( XmlException ); + + class XmlReader + { + public: + XmlReader( Stream &rInput ); + virtual ~XmlReader(); + + private: + Stream &rInput; + int iCurToken; + int iNextToken; + Bu::String sBuf; + Bu::String sStr; + bool bIgnoreWS; + typedef struct StreamPos + { + StreamPos() : iLine( 1 ), iChar( 1 ) { } + int iLine; + int iChar; + } StreamPos; + StreamPos spCurToken; + StreamPos spNextToken; + + + enum + { + tokXmlDeclHead = 0x100, + tokXmlDeclEnd, + tokWS, + tokIdent, + tokString + }; + + void fillBuffer(); + void cleanupBuffer( int iUsed ); + int nextToken(); + + void stDocument(); + void stProlog(); + void stXmlDecl(); + + void error( const char *sMessage ); + }; +}; + +#endif -- cgit v1.2.3