Files
Upsilon/apps/code/accordion.cpp
Léa Saviot c543145aae [code] Optimized MenuController's script list scrolling speed.
To know if the AddScript row should be displayed, we need to compute the free
space size of the accordion, which is very slow. We thus memoized a bool
indicating if the AddScript row should be displayed.

Change-Id: Ia3891c0fc8df9b0b8bf2a9ad053be6dfc848c970
2017-11-17 14:05:23 +01:00

225 lines
6.1 KiB
C++

#include "accordion.h"
#include <assert.h>
#include <string.h>
Accordion::Accordion(char * buffer, int bufferSize) :
m_historySize(bufferSize),
m_history(buffer),
m_numberOfBuffers(0),
m_startOfLastEditedBuffer(0)
{
for (int i = 0; i < m_historySize; i ++) {
m_history[i] = k_freeSpaceMarker;
}
}
const char * Accordion::bufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int startOfBuffer = startOfBufferAtIndex(index);
return &m_history[startOfBuffer];
}
char * Accordion::editableBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
moveFreeSpaceAtEndOfBufferAtIndex(index);
int startOfBuffer = startOfBufferAtIndex(index);
return &m_history[startOfBuffer];
}
int Accordion::sizeOfEditableBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
moveFreeSpaceAtEndOfBufferAtIndex(index);
int length = 0;
for (int i = startOfBufferAtIndex(index); i < m_historySize-1; i++) {
if (m_history[i] == k_freeSpaceMarker && m_history[i+1] != k_freeSpaceMarker) {
break;
}
length++;
// We do not count one Free Space Marker, in order to always have at a Free
// Space of size at least one.
}
return length;
}
bool Accordion::appendBuffer(const char * buffer) {
cleanFreeSpace();
moveFreeSpaceAtEndOfHistory();
int len = strlen(buffer);
if (len + 1 > freeSpaceSize() - 1) { // We keep at keast one Free char.
return false;
}
m_startOfLastEditedBuffer = startOfFreeSpace();
memcpy(&m_history[m_startOfLastEditedBuffer], buffer, len+1);
m_numberOfBuffers++;
return true;
}
bool Accordion::replaceBufferAtIndex(int index, const char * buffer) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int len = strlen(buffer);
if (len < sizeOfEditableBufferAtIndex(index)) {
int startOfOldBuffer = startOfBufferAtIndex(index);
memcpy(&m_history[startOfOldBuffer], buffer, len+1);
m_startOfLastEditedBuffer = startOfOldBuffer;
return true;
}
return false;
}
void Accordion::deleteBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
moveFreeSpaceAtEndOfBufferAtIndex(index);
int i = startOfBufferAtIndex(index);
while (i < m_historySize && m_history[i] != k_freeSpaceMarker) {
m_history[i] = k_freeSpaceMarker;
i++;
}
m_numberOfBuffers--;
}
void Accordion::deleteLastBuffer() {
cleanFreeSpace();
if (m_numberOfBuffers > 0) {
deleteBufferAtIndex(m_numberOfBuffers-1);
}
}
void Accordion::deleteAll() {
cleanFreeSpace();
for (int i = 0; i < m_historySize; i++){
m_history[i] = k_freeSpaceMarker;
}
m_numberOfBuffers = 0;
}
int Accordion::freeSpaceSize() {
cleanFreeSpace();
int sizeOfFreeSpace = 0;
int freeSpaceStart = startOfFreeSpace();
for (int i = freeSpaceStart; i < m_historySize; i++) {
if (m_history[i] == k_freeSpaceMarker) {
sizeOfFreeSpace++;
} else {
return sizeOfFreeSpace;
}
}
return sizeOfFreeSpace;
}
int Accordion::startOfBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int bufferCount = 0;
int startOfBuffer = 0;
while (m_history[startOfBuffer] == k_freeSpaceMarker && startOfBuffer < m_historySize) {
startOfBuffer++;
}
for (int i = startOfBuffer; i < m_historySize; i++) {
if (bufferCount == index) {
while (m_history[i] == k_freeSpaceMarker && i < m_historySize) {
i++;
}
return i;
}
if (m_history[i] == 0) {
bufferCount++;
}
}
assert(false);
return 0;
}
int Accordion::endOfBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int startOfBuffer = startOfBufferAtIndex(index);
for (int i = startOfBuffer; i < m_historySize; i++) {
if (m_history[i] == 0) {
return i;
}
}
assert(false);
return 0;
}
int Accordion::startOfFreeSpace() {
cleanFreeSpace();
for (int i = 0; i < m_historySize; i++) {
if (m_history[i] == k_freeSpaceMarker) {
return i;
}
}
assert(false);
return 0;
}
void Accordion::cleanFreeSpace() {
if (m_history[m_startOfLastEditedBuffer] == k_freeSpaceMarker) {
return;
}
int indexOfCharToChangeIntoFreeSpaceMarker = m_startOfLastEditedBuffer
+ strlen(&m_history[m_startOfLastEditedBuffer]) + 1;
while (m_history[indexOfCharToChangeIntoFreeSpaceMarker] != k_freeSpaceMarker
&& indexOfCharToChangeIntoFreeSpaceMarker < m_historySize)
{
m_history[indexOfCharToChangeIntoFreeSpaceMarker] = k_freeSpaceMarker;
indexOfCharToChangeIntoFreeSpaceMarker++;
}
}
void Accordion::moveFreeSpaceAtPosition(int i) {
assert(i >= 0 && i <= m_historySize);
cleanFreeSpace();
int freeSpaceStart = startOfFreeSpace();
if (freeSpaceStart != i){
// First, move the chars that would be overriden by the free space.
// The indexes depend on the relative positions of the free space and the
// new destination.
int sizeFreeSpace = freeSpaceSize();
int len, src, dst, newFreeSpaceStart;
if (freeSpaceStart > i) {
len = freeSpaceStart - i;
src = i;
dst = i + sizeFreeSpace;
newFreeSpaceStart = i;
} else {
src = freeSpaceStart + sizeFreeSpace;
len = i - src;
dst = freeSpaceStart;
newFreeSpaceStart = i-sizeFreeSpace;
}
memmove(&m_history[dst], &m_history[src], len);
// Then move the free space.
for (int j = newFreeSpaceStart ; j < newFreeSpaceStart+sizeFreeSpace; j++) {
m_history[j] = k_freeSpaceMarker;
}
}
m_startOfLastEditedBuffer = i-1;
while (m_startOfLastEditedBuffer > 0 && m_history[m_startOfLastEditedBuffer-1] != 0 ) {
m_startOfLastEditedBuffer--;
}
}
void Accordion::moveFreeSpaceAtEndOfBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int endOfBuffer = endOfBufferAtIndex(index);
moveFreeSpaceAtPosition(endOfBuffer+1);
}
void Accordion::moveFreeSpaceAtEndOfHistory() {
cleanFreeSpace();
if (m_numberOfBuffers > 0) {
moveFreeSpaceAtEndOfBufferAtIndex(m_numberOfBuffers-1);
}
}