FreeCAD
1// SPDX-License-Identifier: LGPL-2.1-or-later
2
3/****************************************************************************
4* Copyright (c) 2022 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
5* Copyright (c) 2023 FreeCAD Project Association *
6* *
7* This file is part of FreeCAD. *
8* *
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. *
13* *
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. *
18* *
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/>. *
22* *
23***************************************************************************/
24
25// NOLINTNEXTLINE
26#include "PreCompiled.h"27
28#ifndef _PreComp_29# include <cstdlib>30# include <unordered_set>31#endif32
33#include "IndexedName.h"34
35using namespace Data;36
37/// Check whether the input character is an underscore or an ASCII letter a-Z or A-Z
38inline bool isInvalidChar(char test)39{
40return test != '_' && (test < 'a' || test > 'z' ) && (test < 'A' || test > 'Z');41}
42
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
45/// suffix).
46///
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
50std::pair<int,int> getIntegerSuffix(const char *name, int nameLength)51{
52int suffixPosition {nameLength - 1};53
54for (; suffixPosition >= 0; --suffixPosition) {55// When we support C++20 we can use std::span<> to eliminate the clang-tidy warning56// NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic57if (!isdigit(name[suffixPosition])) {58break;59}60}61++suffixPosition;62int suffix {0};63if (suffixPosition < nameLength) {64// When we support C++20 we can use std::span<> to eliminate the clang-tidy warning65// NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic66suffix = std::atoi(name + suffixPosition);67}68return std::make_pair(suffix, suffixPosition);69}
70
71void IndexedName::set(72const char* name,73int length,74const std::vector<const char*>& allowedNames,75bool allowOthers)76{
77// Storage for names that we weren't given external storage for78static std::unordered_set<ByteArray, ByteArrayHasher> NameSet;79
80if (length < 0) {81length = static_cast<int>(std::strlen(name));82}83// Name typically ends with an integer: find that integer84auto [suffix, suffixPosition] = getIntegerSuffix(name, length);85if (suffixPosition < length) {86this->index = suffix;87}88
89// Make sure that every character is either an ASCII letter (upper or lowercase), or an90// 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 warning92// NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic93if (std::any_of(name, name+suffixPosition, isInvalidChar)) {94this->type = "";95return;96}97
98// If a list of allowedNames was provided, see if our set name matches one of those allowedNames: if it99// does, reference that memory location and return.100for (const auto *typeName : allowedNames) {101if (std::strncmp(name, typeName, suffixPosition) == 0) {102this->type = typeName;103return;104}105}106
107// If the type was NOT in the list of allowedNames, but the caller has set the allowOthers flag to108// true, then add the new type to the static NameSet (if it is not already there).109if (allowOthers) {110auto res = NameSet.insert(ByteArray(QByteArray::fromRawData(name, suffixPosition)));111if (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 text113res.first->ensureUnshared();114}115this->type = res.first->bytes.constData();116}117else {118// The passed-in type is not in the allowed list, and allowOthers was not true, so don't119// store the type120this->type = "";121}122}
123