Skip to content

Commit c82dadb

Browse files
authored
Merge pull request #576 from scratchcpp/refactor_list
Refactor List class
2 parents 18654df + 5373108 commit c82dadb

File tree

12 files changed

+1947
-222
lines changed

12 files changed

+1947
-222
lines changed

Doxyfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -958,7 +958,9 @@ RECURSIVE = YES
958958
# run.
959959

960960
EXCLUDE = docs/theme \
961-
include/scratchcpp/spimpl.h
961+
include/scratchcpp/spimpl.h \
962+
include/scratchcpp/signal.h \
963+
include/scratchcpp/veque.h
962964

963965
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
964966
# directories that are symbolic links (a Unix file system feature) are excluded

include/scratchcpp/list.h

Lines changed: 188 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#pragma once
44

55
#include <string>
6-
#include <deque>
6+
#include <scratchcpp/veque.h>
77

88
#include "value.h"
99
#include "entity.h"
@@ -15,15 +15,19 @@ class Target;
1515
class Monitor;
1616
class ListPrivate;
1717

18-
/*! \brief The List class represents a Scratch list. */
19-
class LIBSCRATCHCPP_EXPORT List
20-
: public std::deque<Value>
21-
, public Entity
18+
/*!
19+
* \brief The List class represents a Scratch list.
20+
*
21+
* Due to the high optimization of the methods, indices out of range will result in undefined behavior!
22+
*/
23+
class LIBSCRATCHCPP_EXPORT List : public Entity
2224
{
2325
public:
2426
List(const std::string &id, const std::string &name);
2527
List(const List &) = delete;
2628

29+
~List();
30+
2731
const std::string &name();
2832
void setName(const std::string &name);
2933

@@ -33,24 +37,197 @@ class LIBSCRATCHCPP_EXPORT List
3337
Monitor *monitor() const;
3438
void setMonitor(Monitor *monitor);
3539

36-
long indexOf(const Value &value) const;
37-
bool contains(const Value &value) const;
40+
/*! Returns the list size. */
41+
inline size_t size() const { return m_size; }
42+
43+
/*! Returns true if the list is empty. */
44+
inline bool empty() const { return m_size == 0; }
45+
46+
/*! Returns the index of the given item. */
47+
inline size_t indexOf(const ValueData &value) const
48+
{
49+
for (size_t i = 0; i < m_size; i++) {
50+
if (value_equals(&m_dataPtr->operator[](i), &value))
51+
return i;
52+
}
53+
54+
return -1;
55+
}
56+
57+
/*! Returns the index of the given item. */
58+
inline size_t indexOf(const Value &value) const { return indexOf(value.data()); }
59+
60+
/*! Returns true if the list contains the given item. */
61+
inline bool contains(const ValueData &value) const { return (indexOf(value) != -1); }
62+
63+
/*! Returns true if the list contains the given item. */
64+
inline bool contains(const Value &value) const { return contains(value.data()); }
65+
66+
/*! Clears the list. */
67+
inline void clear()
68+
{
69+
// Keep at least 200,000 items allocated if the list has more
70+
constexpr size_t limit = 200000;
71+
m_size = 0;
72+
73+
if (m_dataPtr->size() > limit)
74+
reserve(limit);
75+
}
76+
77+
/*! Appends an empty item and returns the reference to it. Can be used for custom initialization. */
78+
inline ValueData &appendEmpty()
79+
{
80+
m_size++;
81+
reserve(getAllocSize(m_size));
82+
return m_dataPtr->operator[](m_size - 1);
83+
}
84+
85+
/*! Appends an item. */
86+
inline void append(const ValueData &value) { value_assign_copy(&appendEmpty(), &value); }
87+
88+
/*! Appends an item. */
89+
inline void append(const Value &value) { append(value.data()); }
3890

3991
/*! Removes the item at index. */
40-
void removeAt(int index) { erase(begin() + index); }
92+
inline void removeAt(size_t index)
93+
{
94+
assert(index >= 0 && index < size());
95+
std::rotate(m_dataPtr->begin() + index, m_dataPtr->begin() + index + 1, m_dataPtr->begin() + m_size);
96+
m_size--;
97+
}
98+
99+
/*! Inserts an item at index. */
100+
inline void insert(size_t index, const ValueData &value)
101+
{
102+
assert(index >= 0 && index <= size());
103+
m_size++;
104+
reserve(getAllocSize(m_size));
105+
std::rotate(m_dataPtr->rbegin() + m_dataPtr->size() - m_size, m_dataPtr->rbegin() + m_dataPtr->size() - m_size + 1, m_dataPtr->rend() - index);
106+
value_assign_copy(&m_dataPtr->operator[](index), &value);
107+
}
41108

42109
/*! Inserts an item at index. */
43-
void insert(int index, const Value &value) { std::deque<Value>::insert(begin() + index, value); }
110+
inline void insert(size_t index, const Value &value) { insert(index, value.data()); }
44111

45112
/*! Replaces the item at index. */
46-
void replace(int index, const Value &value) { at(index) = value; }
113+
inline void replace(size_t index, const ValueData &value)
114+
{
115+
assert(index >= 0 && index < size());
116+
value_assign_copy(&m_dataPtr->operator[](index), &value);
117+
}
47118

48-
std::string toString() const;
119+
/*! Replaces the item at index. */
120+
inline void replace(size_t index, const Value &value) { replace(index, value.data()); }
121+
122+
inline ValueData &operator[](size_t index)
123+
{
124+
assert(index >= 0 && index < size());
125+
return m_dataPtr->operator[](index);
126+
}
127+
128+
/*! Joins the list items with spaces or without any separator if there are only digits and stores the result in dst. */
129+
inline void toString(std::string &dst) const
130+
{
131+
dst.clear();
132+
veque::veque<std::string> strings;
133+
strings.reserve(m_size);
134+
bool digits = true;
135+
size_t i;
136+
137+
for (i = 0; i < m_size; i++) {
138+
const ValueData *item = &m_dataPtr->operator[](i);
139+
strings.push_back(std::string());
140+
value_toString(item, &strings.back());
141+
142+
if (value_isValidNumber(item) && !strings.back().empty()) {
143+
double doubleNum = value_toDouble(item);
144+
long num = value_toLong(item);
145+
146+
if (doubleNum != num) {
147+
digits = false;
148+
break;
149+
}
150+
151+
if (num < 0 || num >= 10) {
152+
digits = false;
153+
break;
154+
}
155+
} else {
156+
digits = false;
157+
break;
158+
}
159+
}
160+
161+
std::string s;
162+
163+
if (digits) {
164+
for (i = 0; i < strings.size(); i++)
165+
dst.append(strings[i]);
166+
167+
for (; i < m_size; i++) {
168+
value_toString(&m_dataPtr->operator[](i), &s);
169+
dst.append(s);
170+
}
171+
} else {
172+
for (i = 0; i < strings.size(); i++) {
173+
dst.append(strings[i]);
174+
175+
if (i + 1 < m_size)
176+
dst.push_back(' ');
177+
}
178+
179+
for (; i < m_size; i++) {
180+
value_toString(&m_dataPtr->operator[](i), &s);
181+
dst.append(s);
182+
183+
if (i + 1 < m_size)
184+
dst.push_back(' ');
185+
}
186+
}
187+
}
188+
189+
/*! Same as the other method, but returns the result directly. */
190+
inline std::string toString() const
191+
{
192+
std::string ret;
193+
toString(ret);
194+
return ret;
195+
}
49196

50197
std::shared_ptr<List> clone();
51198

52199
private:
200+
inline void reserve(size_t size)
201+
{
202+
assert(size >= m_size);
203+
204+
while (size > m_dataPtr->size()) {
205+
m_dataPtr->push_back(ValueData());
206+
value_init(&m_dataPtr->back());
207+
}
208+
209+
while (size < m_dataPtr->size()) {
210+
value_free(&m_dataPtr->back());
211+
m_dataPtr->erase(m_dataPtr->end());
212+
}
213+
}
214+
215+
inline size_t getAllocSize(size_t x)
216+
{
217+
if (x == 0)
218+
return 0;
219+
220+
size_t ret = 1;
221+
222+
while (ret < x)
223+
ret *= 2;
224+
225+
return ret;
226+
}
227+
53228
spimpl::unique_impl_ptr<ListPrivate> impl;
229+
veque::veque<ValueData> *m_dataPtr = nullptr; // NOTE: accessing through pointer is faster! (from benchmarks)
230+
size_t m_size = 0;
54231
};
55232

56233
} // namespace libscratchcpp

include/scratchcpp/value.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ class LIBSCRATCHCPP_EXPORT Value
8484
value_assign_special(&m_data, specialValue);
8585
}
8686

87+
/*! Constructs value from ValueData. */
88+
Value(const ValueData &v)
89+
{
90+
value_init(&m_data);
91+
value_assign_copy(&m_data, &v);
92+
}
93+
8794
Value(const Value &v)
8895
{
8996
value_init(&m_data);
@@ -92,6 +99,18 @@ class LIBSCRATCHCPP_EXPORT Value
9299

93100
~Value() { value_free(&m_data); }
94101

102+
/*!
103+
* Returns a read-only reference to the data.
104+
* \note Valid until the Value object is destroyed.
105+
*/
106+
const ValueData &data() const { return m_data; };
107+
108+
/*!
109+
* Returns a read/write reference to the data.
110+
* \note Valid until the Value object is destroyed.
111+
*/
112+
ValueData &data() { return m_data; }
113+
95114
/*! Returns the type of the value. */
96115
ValueType type() const { return m_data.type; }
97116

@@ -204,6 +223,12 @@ class LIBSCRATCHCPP_EXPORT Value
204223
return *this;
205224
}
206225

226+
const Value &operator=(const ValueData &v)
227+
{
228+
value_assign_copy(&m_data, &v);
229+
return *this;
230+
}
231+
207232
const Value &operator=(const Value &v)
208233
{
209234
value_assign_copy(&m_data, &v.m_data);

0 commit comments

Comments
 (0)