/* 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" cbuf::cbuf(size_t size) : next(NULL), _size(size), _buf(new char[size]), _bufend(_buf + size), _begin(_buf), _end(_begin) { } cbuf::~cbuf() { delete[] _buf; } size_t cbuf::resizeAdd(size_t addSize) { return resize(_size + addSize); } size_t cbuf::resize(size_t newSize) { size_t bytes_available = available(); // not lose any data // if data can be lost use remove or flush before resize if((newSize < bytes_available) || (newSize == _size)) { return _size; } char *newbuf = new char[newSize]; char *oldbuf = _buf; if(!newbuf) { return _size; } if(_buf) { read(newbuf, bytes_available); memset((newbuf + bytes_available), 0x00, (newSize - bytes_available)); } _begin = newbuf; _end = newbuf + bytes_available; _bufend = newbuf + newSize; _size = newSize; _buf = newbuf; delete[] oldbuf; return _size; } size_t cbuf::available() const { if(_end >= _begin) { return _end - _begin; } return _size - (_begin - _end); } size_t cbuf::size() { return _size; } size_t cbuf::room() const { if(_end >= _begin) { return _size - (_end - _begin) - 1; } return _begin - _end - 1; } int cbuf::peek() { if(empty()) { return -1; } return static_cast(*_begin); } size_t cbuf::peek(char *dst, size_t size) { size_t bytes_available = available(); size_t size_to_read = (size < bytes_available) ? size : bytes_available; size_t size_read = size_to_read; char * begin = _begin; if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) { size_t top_size = _bufend - _begin; memcpy(dst, _begin, top_size); begin = _buf; size_to_read -= top_size; dst += top_size; } memcpy(dst, begin, size_to_read); return size_read; } int cbuf::read() { if(empty()) { return -1; } char result = *_begin; _begin = wrap_if_bufend(_begin + 1); return static_cast(result); } size_t cbuf::read(char* dst, size_t size) { size_t bytes_available = available(); size_t size_to_read = (size < bytes_available) ? size : bytes_available; size_t size_read = size_to_read; if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) { size_t top_size = _bufend - _begin; memcpy(dst, _begin, top_size); _begin = _buf; size_to_read -= top_size; dst += top_size; } memcpy(dst, _begin, size_to_read); _begin = wrap_if_bufend(_begin + size_to_read); return size_read; } size_t cbuf::write(char c) { if(full()) { return 0; } *_end = c; _end = wrap_if_bufend(_end + 1); return 1; } size_t cbuf::write(const char* src, size_t size) { size_t bytes_available = room(); size_t size_to_write = (size < bytes_available) ? size : bytes_available; size_t size_written = size_to_write; if(_end >= _begin && size_to_write > (size_t) (_bufend - _end)) { size_t top_size = _bufend - _end; memcpy(_end, src, top_size); _end = _buf; size_to_write -= top_size; src += top_size; } memcpy(_end, src, size_to_write); _end = wrap_if_bufend(_end + size_to_write); return size_written; } void cbuf::flush() { _begin = _buf; _end = _buf; } size_t cbuf::remove(size_t size) { size_t bytes_available = available(); if(size >= bytes_available) { flush(); return 0; } size_t size_to_remove = (size < bytes_available) ? size : bytes_available; if(_end < _begin && size_to_remove > (size_t) (_bufend - _begin)) { size_t top_size = _bufend - _begin; _begin = _buf; size_to_remove -= top_size; } _begin = wrap_if_bufend(_begin + size_to_remove); return available(); }