1
// SPDX-License-Identifier: LGPL-2.1-or-later
3
/****************************************************************************
4
* Copyright (c) 2022 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
5
* Copyright (c) 2023 FreeCAD Project Association *
7
* This file is part of FreeCAD. *
9
* FreeCAD is free software: you can redistribute it and/or modify it *
10
* under the terms of the GNU Lesser General Public License as *
11
* published by the Free Software Foundation, either version 2.1 of the *
12
* License, or (at your option) any later version. *
14
* FreeCAD is distributed in the hope that it will be useful, but *
15
* WITHOUT ANY WARRANTY; without even the implied warranty of *
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
17
* Lesser General Public License for more details. *
19
* You should have received a copy of the GNU Lesser General Public *
20
* License along with FreeCAD. If not, see *
21
* <https://www.gnu.org/licenses/>. *
23
***************************************************************************/
26
#include "PreCompiled.h"
30
# include <unordered_set>
33
#include "IndexedName.h"
37
/// Check whether the input character is an underscore or an ASCII letter a-Z or A-Z
38
inline bool isInvalidChar(char test)
40
return test != '_' && (test < 'a' || test > 'z' ) && (test < 'A' || test > 'Z');
43
/// Get the integer suffix of name. Returns a tuple of (suffix, suffixPosition). Calling code
44
/// should check to ensure that suffixPosition is not equal to nameLength (in which case there was no
47
/// \param name The name to check
48
/// \param nameLength The length of the string in name
49
/// \returns An integer pair of the suffix itself and the position of that suffix in name
50
std::pair<int,int> getIntegerSuffix(const char *name, int nameLength)
52
int suffixPosition {nameLength - 1};
54
for (; suffixPosition >= 0; --suffixPosition) {
55
// When we support C++20 we can use std::span<> to eliminate the clang-tidy warning
56
// NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic
57
if (!isdigit(name[suffixPosition])) {
63
if (suffixPosition < nameLength) {
64
// When we support C++20 we can use std::span<> to eliminate the clang-tidy warning
65
// NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic
66
suffix = std::atoi(name + suffixPosition);
68
return std::make_pair(suffix, suffixPosition);
74
const std::vector<const char*>& allowedNames,
77
// Storage for names that we weren't given external storage for
78
static std::unordered_set<ByteArray, ByteArrayHasher> NameSet;
81
length = static_cast<int>(std::strlen(name));
83
// Name typically ends with an integer: find that integer
84
auto [suffix, suffixPosition] = getIntegerSuffix(name, length);
85
if (suffixPosition < length) {
89
// Make sure that every character is either an ASCII letter (upper or lowercase), or an
90
// underscore. If any other character appears, reject the entire string.
91
// When we support C++20 we can use std::span<> to eliminate the clang-tidy warning
92
// NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic
93
if (std::any_of(name, name+suffixPosition, isInvalidChar)) {
98
// If a list of allowedNames was provided, see if our set name matches one of those allowedNames: if it
99
// does, reference that memory location and return.
100
for (const auto *typeName : allowedNames) {
101
if (std::strncmp(name, typeName, suffixPosition) == 0) {
102
this->type = typeName;
107
// If the type was NOT in the list of allowedNames, but the caller has set the allowOthers flag to
108
// true, then add the new type to the static NameSet (if it is not already there).
110
auto res = NameSet.insert(ByteArray(QByteArray::fromRawData(name, suffixPosition)));
111
if (res.second /*The insert succeeded (the type was new)*/) {
112
// Make sure that the data in the set is a unique (unshared) copy of the text
113
res.first->ensureUnshared();
115
this->type = res.first->bytes.constData();
118
// The passed-in type is not in the allowed list, and allowOthers was not true, so don't