549 lines
19 KiB
C

/*
* Copyright (c) 2012-2022 DSR Corporation, Denver CO, USA
* Copyright (c) 2021-2022 Espressif Systems (Shanghai) PTE LTD
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Espressif Systems
* integrated circuit in a product or a software update for such product,
* must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* 4. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* PURPOSE: Declare ring buffer internals
*/
#ifndef ZB_RINGBUFFER_H
#define ZB_RINGBUFFER_H 1
/*! @cond internals_doc */
/**
@addtogroup ZB_BASE
@{
*/
/**
\par Generic ring buffer macros
*/
/**
Declare ring buffer for entries of given type and capacity.
This is typedef, not variable declaration.
@param type_name_prefix - prefix for names (like xxx_s, xxx_t)
@param ent_type - type of the ring buffer entry
@param capacity - ring buffer capacity
*/
#define ZB_RING_BUFFER_DECLARE(type_name_prefix, ent_type, capacity) \
typedef struct type_name_prefix ## _s \
{ \
zb_ushort_t read_i; \
zb_ushort_t write_i; \
zb_ushort_t written; \
ent_type ring_buf[capacity]; \
} type_name_prefix ## _t
/**
* Initialize ring buffer internals
*/
#define ZB_RING_BUFFER_INIT(rb) ( (rb)->read_i = (rb)->write_i = (rb)->written = 0U)
/**
* Return ring buffer capacity
*
* @param rb - ring buffer pointer.
*/
#define ZB_RING_BUFFER_CAPACITY(rb) ((sizeof((rb)->ring_buf) / sizeof((rb)->ring_buf[0])))
/**
* Return 1 if ring buffer is full
*
* @param rb - ring buffer pointer.
*/
#define ZB_RING_BUFFER_IS_FULL(rb) ((zb_uint_t)(rb)->written >= ZB_RING_BUFFER_CAPACITY(rb))
/**
* Return 1 if ring buffer is empty
*
* @param rb - ring buffer pointer.
*/
#define ZB_RING_BUFFER_IS_EMPTY(rb) ((rb)->written == 0U)
/**
* Return free space available in the ring buffer
*
* @param rb - ring buffer pointer.
*/
#define ZB_RING_BUFFER_FREE_SPACE(rb) (ZB_RING_BUFFER_CAPACITY(rb) - (zb_uint_t)(rb)->written)
/**
* Reserve slot in the ring buffer but do not update pointers
*
* @param rb - ring buffer pointer.
* @return Pointer to the ring buffer entry or NULL if ring buffer is full
*/
#define ZB_RING_BUFFER_PUT_RESERVE(rb) \
( \
ZB_RING_BUFFER_IS_FULL(rb) ? NULL \
: (rb)->ring_buf + (rb)->write_i \
)
#define ZB_RING_BUFFER_PUT_HEAD_RESERVE_IDX(rb) \
( \
((rb)->read_i > 0U) ? \
((rb)->read_i - 1U) : (ZB_RING_BUFFER_CAPACITY(rb) - 1U) \
)
#define ZB_RING_BUFFER_PUT_HEAD_RESERVE(rb) \
( \
ZB_RING_BUFFER_IS_FULL(rb) ? NULL \
: ((rb)->ring_buf + ZB_RING_BUFFER_PUT_HEAD_RESERVE_IDX(rb)) \
)
/**
* Put to the ring buffer.
* Get free slot from the ring buffer, return pointer to it.
*
* @param rb - ring buffer pointer.
* @return nothing
*/
#define ZB_RING_BUFFER_FLUSH_PUT(rb) \
( \
(rb)->written++, \
(rb)->write_i = ((rb)->write_i + 1U) % ZB_RING_BUFFER_CAPACITY(rb) \
)
#define ZB_RING_BUFFER_FLUSH_PUT_HEAD(rb) \
do \
{ \
(rb)->written++; \
if ((rb)->read_i > 0U) \
{ \
--((rb)->read_i); \
} \
else \
{ \
(rb)->read_i = ZB_RING_BUFFER_CAPACITY(rb) - 1U; \
} \
} while(ZB_FALSE)
/**
Return amount of data which can be put into ring buffer tail starting from write_i
@param rb - ring buffer pointer
@param size - requested data size
*/
#define ZB_RING_BUFFER_LINEAR_PORTION(rb, size) \
( \
ZB_RING_BUFFER_CAPACITY(rb) - (rb)->write_i < size ? \
ZB_RING_BUFFER_CAPACITY(rb) - (rb)->write_i : size \
)
/**
Return amount of data which can be put into ring buffer tail starting from write_i using external rb capacity
@param rb - ring buffer pointer
@param size - requested data size
*/
#define ZB_RING_BUFFER_LINEAR_PORTION_BY_CAP(rb, size, rb_cap) \
( \
(rb_cap) - (rb)->write_i < (size) ? \
(rb_cap) - (rb)->write_i : (size) \
)
/**
* Get the size of available for writing continuous portion in ring buffer.
*
* That portion is from write_i index to the end of the buffer or
* from write_i index to the beginning of previously written but still not read data
*
* @param buf - ring buffer pointer
* @return size of available continuous portion
*/
#define ZB_RING_BUFFER_AVAILABLE_CONTINUOUS_PORTION(rb) \
ZB_RING_BUFFER_LINEAR_PORTION((rb), ZB_RING_BUFFER_FREE_SPACE(rb))
/**
Batch put data into ringbuffer
To be used to copy from external buffer to ring buffer
@param rb - ring buffer pointer
@param data - data ptr
@param size - requested data size
@param entries_written - (out) amount of data put
*/
#define ZB_RING_BUFFER_BATCH_PUT(rb, data, size, entries_written) \
do \
{ \
(entries_written) = ZB_RING_BUFFER_LINEAR_PORTION((rb), (size)); \
ZB_MEMCPY((rb)->ring_buf + (rb)->write_i, (data), (entries_written)); \
(rb)->written += (entries_written); \
(rb)->write_i = (((rb)->write_i + (entries_written)) % ZB_RING_BUFFER_CAPACITY(rb)); \
} while(ZB_FALSE)
/**
Batch put data into ringbuffer using external rb capacity
To be used to copy from external buffer to ring buffer
@param rb - ring buffer pointer
@param data - data ptr
@param size - requested data size
@param entries_written - (out) amount of data put
@param rb_cap - cap of the ring buffer
*/
#define ZB_RING_BUFFER_BATCH_PUT_BY_CAP(rb, data, size, entries_written, rb_cap) \
do \
{ \
(entries_written) = ZB_RING_BUFFER_LINEAR_PORTION_BY_CAP((rb), (size), (rb_cap)); \
ZB_MEMCPY((rb)->ring_buf + (rb)->write_i, (data), (entries_written)); \
(rb)->written += (entries_written); \
(rb)->write_i = (((rb)->write_i + (entries_written)) % (rb_cap)); \
} while(ZB_FALSE)
/**
* Flush after put more then 1 element to the ring buffer
*
* Do not check for write_i overflow!
*
* @param rb - ring buffer pointer.
* @param size - number of items put
* @return nothing
*/
#define ZB_RING_BUFFER_FLUSH_BATCH_PUT(rb, size) \
( \
(rb)->written += size, \
(rb)->write_i = (((rb)->write_i) + size) % ZB_RING_BUFFER_CAPACITY(rb) \
)
/**
* Put value to the ring buffer.
*
* @param rb - ring buffer pointer.
* @param value - value to put to ring buffer
* @return nothing
*/
#define ZB_RING_BUFFER_PUT(rb, value) \
( \
(rb)->ring_buf[(rb)->write_i] = (value), \
(rb)->written++, \
(rb)->write_i = ((rb)->write_i + 1U) % ZB_RING_BUFFER_CAPACITY(rb) \
)
/**
* Put value to the ring buffer using memcpy.
*
* @param rb - ring buffer pointer.
* @param value_ptr - pointer to value to put to ring buffer
* @return nothing
*/
#define ZB_RING_BUFFER_PUT_PTR(rb, value_ptr) \
( \
memcpy(&((rb)->ring_buf[(rb)->write_i]), (value_ptr), sizeof((rb)->ring_buf[0])), \
(rb)->written++, \
(rb)->write_i = ((rb)->write_i + 1U) % ZB_RING_BUFFER_CAPACITY(rb) \
)
/**
* Reinsert last value into the ring buffer using memcpy.
*
* @param rb - ring buffer pointer.
* @param value_ptr - pointer to value to put to ring buffer
* @return nothing
*/
#define ZB_RING_BUFFER_PUT_REUSE_LAST(rb, value_ptr) \
( \
memcpy(&((rb)->ring_buf[((rb)->write_i ? (rb)->write_i - 1U : (rb)->write_i + ZB_RING_BUFFER_CAPACITY(rb) - 1U)]), \
(value_ptr), \
sizeof((rb)->ring_buf[0])) \
)
/**
* Get entry from the ring buffer read pointer position
*
* @param rb - ring buffer pointer.
*
* @return pointer to the ring buffer entry or NULL if it is empty
*/
#define ZB_RING_BUFFER_PEEK(rb) \
( \
ZB_RING_BUFFER_IS_EMPTY(rb) ? NULL \
: (rb)->ring_buf + (rb)->read_i \
)
/**
* Get entry from the ring buffer read pointer position
*
* @param rb - ring buffer pointer.
*
* @return pointer to the ring buffer entry
*/
#define ZB_RING_BUFFER_GET(rb) \
( \
(rb)->ring_buf + (rb)->read_i \
)
/**
* Get entry from the ring buffer write pointer position
*
* @param rb - ring buffer pointer.
*
* @return pointer to the ring buffer entry
*/
#define ZB_RING_BUFFER_GETW(rb) \
( (rb)->ring_buf + (rb)->write_i )
/**
* Move ring buffer read pointer.
*
* To be used after ZB_RING_BUFFER_PEEK().
* @note This macro does not check for an empty ring buffer.
*
* @param rb - ring buffer pointer.
* @return nothing
*/
#define ZB_RING_BUFFER_FLUSH_GET(rb) \
( \
(rb)->written--, \
((rb)->read_i = ((rb)->read_i + 1U) % ZB_RING_BUFFER_CAPACITY(rb)) \
)
/**
* Get entries from the ring buffer read pointer position which can be get at once
*
* @param rb - ring buffer pointer.
* @param size - (out) number of entries which can be got
*
* @return pointer to the ring buffer entry
*/
#define ZB_RING_BUFFER_GET_BATCH(rb, size) \
( \
(size) = ((rb)->written <= (ZB_RING_BUFFER_CAPACITY(rb) - (rb)->read_i) \
? \
(rb)->written \
: \
(ZB_RING_BUFFER_CAPACITY(rb) - (rb)->read_i)), \
(rb)->ring_buf + (rb)->read_i \
) \
/**
* Move ring buffer read pointer for more than 1 element
*
* @note This macro does not check for empty ring buffer.
*
* @param rb - ring buffer pointer.
* @param size - number of elements to mark as read
* @return nothing
*/
#define ZB_RING_BUFFER_FLUSH_GET_BATCH(rb, size) \
( \
(rb)->written -= (size), \
((rb)->read_i = ((rb)->read_i + (size)) % ZB_RING_BUFFER_CAPACITY((rb))) \
)
/**
* Move ring buffer's read and write pointers to the specified pos
*
* @param rb - ring buffer pointer.
* @param b - new position of read_i buffer's pointer
* @param e - new position of write_i buffer's pointer
* @return nothing
*/
#define ZB_RING_BUFFER_LOCATE(rb, b, e) \
( (rb)->read_i = b, \
(rb)->write_i = e, \
(rb)->written = e - b \
)
/**
* Returns number of used entries
*
* @param rb - ring buffer pointer.
*/
#define ZB_RING_BUFFER_USED_SPACE(rb) \
( \
(rb)->written \
)
#define ZB_RING_BUFFER_SEARCH_GET(_rb, _i) \
( \
ZB_RING_BUFFER_IS_EMPTY(_rb) ? NULL \
: (_rb)->ring_buf + ((_rb)->read_i +(_i)) % ZB_RING_BUFFER_CAPACITY(_rb) \
)
/**
* This is a fake type used for type casting.
* Represents array of bytes, used for serial trace e.t.c.
*
* @Example:
* @code
* ZB_RING_BUFFER_DECLARE(sniffer_io_buffer, zb_uint8_t, 4096);
* ...
* zb_osif_set_user_io_buffer((zb_byte_array_t*)&gs_sniffer_io_buf, 4096);
* @endcode
*/
ZB_RING_BUFFER_DECLARE(zb_byte_array, zb_uint8_t, 1);
/**
* @see ZB_RING_BUFFER_IS_EMPTY
*/
#define ZB_BYTE_ARRAY_IS_EMPTY(rb) ZB_RING_BUFFER_IS_EMPTY(rb)
/**
* @see ZB_RING_BUFFER_PEEK
*/
#define ZB_BYTE_ARRAY_PEEK(rb) ZB_RING_BUFFER_PEEK(rb)
/**
* Return 1 if ring buffer is full
*
* @param rb - ring buffer pointer.
* @param cap - ring buffer capacity (number of bytes)
*/
#define ZB_BYTE_ARRAY_IS_FULL(rb, cap) ((zb_uint_t)(rb)->written >= cap)
/**
* Move ring buffer read pointer.
*
* To be used after ZB_BYTE_ARRAY_PEEK().
* @note This macro does not check for empty ring buffer.
*
* @param rb - ring buffer pointer.
* @param cap - ring buffer capacity (number of bytes)
* @return nothing
*/
#define ZB_BYTE_ARRAY_FLUSH_GET(rb, cap) \
( \
(rb)->written--, \
((rb)->read_i = ((rb)->read_i + 1U) % (cap)) \
)
/* 10/04/17 CR Ustimenko start */
/**
* Get entries from the ring buffer read pointer position which can be get at once
*
* @param rb - ring buffer pointer.
* @param size - (out) number of entries which can be got
* @param cap - ring buffer capacity (number of bytes)
*
* @return pointer to the ring buffer entry
*/
#define ZB_BYTE_ARRAY_GET_BATCH(rb, size, cap) \
( \
size = ((rb)->written <= (cap - (rb)->read_i) \
? \
(rb)->written \
: \
(cap - (rb)->read_i)), \
(rb)->ring_buf + (rb)->read_i \
)
/**
* Move ring buffer read pointer for more than 1 element
*
* This macro does not check for an empty ring buffer.
*
* @param rb - ring buffer pointer.
* @param size - number of elements to mark as read
* @param cap - ring buffer capacity (number of bytes)
* @return nothing
*/
#define ZB_BYTE_ARRAY_FLUSH_GET_BATCH(rb, size, cap) \
( \
(rb)->written -= size, \
((rb)->read_i = ((rb)->read_i + size) % cap) \
)
/* 10/04/17 CR Ustimenko end */
/**
* Put value to the ring buffer.
*
* @param rb - ring buffer pointer.
* @param value - value to put to ring buffer
* @param cap - ring buffer capacity (number of bytes)
* @return nothing
*/
#define ZB_BYTE_ARRAY_PUT(rb, value, cap) \
( \
(rb)->ring_buf[(rb)->write_i] = (value), \
(rb)->written++, \
(rb)->write_i = ((rb)->write_i + 1U) % cap \
)
#define ZB_BYTE_ARRAY_PUT_HEAD_RESERVE_IDX(rb, cap) \
( \
((rb)->read_i > 0U) ? \
((rb)->read_i - 1U) : ((cap) - 1U) \
)
#define ZB_BYTE_ARRAY_PUT_HEAD_RESERVE(rb, cap) \
( \
((rb)->ring_buf + ZB_BYTE_ARRAY_PUT_HEAD_RESERVE_IDX(rb, (cap))) \
)
#define ZB_BYTE_ARRAY_FLUSH_PUT_HEAD(rb, cap) \
do \
{ \
(rb)->written++; \
if ((rb)->read_i > 0U) \
{ \
--((rb)->read_i); \
} \
else \
{ \
(rb)->read_i = (cap) - 1U; \
} \
} while(ZB_FALSE)
#define ZB_BYTE_ARRAY_FLUSH_PUT(rb, cap) \
( \
(rb)->written++, \
(rb)->write_i = ((rb)->write_i + 1U) % (cap) \
)
/*! @} */
/*! @endcond */
#endif /* ZB_RINGBUFFER_H */