aboutsummaryrefslogtreecommitdiff
path: root/src/unstable
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2012-03-25 20:00:08 +0000
committerMike Buland <eichlan@xagasoft.com>2012-03-25 20:00:08 +0000
commit469bbcf0701e1eb8a6670c23145b0da87357e178 (patch)
treeb5b062a16e46a6c5d3410b4e574cd0cc09057211 /src/unstable
parentee1b79396076edc4e30aefb285fada03bb45e80d (diff)
downloadlibbu++-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/unstable')
-rw-r--r--src/unstable/bitstring.cpp479
-rw-r--r--src/unstable/bitstring.h224
-rw-r--r--src/unstable/fifo.cpp162
-rw-r--r--src/unstable/fifo.h72
-rw-r--r--src/unstable/itoserver.cpp242
-rw-r--r--src/unstable/itoserver.h141
-rw-r--r--src/unstable/minimacro.cpp187
-rw-r--r--src/unstable/minimacro.h130
-rw-r--r--src/unstable/myriad.cpp663
-rw-r--r--src/unstable/myriad.h222
-rw-r--r--src/unstable/myriadfs.cpp703
-rw-r--r--src/unstable/myriadfs.h203
-rw-r--r--src/unstable/myriadstream.cpp309
-rw-r--r--src/unstable/myriadstream.h61
-rw-r--r--src/unstable/newline.cpp68
-rw-r--r--src/unstable/newline.h41
-rw-r--r--src/unstable/udpsocket.cpp240
-rw-r--r--src/unstable/udpsocket.h84
-rw-r--r--src/unstable/url.cpp285
-rw-r--r--src/unstable/url.h85
-rw-r--r--src/unstable/urn.cpp8
-rw-r--r--src/unstable/urn.h8
-rw-r--r--src/unstable/utfstring.cpp539
-rw-r--r--src/unstable/utfstring.h174
-rw-r--r--src/unstable/uuid.cpp117
-rw-r--r--src/unstable/uuid.h56
26 files changed, 5503 insertions, 0 deletions
diff --git a/src/unstable/bitstring.cpp b/src/unstable/bitstring.cpp
new file mode 100644
index 0000000..bdd1bc2
--- /dev/null
+++ b/src/unstable/bitstring.cpp
@@ -0,0 +1,479 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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/bitstring.h"
9#include <stdlib.h>
10#include <stdio.h>
11#include <string.h>
12
13#include "bu/exceptionbase.h"
14
15#ifdef _WIN32
16#define random() rand()
17#endif
18
19#define bitsToBytes( iBits ) (((iBits/8)+((iBits%8)?(1):(0))));
20
21Bu::BitString::BitString()
22{
23 caData = NULL;
24 cTopByteMask = 0;
25 iBits = iBytes = 0;
26}
27
28Bu::BitString::BitString( const Bu::BitString &xSrc )
29{
30 iBits = xSrc.iBits;
31 iBytes = xSrc.iBytes;
32 cTopByteMask = xSrc.cTopByteMask;
33 caData = new unsigned char[iBytes];
34 memcpy( caData, xSrc.caData, iBytes );
35
36 fixup();
37}
38
39Bu::BitString::BitString( long iNewBits, bool bFillRandomly )
40{
41 long j;
42 iBits = iNewBits;
43 iBytes = bitsToBytes( iNewBits );//(iNewBits/8)+((iNewBits%8)?(1):(0));
44 caData = new unsigned char[iBytes];
45
46 setMask();
47
48 if( bFillRandomly )
49 {
50 // rand() only returns a value up to RAND_MAX (0x7FFF on my system) so
51 // I'll just use the low order byte)
52 for( j = 0; j < iBytes; j++ )
53 {
54 caData[j] = (unsigned char)(random() & 0xFF);
55 }
56 }
57 else
58 {
59 clear();
60 }
61
62 fixup();
63}
64
65Bu::BitString::~BitString()
66{
67 if( caData != NULL ) delete[] caData;
68}
69
70Bu::BitString &Bu::BitString::operator=( const Bu::BitString &xSrc )
71{
72 if( caData != NULL )
73 {
74 delete[] caData;
75 }
76 iBits = xSrc.iBits;
77 iBytes = xSrc.iBytes;
78 cTopByteMask = xSrc.cTopByteMask;
79 caData = new unsigned char[iBytes];
80 memcpy( caData, xSrc.caData, iBytes );
81
82 fixup();
83
84 return *this;
85}
86
87Bu::BitString Bu::BitString::operator~()
88{
89 Bu::BitString xRet( *this );
90
91 for( int j = 0; j < xRet.iBytes; j++ )
92 {
93 xRet.caData[j] = ~xRet.caData[j];
94 }
95
96 xRet.fixup();
97
98 return xRet;
99}
100
101Bu::BitString Bu::BitString::operator<<( const long iAmt )
102{
103 if( iAmt == 0 )
104 {
105 return (*this);
106 }
107 //int iByteShift = iAmt/8;
108
109 Bu::BitString xSub( getSize() );
110
111 long shft = (iAmt%8);
112 long base = (iAmt/8);
113 unsigned char lowmask=0;
114 for( long j = 0; j < 8-shft; j++ )
115 {
116 lowmask |= (1<<j);
117 }
118 for( long j = 0; j < xSub.iBytes; j++ )
119 {
120 xSub.caData[base+j] = ((caData[j]>>shft)&(lowmask)) | ((caData[j+1]<<(8-shft))&(~lowmask));
121 }
122 xSub.fixup();
123
124 return xSub;
125}
126
127Bu::BitString Bu::BitString::operator>>( const long iAmt )
128{
129 if( iAmt == 0 )
130 {
131 return (*this);
132 }
133 return (*this);
134}
135
136void Bu::BitString::shiftLeft( long iAmt )
137{
138 if( iAmt == 0 )
139 {
140 return;
141 }
142 else if( iAmt < 0 )
143 {
144 shiftRight( -iAmt );
145 return;
146 }
147
148 long iByteShift = iAmt/8;
149 long iBitShift = iAmt%8;
150
151 long j;
152 for( j = iBytes-1; j >= 0; j-- )
153 {
154 caData[j] = (((j-iByteShift)<0)?(0):((caData[j-iByteShift]<<iBitShift))) | (((j-iByteShift-1)<0)?(0):((caData[j-iByteShift-1]>>(8-iBitShift))));
155 }
156
157 fixup();
158}
159
160void Bu::BitString::shiftRight( long iAmt )
161{
162 if( iAmt == 0 )
163 {
164 return;
165 }
166 else if( iAmt < 0 )
167 {
168 shiftLeft( -iAmt );
169 return;
170 }
171
172 long iByteShift = iAmt/8;
173 long iBitShift = iAmt%8;
174
175 long j;
176 for( j = 0; j < iBytes; j++ )
177 {
178 caData[j] = (((j+iByteShift)>iBytes)?(0):((caData[j+iByteShift]>>iBitShift))) | (((j+iByteShift+1)>iBytes)?(0):((caData[j+iByteShift+1]<<(8-iBitShift))));
179 }
180
181 fixup();
182}
183/*
184long Bu::BitString::bitsToBytes( long iBits )
185{
186 return (iBits/8)+((iBits%8)?(1):(0));
187}
188*/
189void Bu::BitString::fixup()
190{
191 if( caData != NULL )
192 {
193 caData[iBytes-1] &= cTopByteMask;
194 }
195}
196
197void Bu::BitString::setBit( long iBit, bool bBitState )
198{
199 if( iBit < 0 || iBit >= iBits )
200 throw Bu::ExceptionBase("bit out of range: %d in (0-%d)", iBit, iBits );
201 if( bBitState )
202 {
203 caData[iBit/8] |= (1<<(iBit%8));
204 }
205 else
206 {
207 caData[iBit/8] &= ~(1<<(iBit%8));
208 }
209}
210
211void Bu::BitString::flipBit( long iBit )
212{
213 caData[iBit/8] ^= (1<<(iBit%8));
214}
215
216bool Bu::BitString::getBit( long iBit )
217{
218 if( iBit >= iBits || iBit < 0 ) return false;
219 if( (caData[iBit/8] & (1<<(iBit%8))) == 0 )
220 {
221 return false;
222 }
223 return true;
224}
225
226long Bu::BitString::getBitLength()
227{
228 return iBits;
229}
230
231long Bu::BitString::getSize()
232{
233 return iBits;
234}
235
236class Bu::BitString Bu::BitString::getSubString( long iLower, long iUpper )
237{
238 if( iUpper == 0 || iUpper < iLower ) iUpper = iBits;
239
240 Bu::BitString xSub( iUpper-iLower+1 );
241
242 long shft = (iLower%8);
243 long base = (iLower/8);
244 unsigned char lowmask=0;
245 for( long j = 0; j < 8-shft; j++ )
246 {
247 lowmask |= (1<<j);
248 }
249 for( long j = 0; j < xSub.iBytes; j++ )
250 {
251 xSub.caData[j] = ((caData[base+j]>>shft)&(lowmask)) | ((caData[base+j+1]<<(8-shft))&(~lowmask));
252 }
253 xSub.fixup();
254
255 return xSub;
256}
257
258long Bu::BitString::toLong( long iStart, long iSize )
259{
260 if( iSize < 1 ) iSize = 1;
261 if( iSize > 32 ) iSize = 32;
262 if( iStart < 0 ) return 0;
263 if( iStart+iSize > getSize() ) return 0;
264
265 Bu::BitString tmpo;
266 tmpo = getSubString( iStart, iStart+iSize-1 );
267 long x = *((long *)tmpo.caData);
268
269 return x;
270}
271/*
272std::string Bu::BitString::toString( bool bAddSpacers )
273{
274 long iSz = iBits;
275 if( bAddSpacers )
276 {
277 iSz += (iBits/8);
278 if( iBits%8 == 0 ) iSz--;
279 }
280 std::string xStr;
281
282 int bw=0;
283 int of=0;
284 for( int j = iBits-1; j >= 0; j-- )
285 {
286 if( getBit( j ) )
287 {
288 xStr += '1';
289 }
290 else
291 {
292 xStr += '0';
293 }
294
295 if( bAddSpacers )
296 {
297 bw++;
298 if( bw >= 8 && j < iBits-1 )
299 {
300 bw = 0;
301 of++;
302 xStr += ' ';
303 }
304 }
305 }
306
307 return xStr;
308}
309*/
310void Bu::BitString::clear()
311{
312 if( caData != NULL )
313 {
314 memset( caData, 0, iBytes );
315 }
316}
317
318bool Bu::BitString::setBitLength( long iLength, bool bClear )
319{
320 return setSize( iLength, bClear );
321}
322
323bool Bu::BitString::setSize( long iLength, bool bClear )
324{
325 // First, if there's nothing, then allocate an empty one.
326 if( caData == NULL )
327 {
328 iBits = iLength;
329 iBytes = bitsToBytes( iLength );
330 caData = new unsigned char[iBytes];
331 memset( caData, 0, iBytes );
332 return true;
333 }
334
335 // If the new length is the same as the old, don't do anything, but do
336 // check to see if we should still clear the data.
337 if( iBits != iLength )
338 {
339 // Ok, we are changing the number if bits, but are we changing the
340 // number of bytes?
341 long iNewBytes = bitsToBytes( iLength );
342 if( iBytes == iNewBytes )
343 {
344 // No? That makes life easier
345 iBits = iLength;
346 setMask();
347 if( bClear )
348 {
349 clear();
350 }
351 }
352 else
353 {
354 // Ok, reallocate and copy...
355 iBits = iLength;
356// long iNewBytes = bitsToBytes( iLength );
357 if( bClear )
358 {
359 delete[] caData;
360 caData = new unsigned char[iNewBytes];
361 memset( caData, 0, iNewBytes );
362 }
363 else
364 {
365 unsigned char *tmp = caData;
366 caData = new unsigned char[iNewBytes];
367 if( iNewBytes < iBytes )
368 {
369 memcpy( caData, tmp, iNewBytes );
370 }
371 else
372 {
373 memcpy( caData, tmp, iBytes );
374 }
375 delete[] tmp;
376 }
377 iBytes = iNewBytes;
378
379 setMask();
380 }
381
382 }
383 else if( bClear )
384 {
385 clear();
386 }
387
388 return true;
389}
390
391void Bu::BitString::setMask()
392{
393 // This can either mean that there are a multiple of eight bits or
394 // zero, if there are zero you're an idiot (zero can't happen, because
395 // we would allocate an extra byte and never use it)
396 if( (iBits%8 == 0) )
397 {
398 cTopByteMask = 0xFF;
399 }
400 else
401 {
402 cTopByteMask = 0;
403 for( long j = 0; j < (iBits%8); j++ )
404 {
405 cTopByteMask |= (1<<j);
406 }
407 }
408}
409
410void Bu::BitString::randomize()
411{
412 if( caData != NULL )
413 {
414 for( int j = 0; j < iBytes; j++ )
415 {
416 caData[j] = (unsigned char)(random() & 0xFF);
417 }
418 fixup();
419 }
420}
421
422void Bu::BitString::invert()
423{
424 if( caData != NULL )
425 {
426 for( long j = 0; j < iBytes; j++ )
427 {
428 caData[j] = ~caData[j];
429 }
430 fixup();
431 }
432}
433
434long Bu::BitString::getHighestOrderBitPos()
435{
436 for( long j = iBits-1; j >= 0; j-- )
437 {
438 if( getBit( j ) )
439 {
440 return j;
441 }
442 }
443
444 return -1;
445}
446
447Bu::String Bu::BitString::toString()
448{
449 Bu::String sRet;
450 for( int j = iBits-1; j >= 0; j-- )
451 sRet.append( getBit( j )?'1':'0' );
452 return sRet;
453}
454
455/*
456bool Bu::BitString::writeToFile( FILE *fh )
457{
458 fwrite( &iBits, sizeof(long), 1, fh );
459 fwrite( caData, sizeof(char), iBytes, fh );
460
461 return true;
462}
463
464bool Bu::BitString::readFromFile( FILE *fh )
465{
466 fread( &iBits, sizeof(long), 1, fh );
467
468 iBytes = bitsToBytes( iBits );
469 if( caData ) delete[] caData;
470 caData = new unsigned char[iBytes];
471
472 fread( caData, sizeof(char), iBytes, fh );
473
474 setMask();
475
476 fixup();
477
478 return true;
479}*/
diff --git a/src/unstable/bitstring.h b/src/unstable/bitstring.h
new file mode 100644
index 0000000..7a8fc48
--- /dev/null
+++ b/src/unstable/bitstring.h
@@ -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#ifndef BU_BITSTRING_H
9#define BU_BITSTRING_H
10
11#include "bu/util.h"
12#include "bu/string.h"
13
14namespace Bu
15{
16 /**
17 * Manages an arbitrarily sized string of bits, and allows basic interaction
18 * with them. This includes basic non-mathematical bitwise operations such
19 * as setting and testing bits, shifting the string, inversion and a few
20 * extras like randomization. On linux systems this takes advantage of long
21 * longs giving you a maximum size of about 2tb per string.
22 *
23 * For more general and mathematical type interaction see BitStringInt.
24 *
25 */
26 class BitString
27 {
28 public:
29 /**
30 * Constructs a blank and basic BitString. This is actually useful
31 * since you can resize BitStrings at will, and even retain the data
32 * that was in them.
33 */
34 BitString();
35
36 /**
37 * Constructs a BitString object as a copy of another BitString. This
38 * is a standard copy constructor and produces an exact duplicate of
39 * the original BitString object.
40 *@param xSrc Source BitString to copy data from.
41 */
42 BitString( const BitString &xSrc );
43
44 /**
45 * Constructs a BitString with length iBits and optionally fills it with
46 * random data. The default setting, to not fill randomly, will produce
47 * a blank (all zeros) string of the specified size.
48 *@param iBits The length of the new BitString in bits.
49 *@param bFillRandomly Wether or not to randomize this BitString.
50 */
51 BitString( long iBits, bool bFillRandomly=false );
52
53 /**
54 * Virtual deconstructor for the BitString. Takes care of cleanup for
55 * you. What more do you really want to know?
56 */
57 virtual ~BitString();
58
59 // basic interaction
60 /**
61 * Sets a bit in the BitString. In it's normal mode it will always turn
62 * the given bit on, to clear a bit set bBitState to false instead of
63 * true. This operation runs in O(1).
64 *@param iBit The zero-based index of the bit to modify.
65 *@param bBitState Set to true to set the bit to 1, set to false to set
66 * the bit to 0.
67 */
68 void setBit( long iBit, bool bBitState=true );
69
70 /**
71 * Reverses the state of the given bit. This will set the given bit
72 * to a 1 if it was 0, and to 0 if it was 1. This operation runs in
73 * O(1), and it should be noted that using this is marginally faster
74 * than doing the test and flip yourself with getBit and setBit since
75 * it uses a bitwise not operation and doesn't actually test the bit
76 * itself.
77 *@param iBit The index of the bit to flip.
78 */
79 void flipBit( long iBit );
80
81 /**
82 * Gets the state of the given bit. This follows the standard
83 * convention used so far, a returned value of true means the bit in
84 * question is 1, and a value of flase means the bit is 0. All bits
85 * out of range of the BitString are treated as off, but are
86 * "accessable" in that this does not produce any kind of error
87 * message. This is intentional. This operation runs in O(1).
88 *@param iBit The index of the bit to test.
89 *@returns True for a 1, false for a 0.
90 */
91 bool getBit( long iBit );
92
93 /**
94 * Inverts the entire BitString, in effect this calls flipBit on every
95 * bit in the string but is faster since it can operate on whole bytes
96 * at a time instead of individual bits. This operation runs in O(N).
97 */
98 void invert();
99
100 /**
101 * Returns the number of bits allocated in this BitString. This
102 * operation runs in O(1) time since this value is cached and not
103 * computed.
104 *@returns The number of bits allocated in this BitString.
105 */
106 DEPRECATED
107 long getBitLength();
108
109 long getSize();
110
111 /**
112 * Sets the entire BitString to zeros, but it does it very quickly.
113 * This operation runs in O(N).
114 */
115 void clear();
116
117 /**
118 * Gets another BitString that is autonomous of the current one
119 * (contains a copy of the memory, not a pointer) and contains a subset
120 * of the data in the current BitString. This is an inclusive
121 * operation, so grabbing bits 0-5 will give you 6 bits. This is based
122 * on a very tricky bit-shifting algorithm and runs very quickly, in
123 * O(N) time. Passing in a value of zero for iUpper, or any value for
124 * iUpper that is less than iLower will set iUpper equal to the number
125 * of bits in the BitString.
126 *@param iLower The first bit in the current string, will be the first
127 * bit (0 index) in the new sub string.
128 *@param iUpper The last bit in the current string, will be the last
129 * bit in the new sub string. iUpper is included in the sub string.
130 *@returns A new BitString object ready to be used. Please note that
131 * managing this new object is up to whomever calls this function.
132 */
133 class BitString getSubString( long iLower, long iUpper );
134
135 /**
136 * Sets the number of bits in the BitString, allocating more memory if
137 * necesarry, or freeing extra if able. The default operation of this
138 * function clears all data in the BitString while resizing it. If you
139 * would like to keep as much of the data that you had in your BitString
140 * as possible, then set bClear to false, and any data that will fit
141 * into the new BitString length will be retained. If increasing the
142 * number of bits, the new bits will come into existance cleared (set
143 * to 0).
144 *@param iLength The number of bits to set the BitString to.
145 *@param bClear When true, all data is eradicated and zeroed, when set
146 * to false an effort is made to retain the existing data.
147 *@returns true on success, false on failure.
148 */
149 DEPRECATED
150 bool setBitLength( long iLength, bool bClear=true );
151 bool setSize( long iLength, bool bClear=true );
152
153 /**
154 * Randomize the entire BitString, one bit at a time. This is actually
155 * the function called by the constructor when the user selects initial
156 * randomization. This function uses the system random() function, so
157 * srandom may be used to effect this process at will.
158 */
159 void randomize();
160
161 /**
162 * Operates exactly like <<. All data in the BitString is shifted to
163 * the left by some number of bits, any data pushed off the edge of the
164 * BitString is lost, and all new data coming in will be zeroed.
165 * Using a negative value in the shiftLeft function will turn it into
166 * the shiftRight function.
167 *@param iAmt The number of bit positions to shift all data.
168 */
169 void shiftLeft( long iAmt ); // just like <<
170
171 /**
172 * Operates exactly like >>. All data in the BitString is shifted to
173 * the right by some number of bits, any data pushed off the edge of the
174 * BitString is lost, and all new data coming in will be zeroed.
175 * Using a negative value in the shiftRight function will turn it into
176 * the shiftLeft function.
177 *@param iAmt The number of bit positions to shift all data.
178 */
179 void shiftRight( long iAmt ); // just like >>
180
181 /**
182 * Searches through the BitString and returns the index of the highest
183 * order bit position (the highest index) with an on bit (a bit set to
184 * 1). This is a handy helper function and rather faster than calling
185 * getBit() over and over again.
186 *@returns The index of the highest indexed on bit.
187 */
188 long getHighestOrderBitPos();
189
190 // Conversion
191 /**
192 * Convert a block of data (no more than 32 bits) to a primitive long
193 * type.
194 * This is done in a little bit interesting way, so it may not always be
195 * the fastest way to access the data that you want, although it will
196 * always ensure that the long that is written makes numerical sense, as
197 * we write numbers, regaurdless of platform.
198 *@param iStart The first bit in the BitString to include in the long
199 *@param iSize THe number of bits to include, if this value is set over
200 * 32 it will be automatically truncated to, or however many bits there
201 * are in a long in your system.
202 *@returns A long converted from your raw BitString data.
203 */
204 long toLong( long iStart = 0, long iSize = 32 );
205
206 Bu::String toString();
207
208 //operators
209 BitString &operator=( const BitString &xSrc );
210 BitString operator~();
211 BitString operator<<( const long iAmt );
212 BitString operator>>( const long iAmt );
213
214 private:
215 void fixup();
216 void setMask();
217 unsigned char *caData;
218 long iBits;
219 long iBytes;
220 unsigned char cTopByteMask;
221 };
222};
223
224#endif
diff --git a/src/unstable/fifo.cpp b/src/unstable/fifo.cpp
new file mode 100644
index 0000000..b0cf1c7
--- /dev/null
+++ b/src/unstable/fifo.cpp
@@ -0,0 +1,162 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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/fifo.h"
9#include <errno.h>
10#include <sys/types.h>
11#include <sys/stat.h>
12#include <fcntl.h>
13#include <unistd.h>
14
15#include "bu/config.h"
16
17namespace Bu { subExceptionDef( FifoException ) }
18
19Bu::Fifo::Fifo( const Bu::String &sName, int iFlags, mode_t mAcc ) :
20 iFlags( iFlags ),
21 iIn( -1 ),
22 iOut( -1 )
23{
24#ifndef WIN32
25 if( iFlags&Create )
26 {
27 if( mkfifo( sName.getStr(), mAcc ) )
28 {
29 throw FifoException("Error creating fifo: %s\n", strerror( errno ) );
30 }
31 }
32 if( iFlags&Read )
33 {
34 iIn = ::open(
35 sName.getStr(),
36 O_RDONLY|((iFlags&NonBlock)?O_NONBLOCK:0)
37 );
38 }
39 if( iFlags&Write )
40 {
41 iOut = ::open(
42 sName.getStr(),
43 O_WRONLY
44 );
45 }
46#else
47 #warning Bu::Fifo::Fifo IS A STUB for WIN32!!!!
48#endif
49}
50
51Bu::Fifo::~Fifo()
52{
53 close();
54}
55
56void Bu::Fifo::close()
57{
58 if( iIn > -1 )
59 {
60 ::close( iIn );
61 iIn = -1;
62 }
63 if( iOut > -1 )
64 {
65 ::close( iOut );
66 iOut = -1;
67 }
68}
69
70Bu::size Bu::Fifo::read( void *pBuf, Bu::size nBytes )
71{
72 if( iIn < 0 )
73 throw FifoException("Fifo not open for reading.");
74
75 return TEMP_FAILURE_RETRY( ::read( iIn, pBuf, nBytes ) );
76}
77
78Bu::size Bu::Fifo::write( const void *pBuf, Bu::size nBytes )
79{
80 if( iOut < 0 )
81 throw FifoException("Fifo not open for writing.");
82
83 return TEMP_FAILURE_RETRY( ::write( iOut, pBuf, nBytes ) );
84}
85
86Bu::size Bu::Fifo::tell()
87{
88 return -1;
89}
90
91void Bu::Fifo::seek( Bu::size )
92{
93}
94
95void Bu::Fifo::setPos( Bu::size )
96{
97}
98
99void Bu::Fifo::setPosEnd( Bu::size )
100{
101}
102
103bool Bu::Fifo::isEos()
104{
105 return false;
106}
107
108bool Bu::Fifo::canRead()
109{
110 return (iIn>-1);
111}
112
113bool Bu::Fifo::canWrite()
114{
115 return (iOut>-1);
116}
117
118bool Bu::Fifo::isReadable()
119{
120 return (iIn>-1);
121}
122
123bool Bu::Fifo::isWritable()
124{
125 return (iOut>-1);
126}
127
128bool Bu::Fifo::isSeekable()
129{
130 return false;
131}
132
133bool Bu::Fifo::isBlocking()
134{
135#ifndef WIN32
136 return ((fcntl( iIn, F_GETFL, 0 )&O_NONBLOCK) == O_NONBLOCK);
137#else
138 #warning Bu::Fifo::isBlocking IS A STUB for WIN32!!!!
139#endif
140}
141
142void Bu::Fifo::setBlocking( bool bBlocking )
143{
144#ifndef WIN32
145 if( bBlocking )
146 fcntl( iIn, F_SETFL, fcntl( iIn, F_GETFL, 0 )&(~O_NONBLOCK) );
147 else
148 fcntl( iIn, F_SETFL, fcntl( iIn, F_GETFL, 0 )|O_NONBLOCK );
149#else
150 #warning Bu::Fifo::setBlocking IS A STUB for WIN32!!!!
151#endif
152}
153
154void Bu::Fifo::flush()
155{
156}
157
158bool Bu::Fifo::isOpen()
159{
160 return (iIn > -1) || (iOut > -1);
161}
162
diff --git a/src/unstable/fifo.h b/src/unstable/fifo.h
new file mode 100644
index 0000000..18a70ba
--- /dev/null
+++ b/src/unstable/fifo.h
@@ -0,0 +1,72 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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_FIFO_H
9#define BU_FIFO_H
10
11#include <stdint.h>
12#include <sys/types.h>
13#include <stdlib.h>
14
15#include "bu/stream.h"
16#include "bu/string.h"
17#include "bu/exceptionbase.h"
18
19namespace Bu
20{
21 subExceptionDecl( FifoException );
22
23 /**
24 * A fifo stream.
25 *@ingroup Streams
26 */
27 class Fifo : public Bu::Stream
28 {
29 public:
30 Fifo( const Bu::String &sName, int iFlags, mode_t mAcc=-1 );
31 virtual ~Fifo();
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 Read = 0x01,
59 Write = 0x02,
60 Create = 0x04,
61 Delete = 0x08,
62 NonBlock = 0x10
63 };
64
65 private:
66 int iFlags;
67 int iIn;
68 int iOut;
69 };
70}
71
72#endif
diff --git a/src/unstable/itoserver.cpp b/src/unstable/itoserver.cpp
new file mode 100644
index 0000000..c7165e2
--- /dev/null
+++ b/src/unstable/itoserver.cpp
@@ -0,0 +1,242 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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/itoserver.h"
9#include <errno.h>
10#include "bu/tcpserversocket.h"
11#include "bu/client.h"
12#include "bu/tcpsocket.h"
13
14#include "bu/config.h"
15
16Bu::ItoServer::ItoServer() :
17 nTimeoutSec( 1 ),
18 nTimeoutUSec( 0 )
19{
20 FD_ZERO( &fdActive );
21}
22
23Bu::ItoServer::~ItoServer()
24{
25 while( !qClientCleanup.isEmpty() )
26 {
27 ItoClient *pCli = qClientCleanup.dequeue();
28 pCli->join();
29 delete pCli;
30 }
31 // TODO: Make sure here that each client has shutdown it's socket, and
32 // maybe even written any extra data, we could put a timelimit on this...
33 // anyway, it's not as clean as it could be right now.
34 for( ClientHash::iterator i = hClients.begin(); i != hClients.end(); i++ )
35 {
36 ItoClient *pCli = (*i);
37 pCli->join();
38 delete pCli;
39 }
40}
41
42void Bu::ItoServer::addPort( int nPort, int nPoolSize )
43{
44 TcpServerSocket *s = new TcpServerSocket( nPort, nPoolSize );
45 int nSocket = s->getSocket();
46 FD_SET( nSocket, &fdActive );
47 hServers.insert( nSocket, s );
48}
49
50void Bu::ItoServer::addPort( const String &sAddr, int nPort, int nPoolSize )
51{
52 TcpServerSocket *s = new TcpServerSocket( sAddr, nPort, nPoolSize );
53 int nSocket = s->getSocket();
54 FD_SET( nSocket, &fdActive );
55 hServers.insert( nSocket, s );
56}
57
58void Bu::ItoServer::setTimeout( int nTimeoutSec, int nTimeoutUSec )
59{
60 this->nTimeoutSec = nTimeoutSec;
61 this->nTimeoutUSec = nTimeoutUSec;
62}
63
64void Bu::ItoServer::addClient( int nSocket, int nPort )
65{
66 ItoClient *pC = new ItoClient( *this, nSocket, nPort, nTimeoutSec,
67 nTimeoutUSec );
68
69 imClients.lock();
70 hClients.insert( nSocket, pC );
71 imClients.unlock();
72
73 pC->start();
74}
75
76void Bu::ItoServer::run()
77{
78 for(;;)
79 {
80 struct timeval xTimeout = { nTimeoutSec, nTimeoutUSec };
81
82 fd_set fdRead = fdActive;
83 //fd_set fdWrite = fdActive;
84 fd_set fdException = fdActive;
85
86 if( TEMP_FAILURE_RETRY( select( FD_SETSIZE, &fdRead, NULL, &fdException, &xTimeout ) ) < 0 )
87 {
88 throw ExceptionBase("Error attempting to scan open connections.");
89 }
90
91 for( ServerHash::iterator i = hServers.begin(); i != hServers.end(); i++ )
92 {
93 if( FD_ISSET( i.getKey(), &fdRead ) )
94 {
95 TcpServerSocket *pSrv = i.getValue();
96 addClient( pSrv->accept(), pSrv->getPort() );
97 }
98 }
99
100 while( !qClientCleanup.isEmpty() )
101 {
102 ItoClient *pCli = qClientCleanup.dequeue();
103 pCli->join();
104 delete pCli;
105 }
106 }
107}
108
109void Bu::ItoServer::clientCleanup( int iSocket )
110{
111 imClients.lock();
112 ItoClient *pCli = hClients.get( iSocket );
113 imClients.unlock();
114 qClientCleanup.enqueue( pCli );
115}
116
117Bu::ItoServer::ItoClient::ItoClient( ItoServer &rSrv, int iSocket, int iPort,
118 int nTimeoutSec, int nTimeoutUSec ) :
119 rSrv( rSrv ),
120 iSocket( iSocket ),
121 iPort( iPort ),
122 nTimeoutSec( nTimeoutSec ),
123 nTimeoutUSec( nTimeoutUSec )
124{
125 FD_ZERO( &fdActive );
126 FD_SET( iSocket, &fdActive );
127
128 pClient = new Client(
129 new Bu::TcpSocket( iSocket ),
130 new SrvClientLinkFactory( rSrv )
131 );
132}
133
134Bu::ItoServer::ItoClient::~ItoClient()
135{
136}
137
138void Bu::ItoServer::ItoClient::run()
139{
140 imProto.lock();
141 rSrv.onNewConnection( pClient, iPort );
142 pClient->processOutput();
143 imProto.unlock();
144
145 for(;;)
146 {
147 struct timeval xTimeout = { nTimeoutSec, nTimeoutUSec };
148
149 fd_set fdRead = fdActive;
150 fd_set fdWrite;
151 fd_set fdException = fdActive;
152
153 FD_ZERO( &fdWrite );
154 if( pClient->hasOutput() )
155 FD_SET( iSocket, &fdWrite );
156
157 if( TEMP_FAILURE_RETRY( select( FD_SETSIZE,
158 &fdRead, &fdWrite, &fdException, &xTimeout ) ) < 0 )
159 {
160 throw ExceptionBase("Error attempting to scan open connections.");
161 }
162
163 while( !qMsg.isEmpty() )
164 {
165 imProto.lock();
166 Bu::String *pMsg = qMsg.dequeue();
167 pClient->onMessage( *pMsg );
168 delete pMsg;
169 pClient->processOutput();
170 imProto.unlock();
171 }
172
173 if( FD_ISSET( iSocket, &fdRead ) )
174 {
175 imProto.lock();
176 pClient->processInput();
177 imProto.unlock();
178 if( !pClient->isOpen() )
179 {
180 imProto.lock();
181 rSrv.onClosedConnection( pClient );
182 imProto.unlock();
183
184 rSrv.clientCleanup( iSocket );
185
186 return;
187 }
188 }
189
190 if( FD_ISSET( iSocket, &fdWrite ) )
191 {
192 imProto.lock();
193 pClient->processOutput();
194 imProto.unlock();
195 }
196 }
197}
198
199Bu::ItoServer::SrvClientLink::SrvClientLink( ItoClient *pClient ) :
200 pClient( pClient )
201{
202}
203
204Bu::ItoServer::SrvClientLink::~SrvClientLink()
205{
206}
207
208void Bu::ItoServer::SrvClientLink::sendMessage( const Bu::String &sMsg )
209{
210 if( !pClient->imProto.trylock() )
211 {
212 pClient->pClient->onMessage( sMsg );
213 pClient->pClient->processOutput();
214 pClient->imProto.unlock();
215 }
216 else
217 {
218 Bu::String *pMsg = new Bu::String( sMsg );
219 pClient->qMsg.enqueue( pMsg );
220 }
221}
222
223Bu::ItoServer::SrvClientLinkFactory::SrvClientLinkFactory(
224 Bu::ItoServer &rSrv ) :
225 rSrv( rSrv )
226{
227}
228
229Bu::ItoServer::SrvClientLinkFactory::~SrvClientLinkFactory()
230{
231}
232
233Bu::ClientLink *Bu::ItoServer::SrvClientLinkFactory::createLink(
234 Bu::Client *pClient )
235{
236 rSrv.imClients.lock();
237 ItoClient *pCli = rSrv.hClients.get( *pClient->getSocket() );
238 rSrv.imClients.unlock();
239
240 return new SrvClientLink( pCli );
241}
242
diff --git a/src/unstable/itoserver.h b/src/unstable/itoserver.h
new file mode 100644
index 0000000..81db4e2
--- /dev/null
+++ b/src/unstable/itoserver.h
@@ -0,0 +1,141 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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_ITO_SERVER_H
9#define BU_ITO_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#include "bu/thread.h"
20#include "bu/mutex.h"
21#include "bu/synchroqueue.h"
22#include "bu/hash.h"
23
24#include "bu/clientlink.h"
25#include "bu/clientlinkfactory.h"
26
27namespace Bu
28{
29 class TcpServerSocket;
30 class TcpSocket;
31 class Client;
32
33 /**
34 * Core of a network server. This class is distinct from a ServerSocket in
35 * that a ServerSocket is one listening socket, nothing more. Socket will
36 * manage a pool of both ServerSockets and connected Sockets along with
37 * their protocols and buffers.
38 *
39 * To start serving on a new port, use the addPort functions. Each call to
40 * addPort creates a new ServerSocket, starts it listening, and adds it to
41 * the server pool.
42 *
43 * All of the real work is done by scan, which will wait for up
44 * to the timeout set by setTimeout before returning if there is no data
45 * pending. scan should probably be called in some sort of tight
46 * loop, possibly in it's own thread, or in the main control loop.
47 *
48 * In order to use a Server you must subclass it and implement the pure
49 * virtual functions. These allow you to receive notification of events
50 * happening within the server itself, and actually makes it useful.
51 *@ingroup Threading Serving
52 */
53 class ItoServer : public Thread
54 {
55 friend class ItoClient;
56 friend class SrvClientLinkFactory;
57 public:
58 ItoServer();
59 virtual ~ItoServer();
60
61 void addPort( int nPort, int nPoolSize=40 );
62 void addPort( const String &sAddr, int nPort, int nPoolSize=40 );
63
64 //void scan();
65 void setTimeout( int nTimeoutSec, int nTimeoutUSec=0 );
66
67 void addClient( int nSocket, int nPort );
68
69 virtual void onNewConnection( Client *pClient, int nPort )=0;
70 virtual void onClosedConnection( Client *pClient )=0;
71
72 protected:
73 virtual void run();
74
75 private:
76 class SrvClientLink;
77 class ItoClient : public Thread
78 {
79 friend class Bu::ItoServer::SrvClientLink;
80 public:
81 ItoClient( ItoServer &rSrv, int nSocket, int nPort,
82 int nTimeoutSec, int nTimeoutUSec );
83 virtual ~ItoClient();
84
85 typedef SynchroQueue<Bu::String *> StringQueue;
86 StringQueue qMsg;
87
88 protected:
89 virtual void run();
90
91 private:
92 ItoServer &rSrv;
93 Client *pClient;
94 fd_set fdActive;
95 int iSocket;
96 int iPort;
97 int nTimeoutSec;
98 int nTimeoutUSec;
99 Mutex imProto;
100 };
101
102 class SrvClientLink : public Bu::ClientLink
103 {
104 public:
105 SrvClientLink( ItoClient *pClient );
106 virtual ~SrvClientLink();
107
108 virtual void sendMessage( const Bu::String &sMsg );
109
110 private:
111 ItoClient *pClient;
112 };
113
114 class SrvClientLinkFactory : public Bu::ClientLinkFactory
115 {
116 public:
117 SrvClientLinkFactory( ItoServer &rSrv );
118 virtual ~SrvClientLinkFactory();
119
120 virtual Bu::ClientLink *createLink( Bu::Client *pClient );
121
122 private:
123 ItoServer &rSrv;
124 };
125
126 int nTimeoutSec;
127 int nTimeoutUSec;
128 fd_set fdActive;
129 typedef Hash<int,TcpServerSocket *> ServerHash;
130 ServerHash hServers;
131 typedef Hash<int,ItoClient *> ClientHash;
132 typedef SynchroQueue<ItoClient *> ClientQueue;
133 ClientHash hClients;
134 ClientQueue qClientCleanup;
135 Mutex imClients;
136
137 void clientCleanup( int iSocket );
138 };
139}
140
141#endif
diff --git a/src/unstable/minimacro.cpp b/src/unstable/minimacro.cpp
new file mode 100644
index 0000000..b6fd6a8
--- /dev/null
+++ b/src/unstable/minimacro.cpp
@@ -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#include "bu/minimacro.h"
9
10Bu::MiniMacro::MiniMacro()
11{
12 hFuncs.insert("toupper", new FuncToUpper() );
13 hFuncs.insert("tolower", new FuncToLower() );
14}
15
16Bu::MiniMacro::MiniMacro( const StrHash &sVarSrc )
17{
18 for( StrHash::const_iterator i = sVarSrc.begin(); i != sVarSrc.end(); i++ )
19 {
20 addVar( i.getKey(), i.getValue() );
21 }
22}
23
24Bu::MiniMacro::~MiniMacro()
25{
26}
27
28Bu::String Bu::MiniMacro::parse( const Bu::String &sIn )
29{
30 bContinue = true;
31 Bu::String sOut;
32 for( sCur = sIn.getStr(); *sCur && bContinue; sCur++ )
33 {
34 if( *sCur == '{' )
35 {
36 switch( sCur[1] )
37 {
38 case '=':
39 sCur += 2;
40 sOut += parseRepl();
41 break;
42
43 case '?':
44 sCur += 2;
45 sOut += parseCond();
46 break;
47
48 case ':':
49 sCur += 2;
50 sOut += parseCmd();
51 break;
52
53 default:
54 sOut += *sCur;
55 continue;
56 }
57 }
58 else
59 {
60 sOut += *sCur;
61 }
62 }
63
64 iLastPos = (ptrdiff_t)sCur - (ptrdiff_t)sIn.getStr();
65
66 return sOut;
67}
68
69Bu::String Bu::MiniMacro::parseRepl()
70{
71 Bu::String sOut;
72 bool bIsFirst = true;
73 for( const char *sNext = sCur;;)
74 {
75 for(; *sNext != ':' && *sNext != '}' && *sNext != '\0'; sNext++ ) { }
76 if( *sNext == '\0' )
77 break;
78 Bu::String sName( sCur, (ptrdiff_t)sNext-(ptrdiff_t)sCur );
79 if( bIsFirst )
80 {
81 sOut = hVars[sName];
82 bIsFirst = false;
83 //printf("Variable: \"%s\"\n", sName.getStr() );
84 }
85 else
86 {
87 sOut = callFunc( sOut, sName );
88 //printf("Filter: \"%s\"\n", sName.getStr() );
89 }
90 if( *sNext == '}' )
91 {
92 sCur = sNext;
93 break;
94 }
95 else if( *sNext == ':' )
96 {
97 }
98 sNext++;
99 sCur = sNext;
100 }
101 return sOut;
102}
103
104Bu::String Bu::MiniMacro::parseCond()
105{
106 Bu::String sOut;
107 //printf("%20s\n", sCur );
108 return sOut;
109}
110
111Bu::String Bu::MiniMacro::parseCmd()
112{
113 Bu::String sOut;
114 const char *sNext = sCur;
115 for(; *sNext != ':' && *sNext != '}' && *sNext != '\0'; sNext++ ) { }
116 if( *sNext != '\0' )
117 {
118 Bu::String sName( sCur, (ptrdiff_t)sNext-(ptrdiff_t)sCur );
119 if( sName == "end" )
120 {
121 sCur = sNext;
122 bContinue = false;
123 return sOut;
124 }
125 else
126 {
127 throw Bu::ExceptionBase("Unknown command '%s'.",
128 sName.getStr()
129 );
130 }
131 }
132 else
133 {
134 //printf("Uh...?\n");
135 }
136
137 //printf("%20s\n", sCur );
138 return sOut;
139}
140
141Bu::String Bu::MiniMacro::callFunc(
142 const Bu::String &sIn, const Bu::String &sFunc )
143{
144 int i = sFunc.findIdx('(');
145 if( i < 0 )
146 throw Bu::ExceptionBase("That doesn't look like a function call");
147 Bu::String sName( sFunc.getStr(), i );
148 StrList lsParams;
149 for( const char *s = sFunc.getStr()+i+1; *s && *s != ')'; s++ )
150 {
151 for(; *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n'; s++ ) { }
152 const char *sNext;
153 for( sNext = s; *sNext && *sNext != ')' && *sNext != ','; sNext++ ) { }
154 Bu::String p( s, (ptrdiff_t)sNext-(ptrdiff_t)s );
155 lsParams.append( p );
156 sNext++;
157 s = sNext;
158 }
159 return hFuncs.get( sName )->call( sIn, lsParams );
160}
161
162void Bu::MiniMacro::addVar(
163 const Bu::String &sName, const Bu::String &sValue )
164{
165 hVars.insert( sName, sValue );
166}
167
168bool Bu::MiniMacro::hasVar( const Bu::String &sName )
169{
170 return hVars.has( sName );
171}
172
173const Bu::String &Bu::MiniMacro::getVar( const Bu::String &sName )
174{
175 return hVars.get( sName );
176}
177
178const Bu::StrHash &Bu::MiniMacro::getVars()
179{
180 return hVars;
181}
182
183int Bu::MiniMacro::getPosition()
184{
185 return iLastPos;
186}
187
diff --git a/src/unstable/minimacro.h b/src/unstable/minimacro.h
new file mode 100644
index 0000000..b6c7c13
--- /dev/null
+++ b/src/unstable/minimacro.h
@@ -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#ifndef BU_MINI_MACRO_H
9#define BU_MINI_MACRO_H
10
11#include "bu/hash.h"
12#include "bu/string.h"
13
14namespace Bu
15{
16 typedef Bu::Hash<Bu::String, Bu::String> StrHash;
17 /**
18 * A processor for Libbu++ brand Mini Macros. These are really simple, but
19 * still fairly flexible. It's mainly text replacement, but with a few
20 * extras like filter functions and conditional text segments. So far we
21 * don't support loops or anything, I'm not sure we ever will.
22 *
23 * Anatomy of a mini macro:
24 * - Every macro begins with a two character code, the first character is
25 * always '{', the second character determines the operation to perform.
26 * - If the '{' is followed by a character that is not valid it is not
27 * considered for expansion and the characters are copied to the output.
28 * - Every macro ends with a closing '}'
29 * - Macro types:
30 * - '=': variable replacement. The '=' is immediatley followed by the
31 * name of the variable to replace, then any number of optional filter
32 * segments.
33 * - '?': conditional text. The '?' is immediately followed by the
34 * variable to test. This works two ways, the variable can be alone, in
35 * which case it's existance is tested, or it can be followed by a "="
36 * and a string to compare to. This is then followed by a text segment
37 * that will be used if the test is true, and an optional text segment
38 * to be used if the test is false.
39 * - ':': command. The ':' is immediately followed by a command string,
40 * of which there's only one right now, but that's ok. These are not
41 * put into the output stream, but instead mark something for the
42 * parser. Currently supported:
43 * - {:end}: end of parsing, stop here, also make note of how many input
44 * characters were used.
45 * - Segments:
46 * - Each segment is seperated by a colon.
47 * - Filter segments give the name of the filter, followed by
48 * parenthesies. Parameters may be provided within the parenthesies.
49 * - Text segments should always be quoted, but may contain any characters
50 * within the quotes, backslash is used as per C/ANSI/ISO standard.
51 * You can also quote any text using [' '] instead of quotes, which
52 * allows for nested strings. The [' token is only recognised within
53 * a macro.
54 *
55 *@verbatim
56 {=name:tolower()}
57 {=name:ccsplit("_"):toupper()}
58 {?name:"name exists and is {=name}"}
59 {?name:"{=name}":"no name!"}
60 {?name="bob":"You're named bob!":"Who are you? I only know bob..."}
61 @endverbatim
62 */
63 class MiniMacro
64 {
65 public:
66 MiniMacro();
67 MiniMacro( const StrHash &sVarSrc );
68 virtual ~MiniMacro();
69
70 Bu::String parse( const Bu::String &sIn );
71 void addVar( const Bu::String &sName, const Bu::String &sValue );
72 bool hasVar( const Bu::String &sName );
73 const Bu::String &getVar( const Bu::String &sName );
74 const StrHash &getVars();
75 int getPosition();
76
77 private:
78 const char *sCur;
79 Bu::String parseRepl();
80 Bu::String parseCond();
81 Bu::String parseCmd();
82 Bu::String callFunc(
83 const Bu::String &sIn, const Bu::String &sFunc );
84
85 StrHash hVars;
86 bool bContinue;
87 int iLastPos;
88
89 public:
90 typedef Bu::List<Bu::String> StrList;
91 class Func
92 {
93 public:
94 Func(){}
95 virtual ~Func(){}
96 virtual Bu::String call(
97 const Bu::String &sIn, StrList &lsParam )=0;
98 };
99
100 class FuncToUpper : public Func
101 {
102 public:
103 FuncToUpper(){}
104 virtual ~FuncToUpper(){}
105 virtual Bu::String call(
106 const Bu::String &sIn, StrList & )
107 {
108 return sIn.toUpper();
109 }
110 };
111
112 class FuncToLower : public Func
113 {
114 public:
115 FuncToLower(){}
116 virtual ~FuncToLower(){}
117 virtual Bu::String call(
118 const Bu::String &sIn, StrList & )
119 {
120 return sIn.toLower();
121 }
122 };
123
124 private:
125 typedef Bu::Hash<Bu::String,class Func *> FuncHash;
126 FuncHash hFuncs;
127 };
128};
129
130#endif
diff --git a/src/unstable/myriad.cpp b/src/unstable/myriad.cpp
new file mode 100644
index 0000000..de44930
--- /dev/null
+++ b/src/unstable/myriad.cpp
@@ -0,0 +1,663 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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/myriad.h"
9#include "bu/stream.h"
10#include "bu/myriadstream.h"
11#include <stdio.h>
12
13#include "bu/sio.h"
14using Bu::sio;
15using Bu::Fmt;
16
17#define Myriad_MAGIC_CODE ((unsigned char *)"\x0a\xd3\xfa\x84")
18
19namespace Bu
20{
21 subExceptionDef( MyriadException )
22 template<typename t> t blkDiv( t total, t block ) {
23 return (total/block)+((total%block==0)?(0):(1));
24 }
25}
26
27Bu::Myriad::Myriad( Bu::Stream &sStore, int iBlockSize, int iPreallocate ) :
28 sStore( sStore ),
29 iBlockSize( iBlockSize ),
30 iBlocks( 0 ),
31 iUsed( 0 ),
32 bHeaderChanged( false )
33{
34 try
35 {
36 initialize();
37 }
38 catch( Bu::MyriadException &e )
39 {
40 if( e.getErrorCode() == MyriadException::emptyStream )
41 {
42 initialize( iBlockSize, iPreallocate );
43 }
44 else
45 {
46 throw;
47 }
48 }
49}
50
51Bu::Myriad::~Myriad()
52{
53 if( !hActiveBlocks.isEmpty() )
54 {
55 sio << "Bu::Myriad::~Myriad(): Error: There are "
56 << hActiveBlocks.getSize() << " unsynced blocks!" << sio.nl;
57 }
58 sync();
59
60 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
61 {
62 delete *i;
63 }
64}
65
66void Bu::Myriad::sync()
67{
68 updateHeader();
69
70 for( BlockHash::iterator i = hActiveBlocks.begin(); i; i++ )
71 {
72 if( (*i)->bChanged )
73 {
74 syncBlock( *i );
75 }
76 }
77}
78
79void Bu::Myriad::initialize()
80{
81 sStore.setPosEnd( 0 );
82 int iSize = sStore.tell();
83 sStore.setPos( 0 );
84
85 unsigned char buf[4];
86 if( sStore.read( buf, 4 ) < 4 )
87 throw MyriadException( MyriadException::emptyStream,
88 "Input stream appears to be empty.");
89 if( memcmp( buf, Myriad_MAGIC_CODE, 4 ) )
90 {
91 throw MyriadException( MyriadException::invalidFormat,
92 "Stream does not appear to be a valid Myriad format.");
93 }
94 sStore.read( buf, 2 );
95 if( buf[0] != 1 )
96 throw MyriadException( MyriadException::badVersion,
97 "We can only handle version 1 for now.");
98 if( buf[1] != 32 )
99 throw MyriadException( MyriadException::invalidWordSize,
100 "We can only handle 32-bit words at the moment.");
101 sStore.read( &iBlockSize, 4 );
102 int iStreams;
103 sStore.read( &iStreams, 4 );
104
105 iBlocks = iSize/iBlockSize;
106 //sio << "Myriad: iSize=" << iSize << ", iBlockSize=" << iBlockSize
107 // << ", iBlocks=" << iBlocks << ", iStreams=" << iStreams << sio.nl;
108
109 int iHeaderSize = 14 + 8 + 4;
110 int iHeaderBlocks = 0; //blkDiv( iHeaderSize+4, iBlockSize );
111
112 while( iHeaderSize > iHeaderBlocks*iBlockSize )
113 {
114 iHeaderBlocks = blkDiv( iHeaderSize+4, iBlockSize );
115 iHeaderSize = 14 + 8 + 4*iHeaderBlocks;
116 }
117
118 //sio << "Myriad: iHeaderSize=" << iHeaderSize
119 // << ", iHeaderBlocks=" << iHeaderBlocks << sio.nl;
120
121 Stream *pFakeHdr = new Stream;
122 pFakeHdr->iId = 0;
123 pFakeHdr->iSize = iHeaderSize;
124 for( int j = 0; j < iHeaderBlocks; j++ )
125 {
126 pFakeHdr->aBlocks.append( j );
127 }
128
129 bsBlockUsed.setSize( iBlocks, true );
130
131// bool bCanSkip = false; // Can skip around, post initial header stream i/o
132 MyriadStream *pIn = new MyriadStream( *this, pFakeHdr );
133 pIn->setPos( sStore.tell() );
134 for( int j = 0; j < iStreams; j++ )
135 {
136 aStreams.append( new Stream() );
137 Stream &s = *aStreams[j];
138 pIn->read( &s.iId, 4 );
139 pIn->read( &s.iSize, 4 );
140 int iSBlocks = blkDiv(s.iSize, iBlockSize);
141 // sio << "Myriad: - Stream::iId=" << s.iId
142 // << ", Stream::iSize=" << s.iSize
143 // << ", Stream::aBlocks=" << iSBlocks
144 // << ", pIn->tell()=" << pIn->tell() << sio.nl;
145 for( int k = 0; k < iSBlocks; k++ )
146 {
147 int iBId;
148 pIn->read( &iBId, 4 );
149 // sio << "Myriad: - iBId=" << iBId
150 // << ", iStartPos=" << iBId*iBlockSize
151 // << ", pIn->tell()=" << pIn->tell() << sio.nl;
152 s.aBlocks.append( iBId );
153 bsBlockUsed.setBit( iBId );
154 iUsed++;
155 if( (j == 0 && k == iHeaderBlocks-1) )
156 {
157 // sio << "Myriad: - End of prepartition, unlocking skipping."
158 // << sio.nl;
159// bCanSkip = true;
160 MyriadStream *pTmp = new MyriadStream( *this, aStreams[0] );
161 // sio << "Myriad - Position = " << pIn->tell() << sio.nl;
162 pTmp->setPos( pIn->tell() );
163 delete pIn;
164 delete pFakeHdr;
165 pIn = pTmp;
166 }
167 }
168 }
169 delete pIn;
170
171 //sio << "Myriad: Blocks used: " << bsBlockUsed.toString() << sio.nl;
172}
173
174void Bu::Myriad::initialize( int iBlockSize, int iPreAllocate )
175{
176 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
177 {
178 delete *i;
179 }
180 aStreams.clear();
181 iUsed = 0;
182
183 int iHeaderSize = 14 + 8 + 4;
184 int iHeaderBlocks = 0; //blkDiv( iHeaderSize+4, iBlockSize );
185 char cBuf = 1;
186 int iBuf = 0;
187
188 Stream *pStr = new Stream;
189 pStr->iId = 0;
190
191 while( iHeaderSize > iHeaderBlocks*iBlockSize )
192 {
193 iHeaderBlocks = blkDiv( iHeaderSize+4, iBlockSize );
194 iHeaderSize = 14 + 8 + 4*iHeaderBlocks;
195 }
196
197 iPreAllocate += iHeaderBlocks;
198
199 //sio << "Myriad: iHeaderSize=" << iHeaderSize << ", iBlockSize="
200 // << iBlockSize << ", iHeaderBlocks=" << iHeaderBlocks << sio.nl;
201
202 bsBlockUsed.setSize( iPreAllocate, true );
203 iUsed++;
204
205 char *pBlock = new char[iBlockSize];
206 memset( pBlock, 0, iBlockSize );
207 for( int j = 0; j < iPreAllocate; j++ )
208 {
209 sStore.write( pBlock, iBlockSize );
210 }
211 delete[] (char *)pBlock;
212
213 sStore.setPos( 0 );
214
215 // Magic number
216 sStore.write( Myriad_MAGIC_CODE, 4 );
217
218 // Version (0)
219 sStore.write( &cBuf, 1 );
220
221 // Bits per int
222 cBuf = 32;
223 sStore.write( &cBuf, 1 );
224
225 // The size of each block
226 sStore.write( &iBlockSize, 4 );
227
228 iBuf = 1;
229 // The number of streams
230 sStore.write( &iBuf, 4 );
231
232 // Stream header
233 iBuf = 0;
234 sStore.write( &iBuf, 4 );
235 sStore.write( &iHeaderSize, 4 );
236 for( iBuf = 0; iBuf < iHeaderBlocks; iBuf++ )
237 {
238 sStore.write( &iBuf, 4 );
239 }
240
241 this->iBlockSize = iBlockSize;
242 this->iBlocks = iPreAllocate;
243
244 pStr->iSize = sStore.tell();
245// sio << "Myriad: Actual end of header stream = " << pStr->iSize << sio.nl;
246
247 pStr->iSize = iHeaderSize;
248 for( int j = 0; j < iHeaderBlocks; j++ )
249 {
250 pStr->aBlocks.append( j );
251 bsBlockUsed.setBit( j );
252 iUsed++;
253 }
254
255 aStreams.append( pStr );
256
257 //sio << bsBlockUsed.toString() << " - " << pStr->aBlocks << sio.nl;
258
259 bHeaderChanged = true;
260 //hStreams.insert( 0, BlockArray( 0 ) );
261}
262
263void Bu::Myriad::updateHeader()
264{
265 if( bHeaderChanged == false )
266 return;
267 if( !sStore.canWrite() )
268 return;
269
270 char cBuf;
271 int iBuf;
272
273 //for( StreamArray::iterator i = aStreams.begin(); i; i++ )
274 //{
275 // sio << "Myriad: Stream " << Fmt(4) << (*i)->iId << ": " << (*i)->aBlocks << sio.nl;
276 //}
277
278 // Compute the new size of the header.
279 int iHeaderSize = 14 + 8*aStreams.getSize();
280// sio << "Myriad: updateHeader: aStreams.getSize() = " << aStreams.getSize()
281// << sio.nl;
282 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
283 {
284 iHeaderSize += 4*(*i)->aBlocks.getSize();
285// sio << "Myriad: updateHeader: (*i)->aBlocks.getSize() = "
286// << (*i)->aBlocks.getSize() << sio.nl;
287 }
288 int iNewBlocks = blkDiv( iHeaderSize, iBlockSize );
289 while( iNewBlocks > aStreams[0]->aBlocks.getSize() )
290 {
291 int iBlock = findEmptyBlock();
292// sio << "Myriad: updateHeader: Appending block " << iBlock
293// << " to header." << sio.nl;
294 aStreams[0]->aBlocks.append( iBlock );
295 bsBlockUsed.setBit( iBlock );
296 iUsed++;
297 iHeaderSize += 4;
298 iNewBlocks = blkDiv( iHeaderSize, iBlockSize );
299 }
300 aStreams[0]->iSize = iHeaderSize;
301// sio << "Myriad: updateHeader: iHeaderSize=" << iHeaderSize
302// << ", iNewBlocks=" << iNewBlocks << ", curBlocks="
303// << aStreams[0]->aBlocks.getSize() << sio.nl;
304
305 MyriadStream sHdr( *this, aStreams[0] );
306 sHdr.write( Myriad_MAGIC_CODE, 4 );
307
308 // Version (1)
309 cBuf = 1;
310 sHdr.write( &cBuf, 1 );
311
312 // Bits per int
313 cBuf = 32;
314 sHdr.write( &cBuf, 1 );
315
316 // The size of each block
317 sHdr.write( &iBlockSize, 4 );
318
319 iBuf = aStreams.getSize();
320 // The number of streams
321 sHdr.write( &iBuf, 4 );
322
323 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
324 {
325 sHdr.write( &(*i)->iId, 4 );
326 sHdr.write( &(*i)->iSize, 4 );
327 int iUsedBlocks = blkDiv( (*i)->iSize, iBlockSize );
328// for( BlockArray::iterator j = (*i)->aBlocks.begin(); j; j++ )
329 for( int j = 0; j < iUsedBlocks; j++ )
330 {
331 sHdr.write( &(*i)->aBlocks[j], 4 );
332 }
333 }
334
335 bHeaderChanged = false;
336}
337
338int Bu::Myriad::createStream( int iPreAllocate )
339{
340 Stream *pStr = new Stream();
341 pStr->iId = aStreams.last()->iId+1;
342 //sio << "Myriad: New stream id=" << pStr->iId << ", iPreAllocate="
343 // << iPreAllocate << sio.nl;
344 pStr->iSize = 0;
345 aStreams.append( pStr );
346
347 for( int j = 0; j < iPreAllocate; j++ )
348 {
349 int iFreeBlock = findEmptyBlock();
350// sio << "Myriad: Adding block " << iFreeBlock << sio.nl;
351 pStr->aBlocks.append( iFreeBlock );
352 bsBlockUsed.setBit( iFreeBlock );
353 iUsed++;
354 }
355
356 bHeaderChanged = true;
357
358 return pStr->iId;
359}
360
361int Bu::Myriad::createStreamWithId( int iId, int iPreAllocate )
362{
363 try
364 {
365 findStream( iId );
366 throw MyriadException( MyriadException::streamExists,
367 "There is already a stream with the given id.");
368 }
369 catch( MyriadException &e )
370 {
371 Stream *pStr = new Stream();
372 pStr->iId = iId;
373 //sio << "Myriad: New stream id=" << pStr->iId << ", iPreAllocate="
374 // << iPreAllocate << sio.nl;
375 pStr->iSize = 0;
376 if( aStreams.last()->iId < iId )
377 {
378 aStreams.append( pStr );
379 }
380 else
381 {
382 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
383 {
384 if( (*i)->iId > iId )
385 {
386 aStreams.insert( i, pStr );
387 break;
388 }
389 }
390 }
391
392 for( int j = 0; j < iPreAllocate; j++ )
393 {
394 int iFreeBlock = findEmptyBlock();
395 // sio << "Myriad: Adding block " << iFreeBlock << sio.nl;
396 pStr->aBlocks.append( iFreeBlock );
397 bsBlockUsed.setBit( iFreeBlock );
398 iUsed++;
399 }
400
401 bHeaderChanged = true;
402
403 return pStr->iId;
404 }
405}
406
407int Bu::Myriad::findEmptyBlock()
408{
409 bHeaderChanged = true;
410
411 for( int j = 0; j < bsBlockUsed.getSize(); j++ )
412 {
413 if( bsBlockUsed.getBit( j ) == false )
414 return j;
415 }
416// sio << "Myriad: findEmptyBlock(): No empty blocks, adding new one." << sio.nl;
417
418 bsBlockUsed.setSize( bsBlockUsed.getSize()+1, false );
419 /*
420 sStore.setPos( iBlockSize*iBlocks );
421
422 char *pBlock = new char[iBlockSize];
423 memset( pBlock, 0, iBlockSize );
424 sStore.write( pBlock, iBlockSize );
425 delete[] pBlock;
426 */
427
428 sStore.setSize( (iBlocks+1)*iBlockSize );
429
430 return iBlocks++;
431}
432
433void Bu::Myriad::deleteStream( int iId )
434{
435 if( iId < 0 )
436 throw MyriadException( MyriadException::invalidStreamId,
437 "Invalid stream id.");
438 if( iId == 0 )
439 throw MyriadException( MyriadException::protectedStream,
440 "You cannot delete stream zero, it is protected.");
441 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
442 {
443 if( (*i)->iId == iId )
444 {
445 Stream *pStream = *i;
446 for( BlockArray::iterator j = pStream->aBlocks.begin(); j; j++ )
447 {
448 bsBlockUsed.setBit( *j, false );
449 iUsed--;
450 }
451 aStreams.erase( i );
452 bHeaderChanged = true;
453 delete pStream;
454 return;
455 }
456 }
457}
458
459Bu::Array<int> Bu::Myriad::getStreamIds()
460{
461 Bu::Array<int> aRet( aStreams.getSize() );
462 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
463 {
464 aRet.append( (*i)->iId );
465 }
466
467 return aRet;
468}
469
470int Bu::Myriad::getStreamSize( int iId )
471{
472 return findStream( iId )->iSize;
473}
474
475bool Bu::Myriad::hasStream( int iId )
476{
477 try
478 {
479 findStream( iId );
480 return true;
481 }catch(...)
482 {
483 return false;
484 }
485}
486
487Bu::MyriadStream Bu::Myriad::openStream( int iId )
488{
489 //sio << "Myriad: Request to open stream: " << iId << sio.nl;
490 return MyriadStream( *this, findStream( iId ) );
491}
492
493int Bu::Myriad::getNumStreams()
494{
495 return aStreams.getSize();
496}
497
498int Bu::Myriad::getBlockSize()
499{
500 return iBlockSize;
501}
502
503int Bu::Myriad::getNumBlocks()
504{
505 return iBlocks;
506}
507
508int Bu::Myriad::getNumUsedBlocks()
509{
510 return iUsed;
511}
512
513int Bu::Myriad::getTotalUsedBytes()
514{
515 int iTotalSize = 0;
516 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
517 {
518 iTotalSize += (*i)->iSize;
519 }
520 return iTotalSize;
521}
522
523int Bu::Myriad::getTotalUnusedBytes()
524{
525 int iTotalSize = (iBlocks-iUsed)*iBlockSize;
526 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
527 {
528 iTotalSize += iBlockSize - ((*i)->iSize%iBlockSize);
529 }
530 return iTotalSize;
531}
532
533int Bu::Myriad::getTotalUnusedBytes( int iFakeBlockSize )
534{
535 int iTotalSize = (iBlocks-iUsed)*iFakeBlockSize;
536 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
537 {
538 iTotalSize += iFakeBlockSize - ((*i)->iSize%iFakeBlockSize);
539 }
540 return iTotalSize;
541}
542
543Bu::Myriad::Stream *Bu::Myriad::findStream( int iId )
544{
545 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
546 {
547 if( (*i)->iId == iId )
548 return *i;
549 }
550
551 throw MyriadException( MyriadException::noSuchStream,
552 "The requested stream doesn't exist and cannot be opened." );
553
554 return NULL;
555}
556
557Bu::Myriad::Block *Bu::Myriad::getBlock( int iBlock )
558{
559// sio << "Myriad: Reading block " << iBlock << ", bytes "
560// << iBlockSize*iBlock << "-" << iBlockSize*(iBlock+1) << sio.nl;
561 Block *pBlock = new Block;
562 pBlock->pData = new char[iBlockSize];
563 sStore.setPos( iBlockSize * iBlock );
564 sStore.read( pBlock->pData, iBlockSize );
565 pBlock->bChanged = false;
566 pBlock->iBlockIndex = iBlock;
567
568 hActiveBlocks.insert( iBlock, pBlock );
569
570 return pBlock;
571}
572
573void Bu::Myriad::releaseBlock( Bu::Myriad::Block *pBlock )
574{
575 if( pBlock == NULL )
576 return;
577// sio << "Myriad: Releasing block " << pBlock->iBlockIndex << sio.nl;
578 syncBlock( pBlock );
579 hActiveBlocks.erase( pBlock->iBlockIndex );
580 delete[] pBlock->pData;
581 delete pBlock;
582}
583
584void Bu::Myriad::syncBlock( Block *pBlock )
585{
586 if( pBlock->bChanged )
587 {
588// sio << "Myriad: - Block changed, writing back to stream." << sio.nl;
589 sStore.setPos( iBlockSize * pBlock->iBlockIndex );
590 sStore.write( pBlock->pData, iBlockSize );
591 pBlock->bChanged = false;
592 }
593}
594
595int Bu::Myriad::streamAddBlock( Stream *pStream )
596{
597 int iBlock = findEmptyBlock();
598 pStream->aBlocks.append( iBlock );
599 bsBlockUsed.setBit( iBlock );
600 bHeaderChanged = true;
601 iUsed++;
602 return iBlock;
603}
604
605void Bu::Myriad::setStreamSize( Stream *pStream, long iSize )
606{
607 if( pStream->iSize == iSize )
608 {
609 return;
610 }
611 else if( pStream->iSize > iSize )
612 {
613 // Shrink
614 for( int iNewSize = pStream->aBlocks.getSize()*iBlockSize;
615 iNewSize-iBlockSize > iSize; iNewSize -= iBlockSize )
616 {
617 if( bsBlockUsed.getBit( pStream->aBlocks.last() ) )
618 iUsed--;
619 bsBlockUsed.setBit( pStream->aBlocks.last(), false );
620 pStream->aBlocks.eraseLast();
621 }
622 pStream->iSize = iSize;
623 bHeaderChanged = true;
624 }
625 else
626 {
627 // Grow
628 for( int iNewSize = pStream->aBlocks.getSize()*iBlockSize;
629 iNewSize < iSize; iNewSize += iBlockSize )
630 {
631 streamAddBlock( pStream );
632 }
633 pStream->iSize = iSize;
634 bHeaderChanged = true;
635 }
636}
637
638void Bu::Myriad::headerChanged()
639{
640 bHeaderChanged = true;
641}
642
643bool Bu::Myriad::isMyriad( Bu::Stream &sStore )
644{
645 sStore.setPos( 0 );
646
647 unsigned char buf[4];
648 if( sStore.read( buf, 4 ) < 4 )
649 throw MyriadException( MyriadException::emptyStream,
650 "Input stream appears to be empty.");
651 sStore.setPos( 0 );
652 if( memcmp( buf, Myriad_MAGIC_CODE, 4 ) )
653 {
654 return false;
655 }
656 return true;
657}
658
659const Bu::BitString &Bu::Myriad::getBlocksUsed() const
660{
661 return bsBlockUsed;
662}
663
diff --git a/src/unstable/myriad.h b/src/unstable/myriad.h
new file mode 100644
index 0000000..3382ab5
--- /dev/null
+++ b/src/unstable/myriad.h
@@ -0,0 +1,222 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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_MYRIAD_H
9#define BU_MYRIAD_H
10
11#include <stdint.h>
12#include "bu/bitstring.h"
13#include "bu/exceptionbase.h"
14#include "bu/array.h"
15#include "bu/hash.h"
16
17namespace Bu
18{
19 class Stream;
20 class MyriadStream;
21
22 subExceptionDeclBegin( MyriadException )
23 enum
24 {
25 emptyStream,
26 invalidFormat,
27 badVersion,
28 invalidWordSize,
29 noSuchStream,
30 streamExists,
31 invalidStreamId,
32 protectedStream
33 };
34 subExceptionDeclEnd();
35
36 /**
37 * Myriad block-allocated stream multiplexing system. This is a system for
38 * creating streams that contain other streams in a flexible and lightweight
39 * manner. Basically, you can create a file (or any other stream) that can
40 * store any number of flexible, growing streams. The streams within the
41 * Myriad stream are automatically numbered, not named. This works more
42 * or less like a filesystem, but without the extra layer for managing
43 * file and directory links. This would actually be very easy to add
44 * on top of Myriad, but is not required.
45 *
46 * Header format is as follows:
47 *
48 * MMMMvBssssSSSS*
49 * M = Magic number (0AD3FA84)
50 * v = version number
51 * B = Bits per int
52 * s = Blocksize in bytes
53 * S = Number of Streams
54 *
55 * The * represents the Stream headers, one per stream, as follows:
56 * IIIIssss$
57 * I = Id number of the stream
58 * s = size of stream in bytes
59 *
60 * The $ represents the Block headers, one per used block, as follows:
61 * IIII
62 * I = Index of the block
63 *
64 * The stream/block data is interleaved in the header, so all blocks stored
65 * with one stream are together. The block headers are in order, and the
66 * data in them is required to be "solid" you cannot fill partial blocks
67 * mid-way through a stream.
68 *
69 * The initial block starts with the nids header, and is both the zero block
70 * and the zero stream. For now, the minimum block size is the size needed
71 * to store the base header, the zero stream header, and the first two
72 * blocks of the zero stream, so 30 bytes. Since it's reccomended to use
73 * a size that will fit evenly into filesystem blocks, then a size of 32 is
74 * probably the smallest reccomended size because all powers of two equal
75 * to or greater than 32 are evenly divisible by 32.
76 *
77 * I have had a thought that if the block size were smaller than 42 bytes
78 * the header would consume the first N blocks where N * block size is
79 * enough space to house the initial header, the first stream header, and
80 * the first N block headers. This, of course, causes you to hit an
81 * infinite header if the block size is small enough.
82 */
83 class Myriad
84 {
85 friend class MyriadStream;
86 public:
87 /**
88 * Create a Myriad object that uses the given stream to store data.
89 * This stream must be random access. The block size and preallocate
90 * values passed in are values that will be used if the given stream
91 * is empty. In that case the stream will be "formatted" for myriad
92 * with the specified block size. If there is already a viable Myriad
93 * format present in the stream, then the blocksize and preallocate
94 * values will be ignored and the values from the stream will be used
95 * instead. If the stream doesn't appear to be Myriad formatted an
96 * exception will be thrown.
97 */
98 Myriad( Bu::Stream &sStore, int iBlockSize=512, int iPreallocate=8 );
99 virtual ~Myriad();
100
101 /**
102 * Destroy whatever data may be in the base stream and create a new
103 * Myriad system there with the given blocksize. Use this with care,
104 * it will destroy anything that was already in the stream, and
105 * generally, should not ever have to be used.
106 */
107 void initialize( int iBlockSize, int iPreAllocate=1 );
108
109 /**
110 * Create a new stream within the Myriad system. The ID of the new
111 * stream is returned.
112 */
113 int createStream( int iPreAllocate=1 );
114
115 /**
116 * Create a new stream within the Myriad system with a given id. The
117 * id that you provide will be the new id of the stream unless it's
118 * already used, in which case an error is thrown. This is primarilly
119 * useful when copying an old Myriad file into a new one.
120 */
121 int createStreamWithId( int iId, int iPreAllocate=1 );
122
123 /**
124 * Delete a stream that's already within the Myriad.
125 */
126 void deleteStream( int iId );
127
128 /**
129 * Return a new Stream object assosiated with the given stream ID.
130 */
131 MyriadStream openStream( int iId );
132
133 Bu::Array<int> getStreamIds();
134 int getStreamSize( int iId );
135 bool hasStream( int iId );
136
137 int getNumStreams();
138 int getBlockSize();
139 int getNumBlocks();
140 int getNumUsedBlocks();
141 int getTotalUsedBytes();
142 int getTotalUnusedBytes();
143 int getTotalUnusedBytes( int iFakeBlockSize );
144
145 /**
146 * Syncronize the header data, etc. with the storage stream. It's not
147 * a bad idea to call this periodically.
148 */
149 void sync();
150
151 /**
152 * Read the first few bytes from the given stream and return true/false
153 * depending on weather or not it's a Myriad stream. This will throw
154 * an exception if the stream is empty, or is not random access.
155 */
156 static bool isMyriad( Bu::Stream &sStore );
157
158 const Bu::BitString &getBlocksUsed() const;
159
160 private:
161 /**
162 * Initialize this object based on the data already in the assosiated
163 * stream. This will be called automatically for you if you forget,
164 * but if you want to pre-initialize for some reason, just call this
165 * once before you actually start doing anything with your Myriad.
166 */
167 void initialize();
168
169 enum
170 {
171 blockUnused = 0xFFFFFFFFUL
172 };
173
174 typedef Bu::Array<int> BlockArray;
175 class Stream
176 {
177 public:
178 int iId;
179 int iSize;
180 BlockArray aBlocks;
181 };
182 typedef Bu::Array<Stream *> StreamArray;
183
184 class Block
185 {
186 public:
187 char *pData;
188 bool bChanged;
189 int iBlockIndex;
190 };
191
192 void updateHeader();
193 int findEmptyBlock();
194
195 /**
196 *@todo Change this to use a binary search, it's nicer.
197 */
198 Stream *findStream( int iId );
199
200 Block *getBlock( int iBlock );
201 void releaseBlock( Block *pBlock );
202 void syncBlock( Block *pBlock );
203
204 int streamAddBlock( Stream *pStream );
205 void setStreamSize( Stream *pStream, long iSize );
206
207 void headerChanged();
208
209 private:
210 Bu::Stream &sStore;
211 int iBlockSize;
212 int iBlocks;
213 int iUsed;
214 Bu::BitString bsBlockUsed;
215 StreamArray aStreams;
216 typedef Bu::Hash<int, Block *> BlockHash;
217 BlockHash hActiveBlocks;
218 bool bHeaderChanged;
219 };
220};
221
222#endif
diff --git a/src/unstable/myriadfs.cpp b/src/unstable/myriadfs.cpp
new file mode 100644
index 0000000..6884a31
--- /dev/null
+++ b/src/unstable/myriadfs.cpp
@@ -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#include "bu/myriadfs.h"
9#include "bu/myriadstream.h"
10
11#include <string.h>
12#include <unistd.h>
13#include <time.h>
14
15#include "bu/sio.h"
16using Bu::sio;
17using Bu::Fmt;
18
19namespace Bu { subExceptionDef( MyriadFsException ) }
20
21#define Myriad_Fs_MAGIC_CODE ((char *)"\xa7\x18\x8b\x39")
22
23Bu::MyriadFs::MyriadFs( Bu::Stream &rStore, int iBlockSize ) :
24 rStore( rStore ),
25 mStore( rStore, iBlockSize ),
26 iUser( 0 ),
27 iGroup( 0 )
28{
29#ifndef WIN32
30 iUser = getuid();
31 iGroup = getgid();
32#endif
33
34 if( mStore.hasStream( 1 ) )
35 {
36 // Check to see if this is a MyriadFs stream.
37 Bu::MyriadStream ms = mStore.openStream( 1 );
38 char sMagic[4];
39 if( ms.read( sMagic, 4 ) < 4 )
40 throw MyriadFsException("The provided stream does not appear to be "
41 "a MyriadFs stream.");
42 if( ::strncmp( sMagic, Myriad_Fs_MAGIC_CODE, 4 ) )
43 throw MyriadFsException("The provided stream does not appear to be "
44 "a MyriadFs stream.");
45
46 int8_t iVer;
47 ms.read( &iVer, 1 );
48
49 int32_t iNumNodes;
50 ms.read( &iNumNodes, 4 );
51 for( int32_t j = 0; j < iNumNodes; j++ )
52 {
53 int32_t iNode;
54 int32_t iPos;
55 ms.read( &iNode, 4 );
56 ms.read( &iPos, 4 );
57 hNodeIndex.insert( iNode, iPos );
58 }
59 }
60 else
61 {
62 // Create initial header stream
63 {
64 mStore.createStream( 1 );
65 Bu::MyriadStream ms = mStore.openStream( 1 );
66 ms.write( Myriad_Fs_MAGIC_CODE, 4 );
67 int8_t iVer = 1;
68 int32_t iTmp = 1;
69 ms.write( &iVer, 1 );
70 ms.write( &iTmp, 4 ); // iNumNodes
71 iTmp = 0;
72 ms.write( &iTmp, 4 ); // iInode
73 ms.write( &iTmp, 4 ); // iPosition
74 hNodeIndex.insert( 0, 0 );
75 }
76
77 // Create initial inode stream, with one root node.
78 {
79 mStore.createStream( 2 );
80 Bu::MyriadStream ms = mStore.openStream( 2 );
81 RawStat rs;
82 rs.iNode = 0;
83 rs.iUser = iUser;
84 rs.iGroup = iGroup;
85 rs.uPerms = 0755|typeDir;
86 rs.iLinks = 1;
87 rs.uStreamIndex = 3;
88 rs.iCTime = rs.iMTime = rs.iATime = time(NULL);
89 ms.write( &rs, sizeof(RawStat) );
90 }
91
92 // Create inode 0's storage stream.
93 {
94 mStore.createStream( 3 );
95 Bu::MyriadStream ms = mStore.openStream( 3 );
96 int32_t iTmp32 = 0;
97 ms.write( &iTmp32, 4 ); // iChildCount
98 }
99 }
100}
101
102Bu::MyriadFs::~MyriadFs()
103{
104 writeHeader();
105}
106
107void Bu::MyriadFs::stat( const Bu::String &sPath, Bu::MyriadFs::Stat &rBuf )
108{
109 int32_t iParent;
110 int32_t iNode = lookupInode( sPath, iParent );
111 Bu::MyriadStream is = mStore.openStream( 2 );
112 stat( iNode, rBuf, is );
113}
114
115Bu::MyriadStream Bu::MyriadFs::open( const Bu::String &sPath, int /*iMode*/,
116 uint16_t uPerms )
117{
118 int32_t iParent = -1;
119 int32_t iNode;
120 try
121 {
122 iNode = lookupInode( sPath, iParent );
123 sio << "File found." << sio.nl;
124 // The file was found
125 return openByInode( iNode );
126 }
127 catch( Bu::MyriadFsException &e )
128 {
129 if( iParent < 0 )
130 throw;
131
132 // This means that an intermediate path component couldn't be found
133 if( e.getErrorCode() == 1 )
134 throw;
135
136 // The file wasn't found, but the path leading up to it was.
137 // first, figure out the final path element...
138 Bu::String sName = filePart( sPath );
139 sio << "End filename: " << sName << sio.nl;
140 sio << "Parent inode: " << iParent << sio.nl;
141 iNode = create( iParent, sName, (uPerms&permMask)|typeRegFile, 0 );
142 sio << "New iNode: " << iNode << sio.nl;
143 return openByInode( iNode );
144 }
145}
146
147void Bu::MyriadFs::create( const Bu::String &sPath, uint16_t iPerms )
148{
149 create( sPath, iPerms, 0 );
150}
151
152void Bu::MyriadFs::create( const Bu::String &sPath, uint16_t iPerms,
153 uint16_t iDevHi, uint16_t iDevLo )
154{
155 create( sPath, iPerms, ((uint32_t)iDevHi<<16)|(uint32_t)iDevLo );
156}
157
158void Bu::MyriadFs::create( const Bu::String &sPath, uint16_t iPerms,
159 uint32_t uSpecial )
160{
161 int32_t iParent = -1;
162 int32_t iNode;
163 try
164 {
165 iNode = lookupInode( sPath, iParent );
166 sio << "File found." << sio.nl;
167 // The file was found
168 throw Bu::MyriadFsException("Path already exists.");
169 }
170 catch( Bu::MyriadFsException &e )
171 {
172 if( iParent < 0 )
173 throw;
174
175 // This means that an intermediate path component couldn't be found
176 if( e.getErrorCode() == 1 )
177 throw;
178
179 // The file wasn't found, but the path leading up to it was.
180 // first, figure out the final path element...
181 Bu::String sName = filePart( sPath );
182 sio << "End filename: " << sName << sio.nl;
183 sio << "Parent inode: " << iParent << sio.nl;
184 iNode = create( iParent, sName, iPerms, uSpecial );
185 sio << "New iNode: " << iNode << sio.nl;
186 }
187}
188
189void Bu::MyriadFs::mkDir( const Bu::String &sPath, uint16_t iPerms )
190{
191 create( sPath, (iPerms&permMask)|typeDir, 0 );
192}
193
194void Bu::MyriadFs::mkSymLink( const Bu::String &sTarget,
195 const Bu::String &sPath )
196{
197 int32_t iParent = -1;
198 int32_t iNode;
199 try
200 {
201 iNode = lookupInode( sPath, iParent );
202 throw Bu::MyriadFsException("Path already exists.");
203 }
204 catch( Bu::MyriadFsException &e )
205 {
206 if( iParent < 0 )
207 throw;
208
209 // This means that an intermediate path component couldn't be found
210 if( e.getErrorCode() == 1 )
211 throw;
212
213 // The file wasn't found, but the path leading up to it was.
214 // first, figure out the final path element...
215 Bu::String sName = filePart( sPath );
216 sio << "End filename: " << sName << sio.nl;
217 sio << "Parent inode: " << iParent << sio.nl;
218 iNode = create( iParent, sName, 0777|typeSymLink, 0 );
219 sio << "New iNode: " << iNode << sio.nl;
220 MyriadStream ms = openByInode( iNode );
221 ms.write( sTarget );
222 }
223}
224
225void Bu::MyriadFs::mkHardLink( const Bu::String &sTarget,
226 const Bu::String &sPath )
227{
228 int32_t iParent = -1;
229 int32_t iNode;
230
231 iNode = lookupInode( sTarget, iParent );
232
233 try
234 {
235 lookupInode( sPath, iParent );
236 throw Bu::MyriadFsException("Path already exists.");
237 }
238 catch( Bu::MyriadFsException &e )
239 {
240 if( iParent < 0 )
241 throw;
242
243 // This means that an intermediate path component couldn't be found
244 if( e.getErrorCode() == 1 )
245 throw;
246
247 // The file wasn't found, but the path leading up to it was.
248 // first, figure out the final path element...
249 Bu::String sName = filePart( sPath );
250 sio << "End filename: " << sName << sio.nl;
251 sio << "Parent inode: " << iParent << sio.nl;
252 addToDir( iParent, iNode, sName );
253 MyriadStream is = mStore.openStream( 2 );
254 RawStat rs;
255 readInode( iNode, rs, is );
256 rs.iLinks++;
257 writeInode( rs, is );
258 }
259}
260
261Bu::String Bu::MyriadFs::readSymLink( const Bu::String &sPath )
262{
263 int32_t iParent = -1;
264 int32_t iNode;
265 iNode = lookupInode( sPath, iParent );
266 MyriadStream ms = openByInode( iNode );
267 Bu::String sRet;
268 sRet.setSize( ms.getSize() );
269 ms.read( sRet.getStr(), ms.getSize() );
270 return sRet;
271}
272
273Bu::MyriadFs::Dir Bu::MyriadFs::readDir( const Bu::String &sPath )
274{
275 int32_t iParent = -1;
276 int32_t iNode = lookupInode( sPath, iParent );
277 return readDir( iNode );
278}
279
280void Bu::MyriadFs::setTimes( const Bu::String &sPath, int64_t iATime,
281 int64_t iMTime )
282{
283 int32_t iParent = -1;
284 int32_t iNode;
285
286 iNode = lookupInode( sPath, iParent );
287
288 setTimes( iNode, iATime, iMTime );
289}
290
291void Bu::MyriadFs::unlink( const Bu::String &sPath )
292{
293 int32_t iParent = -1;
294// int32_t iNode;
295
296 /*iNode =*/ lookupInode( sPath, iParent );
297
298 Dir lDir = readDir( iParent );
299
300 Bu::String sName = filePart( sPath );
301
302 for( Dir::iterator i = lDir.begin(); i; i++ )
303 {
304 if( sName == (*i).sName )
305 {
306 RawStat rs;
307 readInode( (*i).iNode, rs );
308 if( (rs.uPerms&typeMask) == typeDir )
309 {
310 MyriadStream msDir = mStore.openStream( rs.uStreamIndex );
311 int32_t iCount;
312 msDir.read( &iCount, 4 );
313 if( iCount > 0 )
314 {
315 throw Bu::MyriadFsException("Directory not empty.");
316 }
317 }
318 if( --rs.iLinks == 0 )
319 {
320 destroyNode( (*i).iNode );
321 }
322 else
323 {
324 writeInode( rs );
325 }
326 lDir.erase( i );
327 break;
328 }
329 }
330
331 Bu::MyriadStream ms = openByInode( iParent );
332 int32_t iNumChildren = lDir.getSize();
333 ms.write( &iNumChildren, 4 );
334 for( Dir::iterator i = lDir.begin(); i; i++ )
335 {
336 ms.write( &(*i).iNode, 4 );
337 uint8_t iSize = (*i).sName.getSize();
338 ms.write( &iSize, 1 );
339 ms.write( (*i).sName.getStr(), iSize );
340 }
341 ms.setSize( ms.tell() );
342}
343
344void Bu::MyriadFs::setFileSize( const Bu::String &sPath, int32_t iSize )
345{
346 int32_t iParent = -1;
347 int32_t iNode;
348 iNode = lookupInode( sPath, iParent );
349 MyriadStream ms = openByInode( iNode );
350 ms.setSize( iSize );
351}
352
353void Bu::MyriadFs::rename( const Bu::String &sFrom, const Bu::String &sTo )
354{
355 mkHardLink( sFrom, sTo );
356 unlink( sFrom );
357}
358
359dev_t Bu::MyriadFs::devToSys( uint32_t uDev )
360{
361 return (((uDev&0xFFFF0000)>>8)&0xFF00) | ((uDev&0xFF));
362}
363
364uint32_t Bu::MyriadFs::sysToDev( dev_t uDev )
365{
366 return (((uint32_t)uDev&0xFF00)<<8) | ((uint32_t)uDev&0xFF);
367}
368
369int32_t Bu::MyriadFs::lookupInode( const Bu::String &sPath, int32_t &iParent )
370{
371 if( sPath == "/" )
372 {
373 return 0;
374 }
375 if( sPath[0] == '/' )
376 {
377 // Absolute lookup
378 return lookupInode( sPath.begin()+1, 0, iParent );
379 }
380 else
381 {
382 // Relative lookup
383 throw Bu::ExceptionBase(
384 "Relative lookups in MyriadFs are not working yet.");
385 }
386}
387
388int32_t Bu::MyriadFs::lookupInode( Bu::String::const_iterator iStart,
389 int32_t iNode, int32_t &iParent )
390{
391 iParent = iNode;
392
393 Bu::String::const_iterator iEnd = iStart.find('/');
394 Bu::String sTok( iStart, iEnd );
395
396// sio << "Direcotry component: " << sTok << sio.nl;
397
398 Dir lDir = readDir( iNode );
399
400 for( Dir::iterator i = lDir.begin(); i; i++ )
401 {
402 if( (*i).sName == sTok )
403 {
404 // We found an item
405 if( !iEnd )
406 {
407 // It's the last one in the requested path, return it
408 return (*i).iNode;
409 }
410 else
411 {
412 // Not the last one in our path, double check it's a dir
413 if( ((*i).uPerms&typeMask) == typeDir )
414 {
415 return lookupInode( iEnd+1, (*i).iNode, iParent );
416 }
417 else
418 {
419 iParent = -1;
420 throw Bu::MyriadFsException(
421 "Element '%s' in given path is not a directory.",
422 sTok.getStr() );
423 }
424 }
425 }
426 }
427
428 if( iEnd )
429 throw Bu::MyriadFsException( 1, "Path not found");
430 else
431 throw Bu::MyriadFsException( 2, "Path not found");
432}
433
434void Bu::MyriadFs::readInode( int32_t iNode, RawStat &rs, MyriadStream &rIs )
435{
436 rIs.setPos( hNodeIndex.get( iNode )*sizeof(RawStat) );
437 if( rIs.read( &rs, sizeof(RawStat) ) < sizeof(RawStat) )
438 throw Bu::MyriadFsException("Filesystem corruption detected.");
439 if( rs.iNode != iNode )
440 throw Bu::MyriadFsException("Filesystem corruption detected.");
441}
442
443void Bu::MyriadFs::readInode( int32_t iNode, RawStat &rs )
444{
445 MyriadStream ms = mStore.openStream( 2 );
446 readInode( iNode, rs, ms );
447}
448
449void Bu::MyriadFs::writeInode( const RawStat &rs,
450 MyriadStream &rOs )
451{
452 rOs.setSize( hNodeIndex.getSize()*sizeof(RawStat) );
453 rOs.setPos( hNodeIndex.get( rs.iNode )*sizeof(RawStat) );
454 if( rOs.write( &rs, sizeof(RawStat) ) < sizeof(RawStat) )
455 throw Bu::MyriadFsException("Error writing inode to header stream.");
456}
457
458void Bu::MyriadFs::writeInode( const RawStat &rs )
459{
460 MyriadStream ms = mStore.openStream( 2 );
461 writeInode( rs, ms );
462}
463
464Bu::MyriadFs::Dir Bu::MyriadFs::readDir( int32_t iNode )
465{
466 Bu::MyriadStream ms = openByInode( iNode );
467 int32_t iNumChildren = 0;
468 ms.read( &iNumChildren, 4 );
469
470 Bu::MyriadStream is = mStore.openStream( 2 );
471 Dir lDir;
472// sio << "Reading dir, " << iNumChildren << " entries:" << sio.nl;
473 for( int32_t j = 0; j < iNumChildren; j++ )
474 {
475 int32_t iChildNode;
476 ms.read( &iChildNode, 4 );
477 Stat s;
478 stat( iChildNode, s, is );
479 uint8_t uLen;
480 ms.read( &uLen, 1 );
481 s.sName.setSize( uLen );
482 ms.read( s.sName.getStr(), uLen );
483 lDir.append( s );
484
485// sio << " " << s.sName << sio.nl;
486 }
487
488 return lDir;
489}
490
491Bu::MyriadStream Bu::MyriadFs::openByInode( int32_t iNode )
492{
493 RawStat rs;
494 readInode( iNode, rs );
495 switch( (rs.uPerms&typeMask) )
496 {
497 case typeDir:
498 case typeSymLink:
499 case typeRegFile:
500 return mStore.openStream( rs.uStreamIndex );
501
502 default:
503 throw Bu::MyriadFsException(
504 "inode incorrect type for low-level openByInode.");
505 }
506}
507
508void Bu::MyriadFs::addToDir( int32_t iDir, int32_t iNode,
509 const Bu::String &sName )
510{
511 if( sName.getSize() > 255 )
512 {
513 throw Bu::MyriadFsException("Filename too long, max is 255 bytes.");
514 }
515 Bu::MyriadStream ms = openByInode( iDir );
516 int32_t iNumChildren = 0;
517 ms.read( &iNumChildren, 4 );
518 iNumChildren++;
519 ms.setPos( 0 );
520 ms.write( &iNumChildren, 4 );
521 ms.setPosEnd( 0 );
522 ms.write( &iNode, 4 );
523 uint8_t uLen = sName.getSize();
524 ms.write( &uLen, 1 );
525 ms.write( sName.getStr(), uLen );
526}
527
528int32_t Bu::MyriadFs::create( int32_t iParent, const Bu::String &sName,
529 uint16_t uPerms, uint32_t uSpecial )
530{
531 int32_t iNode = allocInode( uPerms, uSpecial );
532 addToDir( iParent, iNode, sName );
533 return iNode;
534}
535
536int32_t Bu::MyriadFs::allocInode( uint16_t uPerms, uint32_t uSpecial )
537{
538 int32_t iNode = 0;
539 for(; iNode < 0xfffffff; iNode++ )
540 {
541 if( !hNodeIndex.has( iNode ) )
542 {
543 hNodeIndex.insert( iNode, hNodeIndex.getSize() );
544 RawStat rs;
545 rs.iNode = iNode;
546 rs.iUser = iUser;
547 rs.iGroup = iGroup;
548 rs.uPerms = uPerms;
549 rs.iLinks = 1;
550 switch( (uPerms&typeMask) )
551 {
552 case typeRegFile:
553 case typeSymLink:
554 rs.uStreamIndex = mStore.createStream();
555 break;
556
557 case typeDir:
558 rs.uStreamIndex = mStore.createStream();
559 sio << "Creating directory node, storage: "
560 << rs.uStreamIndex << sio.nl;
561 {
562 Bu::MyriadStream msDir = mStore.openStream(
563 rs.uStreamIndex
564 );
565 uint32_t uSize = 0;
566 msDir.write( &uSize, 4 );
567 }
568 break;
569
570 case typeChrDev:
571 case typeBlkDev:
572 rs.uStreamIndex = uSpecial;
573 break;
574
575 default:
576 rs.uStreamIndex = 0;
577 break;
578 }
579 rs.iATime = time(NULL);
580 rs.iMTime = time(NULL);
581 rs.iCTime = time(NULL);
582 writeInode( rs );
583
584 return iNode;
585 }
586 }
587
588 throw Bu::MyriadFsException(
589 "No inode could be allocated. You've run out!");
590}
591
592void Bu::MyriadFs::stat( int32_t iNode, Stat &rBuf, MyriadStream &rIs )
593{
594 RawStat rs;
595 readInode( iNode, rs, rIs );
596 rBuf.iNode = iNode;
597 rBuf.iUser = rs.iUser;
598 rBuf.iGroup = rs.iGroup;
599 rBuf.uPerms = rs.uPerms;
600 rBuf.iLinks = rs.iLinks;
601 rBuf.iATime = rs.iATime;
602 rBuf.iMTime = rs.iMTime;
603 rBuf.iCTime = rs.iCTime;
604 rBuf.uDev = 0;
605 rBuf.iSize = 0;
606 switch( (rBuf.uPerms&typeMask) )
607 {
608 case typeRegFile:
609 case typeSymLink:
610 rBuf.iSize = mStore.getStreamSize( rs.uStreamIndex );
611 break;
612
613 case typeChrDev:
614 case typeBlkDev:
615 rBuf.uDev = rs.uStreamIndex;
616 break;
617
618 default:
619 rBuf.iSize = 0;
620 break;
621 }
622}
623
624void Bu::MyriadFs::writeHeader()
625{
626 Bu::MyriadStream ms = mStore.openStream( 1 );
627 ms.write( Myriad_Fs_MAGIC_CODE, 4 );
628 int8_t iVer = 1;
629 int32_t iNumNodes = hNodeIndex.getSize();
630 ms.write( &iVer, 1 );
631 ms.write( &iNumNodes, 4 ); // iNumNodes
632 for( NodeIndex::iterator i = hNodeIndex.begin(); i; i++ )
633 {
634 int32_t iNode = i.getKey();
635 int32_t iPosition = i.getValue();
636 ms.write( &iNode, 4 );
637 ms.write( &iPosition, 4 );
638 }
639
640 // Truncate the stream afterwards so we don't use up too much space.
641 ms.setSize( ms.tell() );
642}
643
644void Bu::MyriadFs::setTimes( int32_t iNode, int64_t iATime, int64_t iMTime )
645{
646 RawStat rs;
647 Bu::MyriadStream is = mStore.openStream( 2 );
648
649 readInode( iNode, rs, is );
650 rs.iATime = iATime;
651 rs.iMTime = iMTime;
652 writeInode( rs, is );
653}
654
655void Bu::MyriadFs::destroyNode( int32_t iNode )
656{
657 if( iNode == 0 )
658 throw Bu::MyriadFsException("You cannot destroy the root.");
659
660 Bu::MyriadStream is = mStore.openStream( 2 );
661
662 // This will be overwritten with the last node
663 uint32_t iPosition = hNodeIndex.get( iNode );
664 RawStat rsOld;
665 readInode( iNode, rsOld, is );
666 switch( (rsOld.uPerms&typeMask) )
667 {
668 case typeRegFile:
669 case typeDir:
670 case typeSymLink:
671 mStore.deleteStream( rsOld.uStreamIndex );
672 break;
673 }
674
675 hNodeIndex.erase( iNode );
676
677 // Read the last node, can't use the helpers, because we don't know the
678 // iNode yet.
679 if( iPosition != hNodeIndex.getSize() )
680 {
681 // If this is the last node, then we don't need to do anything, but
682 // this case handles what to do if we aren't on the last node
683 RawStat rs;
684 is.setPos( (hNodeIndex.getSize())*sizeof(RawStat) );
685 is.read( &rs, sizeof(RawStat) );
686
687 hNodeIndex.get( rs.iNode ) = iPosition;
688 writeInode( rs, is );
689 }
690
691 is.setSize( hNodeIndex.getSize() * sizeof(RawStat) );
692}
693
694Bu::String Bu::MyriadFs::filePart( const Bu::String &sPath )
695{
696 Bu::String::const_iterator iStart = sPath.begin();
697 if( *iStart == '/' )
698 iStart++;
699 for( Bu::String::const_iterator iEnd = iStart.find('/'); iEnd;
700 iStart = iEnd+1, iEnd = iStart.find('/') ) { }
701 return Bu::String( iStart, sPath.end() );
702}
703
diff --git a/src/unstable/myriadfs.h b/src/unstable/myriadfs.h
new file mode 100644
index 0000000..cc9961a
--- /dev/null
+++ b/src/unstable/myriadfs.h
@@ -0,0 +1,203 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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 MYRIAD_FS_H
9#define MYRIAD_FS_H
10
11#include <sys/types.h>
12
13#include "bu/myriad.h"
14
15namespace Bu
16{
17 class Stream;
18
19 subExceptionDecl( MyriadFsException );
20
21 /**
22 * A POSIX compliant, node based filesystem built on top of Myriad.
23 *
24 * A header is placed into stream 1.
25 * Header format:
26 * int32_t iMagicHeader (A7188B39)
27 * int8_t iVersion (1)
28 * int32_t iNumNodes
29 * NodeLookup[iNumNodes] nNode
30 *
31 * Node lookup:
32 * int32_t iInode
33 * int32_t iPosition
34 *
35 * The node headers or inode structures have a base size of 44 bytes.
36 * The name is stored in the directory format.
37 * Basic node header format:
38 * int32_t iNode
39 * int32_t iUser
40 * int32_t iGroup
41 * uint16_t uPerms
42 * int16_t iLinks
43 * uint32_t uStreamIndex
44 * int64_t iATime
45 * int64_t iMTime
46 * int64_t iCTime
47 *
48 * Some types get special formats for their assosiated data stream, or
49 * other special considerations, here's a list:
50 *
51 * - typeFifo: No stream, uStreamIndex unused (probably)
52 * - typeChrDev: No stream, uStreamIndex is device hi/lo
53 * - typeDir: The stream contains a directory contents listing, described
54 * below
55 * - typeBlkDev: No stream, uStreamIndex is device hi/lo
56 * - typeRegFile: The stream is the file data
57 * - typeSymLink: The stream is the destination of the symlink
58 * - typeSocket: No steram, uStreamIndex unused (probably)
59 *
60 * Directory streams have this simple listing format. They contain a list
61 * of all child elements, with no particular order at the moment. The . and
62 * .. entries are not listed, they are implicit:
63 * int32_t iNumNodes
64 * NodeTable[iNumNodes] nChildren
65 *
66 * NodeTable:
67 * int32_t iInode
68 * uint8_t uNameSize
69 * char[uNameSize] sName
70 */
71 class MyriadFs
72 {
73 public:
74 MyriadFs( Bu::Stream &rStore, int iBlockSize=512 );
75 virtual ~MyriadFs();
76
77 enum
78 {
79 permOthX = 0000001,
80 permOthW = 0000002,
81 permOthR = 0000004,
82 permGrpX = 0000010,
83 permGrpW = 0000020,
84 permGrpR = 0000040,
85 permUsrX = 0000100,
86 permUsrW = 0000200,
87 permUsrR = 0000400,
88 permSticky = 0001000,
89 permSetGid = 0002000,
90 permSetUid = 0004000,
91 permMask = 0007777,
92 typeFifo = 0010000,
93 typeChrDev = 0020000,
94 typeDir = 0040000,
95 typeBlkDev = 0060000,
96 typeRegFile = 0100000,
97 typeSymLink = 0120000,
98 typeSocket = 0140000,
99 typeMask = 0170000
100 };
101
102 enum
103 {
104 Read = 0x01, ///< Open file for reading
105 Write = 0x02, ///< Open file for writing
106 Create = 0x04, ///< Create file if it doesn't exist
107 Truncate = 0x08, ///< Truncate file if it does exist
108 Append = 0x10, ///< Always append on every write
109 NonBlock = 0x20, ///< Open file in non-blocking mode
110 Exclusive = 0x44, ///< Create file, if it exists then fail
111
112 // Helpful mixes
113 ReadWrite = 0x03, ///< Open for reading and writing
114 WriteNew = 0x0E ///< Create a file (or truncate) for writing.
115 /// Same as Write|Create|Truncate
116 };
117
118 class Stat
119 {
120 public:
121 int32_t iNode;
122 int32_t iUser;
123 int32_t iGroup;
124 uint16_t uPerms;
125 int16_t iLinks;
126 int64_t iATime;
127 int64_t iMTime;
128 int64_t iCTime;
129 int32_t iSize;
130 uint32_t uDev;
131 Bu::String sName;
132 };
133 typedef Bu::List<Stat> Dir;
134
135 void stat( const Bu::String &sPath, Stat &rBuf );
136 MyriadStream open( const Bu::String &sPath, int iMode,
137 uint16_t uPerms=0664 );
138 void create( const Bu::String &sPath, uint16_t iPerms );
139 void create( const Bu::String &sPath, uint16_t iPerms,
140 uint16_t iDevHi, uint16_t iDevLo );
141 void create( const Bu::String &sPath, uint16_t iPerms,
142 uint32_t uSpecial );
143 void mkDir( const Bu::String &sPath, uint16_t iPerms );
144 void mkSymLink( const Bu::String &sTarget, const Bu::String &sPath );
145 void mkHardLink( const Bu::String &sTarget, const Bu::String &sPath );
146 Bu::String readSymLink( const Bu::String &sPath );
147 Dir readDir( const Bu::String &sPath );
148 void setTimes( const Bu::String &sPath, int64_t iATime,
149 int64_t iMTime );
150 void unlink( const Bu::String &sPath );
151 void setFileSize( const Bu::String &sPath, int32_t iSize );
152 void rename( const Bu::String &sFrom, const Bu::String &sTo );
153
154 static dev_t devToSys( uint32_t uDev );
155 static uint32_t sysToDev( dev_t uDev );
156
157 private:
158 class RawStat
159 {
160 public:
161 int32_t iNode;
162 int32_t iUser;
163 int32_t iGroup;
164 uint16_t uPerms;
165 int16_t iLinks;
166 uint32_t uStreamIndex;
167 int64_t iATime;
168 int64_t iMTime;
169 int64_t iCTime;
170 };
171 typedef Bu::Hash<int32_t, int32_t> NodeIndex;
172
173 private:
174 int32_t lookupInode( const Bu::String &sPath, int32_t &iParent );
175 int32_t lookupInode( Bu::String::const_iterator iStart,
176 int32_t iNode, int32_t &iParent );
177 void readInode( int32_t iNode, RawStat &rs, MyriadStream &rIs );
178 void readInode( int32_t iNode, RawStat &rs );
179 void writeInode( const RawStat &rs );
180 void writeInode( const RawStat &rs, MyriadStream &rOs );
181 Dir readDir( int32_t iNode );
182 MyriadStream openByInode( int32_t iNode );
183 void addToDir( int32_t iDir, int32_t iNode, const Bu::String &sName );
184 int32_t create( int32_t iParent, const Bu::String &sName,
185 uint16_t uPerms, uint32_t uSpecial );
186 int32_t allocInode( uint16_t uPerms, uint32_t uSpecial );
187 void stat( int32_t iNode, Stat &rBuf, MyriadStream &rIs );
188 void writeHeader();
189 void setTimes( int32_t iNode, int64_t iATime, int64_t iMTime );
190 void destroyNode( int32_t iNode );
191
192 Bu::String filePart( const Bu::String &sPath );
193
194 private:
195 Bu::Stream &rStore;
196 Bu::Myriad mStore;
197 NodeIndex hNodeIndex;
198 int32_t iUser;
199 int32_t iGroup;
200 };
201};
202
203#endif
diff --git a/src/unstable/myriadstream.cpp b/src/unstable/myriadstream.cpp
new file mode 100644
index 0000000..e6e94bc
--- /dev/null
+++ b/src/unstable/myriadstream.cpp
@@ -0,0 +1,309 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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/myriadstream.h"
9
10#include <string.h>
11
12// #define MYRIAD_STREAM_DEBUG 1
13
14#ifdef MYRIAD_STREAM_DEBUG
15#include "bu/sio.h"
16
17using Bu::sio;
18using Bu::Fmt;
19#endif
20
21Bu::MyriadStream::MyriadStream( Bu::Myriad &rMyriad,
22 Bu::Myriad::Stream *pStream ) :
23 rMyriad( rMyriad ),
24 pStream( pStream ),
25 pCurBlock( NULL ),
26 iPos( 0 )
27{
28#ifdef MYRIAD_STREAM_DEBUG
29 sio << "MyriadStream: " << __LINE__ << ": Created, iId=" << pStream->iId << ", iSize="
30 << pStream->iSize << sio.nl;
31#endif
32 //pCurBlock = rMyriad.newBlock();
33 //rMyriad.getBlock( uStream, pCurBlock );
34 //uSize = pCurBlock->uBytesUsed;
35}
36
37Bu::MyriadStream::~MyriadStream()
38{
39 if( pCurBlock )
40 rMyriad.releaseBlock( pCurBlock );
41 //rMyriad.updateStreamSize( uStream, uSize );
42 //rMyriad.deleteBlock( pCurBlock );
43}
44
45void Bu::MyriadStream::close()
46{
47}
48
49Bu::size Bu::MyriadStream::read( void *pBuf, Bu::size nBytes )
50{
51#ifdef MYRIAD_STREAM_DEBUG
52 sio << "MyriadStream: read: " << __LINE__ << ": Started, asked to read " << nBytes << "b."
53 << sio.nl;
54#endif
55 if( nBytes > (Bu::size)pStream->iSize-iPos )
56 nBytes = pStream->iSize-iPos;
57 if( nBytes <= 0 )
58 return 0;
59 int iLeft = nBytes;
60#ifdef MYRIAD_STREAM_DEBUG
61 sio << "MyriadStream: read: " << __LINE__ << ": Started, going to read " << nBytes << "b."
62 << sio.nl;
63#endif
64 if( pCurBlock == NULL )
65 {
66#ifdef MYRIAD_STREAM_DEBUG
67 sio << "MyriadStream: read: " << __LINE__ << ": No block loaded, loading initial block."
68 << sio.nl;
69#endif
70 pCurBlock = rMyriad.getBlock(
71 pStream->aBlocks[iPos/rMyriad.iBlockSize]
72 );
73 }
74 while( iLeft > 0 )
75 {
76 int iCurBlock = pStream->aBlocks[iPos/rMyriad.iBlockSize];
77 if( pCurBlock->iBlockIndex != iCurBlock )
78 {
79#ifdef MYRIAD_STREAM_DEBUG
80 sio << "MyriadStream: read: " << __LINE__ << ": Loading new block " << iCurBlock << "."
81 << sio.nl;
82#endif
83 rMyriad.releaseBlock( pCurBlock );
84 pCurBlock = rMyriad.getBlock( iCurBlock );
85 }
86
87 int iAmnt = Bu::min(
88 Bu::min(
89 rMyriad.iBlockSize - iPos%rMyriad.iBlockSize,
90 iLeft
91 ),
92 pStream->iSize-iPos
93 );
94#ifdef MYRIAD_STREAM_DEBUG
95 sio << "MyriadStream: read: " << __LINE__ << ": Copying out bytes: "
96 << iPos << "(" << (iPos%rMyriad.iBlockSize) << ")+"
97 << iAmnt
98 << ", " << iLeft << "b left." << sio.nl;
99#endif
100 memcpy(
101 pBuf,
102 pCurBlock->pData+(iPos%rMyriad.iBlockSize),
103 iAmnt
104 );
105 iPos += iAmnt;
106 pBuf = &((char *)pBuf)[iAmnt];
107 iLeft -= iAmnt;
108 }
109 return nBytes;
110}
111
112Bu::size Bu::MyriadStream::write( const void *pBuf, Bu::size nBytes )
113{
114 if( nBytes <= 0 )
115 return 0;
116
117#ifdef MYRIAD_STREAM_DEBUG
118 sio << "MyriadStream: write: " << __LINE__ << ": Started, asked to write " << nBytes << "b."
119 << sio.nl;
120#endif
121 if( nBytes <= 0 )
122 return 0;
123 int iLeft = nBytes;
124 /*
125 if( pCurBlock == NULL )
126 {
127#ifdef MYRIAD_STREAM_DEBUG
128 sio << "MyriadStream: write: No block loaded, loading initial block."
129 << sio.nl;
130#endif
131 pCurBlock = rMyriad.getBlock(
132 pStream->aBlocks[iPos/rMyriad.iBlockSize]
133 );
134 }*/
135
136 while( iLeft > 0 )
137 {
138 int iCurBlock;
139 if( iPos/rMyriad.iBlockSize < pStream->aBlocks.getSize() )
140 {
141 iCurBlock = pStream->aBlocks[iPos/rMyriad.iBlockSize];
142 }
143 else
144 {
145 iCurBlock = rMyriad.streamAddBlock( pStream );
146#ifdef MYRIAD_STREAM_DEBUG
147 sio << "MyriadStream: write: " << __LINE__ << ": New block allocated and appended: "
148 << iCurBlock << "." << sio.nl;
149
150#endif
151 }
152 if( !pCurBlock || pCurBlock->iBlockIndex != iCurBlock )
153 {
154#ifdef MYRIAD_STREAM_DEBUG
155 sio << "MyriadStream: write: " << __LINE__ << ": Loading new block " << iCurBlock << "."
156 << sio.nl;
157#endif
158 rMyriad.releaseBlock( pCurBlock );
159 pCurBlock = rMyriad.getBlock( iCurBlock );
160 }
161 pCurBlock->bChanged = true;
162
163 // There are two main writing modes when it comes down to it.
164 // Overwrite mode and append mode. Append is what pretty much always
165 // happens when creating a new stream.
166 if( iPos < pStream->iSize )
167 {
168 int iAmnt = Bu::min(
169 Bu::min(
170 rMyriad.iBlockSize - iPos%rMyriad.iBlockSize,
171 iLeft
172 ),
173 pStream->iSize-iPos
174 );
175#ifdef MYRIAD_STREAM_DEBUG
176 sio << "MyriadStream: write (ovr): " << __LINE__ << ": Copying in bytes: "
177 << (iPos%rMyriad.iBlockSize) << "+"
178 << iAmnt
179 << ", " << iLeft << "b left." << sio.nl;
180#endif
181 memcpy(
182 pCurBlock->pData+(iPos%rMyriad.iBlockSize),
183 pBuf,
184 iAmnt
185 );
186 iPos += iAmnt;
187 pBuf = &((char *)pBuf)[iAmnt];
188 iLeft -= iAmnt;
189 }
190 else
191 {
192 int iAmnt = Bu::min(
193 rMyriad.iBlockSize - iPos%rMyriad.iBlockSize,
194 iLeft
195 );
196#ifdef MYRIAD_STREAM_DEBUG
197 sio << "MyriadStream: write (app): " << __LINE__ << ": Copying in bytes: "
198 << (iPos%rMyriad.iBlockSize) << "+"
199 << iAmnt
200 << ", " << iLeft << "b left." << sio.nl;
201#endif
202 memcpy(
203 pCurBlock->pData+(iPos%rMyriad.iBlockSize),
204 pBuf,
205 iAmnt
206 );
207 iPos += iAmnt;
208 pStream->iSize += iAmnt;
209 rMyriad.headerChanged();
210 pBuf = &((char *)pBuf)[iAmnt];
211 iLeft -= iAmnt;
212 }
213 }
214
215 return nBytes;
216}
217
218Bu::size Bu::MyriadStream::tell()
219{
220 return iPos;
221}
222
223void Bu::MyriadStream::seek( Bu::size offset )
224{
225 iPos += offset;
226}
227
228void Bu::MyriadStream::setPos( Bu::size pos )
229{
230 iPos = pos;
231}
232
233void Bu::MyriadStream::setPosEnd( Bu::size pos )
234{
235 iPos = pStream->iSize-pos;
236}
237
238bool Bu::MyriadStream::isEos()
239{
240 return iPos >= pStream->iSize;
241}
242
243bool Bu::MyriadStream::isOpen()
244{
245 return true;
246}
247
248void Bu::MyriadStream::flush()
249{
250}
251
252bool Bu::MyriadStream::canRead()
253{
254 return true;
255}
256
257bool Bu::MyriadStream::canWrite()
258{
259 return true;
260}
261
262bool Bu::MyriadStream::isReadable()
263{
264 return true;
265}
266
267bool Bu::MyriadStream::isWritable()
268{
269 return true;
270}
271
272bool Bu::MyriadStream::isSeekable()
273{
274 return true;
275}
276
277bool Bu::MyriadStream::isBlocking()
278{
279 return true;
280}
281
282void Bu::MyriadStream::setBlocking( bool /*bBlocking*/ )
283{
284}
285
286void Bu::MyriadStream::setSize( Bu::size iSize )
287{
288 if( iSize < 0 )
289 iSize = 0;
290 rMyriad.setStreamSize( pStream, iSize );
291 if( iPos > iSize )
292 iPos = iSize;
293}
294
295Bu::size Bu::MyriadStream::getSize() const
296{
297 return pStream->iSize;
298}
299
300Bu::size Bu::MyriadStream::getBlockSize() const
301{
302 return rMyriad.getBlockSize();
303}
304
305Bu::String Bu::MyriadStream::getLocation() const
306{
307 return Bu::String("%1").arg( pStream->iId );
308}
309
diff --git a/src/unstable/myriadstream.h b/src/unstable/myriadstream.h
new file mode 100644
index 0000000..fdad669
--- /dev/null
+++ b/src/unstable/myriadstream.h
@@ -0,0 +1,61 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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_MYRIAD_STREAM_H
9#define BU_MYRIAD_STREAM_H
10
11#include "bu/stream.h"
12#include "bu/myriad.h"
13
14namespace Bu
15{
16 class MyriadStream : public Bu::Stream
17 {
18 friend class Myriad;
19 private:
20 /**
21 * These can only be created by the Myriad class.
22 */
23 MyriadStream( Myriad &rMyriad, Myriad::Stream *pStream );
24
25 public:
26 virtual ~MyriadStream();
27
28 virtual void close();
29 virtual Bu::size read( void *pBuf, Bu::size nBytes );
30 virtual Bu::size write( const void *pBuf, Bu::size nBytes );
31 using Stream::write;
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 Myriad &rMyriad;
54 Myriad::Stream *pStream;
55 Myriad::Block *pCurBlock;
56 int iBlockSize;
57 int iPos;
58 };
59};
60
61#endif
diff --git a/src/unstable/newline.cpp b/src/unstable/newline.cpp
new file mode 100644
index 0000000..ffc9eb0
--- /dev/null
+++ b/src/unstable/newline.cpp
@@ -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#include "bu/newline.h"
9
10Bu::NewLine::NewLine( Bu::Stream &rNext ) :
11 Bu::Filter( rNext ),
12 bExChar( false )
13{
14}
15
16Bu::NewLine::~NewLine()
17{
18}
19
20void Bu::NewLine::start()
21{
22}
23
24Bu::size Bu::NewLine::stop()
25{
26 return 0;
27}
28
29Bu::size Bu::NewLine::read( void *pBufV, Bu::size iAmnt )
30{
31 Bu::size iTotal = 0;
32 Bu::size iOffset = 0;
33 Bu::size iRead = rNext.read( pBufV, iAmnt );
34 char *pBuf = (char *)pBufV;
35
36 for( Bu::size i = 0; i < iRead; i++ )
37 {
38 if( pBuf[i] == '\r' )
39 {
40 pBuf[i+iOffset] = '\n';
41 if( pBuf[i+1] == '\n' )
42 {
43 iOffset--;
44 }
45 }
46 else if( pBuf[i] == '\n' )
47 {
48 pBuf[i+iOffset] = '\n';
49 if( pBuf[i+1] == '\r' )
50 {
51 iOffset--;
52 }
53 }
54 else if( iOffset )
55 {
56 pBuf[i+iOffset] = pBuf[i];
57 }
58 }
59
60 iTotal += iRead + iOffset;
61 return iTotal;
62}
63
64Bu::size Bu::NewLine::write( const void *, Bu::size )
65{
66 return 0;
67}
68
diff --git a/src/unstable/newline.h b/src/unstable/newline.h
new file mode 100644
index 0000000..afe0a84
--- /dev/null
+++ b/src/unstable/newline.h
@@ -0,0 +1,41 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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_NEW_LINE_H
9#define BU_NEW_LINE_H
10
11#include "bu/filter.h"
12
13namespace Bu
14{
15 /**
16 * Converts new-line characters from any standard convention into linefeeds
17 * (\\n) on reading, and converts them to either your OS's standard or a
18 * specified standard, depending on how you construct the class.
19 *
20 * If you're reading in a text file, then this filter is practically
21 * required.
22 */
23 class NewLine : public Bu::Filter
24 {
25 public:
26 NewLine( Bu::Stream &rNext );
27 virtual ~NewLine();
28
29 virtual void start();
30 virtual Bu::size stop();
31
32 virtual Bu::size read( void *pBuf, Bu::size iAmnt );
33 virtual Bu::size write( const void *pBuf, Bu::size iAmnt );
34
35 private:
36 bool bExChar;
37 char cExChar;
38 };
39};
40
41#endif
diff --git a/src/unstable/udpsocket.cpp b/src/unstable/udpsocket.cpp
new file mode 100644
index 0000000..702840a
--- /dev/null
+++ b/src/unstable/udpsocket.cpp
@@ -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 WIN32 //not on windows
9
10#include "bu/udpsocket.h"
11
12#include "bu/sio.h"
13using namespace Bu;
14#include <fcntl.h>
15
16#include <errno.h>
17#include <arpa/inet.h>
18#include <sys/socket.h>
19#include <netinet/in.h>
20#include <sys/utsname.h>
21
22namespace Bu { subExceptionDef( UdpSocketException ) }
23
24#define saTarget ( *((struct sockaddr_in *)paTarget) )
25
26Bu::UdpSocket::UdpSocket( int iUdpSocket ) :
27 iUdpSocket( iUdpSocket ),
28 paTarget( NULL ),
29 bBound( false )
30{
31}
32
33Bu::UdpSocket::UdpSocket( const Bu::String &sAddr, int iPort, int iFlags ) :
34 iUdpSocket( 0 ),
35 paTarget( NULL ),
36 bBound( false )
37{
38 iUdpSocket = socket( PF_INET, SOCK_DGRAM, 0 );
39 if( iUdpSocket < 0 )
40 {
41 throw UdpSocketException("Couldn't open udp socket: %s",
42 strerror( errno )
43 );
44 }
45
46 if( (iFlags&Broadcast) )
47 {
48 int broadcast = 1;
49 if( (setsockopt( iUdpSocket, SOL_SOCKET, SO_BROADCAST,
50 &broadcast, sizeof(broadcast) )) == -1)
51 {
52 throw UdpSocketException("Couldn't set udp socket to broadcast: %s",
53 strerror( errno )
54 );
55 }
56 }
57
58 paTarget = new struct sockaddr_in;
59 saTarget.sin_family = AF_INET;
60 saTarget.sin_port = htons( iPort );
61 saTarget.sin_addr.s_addr = inet_addr( sAddr.getStr() ); // INADDR_ANY;
62 memset( saTarget.sin_zero, '\0', sizeof(saTarget.sin_zero) );
63
64 if( (iFlags&Read) )
65 {
66 if( bind( iUdpSocket, (struct sockaddr*)paTarget, sizeof(struct sockaddr_in) )
67 == -1 )
68 {
69 throw UdpSocketException("Couldn't bind port to udp socket: %s",
70 strerror( errno )
71 );
72 }
73 bBound = true;
74 }
75}
76
77Bu::UdpSocket::~UdpSocket()
78{
79 close();
80 delete (struct sockaddr_in *)paTarget;
81 paTarget = NULL;
82}
83
84Bu::String Bu::UdpSocket::addrToStr( const addr &a )
85{
86 return Bu::String("%1.%2.%3.%4").
87 arg( (a&0xff) ).
88 arg( (a&0xff00)>>8 ).
89 arg( (a&0xff0000)>>16 ).
90 arg( (a&0xff000000)>>24 );
91}
92
93void Bu::UdpSocket::close()
94{
95 ::close( iUdpSocket );
96}
97
98Bu::size Bu::UdpSocket::read( void *pBuf, Bu::size nBytes )
99{
100 return recv( iUdpSocket, pBuf, nBytes, 0 );
101}
102
103Bu::size Bu::UdpSocket::read( void *pBuf, Bu::size nBytes,
104 Bu::UdpSocket::addr &aHost, int &iPort )
105{
106 sockaddr_in name;
107 socklen_t size = sizeof(name);
108 Bu::size ret = recvfrom( iUdpSocket, pBuf, nBytes, 0,
109 (struct sockaddr *)&name, &size );
110 aHost = name.sin_addr.s_addr;
111 iPort = ntohs(name.sin_port);
112 return ret;
113}
114
115Bu::size Bu::UdpSocket::write( const void *pBuf, Bu::size nBytes )
116{
117 if( bBound )
118 {
119 return sendto( iUdpSocket, pBuf, nBytes, 0, NULL, 0 );
120 }
121 else
122 {
123 return sendto( iUdpSocket, pBuf, nBytes, 0,
124 (struct sockaddr*)paTarget, sizeof(struct sockaddr_in) );
125 }
126}
127
128Bu::size Bu::UdpSocket::tell()
129{
130 throw Bu::UnsupportedException();
131}
132
133void Bu::UdpSocket::seek( Bu::size )
134{
135 throw Bu::UnsupportedException();
136}
137
138void Bu::UdpSocket::setPos( Bu::size )
139{
140 throw Bu::UnsupportedException();
141}
142
143void Bu::UdpSocket::setPosEnd( Bu::size )
144{
145 throw Bu::UnsupportedException();
146}
147
148bool Bu::UdpSocket::isEos()
149{
150 return false;
151}
152
153bool Bu::UdpSocket::isOpen()
154{
155 return true;
156}
157
158void Bu::UdpSocket::flush()
159{
160}
161
162bool Bu::UdpSocket::canRead()
163{
164 return bBound;
165}
166
167bool Bu::UdpSocket::canWrite()
168{
169 return true;
170}
171
172bool Bu::UdpSocket::isReadable()
173{
174 return bBound;
175}
176
177bool Bu::UdpSocket::isWritable()
178{
179 return true;
180}
181
182bool Bu::UdpSocket::isSeekable()
183{
184 return false;
185}
186
187bool Bu::UdpSocket::isBlocking()
188{
189 return true;
190}
191
192void Bu::UdpSocket::setBlocking( bool bBlocking )
193{
194#ifndef WIN32
195 if( bBlocking )
196 {
197 fcntl( iUdpSocket, F_SETFL, fcntl( iUdpSocket, F_GETFL, 0 ) & (~O_NONBLOCK) );
198 }
199 else
200 {
201 fcntl( iUdpSocket, F_SETFL, fcntl( iUdpSocket, F_GETFL, 0 ) | O_NONBLOCK );
202 }
203#else
204 u_long iMode;
205 if( bBlocking )
206 iMode = 0;
207 else
208 iMode = 1;
209 //-------------------------
210 // Set the socket I/O mode: In this case FIONBIO
211 // enables or disables the blocking mode for the
212 // socket based on the numerical value of iMode.
213 // If iMode = 0, blocking is enabled;
214 // If iMode != 0, non-blocking mode is enabled.
215 bu_ioctlsocket(iUdpSocket, FIONBIO, &iMode);
216#endif
217}
218
219void Bu::UdpSocket::setSize( Bu::size )
220{
221 throw Bu::UnsupportedException();
222}
223
224Bu::size Bu::UdpSocket::getSize() const
225{
226 throw Bu::UnsupportedException();
227}
228
229Bu::size Bu::UdpSocket::getBlockSize() const
230{
231 return 1500;
232}
233
234Bu::String Bu::UdpSocket::getLocation() const
235{
236 throw Bu::UnsupportedException();
237}
238
239#endif
240
diff --git a/src/unstable/udpsocket.h b/src/unstable/udpsocket.h
new file mode 100644
index 0000000..f228f08
--- /dev/null
+++ b/src/unstable/udpsocket.h
@@ -0,0 +1,84 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file is part of the libbu++ library and is released under the
5 * terms of the license contained in the file LICENSE.
6 */
7#ifndef WIN32 //not on windows
8
9#ifndef BU_UDP_SOCKET_H
10#define BU_UDP_SOCKET_H
11
12#include <stdint.h>
13
14#include "bu/stream.h"
15
16namespace Bu
17{
18 subExceptionDecl( UdpSocketException );
19
20 class UdpSocket : public Stream
21 {
22 public:
23 UdpSocket( int iUdpSocket );
24 UdpSocket( const Bu::String &sAddr, int iPort, int iFlags );
25 virtual ~UdpSocket();
26
27 typedef uint32_t addr;
28
29 static Bu::String addrToStr( const addr &a );
30
31 virtual void close();
32 virtual Bu::size read( void *pBuf, Bu::size nBytes );
33 virtual Bu::size read( void *pBuf, Bu::size nBytes,
34 addr &sHost, int &iPort );
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 virtual void setSize( Bu::size iSize );
58
59 enum {
60 // Flags
61 Read = 0x01, ///< Open udp socket for reading
62 Write = 0x02, ///< Open udp socket for writing
63 ReadWrite = 0x03, ///< Open for both read and write
64 Broadcast = 0x04, ///< Open for broadcast
65 };
66
67 virtual size getSize() const;
68 virtual size getBlockSize() const;
69 virtual Bu::String getLocation() const;
70
71 private:
72#ifdef WIN32
73 unsigned int iUdpSocket;
74#else
75 int iUdpSocket;
76#endif
77 void *paTarget;
78 bool bBound;
79 };
80};
81
82#endif
83
84#endif
diff --git a/src/unstable/url.cpp b/src/unstable/url.cpp
new file mode 100644
index 0000000..7b4a48e
--- /dev/null
+++ b/src/unstable/url.cpp
@@ -0,0 +1,285 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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/url.h"
9#ifndef WIN32
10# include <netdb.h>
11# include <netinet/in.h>
12#endif
13#include <stdlib.h>
14
15char Bu::Url::hexcode[] = {
16 '0', '1', '2', '3', '4', '5', '6', '7',
17 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
18};
19
20Bu::Url::Url()
21{
22}
23
24Bu::Url::Url( const Bu::String &sUrl )
25{
26 parseUrl( sUrl );
27}
28
29Bu::Url::~Url()
30{
31}
32
33void Bu::Url::parseUrl( const Bu::String &sUrl )
34{
35 clear();
36
37 Bu::String::const_iterator i = sUrl.begin();
38 parseProtocol( i );
39 parseUserPass( i );
40 parseHost( i );
41 parsePath( i );
42}
43
44Bu::String Bu::Url::decode( const Bu::String &sStr )
45{
46 Bu::String sRet;
47 char buf[3] = {0, 0, 0};
48 for( Bu::String::const_iterator i = sStr.begin(); i; i++ )
49 {
50 if( *i == '+' )
51 {
52 sRet += ' ';
53 }
54 else if( *i == '%' )
55 {
56 i++;
57 buf[0] = *i;
58 i++;
59 buf[1] = *i;
60 sRet += (char)((unsigned char)strtol( buf, NULL, 16 ));
61 }
62 else
63 {
64 sRet += *i;
65 }
66 }
67 return sRet;
68}
69
70Bu::String Bu::Url::encode( const Bu::String &sStr )
71{
72 Bu::String sRet;
73 for( Bu::String::const_iterator i = sStr.begin(); i; i++ )
74 {
75 if( *i == ' ' )
76 {
77 sRet += '+';
78 }
79 else if(
80 (*i >= 'A' && *i <= 'Z') ||
81 (*i >= 'a' && *i <= 'z') ||
82 (*i >= '0' && *i <= '9') ||
83 (*i == '-' || *i == '_' || *i == '.' || *i == '~')
84 )
85 {
86 sRet += *i;
87 }
88 else
89 {
90 unsigned char b = *i;
91 sRet += '%';
92 sRet += hexcode[(b>>4)&0xF];
93 sRet += hexcode[b&0xF];
94 }
95 }
96 return sRet;
97}
98
99void Bu::Url::parseProtocol( Bu::String::const_iterator &i )
100{
101 Bu::String::const_iterator s = i.find("://", 3);
102 if( !s )
103 throw Bu::ExceptionBase("No :// in url");
104 Bu::String sTmp( i, s );
105 setProtocol( sTmp );
106 i = s + 3;
107}
108
109void Bu::Url::setProtocol( const Bu::String &sNewProto, bool bAutoSetPort )
110{
111 sProtocol = sNewProto;
112#ifndef WIN32
113 if( bAutoSetPort )
114 {
115 struct servent *se = getservbyname( sProtocol.getStr(), "tcp" );
116 if( se )
117 {
118 iPort = ntohs( se->s_port );
119 }
120 }
121#endif
122}
123
124void Bu::Url::parseUserPass( Bu::String::const_iterator &i )
125{
126 Bu::String::const_iterator s = i.find('@');
127 if( !s )
128 return;
129
130 Bu::String::const_iterator p = i.find(':');
131 if( p )
132 {
133 sUser.set( i, p );
134 sPass.set( p+1, s );
135 }
136 else
137 {
138 sUser.set( i, s );
139 }
140
141 i = s + 1;
142}
143
144void Bu::Url::parseHost( Bu::String::const_iterator &i )
145{
146 Bu::String::const_iterator s = i;
147 for( ; s && *s != '/'; s++ )
148 {
149 if( *s == ':' )
150 {
151 sHost.set( i, s );
152 i = s + 1;
153 s = i.find('/');
154 Bu::String sPort( i, s );
155 iPort = strtol( sPort.getStr(), NULL, 10 );
156 i = s;
157 return;
158 }
159 }
160 sHost.set( i, s );
161 i = s;
162}
163
164void Bu::Url::parsePath( const Bu::String &sPath )
165{
166 Bu::String::const_iterator i = sPath.begin();
167 parsePath( i );
168}
169
170void Bu::Url::parsePath( Bu::String::const_iterator &i )
171{
172 if( i )
173 {
174 Bu::String::const_iterator s = i.find('?');
175 sPath.set( i, s );
176 i = s + 1;
177 if( s )
178 {
179 parseParams( i );
180 }
181 }
182 else
183 {
184 sPath = "/";
185 }
186}
187
188void Bu::Url::parseParams( const Bu::String &sQuery )
189{
190 Bu::String::const_iterator i = sQuery.begin();
191 parseParams( i );
192}
193
194void Bu::Url::parseParams( Bu::String::const_iterator &i )
195{
196 bool bName = true;
197 Bu::String sName, sValue;
198 for( Bu::String::const_iterator s = i; s; s++ )
199 {
200 if( bName )
201 {
202 if( *s == '&' )
203 {
204 sName.set( i, s );
205 sValue.clear();
206 i = s + 1;
207 addParam( decode( sName ), decode( sValue ) );
208 }
209 else if( *s == '=' )
210 {
211 sName.set( i, s );
212 i = s + 1;
213 bName = false;
214 }
215 }
216 else
217 {
218 if( *s == '&' )
219 {
220 sValue.set( i, s );
221 i = s + 1;
222 bName = true;
223 addParam( decode( sName ), decode( sValue ) );
224 }
225 }
226 }
227 if( i )
228 {
229 if( bName )
230 {
231 sName.set( i );
232 sValue.clear();
233 }
234 else
235 {
236 sValue.set( i );
237 }
238 addParam( decode( sName ), decode( sValue ) );
239 }
240}
241
242void Bu::Url::addParam( const Bu::String &n, const Bu::String &v )
243{
244 lParam.append( Param( n, v ) );
245}
246
247void Bu::Url::clear()
248{
249 sProtocol.clear();
250 sUser.clear();
251 sPass.clear();
252 sHost.clear();
253 sPath.clear();
254 iPort.clear();
255}
256
257Bu::String Bu::Url::getFullPath() const
258{
259 Bu::String sBuf = sPath;
260 if( !lParam.isEmpty() )
261 {
262 for( ParamList::const_iterator i = lParam.begin(); i; i++ )
263 {
264 if( i == lParam.begin() )
265 sBuf += "?";
266 else
267 sBuf += "&";
268
269 sBuf += encode( (*i).sName );
270 if( !(*i).sValue.isEmpty() )
271 {
272 sBuf += "=" + encode( (*i).sValue );
273 }
274 }
275 }
276
277 return sBuf;
278}
279
280Bu::String Bu::Url::getUrl() const
281{
282 Bu::String sBuf = sProtocol + "://" + sHost + getFullPath();
283 return sBuf;
284}
285
diff --git a/src/unstable/url.h b/src/unstable/url.h
new file mode 100644
index 0000000..d751578
--- /dev/null
+++ b/src/unstable/url.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_URL_H
9#define BU_URL_H
10
11#include "bu/string.h"
12#include "bu/atom.h"
13
14namespace Bu
15{
16 class Url
17 {
18 public:
19 typedef struct Param
20 {
21 Param() { }
22 Param( const Param &r ) : sName( r.sName ), sValue( r.sValue ) { }
23 Param( const Bu::String &n, const Bu::String &v ) :
24 sName( n ), sValue( v ) { }
25 Bu::String sName;
26 Bu::String sValue;
27 } Param;
28 typedef Bu::List<Param> ParamList;
29
30 public:
31 Url();
32 Url( const Bu::String &sUrl );
33 virtual ~Url();
34
35 void parseUrl( const Bu::String &sUrl );
36 void parseParams( const Bu::String &sQuery );
37 void parseParams( Bu::String::const_iterator &i );
38 void parsePath( const Bu::String &sPath );
39 void parsePath( Bu::String::const_iterator &i );
40 void clear();
41
42 Bu::String getUrl() const;
43 Bu::String getFullPath() const;
44
45 const Bu::String &getProtocol() const { return sProtocol; }
46 const Bu::String &getUser() const { return sUser; }
47 const Bu::String &getPass() const { return sPass; }
48 const Bu::String &getHost() const { return sHost; }
49 const Bu::String &getPath() const { return sPath; }
50 int getPort() const { return iPort; }
51 ParamList::const_iterator getParamBegin() const
52 { return lParam.begin(); }
53
54 void setProtocol( const Bu::String &sNewHost, bool bAutoSetPort=true );
55 void setUser( const Bu::String &s ) { sUser = s; }
56 void setPass( const Bu::String &s ) { sPass = s; }
57 void setHost( const Bu::String &s ) { sHost = s; }
58 void setPath( const Bu::String &s ) { sPath = s; }
59 void setPort( int i ) { iPort = i; }
60 void addParam( const Bu::String &n, const Bu::String &v );
61
62 bool hasPort() const { return iPort.has(); }
63
64 static Bu::String decode( const Bu::String &sStr );
65 static Bu::String encode( const Bu::String &sStr );
66
67 private: // Parsing code
68 void parseProtocol( Bu::String::const_iterator &i );
69 void parseUserPass( Bu::String::const_iterator &i );
70 void parseHost( Bu::String::const_iterator &i );
71
72 private:
73 Bu::String sProtocol;
74 Bu::String sUser;
75 Bu::String sPass;
76 Bu::String sHost;
77 Bu::String sPath;
78 Bu::Atom<int> iPort;
79 ParamList lParam;
80
81 static char hexcode[16];
82 };
83};
84
85#endif
diff --git a/src/unstable/urn.cpp b/src/unstable/urn.cpp
new file mode 100644
index 0000000..106fb64
--- /dev/null
+++ b/src/unstable/urn.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
diff --git a/src/unstable/urn.h b/src/unstable/urn.h
new file mode 100644
index 0000000..106fb64
--- /dev/null
+++ b/src/unstable/urn.h
@@ -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
diff --git a/src/unstable/utfstring.cpp b/src/unstable/utfstring.cpp
new file mode 100644
index 0000000..19d3ddc
--- /dev/null
+++ b/src/unstable/utfstring.cpp
@@ -0,0 +1,539 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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/utfstring.h"
9
10#include "bu/string.h"
11#include "bu/stream.h"
12#include "bu/config.h"
13#include "bu/sio.h"
14using Bu::sio;
15
16Bu::UtfString::UtfString()
17{
18}
19
20Bu::UtfString::UtfString( const Bu::String &sInput, Encoding eEnc )
21{
22 set( sInput, eEnc );
23}
24
25Bu::UtfString::~UtfString()
26{
27}
28
29void Bu::UtfString::set( const Bu::String &sInput, Encoding eEnc )
30{
31 switch( eEnc )
32 {
33 case Utf8:
34 setUtf8( sInput );
35 break;
36
37 case Utf16:
38 setUtf16( sInput );
39 break;
40
41 case Utf16be:
42 setUtf16be( sInput );
43 break;
44
45 case Utf16le:
46 setUtf16le( sInput );
47 break;
48
49 case Utf32:
50 setUtf32( sInput );
51 break;
52
53 case Utf32be:
54 setUtf32be( sInput );
55 break;
56
57 case Utf32le:
58 setUtf32le( sInput );
59 break;
60
61 case Ucs2:
62 throw Bu::ExceptionBase("Ucs2 not supported yet.");
63 break;
64
65 case Ucs4:
66 throw Bu::ExceptionBase("Ucs4 not supported yet.");
67 break;
68
69 case GuessEncoding:
70 throw Bu::ExceptionBase("Guessing mode not supported yet.");
71 break;
72 }
73}
74
75void Bu::UtfString::append( UtfChar ch )
76{
77 if( ch >= 0x10000 )
78 {
79 ch -= 0x10000;
80 append16( ((ch>>10)&0x3FF)| 0xD800u );
81 append16( (ch&0x3FF)| 0xDC00u );
82 }
83 else
84 {
85 append16( (uint16_t)(ch) );
86 }
87}
88
89void Bu::UtfString::setUtf8( const Bu::String &sInput )
90{
91 static uint8_t lmask[8] = {
92 0x00,
93 0x01,
94 0x03,
95 0x07,
96 0x0f,
97 0x1f,
98 0x3f,
99 0x7f
100 };
101 for( Bu::String::const_iterator i = sInput.begin(); i; i++ )
102 {
103 if( ((int)(uint8_t)*i)&0x80 )
104 {
105 int iBytes = 1;
106 for(; (((uint8_t)(*i))<<iBytes)&0x80; iBytes++ ) { }
107 Bu::UtfChar uPt = ((*i) & lmask[7-iBytes])<<(6*(iBytes-1));
108 for( iBytes--; iBytes >= 1; iBytes-- )
109 {
110 i++;
111 uPt |= ((*i)&lmask[6])<<(6*(iBytes-1));
112 }
113 append( uPt );
114 }
115 else
116 {
117 append( (Bu::UtfChar)(*i) );
118 }
119 }
120}
121
122void Bu::UtfString::setUtf16( const Bu::String &sInput )
123{
124// Bu::String::const_iterator i = sInput.begin();
125 if( (uint8_t)*sInput.begin() == 0xFF &&
126 (uint8_t)*(sInput.begin()+1) == 0xFE )
127 {
128 setUtf16le( sInput );
129 return;
130 }
131 setUtf16be( sInput );
132}
133
134void Bu::UtfString::setUtf16be( const Bu::String &sInput )
135{
136 Bu::String::const_iterator i = sInput.begin();
137 if( (uint8_t)*sInput.begin() == 0xFE &&
138 (uint8_t)*(sInput.begin()+1) == 0xFF )
139
140 {
141 i += 2;
142 sio << "Verified big endian." << sio.nl;
143 }
144 else
145 {
146 sio << "Assuming big endian." << sio.nl;
147 }
148 uint16_t hi, lo;
149 for( ; i; i++ )
150 {
151 hi = (((uint8_t)*i)<<8) | ((uint8_t)*(++i));
152 append16( hi );
153 if( (hi&0xD800u) == 0xD800u )
154 {
155 lo = (((uint8_t)*(++i))<<8) | ((uint8_t)*(++i));
156 append16( lo );
157 }
158 }
159}
160
161void Bu::UtfString::setUtf16le( const Bu::String &sInput )
162{
163 Bu::String::const_iterator i = sInput.begin();
164 if( (uint8_t)*sInput.begin() == 0xFF &&
165 (uint8_t)*(sInput.begin()+1) == 0xFE )
166 {
167 i += 2;
168 sio << "Verified little endian." << sio.nl;
169 }
170 else
171 {
172 sio << "Assuming little endian." << sio.nl;
173 }
174 uint16_t hi, lo;
175 for( ; i; i++ )
176 {
177 hi = (((uint8_t)*i)) | ((uint8_t)*(++i)<<8);
178 append16( hi );
179 if( (hi&0xD800u) == 0xD800u )
180 {
181 lo = (((uint8_t)*(++i))) | ((uint8_t)*(++i)<<8);
182 append16( lo );
183 }
184 }
185}
186
187void Bu::UtfString::setUtf32( const Bu::String &sInput )
188{
189 Bu::String::const_iterator i = sInput.begin();
190 if( (uint8_t)*i == 0x00 &&
191 (uint8_t)*(++i) == 0x00 &&
192 (uint8_t)*(++i) == 0xFF &&
193 (uint8_t)*(++i) == 0xFE )
194 {
195 setUtf32le( sInput );
196 return;
197 }
198 setUtf32be( sInput );
199}
200
201void Bu::UtfString::setUtf32be( const Bu::String &sInput )
202{
203 Bu::String::const_iterator i = sInput.begin();
204 if( (uint8_t)*i == 0x00 &&
205 (uint8_t)*(++i) == 0x00 &&
206 (uint8_t)*(++i) == 0xFE &&
207 (uint8_t)*(++i) == 0xFF )
208 {
209 i++;
210 sio << "Verified big endian." << sio.nl;
211 }
212 else
213 {
214 i = sInput.begin();
215 sio << "Assuming big endian." << sio.nl;
216 }
217 for( ; i; i++ )
218 {
219 append( (((uint8_t)*i)<<24) |
220 (((uint8_t)*(++i))<<16) |
221 (((uint8_t)*(++i))<<8) |
222 ((uint8_t)*(++i))
223 );
224 }
225}
226
227void Bu::UtfString::setUtf32le( const Bu::String &sInput )
228{
229 Bu::String::const_iterator i = sInput.begin();
230 if( (uint8_t)*i == 0x00 &&
231 (uint8_t)*(++i) == 0x00 &&
232 (uint8_t)*(++i) == 0xFF &&
233 (uint8_t)*(++i) == 0xFE )
234 {
235 i++;
236 sio << "Verified little endian." << sio.nl;
237 }
238 else
239 {
240 i = sInput.begin();
241 sio << "Assuming little endian." << sio.nl;
242 }
243 for( ; i; i++ )
244 {
245 append( ((uint8_t)*i) |
246 (((uint8_t)*(++i))<<8) |
247 (((uint8_t)*(++i))<<16) |
248 (((uint8_t)*(++i))<<24)
249 );
250 }
251}
252
253void Bu::UtfString::write( Bu::Stream &sOut, Encoding eEnc )
254{
255 switch( eEnc )
256 {
257 case Utf8:
258 writeUtf8( sOut );
259 break;
260
261 case Utf16:
262// writeUtf16( sOut );
263// break;
264
265 case Utf16be:
266 writeUtf16be( sOut );
267 break;
268
269 case Utf16le:
270 writeUtf16le( sOut );
271 break;
272
273 case Utf32:
274// writeUtf32( sOut );
275// break;
276
277 case Utf32be:
278 writeUtf32be( sOut );
279 break;
280
281 case Utf32le:
282 writeUtf32le( sOut );
283 break;
284
285 case Ucs2:
286 throw Bu::ExceptionBase("Ucs2 not supported yet.");
287 break;
288
289 case Ucs4:
290 throw Bu::ExceptionBase("Ucs4 not supported yet.");
291 break;
292
293 case GuessEncoding:
294 throw Bu::ExceptionBase(
295 "GuessEncoding is incompatible with encoding.");
296 break;
297
298 }
299}
300
301void Bu::UtfString::writeUtf8( Bu::Stream &sOut )
302{
303 int iPos = 0;
304 while( iPos < aData.getSize() )
305 {
306 uint8_t uByte;
307 Bu::UtfChar chr = nextChar( iPos );
308 if( chr >= 0x010000 )
309 {
310 // Four bytes
311 // 111 111111 111111 111111
312 uByte = (chr>>18)|0xF0;
313 sOut.write( &uByte, 1 );
314 uByte = ((chr>>12)&0x3F)|0x80;
315 sOut.write( &uByte, 1 );
316 uByte = ((chr>>6)&0x3F)|0x80;
317 sOut.write( &uByte, 1 );
318 uByte = (chr&0x3F)|0x80;
319 sOut.write( &uByte, 1 );
320 }
321 else if( chr >= 0x800 )
322 {
323 // Three bytes
324 // 1111 111111 111111
325 uByte = (chr>>12)|0xE0;
326 sOut.write( &uByte, 1 );
327 uByte = ((chr>>6)&0x3F)|0x80;
328 sOut.write( &uByte, 1 );
329 uByte = (chr&0x3F)|0x80;
330 sOut.write( &uByte, 1 );
331 }
332 else if( chr >= 0x80 )
333 {
334 // Two bytes
335 // 11111 111111
336 uByte = (chr>>6)|0xC0;
337 sOut.write( &uByte, 1 );
338 uByte = (chr&0x3F)|0x80;
339 sOut.write( &uByte, 1 );
340 }
341 else
342 {
343 // One byte
344 uByte = chr;
345 sOut.write( &uByte, 1 );
346 }
347 }
348}
349/*
350void Bu::UtfString::writeUtf16( Bu::Stream &sOut )
351{
352}
353*/
354void Bu::UtfString::writeUtf16be( Bu::Stream &sOut )
355{
356#if BYTE_ORDER == BIG_ENDIAN
357 uint16_t iTmp = 0xFEFF; // Byte Order Marker
358 sOut.write( &iTmp, 2 );
359 for( Array<uint16_t>::iterator i = aData.begin(); i; i++ )
360 {
361 iTmp = *i;
362 sOut.write( &iTmp, 2 );
363 }
364#else
365 uint16_t iTmp = 0xFEFF; // Byte Order Marker
366 iTmp = (iTmp>>8) | (iTmp<<8);
367 sOut.write( &iTmp, 2 );
368 for( Array<uint16_t>::iterator i = aData.begin(); i; i++ )
369 {
370 iTmp = *i;
371 iTmp = (iTmp>>8) | (iTmp<<8);
372 sOut.write( &iTmp, 2 );
373 }
374#endif
375}
376
377void Bu::UtfString::writeUtf16le( Bu::Stream &sOut )
378{
379#if BYTE_ORDER == LITTLE_ENDIAN
380 uint16_t iTmp = 0xFEFF; // Byte Order Marker
381 sOut.write( &iTmp, 2 );
382 for( Array<uint16_t>::iterator i = aData.begin(); i; i++ )
383 {
384 iTmp = *i;
385 sOut.write( &iTmp, 2 );
386 }
387#else
388 uint16_t iTmp = 0xFEFF; // Byte Order Marker
389 iTmp = (iTmp>>8) | (iTmp<<8);
390 sOut.write( &iTmp, 2 );
391 for( Array<uint16_t>::iterator i = aData.begin(); i; i++ )
392 {
393 iTmp = *i;
394 iTmp = (iTmp>>8) | (iTmp<<8);
395 sOut.write( &iTmp, 2 );
396 }
397#endif
398}
399
400void Bu::UtfString::writeUtf32be( Bu::Stream &sOut )
401{
402#if BYTE_ORDER == BIG_ENDIAN
403 uint32_t iTmp = 0xFEFF; // Byte Order Marker
404 sOut.write( &iTmp, 4 );
405 int i = 0;
406 while( i < aData.getSize() )
407 {
408 iTmp = nextChar( i );
409 sOut.write( &iTmp, 4 );
410 }
411#else
412 uint32_t iTmp = 0xFEFF; // Byte Order Marker
413 iTmp = (iTmp>>24)|(iTmp<<24)|((iTmp&0xff0000)>>8)|((iTmp&0xff00)<<8);
414 sOut.write( &iTmp, 4 );
415 int i = 0;
416 while( i < aData.getSize() )
417 {
418 iTmp = nextChar( i );
419 iTmp = (iTmp>>24)|(iTmp<<24)|((iTmp&0xff0000)>>8)|((iTmp&0xff00)<<8);
420 sOut.write( &iTmp, 4 );
421 }
422#endif
423}
424
425void Bu::UtfString::writeUtf32le( Bu::Stream &sOut )
426{
427#if BYTE_ORDER == LITTLE_ENDIAN
428 uint32_t iTmp = 0xFEFF; // Byte Order Marker
429 sOut.write( &iTmp, 4 );
430 int i = 0;
431 while( i < aData.getSize() )
432 {
433 iTmp = nextChar( i );
434 sOut.write( &iTmp, 4 );
435 }
436#else
437 uint32_t iTmp = 0xFEFF; // Byte Order Marker
438 iTmp = (iTmp>>24)|(iTmp<<24)|((iTmp&0xff0000)>>8)|((iTmp&0xff00)<<8);
439 sOut.write( &iTmp, 4 );
440 int i = 0;
441 while( i < aData.getSize() )
442 {
443 iTmp = nextChar( i );
444 iTmp = (iTmp>>24)|(iTmp<<24)|((iTmp&0xff0000)>>8)|((iTmp&0xff00)<<8);
445 sOut.write( &iTmp, 4 );
446 }
447#endif
448}
449
450Bu::UtfChar Bu::UtfString::get( int iIndex )
451{
452 return nextChar( iIndex );
453}
454
455Bu::UtfChar Bu::UtfString::nextChar( int &iIndex )
456{
457 Bu::UtfChar i = aData[iIndex++];
458 switch( i&0xFC00 )
459 {
460 case 0xD800:
461 return (((i&0x3FF)<<10) | ((aData[iIndex++]&0x3FF)))+0x10000;
462
463 case 0xDC00:
464 return (((aData[iIndex-2]&0x3FF)<<10) | ((i&0x3FF)))+0x10000;
465
466 default:
467 return i;
468 }
469}
470
471void Bu::UtfString::debug()
472{
473 sio << "Raw Utf16: ";
474 for( int i = 0; i < aData.getSize(); i++ )
475 {
476 if( i > 0 )
477 sio << ", ";
478 sio << "0x" << Fmt::hex() << aData[i];
479 }
480 sio << sio.nl;
481 sio << "Code Points: ";
482 for( int i = 0; i < aData.getSize(); i++ )
483 {
484 if( i > 0 )
485 sio << ", ";
486 sio << "0x" << Fmt::hex() << nextChar( i );
487 }
488 sio << sio.nl;
489}
490/*
491void Bu::UtfString::debugUtf8( const Bu::String &sUtf8 )
492{
493 static uint8_t lmask[8] = {
494 0x00,
495 0x01,
496 0x03,
497 0x07,
498 0x0f,
499 0x1f,
500 0x3f,
501 0x7f
502 };
503 for( Bu::String::const_iterator i = sUtf8.begin(); i; i++ )
504 {
505 if( i != sUtf8.begin() )
506 sio << ", ";
507 if( ((int)(uint8_t)*i)&0x80 )
508 {
509// sio << "Flag byte: " << Bu::Fmt().radix(2).width(8).fill('0')
510// << (int)(uint8_t)*i << sio.nl;
511 int iBytes = 1;
512 for(; (((uint8_t)(*i))<<iBytes)&0x80; iBytes++ ) { }
513// sio << "iBytes = " << iBytes << sio.nl;
514 Bu::UtfChar uPt = ((*i) & lmask[7-iBytes])<<(6*(iBytes-1));
515// sio << "mask: " << Bu::Fmt().radix(2).width(8).fill('0')
516// << (int)lmask[7-iBytes] << sio.nl;
517 for( iBytes--; iBytes >= 1; iBytes-- )
518 {
519// sio << "iBytes = " << iBytes << ", shift = " << (6*(iBytes-1))
520// << sio.nl;
521// sio << "next: " << Bu::Fmt().radix(2).width(8).fill('0')
522// << (int)(uint8_t)*i << sio.nl
523// << "mask: " << Bu::Fmt().radix(2).width(8).fill('0')
524// << (int)lmask[6] << sio.nl;
525 i++;
526 uPt |= ((*i)&lmask[6])<<(6*(iBytes-1));
527 }
528 sio << uPt;
529// sio << " (" << Bu::Fmt( 8, 2 ).fill('0')
530// << uPt << ")";
531 }
532 else
533 {
534 sio << (int)((uint8_t)*i);
535 }
536 }
537 sio << sio.nl;
538}
539*/
diff --git a/src/unstable/utfstring.h b/src/unstable/utfstring.h
new file mode 100644
index 0000000..477e272
--- /dev/null
+++ b/src/unstable/utfstring.h
@@ -0,0 +1,174 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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_UTF_STRING_H
9#define BU_UTF_STRING_H
10
11#include <stdint.h>
12#include "bu/array.h"
13
14namespace Bu
15{
16 class String;
17 class Stream;
18
19 /**
20 * UtfChar isn't actually a character, unicode specifies "code points" not
21 * characters. The main reason for this is that not all code points define
22 * usable characters. Some control text directionality, some apply
23 * properties to other code points which are characters. However, most of
24 * these distinctions are only important when implementing displays that
25 * comply with the Unicode standard fully.
26 */
27 typedef uint32_t UtfChar;
28
29 /**
30 * A unicode string. This class represents a string of unicode code points.
31 * Every character in unicode can be represented with 21 bits, but we don't
32 * have a datatype that's 24 bits long, so we return all code points as a
33 * 32 bit unsigned value represented by Bu::UtfChar. However, the UtfString
34 * class, for efficiency purposes doesn't store 32 bit values internally.
35 * It represents all code points in the native utf16 encodeng. This means
36 * that it may be very difficult to quickly determine the length of a
37 * UtfString in code points. Unlike many Unicode handling systems, this
38 * one actually works with complete code points. When using this class you
39 * don't ever have to know about the inner workings of the different
40 * encoding schemes. All of the data is dealt with as whole code points.
41 *
42 * As an aside, this means that when encoding a UtfString to a Utf16
43 * encoding that matches your archetecture this operation will be very
44 * fast since it will effectively be a raw dump of the internal data
45 * structures. However, it is highly reccomended that you DO NOT use the
46 * little endian encodings if you can possibly avoid it. They are not
47 * reccomended by the Unicode Consortium and are mainly supported as a
48 * means of communicating with other systems that encode their data
49 * incorrectly. That said, whenever UtfString encodes the contained string
50 * it always includes a BOM at the begining (the byte order marker) so that
51 * proper byte order can be easily determined by the program reading the
52 * data.
53 *
54 *@todo Investigate http://www.unicode.org/reports/tr6/ for compression.
55 */
56 class UtfString
57 {
58 public:
59 enum Encoding
60 {
61 Utf8,
62 Utf16,
63 Utf16be,
64 Utf16le,
65 Utf32,
66 Utf32be,
67 Utf32le,
68 Ucs2,
69 Ucs4,
70 GuessEncoding
71 };
72
73 UtfString();
74 UtfString( const Bu::String &sInput, Encoding eEnc=Utf8 );
75 virtual ~UtfString();
76
77 class iterator
78 {
79 private:
80 iterator( UtfString *pSrc, int iCodePos ) :
81 pSrc( pSrc ), iCodePos( iCodePos )
82 {
83 }
84
85 public:
86 iterator() :
87 pSrc( NULL ), iCodePos( 0 )
88 {
89 }
90
91 UtfChar operator*()
92 {
93 if( !pSrc )
94 throw Bu::ExceptionBase("invalid UtfString::iterator dereferenced.");
95 return pSrc->nextChar( iCodePos );
96 }
97
98 private:
99 UtfString *pSrc;
100 int iCodePos;
101 };
102
103 /**
104 * Append a UtfChar (A unicode code point) to the string. This can be
105 * any valid code point, and is just the value of the code point, no
106 * encoding necessary.
107 */
108 void append( UtfChar ch );
109
110 /**
111 * Set the value of the entire string based on the given input and
112 * encoding. The default encoding is Utf8, which is compatible with
113 * 7-bit ascii, so it's a great choice for setting UtfStrings from
114 * string literals in code.
115 */
116 void set( const Bu::String &sInput, Encoding eEnc=Utf8 );
117
118 /**
119 * This encodes the UtfString in the given encoding and outputs it to
120 * the provided stream. all Utf16 and Utf32 encodings will have the
121 * correct BOM (byte order marker) at the begining.
122 */
123 void write( Bu::Stream &sOut, Encoding eEnc=Utf8 );
124
125 /**
126 * This encodes the UtfString in the given encoding and returns it as
127 * a binary Bu::String. Like write, this also includes the proper BOM
128 * at the begining.
129 */
130 Bu::String get( Encoding eEnc=Utf8 );
131
132 void debug();
133
134 /**
135 * This may or may not stick around, given an index, this returns a
136 * codepoint, however there isn't necesarilly a 1:1 ratio between
137 * indexes and code points.
138 */
139 UtfChar get( int iIndex );
140
141 /**
142 * This is what to use if you want to iterate through a section of the
143 * UtfString and you want to use a numerical index. In most cases it
144 * will be much easier to use an iterator, though. Given an index this
145 * will return the codepoint at that position and increment iIndex an
146 * appropriate amount for it to point to the next code point.
147 */
148 UtfChar nextChar( int &iIndex );
149
150 private:
151 void append16( uint16_t i ) { aData.append( i ); }
152
153 void setUtf8( const Bu::String &sInput );
154 void setUtf16( const Bu::String &sInput );
155 void setUtf16be( const Bu::String &sInput );
156 void setUtf16le( const Bu::String &sInput );
157 void setUtf32( const Bu::String &sInput );
158 void setUtf32be( const Bu::String &sInput );
159 void setUtf32le( const Bu::String &sInput );
160
161 void writeUtf8( Bu::Stream &sOut );
162 void writeUtf16be( Bu::Stream &sOut );
163 void writeUtf16le( Bu::Stream &sOut );
164 void writeUtf32be( Bu::Stream &sOut );
165 void writeUtf32le( Bu::Stream &sOut );
166
167 private:
168 Bu::Array<uint16_t> aData;
169 int iRawLen;
170 int iCharLen;
171 };
172};
173
174#endif
diff --git a/src/unstable/uuid.cpp b/src/unstable/uuid.cpp
new file mode 100644
index 0000000..088450b
--- /dev/null
+++ b/src/unstable/uuid.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 "bu/uuid.h"
9#include "bu/file.h"
10#include "bu/formatter.h"
11#include "bu/membuf.h"
12#include <string.h>
13
14Bu::Uuid::Uuid()
15{
16 clear();
17}
18
19Bu::Uuid::Uuid( const Uuid &src )
20{
21 memcpy( data, src.data, 16 );
22}
23
24Bu::Uuid::Uuid( const Bu::String &sSrc )
25{
26 if( sSrc.getSize() == 16 )
27 {
28 memcpy( data, sSrc.getStr(), 16 );
29 }
30 else if( sSrc.getSize() == 36 )
31 {
32 // Parse it
33 set( sSrc );
34 }
35}
36
37Bu::Uuid::~Uuid()
38{
39}
40
41Bu::String Bu::Uuid::toRawString() const
42{
43 return Bu::String( (char *)data, 16 );
44}
45
46Bu::String Bu::Uuid::toString() const
47{
48 Bu::MemBuf mb;
49 Bu::Formatter f( mb );
50
51 for( int j = 0; j < 16; j++ )
52 {
53 if( j == 4 || j == 6 || j == 8 || j == 10 )
54 f << '-';
55 f << Bu::Fmt::hex(2).caps(false) << (unsigned int)data[j];
56 }
57
58 return mb.getString();
59}
60
61Bu::String Bu::Uuid::toUrn() const
62{
63 return "urn:uuid:" + toString();
64}
65
66int Bu::Uuid::getVersion()
67{
68 return (data[6]&((8|4|2|1)<<4))>>4;
69}
70
71#define msb( i ) (1<<(7-i))
72
73void Bu::Uuid::clear()
74{
75 data[7] = msb(0);
76}
77
78Bu::Uuid Bu::Uuid::gen()
79{
80 Bu::File fIn( "/proc/sys/kernel/random/uuid", Bu::File::Read );
81 char dat[36];
82 fIn.read( dat, 36 );
83 Uuid id;
84 id.set( dat );
85 return id;
86}
87
88void Bu::Uuid::set( const Bu::String &sSrc )
89{
90 const char *dat = sSrc.getStr();
91 int iNibble = 0;
92 memset( data, 0, 16 );
93 for( int j = 0; j < 36; j++ )
94 {
95 if( dat[j] == '-' )
96 continue;
97 unsigned char c = (dat[j]>='0'&&dat[j]<='9')?(dat[j]-'0'):(dat[j]-'a'+10);
98 data[iNibble/2] |= (iNibble%2==0)?(c<<4):(c);
99 iNibble++;
100 }
101}
102
103bool Bu::Uuid::operator==( const Uuid &rhs ) const
104{
105 return memcmp( data, rhs.data, 16 ) == 0;
106}
107
108template<> uint32_t Bu::__calcHashCode<Bu::Uuid>( const Bu::Uuid &k )
109{
110 return __calcHashCode<String>( k.toRawString() );
111}
112
113template<> bool Bu::__cmpHashKeys<Bu::Uuid>( const Bu::Uuid &a, const Bu::Uuid &b )
114{
115 return a == b;
116}
117
diff --git a/src/unstable/uuid.h b/src/unstable/uuid.h
new file mode 100644
index 0000000..261f653
--- /dev/null
+++ b/src/unstable/uuid.h
@@ -0,0 +1,56 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file 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_UUID_H
9#define BU_UUID_H
10
11#include "bu/string.h"
12
13namespace Bu
14{
15 class Uuid
16 {
17 public:
18 Uuid();
19 Uuid( const Uuid &src );
20 Uuid( const Bu::String &sSrc );
21 virtual ~Uuid();
22
23 Bu::String toRawString() const;
24 Bu::String toString() const;
25 Bu::String toUrn() const;
26
27 int getVersion();
28
29 static Uuid gen();
30 static Uuid genV1();
31 static Uuid genV2();
32 static Uuid genV3();
33 static Uuid genV4();
34 static Uuid genV5();
35
36 void clear();
37
38 bool operator==( const Uuid &rhs ) const;
39
40 private:
41 void set( const Bu::String &sSrc );
42 unsigned char data[16];
43 };
44
45 template<typename T>
46 uint32_t __calcHashCode( const T &k );
47
48 template<typename T>
49 bool __cmpHashKeys( const T &a, const T &b );
50
51 template<> uint32_t __calcHashCode<Uuid>( const Uuid &k );
52 template<> bool __cmpHashKeys<Uuid>(
53 const Uuid &a, const Uuid &b );
54};
55
56#endif