2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #if !defined( INCLUDED_UNIQUENAMES_H )
23 #define INCLUDED_UNIQUENAMES_H
25 #include "debugging/debugging.h"
27 #include "string/string.h"
28 #include "generic/static.h"
35 Postfix( const char* postfix ) : m_value( atoi( postfix ) ){
37 unsigned int number() const {
40 void write( char* buffer ) const {
41 sprintf( buffer, "%u", m_value );
43 Postfix& operator++(){
47 bool operator<( const Postfix& other ) const {
48 return m_value < other.m_value;
50 bool operator==( const Postfix& other ) const {
51 return m_value == other.m_value;
53 bool operator!=( const Postfix& other ) const {
54 return !operator==( other );
61 std::pair<unsigned int, unsigned int> m_value;
63 Postfix( unsigned int number, unsigned int leading_zeros )
64 : m_value( leading_zeros, number ){
66 Postfix( const char* postfix )
67 : m_value( number_count_leading_zeros( postfix ), atoi( postfix ) ){
69 unsigned int number() const {
70 return m_value.second;
72 unsigned int leading_zeros() const {
75 void write( char* buffer ){
76 for ( unsigned int count = 0; count < m_value.first; ++count, ++buffer )
78 sprintf( buffer, "%u", m_value.second );
80 Postfix& operator++(){
82 if ( m_value.first != 0 && m_value.second % 10 == 0 ) {
87 bool operator<( const Postfix& other ) const {
88 return m_value < other.m_value;
90 bool operator==( const Postfix& other ) const {
91 return m_value == other.m_value;
93 bool operator!=( const Postfix& other ) const {
94 return !operator==( other );
100 typedef std::pair<CopiedString, Postfix> name_t;
102 inline void name_write( char* buffer, name_t name ){
103 strcpy( buffer, name.first.c_str() );
104 name.second.write( buffer + strlen( name.first.c_str() ) );
107 inline name_t name_read( const char* name ){
108 const char* end = name + strlen( name );
111 /* HACK: Apple shipped a clang built for macOS with an optimization enabled
112 that is not available on macOS. This compiler error may then be faced:
114 ld: Undefined symbols:
115 _memrchr, referenced from:
116 name_read(char const*) in map.cpp.o
118 This is a compiler error:
120 > On Mac OSX (macOS version 12.4, sdk version 12.1) llvm can replace call
121 > to strrchr() with call to memrchr() when string length is known at
122 > compile time. This results in link error, because memrchr is not present
124 > -- https://github.com/llvm/llvm-project/issues/62254
126 We workaround this by making the string length not known at build time
127 on macOS to avoid triggering the unavailable compiler optimization. */
129 const char* volatile numbers = "1234567890";
131 const char* numbers = "1234567890";
134 for ( const char* p = end; end != name; --p )
136 if ( strrchr( numbers, *p ) == NULL ) {
142 return name_t( CopiedString( StringRange( name, end ) ), Postfix( end ) );
149 typedef std::map<Postfix, unsigned int> postfixes_t;
150 postfixes_t m_postfixes;
153 Postfix find_first_empty() const {
154 Postfix postfix( "1" );
155 for ( postfixes_t::const_iterator i = m_postfixes.find( postfix ); i != m_postfixes.end(); ++i, ++postfix )
157 if ( ( *i ).first != postfix ) {
165 Postfix make_unique( Postfix postfix ) const {
166 postfixes_t::const_iterator i = m_postfixes.find( postfix );
167 if ( i == m_postfixes.end() ) {
172 return find_first_empty();
176 void insert( Postfix postfix ){
177 postfixes_t::iterator i = m_postfixes.find( postfix );
178 if ( i == m_postfixes.end() ) {
179 m_postfixes.insert( postfixes_t::value_type( postfix, 1 ) );
187 void erase( Postfix postfix ){
188 postfixes_t::iterator i = m_postfixes.find( postfix );
189 if ( i == m_postfixes.end() ) {
194 if ( --( *i ).second == 0 ) {
195 m_postfixes.erase( i );
201 return m_postfixes.empty();
208 typedef std::map<CopiedString, PostFixes> names_t;
211 name_t make_unique( const name_t& name ) const {
214 name_write( buf, name );
216 globalErrorStream() << "find unique name for " << buf << "\n";
217 globalErrorStream() << "> currently registered names:\n";
219 for ( names_t::const_iterator i = m_names.begin(); i != m_names.end(); ++i )
222 globalErrorStream() << ">> " << i->first.c_str() << ": ";
224 for ( PostFixes::postfixes_t::const_iterator j = i->second.m_postfixes.begin(); j != i->second.m_postfixes.end(); ++j )
226 j->first.write( buf );
228 globalErrorStream() << " '" << buf << "'";
232 globalErrorStream() << "\n";
235 names_t::const_iterator i = m_names.find( name.first );
236 if ( i == m_names.end() ) {
241 r = name_t( name.first, ( *i ).second.make_unique( name.second ) );
243 name_write( buf, r );
245 globalErrorStream() << "> unique name is " << buf << "\n";
250 void insert( const name_t& name ){
251 m_names[name.first].insert( name.second );
254 void erase( const name_t& name ){
255 names_t::iterator i = m_names.find( name.first );
256 if ( i == m_names.end() ) {
257 ASSERT_MESSAGE( true, "erase: name not found" );
261 ( *i ).second.erase( name.second );
262 if ( ( *i ).second.empty() ) {
269 return m_names.empty();
278 #define ERROR_MESSAGE( message )
282 void name_check_equal( const name_t& name, const char* string, unsigned int postfix ){
283 ASSERT_MESSAGE( strcmp( name.first.c_str(), string ) == 0
284 && name.second.number() == postfix,
287 void test_refcount(){
290 names.insert( name_t( "func_bleh_", "100" ) );
291 names.insert( name_t( "func_bleh_", "100" ) );
292 names.insert( name_t( "func_bleh_", "100" ) );
295 names.erase( name_t( "func_bleh_", "100" ) );
296 names.erase( name_t( "func_bleh_", "100" ) );
297 names.erase( name_t( "func_bleh_", "100" ) );
299 ASSERT_MESSAGE( names.empty(), "test failed!" );
302 void test_make_unique(){
306 name_t name( names.make_unique( name_t( "func_bleh_", "01" ) ) );
307 name_check_equal( name, "func_bleh_", 1 );
308 names.insert( name );
311 name_t name( names.make_unique( name_t( "func_bleh_", "04" ) ) );
312 name_check_equal( name, "func_bleh_", 4 );
313 names.insert( name );
316 name_t name( names.make_unique( name_t( "func_bleh_", "04" ) ) );
317 name_check_equal( name, "func_bleh_", 2 );
318 names.insert( name );
321 name_t name( names.make_unique( name_t( "func_bleh_", "1" ) ) );
322 name_check_equal( name, "func_bleh_", 3 );
323 names.insert( name );
326 name_t name( names.make_unique( name_t( "func_bleh_", "2" ) ) );
327 name_check_equal( name, "func_bleh_", 5 );
328 names.insert( name );
331 name_t name( names.make_unique( name_t( "func_bleh_", "3" ) ) );
332 name_check_equal( name, "func_bleh_", 6 );
333 names.insert( name );
336 names.erase( name_t( "func_bleh_", "1" ) );
337 names.erase( name_t( "func_bleh_", "2" ) );
338 names.erase( name_t( "func_bleh_", "3" ) );
339 names.erase( name_t( "func_bleh_", "4" ) );
340 names.erase( name_t( "func_bleh_", "5" ) );
341 names.erase( name_t( "func_bleh_", "6" ) );
343 ASSERT_MESSAGE( names.empty(), "test failed!" );
352 const TestUniqueName g_testuniquename;