| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) RedWolf Design |
| 5 | * Copyright (c) 2001, Sven2 |
| 6 | * Copyright (c) 2017-2021, The LegacyClonk Team and contributors |
| 7 | * |
| 8 | * Distributed under the terms of the ISC license; see accompanying file |
| 9 | * "COPYING" for details. |
| 10 | * |
| 11 | * "Clonk" is a registered trademark of Matthes Bender, used with permission. |
| 12 | * See accompanying file "TRADEMARK" for details. |
| 13 | * |
| 14 | * To redistribute this file separately, substitute the full license texts |
| 15 | * for the above references. |
| 16 | */ |
| 17 | |
| 18 | // C4Aul script engine CP conversion |
| 19 | |
| 20 | #include <C4Include.h> |
| 21 | #include <C4Aul.h> |
| 22 | |
| 23 | #include <C4Config.h> |
| 24 | #include <C4Def.h> |
| 25 | #include <C4Log.h> |
| 26 | #include <C4Components.h> |
| 27 | |
| 28 | #include <format> |
| 29 | |
| 30 | C4AulError::C4AulError() {} |
| 31 | |
| 32 | void C4AulError::show() const |
| 33 | { |
| 34 | // simply log error message |
| 35 | if (!message.empty()) |
| 36 | DebugLog(level: isWarning ? spdlog::level::warn : spdlog::level::err, message); |
| 37 | } |
| 38 | |
| 39 | C4AulFunc::C4AulFunc(C4AulScript *pOwner, const char *pName, bool bAtEnd) : |
| 40 | MapNext(nullptr), |
| 41 | LinkedTo(nullptr), |
| 42 | OverloadedBy(nullptr), |
| 43 | NextSNFunc(nullptr) |
| 44 | { |
| 45 | // reg2list (at end or at the beginning) |
| 46 | Owner = pOwner; |
| 47 | if (bAtEnd) |
| 48 | { |
| 49 | if (Prev = Owner->FuncL) |
| 50 | { |
| 51 | Prev->Next = this; |
| 52 | Owner->FuncL = this; |
| 53 | } |
| 54 | else |
| 55 | { |
| 56 | Owner->Func0 = this; |
| 57 | Owner->FuncL = this; |
| 58 | } |
| 59 | Next = nullptr; |
| 60 | } |
| 61 | else |
| 62 | { |
| 63 | if (Next = Owner->Func0) |
| 64 | { |
| 65 | Next->Prev = this; |
| 66 | Owner->Func0 = this; |
| 67 | } |
| 68 | else |
| 69 | { |
| 70 | Owner->Func0 = this; |
| 71 | Owner->FuncL = this; |
| 72 | } |
| 73 | Prev = nullptr; |
| 74 | } |
| 75 | |
| 76 | // store name |
| 77 | SCopy(szSource: pName, sTarget: Name, C4AUL_MAX_Identifier); |
| 78 | // add to global lookuptable with this name |
| 79 | Owner->Engine->FuncLookUp.Add(func: this, bAtEnd); |
| 80 | } |
| 81 | |
| 82 | C4AulFunc::~C4AulFunc() |
| 83 | { |
| 84 | // if it's a global: remove the global link! |
| 85 | if (LinkedTo && Owner) |
| 86 | if (LinkedTo->Owner == Owner->Engine) |
| 87 | delete LinkedTo; |
| 88 | // unlink func |
| 89 | if (LinkedTo) |
| 90 | { |
| 91 | // find prev |
| 92 | C4AulFunc *pAkt = this; |
| 93 | while (pAkt->LinkedTo != this) pAkt = pAkt->LinkedTo; |
| 94 | if (pAkt == LinkedTo) |
| 95 | pAkt->LinkedTo = nullptr; |
| 96 | else |
| 97 | pAkt->LinkedTo = LinkedTo; |
| 98 | LinkedTo = nullptr; |
| 99 | } |
| 100 | // remove from list |
| 101 | if (Prev) Prev->Next = Next; |
| 102 | if (Next) Next->Prev = Prev; |
| 103 | if (Owner) |
| 104 | { |
| 105 | if (Owner->Func0 == this) Owner->Func0 = Next; |
| 106 | if (Owner->FuncL == this) Owner->FuncL = Prev; |
| 107 | Owner->Engine->FuncLookUp.Remove(func: this); |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | void C4AulFunc::DestroyLinked() |
| 112 | { |
| 113 | // delete all functions linked to this one. |
| 114 | while (LinkedTo) |
| 115 | delete LinkedTo; |
| 116 | } |
| 117 | |
| 118 | C4AulFunc *C4AulFunc::GetLocalSFunc(const char *szIdtf) |
| 119 | { |
| 120 | // owner is engine, i.e. this is a global func? |
| 121 | if (Owner == Owner->Engine && LinkedTo) |
| 122 | { |
| 123 | // then search linked scope first |
| 124 | if (C4AulFunc *pFn = LinkedTo->Owner->GetSFunc(pIdtf: szIdtf)) return pFn; |
| 125 | } |
| 126 | // search local owner list |
| 127 | return Owner->GetSFunc(pIdtf: szIdtf); |
| 128 | } |
| 129 | |
| 130 | C4AulFunc *C4AulFunc::FindSameNameFunc(C4Def *pScope) |
| 131 | { |
| 132 | // Note: NextSNFunc forms a ring, not a list |
| 133 | // find function |
| 134 | C4AulFunc *pFunc = this, *pResult = nullptr; |
| 135 | do |
| 136 | { |
| 137 | // definition matches? This is the one |
| 138 | if (pFunc->Owner->Def == pScope) |
| 139 | { |
| 140 | pResult = pFunc; break; |
| 141 | } |
| 142 | // global function? Set func, but continue searching |
| 143 | // (may be overloaded by a local fn) |
| 144 | if (pFunc->Owner == Owner->Engine) |
| 145 | pResult = pFunc; |
| 146 | } while ((pFunc = pFunc->NextSNFunc) && pFunc != this); |
| 147 | return pResult; |
| 148 | } |
| 149 | |
| 150 | std::string C4AulScriptFunc::GetFullName() |
| 151 | { |
| 152 | // "lost" function? |
| 153 | std::string owner; |
| 154 | if (!Owner) |
| 155 | { |
| 156 | owner = "(unknown) " ; |
| 157 | } |
| 158 | else if (Owner->Def) |
| 159 | { |
| 160 | owner = std::format(fmt: "{}::" , args: C4IdText(id: Owner->Def->id)); |
| 161 | } |
| 162 | else if (Owner->Engine == Owner) |
| 163 | { |
| 164 | owner = "global " ; |
| 165 | } |
| 166 | else |
| 167 | { |
| 168 | owner = "game " ; |
| 169 | } |
| 170 | |
| 171 | owner += Name; |
| 172 | return owner; |
| 173 | } |
| 174 | |
| 175 | C4AulScript::C4AulScript() |
| 176 | { |
| 177 | // init defaults |
| 178 | Default(); |
| 179 | } |
| 180 | |
| 181 | void C4AulScript::Default() |
| 182 | { |
| 183 | // not compiled |
| 184 | State = ASS_NONE; |
| 185 | Script.Clear(); |
| 186 | Code = CPos = nullptr; |
| 187 | CodeSize = CodeBufSize = 0; |
| 188 | IncludesResolved = false; |
| 189 | |
| 190 | // defaults |
| 191 | idDef = C4ID_None; |
| 192 | Strict = C4AulScriptStrict::NONSTRICT; |
| 193 | Preparsing = Resolving = false; |
| 194 | Temporary = false; |
| 195 | LocalNamed.Reset(); |
| 196 | |
| 197 | // prepare lists |
| 198 | Child0 = ChildL = Prev = Next = nullptr; |
| 199 | Owner = Engine = nullptr; |
| 200 | Func0 = FuncL = nullptr; |
| 201 | // prepare include list |
| 202 | Includes.clear(); |
| 203 | Appends.clear(); |
| 204 | } |
| 205 | |
| 206 | C4AulScript::~C4AulScript() |
| 207 | { |
| 208 | // clear |
| 209 | Clear(); |
| 210 | // unreg |
| 211 | Unreg(); |
| 212 | } |
| 213 | |
| 214 | void C4AulScript::Unreg() |
| 215 | { |
| 216 | // remove from list |
| 217 | if (Prev) Prev->Next = Next; else if (Owner) Owner->Child0 = Next; |
| 218 | if (Next) Next->Prev = Prev; else if (Owner) Owner->ChildL = Prev; |
| 219 | Prev = Next = Owner = nullptr; |
| 220 | } |
| 221 | |
| 222 | void C4AulScript::Clear() |
| 223 | { |
| 224 | // remove includes |
| 225 | Includes.clear(); |
| 226 | Appends.clear(); |
| 227 | // delete child scripts + funcs |
| 228 | while (Child0) |
| 229 | if (Child0->Delete()) delete Child0; else Child0->Unreg(); |
| 230 | while (Func0) delete Func0; |
| 231 | // delete script+code |
| 232 | Script.Clear(); |
| 233 | delete[] Code; Code = nullptr; |
| 234 | CodeSize = CodeBufSize = 0; |
| 235 | // reset flags |
| 236 | State = ASS_NONE; |
| 237 | } |
| 238 | |
| 239 | void C4AulScript::Reg2List(C4AulScriptEngine *pEngine, C4AulScript *pOwner) |
| 240 | { |
| 241 | // already regged? (def reloaded) |
| 242 | if (Owner) return; |
| 243 | // reg to list |
| 244 | Engine = pEngine; |
| 245 | if (Owner = pOwner) |
| 246 | { |
| 247 | if (Prev = Owner->ChildL) |
| 248 | Prev->Next = this; |
| 249 | else |
| 250 | Owner->Child0 = this; |
| 251 | Owner->ChildL = this; |
| 252 | } |
| 253 | else |
| 254 | Prev = nullptr; |
| 255 | Next = nullptr; |
| 256 | } |
| 257 | |
| 258 | C4AulFunc *C4AulScript::GetOverloadedFunc(C4AulFunc *ByFunc) |
| 259 | { |
| 260 | assert(ByFunc); |
| 261 | // search local list |
| 262 | C4AulFunc *f = ByFunc; |
| 263 | if (f) f = f->Prev; else f = FuncL; |
| 264 | while (f) |
| 265 | { |
| 266 | if (SEqual(szStr1: ByFunc->Name, szStr2: f->Name)) break; |
| 267 | f = f->Prev; |
| 268 | } |
| 269 | #ifndef NDEBUG |
| 270 | C4AulFunc *f2 = Engine ? Engine->GetFunc(ByFunc->Name, this, ByFunc) : nullptr; |
| 271 | assert(f == f2); |
| 272 | #endif |
| 273 | // nothing found? then search owner, if existent |
| 274 | if (!f && Owner) |
| 275 | { |
| 276 | if (f = Owner->GetFuncRecursive(pIdtf: ByFunc->Name)) |
| 277 | // just found the global link? |
| 278 | if (ByFunc && f->LinkedTo == ByFunc) |
| 279 | f = Owner->GetOverloadedFunc(ByFunc: f); |
| 280 | } |
| 281 | // return found fn |
| 282 | return f; |
| 283 | } |
| 284 | |
| 285 | C4AulFunc *C4AulScript::GetFuncRecursive(const char *pIdtf) |
| 286 | { |
| 287 | // search local list |
| 288 | C4AulFunc *f = GetFunc(pIdtf); |
| 289 | if (f) return f; |
| 290 | // nothing found? then search owner, if existent |
| 291 | else if (Owner) return Owner->GetFuncRecursive(pIdtf); |
| 292 | return nullptr; |
| 293 | } |
| 294 | |
| 295 | C4AulFunc *C4AulScript::GetFunc(const char *pIdtf) |
| 296 | { |
| 297 | return Engine ? Engine->GetFunc(Name: pIdtf, Owner: this, After: nullptr) : nullptr; |
| 298 | } |
| 299 | |
| 300 | C4AulScriptFunc *C4AulScript::GetSFuncWarn(const char *pIdtf, C4AulAccess AccNeeded, const char *WarnStr) |
| 301 | { |
| 302 | // no identifier |
| 303 | if (!pIdtf || !pIdtf[0]) return nullptr; |
| 304 | // get func? |
| 305 | C4AulScriptFunc *pFn = GetSFunc(pIdtf, AccNeeded, fFailSafe: true); |
| 306 | if (!pFn) |
| 307 | Warn(msg: std::format(fmt: "Error getting {} function '{}'" , args&: WarnStr, args&: pIdtf), pIdtf: nullptr); |
| 308 | return pFn; |
| 309 | } |
| 310 | |
| 311 | C4AulScriptFunc *C4AulScript::GetSFunc(const char *pIdtf, C4AulAccess AccNeeded, bool fFailsafe) |
| 312 | { |
| 313 | // failsafe call |
| 314 | if (*pIdtf == '~') { fFailsafe = true; pIdtf++; } |
| 315 | |
| 316 | // get function reference from table |
| 317 | C4AulScriptFunc *pFn = GetSFunc(pIdtf); |
| 318 | |
| 319 | // undefined function |
| 320 | if (!pFn) |
| 321 | { |
| 322 | // not failsafe? |
| 323 | if (!fFailsafe) |
| 324 | { |
| 325 | // show error |
| 326 | C4AulParseError err(this, "Undefined function: " , pIdtf); |
| 327 | err.show(); |
| 328 | } |
| 329 | return nullptr; |
| 330 | } |
| 331 | |
| 332 | // check access |
| 333 | if (pFn->Access < AccNeeded) |
| 334 | { |
| 335 | // no access? show error |
| 336 | C4AulParseError err(this, "insufficient access level" ); |
| 337 | err.show(); |
| 338 | // don't even break in strict execution, because the caller might be non-strict |
| 339 | } |
| 340 | |
| 341 | // return found function |
| 342 | return pFn; |
| 343 | } |
| 344 | |
| 345 | C4AulScriptFunc *C4AulScript::GetSFunc(const char *pIdtf) |
| 346 | { |
| 347 | // get func by name; return script func |
| 348 | if (!pIdtf) return nullptr; |
| 349 | if (!pIdtf[0]) return nullptr; |
| 350 | if (pIdtf[0] == '~') pIdtf++; |
| 351 | C4AulFunc *f = GetFunc(pIdtf); |
| 352 | if (!f) return nullptr; |
| 353 | return f->SFunc(); |
| 354 | |
| 355 | } |
| 356 | |
| 357 | C4AulScriptFunc *C4AulScript::GetSFunc(int iIndex, const char *szPattern, C4AulAccess AccNeeded) |
| 358 | { |
| 359 | C4AulFunc *f; |
| 360 | C4AulScriptFunc *sf; |
| 361 | // loop through script funcs |
| 362 | f = FuncL; |
| 363 | while (f) |
| 364 | { |
| 365 | if (sf = f->SFunc(); sf) |
| 366 | if (!szPattern || SEqual2(szStr1: sf->Name, szStr2: szPattern)) |
| 367 | { |
| 368 | if (!iIndex) |
| 369 | { |
| 370 | if (sf->Access < AccNeeded) |
| 371 | { |
| 372 | C4AulParseError err(this, "insufficient access level" ); |
| 373 | err.show(); |
| 374 | } |
| 375 | return sf; |
| 376 | } |
| 377 | --iIndex; |
| 378 | } |
| 379 | f = f->Prev; |
| 380 | } |
| 381 | |
| 382 | // indexed script func not found |
| 383 | return nullptr; |
| 384 | } |
| 385 | |
| 386 | C4AulAccess C4AulScript::GetAllowedAccess(C4AulFunc *func, C4AulScript *caller) |
| 387 | { |
| 388 | C4AulScriptFunc *sfunc = func->SFunc(); |
| 389 | |
| 390 | if ( |
| 391 | !sfunc || |
| 392 | sfunc->pOrgScript == caller || |
| 393 | std::find(first: sfunc->pOrgScript->Includes.begin(), last: sfunc->pOrgScript->Includes.end(), val: caller->idDef) != sfunc->pOrgScript->Includes.end() || |
| 394 | std::find(first: caller->Includes.begin(), last: caller->Includes.end(), val: sfunc->pOrgScript->idDef) != caller->Includes.end() || |
| 395 | std::find(first: sfunc->pOrgScript->Appends.begin(), last: sfunc->pOrgScript->Appends.end(), val: caller->idDef) != sfunc->pOrgScript->Appends.end() || |
| 396 | std::find(first: caller->Appends.begin(), last: caller->Appends.end(), val: sfunc->pOrgScript->idDef) != caller->Appends.end() |
| 397 | ) |
| 398 | { |
| 399 | return AA_PRIVATE; |
| 400 | } |
| 401 | else if (sfunc->pOrgScript->Strict >= C4AulScriptStrict::STRICT3 && |
| 402 | caller->Strict >= C4AulScriptStrict::STRICT3) |
| 403 | { |
| 404 | return sfunc->Access; |
| 405 | } |
| 406 | |
| 407 | return AA_PRIVATE; |
| 408 | } |
| 409 | |
| 410 | void C4AulScriptFunc::CopyBody(C4AulScriptFunc &FromFunc) |
| 411 | { |
| 412 | // copy some members, that are set before linking |
| 413 | Access = FromFunc.Access; |
| 414 | Desc.Copy(Buf2: FromFunc.Desc); |
| 415 | DescText.Copy(Buf2: FromFunc.DescText); |
| 416 | DescLong.Copy(Buf2: FromFunc.DescLong); |
| 417 | Condition = FromFunc.Condition; |
| 418 | idImage = FromFunc.idImage; |
| 419 | iImagePhase = FromFunc.iImagePhase; |
| 420 | ControlMethod = FromFunc.ControlMethod; |
| 421 | Script = FromFunc.Script; |
| 422 | VarNamed = FromFunc.VarNamed; |
| 423 | ParNamed = FromFunc.ParNamed; |
| 424 | bNewFormat = FromFunc.bNewFormat; |
| 425 | bReturnRef = FromFunc.bReturnRef; |
| 426 | pOrgScript = FromFunc.pOrgScript; |
| 427 | for (int i = 0; i < C4AUL_MAX_Par; i++) |
| 428 | ParType[i] = FromFunc.ParType[i]; |
| 429 | // must reset field here |
| 430 | NextSNFunc = nullptr; |
| 431 | } |
| 432 | |
| 433 | // C4AulScriptEngine |
| 434 | |
| 435 | C4AulScriptEngine::C4AulScriptEngine() : |
| 436 | warnCnt(0), errCnt(0), nonStrictCnt(0), lineCnt(0) |
| 437 | { |
| 438 | // /me r b engine |
| 439 | Engine = this; |
| 440 | ScriptName = C4CFN_System; |
| 441 | Strict = C4AulScriptStrict::MAXSTRICT; |
| 442 | |
| 443 | Global.Reset(); |
| 444 | |
| 445 | GlobalNamedNames.Reset(); |
| 446 | GlobalNamed.Reset(); |
| 447 | GlobalNamed.SetNameList(&GlobalNamedNames); |
| 448 | GlobalConstNames.Reset(); |
| 449 | GlobalConsts.Reset(); |
| 450 | GlobalConsts.SetNameList(&GlobalConstNames); |
| 451 | } |
| 452 | |
| 453 | C4AulScriptEngine::~C4AulScriptEngine() { Clear(); } |
| 454 | |
| 455 | void C4AulScriptEngine::Clear() |
| 456 | { |
| 457 | // clear inherited |
| 458 | C4AulScript::Clear(); |
| 459 | // clear own stuff |
| 460 | // reset values |
| 461 | warnCnt = errCnt = nonStrictCnt = lineCnt = 0; |
| 462 | // resetting name lists will reset all data lists, too |
| 463 | // except not... |
| 464 | Global.Reset(); |
| 465 | GlobalNamedNames.Reset(); |
| 466 | GlobalConstNames.Reset(); |
| 467 | GlobalConsts.Reset(); |
| 468 | GlobalConsts.SetNameList(&GlobalConstNames); |
| 469 | GlobalNamed.Reset(); |
| 470 | GlobalNamed.SetNameList(&GlobalNamedNames); |
| 471 | } |
| 472 | |
| 473 | void C4AulScriptEngine::UnLink() |
| 474 | { |
| 475 | // unlink scripts |
| 476 | C4AulScript::UnLink(); |
| 477 | // clear string table ("hold" strings only) |
| 478 | Strings.Clear(); |
| 479 | // Do not clear global variables and constants, because they are registered by the |
| 480 | // preparser. Note that keeping those fields means that you cannot delete a global |
| 481 | // variable or constant at runtime by removing it from the script. |
| 482 | } |
| 483 | |
| 484 | void C4AulScriptEngine::RegisterGlobalConstant(const char *szName, const C4Value &rValue) |
| 485 | { |
| 486 | // Register name and set value. |
| 487 | // AddName returns the index of existing element if the name is assigned already. |
| 488 | // That is OK, since it will only change the value of the global ("overload" it). |
| 489 | // A warning would be nice here. However, this warning would show up whenever a script |
| 490 | // containing globals constants is recompiled. |
| 491 | GlobalConsts[GlobalConstNames.AddName(pnName: szName)] = rValue; |
| 492 | } |
| 493 | |
| 494 | bool C4AulScriptEngine::GetGlobalConstant(const char *szName, C4Value *pTargetValue) |
| 495 | { |
| 496 | // get index of global by name |
| 497 | int32_t iConstIndex = GlobalConstNames.GetItemNr(strName: szName); |
| 498 | // not found? |
| 499 | if (iConstIndex < 0) return false; |
| 500 | // if it's found, assign the value if desired |
| 501 | if (pTargetValue) *pTargetValue = GlobalConsts[iConstIndex]; |
| 502 | // constant exists |
| 503 | return true; |
| 504 | } |
| 505 | |
| 506 | bool C4AulScriptEngine::DenumerateVariablePointers() |
| 507 | { |
| 508 | Global.DenumeratePointers(); |
| 509 | GlobalNamed.DenumeratePointers(); |
| 510 | // runtime data only: don't denumerate consts |
| 511 | return true; |
| 512 | } |
| 513 | |
| 514 | void C4AulScriptEngine::CompileFunc(StdCompiler *pComp) |
| 515 | { |
| 516 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Global, szName: "Globals" , rDefault: C4ValueList())); |
| 517 | C4ValueMapData GlobalNamedDefault; |
| 518 | GlobalNamedDefault.SetNameList(&GlobalNamedNames); |
| 519 | pComp->Value(rStruct: mkNamingAdapt(rValue&: GlobalNamed, szName: "GlobalNamed" , rDefault: GlobalNamedDefault)); |
| 520 | } |
| 521 | |
| 522 | // C4AulFuncMap |
| 523 | |
| 524 | static const size_t CapacityInc = 1024; |
| 525 | |
| 526 | C4AulFuncMap::C4AulFuncMap() : Capacity(CapacityInc), FuncCnt(0), Funcs(new C4AulFunc *[CapacityInc]) |
| 527 | { |
| 528 | memset(s: Funcs, c: 0, n: sizeof(C4AulFunc *) * Capacity); |
| 529 | } |
| 530 | |
| 531 | C4AulFuncMap::~C4AulFuncMap() |
| 532 | { |
| 533 | delete[] Funcs; |
| 534 | } |
| 535 | |
| 536 | unsigned int C4AulFuncMap::Hash(const char *name) |
| 537 | { |
| 538 | // Fowler/Noll/Vo hash |
| 539 | unsigned int h = 2166136261u; |
| 540 | while (*name) |
| 541 | h = (h ^ * (name++)) * 16777619; |
| 542 | return h; |
| 543 | } |
| 544 | |
| 545 | C4AulFunc *C4AulFuncMap::GetFirstFunc(const char *Name) |
| 546 | { |
| 547 | if (!Name) return nullptr; |
| 548 | C4AulFunc *Func = Funcs[Hash(name: Name) % Capacity]; |
| 549 | while (Func && !SEqual(szStr1: Name, szStr2: Func->Name)) |
| 550 | Func = Func->MapNext; |
| 551 | return Func; |
| 552 | } |
| 553 | |
| 554 | C4AulFunc *C4AulFuncMap::GetNextSNFunc(const C4AulFunc *After) |
| 555 | { |
| 556 | C4AulFunc *Func = After->MapNext; |
| 557 | while (Func && !SEqual(szStr1: After->Name, szStr2: Func->Name)) |
| 558 | Func = Func->MapNext; |
| 559 | return Func; |
| 560 | } |
| 561 | |
| 562 | C4AulFunc *C4AulFuncMap::GetFunc(const char *Name, const C4AulScript *Owner, const C4AulFunc *After) |
| 563 | { |
| 564 | if (!Name) return nullptr; |
| 565 | C4AulFunc *Func = Funcs[Hash(name: Name) % Capacity]; |
| 566 | if (After) |
| 567 | { |
| 568 | while (Func && Func != After) |
| 569 | Func = Func->MapNext; |
| 570 | if (Func) |
| 571 | Func = Func->MapNext; |
| 572 | } |
| 573 | while (Func && (Func->Owner != Owner || !SEqual(szStr1: Name, szStr2: Func->Name))) |
| 574 | Func = Func->MapNext; |
| 575 | return Func; |
| 576 | } |
| 577 | |
| 578 | void C4AulFuncMap::Add(C4AulFunc *func, bool bAtStart) |
| 579 | { |
| 580 | if (++FuncCnt > Capacity) |
| 581 | { |
| 582 | int NCapacity = Capacity + CapacityInc; |
| 583 | C4AulFunc **NFuncs = new C4AulFunc *[NCapacity]; |
| 584 | memset(s: NFuncs, c: 0, n: sizeof(C4AulFunc *) * NCapacity); |
| 585 | for (int i = 0; i < Capacity; ++i) |
| 586 | { |
| 587 | while (Funcs[i]) |
| 588 | { |
| 589 | // Get a pointer to the bucket |
| 590 | C4AulFunc **pNFunc = &(NFuncs[Hash(name: Funcs[i]->Name) % NCapacity]); |
| 591 | // get a pointer to the end of the linked list |
| 592 | while (*pNFunc) pNFunc = &((*pNFunc)->MapNext); |
| 593 | // Move the func over |
| 594 | *pNFunc = Funcs[i]; |
| 595 | // proceed with the next list member |
| 596 | Funcs[i] = Funcs[i]->MapNext; |
| 597 | // Terminate the linked list |
| 598 | (*pNFunc)->MapNext = nullptr; |
| 599 | } |
| 600 | } |
| 601 | Capacity = NCapacity; |
| 602 | delete[] Funcs; |
| 603 | Funcs = NFuncs; |
| 604 | } |
| 605 | // Get a pointer to the bucket |
| 606 | C4AulFunc **pFunc = &(Funcs[Hash(name: func->Name) % Capacity]); |
| 607 | if (bAtStart) |
| 608 | { |
| 609 | // move the current first to the second position |
| 610 | func->MapNext = *pFunc; |
| 611 | } |
| 612 | else |
| 613 | { |
| 614 | // get a pointer to the end of the linked list |
| 615 | while (*pFunc) |
| 616 | { |
| 617 | pFunc = &((*pFunc)->MapNext); |
| 618 | } |
| 619 | } |
| 620 | // Add the func |
| 621 | *pFunc = func; |
| 622 | } |
| 623 | |
| 624 | void C4AulFuncMap::Remove(C4AulFunc *func) |
| 625 | { |
| 626 | C4AulFunc **pFunc = &Funcs[Hash(name: func->Name) % Capacity]; |
| 627 | while (*pFunc != func) |
| 628 | { |
| 629 | pFunc = &((*pFunc)->MapNext); |
| 630 | assert(*pFunc); // crash on remove of a not contained func |
| 631 | } |
| 632 | *pFunc = (*pFunc)->MapNext; |
| 633 | --FuncCnt; |
| 634 | } |
| 635 | |