diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2021-10-04 14:15:51 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-10-04 14:15:51 -0400 |
| commit | 97bb82ebcdf8f1391b9d93b5a8d7b1dfc4e88e52 (patch) | |
| tree | f120ba282cbea96d23ed179737984a4610d3b520 /source/core | |
| parent | b3dfe383c6d31ff3dbd76dcfb32de8d536382f3e (diff) | |
Removing exceptions from core/compiler-core (#1953)
* #include an absolute path didn't work - because paths were taken to always be relative.
* Refactor Stream. Working on all tests.
* Split out CharEncode.
* Make method names lower camel.
m_prefix in Writer/Reader
* Tidy up around CharEncode interface.
* Small improvements around encode/decode.
* Better use of types.
* Remove readLine from TextReader.
* Remove exceptions from Stream/Text handling.
* Fix some typos.
* Fix tabbing.
* Fix missing override.
* Remove remaining exception throw/catch via using signal mechanism.
* Remove exceptions that are not used anymore.
* Document the Stream interface.
* Remove index for decoding 'get byte' function.
* Fix CharReader -> ByteReader.
Diffstat (limited to 'source/core')
| -rw-r--r-- | source/core/slang-char-encode.cpp | 181 | ||||
| -rw-r--r-- | source/core/slang-char-encode.h | 200 | ||||
| -rw-r--r-- | source/core/slang-common.h | 50 | ||||
| -rw-r--r-- | source/core/slang-dictionary.h | 27 | ||||
| -rw-r--r-- | source/core/slang-exception.h | 87 | ||||
| -rw-r--r-- | source/core/slang-file-system.cpp | 22 | ||||
| -rw-r--r-- | source/core/slang-io.cpp | 130 | ||||
| -rw-r--r-- | source/core/slang-io.h | 6 | ||||
| -rw-r--r-- | source/core/slang-riff.cpp | 185 | ||||
| -rw-r--r-- | source/core/slang-signal.cpp | 69 | ||||
| -rw-r--r-- | source/core/slang-signal.h | 44 | ||||
| -rw-r--r-- | source/core/slang-stream.cpp | 139 | ||||
| -rw-r--r-- | source/core/slang-stream.h | 87 | ||||
| -rw-r--r-- | source/core/slang-string-escape-util.cpp | 5 | ||||
| -rw-r--r-- | source/core/slang-string.cpp | 48 | ||||
| -rw-r--r-- | source/core/slang-string.h | 13 | ||||
| -rw-r--r-- | source/core/slang-text-io.cpp | 438 | ||||
| -rw-r--r-- | source/core/slang-text-io.h | 434 |
18 files changed, 1132 insertions, 1033 deletions
diff --git a/source/core/slang-char-encode.cpp b/source/core/slang-char-encode.cpp new file mode 100644 index 000000000..d061e34ba --- /dev/null +++ b/source/core/slang-char-encode.cpp @@ -0,0 +1,181 @@ +#include "slang-char-encode.h" + +namespace Slang +{ + +class Utf8CharEncoding : public CharEncoding +{ +public: + typedef CharEncoding Super; + + virtual void encode(const UnownedStringSlice& slice, List<Byte>& ioBuffer) override + { + ioBuffer.addRange((const Byte*)slice.begin(), slice.getLength()); + } + virtual void decode(const Byte* bytes, int length, List<char>& ioChars) override + { + ioChars.addRange((const char*)bytes, length); + } + Utf8CharEncoding() : Super(CharEncodeType::UTF8) {} +}; + +class Utf32CharEncoding : public CharEncoding +{ +public: + typedef CharEncoding Super; + + virtual void encode(const UnownedStringSlice& slice, List<Byte>& ioBuffer) override + { + Index ptr = 0; + while (ptr < slice.getLength()) + { + const Char32 codePoint = getUnicodePointFromUTF8([&]() -> Byte + { + if (ptr < slice.getLength()) + return slice[ptr++]; + else + return '\0'; + }); + // Note: Assumes byte order is same as arch byte order + ioBuffer.addRange((const Byte*)&codePoint, 4); + } + } + virtual void decode(const Byte* bytes, int length, List<char>& ioBuffer) override + { + // Note: Assumes bytes is Char32 aligned + SLANG_ASSERT((size_t(bytes) & 3) == 0); + const Char32* content = (const Char32*)bytes; + for (int i = 0; i < (length >> 2); i++) + { + char buf[5]; + int count = encodeUnicodePointToUTF8(content[i], buf); + for (int j = 0; j < count; j++) + ioBuffer.addRange(buf, count); + } + } + + Utf32CharEncoding() : Super(CharEncodeType::UTF32) {} +}; + +class Utf16CharEncoding : public CharEncoding //UTF16 +{ +public: + typedef CharEncoding Super; + Utf16CharEncoding(bool reverseOrder): + Super(reverseOrder ? CharEncodeType::UTF16Reversed : CharEncodeType::UTF16), + m_reverseOrder(reverseOrder) + {} + virtual void encode(const UnownedStringSlice& slice, List<Byte>& ioBuffer) override + { + Index index = 0; + while (index < slice.getLength()) + { + const Char32 codePoint = getUnicodePointFromUTF8([&]() -> Byte + { + if (index < slice.getLength()) + return slice[index++]; + else + return '\0'; + }); + + Char16 buffer[2]; + int count; + if (!m_reverseOrder) + count = encodeUnicodePointToUTF16(codePoint, buffer); + else + count = encodeUnicodePointToUTF16Reversed(codePoint, buffer); + ioBuffer.addRange((const Byte*)buffer, count * 2); + } + } + virtual void decode(const Byte* bytes, int length, List<char>& ioBuffer) override + { + Index index = 0; + while (index < length) + { + const Char32 codePoint = getUnicodePointFromUTF16([&]() -> Byte + { + if (index < length) + return bytes[index++]; + else + return Byte(0); + }); + + char buf[5]; + int count = encodeUnicodePointToUTF8(codePoint, buf); + ioBuffer.addRange((const char*)buf, count); + } + } + +private: + bool m_reverseOrder = false; +}; + +/* static */CharEncodeType CharEncoding::determineEncoding(const Byte* bytes, size_t bytesCount, size_t& outOffset) +{ + // TODO(JS): Assumes the bytes are suitably aligned + + if (bytesCount >= 3 && bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf) + { + outOffset = 3; + return CharEncodeType::UTF8; + } + else if (bytesCount >= 2) + { + Char16 c; + ::memcpy(&c, bytes, 2); + + if (c == kUTF16Header) + { + outOffset = 2; + return CharEncodeType::UTF16; + } + else if (c == kUTF16ReversedHeader) + { + outOffset = 2; + return CharEncodeType::UTF16Reversed; + } + } + else + { + // If we don't have a 'mark' byte then we are bit stumped. We'll look for a null bytes and assume they mean we have a 16 bit encoding + for (size_t i = 0; i < bytesCount; i += 2) + { +#if SLANG_LITTLE_ENDIAN + const auto low = bytes[i]; + const auto high = bytes[i + 1]; +#else + const auto low = bytes[i + 1]; + const auto high = bytes[i]; +#endif + if ((low == 0) ^ (high == 0)) + { + outOffset = 2; + return (high == 0) ? CharEncodeType::UTF16 : CharEncodeType::UTF16Reversed; + } + } + } + + // Assume it's UTF8 or 7 bit ascii which UTF8 is a superset of + outOffset = 0; + return CharEncodeType::UTF8; +} + +static Utf8CharEncoding _utf8Encoding; +static Utf16CharEncoding _utf16Encoding(false); +static Utf16CharEncoding _utf16EncodingReversed(true); +static Utf32CharEncoding _utf32Encoding; + +/* static */CharEncoding* const CharEncoding::g_encoding[Index(CharEncodeType::CountOf)] +{ + &_utf8Encoding, // UTF8, + &_utf16Encoding, // UTF16, + &_utf16EncodingReversed, // UTF16Reversed, + &_utf32Encoding, // UTF32, +}; + +CharEncoding* CharEncoding::UTF8 = &_utf8Encoding; +CharEncoding* CharEncoding::UTF16 = &_utf16Encoding; +CharEncoding* CharEncoding::UTF16Reversed = &_utf16EncodingReversed; +CharEncoding* CharEncoding::UTF32 = &_utf32Encoding; + +} // namespace Slang diff --git a/source/core/slang-char-encode.h b/source/core/slang-char-encode.h new file mode 100644 index 000000000..a778cc3c9 --- /dev/null +++ b/source/core/slang-char-encode.h @@ -0,0 +1,200 @@ +#ifndef SLANG_CORE_CHAR_ENCODE_H +#define SLANG_CORE_CHAR_ENCODE_H + +#include "slang-secure-crt.h" +#include "slang-basic.h" + +namespace Slang +{ + +// NOTE! Order must be kept the same to match up with +enum class CharEncodeType +{ + UTF8, + UTF16, + UTF16Reversed, + UTF32, + CountOf, +}; + +template <typename ReadByteFunc> +Char32 getUnicodePointFromUTF8(const ReadByteFunc& readByte) +{ + Char32 codePoint = 0; + uint32_t leading = Byte(readByte()); + uint32_t mask = 0x80; + Index count = 0; + while (leading & mask) + { + count++; + mask >>= 1; + } + codePoint = (leading & (mask - 1)); + for (Index i = 1; i <= count - 1; i++) + { + codePoint <<= 6; + codePoint += (readByte() & 0x3F); + } + return codePoint; +} + +template <typename ReadByteFunc> +Char32 getUnicodePointFromUTF16(const ReadByteFunc& readByte) +{ + uint32_t byte0 = Byte(readByte()); + uint32_t byte1 = Byte(readByte()); + uint32_t word0 = byte0 + (byte1 << 8); + if (word0 >= 0xD800 && word0 <= 0xDFFF) + { + uint32_t byte2 = Byte(readByte()); + uint32_t byte3 = Byte(readByte()); + uint32_t word1 = byte2 + (byte3 << 8); + return Char32(((word0 & 0x3FF) << 10) + (word1 & 0x3FF) + 0x10000); + } + else + return Char32(word0); +} + +template <typename ReadByteFunc> +Char32 getUnicodePointFromUTF16Reversed(const ReadByteFunc& readByte) +{ + uint32_t byte0 = Byte(readByte()); + uint32_t byte1 = Byte(readByte()); + uint32_t word0 = (byte0 << 8) + byte1; + if (word0 >= 0xD800 && word0 <= 0xDFFF) + { + uint32_t byte2 = Byte(readByte()); + uint32_t byte3 = Byte(readByte()); + uint32_t word1 = (byte2 << 8) + byte3; + return Char32(((word0 & 0x3FF) << 10) + (word1 & 0x3FF)); + } + else + return Char32(word0); +} + +template <typename ReadByteFunc> +Char32 getUnicodePointFromUTF32(const ReadByteFunc& readByte) +{ + uint32_t byte0 = Byte(readByte()); + uint32_t byte1 = Byte(readByte()); + uint32_t byte2 = Byte(readByte()); + uint32_t byte3 = Byte(readByte()); + return Char32(byte0 + (byte1 << 8) + (byte2 << 16) + (byte3 << 24)); +} + +// Encode functions return the amount of elements output to the buffer +inline int encodeUnicodePointToUTF8(Char32 codePoint, char* outBuffer) +{ + char* const dst = outBuffer; + // TODO(JS): This supports 4 + 6 * 3 = 22 bits. + // The standard allows up to 0x10FFFF. + if (codePoint <= 0x7F) + { + dst[0] = char(codePoint); + return 1; + } + else if (codePoint <= 0x7FF) + { + dst[0] = char(0xC0 + (codePoint >> 6)); + dst[1] = char(0x80 + (codePoint & 0x3F)); + return 2; + } + else if (codePoint <= 0xFFFF) + { + dst[0] = char(0xE0 + (codePoint >> 12)); + dst[1] = char(0x80 + ((codePoint >> 6) & (0x3F))); + dst[2] = char(0x80 + (codePoint & 0x3F)); + return 3; + } + else + { + dst[0] = char(0xF0 + (codePoint >> 18)); + dst[1] = char(0x80 + ((codePoint >> 12) & 0x3F)); + dst[2] = char(0x80 + ((codePoint >> 6) & 0x3F)); + dst[3] = char(0x80 + (codePoint & 0x3F)); + return 4; + } +} + +inline int encodeUnicodePointToUTF16(Char32 codePoint, Char16* outBuffer) +{ + Char16* const dst = outBuffer; + if (codePoint <= 0xD7FF || (codePoint >= 0xE000 && codePoint <= 0xFFFF)) + { + dst[0] = Char16(codePoint); + return 1; + } + else + { + const uint32_t sub = codePoint - 0x10000; + dst[0] = Char16((sub >> 10) + 0xD800); + dst[1] = Char16((sub & 0x3FF) + 0xDC00); + return 2; + } +} + +SLANG_FORCE_INLINE Char16 reverseByteOrder(Char16 val) +{ + return (val >> 8) | (val << 8); +} + +inline int encodeUnicodePointToUTF16Reversed(Char32 codePoint, Char16* outBuffer) +{ + Char16* const dst = outBuffer; + if (codePoint <= 0xD7FF || (codePoint >= 0xE000 && codePoint <= 0xFFFF)) + { + dst[0] = reverseByteOrder(Char16(codePoint)); + return 1; + } + else + { + const uint32_t sub = codePoint - 0x10000; + const uint32_t high = (sub >> 10) + 0xD800; + const uint32_t low = (sub & 0x3FF) + 0xDC00; + dst[0] = reverseByteOrder(Char16(high)); + dst[1] = reverseByteOrder(Char16(low)); + return 2; + } +} + +static const Char16 kUTF16Header = 0xFEFF; +static const Char16 kUTF16ReversedHeader = 0xFFFE; + +class CharEncoding +{ +public: + static CharEncoding* UTF8,* UTF16,* UTF16Reversed,* UTF32; + + /// Encode Utf8 held in slice append into ioBuffer + virtual void encode(const UnownedStringSlice& str, List<Byte>& ioBuffer) = 0; + /// Decode buffer into Utf8 held in ioBuffer + virtual void decode(const Byte* buffer, int length, List<char>& ioBuffer) = 0; + + virtual ~CharEncoding() {} + + /// Get the encoding type + CharEncodeType getEncodingType() const { return m_encodingType; } + + /// Given some bytes determines a character encoding type, based on the initial bytes. + /// If can't be determined will assume UTF8. + /// Outputs the offset to the first non mark in outOffset + static CharEncodeType determineEncoding(const Byte* bytes, size_t bytesCount, size_t& outOffset); + + /// Get the + static CharEncoding* getEncoding(CharEncodeType type) { return g_encoding[Index(type)]; } + + CharEncoding(CharEncodeType encodingType) : + m_encodingType(encodingType) + { + } + +protected: + + CharEncodeType m_encodingType; + + static CharEncoding*const g_encoding[Index(CharEncodeType::CountOf)]; +}; + +} + +#endif diff --git a/source/core/slang-common.h b/source/core/slang-common.h index 717b63740..eb6502b41 100644 --- a/source/core/slang-common.h +++ b/source/core/slang-common.h @@ -7,11 +7,7 @@ #include <stdint.h> -#ifdef __GNUC__ -#define CORE_LIB_ALIGN_16(x) x __attribute__((aligned(16))) -#else -#define CORE_LIB_ALIGN_16(x) __declspec(align(16)) x -#endif +#include "slang-signal.h" #define VARIADIC_TEMPLATE @@ -27,13 +23,33 @@ namespace Slang typedef SlangUInt UInt; typedef SlangInt Int; + static const UInt kMaxUInt = ~UInt(0); + static const Int kMaxInt = Int(kMaxUInt >> 1); + // typedef unsigned short Word; typedef intptr_t PtrInt; + // TODO(JS): It looks like Index is actually 64 bit on 64 bit targets(!) + // Previous discussions landed on Index being int32_t. + // Type used for indexing, in arrays/views etc typedef Int Index; + static const Index kMaxIndex = kMaxInt; + + typedef uint8_t Byte; + + // TODO(JS): + // Perhaps these should be named Utf8, Utf16 and UnicodePoint/Rune/etc? For now, just keep it simple + // + typedef char Char8; + // 16 bit character. Note much like in utf8, a character may or may not represent a code point (it can be part of a code point). + typedef uint16_t Char16; + + // Can always hold a unicode code point. + typedef uint32_t Char32; + template <typename T> inline T&& _Move(T & obj) { @@ -48,15 +64,7 @@ namespace Slang v1 = _Move(tmp); } -#ifdef _MSC_VER -# define SLANG_RETURN_NEVER __declspec(noreturn) -//#elif SLANG_CLANG -//# define SLANG_RETURN_NEVER [[noreturn]] -#else -# define SLANG_RETURN_NEVER [[noreturn]] -//# define SLANG_RETURN_NEVER /* empty */ -#endif - +// TODO: Shouldn't these be SLANG_ prefixed? #ifdef _MSC_VER #define UNREACHABLE_RETURN(x) #define UNREACHABLE(x) @@ -65,27 +73,17 @@ namespace Slang #define UNREACHABLE(x) x; #endif - SLANG_RETURN_NEVER void signalUnexpectedError(char const* message); } -#define SLANG_UNEXPECTED(reason) \ - Slang::signalUnexpectedError("unexpected: " reason) - -#define SLANG_UNIMPLEMENTED_X(what) \ - Slang::signalUnexpectedError("unimplemented: " what) - -#define SLANG_UNREACHABLE(msg) \ - Slang::signalUnexpectedError("unreachable code executed: " msg) - #ifdef _DEBUG -#define SLANG_EXPECT(VALUE, MSG) if(VALUE) {} else Slang::signalUnexpectedError("assertion failed: '" MSG "'") +#define SLANG_EXPECT(VALUE, MSG) if(VALUE) {} else SLANG_ASSERT_FAILURE(MSG) #define SLANG_ASSERT(VALUE) SLANG_EXPECT(VALUE, #VALUE) #else #define SLANG_EXPECT(VALUE, MSG) do {} while(0) #define SLANG_ASSERT(VALUE) do {} while(0) #endif -#define SLANG_RELEASE_ASSERT(VALUE) if(VALUE) {} else Slang::signalUnexpectedError("assertion failed") +#define SLANG_RELEASE_ASSERT(VALUE) if(VALUE) {} else SLANG_ASSERT_FAILURE(#VALUE) #define SLANG_RELEASE_EXPECT(VALUE, WHAT) if(VALUE) {} else SLANG_UNEXPECTED(WHAT) template<typename T> void slang_use_obj(T&) {} diff --git a/source/core/slang-dictionary.h b/source/core/slang-dictionary.h index 51b61de60..eef7d6908 100644 --- a/source/core/slang-dictionary.h +++ b/source/core/slang-dictionary.h @@ -171,7 +171,7 @@ namespace Slang } if (insertPos != -1) return FindPositionResult(-1, insertPos); - throw InvalidOperationException("Hash map is full. This indicates an error in Key::Equal or Key::getHashCode."); + SLANG_ASSERT_FAILURE("Hash map is full. This indicates an error in Key::Equal or Key::getHashCode."); } TValue & _Insert(KeyValuePair<TKey, TValue>&& kvPair, int pos) { @@ -217,12 +217,12 @@ namespace Slang return true; } else - throw InvalidOperationException("Inconsistent find result returned. This is a bug in Dictionary implementation."); + SLANG_ASSERT_FAILURE("Inconsistent find result returned. This is a bug in Dictionary implementation."); } void Add(KeyValuePair<TKey, TValue>&& kvPair) { if (!AddIfNotExists(_Move(kvPair))) - throw KeyExistsException("The key already exists in Dictionary."); + SLANG_ASSERT_FAILURE("The key already exists in Dictionary."); } TValue& Set(KeyValuePair<TKey, TValue>&& kvPair) { @@ -236,7 +236,7 @@ namespace Slang return _Insert(_Move(kvPair), pos.InsertionPosition); } else - throw InvalidOperationException("Inconsistent find result returned. This is a bug in Dictionary implementation."); + SLANG_ASSERT_FAILURE("Inconsistent find result returned. This is a bug in Dictionary implementation."); } public: class Iterator @@ -358,7 +358,7 @@ namespace Slang return nullptr; } else - throw InvalidOperationException("Inconsistent find result returned. This is a bug in Dictionary implementation."); + SLANG_ASSERT_FAILURE("Inconsistent find result returned. This is a bug in Dictionary implementation."); } /// This differs from TryGetValueOrAdd, in that it always returns the Value held in the Dictionary. @@ -379,7 +379,7 @@ namespace Slang return _Insert(_Move(kvPair), pos.InsertionPosition); } else - throw InvalidOperationException("Inconsistent find result returned. This is a bug in Dictionary implementation."); + SLANG_ASSERT_FAILURE("Inconsistent find result returned. This is a bug in Dictionary implementation."); } template<typename KeyType> @@ -440,7 +440,7 @@ namespace Slang return dict->hashMap[pos.ObjectPosition].Value; } else - throw KeyNotFoundException("The key does not exists in dictionary."); + SLANG_ASSERT_FAILURE("The key does not exists in dictionary."); } inline TValue & operator()() const { @@ -742,8 +742,7 @@ namespace Slang } if (insertPos != -1) return FindPositionResult(-1, insertPos); - throw InvalidOperationException( - "Hash map is full. This indicates an error in Key::Equal or Key::GetHashCode."); + SLANG_ASSERT_FAILURE("Hash map is full. This indicates an error in Key::Equal or Key::GetHashCode."); } TValue& _Insert(KeyValuePair<TKey, TValue>&& kvPair, int pos) { @@ -791,13 +790,12 @@ namespace Slang return true; } else - throw InvalidOperationException("Inconsistent find result returned. This is a " - "bug in Dictionary implementation."); + SLANG_ASSERT_FAILURE("Inconsistent find result returned. This is a bug in Dictionary implementation."); } void Add(KeyValuePair<TKey, TValue>&& kvPair) { if (!AddIfNotExists(_Move(kvPair))) - throw KeyExistsException("The key already exists in Dictionary."); + SLANG_ASSERT_FAILURE("The key already exists in Dictionary."); } TValue& Set(KeyValuePair<TKey, TValue>&& kvPair) { @@ -814,8 +812,7 @@ namespace Slang return _Insert(_Move(kvPair), pos.InsertionPosition); } else - throw InvalidOperationException("Inconsistent find result returned. This is a " - "bug in Dictionary implementation."); + SLANG_ASSERT_FAILURE("Inconsistent find result returned. This is a bug in Dictionary implementation."); } public: @@ -923,7 +920,7 @@ namespace Slang } else { - throw KeyNotFoundException("The key does not exists in dictionary."); + SLANG_ASSERT_FAILURE("The key does not exists in dictionary."); } } inline TValue& operator()() const { return GetValue(); } diff --git a/source/core/slang-exception.h b/source/core/slang-exception.h index 91139e298..3c5621d7f 100644 --- a/source/core/slang-exception.h +++ b/source/core/slang-exception.h @@ -6,6 +6,13 @@ namespace Slang { + // NOTE! + // Exceptions should not generally be used in core/compiler-core, use the 'signal' mechanism + // ideally using the macros in the slang-signal.h such as `SLANG_UNEXPECTED` + // + // If core/compiler-core libraries are compiled with SLANG_DISABLE_EXCEPTIONS, + // these classes will *never* be thrown. + class Exception { public: @@ -21,18 +28,6 @@ namespace Slang {} }; - class IndexOutofRangeException : public Exception - { - public: - IndexOutofRangeException() - {} - IndexOutofRangeException(const String & message) - : Exception(message) - { - } - - }; - class InvalidOperationException : public Exception { public: @@ -44,73 +39,7 @@ namespace Slang } }; - - class ArgumentException : public Exception - { - public: - ArgumentException() - {} - ArgumentException(const String & message) - : Exception(message) - { - } - - }; - - class KeyNotFoundException : public Exception - { - public: - KeyNotFoundException() - {} - KeyNotFoundException(const String & message) - : Exception(message) - { - } - }; - class KeyExistsException : public Exception - { - public: - KeyExistsException() - {} - KeyExistsException(const String & message) - : Exception(message) - { - } - }; - - class NotSupportedException : public Exception - { - public: - NotSupportedException() - {} - NotSupportedException(const String & message) - : Exception(message) - { - } - }; - - class NotImplementedException : public Exception - { - public: - NotImplementedException() - {} - NotImplementedException(const String & message) - : Exception(message) - { - } - }; - - class InvalidProgramException : public Exception - { - public: - InvalidProgramException() - {} - InvalidProgramException(const String & message) - : Exception(message) - { - } - }; - + class InternalError : public Exception { public: diff --git a/source/core/slang-file-system.cpp b/source/core/slang-file-system.cpp index 8dd01ebf0..52ec40c07 100644 --- a/source/core/slang-file-system.cpp +++ b/source/core/slang-file-system.cpp @@ -201,26 +201,10 @@ SlangResult OSFileSystem::enumeratePathContents(const char* path, FileSystemCont SlangResult OSFileSystem::saveFile(const char* pathIn, const void* data, size_t size) { SLANG_RETURN_ON_FAIL(_checkMutable(m_style)); - const String path = _fixPathDelimiters(pathIn); - - try - { - FileStream stream(pathIn, FileMode::Create, FileAccess::Write, FileShare::ReadWrite); - - int64_t numWritten = stream.write(data, size); - - if (numWritten != int64_t(size)) - { - return SLANG_FAIL; - } - - } - catch (const IOException&) - { - return SLANG_E_CANNOT_OPEN; - } - + FileStream stream; + SLANG_RETURN_ON_FAIL(stream.init(pathIn, FileMode::Create, FileAccess::Write, FileShare::ReadWrite)); + SLANG_RETURN_ON_FAIL(stream.write(data, size)); return SLANG_OK; } diff --git a/source/core/slang-io.cpp b/source/core/slang-io.cpp index 0755a6a2f..162e49980 100644 --- a/source/core/slang-io.cpp +++ b/source/core/slang-io.cpp @@ -825,94 +825,98 @@ namespace Slang return _getExecutablePath(); } - Slang::String File::readAllText(const Slang::String& fileName) + SlangResult File::readAllText(const Slang::String& fileName, String& outText) { - StreamReader reader(new FileStream(fileName, FileMode::Open, FileAccess::Read, FileShare::ReadWrite)); - return reader.ReadToEnd(); + RefPtr<FileStream> stream(new FileStream); + SLANG_RETURN_ON_FAIL(stream->init(fileName, FileMode::Open, FileAccess::Read, FileShare::ReadWrite)); + + StreamReader reader; + SLANG_RETURN_ON_FAIL(reader.init(stream)); + SLANG_RETURN_ON_FAIL(reader.readToEnd(outText)); + + return SLANG_OK; } - Slang::List<unsigned char> File::readAllBytes(const Slang::String& fileName) + SlangResult File::readAllBytes(const Slang::String& path, Slang::List<unsigned char>& out) { - RefPtr<FileStream> fs = new FileStream(fileName, FileMode::Open, FileAccess::Read, FileShare::ReadWrite); - List<unsigned char> buffer; - while (!fs->isEnd()) + FileStream stream; + SLANG_RETURN_ON_FAIL(stream.init(path, FileMode::Open, FileAccess::Read, FileShare::ReadWrite)); + + const Int64 start = stream.getPosition(); + stream.seek(SeekOrigin::End, 0); + const Int64 end = stream.getPosition(); + stream.seek(SeekOrigin::Start, start); + + const Int64 positionSizeInBytes = end - start; + + if (UInt64(positionSizeInBytes) > UInt64(kMaxIndex)) { - unsigned char ch; - int read = (int)fs->read(&ch, 1); - if (read) - buffer.add(ch); - else - break; + // It's too large to fit in memory. + return SLANG_FAIL; } - return _Move(buffer); + + const Index sizeInBytes = Index(positionSizeInBytes); + + out.setCount(sizeInBytes); + + size_t readSizeInBytes; + SLANG_RETURN_ON_FAIL(stream.read(out.getBuffer(), sizeInBytes, readSizeInBytes)); + + // If not all read just return an error + return (size_t(sizeInBytes) == readSizeInBytes) ? SLANG_OK : SLANG_FAIL; } SlangResult File::readAllBytes(const String& path, ScopedAllocation& out) { - try - { - FileStream stream(path, FileMode::Open, FileAccess::Read, FileShare::ReadWrite); - - const Int64 start = stream.getPosition(); - stream.seek(SeekOrigin::End, 0); - const Int64 end = stream.getPosition(); - stream.seek(SeekOrigin::Start, start); + FileStream stream; + SLANG_RETURN_ON_FAIL(stream.init(path, FileMode::Open, FileAccess::Read, FileShare::ReadWrite)); - const Int64 positionSizeInBytes = end - start; + const Int64 start = stream.getPosition(); + stream.seek(SeekOrigin::End, 0); + const Int64 end = stream.getPosition(); + stream.seek(SeekOrigin::Start, start); - if (UInt64(positionSizeInBytes) > UInt64(~size_t(0))) - { - // It's too large to fit in memory. - return SLANG_FAIL; - } + const Int64 positionSizeInBytes = end - start; - const size_t sizeInBytes = size_t(positionSizeInBytes); - void* data = out.allocate(sizeInBytes); - if (!data) - { - return SLANG_E_OUT_OF_MEMORY; - } + if (UInt64(positionSizeInBytes) > UInt64(~size_t(0))) + { + // It's too large to fit in memory. + return SLANG_FAIL; + } - const size_t readSizeInBytes = stream.read(data, sizeInBytes); + const size_t sizeInBytes = size_t(positionSizeInBytes); - // If not all read just return an error - if (sizeInBytes != readSizeInBytes) - { - return SLANG_FAIL; - } - } - catch (const IOException&) + void* data = out.allocate(sizeInBytes); + if (!data) { - return SLANG_FAIL; + return SLANG_E_OUT_OF_MEMORY; } - return SLANG_OK; + + size_t readSizeInBytes; + SLANG_RETURN_ON_FAIL(stream.read(data, sizeInBytes, readSizeInBytes)); + + // If not all read just return an error + return (sizeInBytes == readSizeInBytes) ? SLANG_OK : SLANG_FAIL; } SlangResult File::writeAllBytes(const String& path, const void* data, size_t size) { - try - { - FileStream stream(path, FileMode::Create, FileAccess::Write, FileShare::ReadWrite); - - const size_t writeSizeInBytes = stream.write(data, size); - - // If not all written just return an error - if (size != writeSizeInBytes) - { - return SLANG_FAIL; - } - } - catch (const IOException&) - { - return SLANG_FAIL; - } + FileStream stream; + SLANG_RETURN_ON_FAIL(stream.init(path, FileMode::Create, FileAccess::Write, FileShare::ReadWrite)); + SLANG_RETURN_ON_FAIL(stream.write(data, size)); return SLANG_OK; } - void File::writeAllText(const Slang::String& fileName, const Slang::String& text) + SlangResult File::writeAllText(const Slang::String& fileName, const Slang::String& text) { - StreamWriter writer(new FileStream(fileName, FileMode::Create)); - writer.Write(text); + RefPtr<FileStream> stream = new FileStream; + SLANG_RETURN_ON_FAIL(stream->init(fileName, FileMode::Create)); + + StreamWriter writer; + SLANG_RETURN_ON_FAIL(writer.init(stream)); + SLANG_RETURN_ON_FAIL(writer.write(text)); + + return SLANG_OK; } diff --git a/source/core/slang-io.h b/source/core/slang-io.h index a077d416b..50ce67784 100644 --- a/source/core/slang-io.h +++ b/source/core/slang-io.h @@ -14,12 +14,12 @@ namespace Slang public: static bool exists(const String& fileName); - static String readAllText(const String& fileName); + static SlangResult readAllText(const String& fileName, String& outString); - static List<unsigned char> readAllBytes(const String& fileName); + static SlangResult readAllBytes(const String& fileName, List<unsigned char>& out); static SlangResult readAllBytes(const String& fileName, ScopedAllocation& out); - static void writeAllText(const String& fileName, const String& text); + static SlangResult writeAllText(const String& fileName, const String& text); static SlangResult writeAllBytes(const String& fileName, const void* data, size_t size); diff --git a/source/core/slang-riff.cpp b/source/core/slang-riff.cpp index 482c35e81..670fed7a2 100644 --- a/source/core/slang-riff.cpp +++ b/source/core/slang-riff.cpp @@ -22,27 +22,18 @@ namespace Slang } // Skip the payload (we don't need to skip the Chunk because that was already read - stream->seek(SeekOrigin::Current, chunkSize - sizeof(RiffHeader)); + SLANG_RETURN_ON_FAIL(stream->seek(SeekOrigin::Current, chunkSize - sizeof(RiffHeader))); return SLANG_OK; } /* static */SlangResult RiffUtil::readChunk(Stream* stream, RiffHeader& outChunk) { - try - { - stream->read(&outChunk, sizeof(RiffHeader)); - } - catch (const IOException&) - { - return SLANG_FAIL; - } - + size_t readBytes; + SLANG_RETURN_ON_FAIL(stream->read(&outChunk, sizeof(RiffHeader), readBytes)); // TODO(JS): Could handle endianness issues here... - - return SLANG_OK; + return (readBytes == sizeof(RiffHeader)) ? SLANG_OK : SLANG_FAIL; } - /* static */SlangResult RiffUtil::writeData(const RiffHeader* header, size_t headerSize, const void* payload, size_t payloadSize, Stream* out) { SLANG_ASSERT(uint64_t(payloadSize) <= uint64_t(0xfffffffff)); @@ -54,56 +45,43 @@ namespace Slang chunk.type = header->type; chunk.size = uint32_t(headerSize - sizeof(RiffHeader) + payloadSize); - try - { - // The chunk - out->write(&chunk, sizeof(RiffHeader)); + // The chunk + SLANG_RETURN_ON_FAIL(out->write(&chunk, sizeof(RiffHeader))); - // Remainder of header - if (headerSize > sizeof(RiffHeader)) - { - // The rest of the header - out->write(header + 1, headerSize - sizeof(RiffHeader)); - } + // Remainder of header + if (headerSize > sizeof(RiffHeader)) + { + // The rest of the header + SLANG_RETURN_ON_FAIL(out->write(header + 1, headerSize - sizeof(RiffHeader))); + } - // Write the payload - out->write(payload, payloadSize); + // Write the payload + SLANG_RETURN_ON_FAIL(out->write(payload, payloadSize)); - // The riff spec requires all chunks are 4 byte aligned (even if size is not) - size_t padSize = getPadSize(payloadSize); - if (padSize - payloadSize) - { - uint8_t end[kRiffPadSize] = { 0 }; - out->write(end, padSize - payloadSize); - } - } - catch (const IOException&) + // The riff spec requires all chunks are 4 byte aligned (even if size is not) + size_t padSize = getPadSize(payloadSize); + if (padSize - payloadSize) { - return SLANG_FAIL; + uint8_t end[kRiffPadSize] = { 0 }; + SLANG_RETURN_ON_FAIL(out->write(end, padSize - payloadSize)); } - + return SLANG_OK; } /* static */SlangResult RiffUtil::readPayload(Stream* stream, size_t size, void* outData, size_t& outReadSize) { outReadSize = 0; - try - { - stream->read(outData, size); - const size_t alignedSize = getPadSize(size); - // Skip to the alignment - if (alignedSize > size) - { - stream->seek(SeekOrigin::Current, alignedSize - size); - } - outReadSize = alignedSize; - } - catch (const IOException&) + + SLANG_RETURN_ON_FAIL(stream->readExactly(outData, size)); + + const size_t alignedSize = getPadSize(size); + // Skip to the alignment + if (alignedSize > size) { - return SLANG_FAIL; + SLANG_RETURN_ON_FAIL(stream->seek(SeekOrigin::Current, alignedSize - size)); } - + outReadSize = alignedSize; return SLANG_OK; } @@ -118,19 +96,12 @@ namespace Slang *outHeader = chunk; - try - { - // Read the header - if (headerSize > sizeof(RiffHeader)) - { - stream->read(outHeader + 1, headerSize - sizeof(RiffHeader)); - } - } - catch (const IOException&) + // Read the header + if (headerSize > sizeof(RiffHeader)) { - return SLANG_FAIL; + SLANG_RETURN_ON_FAIL(stream->readExactly(outHeader + 1, headerSize - sizeof(RiffHeader))); } - + const size_t payloadSize = chunk.size - (headerSize - sizeof(RiffHeader)); size_t readSize; data.setCount(payloadSize); @@ -146,14 +117,7 @@ namespace Slang if (isListType(outHeader.chunk.type)) { // Read the sub type - try - { - stream->read(&outHeader.subType, sizeof(RiffListHeader) - sizeof(RiffHeader)); - } - catch (const IOException&) - { - return SLANG_FAIL; - } + SLANG_RETURN_ON_FAIL(stream->readExactly(&outHeader.subType, sizeof(RiffListHeader) - sizeof(RiffHeader))); } return SLANG_OK; @@ -251,65 +215,58 @@ struct DumpVisitor : public RiffContainer::Visitor listHeader.chunk.size = uint32_t(list->m_payloadSize); listHeader.subType = list->getSubType(); - try - { - // Write the header - stream->write(&listHeader, sizeof(listHeader)); + // Write the header + SLANG_RETURN_ON_FAIL(stream->write(&listHeader, sizeof(listHeader))); // Write the contained chunks - Chunk* chunk = list->m_containedChunks; - while (chunk) + Chunk* chunk = list->m_containedChunks; + while (chunk) + { + switch (chunk->m_kind) { - switch (chunk->m_kind) + case Chunk::Kind::List: { - case Chunk::Kind::List: - { - auto listChunk = static_cast<ListChunk*>(chunk); - // It's a container - SLANG_RETURN_ON_FAIL(write(listChunk, false, stream)); - break; - } - case Chunk::Kind::Data: - { - auto dataChunk = static_cast<DataChunk*>(chunk); + auto listChunk = static_cast<ListChunk*>(chunk); + // It's a container + SLANG_RETURN_ON_FAIL(write(listChunk, false, stream)); + break; + } + case Chunk::Kind::Data: + { + auto dataChunk = static_cast<DataChunk*>(chunk); - // Must be a regular chunk with data - RiffHeader chunkHeader; - chunkHeader.type = dataChunk->m_fourCC; - chunkHeader.size = uint32_t(dataChunk->m_payloadSize); + // Must be a regular chunk with data + RiffHeader chunkHeader; + chunkHeader.type = dataChunk->m_fourCC; + chunkHeader.size = uint32_t(dataChunk->m_payloadSize); - stream->write(&chunkHeader, sizeof(chunkHeader)); + SLANG_RETURN_ON_FAIL(stream->write(&chunkHeader, sizeof(chunkHeader))); - RiffContainer::Data* data = dataChunk->m_dataList; - while (data) - { - stream->write(data->getPayload(), data->getSize()); + RiffContainer::Data* data = dataChunk->m_dataList; + while (data) + { + SLANG_RETURN_ON_FAIL(stream->write(data->getPayload(), data->getSize())); - // Next but of data - data = data->m_next; - } + // Next but of data + data = data->m_next; + } - // Need to write for alignment - const size_t remainingSize = getPadSize(dataChunk->m_payloadSize) - dataChunk->m_payloadSize; + // Need to write for alignment + const size_t remainingSize = getPadSize(dataChunk->m_payloadSize) - dataChunk->m_payloadSize; - if (remainingSize) - { - static const uint8_t trailing[kRiffPadSize] = { 0 }; - stream->write(trailing, remainingSize); - } + if (remainingSize) + { + static const uint8_t trailing[kRiffPadSize] = { 0 }; + SLANG_RETURN_ON_FAIL(stream->write(trailing, remainingSize)); } - default: break; } - - // Next - chunk = chunk->m_next; + default: break; } - } - catch (const IOException&) - { - return SLANG_FAIL; - } + // Next + chunk = chunk->m_next; + } + return SLANG_OK; } diff --git a/source/core/slang-signal.cpp b/source/core/slang-signal.cpp new file mode 100644 index 000000000..9641fe5f7 --- /dev/null +++ b/source/core/slang-signal.cpp @@ -0,0 +1,69 @@ +#include "slang-signal.h" + +#include "slang-exception.h" + +#include "stdio.h" + +namespace Slang +{ + +static const char* _getSignalTypeAsText(SignalType type) +{ + switch (type) + { + case SignalType::AssertFailure: return "assert failure"; + case SignalType::Unimplemented: return "unimplemented"; + case SignalType::Unreachable: return "hit unreachable code"; + case SignalType::Unexpected: return "unexpected"; + case SignalType::InvalidOperation: return "invalid operation"; + case SignalType::AbortCompilation: return "abort compilation"; + default: return "unhandled"; + } +} + +String _getMessage(SignalType type, char const* message) +{ + StringBuilder buf; + const char* const typeText = _getSignalTypeAsText(type); + buf << typeText; + if (message) + { + buf << ": " << message; + } + + return buf.ProduceString(); +} + +// One point of having as a single function is a choke point both for handling (allowing different +// handling scenarios) as well as a choke point to set a breakpoint to catch 'signal' types +SLANG_RETURN_NEVER void handleSignal(SignalType type, char const* message) +{ + StringBuilder buf; + const char*const typeText = _getSignalTypeAsText(type); + buf << typeText << ": " << message; + + // Can be useful to enable during debug when problem is on CI + if (false) + { + printf("%s\n", _getMessage(type, message).getBuffer()); + } + +#if SLANG_HAS_EXCEPTIONS + switch (type) + { + case SignalType::InvalidOperation: throw InvalidOperationException(_getMessage(type, message)); + case SignalType::AbortCompilation: throw AbortCompilationException(); + default: throw InternalError(_getMessage(type, message)); + } +#else + // Attempt to drop out into the debugger. If a debugger isn't attached this will likely crash - which is probably the best + // we can do. + + SLANG_BREAKPOINT(0); + + // 'panic'. Exit with an error code as we can't throw or catch. + exit(-1); +#endif +} + +} diff --git a/source/core/slang-signal.h b/source/core/slang-signal.h new file mode 100644 index 000000000..2151bdcfe --- /dev/null +++ b/source/core/slang-signal.h @@ -0,0 +1,44 @@ +#ifndef SLANG_CORE_SIGNAL_H +#define SLANG_CORE_SIGNAL_H + +#include "slang-common.h" + +namespace Slang +{ + +enum class SignalType +{ + Unexpected, + Unimplemented, + AssertFailure, + Unreachable, + InvalidOperation, + AbortCompilation, +}; + + +// Note that message can be passed as nullptr for no message. +SLANG_RETURN_NEVER void handleSignal(SignalType type, char const* message); + +#define SLANG_UNEXPECTED(reason) \ + ::Slang::handleSignal(::Slang::SignalType::Unexpected, reason) + +#define SLANG_UNIMPLEMENTED_X(what) \ + ::Slang::handleSignal(::Slang::SignalType::Unimplemented, what) + +#define SLANG_UNREACHABLE(msg) \ + ::Slang::handleSignal(::Slang::SignalType::Unreachable, msg) + +#define SLANG_ASSERT_FAILURE(msg) \ + ::Slang::handleSignal(::Slang::SignalType::AssertFailure, msg) + +#define SLANG_INVALID_OPERATION(msg) \ + ::Slang::handleSignal(::Slang::SignalType::InvalidOperation, msg) + +#define SLANG_ABORT_COMPILATION(msg) \ + ::Slang::handleSignal(::Slang::SignalType::AbortCompilation, msg) + + +} + +#endif diff --git a/source/core/slang-stream.cpp b/source/core/slang-stream.cpp index c4ba27927..f938eb886 100644 --- a/source/core/slang-stream.cpp +++ b/source/core/slang-stream.cpp @@ -9,33 +9,53 @@ namespace Slang // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! FileStream !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -FileStream::FileStream(const String& fileName, FileMode fileMode) +SlangResult Stream::readExactly(void* buffer, size_t length) +{ + size_t readBytes; + SLANG_RETURN_ON_FAIL(read(buffer, length, readBytes)); + return (readBytes == length) ? SLANG_OK : SLANG_FAIL; +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! FileStream !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +FileStream::FileStream() : + m_handle(nullptr), + m_fileAccess(FileAccess::None), + m_endReached(false) +{ +} + +SlangResult FileStream::init(const String& fileName, FileMode fileMode) { const FileAccess access = (fileMode == FileMode::Open) ? FileAccess::Read : FileAccess::Write; - _init(fileName, fileMode, access, FileShare::None); + return _init(fileName, fileMode, access, FileShare::None); } -FileStream::FileStream(const String& fileName, FileMode fileMode, FileAccess access, FileShare share) +SlangResult FileStream::init(const String& fileName, FileMode fileMode, FileAccess access, FileShare share) { - _init(fileName, fileMode, access, share); + return _init(fileName, fileMode, access, share); } -void FileStream::_init(const String& fileName, FileMode fileMode, FileAccess access, FileShare share) +SlangResult FileStream::_init(const String& fileName, FileMode fileMode, FileAccess access, FileShare share) { + // Make sure it's closed to start with + close(); + if (access == FileAccess::None) { - throw ArgumentException("FileAccess::None not valid to create a FileStream."); + SLANG_ASSERT(!"FileAccess::None not valid to create a FileStream."); + return SLANG_E_INVALID_ARG; } - // Default to no access, until stream is fully constructed - m_fileAccess = FileAccess::None; - const char* mode = "rt"; switch (fileMode) { case FileMode::Create: if (access == FileAccess::Read) - throw ArgumentException("Read-only access is incompatible with Create mode."); + { + SLANG_ASSERT(!"Read-only access is incompatible with Create mode."); + return SLANG_E_INVALID_ARG; + } else if (access == FileAccess::ReadWrite) { mode = "w+b"; @@ -62,10 +82,13 @@ void FileStream::_init(const String& fileName, FileMode fileMode, FileAccess acc case FileMode::CreateNew: if (File::exists(fileName)) { - throw IOException("Failed opening '" + fileName + "', file already exists."); + return SLANG_E_CANNOT_OPEN; } if (access == FileAccess::Read) - throw ArgumentException("Read-only access is incompatible with Create mode."); + { + SLANG_ASSERT(!"Read-only access is incompatible with Create mode."); + return SLANG_E_INVALID_ARG; + } else if (access == FileAccess::ReadWrite) { mode = "w+b"; @@ -77,7 +100,10 @@ void FileStream::_init(const String& fileName, FileMode fileMode, FileAccess acc break; case FileMode::Append: if (access == FileAccess::Read) - throw ArgumentException("Read-only access is incompatible with Append mode."); + { + SLANG_ASSERT(!"Read-only access is incompatible with Append mode."); + return SLANG_E_INVALID_ARG; + } else if (access == FileAccess::ReadWrite) { mode = "a+b"; @@ -122,8 +148,8 @@ void FileStream::_init(const String& fileName, FileMode fileMode, FileAccess acc shFlag = _SH_DENYNO; break; default: - throw ArgumentException("Invalid file share mode."); - break; + SLANG_ASSERT(!"Invalid file share mode."); + return SLANG_FAIL; } if (share == FileShare::None) #pragma warning(suppress:4996) @@ -135,11 +161,12 @@ void FileStream::_init(const String& fileName, FileMode fileMode, FileAccess acc #endif if (!m_handle) { - throw IOException("Cannot open file '" + fileName + "'"); + return SLANG_E_CANNOT_OPEN; } // Just set the access specified m_fileAccess = access; + return SLANG_OK; } FileStream::~FileStream() @@ -162,7 +189,7 @@ Int64 FileStream::getPosition() #endif } -void FileStream::seek(SeekOrigin seekOrigin, Int64 offset) +SlangResult FileStream::seek(SeekOrigin seekOrigin, Int64 offset) { int fseekOrigin; switch (seekOrigin) @@ -177,8 +204,8 @@ void FileStream::seek(SeekOrigin seekOrigin, Int64 offset) fseekOrigin = SEEK_CUR; break; default: - throw NotSupportedException("Unsupported seek origin."); - break; + SLANG_ASSERT(!"Unsupported seek origin."); + return SLANG_FAIL; } // If endReached is intended to be like feof - then doing a seek will reset it @@ -190,34 +217,37 @@ void FileStream::seek(SeekOrigin seekOrigin, Int64 offset) int rs = fseek(m_handle, (long int)offset, fseekOrigin); #endif - if (rs != 0) - { - throw IOException("FileStream seek failed."); - } + // If rs != 0 then the the seek failed + SLANG_ASSERT(rs == 0); + + return (rs == 0) ? SLANG_OK : SLANG_FAIL; } -size_t FileStream::read(void* buffer, size_t length) +SlangResult FileStream::read(void* buffer, size_t length, size_t& outBytesRead) { - auto bytes = fread_s(buffer, length, 1, length, m_handle); - if (bytes == 0 && length > 0) + auto bytesRead = fread_s(buffer, length, 1, length, m_handle); + + outBytesRead = bytesRead; + if (bytesRead == 0 && length > 0) { - if (!feof(m_handle)) - throw IOException("FileStream read failed."); - else if (m_endReached) - throw EndOfStreamException("End of file is reached."); - m_endReached = true; + // If we have reached the end, then reading nothing is ok. + if (!m_endReached) + { + // If we are not at the end of the file we should be able to read some bytes + if (!feof(m_handle)) + { + return SLANG_FAIL; + } + m_endReached = true; + } } - return bytes; + return SLANG_OK; } -size_t FileStream::write(const void* buffer, size_t length) +SlangResult FileStream::write(const void* buffer, size_t length) { - auto bytes = fwrite(buffer, 1, length, m_handle); - if (bytes < length) - { - throw IOException("FileStream write failed."); - } - return bytes; + auto bytesWritten = fwrite(buffer, 1, length, m_handle); + return (bytesWritten == length) ? SLANG_OK : SLANG_FAIL; } bool FileStream::canRead() @@ -235,7 +265,7 @@ void FileStream::close() if (m_handle) { fclose(m_handle); - m_handle = 0; + m_handle = nullptr; // If closed, can neither read or write m_fileAccess = FileAccess::None; @@ -249,7 +279,7 @@ bool FileStream::isEnd() // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! MemoryStreamBase !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -void MemoryStreamBase::seek(SeekOrigin origin, Int64 offset) +SlangResult MemoryStreamBase::seek(SeekOrigin origin, Int64 offset) { Int64 pos = 0; switch (origin) @@ -264,8 +294,8 @@ void MemoryStreamBase::seek(SeekOrigin origin, Int64 offset) pos = Int64(m_position) + offset; break; default: - throw NotSupportedException("Unsupported seek origin."); - break; + SLANG_ASSERT(!"Unsupported seek origin."); + return SLANG_E_NOT_IMPLEMENTED; } m_atEnd = false; @@ -275,36 +305,43 @@ void MemoryStreamBase::seek(SeekOrigin origin, Int64 offset) pos = (pos > Int64(m_contentsSize)) ? Int64(m_contentsSize) : pos; m_position = ptrdiff_t(pos); + return SLANG_OK; } -size_t MemoryStreamBase::read(void* buffer, size_t length) +SlangResult MemoryStreamBase::read(void* buffer, size_t length, size_t& outReadBytes) { + outReadBytes = 0; if (!canRead()) { - throw IOException("Cannot read this stream."); + SLANG_ASSERT(!"Cannot read this stream."); + return SLANG_FAIL; } const size_t maxRead = size_t(m_contentsSize - m_position); if (maxRead == 0 && length > 0) { - m_atEnd = true; - throw EndOfStreamException("End of file is reached."); + // At end of stream + m_atEnd = true; + return SLANG_OK; } length = length > maxRead ? maxRead : length; ::memcpy(buffer, m_contents + m_position, length); m_position += ptrdiff_t(length); - return maxRead; + outReadBytes = length; + + return SLANG_OK; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!! OwnedMemoryStream !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -size_t OwnedMemoryStream::write(const void * buffer, size_t length) +SlangResult OwnedMemoryStream::write(const void * buffer, size_t length) { if (!canWrite()) { - throw IOException("Cannot write this stream."); + SLANG_ASSERT(!"Cannot write this stream."); + return SLANG_FAIL; } if (m_position == m_ownedContents.getCount()) @@ -322,7 +359,7 @@ size_t OwnedMemoryStream::write(const void * buffer, size_t length) m_atEnd = false; m_position += ptrdiff_t(length); - return length; + return SLANG_OK; } } // namespace Slang diff --git a/source/core/slang-stream.h b/source/core/slang-stream.h index 730ce70be..e33e1c601 100644 --- a/source/core/slang-stream.h +++ b/source/core/slang-stream.h @@ -6,45 +6,45 @@ namespace Slang { -class IOException : public Exception -{ -public: - IOException() - {} - IOException(const String & message) - : Slang::Exception(message) - { - } -}; - -class EndOfStreamException : public IOException -{ -public: - EndOfStreamException() - {} - EndOfStreamException(const String & message) - : IOException(message) - { - } -}; - enum class SeekOrigin { - Start, End, Current + Start, ///< Seek from the start of the stream + End, ///< Seek from the end of the stream + Current, ///< Seek from the current cursor position }; class Stream : public RefObject { public: virtual ~Stream() {} + /// Get the current 'cursor' position in the stream virtual Int64 getPosition()=0; - virtual void seek(SeekOrigin origin, Int64 offset)=0; - virtual size_t read(void * buffer, size_t length) = 0; - virtual size_t write(const void * buffer, size_t length) = 0; + /// Seek the cursor to a position. How the seek is performed is dependent on the 'origin' and the offset required. + /// NOTE that *any* seek will reset the 'end of stream' status. See 'read' for requirements for 'isEnd' to be reached. + virtual SlangResult seek(SeekOrigin origin, Int64 offset)=0; + /// Read from the current position into buffer. + /// If there are less bytes available than requested only the amount available will be read. outReadBytes holds the actual amount of bytes read. + /// It is valid (and not an error) for read to return 0 bytes read - even if the end of the stream. + /// + /// 'isEnd' only becomes true when a read is performed *past* the end of a stream. + /// If a non zero read is performed from the end then isEnd must be true. + /// + /// Will return an error if there is a reading failure. + virtual SlangResult read(void* buffer, size_t length, size_t& outReadBytes) = 0; + /// Write to the stream from current position + virtual SlangResult write(const void* buffer, size_t length) = 0; + /// True if the of the stream has been hit. The 'read' method has more discussion as to when this can occur. virtual bool isEnd() = 0; + /// Returns true if it's possible to read from the stream. virtual bool canRead() = 0; + /// Returns true when it's possible to write to the stream. virtual bool canWrite() = 0; + /// Close the stream. Once closed no more operations can be performed on the stream. + /// Implies any pending data is flushed. virtual void close() = 0; + + /// Helper function that will also *fail* if the specified amount of bytes aren't read. + SlangResult readExactly(void* buffer, size_t length); }; enum class FileMode @@ -69,9 +69,9 @@ public: typedef Stream Super; virtual Int64 getPosition() SLANG_OVERRIDE { return m_position; } - virtual void seek(SeekOrigin origin, Int64 offset) SLANG_OVERRIDE; - virtual size_t read(void * buffer, size_t length) SLANG_OVERRIDE; - virtual size_t write(const void * buffer, size_t length) SLANG_OVERRIDE { SLANG_UNUSED(buffer); SLANG_UNUSED(length); return 0; } + virtual SlangResult seek(SeekOrigin origin, Int64 offset) SLANG_OVERRIDE; + virtual SlangResult read(void * buffer, size_t length, size_t& outReadByts) SLANG_OVERRIDE; + virtual SlangResult write(const void * buffer, size_t length) SLANG_OVERRIDE { SLANG_UNUSED(buffer); SLANG_UNUSED(length); return SLANG_E_NOT_IMPLEMENTED; } virtual bool isEnd() SLANG_OVERRIDE { return m_atEnd; } virtual bool canRead() SLANG_OVERRIDE { return (int(m_access) & int(FileAccess::Read)) != 0; } virtual bool canWrite() SLANG_OVERRIDE { return (int(m_access) & int(FileAccess::Write)) != 0; } @@ -120,7 +120,7 @@ class OwnedMemoryStream : public MemoryStreamBase public: typedef MemoryStreamBase Super; - virtual size_t write(const void* buffer, size_t length) SLANG_OVERRIDE; + virtual SlangResult write(const void* buffer, size_t length) SLANG_OVERRIDE; /// Set the contents void setContent(const void* contents, size_t contentsSize) @@ -151,21 +151,24 @@ public: typedef Stream Super; // Stream interface - virtual Int64 getPosition(); - virtual void seek(SeekOrigin origin, Int64 offset); - virtual size_t read(void* buffer, size_t length); - virtual size_t write(const void* buffer, size_t length); - virtual bool canRead(); - virtual bool canWrite(); - virtual void close(); - virtual bool isEnd(); - - FileStream(const String& fileName, FileMode fileMode = FileMode::Open); - FileStream(const String& fileName, FileMode fileMode, FileAccess access, FileShare share); + virtual Int64 getPosition() SLANG_OVERRIDE; + virtual SlangResult seek(SeekOrigin origin, Int64 offset) SLANG_OVERRIDE; + virtual SlangResult read(void* buffer, size_t length, size_t& outReadBytes) SLANG_OVERRIDE; + virtual SlangResult write(const void* buffer, size_t length) SLANG_OVERRIDE; + virtual bool canRead() SLANG_OVERRIDE; + virtual bool canWrite() SLANG_OVERRIDE; + virtual void close() SLANG_OVERRIDE; + virtual bool isEnd() SLANG_OVERRIDE; + + FileStream(); + + SlangResult init(const String& fileName, FileMode fileMode, FileAccess access, FileShare share); + SlangResult init(const String& fileName, FileMode fileMode = FileMode::Open); + ~FileStream(); private: - void _init(const String& fileName, FileMode fileMode, FileAccess access, FileShare share); + SlangResult _init(const String& fileName, FileMode fileMode, FileAccess access, FileShare share); FILE* m_handle; FileAccess m_fileAccess; diff --git a/source/core/slang-string-escape-util.cpp b/source/core/slang-string-escape-util.cpp index ffc43a7cb..513908c4c 100644 --- a/source/core/slang-string-escape-util.cpp +++ b/source/core/slang-string-escape-util.cpp @@ -351,8 +351,7 @@ SlangResult CppStringEscapeHandler::appendUnescaped(const UnownedStringSlice& sl const Index maxUtf8EncodeCount = 6; char* chars = out.prepareForAppend(maxUtf8EncodeCount); - - int numChars = EncodeUnicodePointToUTF8(chars, int(value)); + int numChars = encodeUnicodePointToUTF8(Char32(value), chars); out.appendInPlace(chars, numChars); start = cur; @@ -812,7 +811,7 @@ SlangResult JSONStringEscapeHandler::appendUnescaped(const UnownedStringSlice& s // Need to encode in UTF8 to concat char buf[8]; - int len = EncodeUnicodePointToUTF8(buf, value); + int len = encodeUnicodePointToUTF8(Char32(value), buf); out.append(buf, buf + len); diff --git a/source/core/slang-string.cpp b/source/core/slang-string.cpp index 0a5b7d260..e21333809 100644 --- a/source/core/slang-string.cpp +++ b/source/core/slang-string.cpp @@ -11,15 +11,6 @@ namespace Slang // for anything that uses core static const auto s_charUtilLink = CharUtil::_ensureLink(); - // TODO: this belongs in a different file: - - SLANG_RETURN_NEVER void signalUnexpectedError(char const* message) - { - // Can be useful to uncomment during debug when problem is on CI - // printf("Unexpected: %s\n", message); - throw InternalError(message); - } - // OSString OSString::OSString() @@ -120,6 +111,14 @@ namespace Slang return UnownedStringSlice(start, end); } + UnownedStringSlice UnownedStringSlice::trimStart() const + { + const char* start = m_begin; + + while (start < m_end && CharUtil::isHorizontalWhitespace(*start)) start++; + return UnownedStringSlice(start, m_end); + } + UnownedStringSlice UnownedStringSlice::trim(char c) const { const char* start = m_begin; @@ -217,37 +216,42 @@ namespace Slang String String::fromWString(const wchar_t * wstr) { + List<char> buf; #ifdef _WIN32 - return Slang::Encoding::UTF16->ToString((const char*)wstr, (int)(wcslen(wstr) * sizeof(wchar_t))); + Slang::CharEncoding::UTF16->decode((const Byte*)wstr, (int)(wcslen(wstr) * sizeof(wchar_t)), buf); #else - return Slang::Encoding::UTF32->ToString((const char*)wstr, (int)(wcslen(wstr) * sizeof(wchar_t))); + Slang::CharEncoding::UTF32->decode((const Byte*)wstr, (int)(wcslen(wstr) * sizeof(wchar_t)), buf); #endif + return String(buf.begin(), buf.end()); } String String::fromWString(const wchar_t * wstr, const wchar_t * wend) { + List<char> buf; #ifdef _WIN32 - return Slang::Encoding::UTF16->ToString((const char*)wstr, (int)((wend - wstr) * sizeof(wchar_t))); + Slang::CharEncoding::UTF16->decode((const Byte*)wstr, (int)((wend - wstr) * sizeof(wchar_t)), buf); #else - return Slang::Encoding::UTF32->ToString((const char*)wstr, (int)((wend - wstr) * sizeof(wchar_t))); + Slang::CharEncoding::UTF32->decode((const Byte*)wstr, (int)((wend - wstr) * sizeof(wchar_t)), buf); #endif + return String(buf.begin(), buf.end()); } String String::fromWChar(const wchar_t ch) { + List<char> buf; #ifdef _WIN32 - return Slang::Encoding::UTF16->ToString((const char*)&ch, (int)(sizeof(wchar_t))); + Slang::CharEncoding::UTF16->decode((const Byte*)&ch, (int)(sizeof(wchar_t)), buf); #else - return Slang::Encoding::UTF32->ToString((const char*)&ch, (int)(sizeof(wchar_t))); + Slang::CharEncoding::UTF32->decode((const Byte*)&ch, (int)(sizeof(wchar_t)), buf); #endif + return String(buf.begin(), buf.end()); } - String String::fromUnicodePoint(unsigned int codePoint) + /* static */String String::fromUnicodePoint(Char32 codePoint) { char buf[6]; - int len = Slang::EncodeUnicodePointToUTF8(buf, (int)codePoint); - buf[len] = 0; - return String(buf); + int len = Slang::encodeUnicodePointToUTF8(codePoint, buf); + return String(buf, buf + len); } OSString String::toWString(Index* outLength) const @@ -258,15 +262,15 @@ namespace Slang } else { - List<char> buf; + List<Byte> buf; switch(sizeof(wchar_t)) { case 2: - Slang::Encoding::UTF16->GetBytes(buf, *this); + Slang::CharEncoding::UTF16->encode(getUnownedSlice(), buf); break; case 4: - Slang::Encoding::UTF32->GetBytes(buf, *this); + Slang::CharEncoding::UTF32->encode(getUnownedSlice(), buf); break; default: diff --git a/source/core/slang-string.h b/source/core/slang-string.h index 6b9a73bcb..85f7b894f 100644 --- a/source/core/slang-string.h +++ b/source/core/slang-string.h @@ -168,6 +168,9 @@ namespace Slang /// Trims any 'c' from the start or the end, and returns as a substring UnownedStringSlice trim(char c) const; + /// Trims any horizonatl whitespace from start and returns as a substring + UnownedStringSlice trimStart() const; + HashCode getHashCode() const { return Slang::getHashCode(m_begin, size_t(m_end - m_begin)); @@ -409,7 +412,7 @@ namespace Slang static String fromWString(const wchar_t * wstr); static String fromWString(const wchar_t * wstr, const wchar_t * wend); static String fromWChar(const wchar_t ch); - static String fromUnicodePoint(unsigned int codePoint); + static String fromUnicodePoint(Char32 codePoint); String() { } @@ -619,9 +622,9 @@ namespace Slang len = getLength() - id; #if _DEBUG if (id < 0 || id >= getLength() || (id + len) > getLength()) - throw "SubString: index out of range."; + SLANG_ASSERT_FAILURE("SubString: index out of range."); if (len < 0) - throw "SubString: length less than zero."; + SLANG_ASSERT_FAILURE("SubString: length less than zero."); #endif return StringSlice(m_buffer, id, id + len); } @@ -997,9 +1000,9 @@ namespace Slang { #if _DEBUG if (id >= length || id < 0) - throw "Remove: Index out of range."; + SLANG_ASSERT_FAILURE("Remove: Index out of range."); if (len < 0) - throw "Remove: remove length smaller than zero."; + SLANG_ASSERT_FAILURE("Remove: remove length smaller than zero."); #endif int actualDelLength = ((id + len) >= length) ? (length - id) : len; for (int i = id + actualDelLength; i <= length; i++) diff --git a/source/core/slang-text-io.cpp b/source/core/slang-text-io.cpp index ca3b9447c..3f5d739d9 100644 --- a/source/core/slang-text-io.cpp +++ b/source/core/slang-text-io.cpp @@ -1,343 +1,163 @@ #include "slang-text-io.h" -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include <Windows.h> -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX -#define CONVERT_END_OF_LINE -#endif + +#include "../../slang-com-helper.h" namespace Slang { - class Utf8Encoding : public Encoding - { - public: - virtual void GetBytes(List<char> & result, const String & str) override - { - result.addRange(str.getBuffer(), str.getLength()); - } - virtual String ToString(const char * bytes, int /*length*/) override - { - return String(bytes); - } - }; - class Utf32Encoding : public Encoding - { - public: - virtual void GetBytes(List<char> & result, const String & str) override - { - Index ptr = 0; - while (ptr < str.getLength()) - { - int codePoint = GetUnicodePointFromUTF8([&](int) - { - if (ptr < str.getLength()) - return str[ptr++]; - else - return '\0'; - }); - result.addRange((char*)&codePoint, 4); - } - } - virtual String ToString(const char * bytes, int length) override - { - StringBuilder sb; - int * content = (int*)bytes; - for (int i = 0; i < (length >> 2); i++) - { - char buf[5]; - int count = EncodeUnicodePointToUTF8(buf, content[i]); - for (int j = 0; j < count; j++) - sb.Append(buf[j]); - } - return sb.ProduceString(); - } - }; +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! StreamWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - class Utf16Encoding : public Encoding //UTF16 - { - private: - bool reverseOrder = false; - public: - Utf16Encoding(bool pReverseOrder) - : reverseOrder(pReverseOrder) - {} - virtual void GetBytes(List<char> & result, const String & str) override - { - Index ptr = 0; - while (ptr < str.getLength()) - { - int codePoint = GetUnicodePointFromUTF8([&](int) - { - if (ptr < str.getLength()) - return str[ptr++]; - else - return '\0'; - }); - unsigned short buffer[2]; - int count; - if (!reverseOrder) - count = EncodeUnicodePointToUTF16(buffer, codePoint); - else - count = EncodeUnicodePointToUTF16Reversed(buffer, codePoint); - result.addRange((char*)buffer, count * 2); - } - } - virtual String ToString(const char * bytes, int length) override - { - int ptr = 0; - StringBuilder sb; - while (ptr < length) - { - int codePoint = GetUnicodePointFromUTF16([&](int) - { - if (ptr < length) - return bytes[ptr++]; - else - return '\0'; - }); - char buf[5]; - int count = EncodeUnicodePointToUTF8(buf, codePoint); - for (int i = 0; i < count; i++) - sb.Append(buf[i]); - } - return sb.ProduceString(); - } - }; - - Utf8Encoding __utf8Encoding; - Utf16Encoding __utf16Encoding(false); - Utf16Encoding __utf16EncodingReversed(true); - Utf32Encoding __utf32Encoding; - - Encoding * Encoding::UTF8 = &__utf8Encoding; - Encoding * Encoding::UTF16 = &__utf16Encoding; - Encoding * Encoding::UTF16Reversed = &__utf16EncodingReversed; - Encoding * Encoding::UTF32 = &__utf32Encoding; - - const unsigned short Utf16Header = 0xFEFF; - const unsigned short Utf16ReversedHeader = 0xFFFE; +SlangResult StreamWriter::init(const String& path, CharEncoding* encoding) +{ + RefPtr<FileStream> fileStream = new FileStream; + SLANG_RETURN_ON_FAIL(fileStream->init(path, FileMode::Create)); + return init(fileStream, encoding); +} - StreamWriter::StreamWriter(const String & path, Encoding * encoding) +SlangResult StreamWriter::init(RefPtr<Stream> stream, CharEncoding* encoding) +{ + m_stream = stream; + m_encoding = encoding; + if (encoding == CharEncoding::UTF16) { - this->stream = new FileStream(path, FileMode::Create); - this->encoding = encoding; - if (encoding == Encoding::UTF16) - { - this->stream->write(&Utf16Header, 2); - } - else if (encoding == Encoding::UTF16Reversed) - { - this->stream->write(&Utf16ReversedHeader, 2); - } + SLANG_RETURN_ON_FAIL(m_stream->write(&kUTF16Header, 2)); } - StreamWriter::StreamWriter(RefPtr<Stream> stream, Encoding * encoding) + else if (encoding == CharEncoding::UTF16Reversed) { - this->stream = stream; - this->encoding = encoding; - if (encoding == Encoding::UTF16) - { - this->stream->write(&Utf16Header, 2); - } - else if (encoding == Encoding::UTF16Reversed) - { - this->stream->write(&Utf16ReversedHeader, 2); - } + SLANG_RETURN_ON_FAIL(m_stream->write(&kUTF16ReversedHeader, 2)); } - void StreamWriter::Write(const String & str) - { - encodingBuffer.clear(); - StringBuilder sb; - String newLine; + + return SLANG_OK; +} + +SlangResult StreamWriter::writeSlice(const UnownedStringSlice& slice) +{ + // TODO(JS): + // We can do better here. On Linux, this is a no-op and can just write directly (assuming slice only contains \n) + + m_encodingBuffer.clear(); + + StringBuilder sb; #ifdef _WIN32 - newLine = "\r\n"; + const char newLine[] = "\r\n"; #else - newLine = "\n"; + const char newLine[] = "\n"; #endif - for (Index i = 0; i < str.getLength(); i++) + const Index length = slice.getLength(); + + for (Index i = 0; i < length; i++) + { + if (slice[i] == '\r') + sb << newLine; + else if (slice[i] == '\n') { - if (str[i] == '\r') + if (i > 0 && slice[i - 1] != '\r') sb << newLine; - else if (str[i] == '\n') - { - if (i > 0 && str[i - 1] != '\r') - sb << newLine; - } - else - sb << str[i]; } - encoding->GetBytes(encodingBuffer, sb.ProduceString()); - stream->write(encodingBuffer.getBuffer(), encodingBuffer.getCount()); - } - void StreamWriter::Write(const char * str) - { - Write(String(str)); + else + sb << slice[i]; } - StreamReader::StreamReader(const String & path) - { - stream = new FileStream(path, FileMode::Open); - ReadBuffer(); - encoding = DetermineEncoding(); - if (encoding == 0) - encoding = Encoding::UTF8; - } - StreamReader::StreamReader(RefPtr<Stream> stream, Encoding * encoding) - { - this->stream = stream; - this->encoding = encoding; - ReadBuffer(); - auto determinedEncoding = DetermineEncoding(); - if (this->encoding == nullptr) - this->encoding = determinedEncoding; - } + // NOTE! This assumes that sb contains *complete* utf8 code points, which it might not, as encoder is only able to handle complete code points. + m_encodingBuffer.clear(); + m_encoding->encode(sb.getUnownedSlice(), m_encodingBuffer); + return m_stream->write(m_encodingBuffer.getBuffer(), m_encodingBuffer.getCount()); +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! StreamReader !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +StreamReader::StreamReader() +{ +} + +SlangResult StreamReader::init(const String& path) +{ + RefPtr<FileStream> fileStream = new FileStream; + SLANG_RETURN_ON_FAIL(fileStream->init(path, FileMode::Open)); + return init(fileStream); +} - bool HasNullBytes(char * str, int len) +SlangResult StreamReader::init(RefPtr<Stream> stream, CharEncoding* encoding) +{ + m_stream = stream; + m_encoding = encoding; + SLANG_RETURN_ON_FAIL(readBuffer()); + + if (encoding == nullptr) { - bool hasSeenNull = false; - for (int i = 0; i < len - 1; i++) - if (str[i] == 0) - hasSeenNull = true; - else if (hasSeenNull) - return true; - return false; + size_t offset; + m_encodingType = CharEncoding::determineEncoding((const Byte*)m_buffer.getBuffer(), m_buffer.getCount(), offset); + m_encoding = CharEncoding::getEncoding(m_encodingType); + m_index = Index(offset); + } + else + { + m_encodingType = encoding->getEncodingType(); + m_encoding = encoding; } - Encoding * StreamReader::DetermineEncoding() - { - if (buffer.getCount() >= 3 && (unsigned char)(buffer[0]) == 0xEF && (unsigned char)(buffer[1]) == 0xBB && (unsigned char)(buffer[2]) == 0xBF) - { - ptr += 3; - return Encoding::UTF8; - } - else if (*((unsigned short*)(buffer.getBuffer())) == 0xFEFF) - { - ptr += 2; - return Encoding::UTF16; - } - else if (*((unsigned short*)(buffer.getBuffer())) == 0xFFFE) - { - ptr += 2; - return Encoding::UTF16Reversed; - } - else - { - // find null bytes - if (HasNullBytes(buffer.getBuffer(), (int)buffer.getCount())) - { - return Encoding::UTF16; - } - return Encoding::UTF8; - } - } + return SLANG_OK; +} - void StreamReader::ReadBuffer() - { - buffer.setCount(4096); - memset(buffer.getBuffer(), 0, buffer.getCount() * sizeof(buffer[0])); - auto len = stream->read(buffer.getBuffer(), buffer.getCount()); - buffer.setCount((int)len); - ptr = 0; - } +SlangResult StreamReader::readBuffer() +{ + m_buffer.setCount(0); + m_index = 0; - char StreamReader::ReadBufferChar() - { - if (ptr<buffer.getCount()) - { - return buffer[ptr++]; - } - if (!stream->isEnd()) - ReadBuffer(); - if (ptr<buffer.getCount()) - { - return buffer[ptr++]; - } - return 0; - } - int TextReader::Read(char * destBuffer, int length) + if (m_stream->isEnd()) + { + return SLANG_OK; + } + + m_buffer.setCount(4096); + + // TODO(JS): Not clear this is necessary + memset(m_buffer.getBuffer(), 0, m_buffer.getCount() * sizeof(m_buffer[0])); + + size_t readBytes; + SLANG_RETURN_ON_FAIL(m_stream->read(m_buffer.getBuffer(), m_buffer.getCount(), readBytes)); + + m_buffer.setCount(Index(readBytes)); + m_index = 0; + return SLANG_OK; +} + +char StreamReader::readBufferChar() +{ + if (m_index < m_buffer.getCount()) { - int i = 0; - for (i = 0; i<length; i++) - { - try - { - auto ch = Read(); - if (IsEnd()) - break; - if (ch == '\r') - { - if (Peak() == '\n') - Read(); - break; - } - else if (ch == '\n') - { - break; - } - destBuffer[i] = ch; - } - catch (const EndOfStreamException& ) - { - break; - } - } - return i; + return m_buffer[m_index++]; } - String StreamReader::ReadLine() + + readBuffer(); + + if (m_index < m_buffer.getCount()) { - StringBuilder sb(256); - while (!IsEnd()) - { - try - { - auto ch = Read(); - if (IsEnd()) - break; - if (ch == '\r') - { - if (Peak() == '\n') - Read(); - break; - } - else if (ch == '\n') - { - break; - } - sb.Append(ch); - } - catch (const EndOfStreamException&) - { - break; - } - } - return sb.ProduceString(); + return m_buffer[m_index++]; } - String StreamReader::ReadToEnd() + return 0; +} + +SlangResult StreamReader::readToEnd(String& outString) +{ + StringBuilder sb(16384); + while (!isEnd()) { - StringBuilder sb(16384); - while (!IsEnd()) + auto ch = read(); + if (isEnd()) + break; + if (ch == '\r') { - try - { - auto ch = Read(); - if (IsEnd()) - break; - if (ch == '\r') - { - sb.Append('\n'); - if (Peak() == '\n') - Read(); - } - else - sb.Append(ch); - } - catch (const EndOfStreamException&) - { - break; - } + sb.Append('\n'); + if (peek() == '\n') + read(); } - return sb.ProduceString(); + else + sb.Append(ch); } + + outString = sb.ProduceString(); + return SLANG_OK; } + +} // namespace Slang diff --git a/source/core/slang-text-io.h b/source/core/slang-text-io.h index b5a9ad0e1..0ee381c46 100644 --- a/source/core/slang-text-io.h +++ b/source/core/slang-text-io.h @@ -3,314 +3,184 @@ #include "slang-secure-crt.h" #include "slang-stream.h" +#include "slang-char-encode.h" namespace Slang { - using Slang::List; - using Slang::_EndLine; +using Slang::List; +using Slang::_EndLine; - class TextReader - { - protected: - char decodedChar[5]; - int decodedCharPtr = 0, decodedCharSize = 0; - virtual void ReadChar() = 0; - public: - virtual ~TextReader() - { - Close(); - } - virtual void Close(){} - virtual String ReadLine()=0; - virtual String ReadToEnd()=0; - virtual bool IsEnd() = 0; - int Read(char * buffer, int count); - char Read() - { - if (decodedCharPtr == decodedCharSize) - ReadChar(); - if (decodedCharPtr < decodedCharSize) - return decodedChar[decodedCharPtr++]; - else - return 0; - } - char Peak() - { - if (decodedCharPtr == decodedCharSize) - ReadChar(); - if (decodedCharPtr < decodedCharSize) - return decodedChar[decodedCharPtr]; - else - return 0; - } - }; - - class TextWriter - { - public: - virtual ~TextWriter() - { - Close(); - } - virtual void Write(const String & str)=0; - virtual void Write(const char * str)=0; - virtual void Close(){} - template<typename T> - TextWriter & operator << (const T& val) - { - Write(val.ToString()); - return *this; - } - TextWriter & operator << (int value) - { - Write(String(value)); - return *this; - } - TextWriter & operator << (float value) - { - Write(String(value)); - return *this; - } - TextWriter & operator << (double value) - { - Write(String(value)); - return *this; - } - TextWriter & operator << (const char* value) - { - Write(value); - return *this; - } - TextWriter & operator << (const String & val) - { - Write(val); - return *this; - } - TextWriter & operator << (const _EndLine &) - { -#ifdef _WIN32 - Write("\r\n"); -#else - Write("\n"); -#endif - return *this; - } - }; +class TextReader +{ +public: + virtual void close(){} + virtual SlangResult readToEnd(String& outString) = 0; + virtual bool isEnd() = 0; - template <typename ReadCharFunc> - int GetUnicodePointFromUTF8(const ReadCharFunc & get) + char read() { - int codePoint = 0; - int leading = get(0); - int mask = 0x80; - int count = 0; - while (leading & mask) - { - count++; - mask >>= 1; - } - codePoint = (leading & (mask - 1)); - for (int i = 1; i <= count - 1; i++) - { - codePoint <<= 6; - codePoint += (get(i) & 0x3F); - } - return codePoint; + if (m_decodedCharIndex == m_decodedCharSize) + readChar(); + if (m_decodedCharIndex < m_decodedCharSize) + return m_decodedChar[m_decodedCharIndex++]; + else + return 0; } - - template <typename ReadCharFunc> - int GetUnicodePointFromUTF16(const ReadCharFunc & get) + char peek() { - int byte0 = (unsigned char)get(0); - int byte1 = (unsigned char)get(1); - int word0 = byte0 + (byte1 << 8); - if (word0 >= 0xD800 && word0 <= 0xDFFF) - { - int byte2 = (unsigned char)get(2); - int byte3 = (unsigned char)get(3); - int word1 = byte2 + (byte3 << 8); - return ((word0 & 0x3FF) << 10) + (word1 & 0x3FF) + 0x10000; - } + if (m_decodedCharIndex == m_decodedCharSize) + readChar(); + if (m_decodedCharIndex < m_decodedCharSize) + return m_decodedChar[m_decodedCharIndex]; else - return word0; + return 0; } - template <typename ReadCharFunc> - int GetUnicodePointFromUTF16Reversed(const ReadCharFunc & get) + virtual ~TextReader() { close(); } + +protected: + char m_decodedChar[5]; + Index m_decodedCharIndex = 0; + Index m_decodedCharSize = 0; + + virtual void readChar() = 0; +}; + + +class StreamReader : public TextReader +{ +public: + virtual SlangResult readToEnd(String& outString) SLANG_OVERRIDE; + virtual bool isEnd() SLANG_OVERRIDE { return m_index == m_buffer.getCount() && m_stream->isEnd(); } + virtual void close() SLANG_OVERRIDE { m_stream->close(); } + + void releaseStream() { m_stream.setNull(); } + + StreamReader(); + + SlangResult init(const String& path); + SlangResult init(RefPtr<Stream> stream, CharEncoding* encoding = nullptr); + +protected: + virtual void readChar() SLANG_OVERRIDE + { + m_decodedCharIndex = 0; + + Char32 codePoint = 0; + switch (m_encodingType) + { + case CharEncodeType::UTF8: + { + codePoint = getUnicodePointFromUTF8([&]() -> Byte {return readBufferChar(); }); + break; + } + case CharEncodeType::UTF16: + { + codePoint = getUnicodePointFromUTF16([&]() -> Byte {return readBufferChar(); }); + break; + } + case CharEncodeType::UTF16Reversed: + { + codePoint = getUnicodePointFromUTF16Reversed([&]() -> Byte {return readBufferChar(); }); + break; + } + case CharEncodeType::UTF32: + { + codePoint = getUnicodePointFromUTF32([&]() -> Byte {return readBufferChar(); }); + break; + } + } + + m_decodedCharSize = encodeUnicodePointToUTF8(codePoint, m_decodedChar); + } + +private: + char readBufferChar(); + SlangResult readBuffer(); + + RefPtr<Stream> m_stream; + List<char> m_buffer; + + CharEncodeType m_encodingType = CharEncodeType::UTF8; + CharEncoding* m_encoding = nullptr; + Index m_index = 0; ///< Index into buffer +}; + +class TextWriter +{ +public: + + virtual SlangResult writeSlice(const UnownedStringSlice& slice) = 0; + virtual void close(){} + + SlangResult write(const UnownedStringSlice& slice) { return writeSlice(slice); } + SlangResult write(const char* str) { return writeSlice(UnownedStringSlice(str)); } + SlangResult write(const String& str) { return writeSlice(str.getUnownedSlice()); } + + virtual ~TextWriter() { close(); } + + template<typename T> + TextWriter& operator << (const T& val) { - int byte0 = (unsigned char)get(0); - int byte1 = (unsigned char)get(1); - int word0 = (byte0 << 8) + byte1; - if (word0 >= 0xD800 && word0 <= 0xDFFF) - { - int byte2 = (unsigned char)get(2); - int byte3 = (unsigned char)get(3); - int word1 = (byte2 << 8) + byte3; - return ((word0 & 0x3FF) << 10) + (word1 & 0x3FF); - } - else - return word0; + write(val.ToString()); + return *this; } - - template <typename ReadCharFunc> - int GetUnicodePointFromUTF32(const ReadCharFunc & get) + TextWriter& operator << (int value) { - int byte0 = (unsigned char)get(0); - int byte1 = (unsigned char)get(1); - int byte2 = (unsigned char)get(2); - int byte3 = (unsigned char)get(3); - return byte0 + (byte1 << 8) + (byte2 << 16) + (byte3 << 24); + write(String(value)); + return *this; } - - inline int EncodeUnicodePointToUTF8(char * buffer, int codePoint) + TextWriter& operator << (float value) { - int count = 0; - if (codePoint <= 0x7F) - buffer[count++] = ((char)codePoint); - else if (codePoint <= 0x7FF) - { - unsigned char byte = (unsigned char)(0xC0 + (codePoint >> 6)); - buffer[count++] = ((char)byte); - byte = 0x80 + (codePoint & 0x3F); - buffer[count++] = ((char)byte); - } - else if (codePoint <= 0xFFFF) - { - unsigned char byte = (unsigned char)(0xE0 + (codePoint >> 12)); - buffer[count++] = ((char)byte); - byte = (unsigned char)(0x80 + ((codePoint >> 6) & (0x3F))); - buffer[count++] = ((char)byte); - byte = (unsigned char)(0x80 + (codePoint & 0x3F)); - buffer[count++] = ((char)byte); - } - else - { - unsigned char byte = (unsigned char)(0xF0 + (codePoint >> 18)); - buffer[count++] = ((char)byte); - byte = (unsigned char)(0x80 + ((codePoint >> 12) & 0x3F)); - buffer[count++] = ((char)byte); - byte = (unsigned char)(0x80 + ((codePoint >> 6) & 0x3F)); - buffer[count++] = ((char)byte); - byte = (unsigned char)(0x80 + (codePoint & 0x3F)); - buffer[count++] = ((char)byte); - } - return count; + write(String(value)); + return *this; } - - inline int EncodeUnicodePointToUTF16(unsigned short * buffer, int codePoint) + TextWriter& operator << (double value) { - int count = 0; - if (codePoint <= 0xD7FF || (codePoint >= 0xE000 && codePoint <= 0xFFFF)) - buffer[count++] = (unsigned short)codePoint; - else - { - int sub = codePoint - 0x10000; - int high = (sub >> 10) + 0xD800; - int low = (sub & 0x3FF) + 0xDC00; - buffer[count++] = (unsigned short)high; - buffer[count++] = (unsigned short)low; - } - return count; + write(String(value)); + return *this; } - - inline unsigned short ReverseBitOrder(unsigned short val) + TextWriter& operator << (const char* value) { - int byte0 = val & 0xFF; - int byte1 = val >> 8; - return (unsigned short)(byte1 + (byte0 << 8)); + writeSlice(UnownedStringSlice(value)); + return *this; } - - inline int EncodeUnicodePointToUTF16Reversed(unsigned short * buffer, int codePoint) + TextWriter& operator << (const String & val) { - int count = 0; - if (codePoint <= 0xD7FF || (codePoint >= 0xE000 && codePoint <= 0xFFFF)) - buffer[count++] = ReverseBitOrder((unsigned short)codePoint); - else - { - int sub = codePoint - 0x10000; - int high = (sub >> 10) + 0xD800; - int low = (sub & 0x3FF) + 0xDC00; - buffer[count++] = ReverseBitOrder((unsigned short)high); - buffer[count++] = ReverseBitOrder((unsigned short)low); - } - return count; + writeSlice(val.getUnownedSlice()); + return *this; } - - class Encoding + TextWriter& operator << (const _EndLine &) { - public: - static Encoding * UTF8, * UTF16, *UTF16Reversed, * UTF32; - virtual void GetBytes(List<char>& buffer, const String & str) = 0; - virtual String ToString(const char * buffer, int length) = 0; - virtual ~Encoding() - {} - }; +#ifdef _WIN32 + writeSlice(UnownedStringSlice::fromLiteral("\r\n")); +#else + writeSlice(UnownedStringSlice::fromLiteral("\n")); +#endif + return *this; + } +}; + +class StreamWriter : public TextWriter +{ +public: + // TextWriter + virtual SlangResult writeSlice(const UnownedStringSlice& slice) SLANG_OVERRIDE; + virtual void close() SLANG_OVERRIDE { m_stream->close(); } - class StreamWriter : public TextWriter - { - private: - List<char> encodingBuffer; - RefPtr<Stream> stream; - Encoding * encoding; - public: - StreamWriter(const String & path, Encoding * encoding = Encoding::UTF8); - StreamWriter(RefPtr<Stream> stream, Encoding * encoding = Encoding::UTF8); - virtual void Write(const String & str); - virtual void Write(const char * str); - virtual void Close() - { - stream->close(); - } - void ReleaseStream() - { - stream = 0; - } - }; + void releaseStream() { m_stream.setNull(); } + + StreamWriter() {} + + SlangResult init(const String& path, CharEncoding* encoding = CharEncoding::UTF8); + SlangResult init(RefPtr<Stream> stream, CharEncoding* encoding = CharEncoding::UTF8); + +private: + List<Byte> m_encodingBuffer; + RefPtr<Stream> m_stream; + CharEncoding* m_encoding = nullptr; +}; - class StreamReader : public TextReader - { - private: - RefPtr<Stream> stream; - List<char> buffer; - Encoding * encoding; - Index ptr; - char ReadBufferChar(); - void ReadBuffer(); - - Encoding * DetermineEncoding(); - protected: - virtual void ReadChar() - { - decodedCharPtr = 0; - int codePoint = 0; - if (encoding == Encoding::UTF8) - codePoint = GetUnicodePointFromUTF8([&](int) {return ReadBufferChar(); }); - else if (encoding == Encoding::UTF16) - codePoint = GetUnicodePointFromUTF16([&](int) {return ReadBufferChar(); }); - else if (encoding == Encoding::UTF16Reversed) - codePoint = GetUnicodePointFromUTF16Reversed([&](int) {return ReadBufferChar(); }); - else if (encoding == Encoding::UTF32) - codePoint = GetUnicodePointFromUTF32([&](int) {return ReadBufferChar(); }); - decodedCharSize = EncodeUnicodePointToUTF8(decodedChar, codePoint); - } - public: - StreamReader(const String & path); - StreamReader(RefPtr<Stream> stream, Encoding * encoding = nullptr); - virtual String ReadLine(); - virtual String ReadToEnd(); - virtual bool IsEnd() - { - return ptr == buffer.getCount() && stream->isEnd(); - } - virtual void Close() - { - stream->close(); - } - void ReleaseStream() - { - stream = 0; - } - }; } #endif |
