yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
b118451e3
master
1#include "slang-file-system.h" 2 3#include "../core/slang-io.h" 4#include "../core/slang-string-util.h" 5#include "slang-com-ptr.h" 6 7namespace Slang 8{ 9 10SLANG_FORCE_INLINE static SlangResult _checkExt (FileSystemStyle style ) 11{ 12return Index (style ) >=Index (FileSystemStyle ::Ext ) ?SLANG_OK :SLANG_E_NOT_IMPLEMENTED ; 13} 14SLANG_FORCE_INLINE static SlangResult _checkMutable (FileSystemStyle style ) 15{ 16return Index (style ) >=Index (FileSystemStyle ::Mutable ) ?SLANG_OK :SLANG_E_NOT_IMPLEMENTED ; 17} 18 19SLANG_FORCE_INLINE static bool _canCast (FileSystemStyle style ,const Guid & guid ) 20{ 21if (guid == ISlangUnknown ::getTypeGuid ()|| guid == ISlangCastable ::getTypeGuid ()|| 22guid == ISlangFileSystem ::getTypeGuid ()) 23 { 24return true; 25 } 26else if (guid == ISlangFileSystemExt ::getTypeGuid ()) 27 { 28return Index (style ) >=Index (FileSystemStyle ::Ext ); 29 } 30else if (guid == ISlangMutableFileSystem ::getTypeGuid ()) 31 { 32return Index (style ) >=Index (FileSystemStyle ::Mutable ); 33 } 34return false; 35} 36 37static FileSystemStyle _getFileSystemStyle (ISlangFileSystem * system ,ComPtr < ISlangFileSystem >& out ) 38{ 39SLANG_ASSERT (system ); 40 41FileSystemStyle style = FileSystemStyle ::Load ; 42 43if (SLANG_SUCCEEDED ( 44system -> queryInterface (ISlangMutableFileSystem ::getTypeGuid (), (void ** )out .writeRef ()))) 45 { 46style = FileSystemStyle ::Mutable ; 47 } 48else if (SLANG_SUCCEEDED (system -> queryInterface ( 49ISlangFileSystemExt ::getTypeGuid (), 50 (void ** )out .writeRef ()))) 51 { 52style = FileSystemStyle ::Ext ; 53 } 54else 55 { 56style = FileSystemStyle ::Load ; 57out = system ; 58 } 59 60SLANG_ASSERT (out ); 61return style ; 62} 63 64// Calcuate a combined path, just using Path:: string processing 65static SlangResult _calcCombinedPath ( 66SlangPathType fromPathType , 67const char * fromPath , 68const char * path , 69ISlangBlob ** pathOut ) 70{ 71String relPath ; 72switch (fromPathType ) 73 { 74case SLANG_PATH_TYPE_FILE : 75 { 76relPath = Path ::combine (Path ::getParentDirectory (fromPath ),path ); 77break ; 78 } 79case SLANG_PATH_TYPE_DIRECTORY : 80 { 81relPath = Path ::combine (fromPath ,path ); 82break ; 83 } 84 } 85 86* pathOut = StringUtil ::createStringBlob (relPath ).detach (); 87return SLANG_OK ; 88} 89 90/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! OSFileSystem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ 91 92/* static */ OSFileSystem OSFileSystem ::g_load (FileSystemStyle ::Load ); 93/* static */ OSFileSystem OSFileSystem ::g_ext (FileSystemStyle ::Ext ); 94/* static */ OSFileSystem OSFileSystem ::g_mutable (FileSystemStyle ::Mutable ); 95 96void * OSFileSystem ::castAs (const Guid & guid ) 97{ 98if (auto ptr = getInterface (guid )) 99 { 100return ptr ; 101 } 102return getObject (guid ); 103} 104 105ISlangUnknown * OSFileSystem ::getInterface (const Guid & guid ) 106{ 107return _canCast (m_style ,guid ) ?static_cast < ISlangFileSystem *> (this ) :nullptr ; 108} 109 110void * OSFileSystem ::getObject (const Guid & guid ) 111{ 112SLANG_UNUSED (guid ); 113return nullptr ; 114} 115 116static String _fixPathDelimiters (const char * pathIn ) 117{ 118#if SLANG_WINDOWS_FAMILY 119return pathIn ; 120#else 121// To allow windows style \ delimiters on other platforms, we convert to our standard delimiter 122String path (pathIn ); 123return StringUtil ::calcCharReplaced (pathIn ,'\\' ,Path ::kPathDelimiter ); 124#endif 125} 126 127SlangResult OSFileSystem ::getFileUniqueIdentity (const char * pathIn ,ISlangBlob ** outUniqueIdentity ) 128{ 129SLANG_RETURN_ON_FAIL (_checkExt (m_style )); 130 131// By default we use the canonical path to uniquely identify a file 132return getPath (PathKind ::Canonical ,pathIn ,outUniqueIdentity ); 133} 134 135SlangResult OSFileSystem ::getPath (PathKind pathKind ,const char * path ,ISlangBlob ** outPath ) 136{ 137SLANG_RETURN_ON_FAIL (_checkExt (m_style )); 138 139switch (pathKind ) 140 { 141case PathKind ::OperatingSystem : 142case PathKind ::Display : 143 { 144// It's possible canonical path fail... 145if (SLANG_SUCCEEDED (getPath (PathKind ::Canonical ,path ,outPath ))) 146 { 147return SLANG_OK ; 148 } 149// If so try simplified 150return getPath (PathKind ::Simplified ,path ,outPath ); 151 } 152case PathKind ::Canonical : 153 { 154String canonicalPath ; 155SLANG_RETURN_ON_FAIL (Path ::getCanonical (_fixPathDelimiters (path ),canonicalPath )); 156* outPath = StringUtil ::createStringBlob (canonicalPath ).detach (); 157return SLANG_OK ; 158 } 159case PathKind ::Simplified : 160 { 161String simplifiedPath = Path ::simplify (path ); 162* outPath = StringUtil ::createStringBlob (simplifiedPath ).detach (); 163return SLANG_OK ; 164 } 165 } 166 167return SLANG_E_NOT_AVAILABLE ; 168} 169 170SlangResult OSFileSystem ::calcCombinedPath ( 171SlangPathType fromPathType , 172const char * fromPath , 173const char * path , 174ISlangBlob ** pathOut ) 175{ 176SLANG_RETURN_ON_FAIL (_checkExt (m_style )); 177 178// Don't need to fix delimiters - because combine path handles both path delimiter types 179return _calcCombinedPath (fromPathType ,fromPath ,path ,pathOut ); 180} 181 182SlangResult SLANG_MCALL OSFileSystem ::getPathType (const char * pathIn ,SlangPathType * pathTypeOut ) 183{ 184SLANG_RETURN_ON_FAIL (_checkExt (m_style )); 185 186return Path ::getPathType (_fixPathDelimiters (pathIn ),pathTypeOut ); 187} 188 189 190SlangResult OSFileSystem ::loadFile (char const * pathIn ,ISlangBlob ** outBlob ) 191{ 192// Default implementation that uses the `core` libraries facilities for talking to the OS 193// filesystem. 194// 195// TODO: we might want to conditionally compile these in, so that 196// a user could create a build of Slang that doesn't include any OS 197// filesystem calls. 198 199const String path = _fixPathDelimiters (pathIn ); 200if (!File ::exists (path )) 201 { 202return SLANG_E_NOT_FOUND ; 203 } 204 205ScopedAllocation alloc ; 206SLANG_RETURN_ON_FAIL (File ::readAllBytes (path ,alloc )); 207* outBlob = RawBlob ::moveCreate (alloc ).detach (); 208return SLANG_OK ; 209} 210 211SlangResult OSFileSystem ::enumeratePathContents ( 212const char * path , 213FileSystemContentsCallBack callback , 214void * userData ) 215{ 216SLANG_RETURN_ON_FAIL (_checkExt (m_style )); 217 218struct Visitor :Path ::Visitor 219 { 220void accept (Path ::Type type ,const UnownedStringSlice & filename )SLANG_OVERRIDE 221 { 222m_buffer .clear (); 223m_buffer .append (filename ); 224 225SlangPathType pathType ; 226switch (type ) 227 { 228case Path ::Type ::File : 229pathType = SLANG_PATH_TYPE_FILE ; 230break ; 231case Path ::Type ::Directory : 232pathType = SLANG_PATH_TYPE_DIRECTORY ; 233break ; 234default : 235return ; 236 } 237 238m_callback (pathType ,m_buffer .getBuffer (),m_userData ); 239 } 240 241Visitor (FileSystemContentsCallBack callback ,void * userData ) 242 :m_callback (callback ),m_userData (userData ) 243 { 244 } 245StringBuilder m_buffer ; 246FileSystemContentsCallBack m_callback ; 247void * m_userData ; 248 }; 249 250Visitor visitor (callback ,userData ); 251Path ::find (path ,nullptr ,& visitor ); 252 253return SLANG_OK ; 254} 255 256SlangResult OSFileSystem ::saveFile (const char * pathIn ,const void * data ,size_t size ) 257{ 258SLANG_RETURN_ON_FAIL (_checkMutable (m_style )); 259const String path = _fixPathDelimiters (pathIn ); 260FileStream stream ; 261SLANG_RETURN_ON_FAIL ( 262stream .init (pathIn ,FileMode ::Create ,FileAccess ::Write ,FileShare ::ReadWrite )); 263SLANG_RETURN_ON_FAIL (stream .write (data ,size )); 264return SLANG_OK ; 265} 266 267SlangResult OSFileSystem ::saveFileBlob (const char * path ,ISlangBlob * dataBlob ) 268{ 269if (!dataBlob ) 270 { 271return SLANG_E_INVALID_ARG ; 272 } 273return saveFile (path ,dataBlob -> getBufferPointer (),dataBlob -> getBufferSize ()); 274} 275 276SlangResult OSFileSystem ::remove (const char * path ) 277{ 278SLANG_RETURN_ON_FAIL (_checkMutable (m_style )); 279return Path ::remove (path ); 280} 281 282SlangResult OSFileSystem ::createDirectory (const char * path ) 283{ 284SLANG_RETURN_ON_FAIL (_checkMutable (m_style )); 285return Path ::createDirectory (path ); 286} 287 288// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CacheFileSystem !!!!!!!!!!!!!!!!!!!!!!!!!!! 289 290/* static */ const Result CacheFileSystem ::s_compressedResultToResult []= { 291SLANG_E_UNINITIALIZED , 292SLANG_OK ,///< Ok 293SLANG_E_NOT_FOUND ,///< File not found 294SLANG_E_CANNOT_OPEN ,///< CannotOpen, 295SLANG_FAIL ,///< Fail 296}; 297 298/* static */ CacheFileSystem ::CompressedResult CacheFileSystem ::toCompressedResult (Result res ) 299{ 300if (SLANG_SUCCEEDED (res )) 301 { 302return CompressedResult ::Ok ; 303 } 304switch (res ) 305 { 306case SLANG_E_CANNOT_OPEN : 307return CompressedResult ::CannotOpen ; 308case SLANG_E_NOT_FOUND : 309return CompressedResult ::NotFound ; 310default : 311return CompressedResult ::Fail ; 312 } 313} 314 315void * CacheFileSystem ::castAs (const Guid & guid ) 316{ 317if (auto ptr = getInterface (guid )) 318 { 319return ptr ; 320 } 321return getObject (guid ); 322} 323 324void * CacheFileSystem ::getInterface (const Guid & guid ) 325{ 326if (_canCast (FileSystemStyle ::Ext ,guid )) 327 { 328return static_cast < ISlangFileSystemExt *> (this ); 329 } 330return nullptr ; 331} 332 333void * CacheFileSystem ::getObject (const Guid & guid ) 334{ 335if (guid == CacheFileSystem ::getTypeGuid ()) 336 { 337return this ; 338 } 339return nullptr ; 340} 341 342CacheFileSystem ::CacheFileSystem ( 343ISlangFileSystem * fileSystem , 344UniqueIdentityMode uniqueIdentityMode , 345PathStyle pathStyle ) 346{ 347setInnerFileSystem (fileSystem ,uniqueIdentityMode ,pathStyle ); 348} 349 350CacheFileSystem ::~CacheFileSystem () 351{ 352for (const auto & [_ ,pathInfo ] :m_uniqueIdentityMap ) 353delete pathInfo ; 354} 355 356void CacheFileSystem ::setInnerFileSystem ( 357ISlangFileSystem * fileSystem , 358UniqueIdentityMode uniqueIdentityMode , 359PathStyle pathStyle ) 360{ 361m_fileSystem = fileSystem ; 362 363m_uniqueIdentityMode = uniqueIdentityMode ; 364m_pathStyle = pathStyle ; 365 366m_fileSystemExt .setNull (); 367 368if (fileSystem ) 369 { 370// Try to get the more sophisticated interface 371fileSystem -> queryInterface (SLANG_IID_PPV_ARGS (m_fileSystemExt .writeRef ())); 372 } 373 374// Determine how paths map 375m_osPathKind = m_fileSystemExt ?m_fileSystemExt -> getOSPathKind () :OSPathKind ::None ; 376 377switch (m_uniqueIdentityMode ) 378 { 379case UniqueIdentityMode ::Default : 380case UniqueIdentityMode ::FileSystemExt : 381 { 382// If it's not a complete file system, we will default to SimplifyAndHash style by 383// default 384m_uniqueIdentityMode = m_fileSystemExt ?UniqueIdentityMode ::FileSystemExt 385 :UniqueIdentityMode ::SimplifyPathAndHash ; 386break ; 387 } 388default : 389break ; 390 } 391 392if (pathStyle == PathStyle ::Default ) 393 { 394// We'll assume it's simplify-able 395m_pathStyle = PathStyle ::Simplifiable ; 396// If we have fileSystemExt, we defer to that 397if (m_fileSystemExt ) 398 { 399// We just defer to the m_fileSystem 400m_pathStyle = PathStyle ::FileSystemExt ; 401 } 402 } 403 404// It can't be default 405SLANG_ASSERT (m_uniqueIdentityMode != UniqueIdentityMode ::Default ); 406} 407 408void CacheFileSystem ::clearCache () 409{ 410for (const auto & [_ ,pathInfo ] :m_uniqueIdentityMap ) 411delete pathInfo ; 412 413m_uniqueIdentityMap .clear (); 414m_pathMap .clear (); 415 416if (m_fileSystemExt ) 417 { 418m_fileSystemExt -> clearCache (); 419 } 420} 421 422 423// Determines if we can simplify a path for a given mode 424static bool _canSimplifyPath (CacheFileSystem ::UniqueIdentityMode mode ) 425{ 426typedef CacheFileSystem ::UniqueIdentityMode UniqueIdentityMode ; 427switch (mode ) 428 { 429case UniqueIdentityMode ::SimplifyPath : 430case UniqueIdentityMode ::SimplifyPathAndHash : 431 { 432return true; 433 } 434default : 435 { 436return false; 437 } 438 } 439} 440 441SlangResult CacheFileSystem ::enumeratePathContents ( 442const char * path , 443FileSystemContentsCallBack callback , 444void * userData ) 445{ 446if (m_fileSystemExt ) 447 { 448return m_fileSystemExt -> enumeratePathContents (path ,callback ,userData ); 449 } 450 451// Okay.. the contents of the 'cache' *is* the filesystem. So lets iterate over that 452// This will win no prizes for efficiency, but that is unlikely to matter for typical usage 453 454if (!_canSimplifyPath (m_uniqueIdentityMode )) 455 { 456// As it stands if we can't simplify paths, it's kind of hard to make this 457// all work. As we use the simplified path cache 458return SLANG_E_NOT_IMPLEMENTED ; 459 } 460 461// Simplify the path 462String simplifiedPath = Path ::simplify (path ); 463 464// If the simplified path is just a . then we don't have any prefix 465if (simplifiedPath == "." ) 466 { 467simplifiedPath = "" ; 468 } 469 470for (const auto & [currentPath ,pathInfo ] :m_pathMap ) 471 { 472// NOTE! The currentPath can be a *non* simplified path (the m_pathMap is the cache of paths 473// simplified and other to a file/directory) Also note that there will always be the 474// simplified version of the path in cache. 475 476// If it doesn't start with simplified path, then it can't be a hit 477if (!currentPath .startsWith (simplifiedPath )) 478 { 479continue ; 480 } 481 482UnownedStringSlice remaining ( 483currentPath .getBuffer ()+ simplifiedPath .getLength (), 484currentPath .end ()); 485 486// If it starts with a / delimiter strip it 487if (remaining .getLength ()> 0 && remaining [0 ]== '/' ) 488 { 489remaining = UnownedStringSlice (remaining .begin ()+ 1 ,remaining .end ()); 490 } 491 492// If it has a path separator then it's either not simplified - so we ignore (we only want 493// to invoke on the simplified path version as there is only one of these for every 494// PathInfo) or it is a child file/directory, and so we ignore that too. 495if (remaining .indexOf ('/' ) >=0 || remaining .indexOf ('\\' ) >=0 ) 496 { 497continue ; 498 } 499 500// We *know* that remaining comes from the end of currentPath .We also know currentPath is 501// zero terminated. So we can just use (normally this would be a problem because 502// UnownedStringSlice is generally *not* followed by zero termination. 503const char * foundPath = remaining .begin (); 504// Let's check that fact... 505SLANG_ASSERT (foundPath [remaining .getLength ()]== 0 ); 506 507SlangPathType pathType ; 508if (SLANG_FAILED (_getPathType (pathInfo ,currentPath .getBuffer (),& pathType ))) 509 { 510continue ; 511 } 512 513callback (pathType ,foundPath ,userData ); 514 } 515 516return SLANG_OK ; 517} 518 519 520SlangResult CacheFileSystem ::_calcUniqueIdentity ( 521const String & path , 522String & outUniqueIdentity , 523ComPtr < ISlangBlob >& outFileContents ) 524{ 525switch (m_uniqueIdentityMode ) 526 { 527case UniqueIdentityMode ::FileSystemExt : 528 { 529// Try getting the uniqueIdentity by asking underlying file system 530ComPtr < ISlangBlob > uniqueIdentity ; 531SLANG_RETURN_ON_FAIL (m_fileSystemExt -> getFileUniqueIdentity ( 532path .getBuffer (), 533uniqueIdentity .writeRef ())); 534// Get the path as a string 535outUniqueIdentity = StringUtil ::getString (uniqueIdentity ); 536return SLANG_OK ; 537 } 538case UniqueIdentityMode ::Path : 539 { 540outUniqueIdentity = path ; 541return SLANG_OK ; 542 } 543case UniqueIdentityMode ::SimplifyPath : 544 { 545outUniqueIdentity = Path ::simplify (path ); 546// If it still has relative elements can't uniquely identify, so give up 547return Path ::hasRelativeElement (outUniqueIdentity ) ?SLANG_FAIL :SLANG_OK ; 548 } 549case UniqueIdentityMode ::SimplifyPathAndHash : 550case UniqueIdentityMode ::Hash : 551 { 552// If m_uniqueIdentityMode is SimplifyPathAndHash, the path will already be simplified 553// before this function is hit (and it hasn't been found via path lookup). That being 554// the case only option left is to 'hash' (or fallback to backing impls uniqueIdentity 555// impl) 556 557// If we don't have a file system -> assume cannot be found 558if (m_fileSystem == nullptr ) 559 { 560return SLANG_E_NOT_FOUND ; 561 } 562 563// First attempt to load as a file 564Result res = m_fileSystem -> loadFile (path .getBuffer (),outFileContents .writeRef ()); 565 566// If it succeeded but there is no contents, then make the result NOT_FOUND 567res = (SLANG_SUCCEEDED (res )&& outFileContents == nullptr ) ?SLANG_E_NOT_FOUND :res ; 568 569// If that failed, we may be able to do something if m_fileSystemExt is available 570if (SLANG_FAILED (res )) 571 { 572// If we have m_fileSystemExt interface we can just use it's implementation, as a 573// fallback. Doing so will mean the uniqueIdentity will work if say it's a directory 574if (m_fileSystemExt ) 575 { 576ComPtr < ISlangBlob > uniqueIdentity ; 577SLANG_RETURN_ON_FAIL (m_fileSystemExt -> getFileUniqueIdentity ( 578path .getBuffer (), 579uniqueIdentity .writeRef ())); 580// Get the path as a string 581outUniqueIdentity = StringUtil ::getString (uniqueIdentity ); 582return SLANG_OK ; 583 } 584 585// If we can't access as a file (or use the backing implementations impl), we are in 586// a tricky situation. The ISlangFileSystem interface provides no way to determine 587// if the path is a directory for example - so there is no way of determining if 588// something along the path exists. 589// 590// So we just return the error. 591return res ; 592 } 593 594// Calculate the hash on the contents 595const StableHashCode64 hash = getStableHashCode64 ( 596 (const char * )outFileContents -> getBufferPointer (), 597outFileContents -> getBufferSize ()); 598 599String hashString = Path ::getFileName (path ); 600hashString = hashString .toLower (); 601 602hashString .append (':' ); 603 604// The uniqueIdentity is a combination of name and hash 605hashString .append (hash ); 606 607outUniqueIdentity = hashString ; 608return SLANG_OK ; 609 } 610 } 611 612return SLANG_FAIL ; 613} 614 615CacheFileSystem ::PathInfo * CacheFileSystem ::_resolveUniqueIdentityCacheInfo (const String & path ) 616{ 617// Use the path to produce uniqueIdentity information 618ComPtr < ISlangBlob > fileContents ; 619String uniqueIdentity ; 620 621SlangResult res = _calcUniqueIdentity (path ,uniqueIdentity ,fileContents ); 622if (SLANG_FAILED (res )) 623 { 624// Was not able to create a uniqueIdentity - return failure as nullptr 625return nullptr ; 626 } 627 628// Now try looking up by uniqueIdentity path. If not found, add a new result 629PathInfo * pathInfo = nullptr ; 630if (!m_uniqueIdentityMap .tryGetValue (uniqueIdentity ,pathInfo )) 631 { 632// Create with found uniqueIdentity 633pathInfo = new PathInfo (uniqueIdentity ); 634m_uniqueIdentityMap .add (uniqueIdentity ,pathInfo ); 635 } 636 637// At this point they must have same uniqueIdentity 638SLANG_ASSERT (pathInfo -> getUniqueIdentity ()== uniqueIdentity ); 639 640// If we have the file contents (because of calc-ing uniqueIdentity), and there isn't a read 641// file blob already store the data as if read, so doesn't get read again 642if (fileContents && !pathInfo -> m_fileBlob ) 643 { 644pathInfo -> m_fileBlob = fileContents ; 645pathInfo -> m_loadFileResult = CompressedResult ::Ok ; 646 } 647 648return pathInfo ; 649} 650 651CacheFileSystem ::PathInfo * CacheFileSystem ::_resolveSimplifiedPathCacheInfo (const String & path ) 652{ 653// If we can simplify the path, try looking up in path cache with simplified path (as long as 654// it's different!) 655if (_canSimplifyPath (m_uniqueIdentityMode )) 656 { 657const String simplifiedPath = Path ::simplify (path ); 658// Only lookup if the path is different - because otherwise will recurse forever... 659if (simplifiedPath != path ) 660 { 661// This is a recursive call - and will ensure the simplified path is added to the cache 662return _resolvePathCacheInfo (simplifiedPath ); 663 } 664 } 665 666return _resolveUniqueIdentityCacheInfo (path ); 667} 668 669CacheFileSystem ::PathInfo * CacheFileSystem ::_resolvePathCacheInfo (const String & path ) 670{ 671// Lookup in path cache 672PathInfo * pathInfo ; 673if (m_pathMap .tryGetValue (path ,pathInfo )) 674 { 675// Found so done 676return pathInfo ; 677 } 678 679// Try getting or creating taking into account possible path simplification 680pathInfo = _resolveSimplifiedPathCacheInfo (path ); 681// Always add the result to the path cache (even if null) 682m_pathMap .add (path ,pathInfo ); 683return pathInfo ; 684} 685 686SlangResult CacheFileSystem ::loadFile (char const * pathIn ,ISlangBlob ** blobOut ) 687{ 688* blobOut = nullptr ; 689String path (pathIn ); 690PathInfo * info = _resolvePathCacheInfo (path ); 691if (!info ) 692 { 693return SLANG_FAIL ; 694 } 695 696if (info -> m_loadFileResult == CompressedResult ::Uninitialized ) 697 { 698info -> m_loadFileResult = toCompressedResult ( 699m_fileSystem -> loadFile (path .getBuffer (),info -> m_fileBlob .writeRef ())); 700 } 701 702* blobOut = info -> m_fileBlob ; 703if (* blobOut ) 704 { 705 (* blobOut )-> addRef (); 706 } 707return toResult (info -> m_loadFileResult ); 708} 709 710SlangResult CacheFileSystem ::getFileUniqueIdentity (const char * path ,ISlangBlob ** outUniqueIdentity ) 711{ 712* outUniqueIdentity = nullptr ; 713PathInfo * info = _resolvePathCacheInfo (path ); 714if (!info || info -> m_uniqueIdentity .getLength () <=0 ) 715 { 716return SLANG_E_NOT_FOUND ; 717 } 718 719* outUniqueIdentity = StringBlob ::create (info -> m_uniqueIdentity ).detach (); 720return SLANG_OK ; 721} 722 723SlangResult CacheFileSystem ::calcCombinedPath ( 724SlangPathType fromPathType , 725const char * fromPath , 726const char * path , 727ISlangBlob ** pathOut ) 728{ 729// Just defer to contained implementation 730switch (m_pathStyle ) 731 { 732case PathStyle ::FileSystemExt : 733 { 734return m_fileSystemExt -> calcCombinedPath (fromPathType ,fromPath ,path ,pathOut ); 735 } 736default : 737 { 738// Just use the default implementation 739return _calcCombinedPath (fromPathType ,fromPath ,path ,pathOut ); 740 } 741 } 742} 743 744SlangResult CacheFileSystem ::_getPathType ( 745PathInfo * info , 746const char * inPath , 747SlangPathType * outPathType ) 748{ 749if (info -> m_getPathTypeResult == CompressedResult ::Uninitialized ) 750 { 751if (m_fileSystemExt ) 752 { 753info -> m_getPathTypeResult = 754toCompressedResult (m_fileSystemExt -> getPathType (inPath ,& info -> m_pathType )); 755 } 756else 757 { 758// Okay try to load the file 759if (info -> m_loadFileResult == CompressedResult ::Uninitialized ) 760 { 761info -> m_loadFileResult = 762toCompressedResult (m_fileSystem -> loadFile (inPath ,info -> m_fileBlob .writeRef ())); 763 } 764 765// Make the getPathResult the same as the load result 766info -> m_getPathTypeResult = info -> m_loadFileResult ; 767// Just set to file... the result is what matters in this case 768info -> m_pathType = SLANG_PATH_TYPE_FILE ; 769 } 770 } 771 772* outPathType = info -> m_pathType ; 773return toResult (info -> m_getPathTypeResult ); 774} 775 776SlangResult CacheFileSystem ::getPathType (const char * inPath ,SlangPathType * outPathType ) 777{ 778PathInfo * info = _resolvePathCacheInfo (inPath ); 779if (!info ) 780 { 781return SLANG_E_NOT_FOUND ; 782 } 783 784return _getPathType (info ,inPath ,outPathType ); 785} 786 787SlangResult CacheFileSystem ::getPath (PathKind kind ,const char * path ,ISlangBlob ** outPath ) 788{ 789switch (kind ) 790 { 791case PathKind ::Simplified : 792return _getSimplifiedPath (path ,outPath ); 793case PathKind ::Canonical : 794return _getCanonicalPath (path ,outPath ); 795default : 796break ; 797 } 798 799if (m_fileSystemExt ) 800 { 801return m_fileSystemExt -> getPath (kind ,path ,outPath ); 802 } 803 804// If we don't have a fileSystem, we can try the canonical path 805if (SLANG_SUCCEEDED (getPath (PathKind ::Canonical ,path ,outPath ))) 806 { 807return SLANG_OK ; 808 } 809// Else we can try simplified 810return getPath (PathKind ::Simplified ,path ,outPath ); 811} 812 813SlangResult CacheFileSystem ::_getSimplifiedPath (const char * path ,ISlangBlob ** outSimplifiedPath ) 814{ 815// If we have a ISlangFileSystemExt we can just pass on the request to it 816switch (m_pathStyle ) 817 { 818case PathStyle ::FileSystemExt : 819 { 820return m_fileSystemExt -> getPath (PathKind ::Simplified ,path ,outSimplifiedPath ); 821 } 822case PathStyle ::Simplifiable : 823 { 824String simplifiedPath = Path ::simplify (path ); 825* outSimplifiedPath = StringUtil ::createStringBlob (simplifiedPath ).detach (); 826return SLANG_OK ; 827 } 828default : 829return SLANG_E_NOT_IMPLEMENTED ; 830 } 831} 832 833SlangResult CacheFileSystem ::_getCanonicalPath (const char * path ,ISlangBlob ** outCanonicalPath ) 834{ 835* outCanonicalPath = nullptr ; 836 837// A file must exist to get a canonical path... 838PathInfo * info = _resolvePathCacheInfo (path ); 839if (!info ) 840 { 841return SLANG_E_NOT_FOUND ; 842 } 843 844// We don't have this -> so read it ... 845if (info -> m_getCanonicalPathResult == CompressedResult ::Uninitialized ) 846 { 847if (!m_fileSystemExt ) 848 { 849return SLANG_E_NOT_IMPLEMENTED ; 850 } 851 852// Try getting the canonicalPath by asking underlying file system 853ComPtr < ISlangBlob > canonicalPathBlob ; 854SlangResult res = 855m_fileSystemExt -> getPath (PathKind ::Canonical ,path ,canonicalPathBlob .writeRef ()); 856 857if (SLANG_SUCCEEDED (res )) 858 { 859// Get the path as a string 860info -> m_canonicalPath = StringUtil ::getString (canonicalPathBlob ); 861if (info -> m_canonicalPath .getLength () <=0 ) 862 { 863res = SLANG_FAIL ; 864 } 865 } 866 867// Save the result 868info -> m_getCanonicalPathResult = toCompressedResult (res ); 869 } 870 871// Create the blob 872if (info -> m_canonicalPath .getLength ()) 873 { 874* outCanonicalPath = StringBlob ::create (info -> m_canonicalPath ).detach (); 875 } 876 877return SLANG_OK ; 878} 879 880/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! RelativeFileSystem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ 881 882RelativeFileSystem ::RelativeFileSystem ( 883ISlangFileSystem * fileSystem , 884const String & relativePath , 885bool stripPath ) 886 :m_relativePath (relativePath ),m_stripPath (stripPath ) 887{ 888m_style = _getFileSystemStyle (fileSystem ,m_fileSystem ); 889 890m_osPathKind = OSPathKind ::None ; 891 892ComPtr < ISlangFileSystemExt > ext ; 893if (SLANG_SUCCEEDED (fileSystem -> queryInterface (SLANG_IID_PPV_ARGS (ext .writeRef ())))) 894 { 895m_osPathKind = ext -> getOSPathKind (); 896 897// If it's direct, but we have a relative path, "operating system" should work 898if (m_osPathKind == OSPathKind ::Direct && relativePath .getLength ()) 899 { 900m_osPathKind = OSPathKind ::OperatingSystem ; 901 } 902 } 903} 904 905ISlangUnknown * RelativeFileSystem ::getInterface (const Guid & guid ) 906{ 907return _canCast (m_style ,guid ) ?static_cast < ISlangMutableFileSystem *> (this ) :nullptr ; 908} 909 910void * RelativeFileSystem ::getObject (const Guid & guid ) 911{ 912SLANG_UNUSED (guid ); 913return nullptr ; 914} 915 916void * RelativeFileSystem ::castAs (const Guid & guid ) 917{ 918if (auto ptr = getInterface (guid )) 919 { 920return ptr ; 921 } 922return getObject (guid ); 923} 924 925SlangResult RelativeFileSystem ::_calcCombinedPathInner ( 926SlangPathType fromPathType , 927const char * fromPath , 928const char * path , 929ISlangBlob ** outPath ) 930{ 931ISlangFileSystemExt * fileSystem = _getExt (); 932if (fileSystem ) 933 { 934return fileSystem -> calcCombinedPath (fromPathType ,fromPath ,path ,outPath ); 935 } 936else 937 { 938return _calcCombinedPath (fromPathType ,fromPath ,path ,outPath ); 939 } 940} 941 942SlangResult RelativeFileSystem ::_getCanonicalPath (const char * path ,String & outPath ) 943{ 944if (m_stripPath ) 945 { 946// We are just using the filename. There is no path that could go outside of the the 947// relative path so we can use as is 948outPath = Path ::getFileName (path ); 949 } 950else 951 { 952// NOTE that we don't want the canonical path to be absolute with a leading "/" 953// because paths specified which aren't absolute, would produce a different path. 954// 955// Ie we want (and get with these options) 956// "a" -> "a" 957// "/a" -> "a". 958// 959// If we allowed the root to be included then... 960// "a" -> "a" 961// "/a" -> "/a" 962// 963// Two identical paths would match to different paths, which wouldn't be canonical. 964// 965// This could be fixed by making all paths absolute with '/' too, but it's easier to just 966// make all not have "/" 967 968StringBuilder canonicalPath ; 969// We want the input path to be local to this file system 970SLANG_RETURN_ON_FAIL ( 971Path ::simplify (path ,Path ::SimplifyStyle ::AbsoluteOnlyAndNoRoot ,canonicalPath )); 972outPath = canonicalPath ; 973 } 974return SLANG_OK ; 975} 976 977SlangResult RelativeFileSystem ::_getFixedPath (const char * path ,String & outPath ) 978{ 979ComPtr < ISlangBlob > blob ; 980 981String canonicalPath ; 982SLANG_RETURN_ON_FAIL (_getCanonicalPath (path ,canonicalPath )); 983 984SLANG_RETURN_ON_FAIL (_calcCombinedPathInner ( 985SLANG_PATH_TYPE_DIRECTORY , 986m_relativePath .getBuffer (), 987canonicalPath .getBuffer (), 988blob .writeRef ())); 989outPath = StringUtil ::getString (blob ); 990 991return SLANG_OK ; 992} 993 994SlangResult RelativeFileSystem ::loadFile (char const * path ,ISlangBlob ** outBlob ) 995{ 996String fixedPath ; 997SLANG_RETURN_ON_FAIL (_getFixedPath (path ,fixedPath )); 998return m_fileSystem -> loadFile (fixedPath .getBuffer (),outBlob ); 999} 1000 1001SlangResult RelativeFileSystem ::getFileUniqueIdentity ( 1002const char * path , 1003ISlangBlob ** outUniqueIdentity ) 1004{ 1005auto fileSystem = _getExt (); 1006if (!fileSystem ) 1007return SLANG_E_NOT_IMPLEMENTED ; 1008 1009String fixedPath ; 1010SLANG_RETURN_ON_FAIL (_getFixedPath (path ,fixedPath )); 1011return fileSystem -> getFileUniqueIdentity (fixedPath .getBuffer (),outUniqueIdentity ); 1012} 1013 1014SlangResult RelativeFileSystem ::calcCombinedPath ( 1015SlangPathType fromPathType , 1016const char * fromPath , 1017const char * path , 1018ISlangBlob ** outPath ) 1019{ 1020auto fileSystem = _getExt (); 1021if (!fileSystem ) 1022return SLANG_E_NOT_IMPLEMENTED ; 1023 1024String fixedFromPath ; 1025SLANG_RETURN_ON_FAIL (_getFixedPath (fromPath ,fixedFromPath )); 1026 1027return fileSystem -> calcCombinedPath (fromPathType ,fixedFromPath .getBuffer (),path ,outPath ); 1028} 1029 1030SlangResult RelativeFileSystem ::getPathType (const char * path ,SlangPathType * outPathType ) 1031{ 1032auto fileSystem = _getExt (); 1033if (!fileSystem ) 1034return SLANG_E_NOT_IMPLEMENTED ; 1035 1036String fixedPath ; 1037SLANG_RETURN_ON_FAIL (_getFixedPath (path ,fixedPath )); 1038return fileSystem -> getPathType (fixedPath .getBuffer (),outPathType ); 1039} 1040 1041SlangResult RelativeFileSystem ::getPath (PathKind kind ,const char * path ,ISlangBlob ** outPath ) 1042{ 1043auto fileSystem = _getExt (); 1044if (!fileSystem ) 1045return SLANG_E_NOT_IMPLEMENTED ; 1046 1047switch (kind ) 1048 { 1049case PathKind ::Simplified : 1050 { 1051return fileSystem -> getPath (kind ,path ,outPath ); 1052 } 1053case PathKind ::Display : 1054 { 1055// If not backed by OS, just use simplified path, else use the Operating system path 1056kind = (fileSystem -> getOSPathKind ()== OSPathKind ::None ) ?PathKind ::Simplified 1057 :PathKind ::OperatingSystem ; 1058return getPath (kind ,path ,outPath ); 1059 } 1060case PathKind ::Canonical : 1061 { 1062String canonicalPath ; 1063SLANG_RETURN_ON_FAIL (_getCanonicalPath (path ,canonicalPath )); 1064* outPath = StringBlob ::moveCreate (canonicalPath ).detach (); 1065return SLANG_OK ; 1066 } 1067case PathKind ::OperatingSystem : 1068 { 1069String fixedPath ; 1070SLANG_RETURN_ON_FAIL (_getFixedPath (path ,fixedPath )); 1071return fileSystem -> getPath (kind ,fixedPath .getBuffer (),outPath ); 1072 } 1073 } 1074 1075return SLANG_FAIL ; 1076} 1077 1078void RelativeFileSystem ::clearCache () 1079{ 1080auto fileSystem = _getExt (); 1081if (!fileSystem ) 1082return ; 1083 1084fileSystem -> clearCache (); 1085} 1086 1087SlangResult RelativeFileSystem ::enumeratePathContents ( 1088const char * path , 1089FileSystemContentsCallBack callback , 1090void * userData ) 1091{ 1092auto fileSystem = _getExt (); 1093if (!fileSystem ) 1094return SLANG_E_NOT_IMPLEMENTED ; 1095 1096String fixedPath ; 1097SLANG_RETURN_ON_FAIL (_getFixedPath (path ,fixedPath )); 1098return fileSystem -> enumeratePathContents (fixedPath .getBuffer (),callback ,userData ); 1099} 1100 1101SlangResult RelativeFileSystem ::saveFile (const char * path ,const void * data ,size_t size ) 1102{ 1103auto fileSystem = _getMutable (); 1104if (!fileSystem ) 1105return SLANG_E_NOT_IMPLEMENTED ; 1106 1107String fixedPath ; 1108SLANG_RETURN_ON_FAIL (_getFixedPath (path ,fixedPath )); 1109return fileSystem -> saveFile (fixedPath .getBuffer (),data ,size ); 1110} 1111 1112SlangResult RelativeFileSystem ::saveFileBlob (const char * path ,ISlangBlob * dataBlob ) 1113{ 1114auto fileSystem = _getMutable (); 1115if (!fileSystem ) 1116return SLANG_E_NOT_IMPLEMENTED ; 1117 1118String fixedPath ; 1119SLANG_RETURN_ON_FAIL (_getFixedPath (path ,fixedPath )); 1120return fileSystem -> saveFileBlob (fixedPath .getBuffer (),dataBlob ); 1121} 1122 1123SlangResult RelativeFileSystem ::remove (const char * path ) 1124{ 1125auto fileSystem = _getMutable (); 1126if (!fileSystem ) 1127return SLANG_E_NOT_IMPLEMENTED ; 1128 1129String fixedPath ; 1130SLANG_RETURN_ON_FAIL (_getFixedPath (path ,fixedPath )); 1131return fileSystem -> remove (fixedPath .getBuffer ()); 1132} 1133 1134SlangResult RelativeFileSystem ::createDirectory (const char * path ) 1135{ 1136auto fileSystem = _getMutable (); 1137if (!fileSystem ) 1138return SLANG_E_NOT_IMPLEMENTED ; 1139 1140String fixedPath ; 1141SLANG_RETURN_ON_FAIL (_getFixedPath (path ,fixedPath )); 1142return fileSystem -> createDirectory (fixedPath .getBuffer ()); 1143} 1144 1145}// namespace Slang