1 /*
2 This file is part of the Okteta Kasten Framework, made within the KDE community.
3
4 SPDX-FileCopyrightText: 2012 Alex Richardson <alex.richardson@gmx.de>
5
6 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8
9 #include "taggeduniondatainformation.hpp"
10
11 #include "topleveldatainformation.hpp"
12 #include "structuredatainformation.hpp"
13 #include "primitive/primitivedatainformation.hpp"
14 #include "../parsers/parserutils.hpp"
15
16 #include <KLocalizedString>
17
TaggedUnionDataInformation(const QString & name,DataInformation * parent)18 TaggedUnionDataInformation::TaggedUnionDataInformation(const QString& name, DataInformation* parent)
19 : DataInformationWithChildren(name, QVector<DataInformation*>(), parent)
20 {
21 }
22
TaggedUnionDataInformation(const TaggedUnionDataInformation & d)23 TaggedUnionDataInformation::TaggedUnionDataInformation(const TaggedUnionDataInformation& d)
24 : DataInformationWithChildren(d)
25 , mDefaultFields(cloneList(d.mDefaultFields, this))
26 {
27 Q_ASSERT(mDefaultFields.isEmpty() || mDefaultFields.at(0) != nullptr);
28 mAlternatives.reserve(d.mAlternatives.size());
29 for (const FieldInfo& fi : d.mAlternatives) {
30 mAlternatives.append(FieldInfo(fi.name, fi.selectIf, cloneList(fi.fields, this)));
31 }
32 }
33
~TaggedUnionDataInformation()34 TaggedUnionDataInformation::~TaggedUnionDataInformation()
35 {
36 qDeleteAll(mDefaultFields);
37 for (const FieldInfo& fi : qAsConst(mAlternatives)) {
38 qDeleteAll(fi.fields);
39 }
40 }
41
typeNameImpl() const42 QString TaggedUnionDataInformation::typeNameImpl() const
43 {
44 if (mLastIndex >= 0) {
45 return i18nc("data type in C/C++, then name", "struct %1", mAlternatives.at(mLastIndex).name);
46 }
47
48 return i18nc("data type, then name", "tagged union %1", name());
49 }
50
appendDefaultField(DataInformation * field,bool emitSignal)51 void TaggedUnionDataInformation::appendDefaultField(DataInformation* field, bool emitSignal)
52 {
53 const uint oldCount = childCount();
54 if (emitSignal && mLastIndex == -1) {
55 topLevelDataInformation()->_childCountAboutToChange(this, oldCount, oldCount + 1);
56 }
57 mDefaultFields.append(field);
58 if (emitSignal && mLastIndex == -1) {
59 topLevelDataInformation()->_childCountChanged(this, oldCount, oldCount + 1);
60 }
61 }
62
setAlternatives(const QVector<FieldInfo> & alternatives,bool emitSignal)63 void TaggedUnionDataInformation::setAlternatives(const QVector<FieldInfo>& alternatives, bool emitSignal)
64 {
65 const uint oldChildCount = childCount();
66 mLastIndex = -1;
67 const uint newChidCount = childCount();
68 if (emitSignal) {
69 topLevelDataInformation()->_childCountAboutToChange(this, oldChildCount, newChidCount);
70 }
71 // remove them all
72 for (const FieldInfo& fi : qAsConst(mAlternatives)) {
73 qDeleteAll(fi.fields);
74 }
75
76 mAlternatives.clear();
77 mAlternatives = alternatives;
78 // set parent
79 for (const FieldInfo& fi : qAsConst(mAlternatives)) {
80 for (auto* field : fi.fields) {
81 field->setParent(this);
82 }
83 }
84
85 if (emitSignal) {
86 topLevelDataInformation()->_childCountChanged(this, oldChildCount, newChidCount);
87 }
88 }
89
determineSelection(TopLevelDataInformation * top)90 int TaggedUnionDataInformation::determineSelection(TopLevelDataInformation* top)
91 {
92 // now find out which one of the alternatives to select
93 for (int i = 0; i < mAlternatives.size(); ++i) {
94 const FieldInfo& fi = mAlternatives.at(i);
95 if (fi.selectIf.isFunction()) {
96 QScriptValue result = top->scriptHandler()->callFunction(fi.selectIf, this,
97 ScriptHandlerInfo::Mode::TaggedUnionSelection);
98 if (!result.isBool()) {
99 logError() << "Evaluating select function for alternative" << i
100 << "did not return a boolean value. Got the following instead:" << result.toString();
101 continue;
102 }
103 if (result.toBool()) {
104 return i;
105 }
106 } else {
107 ParsedNumber<quint64> number = ParserUtils::uint64FromScriptValue(fi.selectIf);
108 if (!number.isValid) {
109 logError() << "Alternative number" << i << "is not valid. SelectIf"
110 " is neither function nor number:" << fi.selectIf.toString();
111 continue;
112
113 }
114 // number is valid -> there must be exactly one field
115 if (mChildren.size() != 1) {
116 logError() << "Alternative number" << i << "is not valid. SelectIf is number, but there is not exactly one child!";
117 continue;
118 }
119 if (!mChildren.at(0)->isPrimitive()) {
120 logError() << "Alternative number" << i << "is not valid. SelectIf is number, but only child is not primitive!";
121 continue;
122 }
123 if (mChildren.at(0)->asPrimitive()->value() == number.value) {
124 return i; // found it
125 }
126 }
127 }
128
129 return -1;
130 }
131
readData(Okteta::AbstractByteArrayModel * input,Okteta::Address address,BitCount64 bitsRemaining,quint8 * bitOffset)132 qint64 TaggedUnionDataInformation::readData(Okteta::AbstractByteArrayModel* input,
133 Okteta::Address address, BitCount64 bitsRemaining, quint8* bitOffset)
134 {
135 Q_ASSERT(mHasBeenUpdated);
136 // update must have been called prior to reading
137 TopLevelDataInformation* top = topLevelDataInformation();
138 Q_CHECK_PTR(top);
139
140 const QVector<DataInformation*>& oldChildren = currentChildren();
141
142 qint64 readBits = 0;
143 mWasAbleToRead = StructureDataInformation::readChildren(mChildren,
144 input, address, bitsRemaining, bitOffset, &readBits, top);
145 mLastIndex = determineSelection(top);
146 const QVector<DataInformation*>& others = currentChildren();
147 // check whether we have different children now, if yes we have to emit child count changed
148 if (oldChildren != others) {
149 const int fixedSize = mChildren.size();
150 // tell the model that all children have changed by setting to 0 and then to new size
151 top->_childCountAboutToChange(this, fixedSize + oldChildren.size(), fixedSize);
152 top->_childCountChanged(this, fixedSize + oldChildren.size(), fixedSize);
153 top->_childCountAboutToChange(this, fixedSize, fixedSize + others.size());
154 top->_childCountChanged(this, fixedSize, fixedSize + others.size());
155 }
156
157 // this is important since the remaining children might have changed since before the read
158 // where beginRead was called on the children at that time
159 for (auto* other : others) {
160 other->beginRead();
161 }
162
163 if (!mWasAbleToRead) {
164 Q_ASSERT(readBits == -1);
165 return -1;
166 }
167 // otherwise continue reading the remaining children
168 mWasAbleToRead = StructureDataInformation::readChildren(others,
169 input, address, bitsRemaining, bitOffset, &readBits, top);
170 return readBits;
171 }
172
childPosition(const DataInformation * child,Okteta::Address start) const173 BitCount64 TaggedUnionDataInformation::childPosition(const DataInformation* child,
174 Okteta::Address start) const
175 {
176 BitCount64 offset = 0;
177 bool found = false;
178 // sum size of elements up to index
179 for (auto* current : mChildren) {
180 if (current == child) {
181 found = true;
182 break;
183 }
184 offset += current->size();
185 }
186
187 if (!found) {
188 const QVector<DataInformation*> others = currentChildren();
189 for (auto* current : others) {
190 if (current == child) {
191 found = true;
192 break;
193 }
194 offset += current->size();
195 }
196 }
197 Q_ASSERT(found);
198 if (mParent->isTopLevel()) {
199 return start * 8 + offset;
200 }
201
202 return mParent->asDataInformation()->childPosition(this, start) + offset;
203 }
204
size() const205 BitCount32 TaggedUnionDataInformation::size() const
206 {
207 BitCount32 total = 0;
208 for (auto* child : mChildren) {
209 total += child->size();
210 }
211
212 const QVector<DataInformation*> others = currentChildren();
213 for (auto* other : others) {
214 total += other->size();
215 }
216
217 return total;
218 }
219
replaceChildAt(unsigned int index,DataInformation * newChild)220 bool TaggedUnionDataInformation::replaceChildAt(unsigned int index, DataInformation* newChild)
221 {
222 Q_ASSERT(false); // TODO implement
223 Q_UNUSED(index)
224 Q_UNUSED(newChild)
225 return false;
226 }
227
indexOf(const DataInformation * const data) const228 int TaggedUnionDataInformation::indexOf(const DataInformation* const data) const
229 {
230 int index = 0;
231 for (auto* child : mChildren) {
232 if (child == data) {
233 return index;
234 }
235 index++;
236 }
237
238 const QVector<DataInformation*> others = currentChildren();
239 for (auto* other : others) {
240 if (other == data) {
241 return index;
242 }
243 index++;
244 }
245
246 Q_ASSERT(false); // should never land here
247 return -1;
248 }
249
childAt(unsigned int index) const250 DataInformation* TaggedUnionDataInformation::childAt(unsigned int index) const
251 {
252 const uint permanentChildCount = uint(mChildren.size());
253 if (index < permanentChildCount) {
254 return mChildren.at(index);
255 }
256 const QVector<DataInformation*> others = currentChildren();
257 if (index < permanentChildCount + others.size()) {
258 return others.at(index - permanentChildCount);
259 }
260 Q_ASSERT(false); // should never happen
261 return nullptr;
262 }
263
childCount() const264 unsigned int TaggedUnionDataInformation::childCount() const
265 {
266 return mChildren.size() + currentChildren().size();
267 }
268
isTaggedUnion() const269 bool TaggedUnionDataInformation::isTaggedUnion() const
270 {
271 return true;
272 }
273