2020-11-12 11:40:06 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* mm/circbuf/circbuf.c
|
|
|
|
*
|
|
|
|
* 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 <nuttx/config.h>
|
|
|
|
|
2021-05-18 08:59:14 +02:00
|
|
|
#include <assert.h>
|
|
|
|
|
2020-11-12 11:40:06 +01:00
|
|
|
#include <nuttx/kmalloc.h>
|
|
|
|
#include <nuttx/mm/circbuf.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 = 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)
|
|
|
|
{
|
2020-11-30 09:24:41 +01:00
|
|
|
FAR void *tmp = NULL;
|
|
|
|
size_t len = 0;
|
2020-11-12 11:40:06 +01:00
|
|
|
|
|
|
|
DEBUGASSERT(circ);
|
|
|
|
DEBUGASSERT(!circ->external);
|
2021-12-14 15:02:16 +01:00
|
|
|
if (bytes == circ->size)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2020-11-12 11:40:06 +01:00
|
|
|
|
2020-11-30 09:24:41 +01:00
|
|
|
if (bytes)
|
2020-11-12 11:40:06 +01:00
|
|
|
{
|
2020-11-30 09:24:41 +01:00
|
|
|
tmp = kmm_malloc(bytes);
|
|
|
|
if (!tmp)
|
|
|
|
{
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2020-11-12 11:40:06 +01:00
|
|
|
|
2020-11-30 09:24:41 +01:00
|
|
|
len = circbuf_used(circ);
|
|
|
|
if (bytes < len)
|
|
|
|
{
|
|
|
|
circbuf_skip(circ, len - bytes);
|
|
|
|
len = bytes;
|
|
|
|
}
|
2020-11-12 11:40:06 +01:00
|
|
|
|
2020-11-30 09:24:41 +01:00
|
|
|
circbuf_read(circ, tmp, len);
|
|
|
|
}
|
2020-11-12 11:40:06 +01:00
|
|
|
|
|
|
|
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:
|
2021-02-25 13:48:46 +01:00
|
|
|
* Return the remaining space of the circular buffer.
|
2020-11-12 11:40:06 +01:00
|
|
|
*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2022-07-21 04:31:18 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2020-11-12 11:40:06 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
2022-03-22 08:44:23 +01:00
|
|
|
* Name: circbuf_peekat
|
2020-11-12 11:40:06 +01:00
|
|
|
*
|
|
|
|
* Description:
|
2022-03-22 08:44:23 +01:00
|
|
|
* Get data speicified position from the circular buffer without removing
|
2020-11-12 11:40:06 +01:00
|
|
|
*
|
|
|
|
* 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.
|
2022-03-22 08:44:23 +01:00
|
|
|
* pos - Position to read.
|
2020-11-12 11:40:06 +01:00
|
|
|
* 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.
|
|
|
|
****************************************************************************/
|
|
|
|
|
2022-03-22 08:44:23 +01:00
|
|
|
ssize_t circbuf_peekat(FAR struct circbuf_s *circ, size_t pos,
|
|
|
|
FAR void *dst, size_t bytes)
|
2020-11-12 11:40:06 +01:00
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
size_t off;
|
|
|
|
|
|
|
|
DEBUGASSERT(circ);
|
|
|
|
|
2020-11-30 09:24:41 +01:00
|
|
|
if (!circ->size)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-12 11:40:06 +01:00
|
|
|
len = circbuf_used(circ);
|
2022-03-22 08:44:23 +01:00
|
|
|
off = pos % circ->size;
|
2020-11-12 11:40:06 +01:00
|
|
|
|
|
|
|
if (bytes > len)
|
|
|
|
{
|
|
|
|
bytes = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = circ->size - off;
|
|
|
|
if (bytes < len)
|
|
|
|
{
|
|
|
|
len = bytes;
|
|
|
|
}
|
|
|
|
|
2022-11-23 11:37:27 +01:00
|
|
|
memcpy(dst, (FAR char *)circ->base + off, len);
|
|
|
|
memcpy((FAR char *)dst + len, circ->base, bytes - len);
|
2020-11-12 11:40:06 +01:00
|
|
|
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
2022-03-22 08:44:23 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2020-11-12 11:40:06 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: circbuf_read
|
|
|
|
*
|
|
|
|
* Description:
|
2022-03-22 08:44:23 +01:00
|
|
|
* Get data from the circular buffer.
|
2020-11-12 11:40:06 +01:00
|
|
|
*
|
|
|
|
* 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,
|
2022-03-22 08:44:23 +01:00
|
|
|
FAR void *dst, size_t bytes)
|
2020-11-12 11:40:06 +01:00
|
|
|
{
|
|
|
|
DEBUGASSERT(circ);
|
|
|
|
DEBUGASSERT(dst || !bytes);
|
|
|
|
|
|
|
|
bytes = circbuf_peek(circ, dst, bytes);
|
|
|
|
circ->tail += bytes;
|
|
|
|
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: circbuf_skip
|
|
|
|
*
|
|
|
|
* Description:
|
2022-03-22 08:44:23 +01:00
|
|
|
* Skip data from the circular buffer.
|
2020-11-12 11:40:06 +01:00
|
|
|
*
|
|
|
|
* 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,
|
2022-03-22 08:44:23 +01:00
|
|
|
FAR const void *src, size_t bytes)
|
2020-11-12 11:40:06 +01:00
|
|
|
{
|
|
|
|
size_t space;
|
|
|
|
size_t off;
|
|
|
|
|
|
|
|
DEBUGASSERT(circ);
|
|
|
|
DEBUGASSERT(src || !bytes);
|
|
|
|
|
2020-11-30 09:24:41 +01:00
|
|
|
if (!circ->size)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-12 11:40:06 +01:00
|
|
|
space = circbuf_space(circ);
|
|
|
|
off = circ->head % circ->size;
|
|
|
|
if (bytes > space)
|
|
|
|
{
|
|
|
|
bytes = space;
|
|
|
|
}
|
|
|
|
|
|
|
|
space = circ->size - off;
|
|
|
|
if (bytes < space)
|
|
|
|
{
|
|
|
|
space = bytes;
|
|
|
|
}
|
|
|
|
|
2022-11-23 11:37:27 +01:00
|
|
|
memcpy((FAR char *)circ->base + off, src, space);
|
|
|
|
memcpy(circ->base, (FAR char *)src + space, bytes - space);
|
2020-11-12 11:40:06 +01:00
|
|
|
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,
|
2022-03-22 08:44:23 +01:00
|
|
|
FAR const void *src, size_t bytes)
|
2020-11-12 11:40:06 +01:00
|
|
|
{
|
|
|
|
size_t overwrite = 0;
|
2023-02-25 03:41:22 +01:00
|
|
|
size_t skip = 0;
|
2020-11-12 11:40:06 +01:00
|
|
|
size_t space;
|
|
|
|
size_t off;
|
|
|
|
|
|
|
|
DEBUGASSERT(circ);
|
|
|
|
DEBUGASSERT(src || !bytes);
|
|
|
|
|
2020-11-30 09:24:41 +01:00
|
|
|
if (!circ->size)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-12 11:40:06 +01:00
|
|
|
if (bytes > circ->size)
|
|
|
|
{
|
2023-02-25 03:41:22 +01:00
|
|
|
skip = bytes - circ->size;
|
|
|
|
src = (FAR const void *)((FAR char *)src + skip);
|
2020-11-12 11:40:06 +01:00
|
|
|
bytes = circ->size;
|
|
|
|
}
|
|
|
|
|
|
|
|
space = circbuf_space(circ);
|
|
|
|
if (bytes > space)
|
|
|
|
{
|
2023-02-25 03:41:22 +01:00
|
|
|
overwrite = bytes - space + skip;
|
2020-11-12 11:40:06 +01:00
|
|
|
}
|
|
|
|
|
2023-02-27 10:34:15 +01:00
|
|
|
circ->head += skip;
|
2020-11-12 11:40:06 +01:00
|
|
|
off = circ->head % circ->size;
|
|
|
|
space = circ->size - off;
|
|
|
|
if (bytes < space)
|
|
|
|
{
|
|
|
|
space = bytes;
|
|
|
|
}
|
|
|
|
|
2022-11-23 11:37:27 +01:00
|
|
|
memcpy((FAR char *)circ->base + off, src, space);
|
|
|
|
memcpy(circ->base, (FAR char *)src + space, bytes - space);
|
2023-02-27 10:34:15 +01:00
|
|
|
circ->head += bytes;
|
2020-11-12 11:40:06 +01:00
|
|
|
circ->tail += overwrite;
|
|
|
|
|
|
|
|
return overwrite;
|
|
|
|
}
|