/**************************************************************************** * mm/circbuf/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. * ****************************************************************************/ /* Note about locking: There is no locking required while only one reader * 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 #include #include #include /**************************************************************************** * 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 = kmm_malloc(bytes); if (!base) { return -ENOMEM; } } circ->base = base; circ->size = bytes; circ->head = 0; circ->tail = 0; return 0; } /**************************************************************************** * 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; size_t len = 0; DEBUGASSERT(circ); DEBUGASSERT(!circ->external); if (bytes == circ->size) { return 0; } if (bytes) { tmp = kmm_malloc(bytes); if (!tmp) { return -ENOMEM; } len = circbuf_used(circ); if (bytes < len) { circbuf_skip(circ, len - bytes); len = bytes; } circbuf_read(circ, tmp, len); } kmm_free(circ->base); circ->base = tmp; circ->size = bytes; circ->head = len; circ->tail = 0; 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) { kmm_free(circ->base); } memset(circ, 0, sizeof(*circ)); } /**************************************************************************** * Name: circbuf_size * * Description: * Return size of the circular buffer. * * Input Parameters: * circ - Address of the circular buffer to be used. ****************************************************************************/ size_t circbuf_size(FAR struct circbuf_s *circ) { DEBUGASSERT(circ); return circ->size; } /**************************************************************************** * Name: circbuf_used * * Description: * Return the used bytes of the circular buffer. * * Input Parameters: * circ - Address of the circular buffer to be used. ****************************************************************************/ size_t circbuf_used(FAR struct circbuf_s *circ) { DEBUGASSERT(circ); return circ->head - circ->tail; } /**************************************************************************** * Name: circbuf_space * * Description: * Return the remaining space of the circular buffer. * * Input Parameters: * circ - Address of the circular buffer to be used. ****************************************************************************/ size_t circbuf_space(FAR struct circbuf_s *circ) { return circbuf_size(circ) - circbuf_used(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: circbuf_is_empty * * Description: * Return true if the circular buffer is empty. * * Input Parameters: * circ - Address of the circular buffer to be used. ****************************************************************************/ bool circbuf_is_empty(FAR struct circbuf_s *circ) { return !circbuf_used(circ); } /**************************************************************************** * Name: circbuf_is_full * * Description: * Return true if the circular buffer is full. * * Input Parameters: * circ - Address of the circular buffer to be used. ****************************************************************************/ bool circbuf_is_full(FAR struct circbuf_s *circ) { return !circbuf_space(circ); } /**************************************************************************** * 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 (circ->head - pos > circ->head - 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_peek * * Description: * Get data 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. * 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_peek(FAR struct circbuf_s *circ, FAR void *dst, size_t bytes) { return circbuf_peekat(circ, circ->tail, dst, bytes); } /**************************************************************************** * Name: circbuf_read * * Description: * Get data from 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. * dst - Address where to store the data. * bytes - Number of bytes to get. * * Returned Value: * The bytes of get data is returned if the read data is successful; * A negated errno value is returned on any failure. ****************************************************************************/ ssize_t circbuf_read(FAR struct circbuf_s *circ, FAR void *dst, size_t bytes) { DEBUGASSERT(circ); DEBUGASSERT(dst || !bytes); bytes = circbuf_peek(circ, dst, bytes); circ->tail += bytes; return bytes; } /**************************************************************************** * Name: circbuf_skip * * Description: * Skip data from 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. * bytes - Number of bytes to skip. * * Returned Value: * The bytes of get data is returned if the skip data is successful; * A negated errno value is returned on any failure. ****************************************************************************/ ssize_t circbuf_skip(FAR struct circbuf_s *circ, size_t bytes) { size_t len; DEBUGASSERT(circ); len = circbuf_used(circ); if (bytes > len) { bytes = len; } circ->tail += bytes; 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; } /**************************************************************************** * Name: circbuf_get_writeptr * * Description: * Get the write pointer of the circbuf. * * Input Parameters: * circ - Address of the circular buffer to be used. * size - Returns the maximum size that can be written consecutively * * Returned Value: * The write pointer of the circbuf. * ****************************************************************************/ FAR void *circbuf_get_writeptr(FAR struct circbuf_s *circ, FAR size_t *size) { size_t off; size_t pos; DEBUGASSERT(circ); off = circ->head % circ->size; pos = circ->tail % circ->size; if (off >= pos) { *size = circ->size - off; } else { *size = pos - off; } return (FAR char *)circ->base + off; } /**************************************************************************** * Name: circbuf_get_readptr * * Description: * Get the read pointer of the circbuf. * * Input Parameters: * circ - Address of the circular buffer to be used. * size - Returns the maximum size that can be read consecutively. * * Returned Value: * The read pointer of the circbuf. * ****************************************************************************/ FAR void *circbuf_get_readptr(FAR struct circbuf_s *circ, size_t *size) { size_t off; size_t pos; DEBUGASSERT(circ); off = circ->head % circ->size; pos = circ->tail % circ->size; if (pos > off) { *size = circ->size - pos; } else { *size = off - pos; } return (FAR char *)circ->base + pos; } /**************************************************************************** * Name: circbuf_writecommit * * Description: * After writing data using the buf returned by circbuf_writebuf, * you need to use this function to update the internal structure * of cricbuf. * * Input Parameters: * circ - Address of the circular buffer to be used. * writtensize - The data that has been written to the buffer. * ****************************************************************************/ void circbuf_writecommit(FAR struct circbuf_s *circ, size_t writtensize) { DEBUGASSERT(circ); circ->head += writtensize; } /**************************************************************************** * Name: circbuf_readcommit * * Description: * After reading data using the buf returned by circbuf_readbuf, * you need to use this function to update the internal structure * of cricbuf. * * Input Parameters: * circ - Address of the circular buffer to be used. * readsize - The data that has been read to the buffer. * ****************************************************************************/ void circbuf_readcommit(FAR struct circbuf_s *circ, size_t readsize) { DEBUGASSERT(circ); circ->tail += readsize; }