Issue 2 move folders and use deep path include file names to prevent collisions (#4)

* moving folders and files and adjust server demo build

* Fix Makefile for apps/server on Linux

* fix unit test source file folders

* fix datetime convert UTC functions. Add Code::Blocks project for datetime testing

* added some ignore extensions

* disable parallel make option

* fix build for abort, dcc, and epics apps

* fix build for dcc, epics, error, and getevent apps.

* Fixed building of all apps

* fix the ipv4 to ipv6 router app build

* Change indent style from Google to Webkit

* make pretty to re-format style

* removed common Makefile since we already had one and two was too many

* remove scripts from root folder that are no longer maintained or used

* remove mercurial EOL and ignore files for git repo

* remove .vscodeconfig files from repo

* tweak clang-format style

* clang-format src and apps with tweaked style

* added clang-tidy to fix readability if braces in src

* result of make tidy for src and apps

* fix clang-tidy mangling

* Added code::blocks project for BACnet server simulation

* added code::blocks linux project for WhoIs app

* update text files for EOL

* fix EOL in some files

* fixed make win32 apps for older gcc

* Removed Borland C++ Makefile in apps. Unable to maintain support for Borland C++ compiler.

* created codeblocks project for apps/epics for Windows

* fixing ports/xplained to work with new data structure.

* fix ports/xplained example for Atmel Studio compile

* fix ports/stm32f10x example for gcc Makefile compile

* fix ports/stm32f10x example for IAR EWARM compile

* fix ports/xplained timer callback

* fix ports/bdk_atxx_mspt build with subdirs

* fix ports/bdk_atxx_mspt build with subdirs

* updated git ignore for IAR build artifacts

* updated gitignore for non-tracked files and folders

* fixed bdk-atxx4-mstp port for Rowley Crossworks project file

* fixed bdk-atxx4-mstp port for GCC AVR Makefile

* fixed atmega168 port for IAR AVR and GCC AVR Makefile

* fixed at91sam7s port for IAR ARM and GCC ARM Makefile

* removed unmaintainable DOS, RTOS32, and atmega8 ports.  Updated rx62n (untested).

* changed arm7 to uip port
This commit is contained in:
Steve Karg
2019-12-13 15:19:10 -06:00
committed by GitHub
parent 8a38dbe2cf
commit d50c190957
912 changed files with 36206 additions and 52502 deletions
+48
View File
@@ -0,0 +1,48 @@
/* Derived from "Unix Incompatibility Notes: Byte Order" by Jan Wolter */
/* http://unixpapa.com/incnote/byteorder.html */
/** @file bigend.c Determination of Endianess */
#include "bigend.h"
/* Big-Endian systems save the most significant byte first. */
/* Sun and Motorola processors, IBM-370s and PDP-10s are big-endian. */
/* "Network Byte Order" is also know as "Big-Endian Byte Order" */
/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */
/* x[0] = 0x04 */
/* x[1] = 0x03 */
/* x[2] = 0x02 */
/* x[3] = 0x01 */
/* Little-Endian systems save the least significant byte first. */
/* The entire Intel x86 family, Vaxes, Alphas and PDP-11s are little-endian. */
/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */
/* x[0] = 0x01 */
/* x[1] = 0x02 */
/* x[2] = 0x03 */
/* x[3] = 0x04 */
/* Note: Endianness doesn't apply to all variable manipulation.
If you use bitwise or bitshift operations on integers,
you can avoid having to check for endianness. */
/* The names are derived from Jonathon Swift's book Gulliver's Travels,
where they describe Lilliputian political parties who disagree
vehemently over which end to start eating an egg from.
This terminology was popularized for byte order by a less than
completely serious paper authored by Danny Cohen which appeared
on April 1, 1980 and was entitled "On Holy Wars and a Plea for Peace" */
/* function to return true on Big-Endian architectures */
/* (based on Harbison & Steele) */
int big_endian(void)
{
union {
long l;
char c[sizeof(long)];
} u;
u.l = 1;
return (u.c[sizeof(long) - 1] == 1);
}
+53
View File
@@ -0,0 +1,53 @@
/**************************************************************************
*
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************************/
#ifndef BIGEND_H
#define BIGEND_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Big-Endian systems save the most significant byte first. */
/* Sun and Motorola processors, IBM-370s and PDP-10s are big-endian. */
/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */
/* x[0] = 0x04 */
/* x[1] = 0x03 */
/* x[2] = 0x02 */
/* x[3] = 0x01 */
/* Little-Endian systems save the least significant byte first. */
/* The entire Intel x86 family, Vaxes, Alphas and PDP-11s are little-endian. */
/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */
/* x[0] = 0x01 */
/* x[1] = 0x02 */
/* x[2] = 0x03 */
/* x[3] = 0x04 */
int big_endian(
void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+61
View File
@@ -0,0 +1,61 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2008 Steve Karg
This program 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.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include <stdio.h> /* Standard I/O */
#include <stdlib.h> /* Standard Library */
#include <stdarg.h>
#include "bacnet/basic/sys/debug.h"
/** @file debug.c Debug print function */
#if DEBUG_ENABLED
void debug_printf(const char *format, ...)
{
va_list ap;
va_start(ap, format);
vfprintf(stdout, format, ap);
va_end(ap);
fflush(stdout);
return;
}
#else
void debug_printf(const char *format, ...)
{
format = format;
}
#endif
+57
View File
@@ -0,0 +1,57 @@
/**************************************************************************
*
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************************/
#ifndef DEBUG_H
#define DEBUG_H
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#ifndef DEBUG_ENABLED
#define DEBUG_ENABLED 0
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void debug_printf(
const char *format,
...);
#if DEBUG_ENABLED
/* Nothing more here */
#else
/* If your compiler supports it, this is more compact:
inline void debug_printf(
const char *format,
...) {
format = format;
}
*/
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+481
View File
@@ -0,0 +1,481 @@
/**
* @file
* @author Steve Karg
* @date 2004
* @brief Generic interrupt safe FIFO library for deeply embedded system.
*
* @section LICENSE
*
* This program 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.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*
* As a special exception, if other files instantiate templates or
* use macros or inline functions from this file, or you compile
* this file and link it with other works to produce a work based
* on this file, this file does not by itself cause the resulting
* work to be covered by the GNU General Public License. However
* the source code for this file must still be made available in
* accordance with section (3) of the GNU General Public License.
*
* This exception does not invalidate any other reasons why a work
* based on this file might be covered by the GNU General Public
* License.
*
* @section DESCRIPTION
*
* Generic interrupt safe FIFO library for deeply embedded system
* This library only uses a byte sized chunk for a data element.
* It uses a data store whose size is a power of 2 (8, 16, 32, 64, ...)
* and doesn't waste any data bytes. It has very low overhead, and
* utilizes modulo for indexing the data in the data store.
*
* To use this library, first declare a data store, sized for a power of 2:
* {@code
* static volatile uint8_t data_store[64];
* }
*
* Then declare the FIFO tracking structure:
* {@code
* static FIFO_BUFFER queue;
* }
*
* Initialize the queue with the data store:
* {@code
* FIFO_Init(&queue, data_store, sizeof(data_store));
* }
*
* Then begin to use the FIFO queue by giving it data, retreiving data,
* and checking the FIFO queue to see if it is empty or full:
* {@code
* uint8_t in_data = 0;
* uint8_t out_data = 0;
* uint8_t add_data[5] = {0};
* uint8_t pull_data[5] = {0};
* unsigned count = 0;
* bool status = false;
*
* status = FIFO_Put(&queue, in_data);
* if (!FIFO_Empty(&queue)) {
* out_data = FIFO_Get(&queue);
* }
* if (FIFO_Available(&queue, sizeof(add_data))) {
* status = FIFO_Add(&queue, add_data, sizeof(add_data));
* }
* count = FIFO_Count(&queue);
* if (count == sizeof(add_data)) {
* count = FIFO_Pull(&queue, &pull_data[0], sizeof(pull_data));
* }
*
* }
*
* Normally the FIFO is used by a producer, such as in interrupt service
* routine, which places data into the queue using FIFO_Put(), and a consumer,
* such as a main loop handler, which pulls data from the queue by first
* checking the queue for data using FIFO_Empty(), and then pulling data from
* the queue using FIFO_Get().
*
*/
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/basic/sys/fifo.h"
/**
* Returns the number of bytes in the FIFO
*
* @param b - pointer to FIFO_BUFFER structure
*
* @return Number of bytes in the FIFO
*/
unsigned FIFO_Count(FIFO_BUFFER const *b)
{
unsigned head, tail; /* used to avoid volatile decision */
if (b) {
head = b->head;
tail = b->tail;
return head - tail;
} else {
return 0;
}
}
/**
* Returns the full status of the FIFO
*
* @param b - pointer to FIFO_BUFFER structure
*
* @return true if the FIFO is full, false if it is not.
*/
bool FIFO_Full(FIFO_BUFFER const *b)
{
return (b ? (FIFO_Count(b) == b->buffer_len) : true);
}
/**
* Tests to see if space is available in the FIFO
*
* @param b - pointer to FIFO_BUFFER structure
* @param count [in] - number of bytes tested for availability
*
* @return true if the number of bytes sought is available
*/
bool FIFO_Available(FIFO_BUFFER const *b, unsigned count)
{
return (b ? (count <= (b->buffer_len - FIFO_Count(b))) : false);
}
/**
* Returns the empty status of the FIFO
*
* @param b - pointer to FIFO_BUFFER structure
* @return true if the FIFO is empty, false if it is not.
*/
bool FIFO_Empty(FIFO_BUFFER const *b)
{
return (b ? (FIFO_Count(b) == 0) : true);
}
/**
* Peeks at the data from the front of the FIFO without removing it.
* Use FIFO_Empty() or FIFO_Available() function to see if there is
* data to retrieve since this function doesn't return a flag indicating
* success or failure.
*
* @param b - pointer to FIFO_BUFFER structure
*
* @return byte of data, or zero if nothing in the list
*/
uint8_t FIFO_Peek(FIFO_BUFFER const *b)
{
unsigned index;
if (b) {
index = b->tail % b->buffer_len;
return (b->buffer[index]);
}
return 0;
}
/**
* Gets a byte from the front of the FIFO, and removes it.
* Use FIFO_Empty() or FIFO_Available() function to see if there is
* data to retrieve since this function doesn't return a flag indicating
* success or failure.
*
* @param b - pointer to FIFO_BUFFER structure
*
* @return the data
*/
uint8_t FIFO_Get(FIFO_BUFFER *b)
{
uint8_t data_byte = 0;
unsigned index;
if (!FIFO_Empty(b)) {
index = b->tail % b->buffer_len;
data_byte = b->buffer[index];
b->tail++;
}
return data_byte;
}
/**
* Pulls one or more bytes from the front of the FIFO, and removes them
* from the FIFO. If less bytes are available, only the available bytes
* are retrieved.
*
* @param b - pointer to FIFO_BUFFER structure
* @param buffer [out] - buffer to hold the pulled bytes
* @param length [in] - number of bytes to pull from the FIFO
*
* @return the number of bytes actually pulled from the FIFO
*/
unsigned FIFO_Pull(FIFO_BUFFER *b, uint8_t *buffer, unsigned length)
{
unsigned count;
uint8_t data_byte;
unsigned index;
count = FIFO_Count(b);
if (count > length) {
/* adjust to limit the number of bytes pulled */
count = length;
}
if (length > count) {
/* adjust the return value */
length = count;
}
while (count) {
index = b->tail % b->buffer_len;
data_byte = b->buffer[index];
b->tail++;
if (buffer) {
*buffer = data_byte;
buffer++;
}
count--;
}
return length;
}
/**
* Adds a byte of data to the FIFO
*
* @param b - pointer to FIFO_BUFFER structure
* @param data_byte [in] - data to put into the FIFO
*
* @return true on successful add, false if not added
*/
bool FIFO_Put(FIFO_BUFFER *b, uint8_t data_byte)
{
bool status = false; /* return value */
unsigned index;
if (b) {
/* limit the buffer to prevent overwriting */
if (!FIFO_Full(b)) {
index = b->head % b->buffer_len;
b->buffer[index] = data_byte;
b->head++;
status = true;
}
}
return status;
}
/**
* Adds one or more bytes of data to the FIFO
*
* @param b - pointer to FIFO_BUFFER structure
* @param buffer [out] - data bytes to add to the FIFO
* @param count [in] - number of bytes to add to the FIFO
*
* @return true if space available and added, false if not added
*/
bool FIFO_Add(FIFO_BUFFER *b, uint8_t *buffer, unsigned count)
{
bool status = false; /* return value */
unsigned index;
/* limit the buffer to prevent overwriting */
if (FIFO_Available(b, count) && buffer) {
while (count) {
index = b->head % b->buffer_len;
b->buffer[index] = *buffer;
b->head++;
buffer++;
count--;
}
status = true;
}
return status;
}
/**
* Flushes any data in the FIFO buffer
*
* @param b - pointer to FIFO_BUFFER structure
*
* @return none
*/
void FIFO_Flush(FIFO_BUFFER *b)
{
unsigned head; /* used to avoid volatile decision */
if (b) {
head = b->head;
b->tail = head;
}
}
/**
* Initializes the FIFO buffer with a data store
*
* @param b - pointer to FIFO_BUFFER structure
* @param buffer [in] - data bytes used to store bytes used by the FIFO
* @param buffer_len [in] - size of the buffer in bytes - must be power of 2.
*
* @return none
*/
void FIFO_Init(FIFO_BUFFER *b, volatile uint8_t *buffer, unsigned buffer_len)
{
if (b && buffer && buffer_len) {
b->head = 0;
b->tail = 0;
b->buffer = buffer;
b->buffer_len = buffer_len;
}
return;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include "ctest.h"
/**
* Unit Test for the FIFO buffer
*
* @param pTest - test tracking pointer
*/
void testFIFOBuffer(Test *pTest)
{
/* FIFO data structure */
FIFO_BUFFER test_buffer = { 0 };
/* FIFO data store. Note: size must be a power of two! */
volatile uint8_t data_store[64] = { 0 };
uint8_t add_data[40] = { "RoseSteveLouPatRachelJessicaDaniAmyHerb" };
uint8_t test_add_data[40] = { 0 };
uint8_t test_data = 0;
unsigned index = 0;
unsigned count = 0;
bool status = 0;
FIFO_Init(&test_buffer, data_store, sizeof(data_store));
ct_test(pTest, FIFO_Empty(&test_buffer));
/* load the buffer */
for (test_data = 0; test_data < sizeof(data_store); test_data++) {
ct_test(pTest, !FIFO_Full(&test_buffer));
ct_test(pTest, FIFO_Available(&test_buffer, 1));
status = FIFO_Put(&test_buffer, test_data);
ct_test(pTest, status == true);
ct_test(pTest, !FIFO_Empty(&test_buffer));
}
/* not able to put any more */
ct_test(pTest, FIFO_Full(&test_buffer));
ct_test(pTest, !FIFO_Available(&test_buffer, 1));
status = FIFO_Put(&test_buffer, 42);
ct_test(pTest, status == false);
/* unload the buffer */
for (index = 0; index < sizeof(data_store); index++) {
ct_test(pTest, !FIFO_Empty(&test_buffer));
test_data = FIFO_Peek(&test_buffer);
ct_test(pTest, test_data == index);
test_data = FIFO_Get(&test_buffer);
ct_test(pTest, test_data == index);
ct_test(pTest, FIFO_Available(&test_buffer, 1));
ct_test(pTest, !FIFO_Full(&test_buffer));
}
ct_test(pTest, FIFO_Empty(&test_buffer));
test_data = FIFO_Get(&test_buffer);
ct_test(pTest, test_data == 0);
test_data = FIFO_Peek(&test_buffer);
ct_test(pTest, test_data == 0);
ct_test(pTest, FIFO_Empty(&test_buffer));
/* test the ring around the buffer */
for (index = 0; index < sizeof(data_store); index++) {
ct_test(pTest, FIFO_Empty(&test_buffer));
ct_test(pTest, FIFO_Available(&test_buffer, 4));
for (count = 1; count < 4; count++) {
test_data = count;
status = FIFO_Put(&test_buffer, test_data);
ct_test(pTest, status == true);
ct_test(pTest, !FIFO_Empty(&test_buffer));
}
for (count = 1; count < 4; count++) {
ct_test(pTest, !FIFO_Empty(&test_buffer));
test_data = FIFO_Peek(&test_buffer);
ct_test(pTest, test_data == count);
test_data = FIFO_Get(&test_buffer);
ct_test(pTest, test_data == count);
}
}
ct_test(pTest, FIFO_Empty(&test_buffer));
/* test Add */
ct_test(pTest, FIFO_Available(&test_buffer, sizeof(add_data)));
status = FIFO_Add(&test_buffer, add_data, sizeof(add_data));
ct_test(pTest, status == true);
count = FIFO_Count(&test_buffer);
ct_test(pTest, count == sizeof(add_data));
ct_test(pTest, !FIFO_Empty(&test_buffer));
for (index = 0; index < sizeof(add_data); index++) {
/* unload the buffer */
ct_test(pTest, !FIFO_Empty(&test_buffer));
test_data = FIFO_Peek(&test_buffer);
ct_test(pTest, test_data == add_data[index]);
test_data = FIFO_Get(&test_buffer);
ct_test(pTest, test_data == add_data[index]);
}
ct_test(pTest, FIFO_Empty(&test_buffer));
/* test Pull */
ct_test(pTest, FIFO_Available(&test_buffer, sizeof(add_data)));
status = FIFO_Add(&test_buffer, add_data, sizeof(add_data));
ct_test(pTest, status == true);
count = FIFO_Count(&test_buffer);
ct_test(pTest, count == sizeof(add_data));
ct_test(pTest, !FIFO_Empty(&test_buffer));
count = FIFO_Pull(&test_buffer, &test_add_data[0], sizeof(test_add_data));
ct_test(pTest, FIFO_Empty(&test_buffer));
ct_test(pTest, count == sizeof(test_add_data));
for (index = 0; index < sizeof(add_data); index++) {
ct_test(pTest, test_add_data[index] == add_data[index]);
}
ct_test(pTest, FIFO_Available(&test_buffer, sizeof(add_data)));
status = FIFO_Add(&test_buffer, test_add_data, sizeof(add_data));
ct_test(pTest, status == true);
ct_test(pTest, !FIFO_Empty(&test_buffer));
for (index = 0; index < sizeof(add_data); index++) {
count = FIFO_Pull(&test_buffer, &test_add_data[0], 1);
ct_test(pTest, count == 1);
ct_test(pTest, test_add_data[0] == add_data[index]);
}
ct_test(pTest, FIFO_Empty(&test_buffer));
/* test flush */
status = FIFO_Add(&test_buffer, test_add_data, sizeof(test_add_data));
ct_test(pTest, status == true);
ct_test(pTest, !FIFO_Empty(&test_buffer));
FIFO_Flush(&test_buffer);
ct_test(pTest, FIFO_Empty(&test_buffer));
return;
}
#ifdef TEST_FIFO_BUFFER
/**
* Main program entry for Unit Test
*
* @return returns 0 on success, and non-zero on fail.
*/
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("FIFO Buffer", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testFIFOBuffer);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif
+85
View File
@@ -0,0 +1,85 @@
/**
* @file
* @author Steve Karg
* @date 2004
*/
#ifndef FIFO_H
#define FIFO_H
#include <stdint.h>
#include <stdbool.h>
/**
* FIFO data structure
*
* @{
*/
struct fifo_buffer_t {
/** first byte of data */
volatile unsigned head;
/** last byte of data */
volatile unsigned tail;
/** block of memory or array of data */
volatile uint8_t *buffer;
/** length of the data */
unsigned buffer_len;
};
typedef struct fifo_buffer_t FIFO_BUFFER;
/** @} */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
unsigned FIFO_Count(
FIFO_BUFFER const *b);
bool FIFO_Full(
FIFO_BUFFER const *b);
bool FIFO_Available(
FIFO_BUFFER const *b,
unsigned count);
bool FIFO_Empty(
FIFO_BUFFER const *b);
uint8_t FIFO_Peek(
FIFO_BUFFER const *b);
uint8_t FIFO_Get(
FIFO_BUFFER * b);
unsigned FIFO_Pull(
FIFO_BUFFER * b,
uint8_t * data_bytes,
unsigned length);
bool FIFO_Put(
FIFO_BUFFER * b,
uint8_t data_byte);
bool FIFO_Add(
FIFO_BUFFER * b,
uint8_t * data_bytes,
unsigned count);
void FIFO_Flush(
FIFO_BUFFER * b);
/* note: buffer_len must be a power of two */
void FIFO_Init(
FIFO_BUFFER * b,
volatile uint8_t * buffer,
unsigned buffer_len);
#ifdef TEST
#include "ctest.h"
void testFIFOBuffer(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+112
View File
@@ -0,0 +1,112 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2006 Steve Karg
This program 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.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdio.h>
#include <string.h>
#include "bacnet/basic/sys/filename.h"
/** @file filename.c Function for filename manipulation */
char *filename_remove_path(const char *filename_in)
{
char *filename_out = (char *)filename_in;
/* allow the device ID to be set */
if (filename_in) {
filename_out = strrchr(filename_in, '\\');
if (!filename_out) {
filename_out = strrchr(filename_in, '/');
}
/* go beyond the slash */
if (filename_out) {
filename_out++;
} else {
/* no slash in filename */
filename_out = (char *)filename_in;
}
}
return filename_out;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testFilename(Test *pTest)
{
char *data1 = "c:\\Joshua\\run";
char *data2 = "/home/Anna/run";
char *data3 = "c:\\Program Files\\Christopher\\run.exe";
char *data4 = "//Mary/data/run";
char *data5 = "bin\\run";
char *filename = NULL;
filename = filename_remove_path(data1);
ct_test(pTest, strcmp("run", filename) == 0);
filename = filename_remove_path(data2);
ct_test(pTest, strcmp("run", filename) == 0);
filename = filename_remove_path(data3);
ct_test(pTest, strcmp("run.exe", filename) == 0);
filename = filename_remove_path(data4);
ct_test(pTest, strcmp("run", filename) == 0);
filename = filename_remove_path(data5);
ct_test(pTest, strcmp("run", filename) == 0);
return;
}
#ifdef TEST_FILENAME
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("filename remove path", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testFilename);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_FILENAME */
#endif /* TEST */
+37
View File
@@ -0,0 +1,37 @@
/**************************************************************************
*
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************************/
#ifndef FILENAME_H
#define FILENAME_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
char *filename_remove_path(
const char *filename_in);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+118
View File
@@ -0,0 +1,118 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2003 Steve Karg
This program 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.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
/*#define TEST */
/*#define TEST_KEY */
#include "key.h"
/** @file key.c Tests (only) of key encoding/decoding. */
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
/* test the encode and decode macros */
void testKeys(Test *pTest)
{
int type, id;
int decoded_type, decoded_id;
KEY key;
for (type = 0; type < KEY_TYPE_MAX; type++) {
for (id = 0; id < KEY_ID_MAX; id++) {
key = KEY_ENCODE(type, id);
decoded_type = KEY_DECODE_TYPE(key);
decoded_id = KEY_DECODE_ID(key);
ct_test(pTest, decoded_type == type);
ct_test(pTest, decoded_id == id);
}
}
return;
}
/* test the encode and decode macros */
void testKeySample(Test *pTest)
{
int type, id;
int type_list[] = { 0, 1, KEY_TYPE_MAX / 2, KEY_TYPE_MAX - 1, -1 };
int id_list[] = { 0, 1, KEY_ID_MAX / 2, KEY_ID_MAX - 1, -1 };
int type_index = 0;
int id_index = 0;
int decoded_type, decoded_id;
KEY key;
while (type_list[type_index] != -1) {
while (id_list[id_index] != -1) {
type = type_list[type_index];
id = id_list[id_index];
key = KEY_ENCODE(type, id);
decoded_type = KEY_DECODE_TYPE(key);
decoded_id = KEY_DECODE_ID(key);
ct_test(pTest, decoded_type == type);
ct_test(pTest, decoded_id == id);
id_index++;
}
id_index = 0;
type_index++;
}
return;
}
#ifdef TEST_KEY
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("key", NULL);
/* add the individual tests */
/* rc = ct_addTestFunction(pTest, testKeys); */
/* assert(rc); */
rc = ct_addTestFunction(pTest, testKeySample);
assert(rc);
/* run all the tests */
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
/* completed testing - cleanup */
ct_destroy(pTest);
return 0;
}
#endif /* LOCAL_TEST */
#endif
+50
View File
@@ -0,0 +1,50 @@
/**************************************************************************
*
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************************/
#ifndef KEY_H
#define KEY_H
#include <stdint.h>
/* This file has the macros that encode and decode the */
/* keys for the keylist when used with BACnet Object Id's */
typedef uint32_t KEY;
/* assuming a 32 bit KEY */
#define KEY_TYPE_OFFSET 22 /* bits - for BACnet */
#define KEY_TYPE_MASK 0x000003FFL
#define KEY_ID_MASK 0x003FFFFFL
#define KEY_ID_MAX (KEY_ID_MASK + 1L)
#define KEY_TYPE_MAX (KEY_TYPE_MASK + 1L)
#define KEY_LAST(key) ((key & KEY_ID_MASK) == KEY_ID_MAX)
#define KEY_ENCODE(type,id) ( ((unsigned int)\
((unsigned int)(type) & KEY_TYPE_MASK) << KEY_TYPE_OFFSET) |\
((unsigned int)(id) & KEY_ID_MASK) )
#define KEY_DECODE_TYPE(key) ((int)(((unsigned int)(key) >> KEY_TYPE_OFFSET)\
& KEY_TYPE_MASK))
#define KEY_DECODE_ID(key) ((int)((unsigned int)(key) & KEY_ID_MASK))
#endif
+695
View File
@@ -0,0 +1,695 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2003 Steve Karg
This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
/** @file keylist.c Keyed Linked List Library */
/* */
/* This is an enhanced array of pointers to data. */
/* The list is sorted, indexed, and keyed. */
/* The array is much faster than a linked list. */
/* It stores a pointer to data, which you must */
/* malloc and free on your own, or just use */
/* static data */
#include <stdlib.h>
#include "bacnet/basic/sys/keylist.h" /* check for valid prototypes */
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
/******************************************************************** */
/* Generic node routines */
/******************************************************************** */
/* grab memory for a node */
static struct Keylist_Node *NodeCreate(void)
{
return calloc(1, sizeof(struct Keylist_Node));
}
/* grab memory for a list */
static struct Keylist *KeylistCreate(void)
{
return calloc(1, sizeof(struct Keylist));
}
/* check to see if the array is big enough for an addition */
/* or is too big when we are deleting and we can shrink */
/* returns TRUE if success, FALSE if failed */
static int CheckArraySize(OS_Keylist list)
{
int new_size = 0; /* set it up so that no size change is the default */
const int chunk = 8; /* minimum number of nodes to allocate memory for */
struct Keylist_Node **new_array; /* new array of nodes, if needed */
int i; /* counter */
if (!list) {
return FALSE;
}
/* indicates the need for more memory allocation */
if (list->count == list->size) {
new_size = list->size + chunk;
/* allow for shrinking memory */
} else if ((list->size > chunk) && (list->count < (list->size - chunk))) {
new_size = list->size - chunk;
}
if (new_size) {
/* Allocate more room for node pointer array */
new_array = calloc((size_t)new_size, sizeof(new_array));
/* See if we got the memory we wanted */
if (!new_array) {
return FALSE;
}
/* copy the nodes from the old array to the new array */
if (list->array) {
for (i = 0; i < list->count; i++) {
new_array[i] = list->array[i];
}
free(list->array);
}
list->array = new_array;
list->size = new_size;
}
return TRUE;
}
/* find the index of the key that we are looking for */
/* since it is sorted, we can optimize the search */
/* returns TRUE if found, and FALSE not found */
/* returns the found key and the index where it was found in parameters */
/* If the key is not found, the nearest index from the bottom will be returned,
*/
/* allowing the ability to find where an key should go into the list. */
static int FindIndex(OS_Keylist list, KEY key, int *pIndex)
{
struct Keylist_Node *node; /* holds the new node */
int left = 0; /* the left branch of tree, beginning of list */
int right = 0; /* the right branch on the tree, end of list */
int index = 0; /* our current search place in the array */
KEY current_key = 0; /* place holder for current node key */
int status = FALSE; /* return value */
if (!list || !list->array || !list->count) {
*pIndex = 0;
return (FALSE);
}
right = list->count - 1;
/* assume that the list is sorted */
do {
/* A binary search */
index = (left + right) / 2;
node = list->array[index];
if (!node) {
break;
}
current_key = node->key;
if (key < current_key) {
right = index - 1;
} else {
left = index + 1;
}
} while ((key != current_key) && (left <= right));
if (key == current_key) {
status = TRUE;
*pIndex = index;
}
else {
/* where the index should be */
if (key > current_key) {
*pIndex = index + 1;
} else {
*pIndex = index;
}
}
return (status);
}
/******************************************************************** */
/* list data functions */
/******************************************************************** */
/* inserts a node into its sorted position */
int Keylist_Data_Add(OS_Keylist list, KEY key, void *data)
{
struct Keylist_Node *node; /* holds the new node */
int index = -1; /* return value */
int i; /* counts through the array */
if (list && CheckArraySize(list)) {
/* figure out where to put the new node */
if (list->count) {
(void)FindIndex(list, key, &index);
if (index < 0) {
/* Add to the beginning of the list */
index = 0;
} else if (index > list->count) {
/* Add to the end of the list */
index = list->count;
}
/* Move all the items up to make room for the new one */
for (i = list->count; i > index; i--) {
list->array[i] = list->array[i - 1];
}
}
else {
index = 0;
}
/* create and add the node */
node = NodeCreate();
if (node) {
list->count++;
node->key = key;
node->data = data;
list->array[index] = node;
}
}
return index;
}
/* deletes a node specified by its index */
/* returns the data from the node */
void *Keylist_Data_Delete_By_Index(OS_Keylist list, int index)
{
struct Keylist_Node *node;
void *data = NULL;
if (list && list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
if (node)
data = node->data;
/* move the nodes to account for the deleted one */
if (list->count == 1) {
/* There is no node shifting to do */
} else if (index == (list->count - 1)) {
/* We are the last one */
/* There is no node shifting to do */
} else {
/* Move all the nodes down one */
int i; /* counter */
int count = list->count - 1;
for (i = index; i < count; i++) {
list->array[i] = list->array[i + 1];
}
}
list->count--;
if (node) {
free(node);
}
/* potentially reduce the size of the array */
(void)CheckArraySize(list);
}
return (data);
}
/* deletes a node specified by its key */
/* returns the data from the node */
void *Keylist_Data_Delete(OS_Keylist list, KEY key)
{
void *data = NULL; /* return value */
int index; /* where the node is in the array */
if (list) {
if (FindIndex(list, key, &index))
data = Keylist_Data_Delete_By_Index(list, index);
}
return data;
}
/* returns the data from last node, and removes it from the list */
void *Keylist_Data_Pop(OS_Keylist list)
{
void *data = NULL; /* return value */
int index; /* position in the array */
if (list && list->count) {
index = list->count - 1;
data = Keylist_Data_Delete_By_Index(list, index);
}
return data;
}
/* returns the data from the node specified by key */
void *Keylist_Data(OS_Keylist list, KEY key)
{
struct Keylist_Node *node = NULL;
int index = 0; /* used to look up the index of node */
if (list && list->array && list->count) {
if (FindIndex(list, key, &index))
node = list->array[index];
}
return node ? node->data : NULL;
}
/* returns the index from the node specified by key */
int Keylist_Index(OS_Keylist list, KEY key)
{
int index = -1; /* used to look up the index of node */
if (list && list->array && list->count) {
if (!FindIndex(list, key, &index)) {
index = -1;
}
}
return index;
}
/* returns the data specified by index */
void *Keylist_Data_Index(OS_Keylist list, int index)
{
struct Keylist_Node *node = NULL;
if (list && list->array && list->count && (index >= 0) &&
(index < list->count))
node = list->array[index];
return node ? node->data : NULL;
}
/* return the key at the given index */
KEY Keylist_Key(OS_Keylist list, int index)
{
KEY key = 0; /* return value */
struct Keylist_Node *node;
if (list && list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
if (node)
key = node->key;
}
return key;
}
/* returns the next empty key from the list */
KEY Keylist_Next_Empty_Key(OS_Keylist list, KEY key)
{
int index;
if (list) {
while (FindIndex(list, key, &index)) {
if (KEY_LAST(key))
break;
key++;
}
}
return key;
}
/* return the number of nodes in this list */
int Keylist_Count(OS_Keylist list)
{
return list->count;
}
/******************************************************************** */
/* Public List functions */
/******************************************************************** */
/* returns head of the list or NULL on failure. */
OS_Keylist Keylist_Create(void)
{
struct Keylist *list;
list = KeylistCreate();
if (list)
CheckArraySize(list);
return list;
}
/* delete specified list */
void Keylist_Delete(OS_Keylist list)
{ /* list number to be deleted */
if (list) {
/* clean out the list */
while (list->count) {
(void)Keylist_Data_Delete_By_Index(list, 0);
}
if (list->array)
free(list->array);
free(list);
}
return;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
/* test the FIFO */
static void testKeyListFIFO(Test *pTest)
{
OS_Keylist list;
KEY key;
int index;
char *data1 = "Joshua";
char *data2 = "Anna";
char *data3 = "Mary";
char *data;
list = Keylist_Create();
ct_test(pTest, list != NULL);
key = 0;
index = Keylist_Data_Add(list, key, data1);
ct_test(pTest, index == 0);
index = Keylist_Data_Add(list, key, data2);
ct_test(pTest, index == 0);
index = Keylist_Data_Add(list, key, data3);
ct_test(pTest, index == 0);
ct_test(pTest, Keylist_Count(list) == 3);
data = Keylist_Data_Pop(list);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
data = Keylist_Data_Pop(list);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data2) == 0);
data = Keylist_Data_Pop(list);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data3) == 0);
data = Keylist_Data_Pop(list);
ct_test(pTest, data == NULL);
data = Keylist_Data_Pop(list);
ct_test(pTest, data == NULL);
Keylist_Delete(list);
return;
}
/* test the FILO */
static void testKeyListFILO(Test *pTest)
{
OS_Keylist list;
KEY key;
int index;
char *data1 = "Joshua";
char *data2 = "Anna";
char *data3 = "Mary";
char *data;
list = Keylist_Create();
ct_test(pTest, list != NULL);
key = 0;
index = Keylist_Data_Add(list, key, data1);
ct_test(pTest, index == 0);
index = Keylist_Data_Add(list, key, data2);
ct_test(pTest, index == 0);
index = Keylist_Data_Add(list, key, data3);
ct_test(pTest, index == 0);
ct_test(pTest, Keylist_Count(list) == 3);
data = Keylist_Data_Delete_By_Index(list, 0);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data3) == 0);
data = Keylist_Data_Delete_By_Index(list, 0);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data2) == 0);
data = Keylist_Data_Delete_By_Index(list, 0);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
data = Keylist_Data_Delete_By_Index(list, 0);
ct_test(pTest, data == NULL);
data = Keylist_Data_Delete_By_Index(list, 0);
ct_test(pTest, data == NULL);
Keylist_Delete(list);
return;
}
static void testKeyListDataKey(Test *pTest)
{
OS_Keylist list;
KEY key;
KEY test_key;
int index;
char *data1 = "Joshua";
char *data2 = "Anna";
char *data3 = "Mary";
char *data;
list = Keylist_Create();
ct_test(pTest, list != NULL);
key = 1;
index = Keylist_Data_Add(list, key, data1);
ct_test(pTest, index == 0);
test_key = Keylist_Key(list, index);
ct_test(pTest, test_key == key);
key = 2;
index = Keylist_Data_Add(list, key, data2);
ct_test(pTest, index == 1);
test_key = Keylist_Key(list, index);
ct_test(pTest, test_key == key);
key = 3;
index = Keylist_Data_Add(list, key, data3);
ct_test(pTest, index == 2);
test_key = Keylist_Key(list, index);
ct_test(pTest, test_key == key);
ct_test(pTest, Keylist_Count(list) == 3);
/* look at the data */
key = 2;
data = Keylist_Data(list, key);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data2) == 0);
key = 1;
data = Keylist_Data(list, key);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
key = 3;
data = Keylist_Data(list, key);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data3) == 0);
/* work the data */
key = 2;
data = Keylist_Data_Delete(list, key);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data2) == 0);
data = Keylist_Data_Delete(list, key);
ct_test(pTest, data == NULL);
ct_test(pTest, Keylist_Count(list) == 2);
key = 1;
data = Keylist_Data(list, key);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
key = 3;
data = Keylist_Data(list, key);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data3) == 0);
/* cleanup */
do {
data = Keylist_Data_Pop(list);
} while (data);
Keylist_Delete(list);
return;
}
static void testKeyListDataIndex(Test *pTest)
{
OS_Keylist list;
KEY key;
int index;
char *data1 = "Joshua";
char *data2 = "Anna";
char *data3 = "Mary";
char *data;
list = Keylist_Create();
ct_test(pTest, list != NULL);
key = 0;
index = Keylist_Data_Add(list, key, data1);
ct_test(pTest, index == 0);
index = Keylist_Data_Add(list, key, data2);
ct_test(pTest, index == 0);
index = Keylist_Data_Add(list, key, data3);
ct_test(pTest, index == 0);
ct_test(pTest, Keylist_Count(list) == 3);
/* look at the data */
data = Keylist_Data_Index(list, 0);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data3) == 0);
data = Keylist_Data_Index(list, 1);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data2) == 0);
data = Keylist_Data_Index(list, 2);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
/* work the data */
data = Keylist_Data_Delete_By_Index(list, 1);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data2) == 0);
ct_test(pTest, Keylist_Count(list) == 2);
data = Keylist_Data_Index(list, 0);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data3) == 0);
data = Keylist_Data_Index(list, 1);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
data = Keylist_Data_Delete_By_Index(list, 1);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
data = Keylist_Data_Delete_By_Index(list, 1);
ct_test(pTest, data == NULL);
/* cleanup */
do {
data = Keylist_Data_Pop(list);
} while (data);
Keylist_Delete(list);
return;
}
/* test access of a lot of entries */
static void testKeyListLarge(Test *pTest)
{
int data1 = 42;
int *data;
OS_Keylist list;
KEY key;
int index;
const unsigned num_keys = 1024 * 16;
list = Keylist_Create();
if (!list)
return;
for (key = 0; key < num_keys; key++) {
index = Keylist_Data_Add(list, key, &data1);
}
for (key = 0; key < num_keys; key++) {
data = Keylist_Data(list, key);
ct_test(pTest, *data == data1);
}
for (index = 0; index < num_keys; index++) {
data = Keylist_Data_Index(list, index);
ct_test(pTest, *data == data1);
}
Keylist_Delete(list);
return;
}
/* test access of a lot of entries */
void testKeyList(Test *pTest)
{
bool rc;
/* individual tests */
rc = ct_addTestFunction(pTest, testKeyListFIFO);
assert(rc);
rc = ct_addTestFunction(pTest, testKeyListFILO);
assert(rc);
rc = ct_addTestFunction(pTest, testKeyListDataKey);
assert(rc);
rc = ct_addTestFunction(pTest, testKeyListDataIndex);
assert(rc);
rc = ct_addTestFunction(pTest, testKeyListLarge);
assert(rc);
}
#ifdef TEST_KEYLIST
int main(void)
{
Test *pTest;
pTest = ct_create("keylist", NULL);
testKeyList(pTest);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_KEYLIST */
#endif /* TEST */
+120
View File
@@ -0,0 +1,120 @@
/**************************************************************************
*
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************************/
#ifndef KEYLIST_H
#define KEYLIST_H
#include "key.h"
/* This is a key sorted linked list data library that */
/* uses a key or index to access the data. */
/* If the keys are duplicated, they can be added into the list like FIFO */
/* list data and datatype */
struct Keylist_Node {
KEY key; /* unique number that is sorted in the list */
void *data; /* pointer to some data that is stored */
};
typedef struct Keylist {
struct Keylist_Node **array; /* array of nodes */
int count; /* number of nodes in this list - more effecient than loop */
int size; /* number of available nodes on this list - can grow or shrink */
} KEYLIST_TYPE;
typedef KEYLIST_TYPE *OS_Keylist;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* returns head of the list or NULL on failure. */
OS_Keylist Keylist_Create(
void);
/* delete specified list */
/* note: you should pop all the nodes off the list first. */
void Keylist_Delete(
OS_Keylist list);
/* inserts a node into its sorted position */
/* returns the index where it was added */
int Keylist_Data_Add(
OS_Keylist list,
KEY key,
void *data);
/* deletes a node specified by its key */
/* returns the data from the node */
void *Keylist_Data_Delete(
OS_Keylist list,
KEY key);
/* deletes a node specified by its index */
/* returns the data from the node */
void *Keylist_Data_Delete_By_Index(
OS_Keylist list,
int index);
/* returns the data from last node, and removes it from the list */
void *Keylist_Data_Pop(
OS_Keylist list);
/* returns the data from the node specified by key */
void *Keylist_Data(
OS_Keylist list,
KEY key);
/* returns the index from the node specified by key */
int Keylist_Index(
OS_Keylist list,
KEY key);
/* returns the data specified by key */
void *Keylist_Data_Index(
OS_Keylist list,
int index);
/* return the key at the given index */
KEY Keylist_Key(
OS_Keylist list,
int index);
/* returns the next empty key from the list */
KEY Keylist_Next_Empty_Key(
OS_Keylist list,
KEY key);
/* returns the number of items in the list */
int Keylist_Count(
OS_Keylist list);
#ifdef TEST
#include "ctest.h"
void testKeyList(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+123
View File
@@ -0,0 +1,123 @@
/**
* @file
* @author Steve Karg
* @date 2009
* @brief Millisecond timer library header file.
*
* @section DESCRIPTION
*
* The mstimer library provides functions for setting, resetting and
* restarting timers, and for checking if a timer has expired. An
* application must "manually" check if its timers have expired; this
* is not done automatically.
*
* A timer is declared as a \c struct \c mstimer and all access to the
* timer is made by a pointer to the declared timer.
*
* Adapted from the Contiki operating system.
* Original Authors: Adam Dunkels <adam@sics.se>, Nicolas Tsiftes <nvt@sics.se>
*/
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#include "mstimer.h"
/**
* @brief Set a timer for a time sometime in the future
*
* This function is used to set a timer for a time sometime in the
* future. The function mstimer_expired() will evaluate to true after
* the timer has expired.
*
* @param t A pointer to the timer
* @param interval The interval before the timer expires.
*/
void mstimer_set(struct mstimer *t, unsigned long interval)
{
t->interval = interval;
t->start = mstimer_now();
}
/**
* @brief Reset the timer with the same interval.
*
* This function resets the timer with the same interval that was
* given to the mstimer_set() function. The start point of the interval
* is the exact time that the timer last expired. Therefore, this
* function will cause the timer to be stable over time, unlike the
* mstimer_restart() function.
*
* @param t A pointer to the timer.
* @sa mstimer_restart()
*/
void mstimer_reset(struct mstimer *t)
{
t->start += t->interval;
}
/**
* @brief Restart the timer from the current point in time
*
* This function restarts a timer with the same interval that was
* given to the mstimer_set() function. The timer will start at the
* current time.
*
* @note A periodic timer will drift if this function is used to reset
* it. For preioric timers, use the mstimer_reset() function instead.
* @param t A pointer to the timer.
* @sa mstimer_reset()
*/
void mstimer_restart(struct mstimer *t)
{
t->start = mstimer_now();
}
/**
* @brief Check if a timer has expired.
*
* This function tests if a timer has expired and returns true or
* false depending on its status.
*
* @param t A pointer to the timer
* @return Non-zero if the timer has expired, zero otherwise.
*/
int mstimer_expired(struct mstimer *t)
{
if (t->interval) {
return ((unsigned long)((mstimer_now()) - (t->start + t->interval)) <
((unsigned long)(~((unsigned long)0)) >> 1));
}
return 0;
}
/*---------------------------------------------------------------------------*/
/**
* The time until the timer expires
*
* This function returns the time until the timer expires.
*
* @param t A pointer to the timer
*
* @return The time until the timer expires
*
*/
unsigned long mstimer_remaining(struct mstimer *t)
{
return t->start + t->interval - mstimer_now();
}
/**
* The value of the interval
*
* This function returns the interval value
*
* @param t A pointer to the timer
*
* @return The interval value
*
*/
unsigned long mstimer_interval(struct mstimer *t)
{
return t->interval;
}
+66
View File
@@ -0,0 +1,66 @@
/**
* @file
* @author Steve Karg
* @date 2009
* @brief Millisecond timer library header file.
*
* @section DESCRIPTION
*
* The mstimer library provides functions for setting, resetting and
* restarting timers, and for checking if a timer has expired. An
* application must "manually" check if its timers have expired; this
* is not done automatically.
*
* A timer is declared as a \c struct \c mstimer and all access to the
* timer is made by a pointer to the declared timer.
*
* Adapted from the Contiki operating system.
* Original Authors: Adam Dunkels <adam@sics.se>, Nicolas Tsiftes <nvt@sics.se>
*/
#ifndef MSTIMER_H_
#define MSTIMER_H_
/**
* A timer.
*
* This structure is used for declaring a timer. The timer must be set
* with mstimer_set() before it can be used.
*
* \hideinitializer
*/
struct mstimer {
unsigned long start;
unsigned long interval;
};
typedef void (*mstimer_callback_function) (void);
/* callback data structure */
struct mstimer_callback_data_t;
struct mstimer_callback_data_t {
struct mstimer timer;
mstimer_callback_function callback;
struct mstimer_callback_data_t *next;
};
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void mstimer_set(struct mstimer *t, unsigned long interval);
void mstimer_reset(struct mstimer *t);
void mstimer_restart(struct mstimer *t);
int mstimer_expired(struct mstimer *t);
unsigned long mstimer_remaining(struct mstimer *t);
unsigned long mstimer_interval(struct mstimer *t);
/* HAL implementation */
unsigned long mstimer_now(void);
void mstimer_callback(
struct mstimer_callback_data_t *cb,
mstimer_callback_function callback,
unsigned long milliseconds);
void mstimer_init(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+823
View File
@@ -0,0 +1,823 @@
/**
* @file
* @author Steve Karg
* @date 2004
* @brief Generic ring buffer library for deeply embedded system.
*
* @section LICENSE
*
* This program 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.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*
* As a special exception, if other files instantiate templates or
* use macros or inline functions from this file, or you compile
* this file and link it with other works to produce a work based
* on this file, this file does not by itself cause the resulting
* work to be covered by the GNU General Public License. However
* the source code for this file must still be made available in
* accordance with section (3) of the GNU General Public License.
*
* This exception does not invalidate any other reasons why a work
* based on this file might be covered by the GNU General Public
* License.
*
* @section DESCRIPTION
*
* Generic ring buffer library for deeply embedded system.
* It uses a data store whose size is a power of 2 (8, 16, 32, 64, ...)
* and doesn't waste any data bytes. It has very low overhead, and
* utilizes modulo for indexing the data in the data store.
* It uses separate variables for consumer and producer so it can
* be used in multithreaded environment.
*
* See the unit tests for usage examples.
*
*/
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/basic/sys/ringbuf.h"
/**
* Returns the number of elements in the ring buffer
*
* @param b - pointer to RING_BUFFER structure
* @return Number of elements in the ring buffer
*/
unsigned Ringbuf_Count(RING_BUFFER const *b)
{
unsigned head, tail; /* used to avoid volatile decision */
if (b) {
head = b->head;
tail = b->tail;
return head - tail;
}
return 0;
}
/**
* Returns the empty/full status of the ring buffer
*
* @param b - pointer to RING_BUFFER structure
* @return true if the ring buffer is full, false if it is not.
*/
bool Ringbuf_Full(RING_BUFFER const *b)
{
return (b ? (Ringbuf_Count(b) == b->element_count) : true);
}
/**
* Returns the empty/full status of the ring buffer
*
* @param b - pointer to RING_BUFFER structure
* @return true if the ring buffer is empty, false if it is not.
*/
bool Ringbuf_Empty(RING_BUFFER const *b)
{
return (b ? (Ringbuf_Count(b) == 0) : true);
}
/**
* Updates the depth tracking in the ring buffer
*
* @param b - pointer to RING_BUFFER structure
*/
static void Ringbuf_Depth_Update(RING_BUFFER *b)
{
unsigned count;
if (b) {
count = Ringbuf_Count(b);
if (count > b->depth) {
b->depth = count;
}
}
}
/**
* Updates the depth tracking in the ring buffer
*
* @param b - pointer to RING_BUFFER structure
* @return largest number of items that have been in the ring buffer
*/
unsigned Ringbuf_Depth(RING_BUFFER const *b)
{
unsigned depth = 0;
if (b) {
depth = b->depth;
}
return depth;
}
/**
* Resets the depth tracking in the ring buffer
*
* @param b - pointer to RING_BUFFER structure
* @return largest number of items that have been in the ring buffer
*/
unsigned Ringbuf_Depth_Reset(RING_BUFFER *b)
{
unsigned depth = 0;
if (b) {
depth = b->depth;
b->depth = 0;
}
return depth;
}
/**
* Gets the capacity of the ring buffer (number of possible elements)
*
* @param b - pointer to RING_BUFFER structure
* @return largest number of items that have been in the ring buffer
*/
unsigned Ringbuf_Size(RING_BUFFER const *b)
{
unsigned count = 0;
if (b) {
count = b->element_count;
}
return count;
}
/**
* Looks at the data from the head of the list without removing it
*
* @param b - pointer to RING_BUFFER structure
* @return pointer to the data, or NULL if nothing in the list
*/
volatile uint8_t *Ringbuf_Peek(RING_BUFFER const *b)
{
volatile uint8_t *data_element = NULL; /* return value */
if (!Ringbuf_Empty(b)) {
data_element = b->buffer;
data_element += ((b->tail % b->element_count) * b->element_size);
}
return data_element;
}
/**
* Looks at the data from the next element of the list without removing it
*
* @param b - pointer to RING_BUFFER structure
* @param data_element - find the next element from this one
* @return pointer to the data, or NULL if nothing in the list
*/
volatile uint8_t *Ringbuf_Peek_Next(RING_BUFFER const *b, uint8_t *data_element)
{
unsigned index; /* list index */
volatile uint8_t *this_element;
volatile uint8_t *next_element = NULL; /* return value */
if (!Ringbuf_Empty(b) && data_element != NULL) {
/* Use (b->head-1) here to avoid walking off end of ring */
for (index = b->tail; index < b->head - 1; index++) {
/* Find the specified data_element */
this_element =
b->buffer + ((index % b->element_count) * b->element_size);
if (data_element == this_element) {
/* Found the current element, get the next one on the list */
next_element = b->buffer +
(((index + 1) % b->element_count) * b->element_size);
break;
}
}
}
return next_element;
}
/**
* Copy the data from the front of the list, and removes it
*
* @param b - pointer to RING_BUFFER structure
* @param data_element - element of data that is loaded with data from ring
* @return true if data was copied, false if list is empty
*/
bool Ringbuf_Pop(RING_BUFFER *b, uint8_t *data_element)
{
bool status = false; /* return value */
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
unsigned i; /* loop counter */
if (!Ringbuf_Empty(b)) {
ring_data = b->buffer;
ring_data += ((b->tail % b->element_count) * b->element_size);
if (data_element) {
for (i = 0; i < b->element_size; i++) {
data_element[i] = ring_data[i];
}
}
b->tail++;
status = true;
}
return status;
}
/**
* Copy the data from the specified element, and removes it and moves other
* elements up the list
*
* @param b - pointer to RING_BUFFER structure
* @param this_element - element to find
* @param data_element - element of data that is loaded with data from ring
* @return true if data was copied, false if list is empty
*/
bool Ringbuf_Pop_Element(
RING_BUFFER *b, uint8_t *this_element, uint8_t *data_element)
{
bool status = false; /* return value */
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
volatile uint8_t *prev_data;
unsigned index; /* list index */
unsigned this_index = b->head; /* index of element to remove */
unsigned i; /* loop counter */
if (!Ringbuf_Empty(b) && this_element != NULL) {
for (index = b->tail; index < b->head; index++) {
/* Find the specified data_element */
ring_data =
b->buffer + ((index % b->element_count) * b->element_size);
if (this_element == ring_data) {
/* Found the specified element, copy the data if required */
this_index = index;
if (data_element) {
for (i = 0; i < b->element_size; i++) {
data_element[i] = ring_data[i];
}
}
break;
}
}
if (this_index < b->head) {
/* Found a match, move elements up the list to fill the gap */
for (index = this_index; index > b->tail; index--) {
/* Get pointers to current and previous data_elements */
ring_data =
b->buffer + ((index % b->element_count) * b->element_size);
prev_data = b->buffer +
(((index - 1) % b->element_count) * b->element_size);
for (i = 0; i < b->element_size; i++) {
ring_data[i] = prev_data[i];
}
}
}
b->tail++;
status = true;
}
return status;
}
/**
* Adds an element of data to the ring buffer
*
* @param b - pointer to RING_BUFFER structure
* @param data_element - one element that is copied to the ring buffer
* @return true on succesful add, false if not added
*/
bool Ringbuf_Put(RING_BUFFER *b, uint8_t *data_element)
{
bool status = false; /* return value */
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
unsigned i; /* loop counter */
if (b && data_element) {
/* limit the amount of elements that we accept */
if (!Ringbuf_Full(b)) {
ring_data = b->buffer;
ring_data += ((b->head % b->element_count) * b->element_size);
for (i = 0; i < b->element_size; i++) {
ring_data[i] = data_element[i];
}
b->head++;
Ringbuf_Depth_Update(b);
status = true;
}
}
return status;
}
/**
* Adds an element of data to the front of the ring buffer
*
* Note that this function moves the tail on add instead of head,
* so this function cannot be used if you are keeping producer and
* consumer as separate processes (i.e. interrupt handlers)
*
* @param b - pointer to RING_BUFFER structure
* @param data_element - one element to copy to the front of the ring
* @return true on succesful add, false if not added
*/
bool Ringbuf_Put_Front(RING_BUFFER *b, uint8_t *data_element)
{
bool status = false; /* return value */
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
unsigned i = 0; /* loop counter */
if (b && data_element) {
/* limit the amount of elements that we accept */
if (!Ringbuf_Full(b)) {
b->tail--;
ring_data = b->buffer;
ring_data += ((b->tail % b->element_count) * b->element_size);
/* copy the data to the ring data element */
for (i = 0; i < b->element_size; i++) {
ring_data[i] = data_element[i];
}
Ringbuf_Depth_Update(b);
status = true;
}
}
return status;
}
/**
* Gets a pointer to the next free data element of the buffer
* without adding it to the ring.
*
* @param b - pointer to RING_BUFFER structure
* @return pointer to the next data element, or NULL if the list is full
*/
volatile uint8_t *Ringbuf_Data_Peek(RING_BUFFER *b)
{
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
if (b) {
/* limit the amount of elements that we accept */
if (!Ringbuf_Full(b)) {
ring_data = b->buffer;
ring_data += ((b->head % b->element_count) * b->element_size);
}
}
return ring_data;
}
/**
* Adds the previously peeked element of data to the end of the
* ring buffer.
*
* @param b - pointer to RING_BUFFER structure
* @param data_element - pointer to the peeked data element
* @return true if the buffer has space and the data element points to the
* same memory previously peeked.
*/
bool Ringbuf_Data_Put(RING_BUFFER *b, volatile uint8_t *data_element)
{
bool status = false;
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
if (b) {
/* limit the amount of elements that we accept */
if (!Ringbuf_Full(b)) {
ring_data = b->buffer;
ring_data += ((b->head % b->element_count) * b->element_size);
if (ring_data == data_element) {
/* same chunk of memory - okay to signal the head */
b->head++;
Ringbuf_Depth_Update(b);
status = true;
}
}
}
return status;
}
/**
* Test that the parameter is a power of two.
*
* @param x unsigned integer value to be tested
*
* @return true if the parameter is a power of 2
*/
static bool isPowerOfTwo(unsigned int x)
{
/* First x in the below expression is for the case when x is 0 */
return x && (!(x & (x - 1)));
}
/**
* Configures the ring buffer data buffer. Note that the element_count
* parameter must be a power of two.
*
* @param b - pointer to RING_BUFFER structure
* @param buffer - pointer to a data buffer that is used to store the ring data
* @param element_size - size of one element in the data block
* @param element_count - number elements in the data block
*
* @return true if ring buffer was initialized
*/
bool Ringbuf_Init(RING_BUFFER *b,
volatile uint8_t *buffer,
unsigned element_size,
unsigned element_count)
{
bool status = false;
if (b && isPowerOfTwo(element_count)) {
b->head = 0;
b->tail = 0;
b->buffer = buffer;
b->element_size = element_size;
b->element_count = element_count;
/* tuning diagnostics */
b->depth = 0;
status = true;
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include <limits.h>
#include "ctest.h"
/**
* Unit Test for the ring buffer
*
* @param pTest - test tracking pointer
* @param test_buffer - pointer to RING_BUFFER structure
* @param data_element - one data element
* @param element_size - size of one data element
* @param element_count - number of data elements in the store
*/
static void testRingAroundBuffer(Test *pTest,
RING_BUFFER *test_buffer,
uint8_t *data_element,
unsigned element_size,
unsigned element_count)
{
volatile uint8_t *test_data;
unsigned index;
unsigned data_index;
unsigned count;
uint8_t value;
bool status;
ct_test(pTest, Ringbuf_Empty(test_buffer));
/* test the ring around the buffer */
for (index = 0; index < element_count; index++) {
for (count = 1; count < 4; count++) {
value = (index * count) % 255;
for (data_index = 0; data_index < element_size; data_index++) {
data_element[data_index] = value;
}
status = Ringbuf_Put(test_buffer, data_element);
ct_test(pTest, status == true);
ct_test(pTest, Ringbuf_Count(test_buffer) == count);
}
for (count = 1; count < 4; count++) {
value = (index * count) % 255;
test_data = Ringbuf_Peek(test_buffer);
ct_test(pTest, test_data);
if (test_data) {
for (data_index = 0; data_index < element_size; data_index++) {
ct_test(pTest, test_data[data_index] == value);
}
}
status = Ringbuf_Pop(test_buffer, NULL);
ct_test(pTest, status == true);
}
}
ct_test(pTest, Ringbuf_Empty(test_buffer));
}
/**
* Unit Test for the ring buffer
*
* @param pTest - test tracking pointer
* @param data_store - buffer to store elements
* @param data_element - one data element
* @param element_size - size of one data element
* @param element_count - number of data elements in the store
*/
static bool testRingBuf(Test *pTest,
uint8_t *data_store,
uint8_t *data_element,
unsigned element_size,
unsigned element_count)
{
RING_BUFFER test_buffer;
volatile uint8_t *test_data;
unsigned index;
unsigned data_index;
bool status;
status =
Ringbuf_Init(&test_buffer, data_store, element_size, element_count);
if (!status) {
return false;
}
ct_test(pTest, Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 0);
for (data_index = 0; data_index < element_size; data_index++) {
data_element[data_index] = data_index;
}
status = Ringbuf_Put(&test_buffer, data_element);
ct_test(pTest, status == true);
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 1);
test_data = Ringbuf_Peek(&test_buffer);
for (data_index = 0; data_index < element_size; data_index++) {
ct_test(pTest, test_data[data_index] == data_element[data_index]);
}
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
(void)Ringbuf_Pop(&test_buffer, NULL);
ct_test(pTest, Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 1);
/* fill to max */
for (index = 0; index < element_count; index++) {
for (data_index = 0; data_index < element_size; data_index++) {
data_element[data_index] = index;
}
status = Ringbuf_Put(&test_buffer, data_element);
ct_test(pTest, status == true);
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == (index + 1));
}
ct_test(pTest, Ringbuf_Depth(&test_buffer) == element_count);
/* verify actions on full buffer */
for (index = 0; index < element_count; index++) {
for (data_index = 0; data_index < element_size; data_index++) {
data_element[data_index] = index;
}
status = Ringbuf_Put(&test_buffer, data_element);
ct_test(pTest, status == false);
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == element_count);
}
/* check buffer full */
for (index = 0; index < element_count; index++) {
test_data = Ringbuf_Peek(&test_buffer);
ct_test(pTest, test_data);
if (test_data) {
for (data_index = 0; data_index < element_size; data_index++) {
ct_test(pTest, test_data[data_index] == index);
}
}
(void)Ringbuf_Pop(&test_buffer, NULL);
}
ct_test(pTest, Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == element_count);
Ringbuf_Depth_Reset(&test_buffer);
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 0);
testRingAroundBuffer(
pTest, &test_buffer, data_element, element_size, element_count);
/* adjust the internal index of Ringbuf to test unsigned wrapping */
test_buffer.head = UINT_MAX - 1;
test_buffer.tail = UINT_MAX - 1;
testRingAroundBuffer(
pTest, &test_buffer, data_element, element_size, element_count);
return true;
}
/**
* Unit Test for the ring buffer with 16 data elements
*
* @param pTest - test tracking pointer
*/
void testRingBufSizeSmall(Test *pTest)
{
bool status;
uint8_t data_element[5];
uint8_t data_store[sizeof(data_element) * NEXT_POWER_OF_2(16)];
status = testRingBuf(pTest, data_store, data_element, sizeof(data_element),
sizeof(data_store) / sizeof(data_element));
ct_test(pTest, status);
}
/**
* Unit Test for the ring buffer with 32 data elements
*
* @param pTest - test tracking pointer
*/
void testRingBufSizeLarge(Test *pTest)
{
bool status;
uint8_t data_element[16];
uint8_t data_store[sizeof(data_element) * NEXT_POWER_OF_2(99)];
status = testRingBuf(pTest, data_store, data_element, sizeof(data_element),
sizeof(data_store) / sizeof(data_element));
ct_test(pTest, status);
}
/**
* Unit Test for the ring buffer with 32 data elements
*
* @param pTest - test tracking pointer
*/
void testRingBufSizeInvalid(Test *pTest)
{
bool status;
uint8_t data_element[16];
uint8_t data_store[sizeof(data_element) * 99];
status = testRingBuf(pTest, data_store, data_element, sizeof(data_element),
sizeof(data_store) / sizeof(data_element));
ct_test(pTest, status == false);
}
void testRingBufPowerOfTwo(Test *pTest)
{
ct_test(pTest, NEXT_POWER_OF_2(3) == 4);
ct_test(pTest, NEXT_POWER_OF_2(100) == 128);
ct_test(pTest, NEXT_POWER_OF_2(127) == 128);
ct_test(pTest, NEXT_POWER_OF_2(128) == 128);
ct_test(pTest, NEXT_POWER_OF_2(129) == 256);
ct_test(pTest, NEXT_POWER_OF_2(300) == 512);
ct_test(pTest, NEXT_POWER_OF_2(500) == 512);
}
/**
* Unit Test for the ring buffer peek/pop next element
*
* @param pTest - test tracking pointer
* @param data_store - buffer to store elements
* @param data_element - one data element
* @param element_size - size of one data element
* @param element_count - number of data elements in the store
*/
static bool testRingBufNextElement(Test *pTest,
uint8_t *data_store,
uint8_t *data_element,
unsigned element_size,
unsigned element_count)
{
RING_BUFFER test_buffer;
volatile uint8_t *test_data;
unsigned index;
unsigned data_index;
bool status;
status =
Ringbuf_Init(&test_buffer, data_store, element_size, element_count);
if (!status) {
return false;
}
ct_test(pTest, Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 0);
for (data_index = 0; data_index < element_size; data_index++) {
data_element[data_index] = data_index;
}
status = Ringbuf_Put(&test_buffer, data_element);
ct_test(pTest, status == true);
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 1);
test_data = Ringbuf_Peek(&test_buffer);
for (data_index = 0; data_index < element_size; data_index++) {
ct_test(pTest, test_data[data_index] == data_element[data_index]);
}
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
(void)Ringbuf_Pop(&test_buffer, NULL);
ct_test(pTest, Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 1);
/* fill to max */
for (index = 0; index < element_count; index++) {
for (data_index = 0; data_index < element_size; data_index++) {
data_element[data_index] = index;
}
status = Ringbuf_Put(&test_buffer, data_element);
ct_test(pTest, status == true);
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == (index + 1));
}
ct_test(pTest, Ringbuf_Depth(&test_buffer) == element_count);
ct_test(pTest, Ringbuf_Count(&test_buffer) == element_count);
/* Walk through ring buffer */
test_data = Ringbuf_Peek(&test_buffer);
ct_test(pTest, test_data);
for (index = 1; index < element_count; index++) {
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
ct_test(pTest, test_data);
if (test_data) {
for (data_index = 0; data_index < element_size; data_index++) {
ct_test(pTest, test_data[data_index] == index);
}
}
}
ct_test(pTest, Ringbuf_Count(&test_buffer) == element_count);
/* Try to walk off end of buffer - should return NULL */
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
ct_test(pTest, (test_data == NULL));
/* Walk through ring buffer and pop alternate elements */
test_data = Ringbuf_Peek(&test_buffer);
ct_test(pTest, test_data);
for (index = 1; index < element_count / 2; index++) {
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
ct_test(pTest, test_data);
(void)Ringbuf_Pop_Element(&test_buffer, (uint8_t *)test_data, NULL);
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
}
ct_test(pTest, Ringbuf_Count(&test_buffer) == element_count / 2 + 1);
/* Walk through ring buffer and check data */
test_data = Ringbuf_Peek(&test_buffer);
ct_test(pTest, test_data);
for (index = 0; index < element_count / 2; index++) {
if (test_data) {
for (data_index = 0; data_index < element_size; data_index++) {
ct_test(pTest, test_data[data_index] == index * 2);
}
}
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
ct_test(pTest, test_data);
}
ct_test(pTest, Ringbuf_Count(&test_buffer) == element_count / 2 + 1);
return true;
}
/**
* Unit Test for the ring buffer with 16 data elements
*
* @param pTest - test tracking pointer
*/
void testRingBufNextElementSizeSmall(Test *pTest)
{
bool status;
uint8_t data_element[5];
uint8_t data_store[sizeof(data_element) * NEXT_POWER_OF_2(16)];
status = testRingBufNextElement(pTest, data_store, data_element,
sizeof(data_element), sizeof(data_store) / sizeof(data_element));
ct_test(pTest, status);
}
#ifdef TEST_RING_BUFFER
/**
* Main program entry for Unit Test
*
* @return returns 0 on success, and non-zero on fail.
*/
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("Ring Buffer", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testRingBufPowerOfTwo);
assert(rc);
rc = ct_addTestFunction(pTest, testRingBufSizeSmall);
assert(rc);
rc = ct_addTestFunction(pTest, testRingBufSizeLarge);
assert(rc);
rc = ct_addTestFunction(pTest, testRingBufSizeInvalid);
assert(rc);
rc = ct_addTestFunction(pTest, testRingBufNextElementSizeSmall);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif
+96
View File
@@ -0,0 +1,96 @@
/**
* @file
* @author Steve Karg
* @date 2004
*
* Generic ring buffer library for deeply embedded system.
* See the unit tests for usage examples.
*/
#ifndef RINGBUF_H
#define RINGBUF_H
#include <stdint.h>
#include <stdbool.h>
/**
* ring buffer power of two alignment macro
*
* @{
*/
#ifndef NEXT_POWER_OF_2
#define B2(x) ( (x) | ( (x) >> 1) )
#define B4(x) ( B2(x) | ( B2(x) >> 2) )
#define B8(x) ( B4(x) | ( B4(x) >> 4) )
#define B16(x) ( B8(x) | ( B8(x) >> 8) )
#define B32(x) (B16(x) | (B16(x) >>16) )
#define NEXT_POWER_OF_2(x) (B32((x)-1) + 1)
#endif
/** @} */
/**
* ring buffer data structure
*
* @{
*/
struct ring_buffer_t {
/** block of memory or array of data */
volatile uint8_t *buffer;
/** how many bytes for each chunk */
unsigned element_size;
/** number of chunks of data */
unsigned element_count;
/** where the writes go */
volatile unsigned head;
/** where the reads come from */
volatile unsigned tail;
/* maximum depth reached */
volatile unsigned depth;
};
typedef struct ring_buffer_t RING_BUFFER;
/** @} */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
unsigned Ringbuf_Count(RING_BUFFER const *b);
unsigned Ringbuf_Depth(RING_BUFFER const *b);
unsigned Ringbuf_Depth_Reset(RING_BUFFER *b);
unsigned Ringbuf_Size(RING_BUFFER const *b);
bool Ringbuf_Full(RING_BUFFER const *b);
bool Ringbuf_Empty(RING_BUFFER const *b);
/* tail */
volatile uint8_t *Ringbuf_Peek(RING_BUFFER const *b);
bool Ringbuf_Pop(RING_BUFFER * b,
uint8_t * data_element);
bool Ringbuf_Pop_Element(RING_BUFFER * b,
uint8_t * this_element,
uint8_t * data_element);
bool Ringbuf_Put_Front(RING_BUFFER * b,
uint8_t * data_element);
/* head */
bool Ringbuf_Put(RING_BUFFER * b,
uint8_t * data_element);
/* pair of functions to use head memory directly */
volatile uint8_t *Ringbuf_Data_Peek(RING_BUFFER * b);
volatile uint8_t *Ringbuf_Peek_Next(RING_BUFFER const *b,
uint8_t * data_element);
bool Ringbuf_Data_Put(RING_BUFFER * b, volatile uint8_t *data_element);
/* Note: element_count must be a power of two */
bool Ringbuf_Init(RING_BUFFER * b,
volatile uint8_t * buffer,
unsigned element_size,
unsigned element_count);
#ifdef TEST
#include "ctest.h"
void testRingBufPowerOfTwo(Test * pTest);
void testRingBufSizeSmall(Test * pTest);
void testRingBufSizeLarge(Test * pTest);
void testRingBufSizeInvalid(Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+212
View File
@@ -0,0 +1,212 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 by Steve Karg
This program 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.
59 Temple Place - Suite 330
Boston, MA 02111-1307
USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
/** @file sbuf.c Static buffer library for deeply embedded system. */
/* Functional Description: Static buffer library for deeply
embedded system. See the unit tests for usage examples. */
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "sbuf.h"
void sbuf_init(STATIC_BUFFER *b, /* static buffer structure */
char *data, /* data block */
unsigned size)
{ /* actual size, in bytes, of the data block or array of data */
if (b) {
b->data = data;
b->size = size;
b->count = 0;
}
return;
}
/* returns true if count==0, false if count > 0 */
bool sbuf_empty(STATIC_BUFFER const *b)
{
return (b ? (b->count == 0) : false);
}
char *sbuf_data(STATIC_BUFFER const *b)
{
return (b ? b->data : NULL);
}
unsigned sbuf_size(STATIC_BUFFER *b)
{
return (b ? b->size : 0);
}
unsigned sbuf_count(STATIC_BUFFER *b)
{
return (b ? b->count : 0);
}
/* returns true if successful, false if not enough room to append data */
bool sbuf_put(STATIC_BUFFER *b, /* static buffer structure */
unsigned offset, /* where to start */
char *data, /* data to place in buffer */
unsigned data_size)
{ /* how many bytes to add */
bool status = false; /* return value */
if (b && b->data) {
if (((offset + data_size) < b->size)) {
b->count = offset + data_size;
while (data_size) {
b->data[offset] = *data;
offset++;
data++;
data_size--;
}
status = true;
}
}
return status;
}
/* returns true if successful, false if not enough room to append data */
bool sbuf_append(STATIC_BUFFER *b, /* static buffer structure */
char *data, /* data to place in buffer */
unsigned data_size)
{ /* how many bytes to add */
unsigned count = 0;
if (b) {
count = b->count;
}
return sbuf_put(b, count, data, data_size);
}
/* returns true if successful, false if not enough room to append data */
bool sbuf_truncate(STATIC_BUFFER *b, /* static buffer structure */
unsigned count)
{ /* total number of bytes in to remove */
bool status = false; /* return value */
if (b) {
if (count < b->size) {
b->count = count;
status = true;
}
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testStaticBuffer(Test *pTest)
{
STATIC_BUFFER sbuffer;
char *data1 = "Joshua";
char *data2 = "Anna";
char *data3 = "Christopher";
char *data4 = "Mary";
char data_buffer[480] = "";
char test_data_buffer[480] = "";
char *data;
unsigned count;
sbuf_init(&sbuffer, NULL, 0);
ct_test(pTest, sbuf_empty(&sbuffer) == true);
ct_test(pTest, sbuf_data(&sbuffer) == NULL);
ct_test(pTest, sbuf_size(&sbuffer) == 0);
ct_test(pTest, sbuf_count(&sbuffer) == 0);
ct_test(pTest, sbuf_append(&sbuffer, data1, strlen(data1)) == false);
sbuf_init(&sbuffer, data_buffer, sizeof(data_buffer));
ct_test(pTest, sbuf_empty(&sbuffer) == true);
ct_test(pTest, sbuf_data(&sbuffer) == data_buffer);
ct_test(pTest, sbuf_size(&sbuffer) == sizeof(data_buffer));
ct_test(pTest, sbuf_count(&sbuffer) == 0);
ct_test(pTest, sbuf_append(&sbuffer, data1, strlen(data1)) == true);
ct_test(pTest, sbuf_append(&sbuffer, data2, strlen(data2)) == true);
ct_test(pTest, sbuf_append(&sbuffer, data3, strlen(data3)) == true);
ct_test(pTest, sbuf_append(&sbuffer, data4, strlen(data4)) == true);
strcat(test_data_buffer, data1);
strcat(test_data_buffer, data2);
strcat(test_data_buffer, data3);
strcat(test_data_buffer, data4);
ct_test(pTest, sbuf_count(&sbuffer) == strlen(test_data_buffer));
data = sbuf_data(&sbuffer);
count = sbuf_count(&sbuffer);
ct_test(pTest, memcmp(data, test_data_buffer, count) == 0);
ct_test(pTest, count == strlen(test_data_buffer));
ct_test(pTest, sbuf_truncate(&sbuffer, 0) == true);
ct_test(pTest, sbuf_count(&sbuffer) == 0);
ct_test(pTest, sbuf_size(&sbuffer) == sizeof(data_buffer));
ct_test(pTest, sbuf_append(&sbuffer, data4, strlen(data4)) == true);
data = sbuf_data(&sbuffer);
count = sbuf_count(&sbuffer);
ct_test(pTest, memcmp(data, data4, count) == 0);
ct_test(pTest, count == strlen(data4));
return;
}
#ifdef TEST_STATIC_BUFFER
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("static buffer", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testStaticBuffer);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_STATIC_BUFFER */
#endif /* TEST */
+80
View File
@@ -0,0 +1,80 @@
/**************************************************************************
*
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************************/
#ifndef SBUF_H
#define SBUF_H
/* Functional Description: Static buffer library for deeply
embedded system. See the unit tests for usage examples. */
#include <stdint.h>
#include <stdbool.h>
struct static_buffer_t {
char *data; /* block of memory or array of data */
unsigned size; /* actual size, in bytes, of the block of data */
unsigned count; /* number of bytes in use */
};
typedef struct static_buffer_t STATIC_BUFFER;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void sbuf_init(
STATIC_BUFFER * b, /* static buffer structure */
char *data, /* data block */
unsigned size); /* actual size, in bytes, of the data block or array of data */
/* returns true if size==0, false if size > 0 */
bool sbuf_empty(
STATIC_BUFFER const *b);
/* returns the data block, or NULL if not initialized */
char *sbuf_data(
STATIC_BUFFER const *b);
/* returns the max size of the data block */
unsigned sbuf_size(
STATIC_BUFFER * b);
/* returns the number of bytes used in the data block */
unsigned sbuf_count(
STATIC_BUFFER * b);
/* returns true if successful, false if not enough room to append data */
bool sbuf_put(
STATIC_BUFFER * b, /* static buffer structure */
unsigned offset, /* where to start */
char *data, /* data to add */
unsigned data_size); /* how many to add */
/* returns true if successful, false if not enough room to append data */
bool sbuf_append(
STATIC_BUFFER * b, /* static buffer structure */
char *data, /* data to append */
unsigned data_size); /* how many to append */
/* returns true if successful, false if count is bigger than size */
bool sbuf_truncate(
STATIC_BUFFER * b, /* static buffer structure */
unsigned count); /* new number of bytes used in buffer */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif