[poincare] Remove TreePool::insert method, use Helper::Rotate instead

This removes the use of an enormous buffer on the stack and uses instead
a linear algorithm with one temporary data.
This commit is contained in:
Léa Saviot
2018-09-18 15:24:12 +02:00
parent 7722077e69
commit 1852a71f95
5 changed files with 124 additions and 49 deletions

View File

@@ -34,6 +34,7 @@ objs += $(addprefix poincare/src/,\
objs += $(addprefix poincare/src/,\
init.o\
exception_checkpoint.o\
helpers.o\
)
objs += $(addprefix poincare/src/,\

View File

@@ -0,0 +1,18 @@
#ifndef POINCARE_HELPERS_H
#define POINCARE_HELPERS_H
#include <stddef.h>
#include <stdint.h>
namespace Poincare {
namespace Helpers {
size_t Gcd(size_t a, size_t b);
bool Rotate(uint32_t * dst, uint32_t * src, size_t len);
}
}
#endif

View File

@@ -108,7 +108,6 @@ private:
// Pool memory
void * alloc(size_t size);
void dealloc(TreeNode * ptr, size_t size);
bool insert(char * destination, char * source, size_t length);
void moveNodes(TreeNode * destination, TreeNode * source, size_t moveLength);
// Identifiers

94
poincare/src/helpers.cpp Normal file
View File

@@ -0,0 +1,94 @@
#include <poincare/helpers.h>
#include <assert.h>
namespace Poincare {
namespace Helpers {
size_t Gcd(size_t a, size_t b) {
int i = a;
int j = b;
do {
if (i == 0) {
return j;
}
if (j == 0) {
return i;
}
if (i > j) {
i = i - j*(i/j);
} else {
j = j - i*(j/i);
}
} while(true);
}
bool Rotate(uint32_t * dst, uint32_t * src, size_t len) {
/* This method "rotates" an array to insert data at src with length len at
* address dst.
*
* For instance, rotate(2, 4, 1) is:
*
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
* ^¨¨¨¨¨¨¨^---^
* Dst Src Len = 1
*
* After rotation :
* | 0 | 1 | 4 | 2 | 3 | 5 | 6 |
* --- ¨¨¨¨¨¨¨
*/
if (len == 0 || src == dst || (dst > src && dst < src + len)) {
return false;
}
/* We start with the first data to move at address a0 (we chose src but this
* does not matter), move it to its final address (a1 = a0 + len). We then
* move the data that was in a1 to its final address (a2) and so on, until we
* set the data at the starting address a0.
* This is a cycle of changes, and we have to make GCD(len, |dst-src]) such
* cycles, each starting at the previous cycle starting address + 1.
* This is a one-move per data algorithm, so it is O(dst - src) if dst > src,
* else O(src+len - dst). */
size_t numberOfCycles = Gcd(len, dst < src ? src - dst : dst - src);
size_t dstAddressOffset = dst < src ? len : dst - len - src;
// We need the limit addresses of the data that will change
uint32_t * insertionZoneStart = dst < src ? dst : src;
uint32_t * insertionZoneEnd = dst < src ? src + len - 1 : dst - 1;
uint32_t * cycleStartAddress;
uint32_t * moveSrcAddress;
uint32_t * moveDstAddress;
uint32_t tmpData;
uint32_t nextTmpData;
for (size_t i = 0; i < numberOfCycles; i++) {
// Set the cycle starting source
cycleStartAddress = src + i;
assert(cycleStartAddress <= insertionZoneEnd);
moveSrcAddress = cycleStartAddress;
// Set the cycle starting destination, dstAddressOffset after the source
moveDstAddress = moveSrcAddress + dstAddressOffset;
if (moveDstAddress > insertionZoneEnd) {
moveDstAddress = insertionZoneStart + (moveDstAddress - insertionZoneEnd - 1);
}
tmpData = *moveSrcAddress;
do {
nextTmpData = *moveDstAddress;
*moveDstAddress = tmpData;
tmpData = nextTmpData;
moveSrcAddress = moveDstAddress;
moveDstAddress = moveSrcAddress + dstAddressOffset;
if (moveDstAddress > insertionZoneEnd) {
moveDstAddress = insertionZoneStart + (moveDstAddress - insertionZoneEnd - 1);
}
} while (moveSrcAddress != cycleStartAddress);
}
return true;
}
}
}

View File

@@ -1,6 +1,7 @@
#include <poincare/tree_pool.h>
#include <poincare/tree_handle.h>
#include <poincare/exception_checkpoint.h>
#include <poincare/helpers.h>
#include <poincare/tree_handle.h>
#include <poincare/test/tree/blob_node.h>
#include <poincare/test/tree/pair_node.h>
#include <poincare.h>
@@ -11,21 +12,6 @@ namespace Poincare {
TreePool * TreePool::SharedStaticPool;
static void memmove32(uint32_t * dst, uint32_t * src, size_t len) {
if (src < dst && dst < src + len) {
/* Copy backwards to avoid overwrites */
src += len;
dst += len;
while (len--) {
*--dst = *--src;
}
} else {
while (len--) {
*dst++ = *src++;
}
}
}
void TreePool::freeIdentifier(int identifier) {
if (identifier >= 0 && identifier < MaxNumberOfNodes) {
m_nodeForIdentifier[identifier] = nullptr;
@@ -86,14 +72,16 @@ void TreePool::removeChildrenAndDestroy(TreeNode * nodeToDestroy, int nodeNumber
}
void TreePool::moveNodes(TreeNode * destination, TreeNode * source, size_t moveSize) {
if (source == destination || moveSize == 0) {
return;
}
assert(moveSize % 4 == 0);
assert((long)source % 4 == 0);
assert((long)destination % 4 == 0);
char * destinationAddress = reinterpret_cast<char *>(destination);
char * sourceAddress = reinterpret_cast<char *>(source);
if (insert(destinationAddress, sourceAddress, moveSize)) {
updateNodeForIdentifierFromNode(destinationAddress < sourceAddress ? destination : source);
uint32_t * src = reinterpret_cast<uint32_t *>(source);
uint32_t * dst = reinterpret_cast<uint32_t *>(destination);
size_t len = moveSize/4;
if (Helpers::Rotate(dst, src, len)) {
updateNodeForIdentifierFromNode(dst < src ? destination : source);
}
}
@@ -155,31 +143,6 @@ void TreePool::dealloc(TreeNode * node, size_t size) {
updateNodeForIdentifierFromNode(node);
}
bool TreePool::insert(char * destination, char * source, size_t length) {
if (source == destination || (destination > source && destination < source + length)) {
return false;
}
assert(length % 4 == 0);
assert((long)source % 4 == 0);
assert((long)destination % 4 == 0);
uint32_t * src = reinterpret_cast<uint32_t *>(source);
uint32_t * dst = reinterpret_cast<uint32_t *>(destination);
size_t len = length/4;
char tempBuffer[BufferSize];
uint32_t * tmp = reinterpret_cast<uint32_t *>(tempBuffer);
memmove32(reinterpret_cast<uint32_t *>(tmp), src, len);
if (dst < src) {
memmove32(dst + len, dst, src - dst);
memmove32(dst, tmp, len);
} else {
memmove32(src, src + len, dst - (src + len));
memmove32(dst - len, tmp, len);
}
return true;
}
void TreePool::addGhostChildrenAndRename(TreeNode * node) {
/* Ensure the pool is syntaxically correct by creating ghost children for
* nodes that have a fixed, non-zero number of children. */