yum-mirror/slang

Making it easier to work with shaders

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

Theresa FoleyAdd support for on-demand AST deserialization (#7482)3ed776159

master
18.6 KiB801 linesraw
1#include "slang-string.h"
2
3#include "slang-char-util.h"
4#include "slang-text-io.h"
5
6namespace Slang
7{
8// HACK!
9// JS: Many of the inlined functions of CharUtil just access a global map. That referencing this
10// global is *NOT* enough to link correctly with CharUtil on linux for a shared library. The
11// following call exists to try and force linkage of CharUtil for anything that uses core
12static const auto s_charUtilLink = CharUtil::_ensureLink();
13
14
15// StringRepresentation
16
17void StringRepresentation::setContents(const UnownedStringSlice& slice)
18{
19    const auto sliceLength = slice.getLength();
20    SLANG_ASSERT(sliceLength <= capacity);
21
22    char* chars = getData();
23
24    // Use move (rather than memcpy), because the slice *could* be contained in the
25    // StringRepresentation
26    ::memmove(chars, slice.begin(), sliceLength * sizeof(char));
27    // Zero terminate.
28    chars[sliceLength] = 0;
29    // Set the length
30    length = sliceLength;
31}
32
33
34/* static */ StringRepresentation* StringRepresentation::create(const UnownedStringSlice& slice)
35{
36    const auto sliceLength = slice.getLength();
37
38    if (sliceLength)
39    {
40        StringRepresentation* rep = StringRepresentation::createWithLength(sliceLength);
41
42        char* chars = rep->getData();
43        ::memcpy(chars, slice.begin(), sizeof(char) * sliceLength);
44        chars[sliceLength] = 0;
45
46        return rep;
47    }
48    else
49    {
50        return nullptr;
51    }
52}
53
54/* static */ StringRepresentation* StringRepresentation::createWithReference(
55    const UnownedStringSlice& slice)
56{
57    const auto sliceLength = slice.getLength();
58
59    if (sliceLength)
60    {
61        StringRepresentation* rep = StringRepresentation::createWithLength(sliceLength);
62        rep->addReference();
63
64        char* chars = rep->getData();
65        ::memcpy(chars, slice.begin(), sizeof(char) * sliceLength);
66        chars[sliceLength] = 0;
67
68        return rep;
69    }
70    else
71    {
72        return nullptr;
73    }
74}
75
76// OSString
77
78OSString::OSString()
79    : m_begin(nullptr), m_end(nullptr)
80{
81}
82
83OSString::OSString(wchar_t* begin, wchar_t* end)
84    : m_begin(begin), m_end(end)
85{
86}
87
88void OSString::_releaseBuffer()
89{
90    if (m_begin)
91    {
92        delete[] m_begin;
93    }
94}
95
96void OSString::set(const wchar_t* begin, const wchar_t* end)
97{
98    if (m_begin)
99    {
100        delete[] m_begin;
101        m_begin = nullptr;
102        m_end = nullptr;
103    }
104    const size_t len = end - begin;
105    if (len > 0)
106    {
107        // TODO(JS): The allocation is only done this way to be compatible with the buffer being
108        // detached from an array This is unfortunate, because it means that the allocation stores
109        // the size (and alignment fix), which is a shame because we know the size
110        m_begin = new wchar_t[len + 1];
111        memcpy(m_begin, begin, len * sizeof(wchar_t));
112        // Zero terminate
113        m_begin[len] = 0;
114        m_end = m_begin + len;
115    }
116}
117
118static const wchar_t kEmptyOSString[] = {0};
119
120wchar_t const* OSString::begin() const
121{
122    return m_begin ? m_begin : kEmptyOSString;
123}
124
125wchar_t const* OSString::end() const
126{
127    return m_end ? m_end : kEmptyOSString;
128}
129
130// UnownedStringSlice
131
132bool UnownedStringSlice::startsWith(UnownedStringSlice const& other) const
133{
134    UInt thisSize = getLength();
135    UInt otherSize = other.getLength();
136
137    if (otherSize > thisSize)
138        return false;
139
140    return head(otherSize) == other;
141}
142
143bool UnownedStringSlice::startsWith(char const* str) const
144{
145    return startsWith(UnownedTerminatedStringSlice(str));
146}
147
148bool UnownedStringSlice::startsWithCaseInsensitive(UnownedStringSlice const& other) const
149{
150    UInt thisSize = getLength();
151    UInt otherSize = other.getLength();
152
153    if (otherSize > thisSize)
154        return false;
155
156    return head(otherSize).caseInsensitiveEquals(other);
157}
158
159
160bool UnownedStringSlice::endsWith(UnownedStringSlice const& other) const
161{
162    UInt thisSize = getLength();
163    UInt otherSize = other.getLength();
164
165    if (otherSize > thisSize)
166        return false;
167
168    return UnownedStringSlice(end() - otherSize, end()) == other;
169}
170
171bool UnownedStringSlice::endsWithCaseInsensitive(UnownedStringSlice const& other) const
172{
173    UInt thisSize = getLength();
174    UInt otherSize = other.getLength();
175
176    if (otherSize > thisSize)
177        return false;
178
179    return UnownedStringSlice(end() - otherSize, end()).caseInsensitiveEquals(other);
180}
181
182bool UnownedStringSlice::endsWith(char const* str) const
183{
184    return endsWith(UnownedTerminatedStringSlice(str));
185}
186
187bool UnownedStringSlice::endsWithCaseInsensitive(char const* str) const
188{
189    return endsWithCaseInsensitive(UnownedTerminatedStringSlice(str));
190}
191
192UnownedStringSlice UnownedStringSlice::trim() const
193{
194    const char* start = m_begin;
195    const char* end = m_end;
196
197    while (start < end && CharUtil::isHorizontalWhitespace(*start))
198        start++;
199    while (end > start && CharUtil::isHorizontalWhitespace(end[-1]))
200        end--;
201    return UnownedStringSlice(start, end);
202}
203
204UnownedStringSlice UnownedStringSlice::trimStart() const
205{
206    const char* start = m_begin;
207
208    while (start < m_end && CharUtil::isHorizontalWhitespace(*start))
209        start++;
210    return UnownedStringSlice(start, m_end);
211}
212
213UnownedStringSlice UnownedStringSlice::trim(char c) const
214{
215    const char* start = m_begin;
216    const char* end = m_end;
217
218    while (start < end && *start == c)
219        start++;
220    while (end > start && end[-1] == c)
221        end--;
222    return UnownedStringSlice(start, end);
223}
224
225// StringSlice
226
227StringSlice::StringSlice()
228    : representation(0), beginIndex(0), endIndex(0)
229{
230}
231
232StringSlice::StringSlice(String const& str)
233    : representation(str.m_buffer), beginIndex(0), endIndex(str.getLength())
234{
235}
236
237StringSlice::StringSlice(String const& str, UInt beginIndex, UInt endIndex)
238    : representation(str.m_buffer), beginIndex(beginIndex), endIndex(endIndex)
239{
240}
241
242
243//
244
245_EndLine EndLine;
246
247String operator+(const char* op1, const String& op2)
248{
249    String result(op1);
250    result.append(op2);
251    return result;
252}
253
254String operator+(const String& op1, const char* op2)
255{
256    String result(op1);
257    result.append(op2);
258    return result;
259}
260
261String operator+(const String& op1, const String& op2)
262{
263    String result(op1);
264    result.append(op2);
265    return result;
266}
267
268int stringToInt(const String& str, int radix)
269{
270    if (str.startsWith("0x"))
271        return (int)strtoll(str.getBuffer(), NULL, 16);
272    else
273        return (int)strtoll(str.getBuffer(), NULL, radix);
274}
275unsigned int stringToUInt(const String& str, int radix)
276{
277    if (str.startsWith("0x"))
278        return (unsigned int)strtoull(str.getBuffer(), NULL, 16);
279    else
280        return (unsigned int)strtoull(str.getBuffer(), NULL, radix);
281}
282double stringToDouble(const String& str)
283{
284    return (double)strtod(str.getBuffer(), NULL);
285}
286float stringToFloat(const String& str)
287{
288    return strtof(str.getBuffer(), NULL);
289}
290
291#if 0
292    String String::ReplaceAll(String src, String dst) const
293    {
294        String rs = *this;
295        int index = 0;
296        int srcLen = src.length;
297        int len = rs.length;
298        while ((index = rs.IndexOf(src, index)) != -1)
299        {
300            rs = rs.SubString(0, index) + dst + rs.SubString(index + srcLen, len - index - srcLen);
301            len = rs.length;
302        }
303        return rs;
304    }
305#endif
306
307String String::fromWString(const wchar_t* wstr)
308{
309    List<char> buf;
310#ifdef _WIN32
311    Slang::CharEncoding::UTF16->decode(
312        (const Byte*)wstr,
313        (int)(wcslen(wstr) * sizeof(wchar_t)),
314        buf);
315#else
316    Slang::CharEncoding::UTF32->decode(
317        (const Byte*)wstr,
318        (int)(wcslen(wstr) * sizeof(wchar_t)),
319        buf);
320#endif
321    return String(buf.begin(), buf.end());
322}
323
324String String::fromWString(const wchar_t* wstr, const wchar_t* wend)
325{
326    List<char> buf;
327#ifdef _WIN32
328    Slang::CharEncoding::UTF16->decode(
329        (const Byte*)wstr,
330        (int)((wend - wstr) * sizeof(wchar_t)),
331        buf);
332#else
333    Slang::CharEncoding::UTF32->decode(
334        (const Byte*)wstr,
335        (int)((wend - wstr) * sizeof(wchar_t)),
336        buf);
337#endif
338    return String(buf.begin(), buf.end());
339}
340
341String String::fromWChar(const wchar_t ch)
342{
343    List<char> buf;
344#ifdef _WIN32
345    Slang::CharEncoding::UTF16->decode((const Byte*)&ch, (int)(sizeof(wchar_t)), buf);
346#else
347    Slang::CharEncoding::UTF32->decode((const Byte*)&ch, (int)(sizeof(wchar_t)), buf);
348#endif
349    return String(buf.begin(), buf.end());
350}
351
352/* static */ String String::fromUnicodePoint(Char32 codePoint)
353{
354    char buf[6];
355    int len = Slang::encodeUnicodePointToUTF8(codePoint, buf);
356    return String(buf, buf + len);
357}
358
359OSString String::toWString(Index* outLength) const
360{
361    if (!m_buffer)
362    {
363        return OSString();
364    }
365    else
366    {
367        List<Byte> buf;
368        switch (sizeof(wchar_t))
369        {
370        case 2:
371            Slang::CharEncoding::UTF16->encode(getUnownedSlice(), buf);
372            break;
373
374        case 4:
375            Slang::CharEncoding::UTF32->encode(getUnownedSlice(), buf);
376            break;
377
378        default:
379            break;
380        }
381
382        auto length = Index(buf.getCount() / sizeof(wchar_t));
383        if (outLength)
384            *outLength = length;
385
386        for (size_t ii = 0; ii < sizeof(wchar_t); ++ii)
387            buf.add(0);
388
389        wchar_t* beginData = (wchar_t*)buf.getBuffer();
390        wchar_t* endData = beginData + length;
391
392        OSString ret;
393        ret.set(beginData, endData);
394        return ret;
395    }
396}
397
398//
399
400void String::ensureUniqueStorageWithCapacity(Index requiredCapacity)
401{
402    if (m_buffer && m_buffer->isUniquelyReferenced() && m_buffer->capacity >= requiredCapacity)
403        return;
404
405    Index newCapacity = m_buffer ? 2 * m_buffer->capacity : 16;
406    if (newCapacity < requiredCapacity)
407    {
408        newCapacity = requiredCapacity;
409    }
410
411    Index length = getLength();
412    StringRepresentation* newRepresentation =
413        StringRepresentation::createWithCapacityAndLength(newCapacity, length);
414
415    if (m_buffer)
416    {
417        memcpy(newRepresentation->getData(), m_buffer->getData(), length + 1);
418    }
419
420    m_buffer = newRepresentation;
421}
422
423char* String::prepareForAppend(Index count)
424{
425    auto oldLength = getLength();
426    auto newLength = oldLength + count;
427    ensureUniqueStorageWithCapacity(newLength);
428    return getData() + oldLength;
429}
430void String::appendInPlace(const char* chars, Index count)
431{
432    SLANG_UNUSED(chars);
433
434    if (count > 0)
435    {
436        SLANG_ASSERT(m_buffer && m_buffer->isUniquelyReferenced());
437
438        auto oldLength = getLength();
439        auto newLength = oldLength + count;
440
441        char* dst = m_buffer->getData();
442
443        // Make sure the input buffer is the same one returned from prepareForAppend
444        SLANG_ASSERT(chars == dst + oldLength);
445        // It has to fit within the capacity
446        SLANG_ASSERT(newLength <= m_buffer->capacity);
447
448        // We just need to modify the length
449        m_buffer->length = newLength;
450
451        // And mark with a terminating 0
452        dst[newLength] = 0;
453    }
454}
455
456void String::reduceLength(Index newLength)
457{
458    Index oldLength = getLength();
459    SLANG_ASSERT(newLength <= oldLength);
460    if (oldLength == newLength)
461    {
462        return;
463    }
464
465    // It must have a buffer, because only 0 length allows for nullptr
466    // and being 0 sized is already covered
467    SLANG_ASSERT(m_buffer);
468
469    if (m_buffer->isUniquelyReferenced())
470    {
471        m_buffer->length = newLength;
472        m_buffer->getData()[newLength] = 0;
473    }
474    else
475    {
476        // If 0 length is wanted we can just free
477        if (newLength == 0)
478        {
479            m_buffer.setNull();
480        }
481        else
482        {
483            // We need to make a new copy, that we will shrink
484
485            // We'll just go with capacity enough for the new length
486            const Index newCapacity = newLength;
487            StringRepresentation* newRepresentation =
488                StringRepresentation::createWithCapacityAndLength(newCapacity, newLength);
489
490            // Copy
491            char* dst = newRepresentation->getData();
492            memcpy(dst, m_buffer->getData(), sizeof(char) * newLength);
493            // Zero terminate
494            dst[newLength] = 0;
495
496            // Set the new rep
497            m_buffer = newRepresentation;
498        }
499    }
500}
501
502void String::append(char const* str, size_t len)
503{
504    append(str, str + len);
505}
506
507void String::append(const char* textBegin, char const* textEnd)
508{
509    auto oldLength = getLength();
510    auto textLength = textEnd - textBegin;
511    if (textLength <= 0)
512        return;
513
514    auto newLength = oldLength + textLength;
515
516    ensureUniqueStorageWithCapacity(newLength);
517
518    memcpy(getData() + oldLength, textBegin, textLength);
519    getData()[newLength] = 0;
520    m_buffer->length = newLength;
521}
522
523void String::append(char const* str)
524{
525    if (str)
526    {
527        append(str, str + strlen(str));
528    }
529}
530
531void String::appendRepeatedChar(char chr, Index count)
532{
533    SLANG_ASSERT(count >= 0);
534    if (count > 0)
535    {
536        char* chars = prepareForAppend(count);
537        // Set all space to repeated chr.
538        ::memset(chars, chr, sizeof(char) * count);
539        appendInPlace(chars, count);
540    }
541}
542
543void String::appendChar(char c)
544{
545    const auto oldLength = getLength();
546    const auto newLength = oldLength + 1;
547
548    ensureUniqueStorageWithCapacity(newLength);
549
550    // Since there must be space for at least one character, m_buffer cannot be nullptr
551    SLANG_ASSERT(m_buffer);
552    char* data = m_buffer->getData();
553    data[oldLength] = c;
554    data[newLength] = 0;
555
556    m_buffer->length = newLength;
557}
558
559void String::append(char chr)
560{
561    appendChar(chr);
562}
563
564void String::append(String const& str)
565{
566    if (!m_buffer)
567    {
568        m_buffer = str.m_buffer;
569        return;
570    }
571
572    append(str.begin(), str.end());
573}
574
575void String::append(StringSlice const& slice)
576{
577    append(slice.begin(), slice.end());
578}
579
580void String::append(UnownedStringSlice const& slice)
581{
582    append(slice.begin(), slice.end());
583}
584
585void String::append(int32_t value, int radix)
586{
587    enum
588    {
589        kCount = 33
590    };
591    char* data = prepareForAppend(kCount);
592    const auto count = intToAscii(data, value, radix);
593    m_buffer->length += count;
594}
595
596void String::append(uint32_t value, int radix)
597{
598    enum
599    {
600        kCount = 33
601    };
602    char* data = prepareForAppend(kCount);
603    const auto count = intToAscii(data, value, radix);
604    m_buffer->length += count;
605}
606
607void String::append(int64_t value, int radix)
608{
609    enum
610    {
611        kCount = 65
612    };
613    char* data = prepareForAppend(kCount);
614    auto count = intToAscii(data, value, radix);
615    m_buffer->length += count;
616}
617
618void String::append(uint64_t value, int radix)
619{
620    enum
621    {
622        kCount = 65
623    };
624    char* data = prepareForAppend(kCount);
625    auto count = intToAscii(data, value, radix);
626    m_buffer->length += count;
627}
628
629void String::append(float val, const char* format)
630{
631    enum
632    {
633        kCount = 128
634    };
635    char* data = prepareForAppend(kCount);
636    sprintf_s(data, kCount, format, val);
637    m_buffer->length += strnlen_s(data, kCount);
638}
639
640void String::append(double val, const char* format)
641{
642    enum
643    {
644        kCount = 128
645    };
646    char* data = prepareForAppend(kCount);
647    sprintf_s(data, kCount, format, val);
648    m_buffer->length += strnlen_s(data, kCount);
649}
650
651void String::append(StableHashCode32 value)
652{
653    const Index digits = 8;
654    // + null terminator
655    char* data = prepareForAppend(digits + 1);
656    auto count = intToAscii(data, value.hash, 16, digits);
657    m_buffer->length += count;
658}
659
660void String::append(StableHashCode64 value)
661{
662    const Index digits = 16;
663    // + null terminator
664    char* data = prepareForAppend(digits + 1);
665    auto count = intToAscii(data, value.hash, 16, digits);
666    m_buffer->length += count;
667}
668
669// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! UnownedStringSlice !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
670
671Index UnownedStringSlice::indexOf(char c) const
672{
673    const Index size = Index(m_end - m_begin);
674    for (Index i = 0; i < size; ++i)
675    {
676        if (m_begin[i] == c)
677        {
678            return i;
679        }
680    }
681    return -1;
682}
683
684Index UnownedStringSlice::indexOf(const UnownedStringSlice& in) const
685{
686    const Index len = getLength();
687    const Index inLen = in.getLength();
688    if (inLen > len)
689    {
690        return -1;
691    }
692
693    const char* inChars = in.m_begin;
694    switch (inLen)
695    {
696    case 0:
697        return 0;
698    case 1:
699        return indexOf(inChars[0]);
700    default:
701        break;
702    }
703
704    const char* chars = m_begin;
705    const char firstChar = inChars[0];
706
707    for (Int i = 0; i <= len - inLen; ++i)
708    {
709        if (chars[i] == firstChar && in == UnownedStringSlice(chars + i, inLen))
710        {
711            return i;
712        }
713    }
714
715    return -1;
716}
717
718UnownedStringSlice UnownedStringSlice::subString(Index idx, Index len) const
719{
720    const Index totalLen = getLength();
721    SLANG_ASSERT(idx >= 0 && len >= 0 && idx <= totalLen);
722
723    // If too large, we truncate
724    len = (idx + len > totalLen) ? (totalLen - idx) : len;
725
726    // Return the substring
727    return UnownedStringSlice(m_begin + idx, m_begin + idx + len);
728}
729
730int compare(UnownedStringSlice const& lhs, UnownedStringSlice const& rhs)
731{
732    auto lhsSize = lhs.getLength();
733    auto rhsSize = rhs.getLength();
734
735    auto lhsData = lhs.begin();
736    auto rhsData = rhs.begin();
737
738    auto sharedPrefixSize = std::min(lhsSize, rhsSize);
739    int sharedPrefixCmp = memcmp(lhsData, rhsData, sharedPrefixSize);
740    if (sharedPrefixCmp != 0)
741        return sharedPrefixCmp;
742
743    return int(lhsSize - rhsSize);
744}
745
746bool UnownedStringSlice::operator==(ThisType const& other) const
747{
748    // Note that memcmp is undefined when passed in null ptrs, so if we want to handle
749    // we need to cover that case.
750    // Can only be nullptr if size is 0.
751    auto thisSize = getLength();
752    auto otherSize = other.getLength();
753
754    if (thisSize != otherSize)
755    {
756        return false;
757    }
758
759    const char* const thisChars = begin();
760    const char* const otherChars = other.begin();
761    if (thisChars == otherChars || thisSize == 0)
762    {
763        return true;
764    }
765    SLANG_ASSERT(thisChars && otherChars);
766    return memcmp(thisChars, otherChars, thisSize) == 0;
767}
768
769bool UnownedStringSlice::caseInsensitiveEquals(const ThisType& rhs) const
770{
771    const auto length = getLength();
772    if (length != rhs.getLength())
773    {
774        return false;
775    }
776
777    const char* a = m_begin;
778    const char* b = rhs.m_begin;
779
780    // Assuming this is a faster test
781    if (memcmp(a, b, length) != 0)
782    {
783        // They aren't identical so compare character by character
784        for (Index i = 0; i < length; ++i)
785        {
786            if (CharUtil::toLower(a[i]) != CharUtil::toLower(b[i]))
787            {
788                return false;
789            }
790        }
791    }
792
793    return true;
794}
795} // namespace Slang
796
797std::ostream& operator<<(std::ostream& stream, const Slang::String& s)
798{
799    stream << s.getBuffer();
800    return stream;
801}