/* cbuf.cpp - Circular buffer implementation Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. This file is part of the esp8266 core for Arduino environment. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cbuf.h" #include "esp32-hal-log.h" #if CONFIG_DISABLE_HAL_LOCKS #define CBUF_MUTEX_CREATE() #define CBUF_MUTEX_LOCK() #define CBUF_MUTEX_UNLOCK() #define CBUF_MUTEX_DELETE() #else #define CBUF_MUTEX_CREATE() \ if (_lock == NULL) { \ _lock = xSemaphoreCreateMutex(); \ if (_lock == NULL) { \ log_e("failed to create mutex"); \ } \ } #define CBUF_MUTEX_LOCK() \ if (_lock != NULL) { \ xSemaphoreTakeRecursive(_lock, portMAX_DELAY); \ } #define CBUF_MUTEX_UNLOCK() \ if (_lock != NULL) { \ xSemaphoreGiveRecursive(_lock); \ } #define CBUF_MUTEX_DELETE() \ if (_lock != NULL) { \ SemaphoreHandle_t l = _lock; \ _lock = NULL; \ vSemaphoreDelete(l); \ } #endif cbuf::cbuf(size_t size) : next(NULL), has_peek(false), peek_byte(0), _buf(xRingbufferCreate(size, RINGBUF_TYPE_BYTEBUF)) { if (_buf == NULL) { log_e("failed to allocate ring buffer"); } CBUF_MUTEX_CREATE(); } cbuf::~cbuf() { CBUF_MUTEX_LOCK(); if (_buf != NULL) { RingbufHandle_t b = _buf; _buf = NULL; vRingbufferDelete(b); } CBUF_MUTEX_UNLOCK(); CBUF_MUTEX_DELETE(); } size_t cbuf::resizeAdd(size_t addSize) { return resize(size() + addSize); } size_t cbuf::resize(size_t newSize) { CBUF_MUTEX_LOCK(); size_t _size = size(); if (newSize == _size) { return _size; } // not lose any data // if data can be lost use remove or flush before resize size_t bytes_available = available(); if (newSize < bytes_available) { CBUF_MUTEX_UNLOCK(); log_e("new size is less than the currently available data size"); return _size; } RingbufHandle_t newbuf = xRingbufferCreate(newSize, RINGBUF_TYPE_BYTEBUF); if (newbuf == NULL) { CBUF_MUTEX_UNLOCK(); log_e("failed to allocate new ring buffer"); return _size; } if (_buf != NULL) { if (bytes_available) { char *old_data = (char *)malloc(bytes_available); if (old_data == NULL) { vRingbufferDelete(newbuf); CBUF_MUTEX_UNLOCK(); log_e("failed to allocate temporary buffer"); return _size; } bytes_available = read(old_data, bytes_available); if (!bytes_available) { free(old_data); vRingbufferDelete(newbuf); CBUF_MUTEX_UNLOCK(); log_e("failed to read previous data"); return _size; } if (xRingbufferSend(newbuf, (void *)old_data, bytes_available, 0) != pdTRUE) { write(old_data, bytes_available); free(old_data); vRingbufferDelete(newbuf); CBUF_MUTEX_UNLOCK(); log_e("failed to restore previous data"); return _size; } free(old_data); } RingbufHandle_t b = _buf; _buf = newbuf; vRingbufferDelete(b); } else { _buf = newbuf; } CBUF_MUTEX_UNLOCK(); return newSize; } size_t cbuf::available() const { size_t available = 0; if (_buf != NULL) { vRingbufferGetInfo(_buf, NULL, NULL, NULL, NULL, (UBaseType_t *)&available); } if (has_peek) { available++; } return available; } size_t cbuf::size() { size_t _size = 0; if (_buf != NULL) { _size = xRingbufferGetMaxItemSize(_buf); } return _size; } size_t cbuf::room() const { size_t _room = 0; if (_buf != NULL) { _room = xRingbufferGetCurFreeSize(_buf); } return _room; } bool cbuf::empty() const { return available() == 0; } bool cbuf::full() const { return room() == 0; } int cbuf::peek() { if (!available()) { return -1; } int c; CBUF_MUTEX_LOCK(); if (has_peek) { c = peek_byte; } else { c = read(); if (c >= 0) { has_peek = true; peek_byte = c; } } CBUF_MUTEX_UNLOCK(); return c; } int cbuf::read() { char result = 0; if (!read(&result, 1)) { return -1; } return static_cast(result); } size_t cbuf::read(char *dst, size_t size) { CBUF_MUTEX_LOCK(); size_t bytes_available = available(); if (!bytes_available || !size) { CBUF_MUTEX_UNLOCK(); return 0; } if (has_peek) { if (dst != NULL) { *dst++ = peek_byte; } size--; } size_t size_read = 0; if (size) { size_t received_size = 0; size_t size_to_read = (size < bytes_available) ? size : bytes_available; uint8_t *received_buff = (uint8_t *)xRingbufferReceiveUpTo(_buf, &received_size, 0, size_to_read); if (received_buff != NULL) { if (dst != NULL) { memcpy(dst, received_buff, received_size); } vRingbufferReturnItem(_buf, received_buff); size_read = received_size; size_to_read -= received_size; // wrap around data if (size_to_read) { received_size = 0; received_buff = (uint8_t *)xRingbufferReceiveUpTo(_buf, &received_size, 0, size_to_read); if (received_buff != NULL) { if (dst != NULL) { memcpy(dst + size_read, received_buff, received_size); } vRingbufferReturnItem(_buf, received_buff); size_read += received_size; } else { log_e("failed to read wrap around data from ring buffer"); } } } else { log_e("failed to read from ring buffer"); } } if (has_peek) { has_peek = false; size_read++; } CBUF_MUTEX_UNLOCK(); return size_read; } size_t cbuf::write(char c) { return write(&c, 1); } size_t cbuf::write(const char *src, size_t size) { CBUF_MUTEX_LOCK(); size_t bytes_available = room(); if (!bytes_available || !size) { CBUF_MUTEX_UNLOCK(); return 0; } size_t size_to_write = (size < bytes_available) ? size : bytes_available; if (xRingbufferSend(_buf, (void *)src, size_to_write, 0) != pdTRUE) { CBUF_MUTEX_UNLOCK(); log_e("failed to write to ring buffer"); return 0; } CBUF_MUTEX_UNLOCK(); return size_to_write; } void cbuf::flush() { read(NULL, available()); } size_t cbuf::remove(size_t size) { CBUF_MUTEX_LOCK(); size_t bytes_available = available(); if (bytes_available && size) { size_t size_to_remove = (size < bytes_available) ? size : bytes_available; bytes_available -= read(NULL, size_to_remove); } CBUF_MUTEX_UNLOCK(); return bytes_available; }