1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2017-2022, The LegacyClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16
17#include "C4Include.h"
18#include "C4ValueMap.h"
19
20// *** C4ValueMapData ***
21
22C4ValueMapData::C4ValueMapData()
23 : pData(nullptr), pNames(nullptr), bTempNameList(false), pNext(nullptr) {}
24
25C4ValueMapData &C4ValueMapData::operator=(const C4ValueMapData &DataToCopy)
26{
27 SetNameList(DataToCopy.pNames);
28 if (pNames) for (int32_t i = 0; i < pNames->iSize; i++)
29 pData[i].Set(DataToCopy.pData[i]);
30 return *this;
31}
32
33bool C4ValueMapData::operator==(const C4ValueMapData &Data) const
34{
35 if (pNames != Data.pNames) return false;
36 if (pNames)
37 for (int i = 0; i < pNames->iSize; i++)
38 if (pData[i] != Data.pData[i])
39 return false;
40 return true;
41}
42
43C4ValueMapData::~C4ValueMapData()
44{
45 Reset();
46}
47
48void C4ValueMapData::Reset()
49{
50 // unreg from name list (if using one)
51 if (pNames) UnRegister();
52 pNames = nullptr;
53 // free data
54 delete[] pData;
55 pData = nullptr;
56}
57
58void C4ValueMapData::SetNameList(C4ValueMapNames *pnNames)
59{
60 if (pNames == pnNames) return;
61 if (pNames)
62 {
63 // save name array from old name list
64 char **pOldNames = pNames->pNames;
65 int32_t iOldSize = pNames->iSize;
66
67 // unreg from old name list
68 // (manually, because Data::UnRegister() would destroy content)
69 C4ValueMapNames *pNames = this->pNames;
70
71 pNames->UnRegister(pData: this);
72
73 // register at new name list
74 pnNames->Register(pData: this);
75
76 // call OnNameListChanged to copy data and realloc data array
77 OnNameListChanged(pOldNames, iOldSize);
78
79 // delete old names list, if it is temporary
80 if (bTempNameList)
81 delete pNames;
82 bTempNameList = false;
83
84 // ok
85 }
86 else
87 {
88 // simply register...
89 Register(pnNames);
90 }
91}
92
93void C4ValueMapData::Register(C4ValueMapNames *pnNames)
94{
95 // UnReg from old?
96 if (pNames) UnRegister();
97
98 if (pnNames) pnNames->Register(pData: this);
99 pNames = pnNames;
100
101 // alloc data array
102 ReAllocList();
103}
104
105void C4ValueMapData::UnRegister()
106{
107 if (!pNames) return;
108
109 // safe pointer
110 C4ValueMapNames *pNames = this->pNames;
111
112 // unreg
113 pNames->UnRegister(pData: this);
114
115 // delete name list (if it is temporary)
116 if (bTempNameList)
117 delete pNames;
118 bTempNameList = false;
119
120 // delete data array
121 delete[] pData;
122 pData = nullptr;
123}
124
125C4ValueMapNames *C4ValueMapData::CreateTempNameList()
126{
127 // create new list
128 C4ValueMapNames *pTempNames = new C4ValueMapNames();
129
130 // register (this func will unreg if necessary, too)
131 Register(pnNames: pTempNames);
132
133 // error?
134 if (pNames != pTempNames)
135 {
136 delete pTempNames;
137 return nullptr;
138 }
139
140 // set flag
141 bTempNameList = true;
142
143 return pTempNames;
144}
145
146void C4ValueMapData::ReAllocList()
147{
148 if (!pNames)
149 {
150 Reset();
151 return;
152 }
153
154 // delete old array
155 delete[] pData;
156
157 // create new one
158 pData = new C4Value[pNames->iSize]();
159
160 // ok...
161}
162
163void C4ValueMapData::OnNameListChanged(const char *const *pOldNames, int32_t iOldSize)
164{
165 if (!pNames)
166 {
167 Reset();
168 return;
169 }
170
171 // this function does not use ReAllocList because the old values
172 // have to be hold.
173
174 // save pointer on old data
175 C4Value *pOldData = pData;
176
177 // create new data list
178 pData = new C4Value[pNames->iSize]();
179
180 // (try to) copy data
181 int32_t i, j;
182 for (i = 0; i < iOldSize; i++)
183 {
184 // FIXME: This optimization is ugly.
185 if (i < pNames->iSize && SEqual(szStr1: pNames->pNames[i], szStr2: pOldNames[i]))
186 {
187 pOldData[i].Move(nValue: &pData[i]);
188 }
189 else for (j = 0; j < pNames->iSize; j++)
190 {
191 if (SEqual(szStr1: pNames->pNames[j], szStr2: pOldNames[i]))
192 {
193 pOldData[i].Move(nValue: &pData[j]);
194 break; // in hope this will only break the last for...
195 }
196 }
197 }
198 // delete old data array
199 delete[] pOldData;
200}
201
202C4Value *C4ValueMapData::GetItem(int32_t iNr)
203{
204 // the list is nothing without name list...
205 if (!pNames) return nullptr;
206
207 if (iNr >= pNames->iSize) return nullptr;
208
209 return &pData[iNr];
210}
211
212C4Value *C4ValueMapData::GetItem(const char *strName)
213{
214 if (!pNames) return nullptr;
215
216 int32_t iNr = pNames->GetItemNr(strName);
217
218 if (iNr == -1) return nullptr;
219
220 return &pData[iNr];
221}
222
223int32_t C4ValueMapData::GetAnzItems()
224{
225 if (!pNames) return 0;
226 return pNames->iSize;
227}
228
229void C4ValueMapData::DenumeratePointers()
230{
231 if (!pNames) return;
232 for (int32_t i = 0; i < pNames->iSize; i++)
233 pData[i].DenumeratePointer();
234}
235
236void C4ValueMapData::CompileFunc(StdCompiler *pComp)
237{
238 bool fCompiler = pComp->isCompiler();
239 if (fCompiler) Reset();
240 // Compile item count
241 int32_t iValueCnt;
242 if (!fCompiler) iValueCnt = pNames ? pNames->iSize : 0;
243 pComp->Value(rStruct: mkDefaultAdapt(rValue&: iValueCnt, rDefault: 0));
244 // nuthing 2do for no items
245 if (!iValueCnt) return;
246 // Separator (';')
247 pComp->Separator(eSep: StdCompiler::SEP_SEP2);
248 // Data
249 char **ppNames = !fCompiler ? pNames->pNames : new char *[iValueCnt];
250 if (fCompiler) for (int32_t i = 0; i < iValueCnt; i++) ppNames[i] = nullptr;
251 C4Value *pValues = !fCompiler ? pData : new C4Value[iValueCnt];
252 // Compile
253 try
254 {
255 for (int32_t i = 0; i < iValueCnt; i++)
256 {
257 // Separate
258 if (i) pComp->Separator();
259 // Name
260 StdStrBuf Name;
261 if (!fCompiler) Name.Ref(pnData: ppNames[i]);
262 pComp->Value(rStruct: mkParAdapt(rObj&: Name, rPar: StdCompiler::RCT_Idtf));
263 if (fCompiler) ppNames[i] = Name.GrabPointer();
264 // Separator ('=')
265 pComp->Separator(eSep: StdCompiler::SEP_SET);
266 // Value
267 pComp->Value(rStruct&: pValues[i]);
268 }
269 }
270 catch (...)
271 {
272 // make sure no mem is leaked on compiler error in name list
273 if (fCompiler)
274 {
275 for (int32_t i = 0; i < iValueCnt; i++) free(ptr: ppNames[i]);
276 delete[] ppNames;
277 delete[] pValues;
278 }
279 throw;
280 }
281 // Set
282 if (fCompiler)
283 {
284 C4ValueMapNames *pOldNames = pNames;
285 // Set
286 CreateTempNameList();
287 pNames->SetNameArray(pnNames: ppNames, nSize: iValueCnt);
288 for (int32_t i = 0; i < iValueCnt; i++) free(ptr: ppNames[i]);
289 delete[] ppNames; delete[] pData;
290 pData = pValues;
291 // Assign old name list
292 if (pOldNames) SetNameList(pOldNames);
293 }
294}
295
296// *** C4ValueMapNames ***
297
298C4ValueMapNames::C4ValueMapNames()
299 : pNames(nullptr), pExtra(nullptr), iSize(0), pFirst(nullptr) {}
300
301C4ValueMapNames &C4ValueMapNames::operator=(C4ValueMapNames &NamesToCopy)
302{
303 ChangeNameList(pnNames: NamesToCopy.pNames, pnExtra: NamesToCopy.pExtra, nSize: NamesToCopy.iSize);
304 return *this;
305}
306
307C4ValueMapNames::~C4ValueMapNames()
308{
309 Reset();
310}
311
312void C4ValueMapNames::Reset()
313{
314 // unreg all data lists
315 while (pFirst) UnRegister(pData: pFirst);
316 // free name list
317 for (int32_t i = 0; i < iSize; i++)
318 delete[] pNames[i];
319 delete[] pNames;
320 delete[] pExtra;
321 pNames = nullptr; pExtra = nullptr;
322 iSize = 0;
323}
324
325void C4ValueMapNames::Register(C4ValueMapData *pData)
326{
327 // add to begin of list
328 pData->pNext = pFirst;
329 pFirst = pData;
330 // set name list
331 pData->pNames = this;
332}
333
334void C4ValueMapNames::UnRegister(C4ValueMapData *pData)
335{
336 // find in list
337 C4ValueMapData *pAktData = pFirst, *pLastData = nullptr;
338 while (pAktData && pAktData != pData)
339 {
340 pLastData = pAktData;
341 pAktData = pAktData->pNext;
342 }
343
344 if (!pAktData)
345 // isn't registred here...
346 return;
347
348 // unreg
349 if (pLastData)
350 pLastData->pNext = pData->pNext;
351 else
352 pFirst = pData->pNext;
353 pData->pNext = nullptr;
354
355 pData->pNames = nullptr;
356}
357
358void C4ValueMapNames::ChangeNameList(const char *const *pnNames, int32_t *pnExtra, int32_t nSize)
359{
360 // safe old name list
361 char **pOldNames = pNames;
362 int32_t *pOldExtra = pExtra;
363 int32_t iOldSize = iSize;
364
365 // create new lists
366 pNames = new char *[nSize];
367 pExtra = new int32_t[nSize];
368
369 // copy names
370 int32_t i;
371 for (i = 0; i < nSize; i++)
372 {
373 pNames[i] = new char[SLen(sptr: pnNames[i]) + 1];
374 SCopy(szSource: pnNames[i], sTarget: pNames[i], iMaxL: SLen(sptr: pnNames[i]) + 1);
375 if (pnExtra) pExtra[i] = pnExtra[i];
376 }
377
378 if (!pnExtra) std::fill_n(first: pExtra, n: nSize, value: 0);
379
380 // set new size
381 iSize = nSize;
382
383 // call OnNameListChanged list for all "child" lists
384 C4ValueMapData *pAktData = pFirst;
385 while (pAktData)
386 {
387 pAktData->OnNameListChanged(pOldNames, iOldSize);
388 pAktData = pAktData->pNext;
389 }
390
391 // delete old list
392 for (i = 0; i < iOldSize; i++)
393 delete[] pOldNames[i];
394 delete[] pOldNames;
395 delete[] pOldExtra;
396
397 // ok.
398}
399
400void C4ValueMapNames::SetNameArray(const char *const *pnNames, int32_t nSize)
401{
402 // simply pass it through...
403 ChangeNameList(pnNames, pnExtra: nullptr, nSize);
404}
405
406int32_t C4ValueMapNames::AddName(const char *pnName, int32_t iExtra)
407{
408 // name already existing?
409 int32_t iNr;
410 if ((iNr = GetItemNr(strName: pnName)) != -1)
411 return iNr;
412
413 // create new dummy lists
414 const char **pDummyNames = new const char *[iSize + 1];
415 int32_t *pDummyExtra = new int32_t[iSize + 1];
416
417 // copy all data from old list
418 // (danger! if ChangeNameList would now delete them before
419 // creating the new list, this would cause cruel errors...)
420 int32_t i;
421 for (i = 0; i < iSize; i++)
422 {
423 pDummyNames[i] = pNames[i];
424 pDummyExtra[i] = pExtra[i];
425 }
426 pDummyNames[i] = pnName;
427 pDummyExtra[i] = iExtra;
428
429 // change list
430 ChangeNameList(pnNames: pDummyNames, pnExtra: pDummyExtra, nSize: iSize + 1);
431
432 // delete dummy arrays
433 delete[] pDummyNames;
434 delete[] pDummyExtra;
435
436 // return index to new element (simply last element)
437 return iSize - 1;
438}
439
440int32_t C4ValueMapNames::GetItemNr(const char *strName)
441{
442 for (int32_t i = 0; i < iSize; i++)
443 if (SEqual(szStr1: pNames[i], szStr2: strName))
444 return i;
445 return -1;
446}
447