From 11b5a91c5884d496744911f261ed6c2b053b9940 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Thu, 19 Aug 2010 06:28:17 +0000 Subject: Wow, it pretty much all works. the float format is a little funny, I treat it as a string, with a string header and then string data that is then turned into a float. It's pretty much how it's going to work, unless I come up with something revolutionary. --- default.bld | 2 +- src/dictionary.cpp | 11 +++-- src/float.cpp | 37 +++++++++++++++ src/float.h | 11 ++++- src/gatsstream.cpp | 85 ++++++++++++++++++++++++++++++++++- src/gatsstream.h | 31 +++++++++++++ src/object.cpp | 4 ++ src/unit/basic.unit | 19 ++++++++ src/unit/io.unit | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 317 insertions(+), 10 deletions(-) create mode 100644 src/unit/io.unit diff --git a/default.bld b/default.bld index ae6c61b..d78fbd3 100644 --- a/default.bld +++ b/default.bld @@ -46,7 +46,7 @@ target files("src/unit/*.unit").replace("src/","").replace(".unit","") requires "libbu++.a"; tag ["tests", "unit tests"]; CXXFLAGS += "-I."; - LDFLAGS += "-L. -lbu++ -lgats"; + LDFLAGS += "-L. -lgats -lbu++"; } rule "unit" diff --git a/src/dictionary.cpp b/src/dictionary.cpp index 2223c8b..1a9549f 100644 --- a/src/dictionary.cpp +++ b/src/dictionary.cpp @@ -77,9 +77,9 @@ void Gats::Dictionary::insert( const Bu::FString &sKey, bool b ) void Gats::Dictionary::insert( const Bu::FString &sKey, double d ) { -// Bu::Hash::insert( -// sKey, new Gats::Float( d ) -// ); + Bu::Hash::insert( + sKey, new Gats::Float( d ) + ); } void Gats::Dictionary::insert( const Bu::FString &sKey, const char *s ) @@ -117,14 +117,13 @@ int64_t Gats::Dictionary::getInt( const Bu::FString &sKey ) } double Gats::Dictionary::getFloat( const Bu::FString &sKey ) -{/* +{ Gats::Boolean *pOb = dynamic_cast( get( sKey ) ); if( pOb ) throw Bu::ExceptionBase("Cannot cast item '%s' to bool.", sKey.getStr() ); - return pOb->getValue();*/ - return 0.0; + return pOb->getValue(); } Bu::FString Gats::Dictionary::getStr( const Bu::FString &sKey ) diff --git a/src/float.cpp b/src/float.cpp index e69de29..e257b37 100644 --- a/src/float.cpp +++ b/src/float.cpp @@ -0,0 +1,37 @@ +#include "gats/float.h" +#include "gats/integer.h" + +Gats::Float::Float() : + fVal( 0.0 ) +{ +} + +Gats::Float::Float( double f ) : + fVal( f ) +{ +} + +Gats::Float::~Float() +{ +} + +void Gats::Float::write( Bu::Stream &rOut ) const +{ + char buf[50]; + + int iSize = snprintf( buf, 50, "%la", fVal ); + + rOut.write("f", 1 ); + Gats::Integer::writePackedInt( rOut, iSize ); + rOut.write( buf, iSize ); +} + +void Gats::Float::read( Bu::Stream &rIn, char cType ) +{ + int iSize; + Gats::Integer::readPackedInt( rIn, iSize ); + char buf[50]; + rIn.read( buf, iSize ); + sscanf( buf, "%la", &fVal ); +} + diff --git a/src/float.h b/src/float.h index cf20010..1b3a06a 100644 --- a/src/float.h +++ b/src/float.h @@ -1,16 +1,25 @@ #ifndef GATS_FLOAT_H #define GATS_FLOAT_H +#include "gats/object.h" + namespace Gats { - class Float + class Float : public Gats::Object { public: Float(); Float( double f ); virtual ~Float(); + virtual Type getType() const { return typeFloat; } + double getValue() const { return fVal; } + + virtual void write( Bu::Stream &rOut ) const; + virtual void read( Bu::Stream &rIn, char cType ); + private: + double fVal; }; } diff --git a/src/gatsstream.cpp b/src/gatsstream.cpp index 4b9cadf..38adfbb 100644 --- a/src/gatsstream.cpp +++ b/src/gatsstream.cpp @@ -1,7 +1,9 @@ #include "gats/gatsstream.h" #include "gats/object.h" -#include +#include + +// #include #include using namespace Bu; @@ -16,7 +18,59 @@ Gats::GatsStream::~GatsStream() Gats::Object *Gats::GatsStream::readObject() { + char buf[1500]; + + // sio << "Gats::GatsStream::readObject(): Scanning for object header." << sio.nl; + do + { + if( qbRead.getSize() < 5 ) + { + // sio << "Gats::GatsStream::readObject(): reading header data, need 5b, have " << qbRead.getSize() << "b." << sio.nl; + int iRead = rStream.read( buf, 5-qbRead.getSize() ); + qbRead.write( buf, iRead ); + + if( qbRead.getSize() < 5 ) + return NULL; + } + } while( !skipReadNulls() ); + + uint8_t uVer; + qbRead.peek( &uVer, 1 ); + // sio << "Gats::GatsStream::readObject(): Packet version: " << (int)uVer << sio.nl; + + int32_t iSize; + qbRead.peek( &iSize, 4, 1 ); + iSize = ntohl( iSize ); + // sio << "Gats::GatsStream::readObject(): Header read, looking for " << iSize << "b, we have " << qbRead.getSize() << "b." << sio.nl; + while( qbRead.getSize() < iSize ) + { + int32_t iRead = iSize - qbRead.getSize(); + if( iRead > 1500 ) + iRead = 1500; + // sio << "Gats::GatsStream::readObject(): Attempting to read " << iRead << "b." << sio.nl; + int32_t iReal = rStream.read( buf, iRead ); + // sio << "Gats::GatsStream::readObject(): Read " << iReal << "b." << sio.nl; + qbRead.write( buf, iReal ); + if( iReal < iRead ) + { + // sio << "Gats::GatsStream::readObject(): Insufficient data read in block, bailing on read." << sio.nl; + return NULL; + } + } + + if( qbRead.getSize() < iSize ) + { + // sio << "Gats::GatsStream::readObject(): Somehow, we still don't have enough data, bailing." << sio.nl; + return NULL; + } + + // sio << "Gats::GatsStream::readObject(): We have " << qbRead.getSize() << "b of " << iSize << "b, time to read the object." << sio.nl; + qbRead.seek( 5 ); + Gats::Object *pObj = Gats::Object::read( qbRead ); + + // sio << "Gats::GatsStream::readObject(): Read completed, there are " << qbRead.getSize() << "b left in the buffer." << sio.nl; + return pObj; } void Gats::GatsStream::writeObject( Gats::Object *pObject ) @@ -24,6 +78,33 @@ void Gats::GatsStream::writeObject( Gats::Object *pObject ) Bu::NullStream ns; pObject->write( ns ); - sio << "Object consumed " << ns.tell() << "b." << sio.nl; + uint8_t uBuf = 1; + int32_t iSize = htonl( ns.tell()+5 ); + rStream.write( &uBuf, 1 ); + rStream.write( &iSize, 4 ); + pObject->write( rStream ); + + // sio << "Object consumed " << ns.tell() << "b." << sio.nl; +} + +bool Gats::GatsStream::skipReadNulls() +{ + char buf; + + // sio << "Gats::GatsStream::skipReadNulls(): Scanning for nulls, " << qbRead.getSize() << "b." << sio.nl; + bool bHaveSeeked = false; + for(;;) + { + if( qbRead.peek( &buf, 1 ) == 0 ) + return false; + if( buf != 0 ) + return !bHaveSeeked; //true; + else + { + // sio << "Gats::GatsStream::skipReadNulls(): Null byte read, not header yet..." << sio.nl; + qbRead.seek( 1 ); + bHaveSeeked = true; + } + } } diff --git a/src/gatsstream.h b/src/gatsstream.h index d668c28..b5efb87 100644 --- a/src/gatsstream.h +++ b/src/gatsstream.h @@ -2,6 +2,7 @@ #define GATS_STREAM_H #include +#include namespace Gats { @@ -13,11 +14,41 @@ namespace Gats GatsStream( Bu::Stream &rStream ); virtual ~GatsStream(); + /** + * Read an object packet from the assosiated stream. This will make + * every effort to only read exactly enough data to describe one packet, + * in case you want to do other things with your stream. It will + * automatically skip NULL byte spacing between packets, which makes + * a convinient padding method for encrypted data streams. Since + * sizing information is available in the packet header exact amounts + * of data can be read, however this function doesn't assume that it + * can read the entire object in one operation. If it fails to read + * a complete packet in one call, it will keep the data it's read so + * far buffered and return NULL, ready for another attempt. You can + * use the function hasReadBuffer() to deterimne if readObject() + * has read part of an object packet or not. If readObject returns + * non-null then hasReadBuffer should return false on it's next call. + */ Gats::Object *readObject(); + + /** + * Write an object + */ void writeObject( Gats::Object *pObject ); + /** + * Tells you if there is data still in the read buffer, i.e. that a + * packet is part way through being read. If readObject has returned + * non-null in the most recent call, this should always be false. + */ + bool hasReadBuffer() { return qbRead.getSize() > 0; } + + private: + bool skipReadNulls(); + private: Bu::Stream &rStream; + Bu::QueueBuf qbRead; }; }; diff --git a/src/object.cpp b/src/object.cpp index 4c1f3ca..9176b49 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -45,6 +45,10 @@ Gats::Object *Gats::Object::read( Bu::Stream &rIn ) pObj = new Gats::Dictionary(); break; + case 'f': + pObj = new Gats::Float(); + break; + case 'e': return NULL; diff --git a/src/unit/basic.unit b/src/unit/basic.unit index 744d679..d0309ee 100644 --- a/src/unit/basic.unit +++ b/src/unit/basic.unit @@ -18,6 +18,7 @@ #include "bu/sio.h" #include +#include using namespace Bu; @@ -120,6 +121,24 @@ suite Basic } } + test floats + { + Bu::MemBuf mb; + + Gats::Float( M_PI ).write( mb ); + + mb.setPos( 0 ); + + Gats::Object *pObj = Gats::Object::read( mb ); + unitTest( pObj != NULL ); + unitTest( pObj->getType() == Gats::typeFloat ); + Gats::Float *pFlt = dynamic_cast(pObj); + sio << "old = " << M_PI << ", new = " << pFlt->getValue() << sio.nl; + unitTest( pFlt->getValue() == M_PI ); + + delete pObj; + } + test dictionary { MemBuf mb; diff --git a/src/unit/io.unit b/src/unit/io.unit new file mode 100644 index 0000000..3e9c82c --- /dev/null +++ b/src/unit/io.unit @@ -0,0 +1,127 @@ +// vim: syntax=cpp +/* + * Copyright (C) 2007-2010 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 "gats/dictionary.h" +#include "gats/integer.h" +#include "gats/float.h" +#include "gats/list.h" +#include "gats/boolean.h" +#include "gats/string.h" +#include "gats/gatsstream.h" + +#include "bu/membuf.h" +#include "bu/list.h" +#include "bu/sio.h" + +#include + +using namespace Bu; + +suite Basic +{ + test basic + { + Bu::FString sTmpFileName("temp-XXXXXXXXX"); + Bu::File fIo = tempFile( sTmpFileName ); + + { + Gats::Dictionary dTest; + dTest.insert("age", 27 ); + dTest.insert("firstName", "Mike"); + dTest.insert("lastName", "Buland"); + dTest.insert("awake", true ); + + Gats::GatsStream sGats( fIo ); + sGats.writeObject( &dTest ); + } + + fIo.setPos( 0 ); + + { + Gats::GatsStream sGats( fIo ); + Gats::Object *pObj = sGats.readObject(); + unitTest( pObj != NULL ); + unitTest( pObj->getType() == Gats::typeDictionary ); + Gats::Dictionary *pDic = dynamic_cast(pObj); + unitTest( pDic->getSize() == 4 ); + unitTest( pDic->getInt("age") == 27 ); + unitTest( pDic->getStr("firstName") == "Mike" ); + unitTest( pDic->getStr("lastName") == "Buland" ); + unitTest( pDic->getBool("awake") == true ); + + delete pDic; + } + } + + test spacers + { + Bu::FString sTmpFileName("temp-XXXXXXXXX"); + Bu::File fIo = tempFile( sTmpFileName ); + + { + Gats::GatsStream sGats( fIo ); + Gats::Integer i( -157 ); + sGats.writeObject( &i ); + fIo.write( "\x00\x00\x00", 3 ); + Gats::String s("negative one hundred and fifty seven"); + sGats.writeObject( &s ); + } + + fIo.setPos( 0 ); + + { + Gats::GatsStream sGats( fIo ); + Gats::Object *pObj1 = sGats.readObject(); + unitTest( pObj1 != NULL ); + unitTest( pObj1->getType() == Gats::typeInteger ); + unitTest( dynamic_cast(pObj1)->getValue() == -157 ); + + Gats::Object *pObj2 = sGats.readObject(); + unitTest( pObj2 != NULL ); + unitTest( pObj2->getType() == Gats::typeString ); + unitTest( *dynamic_cast(pObj2) == + "negative one hundred and fifty seven" ); + + delete pObj1; + delete pObj2; + } + } + test biggerSpacers + { + Bu::FString sTmpFileName("temp-XXXXXXXXX"); + Bu::File fIo = tempFile( sTmpFileName ); + + { + Gats::GatsStream sGats( fIo ); + Gats::Integer i( -157 ); + sGats.writeObject( &i ); + fIo.write( "\x00\x00\x00\x00\x00\x00\x00\x00\x00", 9 ); + Gats::String s("negative one hundred and fifty seven"); + sGats.writeObject( &s ); + } + + fIo.setPos( 0 ); + + { + Gats::GatsStream sGats( fIo ); + Gats::Object *pObj1 = sGats.readObject(); + unitTest( pObj1 != NULL ); + unitTest( pObj1->getType() == Gats::typeInteger ); + unitTest( dynamic_cast(pObj1)->getValue() == -157 ); + + Gats::Object *pObj2 = sGats.readObject(); + unitTest( pObj2 != NULL ); + unitTest( pObj2->getType() == Gats::typeString ); + unitTest( *dynamic_cast(pObj2) == + "negative one hundred and fifty seven" ); + + delete pObj1; + delete pObj2; + } + } +} -- cgit v1.2.3