mirror of
https://github.com/markqvist/OpenModem.git
synced 2025-11-27 01:56:23 -05:00
Working
This commit is contained in:
commit
c898b090dd
1049 changed files with 288572 additions and 0 deletions
269
bertos/struct/bitarray.h
Normal file
269
bertos/struct/bitarray.h
Normal 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 */
|
||||
132
bertos/struct/bitarray_test.c
Normal file
132
bertos/struct/bitarray_test.c
Normal 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
363
bertos/struct/fifobuf.h
Normal 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
287
bertos/struct/hashtable.c
Normal 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
301
bertos/struct/hashtable.h
Normal 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 */
|
||||
120
bertos/struct/hashtable_test.c
Normal file
120
bertos/struct/hashtable_test.c
Normal 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
258
bertos/struct/heap.c
Normal 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
125
bertos/struct/heap.h
Normal 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
123
bertos/struct/heap_test.c
Normal 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);
|
||||
75
bertos/struct/kfile_fifo.c
Normal file
75
bertos/struct/kfile_fifo.c
Normal 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);
|
||||
}
|
||||
99
bertos/struct/kfile_fifo.h
Normal file
99
bertos/struct/kfile_fifo.h
Normal 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 */
|
||||
147
bertos/struct/kfile_fifo_test.c
Normal file
147
bertos/struct/kfile_fifo_test.c
Normal 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
82
bertos/struct/kfile_mem.c
Normal 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
81
bertos/struct/kfile_mem.h
Normal 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
410
bertos/struct/list.h
Normal 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
177
bertos/struct/pool.h
Normal 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 */
|
||||
Loading…
Add table
Add a link
Reference in a new issue