yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
39c9e25f6
master
1// slang-command-options.cpp 2 3#include "slang-command-options.h" 4 5#include "slang-byte-encode-util.h" 6#include "slang-char-util.h" 7#include "slang-string-util.h" 8 9namespace Slang 10{ 11 12UnownedStringSlice CommandOptions ::getFirstNameForOption (Index optionIndex ) 13{ 14const auto & opt = m_options [optionIndex ]; 15return StringUtil ::getAtInSplit (opt .names ,',' ,0 ); 16} 17 18UnownedStringSlice CommandOptions ::getFirstNameForCategory (Index categoryIndex ) 19{ 20const auto & cat = m_categories [categoryIndex ]; 21return cat .name ; 22} 23 24CommandOptions ::NameKey CommandOptions ::getNameKeyForOption (Index optionIndex ) 25{ 26const auto & opt = m_options [optionIndex ]; 27const auto & cat = m_categories [opt .categoryIndex ]; 28NameKey key ; 29key .nameIndex = m_pool .findIndex (getFirstNameForOption (optionIndex )); 30key .kind = 31 (cat .kind == CategoryKind ::Option ) ?LookupKind ::Option :makeLookupKind (opt .categoryIndex ); 32return key ; 33} 34 35CommandOptions ::NameKey CommandOptions ::getNameKeyForCategory (Index categoryIndex ) 36{ 37NameKey key ; 38key .nameIndex = m_pool .findIndex (getFirstNameForCategory (categoryIndex )); 39key .kind = LookupKind ::Category ; 40return key ; 41} 42 43SlangResult CommandOptions ::_addName ( 44LookupKind kind , 45const UnownedStringSlice & name , 46Index targetIndex ) 47{ 48NameKey nameKey ; 49nameKey .kind = kind ; 50nameKey .nameIndex = (Index )m_pool .add (name ); 51 52if (m_nameMap .tryGetValueOrAdd (nameKey ,targetIndex )) 53 { 54SLANG_ASSERT (!"Option is already added!" ); 55return SLANG_FAIL ; 56 } 57return SLANG_OK ; 58} 59 60SlangResult CommandOptions ::_addOptionName ( 61const UnownedStringSlice & name , 62Flags flags , 63Index targetIndex ) 64{ 65SLANG_RETURN_ON_FAIL (_addName (LookupKind ::Option ,name ,targetIndex )); 66 67// Add to prefix flags 68if (flags & (Flag ::CanPrefix |Flag ::IsPrefix )) 69 { 70const auto length = name .getLength (); 71SLANG_ASSERT (length < 32 ); 72m_prefixSizes |=uint32_t (1 ) <<length ; 73 } 74 75return SLANG_OK ; 76} 77 78SlangResult CommandOptions ::_addValueName ( 79const UnownedStringSlice & name , 80Index categoryIndex , 81Index optionIndex ) 82{ 83return _addName (LookupKind (categoryIndex ),name ,optionIndex ); 84} 85 86SlangResult CommandOptions ::_addUserValue (LookupKind kind ,UserValue userValue ,Index targetIndex ) 87{ 88// If it's invalid we don't need to add it 89if (userValue == kInvalidUserValue ) 90 { 91return SLANG_OK ; 92 } 93 94UserValueKey userValueKey ; 95userValueKey .kind = kind ; 96userValueKey .userValue = userValue ; 97 98if (m_userValueMap .tryGetValueOrAdd (userValueKey ,targetIndex )) 99 { 100SLANG_ASSERT (!"UserValue is already used for this kind!" ); 101return SLANG_FAIL ; 102 } 103return SLANG_OK ; 104} 105 106UnownedStringSlice CommandOptions ::_addString (const char * text ) 107{ 108if (text == nullptr ) 109 { 110return UnownedStringSlice (); 111 } 112return _addString (UnownedStringSlice (text )); 113} 114 115UnownedStringSlice CommandOptions ::_addString (const UnownedStringSlice & slice ) 116{ 117const auto length = slice .getLength (); 118const char * dst = m_arena .allocateString (slice .begin (),length ); 119return UnownedStringSlice (dst ,length ); 120} 121 122Index CommandOptions ::_addOption (const UnownedStringSlice & name ,const Option & inOption ) 123{ 124if (name .indexOf (',' )< 0 ) 125 { 126return _addOption (& name ,1 ,inOption ); 127 } 128else 129 { 130List < UnownedStringSlice > names ; 131StringUtil ::split (name ,',' ,names ); 132return _addOption (names .getBuffer (),names .getCount (),inOption ); 133 } 134} 135 136Index CommandOptions ::_addOption ( 137const UnownedStringSlice * names , 138Count namesCount , 139const Option & inOption ) 140{ 141SLANG_ASSERT (namesCount > 0 ); 142SLANG_ASSERT (inOption .categoryIndex >=0 ); 143 144if (namesCount <=0 || inOption .categoryIndex < 0 ) 145 { 146return -1 ; 147 } 148 149auto & cat = m_categories [inOption .categoryIndex ]; 150 151// If there are already options associated with this category, we have to be in the run of the 152// last ones added 153if (cat .optionStartIndex != cat .optionEndIndex ) 154 { 155// If we aren't at the end then this is an error 156if (cat .optionEndIndex != m_options .getCount ()) 157 { 158return -1 ; 159 } 160 } 161else 162 { 163// Move to the end of the option list 164cat .optionStartIndex = m_options .getCount (); 165cat .optionEndIndex = cat .optionStartIndex ; 166 } 167 168Option option (inOption ); 169 170const Index optionIndex = m_options .getCount (); 171 172if (cat .kind == CategoryKind ::Option ) 173 { 174for (Index i = 0 ;i < namesCount ;++ i ) 175 { 176if (SLANG_FAILED (_addOptionName (names [i ],inOption .flags ,optionIndex ))) 177 { 178return -1 ; 179 } 180 } 181if (SLANG_FAILED (_addUserValue (LookupKind ::Option ,inOption .userValue ,optionIndex ))) 182 { 183return -1 ; 184 } 185 } 186else 187 { 188for (Index i = 0 ;i < namesCount ;++ i ) 189 { 190_addValueName (names [i ],inOption .categoryIndex ,optionIndex ); 191 } 192if (SLANG_FAILED ( 193_addUserValue (LookupKind (inOption .categoryIndex ),inOption .userValue ,optionIndex ))) 194 { 195return -1 ; 196 } 197 } 198 199if (namesCount == 1 ) 200 { 201// We already have storage on the slice 202option .names = m_pool .addAndGetSlice (names [0 ]); 203 } 204else 205 { 206// Put all of the names in the list 207StringBuilder buf ; 208StringUtil ::join (names ,namesCount ,',' ,buf ); 209// Allocate storage no in the pool 210option .names = _addString (buf .getUnownedSlice ()); 211 } 212 213m_options .add (option ); 214 215// Set the end index 216cat .optionEndIndex = optionIndex + 1 ; 217 218return optionIndex ; 219} 220 221static void _handlePostFix (UnownedStringSlice & ioSlice ,CommandOptions ::Flags & ioFlags ) 222{ 223if (ioSlice .endsWith (toSlice ("..." ))) 224 { 225if (ioSlice .endsWith (toSlice ("?..." ))) 226 { 227ioFlags |=CommandOptions ::Flag ::CanPrefix ; 228ioSlice = ioSlice .head (ioSlice .getLength ()- 4 ); 229 } 230else 231 { 232ioFlags |=CommandOptions ::Flag ::IsPrefix ; 233ioSlice = ioSlice .head (ioSlice .getLength ()- 3 ); 234 } 235 } 236} 237 238void CommandOptions ::add ( 239const char * inName , 240const char * usage , 241const char * description , 242UserValue userValue ) 243{ 244UnownedStringSlice nameSlice (inName ); 245 246Option option ; 247option .categoryIndex = m_currentCategoryIndex ; 248option .usage = _addString (usage ); 249option .description = _addString (UnownedStringSlice (description )); 250option .userValue = userValue ; 251option .flags = 0 ; 252 253if (nameSlice .indexOf (',' ) >=0 ) 254 { 255List < UnownedStringSlice > names ; 256StringUtil ::split (nameSlice ,',' ,names ); 257 258for (auto & name :names ) 259 { 260_handlePostFix (name ,option .flags ); 261 } 262 263_addOption (names .getBuffer (),names .getCount (),option ); 264 } 265else 266 { 267_handlePostFix (nameSlice ,option .flags ); 268 269_addOption (& nameSlice ,1 ,option ); 270 } 271} 272 273void CommandOptions ::add ( 274const UnownedStringSlice * names , 275Count namesCount , 276const char * usage , 277const char * description , 278UserValue userValue , 279Flags flags ) 280{ 281Option option ; 282option .categoryIndex = m_currentCategoryIndex ; 283option .usage = _addString (usage ); 284option .description = _addString (UnownedStringSlice (description )); 285option .flags = flags ; 286option .userValue = userValue ; 287 288_addOption (names ,namesCount ,option ); 289} 290 291Index CommandOptions ::_addValue (const UnownedStringSlice & name ,const Option & inOption ) 292{ 293SLANG_ASSERT (m_currentCategoryIndex >=0 ); 294SLANG_ASSERT (m_categories [m_currentCategoryIndex ].kind == CategoryKind ::Value ); 295 296return _addOption (name ,inOption ); 297} 298 299void CommandOptions ::addValues (const ValuePair * pairs ,Count pairsCount ) 300{ 301for (auto & pair :makeConstArrayView (pairs ,pairsCount )) 302 { 303addValue (pair .name ,pair .description ); 304 } 305} 306 307void CommandOptions ::addValues (const ConstArrayView < NameValue >& values ) 308{ 309for (const auto & value :values ) 310 { 311addValue (value .name ,UserValue (value .value )); 312 } 313} 314 315void CommandOptions ::addValues (const ConstArrayView < NamesValue >& values ) 316{ 317for (const auto & value :values ) 318 { 319addValue (value .names ,UserValue (value .value )); 320 } 321} 322 323void CommandOptions ::addValues (const ConstArrayView < NamesDescriptionValue >& values ) 324{ 325for (const auto & value :values ) 326 { 327addValue (value .names ,value .description ,UserValue (value .value )); 328 } 329} 330 331void CommandOptions ::addValuesWithAliases (const ConstArrayView < NameValue >& inValues ) 332{ 333List < NameValue > values ; 334values .addRange (inValues .getBuffer (),inValues .getCount ()); 335 336// Use stable_sort to preserve the original order for names with the same value. 337 std::stable_sort ( 338values .begin (), 339values .end (), 340 [](const NameValue & a ,const NameValue & b )-> bool {return a .value < b .value ; }); 341 342List < UnownedStringSlice > names ; 343 344const Count count = values .getCount (); 345Index i = 0 ; 346while (i < count ) 347 { 348names .clear (); 349 350const auto value = values [i ].value ; 351names .add (UnownedStringSlice (values [i ++ ].name )); 352 353// For all names with the same value, preserve their original order (primary first) 354for (;i < count && values [i ].value == value ;++ i ) 355 { 356names .add (UnownedStringSlice (values [i ].name )); 357 } 358 359addValue (names .getBuffer (),names .getCount (),UserValue (value )); 360 } 361} 362 363void CommandOptions ::addValue (const UnownedStringSlice & name ,UserValue userValue ) 364{ 365Option option ; 366option .categoryIndex = m_currentCategoryIndex ; 367option .userValue = userValue ; 368_addValue (name ,option ); 369} 370 371void CommandOptions ::addValue ( 372const UnownedStringSlice & name , 373const UnownedStringSlice & description , 374UserValue userValue ) 375{ 376Option option ; 377option .categoryIndex = m_currentCategoryIndex ; 378option .description = _addString (description ); 379option .userValue = userValue ; 380_addValue (name ,option ); 381} 382 383void CommandOptions ::addValue ( 384const UnownedStringSlice * names , 385Count namesCount , 386UserValue userValue ) 387{ 388Option option ; 389option .categoryIndex = m_currentCategoryIndex ; 390option .userValue = userValue ; 391 392SLANG_ASSERT (m_currentCategoryIndex >=0 ); 393SLANG_ASSERT (m_categories [m_currentCategoryIndex ].kind == CategoryKind ::Value ); 394 395_addOption (names ,namesCount ,option ); 396} 397 398void CommandOptions ::addValue (const char * inName ,const char * description ,UserValue userValue ) 399{ 400const UnownedStringSlice name (inName ); 401 402if (description ) 403 { 404addValue (name ,UnownedStringSlice (description ),userValue ); 405 } 406else 407 { 408addValue (name ,userValue ); 409 } 410} 411 412void CommandOptions ::addValue (const char * name ,UserValue userValue ) 413{ 414addValue (UnownedStringSlice (name ),userValue ); 415} 416 417Index CommandOptions ::addCategory ( 418CategoryKind kind , 419const char * name , 420const char * description , 421UserValue userValue ) 422{ 423const UnownedStringSlice nameSlice (name ); 424 425const auto categoryIndex = m_categories .getCount (); 426 427if (SLANG_FAILED (_addName (LookupKind ::Category ,nameSlice ,categoryIndex ))) 428 { 429return -1 ; 430 } 431 432if (userValue != kInvalidUserValue ) 433 { 434_addUserValue (LookupKind ::Category ,userValue ,categoryIndex ); 435 } 436 437Category cat ; 438cat .kind = kind ; 439cat .name = _addString (nameSlice ); 440cat .description = _addString (description ); 441cat .userValue = userValue ; 442 443m_currentCategoryIndex = categoryIndex ; 444 445m_categories .add (cat ); 446 447return categoryIndex ; 448} 449 450void CommandOptions ::setCategory (const char * name ) 451{ 452const UnownedStringSlice nameSlice (name ); 453 454for (Index i = 0 ;i < m_categories .getCount ();++ i ) 455 { 456auto & cat = m_categories [i ]; 457if (cat .name == nameSlice ) 458 { 459m_currentCategoryIndex = i ; 460return ; 461 } 462 } 463 464SLANG_ASSERT (!"Category not found" ); 465 466m_currentCategoryIndex = -1 ; 467} 468 469Index CommandOptions ::findTargetIndexByName ( 470LookupKind kind , 471const UnownedStringSlice & name , 472NameKey * outNameKey )const 473{ 474// Look up directly 475 { 476auto index = _findTargetIndexByName (kind ,name ,outNameKey ); 477if (index >=0 ) 478 { 479return index ; 480 } 481 } 482 483// Special case options, which can have prefix styles 484if (kind == LookupKind ::Option ) 485 { 486auto prefixSizes = m_prefixSizes ; 487 488while (prefixSizes ) 489 { 490auto prefixSize = ByteEncodeUtil ::calcMsb32 (prefixSizes ); 491 492if (prefixSize < name .getLength ()) 493 { 494// Look it up 495const auto index = _findTargetIndexByName (kind ,name .head (prefixSize ),outNameKey ); 496if (index >=0 ) 497 { 498auto & option = m_options [index ]; 499 500// If the option accepts prefixes, we return the index 501if (option .flags & (Flag ::CanPrefix |Flag ::IsPrefix )) 502 { 503return index ; 504 } 505 } 506 } 507 508// Remove the bit 509prefixSizes &= ~(uint32_t (1 ) <<prefixSize ); 510 } 511 } 512 513// Was not found 514return -1 ; 515} 516 517Index CommandOptions ::_findTargetIndexByName ( 518LookupKind kind , 519const UnownedStringSlice & name , 520NameKey * outNameKey )const 521{ 522const auto nameIndex = m_pool .findIndex (name ); 523// If the name isn't in the pool then there isn't a category with this name 524if (nameIndex < 0 ) 525 { 526return -1 ; 527 } 528 529NameKey key ; 530key .kind = kind ; 531key .nameIndex = nameIndex ; 532 533if (auto ptr = m_nameMap .tryGetValue (key )) 534 { 535if (outNameKey ) 536 { 537* outNameKey = key ; 538 } 539return * ptr ; 540 } 541 542return -1 ; 543} 544 545Index CommandOptions ::findTargetIndexByUserValue (LookupKind kind ,UserValue userValue )const 546{ 547UserValueKey key ; 548key .kind = kind ; 549key .userValue = userValue ; 550 551if (auto ptr = m_userValueMap .tryGetValue (key )) 552 { 553return * ptr ; 554 } 555 556return -1 ; 557} 558 559Index CommandOptions ::findCategoryByCaseInsensitiveName (const UnownedStringSlice & slice )const 560{ 561const Count count = m_categories .getCount (); 562for (Index i = 0 ;i < count ;++ i ) 563 { 564const auto & cat = m_categories [i ]; 565 566if (cat .name .caseInsensitiveEquals (slice )) 567 { 568return i ; 569 } 570 } 571return -1 ; 572} 573 574Index CommandOptions ::findOptionByCategoryUserValue ( 575UserValue categoryUserValue , 576const UnownedStringSlice & name )const 577{ 578Index categoryIndex = findTargetIndexByUserValue (LookupKind ::Category ,categoryUserValue ); 579if (categoryIndex < 0 ) 580 { 581return -1 ; 582 } 583 584return findValueByName (categoryIndex ,name ); 585} 586 587ConstArrayView < CommandOptions ::Option > CommandOptions ::getOptionsForCategory ( 588Index categoryIndex )const 589{ 590const auto & cat = m_categories [categoryIndex ]; 591return makeConstArrayView ( 592m_options .getBuffer ()+ cat .optionStartIndex , 593cat .optionEndIndex - cat .optionStartIndex ); 594} 595 596 597void CommandOptions ::appendCategoryOptionNames ( 598Index categoryIndex , 599List < UnownedStringSlice >& outNames )const 600{ 601for (const auto & option :getOptionsForCategory (categoryIndex )) 602 { 603StringUtil ::appendSplit (option .names ,',' ,outNames ); 604 } 605} 606 607void CommandOptions ::getCategoryOptionNames (Index categoryIndex ,List < UnownedStringSlice >& outNames ) 608const 609{ 610outNames .clear (); 611appendCategoryOptionNames (categoryIndex ,outNames ); 612} 613 614void CommandOptions ::splitUsage ( 615const UnownedStringSlice & usageSlice , 616List < UnownedStringSlice >& outSlices )const 617{ 618const auto * cur = usageSlice .begin (); 619const auto * end = usageSlice .end (); 620 621while (cur < end ) 622 { 623// Find < 624while (cur < end && * cur != '<' ) 625cur ++ ; 626 627// If we found it look for the end 628if (cur < end && * cur == '<' ) 629 { 630++ cur ; 631auto start = cur ; 632while (cur < end && (CharUtil ::isAlphaOrDigit (* cur )|| * cur == '-' || * cur == '_' )&& 633* cur != '>' ) 634 { 635cur ++ ; 636 } 637 638// If we hit closing > we want to lookup 639if (cur < end && * cur == '>' ) 640 { 641const UnownedStringSlice categoryName (start ,cur ); 642 643Index categoryIndex = findCategoryByName (categoryName ); 644if (categoryIndex >=0 ) 645 { 646outSlices .add (categoryName ); 647 } 648 } 649 650cur ++ ; 651 } 652 } 653} 654 655 656void CommandOptions ::findCategoryIndicesFromUsage ( 657const UnownedStringSlice & slice , 658List < Index >& outCategories )const 659{ 660List < UnownedStringSlice > categoryNames ; 661splitUsage (slice ,categoryNames ); 662 663for (auto name :categoryNames ) 664 { 665Index categoryIndex = findCategoryByName (name ); 666if (categoryIndex >=0 && outCategories .indexOf (categoryIndex )< 0 ) 667 { 668outCategories .add (categoryIndex ); 669 } 670 } 671} 672 673Count CommandOptions ::getOptionCountInRange ( 674Index categoryIndex , 675UserValue start , 676UserValue nonInclEnd )const 677{ 678const UserIndex startIndex = UserIndex (start ); 679const UserIndex endIndex = UserIndex (nonInclEnd ); 680 681Count count = 0 ; 682 683for (auto & opt :getOptionsForCategory (categoryIndex )) 684 { 685const auto val = opt .userValue ; 686if (val == kInvalidUserValue ) 687 { 688continue ; 689 } 690 691const auto valIndex = UserIndex (val ); 692count += Index (valIndex >=startIndex && valIndex < endIndex ); 693 } 694 695return count ; 696} 697 698Count CommandOptions ::getOptionCountInRange (LookupKind kind ,UserValue start ,UserValue nonInclEnd ) 699const 700{ 701Index count = 0 ; 702 703if (kind == LookupKind ::Category ) 704 { 705const UserIndex startIndex = UserIndex (start ); 706const UserIndex endIndex = UserIndex (nonInclEnd ); 707 708for (auto & cat :m_categories ) 709 { 710if (cat .userValue != kInvalidUserValue ) 711 { 712const auto valIndex = UserIndex (cat .userValue ); 713count += Index (valIndex >=startIndex && valIndex < endIndex ); 714 } 715 } 716 } 717if (kind == LookupKind ::Option ) 718 { 719// If we are lookup up options, then we iterate over all option categories 720const auto catCount = m_categories .getCount (); 721for (Index categoryIndex = 0 ;categoryIndex < catCount ;++ categoryIndex ) 722 { 723if (m_categories [categoryIndex ].kind == CategoryKind ::Option ) 724 { 725count += getOptionCountInRange (categoryIndex ,start ,nonInclEnd ); 726 } 727 } 728 } 729else if (Index (kind ) >=0 ) 730 { 731// It's a regular category 732count = getOptionCountInRange (Index (kind ),start ,nonInclEnd ); 733 } 734 735return count ; 736} 737 738 739bool CommandOptions ::hasContiguousUserValueRange ( 740LookupKind kind , 741UserValue start , 742UserValue nonInclEnd )const 743{ 744const Count rangeCount = Count (nonInclEnd )- Count (start ); 745SLANG_ASSERT (rangeCount >=0 ); 746 747if (rangeCount <=0 ) 748 { 749return true; 750 } 751 752const Count count = getOptionCountInRange (kind ,start ,nonInclEnd ); 753return rangeCount == count ; 754} 755 756}// namespace Slang