diff options
author | Mike Buland <eichlan@xagasoft.com> | 2012-03-25 20:00:08 +0000 |
---|---|---|
committer | Mike Buland <eichlan@xagasoft.com> | 2012-03-25 20:00:08 +0000 |
commit | 469bbcf0701e1eb8a6670c23145b0da87357e178 (patch) | |
tree | b5b062a16e46a6c5d3410b4e574cd0cc09057211 /src/stable | |
parent | ee1b79396076edc4e30aefb285fada03bb45e80d (diff) | |
download | libbu++-469bbcf0701e1eb8a6670c23145b0da87357e178.tar.gz libbu++-469bbcf0701e1eb8a6670c23145b0da87357e178.tar.bz2 libbu++-469bbcf0701e1eb8a6670c23145b0da87357e178.tar.xz libbu++-469bbcf0701e1eb8a6670c23145b0da87357e178.zip |
Code is all reorganized. We're about ready to release. I should write up a
little explenation of the arrangement.
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 | ||