1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-23 16:16:13 +01:00
CommandStation-EX/CircularBuffer.cpp
Harald Barth a3274caf49 copy in
2023-06-06 00:50:03 +02:00

388 lines
8.3 KiB
C++

/*
* © 2023 Thierry Paris / Locoduino
* All rights reserved.
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/
#include "CircularBuffer.hpp"
CircularBuffer::CircularBuffer(int inSize)
{
this->head = 0;
this->tail = 0;
this->full = false;
this->peakCount = 0;
this->buffer = NULL;
this->size = inSize;
#ifdef ARDUINO_ARCH_ESP32
this->xSemaphore = NULL;
#endif
}
void CircularBuffer::begin(bool inMultiThread)
{
#ifdef ARDUINO_ARCH_ESP32
if (inMultiThread)
{
this->xSemaphore = xSemaphoreCreateMutex();
}
#endif
this->buffer = new byte[this->size];
this->clear();
}
bool CircularBuffer::CheckIfBeginDone()
{
if (this->buffer == NULL)
{
DIAG(F("Error : CircularBuffer missing begin !"));
return false;
}
return true;
}
void CircularBuffer::end()
{
START_SEMAPHORE()
if (!this->CheckIfBeginDone()) return;
delete[] this->buffer;
this->buffer = NULL; // to be sure to crash at any attemps to use it !
END_SEMAPHORE()
}
void CircularBuffer::clear()
{
if (!this->CheckIfBeginDone()) return;
START_SEMAPHORE()
memset(this->buffer, 0, this->size);
this->full = false;
this->head = this->tail = 0;
this->peakCount = 0;
END_SEMAPHORE()
}
bool CircularBuffer::PushByte(byte inpData)
{
if (!this->CheckIfBeginDone()) return false;
bool ok = true;
START_SEMAPHORE()
if (!this->full)
{
this->buffer[this->head] = inpData;
this->head++;
this->full = this->head == this->tail;
if (this->full)
{
ok = false;
this->peakCount = this->size + 1; // maximum size !
}
}
if (!ok)
{
DIAG(F("Error : the byte has been lost ! Buffer is full !"));
}
END_SEMAPHORE()
this->GetCount(); // update peakCount...
return ok;
}
bool CircularBuffer::PushBytes(byte* inpData, int inDataLength)
{
if (!this->CheckIfBeginDone()) return false;
bool ok = true;
START_SEMAPHORE()
if (!this->full)
{
for (int i = 0; i < inDataLength; i++)
{
this->buffer[this->head] = inpData[i];
this->head = (this->head + 1) % this->size;
this->full = this->head == this->tail;
if (this->full)
{
ok = false;
this->peakCount = this->size + (inDataLength - i); // maximum size !
break;
}
}
}
if (!ok)
{
DIAG(F("Error : bytes has been lost ! Buffer is full !"));
}
END_SEMAPHORE()
this->GetCount(); // update peakCount...
return ok;
}
byte CircularBuffer::GetByte()
{
if (!this->CheckIfBeginDone()) return 0;
byte value = 0;
if (this->isEmpty())
return 0;
START_SEMAPHORE()
value = this->buffer[this->tail];
this->tail = (this->tail + 1) % this->size;
this->full = false;
END_SEMAPHORE()
return value;
}
int16_t CircularBuffer::GetInt16()
{
if (!this->CheckIfBeginDone()) return 0;
if (this->isEmpty() || this->GetCount() < 2)
return 0;
byte value1 = this->GetByte();
byte value2 = this->GetByte();
return int16_t((unsigned char)(value2) << 8 | (unsigned char)(value1));
}
int32_t CircularBuffer::GetInt32()
{
if (!this->CheckIfBeginDone()) return 0;
if (this->isEmpty() || this->GetCount() < 4)
return 0;
byte value1 = this->GetByte();
byte value2 = this->GetByte();
byte value3 = this->GetByte();
byte value4 = this->GetByte();
return int32_t(value4 << 24 | value3 << 16 | value2 << 8 | value1);
}
bool CircularBuffer::GetBytes(byte* inpData, int inDataLength)
{
if (!this->CheckIfBeginDone()) return false;
if (this->GetCount() < inDataLength)
return false;
START_SEMAPHORE()
for (int i = 0; i < inDataLength; i++)
{
inpData[i] = this->buffer[this->tail];
this->tail = (this->tail + 1) % this->size;
}
this->full = false;
END_SEMAPHORE()
return true;
}
int16_t CircularBuffer::GetInt16(byte* pBuffer, int inPos)
{
byte value1 = pBuffer[inPos];
byte value2 = pBuffer[inPos+1];
return int16_t((unsigned char)(value2) << 8 | (unsigned char)(value1));
}
int32_t CircularBuffer::GetInt32(byte* pBuffer, int inPos)
{
byte value1 = pBuffer[inPos];
byte value2 = pBuffer[inPos + 1];
byte value3 = pBuffer[inPos + 2];
byte value4 = pBuffer[inPos + 3];
return int32_t(value4 << 24 | value3 << 16 | value2 << 8 | value1);
}
void CircularBuffer::GetBytes(byte* pBuffer, int inPos, byte* inpData, int inDataLength)
{
memcpy(pBuffer + inPos, inpData, inDataLength);
}
int CircularBuffer::GetCount()
{
if (!this->CheckIfBeginDone()) return 0;
int usedSize = 0;
usedSize = this->size;
if (!this->full)
{
if (this->head >= this->tail)
{
usedSize = this->head - this->tail;
}
else
{
usedSize = this->size + this->head - this->tail;
}
}
if (usedSize > this->peakCount)
this->peakCount = usedSize;
return usedSize;
}
#ifdef DCCPP_DEBUG_MODE
#ifdef VISUALSTUDIO
const char textNum[] = "123456789";
const char textChars[] = "ABCDEF";
const char textSymb[] = "/*-+&$!:;,";
void CircularBuffer::Test()
{
CircularBuffer test(20);
test.begin(false);
Serial.println("After all initialized");
test.printCircularBuffer();
test.PushBytes((byte*)textNum, 9);
Serial.println("After nums pushed");
test.printCircularBuffer();
byte ret = test.GetByte();
Serial.print("ret '1' : ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret '2': ");
Serial.println((int)ret);
test.printCircularBuffer();
test.PushBytes((byte*)textChars, 6);
Serial.println("After chars pushed");
test.printCircularBuffer();
ret = test.GetByte();
Serial.print("ret '3' : ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret '4': ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret '5': ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret '6': ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret '7': ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret '8': ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret '9': ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret 'A': ");
Serial.println((int)ret);
test.printCircularBuffer();
test.PushBytes((byte*)textSymb, 10);
Serial.println("After chars pushed");
test.printCircularBuffer();
ret = test.GetByte();
Serial.print("ret 'B' : ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret 'C' : ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret 'D' : ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret 'E' : ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret 'F' : ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret '/' : ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret '*' : ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret '-' : ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret '+' : ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret '&' : ");
Serial.println((int)ret);
ret = test.GetByte();
Serial.print("ret '$' : ");
Serial.println((int)ret);
test.printCircularBuffer();
Serial.print("Final size : ");
Serial.println((int)test.GetCount());
Serial.print("Max used : ");
Serial.println((int)test.GetPeakCount());
// add too much data in the buffer...
test.PushBytes((byte*)textSymb, 10);
Serial.println("After chars pushed");
test.PushBytes((byte*)textNum, 9);
Serial.println("After chars pushed");
test.printCircularBuffer();
Serial.print("Final size : ");
Serial.println((int)test.GetCount());
Serial.print("Max used : ");
Serial.println((int)test.GetPeakCount());
}
#endif
void CircularBuffer::printCircularBuffer()
{
if (!this->CheckIfBeginDone()) return;
if (this->full)
Serial.println("FULL !");
for (int i = 0; i < this->size; i++)
{
if (i == this->tail)
Serial.print("Tail ");
if (i == this->head)
Serial.print("Head ");
if (i != this->tail && i != this->head)
Serial.print(" ");
Serial.println((int)this->buffer[i]);
}
}
#endif