ultimatepp/uppdev/Queue/Patrik.cpp
cxl 945cda646f SDraw radial gradients
git-svn-id: svn://ultimatepp.org/upp/trunk@736 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2009-01-09 16:40:06 +00:00

144 lines
3.5 KiB
C++

// Solution to our pre-interview test.
#include <stdlib.h>
#include <stdio.h>
// Macros I can't do without anymore.
#define hope(x) while ( ! (x) ) { onIllegalOperation() ; }
#define require(x) typedef char require[ (x) ? 1 : -1 ] ;
// Error handlers.
#define onOutOfMemory() do{ fprintf( stderr, "Out of memory!\n" ) ; exit( EXIT_FAILURE ) ; } while( 0 )
#define onIllegalOperation() do{ fprintf( stderr, "Illegal Operation!\n" ) ; exit( EXIT_FAILURE ) ; } while( 0 )
// Typedefs.
typedef unsigned char byte ;
typedef unsigned int uint ;
struct Q {
uint first ;
uint last ;
} ;
// Constants.
const uint BLOCK_SIZE = 8 ;
const uint BLOCK_MASK = BLOCK_SIZE - 1 ;
const uint BLOCK_LAST = BLOCK_SIZE - 1 ;
const uint TOTAL_SIZE = 2048 ;
const uint BLOCK_COUNT = TOTAL_SIZE / BLOCK_SIZE ;
// The provided memory, and appropriate aliasing.
//
// Everything is split to 256 8byte blocks.
//
// Blocks 1-255 are used for both queues and data, block 0 for book-keeping of free blocks.
// data[ 0 ] maintains single linked list of free blocks, while
// data[ 1 ] keeps the index of the last block ever allocated.
// Both used and free blocks are linked via their last byte.
//
// Note that array requires alignment to uint on some architectures,
// which is simple to achieve with "aligned" attribute if necessary.
// The alternative would be to use aligned memory within it ourselves.
byte data[ TOTAL_SIZE ] ;
#define queues ((Q *)(data))
require( sizeof( Q ) == BLOCK_SIZE ) ;
// Block management.
static byte allocate_block( void )
{
// Use one of the freed blocks, if possible.
byte block = data[ 0 ] ;
if ( block ) {
data[ 0 ] = data[ block * BLOCK_SIZE + BLOCK_LAST ] ;
return block ;
}
// Otherwise grab new block from the array, if possible.
//
// We can use an arguable trick that it will overflow in case we run out of blocks.
// This may be considered either cool or entirely intorelable, depending on the point of view.
require( BLOCK_COUNT == 256 ) ;
block = ++data[ 1 ] ;
if ( block ) {
return block ;
}
// Well, bad luck.
onOutOfMemory() ;
}
static void free_block( const byte block )
{
hope( block > 0 ) ;
hope( block < BLOCK_COUNT ) ;
data[ block * BLOCK_SIZE + BLOCK_LAST ] = data[ 0 ] ;
data[ 0 ] = block ;
}
// Queue methods themselves, each O(1).
Q * createQ( void )
{
Q * const q = queues + allocate_block() ;
q->first = q->last = allocate_block() * BLOCK_SIZE ;
return q ;
}
void destroyQ( Q * q )
{
hope( q ) ;
data[ q->last | BLOCK_MASK ] = data[ 0 ] ;
data[ 0 ] = q->first / BLOCK_SIZE ;
free_block( q - queues ) ;
}
void enQ( Q * q, byte n )
{
hope( q ) ;
if ( q->last & BLOCK_MASK == BLOCK_LAST ) {
const byte block = allocate_block() ;
data[ q->last ] = block ;
q->last = block * BLOCK_SIZE ;
}
data[ q->last++ ] = n ;
}
byte deQ( Q * q )
{
hope( q ) ;
if ( q->first == q->last ) {
onIllegalOperation() ;
}
if ( q->first & BLOCK_MASK == BLOCK_LAST ) {
const byte block = data[ q->first ] ;
free_block( q->first / BLOCK_SIZE ) ;
q->first = block * BLOCK_SIZE ;
}
return data[ q->first++ ] ;
}
void TestPatrik()
{
Q *q0 = createQ();
for(int i = 0; i < 3000; i++) {
DDUMP(i);
enQ(q0, 0);
}
}