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;
1515class Monitor ;
1616class 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
0 commit comments