-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathVariableDynamicStack.h
174 lines (156 loc) · 4.4 KB
/
VariableDynamicStack.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#pragma once
#include <ConfigurableFirmata.h>
#include <FirmataFeature.h>
#include "MemoryManagement.h"
#include "ObjectVector.h"
#include "Variable.h"
#include "Exceptions.h"
/// <summary>
/// A stack that can hold arbitrarily sized variables and auto-extends if needed
/// This stack grows from smaller to larger addresses, basically working like a linked list
/// </summary>
class VariableDynamicStack
{
private:
uint32_t _bytesAllocated;
Variable* _begin; // bottom of stack
Variable* _sp; // the stack pointer (points to next element that will be used)
int* _revPtr; // points to the tail of the stack. This field contains the size of the previous element. It is always 4 bytes in front of _sp
public:
class Iterator
{
Variable* _iter;
Variable* _begin;
int* _rev;
public:
Iterator(Variable* begin, Variable* sp, int* revPtr)
{
_begin = begin;
_iter = sp;
_rev = revPtr;
}
/// <summary>
/// Gets the next element in the stack (iterating in reverse).
/// The iteration starts before the first element.
/// Returns null at the end.
/// </summary>
Variable* next()
{
if (_iter == _begin)
{
return nullptr;
}
_iter = AddBytes(_iter, -*_rev);
_rev = (int*)AddBytes(_iter, -4);
return _iter;
}
};
VariableDynamicStack(int initialElements)
{
_bytesAllocated = (initialElements * sizeof(Variable)) + sizeof(void*);
_begin = (Variable*)mallocEx(_bytesAllocated);
if (_begin == nullptr)
{
_revPtr = nullptr;
_sp = nullptr;
stdSimple::OutOfMemoryException::Throw("Out of memory initializing dynamic stack");
return;
}
memset(_begin, 0, _bytesAllocated);
_revPtr = (int*)AddBytes(_begin, -4); // Points before start, but won't be used until at least one element is on the stack
_sp = _begin;
}
~VariableDynamicStack()
{
if (_begin != nullptr)
{
freeEx(_begin);
}
_begin = nullptr;
_sp = nullptr;
_revPtr = nullptr;
}
bool empty() const
{
return (_sp == _begin);
}
uint32_t BytesUsed() const
{
return ByteDifference(_sp, _begin);
}
uint32_t FreeBytes() const
{
return _bytesAllocated - BytesUsed();
}
void push(const Variable& object)
{
// This might be 8, depending on compiler alignment of i64 and double types, therefore it is different from sizeof(VariableDescription)
const int variableDescriptionSizeUsed = Variable::headersize();
uint32_t sizeUsed = MAX(object.fieldSize(), 8) + variableDescriptionSizeUsed + 4; // + 4 for the tail (the reverse pointer)
if (sizeUsed > FreeBytes())
{
uint32_t newSize = _bytesAllocated + sizeUsed; // Extend so that it certainly matches
Variable* newBegin = (Variable*)realloc(_begin, newSize); // with this, also _sp and _revPtr become invalid
if (newBegin == nullptr)
{
stdSimple::OutOfMemoryException::Throw("Out of memory increasing dynamic stack");
}
int oldOffset = BytesUsed();
_sp = AddBytes(newBegin, oldOffset); // be sure to calculate in bytes
_revPtr = (int*)AddBytes(newBegin, oldOffset - 4);
_bytesAllocated = newSize;
_begin = newBegin;
}
// Now we have enough room for the new variable
_sp->setSize(object.fieldSize()); // Tell the operator that it is fine to copy the stuff here
*_sp = object; // call operator =
Variable* oldsp = _sp;
_sp = AddBytes(_sp, sizeUsed);
_revPtr = (int*)AddBytes(_sp, -4);
*_revPtr = ByteDifference(_sp, oldsp);
}
Variable& top() const
{
if (empty())
{
Firmata.sendString(F("FATAL: Execution stack underflow"));
throw stdSimple::ExecutionEngineException("Execution stack underflow");
}
Variable* lastElem = AddBytes(_sp, -*_revPtr);
return *lastElem;
}
/// <summary>
/// Removes the last element from the stack.
/// Any previously retrieved elements (using top() or nth() stay valid until the next push)
/// </summary>
void pop()
{
if (empty())
{
Firmata.sendString(F("FATAL: Execution stack underflow"));
throw stdSimple::ExecutionEngineException("Execution stack underflow");
}
Variable* lastElem = AddBytes(_sp, -*_revPtr);
_sp = lastElem;
_revPtr = (int*)AddBytes(_sp, -4);
}
// Returns the nth-last element from the stack (0 being the top)
Variable& nth(int index)
{
int i = index;
Variable* iter = _sp;
int* rev = _revPtr;
// One iteration for index == 0
while (i >= 0)
{
iter = AddBytes(iter, -*rev);
rev = (int*)AddBytes(iter, -4);
i--;
}
return *iter;
}
Iterator GetIterator()
{
return Iterator(_begin, _sp, _revPtr);
}
};