This commit is contained in:
Mark Qvist 2014-04-03 22:21:37 +02:00
commit c898b090dd
1049 changed files with 288572 additions and 0 deletions

269
bertos/struct/bitarray.h Normal file
View file

@ -0,0 +1,269 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
* -->
*
* \brief Bitarray module
*
* \author Daniele Basile <asterix@develer.com>
*
* $WIZ$ module_name = "bitarray"
*/
#ifndef STRUCT_BITARRAY_H
#define STRUCT_BITARRAY_H
#include <cfg/compiler.h>
#include <cfg/macros.h>
#include <cfg/debug.h>
#include <cpu/types.h>
typedef struct BitArray
{
size_t size; /// Size in bytes of the bitarray
size_t bitarray_len; /// Number of bits used
uint8_t *array; /// Pointer to memory occupied by the bitarray
} BitArray;
/**
* Convenience macro to create a memory area for the BitArray.
* \param name Name of the variable.
* \param size Number of bits requested. It will be rounded to the nearest
* byte
*/
#define BITARRAY_ALLOC(name, size) uint8_t name[DIV_ROUNDUP((size), 8)]
/**
* Set one bit into the bit array.
* \param bitx BitArray context
* \param idx The bit to set
*/
INLINE void bitarray_set(BitArray *bitx, int idx)
{
ASSERT((size_t)idx <= bitx->bitarray_len);
int page = idx / 8;
uint8_t bit = idx % 8;
bitx->array[page] |= BV(bit);
}
/**
* Clear one bit in the bit array.
* \param bitx BitArray context
* \param idx The bit to clear
*/
INLINE void bitarray_clear(BitArray *bitx, int idx)
{
ASSERT((size_t)idx <= bitx->bitarray_len);
int page = idx / 8;
uint8_t bit = idx % 8;
bitx->array[page] &= ~BV(bit);
}
/**
* Set a range of bits.
*
* The range starts from \a idx (inclusive) and spans \a offset bits.
*
* \param bitx BitArray context
* \param idx Starting bit
* \param offset Number of bit to set
*/
INLINE void bitarray_setRange(BitArray *bitx, int idx, int offset)
{
ASSERT((size_t)idx <= bitx->bitarray_len);
for (int i = idx; i < offset + idx; i++)
bitarray_set(bitx, i);
}
/**
* Clear a range of bits.
*
* The range starts from \a idx (inclusive) and spans \a offset bits.
*
* \param bitx BitArray context
* \param idx Starting bit
* \param offset Number of bits to clear
*/
INLINE void bitarray_clearRange(BitArray *bitx, int idx, int offset)
{
ASSERT((size_t)idx <= bitx->bitarray_len);
for (int i = idx; i < offset + idx; i++)
bitarray_clear(bitx, i);
}
/**
* Test a bit.
*
* \param bitx BitArray context
* \param idx Bit to test
* \return True if bit is set, false otherwise.
*/
INLINE bool bitarray_test(BitArray *bitx, int idx)
{
ASSERT((size_t)idx <= bitx->bitarray_len);
int page = idx / 8;
uint8_t bit = idx % 8;
return (bitx->array[page] & BV(bit));
}
/**
* Check if the bitarray is full
*
* Only \a bitarray_len bits are tested.
*
* \param bitx BitArray to test
* \return True if \a bitx is full, false otherwise
*/
INLINE bool bitarray_isFull(BitArray *bitx)
{
// test full bytes except the last one
for (size_t page = 0; page <= bitx->size - 2; page++)
{
if (!(bitx->array[page] == 0xff))
return 0;
}
// test the last byte using the correct bitmask
uint8_t mask = BV(bitx->bitarray_len >> 3) - 1;
if (!(bitx->array[bitx->size - 1] & mask))
return 0;
return 1;
}
/*
* Ugly!.. reformat it.
*/
/**
* Test if a range of bit is full.
*
* \param bitx BitArray context
* \param idx Starting bit
* \param offset Number of bits to test
* \return True if range is full, false otherwise
*/
INLINE bool bitarray_isRangeFull(BitArray *bitx, int idx, int offset)
{
ASSERT((size_t)(idx + offset) <= bitx->bitarray_len);
for (int i = idx; i <= idx + offset; i++)
if (!bitarray_test(bitx, i))
return 0;
return 1;
}
/*
* Ugly!.. reformat it.
*/
/**
* Test if a range of bit is empty.
*
* \param bitx BitArray context
* \param idx Starting bit
* \param offset Number of bits to test
* \return True if range is empty, false otherwise
*/
INLINE bool bitarray_isRangeEmpty(BitArray *bitx, int idx, int offset)
{
ASSERT((size_t)(idx + offset) <= bitx->bitarray_len);
for (int i = idx; i <= idx + offset; i++)
if (bitarray_test(bitx, i))
return 0;
return 1;
}
/**
* Print on debug serial a BitArray.
* \note This module does not use the logging module, so you
* can't decide the logging level.
* \param bitx BitArray to be printed.
*/
INLINE void bitarray_dump(BitArray *bitx)
{
kprintf("bitarray size[%zu]bits on [%zu]bytes\n", bitx->bitarray_len, bitx->size);
int i = 0;
int j = 0;
int count = bitx->bitarray_len;
while (count--)
{
kprintf("%d", bitarray_test(bitx, i++));
if (j == 7)
{
kprintf("..%02x [%d]\n", bitx->array[(i / 8) - 1], i);
j = 0;
continue;
}
j++;
}
if (j != 0)
kprintf("..%02x [%d]\n", bitx->array[i / 8], i);
}
/**
* Init a BitArray.
*
* The BitArray uses an external array for storage. You can use the macro
* BITARRAY_ALLOC to declare an appropriate memory size. Example usage:
* \code
* BITARRAY_ALLOC(bits_mem, 17);
* BitArray bits;
* bitarray_init(&bits, 17, bits_mem, sizeof(bits_mem))
* \endcode
*
* \param bitx BitArray context
* \param bitarray_len Number of bits in the BitArray
* \param array Memory area for the BitArray
* \param size Size (in bytes) of the memory area \a array
*/
INLINE void bitarray_init(BitArray *bitx, size_t bitarray_len, uint8_t *array, size_t size)
{
bitx->size = size;
bitx->array = array;
bitx->bitarray_len = bitarray_len;
}
int bitarray_testSetup(void);
int bitarray_testRun(void);
int bitarray_testTearDown(void);
#endif /* STRUCT_BITARRAY_H */

View file

@ -0,0 +1,132 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Bitarray test
*
* \author Daniele Basile <asterix@develer.com>
*/
#include <struct/bitarray.h>
#include <cfg/compiler.h>
#include <cfg/test.h>
#include <cfg/debug.h>
#include <string.h>
#define TEST1_LEN 31
#define TEST2_LEN 17
BITARRAY_ALLOC(test1, TEST1_LEN);
BITARRAY_ALLOC(test2, TEST2_LEN);
BitArray bitx1;
BitArray bitx2;
int bitarray_testSetup(void)
{
kdbg_init();
bitarray_init(&bitx1, TEST1_LEN, test1, sizeof(test1));
bitarray_init(&bitx2, TEST2_LEN, test2, sizeof(test2));
return 0;
}
int bitarray_testRun(void)
{
memset(test1, 0xaa, sizeof(test1));
bitarray_dump(&bitx1);
for (size_t i = 0; i < TEST1_LEN; i++)
{
if (!((bool)(i % 2) == bitarray_test(&bitx1,i)))
goto error;
}
memset(test1, 0, sizeof(test1));
for (size_t i = 0; i < TEST1_LEN; i++)
{
if ((i % 2) == 0)
bitarray_clear(&bitx1,i);
else
bitarray_set(&bitx1, i);
}
bitarray_dump(&bitx1);
for (size_t i = 0; i < TEST1_LEN; i++)
{
if (!((bool)(i % 2) == bitarray_test(&bitx1, i)))
goto error;
}
memset(test1, 0, sizeof(test1));
bitarray_set(&bitx1, 0);
bitarray_dump(&bitx1);
if (!bitarray_test(&bitx1, 0))
goto error;
memset(test1, 0, sizeof(test1));
bitarray_set(&bitx1, TEST1_LEN);
bitarray_dump(&bitx1);
if (!bitarray_test(&bitx1, TEST1_LEN))
goto error;
kprintf("Test 2\n");
memset(test2, 0xFF, sizeof(test2));
bitarray_dump(&bitx2);
if (!bitarray_isFull(&bitx2))
goto error;
memset(test2, 0xFF, sizeof(test2));
bitarray_clear(&bitx2, 5);
bitarray_dump(&bitx2);
if (bitarray_isFull(&bitx2))
goto error;
memset(test2, 0xFF, sizeof(test2));
bitarray_clear(&bitx2, 13);
bitarray_dump(&bitx2);
if (bitarray_isFull(&bitx2))
goto error;
return 0;
error:
kprintf("Error!\n");
return -1;
}
int bitarray_testTearDown(void)
{
return 0;
}
TEST_MAIN(bitarray);

363
bertos/struct/fifobuf.h Normal file
View file

@ -0,0 +1,363 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
* Copyright 2001, 2008 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \defgroup fifobuf FIFO buffer
* \ingroup struct
* \{
*
* \brief General pourpose FIFO buffer implemented with a ring buffer
*
* \li \c begin points to the first buffer element;
* \li \c end points to the last buffer element (unlike the STL convention);
* \li \c head points to the element to be extracted next;
* \li \c tail points to the location following the last insertion;
* \li when any of the pointers advances beyond \c end, it is reset
* back to \c begin.
*
* \code
*
* +-----------------------------------+
* | empty | valid data | empty |
* +-----------------------------------+
* ^ ^ ^ ^
* begin head tail end
*
* \endcode
*
* The buffer is EMPTY when \c head and \c tail point to the same location:
* \code head == tail \endcode
*
* The buffer is FULL when \c tail points to the location immediately
* after \c head:
* \code tail == head - 1 \endcode
*
* The buffer is also FULL when \c tail points to the last buffer
* location and head points to the first one:
* \code head == begin && tail == end \endcode
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#ifndef STRUCT_FIFO_H
#define STRUCT_FIFO_H
#include <cpu/types.h>
#include <cpu/irq.h>
#include <cfg/debug.h>
typedef struct FIFOBuffer
{
unsigned char * volatile head;
unsigned char * volatile tail;
unsigned char *begin;
unsigned char *end;
} FIFOBuffer;
#define ASSERT_VALID_FIFO(fifo) \
ATOMIC( \
ASSERT((fifo)->head >= (fifo)->begin); \
ASSERT((fifo)->head <= (fifo)->end); \
ASSERT((fifo)->tail >= (fifo)->begin); \
ASSERT((fifo)->tail <= (fifo)->end); \
)
/**
* Check whether the fifo is empty
*
* \note Calling fifo_isempty() is safe while a concurrent
* execution context is calling fifo_push() or fifo_pop()
* only if the CPU can atomically update a pointer
* (which the AVR and other 8-bit processors can't do).
*
* \sa fifo_isempty_locked
*/
INLINE bool fifo_isempty(const FIFOBuffer *fb)
{
//ASSERT_VALID_FIFO(fb);
return fb->head == fb->tail;
}
/**
* Check whether the fifo is full
*
* \note Calling fifo_isfull() is safe while a concurrent
* execution context is calling fifo_pop() and the
* CPU can update a pointer atomically.
* It is NOT safe when the other context calls
* fifo_push().
* This limitation is not usually problematic in a
* consumer/producer scenario because the
* fifo_isfull() and fifo_push() are usually called
* in the producer context.
*/
INLINE bool fifo_isfull(const FIFOBuffer *fb)
{
//ASSERT_VALID_FIFO(fb);
return
((fb->head == fb->begin) && (fb->tail == fb->end))
|| (fb->tail == fb->head - 1);
}
/**
* Push a character on the fifo buffer.
*
* \note Calling \c fifo_push() on a full buffer is undefined.
* The caller must make sure the buffer has at least
* one free slot before calling this function.
*
* \note It is safe to call fifo_pop() and fifo_push() from
* concurrent contexts, unless the CPU can't update
* a pointer atomically (which the AVR and other 8-bit
* processors can't do).
*
* \sa fifo_push_locked
*/
INLINE void fifo_push(FIFOBuffer *fb, unsigned char c)
{
#ifdef __MWERKS__
#pragma interrupt called
#endif
//ASSERT_VALID_FIFO(fb);
/* Write at tail position */
*(fb->tail) = c;
if (UNLIKELY(fb->tail == fb->end))
/* wrap tail around */
fb->tail = fb->begin;
else
/* Move tail forward */
fb->tail++;
}
/**
* Pop a character from the fifo buffer.
*
* \note Calling \c fifo_pop() on an empty buffer is undefined.
* The caller must make sure the buffer contains at least
* one character before calling this function.
*
* \note It is safe to call fifo_pop() and fifo_push() from
* concurrent contexts.
*/
INLINE unsigned char fifo_pop(FIFOBuffer *fb)
{
#ifdef __MWERKS__
#pragma interrupt called
#endif
//ASSERT_VALID_FIFO(fb);
if (UNLIKELY(fb->head == fb->end))
{
/* wrap head around */
fb->head = fb->begin;
return *(fb->end);
}
else
/* move head forward */
return *(fb->head++);
}
/**
* Make the fifo empty, discarding all its current contents.
*/
INLINE void fifo_flush(FIFOBuffer *fb)
{
//ASSERT_VALID_FIFO(fb);
fb->head = fb->tail;
}
#if CPU_REG_BITS >= CPU_BITS_PER_PTR
/*
* 16/32bit CPUs that can update a pointer with a single write
* operation, no need to disable interrupts.
*/
#define fifo_isempty_locked(fb) fifo_isempty((fb))
#define fifo_push_locked(fb, c) fifo_push((fb), (c))
#define fifo_pop_locked(fb) fifo_pop((fb))
#define fifo_flush_locked(fb) fifo_flush((fb))
#else /* CPU_REG_BITS < CPU_BITS_PER_PTR */
/**
* Similar to fifo_isempty(), but with stronger guarantees for
* concurrent access between user and interrupt code.
*
* \note This is actually only needed for 8-bit processors.
*
* \sa fifo_isempty()
*/
INLINE bool fifo_isempty_locked(const FIFOBuffer *fb)
{
bool result;
ATOMIC(result = fifo_isempty(fb));
return result;
}
/**
* Similar to fifo_push(), but with stronger guarantees for
* concurrent access between user and interrupt code.
*
* \note This is actually only needed for 8-bit processors.
*
* \sa fifo_push()
*/
INLINE void fifo_push_locked(FIFOBuffer *fb, unsigned char c)
{
ATOMIC(fifo_push(fb, c));
}
/* Probably not really needed, but hard to prove. */
INLINE unsigned char fifo_pop_locked(FIFOBuffer *fb)
{
unsigned char c;
ATOMIC(c = fifo_pop(fb));
return c;
}
/**
* Similar to fifo_flush(), but with stronger guarantees for
* concurrent access between user and interrupt code.
*
* \note This is actually only needed for 8-bit processors.
*
* \sa fifo_flush()
*/
INLINE void fifo_flush_locked(FIFOBuffer *fb)
{
ATOMIC(fifo_flush(fb));
}
#endif /* CPU_REG_BITS < BITS_PER_PTR */
/**
* Thread safe version of fifo_isfull()
*/
INLINE bool fifo_isfull_locked(const FIFOBuffer *_fb)
{
bool result;
ATOMIC(result = fifo_isfull(_fb));
return result;
}
/**
* FIFO Initialization.
*/
INLINE void fifo_init(FIFOBuffer *fb, unsigned char *buf, size_t size)
{
/* FIFO buffers have a known bug with 1-byte buffers. */
ASSERT(size > 1);
fb->head = fb->tail = fb->begin = buf;
fb->end = buf + size - 1;
}
/**
* \return Lenght of the FIFOBuffer \a fb.
*/
INLINE size_t fifo_len(FIFOBuffer *fb)
{
return fb->end - fb->begin;
}
#if 0
/*
* UNTESTED: if uncommented, to be moved in fifobuf.c
*/
void fifo_pushblock(FIFOBuffer *fb, unsigned char *block, size_t len)
{
size_t freelen;
/* Se c'e' spazio da tail alla fine del buffer */
if (fb->tail >= fb->head)
{
freelen = fb->end - fb->tail + 1;
/* C'e' abbastanza spazio per scrivere tutto il blocco? */
if (freelen < len)
{
/* Scrivi quello che entra fino alla fine del buffer */
memcpy(fb->tail, block, freelen);
block += freelen;
len -= freelen;
fb->tail = fb->begin;
}
else
{
/* Scrivi tutto il blocco */
memcpy(fb->tail, block, len);
fb->tail += len;
return;
}
}
for(;;)
{
while (!(freelen = fb->head - fb->tail - 1))
Delay(FIFO_POLLDELAY);
/* C'e' abbastanza spazio per scrivere tutto il blocco? */
if (freelen < len)
{
/* Scrivi quello che entra fino alla fine del buffer */
memcpy(fb->tail, block, freelen);
block += freelen;
len -= freelen;
fb->tail += freelen;
}
else
{
/* Scrivi tutto il blocco */
memcpy(fb->tail, block, len);
fb->tail += len;
return;
}
}
}
#endif
/** \} */ /* defgroup fifobuf */
#endif /* STRUCT_FIFO_H */

287
bertos/struct/hashtable.c Normal file
View file

@ -0,0 +1,287 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
* Copyright 2004 Giovanni Bajo
* -->
*
* \brief Portable hash table implementation
*
* Some rationales of our choices in implementation:
*
* \li For embedded systems, it is vital to allocate the table in static memory. To do
* so, it is necessary to expose the \c HashNode and \c HashTable structures in the header file.
* Nevertheless, they should be used as opaque types (that is, the users should not
* access the structure fields directly).
*
* \li To statically allocate the structures, a macro is provided. With this macro, we
* are hiding completely \c HashNode to the user (who only manipulates \c HashTable). Without
* the macro, the user would have had to define both the \c HashNode and the \c HashTable
* manually, and pass both of them to \c ht_init() (which would have created the link between
* the two). Instead, the link is created with a literal initialization.
*
* \li The hash table is created as power of two to remove the divisions from the code.
* Of course, hash functions work at their best when the table size is a prime number.
* When calculating the modulus to convert the hash value to an index, the actual operation
* becomes a bitwise AND: this is fast, but truncates the value losing bits. Thus, the higher
* bits are first "merged" with the lower bits through some XOR operations (see the last line of
* \c calc_hash()).
*
* \li To minimize the memory occupation, there is no flag to set for the empty node. An
* empty node is recognized by its data pointer set to NULL. It is then invalid to store
* NULL as data pointer in the table.
*
* \li The visiting interface through iterators is implemented with pass-by-value semantic.
* While this is overkill for medium-to-stupid compilers, it is the best designed from an
* user point of view. Moreover, being totally inlined (defined completely in the header),
* even a stupid compiler should be able to perform basic optimizations on it.
* We thought about using a pass-by-pointer semantic but it was much more awful to use, and
* the compiler is then forced to spill everything to the stack (unless it is *very* smart).
*
* \li The current implementation allows to either store the key internally (that is, copy
* the key within the hash table) or keep it external (that is, a hook is used to extract
* the key from the data in the node). The former is more memory-hungry of course, as it
* allocated static space to store the key copies. The overhead to keep both methods at
* the same time is minimal:
* <ul>
* <li>There is a run-time check in node_get_key which is execute per each node visited.</li>
* <li>Theoretically, there is no memory overhead. In practice, there were no
* flags in \c struct HashTable till now, so we had to add a first bit flag, but the
* overhead will disappear if a second flag is added for a different reason later.</li>
* <li>There is a little interface overhead, since we have two different versions of
* \c ht_insert(), one with the key passed as parameter and one without, but in
* the common case (external keys) both can be used.</li>
* </ul>
*
* \author Giovanni Bajo <rasky@develer.com>
*/
#include "hashtable.h"
#include "cfg/cfg_hashtable.h"
#include <cfg/debug.h>
#include <cfg/compiler.h>
#include <cfg/macros.h> //ROTL(), ROTR();
#include <string.h>
typedef const void** HashNodePtr;
#define NODE_EMPTY(node) (!*(node))
#define HT_HAS_INTERNAL_KEY(ht) (CONFIG_HT_OPTIONAL_INTERNAL_KEY && ht->flags.key_internal)
/** For hash tables with internal keys, compute the pointer to the internal key for a given \a node. */
INLINE uint8_t *key_internal_get_ptr(struct HashTable *ht, HashNodePtr node)
{
uint8_t* key_buf = ht->key_data.mem;
size_t index;
// Compute the index of the node and use it to move within the whole key buffer
index = node - &ht->mem[0];
ASSERT(index < (size_t)(1 << ht->max_elts_log2));
key_buf += index * (INTERNAL_KEY_MAX_LENGTH + 1);
return key_buf;
}
INLINE void node_get_key(struct HashTable* ht, HashNodePtr node, const void** key, uint8_t* key_length)
{
if (HT_HAS_INTERNAL_KEY(ht))
{
uint8_t* k = key_internal_get_ptr(ht, node);
// Key has its length stored in the first byte
*key_length = *k++;
*key = k;
}
else
*key = ht->key_data.hook(*node, key_length);
}
INLINE bool node_key_match(struct HashTable* ht, HashNodePtr node, const void* key, uint8_t key_length)
{
const void* key2;
uint8_t key2_length;
node_get_key(ht, node, &key2, &key2_length);
return (key_length == key2_length && memcmp(key, key2, key_length) == 0);
}
static uint16_t calc_hash(const void* _key, uint8_t key_length)
{
const char* key = (const char*)_key;
uint16_t hash = key_length;
int i;
int len = (int)key_length;
for (i = 0; i < len; ++i)
hash = ROTL(hash, 4) ^ key[i];
return hash ^ (hash >> 6) ^ (hash >> 13);
}
static HashNodePtr perform_lookup(struct HashTable* ht,
const void* key, uint8_t key_length)
{
uint16_t hash = calc_hash(key, key_length);
uint16_t mask = ((1 << ht->max_elts_log2) - 1);
uint16_t index = hash & mask;
uint16_t first_index = index;
uint16_t step;
HashNodePtr node;
// Fast-path optimization: we check immediately if the current node
// is the one we were looking for, so we save the computation of the
// increment step in the common case.
node = &ht->mem[index];
if (NODE_EMPTY(node)
|| node_key_match(ht, node, key, key_length))
return node;
// Increment while going through the hash table in case of collision.
// This implements the double-hash technique: we use the higher part
// of the hash as a step increment instead of just going to the next
// element, to minimize the collisions.
// Notice that the number must be odd to be sure that the whole table
// is traversed. Actually MCD(table_size, step) must be 1, but
// table_size is always a power of 2, so we just ensure that step is
// never a multiple of 2.
step = (ROTR(hash, ht->max_elts_log2) & mask) | 1;
do
{
index += step;
index &= mask;
node = &ht->mem[index];
if (NODE_EMPTY(node)
|| node_key_match(ht, node, key, key_length))
return node;
// The check is done after the key compare. This actually causes
// one more compare in the case the table is full (since the first
// element was compared at the very start, and then at the end),
// but it makes faster the common path where we enter this loop
// for the first time, and index will not match first_index for
// sure.
} while (index != first_index);
return NULL;
}
void ht_init(struct HashTable* ht)
{
memset(ht->mem, 0, sizeof(ht->mem[0]) * (1 << ht->max_elts_log2));
}
static bool insert(struct HashTable* ht, const void* key, uint8_t key_length, const void* data)
{
HashNodePtr node;
if (!data)
return false;
if (HT_HAS_INTERNAL_KEY(ht))
key_length = MIN(key_length, (uint8_t)INTERNAL_KEY_MAX_LENGTH);
node = perform_lookup(ht, key, key_length);
if (!node)
return false;
if (HT_HAS_INTERNAL_KEY(ht))
{
uint8_t* k = key_internal_get_ptr(ht, node);
*k++ = key_length;
memcpy(k, key, key_length);
}
*node = data;
return true;
}
bool ht_insert_with_key(struct HashTable* ht, const void* key, uint8_t key_length, const void* data)
{
#ifdef _DEBUG
if (!HT_HAS_INTERNAL_KEY(ht))
{
// Construct a fake node and use it to match the key
HashNodePtr node = &data;
if (!node_key_match(ht, node, key, key_length))
{
ASSERT2(0, "parameter key is different from the external key");
return false;
}
}
#endif
return insert(ht, key, key_length, data);
}
bool ht_insert(struct HashTable* ht, const void* data)
{
const void* key;
uint8_t key_length;
#ifdef _DEBUG
if (HT_HAS_INTERNAL_KEY(ht))
{
ASSERT("parameter cannot be a hash table with internal keys - use ht_insert_with_key()"
&& 0);
return false;
}
#endif
key = ht->key_data.hook(data, &key_length);
return insert(ht, key, key_length, data);
}
const void* ht_find(struct HashTable* ht, const void* key, uint8_t key_length)
{
HashNodePtr node;
if (HT_HAS_INTERNAL_KEY(ht))
key_length = MIN(key_length, (uint8_t)INTERNAL_KEY_MAX_LENGTH);
node = perform_lookup(ht, key, key_length);
if (!node || NODE_EMPTY(node))
return NULL;
return *node;
}

301
bertos/struct/hashtable.h Normal file
View file

@ -0,0 +1,301 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004, 2006, 2008 Develer S.r.l. (http://www.develer.com/)
* Copyright 2004 Giovanni Bajo
* -->
*
* \defgroup hashtable Hash table implementation
* \ingroup struct
* \{
*
* \brief Portable hash table
*
* This file implements a portable hash table, with the following features:
*
* \li Open double-hashing. The maximum number of elements is fixed. The double hashing
* function improves recovery in case of collisions.
* \li Configurable size (which is clamped to a power of two)
* \li Visiting interface through iterator (returns the element in random order).
* \li The key is stored within the data and a hook is used to extract it. Optionally, it
* is possible to store a copy of the key within the hash table.
*
* Since the hashing is open, there is no way to remove elements from the table. Instead, a
* function is provided to clear the table completely.
*
* The data stored within the table must be a pointer. The NULL pointer is used as
* a marker for a free node, so it is invalid to store a NULL pointer in the table
* with \c ht_insert().
*
* \author Giovanni Bajo <rasky@develer.com>
*
* $WIZ$ module_name = "hashtable"
* $WIZ$ module_configuration = "bertos/cfg/cfg_hashtable.h"
*/
#ifndef STRUCT_HASHTABLE_H
#define STRUCT_HASHTABLE_H
#include "cfg/cfg_hashtable.h"
#include <cfg/compiler.h>
#include <cfg/macros.h>
#include <cfg/debug.h>
/// Maximum length of the internal key (use (2^n)-1 for slight speedup)
#define INTERNAL_KEY_MAX_LENGTH 15
/**
* Hook to get the key from \a data, which is an element of the hash table. The
* key must be returned together with \a key_length (in words).
*/
typedef const void *(*hook_get_key)(const void *data, uint8_t *key_length);
/**
* Hash table description
*
* \note This structures MUST NOT be accessed directly. Its definition is
* provided in the header file only for optimization purposes (see the rationale
* in hashtable.c).
*
* \note If new elements must be added to this list, please double check
* \c DECLARE_HASHTABLE, which requires the existing elements to be at the top.
*/
struct HashTable
{
const void **mem; ///< Buckets of data
uint16_t max_elts_log2; ///< Log2 of the size of the table
struct {
bool key_internal : 1; ///< true if the key is copied internally
} flags;
union {
hook_get_key hook; ///< Hook to get the key
uint8_t *mem; ///< Pointer to the key memory
} key_data;
};
/// Iterator to walk the hash table
typedef struct
{
const void** pos;
const void** end;
} HashIterator;
/**
* Declare a hash table in the current scope
*
* \param name Variable name
* \param size Number of elements
* \param hook_gk Hook to be used to extract the key from the node
*
* \note The number of elements will be rounded down to the nearest
* power of two.
*
*/
#define DECLARE_HASHTABLE(name, size, hook_gk) \
static const void* name##_nodes[1 << UINT32_LOG2(size)]; \
struct HashTable name = \
{ \
.mem = name##_nodes, \
.max_elts_log2 = UINT32_LOG2(size), \
.flags = { .key_internal = false }, \
.key_data.hook = hook_gk \
}
/** Exactly like \c DECLARE_HASHTABLE, but the variable will be declared as static. */
#define DECLARE_HASHTABLE_STATIC(name, size, hook_gk) \
enum { name##_SIZE = (1 << UINT32_LOG2(size)), }; \
static const void* name##_nodes[name##_SIZE]; \
static struct HashTable name = \
{ \
.mem = name##_nodes, \
.max_elts_log2 = UINT32_LOG2(size), \
.flags = { .key_internal = false }, \
.key_data.hook = hook_gk \
}
#if CONFIG_HT_OPTIONAL_INTERNAL_KEY
/** Declare a hash table with internal copies of the keys. This version does not
* require a hook, nor it requires the user to allocate static memory for the keys.
* It is mostly suggested for tables whose keys are computed on the fly and need
* to be stored somewhere.
*/
#define DECLARE_HASHTABLE_INTERNALKEY(name, size) \
static uint8_t name##_keys[(1 << UINT32_LOG2(size)) * (INTERNAL_KEY_MAX_LENGTH + 1)]; \
static const void* name##_nodes[1 << UINT32_LOG2(size)]; \
struct HashTable name = { name##_nodes, UINT32_LOG2(size), { true }, name##_keys }
/** Exactly like \c DECLARE_HASHTABLE_INTERNALKEY, but the variable will be declared as static. */
#define DECLARE_HASHTABLE_INTERNALKEY_STATIC(name, size) \
enum { name##_KEYS = ((1 << UINT32_LOG2(size)) * (INTERNAL_KEY_MAX_LENGTH + 1)), \
name##_SIZE = (1 << UINT32_LOG2(size)), }; \
static uint8_t name##_keys[name##_KEYS]; \
static const void* name##_nodes[name##_SIZE]; \
static struct HashTable name = \
{ \
.mem = name##_nodes, \
.max_elts_log2 = UINT32_LOG2(size), \
.flags = { .key_internal = true }, \
.key_data.mem = name##_keys \
}
#endif
/**
* Initialize (and clear) a hash table in a memory buffer.
*
* \param ht Hash table declared with \c DECLARE_HASHTABLE
*
* \note This function must be called before using the hash table. Optionally,
* it can be called later in the program to clear the hash table,
* removing all its elements.
*/
void ht_init(struct HashTable* ht);
/**
* Insert an element into the hash table
*
* \param ht Handle of the hash table
* \param data Data to be inserted into the table
* \return true if insertion was successful, false otherwise (table is full)
*
* \note The key for the element to insert is extract from the data with
* the hook. This means that this function cannot be called for hashtables
* with internal keys.
*
* \note If an element with the same key already exists in the table,
* it will be overwritten.
*
* \note It is not allowed to store NULL in the table. If you pass NULL as data,
* the function call will fail.
*/
bool ht_insert(struct HashTable* ht, const void* data);
/**
* Insert an element into the hash table
*
* \param ht Handle of the hash table
* \param key Key of the element
* \param key_length Length of the key in characters
* \param data Data to be inserted into the table
* \return true if insertion was successful, false otherwise (table is full)
*
* \note If this function is called for hash table with external keys,
* the key provided must be match the key that would be extracted with the
* hook, otherwise the function will fail.
*
* \note If an element with the same key already exists in the table,
* it will be overwritten.
*
* \note It is not allowed to store NULL in the table. If you pass NULL as data,
* the function call will fail.
*/
bool ht_insert_with_key(struct HashTable* ht, const void* key, uint8_t key_length, const void* data);
/**
* Find an element in the hash table
*
* \param ht Handle of the hash table
* \param key Key of the element
* \param key_length Length of the key in characters
* \return Data of the element, or NULL if no element was found for the given key.
*/
const void* ht_find(struct HashTable* ht, const void* key, uint8_t key_length);
/** Similar to \c ht_insert_with_key() but \a key is an ASCIIZ string */
#define ht_insert_str(ht, key, data) ht_insert_with_key(ht, key, strlen(key), data)
/** Similar to \c ht_find() but \a key is an ASCIIZ string */
#define ht_find_str(ht, key) ht_find(ht, key, strlen(key))
/// Get an iterator to the begin of the hash table \a ht
INLINE HashIterator ht_iter_begin(struct HashTable* ht)
{
HashIterator h;
h.pos = &ht->mem[0];
h.end = &ht->mem[1 << ht->max_elts_log2];
while (h.pos != h.end && !*h.pos)
++h.pos;
return h;
}
/**
* Get an iterator to the (exclusive) end of the hash table \a ht
*
* \note Like in STL, the end iterator is not a valid iterator (you
* cannot call \c ht_iter_get() on it), and it must be used only to
* detect if we reached the end of the iteration (through \c ht_iter_cmp()).
*/
INLINE HashIterator ht_iter_end(struct HashTable* ht)
{
HashIterator h;
h.pos = h.end = &ht->mem[1 << ht->max_elts_log2];
return h;
}
/// Compare \a it1 and \a it2 for equality
INLINE bool ht_iter_cmp(HashIterator it1, HashIterator it2)
{
ASSERT(it1.end == it2.end);
return it1.pos == it2.pos;
}
/// Get the element within the hash table \a ht pointed by the iterator \a iter
INLINE const void* ht_iter_get(HashIterator iter)
{ return *iter.pos; }
/** Return an iterator pointing to the element following \a h
*
* \note The order of the elements visited during the iteration is casual,
* and depends on the implementation.
*
*/
INLINE HashIterator ht_iter_next(HashIterator h)
{
++h.pos;
while (h.pos != h.end && !(*h.pos))
++h.pos;
return h;
}
int hashtable_testSetup(void);
int hashtable_testRun(void);
int hashtable_testTearDown(void);
/** \} */ // \defgroup hashtable
#endif /* STRUCT_HASHTABLE_H */

View file

@ -0,0 +1,120 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
* -->
*
* \brief Test hashtable module.
*
* Test the hashtable module (insertion and find).
*
* \author Andrea Righi <arighi@develer.com>
*
* $test$: cp bertos/cfg/cfg_hashtable.h $cfgdir/
*/
#include <cfg/debug.h>
#include <cfg/test.h>
#include <string.h> /* strlen() */
#include "struct/hashtable.h"
static const void *test_get_key(const void *ptr, uint8_t *length)
{
const char *s = ptr;
*length = strlen(s);
return s;
}
#define NUM_ELEMENTS 256
DECLARE_HASHTABLE_STATIC(hash1, NUM_ELEMENTS, test_get_key);
DECLARE_HASHTABLE_INTERNALKEY_STATIC(hash2, NUM_ELEMENTS);
static char data[NUM_ELEMENTS][10];
static char keydomain[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
static bool single_test(void)
{
int i;
ht_init(&hash1);
ht_init(&hash2);
for (i = 0; i < NUM_ELEMENTS; i++)
{
int k, klen;
klen = (i % 8) + 1;
for (k = 0; k < klen; k++)
data[i][k] = keydomain[i % (sizeof(keydomain) - 1)];
data[i][k] = 0;
ASSERT(ht_insert(&hash1, data[i]));
ASSERT(ht_insert_str(&hash2, data[i], data[i]));
}
for (i = 0; i < NUM_ELEMENTS; i++)
{
const char *found1, *found2;
found1 = ht_find_str(&hash1, data[i]);
if (strcmp(found1, data[i]))
return false;
kprintf("hash1: found data[%d] = %s\n", i, found1);
found2 = ht_find_str(&hash2, data[i]);
if (strcmp(found2, data[i]))
return false;
kprintf("hash2: found data[%d] = %s\n", i, found2);
}
return true;
}
int hashtable_testRun(void)
{
if (!single_test())
{
kprintf("hashtable_test failed\n");
return -1;
}
kprintf("hashtable_test successful\n");
return 0;
}
int hashtable_testSetup(void)
{
kdbg_init();
return 0;
}
int hashtable_testTearDown(void)
{
kputs("TearDown hashtable test.\n");
return 0;
}
TEST_MAIN(hashtable);

258
bertos/struct/heap.c Normal file
View file

@ -0,0 +1,258 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999, 2000, 2001, 2008 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief Heap subsystem (public interface).
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#include "heap.h"
#include <cfg/debug.h> // ASSERT()
#include <string.h> // memset()
#define FREE_FILL_CODE 0xDEAD
#define ALLOC_FILL_CODE 0xBEEF
/*
* This function prototype is deprecated, will change in:
* void heap_init(struct Heap* h, heap_buf_t* memory, size_t size)
* in the next BeRTOS release.
*/
void heap_init(struct Heap* h, void* memory, size_t size)
{
#ifdef _DEBUG
memset(memory, FREE_FILL_CODE, size);
#endif
ASSERT2(((size_t)memory % alignof(heap_buf_t)) == 0,
"memory buffer is unaligned, please use the HEAP_DEFINE_BUF() macro to declare heap buffers!\n");
/* Initialize heap with a single big chunk */
h->FreeList = (MemChunk *)memory;
h->FreeList->next = NULL;
h->FreeList->size = size;
}
void *heap_allocmem(struct Heap* h, size_t size)
{
MemChunk *chunk, *prev;
/* Round size up to the allocation granularity */
size = ROUND_UP2(size, sizeof(MemChunk));
/* Handle allocations of 0 bytes */
if (!size)
size = sizeof(MemChunk);
/* Walk on the free list looking for any chunk big enough to
* fit the requested block size.
*/
for (prev = (MemChunk *)&h->FreeList, chunk = h->FreeList;
chunk;
prev = chunk, chunk = chunk->next)
{
if (chunk->size >= size)
{
if (chunk->size == size)
{
/* Just remove this chunk from the free list */
prev->next = chunk->next;
#ifdef _DEBUG
memset(chunk, ALLOC_FILL_CODE, size);
#endif
return (void *)chunk;
}
else
{
/* Allocate from the END of an existing chunk */
chunk->size -= size;
#ifdef _DEBUG
memset((uint8_t *)chunk + chunk->size, ALLOC_FILL_CODE, size);
#endif
return (void *)((uint8_t *)chunk + chunk->size);
}
}
}
return NULL; /* fail */
}
void heap_freemem(struct Heap* h, void *mem, size_t size)
{
MemChunk *prev;
ASSERT(mem);
#ifdef _DEBUG
memset(mem, FREE_FILL_CODE, size);
#endif
/* Round size up to the allocation granularity */
size = ROUND_UP2(size, sizeof(MemChunk));
/* Handle allocations of 0 bytes */
if (!size)
size = sizeof(MemChunk);
/* Special cases: first chunk in the free list or memory completely full */
ASSERT((uint8_t*)mem != (uint8_t*)h->FreeList);
if (((uint8_t *)mem) < ((uint8_t *)h->FreeList) || !h->FreeList)
{
/* Insert memory block before the current free list head */
prev = (MemChunk *)mem;
prev->next = h->FreeList;
prev->size = size;
h->FreeList = prev;
}
else /* Normal case: not the first chunk in the free list */
{
/*
* Walk on the free list. Stop at the insertion point (when mem
* is between prev and prev->next)
*/
prev = h->FreeList;
while (prev->next < (MemChunk *)mem && prev->next)
prev = prev->next;
/* Make sure mem is not *within* prev */
ASSERT((uint8_t*)mem >= (uint8_t*)prev + prev->size);
/* Should it be merged with previous block? */
if (((uint8_t *)prev) + prev->size == ((uint8_t *)mem))
{
/* Yes */
prev->size += size;
}
else /* not merged with previous chunk */
{
MemChunk *curr = (MemChunk*)mem;
/* insert it after the previous node
* and move the 'prev' pointer forward
* for the following operations
*/
curr->next = prev->next;
curr->size = size;
prev->next = curr;
/* Adjust for the following test */
prev = curr;
}
}
/* Also merge with next chunk? */
if (((uint8_t *)prev) + prev->size == ((uint8_t *)prev->next))
{
prev->size += prev->next->size;
prev->next = prev->next->next;
/* There should be only one merge opportunity, becuase we always merge on free */
ASSERT((uint8_t*)prev + prev->size != (uint8_t*)prev->next);
}
}
/**
* Returns the number of free bytes in a heap.
* \param h the heap to check.
*
* \note The returned value is the sum of all free memory regions
* in the heap.
* Those regions are likely to be *not* contiguous,
* so a successive allocation may fail even if the
* requested amount of memory is lower than the current free space.
*/
size_t heap_freeSpace(struct Heap *h)
{
size_t free_mem = 0;
for (MemChunk *chunk = h->FreeList; chunk; chunk = chunk->next)
free_mem += chunk->size;
return free_mem;
}
#if CONFIG_HEAP_MALLOC
/**
* Standard malloc interface
*/
void *heap_malloc(struct Heap* h, size_t size)
{
size_t *mem;
size += sizeof(size_t);
if ((mem = (size_t*)heap_allocmem(h, size)))
*mem++ = size;
return mem;
}
/**
* Standard calloc interface
*/
void *heap_calloc(struct Heap* h, size_t size)
{
void *mem;
if ((mem = heap_malloc(h, size)))
memset(mem, 0, size);
return mem;
}
/**
* Free a block of memory, determining its size automatically.
*
* \param h Heap from which the block was allocated.
* \param mem Pointer to a block of memory previously allocated with
* either heap_malloc() or heap_calloc().
*
* \note If \a mem is a NULL pointer, no operation is performed.
*
* \note Freeing the same memory block twice has undefined behavior.
*
* \note This function works like the ANSI C free().
*/
void heap_free(struct Heap *h, void *mem)
{
size_t *_mem = (size_t *)mem;
if (_mem)
{
--_mem;
heap_freemem(h, _mem, *_mem);
}
}
#endif /* CONFIG_HEAP_MALLOC */

125
bertos/struct/heap.h Normal file
View file

@ -0,0 +1,125 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999, 2000, 2001, 2008 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \defgroup heap Embedded optimized memory allocator
* \ingroup core
* \{
*
* \brief Heap subsystem (public interface).
*
* \todo Heap memory could be defined as an array of MemChunk, and used
* in this form also within the implementation. This would probably remove
* memory alignment problems, and also some aliasing issues.
*
* \author Bernie Innocenti <bernie@codewiz.org>
*
* $WIZ$ module_name = "heap"
* $WIZ$ module_configuration = "bertos/cfg/cfg_heap.h"
*/
#ifndef STRUCT_HEAP_H
#define STRUCT_HEAP_H
#include "cfg/cfg_heap.h"
#include <cfg/compiler.h>
#include <cfg/macros.h> // IS_POW2()
/* NOTE: struct size must be a 2's power! */
typedef struct _MemChunk
{
struct _MemChunk *next;
size_t size;
} MemChunk;
STATIC_ASSERT(IS_POW2(sizeof(MemChunk)));
typedef MemChunk heap_buf_t;
/// A heap
typedef struct Heap
{
struct _MemChunk *FreeList; ///< Head of the free list
} Heap;
/**
* Utility macro to allocate a heap of size \a size.
*
* \param name Variable name for the heap.
* \param size Heap size in bytes.
*/
#define HEAP_DEFINE_BUF(name, size) \
heap_buf_t name[((size) + sizeof(heap_buf_t) - 1) / sizeof(heap_buf_t)]
/// Initialize \a heap within the buffer pointed by \a memory which is of \a size bytes
void heap_init(struct Heap* heap, void* memory, size_t size);
/// Allocate a chunk of memory of \a size bytes from the heap
void *heap_allocmem(struct Heap* heap, size_t size);
/// Free a chunk of memory of \a size bytes from the heap
void heap_freemem(struct Heap* heap, void *mem, size_t size);
size_t heap_freeSpace(struct Heap *h);
#define HNEW(heap, type) \
(type*)heap_allocmem(heap, sizeof(type))
#define HNEWVEC(heap, type, nelem) \
(type*)heap_allocmem(heap, sizeof(type) * (nelem))
#define HDELETE(heap, type, mem) \
heap_freemem(heap, mem, sizeof(type))
#define HDELETEVEC(heap, type, nelem, mem) \
heap_freemem(heap, mem, sizeof(type) * (nelem))
#if CONFIG_HEAP_MALLOC
/**
* \name Compatibility interface with C standard library
* \{
*/
void *heap_malloc(struct Heap* heap, size_t size);
void *heap_calloc(struct Heap* heap, size_t size);
void heap_free(struct Heap* heap, void * mem);
/** \} */
#endif
/** \} */ //defgroup heap
int heap_testSetup(void);
int heap_testRun(void);
int heap_testTearDown(void);
#endif /* STRUCT_HEAP_H */

123
bertos/struct/heap_test.c Normal file
View file

@ -0,0 +1,123 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Heap test.
*
* \author Francesco Sacchi <batt@codewiz.org>
*/
#include <struct/fifobuf.h>
#include <struct/heap.h>
#include <cfg/compiler.h>
#include <cfg/test.h>
#include <cfg/debug.h>
#define TEST_LEN 31
#define ALLOC_SIZE 113
#define TEST_LEN2 32
#define ALLOC_SIZE2 128
#define HEAP_SIZE 4096
HEAP_DEFINE_BUF(heap_buf, HEAP_SIZE);
STATIC_ASSERT(sizeof(heap_buf) % sizeof(heap_buf_t) == 0);
Heap h;
int heap_testSetup(void)
{
kdbg_init();
heap_init(&h, heap_buf, sizeof(heap_buf));
return 0;
}
static void alloc_test(size_t size, size_t test_len)
{
//Simple test
uint8_t *a[test_len];
for (size_t i = 0; i < test_len; i++)
{
a[i] = heap_allocmem(&h, size);
ASSERT(a[i]);
for (size_t j = 0; j < size; j++)
a[i][j] = i;
}
ASSERT(heap_freeSpace(&h) == HEAP_SIZE - test_len * ROUND_UP2(size, sizeof(MemChunk)));
for (size_t i = 0; i < test_len; i++)
{
for (size_t j = 0; j < size; j++)
{
kprintf("a[%d][%d] = %d\n", i, j, a[i][j]);
ASSERT(a[i][j] == i);
}
heap_freemem(&h, a[i], size);
}
ASSERT(heap_freeSpace(&h) == HEAP_SIZE);
}
int heap_testRun(void)
{
alloc_test(ALLOC_SIZE, TEST_LEN);
alloc_test(ALLOC_SIZE2, TEST_LEN2);
/* Try to allocate the whole heap */
uint8_t *b = heap_allocmem(&h, HEAP_SIZE);
ASSERT(b);
ASSERT(heap_freeSpace(&h) == 0);
ASSERT(!heap_allocmem(&h, HEAP_SIZE));
for (int j = 0; j < HEAP_SIZE; j++)
b[j] = j;
for (int j = 0; j < HEAP_SIZE; j++)
{
kprintf("b[%d] = %d\n", j, j);
ASSERT(b[j] == (j & 0xff));
}
heap_freemem(&h, b, HEAP_SIZE);
ASSERT(heap_freeSpace(&h) == HEAP_SIZE);
return 0;
}
int heap_testTearDown(void)
{
return 0;
}
TEST_MAIN(heap);

View file

@ -0,0 +1,75 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief KFile interface over a FIFO buffer.
*
* \author Francesco Sacchi <asterix@develer.com>
*/
#include "kfile_fifo.h"
#include "fifobuf.h"
#include <io/kfile.h>
#include <string.h>
static size_t kfilefifo_read(struct KFile *_fd, void *_buf, size_t size)
{
KFileFifo *fd = KFILEFIFO_CAST(_fd);
uint8_t *buf = (uint8_t *)_buf;
while (size-- && !fifo_isempty_locked(fd->fifo))
*buf++ = fifo_pop_locked(fd->fifo);
return buf - (uint8_t *)_buf;
}
static size_t kfilefifo_write(struct KFile *_fd, const void *_buf, size_t size)
{
KFileFifo *fd = KFILEFIFO_CAST(_fd);
const uint8_t *buf = (const uint8_t *)_buf;
while (size-- && !fifo_isfull_locked(fd->fifo))
fifo_push_locked(fd->fifo, *buf++);
return buf - (const uint8_t *)_buf;
}
void kfilefifo_init(KFileFifo *kf, FIFOBuffer *fifo)
{
memset(kf, 0, sizeof(*kf));
kf->fifo = fifo;
kf->fd.read = kfilefifo_read;
kf->fd.write = kfilefifo_write;
DB(kf->fd._type = KFT_KFILEFIFO);
}

View file

@ -0,0 +1,99 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief KFile interface over a FIFO buffer.
*
* Convenient way to push data into a FIFO using the KFile interface.
* For example, it's possible to read from a serial port and put the characters
* into a fifo:
* \code
* // serial reader process
* {
* // other stuff here...
* kfile_read(&ser_port.fd, buffer, sizeof(buffer));
* kfile_write(&kfifo.fd, buffer, sizeof(buffer));
* // ...
* }
*
* // controller process
* {
* //...
* kfile_read(&kfifo.fd, buffer2, sizeof(buffer2));
* // use read data
* }
* \endcode
*
*
* \author Francesco Sacchi <asterix@develer.com>
*
* $WIZ$ module_name = "kfilefifo"
* $WIZ$ module_depends = "kfile"
*/
#ifndef STRUCT_KFILE_FIFO
#define STRUCT_KFILE_FIFO
#include "fifobuf.h"
#include <io/kfile.h>
typedef struct KFileFifo
{
KFile fd;
FIFOBuffer *fifo;
} KFileFifo;
/**
* ID for KFile FIFO.
*/
#define KFT_KFILEFIFO MAKE_ID('F', 'I', 'F', '0')
/**
* Convert + ASSERT from generic KFile to KFileFifo.
*/
INLINE KFileFifo * KFILEFIFO_CAST(KFile *fd)
{
ASSERT(fd->_type == KFT_KFILEFIFO);
return (KFileFifo *)fd;
}
/**
* Initialize KFileFifo struct.
*
* \param kf Interface to initialize.
* \param fifo Fifo buffer to operate on.
*/
void kfilefifo_init(KFileFifo *kf, FIFOBuffer *fifo);
int kfilefifo_testSetup(void);
int kfilefifo_testRun(void);
int kfilefifo_testTearDown(void);
#endif /* STRUCT_KFILE_FIFO */

View file

@ -0,0 +1,147 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief FIFO and KFileFifo test.
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#include <struct/fifobuf.h>
#include <struct/kfile_fifo.h>
#include <cfg/compiler.h>
#include <cfg/test.h>
#include <cfg/debug.h>
int kfilefifo_testSetup(void)
{
kdbg_init();
return 0;
}
int kfilefifo_testRun(void)
{
#define FIFOBUF_LEN 256
uint8_t buf[FIFOBUF_LEN];
FIFOBuffer fifo;
fifo_init(&fifo, buf, sizeof(buf));
ASSERT(fifo_isempty(&fifo));
ASSERT(!fifo_isfull(&fifo));
for (int i = 0; i < FIFOBUF_LEN - 1; i++)
{
ASSERT(!fifo_isfull(&fifo));
fifo_push(&fifo, i);
}
ASSERT(fifo_isfull(&fifo));
for (int i = 0; i < FIFOBUF_LEN - 1; i++)
{
ASSERT(!fifo_isempty(&fifo));
ASSERT(fifo_pop(&fifo) == i);
}
ASSERT(fifo_isempty(&fifo));
for (int i = 0; i < FIFOBUF_LEN - 1; i++)
{
ASSERT(!fifo_isfull(&fifo));
fifo_push(&fifo, i);
}
ASSERT(fifo_isfull(&fifo));
fifo_flush(&fifo);
ASSERT(!fifo_isfull(&fifo));
ASSERT(fifo_isempty(&fifo));
KFileFifo kfifo;
kfilefifo_init(&kfifo, &fifo);
for (int i = 0; i < FIFOBUF_LEN - 1; i++)
{
ASSERT(!fifo_isfull(&fifo));
fifo_push(&fifo, i);
}
for (int i = 0; i < FIFOBUF_LEN - 1; i++)
ASSERT(kfile_getc(&kfifo.fd) == i);
ASSERT(kfile_getc(&kfifo.fd) == EOF);
ASSERT(fifo_isempty(&fifo));
for (int i = 0; i < FIFOBUF_LEN - 1; i++)
ASSERT(kfile_putc(i, &kfifo.fd) == i);
ASSERT(fifo_isfull(&fifo));
for (int i = 0; i < FIFOBUF_LEN - 1; i++)
{
ASSERT(!fifo_isempty(&fifo));
ASSERT(fifo_pop(&fifo) == i);
}
ASSERT(fifo_isempty(&fifo));
for (int i = 0; i < FIFOBUF_LEN - 1; i++)
ASSERT(kfile_putc(i, &kfifo.fd) == i);
ASSERT(fifo_isfull(&fifo));
ASSERT(kfile_putc('a', &kfifo.fd) == EOF);
fifo_flush(&fifo);
ASSERT(!fifo_isfull(&fifo));
ASSERT(fifo_isempty(&fifo));
ASSERT(kfile_getc(&kfifo.fd) == EOF);
ASSERT(kfile_write(&kfifo.fd, "hello world", 11) == 11);
ASSERT(kfile_write(&kfifo.fd, "hello world", FIFOBUF_LEN) == FIFOBUF_LEN - 1 - 11);
uint8_t test_buf[FIFOBUF_LEN];
ASSERT(kfile_read(&kfifo.fd, test_buf, FIFOBUF_LEN + 20) == FIFOBUF_LEN - 1);
ASSERT(!fifo_isfull(&fifo));
ASSERT(fifo_isempty(&fifo));
ASSERT(kfile_getc(&kfifo.fd) == EOF);
return 0;
}
int kfilefifo_testTearDown(void)
{
return 0;
}
TEST_MAIN(kfilefifo);

82
bertos/struct/kfile_mem.c Normal file
View file

@ -0,0 +1,82 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief KFile interface over a memory buffer.
*
* \author Francesco Sacchi <batt@develer.com>
*/
#include "kfile_mem.h"
#include <io/kfile.h>
#include <string.h>
static size_t kfilemem_read(struct KFile *_fd, void *buf, size_t size)
{
KFileMem *fd = KFILEMEM_CAST(_fd);
size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
uint8_t *mem = (uint8_t *)fd->mem;
memcpy(buf, mem + fd->fd.seek_pos, size);
fd->fd.seek_pos += size;
return size;
}
static size_t kfilemem_write(struct KFile *_fd, const void *buf, size_t size)
{
KFileMem *fd = KFILEMEM_CAST(_fd);
size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
uint8_t *mem = (uint8_t *)fd->mem;
memcpy(mem + fd->fd.seek_pos, buf, size);
fd->fd.seek_pos += size;
return size;
}
void kfilemem_init(KFileMem *km, void *mem, size_t len)
{
ASSERT(km);
ASSERT(mem);
ASSERT(len);
memset(km, 0, sizeof(*km));
km->mem = mem;
kfile_init(&km->fd);
km->fd.read = kfilemem_read;
km->fd.write = kfilemem_write;
km->fd.size = len;
DB(km->fd._type = KFT_KFILEMEM);
}

81
bertos/struct/kfile_mem.h Normal file
View file

@ -0,0 +1,81 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief KFile interface over a memory buffer.
*
* Convenient way to access a memory region using the KFile interface.
*
*
* \author Francesco Sacchi <batt@develer.com>
*
* $WIZ$ module_name = "kfilemem"
* $WIZ$ module_depends = "kfile"
*/
#ifndef STRUCT_KFILE_MEM
#define STRUCT_KFILE_MEM
#include <io/kfile.h>
/**
* Context for KFile over memory buffer.
*/
typedef struct KFileMem
{
KFile fd; ///< KFile base class
void *mem; ///< Pointer to the memory buffer used.
} KFileMem;
/**
* ID for KFile Mem.
*/
#define KFT_KFILEMEM MAKE_ID('M', 'E', 'M', '0')
/**
* Convert + ASSERT from generic KFile to KFileMem.
*/
INLINE KFileMem * KFILEMEM_CAST(KFile *fd)
{
ASSERT(fd->_type == KFT_KFILEMEM);
return (KFileMem *)fd;
}
/**
* Initialize KFileMem struct.
*
* \param km Interface to initialize.
* \param mem Pointer to the memory buffer to operate on.
* \param len Size of the buffer
*/
void kfilemem_init(KFileMem *km, void *mem, size_t len);
#endif /* STRUCT_KFILE_MEM */

410
bertos/struct/list.h Normal file
View file

@ -0,0 +1,410 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
* Copyright 2001, 2008 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \defgroup list General purpose lists
* \ingroup struct
* \{
*
* \brief General pourpose double-linked lists
*
* Lists contain nodes. You can put any custom struct into any list as long
* as it has a Node struct inside it. If you make the Node struct the first
* member of your data type, you can simply cast it to (Node *) when passing
* it to list functions.
*
* Lists must be initialized before use with LIST_INIT(). You can then add
* objects using ADDHEAD() and ADDTAIL() macros, and remove them with
* list_remHead() and list_remTail().
*
* You can create lists with priorities by using PriNode instead of Node as
* the base member struct.
* Use LIST_ENQUEUE() and LIST_ENQUEUE_HEAD() to insert a priority node into
* a list.
*
* To iterate over a list, use the macros FOREACH_NODE() and REVERSE_FOREACH_NODE()
* in this way:
* \code
* struct Foo
* {
* Node n;
* int a;
* }
*
* int main()
* {
* List foo_list;
* static Foo foo1, foo2;
* Foo *fp;
*
* LIST_INIT(&foo_list);
* ADDHEAD(&foo_list, (Node *)&foo1);
* INSERT_BEFORE(&foo_list, (Node *)&foo2);
* FOREACH_NODE(fp, &foo_list)
* fp->a = 10;
* }
* \endcode
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#ifndef STRUCT_LIST_H
#define STRUCT_LIST_H
#include <cfg/compiler.h> /* INLINE */
#include <cfg/debug.h> /* ASSERT_VALID_PTR() */
/**
* This structure represents a node for bidirectional lists.
*
* Data is usually appended to nodes by making them the first
* field of another struture, as a poor-man's form of inheritance.
*/
typedef struct _Node
{
struct _Node *succ;
struct _Node *pred;
} Node;
/**
* Head of a doubly-linked list of \c Node structs.
*
* Lists must be initialized with LIST_INIT() prior to use.
*
* Nodes can be added and removed from either end of the list
* with O(1) performance. Iterating over these lists can be
* tricky: use the FOREACH_NODE() macro instead.
*/
typedef struct _List
{
Node head;
Node tail;
} List;
/**
* Extended node for priority queues.
*/
typedef struct _PriNode
{
Node link;
int pri;
} PriNode;
/**
* Template for a naked node in a list of \a T structures.
*
* To be used as data member in other structures:
*
* \code
* struct Foo
* {
* DECLARE_NODE_ANON(struct Foo)
* int a;
* float b;
* }
*
* DECLARE_LIST_TYPE(Foo);
*
* void foo(void)
* {
* static LIST_TYPE(Foo) foo_list;
* static Foo foo1, foo2;
* Foo *fp;
*
* LIST_INIT(&foo_list);
* ADDHEAD(&foo_list, &foo1);
* INSERT_BEFORE(&foo_list, &foo2);
* FOREACH_NODE(fp, &foo_list)
* fp->a = 10;
* }
*
* \endcode
*/
#define DECLARE_NODE_ANON(T) \
T *succ; T *pred;
/** Declare a typesafe node for structures of type \a T. */
#define DECLARE_NODE_TYPE(T) \
typedef struct T##Node { T *succ; T *pred; } T##Node
/** Template for a list of \a T structures. */
#define DECLARE_LIST_TYPE(T) \
DECLARE_NODE_TYPE(T); \
typedef struct T##List { \
T##Node head; \
T##Node tail; \
} T##List
#define NODE_TYPE(T) T##Node
#define LIST_TYPE(T) T##List
/**
* Get a pointer to the first node in a list.
*
* If \a l is empty, result points to l->tail.
*/
#define LIST_HEAD(l) ((l)->head.succ)
/**
* Get a pointer to the last node in a list.
*
* If \a l is empty, result points to l->head.
*/
#define LIST_TAIL(l) ((l)->tail.pred)
// TODO: move in compiler.h
#if COMPILER_TYPEOF
#define TYPEOF_OR_VOIDPTR(type) typeof(type)
#else
#define TYPEOF_OR_VOIDPTR(type) void *
#endif
/**
* Iterate over all nodes in a list.
*
* This macro generates a "for" statement using the following parameters:
* \param n Node pointer to be used in each iteration.
* \param l Pointer to list.
*/
#define FOREACH_NODE(n, l) \
for( \
(n) = (TYPEOF_OR_VOIDPTR(n))LIST_HEAD(l); \
((Node *)(n))->succ; \
(n) = (TYPEOF_OR_VOIDPTR(n))(((Node *)(n))->succ) \
)
/**
* Iterate backwards over all nodes in a list.
*
* This macro generates a "for" statement using the following parameters:
* \param n Node pointer to be used in each iteration.
* \param l Pointer to list.
*/
#define REVERSE_FOREACH_NODE(n, l) \
for( \
(n) = (TYPEOF_OR_VOIDPTR(n))LIST_TAIL(l); \
((Node *)(n))->pred; \
(n) = (TYPEOF_OR_VOIDPTR(n))(((Node *)(n))->pred) \
)
/**
* Iterate on the list safely against node removal.
*
* This macro generates a "for" statement using the following parameters:
* \param n Node pointer to be used in each iteration.
* \param p Temporal storage for the iterator.
* \param l Pointer to list.
*/
#define FOREACH_NODE_SAFE(n, p, l) \
for( \
(n) = (TYPEOF_OR_VOIDPTR(n))LIST_HEAD(l), (p) = ((Node *)(n))->succ; \
((Node *)(n))->succ; \
(n) = (p), (p) = (TYPEOF_OR_VOIDPTR(n))(((Node *)(n))->succ) \
)
/** Initialize a list. */
#define LIST_INIT(l) \
do { \
(l)->head.succ = (TYPEOF_OR_VOIDPTR((l)->head.succ)) &(l)->tail; \
(l)->head.pred = NULL; \
(l)->tail.succ = NULL; \
(l)->tail.pred = (TYPEOF_OR_VOIDPTR((l)->tail.pred)) &(l)->head; \
} while (0)
#ifdef _DEBUG
/** Make sure that a list is valid (it was initialized and is not corrupted). */
#define LIST_ASSERT_VALID(l) \
do { \
Node *n, *pred; \
ASSERT((l)->head.succ != NULL); \
ASSERT((l)->head.pred == NULL); \
ASSERT((l)->tail.succ == NULL); \
ASSERT((l)->tail.pred != NULL); \
pred = &(l)->head; \
FOREACH_NODE(n, l) \
{ \
ASSERT(n->pred == pred); \
pred = n; \
} \
ASSERT(n == &(l)->tail); \
} while (0)
/// Checks that a node isn't part of a given list
#define LIST_ASSERT_NOT_CONTAINS(list,node) \
do { \
Node *ln; \
ASSERT_VALID_PTR(list); \
ASSERT_VALID_PTR(node); \
FOREACH_NODE(ln, list) \
ASSERT(ln != (Node *)(node)); \
} while (0)
#define INVALIDATE_NODE(n) ((n)->succ = (n)->pred = NULL)
#else
#define LIST_ASSERT_VALID(l) do {} while (0)
#define LIST_ASSERT_NOT_CONTAINS(list,node) do {} while (0)
#define INVALIDATE_NODE(n) do {} while (0)
#endif
/** Tell whether a list is empty. */
#define LIST_EMPTY(l) ( (void *)((l)->head.succ) == (void *)(&(l)->tail) )
/** Add node to list head. */
#define ADDHEAD(l,n) \
do { \
LIST_ASSERT_NOT_CONTAINS((l),(n)); \
(n)->succ = (l)->head.succ; \
(n)->pred = (l)->head.succ->pred; \
(n)->succ->pred = (n); \
(n)->pred->succ = (n); \
} while (0)
/** Add node to list tail. */
#define ADDTAIL(l,n) \
do { \
LIST_ASSERT_NOT_CONTAINS((l),(n)); \
(n)->succ = &(l)->tail; \
(n)->pred = (l)->tail.pred; \
(n)->pred->succ = (n); \
(l)->tail.pred = (n); \
} while (0)
/**
* Insert node \a n before node \a ln.
*
* \note You can't pass in a list header as \a ln, but
* it is safe to pass list-\>head of an empty list.
*/
#define INSERT_BEFORE(n,ln) \
do { \
ASSERT_VALID_PTR(n); \
ASSERT_VALID_PTR(ln); \
(n)->succ = (ln); \
(n)->pred = (ln)->pred; \
(ln)->pred->succ = (n); \
(ln)->pred = (n); \
} while (0)
/**
* Remove \a n from whatever list it is in.
*
* \note Removing a node that has not previously been
* inserted into a list invokes undefined behavior.
*/
#define REMOVE(n) \
do { \
ASSERT_VALID_PTR(n); \
(n)->pred->succ = (n)->succ; \
(n)->succ->pred = (n)->pred; \
INVALIDATE_NODE(n); \
} while (0)
/**
* Insert a priority node in a priority queue.
*
* The new node is inserted immediately before the first node with the same
* priority or appended to the tail if no such node exists.
*/
#define LIST_ENQUEUE_HEAD(list, node) \
do { \
PriNode *ln; \
LIST_ASSERT_NOT_CONTAINS((list),(node)); \
FOREACH_NODE(ln, (list)) \
if (ln->pri <= (node)->pri) \
break; \
INSERT_BEFORE(&(node)->link, &ln->link); \
} while (0)
/**
* Insert a priority node in a priority queue.
*
* The new node is inserted immediately before the first node with lower
* priority or appended to the tail if no such node exists.
*/
#define LIST_ENQUEUE(list, node) \
do { \
PriNode *ln; \
LIST_ASSERT_NOT_CONTAINS((list),(node)); \
FOREACH_NODE(ln, (list)) \
if (ln->pri < (node)->pri) \
break; \
INSERT_BEFORE(&(node)->link, &ln->link); \
} while (0)
/**
* Unlink a node from the head of the list \a l.
*
* \return Pointer to node, or NULL if the list was empty.
*/
INLINE Node *list_remHead(List *l)
{
Node *n;
ASSERT_VALID_PTR(l);
if (LIST_EMPTY(l))
return (Node *)0;
n = l->head.succ; /* Get first node. */
l->head.succ = n->succ; /* Link list head to second node. */
n->succ->pred = &l->head; /* Link second node to list head. */
INVALIDATE_NODE(n);
return n;
}
/**
* Unlink a node from the tail of the list \a l.
*
* \return Pointer to node, or NULL if the list was empty.
*/
INLINE Node *list_remTail(List *l)
{
Node *n;
ASSERT_VALID_PTR(l);
if (LIST_EMPTY(l))
return NULL;
n = l->tail.pred; /* Get last node. */
l->tail.pred = n->pred; /* Link list tail to second last node. */
n->pred->succ = &l->tail; /* Link second last node to list tail. */
INVALIDATE_NODE(n);
return n;
}
/** \} */ //defgroup list
#endif /* STRUCT_LIST_H */

177
bertos/struct/pool.h Normal file
View file

@ -0,0 +1,177 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004, 2008, 2011 Develer S.r.l. (http://www.develer.com/)
* -->
*
* \defgroup pool Pool memory allocator
* \ingroup struct
* \{
*
* \brief Pool macros.
*
* The pool module provides the boilerplate code to create a set of objects
* of the same type.
* It provides an interface similar to the heap module, with pool_alloc() and
* pool_free() functions that allocate and free an element respectively.
* In contrast with the heap module, you can specify exactly the number of
* items that you want to be in the pool.
*
* Items in the pool must be a derived class of <tt>Node *</tt>, which also
* means that they can be used as-is with list containers, eg. MsgPort.
*
* Example code:
* \code
* typedef struct MyType
* {
* Node *n;
* uint16_t *buf;
* // other members here...
* } MyType;
*
* DECLARE_POOL(mypool, MyType, POOL_SIZE);
*
* static void elem_init(MyType *e)
* {
* e->buf = NULL;
* // other initializations here
* }
*
* int main(void)
* {
* pool_init(&mypool, elem_init);
*
* MyType *foo = pool_alloc(&mypool);
* // do stuff with foo
* pool_free(&mypool, foo);
* }
* \endcode
*
* \author Giovanni Bajo <rasky@develer.com>
* \author Luca Ottaviano <lottaviano@develer.com>
*/
#ifndef STRUCT_POOL_H
#define STRUCT_POOL_H
#include <cfg/macros.h>
#include <struct/list.h>
/**
* \brief Extern pool declaration
*/
#define EXTERN_POOL(name) \
extern List name
#define DECLARE_POOL_WITH_STORAGE(name, type, num, storage) \
static type name##_items[num]; \
storage name; \
INLINE void name##_init(void (*init_func)(type*)) \
{ \
size_t i; \
LIST_INIT(&name); \
for (i=0;i<countof(name##_items);++i) \
{ \
if (init_func) init_func(&name##_items[i]); \
ADDTAIL(&name, (Node*)&name##_items[i]); \
} \
} \
INLINE void name##_init(void (*init_func)(type*)) \
/**/
/**
* \brief Helper macro to declare a Pool data type.
*
* Data type inserted into the pool must be a <tt>Node *</tt>
* type.
*
* \param name Variable name of the pool.
* \param type Data type held by the pool.
* \param num Number of elements in pool.
*/
#define DECLARE_POOL(name, type, num) \
DECLARE_POOL_WITH_STORAGE(name, type, num, List)
/**
* \brief Static Pool declaration
*
* \sa DECLARE_POOL
*/
#define DECLARE_POOL_STATIC(name, type, num) \
DECLARE_POOL_WITH_STORAGE(name, type, num, static List)
/**
* Initialize the pool \a name, calling \a init_func on each element.
*
* The init function must have the following prototype:
* \code
* void init_func(type *)
* \endcode
* where \a type is the type of objects held in the pool.
*
* \param name Pool to initialize
* \param init_func Init function to be called on each element
*/
#define pool_init(name, init_func) (*(name##_init))(init_func)
/**
* \brief Allocate an element from the pool.
*
* The returned element is of type <tt>Node *</tt>, it's safe to
* cast it to the type contained in the pool.
*
* \note If the element was recycled with pool_free(), it will not be reset,
* so don't assume that the element has specific values.
*
* \param name Pointer to pool to alloc from.
* \return Element of the type present in the pool.
*/
#define pool_alloc(name) list_remHead(name)
/**
* \brief Recycle an element into the pool
*
* \note Element fields are not reset to its original values, keep that in
* mind when you alloc nodes.
*
* \param name Pointer to pool where the node should be recycled.
* \param elem Element to be recycled.
*/
#define pool_free(name, elem) ADDHEAD(name, (Node*)elem)
/**
* \brief Test if the pool is empty
*
* \param name Pointer to pool.
* \return True if the pool is empty, false otherwise.
*/
#define pool_empty(name) LIST_EMPTY(name)
/** \} */ /* defgroup pool */
#endif /* STRUCT_POOL_H */