ringbuff.c

Go to the documentation of this file.
00001 
00007 #include "ctl/ctldef.h"
00008 #include "ctl/debug.h"
00009 #include "ctl/ctlnew.h"
00010 #include "ctl/ringbuff.h"
00011 
00012 #ifndef CTL_UNIT
00013 
00020 void RingBuff_Init( RingBuff* self, void* buff, size_t size )
00021 {
00022     assertobjptr(self);
00023     assertptr(buff,size);
00024     self->buff = (const uint8*)buff;
00025     self->end = self->buff + size;
00026     self->head = self->tail = (uint8*)self->buff;
00027 }
00028 
00036 bool RingBuff_Space( RingBuff* self, uint8** buff, size_t count )
00037 {
00038     assertobjconst(self);
00039     assert( self->head <= self->tail );
00040     if( self->tail + count <= self->end )
00041     {
00042         *buff = (uint8*)self->tail;
00043         return true;
00044     }
00045     else if( self->head > self->buff && (count <= RingBuff_Free(self) ) )
00046     {
00047         size_t used = self->tail - self->head;
00048         memmove( (uint8*)self->buff, self->head, used );
00049         self->head = (uint8*)self->buff;
00050         self->tail = (uint8*)self->buff + used;
00051         *buff = (uint8*)self->tail;
00052         return true;
00053     }
00054     *buff = NULL;
00055     return false;
00056 }
00057 
00065 bool RingBuff_Peek( RingBuff* self, const uint8** buff, size_t count )
00066 {
00067     assertobjptr(self);
00068     assert( self->tail >= self->head );
00069     if( self->head + count > self->tail )
00070     {   /* Not enough data left */
00071         return false;
00072     }
00073     *buff = self->head;
00074     return true;
00075 }
00076 
00083 bool RingBuff_Consume( RingBuff* self, size_t count )
00084 {
00085     assertobjptr(self);
00086     assert( self->tail >= self->head );
00087     if( self->head + count > self->tail )
00088     {
00089         return false;
00090     }
00091     self->head += count;
00092     if( self->head == self->tail )
00093     {   // Reset if we use up the data
00094         RingBuff_Reset(self);
00095     }
00096     return true;
00097 }
00098 
00105 bool RingBuff_Add( RingBuff* self, uint8** buff, size_t count )
00106 {
00107     if( RingBuff_Space( self, buff, count ) )
00108     {   // Consume free space
00109         self->tail += count;
00110         return true;
00111     }
00112     return false;
00113 }
00114 
00121 bool RingBuff_Pop( RingBuff* self, const uint8** buff, size_t count )
00122 {
00123     if( RingBuff_Peek( self, buff, count ) )
00124     {   // Consume data
00125         self->head += count;
00126         if( self->head == self->tail )
00127         {   // Reset if we use up the data
00128             RingBuff_Reset(self);
00129         }
00130         return true;
00131     }
00132     /* Not enough data left */
00133     return false;
00134 }
00135 
00143 bool RingBuff_CopyTo( RingBuff* self, const void* buff, size_t count )
00144 {
00145     uint8* space;
00146     if( RingBuff_Space( self, &space, count ) )
00147     {   // Consume free space
00148         memcpy(space,buff,count);
00149         self->tail += count;
00150         return true;
00151     }
00152     return false;
00153 }
00154 
00162 bool RingBuff_CopyFrom( RingBuff* self, void* buff, size_t count )
00163 {
00164     const uint8* data;
00165     if( RingBuff_Peek( self, &data, count ) )
00166     {   // Consume data
00167         memcpy(buff,data,count);
00168         self->head += count;
00169         return true;
00170     }
00171     return false;
00172 }
00173 
00182 bool RingBuff_AddSerial( RingBuff* self, ctl_serial* serial, size_t reserve )
00183 {
00184     uint8* space;
00185     uint16 size = 2 + reserve;
00186     if( RingBuff_Add( self, &space, size ) )
00187     {
00188         ctl_serial_init( serial, space, space + size );
00189         uint16_serial_write(&size, serial );
00190         return true;
00191     }
00192     return false;
00193 }
00194 
00202 bool RingBuff_PeekSerial( RingBuff* self, ctl_serial* serial )
00203 {
00204     const uint8* data;
00205     if( RingBuff_Peek( self, &data, 2 ) )
00206     {
00207         ctl_serial_init( serial, (uint8*)data, data+2 );
00208         uint16 size;
00209         uint16_serial_read(&size, serial );
00210         ctl_serial_init( serial, data, data + size );
00211         return true;
00212     }
00213     return false;
00214 }
00215 
00223 bool RingBuff_PopSerial( RingBuff* self, ctl_serial* serial )
00224 {
00225     const uint8* data;
00226     if( RingBuff_Peek( self, &data, 2 ) )
00227     {
00228         uint16 size;
00229         ctl_serial_init( serial, data, data+2 );
00230         uint16_serial_read(&size, serial );
00231         /* Pop the packet off, once it's available */
00232         if( RingBuff_Pop( self, &data, size ) )
00233         {
00234             ctl_serial_init( serial, data, data + size );
00235             /* Parse past length; it can be had by rewinding, but in most cases app won't want it. */
00236             serial->curr += 2;
00237             return true;
00238         }
00239     }
00240     return false;
00241 }
00242 
00243 
00244 #else /* CTL_UNIT */
00245 
00246 #include "unit/unit.h"
00247 
00248 void Test_RingBuff1(void);
00249 void Test_RingBuff2(void);
00250 void Test_RingBuff3(void);
00251 void Test_RingBuff4(void);
00252 void Test_RingBuff5(void);
00253 
00254 void Test_RingBuff(void)
00255 {
00256     /* Don't spew stuff on screen */
00257     Test_RingBuff1();
00258     Test_RingBuff2();
00259     Test_RingBuff3();
00260     Test_RingBuff4();
00261     Test_RingBuff5();
00262 }
00263 
00264 /*
00265  * Do basic unit testing on ring buffer
00266  */
00267 void Test_RingBuff1(void)
00268 {
00269     RingBuff rb;
00270     uint8 buffer[16];
00271 #define RINGBUFF_AVAIL  sizeof(buffer)
00272     const uint8* read;
00273     uint8* write;
00274     uint8* scratch = buffer;
00275     RingBuff_Init(&rb,buffer,sizeof(buffer));
00276 
00277     /* Work through the macros - empty, unused buffer */
00278     Test_Error( RINGBUFF_AVAIL == RingBuff_Total(&rb) );
00279     Test_Error( RINGBUFF_AVAIL == RingBuff_Free(&rb) );
00280     Test_Error( 0 == RingBuff_Remain(&rb) );
00281 
00282     /* Now add some data */
00283     Test_Error( RingBuff_Add( &rb, &write, 2 ) );
00284     Test_Error( scratch == buffer );
00285     Test_Error( buffer == write );
00286     memcpy(write,"HI",2);
00287     Test_Error( RINGBUFF_AVAIL-2 == RingBuff_Free(&rb) );
00288     Test_Error( 2 == RingBuff_Remain(&rb) );
00289 
00290     /* See that it makes an error. */
00291     Test_Error( !RingBuff_Peek( &rb, &read, 3 ) );
00292 
00293     /* Now pop that data */
00294     Test_Error( RingBuff_Peek( &rb, &read, 2 ) );
00295     Test_Error( 0 == memcmp(read,"HI",2) );
00296     Test_Error( RingBuff_Pop( &rb, &read, 2 ) );
00297     Test_Error( 0 == memcmp(read,"HI",2) );
00298     Test_Error( RINGBUFF_AVAIL == RingBuff_Free(&rb) );
00299     Test_Error( 0 == RingBuff_Remain(&rb) );
00300 
00301     /* Now add some more data */
00302     Test_Error( RingBuff_Add( &rb, &write, 5 ) );
00303     memcpy(write,"12345",5);
00304     Test_Error( RINGBUFF_AVAIL-5 == RingBuff_Free(&rb) );
00305     Test_Error( 5 == RingBuff_Remain(&rb) );
00306     Test_Error( RingBuff_Add( &rb, &write, 5 ) );
00307     memcpy(write,"ABCDE",5);
00308     Test_Error( RINGBUFF_AVAIL-10 == RingBuff_Free(&rb) );
00309     Test_Error( 10 == RingBuff_Remain(&rb) );
00310     
00311     /* Cause exception; ensure it's still kosher */
00312     Test_Error( !RingBuff_Add( &rb, &write, 7 ) );
00313     Test_Error( NULL == write );
00314     Test_Error( RINGBUFF_AVAIL-10 == RingBuff_Free(&rb) );
00315     Test_Error( 10 == RingBuff_Remain(&rb) );
00316     
00317     /* Fill it up */
00318     Test_Error( RingBuff_Add( &rb, &write, 6 ) );
00319     memcpy(write,"654321",6);
00320     Test_Error( 0 == RingBuff_Free(&rb) );
00321     Test_Error( 16 == RingBuff_Remain(&rb) );
00322     
00323     /* Pop initial block off */
00324     Test_Error( RingBuff_Pop( &rb, &read, 5 ) );
00325     Test_Error( 0 == memcmp(read,"12345",5) );
00326     Test_Error( RINGBUFF_AVAIL-11 == RingBuff_Free(&rb) );
00327     Test_Error( 11 == RingBuff_Remain(&rb) );
00328 
00329     /* See that we can't read more than there is */
00330     Test_Error( !RingBuff_Peek( &rb, &read, 12 ) );
00331     Test_Error( !RingBuff_Pop( &rb, &read, 12 ) );
00332     
00333     /* Force data to move around and add data and fill it back up */
00334     Test_Error( RingBuff_Add( &rb, &write, 5 ) );
00335     memcpy(write,"ZYXWV",5);
00336     Test_Error( 0 == RingBuff_Free(&rb) );
00337     Test_Error( RINGBUFF_AVAIL == RingBuff_Remain(&rb) );
00338 
00339     /* Pop second block off */
00340     Test_Error( RingBuff_Pop( &rb, &read, 5 ) );
00341     Test_Error( 0 == memcmp(read,"ABCDE",5) );
00342     Test_Error( 5 == RingBuff_Free(&rb) );
00343     Test_Error( 11 == RingBuff_Remain(&rb) );
00344 
00345     /* Cause exception */
00346     Test_Error( !RingBuff_Add( &rb, &write, 11 ) );
00347     Test_Error( 5 == RingBuff_Free(&rb) );
00348     Test_Error( 11 == RingBuff_Remain(&rb) );
00349 
00350     /* Fill it again */
00351     Test_Error( RingBuff_Add( &rb, &write, 5 ) );
00352     memcpy(write,"!@#$%^",5);
00353     Test_Error( 0 == RingBuff_Free(&rb) );
00354     Test_Error( 16 == RingBuff_Remain(&rb) );
00355 
00356     /* Pop everything off and see that it wraps */
00357     RingBuff_Pop( &rb, &read, 10 );
00358     Test_Error( 0 == memcmp(read,"654321ZYXWV",10) );
00359     Test_Error( 6 == RingBuff_Remain(&rb) );
00360     Test_Error( 10 == RingBuff_Free(&rb) );
00361     RingBuff_Add( &rb, &write, 3 );
00362     memcpy(write,"123",3);
00363     Test_Error( 7 == RingBuff_Free(&rb) );
00364     Test_Error( 9 == RingBuff_Remain(&rb) );
00365 
00366 }
00367 
00368 /*
00369  * Do extended testing on ring buffer
00370  * Feeds contents of one to another, expects both to remain in sync.
00371  */
00372 void Test_RingBuff2(void)
00373 {
00374     RingBuff rb1;
00375     uint8 buffer1[64];
00376     RingBuff rb2;
00377     uint8 buffer2[64];
00378     RingBuff_Init(&rb1,buffer1,sizeof(buffer1));
00379     RingBuff_Init(&rb2,buffer2,sizeof(buffer2));
00380 
00381     /*
00382      * Fill first ring buffer with junk
00383      */
00384     while( 3 < RingBuff_Free(&rb1) )
00385     {
00386         uint8* p;
00387         Test_Error( RingBuff_Add( &rb1, &p, 1 ) );
00388         *p = uint8_rand();
00389     }
00390     Test_Error( 3 == RingBuff_Free(&rb1) );
00391     Test_Error( 61 == RingBuff_Remain(&rb1) );
00392     {
00393         uint8* write;
00394         const uint8* read;
00395         Test_Error( RingBuff_Add( &rb2, &write, 5 ) );
00396         Test_Error( RingBuff_CopyFrom( &rb1, write, 5 ) );
00397         Test_Error( 8 == RingBuff_Free(&rb1) );
00398         Test_Error( 56 == RingBuff_Remain(&rb1) );
00399         Test_Error( 59 == RingBuff_Free(&rb2) );
00400         Test_Error( 5 == RingBuff_Remain(&rb2) );
00401         
00402         Test_Error(RingBuff_Pop( &rb2, &read, 5 ));
00403         Test_Error( RingBuff_CopyTo( &rb1, read, 5 ) );
00404         /* 3 would be available, but head==tail = 0 bytes */
00405         Test_Error( 3 == RingBuff_Free(&rb1) );
00406         Test_Error( 61 == RingBuff_Remain(&rb1) );
00407         Test_Error( RingBuff_IsEmpty( &rb2 ) );
00408     }
00409 }
00410 
00411 struct test
00412 {
00413     int32 a, b, c;
00414 };
00415 /*
00416  * Do serialization testing
00417  */
00418 void Test_RingBuff3(void)
00419 {
00420     struct test test1, test2;
00421     RingBuff rb;
00422     uint8 buffer[64];
00423     ctl_serial serial;
00424     RingBuff_Init(&rb,buffer,sizeof(buffer));
00425     test1.a = rand();
00426     test1.b = rand();
00427     test1.c = rand();
00428     Test_Error( RingBuff_AddSerial( &rb, &serial, 12 ) );
00429     int32_serial_write( &test1.a, &serial );
00430     int32_serial_write( &test1.b, &serial );
00431     int32_serial_write( &test1.c, &serial );
00432     Test_Error( RingBuff_PopSerial( &rb, &serial ) );
00433     int32_serial_read( &test2.a, &serial );
00434     int32_serial_read( &test2.b, &serial );
00435     int32_serial_read( &test2.c, &serial );
00436     Test_Error( test1.a == test2.a && test1.b == test2.b && test1.c == test2.c );
00437 }
00438 
00439 #define RAND_TEST_ITERATIONS 1000
00440 
00441 /*
00442  * Throw data into it and take data out of it randomly lots of times
00443  * Attempt to brute-force break it.
00444  * Uses sequential bytes and checks for non-sequential values
00445  */
00446 void Test_RingBuff4(void)
00447 {
00448     int times = RAND_TEST_ITERATIONS;
00449     RingBuff rb;
00450     uint8 buffer[64];
00451     uint8 seq = 0;
00452     uint8 seqpop = 0;
00453     RingBuff_Init(&rb,buffer,sizeof(buffer));
00454     while( times-- )
00455     {
00456         size_t size = RingBuff_Free( &rb );
00457         size_t todo = size * rand() / RAND_MAX;
00458         const uint8* popped;
00459         uint8* pcurr;
00460         if( !RingBuff_Add( &rb, &pcurr, todo ) )
00461         {
00462             size = RingBuff_Free( &rb );
00463             todo = size * rand() / RAND_MAX;
00464             Test_Error(RingBuff_Add( &rb, &pcurr, todo ));
00465         }
00466         while( todo-- )
00467         {
00468             *pcurr++ = seq++;
00469         }
00470         size = RingBuff_Remain( &rb );
00471         todo = size * rand() / RAND_MAX;
00472         Test_Error(RingBuff_Pop( &rb, &popped, todo ));
00473         while( todo-- )
00474         {
00475             Test_Error( *popped++ == seqpop++ );
00476         }
00477     }
00478 }
00479 
00480 /*
00481  * Throw data into it by packet and take data out of it randomly lots of times
00482  * Attempt to brute-force break it.
00483  * Uses sequential bytes and checks for non-sequential values
00484  */
00485 void Test_RingBuff5(void)
00486 {
00487     int times = RAND_TEST_ITERATIONS;
00488     RingBuff rb;
00489     uint8 buffer[64];
00490     uint8 seq = 0;
00491     uint8 seqpop = 0;
00492     RingBuff_Init(&rb,buffer,sizeof(buffer));
00493     while( times-- )
00494     {
00495         size_t size = RingBuff_Free( &rb )-2;
00496         size_t todo = size * rand() / RAND_MAX;
00497         ctl_serial serial;
00498         Test_Error(RingBuff_AddSerial( &rb, &serial, todo ));
00499         while( serial.curr < serial.end )
00500         {
00501             *serial.curr++ = seq++;
00502         }
00503         Test_Error(RingBuff_PopSerial( &rb, &serial ));
00504         while( serial.curr < serial.end )
00505         {
00506             Test_Error( *serial.curr++ == seqpop++ );
00507         }
00508     }
00509 }
00510 
00511 
00512 #endif /* CTL_UNIT */
00513 

Generated on Fri Jan 2 15:28:34 2009 for Squat by  doxygen 1.5.6