yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

RonanImplemented #pragma warning (#6748)5f632cd20

master
29.0 KiB1061 linesraw
1// slang-source-loc.cpp
2#include "slang-source-loc.h"
3
4#include "../core/slang-char-encode.h"
5#include "../core/slang-string-escape-util.h"
6#include "../core/slang-string-util.h"
7#include "slang-artifact-desc-util.h"
8#include "slang-artifact-impl.h"
9#include "slang-artifact-representation-impl.h"
10#include "slang-artifact-util.h"
11
12namespace Slang
13{
14
15/* !!!!!!!!!!!!!!!!!!!!!!!!! SourceView !!!!!!!!!!!!!!!!!!!!!!!!!!!! */
16
17const String PathInfo::getMostUniqueIdentity() const
18{
19    switch (type)
20    {
21    case Type::Normal:
22        return uniqueIdentity;
23    case Type::FoundPath:
24    case Type::FromString:
25        {
26            return foundPath;
27        }
28    default:
29        return "";
30    }
31}
32
33String PathInfo::getName() const
34{
35    switch (type)
36    {
37    case Type::Normal:
38    case Type::FromString:
39    case Type::FoundPath:
40        {
41            return foundPath;
42        }
43    }
44    return String();
45}
46
47bool PathInfo::operator==(const ThisType& rhs) const
48{
49    // They must be the same type
50    if (type != rhs.type)
51    {
52        return false;
53    }
54
55    switch (type)
56    {
57    case Type::TokenPaste:
58    case Type::TypeParse:
59    case Type::Unknown:
60    case Type::CommandLine:
61        {
62            return true;
63        }
64    case Type::Normal:
65        {
66            return foundPath == rhs.foundPath && uniqueIdentity == rhs.uniqueIdentity;
67        }
68    case Type::FromString:
69    case Type::FoundPath:
70        {
71            // Only have a found path
72            return foundPath == rhs.foundPath;
73        }
74    default:
75        break;
76    }
77
78    return false;
79}
80
81void PathInfo::appendDisplayName(StringBuilder& out) const
82{
83    switch (type)
84    {
85    case Type::TokenPaste:
86        out << "[Token Paste]";
87        break;
88    case Type::TypeParse:
89        out << "[Type Parse]";
90        break;
91    case Type::Unknown:
92        out << "[Unknown]";
93        break;
94    case Type::CommandLine:
95        out << "[Command Line]";
96        break;
97    case Type::Normal:
98    case Type::FromString:
99    case Type::FoundPath:
100        {
101            StringEscapeUtil::appendQuoted(
102                StringEscapeUtil::getHandler(StringEscapeUtil::Style::Cpp),
103                foundPath.getUnownedSlice(),
104                out);
105            break;
106        }
107    default:
108        break;
109    }
110}
111
112/* !!!!!!!!!!!!!!!!!!!!!!!!! SourceView !!!!!!!!!!!!!!!!!!!!!!!!!!!! */
113
114int SourceView::findEntryIndex(SourceLoc sourceLoc) const
115{
116    if (!m_range.contains(sourceLoc))
117    {
118        return -1;
119    }
120
121    const auto rawValue = sourceLoc.getRaw();
122
123    Index hi = m_entries.getCount();
124    // If there are no entries, or it is in front of the first entry, then there is no associated
125    // entry
126    if (hi == 0 || m_entries[0].m_startLoc.getRaw() > sourceLoc.getRaw())
127    {
128        return -1;
129    }
130
131    Index lo = 0;
132    while (lo + 1 < hi)
133    {
134        const Index mid = (hi + lo) >> 1;
135        const Entry& midEntry = m_entries[mid];
136        SourceLoc::RawValue midValue = midEntry.m_startLoc.getRaw();
137        if (midValue <= rawValue)
138        {
139            // The location we seek is at or after this entry
140            lo = mid;
141        }
142        else
143        {
144            // The location we seek is before this entry
145            hi = mid;
146        }
147    }
148
149    return int(lo);
150}
151
152void SourceView::addLineDirective(
153    SourceLoc directiveLoc,
154    StringSlicePool::Handle pathHandle,
155    int line)
156{
157    SLANG_ASSERT(pathHandle != StringSlicePool::Handle(0));
158    SLANG_ASSERT(m_range.contains(directiveLoc));
159
160    // Check that the directiveLoc values are always increasing
161    SLANG_ASSERT(
162        m_entries.getCount() == 0 ||
163        (m_entries.getLast().m_startLoc.getRaw() < directiveLoc.getRaw()));
164
165    // Calculate the offset
166    const int offset = m_range.getOffset(directiveLoc);
167
168    // Get the line index in the original file
169    const int lineIndex = m_sourceFile->calcLineIndexFromOffset(offset);
170
171    Entry entry;
172    entry.m_startLoc = directiveLoc;
173    entry.m_pathHandle = pathHandle;
174
175    // We also need to make sure that any lookups for line numbers will
176    // get corrected based on this files location.
177    // We assume the line number coming from the directive is a line number, NOT an index, so the
178    // correction needs + 1 There is an additional + 1 because we want the NEXT line - ie the line
179    // after the #line directive, to the specified value Taking both into account means +2 is
180    // correct 'fix'
181    entry.m_lineAdjust = line - (lineIndex + 2);
182
183    m_entries.add(entry);
184}
185
186void SourceView::addLineDirective(SourceLoc directiveLoc, const String& path, int line)
187{
188    StringSlicePool::Handle pathHandle =
189        getSourceManager()->getStringSlicePool().add(path.getUnownedSlice());
190    return addLineDirective(directiveLoc, pathHandle, line);
191}
192
193void SourceView::addDefaultLineDirective(SourceLoc directiveLoc)
194{
195    SLANG_ASSERT(m_range.contains(directiveLoc));
196    // Check that the directiveLoc values are always increasing
197    SLANG_ASSERT(
198        m_entries.getCount() == 0 ||
199        (m_entries.getLast().m_startLoc.getRaw() < directiveLoc.getRaw()));
200
201    // Well if there are no entries, or the last one puts it in default case, then we don't need to
202    // add anything
203    if (m_entries.getCount() == 0 || (m_entries.getCount() && m_entries.getLast().isDefault()))
204    {
205        return;
206    }
207
208    Entry entry;
209    entry.m_startLoc = directiveLoc;
210    entry.m_lineAdjust = 0; // No line adjustment... we are going back to default
211    entry.m_pathHandle =
212        StringSlicePool::Handle(0); // Mark that there is no path, and that this is a 'default'
213
214    SLANG_ASSERT(entry.isDefault());
215
216    m_entries.add(entry);
217}
218
219// Nominal-like types take into account line directives, and potentially source maps
220static bool _isNominalLike(SourceLocType type)
221{
222    return type == SourceLocType::Nominal || type == SourceLocType::Emit;
223}
224
225static bool _canFollowSourceMap(SourceFile* sourceFile, SourceLocType type)
226{
227    // If we don't have a source map we have nothing to follow
228    if (!sourceFile->getSourceMap())
229    {
230        return false;
231    }
232
233    // If it's obfuscated we can't follow if we are emitting
234    if (sourceFile->getSourceMapKind() == SourceMapKind::Obfuscated && type == SourceLocType::Emit)
235    {
236        return false;
237    }
238
239    return _isNominalLike(type);
240}
241
242static SlangResult _findLocWithSourceMap(
243    SourceManager* lookupSourceManager,
244    SourceView* sourceView,
245    SourceLoc loc,
246    SourceLocType type,
247    HandleSourceLoc& outLoc)
248{
249    auto sourceFile = sourceView->getSourceFile();
250
251    if (!_canFollowSourceMap(sourceFile, type))
252    {
253        return SLANG_E_NOT_FOUND;
254    }
255
256    // Hold a list of sourceFiles visited so we can't end up in a loop of lookups
257    ShortList<SourceFile*, 8> sourceFiles;
258    sourceFiles.add(sourceFile);
259
260    Index entryIndex = -1;
261
262    // Do the initial lookup using the loc
263    {
264        const auto offset = sourceView->getRange().getOffset(loc);
265
266        const auto lineIndex = sourceFile->calcLineIndexFromOffset(offset);
267        const auto colIndex = sourceFile->calcColumnIndex(lineIndex, offset);
268
269        // If we are in this function the sourceFile should have a map
270        auto sourceMap = sourceFile->getSourceMap();
271        SLANG_ASSERT(sourceMap);
272
273        entryIndex = sourceMap->get().findEntry(lineIndex, colIndex);
274    }
275
276    if (entryIndex < 0)
277    {
278        return SLANG_FAIL;
279    }
280
281    // Keep searching through source maps
282    do
283    {
284        auto sourceMap = sourceFile->getSourceMap()->getPtr();
285
286        // Find the entry
287        const auto& entry = sourceMap->getEntryByIndex(entryIndex);
288        const auto sourceFileName = sourceMap->getSourceFileName(entry.sourceFileIndex);
289
290        // If we have a source name, see if it already exists in source manager
291        if (sourceFileName.getLength())
292        {
293            if (auto foundSourceFile =
294                    lookupSourceManager->findSourceFileByPathRecursively(sourceFileName))
295            {
296                // We only follow if the source file hasn't already been visisted
297                if (sourceFiles.indexOf(foundSourceFile) < 0)
298                {
299                    // Add so we don't reprocess
300                    sourceFiles.add(foundSourceFile);
301
302                    // If it has a source map, we try and look up the current location in it's
303                    // source map
304                    if (_canFollowSourceMap(foundSourceFile, type))
305                    {
306                        auto foundSourceMap = foundSourceFile->getSourceMap();
307
308                        const auto foundEntryIndex =
309                            foundSourceMap->get().findEntry(entry.sourceLine, entry.sourceColumn);
310
311                        // If we found the entry repeat the lookup
312                        if (foundEntryIndex >= 0)
313                        {
314                            sourceFile = foundSourceFile;
315                            entryIndex = foundEntryIndex;
316                            continue;
317                        }
318                    }
319                }
320            }
321        }
322    } while (false);
323
324    // Generate the HandleSourceLoc
325    auto sourceMap = sourceFile->getSourceMap()->getPtr();
326    const auto& entry = sourceMap->getEntryByIndex(entryIndex);
327
328    // We need to add the pool of the originating source view/file
329    const auto originatingSourceManager = sourceView->getSourceManager();
330
331    auto& managerPool = originatingSourceManager->getStringSlicePool();
332
333    outLoc.line = entry.sourceLine + 1;
334    outLoc.column = entry.sourceColumn + 1;
335    outLoc.pathHandle = managerPool.add(sourceMap->getSourceFileName(entry.sourceFileIndex));
336
337    return SLANG_OK;
338}
339
340
341SlangResult SourceView::_findSourceMapLoc(
342    SourceLoc loc,
343    SourceLocType type,
344    HandleSourceLoc& outLoc)
345{
346    // We only do source map lookups with nominal
347    if (!_isNominalLike(type))
348    {
349        return SLANG_E_NOT_FOUND;
350    }
351
352    // TODO(JS):
353    // Ideally we'd do the lookup on the "current" source manager rather than the source manager on
354    // this view, which may be a parent to the current one.
355    auto lookupSourceManager = m_sourceFile->getSourceManager();
356
357    SLANG_RETURN_ON_FAIL(_findLocWithSourceMap(lookupSourceManager, this, loc, type, outLoc));
358
359    return SLANG_OK;
360}
361
362HandleSourceLoc SourceView::getHandleLoc(SourceLoc loc, SourceLocType type)
363{
364    {
365        HandleSourceLoc handleLoc;
366        if (SLANG_SUCCEEDED(_findSourceMapLoc(loc, type, handleLoc)))
367        {
368            return handleLoc;
369        }
370    }
371
372    // Get the offset in bytes for this loc
373    const int offset = m_range.getOffset(loc);
374
375    // We need the line index from the original source file
376    const int lineIndex = m_sourceFile->calcLineIndexFromOffset(offset);
377
378    // TODO:
379    // - Tab characters, which should really adjust how we report
380    //   columns (although how are we supposed to know the setting
381    //   that an IDE expects us to use when reporting locations?)
382    //
383    //   For now we just count tabs as single chars
384    const int columnIndex = m_sourceFile->calcColumnIndex(lineIndex, offset);
385
386    HandleSourceLoc handleLoc;
387    handleLoc.column = columnIndex + 1;
388    handleLoc.line = lineIndex + 1;
389
390    // Only bother looking up the entry information if we want a 'Norminal'-like lookup
391    if (_isNominalLike(type))
392    {
393        const int entryIndex = findEntryIndex(loc);
394        if (entryIndex >= 0)
395        {
396            const Entry& entry = m_entries[entryIndex];
397            // Adjust the line
398            handleLoc.line += entry.m_lineAdjust;
399            // Get the pathHandle..
400            handleLoc.pathHandle = entry.m_pathHandle;
401        }
402    }
403
404    return handleLoc;
405}
406
407HumaneSourceLoc SourceView::getHumaneLoc(SourceLoc loc, SourceLocType type)
408{
409    HandleSourceLoc handleLoc = getHandleLoc(loc, type);
410
411    HumaneSourceLoc humaneLoc;
412    humaneLoc.column = handleLoc.column;
413    humaneLoc.line = handleLoc.line;
414    humaneLoc.pathInfo = _getPathInfoFromHandle(handleLoc.pathHandle);
415    return humaneLoc;
416}
417
418PathInfo SourceView::getViewPathInfo() const
419{
420    if (m_viewPath.getLength())
421    {
422        PathInfo pathInfo(m_sourceFile->getPathInfo());
423        pathInfo.foundPath = m_viewPath;
424        return pathInfo;
425    }
426    else
427    {
428        return m_sourceFile->getPathInfo();
429    }
430}
431
432PathInfo SourceView::_getPathInfoFromHandle(StringSlicePool::Handle pathHandle) const
433{
434    // If there is no override path, then just the source files path
435    if (pathHandle == StringSlicePool::Handle(0))
436    {
437        return getViewPathInfo();
438    }
439    else
440    {
441        return PathInfo::makePath(getSourceManager()->getStringSlicePool().getSlice(pathHandle));
442    }
443}
444
445PathInfo SourceView::getPathInfo(SourceLoc loc, SourceLocType type)
446{
447    if (type == SourceLocType::Actual)
448    {
449        return getViewPathInfo();
450    }
451
452    {
453        HandleSourceLoc handleLoc;
454        if (SLANG_SUCCEEDED(_findSourceMapLoc(loc, type, handleLoc)))
455        {
456            return _getPathInfoFromHandle(handleLoc.pathHandle);
457        }
458    }
459
460    const int entryIndex = findEntryIndex(loc);
461    return _getPathInfoFromHandle(
462        (entryIndex >= 0) ? m_entries[entryIndex].m_pathHandle : StringSlicePool::Handle(0));
463}
464
465/* !!!!!!!!!!!!!!!!!!!!!!! SourceFile !!!!!!!!!!!!!!!!!!!!!!!!!!!! */
466
467void SourceFile::setLineBreakOffsets(const uint32_t* offsets, UInt numOffsets)
468{
469    m_lineBreakOffsets.clear();
470    m_lineBreakOffsets.addRange(offsets, numOffsets);
471}
472
473const List<uint32_t>& SourceFile::getLineBreakOffsets()
474{
475    // We now have a raw input file that we can search for line breaks.
476    // We obviously don't want to do a linear scan over and over, so we will
477    // cache an array of line break locations in the file.
478    if (m_lineBreakOffsets.getCount() == 0)
479    {
480        UnownedStringSlice content(getContent()), line;
481        char const* contentBegin = content.begin();
482        while (StringUtil::extractLine(content, line))
483        {
484            m_lineBreakOffsets.add(uint32_t(line.begin() - contentBegin));
485        }
486        // Note that we do *not* treat the end of the file as a line
487        // break, because otherwise we would report errors like
488        // "end of file inside string literal" with a line number
489        // that points at a line that doesn't exist.
490    }
491
492    return m_lineBreakOffsets;
493}
494
495SourceFile::OffsetRange SourceFile::getOffsetRangeAtLineIndex(Index lineIndex)
496{
497    const List<uint32_t>& offsets = getLineBreakOffsets();
498    const Index count = offsets.getCount();
499
500    if (lineIndex >= count - 1)
501    {
502        // Work out the line start
503        const uint32_t offsetEnd = uint32_t(getContentSize());
504        const uint32_t offsetStart = (lineIndex >= count) ? offsetEnd : offsets[lineIndex];
505        // The line is the span from start, to the end of the content
506        return OffsetRange{offsetStart, offsetEnd};
507    }
508    else
509    {
510        const uint32_t offsetStart = offsets[lineIndex];
511        const uint32_t offsetEnd = offsets[lineIndex + 1];
512        return OffsetRange{offsetStart, offsetEnd};
513    }
514}
515
516UnownedStringSlice SourceFile::getLineAtIndex(Index lineIndex)
517{
518    const OffsetRange range = getOffsetRangeAtLineIndex(lineIndex);
519
520    if (range.isValid() && hasContent())
521    {
522        const UnownedStringSlice content = getContent();
523        SLANG_ASSERT(range.end <= uint32_t(content.getLength()));
524
525        const char* const text = content.begin();
526        return UnownedStringSlice(text + range.start, text + range.end);
527    }
528
529    return UnownedStringSlice();
530}
531
532UnownedStringSlice SourceFile::getLineContainingOffset(uint32_t offset)
533{
534    const Index lineIndex = calcLineIndexFromOffset(offset);
535    return getLineAtIndex(lineIndex);
536}
537
538bool SourceFile::isOffsetOnLine(uint32_t offset, Index lineIndex)
539{
540    const OffsetRange range = getOffsetRangeAtLineIndex(lineIndex);
541    return range.isValid() && range.containsInclusive(offset);
542}
543
544int SourceFile::calcLineIndexFromOffset(int offset)
545{
546    SLANG_ASSERT(UInt(offset) <= getContentSize());
547
548    // Make sure we have the line break offsets
549    const auto& lineBreakOffsets = getLineBreakOffsets();
550
551    // At this point we can assume the `lineBreakOffsets` array has been filled in.
552    // We will use a binary search to find the line index that contains our
553    // chosen offset.
554    Index lo = 0;
555    Index hi = lineBreakOffsets.getCount();
556
557    while (lo + 1 < hi)
558    {
559        const Index mid = (hi + lo) >> 1;
560        const uint32_t midOffset = lineBreakOffsets[mid];
561        if (midOffset <= uint32_t(offset))
562        {
563            lo = mid;
564        }
565        else
566        {
567            hi = mid;
568        }
569    }
570
571    return int(lo);
572}
573
574int SourceFile::calcColumnOffset(int lineIndex, int offset)
575{
576    const auto& lineBreakOffsets = getLineBreakOffsets();
577    return offset - lineBreakOffsets[lineIndex];
578}
579
580int SourceFile::calcColumnIndex(int lineIndex, int offset, int tabSize)
581{
582    const int colOffset = calcColumnOffset(lineIndex, offset);
583
584    // If we don't have the content of the file, the best we can do is to assume there is a char per
585    // column
586    if (!hasContent())
587    {
588        return colOffset;
589    }
590
591    const auto line = getLineAtIndex(lineIndex);
592
593    const auto head = line.head(colOffset);
594
595    auto colCount = UTF8Util::calcCodePointCount(head);
596
597    if (tabSize >= 0)
598    {
599        Count tabCount = 0;
600        for (auto c : head)
601        {
602            tabCount += Count(c == '\t');
603        }
604
605        // We substract one from tabSize, because colCount will already holds a +1 for each tab.
606        colCount += tabCount * (tabSize - 1);
607    }
608
609    return int(colCount);
610}
611
612/* !!!!!!!!!!!!!!!!!!!!!!!!! SourceFile !!!!!!!!!!!!!!!!!!!!!!!!!!!! */
613
614void SourceFile::setContents(ISlangBlob* blob)
615{
616    const UInt rawContentSize = blob->getBufferSize();
617
618    SLANG_ASSERT(rawContentSize == m_contentSize);
619
620    Byte* rawContentBegin = (Byte*)blob->getBufferPointer();
621
622    // Query the encoding type and discard the Unicode Byte-Order-Marker before decoding
623    size_t offset;
624    auto type = CharEncoding::determineEncoding(rawContentBegin, rawContentSize, offset);
625    SLANG_ASSERT(rawContentSize >= offset);
626
627    List<char> decodedBuffer;
628    CharEncoding::getEncoding(type)->decode(
629        rawContentBegin + offset,
630        int(rawContentSize - offset),
631        decodedBuffer);
632
633    m_contentBlob = RawBlob::create(decodedBuffer.getBuffer(), decodedBuffer.getCount());
634
635    char const* decodedContentBegin = (char const*)m_contentBlob->getBufferPointer();
636    const UInt decodedContentSize = m_contentBlob->getBufferSize();
637    char const* decodedContentEnd = decodedContentBegin + decodedContentSize;
638
639    m_content = UnownedStringSlice(decodedContentBegin, decodedContentEnd);
640}
641
642void SourceFile::setContents(const String& content)
643{
644    ComPtr<ISlangBlob> contentBlob = StringUtil::createStringBlob(content);
645    setContents(contentBlob);
646}
647
648SourceFile::SourceFile(SourceManager* sourceManager, const PathInfo& pathInfo, size_t contentSize)
649    : m_sourceManager(sourceManager), m_pathInfo(pathInfo), m_contentSize(contentSize)
650{
651}
652
653SourceFile::~SourceFile() {}
654
655SHA1::Digest SourceFile::getDigest()
656{
657    if (m_digest == SHA1::Digest())
658    {
659        DigestBuilder<SHA1> builder;
660        builder.append(getContent());
661        m_digest = builder.finalize();
662    }
663    return m_digest;
664}
665
666String SourceFile::calcVerbosePath() const
667{
668    ISlangFileSystemExt* fileSystemExt = getSourceManager()->getFileSystemExt();
669
670    if (fileSystemExt)
671    {
672        String displayPath;
673        ComPtr<ISlangBlob> displayPathBlob;
674        if (SLANG_SUCCEEDED(fileSystemExt->getPath(
675                PathKind::Display,
676                m_pathInfo.foundPath.getBuffer(),
677                displayPathBlob.writeRef())))
678        {
679            displayPath = StringUtil::getString(displayPathBlob);
680        }
681        if (displayPath.getLength() > 0)
682        {
683            return displayPath;
684        }
685    }
686
687    return m_pathInfo.foundPath;
688}
689
690/* !!!!!!!!!!!!!!!!!!!!!!!!! SourceManager !!!!!!!!!!!!!!!!!!!!!!!!!!!! */
691
692void SourceManager::initialize(SourceManager* p, ISlangFileSystemExt* fileSystemExt)
693{
694    m_fileSystemExt = fileSystemExt;
695
696    m_parent = p;
697
698    _resetLoc();
699}
700
701SourceManager::~SourceManager()
702{
703    _resetSource();
704}
705
706void SourceManager::_resetLoc()
707{
708    if (m_parent)
709    {
710        // If we have a parent source manager, then we assume that all code at that level
711        // has already been loaded, and it is safe to start our own source locations
712        // right after those from the parent.
713        //
714        // TODO: more clever allocation in cases where that might not be reasonable
715        m_startLoc = m_parent->m_nextLoc;
716    }
717    else
718    {
719        // Location zero is reserved for an invalid location,
720        // so we need to start reserving locations starting at 1.
721        m_startLoc = SourceLoc::fromRaw(1);
722    }
723
724    m_nextLoc = m_startLoc;
725}
726
727void SourceManager::_resetSource()
728{
729    for (auto item : m_sourceViews)
730    {
731        delete item;
732    }
733
734    for (auto item : m_sourceFiles)
735    {
736        delete item;
737    }
738
739    m_sourceViews.clear();
740    m_sourceFiles.clear();
741
742    m_sourceFileMap.clear();
743}
744
745
746void SourceManager::reset()
747{
748    _resetSource();
749    _resetLoc();
750}
751
752UnownedStringSlice SourceManager::allocateStringSlice(const UnownedStringSlice& slice)
753{
754    const UInt numChars = slice.getLength();
755
756    char* dst = (char*)m_memoryArena.allocate(numChars);
757    ::memcpy(dst, slice.begin(), numChars);
758
759    return UnownedStringSlice(dst, numChars);
760}
761
762SourceRange SourceManager::allocateSourceRange(UInt size)
763{
764    // TODO: consider using atomics here
765
766
767    SourceLoc beginLoc = m_nextLoc;
768    SourceLoc endLoc = beginLoc + size;
769
770    // We need to be able to represent the location that is *at* the end of
771    // the input source, so the next available location for a new file
772    // must be placed one after the end of this one.
773
774    m_nextLoc = endLoc + 1;
775
776    return SourceRange(beginLoc, endLoc);
777}
778
779SourceFile* SourceManager::createSourceFileWithSize(const PathInfo& pathInfo, size_t contentSize)
780{
781    SourceFile* sourceFile = new SourceFile(this, pathInfo, contentSize);
782    m_sourceFiles.add(sourceFile);
783    return sourceFile;
784}
785
786SourceFile* SourceManager::createSourceFileWithString(
787    const PathInfo& pathInfo,
788    const String& contents)
789{
790    SourceFile* sourceFile = new SourceFile(this, pathInfo, contents.getLength());
791    m_sourceFiles.add(sourceFile);
792    sourceFile->setContents(contents);
793    return sourceFile;
794}
795
796SourceFile* SourceManager::createSourceFileWithBlob(const PathInfo& pathInfo, ISlangBlob* blob)
797{
798    SourceFile* sourceFile = new SourceFile(this, pathInfo, blob->getBufferSize());
799    m_sourceFiles.add(sourceFile);
800    sourceFile->setContents(blob);
801    return sourceFile;
802}
803
804SourceView* SourceManager::createSourceView(
805    SourceFile* sourceFile,
806    const PathInfo* pathInfo,
807    SourceLoc initiatingSourceLoc)
808{
809    SourceRange range = allocateSourceRange(sourceFile->getContentSize());
810
811    SourceView* sourceView = nullptr;
812    if (pathInfo && (pathInfo->foundPath.getLength() &&
813                     sourceFile->getPathInfo().foundPath != pathInfo->foundPath))
814    {
815        sourceView = new SourceView(sourceFile, range, &pathInfo->foundPath, initiatingSourceLoc);
816    }
817    else
818    {
819        sourceView = new SourceView(sourceFile, range, nullptr, initiatingSourceLoc);
820    }
821
822    m_sourceViews.add(sourceView);
823
824    return sourceView;
825}
826
827SourceView* SourceManager::findSourceView(SourceLoc loc) const
828{
829    Index hi = m_sourceViews.getCount();
830    // It must be in the range of this manager and have associated views for it to possibly be a hit
831    if (!getSourceRange().contains(loc) || hi == 0)
832    {
833        return nullptr;
834    }
835
836    // If we don't have very many, we may as well just linearly search
837    if (hi <= 8)
838    {
839        for (int i = 0; i < hi; ++i)
840        {
841            SourceView* view = m_sourceViews[i];
842            if (view->getRange().contains(loc))
843            {
844                return view;
845            }
846        }
847        return nullptr;
848    }
849
850    const SourceLoc::RawValue rawLoc = loc.getRaw();
851
852    // Binary chop to see if we can find the associated SourceUnit
853    Index lo = 0;
854    while (lo + 1 < hi)
855    {
856        Index mid = (hi + lo) >> 1;
857
858        SourceView* midView = m_sourceViews[mid];
859        if (midView->getRange().contains(loc))
860        {
861            return midView;
862        }
863
864        const SourceLoc::RawValue midValue = midView->getRange().begin.getRaw();
865        if (midValue <= rawLoc)
866        {
867            // The location we seek is at or after this entry
868            lo = mid;
869        }
870        else
871        {
872            // The location we seek is before this entry
873            hi = mid;
874        }
875    }
876
877    // Check if low is actually a hit
878    SourceView* view = m_sourceViews[lo];
879    return (view->getRange().contains(loc)) ? view : nullptr;
880}
881
882SourceView* SourceManager::findSourceViewRecursively(SourceLoc loc) const
883{
884    // Start with this manager
885    const SourceManager* manager = this;
886    do
887    {
888        SourceView* sourceView = manager->findSourceView(loc);
889        // If we found a hit we are done
890        if (sourceView)
891        {
892            return sourceView;
893        }
894        // Try the parent
895        manager = manager->m_parent;
896    } while (manager);
897    // Didn't find it
898    return nullptr;
899}
900
901SourceFile* SourceManager::findSourceFileByPathRecursively(const String& name) const
902{
903    // Start with this manager
904    const SourceManager* manager = this;
905    do
906    {
907        SourceFile* sourceFile = manager->findSourceFileByPath(name);
908        // If we found a hit we are done
909        if (sourceFile)
910        {
911            return sourceFile;
912        }
913        // Try the parent
914        manager = manager->m_parent;
915    } while (manager);
916    // Didn't find it
917    return nullptr;
918}
919
920SourceFile* SourceManager::findSourceFileByPath(const String& name) const
921{
922    for (auto sourceFile : m_sourceFiles)
923    {
924        if (sourceFile->getPathInfo().foundPath == name)
925        {
926            return sourceFile;
927        }
928    }
929    return nullptr;
930}
931
932SourceFile* SourceManager::findSourceFile(const String& uniqueIdentity) const
933{
934    SourceFile* const* filePtr = m_sourceFileMap.tryGetValue(uniqueIdentity);
935    return (filePtr) ? *filePtr : nullptr;
936}
937
938SourceFile* SourceManager::findSourceFileRecursively(const String& uniqueIdentity) const
939{
940    const SourceManager* manager = this;
941    do
942    {
943        SourceFile* sourceFile = manager->findSourceFile(uniqueIdentity);
944        if (sourceFile)
945        {
946            return sourceFile;
947        }
948        manager = manager->m_parent;
949    } while (manager);
950    return nullptr;
951}
952
953SourceFile* SourceManager::findSourceFileByContentRecursively(const char* text)
954{
955    const SourceManager* manager = this;
956    do
957    {
958        SourceFile* sourceFile = manager->findSourceFileByContent(text);
959        if (sourceFile)
960        {
961            return sourceFile;
962        }
963        manager = manager->m_parent;
964    } while (manager);
965    return nullptr;
966}
967
968SourceFile* SourceManager::findSourceFileByContent(const char* text) const
969{
970    for (SourceFile* sourceFile : getSourceFiles())
971    {
972        auto content = sourceFile->getContent();
973
974        if (text >= content.begin() && text <= content.end())
975        {
976            return sourceFile;
977        }
978    }
979    return nullptr;
980}
981
982void SourceManager::addSourceFile(const String& uniqueIdentity, SourceFile* sourceFile)
983{
984    SLANG_ASSERT(!findSourceFileRecursively(uniqueIdentity));
985    m_sourceFileMap.add(uniqueIdentity, sourceFile);
986}
987
988void SourceManager::addSourceFileIfNotExist(const String& uniqueIdentity, SourceFile* sourceFile)
989{
990    if (findSourceFileRecursively(uniqueIdentity))
991        return;
992    m_sourceFileMap.addIfNotExists(uniqueIdentity, sourceFile);
993}
994
995HumaneSourceLoc SourceManager::getHumaneLoc(SourceLoc loc, SourceLocType type)
996{
997    SourceView* sourceView = findSourceViewRecursively(loc);
998    if (sourceView)
999    {
1000        return sourceView->getHumaneLoc(loc, type);
1001    }
1002    else
1003    {
1004        return HumaneSourceLoc();
1005    }
1006}
1007
1008PathInfo SourceManager::getPathInfo(SourceLoc loc, SourceLocType type)
1009{
1010    SourceView* sourceView = findSourceViewRecursively(loc);
1011    if (sourceView)
1012    {
1013        return sourceView->getPathInfo(loc, type);
1014    }
1015    else
1016    {
1017        return PathInfo::makeUnknown();
1018    }
1019}
1020
1021SourceLoc::RawValue SourceView::getAbsoluteLocation(SourceLoc location) const
1022{
1023    AbsoluteSegment segment;
1024    if (m_absSegments.getCount())
1025    {
1026        if (m_absSegments.getFirst().begin > location)
1027        {
1028            segment.begin = m_range.begin;
1029            segment.absoluteBegin = m_absoluteLocationBase;
1030        }
1031        else
1032        {
1033            auto it = std::upper_bound(
1034                          m_absSegments.begin(),
1035                          m_absSegments.end(),
1036                          location,
1037                          [](SourceLoc const& loc, AbsoluteSegment const& seg)
1038                          { return loc < seg.begin; }) -
1039                      1;
1040            segment = *it;
1041        }
1042    }
1043    else
1044    {
1045        segment = getLastSegment();
1046    }
1047    auto offset = SourceRange(segment.begin, location).getSize();
1048    return segment.absoluteBegin + offset;
1049}
1050
1051SourceLoc::RawValue SourceManager::getAbsoluteLocation(SourceLoc location) const
1052{
1053    SourceLoc::RawValue res = 0;
1054    if (const SourceView* view = findSourceView(location))
1055    {
1056        res = view->getAbsoluteLocation(location);
1057    }
1058    return res;
1059}
1060
1061} // namespace Slang