diff options
Diffstat (limited to 'src/stable')
154 files changed, 24481 insertions, 0 deletions
| diff --git a/src/stable/archival.cpp b/src/stable/archival.cpp new file mode 100644 index 0000000..687e8a3 --- /dev/null +++ b/src/stable/archival.cpp | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/archival.h" | ||
| 9 | |||
| 10 | Bu::Archival::Archival() | ||
| 11 | { | ||
| 12 | } | ||
| 13 | |||
| 14 | Bu::Archival::~Archival() | ||
| 15 | { | ||
| 16 | } | ||
| 17 | |||
| 18 | Bu::ArchiveBase &Bu::operator<<(Bu::ArchiveBase &s, const Bu::Archival &p) | ||
| 19 | { | ||
| 20 | const_cast<Bu::Archival &>(p).archive( s ); | ||
| 21 | return s; | ||
| 22 | } | ||
| 23 | |||
| 24 | Bu::ArchiveBase &Bu::operator<<(Bu::ArchiveBase &s, Bu::Archival &p) | ||
| 25 | { | ||
| 26 | p.archive( s ); | ||
| 27 | return s; | ||
| 28 | } | ||
| 29 | |||
| 30 | Bu::ArchiveBase &Bu::operator>>(Bu::ArchiveBase &s, Bu::Archival &p) | ||
| 31 | { | ||
| 32 | p.archive( s ); | ||
| 33 | return s; | ||
| 34 | } | ||
| 35 | |||
| diff --git a/src/stable/archival.h b/src/stable/archival.h new file mode 100644 index 0000000..946167a --- /dev/null +++ b/src/stable/archival.h | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_ARCHIVAL_H | ||
| 9 | #define BU_ARCHIVAL_H | ||
| 10 | |||
| 11 | #include "bu/archivebase.h" | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * The base class for any class you want to archive. Simply include this as | ||
| 17 | * a base class, implement the purely virtual archive function and you've | ||
| 18 | * got an easily archiveable class. | ||
| 19 | * | ||
| 20 | * Archival: "of or pertaining to archives or valuable records; contained | ||
| 21 | * in or comprising such archives or records." | ||
| 22 | */ | ||
| 23 | class Archival | ||
| 24 | { | ||
| 25 | public: | ||
| 26 | /** | ||
| 27 | * Does nothing, here for completeness. | ||
| 28 | */ | ||
| 29 | Archival(); | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Here to ensure the deconstructor is virtual. | ||
| 33 | */ | ||
| 34 | virtual ~Archival(); | ||
| 35 | |||
| 36 | /** | ||
| 37 | * This is the main workhorse of the archive system, just override and | ||
| 38 | * you've got a archiveable class. A reference to the Archive | ||
| 39 | * used is passed in as your only parameter, query it to discover if | ||
| 40 | * you are loading or saving. | ||
| 41 | * @param ar A reference to the Archive object to use. | ||
| 42 | */ | ||
| 43 | virtual void archive( class ArchiveBase &ar )=0; | ||
| 44 | }; | ||
| 45 | |||
| 46 | ArchiveBase &operator<<(ArchiveBase &, const class Bu::Archival &); | ||
| 47 | ArchiveBase &operator<<(ArchiveBase &, class Bu::Archival &); | ||
| 48 | ArchiveBase &operator>>(ArchiveBase &, class Bu::Archival &); | ||
| 49 | |||
| 50 | } | ||
| 51 | |||
| 52 | #endif | ||
| diff --git a/src/stable/archive.cpp b/src/stable/archive.cpp new file mode 100644 index 0000000..d300a87 --- /dev/null +++ b/src/stable/archive.cpp | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/archive.h" | ||
| 9 | #include "bu/stream.h" | ||
| 10 | #include "bu/archival.h" | ||
| 11 | |||
| 12 | #include "bu/sio.h" | ||
| 13 | |||
| 14 | Bu::Archive::Archive( Stream &rStream, bool bLoading ) : | ||
| 15 | bLoading( bLoading ), | ||
| 16 | rStream( rStream ), | ||
| 17 | nNextID( 1 ) | ||
| 18 | { | ||
| 19 | } | ||
| 20 | |||
| 21 | Bu::Archive::~Archive() | ||
| 22 | { | ||
| 23 | } | ||
| 24 | |||
| 25 | void Bu::Archive::write( const void *pData, size_t nSize ) | ||
| 26 | { | ||
| 27 | if( nSize == 0 || pData == NULL ) | ||
| 28 | return; | ||
| 29 | |||
| 30 | rStream.write( (const char *)pData, nSize ); | ||
| 31 | } | ||
| 32 | |||
| 33 | void Bu::Archive::read( void *pData, size_t nSize ) | ||
| 34 | { | ||
| 35 | if( nSize == 0 || pData == NULL ) | ||
| 36 | return; | ||
| 37 | |||
| 38 | if( rStream.read( (char *)pData, nSize ) < nSize ) | ||
| 39 | throw Bu::ExceptionBase("Insufficient data to unarchive object."); | ||
| 40 | } | ||
| 41 | |||
| 42 | void Bu::Archive::close() | ||
| 43 | { | ||
| 44 | rStream.close(); | ||
| 45 | } | ||
| 46 | |||
| 47 | bool Bu::Archive::isLoading() | ||
| 48 | { | ||
| 49 | return bLoading; | ||
| 50 | } | ||
| 51 | |||
| 52 | uint32_t Bu::Archive::getID( const void *ptr ) | ||
| 53 | { | ||
| 54 | if( hPtrID.has( (ptrdiff_t)ptr ) ) | ||
| 55 | return hPtrID.get( (ptrdiff_t)ptr ); | ||
| 56 | hPtrID.insert( (ptrdiff_t)ptr, nNextID ); | ||
| 57 | return nNextID++; | ||
| 58 | } | ||
| 59 | |||
| 60 | void Bu::Archive::assocPtrID( void **ptr, uint32_t id ) | ||
| 61 | { | ||
| 62 | if( hPtrID.has( id ) ) | ||
| 63 | { | ||
| 64 | *ptr = (void *)hPtrID.get( id ); | ||
| 65 | return; | ||
| 66 | } | ||
| 67 | |||
| 68 | if( !hPtrDest.has( id ) ) | ||
| 69 | hPtrDest.insert( id, List<void **>() ); | ||
| 70 | |||
| 71 | hPtrDest[id].getValue().append( ptr ); | ||
| 72 | } | ||
| 73 | |||
| 74 | void Bu::Archive::readID( const void *ptr, uint32_t id ) | ||
| 75 | { | ||
| 76 | hPtrID.insert( id, (ptrdiff_t)ptr ); | ||
| 77 | |||
| 78 | if( hPtrDest.has( id ) ) | ||
| 79 | { | ||
| 80 | Bu::List<void **> &l = hPtrDest.get( id ); | ||
| 81 | for( Bu::List<void **>::iterator i = l.begin(); i != l.end(); i++ ) | ||
| 82 | { | ||
| 83 | *(*i) = (void *)ptr; | ||
| 84 | } | ||
| 85 | |||
| 86 | hPtrDest.erase( id ); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| diff --git a/src/stable/archive.h b/src/stable/archive.h new file mode 100644 index 0000000..61474a4 --- /dev/null +++ b/src/stable/archive.h | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_ARCHIVE_H | ||
| 9 | #define BU_ARCHIVE_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include "bu/archivebase.h" | ||
| 13 | #include "bu/hash.h" | ||
| 14 | #include "bu/util.h" | ||
| 15 | #include "bu/variant.h" | ||
| 16 | |||
| 17 | namespace Bu | ||
| 18 | { | ||
| 19 | class Archival; | ||
| 20 | class Stream; | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Provides a framework for serialization of objects and primitives. The | ||
| 24 | * archive will handle any basic primitive, a few special types, like char * | ||
| 25 | * strings, as well as STL classes and anything that inherits from the | ||
| 26 | * Archival class. Each Archive operates on a Stream, so you can send the | ||
| 27 | * data using an Archive almost anywhere. | ||
| 28 | * | ||
| 29 | * In order to use an Archive to store something to a file, try something | ||
| 30 | * like: | ||
| 31 | *@code | ||
| 32 | * File sOut("output", "wb"); // This is a stream subclass | ||
| 33 | * Archive ar( sOut, Archive::save ); | ||
| 34 | * ar << myClass; | ||
| 35 | @endcode | ||
| 36 | * In this example myClass is any class that inherits from Archival. When | ||
| 37 | * the storage operator is called, the Archival::archive() function in the | ||
| 38 | * myClass object is called with a reference to the Archive. This can be | ||
| 39 | * handled in one of two ways: | ||
| 40 | *@code | ||
| 41 | * void MyClass::archive( Archive &ar ) | ||
| 42 | * { | ||
| 43 | * ar && sName && nAge && sJob; | ||
| 44 | * } | ||
| 45 | @endcode | ||
| 46 | * Here we don't worry about weather we're loading or saving by using the | ||
| 47 | * smart && operator. This allows us to write very consistent, very simple | ||
| 48 | * archive functions that really do a lot of work. If we wanted to do | ||
| 49 | * something different in the case of loading or saving we would do: | ||
| 50 | *@code | ||
| 51 | * void MyClass::archive( Archive &ar ) | ||
| 52 | * { | ||
| 53 | * if( ar.isLoading() ) | ||
| 54 | * { | ||
| 55 | * ar >> sName >> nAge >> sJob; | ||
| 56 | * } else | ||
| 57 | * { | ||
| 58 | * ar << sName << nAge << sJob; | ||
| 59 | * } | ||
| 60 | * } | ||
| 61 | @endcode | ||
| 62 | * Archive currently does not provide facility to make fully portable | ||
| 63 | * archives. For example, it will not convert between endianness for you, | ||
| 64 | * nor will it take into account differences between primitive sizes on | ||
| 65 | * different platforms. This, at the moment, is up to the user to ensure. | ||
| 66 | * One way of dealing with the latter problem is to make sure and use | ||
| 67 | * explicit primitive types from the stdint.h header, i.e. int32_t. | ||
| 68 | */ | ||
| 69 | class Archive : public ArchiveBase | ||
| 70 | { | ||
| 71 | private: | ||
| 72 | bool bLoading; | ||
| 73 | public: | ||
| 74 | bool isLoading(); | ||
| 75 | |||
| 76 | enum | ||
| 77 | { | ||
| 78 | load = true, | ||
| 79 | save = false | ||
| 80 | }; | ||
| 81 | |||
| 82 | Archive( Stream &rStream, bool bLoading ); | ||
| 83 | virtual ~Archive(); | ||
| 84 | virtual void close(); | ||
| 85 | |||
| 86 | virtual void write( const void *pData, size_t iSize ); | ||
| 87 | virtual void read( void *pData, size_t iSize ); | ||
| 88 | |||
| 89 | /** | ||
| 90 | * For storage, get an ID for the pointer to the object you're going to | ||
| 91 | * write. | ||
| 92 | */ | ||
| 93 | uint32_t getID( const void *ptr ); | ||
| 94 | |||
| 95 | /** | ||
| 96 | * For loading. Assosiates an empty pointer with an id. When you wind | ||
| 97 | * up loading an id reference to a pointer for an object that may or | ||
| 98 | * may not have loaded yet, call this with the id, if it has been loaded | ||
| 99 | * already, you'll immediately get a pointer, if not, it will write one | ||
| 100 | * for you when the time comes. | ||
| 101 | */ | ||
| 102 | void assocPtrID( void **ptr, uint32_t id ); | ||
| 103 | |||
| 104 | /** | ||
| 105 | * For loading. Call this when you load an object that other things may | ||
| 106 | * have pointers to. It will assosiate every pointer that's been | ||
| 107 | * registered with assocPtrID to the pointer passed in, and id passed | ||
| 108 | * in. It will also set things up so future calls to assocPtrID will | ||
| 109 | * automatically succeed immediately. | ||
| 110 | */ | ||
| 111 | void readID( const void *ptr, uint32_t id ); | ||
| 112 | |||
| 113 | template<typename t> | ||
| 114 | void setProp( const Bu::String &sId, const t &val ) | ||
| 115 | { | ||
| 116 | if( !hProps.has( sId ) ) | ||
| 117 | { | ||
| 118 | hProps.insert( sId, Variant() ); | ||
| 119 | } | ||
| 120 | hProps.get( sId ) = val; | ||
| 121 | } | ||
| 122 | |||
| 123 | template<typename t> | ||
| 124 | t getProp( const Bu::String &sId ) | ||
| 125 | { | ||
| 126 | return hProps.get( sId ); | ||
| 127 | } | ||
| 128 | |||
| 129 | private: | ||
| 130 | Stream &rStream; | ||
| 131 | uint32_t nNextID; | ||
| 132 | Hash<uint32_t,uint32_t> hPtrID; | ||
| 133 | Hash<uint32_t,List<void **> > hPtrDest; | ||
| 134 | Hash<Bu::String, Variant> hProps; | ||
| 135 | }; | ||
| 136 | } | ||
| 137 | |||
| 138 | #endif | ||
| diff --git a/src/stable/archivebase.cpp b/src/stable/archivebase.cpp new file mode 100644 index 0000000..d00b1a5 --- /dev/null +++ b/src/stable/archivebase.cpp | |||
| @@ -0,0 +1,197 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/archivebase.h" | ||
| 9 | |||
| 10 | Bu::ArchiveBase::ArchiveBase() | ||
| 11 | { | ||
| 12 | } | ||
| 13 | |||
| 14 | Bu::ArchiveBase::~ArchiveBase() | ||
| 15 | { | ||
| 16 | } | ||
| 17 | |||
| 18 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, bool p) | ||
| 19 | { | ||
| 20 | ar.write( &p, sizeof(p) ); | ||
| 21 | return ar; | ||
| 22 | } | ||
| 23 | |||
| 24 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, char p) | ||
| 25 | { | ||
| 26 | ar.write( &p, sizeof(p) ); | ||
| 27 | return ar; | ||
| 28 | } | ||
| 29 | |||
| 30 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, signed char p) | ||
| 31 | { | ||
| 32 | ar.write( &p, sizeof(p) ); | ||
| 33 | return ar; | ||
| 34 | } | ||
| 35 | |||
| 36 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, unsigned char p) | ||
| 37 | { | ||
| 38 | ar.write( &p, sizeof(p) ); | ||
| 39 | return ar; | ||
| 40 | } | ||
| 41 | |||
| 42 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, signed short p) | ||
| 43 | { | ||
| 44 | ar.write( &p, sizeof(p) ); | ||
| 45 | return ar; | ||
| 46 | } | ||
| 47 | |||
| 48 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, unsigned short p) | ||
| 49 | { | ||
| 50 | ar.write( &p, sizeof(p) ); | ||
| 51 | return ar; | ||
| 52 | } | ||
| 53 | |||
| 54 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, signed int p) | ||
| 55 | { | ||
| 56 | ar.write( &p, sizeof(p) ); | ||
| 57 | return ar; | ||
| 58 | } | ||
| 59 | |||
| 60 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, unsigned int p) | ||
| 61 | { | ||
| 62 | ar.write( &p, sizeof(p) ); | ||
| 63 | return ar; | ||
| 64 | } | ||
| 65 | |||
| 66 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, signed long p) | ||
| 67 | { | ||
| 68 | ar.write( &p, sizeof(p) ); | ||
| 69 | return ar; | ||
| 70 | } | ||
| 71 | |||
| 72 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, unsigned long p) | ||
| 73 | { | ||
| 74 | ar.write( &p, sizeof(p) ); | ||
| 75 | return ar; | ||
| 76 | } | ||
| 77 | |||
| 78 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, signed long long p) | ||
| 79 | { | ||
| 80 | ar.write( &p, sizeof(p) ); | ||
| 81 | return ar; | ||
| 82 | } | ||
| 83 | |||
| 84 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, unsigned long long p) | ||
| 85 | { | ||
| 86 | ar.write( &p, sizeof(p) ); | ||
| 87 | return ar; | ||
| 88 | } | ||
| 89 | |||
| 90 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, float p) | ||
| 91 | { | ||
| 92 | ar.write( &p, sizeof(p) ); | ||
| 93 | return ar; | ||
| 94 | } | ||
| 95 | |||
| 96 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, double p) | ||
| 97 | { | ||
| 98 | ar.write( &p, sizeof(p) ); | ||
| 99 | return ar; | ||
| 100 | } | ||
| 101 | |||
| 102 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, long double p) | ||
| 103 | { | ||
| 104 | ar.write( &p, sizeof(p) ); | ||
| 105 | return ar; | ||
| 106 | } | ||
| 107 | |||
| 108 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, bool &p) | ||
| 109 | { | ||
| 110 | ar.read( &p, sizeof(p) ); | ||
| 111 | return ar; | ||
| 112 | } | ||
| 113 | |||
| 114 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, char &p) | ||
| 115 | { | ||
| 116 | ar.read( &p, sizeof(p) ); | ||
| 117 | return ar; | ||
| 118 | } | ||
| 119 | |||
| 120 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, signed char &p) | ||
| 121 | { | ||
| 122 | ar.read( &p, sizeof(p) ); | ||
| 123 | return ar; | ||
| 124 | } | ||
| 125 | |||
| 126 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, unsigned char &p) | ||
| 127 | { | ||
| 128 | ar.read( &p, sizeof(p) ); | ||
| 129 | return ar; | ||
| 130 | } | ||
| 131 | |||
| 132 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, signed short &p) | ||
| 133 | { | ||
| 134 | ar.read( &p, sizeof(p) ); | ||
| 135 | return ar; | ||
| 136 | } | ||
| 137 | |||
| 138 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, unsigned short &p) | ||
| 139 | { | ||
| 140 | ar.read( &p, sizeof(p) ); | ||
| 141 | return ar; | ||
| 142 | } | ||
| 143 | |||
| 144 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, signed int &p) | ||
| 145 | { | ||
| 146 | ar.read( &p, sizeof(p) ); | ||
| 147 | return ar; | ||
| 148 | } | ||
| 149 | |||
| 150 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, unsigned int &p) | ||
| 151 | { | ||
| 152 | ar.read( &p, sizeof(p) ); | ||
| 153 | return ar; | ||
| 154 | } | ||
| 155 | |||
| 156 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, signed long &p) | ||
| 157 | { | ||
| 158 | ar.read( &p, sizeof(p) ); | ||
| 159 | return ar; | ||
| 160 | } | ||
| 161 | |||
| 162 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, unsigned long &p) | ||
| 163 | { | ||
| 164 | ar.read( &p, sizeof(p) ); | ||
| 165 | return ar; | ||
| 166 | } | ||
| 167 | |||
| 168 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, signed long long &p) | ||
| 169 | { | ||
| 170 | ar.read( &p, sizeof(p) ); | ||
| 171 | return ar; | ||
| 172 | } | ||
| 173 | |||
| 174 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, unsigned long long &p) | ||
| 175 | { | ||
| 176 | ar.read( &p, sizeof(p) ); | ||
| 177 | return ar; | ||
| 178 | } | ||
| 179 | |||
| 180 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, float &p) | ||
| 181 | { | ||
| 182 | ar.read( &p, sizeof(p) ); | ||
| 183 | return ar; | ||
| 184 | } | ||
| 185 | |||
| 186 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, double &p) | ||
| 187 | { | ||
| 188 | ar.read( &p, sizeof(p) ); | ||
| 189 | return ar; | ||
| 190 | } | ||
| 191 | |||
| 192 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, long double &p) | ||
| 193 | { | ||
| 194 | ar.read( &p, sizeof(p) ); | ||
| 195 | return ar; | ||
| 196 | } | ||
| 197 | |||
| diff --git a/src/stable/archivebase.h b/src/stable/archivebase.h new file mode 100644 index 0000000..4745d91 --- /dev/null +++ b/src/stable/archivebase.h | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_ARCHIVE_BASE_H | ||
| 9 | #define BU_ARCHIVE_BASE_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include <unistd.h> | ||
| 13 | |||
| 14 | namespace Bu | ||
| 15 | { | ||
| 16 | class ArchiveBase | ||
| 17 | { | ||
| 18 | public: | ||
| 19 | ArchiveBase(); | ||
| 20 | virtual ~ArchiveBase(); | ||
| 21 | |||
| 22 | virtual void close()=0; | ||
| 23 | virtual void write( const void *pData, size_t iLength )=0; | ||
| 24 | virtual void read( void *pData, size_t iLength )=0; | ||
| 25 | virtual bool isLoading()=0; | ||
| 26 | }; | ||
| 27 | |||
| 28 | template<typename T> ArchiveBase &operator&&( ArchiveBase &ar, T &dat ) | ||
| 29 | { | ||
| 30 | if( ar.isLoading() ) | ||
| 31 | { | ||
| 32 | return ar >> dat; | ||
| 33 | } | ||
| 34 | else | ||
| 35 | { | ||
| 36 | return ar << dat; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | ArchiveBase &operator<<( ArchiveBase &ar, bool p ); | ||
| 41 | ArchiveBase &operator<<( ArchiveBase &ar, char p ); | ||
| 42 | ArchiveBase &operator<<( ArchiveBase &ar, signed char p ); | ||
| 43 | ArchiveBase &operator<<( ArchiveBase &ar, unsigned char p ); | ||
| 44 | ArchiveBase &operator<<( ArchiveBase &ar, signed short p ); | ||
| 45 | ArchiveBase &operator<<( ArchiveBase &ar, unsigned short p ); | ||
| 46 | ArchiveBase &operator<<( ArchiveBase &ar, signed int p ); | ||
| 47 | ArchiveBase &operator<<( ArchiveBase &ar, unsigned int p ); | ||
| 48 | ArchiveBase &operator<<( ArchiveBase &ar, signed long p ); | ||
| 49 | ArchiveBase &operator<<( ArchiveBase &ar, unsigned long p ); | ||
| 50 | ArchiveBase &operator<<( ArchiveBase &ar, signed long long p ); | ||
| 51 | ArchiveBase &operator<<( ArchiveBase &ar, unsigned long long p ); | ||
| 52 | ArchiveBase &operator<<( ArchiveBase &ar, float p ); | ||
| 53 | ArchiveBase &operator<<( ArchiveBase &ar, double p ); | ||
| 54 | ArchiveBase &operator<<( ArchiveBase &ar, long double p ); | ||
| 55 | |||
| 56 | ArchiveBase &operator>>( ArchiveBase &ar, bool &p ); | ||
| 57 | ArchiveBase &operator>>( ArchiveBase &ar, char &p ); | ||
| 58 | ArchiveBase &operator>>( ArchiveBase &ar, signed char &p ); | ||
| 59 | ArchiveBase &operator>>( ArchiveBase &ar, unsigned char &p ); | ||
| 60 | ArchiveBase &operator>>( ArchiveBase &ar, signed short &p ); | ||
| 61 | ArchiveBase &operator>>( ArchiveBase &ar, unsigned short &p ); | ||
| 62 | ArchiveBase &operator>>( ArchiveBase &ar, signed int &p ); | ||
| 63 | ArchiveBase &operator>>( ArchiveBase &ar, unsigned int &p ); | ||
| 64 | ArchiveBase &operator>>( ArchiveBase &ar, signed long &p ); | ||
| 65 | ArchiveBase &operator>>( ArchiveBase &ar, unsigned long &p ); | ||
| 66 | ArchiveBase &operator>>( ArchiveBase &ar, signed long long &p ); | ||
| 67 | ArchiveBase &operator>>( ArchiveBase &ar, unsigned long long &p ); | ||
| 68 | ArchiveBase &operator>>( ArchiveBase &ar, float &p ); | ||
| 69 | ArchiveBase &operator>>( ArchiveBase &ar, double &p ); | ||
| 70 | ArchiveBase &operator>>( ArchiveBase &ar, long double &p ); | ||
| 71 | |||
| 72 | |||
| 73 | }; | ||
| 74 | |||
| 75 | #endif | ||
| diff --git a/src/stable/array.cpp b/src/stable/array.cpp new file mode 100644 index 0000000..b776fed --- /dev/null +++ b/src/stable/array.cpp | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/array.h" | ||
| 9 | |||
| 10 | namespace Bu { subExceptionDef( ArrayException ) } | ||
| diff --git a/src/stable/array.h b/src/stable/array.h new file mode 100644 index 0000000..fcd800e --- /dev/null +++ b/src/stable/array.h | |||
| @@ -0,0 +1,703 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_ARRAY_H | ||
| 9 | #define BU_ARRAY_H | ||
| 10 | |||
| 11 | #include <memory> | ||
| 12 | #include "bu/exceptionbase.h" | ||
| 13 | #include "bu/archivebase.h" | ||
| 14 | #include "bu/sharedcore.h" | ||
| 15 | |||
| 16 | namespace Bu | ||
| 17 | { | ||
| 18 | subExceptionDecl( ArrayException ) | ||
| 19 | |||
| 20 | template<typename value, int inc, typename valuealloc> | ||
| 21 | class Array; | ||
| 22 | |||
| 23 | /** @cond DEVEL */ | ||
| 24 | template<typename value, int inc, typename valuealloc> | ||
| 25 | class ArrayCore | ||
| 26 | { | ||
| 27 | friend class Array<value, inc, valuealloc>; | ||
| 28 | friend class SharedCore< | ||
| 29 | Array<value, inc, valuealloc>, | ||
| 30 | ArrayCore<value, inc, valuealloc> | ||
| 31 | >; | ||
| 32 | private: | ||
| 33 | ArrayCore() : | ||
| 34 | pData( NULL ), | ||
| 35 | iSize( 0 ), | ||
| 36 | iCapacity( 0 ) | ||
| 37 | { } | ||
| 38 | |||
| 39 | void setCapacity( int iNewLen ) | ||
| 40 | { | ||
| 41 | //clear(); | ||
| 42 | //iCapacity = iCapacity; | ||
| 43 | //pData = va.allocate( iCapacity ); | ||
| 44 | if( iNewLen <= iCapacity ) return; | ||
| 45 | value *pNewData = va.allocate( iNewLen ); | ||
| 46 | if( pData ) | ||
| 47 | { | ||
| 48 | for( int j = 0; j < iSize; j++ ) | ||
| 49 | { | ||
| 50 | va.construct( &pNewData[j], pData[j] ); | ||
| 51 | va.destroy( &pData[j] ); | ||
| 52 | } | ||
| 53 | va.deallocate( pData, iCapacity ); | ||
| 54 | } | ||
| 55 | pData = pNewData; | ||
| 56 | iCapacity = iNewLen; | ||
| 57 | } | ||
| 58 | |||
| 59 | virtual ~ArrayCore() | ||
| 60 | { | ||
| 61 | clear(); | ||
| 62 | } | ||
| 63 | |||
| 64 | void clear() | ||
| 65 | { | ||
| 66 | if( pData ) | ||
| 67 | { | ||
| 68 | for( int j = 0; j < iSize; j++ ) | ||
| 69 | { | ||
| 70 | va.destroy( &pData[j] ); | ||
| 71 | } | ||
| 72 | va.deallocate( pData, iCapacity ); | ||
| 73 | pData = NULL; | ||
| 74 | } | ||
| 75 | iSize = 0; | ||
| 76 | iCapacity = 0; | ||
| 77 | } | ||
| 78 | |||
| 79 | void erase( int iPos ) | ||
| 80 | { | ||
| 81 | for( int j = iPos; j < iSize; j++ ) | ||
| 82 | { | ||
| 83 | va.destroy( &pData[j] ); | ||
| 84 | if( j == iSize-1 ) | ||
| 85 | { | ||
| 86 | iSize--; | ||
| 87 | return; | ||
| 88 | } | ||
| 89 | va.construct( &pData[j], pData[j+1] ); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | void swapErase( int iPos ) | ||
| 94 | { | ||
| 95 | if( iPos == iSize-1 ) | ||
| 96 | { | ||
| 97 | erase( iPos ); | ||
| 98 | return; | ||
| 99 | } | ||
| 100 | va.destroy( &pData[iPos] ); | ||
| 101 | va.construct( &pData[iPos], pData[iSize-1] ); | ||
| 102 | va.destroy( &pData[iSize-1] ); | ||
| 103 | iSize--; | ||
| 104 | } | ||
| 105 | |||
| 106 | valuealloc va; | ||
| 107 | value *pData; | ||
| 108 | long iSize; | ||
| 109 | long iCapacity; | ||
| 110 | }; | ||
| 111 | /** @endcond */ | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Array type container, just like a normal array only flexible and keeps | ||
| 115 | * track of your memory for you. | ||
| 116 | * | ||
| 117 | *@param value (typename) The type of data to store in your list | ||
| 118 | *@param valuealloc (typename) Memory Allocator for your value type | ||
| 119 | *@param linkalloc (typename) Memory Allocator for the list links. | ||
| 120 | *@ingroup Containers | ||
| 121 | */ | ||
| 122 | template<typename value, int inc=10, typename valuealloc=std::allocator<value> > | ||
| 123 | class Array : public SharedCore< | ||
| 124 | Array<value, inc, valuealloc>, | ||
| 125 | ArrayCore<value, inc, valuealloc> | ||
| 126 | > | ||
| 127 | { | ||
| 128 | private: | ||
| 129 | typedef class Array<value, inc, valuealloc> MyType; | ||
| 130 | typedef class ArrayCore<value, inc, valuealloc> Core; | ||
| 131 | |||
| 132 | protected: | ||
| 133 | using SharedCore<MyType, Core>::core; | ||
| 134 | using SharedCore<MyType, Core>::_hardCopy; | ||
| 135 | using SharedCore<MyType, Core>::_resetCore; | ||
| 136 | using SharedCore<MyType, Core>::_allocateCore; | ||
| 137 | |||
| 138 | public: | ||
| 139 | struct const_iterator; | ||
| 140 | struct iterator; | ||
| 141 | |||
| 142 | Array() | ||
| 143 | { | ||
| 144 | } | ||
| 145 | |||
| 146 | Array( const MyType &src ) : | ||
| 147 | SharedCore<MyType, Core >( src ) | ||
| 148 | { | ||
| 149 | } | ||
| 150 | |||
| 151 | Array( long iSetCap ) | ||
| 152 | { | ||
| 153 | setCapacity( iSetCap ); | ||
| 154 | } | ||
| 155 | |||
| 156 | ~Array() | ||
| 157 | { | ||
| 158 | } | ||
| 159 | |||
| 160 | bool operator==( const MyType &src ) const | ||
| 161 | { | ||
| 162 | if( core == src.core ) | ||
| 163 | return true; | ||
| 164 | if( core->iSize != src.core->iSize ) | ||
| 165 | return false; | ||
| 166 | |||
| 167 | for( int j = 0; j < core->iSize; j++ ) | ||
| 168 | { | ||
| 169 | if( core->pData[j] != src.core->pData[j] ) | ||
| 170 | return false; | ||
| 171 | } | ||
| 172 | return true; | ||
| 173 | } | ||
| 174 | |||
| 175 | bool operator!=( const MyType &src ) const | ||
| 176 | { | ||
| 177 | return !(*this == src); | ||
| 178 | } | ||
| 179 | |||
| 180 | /** | ||
| 181 | * Clear the array. | ||
| 182 | */ | ||
| 183 | void clear() | ||
| 184 | { | ||
| 185 | _resetCore(); | ||
| 186 | } | ||
| 187 | |||
| 188 | MyType &append( const value &rVal ) | ||
| 189 | { | ||
| 190 | _hardCopy(); | ||
| 191 | if( core->iSize == core->iCapacity ) | ||
| 192 | { | ||
| 193 | core->setCapacity( core->iCapacity + inc ); | ||
| 194 | } | ||
| 195 | |||
| 196 | core->va.construct( &core->pData[core->iSize++], rVal ); | ||
| 197 | |||
| 198 | return *this; | ||
| 199 | } | ||
| 200 | |||
| 201 | MyType &append( const MyType &rVal ) | ||
| 202 | { | ||
| 203 | _hardCopy(); | ||
| 204 | |||
| 205 | if( core->iSize + rVal.core->iSize > core->iCapacity ) | ||
| 206 | { | ||
| 207 | core->setCapacity( core->iSize + rVal.core->iSize + inc ); | ||
| 208 | } | ||
| 209 | |||
| 210 | for( int j = 0; j < rVal.core->iSize; j++ ) | ||
| 211 | { | ||
| 212 | core->va.construct( | ||
| 213 | &core->pData[core->iSize++], | ||
| 214 | rVal.core->pData[j] | ||
| 215 | ); | ||
| 216 | } | ||
| 217 | |||
| 218 | return *this; | ||
| 219 | } | ||
| 220 | |||
| 221 | //operator | ||
| 222 | value &operator[]( long iIndex ) | ||
| 223 | { | ||
| 224 | _hardCopy(); | ||
| 225 | if( iIndex < 0 || iIndex >= core->iSize ) | ||
| 226 | throw ArrayException( | ||
| 227 | "Index %d out of range 0:%d", iIndex, core->iSize ); | ||
| 228 | |||
| 229 | return core->pData[iIndex]; | ||
| 230 | } | ||
| 231 | |||
| 232 | const value &operator[]( long iIndex ) const | ||
| 233 | { | ||
| 234 | if( iIndex < 0 || iIndex >= core->iSize ) | ||
| 235 | throw ArrayException( | ||
| 236 | "Index %d out of range 0:%d", iIndex, core->iSize ); | ||
| 237 | |||
| 238 | return core->pData[iIndex]; | ||
| 239 | } | ||
| 240 | |||
| 241 | value &get( long iIndex ) | ||
| 242 | { | ||
| 243 | _hardCopy(); | ||
| 244 | if( iIndex < 0 || iIndex >= core->iSize ) | ||
| 245 | throw ArrayException( | ||
| 246 | "Index %d out of range 0:%d", iIndex, core->iSize ); | ||
| 247 | |||
| 248 | return core->pData[iIndex]; | ||
| 249 | } | ||
| 250 | |||
| 251 | const value &get( long iIndex ) const | ||
| 252 | { | ||
| 253 | if( iIndex < 0 || iIndex >= core->iSize ) | ||
| 254 | throw ArrayException( | ||
| 255 | "Index %d out of range 0:%d", iIndex, core->iSize ); | ||
| 256 | |||
| 257 | return core->pData[iIndex]; | ||
| 258 | } | ||
| 259 | |||
| 260 | value &first() | ||
| 261 | { | ||
| 262 | _hardCopy(); | ||
| 263 | return core->pData[0]; | ||
| 264 | } | ||
| 265 | |||
| 266 | const value &first() const | ||
| 267 | { | ||
| 268 | return core->pData[0]; | ||
| 269 | } | ||
| 270 | |||
| 271 | value &last() | ||
| 272 | { | ||
| 273 | _hardCopy(); | ||
| 274 | return core->pData[core->iSize-1]; | ||
| 275 | } | ||
| 276 | |||
| 277 | const value &last() const | ||
| 278 | { | ||
| 279 | return core->pData[core->iSize-1]; | ||
| 280 | } | ||
| 281 | |||
| 282 | /** | ||
| 283 | * Get the current size of the array. | ||
| 284 | *@returns The current size of the array. | ||
| 285 | */ | ||
| 286 | long getSize() const | ||
| 287 | { | ||
| 288 | return core->iSize; | ||
| 289 | } | ||
| 290 | |||
| 291 | /** | ||
| 292 | * Get the capacity of the array. This number will grow as data is | ||
| 293 | * added, and is mainly for the curious, it doesn't really determine | ||
| 294 | * much for the end user. | ||
| 295 | *@returns The current capacity of the array. | ||
| 296 | */ | ||
| 297 | long getCapacity() const | ||
| 298 | { | ||
| 299 | return core->iCapacity; | ||
| 300 | } | ||
| 301 | |||
| 302 | /** | ||
| 303 | * Change the capacity of the array, very useful if you know you'll be | ||
| 304 | * adding a large amount of already counted items to the array, makes | ||
| 305 | * the appending much faster afterwords. | ||
| 306 | *@param iNewLen The new capacity of the array. | ||
| 307 | *@todo Set this up so it can reduce the size of the array as well as | ||
| 308 | * make it bigger. | ||
| 309 | */ | ||
| 310 | void setCapacity( long iNewLen ) | ||
| 311 | { | ||
| 312 | _hardCopy(); | ||
| 313 | core->setCapacity( iNewLen ); | ||
| 314 | } | ||
| 315 | |||
| 316 | typedef struct iterator | ||
| 317 | { | ||
| 318 | friend class Array<value, inc, valuealloc>; | ||
| 319 | private: | ||
| 320 | iterator( MyType &src, long iPos=0 ) : | ||
| 321 | src( src ), | ||
| 322 | iPos( iPos ) | ||
| 323 | { | ||
| 324 | if( this->iPos >= src.getSize() ) | ||
| 325 | this->iPos = -1; | ||
| 326 | } | ||
| 327 | |||
| 328 | MyType &src; | ||
| 329 | long iPos; | ||
| 330 | |||
| 331 | public: | ||
| 332 | iterator operator++( int ) | ||
| 333 | { | ||
| 334 | if( iPos < 0 ) | ||
| 335 | throw ArrayException( | ||
| 336 | "Cannot increment iterator past end of array."); | ||
| 337 | iPos++; | ||
| 338 | if( iPos >= src.getSize() ) | ||
| 339 | iPos = -1; | ||
| 340 | return *this; | ||
| 341 | } | ||
| 342 | |||
| 343 | iterator operator++() | ||
| 344 | { | ||
| 345 | if( iPos >= 0 ) | ||
| 346 | iPos++; | ||
| 347 | if( iPos >= src.getSize() ) | ||
| 348 | iPos = -1; | ||
| 349 | return *this; | ||
| 350 | } | ||
| 351 | |||
| 352 | iterator operator+( int iAmnt ) | ||
| 353 | { | ||
| 354 | if( iPos < 0 ) | ||
| 355 | throw ArrayException( | ||
| 356 | "Cannot increment iterator past end of array."); | ||
| 357 | iPos += iAmnt; | ||
| 358 | if( iPos >= src.getSize() ) | ||
| 359 | iPos = -1; | ||
| 360 | return *this; | ||
| 361 | } | ||
| 362 | |||
| 363 | iterator operator--( int ) | ||
| 364 | { | ||
| 365 | if( iPos < 0 ) | ||
| 366 | throw ArrayException( | ||
| 367 | "Cannot increment iterator past end of array."); | ||
| 368 | iPos--; | ||
| 369 | if( iPos < 0 ) | ||
| 370 | iPos = -1; | ||
| 371 | return *this; | ||
| 372 | } | ||
| 373 | |||
| 374 | iterator operator--() | ||
| 375 | { | ||
| 376 | if( iPos < src.getSize() ) | ||
| 377 | iPos--; | ||
| 378 | if( iPos <= 0 ) | ||
| 379 | iPos = -1; | ||
| 380 | return *this; | ||
| 381 | } | ||
| 382 | |||
| 383 | iterator operator-( int iAmnt ) | ||
| 384 | { | ||
| 385 | if( iPos < src.getSize() ) | ||
| 386 | iPos -= iAmnt; | ||
| 387 | if( iPos <= 0 ) | ||
| 388 | iPos = -1; | ||
| 389 | return *this; | ||
| 390 | } | ||
| 391 | |||
| 392 | bool operator==( const iterator &oth ) const | ||
| 393 | { | ||
| 394 | return iPos == oth.iPos; | ||
| 395 | } | ||
| 396 | |||
| 397 | bool operator!=( const iterator &oth ) const | ||
| 398 | { | ||
| 399 | return iPos != oth.iPos; | ||
| 400 | } | ||
| 401 | |||
| 402 | iterator operator=( const iterator &oth ) | ||
| 403 | { | ||
| 404 | if( &src != &oth.src ) | ||
| 405 | throw ArrayException( | ||
| 406 | "Cannot mix iterators from different array objects."); | ||
| 407 | iPos = oth.iPos; | ||
| 408 | } | ||
| 409 | |||
| 410 | value &operator*() | ||
| 411 | { | ||
| 412 | if( iPos < 0 ) | ||
| 413 | throw ArrayException( | ||
| 414 | "Cannot dereference finished iterator."); | ||
| 415 | return src[iPos]; | ||
| 416 | } | ||
| 417 | |||
| 418 | long getIndex() const | ||
| 419 | { | ||
| 420 | return iPos; | ||
| 421 | } | ||
| 422 | |||
| 423 | operator bool() const | ||
| 424 | { | ||
| 425 | return iPos >= 0; | ||
| 426 | } | ||
| 427 | |||
| 428 | bool isValid() const | ||
| 429 | { | ||
| 430 | return iPos >= 0; | ||
| 431 | } | ||
| 432 | } iterator; | ||
| 433 | |||
| 434 | typedef struct const_iterator | ||
| 435 | { | ||
| 436 | friend class Array<value, inc, valuealloc>; | ||
| 437 | private: | ||
| 438 | const_iterator( const MyType &src, long iPos=0 ) : | ||
| 439 | src( src ), | ||
| 440 | iPos( iPos ) | ||
| 441 | { | ||
| 442 | if( this->iPos >= src.getSize() ) | ||
| 443 | this->iPos = -1; | ||
| 444 | } | ||
| 445 | |||
| 446 | const MyType &src; | ||
| 447 | long iPos; | ||
| 448 | |||
| 449 | public: | ||
| 450 | const_iterator( iterator &rSrc ) : | ||
| 451 | src( rSrc.src ), | ||
| 452 | iPos( rSrc.iPos ) | ||
| 453 | { | ||
| 454 | } | ||
| 455 | const_iterator operator++( int ) | ||
| 456 | { | ||
| 457 | if( iPos < 0 ) | ||
| 458 | throw ArrayException( | ||
| 459 | "Cannot increment iterator past end of array."); | ||
| 460 | iPos++; | ||
| 461 | if( iPos >= src.getSize() ) | ||
| 462 | iPos = -1; | ||
| 463 | return *this; | ||
| 464 | } | ||
| 465 | |||
| 466 | const_iterator operator++() | ||
| 467 | { | ||
| 468 | if( iPos >= 0 ) | ||
| 469 | iPos++; | ||
| 470 | if( iPos >= src.getSize() ) | ||
| 471 | iPos = -1; | ||
| 472 | return *this; | ||
| 473 | } | ||
| 474 | |||
| 475 | const_iterator operator--( int ) | ||
| 476 | { | ||
| 477 | if( iPos < 0 ) | ||
| 478 | throw ArrayException( | ||
| 479 | "Cannot increment iterator past end of array."); | ||
| 480 | iPos--; | ||
| 481 | if( iPos < 0 ) | ||
| 482 | iPos = -1; | ||
| 483 | return *this; | ||
| 484 | } | ||
| 485 | |||
| 486 | const_iterator operator--() | ||
| 487 | { | ||
| 488 | if( iPos < src.getSize() ) | ||
| 489 | iPos--; | ||
| 490 | if( iPos <= 0 ) | ||
| 491 | iPos = -1; | ||
| 492 | return *this; | ||
| 493 | } | ||
| 494 | |||
| 495 | bool operator==( const const_iterator &oth ) const | ||
| 496 | { | ||
| 497 | return iPos == oth.iPos; | ||
| 498 | } | ||
| 499 | |||
| 500 | bool operator!=( const const_iterator &oth ) const | ||
| 501 | { | ||
| 502 | return iPos != oth.iPos; | ||
| 503 | } | ||
| 504 | |||
| 505 | const_iterator operator=( const const_iterator &oth ) | ||
| 506 | { | ||
| 507 | if( &src != &oth.src ) | ||
| 508 | throw ArrayException( | ||
| 509 | "Cannot mix iterators from different array objects."); | ||
| 510 | iPos = oth.iPos; | ||
| 511 | } | ||
| 512 | |||
| 513 | const value &operator*() const | ||
| 514 | { | ||
| 515 | if( iPos < 0 ) | ||
| 516 | throw ArrayException( | ||
| 517 | "Cannot dereference finished iterator."); | ||
| 518 | return src[iPos]; | ||
| 519 | } | ||
| 520 | |||
| 521 | long getIndex() const | ||
| 522 | { | ||
| 523 | return iPos; | ||
| 524 | } | ||
| 525 | |||
| 526 | operator bool() const | ||
| 527 | { | ||
| 528 | return iPos >= 0; | ||
| 529 | } | ||
| 530 | |||
| 531 | bool isValid() const | ||
| 532 | { | ||
| 533 | return iPos >= 0; | ||
| 534 | } | ||
| 535 | } const_iterator; | ||
| 536 | |||
| 537 | iterator begin() | ||
| 538 | { | ||
| 539 | return iterator( *this ); | ||
| 540 | } | ||
| 541 | |||
| 542 | const_iterator begin() const | ||
| 543 | { | ||
| 544 | return const_iterator( *this ); | ||
| 545 | } | ||
| 546 | |||
| 547 | iterator end() | ||
| 548 | { | ||
| 549 | return iterator( *this, -1 ); | ||
| 550 | } | ||
| 551 | |||
| 552 | const_iterator end() const | ||
| 553 | { | ||
| 554 | return const_iterator( *this, -1 ); | ||
| 555 | } | ||
| 556 | |||
| 557 | MyType &insert( iterator i, const value &rVal ) | ||
| 558 | { | ||
| 559 | if( i.iPos == -1 ) | ||
| 560 | { | ||
| 561 | append( rVal ); | ||
| 562 | return *this; | ||
| 563 | } | ||
| 564 | |||
| 565 | _hardCopy(); | ||
| 566 | if( core->iSize == core->iCapacity ) | ||
| 567 | { | ||
| 568 | core->setCapacity( core->iCapacity + inc ); | ||
| 569 | } | ||
| 570 | core->iSize++; | ||
| 571 | |||
| 572 | core->va.construct( | ||
| 573 | &core->pData[core->iSize-1], | ||
| 574 | core->pData[core->iSize-2] | ||
| 575 | ); | ||
| 576 | for( int iPos = core->iSize-2; iPos > i.iPos; iPos-- ) | ||
| 577 | { | ||
| 578 | core->va.destroy( &core->pData[iPos] ); | ||
| 579 | core->va.construct( &core->pData[iPos], core->pData[iPos-1] ); | ||
| 580 | } | ||
| 581 | core->va.destroy( &core->pData[i.iPos] ); | ||
| 582 | core->va.construct( &core->pData[i.iPos], rVal ); | ||
| 583 | |||
| 584 | return *this; | ||
| 585 | } | ||
| 586 | |||
| 587 | /** | ||
| 588 | * If order is important, use this. It will delete the suggested item | ||
| 589 | * and move the rest of the data up a spot. This is a time O(n) | ||
| 590 | * operation. If the order isn't important, check swapErase | ||
| 591 | */ | ||
| 592 | void erase( iterator i ) | ||
| 593 | { | ||
| 594 | _hardCopy(); | ||
| 595 | core->erase( i.iPos ); | ||
| 596 | } | ||
| 597 | |||
| 598 | void erase( const value &v ) | ||
| 599 | { | ||
| 600 | _hardCopy(); | ||
| 601 | for( int j = 0; j < core->iSize; j++ ) | ||
| 602 | { | ||
| 603 | if( core->pData[j] == v ) | ||
| 604 | { | ||
| 605 | core->erase( j ); | ||
| 606 | return; | ||
| 607 | } | ||
| 608 | } | ||
| 609 | } | ||
| 610 | |||
| 611 | void eraseLast() | ||
| 612 | { | ||
| 613 | _hardCopy(); | ||
| 614 | core->erase( core->iSize-1 ); | ||
| 615 | } | ||
| 616 | |||
| 617 | void eraseFirst() | ||
| 618 | { | ||
| 619 | _hardCopy(); | ||
| 620 | core->erase( 0 ); | ||
| 621 | } | ||
| 622 | |||
| 623 | /** | ||
| 624 | * In order to make swapErase faster, what it does is swap the given | ||
| 625 | * item in the array with the last item, then make the array shorter | ||
| 626 | * by one. It changes the order of the elements in the array, so it | ||
| 627 | * should be used carefully, but it is time O(1) instead of O(n) like | ||
| 628 | * erase. | ||
| 629 | */ | ||
| 630 | void swapErase( iterator i ) | ||
| 631 | { | ||
| 632 | _hardCopy(); | ||
| 633 | core->swapErase( i.iPos ); | ||
| 634 | } | ||
| 635 | |||
| 636 | protected: | ||
| 637 | virtual Core *_copyCore( Core *src ) | ||
| 638 | { | ||
| 639 | Core *pRet = _allocateCore(); | ||
| 640 | pRet->setCapacity( src->iCapacity ); | ||
| 641 | pRet->iSize = src->iSize; | ||
| 642 | for( int j = 0; j < src->iSize; j++ ) | ||
| 643 | { | ||
| 644 | pRet->va.construct( &pRet->pData[j], src->pData[j] ); | ||
| 645 | } | ||
| 646 | return pRet; | ||
| 647 | } | ||
| 648 | |||
| 649 | private: | ||
| 650 | }; | ||
| 651 | |||
| 652 | class Formatter; | ||
| 653 | Formatter &operator<<( Formatter &rOut, char *sStr ); | ||
| 654 | Formatter &operator<<( Formatter &rOut, signed char c ); | ||
| 655 | template<typename value> | ||
| 656 | Formatter &operator<<( Formatter &f, const Bu::Array<value> &a ) | ||
| 657 | { | ||
| 658 | f << '['; | ||
| 659 | for( typename Bu::Array<value>::const_iterator i = a.begin(); i; i++ ) | ||
| 660 | { | ||
| 661 | if( i != a.begin() ) | ||
| 662 | f << ", "; | ||
| 663 | f << *i; | ||
| 664 | } | ||
| 665 | f << ']'; | ||
| 666 | |||
| 667 | return f; | ||
| 668 | } | ||
| 669 | |||
| 670 | template<typename value, int inc, typename valuealloc> | ||
| 671 | ArchiveBase &operator<<( ArchiveBase &ar, | ||
| 672 | const Array<value, inc, valuealloc> &h ) | ||
| 673 | { | ||
| 674 | ar << h.getSize(); | ||
| 675 | for( typename Array<value, inc, valuealloc>::const_iterator i = | ||
| 676 | h.begin(); i != h.end(); i++ ) | ||
| 677 | { | ||
| 678 | ar << (*i); | ||
| 679 | } | ||
| 680 | |||
| 681 | return ar; | ||
| 682 | } | ||
| 683 | |||
| 684 | template<typename value, int inc, typename valuealloc> | ||
| 685 | ArchiveBase &operator>>(ArchiveBase &ar, Array<value, inc, valuealloc> &h ) | ||
| 686 | { | ||
| 687 | h.clear(); | ||
| 688 | long nSize; | ||
| 689 | ar >> nSize; | ||
| 690 | |||
| 691 | h.setCapacity( nSize ); | ||
| 692 | for( long j = 0; j < nSize; j++ ) | ||
| 693 | { | ||
| 694 | value v; | ||
| 695 | ar >> v; | ||
| 696 | h.append( v ); | ||
| 697 | } | ||
| 698 | return ar; | ||
| 699 | } | ||
| 700 | |||
| 701 | } | ||
| 702 | |||
| 703 | #endif | ||
| diff --git a/src/stable/atom.cpp b/src/stable/atom.cpp new file mode 100644 index 0000000..3c77b90 --- /dev/null +++ b/src/stable/atom.cpp | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/atom.h" | ||
| diff --git a/src/stable/atom.h b/src/stable/atom.h new file mode 100644 index 0000000..fd88f2d --- /dev/null +++ b/src/stable/atom.h | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_ATOM_H | ||
| 9 | #define BU_ATOM_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include <memory> | ||
| 13 | #include "bu/config.h" | ||
| 14 | #include "bu/exceptionbase.h" | ||
| 15 | |||
| 16 | namespace Bu | ||
| 17 | { | ||
| 18 | /** | ||
| 19 | * | ||
| 20 | *@ingroup Containers | ||
| 21 | */ | ||
| 22 | template <typename t, typename talloc=std::allocator<t> > | ||
| 23 | class Atom | ||
| 24 | { | ||
| 25 | private: | ||
| 26 | typedef struct Atom<t, talloc> MyType; | ||
| 27 | |||
| 28 | public: | ||
| 29 | Atom() : | ||
| 30 | pData( NULL ) | ||
| 31 | { | ||
| 32 | } | ||
| 33 | |||
| 34 | Atom( const MyType &oth ) : | ||
| 35 | pData( NULL ) | ||
| 36 | { | ||
| 37 | if( oth.pData ) | ||
| 38 | set( *oth.pData ); | ||
| 39 | } | ||
| 40 | |||
| 41 | Atom( const t &oth ) : | ||
| 42 | pData( NULL ) | ||
| 43 | { | ||
| 44 | set( oth ); | ||
| 45 | } | ||
| 46 | |||
| 47 | virtual ~Atom() | ||
| 48 | { | ||
| 49 | clear(); | ||
| 50 | } | ||
| 51 | |||
| 52 | bool has() const | ||
| 53 | { | ||
| 54 | return (pData != NULL); | ||
| 55 | } | ||
| 56 | |||
| 57 | void set( const t &val ) | ||
| 58 | { | ||
| 59 | clear(); | ||
| 60 | pData = ta.allocate( 1 ); | ||
| 61 | ta.construct( pData, val ); | ||
| 62 | } | ||
| 63 | |||
| 64 | t &get() | ||
| 65 | { | ||
| 66 | if( !pData ) | ||
| 67 | throw Bu::ExceptionBase("Not set"); | ||
| 68 | return *pData; | ||
| 69 | } | ||
| 70 | |||
| 71 | const t &get() const | ||
| 72 | { | ||
| 73 | if( !pData ) | ||
| 74 | throw Bu::ExceptionBase("Not set"); | ||
| 75 | return *pData; | ||
| 76 | } | ||
| 77 | |||
| 78 | void clear() | ||
| 79 | { | ||
| 80 | if( pData ) | ||
| 81 | { | ||
| 82 | ta.destroy( pData ); | ||
| 83 | ta.deallocate( pData, 1 ); | ||
| 84 | pData = NULL; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | operator const t &() const | ||
| 89 | { | ||
| 90 | if( !pData ) | ||
| 91 | throw Bu::ExceptionBase("Not set"); | ||
| 92 | return *pData; | ||
| 93 | } | ||
| 94 | |||
| 95 | operator t &() | ||
| 96 | { | ||
| 97 | if( !pData ) | ||
| 98 | throw Bu::ExceptionBase("Not set"); | ||
| 99 | return *pData; | ||
| 100 | } | ||
| 101 | |||
| 102 | MyType &operator =( const t &oth ) | ||
| 103 | { | ||
| 104 | set( oth ); | ||
| 105 | |||
| 106 | return *this; | ||
| 107 | } | ||
| 108 | |||
| 109 | MyType &operator =( const MyType &oth ) | ||
| 110 | { | ||
| 111 | if( oth.pData ) | ||
| 112 | set( *oth.pData ); | ||
| 113 | |||
| 114 | return *this; | ||
| 115 | } | ||
| 116 | |||
| 117 | bool operator ==( const MyType &oth ) | ||
| 118 | { | ||
| 119 | return (*pData) == (*oth.pData); | ||
| 120 | } | ||
| 121 | |||
| 122 | bool operator ==( const t &oth ) | ||
| 123 | { | ||
| 124 | return (*pData) == oth; | ||
| 125 | } | ||
| 126 | |||
| 127 | t *operator ->() | ||
| 128 | { | ||
| 129 | if( !pData ) | ||
| 130 | throw Bu::ExceptionBase("Not set"); | ||
| 131 | return pData; | ||
| 132 | } | ||
| 133 | |||
| 134 | t &operator *() | ||
| 135 | { | ||
| 136 | if( !pData ) | ||
| 137 | throw Bu::ExceptionBase("Not set"); | ||
| 138 | return *pData; | ||
| 139 | } | ||
| 140 | |||
| 141 | private: | ||
| 142 | t *pData; | ||
| 143 | talloc ta; | ||
| 144 | }; | ||
| 145 | } | ||
| 146 | |||
| 147 | #endif | ||
| diff --git a/src/stable/base64.cpp b/src/stable/base64.cpp new file mode 100644 index 0000000..4d659f0 --- /dev/null +++ b/src/stable/base64.cpp | |||
| @@ -0,0 +1,219 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/base64.h" | ||
| 9 | |||
| 10 | namespace Bu { subExceptionDef( Base64Exception ) } | ||
| 11 | |||
| 12 | const char Bu::Base64::tblEnc[65] = { | ||
| 13 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" | ||
| 14 | }; | ||
| 15 | |||
| 16 | Bu::Base64::Base64( Bu::Stream &rNext, int iChunkSize ) : | ||
| 17 | Bu::Filter( rNext ), | ||
| 18 | iBPos( 0 ), | ||
| 19 | iBuf( 0 ), | ||
| 20 | iRPos( 0 ), | ||
| 21 | iChars( 0 ), | ||
| 22 | bEosIn( false ), | ||
| 23 | iTotalIn( 0 ), | ||
| 24 | iTotalOut( 0 ), | ||
| 25 | eMode( Nothing ), | ||
| 26 | iChunkSize( iChunkSize ), | ||
| 27 | iCurChunk( 0 ) | ||
| 28 | { | ||
| 29 | start(); | ||
| 30 | |||
| 31 | memset( tblDec, 0, 80 ); | ||
| 32 | for( int j = 0; j < 64; j++ ) | ||
| 33 | { | ||
| 34 | tblDec[tblEnc[j]-'+'] = j; | ||
| 35 | // printf("'%c' = %d\n", tblEnc[j], j ); | ||
| 36 | } | ||
| 37 | /* | ||
| 38 | for( int j = 0; j < 64; j++ ) | ||
| 39 | { | ||
| 40 | printf("'%c' = '%c' (%d = %d)\n", | ||
| 41 | tblEnc[j], tblEnc[tblDec[tblEnc[j]-'+']], | ||
| 42 | j, tblDec[tblEnc[j]-'+'] ); | ||
| 43 | }*/ | ||
| 44 | |||
| 45 | // The following is used to compute the table size for the decoding table. | ||
| 46 | /* | ||
| 47 | char low='A', high='A'; | ||
| 48 | for( int j = 0; j < 64; j++ ) | ||
| 49 | { | ||
| 50 | if( tblEnc[j] < low ) | ||
| 51 | low = tblEnc[j]; | ||
| 52 | if( tblEnc[j] > high ) | ||
| 53 | high = tblEnc[j]; | ||
| 54 | } | ||
| 55 | |||
| 56 | printf("'%c' - '%c' (%d - %d) (%d)\n", low, high, low, high, high-low ); | ||
| 57 | */ | ||
| 58 | } | ||
| 59 | |||
| 60 | Bu::Base64::~Base64() | ||
| 61 | { | ||
| 62 | stop(); | ||
| 63 | } | ||
| 64 | |||
| 65 | void Bu::Base64::start() | ||
| 66 | { | ||
| 67 | iCurChunk = 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | Bu::size Bu::Base64::stop() | ||
| 71 | { | ||
| 72 | if( eMode == Encode ) | ||
| 73 | { | ||
| 74 | char outBuf[4]; | ||
| 75 | int iBUsed = 4-(3-iBPos); | ||
| 76 | if( iBPos == 0 ) | ||
| 77 | return iTotalOut; | ||
| 78 | for( int k = 0; k < 4; k++ ) | ||
| 79 | { | ||
| 80 | outBuf[3-k] = tblEnc[(iBuf>>(6*k))&0x3f]; | ||
| 81 | } | ||
| 82 | for( int k = iBUsed; k < 4; k++ ) | ||
| 83 | { | ||
| 84 | outBuf[k] = '='; | ||
| 85 | } | ||
| 86 | iCurChunk += 4; | ||
| 87 | if( iChunkSize && iCurChunk >= iChunkSize ) | ||
| 88 | { | ||
| 89 | iCurChunk = iCurChunk-iChunkSize; | ||
| 90 | iTotalOut += rNext.write( outBuf, 4-iCurChunk ); | ||
| 91 | iTotalOut += rNext.write("\r\n", 2 ); | ||
| 92 | iTotalOut += rNext.write( outBuf+(4-iCurChunk), iCurChunk ); | ||
| 93 | } | ||
| 94 | else | ||
| 95 | iTotalOut += rNext.write( outBuf, 4 ); | ||
| 96 | return iTotalOut; | ||
| 97 | } | ||
| 98 | else | ||
| 99 | { | ||
| 100 | return iTotalIn; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | Bu::size Bu::Base64::read( void *pBuf, Bu::size nBytes ) | ||
| 105 | { | ||
| 106 | if( eMode == Encode ) | ||
| 107 | throw Bu::Base64Exception("Cannot read from an output stream."); | ||
| 108 | eMode = Decode; | ||
| 109 | |||
| 110 | if( bEosIn == true && iRPos == iChars ) | ||
| 111 | return 0; | ||
| 112 | Bu::size sIn = 0; | ||
| 113 | char buf[4]; | ||
| 114 | while( sIn < nBytes ) | ||
| 115 | { | ||
| 116 | for(; iRPos < iChars && sIn < nBytes; iRPos++, sIn++ ) | ||
| 117 | { | ||
| 118 | ((unsigned char *)pBuf)[sIn] = (iBuf>>(8*(2-iRPos)))&0xFF; | ||
| 119 | } | ||
| 120 | if( iRPos == iChars ) | ||
| 121 | { | ||
| 122 | if( bEosIn == true ) | ||
| 123 | return sIn; | ||
| 124 | else | ||
| 125 | iRPos = 0; | ||
| 126 | } | ||
| 127 | else if( sIn == nBytes ) | ||
| 128 | return sIn; | ||
| 129 | //if( rNext.read( buf, 4 ) == 0 ) | ||
| 130 | // return sIn; | ||
| 131 | for( int j = 0; j < 4; j++ ) | ||
| 132 | { | ||
| 133 | if( rNext.read( &buf[j], 1 ) == 0 ) | ||
| 134 | { | ||
| 135 | if( rNext.isEos() ) | ||
| 136 | { | ||
| 137 | if( iRPos == 0 ) | ||
| 138 | iRPos = iChars; | ||
| 139 | bEosIn = true; | ||
| 140 | if( j != 0 ) | ||
| 141 | { | ||
| 142 | throw Base64Exception( | ||
| 143 | "Premature end of stream detected while " | ||
| 144 | "decoding Base64 data." | ||
| 145 | ); | ||
| 146 | } | ||
| 147 | } | ||
| 148 | return sIn; | ||
| 149 | } | ||
| 150 | if( buf[j] == ' ' || buf[j] == '\t' || | ||
| 151 | buf[j] == '\n' || buf[j] == '\r' ) | ||
| 152 | { | ||
| 153 | j--; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | iChars = 3; | ||
| 157 | iBuf = 0; | ||
| 158 | for( int j = 0; j < 4; j++ ) | ||
| 159 | { | ||
| 160 | if( buf[j] == '=' ) | ||
| 161 | { | ||
| 162 | iChars--; | ||
| 163 | bEosIn = true; | ||
| 164 | } | ||
| 165 | else | ||
| 166 | iBuf |= (tblDec[buf[j]-'+']&0x3f)<<((3-j)*6); | ||
| 167 | //printf("%d: %06X (%02X)\n", j, iBuf, (tblDec[buf[j]-'+']&0x3f) ); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | return sIn; | ||
| 172 | } | ||
| 173 | |||
| 174 | Bu::size Bu::Base64::write( const void *pBuf, Bu::size nBytes ) | ||
| 175 | { | ||
| 176 | if( eMode == Decode ) | ||
| 177 | throw Bu::Base64Exception("Cannot write to an input stream."); | ||
| 178 | eMode = Encode; | ||
| 179 | |||
| 180 | Bu::size sOut = 0; | ||
| 181 | char outBuf[4]; | ||
| 182 | for( Bu::size j = 0; j < nBytes; j++ ) | ||
| 183 | { | ||
| 184 | iBuf |= (((uint8_t *)pBuf)[j])<<((2-iBPos++)*8); | ||
| 185 | if( iBPos == 3 ) | ||
| 186 | { | ||
| 187 | for( int k = 0; k < 4; k++ ) | ||
| 188 | { | ||
| 189 | outBuf[3-k] = tblEnc[(iBuf>>(6*k))&0x3f]; | ||
| 190 | } | ||
| 191 | iCurChunk += 4; | ||
| 192 | if( iChunkSize && iCurChunk >= iChunkSize ) | ||
| 193 | { | ||
| 194 | iCurChunk = iCurChunk-iChunkSize; | ||
| 195 | sOut += rNext.write( outBuf, 4-iCurChunk ); | ||
| 196 | sOut += rNext.write("\r\n", 2 ); | ||
| 197 | sOut += rNext.write( outBuf+(4-iCurChunk), iCurChunk ); | ||
| 198 | } | ||
| 199 | else | ||
| 200 | sOut += rNext.write( outBuf, 4 ); | ||
| 201 | iBPos = iBuf = 0; | ||
| 202 | } | ||
| 203 | } | ||
| 204 | iTotalOut += sOut; | ||
| 205 | return sOut; | ||
| 206 | } | ||
| 207 | |||
| 208 | bool Bu::Base64::isOpen() | ||
| 209 | { | ||
| 210 | return true; | ||
| 211 | } | ||
| 212 | |||
| 213 | bool Bu::Base64::isEos() | ||
| 214 | { | ||
| 215 | if( bEosIn == true && iRPos == iChars ) | ||
| 216 | return true; | ||
| 217 | return false; | ||
| 218 | } | ||
| 219 | |||
| diff --git a/src/stable/base64.h b/src/stable/base64.h new file mode 100644 index 0000000..c081ac1 --- /dev/null +++ b/src/stable/base64.h | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_BASE64_H | ||
| 9 | #define BU_BASE64_H | ||
| 10 | |||
| 11 | #include "bu/filter.h" | ||
| 12 | #include "bu/exceptionbase.h" | ||
| 13 | |||
| 14 | namespace Bu | ||
| 15 | { | ||
| 16 | subExceptionDecl( Base64Exception ); | ||
| 17 | |||
| 18 | /** | ||
| 19 | * | ||
| 20 | *@ingroup Streams | ||
| 21 | */ | ||
| 22 | class Base64 : public Bu::Filter | ||
| 23 | { | ||
| 24 | public: | ||
| 25 | Base64( Bu::Stream &rNext, int iChunkSize=0 ); | ||
| 26 | virtual ~Base64(); | ||
| 27 | |||
| 28 | virtual void start(); | ||
| 29 | virtual Bu::size stop(); | ||
| 30 | virtual Bu::size read( void *pBuf, Bu::size nBytes ); | ||
| 31 | virtual Bu::size write( const void *pBuf, Bu::size nBytes ); | ||
| 32 | |||
| 33 | virtual bool isOpen(); | ||
| 34 | |||
| 35 | virtual bool isEos(); | ||
| 36 | |||
| 37 | private: | ||
| 38 | int iBPos; | ||
| 39 | int iBuf; | ||
| 40 | int iRPos; | ||
| 41 | int iChars; | ||
| 42 | bool bEosIn; | ||
| 43 | Bu::size iTotalIn; | ||
| 44 | Bu::size iTotalOut; | ||
| 45 | static const char tblEnc[65]; | ||
| 46 | char tblDec[80]; | ||
| 47 | enum Mode | ||
| 48 | { | ||
| 49 | Nothing = 0x00, | ||
| 50 | Encode = 0x01, | ||
| 51 | Decode = 0x02, | ||
| 52 | }; | ||
| 53 | Mode eMode; | ||
| 54 | int iChunkSize; | ||
| 55 | int iCurChunk; | ||
| 56 | }; | ||
| 57 | }; | ||
| 58 | |||
| 59 | #endif | ||
| diff --git a/src/stable/buffer.cpp b/src/stable/buffer.cpp new file mode 100644 index 0000000..47fab70 --- /dev/null +++ b/src/stable/buffer.cpp | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/buffer.h" | ||
| 9 | |||
| 10 | Bu::Buffer::Buffer( Bu::Stream &rNext, int iWhat, int iBufSize ) : | ||
| 11 | Bu::Filter( rNext ), | ||
| 12 | sSoFar( 0 ), | ||
| 13 | iBufSize( iBufSize ), | ||
| 14 | sReadBuf( NULL ), | ||
| 15 | sWriteBuf( NULL ), | ||
| 16 | iReadBufFill( 0 ), | ||
| 17 | iReadPos( 0 ), | ||
| 18 | iWriteBufFill( 0 ), | ||
| 19 | iWritePos( 0 ), | ||
| 20 | iWhat( iWhat ) | ||
| 21 | { | ||
| 22 | sReadBuf = new char[iBufSize]; | ||
| 23 | sWriteBuf = new char[iBufSize]; | ||
| 24 | } | ||
| 25 | |||
| 26 | Bu::Buffer::~Buffer() | ||
| 27 | { | ||
| 28 | delete[] sReadBuf; | ||
| 29 | delete[] sWriteBuf; | ||
| 30 | } | ||
| 31 | |||
| 32 | void Bu::Buffer::start() | ||
| 33 | { | ||
| 34 | } | ||
| 35 | |||
| 36 | Bu::size Bu::Buffer::stop() | ||
| 37 | { | ||
| 38 | iReadBufFill = iReadPos = iWriteBufFill = iWritePos = 0; | ||
| 39 | return sSoFar; | ||
| 40 | } | ||
| 41 | |||
| 42 | void Bu::Buffer::fillReadBuf() | ||
| 43 | { | ||
| 44 | if( iReadBufFill+iReadPos < iBufSize ) | ||
| 45 | { | ||
| 46 | iReadBufFill += rNext.read( | ||
| 47 | sReadBuf+iReadPos+iReadBufFill, | ||
| 48 | iBufSize-iReadBufFill-iReadPos | ||
| 49 | ); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | Bu::size Bu::Buffer::read( void *pBuf, Bu::size nBytes ) | ||
| 54 | { | ||
| 55 | if( (iWhat&Read) == 0 ) | ||
| 56 | return rNext.read( pBuf, nBytes ); | ||
| 57 | |||
| 58 | if( nBytes <= 0 ) | ||
| 59 | { | ||
| 60 | fillReadBuf(); | ||
| 61 | return 0; | ||
| 62 | } | ||
| 63 | |||
| 64 | Bu::size nTotRead = 0; | ||
| 65 | // fillReadBuf(); | ||
| 66 | |||
| 67 | do | ||
| 68 | { | ||
| 69 | int iAmnt = nBytes-nTotRead; | ||
| 70 | if( iAmnt > iReadBufFill ) | ||
| 71 | { | ||
| 72 | iAmnt = iReadBufFill; | ||
| 73 | } | ||
| 74 | if( iAmnt > 0 ) | ||
| 75 | { | ||
| 76 | memcpy( ((char *)pBuf)+nTotRead, sReadBuf+iReadPos, iAmnt ); | ||
| 77 | iReadPos += iAmnt; | ||
| 78 | nTotRead += iAmnt; | ||
| 79 | iReadBufFill -= iAmnt; | ||
| 80 | } | ||
| 81 | if( iReadBufFill == 0 ) | ||
| 82 | { | ||
| 83 | iReadPos = 0; | ||
| 84 | fillReadBuf(); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | while( nTotRead < nBytes && iReadBufFill > 0 ); | ||
| 88 | |||
| 89 | //printf("Buffer: %db returned, %db remain in buffer.\n", nTotRead, iReadBufFill ); | ||
| 90 | |||
| 91 | return nTotRead; | ||
| 92 | } | ||
| 93 | |||
| 94 | Bu::size Bu::Buffer::write( const void *pBuf, Bu::size nBytes ) | ||
| 95 | { | ||
| 96 | if( (iWhat&Write) == 0 ) | ||
| 97 | return rNext.write( pBuf, nBytes ); | ||
| 98 | |||
| 99 | Bu::size nTotWrote = 0; | ||
| 100 | |||
| 101 | do | ||
| 102 | { | ||
| 103 | int iAmnt = nBytes-nTotWrote; | ||
| 104 | if( iAmnt > iBufSize-iWritePos-iWriteBufFill ) | ||
| 105 | { | ||
| 106 | iAmnt = iBufSize-iWritePos-iWriteBufFill; | ||
| 107 | } | ||
| 108 | if( iAmnt > 0 ) | ||
| 109 | { | ||
| 110 | memcpy( | ||
| 111 | sWriteBuf+iWritePos+iWriteBufFill, | ||
| 112 | ((char *)pBuf)+nTotWrote, | ||
| 113 | iAmnt | ||
| 114 | ); | ||
| 115 | nTotWrote += iAmnt; | ||
| 116 | iWriteBufFill += iAmnt; | ||
| 117 | //printf("Buffer: Moved %db to write buffer, %db filled now.\n", | ||
| 118 | //iAmnt, iWriteBufFill ); | ||
| 119 | } | ||
| 120 | while( iWritePos+iWriteBufFill == iBufSize ) | ||
| 121 | { | ||
| 122 | //printf("iWritePos = %d\n", iWritePos ); | ||
| 123 | int iWr = rNext.write( sWriteBuf+iWritePos, iWriteBufFill ); | ||
| 124 | //printf("Buffer: Wrote %db from buffer to stream, %db filled now.\n", iWr, iWriteBufFill-iWr ); | ||
| 125 | if( iWr == 0 ) | ||
| 126 | { | ||
| 127 | return nTotWrote; | ||
| 128 | } | ||
| 129 | else if( iWr == iWriteBufFill ) | ||
| 130 | { | ||
| 131 | iWritePos = iWriteBufFill = 0; | ||
| 132 | } | ||
| 133 | else | ||
| 134 | { | ||
| 135 | iWritePos += iWr; | ||
| 136 | iWriteBufFill -= iWr; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | } | ||
| 140 | while( nTotWrote < nBytes && iWriteBufFill < iBufSize+iWritePos ); | ||
| 141 | |||
| 142 | return nTotWrote; | ||
| 143 | } | ||
| 144 | |||
| 145 | void Bu::Buffer::flush() | ||
| 146 | { | ||
| 147 | if( (iWhat&Write) == 0 ) | ||
| 148 | return rNext.flush(); | ||
| 149 | |||
| 150 | if( iWriteBufFill > 0 ) | ||
| 151 | { | ||
| 152 | //printf("Buffer: Flushing remaining data, %db.\n", iWriteBufFill ); | ||
| 153 | int iWr = 0; | ||
| 154 | do | ||
| 155 | { | ||
| 156 | iWr = rNext.write( sWriteBuf+iWritePos, iWriteBufFill ); | ||
| 157 | //printf("Buffer: %db written to stream.\n", iWr ); | ||
| 158 | iWritePos += iWr; | ||
| 159 | iWriteBufFill -= iWr; | ||
| 160 | } while( iWriteBufFill > 0 && iWr > 0 ); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | bool Bu::Buffer::isEos() | ||
| 165 | { | ||
| 166 | return (iReadPos >= (iReadBufFill-1)) && (rNext.isEos()); | ||
| 167 | } | ||
| 168 | |||
| diff --git a/src/stable/buffer.h b/src/stable/buffer.h new file mode 100644 index 0000000..91ec9c2 --- /dev/null +++ b/src/stable/buffer.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_BUFFER_H | ||
| 9 | #define BU_BUFFER_H | ||
| 10 | |||
| 11 | #include "bu/filter.h" | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | class Buffer : public Bu::Filter | ||
| 16 | { | ||
| 17 | public: | ||
| 18 | Buffer( Bu::Stream &rNext, int iWhat=Both, int iBufSize=4096 ); | ||
| 19 | virtual ~Buffer(); | ||
| 20 | |||
| 21 | enum | ||
| 22 | { | ||
| 23 | Write = 1, | ||
| 24 | Read = 2, | ||
| 25 | Both = 3 | ||
| 26 | }; | ||
| 27 | |||
| 28 | virtual void start(); | ||
| 29 | virtual Bu::size stop(); | ||
| 30 | |||
| 31 | virtual Bu::size read( void *pBuf, Bu::size nBytes ); | ||
| 32 | virtual Bu::size write( const void *pBuf, Bu::size nBytes ); | ||
| 33 | using Stream::write; | ||
| 34 | |||
| 35 | Bu::size getReadFill() { return iReadBufFill; } | ||
| 36 | bool isWritePending() { return iWriteBufFill > 0; } | ||
| 37 | |||
| 38 | virtual void flush(); | ||
| 39 | |||
| 40 | virtual bool isEos(); | ||
| 41 | |||
| 42 | private: | ||
| 43 | void fillReadBuf(); | ||
| 44 | |||
| 45 | private: | ||
| 46 | Bu::size sSoFar; | ||
| 47 | int iBufSize; | ||
| 48 | char *sReadBuf; | ||
| 49 | char *sWriteBuf; | ||
| 50 | int iReadBufFill; | ||
| 51 | int iReadPos; | ||
| 52 | int iWriteBufFill; | ||
| 53 | int iWritePos; | ||
| 54 | int iWhat; | ||
| 55 | }; | ||
| 56 | }; | ||
| 57 | |||
| 58 | #endif | ||
| diff --git a/src/stable/bzip2.cpp b/src/stable/bzip2.cpp new file mode 100644 index 0000000..ca007b0 --- /dev/null +++ b/src/stable/bzip2.cpp | |||
| @@ -0,0 +1,233 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/bzip2.h" | ||
| 9 | #include "bu/trace.h" | ||
| 10 | |||
| 11 | #include <bzlib.h> | ||
| 12 | |||
| 13 | #define pState ((bz_stream *)prState) | ||
| 14 | |||
| 15 | using namespace Bu; | ||
| 16 | |||
| 17 | Bu::BZip2::BZip2( Bu::Stream &rNext, int nCompression ) : | ||
| 18 | Bu::Filter( rNext ), | ||
| 19 | prState( NULL ), | ||
| 20 | nCompression( nCompression ), | ||
| 21 | sTotalOut( 0 ) | ||
| 22 | { | ||
| 23 | TRACE( nCompression ); | ||
| 24 | start(); | ||
| 25 | } | ||
| 26 | |||
| 27 | Bu::BZip2::~BZip2() | ||
| 28 | { | ||
| 29 | TRACE(); | ||
| 30 | stop(); | ||
| 31 | } | ||
| 32 | |||
| 33 | void Bu::BZip2::start() | ||
| 34 | { | ||
| 35 | TRACE(); | ||
| 36 | |||
| 37 | prState = new bz_stream; | ||
| 38 | pState->state = NULL; | ||
| 39 | pState->bzalloc = NULL; | ||
| 40 | pState->bzfree = NULL; | ||
| 41 | pState->opaque = NULL; | ||
| 42 | |||
| 43 | nBufSize = 64*1024; | ||
| 44 | pBuf = new char[nBufSize]; | ||
| 45 | } | ||
| 46 | |||
| 47 | Bu::size Bu::BZip2::stop() | ||
| 48 | { | ||
| 49 | TRACE(); | ||
| 50 | if( pState->state ) | ||
| 51 | { | ||
| 52 | if( bReading ) | ||
| 53 | { | ||
| 54 | BZ2_bzDecompressEnd( pState ); | ||
| 55 | delete[] pBuf; | ||
| 56 | pBuf = NULL; | ||
| 57 | delete pState; | ||
| 58 | prState = NULL; | ||
| 59 | return 0; | ||
| 60 | } | ||
| 61 | else | ||
| 62 | { | ||
| 63 | // Bu::size sTotal = 0; | ||
| 64 | for(;;) | ||
| 65 | { | ||
| 66 | pState->next_in = NULL; | ||
| 67 | pState->avail_in = 0; | ||
| 68 | pState->avail_out = nBufSize; | ||
| 69 | pState->next_out = pBuf; | ||
| 70 | int res = BZ2_bzCompress( pState, BZ_FINISH ); | ||
| 71 | if( pState->avail_out < nBufSize ) | ||
| 72 | { | ||
| 73 | sTotalOut += rNext.write( pBuf, nBufSize-pState->avail_out ); | ||
| 74 | } | ||
| 75 | if( res == BZ_STREAM_END ) | ||
| 76 | break; | ||
| 77 | } | ||
| 78 | BZ2_bzCompressEnd( pState ); | ||
| 79 | delete[] pBuf; | ||
| 80 | pBuf = NULL; | ||
| 81 | delete pState; | ||
| 82 | prState = NULL; | ||
| 83 | return sTotalOut; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | void Bu::BZip2::bzError( int code ) | ||
| 90 | { | ||
| 91 | TRACE( code ); | ||
| 92 | switch( code ) | ||
| 93 | { | ||
| 94 | case BZ_OK: | ||
| 95 | case BZ_RUN_OK: | ||
| 96 | case BZ_FLUSH_OK: | ||
| 97 | case BZ_FINISH_OK: | ||
| 98 | return; | ||
| 99 | |||
| 100 | case BZ_CONFIG_ERROR: | ||
| 101 | throw ExceptionBase("BZip2: Library configured improperly, reinstall."); | ||
| 102 | |||
| 103 | case BZ_SEQUENCE_ERROR: | ||
| 104 | throw ExceptionBase("BZip2: Functions were called in an invalid sequence."); | ||
| 105 | |||
| 106 | case BZ_PARAM_ERROR: | ||
| 107 | throw ExceptionBase("BZip2: Invalid parameter was passed into a function."); | ||
| 108 | |||
| 109 | case BZ_MEM_ERROR: | ||
| 110 | throw ExceptionBase("BZip2: Couldn't allocate sufficient memory."); | ||
| 111 | |||
| 112 | case BZ_DATA_ERROR: | ||
| 113 | throw ExceptionBase("BZip2: Data was corrupted before decompression."); | ||
| 114 | |||
| 115 | case BZ_DATA_ERROR_MAGIC: | ||
| 116 | throw ExceptionBase("BZip2: Stream does not appear to be bzip2 data."); | ||
| 117 | |||
| 118 | case BZ_IO_ERROR: | ||
| 119 | throw ExceptionBase("BZip2: File couldn't be read from / written to."); | ||
| 120 | |||
| 121 | case BZ_UNEXPECTED_EOF: | ||
| 122 | throw ExceptionBase("BZip2: End of file encountered before end of stream."); | ||
| 123 | |||
| 124 | case BZ_OUTBUFF_FULL: | ||
| 125 | throw ExceptionBase("BZip2: Buffer not large enough to accomidate data."); | ||
| 126 | |||
| 127 | default: | ||
| 128 | throw ExceptionBase("BZip2: Unknown error encountered."); | ||
| 129 | |||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | Bu::size Bu::BZip2::read( void *pData, Bu::size nBytes ) | ||
| 134 | { | ||
| 135 | TRACE( pData, nBytes ); | ||
| 136 | if( !pState->state ) | ||
| 137 | { | ||
| 138 | bReading = true; | ||
| 139 | BZ2_bzDecompressInit( pState, 0, 0 ); | ||
| 140 | pState->next_in = pBuf; | ||
| 141 | pState->avail_in = 0; | ||
| 142 | } | ||
| 143 | if( bReading == false ) | ||
| 144 | throw ExceptionBase("This bzip2 filter is in writing mode, you can't read."); | ||
| 145 | |||
| 146 | int nRead = 0; | ||
| 147 | int nReadTotal = pState->total_out_lo32; | ||
| 148 | pState->next_out = (char *)pData; | ||
| 149 | pState->avail_out = nBytes; | ||
| 150 | for(;;) | ||
| 151 | { | ||
| 152 | int ret = BZ2_bzDecompress( pState ); | ||
| 153 | |||
| 154 | nReadTotal += nRead-pState->avail_out; | ||
| 155 | |||
| 156 | if( ret == BZ_STREAM_END ) | ||
| 157 | { | ||
| 158 | if( pState->avail_in > 0 ) | ||
| 159 | { | ||
| 160 | if( rNext.isSeekable() ) | ||
| 161 | { | ||
| 162 | rNext.seek( -pState->avail_in ); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | return nBytes-pState->avail_out; | ||
| 166 | } | ||
| 167 | bzError( ret ); | ||
| 168 | |||
| 169 | if( pState->avail_out ) | ||
| 170 | { | ||
| 171 | if( pState->avail_in == 0 ) | ||
| 172 | { | ||
| 173 | nRead = rNext.read( pBuf, nBufSize ); | ||
| 174 | if( nRead == 0 && rNext.isEos() ) | ||
| 175 | { | ||
| 176 | throw Bu::ExceptionBase("Premature end of underlying " | ||
| 177 | "stream found reading bzip2 stream."); | ||
| 178 | } | ||
| 179 | pState->next_in = pBuf; | ||
| 180 | pState->avail_in = nRead; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | else | ||
| 184 | { | ||
| 185 | return nBytes-pState->avail_out; | ||
| 186 | } | ||
| 187 | } | ||
| 188 | return 0; | ||
| 189 | } | ||
| 190 | |||
| 191 | Bu::size Bu::BZip2::write( const void *pData, Bu::size nBytes ) | ||
| 192 | { | ||
| 193 | TRACE( pData, nBytes ); | ||
| 194 | if( !pState->state ) | ||
| 195 | { | ||
| 196 | bReading = false; | ||
| 197 | BZ2_bzCompressInit( pState, nCompression, 0, 30 ); | ||
| 198 | } | ||
| 199 | if( bReading == true ) | ||
| 200 | throw ExceptionBase("This bzip2 filter is in reading mode, you can't write."); | ||
| 201 | |||
| 202 | // Bu::size sTotalOut = 0; | ||
| 203 | pState->next_in = (char *)pData; | ||
| 204 | pState->avail_in = nBytes; | ||
| 205 | for(;;) | ||
| 206 | { | ||
| 207 | pState->avail_out = nBufSize; | ||
| 208 | pState->next_out = pBuf; | ||
| 209 | |||
| 210 | bzError( BZ2_bzCompress( pState, BZ_RUN ) ); | ||
| 211 | |||
| 212 | if( pState->avail_out < nBufSize ) | ||
| 213 | { | ||
| 214 | sTotalOut += rNext.write( pBuf, nBufSize-pState->avail_out ); | ||
| 215 | } | ||
| 216 | if( pState->avail_in == 0 ) | ||
| 217 | break; | ||
| 218 | } | ||
| 219 | |||
| 220 | return nBytes; | ||
| 221 | } | ||
| 222 | |||
| 223 | bool Bu::BZip2::isOpen() | ||
| 224 | { | ||
| 225 | TRACE(); | ||
| 226 | return (pState->state != NULL); | ||
| 227 | } | ||
| 228 | |||
| 229 | Bu::size Bu::BZip2::getCompressedSize() | ||
| 230 | { | ||
| 231 | return sTotalOut; | ||
| 232 | } | ||
| 233 | |||
| diff --git a/src/stable/bzip2.h b/src/stable/bzip2.h new file mode 100644 index 0000000..9a8d172 --- /dev/null +++ b/src/stable/bzip2.h | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_BZIP2_H | ||
| 9 | #define BU_BZIP2_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | |||
| 13 | #include "bu/filter.h" | ||
| 14 | |||
| 15 | namespace Bu | ||
| 16 | { | ||
| 17 | /** | ||
| 18 | * Provides BZip2 type compression and decompression. | ||
| 19 | * | ||
| 20 | *@ingroup Streams | ||
| 21 | *@ingroup Compression | ||
| 22 | */ | ||
| 23 | class BZip2 : public Bu::Filter | ||
| 24 | { | ||
| 25 | public: | ||
| 26 | BZip2( Bu::Stream &rNext, int nCompression=9 ); | ||
| 27 | virtual ~BZip2(); | ||
| 28 | |||
| 29 | virtual void start(); | ||
| 30 | virtual Bu::size stop(); | ||
| 31 | virtual Bu::size read( void *pBuf, Bu::size nBytes ); | ||
| 32 | virtual Bu::size write( const void *pBuf, Bu::size nBytes ); | ||
| 33 | |||
| 34 | virtual bool isOpen(); | ||
| 35 | |||
| 36 | Bu::size getCompressedSize(); | ||
| 37 | |||
| 38 | private: | ||
| 39 | void bzError( int code ); | ||
| 40 | void *prState; | ||
| 41 | bool bReading; | ||
| 42 | int nCompression; | ||
| 43 | char *pBuf; | ||
| 44 | uint32_t nBufSize; | ||
| 45 | Bu::size sTotalOut; | ||
| 46 | }; | ||
| 47 | } | ||
| 48 | |||
| 49 | #endif | ||
| diff --git a/src/stable/client.cpp b/src/stable/client.cpp new file mode 100644 index 0000000..75f6158 --- /dev/null +++ b/src/stable/client.cpp | |||
| @@ -0,0 +1,320 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/client.h" | ||
| 9 | #include "bu/tcpsocket.h" | ||
| 10 | #include <stdlib.h> | ||
| 11 | #include <errno.h> | ||
| 12 | #include "bu/protocol.h" | ||
| 13 | #include "bu/clientlink.h" | ||
| 14 | #include "bu/clientlinkfactory.h" | ||
| 15 | |||
| 16 | /** Read buffer size. */ | ||
| 17 | #define RBS (2000) // 1500 is the nominal MTU for ethernet, it's a good guess | ||
| 18 | |||
| 19 | Bu::Client::Client( Bu::TcpSocket *pSocket, | ||
| 20 | class Bu::ClientLinkFactory *pfLink ) : | ||
| 21 | pTopStream( pSocket ), | ||
| 22 | pSocket( pSocket ), | ||
| 23 | pProto( NULL ), | ||
| 24 | bWantsDisconnect( false ), | ||
| 25 | pfLink( pfLink ) | ||
| 26 | { | ||
| 27 | lFilts.prepend( pSocket ); | ||
| 28 | } | ||
| 29 | |||
| 30 | Bu::Client::~Client() | ||
| 31 | { | ||
| 32 | for( FilterList::iterator i = lFilts.begin(); i; i++ ) | ||
| 33 | { | ||
| 34 | delete *i; | ||
| 35 | } | ||
| 36 | pTopStream = pSocket = NULL; | ||
| 37 | delete pfLink; | ||
| 38 | } | ||
| 39 | |||
| 40 | void Bu::Client::processInput() | ||
| 41 | { | ||
| 42 | char buf[RBS]; | ||
| 43 | Bu::size nRead, nTotal=0; | ||
| 44 | |||
| 45 | for(;;) | ||
| 46 | { | ||
| 47 | try | ||
| 48 | { | ||
| 49 | nRead = pTopStream->read( buf, RBS ); | ||
| 50 | |||
| 51 | if( nRead == 0 ) | ||
| 52 | { | ||
| 53 | break; | ||
| 54 | } | ||
| 55 | else | ||
| 56 | { | ||
| 57 | nTotal += nRead; | ||
| 58 | qbRead.write( buf, nRead ); | ||
| 59 | if( !pTopStream->canRead() ) | ||
| 60 | break; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | catch( Bu::TcpSocketException &e ) | ||
| 64 | { | ||
| 65 | pTopStream->close(); | ||
| 66 | bWantsDisconnect = true; | ||
| 67 | break; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | if( nTotal == 0 ) | ||
| 72 | { | ||
| 73 | pTopStream->close(); | ||
| 74 | bWantsDisconnect = true; | ||
| 75 | } | ||
| 76 | |||
| 77 | if( pProto && nTotal ) | ||
| 78 | { | ||
| 79 | pProto->onNewData( this ); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | void Bu::Client::processOutput() | ||
| 84 | { | ||
| 85 | char buf[RBS]; | ||
| 86 | if( qbWrite.getSize() > 0 ) | ||
| 87 | { | ||
| 88 | int nAmnt = RBS; | ||
| 89 | nAmnt = qbWrite.peek( buf, nAmnt ); | ||
| 90 | int nReal = pTopStream->write( buf, nAmnt ); | ||
| 91 | qbWrite.seek( nReal ); | ||
| 92 | pTopStream->flush(); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | void Bu::Client::setProtocol( Protocol *pProto ) | ||
| 97 | { | ||
| 98 | this->pProto = pProto; | ||
| 99 | this->pProto->onNewConnection( this ); | ||
| 100 | } | ||
| 101 | |||
| 102 | Bu::Protocol *Bu::Client::getProtocol() | ||
| 103 | { | ||
| 104 | return pProto; | ||
| 105 | } | ||
| 106 | |||
| 107 | void Bu::Client::clearProtocol() | ||
| 108 | { | ||
| 109 | pProto = NULL; | ||
| 110 | } | ||
| 111 | /* | ||
| 112 | Bu::String &Bu::Client::getInput() | ||
| 113 | { | ||
| 114 | return sReadBuf; | ||
| 115 | } | ||
| 116 | |||
| 117 | Bu::String &Bu::Client::getOutput() | ||
| 118 | { | ||
| 119 | return sWriteBuf; | ||
| 120 | } | ||
| 121 | */ | ||
| 122 | |||
| 123 | bool Bu::Client::isOpen() | ||
| 124 | { | ||
| 125 | if( !pTopStream ) return false; | ||
| 126 | return pTopStream->isOpen(); | ||
| 127 | } | ||
| 128 | |||
| 129 | Bu::size Bu::Client::write( const Bu::String &sData ) | ||
| 130 | { | ||
| 131 | return qbWrite.write( sData.getStr(), sData.getSize() ); | ||
| 132 | } | ||
| 133 | |||
| 134 | Bu::size Bu::Client::write( const void *pData, Bu::size nBytes ) | ||
| 135 | { | ||
| 136 | return qbWrite.write( pData, nBytes ); | ||
| 137 | } | ||
| 138 | |||
| 139 | Bu::size Bu::Client::write( int8_t nData ) | ||
| 140 | { | ||
| 141 | return qbWrite.write( (const char *)&nData, sizeof(nData) ); | ||
| 142 | } | ||
| 143 | |||
| 144 | Bu::size Bu::Client::write( int16_t nData ) | ||
| 145 | { | ||
| 146 | return qbWrite.write( (const char *)&nData, sizeof(nData) ); | ||
| 147 | } | ||
| 148 | |||
| 149 | Bu::size Bu::Client::write( int32_t nData ) | ||
| 150 | { | ||
| 151 | return qbWrite.write( (const char *)&nData, sizeof(nData) ); | ||
| 152 | } | ||
| 153 | |||
| 154 | Bu::size Bu::Client::write( int64_t nData ) | ||
| 155 | { | ||
| 156 | return qbWrite.write( (const char *)&nData, sizeof(nData) ); | ||
| 157 | } | ||
| 158 | |||
| 159 | Bu::size Bu::Client::write( uint8_t nData ) | ||
| 160 | { | ||
| 161 | return qbWrite.write( (const char *)&nData, sizeof(nData) ); | ||
| 162 | } | ||
| 163 | |||
| 164 | Bu::size Bu::Client::write( uint16_t nData ) | ||
| 165 | { | ||
| 166 | return qbWrite.write( (const char *)&nData, sizeof(nData) ); | ||
| 167 | } | ||
| 168 | |||
| 169 | Bu::size Bu::Client::write( uint32_t nData ) | ||
| 170 | { | ||
| 171 | return qbWrite.write( (const char *)&nData, sizeof(nData) ); | ||
| 172 | } | ||
| 173 | |||
| 174 | Bu::size Bu::Client::write( uint64_t nData ) | ||
| 175 | { | ||
| 176 | return qbWrite.write( (const char *)&nData, sizeof(nData) ); | ||
| 177 | } | ||
| 178 | |||
| 179 | Bu::size Bu::Client::read( void *pData, Bu::size nBytes ) | ||
| 180 | { | ||
| 181 | return qbRead.read( pData, nBytes ); | ||
| 182 | } | ||
| 183 | |||
| 184 | Bu::size Bu::Client::peek( void *pData, int nBytes, int nOffset ) | ||
| 185 | { | ||
| 186 | return qbRead.peek( pData, nBytes, nOffset ); | ||
| 187 | } | ||
| 188 | |||
| 189 | Bu::size Bu::Client::getInputSize() | ||
| 190 | { | ||
| 191 | return qbRead.getSize(); | ||
| 192 | } | ||
| 193 | |||
| 194 | Bu::size Bu::Client::getOutputSize() | ||
| 195 | { | ||
| 196 | return qbWrite.getSize(); | ||
| 197 | } | ||
| 198 | |||
| 199 | const Bu::TcpSocket *Bu::Client::getSocket() const | ||
| 200 | { | ||
| 201 | return pSocket; | ||
| 202 | } | ||
| 203 | |||
| 204 | void Bu::Client::disconnect() | ||
| 205 | { | ||
| 206 | bWantsDisconnect = true; | ||
| 207 | } | ||
| 208 | |||
| 209 | bool Bu::Client::wantsDisconnect() | ||
| 210 | { | ||
| 211 | return bWantsDisconnect; | ||
| 212 | } | ||
| 213 | |||
| 214 | void Bu::Client::close() | ||
| 215 | { | ||
| 216 | pTopStream->close(); | ||
| 217 | } | ||
| 218 | |||
| 219 | Bu::ClientLink *Bu::Client::getLink() | ||
| 220 | { | ||
| 221 | return pfLink->createLink( this ); | ||
| 222 | } | ||
| 223 | |||
| 224 | void Bu::Client::onMessage( const Bu::String &sMsg ) | ||
| 225 | { | ||
| 226 | if( pProto ) | ||
| 227 | pProto->onMessage( this, sMsg ); | ||
| 228 | } | ||
| 229 | |||
| 230 | void Bu::Client::tick() | ||
| 231 | { | ||
| 232 | if( pProto ) | ||
| 233 | pProto->onTick( this ); | ||
| 234 | } | ||
| 235 | |||
| 236 | Bu::size Bu::Client::tell() | ||
| 237 | { | ||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | |||
| 241 | void Bu::Client::seek( Bu::size offset ) | ||
| 242 | { | ||
| 243 | return qbRead.seek( offset ); | ||
| 244 | } | ||
| 245 | |||
| 246 | void Bu::Client::setPos( Bu::size ) | ||
| 247 | { | ||
| 248 | throw Bu::ExceptionBase(); | ||
| 249 | } | ||
| 250 | |||
| 251 | void Bu::Client::setPosEnd( Bu::size ) | ||
| 252 | { | ||
| 253 | throw Bu::ExceptionBase(); | ||
| 254 | } | ||
| 255 | |||
| 256 | bool Bu::Client::isEos() | ||
| 257 | { | ||
| 258 | return true; | ||
| 259 | } | ||
| 260 | |||
| 261 | void Bu::Client::flush() | ||
| 262 | { | ||
| 263 | processOutput(); | ||
| 264 | } | ||
| 265 | |||
| 266 | bool Bu::Client::canRead() | ||
| 267 | { | ||
| 268 | return qbRead.getSize() > 0; | ||
| 269 | } | ||
| 270 | |||
| 271 | bool Bu::Client::canWrite() | ||
| 272 | { | ||
| 273 | return true; | ||
| 274 | } | ||
| 275 | |||
| 276 | bool Bu::Client::isReadable() | ||
| 277 | { | ||
| 278 | return true; | ||
| 279 | } | ||
| 280 | |||
| 281 | bool Bu::Client::isWritable() | ||
| 282 | { | ||
| 283 | return true; | ||
| 284 | } | ||
| 285 | |||
| 286 | bool Bu::Client::isSeekable() | ||
| 287 | { | ||
| 288 | return false; | ||
| 289 | } | ||
| 290 | |||
| 291 | bool Bu::Client::isBlocking() | ||
| 292 | { | ||
| 293 | return false; | ||
| 294 | } | ||
| 295 | |||
| 296 | void Bu::Client::setBlocking( bool ) | ||
| 297 | { | ||
| 298 | throw Bu::ExceptionBase(); | ||
| 299 | } | ||
| 300 | |||
| 301 | void Bu::Client::setSize( Bu::size ) | ||
| 302 | { | ||
| 303 | throw Bu::ExceptionBase(); | ||
| 304 | } | ||
| 305 | |||
| 306 | Bu::size Bu::Client::getSize() const | ||
| 307 | { | ||
| 308 | return 0; | ||
| 309 | } | ||
| 310 | |||
| 311 | Bu::size Bu::Client::getBlockSize() const | ||
| 312 | { | ||
| 313 | return pSocket->getBlockSize(); | ||
| 314 | } | ||
| 315 | |||
| 316 | Bu::String Bu::Client::getLocation() const | ||
| 317 | { | ||
| 318 | return pSocket->getLocation(); | ||
| 319 | } | ||
| 320 | |||
| diff --git a/src/stable/client.h b/src/stable/client.h new file mode 100644 index 0000000..119c2c1 --- /dev/null +++ b/src/stable/client.h | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_CLIENT_H | ||
| 9 | #define BU_CLIENT_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | |||
| 13 | #include "bu/config.h" | ||
| 14 | #include "bu/string.h" | ||
| 15 | #include "bu/queuebuf.h" | ||
| 16 | |||
| 17 | namespace Bu | ||
| 18 | { | ||
| 19 | class Protocol; | ||
| 20 | class Stream; | ||
| 21 | class TcpSocket; | ||
| 22 | class ClientLinkFactory; | ||
| 23 | |||
| 24 | /** | ||
| 25 | *@ingroup Serving | ||
| 26 | */ | ||
| 27 | class Client : public Bu::Stream | ||
| 28 | { | ||
| 29 | public: | ||
| 30 | Client( Bu::TcpSocket *pSocket, Bu::ClientLinkFactory *pfLink ); | ||
| 31 | virtual ~Client(); | ||
| 32 | |||
| 33 | void processInput(); | ||
| 34 | void processOutput(); | ||
| 35 | |||
| 36 | //Bu::String &getInput(); | ||
| 37 | //Bu::String &getOutput(); | ||
| 38 | Bu::size write( const Bu::String &sData ); | ||
| 39 | Bu::size write( const void *pData, Bu::size nBytes ); | ||
| 40 | Bu::size write( int8_t nData ); | ||
| 41 | Bu::size write( int16_t nData ); | ||
| 42 | Bu::size write( int32_t nData ); | ||
| 43 | Bu::size write( int64_t nData ); | ||
| 44 | Bu::size write( uint8_t nData ); | ||
| 45 | Bu::size write( uint16_t nData ); | ||
| 46 | Bu::size write( uint32_t nData ); | ||
| 47 | Bu::size write( uint64_t nData ); | ||
| 48 | Bu::size read( void *pData, Bu::size nBytes ); | ||
| 49 | Bu::size peek( void *pData, int nBytes, int nOffset=0 ); | ||
| 50 | // void seek( int nBytes ); | ||
| 51 | Bu::size getInputSize(); | ||
| 52 | Bu::size getOutputSize(); | ||
| 53 | |||
| 54 | void setProtocol( Protocol *pProto ); | ||
| 55 | Bu::Protocol *getProtocol(); | ||
| 56 | void clearProtocol(); | ||
| 57 | |||
| 58 | bool isOpen(); | ||
| 59 | void close(); | ||
| 60 | void tick(); | ||
| 61 | |||
| 62 | const Bu::TcpSocket *getSocket() const; | ||
| 63 | |||
| 64 | void disconnect(); | ||
| 65 | bool wantsDisconnect(); | ||
| 66 | |||
| 67 | class ClientLink *getLink(); | ||
| 68 | |||
| 69 | void onMessage( const Bu::String &sMsg ); | ||
| 70 | |||
| 71 | bool hasOutput() { return qbWrite.getSize() > 0; } | ||
| 72 | bool hasInput() { return qbRead.getSize() > 0; } | ||
| 73 | |||
| 74 | template<typename filter> | ||
| 75 | void pushFilter() | ||
| 76 | { | ||
| 77 | filter *pFlt = new filter( *pTopStream ); | ||
| 78 | pTopStream = pFlt; | ||
| 79 | lFilts.prepend( pFlt ); | ||
| 80 | } | ||
| 81 | |||
| 82 | template<typename filter, typename p1t> | ||
| 83 | void pushFilter( p1t p1 ) | ||
| 84 | { | ||
| 85 | filter *pFlt = new filter( *pTopStream, p1 ); | ||
| 86 | pTopStream = pFlt; | ||
| 87 | lFilts.prepend( pFlt ); | ||
| 88 | } | ||
| 89 | |||
| 90 | template<typename filter, typename p1t, typename p2t> | ||
| 91 | void pushFilter( p1t p1, p2t p2 ) | ||
| 92 | { | ||
| 93 | filter *pFlt = new filter( *pTopStream, p1, p2 ); | ||
| 94 | pTopStream = pFlt; | ||
| 95 | lFilts.prepend( pFlt ); | ||
| 96 | } | ||
| 97 | |||
| 98 | /* | ||
| 99 | * These are required to qualify as a stream, I dunno how many will | ||
| 100 | * be implemented. | ||
| 101 | */ | ||
| 102 | virtual Bu::size tell(); | ||
| 103 | virtual void seek( Bu::size offset ); | ||
| 104 | virtual void setPos( Bu::size pos ); | ||
| 105 | virtual void setPosEnd( Bu::size pos ); | ||
| 106 | virtual bool isEos(); | ||
| 107 | virtual void flush(); | ||
| 108 | virtual bool canRead(); | ||
| 109 | virtual bool canWrite(); | ||
| 110 | virtual bool isReadable(); | ||
| 111 | virtual bool isWritable(); | ||
| 112 | virtual bool isSeekable(); | ||
| 113 | virtual bool isBlocking(); | ||
| 114 | virtual void setBlocking( bool bBlocking=true ); | ||
| 115 | virtual void setSize( Bu::size iSize ); | ||
| 116 | virtual size getSize() const; | ||
| 117 | virtual size getBlockSize() const; | ||
| 118 | virtual Bu::String getLocation() const; | ||
| 119 | |||
| 120 | private: | ||
| 121 | typedef Bu::List<Bu::Stream *> FilterList; | ||
| 122 | FilterList lFilts; | ||
| 123 | Bu::Stream *pTopStream; | ||
| 124 | Bu::TcpSocket *pSocket; | ||
| 125 | Bu::Protocol *pProto; | ||
| 126 | Bu::QueueBuf qbRead; | ||
| 127 | Bu::QueueBuf qbWrite; | ||
| 128 | bool bWantsDisconnect; | ||
| 129 | class Bu::ClientLinkFactory *pfLink; | ||
| 130 | }; | ||
| 131 | } | ||
| 132 | |||
| 133 | #endif | ||
| diff --git a/src/stable/clientlink.cpp b/src/stable/clientlink.cpp new file mode 100644 index 0000000..ce8b2cb --- /dev/null +++ b/src/stable/clientlink.cpp | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/clientlink.h" | ||
| 9 | |||
| 10 | Bu::ClientLink::ClientLink() | ||
| 11 | { | ||
| 12 | } | ||
| 13 | |||
| 14 | Bu::ClientLink::~ClientLink() | ||
| 15 | { | ||
| 16 | } | ||
| 17 | |||
| diff --git a/src/stable/clientlink.h b/src/stable/clientlink.h new file mode 100644 index 0000000..e4618e7 --- /dev/null +++ b/src/stable/clientlink.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_CLIENT_LINK_H | ||
| 9 | #define BU_CLIENT_LINK_H | ||
| 10 | |||
| 11 | #include "bu/string.h" | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | class ClientLink | ||
| 16 | { | ||
| 17 | public: | ||
| 18 | ClientLink(); | ||
| 19 | virtual ~ClientLink(); | ||
| 20 | |||
| 21 | virtual void sendMessage( const Bu::String &sMsg )=0; | ||
| 22 | }; | ||
| 23 | }; | ||
| 24 | |||
| 25 | #endif | ||
| diff --git a/src/stable/clientlinkfactory.cpp b/src/stable/clientlinkfactory.cpp new file mode 100644 index 0000000..f48e11e --- /dev/null +++ b/src/stable/clientlinkfactory.cpp | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/clientlinkfactory.h" | ||
| 9 | |||
| 10 | Bu::ClientLinkFactory::ClientLinkFactory() | ||
| 11 | { | ||
| 12 | } | ||
| 13 | |||
| 14 | Bu::ClientLinkFactory::~ClientLinkFactory() | ||
| 15 | { | ||
| 16 | } | ||
| 17 | |||
| diff --git a/src/stable/clientlinkfactory.h b/src/stable/clientlinkfactory.h new file mode 100644 index 0000000..21d3363 --- /dev/null +++ b/src/stable/clientlinkfactory.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_CLIENT_LINK_FACTORY_H | ||
| 9 | #define BU_CLIENT_LINK_FACTORY_H | ||
| 10 | |||
| 11 | namespace Bu | ||
| 12 | { | ||
| 13 | class Client; | ||
| 14 | class ClientLink; | ||
| 15 | |||
| 16 | class ClientLinkFactory | ||
| 17 | { | ||
| 18 | public: | ||
| 19 | ClientLinkFactory(); | ||
| 20 | virtual ~ClientLinkFactory(); | ||
| 21 | |||
| 22 | virtual Bu::ClientLink *createLink( Bu::Client *pClient )=0; | ||
| 23 | }; | ||
| 24 | }; | ||
| 25 | |||
| 26 | #endif | ||
| diff --git a/src/stable/condition.cpp b/src/stable/condition.cpp new file mode 100644 index 0000000..2f55ce2 --- /dev/null +++ b/src/stable/condition.cpp | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <sys/time.h> | ||
| 9 | |||
| 10 | #include "bu/condition.h" | ||
| 11 | |||
| 12 | Bu::Condition::Condition() | ||
| 13 | { | ||
| 14 | pthread_cond_init( &cond, NULL ); | ||
| 15 | } | ||
| 16 | |||
| 17 | Bu::Condition::~Condition() | ||
| 18 | { | ||
| 19 | pthread_cond_destroy( &cond ); | ||
| 20 | } | ||
| 21 | |||
| 22 | int Bu::Condition::wait() | ||
| 23 | { | ||
| 24 | return pthread_cond_wait( &cond, &mutex ); | ||
| 25 | } | ||
| 26 | |||
| 27 | int Bu::Condition::wait( int nSec, int nUSec ) | ||
| 28 | { | ||
| 29 | struct timeval now; | ||
| 30 | struct timespec timeout; | ||
| 31 | struct timezone tz; | ||
| 32 | |||
| 33 | gettimeofday( &now, &tz ); | ||
| 34 | timeout.tv_sec = now.tv_sec + nSec + ((now.tv_usec + nUSec)/1000000); | ||
| 35 | timeout.tv_nsec = ((now.tv_usec + nUSec)%1000000)*1000; | ||
| 36 | |||
| 37 | return pthread_cond_timedwait( &cond, &mutex, &timeout ); | ||
| 38 | } | ||
| 39 | |||
| 40 | int Bu::Condition::signal() | ||
| 41 | { | ||
| 42 | return pthread_cond_signal( &cond ); | ||
| 43 | } | ||
| 44 | |||
| 45 | int Bu::Condition::broadcast() | ||
| 46 | { | ||
| 47 | return pthread_cond_broadcast( &cond ); | ||
| 48 | } | ||
| 49 | |||
| diff --git a/src/stable/condition.h b/src/stable/condition.h new file mode 100644 index 0000000..71634f5 --- /dev/null +++ b/src/stable/condition.h | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_CONDITION_H | ||
| 9 | #define BU_CONDITION_H | ||
| 10 | |||
| 11 | #include <pthread.h> | ||
| 12 | |||
| 13 | #include "bu/mutex.h" | ||
| 14 | |||
| 15 | namespace Bu | ||
| 16 | { | ||
| 17 | /** | ||
| 18 | * Ito condition. This is a fairly simple condition mechanism. As you may | ||
| 19 | * notice this class inherits from the Mutex class, this is because all | ||
| 20 | * conditions must be within a locked block. The standard usage of a | ||
| 21 | * condition is to pause one thread, perhaps indefinately, until another | ||
| 22 | * thread signals that it is alright to procede. | ||
| 23 | * <br> | ||
| 24 | * Standard usage for the thread that wants to wait is as follows: | ||
| 25 | * <pre> | ||
| 26 | * Condition cond; | ||
| 27 | * ... // Perform setup and enter your run loop | ||
| 28 | * cond.lock(); | ||
| 29 | * while( !isFinished() ) // Could be anything you're waiting for | ||
| 30 | * cond.wait(); | ||
| 31 | * ... // Take care of what you have to. | ||
| 32 | * cond.unlock(); | ||
| 33 | * </pre> | ||
| 34 | * The usage for the triggering thread is much simpler, when it needs to | ||
| 35 | * tell the others that it's time to grab some data it calls either signal | ||
| 36 | * or broadcast. See both of those functions for the difference. | ||
| 37 | *@ingroup Threading | ||
| 38 | */ | ||
| 39 | class Condition : public Mutex | ||
| 40 | { | ||
| 41 | public: | ||
| 42 | /** | ||
| 43 | * Create a condition. | ||
| 44 | */ | ||
| 45 | Condition(); | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Destroy a condition. | ||
| 49 | */ | ||
| 50 | ~Condition(); | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Wait forever, or until signalled. This has to be called from within | ||
| 54 | * a locked section, i.e. before calling this this object's lock | ||
| 55 | * function should be called. | ||
| 56 | */ | ||
| 57 | int wait(); | ||
| 58 | |||
| 59 | /** | ||
| 60 | * Wait for a maximum of nSec seconds and nUSec micro-seconds or until | ||
| 61 | * signalled. This is a little more friendly function if you want to | ||
| 62 | * perform other operations in the thrad loop that calls this function. | ||
| 63 | * Like the other wait function, this must be inside a locked section. | ||
| 64 | *@param nSec The seconds to wait. | ||
| 65 | *@param nUSec the micro-seconds to wait. | ||
| 66 | */ | ||
| 67 | int wait( int nSec, int nUSec ); | ||
| 68 | |||
| 69 | /** | ||
| 70 | * Notify the next thread waiting on this condition that they can go | ||
| 71 | * ahead. This only signals one thread, the next one in the condition | ||
| 72 | * queue, that it is safe to procede with whatever operation was being | ||
| 73 | * waited on. | ||
| 74 | */ | ||
| 75 | int signal(); | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Notify all threads waiting on this condition that they can go ahead | ||
| 79 | * now. This function is slower than signal, but more effective in | ||
| 80 | * certain situations where you may not know how many threads should be | ||
| 81 | * activated. | ||
| 82 | */ | ||
| 83 | int broadcast(); | ||
| 84 | |||
| 85 | private: | ||
| 86 | pthread_cond_t cond; /**< Internal condition reference. */ | ||
| 87 | }; | ||
| 88 | } | ||
| 89 | |||
| 90 | #endif | ||
| diff --git a/src/stable/conduit.cpp b/src/stable/conduit.cpp new file mode 100644 index 0000000..c9ccdc4 --- /dev/null +++ b/src/stable/conduit.cpp | |||
| @@ -0,0 +1,233 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/conduit.h" | ||
| 9 | |||
| 10 | Bu::Conduit::Conduit( int iBlockSize ) : | ||
| 11 | qb( iBlockSize ), | ||
| 12 | bBlocking( true ), | ||
| 13 | bOpen( true ) | ||
| 14 | { | ||
| 15 | } | ||
| 16 | |||
| 17 | Bu::Conduit::~Conduit() | ||
| 18 | { | ||
| 19 | } | ||
| 20 | |||
| 21 | void Bu::Conduit::close() | ||
| 22 | { | ||
| 23 | im.lock(); | ||
| 24 | // qb.close(); | ||
| 25 | bOpen = false; | ||
| 26 | |||
| 27 | cBlock.signal(); | ||
| 28 | im.unlock(); | ||
| 29 | } | ||
| 30 | |||
| 31 | #include <stdio.h> | ||
| 32 | Bu::size Bu::Conduit::read( void *pBuf, Bu::size nBytes ) | ||
| 33 | { | ||
| 34 | if( !isOpen() ) | ||
| 35 | { | ||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | im.lock(); | ||
| 39 | if( bBlocking ) | ||
| 40 | { | ||
| 41 | im.unlock(); | ||
| 42 | cBlock.lock(); | ||
| 43 | for(;;) | ||
| 44 | { | ||
| 45 | im.lock(); | ||
| 46 | if( qb.getSize() == 0 && bOpen == false ) | ||
| 47 | { | ||
| 48 | im.unlock(); | ||
| 49 | cBlock.unlock(); | ||
| 50 | return 0; | ||
| 51 | } | ||
| 52 | else if( qb.getSize() > 0 ) | ||
| 53 | { | ||
| 54 | im.unlock(); | ||
| 55 | break; | ||
| 56 | } | ||
| 57 | im.unlock(); | ||
| 58 | |||
| 59 | cBlock.wait(); | ||
| 60 | } | ||
| 61 | |||
| 62 | im.lock(); | ||
| 63 | Bu::size iRet = qb.read( pBuf, nBytes ); | ||
| 64 | im.unlock(); | ||
| 65 | |||
| 66 | cBlock.unlock(); | ||
| 67 | return iRet; | ||
| 68 | } | ||
| 69 | else | ||
| 70 | { | ||
| 71 | Bu::size iRet = qb.read( pBuf, nBytes ); | ||
| 72 | im.unlock(); | ||
| 73 | |||
| 74 | return iRet; | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | Bu::size Bu::Conduit::peek( void *pBuf, Bu::size nBytes ) | ||
| 79 | { | ||
| 80 | im.lock(); | ||
| 81 | Bu::size iRet = qb.peek( pBuf, nBytes ); | ||
| 82 | im.unlock(); | ||
| 83 | |||
| 84 | return iRet; | ||
| 85 | } | ||
| 86 | |||
| 87 | Bu::size Bu::Conduit::peek( void *pBuf, Bu::size nBytes, Bu::size nSkip ) | ||
| 88 | { | ||
| 89 | im.lock(); | ||
| 90 | Bu::size iRet = qb.peek( pBuf, nBytes, nSkip ); | ||
| 91 | im.unlock(); | ||
| 92 | |||
| 93 | return iRet; | ||
| 94 | } | ||
| 95 | |||
| 96 | Bu::size Bu::Conduit::write( const void *pBuf, Bu::size nBytes ) | ||
| 97 | { | ||
| 98 | im.lock(); | ||
| 99 | if( bOpen == false ) | ||
| 100 | { | ||
| 101 | im.unlock(); | ||
| 102 | return 0; | ||
| 103 | } | ||
| 104 | Bu::size sRet = qb.write( pBuf, nBytes ); | ||
| 105 | cBlock.signal(); | ||
| 106 | im.unlock(); | ||
| 107 | |||
| 108 | return sRet; | ||
| 109 | } | ||
| 110 | |||
| 111 | Bu::size Bu::Conduit::tell() | ||
| 112 | { | ||
| 113 | im.lock(); | ||
| 114 | Bu::size sRet = qb.tell(); | ||
| 115 | im.unlock(); | ||
| 116 | return sRet; | ||
| 117 | } | ||
| 118 | |||
| 119 | void Bu::Conduit::seek( Bu::size ) | ||
| 120 | { | ||
| 121 | } | ||
| 122 | |||
| 123 | void Bu::Conduit::setPos( Bu::size ) | ||
| 124 | { | ||
| 125 | } | ||
| 126 | |||
| 127 | void Bu::Conduit::setPosEnd( Bu::size ) | ||
| 128 | { | ||
| 129 | } | ||
| 130 | |||
| 131 | bool Bu::Conduit::isEos() | ||
| 132 | { | ||
| 133 | im.lock(); | ||
| 134 | bool bRet = qb.isEos(); | ||
| 135 | im.unlock(); | ||
| 136 | return bRet; | ||
| 137 | } | ||
| 138 | |||
| 139 | bool Bu::Conduit::isOpen() | ||
| 140 | { | ||
| 141 | im.lock(); | ||
| 142 | bool bRet = bOpen || (qb.getSize() > 0); | ||
| 143 | im.unlock(); | ||
| 144 | return bRet; | ||
| 145 | } | ||
| 146 | |||
| 147 | void Bu::Conduit::flush() | ||
| 148 | { | ||
| 149 | } | ||
| 150 | |||
| 151 | bool Bu::Conduit::canRead() | ||
| 152 | { | ||
| 153 | im.lock(); | ||
| 154 | bool bRet = qb.canRead(); | ||
| 155 | im.unlock(); | ||
| 156 | return bRet; | ||
| 157 | } | ||
| 158 | |||
| 159 | bool Bu::Conduit::canWrite() | ||
| 160 | { | ||
| 161 | im.lock(); | ||
| 162 | bool bRet = qb.canWrite(); | ||
| 163 | im.unlock(); | ||
| 164 | return bRet; | ||
| 165 | } | ||
| 166 | |||
| 167 | bool Bu::Conduit::isReadable() | ||
| 168 | { | ||
| 169 | im.lock(); | ||
| 170 | bool bRet = qb.isReadable(); | ||
| 171 | im.unlock(); | ||
| 172 | return bRet; | ||
| 173 | } | ||
| 174 | |||
| 175 | bool Bu::Conduit::isWritable() | ||
| 176 | { | ||
| 177 | im.lock(); | ||
| 178 | bool bRet = qb.isWritable(); | ||
| 179 | im.unlock(); | ||
| 180 | return bRet; | ||
| 181 | } | ||
| 182 | |||
| 183 | bool Bu::Conduit::isSeekable() | ||
| 184 | { | ||
| 185 | im.lock(); | ||
| 186 | bool bRet = qb.isSeekable(); | ||
| 187 | im.unlock(); | ||
| 188 | return bRet; | ||
| 189 | } | ||
| 190 | |||
| 191 | bool Bu::Conduit::isBlocking() | ||
| 192 | { | ||
| 193 | im.lock(); | ||
| 194 | bool bRet = bBlocking; | ||
| 195 | im.unlock(); | ||
| 196 | return bRet; | ||
| 197 | } | ||
| 198 | |||
| 199 | void Bu::Conduit::setBlocking( bool bBlocking ) | ||
| 200 | { | ||
| 201 | im.lock(); | ||
| 202 | this->bBlocking = bBlocking; | ||
| 203 | im.unlock(); | ||
| 204 | } | ||
| 205 | |||
| 206 | void Bu::Conduit::setSize( Bu::size ) | ||
| 207 | { | ||
| 208 | } | ||
| 209 | |||
| 210 | Bu::size Bu::Conduit::getSize() const | ||
| 211 | { | ||
| 212 | im.lock(); | ||
| 213 | Bu::size sRet = qb.getSize(); | ||
| 214 | im.unlock(); | ||
| 215 | return sRet; | ||
| 216 | } | ||
| 217 | |||
| 218 | Bu::size Bu::Conduit::getBlockSize() const | ||
| 219 | { | ||
| 220 | im.lock(); | ||
| 221 | Bu::size sRet = qb.getBlockSize(); | ||
| 222 | im.unlock(); | ||
| 223 | return sRet; | ||
| 224 | } | ||
| 225 | |||
| 226 | Bu::String Bu::Conduit::getLocation() const | ||
| 227 | { | ||
| 228 | im.lock(); | ||
| 229 | Bu::String sRet = qb.getLocation(); | ||
| 230 | im.unlock(); | ||
| 231 | return sRet; | ||
| 232 | } | ||
| 233 | |||
| diff --git a/src/stable/conduit.h b/src/stable/conduit.h new file mode 100644 index 0000000..9babaaf --- /dev/null +++ b/src/stable/conduit.h | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_CONDUIT_H | ||
| 9 | #define BU_CONDUIT_H | ||
| 10 | |||
| 11 | #include "bu/stream.h" | ||
| 12 | #include "bu/string.h" | ||
| 13 | #include "bu/queuebuf.h" | ||
| 14 | #include "bu/mutex.h" | ||
| 15 | #include "bu/condition.h" | ||
| 16 | |||
| 17 | namespace Bu | ||
| 18 | { | ||
| 19 | /** | ||
| 20 | * Simple inter-thread communication stream. This acts like a pair of | ||
| 21 | * pipes for stream communication between any two things, but without the | ||
| 22 | * use of pipes, making this a bad choice for IPC. | ||
| 23 | */ | ||
| 24 | class Conduit : public Stream | ||
| 25 | { | ||
| 26 | public: | ||
| 27 | Conduit( int iBlockSize=256 ); | ||
| 28 | virtual ~Conduit(); | ||
| 29 | |||
| 30 | virtual void close(); | ||
| 31 | virtual Bu::size read( void *pBuf, Bu::size nBytes ); | ||
| 32 | virtual Bu::size peek( void *pBuf, Bu::size nBytes ); | ||
| 33 | virtual Bu::size peek( void *pBuf, Bu::size nBytes, Bu::size nSkip ); | ||
| 34 | virtual Bu::size write( const void *pBuf, Bu::size nBytes ); | ||
| 35 | virtual Bu::size tell(); | ||
| 36 | virtual void seek( Bu::size offset ); | ||
| 37 | virtual void setPos( Bu::size pos ); | ||
| 38 | virtual void setPosEnd( Bu::size pos ); | ||
| 39 | virtual bool isEos(); | ||
| 40 | virtual bool isOpen(); | ||
| 41 | virtual void flush(); | ||
| 42 | virtual bool canRead(); | ||
| 43 | virtual bool canWrite(); | ||
| 44 | virtual bool isReadable(); | ||
| 45 | virtual bool isWritable(); | ||
| 46 | virtual bool isSeekable(); | ||
| 47 | virtual bool isBlocking(); | ||
| 48 | virtual void setBlocking( bool bBlocking=true ); | ||
| 49 | virtual void setSize( Bu::size iSize ); | ||
| 50 | |||
| 51 | virtual size getSize() const; | ||
| 52 | virtual size getBlockSize() const; | ||
| 53 | virtual Bu::String getLocation() const; | ||
| 54 | |||
| 55 | private: | ||
| 56 | QueueBuf qb; | ||
| 57 | mutable Mutex im; | ||
| 58 | Condition cBlock; | ||
| 59 | bool bBlocking; | ||
| 60 | bool bOpen; | ||
| 61 | }; | ||
| 62 | } | ||
| 63 | |||
| 64 | #endif | ||
| diff --git a/src/stable/crypt.cpp b/src/stable/crypt.cpp new file mode 100644 index 0000000..eb87479 --- /dev/null +++ b/src/stable/crypt.cpp | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/crypt.h" | ||
| 9 | #include "bu/md5.h" | ||
| 10 | #include "bu/base64.h" | ||
| 11 | #include "bu/membuf.h" | ||
| 12 | #include "bu/file.h" | ||
| 13 | |||
| 14 | Bu::String Bu::cryptPass( const Bu::String &sPass, const Bu::String &sSalt ) | ||
| 15 | { | ||
| 16 | Bu::Md5 md5; | ||
| 17 | Bu::MemBuf mbOut; | ||
| 18 | Bu::Base64 b64Out( mbOut ); | ||
| 19 | |||
| 20 | Bu::String::const_iterator i = sSalt.find('$'); | ||
| 21 | Bu::String sSaltSml = sSalt.getSubStr( sSalt.begin(), i ); | ||
| 22 | |||
| 23 | md5.addData( sPass ); | ||
| 24 | md5.addData( sSaltSml ); | ||
| 25 | md5.writeResult( b64Out ); | ||
| 26 | |||
| 27 | b64Out.stop(); | ||
| 28 | |||
| 29 | return sSaltSml + "$" + mbOut.getString(); | ||
| 30 | } | ||
| 31 | |||
| 32 | Bu::String Bu::cryptPass( const Bu::String &sPass ) | ||
| 33 | { | ||
| 34 | Bu::MemBuf mbSalt; | ||
| 35 | Bu::Base64 b64Salt( mbSalt ); | ||
| 36 | Bu::File fRand("/dev/urandom", Bu::File::Read ); | ||
| 37 | |||
| 38 | #define STR 6 | ||
| 39 | char buf[STR]; | ||
| 40 | fRand.read( buf, STR ); | ||
| 41 | b64Salt.write( buf, STR ); | ||
| 42 | |||
| 43 | b64Salt.stop(); | ||
| 44 | |||
| 45 | return cryptPass( sPass, mbSalt.getString() ); | ||
| 46 | } | ||
| 47 | |||
| diff --git a/src/stable/crypt.h b/src/stable/crypt.h new file mode 100644 index 0000000..a94402a --- /dev/null +++ b/src/stable/crypt.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_CRYPT_H | ||
| 9 | #define BU_CRYPT_H | ||
| 10 | |||
| 11 | #include "bu/string.h" | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | String cryptPass( const Bu::String &sPass, const Bu::String &sSalt ); | ||
| 16 | String cryptPass( const Bu::String &sPass ); | ||
| 17 | }; | ||
| 18 | |||
| 19 | #endif | ||
| diff --git a/src/stable/cryptohash.cpp b/src/stable/cryptohash.cpp new file mode 100644 index 0000000..ddd293c --- /dev/null +++ b/src/stable/cryptohash.cpp | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/cryptohash.h" | ||
| 9 | |||
| 10 | Bu::CryptoHash::CryptoHash() | ||
| 11 | { | ||
| 12 | } | ||
| 13 | |||
| 14 | Bu::CryptoHash::~CryptoHash() | ||
| 15 | { | ||
| 16 | } | ||
| 17 | |||
| 18 | void Bu::CryptoHash::addData( const Bu::String &sData ) | ||
| 19 | { | ||
| 20 | addData( sData.getStr(), sData.getSize() ); | ||
| 21 | } | ||
| 22 | |||
| 23 | Bu::String Bu::CryptoHash::getHexResult() | ||
| 24 | { | ||
| 25 | Bu::String sResult = getResult(); | ||
| 26 | Bu::String sRet( 2*sResult.getSize() ); | ||
| 27 | static const char hex_tab[] = {"0123456789abcdef"}; | ||
| 28 | |||
| 29 | int k = 0; | ||
| 30 | for( int i = 0; i < sResult.getSize(); i++ ) | ||
| 31 | { | ||
| 32 | sRet[k++] = hex_tab[(((unsigned char)sResult[i])>>4) & 0xF]; | ||
| 33 | sRet[k++] = hex_tab[((unsigned char)sResult[i]) & 0xF]; | ||
| 34 | } | ||
| 35 | |||
| 36 | return sRet; | ||
| 37 | } | ||
| 38 | |||
| diff --git a/src/stable/cryptohash.h b/src/stable/cryptohash.h new file mode 100644 index 0000000..bc5435f --- /dev/null +++ b/src/stable/cryptohash.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_CRYPTO_HASH_H | ||
| 9 | #define BU_CRYPTO_HASH_H | ||
| 10 | |||
| 11 | #include "bu/string.h" | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | class Stream; | ||
| 16 | |||
| 17 | class CryptoHash | ||
| 18 | { | ||
| 19 | public: | ||
| 20 | CryptoHash(); | ||
| 21 | virtual ~CryptoHash(); | ||
| 22 | |||
| 23 | virtual void reset() = 0; | ||
| 24 | virtual void setSalt( const Bu::String &sSalt ) = 0; | ||
| 25 | virtual void addData( const void *sData, int iSize ) = 0; | ||
| 26 | virtual void addData( const Bu::String &sData ); | ||
| 27 | virtual String getResult() = 0; | ||
| 28 | virtual void writeResult( Stream &sOut ) = 0; | ||
| 29 | virtual Bu::String getHexResult(); | ||
| 30 | }; | ||
| 31 | }; | ||
| 32 | |||
| 33 | #endif | ||
| diff --git a/src/stable/csvreader.cpp b/src/stable/csvreader.cpp new file mode 100644 index 0000000..4da7883 --- /dev/null +++ b/src/stable/csvreader.cpp | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/csvreader.h" | ||
| 9 | #include "bu/stream.h" | ||
| 10 | |||
| 11 | #include "bu/sio.h" | ||
| 12 | using namespace Bu; | ||
| 13 | |||
| 14 | Bu::CsvReader::CsvReader( Bu::Stream &sIn, Bu::CsvReader::Style eStyle ) : | ||
| 15 | sIn( sIn ) | ||
| 16 | { | ||
| 17 | switch( eStyle ) | ||
| 18 | { | ||
| 19 | case styleExcel: | ||
| 20 | sDecode = Bu::slot( &decodeExcel ); | ||
| 21 | break; | ||
| 22 | |||
| 23 | case styleC: | ||
| 24 | sDecode = Bu::slot( &decodeC ); | ||
| 25 | break; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | Bu::CsvReader::CsvReader( Bu::Stream &sIn, | ||
| 30 | Bu::CsvReader::DecodeSignal sDecode ) : | ||
| 31 | sIn( sIn ), | ||
| 32 | sDecode( sDecode ) | ||
| 33 | { | ||
| 34 | } | ||
| 35 | |||
| 36 | Bu::CsvReader::~CsvReader() | ||
| 37 | { | ||
| 38 | } | ||
| 39 | |||
| 40 | Bu::StrArray Bu::CsvReader::readLine() | ||
| 41 | { | ||
| 42 | Bu::StrArray aVals; | ||
| 43 | |||
| 44 | Bu::String sLine = sIn.readLine(); | ||
| 45 | |||
| 46 | if( !sLine.isSet() ) | ||
| 47 | return Bu::StrArray(); | ||
| 48 | |||
| 49 | Bu::String::iterator i = sLine.begin(); | ||
| 50 | |||
| 51 | aVals.append( sDecode( i ) ); | ||
| 52 | |||
| 53 | while( i ) | ||
| 54 | { | ||
| 55 | if( *i == ',' ) | ||
| 56 | { | ||
| 57 | i++; | ||
| 58 | if( !i ) | ||
| 59 | { | ||
| 60 | aVals.append(""); | ||
| 61 | break; | ||
| 62 | } | ||
| 63 | aVals.append( sDecode( i ) ); | ||
| 64 | } | ||
| 65 | else | ||
| 66 | { | ||
| 67 | // Blanks and stuff? | ||
| 68 | sio << "Out of bound: '" << *i << "'" << sio.nl; | ||
| 69 | i++; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | return aVals; | ||
| 74 | } | ||
| 75 | |||
| 76 | Bu::String Bu::CsvReader::decodeExcel( Bu::String::iterator &i ) | ||
| 77 | { | ||
| 78 | Bu::String sRet; | ||
| 79 | |||
| 80 | for(; i && (*i == ' ' || *i == '\t'); i++ ) { } | ||
| 81 | |||
| 82 | if( !i ) | ||
| 83 | return sRet; | ||
| 84 | |||
| 85 | if( *i == '\"' ) | ||
| 86 | { | ||
| 87 | for( i++ ; i; i++ ) | ||
| 88 | { | ||
| 89 | if( *i == '\"' ) | ||
| 90 | { | ||
| 91 | i++; | ||
| 92 | if( !i ) | ||
| 93 | { | ||
| 94 | return sRet; | ||
| 95 | } | ||
| 96 | else if( *i == '\"' ) | ||
| 97 | { | ||
| 98 | sRet += *i; | ||
| 99 | } | ||
| 100 | else | ||
| 101 | { | ||
| 102 | return sRet; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | else | ||
| 106 | { | ||
| 107 | sRet += *i; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | else | ||
| 112 | { | ||
| 113 | for( ; i; i++ ) | ||
| 114 | { | ||
| 115 | if( *i == ',' ) | ||
| 116 | { | ||
| 117 | return sRet; | ||
| 118 | } | ||
| 119 | sRet += *i; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | return sRet; | ||
| 124 | } | ||
| 125 | |||
| 126 | Bu::String Bu::CsvReader::decodeC( Bu::String::iterator & ) | ||
| 127 | { | ||
| 128 | return ""; | ||
| 129 | } | ||
| 130 | |||
| diff --git a/src/stable/csvreader.h b/src/stable/csvreader.h new file mode 100644 index 0000000..2e9e7b0 --- /dev/null +++ b/src/stable/csvreader.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_CSV_READER_H | ||
| 9 | #define BU_CSV_READER_H | ||
| 10 | |||
| 11 | #include "bu/string.h" | ||
| 12 | #include "bu/array.h" | ||
| 13 | #include "bu/signals.h" | ||
| 14 | |||
| 15 | namespace Bu | ||
| 16 | { | ||
| 17 | class Stream; | ||
| 18 | typedef Bu::Array<Bu::String> StrArray; | ||
| 19 | |||
| 20 | class CsvReader | ||
| 21 | { | ||
| 22 | public: | ||
| 23 | typedef Bu::Signal1<Bu::String, Bu::String::iterator &> DecodeSignal; | ||
| 24 | enum Style | ||
| 25 | { | ||
| 26 | styleExcel, ///< Excel style quotes around things that need em | ||
| 27 | styleC ///< Escape things that need it C-style | ||
| 28 | }; | ||
| 29 | |||
| 30 | CsvReader( Stream &sIn, Style eStyle=styleExcel ); | ||
| 31 | CsvReader( Stream &sIn, DecodeSignal sDecode ); | ||
| 32 | virtual ~CsvReader(); | ||
| 33 | |||
| 34 | StrArray readLine(); | ||
| 35 | |||
| 36 | private: | ||
| 37 | Stream &sIn; | ||
| 38 | DecodeSignal sDecode; | ||
| 39 | |||
| 40 | static Bu::String decodeExcel( Bu::String::iterator &i ); | ||
| 41 | static Bu::String decodeC( Bu::String::iterator &i ); | ||
| 42 | }; | ||
| 43 | }; | ||
| 44 | |||
| 45 | #endif | ||
| diff --git a/src/stable/csvwriter.cpp b/src/stable/csvwriter.cpp new file mode 100644 index 0000000..d8910aa --- /dev/null +++ b/src/stable/csvwriter.cpp | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/csvwriter.h" | ||
| 9 | #include "bu/stream.h" | ||
| 10 | |||
| 11 | Bu::CsvWriter::CsvWriter( Bu::Stream &sOut, Bu::CsvWriter::Style eStyle ) : | ||
| 12 | sOut( sOut ) | ||
| 13 | { | ||
| 14 | switch( eStyle ) | ||
| 15 | { | ||
| 16 | case styleExcel: | ||
| 17 | sEncode = Bu::slot( &encodeExcel ); | ||
| 18 | break; | ||
| 19 | |||
| 20 | case styleC: | ||
| 21 | sEncode = Bu::slot( &encodeExcel ); | ||
| 22 | break; | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | Bu::CsvWriter::CsvWriter( Bu::Stream &sOut, | ||
| 27 | Bu::CsvWriter::EncodeSignal sEncode ) : | ||
| 28 | sOut( sOut ), | ||
| 29 | sEncode( sEncode ) | ||
| 30 | { | ||
| 31 | } | ||
| 32 | |||
| 33 | Bu::CsvWriter::~CsvWriter() | ||
| 34 | { | ||
| 35 | } | ||
| 36 | |||
| 37 | void Bu::CsvWriter::writeLine( const StrArray &aStrs ) | ||
| 38 | { | ||
| 39 | Bu::String sBuf; | ||
| 40 | for( StrArray::const_iterator i = aStrs.begin(); i; i++ ) | ||
| 41 | { | ||
| 42 | if( i != aStrs.begin() ) | ||
| 43 | sBuf += ","; | ||
| 44 | sBuf += sEncode( *i ); | ||
| 45 | } | ||
| 46 | sBuf += "\n"; | ||
| 47 | |||
| 48 | sOut.write( sBuf ); | ||
| 49 | } | ||
| 50 | |||
| 51 | Bu::String Bu::CsvWriter::encodeExcel( const Bu::String &sIn ) | ||
| 52 | { | ||
| 53 | if( sIn.find('\"') || sIn.find(',') ) | ||
| 54 | { | ||
| 55 | Bu::String sOut = "\""; | ||
| 56 | for( Bu::String::const_iterator i = sIn.begin(); i; i++ ) | ||
| 57 | { | ||
| 58 | if( *i == '\"' ) | ||
| 59 | sOut += "\"\""; | ||
| 60 | else | ||
| 61 | sOut += *i; | ||
| 62 | } | ||
| 63 | sOut += '\"'; | ||
| 64 | return sOut; | ||
| 65 | } | ||
| 66 | return sIn; | ||
| 67 | } | ||
| 68 | |||
| 69 | Bu::String Bu::CsvWriter::encodeC( const Bu::String &sIn ) | ||
| 70 | { | ||
| 71 | Bu::String sOut = ""; | ||
| 72 | for( Bu::String::const_iterator i = sIn.begin(); i; i++ ) | ||
| 73 | { | ||
| 74 | if( *i == ',' ) | ||
| 75 | sOut += "\\,"; | ||
| 76 | else | ||
| 77 | sOut += *i; | ||
| 78 | } | ||
| 79 | return sOut; | ||
| 80 | } | ||
| 81 | |||
| diff --git a/src/stable/csvwriter.h b/src/stable/csvwriter.h new file mode 100644 index 0000000..4291ed5 --- /dev/null +++ b/src/stable/csvwriter.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_CSV_WRITER_H | ||
| 9 | #define BU_CSV_WRITER_H | ||
| 10 | |||
| 11 | #include "bu/string.h" | ||
| 12 | #include "bu/array.h" | ||
| 13 | #include "bu/signals.h" | ||
| 14 | |||
| 15 | namespace Bu | ||
| 16 | { | ||
| 17 | class Stream; | ||
| 18 | typedef Bu::Array<Bu::String> StrArray; | ||
| 19 | |||
| 20 | class CsvWriter | ||
| 21 | { | ||
| 22 | public: | ||
| 23 | typedef Bu::Signal1<Bu::String, const Bu::String &> EncodeSignal; | ||
| 24 | enum Style | ||
| 25 | { | ||
| 26 | styleExcel, ///< Excel style quotes around things that need em | ||
| 27 | styleC ///< Escape things that need it C-style | ||
| 28 | }; | ||
| 29 | |||
| 30 | CsvWriter( Stream &sOut, Style eStyle=styleExcel ); | ||
| 31 | CsvWriter( Stream &sOut, EncodeSignal sEncode ); | ||
| 32 | virtual ~CsvWriter(); | ||
| 33 | |||
| 34 | void writeLine( const StrArray &aStrs ); | ||
| 35 | |||
| 36 | private: | ||
| 37 | Stream &sOut; | ||
| 38 | EncodeSignal sEncode; | ||
| 39 | |||
| 40 | static Bu::String encodeExcel( const Bu::String &sIn ); | ||
| 41 | static Bu::String encodeC( const Bu::String &sIn ); | ||
| 42 | }; | ||
| 43 | }; | ||
| 44 | |||
| 45 | #endif | ||
| diff --git a/src/stable/deflate.cpp b/src/stable/deflate.cpp new file mode 100644 index 0000000..704d172 --- /dev/null +++ b/src/stable/deflate.cpp | |||
| @@ -0,0 +1,253 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/deflate.h" | ||
| 9 | #include "bu/trace.h" | ||
| 10 | |||
| 11 | #include <zlib.h> | ||
| 12 | |||
| 13 | #define pState ((z_stream *)prState) | ||
| 14 | |||
| 15 | using namespace Bu; | ||
| 16 | |||
| 17 | Bu::Deflate::Deflate( Bu::Stream &rNext, int nCompression, Format eFmt ) : | ||
| 18 | Bu::Filter( rNext ), | ||
| 19 | prState( NULL ), | ||
| 20 | nCompression( nCompression ), | ||
| 21 | sTotalOut( 0 ), | ||
| 22 | eFmt( eFmt ), | ||
| 23 | bEos( false ) | ||
| 24 | { | ||
| 25 | TRACE( nCompression ); | ||
| 26 | start(); | ||
| 27 | } | ||
| 28 | |||
| 29 | Bu::Deflate::~Deflate() | ||
| 30 | { | ||
| 31 | TRACE(); | ||
| 32 | stop(); | ||
| 33 | } | ||
| 34 | |||
| 35 | void Bu::Deflate::start() | ||
| 36 | { | ||
| 37 | TRACE(); | ||
| 38 | prState = new z_stream; | ||
| 39 | pState->zalloc = NULL; | ||
| 40 | pState->zfree = NULL; | ||
| 41 | pState->opaque = NULL; | ||
| 42 | pState->state = NULL; | ||
| 43 | |||
| 44 | nBufSize = 64*1024; | ||
| 45 | pBuf = new char[nBufSize]; | ||
| 46 | } | ||
| 47 | |||
| 48 | Bu::size Bu::Deflate::stop() | ||
| 49 | { | ||
| 50 | TRACE(); | ||
| 51 | if( pState && pState->state ) | ||
| 52 | { | ||
| 53 | if( bReading ) | ||
| 54 | { | ||
| 55 | inflateEnd( pState ); | ||
| 56 | delete[] pBuf; | ||
| 57 | pBuf = NULL; | ||
| 58 | delete pState; | ||
| 59 | prState = NULL; | ||
| 60 | return 0; | ||
| 61 | } | ||
| 62 | else | ||
| 63 | { | ||
| 64 | for(;;) | ||
| 65 | { | ||
| 66 | pState->next_in = NULL; | ||
| 67 | pState->avail_in = 0; | ||
| 68 | pState->avail_out = nBufSize; | ||
| 69 | pState->next_out = (Bytef *)pBuf; | ||
| 70 | int res = deflate( pState, Z_FINISH ); | ||
| 71 | if( pState->avail_out < nBufSize ) | ||
| 72 | { | ||
| 73 | sTotalOut += rNext.write( pBuf, nBufSize-pState->avail_out ); | ||
| 74 | } | ||
| 75 | if( res == Z_STREAM_END ) | ||
| 76 | break; | ||
| 77 | } | ||
| 78 | deflateEnd( pState ); | ||
| 79 | delete[] pBuf; | ||
| 80 | pBuf = NULL; | ||
| 81 | delete pState; | ||
| 82 | prState = NULL; | ||
| 83 | return sTotalOut; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | void Bu::Deflate::zError( int code ) | ||
| 90 | { | ||
| 91 | TRACE( code ); | ||
| 92 | switch( code ) | ||
| 93 | { | ||
| 94 | case Z_OK: | ||
| 95 | case Z_STREAM_END: | ||
| 96 | case Z_NEED_DICT: | ||
| 97 | return; | ||
| 98 | |||
| 99 | case Z_ERRNO: | ||
| 100 | throw ExceptionBase("Deflate: Errno - %s", pState->msg ); | ||
| 101 | |||
| 102 | case Z_STREAM_ERROR: | ||
| 103 | throw ExceptionBase("Deflate: Stream Error - %s", pState->msg ); | ||
| 104 | |||
| 105 | case Z_DATA_ERROR: | ||
| 106 | throw ExceptionBase("Deflate: Data Error - %s", pState->msg ); | ||
| 107 | |||
| 108 | case Z_MEM_ERROR: | ||
| 109 | throw ExceptionBase("Deflate: Mem Error - %s", pState->msg ); | ||
| 110 | |||
| 111 | case Z_BUF_ERROR: | ||
| 112 | throw ExceptionBase("Deflate: Buf Error - %s", pState->msg ); | ||
| 113 | |||
| 114 | case Z_VERSION_ERROR: | ||
| 115 | throw ExceptionBase("Deflate: Version Error - %s", pState->msg ); | ||
| 116 | |||
| 117 | default: | ||
| 118 | throw ExceptionBase("Deflate: Unknown error encountered - %s.", pState->msg ); | ||
| 119 | |||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | Bu::size Bu::Deflate::read( void *pData, Bu::size nBytes ) | ||
| 124 | { | ||
| 125 | TRACE( pData, nBytes ); | ||
| 126 | if( nBytes <= 0 ) | ||
| 127 | return 0; | ||
| 128 | if( !pState->state ) | ||
| 129 | { | ||
| 130 | bReading = true; | ||
| 131 | if( eFmt&AutoDetect ) | ||
| 132 | inflateInit2( pState, 32+15 ); // Auto-detect, large window | ||
| 133 | else if( eFmt == Raw ) | ||
| 134 | inflateInit2( pState, -15 ); // Raw | ||
| 135 | else if( eFmt == Zlib ) | ||
| 136 | inflateInit2( pState, 15 ); // Zlib | ||
| 137 | else if( eFmt == Gzip ) | ||
| 138 | inflateInit2( pState, 16+15 ); // GZip | ||
| 139 | else | ||
| 140 | throw Bu::ExceptionBase("Format mode for deflate read."); | ||
| 141 | pState->next_in = (Bytef *)pBuf; | ||
| 142 | pState->avail_in = 0; | ||
| 143 | } | ||
| 144 | if( bReading == false ) | ||
| 145 | throw ExceptionBase("This deflate filter is in writing mode, you can't read."); | ||
| 146 | |||
| 147 | int nRead = 0; | ||
| 148 | int nReadTotal = pState->total_out; | ||
| 149 | pState->next_out = (Bytef *)pData; | ||
| 150 | pState->avail_out = nBytes; | ||
| 151 | for(;;) | ||
| 152 | { | ||
| 153 | int ret = inflate( pState, Z_NO_FLUSH ); | ||
| 154 | nReadTotal += nRead-pState->avail_out; | ||
| 155 | |||
| 156 | if( ret == Z_STREAM_END ) | ||
| 157 | { | ||
| 158 | bEos = true; | ||
| 159 | if( pState->avail_in > 0 ) | ||
| 160 | { | ||
| 161 | if( rNext.isSeekable() ) | ||
| 162 | { | ||
| 163 | rNext.seek( -pState->avail_in ); | ||
| 164 | } | ||
| 165 | } | ||
| 166 | return nBytes-pState->avail_out; | ||
| 167 | } | ||
| 168 | if( ret != Z_BUF_ERROR ) | ||
| 169 | zError( ret ); | ||
| 170 | |||
| 171 | if( pState->avail_out ) | ||
| 172 | { | ||
| 173 | if( pState->avail_in == 0 ) | ||
| 174 | { | ||
| 175 | nRead = rNext.read( pBuf, nBufSize ); | ||
| 176 | if( nRead == 0 && rNext.isEos() ) | ||
| 177 | { | ||
| 178 | throw Bu::ExceptionBase("Premature end of underlying " | ||
| 179 | "stream found reading deflate stream."); | ||
| 180 | } | ||
| 181 | pState->next_in = (Bytef *)pBuf; | ||
| 182 | pState->avail_in = nRead; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | else | ||
| 186 | { | ||
| 187 | return nBytes-pState->avail_out; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | return 0; | ||
| 191 | } | ||
| 192 | |||
| 193 | Bu::size Bu::Deflate::write( const void *pData, Bu::size nBytes ) | ||
| 194 | { | ||
| 195 | TRACE( pData, nBytes ); | ||
| 196 | if( nBytes <= 0 ) | ||
| 197 | return 0; | ||
| 198 | if( !pState->state ) | ||
| 199 | { | ||
| 200 | bReading = false; | ||
| 201 | int iFmt = eFmt&Gzip; | ||
| 202 | if( iFmt == Raw ) | ||
| 203 | deflateInit2( pState, nCompression, Z_DEFLATED, -15, 9, | ||
| 204 | Z_DEFAULT_STRATEGY ); | ||
| 205 | else if( iFmt == Zlib ) | ||
| 206 | deflateInit2( pState, nCompression, Z_DEFLATED, 15, 9, | ||
| 207 | Z_DEFAULT_STRATEGY ); | ||
| 208 | else if( iFmt == Gzip ) | ||
| 209 | deflateInit2( pState, nCompression, Z_DEFLATED, 16+15, 9, | ||
| 210 | Z_DEFAULT_STRATEGY ); | ||
| 211 | else | ||
| 212 | throw Bu::ExceptionBase("Invalid format for deflate."); | ||
| 213 | } | ||
| 214 | if( bReading == true ) | ||
| 215 | throw ExceptionBase("This deflate filter is in reading mode, you can't write."); | ||
| 216 | |||
| 217 | pState->next_in = (Bytef *)pData; | ||
| 218 | pState->avail_in = nBytes; | ||
| 219 | for(;;) | ||
| 220 | { | ||
| 221 | pState->avail_out = nBufSize; | ||
| 222 | pState->next_out = (Bytef *)pBuf; | ||
| 223 | |||
| 224 | zError( deflate( pState, Z_NO_FLUSH ) ); | ||
| 225 | |||
| 226 | if( pState->avail_out < nBufSize ) | ||
| 227 | { | ||
| 228 | sTotalOut += rNext.write( pBuf, nBufSize-pState->avail_out ); | ||
| 229 | } | ||
| 230 | if( pState->avail_in == 0 ) | ||
| 231 | break; | ||
| 232 | } | ||
| 233 | |||
| 234 | return nBytes; | ||
| 235 | } | ||
| 236 | |||
| 237 | bool Bu::Deflate::isOpen() | ||
| 238 | { | ||
| 239 | TRACE(); | ||
| 240 | return (pState != NULL && pState->state != NULL); | ||
| 241 | } | ||
| 242 | |||
| 243 | bool Bu::Deflate::isEos() | ||
| 244 | { | ||
| 245 | TRACE(); | ||
| 246 | return bEos; | ||
| 247 | } | ||
| 248 | |||
| 249 | Bu::size Bu::Deflate::getCompressedSize() | ||
| 250 | { | ||
| 251 | return sTotalOut; | ||
| 252 | } | ||
| 253 | |||
| diff --git a/src/stable/deflate.h b/src/stable/deflate.h new file mode 100644 index 0000000..f835cfc --- /dev/null +++ b/src/stable/deflate.h | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_DEFLATE_H | ||
| 9 | #define BU_DEFLATE_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | |||
| 13 | #include "bu/filter.h" | ||
| 14 | |||
| 15 | namespace Bu | ||
| 16 | { | ||
| 17 | /** | ||
| 18 | * Provides Deflate (LZ77) support via zlib. This provides zlib, raw, and | ||
| 19 | * gzip stream types. By default it will autodetect the input type and | ||
| 20 | * encode into a raw deflate stream. | ||
| 21 | * | ||
| 22 | *@ingroup Streams | ||
| 23 | *@ingroup Compression | ||
| 24 | */ | ||
| 25 | class Deflate : public Bu::Filter | ||
| 26 | { | ||
| 27 | public: | ||
| 28 | enum Format | ||
| 29 | { | ||
| 30 | Raw = 0x01, | ||
| 31 | Zlib = 0x02, | ||
| 32 | Gzip = 0x03, | ||
| 33 | AutoDetect = 0x04, | ||
| 34 | |||
| 35 | AutoRaw = 0x04|0x01, | ||
| 36 | AutoZlib = 0x04|0x02, | ||
| 37 | AutoGzip = 0x04|0x03 | ||
| 38 | }; | ||
| 39 | |||
| 40 | Deflate( Bu::Stream &rNext, int nCompression=-1, Format eFmt=AutoZlib ); | ||
| 41 | virtual ~Deflate(); | ||
| 42 | |||
| 43 | virtual void start(); | ||
| 44 | virtual Bu::size stop(); | ||
| 45 | virtual Bu::size read( void *pBuf, Bu::size nBytes ); | ||
| 46 | virtual Bu::size write( const void *pBuf, Bu::size nBytes ); | ||
| 47 | |||
| 48 | virtual bool isOpen(); | ||
| 49 | virtual bool isEos(); | ||
| 50 | |||
| 51 | Bu::size getCompressedSize(); | ||
| 52 | |||
| 53 | private: | ||
| 54 | void zError( int code ); | ||
| 55 | void *prState; | ||
| 56 | bool bReading; | ||
| 57 | int nCompression; | ||
| 58 | char *pBuf; | ||
| 59 | uint32_t nBufSize; | ||
| 60 | Bu::size sTotalOut; | ||
| 61 | Format eFmt; | ||
| 62 | bool bEos; | ||
| 63 | }; | ||
| 64 | } | ||
| 65 | |||
| 66 | #endif | ||
| diff --git a/src/stable/exceptionbase.cpp b/src/stable/exceptionbase.cpp new file mode 100644 index 0000000..13a98db --- /dev/null +++ b/src/stable/exceptionbase.cpp | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/exceptionbase.h" | ||
| 9 | #include <stdarg.h> | ||
| 10 | #include <string.h> | ||
| 11 | #include <stdio.h> | ||
| 12 | |||
| 13 | Bu::ExceptionBase::ExceptionBase( const char *lpFormat, ... ) throw() : | ||
| 14 | nErrorCode( 0 ), | ||
| 15 | sWhat( NULL ) | ||
| 16 | { | ||
| 17 | va_list ap; | ||
| 18 | |||
| 19 | va_start(ap, lpFormat); | ||
| 20 | setWhat( lpFormat, ap ); | ||
| 21 | va_end(ap); | ||
| 22 | } | ||
| 23 | |||
| 24 | Bu::ExceptionBase::ExceptionBase( int nCode, const char *lpFormat, ... ) throw() : | ||
| 25 | nErrorCode( nCode ), | ||
| 26 | sWhat( NULL ) | ||
| 27 | { | ||
| 28 | va_list ap; | ||
| 29 | |||
| 30 | va_start(ap, lpFormat); | ||
| 31 | setWhat( lpFormat, ap ); | ||
| 32 | va_end(ap); | ||
| 33 | } | ||
| 34 | |||
| 35 | Bu::ExceptionBase::ExceptionBase( int nCode ) throw() : | ||
| 36 | nErrorCode( nCode ), | ||
| 37 | sWhat( NULL ) | ||
| 38 | { | ||
| 39 | } | ||
| 40 | |||
| 41 | Bu::ExceptionBase::ExceptionBase( const ExceptionBase &e ) throw () : | ||
| 42 | std::exception( e ), | ||
| 43 | nErrorCode( e.nErrorCode ), | ||
| 44 | sWhat( NULL ) | ||
| 45 | { | ||
| 46 | setWhat( e.sWhat ); | ||
| 47 | } | ||
| 48 | |||
| 49 | Bu::ExceptionBase::~ExceptionBase() throw() | ||
| 50 | { | ||
| 51 | delete[] sWhat; | ||
| 52 | sWhat = NULL; | ||
| 53 | } | ||
| 54 | |||
| 55 | void Bu::ExceptionBase::setWhat( const char *lpFormat, va_list &vargs ) | ||
| 56 | { | ||
| 57 | if( sWhat ) delete[] sWhat; | ||
| 58 | int nSize; | ||
| 59 | |||
| 60 | va_list vargs2; | ||
| 61 | va_copy( vargs2, vargs ); | ||
| 62 | nSize = vsnprintf( NULL, 0, lpFormat, vargs2 ); | ||
| 63 | va_end( vargs2 ); | ||
| 64 | sWhat = new char[nSize+1]; | ||
| 65 | vsnprintf( sWhat, nSize+1, lpFormat, vargs ); | ||
| 66 | } | ||
| 67 | |||
| 68 | void Bu::ExceptionBase::setWhat( const char *lpText ) | ||
| 69 | { | ||
| 70 | if( sWhat ) delete[] sWhat; | ||
| 71 | int nSize; | ||
| 72 | |||
| 73 | nSize = strlen( lpText ); | ||
| 74 | sWhat = new char[nSize+1]; | ||
| 75 | strcpy( sWhat, lpText ); | ||
| 76 | } | ||
| 77 | |||
| 78 | const char *Bu::ExceptionBase::what() const throw() | ||
| 79 | { | ||
| 80 | return sWhat; | ||
| 81 | } | ||
| 82 | |||
| 83 | int Bu::ExceptionBase::getErrorCode() | ||
| 84 | { | ||
| 85 | return nErrorCode; | ||
| 86 | } | ||
| 87 | |||
| 88 | Bu::UnsupportedException::UnsupportedException() throw() : | ||
| 89 | ExceptionBase( 0 ) | ||
| 90 | { | ||
| 91 | setWhat("An unsupperted operation was attempted."); | ||
| 92 | } | ||
| 93 | |||
| diff --git a/src/stable/exceptionbase.h b/src/stable/exceptionbase.h new file mode 100644 index 0000000..b6ad9ca --- /dev/null +++ b/src/stable/exceptionbase.h | |||
| @@ -0,0 +1,190 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_EXCEPTION_BASE_H | ||
| 9 | #define BU_EXCEPTION_BASE_H | ||
| 10 | |||
| 11 | #include <exception> | ||
| 12 | #include <stdarg.h> | ||
| 13 | |||
| 14 | // This shouldn't normally be defined here, I don't think it's all that portable | ||
| 15 | // and it also changes the class interface, we should find out how much of | ||
| 16 | // an issue that is, we could just put in an empty getBacktrace() function for | ||
| 17 | // when you don't have support for it... | ||
| 18 | |||
| 19 | namespace Bu | ||
| 20 | { | ||
| 21 | /** | ||
| 22 | * A generalized Exception base class. This is nice for making general and | ||
| 23 | * flexible child classes that can create new error code classes. | ||
| 24 | * | ||
| 25 | * In order to create your own exception class use these two lines. | ||
| 26 | * | ||
| 27 | * in your header: subExceptionDecl( NewClassName ); | ||
| 28 | * | ||
| 29 | * in your source: subExcpetienDef( NewClassName ); | ||
| 30 | */ | ||
| 31 | class ExceptionBase : public std::exception | ||
| 32 | { | ||
| 33 | public: | ||
| 34 | /** | ||
| 35 | * Construct an exception with an error code of zero, but with a | ||
| 36 | * description. The use of this is not reccomended most of the time, | ||
| 37 | * it's generally best to include an error code with the exception so | ||
| 38 | * your program can handle the exception in a better way. | ||
| 39 | * @param sFormat The format of the text. See printf for more info. | ||
| 40 | */ | ||
| 41 | ExceptionBase( const char *sFormat, ... ) throw(); | ||
| 42 | |||
| 43 | /** | ||
| 44 | * | ||
| 45 | * @param nCode | ||
| 46 | * @param sFormat | ||
| 47 | */ | ||
| 48 | ExceptionBase( int nCode, const char *sFormat, ... ) throw(); | ||
| 49 | |||
| 50 | /** | ||
| 51 | * | ||
| 52 | * @param nCode | ||
| 53 | * @return | ||
| 54 | */ | ||
| 55 | ExceptionBase( int nCode=0 ) throw(); | ||
| 56 | |||
| 57 | ExceptionBase( const ExceptionBase &e ) throw (); | ||
| 58 | |||
| 59 | /** | ||
| 60 | * | ||
| 61 | * @return | ||
| 62 | */ | ||
| 63 | virtual ~ExceptionBase() throw(); | ||
| 64 | |||
| 65 | /** | ||
| 66 | * | ||
| 67 | * @return | ||
| 68 | */ | ||
| 69 | virtual const char *what() const throw(); | ||
| 70 | |||
| 71 | /** | ||
| 72 | * | ||
| 73 | * @return | ||
| 74 | */ | ||
| 75 | int getErrorCode(); | ||
| 76 | |||
| 77 | /** | ||
| 78 | * | ||
| 79 | * @param lpFormat | ||
| 80 | * @param vargs | ||
| 81 | */ | ||
| 82 | void setWhat( const char *lpFormat, va_list &vargs ); | ||
| 83 | |||
| 84 | /** | ||
| 85 | * | ||
| 86 | * @param lpText | ||
| 87 | */ | ||
| 88 | void setWhat( const char *lpText ); | ||
| 89 | |||
| 90 | private: | ||
| 91 | int nErrorCode; /**< The code for the error that occured. */ | ||
| 92 | char *sWhat; /**< The text string telling people what went wrong. */ | ||
| 93 | }; | ||
| 94 | } | ||
| 95 | |||
| 96 | #define subExceptionDecl( name ) \ | ||
| 97 | class name : public Bu::ExceptionBase \ | ||
| 98 | { \ | ||
| 99 | public: \ | ||
| 100 | name( const char *sFormat, ... ) throw (); \ | ||
| 101 | name( int nCode, const char *sFormat, ... ) throw(); \ | ||
| 102 | name( int nCode=0 ) throw (); \ | ||
| 103 | name( const name &e ) throw (); \ | ||
| 104 | }; | ||
| 105 | |||
| 106 | #define subExceptionDeclChild( name, parent ) \ | ||
| 107 | class name : public parent \ | ||
| 108 | { \ | ||
| 109 | public: \ | ||
| 110 | name( const char *sFormat, ... ) throw (); \ | ||
| 111 | name( int nCode, const char *sFormat, ... ) throw(); \ | ||
| 112 | name( int nCode=0 ) throw (); \ | ||
| 113 | name( const name &e ) throw (); \ | ||
| 114 | }; | ||
| 115 | |||
| 116 | #define subExceptionDeclBegin( name ) \ | ||
| 117 | class name : public Bu::ExceptionBase \ | ||
| 118 | { \ | ||
| 119 | public: \ | ||
| 120 | name( const char *sFormat, ... ) throw (); \ | ||
| 121 | name( int nCode, const char *sFormat, ... ) throw(); \ | ||
| 122 | name( int nCode=0 ) throw (); \ | ||
| 123 | name( const name &e ) throw (); | ||
| 124 | |||
| 125 | #define subExceptionDeclEnd() \ | ||
| 126 | }; | ||
| 127 | |||
| 128 | #define subExceptionDef( name ) \ | ||
| 129 | name::name( const char *lpFormat, ... ) throw() : \ | ||
| 130 | ExceptionBase( 0 ) \ | ||
| 131 | { \ | ||
| 132 | va_list ap; \ | ||
| 133 | va_start( ap, lpFormat ); \ | ||
| 134 | setWhat( lpFormat, ap ); \ | ||
| 135 | va_end( ap ); \ | ||
| 136 | } \ | ||
| 137 | name::name( int nCode, const char *lpFormat, ... ) throw() : \ | ||
| 138 | ExceptionBase( nCode ) \ | ||
| 139 | { \ | ||
| 140 | va_list ap; \ | ||
| 141 | va_start( ap, lpFormat ); \ | ||
| 142 | setWhat( lpFormat, ap ); \ | ||
| 143 | va_end( ap ); \ | ||
| 144 | } \ | ||
| 145 | name::name( int nCode ) throw() : \ | ||
| 146 | ExceptionBase( nCode ) \ | ||
| 147 | { \ | ||
| 148 | } \ | ||
| 149 | name::name( const name &e ) throw() : \ | ||
| 150 | ExceptionBase( e ) \ | ||
| 151 | { \ | ||
| 152 | } | ||
| 153 | |||
| 154 | #define subExceptionDefChild( name, parent ) \ | ||
| 155 | name::name( const char *lpFormat, ... ) throw() : \ | ||
| 156 | parent( 0 ) \ | ||
| 157 | { \ | ||
| 158 | va_list ap; \ | ||
| 159 | va_start( ap, lpFormat ); \ | ||
| 160 | setWhat( lpFormat, ap ); \ | ||
| 161 | va_end( ap ); \ | ||
| 162 | } \ | ||
| 163 | name::name( int nCode, const char *lpFormat, ... ) throw() : \ | ||
| 164 | parent( nCode ) \ | ||
| 165 | { \ | ||
| 166 | va_list ap; \ | ||
| 167 | va_start( ap, lpFormat ); \ | ||
| 168 | setWhat( lpFormat, ap ); \ | ||
| 169 | va_end( ap ); \ | ||
| 170 | } \ | ||
| 171 | name::name( int nCode ) throw() : \ | ||
| 172 | parent( nCode ) \ | ||
| 173 | { \ | ||
| 174 | } \ | ||
| 175 | name::name( const name &e ) throw() : \ | ||
| 176 | ExceptionBase( e ) \ | ||
| 177 | { \ | ||
| 178 | } | ||
| 179 | |||
| 180 | namespace Bu | ||
| 181 | { | ||
| 182 | // Exceptions that are so general they could be used anywhere go here. | ||
| 183 | class UnsupportedException : public Bu::ExceptionBase | ||
| 184 | { | ||
| 185 | public: | ||
| 186 | UnsupportedException() throw (); | ||
| 187 | }; | ||
| 188 | } | ||
| 189 | |||
| 190 | #endif | ||
| diff --git a/src/stable/extratypes.h b/src/stable/extratypes.h new file mode 100644 index 0000000..656cd6d --- /dev/null +++ b/src/stable/extratypes.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef EXTRA_TYPES_H | ||
| 9 | #define EXTRA_TYPES_H | ||
| 10 | |||
| 11 | #include "bu/config.h" | ||
| 12 | |||
| 13 | #include <stdint.h> | ||
| 14 | |||
| 15 | #ifndef NULL | ||
| 16 | #define NULL 0 | ||
| 17 | #endif | ||
| 18 | |||
| 19 | namespace Bu | ||
| 20 | { | ||
| 21 | #ifdef USE_64BIT_IO | ||
| 22 | typedef int64_t size; | ||
| 23 | typedef uint64_t usize; | ||
| 24 | #else | ||
| 25 | typedef int32_t size; | ||
| 26 | typedef uint32_t usize; | ||
| 27 | #endif | ||
| 28 | }; | ||
| 29 | |||
| 30 | #endif | ||
| diff --git a/src/stable/file.cpp b/src/stable/file.cpp new file mode 100644 index 0000000..8c8f540 --- /dev/null +++ b/src/stable/file.cpp | |||
| @@ -0,0 +1,305 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/file.h" | ||
| 9 | #include <errno.h> | ||
| 10 | #include <sys/types.h> | ||
| 11 | #include <sys/stat.h> | ||
| 12 | #include <fcntl.h> | ||
| 13 | #include <unistd.h> | ||
| 14 | #include <time.h> | ||
| 15 | |||
| 16 | #include "bu/config.h" | ||
| 17 | |||
| 18 | namespace Bu { subExceptionDef( FileException ) } | ||
| 19 | |||
| 20 | Bu::File::File( const Bu::String &sName, int iFlags ) : | ||
| 21 | fd( -1 ), | ||
| 22 | bEos( true ) | ||
| 23 | { | ||
| 24 | #ifdef USE_64BIT_IO | ||
| 25 | fd = ::open64( sName.getStr(), getPosixFlags( iFlags ), 0666 ); | ||
| 26 | #else | ||
| 27 | fd = ::open( sName.getStr(), getPosixFlags( iFlags ), 0666 ); | ||
| 28 | #endif | ||
| 29 | if( fd < 0 ) | ||
| 30 | { | ||
| 31 | throw Bu::FileException( errno, "%s: %s", | ||
| 32 | strerror(errno), sName.getStr() ); | ||
| 33 | } | ||
| 34 | bEos = false; | ||
| 35 | } | ||
| 36 | |||
| 37 | Bu::File::File( int fd ) : | ||
| 38 | fd( fd ) | ||
| 39 | { | ||
| 40 | bEos = false; | ||
| 41 | } | ||
| 42 | |||
| 43 | Bu::File::~File() | ||
| 44 | { | ||
| 45 | close(); | ||
| 46 | } | ||
| 47 | |||
| 48 | void Bu::File::close() | ||
| 49 | { | ||
| 50 | if( fd >= 0 ) | ||
| 51 | { | ||
| 52 | if( ::close( fd ) ) | ||
| 53 | { | ||
| 54 | throw Bu::FileException( errno, "%s", | ||
| 55 | strerror(errno) ); | ||
| 56 | } | ||
| 57 | fd = -1; | ||
| 58 | bEos = true; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | Bu::size Bu::File::read( void *pBuf, Bu::size nBytes ) | ||
| 63 | { | ||
| 64 | if( fd < 0 ) | ||
| 65 | throw FileException("File not open."); | ||
| 66 | |||
| 67 | Bu::size iRead = ::read( fd, pBuf, nBytes ); | ||
| 68 | if( iRead == 0 ) | ||
| 69 | bEos = true; | ||
| 70 | else if( iRead == -1 && errno == EAGAIN ) | ||
| 71 | return 0; | ||
| 72 | else if( iRead < 0 ) | ||
| 73 | throw FileException( errno, "%s", strerror( errno ) ); | ||
| 74 | return iRead; | ||
| 75 | } | ||
| 76 | |||
| 77 | Bu::size Bu::File::write( const void *pBuf, Bu::size nBytes ) | ||
| 78 | { | ||
| 79 | if( fd < 0 ) | ||
| 80 | throw FileException("File not open."); | ||
| 81 | |||
| 82 | Bu::size iWrote = ::write( fd, pBuf, nBytes ); | ||
| 83 | if( iWrote < 0 ) | ||
| 84 | throw FileException( errno, "%s", strerror( errno ) ); | ||
| 85 | return iWrote; | ||
| 86 | } | ||
| 87 | |||
| 88 | Bu::size Bu::File::tell() | ||
| 89 | { | ||
| 90 | if( fd < 0 ) | ||
| 91 | throw FileException("File not open."); | ||
| 92 | |||
| 93 | return lseek( fd, 0, SEEK_CUR ); | ||
| 94 | } | ||
| 95 | |||
| 96 | void Bu::File::seek( Bu::size offset ) | ||
| 97 | { | ||
| 98 | if( fd < 0 ) | ||
| 99 | throw FileException("File not open."); | ||
| 100 | |||
| 101 | lseek( fd, offset, SEEK_CUR ); | ||
| 102 | bEos = false; | ||
| 103 | } | ||
| 104 | |||
| 105 | void Bu::File::setPos( Bu::size pos ) | ||
| 106 | { | ||
| 107 | if( fd < 0 ) | ||
| 108 | throw FileException("File not open."); | ||
| 109 | |||
| 110 | lseek( fd, pos, SEEK_SET ); | ||
| 111 | bEos = false; | ||
| 112 | } | ||
| 113 | |||
| 114 | void Bu::File::setPosEnd( Bu::size pos ) | ||
| 115 | { | ||
| 116 | if( fd < 0 ) | ||
| 117 | throw FileException("File not open."); | ||
| 118 | |||
| 119 | lseek( fd, pos, SEEK_END ); | ||
| 120 | bEos = false; | ||
| 121 | } | ||
| 122 | |||
| 123 | bool Bu::File::isEos() | ||
| 124 | { | ||
| 125 | return bEos; | ||
| 126 | } | ||
| 127 | |||
| 128 | bool Bu::File::canRead() | ||
| 129 | { | ||
| 130 | #ifdef WIN32 | ||
| 131 | return true; | ||
| 132 | #else | ||
| 133 | int iMode = fcntl( fd, F_GETFL, 0 )&O_ACCMODE; | ||
| 134 | if( iMode == O_RDONLY || iMode == O_RDWR ) | ||
| 135 | return true; | ||
| 136 | return false; | ||
| 137 | #endif | ||
| 138 | } | ||
| 139 | |||
| 140 | bool Bu::File::canWrite() | ||
| 141 | { | ||
| 142 | #ifdef WIN32 | ||
| 143 | return true; | ||
| 144 | #else | ||
| 145 | int iMode = fcntl( fd, F_GETFL, 0 )&O_ACCMODE; | ||
| 146 | if( iMode == O_WRONLY || iMode == O_RDWR ) | ||
| 147 | return true; | ||
| 148 | return false; | ||
| 149 | #endif | ||
| 150 | } | ||
| 151 | |||
| 152 | bool Bu::File::isReadable() | ||
| 153 | { | ||
| 154 | return true; | ||
| 155 | } | ||
| 156 | |||
| 157 | bool Bu::File::isWritable() | ||
| 158 | { | ||
| 159 | return true; | ||
| 160 | } | ||
| 161 | |||
| 162 | bool Bu::File::isSeekable() | ||
| 163 | { | ||
| 164 | return true; | ||
| 165 | } | ||
| 166 | |||
| 167 | bool Bu::File::isBlocking() | ||
| 168 | { | ||
| 169 | return true; | ||
| 170 | } | ||
| 171 | |||
| 172 | void Bu::File::setBlocking( bool bBlocking ) | ||
| 173 | { | ||
| 174 | #ifdef WIN32 | ||
| 175 | fprintf(stderr, "STUB: Bu::File::setBlocking\n"); | ||
| 176 | #else | ||
| 177 | if( bBlocking ) | ||
| 178 | fcntl( | ||
| 179 | fd, | ||
| 180 | F_SETFL, fcntl( fd, F_GETFL, 0 )&(~O_NONBLOCK) | ||
| 181 | ); | ||
| 182 | else | ||
| 183 | fcntl( | ||
| 184 | fd, | ||
| 185 | F_SETFL, fcntl( fd, F_GETFL, 0 )|O_NONBLOCK | ||
| 186 | ); | ||
| 187 | #endif | ||
| 188 | } | ||
| 189 | |||
| 190 | Bu::File Bu::File::tempFile( Bu::String &sName ) | ||
| 191 | { | ||
| 192 | uint32_t iX; | ||
| 193 | iX = time( NULL ) + getpid(); | ||
| 194 | int iXes; | ||
| 195 | for( iXes = sName.getSize()-1; iXes >= 0; iXes-- ) | ||
| 196 | { | ||
| 197 | if( sName[iXes] != 'X' ) | ||
| 198 | break; | ||
| 199 | } | ||
| 200 | iXes++; | ||
| 201 | if( iXes == sName.getSize() ) | ||
| 202 | throw Bu::ExceptionBase("Invalid temporary filename template."); | ||
| 203 | for( int iter = 0; iter < 100; iter++ ) | ||
| 204 | { | ||
| 205 | for( int j = iXes; j < sName.getSize(); j++ ) | ||
| 206 | { | ||
| 207 | iX = (1103515245 * iX + 12345); | ||
| 208 | sName[j] = ('A'+(iX%26)) | ((iX&0x1000)?(0x20):(0)); | ||
| 209 | } | ||
| 210 | |||
| 211 | try | ||
| 212 | { | ||
| 213 | return Bu::File( sName, Bu::File::Read|Bu::File::Write | ||
| 214 | |Bu::File::Create|Bu::File::Exclusive ); | ||
| 215 | } catch(...) { } | ||
| 216 | } | ||
| 217 | throw Bu::FileException("Failed to create unique temporary file after 100" | ||
| 218 | " iterations."); | ||
| 219 | } | ||
| 220 | |||
| 221 | void Bu::File::setSize( Bu::size iSize ) | ||
| 222 | { | ||
| 223 | #ifdef WIN32 | ||
| 224 | chsize( fd, iSize ); | ||
| 225 | #else | ||
| 226 | ftruncate( fd, iSize ); | ||
| 227 | #endif | ||
| 228 | } | ||
| 229 | |||
| 230 | Bu::size Bu::File::getSize() const | ||
| 231 | { | ||
| 232 | struct stat st; | ||
| 233 | fstat( fd, &st ); | ||
| 234 | return st.st_size; | ||
| 235 | } | ||
| 236 | |||
| 237 | Bu::size Bu::File::getBlockSize() const | ||
| 238 | { | ||
| 239 | #ifdef WIN32 | ||
| 240 | return 4096; | ||
| 241 | #else | ||
| 242 | struct stat st; | ||
| 243 | fstat( fd, &st ); | ||
| 244 | return st.st_blksize; | ||
| 245 | #endif | ||
| 246 | } | ||
| 247 | |||
| 248 | Bu::String Bu::File::getLocation() const | ||
| 249 | { | ||
| 250 | return "to be implemented"; | ||
| 251 | } | ||
| 252 | |||
| 253 | #ifndef WIN32 | ||
| 254 | void Bu::File::chmod( mode_t t ) | ||
| 255 | { | ||
| 256 | fchmod( fd, t ); | ||
| 257 | } | ||
| 258 | #endif | ||
| 259 | |||
| 260 | void Bu::File::flush() | ||
| 261 | { | ||
| 262 | // There is no flushing with direct I/O... | ||
| 263 | //fflush( fh ); | ||
| 264 | } | ||
| 265 | |||
| 266 | bool Bu::File::isOpen() | ||
| 267 | { | ||
| 268 | return (fd > -1); | ||
| 269 | } | ||
| 270 | |||
| 271 | int Bu::File::getPosixFlags( int iFlags ) | ||
| 272 | { | ||
| 273 | int iRet = 0; | ||
| 274 | switch( (iFlags&ReadWrite) ) | ||
| 275 | { | ||
| 276 | // According to posix, O_RDWR is not necesarilly O_RDONLY|O_WRONLY, so | ||
| 277 | // lets be proper and use the right value in the right place. | ||
| 278 | case Read: iRet = O_RDONLY; break; | ||
| 279 | case Write: iRet = O_WRONLY; break; | ||
| 280 | case ReadWrite: iRet = O_RDWR; break; | ||
| 281 | default: | ||
| 282 | throw FileException( | ||
| 283 | "You must specify Read, Write, or both when opening a file."); | ||
| 284 | } | ||
| 285 | |||
| 286 | if( (iFlags&Create) ) | ||
| 287 | iRet |= O_CREAT; | ||
| 288 | if( (iFlags&Append) ) | ||
| 289 | iRet |= O_APPEND; | ||
| 290 | if( (iFlags&Truncate) ) | ||
| 291 | iRet |= O_TRUNC; | ||
| 292 | #ifndef WIN32 | ||
| 293 | if( (iFlags&NonBlock) ) | ||
| 294 | iRet |= O_NONBLOCK; | ||
| 295 | #endif | ||
| 296 | if( (iFlags&Exclusive) == Exclusive ) | ||
| 297 | iRet |= O_EXCL; | ||
| 298 | |||
| 299 | #ifdef O_BINARY | ||
| 300 | iRet |= O_BINARY; | ||
| 301 | #endif | ||
| 302 | |||
| 303 | return iRet; | ||
| 304 | } | ||
| 305 | |||
| diff --git a/src/stable/file.h b/src/stable/file.h new file mode 100644 index 0000000..e3225fa --- /dev/null +++ b/src/stable/file.h | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_FILE_H | ||
| 9 | #define BU_FILE_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include <sys/types.h> | ||
| 13 | |||
| 14 | #include "bu/stream.h" | ||
| 15 | #include "bu/string.h" | ||
| 16 | #include "bu/exceptionbase.h" | ||
| 17 | |||
| 18 | namespace Bu | ||
| 19 | { | ||
| 20 | subExceptionDecl( FileException ); | ||
| 21 | |||
| 22 | /** | ||
| 23 | * A file stream. | ||
| 24 | *@ingroup Streams | ||
| 25 | */ | ||
| 26 | class File : public Bu::Stream | ||
| 27 | { | ||
| 28 | public: | ||
| 29 | File( const Bu::String &sName, int iFlags ); | ||
| 30 | File( int fd ); | ||
| 31 | virtual ~File(); | ||
| 32 | |||
| 33 | virtual void close(); | ||
| 34 | virtual Bu::size read( void *pBuf, Bu::size nBytes ); | ||
| 35 | virtual Bu::size write( const void *pBuf, Bu::size nBytes ); | ||
| 36 | using Stream::write; | ||
| 37 | |||
| 38 | virtual Bu::size tell(); | ||
| 39 | virtual void seek( Bu::size offset ); | ||
| 40 | virtual void setPos( Bu::size pos ); | ||
| 41 | virtual void setPosEnd( Bu::size pos ); | ||
| 42 | virtual bool isEos(); | ||
| 43 | virtual bool isOpen(); | ||
| 44 | |||
| 45 | virtual void flush(); | ||
| 46 | |||
| 47 | virtual bool canRead(); | ||
| 48 | virtual bool canWrite(); | ||
| 49 | |||
| 50 | virtual bool isReadable(); | ||
| 51 | virtual bool isWritable(); | ||
| 52 | virtual bool isSeekable(); | ||
| 53 | |||
| 54 | virtual bool isBlocking(); | ||
| 55 | virtual void setBlocking( bool bBlocking=true ); | ||
| 56 | |||
| 57 | enum { | ||
| 58 | // Flags | ||
| 59 | Read = 0x01, ///< Open file for reading | ||
| 60 | Write = 0x02, ///< Open file for writing | ||
| 61 | Create = 0x04, ///< Create file if it doesn't exist | ||
| 62 | Truncate = 0x08, ///< Truncate file if it does exist | ||
| 63 | Append = 0x10, ///< Always append on every write | ||
| 64 | NonBlock = 0x20, ///< Open file in non-blocking mode | ||
| 65 | Exclusive = 0x44, ///< Create file, if it exists then fail | ||
| 66 | |||
| 67 | // Helpful mixes | ||
| 68 | ReadWrite = 0x03, ///< Open for reading and writing | ||
| 69 | WriteNew = 0x0E ///< Create a file (or truncate) for writing. | ||
| 70 | /// Same as Write|Create|Truncate | ||
| 71 | }; | ||
| 72 | |||
| 73 | virtual void setSize( Bu::size iSize ); | ||
| 74 | |||
| 75 | virtual size getSize() const; | ||
| 76 | virtual size getBlockSize() const; | ||
| 77 | virtual Bu::String getLocation() const; | ||
| 78 | |||
| 79 | /** | ||
| 80 | * Create a temp file and return its handle. The file is opened | ||
| 81 | * Read/Write. | ||
| 82 | *@param sName (Bu::String) Give in the form: "/tmp/tmpfileXXXXXXXX" | ||
| 83 | * It will alter your (sName) setting the 'X's to random | ||
| 84 | * characters. | ||
| 85 | *@returns (Bu::File) A file object representing your temp file. | ||
| 86 | */ | ||
| 87 | static Bu::File tempFile( Bu::String &sName ); | ||
| 88 | |||
| 89 | #ifndef WIN32 | ||
| 90 | /** | ||
| 91 | * Change the file access permissions. | ||
| 92 | *@param t (mode_t) The new file access permissions. | ||
| 93 | */ | ||
| 94 | void chmod( mode_t t ); | ||
| 95 | #endif | ||
| 96 | |||
| 97 | private: | ||
| 98 | int getPosixFlags( int iFlags ); | ||
| 99 | |||
| 100 | private: | ||
| 101 | int fd; | ||
| 102 | bool bEos; | ||
| 103 | }; | ||
| 104 | } | ||
| 105 | |||
| 106 | #endif | ||
| diff --git a/src/stable/filter.cpp b/src/stable/filter.cpp new file mode 100644 index 0000000..3fe8f0e --- /dev/null +++ b/src/stable/filter.cpp | |||
| @@ -0,0 +1,113 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/filter.h" | ||
| 9 | |||
| 10 | Bu::Filter::Filter( Bu::Stream &rNext ) : | ||
| 11 | rNext( rNext ) | ||
| 12 | { | ||
| 13 | } | ||
| 14 | |||
| 15 | Bu::Filter::~Filter() | ||
| 16 | { | ||
| 17 | } | ||
| 18 | |||
| 19 | void Bu::Filter::close() | ||
| 20 | { | ||
| 21 | stop(); | ||
| 22 | rNext.close(); | ||
| 23 | } | ||
| 24 | |||
| 25 | Bu::size Bu::Filter::tell() | ||
| 26 | { | ||
| 27 | return rNext.tell(); | ||
| 28 | } | ||
| 29 | |||
| 30 | void Bu::Filter::seek( Bu::size offset ) | ||
| 31 | { | ||
| 32 | rNext.seek( offset ); | ||
| 33 | } | ||
| 34 | |||
| 35 | void Bu::Filter::setPos( Bu::size pos ) | ||
| 36 | { | ||
| 37 | rNext.setPos( pos ); | ||
| 38 | } | ||
| 39 | |||
| 40 | void Bu::Filter::setPosEnd( Bu::size pos ) | ||
| 41 | { | ||
| 42 | rNext.setPosEnd( pos ); | ||
| 43 | } | ||
| 44 | |||
| 45 | bool Bu::Filter::isEos() | ||
| 46 | { | ||
| 47 | return rNext.isEos(); | ||
| 48 | } | ||
| 49 | |||
| 50 | bool Bu::Filter::isOpen() | ||
| 51 | { | ||
| 52 | return rNext.isOpen(); | ||
| 53 | } | ||
| 54 | |||
| 55 | bool Bu::Filter::canRead() | ||
| 56 | { | ||
| 57 | return rNext.canRead(); | ||
| 58 | } | ||
| 59 | |||
| 60 | bool Bu::Filter::canWrite() | ||
| 61 | { | ||
| 62 | return rNext.canWrite(); | ||
| 63 | } | ||
| 64 | |||
| 65 | bool Bu::Filter::isReadable() | ||
| 66 | { | ||
| 67 | return rNext.isReadable(); | ||
| 68 | } | ||
| 69 | |||
| 70 | bool Bu::Filter::isWritable() | ||
| 71 | { | ||
| 72 | return rNext.isWritable(); | ||
| 73 | } | ||
| 74 | |||
| 75 | bool Bu::Filter::isSeekable() | ||
| 76 | { | ||
| 77 | return rNext.isSeekable(); | ||
| 78 | } | ||
| 79 | |||
| 80 | bool Bu::Filter::isBlocking() | ||
| 81 | { | ||
| 82 | return rNext.isBlocking(); | ||
| 83 | } | ||
| 84 | |||
| 85 | void Bu::Filter::setBlocking( bool bBlocking ) | ||
| 86 | { | ||
| 87 | rNext.setBlocking( bBlocking ); | ||
| 88 | } | ||
| 89 | |||
| 90 | void Bu::Filter::setSize( Bu::size ) | ||
| 91 | { | ||
| 92 | } | ||
| 93 | |||
| 94 | void Bu::Filter::flush() | ||
| 95 | { | ||
| 96 | rNext.flush(); | ||
| 97 | } | ||
| 98 | |||
| 99 | Bu::size Bu::Filter::getSize() const | ||
| 100 | { | ||
| 101 | return rNext.getSize(); | ||
| 102 | } | ||
| 103 | |||
| 104 | Bu::size Bu::Filter::getBlockSize() const | ||
| 105 | { | ||
| 106 | return rNext.getBlockSize(); | ||
| 107 | } | ||
| 108 | |||
| 109 | Bu::String Bu::Filter::getLocation() const | ||
| 110 | { | ||
| 111 | return rNext.getLocation(); | ||
| 112 | } | ||
| 113 | |||
| diff --git a/src/stable/filter.h b/src/stable/filter.h new file mode 100644 index 0000000..2c57805 --- /dev/null +++ b/src/stable/filter.h | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_FILTER_H | ||
| 9 | #define BU_FILTER_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | |||
| 13 | #include "bu/stream.h" | ||
| 14 | |||
| 15 | namespace Bu | ||
| 16 | { | ||
| 17 | /** | ||
| 18 | * Data filter base class. Each data filter should contain a read and write | ||
| 19 | * section. Effectively, the write applies the filter, the read un-applies | ||
| 20 | * the filter, if possible. For example, BZip2 is a filter that compresses | ||
| 21 | * on write and decompresses on read. All bi-directional filters should | ||
| 22 | * follow: x == read( write( x ) ) (byte-for-byte comparison) | ||
| 23 | * | ||
| 24 | * Also, all returned buffers should be owned by the filter, and deleted | ||
| 25 | * when the filter is deleted. This means that the output of a read or | ||
| 26 | * write operation must be used before the next call to read or write or the | ||
| 27 | * data will be destroyed. Also, the internal buffer may be changed or | ||
| 28 | * recreated between calls, so always get a new pointer from a call to | ||
| 29 | * read or write. | ||
| 30 | * | ||
| 31 | * The close function can also return data, so make sure to check for it, | ||
| 32 | * many filters such as compression filters will buffer data until they have | ||
| 33 | * enough to create a compression block, in these cases the leftover data | ||
| 34 | * will be returned by close. | ||
| 35 | *@ingroup Streams | ||
| 36 | */ | ||
| 37 | class Filter : public Bu::Stream | ||
| 38 | { | ||
| 39 | public: | ||
| 40 | Filter( Bu::Stream &rNext ); | ||
| 41 | virtual ~Filter(); | ||
| 42 | |||
| 43 | virtual void start()=0; | ||
| 44 | virtual Bu::size stop()=0; | ||
| 45 | virtual void close(); | ||
| 46 | virtual Bu::size tell(); | ||
| 47 | virtual void seek( Bu::size offset ); | ||
| 48 | virtual void setPos( Bu::size pos ); | ||
| 49 | virtual void setPosEnd( Bu::size pos ); | ||
| 50 | virtual bool isEos(); | ||
| 51 | virtual bool isOpen(); | ||
| 52 | |||
| 53 | virtual void flush(); | ||
| 54 | |||
| 55 | virtual bool canRead(); | ||
| 56 | virtual bool canWrite(); | ||
| 57 | |||
| 58 | virtual bool isReadable(); | ||
| 59 | virtual bool isWritable(); | ||
| 60 | virtual bool isSeekable(); | ||
| 61 | |||
| 62 | virtual bool isBlocking(); | ||
| 63 | virtual void setBlocking( bool bBlocking=true ); | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Most filters won't re-implement this, it doesn't make a lot of sense | ||
| 67 | * for filters, in general. | ||
| 68 | */ | ||
| 69 | virtual void setSize( Bu::size iSize ); | ||
| 70 | |||
| 71 | virtual size getSize() const; | ||
| 72 | virtual size getBlockSize() const; | ||
| 73 | virtual Bu::String getLocation() const; | ||
| 74 | |||
| 75 | protected: | ||
| 76 | Bu::Stream &rNext; | ||
| 77 | |||
| 78 | private: | ||
| 79 | |||
| 80 | }; | ||
| 81 | } | ||
| 82 | |||
| 83 | #endif | ||
| diff --git a/src/stable/fmt.h b/src/stable/fmt.h new file mode 100644 index 0000000..15d8efc --- /dev/null +++ b/src/stable/fmt.h | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | #ifndef BU_FMT_H | ||
| 2 | #define BU_FMT_H | ||
| 3 | |||
| 4 | namespace Bu | ||
| 5 | { | ||
| 6 | typedef struct Fmt | ||
| 7 | { | ||
| 8 | enum Alignment | ||
| 9 | { | ||
| 10 | Left = 0, | ||
| 11 | Center = 1, | ||
| 12 | Right = 2 | ||
| 13 | }; | ||
| 14 | Fmt() : | ||
| 15 | uMinWidth( 0 ), | ||
| 16 | cFillChar(' '), | ||
| 17 | uRadix( 10 ), | ||
| 18 | uAlign( Right ), | ||
| 19 | bPlus( false ), | ||
| 20 | bCaps( true ), | ||
| 21 | bTokenize( true ) | ||
| 22 | { | ||
| 23 | } | ||
| 24 | |||
| 25 | Fmt( unsigned int uMinWidth, unsigned int uRadix=10, | ||
| 26 | Alignment a=Right, bool bPlus=false, bool bCaps=true, | ||
| 27 | char cFill=' ') : | ||
| 28 | uMinWidth( uMinWidth ), | ||
| 29 | cFillChar(cFill), | ||
| 30 | uRadix( uRadix ), | ||
| 31 | uAlign( a ), | ||
| 32 | bPlus( bPlus ), | ||
| 33 | bCaps( bCaps ), | ||
| 34 | bTokenize( true ) | ||
| 35 | { | ||
| 36 | } | ||
| 37 | Fmt( unsigned int uMinWidth, Alignment a, | ||
| 38 | unsigned int uRadix=10, bool bPlus=false, bool bCaps=true, | ||
| 39 | char cFill=' ') : | ||
| 40 | uMinWidth( uMinWidth ), | ||
| 41 | cFillChar(cFill), | ||
| 42 | uRadix( uRadix ), | ||
| 43 | uAlign( a ), | ||
| 44 | bPlus( bPlus ), | ||
| 45 | bCaps( bCaps ), | ||
| 46 | bTokenize( true ) | ||
| 47 | { | ||
| 48 | } | ||
| 49 | |||
| 50 | static Fmt hex( unsigned int uWidth=0, bool bCaps=true ) | ||
| 51 | { | ||
| 52 | return Fmt( uWidth, 16, Right, false, bCaps, '0' ); | ||
| 53 | } | ||
| 54 | |||
| 55 | static Fmt oct( unsigned int uWidth=0 ) | ||
| 56 | { | ||
| 57 | return Fmt( uWidth, 8, Right, false, false, '0' ); | ||
| 58 | } | ||
| 59 | |||
| 60 | static Fmt bin( unsigned int uWidth=0 ) | ||
| 61 | { | ||
| 62 | return Fmt( uWidth, 1, Right, false, false, '0' ); | ||
| 63 | } | ||
| 64 | |||
| 65 | static Fmt ptr( bool bCaps=true ) | ||
| 66 | { | ||
| 67 | return Fmt( sizeof(ptrdiff_t)*2, 16, Right, false, bCaps, '0' ); | ||
| 68 | } | ||
| 69 | |||
| 70 | Fmt &width( unsigned int uWidth ); | ||
| 71 | Fmt &fill( char cFill='0' ); | ||
| 72 | Fmt &radix( unsigned int uRadix ); | ||
| 73 | Fmt &align( Alignment eAlign ); | ||
| 74 | Fmt &plus( bool bPlus=true ); | ||
| 75 | Fmt &caps( bool bCaps=true ); | ||
| 76 | Fmt &tokenize( bool bTokenize=true ); | ||
| 77 | |||
| 78 | Fmt &left(); | ||
| 79 | Fmt &right(); | ||
| 80 | Fmt ¢er(); | ||
| 81 | |||
| 82 | unsigned char uMinWidth; | ||
| 83 | char cFillChar; | ||
| 84 | unsigned short uRadix : 6; | ||
| 85 | unsigned short uAlign : 2; | ||
| 86 | unsigned short bPlus : 1; | ||
| 87 | unsigned short bCaps : 1; | ||
| 88 | unsigned short bTokenize : 1; | ||
| 89 | } Fmt; | ||
| 90 | }; | ||
| 91 | |||
| 92 | #endif | ||
| diff --git a/src/stable/formatter.cpp b/src/stable/formatter.cpp new file mode 100644 index 0000000..f275d71 --- /dev/null +++ b/src/stable/formatter.cpp | |||
| @@ -0,0 +1,547 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/formatter.h" | ||
| 9 | |||
| 10 | #include "bu/stream.h" | ||
| 11 | #include <string.h> | ||
| 12 | |||
| 13 | template<> float Bu::tlog( float x ) | ||
| 14 | { | ||
| 15 | return logf( x ); | ||
| 16 | } | ||
| 17 | |||
| 18 | template<> double Bu::tlog( double x ) | ||
| 19 | { | ||
| 20 | return log( x ); | ||
| 21 | } | ||
| 22 | |||
| 23 | template<> long double Bu::tlog( long double x ) | ||
| 24 | { | ||
| 25 | return logl( x ); | ||
| 26 | } | ||
| 27 | |||
| 28 | template<> float Bu::tfloor( float x ) | ||
| 29 | { | ||
| 30 | return floorf( x ); | ||
| 31 | } | ||
| 32 | |||
| 33 | template<> double Bu::tfloor( double x ) | ||
| 34 | { | ||
| 35 | return floor( x ); | ||
| 36 | } | ||
| 37 | |||
| 38 | template<> long double Bu::tfloor( long double x ) | ||
| 39 | { | ||
| 40 | return floorl( x ); | ||
| 41 | } | ||
| 42 | |||
| 43 | template<> float Bu::tpow( float x, float y ) | ||
| 44 | { | ||
| 45 | return powf( x, y ); | ||
| 46 | } | ||
| 47 | |||
| 48 | template<> double Bu::tpow( double x, double y ) | ||
| 49 | { | ||
| 50 | return pow( x, y ); | ||
| 51 | } | ||
| 52 | |||
| 53 | template<> long double Bu::tpow( long double x, long double y ) | ||
| 54 | { | ||
| 55 | return powl( x, y ); | ||
| 56 | } | ||
| 57 | |||
| 58 | Bu::Formatter::Formatter( Stream &rStream ) : | ||
| 59 | rStream( rStream ), | ||
| 60 | bTempFmt( false ), | ||
| 61 | uIndent( 0 ), | ||
| 62 | cIndent( '\t' ) | ||
| 63 | { | ||
| 64 | } | ||
| 65 | |||
| 66 | Bu::Formatter::~Formatter() | ||
| 67 | { | ||
| 68 | } | ||
| 69 | |||
| 70 | void Bu::Formatter::write( const Bu::String &sStr ) | ||
| 71 | { | ||
| 72 | rStream.write( sStr ); | ||
| 73 | } | ||
| 74 | |||
| 75 | void Bu::Formatter::write( const void *sStr, int iLen ) | ||
| 76 | { | ||
| 77 | rStream.write( sStr, iLen ); | ||
| 78 | } | ||
| 79 | |||
| 80 | void Bu::Formatter::writeAligned( const Bu::String &sStr ) | ||
| 81 | { | ||
| 82 | int iLen = sStr.getSize(); | ||
| 83 | if( iLen > fLast.uMinWidth ) | ||
| 84 | { | ||
| 85 | write( sStr ); | ||
| 86 | } | ||
| 87 | else | ||
| 88 | { | ||
| 89 | int iRem = fLast.uMinWidth - iLen; | ||
| 90 | switch( fLast.uAlign ) | ||
| 91 | { | ||
| 92 | case Fmt::Right: | ||
| 93 | for( int k = 0; k < iRem; k++ ) | ||
| 94 | write( &fLast.cFillChar, 1 ); | ||
| 95 | write( sStr ); | ||
| 96 | break; | ||
| 97 | |||
| 98 | case Fmt::Center: | ||
| 99 | { | ||
| 100 | int iHlf = iRem/2; | ||
| 101 | for( int k = 0; k < iHlf; k++ ) | ||
| 102 | write( &fLast.cFillChar, 1 ); | ||
| 103 | write( sStr ); | ||
| 104 | iHlf = iRem-iHlf;; | ||
| 105 | for( int k = 0; k < iHlf; k++ ) | ||
| 106 | write( &fLast.cFillChar, 1 ); | ||
| 107 | } | ||
| 108 | break; | ||
| 109 | |||
| 110 | case Fmt::Left: | ||
| 111 | write( sStr ); | ||
| 112 | for( int k = 0; k < iRem; k++ ) | ||
| 113 | write( &fLast.cFillChar, 1 ); | ||
| 114 | break; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | usedFormat(); | ||
| 119 | } | ||
| 120 | |||
| 121 | void Bu::Formatter::writeAligned( const char *sStr, int iLen ) | ||
| 122 | { | ||
| 123 | if( iLen > fLast.uMinWidth ) | ||
| 124 | { | ||
| 125 | write( sStr, iLen ); | ||
| 126 | } | ||
| 127 | else | ||
| 128 | { | ||
| 129 | int iRem = fLast.uMinWidth - iLen; | ||
| 130 | switch( fLast.uAlign ) | ||
| 131 | { | ||
| 132 | case Fmt::Right: | ||
| 133 | for( int k = 0; k < iRem; k++ ) | ||
| 134 | write( &fLast.cFillChar, 1 ); | ||
| 135 | write( sStr, iLen ); | ||
| 136 | break; | ||
| 137 | |||
| 138 | case Fmt::Center: | ||
| 139 | { | ||
| 140 | int iHlf = iRem/2; | ||
| 141 | for( int k = 0; k < iHlf; k++ ) | ||
| 142 | write( &fLast.cFillChar, 1 ); | ||
| 143 | write( sStr, iLen ); | ||
| 144 | iHlf = iRem-iHlf;; | ||
| 145 | for( int k = 0; k < iHlf; k++ ) | ||
| 146 | write( &fLast.cFillChar, 1 ); | ||
| 147 | } | ||
| 148 | break; | ||
| 149 | |||
| 150 | case Fmt::Left: | ||
| 151 | write( sStr, iLen ); | ||
| 152 | for( int k = 0; k < iRem; k++ ) | ||
| 153 | write( &fLast.cFillChar, 1 ); | ||
| 154 | break; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | usedFormat(); | ||
| 159 | } | ||
| 160 | |||
| 161 | void Bu::Formatter::read( void *sStr, int iLen ) | ||
| 162 | { | ||
| 163 | rStream.read( sStr, iLen ); | ||
| 164 | } | ||
| 165 | |||
| 166 | Bu::String Bu::Formatter::readToken() | ||
| 167 | { | ||
| 168 | Bu::String sRet; | ||
| 169 | if( fLast.bTokenize ) | ||
| 170 | { | ||
| 171 | for(;;) | ||
| 172 | { | ||
| 173 | char buf; | ||
| 174 | int iRead = rStream.read( &buf, 1 ); | ||
| 175 | if( iRead == 0 ) | ||
| 176 | return sRet; | ||
| 177 | if( buf == ' ' || buf == '\t' || buf == '\n' || buf == '\r' ) | ||
| 178 | continue; | ||
| 179 | else | ||
| 180 | { | ||
| 181 | sRet += buf; | ||
| 182 | break; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | for(;;) | ||
| 186 | { | ||
| 187 | char buf; | ||
| 188 | int iRead = rStream.read( &buf, 1 ); | ||
| 189 | if( iRead == 0 ) | ||
| 190 | return sRet; | ||
| 191 | if( buf == ' ' || buf == '\t' || buf == '\n' || buf == '\r' ) | ||
| 192 | return sRet; | ||
| 193 | else | ||
| 194 | sRet += buf; | ||
| 195 | } | ||
| 196 | } | ||
| 197 | else | ||
| 198 | { | ||
| 199 | for(;;) | ||
| 200 | { | ||
| 201 | char buf; | ||
| 202 | int iRead = rStream.read( &buf, 1 ); | ||
| 203 | if( iRead == 0 ) | ||
| 204 | return sRet; | ||
| 205 | else | ||
| 206 | sRet += buf; | ||
| 207 | } | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | void Bu::Formatter::incIndent() | ||
| 212 | { | ||
| 213 | if( uIndent < 0xFFU ) | ||
| 214 | uIndent++; | ||
| 215 | } | ||
| 216 | |||
| 217 | void Bu::Formatter::decIndent() | ||
| 218 | { | ||
| 219 | if( uIndent > 0 ) | ||
| 220 | uIndent--; | ||
| 221 | } | ||
| 222 | |||
| 223 | void Bu::Formatter::setIndent( uint8_t uLevel ) | ||
| 224 | { | ||
| 225 | uIndent = uLevel; | ||
| 226 | } | ||
| 227 | |||
| 228 | void Bu::Formatter::clearIndent() | ||
| 229 | { | ||
| 230 | uIndent = 0; | ||
| 231 | } | ||
| 232 | |||
| 233 | void Bu::Formatter::setIndentChar( char cIndent ) | ||
| 234 | { | ||
| 235 | this->cIndent = cIndent; | ||
| 236 | } | ||
| 237 | |||
| 238 | void Bu::Formatter::doFlush() | ||
| 239 | { | ||
| 240 | rStream.flush(); | ||
| 241 | } | ||
| 242 | |||
| 243 | Bu::Fmt &Bu::Fmt::width( unsigned int uWidth ) | ||
| 244 | { | ||
| 245 | this->uMinWidth = uWidth; | ||
| 246 | return *this; | ||
| 247 | } | ||
| 248 | |||
| 249 | Bu::Fmt &Bu::Fmt::fill( char cFill ) | ||
| 250 | { | ||
| 251 | this->cFillChar = (unsigned char)cFill; | ||
| 252 | return *this; | ||
| 253 | } | ||
| 254 | |||
| 255 | Bu::Fmt &Bu::Fmt::radix( unsigned int uRadix ) | ||
| 256 | { | ||
| 257 | this->uRadix = uRadix; | ||
| 258 | return *this; | ||
| 259 | } | ||
| 260 | |||
| 261 | Bu::Fmt &Bu::Fmt::align( Alignment eAlign ) | ||
| 262 | { | ||
| 263 | this->uAlign = eAlign; | ||
| 264 | return *this; | ||
| 265 | } | ||
| 266 | |||
| 267 | Bu::Fmt &Bu::Fmt::left() | ||
| 268 | { | ||
| 269 | this->uAlign = Fmt::Left; | ||
| 270 | return *this; | ||
| 271 | } | ||
| 272 | |||
| 273 | Bu::Fmt &Bu::Fmt::center() | ||
| 274 | { | ||
| 275 | this->uAlign = Fmt::Center; | ||
| 276 | return *this; | ||
| 277 | } | ||
| 278 | |||
| 279 | Bu::Fmt &Bu::Fmt::right() | ||
| 280 | { | ||
| 281 | this->uAlign = Fmt::Right; | ||
| 282 | return *this; | ||
| 283 | } | ||
| 284 | |||
| 285 | Bu::Fmt &Bu::Fmt::plus( bool bPlus ) | ||
| 286 | { | ||
| 287 | this->bPlus = bPlus; | ||
| 288 | return *this; | ||
| 289 | } | ||
| 290 | |||
| 291 | Bu::Fmt &Bu::Fmt::caps( bool bCaps ) | ||
| 292 | { | ||
| 293 | this->bCaps = bCaps; | ||
| 294 | return *this; | ||
| 295 | } | ||
| 296 | |||
| 297 | Bu::Fmt &Bu::Fmt::tokenize( bool bTokenize ) | ||
| 298 | { | ||
| 299 | this->bTokenize = bTokenize; | ||
| 300 | return *this; | ||
| 301 | } | ||
| 302 | |||
| 303 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, const Bu::Fmt &fmt ) | ||
| 304 | { | ||
| 305 | f.setTempFormat( fmt ); | ||
| 306 | return f; | ||
| 307 | } | ||
| 308 | |||
| 309 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, Bu::Formatter::Special s ) | ||
| 310 | { | ||
| 311 | switch( s ) | ||
| 312 | { | ||
| 313 | case Formatter::nl: | ||
| 314 | { | ||
| 315 | #ifdef WIN32 | ||
| 316 | f.write("\r\n", 2 ); | ||
| 317 | #else | ||
| 318 | f.write("\n", 1 ); | ||
| 319 | #endif | ||
| 320 | char ci = f.getIndentChar(); | ||
| 321 | for( int j = 0; j < f.getIndent(); j++ ) | ||
| 322 | f.write( &ci, 1 ); | ||
| 323 | f.doFlush(); | ||
| 324 | } | ||
| 325 | break; | ||
| 326 | |||
| 327 | case Formatter::flush: | ||
| 328 | f.doFlush(); | ||
| 329 | break; | ||
| 330 | } | ||
| 331 | return f; | ||
| 332 | } | ||
| 333 | |||
| 334 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, const char *sStr ) | ||
| 335 | { | ||
| 336 | f.writeAligned( sStr, strlen( sStr ) ); | ||
| 337 | return f; | ||
| 338 | } | ||
| 339 | |||
| 340 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, char *sStr ) | ||
| 341 | { | ||
| 342 | f.writeAligned( sStr, strlen( sStr ) ); | ||
| 343 | return f; | ||
| 344 | } | ||
| 345 | |||
| 346 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, const Bu::String &sStr ) | ||
| 347 | { | ||
| 348 | f.writeAligned( sStr ); | ||
| 349 | return f; | ||
| 350 | } | ||
| 351 | |||
| 352 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, signed char c ) | ||
| 353 | { | ||
| 354 | f.ifmt<signed char>( c ); | ||
| 355 | //f.write( (char *)&c, 1 ); | ||
| 356 | return f; | ||
| 357 | } | ||
| 358 | |||
| 359 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, char c ) | ||
| 360 | { | ||
| 361 | f.write( (char *)&c, 1 ); | ||
| 362 | return f; | ||
| 363 | } | ||
| 364 | |||
| 365 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, unsigned char c ) | ||
| 366 | { | ||
| 367 | f.ufmt<unsigned char>( c ); | ||
| 368 | //f.write( (char *)&c, 1 ); | ||
| 369 | return f; | ||
| 370 | } | ||
| 371 | |||
| 372 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, signed short i ) | ||
| 373 | { | ||
| 374 | f.ifmt<signed short>( i ); | ||
| 375 | return f; | ||
| 376 | } | ||
| 377 | |||
| 378 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, unsigned short i ) | ||
| 379 | { | ||
| 380 | f.ufmt<unsigned short>( i ); | ||
| 381 | return f; | ||
| 382 | } | ||
| 383 | |||
| 384 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, signed int i ) | ||
| 385 | { | ||
| 386 | f.ifmt<signed int>( i ); | ||
| 387 | return f; | ||
| 388 | } | ||
| 389 | |||
| 390 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, unsigned int i ) | ||
| 391 | { | ||
| 392 | f.ufmt<unsigned int>( i ); | ||
| 393 | return f; | ||
| 394 | } | ||
| 395 | |||
| 396 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, signed long i ) | ||
| 397 | { | ||
| 398 | f.ifmt<signed long>( i ); | ||
| 399 | return f; | ||
| 400 | } | ||
| 401 | |||
| 402 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, unsigned long i ) | ||
| 403 | { | ||
| 404 | f.ufmt<unsigned long>( i ); | ||
| 405 | return f; | ||
| 406 | } | ||
| 407 | |||
| 408 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, signed long long i ) | ||
| 409 | { | ||
| 410 | f.ifmt<signed long long>( i ); | ||
| 411 | return f; | ||
| 412 | } | ||
| 413 | |||
| 414 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, unsigned long long i ) | ||
| 415 | { | ||
| 416 | f.ufmt<unsigned long long>( i ); | ||
| 417 | return f; | ||
| 418 | } | ||
| 419 | |||
| 420 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, float flt ) | ||
| 421 | { | ||
| 422 | f.ffmt<float>( flt ); | ||
| 423 | return f; | ||
| 424 | } | ||
| 425 | |||
| 426 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, double flt ) | ||
| 427 | { | ||
| 428 | f.ffmt<double>( flt ); | ||
| 429 | return f; | ||
| 430 | } | ||
| 431 | |||
| 432 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, long double flt ) | ||
| 433 | { | ||
| 434 | f.ffmt<long double>( flt ); | ||
| 435 | return f; | ||
| 436 | } | ||
| 437 | |||
| 438 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, bool b ) | ||
| 439 | { | ||
| 440 | f.writeAligned( b?("true"):("false") ); | ||
| 441 | return f; | ||
| 442 | } | ||
| 443 | |||
| 444 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, Bu::String &sStr ) | ||
| 445 | { | ||
| 446 | sStr = f.readToken(); | ||
| 447 | return f; | ||
| 448 | } | ||
| 449 | |||
| 450 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, signed char &c ) | ||
| 451 | { | ||
| 452 | f.read( &c, 1 ); | ||
| 453 | return f; | ||
| 454 | } | ||
| 455 | |||
| 456 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, char &c ) | ||
| 457 | { | ||
| 458 | f.read( &c, 1 ); | ||
| 459 | return f; | ||
| 460 | } | ||
| 461 | |||
| 462 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, unsigned char &c ) | ||
| 463 | { | ||
| 464 | f.read( &c, 1 ); | ||
| 465 | return f; | ||
| 466 | } | ||
| 467 | |||
| 468 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, signed short &i ) | ||
| 469 | { | ||
| 470 | f.iparse( i, f.readToken() ); | ||
| 471 | return f; | ||
| 472 | } | ||
| 473 | |||
| 474 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, unsigned short &i ) | ||
| 475 | { | ||
| 476 | f.uparse( i, f.readToken() ); | ||
| 477 | return f; | ||
| 478 | } | ||
| 479 | |||
| 480 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, signed int &i ) | ||
| 481 | { | ||
| 482 | f.iparse( i, f.readToken() ); | ||
| 483 | return f; | ||
| 484 | } | ||
| 485 | |||
| 486 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, unsigned int &i ) | ||
| 487 | { | ||
| 488 | f.uparse( i, f.readToken() ); | ||
| 489 | return f; | ||
| 490 | } | ||
| 491 | |||
| 492 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, signed long &i ) | ||
| 493 | { | ||
| 494 | f.iparse( i, f.readToken() ); | ||
| 495 | return f; | ||
| 496 | } | ||
| 497 | |||
| 498 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, unsigned long &i ) | ||
| 499 | { | ||
| 500 | f.uparse( i, f.readToken() ); | ||
| 501 | return f; | ||
| 502 | } | ||
| 503 | |||
| 504 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, signed long long &i ) | ||
| 505 | { | ||
| 506 | f.iparse( i, f.readToken() ); | ||
| 507 | return f; | ||
| 508 | } | ||
| 509 | |||
| 510 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, unsigned long long &i ) | ||
| 511 | { | ||
| 512 | f.uparse( i, f.readToken() ); | ||
| 513 | return f; | ||
| 514 | } | ||
| 515 | |||
| 516 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, float &flt ) | ||
| 517 | { | ||
| 518 | f.fparse( flt, f.readToken() ); | ||
| 519 | return f; | ||
| 520 | } | ||
| 521 | |||
| 522 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, double &flt ) | ||
| 523 | { | ||
| 524 | f.fparse( flt, f.readToken() ); | ||
| 525 | return f; | ||
| 526 | } | ||
| 527 | |||
| 528 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, long double &flt ) | ||
| 529 | { | ||
| 530 | f.fparse( flt, f.readToken() ); | ||
| 531 | return f; | ||
| 532 | } | ||
| 533 | |||
| 534 | Bu::Formatter &Bu::operator>>( Bu::Formatter &f, bool &b ) | ||
| 535 | { | ||
| 536 | Bu::String sStr = f.readToken(); | ||
| 537 | if( !sStr.isSet() ) | ||
| 538 | return f; | ||
| 539 | char c = *sStr.begin(); | ||
| 540 | if( c == 'y' || c == 'Y' || c == 't' || c == 'T' ) | ||
| 541 | b = true; | ||
| 542 | else if( c == 'n' || c == 'N' || c == 'f' || c == 'F' ) | ||
| 543 | b = false; | ||
| 544 | |||
| 545 | return f; | ||
| 546 | } | ||
| 547 | |||
| diff --git a/src/stable/formatter.h b/src/stable/formatter.h new file mode 100644 index 0000000..a440ec3 --- /dev/null +++ b/src/stable/formatter.h | |||
| @@ -0,0 +1,303 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_FORMATTER_H | ||
| 9 | #define BU_FORMATTER_H | ||
| 10 | |||
| 11 | #include "bu/string.h" | ||
| 12 | #include "bu/fmt.h" | ||
| 13 | |||
| 14 | #include <math.h> | ||
| 15 | |||
| 16 | namespace Bu | ||
| 17 | { | ||
| 18 | class Stream; | ||
| 19 | |||
| 20 | template<typename t> t tlog( t x ); | ||
| 21 | template<> float tlog( float x ); | ||
| 22 | template<> double tlog( double x ); | ||
| 23 | template<> long double tlog( long double x ); | ||
| 24 | |||
| 25 | template<typename t> t tfloor( t x ); | ||
| 26 | template<> float tfloor( float x ); | ||
| 27 | template<> double tfloor( double x ); | ||
| 28 | template<> long double tfloor( long double x ); | ||
| 29 | |||
| 30 | template<typename t> t tpow( t x, t y ); | ||
| 31 | template<> float tpow( float x, float y ); | ||
| 32 | template<> double tpow( double x, double y ); | ||
| 33 | template<> long double tpow( long double x, long double y ); | ||
| 34 | |||
| 35 | class Formatter | ||
| 36 | { | ||
| 37 | public: | ||
| 38 | Formatter( Stream &rStream ); | ||
| 39 | virtual ~Formatter(); | ||
| 40 | |||
| 41 | void write( const Bu::String &sStr ); | ||
| 42 | void write( const void *sStr, int iLen ); | ||
| 43 | void writeAligned( const Bu::String &sStr ); | ||
| 44 | void writeAligned( const char *sStr, int iLen ); | ||
| 45 | |||
| 46 | void read( void *sStr, int iLen ); | ||
| 47 | Bu::String readToken(); | ||
| 48 | |||
| 49 | void incIndent(); | ||
| 50 | void decIndent(); | ||
| 51 | void setIndent( uint8_t uLevel ); | ||
| 52 | void clearIndent(); | ||
| 53 | uint8_t getIndent() const { return uIndent; } | ||
| 54 | void setIndentChar( char cIndent ); | ||
| 55 | char getIndentChar() const { return cIndent; } | ||
| 56 | |||
| 57 | void setFormat( const Fmt &f ) | ||
| 58 | { | ||
| 59 | fLast = f; | ||
| 60 | bTempFmt = false; | ||
| 61 | } | ||
| 62 | |||
| 63 | void setTempFormat( const Fmt &f ) | ||
| 64 | { | ||
| 65 | fLast = f; | ||
| 66 | bTempFmt = true; | ||
| 67 | } | ||
| 68 | |||
| 69 | void usedFormat() | ||
| 70 | { | ||
| 71 | if( bTempFmt ) | ||
| 72 | fLast = Fmt(); | ||
| 73 | } | ||
| 74 | |||
| 75 | template<typename type> | ||
| 76 | void ifmt( type i ) | ||
| 77 | { | ||
| 78 | // This code is taken from Nango, hopefully we can make it better. | ||
| 79 | bool bNeg = i<0; | ||
| 80 | char cBase = fLast.bCaps?'A':'a'; | ||
| 81 | char buf[sizeof(type)*8+1]; | ||
| 82 | if( bNeg ) i = -i; | ||
| 83 | if( fLast.uRadix < 2 || fLast.uRadix > 36 ) | ||
| 84 | { | ||
| 85 | usedFormat(); | ||
| 86 | return; | ||
| 87 | } | ||
| 88 | |||
| 89 | for( int j = sizeof(type)*8; j >= 0; j-- ) | ||
| 90 | { | ||
| 91 | int c = i%fLast.uRadix; | ||
| 92 | i /= fLast.uRadix; | ||
| 93 | buf[j] = (char)((c<10)?('0'+c):(cBase+c-10)); | ||
| 94 | if( i == 0 ) | ||
| 95 | { | ||
| 96 | if( bNeg ) buf[--j] = '-'; | ||
| 97 | else if( fLast.bPlus ) buf[--j] = '+'; | ||
| 98 | writeAligned( buf+j, sizeof(type)*8-j+1 ); | ||
| 99 | |||
| 100 | return; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | usedFormat(); | ||
| 104 | } | ||
| 105 | |||
| 106 | template<typename type> | ||
| 107 | void ufmt( type i ) | ||
| 108 | { | ||
| 109 | // This code is taken from Nango, hopefully we can make it better. | ||
| 110 | char buf[sizeof(type)*8+1]; | ||
| 111 | char cBase = fLast.bCaps?'A':'a'; | ||
| 112 | if( fLast.uRadix < 2 || fLast.uRadix > 36 ) | ||
| 113 | { | ||
| 114 | usedFormat(); | ||
| 115 | return; | ||
| 116 | } | ||
| 117 | |||
| 118 | for( int j = sizeof(type)*8; j >= 0; j-- ) | ||
| 119 | { | ||
| 120 | int c = i%fLast.uRadix; | ||
| 121 | i /= fLast.uRadix; | ||
| 122 | buf[j] = (char)((c<10)?('0'+c):(cBase+c-10)); | ||
| 123 | if( i == 0 ) | ||
| 124 | { | ||
| 125 | if( fLast.bPlus ) buf[--j] = '+'; | ||
| 126 | writeAligned( buf+j, sizeof(type)*8-j+1 ); | ||
| 127 | |||
| 128 | return; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | usedFormat(); | ||
| 132 | } | ||
| 133 | |||
| 134 | template<typename type> | ||
| 135 | void ffmt( type f ) | ||
| 136 | { | ||
| 137 | Bu::String fTmp; | ||
| 138 | char cBase = fLast.bCaps?'A':'a'; | ||
| 139 | if( fLast.uRadix < 2 || fLast.uRadix > 36 ) | ||
| 140 | { | ||
| 141 | usedFormat(); | ||
| 142 | return; | ||
| 143 | } | ||
| 144 | |||
| 145 | if( signbit(f) ) | ||
| 146 | { | ||
| 147 | f = -f; | ||
| 148 | fTmp += "-"; | ||
| 149 | } | ||
| 150 | int iScale = tfloor(tlog( f ) / tlog( (type)fLast.uRadix )); | ||
| 151 | f /= tpow( (type)fLast.uRadix, (type)iScale ); | ||
| 152 | |||
| 153 | if( iScale < 0 ) | ||
| 154 | { | ||
| 155 | fTmp += "0."; | ||
| 156 | for( int j = 1; j < -iScale; j++ ) | ||
| 157 | fTmp += '0'; | ||
| 158 | } | ||
| 159 | int c = f; | ||
| 160 | fTmp += (char)((c<10)?('0'+c):(cBase+c-10)); | ||
| 161 | f -= (int)f; | ||
| 162 | int j; | ||
| 163 | for( j = 0; j < 8 && f; j++ ) | ||
| 164 | { | ||
| 165 | if( iScale - j == 0 ) | ||
| 166 | fTmp += '.'; | ||
| 167 | f = f*fLast.uRadix; | ||
| 168 | int c = f; | ||
| 169 | fTmp += (char)((c<10)?('0'+c):(cBase+c-10)); | ||
| 170 | f -= (int)f; | ||
| 171 | } | ||
| 172 | if( iScale >= j ) | ||
| 173 | { | ||
| 174 | for( int k = j; k < iScale; k++ ) | ||
| 175 | fTmp += '0'; | ||
| 176 | fTmp += ".0"; | ||
| 177 | } | ||
| 178 | |||
| 179 | writeAligned( fTmp ); | ||
| 180 | usedFormat(); | ||
| 181 | } | ||
| 182 | |||
| 183 | template<typename type> | ||
| 184 | void iparse( type &i, const Bu::String &sBuf ) | ||
| 185 | { | ||
| 186 | if( !sBuf.isSet() ) | ||
| 187 | return; | ||
| 188 | if( sBuf[0] != '+' && sBuf[0] != '-' && | ||
| 189 | (sBuf[0] < '0' && sBuf[0] > '9') ) | ||
| 190 | return; | ||
| 191 | int j = 1; | ||
| 192 | int iMax = sBuf.getSize(); | ||
| 193 | for(; j < iMax && (sBuf[j] >= '0' && sBuf[j] <= '9'); j++ ) { } | ||
| 194 | i = 0; | ||
| 195 | type iPos = 1; | ||
| 196 | for(j--; j >= 0; j-- ) | ||
| 197 | { | ||
| 198 | if( sBuf[j] == '+' || sBuf[j] == '-' ) | ||
| 199 | continue; | ||
| 200 | i += (sBuf[j]-'0')*iPos; | ||
| 201 | iPos *= fLast.uRadix; | ||
| 202 | } | ||
| 203 | if( sBuf[0] == '-' ) | ||
| 204 | i = -i; | ||
| 205 | |||
| 206 | usedFormat(); | ||
| 207 | } | ||
| 208 | |||
| 209 | template<typename type> | ||
| 210 | void uparse( type &i, const Bu::String &sBuf ) | ||
| 211 | { | ||
| 212 | if( !sBuf.isSet() ) | ||
| 213 | return; | ||
| 214 | if( sBuf[0] != '+' && | ||
| 215 | (sBuf[0] < '0' && sBuf[0] > '9') ) | ||
| 216 | return; | ||
| 217 | int j = 1; | ||
| 218 | int iMax = sBuf.getSize(); | ||
| 219 | for(; j < iMax && (sBuf[j] >= '0' && sBuf[j] <= '9'); j++ ) { } | ||
| 220 | i = 0; | ||
| 221 | type iPos = 1; | ||
| 222 | for(j--; j >= 0; j-- ) | ||
| 223 | { | ||
| 224 | if( sBuf[j] == '+' ) | ||
| 225 | continue; | ||
| 226 | i += (sBuf[j]-'0')*iPos; | ||
| 227 | iPos *= fLast.uRadix; | ||
| 228 | } | ||
| 229 | |||
| 230 | usedFormat(); | ||
| 231 | } | ||
| 232 | |||
| 233 | template<typename type> | ||
| 234 | void fparse( type &f, const Bu::String &sBuf ) | ||
| 235 | { | ||
| 236 | double fIn; | ||
| 237 | sscanf( sBuf.getStr(), "%lf", &fIn ); | ||
| 238 | f = fIn; | ||
| 239 | usedFormat(); | ||
| 240 | } | ||
| 241 | |||
| 242 | enum Special | ||
| 243 | { | ||
| 244 | nl, | ||
| 245 | flush | ||
| 246 | }; | ||
| 247 | |||
| 248 | void doFlush(); | ||
| 249 | |||
| 250 | private: | ||
| 251 | Stream &rStream; | ||
| 252 | Fmt fLast; | ||
| 253 | bool bTempFmt; | ||
| 254 | uint8_t uIndent; | ||
| 255 | char cIndent; | ||
| 256 | }; | ||
| 257 | |||
| 258 | Formatter &operator<<( Formatter &f, const Fmt &fmt ); | ||
| 259 | Formatter &operator<<( Formatter &f, Formatter::Special s ); | ||
| 260 | Formatter &operator<<( Formatter &f, const char *sStr ); | ||
| 261 | Formatter &operator<<( Formatter &f, char *sStr ); | ||
| 262 | Formatter &operator<<( Formatter &f, const Bu::String &sStr ); | ||
| 263 | Formatter &operator<<( Formatter &f, signed char c ); | ||
| 264 | Formatter &operator<<( Formatter &f, char c ); | ||
| 265 | Formatter &operator<<( Formatter &f, unsigned char c ); | ||
| 266 | Formatter &operator<<( Formatter &f, signed short i ); | ||
| 267 | Formatter &operator<<( Formatter &f, unsigned short i ); | ||
| 268 | Formatter &operator<<( Formatter &f, signed int i ); | ||
| 269 | Formatter &operator<<( Formatter &f, unsigned int i ); | ||
| 270 | Formatter &operator<<( Formatter &f, signed long i ); | ||
| 271 | Formatter &operator<<( Formatter &f, unsigned long i ); | ||
| 272 | Formatter &operator<<( Formatter &f, signed long long i ); | ||
| 273 | Formatter &operator<<( Formatter &f, unsigned long long i ); | ||
| 274 | Formatter &operator<<( Formatter &f, float flt ); | ||
| 275 | Formatter &operator<<( Formatter &f, double flt ); | ||
| 276 | Formatter &operator<<( Formatter &f, long double flt ); | ||
| 277 | Formatter &operator<<( Formatter &f, bool b ); | ||
| 278 | |||
| 279 | Formatter &operator>>( Formatter &f, Bu::String &sStr ); | ||
| 280 | Formatter &operator>>( Formatter &f, signed char &c ); | ||
| 281 | Formatter &operator>>( Formatter &f, char &c ); | ||
| 282 | Formatter &operator>>( Formatter &f, unsigned char &c ); | ||
| 283 | Formatter &operator>>( Formatter &f, signed short &i ); | ||
| 284 | Formatter &operator>>( Formatter &f, unsigned short &i ); | ||
| 285 | Formatter &operator>>( Formatter &f, signed int &i ); | ||
| 286 | Formatter &operator>>( Formatter &f, unsigned int &i ); | ||
| 287 | Formatter &operator>>( Formatter &f, signed long &i ); | ||
| 288 | Formatter &operator>>( Formatter &f, unsigned long &i ); | ||
| 289 | Formatter &operator>>( Formatter &f, signed long long &i ); | ||
| 290 | Formatter &operator>>( Formatter &f, unsigned long long &i ); | ||
| 291 | Formatter &operator>>( Formatter &f, float &flt ); | ||
| 292 | Formatter &operator>>( Formatter &f, double &flt ); | ||
| 293 | Formatter &operator>>( Formatter &f, long double &flt ); | ||
| 294 | Formatter &operator>>( Formatter &f, bool &b ); | ||
| 295 | |||
| 296 | template<typename type> | ||
| 297 | Formatter &operator<<( Formatter &f, const type *p ) | ||
| 298 | { | ||
| 299 | return f << "0x" << Fmt::hex(sizeof(ptrdiff_t)*2) << (ptrdiff_t)(p); | ||
| 300 | } | ||
| 301 | }; | ||
| 302 | |||
| 303 | #endif | ||
| diff --git a/src/stable/formula.cpp b/src/stable/formula.cpp new file mode 100644 index 0000000..ac435ed --- /dev/null +++ b/src/stable/formula.cpp | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/formula.h" | ||
| 9 | |||
| 10 | namespace Bu | ||
| 11 | { | ||
| 12 | subExceptionDef( FormulaException ); | ||
| 13 | } | ||
| 14 | |||
| diff --git a/src/stable/formula.h b/src/stable/formula.h new file mode 100644 index 0000000..687e6c3 --- /dev/null +++ b/src/stable/formula.h | |||
| @@ -0,0 +1,430 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef FORMULA_H | ||
| 9 | #define FORMULA_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include <stdlib.h> | ||
| 13 | |||
| 14 | #include <math.h> | ||
| 15 | //#include "sbuffer.h" | ||
| 16 | |||
| 17 | #include "bu/stack.h" | ||
| 18 | #include "bu/exceptionbase.h" | ||
| 19 | #include "bu/hash.h" | ||
| 20 | #include "bu/string.h" | ||
| 21 | |||
| 22 | namespace Bu | ||
| 23 | { | ||
| 24 | subExceptionDecl( FormulaException ); | ||
| 25 | /** | ||
| 26 | * Implements a very simple formula parser that allows use of variables and | ||
| 27 | * custom functions. This is based on a simple calculator-type parser that | ||
| 28 | * executes as it processes, accounting for operator precedence and | ||
| 29 | * grouping. | ||
| 30 | * | ||
| 31 | * prec = precision, a type to use for all math (except binary ops) | ||
| 32 | * bin = binary type, a type to hard cast all data to for binary ops | ||
| 33 | */ | ||
| 34 | template<typename prec, typename bin=uint32_t> | ||
| 35 | class Formula | ||
| 36 | { | ||
| 37 | public: | ||
| 38 | class Func | ||
| 39 | { | ||
| 40 | public: | ||
| 41 | virtual prec operator()( prec )=0; | ||
| 42 | }; | ||
| 43 | |||
| 44 | typedef Hash<Bu::String, prec> varHash; | ||
| 45 | typedef Hash<Bu::String, Func *> funcHash; | ||
| 46 | |||
| 47 | Formula() | ||
| 48 | { | ||
| 49 | } | ||
| 50 | |||
| 51 | virtual ~Formula() | ||
| 52 | { | ||
| 53 | for( typename funcHash::iterator i = hFunc.begin(); | ||
| 54 | i != hFunc.end(); i++ ) | ||
| 55 | { | ||
| 56 | delete (*i); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | prec run( const Bu::String &sFormulaSrc ) | ||
| 61 | { | ||
| 62 | if( sFormulaSrc.isEmpty() ) | ||
| 63 | throw FormulaException("Empty formula, nothing to do."); | ||
| 64 | try | ||
| 65 | { | ||
| 66 | const char *sFormula = sFormulaSrc.getStr(); | ||
| 67 | for(;;) | ||
| 68 | { | ||
| 69 | uint8_t tNum = nextToken( &sFormula ); | ||
| 70 | if( tNum == symSubtract ) | ||
| 71 | { | ||
| 72 | sOper.push( symNegate ); | ||
| 73 | continue; | ||
| 74 | } | ||
| 75 | else if( tNum == symNot ) | ||
| 76 | { | ||
| 77 | sOper.push( symNot ); | ||
| 78 | continue; | ||
| 79 | } | ||
| 80 | else if( tNum == symOpenParen ) | ||
| 81 | { | ||
| 82 | sOper.push( tNum ); | ||
| 83 | continue; | ||
| 84 | } | ||
| 85 | else if( tNum == symFunction ) | ||
| 86 | { | ||
| 87 | sOper.push( symFunction ); | ||
| 88 | continue; | ||
| 89 | } | ||
| 90 | else if( tNum == symEOS ) | ||
| 91 | { | ||
| 92 | throw Bu::FormulaException( | ||
| 93 | "Cannot end with an operator."); | ||
| 94 | } | ||
| 95 | |||
| 96 | oppart: uint8_t tOpr = nextToken( &sFormula ); | ||
| 97 | if( tOpr == symEOS ) | ||
| 98 | { | ||
| 99 | reduce(); | ||
| 100 | prec ret = sValue.top(); | ||
| 101 | sValue.clear(); | ||
| 102 | sFunc.clear(); | ||
| 103 | sOper.clear(); | ||
| 104 | return ret; | ||
| 105 | } | ||
| 106 | if( !sOper.isEmpty() && getPrec( sOper.top() ) > | ||
| 107 | getPrec( tOpr ) ) | ||
| 108 | { | ||
| 109 | reduce(); | ||
| 110 | } | ||
| 111 | if( tOpr != symCloseParen ) | ||
| 112 | { | ||
| 113 | sOper.push( tOpr ); | ||
| 114 | } | ||
| 115 | else | ||
| 116 | { | ||
| 117 | reduce( true ); | ||
| 118 | goto oppart; | ||
| 119 | } | ||
| 120 | } | ||
| 121 | } | ||
| 122 | catch( ... ) | ||
| 123 | { | ||
| 124 | sValue.clear(); | ||
| 125 | sFunc.clear(); | ||
| 126 | sOper.clear(); | ||
| 127 | throw; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | varHash hVars; | ||
| 132 | funcHash hFunc; | ||
| 133 | |||
| 134 | private: | ||
| 135 | enum | ||
| 136 | { | ||
| 137 | symEOS, | ||
| 138 | symAdd, | ||
| 139 | symSubtract, | ||
| 140 | symMultiply, | ||
| 141 | symDivide, | ||
| 142 | symOpenParen, | ||
| 143 | symCloseParen, | ||
| 144 | symNumber, | ||
| 145 | symVariable, | ||
| 146 | symFunction, | ||
| 147 | symExponent, | ||
| 148 | symNegate, | ||
| 149 | symModulus, | ||
| 150 | |||
| 151 | symAnd, | ||
| 152 | symOr, | ||
| 153 | symXor, | ||
| 154 | symNot | ||
| 155 | }; | ||
| 156 | |||
| 157 | typedef uint8_t symType; | ||
| 158 | |||
| 159 | Bu::Stack<symType> sOper; | ||
| 160 | Bu::Stack<prec> sValue; | ||
| 161 | Bu::Stack<Bu::String> sFunc; | ||
| 162 | |||
| 163 | private: | ||
| 164 | symType getPrec( symType nOper ) | ||
| 165 | { | ||
| 166 | switch( nOper ) | ||
| 167 | { | ||
| 168 | case symNumber: | ||
| 169 | case symVariable: | ||
| 170 | case symOpenParen: | ||
| 171 | case symCloseParen: | ||
| 172 | return 0; | ||
| 173 | |||
| 174 | case symAdd: | ||
| 175 | case symSubtract: | ||
| 176 | return 1; | ||
| 177 | |||
| 178 | case symMultiply: | ||
| 179 | case symDivide: | ||
| 180 | case symModulus: | ||
| 181 | return 2; | ||
| 182 | |||
| 183 | case symAnd: | ||
| 184 | case symOr: | ||
| 185 | case symXor: | ||
| 186 | return 2; | ||
| 187 | |||
| 188 | case symExponent: | ||
| 189 | case symNot: | ||
| 190 | case symNegate: | ||
| 191 | case symFunction: | ||
| 192 | return 3; | ||
| 193 | |||
| 194 | default: | ||
| 195 | return 0; | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | symType nextToken( const char **sBuf ) | ||
| 200 | { | ||
| 201 | for(;;) | ||
| 202 | { | ||
| 203 | char cbuf = **sBuf; | ||
| 204 | ++(*sBuf); | ||
| 205 | switch( cbuf ) | ||
| 206 | { | ||
| 207 | case '+': | ||
| 208 | return symAdd; | ||
| 209 | |||
| 210 | case '-': | ||
| 211 | return symSubtract; | ||
| 212 | |||
| 213 | case '*': | ||
| 214 | return symMultiply; | ||
| 215 | |||
| 216 | case '/': | ||
| 217 | return symDivide; | ||
| 218 | |||
| 219 | case '^': | ||
| 220 | return symExponent; | ||
| 221 | |||
| 222 | case '%': | ||
| 223 | return symModulus; | ||
| 224 | |||
| 225 | case '(': | ||
| 226 | return symOpenParen; | ||
| 227 | |||
| 228 | case ')': | ||
| 229 | return symCloseParen; | ||
| 230 | |||
| 231 | case '|': | ||
| 232 | return symOr; | ||
| 233 | |||
| 234 | case '&': | ||
| 235 | return symAnd; | ||
| 236 | |||
| 237 | case '#': | ||
| 238 | return symXor; | ||
| 239 | |||
| 240 | case '~': | ||
| 241 | return symNot; | ||
| 242 | |||
| 243 | case ' ': | ||
| 244 | case '\t': | ||
| 245 | case '\n': | ||
| 246 | case '\r': | ||
| 247 | break; | ||
| 248 | |||
| 249 | case '\0': | ||
| 250 | return symEOS; | ||
| 251 | |||
| 252 | default: | ||
| 253 | if( cbuf == '.' || (cbuf >= '0' && cbuf <= '9') ) | ||
| 254 | { | ||
| 255 | char num[50]={cbuf}; | ||
| 256 | int nPos = 1; | ||
| 257 | bool bDot = false; | ||
| 258 | |||
| 259 | for(;;) | ||
| 260 | { | ||
| 261 | cbuf = **sBuf; | ||
| 262 | if( cbuf == '.' ) | ||
| 263 | { | ||
| 264 | if( bDot == false ) | ||
| 265 | bDot = true; | ||
| 266 | else | ||
| 267 | throw FormulaException( | ||
| 268 | "Numbers cannot have more than one " | ||
| 269 | ". in them." | ||
| 270 | ); | ||
| 271 | } | ||
| 272 | if( cbuf == '.' || | ||
| 273 | (cbuf >= '0' && cbuf <= '9') ) | ||
| 274 | { | ||
| 275 | num[nPos++] = cbuf; | ||
| 276 | } | ||
| 277 | else | ||
| 278 | { | ||
| 279 | num[nPos] = '\0'; | ||
| 280 | sValue.push( | ||
| 281 | static_cast<prec>( | ||
| 282 | strtod( num, NULL ) | ||
| 283 | ) | ||
| 284 | ); | ||
| 285 | return symNumber; | ||
| 286 | } | ||
| 287 | ++(*sBuf); | ||
| 288 | } | ||
| 289 | } | ||
| 290 | else if( (cbuf >= 'a' && cbuf <= 'z') || | ||
| 291 | (cbuf >= 'A' && cbuf <= 'Z') || | ||
| 292 | (cbuf == '_') ) | ||
| 293 | { | ||
| 294 | char tok[50]={cbuf}; | ||
| 295 | int nPos = 1; | ||
| 296 | |||
| 297 | for(;;) | ||
| 298 | { | ||
| 299 | cbuf = **sBuf; | ||
| 300 | if( (cbuf >= 'a' && cbuf <= 'z') || | ||
| 301 | (cbuf >= 'A' && cbuf <= 'Z') || | ||
| 302 | (cbuf >= '0' && cbuf <= '9') || | ||
| 303 | cbuf == '_' || cbuf == '.' || cbuf == ':' ) | ||
| 304 | { | ||
| 305 | tok[nPos++] = cbuf; | ||
| 306 | } | ||
| 307 | else | ||
| 308 | { | ||
| 309 | tok[nPos] = '\0'; | ||
| 310 | if( hVars.has( tok ) ) | ||
| 311 | { | ||
| 312 | sValue.push( hVars[tok] ); | ||
| 313 | return symNumber; | ||
| 314 | } | ||
| 315 | else if( hFunc.has( tok ) ) | ||
| 316 | { | ||
| 317 | sFunc.push( tok ); | ||
| 318 | return symFunction; | ||
| 319 | } | ||
| 320 | else | ||
| 321 | { | ||
| 322 | throw FormulaException( | ||
| 323 | "No variable or function named " | ||
| 324 | "\"%s\" exists.", | ||
| 325 | tok | ||
| 326 | ); | ||
| 327 | } | ||
| 328 | } | ||
| 329 | ++(*sBuf); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | break; | ||
| 333 | } | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | void reduce( bool bCloseParen = false ) | ||
| 338 | { | ||
| 339 | while( !sOper.isEmpty() ) | ||
| 340 | { | ||
| 341 | uint8_t nOpr = sOper.top(); | ||
| 342 | if( nOpr == symOpenParen ) | ||
| 343 | { | ||
| 344 | if( bCloseParen == true ) | ||
| 345 | sOper.pop(); | ||
| 346 | return; | ||
| 347 | } | ||
| 348 | sOper.pop(); | ||
| 349 | |||
| 350 | prec dTop = sValue.top(); | ||
| 351 | sValue.pop(); | ||
| 352 | |||
| 353 | switch( nOpr ) | ||
| 354 | { | ||
| 355 | case symAdd: | ||
| 356 | sValue.top() += dTop; | ||
| 357 | break; | ||
| 358 | |||
| 359 | case symSubtract: | ||
| 360 | sValue.top() -= dTop; | ||
| 361 | break; | ||
| 362 | |||
| 363 | case symMultiply: | ||
| 364 | sValue.top() *= dTop; | ||
| 365 | break; | ||
| 366 | |||
| 367 | case symDivide: | ||
| 368 | sValue.top() /= dTop; | ||
| 369 | break; | ||
| 370 | |||
| 371 | case symExponent: | ||
| 372 | sValue.top() = static_cast<prec>( | ||
| 373 | pow( sValue.top(), dTop ) | ||
| 374 | ); | ||
| 375 | break; | ||
| 376 | |||
| 377 | case symModulus: | ||
| 378 | sValue.top() = static_cast<prec>( | ||
| 379 | fmod( sValue.top(), dTop ) | ||
| 380 | ); | ||
| 381 | break; | ||
| 382 | |||
| 383 | case symOr: | ||
| 384 | sValue.top() = static_cast<prec>( | ||
| 385 | static_cast<bin>(sValue.top()) | | ||
| 386 | static_cast<bin>(dTop) | ||
| 387 | ); | ||
| 388 | break; | ||
| 389 | |||
| 390 | case symAnd: | ||
| 391 | sValue.top() = static_cast<prec>( | ||
| 392 | static_cast<bin>(sValue.top()) & | ||
| 393 | static_cast<bin>(dTop) | ||
| 394 | ); | ||
| 395 | break; | ||
| 396 | |||
| 397 | case symXor: | ||
| 398 | sValue.top() = static_cast<prec>( | ||
| 399 | static_cast<bin>(sValue.top()) ^ | ||
| 400 | static_cast<bin>(dTop) | ||
| 401 | ); | ||
| 402 | break; | ||
| 403 | |||
| 404 | case symFunction: | ||
| 405 | sValue.push( (*hFunc.get( sFunc.pop() ))( dTop ) ); | ||
| 406 | break; | ||
| 407 | |||
| 408 | case symNegate: | ||
| 409 | sValue.push( -dTop ); | ||
| 410 | break; | ||
| 411 | |||
| 412 | case symNot: | ||
| 413 | sValue.push( static_cast<prec>( | ||
| 414 | ~static_cast<bin>(dTop) | ||
| 415 | ) ); | ||
| 416 | break; | ||
| 417 | } | ||
| 418 | } | ||
| 419 | |||
| 420 | if( bCloseParen == true ) | ||
| 421 | { | ||
| 422 | throw FormulaException( | ||
| 423 | "Close-paren found without matching open-paren." | ||
| 424 | ); | ||
| 425 | } | ||
| 426 | } | ||
| 427 | }; | ||
| 428 | } | ||
| 429 | |||
| 430 | #endif | ||
| diff --git a/src/stable/hash.cpp b/src/stable/hash.cpp new file mode 100644 index 0000000..59572ec --- /dev/null +++ b/src/stable/hash.cpp | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/hash.h" | ||
| 9 | |||
| 10 | namespace Bu { subExceptionDef( HashException ) } | ||
| 11 | |||
| 12 | template<> | ||
| 13 | uint32_t Bu::__calcHashCode<const char *>( const char * const &k ) | ||
| 14 | { | ||
| 15 | if (k == NULL) | ||
| 16 | { | ||
| 17 | return 0; | ||
| 18 | } | ||
| 19 | |||
| 20 | unsigned long int nPos = 0; | ||
| 21 | for( const char *s = k; *s; s++ ) | ||
| 22 | { | ||
| 23 | nPos = *s + (nPos << 6) + (nPos << 16) - nPos; | ||
| 24 | } | ||
| 25 | |||
| 26 | return nPos; | ||
| 27 | } | ||
| 28 | |||
| 29 | template<> bool Bu::__cmpHashKeys<const char *>( const char * const &a, const char * const &b ) | ||
| 30 | { | ||
| 31 | if( a == b ) | ||
| 32 | return true; | ||
| 33 | |||
| 34 | for(int j=0; a[j] == b[j]; j++ ) | ||
| 35 | if( a[j] == '\0' ) | ||
| 36 | return true; | ||
| 37 | |||
| 38 | return false; | ||
| 39 | } | ||
| 40 | |||
| 41 | template<> | ||
| 42 | uint32_t Bu::__calcHashCode<char *>( char * const &k ) | ||
| 43 | { | ||
| 44 | if (k == NULL) | ||
| 45 | { | ||
| 46 | return 0; | ||
| 47 | } | ||
| 48 | |||
| 49 | unsigned long int nPos = 0; | ||
| 50 | for( const char *s = k; *s; s++ ) | ||
| 51 | { | ||
| 52 | nPos = *s + (nPos << 6) + (nPos << 16) - nPos; | ||
| 53 | } | ||
| 54 | |||
| 55 | return nPos; | ||
| 56 | } | ||
| 57 | |||
| 58 | template<> bool Bu::__cmpHashKeys<char *>( char * const &a, char * const &b ) | ||
| 59 | { | ||
| 60 | if( a == b ) | ||
| 61 | return true; | ||
| 62 | |||
| 63 | for(int j=0; a[j] == b[j]; j++ ) | ||
| 64 | if( a[j] == '\0' ) | ||
| 65 | return true; | ||
| 66 | |||
| 67 | return false; | ||
| 68 | } | ||
| 69 | |||
| diff --git a/src/stable/hash.h b/src/stable/hash.h new file mode 100644 index 0000000..71aec73 --- /dev/null +++ b/src/stable/hash.h | |||
| @@ -0,0 +1,1306 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_HASH_H | ||
| 9 | #define BU_HASH_H | ||
| 10 | |||
| 11 | #include <memory> | ||
| 12 | #include "bu/exceptionbase.h" | ||
| 13 | #include "bu/list.h" | ||
| 14 | #include "bu/util.h" | ||
| 15 | #include "bu/archivebase.h" | ||
| 16 | #include "bu/sharedcore.h" | ||
| 17 | |||
| 18 | namespace Bu | ||
| 19 | { | ||
| 20 | subExceptionDecl( HashException ) | ||
| 21 | |||
| 22 | enum eHashException | ||
| 23 | { | ||
| 24 | excodeNotFilled | ||
| 25 | }; | ||
| 26 | |||
| 27 | template<typename T> | ||
| 28 | uint32_t __calcHashCode( const T &k ); | ||
| 29 | |||
| 30 | template<typename T> | ||
| 31 | bool __cmpHashKeys( const T &a, const T &b ); | ||
| 32 | |||
| 33 | /** | ||
| 34 | * Default functor used to compute the size of hash tables. This version | ||
| 35 | * effectively doubles the size of the table when space is low, ensuring | ||
| 36 | * that you always wind up with an odd number for the table size. A | ||
| 37 | * better but slower option is to always find the next prime number that's | ||
| 38 | * above double your current table size, but that has the potential to be | ||
| 39 | * slower. | ||
| 40 | */ | ||
| 41 | struct __calcNextTSize_fast | ||
| 42 | { | ||
| 43 | uint32_t operator()( uint32_t nCapacity, uint32_t nFilled, | ||
| 44 | uint32_t nDeleted ) const | ||
| 45 | { | ||
| 46 | // This frist case will allow hashtables that are mostly deleted | ||
| 47 | // items to reset to small allocations | ||
| 48 | if( nFilled-nDeleted <= nCapacity/4 ) | ||
| 49 | { | ||
| 50 | nCapacity = 11; | ||
| 51 | while( nCapacity < nFilled*5/4 ) | ||
| 52 | nCapacity = nCapacity*2+1; | ||
| 53 | return nCapacity; | ||
| 54 | } | ||
| 55 | // This will hopefully prevent hash tables from growing needlessly | ||
| 56 | if( nFilled-nDeleted <= nCapacity/2 ) | ||
| 57 | return nCapacity; | ||
| 58 | // Otherwise, just increase the capacity | ||
| 59 | return nCapacity*2+1; | ||
| 60 | } | ||
| 61 | }; | ||
| 62 | |||
| 63 | template<typename totype> | ||
| 64 | int bitsTo( int iCount ) | ||
| 65 | { | ||
| 66 | return ( (iCount/(sizeof(totype)*8)) | ||
| 67 | + (iCount%(sizeof(totype)*8)>0 ? 1 : 0)); | ||
| 68 | } | ||
| 69 | |||
| 70 | template<typename key, typename value, typename sizecalc, typename keyalloc, | ||
| 71 | typename valuealloc, typename challoc> | ||
| 72 | class Hash; | ||
| 73 | |||
| 74 | /** @cond DEVEL */ | ||
| 75 | template<typename key, typename value, typename sizecalc, typename keyalloc, | ||
| 76 | typename valuealloc, typename challoc > | ||
| 77 | class HashCore | ||
| 78 | { | ||
| 79 | friend class Hash<key, value, sizecalc, keyalloc, valuealloc, challoc>; | ||
| 80 | friend class SharedCore< | ||
| 81 | Hash<key, value, sizecalc, keyalloc, valuealloc, challoc>, | ||
| 82 | HashCore<key, value, sizecalc, keyalloc, valuealloc, challoc> | ||
| 83 | >; | ||
| 84 | private: | ||
| 85 | HashCore() : | ||
| 86 | nCapacity( 0 ), | ||
| 87 | nFilled( 0 ), | ||
| 88 | nDeleted( 0 ), | ||
| 89 | bFilled( NULL ), | ||
| 90 | bDeleted( NULL ), | ||
| 91 | aKeys( NULL ), | ||
| 92 | aValues( NULL ), | ||
| 93 | aHashCodes( NULL ) | ||
| 94 | { | ||
| 95 | } | ||
| 96 | |||
| 97 | virtual ~HashCore() | ||
| 98 | { | ||
| 99 | clear(); | ||
| 100 | } | ||
| 101 | |||
| 102 | void init() | ||
| 103 | { | ||
| 104 | if( nCapacity > 0 ) | ||
| 105 | return; | ||
| 106 | |||
| 107 | nCapacity = 11; | ||
| 108 | nKeysSize = bitsTo<uint32_t>( nCapacity ); | ||
| 109 | bFilled = ca.allocate( nKeysSize ); | ||
| 110 | bDeleted = ca.allocate( nKeysSize ); | ||
| 111 | clearBits(); | ||
| 112 | |||
| 113 | aHashCodes = ca.allocate( nCapacity ); | ||
| 114 | aKeys = ka.allocate( nCapacity ); | ||
| 115 | aValues = va.allocate( nCapacity ); | ||
| 116 | } | ||
| 117 | |||
| 118 | void clearBits() | ||
| 119 | { | ||
| 120 | if( nCapacity == 0 ) | ||
| 121 | return; | ||
| 122 | |||
| 123 | for( uint32_t j = 0; j < nKeysSize; j++ ) | ||
| 124 | { | ||
| 125 | bFilled[j] = bDeleted[j] = 0; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | void fill( uint32_t loc, const key &k, const value &v, uint32_t hash ) | ||
| 130 | { | ||
| 131 | init(); | ||
| 132 | |||
| 133 | bFilled[loc/32] |= (1<<(loc%32)); | ||
| 134 | va.construct( &aValues[loc], v ); | ||
| 135 | ka.construct( &aKeys[loc], k ); | ||
| 136 | aHashCodes[loc] = hash; | ||
| 137 | nFilled++; | ||
| 138 | //printf("Filled: %d, Deleted: %d, Capacity: %d\n", | ||
| 139 | // nFilled, nDeleted, nCapacity ); | ||
| 140 | } | ||
| 141 | |||
| 142 | void _erase( uint32_t loc ) | ||
| 143 | { | ||
| 144 | if( nCapacity == 0 ) | ||
| 145 | return; | ||
| 146 | |||
| 147 | bDeleted[loc/32] |= (1<<(loc%32)); | ||
| 148 | va.destroy( &aValues[loc] ); | ||
| 149 | ka.destroy( &aKeys[loc] ); | ||
| 150 | nDeleted++; | ||
| 151 | //printf("Filled: %d, Deleted: %d, Capacity: %d\n", | ||
| 152 | // nFilled, nDeleted, nCapacity ); | ||
| 153 | } | ||
| 154 | |||
| 155 | key &getKeyAtPos( uint32_t nPos ) | ||
| 156 | { | ||
| 157 | if( nPos >= nCapacity ) | ||
| 158 | throw HashException("Referenced position invalid."); | ||
| 159 | return aKeys[nPos]; | ||
| 160 | } | ||
| 161 | |||
| 162 | const key &getKeyAtPos( uint32_t nPos ) const | ||
| 163 | { | ||
| 164 | if( nPos >= nCapacity ) | ||
| 165 | throw HashException("Referenced position invalid."); | ||
| 166 | return aKeys[nPos]; | ||
| 167 | } | ||
| 168 | |||
| 169 | value &getValueAtPos( uint32_t nPos ) | ||
| 170 | { | ||
| 171 | if( nPos >= nCapacity ) | ||
| 172 | throw HashException("Referenced position invalid."); | ||
| 173 | return aValues[nPos]; | ||
| 174 | } | ||
| 175 | |||
| 176 | const value &getValueAtPos( uint32_t nPos ) const | ||
| 177 | { | ||
| 178 | if( nPos >= nCapacity ) | ||
| 179 | throw HashException("Referenced position invalid."); | ||
| 180 | return aValues[nPos]; | ||
| 181 | } | ||
| 182 | |||
| 183 | uint32_t getFirstPos( bool &bFinished ) const | ||
| 184 | { | ||
| 185 | for( uint32_t j = 0; j < nCapacity; j++ ) | ||
| 186 | { | ||
| 187 | if( isFilled( j ) ) | ||
| 188 | if( !isDeleted( j ) ) | ||
| 189 | return j; | ||
| 190 | } | ||
| 191 | |||
| 192 | bFinished = true; | ||
| 193 | return 0; | ||
| 194 | } | ||
| 195 | |||
| 196 | uint32_t getNextPos( uint32_t nPos, bool &bFinished ) const | ||
| 197 | { | ||
| 198 | for( uint32_t j = nPos+1; j < nCapacity; j++ ) | ||
| 199 | { | ||
| 200 | if( isFilled( j ) ) | ||
| 201 | if( !isDeleted( j ) ) | ||
| 202 | return j; | ||
| 203 | } | ||
| 204 | |||
| 205 | bFinished = true; | ||
| 206 | return 0; | ||
| 207 | } | ||
| 208 | |||
| 209 | uint32_t probe( uint32_t hash, const key &k, bool &bFill, bool rehash=true ) | ||
| 210 | { | ||
| 211 | init(); | ||
| 212 | |||
| 213 | uint32_t nCur = hash%nCapacity; | ||
| 214 | |||
| 215 | // First we scan to see if the key is already there, abort if we | ||
| 216 | // run out of probing room, or we find a non-filled entry | ||
| 217 | int8_t j; | ||
| 218 | for( j = 0; | ||
| 219 | isFilled( nCur ) && j < 32; | ||
| 220 | nCur = (nCur + (1<<j))%nCapacity, j++ | ||
| 221 | ) | ||
| 222 | { | ||
| 223 | // Is this the same hash code we were looking for? | ||
| 224 | if( hash == aHashCodes[nCur] ) | ||
| 225 | { | ||
| 226 | // Skip over deleted entries. Deleted entries are also filled, | ||
| 227 | // so we only have to do this check here. | ||
| 228 | if( isDeleted( nCur ) ) | ||
| 229 | continue; | ||
| 230 | |||
| 231 | // Is it really the same key? (for safety) | ||
| 232 | if( __cmpHashKeys( aKeys[nCur], k ) == true ) | ||
| 233 | { | ||
| 234 | bFill = true; | ||
| 235 | return nCur; | ||
| 236 | } | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | // This is our insurance, if the table is full, then go ahead and | ||
| 241 | // rehash, then try again. | ||
| 242 | if( (isFilled( nCur ) || j == 32) && rehash == true ) | ||
| 243 | { | ||
| 244 | reHash( szCalc( nCapacity, nFilled, nDeleted ) ); | ||
| 245 | |||
| 246 | // This is potentially dangerous, and could cause an infinite loop. | ||
| 247 | // Be careful writing probe, eh? | ||
| 248 | return probe( hash, k, bFill ); | ||
| 249 | } | ||
| 250 | |||
| 251 | bFill = false; | ||
| 252 | return nCur; | ||
| 253 | } | ||
| 254 | |||
| 255 | uint32_t probe( uint32_t hash, key k, bool &bFill ) const | ||
| 256 | { | ||
| 257 | if( nCapacity == 0 ) | ||
| 258 | throw Bu::ExceptionBase("Probe in empty hash table."); | ||
| 259 | |||
| 260 | uint32_t nCur = hash%nCapacity; | ||
| 261 | |||
| 262 | // First we scan to see if the key is already there, abort if we | ||
| 263 | // run out of probing room, or we find a non-filled entry | ||
| 264 | for( int8_t j = 0; | ||
| 265 | isFilled( nCur ) && j < 32; | ||
| 266 | nCur = (nCur + (1<<j))%nCapacity, j++ | ||
| 267 | ) | ||
| 268 | { | ||
| 269 | // Is this the same hash code we were looking for? | ||
| 270 | if( hash == aHashCodes[nCur] ) | ||
| 271 | { | ||
| 272 | // Skip over deleted entries. Deleted entries are also filled, | ||
| 273 | // so we only have to do this check here. | ||
| 274 | if( isDeleted( nCur ) ) | ||
| 275 | continue; | ||
| 276 | |||
| 277 | // Is it really the same key? (for safety) | ||
| 278 | if( __cmpHashKeys( aKeys[nCur], k ) == true ) | ||
| 279 | { | ||
| 280 | bFill = true; | ||
| 281 | return nCur; | ||
| 282 | } | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | bFill = false; | ||
| 287 | return nCur; | ||
| 288 | } | ||
| 289 | |||
| 290 | void insert( const key &k, const value &v ) | ||
| 291 | { | ||
| 292 | uint32_t hash = __calcHashCode( k ); | ||
| 293 | bool bFill; | ||
| 294 | uint32_t nPos = probe( hash, k, bFill ); | ||
| 295 | |||
| 296 | if( bFill ) | ||
| 297 | { | ||
| 298 | va.destroy( &aValues[nPos] ); | ||
| 299 | va.construct( &aValues[nPos], v ); | ||
| 300 | } | ||
| 301 | else | ||
| 302 | { | ||
| 303 | fill( nPos, k, v, hash ); | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | void reHash( uint32_t nNewSize ) | ||
| 308 | { | ||
| 309 | //printf("---REHASH---"); | ||
| 310 | //printf("Filled: %d, Deleted: %d, Capacity: %d\n", | ||
| 311 | // nFilled, nDeleted, nCapacity ); | ||
| 312 | |||
| 313 | // Save all the old data | ||
| 314 | uint32_t nOldCapacity = nCapacity; | ||
| 315 | uint32_t *bOldFilled = bFilled; | ||
| 316 | uint32_t *aOldHashCodes = aHashCodes; | ||
| 317 | uint32_t nOldKeysSize = nKeysSize; | ||
| 318 | uint32_t *bOldDeleted = bDeleted; | ||
| 319 | value *aOldValues = aValues; | ||
| 320 | key *aOldKeys = aKeys; | ||
| 321 | |||
| 322 | // Calculate new sizes | ||
| 323 | nCapacity = nNewSize; | ||
| 324 | nKeysSize = bitsTo<uint32_t>( nCapacity ); | ||
| 325 | |||
| 326 | // Allocate new memory + prep | ||
| 327 | bFilled = ca.allocate( nKeysSize ); | ||
| 328 | bDeleted = ca.allocate( nKeysSize ); | ||
| 329 | clearBits(); | ||
| 330 | |||
| 331 | aHashCodes = ca.allocate( nCapacity ); | ||
| 332 | aKeys = ka.allocate( nCapacity ); | ||
| 333 | aValues = va.allocate( nCapacity ); | ||
| 334 | |||
| 335 | nDeleted = nFilled = 0; | ||
| 336 | |||
| 337 | // Re-insert all of the old data (except deleted items) | ||
| 338 | for( uint32_t j = 0; j < nOldCapacity; j++ ) | ||
| 339 | { | ||
| 340 | if( (bOldFilled[j/32]&(1<<(j%32)))!=0 && | ||
| 341 | (bOldDeleted[j/32]&(1<<(j%32)))==0 ) | ||
| 342 | { | ||
| 343 | insert( aOldKeys[j], aOldValues[j] ); | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | // Delete all of the old data | ||
| 348 | for( uint32_t j = 0; j < nOldCapacity; j++ ) | ||
| 349 | { | ||
| 350 | if( (bOldFilled[j/32]&(1<<(j%32)))!=0 && | ||
| 351 | (bOldDeleted[j/32]&(1<<(j%32)))==0 ) | ||
| 352 | { | ||
| 353 | va.destroy( &aOldValues[j] ); | ||
| 354 | ka.destroy( &aOldKeys[j] ); | ||
| 355 | } | ||
| 356 | } | ||
| 357 | va.deallocate( aOldValues, nOldCapacity ); | ||
| 358 | ka.deallocate( aOldKeys, nOldCapacity ); | ||
| 359 | ca.deallocate( bOldFilled, nOldKeysSize ); | ||
| 360 | ca.deallocate( bOldDeleted, nOldKeysSize ); | ||
| 361 | ca.deallocate( aOldHashCodes, nOldCapacity ); | ||
| 362 | } | ||
| 363 | |||
| 364 | bool isFilled( uint32_t loc ) const | ||
| 365 | { | ||
| 366 | if( loc >= nCapacity ) | ||
| 367 | throw HashException("Referenced position invalid."); | ||
| 368 | return (bFilled[loc/32]&(1<<(loc%32)))!=0; | ||
| 369 | } | ||
| 370 | |||
| 371 | bool isDeleted( uint32_t loc ) const | ||
| 372 | { | ||
| 373 | if( loc >= nCapacity ) | ||
| 374 | throw HashException("Referenced position invalid."); | ||
| 375 | return (bDeleted[loc/32]&(1<<(loc%32)))!=0; | ||
| 376 | } | ||
| 377 | |||
| 378 | void clear() | ||
| 379 | { | ||
| 380 | for( uint32_t j = 0; j < nCapacity; j++ ) | ||
| 381 | { | ||
| 382 | if( isFilled( j ) ) | ||
| 383 | if( !isDeleted( j ) ) | ||
| 384 | { | ||
| 385 | va.destroy( &aValues[j] ); | ||
| 386 | ka.destroy( &aKeys[j] ); | ||
| 387 | } | ||
| 388 | } | ||
| 389 | va.deallocate( aValues, nCapacity ); | ||
| 390 | ka.deallocate( aKeys, nCapacity ); | ||
| 391 | ca.deallocate( bFilled, nKeysSize ); | ||
| 392 | ca.deallocate( bDeleted, nKeysSize ); | ||
| 393 | ca.deallocate( aHashCodes, nCapacity ); | ||
| 394 | |||
| 395 | bFilled = NULL; | ||
| 396 | bDeleted = NULL; | ||
| 397 | aKeys = NULL; | ||
| 398 | aValues = NULL; | ||
| 399 | aHashCodes = NULL; | ||
| 400 | |||
| 401 | nCapacity = 0; | ||
| 402 | nFilled = 0; | ||
| 403 | nDeleted = 0; | ||
| 404 | } | ||
| 405 | |||
| 406 | uint32_t nCapacity; | ||
| 407 | uint32_t nFilled; | ||
| 408 | uint32_t nDeleted; | ||
| 409 | uint32_t *bFilled; | ||
| 410 | uint32_t *bDeleted; | ||
| 411 | uint32_t nKeysSize; | ||
| 412 | key *aKeys; | ||
| 413 | value *aValues; | ||
| 414 | uint32_t *aHashCodes; | ||
| 415 | valuealloc va; | ||
| 416 | keyalloc ka; | ||
| 417 | challoc ca; | ||
| 418 | sizecalc szCalc; | ||
| 419 | }; | ||
| 420 | /** @endcond */ | ||
| 421 | |||
| 422 | /** | ||
| 423 | * Libbu++ Template Hash Table. This is your average hash table, that uses | ||
| 424 | * template functions in order to do fast, efficient, generalized hashing. | ||
| 425 | * It's pretty easy to use, and works well with all other libbu++ types so | ||
| 426 | * far. | ||
| 427 | * | ||
| 428 | * In order to use it, I recommend the following for all basic usage: | ||
| 429 | *@code | ||
| 430 | // Define a Hash typedef with strings as keys and ints as values. | ||
| 431 | typedef Bu::Hash<Bu::String, int> StrIntHash; | ||
| 432 | |||
| 433 | // Create one | ||
| 434 | StrIntHash hInts; | ||
| 435 | |||
| 436 | // Insert some integers | ||
| 437 | hInts["one"] = 1; | ||
| 438 | hInts["forty-two"] = 42; | ||
| 439 | hInts.insert("forty two", 42 ); | ||
| 440 | |||
| 441 | // Get values out of the hash, the last two options are the most explicit, | ||
| 442 | // and must be used if the hash's value type does not match what you're | ||
| 443 | // comparing to exactly. | ||
| 444 | if( hInts["one"] == 1 ) doSomething(); | ||
| 445 | if( hInts["forty-two"].value() == 42 ) doSomething(); | ||
| 446 | if( hInts.get("forty two") == 42 ) doSomething(); | ||
| 447 | |||
| 448 | // Iterate through the Hash | ||
| 449 | for( StrIntHash::iterator i = hInts.begin(); i != hInts.end(); i++ ) | ||
| 450 | { | ||
| 451 | // i.getValue() works too | ||
| 452 | print("'%s' = %d\n", i.getKey().getStr(), (*i) ); | ||
| 453 | } | ||
| 454 | |||
| 455 | @endcode | ||
| 456 | *@param key (typename) The datatype of the hashtable keys | ||
| 457 | *@param value (typename) The datatype of the hashtable data | ||
| 458 | *@param sizecalc (typename) Functor to compute new table size on rehash | ||
| 459 | *@param keyalloc (typename) Memory allocator for hashtable keys | ||
| 460 | *@param valuealloc (typename) Memory allocator for hashtable values | ||
| 461 | *@param challoc (typename) Byte allocator for bitflags | ||
| 462 | *@ingroup Containers | ||
| 463 | */ | ||
| 464 | template<typename key, typename value, | ||
| 465 | typename sizecalc = __calcNextTSize_fast, | ||
| 466 | typename keyalloc = std::allocator<key>, | ||
| 467 | typename valuealloc = std::allocator<value>, | ||
| 468 | typename challoc = std::allocator<uint32_t> | ||
| 469 | > | ||
| 470 | class Hash : public SharedCore< | ||
| 471 | Hash<key, value, sizecalc, keyalloc, valuealloc, challoc>, | ||
| 472 | HashCore<key, value, sizecalc, keyalloc, valuealloc, challoc> | ||
| 473 | > | ||
| 474 | { | ||
| 475 | private: | ||
| 476 | typedef class HashCore<key, value, sizecalc, keyalloc, valuealloc, challoc> Core; | ||
| 477 | typedef class Hash<key, value, sizecalc, keyalloc, valuealloc, challoc> MyType; | ||
| 478 | protected: | ||
| 479 | using SharedCore<MyType, Core>::core; | ||
| 480 | using SharedCore<MyType, Core>::_hardCopy; | ||
| 481 | using SharedCore<MyType, Core>::_resetCore; | ||
| 482 | using SharedCore<MyType, Core>::_allocateCore; | ||
| 483 | |||
| 484 | public: | ||
| 485 | Hash() | ||
| 486 | { | ||
| 487 | } | ||
| 488 | |||
| 489 | Hash( const MyType &src ) : | ||
| 490 | SharedCore<MyType, Core >( src ) | ||
| 491 | { | ||
| 492 | } | ||
| 493 | |||
| 494 | virtual ~Hash() | ||
| 495 | { | ||
| 496 | } | ||
| 497 | |||
| 498 | /** | ||
| 499 | * Get the current hash table capacity. (Changes at re-hash) | ||
| 500 | *@returns (uint32_t) The current capacity. | ||
| 501 | */ | ||
| 502 | uint32_t getCapacity() const | ||
| 503 | { | ||
| 504 | return core->nCapacity; | ||
| 505 | } | ||
| 506 | |||
| 507 | /** | ||
| 508 | * Get the number of hash locations spoken for. (Including | ||
| 509 | * not-yet-cleaned-up deleted items.) | ||
| 510 | *@returns (uint32_t) The current fill state. | ||
| 511 | */ | ||
| 512 | uint32_t getFill() const | ||
| 513 | { | ||
| 514 | return core->nFilled; | ||
| 515 | } | ||
| 516 | |||
| 517 | /** | ||
| 518 | * Get the number of items stored in the hash table. | ||
| 519 | *@returns (uint32_t) The number of items stored in the hash table. | ||
| 520 | */ | ||
| 521 | uint32_t getSize() const | ||
| 522 | { | ||
| 523 | return core->nFilled-core->nDeleted; | ||
| 524 | } | ||
| 525 | |||
| 526 | bool isEmpty() const | ||
| 527 | { | ||
| 528 | return (core->nFilled-core->nDeleted) == 0; | ||
| 529 | } | ||
| 530 | |||
| 531 | /** | ||
| 532 | * Get the number of items which have been deleted, but not yet | ||
| 533 | * cleaned up. | ||
| 534 | *@returns (uint32_t) The number of deleted items. | ||
| 535 | */ | ||
| 536 | uint32_t getDeleted() const | ||
| 537 | { | ||
| 538 | return core->nDeleted; | ||
| 539 | } | ||
| 540 | |||
| 541 | struct HashProxy | ||
| 542 | { | ||
| 543 | friend class Hash<key, value, sizecalc, keyalloc, valuealloc, challoc>; | ||
| 544 | private: | ||
| 545 | HashProxy( MyType &h, const key *k, uint32_t nPos, uint32_t hash ) : | ||
| 546 | hsh( h ), | ||
| 547 | pKey( k ), | ||
| 548 | nPos( nPos ), | ||
| 549 | hash( hash ), | ||
| 550 | bFilled( false ) | ||
| 551 | { | ||
| 552 | } | ||
| 553 | |||
| 554 | HashProxy( MyType &h, uint32_t nPos, value *pValue ) : | ||
| 555 | hsh( h ), | ||
| 556 | nPos( nPos ), | ||
| 557 | pValue( pValue ), | ||
| 558 | bFilled( true ) | ||
| 559 | { | ||
| 560 | } | ||
| 561 | |||
| 562 | MyType &hsh; | ||
| 563 | const key *pKey; | ||
| 564 | uint32_t nPos; | ||
| 565 | value *pValue; | ||
| 566 | uint32_t hash; | ||
| 567 | bool bFilled; | ||
| 568 | |||
| 569 | public: | ||
| 570 | /** | ||
| 571 | * Cast operator for HashProxy. | ||
| 572 | *@returns (value_type &) The value the HashProxy is pointing to. | ||
| 573 | */ | ||
| 574 | operator value &() | ||
| 575 | { | ||
| 576 | if( bFilled == false ) | ||
| 577 | throw HashException( | ||
| 578 | excodeNotFilled, | ||
| 579 | "No data assosiated with that key." | ||
| 580 | ); | ||
| 581 | return *pValue; | ||
| 582 | } | ||
| 583 | |||
| 584 | /** | ||
| 585 | * Direct function for retrieving a value out of the HashProxy. | ||
| 586 | *@returns (value_type &) The value pointed to by this HashProxy. | ||
| 587 | */ | ||
| 588 | value &getValue() | ||
| 589 | { | ||
| 590 | if( bFilled == false ) | ||
| 591 | throw HashException( | ||
| 592 | excodeNotFilled, | ||
| 593 | "No data assosiated with that key." | ||
| 594 | ); | ||
| 595 | return *pValue; | ||
| 596 | } | ||
| 597 | |||
| 598 | /** | ||
| 599 | * Whether this HashProxy points to something real or not. | ||
| 600 | */ | ||
| 601 | bool isFilled() | ||
| 602 | { | ||
| 603 | return bFilled; | ||
| 604 | } | ||
| 605 | |||
| 606 | /** | ||
| 607 | * Erase the data pointed to by this HashProxy. | ||
| 608 | */ | ||
| 609 | void erase() | ||
| 610 | { | ||
| 611 | if( bFilled ) | ||
| 612 | { | ||
| 613 | hsh.core->_erase( nPos ); | ||
| 614 | } | ||
| 615 | } | ||
| 616 | |||
| 617 | /** | ||
| 618 | * Assign data to this point in the hash table. | ||
| 619 | *@param nval (value_type) the data to assign. | ||
| 620 | */ | ||
| 621 | value operator=( value nval ) | ||
| 622 | { | ||
| 623 | if( bFilled ) | ||
| 624 | { | ||
| 625 | hsh.core->va.destroy( &hsh.core->aValues[nPos] ); | ||
| 626 | hsh.core->va.construct( &hsh.core->aValues[nPos], nval ); | ||
| 627 | } | ||
| 628 | else | ||
| 629 | { | ||
| 630 | hsh.core->fill( nPos, *pKey, nval, hash ); | ||
| 631 | } | ||
| 632 | |||
| 633 | return nval; | ||
| 634 | } | ||
| 635 | |||
| 636 | /** | ||
| 637 | * Pointer extraction operator. Access to members of data pointed to | ||
| 638 | * by HashProxy. | ||
| 639 | *@returns (value_type *) | ||
| 640 | */ | ||
| 641 | value *operator->() | ||
| 642 | { | ||
| 643 | if( bFilled == false ) | ||
| 644 | throw HashException( | ||
| 645 | excodeNotFilled, | ||
| 646 | "No data assosiated with that key." | ||
| 647 | ); | ||
| 648 | return pValue; | ||
| 649 | } | ||
| 650 | }; | ||
| 651 | |||
| 652 | /** | ||
| 653 | * Hash table index operator | ||
| 654 | *@param k (key_type) Key of data to be retrieved. | ||
| 655 | *@returns (HashProxy) Proxy pointing to the data. | ||
| 656 | */ | ||
| 657 | HashProxy operator[]( const key &k ) | ||
| 658 | { | ||
| 659 | _hardCopy(); | ||
| 660 | |||
| 661 | uint32_t hash = __calcHashCode( k ); | ||
| 662 | bool bFill; | ||
| 663 | uint32_t nPos = core->probe( hash, k, bFill ); | ||
| 664 | |||
| 665 | if( bFill ) | ||
| 666 | { | ||
| 667 | return HashProxy( *this, nPos, &core->aValues[nPos] ); | ||
| 668 | } | ||
| 669 | else | ||
| 670 | { | ||
| 671 | return HashProxy( *this, &k, nPos, hash ); | ||
| 672 | } | ||
| 673 | } | ||
| 674 | |||
| 675 | /** | ||
| 676 | * Insert a value (v) under key (k) into the hash table | ||
| 677 | *@param k (key_type) Key to list the value under. | ||
| 678 | *@param v (value_type) Value to store in the hash table. | ||
| 679 | */ | ||
| 680 | void insert( const key &k, const value &v ) | ||
| 681 | { | ||
| 682 | _hardCopy(); | ||
| 683 | |||
| 684 | core->insert( k, v ); | ||
| 685 | } | ||
| 686 | |||
| 687 | /** | ||
| 688 | * Remove a value from the hash table. | ||
| 689 | *@param k (key_type) The data under this key will be erased. | ||
| 690 | */ | ||
| 691 | void erase( const key &k ) | ||
| 692 | { | ||
| 693 | _hardCopy(); | ||
| 694 | |||
| 695 | uint32_t hash = __calcHashCode( k ); | ||
| 696 | bool bFill; | ||
| 697 | uint32_t nPos = core->probe( hash, k, bFill ); | ||
| 698 | |||
| 699 | if( bFill ) | ||
| 700 | { | ||
| 701 | core->_erase( nPos ); | ||
| 702 | } | ||
| 703 | } | ||
| 704 | |||
| 705 | struct iterator; | ||
| 706 | |||
| 707 | /** | ||
| 708 | * Remove a value from the hash pointed to from an iterator. | ||
| 709 | *@param i (iterator &) The data to be erased. | ||
| 710 | */ | ||
| 711 | void erase( struct iterator &i ) | ||
| 712 | { | ||
| 713 | if( this != i.hsh ) | ||
| 714 | throw HashException("This iterator didn't come from this Hash."); | ||
| 715 | |||
| 716 | _hardCopy(); | ||
| 717 | |||
| 718 | if( core->isFilled( i.nPos ) && !core->isDeleted( i.nPos ) ) | ||
| 719 | { | ||
| 720 | core->_erase( i.nPos ); | ||
| 721 | } | ||
| 722 | } | ||
| 723 | |||
| 724 | /** | ||
| 725 | * Remove all data from the hash table. | ||
| 726 | */ | ||
| 727 | virtual void clear() | ||
| 728 | { | ||
| 729 | _resetCore(); | ||
| 730 | } | ||
| 731 | |||
| 732 | /** | ||
| 733 | * Get an item of data from the hash table. | ||
| 734 | *@param k (key_type) Key pointing to the data to be retrieved. | ||
| 735 | *@returns (value_type &) The data pointed to by (k). | ||
| 736 | */ | ||
| 737 | value &get( const key &k ) | ||
| 738 | { | ||
| 739 | _hardCopy(); | ||
| 740 | |||
| 741 | uint32_t hash = __calcHashCode( k ); | ||
| 742 | bool bFill; | ||
| 743 | uint32_t nPos = core->probe( hash, k, bFill, false ); | ||
| 744 | |||
| 745 | if( bFill ) | ||
| 746 | { | ||
| 747 | return core->aValues[nPos]; | ||
| 748 | } | ||
| 749 | else | ||
| 750 | { | ||
| 751 | throw HashException( | ||
| 752 | excodeNotFilled, | ||
| 753 | "No data assosiated with that key." | ||
| 754 | ); | ||
| 755 | } | ||
| 756 | } | ||
| 757 | |||
| 758 | /** | ||
| 759 | * Get a const item of data from the hash table. | ||
| 760 | *@param k (key_type) Key pointing to the data to be retrieved. | ||
| 761 | *@returns (const value_type &) A const version of the data pointed | ||
| 762 | * to by (k). | ||
| 763 | */ | ||
| 764 | const value &get( const key &k ) const | ||
| 765 | { | ||
| 766 | uint32_t hash = __calcHashCode( k ); | ||
| 767 | bool bFill; | ||
| 768 | uint32_t nPos = core->probe( hash, k, bFill ); | ||
| 769 | |||
| 770 | if( bFill ) | ||
| 771 | { | ||
| 772 | return core->aValues[nPos]; | ||
| 773 | } | ||
| 774 | else | ||
| 775 | { | ||
| 776 | throw HashException( | ||
| 777 | excodeNotFilled, | ||
| 778 | "No data assosiated with that key." | ||
| 779 | ); | ||
| 780 | } | ||
| 781 | } | ||
| 782 | |||
| 783 | /** | ||
| 784 | * Does the hash table contain an item under key (k). | ||
| 785 | *@param k (key_type) The key to check. | ||
| 786 | *@returns (bool) Whether there was an item in the hash under key (k). | ||
| 787 | */ | ||
| 788 | bool has( const key &k ) const | ||
| 789 | { | ||
| 790 | bool bFill; | ||
| 791 | core->probe( __calcHashCode( k ), k, bFill ); | ||
| 792 | |||
| 793 | return bFill; | ||
| 794 | } | ||
| 795 | |||
| 796 | /** | ||
| 797 | * Iteration structure for iterating through the hash. | ||
| 798 | */ | ||
| 799 | typedef struct iterator | ||
| 800 | { | ||
| 801 | friend class Hash<key, value, sizecalc, keyalloc, valuealloc, challoc>; | ||
| 802 | private: | ||
| 803 | iterator( MyType *hsh ) : | ||
| 804 | hsh( hsh ), | ||
| 805 | nPos( 0 ), | ||
| 806 | bFinished( false ) | ||
| 807 | { | ||
| 808 | nPos = hsh->core->getFirstPos( bFinished ); | ||
| 809 | } | ||
| 810 | |||
| 811 | iterator( MyType *hsh, bool bDone ) : | ||
| 812 | hsh( hsh ), | ||
| 813 | nPos( 0 ), | ||
| 814 | bFinished( bDone ) | ||
| 815 | { | ||
| 816 | } | ||
| 817 | |||
| 818 | MyType *hsh; | ||
| 819 | uint32_t nPos; | ||
| 820 | bool bFinished; | ||
| 821 | |||
| 822 | public: | ||
| 823 | iterator( const iterator &i ) : | ||
| 824 | hsh( i.hsh ), | ||
| 825 | nPos( i.nPos ), | ||
| 826 | bFinished( i.bFinished ) | ||
| 827 | { | ||
| 828 | } | ||
| 829 | |||
| 830 | iterator() : | ||
| 831 | hsh( NULL ), | ||
| 832 | nPos( NULL ), | ||
| 833 | bFinished( true ) | ||
| 834 | { | ||
| 835 | } | ||
| 836 | |||
| 837 | bool isValid() const | ||
| 838 | { | ||
| 839 | return !bFinished; | ||
| 840 | } | ||
| 841 | |||
| 842 | operator bool() const | ||
| 843 | { | ||
| 844 | return !bFinished; | ||
| 845 | } | ||
| 846 | |||
| 847 | /** | ||
| 848 | * Iterator incrementation operator. Move the iterator forward. | ||
| 849 | */ | ||
| 850 | iterator operator++( int ) | ||
| 851 | { | ||
| 852 | if( bFinished == false ) | ||
| 853 | nPos = hsh->core->getNextPos( nPos, bFinished ); | ||
| 854 | |||
| 855 | return *this; | ||
| 856 | } | ||
| 857 | |||
| 858 | /** | ||
| 859 | * Iterator incrementation operator. Move the iterator forward. | ||
| 860 | */ | ||
| 861 | iterator operator++() | ||
| 862 | { | ||
| 863 | if( bFinished == false ) | ||
| 864 | nPos = hsh->core->getNextPos( nPos, bFinished ); | ||
| 865 | |||
| 866 | return *this; | ||
| 867 | } | ||
| 868 | |||
| 869 | /** | ||
| 870 | * Iterator equality comparison operator. Iterators the same? | ||
| 871 | */ | ||
| 872 | bool operator==( const iterator &oth ) const | ||
| 873 | { | ||
| 874 | if( bFinished != oth.bFinished ) | ||
| 875 | return false; | ||
| 876 | if( bFinished == true ) | ||
| 877 | { | ||
| 878 | return true; | ||
| 879 | } | ||
| 880 | else | ||
| 881 | { | ||
| 882 | if( oth.nPos == nPos ) | ||
| 883 | return true; | ||
| 884 | return false; | ||
| 885 | } | ||
| 886 | } | ||
| 887 | |||
| 888 | /** | ||
| 889 | * Iterator not equality comparison operator. Not the same? | ||
| 890 | */ | ||
| 891 | bool operator!=( const iterator &oth ) const | ||
| 892 | { | ||
| 893 | return !(*this == oth ); | ||
| 894 | } | ||
| 895 | |||
| 896 | /** | ||
| 897 | * Iterator assignment operator. | ||
| 898 | */ | ||
| 899 | iterator operator=( const iterator &oth ) | ||
| 900 | { | ||
| 901 | hsh = oth.hsh; | ||
| 902 | nPos = oth.nPos; | ||
| 903 | bFinished = oth.bFinished; | ||
| 904 | return *this; | ||
| 905 | } | ||
| 906 | |||
| 907 | /** | ||
| 908 | * Iterator dereference operator... err.. get the value | ||
| 909 | *@returns (value_type &) The value behind this iterator. | ||
| 910 | */ | ||
| 911 | value &operator *() | ||
| 912 | { | ||
| 913 | hsh->_hardCopy(); | ||
| 914 | return hsh->core->getValueAtPos( nPos ); | ||
| 915 | } | ||
| 916 | |||
| 917 | const value &operator *() const | ||
| 918 | { | ||
| 919 | return hsh->core->getValueAtPos( nPos ); | ||
| 920 | } | ||
| 921 | |||
| 922 | /** | ||
| 923 | * Get the key behind this iterator. | ||
| 924 | *@returns (key_type &) The key behind this iterator. | ||
| 925 | */ | ||
| 926 | const key &getKey() const | ||
| 927 | { | ||
| 928 | return hsh->core->getKeyAtPos( nPos ); | ||
| 929 | } | ||
| 930 | |||
| 931 | /** | ||
| 932 | * Get the value behind this iterator. | ||
| 933 | *@returns (value_type &) The value behind this iterator. | ||
| 934 | */ | ||
| 935 | value &getValue() | ||
| 936 | { | ||
| 937 | hsh->_hardCopy(); | ||
| 938 | return hsh->core->getValueAtPos( nPos ); | ||
| 939 | } | ||
| 940 | |||
| 941 | /** | ||
| 942 | * Get the value behind this iterator. | ||
| 943 | *@returns (value_type &) The value behind this iterator. | ||
| 944 | */ | ||
| 945 | const value &getValue() const | ||
| 946 | { | ||
| 947 | return hsh->core->getValueAtPos( nPos ); | ||
| 948 | } | ||
| 949 | } iterator; | ||
| 950 | |||
| 951 | /** | ||
| 952 | * Iteration structure for iterating through the hash (const). | ||
| 953 | */ | ||
| 954 | typedef struct const_iterator | ||
| 955 | { | ||
| 956 | friend class Hash<key, value, sizecalc, keyalloc, valuealloc, challoc>; | ||
| 957 | private: | ||
| 958 | const_iterator( const MyType *hsh ) : | ||
| 959 | hsh( hsh ), | ||
| 960 | nPos( 0 ), | ||
| 961 | bFinished( false ) | ||
| 962 | { | ||
| 963 | nPos = hsh->core->getFirstPos( bFinished ); | ||
| 964 | } | ||
| 965 | |||
| 966 | const_iterator( const MyType *hsh, bool bDone ) : | ||
| 967 | hsh( hsh ), | ||
| 968 | nPos( 0 ), | ||
| 969 | bFinished( bDone ) | ||
| 970 | { | ||
| 971 | } | ||
| 972 | |||
| 973 | const MyType *hsh; | ||
| 974 | uint32_t nPos; | ||
| 975 | bool bFinished; | ||
| 976 | |||
| 977 | public: | ||
| 978 | const_iterator() : | ||
| 979 | hsh( NULL ), | ||
| 980 | nPos( 0 ), | ||
| 981 | bFinished( true ) | ||
| 982 | { | ||
| 983 | } | ||
| 984 | |||
| 985 | const_iterator( const const_iterator &src ) : | ||
| 986 | hsh( src.hsh ), | ||
| 987 | nPos( src.nPos ), | ||
| 988 | bFinished( src.bFinished ) | ||
| 989 | { | ||
| 990 | } | ||
| 991 | |||
| 992 | const_iterator( const iterator &src ) : | ||
| 993 | hsh( src.hsh ), | ||
| 994 | nPos( src.nPos ), | ||
| 995 | bFinished( src.bFinished ) | ||
| 996 | { | ||
| 997 | } | ||
| 998 | |||
| 999 | bool isValid() const | ||
| 1000 | { | ||
| 1001 | return !bFinished; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | operator bool() const | ||
| 1005 | { | ||
| 1006 | return !bFinished; | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | /** | ||
| 1010 | * Iterator incrementation operator. Move the iterator forward. | ||
| 1011 | */ | ||
| 1012 | const_iterator operator++( int ) | ||
| 1013 | { | ||
| 1014 | if( bFinished == false ) | ||
| 1015 | nPos = hsh->core->getNextPos( nPos, bFinished ); | ||
| 1016 | |||
| 1017 | return *this; | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | /** | ||
| 1021 | * Iterator incrementation operator. Move the iterator forward. | ||
| 1022 | */ | ||
| 1023 | const_iterator operator++() | ||
| 1024 | { | ||
| 1025 | if( bFinished == false ) | ||
| 1026 | nPos = hsh->core->getNextPos( nPos, bFinished ); | ||
| 1027 | |||
| 1028 | return *this; | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | /** | ||
| 1032 | * Iterator equality comparison operator. Iterators the same? | ||
| 1033 | */ | ||
| 1034 | bool operator==( const const_iterator &oth ) const | ||
| 1035 | { | ||
| 1036 | if( bFinished != oth.bFinished ) | ||
| 1037 | return false; | ||
| 1038 | if( bFinished == true ) | ||
| 1039 | { | ||
| 1040 | return true; | ||
| 1041 | } | ||
| 1042 | else | ||
| 1043 | { | ||
| 1044 | if( oth.nPos == nPos ) | ||
| 1045 | return true; | ||
| 1046 | return false; | ||
| 1047 | } | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | /** | ||
| 1051 | * Iterator not equality comparison operator. Not the same? | ||
| 1052 | */ | ||
| 1053 | bool operator!=( const const_iterator &oth ) const | ||
| 1054 | { | ||
| 1055 | return !(*this == oth ); | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | /** | ||
| 1059 | * Iterator assignment operator. | ||
| 1060 | */ | ||
| 1061 | const_iterator operator=( const const_iterator &oth ) | ||
| 1062 | { | ||
| 1063 | hsh = oth.hsh; | ||
| 1064 | nPos = oth.nPos; | ||
| 1065 | bFinished = oth.bFinished; | ||
| 1066 | return *this; | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | /** | ||
| 1070 | * Iterator dereference operator... err.. get the value | ||
| 1071 | *@returns (value_type &) The value behind this iterator. | ||
| 1072 | */ | ||
| 1073 | const value &operator *() const | ||
| 1074 | { | ||
| 1075 | return hsh->core->getValueAtPos( nPos ); | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | /** | ||
| 1079 | * Get the key behind this iterator. | ||
| 1080 | *@returns (key_type &) The key behind this iterator. | ||
| 1081 | */ | ||
| 1082 | const key &getKey() const | ||
| 1083 | { | ||
| 1084 | return hsh->core->getKeyAtPos( nPos ); | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | /** | ||
| 1088 | * Get the value behind this iterator. | ||
| 1089 | *@returns (value_type &) The value behind this iterator. | ||
| 1090 | */ | ||
| 1091 | const value &getValue() const | ||
| 1092 | { | ||
| 1093 | return hsh->core->getValueAtPos( nPos ); | ||
| 1094 | } | ||
| 1095 | } const_iterator; | ||
| 1096 | |||
| 1097 | /** | ||
| 1098 | * Get an iterator pointing to the first item in the hash table. | ||
| 1099 | *@returns (iterator) An iterator pointing to the first item in the | ||
| 1100 | * hash table. | ||
| 1101 | */ | ||
| 1102 | iterator begin() | ||
| 1103 | { | ||
| 1104 | return iterator( this ); | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | const_iterator begin() const | ||
| 1108 | { | ||
| 1109 | return const_iterator( this ); | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | /** | ||
| 1113 | * Get an iterator pointing to a point just past the last item in the | ||
| 1114 | * hash table. | ||
| 1115 | *@returns (iterator) An iterator pointing to a point just past the | ||
| 1116 | * last item in the hash table. | ||
| 1117 | */ | ||
| 1118 | iterator end() | ||
| 1119 | { | ||
| 1120 | return iterator( this, true ); | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | const_iterator end() const | ||
| 1124 | { | ||
| 1125 | return const_iterator( this, true ); | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | /** | ||
| 1129 | * Get a list of all the keys in the hash table. | ||
| 1130 | *@returns (std::list<key_type>) The list of keys in the hash table. | ||
| 1131 | */ | ||
| 1132 | Bu::List<key> getKeys() const | ||
| 1133 | { | ||
| 1134 | Bu::List<key> lKeys; | ||
| 1135 | |||
| 1136 | for( uint32_t j = 0; j < core->nCapacity; j++ ) | ||
| 1137 | { | ||
| 1138 | if( core->isFilled( j ) ) | ||
| 1139 | { | ||
| 1140 | if( !core->isDeleted( j ) ) | ||
| 1141 | { | ||
| 1142 | lKeys.append( core->aKeys[j] ); | ||
| 1143 | } | ||
| 1144 | } | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | return lKeys; | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | Bu::List<value> getValues() const | ||
| 1151 | { | ||
| 1152 | Bu::List<value> lValues; | ||
| 1153 | |||
| 1154 | for( uint32_t j = 0; j < core->nCapacity; j++ ) | ||
| 1155 | { | ||
| 1156 | if( core->isFilled( j ) ) | ||
| 1157 | { | ||
| 1158 | if( !core->isDeleted( j ) ) | ||
| 1159 | { | ||
| 1160 | lValues.append( core->aValues[j] ); | ||
| 1161 | } | ||
| 1162 | } | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | return lValues; | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | bool operator==( const MyType &rhs ) const | ||
| 1169 | { | ||
| 1170 | if( this == &rhs ) | ||
| 1171 | return true; | ||
| 1172 | if( core == rhs.core ) | ||
| 1173 | return true; | ||
| 1174 | if( core == NULL || rhs.core == NULL ) | ||
| 1175 | return false; | ||
| 1176 | if( getSize() != rhs.getSize() ) | ||
| 1177 | return false; | ||
| 1178 | |||
| 1179 | for( uint32_t j = 0; j < core->nCapacity; j++ ) | ||
| 1180 | { | ||
| 1181 | if( core->isFilled( j ) ) | ||
| 1182 | { | ||
| 1183 | if( !core->isDeleted( j ) ) | ||
| 1184 | { | ||
| 1185 | // Check to see if this key is in the other hash | ||
| 1186 | if( rhs.has( core->aKeys[j] ) ) | ||
| 1187 | { | ||
| 1188 | if( !(core->aValues[j] == rhs.get( core->aKeys[j]) ) ) | ||
| 1189 | { | ||
| 1190 | return false; | ||
| 1191 | } | ||
| 1192 | } | ||
| 1193 | else | ||
| 1194 | { | ||
| 1195 | return false; | ||
| 1196 | } | ||
| 1197 | } | ||
| 1198 | } | ||
| 1199 | } | ||
| 1200 | |||
| 1201 | return true; | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | bool operator!=( const MyType &rhs ) const | ||
| 1205 | { | ||
| 1206 | return !(*this == rhs); | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | protected: | ||
| 1210 | virtual Core *_copyCore( Core *src ) | ||
| 1211 | { | ||
| 1212 | Core *pRet = _allocateCore(); | ||
| 1213 | |||
| 1214 | pRet->nFilled = 0; | ||
| 1215 | pRet->nDeleted = 0; | ||
| 1216 | pRet->nCapacity = src->nCapacity; | ||
| 1217 | pRet->nKeysSize = bitsTo<uint32_t>( pRet->nCapacity ); | ||
| 1218 | pRet->bFilled = pRet->ca.allocate( pRet->nKeysSize ); | ||
| 1219 | pRet->bDeleted = pRet->ca.allocate( pRet->nKeysSize ); | ||
| 1220 | pRet->clearBits(); | ||
| 1221 | |||
| 1222 | pRet->aHashCodes = pRet->ca.allocate( pRet->nCapacity ); | ||
| 1223 | pRet->aKeys = pRet->ka.allocate( pRet->nCapacity ); | ||
| 1224 | pRet->aValues = pRet->va.allocate( pRet->nCapacity ); | ||
| 1225 | |||
| 1226 | for( uint32_t j = 0; j < src->nCapacity; j++ ) | ||
| 1227 | { | ||
| 1228 | if( src->isFilled( j ) && !src->isDeleted( j ) ) | ||
| 1229 | { | ||
| 1230 | pRet->insert( src->aKeys[j], src->aValues[j] ); | ||
| 1231 | } | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | return pRet; | ||
| 1235 | } | ||
| 1236 | }; | ||
| 1237 | |||
| 1238 | template<typename T> uint32_t __calcHashCode( const T &k ) | ||
| 1239 | { | ||
| 1240 | return static_cast<uint32_t>( k ); | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | template<typename T> bool __cmpHashKeys( const T &a, const T &b ) | ||
| 1244 | { | ||
| 1245 | return (a == b); | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | template<> uint32_t __calcHashCode<const char *>( const char * const &k ); | ||
| 1249 | template<> bool __cmpHashKeys<const char *>( const char * const &a, const char * const &b ); | ||
| 1250 | |||
| 1251 | template<> uint32_t __calcHashCode<char *>( char * const &k ); | ||
| 1252 | template<> bool __cmpHashKeys<char *>( char * const &a, char * const &b ); | ||
| 1253 | |||
| 1254 | class Formatter; | ||
| 1255 | Formatter &operator<<( Formatter &rOut, char *sStr ); | ||
| 1256 | Formatter &operator<<( Formatter &rOut, signed char c ); | ||
| 1257 | template<typename key, typename value> | ||
| 1258 | Formatter &operator<<( Formatter &f, const Bu::Hash<key, value> &l ) | ||
| 1259 | { | ||
| 1260 | f << '{'; | ||
| 1261 | for( typename Bu::Hash<key,value>::const_iterator i = l.begin(); i; i++ ) | ||
| 1262 | { | ||
| 1263 | if( i != l.begin() ) | ||
| 1264 | f << ", "; | ||
| 1265 | f << i.getKey() << ": " << i.getValue(); | ||
| 1266 | } | ||
| 1267 | f << '}'; | ||
| 1268 | |||
| 1269 | return f; | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | template<typename key, typename value, typename a, typename b, | ||
| 1273 | typename c, typename d> | ||
| 1274 | ArchiveBase &operator<<( ArchiveBase &ar, const Hash<key,value,a,b,c,d> &h ) | ||
| 1275 | { | ||
| 1276 | long iSize = h.getSize(); | ||
| 1277 | ar << iSize; | ||
| 1278 | for( typename Hash<key,value,a,b,c,d>::const_iterator i = h.begin(); i != h.end(); i++ ) | ||
| 1279 | { | ||
| 1280 | ar << (i.getKey()); | ||
| 1281 | ar << (i.getValue()); | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | return ar; | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | template<typename key, typename value, typename a, typename b, | ||
| 1288 | typename c, typename d> | ||
| 1289 | ArchiveBase &operator>>( ArchiveBase &ar, Hash<key,value,a,b,c,d> &h ) | ||
| 1290 | { | ||
| 1291 | h.clear(); | ||
| 1292 | long nSize; | ||
| 1293 | ar >> nSize; | ||
| 1294 | |||
| 1295 | for( long j = 0; j < nSize; j++ ) | ||
| 1296 | { | ||
| 1297 | key k; value v; | ||
| 1298 | ar >> k >> v; | ||
| 1299 | h.insert( k, v ); | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | return ar; | ||
| 1303 | } | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | #endif | ||
| diff --git a/src/stable/heap.cpp b/src/stable/heap.cpp new file mode 100644 index 0000000..a2ffac2 --- /dev/null +++ b/src/stable/heap.cpp | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/heap.h" | ||
| 9 | |||
| 10 | namespace Bu { subExceptionDef( HeapException ) } | ||
| diff --git a/src/stable/heap.h b/src/stable/heap.h new file mode 100644 index 0000000..afe8be6 --- /dev/null +++ b/src/stable/heap.h | |||
| @@ -0,0 +1,612 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_HEAP_H | ||
| 9 | #define BU_HEAP_H | ||
| 10 | |||
| 11 | #include <stddef.h> | ||
| 12 | #include <memory> | ||
| 13 | #include "bu/exceptionbase.h" | ||
| 14 | #include "bu/util.h" | ||
| 15 | #include "bu/queue.h" | ||
| 16 | #include "bu/sharedcore.h" | ||
| 17 | |||
| 18 | namespace Bu | ||
| 19 | { | ||
| 20 | subExceptionDecl( HeapException ); | ||
| 21 | |||
| 22 | template<typename item, typename cmpfunc, typename itemalloc> | ||
| 23 | class Heap; | ||
| 24 | |||
| 25 | /** @cond DEVEL */ | ||
| 26 | template<typename item, typename cmpfunc, typename itemalloc> | ||
| 27 | class HeapCore | ||
| 28 | { | ||
| 29 | friend class Heap<item, cmpfunc, itemalloc>; | ||
| 30 | friend class SharedCore< | ||
| 31 | Heap<item, cmpfunc, itemalloc>, HeapCore<item, cmpfunc, itemalloc> | ||
| 32 | >; | ||
| 33 | private: | ||
| 34 | HeapCore() : | ||
| 35 | iSize( 0 ), | ||
| 36 | iFill( 0 ), | ||
| 37 | aItem( NULL ) | ||
| 38 | { | ||
| 39 | } | ||
| 40 | |||
| 41 | virtual ~HeapCore() | ||
| 42 | { | ||
| 43 | clear(); | ||
| 44 | } | ||
| 45 | |||
| 46 | void init() | ||
| 47 | { | ||
| 48 | if( iSize > 0 ) | ||
| 49 | return; | ||
| 50 | |||
| 51 | iSize = 7; | ||
| 52 | iFill = 0; | ||
| 53 | aItem = ia.allocate( iSize ); | ||
| 54 | } | ||
| 55 | |||
| 56 | void init( int iCap ) | ||
| 57 | { | ||
| 58 | if( iSize > 0 ) | ||
| 59 | return; | ||
| 60 | |||
| 61 | for( iSize = 1; iSize < iCap; iSize=iSize*2+1 ) { } | ||
| 62 | iFill = 0; | ||
| 63 | aItem = ia.allocate( iSize ); | ||
| 64 | } | ||
| 65 | |||
| 66 | void clear() | ||
| 67 | { | ||
| 68 | if( iSize == 0 ) | ||
| 69 | return; | ||
| 70 | |||
| 71 | for( int j = 0; j < iFill; j++ ) | ||
| 72 | ia.destroy( &aItem[j] ); | ||
| 73 | ia.deallocate( aItem, iSize ); | ||
| 74 | aItem = NULL; | ||
| 75 | iSize = 0; | ||
| 76 | iFill = 0; | ||
| 77 | } | ||
| 78 | |||
| 79 | void upSize() | ||
| 80 | { | ||
| 81 | if( iSize == 0 ) | ||
| 82 | { | ||
| 83 | init(); | ||
| 84 | return; | ||
| 85 | } | ||
| 86 | |||
| 87 | item *aNewItems = ia.allocate( iSize*2+1 ); | ||
| 88 | // | ||
| 89 | // We cannot use a memcopy here because we don't know what kind | ||
| 90 | // of datastructures are being used, we have to copy them one at | ||
| 91 | // a time. | ||
| 92 | // | ||
| 93 | for( int j = 0; j < iFill; j++ ) | ||
| 94 | { | ||
| 95 | ia.construct( &aNewItems[j], aItem[j] ); | ||
| 96 | ia.destroy( &aItem[j] ); | ||
| 97 | } | ||
| 98 | ia.deallocate( aItem, iSize ); | ||
| 99 | aItem = aNewItems; | ||
| 100 | iSize = iSize*2+1; | ||
| 101 | } | ||
| 102 | |||
| 103 | virtual void enqueue( const item &it ) | ||
| 104 | { | ||
| 105 | item i = it; // TODO: This is a silly workaround, put the i item | ||
| 106 | // at the end. | ||
| 107 | if( iFill+1 >= iSize ) | ||
| 108 | upSize(); | ||
| 109 | |||
| 110 | for( int j = 0; j < iFill; ) | ||
| 111 | { | ||
| 112 | if( cmp( i, aItem[j] ) ) | ||
| 113 | { | ||
| 114 | Bu::swap( i, aItem[j] ); | ||
| 115 | } | ||
| 116 | |||
| 117 | if( j*2+1 >= iFill ) | ||
| 118 | break; | ||
| 119 | if( cmp( i, aItem[j*2+1] ) ) | ||
| 120 | { | ||
| 121 | j = j*2+1; | ||
| 122 | } | ||
| 123 | else | ||
| 124 | { | ||
| 125 | j = j*2+2; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | ia.construct( &aItem[iFill], i ); | ||
| 129 | if( iFill > 0 ) | ||
| 130 | { | ||
| 131 | for( int j = iFill; j >= 0; ) | ||
| 132 | { | ||
| 133 | int k = (j-1)/2; | ||
| 134 | if( j == k ) | ||
| 135 | break; | ||
| 136 | if( cmp( aItem[k], aItem[j] ) ) | ||
| 137 | break; | ||
| 138 | |||
| 139 | Bu::swap( aItem[k], aItem[j] ); | ||
| 140 | j = k; | ||
| 141 | } | ||
| 142 | } | ||
| 143 | iFill++; | ||
| 144 | } | ||
| 145 | |||
| 146 | virtual item dequeue() | ||
| 147 | { | ||
| 148 | if( iFill == 0 ) | ||
| 149 | throw HeapException("Heap empty."); | ||
| 150 | item iRet = aItem[0]; | ||
| 151 | int j; | ||
| 152 | for( j = 0; j < iFill; ) | ||
| 153 | { | ||
| 154 | int k = j*2+1; | ||
| 155 | if( k+1 < iFill && cmp( aItem[k+1], aItem[k] ) ) | ||
| 156 | { | ||
| 157 | if( k+1 < iFill-1 && cmp( aItem[iFill-1], aItem[k+1] ) ) | ||
| 158 | break; | ||
| 159 | aItem[j] = aItem[k+1]; | ||
| 160 | j = k+1; | ||
| 161 | } | ||
| 162 | else if( k < iFill ) | ||
| 163 | { | ||
| 164 | if( k < iFill-1 && cmp( aItem[iFill-1], aItem[k] ) ) | ||
| 165 | break; | ||
| 166 | aItem[j] = aItem[k]; | ||
| 167 | j = k; | ||
| 168 | } | ||
| 169 | else | ||
| 170 | break; | ||
| 171 | } | ||
| 172 | if( j < iFill-1 ) | ||
| 173 | aItem[j] = aItem[iFill-1]; | ||
| 174 | ia.destroy( &aItem[iFill-1] ); | ||
| 175 | iFill--; | ||
| 176 | |||
| 177 | return iRet; | ||
| 178 | } | ||
| 179 | |||
| 180 | private: | ||
| 181 | int iSize; | ||
| 182 | int iFill; | ||
| 183 | item *aItem; | ||
| 184 | cmpfunc cmp; | ||
| 185 | itemalloc ia; | ||
| 186 | }; | ||
| 187 | /** @endcond */ | ||
| 188 | |||
| 189 | /** | ||
| 190 | * A priority queue that allows for an unlimited number of priorities. All | ||
| 191 | * objects enqueued must support less-than-comparison. Then every time an | ||
| 192 | * item is dequeued it is always the least item in the heap. The heap | ||
| 193 | * operates using a binary tree for storage, which allows most operations | ||
| 194 | * to be very fast. Enqueueing and dequeueing are both O(log(N)) operatoins | ||
| 195 | * whereas peeking is constant time. | ||
| 196 | * | ||
| 197 | * This heap implementation allows iterating, however please note that any | ||
| 198 | * enqueue or dequeue operation will invalidate the iterator and make it | ||
| 199 | * unusable (if it still works, you shouldn't trust the results). Also, | ||
| 200 | * the items are not stored in memory in order, they are optomized into a | ||
| 201 | * tree. This means that the items will be in effectively random order | ||
| 202 | * while iterating through them, and the order cannot be trusted. Also, | ||
| 203 | * modifying an item in the heap will not cause that item to be re-sorted. | ||
| 204 | * If you want to change the position of an item in the heap you will have | ||
| 205 | * to dequeue every item before it, dequeue that item, change it, and | ||
| 206 | * re-enqueue all of the items removed. | ||
| 207 | */ | ||
| 208 | template<typename item, typename cmpfunc=__basicLTCmp<item>, typename itemalloc=std::allocator<item> > | ||
| 209 | class Heap : public Queue<item>, public SharedCore< | ||
| 210 | Heap<item, cmpfunc, itemalloc>, | ||
| 211 | HeapCore<item, cmpfunc, itemalloc> | ||
| 212 | > | ||
| 213 | { | ||
| 214 | private: | ||
| 215 | typedef class Heap<item,cmpfunc,itemalloc> MyType; | ||
| 216 | typedef class HeapCore<item,cmpfunc,itemalloc> Core; | ||
| 217 | |||
| 218 | protected: | ||
| 219 | using SharedCore<MyType, Core>::core; | ||
| 220 | using SharedCore<MyType, Core>::_hardCopy; | ||
| 221 | using SharedCore<MyType, Core>::_resetCore; | ||
| 222 | using SharedCore<MyType, Core>::_allocateCore; | ||
| 223 | |||
| 224 | public: | ||
| 225 | Heap() | ||
| 226 | { | ||
| 227 | } | ||
| 228 | |||
| 229 | Heap( cmpfunc cmpin ) | ||
| 230 | { | ||
| 231 | core->cmp = cmpin; | ||
| 232 | } | ||
| 233 | |||
| 234 | Heap( int iInitialCapacity ) | ||
| 235 | { | ||
| 236 | core->init( iInitialCapacity ); | ||
| 237 | } | ||
| 238 | |||
| 239 | Heap( cmpfunc cmpin, int iInitialCapacity ) | ||
| 240 | { | ||
| 241 | core->cmp = cmpin; | ||
| 242 | core->init( iInitialCapacity ); | ||
| 243 | } | ||
| 244 | |||
| 245 | Heap( const MyType &rSrc ) : | ||
| 246 | SharedCore<MyType, Core>( rSrc ) | ||
| 247 | { | ||
| 248 | } | ||
| 249 | |||
| 250 | virtual ~Heap() | ||
| 251 | { | ||
| 252 | } | ||
| 253 | |||
| 254 | virtual void enqueue( const item &it ) | ||
| 255 | { | ||
| 256 | _hardCopy(); | ||
| 257 | |||
| 258 | core->enqueue( it ); | ||
| 259 | } | ||
| 260 | |||
| 261 | virtual item &peek() | ||
| 262 | { | ||
| 263 | _hardCopy(); | ||
| 264 | |||
| 265 | if( core->iFill == 0 ) | ||
| 266 | throw HeapException("Heap empty."); | ||
| 267 | return core->aItem[0]; | ||
| 268 | } | ||
| 269 | |||
| 270 | virtual const item &peek() const | ||
| 271 | { | ||
| 272 | if( core->iFill == 0 ) | ||
| 273 | throw HeapException("Heap empty."); | ||
| 274 | return core->aItem[0]; | ||
| 275 | } | ||
| 276 | |||
| 277 | virtual item dequeue() | ||
| 278 | { | ||
| 279 | _hardCopy(); | ||
| 280 | |||
| 281 | return core->dequeue(); | ||
| 282 | } | ||
| 283 | |||
| 284 | virtual bool isEmpty() const | ||
| 285 | { | ||
| 286 | return (core->iFill==0); | ||
| 287 | } | ||
| 288 | |||
| 289 | virtual int getSize() const | ||
| 290 | { | ||
| 291 | return core->iFill; | ||
| 292 | } | ||
| 293 | |||
| 294 | class iterator | ||
| 295 | { | ||
| 296 | friend class const_iterator; | ||
| 297 | friend class Heap<item, cmpfunc, itemalloc>; | ||
| 298 | private: | ||
| 299 | Heap<item, cmpfunc, itemalloc> *pHeap; | ||
| 300 | int iIndex; | ||
| 301 | |||
| 302 | iterator( Heap<item, cmpfunc, itemalloc> *pHeap, int iIndex ) : | ||
| 303 | pHeap( pHeap ), iIndex( iIndex ) | ||
| 304 | { | ||
| 305 | } | ||
| 306 | |||
| 307 | void checkValid() | ||
| 308 | { | ||
| 309 | if( pHeap == NULL ) | ||
| 310 | throw Bu::ExceptionBase("Iterator not initialized."); | ||
| 311 | if( iIndex < 0 || iIndex >= pHeap->core->iFill ) | ||
| 312 | throw Bu::ExceptionBase("Iterator out of bounds."); | ||
| 313 | } | ||
| 314 | |||
| 315 | public: | ||
| 316 | iterator() : | ||
| 317 | pHeap( NULL ), | ||
| 318 | iIndex( -1 ) | ||
| 319 | { | ||
| 320 | } | ||
| 321 | |||
| 322 | iterator( const iterator &i ) : | ||
| 323 | pHeap( i.pHeap ), | ||
| 324 | iIndex( i.iIndex ) | ||
| 325 | { | ||
| 326 | } | ||
| 327 | |||
| 328 | bool operator==( const iterator &oth ) const | ||
| 329 | { | ||
| 330 | return (oth.pHeap == pHeap) && (oth.iIndex == iIndex); | ||
| 331 | } | ||
| 332 | |||
| 333 | bool operator!=( const iterator &oth ) const | ||
| 334 | { | ||
| 335 | return (oth.pHeap != pHeap) || (oth.iIndex != iIndex); | ||
| 336 | } | ||
| 337 | |||
| 338 | item &operator*() | ||
| 339 | { | ||
| 340 | pHeap->_hardCopy(); | ||
| 341 | |||
| 342 | return pHeap->core->aItem[iIndex]; | ||
| 343 | } | ||
| 344 | |||
| 345 | item *operator->() | ||
| 346 | { | ||
| 347 | pHeap->_hardCopy(); | ||
| 348 | |||
| 349 | return &(pHeap->core->aItem[iIndex]); | ||
| 350 | } | ||
| 351 | |||
| 352 | iterator &operator++() | ||
| 353 | { | ||
| 354 | checkValid(); | ||
| 355 | iIndex++; | ||
| 356 | if( iIndex >= pHeap->iFill ) | ||
| 357 | iIndex = -1; | ||
| 358 | |||
| 359 | return *this; | ||
| 360 | } | ||
| 361 | |||
| 362 | iterator &operator--() | ||
| 363 | { | ||
| 364 | checkValid(); | ||
| 365 | iIndex--; | ||
| 366 | |||
| 367 | return *this; | ||
| 368 | } | ||
| 369 | |||
| 370 | iterator &operator++( int ) | ||
| 371 | { | ||
| 372 | checkValid(); | ||
| 373 | iIndex++; | ||
| 374 | if( iIndex >= pHeap->core->iFill ) | ||
| 375 | iIndex = -1; | ||
| 376 | |||
| 377 | return *this; | ||
| 378 | } | ||
| 379 | |||
| 380 | iterator &operator--( int ) | ||
| 381 | { | ||
| 382 | checkValid(); | ||
| 383 | iIndex--; | ||
| 384 | |||
| 385 | return *this; | ||
| 386 | } | ||
| 387 | |||
| 388 | iterator operator+( int iDelta ) | ||
| 389 | { | ||
| 390 | checkValid(); | ||
| 391 | iterator ret( *this ); | ||
| 392 | ret.iIndex += iDelta; | ||
| 393 | if( ret.iIndex >= pHeap->core->iFill ) | ||
| 394 | ret.iIndex = -1; | ||
| 395 | return ret; | ||
| 396 | } | ||
| 397 | |||
| 398 | iterator operator-( int iDelta ) | ||
| 399 | { | ||
| 400 | checkValid(); | ||
| 401 | iterator ret( *this ); | ||
| 402 | ret.iIndex -= iDelta; | ||
| 403 | if( ret.iIndex < 0 ) | ||
| 404 | ret.iIndex = -1; | ||
| 405 | return ret; | ||
| 406 | } | ||
| 407 | |||
| 408 | operator bool() const | ||
| 409 | { | ||
| 410 | return iIndex != -1; | ||
| 411 | } | ||
| 412 | |||
| 413 | bool isValid() const | ||
| 414 | { | ||
| 415 | return iIndex != -1; | ||
| 416 | } | ||
| 417 | |||
| 418 | iterator &operator=( const iterator &oth ) | ||
| 419 | { | ||
| 420 | pHeap = oth.pHeap; | ||
| 421 | iIndex = oth.iIndex; | ||
| 422 | } | ||
| 423 | }; | ||
| 424 | |||
| 425 | class const_iterator | ||
| 426 | { | ||
| 427 | friend class Heap<item, cmpfunc, itemalloc>; | ||
| 428 | private: | ||
| 429 | Heap<item, cmpfunc, itemalloc> *pHeap; | ||
| 430 | int iIndex; | ||
| 431 | |||
| 432 | const_iterator( Heap<item, cmpfunc, itemalloc> *pHeap, | ||
| 433 | int iIndex ) : | ||
| 434 | pHeap( pHeap ), iIndex( iIndex ) | ||
| 435 | { | ||
| 436 | } | ||
| 437 | |||
| 438 | void checkValid() | ||
| 439 | { | ||
| 440 | if( pHeap == NULL ) | ||
| 441 | throw Bu::ExceptionBase("Iterator not initialized."); | ||
| 442 | if( iIndex < 0 || iIndex >= pHeap->core->iFill ) | ||
| 443 | throw Bu::ExceptionBase("Iterator out of bounds."); | ||
| 444 | } | ||
| 445 | |||
| 446 | public: | ||
| 447 | const_iterator() : | ||
| 448 | pHeap( NULL ), | ||
| 449 | iIndex( -1 ) | ||
| 450 | { | ||
| 451 | } | ||
| 452 | |||
| 453 | const_iterator( const const_iterator &i ) : | ||
| 454 | pHeap( i.pHeap ), | ||
| 455 | iIndex( i.iIndex ) | ||
| 456 | { | ||
| 457 | } | ||
| 458 | |||
| 459 | const_iterator( const iterator &i ) : | ||
| 460 | pHeap( i.pHeap ), | ||
| 461 | iIndex( i.iIndex ) | ||
| 462 | { | ||
| 463 | } | ||
| 464 | |||
| 465 | bool operator==( const const_iterator &oth ) const | ||
| 466 | { | ||
| 467 | return (oth.pHeap == pHeap) && (oth.iIndex == iIndex); | ||
| 468 | } | ||
| 469 | |||
| 470 | bool operator!=( const const_iterator &oth ) const | ||
| 471 | { | ||
| 472 | return (oth.pHeap != pHeap) || (oth.iIndex != iIndex); | ||
| 473 | } | ||
| 474 | |||
| 475 | const item &operator*() | ||
| 476 | { | ||
| 477 | pHeap->_hardCopy(); | ||
| 478 | |||
| 479 | return pHeap->core->aItem[iIndex]; | ||
| 480 | } | ||
| 481 | |||
| 482 | const item *operator->() | ||
| 483 | { | ||
| 484 | pHeap->_hardCopy(); | ||
| 485 | |||
| 486 | return &(pHeap->core->aItem[iIndex]); | ||
| 487 | } | ||
| 488 | |||
| 489 | const_iterator &operator++() | ||
| 490 | { | ||
| 491 | checkValid(); | ||
| 492 | iIndex++; | ||
| 493 | if( iIndex >= pHeap->core->iFill ) | ||
| 494 | iIndex = -1; | ||
| 495 | |||
| 496 | return *this; | ||
| 497 | } | ||
| 498 | |||
| 499 | const_iterator &operator--() | ||
| 500 | { | ||
| 501 | checkValid(); | ||
| 502 | iIndex--; | ||
| 503 | |||
| 504 | return *this; | ||
| 505 | } | ||
| 506 | |||
| 507 | const_iterator &operator++( int ) | ||
| 508 | { | ||
| 509 | checkValid(); | ||
| 510 | iIndex++; | ||
| 511 | if( iIndex >= pHeap->core->iFill ) | ||
| 512 | iIndex = -1; | ||
| 513 | |||
| 514 | return *this; | ||
| 515 | } | ||
| 516 | |||
| 517 | const_iterator &operator--( int ) | ||
| 518 | { | ||
| 519 | checkValid(); | ||
| 520 | iIndex--; | ||
| 521 | |||
| 522 | return *this; | ||
| 523 | } | ||
| 524 | |||
| 525 | const_iterator operator+( int iDelta ) | ||
| 526 | { | ||
| 527 | checkValid(); | ||
| 528 | const_iterator ret( *this ); | ||
| 529 | ret.iIndex += iDelta; | ||
| 530 | if( ret.iIndex >= pHeap->iFill ) | ||
| 531 | ret.iIndex = -1; | ||
| 532 | return ret; | ||
| 533 | } | ||
| 534 | |||
| 535 | const_iterator operator-( int iDelta ) | ||
| 536 | { | ||
| 537 | checkValid(); | ||
| 538 | const_iterator ret( *this ); | ||
| 539 | ret.iIndex -= iDelta; | ||
| 540 | if( ret.iIndex < 0 ) | ||
| 541 | ret.iIndex = -1; | ||
| 542 | return ret; | ||
| 543 | } | ||
| 544 | |||
| 545 | operator bool() const | ||
| 546 | { | ||
| 547 | return iIndex != -1; | ||
| 548 | } | ||
| 549 | |||
| 550 | bool isValid() const | ||
| 551 | { | ||
| 552 | return iIndex != -1; | ||
| 553 | } | ||
| 554 | |||
| 555 | const_iterator &operator=( const const_iterator &oth ) | ||
| 556 | { | ||
| 557 | pHeap = oth.pHeap; | ||
| 558 | iIndex = oth.iIndex; | ||
| 559 | } | ||
| 560 | |||
| 561 | const_iterator &operator=( const iterator &oth ) | ||
| 562 | { | ||
| 563 | pHeap = oth.pHeap; | ||
| 564 | iIndex = oth.iIndex; | ||
| 565 | } | ||
| 566 | }; | ||
| 567 | |||
| 568 | iterator begin() | ||
| 569 | { | ||
| 570 | if( core->iFill == 0 ) | ||
| 571 | return end(); | ||
| 572 | return iterator( this, 0 ); | ||
| 573 | } | ||
| 574 | |||
| 575 | const_iterator begin() const | ||
| 576 | { | ||
| 577 | if( core->iFill == 0 ) | ||
| 578 | return end(); | ||
| 579 | return const_iterator( this, 0 ); | ||
| 580 | } | ||
| 581 | |||
| 582 | iterator end() | ||
| 583 | { | ||
| 584 | return iterator( this, -1 ); | ||
| 585 | } | ||
| 586 | |||
| 587 | const_iterator end() const | ||
| 588 | { | ||
| 589 | return const_iterator( this, -1 ); | ||
| 590 | } | ||
| 591 | |||
| 592 | |||
| 593 | protected: | ||
| 594 | virtual Core *_copyCore( Core *src ) | ||
| 595 | { | ||
| 596 | Core *pRet = _allocateCore(); | ||
| 597 | |||
| 598 | pRet->iSize = src->iSize; | ||
| 599 | pRet->iFill = src->iFill; | ||
| 600 | pRet->cmp = src->cmp; | ||
| 601 | pRet->aItem = pRet->ia.allocate( pRet->iSize ); | ||
| 602 | for( int j = 0; j < pRet->iFill; j++ ) | ||
| 603 | { | ||
| 604 | pRet->ia.construct( &pRet->aItem[j], src->aItem[j] ); | ||
| 605 | } | ||
| 606 | |||
| 607 | return pRet; | ||
| 608 | } | ||
| 609 | }; | ||
| 610 | }; | ||
| 611 | |||
| 612 | #endif | ||
| diff --git a/src/stable/hex.cpp b/src/stable/hex.cpp new file mode 100644 index 0000000..2a04c6f --- /dev/null +++ b/src/stable/hex.cpp | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/hex.h" | ||
| 9 | |||
| 10 | Bu::Hex::Hex( Bu::Stream &rNext, bool bUpperCase, int iChunk ) : | ||
| 11 | Bu::Filter( rNext ), | ||
| 12 | iChunk( iChunk ), | ||
| 13 | iPos( 0 ), | ||
| 14 | iIn( 0 ), | ||
| 15 | sChrs(bUpperCase?"0123456789ABCDEF":"0123456789abcdef") | ||
| 16 | { | ||
| 17 | } | ||
| 18 | |||
| 19 | Bu::Hex::~Hex() | ||
| 20 | { | ||
| 21 | } | ||
| 22 | |||
| 23 | void Bu::Hex::start() | ||
| 24 | { | ||
| 25 | iPos = iIn = 0; | ||
| 26 | } | ||
| 27 | |||
| 28 | Bu::size Bu::Hex::stop() | ||
| 29 | { | ||
| 30 | return iPos; | ||
| 31 | } | ||
| 32 | |||
| 33 | Bu::size Bu::Hex::read( void *pBuf, Bu::size iBytes ) | ||
| 34 | { | ||
| 35 | Bu::size j; | ||
| 36 | uint8_t *puBuf = (uint8_t *)pBuf; | ||
| 37 | for( j = 0; j < iBytes; j++ ) | ||
| 38 | { | ||
| 39 | for(; iIn < 2; iIn++ ) | ||
| 40 | { | ||
| 41 | if( rNext.read( &cIn[iIn], 1 ) == 0 ) | ||
| 42 | return j; | ||
| 43 | if( cIn[iIn] == ' ' || cIn[iIn] == '\t' || | ||
| 44 | cIn[iIn] == '\n' || cIn[iIn] == '\r' ) | ||
| 45 | iIn--; | ||
| 46 | } | ||
| 47 | #define chr2nibble( c ) ((c>='0'&&c<='9')?(c-'0'):((c|0x60)-'a'+10)) | ||
| 48 | puBuf[j] = ((chr2nibble(cIn[0])<<4)|chr2nibble(cIn[1])); | ||
| 49 | iIn = 0; | ||
| 50 | } | ||
| 51 | return j; | ||
| 52 | } | ||
| 53 | |||
| 54 | Bu::size Bu::Hex::write( const void *pBuf, Bu::size iBytes ) | ||
| 55 | { | ||
| 56 | char cOut[2]; | ||
| 57 | uint8_t *puBuf = (uint8_t *)pBuf; | ||
| 58 | for( Bu::size j = 0; j < iBytes; j++ ) | ||
| 59 | { | ||
| 60 | cOut[0] = sChrs[(puBuf[j]&0xf0)>>4]; | ||
| 61 | cOut[1] = sChrs[(puBuf[j]&0x0f)]; | ||
| 62 | if( iChunk > 0 && iPos%iChunk == 0 && iPos>0 ) | ||
| 63 | rNext.write(" ", 1 ); | ||
| 64 | rNext.write( cOut, 2 ); | ||
| 65 | iPos++; | ||
| 66 | } | ||
| 67 | return iBytes; | ||
| 68 | } | ||
| 69 | |||
| diff --git a/src/stable/hex.h b/src/stable/hex.h new file mode 100644 index 0000000..3595fae --- /dev/null +++ b/src/stable/hex.h | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_HEX_H | ||
| 9 | #define BU_HEX_H | ||
| 10 | |||
| 11 | #include "bu/filter.h" | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * This very simple filter encodes to/decodes from hex encoded string data. | ||
| 17 | * The primary use of this filter is in debugging, use it with | ||
| 18 | * Bu::encodeStr to easily create hex dumps of string data, even other raw | ||
| 19 | * structures. | ||
| 20 | * | ||
| 21 | *@code | ||
| 22 | Bu::println("Hexdump: " + Bu::encodeStr<Bu::Hex>("Test data ;)") ); | ||
| 23 | @endcode | ||
| 24 | * Or... | ||
| 25 | *@code | ||
| 26 | complex_struct data; | ||
| 27 | ... | ||
| 28 | Bu::println("Hexdump: " + | ||
| 29 | Bu::encodeStr<Bu::Hex>( | ||
| 30 | Bu::String( &data, sizeof(data) ) | ||
| 31 | ) | ||
| 32 | ); | ||
| 33 | @endcode | ||
| 34 | **/ | ||
| 35 | class Hex : public Bu::Filter | ||
| 36 | { | ||
| 37 | public: | ||
| 38 | Hex( Bu::Stream &rNext, bool bUpperCase=false, int iChunk=-1 ); | ||
| 39 | virtual ~Hex(); | ||
| 40 | |||
| 41 | virtual void start(); | ||
| 42 | virtual Bu::size stop(); | ||
| 43 | |||
| 44 | virtual Bu::size read( void *pBuf, Bu::size iBytes ); | ||
| 45 | virtual Bu::size write( const void *pBuf, Bu::size iBytes ); | ||
| 46 | using Bu::Stream::write; | ||
| 47 | |||
| 48 | private: | ||
| 49 | int iChunk; | ||
| 50 | Bu::size iPos; | ||
| 51 | char cIn[2]; | ||
| 52 | int iIn; | ||
| 53 | const char *sChrs; | ||
| 54 | }; | ||
| 55 | }; | ||
| 56 | |||
| 57 | #endif | ||
| diff --git a/src/stable/list.cpp b/src/stable/list.cpp new file mode 100644 index 0000000..e05765e --- /dev/null +++ b/src/stable/list.cpp | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/list.h" | ||
| 9 | |||
| diff --git a/src/stable/list.h b/src/stable/list.h new file mode 100644 index 0000000..21ba0b5 --- /dev/null +++ b/src/stable/list.h | |||
| @@ -0,0 +1,1014 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_LIST_H | ||
| 9 | #define BU_LIST_H | ||
| 10 | |||
| 11 | #include <memory> | ||
| 12 | #include "bu/exceptionbase.h" | ||
| 13 | #include "bu/sharedcore.h" | ||
| 14 | #include "bu/archivebase.h" | ||
| 15 | #include "bu/heap.h" | ||
| 16 | |||
| 17 | namespace Bu | ||
| 18 | { | ||
| 19 | /** @cond DEVEL */ | ||
| 20 | template<typename value> | ||
| 21 | struct ListLink | ||
| 22 | { | ||
| 23 | value *pValue; | ||
| 24 | ListLink *pNext; | ||
| 25 | ListLink *pPrev; | ||
| 26 | }; | ||
| 27 | |||
| 28 | template<typename value, typename valuealloc, typename linkalloc> | ||
| 29 | class List; | ||
| 30 | |||
| 31 | template<typename value, typename valuealloc, typename linkalloc> | ||
| 32 | struct ListCore | ||
| 33 | { | ||
| 34 | friend class List<value, valuealloc, linkalloc>; | ||
| 35 | friend class SharedCore< | ||
| 36 | List<value, valuealloc, linkalloc>, | ||
| 37 | ListCore<value, valuealloc, linkalloc> | ||
| 38 | >; | ||
| 39 | private: | ||
| 40 | typedef struct ListLink<value> Link; | ||
| 41 | ListCore() : | ||
| 42 | pFirst( NULL ), | ||
| 43 | pLast( NULL ), | ||
| 44 | nSize( 0 ) | ||
| 45 | { } | ||
| 46 | |||
| 47 | virtual ~ListCore() | ||
| 48 | { | ||
| 49 | clear(); | ||
| 50 | } | ||
| 51 | |||
| 52 | Link *pFirst; | ||
| 53 | Link *pLast; | ||
| 54 | long nSize; | ||
| 55 | linkalloc la; | ||
| 56 | valuealloc va; | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Append a value to the list. | ||
| 60 | *@param v (const value_type &) The value to append. | ||
| 61 | */ | ||
| 62 | Link *append( const value &v ) | ||
| 63 | { | ||
| 64 | Link *pNew = la.allocate( 1 ); | ||
| 65 | pNew->pValue = va.allocate( 1 ); | ||
| 66 | va.construct( pNew->pValue, v ); | ||
| 67 | nSize++; | ||
| 68 | if( pFirst == NULL ) | ||
| 69 | { | ||
| 70 | // Empty list | ||
| 71 | pFirst = pLast = pNew; | ||
| 72 | pNew->pNext = pNew->pPrev = NULL; | ||
| 73 | } | ||
| 74 | else | ||
| 75 | { | ||
| 76 | pNew->pNext = NULL; | ||
| 77 | pNew->pPrev = pLast; | ||
| 78 | pLast->pNext = pNew; | ||
| 79 | pLast = pNew; | ||
| 80 | } | ||
| 81 | return pNew; | ||
| 82 | } | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Prepend a value to the list. | ||
| 86 | *@param v (const value_type &) The value to prepend. | ||
| 87 | */ | ||
| 88 | Link *prepend( const value &v ) | ||
| 89 | { | ||
| 90 | Link *pNew = la.allocate( 1 ); | ||
| 91 | pNew->pValue = va.allocate( 1 ); | ||
| 92 | va.construct( pNew->pValue, v ); | ||
| 93 | nSize++; | ||
| 94 | if( pFirst == NULL ) | ||
| 95 | { | ||
| 96 | // Empty list | ||
| 97 | pFirst = pLast = pNew; | ||
| 98 | pNew->pNext = pNew->pPrev = NULL; | ||
| 99 | } | ||
| 100 | else | ||
| 101 | { | ||
| 102 | pNew->pNext = pFirst; | ||
| 103 | pNew->pPrev = NULL; | ||
| 104 | pFirst->pPrev = pNew; | ||
| 105 | pFirst = pNew; | ||
| 106 | } | ||
| 107 | return pNew; | ||
| 108 | } | ||
| 109 | |||
| 110 | void clear() | ||
| 111 | { | ||
| 112 | Link *pCur = pFirst; | ||
| 113 | for(;;) | ||
| 114 | { | ||
| 115 | if( pCur == NULL ) break; | ||
| 116 | va.destroy( pCur->pValue ); | ||
| 117 | va.deallocate( pCur->pValue, 1 ); | ||
| 118 | Link *pTmp = pCur->pNext; | ||
| 119 | la.destroy( pCur ); | ||
| 120 | la.deallocate( pCur, 1 ); | ||
| 121 | pCur = pTmp; | ||
| 122 | } | ||
| 123 | pFirst = pLast = NULL; | ||
| 124 | nSize = 0; | ||
| 125 | } | ||
| 126 | |||
| 127 | Link *insert( Link *pLink, const value &v ) | ||
| 128 | { | ||
| 129 | Link *pAfter = pLink; | ||
| 130 | if( pAfter == NULL ) | ||
| 131 | { | ||
| 132 | return append( v ); | ||
| 133 | } | ||
| 134 | Link *pPrev = pAfter->pPrev; | ||
| 135 | if( pPrev == NULL ) | ||
| 136 | { | ||
| 137 | return prepend( v ); | ||
| 138 | } | ||
| 139 | |||
| 140 | Link *pNew = la.allocate( 1 ); | ||
| 141 | pNew->pValue = va.allocate( 1 ); | ||
| 142 | va.construct( pNew->pValue, v ); | ||
| 143 | nSize++; | ||
| 144 | |||
| 145 | pNew->pNext = pAfter; | ||
| 146 | pNew->pPrev = pPrev; | ||
| 147 | pAfter->pPrev = pNew; | ||
| 148 | pPrev->pNext = pNew; | ||
| 149 | |||
| 150 | return pNew; | ||
| 151 | } | ||
| 152 | |||
| 153 | /** | ||
| 154 | * Erase an item from the list. | ||
| 155 | *@param i (iterator) The item to erase. | ||
| 156 | */ | ||
| 157 | void erase( Link *pLink ) | ||
| 158 | { | ||
| 159 | Link *pCur = pLink; | ||
| 160 | if( pCur == NULL ) return; | ||
| 161 | Link *pPrev = pCur->pPrev; | ||
| 162 | if( pPrev == NULL ) | ||
| 163 | { | ||
| 164 | va.destroy( pCur->pValue ); | ||
| 165 | va.deallocate( pCur->pValue, 1 ); | ||
| 166 | pFirst = pCur->pNext; | ||
| 167 | la.destroy( pCur ); | ||
| 168 | la.deallocate( pCur, 1 ); | ||
| 169 | if( pFirst == NULL ) | ||
| 170 | pLast = NULL; | ||
| 171 | else | ||
| 172 | pFirst->pPrev = NULL; | ||
| 173 | nSize--; | ||
| 174 | } | ||
| 175 | else | ||
| 176 | { | ||
| 177 | va.destroy( pCur->pValue ); | ||
| 178 | va.deallocate( pCur->pValue, 1 ); | ||
| 179 | Link *pTmp = pCur->pNext; | ||
| 180 | la.destroy( pCur ); | ||
| 181 | la.deallocate( pCur, 1 ); | ||
| 182 | pPrev->pNext = pTmp; | ||
| 183 | if( pTmp != NULL ) | ||
| 184 | pTmp->pPrev = pPrev; | ||
| 185 | else | ||
| 186 | pLast = pPrev; | ||
| 187 | nSize--; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | }; | ||
| 191 | /** @endcond */ | ||
| 192 | |||
| 193 | /** | ||
| 194 | * Linked list template container. This class is similar to the stl list | ||
| 195 | * class except for a few minor changes. First, when const, all | ||
| 196 | * members are only accessable const. Second, erasing a location does not | ||
| 197 | * invalidate the iterator used, it simply points to the next valid | ||
| 198 | * location, or end() if there are no more. Other iterators pointing to | ||
| 199 | * the deleted record will, of course, no longer be valid. | ||
| 200 | * | ||
| 201 | *@param value (typename) The type of data to store in your list | ||
| 202 | *@param valuealloc (typename) Memory Allocator for your value type | ||
| 203 | *@param linkalloc (typename) Memory Allocator for the list links. | ||
| 204 | *@extends SharedCore | ||
| 205 | *@ingroup Containers | ||
| 206 | */ | ||
| 207 | template<typename value, typename valuealloc=std::allocator<value>, | ||
| 208 | typename linkalloc=std::allocator<struct ListLink<value> > > | ||
| 209 | class List /** @cond */ : public SharedCore< | ||
| 210 | List<value, valuealloc, linkalloc>, | ||
| 211 | ListCore<value, valuealloc, linkalloc> | ||
| 212 | > /** @endcond */ | ||
| 213 | { | ||
| 214 | private: | ||
| 215 | typedef struct ListLink<value> Link; | ||
| 216 | typedef class List<value, valuealloc, linkalloc> MyType; | ||
| 217 | typedef struct ListCore<value, valuealloc, linkalloc> Core; | ||
| 218 | |||
| 219 | protected: | ||
| 220 | using SharedCore<MyType, Core>::core; | ||
| 221 | using SharedCore<MyType, Core>::_hardCopy; | ||
| 222 | using SharedCore<MyType, Core>::_allocateCore; | ||
| 223 | |||
| 224 | public: | ||
| 225 | struct const_iterator; | ||
| 226 | struct iterator; | ||
| 227 | |||
| 228 | List() | ||
| 229 | { | ||
| 230 | } | ||
| 231 | |||
| 232 | List( const MyType &src ) : | ||
| 233 | SharedCore<MyType, Core >( src ) | ||
| 234 | { | ||
| 235 | } | ||
| 236 | |||
| 237 | List( const value &v ) | ||
| 238 | { | ||
| 239 | append( v ); | ||
| 240 | } | ||
| 241 | |||
| 242 | ~List() | ||
| 243 | { | ||
| 244 | } | ||
| 245 | |||
| 246 | MyType &operator+=( const value &v ) | ||
| 247 | { | ||
| 248 | _hardCopy(); | ||
| 249 | append( v ); | ||
| 250 | return *this; | ||
| 251 | } | ||
| 252 | |||
| 253 | MyType &operator+=( const MyType &src ) | ||
| 254 | { | ||
| 255 | _hardCopy(); | ||
| 256 | append( src ); | ||
| 257 | return *this; | ||
| 258 | } | ||
| 259 | |||
| 260 | MyType operator+( const MyType &src ) | ||
| 261 | { | ||
| 262 | MyType lNew( *this ); | ||
| 263 | lNew += src; | ||
| 264 | return lNew; | ||
| 265 | } | ||
| 266 | |||
| 267 | bool operator==( const MyType &rhs ) const | ||
| 268 | { | ||
| 269 | if( getSize() != rhs.getSize() ) | ||
| 270 | return false; | ||
| 271 | |||
| 272 | for( typename MyType::const_iterator a = begin(), b = rhs.begin(); | ||
| 273 | a; a++, b++ ) | ||
| 274 | { | ||
| 275 | if( *a != *b ) | ||
| 276 | return false; | ||
| 277 | } | ||
| 278 | |||
| 279 | return true; | ||
| 280 | } | ||
| 281 | |||
| 282 | bool operator!=( const MyType &rhs ) const | ||
| 283 | { | ||
| 284 | return !(*this == rhs); | ||
| 285 | } | ||
| 286 | |||
| 287 | /** | ||
| 288 | * Clear the data from the list. | ||
| 289 | */ | ||
| 290 | void clear() | ||
| 291 | { | ||
| 292 | _hardCopy(); | ||
| 293 | core->clear(); | ||
| 294 | } | ||
| 295 | |||
| 296 | MyType &enqueue( const value &v ) | ||
| 297 | { | ||
| 298 | _hardCopy(); | ||
| 299 | append( v ); | ||
| 300 | |||
| 301 | return *this; | ||
| 302 | } | ||
| 303 | |||
| 304 | value dequeue() | ||
| 305 | { | ||
| 306 | // _hardCopy(); erase will call this for me | ||
| 307 | value v = *core->pFirst->pValue; | ||
| 308 | |||
| 309 | erase( begin() ); | ||
| 310 | |||
| 311 | return v; | ||
| 312 | } | ||
| 313 | |||
| 314 | MyType &push( const value &v ) | ||
| 315 | { | ||
| 316 | _hardCopy(); | ||
| 317 | prepend( v ); | ||
| 318 | |||
| 319 | return *this; | ||
| 320 | } | ||
| 321 | |||
| 322 | MyType &pop() | ||
| 323 | { | ||
| 324 | _hardCopy(); | ||
| 325 | erase( begin() ); | ||
| 326 | |||
| 327 | return *this; | ||
| 328 | } | ||
| 329 | |||
| 330 | value peekPop() | ||
| 331 | { | ||
| 332 | value v = first(); | ||
| 333 | pop(); | ||
| 334 | return v; | ||
| 335 | } | ||
| 336 | |||
| 337 | value &peek() | ||
| 338 | { | ||
| 339 | return first(); | ||
| 340 | } | ||
| 341 | |||
| 342 | /** | ||
| 343 | * Append a value to the list. | ||
| 344 | *@param v (const value_type &) The value to append. | ||
| 345 | */ | ||
| 346 | MyType &append( const value &v ) | ||
| 347 | { | ||
| 348 | _hardCopy(); | ||
| 349 | core->append( v ); | ||
| 350 | |||
| 351 | return *this; | ||
| 352 | } | ||
| 353 | |||
| 354 | MyType &append( const MyType &rSrc ) | ||
| 355 | { | ||
| 356 | _hardCopy(); | ||
| 357 | for( typename MyType::const_iterator i = rSrc.begin(); | ||
| 358 | i != rSrc.end(); i++ ) | ||
| 359 | { | ||
| 360 | core->append( *i ); | ||
| 361 | } | ||
| 362 | |||
| 363 | return *this; | ||
| 364 | } | ||
| 365 | |||
| 366 | /** | ||
| 367 | * Prepend a value to the list. | ||
| 368 | *@param v (const value_type &) The value to prepend. | ||
| 369 | */ | ||
| 370 | MyType &prepend( const value &v ) | ||
| 371 | { | ||
| 372 | _hardCopy(); | ||
| 373 | core->prepend( v ); | ||
| 374 | |||
| 375 | return *this; | ||
| 376 | } | ||
| 377 | |||
| 378 | /** | ||
| 379 | * Prepend another list to the front of this one. This will prepend | ||
| 380 | * the rSrc list in reverse order...I may fix that later. | ||
| 381 | */ | ||
| 382 | MyType &prepend( const MyType &rSrc ) | ||
| 383 | { | ||
| 384 | _hardCopy(); | ||
| 385 | for( typename MyType::const_iterator i = rSrc.begin(); | ||
| 386 | i != rSrc.end(); i++ ) | ||
| 387 | { | ||
| 388 | core->prepend( *i ); | ||
| 389 | } | ||
| 390 | |||
| 391 | return *this; | ||
| 392 | } | ||
| 393 | |||
| 394 | MyType &insert( MyType::iterator &i, const value &v ) | ||
| 395 | { | ||
| 396 | _hardCopy(); | ||
| 397 | |||
| 398 | core->insert( i.pLink, v ); | ||
| 399 | |||
| 400 | return *this; | ||
| 401 | } | ||
| 402 | |||
| 403 | template<typename cmptype> | ||
| 404 | void sort( cmptype cmp ) | ||
| 405 | { | ||
| 406 | Heap<value, cmptype, valuealloc> hSort( cmp, getSize() ); | ||
| 407 | for( typename MyType::iterator i = begin(); i; i++ ) | ||
| 408 | { | ||
| 409 | hSort.enqueue( *i ); | ||
| 410 | } | ||
| 411 | clear(); | ||
| 412 | while( !hSort.isEmpty() ) | ||
| 413 | { | ||
| 414 | append( hSort.dequeue() ); | ||
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | void sort() | ||
| 419 | { | ||
| 420 | sort<__basicLTCmp<value> >(); | ||
| 421 | } | ||
| 422 | |||
| 423 | template<typename cmptype> | ||
| 424 | void sort() | ||
| 425 | { | ||
| 426 | Heap<value, cmptype, valuealloc> hSort( getSize() ); | ||
| 427 | for( typename MyType::iterator i = begin(); i; i++ ) | ||
| 428 | { | ||
| 429 | hSort.enqueue( *i ); | ||
| 430 | } | ||
| 431 | clear(); | ||
| 432 | while( !hSort.isEmpty() ) | ||
| 433 | { | ||
| 434 | append( hSort.dequeue() ); | ||
| 435 | } | ||
| 436 | } | ||
| 437 | |||
| 438 | /** | ||
| 439 | * Insert a new item in sort order by searching for the first item that | ||
| 440 | * is larger and inserting this before it, or at the end if none are | ||
| 441 | * larger. If this is the only function used to insert data in the | ||
| 442 | * List all items will be sorted. To use this, the value type must | ||
| 443 | * support the > operator. | ||
| 444 | */ | ||
| 445 | template<typename cmptype> | ||
| 446 | iterator insertSorted( cmptype cmp, const value &v ) | ||
| 447 | { | ||
| 448 | _hardCopy(); | ||
| 449 | if( core->pFirst == NULL ) | ||
| 450 | { | ||
| 451 | // Empty list | ||
| 452 | return iterator( core->append( v ) ); | ||
| 453 | } | ||
| 454 | else | ||
| 455 | { | ||
| 456 | Link *pCur = core->pFirst; | ||
| 457 | for(;;) | ||
| 458 | { | ||
| 459 | if( cmp( v, *(pCur->pValue)) ) | ||
| 460 | { | ||
| 461 | return iterator( core->insert( pCur, v ) ); | ||
| 462 | } | ||
| 463 | pCur = pCur->pNext; | ||
| 464 | if( pCur == NULL ) | ||
| 465 | { | ||
| 466 | return iterator( core->append( v ) ); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 472 | iterator insertSorted( const value &v ) | ||
| 473 | { | ||
| 474 | return insertSorted<__basicLTCmp<value> >( v ); | ||
| 475 | } | ||
| 476 | |||
| 477 | template<typename cmptype> | ||
| 478 | iterator insertSorted( const value &v ) | ||
| 479 | { | ||
| 480 | cmptype cmp; | ||
| 481 | return insertSorted( cmp, v ); | ||
| 482 | } | ||
| 483 | |||
| 484 | /** | ||
| 485 | * An iterator to iterate through your list. | ||
| 486 | */ | ||
| 487 | typedef struct iterator | ||
| 488 | { | ||
| 489 | friend struct const_iterator; | ||
| 490 | friend class List<value, valuealloc, linkalloc>; | ||
| 491 | private: | ||
| 492 | Link *pLink; | ||
| 493 | |||
| 494 | iterator( Link *pLink ) : | ||
| 495 | pLink( pLink ) | ||
| 496 | { | ||
| 497 | } | ||
| 498 | |||
| 499 | public: | ||
| 500 | iterator() : | ||
| 501 | pLink( NULL ) | ||
| 502 | { | ||
| 503 | } | ||
| 504 | |||
| 505 | iterator( const iterator &i ) : | ||
| 506 | pLink( i.pLink ) | ||
| 507 | { | ||
| 508 | } | ||
| 509 | |||
| 510 | /** | ||
| 511 | * Equals comparison operator. | ||
| 512 | *@param oth (const iterator &) The iterator to compare to. | ||
| 513 | *@returns (bool) Are they equal? | ||
| 514 | */ | ||
| 515 | bool operator==( const iterator &oth ) const | ||
| 516 | { | ||
| 517 | return ( pLink == oth.pLink ); | ||
| 518 | } | ||
| 519 | |||
| 520 | /** | ||
| 521 | * Equals comparison operator. | ||
| 522 | *@param pOth (const Link *) The link to compare to. | ||
| 523 | *@returns (bool) Are they equal? | ||
| 524 | */ | ||
| 525 | bool operator==( const Link *pOth ) const | ||
| 526 | { | ||
| 527 | return ( pLink == pOth ); | ||
| 528 | } | ||
| 529 | |||
| 530 | /** | ||
| 531 | * Not equals comparison operator. | ||
| 532 | *@param oth (const iterator &) The iterator to compare to. | ||
| 533 | *@returns (bool) Are they not equal? | ||
| 534 | */ | ||
| 535 | bool operator!=( const iterator &oth ) const | ||
| 536 | { | ||
| 537 | return ( pLink != oth.pLink ); | ||
| 538 | } | ||
| 539 | |||
| 540 | /** | ||
| 541 | * Not equals comparison operator. | ||
| 542 | *@param pOth (const Link *) The link to compare to. | ||
| 543 | *@returns (bool) Are they not equal? | ||
| 544 | */ | ||
| 545 | bool operator!=( const Link *pOth ) const | ||
| 546 | { | ||
| 547 | return ( pLink != pOth ); | ||
| 548 | } | ||
| 549 | |||
| 550 | /** | ||
| 551 | * Dereference operator. | ||
| 552 | *@returns (value_type &) The value. | ||
| 553 | */ | ||
| 554 | value &operator*() | ||
| 555 | { | ||
| 556 | return *(pLink->pValue); | ||
| 557 | } | ||
| 558 | |||
| 559 | /** | ||
| 560 | * Pointer access operator. | ||
| 561 | *@returns (value_type *) A pointer to the value. | ||
| 562 | */ | ||
| 563 | value *operator->() | ||
| 564 | { | ||
| 565 | return pLink->pValue; | ||
| 566 | } | ||
| 567 | |||
| 568 | iterator &operator++() | ||
| 569 | { | ||
| 570 | if( pLink == NULL ) | ||
| 571 | throw Bu::ExceptionBase( | ||
| 572 | "Attempt to iterate past end of list."); | ||
| 573 | pLink = pLink->pNext; | ||
| 574 | return *this; | ||
| 575 | } | ||
| 576 | |||
| 577 | iterator &operator--() | ||
| 578 | { | ||
| 579 | if( pLink == NULL ) | ||
| 580 | throw Bu::ExceptionBase( | ||
| 581 | "Attempt to iterate past begining of list."); | ||
| 582 | pLink = pLink->pPrev; | ||
| 583 | return *this; | ||
| 584 | } | ||
| 585 | |||
| 586 | iterator &operator++( int ) | ||
| 587 | { | ||
| 588 | if( pLink == NULL ) | ||
| 589 | throw Bu::ExceptionBase( | ||
| 590 | "Attempt to iterate past end of list."); | ||
| 591 | pLink = pLink->pNext; | ||
| 592 | return *this; | ||
| 593 | } | ||
| 594 | |||
| 595 | iterator &operator--( int ) | ||
| 596 | { | ||
| 597 | if( pLink == NULL ) | ||
| 598 | throw Bu::ExceptionBase( | ||
| 599 | "Attempt to iterate past begining of list."); | ||
| 600 | pLink = pLink->pPrev; | ||
| 601 | return *this; | ||
| 602 | } | ||
| 603 | |||
| 604 | iterator operator+( int iDelta ) | ||
| 605 | { | ||
| 606 | iterator ret( *this ); | ||
| 607 | for( int j = 0; j < iDelta; j++ ) | ||
| 608 | { | ||
| 609 | if( ret.pLink == NULL ) | ||
| 610 | throw Bu::ExceptionBase( | ||
| 611 | "Attempt to iterate past begining of list."); | ||
| 612 | ret.pLink = ret.pLink->pNext; | ||
| 613 | } | ||
| 614 | return ret; | ||
| 615 | } | ||
| 616 | |||
| 617 | iterator operator-( int iDelta ) | ||
| 618 | { | ||
| 619 | iterator ret( *this ); | ||
| 620 | for( int j = 0; j < iDelta; j++ ) | ||
| 621 | { | ||
| 622 | if( ret.pLink == NULL ) | ||
| 623 | throw Bu::ExceptionBase( | ||
| 624 | "Attempt to iterate past begining of list."); | ||
| 625 | ret.pLink = ret.pLink->pPrev; | ||
| 626 | } | ||
| 627 | return ret; | ||
| 628 | } | ||
| 629 | |||
| 630 | operator bool() | ||
| 631 | { | ||
| 632 | return pLink != NULL; | ||
| 633 | } | ||
| 634 | |||
| 635 | bool isValid() | ||
| 636 | { | ||
| 637 | return pLink != NULL; | ||
| 638 | } | ||
| 639 | |||
| 640 | /** | ||
| 641 | * Assignment operator. | ||
| 642 | *@param oth (const iterator &) The other iterator to set this | ||
| 643 | * one to. | ||
| 644 | */ | ||
| 645 | iterator &operator=( const iterator &oth ) | ||
| 646 | { | ||
| 647 | pLink = oth.pLink; | ||
| 648 | return *this; | ||
| 649 | } | ||
| 650 | } iterator; | ||
| 651 | |||
| 652 | /** | ||
| 653 | *@see iterator | ||
| 654 | */ | ||
| 655 | typedef struct const_iterator | ||
| 656 | { | ||
| 657 | friend class List<value, valuealloc, linkalloc>; | ||
| 658 | private: | ||
| 659 | Link *pLink; | ||
| 660 | |||
| 661 | const_iterator( Link *pLink ) : | ||
| 662 | pLink( pLink ) | ||
| 663 | { | ||
| 664 | } | ||
| 665 | |||
| 666 | public: | ||
| 667 | const_iterator() : | ||
| 668 | pLink( NULL ) | ||
| 669 | { | ||
| 670 | } | ||
| 671 | |||
| 672 | const_iterator( const iterator &i ) : | ||
| 673 | pLink( i.pLink ) | ||
| 674 | { | ||
| 675 | } | ||
| 676 | |||
| 677 | bool operator==( const const_iterator &oth ) const | ||
| 678 | { | ||
| 679 | return ( pLink == oth.pLink ); | ||
| 680 | } | ||
| 681 | |||
| 682 | bool operator==( const Link *pOth ) const | ||
| 683 | { | ||
| 684 | return ( pLink == pOth ); | ||
| 685 | } | ||
| 686 | |||
| 687 | bool operator!=( const const_iterator &oth ) const | ||
| 688 | { | ||
| 689 | return ( pLink != oth.pLink ); | ||
| 690 | } | ||
| 691 | |||
| 692 | bool operator!=( const Link *pOth ) const | ||
| 693 | { | ||
| 694 | return ( pLink != pOth ); | ||
| 695 | } | ||
| 696 | |||
| 697 | const value &operator*() | ||
| 698 | { | ||
| 699 | return *(pLink->pValue); | ||
| 700 | } | ||
| 701 | |||
| 702 | const value *operator->() | ||
| 703 | { | ||
| 704 | return pLink->pValue; | ||
| 705 | } | ||
| 706 | |||
| 707 | const_iterator &operator++() | ||
| 708 | { | ||
| 709 | if( pLink == NULL ) | ||
| 710 | throw Bu::ExceptionBase( | ||
| 711 | "Attempt to iterate past end of list."); | ||
| 712 | pLink = pLink->pNext; | ||
| 713 | return *this; | ||
| 714 | } | ||
| 715 | |||
| 716 | const_iterator &operator--() | ||
| 717 | { | ||
| 718 | if( pLink == NULL ) | ||
| 719 | throw Bu::ExceptionBase( | ||
| 720 | "Attempt to iterate past begining of list."); | ||
| 721 | pLink = pLink->pPrev; | ||
| 722 | return *this; | ||
| 723 | } | ||
| 724 | |||
| 725 | const_iterator &operator++( int ) | ||
| 726 | { | ||
| 727 | if( pLink == NULL ) | ||
| 728 | throw Bu::ExceptionBase( | ||
| 729 | "Attempt to iterate past end of list."); | ||
| 730 | pLink = pLink->pNext; | ||
| 731 | return *this; | ||
| 732 | } | ||
| 733 | |||
| 734 | const_iterator &operator--( int ) | ||
| 735 | { | ||
| 736 | if( pLink == NULL ) | ||
| 737 | throw Bu::ExceptionBase( | ||
| 738 | "Attempt to iterate past begining of list."); | ||
| 739 | pLink = pLink->pPrev; | ||
| 740 | return *this; | ||
| 741 | } | ||
| 742 | |||
| 743 | const_iterator operator+( int iDelta ) | ||
| 744 | { | ||
| 745 | const_iterator ret( *this ); | ||
| 746 | for( int j = 0; j < iDelta; j++ ) | ||
| 747 | { | ||
| 748 | if( ret.pLink == NULL ) | ||
| 749 | throw Bu::ExceptionBase( | ||
| 750 | "Attempt to iterate past begining of list."); | ||
| 751 | ret.pLink = ret.pLink->pNext; | ||
| 752 | } | ||
| 753 | return ret; | ||
| 754 | } | ||
| 755 | |||
| 756 | const_iterator operator-( int iDelta ) | ||
| 757 | { | ||
| 758 | const_iterator ret( *this ); | ||
| 759 | for( int j = 0; j < iDelta; j++ ) | ||
| 760 | { | ||
| 761 | if( ret.pLink == NULL ) | ||
| 762 | throw Bu::ExceptionBase( | ||
| 763 | "Attempt to iterate past begining of list."); | ||
| 764 | ret.pLink = ret.pLink->pPrev; | ||
| 765 | } | ||
| 766 | return ret; | ||
| 767 | } | ||
| 768 | |||
| 769 | const_iterator &operator=( const iterator &oth ) | ||
| 770 | { | ||
| 771 | pLink = oth.pLink; | ||
| 772 | return *this; | ||
| 773 | } | ||
| 774 | |||
| 775 | const_iterator &operator=( const const_iterator &oth ) | ||
| 776 | { | ||
| 777 | pLink = oth.pLink; | ||
| 778 | return *this; | ||
| 779 | } | ||
| 780 | |||
| 781 | operator bool() | ||
| 782 | { | ||
| 783 | return pLink != NULL; | ||
| 784 | } | ||
| 785 | |||
| 786 | bool isValid() | ||
| 787 | { | ||
| 788 | return pLink != NULL; | ||
| 789 | } | ||
| 790 | } const_iterator; | ||
| 791 | |||
| 792 | /** | ||
| 793 | * Get an iterator pointing to the first item in the list. | ||
| 794 | *@returns (iterator) | ||
| 795 | */ | ||
| 796 | iterator begin() | ||
| 797 | { | ||
| 798 | _hardCopy(); | ||
| 799 | return iterator( core->pFirst ); | ||
| 800 | } | ||
| 801 | |||
| 802 | /** | ||
| 803 | * Get a const iterator pointing to the first item in the list. | ||
| 804 | *@returns (const const_iterator) | ||
| 805 | */ | ||
| 806 | const_iterator begin() const | ||
| 807 | { | ||
| 808 | return const_iterator( core->pFirst ); | ||
| 809 | } | ||
| 810 | |||
| 811 | /** | ||
| 812 | * Get an iterator pointing to a place just past the last item in | ||
| 813 | * the list. | ||
| 814 | *@returns (const Link *) | ||
| 815 | */ | ||
| 816 | const iterator end() | ||
| 817 | { | ||
| 818 | return iterator( NULL ); | ||
| 819 | } | ||
| 820 | |||
| 821 | /** | ||
| 822 | * Get an iterator pointing to a place just past the last item in | ||
| 823 | * the list. | ||
| 824 | *@returns (const Link *) | ||
| 825 | */ | ||
| 826 | const const_iterator end() const | ||
| 827 | { | ||
| 828 | return const_iterator( NULL ); | ||
| 829 | } | ||
| 830 | |||
| 831 | /** | ||
| 832 | * Erase an item from the list. | ||
| 833 | *@param i (iterator) The item to erase. | ||
| 834 | */ | ||
| 835 | MyType &erase( iterator i ) | ||
| 836 | { | ||
| 837 | _hardCopy(); | ||
| 838 | core->erase( i.pLink ); | ||
| 839 | |||
| 840 | return *this; | ||
| 841 | } | ||
| 842 | |||
| 843 | /** | ||
| 844 | * Erase an item from the list. | ||
| 845 | *@param i (iterator) The item to erase. | ||
| 846 | */ | ||
| 847 | MyType &erase( const_iterator i ) | ||
| 848 | { | ||
| 849 | _hardCopy(); | ||
| 850 | core->erase( i.pLink ); | ||
| 851 | |||
| 852 | return *this; | ||
| 853 | } | ||
| 854 | |||
| 855 | /** | ||
| 856 | * Erase an item from the list if you already know the item. | ||
| 857 | *@param v The item to find and erase. | ||
| 858 | */ | ||
| 859 | MyType &erase( const value &v ) | ||
| 860 | { | ||
| 861 | for( const_iterator i = begin(); i != end(); i++ ) | ||
| 862 | { | ||
| 863 | if( (*i) == v ) | ||
| 864 | { | ||
| 865 | erase( i ); | ||
| 866 | return *this; | ||
| 867 | } | ||
| 868 | } | ||
| 869 | |||
| 870 | return *this; | ||
| 871 | } | ||
| 872 | |||
| 873 | iterator find( const value &v ) | ||
| 874 | { | ||
| 875 | for( iterator i = begin(); i; i++ ) | ||
| 876 | { | ||
| 877 | if( (*i) == v ) | ||
| 878 | return i; | ||
| 879 | } | ||
| 880 | |||
| 881 | return end(); | ||
| 882 | } | ||
| 883 | |||
| 884 | const_iterator find( const value &v ) const | ||
| 885 | { | ||
| 886 | for( const_iterator i = begin(); i; i++ ) | ||
| 887 | { | ||
| 888 | if( (*i) == v ) | ||
| 889 | return i; | ||
| 890 | } | ||
| 891 | |||
| 892 | return end(); | ||
| 893 | } | ||
| 894 | |||
| 895 | /** | ||
| 896 | * Get the current size of the list. | ||
| 897 | *@returns (int) The current size of the list. | ||
| 898 | */ | ||
| 899 | long getSize() const | ||
| 900 | { | ||
| 901 | return core->nSize; | ||
| 902 | } | ||
| 903 | |||
| 904 | /** | ||
| 905 | * Get the first item in the list. | ||
| 906 | *@returns (value_type &) The first item in the list. | ||
| 907 | */ | ||
| 908 | value &first() | ||
| 909 | { | ||
| 910 | if( core->pFirst->pValue == NULL ) | ||
| 911 | throw Bu::ExceptionBase("Attempt to read first element from empty list."); | ||
| 912 | _hardCopy(); | ||
| 913 | return *core->pFirst->pValue; | ||
| 914 | } | ||
| 915 | |||
| 916 | /** | ||
| 917 | * Get the first item in the list. | ||
| 918 | *@returns (const value_type &) The first item in the list. | ||
| 919 | */ | ||
| 920 | const value &first() const | ||
| 921 | { | ||
| 922 | if( core->pFirst->pValue == NULL ) | ||
| 923 | throw Bu::ExceptionBase("Attempt to read first element from empty list."); | ||
| 924 | return *core->pFirst->pValue; | ||
| 925 | } | ||
| 926 | |||
| 927 | /** | ||
| 928 | * Get the last item in the list. | ||
| 929 | *@returns (value_type &) The last item in the list. | ||
| 930 | */ | ||
| 931 | value &last() | ||
| 932 | { | ||
| 933 | _hardCopy(); | ||
| 934 | return *core->pLast->pValue; | ||
| 935 | } | ||
| 936 | |||
| 937 | /** | ||
| 938 | * Get the last item in the list. | ||
| 939 | *@returns (const value_type &) The last item in the list. | ||
| 940 | */ | ||
| 941 | const value &last() const | ||
| 942 | { | ||
| 943 | return *core->pLast->pValue; | ||
| 944 | } | ||
| 945 | |||
| 946 | bool isEmpty() const | ||
| 947 | { | ||
| 948 | return (core->nSize == 0); | ||
| 949 | } | ||
| 950 | |||
| 951 | protected: | ||
| 952 | virtual Core *_copyCore( Core *src ) | ||
| 953 | { | ||
| 954 | Core *pRet = _allocateCore(); | ||
| 955 | for( Link *pCur = src->pFirst; pCur; pCur = pCur->pNext ) | ||
| 956 | { | ||
| 957 | pRet->append( *pCur->pValue ); | ||
| 958 | } | ||
| 959 | return pRet; | ||
| 960 | } | ||
| 961 | |||
| 962 | private: | ||
| 963 | }; | ||
| 964 | |||
| 965 | class Formatter; | ||
| 966 | Formatter &operator<<( Formatter &rOut, char *sStr ); | ||
| 967 | Formatter &operator<<( Formatter &rOut, signed char c ); | ||
| 968 | template<typename a, typename b, typename c> | ||
| 969 | Formatter &operator<<( Formatter &f, const Bu::List<a,b,c> &l ) | ||
| 970 | { | ||
| 971 | f << '['; | ||
| 972 | for( typename Bu::List<a,b,c>::const_iterator i = l.begin(); i; i++ ) | ||
| 973 | { | ||
| 974 | if( i != l.begin() ) | ||
| 975 | f << ", "; | ||
| 976 | f << *i; | ||
| 977 | } | ||
| 978 | f << ']'; | ||
| 979 | |||
| 980 | return f; | ||
| 981 | } | ||
| 982 | |||
| 983 | template<typename value, typename a, typename b> | ||
| 984 | ArchiveBase &operator<<( ArchiveBase &ar, const List<value,a,b> &h ) | ||
| 985 | { | ||
| 986 | ar << h.getSize(); | ||
| 987 | for( typename List<value>::const_iterator i = h.begin(); i != h.end(); i++ ) | ||
| 988 | { | ||
| 989 | ar << (*i); | ||
| 990 | } | ||
| 991 | |||
| 992 | return ar; | ||
| 993 | } | ||
| 994 | |||
| 995 | template<typename value, typename a, typename b> | ||
| 996 | ArchiveBase &operator>>( ArchiveBase &ar, List<value,a,b> &h ) | ||
| 997 | { | ||
| 998 | h.clear(); | ||
| 999 | long nSize; | ||
| 1000 | ar >> nSize; | ||
| 1001 | |||
| 1002 | for( long j = 0; j < nSize; j++ ) | ||
| 1003 | { | ||
| 1004 | value v; | ||
| 1005 | ar >> v; | ||
| 1006 | h.append( v ); | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | return ar; | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | } | ||
| 1013 | |||
| 1014 | #endif | ||
| diff --git a/src/stable/logger.cpp b/src/stable/logger.cpp new file mode 100644 index 0000000..8e46390 --- /dev/null +++ b/src/stable/logger.cpp | |||
| @@ -0,0 +1,209 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/logger.h" | ||
| 9 | #include <stdarg.h> | ||
| 10 | #include <time.h> | ||
| 11 | #include <stdio.h> | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include <unistd.h> | ||
| 14 | |||
| 15 | Bu::Logger::Logger() | ||
| 16 | { | ||
| 17 | setFormat("%t"); | ||
| 18 | } | ||
| 19 | |||
| 20 | Bu::Logger::~Logger() | ||
| 21 | { | ||
| 22 | } | ||
| 23 | |||
| 24 | void Bu::Logger::log( uint32_t nLevel, const char *sFile, const char *sFunction, int nLine, const char *sFormat, ...) | ||
| 25 | { | ||
| 26 | #ifndef WIN32 | ||
| 27 | if( (nLevel&nLevelMask) == 0 ) | ||
| 28 | return; | ||
| 29 | |||
| 30 | va_list ap; | ||
| 31 | va_start( ap, sFormat ); | ||
| 32 | char *text; | ||
| 33 | if( vasprintf( &text, sFormat, ap ) < 0 ) | ||
| 34 | { | ||
| 35 | printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WTF?\n"); | ||
| 36 | return; | ||
| 37 | } | ||
| 38 | va_end(ap); | ||
| 39 | |||
| 40 | time_t t = time(NULL); | ||
| 41 | |||
| 42 | char *line = NULL; | ||
| 43 | struct tm *pTime; | ||
| 44 | pTime = localtime( &t ); | ||
| 45 | if ( asprintf( | ||
| 46 | &line, | ||
| 47 | sLogFormat.getStr(), | ||
| 48 | pTime->tm_year+1900, | ||
| 49 | pTime->tm_mon+1, | ||
| 50 | pTime->tm_mday, | ||
| 51 | pTime->tm_hour, | ||
| 52 | pTime->tm_min, | ||
| 53 | pTime->tm_sec, | ||
| 54 | nLevel, | ||
| 55 | sFile, | ||
| 56 | nLine, | ||
| 57 | text, | ||
| 58 | sFunction | ||
| 59 | ) < 0 ) | ||
| 60 | { | ||
| 61 | //printf("LOGGER: ERROR ALLOCATING STRING: %s\n", strerror( errno ) ); | ||
| 62 | return; | ||
| 63 | } | ||
| 64 | write( fileno(stdout), line, strlen(line) ); | ||
| 65 | free( text ); | ||
| 66 | free( line ); | ||
| 67 | #else | ||
| 68 | #warning Bu::Logger::log IS A STUB for WIN32!!!! | ||
| 69 | #endif | ||
| 70 | } | ||
| 71 | |||
| 72 | void Bu::Logger::setFormat( const Bu::String &str ) | ||
| 73 | { | ||
| 74 | sLogFormat = ""; | ||
| 75 | |||
| 76 | static char fmts[][4]={ | ||
| 77 | {'y', 'd', '0', '1'}, | ||
| 78 | {'m', 'd', '0', '2'}, | ||
| 79 | {'d', 'd', '0', '3'}, | ||
| 80 | {'h', 'd', '0', '4'}, | ||
| 81 | {'M', 'd', '0', '5'}, | ||
| 82 | {'s', 'd', '0', '6'}, | ||
| 83 | {'L', 'd', '0', '7'}, | ||
| 84 | {'f', 's', '0', '8'}, | ||
| 85 | {'l', 'd', '0', '9'}, | ||
| 86 | {'t', 's', '1', '0'}, | ||
| 87 | {'F', 's', '1', '1'}, | ||
| 88 | {'\0', '\0', '\0', '\0'}, | ||
| 89 | }; | ||
| 90 | |||
| 91 | for( const char *s = str.getStr(); *s; s++ ) | ||
| 92 | { | ||
| 93 | if( *s == '%' ) | ||
| 94 | { | ||
| 95 | sLogFormat += '%'; | ||
| 96 | Bu::String sBuf; | ||
| 97 | for(;;) | ||
| 98 | { | ||
| 99 | s++; | ||
| 100 | int l; | ||
| 101 | for( l = 0;; l++ ) | ||
| 102 | { | ||
| 103 | if( fmts[l][0] == '\0' ) | ||
| 104 | { | ||
| 105 | sBuf += *s; | ||
| 106 | break; | ||
| 107 | } | ||
| 108 | else if( *s == fmts[l][0] ) | ||
| 109 | { | ||
| 110 | sLogFormat += fmts[l][2]; | ||
| 111 | sLogFormat += fmts[l][3]; | ||
| 112 | sLogFormat += '$'; | ||
| 113 | sLogFormat += sBuf; | ||
| 114 | sLogFormat += fmts[l][1]; | ||
| 115 | break; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | if( fmts[l][0] != '\0' ) | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | else | ||
| 123 | { | ||
| 124 | sLogFormat += *s; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | sLogFormat += '\n'; | ||
| 128 | |||
| 129 | //write( fileno(stdout), sLogFormat.getStr(), sLogFormat.getSize() ); | ||
| 130 | } | ||
| 131 | |||
| 132 | void Bu::Logger::setMask( uint32_t n ) | ||
| 133 | { | ||
| 134 | nLevelMask = n; | ||
| 135 | } | ||
| 136 | |||
| 137 | uint32_t Bu::Logger::getMask() | ||
| 138 | { | ||
| 139 | return nLevelMask; | ||
| 140 | } | ||
| 141 | |||
| 142 | void Bu::Logger::setLevel( uint32_t n ) | ||
| 143 | { | ||
| 144 | int j; | ||
| 145 | for( j = 31; j > 0; j-- ) | ||
| 146 | { | ||
| 147 | if( (n&(1<<j)) ) | ||
| 148 | { | ||
| 149 | for(; j >= 0; j-- ) | ||
| 150 | { | ||
| 151 | n |= (1<<j); | ||
| 152 | } | ||
| 153 | nLevelMask = n; | ||
| 154 | return; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | void Bu::Logger::hexDump( uint32_t nLevel, const char *sFile, | ||
| 160 | const char *sFunction, int nLine, const void *pDataV, long nDataLen, | ||
| 161 | const char *lpName ) | ||
| 162 | { | ||
| 163 | if( (nLevel&nLevelMask) == 0 ) | ||
| 164 | return; | ||
| 165 | |||
| 166 | log( nLevel, sFile, sFunction, nLine, "Displaying %ld bytes of %s.", nDataLen, lpName ); | ||
| 167 | const unsigned char *pData = (const unsigned char *)pDataV; | ||
| 168 | int j = 0; | ||
| 169 | Bu::String sBorder; | ||
| 170 | for( int l = 0; l < 8*3+2*8+2+5; l++ ) sBorder += ((l!=11&&l!=37)?("-"):("+")); | ||
| 171 | log( nLevel, sFile, sFunction, nLine, sBorder.getStr() ); | ||
| 172 | Bu::String sLine; | ||
| 173 | for(;;) | ||
| 174 | { | ||
| 175 | { | ||
| 176 | char buf[16]; | ||
| 177 | sprintf( buf, "%010d | ", j ); | ||
| 178 | sLine += buf; | ||
| 179 | } | ||
| 180 | int kmax = 8; | ||
| 181 | if( nDataLen-j < 8 ) kmax = nDataLen-j; | ||
| 182 | for(int k = 0; k < 8; k++ ) | ||
| 183 | { | ||
| 184 | if( k < kmax ) | ||
| 185 | { | ||
| 186 | char buf[4]; | ||
| 187 | sprintf( buf, "%02X ", (int)((unsigned char)pData[j+k]) ); | ||
| 188 | sLine += buf; | ||
| 189 | } | ||
| 190 | else | ||
| 191 | { | ||
| 192 | sLine += "-- "; | ||
| 193 | } | ||
| 194 | } | ||
| 195 | sLine += "| "; | ||
| 196 | for(int k = 0; k < kmax; k++ ) | ||
| 197 | { | ||
| 198 | char buf[3]; | ||
| 199 | sprintf( buf, "%c", (pData[j+k]>32 && pData[j+k]<=128)?(pData[j+k]):('.') ); | ||
| 200 | sLine += buf; | ||
| 201 | } | ||
| 202 | log( nLevel, sFile, sFunction, nLine, sLine.getStr() ); | ||
| 203 | sLine = ""; | ||
| 204 | j += kmax; | ||
| 205 | if( j >= nDataLen ) break; | ||
| 206 | } | ||
| 207 | log( nLevel, sFile, sFunction, nLine, sBorder.getStr() ); | ||
| 208 | } | ||
| 209 | |||
| diff --git a/src/stable/logger.h b/src/stable/logger.h new file mode 100644 index 0000000..5c1352b --- /dev/null +++ b/src/stable/logger.h | |||
| @@ -0,0 +1,125 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_LOGGER_H | ||
| 9 | #define BU_LOGGER_H | ||
| 10 | |||
| 11 | #include "bu/singleton.h" | ||
| 12 | #include "bu/string.h" | ||
| 13 | |||
| 14 | namespace Bu | ||
| 15 | { | ||
| 16 | /** | ||
| 17 | * Simple logging facility. All output goes straight to stdout, unlike the | ||
| 18 | * old multi-log system. Generally we expect any program complex enough to | ||
| 19 | * want to use this will have other facilities for processing the logging | ||
| 20 | * output, but if we need it we can add other output methods. | ||
| 21 | * | ||
| 22 | * Currently implemented as a singleton to avoid clutter with globals, you | ||
| 23 | * generally never want to use the logging system directly, it's annoying. | ||
| 24 | * Instead use the handy macros lineLog, setLogMask, setLogFormat, and | ||
| 25 | * setLogLevel. They do all the real work for you. | ||
| 26 | * | ||
| 27 | * In the log format, you can specify extra information that will be written | ||
| 28 | * to the log with every message, and extras in printf style. Use %X flags | ||
| 29 | * where X is one of the following: | ||
| 30 | * - L - Logging level of the log message (not the current mask) | ||
| 31 | * - y - Full year | ||
| 32 | * - m - Month | ||
| 33 | * - d - Day of month | ||
| 34 | * - h - Hour (24-hour format) | ||
| 35 | * - M - Minutes | ||
| 36 | * - s - Seconds | ||
| 37 | * - f - Source file | ||
| 38 | * - l - Line number | ||
| 39 | * - F - function name | ||
| 40 | * - t - Text of message (usually important) | ||
| 41 | * | ||
| 42 | * You can include anything extra that you would like, a newline will always | ||
| 43 | * be added automatically, so no need to worry about that. You can also | ||
| 44 | * include any extra printf style formatting that you would like, for | ||
| 45 | * example: "%h:%02M:%02s" for the time 4:02:09 instead of 4:2:9. | ||
| 46 | * | ||
| 47 | * It's generally handy to create an enum of values you use as levels during | ||
| 48 | * program execution (such as error, warning, info, debug, etc). These | ||
| 49 | * levels should be treated as bitflags, and the most desirable messages, | ||
| 50 | * i.e. serious errors and the like should be low order (0x01), and the much | ||
| 51 | * less desirable messages, like debugging info, should be higher order | ||
| 52 | * (0xF0). During operation you can then set either an explicit mask, | ||
| 53 | * selecting just the levels that you would like to see printed, or set the | ||
| 54 | * mask using the setLevel helper function, which simulates verbosity | ||
| 55 | * levels, enabling every flag lower order than the highest order set bit | ||
| 56 | * passed. I.E. if you had the following enumerated levels: | ||
| 57 | * | ||
| 58 | *@code | ||
| 59 | enum { | ||
| 60 | logError = 0x01, | ||
| 61 | logWarning = 0x02, | ||
| 62 | logInfo = 0x04, | ||
| 63 | logDebug = 0x08 | ||
| 64 | }; | ||
| 65 | @endcode | ||
| 66 | * And you set the mask with setMask( logInfo ) the only messages you would | ||
| 67 | * see are the ones catagorized logInfo. However, if you used | ||
| 68 | * setLevel( logInfo ) then you would see logInfo, logWarning, and logError | ||
| 69 | * type messages, since they are lower order. | ||
| 70 | */ | ||
| 71 | class Logger : public Bu::Singleton<Bu::Logger> | ||
| 72 | { | ||
| 73 | friend class Bu::Singleton<Bu::Logger>; | ||
| 74 | private: | ||
| 75 | Logger(); | ||
| 76 | virtual ~Logger(); | ||
| 77 | |||
| 78 | public: | ||
| 79 | void log( uint32_t nLevel, const char *sFile, const char *sFunction, int nLine, const char *sFormat, ...); | ||
| 80 | |||
| 81 | void setFormat( const Bu::String &str ); | ||
| 82 | void setMask( uint32_t n ); | ||
| 83 | void setLevel( uint32_t n ); | ||
| 84 | uint32_t getMask(); | ||
| 85 | |||
| 86 | void hexDump( uint32_t nLevel, const char *sFile, const char *sFunction, int nLine, const void *pData, long nDataLen, const char *lpName ); | ||
| 87 | |||
| 88 | private: | ||
| 89 | Bu::String sLogFormat; | ||
| 90 | uint32_t nLevelMask; | ||
| 91 | }; | ||
| 92 | } | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Use Bu::Logger to log a message at the given level and with the given message | ||
| 96 | * using printf style formatting, and include extra data such as the current | ||
| 97 | * file, line number, and function. | ||
| 98 | */ | ||
| 99 | #define lineLog( nLevel, sFrmt, ...) \ | ||
| 100 | Bu::Logger::getInstance().log( nLevel, __FILE__, __PRETTY_FUNCTION__, __LINE__, sFrmt, ##__VA_ARGS__ ) | ||
| 101 | |||
| 102 | #define logHexDump( nLevel, pData, iSize, sName ) \ | ||
| 103 | Bu::Logger::getInstance().hexDump( nLevel, __FILE__, __PRETTY_FUNCTION__, __LINE__, pData, iSize, sName ) | ||
| 104 | |||
| 105 | /** | ||
| 106 | * Set the Bu::Logger logging mask directly. See Bu::Logger::setMask for | ||
| 107 | * details. | ||
| 108 | */ | ||
| 109 | #define setLogMask( nLevel ) \ | ||
| 110 | Bu::Logger::getInstance().setMask( nLevel ) | ||
| 111 | |||
| 112 | /** | ||
| 113 | * Set the Bu::Logger format. See Bu::Logger::setFormat for details. | ||
| 114 | */ | ||
| 115 | #define setLogFormat( sFrmt ) \ | ||
| 116 | Bu::Logger::getInstance().setFormat( sFrmt ) | ||
| 117 | |||
| 118 | /** | ||
| 119 | * Set the Bu::Logger logging mask simulating levels. See Bu::Logger::setLevel | ||
| 120 | * for details. | ||
| 121 | */ | ||
| 122 | #define setLogLevel( nLevel ) \ | ||
| 123 | Bu::Logger::getInstance().setLevel( nLevel ) | ||
| 124 | |||
| 125 | #endif | ||
| diff --git a/src/stable/lzma.cpp b/src/stable/lzma.cpp new file mode 100644 index 0000000..6ed0806 --- /dev/null +++ b/src/stable/lzma.cpp | |||
| @@ -0,0 +1,248 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/lzma.h" | ||
| 9 | #include "bu/trace.h" | ||
| 10 | |||
| 11 | #include <lzma.h> | ||
| 12 | |||
| 13 | #define pState ((lzma_stream *)prState) | ||
| 14 | |||
| 15 | using namespace Bu; | ||
| 16 | |||
| 17 | Bu::Lzma::Lzma( Bu::Stream &rNext, int nCompression, Format eFmt ) : | ||
| 18 | Bu::Filter( rNext ), | ||
| 19 | prState( NULL ), | ||
| 20 | nCompression( nCompression ), | ||
| 21 | sTotalOut( 0 ), | ||
| 22 | eFmt( eFmt ), | ||
| 23 | bEos( false ) | ||
| 24 | { | ||
| 25 | TRACE( nCompression ); | ||
| 26 | start(); | ||
| 27 | } | ||
| 28 | |||
| 29 | Bu::Lzma::~Lzma() | ||
| 30 | { | ||
| 31 | TRACE(); | ||
| 32 | stop(); | ||
| 33 | } | ||
| 34 | |||
| 35 | void Bu::Lzma::start() | ||
| 36 | { | ||
| 37 | TRACE(); | ||
| 38 | nBufSize = 64*1024; | ||
| 39 | pBuf = new char[nBufSize]; | ||
| 40 | } | ||
| 41 | |||
| 42 | Bu::size Bu::Lzma::stop() | ||
| 43 | { | ||
| 44 | TRACE(); | ||
| 45 | if( pState ) | ||
| 46 | { | ||
| 47 | if( bReading ) | ||
| 48 | { | ||
| 49 | lzma_end( pState ); | ||
| 50 | delete[] pBuf; | ||
| 51 | pBuf = NULL; | ||
| 52 | delete pState; | ||
| 53 | prState = NULL; | ||
| 54 | return 0; | ||
| 55 | } | ||
| 56 | else | ||
| 57 | { | ||
| 58 | for(;;) | ||
| 59 | { | ||
| 60 | pState->next_in = NULL; | ||
| 61 | pState->avail_in = 0; | ||
| 62 | pState->avail_out = nBufSize; | ||
| 63 | pState->next_out = (uint8_t *)pBuf; | ||
| 64 | int res = lzma_code( pState, LZMA_FINISH ); | ||
| 65 | if( pState->avail_out < nBufSize ) | ||
| 66 | { | ||
| 67 | sTotalOut += rNext.write( pBuf, nBufSize-pState->avail_out ); | ||
| 68 | } | ||
| 69 | if( res == LZMA_STREAM_END ) | ||
| 70 | break; | ||
| 71 | } | ||
| 72 | lzma_end( pState ); | ||
| 73 | delete[] pBuf; | ||
| 74 | pBuf = NULL; | ||
| 75 | delete pState; | ||
| 76 | prState = NULL; | ||
| 77 | return sTotalOut; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | |||
| 83 | void Bu::Lzma::lzmaError( int code ) | ||
| 84 | { | ||
| 85 | TRACE( code ); | ||
| 86 | switch( code ) | ||
| 87 | { | ||
| 88 | case LZMA_OK: | ||
| 89 | case LZMA_STREAM_END: | ||
| 90 | case LZMA_NO_CHECK: | ||
| 91 | case LZMA_UNSUPPORTED_CHECK: | ||
| 92 | break; | ||
| 93 | |||
| 94 | case LZMA_MEM_ERROR: | ||
| 95 | throw ExceptionBase("Lzma: Memory allocation error."); | ||
| 96 | |||
| 97 | case LZMA_MEMLIMIT_ERROR: | ||
| 98 | throw ExceptionBase("Lzma: Memory usage limit was reached."); | ||
| 99 | |||
| 100 | case LZMA_FORMAT_ERROR: | ||
| 101 | throw ExceptionBase("Lzma: File format not recognized."); | ||
| 102 | |||
| 103 | case LZMA_OPTIONS_ERROR: | ||
| 104 | throw ExceptionBase("Lzma: Invalid or unsupported options."); | ||
| 105 | |||
| 106 | case LZMA_DATA_ERROR: | ||
| 107 | throw ExceptionBase("Lzma: Data is corrupt."); | ||
| 108 | |||
| 109 | case LZMA_BUF_ERROR: | ||
| 110 | throw ExceptionBase("Lzma: No progress is possible."); | ||
| 111 | |||
| 112 | case LZMA_PROG_ERROR: | ||
| 113 | throw ExceptionBase("Lzma: Programming error."); | ||
| 114 | |||
| 115 | default: | ||
| 116 | throw ExceptionBase("Lzma: Unknown error encountered." ); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | Bu::size Bu::Lzma::read( void *pData, Bu::size nBytes ) | ||
| 121 | { | ||
| 122 | TRACE( pData, nBytes ); | ||
| 123 | if( !pState ) | ||
| 124 | { | ||
| 125 | prState = new ::lzma_stream; | ||
| 126 | lzma_stream zEmpty = LZMA_STREAM_INIT; | ||
| 127 | Bu::memcpy( prState, &zEmpty, sizeof(lzma_stream) ); | ||
| 128 | |||
| 129 | bReading = true; | ||
| 130 | lzmaError( lzma_auto_decoder( pState, UINT64_MAX, 0 ) ); | ||
| 131 | pState->next_in = (uint8_t *)pBuf; | ||
| 132 | pState->avail_in = 0; | ||
| 133 | } | ||
| 134 | if( bReading == false ) | ||
| 135 | throw ExceptionBase("This lzma filter is in writing mode, you can't read."); | ||
| 136 | |||
| 137 | int nRead = 0; | ||
| 138 | int nReadTotal = pState->total_out; | ||
| 139 | pState->next_out = (uint8_t *)pData; | ||
| 140 | pState->avail_out = nBytes; | ||
| 141 | for(;;) | ||
| 142 | { | ||
| 143 | int ret = lzma_code( pState, LZMA_RUN ); | ||
| 144 | printf("inflate returned %d; avail in=%d, out=%d\n", ret, | ||
| 145 | pState->avail_in, pState->avail_out ); | ||
| 146 | |||
| 147 | nReadTotal += nRead-pState->avail_out; | ||
| 148 | |||
| 149 | if( ret == LZMA_STREAM_END ) | ||
| 150 | { | ||
| 151 | bEos = true; | ||
| 152 | if( pState->avail_in > 0 ) | ||
| 153 | { | ||
| 154 | if( rNext.isSeekable() ) | ||
| 155 | { | ||
| 156 | rNext.seek( -pState->avail_in ); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | return nBytes-pState->avail_out; | ||
| 160 | } | ||
| 161 | // if( ret != LZMA_BUF_ERROR ) | ||
| 162 | lzmaError( ret ); | ||
| 163 | |||
| 164 | if( pState->avail_out ) | ||
| 165 | { | ||
| 166 | if( pState->avail_in == 0 ) | ||
| 167 | { | ||
| 168 | nRead = rNext.read( pBuf, nBufSize ); | ||
| 169 | if( nRead == 0 && rNext.isEos() ) | ||
| 170 | { | ||
| 171 | throw Bu::ExceptionBase("Premature end of underlying " | ||
| 172 | "stream found reading deflate stream."); | ||
| 173 | } | ||
| 174 | pState->next_in = (uint8_t *)pBuf; | ||
| 175 | pState->avail_in = nRead; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | else | ||
| 179 | { | ||
| 180 | return nBytes-pState->avail_out; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | return 0; | ||
| 184 | } | ||
| 185 | |||
| 186 | Bu::size Bu::Lzma::write( const void *pData, Bu::size nBytes ) | ||
| 187 | { | ||
| 188 | TRACE( pData, nBytes ); | ||
| 189 | if( !pState ) | ||
| 190 | { | ||
| 191 | prState = new ::lzma_stream; | ||
| 192 | lzma_stream zEmpty = LZMA_STREAM_INIT; | ||
| 193 | Bu::memcpy( prState, &zEmpty, sizeof(lzma_stream) ); | ||
| 194 | |||
| 195 | bReading = false; | ||
| 196 | if( eFmt == Xz ) | ||
| 197 | lzmaError( | ||
| 198 | lzma_easy_encoder( pState, nCompression, LZMA_CHECK_CRC64 ) | ||
| 199 | ); | ||
| 200 | else if( eFmt == LzmaAlone ) | ||
| 201 | { | ||
| 202 | lzma_options_lzma opt; | ||
| 203 | lzma_lzma_preset( &opt, nCompression ); | ||
| 204 | lzmaError( lzma_alone_encoder( pState, &opt ) ); | ||
| 205 | } | ||
| 206 | else | ||
| 207 | throw Bu::ExceptionBase("Invalid format for lzma."); | ||
| 208 | } | ||
| 209 | if( bReading == true ) | ||
| 210 | throw ExceptionBase("This lzma filter is in reading mode, you can't write."); | ||
| 211 | |||
| 212 | pState->next_in = (uint8_t *)pData; | ||
| 213 | pState->avail_in = nBytes; | ||
| 214 | for(;;) | ||
| 215 | { | ||
| 216 | pState->avail_out = nBufSize; | ||
| 217 | pState->next_out = (uint8_t *)pBuf; | ||
| 218 | |||
| 219 | lzmaError( lzma_code( pState, LZMA_RUN ) ); | ||
| 220 | |||
| 221 | if( pState->avail_out < nBufSize ) | ||
| 222 | { | ||
| 223 | sTotalOut += rNext.write( pBuf, nBufSize-pState->avail_out ); | ||
| 224 | } | ||
| 225 | if( pState->avail_in == 0 ) | ||
| 226 | break; | ||
| 227 | } | ||
| 228 | |||
| 229 | return nBytes; | ||
| 230 | } | ||
| 231 | |||
| 232 | bool Bu::Lzma::isOpen() | ||
| 233 | { | ||
| 234 | TRACE(); | ||
| 235 | return (pState != NULL); | ||
| 236 | } | ||
| 237 | |||
| 238 | bool Bu::Lzma::isEos() | ||
| 239 | { | ||
| 240 | TRACE(); | ||
| 241 | return bEos; | ||
| 242 | } | ||
| 243 | |||
| 244 | Bu::size Bu::Lzma::getCompressedSize() | ||
| 245 | { | ||
| 246 | return sTotalOut; | ||
| 247 | } | ||
| 248 | |||
| diff --git a/src/stable/lzma.h b/src/stable/lzma.h new file mode 100644 index 0000000..090da8d --- /dev/null +++ b/src/stable/lzma.h | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_LZMA_H | ||
| 9 | #define BU_LZMA_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | |||
| 13 | #include "bu/filter.h" | ||
| 14 | |||
| 15 | namespace Bu | ||
| 16 | { | ||
| 17 | /** | ||
| 18 | * Provides XZ compression and decompression, both LZMA1 (LzmaAlone) as | ||
| 19 | * well as the newer LZMA2 (xz) format. This uses .xz by default. | ||
| 20 | * | ||
| 21 | *@ingroup Streams | ||
| 22 | *@ingroup Compression | ||
| 23 | */ | ||
| 24 | class Lzma : public Bu::Filter | ||
| 25 | { | ||
| 26 | public: | ||
| 27 | enum Format | ||
| 28 | { | ||
| 29 | Xz = 0x01, | ||
| 30 | LzmaAlone = 0x02, | ||
| 31 | }; | ||
| 32 | |||
| 33 | Lzma( Bu::Stream &rNext, int nCompression=6, Format eFmt=Xz ); | ||
| 34 | virtual ~Lzma(); | ||
| 35 | |||
| 36 | virtual void start(); | ||
| 37 | virtual Bu::size stop(); | ||
| 38 | virtual Bu::size read( void *pBuf, Bu::size nBytes ); | ||
| 39 | virtual Bu::size write( const void *pBuf, Bu::size nBytes ); | ||
| 40 | |||
| 41 | virtual bool isOpen(); | ||
| 42 | virtual bool isEos(); | ||
| 43 | |||
| 44 | Bu::size getCompressedSize(); | ||
| 45 | |||
| 46 | private: | ||
| 47 | void lzmaError( int code ); | ||
| 48 | void *prState; | ||
| 49 | bool bReading; | ||
| 50 | int nCompression; | ||
| 51 | char *pBuf; | ||
| 52 | uint32_t nBufSize; | ||
| 53 | Bu::size sTotalOut; | ||
| 54 | Format eFmt; | ||
| 55 | bool bEos; | ||
| 56 | }; | ||
| 57 | } | ||
| 58 | |||
| 59 | #endif | ||
| diff --git a/src/stable/md5.cpp b/src/stable/md5.cpp new file mode 100644 index 0000000..15cba17 --- /dev/null +++ b/src/stable/md5.cpp | |||
| @@ -0,0 +1,246 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <stdio.h> | ||
| 9 | #include <stdlib.h> | ||
| 10 | #include <string.h> | ||
| 11 | #include "bu/md5.h" | ||
| 12 | #include "bu/stream.h" | ||
| 13 | |||
| 14 | #ifdef SYSTEM_BIG_ENDIAN | ||
| 15 | # define toLittleEndian( a, b ) _toLittleEndian( a, b ) | ||
| 16 | #else | ||
| 17 | # define toLittleEndian( a, b ) (void)0 | ||
| 18 | #endif | ||
| 19 | |||
| 20 | Bu::Md5::Md5() | ||
| 21 | { | ||
| 22 | reset(); | ||
| 23 | } | ||
| 24 | |||
| 25 | Bu::Md5::~Md5() | ||
| 26 | { | ||
| 27 | } | ||
| 28 | |||
| 29 | void Bu::Md5::reset() | ||
| 30 | { | ||
| 31 | // These are the magic seed numbers... | ||
| 32 | |||
| 33 | sum[0] = 0x67452301U; | ||
| 34 | sum[1] = 0xEFCDAB89U; | ||
| 35 | sum[2] = 0x98BADCFEU; | ||
| 36 | sum[3] = 0x10325476U; | ||
| 37 | |||
| 38 | uBits[0] = 0; | ||
| 39 | uBits[1] = 0; | ||
| 40 | } | ||
| 41 | |||
| 42 | void Bu::Md5::setSalt( const Bu::String & /*sSalt*/ ) | ||
| 43 | { | ||
| 44 | } | ||
| 45 | |||
| 46 | void Bu::Md5::addData( const void *sVData, int iSize ) | ||
| 47 | { | ||
| 48 | const char *sData = (const char *)sVData; | ||
| 49 | uint32_t t; | ||
| 50 | |||
| 51 | t = uBits[0]; | ||
| 52 | if( (uBits[0] = t + ((uint32_t)iSize << 3)) < t ) | ||
| 53 | uBits[1]++; | ||
| 54 | uBits[1] += iSize >> 29; | ||
| 55 | |||
| 56 | t = (t >> 3) & 0x3f; /* How many bytes we have buffered */ | ||
| 57 | |||
| 58 | /* Handle any leading odd-sized chunks */ | ||
| 59 | if( t ) | ||
| 60 | { | ||
| 61 | unsigned char *p = (unsigned char *) inbuf + t; | ||
| 62 | |||
| 63 | t = 64 - t; | ||
| 64 | if( (uint32_t)iSize < t ) { | ||
| 65 | memcpy( p, sData, iSize ); | ||
| 66 | return; | ||
| 67 | } | ||
| 68 | memcpy( p, sData, t ); | ||
| 69 | toLittleEndian( inbuf, 16 ); | ||
| 70 | compBlock( sum, (uint32_t *)inbuf ); | ||
| 71 | sData += t; | ||
| 72 | iSize -= t; | ||
| 73 | } | ||
| 74 | |||
| 75 | /* Process data in 64-byte chunks */ | ||
| 76 | while( iSize >= 64 ) | ||
| 77 | { | ||
| 78 | memcpy( inbuf, sData, 64 ); | ||
| 79 | toLittleEndian( inbuf, 16 ); | ||
| 80 | compBlock( sum, (uint32_t *)inbuf ); | ||
| 81 | sData += 64; | ||
| 82 | iSize -= 64; | ||
| 83 | } | ||
| 84 | |||
| 85 | /* Handle any remaining bytes of data. */ | ||
| 86 | memcpy( inbuf, sData, iSize ); | ||
| 87 | } | ||
| 88 | |||
| 89 | Bu::String Bu::Md5::getResult() | ||
| 90 | { | ||
| 91 | uint32_t lsum[4]; | ||
| 92 | compCap( lsum ); | ||
| 93 | return Bu::String( (const char *)lsum, 4*4 ); | ||
| 94 | } | ||
| 95 | |||
| 96 | void Bu::Md5::writeResult( Bu::Stream &sOut ) | ||
| 97 | { | ||
| 98 | uint32_t lsum[4]; | ||
| 99 | compCap( lsum ); | ||
| 100 | sOut.write( lsum, 4*4 ); | ||
| 101 | } | ||
| 102 | |||
| 103 | void Bu::Md5::compCap( uint32_t *sumout ) | ||
| 104 | { | ||
| 105 | uint8_t tmpbuf[64]; | ||
| 106 | memcpy( sumout, sum, 4*4 ); | ||
| 107 | memcpy( tmpbuf, inbuf, 64 ); | ||
| 108 | |||
| 109 | uint32_t count; | ||
| 110 | uint8_t *p; | ||
| 111 | |||
| 112 | /* Compute number of bytes mod 64 */ | ||
| 113 | count = (uBits[0] >> 3) & 0x3F; | ||
| 114 | |||
| 115 | /* Set the first char of padding to 0x80. This is safe since there is | ||
| 116 | always at least one byte free */ | ||
| 117 | p = tmpbuf + count; | ||
| 118 | *p++ = 0x80; | ||
| 119 | |||
| 120 | /* Bytes of padding needed to make 64 bytes */ | ||
| 121 | count = 64 - 1 - count; | ||
| 122 | |||
| 123 | /* Pad out to 56 mod 64 */ | ||
| 124 | if (count < 8) { | ||
| 125 | /* Two lots of padding: Pad the first block to 64 bytes */ | ||
| 126 | memset( p, 0, count ); | ||
| 127 | toLittleEndian( tmpbuf, 16 ); | ||
| 128 | compBlock( sumout, (uint32_t *)tmpbuf ); | ||
| 129 | |||
| 130 | /* Now fill the next block with 56 bytes */ | ||
| 131 | memset( tmpbuf, 0, 56); | ||
| 132 | } else { | ||
| 133 | /* Pad block to 56 bytes */ | ||
| 134 | memset( p, 0, count - 8); | ||
| 135 | } | ||
| 136 | toLittleEndian( tmpbuf, 14 ); | ||
| 137 | |||
| 138 | /* Append length in bits and transform */ | ||
| 139 | ((uint32_t *) tmpbuf)[14] = uBits[0]; | ||
| 140 | ((uint32_t *) tmpbuf)[15] = uBits[1]; | ||
| 141 | |||
| 142 | compBlock( sumout, (uint32_t *)tmpbuf ); | ||
| 143 | toLittleEndian((unsigned char *)sumout, 4); | ||
| 144 | } | ||
| 145 | |||
| 146 | #define F1(x, y, z) (z ^ (x & (y ^ z))) | ||
| 147 | #define F2(x, y, z) F1(z, x, y) | ||
| 148 | #define F3(x, y, z) (x ^ y ^ z) | ||
| 149 | #define F4(x, y, z) (y ^ (x | ~z)) | ||
| 150 | |||
| 151 | /* This is the central step in the MD5 algorithm. */ | ||
| 152 | #define MD5STEP(f, w, x, y, z, data, s) \ | ||
| 153 | ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) | ||
| 154 | |||
| 155 | void Bu::Md5::compBlock( uint32_t *lsum, uint32_t *x ) | ||
| 156 | { | ||
| 157 | register uint32_t a, b, c, d; | ||
| 158 | a = lsum[0]; | ||
| 159 | b = lsum[1]; | ||
| 160 | c = lsum[2]; | ||
| 161 | d = lsum[3]; | ||
| 162 | |||
| 163 | MD5STEP(F1, a, b, c, d, x[0] + 0xd76aa478, 7); | ||
| 164 | MD5STEP(F1, d, a, b, c, x[1] + 0xe8c7b756, 12); | ||
| 165 | MD5STEP(F1, c, d, a, b, x[2] + 0x242070db, 17); | ||
| 166 | MD5STEP(F1, b, c, d, a, x[3] + 0xc1bdceee, 22); | ||
| 167 | MD5STEP(F1, a, b, c, d, x[4] + 0xf57c0faf, 7); | ||
| 168 | MD5STEP(F1, d, a, b, c, x[5] + 0x4787c62a, 12); | ||
| 169 | MD5STEP(F1, c, d, a, b, x[6] + 0xa8304613, 17); | ||
| 170 | MD5STEP(F1, b, c, d, a, x[7] + 0xfd469501, 22); | ||
| 171 | MD5STEP(F1, a, b, c, d, x[8] + 0x698098d8, 7); | ||
| 172 | MD5STEP(F1, d, a, b, c, x[9] + 0x8b44f7af, 12); | ||
| 173 | MD5STEP(F1, c, d, a, b, x[10] + 0xffff5bb1, 17); | ||
| 174 | MD5STEP(F1, b, c, d, a, x[11] + 0x895cd7be, 22); | ||
| 175 | MD5STEP(F1, a, b, c, d, x[12] + 0x6b901122, 7); | ||
| 176 | MD5STEP(F1, d, a, b, c, x[13] + 0xfd987193, 12); | ||
| 177 | MD5STEP(F1, c, d, a, b, x[14] + 0xa679438e, 17); | ||
| 178 | MD5STEP(F1, b, c, d, a, x[15] + 0x49b40821, 22); | ||
| 179 | |||
| 180 | MD5STEP(F2, a, b, c, d, x[1] + 0xf61e2562, 5); | ||
| 181 | MD5STEP(F2, d, a, b, c, x[6] + 0xc040b340, 9); | ||
| 182 | MD5STEP(F2, c, d, a, b, x[11] + 0x265e5a51, 14); | ||
| 183 | MD5STEP(F2, b, c, d, a, x[0] + 0xe9b6c7aa, 20); | ||
| 184 | MD5STEP(F2, a, b, c, d, x[5] + 0xd62f105d, 5); | ||
| 185 | MD5STEP(F2, d, a, b, c, x[10] + 0x02441453, 9); | ||
| 186 | MD5STEP(F2, c, d, a, b, x[15] + 0xd8a1e681, 14); | ||
| 187 | MD5STEP(F2, b, c, d, a, x[4] + 0xe7d3fbc8, 20); | ||
| 188 | MD5STEP(F2, a, b, c, d, x[9] + 0x21e1cde6, 5); | ||
| 189 | MD5STEP(F2, d, a, b, c, x[14] + 0xc33707d6, 9); | ||
| 190 | MD5STEP(F2, c, d, a, b, x[3] + 0xf4d50d87, 14); | ||
| 191 | MD5STEP(F2, b, c, d, a, x[8] + 0x455a14ed, 20); | ||
| 192 | MD5STEP(F2, a, b, c, d, x[13] + 0xa9e3e905, 5); | ||
| 193 | MD5STEP(F2, d, a, b, c, x[2] + 0xfcefa3f8, 9); | ||
| 194 | MD5STEP(F2, c, d, a, b, x[7] + 0x676f02d9, 14); | ||
| 195 | MD5STEP(F2, b, c, d, a, x[12] + 0x8d2a4c8a, 20); | ||
| 196 | |||
| 197 | MD5STEP(F3, a, b, c, d, x[5] + 0xfffa3942, 4); | ||
| 198 | MD5STEP(F3, d, a, b, c, x[8] + 0x8771f681, 11); | ||
| 199 | MD5STEP(F3, c, d, a, b, x[11] + 0x6d9d6122, 16); | ||
| 200 | MD5STEP(F3, b, c, d, a, x[14] + 0xfde5380c, 23); | ||
| 201 | MD5STEP(F3, a, b, c, d, x[1] + 0xa4beea44, 4); | ||
| 202 | MD5STEP(F3, d, a, b, c, x[4] + 0x4bdecfa9, 11); | ||
| 203 | MD5STEP(F3, c, d, a, b, x[7] + 0xf6bb4b60, 16); | ||
| 204 | MD5STEP(F3, b, c, d, a, x[10] + 0xbebfbc70, 23); | ||
| 205 | MD5STEP(F3, a, b, c, d, x[13] + 0x289b7ec6, 4); | ||
| 206 | MD5STEP(F3, d, a, b, c, x[0] + 0xeaa127fa, 11); | ||
| 207 | MD5STEP(F3, c, d, a, b, x[3] + 0xd4ef3085, 16); | ||
| 208 | MD5STEP(F3, b, c, d, a, x[6] + 0x04881d05, 23); | ||
| 209 | MD5STEP(F3, a, b, c, d, x[9] + 0xd9d4d039, 4); | ||
| 210 | MD5STEP(F3, d, a, b, c, x[12] + 0xe6db99e5, 11); | ||
| 211 | MD5STEP(F3, c, d, a, b, x[15] + 0x1fa27cf8, 16); | ||
| 212 | MD5STEP(F3, b, c, d, a, x[2] + 0xc4ac5665, 23); | ||
| 213 | |||
| 214 | MD5STEP(F4, a, b, c, d, x[0] + 0xf4292244, 6); | ||
| 215 | MD5STEP(F4, d, a, b, c, x[7] + 0x432aff97, 10); | ||
| 216 | MD5STEP(F4, c, d, a, b, x[14] + 0xab9423a7, 15); | ||
| 217 | MD5STEP(F4, b, c, d, a, x[5] + 0xfc93a039, 21); | ||
| 218 | MD5STEP(F4, a, b, c, d, x[12] + 0x655b59c3, 6); | ||
| 219 | MD5STEP(F4, d, a, b, c, x[3] + 0x8f0ccc92, 10); | ||
| 220 | MD5STEP(F4, c, d, a, b, x[10] + 0xffeff47d, 15); | ||
| 221 | MD5STEP(F4, b, c, d, a, x[1] + 0x85845dd1, 21); | ||
| 222 | MD5STEP(F4, a, b, c, d, x[8] + 0x6fa87e4f, 6); | ||
| 223 | MD5STEP(F4, d, a, b, c, x[15] + 0xfe2ce6e0, 10); | ||
| 224 | MD5STEP(F4, c, d, a, b, x[6] + 0xa3014314, 15); | ||
| 225 | MD5STEP(F4, b, c, d, a, x[13] + 0x4e0811a1, 21); | ||
| 226 | MD5STEP(F4, a, b, c, d, x[4] + 0xf7537e82, 6); | ||
| 227 | MD5STEP(F4, d, a, b, c, x[11] + 0xbd3af235, 10); | ||
| 228 | MD5STEP(F4, c, d, a, b, x[2] + 0x2ad7d2bb, 15); | ||
| 229 | MD5STEP(F4, b, c, d, a, x[9] + 0xeb86d391, 21); | ||
| 230 | |||
| 231 | lsum[0] += a; | ||
| 232 | lsum[1] += b; | ||
| 233 | lsum[2] += c; | ||
| 234 | lsum[3] += d; | ||
| 235 | } | ||
| 236 | |||
| 237 | void Bu::Md5::_toLittleEndian( uint8_t *buf, uint32_t count ) | ||
| 238 | { | ||
| 239 | uint32_t t; | ||
| 240 | do { | ||
| 241 | t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | | ||
| 242 | ((unsigned) buf[1] << 8 | buf[0]); | ||
| 243 | *(uint32_t *) buf = t; | ||
| 244 | buf += 4; | ||
| 245 | } while( --count ); | ||
| 246 | } | ||
| diff --git a/src/stable/md5.h b/src/stable/md5.h new file mode 100644 index 0000000..b7597fd --- /dev/null +++ b/src/stable/md5.h | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_MD5_H | ||
| 9 | #define BU_MD5_H | ||
| 10 | |||
| 11 | #include "bu/cryptohash.h" | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * Class for easily calculating MD5 sums of just about any data. | ||
| 17 | * This code is based on some public domain code written by Colin Plumb in | ||
| 18 | * 1993. | ||
| 19 | *@author Mike Buland | ||
| 20 | */ | ||
| 21 | class Md5 : public Bu::CryptoHash | ||
| 22 | { | ||
| 23 | public: | ||
| 24 | /** Build an MD5 sum builder. */ | ||
| 25 | Md5(); | ||
| 26 | |||
| 27 | /** Deconstruct */ | ||
| 28 | virtual ~Md5(); | ||
| 29 | |||
| 30 | virtual void reset(); | ||
| 31 | virtual void setSalt( const Bu::String &sSalt ); | ||
| 32 | virtual void addData( const void *sData, int iSize ); | ||
| 33 | using Bu::CryptoHash::addData; | ||
| 34 | virtual String getResult(); | ||
| 35 | virtual void writeResult( Bu::Stream &sOut ); | ||
| 36 | |||
| 37 | private: | ||
| 38 | /** | ||
| 39 | * Compute one block of input data. | ||
| 40 | */ | ||
| 41 | void compBlock( uint32_t *lsum, uint32_t *x ); | ||
| 42 | void compCap( uint32_t *sumout ); | ||
| 43 | |||
| 44 | void _addData( uint8_t *target, int &iCurFill, const void *sData, | ||
| 45 | int iSize ); | ||
| 46 | void _toLittleEndian( uint8_t *buf, uint32_t count ); | ||
| 47 | |||
| 48 | uint8_t inbuf[64]; | ||
| 49 | uint32_t sum[4]; | ||
| 50 | uint32_t uBits[2]; | ||
| 51 | }; | ||
| 52 | }; | ||
| 53 | |||
| 54 | #endif | ||
| diff --git a/src/stable/membuf.cpp b/src/stable/membuf.cpp new file mode 100644 index 0000000..14d0d58 --- /dev/null +++ b/src/stable/membuf.cpp | |||
| @@ -0,0 +1,177 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/membuf.h" | ||
| 9 | |||
| 10 | using namespace Bu; | ||
| 11 | |||
| 12 | Bu::MemBuf::MemBuf() : | ||
| 13 | nPos( 0 ) | ||
| 14 | { | ||
| 15 | } | ||
| 16 | |||
| 17 | Bu::MemBuf::MemBuf( const Bu::String &str ) : | ||
| 18 | sBuf( str ), | ||
| 19 | nPos( 0 ) | ||
| 20 | { | ||
| 21 | } | ||
| 22 | |||
| 23 | Bu::MemBuf::~MemBuf() | ||
| 24 | { | ||
| 25 | } | ||
| 26 | |||
| 27 | void Bu::MemBuf::close() | ||
| 28 | { | ||
| 29 | } | ||
| 30 | |||
| 31 | size Bu::MemBuf::read( void *pBuf, size nBytes ) | ||
| 32 | { | ||
| 33 | if( (size)sBuf.getSize()-(size)nPos < nBytes ) | ||
| 34 | nBytes = sBuf.getSize()-nPos; | ||
| 35 | |||
| 36 | memcpy( pBuf, sBuf.getStr()+nPos, nBytes ); | ||
| 37 | nPos += nBytes; | ||
| 38 | |||
| 39 | return nBytes; | ||
| 40 | } | ||
| 41 | |||
| 42 | size Bu::MemBuf::write( const void *pBuf, size nBytes ) | ||
| 43 | { | ||
| 44 | if( nPos == sBuf.getSize() ) | ||
| 45 | { | ||
| 46 | // Easiest, just append the data. | ||
| 47 | sBuf.append( (const char *)pBuf, nBytes ); | ||
| 48 | nPos += nBytes; | ||
| 49 | return nBytes; | ||
| 50 | } | ||
| 51 | else | ||
| 52 | { | ||
| 53 | // Trickier, we must do this in two parts, overwrite, then append | ||
| 54 | // Frist, overwrite. | ||
| 55 | size iOver = sBuf.getSize() - nPos; | ||
| 56 | if( iOver > nBytes ) | ||
| 57 | iOver = nBytes; | ||
| 58 | memcpy( sBuf.getStr()+nPos, pBuf, iOver ); | ||
| 59 | // Then append | ||
| 60 | if( iOver < nBytes ) | ||
| 61 | { | ||
| 62 | sBuf.append( ((const char *)pBuf)+iOver, nBytes-iOver ); | ||
| 63 | } | ||
| 64 | nPos += nBytes; | ||
| 65 | return nBytes; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | size Bu::MemBuf::tell() | ||
| 70 | { | ||
| 71 | return nPos; | ||
| 72 | } | ||
| 73 | |||
| 74 | void Bu::MemBuf::seek( size offset ) | ||
| 75 | { | ||
| 76 | nPos += offset; | ||
| 77 | if( nPos < 0 ) nPos = 0; | ||
| 78 | else if( nPos > sBuf.getSize() ) nPos = sBuf.getSize(); | ||
| 79 | } | ||
| 80 | |||
| 81 | void Bu::MemBuf::setPos( size pos ) | ||
| 82 | { | ||
| 83 | nPos = pos; | ||
| 84 | if( nPos < 0 ) nPos = 0; | ||
| 85 | else if( nPos > sBuf.getSize() ) nPos = sBuf.getSize(); | ||
| 86 | } | ||
| 87 | |||
| 88 | void Bu::MemBuf::setPosEnd( size pos ) | ||
| 89 | { | ||
| 90 | nPos = sBuf.getSize()-pos; | ||
| 91 | if( nPos < 0 ) nPos = 0; | ||
| 92 | else if( nPos > sBuf.getSize() ) nPos = sBuf.getSize(); | ||
| 93 | } | ||
| 94 | |||
| 95 | bool Bu::MemBuf::isEos() | ||
| 96 | { | ||
| 97 | return (nPos == sBuf.getSize()); | ||
| 98 | } | ||
| 99 | |||
| 100 | bool Bu::MemBuf::isOpen() | ||
| 101 | { | ||
| 102 | return true; | ||
| 103 | } | ||
| 104 | |||
| 105 | void Bu::MemBuf::flush() | ||
| 106 | { | ||
| 107 | } | ||
| 108 | |||
| 109 | bool Bu::MemBuf::canRead() | ||
| 110 | { | ||
| 111 | return !isEos(); | ||
| 112 | } | ||
| 113 | |||
| 114 | bool Bu::MemBuf::canWrite() | ||
| 115 | { | ||
| 116 | return true; | ||
| 117 | } | ||
| 118 | |||
| 119 | bool Bu::MemBuf::isReadable() | ||
| 120 | { | ||
| 121 | return true; | ||
| 122 | } | ||
| 123 | |||
| 124 | bool Bu::MemBuf::isWritable() | ||
| 125 | { | ||
| 126 | return true; | ||
| 127 | } | ||
| 128 | |||
| 129 | bool Bu::MemBuf::isSeekable() | ||
| 130 | { | ||
| 131 | return true; | ||
| 132 | } | ||
| 133 | |||
| 134 | bool Bu::MemBuf::isBlocking() | ||
| 135 | { | ||
| 136 | return true; | ||
| 137 | } | ||
| 138 | |||
| 139 | void Bu::MemBuf::setBlocking( bool ) | ||
| 140 | { | ||
| 141 | } | ||
| 142 | |||
| 143 | void Bu::MemBuf::setSize( size iSize ) | ||
| 144 | { | ||
| 145 | if( iSize < 0 ) | ||
| 146 | iSize = 0; | ||
| 147 | sBuf.setSize( iSize ); | ||
| 148 | if( nPos > iSize ) | ||
| 149 | nPos = iSize; | ||
| 150 | } | ||
| 151 | |||
| 152 | Bu::size Bu::MemBuf::getSize() const | ||
| 153 | { | ||
| 154 | return sBuf.getSize(); | ||
| 155 | } | ||
| 156 | |||
| 157 | Bu::size Bu::MemBuf::getBlockSize() const | ||
| 158 | { | ||
| 159 | return sBuf.getSize(); | ||
| 160 | } | ||
| 161 | |||
| 162 | Bu::String Bu::MemBuf::getLocation() const | ||
| 163 | { | ||
| 164 | return ""; | ||
| 165 | } | ||
| 166 | |||
| 167 | Bu::String &Bu::MemBuf::getString() | ||
| 168 | { | ||
| 169 | return sBuf; | ||
| 170 | } | ||
| 171 | |||
| 172 | void Bu::MemBuf::setString( const Bu::String &sNewData ) | ||
| 173 | { | ||
| 174 | sBuf = sNewData; | ||
| 175 | nPos = 0; | ||
| 176 | } | ||
| 177 | |||
| diff --git a/src/stable/membuf.h b/src/stable/membuf.h new file mode 100644 index 0000000..544dc83 --- /dev/null +++ b/src/stable/membuf.h | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_MEM_BUF_H | ||
| 9 | #define BU_MEM_BUF_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | |||
| 13 | #include "bu/config.h" | ||
| 14 | #include "bu/stream.h" | ||
| 15 | #include "bu/string.h" | ||
| 16 | |||
| 17 | namespace Bu | ||
| 18 | { | ||
| 19 | /** | ||
| 20 | * A memory buffer stream. This provides a read/write stream in memory that | ||
| 21 | * works exactly like a file stream...only in memory. You can seed the | ||
| 22 | * memory buffer with a Bu::String of your own, or start with an empty one. | ||
| 23 | * Due to Bu::String using Bu::SharedCore starting with a string will not | ||
| 24 | * necesarilly cause the MemBuf to make a copy of your memory, but if you're | ||
| 25 | * sure you're not going to need to change the stream then use StaticMemBuf. | ||
| 26 | *@ingroup Streams | ||
| 27 | */ | ||
| 28 | class MemBuf : public Stream | ||
| 29 | { | ||
| 30 | public: | ||
| 31 | MemBuf(); | ||
| 32 | MemBuf( const Bu::String &str ); | ||
| 33 | virtual ~MemBuf(); | ||
| 34 | |||
| 35 | virtual void close(); | ||
| 36 | virtual size read( void *pBuf, size iBytes ); | ||
| 37 | |||
| 38 | virtual size write( const void *pBuf, size iBytes ); | ||
| 39 | using Stream::write; | ||
| 40 | virtual size tell(); | ||
| 41 | virtual void seek( size offset ); | ||
| 42 | virtual void setPos( size pos ); | ||
| 43 | virtual void setPosEnd( size pos ); | ||
| 44 | virtual bool isEos(); | ||
| 45 | virtual bool isOpen(); | ||
| 46 | virtual void flush(); | ||
| 47 | virtual bool canRead(); | ||
| 48 | virtual bool canWrite(); | ||
| 49 | virtual bool isReadable(); | ||
| 50 | virtual bool isWritable(); | ||
| 51 | virtual bool isSeekable(); | ||
| 52 | virtual bool isBlocking(); | ||
| 53 | virtual void setBlocking( bool bBlocking=true ); | ||
| 54 | virtual void setSize( size iSize ); | ||
| 55 | virtual size getSize() const; | ||
| 56 | virtual size getBlockSize() const; | ||
| 57 | virtual Bu::String getLocation() const; | ||
| 58 | |||
| 59 | Bu::String &getString(); | ||
| 60 | void setString( const Bu::String &sNewData ); | ||
| 61 | |||
| 62 | private: | ||
| 63 | Bu::String sBuf; | ||
| 64 | size nPos; | ||
| 65 | }; | ||
| 66 | } | ||
| 67 | |||
| 68 | #endif | ||
| diff --git a/src/stable/minicron.cpp b/src/stable/minicron.cpp new file mode 100644 index 0000000..95cf66b --- /dev/null +++ b/src/stable/minicron.cpp | |||
| @@ -0,0 +1,477 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/minicron.h" | ||
| 9 | |||
| 10 | #include <stdlib.h> | ||
| 11 | #include <time.h> | ||
| 12 | |||
| 13 | Bu::MiniCron::MiniCron() : | ||
| 14 | jidNext( 1 ) | ||
| 15 | { | ||
| 16 | } | ||
| 17 | |||
| 18 | Bu::MiniCron::~MiniCron() | ||
| 19 | { | ||
| 20 | while( !hJobs.isEmpty() ) | ||
| 21 | { | ||
| 22 | delete hJobs.dequeue(); | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | bool Bu::MiniCron::hasJobs() | ||
| 27 | { | ||
| 28 | return !hJobs.isEmpty(); | ||
| 29 | } | ||
| 30 | |||
| 31 | time_t Bu::MiniCron::getNextRun() | ||
| 32 | { | ||
| 33 | if( hasJobs() ) | ||
| 34 | return hJobs.peek()->getNextRun(); | ||
| 35 | return -1; | ||
| 36 | } | ||
| 37 | |||
| 38 | time_t Bu::MiniCron::getNextRun( Bu::MiniCron::JobId jid ) | ||
| 39 | { | ||
| 40 | for( JobHeap::iterator i = hJobs.begin(); i; i++ ) | ||
| 41 | { | ||
| 42 | if( (*i)->getId() == jid ) | ||
| 43 | { | ||
| 44 | return (*i)->getNextRunTime(); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | return -1; | ||
| 48 | } | ||
| 49 | |||
| 50 | void Bu::MiniCron::poll() | ||
| 51 | { | ||
| 52 | time_t tNow = time( NULL ); | ||
| 53 | |||
| 54 | while( !hJobs.isEmpty() ) | ||
| 55 | { | ||
| 56 | if( hJobs.peek()->getNextRun() <= tNow ) | ||
| 57 | { | ||
| 58 | Job *pJob = hJobs.dequeue(); | ||
| 59 | pJob->run(); | ||
| 60 | if( pJob->bContinue ) | ||
| 61 | { | ||
| 62 | hJobs.enqueue( pJob ); | ||
| 63 | } | ||
| 64 | else | ||
| 65 | { | ||
| 66 | delete pJob; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | else | ||
| 70 | { | ||
| 71 | break; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | Bu::MiniCron::JobId Bu::MiniCron::addJob( const Bu::String &sName, | ||
| 77 | Bu::MiniCron::CronSignal sigJob, const Bu::MiniCron::Timer &t ) | ||
| 78 | { | ||
| 79 | JobId jid = jidNext++; | ||
| 80 | Job *pJob = new Job( sName, jid ); | ||
| 81 | pJob->sigJob = sigJob; | ||
| 82 | pJob->pTimer = t.clone(); | ||
| 83 | pJob->tNextRun = pJob->pTimer->nextTime(); | ||
| 84 | hJobs.enqueue( pJob ); | ||
| 85 | |||
| 86 | return jid; | ||
| 87 | } | ||
| 88 | |||
| 89 | Bu::MiniCron::JobId Bu::MiniCron::addJobOnce( const Bu::String &sName, | ||
| 90 | Bu::MiniCron::CronSignal sigJob, const Bu::MiniCron::Timer &t ) | ||
| 91 | { | ||
| 92 | JobId jid = jidNext++; | ||
| 93 | Job *pJob = new Job( sName, jid, false ); | ||
| 94 | pJob->sigJob = sigJob; | ||
| 95 | pJob->pTimer = t.clone(); | ||
| 96 | pJob->tNextRun = pJob->pTimer->nextTime(); | ||
| 97 | hJobs.enqueue( pJob ); | ||
| 98 | |||
| 99 | return jid; | ||
| 100 | } | ||
| 101 | |||
| 102 | void Bu::MiniCron::removeJob( JobId jid ) | ||
| 103 | { | ||
| 104 | Bu::List<Job *> lJobs; | ||
| 105 | while( !hJobs.isEmpty() ) | ||
| 106 | { | ||
| 107 | Job *pJob = hJobs.dequeue(); | ||
| 108 | if( pJob->getId() == jid ) | ||
| 109 | { | ||
| 110 | delete pJob; | ||
| 111 | } | ||
| 112 | else | ||
| 113 | lJobs.append( pJob ); | ||
| 114 | } | ||
| 115 | |||
| 116 | for( Bu::List<Job *>::iterator i = lJobs.begin(); i; i++ ) | ||
| 117 | { | ||
| 118 | hJobs.enqueue( *i ); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | void Bu::MiniCron::runJob( JobId jid, bool bReschedule ) | ||
| 123 | { | ||
| 124 | Bu::List<Job *> lJobs; | ||
| 125 | while( !hJobs.isEmpty() ) | ||
| 126 | { | ||
| 127 | Job *pJob = hJobs.dequeue(); | ||
| 128 | if( pJob->getId() == jid ) | ||
| 129 | { | ||
| 130 | pJob->run( bReschedule ); | ||
| 131 | if( !pJob->bContinue ) | ||
| 132 | { | ||
| 133 | delete pJob; | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | lJobs.append( pJob ); | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | lJobs.append( pJob ); | ||
| 140 | } | ||
| 141 | |||
| 142 | for( Bu::List<Job *>::iterator i = lJobs.begin(); i; i++ ) | ||
| 143 | { | ||
| 144 | hJobs.enqueue( *i ); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | void Bu::MiniCron::runJob( const Bu::String &sName, bool bReschedule ) | ||
| 149 | { | ||
| 150 | Bu::List<Job *> lJobs; | ||
| 151 | while( !hJobs.isEmpty() ) | ||
| 152 | { | ||
| 153 | Job *pJob = hJobs.dequeue(); | ||
| 154 | if( pJob->getName() == sName ) | ||
| 155 | { | ||
| 156 | pJob->run( bReschedule ); | ||
| 157 | if( !pJob->bContinue ) | ||
| 158 | { | ||
| 159 | delete pJob; | ||
| 160 | break; | ||
| 161 | } | ||
| 162 | lJobs.append( pJob ); | ||
| 163 | break; | ||
| 164 | } | ||
| 165 | lJobs.append( pJob ); | ||
| 166 | } | ||
| 167 | |||
| 168 | for( Bu::List<Job *>::iterator i = lJobs.begin(); i; i++ ) | ||
| 169 | { | ||
| 170 | hJobs.enqueue( *i ); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | Bu::MiniCron::JobInfoList Bu::MiniCron::getJobInfo() | ||
| 175 | { | ||
| 176 | JobInfoList lRet; | ||
| 177 | for( JobHeap::iterator i = hJobs.begin(); i; i++ ) | ||
| 178 | { | ||
| 179 | lRet.append( | ||
| 180 | JobInfo( (*i)->getName(), (*i)->getId(), (*i)->getNextRun() ) | ||
| 181 | ); | ||
| 182 | } | ||
| 183 | lRet.sort(); | ||
| 184 | return lRet; | ||
| 185 | } | ||
| 186 | |||
| 187 | Bu::MiniCron::Job::Job( const Bu::String &sName, JobId jid, bool bRepeat ) : | ||
| 188 | sName( sName ), | ||
| 189 | pTimer( NULL ), | ||
| 190 | bContinue( bRepeat ), | ||
| 191 | jid( jid ), | ||
| 192 | tAdded( time( NULL ) ), | ||
| 193 | iRunCount( 0 ) | ||
| 194 | { | ||
| 195 | } | ||
| 196 | |||
| 197 | Bu::MiniCron::Job::~Job() | ||
| 198 | { | ||
| 199 | delete pTimer; | ||
| 200 | pTimer = NULL; | ||
| 201 | } | ||
| 202 | |||
| 203 | void Bu::MiniCron::Job::run( bool bReschedule ) | ||
| 204 | { | ||
| 205 | iRunCount++; | ||
| 206 | if( bReschedule ) | ||
| 207 | tNextRun = pTimer->nextTime(); | ||
| 208 | sigJob( *this ); | ||
| 209 | } | ||
| 210 | |||
| 211 | time_t Bu::MiniCron::Job::getNextRun() const | ||
| 212 | { | ||
| 213 | return tNextRun; | ||
| 214 | } | ||
| 215 | |||
| 216 | void Bu::MiniCron::Job::calcNextRun() | ||
| 217 | { | ||
| 218 | if( pTimer ) | ||
| 219 | tNextRun = pTimer->nextTime(); | ||
| 220 | } | ||
| 221 | |||
| 222 | void Bu::MiniCron::Job::setTimer( const Timer &t ) | ||
| 223 | { | ||
| 224 | delete pTimer; | ||
| 225 | pTimer = t.clone(); | ||
| 226 | } | ||
| 227 | |||
| 228 | void Bu::MiniCron::Job::stop() | ||
| 229 | { | ||
| 230 | bContinue = false; | ||
| 231 | } | ||
| 232 | |||
| 233 | void Bu::MiniCron::Job::resume() | ||
| 234 | { | ||
| 235 | bContinue = true; | ||
| 236 | } | ||
| 237 | |||
| 238 | Bu::MiniCron::JobId Bu::MiniCron::Job::getId() const | ||
| 239 | { | ||
| 240 | return jid; | ||
| 241 | } | ||
| 242 | |||
| 243 | time_t Bu::MiniCron::Job::getTimeCreated() const | ||
| 244 | { | ||
| 245 | return tAdded; | ||
| 246 | } | ||
| 247 | |||
| 248 | int Bu::MiniCron::Job::getRunCount() const | ||
| 249 | { | ||
| 250 | return iRunCount; | ||
| 251 | } | ||
| 252 | |||
| 253 | time_t Bu::MiniCron::Job::getNextRunTime() const | ||
| 254 | { | ||
| 255 | return tNextRun; | ||
| 256 | } | ||
| 257 | |||
| 258 | Bu::String Bu::MiniCron::Job::getName() const | ||
| 259 | { | ||
| 260 | return sName; | ||
| 261 | } | ||
| 262 | |||
| 263 | Bu::MiniCron::JobInfo::JobInfo( const Bu::String &sName, JobId jid, | ||
| 264 | time_t tNext ) : | ||
| 265 | sName( sName ), | ||
| 266 | jid( jid ), | ||
| 267 | tNext( tNext ) | ||
| 268 | { | ||
| 269 | } | ||
| 270 | |||
| 271 | Bu::MiniCron::JobInfo::~JobInfo() | ||
| 272 | { | ||
| 273 | } | ||
| 274 | |||
| 275 | bool Bu::MiniCron::JobInfo::operator<( const JobInfo &rhs ) const | ||
| 276 | { | ||
| 277 | return jid < rhs.jid; | ||
| 278 | } | ||
| 279 | |||
| 280 | Bu::MiniCron::Timer::Timer() | ||
| 281 | { | ||
| 282 | } | ||
| 283 | |||
| 284 | Bu::MiniCron::Timer::~Timer() | ||
| 285 | { | ||
| 286 | } | ||
| 287 | |||
| 288 | Bu::MiniCron::TimerInterval::TimerInterval( time_t tFirst, time_t tInterval ) : | ||
| 289 | tNext( tFirst ), | ||
| 290 | tInterval( tInterval ) | ||
| 291 | { | ||
| 292 | } | ||
| 293 | |||
| 294 | Bu::MiniCron::TimerInterval::~TimerInterval() | ||
| 295 | { | ||
| 296 | } | ||
| 297 | |||
| 298 | time_t Bu::MiniCron::TimerInterval::nextTime() | ||
| 299 | { | ||
| 300 | time_t tRet = tNext; | ||
| 301 | tNext += tInterval; | ||
| 302 | return tRet; | ||
| 303 | } | ||
| 304 | |||
| 305 | Bu::MiniCron::TimerBasic::TimerBasic( const Bu::String &s ) : | ||
| 306 | tLast( -1 ), | ||
| 307 | sSpec( s ) | ||
| 308 | { | ||
| 309 | } | ||
| 310 | |||
| 311 | Bu::MiniCron::TimerBasic::~TimerBasic() | ||
| 312 | { | ||
| 313 | } | ||
| 314 | |||
| 315 | time_t Bu::MiniCron::TimerBasic::nextTime() | ||
| 316 | { | ||
| 317 | if( tLast == -1 ) | ||
| 318 | tLast = time( NULL ); | ||
| 319 | |||
| 320 | Bu::String::const_iterator i = sSpec.begin(); | ||
| 321 | switch( lex( i ) ) | ||
| 322 | { | ||
| 323 | case tokDaily: | ||
| 324 | { | ||
| 325 | int iHour = lexInt( i ); | ||
| 326 | int iMin = lexInt( i ); | ||
| 327 | |||
| 328 | struct tm t; | ||
| 329 | ::memcpy( &t, localtime( &tLast ), sizeof(struct tm) ); | ||
| 330 | if( iHour < t.tm_hour || | ||
| 331 | (iHour == t.tm_hour && iMin <= t.tm_min) ) | ||
| 332 | { | ||
| 333 | t.tm_mday++; | ||
| 334 | } | ||
| 335 | t.tm_hour = iHour; | ||
| 336 | t.tm_min = iMin; | ||
| 337 | t.tm_sec = 0; | ||
| 338 | tLast = mktime( &t ); | ||
| 339 | } | ||
| 340 | break; | ||
| 341 | |||
| 342 | case tokHourly: | ||
| 343 | { | ||
| 344 | int iMin = lexInt( i ); | ||
| 345 | |||
| 346 | struct tm t; | ||
| 347 | ::memcpy( &t, localtime( &tLast ), sizeof(struct tm) ); | ||
| 348 | if( iMin <= t.tm_min ) | ||
| 349 | t.tm_hour++; | ||
| 350 | t.tm_min = iMin; | ||
| 351 | t.tm_sec = 0; | ||
| 352 | tLast = mktime( &t ); | ||
| 353 | } | ||
| 354 | break; | ||
| 355 | |||
| 356 | case tokWeekly: | ||
| 357 | { | ||
| 358 | int iDay = lexInt( i ); | ||
| 359 | int iHour = lexInt( i ); | ||
| 360 | int iMin = lexInt( i ); | ||
| 361 | |||
| 362 | struct tm t; | ||
| 363 | ::memcpy( &t, localtime( &tLast ), sizeof(struct tm) ); | ||
| 364 | if( iDay < t.tm_wday || | ||
| 365 | (iDay == t.tm_wday && iHour < t.tm_hour) || | ||
| 366 | (iDay == t.tm_wday && iHour == t.tm_hour | ||
| 367 | && iMin <= t.tm_min) ) | ||
| 368 | { | ||
| 369 | if( iDay <= t.tm_wday ) | ||
| 370 | t.tm_mday += 7 - (t.tm_wday-iDay); | ||
| 371 | else | ||
| 372 | t.tm_mday += 7 - (iDay-t.tm_wday); | ||
| 373 | } | ||
| 374 | else | ||
| 375 | { | ||
| 376 | t.tm_mday += (iDay-t.tm_wday); | ||
| 377 | } | ||
| 378 | t.tm_hour = iHour; | ||
| 379 | t.tm_min = iMin; | ||
| 380 | t.tm_sec = 0; | ||
| 381 | tLast = mktime( &t ); | ||
| 382 | } | ||
| 383 | break; | ||
| 384 | |||
| 385 | case tokMonthly: | ||
| 386 | break; | ||
| 387 | |||
| 388 | case tokYearly: | ||
| 389 | break; | ||
| 390 | |||
| 391 | default: | ||
| 392 | break; | ||
| 393 | } | ||
| 394 | |||
| 395 | return tLast; | ||
| 396 | } | ||
| 397 | |||
| 398 | Bu::MiniCron::TimerBasic::Token Bu::MiniCron::TimerBasic::lex( | ||
| 399 | Bu::String::const_iterator &i ) | ||
| 400 | { | ||
| 401 | if( !i ) | ||
| 402 | { | ||
| 403 | return tokEos; | ||
| 404 | } | ||
| 405 | |||
| 406 | Bu::String::const_iterator b = i; | ||
| 407 | |||
| 408 | for(; b && (*b == ' ' || *b == '\t'); b++ ) { i = b+1; } | ||
| 409 | for(; b && *b != ' ' && *b != '\t'; b++ ) { } | ||
| 410 | |||
| 411 | Bu::String sTok( i, b ); | ||
| 412 | i = b; | ||
| 413 | |||
| 414 | if( sTok == "daily" ) | ||
| 415 | return tokDaily; | ||
| 416 | else if( sTok == "hourly" ) | ||
| 417 | return tokHourly; | ||
| 418 | else if( sTok == "weekly" ) | ||
| 419 | return tokWeekly; | ||
| 420 | else if( sTok == "monthly" ) | ||
| 421 | return tokMonthly; | ||
| 422 | else if( sTok == "yearly" ) | ||
| 423 | return tokYearly; | ||
| 424 | else if( sTok == "sun" ) | ||
| 425 | { | ||
| 426 | iVal = 0; | ||
| 427 | return valInt; | ||
| 428 | } | ||
| 429 | else if( sTok == "mon" ) | ||
| 430 | { | ||
| 431 | iVal = 1; | ||
| 432 | return valInt; | ||
| 433 | } | ||
| 434 | else if( sTok == "tue" ) | ||
| 435 | { | ||
| 436 | iVal = 2; | ||
| 437 | return valInt; | ||
| 438 | } | ||
| 439 | else if( sTok == "wed" ) | ||
| 440 | { | ||
| 441 | iVal = 3; | ||
| 442 | return valInt; | ||
| 443 | } | ||
| 444 | else if( sTok == "thu" ) | ||
| 445 | { | ||
| 446 | iVal = 4; | ||
| 447 | return valInt; | ||
| 448 | } | ||
| 449 | else if( sTok == "fri" ) | ||
| 450 | { | ||
| 451 | iVal = 5; | ||
| 452 | return valInt; | ||
| 453 | } | ||
| 454 | else if( sTok == "sat" ) | ||
| 455 | { | ||
| 456 | iVal = 6; | ||
| 457 | return valInt; | ||
| 458 | } | ||
| 459 | else if( sTok[0] >= '0' && sTok[0] <= '9' ) | ||
| 460 | { | ||
| 461 | iVal = strtol( sTok.getStr(), NULL, 0 ); | ||
| 462 | return valInt; | ||
| 463 | } | ||
| 464 | |||
| 465 | return tokErr; | ||
| 466 | } | ||
| 467 | |||
| 468 | int Bu::MiniCron::TimerBasic::lexInt( Bu::String::const_iterator &i ) | ||
| 469 | { | ||
| 470 | Token t = lex( i ); | ||
| 471 | if( t == tokEos ) | ||
| 472 | return 0; | ||
| 473 | if( t != valInt ) | ||
| 474 | throw Bu::ExceptionBase("Expected int, got something else."); | ||
| 475 | return iVal; | ||
| 476 | } | ||
| 477 | |||
| diff --git a/src/stable/minicron.h b/src/stable/minicron.h new file mode 100644 index 0000000..0d1cb62 --- /dev/null +++ b/src/stable/minicron.h | |||
| @@ -0,0 +1,336 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_MINICRON_H | ||
| 9 | #define BU_MINICRON_H | ||
| 10 | |||
| 11 | #include "bu/signals.h" | ||
| 12 | #include "bu/heap.h" | ||
| 13 | #include "bu/string.h" | ||
| 14 | |||
| 15 | #include <time.h> | ||
| 16 | |||
| 17 | namespace Bu | ||
| 18 | { | ||
| 19 | /** | ||
| 20 | * A simple cron like system designed to be embedded in any program. This | ||
| 21 | * class creates a simple cron system that can run any number of jobs at | ||
| 22 | * customizable intervals or schedules. It does not support some of the | ||
| 23 | * more complex scheduling that some cron systems can do such as load | ||
| 24 | * balancing directly, but this could be done on the job side. | ||
| 25 | * | ||
| 26 | * This system is synchronous, it does not use any threads on it's own, but | ||
| 27 | * it is threadsafe, so a cron thread could be created if desired. | ||
| 28 | * | ||
| 29 | * The operation is fairly simple, jobs can be added at any time, and use | ||
| 30 | * any timer they would like, even custom timers. When it is time for a | ||
| 31 | * job to be run it signals the slot provided when the job was added. Every | ||
| 32 | * job slot recieves a handle to the job object so that it may control it's | ||
| 33 | * own lifetime and get information about itself. In addition, every job | ||
| 34 | * is assigned a unique ID that can be used to control it's operation | ||
| 35 | * at any time. | ||
| 36 | * | ||
| 37 | * By default a job will continually reschedule itself after being run | ||
| 38 | * unless it calls stop() on it's job object, it is removed using | ||
| 39 | * removeJob() on the cron object, or it is added with addJobOnce. | ||
| 40 | * | ||
| 41 | *@todo A minor change to the job execution system could allow a Timer to | ||
| 42 | * defer or reschedule execution instead of the job executing. This would, | ||
| 43 | * in effect, allow us to do every type of interesting scheduling that | ||
| 44 | * systems like fcron offer, including time constrained load-balanced | ||
| 45 | * execution. | ||
| 46 | */ | ||
| 47 | class MiniCron | ||
| 48 | { | ||
| 49 | public: | ||
| 50 | class Job; | ||
| 51 | class Timer; | ||
| 52 | typedef Bu::Signal1<void, Bu::MiniCron::Job &> CronSignal; | ||
| 53 | typedef int JobId; | ||
| 54 | |||
| 55 | MiniCron(); | ||
| 56 | virtual ~MiniCron(); | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Tells you if there are jobs registered in the MiniCron. | ||
| 60 | *@returns true if there are jobs, false otherwise. | ||
| 61 | */ | ||
| 62 | virtual bool hasJobs(); | ||
| 63 | |||
| 64 | /** | ||
| 65 | * If there are jobs, tells you the time the next one will execute. | ||
| 66 | *@returns The timestamp that the next job will execute at. | ||
| 67 | */ | ||
| 68 | virtual time_t getNextRun(); | ||
| 69 | |||
| 70 | /** | ||
| 71 | * Tells you the time the job matching jid will run next. | ||
| 72 | *@returns The timestamp that the job jid will next run. | ||
| 73 | */ | ||
| 74 | virtual time_t getNextRun( JobId jid ); | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Call this regularly to execute all jobs that should be executed. | ||
| 78 | * This will loop until all jobs who's run time match the current time | ||
| 79 | * or are below the current time (we've missed them). | ||
| 80 | * If there is nothing to run, the runtime of this funcion is constant, | ||
| 81 | * it is very fast. Otherwise it executes at log(N) per job run, | ||
| 82 | * O(N*log(N)). | ||
| 83 | */ | ||
| 84 | virtual void poll(); | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Add a job for repeated scheduling. Pass in a slot to signal, and a | ||
| 88 | * Timer object to use to do the scheduling. This function returns a | ||
| 89 | * JobId which can be used at a later time to control the execution of | ||
| 90 | * the job. | ||
| 91 | */ | ||
| 92 | virtual JobId addJob( const Bu::String &sName, CronSignal sigJob, | ||
| 93 | const Timer &t ); | ||
| 94 | |||
| 95 | /** | ||
| 96 | * Add a job for one time scheduling. Pass in a slot to signal, and a | ||
| 97 | * Timer object to use to schodule the one run of this job. This | ||
| 98 | * function returns a JobId which can be used at a later time to control | ||
| 99 | * the execution of the job. | ||
| 100 | */ | ||
| 101 | virtual JobId addJobOnce( const Bu::String &sName, CronSignal sigJob, | ||
| 102 | const Timer &t ); | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Remove a job, preventing all future runs of the job. If there is no | ||
| 106 | * job matching the given JobId then nothing will happen. However, this | ||
| 107 | * function is relatively expensive compared to the others in this class | ||
| 108 | * and has a worse case runtime of 2*N*log(N), still not that bad, and | ||
| 109 | * a O(N*log(N)). | ||
| 110 | */ | ||
| 111 | virtual void removeJob( JobId jid ); | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Executes the job specified right now. If bReschedule is true then | ||
| 115 | * the job is then removed from the queue and rescheduled as though | ||
| 116 | * it's time had come naturally to be run. Otherwise, it's run without | ||
| 117 | * interrupting the normal schedule. | ||
| 118 | */ | ||
| 119 | virtual void runJob( JobId jid, bool bReschedule=false ); | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Executes the job specified right now. If bReschedule is true then | ||
| 123 | * the job is then removed from the queue and rescheduled as though | ||
| 124 | * it's time had come naturally to be run. Otherwise, it's run without | ||
| 125 | * interrupting the normal schedule. | ||
| 126 | */ | ||
| 127 | virtual void runJob( const Bu::String &sName, bool bReschedule=false ); | ||
| 128 | |||
| 129 | class JobInfo | ||
| 130 | { | ||
| 131 | public: | ||
| 132 | JobInfo( const Bu::String &sName, JobId jid, time_t tNext ); | ||
| 133 | virtual ~JobInfo(); | ||
| 134 | |||
| 135 | bool operator<( const JobInfo &rhs ) const; | ||
| 136 | |||
| 137 | Bu::String sName; | ||
| 138 | JobId jid; | ||
| 139 | time_t tNext; | ||
| 140 | }; | ||
| 141 | typedef Bu::List<JobInfo> JobInfoList; | ||
| 142 | |||
| 143 | JobInfoList getJobInfo(); | ||
| 144 | |||
| 145 | /** | ||
| 146 | * The baseclass for timer/schedulers for MiniCron jobs. Classes that | ||
| 147 | * inherit from this are used to determine when jobs will run and at | ||
| 148 | * what interval. | ||
| 149 | */ | ||
| 150 | class Timer | ||
| 151 | { | ||
| 152 | public: | ||
| 153 | Timer(); | ||
| 154 | virtual ~Timer(); | ||
| 155 | |||
| 156 | /** | ||
| 157 | * Called by MiniCron when each job is run to determine the next | ||
| 158 | * time that a job should be run. When a job is run, this function | ||
| 159 | * is actually called before the job is executed again so that the | ||
| 160 | * job can tell when the next time it will be run will be. | ||
| 161 | */ | ||
| 162 | virtual time_t nextTime()=0; | ||
| 163 | |||
| 164 | /** | ||
| 165 | * This function should return a copy of the child class. | ||
| 166 | */ | ||
| 167 | virtual Timer *clone() const = 0; | ||
| 168 | }; | ||
| 169 | |||
| 170 | /** | ||
| 171 | * Execute the job every tInterval seconds, also you can delay the | ||
| 172 | * first run by a different amount of time from the job's creation. | ||
| 173 | */ | ||
| 174 | class TimerInterval : public Timer | ||
| 175 | { | ||
| 176 | public: | ||
| 177 | TimerInterval( time_t tFirst, time_t tInterval ); | ||
| 178 | virtual ~TimerInterval(); | ||
| 179 | |||
| 180 | virtual time_t nextTime(); | ||
| 181 | virtual Timer *clone() const | ||
| 182 | { return new TimerInterval( *this ); } | ||
| 183 | private: | ||
| 184 | time_t tNext; | ||
| 185 | time_t tInterval; | ||
| 186 | }; | ||
| 187 | |||
| 188 | /** | ||
| 189 | * A much more general timer class that can be used for much more | ||
| 190 | * "cron-like" functionality. The constructor takes a string that | ||
| 191 | * describes the times that the job should be run. At the moment the | ||
| 192 | * following schemes are understood: | ||
| 193 | * | ||
| 194 | * "daily [hour] [minute]" | ||
| 195 | * "hourly [minute]" | ||
| 196 | * "weekly [day] [hour] [minute]" | ||
| 197 | * | ||
| 198 | * In these examples each word in [brackets] represents a number that | ||
| 199 | * matches the data type in the brackets. [day] is the number of days | ||
| 200 | * since sunday, 0-6. You can also use lowercase three character | ||
| 201 | * abbreviations for the day names. | ||
| 202 | * | ||
| 203 | * Many more forms follow. | ||
| 204 | */ | ||
| 205 | class TimerBasic : public Timer | ||
| 206 | { | ||
| 207 | public: | ||
| 208 | TimerBasic( const Bu::String &s ); | ||
| 209 | virtual ~TimerBasic(); | ||
| 210 | |||
| 211 | virtual time_t nextTime(); | ||
| 212 | virtual Timer *clone() const | ||
| 213 | { return new TimerBasic( *this ); } | ||
| 214 | |||
| 215 | private: | ||
| 216 | enum Token | ||
| 217 | { | ||
| 218 | tokDaily, | ||
| 219 | tokHourly, | ||
| 220 | tokWeekly, | ||
| 221 | tokMonthly, | ||
| 222 | tokYearly, | ||
| 223 | valInt, | ||
| 224 | tokErr, | ||
| 225 | tokEos | ||
| 226 | }; | ||
| 227 | Token lex( Bu::String::const_iterator &i ); | ||
| 228 | int lexInt( Bu::String::const_iterator &i ); | ||
| 229 | int iVal; //< A temp variable for parsing. | ||
| 230 | time_t tLast; | ||
| 231 | Bu::String sSpec; | ||
| 232 | }; | ||
| 233 | |||
| 234 | /** | ||
| 235 | * Represents a MiniCron Job. This class is used for both internal | ||
| 236 | * job management as well as job slot interaction and control. Objects | ||
| 237 | * of this class are passed into the slots that are signaled when a job | ||
| 238 | * is executed. | ||
| 239 | */ | ||
| 240 | class Job | ||
| 241 | { | ||
| 242 | friend class Bu::MiniCron; | ||
| 243 | private: | ||
| 244 | Job( const Bu::String &sName, JobId jid, bool bRepeat=true ); | ||
| 245 | virtual ~Job(); | ||
| 246 | |||
| 247 | public: | ||
| 248 | |||
| 249 | /** | ||
| 250 | * Execute this job once, increment the runcount and schedule the | ||
| 251 | * next occurance of it. | ||
| 252 | */ | ||
| 253 | void run( bool bReschedule=true ); | ||
| 254 | |||
| 255 | /** | ||
| 256 | * Get the time this job will next run. | ||
| 257 | */ | ||
| 258 | time_t getNextRun() const; | ||
| 259 | |||
| 260 | /** | ||
| 261 | * Compute the time this job will next run. | ||
| 262 | */ | ||
| 263 | void calcNextRun(); | ||
| 264 | |||
| 265 | /** | ||
| 266 | * Replace the current job timer with a new one, this will trigger | ||
| 267 | * a re-schedule. | ||
| 268 | */ | ||
| 269 | void setTimer( const Timer &t ); | ||
| 270 | |||
| 271 | /** | ||
| 272 | * Stop execution of this job, never execute this job again. | ||
| 273 | */ | ||
| 274 | void stop(); | ||
| 275 | |||
| 276 | /** | ||
| 277 | * Undo a previous stop. This will cause a job that has been | ||
| 278 | * stopped or even added with addJobOnce to be set for repeated | ||
| 279 | * scheduling. | ||
| 280 | */ | ||
| 281 | void resume(); | ||
| 282 | |||
| 283 | /** | ||
| 284 | * Get the unique ID of this job. | ||
| 285 | */ | ||
| 286 | JobId getId() const; | ||
| 287 | |||
| 288 | /** | ||
| 289 | * Get the timestamp this job was created. | ||
| 290 | */ | ||
| 291 | time_t getTimeCreated() const; | ||
| 292 | |||
| 293 | /** | ||
| 294 | * Get the current run count of this job, how many times it has been | ||
| 295 | * executed. This is incremented before the slot is signaled. | ||
| 296 | */ | ||
| 297 | int getRunCount() const; | ||
| 298 | |||
| 299 | /** | ||
| 300 | * Get the next time that this job will be run. Certain timers may | ||
| 301 | * have the ability to delay job executions, so this is the earliest | ||
| 302 | * time that the job may run. | ||
| 303 | */ | ||
| 304 | time_t getNextRunTime() const; | ||
| 305 | |||
| 306 | /** | ||
| 307 | * Gets the name that was set when the job was created. | ||
| 308 | */ | ||
| 309 | Bu::String getName() const; | ||
| 310 | |||
| 311 | private: | ||
| 312 | Bu::String sName; | ||
| 313 | CronSignal sigJob; | ||
| 314 | time_t tNextRun; | ||
| 315 | Timer *pTimer; | ||
| 316 | bool bContinue; | ||
| 317 | JobId jid; | ||
| 318 | time_t tAdded; | ||
| 319 | int iRunCount; | ||
| 320 | }; | ||
| 321 | |||
| 322 | private: | ||
| 323 | struct JobPtrCmp | ||
| 324 | { | ||
| 325 | bool operator()( const Job *pLeft, const Job *pRight ) | ||
| 326 | { | ||
| 327 | return pLeft->tNextRun < pRight->tNextRun; | ||
| 328 | } | ||
| 329 | }; | ||
| 330 | typedef Bu::Heap<Job *, JobPtrCmp> JobHeap; | ||
| 331 | JobHeap hJobs; | ||
| 332 | JobId jidNext; | ||
| 333 | }; | ||
| 334 | }; | ||
| 335 | |||
| 336 | #endif | ||
| diff --git a/src/stable/multiserver.cpp b/src/stable/multiserver.cpp new file mode 100644 index 0000000..bd598ed --- /dev/null +++ b/src/stable/multiserver.cpp | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/multiserver.h" | ||
| 9 | #include "bu/protocol.h" | ||
| 10 | #include "bu/client.h" | ||
| 11 | |||
| 12 | #include "bu/config.h" | ||
| 13 | |||
| 14 | Bu::MultiServer::MultiServer() | ||
| 15 | { | ||
| 16 | } | ||
| 17 | |||
| 18 | Bu::MultiServer::~MultiServer() | ||
| 19 | { | ||
| 20 | } | ||
| 21 | |||
| 22 | void Bu::MultiServer::addProtocol( Bu::Protocol *(*proc)(), int iPort, | ||
| 23 | int nPoolSize ) | ||
| 24 | { | ||
| 25 | hProtos[iPort] = proc; | ||
| 26 | addPort( iPort, nPoolSize ); | ||
| 27 | } | ||
| 28 | |||
| 29 | void Bu::MultiServer::addProtocol( Protocol *(*proc)(), const String &sAddr, | ||
| 30 | int iPort, int nPoolSize ) | ||
| 31 | { | ||
| 32 | hProtos[iPort] = proc; | ||
| 33 | addPort( sAddr, iPort, nPoolSize ); | ||
| 34 | } | ||
| 35 | |||
| 36 | void Bu::MultiServer::onNewConnection( Bu::Client *pClient, int nPort ) | ||
| 37 | { | ||
| 38 | pClient->setProtocol( hProtos.get( nPort )() ); | ||
| 39 | } | ||
| 40 | |||
| 41 | void Bu::MultiServer::onClosedConnection( Bu::Client *pClient ) | ||
| 42 | { | ||
| 43 | delete pClient->getProtocol(); | ||
| 44 | } | ||
| 45 | |||
| 46 | void Bu::MultiServer::shutdown() | ||
| 47 | { | ||
| 48 | Bu::Server::shutdown(); | ||
| 49 | } | ||
| 50 | |||
| 51 | void Bu::MultiServer::tick() | ||
| 52 | { | ||
| 53 | Bu::Server::tick(); | ||
| 54 | } | ||
| 55 | |||
| diff --git a/src/stable/multiserver.h b/src/stable/multiserver.h new file mode 100644 index 0000000..e3b3ec3 --- /dev/null +++ b/src/stable/multiserver.h | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_MULTI_SERVER_H | ||
| 9 | #define BU_MULTI_SERVER_H | ||
| 10 | |||
| 11 | #include "bu/server.h" | ||
| 12 | #include "bu/hash.h" | ||
| 13 | |||
| 14 | namespace Bu | ||
| 15 | { | ||
| 16 | class Protocol; | ||
| 17 | class Client; | ||
| 18 | |||
| 19 | template<class T> | ||
| 20 | Protocol *genProtocol() | ||
| 21 | { | ||
| 22 | return new T; | ||
| 23 | } | ||
| 24 | |||
| 25 | class MultiServer : protected Server | ||
| 26 | { | ||
| 27 | public: | ||
| 28 | MultiServer(); | ||
| 29 | virtual ~MultiServer(); | ||
| 30 | |||
| 31 | void addProtocol( Protocol *(*proc)(), int iPort, int nPoolSize=40 ); | ||
| 32 | void addProtocol( Protocol *(*proc)(), const String &sAddr, int iPort, | ||
| 33 | int nPoolSize=40 ); | ||
| 34 | |||
| 35 | void scan() | ||
| 36 | { | ||
| 37 | Server::scan(); | ||
| 38 | } | ||
| 39 | |||
| 40 | void setTimeout( int nTimeoutSec, int nTimeoutUSec=0 ) | ||
| 41 | { | ||
| 42 | Server::setTimeout( nTimeoutSec, nTimeoutUSec ); | ||
| 43 | } | ||
| 44 | |||
| 45 | virtual void onNewConnection( Client *pClient, int nPort ); | ||
| 46 | virtual void onClosedConnection( Client *pClient ); | ||
| 47 | |||
| 48 | void shutdown(); | ||
| 49 | |||
| 50 | void tick(); | ||
| 51 | |||
| 52 | private: | ||
| 53 | Bu::Hash<int, Protocol *(*)()> hProtos; | ||
| 54 | }; | ||
| 55 | } | ||
| 56 | |||
| 57 | #endif | ||
| diff --git a/src/stable/mutex.cpp b/src/stable/mutex.cpp new file mode 100644 index 0000000..dbaaece --- /dev/null +++ b/src/stable/mutex.cpp | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/mutex.h" | ||
| 9 | |||
| 10 | Bu::Mutex::Mutex() | ||
| 11 | { | ||
| 12 | pthread_mutex_init( &mutex, NULL ); | ||
| 13 | } | ||
| 14 | |||
| 15 | Bu::Mutex::~Mutex() | ||
| 16 | { | ||
| 17 | pthread_mutex_destroy( &mutex ); | ||
| 18 | } | ||
| 19 | |||
| 20 | int Bu::Mutex::lock() | ||
| 21 | { | ||
| 22 | return pthread_mutex_lock( &mutex ); | ||
| 23 | } | ||
| 24 | |||
| 25 | int Bu::Mutex::unlock() | ||
| 26 | { | ||
| 27 | return pthread_mutex_unlock( &mutex ); | ||
| 28 | } | ||
| 29 | |||
| 30 | int Bu::Mutex::trylock() | ||
| 31 | { | ||
| 32 | return pthread_mutex_trylock( &mutex ); | ||
| 33 | } | ||
| 34 | |||
| diff --git a/src/stable/mutex.h b/src/stable/mutex.h new file mode 100644 index 0000000..b5c8b7a --- /dev/null +++ b/src/stable/mutex.h | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_MUTEX_H | ||
| 9 | #define BU_MUTEX_H | ||
| 10 | |||
| 11 | #include <pthread.h> | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * Simple mutex wrapper. Currently this doesn't do anything extra for you | ||
| 17 | * except keep all of the functionality together in an OO sorta' way and | ||
| 18 | * keep you from having to worry about cleaning up your mutexes properly, | ||
| 19 | * or initing them. | ||
| 20 | *@ingroup Threading | ||
| 21 | */ | ||
| 22 | class Mutex | ||
| 23 | { | ||
| 24 | public: | ||
| 25 | /** | ||
| 26 | * Create an unlocked mutex. | ||
| 27 | */ | ||
| 28 | Mutex(); | ||
| 29 | |||
| 30 | /** | ||
| 31 | * Destroy a mutex. This can only be done when a mutex is unlocked. | ||
| 32 | * Failure to unlock before destroying a mutex object could cause it to | ||
| 33 | * wait for the mutex to unlock, the odds of which are usually farily | ||
| 34 | * low at deconstruction time. | ||
| 35 | */ | ||
| 36 | ~Mutex(); | ||
| 37 | |||
| 38 | /** | ||
| 39 | * Lock the mutex. This causes all future calls to lock on this | ||
| 40 | * instance of mutex to block until the first thread that called mutex | ||
| 41 | * unlocks it. At that point the next thread that called lock will get | ||
| 42 | * a chance to go to work. Because of the nature of a mutex lock it is | ||
| 43 | * a very bad idea to do any kind of serious or rather time consuming | ||
| 44 | * computation within a locked section. This can cause thread-deadlock | ||
| 45 | * and your program may hang. | ||
| 46 | */ | ||
| 47 | int lock(); | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Unlock the mutex. This allows the next thread that asked for a lock | ||
| 51 | * to lock the mutex and continue with execution. | ||
| 52 | */ | ||
| 53 | int unlock(); | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Try to lock the mutex. This is the option to go with if you cannot | ||
| 57 | * avoid putting lengthy operations within a locked section. trylock | ||
| 58 | * will attempt to lock the mutex, if the mutex is already locked this | ||
| 59 | * function returns immediately with an error code. | ||
| 60 | */ | ||
| 61 | int trylock(); | ||
| 62 | |||
| 63 | protected: | ||
| 64 | pthread_mutex_t mutex; /**< The internal mutex reference. */ | ||
| 65 | }; | ||
| 66 | } | ||
| 67 | |||
| 68 | #endif | ||
| diff --git a/src/stable/mutexlocker.cpp b/src/stable/mutexlocker.cpp new file mode 100644 index 0000000..90b730e --- /dev/null +++ b/src/stable/mutexlocker.cpp | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | #include "bu/mutexlocker.h" | ||
| 2 | #include "bu/mutex.h" | ||
| 3 | |||
| 4 | Bu::MutexLocker::MutexLocker( Bu::Mutex &mu ) : | ||
| 5 | mu( mu ) | ||
| 6 | { | ||
| 7 | mu.lock(); | ||
| 8 | } | ||
| 9 | |||
| 10 | Bu::MutexLocker::~MutexLocker() | ||
| 11 | { | ||
| 12 | mu.unlock(); | ||
| 13 | } | ||
| 14 | |||
| 15 | void Bu::MutexLocker::unlock() | ||
| 16 | { | ||
| 17 | mu.unlock(); | ||
| 18 | } | ||
| 19 | |||
| 20 | void Bu::MutexLocker::relock() | ||
| 21 | { | ||
| 22 | mu.lock(); | ||
| 23 | } | ||
| 24 | |||
| diff --git a/src/stable/mutexlocker.h b/src/stable/mutexlocker.h new file mode 100644 index 0000000..7c3c97e --- /dev/null +++ b/src/stable/mutexlocker.h | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | #ifndef BU_MUTEX_LOCKER_H | ||
| 2 | #define BU_MUTEX_LOCKER_H | ||
| 3 | |||
| 4 | namespace Bu | ||
| 5 | { | ||
| 6 | class Mutex; | ||
| 7 | class MutexLocker | ||
| 8 | { | ||
| 9 | public: | ||
| 10 | MutexLocker( Mutex &mu ); | ||
| 11 | virtual ~MutexLocker(); | ||
| 12 | |||
| 13 | void unlock(); | ||
| 14 | void relock(); | ||
| 15 | |||
| 16 | private: | ||
| 17 | Mutex μ | ||
| 18 | }; | ||
| 19 | }; | ||
| 20 | |||
| 21 | #endif | ||
| diff --git a/src/stable/nullstream.cpp b/src/stable/nullstream.cpp new file mode 100644 index 0000000..9552cc5 --- /dev/null +++ b/src/stable/nullstream.cpp | |||
| @@ -0,0 +1,127 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/nullstream.h" | ||
| 9 | |||
| 10 | Bu::NullStream::NullStream() : | ||
| 11 | sRead( 0 ), | ||
| 12 | sWrote( 0 ) | ||
| 13 | { | ||
| 14 | } | ||
| 15 | |||
| 16 | Bu::NullStream::~NullStream() | ||
| 17 | { | ||
| 18 | } | ||
| 19 | |||
| 20 | void Bu::NullStream::close() | ||
| 21 | { | ||
| 22 | sRead = sWrote = 0; | ||
| 23 | } | ||
| 24 | |||
| 25 | Bu::size Bu::NullStream::read( void *pBuf, Bu::size nBytes ) | ||
| 26 | { | ||
| 27 | memset( pBuf, 0, nBytes ); | ||
| 28 | sRead += nBytes; | ||
| 29 | return nBytes; | ||
| 30 | } | ||
| 31 | |||
| 32 | Bu::String Bu::NullStream::readLine() | ||
| 33 | { | ||
| 34 | sRead++; | ||
| 35 | return Bu::String("\0", 1 ); | ||
| 36 | } | ||
| 37 | |||
| 38 | Bu::size Bu::NullStream::write( const void *, Bu::size nBytes ) | ||
| 39 | { | ||
| 40 | sWrote += nBytes; | ||
| 41 | return nBytes; | ||
| 42 | } | ||
| 43 | |||
| 44 | Bu::size Bu::NullStream::tell() | ||
| 45 | { | ||
| 46 | return sRead + sWrote; | ||
| 47 | } | ||
| 48 | |||
| 49 | void Bu::NullStream::seek( Bu::size ) | ||
| 50 | { | ||
| 51 | } | ||
| 52 | |||
| 53 | void Bu::NullStream::setPos( Bu::size ) | ||
| 54 | { | ||
| 55 | } | ||
| 56 | |||
| 57 | void Bu::NullStream::setPosEnd( Bu::size ) | ||
| 58 | { | ||
| 59 | } | ||
| 60 | |||
| 61 | bool Bu::NullStream::isEos() | ||
| 62 | { | ||
| 63 | return false; | ||
| 64 | } | ||
| 65 | |||
| 66 | bool Bu::NullStream::isOpen() | ||
| 67 | { | ||
| 68 | return true; | ||
| 69 | } | ||
| 70 | |||
| 71 | void Bu::NullStream::flush() | ||
| 72 | { | ||
| 73 | } | ||
| 74 | |||
| 75 | bool Bu::NullStream::canRead() | ||
| 76 | { | ||
| 77 | return true; | ||
| 78 | } | ||
| 79 | |||
| 80 | bool Bu::NullStream::canWrite() | ||
| 81 | { | ||
| 82 | return true; | ||
| 83 | } | ||
| 84 | |||
| 85 | bool Bu::NullStream::isReadable() | ||
| 86 | { | ||
| 87 | return true; | ||
| 88 | } | ||
| 89 | |||
| 90 | bool Bu::NullStream::isWritable() | ||
| 91 | { | ||
| 92 | return true; | ||
| 93 | } | ||
| 94 | |||
| 95 | bool Bu::NullStream::isSeekable() | ||
| 96 | { | ||
| 97 | return false; | ||
| 98 | } | ||
| 99 | |||
| 100 | bool Bu::NullStream::isBlocking() | ||
| 101 | { | ||
| 102 | return true; | ||
| 103 | } | ||
| 104 | |||
| 105 | void Bu::NullStream::setBlocking( bool ) | ||
| 106 | { | ||
| 107 | } | ||
| 108 | |||
| 109 | void Bu::NullStream::setSize( Bu::size ) | ||
| 110 | { | ||
| 111 | } | ||
| 112 | |||
| 113 | Bu::size Bu::NullStream::getSize() const | ||
| 114 | { | ||
| 115 | return 0; | ||
| 116 | } | ||
| 117 | |||
| 118 | Bu::size Bu::NullStream::getBlockSize() const | ||
| 119 | { | ||
| 120 | return 0; | ||
| 121 | } | ||
| 122 | |||
| 123 | Bu::String Bu::NullStream::getLocation() const | ||
| 124 | { | ||
| 125 | return ""; | ||
| 126 | } | ||
| 127 | |||
| diff --git a/src/stable/nullstream.h b/src/stable/nullstream.h new file mode 100644 index 0000000..9b75332 --- /dev/null +++ b/src/stable/nullstream.h | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_NULL_STREAM_H | ||
| 9 | #define BU_NULL_STREAM_H | ||
| 10 | |||
| 11 | #include "bu/stream.h" | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * Works a lot like /dev/null on *nix style systems. This class allows | ||
| 17 | * infinite reading and writing. All operatorns "succeed" even if they | ||
| 18 | * don't seem to do anything. This is great for testing writing code or | ||
| 19 | * doing dry runs. When reading, it will produce NULL bytes, so any | ||
| 20 | * application that would like the ability to produce null streams as a | ||
| 21 | * snap-in replacement for any other Bu::Stream, this is the right option. | ||
| 22 | * | ||
| 23 | * As an added feature, the NullStream will track how many bytes it was | ||
| 24 | * asked to read and write, allowing you to use it to determine how many | ||
| 25 | * bytes a write opretion would use without actually writing anything. | ||
| 26 | */ | ||
| 27 | class NullStream : public Bu::Stream | ||
| 28 | { | ||
| 29 | public: | ||
| 30 | NullStream(); | ||
| 31 | virtual ~NullStream(); | ||
| 32 | |||
| 33 | virtual void close(); | ||
| 34 | virtual Bu::size read( void *pBuf, Bu::size nBytes ); | ||
| 35 | virtual Bu::String readLine(); | ||
| 36 | virtual Bu::size write( const void *pBuf, Bu::size nBytes ); | ||
| 37 | using Bu::Stream::write; | ||
| 38 | virtual Bu::size tell(); | ||
| 39 | virtual void seek( Bu::size offset ); | ||
| 40 | virtual void setPos( Bu::size pos ); | ||
| 41 | virtual void setPosEnd( Bu::size pos ); | ||
| 42 | virtual bool isEos(); | ||
| 43 | virtual bool isOpen(); | ||
| 44 | virtual void flush(); | ||
| 45 | virtual bool canRead(); | ||
| 46 | virtual bool canWrite(); | ||
| 47 | virtual bool isReadable(); | ||
| 48 | virtual bool isWritable(); | ||
| 49 | virtual bool isSeekable(); | ||
| 50 | virtual bool isBlocking(); | ||
| 51 | virtual void setBlocking( bool bBlocking=true ); | ||
| 52 | virtual void setSize( Bu::size iSize ); | ||
| 53 | |||
| 54 | virtual size getSize() const; | ||
| 55 | virtual size getBlockSize() const; | ||
| 56 | virtual Bu::String getLocation() const; | ||
| 57 | |||
| 58 | Bu::size getBytesRead() { return sRead; } | ||
| 59 | Bu::size getByetsWritten() { return sWrote; } | ||
| 60 | |||
| 61 | private: | ||
| 62 | Bu::size sRead; | ||
| 63 | Bu::size sWrote; | ||
| 64 | }; | ||
| 65 | }; | ||
| 66 | |||
| 67 | #endif | ||
| diff --git a/src/stable/optparser.cpp b/src/stable/optparser.cpp new file mode 100644 index 0000000..050232c --- /dev/null +++ b/src/stable/optparser.cpp | |||
| @@ -0,0 +1,492 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/optparser.h" | ||
| 9 | #include "bu/sio.h" | ||
| 10 | using namespace Bu; | ||
| 11 | |||
| 12 | #include <stdlib.h> | ||
| 13 | |||
| 14 | Bu::OptParser::OptParser() | ||
| 15 | { | ||
| 16 | } | ||
| 17 | |||
| 18 | Bu::OptParser::~OptParser() | ||
| 19 | { | ||
| 20 | } | ||
| 21 | |||
| 22 | void Bu::OptParser::parse( int argc, char **argv ) | ||
| 23 | { | ||
| 24 | for( int j = 1; j < argc; j++ ) | ||
| 25 | { | ||
| 26 | if( argv[j][0] == '-' ) | ||
| 27 | { | ||
| 28 | // Now we're on to something, which kind is it? | ||
| 29 | if( argv[j][1] == '-' ) | ||
| 30 | { | ||
| 31 | int iEPos; | ||
| 32 | for( iEPos = 2; argv[j][iEPos] != '\0' && | ||
| 33 | argv[j][iEPos] != '='; iEPos++ ) { } | ||
| 34 | |||
| 35 | Bu::String sOpt; | ||
| 36 | int iCount = argc-j; | ||
| 37 | Bu::String sExtraParam; | ||
| 38 | if( argv[j][iEPos] == '=' ) | ||
| 39 | { | ||
| 40 | sOpt.set( argv[j]+2, iEPos-2 ); | ||
| 41 | iCount++; | ||
| 42 | sExtraParam.set( argv[j]+iEPos+1 ); | ||
| 43 | } | ||
| 44 | else | ||
| 45 | { | ||
| 46 | sOpt.set( argv[j]+2 ); | ||
| 47 | } | ||
| 48 | if( !hlOption.has( sOpt ) ) | ||
| 49 | { | ||
| 50 | optionError( "--" + sOpt ); | ||
| 51 | } | ||
| 52 | else | ||
| 53 | { | ||
| 54 | // Long param, cool, that's easy, first search for = | ||
| 55 | Option *pOpt = hlOption.get( sOpt ); | ||
| 56 | if( pOpt->sUsed ) | ||
| 57 | { | ||
| 58 | Bu::StrArray aParams( iCount ); | ||
| 59 | aParams.append( sOpt ); | ||
| 60 | if( sExtraParam.isSet() ) | ||
| 61 | { | ||
| 62 | aParams.append( argv[j]+iEPos+1 ); | ||
| 63 | } | ||
| 64 | for( int k = j+1; k < argc; k++ ) | ||
| 65 | { | ||
| 66 | aParams.append( argv[k] ); | ||
| 67 | } | ||
| 68 | j += pOpt->sUsed( aParams ); | ||
| 69 | } | ||
| 70 | else if( pOpt->pProxy ) | ||
| 71 | { | ||
| 72 | if( pOpt->sOverride.isSet() ) | ||
| 73 | { | ||
| 74 | pOpt->pProxy->setValue( pOpt->sOverride ); | ||
| 75 | } | ||
| 76 | else if( sExtraParam.isSet() ) | ||
| 77 | { | ||
| 78 | pOpt->pProxy->setValueFromStr( sExtraParam ); | ||
| 79 | } | ||
| 80 | else if( argv[j+1] != '\0' ) | ||
| 81 | { | ||
| 82 | pOpt->pProxy->setValueFromStr( argv[j+1] ); | ||
| 83 | j++; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | else | ||
| 89 | { | ||
| 90 | int iCPos; | ||
| 91 | for( iCPos = 1; argv[j][iCPos] != '\0'; iCPos++ ) | ||
| 92 | { | ||
| 93 | if( !hsOption.has( argv[j][iCPos] ) ) | ||
| 94 | { | ||
| 95 | Bu::String sOpt("-"); | ||
| 96 | sOpt += argv[j][iCPos]; | ||
| 97 | optionError( sOpt ); | ||
| 98 | } | ||
| 99 | else | ||
| 100 | { | ||
| 101 | Option *pOpt = hsOption.get( argv[j][iCPos] ); | ||
| 102 | char buf[2] = {argv[j][iCPos], '\0'}; | ||
| 103 | if( pOpt->sUsed ) | ||
| 104 | { | ||
| 105 | Bu::StrArray aParams( argc-j+1 ); | ||
| 106 | aParams.append( buf ); | ||
| 107 | int iMod = 0; | ||
| 108 | if( argv[j][iCPos+1] != '\0' ) | ||
| 109 | { | ||
| 110 | aParams.append( argv[j]+iCPos+1 ); | ||
| 111 | iMod = -1; | ||
| 112 | } | ||
| 113 | for( int k = j+1; k < argc; k++ ) | ||
| 114 | { | ||
| 115 | aParams.append( argv[k] ); | ||
| 116 | } | ||
| 117 | int iUsed = pOpt->sUsed( aParams ); | ||
| 118 | if( iUsed > 0 ) | ||
| 119 | { | ||
| 120 | j += iUsed + iMod; | ||
| 121 | break; | ||
| 122 | } | ||
| 123 | } | ||
| 124 | else if( pOpt->pProxy ) | ||
| 125 | { | ||
| 126 | if( pOpt->sOverride.isSet() ) | ||
| 127 | { | ||
| 128 | pOpt->pProxy->setValue( pOpt->sOverride ); | ||
| 129 | } | ||
| 130 | else if( argv[j][iCPos+1] != '\0' ) | ||
| 131 | { | ||
| 132 | pOpt->pProxy->setValueFromStr( | ||
| 133 | argv[j]+iCPos+1 | ||
| 134 | ); | ||
| 135 | break; | ||
| 136 | } | ||
| 137 | else if( argv[j+1] ) | ||
| 138 | { | ||
| 139 | pOpt->pProxy->setValueFromStr( | ||
| 140 | argv[j+1] | ||
| 141 | ); | ||
| 142 | j++; | ||
| 143 | break; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | } | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
| 150 | else | ||
| 151 | { | ||
| 152 | if( !sNonOption ) | ||
| 153 | { | ||
| 154 | optionError( argv[j] ); | ||
| 155 | } | ||
| 156 | else | ||
| 157 | { | ||
| 158 | int iCount = argc-j; | ||
| 159 | Bu::StrArray aParams( iCount ); | ||
| 160 | for( int k = j; k < argc; k++ ) | ||
| 161 | { | ||
| 162 | aParams.append( argv[k] ); | ||
| 163 | } | ||
| 164 | j += sNonOption( aParams ); | ||
| 165 | } | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | void Bu::OptParser::parse( const Bu::String &sLine ) | ||
| 171 | { | ||
| 172 | Bu::String sCmd = sLine.clone(); | ||
| 173 | int iParams = 0; | ||
| 174 | bool bInGap = true; | ||
| 175 | bool bInQuote = false; | ||
| 176 | for( Bu::String::iterator i = sCmd.begin(); i; i++ ) | ||
| 177 | { | ||
| 178 | if( bInQuote == false && (*i == ' ' || *i == '\t') ) | ||
| 179 | { | ||
| 180 | if( bInGap == false ) | ||
| 181 | { | ||
| 182 | bInGap = true; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | else if( *i == '"' ) | ||
| 186 | { | ||
| 187 | bInQuote = !bInQuote; | ||
| 188 | } | ||
| 189 | else | ||
| 190 | { | ||
| 191 | if( bInGap ) | ||
| 192 | { | ||
| 193 | iParams++; | ||
| 194 | bInGap = false; | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | bInQuote = false; | ||
| 200 | bInGap = true; | ||
| 201 | char **asParam = new char*[iParams]; | ||
| 202 | iParams = 0; | ||
| 203 | for( char *i = sCmd.getStr(); *i; i++ ) | ||
| 204 | { | ||
| 205 | if( bInQuote == false && (*i == ' ' || *i == '\t') ) | ||
| 206 | { | ||
| 207 | if( bInGap == false ) | ||
| 208 | { | ||
| 209 | bInGap = true; | ||
| 210 | *i = '\0'; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | else if( *i == '"' ) | ||
| 214 | { | ||
| 215 | bInQuote = !bInQuote; | ||
| 216 | } | ||
| 217 | else | ||
| 218 | { | ||
| 219 | if( bInGap ) | ||
| 220 | { | ||
| 221 | asParam[iParams++] = i; | ||
| 222 | bInGap = false; | ||
| 223 | } | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | parse( iParams, asParam ); | ||
| 228 | |||
| 229 | delete[] asParam; | ||
| 230 | } | ||
| 231 | |||
| 232 | void Bu::OptParser::addOption( const Option &opt ) | ||
| 233 | { | ||
| 234 | lOption.append( opt ); | ||
| 235 | if( opt.cOpt != '\0' ) | ||
| 236 | hsOption.insert( opt.cOpt, &lOption.last() ); | ||
| 237 | if( opt.sOpt.isSet() ) | ||
| 238 | hlOption.insert( opt.sOpt, &lOption.last() ); | ||
| 239 | } | ||
| 240 | |||
| 241 | void Bu::OptParser::setOverride( char cOpt, const Bu::Variant &sOverride ) | ||
| 242 | { | ||
| 243 | hsOption.get( cOpt )->sOverride = sOverride; | ||
| 244 | } | ||
| 245 | |||
| 246 | void Bu::OptParser::setOverride( const Bu::String &sOpt, const Bu::Variant &sOverride ) | ||
| 247 | { | ||
| 248 | hlOption.get( sOpt )->sOverride = sOverride; | ||
| 249 | } | ||
| 250 | |||
| 251 | void Bu::OptParser::setHelpDefault( const Bu::String &sOpt, const Bu::String &sTxt ) | ||
| 252 | { | ||
| 253 | hlOption.get( sOpt )->sHelpDefault = sTxt; | ||
| 254 | } | ||
| 255 | |||
| 256 | void Bu::OptParser::addHelpOption( char c, const Bu::String &s, const Bu::String &sHelp ) | ||
| 257 | { | ||
| 258 | Option o; | ||
| 259 | o.sUsed = slot( this, &OptParser::optHelp ); | ||
| 260 | o.cOpt = c; | ||
| 261 | o.sOpt = s; | ||
| 262 | o.sHelp = sHelp; | ||
| 263 | addOption( o ); | ||
| 264 | } | ||
| 265 | |||
| 266 | void Bu::OptParser::addHelpBanner( const Bu::String &sText, bool bFormatted ) | ||
| 267 | { | ||
| 268 | Banner b; | ||
| 269 | b.sText = sText; | ||
| 270 | b.bFormatted = bFormatted; | ||
| 271 | if( lOption.getSize() > 0 ) | ||
| 272 | { | ||
| 273 | for( b.iAfter = lOption.begin(); b.iAfter+1; b.iAfter++ ) { } | ||
| 274 | } | ||
| 275 | lBanner.append( b ); | ||
| 276 | } | ||
| 277 | |||
| 278 | int Bu::OptParser::optHelp( StrArray /*aParams*/ ) | ||
| 279 | { | ||
| 280 | bool bHasShort = false; | ||
| 281 | int iMaxWidth = 0; | ||
| 282 | int iScrWidth = 80; | ||
| 283 | char *env = getenv("COLUMNS"); | ||
| 284 | if( env ) | ||
| 285 | iScrWidth = strtol( env, NULL, 10 ); | ||
| 286 | for( OptionList::iterator i = lOption.begin(); i; i++ ) | ||
| 287 | { | ||
| 288 | if( (*i).cOpt != '\0' ) | ||
| 289 | bHasShort = true; | ||
| 290 | int lOptSize = (*i).sOpt.getSize() + (*i).sHelpDefault.getSize(); | ||
| 291 | if( (*i).sOpt.isSet() && iMaxWidth < lOptSize ) | ||
| 292 | iMaxWidth = lOptSize; | ||
| 293 | } | ||
| 294 | int iIndent = 4; | ||
| 295 | if( bHasShort ) | ||
| 296 | iIndent += 4; | ||
| 297 | if( iMaxWidth > 0 ) | ||
| 298 | iIndent += 4 + iMaxWidth; | ||
| 299 | |||
| 300 | BannerList::iterator iBanner; | ||
| 301 | for( iBanner = lBanner.begin(); iBanner; iBanner++ ) | ||
| 302 | { | ||
| 303 | if( (*iBanner).iAfter ) | ||
| 304 | break; | ||
| 305 | |||
| 306 | if( (*iBanner).bFormatted ) | ||
| 307 | sio << format( (*iBanner).sText, iScrWidth-1, 0 ); | ||
| 308 | else | ||
| 309 | sio << (*iBanner).sText; | ||
| 310 | sio << sio.nl; | ||
| 311 | } | ||
| 312 | for( OptionList::iterator i = lOption.begin(); i; i++ ) | ||
| 313 | { | ||
| 314 | sio << " "; | ||
| 315 | if( bHasShort ) | ||
| 316 | { | ||
| 317 | if( (*i).cOpt == '\0' ) | ||
| 318 | sio << " "; | ||
| 319 | else | ||
| 320 | sio << "-" << (*i).cOpt; | ||
| 321 | sio << " "; | ||
| 322 | } | ||
| 323 | if( iMaxWidth > 0 ) | ||
| 324 | { | ||
| 325 | if( (*i).sOpt.isSet() ) | ||
| 326 | { | ||
| 327 | sio << "--" << Fmt(iMaxWidth, Fmt::Left) | ||
| 328 | << (*i).sOpt + (*i).sHelpDefault; | ||
| 329 | } | ||
| 330 | else | ||
| 331 | { | ||
| 332 | sio << " " << Fmt(iMaxWidth) << ""; | ||
| 333 | } | ||
| 334 | sio << " "; | ||
| 335 | } | ||
| 336 | sio << format( (*i).sHelp, iScrWidth-iIndent-1, iIndent ); | ||
| 337 | sio << sio.nl; | ||
| 338 | |||
| 339 | for( ; iBanner; iBanner++ ) | ||
| 340 | { | ||
| 341 | if( (*iBanner).iAfter != i ) | ||
| 342 | break; | ||
| 343 | |||
| 344 | if( (*iBanner).bFormatted ) | ||
| 345 | sio << format( (*iBanner).sText, iScrWidth-1, 0 ); | ||
| 346 | else | ||
| 347 | sio << (*iBanner).sText; | ||
| 348 | sio << sio.nl; | ||
| 349 | } | ||
| 350 | } | ||
| 351 | exit( 0 ); | ||
| 352 | return 0; | ||
| 353 | } | ||
| 354 | |||
| 355 | void Bu::OptParser::optionError( const Bu::String &sOption ) | ||
| 356 | { | ||
| 357 | sio << "Unregcognized option discovered: " << sOption << sio.nl << sio.nl; | ||
| 358 | exit( 1 ); | ||
| 359 | } | ||
| 360 | |||
| 361 | void Bu::OptParser::setNonOption( OptionSignal sSignal ) | ||
| 362 | { | ||
| 363 | sNonOption = sSignal; | ||
| 364 | } | ||
| 365 | |||
| 366 | Bu::String Bu::OptParser::format( const Bu::String &sIn, int iWidth, | ||
| 367 | int iIndent ) | ||
| 368 | { | ||
| 369 | Bu::String sOut; | ||
| 370 | Bu::String sIndent; | ||
| 371 | for( int j = 0; j < iIndent; j++ ) | ||
| 372 | sIndent.append(" ", 1); | ||
| 373 | bool bFirst = true; | ||
| 374 | int iSpaceCount = 0; | ||
| 375 | bool bSpace = false; | ||
| 376 | int iPrevLineLen; | ||
| 377 | int iLineLen = 0; | ||
| 378 | Bu::String::const_iterator iLastSpace, iStart; | ||
| 379 | for( Bu::String::const_iterator i = iLastSpace = iStart = sIn.begin(); i; i++ ) | ||
| 380 | { | ||
| 381 | if( *i == ' ' ) | ||
| 382 | { | ||
| 383 | if( bSpace == false ) | ||
| 384 | { | ||
| 385 | iLastSpace = i; | ||
| 386 | iSpaceCount++; | ||
| 387 | bSpace = true; | ||
| 388 | iPrevLineLen = iLineLen; | ||
| 389 | } | ||
| 390 | } | ||
| 391 | else | ||
| 392 | { | ||
| 393 | bSpace = false; | ||
| 394 | } | ||
| 395 | iLineLen++; | ||
| 396 | |||
| 397 | if( iLineLen >= iWidth ) | ||
| 398 | { | ||
| 399 | iSpaceCount--; | ||
| 400 | if( bFirst == true ) | ||
| 401 | bFirst = false; | ||
| 402 | else | ||
| 403 | sOut += sIndent; | ||
| 404 | int iExtraSpaces = iWidth-iPrevLineLen; | ||
| 405 | bSpace = false; | ||
| 406 | float fFill = 0.0; | ||
| 407 | int iSubSpaceCount = 0; | ||
| 408 | float fAdd = ((float)iExtraSpaces/(float)iSpaceCount); | ||
| 409 | for( Bu::String::const_iterator k = iStart; k != iLastSpace; k++ ) | ||
| 410 | { | ||
| 411 | sOut += *k; | ||
| 412 | if( *k == ' ' ) | ||
| 413 | { | ||
| 414 | if( bSpace == false && iExtraSpaces > 0 ) | ||
| 415 | { | ||
| 416 | bSpace = true; | ||
| 417 | fFill += fAdd; | ||
| 418 | iSubSpaceCount++; | ||
| 419 | for( int sp = 0; sp < (int)(fFill); sp++ ) | ||
| 420 | { | ||
| 421 | sOut += ' '; | ||
| 422 | iExtraSpaces--; | ||
| 423 | } | ||
| 424 | fFill -= (int)fFill; | ||
| 425 | if( iSubSpaceCount == iSpaceCount && iExtraSpaces > 0 ) | ||
| 426 | { | ||
| 427 | for(; iExtraSpaces > 0; iExtraSpaces-- ) | ||
| 428 | { | ||
| 429 | sOut += ' '; | ||
| 430 | } | ||
| 431 | } | ||
| 432 | } | ||
| 433 | } | ||
| 434 | else | ||
| 435 | bSpace = false; | ||
| 436 | } | ||
| 437 | //sOut.append( iStart, iLastSpace ); | ||
| 438 | sOut.append("\n"); | ||
| 439 | for(; iLastSpace && *iLastSpace == ' '; iLastSpace++ ) { } | ||
| 440 | iStart = i = iLastSpace; | ||
| 441 | bSpace = false; | ||
| 442 | iLineLen = 1; | ||
| 443 | iSpaceCount = 0; | ||
| 444 | } | ||
| 445 | } | ||
| 446 | if( !bFirst ) | ||
| 447 | sOut += sIndent; | ||
| 448 | sOut.append( iStart ); | ||
| 449 | return sOut; | ||
| 450 | } | ||
| 451 | |||
| 452 | |||
| 453 | // | ||
| 454 | // Code for Bu::OptParser::_ValueProxy | ||
| 455 | // | ||
| 456 | |||
| 457 | Bu::OptParser::_ValueProxy::_ValueProxy() | ||
| 458 | { | ||
| 459 | } | ||
| 460 | |||
| 461 | Bu::OptParser::_ValueProxy::~_ValueProxy() | ||
| 462 | { | ||
| 463 | } | ||
| 464 | |||
| 465 | // | ||
| 466 | // Code for Bu::OptParser::Option | ||
| 467 | // | ||
| 468 | |||
| 469 | Bu::OptParser::Option::Option() : | ||
| 470 | cOpt( '\0' ), | ||
| 471 | pProxy( NULL ) | ||
| 472 | { | ||
| 473 | } | ||
| 474 | |||
| 475 | Bu::OptParser::Option::Option( const Option &rSrc ) : | ||
| 476 | cOpt( rSrc.cOpt ), | ||
| 477 | sOpt( rSrc.sOpt ), | ||
| 478 | sHelp( rSrc.sHelp ), | ||
| 479 | sUsed( rSrc.sUsed ), | ||
| 480 | pProxy( NULL ), | ||
| 481 | sOverride( rSrc.sOverride ) | ||
| 482 | { | ||
| 483 | if( rSrc.pProxy ) | ||
| 484 | pProxy = rSrc.pProxy->clone(); | ||
| 485 | } | ||
| 486 | |||
| 487 | Bu::OptParser::Option::~Option() | ||
| 488 | { | ||
| 489 | delete pProxy; | ||
| 490 | pProxy = NULL; | ||
| 491 | } | ||
| 492 | |||
| diff --git a/src/stable/optparser.h b/src/stable/optparser.h new file mode 100644 index 0000000..f2fe531 --- /dev/null +++ b/src/stable/optparser.h | |||
| @@ -0,0 +1,223 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_OPT_PARSER_H | ||
| 9 | #define BU_OPT_PARSER_H | ||
| 10 | |||
| 11 | #include "bu/string.h" | ||
| 12 | #include "bu/list.h" | ||
| 13 | #include "bu/hash.h" | ||
| 14 | #include "bu/signals.h" | ||
| 15 | #include "bu/array.h" | ||
| 16 | #include "bu/membuf.h" | ||
| 17 | #include "bu/formatter.h" | ||
| 18 | #include "bu/variant.h" | ||
| 19 | |||
| 20 | namespace Bu | ||
| 21 | { | ||
| 22 | typedef Bu::Array<Bu::String> StrArray; | ||
| 23 | |||
| 24 | /** | ||
| 25 | * POSIX/Gnu style command line parser. Handles long and short options in | ||
| 26 | * a variety of fun and useful ways, along with singal based callbacks and | ||
| 27 | * automatic variable setting. It's pretty easy to use, and very flexible. | ||
| 28 | * | ||
| 29 | * OptParser supports it's own builtin help mechanism which automatically | ||
| 30 | * enumerates the available options and their help in a well formatted and | ||
| 31 | * easy to read way, automatically formatting your help text per option and | ||
| 32 | * allows for addition "help banners" which can be placed wherever you | ||
| 33 | * would like. | ||
| 34 | */ | ||
| 35 | class OptParser | ||
| 36 | { | ||
| 37 | private: | ||
| 38 | class _ValueProxy | ||
| 39 | { | ||
| 40 | public: | ||
| 41 | _ValueProxy(); | ||
| 42 | virtual ~_ValueProxy(); | ||
| 43 | |||
| 44 | virtual void setValueFromStr( const Bu::String & )=0; | ||
| 45 | virtual void setValue( const Bu::Variant &vVar )=0; | ||
| 46 | virtual _ValueProxy *clone()=0; | ||
| 47 | }; | ||
| 48 | |||
| 49 | template<typename ptype> | ||
| 50 | class ValueProxy : public _ValueProxy | ||
| 51 | { | ||
| 52 | public: | ||
| 53 | ValueProxy( ptype &v ) : | ||
| 54 | v( v ) | ||
| 55 | { | ||
| 56 | } | ||
| 57 | |||
| 58 | virtual ~ValueProxy() | ||
| 59 | { | ||
| 60 | } | ||
| 61 | |||
| 62 | virtual void setValueFromStr( const Bu::String &sVal ) | ||
| 63 | { | ||
| 64 | Bu::MemBuf mb( sVal ); | ||
| 65 | Bu::Formatter f( mb ); | ||
| 66 | f << Bu::Fmt().tokenize( false ); | ||
| 67 | f >> v; | ||
| 68 | } | ||
| 69 | |||
| 70 | virtual void setValue( const Bu::Variant &vVar ) | ||
| 71 | { | ||
| 72 | if( vVar.getType() == typeid(ptype) ) | ||
| 73 | { | ||
| 74 | v = vVar.get<ptype>(); | ||
| 75 | } | ||
| 76 | else if( vVar.getType() == typeid(Bu::String) ) | ||
| 77 | { | ||
| 78 | setValueFromStr( vVar.get<Bu::String>() ); | ||
| 79 | } | ||
| 80 | else | ||
| 81 | { | ||
| 82 | Bu::MemBuf mb; | ||
| 83 | Bu::Formatter f( mb ); | ||
| 84 | // f << vVar; | ||
| 85 | setValueFromStr( mb.getString() ); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | virtual _ValueProxy *clone() | ||
| 90 | { | ||
| 91 | return new ValueProxy<ptype>( v ); | ||
| 92 | } | ||
| 93 | |||
| 94 | private: | ||
| 95 | ptype &v; | ||
| 96 | }; | ||
| 97 | |||
| 98 | public: | ||
| 99 | typedef Signal1<int, StrArray> OptionSignal; | ||
| 100 | class Option | ||
| 101 | { | ||
| 102 | public: | ||
| 103 | Option(); | ||
| 104 | Option( const Option &rSrc ); | ||
| 105 | virtual ~Option(); | ||
| 106 | |||
| 107 | char cOpt; | ||
| 108 | Bu::String sOpt; | ||
| 109 | Bu::String sHelp; | ||
| 110 | OptionSignal sUsed; | ||
| 111 | _ValueProxy *pProxy; | ||
| 112 | Bu::Variant sOverride; | ||
| 113 | Bu::String sHelpDefault; | ||
| 114 | }; | ||
| 115 | |||
| 116 | private: | ||
| 117 | typedef Bu::List<Option> OptionList; | ||
| 118 | typedef Bu::Hash<char, Option *> ShortOptionHash; | ||
| 119 | typedef Bu::Hash<Bu::String, Option *> LongOptionHash; | ||
| 120 | |||
| 121 | class Banner | ||
| 122 | { | ||
| 123 | public: | ||
| 124 | Bu::String sText; | ||
| 125 | bool bFormatted; | ||
| 126 | OptionList::const_iterator iAfter; | ||
| 127 | }; | ||
| 128 | |||
| 129 | typedef Bu::List<Banner> BannerList; | ||
| 130 | |||
| 131 | public: | ||
| 132 | OptParser(); | ||
| 133 | virtual ~OptParser(); | ||
| 134 | |||
| 135 | void parse( int argc, char **argv ); | ||
| 136 | void parse( const Bu::String &sLine ); | ||
| 137 | |||
| 138 | void addOption( const Option &opt ); | ||
| 139 | |||
| 140 | template<typename vtype> | ||
| 141 | void addOption( vtype &var, char cOpt, const Bu::String &sOpt, | ||
| 142 | const Bu::String &sHelp ) | ||
| 143 | { | ||
| 144 | Option o; | ||
| 145 | o.cOpt = cOpt; | ||
| 146 | o.sOpt = sOpt; | ||
| 147 | o.pProxy = new ValueProxy<vtype>( var ); | ||
| 148 | o.sHelp = sHelp; | ||
| 149 | addOption( o ); | ||
| 150 | } | ||
| 151 | |||
| 152 | template<typename vtype> | ||
| 153 | void addOption( vtype &var, const Bu::String &sOpt, | ||
| 154 | const Bu::String &sHelp ) | ||
| 155 | { | ||
| 156 | addOption( var, '\0', sOpt, sHelp ); | ||
| 157 | } | ||
| 158 | |||
| 159 | template<typename vtype> | ||
| 160 | void addOption( vtype &var, char cOpt, const Bu::String &sHelp ) | ||
| 161 | { | ||
| 162 | addOption( var, cOpt, "", sHelp ); | ||
| 163 | } | ||
| 164 | |||
| 165 | void addOption( OptionSignal sUsed, char cOpt, const Bu::String &sOpt, | ||
| 166 | const Bu::String &sHelp ) | ||
| 167 | { | ||
| 168 | Option o; | ||
| 169 | o.cOpt = cOpt; | ||
| 170 | o.sOpt = sOpt; | ||
| 171 | o.sUsed = sUsed; | ||
| 172 | o.sHelp = sHelp; | ||
| 173 | addOption( o ); | ||
| 174 | } | ||
| 175 | |||
| 176 | void addOption( OptionSignal sUsed, const Bu::String &sOpt, | ||
| 177 | const Bu::String &sHelp ) | ||
| 178 | { | ||
| 179 | addOption( sUsed, '\0', sOpt, sHelp ); | ||
| 180 | } | ||
| 181 | |||
| 182 | void addOption( OptionSignal sUsed, char cOpt, | ||
| 183 | const Bu::String &sHelp ) | ||
| 184 | { | ||
| 185 | addOption( sUsed, cOpt, "", sHelp ); | ||
| 186 | } | ||
| 187 | |||
| 188 | void setOverride( char cOpt, const Bu::Variant &sOverride ); | ||
| 189 | void setOverride( const Bu::String &sOpt, | ||
| 190 | const Bu::Variant &sOverride ); | ||
| 191 | |||
| 192 | void setHelpDefault( const Bu::String &sOpt, const Bu::String &sTxt ); | ||
| 193 | |||
| 194 | void addHelpOption( char c='h', const Bu::String &s="help", | ||
| 195 | const Bu::String &sHelp="This help." ); | ||
| 196 | void addHelpBanner( const Bu::String &sText, bool bFormatted=true ); | ||
| 197 | |||
| 198 | int optHelp( StrArray aParams ); | ||
| 199 | |||
| 200 | /** | ||
| 201 | * This function is called when an unrecognized option is found, the | ||
| 202 | * default behaviour is to print an error to stdout and exit( 1 ), if | ||
| 203 | * you want to do something different, just override this function. | ||
| 204 | * This is also called by default when something is found that hasn't | ||
| 205 | * been handled by an option, and isn't an option (starts with - or --). | ||
| 206 | * To change this behaviour call | ||
| 207 | */ | ||
| 208 | virtual void optionError( const Bu::String &sOption ); | ||
| 209 | |||
| 210 | void setNonOption( OptionSignal sSignal ); | ||
| 211 | |||
| 212 | private: | ||
| 213 | Bu::String format( const Bu::String &sIn, int iWidth, int iIndent ); | ||
| 214 | |||
| 215 | OptionList lOption; | ||
| 216 | ShortOptionHash hsOption; | ||
| 217 | LongOptionHash hlOption; | ||
| 218 | BannerList lBanner; | ||
| 219 | OptionSignal sNonOption; | ||
| 220 | }; | ||
| 221 | }; | ||
| 222 | |||
| 223 | #endif | ||
| diff --git a/src/stable/pearsonhash.cpp b/src/stable/pearsonhash.cpp new file mode 100644 index 0000000..d753d20 --- /dev/null +++ b/src/stable/pearsonhash.cpp | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/pearsonhash.h" | ||
| 9 | #include "bu/stream.h" | ||
| 10 | |||
| 11 | uint8_t Bu::PearsonHash::aSBox[] = { | ||
| 12 | 251, 175, 119, 215, 81, 14, 79, 191, 103, 49, 181, 143, 186, 157, 0, | ||
| 13 | 232, 31, 32, 55, 60, 152, 58, 17, 237, 174, 70, 160, 144, 220, 90, 57, | ||
| 14 | 223, 59, 3, 18, 140, 111, 166, 203, 196, 134, 243, 124, 95, 222, 179, | ||
| 15 | 197, 65, 180, 48, 36, 15, 107, 46, 233, 130, 165, 30, 123, 161, 209, 23, | ||
| 16 | 97, 16, 40, 91, 219, 61, 100, 10, 210, 109, 250, 127, 22, 138, 29, 108, | ||
| 17 | 244, 67, 207, 9, 178, 204, 74, 98, 126, 249, 167, 116, 34, 77, 193, | ||
| 18 | 200, 121, 5, 20, 113, 71, 35, 128, 13, 182, 94, 25, 226, 227, 199, 75, | ||
| 19 | 27, 41, 245, 230, 224, 43, 225, 177, 26, 155, 150, 212, 142, 218, 115, | ||
| 20 | 241, 73, 88, 105, 39, 114, 62, 255, 192, 201, 145, 214, 168, 158, 221, | ||
| 21 | 148, 154, 122, 12, 84, 82, 163, 44, 139, 228, 236, 205, 242, 217, 11, | ||
| 22 | 187, 146, 159, 64, 86, 239, 195, 42, 106, 198, 118, 112, 184, 172, 87, | ||
| 23 | 2, 173, 117, 176, 229, 247, 253, 137, 185, 99, 164, 102, 147, 45, 66, | ||
| 24 | 231, 52, 141, 211, 194, 206, 246, 238, 56, 110, 78, 248, 63, 240, 189, | ||
| 25 | 93, 92, 51, 53, 183, 19, 171, 72, 50, 33, 104, 101, 69, 8, 252, 83, 120, | ||
| 26 | 76, 135, 85, 54, 202, 125, 188, 213, 96, 235, 136, 208, 162, 129, 190, | ||
| 27 | 132, 156, 38, 47, 1, 7, 254, 24, 4, 216, 131, 89, 21, 28, 133, 37, 153, | ||
| 28 | 149, 80, 170, 68, 6, 169, 234, 151 | ||
| 29 | }; | ||
| 30 | |||
| 31 | Bu::PearsonHash::PearsonHash() | ||
| 32 | { | ||
| 33 | reset(); | ||
| 34 | } | ||
| 35 | |||
| 36 | Bu::PearsonHash::~PearsonHash() | ||
| 37 | { | ||
| 38 | } | ||
| 39 | |||
| 40 | void Bu::PearsonHash::reset() | ||
| 41 | { | ||
| 42 | iValue = 0; | ||
| 43 | } | ||
| 44 | |||
| 45 | void Bu::PearsonHash::setSalt( const Bu::String & /*sSalt*/ ) | ||
| 46 | { | ||
| 47 | } | ||
| 48 | |||
| 49 | void Bu::PearsonHash::addData( const void *sData, int iSize ) | ||
| 50 | { | ||
| 51 | for( int j = 0; j < iSize; j++ ) | ||
| 52 | { | ||
| 53 | iValue = aSBox[ iValue^((unsigned char *)sData)[j] ]; | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | Bu::String Bu::PearsonHash::getResult() | ||
| 58 | { | ||
| 59 | return Bu::String((char)iValue); | ||
| 60 | } | ||
| 61 | |||
| 62 | void Bu::PearsonHash::writeResult( Stream &sOut ) | ||
| 63 | { | ||
| 64 | sOut.write( &iValue, 1 ); | ||
| 65 | } | ||
| 66 | |||
| diff --git a/src/stable/pearsonhash.h b/src/stable/pearsonhash.h new file mode 100644 index 0000000..2a9cfa7 --- /dev/null +++ b/src/stable/pearsonhash.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_PEARSON_HASH_H | ||
| 9 | #define BU_PEARSON_HASH_H | ||
| 10 | |||
| 11 | #include "bu/cryptohash.h" | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * A pearson hash is a non-cryptographically secure hashing function that | ||
| 17 | * is very light on resources, very fast, and produces a single byte | ||
| 18 | * as it's output. It is strongly dependant on every byte in the input, | ||
| 19 | * which means that it's a good choice for adding to short messages to | ||
| 20 | * ensure that the contents of the messages are unchanged. | ||
| 21 | * | ||
| 22 | * Pearson hash is named for it's inventor Peter K. Pearson who described | ||
| 23 | * it in his article "Fast hashing of variable-length text strings" | ||
| 24 | * published in 1990 by ACM. I haven't read it, because you have to pay to | ||
| 25 | * get a copy :-P | ||
| 26 | */ | ||
| 27 | class PearsonHash : public Bu::CryptoHash | ||
| 28 | { | ||
| 29 | public: | ||
| 30 | PearsonHash(); | ||
| 31 | virtual ~PearsonHash(); | ||
| 32 | |||
| 33 | virtual void reset(); | ||
| 34 | virtual void setSalt( const Bu::String &sSalt ); | ||
| 35 | virtual void addData( const void *sData, int iSize ); | ||
| 36 | using Bu::CryptoHash::addData; | ||
| 37 | virtual String getResult(); | ||
| 38 | virtual void writeResult( Stream &sOut ); | ||
| 39 | |||
| 40 | private: | ||
| 41 | static uint8_t aSBox[256]; | ||
| 42 | uint8_t iValue; | ||
| 43 | }; | ||
| 44 | }; | ||
| 45 | |||
| 46 | #endif | ||
| diff --git a/src/stable/plugger.cpp b/src/stable/plugger.cpp new file mode 100644 index 0000000..fb6850a --- /dev/null +++ b/src/stable/plugger.cpp | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/plugger.h" | ||
| 9 | |||
| 10 | namespace Bu { subExceptionDef( PluginException ) } | ||
| 11 | |||
| diff --git a/src/stable/plugger.h b/src/stable/plugger.h new file mode 100644 index 0000000..c22f964 --- /dev/null +++ b/src/stable/plugger.h | |||
| @@ -0,0 +1,289 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_PLUGGER_H | ||
| 9 | #define BU_PLUGGER_H | ||
| 10 | |||
| 11 | #include "bu/hash.h" | ||
| 12 | #include "bu/list.h" | ||
| 13 | #include "bu/exceptionbase.h" | ||
| 14 | #include "bu/string.h" | ||
| 15 | #include <stddef.h> | ||
| 16 | |||
| 17 | #include "bu/config.h" | ||
| 18 | |||
| 19 | #ifdef WIN32 | ||
| 20 | # include <windows.h> | ||
| 21 | #else | ||
| 22 | # include <dlfcn.h> | ||
| 23 | #endif | ||
| 24 | |||
| 25 | namespace Bu | ||
| 26 | { | ||
| 27 | subExceptionDecl( PluginException ); | ||
| 28 | |||
| 29 | typedef struct PluginInfo | ||
| 30 | { | ||
| 31 | const char *sID; | ||
| 32 | const char *sAuthor; | ||
| 33 | unsigned short nVersion; | ||
| 34 | unsigned short nRevision; | ||
| 35 | void *(*createPlugin)(); | ||
| 36 | void (*destroyPlugin)( void * ); | ||
| 37 | } PluginInfo; | ||
| 38 | |||
| 39 | typedef struct PluginReg | ||
| 40 | { | ||
| 41 | bool bBuiltin; | ||
| 42 | #ifdef WIN32 | ||
| 43 | HMODULE dlHandle; | ||
| 44 | #else | ||
| 45 | void *dlHandle; | ||
| 46 | #endif | ||
| 47 | PluginInfo *pInfo; | ||
| 48 | } PluginReg; | ||
| 49 | |||
| 50 | #define PluginInterface( classname, baseclass, name, ver, rev ) \ | ||
| 51 | extern "C" { \ | ||
| 52 | baseclass *create ##classname() \ | ||
| 53 | { \ | ||
| 54 | return new classname(); \ | ||
| 55 | } \ | ||
| 56 | void destroy ##classname( baseclass *pCls ) \ | ||
| 57 | { \ | ||
| 58 | delete pCls; \ | ||
| 59 | } \ | ||
| 60 | Bu::PluginInfo classname = { \ | ||
| 61 | #classname, name, ver, rev, \ | ||
| 62 | create ##classname, destroy ##classname }; \ | ||
| 63 | } | ||
| 64 | |||
| 65 | #define PluginInterface2( pluginname, classname, baseclass, name, ver, rev ) \ | ||
| 66 | extern "C" { \ | ||
| 67 | baseclass *create ##classname() \ | ||
| 68 | { \ | ||
| 69 | return new classname(); \ | ||
| 70 | } \ | ||
| 71 | void destroy ##classname( baseclass *pCls ) \ | ||
| 72 | { \ | ||
| 73 | delete pCls; \ | ||
| 74 | } \ | ||
| 75 | Bu::PluginInfo pluginname = { \ | ||
| 76 | #pluginname, name, ver, rev, \ | ||
| 77 | (void *(*)())(create ##classname), \ | ||
| 78 | (void (*)( void * ))(destroy ##classname) }; \ | ||
| 79 | } | ||
| 80 | |||
| 81 | // | ||
| 82 | // This is probably the main interface to use, I'll describe it some here... | ||
| 83 | // structname - The name of the structure, this is what you have to pass to | ||
| 84 | // register. Depending on how you build your dll/so files this | ||
| 85 | // will need to be unique (generally not) | ||
| 86 | // pluginname - This is what will be used by the plugin system to refer to | ||
| 87 | // your plugin once it's loaded. This should be unique, but not | ||
| 88 | // a string | ||
| 89 | // classname - The name of the class that is the plugin | ||
| 90 | // baseclass - The name of the base class that is the parent of the plugin | ||
| 91 | // name - The name of the author of this class (or company) | ||
| 92 | // ver - an integer version number for the plugin | ||
| 93 | // rev - an integer revision number for the plugin | ||
| 94 | // | ||
| 95 | #define PluginInterface3( structname, pluginname, classname, baseclass, name, ver, rev ) \ | ||
| 96 | extern "C" { \ | ||
| 97 | baseclass *create ##classname() \ | ||
| 98 | { \ | ||
| 99 | return new classname(); \ | ||
| 100 | } \ | ||
| 101 | void destroy ##classname( baseclass *pCls ) \ | ||
| 102 | { \ | ||
| 103 | delete pCls; \ | ||
| 104 | } \ | ||
| 105 | Bu::PluginInfo structname = { \ | ||
| 106 | #pluginname, name, ver, rev, \ | ||
| 107 | (void *(*)())(create ##classname), \ | ||
| 108 | (void (*)( void * ))(destroy ##classname) }; \ | ||
| 109 | } | ||
| 110 | |||
| 111 | /** | ||
| 112 | * A complete dynamic plugin manager system. This will allow you to design | ||
| 113 | * and use plugins that are compiled into your program and dynamically | ||
| 114 | * linked to your program interchangably. It works on windows and on *nix | ||
| 115 | * and bsd type systems (anything that supports dlopen). Basically you | ||
| 116 | * create a base class that will be the basic interface of your plugins. | ||
| 117 | * Then you create some classes that inherit from it, and use the | ||
| 118 | * PluginInterface3 macro to create the required data structures for it. | ||
| 119 | * | ||
| 120 | * Once you have plugins you can create a Plugger, by passing in the base | ||
| 121 | * class as it's template parameter. Once it's created, you can register | ||
| 122 | * plugins. To register a plugin that is builtin, you just need to pass | ||
| 123 | * a pointer to it's interface structure to the registerBuiltinPlugin | ||
| 124 | * function. To register a plugin that is in a shared object or dll file | ||
| 125 | * you just pass the filename (with path, probably), and the name of the | ||
| 126 | * structure to load and you're all set. | ||
| 127 | * | ||
| 128 | * To instantiate an object from a plugin simply call instantiate with the | ||
| 129 | * name of the plugin as specified in the interface macro. To destroy an | ||
| 130 | * object crated with the plugger do not delete it, instead pass it into | ||
| 131 | * Plugger's destroy function. | ||
| 132 | * | ||
| 133 | * Any objects not destroyed when the plugger is deleted will be destroyed | ||
| 134 | * automatically. | ||
| 135 | * | ||
| 136 | * It is important to note that some systems (linux at least) partition off | ||
| 137 | * the memory allocated by objects linked in at run time into a seperate | ||
| 138 | * segment that, while it can be accessed by the main program, cannot be | ||
| 139 | * safely or reliably freed by the main program. With that in mind it is | ||
| 140 | * a good idea to free all memory allocated by a plugin object in the plugin | ||
| 141 | * object and not allow the calling program to delete it. | ||
| 142 | */ | ||
| 143 | template<class T> | ||
| 144 | class Plugger | ||
| 145 | { | ||
| 146 | public: | ||
| 147 | typedef Bu::Hash<Bu::String, PluginReg *> PluginHash; | ||
| 148 | typedef Bu::Hash<ptrdiff_t, void *> InstHash; | ||
| 149 | |||
| 150 | public: | ||
| 151 | Plugger() | ||
| 152 | { | ||
| 153 | } | ||
| 154 | |||
| 155 | virtual ~Plugger() | ||
| 156 | { | ||
| 157 | for( InstHash::iterator i = hObj.begin(); i != hObj.end(); i++ ) | ||
| 158 | { | ||
| 159 | T *pPlug = (T *)i.getKey(); | ||
| 160 | PluginReg *pReg = (PluginReg *)*i; | ||
| 161 | pReg->pInfo->destroyPlugin( pPlug ); | ||
| 162 | } | ||
| 163 | |||
| 164 | for( PluginHash::iterator i = hPlugin.begin(); | ||
| 165 | i != hPlugin.end(); i++ ) | ||
| 166 | { | ||
| 167 | if( (*i)->bBuiltin == false ) | ||
| 168 | { | ||
| 169 | #ifdef WIN32 | ||
| 170 | FreeLibrary( (*i)->dlHandle ); | ||
| 171 | #else | ||
| 172 | dlclose( (*i)->dlHandle ); | ||
| 173 | #endif | ||
| 174 | } | ||
| 175 | delete (*i); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | void registerBuiltinPlugin( PluginInfo *pInfo ) | ||
| 180 | { | ||
| 181 | PluginReg *pReg = new PluginReg; | ||
| 182 | pReg->bBuiltin = true; | ||
| 183 | pReg->pInfo = pInfo; | ||
| 184 | hPlugin.insert( pInfo->sID, pReg ); | ||
| 185 | } | ||
| 186 | |||
| 187 | void registerExternalPlugin( const Bu::String &sFName, | ||
| 188 | const Bu::String &sPluginName ) | ||
| 189 | { | ||
| 190 | PluginReg *pReg; | ||
| 191 | if( hPlugin.has( sPluginName ) ) | ||
| 192 | throw Bu::ExceptionBase("A plugin with name '%s' is already " | ||
| 193 | "loaded.", sPluginName.getStr() ); | ||
| 194 | |||
| 195 | pReg = new PluginReg; | ||
| 196 | |||
| 197 | pReg->bBuiltin = false; | ||
| 198 | #ifdef WIN32 | ||
| 199 | pReg->dlHandle = LoadLibrary( sFName.getStr() ); | ||
| 200 | if( pReg->dlHandle == NULL ) | ||
| 201 | { | ||
| 202 | throw PluginException( 1, "Error opening %s: %s", | ||
| 203 | sFName.getStr(), Bu::getLastWinError().getStr() ); | ||
| 204 | } | ||
| 205 | pReg->pInfo = (PluginInfo *)GetProcAddress( pReg->dlHandle, | ||
| 206 | sPluginName.getStr() ); | ||
| 207 | if( pReg->pInfo == NULL ) | ||
| 208 | { | ||
| 209 | throw PluginException( 2, "Error mapping %s: %s", | ||
| 210 | sFName.getStr(), Bu::getLastWinError().getStr() ); | ||
| 211 | } | ||
| 212 | #else | ||
| 213 | pReg->dlHandle = dlopen( sFName.getStr(), RTLD_NOW ); | ||
| 214 | if( pReg->dlHandle == NULL ) | ||
| 215 | { | ||
| 216 | throw PluginException( 1, "Error opening %s: %s", | ||
| 217 | sFName.getStr(), dlerror() ); | ||
| 218 | } | ||
| 219 | pReg->pInfo = (PluginInfo *)dlsym( pReg->dlHandle, | ||
| 220 | sPluginName.getStr() ); | ||
| 221 | if( pReg->pInfo == NULL ) | ||
| 222 | { | ||
| 223 | throw PluginException( 2, "Error mapping %s: %s", | ||
| 224 | sFName.getStr(), dlerror() ); | ||
| 225 | } | ||
| 226 | #endif | ||
| 227 | hPlugin.insert( pReg->pInfo->sID, pReg ); | ||
| 228 | } | ||
| 229 | |||
| 230 | T *instantiate( const Bu::String &lpName ) | ||
| 231 | { | ||
| 232 | PluginReg *pReg = (PluginReg *)hPlugin[lpName]; | ||
| 233 | if( pReg == NULL ) | ||
| 234 | return NULL; | ||
| 235 | |||
| 236 | T *p = (T *)pReg->pInfo->createPlugin(); | ||
| 237 | hObj.insert( (ptrdiff_t)p, pReg ); | ||
| 238 | //printf("pReg: %08X, pPlug: %08X\n", pReg, p ); | ||
| 239 | |||
| 240 | return p; | ||
| 241 | } | ||
| 242 | |||
| 243 | bool hasPlugin( const Bu::String &lpName ) | ||
| 244 | { | ||
| 245 | return hPlugin.has( lpName ); | ||
| 246 | } | ||
| 247 | |||
| 248 | void destroy( T *pPlug ) | ||
| 249 | { | ||
| 250 | PluginReg *pReg = (PluginReg *)hObj.get((ptrdiff_t)pPlug); | ||
| 251 | //printf("pReg: %08X, pPlug: %08X\n", pReg, pPlug ); | ||
| 252 | if( pReg == NULL ) | ||
| 253 | return; | ||
| 254 | |||
| 255 | pReg->pInfo->destroyPlugin( pPlug ); | ||
| 256 | |||
| 257 | hObj.erase( (ptrdiff_t)pPlug ); | ||
| 258 | } | ||
| 259 | |||
| 260 | void unloadAll() | ||
| 261 | { | ||
| 262 | for( PluginHash::iterator i = hPlugin.begin(); | ||
| 263 | i != hPlugin.end(); i++ ) | ||
| 264 | { | ||
| 265 | if( (*i)->bBuiltin == false ) | ||
| 266 | { | ||
| 267 | #ifdef WIN32 | ||
| 268 | FreeLibrary( (*i)->dlHandle ); | ||
| 269 | #else | ||
| 270 | dlclose( (*i)->dlHandle ); | ||
| 271 | #endif | ||
| 272 | } | ||
| 273 | delete (*i); | ||
| 274 | } | ||
| 275 | hPlugin.clear(); | ||
| 276 | } | ||
| 277 | |||
| 278 | Bu::List<Bu::String> getPluginList() | ||
| 279 | { | ||
| 280 | return hPlugin.getKeys(); | ||
| 281 | } | ||
| 282 | |||
| 283 | private: | ||
| 284 | PluginHash hPlugin; | ||
| 285 | InstHash hObj; | ||
| 286 | }; | ||
| 287 | } | ||
| 288 | |||
| 289 | #endif | ||
| diff --git a/src/stable/process.cpp b/src/stable/process.cpp new file mode 100644 index 0000000..a98936e --- /dev/null +++ b/src/stable/process.cpp | |||
| @@ -0,0 +1,441 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/process.h" | ||
| 9 | #include <sys/types.h> | ||
| 10 | #include <sys/wait.h> | ||
| 11 | #include <unistd.h> | ||
| 12 | #include <stdarg.h> | ||
| 13 | #include <signal.h> | ||
| 14 | #include <fcntl.h> | ||
| 15 | #include <errno.h> | ||
| 16 | |||
| 17 | #include <sys/select.h> | ||
| 18 | |||
| 19 | #include "bu/config.h" | ||
| 20 | |||
| 21 | Bu::Process::Process( Flags eFlags, const char *sName, char *const argv[] ) : | ||
| 22 | iStdIn( -1 ), | ||
| 23 | iStdOut( -1 ), | ||
| 24 | iStdErr( -1 ), | ||
| 25 | iPid( 0 ), | ||
| 26 | iProcStatus( 0 ), | ||
| 27 | bBlocking( true ), | ||
| 28 | bStdOutEos( true ), | ||
| 29 | bStdErrEos( true ) | ||
| 30 | { | ||
| 31 | gexec( eFlags, sName, argv ); | ||
| 32 | } | ||
| 33 | |||
| 34 | Bu::Process::Process( Flags eFlags, const char *sName, const char *argv, ...) : | ||
| 35 | iStdIn( -1 ), | ||
| 36 | iStdOut( -1 ), | ||
| 37 | iStdErr( -1 ), | ||
| 38 | iPid( 0 ), | ||
| 39 | iProcStatus( 0 ), | ||
| 40 | bBlocking( true ), | ||
| 41 | bStdOutEos( true ), | ||
| 42 | bStdErrEos( true ) | ||
| 43 | { | ||
| 44 | int iCnt = 0; | ||
| 45 | va_list ap; | ||
| 46 | va_start( ap, argv ); | ||
| 47 | for(; va_arg( ap, const char *); iCnt++ ) { } | ||
| 48 | va_end( ap ); | ||
| 49 | |||
| 50 | char const **list = new char const *[iCnt+2]; | ||
| 51 | va_start( ap, argv ); | ||
| 52 | list[0] = argv; | ||
| 53 | for( int j = 1; j <= iCnt; j++ ) | ||
| 54 | { | ||
| 55 | list[j] = va_arg( ap, const char *); | ||
| 56 | } | ||
| 57 | list[iCnt+1] = NULL; | ||
| 58 | va_end( ap ); | ||
| 59 | |||
| 60 | gexec( eFlags, sName, (char *const *)list ); | ||
| 61 | delete[] list; | ||
| 62 | } | ||
| 63 | |||
| 64 | Bu::Process::Process( Flags eFlags, const Bu::Process::Options &opt, const char *sName, char *const argv[] ) : | ||
| 65 | iStdIn( -1 ), | ||
| 66 | iStdOut( -1 ), | ||
| 67 | iStdErr( -1 ), | ||
| 68 | iPid( 0 ), | ||
| 69 | iProcStatus( 0 ), | ||
| 70 | bBlocking( true ), | ||
| 71 | bStdOutEos( true ), | ||
| 72 | bStdErrEos( true ), | ||
| 73 | opt( opt ) | ||
| 74 | { | ||
| 75 | gexec( eFlags, sName, argv ); | ||
| 76 | } | ||
| 77 | |||
| 78 | Bu::Process::Process( Flags eFlags, const Bu::Process::Options &opt, const char *sName, const char *argv, ...) : | ||
| 79 | iStdIn( -1 ), | ||
| 80 | iStdOut( -1 ), | ||
| 81 | iStdErr( -1 ), | ||
| 82 | iPid( 0 ), | ||
| 83 | iProcStatus( 0 ), | ||
| 84 | bBlocking( true ), | ||
| 85 | bStdOutEos( true ), | ||
| 86 | bStdErrEos( true ), | ||
| 87 | opt( opt ) | ||
| 88 | { | ||
| 89 | int iCnt = 0; | ||
| 90 | va_list ap; | ||
| 91 | va_start( ap, argv ); | ||
| 92 | for(; va_arg( ap, const char *); iCnt++ ) { } | ||
| 93 | va_end( ap ); | ||
| 94 | |||
| 95 | char const **list = new char const *[iCnt+2]; | ||
| 96 | va_start( ap, argv ); | ||
| 97 | list[0] = argv; | ||
| 98 | for( int j = 1; j <= iCnt; j++ ) | ||
| 99 | { | ||
| 100 | list[j] = va_arg( ap, const char *); | ||
| 101 | } | ||
| 102 | list[iCnt+1] = NULL; | ||
| 103 | va_end( ap ); | ||
| 104 | |||
| 105 | gexec( eFlags, sName, (char *const *)list ); | ||
| 106 | delete[] list; | ||
| 107 | } | ||
| 108 | |||
| 109 | Bu::Process::~Process() | ||
| 110 | { | ||
| 111 | close(); | ||
| 112 | } | ||
| 113 | |||
| 114 | void Bu::Process::wait() | ||
| 115 | { | ||
| 116 | close(); | ||
| 117 | } | ||
| 118 | |||
| 119 | void Bu::Process::gexec( Flags eFlags, const char *sName, char *const argv[] ) | ||
| 120 | { | ||
| 121 | int iaStdIn[2]; | ||
| 122 | int iaStdOut[2]; | ||
| 123 | int iaStdErr[2]; | ||
| 124 | pipe( iaStdIn ); | ||
| 125 | if( eFlags & StdOut ) | ||
| 126 | { | ||
| 127 | pipe( iaStdOut ); | ||
| 128 | iStdOut = iaStdOut[0]; | ||
| 129 | bStdOutEos = false; | ||
| 130 | } | ||
| 131 | if( eFlags & StdErr ) | ||
| 132 | { | ||
| 133 | pipe( iaStdErr ); | ||
| 134 | iStdErr = iaStdErr[0]; | ||
| 135 | bStdErrEos = false; | ||
| 136 | } | ||
| 137 | |||
| 138 | iStdIn = iaStdIn[1]; | ||
| 139 | |||
| 140 | // fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )|O_NONBLOCK ); | ||
| 141 | |||
| 142 | iPid = fork(); | ||
| 143 | if( iPid == 0 ) | ||
| 144 | { | ||
| 145 | ::close( iaStdIn[1] ); | ||
| 146 | dup2( iaStdIn[0], 0 ); | ||
| 147 | if( eFlags & StdOut ) | ||
| 148 | { | ||
| 149 | ::close( iaStdOut[0] ); | ||
| 150 | dup2( iaStdOut[1], 1 ); | ||
| 151 | } | ||
| 152 | if( eFlags & StdErr ) | ||
| 153 | { | ||
| 154 | ::close( iaStdErr[0] ); | ||
| 155 | dup2( iaStdErr[1], 2 ); | ||
| 156 | } | ||
| 157 | if( (opt.eFlags&Options::SetGid) ) | ||
| 158 | { | ||
| 159 | setgid( opt.iGid ); | ||
| 160 | } | ||
| 161 | if( (opt.eFlags&Options::SetUid) ) | ||
| 162 | { | ||
| 163 | setuid( opt.iUid ); | ||
| 164 | } | ||
| 165 | execvp( sName, argv ); | ||
| 166 | throw Bu::ExceptionBase("Hey, execvp failed!"); | ||
| 167 | } | ||
| 168 | ::close( iaStdIn[0] ); | ||
| 169 | if( eFlags & StdOut ) | ||
| 170 | ::close( iaStdOut[1] ); | ||
| 171 | if( eFlags & StdErr ) | ||
| 172 | ::close( iaStdErr[1] ); | ||
| 173 | } | ||
| 174 | |||
| 175 | void Bu::Process::close() | ||
| 176 | { | ||
| 177 | if( iPid ) | ||
| 178 | { | ||
| 179 | if( iStdIn > -1 ) | ||
| 180 | ::close( iStdIn ); | ||
| 181 | if( iStdOut > -1 ) | ||
| 182 | ::close( iStdOut ); | ||
| 183 | if( iStdErr > -1 ) | ||
| 184 | ::close( iStdErr ); | ||
| 185 | waitpid( iPid, &iProcStatus, 0 ); | ||
| 186 | iPid = 0; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | void Bu::Process::closeStdIn() | ||
| 191 | { | ||
| 192 | ::close( iStdIn ); | ||
| 193 | iStdIn = -1; | ||
| 194 | } | ||
| 195 | |||
| 196 | void Bu::Process::closeStdOut() | ||
| 197 | { | ||
| 198 | ::close( iStdOut ); | ||
| 199 | iStdOut = -1; | ||
| 200 | } | ||
| 201 | |||
| 202 | Bu::size Bu::Process::read( void *pBuf, Bu::size nBytes ) | ||
| 203 | { | ||
| 204 | if( bStdOutEos ) | ||
| 205 | return 0; | ||
| 206 | fd_set rfds; | ||
| 207 | FD_ZERO( &rfds ); | ||
| 208 | FD_SET( iStdOut, &rfds ); | ||
| 209 | struct timeval tv = {0, 0}; | ||
| 210 | if( ::bu_select( iStdOut+1, &rfds, NULL, NULL, &tv ) < 0 ) | ||
| 211 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
| 212 | if( FD_ISSET( iStdOut, &rfds ) || bBlocking ) | ||
| 213 | { | ||
| 214 | Bu::size nRead = TEMP_FAILURE_RETRY( ::read( iStdOut, pBuf, nBytes ) ); | ||
| 215 | if( nRead == 0 ) | ||
| 216 | { | ||
| 217 | bStdOutEos = true; | ||
| 218 | checkClose(); | ||
| 219 | return 0; | ||
| 220 | } | ||
| 221 | if( nRead < 0 ) | ||
| 222 | { | ||
| 223 | if( errno == EAGAIN ) | ||
| 224 | return 0; | ||
| 225 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
| 226 | } | ||
| 227 | return nRead; | ||
| 228 | } | ||
| 229 | return 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | Bu::size Bu::Process::readErr( void *pBuf, Bu::size nBytes ) | ||
| 233 | { | ||
| 234 | if( bStdErrEos ) | ||
| 235 | return 0; | ||
| 236 | fd_set rfds; | ||
| 237 | FD_ZERO( &rfds ); | ||
| 238 | FD_SET( iStdErr, &rfds ); | ||
| 239 | struct timeval tv = {0, 0}; | ||
| 240 | if( ::bu_select( iStdErr+1, &rfds, NULL, NULL, &tv ) < 0 ) | ||
| 241 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
| 242 | if( FD_ISSET( iStdErr, &rfds ) || bBlocking ) | ||
| 243 | { | ||
| 244 | Bu::size nRead = TEMP_FAILURE_RETRY( ::read( iStdErr, pBuf, nBytes ) ); | ||
| 245 | if( nRead == 0 ) | ||
| 246 | { | ||
| 247 | bStdErrEos = true; | ||
| 248 | checkClose(); | ||
| 249 | return 0; | ||
| 250 | } | ||
| 251 | if( nRead < 0 ) | ||
| 252 | { | ||
| 253 | if( errno == EAGAIN ) | ||
| 254 | return 0; | ||
| 255 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
| 256 | } | ||
| 257 | return nRead; | ||
| 258 | } | ||
| 259 | return 0; | ||
| 260 | } | ||
| 261 | |||
| 262 | Bu::size Bu::Process::write( const void *pBuf, Bu::size nBytes ) | ||
| 263 | { | ||
| 264 | return TEMP_FAILURE_RETRY( ::write( iStdIn, pBuf, nBytes ) ); | ||
| 265 | } | ||
| 266 | |||
| 267 | Bu::size Bu::Process::tell() | ||
| 268 | { | ||
| 269 | return 0; | ||
| 270 | } | ||
| 271 | |||
| 272 | void Bu::Process::seek( Bu::size ) | ||
| 273 | { | ||
| 274 | } | ||
| 275 | |||
| 276 | void Bu::Process::setPos( Bu::size ) | ||
| 277 | { | ||
| 278 | } | ||
| 279 | |||
| 280 | void Bu::Process::setPosEnd( Bu::size ) | ||
| 281 | { | ||
| 282 | } | ||
| 283 | |||
| 284 | bool Bu::Process::isEos() | ||
| 285 | { | ||
| 286 | return (iPid == 0); | ||
| 287 | } | ||
| 288 | |||
| 289 | bool Bu::Process::isOpen() | ||
| 290 | { | ||
| 291 | return (iPid != 0); | ||
| 292 | } | ||
| 293 | |||
| 294 | void Bu::Process::flush() | ||
| 295 | { | ||
| 296 | } | ||
| 297 | |||
| 298 | bool Bu::Process::canRead() | ||
| 299 | { | ||
| 300 | return true; | ||
| 301 | } | ||
| 302 | |||
| 303 | bool Bu::Process::canWrite() | ||
| 304 | { | ||
| 305 | return true; | ||
| 306 | } | ||
| 307 | |||
| 308 | bool Bu::Process::isReadable() | ||
| 309 | { | ||
| 310 | return true; | ||
| 311 | } | ||
| 312 | |||
| 313 | bool Bu::Process::isWritable() | ||
| 314 | { | ||
| 315 | return true; | ||
| 316 | } | ||
| 317 | |||
| 318 | bool Bu::Process::isSeekable() | ||
| 319 | { | ||
| 320 | return false; | ||
| 321 | } | ||
| 322 | |||
| 323 | bool Bu::Process::isBlocking() | ||
| 324 | { | ||
| 325 | return true; | ||
| 326 | } | ||
| 327 | |||
| 328 | void Bu::Process::setBlocking( bool bBlocking ) | ||
| 329 | { | ||
| 330 | if( bBlocking ) | ||
| 331 | { | ||
| 332 | if( !bStdOutEos ) | ||
| 333 | fcntl( iStdOut, F_SETFL, fcntl(iStdOut,F_GETFL,0 )&(~O_NONBLOCK) ); | ||
| 334 | if( !bStdErrEos ) | ||
| 335 | fcntl( iStdErr, F_SETFL, fcntl(iStdErr,F_GETFL,0 )&(~O_NONBLOCK) ); | ||
| 336 | } | ||
| 337 | else | ||
| 338 | { | ||
| 339 | if( !bStdOutEos ) | ||
| 340 | fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )|O_NONBLOCK ); | ||
| 341 | if( !bStdErrEos ) | ||
| 342 | fcntl( iStdErr, F_SETFL, fcntl( iStdErr, F_GETFL, 0 )|O_NONBLOCK ); | ||
| 343 | } | ||
| 344 | this->bBlocking = bBlocking; | ||
| 345 | } | ||
| 346 | |||
| 347 | void Bu::Process::setSize( Bu::size ) | ||
| 348 | { | ||
| 349 | } | ||
| 350 | |||
| 351 | Bu::size Bu::Process::getBlockSize() const | ||
| 352 | { | ||
| 353 | return 0; | ||
| 354 | } | ||
| 355 | |||
| 356 | Bu::size Bu::Process::getSize() const | ||
| 357 | { | ||
| 358 | return 0; | ||
| 359 | } | ||
| 360 | |||
| 361 | Bu::String Bu::Process::getLocation() const | ||
| 362 | { | ||
| 363 | return ""; | ||
| 364 | } | ||
| 365 | |||
| 366 | void Bu::Process::select( bool &bStdOut, bool &bStdErr ) | ||
| 367 | { | ||
| 368 | fd_set rfds; | ||
| 369 | FD_ZERO( &rfds ); | ||
| 370 | if( !bStdOutEos ) | ||
| 371 | FD_SET( iStdOut, &rfds ); | ||
| 372 | if( !bStdErrEos ) | ||
| 373 | FD_SET( iStdErr, &rfds ); | ||
| 374 | if( ::bu_select( iStdErr+1, &rfds, NULL, NULL, NULL ) < 0 ) | ||
| 375 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
| 376 | |||
| 377 | if( FD_ISSET( iStdOut, &rfds ) ) | ||
| 378 | bStdOut = true; | ||
| 379 | else | ||
| 380 | bStdOut = false; | ||
| 381 | |||
| 382 | if( FD_ISSET( iStdErr, &rfds ) ) | ||
| 383 | bStdErr = true; | ||
| 384 | else | ||
| 385 | bStdErr = false; | ||
| 386 | } | ||
| 387 | |||
| 388 | bool Bu::Process::isRunning() | ||
| 389 | { | ||
| 390 | if( waitpid( iPid, NULL, WNOHANG ) == iPid ) | ||
| 391 | checkClose(); | ||
| 392 | return iPid != 0; | ||
| 393 | } | ||
| 394 | |||
| 395 | void Bu::Process::ignoreStdErr() | ||
| 396 | { | ||
| 397 | if( iStdErr == -1 ) | ||
| 398 | return; | ||
| 399 | ::close( iStdErr ); | ||
| 400 | iStdErr = -1; | ||
| 401 | bStdErrEos = true; | ||
| 402 | } | ||
| 403 | |||
| 404 | pid_t Bu::Process::getPid() | ||
| 405 | { | ||
| 406 | return iPid; | ||
| 407 | } | ||
| 408 | |||
| 409 | bool Bu::Process::childExited() | ||
| 410 | { | ||
| 411 | return WIFEXITED( iProcStatus ); | ||
| 412 | } | ||
| 413 | |||
| 414 | int Bu::Process::childExitStatus() | ||
| 415 | { | ||
| 416 | return WEXITSTATUS( iProcStatus ); | ||
| 417 | } | ||
| 418 | |||
| 419 | bool Bu::Process::childSignaled() | ||
| 420 | { | ||
| 421 | return WIFSIGNALED( iProcStatus ); | ||
| 422 | } | ||
| 423 | |||
| 424 | int Bu::Process::childSignal() | ||
| 425 | { | ||
| 426 | return WTERMSIG( iProcStatus ); | ||
| 427 | } | ||
| 428 | |||
| 429 | bool Bu::Process::childCoreDumped() | ||
| 430 | { | ||
| 431 | return WCOREDUMP( iProcStatus ); | ||
| 432 | } | ||
| 433 | |||
| 434 | void Bu::Process::checkClose() | ||
| 435 | { | ||
| 436 | if( bStdOutEos && bStdErrEos ) | ||
| 437 | { | ||
| 438 | close(); | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| diff --git a/src/stable/process.h b/src/stable/process.h new file mode 100644 index 0000000..d6282e0 --- /dev/null +++ b/src/stable/process.h | |||
| @@ -0,0 +1,153 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_PROCESS_H | ||
| 9 | #define BU_PROCESS_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include <sys/types.h> | ||
| 13 | |||
| 14 | #include "bu/stream.h" | ||
| 15 | #include "bu/string.h" | ||
| 16 | |||
| 17 | namespace Bu | ||
| 18 | { | ||
| 19 | /** | ||
| 20 | * Runs a program and attaches streams to it's stdin, stdout, and stderr. | ||
| 21 | * Reading from a Bu::Process will read from the program's standard output, | ||
| 22 | * writing to a Bu::Process will write to the program's standard input. | ||
| 23 | */ | ||
| 24 | class Process : public Bu::Stream | ||
| 25 | { | ||
| 26 | public: | ||
| 27 | enum Flags | ||
| 28 | { | ||
| 29 | None = 0x00, | ||
| 30 | StdOut = 0x01, | ||
| 31 | StdErr = 0x02, | ||
| 32 | Both = 0x03 | ||
| 33 | }; | ||
| 34 | |||
| 35 | public: | ||
| 36 | class Options | ||
| 37 | { | ||
| 38 | public: | ||
| 39 | enum OptFlags | ||
| 40 | { | ||
| 41 | None = 0x00, | ||
| 42 | SetUid = 0x01, | ||
| 43 | SetGid = 0x02, | ||
| 44 | }; | ||
| 45 | |||
| 46 | Options() : eFlags( None ) {} | ||
| 47 | |||
| 48 | int eFlags; | ||
| 49 | int iUid; | ||
| 50 | int iGid; | ||
| 51 | }; | ||
| 52 | |||
| 53 | Process( Flags eFlags, const char *sName, char *const argv[] ); | ||
| 54 | Process( Flags eFlags, const char *sName, const char *argv, ...); | ||
| 55 | Process( Flags eFlags, const Options &opt, const char *sName, char *const argv[] ); | ||
| 56 | Process( Flags eFlags, const Options &opt, const char *sName, const char *argv, ...); | ||
| 57 | virtual ~Process(); | ||
| 58 | |||
| 59 | /** | ||
| 60 | * Waits until the process exits. This blocks the caller until the | ||
| 61 | * child process terminates. | ||
| 62 | */ | ||
| 63 | void wait(); | ||
| 64 | |||
| 65 | virtual void close(); | ||
| 66 | virtual void closeStdIn(); | ||
| 67 | virtual void closeStdOut(); | ||
| 68 | virtual Bu::size read( void *pBuf, Bu::size nBytes ); | ||
| 69 | virtual Bu::size readErr( void *pBuf, Bu::size nBytes ); | ||
| 70 | virtual Bu::size write( const void *pBuf, Bu::size nBytes ); | ||
| 71 | using Stream::write; | ||
| 72 | |||
| 73 | virtual Bu::size tell(); | ||
| 74 | virtual void seek( Bu::size offset ); | ||
| 75 | virtual void setPos( Bu::size pos ); | ||
| 76 | virtual void setPosEnd( Bu::size pos ); | ||
| 77 | virtual bool isEos(); | ||
| 78 | virtual bool isOpen(); | ||
| 79 | |||
| 80 | virtual void flush(); | ||
| 81 | |||
| 82 | virtual bool canRead(); | ||
| 83 | virtual bool canWrite(); | ||
| 84 | |||
| 85 | virtual bool isReadable(); | ||
| 86 | virtual bool isWritable(); | ||
| 87 | virtual bool isSeekable(); | ||
| 88 | |||
| 89 | virtual bool isBlocking(); | ||
| 90 | virtual void setBlocking( bool bBlocking=true ); | ||
| 91 | |||
| 92 | virtual void setSize( Bu::size iSize ); | ||
| 93 | |||
| 94 | virtual size getBlockSize() const; | ||
| 95 | virtual size getSize() const; | ||
| 96 | virtual Bu::String getLocation() const; | ||
| 97 | |||
| 98 | void select( bool &bStdOut, bool &bStdErr ); | ||
| 99 | |||
| 100 | bool isRunning(); | ||
| 101 | void ignoreStdErr(); | ||
| 102 | |||
| 103 | /** | ||
| 104 | * Returns the pid of the child process, or zero if there is no | ||
| 105 | * currently running child. Note that a read operation must be | ||
| 106 | * performed in order to discover that the child has ended. | ||
| 107 | */ | ||
| 108 | pid_t getPid(); | ||
| 109 | |||
| 110 | /** | ||
| 111 | * Returns true if the child exited normally (by calling exit or | ||
| 112 | * returning from main). | ||
| 113 | */ | ||
| 114 | bool childExited(); | ||
| 115 | |||
| 116 | /** | ||
| 117 | * Returns the 8 bit integer value returned from the child program if | ||
| 118 | * childExited returned true. | ||
| 119 | */ | ||
| 120 | int childExitStatus(); | ||
| 121 | |||
| 122 | /** | ||
| 123 | * Returns true if the child exited because of a signal. | ||
| 124 | */ | ||
| 125 | bool childSignaled(); | ||
| 126 | |||
| 127 | /** | ||
| 128 | * Returns the signal ID if the childSignaled return true. | ||
| 129 | */ | ||
| 130 | int childSignal(); | ||
| 131 | |||
| 132 | /** | ||
| 133 | * Returns true if the child left a core dump behind when it exited. | ||
| 134 | */ | ||
| 135 | bool childCoreDumped(); | ||
| 136 | |||
| 137 | private: | ||
| 138 | int iStdIn; | ||
| 139 | int iStdOut; | ||
| 140 | int iStdErr; | ||
| 141 | pid_t iPid; | ||
| 142 | int iProcStatus; | ||
| 143 | bool bBlocking; | ||
| 144 | bool bStdOutEos; | ||
| 145 | bool bStdErrEos; | ||
| 146 | |||
| 147 | void gexec( Flags eFlags, const char *sName, char *const argv[] ); | ||
| 148 | void checkClose(); | ||
| 149 | Options opt; | ||
| 150 | }; | ||
| 151 | } | ||
| 152 | |||
| 153 | #endif | ||
| diff --git a/src/stable/protocol.cpp b/src/stable/protocol.cpp new file mode 100644 index 0000000..2489d05 --- /dev/null +++ b/src/stable/protocol.cpp | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/protocol.h" | ||
| 9 | |||
| 10 | using namespace Bu; | ||
| 11 | |||
| 12 | Bu::Protocol::Protocol() | ||
| 13 | { | ||
| 14 | } | ||
| 15 | |||
| 16 | Bu::Protocol::~Protocol() | ||
| 17 | { | ||
| 18 | } | ||
| 19 | |||
| 20 | void Bu::Protocol::onNewConnection( Bu::Client * ) | ||
| 21 | { | ||
| 22 | } | ||
| 23 | |||
| 24 | void Bu::Protocol::onNewData( Bu::Client * ) | ||
| 25 | { | ||
| 26 | } | ||
| 27 | |||
| 28 | void Bu::Protocol::onMessage( Bu::Client *, const Bu::String & ) | ||
| 29 | { | ||
| 30 | } | ||
| 31 | |||
| 32 | void Bu::Protocol::onTick( Bu::Client * ) | ||
| 33 | { | ||
| 34 | } | ||
| 35 | |||
| diff --git a/src/stable/protocol.h b/src/stable/protocol.h new file mode 100644 index 0000000..0058723 --- /dev/null +++ b/src/stable/protocol.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_PROTOCOL_H | ||
| 9 | #define BU_PROTOCOL_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | |||
| 13 | #include "bu/string.h" | ||
| 14 | |||
| 15 | namespace Bu | ||
| 16 | { | ||
| 17 | class Client; | ||
| 18 | |||
| 19 | /** | ||
| 20 | *@ingroup Serving | ||
| 21 | */ | ||
| 22 | class Protocol | ||
| 23 | { | ||
| 24 | public: | ||
| 25 | Protocol(); | ||
| 26 | virtual ~Protocol(); | ||
| 27 | |||
| 28 | virtual void onNewConnection( Bu::Client *pClient ); | ||
| 29 | virtual void onNewData( Bu::Client *pClient ); | ||
| 30 | virtual void onMessage( Bu::Client *pClient, const Bu::String &sMsg ); | ||
| 31 | virtual void onTick( Bu::Client *pClient ); | ||
| 32 | |||
| 33 | private: | ||
| 34 | |||
| 35 | }; | ||
| 36 | } | ||
| 37 | |||
| 38 | #endif | ||
| diff --git a/src/stable/protocolhttp.cpp b/src/stable/protocolhttp.cpp new file mode 100644 index 0000000..eaee9d0 --- /dev/null +++ b/src/stable/protocolhttp.cpp | |||
| @@ -0,0 +1,353 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <dirent.h> | ||
| 9 | |||
| 10 | #ifndef WIN32 | ||
| 11 | #include <sys/wait.h> | ||
| 12 | #endif | ||
| 13 | |||
| 14 | #include <errno.h> | ||
| 15 | #include <stdlib.h> | ||
| 16 | #include "bu/protocolhttp.h" | ||
| 17 | #include "bu/logger.h" | ||
| 18 | |||
| 19 | #define CRLF "\x0D\x0A" | ||
| 20 | #define CR '\x0D' | ||
| 21 | #define LF '\x0A' | ||
| 22 | |||
| 23 | using namespace Bu; | ||
| 24 | |||
| 25 | Bu::ProtocolHttp::ProtocolHttp() | ||
| 26 | { | ||
| 27 | } | ||
| 28 | |||
| 29 | Bu::ProtocolHttp::~ProtocolHttp() | ||
| 30 | { | ||
| 31 | } | ||
| 32 | |||
| 33 | void Bu::ProtocolHttp::onNewConnection( Bu::Client *pClient ) | ||
| 34 | { | ||
| 35 | this->pClient = pClient; | ||
| 36 | |||
| 37 | iState = 0; | ||
| 38 | } | ||
| 39 | |||
| 40 | #define SDB( i ) (void)0 | ||
| 41 | //#define SDB( i ) printf("state %d: %d, \"%s\"\n", i, tt, sToken.getStr() ) | ||
| 42 | |||
| 43 | void Bu::ProtocolHttp::onNewData( Bu::Client *pClient ) | ||
| 44 | { | ||
| 45 | /* logHexDump( | ||
| 46 | 1, | ||
| 47 | pClient->getInput().getStr(), | ||
| 48 | pClient->getInput().getSize(), | ||
| 49 | "input" | ||
| 50 | );*/ | ||
| 51 | |||
| 52 | for(;;) | ||
| 53 | { | ||
| 54 | Bu::String sToken; | ||
| 55 | TokenType tt = getToken( sToken ); | ||
| 56 | |||
| 57 | if( tt == ttOutOfData ) | ||
| 58 | return; | ||
| 59 | |||
| 60 | switch( iState ) | ||
| 61 | { | ||
| 62 | case 0: // Start token, should be "method" (get, put, etc) | ||
| 63 | SDB( 0 ); | ||
| 64 | sMethod = sToken; | ||
| 65 | iState = 1; | ||
| 66 | break; | ||
| 67 | |||
| 68 | case 1: // The path requested | ||
| 69 | SDB( 1 ); | ||
| 70 | sPath = sToken; | ||
| 71 | iState = 2; | ||
| 72 | break; | ||
| 73 | |||
| 74 | case 2: // The protocol name and version | ||
| 75 | SDB( 2 ); | ||
| 76 | if( strncmp( sToken.getStr(), "HTTP/", 5 ) ) | ||
| 77 | { | ||
| 78 | pClient->disconnect(); | ||
| 79 | return; | ||
| 80 | } | ||
| 81 | else | ||
| 82 | { | ||
| 83 | char *s, *s2; | ||
| 84 | s = sToken.getStr()+5; | ||
| 85 | iMajor = strtol( s, &s2, 10 ); | ||
| 86 | iMinor = strtol( s2+1, NULL, 10 ); | ||
| 87 | iState = 3; | ||
| 88 | } | ||
| 89 | break; | ||
| 90 | |||
| 91 | case 3: // End of initial header, now comes mime-style blocks. | ||
| 92 | SDB( 3 ); | ||
| 93 | if( tt == ttNewline ) | ||
| 94 | { | ||
| 95 | iState = 10; | ||
| 96 | } | ||
| 97 | else if( tt == ttDoubleNewline ) | ||
| 98 | { | ||
| 99 | earlyResponse(); | ||
| 100 | } | ||
| 101 | else | ||
| 102 | { | ||
| 103 | pClient->disconnect(); | ||
| 104 | return; | ||
| 105 | } | ||
| 106 | break; | ||
| 107 | |||
| 108 | case 10: // HTTP-Message (skipped for now...) | ||
| 109 | SDB( 10 ); | ||
| 110 | if( tt == ttString ) | ||
| 111 | { | ||
| 112 | iState = 11; | ||
| 113 | } | ||
| 114 | else | ||
| 115 | { | ||
| 116 | pClient->disconnect(); | ||
| 117 | } | ||
| 118 | break; | ||
| 119 | |||
| 120 | case 11: // Should be a colon... | ||
| 121 | SDB( 11 ); | ||
| 122 | if( tt == ttSeperator && sToken == ":" ) | ||
| 123 | { | ||
| 124 | iState = 12; | ||
| 125 | } | ||
| 126 | else | ||
| 127 | { | ||
| 128 | pClient->disconnect(); | ||
| 129 | } | ||
| 130 | break; | ||
| 131 | |||
| 132 | case 12: | ||
| 133 | SDB( 12 ); | ||
| 134 | if( tt == ttNewline ) | ||
| 135 | { | ||
| 136 | iState = 10; | ||
| 137 | } | ||
| 138 | if( tt == ttDoubleNewline ) | ||
| 139 | { | ||
| 140 | earlyResponse(); | ||
| 141 | } | ||
| 142 | break; | ||
| 143 | |||
| 144 | case 20: | ||
| 145 | SDB( 20 ); | ||
| 146 | break; | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | Bu::ProtocolHttp::TokenType Bu::ProtocolHttp::getToken( Bu::String &line ) | ||
| 152 | { | ||
| 153 | char s; | ||
| 154 | int jmax = pClient->getInputSize(); | ||
| 155 | bool bNonWS = false; | ||
| 156 | |||
| 157 | for( int j = 0; j < jmax; j++ ) | ||
| 158 | { | ||
| 159 | pClient->peek( &s, 1, j ); | ||
| 160 | if( iState > 2 && isSeperator( s ) ) | ||
| 161 | { | ||
| 162 | if( j == 0 ) | ||
| 163 | { | ||
| 164 | line += s; | ||
| 165 | pClient->seek( 1 ); | ||
| 166 | return ttSeperator; | ||
| 167 | } | ||
| 168 | else | ||
| 169 | { | ||
| 170 | pClient->seek( j ); | ||
| 171 | return ttString; | ||
| 172 | } | ||
| 173 | } | ||
| 174 | else if( isWS( s ) ) | ||
| 175 | { | ||
| 176 | if( bNonWS ) | ||
| 177 | { | ||
| 178 | pClient->seek( j ); | ||
| 179 | return ttString; | ||
| 180 | } | ||
| 181 | } | ||
| 182 | else if( s == CR ) | ||
| 183 | { | ||
| 184 | if( pClient->getInputSize() < 4 ) | ||
| 185 | return ttOutOfData; | ||
| 186 | |||
| 187 | char ss[3]; | ||
| 188 | pClient->peek( ss, 3, j+1 ); | ||
| 189 | if( ss[0] == LF && ss[1] != ' ' && ss[1] != '\t' ) | ||
| 190 | { | ||
| 191 | if( bNonWS ) | ||
| 192 | { | ||
| 193 | pClient->seek( j ); | ||
| 194 | return ttString; | ||
| 195 | } | ||
| 196 | else if( ss[1] == CR && ss[2] == LF ) | ||
| 197 | { | ||
| 198 | pClient->seek( 4 ); | ||
| 199 | return ttDoubleNewline; | ||
| 200 | } | ||
| 201 | else | ||
| 202 | { | ||
| 203 | pClient->seek( 2 ); | ||
| 204 | return ttNewline; | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | j += 2; | ||
| 209 | if( bNonWS ) | ||
| 210 | { | ||
| 211 | pClient->seek( j ); | ||
| 212 | return ttString; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | else | ||
| 216 | { | ||
| 217 | line += s; | ||
| 218 | bNonWS = true; | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | return ttOutOfData; | ||
| 223 | } | ||
| 224 | |||
| 225 | bool Bu::ProtocolHttp::isWS( char buf ) | ||
| 226 | { | ||
| 227 | return (buf == ' ' || buf == '\t'); | ||
| 228 | } | ||
| 229 | |||
| 230 | bool Bu::ProtocolHttp::isSeperator( char buf ) | ||
| 231 | { | ||
| 232 | return (buf == '(' || buf == ')' || buf == '<' || buf == '>' || | ||
| 233 | buf == '@' || buf == ',' || buf == ';' || buf == ':' || | ||
| 234 | buf == '\\' || buf == '\"' || buf == '/' || buf == '[' || | ||
| 235 | buf == ']' || buf == '?' || buf == '=' || buf == '{' || | ||
| 236 | buf == '}' ); | ||
| 237 | } | ||
| 238 | |||
| 239 | void Bu::ProtocolHttp::earlyResponse() | ||
| 240 | { | ||
| 241 | if( sMethod == "GET" ) | ||
| 242 | { | ||
| 243 | onRequest( sMethod, sPath ); | ||
| 244 | iState = 0; | ||
| 245 | } | ||
| 246 | else | ||
| 247 | { | ||
| 248 | iState = 20; | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | void Bu::ProtocolHttp::lateResponse() | ||
| 253 | { | ||
| 254 | onRequest( sMethod, sPath ); | ||
| 255 | } | ||
| 256 | |||
| 257 | void Bu::ProtocolHttp::sendResponse( const Response &rRes ) | ||
| 258 | { | ||
| 259 | char buf[1024]; | ||
| 260 | int iSize = sprintf( buf, "HTTP/1.1 %d ", rRes.iCode ); | ||
| 261 | |||
| 262 | pClient->write( buf, iSize ); | ||
| 263 | pClient->write( rRes.sReason ); | ||
| 264 | pClient->write( CRLF, 2 ); | ||
| 265 | |||
| 266 | for( Response::StringHash::const_iterator i = rRes.hHeaders.begin(); | ||
| 267 | i != rRes.hHeaders.end(); i++ ) | ||
| 268 | { | ||
| 269 | pClient->write( i.getKey() ); | ||
| 270 | pClient->write(": ", 2 ); | ||
| 271 | pClient->write( i.getValue() ); | ||
| 272 | pClient->write( CRLF, 2 ); | ||
| 273 | } | ||
| 274 | |||
| 275 | iSize = sprintf( buf, "Content-Length: %ld" CRLF, rRes.sContent.getSize() ); | ||
| 276 | pClient->write( buf, iSize ); | ||
| 277 | |||
| 278 | pClient->write( CRLF, 2 ); | ||
| 279 | pClient->write( rRes.sContent ); | ||
| 280 | } | ||
| 281 | |||
| 282 | // | ||
| 283 | // Bu::ProtocolHttp::Response | ||
| 284 | // | ||
| 285 | Bu::ProtocolHttp::Response::Response( int iCode ) : | ||
| 286 | iCode( iCode ) | ||
| 287 | { | ||
| 288 | switch( iCode ) | ||
| 289 | { | ||
| 290 | case 100: sReason = "Continue"; break; | ||
| 291 | case 101: sReason = "Switching Protocols"; break; | ||
| 292 | case 200: sReason = "OK"; break; | ||
| 293 | case 201: sReason = "Created"; break; | ||
| 294 | case 202: sReason = "Accepted"; break; | ||
| 295 | case 203: sReason = "Non-Authoritative Information"; break; | ||
| 296 | case 204: sReason = "No Content"; break; | ||
| 297 | case 205: sReason = "Reset Content"; break; | ||
| 298 | case 206: sReason = "Partial Content"; break; | ||
| 299 | case 300: sReason = "Multiple Choices"; break; | ||
| 300 | case 301: sReason = "Moved Permanently"; break; | ||
| 301 | case 302: sReason = "Found"; break; | ||
| 302 | case 303: sReason = "See Other"; break; | ||
| 303 | case 304: sReason = "Not Modified"; break; | ||
| 304 | case 305: sReason = "Use Proxy"; break; | ||
| 305 | case 307: sReason = "Temporary Redirect"; break; | ||
| 306 | case 400: sReason = "Bad Request"; break; | ||
| 307 | case 401: sReason = "Unauthorized"; break; | ||
| 308 | case 402: sReason = "Payment Required"; break; | ||
| 309 | case 403: sReason = "Forbidden"; break; | ||
| 310 | case 404: sReason = "Not Found"; break; | ||
| 311 | case 405: sReason = "Method Not Allowed"; break; | ||
| 312 | case 406: sReason = "Not Acceptable"; break; | ||
| 313 | case 407: sReason = "Proxy Authentication Required"; break; | ||
| 314 | case 408: sReason = "Request Time-out"; break; | ||
| 315 | case 409: sReason = "Conflict"; break; | ||
| 316 | case 410: sReason = "Gone"; break; | ||
| 317 | case 411: sReason = "Length Required"; break; | ||
| 318 | case 412: sReason = "Precondition Failed"; break; | ||
| 319 | case 413: sReason = "Request Entity Too Large"; break; | ||
| 320 | case 414: sReason = "Request-URI Too Large"; break; | ||
| 321 | case 415: sReason = "Unsupported Media Type"; break; | ||
| 322 | case 416: sReason = "Requested range not satisfiable"; break; | ||
| 323 | case 417: sReason = "Expectation Failed"; break; | ||
| 324 | case 500: sReason = "Internal Server Error"; break; | ||
| 325 | case 501: sReason = "Not Implemented"; break; | ||
| 326 | case 502: sReason = "Bad Gateway"; break; | ||
| 327 | case 503: sReason = "Service Unavailable"; break; | ||
| 328 | case 504: sReason = "Gateway Time-out"; break; | ||
| 329 | case 505: sReason = "HTTP Version not supported"; break; | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | Bu::ProtocolHttp::Response::Response( int iCode, const Bu::String &sReason ) : | ||
| 334 | iCode( iCode ), | ||
| 335 | sReason( sReason ) | ||
| 336 | { | ||
| 337 | } | ||
| 338 | |||
| 339 | Bu::ProtocolHttp::Response::~Response() | ||
| 340 | { | ||
| 341 | } | ||
| 342 | |||
| 343 | void Bu::ProtocolHttp::Response::setHeader( | ||
| 344 | const Bu::String &sKey, const Bu::String &sVal ) | ||
| 345 | { | ||
| 346 | hHeaders.insert( sKey, sVal ); | ||
| 347 | } | ||
| 348 | |||
| 349 | void Bu::ProtocolHttp::Response::setContent( const Bu::String &sCont ) | ||
| 350 | { | ||
| 351 | sContent = sCont; | ||
| 352 | } | ||
| 353 | |||
| diff --git a/src/stable/protocolhttp.h b/src/stable/protocolhttp.h new file mode 100644 index 0000000..153a00d --- /dev/null +++ b/src/stable/protocolhttp.h | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_PROTOCOL_HTTP_H | ||
| 9 | #define BU_PROTOCOL_HTTP_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include <sys/types.h> | ||
| 13 | |||
| 14 | #include "bu/protocol.h" | ||
| 15 | #include "bu/client.h" | ||
| 16 | #include "bu/string.h" | ||
| 17 | #include "bu/hash.h" | ||
| 18 | |||
| 19 | namespace Bu | ||
| 20 | { | ||
| 21 | /** | ||
| 22 | * An HTTP Protocol handler. Yes, I know that HTTP stands for Hyper Text | ||
| 23 | * Transfer Protocol, and that the Protocol part is redundant, but in this | ||
| 24 | * case the word Protocol is refering to the Libbu++ construct Bu::Protocol, | ||
| 25 | * and not a means of encoding conversations. Anyway, this class represents | ||
| 26 | * a general HTTP server processor. Every time a request comes in it calls | ||
| 27 | * the onRequest function in a subclass with the method and URI that were | ||
| 28 | * requested. The sub-class can then do whatever it needs to to send back | ||
| 29 | * a response. | ||
| 30 | *@ingroup Serving | ||
| 31 | */ | ||
| 32 | class ProtocolHttp : public Protocol | ||
| 33 | { | ||
| 34 | public: /* Types */ | ||
| 35 | typedef Bu::List<Bu::String> TokenList; | ||
| 36 | |||
| 37 | public: /* Interface */ | ||
| 38 | ProtocolHttp(); | ||
| 39 | virtual ~ProtocolHttp(); | ||
| 40 | |||
| 41 | virtual void onNewConnection( Bu::Client *pClient ); | ||
| 42 | virtual void onNewData( Bu::Client *pClient ); | ||
| 43 | |||
| 44 | virtual void onRequest( | ||
| 45 | const Bu::String &sMethod, const Bu::String &sPath )=0; | ||
| 46 | |||
| 47 | class Response | ||
| 48 | { | ||
| 49 | friend class Bu::ProtocolHttp; | ||
| 50 | public: | ||
| 51 | Response( int iCode ); | ||
| 52 | Response( int iCode, const Bu::String &sReason ); | ||
| 53 | virtual ~Response(); | ||
| 54 | |||
| 55 | void setHeader( const Bu::String &sKey, const Bu::String &sVal ); | ||
| 56 | void setContent( const Bu::String &sCont ); | ||
| 57 | |||
| 58 | private: | ||
| 59 | int iCode; | ||
| 60 | Bu::String sReason; | ||
| 61 | typedef Bu::Hash<Bu::String,Bu::String> StringHash; | ||
| 62 | StringHash hHeaders; | ||
| 63 | Bu::String sContent; | ||
| 64 | }; | ||
| 65 | |||
| 66 | void sendResponse( const Response &rRes ); | ||
| 67 | |||
| 68 | private: | ||
| 69 | enum TokenType | ||
| 70 | { | ||
| 71 | ttOutOfData, | ||
| 72 | ttString, | ||
| 73 | ttNewline, | ||
| 74 | ttDoubleNewline, | ||
| 75 | ttSeperator | ||
| 76 | }; | ||
| 77 | /** | ||
| 78 | * Read an HTTP line, this is up to the first CRLF that isn't followed | ||
| 79 | * by a continuation character, converting it to one line as it reads. | ||
| 80 | *@param line All data read will be appended to line, even if no | ||
| 81 | * end-of-line is read. | ||
| 82 | *@returns True if an end-of-line is read and the line should be | ||
| 83 | * processed, false if the end-of-line has not been reached, and more | ||
| 84 | * data needs to be read before this operation can continue. | ||
| 85 | */ | ||
| 86 | TokenType getToken( Bu::String &line ); | ||
| 87 | bool isWS( char buf ); | ||
| 88 | bool isSeperator( char buf ); | ||
| 89 | |||
| 90 | void earlyResponse(); | ||
| 91 | void lateResponse(); | ||
| 92 | |||
| 93 | private: /* state */ | ||
| 94 | Bu::Client *pClient; | ||
| 95 | TokenList lTokens; | ||
| 96 | |||
| 97 | int iState; | ||
| 98 | |||
| 99 | Bu::String sMethod; | ||
| 100 | Bu::String sPath; | ||
| 101 | int iMajor; | ||
| 102 | int iMinor; | ||
| 103 | }; | ||
| 104 | } | ||
| 105 | |||
| 106 | #endif | ||
| diff --git a/src/stable/protocoltelnet.cpp b/src/stable/protocoltelnet.cpp new file mode 100644 index 0000000..7e37cca --- /dev/null +++ b/src/stable/protocoltelnet.cpp | |||
| @@ -0,0 +1,620 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/protocoltelnet.h" | ||
| 9 | #include "bu/client.h" | ||
| 10 | |||
| 11 | /* We apparently at least want defs for the lower 13, not sure we care about | ||
| 12 | * the rest of the chars, maybe escape. | ||
| 13 | */ | ||
| 14 | #define CH_NUL '\x00' /* NUL */ | ||
| 15 | #define CH_SOH '\x01' /* Start Of Heading */ | ||
| 16 | #define CH_STX '\x02' /* Start of Text */ | ||
| 17 | #define CH_ETX '\x03' /* End of Text */ | ||
| 18 | #define CH_EOT '\x04' /* End of transmission */ | ||
| 19 | #define CH_ENQ '\x05' /* Enquiery */ | ||
| 20 | #define CH_ACK '\x06' /* Acknowledge */ | ||
| 21 | #define CH_BEL '\x07' /* Bell */ | ||
| 22 | #define CH_BS '\x08' /* Backspace */ | ||
| 23 | #define CH_TAB '\x09' /* Horizontal Tab */ | ||
| 24 | #define CH_LF '\x0A' /* NL Line feed, new line */ | ||
| 25 | #define CH_VT '\x0B' /* Vertical Tab */ | ||
| 26 | #define CH_FF '\x0C' /* Form feed, new page */ | ||
| 27 | #define CH_CR '\x0D' /* Carriage return */ | ||
| 28 | #define CH_ESC '\x1B' /* Escape */ | ||
| 29 | #define CH_DEL '\x7F' /* Delete */ | ||
| 30 | |||
| 31 | #define CODE_SE '\xf0' /* End of subnegotiation params. */ | ||
| 32 | #define CODE_NOP '\xf1' /* No operation (keep-alive). */ | ||
| 33 | #define CODE_DM '\xf2' /* Datastream side of a Synch. */ | ||
| 34 | #define CODE_BRK '\xf3' /* Break character. */ | ||
| 35 | #define CODE_IP '\xf4' /* Interrupt Process character. */ | ||
| 36 | #define CODE_AO '\xf5' /* Abort Output character. */ | ||
| 37 | #define CODE_AYT '\xf6' /* Are You There? character. */ | ||
| 38 | #define CODE_EC '\xf7' /* Erase Character character. */ | ||
| 39 | #define CODE_EL '\xf8' /* Erase Line character. */ | ||
| 40 | #define CODE_GA '\xf9' /* Go Ahead signal. */ | ||
| 41 | #define CODE_SB '\xfa' /* Begin subnegotiation options. */ | ||
| 42 | #define CODE_WILL '\xfb' /* Desire to do something. */ | ||
| 43 | #define CODE_WONT '\xfc' /* Refuse to perform. */ | ||
| 44 | #define CODE_DO '\xfd' /* Request option. */ | ||
| 45 | #define CODE_DONT '\xfe' /* Demand a stop. */ | ||
| 46 | |||
| 47 | #define CODE_IAC '\xff' /* Interpret-As-Command. */ | ||
| 48 | |||
| 49 | #define OPT_BINARY '\x00' /* Binary mode (file transfers?). */ | ||
| 50 | #define OPT_ECHO '\x01' /* (local) Echo mode. */ | ||
| 51 | #define OPT_SUPGA '\x03' /* Suppress Go Ahead signals. */ | ||
| 52 | #define OPT_STATUS '\x05' /* Allow status messages. */ | ||
| 53 | #define OPT_TIMING '\x06' /* Place a timing mark in the code. */ | ||
| 54 | #define OPT_EXASCII '\x11' /* Extended ASCII. */ | ||
| 55 | #define OPT_LOGOUT '\x12' /* Logout. */ | ||
| 56 | #define OPT_TTYPE '\x18' /* Terminal Type. */ | ||
| 57 | #define OPT_NAWS '\x1f' /* Negotiate about window size. */ | ||
| 58 | #define OPT_TSPEED '\x20' /* Terminal Speed. */ | ||
| 59 | #define OPT_NEWENV '\x27' /* New Environment Option. */ | ||
| 60 | #define OPT_EXOPL '\xff' /* Can we, will we, handle extended options. */ | ||
| 61 | |||
| 62 | #ifndef __TELNET_DEBUG | ||
| 63 | # define printCode( a ) (void)0 | ||
| 64 | # define printOpt( a ) (void)0 | ||
| 65 | #endif | ||
| 66 | |||
| 67 | Bu::ProtocolTelnet::ProtocolTelnet() : | ||
| 68 | oBinary( *this, OPT_BINARY ), | ||
| 69 | oEcho( *this, OPT_ECHO ), | ||
| 70 | oNAWS( *this, OPT_NAWS ), | ||
| 71 | oSuppressGA(*this, OPT_SUPGA ), | ||
| 72 | bCanonical( true ), | ||
| 73 | bSubOpt( false ) | ||
| 74 | { | ||
| 75 | } | ||
| 76 | |||
| 77 | Bu::ProtocolTelnet::~ProtocolTelnet() | ||
| 78 | { | ||
| 79 | } | ||
| 80 | |||
| 81 | void Bu::ProtocolTelnet::onNewConnection( Bu::Client *pClient ) | ||
| 82 | { | ||
| 83 | this->pClient = pClient; | ||
| 84 | } | ||
| 85 | |||
| 86 | void Bu::ProtocolTelnet::onNewData( Bu::Client *pClient ) | ||
| 87 | { | ||
| 88 | char bc; | ||
| 89 | int iLeft; | ||
| 90 | while( (iLeft = pClient->getInputSize()) ) | ||
| 91 | { | ||
| 92 | if( bSubOpt ) | ||
| 93 | { | ||
| 94 | pClient->peek( &bc, 1 ); | ||
| 95 | if( bc == CODE_IAC ) | ||
| 96 | { | ||
| 97 | if( iLeft <= 1 ) return; | ||
| 98 | char bc2; | ||
| 99 | printCode( CODE_IAC ); | ||
| 100 | pClient->peek( &bc2, 1, 1 ); | ||
| 101 | printCode( bc2 ); | ||
| 102 | if( bc2 == CODE_SE ) | ||
| 103 | { | ||
| 104 | bSubOpt = false; | ||
| 105 | onSubOpt(); | ||
| 106 | } | ||
| 107 | else if( bc2 == CODE_IAC ) | ||
| 108 | { | ||
| 109 | sSubBuf += CODE_IAC; | ||
| 110 | } | ||
| 111 | else | ||
| 112 | { | ||
| 113 | // Error of some sort. | ||
| 114 | } | ||
| 115 | pClient->seek( 1 ); | ||
| 116 | } | ||
| 117 | else | ||
| 118 | { | ||
| 119 | sSubBuf += bc; | ||
| 120 | } | ||
| 121 | pClient->seek( 1 ); | ||
| 122 | } | ||
| 123 | else | ||
| 124 | { | ||
| 125 | pClient->peek( &bc, 1 ); | ||
| 126 | if( bc == CODE_IAC ) | ||
| 127 | { | ||
| 128 | if( iLeft <= 1 ) return; | ||
| 129 | char bc2; | ||
| 130 | pClient->peek( &bc2, 1, 1 ); | ||
| 131 | printCode( bc ); | ||
| 132 | printCode( bc2 ); | ||
| 133 | |||
| 134 | switch( bc2 ) | ||
| 135 | { | ||
| 136 | case CODE_WILL: | ||
| 137 | if( iLeft <= 2 ) return; | ||
| 138 | { | ||
| 139 | char bc3; | ||
| 140 | pClient->peek( &bc3, 1, 2 ); | ||
| 141 | pClient->seek( 1 ); | ||
| 142 | printOpt( bc3 ); | ||
| 143 | onWill( bc3 ); | ||
| 144 | } | ||
| 145 | break; | ||
| 146 | |||
| 147 | case CODE_WONT: | ||
| 148 | if( iLeft <= 2 ) return; | ||
| 149 | { | ||
| 150 | char bc3; | ||
| 151 | pClient->peek( &bc3, 1, 2 ); | ||
| 152 | pClient->seek( 1 ); | ||
| 153 | printOpt( bc3 ); | ||
| 154 | onWont( bc3 ); | ||
| 155 | } | ||
| 156 | break; | ||
| 157 | |||
| 158 | case CODE_DO: | ||
| 159 | if( iLeft <= 2 ) return; | ||
| 160 | { | ||
| 161 | char bc3; | ||
| 162 | pClient->peek( &bc3, 1, 2 ); | ||
| 163 | pClient->seek( 1 ); | ||
| 164 | printOpt( bc3 ); | ||
| 165 | onDo( bc3 ); | ||
| 166 | } | ||
| 167 | break; | ||
| 168 | |||
| 169 | case CODE_DONT: | ||
| 170 | if( iLeft <= 2 ) return; | ||
| 171 | { | ||
| 172 | char bc3; | ||
| 173 | pClient->peek( &bc3, 1, 2 ); | ||
| 174 | pClient->seek( 1 ); | ||
| 175 | printOpt( bc3 ); | ||
| 176 | onDont( bc3 ); | ||
| 177 | } | ||
| 178 | break; | ||
| 179 | |||
| 180 | case CODE_SB: | ||
| 181 | if( iLeft <= 2 ) return; | ||
| 182 | { | ||
| 183 | pClient->peek( &cSubOpt, 1, 2 ); | ||
| 184 | pClient->seek( 1 ); | ||
| 185 | printOpt( cSubOpt ); | ||
| 186 | bSubOpt = true; | ||
| 187 | } | ||
| 188 | break; | ||
| 189 | |||
| 190 | case CODE_IAC: | ||
| 191 | sDataBuf += CODE_IAC; | ||
| 192 | printCode( CODE_IAC ); | ||
| 193 | break; | ||
| 194 | } | ||
| 195 | pClient->seek( 1 ); | ||
| 196 | #ifdef __TELNET_DEBUG | ||
| 197 | printf("\n"); | ||
| 198 | #endif | ||
| 199 | } | ||
| 200 | else if( bc == CODE_SB ) | ||
| 201 | { | ||
| 202 | } | ||
| 203 | else | ||
| 204 | { | ||
| 205 | // This is where control code handling goes | ||
| 206 | // Also, possibly, character code conversion, although I'm not | ||
| 207 | // sure that really matters anymore, go ASCII/UTF-8 | ||
| 208 | if( bCanonical ) | ||
| 209 | { | ||
| 210 | if( bc < 0x20 || bc >= CH_DEL ) | ||
| 211 | { | ||
| 212 | if( bc == CH_CR ) | ||
| 213 | { | ||
| 214 | if( iLeft <= 1 ) return; | ||
| 215 | char bc2; | ||
| 216 | pClient->peek( &bc2, 1, 1 ); | ||
| 217 | if( bc2 == CH_NUL || bc2 == CH_LF ) | ||
| 218 | { | ||
| 219 | onCtlChar( bc ); | ||
| 220 | gotLine( sDataBuf ); | ||
| 221 | sDataBuf.clear(); | ||
| 222 | } | ||
| 223 | pClient->seek( 1 ); | ||
| 224 | } | ||
| 225 | else | ||
| 226 | { | ||
| 227 | onCtlChar( bc ); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | else | ||
| 231 | { | ||
| 232 | sDataBuf += bc; | ||
| 233 | if( oEcho.isLocalSet() ) | ||
| 234 | { | ||
| 235 | pClient->write( &bc, 1 ); | ||
| 236 | #ifdef __TELNET_DEBUG | ||
| 237 | printf("%c", bc ); | ||
| 238 | fflush( stdout ); | ||
| 239 | #endif | ||
| 240 | } | ||
| 241 | } | ||
| 242 | } | ||
| 243 | else | ||
| 244 | { | ||
| 245 | sDataBuf += bc; | ||
| 246 | if( oEcho.isLocalSet() ) | ||
| 247 | { | ||
| 248 | pClient->write( &bc, 1 ); | ||
| 249 | } | ||
| 250 | } | ||
| 251 | } | ||
| 252 | pClient->seek( 1 ); | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | // It's true, this code will not be executed if we only have half of an | ||
| 257 | // IAC code or multibyte escape sequence or something, but then again, it | ||
| 258 | // shouldn't be called then, and really, shouldn't be, it'll be called soon | ||
| 259 | // enough, when we get the rest of that code. | ||
| 260 | if( !bCanonical ) | ||
| 261 | { | ||
| 262 | gotData( sDataBuf ); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | void Bu::ProtocolTelnet::setCanonical( bool bCon ) | ||
| 267 | { | ||
| 268 | bCanonical = bCon; | ||
| 269 | } | ||
| 270 | |||
| 271 | bool Bu::ProtocolTelnet::isCanonical() | ||
| 272 | { | ||
| 273 | return bCanonical; | ||
| 274 | } | ||
| 275 | |||
| 276 | void Bu::ProtocolTelnet::write( const Bu::String &sData ) | ||
| 277 | { | ||
| 278 | write( sData.getStr(), sData.getSize() ); | ||
| 279 | } | ||
| 280 | |||
| 281 | void Bu::ProtocolTelnet::write( const char *pData, int iSize ) | ||
| 282 | { | ||
| 283 | int iLast = 0, j; | ||
| 284 | for( j = iLast; j < iSize; j++ ) | ||
| 285 | { | ||
| 286 | if( pData[j] == '\n' ) | ||
| 287 | { | ||
| 288 | if( j+1 >= iSize || | ||
| 289 | (pData[j+1] != '\r' && pData[j+1] != '\0') ) | ||
| 290 | { | ||
| 291 | pClient->write( pData+iLast, j-iLast ); | ||
| 292 | pClient->write( "\n\r", 2 ); | ||
| 293 | iLast = j+1; | ||
| 294 | } | ||
| 295 | else | ||
| 296 | { | ||
| 297 | j++; | ||
| 298 | } | ||
| 299 | } | ||
| 300 | } | ||
| 301 | if( j > iLast ) | ||
| 302 | { | ||
| 303 | pClient->write( pData+iLast, iSize-iLast ); | ||
| 304 | } | ||
| 305 | //pClient->write( pData, iSize ); | ||
| 306 | } | ||
| 307 | |||
| 308 | void Bu::ProtocolTelnet::write( char cData ) | ||
| 309 | { | ||
| 310 | write( &cData, 1 ); | ||
| 311 | } | ||
| 312 | |||
| 313 | void Bu::ProtocolTelnet::onWill( char cCode ) | ||
| 314 | { | ||
| 315 | try | ||
| 316 | { | ||
| 317 | Option *pOpt = hOpts[cCode]; | ||
| 318 | if( pOpt->isRemoteEnabled() ) | ||
| 319 | { | ||
| 320 | pOpt->fOpts |= Option::fRemoteIs; | ||
| 321 | char buf[3] = { CODE_IAC, CODE_DO, cCode }; | ||
| 322 | pClient->write( buf, 3 ); | ||
| 323 | } | ||
| 324 | else | ||
| 325 | { | ||
| 326 | char buf[3] = { CODE_IAC, CODE_DONT, cCode }; | ||
| 327 | pClient->write( buf, 3 ); | ||
| 328 | } | ||
| 329 | |||
| 330 | } | ||
| 331 | catch( Bu::HashException &e ) | ||
| 332 | { | ||
| 333 | char buf[3] = { CODE_IAC, CODE_DONT, cCode }; | ||
| 334 | pClient->write( buf, 3 ); | ||
| 335 | } | ||
| 336 | } | ||
| 337 | |||
| 338 | void Bu::ProtocolTelnet::onWont( char cCode ) | ||
| 339 | { | ||
| 340 | try | ||
| 341 | { | ||
| 342 | Option *pOpt = hOpts[cCode]; | ||
| 343 | |||
| 344 | pOpt->fOpts &= ~Option::fRemoteIs; | ||
| 345 | char buf[3] = { CODE_IAC, CODE_DONT, cCode }; | ||
| 346 | pClient->write( buf, 3 ); | ||
| 347 | } | ||
| 348 | catch( Bu::HashException &e ) | ||
| 349 | { | ||
| 350 | char buf[3] = { CODE_IAC, CODE_DONT, cCode }; | ||
| 351 | pClient->write( buf, 3 ); | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | void Bu::ProtocolTelnet::onDo( char cCode ) | ||
| 356 | { | ||
| 357 | try | ||
| 358 | { | ||
| 359 | Option *pOpt = hOpts[cCode]; | ||
| 360 | if( pOpt->isLocalEnabled() ) | ||
| 361 | { | ||
| 362 | pOpt->fOpts |= Option::fLocalIs; | ||
| 363 | char buf[3] = { CODE_IAC, CODE_WILL, cCode }; | ||
| 364 | pClient->write( buf, 3 ); | ||
| 365 | } | ||
| 366 | else | ||
| 367 | { | ||
| 368 | char buf[3] = { CODE_IAC, CODE_WONT, cCode }; | ||
| 369 | pClient->write( buf, 3 ); | ||
| 370 | } | ||
| 371 | |||
| 372 | } | ||
| 373 | catch( Bu::HashException &e ) | ||
| 374 | { | ||
| 375 | char buf[3] = { CODE_IAC, CODE_WONT, cCode }; | ||
| 376 | pClient->write( buf, 3 ); | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | void Bu::ProtocolTelnet::onDont( char cCode ) | ||
| 381 | { | ||
| 382 | try | ||
| 383 | { | ||
| 384 | Option *pOpt = hOpts[cCode]; | ||
| 385 | |||
| 386 | pOpt->fOpts &= ~Option::fLocalIs; | ||
| 387 | char buf[3] = { CODE_IAC, CODE_DONT, cCode }; | ||
| 388 | pClient->write( buf, 3 ); | ||
| 389 | } | ||
| 390 | catch( Bu::HashException &e ) | ||
| 391 | { | ||
| 392 | char buf[3] = { CODE_IAC, CODE_DONT, cCode }; | ||
| 393 | pClient->write( buf, 3 ); | ||
| 394 | } | ||
| 395 | } | ||
| 396 | |||
| 397 | void Bu::ProtocolTelnet::onSubOpt() | ||
| 398 | { | ||
| 399 | switch( cSubOpt ) | ||
| 400 | { | ||
| 401 | case OPT_NAWS: | ||
| 402 | { | ||
| 403 | uint16_t iWidth, iHeight; | ||
| 404 | ((char *)&iWidth)[1] = sSubBuf[0]; | ||
| 405 | ((char *)&iWidth)[0] = sSubBuf[1]; | ||
| 406 | ((char *)&iHeight)[1] = sSubBuf[2]; | ||
| 407 | ((char *)&iHeight)[0] = sSubBuf[3]; | ||
| 408 | onSubNAWS( iWidth, iHeight ); | ||
| 409 | } | ||
| 410 | break; | ||
| 411 | |||
| 412 | default: | ||
| 413 | onSubUnknown( cSubOpt, sSubBuf ); | ||
| 414 | break; | ||
| 415 | } | ||
| 416 | |||
| 417 | sSubBuf.clear(); | ||
| 418 | } | ||
| 419 | |||
| 420 | void Bu::ProtocolTelnet::onCtlChar( char cChr ) | ||
| 421 | { | ||
| 422 | #ifdef __TELNET_DEBUG | ||
| 423 | switch( cChr ) | ||
| 424 | { | ||
| 425 | case CH_NUL: printf("NUL "); break; | ||
| 426 | case CH_SOH: printf("SOH "); break; | ||
| 427 | case CH_STX: printf("STX "); break; | ||
| 428 | case CH_ETX: printf("ETX "); break; | ||
| 429 | case CH_EOT: printf("EOT "); break; | ||
| 430 | case CH_ENQ: printf("ENQ "); break; | ||
| 431 | case CH_ACK: printf("ACK "); break; | ||
| 432 | case CH_BEL: printf("BEL "); break; | ||
| 433 | case CH_BS: printf("BS "); break; | ||
| 434 | case CH_TAB: printf("TAB "); break; | ||
| 435 | case CH_LF: printf("LF "); break; | ||
| 436 | case CH_VT: printf("VT "); break; | ||
| 437 | case CH_FF: printf("FF "); break; | ||
| 438 | case CH_CR: printf("CR "); break; | ||
| 439 | case CH_ESC: printf("ESC "); break; | ||
| 440 | case CH_DEL: printf("DEL "); break; | ||
| 441 | default: printf("!![%02x] ", cChr ); break; | ||
| 442 | } | ||
| 443 | fflush( stdout ); | ||
| 444 | #endif | ||
| 445 | |||
| 446 | switch( cChr ) | ||
| 447 | { | ||
| 448 | case CH_DEL: | ||
| 449 | { | ||
| 450 | if( sDataBuf.getSize() > 0 ) | ||
| 451 | { | ||
| 452 | sDataBuf.resize( sDataBuf.getSize()-1 ); | ||
| 453 | char buf[3] = { CH_BS, ' ', CH_BS }; | ||
| 454 | pClient->write( buf, 3 ); | ||
| 455 | } | ||
| 456 | } | ||
| 457 | break; | ||
| 458 | |||
| 459 | } | ||
| 460 | } | ||
| 461 | |||
| 462 | #ifdef __TELNET_DEBUG | ||
| 463 | void Bu::ProtocolTelnet::printCode( char cCode ) | ||
| 464 | { | ||
| 465 | switch( cCode ) | ||
| 466 | { | ||
| 467 | case CODE_SE: printf("SE "); break; | ||
| 468 | case CODE_NOP: printf("NOP "); break; | ||
| 469 | case CODE_DM: printf("DM "); break; | ||
| 470 | case CODE_BRK: printf("BRK "); break; | ||
| 471 | case CODE_IP: printf("IP "); break; | ||
| 472 | case CODE_AO: printf("AO "); break; | ||
| 473 | case CODE_AYT: printf("AYT "); break; | ||
| 474 | case CODE_EC: printf("EC "); break; | ||
| 475 | case CODE_EL: printf("EL "); break; | ||
| 476 | case CODE_GA: printf("GA "); break; | ||
| 477 | case CODE_SB: printf("SB "); break; | ||
| 478 | case CODE_WILL: printf("WILL "); break; | ||
| 479 | case CODE_WONT: printf("WONT "); break; | ||
| 480 | case CODE_DO: printf("DO "); break; | ||
| 481 | case CODE_DONT: printf("DONT "); break; | ||
| 482 | case CODE_IAC: printf("IAC "); break; | ||
| 483 | default: printf("??%02x ", cCode ); break; | ||
| 484 | } | ||
| 485 | fflush( stdout ); | ||
| 486 | } | ||
| 487 | |||
| 488 | void Bu::ProtocolTelnet::printOpt( char cOpt ) | ||
| 489 | { | ||
| 490 | switch( cOpt ) | ||
| 491 | { | ||
| 492 | case OPT_BINARY: printf("BINARY "); break; | ||
| 493 | case OPT_ECHO: printf("ECHO "); break; | ||
| 494 | case OPT_SUPGA: printf("SUPGA "); break; | ||
| 495 | case OPT_STATUS: printf("STATUS "); break; | ||
| 496 | case OPT_TIMING: printf("TIMING "); break; | ||
| 497 | case OPT_EXASCII: printf("EXASCII "); break; | ||
| 498 | case OPT_LOGOUT: printf("LOGOUT "); break; | ||
| 499 | case OPT_TTYPE: printf("TTYPE "); break; | ||
| 500 | case OPT_NAWS: printf("NAWS "); break; | ||
| 501 | case OPT_TSPEED: printf("TSPEED "); break; | ||
| 502 | case OPT_NEWENV: printf("NEWENV "); break; | ||
| 503 | case OPT_EXOPL: printf("EXOPL "); break; | ||
| 504 | default: printf("??%02x ", cOpt); break; | ||
| 505 | } | ||
| 506 | fflush( stdout ); | ||
| 507 | } | ||
| 508 | #endif | ||
| 509 | |||
| 510 | Bu::ProtocolTelnet::Option::Option( Bu::ProtocolTelnet &rPT, char cCode ) : | ||
| 511 | rPT( rPT ), | ||
| 512 | fOpts( 0 ), | ||
| 513 | cCode( cCode ) | ||
| 514 | { | ||
| 515 | rPT.hOpts.insert( cCode, this ); | ||
| 516 | } | ||
| 517 | |||
| 518 | Bu::ProtocolTelnet::Option::~Option() | ||
| 519 | { | ||
| 520 | } | ||
| 521 | |||
| 522 | void Bu::ProtocolTelnet::Option::localEnable( bool bSet ) | ||
| 523 | { | ||
| 524 | if( bSet == (bool)(!(fOpts&fLocalCant)) ) return; | ||
| 525 | |||
| 526 | if( bSet ) | ||
| 527 | fOpts &= ~fLocalCant; | ||
| 528 | else | ||
| 529 | fOpts |= fLocalCant; | ||
| 530 | } | ||
| 531 | |||
| 532 | void Bu::ProtocolTelnet::Option::localSet( bool bSet ) | ||
| 533 | { | ||
| 534 | if( bSet == (bool)(fOpts&fLocalIs) ) return; | ||
| 535 | |||
| 536 | char buf[3] = { CODE_IAC, 0, cCode }; | ||
| 537 | |||
| 538 | if( bSet ) | ||
| 539 | { | ||
| 540 | buf[1] = CODE_WILL; | ||
| 541 | rPT.pClient->write( buf, 3 ); | ||
| 542 | #ifdef __TELNET_DEBUG | ||
| 543 | printf("<= "); | ||
| 544 | rPT.printCode( buf[0] ); | ||
| 545 | rPT.printCode( buf[1] ); | ||
| 546 | rPT.printOpt( buf[2] ); | ||
| 547 | printf("\n"); | ||
| 548 | #endif | ||
| 549 | } | ||
| 550 | else | ||
| 551 | { | ||
| 552 | buf[1] = CODE_WONT; | ||
| 553 | rPT.pClient->write( buf, 3 ); | ||
| 554 | #ifdef __TELNET_DEBUG | ||
| 555 | printf("<= "); | ||
| 556 | rPT.printCode( buf[0] ); | ||
| 557 | rPT.printCode( buf[1] ); | ||
| 558 | rPT.printOpt( buf[2] ); | ||
| 559 | printf("\n"); | ||
| 560 | #endif | ||
| 561 | } | ||
| 562 | } | ||
| 563 | |||
| 564 | bool Bu::ProtocolTelnet::Option::isLocalEnabled() | ||
| 565 | { | ||
| 566 | return (bool)(!(fOpts&fLocalCant)); | ||
| 567 | } | ||
| 568 | |||
| 569 | bool Bu::ProtocolTelnet::Option::isLocalSet() | ||
| 570 | { | ||
| 571 | return (bool)(fOpts&fLocalIs); | ||
| 572 | } | ||
| 573 | |||
| 574 | void Bu::ProtocolTelnet::Option::remoteEnable( bool /*bSet*/ ) | ||
| 575 | { | ||
| 576 | return; | ||
| 577 | } | ||
| 578 | |||
| 579 | void Bu::ProtocolTelnet::Option::remoteSet( bool bSet ) | ||
| 580 | { | ||
| 581 | //if( bSet == (bool)(fOpts&fRemoteIs) ) return; | ||
| 582 | |||
| 583 | char buf[3] = { CODE_IAC, 0, cCode }; | ||
| 584 | |||
| 585 | if( bSet ) | ||
| 586 | { | ||
| 587 | buf[1] = CODE_DO; | ||
| 588 | rPT.pClient->write( buf, 3 ); | ||
| 589 | #ifdef __TELNET_DEBUG | ||
| 590 | printf("<= "); | ||
| 591 | rPT.printCode( buf[0] ); | ||
| 592 | rPT.printCode( buf[1] ); | ||
| 593 | rPT.printOpt( buf[2] ); | ||
| 594 | printf("\n"); | ||
| 595 | #endif | ||
| 596 | } | ||
| 597 | else | ||
| 598 | { | ||
| 599 | buf[1] = CODE_DONT; | ||
| 600 | rPT.pClient->write( buf, 3 ); | ||
| 601 | #ifdef __TELNET_DEBUG | ||
| 602 | printf("<= "); | ||
| 603 | rPT.printCode( buf[0] ); | ||
| 604 | rPT.printCode( buf[1] ); | ||
| 605 | rPT.printOpt( buf[2] ); | ||
| 606 | printf("\n"); | ||
| 607 | #endif | ||
| 608 | } | ||
| 609 | } | ||
| 610 | |||
| 611 | bool Bu::ProtocolTelnet::Option::isRemoteEnabled() | ||
| 612 | { | ||
| 613 | return (bool)(!(fOpts&fRemoteCant)); | ||
| 614 | } | ||
| 615 | |||
| 616 | bool Bu::ProtocolTelnet::Option::isRemoteSet() | ||
| 617 | { | ||
| 618 | return (bool)(fOpts&fRemoteIs); | ||
| 619 | } | ||
| 620 | |||
| diff --git a/src/stable/protocoltelnet.h b/src/stable/protocoltelnet.h new file mode 100644 index 0000000..74d6478 --- /dev/null +++ b/src/stable/protocoltelnet.h | |||
| @@ -0,0 +1,220 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_PROTOCOL_TELNET_H | ||
| 9 | #define BU_PROTOCOL_TELNET_H | ||
| 10 | |||
| 11 | #include "bu/protocol.h" | ||
| 12 | #include "bu/hash.h" | ||
| 13 | #include "bu/string.h" | ||
| 14 | |||
| 15 | // #define __TELNET_DEBUG | ||
| 16 | |||
| 17 | namespace Bu | ||
| 18 | { | ||
| 19 | /** | ||
| 20 | * Telnet Protocol handler. This attempts to provide useful and general | ||
| 21 | * support for most of the most commonly used Telnet extensions in a simple | ||
| 22 | * and easy to use way. The Option variables control the settings that can | ||
| 23 | * be used on the line, and control which virtual "callbacks" will be called | ||
| 24 | * when different events happen. | ||
| 25 | * | ||
| 26 | * To setup initial values and to disable any options you wish override the | ||
| 27 | * onNewConnection function in your own class, like this: | ||
| 28 | *@code | ||
| 29 | class MyTelnet : public Bu::ProtocolTelnet | ||
| 30 | { | ||
| 31 | public: | ||
| 32 | ... | ||
| 33 | |||
| 34 | virtual void onNewConnection( class Bu::Client *pClient ) | ||
| 35 | { | ||
| 36 | // Call the parent class' onNewConnection to get everything all | ||
| 37 | // set up. | ||
| 38 | Bu::ProtocolTelnet::onNewConnection( pClient ); | ||
| 39 | |||
| 40 | // These functions disable the option to send files via telnet, | ||
| 41 | // disabling the remote option means that we won't accept this | ||
| 42 | // option (binary data being sent to us) from the client. | ||
| 43 | // | ||
| 44 | // Disabling the local option means that the client cannot ask us | ||
| 45 | // to send them binary data. | ||
| 46 | oBinary.enableRemote( false ); | ||
| 47 | oBinary.enableLocal( false ); | ||
| 48 | |||
| 49 | // This requests that the client send us window size updates | ||
| 50 | // whenever the size of their window changes, and an initial set to | ||
| 51 | // boot. | ||
| 52 | // | ||
| 53 | // To see if this option is set later, try oNAWS.isRemoteSet(), but | ||
| 54 | // wait a little while, asking immediatly will always return false, | ||
| 55 | // since the remote side has yet to receive our request. | ||
| 56 | oNAWS.remoteSet(); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | @endcode | ||
| 60 | * | ||
| 61 | *@ingroup Serving | ||
| 62 | */ | ||
| 63 | class ProtocolTelnet : public Protocol | ||
| 64 | { | ||
| 65 | public: | ||
| 66 | ProtocolTelnet(); | ||
| 67 | virtual ~ProtocolTelnet(); | ||
| 68 | |||
| 69 | /** | ||
| 70 | * If you override this function in a child class, make sure to call | ||
| 71 | * this version of it as the very first thing that you do, before you | ||
| 72 | * set any options. See the example in the class docs. | ||
| 73 | */ | ||
| 74 | virtual void onNewConnection( class Bu::Client *pClient ); | ||
| 75 | |||
| 76 | /** | ||
| 77 | * You should never override this function unless you really, really | ||
| 78 | * know what you're doing. If you want to get data after each line | ||
| 79 | * entered (in canonical mode) or after any data arrives (non canonical | ||
| 80 | * mode) then override the gotLine and gotData functions, respectively. | ||
| 81 | */ | ||
| 82 | virtual void onNewData( class Bu::Client *pClient ); | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Override this function to be notified of lines being submitted by | ||
| 86 | * the client. This function is only called in canonical mode, after | ||
| 87 | * all edits are performed on the data. In this mode weather you use | ||
| 88 | * the line or not, the data will be cleared from the buffer when this | ||
| 89 | * function returns, any changes made to the buffer will be destroyed. | ||
| 90 | */ | ||
| 91 | virtual void gotLine( Bu::String & /*sLine*/ ){}; | ||
| 92 | |||
| 93 | /** | ||
| 94 | * Override this function to be notified of any new data that comes in | ||
| 95 | * from the client. This function is only called in non-canonical mode, | ||
| 96 | * and includes all raw data minus telnet control codes and ansi | ||
| 97 | * escape sequences. In this mode control of the buffer is up to the | ||
| 98 | * child class in this function, the buffer will never be cleared unless | ||
| 99 | * it happens in this function's override. | ||
| 100 | */ | ||
| 101 | virtual void gotData( Bu::String & /*sData*/ ){}; | ||
| 102 | |||
| 103 | /** | ||
| 104 | * Using this function to enable or disable canonical mode only affects | ||
| 105 | * the way the data is processed and which virtual functions are called | ||
| 106 | * during processing. It does not affect options set locally or | ||
| 107 | * remotely. Setting this to false will enable char-at-a-time mode, | ||
| 108 | * effectively disabling internal line-editing code. Characters | ||
| 109 | * such as backspace that are detected will not be handled and will be | ||
| 110 | * sent to the user override. The subclass will also be notified every | ||
| 111 | * time new data is available, not just whole lines. | ||
| 112 | * | ||
| 113 | * When set to true (the default), line editing control codes will be | ||
| 114 | * interpreted and used, and the subclass will only be notified when | ||
| 115 | * complete lines are available in the buffer. | ||
| 116 | */ | ||
| 117 | void setCanonical( bool bCon=true ); | ||
| 118 | bool isCanonical(); | ||
| 119 | |||
| 120 | void write( const Bu::String &sData ); | ||
| 121 | void write( const char *pData, int iSize ); | ||
| 122 | void write( char cData ); | ||
| 123 | |||
| 124 | const Bu::String &getBuffer() { return sDataBuf; } | ||
| 125 | |||
| 126 | public: | ||
| 127 | /** | ||
| 128 | * If you wish to know the current dimensions of the client window, | ||
| 129 | * override this function, it will be called whenever the size changes. | ||
| 130 | */ | ||
| 131 | virtual void onSubNAWS( uint16_t /*iWidth*/, uint16_t /*iHeight*/ ){}; | ||
| 132 | |||
| 133 | /** | ||
| 134 | * This function is called whenever an unknown sub negotiation option is | ||
| 135 | * sent over the line. This doesn't mean that it's malformatted, it | ||
| 136 | * just means that this class doesn't support that option yet, but you | ||
| 137 | * can handle it yourself if you'd like. Feel free to change the | ||
| 138 | * sSubBuf, it will be cleared as soon as this function returns anyway. | ||
| 139 | */ | ||
| 140 | virtual void onSubUnknown( char /*cSubOpt*/, | ||
| 141 | Bu::String & /*sSubBuf*/ ){}; | ||
| 142 | |||
| 143 | private: | ||
| 144 | /** | ||
| 145 | * Represents a basic telnet option, either on or off, no parameters. | ||
| 146 | * Each Option can negotiate effectively on it's own, and has two | ||
| 147 | * parameters in each of two classes. Both local and remote can be | ||
| 148 | * enabled/disabled and set/unset. Enabled represents the ability to | ||
| 149 | * set the option, disabling an option should also unset it. Set or | ||
| 150 | * unset represent wether the option is being used, if it is allowed. | ||
| 151 | */ | ||
| 152 | class Option | ||
| 153 | { | ||
| 154 | friend class Bu::ProtocolTelnet; | ||
| 155 | private: | ||
| 156 | Option( ProtocolTelnet &rPT, char cCode ); | ||
| 157 | virtual ~Option(); | ||
| 158 | |||
| 159 | public: | ||
| 160 | void localEnable( bool bSet=true ); | ||
| 161 | void localSet( bool bSet=true ); | ||
| 162 | |||
| 163 | bool isLocalEnabled(); | ||
| 164 | bool isLocalSet(); | ||
| 165 | |||
| 166 | void remoteEnable( bool bSet=true ); | ||
| 167 | void remoteSet( bool bSet=true ); | ||
| 168 | |||
| 169 | bool isRemoteEnabled(); | ||
| 170 | bool isRemoteSet(); | ||
| 171 | |||
| 172 | private: | ||
| 173 | enum | ||
| 174 | { | ||
| 175 | fLocalCant = 0x01, /**< Local can't/won't allow option. */ | ||
| 176 | fLocalIs = 0x02, /**< Local is using option. */ | ||
| 177 | fRemoteCant = 0x04, /**< Remote can't/won't allow option. */ | ||
| 178 | fRemoteIs = 0x08 /**< Remote is using option. */ | ||
| 179 | }; | ||
| 180 | |||
| 181 | ProtocolTelnet &rPT; | ||
| 182 | char fOpts; | ||
| 183 | char cCode; | ||
| 184 | }; | ||
| 185 | friend class Bu::ProtocolTelnet::Option; | ||
| 186 | |||
| 187 | Hash<char, Option *> hOpts; | ||
| 188 | |||
| 189 | public: | ||
| 190 | Option oBinary; | ||
| 191 | Option oEcho; | ||
| 192 | Option oNAWS; | ||
| 193 | Option oSuppressGA; | ||
| 194 | |||
| 195 | private: | ||
| 196 | void onWill( char cCode ); | ||
| 197 | void onWont( char cCode ); | ||
| 198 | void onDo( char cCode ); | ||
| 199 | void onDont( char cCode ); | ||
| 200 | void onSubOpt(); | ||
| 201 | void onCtlChar( char cChr ); | ||
| 202 | |||
| 203 | #ifdef __TELNET_DEBUG | ||
| 204 | void printCode( char cCode ); | ||
| 205 | void printOpt( char cOpt ); | ||
| 206 | #endif | ||
| 207 | |||
| 208 | private: | ||
| 209 | Client *pClient; | ||
| 210 | |||
| 211 | Bu::String sDataBuf; /**< Buffer for regular line data. */ | ||
| 212 | Bu::String sSubBuf; /**< Buffer for subnegotiation data. */ | ||
| 213 | char cSubOpt; /**< Which suboption are we processing. */ | ||
| 214 | |||
| 215 | bool bCanonical; /**< Are we canonicalizing incoming data? */ | ||
| 216 | bool bSubOpt; /**< Are we processing a suboption right now? */ | ||
| 217 | }; | ||
| 218 | } | ||
| 219 | |||
| 220 | #endif | ||
| diff --git a/src/stable/queue.cpp b/src/stable/queue.cpp new file mode 100644 index 0000000..9d6edac --- /dev/null +++ b/src/stable/queue.cpp | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/queue.h" | ||
| diff --git a/src/stable/queue.h b/src/stable/queue.h new file mode 100644 index 0000000..e5d9b5f --- /dev/null +++ b/src/stable/queue.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_QUEUE_H | ||
| 9 | #define BU_QUEUE_H | ||
| 10 | |||
| 11 | namespace Bu | ||
| 12 | { | ||
| 13 | /** | ||
| 14 | * Queue abstract baseclass | ||
| 15 | */ | ||
| 16 | template<typename value> | ||
| 17 | class Queue | ||
| 18 | { | ||
| 19 | public: | ||
| 20 | Queue() | ||
| 21 | { | ||
| 22 | } | ||
| 23 | |||
| 24 | virtual ~Queue() | ||
| 25 | { | ||
| 26 | } | ||
| 27 | |||
| 28 | virtual void enqueue( const value &i )=0; | ||
| 29 | virtual value dequeue()=0; | ||
| 30 | virtual value &peek()=0; | ||
| 31 | virtual const value &peek() const=0; | ||
| 32 | virtual bool isEmpty() const=0; | ||
| 33 | virtual int getSize() const=0; | ||
| 34 | |||
| 35 | private: | ||
| 36 | |||
| 37 | }; | ||
| 38 | } | ||
| 39 | |||
| 40 | #endif | ||
| diff --git a/src/stable/queuebuf.cpp b/src/stable/queuebuf.cpp new file mode 100644 index 0000000..98d8ee0 --- /dev/null +++ b/src/stable/queuebuf.cpp | |||
| @@ -0,0 +1,278 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/queuebuf.h" | ||
| 9 | |||
| 10 | #include "bu/sio.h" | ||
| 11 | using Bu::sio; | ||
| 12 | |||
| 13 | Bu::QueueBuf::QueueBuf( int iBlockSize /*=256*/ ) : | ||
| 14 | iBlockSize( iBlockSize ), | ||
| 15 | iReadOffset( 0 ), | ||
| 16 | iWriteOffset( 0 ), | ||
| 17 | iTotalSize( 0 ) | ||
| 18 | { | ||
| 19 | } | ||
| 20 | |||
| 21 | Bu::QueueBuf::~QueueBuf() | ||
| 22 | { | ||
| 23 | for( BlockList::iterator i = lBlocks.begin(); i; i++ ) | ||
| 24 | delete[] *i; | ||
| 25 | } | ||
| 26 | |||
| 27 | void Bu::QueueBuf::close() | ||
| 28 | { | ||
| 29 | for( BlockList::iterator i = lBlocks.begin(); i; i++ ) | ||
| 30 | delete[] *i; | ||
| 31 | lBlocks.clear(); | ||
| 32 | iReadOffset = iWriteOffset = iTotalSize = 0; | ||
| 33 | } | ||
| 34 | |||
| 35 | Bu::size Bu::QueueBuf::read( void *pRawBuf, Bu::size nBytes ) | ||
| 36 | { | ||
| 37 | if( nBytes <= 0 ) | ||
| 38 | return 0; | ||
| 39 | |||
| 40 | if( lBlocks.isEmpty() ) | ||
| 41 | return 0; | ||
| 42 | |||
| 43 | Bu::size iLeft = nBytes; | ||
| 44 | char *pBuf = (char *)pRawBuf; | ||
| 45 | |||
| 46 | while( iLeft > 0 && iTotalSize > 0 ) | ||
| 47 | { | ||
| 48 | if( iReadOffset == iBlockSize ) | ||
| 49 | { | ||
| 50 | removeBlock(); | ||
| 51 | if( lBlocks.isEmpty() ) | ||
| 52 | { | ||
| 53 | return nBytes-iLeft; | ||
| 54 | } | ||
| 55 | iReadOffset = 0; | ||
| 56 | } | ||
| 57 | char *pBlock = lBlocks.first(); | ||
| 58 | Bu::size iCopy = iBlockSize-iReadOffset; | ||
| 59 | if( iLeft < iCopy ) | ||
| 60 | iCopy = iLeft; | ||
| 61 | if( iTotalSize < iCopy ) | ||
| 62 | iCopy = iTotalSize; | ||
| 63 | memcpy( pBuf, pBlock+iReadOffset, iCopy ); | ||
| 64 | iReadOffset += iCopy; | ||
| 65 | iLeft -= iCopy; | ||
| 66 | pBuf += iCopy; | ||
| 67 | iTotalSize -= iCopy; | ||
| 68 | // sio << "Read " << iCopy << " bytes, new size: " << iTotalSize << sio.nl; | ||
| 69 | } | ||
| 70 | |||
| 71 | return nBytes - iLeft; | ||
| 72 | } | ||
| 73 | |||
| 74 | Bu::size Bu::QueueBuf::peek( void *pBuf, Bu::size nBytes ) | ||
| 75 | { | ||
| 76 | return peek( pBuf, nBytes, 0 ); | ||
| 77 | } | ||
| 78 | |||
| 79 | Bu::size Bu::QueueBuf::peek( void *pRawBuf, Bu::size nBytes, Bu::size nSkip ) | ||
| 80 | { | ||
| 81 | if( nBytes <= 0 ) | ||
| 82 | return 0; | ||
| 83 | |||
| 84 | if( lBlocks.isEmpty() ) | ||
| 85 | return 0; | ||
| 86 | |||
| 87 | Bu::size iLeft = nBytes; | ||
| 88 | char *pBuf = (char *)pRawBuf; | ||
| 89 | |||
| 90 | int iTmpReadOffset = iReadOffset + nSkip; | ||
| 91 | Bu::size iTmpRemSize = iTotalSize; | ||
| 92 | BlockList::iterator iBlock = lBlocks.begin(); | ||
| 93 | while( iTmpReadOffset > iBlockSize ) | ||
| 94 | { | ||
| 95 | iTmpReadOffset -= iBlockSize; | ||
| 96 | iBlock++; | ||
| 97 | } | ||
| 98 | while( iLeft > 0 && iTmpRemSize > 0 ) | ||
| 99 | { | ||
| 100 | if( iTmpReadOffset == iBlockSize ) | ||
| 101 | { | ||
| 102 | iBlock++; | ||
| 103 | if( iBlock == lBlocks.end() ) | ||
| 104 | { | ||
| 105 | return nBytes-iLeft; | ||
| 106 | } | ||
| 107 | iTmpReadOffset = 0; | ||
| 108 | } | ||
| 109 | char *pBlock = *iBlock; | ||
| 110 | Bu::size iCopy = iBlockSize-iTmpReadOffset; | ||
| 111 | if( iLeft < iCopy ) | ||
| 112 | iCopy = iLeft; | ||
| 113 | if( iTmpRemSize < iCopy ) | ||
| 114 | iCopy = iTmpRemSize; | ||
| 115 | memcpy( pBuf, pBlock+iTmpReadOffset, iCopy ); | ||
| 116 | iTmpReadOffset += iCopy; | ||
| 117 | iLeft -= iCopy; | ||
| 118 | pBuf += iCopy; | ||
| 119 | iTmpRemSize -= iCopy; | ||
| 120 | // sio << "Read (peek) " << iCopy << " bytes, new temp size: " | ||
| 121 | // << iTmpRemSize << sio.nl; | ||
| 122 | } | ||
| 123 | |||
| 124 | return nBytes - iLeft; | ||
| 125 | } | ||
| 126 | |||
| 127 | Bu::size Bu::QueueBuf::write( const void *pRawBuf, Bu::size nBytes ) | ||
| 128 | { | ||
| 129 | if( nBytes <= 0 ) | ||
| 130 | return 0; | ||
| 131 | |||
| 132 | if( lBlocks.isEmpty() ) | ||
| 133 | { | ||
| 134 | addBlock(); | ||
| 135 | iWriteOffset = 0; | ||
| 136 | } | ||
| 137 | Bu::size iLeft = nBytes; | ||
| 138 | const char *pBuf = (const char *)pRawBuf; | ||
| 139 | |||
| 140 | while( iLeft > 0 ) | ||
| 141 | { | ||
| 142 | if( iWriteOffset == iBlockSize ) | ||
| 143 | { | ||
| 144 | addBlock(); | ||
| 145 | iWriteOffset = 0; | ||
| 146 | } | ||
| 147 | char *pBlock = lBlocks.last(); | ||
| 148 | Bu::size iCopy = iBlockSize-iWriteOffset; | ||
| 149 | if( iLeft < iCopy ) | ||
| 150 | iCopy = iLeft; | ||
| 151 | memcpy( pBlock+iWriteOffset, pBuf, iCopy ); | ||
| 152 | iWriteOffset += iCopy; | ||
| 153 | iLeft -= iCopy; | ||
| 154 | pBuf += iCopy; | ||
| 155 | iTotalSize += iCopy; | ||
| 156 | // sio << "Wrote " << iCopy << " bytes, new size: " << iTotalSize | ||
| 157 | // << sio.nl; | ||
| 158 | } | ||
| 159 | |||
| 160 | return nBytes; | ||
| 161 | } | ||
| 162 | |||
| 163 | Bu::size Bu::QueueBuf::tell() | ||
| 164 | { | ||
| 165 | return -1; | ||
| 166 | } | ||
| 167 | |||
| 168 | void Bu::QueueBuf::seek( Bu::size iAmnt ) | ||
| 169 | { | ||
| 170 | if( iAmnt <= 0 ) | ||
| 171 | return; | ||
| 172 | |||
| 173 | if( (Bu::size)iAmnt >= iTotalSize ) | ||
| 174 | { | ||
| 175 | // sio << "seek: clear all data (" << iAmnt << ">=" << iTotalSize | ||
| 176 | // << ")." << sio.nl; | ||
| 177 | close(); | ||
| 178 | return; | ||
| 179 | } | ||
| 180 | |||
| 181 | iReadOffset += iAmnt; | ||
| 182 | iTotalSize -= iAmnt; | ||
| 183 | while( iReadOffset >= iBlockSize ) | ||
| 184 | { | ||
| 185 | removeBlock(); | ||
| 186 | iReadOffset -= iBlockSize; | ||
| 187 | // sio << "seek: removal step (" << iReadOffset << ")" << sio.nl; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | void Bu::QueueBuf::setPos( Bu::size ) | ||
| 192 | { | ||
| 193 | } | ||
| 194 | |||
| 195 | void Bu::QueueBuf::setPosEnd( Bu::size ) | ||
| 196 | { | ||
| 197 | } | ||
| 198 | |||
| 199 | bool Bu::QueueBuf::isEos() | ||
| 200 | { | ||
| 201 | return iTotalSize == 0; | ||
| 202 | } | ||
| 203 | |||
| 204 | bool Bu::QueueBuf::isOpen() | ||
| 205 | { | ||
| 206 | return true; | ||
| 207 | } | ||
| 208 | |||
| 209 | void Bu::QueueBuf::flush() | ||
| 210 | { | ||
| 211 | } | ||
| 212 | |||
| 213 | bool Bu::QueueBuf::canRead() | ||
| 214 | { | ||
| 215 | return iTotalSize > 0; | ||
| 216 | } | ||
| 217 | |||
| 218 | bool Bu::QueueBuf::canWrite() | ||
| 219 | { | ||
| 220 | return true; | ||
| 221 | } | ||
| 222 | |||
| 223 | bool Bu::QueueBuf::isReadable() | ||
| 224 | { | ||
| 225 | return true; | ||
| 226 | } | ||
| 227 | |||
| 228 | bool Bu::QueueBuf::isWritable() | ||
| 229 | { | ||
| 230 | return true; | ||
| 231 | } | ||
| 232 | |||
| 233 | bool Bu::QueueBuf::isSeekable() | ||
| 234 | { | ||
| 235 | return false; | ||
| 236 | } | ||
| 237 | |||
| 238 | bool Bu::QueueBuf::isBlocking() | ||
| 239 | { | ||
| 240 | return false; | ||
| 241 | } | ||
| 242 | |||
| 243 | void Bu::QueueBuf::setBlocking( bool ) | ||
| 244 | { | ||
| 245 | } | ||
| 246 | |||
| 247 | void Bu::QueueBuf::setSize( Bu::size ) | ||
| 248 | { | ||
| 249 | } | ||
| 250 | |||
| 251 | Bu::size Bu::QueueBuf::getSize() const | ||
| 252 | { | ||
| 253 | return iTotalSize; | ||
| 254 | } | ||
| 255 | |||
| 256 | Bu::size Bu::QueueBuf::getBlockSize() const | ||
| 257 | { | ||
| 258 | return iBlockSize; | ||
| 259 | } | ||
| 260 | |||
| 261 | Bu::String Bu::QueueBuf::getLocation() const | ||
| 262 | { | ||
| 263 | return ""; | ||
| 264 | } | ||
| 265 | |||
| 266 | void Bu::QueueBuf::addBlock() | ||
| 267 | { | ||
| 268 | lBlocks.append( new char[iBlockSize] ); | ||
| 269 | // sio << "Added new block." << sio.nl; | ||
| 270 | } | ||
| 271 | |||
| 272 | void Bu::QueueBuf::removeBlock() | ||
| 273 | { | ||
| 274 | delete[] lBlocks.first(); | ||
| 275 | lBlocks.erase( lBlocks.begin() ); | ||
| 276 | // sio << "Removed block." << sio.nl; | ||
| 277 | } | ||
| 278 | |||
| diff --git a/src/stable/queuebuf.h b/src/stable/queuebuf.h new file mode 100644 index 0000000..929ca35 --- /dev/null +++ b/src/stable/queuebuf.h | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_QUEUE_BUF_H | ||
| 9 | #define BU_QUEUE_BUF_H | ||
| 10 | |||
| 11 | #include "bu/stream.h" | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * A queuing buffer stream class. All data written to this class is | ||
| 17 | * appended to it, there is no stored position. All data read is read | ||
| 18 | * from the begining and then thrown away. It operates by using a linked | ||
| 19 | * list of small buffers, and deallocating or reusing them when it can. | ||
| 20 | */ | ||
| 21 | class QueueBuf : public Bu::Stream | ||
| 22 | { | ||
| 23 | public: | ||
| 24 | QueueBuf( int iBlockSize=256 ); | ||
| 25 | virtual ~QueueBuf(); | ||
| 26 | |||
| 27 | virtual void close(); | ||
| 28 | virtual Bu::size read( void *pBuf, Bu::size nBytes ); | ||
| 29 | virtual Bu::size peek( void *pBuf, Bu::size nBytes ); | ||
| 30 | virtual Bu::size peek( void *pBuf, Bu::size nBytes, Bu::size nSkip ); | ||
| 31 | virtual Bu::size write( const void *pBuf, Bu::size nBytes ); | ||
| 32 | virtual Bu::size tell(); | ||
| 33 | virtual void seek( Bu::size offset ); | ||
| 34 | virtual void setPos( Bu::size pos ); | ||
| 35 | virtual void setPosEnd( Bu::size pos ); | ||
| 36 | virtual bool isEos(); | ||
| 37 | virtual bool isOpen(); | ||
| 38 | virtual void flush(); | ||
| 39 | virtual bool canRead(); | ||
| 40 | virtual bool canWrite(); | ||
| 41 | virtual bool isReadable(); | ||
| 42 | virtual bool isWritable(); | ||
| 43 | virtual bool isSeekable(); | ||
| 44 | virtual bool isBlocking(); | ||
| 45 | virtual void setBlocking( bool bBlocking=true ); | ||
| 46 | virtual void setSize( Bu::size iSize ); | ||
| 47 | |||
| 48 | virtual size getSize() const; | ||
| 49 | virtual size getBlockSize() const; | ||
| 50 | virtual Bu::String getLocation() const; | ||
| 51 | |||
| 52 | private: | ||
| 53 | void addBlock(); | ||
| 54 | void removeBlock(); | ||
| 55 | |||
| 56 | private: | ||
| 57 | int iBlockSize; | ||
| 58 | int iReadOffset; | ||
| 59 | int iWriteOffset; | ||
| 60 | Bu::size iTotalSize; | ||
| 61 | typedef Bu::List<char *> BlockList; | ||
| 62 | BlockList lBlocks; | ||
| 63 | }; | ||
| 64 | }; | ||
| 65 | |||
| 66 | #endif | ||
| diff --git a/src/stable/ringbuffer.cpp b/src/stable/ringbuffer.cpp new file mode 100644 index 0000000..99b1b1c --- /dev/null +++ b/src/stable/ringbuffer.cpp | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/ringbuffer.h" | ||
| 9 | |||
| diff --git a/src/stable/ringbuffer.h b/src/stable/ringbuffer.h new file mode 100644 index 0000000..f43773d --- /dev/null +++ b/src/stable/ringbuffer.h | |||
| @@ -0,0 +1,228 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_RING_BUFFER_H | ||
| 9 | #define BU_RING_BUFFER_H | ||
| 10 | |||
| 11 | #include <memory> | ||
| 12 | #include "bu/exceptionbase.h" | ||
| 13 | #include "bu/queue.h" | ||
| 14 | #include "bu/sharedcore.h" | ||
| 15 | |||
| 16 | namespace Bu | ||
| 17 | { | ||
| 18 | template<typename value, typename valuealloc> class RingBuffer; | ||
| 19 | |||
| 20 | /** @cond DEVEL */ | ||
| 21 | template<typename value, typename valuealloc> | ||
| 22 | class RingBufferCore | ||
| 23 | { | ||
| 24 | friend class RingBuffer<value, valuealloc>; | ||
| 25 | friend class SharedCore<RingBuffer<value, valuealloc>, | ||
| 26 | RingBufferCore<value, valuealloc> >; | ||
| 27 | private: | ||
| 28 | RingBufferCore() : | ||
| 29 | iCapacity( 0 ), | ||
| 30 | iStart( -1 ), | ||
| 31 | iEnd( -2 ), | ||
| 32 | aData( NULL ) | ||
| 33 | { | ||
| 34 | } | ||
| 35 | |||
| 36 | virtual ~RingBufferCore() | ||
| 37 | { | ||
| 38 | clear(); | ||
| 39 | } | ||
| 40 | |||
| 41 | void init( int iNewCapacity ) | ||
| 42 | { | ||
| 43 | if( iCapacity > 0 ) | ||
| 44 | return; | ||
| 45 | |||
| 46 | iCapacity = iNewCapacity; | ||
| 47 | iStart = -1; | ||
| 48 | iEnd = -2; | ||
| 49 | aData = va.allocate( iCapacity ); | ||
| 50 | } | ||
| 51 | |||
| 52 | void clear() | ||
| 53 | { | ||
| 54 | for( int j = iStart; j < iEnd; j=(j+1%iCapacity) ) | ||
| 55 | { | ||
| 56 | va.destroy( &aData[j] ); | ||
| 57 | } | ||
| 58 | va.deallocate( aData, iCapacity ); | ||
| 59 | aData = NULL; | ||
| 60 | iCapacity = 0; | ||
| 61 | } | ||
| 62 | |||
| 63 | void enqueue( const value &v ) | ||
| 64 | { | ||
| 65 | if( iStart == -1 ) | ||
| 66 | { | ||
| 67 | iStart = 0; | ||
| 68 | iEnd = 1; | ||
| 69 | va.construct( &aData[0], v ); | ||
| 70 | } | ||
| 71 | else if( iStart == iEnd ) | ||
| 72 | { | ||
| 73 | throw ExceptionBase("Hey, it's full!"); | ||
| 74 | } | ||
| 75 | else | ||
| 76 | { | ||
| 77 | va.construct( &aData[iEnd], v ); | ||
| 78 | iEnd = (iEnd+1)%iCapacity; | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | value dequeue() | ||
| 83 | { | ||
| 84 | if( iStart == -1 ) | ||
| 85 | { | ||
| 86 | throw ExceptionBase("No data"); | ||
| 87 | } | ||
| 88 | else | ||
| 89 | { | ||
| 90 | value &v = aData[iStart]; | ||
| 91 | va.destroy( &aData[iStart] ); | ||
| 92 | iStart = (iStart+1)%iCapacity; | ||
| 93 | if( iStart == iEnd ) | ||
| 94 | { | ||
| 95 | iStart = -1; | ||
| 96 | iEnd = -2; | ||
| 97 | } | ||
| 98 | return v; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | value &get( int iIndex ) | ||
| 103 | { | ||
| 104 | return aData[(iIndex+iStart)%iCapacity]; | ||
| 105 | } | ||
| 106 | |||
| 107 | int getSize() | ||
| 108 | { | ||
| 109 | if( iStart < 0 ) | ||
| 110 | return 0; | ||
| 111 | if( iEnd == iStart ) | ||
| 112 | return iCapacity; | ||
| 113 | if( iEnd < iStart ) | ||
| 114 | return iEnd-iStart; | ||
| 115 | return iCapacity-(iEnd-iStart); | ||
| 116 | } | ||
| 117 | |||
| 118 | int iCapacity; | ||
| 119 | int iStart, iEnd; | ||
| 120 | value *aData; | ||
| 121 | valuealloc va; | ||
| 122 | }; | ||
| 123 | /** @endcond */ | ||
| 124 | |||
| 125 | /** | ||
| 126 | *@ingroup Containers | ||
| 127 | */ | ||
| 128 | template<typename value, typename valuealloc=std::allocator<value> > | ||
| 129 | class RingBuffer : public Queue<value>, public SharedCore< | ||
| 130 | RingBuffer<value, valuealloc>, | ||
| 131 | RingBufferCore<value, valuealloc> | ||
| 132 | > | ||
| 133 | { | ||
| 134 | private: | ||
| 135 | typedef RingBuffer<value, valuealloc> MyType; | ||
| 136 | typedef RingBufferCore<value, valuealloc> Core; | ||
| 137 | |||
| 138 | protected: | ||
| 139 | using SharedCore<MyType, Core>::core; | ||
| 140 | using SharedCore<MyType, Core>::_hardCopy; | ||
| 141 | using SharedCore<MyType, Core>::_allocateCore; | ||
| 142 | |||
| 143 | public: | ||
| 144 | RingBuffer( int iCapacity ) | ||
| 145 | { | ||
| 146 | core->init( iCapacity ); | ||
| 147 | } | ||
| 148 | |||
| 149 | RingBuffer( const RingBuffer &rSrc ) : | ||
| 150 | SharedCore<MyType, Core>( rSrc ) | ||
| 151 | { | ||
| 152 | } | ||
| 153 | |||
| 154 | virtual ~RingBuffer() | ||
| 155 | { | ||
| 156 | } | ||
| 157 | |||
| 158 | int getCapacity() const | ||
| 159 | { | ||
| 160 | return core->iCapacity; | ||
| 161 | } | ||
| 162 | |||
| 163 | bool isFilled() const | ||
| 164 | { | ||
| 165 | return (core->iStart == core->iEnd); | ||
| 166 | } | ||
| 167 | |||
| 168 | bool isEmpty() const | ||
| 169 | { | ||
| 170 | return (core->iStart == -1); | ||
| 171 | } | ||
| 172 | |||
| 173 | virtual void enqueue( const value &v ) | ||
| 174 | { | ||
| 175 | _hardCopy(); | ||
| 176 | |||
| 177 | core->enqueue( v ); | ||
| 178 | } | ||
| 179 | |||
| 180 | virtual value dequeue() | ||
| 181 | { | ||
| 182 | _hardCopy(); | ||
| 183 | |||
| 184 | return core->dequeue(); | ||
| 185 | } | ||
| 186 | |||
| 187 | virtual int getSize() const | ||
| 188 | { | ||
| 189 | return core->getSize(); | ||
| 190 | } | ||
| 191 | |||
| 192 | virtual value &peek() | ||
| 193 | { | ||
| 194 | _hardCopy(); | ||
| 195 | |||
| 196 | return core->get( 0 ); | ||
| 197 | } | ||
| 198 | |||
| 199 | virtual const value &peek() const | ||
| 200 | { | ||
| 201 | return core->get( 0 ); | ||
| 202 | } | ||
| 203 | |||
| 204 | value &operator[]( int iIndex ) | ||
| 205 | { | ||
| 206 | _hardCopy(); | ||
| 207 | |||
| 208 | return core->get( iIndex ); | ||
| 209 | } | ||
| 210 | |||
| 211 | protected: | ||
| 212 | virtual Core *_copyCore( Core *src ) | ||
| 213 | { | ||
| 214 | Core *pRet = _allocateCore(); | ||
| 215 | |||
| 216 | pRet->init( src->iCapacity ); | ||
| 217 | int iSize = src->getSize(); | ||
| 218 | for( int j = 0; j < iSize; j++ ) | ||
| 219 | { | ||
| 220 | pRet->enqueue( src->get( j ) ); | ||
| 221 | } | ||
| 222 | |||
| 223 | return pRet; | ||
| 224 | } | ||
| 225 | }; | ||
| 226 | } | ||
| 227 | |||
| 228 | #endif | ||
| diff --git a/src/stable/server.cpp b/src/stable/server.cpp new file mode 100644 index 0000000..1972a3f --- /dev/null +++ b/src/stable/server.cpp | |||
| @@ -0,0 +1,214 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/server.h" | ||
| 9 | #include <errno.h> | ||
| 10 | #include <unistd.h> | ||
| 11 | #include "bu/tcpserversocket.h" | ||
| 12 | #include "bu/client.h" | ||
| 13 | #include "bu/tcpsocket.h" | ||
| 14 | #include "bu/config.h" | ||
| 15 | |||
| 16 | Bu::Server::Server() : | ||
| 17 | nTimeoutSec( 0 ), | ||
| 18 | nTimeoutUSec( 0 ), | ||
| 19 | bAutoTick( false ) | ||
| 20 | { | ||
| 21 | FD_ZERO( &fdActive ); | ||
| 22 | } | ||
| 23 | |||
| 24 | Bu::Server::~Server() | ||
| 25 | { | ||
| 26 | shutdown(); | ||
| 27 | } | ||
| 28 | |||
| 29 | void Bu::Server::addPort( int nPort, int nPoolSize ) | ||
| 30 | { | ||
| 31 | TcpServerSocket *s = new TcpServerSocket( nPort, nPoolSize ); | ||
| 32 | int nSocket = s->getSocket(); | ||
| 33 | FD_SET( nSocket, &fdActive ); | ||
| 34 | hServers.insert( nSocket, s ); | ||
| 35 | } | ||
| 36 | |||
| 37 | void Bu::Server::addPort( const String &sAddr, int nPort, int nPoolSize ) | ||
| 38 | { | ||
| 39 | TcpServerSocket *s = new TcpServerSocket( sAddr, nPort, nPoolSize ); | ||
| 40 | int nSocket = s->getSocket(); | ||
| 41 | FD_SET( nSocket, &fdActive ); | ||
| 42 | hServers.insert( nSocket, s ); | ||
| 43 | } | ||
| 44 | |||
| 45 | void Bu::Server::setTimeout( int nTimeoutSec, int nTimeoutUSec ) | ||
| 46 | { | ||
| 47 | this->nTimeoutSec = nTimeoutSec; | ||
| 48 | this->nTimeoutUSec = nTimeoutUSec; | ||
| 49 | } | ||
| 50 | |||
| 51 | void Bu::Server::scan() | ||
| 52 | { | ||
| 53 | struct timeval xTimeout = { nTimeoutSec, nTimeoutUSec }; | ||
| 54 | |||
| 55 | fd_set fdRead = fdActive; | ||
| 56 | fd_set fdWrite /* = fdActive*/; | ||
| 57 | fd_set fdException = fdActive; | ||
| 58 | |||
| 59 | FD_ZERO( &fdWrite ); | ||
| 60 | for( ClientHash::iterator i = hClients.begin(); i != hClients.end(); i++ ) | ||
| 61 | { | ||
| 62 | if( (*i)->hasOutput() ) | ||
| 63 | FD_SET( i.getKey(), &fdWrite ); | ||
| 64 | } | ||
| 65 | |||
| 66 | if( TEMP_FAILURE_RETRY( select( FD_SETSIZE, | ||
| 67 | &fdRead, &fdWrite, &fdException, &xTimeout ) ) < 0 ) | ||
| 68 | { | ||
| 69 | throw ExceptionBase("Error attempting to scan open connections."); | ||
| 70 | } | ||
| 71 | |||
| 72 | for( int j = 0; j < FD_SETSIZE; j++ ) | ||
| 73 | { | ||
| 74 | if( FD_ISSET( j, &fdRead ) ) | ||
| 75 | { | ||
| 76 | if( hServers.has( j ) ) | ||
| 77 | { | ||
| 78 | TcpServerSocket *pSrv = hServers.get( j ); | ||
| 79 | addClient( pSrv->accept(), pSrv->getPort() ); | ||
| 80 | } | ||
| 81 | else | ||
| 82 | { | ||
| 83 | Client *pClient = hClients.get( j ); | ||
| 84 | pClient->processInput(); | ||
| 85 | if( !pClient->isOpen() ) | ||
| 86 | { | ||
| 87 | closeClient( j ); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | } | ||
| 91 | if( FD_ISSET( j, &fdWrite ) ) | ||
| 92 | { | ||
| 93 | try | ||
| 94 | { | ||
| 95 | Client *pClient = hClients.get( j ); | ||
| 96 | try | ||
| 97 | { | ||
| 98 | pClient->processOutput(); | ||
| 99 | } | ||
| 100 | catch( Bu::TcpSocketException &e ) | ||
| 101 | { | ||
| 102 | closeClient( j ); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | catch( Bu::HashException &e ) | ||
| 106 | { | ||
| 107 | // Do nothing, I guess, the client is already dead... | ||
| 108 | // TODO: Someday, we may want to handle this more graceully. | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | Bu::List<int> lDelete; | ||
| 114 | // Now we just try to write all the pending data on all the sockets. | ||
| 115 | // this could be done better eventually, if we care about the socket | ||
| 116 | // wanting to accept writes (using a select). | ||
| 117 | for( ClientHash::iterator i = hClients.begin(); i != hClients.end(); i++ ) | ||
| 118 | { | ||
| 119 | if( (*i)->wantsDisconnect() && !(*i)->hasOutput() ) | ||
| 120 | { | ||
| 121 | lDelete.append( i.getKey() ); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | for( Bu::List<int>::iterator i = lDelete.begin(); i != lDelete.end(); i++ ) | ||
| 126 | { | ||
| 127 | closeClient( *i ); | ||
| 128 | } | ||
| 129 | |||
| 130 | if( bAutoTick ) | ||
| 131 | tick(); | ||
| 132 | } | ||
| 133 | |||
| 134 | void Bu::Server::addClient( int nSocket, int nPort ) | ||
| 135 | { | ||
| 136 | FD_SET( nSocket, &fdActive ); | ||
| 137 | |||
| 138 | Client *c = new Client( | ||
| 139 | new Bu::TcpSocket( nSocket ), | ||
| 140 | new SrvClientLinkFactory() | ||
| 141 | ); | ||
| 142 | hClients.insert( nSocket, c ); | ||
| 143 | |||
| 144 | onNewConnection( c, nPort ); | ||
| 145 | } | ||
| 146 | |||
| 147 | Bu::Server::SrvClientLink::SrvClientLink( Bu::Client *pClient ) : | ||
| 148 | pClient( pClient ) | ||
| 149 | { | ||
| 150 | } | ||
| 151 | |||
| 152 | Bu::Server::SrvClientLink::~SrvClientLink() | ||
| 153 | { | ||
| 154 | } | ||
| 155 | |||
| 156 | void Bu::Server::SrvClientLink::sendMessage( const Bu::String &sMsg ) | ||
| 157 | { | ||
| 158 | pClient->onMessage( sMsg ); | ||
| 159 | } | ||
| 160 | |||
| 161 | Bu::Server::SrvClientLinkFactory::SrvClientLinkFactory() | ||
| 162 | { | ||
| 163 | } | ||
| 164 | |||
| 165 | Bu::Server::SrvClientLinkFactory::~SrvClientLinkFactory() | ||
| 166 | { | ||
| 167 | } | ||
| 168 | |||
| 169 | Bu::ClientLink *Bu::Server::SrvClientLinkFactory::createLink( | ||
| 170 | Bu::Client *pClient ) | ||
| 171 | { | ||
| 172 | return new SrvClientLink( pClient ); | ||
| 173 | } | ||
| 174 | |||
| 175 | void Bu::Server::setAutoTick( bool bEnable ) | ||
| 176 | { | ||
| 177 | bAutoTick = bEnable; | ||
| 178 | } | ||
| 179 | |||
| 180 | void Bu::Server::tick() | ||
| 181 | { | ||
| 182 | for( ClientHash::iterator i = hClients.begin(); i != hClients.end(); i++ ) | ||
| 183 | { | ||
| 184 | (*i)->tick(); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | void Bu::Server::shutdown() | ||
| 189 | { | ||
| 190 | for( SrvHash::iterator i = hServers.begin(); i != hServers.end(); i++ ) | ||
| 191 | { | ||
| 192 | delete *i; | ||
| 193 | } | ||
| 194 | |||
| 195 | hServers.clear(); | ||
| 196 | |||
| 197 | for( ClientHash::iterator i = hClients.begin(); i != hClients.end(); i++ ) | ||
| 198 | { | ||
| 199 | closeClient( i.getKey() ); | ||
| 200 | } | ||
| 201 | |||
| 202 | hClients.clear(); | ||
| 203 | } | ||
| 204 | |||
| 205 | void Bu::Server::closeClient( int iSocket ) | ||
| 206 | { | ||
| 207 | Bu::Client *pClient = hClients.get( iSocket ); | ||
| 208 | onClosedConnection( pClient ); | ||
| 209 | pClient->close(); | ||
| 210 | hClients.erase( iSocket ); | ||
| 211 | FD_CLR( iSocket, &fdActive ); | ||
| 212 | delete pClient; | ||
| 213 | } | ||
| 214 | |||
| diff --git a/src/stable/server.h b/src/stable/server.h new file mode 100644 index 0000000..c59543a --- /dev/null +++ b/src/stable/server.h | |||
| @@ -0,0 +1,108 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_SERVER_H | ||
| 9 | #define BU_SERVER_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | |||
| 13 | #ifndef WIN32 | ||
| 14 | #include <sys/select.h> | ||
| 15 | #endif | ||
| 16 | |||
| 17 | #include "bu/string.h" | ||
| 18 | #include "bu/list.h" | ||
| 19 | |||
| 20 | #include "bu/clientlink.h" | ||
| 21 | #include "bu/clientlinkfactory.h" | ||
| 22 | #include "bu/hash.h" | ||
| 23 | |||
| 24 | #include "bu/config.h" | ||
| 25 | |||
| 26 | namespace Bu | ||
| 27 | { | ||
| 28 | class TcpServerSocket; | ||
| 29 | class TcpSocket; | ||
| 30 | class Client; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * Core of a network server. This class is distinct from a ServerSocket in | ||
| 34 | * that a ServerSocket is one listening socket, nothing more. Socket will | ||
| 35 | * manage a pool of both ServerSockets and connected Sockets along with | ||
| 36 | * their protocols and buffers. | ||
| 37 | * | ||
| 38 | * To start serving on a new port, use the addPort functions. Each call to | ||
| 39 | * addPort creates a new ServerSocket, starts it listening, and adds it to | ||
| 40 | * the server pool. | ||
| 41 | * | ||
| 42 | * All of the real work is done by scan, which will wait for up | ||
| 43 | * to the timeout set by setTimeout before returning if there is no data | ||
| 44 | * pending. scan should probably be called in some sort of tight | ||
| 45 | * loop, possibly in it's own thread, or in the main control loop. | ||
| 46 | * | ||
| 47 | * In order to use a Server you must subclass it and implement the pure | ||
| 48 | * virtual functions. These allow you to receive notification of events | ||
| 49 | * happening within the server itself, and actually makes it useful. | ||
| 50 | *@ingroup Serving | ||
| 51 | */ | ||
| 52 | class Server | ||
| 53 | { | ||
| 54 | public: | ||
| 55 | Server(); | ||
| 56 | virtual ~Server(); | ||
| 57 | |||
| 58 | void addPort( int nPort, int nPoolSize=40 ); | ||
| 59 | void addPort( const String &sAddr, int nPort, int nPoolSize=40 ); | ||
| 60 | |||
| 61 | virtual void scan(); | ||
| 62 | void setTimeout( int nTimeoutSec, int nTimeoutUSec=0 ); | ||
| 63 | |||
| 64 | void addClient( int nSocket, int nPort ); | ||
| 65 | |||
| 66 | void setAutoTick( bool bEnable=true ); | ||
| 67 | void tick(); | ||
| 68 | |||
| 69 | virtual void onNewConnection( Client *pClient, int nPort )=0; | ||
| 70 | virtual void onClosedConnection( Client *pClient )=0; | ||
| 71 | |||
| 72 | void shutdown(); | ||
| 73 | |||
| 74 | private: | ||
| 75 | void closeClient( int iSocket ); | ||
| 76 | class SrvClientLink : public Bu::ClientLink | ||
| 77 | { | ||
| 78 | public: | ||
| 79 | SrvClientLink( Bu::Client *pClient ); | ||
| 80 | virtual ~SrvClientLink(); | ||
| 81 | |||
| 82 | virtual void sendMessage( const Bu::String &sMsg ); | ||
| 83 | |||
| 84 | private: | ||
| 85 | Bu::Client *pClient; | ||
| 86 | }; | ||
| 87 | |||
| 88 | class SrvClientLinkFactory : public Bu::ClientLinkFactory | ||
| 89 | { | ||
| 90 | public: | ||
| 91 | SrvClientLinkFactory(); | ||
| 92 | virtual ~SrvClientLinkFactory(); | ||
| 93 | |||
| 94 | virtual Bu::ClientLink *createLink( Bu::Client *pClient ); | ||
| 95 | }; | ||
| 96 | |||
| 97 | int nTimeoutSec; | ||
| 98 | int nTimeoutUSec; | ||
| 99 | fd_set fdActive; | ||
| 100 | typedef Hash<int,TcpServerSocket *> SrvHash; | ||
| 101 | SrvHash hServers; | ||
| 102 | typedef Hash<int,Client *> ClientHash; | ||
| 103 | ClientHash hClients; | ||
| 104 | bool bAutoTick; | ||
| 105 | }; | ||
| 106 | } | ||
| 107 | |||
| 108 | #endif | ||
| diff --git a/src/stable/sha1.cpp b/src/stable/sha1.cpp new file mode 100644 index 0000000..bfe4c5a --- /dev/null +++ b/src/stable/sha1.cpp | |||
| @@ -0,0 +1,194 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <string.h> | ||
| 9 | |||
| 10 | #include "bu/stream.h" | ||
| 11 | |||
| 12 | #include "bu/sha1.h" | ||
| 13 | |||
| 14 | Bu::Sha1::Sha1() : | ||
| 15 | uH0( 0x67452301 ), | ||
| 16 | uH1( 0xefcdab89 ), | ||
| 17 | uH2( 0x98badcfe ), | ||
| 18 | uH3( 0x10325476 ), | ||
| 19 | uH4( 0xc3d2e1f0 ), | ||
| 20 | iUnprocessedBytes( 0 ), | ||
| 21 | uTotalBytes( 0 ) | ||
| 22 | { | ||
| 23 | reset(); | ||
| 24 | } | ||
| 25 | |||
| 26 | Bu::Sha1::~Sha1() | ||
| 27 | { | ||
| 28 | } | ||
| 29 | |||
| 30 | void Bu::Sha1::reset() | ||
| 31 | { | ||
| 32 | uH0 = 0x67452301; | ||
| 33 | uH1 = 0xefcdab89; | ||
| 34 | uH2 = 0x98badcfe; | ||
| 35 | uH3 = 0x10325476; | ||
| 36 | uH4 = 0xc3d2e1f0; | ||
| 37 | iUnprocessedBytes = 0; | ||
| 38 | uTotalBytes = 0; | ||
| 39 | } | ||
| 40 | |||
| 41 | void Bu::Sha1::setSalt( const Bu::String & /*sSalt*/ ) | ||
| 42 | { | ||
| 43 | } | ||
| 44 | |||
| 45 | void Bu::Sha1::addData( const void *sDataRaw, int iSize ) | ||
| 46 | { | ||
| 47 | const unsigned char *sData = (const unsigned char *)sDataRaw; | ||
| 48 | // add these bytes to the running total | ||
| 49 | uTotalBytes += iSize; | ||
| 50 | |||
| 51 | // repeat until all data is processed | ||
| 52 | while( iSize > 0 ) | ||
| 53 | { | ||
| 54 | // number of bytes required to complete block | ||
| 55 | int iNeeded = 64 - iUnprocessedBytes; | ||
| 56 | |||
| 57 | // number of bytes to copy (use smaller of two) | ||
| 58 | int iToCopy = (iSize < iNeeded) ? iSize : iNeeded; | ||
| 59 | |||
| 60 | // Copy the bytes | ||
| 61 | memcpy( uBytes + iUnprocessedBytes, sData, iToCopy ); | ||
| 62 | |||
| 63 | // Bytes have been copied | ||
| 64 | iSize -= iToCopy; | ||
| 65 | sData += iToCopy; | ||
| 66 | iUnprocessedBytes += iToCopy; | ||
| 67 | |||
| 68 | // there is a full block | ||
| 69 | if( iUnprocessedBytes == 64 ) | ||
| 70 | { | ||
| 71 | process(); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | Bu::String Bu::Sha1::getResult() | ||
| 77 | { | ||
| 78 | // save the message size | ||
| 79 | uint32_t totalBitsL = uTotalBytes << 3; | ||
| 80 | uint32_t totalBitsH = uTotalBytes >> 29; | ||
| 81 | |||
| 82 | // add 0x80 to the message | ||
| 83 | addData( "\x80", 1 ); | ||
| 84 | |||
| 85 | unsigned char footer[64] = { | ||
| 86 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 87 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 88 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 89 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; | ||
| 90 | |||
| 91 | // block has no room for 8-byte filesize, so finish it | ||
| 92 | if( iUnprocessedBytes > 56 ) | ||
| 93 | addData( (char*)footer, 64 - iUnprocessedBytes); | ||
| 94 | |||
| 95 | // how many zeros do we need | ||
| 96 | int iNeededZeros = 56 - iUnprocessedBytes; | ||
| 97 | |||
| 98 | // store file size (in bits) in big-endian format | ||
| 99 | toBigEndian( totalBitsH, footer + iNeededZeros ); | ||
| 100 | toBigEndian( totalBitsL, footer + iNeededZeros + 4 ); | ||
| 101 | |||
| 102 | // finish the final block | ||
| 103 | addData( (char*)footer, iNeededZeros + 8 ); | ||
| 104 | |||
| 105 | Bu::String sRet( 20 ); | ||
| 106 | |||
| 107 | unsigned char *digest = (unsigned char *)sRet.getStr(); | ||
| 108 | |||
| 109 | // copy the digest bytes | ||
| 110 | toBigEndian( uH0, digest ); | ||
| 111 | toBigEndian( uH1, digest + 4 ); | ||
| 112 | toBigEndian( uH2, digest + 8 ); | ||
| 113 | toBigEndian( uH3, digest + 12 ); | ||
| 114 | toBigEndian( uH4, digest + 16 ); | ||
| 115 | |||
| 116 | // return the digest | ||
| 117 | return sRet; | ||
| 118 | } | ||
| 119 | |||
| 120 | void Bu::Sha1::writeResult( Bu::Stream &sOut ) | ||
| 121 | { | ||
| 122 | sOut.write( getResult() ); | ||
| 123 | } | ||
| 124 | |||
| 125 | void Bu::Sha1::process() | ||
| 126 | { | ||
| 127 | int t; | ||
| 128 | uint32_t a, b, c, d, e, K, f, W[80]; | ||
| 129 | |||
| 130 | // starting values | ||
| 131 | a = uH0; | ||
| 132 | b = uH1; | ||
| 133 | c = uH2; | ||
| 134 | d = uH3; | ||
| 135 | e = uH4; | ||
| 136 | |||
| 137 | // copy and expand the message block | ||
| 138 | for( t = 0; t < 16; t++ ) W[t] = (uBytes[t*4] << 24) | ||
| 139 | +(uBytes[t*4 + 1] << 16) | ||
| 140 | +(uBytes[t*4 + 2] << 8) | ||
| 141 | + uBytes[t*4 + 3]; | ||
| 142 | for(; t< 80; t++ ) W[t] = lrot( W[t-3]^W[t-8]^W[t-14]^W[t-16], 1 ); | ||
| 143 | |||
| 144 | /* main loop */ | ||
| 145 | uint32_t temp; | ||
| 146 | for( t = 0; t < 80; t++ ) | ||
| 147 | { | ||
| 148 | if( t < 20 ) { | ||
| 149 | K = 0x5a827999; | ||
| 150 | f = (b & c) | ((~b) & d); | ||
| 151 | } else if( t < 40 ) { | ||
| 152 | K = 0x6ed9eba1; | ||
| 153 | f = b ^ c ^ d; | ||
| 154 | } else if( t < 60 ) { | ||
| 155 | K = 0x8f1bbcdc; | ||
| 156 | f = (b & c) | (b & d) | (c & d); | ||
| 157 | } else { | ||
| 158 | K = 0xca62c1d6; | ||
| 159 | f = b ^ c ^ d; | ||
| 160 | } | ||
| 161 | temp = lrot(a,5) + f + e + W[t] + K; | ||
| 162 | e = d; | ||
| 163 | d = c; | ||
| 164 | c = lrot(b,30); | ||
| 165 | b = a; | ||
| 166 | a = temp; | ||
| 167 | //printf( "t=%d %08x %08x %08x %08x %08x\n",t,a,b,c,d,e ); | ||
| 168 | } | ||
| 169 | |||
| 170 | /* add variables */ | ||
| 171 | uH0 += a; | ||
| 172 | uH1 += b; | ||
| 173 | uH2 += c; | ||
| 174 | uH3 += d; | ||
| 175 | uH4 += e; | ||
| 176 | |||
| 177 | //printf( "Current: %08x %08x %08x %08x %08x\n",H0,H1,H2,H3,H4 ); | ||
| 178 | /* all bytes have been processed */ | ||
| 179 | iUnprocessedBytes = 0; | ||
| 180 | } | ||
| 181 | |||
| 182 | uint32_t Bu::Sha1::lrot( uint32_t x, int bits ) | ||
| 183 | { | ||
| 184 | return (x<<bits) | (x>>(32 - bits)); | ||
| 185 | } | ||
| 186 | |||
| 187 | void Bu::Sha1::toBigEndian( uint32_t num, unsigned char* byte ) | ||
| 188 | { | ||
| 189 | byte[0] = (unsigned char)(num>>24); | ||
| 190 | byte[1] = (unsigned char)(num>>16); | ||
| 191 | byte[2] = (unsigned char)(num>>8); | ||
| 192 | byte[3] = (unsigned char)num; | ||
| 193 | } | ||
| 194 | |||
| diff --git a/src/stable/sha1.h b/src/stable/sha1.h new file mode 100644 index 0000000..1b7f6df --- /dev/null +++ b/src/stable/sha1.h | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef SHA1_H | ||
| 9 | #define SHA1_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include "bu/cryptohash.h" | ||
| 13 | |||
| 14 | namespace Bu | ||
| 15 | { | ||
| 16 | /** | ||
| 17 | * Calculates SHA-1 sums. This is based strongly on code from Michael D. | ||
| 18 | * Leonhard who released his code under the terms of the MIT license, | ||
| 19 | * thank you! Check out his website http://tamale.net he has a lot of | ||
| 20 | * cool stuff there. | ||
| 21 | */ | ||
| 22 | class Sha1 : public CryptoHash | ||
| 23 | { | ||
| 24 | public: | ||
| 25 | Sha1(); | ||
| 26 | virtual ~Sha1(); | ||
| 27 | |||
| 28 | virtual void reset(); | ||
| 29 | virtual void setSalt( const Bu::String &sSalt ); | ||
| 30 | virtual void addData( const void *sData, int iSize ); | ||
| 31 | using CryptoHash::addData; | ||
| 32 | virtual String getResult(); | ||
| 33 | virtual void writeResult( Stream &sOut ); | ||
| 34 | |||
| 35 | void update( const char* data, int num ); | ||
| 36 | unsigned char* getDigest(); | ||
| 37 | |||
| 38 | // utility methods | ||
| 39 | |||
| 40 | private: | ||
| 41 | static uint32_t lrot( uint32_t x, int bits ); | ||
| 42 | static void toBigEndian( uint32_t in, unsigned char* out ); | ||
| 43 | void process(); | ||
| 44 | |||
| 45 | private: | ||
| 46 | uint32_t uH0, uH1, uH2, uH3, uH4; | ||
| 47 | unsigned char uBytes[64]; | ||
| 48 | int iUnprocessedBytes; | ||
| 49 | uint32_t uTotalBytes; | ||
| 50 | }; | ||
| 51 | }; | ||
| 52 | |||
| 53 | #endif | ||
| diff --git a/src/stable/sharedcore.cpp b/src/stable/sharedcore.cpp new file mode 100644 index 0000000..75f92eb --- /dev/null +++ b/src/stable/sharedcore.cpp | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/sharedcore.h" | ||
| 9 | |||
| diff --git a/src/stable/sharedcore.h b/src/stable/sharedcore.h new file mode 100644 index 0000000..bf9395c --- /dev/null +++ b/src/stable/sharedcore.h | |||
| @@ -0,0 +1,193 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_SHARED_CORE_H | ||
| 9 | #define BU_SHARED_CORE_H | ||
| 10 | |||
| 11 | #include "bu/util.h" | ||
| 12 | |||
| 13 | #include <stdio.h> | ||
| 14 | #include <stdarg.h> | ||
| 15 | |||
| 16 | namespace Bu | ||
| 17 | { | ||
| 18 | /** | ||
| 19 | * A mechanism for creating classes that perform lazy copies. The concept | ||
| 20 | * behind this is that instead of copying a large object when it is assigned | ||
| 21 | * or passed into a copy constructor we simply copy a pointer internally. | ||
| 22 | * The assumption is that many times when an object is passed by value we | ||
| 23 | * don't really want to keep the object around, we want the recipient to | ||
| 24 | * take ownership without allocating a new object. This allows that to | ||
| 25 | * happen. | ||
| 26 | * | ||
| 27 | * When used properly this makes object copying essentially free (O(1), | ||
| 28 | * that is) and performs the actual copy when a user tries to modify the | ||
| 29 | * object. | ||
| 30 | * | ||
| 31 | * For example, lets look at something like the getKeys function in | ||
| 32 | * Bu::Hash. When this function is called it creates a Bu::List of | ||
| 33 | * appropriate type, fills it with keys, and returns it. This is a good | ||
| 34 | * way for this function to behave, there may be additional issues if the | ||
| 35 | * List object were allocated with new and not on the stack. However, | ||
| 36 | * returning the List at the end of the function could potentially take | ||
| 37 | * a very long time depending on the size of the list and the type of the | ||
| 38 | * key. In this case the getKeys function doesn't want ownership of the | ||
| 39 | * List object, and when it returns it, it's local copy will be destroyed. | ||
| 40 | * | ||
| 41 | * However, List inherits from SharedCore, which means that when it is | ||
| 42 | * returned all we do is copy a pointer to the "core" of the list, which | ||
| 43 | * is a very fast operatorion. For a brief moment, before anyone can do | ||
| 44 | * anything else, there are two objects referencing the core of that single | ||
| 45 | * list. However, the getKeys() function will destroy it's local copy | ||
| 46 | * before the calling function can use it's new copy. That means that by | ||
| 47 | * the time the calling function can use it's new List of keys it is the | ||
| 48 | * only one with a reference to the core, and no copy will need to happen. | ||
| 49 | * | ||
| 50 | * Using SharedCore on your own classes is fairly straight forward. There | ||
| 51 | * are only a couple of steps. First, break the class into two classes. | ||
| 52 | * Move every variable from the original class (generally everything that's | ||
| 53 | * private) into the new class. Then make the original class inherit from | ||
| 54 | * SharedCore. The SharedCore template takes 2 parameters, first is the | ||
| 55 | * class it's inheriting from, second is the new core class. Now, in your | ||
| 56 | * original class you will have one class variable, a pointer named core. | ||
| 57 | * All of your original variables will be accessable through core. The next | ||
| 58 | * step is to access everything you used to through core, and to find | ||
| 59 | * every function that may change data in the core. At the top of every | ||
| 60 | * function that may change data you want to call _hardCopy(). | ||
| 61 | * | ||
| 62 | * That's more or less it. A more detailed guide will be written soon. | ||
| 63 | * @todo Write a guide for this. | ||
| 64 | */ | ||
| 65 | template<typename Shell, typename Core> | ||
| 66 | class SharedCore | ||
| 67 | { | ||
| 68 | typedef class SharedCore<Shell, Core> _SharedType; | ||
| 69 | public: | ||
| 70 | SharedCore() : | ||
| 71 | core( NULL ), | ||
| 72 | iRefCount( NULL ) | ||
| 73 | { | ||
| 74 | core = _allocateCore(); | ||
| 75 | iRefCount = new int(1); | ||
| 76 | } | ||
| 77 | |||
| 78 | SharedCore( const _SharedType &rSrc ) : | ||
| 79 | core( NULL ), | ||
| 80 | iRefCount( NULL ) | ||
| 81 | { | ||
| 82 | _softCopy( rSrc ); | ||
| 83 | } | ||
| 84 | |||
| 85 | virtual ~SharedCore() | ||
| 86 | { | ||
| 87 | _deref(); | ||
| 88 | } | ||
| 89 | |||
| 90 | SharedCore &operator=( const SharedCore &rhs ) | ||
| 91 | { | ||
| 92 | if( core == rhs.core ) | ||
| 93 | return *this; | ||
| 94 | |||
| 95 | _softCopy( rhs ); | ||
| 96 | return *this; | ||
| 97 | } | ||
| 98 | |||
| 99 | int getRefCount() const | ||
| 100 | { | ||
| 101 | return *iRefCount; | ||
| 102 | } | ||
| 103 | |||
| 104 | Shell clone() const | ||
| 105 | { | ||
| 106 | Shell s( dynamic_cast<const Shell &>(*this) ); | ||
| 107 | s._hardCopy(); | ||
| 108 | return s; | ||
| 109 | } | ||
| 110 | |||
| 111 | bool isCoreShared( const Shell &rOther ) const | ||
| 112 | { | ||
| 113 | return rOther.core == core; | ||
| 114 | } | ||
| 115 | |||
| 116 | protected: | ||
| 117 | Core *core; | ||
| 118 | void _hardCopy() | ||
| 119 | { | ||
| 120 | if( !core || !iRefCount ) | ||
| 121 | return; | ||
| 122 | if( (*iRefCount) == 1 ) | ||
| 123 | return; | ||
| 124 | Core *copy = _copyCore( core ); | ||
| 125 | _deref(); | ||
| 126 | core = copy; | ||
| 127 | iRefCount = new int( 1 ); | ||
| 128 | } | ||
| 129 | |||
| 130 | /** | ||
| 131 | * Reset core acts like a hard copy, except instead of providing a | ||
| 132 | * standalone copy of the shared core, it provides a brand new core. | ||
| 133 | * | ||
| 134 | * Very useful in functions used to reset the state of an object. | ||
| 135 | */ | ||
| 136 | void _resetCore() | ||
| 137 | { | ||
| 138 | if( core ) | ||
| 139 | _deref(); | ||
| 140 | core = _allocateCore(); | ||
| 141 | iRefCount = new int( 1 ); | ||
| 142 | } | ||
| 143 | |||
| 144 | virtual Core *_allocateCore() | ||
| 145 | { | ||
| 146 | return new Core(); | ||
| 147 | } | ||
| 148 | |||
| 149 | virtual Core *_copyCore( Core *pSrc ) | ||
| 150 | { | ||
| 151 | return new Core( *pSrc ); | ||
| 152 | } | ||
| 153 | |||
| 154 | virtual void _deallocateCore( Core *pSrc ) | ||
| 155 | { | ||
| 156 | delete pSrc; | ||
| 157 | } | ||
| 158 | |||
| 159 | private: | ||
| 160 | void _deref() | ||
| 161 | { | ||
| 162 | if( (--(*iRefCount)) == 0 ) | ||
| 163 | { | ||
| 164 | _deallocateCore( core ); | ||
| 165 | delete iRefCount; | ||
| 166 | } | ||
| 167 | core = NULL; | ||
| 168 | iRefCount = NULL; | ||
| 169 | } | ||
| 170 | |||
| 171 | void _incRefCount() | ||
| 172 | { | ||
| 173 | if( iRefCount && core ) | ||
| 174 | ++(*iRefCount); | ||
| 175 | } | ||
| 176 | |||
| 177 | void _softCopy( const _SharedType &rSrc ) | ||
| 178 | { | ||
| 179 | if( core ) | ||
| 180 | _deref(); | ||
| 181 | core = rSrc.core; | ||
| 182 | iRefCount = rSrc.iRefCount; | ||
| 183 | _incRefCount(); | ||
| 184 | } | ||
| 185 | |||
| 186 | int *iRefCount; | ||
| 187 | }; | ||
| 188 | }; | ||
| 189 | |||
| 190 | #undef fin | ||
| 191 | #undef fout | ||
| 192 | |||
| 193 | #endif | ||
| diff --git a/src/stable/signals.cpp b/src/stable/signals.cpp new file mode 100644 index 0000000..ffbc7ba --- /dev/null +++ b/src/stable/signals.cpp | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/signals.h" | ||
| 9 | |||
| 10 | namespace Bu { subExceptionDef( SignalException ) } | ||
| diff --git a/src/stable/singleton.h b/src/stable/singleton.h new file mode 100644 index 0000000..13db01b --- /dev/null +++ b/src/stable/singleton.h | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_SINGLETON_H | ||
| 9 | #define BU_SINGLETON_H | ||
| 10 | |||
| 11 | #include <stdio.h> | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * Provides singleton functionality in a modular sort of way. Make this the | ||
| 17 | * base class of any other class and you immediately gain singleton | ||
| 18 | * functionality. Be sure to make your constructor and various functions use | ||
| 19 | * intellegent scoping. Cleanup and instantiation are performed automatically | ||
| 20 | * for you at first use and program exit. There are two things that you must | ||
| 21 | * do when using this template, first is to inherit from it with the name of | ||
| 22 | * your class filling in for T and then make this class a friend of your class. | ||
| 23 | *@code | ||
| 24 | * // Making the Single Singleton: | ||
| 25 | * class Single : public Singleton<Single> | ||
| 26 | * { | ||
| 27 | * friend class Singleton<Single>; | ||
| 28 | * protected: | ||
| 29 | * Single(); | ||
| 30 | * ... | ||
| 31 | * }; | ||
| 32 | @endcode | ||
| 33 | * You can still add public functions and variables to your new Singleton child | ||
| 34 | * class, but your constructor should be protected (hence the need for the | ||
| 35 | * friend decleration). | ||
| 36 | */ | ||
| 37 | template <class T> | ||
| 38 | class Singleton | ||
| 39 | { | ||
| 40 | protected: | ||
| 41 | /** | ||
| 42 | * Private constructor. This constructor is empty but has a body so that | ||
| 43 | * you can make your own override of it. Be sure that you're override is | ||
| 44 | * also protected. | ||
| 45 | */ | ||
| 46 | Singleton() {}; | ||
| 47 | |||
| 48 | private: | ||
| 49 | /** | ||
| 50 | * Copy constructor, defined so that you could write your own as well. | ||
| 51 | */ | ||
| 52 | Singleton( const Singleton& ); | ||
| 53 | |||
| 54 | public: | ||
| 55 | /** | ||
| 56 | * Get a handle to the contained instance of the contained class. It is | ||
| 57 | * a reference. | ||
| 58 | *@returns A reference to the contained object. | ||
| 59 | */ | ||
| 60 | static T &getInstance() | ||
| 61 | { | ||
| 62 | static T i; | ||
| 63 | return i; | ||
| 64 | } | ||
| 65 | }; | ||
| 66 | } | ||
| 67 | |||
| 68 | #endif | ||
| diff --git a/src/stable/sio.cpp b/src/stable/sio.cpp new file mode 100644 index 0000000..5f8e234 --- /dev/null +++ b/src/stable/sio.cpp | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/sio.h" | ||
| 9 | |||
| 10 | Bu::StdStream Bu::sioRaw; | ||
| 11 | Bu::Formatter Bu::sio( Bu::sioRaw ); | ||
| 12 | |||
| 13 | Bu::size Bu::print( Bu::Stream &s, const Bu::String &str ) | ||
| 14 | { | ||
| 15 | return s.write( str.getStr(), str.getSize() ); | ||
| 16 | } | ||
| 17 | |||
| 18 | Bu::size Bu::print( const Bu::String &str ) | ||
| 19 | { | ||
| 20 | return print( sioRaw, str ); | ||
| 21 | } | ||
| 22 | |||
| 23 | Bu::size Bu::println( Bu::Stream &s, const Bu::String &str ) | ||
| 24 | { | ||
| 25 | Bu::size sRet = s.write( str.getStr(), str.getSize() ); | ||
| 26 | sRet += s.write("\n", 1 ); | ||
| 27 | s.flush(); | ||
| 28 | return sRet; | ||
| 29 | } | ||
| 30 | |||
| 31 | Bu::size Bu::println( const Bu::String &str ) | ||
| 32 | { | ||
| 33 | return println( sioRaw, str ); | ||
| 34 | } | ||
| 35 | |||
| diff --git a/src/stable/sio.h b/src/stable/sio.h new file mode 100644 index 0000000..9f2cd05 --- /dev/null +++ b/src/stable/sio.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_SIO_H | ||
| 9 | #define BU_SIO_H | ||
| 10 | |||
| 11 | #include "bu/stdstream.h" | ||
| 12 | #include "bu/formatter.h" | ||
| 13 | |||
| 14 | namespace Bu | ||
| 15 | { | ||
| 16 | extern Bu::StdStream sioRaw; | ||
| 17 | extern Bu::Formatter sio; | ||
| 18 | |||
| 19 | Bu::size print( Bu::Stream &s, const Bu::String &str ); | ||
| 20 | Bu::size print( const Bu::String &str ); | ||
| 21 | |||
| 22 | Bu::size println( Bu::Stream &s, const Bu::String &str ); | ||
| 23 | Bu::size println( const Bu::String &str ); | ||
| 24 | }; | ||
| 25 | |||
| 26 | #endif | ||
| diff --git a/src/stable/sptr.cpp b/src/stable/sptr.cpp new file mode 100644 index 0000000..ea21e3b --- /dev/null +++ b/src/stable/sptr.cpp | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/sptr.h" | ||
| diff --git a/src/stable/sptr.h b/src/stable/sptr.h new file mode 100644 index 0000000..4eb5a52 --- /dev/null +++ b/src/stable/sptr.h | |||
| @@ -0,0 +1,229 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_SPTR_H | ||
| 9 | #define BU_SPTR_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include <stdio.h> | ||
| 13 | |||
| 14 | namespace Bu | ||
| 15 | { | ||
| 16 | template<typename T> class SPtr; | ||
| 17 | template< typename Tb, typename Ta > SPtr<Tb> SPtrCast( SPtr<Ta> src ); | ||
| 18 | |||
| 19 | /** | ||
| 20 | *@ingroup Containers | ||
| 21 | */ | ||
| 22 | template<typename T> | ||
| 23 | class SPtr | ||
| 24 | { | ||
| 25 | template<typename Tb, typename Ta> | ||
| 26 | friend SPtr<Tb> SPtrCast( SPtr<Ta> pt ); | ||
| 27 | public: | ||
| 28 | SPtr() : | ||
| 29 | pRefCnt( NULL ), | ||
| 30 | pData( NULL ) | ||
| 31 | { | ||
| 32 | } | ||
| 33 | |||
| 34 | ~SPtr() | ||
| 35 | { | ||
| 36 | decCount(); | ||
| 37 | } | ||
| 38 | |||
| 39 | SPtr( const SPtr<T> &src ) : | ||
| 40 | pRefCnt( src.pRefCnt ), | ||
| 41 | pData( src.pData ) | ||
| 42 | { | ||
| 43 | if( pRefCnt ) | ||
| 44 | (*pRefCnt) += 1; | ||
| 45 | } | ||
| 46 | |||
| 47 | SPtr( T *pSrc ) : | ||
| 48 | pRefCnt( NULL ), | ||
| 49 | pData( pSrc ) | ||
| 50 | { | ||
| 51 | if( pData ) | ||
| 52 | { | ||
| 53 | pRefCnt = new int32_t; | ||
| 54 | (*pRefCnt) = 1; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Get the number of references to this pointer. | ||
| 60 | *@returns (int32_t) The number of references to this pointer. | ||
| 61 | */ | ||
| 62 | int32_t getRefCount() const | ||
| 63 | { | ||
| 64 | return *pRefCnt; | ||
| 65 | } | ||
| 66 | |||
| 67 | void clear() | ||
| 68 | { | ||
| 69 | decCount(); | ||
| 70 | pRefCnt = NULL; | ||
| 71 | pData = NULL; | ||
| 72 | } | ||
| 73 | |||
| 74 | /** | ||
| 75 | * Pointer access operator. | ||
| 76 | *@returns (const T *) | ||
| 77 | */ | ||
| 78 | const T *operator->() const | ||
| 79 | { | ||
| 80 | return pData; | ||
| 81 | } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * Dereference operator. | ||
| 85 | *@returns (const T &) The value at the end of the pointer. | ||
| 86 | */ | ||
| 87 | const T &operator*() const | ||
| 88 | { | ||
| 89 | return *pData; | ||
| 90 | } | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Pointer access operator. | ||
| 94 | *@returns (T *) | ||
| 95 | */ | ||
| 96 | T *operator->() | ||
| 97 | { | ||
| 98 | return pData; | ||
| 99 | } | ||
| 100 | |||
| 101 | /** | ||
| 102 | * Dereference operator. | ||
| 103 | *@returns (T &) The value at the end of the pointer. | ||
| 104 | */ | ||
| 105 | T &operator*() | ||
| 106 | { | ||
| 107 | return *pData; | ||
| 108 | } | ||
| 109 | |||
| 110 | /** | ||
| 111 | * Assignment operator. | ||
| 112 | *@param src (const SPtr<T> &) | ||
| 113 | */ | ||
| 114 | SPtr<T> operator=( const SPtr<T> &src ) | ||
| 115 | { | ||
| 116 | decCount(); | ||
| 117 | pRefCnt = src.pRefCnt; | ||
| 118 | pData = src.pData; | ||
| 119 | if( pRefCnt ) | ||
| 120 | (*pRefCnt) += 1; | ||
| 121 | |||
| 122 | return *this; | ||
| 123 | } | ||
| 124 | |||
| 125 | /** | ||
| 126 | * Assignment operator. | ||
| 127 | *@param src (const SPtr<T> &) | ||
| 128 | */ | ||
| 129 | const SPtr<T> operator=( const SPtr<T> &src ) const | ||
| 130 | { | ||
| 131 | decCount(); | ||
| 132 | pRefCnt = src.pRefCnt; | ||
| 133 | pData = src.pData; | ||
| 134 | if( pRefCnt ) | ||
| 135 | (*pRefCnt) += 1; | ||
| 136 | |||
| 137 | return *this; | ||
| 138 | } | ||
| 139 | |||
| 140 | /** | ||
| 141 | * Equals comparison operator. | ||
| 142 | *@param src (const SPtr<T> &) The SPtr to compare to. | ||
| 143 | *@returns (bool) Are the equal? | ||
| 144 | */ | ||
| 145 | bool operator==( const SPtr<T> &src ) const | ||
| 146 | { | ||
| 147 | return pData == src.pData; | ||
| 148 | } | ||
| 149 | |||
| 150 | /** | ||
| 151 | * Equals comparison operator. | ||
| 152 | *@param src (const T *) The pointer to compare to. | ||
| 153 | *@returns (bool) Are the equal? | ||
| 154 | */ | ||
| 155 | bool operator==( const T *src ) const | ||
| 156 | { | ||
| 157 | return pData == src; | ||
| 158 | } | ||
| 159 | |||
| 160 | /** | ||
| 161 | * Not equals comparison operator. | ||
| 162 | *@param src (const SPtr<T> &) The SPtr to compare to. | ||
| 163 | *@returns (bool) Are the equal? | ||
| 164 | */ | ||
| 165 | bool operator!=( const SPtr<T> &src ) const | ||
| 166 | { | ||
| 167 | return !(pData == src.pData); | ||
| 168 | } | ||
| 169 | |||
| 170 | /** | ||
| 171 | * Not equals comparison operator. | ||
| 172 | *@param src (const T *) The pointer to compare to. | ||
| 173 | *@returns (bool) Are the equal? | ||
| 174 | */ | ||
| 175 | bool operator!=( const T *src ) const | ||
| 176 | { | ||
| 177 | return !(pData == src); | ||
| 178 | } | ||
| 179 | |||
| 180 | /** | ||
| 181 | * Boolean cast operator. Do we have a pointer? | ||
| 182 | */ | ||
| 183 | operator bool() const | ||
| 184 | { | ||
| 185 | return pRefCnt != NULL; | ||
| 186 | } | ||
| 187 | |||
| 188 | /** | ||
| 189 | * Do we have a pointer? | ||
| 190 | *@returns (bool) Do we have a pointer? | ||
| 191 | */ | ||
| 192 | bool isSet() const | ||
| 193 | { | ||
| 194 | return pRefCnt != NULL; | ||
| 195 | } | ||
| 196 | |||
| 197 | private: | ||
| 198 | void decCount() const | ||
| 199 | { | ||
| 200 | if( pRefCnt ) | ||
| 201 | { | ||
| 202 | (*pRefCnt) -= 1; | ||
| 203 | //printf("Decrementing ref-count to %d\n", *pRefCnt ); | ||
| 204 | if( (*pRefCnt) == 0 ) | ||
| 205 | { | ||
| 206 | delete pRefCnt; | ||
| 207 | delete pData; | ||
| 208 | pRefCnt = NULL; | ||
| 209 | pData = NULL; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | mutable int32_t *pRefCnt; | ||
| 215 | mutable T *pData; | ||
| 216 | }; | ||
| 217 | |||
| 218 | template< typename Tb, typename Ta > SPtr<Tb> SPtrCast( SPtr<Ta> src ) | ||
| 219 | { | ||
| 220 | SPtr<Tb> ret; | ||
| 221 | ret.pRefCnt = src.pRefCnt; | ||
| 222 | ret.pData = dynamic_cast<Tb *>(src.pData); | ||
| 223 | if( ret.pRefCnt ) | ||
| 224 | (*(ret.pRefCnt)) += 1; | ||
| 225 | return ret; | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | #endif | ||
| diff --git a/src/stable/stack.cpp b/src/stable/stack.cpp new file mode 100644 index 0000000..73352d3 --- /dev/null +++ b/src/stable/stack.cpp | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/stack.h" | ||
| diff --git a/src/stable/stack.h b/src/stable/stack.h new file mode 100644 index 0000000..0d1ed3c --- /dev/null +++ b/src/stable/stack.h | |||
| @@ -0,0 +1,85 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_STACK_H | ||
| 9 | #define BU_STACK_H | ||
| 10 | |||
| 11 | #include <memory> | ||
| 12 | #include "bu/config.h" | ||
| 13 | |||
| 14 | namespace Bu | ||
| 15 | { | ||
| 16 | template<typename value, typename valuealloc=std::allocator<value> > | ||
| 17 | class Stack | ||
| 18 | { | ||
| 19 | private: | ||
| 20 | typedef struct Chunk | ||
| 21 | { | ||
| 22 | value *pValue; | ||
| 23 | Chunk *pPrev; | ||
| 24 | } Chunk; | ||
| 25 | public: | ||
| 26 | Stack() : | ||
| 27 | pTop( NULL ) | ||
| 28 | { | ||
| 29 | } | ||
| 30 | |||
| 31 | virtual ~Stack() | ||
| 32 | { | ||
| 33 | } | ||
| 34 | |||
| 35 | void push( const value &v ) | ||
| 36 | { | ||
| 37 | Chunk *pChnk = new Chunk; | ||
| 38 | pChnk->pValue = va.allocate( 1 ); | ||
| 39 | va.construct( pChnk->pValue, v ); | ||
| 40 | pChnk->pPrev = pTop; | ||
| 41 | pTop = pChnk; | ||
| 42 | } | ||
| 43 | |||
| 44 | value &peek() | ||
| 45 | { | ||
| 46 | return *pTop->pValue; | ||
| 47 | } | ||
| 48 | |||
| 49 | value &top() | ||
| 50 | { | ||
| 51 | return *pTop->pValue; | ||
| 52 | } | ||
| 53 | |||
| 54 | value pop() | ||
| 55 | { | ||
| 56 | value ret( *pTop->pValue ); | ||
| 57 | |||
| 58 | Chunk *pChnk = pTop; | ||
| 59 | pTop = pTop->pPrev; | ||
| 60 | |||
| 61 | va.destroy( pChnk->pValue ); | ||
| 62 | va.deallocate( pChnk->pValue, 1 ); | ||
| 63 | delete pChnk; | ||
| 64 | |||
| 65 | return ret; | ||
| 66 | } | ||
| 67 | |||
| 68 | void clear() | ||
| 69 | { | ||
| 70 | while( !isEmpty() ) | ||
| 71 | pop(); | ||
| 72 | } | ||
| 73 | |||
| 74 | bool isEmpty() | ||
| 75 | { | ||
| 76 | return pTop == NULL; | ||
| 77 | } | ||
| 78 | |||
| 79 | private: | ||
| 80 | Chunk *pTop; | ||
| 81 | valuealloc va; | ||
| 82 | }; | ||
| 83 | } | ||
| 84 | |||
| 85 | #endif | ||
| diff --git a/src/stable/staticmembuf.cpp b/src/stable/staticmembuf.cpp new file mode 100644 index 0000000..74fae31 --- /dev/null +++ b/src/stable/staticmembuf.cpp | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/staticmembuf.h" | ||
| 9 | |||
| 10 | using namespace Bu; | ||
| 11 | |||
| 12 | Bu::StaticMemBuf::StaticMemBuf( const void *pData, size iSize ) : | ||
| 13 | pData( pData ), | ||
| 14 | iSize( iSize ), | ||
| 15 | nPos( 0 ) | ||
| 16 | { | ||
| 17 | } | ||
| 18 | |||
| 19 | Bu::StaticMemBuf::~StaticMemBuf() | ||
| 20 | { | ||
| 21 | } | ||
| 22 | |||
| 23 | void Bu::StaticMemBuf::close() | ||
| 24 | { | ||
| 25 | } | ||
| 26 | |||
| 27 | size Bu::StaticMemBuf::read( void *pBuf, size nBytes ) | ||
| 28 | { | ||
| 29 | if( iSize-nPos < nBytes ) | ||
| 30 | nBytes = iSize-nPos; | ||
| 31 | |||
| 32 | memcpy( pBuf, ((char *)pData)+nPos, nBytes ); | ||
| 33 | nPos += nBytes; | ||
| 34 | |||
| 35 | return nBytes; | ||
| 36 | } | ||
| 37 | |||
| 38 | size Bu::StaticMemBuf::write( const void *, size ) | ||
| 39 | { | ||
| 40 | return -1; | ||
| 41 | } | ||
| 42 | |||
| 43 | size Bu::StaticMemBuf::tell() | ||
| 44 | { | ||
| 45 | return nPos; | ||
| 46 | } | ||
| 47 | |||
| 48 | void Bu::StaticMemBuf::seek( size offset ) | ||
| 49 | { | ||
| 50 | nPos += offset; | ||
| 51 | if( nPos < 0 ) nPos = 0; | ||
| 52 | else if( nPos > iSize ) nPos = iSize; | ||
| 53 | } | ||
| 54 | |||
| 55 | void Bu::StaticMemBuf::setPos( size pos ) | ||
| 56 | { | ||
| 57 | nPos = pos; | ||
| 58 | if( nPos < 0 ) nPos = 0; | ||
| 59 | else if( nPos > iSize ) nPos = iSize; | ||
| 60 | } | ||
| 61 | |||
| 62 | void Bu::StaticMemBuf::setPosEnd( size pos ) | ||
| 63 | { | ||
| 64 | nPos = iSize-pos; | ||
| 65 | if( nPos < 0 ) nPos = 0; | ||
| 66 | else if( nPos > iSize ) nPos = iSize; | ||
| 67 | } | ||
| 68 | |||
| 69 | bool Bu::StaticMemBuf::isEos() | ||
| 70 | { | ||
| 71 | return (nPos == iSize); | ||
| 72 | } | ||
| 73 | |||
| 74 | bool Bu::StaticMemBuf::isOpen() | ||
| 75 | { | ||
| 76 | return true; | ||
| 77 | } | ||
| 78 | |||
| 79 | void Bu::StaticMemBuf::flush() | ||
| 80 | { | ||
| 81 | } | ||
| 82 | |||
| 83 | bool Bu::StaticMemBuf::canRead() | ||
| 84 | { | ||
| 85 | return !isEos(); | ||
| 86 | } | ||
| 87 | |||
| 88 | bool Bu::StaticMemBuf::canWrite() | ||
| 89 | { | ||
| 90 | return false; | ||
| 91 | } | ||
| 92 | |||
| 93 | bool Bu::StaticMemBuf::isReadable() | ||
| 94 | { | ||
| 95 | return true; | ||
| 96 | } | ||
| 97 | |||
| 98 | bool Bu::StaticMemBuf::isWritable() | ||
| 99 | { | ||
| 100 | return false; | ||
| 101 | } | ||
| 102 | |||
| 103 | bool Bu::StaticMemBuf::isSeekable() | ||
| 104 | { | ||
| 105 | return true; | ||
| 106 | } | ||
| 107 | |||
| 108 | bool Bu::StaticMemBuf::isBlocking() | ||
| 109 | { | ||
| 110 | return true; | ||
| 111 | } | ||
| 112 | |||
| 113 | void Bu::StaticMemBuf::setBlocking( bool ) | ||
| 114 | { | ||
| 115 | } | ||
| 116 | |||
| 117 | void Bu::StaticMemBuf::setSize( size ) | ||
| 118 | { | ||
| 119 | } | ||
| 120 | |||
| 121 | Bu::size Bu::StaticMemBuf::getSize() const | ||
| 122 | { | ||
| 123 | return iSize; | ||
| 124 | } | ||
| 125 | |||
| 126 | Bu::size Bu::StaticMemBuf::getBlockSize() const | ||
| 127 | { | ||
| 128 | return iSize; | ||
| 129 | } | ||
| 130 | |||
| 131 | Bu::String Bu::StaticMemBuf::getLocation() const | ||
| 132 | { | ||
| 133 | return ""; | ||
| 134 | } | ||
| 135 | |||
| diff --git a/src/stable/staticmembuf.h b/src/stable/staticmembuf.h new file mode 100644 index 0000000..f168de3 --- /dev/null +++ b/src/stable/staticmembuf.h | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_STATIC_MEM_BUF_H | ||
| 9 | #define BU_STATIC_MEM_BUF_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | |||
| 13 | #include "bu/config.h" | ||
| 14 | #include "bu/stream.h" | ||
| 15 | |||
| 16 | namespace Bu | ||
| 17 | { | ||
| 18 | /** | ||
| 19 | * An immutable, read-only memory buffer. Construct this buffer around a | ||
| 20 | * block of raw memory, provide the length of the block, and you can read | ||
| 21 | * from that block via this class as though it were a normal stream. | ||
| 22 | * | ||
| 23 | * Use this class instead of MemBuf when you have a string already, and | ||
| 24 | * don't need to change it. MemBuf will make a copy of your string for | ||
| 25 | * it's own use (often) and this will not (ever). | ||
| 26 | *@ingroup Streams | ||
| 27 | */ | ||
| 28 | class StaticMemBuf : public Stream | ||
| 29 | { | ||
| 30 | public: | ||
| 31 | StaticMemBuf( const void *pData, size iSize ); | ||
| 32 | virtual ~StaticMemBuf(); | ||
| 33 | |||
| 34 | virtual void close(); | ||
| 35 | virtual size read( void *pBuf, size iBytes ); | ||
| 36 | |||
| 37 | virtual size write( const void *pBuf, size iBytes ); | ||
| 38 | using Stream::write; | ||
| 39 | virtual size tell(); | ||
| 40 | virtual void seek( size offset ); | ||
| 41 | virtual void setPos( size pos ); | ||
| 42 | virtual void setPosEnd( size pos ); | ||
| 43 | virtual bool isEos(); | ||
| 44 | virtual bool isOpen(); | ||
| 45 | virtual void flush(); | ||
| 46 | virtual bool canRead(); | ||
| 47 | virtual bool canWrite(); | ||
| 48 | virtual bool isReadable(); | ||
| 49 | virtual bool isWritable(); | ||
| 50 | virtual bool isSeekable(); | ||
| 51 | virtual bool isBlocking(); | ||
| 52 | virtual void setBlocking( bool bBlocking=true ); | ||
| 53 | virtual void setSize( size iSize ); | ||
| 54 | virtual size getSize() const; | ||
| 55 | virtual size getBlockSize() const; | ||
| 56 | virtual Bu::String getLocation() const; | ||
| 57 | |||
| 58 | private: | ||
| 59 | const void *pData; | ||
| 60 | size iSize; | ||
| 61 | size nPos; | ||
| 62 | }; | ||
| 63 | } | ||
| 64 | |||
| 65 | #endif | ||
| diff --git a/src/stable/stdstream.cpp b/src/stable/stdstream.cpp new file mode 100644 index 0000000..b1d5d61 --- /dev/null +++ b/src/stable/stdstream.cpp | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <stdio.h> | ||
| 9 | #include "bu/stdstream.h" | ||
| 10 | |||
| 11 | Bu::StdStream::StdStream() | ||
| 12 | { | ||
| 13 | } | ||
| 14 | |||
| 15 | Bu::StdStream::~StdStream() | ||
| 16 | { | ||
| 17 | } | ||
| 18 | |||
| 19 | void Bu::StdStream::close() | ||
| 20 | { | ||
| 21 | } | ||
| 22 | |||
| 23 | Bu::size Bu::StdStream::read( void *pBuf, Bu::size nBytes ) | ||
| 24 | { | ||
| 25 | return fread( pBuf, 1, nBytes, stdin ); | ||
| 26 | } | ||
| 27 | |||
| 28 | Bu::size Bu::StdStream::write( const void *pBuf, Bu::size nBytes ) | ||
| 29 | { | ||
| 30 | return fwrite( pBuf, 1, nBytes, stdout ); | ||
| 31 | } | ||
| 32 | |||
| 33 | Bu::size Bu::StdStream::tell() | ||
| 34 | { | ||
| 35 | return 0; | ||
| 36 | } | ||
| 37 | |||
| 38 | void Bu::StdStream::seek( Bu::size ) | ||
| 39 | { | ||
| 40 | } | ||
| 41 | |||
| 42 | void Bu::StdStream::setPos( Bu::size ) | ||
| 43 | { | ||
| 44 | } | ||
| 45 | |||
| 46 | void Bu::StdStream::setPosEnd( Bu::size ) | ||
| 47 | { | ||
| 48 | } | ||
| 49 | |||
| 50 | bool Bu::StdStream::isEos() | ||
| 51 | { | ||
| 52 | return false; | ||
| 53 | } | ||
| 54 | |||
| 55 | bool Bu::StdStream::isOpen() | ||
| 56 | { | ||
| 57 | return true; | ||
| 58 | } | ||
| 59 | |||
| 60 | void Bu::StdStream::flush() | ||
| 61 | { | ||
| 62 | fflush( stdout ); | ||
| 63 | } | ||
| 64 | |||
| 65 | bool Bu::StdStream::canRead() | ||
| 66 | { | ||
| 67 | return true; | ||
| 68 | } | ||
| 69 | |||
| 70 | bool Bu::StdStream::canWrite() | ||
| 71 | { | ||
| 72 | return true; | ||
| 73 | } | ||
| 74 | |||
| 75 | bool Bu::StdStream::isReadable() | ||
| 76 | { | ||
| 77 | return true; | ||
| 78 | } | ||
| 79 | |||
| 80 | bool Bu::StdStream::isWritable() | ||
| 81 | { | ||
| 82 | return true; | ||
| 83 | } | ||
| 84 | |||
| 85 | bool Bu::StdStream::isSeekable() | ||
| 86 | { | ||
| 87 | return false; | ||
| 88 | } | ||
| 89 | |||
| 90 | bool Bu::StdStream::isBlocking() | ||
| 91 | { | ||
| 92 | return true; | ||
| 93 | } | ||
| 94 | |||
| 95 | void Bu::StdStream::setBlocking( bool ) | ||
| 96 | { | ||
| 97 | } | ||
| 98 | |||
| 99 | void Bu::StdStream::setSize( Bu::size ) | ||
| 100 | { | ||
| 101 | } | ||
| 102 | |||
| 103 | Bu::size Bu::StdStream::getSize() const | ||
| 104 | { | ||
| 105 | return 0; | ||
| 106 | } | ||
| 107 | |||
| 108 | Bu::size Bu::StdStream::getBlockSize() const | ||
| 109 | { | ||
| 110 | return 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | Bu::String Bu::StdStream::getLocation() const | ||
| 114 | { | ||
| 115 | return ""; | ||
| 116 | } | ||
| 117 | |||
| diff --git a/src/stable/stdstream.h b/src/stable/stdstream.h new file mode 100644 index 0000000..ff6c774 --- /dev/null +++ b/src/stable/stdstream.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_STD_STREAM_H | ||
| 9 | #define BU_STD_STREAM_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include "stream.h" | ||
| 13 | |||
| 14 | namespace Bu | ||
| 15 | { | ||
| 16 | /** | ||
| 17 | *@ingroup Streams | ||
| 18 | */ | ||
| 19 | class StdStream : public Stream | ||
| 20 | { | ||
| 21 | public: | ||
| 22 | StdStream(); | ||
| 23 | virtual ~StdStream(); | ||
| 24 | |||
| 25 | virtual void close(); | ||
| 26 | virtual size read( void *pBuf, size nBytes ); | ||
| 27 | virtual size write( const void *pBuf, size nBytes ); | ||
| 28 | using Stream::write; | ||
| 29 | virtual size tell(); | ||
| 30 | virtual void seek( size offset ); | ||
| 31 | virtual void setPos( size pos ); | ||
| 32 | virtual void setPosEnd( size pos ); | ||
| 33 | virtual bool isEos(); | ||
| 34 | virtual bool isOpen(); | ||
| 35 | virtual void flush(); | ||
| 36 | virtual bool canRead(); | ||
| 37 | virtual bool canWrite(); | ||
| 38 | virtual bool isReadable(); | ||
| 39 | virtual bool isWritable(); | ||
| 40 | virtual bool isSeekable(); | ||
| 41 | virtual bool isBlocking(); | ||
| 42 | virtual void setBlocking( bool bBlocking=true ); | ||
| 43 | virtual void setSize( size iSize ); | ||
| 44 | virtual size getSize() const; | ||
| 45 | virtual size getBlockSize() const; | ||
| 46 | virtual Bu::String getLocation() const; | ||
| 47 | }; | ||
| 48 | } | ||
| 49 | |||
| 50 | #endif | ||
| diff --git a/src/stable/stream.cpp b/src/stable/stream.cpp new file mode 100644 index 0000000..58641cc --- /dev/null +++ b/src/stable/stream.cpp | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/stream.h" | ||
| 9 | |||
| 10 | Bu::Stream::Stream() | ||
| 11 | { | ||
| 12 | } | ||
| 13 | |||
| 14 | Bu::Stream::~Stream() | ||
| 15 | { | ||
| 16 | } | ||
| 17 | |||
| 18 | Bu::String Bu::Stream::readLine() | ||
| 19 | { | ||
| 20 | Bu::String sRet; | ||
| 21 | |||
| 22 | for(;;) | ||
| 23 | { | ||
| 24 | char s; | ||
| 25 | if( read( &s, 1 ) == 0 ) | ||
| 26 | return sRet; | ||
| 27 | if( s == '\n' || s == '\r' ) | ||
| 28 | return sRet; | ||
| 29 | sRet.append( s ); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | Bu::String Bu::Stream::readAll() | ||
| 34 | { | ||
| 35 | Bu::String sRet; | ||
| 36 | char buf[4096]; | ||
| 37 | |||
| 38 | while( !isEos() ) | ||
| 39 | { | ||
| 40 | int iRead = read( buf, 4096 ); | ||
| 41 | if( iRead == 0 ) | ||
| 42 | return sRet; | ||
| 43 | sRet.append( buf, iRead ); | ||
| 44 | } | ||
| 45 | |||
| 46 | return sRet; | ||
| 47 | } | ||
| 48 | |||
| 49 | Bu::size Bu::Stream::write( const Bu::String &sBuf ) | ||
| 50 | { | ||
| 51 | return write( sBuf.getStr(), sBuf.getSize() ); | ||
| 52 | } | ||
| 53 | |||
| diff --git a/src/stable/stream.h b/src/stable/stream.h new file mode 100644 index 0000000..b35f6ee --- /dev/null +++ b/src/stable/stream.h | |||
| @@ -0,0 +1,205 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_STREAM_H | ||
| 9 | #define BU_STREAM_H | ||
| 10 | |||
| 11 | #include "bu/config.h" | ||
| 12 | |||
| 13 | #include <stdint.h> | ||
| 14 | #include <stdio.h> | ||
| 15 | |||
| 16 | #include "bu/string.h" | ||
| 17 | |||
| 18 | namespace Bu | ||
| 19 | { | ||
| 20 | /** | ||
| 21 | * The basis for a completely general data transport mechanism. Anything | ||
| 22 | * that inherits from this should provide at least the basic read and/or | ||
| 23 | * write functions, and very probably the close function. Any functions | ||
| 24 | * that aren't supported should throw an exception if called. | ||
| 25 | * | ||
| 26 | * The constructor of a child class should pretty much universally be used | ||
| 27 | * to open the stream. I can't think of anything that should require an | ||
| 28 | * exception. | ||
| 29 | *@ingroup Streams | ||
| 30 | */ | ||
| 31 | class Stream | ||
| 32 | { | ||
| 33 | public: | ||
| 34 | Stream(); | ||
| 35 | virtual ~Stream(); | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Close the stream. | ||
| 39 | */ | ||
| 40 | virtual void close() = 0; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Read data from the stream into a buffer. | ||
| 44 | *@param pBuf (void *) Buffer which will be filled. | ||
| 45 | *@param nBytes (size_t) Max data to read. | ||
| 46 | *@returns (size_t) Amount of data read. | ||
| 47 | */ | ||
| 48 | virtual size read( void *pBuf, size iBytes ) = 0; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Attempts to read a complete line from the stream. This will stop | ||
| 52 | * reading when it has reached the end of the stream, or runs out of | ||
| 53 | * data in a non-blocking stream. | ||
| 54 | *@returns The line read, not including newline character. | ||
| 55 | */ | ||
| 56 | virtual Bu::String readLine(); | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Reads all data from the current position onward until isEos returns | ||
| 60 | * true and returns it as a Bu::String. This will also return if no | ||
| 61 | * data is available and the stream is in non-blocking mode. This | ||
| 62 | * function is intended for very particular circumstances and is often | ||
| 63 | * not the most efficient way to access the data that you would like. | ||
| 64 | *@returns The entire stream contents. | ||
| 65 | */ | ||
| 66 | virtual Bu::String readAll(); | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Write data to the stream. | ||
| 70 | *@param pBuf (const void *) The data to be written. | ||
| 71 | *@param nBytes (size_t) Amount of data to write from pBuf. | ||
| 72 | *@returns (size_t) Amount of data actually written. | ||
| 73 | */ | ||
| 74 | virtual size write( const void *pBuf, size iBytes ) = 0; | ||
| 75 | |||
| 76 | virtual size write( const Bu::String &sBuf ); | ||
| 77 | |||
| 78 | /** | ||
| 79 | * Get the current position in the stream. | ||
| 80 | *@returns (long) The current position in the stream. | ||
| 81 | */ | ||
| 82 | virtual size tell() = 0; | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Seek to a position in the stream relative to the current position. | ||
| 86 | *@param offset (long) Offset from current position to seek to. | ||
| 87 | */ | ||
| 88 | virtual void seek( size offset ) = 0; | ||
| 89 | |||
| 90 | /** | ||
| 91 | * Set position in the stream relative to the start of the stream. | ||
| 92 | *@param pos (long) The position. | ||
| 93 | */ | ||
| 94 | virtual void setPos( size pos ) = 0; | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Set position in the stream relative to the end of the stream. | ||
| 98 | *@param pos (long) The position. | ||
| 99 | */ | ||
| 100 | virtual void setPosEnd( size pos ) = 0; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Are we at the end of the stream? | ||
| 104 | *@returns (bool) Are we at the end of the stream? | ||
| 105 | */ | ||
| 106 | virtual bool isEos() = 0; | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Is the stream open? | ||
| 110 | *@returns (bool) Is the stream open? | ||
| 111 | */ | ||
| 112 | virtual bool isOpen() = 0; | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Flush any data still held in buffers. | ||
| 116 | */ | ||
| 117 | virtual void flush() = 0; | ||
| 118 | |||
| 119 | /** | ||
| 120 | * In non-blocking streams this indicates if a read operation will | ||
| 121 | * return data at the moment or not. In blocking streams this should | ||
| 122 | * return the same value as isEos(). | ||
| 123 | */ | ||
| 124 | virtual bool canRead() = 0; | ||
| 125 | |||
| 126 | /** | ||
| 127 | * In non-blocking streams this indicates if a write operation will | ||
| 128 | * actually write one or more bytes. In some cases writing is not | ||
| 129 | * allowed (e.g. internal buffers are full) temporarilly. In blocking | ||
| 130 | * streams this should return the same value as isWritable. | ||
| 131 | */ | ||
| 132 | virtual bool canWrite() = 0; | ||
| 133 | |||
| 134 | /** | ||
| 135 | * Indicates if the stream is capable of read operations. This does not | ||
| 136 | * indicate if such operations will return useful data, see canRead for | ||
| 137 | * that. | ||
| 138 | */ | ||
| 139 | virtual bool isReadable() = 0; | ||
| 140 | |||
| 141 | /** | ||
| 142 | * Indicates if the stream is capable of write operations. This does | ||
| 143 | * not indicate if such operations will succeed or fail, see canWrite | ||
| 144 | * for that. | ||
| 145 | */ | ||
| 146 | virtual bool isWritable() = 0; | ||
| 147 | |||
| 148 | /** | ||
| 149 | * Indicates if the stream is capable of seek operations. This is | ||
| 150 | * generally false for non-blocking streams. Some buffered streams may | ||
| 151 | * support limited in-buffer seeking. | ||
| 152 | */ | ||
| 153 | virtual bool isSeekable() = 0; | ||
| 154 | |||
| 155 | /** | ||
| 156 | * Are we currently set to block mode? | ||
| 157 | *@returns (bool) | ||
| 158 | */ | ||
| 159 | virtual bool isBlocking() = 0; | ||
| 160 | |||
| 161 | /** | ||
| 162 | * Set stream to blocking or non-blocking mode. | ||
| 163 | *@param bBlocking (bool) Whether we should block or not. | ||
| 164 | */ | ||
| 165 | virtual void setBlocking( bool bBlocking=true ) = 0; | ||
| 166 | |||
| 167 | /** | ||
| 168 | * Set the size of the stream, this does not apply to many types of | ||
| 169 | * streams. For those that it does apply to, data will be added or | ||
| 170 | * removed from the end of the stream, but the content of the added | ||
| 171 | * data is undefined. | ||
| 172 | */ | ||
| 173 | virtual void setSize( size iSize ) = 0; | ||
| 174 | |||
| 175 | /** | ||
| 176 | * Returns the size of the stream if the stream can have a size. For | ||
| 177 | * streams that do not (sockets, pipes, etc.) this should throw an | ||
| 178 | * unsupported exception. | ||
| 179 | */ | ||
| 180 | virtual size getSize() const = 0; | ||
| 181 | |||
| 182 | /** | ||
| 183 | * Returns the block-size of the stream, if it has one. This should | ||
| 184 | * throw an unsupported exception. In some cases the block size | ||
| 185 | * returned will not represent quite the same thing, for example, | ||
| 186 | * sockets will return their MTU, while files will return the | ||
| 187 | * filesystem's block size, and memory buffers will throw an exception. | ||
| 188 | */ | ||
| 189 | virtual size getBlockSize() const = 0; | ||
| 190 | |||
| 191 | /** | ||
| 192 | * If possible, this returns a string that can be used to describe how | ||
| 193 | * to access the open stream. Not all streams support this, such as | ||
| 194 | * MemBuf, but for files it may give you a path to a file, for a socket | ||
| 195 | * it may give you an ip address, etc. If it isn't supported, an empty | ||
| 196 | * string may be returned. | ||
| 197 | */ | ||
| 198 | virtual Bu::String getLocation() const = 0; | ||
| 199 | |||
| 200 | private: | ||
| 201 | |||
| 202 | }; | ||
| 203 | } | ||
| 204 | |||
| 205 | #endif | ||
| diff --git a/src/stable/streamstack.cpp b/src/stable/streamstack.cpp new file mode 100644 index 0000000..d45306d --- /dev/null +++ b/src/stable/streamstack.cpp | |||
| @@ -0,0 +1,234 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/streamstack.h" | ||
| 9 | |||
| 10 | Bu::StreamStack::StreamStack() | ||
| 11 | { | ||
| 12 | } | ||
| 13 | |||
| 14 | Bu::StreamStack::StreamStack( Bu::Stream *pStream ) | ||
| 15 | { | ||
| 16 | lFilts.prepend( pStream ); | ||
| 17 | } | ||
| 18 | |||
| 19 | Bu::StreamStack::~StreamStack() | ||
| 20 | { | ||
| 21 | clear(); | ||
| 22 | } | ||
| 23 | |||
| 24 | bool Bu::StreamStack::isEmpty() | ||
| 25 | { | ||
| 26 | return lFilts.isEmpty(); | ||
| 27 | } | ||
| 28 | |||
| 29 | bool Bu::StreamStack::hasStream() | ||
| 30 | { | ||
| 31 | return !lFilts.isEmpty(); | ||
| 32 | } | ||
| 33 | |||
| 34 | void Bu::StreamStack::setStream( Bu::Stream *pStream ) | ||
| 35 | { | ||
| 36 | if( !lFilts.isEmpty() ) | ||
| 37 | throw Bu::ExceptionBase("There is already a stream set."); | ||
| 38 | |||
| 39 | lFilts.prepend( pStream ); | ||
| 40 | } | ||
| 41 | |||
| 42 | void Bu::StreamStack::clear() | ||
| 43 | { | ||
| 44 | for( FilterList::iterator i = lFilts.begin(); i; i++ ) | ||
| 45 | { | ||
| 46 | delete *i; | ||
| 47 | } | ||
| 48 | |||
| 49 | lFilts.clear(); | ||
| 50 | } | ||
| 51 | |||
| 52 | void Bu::StreamStack::popFilter() | ||
| 53 | { | ||
| 54 | if( lFilts.isEmpty() ) | ||
| 55 | return; | ||
| 56 | |||
| 57 | delete lFilts.first(); | ||
| 58 | lFilts.erase( lFilts.begin() ); | ||
| 59 | } | ||
| 60 | |||
| 61 | Bu::Stream *Bu::StreamStack::getTop() | ||
| 62 | { | ||
| 63 | checkStack(); | ||
| 64 | |||
| 65 | return lFilts.first(); | ||
| 66 | } | ||
| 67 | |||
| 68 | Bu::Stream *Bu::StreamStack::getStream() | ||
| 69 | { | ||
| 70 | checkStack(); | ||
| 71 | |||
| 72 | return lFilts.last(); | ||
| 73 | } | ||
| 74 | |||
| 75 | void Bu::StreamStack::close() | ||
| 76 | { | ||
| 77 | checkStack(); | ||
| 78 | |||
| 79 | lFilts.first()->close(); | ||
| 80 | } | ||
| 81 | |||
| 82 | Bu::size Bu::StreamStack::read( void *pBuf, Bu::size nBytes ) | ||
| 83 | { | ||
| 84 | checkStack(); | ||
| 85 | |||
| 86 | return lFilts.first()->read( pBuf, nBytes ); | ||
| 87 | } | ||
| 88 | |||
| 89 | Bu::size Bu::StreamStack::write( const void *pBuf, Bu::size nBytes ) | ||
| 90 | { | ||
| 91 | checkStack(); | ||
| 92 | |||
| 93 | return lFilts.first()->write( pBuf, nBytes ); | ||
| 94 | } | ||
| 95 | |||
| 96 | Bu::size Bu::StreamStack::write( const Bu::String &sBuf ) | ||
| 97 | { | ||
| 98 | checkStack(); | ||
| 99 | |||
| 100 | return lFilts.first()->write( sBuf ); | ||
| 101 | } | ||
| 102 | |||
| 103 | Bu::size Bu::StreamStack::tell() | ||
| 104 | { | ||
| 105 | checkStack(); | ||
| 106 | |||
| 107 | return lFilts.first()->tell(); | ||
| 108 | } | ||
| 109 | |||
| 110 | void Bu::StreamStack::seek( Bu::size offset ) | ||
| 111 | { | ||
| 112 | checkStack(); | ||
| 113 | |||
| 114 | lFilts.first()->seek( offset ); | ||
| 115 | } | ||
| 116 | |||
| 117 | void Bu::StreamStack::setPos( Bu::size pos ) | ||
| 118 | { | ||
| 119 | checkStack(); | ||
| 120 | |||
| 121 | lFilts.first()->setPos( pos ); | ||
| 122 | } | ||
| 123 | |||
| 124 | void Bu::StreamStack::setPosEnd( Bu::size pos ) | ||
| 125 | { | ||
| 126 | checkStack(); | ||
| 127 | |||
| 128 | lFilts.first()->setPosEnd( pos ); | ||
| 129 | } | ||
| 130 | |||
| 131 | bool Bu::StreamStack::isEos() | ||
| 132 | { | ||
| 133 | checkStack(); | ||
| 134 | |||
| 135 | return lFilts.first()->isEos(); | ||
| 136 | } | ||
| 137 | |||
| 138 | bool Bu::StreamStack::isOpen() | ||
| 139 | { | ||
| 140 | checkStack(); | ||
| 141 | |||
| 142 | return lFilts.first()->isOpen(); | ||
| 143 | } | ||
| 144 | |||
| 145 | void Bu::StreamStack::flush() | ||
| 146 | { | ||
| 147 | checkStack(); | ||
| 148 | |||
| 149 | lFilts.first()->flush(); | ||
| 150 | } | ||
| 151 | |||
| 152 | bool Bu::StreamStack::canRead() | ||
| 153 | { | ||
| 154 | checkStack(); | ||
| 155 | |||
| 156 | return lFilts.first()->canRead(); | ||
| 157 | } | ||
| 158 | |||
| 159 | bool Bu::StreamStack::canWrite() | ||
| 160 | { | ||
| 161 | checkStack(); | ||
| 162 | |||
| 163 | return lFilts.first()->canWrite(); | ||
| 164 | } | ||
| 165 | |||
| 166 | bool Bu::StreamStack::isReadable() | ||
| 167 | { | ||
| 168 | checkStack(); | ||
| 169 | |||
| 170 | return lFilts.first()->isReadable(); | ||
| 171 | } | ||
| 172 | |||
| 173 | bool Bu::StreamStack::isWritable() | ||
| 174 | { | ||
| 175 | checkStack(); | ||
| 176 | |||
| 177 | return lFilts.first()->isWritable(); | ||
| 178 | } | ||
| 179 | |||
| 180 | bool Bu::StreamStack::isSeekable() | ||
| 181 | { | ||
| 182 | checkStack(); | ||
| 183 | |||
| 184 | return lFilts.first()->isSeekable(); | ||
| 185 | } | ||
| 186 | |||
| 187 | bool Bu::StreamStack::isBlocking() | ||
| 188 | { | ||
| 189 | checkStack(); | ||
| 190 | |||
| 191 | return lFilts.first()->isBlocking(); | ||
| 192 | } | ||
| 193 | |||
| 194 | void Bu::StreamStack::setBlocking( bool bBlocking ) | ||
| 195 | { | ||
| 196 | checkStack(); | ||
| 197 | |||
| 198 | lFilts.first()->setBlocking( bBlocking ); | ||
| 199 | } | ||
| 200 | |||
| 201 | void Bu::StreamStack::setSize( Bu::size iSize ) | ||
| 202 | { | ||
| 203 | checkStack(); | ||
| 204 | |||
| 205 | lFilts.first()->setSize( iSize ); | ||
| 206 | } | ||
| 207 | |||
| 208 | Bu::size Bu::StreamStack::getSize() const | ||
| 209 | { | ||
| 210 | checkStack(); | ||
| 211 | |||
| 212 | return lFilts.first()->getSize(); | ||
| 213 | } | ||
| 214 | |||
| 215 | Bu::size Bu::StreamStack::getBlockSize() const | ||
| 216 | { | ||
| 217 | checkStack(); | ||
| 218 | |||
| 219 | return lFilts.first()->getBlockSize(); | ||
| 220 | } | ||
| 221 | |||
| 222 | Bu::String Bu::StreamStack::getLocation() const | ||
| 223 | { | ||
| 224 | checkStack(); | ||
| 225 | |||
| 226 | return lFilts.first()->getLocation(); | ||
| 227 | } | ||
| 228 | |||
| 229 | inline void Bu::StreamStack::checkStack() const | ||
| 230 | { | ||
| 231 | if( lFilts.isEmpty() ) | ||
| 232 | throw Bu::ExceptionBase("StreamStack is empty."); | ||
| 233 | } | ||
| 234 | |||
| diff --git a/src/stable/streamstack.h b/src/stable/streamstack.h new file mode 100644 index 0000000..846935b --- /dev/null +++ b/src/stable/streamstack.h | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_STREAM_STACK_H | ||
| 9 | #define BU_STREAM_STACK_H | ||
| 10 | |||
| 11 | #include "bu/stream.h" | ||
| 12 | |||
| 13 | #include <typeinfo> | ||
| 14 | |||
| 15 | namespace Bu | ||
| 16 | { | ||
| 17 | class StreamStack : public Bu::Stream | ||
| 18 | { | ||
| 19 | private: | ||
| 20 | typedef Bu::List<Bu::Stream *> FilterList; | ||
| 21 | |||
| 22 | public: | ||
| 23 | StreamStack(); | ||
| 24 | StreamStack( Bu::Stream *pStream ); | ||
| 25 | virtual ~StreamStack(); | ||
| 26 | |||
| 27 | bool isEmpty(); | ||
| 28 | bool hasStream(); | ||
| 29 | void setStream( Bu::Stream *pStream ); | ||
| 30 | |||
| 31 | void clear(); | ||
| 32 | void popFilter(); | ||
| 33 | Bu::Stream *getTop(); | ||
| 34 | |||
| 35 | Bu::Stream *getStream(); | ||
| 36 | |||
| 37 | template<typename filter> | ||
| 38 | Bu::Stream *findFilter() | ||
| 39 | { | ||
| 40 | for( FilterList::iterator i = lFilts.begin(); i; i++ ) | ||
| 41 | { | ||
| 42 | if( typeid(**i) == typeid( filter ) ) | ||
| 43 | { | ||
| 44 | return *i; | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | throw Bu::ExceptionBase("Filter not found."); | ||
| 49 | } | ||
| 50 | |||
| 51 | template<typename filter> | ||
| 52 | void pushFilter() | ||
| 53 | { | ||
| 54 | checkStack(); | ||
| 55 | |||
| 56 | filter *pFlt = new filter( *lFilts.first() ); | ||
| 57 | lFilts.prepend( pFlt ); | ||
| 58 | } | ||
| 59 | |||
| 60 | template<typename filter, typename p1t> | ||
| 61 | void pushFilter( p1t p1 ) | ||
| 62 | { | ||
| 63 | checkStack(); | ||
| 64 | |||
| 65 | filter *pFlt = new filter( *lFilts.first(), p1 ); | ||
| 66 | lFilts.prepend( pFlt ); | ||
| 67 | } | ||
| 68 | |||
| 69 | template<typename filter, typename p1t, typename p2t> | ||
| 70 | void pushFilter( p1t p1, p2t p2 ) | ||
| 71 | { | ||
| 72 | checkStack(); | ||
| 73 | |||
| 74 | filter *pFlt = new filter( *lFilts.first(), p1, p2 ); | ||
| 75 | lFilts.prepend( pFlt ); | ||
| 76 | } | ||
| 77 | |||
| 78 | template<typename filter, typename p1t, typename p2t, typename p3t> | ||
| 79 | void pushFilter( p1t p1, p2t p2, p3t p3 ) | ||
| 80 | { | ||
| 81 | checkStack(); | ||
| 82 | |||
| 83 | filter *pFlt = new filter( *lFilts.first(), p1, p2, p3 ); | ||
| 84 | lFilts.prepend( pFlt ); | ||
| 85 | } | ||
| 86 | |||
| 87 | template<typename filter, typename p1t, typename p2t, typename p3t, | ||
| 88 | typename p4t> | ||
| 89 | void pushFilter( p1t p1, p2t p2, p3t p3, p4t p4 ) | ||
| 90 | { | ||
| 91 | checkStack(); | ||
| 92 | |||
| 93 | filter *pFlt = new filter( *lFilts.first(), p1, p2, p3, p4 ); | ||
| 94 | lFilts.prepend( pFlt ); | ||
| 95 | } | ||
| 96 | |||
| 97 | template<typename filter, typename p1t, typename p2t, typename p3t, | ||
| 98 | typename p4t, typename p5t> | ||
| 99 | void pushFilter( p1t p1, p2t p2, p3t p3, p4t p4, p5t p5 ) | ||
| 100 | { | ||
| 101 | checkStack(); | ||
| 102 | |||
| 103 | filter *pFlt = new filter( *lFilts.first(), p1, p2, p3, p4, p5 ); | ||
| 104 | lFilts.prepend( pFlt ); | ||
| 105 | } | ||
| 106 | |||
| 107 | // | ||
| 108 | // Everything below here merely passes on the call to the top of the | ||
| 109 | // stream stack. | ||
| 110 | // | ||
| 111 | |||
| 112 | virtual void close(); | ||
| 113 | virtual Bu::size read( void *pBuf, Bu::size nBytes ); | ||
| 114 | virtual Bu::size write( const void *pBuf, Bu::size nBytes ); | ||
| 115 | |||
| 116 | virtual Bu::size write( const Bu::String &sBuf ); | ||
| 117 | virtual Bu::size tell(); | ||
| 118 | virtual void seek( Bu::size offset ); | ||
| 119 | virtual void setPos( Bu::size pos ); | ||
| 120 | virtual void setPosEnd( Bu::size pos ); | ||
| 121 | virtual bool isEos(); | ||
| 122 | virtual bool isOpen(); | ||
| 123 | virtual void flush(); | ||
| 124 | virtual bool canRead(); | ||
| 125 | virtual bool canWrite(); | ||
| 126 | virtual bool isReadable(); | ||
| 127 | virtual bool isWritable(); | ||
| 128 | virtual bool isSeekable(); | ||
| 129 | virtual bool isBlocking(); | ||
| 130 | virtual void setBlocking( bool bBlocking=true ); | ||
| 131 | virtual void setSize( Bu::size iSize ); | ||
| 132 | virtual size getSize() const; | ||
| 133 | virtual size getBlockSize() const; | ||
| 134 | virtual Bu::String getLocation() const; | ||
| 135 | |||
| 136 | private: | ||
| 137 | void checkStack() const; | ||
| 138 | |||
| 139 | private: | ||
| 140 | FilterList lFilts; | ||
| 141 | }; | ||
| 142 | }; | ||
| 143 | |||
| 144 | #endif | ||
| diff --git a/src/stable/strfilter.h b/src/stable/strfilter.h new file mode 100644 index 0000000..8da0a3f --- /dev/null +++ b/src/stable/strfilter.h | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | #ifndef STR_FILTER_H | ||
| 2 | #define STR_FILTER_H | ||
| 3 | |||
| 4 | #include "bu/string.h" | ||
| 5 | #include "bu/membuf.h" | ||
| 6 | |||
| 7 | namespace Bu | ||
| 8 | { | ||
| 9 | // | ||
| 10 | // Encoders | ||
| 11 | // | ||
| 12 | template<typename tFilter> | ||
| 13 | Bu::String encodeStr( const Bu::String &sIn ) | ||
| 14 | { | ||
| 15 | Bu::MemBuf mb; | ||
| 16 | { | ||
| 17 | tFilter fEnc( mb ); | ||
| 18 | fEnc.write( sIn.getStr(), sIn.getSize() ); | ||
| 19 | } | ||
| 20 | return mb.getString(); | ||
| 21 | } | ||
| 22 | |||
| 23 | template<typename tFilter, typename p1t> | ||
| 24 | Bu::String encodeStr( const Bu::String &sIn, p1t p1 ) | ||
| 25 | { | ||
| 26 | Bu::MemBuf mb; | ||
| 27 | { | ||
| 28 | tFilter fEnc( mb, p1 ); | ||
| 29 | fEnc.write( sIn.getStr(), sIn.getSize() ); | ||
| 30 | } | ||
| 31 | return mb.getString(); | ||
| 32 | } | ||
| 33 | |||
| 34 | template<typename tFilter, typename p1t, typename p2t> | ||
| 35 | Bu::String encodeStr( const Bu::String &sIn, p1t p1, p2t p2 ) | ||
| 36 | { | ||
| 37 | Bu::MemBuf mb; | ||
| 38 | { | ||
| 39 | tFilter fEnc( mb, p1, p2 ); | ||
| 40 | fEnc.write( sIn.getStr(), sIn.getSize() ); | ||
| 41 | } | ||
| 42 | return mb.getString(); | ||
| 43 | } | ||
| 44 | |||
| 45 | template<typename tFilter, typename p1t, typename p2t, typename p3t> | ||
| 46 | Bu::String encodeStr( const Bu::String &sIn, p1t p1, p2t p2, p3t p3 ) | ||
| 47 | { | ||
| 48 | Bu::MemBuf mb; | ||
| 49 | { | ||
| 50 | tFilter fEnc( mb, p1, p2 ); | ||
| 51 | fEnc.write( sIn.getStr(), sIn.getSize() ); | ||
| 52 | } | ||
| 53 | return mb.getString(); | ||
| 54 | } | ||
| 55 | |||
| 56 | // | ||
| 57 | // Decoders | ||
| 58 | // | ||
| 59 | template<typename tFilter> | ||
| 60 | Bu::String decodeStr( const Bu::String &sIn ) | ||
| 61 | { | ||
| 62 | Bu::MemBuf mb( sIn ); | ||
| 63 | tFilter fDec( mb ); | ||
| 64 | char buf[1024]; | ||
| 65 | String sRet; | ||
| 66 | for(;;) | ||
| 67 | { | ||
| 68 | int iRead = fDec.read( buf, 1024 ); | ||
| 69 | if( iRead == 0 ) | ||
| 70 | return sRet; | ||
| 71 | sRet.append( buf, iRead ); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | template<typename tFilter, typename p1t> | ||
| 76 | Bu::String decodeStr( const Bu::String &sIn, p1t p1 ) | ||
| 77 | { | ||
| 78 | Bu::MemBuf mb( sIn ); | ||
| 79 | tFilter fDec( mb, p1 ); | ||
| 80 | char buf[1024]; | ||
| 81 | String sRet; | ||
| 82 | for(;;) | ||
| 83 | { | ||
| 84 | int iRead = fDec.read( buf, 1024 ); | ||
| 85 | if( iRead == 0 ) | ||
| 86 | return sRet; | ||
| 87 | sRet.append( buf, iRead ); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | template<typename tFilter, typename p1t, typename p2t> | ||
| 92 | Bu::String decodeStr( const Bu::String &sIn, p1t p1, p2t p2 ) | ||
| 93 | { | ||
| 94 | Bu::MemBuf mb( sIn ); | ||
| 95 | tFilter fDec( mb, p1, p2 ); | ||
| 96 | char buf[1024]; | ||
| 97 | String sRet; | ||
| 98 | for(;;) | ||
| 99 | { | ||
| 100 | int iRead = fDec.read( buf, 1024 ); | ||
| 101 | if( iRead == 0 ) | ||
| 102 | return sRet; | ||
| 103 | sRet.append( buf, iRead ); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | template<typename tFilter, typename p1t, typename p2t, typename p3t> | ||
| 108 | Bu::String decodeStr( const Bu::String &sIn, p1t p1, p2t p2, p3t p3 ) | ||
| 109 | { | ||
| 110 | Bu::MemBuf mb( sIn ); | ||
| 111 | tFilter fDec( mb, p1, p2, p3 ); | ||
| 112 | char buf[1024]; | ||
| 113 | String sRet; | ||
| 114 | for(;;) | ||
| 115 | { | ||
| 116 | int iRead = fDec.read( buf, 1024 ); | ||
| 117 | if( iRead == 0 ) | ||
| 118 | return sRet; | ||
| 119 | sRet.append( buf, iRead ); | ||
| 120 | } | ||
| 121 | } | ||
| 122 | }; | ||
| 123 | |||
| 124 | #endif | ||
| diff --git a/src/stable/string.cpp b/src/stable/string.cpp new file mode 100644 index 0000000..1a9018b --- /dev/null +++ b/src/stable/string.cpp | |||
| @@ -0,0 +1,1470 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #define BU_TRACE | ||
| 9 | #include "bu/trace.h" | ||
| 10 | |||
| 11 | #include "bu/string.h" | ||
| 12 | #include "bu/hash.h" | ||
| 13 | #include "bu/membuf.h" | ||
| 14 | #include "bu/formatter.h" | ||
| 15 | #include <stdlib.h> | ||
| 16 | |||
| 17 | #define nMinSize (256) | ||
| 18 | |||
| 19 | Bu::StringCore::StringCore() : | ||
| 20 | nLength( 0 ), | ||
| 21 | pFirst( NULL ), | ||
| 22 | pLast( NULL ) | ||
| 23 | { | ||
| 24 | } | ||
| 25 | |||
| 26 | Bu::StringCore::StringCore( const StringCore &rSrc ) : | ||
| 27 | nLength( rSrc.nLength ), | ||
| 28 | pFirst( NULL ), | ||
| 29 | pLast( NULL ) | ||
| 30 | { | ||
| 31 | if( rSrc.pFirst == NULL || rSrc.nLength == 0 ) | ||
| 32 | { | ||
| 33 | pFirst = pLast = NULL; | ||
| 34 | } | ||
| 35 | else | ||
| 36 | { | ||
| 37 | pFirst = pLast = newChunk( nLength ); | ||
| 38 | Chunk *pLink = rSrc.pFirst; | ||
| 39 | int iPos = 0; | ||
| 40 | while( pLink != NULL ) | ||
| 41 | { | ||
| 42 | memcpy( pFirst->pData+iPos, pLink->pData, pLink->nLength ); | ||
| 43 | iPos += pLink->nLength; | ||
| 44 | pLink = pLink->pNext; | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | Bu::StringCore::~StringCore() | ||
| 50 | { | ||
| 51 | clear(); | ||
| 52 | } | ||
| 53 | |||
| 54 | void Bu::StringCore::clear() const | ||
| 55 | { | ||
| 56 | if( pFirst == NULL ) | ||
| 57 | return; | ||
| 58 | |||
| 59 | Chunk *i = pFirst; | ||
| 60 | for(;;) | ||
| 61 | { | ||
| 62 | Chunk *n = i->pNext; | ||
| 63 | delete[] i->pData; | ||
| 64 | delete i; | ||
| 65 | if( n == NULL ) | ||
| 66 | break; | ||
| 67 | i = n; | ||
| 68 | } | ||
| 69 | pFirst = pLast = NULL; | ||
| 70 | nLength = 0; | ||
| 71 | } | ||
| 72 | |||
| 73 | Bu::StringCore::Chunk *Bu::StringCore::newChunk() const | ||
| 74 | { | ||
| 75 | Chunk *pNew = new Chunk; | ||
| 76 | pNew->pNext = NULL; | ||
| 77 | return pNew; | ||
| 78 | } | ||
| 79 | |||
| 80 | Bu::StringCore::Chunk *Bu::StringCore::newChunk( long nLen ) const | ||
| 81 | { | ||
| 82 | Chunk *pNew = new Chunk; | ||
| 83 | pNew->pNext = NULL; | ||
| 84 | pNew->nLength = nLen; | ||
| 85 | pNew->pData = new char[(nLen<nMinSize)?(nMinSize):(nLen)+1]; | ||
| 86 | pNew->pData[nLen] = (char)0; | ||
| 87 | return pNew; | ||
| 88 | } | ||
| 89 | |||
| 90 | Bu::StringCore::Chunk *Bu::StringCore::copyChunk( | ||
| 91 | Bu::StringCore::Chunk *pSrc ) const | ||
| 92 | { | ||
| 93 | Chunk *pNew = new Chunk; | ||
| 94 | pNew->pNext = pSrc->pNext; | ||
| 95 | pNew->nLength = pSrc->nLength; | ||
| 96 | pNew->pData = new char[ | ||
| 97 | (pNew->nLength<nMinSize)?(nMinSize):(pNew->nLength)+1 | ||
| 98 | ]; | ||
| 99 | memcpy( pNew->pData, pSrc->pData, pSrc->nLength ); | ||
| 100 | pNew->pData[pNew->nLength] = (char)0; | ||
| 101 | return pNew; | ||
| 102 | } | ||
| 103 | |||
| 104 | void Bu::StringCore::appendChunk( Bu::StringCore::Chunk *pNewChunk ) | ||
| 105 | { | ||
| 106 | if( pFirst == NULL ) | ||
| 107 | pLast = pFirst = pNewChunk; | ||
| 108 | else | ||
| 109 | { | ||
| 110 | pLast->pNext = pNewChunk; | ||
| 111 | pLast = pNewChunk; | ||
| 112 | } | ||
| 113 | |||
| 114 | nLength += pNewChunk->nLength; | ||
| 115 | } | ||
| 116 | |||
| 117 | void Bu::StringCore::prependChunk( Bu::StringCore::Chunk *pNewChunk ) | ||
| 118 | { | ||
| 119 | if( pFirst == NULL ) | ||
| 120 | pLast = pFirst = pNewChunk; | ||
| 121 | else | ||
| 122 | { | ||
| 123 | pNewChunk->pNext = pFirst; | ||
| 124 | pFirst = pNewChunk; | ||
| 125 | } | ||
| 126 | |||
| 127 | nLength += pNewChunk->nLength; | ||
| 128 | } | ||
| 129 | |||
| 130 | Bu::String::String() | ||
| 131 | { | ||
| 132 | } | ||
| 133 | |||
| 134 | Bu::String::String( const char *pData ) | ||
| 135 | { | ||
| 136 | append( pData ); | ||
| 137 | } | ||
| 138 | |||
| 139 | Bu::String::String( const char *pData, long nLength ) | ||
| 140 | { | ||
| 141 | append( pData, nLength ); | ||
| 142 | } | ||
| 143 | |||
| 144 | Bu::String::String( const Bu::String &rSrc ) : | ||
| 145 | Bu::SharedCore<Bu::String, Bu::StringCore>( rSrc ) | ||
| 146 | { | ||
| 147 | } | ||
| 148 | |||
| 149 | Bu::String::String( const Bu::String &rSrc, long nLength ) | ||
| 150 | { | ||
| 151 | append( rSrc, nLength ); | ||
| 152 | } | ||
| 153 | |||
| 154 | Bu::String::String( const Bu::String &rSrc, long nStart, long nLength ) | ||
| 155 | { | ||
| 156 | append( rSrc, nStart, nLength ); | ||
| 157 | } | ||
| 158 | |||
| 159 | Bu::String::String( long nSize ) | ||
| 160 | { | ||
| 161 | core->pFirst = core->pLast = core->newChunk( nSize ); | ||
| 162 | core->nLength = nSize; | ||
| 163 | } | ||
| 164 | |||
| 165 | Bu::String::String( const Bu::String::const_iterator &s ) | ||
| 166 | { | ||
| 167 | append( s ); | ||
| 168 | } | ||
| 169 | |||
| 170 | Bu::String::String( const Bu::String::const_iterator &s, | ||
| 171 | const Bu::String::const_iterator &e ) | ||
| 172 | { | ||
| 173 | append( s, e ); | ||
| 174 | } | ||
| 175 | |||
| 176 | Bu::String::~String() | ||
| 177 | { | ||
| 178 | } | ||
| 179 | |||
| 180 | void Bu::String::append( const char *pData ) | ||
| 181 | { | ||
| 182 | if( !pData ) return; | ||
| 183 | long nLen; | ||
| 184 | for( nLen = 0; pData[nLen] != (char)0; nLen++ ) { } | ||
| 185 | |||
| 186 | append( pData, 0, nLen ); | ||
| 187 | } | ||
| 188 | |||
| 189 | void Bu::String::append( const char *pData, long nLen ) | ||
| 190 | { | ||
| 191 | append( pData, 0, nLen ); | ||
| 192 | } | ||
| 193 | |||
| 194 | void Bu::String::append( const char *pData, long nStart, long nLen ) | ||
| 195 | { | ||
| 196 | if( !pData ) return; | ||
| 197 | if( nLen <= 0 ) | ||
| 198 | return; | ||
| 199 | |||
| 200 | pData += nStart; | ||
| 201 | |||
| 202 | _hardCopy(); | ||
| 203 | |||
| 204 | if( core->pLast && core->pLast->nLength < nMinSize ) | ||
| 205 | { | ||
| 206 | int nAmnt = nMinSize - core->pLast->nLength; | ||
| 207 | if( nAmnt > nLen ) | ||
| 208 | nAmnt = nLen; | ||
| 209 | memcpy( | ||
| 210 | core->pLast->pData+core->pLast->nLength, | ||
| 211 | pData, | ||
| 212 | nAmnt | ||
| 213 | ); | ||
| 214 | pData += nAmnt; | ||
| 215 | core->pLast->nLength += nAmnt; | ||
| 216 | nLen -= nAmnt; | ||
| 217 | core->nLength += nAmnt; | ||
| 218 | } | ||
| 219 | |||
| 220 | if( nLen > 0 ) | ||
| 221 | { | ||
| 222 | Chunk *pNew = core->newChunk( nLen ); | ||
| 223 | memcpy( pNew->pData, pData, nLen ); | ||
| 224 | core->appendChunk( pNew ); | ||
| 225 | // core->nLength += nLen; | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | void Bu::String::append( const char &cData ) | ||
| 230 | { | ||
| 231 | if( core->pLast && core->pLast->nLength < nMinSize ) | ||
| 232 | { | ||
| 233 | _hardCopy(); | ||
| 234 | core->pLast->pData[core->pLast->nLength] = cData; | ||
| 235 | ++core->pLast->nLength; ++core->nLength; | ||
| 236 | // pLast->pData[pLast->nLength] = (char)0; | ||
| 237 | } | ||
| 238 | else | ||
| 239 | { | ||
| 240 | append( &cData, 1 ); | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | void Bu::String::append( const String & sData ) | ||
| 245 | { | ||
| 246 | append( sData.getStr(), 0, sData.getSize() ); | ||
| 247 | } | ||
| 248 | |||
| 249 | void Bu::String::append( const String & sData, long nLen ) | ||
| 250 | { | ||
| 251 | append( sData.getStr(), 0, nLen ); | ||
| 252 | } | ||
| 253 | |||
| 254 | void Bu::String::append( const String & sData, long nStart, long nLen ) | ||
| 255 | { | ||
| 256 | if( nLen < 0 ) | ||
| 257 | nLen = sData.getSize() - nStart; | ||
| 258 | append( sData.getStr(), nStart, nLen ); | ||
| 259 | } | ||
| 260 | |||
| 261 | void Bu::String::append( const const_iterator &s ) | ||
| 262 | { | ||
| 263 | if( !s.isValid() ) | ||
| 264 | return; | ||
| 265 | Chunk *pSrc = s.pChunk; | ||
| 266 | |||
| 267 | Chunk *pNew = core->newChunk( pSrc->nLength-s.iPos ); | ||
| 268 | memcpy( pNew->pData, pSrc->pData+s.iPos, pSrc->nLength-s.iPos ); | ||
| 269 | |||
| 270 | _hardCopy(); | ||
| 271 | core->appendChunk( pNew ); | ||
| 272 | |||
| 273 | while( (pSrc = pSrc->pNext) ) | ||
| 274 | { | ||
| 275 | core->appendChunk( core->copyChunk( pSrc ) ); | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | void Bu::String::append( const iterator &s ) | ||
| 280 | { | ||
| 281 | append( const_iterator( s ) ); | ||
| 282 | } | ||
| 283 | |||
| 284 | void Bu::String::append( const const_iterator &s, const const_iterator &e ) | ||
| 285 | { | ||
| 286 | if( !s.isValid() ) | ||
| 287 | return; | ||
| 288 | if( !e.isValid() ) | ||
| 289 | { | ||
| 290 | append( s ); | ||
| 291 | return; | ||
| 292 | } | ||
| 293 | _hardCopy(); | ||
| 294 | if( s.pChunk == e.pChunk ) | ||
| 295 | { | ||
| 296 | // Simple case, they're the same chunk | ||
| 297 | Chunk *pNew = core->newChunk( e.iPos-s.iPos ); | ||
| 298 | memcpy( pNew->pData, s.pChunk->pData+s.iPos, e.iPos-s.iPos ); | ||
| 299 | core->appendChunk( pNew ); | ||
| 300 | } | ||
| 301 | else | ||
| 302 | { | ||
| 303 | // A little trickier, scan the blocks... | ||
| 304 | Chunk *pSrc = s.pChunk; | ||
| 305 | Chunk *pNew = core->newChunk( pSrc->nLength-s.iPos ); | ||
| 306 | memcpy( pNew->pData, pSrc->pData+s.iPos, pSrc->nLength-s.iPos ); | ||
| 307 | core->appendChunk( pNew ); | ||
| 308 | |||
| 309 | while( (pSrc = pSrc->pNext) != e.pChunk ) | ||
| 310 | { | ||
| 311 | core->appendChunk( core->copyChunk( pSrc ) ); | ||
| 312 | } | ||
| 313 | |||
| 314 | pNew = core->newChunk( e.iPos ); | ||
| 315 | memcpy( pNew->pData, pSrc->pData, e.iPos ); | ||
| 316 | core->appendChunk( pNew ); | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | void Bu::String::prepend( const String & sData ) | ||
| 321 | { | ||
| 322 | prepend( sData.getStr(), sData.getSize() ); | ||
| 323 | } | ||
| 324 | |||
| 325 | void Bu::String::prepend( const char *pData ) | ||
| 326 | { | ||
| 327 | if( pData == NULL ) | ||
| 328 | return; | ||
| 329 | |||
| 330 | _hardCopy(); | ||
| 331 | long nLen; | ||
| 332 | for( nLen = 0; pData[nLen] != (char)0; nLen++ ) { } | ||
| 333 | |||
| 334 | Chunk *pNew = core->newChunk( nLen ); | ||
| 335 | memcpy( pNew->pData, pData, nLen ); | ||
| 336 | |||
| 337 | core->prependChunk( pNew ); | ||
| 338 | } | ||
| 339 | |||
| 340 | void Bu::String::prepend( const char *pData, long nLen ) | ||
| 341 | { | ||
| 342 | Chunk *pNew = core->newChunk( nLen ); | ||
| 343 | |||
| 344 | memcpy( pNew->pData, pData, nLen ); | ||
| 345 | |||
| 346 | _hardCopy(); | ||
| 347 | core->prependChunk( pNew ); | ||
| 348 | } | ||
| 349 | |||
| 350 | void Bu::String::prepend( const char c ) | ||
| 351 | { | ||
| 352 | prepend( &c, 1 ); | ||
| 353 | } | ||
| 354 | |||
| 355 | void Bu::String::insert( long nPos, const char *pData, long nLen ) | ||
| 356 | { | ||
| 357 | if( nLen <= 0 ) | ||
| 358 | return; | ||
| 359 | if( nPos <= 0 ) | ||
| 360 | { | ||
| 361 | prepend( pData, nLen ); | ||
| 362 | } | ||
| 363 | else if( nPos >= core->nLength ) | ||
| 364 | { | ||
| 365 | append( pData, nLen ); | ||
| 366 | } | ||
| 367 | else | ||
| 368 | { | ||
| 369 | // If we're going to flatten anyway, might as well for everyone | ||
| 370 | flatten(); | ||
| 371 | _hardCopy(); | ||
| 372 | Chunk *p1 = core->newChunk( nPos ); | ||
| 373 | Chunk *p2 = core->newChunk( nLen ); | ||
| 374 | Chunk *p3 = core->newChunk( core->nLength-nPos ); | ||
| 375 | memcpy( p1->pData, core->pFirst->pData, nPos ); | ||
| 376 | memcpy( p2->pData, pData, nLen ); | ||
| 377 | memcpy( p3->pData, core->pFirst->pData+nPos, core->nLength-nPos ); | ||
| 378 | core->clear(); | ||
| 379 | core->appendChunk( p1 ); | ||
| 380 | core->appendChunk( p2 ); | ||
| 381 | core->appendChunk( p3 ); | ||
| 382 | } | ||
| 383 | } | ||
| 384 | |||
| 385 | void Bu::String::insert( long nPos, const String &str ) | ||
| 386 | { | ||
| 387 | if( nPos <= 0 ) | ||
| 388 | { | ||
| 389 | prepend( str ); | ||
| 390 | } | ||
| 391 | else if( nPos >= core->nLength ) | ||
| 392 | { | ||
| 393 | append( str ); | ||
| 394 | } | ||
| 395 | else | ||
| 396 | { | ||
| 397 | flatten(); | ||
| 398 | _hardCopy(); | ||
| 399 | Chunk *p1 = core->newChunk( nPos ); | ||
| 400 | Chunk *p3 = core->newChunk( core->nLength-nPos ); | ||
| 401 | memcpy( p1->pData, core->pFirst->pData, nPos ); | ||
| 402 | memcpy( p3->pData, core->pFirst->pData+nPos, core->nLength-nPos ); | ||
| 403 | core->clear(); | ||
| 404 | core->appendChunk( p1 ); | ||
| 405 | for( Chunk *pChnk = str.core->pFirst; pChnk; | ||
| 406 | pChnk = pChnk->pNext ) | ||
| 407 | { | ||
| 408 | core->appendChunk( core->copyChunk( pChnk ) ); | ||
| 409 | } | ||
| 410 | |||
| 411 | core->appendChunk( p3 ); | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | void Bu::String::insert( long nPos, const char *pData ) | ||
| 416 | { | ||
| 417 | insert( nPos, pData, strlen( pData ) ); | ||
| 418 | } | ||
| 419 | |||
| 420 | void Bu::String::remove( long nPos, long nLen ) | ||
| 421 | { | ||
| 422 | if( nLen <= 0 || nPos < 0 || nPos >= core->nLength ) | ||
| 423 | return; | ||
| 424 | if( nLen > core->nLength-nPos ) | ||
| 425 | nLen = core->nLength-nPos; | ||
| 426 | flatten(); | ||
| 427 | _hardCopy(); | ||
| 428 | memmove( core->pFirst->pData+nPos, core->pFirst->pData+nPos+nLen, core->nLength-nPos-nLen+1 ); | ||
| 429 | core->nLength -= nLen; | ||
| 430 | core->pFirst->nLength -= nLen; | ||
| 431 | } | ||
| 432 | |||
| 433 | void Bu::String::clear() | ||
| 434 | { | ||
| 435 | _hardCopy(); | ||
| 436 | core->clear(); | ||
| 437 | } | ||
| 438 | |||
| 439 | Bu::String Bu::String::replace( const Bu::String &fnd, | ||
| 440 | const Bu::String &rep ) const | ||
| 441 | { | ||
| 442 | String out; | ||
| 443 | const_iterator o = begin(); | ||
| 444 | while( true ) | ||
| 445 | { | ||
| 446 | const_iterator i = o.find( fnd, fnd.getSize() ); | ||
| 447 | if( !i ) | ||
| 448 | { | ||
| 449 | out.append( o ); | ||
| 450 | return out; | ||
| 451 | } | ||
| 452 | else | ||
| 453 | { | ||
| 454 | out.append( o, i ); | ||
| 455 | out.append( rep ); | ||
| 456 | o = i; | ||
| 457 | o += fnd.getSize(); | ||
| 458 | } | ||
| 459 | } | ||
| 460 | } | ||
| 461 | |||
| 462 | void Bu::String::resize( long nNewSize ) | ||
| 463 | { | ||
| 464 | if( core->nLength == nNewSize ) | ||
| 465 | return; | ||
| 466 | if( nNewSize < 0 ) | ||
| 467 | nNewSize = 0; | ||
| 468 | |||
| 469 | flatten(); | ||
| 470 | _hardCopy(); | ||
| 471 | |||
| 472 | // TODO: This is bad | ||
| 473 | |||
| 474 | Chunk *pNew = core->newChunk( nNewSize ); | ||
| 475 | long nNewLen = (nNewSize<core->nLength)?(nNewSize):(core->nLength); | ||
| 476 | if( core->nLength > 0 ) | ||
| 477 | { | ||
| 478 | memcpy( pNew->pData, core->pFirst->pData, nNewLen ); | ||
| 479 | delete[] core->pFirst->pData; | ||
| 480 | delete core->pFirst; | ||
| 481 | } | ||
| 482 | pNew->pData[nNewLen] = (char)0; | ||
| 483 | core->pFirst = core->pLast = pNew; | ||
| 484 | core->nLength = nNewSize; | ||
| 485 | } | ||
| 486 | |||
| 487 | long Bu::String::getSize() const | ||
| 488 | { | ||
| 489 | return core->nLength; | ||
| 490 | } | ||
| 491 | |||
| 492 | char *Bu::String::getStr() | ||
| 493 | { | ||
| 494 | if( core->pFirst == NULL || core->nLength == 0 ) | ||
| 495 | return (char *)""; | ||
| 496 | |||
| 497 | flatten(); | ||
| 498 | _hardCopy(); | ||
| 499 | core->pFirst->pData[core->nLength] = (char)0; | ||
| 500 | return core->pFirst->pData; | ||
| 501 | } | ||
| 502 | |||
| 503 | const char *Bu::String::getStr() const | ||
| 504 | { | ||
| 505 | if( core->pFirst == NULL || core->nLength == 0 ) | ||
| 506 | return (char *)""; | ||
| 507 | |||
| 508 | flatten(); | ||
| 509 | core->pFirst->pData[core->nLength] = (char)0; | ||
| 510 | return core->pFirst->pData; | ||
| 511 | } | ||
| 512 | |||
| 513 | const char *Bu::String::getConstStr() const | ||
| 514 | { | ||
| 515 | return getStr(); | ||
| 516 | } | ||
| 517 | |||
| 518 | Bu::String Bu::String::getSubStrIdx( long iStart, long iSize ) const | ||
| 519 | { | ||
| 520 | if( iStart < 0 ) | ||
| 521 | iStart = 0; | ||
| 522 | if( iStart >= core->nLength ) | ||
| 523 | return (const char[]){(char)0}; | ||
| 524 | if( iSize < 0 ) | ||
| 525 | iSize = core->nLength; | ||
| 526 | if( iStart+iSize > core->nLength ) | ||
| 527 | iSize = core->nLength-iStart; | ||
| 528 | if( iSize == 0 ) | ||
| 529 | return (const char[]){(char)0}; | ||
| 530 | |||
| 531 | flatten(); | ||
| 532 | String ret( core->pFirst->pData+iStart, iSize ); | ||
| 533 | return ret; | ||
| 534 | } | ||
| 535 | |||
| 536 | Bu::String Bu::String::getSubStr( const_iterator iBegin, | ||
| 537 | const_iterator iEnd ) const | ||
| 538 | { | ||
| 539 | if( !iBegin.isValid() ) | ||
| 540 | return String(); | ||
| 541 | if( iBegin.pChunk == iEnd.pChunk ) | ||
| 542 | { | ||
| 543 | return String( iBegin.pChunk->pData+iBegin.iPos, | ||
| 544 | iEnd.iPos-iBegin.iPos ); | ||
| 545 | } | ||
| 546 | else if( !iEnd.isValid() ) | ||
| 547 | { | ||
| 548 | String ret; | ||
| 549 | ret.append( | ||
| 550 | iBegin.pChunk->pData+iBegin.iPos, | ||
| 551 | iBegin.pChunk->nLength-iBegin.iPos | ||
| 552 | ); | ||
| 553 | for( Chunk *pCur = iBegin.pChunk->pNext; | ||
| 554 | pCur; pCur = pCur->pNext ) | ||
| 555 | { | ||
| 556 | ret.append( pCur->pData, pCur->nLength ); | ||
| 557 | } | ||
| 558 | return ret; | ||
| 559 | } | ||
| 560 | else | ||
| 561 | { | ||
| 562 | String ret; | ||
| 563 | ret.append( | ||
| 564 | iBegin.pChunk->pData+iBegin.iPos, | ||
| 565 | iBegin.pChunk->nLength-iBegin.iPos | ||
| 566 | ); | ||
| 567 | for( Chunk *pCur = iBegin.pChunk->pNext; | ||
| 568 | pCur != iEnd.pChunk; pCur = pCur->pNext ) | ||
| 569 | { | ||
| 570 | ret.append( pCur->pData, pCur->nLength ); | ||
| 571 | } | ||
| 572 | ret.append( | ||
| 573 | iEnd.pChunk->pData, | ||
| 574 | iEnd.iPos | ||
| 575 | ); | ||
| 576 | return ret; | ||
| 577 | } | ||
| 578 | } | ||
| 579 | |||
| 580 | Bu::StringList Bu::String::split( const char c ) const | ||
| 581 | { | ||
| 582 | Bu::StringList ret; | ||
| 583 | const_iterator l, r; | ||
| 584 | l = begin(); | ||
| 585 | for(r=l; l;) | ||
| 586 | { | ||
| 587 | for( r = l; r && r != c; r++ ) { } | ||
| 588 | ret.append( String( l, r ) ); | ||
| 589 | l = r; | ||
| 590 | l++; | ||
| 591 | } | ||
| 592 | return ret; | ||
| 593 | } | ||
| 594 | |||
| 595 | Bu::String &Bu::String::operator+=( const char *pData ) | ||
| 596 | { | ||
| 597 | append( pData ); | ||
| 598 | |||
| 599 | return (*this); | ||
| 600 | } | ||
| 601 | |||
| 602 | Bu::String &Bu::String::operator+=( const Bu::String &rSrc ) | ||
| 603 | { | ||
| 604 | append( rSrc ); | ||
| 605 | |||
| 606 | return (*this); | ||
| 607 | } | ||
| 608 | |||
| 609 | Bu::String &Bu::String::operator+=( const Bu::String::const_iterator &i ) | ||
| 610 | { | ||
| 611 | append( i, i+1 ); | ||
| 612 | |||
| 613 | return (*this); | ||
| 614 | } | ||
| 615 | |||
| 616 | Bu::String &Bu::String::operator+=( const char cData ) | ||
| 617 | { | ||
| 618 | if( core->pLast && core->pLast->nLength < nMinSize ) | ||
| 619 | { | ||
| 620 | _hardCopy(); | ||
| 621 | core->pLast->pData[core->pLast->nLength] = cData; | ||
| 622 | ++core->pLast->nLength; ++core->nLength; | ||
| 623 | // pLast->pData[pLast->nLength] = (char)0; | ||
| 624 | } | ||
| 625 | else | ||
| 626 | { | ||
| 627 | append( &cData, 1 ); | ||
| 628 | } | ||
| 629 | //append( pData ); | ||
| 630 | |||
| 631 | return (*this); | ||
| 632 | } | ||
| 633 | |||
| 634 | Bu::String &Bu::String::operator=( const char *pData ) | ||
| 635 | { | ||
| 636 | set( pData ); | ||
| 637 | |||
| 638 | return (*this); | ||
| 639 | } | ||
| 640 | |||
| 641 | Bu::String Bu::String::operator+( const Bu::String &rRight ) const | ||
| 642 | { | ||
| 643 | String ret( *this ); | ||
| 644 | ret.append( rRight ); | ||
| 645 | return ret; | ||
| 646 | } | ||
| 647 | |||
| 648 | Bu::String Bu::String::operator+( const char *pRight ) const | ||
| 649 | { | ||
| 650 | String ret( *this ); | ||
| 651 | ret.append( pRight ); | ||
| 652 | return ret; | ||
| 653 | } | ||
| 654 | |||
| 655 | Bu::String Bu::String::operator+( char *pRight ) const | ||
| 656 | { | ||
| 657 | String ret( *this ); | ||
| 658 | ret.append( pRight ); | ||
| 659 | return ret; | ||
| 660 | } | ||
| 661 | |||
| 662 | void Bu::String::set( const char *pData ) | ||
| 663 | { | ||
| 664 | clear(); | ||
| 665 | append( pData ); | ||
| 666 | } | ||
| 667 | |||
| 668 | void Bu::String::set( const char *pData, long nSize ) | ||
| 669 | { | ||
| 670 | clear(); | ||
| 671 | append( pData, nSize ); | ||
| 672 | } | ||
| 673 | |||
| 674 | void Bu::String::set( const char *pData, long nStart, long nSize ) | ||
| 675 | { | ||
| 676 | clear(); | ||
| 677 | append( pData, nStart, nSize ); | ||
| 678 | } | ||
| 679 | |||
| 680 | void Bu::String::set( const String &rData ) | ||
| 681 | { | ||
| 682 | clear(); | ||
| 683 | append( rData ); | ||
| 684 | } | ||
| 685 | |||
| 686 | void Bu::String::set( const String &rData, long nSize ) | ||
| 687 | { | ||
| 688 | clear(); | ||
| 689 | append( rData, nSize ); | ||
| 690 | } | ||
| 691 | |||
| 692 | void Bu::String::set( const String &rData, long nStart, long nSize ) | ||
| 693 | { | ||
| 694 | clear(); | ||
| 695 | append( rData, nStart, nSize ); | ||
| 696 | } | ||
| 697 | |||
| 698 | void Bu::String::set( const_iterator s ) | ||
| 699 | { | ||
| 700 | clear(); | ||
| 701 | append( s ); | ||
| 702 | } | ||
| 703 | |||
| 704 | void Bu::String::set( const_iterator s, const_iterator e ) | ||
| 705 | { | ||
| 706 | clear(); | ||
| 707 | append( s, e ); | ||
| 708 | } | ||
| 709 | |||
| 710 | void Bu::String::setSize( long iSize ) | ||
| 711 | { | ||
| 712 | _hardCopy(); | ||
| 713 | core->clear(); | ||
| 714 | core->appendChunk( core->newChunk( iSize ) ); | ||
| 715 | } | ||
| 716 | |||
| 717 | bool Bu::String::operator==( const char *pData ) const | ||
| 718 | { | ||
| 719 | if( core->pFirst == NULL || core->nLength == 0 ) { | ||
| 720 | if( pData == NULL ) | ||
| 721 | return true; | ||
| 722 | if( pData[0] == (char)0 ) | ||
| 723 | return true; | ||
| 724 | return false; | ||
| 725 | } | ||
| 726 | |||
| 727 | flatten(); | ||
| 728 | core->pFirst->pData[core->nLength] = (char)0; | ||
| 729 | const char *a = pData; | ||
| 730 | char *b = core->pFirst->pData; | ||
| 731 | for( long j = 0; *a!=(char)0 || *b!=(char)0; j++, a++, b++ ) | ||
| 732 | { | ||
| 733 | if( *a != *b ) | ||
| 734 | return false; | ||
| 735 | if( *a == (char)0 && j < core->nLength ) | ||
| 736 | return false; | ||
| 737 | } | ||
| 738 | |||
| 739 | return true; | ||
| 740 | } | ||
| 741 | |||
| 742 | bool Bu::String::operator==( const String &pData ) const | ||
| 743 | { | ||
| 744 | if( core == pData.core ) | ||
| 745 | return true; | ||
| 746 | if( core->pFirst == pData.core->pFirst ) | ||
| 747 | return true; | ||
| 748 | if( (core->nLength == 0 && pData.core->nLength == 0) ) | ||
| 749 | return true; | ||
| 750 | if( core->nLength != pData.core->nLength ) | ||
| 751 | return false; | ||
| 752 | if( pData.core->pFirst == NULL || core->pFirst == NULL ) | ||
| 753 | return false; | ||
| 754 | |||
| 755 | flatten(); | ||
| 756 | pData.flatten(); | ||
| 757 | const char *a = pData.core->pFirst->pData; | ||
| 758 | char *b = core->pFirst->pData; | ||
| 759 | for( long j = 0; j < core->nLength; j++, a++, b++ ) | ||
| 760 | { | ||
| 761 | if( *a != *b ) | ||
| 762 | return false; | ||
| 763 | } | ||
| 764 | |||
| 765 | return true; | ||
| 766 | } | ||
| 767 | |||
| 768 | bool Bu::String::operator!=(const char *pData ) const | ||
| 769 | { | ||
| 770 | return !(*this == pData); | ||
| 771 | } | ||
| 772 | |||
| 773 | bool Bu::String::operator!=(const String &pData ) const | ||
| 774 | { | ||
| 775 | return !(*this == pData); | ||
| 776 | } | ||
| 777 | |||
| 778 | bool Bu::String::operator<(const String &pData ) const | ||
| 779 | { | ||
| 780 | flatten(); | ||
| 781 | pData.flatten(); | ||
| 782 | |||
| 783 | const char *a = core->pFirst->pData; | ||
| 784 | char *b = pData.core->pFirst->pData; | ||
| 785 | for( long j = 0; j < core->nLength; j++, a++, b++ ) | ||
| 786 | { | ||
| 787 | if( *a != *b ) | ||
| 788 | return *a < *b; | ||
| 789 | } | ||
| 790 | |||
| 791 | return false; | ||
| 792 | } | ||
| 793 | |||
| 794 | bool Bu::String::operator<=(const String &pData ) const | ||
| 795 | { | ||
| 796 | flatten(); | ||
| 797 | pData.flatten(); | ||
| 798 | |||
| 799 | const char *a = core->pFirst->pData; | ||
| 800 | char *b = pData.core->pFirst->pData; | ||
| 801 | for( long j = 0; j < core->nLength; j++, a++, b++ ) | ||
| 802 | { | ||
| 803 | if( *a != *b ) | ||
| 804 | return *a < *b; | ||
| 805 | } | ||
| 806 | |||
| 807 | return true; | ||
| 808 | } | ||
| 809 | |||
| 810 | bool Bu::String::operator>(const String &pData ) const | ||
| 811 | { | ||
| 812 | flatten(); | ||
| 813 | pData.flatten(); | ||
| 814 | |||
| 815 | const char *a = core->pFirst->pData; | ||
| 816 | char *b = pData.core->pFirst->pData; | ||
| 817 | for( long j = 0; j < core->nLength; j++, a++, b++ ) | ||
| 818 | { | ||
| 819 | if( *a != *b ) | ||
| 820 | return *a > *b; | ||
| 821 | } | ||
| 822 | |||
| 823 | return false; | ||
| 824 | } | ||
| 825 | |||
| 826 | bool Bu::String::operator>=(const String &pData ) const | ||
| 827 | { | ||
| 828 | flatten(); | ||
| 829 | pData.flatten(); | ||
| 830 | |||
| 831 | const char *a = core->pFirst->pData; | ||
| 832 | char *b = pData.core->pFirst->pData; | ||
| 833 | for( long j = 0; j < core->nLength; j++, a++, b++ ) | ||
| 834 | { | ||
| 835 | if( *a != *b ) | ||
| 836 | return *a > *b; | ||
| 837 | } | ||
| 838 | |||
| 839 | return true; | ||
| 840 | } | ||
| 841 | |||
| 842 | char &Bu::String::operator[]( long nIndex ) | ||
| 843 | { | ||
| 844 | if( nIndex < 0 || nIndex >= core->nLength ) | ||
| 845 | throw Bu::ExceptionBase("Index out of range."); | ||
| 846 | flatten(); | ||
| 847 | _hardCopy(); | ||
| 848 | |||
| 849 | return core->pFirst->pData[nIndex]; | ||
| 850 | } | ||
| 851 | |||
| 852 | const char &Bu::String::operator[]( long nIndex ) const | ||
| 853 | { | ||
| 854 | if( nIndex < 0 || nIndex >= core->nLength ) | ||
| 855 | throw Bu::ExceptionBase("Index out of range."); | ||
| 856 | flatten(); | ||
| 857 | |||
| 858 | return core->pFirst->pData[nIndex]; | ||
| 859 | } | ||
| 860 | |||
| 861 | bool Bu::String::isSet() const | ||
| 862 | { | ||
| 863 | return (core->pFirst != NULL); | ||
| 864 | } | ||
| 865 | |||
| 866 | bool Bu::String::compareSub( const char *pData, long nIndex, long nLen ) const | ||
| 867 | { | ||
| 868 | if( core->pFirst == NULL || core->nLength == 0 ) { | ||
| 869 | if( pData == NULL ) | ||
| 870 | return true; | ||
| 871 | if( nLen == 0 ) | ||
| 872 | return true; | ||
| 873 | if( pData[0] == (char)0 ) | ||
| 874 | return true; | ||
| 875 | return false; | ||
| 876 | } | ||
| 877 | if( nIndex+nLen > core->nLength ) | ||
| 878 | return false; | ||
| 879 | |||
| 880 | flatten(); | ||
| 881 | core->pFirst->pData[core->nLength] = (char)0; | ||
| 882 | const char *a = pData; | ||
| 883 | char *b = core->pFirst->pData+nIndex; | ||
| 884 | for( long j = 0; j < nLen; j++, a++, b++ ) | ||
| 885 | { | ||
| 886 | if( *a != *b ) | ||
| 887 | return false; | ||
| 888 | if( *a == (char)0 && j < core->nLength ) | ||
| 889 | return false; | ||
| 890 | } | ||
| 891 | |||
| 892 | return true; | ||
| 893 | } | ||
| 894 | |||
| 895 | bool Bu::String::compareSub( const String &rData, long nIndex, long nLen ) const | ||
| 896 | { | ||
| 897 | if( core->pFirst == NULL || core->nLength == 0 || rData.core->pFirst == NULL || rData.core->nLength == 0 ) | ||
| 898 | return false; | ||
| 899 | if( nLen < 0 ) | ||
| 900 | nLen = rData.core->nLength; | ||
| 901 | if( nIndex+nLen > core->nLength ) | ||
| 902 | return false; | ||
| 903 | |||
| 904 | flatten(); | ||
| 905 | rData.flatten(); | ||
| 906 | const char *a = rData.core->pFirst->pData; | ||
| 907 | char *b = core->pFirst->pData + nIndex; | ||
| 908 | for( long j = 0; j < nLen; j++, a++, b++ ) | ||
| 909 | { | ||
| 910 | if( *a != *b ) | ||
| 911 | return false; | ||
| 912 | } | ||
| 913 | |||
| 914 | return true; | ||
| 915 | } | ||
| 916 | |||
| 917 | bool Bu::String::isWS( long nIndex ) const | ||
| 918 | { | ||
| 919 | flatten(); | ||
| 920 | |||
| 921 | return core->pFirst->pData[nIndex]==' ' || core->pFirst->pData[nIndex]=='\t' | ||
| 922 | || core->pFirst->pData[nIndex]=='\r' || core->pFirst->pData[nIndex]=='\n'; | ||
| 923 | } | ||
| 924 | |||
| 925 | bool Bu::String::isAlpha( long nIndex ) const | ||
| 926 | { | ||
| 927 | flatten(); | ||
| 928 | |||
| 929 | return (core->pFirst->pData[nIndex] >= 'a' && core->pFirst->pData[nIndex] <= 'z') | ||
| 930 | || (core->pFirst->pData[nIndex] >= 'A' && core->pFirst->pData[nIndex] <= 'Z'); | ||
| 931 | } | ||
| 932 | |||
| 933 | Bu::String Bu::String::toLower() const | ||
| 934 | { | ||
| 935 | Bu::String sRet = *this; | ||
| 936 | |||
| 937 | sRet.flatten(); | ||
| 938 | sRet._hardCopy(); | ||
| 939 | |||
| 940 | for( long j = 0; j < sRet.core->nLength; j++ ) | ||
| 941 | { | ||
| 942 | if( sRet.core->pFirst->pData[j] >= 'A' && | ||
| 943 | sRet.core->pFirst->pData[j] <= 'Z' ) | ||
| 944 | sRet.core->pFirst->pData[j] -= 'A'-'a'; | ||
| 945 | } | ||
| 946 | |||
| 947 | return sRet; | ||
| 948 | } | ||
| 949 | |||
| 950 | Bu::String Bu::String::toUpper() const | ||
| 951 | { | ||
| 952 | Bu::String sRet = *this; | ||
| 953 | |||
| 954 | sRet.flatten(); | ||
| 955 | sRet._hardCopy(); | ||
| 956 | |||
| 957 | for( long j = 0; j < sRet.core->nLength; j++ ) | ||
| 958 | { | ||
| 959 | if( sRet.core->pFirst->pData[j] >= 'a' && | ||
| 960 | sRet.core->pFirst->pData[j] <= 'z' ) | ||
| 961 | sRet.core->pFirst->pData[j] += 'A'-'a'; | ||
| 962 | } | ||
| 963 | |||
| 964 | return sRet; | ||
| 965 | } | ||
| 966 | |||
| 967 | Bu::String::const_iterator Bu::String::find( const char cChar, | ||
| 968 | Bu::String::const_iterator iStart ) const | ||
| 969 | { | ||
| 970 | if( !iStart ) iStart = begin(); | ||
| 971 | for( ; iStart; iStart++ ) | ||
| 972 | { | ||
| 973 | if( cChar == *iStart ) | ||
| 974 | return iStart; | ||
| 975 | } | ||
| 976 | return end(); | ||
| 977 | } | ||
| 978 | |||
| 979 | Bu::String::const_iterator Bu::String::find( const char *sText, int nLen, | ||
| 980 | Bu::String::const_iterator iStart ) const | ||
| 981 | { | ||
| 982 | if( !iStart ) iStart = begin(); | ||
| 983 | for( ; iStart; iStart++ ) | ||
| 984 | { | ||
| 985 | if( iStart.compare( sText, nLen ) ) | ||
| 986 | return iStart; | ||
| 987 | } | ||
| 988 | return end(); | ||
| 989 | } | ||
| 990 | |||
| 991 | Bu::String::const_iterator Bu::String::find( const String &rStr, | ||
| 992 | Bu::String::const_iterator iStart ) const | ||
| 993 | { | ||
| 994 | if( !iStart ) iStart = begin(); | ||
| 995 | for( ; iStart; iStart++ ) | ||
| 996 | { | ||
| 997 | if( iStart.compare( rStr ) ) | ||
| 998 | return iStart; | ||
| 999 | } | ||
| 1000 | return end(); | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | Bu::String::const_iterator Bu::String::find( const String &rStr, int nLen, | ||
| 1004 | Bu::String::const_iterator iStart ) const | ||
| 1005 | { | ||
| 1006 | if( !iStart ) iStart = begin(); | ||
| 1007 | for( ; iStart; iStart++ ) | ||
| 1008 | { | ||
| 1009 | if( iStart.compare( rStr, nLen ) ) | ||
| 1010 | return iStart; | ||
| 1011 | } | ||
| 1012 | return end(); | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | Bu::String::iterator Bu::String::find( const char cChar, | ||
| 1016 | Bu::String::const_iterator iStart ) | ||
| 1017 | { | ||
| 1018 | if( !iStart ) iStart = begin(); | ||
| 1019 | for( ; iStart; iStart++ ) | ||
| 1020 | { | ||
| 1021 | if( cChar == *iStart ) | ||
| 1022 | return iterator( iStart.pChunk, iStart.iPos ); | ||
| 1023 | } | ||
| 1024 | return end(); | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | Bu::String::iterator Bu::String::find( const char *sText, int nLen, | ||
| 1028 | Bu::String::const_iterator iStart ) | ||
| 1029 | { | ||
| 1030 | if( !iStart ) iStart = begin(); | ||
| 1031 | for( ; iStart; iStart++ ) | ||
| 1032 | { | ||
| 1033 | if( iStart.compare( sText, nLen ) ) | ||
| 1034 | return iterator( iStart.pChunk, iStart.iPos ); | ||
| 1035 | } | ||
| 1036 | return end(); | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | Bu::String::iterator Bu::String::find( const String &rStr, | ||
| 1040 | Bu::String::const_iterator iStart ) | ||
| 1041 | { | ||
| 1042 | if( !iStart ) iStart = begin(); | ||
| 1043 | for( ; iStart; iStart++ ) | ||
| 1044 | { | ||
| 1045 | if( iStart.compare( rStr ) ) | ||
| 1046 | return iterator( iStart.pChunk, iStart.iPos ); | ||
| 1047 | } | ||
| 1048 | return end(); | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | Bu::String::iterator Bu::String::find( const String &rStr, int nLen, | ||
| 1052 | Bu::String::const_iterator iStart ) | ||
| 1053 | { | ||
| 1054 | if( !iStart ) iStart = begin(); | ||
| 1055 | for( ; iStart; iStart++ ) | ||
| 1056 | { | ||
| 1057 | if( iStart.compare( rStr, nLen ) ) | ||
| 1058 | return iterator( iStart.pChunk, iStart.iPos ); | ||
| 1059 | } | ||
| 1060 | return end(); | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | long Bu::String::findIdx( const char cChar, long iStart ) const | ||
| 1064 | { | ||
| 1065 | flatten(); | ||
| 1066 | for( long j = iStart; j < core->pFirst->nLength; j++ ) | ||
| 1067 | { | ||
| 1068 | if( core->pFirst->pData[j] == cChar ) | ||
| 1069 | return j; | ||
| 1070 | } | ||
| 1071 | return -1; | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | long Bu::String::findIdx( const char *sText, long iStart ) const | ||
| 1075 | { | ||
| 1076 | long nTLen = strlen( sText ); | ||
| 1077 | flatten(); | ||
| 1078 | for( long j = iStart; j < core->pFirst->nLength-nTLen; j++ ) | ||
| 1079 | { | ||
| 1080 | if( !strncmp( sText, core->pFirst->pData+j, nTLen ) ) | ||
| 1081 | return j; | ||
| 1082 | } | ||
| 1083 | return -1; | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | long Bu::String::rfindIdx( const char *sText ) const | ||
| 1087 | { | ||
| 1088 | long nTLen = strlen( sText ); | ||
| 1089 | flatten(); | ||
| 1090 | for( long j = core->pFirst->nLength-nTLen-1; j >= 0; j-- ) | ||
| 1091 | { | ||
| 1092 | if( !strncmp( sText, core->pFirst->pData+j, nTLen ) ) | ||
| 1093 | return j; | ||
| 1094 | } | ||
| 1095 | return -1; | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | void Bu::String::trimFront( long nAmnt ) | ||
| 1099 | { | ||
| 1100 | long nNewLen = core->nLength - nAmnt; | ||
| 1101 | flatten(); | ||
| 1102 | Chunk *pNew = core->newChunk( nNewLen ); | ||
| 1103 | memcpy( pNew->pData, core->pFirst->pData+nAmnt, nNewLen ); | ||
| 1104 | _hardCopy(); | ||
| 1105 | core->clear(); | ||
| 1106 | core->appendChunk( pNew ); | ||
| 1107 | } | ||
| 1108 | /* | ||
| 1109 | void Bu::String::trimBack( char c ) | ||
| 1110 | { | ||
| 1111 | if( core->pFirst == NULL || core->nLength == 0 ) | ||
| 1112 | return; | ||
| 1113 | flatten(); | ||
| 1114 | for( ; core->pFirst->nLength > 0 && | ||
| 1115 | core->pFirst->pData[core->pFirst->nLength-1] == c; | ||
| 1116 | core->pFirst->nLength--, core->nLength-- ) { } | ||
| 1117 | } | ||
| 1118 | */ | ||
| 1119 | void Bu::String::trimBack( long iAmnt ) | ||
| 1120 | { | ||
| 1121 | if( iAmnt < 0 ) | ||
| 1122 | return; | ||
| 1123 | if( core->nLength - iAmnt < 0 ) | ||
| 1124 | { | ||
| 1125 | clear(); | ||
| 1126 | return; | ||
| 1127 | } | ||
| 1128 | if( core->pFirst == NULL || core->nLength == 0 ) | ||
| 1129 | return; | ||
| 1130 | |||
| 1131 | flatten(); | ||
| 1132 | core->pFirst->nLength -= iAmnt; | ||
| 1133 | core->nLength -= iAmnt; | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | Bu::String Bu::String::trimWhitespace() const | ||
| 1137 | { | ||
| 1138 | if( core->nLength == 0 ) | ||
| 1139 | return ""; | ||
| 1140 | const_iterator i = begin(); | ||
| 1141 | for( ; i && (*i == ' ' || *i == '\t' || *i == '\n' || *i == '\r'); i++ ) { } | ||
| 1142 | if( !i ) | ||
| 1143 | return ""; | ||
| 1144 | |||
| 1145 | const_iterator e = i; | ||
| 1146 | for( ; e; e++ ) | ||
| 1147 | { | ||
| 1148 | if( *e == ' ' || *e == '\t' || *e == '\n' || *e == '\r' ) | ||
| 1149 | { | ||
| 1150 | const_iterator t = e; | ||
| 1151 | for( ; t && (*t == ' ' || *t == '\t' || *t == '\n' || *t == '\r'); t++ ) { } | ||
| 1152 | if( t ) | ||
| 1153 | { | ||
| 1154 | e = t; | ||
| 1155 | } | ||
| 1156 | else | ||
| 1157 | { | ||
| 1158 | break; | ||
| 1159 | } | ||
| 1160 | } | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | return Bu::String( i, e ); | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | Bu::String::iterator Bu::String::begin() | ||
| 1167 | { | ||
| 1168 | if( core->nLength == 0 ) | ||
| 1169 | return iterator( NULL, 0 ); | ||
| 1170 | return iterator( core->pFirst, 0 ); | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | Bu::String::const_iterator Bu::String::begin() const | ||
| 1174 | { | ||
| 1175 | if( core->nLength == 0 ) | ||
| 1176 | return const_iterator( NULL, 0 ); | ||
| 1177 | return iterator( core->pFirst, 0 ); | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | Bu::String::iterator Bu::String::end() | ||
| 1181 | { | ||
| 1182 | return iterator( NULL, 0 ); | ||
| 1183 | } | ||
| 1184 | |||
| 1185 | Bu::String::const_iterator Bu::String::end() const | ||
| 1186 | { | ||
| 1187 | return const_iterator( NULL, 0 ); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | bool Bu::String::isEmpty() const | ||
| 1191 | { | ||
| 1192 | if( core->nLength == 0 ) | ||
| 1193 | return true; | ||
| 1194 | return false; | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | void Bu::String::flatten() const | ||
| 1198 | { | ||
| 1199 | if( isFlat() ) | ||
| 1200 | return; | ||
| 1201 | |||
| 1202 | if( core->pFirst == NULL || core->nLength == 0 ) | ||
| 1203 | return; | ||
| 1204 | |||
| 1205 | Chunk *pNew = core->newChunk( core->nLength ); | ||
| 1206 | char *pos = pNew->pData; | ||
| 1207 | Chunk *i = core->pFirst; | ||
| 1208 | for(;;) | ||
| 1209 | { | ||
| 1210 | memcpy( pos, i->pData, i->nLength ); | ||
| 1211 | pos += i->nLength; | ||
| 1212 | i = i->pNext; | ||
| 1213 | if( i == NULL ) | ||
| 1214 | break; | ||
| 1215 | } | ||
| 1216 | core->clear(); | ||
| 1217 | |||
| 1218 | core->pLast = core->pFirst = pNew; | ||
| 1219 | core->nLength = pNew->nLength; | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | bool Bu::String::isFlat() const | ||
| 1223 | { | ||
| 1224 | return (core->pFirst == core->pLast); | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | // | ||
| 1228 | // Sub-class Bu::String::FormatProxy | ||
| 1229 | // | ||
| 1230 | |||
| 1231 | Bu::String::FormatProxy::FormatProxy( const String &rFmt ) : | ||
| 1232 | rFmt( rFmt ) | ||
| 1233 | { | ||
| 1234 | } | ||
| 1235 | |||
| 1236 | Bu::String::FormatProxy::~FormatProxy() | ||
| 1237 | { | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | Bu::String::FormatProxy::operator Bu::String() const | ||
| 1241 | { | ||
| 1242 | int iCount = lArgs.getSize(); | ||
| 1243 | ArgList::const_iterator *aArg = | ||
| 1244 | new ArgList::const_iterator[iCount]; | ||
| 1245 | { | ||
| 1246 | int j = 0; | ||
| 1247 | for( ArgList::const_iterator i = lArgs.begin(); | ||
| 1248 | i; i++, j++ ) | ||
| 1249 | { | ||
| 1250 | aArg[j] = i; | ||
| 1251 | } | ||
| 1252 | } | ||
| 1253 | Bu::MemBuf mbOut; | ||
| 1254 | Bu::Formatter f( mbOut ); | ||
| 1255 | for( String::const_iterator s = rFmt.begin(); s; s++ ) | ||
| 1256 | { | ||
| 1257 | if( *s == '%' ) | ||
| 1258 | { | ||
| 1259 | s++; | ||
| 1260 | if( *s == '%' ) | ||
| 1261 | f << *s; | ||
| 1262 | else | ||
| 1263 | { | ||
| 1264 | String sNum; | ||
| 1265 | while( s && *s >= '0' && *s <= '9' ) | ||
| 1266 | { | ||
| 1267 | sNum += *s; | ||
| 1268 | s++; | ||
| 1269 | } | ||
| 1270 | int iIndex = strtol( sNum.getStr(), 0, 10 )-1; | ||
| 1271 | if( iIndex < 0 || iIndex >= iCount ) | ||
| 1272 | { | ||
| 1273 | delete[] aArg; | ||
| 1274 | throw Bu::ExceptionBase( | ||
| 1275 | "Argument index %d is outside of " | ||
| 1276 | "valid range (1-%d).", iIndex+1, iCount | ||
| 1277 | ); | ||
| 1278 | } | ||
| 1279 | |||
| 1280 | f << (*aArg[iIndex]).format << (*aArg[iIndex]).value; | ||
| 1281 | if( s ) | ||
| 1282 | f << *s; | ||
| 1283 | } | ||
| 1284 | } | ||
| 1285 | else | ||
| 1286 | { | ||
| 1287 | f << *s; | ||
| 1288 | } | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | delete[] aArg; | ||
| 1292 | return mbOut.getString(); | ||
| 1293 | } | ||
| 1294 | |||
| 1295 | |||
| 1296 | |||
| 1297 | |||
| 1298 | |||
| 1299 | |||
| 1300 | |||
| 1301 | |||
| 1302 | |||
| 1303 | |||
| 1304 | |||
| 1305 | |||
| 1306 | |||
| 1307 | |||
| 1308 | |||
| 1309 | |||
| 1310 | |||
| 1311 | |||
| 1312 | |||
| 1313 | |||
| 1314 | |||
| 1315 | |||
| 1316 | |||
| 1317 | |||
| 1318 | |||
| 1319 | |||
| 1320 | |||
| 1321 | |||
| 1322 | |||
| 1323 | |||
| 1324 | |||
| 1325 | |||
| 1326 | |||
| 1327 | |||
| 1328 | |||
| 1329 | |||
| 1330 | |||
| 1331 | |||
| 1332 | |||
| 1333 | |||
| 1334 | |||
| 1335 | |||
| 1336 | |||
| 1337 | |||
| 1338 | |||
| 1339 | |||
| 1340 | |||
| 1341 | template<> uint32_t Bu::__calcHashCode<Bu::String>( const Bu::String &k ) | ||
| 1342 | { | ||
| 1343 | long j, sz = k.getSize(); | ||
| 1344 | const char *s = k.getStr(); | ||
| 1345 | |||
| 1346 | long nPos = 0; | ||
| 1347 | for( j = 0; j < sz; j++, s++ ) | ||
| 1348 | { | ||
| 1349 | nPos = *s + (nPos << 6) + (nPos << 16) - nPos; | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | return nPos; | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | template<> bool Bu::__cmpHashKeys<Bu::String>( | ||
| 1356 | const Bu::String &a, const Bu::String &b ) | ||
| 1357 | { | ||
| 1358 | return a == b; | ||
| 1359 | } | ||
| 1360 | |||
| 1361 | template<> void Bu::__tracer_format<Bu::String>( const Bu::String &v ) | ||
| 1362 | { | ||
| 1363 | printf("(%ld)\"%s\"", v.getSize(), v.getStr() ); | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | bool &Bu::operator<<( bool &dst, const Bu::String &sIn ) | ||
| 1367 | { | ||
| 1368 | if( sIn == "true" || sIn == "yes" || sIn == "t" ) | ||
| 1369 | dst = true; | ||
| 1370 | else | ||
| 1371 | dst = false; | ||
| 1372 | |||
| 1373 | return dst; | ||
| 1374 | } | ||
| 1375 | |||
| 1376 | uint8_t &Bu::operator<<( uint8_t &dst, const Bu::String &sIn ) | ||
| 1377 | { | ||
| 1378 | sscanf( sIn.getStr(), "%hhu", &dst ); | ||
| 1379 | return dst; | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | int8_t &Bu::operator<<( int8_t &dst, const Bu::String &sIn ) | ||
| 1383 | { | ||
| 1384 | sscanf( sIn.getStr(), "%hhd", &dst ); | ||
| 1385 | return dst; | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | char &Bu::operator<<( char &dst, const Bu::String &sIn ) | ||
| 1389 | { | ||
| 1390 | sscanf( sIn.getStr(), "%hhd", &dst ); | ||
| 1391 | return dst; | ||
| 1392 | } | ||
| 1393 | |||
| 1394 | uint16_t &Bu::operator<<( uint16_t &dst, const Bu::String &sIn ) | ||
| 1395 | { | ||
| 1396 | sscanf( sIn.getStr(), "%hu", &dst ); | ||
| 1397 | return dst; | ||
| 1398 | } | ||
| 1399 | |||
| 1400 | int16_t &Bu::operator<<( int16_t &dst, const Bu::String &sIn ) | ||
| 1401 | { | ||
| 1402 | sscanf( sIn.getStr(), "%hd", &dst ); | ||
| 1403 | return dst; | ||
| 1404 | } | ||
| 1405 | |||
| 1406 | uint32_t &Bu::operator<<( uint32_t &dst, const Bu::String &sIn ) | ||
| 1407 | { | ||
| 1408 | sscanf( sIn.getStr(), "%u", &dst ); | ||
| 1409 | return dst; | ||
| 1410 | } | ||
| 1411 | |||
| 1412 | int32_t &Bu::operator<<( int32_t &dst, const Bu::String &sIn ) | ||
| 1413 | { | ||
| 1414 | sscanf( sIn.getStr(), "%d", &dst ); | ||
| 1415 | return dst; | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | uint64_t &Bu::operator<<( uint64_t &dst, const Bu::String &sIn ) | ||
| 1419 | { | ||
| 1420 | sscanf( sIn.getStr(), "%llu", &dst ); | ||
| 1421 | return dst; | ||
| 1422 | } | ||
| 1423 | |||
| 1424 | int64_t &Bu::operator<<( int64_t &dst, const Bu::String &sIn ) | ||
| 1425 | { | ||
| 1426 | sscanf( sIn.getStr(), "%lld", &dst ); | ||
| 1427 | return dst; | ||
| 1428 | } | ||
| 1429 | |||
| 1430 | float &Bu::operator<<( float &dst, const Bu::String &sIn ) | ||
| 1431 | { | ||
| 1432 | sscanf( sIn.getStr(), "%f", &dst ); | ||
| 1433 | return dst; | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | double &Bu::operator<<( double &dst, const Bu::String &sIn ) | ||
| 1437 | { | ||
| 1438 | sscanf( sIn.getStr(), "%lf", &dst ); | ||
| 1439 | return dst; | ||
| 1440 | } | ||
| 1441 | |||
| 1442 | long double &Bu::operator<<( long double &dst, const Bu::String &sIn ) | ||
| 1443 | { | ||
| 1444 | sscanf( sIn.getStr(), "%Lf", &dst ); | ||
| 1445 | return dst; | ||
| 1446 | } | ||
| 1447 | |||
| 1448 | Bu::String &Bu::operator<<( Bu::String &dst, const Bu::String &sIn ) | ||
| 1449 | { | ||
| 1450 | dst = sIn; | ||
| 1451 | return dst; | ||
| 1452 | } | ||
| 1453 | |||
| 1454 | Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, const Bu::String &s ) | ||
| 1455 | { | ||
| 1456 | long n = s.getSize(); | ||
| 1457 | ar << n; | ||
| 1458 | ar.write( s.getConstStr(), n ); | ||
| 1459 | return ar; | ||
| 1460 | } | ||
| 1461 | |||
| 1462 | Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, Bu::String &s ) | ||
| 1463 | { | ||
| 1464 | long n; | ||
| 1465 | ar >> n; | ||
| 1466 | s.setSize( n ); | ||
| 1467 | ar.read( s.getStr(), n ); | ||
| 1468 | return ar; | ||
| 1469 | } | ||
| 1470 | |||
| diff --git a/src/stable/string.h b/src/stable/string.h new file mode 100644 index 0000000..a9006d1 --- /dev/null +++ b/src/stable/string.h | |||
| @@ -0,0 +1,1053 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_STRING_H | ||
| 9 | #define BU_STRING_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include <memory> | ||
| 13 | |||
| 14 | #include "bu/util.h" | ||
| 15 | #include "bu/sharedcore.h" | ||
| 16 | #include "bu/exceptionbase.h" | ||
| 17 | #include "bu/archivebase.h" | ||
| 18 | #include "bu/list.h" | ||
| 19 | #include "bu/fmt.h" | ||
| 20 | #include "bu/variant.h" | ||
| 21 | #include <string.h> | ||
| 22 | |||
| 23 | namespace Bu | ||
| 24 | { | ||
| 25 | class String; | ||
| 26 | class MemBuf; | ||
| 27 | |||
| 28 | /** @cond DEVEL */ | ||
| 29 | class StringCore | ||
| 30 | { | ||
| 31 | friend class String; | ||
| 32 | friend class SharedCore<String, StringCore>; | ||
| 33 | private: | ||
| 34 | struct Chunk | ||
| 35 | { | ||
| 36 | long nLength; | ||
| 37 | char *pData; | ||
| 38 | Chunk *pNext; | ||
| 39 | }; | ||
| 40 | |||
| 41 | StringCore(); | ||
| 42 | StringCore( const StringCore &rSrc ); | ||
| 43 | virtual ~StringCore(); | ||
| 44 | |||
| 45 | mutable long nLength; | ||
| 46 | mutable Chunk *pFirst; | ||
| 47 | mutable Chunk *pLast; | ||
| 48 | |||
| 49 | void clear() const; | ||
| 50 | Chunk *newChunk() const; | ||
| 51 | Chunk *newChunk( long nLen ) const; | ||
| 52 | Chunk *copyChunk( Chunk *pSrc ) const; | ||
| 53 | void appendChunk( Chunk *pNewChunk ); | ||
| 54 | void prependChunk( Chunk *pNewChunk ); | ||
| 55 | }; | ||
| 56 | /** @endcond */ | ||
| 57 | |||
| 58 | /** | ||
| 59 | */ | ||
| 60 | class String : public SharedCore<String, StringCore> | ||
| 61 | { | ||
| 62 | protected: | ||
| 63 | using SharedCore<String, StringCore >::core; | ||
| 64 | using SharedCore<String, StringCore >::_hardCopy; | ||
| 65 | |||
| 66 | private: | ||
| 67 | typedef StringCore::Chunk Chunk; | ||
| 68 | |||
| 69 | public: // Iterators | ||
| 70 | struct iterator; | ||
| 71 | typedef struct const_iterator | ||
| 72 | { | ||
| 73 | friend class String; | ||
| 74 | friend struct iterator; | ||
| 75 | private: | ||
| 76 | const_iterator( Chunk *pChunk, int iPos ) : | ||
| 77 | pChunk( pChunk ), | ||
| 78 | iPos( iPos ) | ||
| 79 | { | ||
| 80 | } | ||
| 81 | |||
| 82 | Chunk *pChunk; | ||
| 83 | int iPos; | ||
| 84 | |||
| 85 | public: | ||
| 86 | const_iterator( const const_iterator &i ) : | ||
| 87 | pChunk( i.pChunk ), | ||
| 88 | iPos( i.iPos ) | ||
| 89 | { | ||
| 90 | } | ||
| 91 | |||
| 92 | const_iterator( const struct iterator &i ) : | ||
| 93 | pChunk( i.pChunk ), | ||
| 94 | iPos( i.iPos ) | ||
| 95 | { | ||
| 96 | } | ||
| 97 | |||
| 98 | const_iterator() : | ||
| 99 | pChunk( NULL ), | ||
| 100 | iPos( 0 ) | ||
| 101 | { | ||
| 102 | } | ||
| 103 | |||
| 104 | bool operator==( const const_iterator &i ) const | ||
| 105 | { | ||
| 106 | return pChunk == i.pChunk && iPos == i.iPos; | ||
| 107 | } | ||
| 108 | |||
| 109 | bool operator!=( const const_iterator &i ) const | ||
| 110 | { | ||
| 111 | return !(*this == i); | ||
| 112 | } | ||
| 113 | |||
| 114 | const_iterator &operator=( const const_iterator &i ) | ||
| 115 | { | ||
| 116 | pChunk = i.pChunk; | ||
| 117 | iPos = i.iPos; | ||
| 118 | return *this; | ||
| 119 | } | ||
| 120 | |||
| 121 | const_iterator &operator=( const iterator &i ) | ||
| 122 | { | ||
| 123 | pChunk = i.pChunk; | ||
| 124 | iPos = i.iPos; | ||
| 125 | return *this; | ||
| 126 | } | ||
| 127 | |||
| 128 | const_iterator &operator++() | ||
| 129 | { | ||
| 130 | if( !pChunk ) return *this; | ||
| 131 | iPos++; | ||
| 132 | if( iPos >= pChunk->nLength ) | ||
| 133 | { | ||
| 134 | iPos = 0; | ||
| 135 | pChunk = pChunk->pNext; | ||
| 136 | } | ||
| 137 | return *this; | ||
| 138 | } | ||
| 139 | |||
| 140 | const_iterator &operator++( int ) | ||
| 141 | { | ||
| 142 | if( !pChunk ) return *this; | ||
| 143 | iPos++; | ||
| 144 | if( iPos >= pChunk->nLength ) | ||
| 145 | { | ||
| 146 | iPos = 0; | ||
| 147 | pChunk = pChunk->pNext; | ||
| 148 | } | ||
| 149 | return *this; | ||
| 150 | } | ||
| 151 | |||
| 152 | const_iterator &operator+=( int iAmnt ) | ||
| 153 | { | ||
| 154 | if( !pChunk ) return *this; | ||
| 155 | iPos += iAmnt; | ||
| 156 | while( iPos >= pChunk->nLength ) | ||
| 157 | { | ||
| 158 | iPos -= pChunk->nLength; | ||
| 159 | pChunk = pChunk->pNext; | ||
| 160 | if( pChunk == NULL ) | ||
| 161 | break; | ||
| 162 | } | ||
| 163 | return *this; | ||
| 164 | } | ||
| 165 | |||
| 166 | const_iterator operator+( int iAmnt ) const | ||
| 167 | { | ||
| 168 | if( !pChunk ) return *this; | ||
| 169 | const_iterator ret( *this ); | ||
| 170 | ret += iAmnt; | ||
| 171 | return ret; | ||
| 172 | } | ||
| 173 | |||
| 174 | const char &operator *() const | ||
| 175 | { | ||
| 176 | if( !pChunk ) throw Bu::ExceptionBase("Not a valid const_iterator."); | ||
| 177 | return pChunk->pData[iPos]; | ||
| 178 | } | ||
| 179 | |||
| 180 | bool operator==( const char &c ) const | ||
| 181 | { | ||
| 182 | if( !pChunk ) return false; | ||
| 183 | return pChunk->pData[iPos] == c; | ||
| 184 | } | ||
| 185 | |||
| 186 | bool operator!=( const char &c ) const | ||
| 187 | { | ||
| 188 | if( !pChunk ) return false; | ||
| 189 | return pChunk->pData[iPos] != c; | ||
| 190 | } | ||
| 191 | |||
| 192 | operator bool() const | ||
| 193 | { | ||
| 194 | return pChunk != NULL; | ||
| 195 | } | ||
| 196 | |||
| 197 | bool isValid() const | ||
| 198 | { | ||
| 199 | return pChunk != NULL; | ||
| 200 | } | ||
| 201 | |||
| 202 | bool compare( const const_iterator &c ) const | ||
| 203 | { | ||
| 204 | const_iterator a = *this; | ||
| 205 | const_iterator b = c; | ||
| 206 | if( a == b ) | ||
| 207 | return true; | ||
| 208 | for(; a && b; a++, b++ ) | ||
| 209 | { | ||
| 210 | if( *a != *b ) | ||
| 211 | return false; | ||
| 212 | } | ||
| 213 | if( (bool)a != (bool)b ) | ||
| 214 | return false; | ||
| 215 | return true; | ||
| 216 | } | ||
| 217 | |||
| 218 | bool compare( const const_iterator &c, int nLen ) const | ||
| 219 | { | ||
| 220 | const_iterator a = *this; | ||
| 221 | const_iterator b = c; | ||
| 222 | if( a == b ) | ||
| 223 | return true; | ||
| 224 | for(int j = 0; j < nLen; a++, b++, j++ ) | ||
| 225 | { | ||
| 226 | if( !a || !b || *a != *b ) | ||
| 227 | return false; | ||
| 228 | } | ||
| 229 | return true; | ||
| 230 | } | ||
| 231 | |||
| 232 | bool compare( const char *c ) const | ||
| 233 | { | ||
| 234 | if( !pChunk ) return false; | ||
| 235 | const_iterator a = *this; | ||
| 236 | for(; a && *c; a++, c++ ) | ||
| 237 | { | ||
| 238 | if( *a != *c ) | ||
| 239 | return false; | ||
| 240 | } | ||
| 241 | if( a.isValid() != (*c!=(char)0) ) | ||
| 242 | return false; | ||
| 243 | return true; | ||
| 244 | } | ||
| 245 | |||
| 246 | bool compare( const char *c, int nLen ) const | ||
| 247 | { | ||
| 248 | if( !pChunk ) return false; | ||
| 249 | const_iterator a = *this; | ||
| 250 | int j = 0; | ||
| 251 | for(; a && j < nLen; a++, c++, j++ ) | ||
| 252 | { | ||
| 253 | if( *a != *c ) | ||
| 254 | return false; | ||
| 255 | } | ||
| 256 | if( j < nLen ) | ||
| 257 | return false; | ||
| 258 | return true; | ||
| 259 | } | ||
| 260 | |||
| 261 | bool compare( const String &s ) const | ||
| 262 | { | ||
| 263 | if( !pChunk ) return false; | ||
| 264 | return compare( s.begin() ); | ||
| 265 | } | ||
| 266 | |||
| 267 | bool compare( const String &s, int nLen ) const | ||
| 268 | { | ||
| 269 | if( !pChunk ) return false; | ||
| 270 | return compare( s.begin(), nLen ); | ||
| 271 | } | ||
| 272 | |||
| 273 | const_iterator find( const char c ) const | ||
| 274 | { | ||
| 275 | for( const_iterator i = *this; i; i++ ) | ||
| 276 | { | ||
| 277 | if( *i == c ) | ||
| 278 | return i; | ||
| 279 | } | ||
| 280 | return const_iterator( NULL, 0 ); | ||
| 281 | } | ||
| 282 | |||
| 283 | const_iterator find( const char *pStr, int nLen ) const | ||
| 284 | { | ||
| 285 | for( const_iterator i = *this; i; i++ ) | ||
| 286 | { | ||
| 287 | if( i.compare( pStr, nLen ) ) | ||
| 288 | return i; | ||
| 289 | } | ||
| 290 | return const_iterator( NULL, 0 ); | ||
| 291 | } | ||
| 292 | |||
| 293 | const_iterator find( const String &s ) const | ||
| 294 | { | ||
| 295 | for( const_iterator i = *this; i; i++ ) | ||
| 296 | { | ||
| 297 | if( i.compare( s ) ) | ||
| 298 | return i; | ||
| 299 | } | ||
| 300 | return const_iterator( NULL, 0 ); | ||
| 301 | } | ||
| 302 | |||
| 303 | const_iterator find( const String &s, int nLen ) const | ||
| 304 | { | ||
| 305 | for( const_iterator i = *this; i; i++ ) | ||
| 306 | { | ||
| 307 | if( i.compare( s, nLen ) ) | ||
| 308 | return i; | ||
| 309 | } | ||
| 310 | return const_iterator( NULL, 0 ); | ||
| 311 | } | ||
| 312 | } const_iterator; | ||
| 313 | |||
| 314 | typedef struct iterator | ||
| 315 | { | ||
| 316 | friend class String; | ||
| 317 | friend struct const_iterator; | ||
| 318 | private: | ||
| 319 | iterator( Chunk *pChunk, int iPos ) : | ||
| 320 | pChunk( pChunk ), | ||
| 321 | iPos( iPos ) | ||
| 322 | { | ||
| 323 | } | ||
| 324 | |||
| 325 | Chunk *pChunk; | ||
| 326 | int iPos; | ||
| 327 | |||
| 328 | public: | ||
| 329 | iterator( const iterator &i ) : | ||
| 330 | pChunk( i.pChunk ), | ||
| 331 | iPos( i.iPos ) | ||
| 332 | { | ||
| 333 | } | ||
| 334 | |||
| 335 | iterator() : | ||
| 336 | pChunk( NULL ), | ||
| 337 | iPos( 0 ) | ||
| 338 | { | ||
| 339 | } | ||
| 340 | |||
| 341 | operator const_iterator() const | ||
| 342 | { | ||
| 343 | return const_iterator( pChunk, iPos ); | ||
| 344 | } | ||
| 345 | |||
| 346 | bool operator==( const iterator &i ) const | ||
| 347 | { | ||
| 348 | return pChunk == i.pChunk && iPos == i.iPos; | ||
| 349 | } | ||
| 350 | |||
| 351 | bool operator!=( const iterator &i ) const | ||
| 352 | { | ||
| 353 | return !(*this == i); | ||
| 354 | } | ||
| 355 | |||
| 356 | iterator &operator=( const iterator &i ) | ||
| 357 | { | ||
| 358 | pChunk = i.pChunk; | ||
| 359 | iPos = i.iPos; | ||
| 360 | return *this; | ||
| 361 | } | ||
| 362 | |||
| 363 | iterator &operator++() | ||
| 364 | { | ||
| 365 | if( !pChunk ) return *this; | ||
| 366 | iPos++; | ||
| 367 | if( iPos >= pChunk->nLength ) | ||
| 368 | { | ||
| 369 | iPos = 0; | ||
| 370 | pChunk = pChunk->pNext; | ||
| 371 | } | ||
| 372 | return *this; | ||
| 373 | } | ||
| 374 | |||
| 375 | iterator &operator++( int ) | ||
| 376 | { | ||
| 377 | if( !pChunk ) return *this; | ||
| 378 | iPos++; | ||
| 379 | if( iPos >= pChunk->nLength ) | ||
| 380 | { | ||
| 381 | iPos = 0; | ||
| 382 | pChunk = pChunk->pNext; | ||
| 383 | } | ||
| 384 | return *this; | ||
| 385 | } | ||
| 386 | |||
| 387 | iterator &operator+=( int iAmnt ) | ||
| 388 | { | ||
| 389 | if( !pChunk ) return *this; | ||
| 390 | iPos += iAmnt; | ||
| 391 | while( iPos >= pChunk->nLength ) | ||
| 392 | { | ||
| 393 | iPos -= pChunk->nLength; | ||
| 394 | pChunk = pChunk->pNext; | ||
| 395 | if( pChunk == NULL ) | ||
| 396 | break; | ||
| 397 | } | ||
| 398 | return *this; | ||
| 399 | } | ||
| 400 | |||
| 401 | iterator operator+( int iAmnt ) const | ||
| 402 | { | ||
| 403 | if( !pChunk ) return *this; | ||
| 404 | iterator ret( *this ); | ||
| 405 | ret += iAmnt; | ||
| 406 | return ret; | ||
| 407 | } | ||
| 408 | |||
| 409 | char &operator*() | ||
| 410 | { | ||
| 411 | if( !pChunk ) throw Bu::ExceptionBase("Not a valid iterator."); | ||
| 412 | return pChunk->pData[iPos]; | ||
| 413 | } | ||
| 414 | |||
| 415 | const char &operator*() const | ||
| 416 | { | ||
| 417 | if( !pChunk ) throw Bu::ExceptionBase("Not a valid iterator."); | ||
| 418 | return pChunk->pData[iPos]; | ||
| 419 | } | ||
| 420 | |||
| 421 | bool operator==( const char &c ) const | ||
| 422 | { | ||
| 423 | if( !pChunk ) return false; | ||
| 424 | return pChunk->pData[iPos] == c; | ||
| 425 | } | ||
| 426 | |||
| 427 | bool operator!=( const char &c ) const | ||
| 428 | { | ||
| 429 | if( !pChunk ) return false; | ||
| 430 | return pChunk->pData[iPos] != c; | ||
| 431 | } | ||
| 432 | |||
| 433 | iterator &operator=( const char &c ) | ||
| 434 | { | ||
| 435 | if( !pChunk ) throw Bu::ExceptionBase("Not a valid iterator."); | ||
| 436 | pChunk->pData[iPos] = c; | ||
| 437 | return *this; | ||
| 438 | } | ||
| 439 | |||
| 440 | operator bool() const | ||
| 441 | { | ||
| 442 | return pChunk != NULL; | ||
| 443 | } | ||
| 444 | |||
| 445 | bool isValid() const | ||
| 446 | { | ||
| 447 | return pChunk != NULL; | ||
| 448 | } | ||
| 449 | |||
| 450 | bool compare( const const_iterator &c ) const | ||
| 451 | { | ||
| 452 | const_iterator a( *this ); | ||
| 453 | const_iterator b = c; | ||
| 454 | if( a == b ) | ||
| 455 | return true; | ||
| 456 | for(; a && b; a++, b++ ) | ||
| 457 | { | ||
| 458 | if( *a != *b ) | ||
| 459 | return false; | ||
| 460 | } | ||
| 461 | if( (bool)a != (bool)b ) | ||
| 462 | return false; | ||
| 463 | return true; | ||
| 464 | } | ||
| 465 | |||
| 466 | bool compare( const const_iterator &c, int nLen ) const | ||
| 467 | { | ||
| 468 | const_iterator a( *this ); | ||
| 469 | const_iterator b = c; | ||
| 470 | if( a == b ) | ||
| 471 | return true; | ||
| 472 | for(int j = 0; j < nLen; a++, b++, j++ ) | ||
| 473 | { | ||
| 474 | if( !a || !b || *a != *b ) | ||
| 475 | return false; | ||
| 476 | } | ||
| 477 | return true; | ||
| 478 | } | ||
| 479 | |||
| 480 | bool compare( const char *c ) const | ||
| 481 | { | ||
| 482 | if( !pChunk ) return false; | ||
| 483 | iterator a = *this; | ||
| 484 | for(; a && *c; a++, c++ ) | ||
| 485 | { | ||
| 486 | if( *a != *c ) | ||
| 487 | return false; | ||
| 488 | } | ||
| 489 | if( a.isValid() != (*c!=(char)0) ) | ||
| 490 | return false; | ||
| 491 | return true; | ||
| 492 | } | ||
| 493 | |||
| 494 | bool compare( const char *c, int nLen ) const | ||
| 495 | { | ||
| 496 | if( !pChunk ) return false; | ||
| 497 | iterator a = *this; | ||
| 498 | int j = 0; | ||
| 499 | for(; a && j < nLen; a++, c++, j++ ) | ||
| 500 | { | ||
| 501 | if( *a != *c ) | ||
| 502 | return false; | ||
| 503 | } | ||
| 504 | if( j < nLen ) | ||
| 505 | return false; | ||
| 506 | return true; | ||
| 507 | } | ||
| 508 | |||
| 509 | bool compare( const String &s ) const | ||
| 510 | { | ||
| 511 | if( !pChunk ) return false; | ||
| 512 | return compare( s.begin() ); | ||
| 513 | } | ||
| 514 | |||
| 515 | bool compare( const String &s, int nLen ) const | ||
| 516 | { | ||
| 517 | if( !pChunk ) return false; | ||
| 518 | return compare( s.begin(), nLen ); | ||
| 519 | } | ||
| 520 | |||
| 521 | iterator find( const char c ) const | ||
| 522 | { | ||
| 523 | for( iterator i = *this; i; i++ ) | ||
| 524 | { | ||
| 525 | if( *i == c ) | ||
| 526 | return i; | ||
| 527 | } | ||
| 528 | return iterator( NULL, 0 ); | ||
| 529 | } | ||
| 530 | |||
| 531 | iterator find( const char *pStr, int nLen ) const | ||
| 532 | { | ||
| 533 | for( iterator i = *this; i; i++ ) | ||
| 534 | { | ||
| 535 | if( i.compare( pStr, nLen ) ) | ||
| 536 | return i; | ||
| 537 | } | ||
| 538 | return iterator( NULL, 0 ); | ||
| 539 | } | ||
| 540 | |||
| 541 | iterator find( const String &s ) const | ||
| 542 | { | ||
| 543 | for( iterator i = *this; i; i++ ) | ||
| 544 | { | ||
| 545 | if( i.compare( s ) ) | ||
| 546 | return i; | ||
| 547 | } | ||
| 548 | return iterator( NULL, 0 ); | ||
| 549 | } | ||
| 550 | |||
| 551 | iterator find( const String &s, int nLen ) const | ||
| 552 | { | ||
| 553 | for( iterator i = *this; i; i++ ) | ||
| 554 | { | ||
| 555 | if( i.compare( s, nLen ) ) | ||
| 556 | return i; | ||
| 557 | } | ||
| 558 | return iterator( NULL, 0 ); | ||
| 559 | } | ||
| 560 | } iterator; | ||
| 561 | |||
| 562 | public: | ||
| 563 | String(); | ||
| 564 | String( const char *pData ); | ||
| 565 | String( const char *pData, long nLength ); | ||
| 566 | String( const String &rSrc ); | ||
| 567 | String( const String &rSrc, long nLength ); | ||
| 568 | String( const String &rSrc, long nStart, long nLength ); | ||
| 569 | String( long nSize ); | ||
| 570 | String( const const_iterator &s ); | ||
| 571 | String( const const_iterator &s, const const_iterator &e ); | ||
| 572 | virtual ~String(); | ||
| 573 | |||
| 574 | /** | ||
| 575 | * Append data to your string. | ||
| 576 | *@param pData (const char *) The data to append. | ||
| 577 | */ | ||
| 578 | void append( const char *pData ); | ||
| 579 | |||
| 580 | /** | ||
| 581 | * Append data to your string. | ||
| 582 | *@param pData (const char *) The data to append. | ||
| 583 | *@param nLen (long) The length of the data to append. | ||
| 584 | */ | ||
| 585 | void append( const char *pData, long nLen ); | ||
| 586 | |||
| 587 | /** | ||
| 588 | * Append data to your string. | ||
| 589 | *@param pData (const char *) The data to append. | ||
| 590 | *@param nStart (long) The start position to copy from. | ||
| 591 | *@param nLen (long) The length of the data to append. | ||
| 592 | */ | ||
| 593 | void append( const char *pData, long nStart, long nLen ); | ||
| 594 | |||
| 595 | /** | ||
| 596 | * Append a single char to your string. | ||
| 597 | *@param cData (const char &) The character to append. | ||
| 598 | */ | ||
| 599 | void append( const char &cData ); | ||
| 600 | |||
| 601 | /** | ||
| 602 | * Append another String to this one. | ||
| 603 | *@param sData (String &) The String to append. | ||
| 604 | *@todo This function can be made much faster by not using getStr() | ||
| 605 | */ | ||
| 606 | void append( const String & sData ); | ||
| 607 | |||
| 608 | /** | ||
| 609 | * Append another String to this one. | ||
| 610 | *@param sData (String &) The String to append. | ||
| 611 | *@param nLen How much data to append. | ||
| 612 | *@todo This function can be made much faster by not using getStr() | ||
| 613 | */ | ||
| 614 | void append( const String & sData, long nLen ); | ||
| 615 | |||
| 616 | /** | ||
| 617 | * Append another String to this one. | ||
| 618 | *@param sData (String &) The String to append. | ||
| 619 | *@param nStart Start position in sData to start copying from. | ||
| 620 | *@param nLen How much data to append. | ||
| 621 | *@todo This function can be made much faster by not using getStr() | ||
| 622 | */ | ||
| 623 | void append( const String & sData, long nStart, long nLen ); | ||
| 624 | |||
| 625 | /** | ||
| 626 | * Append data to this String using the passed in iterator as a base. | ||
| 627 | * The iterator is const, it is not changed. | ||
| 628 | *@param s Iterator from any compatible String to copy data from. | ||
| 629 | */ | ||
| 630 | void append( const const_iterator &s ); | ||
| 631 | |||
| 632 | /** | ||
| 633 | * Append data to this String using the passed in iterator as a base. | ||
| 634 | * The iterator is const, it is not changed. | ||
| 635 | *@param s Iterator from any compatible String to copy data from. | ||
| 636 | */ | ||
| 637 | void append( const iterator &s ); | ||
| 638 | |||
| 639 | /** | ||
| 640 | * Append data to this String using the passed in iterator as a base, | ||
| 641 | * and copy data until the ending iterator is reached. The character | ||
| 642 | * at the ending iterator is not copied. | ||
| 643 | * The iterators are const, they are not changed. | ||
| 644 | *@param s Iterator from any compatible String to copy data from. | ||
| 645 | *@param e Iterator to stop copying at. | ||
| 646 | */ | ||
| 647 | void append( const const_iterator &s, const const_iterator &e ); | ||
| 648 | |||
| 649 | /** | ||
| 650 | * Prepend another String to this one. | ||
| 651 | *@param sData (String &) The String to prepend. | ||
| 652 | *@todo This function can be made much faster by not using getStr() | ||
| 653 | */ | ||
| 654 | void prepend( const String & sData ); | ||
| 655 | |||
| 656 | /** | ||
| 657 | * Prepend data to your string. | ||
| 658 | *@param pData (const char *) The data to prepend. | ||
| 659 | */ | ||
| 660 | void prepend( const char *pData ); | ||
| 661 | |||
| 662 | /** | ||
| 663 | * Prepend data to your string. | ||
| 664 | *@param pData (const char *) The data to prepend. | ||
| 665 | *@param nLen (long) The length of the data to prepend. | ||
| 666 | */ | ||
| 667 | void prepend( const char *pData, long nLen ); | ||
| 668 | |||
| 669 | void prepend( const char c ); | ||
| 670 | |||
| 671 | /** | ||
| 672 | * Insert pData before byte nPos, that is, the first byte of pData will | ||
| 673 | * start at nPos. This could probably be made faster by avoiding | ||
| 674 | * flattening. | ||
| 675 | */ | ||
| 676 | void insert( long nPos, const char *pData, long nLen ); | ||
| 677 | |||
| 678 | void insert( long nPos, const String &str ); | ||
| 679 | |||
| 680 | /** | ||
| 681 | *@todo This function shouldn't use strlen, we should add our own to | ||
| 682 | * this class, one that can be overridden in a specific implementation. | ||
| 683 | */ | ||
| 684 | void insert( long nPos, const char *pData ); | ||
| 685 | |||
| 686 | void remove( long nPos, long nLen ); | ||
| 687 | |||
| 688 | /** | ||
| 689 | * Clear all data from the string. | ||
| 690 | */ | ||
| 691 | void clear(); | ||
| 692 | |||
| 693 | String replace( const String &fnd, const String &rep ) const; | ||
| 694 | |||
| 695 | /** | ||
| 696 | * Force the string to resize | ||
| 697 | *@param nNewSize (long) The new size of the string. | ||
| 698 | */ | ||
| 699 | void resize( long nNewSize ); | ||
| 700 | |||
| 701 | /** | ||
| 702 | * Get the current size of the string. | ||
| 703 | *@returns (long) The current size of the string. | ||
| 704 | */ | ||
| 705 | long getSize() const; | ||
| 706 | |||
| 707 | /** | ||
| 708 | * Get a pointer to the string array. | ||
| 709 | *@returns (char *) The string data. | ||
| 710 | */ | ||
| 711 | char *getStr(); | ||
| 712 | |||
| 713 | /** | ||
| 714 | * Get a const pointer to the string array. | ||
| 715 | *@returns (const char *) The string data. | ||
| 716 | */ | ||
| 717 | const char *getStr() const; | ||
| 718 | |||
| 719 | /** | ||
| 720 | * A convinience function, this one won't cause as much work as the | ||
| 721 | * non-const getStr, so if you're not changing the data, consider it. | ||
| 722 | */ | ||
| 723 | const char *getConstStr() const; | ||
| 724 | |||
| 725 | String getSubStrIdx( long iStart, long iSize=-1 ) const; | ||
| 726 | |||
| 727 | String getSubStr( const_iterator iBegin, | ||
| 728 | const_iterator iEnd=String::const_iterator() ) const; | ||
| 729 | |||
| 730 | Bu::List<String> split( const char c ) const; | ||
| 731 | |||
| 732 | /** | ||
| 733 | * Plus equals operator for String. | ||
| 734 | *@param pData (const char *) The data to append to your String. | ||
| 735 | */ | ||
| 736 | String &operator+=( const char *pData ); | ||
| 737 | |||
| 738 | /** | ||
| 739 | * Plus equals operator for String. | ||
| 740 | *@param rSrc (const String &) The String to append to your String. | ||
| 741 | */ | ||
| 742 | String &operator+=( const String &rSrc ); | ||
| 743 | |||
| 744 | String &operator+=( const String::const_iterator &i ); | ||
| 745 | |||
| 746 | /** | ||
| 747 | * Plus equals operator for String. | ||
| 748 | *@param cData (const char) The character to append to your String. | ||
| 749 | */ | ||
| 750 | String &operator+=( const char cData ); | ||
| 751 | |||
| 752 | /** | ||
| 753 | * Assignment operator. | ||
| 754 | *@param pData (const char *) The character array to append to your | ||
| 755 | * String. | ||
| 756 | */ | ||
| 757 | String &operator=( const char *pData ); | ||
| 758 | |||
| 759 | String operator+( const String &rRight ) const; | ||
| 760 | |||
| 761 | String operator+( const char *pRight ) const; | ||
| 762 | |||
| 763 | String operator+( char *pRight ) const; | ||
| 764 | |||
| 765 | /** | ||
| 766 | * Reset your String to this character array. | ||
| 767 | *@param pData (const char *) The character array to set your String to. | ||
| 768 | */ | ||
| 769 | void set( const char *pData ); | ||
| 770 | |||
| 771 | /** | ||
| 772 | * Reset your String to this character array. | ||
| 773 | *@param pData (const char *) The character array to set your String to. | ||
| 774 | *@param nSize (long) The length of the inputted character array. | ||
| 775 | */ | ||
| 776 | void set( const char *pData, long nSize ); | ||
| 777 | |||
| 778 | void set( const char *pData, long nStart, long nSize ); | ||
| 779 | |||
| 780 | void set( const String &rData ); | ||
| 781 | |||
| 782 | void set( const String &rData, long nSize ); | ||
| 783 | |||
| 784 | void set( const String &rData, long nStart, long nSize ); | ||
| 785 | |||
| 786 | void set( const_iterator s ); | ||
| 787 | |||
| 788 | void set( const_iterator s, const_iterator e ); | ||
| 789 | |||
| 790 | /** | ||
| 791 | * Resize the string, possibly to make room for a copy. At the moment | ||
| 792 | * this operation *is* destructive. What was in the string will in no | ||
| 793 | * way be preserved. This is, however, very fast. If you want to | ||
| 794 | * keep your data check out resize. | ||
| 795 | *@param iSize the new size in bytes. The string is guranteed to have | ||
| 796 | * at least this much contiguous space available when done. | ||
| 797 | */ | ||
| 798 | void setSize( long iSize ); | ||
| 799 | |||
| 800 | /** | ||
| 801 | * Equals comparison operator. | ||
| 802 | *@param pData (const char *) The character array to compare your String | ||
| 803 | * to. | ||
| 804 | */ | ||
| 805 | bool operator==( const char *pData ) const; | ||
| 806 | |||
| 807 | /** | ||
| 808 | * Equals comparison operator. | ||
| 809 | *@param pData (const String &) The String to compare your String to. | ||
| 810 | */ | ||
| 811 | bool operator==( const String &pData ) const; | ||
| 812 | |||
| 813 | /** | ||
| 814 | * Not equals comparison operator. | ||
| 815 | *@param pData (const char *) The character array to compare your String | ||
| 816 | * to. | ||
| 817 | */ | ||
| 818 | bool operator!=(const char *pData ) const; | ||
| 819 | |||
| 820 | /** | ||
| 821 | * Not equals comparison operator. | ||
| 822 | *@param pData (const String &) The String to compare your String to. | ||
| 823 | */ | ||
| 824 | bool operator!=(const String &pData ) const; | ||
| 825 | |||
| 826 | bool operator<(const String &pData ) const; | ||
| 827 | |||
| 828 | bool operator<=(const String &pData ) const; | ||
| 829 | |||
| 830 | bool operator>(const String &pData ) const; | ||
| 831 | |||
| 832 | bool operator>=(const String &pData ) const; | ||
| 833 | |||
| 834 | /** | ||
| 835 | * Indexing operator | ||
| 836 | *@param nIndex (long) The index of the character you want. | ||
| 837 | *@returns (char &) The character at position (nIndex). | ||
| 838 | */ | ||
| 839 | char &operator[]( long nIndex ); | ||
| 840 | |||
| 841 | /** | ||
| 842 | * Const indexing operator | ||
| 843 | *@param nIndex (long) The index of the character you want. | ||
| 844 | *@returns (const char &) The character at position (nIndex). | ||
| 845 | */ | ||
| 846 | const char &operator[]( long nIndex ) const; | ||
| 847 | |||
| 848 | bool isSet() const; | ||
| 849 | |||
| 850 | bool compareSub( const char *pData, long nIndex, long nLen ) const; | ||
| 851 | |||
| 852 | bool compareSub( const String &rData, long nIndex, long nLen ) const; | ||
| 853 | |||
| 854 | /** | ||
| 855 | * Is the character at index (nIndex) white space? | ||
| 856 | *@param nIndex (long) The index of the character you want to check. | ||
| 857 | *@returns (bool) Is it white space? | ||
| 858 | */ | ||
| 859 | bool isWS( long nIndex ) const; | ||
| 860 | |||
| 861 | /** | ||
| 862 | * Is the character at index (nIndex) a letter? | ||
| 863 | *@param nIndex (long) The index of the character you want to check. | ||
| 864 | *@returns (bool) Is it a letter? | ||
| 865 | */ | ||
| 866 | bool isAlpha( long nIndex ) const; | ||
| 867 | |||
| 868 | /** | ||
| 869 | * Convert your alpha characters to lower case. | ||
| 870 | */ | ||
| 871 | String toLower() const; | ||
| 872 | |||
| 873 | /** | ||
| 874 | * Convert your alpha characters to upper case. | ||
| 875 | */ | ||
| 876 | String toUpper() const; | ||
| 877 | |||
| 878 | const_iterator find( const char cChar, | ||
| 879 | const_iterator iStart=const_iterator() ) const; | ||
| 880 | |||
| 881 | const_iterator find( const char *sText, int nLen, | ||
| 882 | const_iterator iStart=const_iterator() ) const; | ||
| 883 | |||
| 884 | const_iterator find( const String &rStr, | ||
| 885 | const_iterator iStart=const_iterator() ) const; | ||
| 886 | |||
| 887 | const_iterator find( const String &rStr, int nLen, | ||
| 888 | const_iterator iStart=const_iterator() ) const; | ||
| 889 | |||
| 890 | iterator find( const char cChar, | ||
| 891 | const_iterator iStart=const_iterator() ); | ||
| 892 | |||
| 893 | iterator find( const char *sText, int nLen, | ||
| 894 | const_iterator iStart=const_iterator() ); | ||
| 895 | |||
| 896 | iterator find( const String &rStr, | ||
| 897 | const_iterator iStart=const_iterator() ); | ||
| 898 | |||
| 899 | iterator find( const String &rStr, int nLen, | ||
| 900 | const_iterator iStart=const_iterator() ); | ||
| 901 | |||
| 902 | /** | ||
| 903 | * Find the index of the first occurrance of cChar | ||
| 904 | *@param cChar The character to search for. | ||
| 905 | *@param iStart The position in the string to start searching from. | ||
| 906 | *@returns (long) The index of the first occurrance. -1 for not found. | ||
| 907 | */ | ||
| 908 | long findIdx( const char cChar, long iStart=0 ) const; | ||
| 909 | |||
| 910 | /** | ||
| 911 | * Find the index of the first occurrance of sText | ||
| 912 | *@param sText The null-terminated string to search for. | ||
| 913 | *@param iStart The position in the string to start searching from. | ||
| 914 | *@returns The index of the first occurrance. -1 for not found. | ||
| 915 | */ | ||
| 916 | long findIdx( const char *sText, long iStart=0 ) const; | ||
| 917 | |||
| 918 | /** | ||
| 919 | * Do a reverse search for (sText) | ||
| 920 | *@param sText (const char *) The string to search for. | ||
| 921 | *@returns (long) The index of the last occurrance. -1 for not found. | ||
| 922 | */ | ||
| 923 | long rfindIdx( const char *sText ) const; | ||
| 924 | |||
| 925 | /** | ||
| 926 | * Remove nAmnt bytes from the front of the string. This function | ||
| 927 | * operates in O(n) time and should be used sparingly. | ||
| 928 | */ | ||
| 929 | void trimFront( long nAmnt ); | ||
| 930 | |||
| 931 | void trimBack( long iAmnt ); | ||
| 932 | |||
| 933 | Bu::String trimWhitespace() const; | ||
| 934 | |||
| 935 | iterator begin(); | ||
| 936 | |||
| 937 | const_iterator begin() const; | ||
| 938 | |||
| 939 | iterator end(); | ||
| 940 | |||
| 941 | const_iterator end() const; | ||
| 942 | |||
| 943 | bool isEmpty() const; | ||
| 944 | |||
| 945 | private: | ||
| 946 | void flatten() const; | ||
| 947 | bool isFlat() const; | ||
| 948 | |||
| 949 | class FormatProxy | ||
| 950 | { | ||
| 951 | public: | ||
| 952 | FormatProxy( const String &rFmt ); | ||
| 953 | virtual ~FormatProxy(); | ||
| 954 | |||
| 955 | template<typename T> | ||
| 956 | FormatProxy &arg( const T &x ) | ||
| 957 | { | ||
| 958 | lArgs.append( Arg( x ) ); | ||
| 959 | |||
| 960 | return *this; | ||
| 961 | } | ||
| 962 | |||
| 963 | template<typename T> | ||
| 964 | FormatProxy &arg( const T &x, const Bu::Fmt &f ) | ||
| 965 | { | ||
| 966 | lArgs.append( Arg( x, f ) ); | ||
| 967 | |||
| 968 | return *this; | ||
| 969 | } | ||
| 970 | |||
| 971 | operator String() const; | ||
| 972 | |||
| 973 | private: | ||
| 974 | const String &rFmt; | ||
| 975 | class Arg | ||
| 976 | { | ||
| 977 | public: | ||
| 978 | template<typename T> | ||
| 979 | Arg( const T &v ) : | ||
| 980 | value( v ) | ||
| 981 | { | ||
| 982 | } | ||
| 983 | |||
| 984 | template<typename T> | ||
| 985 | Arg( const T &v, const Bu::Fmt &f ) : | ||
| 986 | value( v ), | ||
| 987 | format( f ) | ||
| 988 | { | ||
| 989 | } | ||
| 990 | |||
| 991 | Bu::Variant value; | ||
| 992 | Bu::Fmt format; | ||
| 993 | }; | ||
| 994 | typedef Bu::List<Arg> ArgList; | ||
| 995 | ArgList lArgs; | ||
| 996 | }; | ||
| 997 | |||
| 998 | public: | ||
| 999 | template<typename ArgType> | ||
| 1000 | FormatProxy arg( const ArgType &x ) | ||
| 1001 | { | ||
| 1002 | return FormatProxy( *this ).arg( x ); | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | template<typename ArgType> | ||
| 1006 | FormatProxy arg( const ArgType &x, const Bu::Fmt &f ) | ||
| 1007 | { | ||
| 1008 | return FormatProxy( *this ).arg( x, f ); | ||
| 1009 | } | ||
| 1010 | }; | ||
| 1011 | |||
| 1012 | template<class T> String operator+( const T *pLeft, const String &rRight ) | ||
| 1013 | { | ||
| 1014 | Bu::String ret( pLeft ); | ||
| 1015 | ret.append( rRight ); | ||
| 1016 | return ret; | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | ArchiveBase &operator<<( ArchiveBase &ar, const String &s ); | ||
| 1020 | ArchiveBase &operator>>( ArchiveBase &ar, String &s ); | ||
| 1021 | |||
| 1022 | template<typename T> | ||
| 1023 | uint32_t __calcHashCode( const T &k ); | ||
| 1024 | |||
| 1025 | template<typename T> | ||
| 1026 | bool __cmpHashKeys( const T &a, const T &b ); | ||
| 1027 | |||
| 1028 | template<> uint32_t __calcHashCode<String>( const String &k ); | ||
| 1029 | template<> bool __cmpHashKeys<String>( | ||
| 1030 | const String &a, const String &b ); | ||
| 1031 | |||
| 1032 | template<typename t> void __tracer_format( const t &v ); | ||
| 1033 | template<> void __tracer_format<String>( const String &v ); | ||
| 1034 | |||
| 1035 | bool &operator<<( bool &dst, const String &sIn ); | ||
| 1036 | uint8_t &operator<<( uint8_t &dst, const String &sIn ); | ||
| 1037 | int8_t &operator<<( int8_t &dst, const String &sIn ); | ||
| 1038 | char &operator<<( char &dst, const String &sIn ); | ||
| 1039 | uint16_t &operator<<( uint16_t &dst, const String &sIn ); | ||
| 1040 | int16_t &operator<<( int16_t &dst, const String &sIn ); | ||
| 1041 | uint32_t &operator<<( uint32_t &dst, const String &sIn ); | ||
| 1042 | int32_t &operator<<( int32_t &dst, const String &sIn ); | ||
| 1043 | uint64_t &operator<<( uint64_t &dst, const String &sIn ); | ||
| 1044 | int64_t &operator<<( int64_t &dst, const String &sIn ); | ||
| 1045 | float &operator<<( float &dst, const String &sIn ); | ||
| 1046 | double &operator<<( double &dst, const String &sIn ); | ||
| 1047 | long double &operator<<( long double &dst, const String &sIn ); | ||
| 1048 | Bu::String &operator<<( Bu::String &dst, const String &sIn ); | ||
| 1049 | |||
| 1050 | typedef Bu::List<String> StringList; | ||
| 1051 | }; | ||
| 1052 | |||
| 1053 | #endif | ||
| diff --git a/src/stable/substream.cpp b/src/stable/substream.cpp new file mode 100644 index 0000000..c201752 --- /dev/null +++ b/src/stable/substream.cpp | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/substream.h" | ||
| 9 | |||
| 10 | Bu::SubStream::SubStream( Bu::Stream &rNext, Bu::size iSize ) : | ||
| 11 | Bu::Filter( rNext ), | ||
| 12 | iStart( 0 ), | ||
| 13 | iPos( 0 ), | ||
| 14 | iSize( iSize ) | ||
| 15 | { | ||
| 16 | iStart = rNext.tell(); | ||
| 17 | } | ||
| 18 | |||
| 19 | Bu::SubStream::~SubStream() | ||
| 20 | { | ||
| 21 | } | ||
| 22 | |||
| 23 | Bu::size Bu::SubStream::read( void *pBuf, Bu::size nBytes ) | ||
| 24 | { | ||
| 25 | if( (Bu::size)nBytes > iSize-iPos ) | ||
| 26 | nBytes = iSize-iPos; | ||
| 27 | nBytes = rNext.read( pBuf, nBytes ); | ||
| 28 | iPos += nBytes; | ||
| 29 | return nBytes; | ||
| 30 | } | ||
| 31 | |||
| 32 | Bu::size Bu::SubStream::write( const void *pBuf, Bu::size nBytes ) | ||
| 33 | { | ||
| 34 | if( (Bu::size)nBytes > iSize-iPos ) | ||
| 35 | nBytes = iSize-iPos; | ||
| 36 | nBytes = rNext.write( pBuf, nBytes ); | ||
| 37 | iPos += nBytes; | ||
| 38 | return nBytes; | ||
| 39 | } | ||
| 40 | |||
| 41 | void Bu::SubStream::start() | ||
| 42 | { | ||
| 43 | // doesn't mean anything... | ||
| 44 | } | ||
| 45 | |||
| 46 | Bu::size Bu::SubStream::stop() | ||
| 47 | { | ||
| 48 | // doesn't mean anything... | ||
| 49 | return 0; | ||
| 50 | } | ||
| 51 | |||
| 52 | void Bu::SubStream::close() | ||
| 53 | { | ||
| 54 | // don't do anything? maybe... | ||
| 55 | } | ||
| 56 | |||
| 57 | Bu::size Bu::SubStream::tell() | ||
| 58 | { | ||
| 59 | return iPos; | ||
| 60 | } | ||
| 61 | |||
| 62 | void Bu::SubStream::seek( Bu::size offset ) | ||
| 63 | { | ||
| 64 | if( iPos+offset < 0 ) | ||
| 65 | offset = -iPos; | ||
| 66 | else if( iPos+offset > iSize ) | ||
| 67 | offset = iSize-iPos; | ||
| 68 | rNext.seek( offset ); | ||
| 69 | iPos += offset; | ||
| 70 | } | ||
| 71 | |||
| 72 | void Bu::SubStream::setPos( Bu::size pos ) | ||
| 73 | { | ||
| 74 | if( pos < 0 ) | ||
| 75 | pos = 0; | ||
| 76 | else if( pos > iSize ) | ||
| 77 | pos = iSize; | ||
| 78 | iPos = pos; | ||
| 79 | pos += iStart; | ||
| 80 | rNext.setPos( pos ); | ||
| 81 | } | ||
| 82 | |||
| 83 | void Bu::SubStream::setPosEnd( Bu::size pos ) | ||
| 84 | { | ||
| 85 | if( iSize-pos < 0 ) | ||
| 86 | pos = 0; | ||
| 87 | else if( iSize-pos > iSize ) | ||
| 88 | pos = iSize; | ||
| 89 | else | ||
| 90 | pos = iSize-pos; | ||
| 91 | iPos = pos; | ||
| 92 | rNext.setPos( iStart+pos ); | ||
| 93 | } | ||
| 94 | |||
| 95 | bool Bu::SubStream::isEos() | ||
| 96 | { | ||
| 97 | return rNext.isEos() || iPos == iSize; | ||
| 98 | } | ||
| 99 | |||
| 100 | bool Bu::SubStream::canRead() | ||
| 101 | { | ||
| 102 | return rNext.canRead() && (iPos < iSize); | ||
| 103 | } | ||
| 104 | |||
| 105 | bool Bu::SubStream::canWrite() | ||
| 106 | { | ||
| 107 | return rNext.canWrite() && (iPos < iSize); | ||
| 108 | } | ||
| 109 | |||
| diff --git a/src/stable/substream.h b/src/stable/substream.h new file mode 100644 index 0000000..1db4d6c --- /dev/null +++ b/src/stable/substream.h | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_SUB_STREAM_H | ||
| 9 | #define BU_SUB_STREAM_H | ||
| 10 | |||
| 11 | #include "bu/filter.h" | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * Creates a sub-stream of a given stream. This allows you to read and | ||
| 17 | * write safely to a section of another stream, keeping all data within | ||
| 18 | * the given bounds. The substream acts exactly like a top level stream | ||
| 19 | * when you reach the bounds of either the containing stream or the | ||
| 20 | * artificial bounds of the substream, except that unlike many stream types, | ||
| 21 | * when writing you cannot move beyond the bounds of the substream. Reads, | ||
| 22 | * on the other hand, work exactly the same way, returning less data than | ||
| 23 | * requested when the end of the stream is reached. | ||
| 24 | * | ||
| 25 | * The substream always begins at the current position in the base stream, | ||
| 26 | * if you would like to skip some data first, simply seek. | ||
| 27 | * | ||
| 28 | * The substream class is safe to use with all blocking and non-blocking | ||
| 29 | * base streams, including sockets, however it can have unpredictable | ||
| 30 | * results when used on a buffering stream that may read more data than | ||
| 31 | * requested in order to complete a request such as the buffer or bzip2 | ||
| 32 | * filters. | ||
| 33 | */ | ||
| 34 | class SubStream : public Bu::Filter | ||
| 35 | { | ||
| 36 | public: | ||
| 37 | SubStream( Bu::Stream &rNext, Bu::size iSize ); | ||
| 38 | virtual ~SubStream(); | ||
| 39 | |||
| 40 | virtual Bu::size read( void *pBuf, Bu::size nBytes ); | ||
| 41 | virtual Bu::size write( const void *pBuf, Bu::size nBytes ); | ||
| 42 | using Bu::Stream::write; | ||
| 43 | |||
| 44 | virtual void start(); | ||
| 45 | virtual Bu::size stop(); | ||
| 46 | virtual void close(); | ||
| 47 | virtual Bu::size tell(); | ||
| 48 | virtual void seek( Bu::size offset ); | ||
| 49 | virtual void setPos( Bu::size pos ); | ||
| 50 | virtual void setPosEnd( Bu::size pos ); | ||
| 51 | virtual bool isEos(); | ||
| 52 | |||
| 53 | virtual bool canRead(); | ||
| 54 | virtual bool canWrite(); | ||
| 55 | |||
| 56 | protected: | ||
| 57 | Bu::size iStart; | ||
| 58 | Bu::size iPos; | ||
| 59 | Bu::size iSize; | ||
| 60 | }; | ||
| 61 | }; | ||
| 62 | |||
| 63 | #endif | ||
| diff --git a/src/stable/synchroatom.h b/src/stable/synchroatom.h new file mode 100644 index 0000000..fb02054 --- /dev/null +++ b/src/stable/synchroatom.h | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_SYNCHRO_ATOM_H | ||
| 9 | #define BU_SYNCHRO_ATOM_H | ||
| 10 | |||
| 11 | #include <pthread.h> | ||
| 12 | |||
| 13 | #include "bu/mutex.h" | ||
| 14 | |||
| 15 | namespace Bu | ||
| 16 | { | ||
| 17 | /** | ||
| 18 | * A thread-safe wrapper class. | ||
| 19 | *@ingroup Threading | ||
| 20 | */ | ||
| 21 | template <class T> | ||
| 22 | class SynchroAtom | ||
| 23 | { | ||
| 24 | public: | ||
| 25 | /** | ||
| 26 | * Construct an empty queue. | ||
| 27 | */ | ||
| 28 | SynchroAtom() | ||
| 29 | { | ||
| 30 | } | ||
| 31 | |||
| 32 | SynchroAtom( const T &src ) : | ||
| 33 | data( src ) | ||
| 34 | { | ||
| 35 | } | ||
| 36 | |||
| 37 | ~SynchroAtom() | ||
| 38 | { | ||
| 39 | } | ||
| 40 | |||
| 41 | T get() | ||
| 42 | { | ||
| 43 | mOperate.lock(); | ||
| 44 | T ret = data; | ||
| 45 | mOperate.unlock(); | ||
| 46 | return ret; | ||
| 47 | } | ||
| 48 | |||
| 49 | void set( const T &val ) | ||
| 50 | { | ||
| 51 | mOperate.lock(); | ||
| 52 | data = val; | ||
| 53 | mOperate.unlock(); | ||
| 54 | } | ||
| 55 | |||
| 56 | private: | ||
| 57 | T data; | ||
| 58 | |||
| 59 | Mutex mOperate; /**< The master mutex, used on all operations. */ | ||
| 60 | }; | ||
| 61 | }; | ||
| 62 | |||
| 63 | #endif | ||
| diff --git a/src/stable/synchrocounter.cpp b/src/stable/synchrocounter.cpp new file mode 100644 index 0000000..48bbe21 --- /dev/null +++ b/src/stable/synchrocounter.cpp | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/synchrocounter.h" | ||
| diff --git a/src/stable/synchrocounter.h b/src/stable/synchrocounter.h new file mode 100644 index 0000000..d201bee --- /dev/null +++ b/src/stable/synchrocounter.h | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_SYNCHRO_COUNTER_H | ||
| 9 | #define BU_SYNCHRO_COUNTER_H | ||
| 10 | |||
| 11 | #include "bu/mutex.h" | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * A simple thread-safe counter class. This is handy for assigning unique | ||
| 17 | * IDs to objects that are being created in different threads. | ||
| 18 | *@ingroup Threading Containers | ||
| 19 | */ | ||
| 20 | template <class T> | ||
| 21 | class SynchroCounter | ||
| 22 | { | ||
| 23 | public: | ||
| 24 | SynchroCounter() : | ||
| 25 | tCounter( 0 ) | ||
| 26 | { | ||
| 27 | } | ||
| 28 | |||
| 29 | virtual ~SynchroCounter() | ||
| 30 | { | ||
| 31 | } | ||
| 32 | |||
| 33 | T next() | ||
| 34 | { | ||
| 35 | mOperate.lock(); | ||
| 36 | T tRet = tCounter; | ||
| 37 | tCounter++; | ||
| 38 | mOperate.unlock(); | ||
| 39 | |||
| 40 | return tRet; | ||
| 41 | } | ||
| 42 | |||
| 43 | private: | ||
| 44 | T tCounter; /**< The counter itself. */ | ||
| 45 | Mutex mOperate; /**< The master mutex, used on all operations. */ | ||
| 46 | }; | ||
| 47 | } | ||
| 48 | |||
| 49 | #endif | ||
| diff --git a/src/stable/synchroheap.cpp b/src/stable/synchroheap.cpp new file mode 100644 index 0000000..5dcce33 --- /dev/null +++ b/src/stable/synchroheap.cpp | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/synchroheap.h" | ||
| 9 | |||
| diff --git a/src/stable/synchroheap.h b/src/stable/synchroheap.h new file mode 100644 index 0000000..4dd898d --- /dev/null +++ b/src/stable/synchroheap.h | |||
| @@ -0,0 +1,151 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_SYNCHRO_HEAP_H | ||
| 9 | #define BU_SYNCHRO_HEAP_H | ||
| 10 | |||
| 11 | #include "bu/heap.h" | ||
| 12 | #include "bu/mutex.h" | ||
| 13 | #include "bu/condition.h" | ||
| 14 | |||
| 15 | namespace Bu | ||
| 16 | { | ||
| 17 | template<typename item, typename cmpfunc=__basicLTCmp<item>, | ||
| 18 | typename itemalloc=std::allocator<item> > | ||
| 19 | class SynchroHeap | ||
| 20 | { | ||
| 21 | public: | ||
| 22 | SynchroHeap() | ||
| 23 | { | ||
| 24 | } | ||
| 25 | |||
| 26 | virtual ~SynchroHeap() | ||
| 27 | { | ||
| 28 | } | ||
| 29 | |||
| 30 | void enqueue( item i ) | ||
| 31 | { | ||
| 32 | imData.lock(); | ||
| 33 | hData.enqueue( i ); | ||
| 34 | icBlock.signal(); | ||
| 35 | imData.unlock(); | ||
| 36 | } | ||
| 37 | |||
| 38 | item dequeue( bool bBlock=false ) | ||
| 39 | { | ||
| 40 | imData.lock(); | ||
| 41 | if( hData.isEmpty() ) | ||
| 42 | { | ||
| 43 | imData.unlock(); | ||
| 44 | |||
| 45 | if( bBlock ) | ||
| 46 | { | ||
| 47 | icBlock.lock(); | ||
| 48 | |||
| 49 | while( hData.isEmpty() ) | ||
| 50 | icBlock.wait(); | ||
| 51 | |||
| 52 | imData.lock(); | ||
| 53 | try | ||
| 54 | { | ||
| 55 | item iRet = hData.dequeue(); | ||
| 56 | imData.unlock(); | ||
| 57 | icBlock.unlock(); | ||
| 58 | return iRet; | ||
| 59 | } | ||
| 60 | catch(...) | ||
| 61 | { | ||
| 62 | imData.unlock(); | ||
| 63 | icBlock.unlock(); | ||
| 64 | throw; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | throw HeapException("Heap empty."); | ||
| 68 | } | ||
| 69 | else | ||
| 70 | { | ||
| 71 | try | ||
| 72 | { | ||
| 73 | item iRet = hData.dequeue(); | ||
| 74 | imData.unlock(); | ||
| 75 | return iRet; | ||
| 76 | } | ||
| 77 | catch(...) | ||
| 78 | { | ||
| 79 | imData.unlock(); | ||
| 80 | throw; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | item dequeue( int iSec, int iUSec ) | ||
| 86 | { | ||
| 87 | imData.lock(); | ||
| 88 | if( hData.isEmpty() ) | ||
| 89 | { | ||
| 90 | imData.unlock(); | ||
| 91 | |||
| 92 | icBlock.lock(); | ||
| 93 | |||
| 94 | icBlock.wait( iSec, iUSec ); | ||
| 95 | |||
| 96 | imData.lock(); | ||
| 97 | try | ||
| 98 | { | ||
| 99 | item iRet = hData.dequeue(); | ||
| 100 | imData.unlock(); | ||
| 101 | icBlock.unlock(); | ||
| 102 | return iRet; | ||
| 103 | } | ||
| 104 | catch(...) | ||
| 105 | { | ||
| 106 | imData.unlock(); | ||
| 107 | icBlock.unlock(); | ||
| 108 | throw; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | else | ||
| 112 | { | ||
| 113 | try | ||
| 114 | { | ||
| 115 | item iRet = hData.dequeue(); | ||
| 116 | imData.unlock(); | ||
| 117 | return iRet; | ||
| 118 | } | ||
| 119 | catch(...) | ||
| 120 | { | ||
| 121 | imData.unlock(); | ||
| 122 | throw; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | bool isEmpty() | ||
| 128 | { | ||
| 129 | imData.lock(); | ||
| 130 | bool bRet = hData.isEmpty(); | ||
| 131 | imData.unlock(); | ||
| 132 | return bRet; | ||
| 133 | } | ||
| 134 | |||
| 135 | int getSize() | ||
| 136 | { | ||
| 137 | imData.lock(); | ||
| 138 | int iRet = hData.getSize(); | ||
| 139 | imData.unlock(); | ||
| 140 | return iRet; | ||
| 141 | } | ||
| 142 | |||
| 143 | private: | ||
| 144 | Heap< item, cmpfunc, itemalloc > hData; | ||
| 145 | Mutex imData; | ||
| 146 | Condition icBlock; | ||
| 147 | }; | ||
| 148 | }; | ||
| 149 | |||
| 150 | #endif | ||
| 151 | |||
| diff --git a/src/stable/synchroqueue.h b/src/stable/synchroqueue.h new file mode 100644 index 0000000..79d5e49 --- /dev/null +++ b/src/stable/synchroqueue.h | |||
| @@ -0,0 +1,240 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_SYNCHRO_QUEUE_H | ||
| 9 | #define BU_SYNCHRO_QUEUE_H | ||
| 10 | |||
| 11 | #include <pthread.h> | ||
| 12 | |||
| 13 | #include "bu/mutex.h" | ||
| 14 | #include "bu/condition.h" | ||
| 15 | |||
| 16 | namespace Bu | ||
| 17 | { | ||
| 18 | /** | ||
| 19 | * A thread-safe queue class. This class is a very simple queue with some | ||
| 20 | * cool extra functionality for use with the Synchro system. The main extra | ||
| 21 | * that it provides is the option to either dequeue without blocking, with | ||
| 22 | * infinite blocking, or with timed blocking, which will return a value if | ||
| 23 | * something is enqueued within the specified time limit, or NULL if the | ||
| 24 | * time limit is exceded. | ||
| 25 | *@ingroup Threading Containers | ||
| 26 | */ | ||
| 27 | template <class T> | ||
| 28 | class SynchroQueue | ||
| 29 | { | ||
| 30 | private: | ||
| 31 | /** | ||
| 32 | * Helper struct. Keeps track of linked-list items for the queue data. | ||
| 33 | */ | ||
| 34 | typedef struct Item | ||
| 35 | { | ||
| 36 | T pData; | ||
| 37 | Item *pNext; | ||
| 38 | } Item; | ||
| 39 | |||
| 40 | public: | ||
| 41 | /** | ||
| 42 | * Construct an empty queue. | ||
| 43 | */ | ||
| 44 | SynchroQueue() : | ||
| 45 | pStart( NULL ), | ||
| 46 | pEnd( NULL ), | ||
| 47 | nSize( 0 ) | ||
| 48 | { | ||
| 49 | } | ||
| 50 | |||
| 51 | /** | ||
| 52 | * Destroy the queue. This function will simply free all contained | ||
| 53 | * structures. If you stored pointers in the queue, this will lose the | ||
| 54 | * pointers without cleaning up the memory they pointed to. Make sure | ||
| 55 | * you're queue is empty before allowing it to be destroyed! | ||
| 56 | */ | ||
| 57 | ~SynchroQueue() | ||
| 58 | { | ||
| 59 | Item *pCur = pStart; | ||
| 60 | while( pCur ) | ||
| 61 | { | ||
| 62 | Item *pTmp = pCur->pNext; | ||
| 63 | delete pCur; | ||
| 64 | pCur = pTmp; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Enqueue a pieces of data. The new data will go at the end of the | ||
| 70 | * queue, and unless another piece of data is enqueued, will be the | ||
| 71 | * last piece of data to be dequeued. | ||
| 72 | *@param pData The data to enqueue. If this is not a primitive data | ||
| 73 | * type it's probably best to use a pointer type. | ||
| 74 | */ | ||
| 75 | void enqueue( T pData ) | ||
| 76 | { | ||
| 77 | mOperate.lock(); | ||
| 78 | |||
| 79 | if( pStart == NULL ) | ||
| 80 | { | ||
| 81 | pStart = pEnd = new Item; | ||
| 82 | pStart->pData = pData; | ||
| 83 | pStart->pNext = NULL; | ||
| 84 | nSize++; | ||
| 85 | } | ||
| 86 | else | ||
| 87 | { | ||
| 88 | pEnd->pNext = new Item; | ||
| 89 | pEnd = pEnd->pNext; | ||
| 90 | pEnd->pData = pData; | ||
| 91 | pEnd->pNext = NULL; | ||
| 92 | nSize++; | ||
| 93 | } | ||
| 94 | |||
| 95 | cBlock.signal(); | ||
| 96 | |||
| 97 | mOperate.unlock(); | ||
| 98 | } | ||
| 99 | |||
| 100 | /** | ||
| 101 | * Dequeue the first item from the queue. This function can operate in | ||
| 102 | * two different modes, blocking and non-blocking. In non-blocking | ||
| 103 | * mode it will return immediately weather there was data in the queue | ||
| 104 | * or not. If there was data it will remove it from the queue and | ||
| 105 | * return it to the caller. | ||
| 106 | * | ||
| 107 | * In blocking mode it will block forever wating for data to be | ||
| 108 | * enqueued. When data finally is enqueued this function will return | ||
| 109 | * immediately with the new data. The only way this function should | ||
| 110 | * ever return a null in blocking mode is if the calling thread was | ||
| 111 | * cancelled. It's probably a good idea to check for NULL return | ||
| 112 | * values even if you use blocking, just to be on the safe side. | ||
| 113 | *@param bBlock Set to true to enable blocking, leave as false to work | ||
| 114 | * in non-blocking mode. | ||
| 115 | *@returns The next piece of data in the queue, or NULL if no data was | ||
| 116 | * in the queue. | ||
| 117 | */ | ||
| 118 | T dequeue( bool bBlock=false ) | ||
| 119 | { | ||
| 120 | mOperate.lock(); | ||
| 121 | if( pStart == NULL ) | ||
| 122 | { | ||
| 123 | mOperate.unlock(); | ||
| 124 | |||
| 125 | if( bBlock ) | ||
| 126 | { | ||
| 127 | cBlock.lock(); | ||
| 128 | |||
| 129 | while( pStart == NULL ) | ||
| 130 | cBlock.wait(); | ||
| 131 | |||
| 132 | T tmp = dequeue( false ); | ||
| 133 | |||
| 134 | cBlock.unlock(); | ||
| 135 | return tmp; | ||
| 136 | |||
| 137 | } | ||
| 138 | |||
| 139 | return NULL; | ||
| 140 | } | ||
| 141 | else | ||
| 142 | { | ||
| 143 | T pTmp = pStart->pData; | ||
| 144 | Item *pDel = pStart; | ||
| 145 | pStart = pStart->pNext; | ||
| 146 | delete pDel; | ||
| 147 | nSize--; | ||
| 148 | |||
| 149 | mOperate.unlock(); | ||
| 150 | return pTmp; | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | /** | ||
| 155 | * Operates just like the other dequeue function in blocking mode with | ||
| 156 | * one twist. This function will block for at most nSec seconds and | ||
| 157 | * nUSec micro-seconds. If the timer is up and no data is available, | ||
| 158 | * this will just return NULL. If data is enqueued before the timeout | ||
| 159 | * expires, it will dequeue and exit immediately. | ||
| 160 | *@param nSec The number of seconds to wait, max. | ||
| 161 | *@param nUSec The number of micro-seconds to wait, max. | ||
| 162 | *@returns The next piece of data in the queue, or NULL if the timeout | ||
| 163 | * was exceeded. | ||
| 164 | */ | ||
| 165 | T dequeue( int nSec, int nUSec ) | ||
| 166 | { | ||
| 167 | mOperate.lock(); | ||
| 168 | if( pStart == NULL ) | ||
| 169 | { | ||
| 170 | mOperate.unlock(); | ||
| 171 | |||
| 172 | cBlock.lock(); | ||
| 173 | |||
| 174 | cBlock.wait( nSec, nUSec ); | ||
| 175 | |||
| 176 | if( pStart == NULL ) | ||
| 177 | { | ||
| 178 | cBlock.unlock(); | ||
| 179 | return NULL; | ||
| 180 | } | ||
| 181 | |||
| 182 | mOperate.lock(); | ||
| 183 | T pTmp = pStart->pData; | ||
| 184 | Item *pDel = pStart; | ||
| 185 | pStart = pStart->pNext; | ||
| 186 | delete pDel; | ||
| 187 | nSize--; | ||
| 188 | mOperate.unlock(); | ||
| 189 | |||
| 190 | cBlock.unlock(); | ||
| 191 | return pTmp; | ||
| 192 | } | ||
| 193 | else | ||
| 194 | { | ||
| 195 | T pTmp = pStart->pData; | ||
| 196 | Item *pDel = pStart; | ||
| 197 | pStart = pStart->pNext; | ||
| 198 | delete pDel; | ||
| 199 | nSize--; | ||
| 200 | |||
| 201 | mOperate.unlock(); | ||
| 202 | return pTmp; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | /** | ||
| 207 | * Checks to see if the queue has data in it or not. Note that there | ||
| 208 | * is no function to determine the length of the queue. This data | ||
| 209 | * isn't kept track of. If you really need to know, fix this. | ||
| 210 | *@returns True if the queue is empty, false if it has data in it. | ||
| 211 | */ | ||
| 212 | bool isEmpty() | ||
| 213 | { | ||
| 214 | mOperate.lock(); | ||
| 215 | bool bEmpty = (pStart == NULL ); | ||
| 216 | mOperate.unlock(); | ||
| 217 | |||
| 218 | return bEmpty; | ||
| 219 | } | ||
| 220 | |||
| 221 | long getSize() | ||
| 222 | { | ||
| 223 | mOperate.lock(); | ||
| 224 | long nRet = nSize; | ||
| 225 | mOperate.unlock(); | ||
| 226 | |||
| 227 | return nRet; | ||
| 228 | } | ||
| 229 | |||
| 230 | private: | ||
| 231 | Item *pStart; /**< The start of the queue, the next element to dequeue. */ | ||
| 232 | Item *pEnd; /**< The end of the queue, the last element to dequeue. */ | ||
| 233 | long nSize; /**< The number of items in the queue. */ | ||
| 234 | |||
| 235 | Mutex mOperate; /**< The master mutex, used on all operations. */ | ||
| 236 | Condition cBlock; /**< The condition for blocking dequeues. */ | ||
| 237 | }; | ||
| 238 | } | ||
| 239 | |||
| 240 | #endif | ||
| diff --git a/src/stable/taf.h b/src/stable/taf.h new file mode 100644 index 0000000..951f80f --- /dev/null +++ b/src/stable/taf.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | // | ||
| 9 | // There's no protection on this file, it just includes other files. | ||
| 10 | // | ||
| 11 | |||
| 12 | #include "bu/tafnode.h" | ||
| 13 | #include "bu/tafgroup.h" | ||
| 14 | #include "bu/tafproperty.h" | ||
| 15 | #include "bu/tafcomment.h" | ||
| 16 | #include "bu/tafreader.h" | ||
| 17 | #include "bu/tafwriter.h" | ||
| 18 | |||
| diff --git a/src/stable/tafcomment.cpp b/src/stable/tafcomment.cpp new file mode 100644 index 0000000..c7096ca --- /dev/null +++ b/src/stable/tafcomment.cpp | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/tafcomment.h" | ||
| 9 | |||
| 10 | Bu::TafComment::TafComment( const Bu::TafComment &rSrc ) : | ||
| 11 | TafNode( typeComment ), | ||
| 12 | sText( rSrc.sText ), | ||
| 13 | bEOL( rSrc.bEOL ) | ||
| 14 | { | ||
| 15 | } | ||
| 16 | |||
| 17 | Bu::TafComment::TafComment( const Bu::String &sText, bool bEOL ) : | ||
| 18 | TafNode( typeComment ), | ||
| 19 | sText( sText ), | ||
| 20 | bEOL( bEOL ) | ||
| 21 | { | ||
| 22 | } | ||
| 23 | |||
| 24 | Bu::TafComment::~TafComment() | ||
| 25 | { | ||
| 26 | } | ||
| 27 | |||
| 28 | const Bu::String &Bu::TafComment::getText() const | ||
| 29 | { | ||
| 30 | return sText; | ||
| 31 | } | ||
| 32 | |||
| 33 | bool Bu::TafComment::isEOLStyle() const | ||
| 34 | { | ||
| 35 | return bEOL; | ||
| 36 | } | ||
| 37 | |||
| diff --git a/src/stable/tafcomment.h b/src/stable/tafcomment.h new file mode 100644 index 0000000..4efd548 --- /dev/null +++ b/src/stable/tafcomment.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_TAF_COMMENT_H | ||
| 9 | #define BU_TAF_COMMENT_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include "bu/tafnode.h" | ||
| 13 | |||
| 14 | namespace Bu | ||
| 15 | { | ||
| 16 | /** | ||
| 17 | * | ||
| 18 | *@ingroup Taf | ||
| 19 | */ | ||
| 20 | class TafComment : public TafNode | ||
| 21 | { | ||
| 22 | public: | ||
| 23 | TafComment( const Bu::TafComment &rSrc ); | ||
| 24 | TafComment( const Bu::String &sText, bool bEOL=false ); | ||
| 25 | virtual ~TafComment(); | ||
| 26 | |||
| 27 | const Bu::String &getText() const; | ||
| 28 | bool isEOLStyle() const; | ||
| 29 | |||
| 30 | private: | ||
| 31 | Bu::String sText; | ||
| 32 | bool bEOL; | ||
| 33 | }; | ||
| 34 | } | ||
| 35 | |||
| 36 | #endif | ||
| diff --git a/src/stable/tafgroup.cpp b/src/stable/tafgroup.cpp new file mode 100644 index 0000000..ee180c3 --- /dev/null +++ b/src/stable/tafgroup.cpp | |||
| @@ -0,0 +1,224 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/tafgroup.h" | ||
| 9 | #include "bu/tafproperty.h" | ||
| 10 | #include "bu/tafcomment.h" | ||
| 11 | |||
| 12 | Bu::TafGroup::TafGroup( const TafGroup &rSrc ) : | ||
| 13 | TafNode( typeGroup ), | ||
| 14 | sName( rSrc.sName ) | ||
| 15 | { | ||
| 16 | for( NodeList::const_iterator i = rSrc.lChildren.begin(); i; i++ ) | ||
| 17 | { | ||
| 18 | switch( (*i)->getType() ) | ||
| 19 | { | ||
| 20 | case typeGroup: | ||
| 21 | addChild( new TafGroup( *dynamic_cast<const TafGroup *>(*i) ) ); | ||
| 22 | break; | ||
| 23 | |||
| 24 | case typeProperty: | ||
| 25 | addChild( new TafProperty( *dynamic_cast<const TafProperty *>(*i) ) ); | ||
| 26 | break; | ||
| 27 | |||
| 28 | case typeComment: | ||
| 29 | addChild( new TafComment( *dynamic_cast<const TafComment *>(*i) ) ); | ||
| 30 | break; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | Bu::TafGroup::TafGroup( const Bu::String &sName ) : | ||
| 36 | TafNode( typeGroup ), | ||
| 37 | sName( sName ) | ||
| 38 | { | ||
| 39 | } | ||
| 40 | |||
| 41 | Bu::TafGroup::~TafGroup() | ||
| 42 | { | ||
| 43 | for( NodeList::iterator i = lChildren.begin(); i != lChildren.end(); i++ ) | ||
| 44 | { | ||
| 45 | delete (*i); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | const Bu::String &Bu::TafGroup::getName() const | ||
| 50 | { | ||
| 51 | return sName; | ||
| 52 | } | ||
| 53 | |||
| 54 | void Bu::TafGroup::setName( const Bu::String &sName ) | ||
| 55 | { | ||
| 56 | this->sName = sName; | ||
| 57 | } | ||
| 58 | |||
| 59 | Bu::TafNode *Bu::TafGroup::addChild( Bu::TafNode *pNode ) | ||
| 60 | { | ||
| 61 | switch( pNode->getType() ) | ||
| 62 | { | ||
| 63 | case typeGroup: | ||
| 64 | addChild( (Bu::TafGroup *)pNode ); | ||
| 65 | break; | ||
| 66 | |||
| 67 | case typeProperty: | ||
| 68 | addChild( (Bu::TafProperty *)pNode ); | ||
| 69 | break; | ||
| 70 | |||
| 71 | case typeComment: | ||
| 72 | addChild( (Bu::TafComment *)pNode ); | ||
| 73 | break; | ||
| 74 | } | ||
| 75 | |||
| 76 | return pNode; | ||
| 77 | } | ||
| 78 | |||
| 79 | Bu::TafGroup *Bu::TafGroup::addChild( TafGroup *pNode ) | ||
| 80 | { | ||
| 81 | TafGroup *pGroup = (TafGroup *)pNode; | ||
| 82 | if( !hChildren.has( pGroup->getName() ) ) | ||
| 83 | hChildren.insert( pGroup->getName(), GroupList() ); | ||
| 84 | hChildren.get( pGroup->getName() ).append( pGroup ); | ||
| 85 | lChildren.append( pNode ); | ||
| 86 | return pNode; | ||
| 87 | } | ||
| 88 | |||
| 89 | Bu::TafProperty *Bu::TafGroup::addChild( TafProperty *pNode ) | ||
| 90 | { | ||
| 91 | TafProperty *pProperty = (TafProperty *)pNode; | ||
| 92 | if( !hProp.has( pProperty->getName() ) ) | ||
| 93 | hProp.insert( pProperty->getName(), PropList() ); | ||
| 94 | hProp.get( pProperty->getName() ).append( pProperty->getValue() ); | ||
| 95 | lChildren.append( pNode ); | ||
| 96 | return pNode; | ||
| 97 | } | ||
| 98 | |||
| 99 | Bu::TafComment *Bu::TafGroup::addChild( TafComment *pNode ) | ||
| 100 | { | ||
| 101 | lChildren.append( pNode ); | ||
| 102 | return pNode; | ||
| 103 | } | ||
| 104 | |||
| 105 | Bu::TafGroup *Bu::TafGroup::addGroup( const Bu::String &sName ) | ||
| 106 | { | ||
| 107 | return addChild( new TafGroup( sName ) ); | ||
| 108 | } | ||
| 109 | |||
| 110 | Bu::TafProperty *Bu::TafGroup::addProperty( | ||
| 111 | const Bu::String &sName, const Bu::String &sValue ) | ||
| 112 | { | ||
| 113 | return addChild( new TafProperty( sName, sValue ) ); | ||
| 114 | } | ||
| 115 | |||
| 116 | bool Bu::TafGroup::hasChild( const Bu::String &sName ) const | ||
| 117 | { | ||
| 118 | return hChildren.has( sName ); | ||
| 119 | } | ||
| 120 | |||
| 121 | const Bu::TafGroup::GroupList &Bu::TafGroup::getChildren( const Bu::String &sName ) const | ||
| 122 | { | ||
| 123 | try { | ||
| 124 | return hChildren.get( sName ); | ||
| 125 | } catch( Bu::HashException &e ) | ||
| 126 | { | ||
| 127 | throw Bu::TafException("No children of group \"%s\" match \"%s\".", | ||
| 128 | this->sName.getStr(), sName.getStr() ); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | const Bu::TafGroup::NodeList &Bu::TafGroup::getChildren() const | ||
| 133 | { | ||
| 134 | return lChildren; | ||
| 135 | } | ||
| 136 | |||
| 137 | const Bu::TafGroup *Bu::TafGroup::getChild( const Bu::String &sName ) const | ||
| 138 | { | ||
| 139 | try { | ||
| 140 | return hChildren.get( sName ).first(); | ||
| 141 | } catch( Bu::HashException &e ) | ||
| 142 | { | ||
| 143 | throw Bu::TafException("No children of group \"%s\" match \"%s\".", | ||
| 144 | this->sName.getStr(), sName.getStr() ); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | bool Bu::TafGroup::hasProperty( const Bu::String &sName ) const | ||
| 149 | { | ||
| 150 | return hProp.has( sName ); | ||
| 151 | } | ||
| 152 | |||
| 153 | const Bu::TafGroup::PropList &Bu::TafGroup::getProperties( const Bu::String &sName ) const | ||
| 154 | { | ||
| 155 | try { | ||
| 156 | return hProp.get( sName ); | ||
| 157 | } catch( Bu::HashException &e ) | ||
| 158 | { | ||
| 159 | throw Bu::TafException("No properties of group \"%s\" match \"%s\".", | ||
| 160 | this->sName.getStr(), sName.getStr() ); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | const Bu::String &Bu::TafGroup::getProperty( const Bu::String &sName ) const | ||
| 165 | { | ||
| 166 | try { | ||
| 167 | return hProp.get( sName ).first(); | ||
| 168 | } catch( Bu::HashException &e ) | ||
| 169 | { | ||
| 170 | throw Bu::TafException("No properties of group \"%s\" match \"%s\".", | ||
| 171 | this->sName.getStr(), sName.getStr() ); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | const Bu::String &Bu::TafGroup::getProperty( const Bu::String &sName, | ||
| 176 | const Bu::String &sDef ) const | ||
| 177 | { | ||
| 178 | try | ||
| 179 | { | ||
| 180 | return hProp.get( sName ).first(); | ||
| 181 | } | ||
| 182 | catch( Bu::HashException &e ) | ||
| 183 | { | ||
| 184 | return sDef; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | const Bu::TafGroup *Bu::TafGroup::getChildByPath( | ||
| 189 | const Bu::String &sPath ) const | ||
| 190 | { | ||
| 191 | return getChildByPath( sPath.split('/') ); | ||
| 192 | } | ||
| 193 | |||
| 194 | const Bu::TafGroup *Bu::TafGroup::getChildByPath( Bu::StrList lPath ) const | ||
| 195 | { | ||
| 196 | const Bu::TafGroup *cur = this; | ||
| 197 | |||
| 198 | for( Bu::StrList::const_iterator i = lPath.begin(); i; i++ ) | ||
| 199 | { | ||
| 200 | cur = cur->getChild( *i ); | ||
| 201 | } | ||
| 202 | |||
| 203 | return cur; | ||
| 204 | } | ||
| 205 | |||
| 206 | const Bu::String &Bu::TafGroup::getByPath( const Bu::String &sPath ) const | ||
| 207 | { | ||
| 208 | return getByPath( sPath.split('/') ); | ||
| 209 | } | ||
| 210 | |||
| 211 | const Bu::String &Bu::TafGroup::getByPath( Bu::StrList lPath ) const | ||
| 212 | { | ||
| 213 | const Bu::TafGroup *cur = this; | ||
| 214 | |||
| 215 | for( Bu::StrList::const_iterator i = lPath.begin(); i; i++ ) | ||
| 216 | { | ||
| 217 | if( !(i+1) ) | ||
| 218 | break; | ||
| 219 | cur = cur->getChild( *i ); | ||
| 220 | } | ||
| 221 | |||
| 222 | return cur->getProperty( lPath.last() ); | ||
| 223 | } | ||
| 224 | |||
| diff --git a/src/stable/tafgroup.h b/src/stable/tafgroup.h new file mode 100644 index 0000000..119e827 --- /dev/null +++ b/src/stable/tafgroup.h | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_TAF_GROUP_H | ||
| 9 | #define BU_TAF_GROUP_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include "bu/tafnode.h" | ||
| 13 | #include "bu/string.h" | ||
| 14 | #include "bu/hash.h" | ||
| 15 | #include "bu/list.h" | ||
| 16 | |||
| 17 | namespace Bu | ||
| 18 | { | ||
| 19 | typedef Bu::List<Bu::String> StrList; | ||
| 20 | class TafProperty; | ||
| 21 | class TafComment; | ||
| 22 | /** | ||
| 23 | * | ||
| 24 | *@ingroup Taf | ||
| 25 | */ | ||
| 26 | class TafGroup : public TafNode | ||
| 27 | { | ||
| 28 | public: | ||
| 29 | typedef Bu::List<Bu::String> PropList; | ||
| 30 | typedef Bu::Hash<Bu::String, PropList> PropHash; | ||
| 31 | typedef Bu::List<class Bu::TafGroup *> GroupList; | ||
| 32 | typedef Bu::Hash<Bu::String, GroupList> GroupHash; | ||
| 33 | typedef Bu::List<class Bu::TafNode *> NodeList; | ||
| 34 | |||
| 35 | TafGroup( const TafGroup &rSrc ); | ||
| 36 | TafGroup( const Bu::String &sName ); | ||
| 37 | virtual ~TafGroup(); | ||
| 38 | |||
| 39 | const Bu::String &getName() const; | ||
| 40 | void setName( const Bu::String &sName ); | ||
| 41 | |||
| 42 | bool hasProperty( const Bu::String &sName ) const; | ||
| 43 | const Bu::String &getProperty( const Bu::String &sName ) const; | ||
| 44 | const Bu::String &getProperty( const Bu::String &sName, | ||
| 45 | const Bu::String &sDef ) const; | ||
| 46 | const PropList &getProperties( const Bu::String &sName ) const; | ||
| 47 | bool hasChild( const Bu::String &sName ) const; | ||
| 48 | const TafGroup *getChild( const Bu::String &sName ) const; | ||
| 49 | const GroupList &getChildren( const Bu::String &sName ) const; | ||
| 50 | TafNode *addChild( TafNode *pNode ); | ||
| 51 | TafGroup *addChild( TafGroup *pNode ); | ||
| 52 | TafProperty *addChild( TafProperty *pNode ); | ||
| 53 | TafComment *addChild( TafComment *pNode ); | ||
| 54 | TafGroup *addGroup( const Bu::String &sName ); | ||
| 55 | TafProperty *addProperty( | ||
| 56 | const Bu::String &sName, const Bu::String &sValue ); | ||
| 57 | const NodeList &getChildren() const; | ||
| 58 | const TafGroup *getChildByPath( const Bu::String &sPath ) const; | ||
| 59 | const TafGroup *getChildByPath( StrList lPath ) const; | ||
| 60 | const Bu::String &getByPath( const Bu::String &sPath ) const; | ||
| 61 | const Bu::String &getByPath( StrList lPath ) const; | ||
| 62 | |||
| 63 | private: | ||
| 64 | Bu::String sName; | ||
| 65 | PropHash hProp; | ||
| 66 | GroupHash hChildren; | ||
| 67 | NodeList lChildren; | ||
| 68 | }; | ||
| 69 | } | ||
| 70 | |||
| 71 | #endif | ||
| diff --git a/src/stable/tafnode.cpp b/src/stable/tafnode.cpp new file mode 100644 index 0000000..0757a46 --- /dev/null +++ b/src/stable/tafnode.cpp | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/tafnode.h" | ||
| 9 | |||
| 10 | namespace Bu { subExceptionDef( TafException ) } | ||
| 11 | |||
| 12 | Bu::TafNode::TafNode( NodeType eType ) : | ||
| 13 | eType( eType ) | ||
| 14 | { | ||
| 15 | } | ||
| 16 | |||
| 17 | Bu::TafNode::~TafNode() | ||
| 18 | { | ||
| 19 | } | ||
| 20 | |||
| 21 | Bu::TafNode::NodeType Bu::TafNode::getType() const | ||
| 22 | { | ||
| 23 | return eType; | ||
| 24 | } | ||
| 25 | |||
| diff --git a/src/stable/tafnode.h b/src/stable/tafnode.h new file mode 100644 index 0000000..d7a9159 --- /dev/null +++ b/src/stable/tafnode.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_TAF_NODE_H | ||
| 9 | #define BU_TAF_NODE_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include "bu/string.h" | ||
| 13 | #include "bu/hash.h" | ||
| 14 | #include "bu/exceptionbase.h" | ||
| 15 | |||
| 16 | namespace Bu | ||
| 17 | { | ||
| 18 | subExceptionDecl( TafException ); | ||
| 19 | /** | ||
| 20 | * | ||
| 21 | *@ingroup Taf | ||
| 22 | */ | ||
| 23 | class TafNode | ||
| 24 | { | ||
| 25 | public: | ||
| 26 | enum NodeType | ||
| 27 | { | ||
| 28 | typeGroup, | ||
| 29 | typeProperty, | ||
| 30 | typeComment | ||
| 31 | }; | ||
| 32 | |||
| 33 | public: | ||
| 34 | TafNode( NodeType eType ); | ||
| 35 | virtual ~TafNode(); | ||
| 36 | |||
| 37 | NodeType getType() const; | ||
| 38 | |||
| 39 | private: | ||
| 40 | NodeType eType; | ||
| 41 | }; | ||
| 42 | } | ||
| 43 | |||
| 44 | #endif | ||
| diff --git a/src/stable/tafproperty.cpp b/src/stable/tafproperty.cpp new file mode 100644 index 0000000..4ef5c24 --- /dev/null +++ b/src/stable/tafproperty.cpp | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/tafproperty.h" | ||
| 9 | |||
| 10 | Bu::TafProperty::TafProperty( const Bu::TafProperty &rSrc ) : | ||
| 11 | TafNode( typeProperty ), | ||
| 12 | sName( rSrc.sName ), | ||
| 13 | sValue( rSrc.sValue ) | ||
| 14 | { | ||
| 15 | } | ||
| 16 | |||
| 17 | Bu::TafProperty::TafProperty( const Bu::String &sName, const Bu::String &sValue ) : | ||
| 18 | TafNode( typeProperty ), | ||
| 19 | sName( sName ), | ||
| 20 | sValue( sValue ) | ||
| 21 | { | ||
| 22 | } | ||
| 23 | |||
| 24 | Bu::TafProperty::~TafProperty() | ||
| 25 | { | ||
| 26 | } | ||
| 27 | |||
| 28 | const Bu::String &Bu::TafProperty::getName() const | ||
| 29 | { | ||
| 30 | return sName; | ||
| 31 | } | ||
| 32 | |||
| 33 | const Bu::String &Bu::TafProperty::getValue() const | ||
| 34 | { | ||
| 35 | return sValue; | ||
| 36 | } | ||
| 37 | |||
| diff --git a/src/stable/tafproperty.h b/src/stable/tafproperty.h new file mode 100644 index 0000000..7091de5 --- /dev/null +++ b/src/stable/tafproperty.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_TAF_PROPERTY_H | ||
| 9 | #define BU_TAF_PROPERTY_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include "bu/tafnode.h" | ||
| 13 | |||
| 14 | namespace Bu | ||
| 15 | { | ||
| 16 | /** | ||
| 17 | * | ||
| 18 | *@ingroup Taf | ||
| 19 | */ | ||
| 20 | class TafProperty : public TafNode | ||
| 21 | { | ||
| 22 | public: | ||
| 23 | TafProperty( const Bu::TafProperty &rSrc ); | ||
| 24 | TafProperty( const Bu::String &sName, const Bu::String &sValue ); | ||
| 25 | virtual ~TafProperty(); | ||
| 26 | |||
| 27 | const Bu::String &getName() const; | ||
| 28 | const Bu::String &getValue() const; | ||
| 29 | |||
| 30 | private: | ||
| 31 | Bu::String sName; | ||
| 32 | Bu::String sValue; | ||
| 33 | }; | ||
| 34 | } | ||
| 35 | |||
| 36 | #endif | ||
| diff --git a/src/stable/tafreader.cpp b/src/stable/tafreader.cpp new file mode 100644 index 0000000..6708c8c --- /dev/null +++ b/src/stable/tafreader.cpp | |||
| @@ -0,0 +1,252 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/taf.h" | ||
| 9 | #include "bu/string.h" | ||
| 10 | #include "bu/stream.h" | ||
| 11 | |||
| 12 | #include <stdlib.h> | ||
| 13 | |||
| 14 | using namespace Bu; | ||
| 15 | |||
| 16 | Bu::TafReader::TafReader( Bu::Stream &sIn ) : | ||
| 17 | c( 0 ), | ||
| 18 | la( 0 ), | ||
| 19 | sIn( sIn ), | ||
| 20 | iLine( 1 ), iCol( -1 ) | ||
| 21 | { | ||
| 22 | next(); next(); | ||
| 23 | } | ||
| 24 | |||
| 25 | Bu::TafReader::~TafReader() | ||
| 26 | { | ||
| 27 | |||
| 28 | } | ||
| 29 | |||
| 30 | Bu::TafGroup *Bu::TafReader::readGroup() | ||
| 31 | { | ||
| 32 | ws(); | ||
| 33 | if( c != '{' ) | ||
| 34 | throw TafException("%d:%d: Expected '{' got '%c'.", iLine, iCol, c ); | ||
| 35 | next(); | ||
| 36 | ws(); | ||
| 37 | String sName = readStr(); | ||
| 38 | TafGroup *pGroup = new TafGroup( sName ); | ||
| 39 | try | ||
| 40 | { | ||
| 41 | ws(); | ||
| 42 | if( c != ':' ) | ||
| 43 | throw TafException("%d:%d: Expected ':' got '%c'.", | ||
| 44 | iLine, iCol, c ); | ||
| 45 | next(); | ||
| 46 | //printf("Node[%s]:\n", sName.getStr() ); | ||
| 47 | |||
| 48 | groupContent( pGroup ); | ||
| 49 | |||
| 50 | if( c != '}' ) | ||
| 51 | throw TafException("%d:%d: Expected '}' got '%c'.", | ||
| 52 | iLine, iCol, c ); | ||
| 53 | |||
| 54 | //next(); | ||
| 55 | |||
| 56 | return pGroup; | ||
| 57 | } | ||
| 58 | catch(...) | ||
| 59 | { | ||
| 60 | delete pGroup; | ||
| 61 | throw; | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | void Bu::TafReader::groupContent( Bu::TafGroup *pGroup ) | ||
| 66 | { | ||
| 67 | for(;;) | ||
| 68 | { | ||
| 69 | ws(); | ||
| 70 | if( c == '{' ) | ||
| 71 | { | ||
| 72 | pGroup->addChild( readGroup() ); | ||
| 73 | next(); | ||
| 74 | } | ||
| 75 | else if( c == '}' ) | ||
| 76 | return; | ||
| 77 | else if( c == '/' && la == '*' ) | ||
| 78 | pGroup->addChild( readComment() ); | ||
| 79 | else if( c == '/' && la == '/' ) | ||
| 80 | pGroup->addChild( readComment( true ) ); | ||
| 81 | else if( c == ':' ) | ||
| 82 | throw TafException("%d:%d: Encountered stray ':' in taf stream.", | ||
| 83 | iLine, iCol ); | ||
| 84 | else | ||
| 85 | pGroup->addChild( readProperty() ); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | Bu::TafProperty *Bu::TafReader::readProperty() | ||
| 90 | { | ||
| 91 | String sName = readStr(); | ||
| 92 | ws(); | ||
| 93 | if( c != '=' ) | ||
| 94 | { | ||
| 95 | //printf(" %s (true)\n", sName.getStr() ); | ||
| 96 | return new Bu::TafProperty( "", sName ); | ||
| 97 | } | ||
| 98 | next(); | ||
| 99 | String sValue = readStr(); | ||
| 100 | return new Bu::TafProperty( sName, sValue ); | ||
| 101 | //printf(" %s = %s\n", sName.getStr(), sValue.getStr() ); | ||
| 102 | } | ||
| 103 | |||
| 104 | Bu::TafComment *Bu::TafReader::readComment( bool bEOL ) | ||
| 105 | { | ||
| 106 | String sCmnt; | ||
| 107 | next(); | ||
| 108 | if( bEOL ) | ||
| 109 | { | ||
| 110 | for(;;) | ||
| 111 | { | ||
| 112 | next(); | ||
| 113 | if( c == '\n' && la == '\r' ) | ||
| 114 | { | ||
| 115 | next(); next(); | ||
| 116 | break; | ||
| 117 | } | ||
| 118 | else if( c == '\n' || c == '\r' ) | ||
| 119 | { | ||
| 120 | next(); | ||
| 121 | break; | ||
| 122 | } | ||
| 123 | sCmnt += c; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | else | ||
| 127 | { | ||
| 128 | for(;;) | ||
| 129 | { | ||
| 130 | next(); | ||
| 131 | if( c == '*' && la == '/' ) | ||
| 132 | { | ||
| 133 | next(); next(); | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | sCmnt += c; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | return new TafComment( sCmnt, bEOL ); | ||
| 141 | } | ||
| 142 | |||
| 143 | Bu::String Bu::TafReader::readStr() | ||
| 144 | { | ||
| 145 | ws(); | ||
| 146 | String s; | ||
| 147 | if( c == '"' ) | ||
| 148 | { | ||
| 149 | next(); | ||
| 150 | for(;;) | ||
| 151 | { | ||
| 152 | if( c == '\\' ) | ||
| 153 | { | ||
| 154 | next(); | ||
| 155 | if( c == 'x' ) | ||
| 156 | { | ||
| 157 | char code[3]={'\0','\0','\0'}; | ||
| 158 | next(); | ||
| 159 | code[0] = c; | ||
| 160 | next(); | ||
| 161 | code[1] = c; | ||
| 162 | c = (unsigned char)strtol( code, NULL, 16 ); | ||
| 163 | } | ||
| 164 | else if( c == '"' ) | ||
| 165 | c = '"'; | ||
| 166 | else if( c == '\\' ) | ||
| 167 | c = '\\'; | ||
| 168 | else if( c == 'n' ) | ||
| 169 | c = '\n'; | ||
| 170 | else if( c == 't' ) | ||
| 171 | c = '\t'; | ||
| 172 | else | ||
| 173 | throw TafException("%d:%d: Invalid escape sequence '\\%c'.", | ||
| 174 | iLine, iCol, c ); | ||
| 175 | } | ||
| 176 | else if( c == '"' ) | ||
| 177 | break; | ||
| 178 | s += c; | ||
| 179 | next(); | ||
| 180 | } | ||
| 181 | next(); | ||
| 182 | } | ||
| 183 | else | ||
| 184 | { | ||
| 185 | for(;;) | ||
| 186 | { | ||
| 187 | if( isws() || c == '}' || c == '{' || c == ':' || c == '=' ) | ||
| 188 | break; | ||
| 189 | s += c; | ||
| 190 | next(); | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | return s; | ||
| 195 | } | ||
| 196 | |||
| 197 | void Bu::TafReader::ws() | ||
| 198 | { | ||
| 199 | for(;;) | ||
| 200 | { | ||
| 201 | if( !isws() ) | ||
| 202 | return; | ||
| 203 | |||
| 204 | next(); | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | bool Bu::TafReader::isws() | ||
| 209 | { | ||
| 210 | return (c == ' ' || c == '\t' || c == '\n' || c == '\r'); | ||
| 211 | } | ||
| 212 | |||
| 213 | void Bu::TafReader::next() | ||
| 214 | { | ||
| 215 | if( c == '\n' ) | ||
| 216 | { | ||
| 217 | iLine++; | ||
| 218 | iCol = 1; | ||
| 219 | } | ||
| 220 | else | ||
| 221 | iCol++; | ||
| 222 | if( c == '}' ) | ||
| 223 | { | ||
| 224 | rawread( &c ); | ||
| 225 | if( c != '}' ) | ||
| 226 | rawread( &la ); | ||
| 227 | } | ||
| 228 | else | ||
| 229 | { | ||
| 230 | c = la; | ||
| 231 | if( c != '}' ) | ||
| 232 | rawread( &la ); | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | void Bu::TafReader::rawread( char *c ) | ||
| 237 | { | ||
| 238 | if( sIn.read( c, 1 ) < 1 ) | ||
| 239 | { | ||
| 240 | if( sIn.isEos() ) | ||
| 241 | { | ||
| 242 | throw TafException("%d:%d: Premature end of stream.", | ||
| 243 | iLine, iCol, c ); | ||
| 244 | } | ||
| 245 | else | ||
| 246 | { | ||
| 247 | throw TafException("%d:%d: No data read, but not end of stream?", | ||
| 248 | iLine, iCol, c ); | ||
| 249 | } | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| diff --git a/src/stable/tafreader.h b/src/stable/tafreader.h new file mode 100644 index 0000000..10ebfc0 --- /dev/null +++ b/src/stable/tafreader.h | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_TAF_READER_H | ||
| 9 | #define BU_TAF_READER_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include "bu/string.h" | ||
| 13 | |||
| 14 | namespace Bu | ||
| 15 | { | ||
| 16 | class TafNode; | ||
| 17 | class TafGroup; | ||
| 18 | class TafProperty; | ||
| 19 | class TafComment; | ||
| 20 | class Stream; | ||
| 21 | |||
| 22 | /** | ||
| 23 | * | ||
| 24 | *@ingroup Taf | ||
| 25 | */ | ||
| 26 | class TafReader | ||
| 27 | { | ||
| 28 | public: | ||
| 29 | TafReader( Bu::Stream &sIn ); | ||
| 30 | virtual ~TafReader(); | ||
| 31 | |||
| 32 | Bu::TafGroup *readGroup(); | ||
| 33 | |||
| 34 | private: | ||
| 35 | void groupContent( Bu::TafGroup *pNode ); | ||
| 36 | Bu::TafProperty *readProperty(); | ||
| 37 | Bu::TafComment *readComment( bool bEOL=false ); | ||
| 38 | void ws(); | ||
| 39 | bool isws(); | ||
| 40 | void next(); | ||
| 41 | Bu::String readStr(); | ||
| 42 | void rawread( char *c ); | ||
| 43 | char c, la; | ||
| 44 | Bu::Stream &sIn; | ||
| 45 | int iLine, iCol; | ||
| 46 | }; | ||
| 47 | } | ||
| 48 | |||
| 49 | #endif | ||
| diff --git a/src/stable/tafwriter.cpp b/src/stable/tafwriter.cpp new file mode 100644 index 0000000..b24bd1e --- /dev/null +++ b/src/stable/tafwriter.cpp | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/taf.h" | ||
| 9 | #include "bu/stream.h" | ||
| 10 | |||
| 11 | Bu::TafWriter::TafWriter( Bu::Stream &sOut ) : | ||
| 12 | sOut( sOut ), | ||
| 13 | iDepth( 0 ) | ||
| 14 | { | ||
| 15 | } | ||
| 16 | |||
| 17 | Bu::TafWriter::~TafWriter() | ||
| 18 | { | ||
| 19 | } | ||
| 20 | |||
| 21 | void Bu::TafWriter::ident() | ||
| 22 | { | ||
| 23 | for( int j = 0; j < iDepth; j++ ) | ||
| 24 | sOut.write(" ", 4 ); | ||
| 25 | } | ||
| 26 | |||
| 27 | void Bu::TafWriter::writeGroup( const Bu::TafGroup *pRoot ) | ||
| 28 | { | ||
| 29 | ident(); | ||
| 30 | sOut.write("{", 1 ); | ||
| 31 | if( pRoot->getName().isSet() ) | ||
| 32 | writeString( pRoot->getName() ); | ||
| 33 | sOut.write(":\n", 2 ); | ||
| 34 | iDepth++; | ||
| 35 | const Bu::TafGroup::NodeList &nl = pRoot->getChildren(); | ||
| 36 | for( Bu::TafGroup::NodeList::const_iterator i = nl.begin(); i != nl.end(); i++ ) | ||
| 37 | { | ||
| 38 | switch( (*i)->getType() ) | ||
| 39 | { | ||
| 40 | case Bu::TafNode::typeGroup: | ||
| 41 | writeGroup( (Bu::TafGroup *)(*i) ); | ||
| 42 | break; | ||
| 43 | |||
| 44 | case Bu::TafNode::typeProperty: | ||
| 45 | writeProperty( (Bu::TafProperty *)(*i) ); | ||
| 46 | break; | ||
| 47 | |||
| 48 | case Bu::TafNode::typeComment: | ||
| 49 | writeComment( (Bu::TafComment *)(*i) ); | ||
| 50 | break; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | iDepth--; | ||
| 54 | ident(); | ||
| 55 | sOut.write("}\n", 2 ); | ||
| 56 | } | ||
| 57 | |||
| 58 | void Bu::TafWriter::writeProperty( const Bu::TafProperty *pProp ) | ||
| 59 | { | ||
| 60 | ident(); | ||
| 61 | if( !pProp->getName().isEmpty() ) | ||
| 62 | { | ||
| 63 | writeString( pProp->getName() ); | ||
| 64 | sOut.write("=", 1 ); | ||
| 65 | writeString( pProp->getValue() ); | ||
| 66 | } | ||
| 67 | else | ||
| 68 | { | ||
| 69 | writeString( pProp->getValue() ); | ||
| 70 | } | ||
| 71 | sOut.write("\n", 1 ); | ||
| 72 | } | ||
| 73 | |||
| 74 | void Bu::TafWriter::writeComment( const Bu::TafComment *pComment ) | ||
| 75 | { | ||
| 76 | ident(); | ||
| 77 | if( pComment->isEOLStyle() ) | ||
| 78 | { | ||
| 79 | sOut.write("//", 2 ); | ||
| 80 | sOut.write( pComment->getText().getStr(), pComment->getText().getSize() ); | ||
| 81 | sOut.write("\n", 1 ); | ||
| 82 | } | ||
| 83 | else | ||
| 84 | { | ||
| 85 | sOut.write("/*", 2 ); | ||
| 86 | sOut.write( pComment->getText().getStr(), pComment->getText().getSize() ); | ||
| 87 | sOut.write("*/ ", 3 ); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | void Bu::TafWriter::writeString( const Bu::String &str ) | ||
| 92 | { | ||
| 93 | sOut.write("\"", 1 ); | ||
| 94 | for( Bu::String::const_iterator s = str.begin(); s != str.end(); s++ ) | ||
| 95 | { | ||
| 96 | if( *s == '\"' ) | ||
| 97 | sOut.write("\\\"", 2 ); | ||
| 98 | else if( *s == '\\' ) | ||
| 99 | sOut.write("\\\\", 2 ); | ||
| 100 | else if( *s < 32 || *s > 126 ) | ||
| 101 | { | ||
| 102 | char buf[5]; | ||
| 103 | sprintf( buf, "\\x%02X", (unsigned char)*s ); | ||
| 104 | sOut.write(buf, 4 ); | ||
| 105 | } | ||
| 106 | else | ||
| 107 | { | ||
| 108 | const char buf = *s; | ||
| 109 | sOut.write( &buf, 1 ); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | sOut.write("\"", 1 ); | ||
| 113 | } | ||
| 114 | |||
| diff --git a/src/stable/tafwriter.h b/src/stable/tafwriter.h new file mode 100644 index 0000000..3fd71de --- /dev/null +++ b/src/stable/tafwriter.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_TAF_WRITER_H | ||
| 9 | #define BU_TAF_WRITER_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | |||
| 13 | #include "bu/string.h" | ||
| 14 | |||
| 15 | namespace Bu | ||
| 16 | { | ||
| 17 | class Stream; | ||
| 18 | class TafNode; | ||
| 19 | class TafGroup; | ||
| 20 | class TafProperty; | ||
| 21 | class TafComment; | ||
| 22 | |||
| 23 | /** | ||
| 24 | * | ||
| 25 | *@ingroup Taf | ||
| 26 | */ | ||
| 27 | class TafWriter | ||
| 28 | { | ||
| 29 | public: | ||
| 30 | TafWriter( Bu::Stream &sOut ); | ||
| 31 | virtual ~TafWriter(); | ||
| 32 | |||
| 33 | void writeGroup( const Bu::TafGroup *pRoot ); | ||
| 34 | |||
| 35 | private: | ||
| 36 | void writeProperty( const Bu::TafProperty *pProp ); | ||
| 37 | void writeComment( const Bu::TafComment *pComment ); | ||
| 38 | void writeString( const Bu::String &str ); | ||
| 39 | void ident(); | ||
| 40 | Bu::Stream &sOut; | ||
| 41 | int iDepth; | ||
| 42 | }; | ||
| 43 | } | ||
| 44 | |||
| 45 | #endif | ||
| diff --git a/src/stable/tcpserversocket.cpp b/src/stable/tcpserversocket.cpp new file mode 100644 index 0000000..a2fe6b4 --- /dev/null +++ b/src/stable/tcpserversocket.cpp | |||
| @@ -0,0 +1,249 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef WIN32 | ||
| 9 | #include <sys/socket.h> | ||
| 10 | #include <netinet/in.h> | ||
| 11 | #include <netdb.h> | ||
| 12 | #include <arpa/inet.h> | ||
| 13 | #endif | ||
| 14 | |||
| 15 | #include <time.h> | ||
| 16 | #include <string.h> | ||
| 17 | #include <stdio.h> | ||
| 18 | #include <errno.h> | ||
| 19 | #include <stdlib.h> | ||
| 20 | #include <unistd.h> | ||
| 21 | #include <sys/types.h> | ||
| 22 | //#include <termios.h> | ||
| 23 | #include <fcntl.h> | ||
| 24 | #include "bu/tcpserversocket.h" | ||
| 25 | |||
| 26 | #include "bu/config.h" | ||
| 27 | |||
| 28 | namespace Bu { subExceptionDef( TcpServerSocketException ) } | ||
| 29 | |||
| 30 | Bu::TcpServerSocket::TcpServerSocket( int nPort, int nPoolSize ) : | ||
| 31 | nPort( nPort ) | ||
| 32 | { | ||
| 33 | #ifdef WIN32 | ||
| 34 | Bu::Winsock2::getInstance(); | ||
| 35 | #endif | ||
| 36 | |||
| 37 | /* Create the socket and set it up to accept connections. */ | ||
| 38 | struct sockaddr_in name; | ||
| 39 | |||
| 40 | /* Give the socket a name. */ | ||
| 41 | name.sin_family = AF_INET; | ||
| 42 | name.sin_port = bu_htons( nPort ); | ||
| 43 | |||
| 44 | // I think this specifies who we will accept connections from, | ||
| 45 | // a good thing to make configurable later on | ||
| 46 | name.sin_addr.s_addr = bu_htonl( INADDR_ANY ); | ||
| 47 | |||
| 48 | startServer( name, nPoolSize ); | ||
| 49 | } | ||
| 50 | |||
| 51 | Bu::TcpServerSocket::TcpServerSocket(const String &sAddr,int nPort, int nPoolSize) : | ||
| 52 | nPort( nPort ) | ||
| 53 | { | ||
| 54 | #ifdef WIN32 | ||
| 55 | Bu::Winsock2::getInstance(); | ||
| 56 | #endif | ||
| 57 | |||
| 58 | /* Create the socket and set it up to accept connections. */ | ||
| 59 | struct sockaddr_in name; | ||
| 60 | |||
| 61 | /* Give the socket a name. */ | ||
| 62 | name.sin_family = AF_INET; | ||
| 63 | |||
| 64 | name.sin_port = bu_htons( nPort ); | ||
| 65 | |||
| 66 | #ifdef WIN32 | ||
| 67 | name.sin_addr.s_addr = bu_inet_addr( sAddr.getStr() ); | ||
| 68 | #else | ||
| 69 | inet_aton( sAddr.getStr(), &name.sin_addr ); | ||
| 70 | #endif | ||
| 71 | |||
| 72 | startServer( name, nPoolSize ); | ||
| 73 | } | ||
| 74 | |||
| 75 | Bu::TcpServerSocket::TcpServerSocket( int nServer, bool bInit, int nPoolSize ) : | ||
| 76 | nServer( nServer ), | ||
| 77 | nPort( 0 ) | ||
| 78 | { | ||
| 79 | #ifdef WIN32 | ||
| 80 | Bu::Winsock2::getInstance(); | ||
| 81 | #endif | ||
| 82 | |||
| 83 | if( bInit ) | ||
| 84 | { | ||
| 85 | struct sockaddr name; | ||
| 86 | socklen_t namelen = sizeof(name); | ||
| 87 | getpeername( nServer, &name, &namelen ); | ||
| 88 | |||
| 89 | initServer( *((sockaddr_in *)&name), nPoolSize ); | ||
| 90 | } | ||
| 91 | else | ||
| 92 | { | ||
| 93 | FD_ZERO( &fdActive ); | ||
| 94 | FD_SET( nServer, &fdActive ); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | Bu::TcpServerSocket::TcpServerSocket( const TcpServerSocket &rSrc ) | ||
| 99 | { | ||
| 100 | #ifdef WIN32 | ||
| 101 | Bu::Winsock2::getInstance(); | ||
| 102 | #endif | ||
| 103 | |||
| 104 | nServer = dup( rSrc.nServer ); | ||
| 105 | nPort = rSrc.nPort; | ||
| 106 | FD_ZERO( &fdActive ); | ||
| 107 | FD_SET( nServer, &fdActive ); | ||
| 108 | } | ||
| 109 | |||
| 110 | Bu::TcpServerSocket::~TcpServerSocket() | ||
| 111 | { | ||
| 112 | if( nServer > -1 ) | ||
| 113 | ::close( nServer ); | ||
| 114 | } | ||
| 115 | |||
| 116 | void Bu::TcpServerSocket::startServer( struct sockaddr_in &name, int nPoolSize ) | ||
| 117 | { | ||
| 118 | /* Create the socket. */ | ||
| 119 | nServer = bu_socket( PF_INET, SOCK_STREAM, 0 ); | ||
| 120 | |||
| 121 | if( nServer < 0 ) | ||
| 122 | { | ||
| 123 | throw Bu::TcpServerSocketException("Couldn't create a listen socket."); | ||
| 124 | } | ||
| 125 | |||
| 126 | int opt = 1; | ||
| 127 | bu_setsockopt( | ||
| 128 | nServer, | ||
| 129 | SOL_SOCKET, | ||
| 130 | SO_REUSEADDR, | ||
| 131 | (char *)&opt, | ||
| 132 | sizeof( opt ) | ||
| 133 | ); | ||
| 134 | |||
| 135 | initServer( name, nPoolSize ); | ||
| 136 | } | ||
| 137 | |||
| 138 | void Bu::TcpServerSocket::initServer( struct sockaddr_in &name, int nPoolSize ) | ||
| 139 | { | ||
| 140 | if( bu_bind( nServer, (struct sockaddr *) &name, sizeof(name) ) < 0 ) | ||
| 141 | { | ||
| 142 | throw Bu::TcpServerSocketException("Couldn't bind to the listen socket."); | ||
| 143 | } | ||
| 144 | |||
| 145 | if( bu_listen( nServer, nPoolSize ) < 0 ) | ||
| 146 | { | ||
| 147 | throw Bu::TcpServerSocketException( | ||
| 148 | "Couldn't begin listening to the server socket." | ||
| 149 | ); | ||
| 150 | } | ||
| 151 | |||
| 152 | FD_ZERO( &fdActive ); | ||
| 153 | /* Initialize the set of active sockets. */ | ||
| 154 | FD_SET( nServer, &fdActive ); | ||
| 155 | } | ||
| 156 | |||
| 157 | int Bu::TcpServerSocket::getSocket() | ||
| 158 | { | ||
| 159 | return nServer; | ||
| 160 | } | ||
| 161 | |||
| 162 | int Bu::TcpServerSocket::accept( int nTimeoutSec, int nTimeoutUSec ) | ||
| 163 | { | ||
| 164 | fd_set fdRead = fdActive; | ||
| 165 | |||
| 166 | struct timeval xT; | ||
| 167 | |||
| 168 | xT.tv_sec = nTimeoutSec; | ||
| 169 | xT.tv_usec = nTimeoutUSec; | ||
| 170 | |||
| 171 | if( TEMP_FAILURE_RETRY( | ||
| 172 | bu_select( nServer+1, &fdRead, NULL, NULL, &xT )) < 0 ) | ||
| 173 | { | ||
| 174 | throw Bu::TcpServerSocketException( | ||
| 175 | "Error scanning for new connections: %s", strerror( errno ) | ||
| 176 | ); | ||
| 177 | } | ||
| 178 | |||
| 179 | if( FD_ISSET( nServer, &fdRead ) ) | ||
| 180 | { | ||
| 181 | struct sockaddr_in clientname; | ||
| 182 | socklen_t size; | ||
| 183 | int nClient; | ||
| 184 | |||
| 185 | size = sizeof( clientname ); | ||
| 186 | #ifdef WIN32 | ||
| 187 | nClient = bu_accept( nServer, (struct sockaddr *)&clientname, &size); | ||
| 188 | #else /* not-WIN32 */ | ||
| 189 | #ifdef __CYGWIN__ | ||
| 190 | nClient = ::accept( nServer, (struct sockaddr *)&clientname, | ||
| 191 | (int *)&size | ||
| 192 | ); | ||
| 193 | #else /* not-cygwin */ | ||
| 194 | #ifdef __APPLE__ | ||
| 195 | nClient = ::accept( nServer, (struct sockaddr *)&clientname, (socklen_t*)&size ); | ||
| 196 | #else /* linux */ | ||
| 197 | nClient = ::accept( nServer, (struct sockaddr *)&clientname, &size ); | ||
| 198 | #endif /* __APPLE__ */ | ||
| 199 | #endif /* __CYGWIN__ */ | ||
| 200 | #endif /* WIN32 */ | ||
| 201 | if( nClient < 0 ) | ||
| 202 | { | ||
| 203 | throw Bu::TcpServerSocketException( | ||
| 204 | "Error accepting a new connection: %s", strerror( errno ) | ||
| 205 | ); | ||
| 206 | } | ||
| 207 | |||
| 208 | #ifndef WIN32 | ||
| 209 | char tmpa[20]; | ||
| 210 | inet_ntop( AF_INET, (void *)&clientname.sin_addr, tmpa, 20 ); | ||
| 211 | //"New connection from host %s, port %hd.", | ||
| 212 | // tmpa, ntohs (clientname.sin_port) ); | ||
| 213 | #endif | ||
| 214 | |||
| 215 | { | ||
| 216 | #ifndef WIN32 | ||
| 217 | int flags; | ||
| 218 | flags = fcntl( nClient, F_GETFL, 0 ); | ||
| 219 | flags |= O_NONBLOCK; | ||
| 220 | if( fcntl( nClient, F_SETFL, flags ) < 0) | ||
| 221 | { | ||
| 222 | throw Bu::TcpServerSocketException( | ||
| 223 | "Error setting option on client socket: %s", | ||
| 224 | strerror( errno ) | ||
| 225 | ); | ||
| 226 | } | ||
| 227 | #else | ||
| 228 | //------------------------- | ||
| 229 | // Set the socket I/O mode: In this case FIONBIO | ||
| 230 | // enables or disables the blocking mode for the | ||
| 231 | // socket based on the numerical value of iMode. | ||
| 232 | // If iMode = 0, blocking is enabled; | ||
| 233 | // If iMode != 0, non-blocking mode is enabled. | ||
| 234 | u_long iMode = 1; | ||
| 235 | bu_ioctlsocket(nClient, FIONBIO, &iMode); | ||
| 236 | #endif | ||
| 237 | } | ||
| 238 | |||
| 239 | return nClient; | ||
| 240 | } | ||
| 241 | |||
| 242 | return -1; | ||
| 243 | } | ||
| 244 | |||
| 245 | int Bu::TcpServerSocket::getPort() | ||
| 246 | { | ||
| 247 | return nPort; | ||
| 248 | } | ||
| 249 | |||
| diff --git a/src/stable/tcpserversocket.h b/src/stable/tcpserversocket.h new file mode 100644 index 0000000..efb7287 --- /dev/null +++ b/src/stable/tcpserversocket.h | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_TCP_SERVER_SOCKET_H | ||
| 9 | #define BU_TCP_SERVER_SOCKET_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include "bu/string.h" | ||
| 13 | #include "bu/exceptionbase.h" | ||
| 14 | |||
| 15 | #ifdef WIN32 | ||
| 16 | #include <Winsock2.h> | ||
| 17 | #else | ||
| 18 | #include <sys/select.h> | ||
| 19 | #endif | ||
| 20 | |||
| 21 | namespace Bu | ||
| 22 | { | ||
| 23 | subExceptionDecl( TcpServerSocketException ); | ||
| 24 | |||
| 25 | /** | ||
| 26 | * A single tcp/ip server socket. When created the server socket will bind | ||
| 27 | * to the specified interface and port, and immediately begin listening for | ||
| 28 | * connections. When connections come in they are pooled by the networking | ||
| 29 | * drivers in the kernel until they are accepted, this means that failure | ||
| 30 | * to keep space in the connection pool will result in connection refusals. | ||
| 31 | * | ||
| 32 | * Although the accept function returns an integral file descriptor, it is | ||
| 33 | * designed to be used with the Socket class. | ||
| 34 | * | ||
| 35 | *@ingroup Serving | ||
| 36 | */ | ||
| 37 | class TcpServerSocket | ||
| 38 | { | ||
| 39 | public: | ||
| 40 | TcpServerSocket( int nPort, int nPoolSize=40 ); | ||
| 41 | TcpServerSocket( const String &sAddr, int nPort, int nPoolSize=40 ); | ||
| 42 | TcpServerSocket( int nSocket, bool bInit, int nPoolSize=40 ); | ||
| 43 | TcpServerSocket( const TcpServerSocket &rSrc ); | ||
| 44 | virtual ~TcpServerSocket(); | ||
| 45 | |||
| 46 | int accept( int nTimeoutSec=0, int nTimeoutUSec=0 ); | ||
| 47 | int getSocket(); | ||
| 48 | int getPort(); | ||
| 49 | |||
| 50 | private: | ||
| 51 | void startServer( struct sockaddr_in &name, int nPoolSize ); | ||
| 52 | void initServer( struct sockaddr_in &name, int nPoolSize ); | ||
| 53 | |||
| 54 | fd_set fdActive; | ||
| 55 | #ifdef WIN32 | ||
| 56 | unsigned int nServer; | ||
| 57 | #else | ||
| 58 | int nServer; | ||
| 59 | #endif | ||
| 60 | int nPort; | ||
| 61 | }; | ||
| 62 | } | ||
| 63 | |||
| 64 | #endif | ||
| diff --git a/src/stable/tcpsocket.cpp b/src/stable/tcpsocket.cpp new file mode 100644 index 0000000..b9b215c --- /dev/null +++ b/src/stable/tcpsocket.cpp | |||
| @@ -0,0 +1,478 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <string.h> | ||
| 9 | #include <stdio.h> | ||
| 10 | #include <errno.h> | ||
| 11 | #include <stdlib.h> | ||
| 12 | #include <unistd.h> | ||
| 13 | #include <sys/types.h> | ||
| 14 | #include <sys/time.h> | ||
| 15 | #include <errno.h> | ||
| 16 | #include <fcntl.h> | ||
| 17 | #include "bu/tcpsocket.h" | ||
| 18 | |||
| 19 | #include "bu/config.h" | ||
| 20 | |||
| 21 | #ifndef WIN32 | ||
| 22 | #include <sys/socket.h> | ||
| 23 | #include <netinet/in.h> | ||
| 24 | #include <netdb.h> | ||
| 25 | #include <arpa/inet.h> | ||
| 26 | #else | ||
| 27 | #include <Winsock2.h> | ||
| 28 | #endif | ||
| 29 | |||
| 30 | #define RBS (1024*2) | ||
| 31 | |||
| 32 | namespace Bu { subExceptionDef( TcpSocketException ) } | ||
| 33 | |||
| 34 | Bu::TcpSocket::TcpSocket( handle nTcpSocket ) : | ||
| 35 | nTcpSocket( nTcpSocket ), | ||
| 36 | bActive( true ), | ||
| 37 | bBlocking( true ) | ||
| 38 | { | ||
| 39 | #ifdef WIN32 | ||
| 40 | Bu::Winsock2::getInstance(); | ||
| 41 | #endif | ||
| 42 | setAddress(); | ||
| 43 | } | ||
| 44 | |||
| 45 | Bu::TcpSocket::TcpSocket( const Bu::String &sAddr, int nPort, int nTimeout, | ||
| 46 | bool bBlocking ) : | ||
| 47 | nTcpSocket( 0 ), | ||
| 48 | bActive( false ), | ||
| 49 | bBlocking( true ) | ||
| 50 | { | ||
| 51 | #ifdef WIN32 | ||
| 52 | Bu::Winsock2::getInstance(); | ||
| 53 | #endif | ||
| 54 | |||
| 55 | /* Create the socket. */ | ||
| 56 | nTcpSocket = bu_socket( PF_INET, SOCK_STREAM, 0 ); | ||
| 57 | |||
| 58 | if( nTcpSocket < 0 ) | ||
| 59 | { | ||
| 60 | throw ExceptionBase("Couldn't create socket.\n"); | ||
| 61 | } | ||
| 62 | |||
| 63 | setBlocking( false ); | ||
| 64 | |||
| 65 | /* Connect to the server. */ | ||
| 66 | //printf("Resolving hostname (%s)...\n", sAddr ); | ||
| 67 | { | ||
| 68 | struct addrinfo *pAddr = NULL; | ||
| 69 | struct addrinfo aiHints; | ||
| 70 | memset( &aiHints, 0, sizeof(addrinfo) ); | ||
| 71 | aiHints.ai_flags = AI_CANONNAME; | ||
| 72 | aiHints.ai_family = AF_INET; | ||
| 73 | aiHints.ai_socktype = SOCK_STREAM; | ||
| 74 | char ibuf[10]; | ||
| 75 | sprintf( ibuf, "%d", nPort ); | ||
| 76 | |||
| 77 | int ret; | ||
| 78 | if( (ret = bu_getaddrinfo( | ||
| 79 | sAddr.getStr(), ibuf, &aiHints, &pAddr )) != 0 ) | ||
| 80 | { | ||
| 81 | close(); | ||
| 82 | throw Bu::TcpSocketException("Couldn't resolve hostname %s (%s).\n", | ||
| 83 | sAddr.getStr(), bu_gai_strerror(ret)); | ||
| 84 | } | ||
| 85 | |||
| 86 | bu_connect( | ||
| 87 | nTcpSocket, | ||
| 88 | pAddr->ai_addr, | ||
| 89 | pAddr->ai_addrlen | ||
| 90 | ); | ||
| 91 | |||
| 92 | sAddress = pAddr->ai_canonname; | ||
| 93 | |||
| 94 | bu_freeaddrinfo( pAddr ); | ||
| 95 | } | ||
| 96 | |||
| 97 | bActive = true; | ||
| 98 | |||
| 99 | if( nTimeout > 0 ) | ||
| 100 | { | ||
| 101 | fd_set rfds, wfds, efds; | ||
| 102 | int retval; | ||
| 103 | |||
| 104 | FD_ZERO(&rfds); | ||
| 105 | FD_SET(nTcpSocket, &rfds); | ||
| 106 | FD_ZERO(&wfds); | ||
| 107 | FD_SET(nTcpSocket, &wfds); | ||
| 108 | FD_ZERO(&efds); | ||
| 109 | FD_SET(nTcpSocket, &efds); | ||
| 110 | |||
| 111 | struct timeval tv; | ||
| 112 | tv.tv_sec = nTimeout; | ||
| 113 | tv.tv_usec = 0; | ||
| 114 | |||
| 115 | retval = bu_select( nTcpSocket+1, &rfds, &wfds, &efds, &tv ); | ||
| 116 | |||
| 117 | if( retval == 0 ) | ||
| 118 | { | ||
| 119 | close(); | ||
| 120 | throw ExceptionBase("Connection timeout.\n"); | ||
| 121 | } | ||
| 122 | read( NULL, 0 ); // See if we can get any errors out of the way early. | ||
| 123 | } | ||
| 124 | |||
| 125 | if( bBlocking ) | ||
| 126 | setBlocking( bBlocking ); | ||
| 127 | } | ||
| 128 | |||
| 129 | Bu::TcpSocket::~TcpSocket() | ||
| 130 | { | ||
| 131 | close(); | ||
| 132 | } | ||
| 133 | |||
| 134 | void Bu::TcpSocket::close() | ||
| 135 | { | ||
| 136 | if( bActive ) | ||
| 137 | { | ||
| 138 | #ifndef WIN32 | ||
| 139 | fsync( nTcpSocket ); | ||
| 140 | #endif | ||
| 141 | #ifdef WIN32 | ||
| 142 | #ifndef SHUT_RDWR | ||
| 143 | #define SHUT_RDWR (SD_BOTH) | ||
| 144 | #endif | ||
| 145 | #endif | ||
| 146 | bu_shutdown( nTcpSocket, SHUT_RDWR ); | ||
| 147 | ::close( nTcpSocket ); | ||
| 148 | } | ||
| 149 | bActive = false; | ||
| 150 | } | ||
| 151 | |||
| 152 | Bu::size Bu::TcpSocket::read( void *pBuf, Bu::size nBytes ) | ||
| 153 | { | ||
| 154 | fd_set rfds; | ||
| 155 | FD_ZERO(&rfds); | ||
| 156 | FD_SET(nTcpSocket, &rfds); | ||
| 157 | struct timeval tv = {0, 0}; | ||
| 158 | if( bu_select( nTcpSocket+1, &rfds, NULL, NULL, &tv ) < 0 ) | ||
| 159 | { | ||
| 160 | int iErr = errno; | ||
| 161 | close(); | ||
| 162 | throw TcpSocketException( TcpSocketException::cRead, strerror(iErr) ); | ||
| 163 | } | ||
| 164 | if( FD_ISSET( nTcpSocket, &rfds ) || bBlocking ) | ||
| 165 | { | ||
| 166 | int nRead = TEMP_FAILURE_RETRY( | ||
| 167 | bu_recv( nTcpSocket, (char *) pBuf, nBytes, 0 ) ); | ||
| 168 | if( nRead == 0 && nBytes > 0 ) | ||
| 169 | { | ||
| 170 | close(); | ||
| 171 | throw TcpSocketException( TcpSocketException::cClosed, "TcpSocket closed."); | ||
| 172 | } | ||
| 173 | if( nRead < 0 ) | ||
| 174 | { | ||
| 175 | #ifdef WIN32 | ||
| 176 | int iWSAError = bu_WSAGetLastError(); | ||
| 177 | if( iWSAError == WSAEWOULDBLOCK ) | ||
| 178 | return 0; | ||
| 179 | #else | ||
| 180 | if( errno == ENETRESET || errno == ECONNRESET ) | ||
| 181 | { | ||
| 182 | close(); | ||
| 183 | throw TcpSocketException( TcpSocketException::cClosed, | ||
| 184 | strerror(errno) ); | ||
| 185 | } | ||
| 186 | if( errno == EAGAIN ) | ||
| 187 | return 0; | ||
| 188 | int iErr = errno; | ||
| 189 | close(); | ||
| 190 | throw TcpSocketException( TcpSocketException::cRead, strerror(iErr) ); | ||
| 191 | #endif | ||
| 192 | } | ||
| 193 | return nRead; | ||
| 194 | } | ||
| 195 | return 0; | ||
| 196 | } | ||
| 197 | |||
| 198 | Bu::size Bu::TcpSocket::read( void *pBuf, Bu::size nBytes, | ||
| 199 | uint32_t nSec, uint32_t nUSec ) | ||
| 200 | { | ||
| 201 | struct timeval tv; | ||
| 202 | Bu::size nRead = 0; | ||
| 203 | |||
| 204 | fd_set rfds; | ||
| 205 | FD_ZERO(&rfds); | ||
| 206 | FD_SET(nTcpSocket, &rfds); | ||
| 207 | |||
| 208 | #ifdef WIN32 | ||
| 209 | DWORD dwStart = GetTickCount(); | ||
| 210 | uint64_t uOver = dwStart + ((nUSec / 1000) * (nSec * 1000)); | ||
| 211 | DWORD dwEnd = uOver>4294967295U?uOver-4294967295U:uOver; | ||
| 212 | #else | ||
| 213 | struct timeval nt, ct; | ||
| 214 | gettimeofday( &nt, NULL ); | ||
| 215 | nt.tv_sec += nSec; | ||
| 216 | nt.tv_usec += nUSec; | ||
| 217 | #endif | ||
| 218 | |||
| 219 | for(;;) | ||
| 220 | { | ||
| 221 | tv.tv_sec = nSec; | ||
| 222 | tv.tv_usec = nUSec; | ||
| 223 | bu_select( nTcpSocket+1, &rfds, NULL, NULL, &tv ); | ||
| 224 | nRead += read( ((char *)pBuf)+nRead, nBytes-nRead ); | ||
| 225 | if( nRead >= nBytes ) | ||
| 226 | break; | ||
| 227 | #ifdef WIN32 | ||
| 228 | DWORD dwNow = GetTickCount(); | ||
| 229 | if( dwNow > dwEnd ) | ||
| 230 | break; | ||
| 231 | #else | ||
| 232 | gettimeofday( &ct, NULL ); | ||
| 233 | if( (ct.tv_sec > nt.tv_sec) || | ||
| 234 | (ct.tv_sec == nt.tv_sec && | ||
| 235 | ct.tv_usec >= nt.tv_usec) ) | ||
| 236 | break; | ||
| 237 | #endif | ||
| 238 | } | ||
| 239 | return nRead; | ||
| 240 | } | ||
| 241 | |||
| 242 | Bu::size Bu::TcpSocket::write( const void *pBuf, Bu::size nBytes ) | ||
| 243 | { | ||
| 244 | //#ifdef WIN32 | ||
| 245 | int nWrote = TEMP_FAILURE_RETRY( | ||
| 246 | bu_send( nTcpSocket, (const char *) pBuf, nBytes, 0 ) ); | ||
| 247 | //#else | ||
| 248 | // int nWrote = TEMP_FAILURE_RETRY( ::write( nTcpSocket, pBuf, nBytes ) ); | ||
| 249 | //#endif | ||
| 250 | if( nWrote < 0 ) | ||
| 251 | { | ||
| 252 | #ifdef WIN32 | ||
| 253 | int iWSAError = bu_WSAGetLastError(); | ||
| 254 | if( iWSAError == WSAEWOULDBLOCK ) | ||
| 255 | return 0; | ||
| 256 | #else | ||
| 257 | if( errno == EAGAIN ) return 0; | ||
| 258 | #endif | ||
| 259 | throw TcpSocketException( TcpSocketException::cWrite, strerror(errno) ); | ||
| 260 | } | ||
| 261 | return nWrote; | ||
| 262 | } | ||
| 263 | |||
| 264 | Bu::size Bu::TcpSocket::write( const void *pBuf, Bu::size nBytes, uint32_t nSec, uint32_t nUSec ) | ||
| 265 | { | ||
| 266 | struct timeval tv; | ||
| 267 | Bu::size nWrote = 0; | ||
| 268 | |||
| 269 | fd_set wfds; | ||
| 270 | FD_ZERO(&wfds); | ||
| 271 | FD_SET(nTcpSocket, &wfds); | ||
| 272 | |||
| 273 | #ifdef WIN32 | ||
| 274 | DWORD dwStart = GetTickCount(); | ||
| 275 | uint64_t uOver = dwStart + ((nUSec / 1000) * (nSec * 1000)); | ||
| 276 | DWORD dwEnd = uOver>4294967295U?uOver-4294967295U:uOver; | ||
| 277 | #else | ||
| 278 | struct timeval nt, ct; | ||
| 279 | gettimeofday( &nt, NULL ); | ||
| 280 | nt.tv_sec += nSec; | ||
| 281 | nt.tv_usec += nUSec; | ||
| 282 | #endif | ||
| 283 | |||
| 284 | for(;;) | ||
| 285 | { | ||
| 286 | tv.tv_sec = nSec; | ||
| 287 | tv.tv_usec = nUSec; | ||
| 288 | bu_select( nTcpSocket+1, NULL, &wfds, NULL, &tv ); | ||
| 289 | nWrote += write( ((char *)pBuf)+nWrote, nBytes-nWrote ); | ||
| 290 | if( nWrote >= nBytes ) | ||
| 291 | break; | ||
| 292 | #ifdef WIN32 | ||
| 293 | DWORD dwNow = GetTickCount(); | ||
| 294 | if( dwNow > dwEnd ) | ||
| 295 | break; | ||
| 296 | #else | ||
| 297 | gettimeofday( &ct, NULL ); | ||
| 298 | if( (ct.tv_sec > nt.tv_sec) || | ||
| 299 | (ct.tv_sec == nt.tv_sec && | ||
| 300 | ct.tv_usec >= nt.tv_usec) ) | ||
| 301 | break; | ||
| 302 | #endif | ||
| 303 | } | ||
| 304 | return nWrote; | ||
| 305 | } | ||
| 306 | |||
| 307 | Bu::size Bu::TcpSocket::tell() | ||
| 308 | { | ||
| 309 | throw UnsupportedException(); | ||
| 310 | } | ||
| 311 | |||
| 312 | void Bu::TcpSocket::seek( Bu::size ) | ||
| 313 | { | ||
| 314 | throw UnsupportedException(); | ||
| 315 | } | ||
| 316 | |||
| 317 | void Bu::TcpSocket::setPos( Bu::size ) | ||
| 318 | { | ||
| 319 | throw UnsupportedException(); | ||
| 320 | } | ||
| 321 | |||
| 322 | void Bu::TcpSocket::setPosEnd( Bu::size ) | ||
| 323 | { | ||
| 324 | throw UnsupportedException(); | ||
| 325 | } | ||
| 326 | |||
| 327 | bool Bu::TcpSocket::isEos() | ||
| 328 | { | ||
| 329 | return !bActive; | ||
| 330 | } | ||
| 331 | |||
| 332 | bool Bu::TcpSocket::canRead() | ||
| 333 | { | ||
| 334 | fd_set rfds; | ||
| 335 | FD_ZERO(&rfds); | ||
| 336 | FD_SET(nTcpSocket, &rfds); | ||
| 337 | struct timeval tv = { 0, 0 }; | ||
| 338 | int retval = bu_select( nTcpSocket+1, &rfds, NULL, NULL, &tv ); | ||
| 339 | if( retval == -1 ) | ||
| 340 | throw TcpSocketException( | ||
| 341 | TcpSocketException::cBadRead, | ||
| 342 | "Bad Read error" | ||
| 343 | ); | ||
| 344 | |||
| 345 | if( !FD_ISSET( nTcpSocket, &rfds ) ) | ||
| 346 | return false; | ||
| 347 | return true; | ||
| 348 | } | ||
| 349 | |||
| 350 | bool Bu::TcpSocket::canWrite() | ||
| 351 | { | ||
| 352 | fd_set wfds; | ||
| 353 | FD_ZERO(&wfds); | ||
| 354 | FD_SET(nTcpSocket, &wfds); | ||
| 355 | struct timeval tv = { 0, 0 }; | ||
| 356 | int retval = bu_select( nTcpSocket+1, NULL, &wfds, NULL, &tv ); | ||
| 357 | if( retval == -1 ) | ||
| 358 | throw TcpSocketException( | ||
| 359 | TcpSocketException::cBadRead, | ||
| 360 | "Bad Read error" | ||
| 361 | ); | ||
| 362 | if( !FD_ISSET( nTcpSocket, &wfds ) ) | ||
| 363 | return false; | ||
| 364 | return true; | ||
| 365 | } | ||
| 366 | |||
| 367 | bool Bu::TcpSocket::isReadable() | ||
| 368 | { | ||
| 369 | return true; | ||
| 370 | } | ||
| 371 | |||
| 372 | bool Bu::TcpSocket::isWritable() | ||
| 373 | { | ||
| 374 | return true; | ||
| 375 | } | ||
| 376 | |||
| 377 | bool Bu::TcpSocket::isSeekable() | ||
| 378 | { | ||
| 379 | return false; | ||
| 380 | } | ||
| 381 | |||
| 382 | bool Bu::TcpSocket::isBlocking() | ||
| 383 | { | ||
| 384 | #ifndef WIN32 | ||
| 385 | return ((fcntl( nTcpSocket, F_GETFL, 0 ) & O_NONBLOCK) != O_NONBLOCK); | ||
| 386 | #else | ||
| 387 | return false; | ||
| 388 | #endif | ||
| 389 | } | ||
| 390 | |||
| 391 | void Bu::TcpSocket::setBlocking( bool bBlocking ) | ||
| 392 | { | ||
| 393 | this->bBlocking = bBlocking; | ||
| 394 | #ifndef WIN32 | ||
| 395 | if( bBlocking ) | ||
| 396 | { | ||
| 397 | fcntl( nTcpSocket, F_SETFL, fcntl( nTcpSocket, F_GETFL, 0 ) & (~O_NONBLOCK) ); | ||
| 398 | } | ||
| 399 | else | ||
| 400 | { | ||
| 401 | fcntl( nTcpSocket, F_SETFL, fcntl( nTcpSocket, F_GETFL, 0 ) | O_NONBLOCK ); | ||
| 402 | } | ||
| 403 | #else | ||
| 404 | u_long iMode; | ||
| 405 | if( bBlocking ) | ||
| 406 | iMode = 0; | ||
| 407 | else | ||
| 408 | iMode = 1; | ||
| 409 | //------------------------- | ||
| 410 | // Set the socket I/O mode: In this case FIONBIO | ||
| 411 | // enables or disables the blocking mode for the | ||
| 412 | // socket based on the numerical value of iMode. | ||
| 413 | // If iMode = 0, blocking is enabled; | ||
| 414 | // If iMode != 0, non-blocking mode is enabled. | ||
| 415 | bu_ioctlsocket(nTcpSocket, FIONBIO, &iMode); | ||
| 416 | #endif | ||
| 417 | } | ||
| 418 | |||
| 419 | void Bu::TcpSocket::setSize( Bu::size ) | ||
| 420 | { | ||
| 421 | } | ||
| 422 | |||
| 423 | void Bu::TcpSocket::flush() | ||
| 424 | { | ||
| 425 | } | ||
| 426 | |||
| 427 | bool Bu::TcpSocket::isOpen() | ||
| 428 | { | ||
| 429 | return bActive; | ||
| 430 | } | ||
| 431 | |||
| 432 | void Bu::TcpSocket::setAddress() | ||
| 433 | { | ||
| 434 | struct sockaddr_in addr; | ||
| 435 | socklen_t len = sizeof(addr); | ||
| 436 | addr.sin_family = AF_INET; | ||
| 437 | bu_getpeername( nTcpSocket, (sockaddr *)(&addr), &len ); | ||
| 438 | sAddress = bu_inet_ntoa( addr.sin_addr ); | ||
| 439 | } | ||
| 440 | |||
| 441 | Bu::String Bu::TcpSocket::getAddress() const | ||
| 442 | { | ||
| 443 | return sAddress; | ||
| 444 | } | ||
| 445 | |||
| 446 | Bu::TcpSocket::operator Bu::TcpSocket::handle() const | ||
| 447 | { | ||
| 448 | return nTcpSocket; | ||
| 449 | } | ||
| 450 | |||
| 451 | Bu::TcpSocket::handle Bu::TcpSocket::getHandle() const | ||
| 452 | { | ||
| 453 | return nTcpSocket; | ||
| 454 | } | ||
| 455 | |||
| 456 | Bu::TcpSocket::handle Bu::TcpSocket::takeHandle() | ||
| 457 | { | ||
| 458 | handle nRet = nTcpSocket; | ||
| 459 | bActive = false; | ||
| 460 | nTcpSocket = 0; | ||
| 461 | return nRet; | ||
| 462 | } | ||
| 463 | |||
| 464 | Bu::size Bu::TcpSocket::getSize() const | ||
| 465 | { | ||
| 466 | throw UnsupportedException(); | ||
| 467 | } | ||
| 468 | |||
| 469 | Bu::size Bu::TcpSocket::getBlockSize() const | ||
| 470 | { | ||
| 471 | return 1500; //TODO: Fix this, it's stupid. | ||
| 472 | } | ||
| 473 | |||
| 474 | Bu::String Bu::TcpSocket::getLocation() const | ||
| 475 | { | ||
| 476 | return getAddress(); | ||
| 477 | } | ||
| 478 | |||
| diff --git a/src/stable/tcpsocket.h b/src/stable/tcpsocket.h new file mode 100644 index 0000000..52218bd --- /dev/null +++ b/src/stable/tcpsocket.h | |||
| @@ -0,0 +1,126 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_TCP_SOCKET_H | ||
| 9 | #define BU_TCP_SOCKET_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | |||
| 13 | #include "bu/config.h" | ||
| 14 | #include "bu/stream.h" | ||
| 15 | #include "bu/string.h" | ||
| 16 | #include "bu/exceptionbase.h" | ||
| 17 | |||
| 18 | namespace Bu | ||
| 19 | { | ||
| 20 | subExceptionDeclBegin( TcpSocketException ); | ||
| 21 | enum { | ||
| 22 | cRead, | ||
| 23 | cWrite, | ||
| 24 | cBadRead, | ||
| 25 | cClosed, | ||
| 26 | cTimeout | ||
| 27 | }; | ||
| 28 | subExceptionDeclEnd(); | ||
| 29 | |||
| 30 | /** | ||
| 31 | * Network socket stream class. This class provides a mechanism for | ||
| 32 | * communicating over a network using TCP/IP. It will provide other low | ||
| 33 | * level protocol and addressing support later on, but for now it's just | ||
| 34 | * standard STREAM TCP/IP sockets. | ||
| 35 | * | ||
| 36 | * Unlike system sockets, these sockets are opened by default in | ||
| 37 | * non-blocking mode, you can specify your own timeout for opening a socket, | ||
| 38 | * and a number of non-fatal error messages have been automatically handled | ||
| 39 | * and treated as standard no-data-available-yet situations on read. | ||
| 40 | * | ||
| 41 | * Please note that there is a condition that will occur eventually (at | ||
| 42 | * least on *nix systems) that will trigger a SIGPIPE condition. This | ||
| 43 | * will terminate your program immediately unless handled properly. Most | ||
| 44 | * people doing any connections with TcpSocket will want to put this in | ||
| 45 | * their program somewhere before they use it: | ||
| 46 | *@code | ||
| 47 | #include <signal.h> | ||
| 48 | ... | ||
| 49 | ... | ||
| 50 | ... | ||
| 51 | sigset( SIGPIPE, SIG_IGN ); // do this before you use a Bu::TcpSocket | ||
| 52 | @endcode | ||
| 53 | * When this is done, Bu::TcpSocket will simply throw a broken pipe | ||
| 54 | * exception just like every other error condition, allowing your program | ||
| 55 | * to handle it sanely. | ||
| 56 | * | ||
| 57 | *@ingroup Serving | ||
| 58 | *@ingroup Streams | ||
| 59 | */ | ||
| 60 | class TcpSocket : public Stream | ||
| 61 | { | ||
| 62 | public: | ||
| 63 | #ifdef WIN32 | ||
| 64 | typedef unsigned int handle; | ||
| 65 | #else | ||
| 66 | typedef int handle; | ||
| 67 | #endif | ||
| 68 | |||
| 69 | TcpSocket( handle nTcpSocket ); | ||
| 70 | TcpSocket( const String &sAddr, int nPort, int nTimeout=30, | ||
| 71 | bool bBlocking=true ); | ||
| 72 | virtual ~TcpSocket(); | ||
| 73 | |||
| 74 | virtual void close(); | ||
| 75 | virtual size read( void *pBuf, size nBytes ); | ||
| 76 | virtual size read( void *pBuf, size nBytes, | ||
| 77 | uint32_t nSec, uint32_t nUSec=0 ); | ||
| 78 | virtual size write( const void *pBuf, size nBytes ); | ||
| 79 | virtual size write( const void *pBuf, size nBytes, | ||
| 80 | uint32_t nSec, uint32_t nUSec=0 ); | ||
| 81 | using Stream::write; | ||
| 82 | |||
| 83 | virtual size tell(); | ||
| 84 | virtual void seek( size offset ); | ||
| 85 | virtual void setPos( size pos ); | ||
| 86 | virtual void setPosEnd( size pos ); | ||
| 87 | virtual bool isEos(); | ||
| 88 | virtual bool isOpen(); | ||
| 89 | |||
| 90 | virtual void flush(); | ||
| 91 | |||
| 92 | virtual bool canRead(); | ||
| 93 | virtual bool canWrite(); | ||
| 94 | |||
| 95 | virtual bool isReadable(); | ||
| 96 | virtual bool isWritable(); | ||
| 97 | virtual bool isSeekable(); | ||
| 98 | |||
| 99 | virtual bool isBlocking(); | ||
| 100 | virtual void setBlocking( bool bBlocking=true ); | ||
| 101 | |||
| 102 | virtual void setSize( size iSize ); | ||
| 103 | |||
| 104 | Bu::String getAddress() const; | ||
| 105 | operator handle() const; | ||
| 106 | |||
| 107 | handle getHandle() const; | ||
| 108 | handle takeHandle(); | ||
| 109 | |||
| 110 | virtual size getSize() const; | ||
| 111 | virtual size getBlockSize() const; | ||
| 112 | virtual Bu::String getLocation() const; | ||
| 113 | |||
| 114 | private: | ||
| 115 | void setAddress(); | ||
| 116 | |||
| 117 | handle nTcpSocket; | ||
| 118 | |||
| 119 | bool bActive; | ||
| 120 | bool bBlocking; | ||
| 121 | String sReadBuf; | ||
| 122 | String sAddress; | ||
| 123 | }; | ||
| 124 | } | ||
| 125 | |||
| 126 | #endif | ||
| diff --git a/src/stable/thread.cpp b/src/stable/thread.cpp new file mode 100644 index 0000000..e4563a2 --- /dev/null +++ b/src/stable/thread.cpp | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/thread.h" | ||
| 9 | |||
| 10 | #include "bu/config.h" | ||
| 11 | |||
| 12 | Bu::Thread::Thread() | ||
| 13 | { | ||
| 14 | } | ||
| 15 | |||
| 16 | Bu::Thread::~Thread() | ||
| 17 | { | ||
| 18 | } | ||
| 19 | |||
| 20 | bool Bu::Thread::start() | ||
| 21 | { | ||
| 22 | nHandle = pthread_create( &ptHandle, NULL, threadRunner, this ); | ||
| 23 | |||
| 24 | return true; | ||
| 25 | } | ||
| 26 | |||
| 27 | bool Bu::Thread::stop() | ||
| 28 | { | ||
| 29 | pthread_cancel( ptHandle ); | ||
| 30 | |||
| 31 | return true; | ||
| 32 | } | ||
| 33 | |||
| 34 | void *Bu::Thread::threadRunner( void *pThread ) | ||
| 35 | { | ||
| 36 | ((Thread *)pThread)->run(); | ||
| 37 | pthread_exit( NULL ); | ||
| 38 | return NULL; | ||
| 39 | } | ||
| 40 | |||
| 41 | bool Bu::Thread::join() | ||
| 42 | { | ||
| 43 | pthread_join( ptHandle, NULL ); | ||
| 44 | return true; | ||
| 45 | } | ||
| 46 | |||
| 47 | void Bu::Thread::yield() | ||
| 48 | { | ||
| 49 | #ifndef WIN32 | ||
| 50 | pthread_yield(); | ||
| 51 | #else | ||
| 52 | #warning Bu::Thread::yield IS A STUB for WIN32!!!! | ||
| 53 | #endif | ||
| 54 | } | ||
| 55 | |||
| diff --git a/src/stable/thread.h b/src/stable/thread.h new file mode 100644 index 0000000..70e6f5f --- /dev/null +++ b/src/stable/thread.h | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_THREAD_H | ||
| 9 | #define BU_THREAD_H | ||
| 10 | |||
| 11 | #include <pthread.h> | ||
| 12 | |||
| 13 | namespace Bu | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * Simple thread class. This wraps the basic pthread (posix threads) | ||
| 17 | * system in an object oriented sort of way. It allows you to create a | ||
| 18 | * class with standard member variables and callable functions that can be | ||
| 19 | * run in it's own thread, one per class instance. | ||
| 20 | *@ingroup Threading | ||
| 21 | */ | ||
| 22 | class Thread | ||
| 23 | { | ||
| 24 | public: | ||
| 25 | /** | ||
| 26 | * Construct an Thread thread. | ||
| 27 | */ | ||
| 28 | Thread(); | ||
| 29 | |||
| 30 | /** | ||
| 31 | * Destroy an Thread thread. | ||
| 32 | */ | ||
| 33 | virtual ~Thread(); | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Begin thread execution. This will call the overridden run function, | ||
| 37 | * which will simply execute in it's own thread until the function | ||
| 38 | * exits, the thread is killed, or the thread is cancelled (optionally). | ||
| 39 | * The thread started in this manner has access to all of it's class | ||
| 40 | * variables, but be sure to protect possible multiple-access with | ||
| 41 | * ThreadMutex objects. | ||
| 42 | * @returns True if starting the thread was successful. False if | ||
| 43 | * something went wrong and the thread has not started. | ||
| 44 | */ | ||
| 45 | bool start(); | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Forcibly kill a thread. This is not generally considered a good | ||
| 49 | * thing to do, but in those rare cases you need it, it's invaluable. | ||
| 50 | * The problem with stopping (or killing) a thread is that it stops it | ||
| 51 | * the moment you call stop, no matter what it's doing. The object | ||
| 52 | * oriented approach to this will help clean up any class variables | ||
| 53 | * that were used, but anything not managed as a member variable will | ||
| 54 | * probably create a memory leak type of situation. Instead of stop, | ||
| 55 | * consider using cancel, which can be handled by the running thread in | ||
| 56 | * a graceful manner. | ||
| 57 | *@returns True if the thread was stopped, false otherwise. When this | ||
| 58 | * function returns the thread may not have stopped, to ensure that the | ||
| 59 | * thread has really stopped, call join. | ||
| 60 | */ | ||
| 61 | bool stop(); | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Join the thread in action. This function performs what is commonly | ||
| 65 | * called a thread join. That is that it effectively makes the calling | ||
| 66 | * thread an the Thread thread contained in the called object one in the | ||
| 67 | * same, and pauses the calling thread until the called thread exits. | ||
| 68 | * That is, when called from, say, your main(), mythread.join() will | ||
| 69 | * not return until the thread mythread has exited. This is very handy | ||
| 70 | * at the end of programs to ensure all of your data was cleaned up. | ||
| 71 | *@returns True if the thread was joined, false if the thread couldn't | ||
| 72 | * be joined, usually because it isn't running to begin with. | ||
| 73 | */ | ||
| 74 | bool join(); | ||
| 75 | |||
| 76 | private: | ||
| 77 | pthread_t ptHandle; /**< Internal handle to the posix thread. */ | ||
| 78 | int nHandle; /**< Numeric handle to the posix thread. */ | ||
| 79 | |||
| 80 | protected: | ||
| 81 | /** | ||
| 82 | * The workhorse of the Thread class. This is the function that will run | ||
| 83 | * in the thread, when this function exits the thread dies and is | ||
| 84 | * cleaned up by the system. Make sure to read up on ThreadMutex, | ||
| 85 | * ThreadCondition, and cancel to see how to control and protect | ||
| 86 | * everything you do in a safe way within this function. | ||
| 87 | *@returns I'm not sure right now, but this is the posix standard form. | ||
| 88 | */ | ||
| 89 | virtual void run()=0; | ||
| 90 | |||
| 91 | /** | ||
| 92 | * This is the hidden-heard of the thread system. While run is what the | ||
| 93 | * user gets to override, and everything said about it is true, this is | ||
| 94 | * the function that actually makes up the thread, it simply calls the | ||
| 95 | * run member function in an OO-friendly way. This is what allows us to | ||
| 96 | * use member variables from within the thread itself. | ||
| 97 | *@param pThread Should always be this. | ||
| 98 | *@returns This is specified by posix, I'm not sure yet. | ||
| 99 | */ | ||
| 100 | static void *threadRunner( void *pThread ); | ||
| 101 | |||
| 102 | void yield(); | ||
| 103 | |||
| 104 | }; | ||
| 105 | } | ||
| 106 | |||
| 107 | #endif | ||
| diff --git a/src/stable/trace.cpp b/src/stable/trace.cpp new file mode 100644 index 0000000..03181e9 --- /dev/null +++ b/src/stable/trace.cpp | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/trace.h" | ||
| 9 | |||
| 10 | void Bu::__tracer( const char *pf ) | ||
| 11 | { | ||
| 12 | printf("trace: %s\n", pf ); | ||
| 13 | } | ||
| 14 | |||
| 15 | template<> void Bu::__tracer_format<float>( const float &v ) | ||
| 16 | { | ||
| 17 | printf("%f", v ); | ||
| 18 | } | ||
| 19 | |||
| 20 | template<> void Bu::__tracer_format<double>( const double &v ) | ||
| 21 | { | ||
| 22 | printf("%f", v ); | ||
| 23 | } | ||
| 24 | |||
| 25 | template<> void Bu::__tracer_format<void *>( void * const &v ) | ||
| 26 | { | ||
| 27 | printf("0x%08X", (ptrdiff_t)v ); | ||
| 28 | } | ||
| 29 | |||
| 30 | template<> void Bu::__tracer_format<char *>( char * const &v ) | ||
| 31 | { | ||
| 32 | printf("\"%s\"", v ); | ||
| 33 | } | ||
| 34 | |||
| 35 | template<> void Bu::__tracer_format<char **>( char ** const &v ) | ||
| 36 | { | ||
| 37 | printf("["); | ||
| 38 | for( int j = 0; v[j]; j++ ) | ||
| 39 | { | ||
| 40 | if( j > 0 ) | ||
| 41 | printf(", "); | ||
| 42 | printf("\"%s\"", v[j] ); | ||
| 43 | } | ||
| 44 | printf("]"); | ||
| 45 | } | ||
| 46 | |||
| 47 | template<> void Bu::__tracer_format<void const *>( void const * const &v ) | ||
| 48 | { | ||
| 49 | printf("0x%08X", (ptrdiff_t)v ); | ||
| 50 | } | ||
| 51 | |||
| 52 | template<> void Bu::__tracer_format<char const *>( char const * const &v ) | ||
| 53 | { | ||
| 54 | printf("\"%s\"", v ); | ||
| 55 | } | ||
| 56 | |||
| 57 | template<> void Bu::__tracer_format<char const **>( char const ** const &v ) | ||
| 58 | { | ||
| 59 | printf("["); | ||
| 60 | for( int j = 0; v[j]; j++ ) | ||
| 61 | { | ||
| 62 | if( j > 0 ) | ||
| 63 | printf(", "); | ||
| 64 | printf("\"%s\"", v[j] ); | ||
| 65 | } | ||
| 66 | printf("]"); | ||
| 67 | } | ||
| diff --git a/src/stable/trace.h b/src/stable/trace.h new file mode 100644 index 0000000..51dfb8e --- /dev/null +++ b/src/stable/trace.h | |||
| @@ -0,0 +1,187 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_TRACE_H | ||
| 9 | #define BU_TRACE_H | ||
| 10 | |||
| 11 | #include <stdio.h> | ||
| 12 | #include <stdint.h> | ||
| 13 | #include <stddef.h> | ||
| 14 | #include <string.h> | ||
| 15 | |||
| 16 | #include <bu/sio.h> | ||
| 17 | |||
| 18 | namespace Bu | ||
| 19 | { | ||
| 20 | /* template<typename t> void __tracer_format( t &v ) | ||
| 21 | { | ||
| 22 | __tracer_format( *const_cast<const t *>(&v) ); | ||
| 23 | } | ||
| 24 | */ | ||
| 25 | template<typename t> void __tracer_format( const t &v ) | ||
| 26 | { | ||
| 27 | Bu::sio << v; | ||
| 28 | } | ||
| 29 | |||
| 30 | void __tracer( const char *pf ); | ||
| 31 | |||
| 32 | #define looper( vv ) \ | ||
| 33 | for( ; *n; n++ ) \ | ||
| 34 | { \ | ||
| 35 | if( bInBracket == true ) \ | ||
| 36 | { \ | ||
| 37 | if( *n == '>' ) \ | ||
| 38 | bInBracket = false; \ | ||
| 39 | } \ | ||
| 40 | else if( *n == ',' || *n == ')' ) \ | ||
| 41 | { \ | ||
| 42 | fwrite( s, (ptrdiff_t)n-(ptrdiff_t)s, 1, stdout ); \ | ||
| 43 | fwrite("=", 1, 1, stdout); \ | ||
| 44 | __tracer_format( vv ); \ | ||
| 45 | s = n; \ | ||
| 46 | n++; \ | ||
| 47 | break; \ | ||
| 48 | } \ | ||
| 49 | else if( *n == '<' ) \ | ||
| 50 | { \ | ||
| 51 | bInBracket = true; \ | ||
| 52 | } \ | ||
| 53 | } (void)0 | ||
| 54 | |||
| 55 | template<typename t1> void __tracer( const char *pf, t1 &v1 ) | ||
| 56 | { | ||
| 57 | printf("trace: "); | ||
| 58 | const char *s = pf; | ||
| 59 | const char *n = pf; | ||
| 60 | bool bInBracket = false; | ||
| 61 | looper( v1 ); | ||
| 62 | fwrite( s, (ptrdiff_t)n-(ptrdiff_t)s, strlen(s), stdout ); | ||
| 63 | fwrite( "\n", 1, 1, stdout ); | ||
| 64 | fflush( stdout ); | ||
| 65 | } | ||
| 66 | |||
| 67 | template<typename t1, typename t2> void __tracer( const char *pf, | ||
| 68 | t1 &v1, t2 &v2 ) | ||
| 69 | { | ||
| 70 | printf("trace: "); | ||
| 71 | const char *s = pf; | ||
| 72 | const char *n = pf; | ||
| 73 | bool bInBracket = false; | ||
| 74 | looper( v1 ); | ||
| 75 | looper( v2 ); | ||
| 76 | fwrite( s, (ptrdiff_t)n-(ptrdiff_t)s, strlen(s), stdout ); | ||
| 77 | fwrite( "\n", 1, 1, stdout ); | ||
| 78 | fflush( stdout ); | ||
| 79 | } | ||
| 80 | |||
| 81 | template<typename t1, typename t2, typename t3> | ||
| 82 | void __tracer( const char *pf, t1 &v1, t2 &v2, t3 &v3 ) | ||
| 83 | { | ||
| 84 | printf("trace: "); | ||
| 85 | const char *s = pf; | ||
| 86 | const char *n = pf; | ||
| 87 | bool bInBracket = false; | ||
| 88 | looper( v1 ); | ||
| 89 | looper( v2 ); | ||
| 90 | looper( v3 ); | ||
| 91 | fwrite( s, (ptrdiff_t)n-(ptrdiff_t)s, strlen(s), stdout ); | ||
| 92 | fwrite( "\n", 1, 1, stdout ); | ||
| 93 | fflush( stdout ); | ||
| 94 | } | ||
| 95 | |||
| 96 | template<typename t1, typename t2, typename t3, typename t4> | ||
| 97 | void __tracer( const char *pf, t1 &v1, t2 &v2, t3 &v3, t4 &v4 ) | ||
| 98 | { | ||
| 99 | printf("trace: "); | ||
| 100 | const char *s = pf; | ||
| 101 | const char *n = pf; | ||
| 102 | bool bInBracket = false; | ||
| 103 | looper( v1 ); | ||
| 104 | looper( v2 ); | ||
| 105 | looper( v3 ); | ||
| 106 | looper( v4 ); | ||
| 107 | fwrite( s, (ptrdiff_t)n-(ptrdiff_t)s, strlen(s), stdout ); | ||
| 108 | fwrite( "\n", 1, 1, stdout ); | ||
| 109 | fflush( stdout ); | ||
| 110 | } | ||
| 111 | |||
| 112 | template<typename t1, typename t2, typename t3, typename t4, typename t5> | ||
| 113 | void __tracer( const char *pf, t1 &v1, t2 &v2, t3 &v3, t4 &v4, t5 &v5 ) | ||
| 114 | { | ||
| 115 | printf("trace: "); | ||
| 116 | const char *s = pf; | ||
| 117 | const char *n = pf; | ||
| 118 | bool bInBracket = false; | ||
| 119 | looper( v1 ); | ||
| 120 | looper( v2 ); | ||
| 121 | looper( v3 ); | ||
| 122 | looper( v4 ); | ||
| 123 | looper( v5 ); | ||
| 124 | fwrite( s, (ptrdiff_t)n-(ptrdiff_t)s, strlen(s), stdout ); | ||
| 125 | fwrite( "\n", 1, 1, stdout ); | ||
| 126 | fflush( stdout ); | ||
| 127 | } | ||
| 128 | |||
| 129 | template<typename t1, typename t2, typename t3, typename t4, typename t5, | ||
| 130 | typename t6> | ||
| 131 | void __tracer( const char *pf, t1 &v1, t2 &v2, t3 &v3, t4 &v4, t5 &v5, | ||
| 132 | t6 &v6) | ||
| 133 | { | ||
| 134 | printf("trace: "); | ||
| 135 | const char *s = pf; | ||
| 136 | const char *n = pf; | ||
| 137 | bool bInBracket = false; | ||
| 138 | looper( v1 ); | ||
| 139 | looper( v2 ); | ||
| 140 | looper( v3 ); | ||
| 141 | looper( v4 ); | ||
| 142 | looper( v5 ); | ||
| 143 | looper( v6 ); | ||
| 144 | fwrite( s, (ptrdiff_t)n-(ptrdiff_t)s, strlen(s), stdout ); | ||
| 145 | fwrite( "\n", 1, 1, stdout ); | ||
| 146 | fflush( stdout ); | ||
| 147 | } | ||
| 148 | |||
| 149 | template<typename t1, typename t2, typename t3, typename t4, typename t5, | ||
| 150 | typename t6, typename t7> | ||
| 151 | void __tracer( const char *pf, t1 &v1, t2 &v2, t3 &v3, t4 &v4, t5 &v5, | ||
| 152 | t6 &v6, t7 &v7 ) | ||
| 153 | { | ||
| 154 | printf("trace: "); | ||
| 155 | const char *s = pf; | ||
| 156 | const char *n = pf; | ||
| 157 | bool bInBracket = false; | ||
| 158 | looper( v1 ); | ||
| 159 | looper( v2 ); | ||
| 160 | looper( v3 ); | ||
| 161 | looper( v4 ); | ||
| 162 | looper( v5 ); | ||
| 163 | looper( v6 ); | ||
| 164 | looper( v7 ); | ||
| 165 | fwrite( s, (ptrdiff_t)n-(ptrdiff_t)s, strlen(s), stdout ); | ||
| 166 | fwrite( "\n", 1, 1, stdout ); | ||
| 167 | fflush( stdout ); | ||
| 168 | } | ||
| 169 | #undef looper | ||
| 170 | |||
| 171 | template<> void __tracer_format<float>( const float &v ); | ||
| 172 | template<> void __tracer_format<double>( const double &v ); | ||
| 173 | template<> void __tracer_format<void *>( void * const &v ); | ||
| 174 | template<> void __tracer_format<char *>( char * const &v ); | ||
| 175 | template<> void __tracer_format<char **>( char ** const &v ); | ||
| 176 | template<> void __tracer_format<void const *>( void const * const &v ); | ||
| 177 | template<> void __tracer_format<char const *>( char const * const &v ); | ||
| 178 | template<> void __tracer_format<char const **>( char const ** const &v ); | ||
| 179 | } | ||
| 180 | |||
| 181 | #ifdef BU_TRACE | ||
| 182 | # define TRACE(args...) Bu::__tracer( __PRETTY_FUNCTION__, ##args ) | ||
| 183 | #else | ||
| 184 | # define TRACE(args...) (void)0 | ||
| 185 | #endif | ||
| 186 | |||
| 187 | #endif | ||
| diff --git a/src/stable/unitsuite.cpp b/src/stable/unitsuite.cpp new file mode 100644 index 0000000..db930a4 --- /dev/null +++ b/src/stable/unitsuite.cpp | |||
| @@ -0,0 +1,255 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/unitsuite.h" | ||
| 9 | #include "bu/file.h" | ||
| 10 | #include "bu/sio.h" | ||
| 11 | #include "bu/optparser.h" | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include <time.h> | ||
| 14 | |||
| 15 | using namespace Bu; | ||
| 16 | |||
| 17 | #include <unistd.h> | ||
| 18 | |||
| 19 | Bu::UnitSuite::UnitSuite() : | ||
| 20 | iOptions( 0 ), | ||
| 21 | iNameWidth( 0 ) | ||
| 22 | { | ||
| 23 | } | ||
| 24 | |||
| 25 | Bu::UnitSuite::UnitSuite( int iOptions ) : | ||
| 26 | iOptions( iOptions ), | ||
| 27 | iNameWidth( 0 ) | ||
| 28 | { | ||
| 29 | } | ||
| 30 | |||
| 31 | Bu::UnitSuite::~UnitSuite() | ||
| 32 | { | ||
| 33 | } | ||
| 34 | |||
| 35 | // Argument handling is coming soon, I promise. | ||
| 36 | int Bu::UnitSuite::run( int argc, char *argv[] ) | ||
| 37 | { | ||
| 38 | bool bCleanup = true; | ||
| 39 | OptParser p; | ||
| 40 | p.addOption( Bu::slot( this, &Bu::UnitSuite::onListCases ), 'l', "list", | ||
| 41 | "List available test cases." ); | ||
| 42 | p.addOption( bCleanup, "no-cleanup", "Don't erase temp files."); | ||
| 43 | p.setOverride( "no-cleanup", false ); | ||
| 44 | p.addHelpOption(); | ||
| 45 | p.parse( argc, argv ); | ||
| 46 | |||
| 47 | int iEPass = 0; | ||
| 48 | int iEFail = 0; | ||
| 49 | int iUPass = 0; | ||
| 50 | int iUFail = 0; | ||
| 51 | for( TestList::iterator i = lTests.begin(); i != lTests.end(); i++ ) | ||
| 52 | { | ||
| 53 | sio << Fmt( iNameWidth+3, Fmt::Left ).fill('.') << i->sName | ||
| 54 | << sio.flush; | ||
| 55 | try | ||
| 56 | { | ||
| 57 | iStepCount = -1; | ||
| 58 | iProgress = 0; | ||
| 59 | (this->*(i->fTest))(); | ||
| 60 | switch( i->eExpect ) | ||
| 61 | { | ||
| 62 | case expectPass: | ||
| 63 | sio << "pass." << sio.nl; | ||
| 64 | iEPass++; | ||
| 65 | break; | ||
| 66 | |||
| 67 | case expectFail: | ||
| 68 | sio << "unexpected pass." << sio.nl; | ||
| 69 | iUPass++; | ||
| 70 | break; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | catch( Failed &e ) | ||
| 74 | { | ||
| 75 | switch( i->eExpect ) | ||
| 76 | { | ||
| 77 | case expectPass: | ||
| 78 | sio << "unexpected "; | ||
| 79 | iUFail++; | ||
| 80 | break; | ||
| 81 | |||
| 82 | case expectFail: | ||
| 83 | sio << "expected "; | ||
| 84 | iEFail++; | ||
| 85 | break; | ||
| 86 | } | ||
| 87 | if( e.bFile ) | ||
| 88 | { | ||
| 89 | sio << "fail in unitTest(" << e.str << "). (" << e.sFile | ||
| 90 | << ":" << e.nLine << ")." << sio.nl; | ||
| 91 | } | ||
| 92 | else | ||
| 93 | { | ||
| 94 | sio << "fail in unitTest(" << e.str << ")." << sio.nl; | ||
| 95 | } | ||
| 96 | |||
| 97 | if( (iOptions & optStopOnError) ) | ||
| 98 | return 0; | ||
| 99 | } | ||
| 100 | catch( std::exception &e ) | ||
| 101 | { | ||
| 102 | switch( i->eExpect ) | ||
| 103 | { | ||
| 104 | case expectPass: | ||
| 105 | sio << "unexpected "; | ||
| 106 | iUFail++; | ||
| 107 | break; | ||
| 108 | |||
| 109 | case expectFail: | ||
| 110 | sio << "expected "; | ||
| 111 | iEFail++; | ||
| 112 | break; | ||
| 113 | } | ||
| 114 | sio << "fail with unknown exception. what: " << e.what() << sio.nl; | ||
| 115 | |||
| 116 | if( (iOptions & optStopOnError) ) | ||
| 117 | return 0; | ||
| 118 | } | ||
| 119 | catch( ... ) | ||
| 120 | { | ||
| 121 | switch( i->eExpect ) | ||
| 122 | { | ||
| 123 | case expectPass: | ||
| 124 | sio << "unexpected "; | ||
| 125 | iUFail++; | ||
| 126 | break; | ||
| 127 | |||
| 128 | case expectFail: | ||
| 129 | sio << "expected "; | ||
| 130 | iEFail++; | ||
| 131 | break; | ||
| 132 | } | ||
| 133 | sio << "fail with external exception." << sio.nl; | ||
| 134 | |||
| 135 | if( (iOptions & optStopOnError) ) | ||
| 136 | return 0; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | sio << sio.nl | ||
| 141 | << "Report:" << sio.nl | ||
| 142 | << "\tTotal tests run: " << lTests.getSize() << sio.nl | ||
| 143 | << "\tExpected passes: " << iEPass << sio.nl | ||
| 144 | << "\tExpected failures: " << iEFail << sio.nl | ||
| 145 | << "\tUnexpected passes: " << iUPass << sio.nl | ||
| 146 | << "\tUnexpected failures: " << iUFail << sio.nl << sio.nl; | ||
| 147 | if( iUPass == 0 && iUFail == 0 ) | ||
| 148 | sio << "\tNothing unexpected." << sio.nl << sio.nl; | ||
| 149 | |||
| 150 | if( bCleanup ) | ||
| 151 | { | ||
| 152 | for( StrList::iterator i = lFileCleanup.begin(); i; i++ ) | ||
| 153 | { | ||
| 154 | unlink( (*i).getStr() ); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | Bu::File Bu::UnitSuite::tempFile( Bu::String &sFileName ) | ||
| 162 | { | ||
| 163 | Bu::File f = Bu::File::tempFile( sFileName ); | ||
| 164 | lFileCleanup.append( sFileName ); | ||
| 165 | return f; | ||
| 166 | } | ||
| 167 | |||
| 168 | void Bu::UnitSuite::add( Test fTest, const Bu::String &sName, Expect e ) | ||
| 169 | { | ||
| 170 | TestInfo ti; | ||
| 171 | ti.sName = sName; | ||
| 172 | ti.eExpect = e; | ||
| 173 | long index = ti.sName.rfindIdx("::"); | ||
| 174 | if( index != -1 ) | ||
| 175 | { | ||
| 176 | String tmp = sSuiteName; | ||
| 177 | tmp += ti.sName.getStr()+index; | ||
| 178 | ti.sName = tmp; | ||
| 179 | } | ||
| 180 | ti.fTest = fTest; | ||
| 181 | lTests.append( ti ); | ||
| 182 | if( iNameWidth < ti.sName.getSize() ) | ||
| 183 | iNameWidth = ti.sName.getSize(); | ||
| 184 | } | ||
| 185 | |||
| 186 | void Bu::UnitSuite::setName( const String &sName ) | ||
| 187 | { | ||
| 188 | sSuiteName = sName; | ||
| 189 | } | ||
| 190 | |||
| 191 | void Bu::UnitSuite::dispProgress() | ||
| 192 | { | ||
| 193 | if( tLastUpdate == time( NULL ) ) | ||
| 194 | return; | ||
| 195 | sio << Fmt(3) << (iProgress*100/iStepCount) << "%" << "\b\b\b\b" | ||
| 196 | << sio.flush; | ||
| 197 | tLastUpdate = time( NULL ); | ||
| 198 | } | ||
| 199 | |||
| 200 | void Bu::UnitSuite::setStepCount( int iSteps ) | ||
| 201 | { | ||
| 202 | iStepCount = iSteps; | ||
| 203 | if( iStepCount < 0 ) | ||
| 204 | return; | ||
| 205 | tLastUpdate = 0; | ||
| 206 | dispProgress(); | ||
| 207 | } | ||
| 208 | |||
| 209 | void Bu::UnitSuite::incProgress( int iAmnt ) | ||
| 210 | { | ||
| 211 | iProgress += iAmnt; | ||
| 212 | if( iProgress < 0 ) | ||
| 213 | iProgress = 0; | ||
| 214 | if( iProgress > iStepCount ) | ||
| 215 | iProgress = iStepCount; | ||
| 216 | dispProgress(); | ||
| 217 | } | ||
| 218 | |||
| 219 | void Bu::UnitSuite::setProgress( int iAmnt ) | ||
| 220 | { | ||
| 221 | iProgress = iAmnt; | ||
| 222 | if( iProgress < 0 ) | ||
| 223 | iProgress = 0; | ||
| 224 | if( iProgress > iStepCount ) | ||
| 225 | iProgress = iStepCount; | ||
| 226 | dispProgress(); | ||
| 227 | } | ||
| 228 | |||
| 229 | int Bu::UnitSuite::onListCases( StrArray ) | ||
| 230 | { | ||
| 231 | sio << "Test cases:" << sio.nl; | ||
| 232 | for( TestList::iterator i = lTests.begin(); i; i++ ) | ||
| 233 | { | ||
| 234 | sio << "\t- " << Fmt( iNameWidth, 10, Fmt::Left ) << (*i).sName << " " | ||
| 235 | << (*i).eExpect << sio.nl; | ||
| 236 | } | ||
| 237 | sio << sio.nl; | ||
| 238 | exit( 0 ); | ||
| 239 | return 0; | ||
| 240 | } | ||
| 241 | |||
| 242 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, const Bu::UnitSuite::Expect &e ) | ||
| 243 | { | ||
| 244 | switch( e ) | ||
| 245 | { | ||
| 246 | case Bu::UnitSuite::expectPass: | ||
| 247 | return f << "expect pass"; | ||
| 248 | |||
| 249 | case Bu::UnitSuite::expectFail: | ||
| 250 | return f << "expect fail"; | ||
| 251 | } | ||
| 252 | |||
| 253 | return f << "**error**"; | ||
| 254 | } | ||
| 255 | |||
| diff --git a/src/stable/unitsuite.h b/src/stable/unitsuite.h new file mode 100644 index 0000000..2250a4d --- /dev/null +++ b/src/stable/unitsuite.h | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_UNIT_SUITE_H | ||
| 9 | #define BU_UNIT_SUITE_H | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | #include "bu/list.h" | ||
| 13 | #include "bu/string.h" | ||
| 14 | #include "bu/file.h" | ||
| 15 | #include "bu/array.h" | ||
| 16 | |||
| 17 | namespace Bu | ||
| 18 | { | ||
| 19 | /** | ||
| 20 | * Provides a unit testing framework. This is pretty easy to use, probably | ||
| 21 | * the best way to get started is to use ch to generate a template, or use | ||
| 22 | * the code below with appropriate tweaks: | ||
| 23 | *@code | ||
| 24 | * #include "unitsuite.h" | ||
| 25 | * | ||
| 26 | * class Unit : public Bu::UnitSuite | ||
| 27 | * { | ||
| 28 | * public: | ||
| 29 | * Unit() | ||
| 30 | * { | ||
| 31 | * setName("Example"); | ||
| 32 | * addTest( Unit::test ); | ||
| 33 | * } | ||
| 34 | * | ||
| 35 | * virtual ~Unit() { } | ||
| 36 | * | ||
| 37 | * // | ||
| 38 | * // Tests go here | ||
| 39 | * // | ||
| 40 | * void test() | ||
| 41 | * { | ||
| 42 | * unitTest( 1 == 1 ); | ||
| 43 | * } | ||
| 44 | * }; | ||
| 45 | * | ||
| 46 | * int main( int argc, char *argv[] ) | ||
| 47 | * { | ||
| 48 | * return Unit().run( argc, argv ); | ||
| 49 | * } | ||
| 50 | * | ||
| 51 | @endcode | ||
| 52 | * The main function can contain other things, but using this one exactly | ||
| 53 | * makes all of the test suites work exactly the same. Using the optional | ||
| 54 | * setName at the top of the constructor replaces the class name with the | ||
| 55 | * chosen name when printing out stats and info. | ||
| 56 | */ | ||
| 57 | class UnitSuite | ||
| 58 | { | ||
| 59 | public: | ||
| 60 | UnitSuite(); | ||
| 61 | UnitSuite( int iOptions ); | ||
| 62 | virtual ~UnitSuite(); | ||
| 63 | |||
| 64 | int run( int argc=0, char *argv[]=NULL ); | ||
| 65 | |||
| 66 | Bu::File tempFile( Bu::String &sFileName ); | ||
| 67 | |||
| 68 | typedef void (UnitSuite::*Test)(); | ||
| 69 | |||
| 70 | class Failed | ||
| 71 | { | ||
| 72 | public: | ||
| 73 | Failed() : str(""), bFile( false ) { } | ||
| 74 | Failed( const String &s ) : str( s ), bFile( false ) { } | ||
| 75 | Failed( const String &s, const String &sFile, int nLine ) : | ||
| 76 | str( s ), sFile( sFile ), nLine( nLine ), bFile( true ) { } | ||
| 77 | |||
| 78 | String str; | ||
| 79 | String sFile; | ||
| 80 | int nLine; | ||
| 81 | bool bFile; | ||
| 82 | }; | ||
| 83 | |||
| 84 | enum | ||
| 85 | { | ||
| 86 | optStopOnError = 0x000001 | ||
| 87 | }; | ||
| 88 | |||
| 89 | enum Expect | ||
| 90 | { | ||
| 91 | expectPass, | ||
| 92 | expectFail | ||
| 93 | }; | ||
| 94 | |||
| 95 | protected: | ||
| 96 | void add( Test fTest, const Bu::String &sName, Expect e=expectPass ); | ||
| 97 | void setName( const String &sName ); | ||
| 98 | |||
| 99 | void dispProgress(); | ||
| 100 | void setStepCount( int iSteps ); | ||
| 101 | void incProgress( int iAmnt = 1 ); | ||
| 102 | void setProgress( int iAmnt ); | ||
| 103 | |||
| 104 | private: | ||
| 105 | int onListCases( Bu::Array<Bu::String> aParam ); | ||
| 106 | |||
| 107 | private: | ||
| 108 | typedef struct TestInfo | ||
| 109 | { | ||
| 110 | String sName; | ||
| 111 | Test fTest; | ||
| 112 | Expect eExpect; | ||
| 113 | } TestInfo; | ||
| 114 | |||
| 115 | typedef Bu::List<TestInfo> TestList; | ||
| 116 | TestList lTests; | ||
| 117 | String sSuiteName; | ||
| 118 | |||
| 119 | int iOptions; | ||
| 120 | |||
| 121 | typedef Bu::List<Bu::String> StrList; | ||
| 122 | StrList lFileCleanup; | ||
| 123 | int iNameWidth; | ||
| 124 | int iStepCount; | ||
| 125 | int iProgress; | ||
| 126 | time_t tLastUpdate; | ||
| 127 | }; | ||
| 128 | |||
| 129 | Bu::Formatter &operator<<( Bu::Formatter &f, const Bu::UnitSuite::Expect &e ); | ||
| 130 | } | ||
| 131 | |||
| 132 | #define addTest( fn ) add( static_cast<Bu::UnitSuite::Test>(&fn), #fn ) | ||
| 133 | #define unitTest( tst ) if( !(tst) ) \ | ||
| 134 | { \ | ||
| 135 | throw Bu::UnitSuite::Failed( #tst, __FILE__, __LINE__ ); \ | ||
| 136 | } else (void)0 | ||
| 137 | #define unitFailed( msg ) throw Bu::UnitSuite::Failed(msg, __FILE__, __LINE__) | ||
| 138 | |||
| 139 | #endif | ||
| diff --git a/src/stable/util.cpp b/src/stable/util.cpp new file mode 100644 index 0000000..6983dfd --- /dev/null +++ b/src/stable/util.cpp | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/util.h" | ||
| 9 | |||
| 10 | int Bu::getDaysInMonth( int iMonth, int iYear ) | ||
| 11 | { | ||
| 12 | if( iMonth > 11 ) | ||
| 13 | { | ||
| 14 | iYear += iMonth/12; | ||
| 15 | iMonth = iMonth%12; | ||
| 16 | } | ||
| 17 | switch( iMonth ) | ||
| 18 | { | ||
| 19 | case 0: | ||
| 20 | case 2: | ||
| 21 | case 4: | ||
| 22 | case 6: | ||
| 23 | case 7: | ||
| 24 | case 9: | ||
| 25 | case 11: | ||
| 26 | return 31; | ||
| 27 | break; | ||
| 28 | |||
| 29 | case 3: | ||
| 30 | case 5: | ||
| 31 | case 8: | ||
| 32 | case 10: | ||
| 33 | return 30; | ||
| 34 | break; | ||
| 35 | |||
| 36 | case 1: | ||
| 37 | if( iYear%400 == 0 ) | ||
| 38 | return 29; | ||
| 39 | if( iYear%100 == 0 ) | ||
| 40 | return 28; | ||
| 41 | if( iYear%4 == 0 ) | ||
| 42 | return 29; | ||
| 43 | return 28; | ||
| 44 | break; | ||
| 45 | |||
| 46 | default: | ||
| 47 | return -1; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | void Bu::memcpy( void *pDest, const void *pSrc, size_t iBytes ) | ||
| 51 | { | ||
| 52 | #ifdef VALTEST | ||
| 53 | const char *src = (const char *)pSrc; | ||
| 54 | char *dest = (char *)pDest; | ||
| 55 | for( int j = 0; j < count; j++ ) | ||
| 56 | { | ||
| 57 | *dest = *src; | ||
| 58 | dest++; | ||
| 59 | src++; | ||
| 60 | } | ||
| 61 | #else | ||
| 62 | ::memcpy( pDest, pSrc, iBytes ); | ||
| 63 | #endif | ||
| 64 | } | ||
| 65 | |||
| diff --git a/src/stable/util.h b/src/stable/util.h new file mode 100644 index 0000000..691184d --- /dev/null +++ b/src/stable/util.h | |||
| @@ -0,0 +1,187 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_UTIL_H | ||
| 9 | #define BU_UTIL_H | ||
| 10 | |||
| 11 | #ifndef NULL | ||
| 12 | # define NULL 0 | ||
| 13 | #endif | ||
| 14 | |||
| 15 | /* I borrowed this from someone who borrowed it from glib who borrowed it | ||
| 16 | * from... | ||
| 17 | */ | ||
| 18 | #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) | ||
| 19 | # define DEPRECATED __attribute__((__deprecated__)) | ||
| 20 | #else | ||
| 21 | # define DEPRECATED | ||
| 22 | #endif /* __GNUC__ */ | ||
| 23 | |||
| 24 | #include <string.h> | ||
| 25 | |||
| 26 | namespace Bu | ||
| 27 | { | ||
| 28 | /** | ||
| 29 | * Swap the value of two variables, uses references, so it's pretty safe. | ||
| 30 | * Objects passed in must support a basic assignemnt operator (=); | ||
| 31 | *@param a Variable to recieve the value of parameter b | ||
| 32 | *@param b Variable to recieve the value of parameter a | ||
| 33 | */ | ||
| 34 | template<typename item> | ||
| 35 | void swap( item &a, item &b ) | ||
| 36 | { | ||
| 37 | item tmp = a; | ||
| 38 | a = b; | ||
| 39 | b = tmp; | ||
| 40 | } | ||
| 41 | |||
| 42 | #ifdef WIN32 | ||
| 43 | #warning: removing min and max win32 macros because of compile conflict | ||
| 44 | #undef min | ||
| 45 | #undef max | ||
| 46 | #endif | ||
| 47 | /** | ||
| 48 | * Finds the lesser of the two objects, objects passed in must be | ||
| 49 | * less-than-comparable. | ||
| 50 | *@param a A value to test. | ||
| 51 | *@param b Another value to test. | ||
| 52 | *@returns A reference to the lesser of a or b. | ||
| 53 | */ | ||
| 54 | template<typename item> | ||
| 55 | const item &min( const item &a, const item &b ) | ||
| 56 | { | ||
| 57 | return a<b?a:b; | ||
| 58 | } | ||
| 59 | |||
| 60 | /** | ||
| 61 | * Finds the lesser of the two objects, objects passed in must be | ||
| 62 | * less-than-comparable. | ||
| 63 | *@param a A value to test. | ||
| 64 | *@param b Another value to test. | ||
| 65 | *@returns A reference to the lesser of a or b. | ||
| 66 | */ | ||
| 67 | template<typename item> | ||
| 68 | item &min( item &a, item &b ) | ||
| 69 | { | ||
| 70 | return a<b?a:b; | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Finds the greater of the two objects, objects passed in must be | ||
| 75 | * less-than-comparable. | ||
| 76 | *@param a A value to test. | ||
| 77 | *@param b Another value to test. | ||
| 78 | *@returns A reference to the greater of a or b. | ||
| 79 | */ | ||
| 80 | template<typename item> | ||
| 81 | const item &max( const item &a, const item &b ) | ||
| 82 | { | ||
| 83 | return b<a?a:b; | ||
| 84 | } | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Finds the greater of the two objects, objects passed in must be | ||
| 88 | * less-than-comparable. | ||
| 89 | *@param a A value to test. | ||
| 90 | *@param b Another value to test. | ||
| 91 | *@returns A reference to the greater of a or b. | ||
| 92 | */ | ||
| 93 | template<typename item> | ||
| 94 | item &max( item &a, item &b ) | ||
| 95 | { | ||
| 96 | return b<a?a:b; | ||
| 97 | } | ||
| 98 | |||
| 99 | /** | ||
| 100 | * Given three objects this finds the one between the other two. | ||
| 101 | *@param a A value to test. | ||
| 102 | *@param b Another value to test. | ||
| 103 | *@param c Yet another value to test. | ||
| 104 | *@returns A reference to the mid-value of a, b, and c. | ||
| 105 | */ | ||
| 106 | template<typename item> | ||
| 107 | const item &mid( const item &a, const item &b, const item &c ) | ||
| 108 | { | ||
| 109 | return min( max( a, b ), c ); | ||
| 110 | } | ||
| 111 | |||
| 112 | /** | ||
| 113 | * Given three objects this finds the one between the other two. | ||
| 114 | *@param a A value to test. | ||
| 115 | *@param b Another value to test. | ||
| 116 | *@param c Yet another value to test. | ||
| 117 | *@returns A reference to the mid-value of a, b, and c. | ||
| 118 | */ | ||
| 119 | template<typename item> | ||
| 120 | item &mid( item &a, item &b, item &c ) | ||
| 121 | { | ||
| 122 | return min( max( a, b ), c ); | ||
| 123 | } | ||
| 124 | |||
| 125 | // | ||
| 126 | // Basic comparison functors | ||
| 127 | // | ||
| 128 | /** | ||
| 129 | * Simple less-than comparison functor. Objects being used should be | ||
| 130 | * less-than-comparable. | ||
| 131 | */ | ||
| 132 | template<typename item> | ||
| 133 | struct __basicLTCmp | ||
| 134 | { | ||
| 135 | bool operator()( const item &a, const item &b ) | ||
| 136 | { | ||
| 137 | return a < b; | ||
| 138 | } | ||
| 139 | }; | ||
| 140 | |||
| 141 | /** | ||
| 142 | * Simple greater-than comparison functor. Objects being used should be | ||
| 143 | * greater-than-comparable. | ||
| 144 | */ | ||
| 145 | template<typename item> | ||
| 146 | struct __basicGTCmp | ||
| 147 | { | ||
| 148 | bool operator()( const item &a, const item &b ) | ||
| 149 | { | ||
| 150 | return a > b; | ||
| 151 | } | ||
| 152 | }; | ||
| 153 | |||
| 154 | /** | ||
| 155 | * As __basicLTCmp but dereferences the passed in pointers before comparing. | ||
| 156 | */ | ||
| 157 | template<typename item> | ||
| 158 | struct __basicPtrLTCmp | ||
| 159 | { | ||
| 160 | bool operator()( const item &a, const item &b ) | ||
| 161 | { | ||
| 162 | return *a < *b; | ||
| 163 | } | ||
| 164 | }; | ||
| 165 | |||
| 166 | /** | ||
| 167 | * As __basicGTCmp but dereferences the passed in pointers before comparing. | ||
| 168 | */ | ||
| 169 | template<typename item> | ||
| 170 | struct __basicPtrGTCmp | ||
| 171 | { | ||
| 172 | bool operator()( const item &a, const item &b ) | ||
| 173 | { | ||
| 174 | return *a > *b; | ||
| 175 | } | ||
| 176 | }; | ||
| 177 | |||
| 178 | /** | ||
| 179 | * Get the number of days in the month in the gregorian calendar, taking | ||
| 180 | * leap years into account. | ||
| 181 | */ | ||
| 182 | int getDaysInMonth( int iMonth, int iYear ); | ||
| 183 | |||
| 184 | void memcpy( void *pDest, const void *pSrc, size_t iBytes ); | ||
| 185 | }; | ||
| 186 | |||
| 187 | #endif | ||
| diff --git a/src/stable/variant.cpp b/src/stable/variant.cpp new file mode 100644 index 0000000..5cdaa5b --- /dev/null +++ b/src/stable/variant.cpp | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "bu/variant.h" | ||
| 9 | #include "bu/formatter.h" | ||
| 10 | #include "bu/membuf.h" | ||
| 11 | |||
| 12 | namespace Bu | ||
| 13 | { | ||
| 14 | Formatter &operator<<( Formatter &f, const String &s ); | ||
| 15 | }; | ||
| 16 | |||
| 17 | Bu::VariantTypeRoot::VariantTypeRoot() | ||
| 18 | { | ||
| 19 | } | ||
| 20 | |||
| 21 | Bu::VariantTypeRoot::~VariantTypeRoot() | ||
| 22 | { | ||
| 23 | } | ||
| 24 | |||
| 25 | Bu::Variant::Variant() : | ||
| 26 | pCore( NULL ) | ||
| 27 | { | ||
| 28 | } | ||
| 29 | |||
| 30 | Bu::Variant::Variant( const Variant &v ) : | ||
| 31 | pCore( NULL ) | ||
| 32 | { | ||
| 33 | if( v.pCore ) | ||
| 34 | { | ||
| 35 | pCore = v.pCore->clone(); | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | Bu::Variant::Variant( const char *t ) : | ||
| 40 | pCore( NULL ) | ||
| 41 | { | ||
| 42 | set( Bu::String( t ) ); | ||
| 43 | } | ||
| 44 | |||
| 45 | Bu::Variant::~Variant() | ||
| 46 | { | ||
| 47 | if( pCore ) | ||
| 48 | { | ||
| 49 | delete pCore; | ||
| 50 | pCore = NULL; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | Bu::String Bu::Variant::toString() const | ||
| 55 | { | ||
| 56 | Bu::MemBuf mb; | ||
| 57 | Bu::Formatter f( mb ); | ||
| 58 | f << *this; | ||
| 59 | return mb.getString(); | ||
| 60 | } | ||
| 61 | |||
| 62 | bool Bu::Variant::isSet() const | ||
| 63 | { | ||
| 64 | return pCore != NULL; | ||
| 65 | } | ||
| 66 | |||
| 67 | const std::type_info &Bu::Variant::getType() const | ||
| 68 | { | ||
| 69 | if( !pCore ) | ||
| 70 | { | ||
| 71 | throw Bu::ExceptionBase("No data!"); | ||
| 72 | } | ||
| 73 | return pCore->getType(); | ||
| 74 | } | ||
| 75 | |||
| 76 | Bu::Variant &Bu::Variant::operator=( const Bu::Variant &rhs ) | ||
| 77 | { | ||
| 78 | if( pCore ) | ||
| 79 | { | ||
| 80 | delete pCore; | ||
| 81 | pCore = NULL; | ||
| 82 | } | ||
| 83 | if( rhs.pCore ) | ||
| 84 | { | ||
| 85 | pCore = rhs.pCore->clone(); | ||
| 86 | } | ||
| 87 | |||
| 88 | return *this; | ||
| 89 | } | ||
| 90 | |||
| 91 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, const Bu::Variant &v ) | ||
| 92 | { | ||
| 93 | if( !v.pCore ) | ||
| 94 | return f << "(null)"; | ||
| 95 | |||
| 96 | v.pCore->format( f ); | ||
| 97 | return f; | ||
| 98 | } | ||
| 99 | |||
| diff --git a/src/stable/variant.h b/src/stable/variant.h new file mode 100644 index 0000000..45e3339 --- /dev/null +++ b/src/stable/variant.h | |||
| @@ -0,0 +1,236 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
| 3 | * | ||
| 4 | * This file is part of the libbu++ library and is released under the | ||
| 5 | * terms of the license contained in the file LICENSE. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef BU_VARIANT_H | ||
| 9 | #define BU_VARIANT_H | ||
| 10 | |||
| 11 | //#include <bu/string.h> | ||
| 12 | #include <typeinfo> | ||
| 13 | // #include <bu/formatter.h> | ||
| 14 | |||
| 15 | #ifndef NULL | ||
| 16 | #define NULL (0L) | ||
| 17 | #endif | ||
| 18 | |||
| 19 | #include "bu/exceptionbase.h" | ||
| 20 | |||
| 21 | namespace Bu | ||
| 22 | { | ||
| 23 | class String; | ||
| 24 | class Formatter; | ||
| 25 | class Variant; | ||
| 26 | /** @cond DEVEL */ | ||
| 27 | template<class t> class VariantType; | ||
| 28 | |||
| 29 | class VariantTypeRoot | ||
| 30 | { | ||
| 31 | public: | ||
| 32 | VariantTypeRoot(); | ||
| 33 | virtual ~VariantTypeRoot(); | ||
| 34 | |||
| 35 | virtual const std::type_info &getType() const=0; | ||
| 36 | virtual VariantTypeRoot *clone() const=0; | ||
| 37 | virtual void format( Bu::Formatter &f ) const=0; | ||
| 38 | }; | ||
| 39 | |||
| 40 | template<class t> | ||
| 41 | class VariantType : public VariantTypeRoot | ||
| 42 | { | ||
| 43 | friend class Variant; | ||
| 44 | private: | ||
| 45 | VariantType() | ||
| 46 | { | ||
| 47 | } | ||
| 48 | |||
| 49 | VariantType( const t &d ) : | ||
| 50 | data( d ) | ||
| 51 | { | ||
| 52 | } | ||
| 53 | |||
| 54 | VariantType( const VariantType<t> &vt ) : | ||
| 55 | data( vt.data ) | ||
| 56 | { | ||
| 57 | } | ||
| 58 | |||
| 59 | virtual ~VariantType() | ||
| 60 | { | ||
| 61 | } | ||
| 62 | |||
| 63 | public: | ||
| 64 | t &getData() | ||
| 65 | { | ||
| 66 | return data; | ||
| 67 | } | ||
| 68 | |||
| 69 | const t &getData() const | ||
| 70 | { | ||
| 71 | return data; | ||
| 72 | } | ||
| 73 | |||
| 74 | virtual void format( Formatter &f ) const | ||
| 75 | { | ||
| 76 | f << data; | ||
| 77 | } | ||
| 78 | |||
| 79 | virtual const std::type_info &getType() const | ||
| 80 | { | ||
| 81 | return typeid( data ); | ||
| 82 | } | ||
| 83 | |||
| 84 | VariantType<t> operator=( const t &rhs ) | ||
| 85 | { | ||
| 86 | data = rhs; | ||
| 87 | |||
| 88 | return *this; | ||
| 89 | } | ||
| 90 | |||
| 91 | virtual VariantTypeRoot *clone() const | ||
| 92 | { | ||
| 93 | return new VariantType<t>( *this ); | ||
| 94 | } | ||
| 95 | |||
| 96 | private: | ||
| 97 | t data; | ||
| 98 | }; | ||
| 99 | /** @endcond */ | ||
| 100 | |||
| 101 | /** | ||
| 102 | * Store any data type and access it safely. Variant gives you a way to | ||
| 103 | * pass arbitrary data types around without having to worry about what | ||
| 104 | * type a variable is. It allows code to be easily extended and to manage | ||
| 105 | * data without having to know what type it is ahead of time. | ||
| 106 | * | ||
| 107 | * Because of the generic method that this class was implemented it may seem | ||
| 108 | * to have some drawbacks compared to other Variant classes you may have | ||
| 109 | * seen, however it is fairly easy to get it to do just about anything you | ||
| 110 | * may need. It is also very low overhead. On most compilers the class | ||
| 111 | * itself has only 3 words of overhead + the size of the variable you store | ||
| 112 | * in it. And, since many parts of it are templatized they can often be | ||
| 113 | * optimized quite a bit. | ||
| 114 | */ | ||
| 115 | class Variant | ||
| 116 | { | ||
| 117 | friend Bu::Formatter &operator<<( Bu::Formatter &f, const Variant &v ); | ||
| 118 | public: | ||
| 119 | Variant(); | ||
| 120 | Variant( const Variant &v ); | ||
| 121 | Variant( const char *t ); | ||
| 122 | template<class t> | ||
| 123 | Variant( const t &v ) : | ||
| 124 | pCore( new VariantType<t>() ) | ||
| 125 | { | ||
| 126 | (*dynamic_cast<VariantType<t> *>(pCore)) = v; | ||
| 127 | } | ||
| 128 | virtual ~Variant(); | ||
| 129 | |||
| 130 | Bu::String toString() const; | ||
| 131 | bool isSet() const; | ||
| 132 | const std::type_info &getType() const; | ||
| 133 | |||
| 134 | Variant &operator=( const Variant &rhs ); | ||
| 135 | |||
| 136 | template<class t> | ||
| 137 | Variant &operator=( const t &rhs ) | ||
| 138 | { | ||
| 139 | if( pCore ) // && pCore->getType() != typeid(t) ) | ||
| 140 | { | ||
| 141 | delete pCore; | ||
| 142 | pCore = NULL; | ||
| 143 | } | ||
| 144 | pCore = new VariantType<t>(); | ||
| 145 | (*dynamic_cast<VariantType<t> *>(pCore)) = rhs; | ||
| 146 | return *this; | ||
| 147 | } | ||
| 148 | |||
| 149 | template<class t> | ||
| 150 | t &get() | ||
| 151 | { | ||
| 152 | if( !pCore ) | ||
| 153 | { | ||
| 154 | throw Bu::ExceptionBase("No data!"); | ||
| 155 | } | ||
| 156 | if( pCore->getType() != typeid(t) ) | ||
| 157 | { | ||
| 158 | throw Bu::ExceptionBase("Invalid type conversion."); | ||
| 159 | } | ||
| 160 | return dynamic_cast<VariantType<t> *>(pCore)->getData(); | ||
| 161 | } | ||
| 162 | |||
| 163 | template<class t> | ||
| 164 | t &get() const | ||
| 165 | { | ||
| 166 | if( !pCore ) | ||
| 167 | { | ||
| 168 | throw Bu::ExceptionBase("No data!"); | ||
| 169 | } | ||
| 170 | if( pCore->getType() != typeid(t) ) | ||
| 171 | { | ||
| 172 | throw Bu::ExceptionBase("Invalid type conversion."); | ||
| 173 | } | ||
| 174 | return dynamic_cast<VariantType<t> *>(pCore)->getData(); | ||
| 175 | } | ||
| 176 | |||
| 177 | template<class t> | ||
| 178 | void set( const t &val ) | ||
| 179 | { | ||
| 180 | if( pCore && pCore->getType() != typeid(t) ) | ||
| 181 | { | ||
| 182 | delete pCore; | ||
| 183 | pCore = NULL; | ||
| 184 | } | ||
| 185 | pCore = new VariantType<t>(); | ||
| 186 | (*dynamic_cast<VariantType<t> *>(pCore)) = val; | ||
| 187 | } | ||
| 188 | |||
| 189 | template<class t> | ||
| 190 | bool isType() const | ||
| 191 | { | ||
| 192 | return pCore->getType() == typeid(t); | ||
| 193 | } | ||
| 194 | |||
| 195 | template<class t> | ||
| 196 | operator t() | ||
| 197 | { | ||
| 198 | if( !pCore ) | ||
| 199 | { | ||
| 200 | throw Bu::ExceptionBase("No data!"); | ||
| 201 | } | ||
| 202 | if( pCore->getType() != typeid(t) ) | ||
| 203 | { | ||
| 204 | throw Bu::ExceptionBase("Invalid type conversion."); | ||
| 205 | } | ||
| 206 | return dynamic_cast<VariantType<t> *>(pCore)->getData(); | ||
| 207 | } | ||
| 208 | |||
| 209 | template<class t> | ||
| 210 | operator t() const | ||
| 211 | { | ||
| 212 | if( !pCore ) | ||
| 213 | { | ||
| 214 | throw Bu::ExceptionBase("No data!"); | ||
| 215 | } | ||
| 216 | if( pCore->getType() != typeid(t) ) | ||
| 217 | { | ||
| 218 | throw Bu::ExceptionBase("Invalid type conversion."); | ||
| 219 | } | ||
| 220 | return dynamic_cast<VariantType<t> *>(pCore)->getData(); | ||
| 221 | } | ||
| 222 | |||
| 223 | private: | ||
| 224 | VariantTypeRoot *pCore; | ||
| 225 | }; | ||
| 226 | /* | ||
| 227 | template<class t> | ||
| 228 | Bu::Formatter &operator<<( Bu::Formatter &f, const VariantType<t> &vt ) | ||
| 229 | { | ||
| 230 | return f << vt.getData; | ||
| 231 | }*/ | ||
| 232 | |||
| 233 | Bu::Formatter &operator<<( Bu::Formatter &f, const Variant &v ); | ||
| 234 | }; | ||
| 235 | |||
| 236 | #endif | ||
