* libs/libc/misc/lib_circbuf.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* and one writer is using the circular buffer.
* For multiple writer and one reader there is only a need to lock the
* writer. And vice versa for only one writer and multiple reader there is
* only a need to lock the reader.
*/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/circbuf.h>
#include <nuttx/lib/lib.h>
* Private Types
****************************************************************************/
* Public Functions
****************************************************************************/
* Name: circbuf_init
*
* Description:
* Initialize a circular buffer.
*
* Input Parameters:
* circ - Address of the circular buffer to be used.
* base - A pointer to circular buffer's internal buffer. It can be
* provided by caller because sometimes the creation of buffer
* is special or needs to preallocated, eg: DMA buffer.
* If NULL, a buffer of the given size will be allocated.
* bytes - The size of the internal buffer.
*
* Returned Value:
* Zero on success; A negated errno value is returned on any failure.
*
****************************************************************************/
int circbuf_init(FAR struct circbuf_s *circ, FAR void *base, size_t bytes)
{
DEBUGASSERT(circ);
DEBUGASSERT(!base || bytes);
circ->external = !!base;
if (!base && bytes)
{
base = lib_malloc(bytes);
if (!base)
{
return -ENOMEM;
}
}
circ->base = base;
circ->size = bytes;
circ->head = 0;
circ->tail = 0;
return 0;
}
* Name: circbuf_reinit
*
* Description:
* Reinitialize a circular buffer with a new buffer, and new size.
* In case malloc & free is not reliable here, we handle buffer outside.
* Will copy the old data to new buffer.
*
* Input Parameters:
* circ - Address of the circular buffer to be used.
* base - A pointer to circular buffer's internal buffer. It can be
* provided by caller because sometimes the creation of buffer
* is special or needs to preallocated, eg: DMA buffer.
* If NULL, a buffer of the given size will be allocated.
* bytes - The size of the internal buffer.
*
* Returned Value:
* The previous internal buffer pointer is returned.
*
****************************************************************************/
FAR void *circbuf_reinit(FAR struct circbuf_s *circ, FAR void *base,
size_t bytes)
{
size_t len = 0;
FAR void *old;
if (bytes && base)
{
len = circbuf_used(circ);
if (bytes < len)
{
circbuf_skip(circ, len - bytes);
len = bytes;
}
circbuf_read(circ, base, len);
}
old = circ->base;
circ->base = base;
circ->size = bytes;
circ->head = len;
circ->tail = 0;
return old;
}
* Name: circbuf_resize
*
* Description:
* Resize a circular buffer (change buffer size).
*
* Input Parameters:
* circ - Address of the circular buffer to be used.
* bytes - The size of the internal buffer.
*
* Returned Value:
* Zero on success; A negated errno value is returned on any failure.
*
****************************************************************************/
int circbuf_resize(FAR struct circbuf_s *circ, size_t bytes)
{
FAR void *tmp = NULL;
DEBUGASSERT(circ);
DEBUGASSERT(!circ->external);
if (bytes == circ->size)
{
return 0;
}
if (bytes)
{
tmp = lib_malloc(bytes);
if (!tmp)
{
return -ENOMEM;
}
}
lib_free(circbuf_reinit(circ, tmp, bytes));
return 0;
}
* Name: circbuf_reset
*
* Description:
* Remove the entire circular buffer content.
*
* Input Parameters:
* circ - Address of the circular buffer to be used.
****************************************************************************/
void circbuf_reset(FAR struct circbuf_s *circ)
{
DEBUGASSERT(circ);
circ->head = circ->tail = 0;
}
* Name: circbuf_uninit
*
* Description:
* Free the circular buffer.
*
* Input Parameters:
* circ - Address of the circular buffer to be used.
****************************************************************************/
void circbuf_uninit(FAR struct circbuf_s *circ)
{
DEBUGASSERT(circ);
if (!circ->external)
{
lib_free(circ->base);
}
memset(circ, 0, sizeof(*circ));
}
* Name: circbuf_is_init
*
* Description:
* Return true if the circular buffer had been initialized.
*
* Input Parameters:
* circ - Address of the circular buffer to be used.
****************************************************************************/
bool circbuf_is_init(FAR struct circbuf_s *circ)
{
return !!circ->base;
}
* Name: cirbuf_dump
*
* Description:
* Dump data from the circular buffer.
*
* Input Parameters:
* msg - Describe message.
* circ - Address of the circular buffer to be used.
****************************************************************************/
void cirbuf_dump(FAR struct circbuf_s *circ, FAR const char *msg)
{
size_t head;
size_t tail;
DEBUGASSERT(circ);
head = circ->head % circ->size;
tail = circ->tail % circ->size;
if (head < tail)
{
lib_dumpbuffer(msg, (FAR const uint8_t *)circ->base + tail,
circ->size - tail);
lib_dumpbuffer(msg, (FAR const uint8_t *)circ->base, head);
}
else if (head > tail)
{
lib_dumpbuffer(msg, (FAR const uint8_t *)circ->base + tail,
head - tail);
}
}
* Name: circbuf_peekat
*
* Description:
* Get data specified position from the circular buffer without removing
*
* Note :
* That with only one concurrent reader and one concurrent writer,
* you don't need extra locking to use these api.
*
* Input Parameters:
* circ - Address of the circular buffer to be used.
* pos - Position to read.
* dst - Address where to store the data.
* bytes - Number of bytes to get.
*
* Returned Value:
* The bytes of get data is returned if the peek data is successful;
* A negated errno value is returned on any failure.
****************************************************************************/
ssize_t circbuf_peekat(FAR struct circbuf_s *circ, size_t pos,
FAR void *dst, size_t bytes)
{
size_t len;
size_t off;
DEBUGASSERT(circ);
if (!circ->size)
{
return 0;
}
if (pos < circ->tail)
{
pos = circ->tail;
}
len = circ->head - pos;
off = pos % circ->size;
if (bytes > len)
{
bytes = len;
}
len = circ->size - off;
if (bytes < len)
{
len = bytes;
}
memcpy(dst, (FAR char *)circ->base + off, len);
memcpy((FAR char *)dst + len, circ->base, bytes - len);
return bytes;
}
* Name: circbuf_write
*
* Description:
* Write data to the circular buffer.
*
* Note :
* That with only one concurrent reader and one concurrent writer,
* you don't need extra locking to use these api.
*
* Input Parameters:
* circ - Address of the circular buffer to be used.
* src - The data to be added.
* bytes - Number of bytes to be added.
*
* Returned Value:
* The bytes of get data is returned if the write data is successful;
* A negated errno value is returned on any failure.
****************************************************************************/
ssize_t circbuf_write(FAR struct circbuf_s *circ,
FAR const void *src, size_t bytes)
{
size_t space;
size_t off;
DEBUGASSERT(circ);
DEBUGASSERT(src || !bytes);
if (!circ->size)
{
return 0;
}
space = circbuf_space(circ);
off = circ->head % circ->size;
if (bytes > space)
{
bytes = space;
}
space = circ->size - off;
if (bytes < space)
{
space = bytes;
}
memcpy((FAR char *)circ->base + off, src, space);
memcpy(circ->base, (FAR char *)src + space, bytes - space);
circ->head += bytes;
return bytes;
}
* Name: circbuf_overwrite
*
* Description:
* Write data to the circular buffer. It can overwrite old data when
* circular buffer don't have enough space to store data.
*
* Note:
* Usage circbuf_overwrite () is dangerous. It should be only called
* when the buffer is exclusived locked or when it is secured that no
* other thread is accessing the buffer.
*
* Input Parameters:
* circ - Address of the circular buffer to be used.
* src - The data to be added.
* bytes - Number of bytes to be added.
*
* Returned Value:
* The bytes length of overwrite is returned if it's successful;
* A negated errno value is returned on any failure.
****************************************************************************/
ssize_t circbuf_overwrite(FAR struct circbuf_s *circ,
FAR const void *src, size_t bytes)
{
size_t overwrite = 0;
size_t skip = 0;
size_t space;
size_t off;
DEBUGASSERT(circ);
DEBUGASSERT(src || !bytes);
if (!circ->size)
{
return 0;
}
if (bytes > circ->size)
{
skip = bytes - circ->size;
src = (FAR const void *)((FAR char *)src + skip);
bytes = circ->size;
}
space = circbuf_space(circ);
if (bytes > space)
{
overwrite = bytes - space + skip;
}
circ->head += skip;
off = circ->head % circ->size;
space = circ->size - off;
if (bytes < space)
{
space = bytes;
}
memcpy((FAR char *)circ->base + off, src, space);
memcpy(circ->base, (FAR char *)src + space, bytes - space);
circ->head += bytes;
circ->tail += overwrite;
return overwrite;
}