├── .gitattributes ├── .gitignore ├── Import ├── Vlpp.Linux.cpp ├── Vlpp.Windows.cpp ├── Vlpp.cpp └── Vlpp.h ├── LICENSE.md ├── README.md ├── Release ├── CodegenConfig.xml ├── IncludeOnly │ ├── VlppOS.Linux.cpp │ ├── VlppOS.Windows.cpp │ ├── VlppOS.cpp │ └── VlppOS.h ├── VlppOS.Linux.cpp ├── VlppOS.Windows.cpp ├── VlppOS.cpp └── VlppOS.h ├── Source ├── Encoding │ ├── Base64Encoding.cpp │ ├── Base64Encoding.h │ ├── CharFormat │ │ ├── BomEncoding.cpp │ │ ├── BomEncoding.h │ │ ├── CharFormat.Linux.cpp │ │ ├── CharFormat.Windows.cpp │ │ ├── CharFormat.cpp │ │ ├── CharFormat.h │ │ ├── MbcsEncoding.cpp │ │ ├── MbcsEncoding.h │ │ ├── UtfEncoding.cpp │ │ └── UtfEncoding.h │ ├── Encoding.cpp │ ├── Encoding.h │ ├── LzwEncoding.cpp │ └── LzwEncoding.h ├── FileSystem.Linux.cpp ├── FileSystem.Windows.cpp ├── FileSystem.cpp ├── FileSystem.h ├── HttpUtility.Windows.cpp ├── HttpUtility.h ├── Locale.Linux.cpp ├── Locale.Windows.cpp ├── Locale.cpp ├── Locale.h ├── Stream │ ├── Accessor.cpp │ ├── Accessor.h │ ├── BroadcastStream.cpp │ ├── BroadcastStream.h │ ├── CacheStream.cpp │ ├── CacheStream.h │ ├── EncodingStream.cpp │ ├── EncodingStream.h │ ├── FileStream.cpp │ ├── FileStream.h │ ├── Interfaces.h │ ├── MemoryStream.cpp │ ├── MemoryStream.h │ ├── MemoryWrapperStream.cpp │ ├── MemoryWrapperStream.h │ ├── RecorderStream.cpp │ ├── RecorderStream.h │ └── Serialization.h ├── Threading.Linux.cpp ├── Threading.Windows.cpp ├── Threading.cpp └── Threading.h ├── TODO.md └── Test ├── Linux ├── Main.cpp ├── makefile └── vmake ├── Source ├── TestFileSystem.cpp ├── TestLocale.cpp ├── TestLocaleString.cpp ├── TestSerialization.cpp ├── TestStream.cpp ├── TestStreamBase64.cpp ├── TestStreamEncoding.cpp ├── TestStreamLzw.cpp ├── TestStreamReaderWriter.cpp └── TestThread.cpp └── UnitTest ├── UnitTest.sln └── UnitTest ├── Main.cpp ├── UnitTest.vcxproj └── UnitTest.vcxproj.filters /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | #Vczh Libraries 5 | .Output/ 6 | Test/Output/ 7 | Test/Linux/Coverage/ 8 | Tools/*.exe 9 | Tools/*.dll 10 | 11 | # User-specific files 12 | */**/.vs/ 13 | *.suo 14 | *.user 15 | *.sln.docstates 16 | 17 | # Build results 18 | 19 | */**/[Dd]ebugNoReflection/ 20 | */**/[Dd]ebug/ 21 | */**/[Rr]elease/ 22 | */**/x64/ 23 | */**/build/ 24 | */**/[Bb]in/ 25 | */**/[Oo]bj/ 26 | 27 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 28 | !packages/*/build/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | *_i.c 35 | *_p.c 36 | *.ilk 37 | *.meta 38 | *.obj 39 | *.pch 40 | *.pdb 41 | *.pgc 42 | *.pgd 43 | *.rsp 44 | *.sbr 45 | *.tlb 46 | *.tli 47 | *.tlh 48 | *.tmp 49 | *.tmp_proj 50 | *.log 51 | *.vspscc 52 | *.vssscc 53 | .builds 54 | *.pidb 55 | *.log 56 | *.scc 57 | 58 | # Visual C++ cache files 59 | ipch/ 60 | *.aps 61 | *.ncb 62 | *.opensdf 63 | *.sdf 64 | *.cachefile 65 | *.VC.db 66 | *.VC.db-* 67 | *.VC.opendb 68 | *.VC.VC.opendb 69 | 70 | # Visual Studio profiler 71 | *.psess 72 | *.vsp 73 | *.vspx 74 | 75 | # Guidance Automation Toolkit 76 | *.gpState 77 | 78 | # ReSharper is a .NET coding add-in 79 | _ReSharper*/ 80 | *.[Rr]e[Ss]harper 81 | 82 | # TeamCity is a build add-in 83 | _TeamCity* 84 | 85 | # DotCover is a Code Coverage Tool 86 | *.dotCover 87 | 88 | # NCrunch 89 | *.ncrunch* 90 | .*crunch*.local.xml 91 | 92 | # Installshield output folder 93 | [Ee]xpress/ 94 | 95 | # DocProject is a documentation generator add-in 96 | DocProject/buildhelp/ 97 | DocProject/Help/*.HxT 98 | DocProject/Help/*.HxC 99 | DocProject/Help/*.hhc 100 | DocProject/Help/*.hhk 101 | DocProject/Help/*.hhp 102 | DocProject/Help/Html2 103 | DocProject/Help/html 104 | 105 | # Click-Once directory 106 | publish/ 107 | 108 | # Publish Web Output 109 | *.Publish.xml 110 | 111 | # NuGet Packages Directory 112 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 113 | #packages/ 114 | 115 | # Windows Azure Build Output 116 | csx 117 | *.build.csdef 118 | 119 | # Windows Store app package directory 120 | AppPackages/ 121 | 122 | # Others 123 | sql/ 124 | *.Cache 125 | ClientBin/ 126 | [Ss]tyle[Cc]op.* 127 | ~$* 128 | *~ 129 | *.dbmdl 130 | *.[Pp]ublish.xml 131 | *.pfx 132 | *.publishsettings 133 | 134 | # RIA/Silverlight projects 135 | Generated_Code/ 136 | 137 | # Backup & report files from converting an old project file to a newer 138 | # Visual Studio version. Backup files are not needed, because we have git ;-) 139 | _UpgradeReport_Files/ 140 | Backup*/ 141 | UpgradeLog*.XML 142 | UpgradeLog*.htm 143 | 144 | # SQL Server files 145 | App_Data/*.mdf 146 | App_Data/*.ldf 147 | 148 | 149 | #LightSwitch generated files 150 | GeneratedArtifacts/ 151 | _Pvt_Extensions/ 152 | ModelManifest.xml 153 | 154 | # ========================= 155 | # Windows detritus 156 | # ========================= 157 | 158 | # Windows image file caches 159 | Thumbs.db 160 | ehthumbs.db 161 | 162 | # Folder config file 163 | Desktop.ini 164 | 165 | # Recycle Bin used on file shares 166 | $RECYCLE.BIN/ 167 | 168 | # Mac desktop service store files 169 | .DS_Store 170 | -------------------------------------------------------------------------------- /Import/Vlpp.Linux.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY 3 | DEVELOPER: Zihan Chen(vczh) 4 | ***********************************************************************/ 5 | #include "Vlpp.h" 6 | 7 | /*********************************************************************** 8 | .\CONSOLE.LINUX.CPP 9 | ***********************************************************************/ 10 | /*********************************************************************** 11 | Author: Zihan Chen (vczh) 12 | Licensed under https://github.com/vczh-libraries/License 13 | ***********************************************************************/ 14 | 15 | #include 16 | #include 17 | 18 | #ifndef VCZH_GCC 19 | static_assert(false, "Do not build this file for Windows applications."); 20 | #endif 21 | 22 | namespace vl 23 | { 24 | namespace console 25 | { 26 | 27 | /*********************************************************************** 28 | Console 29 | ***********************************************************************/ 30 | 31 | void Console::Write(const wchar_t* string, vint length) 32 | { 33 | std::wstring s(string, string + length); 34 | std::wcout << s << std::ends; 35 | } 36 | 37 | WString Console::Read() 38 | { 39 | std::wstring s; 40 | std::getline(std::wcin, s, L'\n'); 41 | return s.c_str(); 42 | } 43 | 44 | void Console::SetColor(bool red, bool green, bool blue, bool light) 45 | { 46 | int color = (blue ? 1 : 0) * 4 + (green ? 1 : 0) * 2 + (red ? 1 : 0); 47 | if (light) 48 | wprintf(L"\x1B[00;3%dm", color); 49 | else 50 | wprintf(L"\x1B[01;3%dm", color); 51 | } 52 | 53 | void Console::SetTitle(const WString& string) 54 | { 55 | } 56 | } 57 | } 58 | 59 | 60 | /*********************************************************************** 61 | .\PRIMITIVES\DATETIME.LINUX.CPP 62 | ***********************************************************************/ 63 | /*********************************************************************** 64 | Author: Zihan Chen (vczh) 65 | Licensed under https://github.com/vczh-libraries/License 66 | ***********************************************************************/ 67 | 68 | #include 69 | #include 70 | #include 71 | 72 | #ifndef VCZH_GCC 73 | static_assert(false, "Do not build this file for Windows applications."); 74 | #endif 75 | 76 | namespace vl 77 | { 78 | 79 | /*********************************************************************** 80 | DateTime 81 | ***********************************************************************/ 82 | 83 | class LinuxDateTimeImpl : public Object, public virtual IDateTimeImpl 84 | { 85 | public: 86 | 87 | static void TimeToOSInternal(time_t timer, vint milliseconds, vuint64_t& osInternal) 88 | { 89 | osInternal = (vuint64_t)timer * 1000 + milliseconds; 90 | } 91 | 92 | static void OSInternalToTime(vuint64_t osInternal, time_t& timer, vint& milliseconds) 93 | { 94 | timer = (time_t)(osInternal / 1000); 95 | milliseconds = (vint)(osInternal % 1000); 96 | } 97 | 98 | static vuint64_t ConvertTMTToOSInternal(tm* timeinfo, vint milliseconds) 99 | { 100 | time_t timer = mktime(timeinfo); 101 | vuint64_t osInternal; 102 | TimeToOSInternal(timer, milliseconds, osInternal); 103 | return osInternal; 104 | } 105 | 106 | static DateTime ConvertTMToDateTime(tm* timeinfo, vint milliseconds) 107 | { 108 | time_t timer = mktime(timeinfo); 109 | DateTime dt; 110 | dt.year = timeinfo->tm_year + 1900; 111 | dt.month = timeinfo->tm_mon + 1; 112 | dt.day = timeinfo->tm_mday; 113 | dt.dayOfWeek = timeinfo->tm_wday; 114 | dt.hour = timeinfo->tm_hour; 115 | dt.minute = timeinfo->tm_min; 116 | dt.second = timeinfo->tm_sec; 117 | dt.milliseconds = milliseconds; 118 | 119 | // in Linux and macOS, filetime will be mktime(t) * 1000 + gettimeofday().tv_usec / 1000 120 | TimeToOSInternal(timer, milliseconds, dt.osInternal); 121 | dt.osMilliseconds = dt.osInternal; 122 | return dt; 123 | } 124 | 125 | DateTime FromDateTime(vint _year, vint _month, vint _day, vint _hour, vint _minute, vint _second, vint _milliseconds) override 126 | { 127 | tm timeinfo; 128 | memset(&timeinfo, 0, sizeof(timeinfo)); 129 | timeinfo.tm_year = _year - 1900; 130 | timeinfo.tm_mon = _month - 1; 131 | timeinfo.tm_mday = _day; 132 | timeinfo.tm_hour = _hour; 133 | timeinfo.tm_min = _minute; 134 | timeinfo.tm_sec = _second; 135 | timeinfo.tm_isdst = -1; 136 | return ConvertTMToDateTime(&timeinfo, _milliseconds); 137 | } 138 | 139 | DateTime FromOSInternal(vuint64_t osInternal) override 140 | { 141 | time_t timer; 142 | vint milliseconds; 143 | OSInternalToTime(osInternal, timer, milliseconds); 144 | 145 | tm* timeinfo = localtime(&timer); 146 | return ConvertTMToDateTime(timeinfo, milliseconds); 147 | } 148 | 149 | vuint64_t LocalTime() override 150 | { 151 | struct timeval tv; 152 | gettimeofday(&tv, nullptr); 153 | tm* timeinfo = localtime(&tv.tv_sec); 154 | return ConvertTMTToOSInternal(timeinfo, tv.tv_usec / 1000); 155 | } 156 | 157 | vuint64_t UtcTime() override 158 | { 159 | struct timeval tv; 160 | gettimeofday(&tv, nullptr); 161 | tm* timeinfo = gmtime(&tv.tv_sec); 162 | return ConvertTMTToOSInternal(timeinfo, tv.tv_usec / 1000); 163 | } 164 | 165 | vuint64_t LocalToUtcTime(vuint64_t osInternal) override 166 | { 167 | time_t timer; 168 | vint milliseconds; 169 | OSInternalToTime(osInternal, timer, milliseconds); 170 | 171 | tm* timeinfo = gmtime(&timer); 172 | return ConvertTMTToOSInternal(timeinfo, milliseconds); 173 | } 174 | 175 | vuint64_t UtcToLocalTime(vuint64_t osInternal) override 176 | { 177 | time_t timer; 178 | vint milliseconds; 179 | OSInternalToTime(osInternal, timer, milliseconds); 180 | 181 | time_t localTimer = mktime(localtime(&timer)); 182 | time_t utcTimer = mktime(gmtime(&timer)); 183 | timer += localTimer - utcTimer; 184 | 185 | TimeToOSInternal(timer, milliseconds, osInternal); 186 | return osInternal; 187 | } 188 | 189 | vuint64_t Forward(vuint64_t osInternal, vuint64_t milliseconds) override 190 | { 191 | return osInternal + milliseconds; 192 | } 193 | 194 | vuint64_t Backward(vuint64_t osInternal, vuint64_t milliseconds) override 195 | { 196 | return osInternal - milliseconds; 197 | } 198 | }; 199 | 200 | LinuxDateTimeImpl osDateTimeImpl; 201 | 202 | IDateTimeImpl* GetOSDateTimeImpl() 203 | { 204 | return &osDateTimeImpl; 205 | } 206 | } 207 | 208 | 209 | /*********************************************************************** 210 | .\STRINGS\CONVERSION.LINUX.CPP 211 | ***********************************************************************/ 212 | /*********************************************************************** 213 | Author: Zihan Chen (vczh) 214 | Licensed under https://github.com/vczh-libraries/License 215 | ***********************************************************************/ 216 | 217 | #include 218 | #include 219 | #include 220 | 221 | namespace vl 222 | { 223 | /*********************************************************************** 224 | String Conversions (buffer walkthrough) 225 | ***********************************************************************/ 226 | 227 | vint _wtoa(const wchar_t* w, char* a, vint chars) 228 | { 229 | return wcstombs(a, w, chars - 1) + 1; 230 | } 231 | 232 | vint _atow(const char* a, wchar_t* w, vint chars) 233 | { 234 | return mbstowcs(w, a, chars - 1) + 1; 235 | } 236 | } 237 | 238 | 239 | /*********************************************************************** 240 | .\UNITTEST\UNITTEST.LINUX.CPP 241 | ***********************************************************************/ 242 | /*********************************************************************** 243 | Author: Zihan Chen (vczh) 244 | Licensed under https://github.com/vczh-libraries/License 245 | ***********************************************************************/ 246 | 247 | 248 | #ifndef VCZH_GCC 249 | static_assert(false, "Do not build this file for Windows applications."); 250 | #endif 251 | 252 | namespace vl 253 | { 254 | namespace unittest 255 | { 256 | /*********************************************************************** 257 | UnitTest 258 | ***********************************************************************/ 259 | 260 | bool UnitTest::IsDebuggerAttached() 261 | { 262 | return false; 263 | } 264 | } 265 | } 266 | 267 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | See https://github.com/vczh-libraries/License/blob/master/README.md for the detail. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VlppOS 2 | 3 | **Minimum Operator System Construction.** 4 | 5 | ## License 6 | 7 | This project is licensed under [the License repo](https://github.com/vczh-libraries/License). 8 | 9 | Source code in this repo is for reference only, please use the source code in [the Release repo](https://github.com/vczh-libraries/Release). 10 | 11 | You are welcome to contribute to this repo by opening pull requests. 12 | 13 | ## Document 14 | 15 | For **Gaclib**: click [here](http://vczh-libraries.github.io/doc/current/home.html) 16 | 17 | For **VlppOS**: click [here](http://vczh-libraries.github.io/doc/current/vlppos/home.html) 18 | 19 | ## Unit Test 20 | 21 | For **Windows**, open `Test/UnitTest/UnitTest.sln`, and run the `UnitTest` project. 22 | 23 | For **Linux**, use `Test/Linux/makefile` to build and run the unit test project. 24 | -------------------------------------------------------------------------------- /Release/CodegenConfig.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Release/IncludeOnly/VlppOS.Linux.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY 3 | DEVELOPER: Zihan Chen(vczh) 4 | ***********************************************************************/ 5 | #include "VlppOS.h" 6 | #include "Vlpp.h" 7 | 8 | #include "..\..\Source\FileSystem.Linux.cpp" 9 | #include "..\..\Source\Locale.Linux.cpp" 10 | #include "..\..\Source\Threading.Linux.cpp" 11 | #include "..\..\Source\Encoding\CharFormat\CharFormat.Linux.cpp" 12 | -------------------------------------------------------------------------------- /Release/IncludeOnly/VlppOS.Windows.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY 3 | DEVELOPER: Zihan Chen(vczh) 4 | ***********************************************************************/ 5 | #include "VlppOS.h" 6 | #include "Vlpp.h" 7 | 8 | #include "..\..\Source\FileSystem.Windows.cpp" 9 | #include "..\..\Source\HttpUtility.Windows.cpp" 10 | #include "..\..\Source\Locale.Windows.cpp" 11 | #include "..\..\Source\Threading.Windows.cpp" 12 | #include "..\..\Source\Encoding\CharFormat\CharFormat.Windows.cpp" 13 | -------------------------------------------------------------------------------- /Release/IncludeOnly/VlppOS.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY 3 | DEVELOPER: Zihan Chen(vczh) 4 | ***********************************************************************/ 5 | #include "VlppOS.h" 6 | 7 | #include "..\..\Source\FileSystem.cpp" 8 | #include "..\..\Source\Locale.cpp" 9 | #include "..\..\Source\Threading.cpp" 10 | #include "..\..\Source\Encoding\Base64Encoding.cpp" 11 | #include "..\..\Source\Encoding\Encoding.cpp" 12 | #include "..\..\Source\Encoding\LzwEncoding.cpp" 13 | #include "..\..\Source\Encoding\CharFormat\BomEncoding.cpp" 14 | #include "..\..\Source\Encoding\CharFormat\CharFormat.cpp" 15 | #include "..\..\Source\Encoding\CharFormat\MbcsEncoding.cpp" 16 | #include "..\..\Source\Encoding\CharFormat\UtfEncoding.cpp" 17 | #include "..\..\Source\Stream\Accessor.cpp" 18 | #include "..\..\Source\Stream\BroadcastStream.cpp" 19 | #include "..\..\Source\Stream\CacheStream.cpp" 20 | #include "..\..\Source\Stream\EncodingStream.cpp" 21 | #include "..\..\Source\Stream\FileStream.cpp" 22 | #include "..\..\Source\Stream\MemoryStream.cpp" 23 | #include "..\..\Source\Stream\MemoryWrapperStream.cpp" 24 | #include "..\..\Source\Stream\RecorderStream.cpp" 25 | -------------------------------------------------------------------------------- /Release/IncludeOnly/VlppOS.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY 3 | DEVELOPER: Zihan Chen(vczh) 4 | ***********************************************************************/ 5 | #include "Vlpp.h" 6 | 7 | #include "..\..\Source\HttpUtility.h" 8 | #include "..\..\Source\Locale.h" 9 | #include "..\..\Source\Threading.h" 10 | #include "..\..\Source\Stream\Interfaces.h" 11 | #include "..\..\Source\Encoding\Encoding.h" 12 | #include "..\..\Source\Encoding\Base64Encoding.h" 13 | #include "..\..\Source\Encoding\CharFormat\BomEncoding.h" 14 | #include "..\..\Source\Encoding\CharFormat\MbcsEncoding.h" 15 | #include "..\..\Source\Encoding\CharFormat\UtfEncoding.h" 16 | #include "..\..\Source\Encoding\CharFormat\CharFormat.h" 17 | #include "..\..\Source\Encoding\LzwEncoding.h" 18 | #include "..\..\Source\FileSystem.h" 19 | #include "..\..\Source\Stream\BroadcastStream.h" 20 | #include "..\..\Source\Stream\CacheStream.h" 21 | #include "..\..\Source\Stream\EncodingStream.h" 22 | #include "..\..\Source\Stream\FileStream.h" 23 | #include "..\..\Source\Stream\MemoryStream.h" 24 | #include "..\..\Source\Stream\Accessor.h" 25 | #include "..\..\Source\Stream\MemoryWrapperStream.h" 26 | #include "..\..\Source\Stream\RecorderStream.h" 27 | #include "..\..\Source\Stream\Serialization.h" 28 | -------------------------------------------------------------------------------- /Source/Encoding/Base64Encoding.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "Base64Encoding.h" 7 | 8 | namespace vl 9 | { 10 | namespace stream 11 | { 12 | const char8_t Utf8Base64Codes[] = u8"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 13 | 14 | /*********************************************************************** 15 | Utf8Base64Encoder 16 | ***********************************************************************/ 17 | 18 | void Utf8Base64Encoder::WriteBytesToCharArray(uint8_t* fromBytes, char8_t(&toChars)[Base64CycleChars], vint bytes) 19 | { 20 | switch (bytes) 21 | { 22 | case 1: 23 | { 24 | toChars[0] = Utf8Base64Codes[fromBytes[0] >> 2]; 25 | toChars[1] = Utf8Base64Codes[(fromBytes[0] % (1 << 2)) << 4]; 26 | toChars[2] = u8'='; 27 | toChars[3] = u8'='; 28 | } 29 | break; 30 | case 2: 31 | { 32 | toChars[0] = Utf8Base64Codes[fromBytes[0] >> 2]; 33 | toChars[1] = Utf8Base64Codes[((fromBytes[0] % (1 << 2)) << 4) | (fromBytes[1] >> 4)]; 34 | toChars[2] = Utf8Base64Codes[(fromBytes[1] % (1 << 4)) << 2]; 35 | toChars[3] = u8'='; 36 | } 37 | break; 38 | case 3: 39 | { 40 | toChars[0] = Utf8Base64Codes[fromBytes[0] >> 2]; 41 | toChars[1] = Utf8Base64Codes[((fromBytes[0] % (1 << 2)) << 4) | (fromBytes[1] >> 4)]; 42 | toChars[2] = Utf8Base64Codes[((fromBytes[1] % (1 << 4)) << 2) | (fromBytes[2] >> 6)]; 43 | toChars[3] = Utf8Base64Codes[fromBytes[2] % (1 << 6)]; 44 | } 45 | break; 46 | default: 47 | CHECK_FAIL(L"vl::stream::Utf8Base64Encoder::WriteBytesToCharArray(uint8_t*, char8_t(&)[Base64CycleChars], vint)#Parameter bytes should be 1, 2 or 3."); 48 | } 49 | } 50 | 51 | bool Utf8Base64Encoder::WriteCycle(uint8_t*& reading, vint& _size) 52 | { 53 | if (_size <= 0) return false; 54 | vint bytes = _size < Base64CycleBytes ? _size : Base64CycleBytes; 55 | 56 | char8_t chars[Base64CycleChars]; 57 | WriteBytesToCharArray(reading, chars, bytes); 58 | vint writtenBytes = stream->Write(chars, Base64CycleChars); 59 | CHECK_ERROR(writtenBytes == Base64CycleChars, L"vl::stream::Utf8Base64Encoder::WriteCycle(uint8_t*&, vint&)#The underlying stream failed to accept enough base64 characters."); 60 | 61 | reading += bytes; 62 | _size -= bytes; 63 | return true; 64 | } 65 | 66 | bool Utf8Base64Encoder::WriteCache(uint8_t*& reading, vint& _size) 67 | { 68 | if (cacheSize > 0 || _size < Base64CycleBytes) 69 | { 70 | vint copiedBytes = Base64CycleBytes - cacheSize; 71 | if (copiedBytes > _size) copiedBytes = _size; 72 | if (copiedBytes > 0) 73 | { 74 | memcpy(cache + cacheSize, reading, copiedBytes); 75 | reading += copiedBytes; 76 | _size -= copiedBytes; 77 | cacheSize += copiedBytes; 78 | } 79 | } 80 | 81 | if (cacheSize == 0) return _size > 0; 82 | if (cacheSize == Base64CycleBytes) 83 | { 84 | uint8_t* cacheReading = cache; 85 | return WriteCycle(cacheReading, cacheSize); 86 | } 87 | return true; 88 | } 89 | 90 | vint Utf8Base64Encoder::Write(void* _buffer, vint _size) 91 | { 92 | uint8_t* reading = (uint8_t*)_buffer; 93 | 94 | // flush cache if any 95 | if (!WriteCache(reading, _size)) goto FINISHED_WRITING; 96 | 97 | // run Base64 encoding 98 | while (_size >= Base64CycleBytes) 99 | { 100 | if (!WriteCycle(reading, _size)) goto FINISHED_WRITING; 101 | } 102 | 103 | // run the last Base64 encoding cycle and wrote a postfix to cache 104 | WriteCache(reading, _size); 105 | 106 | FINISHED_WRITING: 107 | return reading - (uint8_t*)_buffer; 108 | } 109 | 110 | void Utf8Base64Encoder::Close() 111 | { 112 | if (cacheSize > 0) 113 | { 114 | char8_t chars[Base64CycleChars]; 115 | WriteBytesToCharArray(cache, chars, cacheSize); 116 | vint writtenBytes = stream->Write(chars, Base64CycleChars); 117 | CHECK_ERROR(writtenBytes == Base64CycleChars, L"vl::stream::Utf8Base64Encoder::Close()#The underlying stream failed to accept enough base64 characters."); 118 | 119 | cacheSize = 0; 120 | } 121 | EncoderBase::Close(); 122 | } 123 | 124 | /*********************************************************************** 125 | Utf8Base64Decoder 126 | ***********************************************************************/ 127 | 128 | vint Utf8Base64Decoder::ReadBytesFromCharArray(char8_t(&fromChars)[Base64CycleChars], uint8_t* toBytes) 129 | { 130 | uint8_t nums[Base64CycleChars]; 131 | for (vint i = 0; i < Base64CycleChars; i++) 132 | { 133 | char8_t c = fromChars[i]; 134 | if (u8'A' <= c && c <= u8'Z') nums[i] = c - u8'A'; 135 | else if (u8'a' <= c && c <= u8'z') nums[i] = c - u8'a' + 26; 136 | else if (u8'0' <= c && c <= u8'9') nums[i] = c - u8'0' + 52; 137 | else switch (c) 138 | { 139 | case '+':nums[i] = 62; break; 140 | case '/':nums[i] = 63; break; 141 | case '=':nums[i] = 0; break; 142 | default: 143 | CHECK_FAIL(L"vl::stream::Utf8Base64Decoder::ReadBytesFromCharArray(char(&)[Base64CycleChars], uint8_t*)#Illegal Base64 character."); 144 | } 145 | } 146 | 147 | toBytes[0] = (nums[0] << 2) | (nums[1] >> 4); 148 | if (fromChars[2] == u8'=') return 1; 149 | toBytes[1] = ((nums[1] % (1 << 4)) << 4) | (nums[2] >> 2); 150 | if (fromChars[3] == u8'=') return 2; 151 | toBytes[2] = ((nums[2] % (1 << 2)) << 6) | nums[3]; 152 | return 3; 153 | } 154 | 155 | vint Utf8Base64Decoder::ReadCycle(uint8_t*& writing, vint& _size) 156 | { 157 | char8_t chars[Base64CycleChars]; 158 | vint readChars = stream->Read((void*)chars, Base64CycleChars); 159 | if (readChars == 0) return 0; 160 | CHECK_ERROR(readChars == Base64CycleChars, L"vl::stream::Utf8Base64Decoder::ReadCycle(uint8_t*&, vint&)#The underlying stream failed to provide enough base64 characters."); 161 | 162 | vint readBytes = ReadBytesFromCharArray(chars, writing); 163 | writing += readBytes; 164 | _size -= readBytes; 165 | return readBytes; 166 | } 167 | 168 | void Utf8Base64Decoder::ReadCache(uint8_t*& writing, vint& _size) 169 | { 170 | if (cacheSize > 0) 171 | { 172 | vint copiedBytes = cacheSize; 173 | if (copiedBytes > _size) copiedBytes = _size; 174 | if (copiedBytes > 0) 175 | { 176 | memcpy(writing, cache, copiedBytes); 177 | writing += copiedBytes; 178 | _size -= copiedBytes; 179 | cacheSize -= copiedBytes; 180 | if (cacheSize > 0) 181 | { 182 | memmove(cache, cache + copiedBytes, cacheSize); 183 | } 184 | } 185 | } 186 | } 187 | 188 | vint Utf8Base64Decoder::Read(void* _buffer, vint _size) 189 | { 190 | uint8_t* writing = (uint8_t*)_buffer; 191 | 192 | // write cache to buffer if any 193 | ReadCache(writing, _size); 194 | 195 | // run Base64 decoding 196 | while (_size >= Base64CycleBytes) 197 | { 198 | if (ReadCycle(writing, _size) == 0) goto FINISHED_READING; 199 | } 200 | 201 | // run the last Base64 decoding cycle and write a prefix to buffer 202 | if (_size > 0) 203 | { 204 | uint8_t* cacheWriting = cache; 205 | vint temp = 0; 206 | if ((cacheSize = ReadCycle(cacheWriting, temp)) == 0) goto FINISHED_READING; 207 | ReadCache(writing, _size); 208 | } 209 | FINISHED_READING: 210 | return writing - (uint8_t*)_buffer; 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /Source/Encoding/Base64Encoding.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_ENCODING_BASE64ENCODING 7 | #define VCZH_STREAM_ENCODING_BASE64ENCODING 8 | 9 | #include "Encoding.h" 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | constexpr const vint Base64CycleBytes = 3; 16 | constexpr const vint Base64CycleChars = 4; 17 | 18 | /*********************************************************************** 19 | Utf8Base64Encoder 20 | ***********************************************************************/ 21 | 22 | class Utf8Base64Encoder : public EncoderBase 23 | { 24 | protected: 25 | uint8_t cache[Base64CycleBytes]; 26 | vint cacheSize = 0; 27 | 28 | void WriteBytesToCharArray(uint8_t* fromBytes, char8_t(&toChars)[Base64CycleChars], vint bytes); 29 | bool WriteCycle(uint8_t*& reading, vint& _size); 30 | bool WriteCache(uint8_t*& reading, vint& _size); 31 | public: 32 | vint Write(void* _buffer, vint _size) override; 33 | void Close() override; 34 | }; 35 | 36 | /*********************************************************************** 37 | Utf8Base64Decoder 38 | ***********************************************************************/ 39 | 40 | class Utf8Base64Decoder : public DecoderBase 41 | { 42 | protected: 43 | uint8_t cache[Base64CycleBytes]; 44 | vint cacheSize = 0; 45 | 46 | vint ReadBytesFromCharArray(char8_t(&fromChars)[Base64CycleChars], uint8_t* toBytes); 47 | vint ReadCycle(uint8_t*& writing, vint& _size); 48 | void ReadCache(uint8_t*& writing, vint& _size); 49 | public: 50 | vint Read(void* _buffer, vint _size) override; 51 | }; 52 | } 53 | } 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /Source/Encoding/CharFormat/BomEncoding.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "BomEncoding.h" 7 | #include "CharFormat.h" 8 | 9 | namespace vl 10 | { 11 | namespace stream 12 | { 13 | /*********************************************************************** 14 | BomEncoder 15 | ***********************************************************************/ 16 | 17 | BomEncoder::BomEncoder(Encoding _encoding) 18 | :encoding(_encoding) 19 | ,encoder(0) 20 | { 21 | switch(encoding) 22 | { 23 | case Mbcs: 24 | encoder=new MbcsEncoder; 25 | break; 26 | case Utf8: 27 | encoder=new Utf8Encoder; 28 | break; 29 | case Utf16: 30 | encoder=new Utf16Encoder; 31 | break; 32 | case Utf16BE: 33 | encoder=new Utf16BEEncoder; 34 | break; 35 | } 36 | } 37 | 38 | BomEncoder::~BomEncoder() 39 | { 40 | Close(); 41 | } 42 | 43 | void BomEncoder::Setup(IStream* _stream) 44 | { 45 | switch(encoding) 46 | { 47 | case Mbcs: 48 | break; 49 | case Utf8: 50 | _stream->Write((void*)"\xEF\xBB\xBF", 3); 51 | break; 52 | case Utf16: 53 | _stream->Write((void*)"\xFF\xFE", 2); 54 | break; 55 | case Utf16BE: 56 | _stream->Write((void*)"\xFE\xFF", 2); 57 | break; 58 | } 59 | encoder->Setup(_stream); 60 | } 61 | 62 | void BomEncoder::Close() 63 | { 64 | if(encoder) 65 | { 66 | encoder->Close(); 67 | delete encoder; 68 | encoder=0; 69 | } 70 | } 71 | 72 | vint BomEncoder::Write(void* _buffer, vint _size) 73 | { 74 | return encoder->Write(_buffer, _size); 75 | } 76 | 77 | /*********************************************************************** 78 | BomDecoder 79 | ***********************************************************************/ 80 | 81 | BomDecoder::BomStream::BomStream(IStream* _stream, char* _bom, vint _bomLength) 82 | :stream(_stream) 83 | ,bomPosition(0) 84 | ,bomLength(_bomLength) 85 | { 86 | memcpy(bom, _bom, bomLength); 87 | } 88 | 89 | bool BomDecoder::BomStream::CanRead()const 90 | { 91 | return IsAvailable(); 92 | } 93 | 94 | bool BomDecoder::BomStream::CanWrite()const 95 | { 96 | return false; 97 | } 98 | 99 | bool BomDecoder::BomStream::CanSeek()const 100 | { 101 | return false; 102 | } 103 | 104 | bool BomDecoder::BomStream::CanPeek()const 105 | { 106 | return false; 107 | } 108 | 109 | bool BomDecoder::BomStream::IsLimited()const 110 | { 111 | return stream!=0 && stream->IsLimited(); 112 | } 113 | 114 | bool BomDecoder::BomStream::IsAvailable()const 115 | { 116 | return stream!=0 && stream->IsAvailable(); 117 | } 118 | 119 | void BomDecoder::BomStream::Close() 120 | { 121 | stream=0; 122 | } 123 | 124 | pos_t BomDecoder::BomStream::Position()const 125 | { 126 | return IsAvailable()?bomPosition+stream->Position():-1; 127 | } 128 | 129 | pos_t BomDecoder::BomStream::Size()const 130 | { 131 | return -1; 132 | } 133 | 134 | void BomDecoder::BomStream::Seek(pos_t _size) 135 | { 136 | CHECK_FAIL(L"BomDecoder::BomStream::Seek(pos_t)#Operation not supported."); 137 | } 138 | 139 | void BomDecoder::BomStream::SeekFromBegin(pos_t _size) 140 | { 141 | CHECK_FAIL(L"BomDecoder::BomStream::SeekFromBegin(pos_t)#Operation not supported."); 142 | } 143 | 144 | void BomDecoder::BomStream::SeekFromEnd(pos_t _size) 145 | { 146 | CHECK_FAIL(L"BomDecoder::BomStream::SeekFromEnd(pos_t)#Operation not supported."); 147 | } 148 | 149 | vint BomDecoder::BomStream::Read(void* _buffer, vint _size) 150 | { 151 | vint result=0; 152 | unsigned char* buffer=(unsigned char*)_buffer; 153 | if(bomPositionRead(buffer, _size); 165 | } 166 | return result; 167 | } 168 | 169 | vint BomDecoder::BomStream::Write(void* _buffer, vint _size) 170 | { 171 | CHECK_FAIL(L"BomDecoder::BomStream::Write(void*, vint)#Operation not supported."); 172 | } 173 | 174 | vint BomDecoder::BomStream::Peek(void* _buffer, vint _size) 175 | { 176 | CHECK_FAIL(L"BomDecoder::BomStream::Peek(void*, vint)#Operation not supported."); 177 | } 178 | 179 | BomDecoder::BomDecoder() 180 | :decoder(0) 181 | { 182 | } 183 | 184 | BomDecoder::~BomDecoder() 185 | { 186 | Close(); 187 | } 188 | 189 | void BomDecoder::Setup(IStream* _stream) 190 | { 191 | char bom[3]={0}; 192 | vint length=_stream->Read(bom, sizeof(bom)); 193 | if(strncmp(bom, "\xEF\xBB\xBF", 3)==0) 194 | { 195 | decoder=new Utf8Decoder; 196 | stream=new BomStream(_stream, bom+3, 0); 197 | } 198 | else if(strncmp(bom, "\xFF\xFE", 2)==0) 199 | { 200 | decoder=new Utf16Decoder; 201 | stream=new BomStream(_stream, bom+2, 1); 202 | } 203 | else if(strncmp(bom, "\xFE\xFF", 2)==0) 204 | { 205 | decoder=new Utf16BEDecoder; 206 | stream=new BomStream(_stream, bom+2, 1); 207 | } 208 | else 209 | { 210 | decoder=new MbcsDecoder; 211 | stream=new BomStream(_stream, bom, 3); 212 | } 213 | decoder->Setup(stream); 214 | } 215 | 216 | void BomDecoder::Close() 217 | { 218 | if(decoder) 219 | { 220 | decoder->Close(); 221 | delete decoder; 222 | decoder=0; 223 | stream->Close(); 224 | delete stream; 225 | stream=0; 226 | } 227 | } 228 | 229 | vint BomDecoder::Read(void* _buffer, vint _size) 230 | { 231 | return decoder->Read(_buffer, _size); 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /Source/Encoding/CharFormat/BomEncoding.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_ENCODING_CHARFORMAT_BOMENCODING 7 | #define VCZH_STREAM_ENCODING_CHARFORMAT_BOMENCODING 8 | 9 | #include "../Encoding.h" 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | /*********************************************************************** 16 | Bom 17 | ***********************************************************************/ 18 | 19 | /// Encoder to write text in a specified encoding. A BOM will be added at the beginning. 20 | class BomEncoder : public Object, public IEncoder 21 | { 22 | public: 23 | /// Text encoding. 24 | enum Encoding 25 | { 26 | /// Multi-bytes character string. 27 | Mbcs, 28 | /// UTF-8. EF, BB, BF will be written before writing any text. 29 | Utf8, 30 | /// UTF-16. FF FE will be written before writing any text. 31 | Utf16, 32 | /// Big endian UTF-16. FE FF, BF will be written before writing any text. 33 | Utf16BE 34 | }; 35 | protected: 36 | Encoding encoding; 37 | IEncoder* encoder; 38 | public: 39 | /// Create an encoder with a specified encoding. 40 | /// The specified encoding. 41 | BomEncoder(Encoding _encoding); 42 | ~BomEncoder(); 43 | 44 | void Setup(IStream* _stream); 45 | void Close(); 46 | vint Write(void* _buffer, vint _size); 47 | }; 48 | 49 | /// Decoder to read text. This decoder depends on BOM at the beginning to decide the format of the input. 50 | class BomDecoder : public Object, public IDecoder 51 | { 52 | private: 53 | class BomStream : public Object, public IStream 54 | { 55 | protected: 56 | IStream* stream; 57 | char bom[3]; 58 | vint bomLength; 59 | vint bomPosition; 60 | public: 61 | BomStream(IStream* _stream, char* _bom, vint _bomLength); 62 | 63 | bool CanRead()const; 64 | bool CanWrite()const; 65 | bool CanSeek()const; 66 | bool CanPeek()const; 67 | bool IsLimited()const; 68 | bool IsAvailable()const; 69 | void Close(); 70 | pos_t Position()const; 71 | pos_t Size()const; 72 | void Seek(pos_t _size); 73 | void SeekFromBegin(pos_t _size); 74 | void SeekFromEnd(pos_t _size); 75 | vint Read(void* _buffer, vint _size); 76 | vint Write(void* _buffer, vint _size); 77 | vint Peek(void* _buffer, vint _size); 78 | }; 79 | protected: 80 | IDecoder* decoder; 81 | IStream* stream; 82 | 83 | public: 84 | /// Create an decoder, BOM will be consumed before reading any text. 85 | BomDecoder(); 86 | ~BomDecoder(); 87 | 88 | void Setup(IStream* _stream); 89 | void Close(); 90 | vint Read(void* _buffer, vint _size); 91 | }; 92 | } 93 | } 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /Source/Encoding/CharFormat/CharFormat.Linux.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "CharFormat.h" 7 | #include 8 | 9 | #ifndef VCZH_GCC 10 | static_assert(false, "Do not build this file for Windows applications."); 11 | #endif 12 | 13 | namespace vl 14 | { 15 | namespace stream 16 | { 17 | using namespace vl::encoding; 18 | 19 | bool IsMbcsLeadByte(char c) 20 | { 21 | return (vint8_t)c < 0; 22 | } 23 | 24 | void MbcsToWChar(wchar_t* wideBuffer, vint wideChars, vint wideReaded, char* mbcsBuffer, vint mbcsChars) 25 | { 26 | AString a = AString::CopyFrom(mbcsBuffer, mbcsChars); 27 | WString w = atow(a); 28 | memcpy(wideBuffer, w.Buffer(), wideReaded * sizeof(wchar_t)); 29 | } 30 | 31 | /*********************************************************************** 32 | MbcsEncoder 33 | ***********************************************************************/ 34 | 35 | vint MbcsEncoder::WriteString(wchar_t* _buffer, vint chars) 36 | { 37 | WString w = WString::CopyFrom(_buffer, chars); 38 | AString a = wtoa(w); 39 | vint length = a.Length(); 40 | vint result = stream->Write((void*)a.Buffer(), length); 41 | 42 | if (result != length) 43 | { 44 | Close(); 45 | return 0; 46 | } 47 | return chars; 48 | } 49 | 50 | /*********************************************************************** 51 | Helper Functions 52 | ***********************************************************************/ 53 | 54 | extern bool CanBeMbcs(unsigned char* buffer, vint size); 55 | extern bool CanBeUtf8(unsigned char* buffer, vint size); 56 | extern bool CanBeUtf16(unsigned char* buffer, vint size, bool& hitSurrogatePairs); 57 | extern bool CanBeUtf16BE(unsigned char* buffer, vint size, bool& hitSurrogatePairs); 58 | 59 | /*********************************************************************** 60 | TestEncoding 61 | ***********************************************************************/ 62 | 63 | extern void TestEncodingInternal( 64 | unsigned char* buffer, 65 | vint size, 66 | BomEncoder::Encoding& encoding, 67 | bool containsBom, 68 | bool utf16HitSurrogatePairs, 69 | bool utf16BEHitSurrogatePairs, 70 | bool roughMbcs, 71 | bool roughUtf8, 72 | bool roughUtf16, 73 | bool roughUtf16BE 74 | ) 75 | { 76 | if (roughUtf16 && roughUtf16BE && !roughUtf8) 77 | { 78 | if (utf16BEHitSurrogatePairs && !utf16HitSurrogatePairs) 79 | { 80 | encoding = BomEncoder::Utf16BE; 81 | } 82 | else 83 | { 84 | encoding = BomEncoder::Utf16; 85 | } 86 | } 87 | else 88 | { 89 | encoding = BomEncoder::Utf8; 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Source/Encoding/CharFormat/CharFormat.Windows.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "CharFormat.h" 7 | #include 8 | 9 | #ifndef VCZH_MSVC 10 | static_assert(false, "Do not build this file for non-Windows applications."); 11 | #endif 12 | 13 | namespace vl 14 | { 15 | namespace stream 16 | { 17 | bool IsMbcsLeadByte(char c) 18 | { 19 | return IsDBCSLeadByte(c); 20 | } 21 | 22 | void MbcsToWChar(wchar_t* wideBuffer, vint wideChars, vint wideReaded, char* mbcsBuffer, vint mbcsChars) 23 | { 24 | MultiByteToWideChar(CP_THREAD_ACP, 0, mbcsBuffer, (int)mbcsChars, wideBuffer, (int)wideChars); 25 | } 26 | 27 | /*********************************************************************** 28 | MbcsEncoder 29 | ***********************************************************************/ 30 | 31 | vint MbcsEncoder::WriteString(wchar_t* _buffer, vint chars) 32 | { 33 | vint length = WideCharToMultiByte(CP_THREAD_ACP, 0, _buffer, (int)chars, NULL, NULL, NULL, NULL); 34 | char* mbcs = new char[length]; 35 | WideCharToMultiByte(CP_THREAD_ACP, 0, _buffer, (int)chars, mbcs, (int)length, NULL, NULL); 36 | vint result = stream->Write(mbcs, length); 37 | delete[] mbcs; 38 | 39 | if (result != length) 40 | { 41 | Close(); 42 | return 0; 43 | } 44 | return chars; 45 | } 46 | 47 | /*********************************************************************** 48 | Helper Functions 49 | ***********************************************************************/ 50 | 51 | extern bool CanBeMbcs(unsigned char* buffer, vint size); 52 | extern bool CanBeUtf8(unsigned char* buffer, vint size); 53 | extern bool CanBeUtf16(unsigned char* buffer, vint size, bool& hitSurrogatePairs); 54 | extern bool CanBeUtf16BE(unsigned char* buffer, vint size, bool& hitSurrogatePairs); 55 | 56 | template 57 | bool GetEncodingResult(int(&tests)[Count], bool(&results)[Count], int test) 58 | { 59 | for (vint i = 0; i < Count; i++) 60 | { 61 | if (tests[i] & test) 62 | { 63 | if (results[i]) return true; 64 | } 65 | } 66 | return false; 67 | } 68 | 69 | /*********************************************************************** 70 | TestEncoding 71 | ***********************************************************************/ 72 | 73 | extern void TestEncodingInternal( 74 | unsigned char* buffer, 75 | vint size, 76 | BomEncoder::Encoding& encoding, 77 | bool containsBom, 78 | bool utf16HitSurrogatePairs, 79 | bool utf16BEHitSurrogatePairs, 80 | bool roughMbcs, 81 | bool roughUtf8, 82 | bool roughUtf16, 83 | bool roughUtf16BE 84 | ) 85 | { 86 | int tests[] = 87 | { 88 | IS_TEXT_UNICODE_REVERSE_ASCII16, 89 | IS_TEXT_UNICODE_REVERSE_STATISTICS, 90 | IS_TEXT_UNICODE_REVERSE_CONTROLS, 91 | 92 | IS_TEXT_UNICODE_ASCII16, 93 | IS_TEXT_UNICODE_STATISTICS, 94 | IS_TEXT_UNICODE_CONTROLS, 95 | 96 | IS_TEXT_UNICODE_ILLEGAL_CHARS, 97 | IS_TEXT_UNICODE_ODD_LENGTH, 98 | IS_TEXT_UNICODE_NULL_BYTES, 99 | }; 100 | 101 | const vint TestCount = sizeof(tests) / sizeof(*tests); 102 | bool results[TestCount]; 103 | for (vint i = 0; i < TestCount; i++) 104 | { 105 | int test = tests[i]; 106 | results[i] = IsTextUnicode(buffer, (int)size, &test) != 0; 107 | } 108 | 109 | if (size % 2 == 0 110 | && !GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_ASCII16) 111 | && !GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_STATISTICS) 112 | && !GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_CONTROLS) 113 | ) 114 | { 115 | for (vint i = 0; i < size; i += 2) 116 | { 117 | unsigned char c = buffer[i]; 118 | buffer[i] = buffer[i + 1]; 119 | buffer[i + 1] = c; 120 | } 121 | // 3 = (count of reverse group) = (count of unicode group) 122 | for (vint i = 0; i < 3; i++) 123 | { 124 | int test = tests[i + 3]; 125 | results[i] = IsTextUnicode(buffer, (int)size, &test) != 0; 126 | } 127 | for (vint i = 0; i < size; i += 2) 128 | { 129 | unsigned char c = buffer[i]; 130 | buffer[i] = buffer[i + 1]; 131 | buffer[i + 1] = c; 132 | } 133 | } 134 | 135 | if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_NOT_UNICODE_MASK)) 136 | { 137 | if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_NOT_ASCII_MASK)) 138 | { 139 | encoding = BomEncoder::Utf8; 140 | } 141 | else if (roughUtf8 || !roughMbcs) 142 | { 143 | encoding = BomEncoder::Utf8; 144 | } 145 | } 146 | else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_ASCII16)) 147 | { 148 | encoding = BomEncoder::Utf16; 149 | } 150 | else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_ASCII16)) 151 | { 152 | encoding = BomEncoder::Utf16BE; 153 | } 154 | else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_CONTROLS)) 155 | { 156 | encoding = BomEncoder::Utf16; 157 | } 158 | else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_CONTROLS)) 159 | { 160 | encoding = BomEncoder::Utf16BE; 161 | } 162 | else 163 | { 164 | if (!roughUtf8) 165 | { 166 | if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_STATISTICS)) 167 | { 168 | encoding = BomEncoder::Utf16; 169 | } 170 | else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_STATISTICS)) 171 | { 172 | encoding = BomEncoder::Utf16BE; 173 | } 174 | } 175 | else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_NOT_UNICODE_MASK)) 176 | { 177 | encoding = BomEncoder::Utf8; 178 | } 179 | else if (roughUtf8 || !roughMbcs) 180 | { 181 | encoding = BomEncoder::Utf8; 182 | } 183 | } 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /Source/Encoding/CharFormat/CharFormat.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "CharFormat.h" 7 | 8 | namespace vl 9 | { 10 | namespace stream 11 | { 12 | /*********************************************************************** 13 | Helper Functions 14 | ***********************************************************************/ 15 | 16 | bool CanBeMbcs(unsigned char* buffer, vint size) 17 | { 18 | for(vint i=0;i= 3 && strncmp((char*)buffer, "\xEF\xBB\xBF", 3) == 0) 160 | { 161 | encoding = BomEncoder::Utf8; 162 | containsBom = true; 163 | } 164 | else if (size >= 2 && strncmp((char*)buffer, "\xFF\xFE", 2) == 0) 165 | { 166 | encoding = BomEncoder::Utf16; 167 | containsBom = true; 168 | } 169 | else if (size >= 2 && strncmp((char*)buffer, "\xFE\xFF", 2) == 0) 170 | { 171 | encoding = BomEncoder::Utf16BE; 172 | containsBom = true; 173 | } 174 | else 175 | { 176 | encoding = BomEncoder::Mbcs; 177 | containsBom = false; 178 | 179 | bool utf16HitSurrogatePairs = false; 180 | bool utf16BEHitSurrogatePairs = false; 181 | bool roughMbcs = CanBeMbcs(buffer, size); 182 | bool roughUtf8 = CanBeUtf8(buffer, size); 183 | bool roughUtf16 = CanBeUtf16(buffer, size, utf16HitSurrogatePairs); 184 | bool roughUtf16BE = CanBeUtf16BE(buffer, size, utf16BEHitSurrogatePairs); 185 | 186 | vint roughCount = (roughMbcs ? 1 : 0) + (roughUtf8 ? 1 : 0) + (roughUtf16 ? 1 : 0) + (roughUtf16BE ? 1 : 0); 187 | if (roughCount == 1) 188 | { 189 | if (roughUtf8) encoding = BomEncoder::Utf8; 190 | else if (roughUtf16) encoding = BomEncoder::Utf16; 191 | else if (roughUtf16BE) encoding = BomEncoder::Utf16BE; 192 | } 193 | else if (roughCount > 1) 194 | { 195 | TestEncodingInternal(buffer, size, encoding, containsBom, utf16HitSurrogatePairs, utf16BEHitSurrogatePairs, roughMbcs, roughUtf8, roughUtf16, roughUtf16BE); 196 | } 197 | } 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /Source/Encoding/CharFormat/CharFormat.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_ENCODING_CHARFORMAT 7 | #define VCZH_STREAM_ENCODING_CHARFORMAT 8 | 9 | #include "MbcsEncoding.h" 10 | #include "UtfEncoding.h" 11 | #include "BomEncoding.h" 12 | 13 | namespace vl 14 | { 15 | namespace stream 16 | { 17 | /*********************************************************************** 18 | Utf-8 19 | ***********************************************************************/ 20 | 21 | /// Encoder to write UTF-8 text. 22 | class Utf8Encoder : public UtfGeneralEncoder {}; 23 | /// Decoder to read UTF-8 text. 24 | class Utf8Decoder : public UtfGeneralDecoder {}; 25 | 26 | /*********************************************************************** 27 | Utf-16 28 | ***********************************************************************/ 29 | 30 | /// Encoder to write UTF-16 text. 31 | class Utf16Encoder : public UtfGeneralEncoder {}; 32 | /// Decoder to read UTF-16 text. 33 | class Utf16Decoder : public UtfGeneralDecoder {}; 34 | 35 | /*********************************************************************** 36 | Utf-16BE 37 | ***********************************************************************/ 38 | 39 | /// Encoder to write big endian UTF-16 to. 40 | class Utf16BEEncoder : public UtfGeneralEncoder {}; 41 | /// Decoder to read big endian UTF-16 text. 42 | class Utf16BEDecoder : public UtfGeneralDecoder {}; 43 | 44 | /*********************************************************************** 45 | Utf-32 46 | ***********************************************************************/ 47 | 48 | /// Encoder to write UTF-8 text. 49 | class Utf32Encoder : public UtfGeneralEncoder {}; 50 | /// Decoder to read UTF-8 text. 51 | class Utf32Decoder : public UtfGeneralDecoder {}; 52 | 53 | /*********************************************************************** 54 | Encoding Test 55 | ***********************************************************************/ 56 | 57 | /// Guess the text encoding in a buffer. 58 | /// The buffer to guess. 59 | /// Size of the buffer in bytes. 60 | /// Returns the most possible encoding. 61 | /// Returns true if the BOM information is at the beginning of the buffer. 62 | extern void TestEncoding(unsigned char* buffer, vint size, BomEncoder::Encoding& encoding, bool& containsBom); 63 | } 64 | } 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /Source/Encoding/CharFormat/MbcsEncoding.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "CharFormat.h" 7 | 8 | namespace vl 9 | { 10 | namespace stream 11 | { 12 | /*********************************************************************** 13 | MbcsDecoder::ReadString 14 | ***********************************************************************/ 15 | 16 | extern bool IsMbcsLeadByte(char c); 17 | extern void MbcsToWChar(wchar_t* wideBuffer, vint wideChars, vint wideReaded, char* mbcsBuffer, vint mbcsChars); 18 | 19 | vint MbcsDecoder::ReadString(wchar_t* _buffer, vint chars) 20 | { 21 | char* source = new char[chars * 2]; 22 | char* reading = source; 23 | vint readed = 0; 24 | while (readed < chars) 25 | { 26 | if (stream->Read(reading, 1) != 1) 27 | { 28 | break; 29 | } 30 | if (IsMbcsLeadByte(*reading)) 31 | { 32 | if (stream->Read(reading + 1, 1) != 1) 33 | { 34 | break; 35 | } 36 | reading += 2; 37 | } 38 | else 39 | { 40 | reading++; 41 | } 42 | readed++; 43 | } 44 | 45 | MbcsToWChar(_buffer, chars, readed, source, (vint)(reading - source)); 46 | delete[] source; 47 | return readed; 48 | } 49 | 50 | /*********************************************************************** 51 | MbcsEncoder 52 | ***********************************************************************/ 53 | 54 | vint MbcsEncoder::Write(void* _buffer, vint _size) 55 | { 56 | // prepare a buffer for input 57 | vint availableChars = (cacheSize + _size) / sizeof(wchar_t); 58 | vint availableBytes = availableChars * sizeof(wchar_t); 59 | bool needToFree = false; 60 | vuint8_t* unicode = nullptr; 61 | if (cacheSize > 0) 62 | { 63 | unicode = new vuint8_t[cacheSize + _size]; 64 | memcpy(unicode, cacheBuffer, cacheSize); 65 | memcpy((vuint8_t*)unicode + cacheSize, _buffer, _size); 66 | needToFree = true; 67 | } 68 | else 69 | { 70 | unicode = (vuint8_t*)_buffer; 71 | } 72 | 73 | #if defined VCZH_WCHAR_UTF16 74 | if (availableChars > 0) 75 | { 76 | // a surrogate pair must be written as a whole thing 77 | vuint16_t c = (vuint16_t)((wchar_t*)unicode)[availableChars - 1]; 78 | if ((c & 0xFC00U) == 0xD800U) 79 | { 80 | availableChars -= 1; 81 | availableBytes -= sizeof(wchar_t); 82 | } 83 | } 84 | #endif 85 | 86 | // write the buffer 87 | if (availableChars > 0) 88 | { 89 | vint written = WriteString((wchar_t*)unicode, availableChars) * sizeof(wchar_t); 90 | CHECK_ERROR(written == availableBytes, L"MbcsEncoder::Write(void*, vint)#Failed to write a complete string."); 91 | } 92 | 93 | // cache the remaining 94 | cacheSize = cacheSize + _size - availableBytes; 95 | if (cacheSize > 0) 96 | { 97 | CHECK_ERROR(cacheSize <= sizeof(char32_t), L"MbcsEncoder::Write(void*, vint)#Unwritten text is too large to cache."); 98 | memcpy(cacheBuffer, unicode + availableBytes, cacheSize); 99 | } 100 | 101 | if (needToFree) delete[] unicode; 102 | return _size; 103 | } 104 | 105 | /*********************************************************************** 106 | MbcsDecoder::WriteString 107 | ***********************************************************************/ 108 | 109 | // implemented in platform dependent files 110 | 111 | /*********************************************************************** 112 | MbcsDecoder 113 | ***********************************************************************/ 114 | 115 | vint MbcsDecoder::Read(void* _buffer, vint _size) 116 | { 117 | vuint8_t* writing = (vuint8_t*)_buffer; 118 | vint filledBytes = 0; 119 | 120 | // feed the cache first 121 | if (cacheSize > 0) 122 | { 123 | filledBytes = cacheSize < _size ? cacheSize : _size; 124 | memcpy(writing, cacheBuffer, cacheSize); 125 | _size -= filledBytes; 126 | writing += filledBytes; 127 | 128 | // adjust the cache if it is not fully consumed 129 | cacheSize -= filledBytes; 130 | if (cacheSize > 0) 131 | { 132 | memcpy(cacheBuffer, cacheBuffer + filledBytes, cacheSize); 133 | } 134 | 135 | if (_size == 0) 136 | { 137 | return filledBytes; 138 | } 139 | } 140 | 141 | // fill the buffer as many as possible 142 | while (_size >= sizeof(wchar_t)) 143 | { 144 | vint availableChars = _size / sizeof(wchar_t); 145 | vint readBytes = ReadString((wchar_t*)writing, availableChars) * sizeof(wchar_t); 146 | if (readBytes == 0) break; 147 | filledBytes += readBytes; 148 | _size -= readBytes; 149 | writing += readBytes; 150 | } 151 | 152 | // cache the remaining wchar_t 153 | if (_size < sizeof(wchar_t)) 154 | { 155 | wchar_t c; 156 | vint readChars = ReadString(&c, 1) * sizeof(wchar_t); 157 | if (readChars == sizeof(wchar_t)) 158 | { 159 | vuint8_t* reading = (vuint8_t*)&c; 160 | memcpy(writing, reading, _size); 161 | filledBytes += _size; 162 | cacheSize = sizeof(wchar_t) - _size; 163 | memcpy(cacheBuffer, reading + _size, cacheSize); 164 | } 165 | } 166 | 167 | return filledBytes; 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /Source/Encoding/CharFormat/MbcsEncoding.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_ENCODING_CHARFORMAT_MBCSENCODING 7 | #define VCZH_STREAM_ENCODING_CHARFORMAT_MBCSENCODING 8 | 9 | #include "../Encoding.h" 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | /*********************************************************************** 16 | MbcsEncoder 17 | ***********************************************************************/ 18 | 19 | /// Encoder to write text in the local code page. 20 | class MbcsEncoder : public EncoderBase 21 | { 22 | protected: 23 | vuint8_t cacheBuffer[sizeof(char32_t)]; 24 | vint cacheSize = 0; 25 | 26 | vint WriteString(wchar_t* _buffer, vint chars); 27 | public: 28 | 29 | vint Write(void* _buffer, vint _size) override; 30 | }; 31 | 32 | /*********************************************************************** 33 | MbcsDecoder 34 | ***********************************************************************/ 35 | 36 | /// Decoder to read text in the local code page. 37 | class MbcsDecoder : public DecoderBase 38 | { 39 | protected: 40 | vuint8_t cacheBuffer[sizeof(wchar_t)]; 41 | vint cacheSize = 0; 42 | 43 | vint ReadString(wchar_t* _buffer, vint chars); 44 | public: 45 | 46 | vint Read(void* _buffer, vint _size) override; 47 | }; 48 | } 49 | } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /Source/Encoding/CharFormat/UtfEncoding.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "UtfEncoding.h" 7 | 8 | namespace vl 9 | { 10 | namespace stream 11 | { 12 | /*********************************************************************** 13 | UtfGeneralEncoder 14 | ***********************************************************************/ 15 | 16 | template 17 | vint UtfGeneralEncoder::Write(void* _buffer, vint _size) 18 | { 19 | // prepare a buffer for input 20 | vint availableChars = (cacheSize + _size) / sizeof(TExpect); 21 | vint availableBytes = availableChars * sizeof(TExpect); 22 | bool needToFree = false; 23 | vuint8_t* unicode = nullptr; 24 | if (cacheSize > 0) 25 | { 26 | unicode = new vuint8_t[cacheSize + _size]; 27 | memcpy(unicode, cacheBuffer, cacheSize); 28 | memcpy((vuint8_t*)unicode + cacheSize, _buffer, _size); 29 | needToFree = true; 30 | } 31 | else 32 | { 33 | unicode = (vuint8_t*)_buffer; 34 | } 35 | 36 | // write the buffer 37 | if (availableChars > 0) 38 | { 39 | TStringRangeReader reader((TExpect*)unicode, availableChars); 40 | while (TNative c = reader.Read()) 41 | { 42 | vint written = stream->Write(&c, sizeof(c)); 43 | if (written != sizeof(c)) 44 | { 45 | Close(); 46 | CHECK_FAIL(L"UtfGeneralEncoder::Write(void*, vint)#Failed to write a complete string."); 47 | } 48 | } 49 | auto cluster = reader.SourceCluster(); 50 | availableChars = cluster.index + cluster.size; 51 | availableBytes = availableChars * sizeof(TExpect); 52 | } 53 | 54 | // cache the remaining 55 | cacheSize = cacheSize + _size - availableBytes; 56 | if (cacheSize > 0) 57 | { 58 | CHECK_ERROR(cacheSize <= sizeof(cacheBuffer), L"UtfGeneralEncoder::Write(void*, vint)#Unwritten text is too large to cache."); 59 | memcpy(cacheBuffer, unicode + availableBytes, cacheSize); 60 | } 61 | 62 | if (needToFree) delete[] unicode; 63 | return _size; 64 | } 65 | 66 | /*********************************************************************** 67 | UtfGeneralDecoder 68 | ***********************************************************************/ 69 | 70 | template 71 | void UtfGeneralDecoder::Setup(IStream* _stream) 72 | { 73 | DecoderBase::Setup(_stream); 74 | reader.Setup(_stream); 75 | } 76 | 77 | template 78 | vint UtfGeneralDecoder::Read(void* _buffer, vint _size) 79 | { 80 | vuint8_t* writing = (vuint8_t*)_buffer; 81 | vint filledBytes = 0; 82 | 83 | // feed the cache first 84 | if (cacheSize > 0) 85 | { 86 | filledBytes = cacheSize < _size ? cacheSize : _size; 87 | memcpy(writing, cacheBuffer, cacheSize); 88 | _size -= filledBytes; 89 | writing += filledBytes; 90 | 91 | // adjust the cache if it is not fully consumed 92 | cacheSize -= filledBytes; 93 | if (cacheSize > 0) 94 | { 95 | memcpy(cacheBuffer, cacheBuffer + filledBytes, cacheSize); 96 | } 97 | 98 | if (_size == 0) 99 | { 100 | return filledBytes; 101 | } 102 | } 103 | 104 | // fill the buffer as many as possible 105 | while (_size >= sizeof(TExpect)) 106 | { 107 | vint availableChars = _size / sizeof(TExpect); 108 | vint readBytes = 0; 109 | for (vint i = 0; i < availableChars; i++) 110 | { 111 | TExpect c = reader.Read(); 112 | if (!c) break; 113 | *((TExpect*)writing) = c; 114 | writing += sizeof(TExpect); 115 | readBytes += sizeof(TExpect); 116 | } 117 | if (readBytes == 0) break; 118 | filledBytes += readBytes; 119 | _size -= readBytes; 120 | } 121 | 122 | // cache the remaining TExpect 123 | if (_size < sizeof(TExpect)) 124 | { 125 | if (TExpect c = reader.Read()) 126 | { 127 | vuint8_t* reading = (vuint8_t*)&c; 128 | memcpy(writing, reading, _size); 129 | filledBytes += _size; 130 | cacheSize = sizeof(TExpect) - _size; 131 | memcpy(cacheBuffer, reading + _size, cacheSize); 132 | } 133 | } 134 | 135 | return filledBytes; 136 | } 137 | 138 | /*********************************************************************** 139 | UtfGeneralEncoder 140 | ***********************************************************************/ 141 | 142 | template 143 | vint UtfGeneralEncoder::Write(void* _buffer, vint _size) 144 | { 145 | return stream->Write(_buffer, _size); 146 | } 147 | 148 | /*********************************************************************** 149 | UtfGeneralDecoder 150 | ***********************************************************************/ 151 | 152 | template 153 | vint UtfGeneralDecoder::Read(void* _buffer, vint _size) 154 | { 155 | return stream->Read(_buffer, _size); 156 | } 157 | 158 | /*********************************************************************** 159 | Unicode General (extern templates) 160 | ***********************************************************************/ 161 | 162 | template class UtfGeneralEncoder; 163 | template class UtfGeneralEncoder; 164 | template class UtfGeneralEncoder; 165 | template class UtfGeneralEncoder; 166 | template class UtfGeneralEncoder; 167 | 168 | template class UtfGeneralEncoder; 169 | template class UtfGeneralEncoder; 170 | template class UtfGeneralEncoder; 171 | template class UtfGeneralEncoder; 172 | template class UtfGeneralEncoder; 173 | 174 | template class UtfGeneralEncoder; 175 | template class UtfGeneralEncoder; 176 | template class UtfGeneralEncoder; 177 | template class UtfGeneralEncoder; 178 | template class UtfGeneralEncoder; 179 | 180 | template class UtfGeneralEncoder; 181 | template class UtfGeneralEncoder; 182 | template class UtfGeneralEncoder; 183 | template class UtfGeneralEncoder; 184 | template class UtfGeneralEncoder; 185 | 186 | template class UtfGeneralEncoder; 187 | template class UtfGeneralEncoder; 188 | template class UtfGeneralEncoder; 189 | template class UtfGeneralEncoder; 190 | template class UtfGeneralEncoder; 191 | 192 | template class UtfGeneralDecoder; 193 | template class UtfGeneralDecoder; 194 | template class UtfGeneralDecoder; 195 | template class UtfGeneralDecoder; 196 | template class UtfGeneralDecoder; 197 | 198 | template class UtfGeneralDecoder; 199 | template class UtfGeneralDecoder; 200 | template class UtfGeneralDecoder; 201 | template class UtfGeneralDecoder; 202 | template class UtfGeneralDecoder; 203 | 204 | template class UtfGeneralDecoder; 205 | template class UtfGeneralDecoder; 206 | template class UtfGeneralDecoder; 207 | template class UtfGeneralDecoder; 208 | template class UtfGeneralDecoder; 209 | 210 | template class UtfGeneralDecoder; 211 | template class UtfGeneralDecoder; 212 | template class UtfGeneralDecoder; 213 | template class UtfGeneralDecoder; 214 | template class UtfGeneralDecoder; 215 | 216 | template class UtfGeneralDecoder; 217 | template class UtfGeneralDecoder; 218 | template class UtfGeneralDecoder; 219 | template class UtfGeneralDecoder; 220 | template class UtfGeneralDecoder; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /Source/Encoding/CharFormat/UtfEncoding.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_ENCODING_CHARFORMAT_UTFENCODING 7 | #define VCZH_STREAM_ENCODING_CHARFORMAT_UTFENCODING 8 | 9 | #include "../Encoding.h" 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | 16 | /*********************************************************************** 17 | UtfStreamConsumer 18 | ***********************************************************************/ 19 | 20 | template 21 | class UtfStreamConsumer : public Object 22 | { 23 | protected: 24 | IStream* stream = nullptr; 25 | 26 | T Consume() 27 | { 28 | T c; 29 | vint size = stream->Read(&c, sizeof(c)); 30 | if (size != sizeof(c)) return 0; 31 | return c; 32 | } 33 | public: 34 | void Setup(IStream* _stream) 35 | { 36 | stream = _stream; 37 | } 38 | 39 | bool HasIllegalChar() const 40 | { 41 | return false; 42 | } 43 | }; 44 | 45 | template 46 | class UtfStreamConsumerApiRedirection : public Object 47 | { 48 | private: 49 | T& internalConsumer; 50 | 51 | public: 52 | UtfStreamConsumerApiRedirection(T& _internalConsumer) 53 | : internalConsumer(_internalConsumer) 54 | { 55 | } 56 | 57 | void Setup(IStream* _stream) 58 | { 59 | internalConsumer.Setup(_stream); 60 | } 61 | 62 | encoding::UtfCharCluster SourceCluster() const 63 | { 64 | return internalConsumer.SourceCluster(); 65 | } 66 | }; 67 | 68 | /*********************************************************************** 69 | UtfStreamToStreamReader 70 | ***********************************************************************/ 71 | 72 | template 73 | using UtfStreamToStreamReader = encoding::UtfToUtfReaderBase, UtfStreamConsumerApiRedirection>; 74 | 75 | /*********************************************************************** 76 | Unicode General 77 | ***********************************************************************/ 78 | 79 | template 80 | struct MaxPossibleCodePoints 81 | { 82 | static const vint Value = encoding::UtfConversion::BufferLength; 83 | }; 84 | 85 | template<> 86 | struct MaxPossibleCodePoints 87 | { 88 | static const vint Value = 1; 89 | }; 90 | 91 | template 92 | class UtfGeneralEncoder : public EncoderBase 93 | { 94 | using TStringRangeReader = encoding::UtfStringRangeToStringRangeReader; 95 | protected: 96 | vuint8_t cacheBuffer[sizeof(TExpect) * MaxPossibleCodePoints::Value]; 97 | vint cacheSize = 0; 98 | 99 | public: 100 | 101 | vint Write(void* _buffer, vint _size) override; 102 | }; 103 | 104 | template 105 | class UtfGeneralDecoder : public DecoderBase 106 | { 107 | using TStreamReader = UtfStreamToStreamReader; 108 | protected: 109 | vuint8_t cacheBuffer[sizeof(TExpect)]; 110 | vint cacheSize = 0; 111 | TStreamReader reader; 112 | 113 | public: 114 | 115 | void Setup(IStream* _stream) override; 116 | vint Read(void* _buffer, vint _size) override; 117 | }; 118 | 119 | /*********************************************************************** 120 | Unicode General (without conversion) 121 | ***********************************************************************/ 122 | 123 | template 124 | class UtfGeneralEncoder : public EncoderBase 125 | { 126 | public: 127 | vint Write(void* _buffer, vint _size) override; 128 | }; 129 | 130 | template 131 | class UtfGeneralDecoder : public DecoderBase 132 | { 133 | public: 134 | vint Read(void* _buffer, vint _size) override; 135 | }; 136 | 137 | #if defined VCZH_WCHAR_UTF16 138 | 139 | template<> 140 | class UtfGeneralEncoder : public UtfGeneralEncoder {}; 141 | 142 | template<> 143 | class UtfGeneralEncoder : public UtfGeneralEncoder {}; 144 | 145 | #elif defined VCZH_WCHAR_UTF32 146 | 147 | template<> 148 | class UtfGeneralEncoder : public UtfGeneralEncoder {}; 149 | 150 | template<> 151 | class UtfGeneralEncoder : public UtfGeneralEncoder {}; 152 | 153 | #endif 154 | 155 | /*********************************************************************** 156 | Unicode General (extern templates) 157 | ***********************************************************************/ 158 | 159 | extern template class UtfGeneralEncoder; 160 | extern template class UtfGeneralEncoder; 161 | extern template class UtfGeneralEncoder; 162 | extern template class UtfGeneralEncoder; 163 | extern template class UtfGeneralEncoder; 164 | 165 | extern template class UtfGeneralEncoder; 166 | extern template class UtfGeneralEncoder; 167 | extern template class UtfGeneralEncoder; 168 | extern template class UtfGeneralEncoder; 169 | extern template class UtfGeneralEncoder; 170 | 171 | extern template class UtfGeneralEncoder; 172 | extern template class UtfGeneralEncoder; 173 | extern template class UtfGeneralEncoder; 174 | extern template class UtfGeneralEncoder; 175 | extern template class UtfGeneralEncoder; 176 | 177 | extern template class UtfGeneralEncoder; 178 | extern template class UtfGeneralEncoder; 179 | extern template class UtfGeneralEncoder; 180 | extern template class UtfGeneralEncoder; 181 | extern template class UtfGeneralEncoder; 182 | 183 | extern template class UtfGeneralEncoder; 184 | extern template class UtfGeneralEncoder; 185 | extern template class UtfGeneralEncoder; 186 | extern template class UtfGeneralEncoder; 187 | extern template class UtfGeneralEncoder; 188 | 189 | extern template class UtfGeneralDecoder; 190 | extern template class UtfGeneralDecoder; 191 | extern template class UtfGeneralDecoder; 192 | extern template class UtfGeneralDecoder; 193 | extern template class UtfGeneralDecoder; 194 | 195 | extern template class UtfGeneralDecoder; 196 | extern template class UtfGeneralDecoder; 197 | extern template class UtfGeneralDecoder; 198 | extern template class UtfGeneralDecoder; 199 | extern template class UtfGeneralDecoder; 200 | 201 | extern template class UtfGeneralDecoder; 202 | extern template class UtfGeneralDecoder; 203 | extern template class UtfGeneralDecoder; 204 | extern template class UtfGeneralDecoder; 205 | extern template class UtfGeneralDecoder; 206 | 207 | extern template class UtfGeneralDecoder; 208 | extern template class UtfGeneralDecoder; 209 | extern template class UtfGeneralDecoder; 210 | extern template class UtfGeneralDecoder; 211 | extern template class UtfGeneralDecoder; 212 | 213 | extern template class UtfGeneralDecoder; 214 | extern template class UtfGeneralDecoder; 215 | extern template class UtfGeneralDecoder; 216 | extern template class UtfGeneralDecoder; 217 | extern template class UtfGeneralDecoder; 218 | } 219 | } 220 | 221 | #endif 222 | -------------------------------------------------------------------------------- /Source/Encoding/Encoding.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "Encoding.h" 7 | 8 | namespace vl 9 | { 10 | namespace stream 11 | { 12 | /*********************************************************************** 13 | EncoderBase 14 | ***********************************************************************/ 15 | 16 | void EncoderBase::Setup(IStream* _stream) 17 | { 18 | stream = _stream; 19 | } 20 | 21 | void EncoderBase::Close() 22 | { 23 | } 24 | 25 | /*********************************************************************** 26 | DecoderBase 27 | ***********************************************************************/ 28 | 29 | void DecoderBase::Setup(IStream* _stream) 30 | { 31 | stream = _stream; 32 | } 33 | 34 | void DecoderBase::Close() 35 | { 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Source/Encoding/Encoding.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_ENCODING_ENCODING 7 | #define VCZH_STREAM_ENCODING_ENCODING 8 | 9 | #include "../Stream/Interfaces.h" 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | /*********************************************************************** 16 | IEncoder and IDecoder 17 | ***********************************************************************/ 18 | 19 | /// Encoder interface. This interface defines a writable transformation from one stream to another stream. You can create a [T:vl.stream.EncoderStream] after you have an encoder. 20 | class IEncoder : public Interface 21 | { 22 | public: 23 | /// Set a target writable stream to receive data. transforms the content and write to this tream. 24 | /// The target writable stream. 25 | virtual void Setup(IStream* _stream)=0; 26 | /// Stop the transformation, ensuring all content is written to the target stream. 27 | virtual void Close()=0; 28 | /// 29 | /// Transform content and write to the target stream. 30 | /// This function could use caching to improve performance. 31 | /// Please do not expect that all transformed content will be written to the target stream immediately. 32 | /// 33 | /// 34 | /// Returns the actual size of the content that has written before transforming. 35 | /// A successful write operation may only cache the data without actually write anything to the target stream. 36 | /// 37 | /// A buffer storing the content to transform. 38 | /// The expected size of the content in bytes in "_buffer" to use. 39 | virtual vint Write(void* _buffer, vint _size)=0; 40 | }; 41 | 42 | /// Decoder interface. This interface defines a readable transformation from one stream to another stream. You can create a [T:vl.stream.DecoderStream] after you have an decoder. 43 | class IDecoder : public Interface 44 | { 45 | public: 46 | /// 47 | /// Set a target readable stream. 48 | /// reads from this tream and transform the content. 49 | /// 50 | /// The target readable stream. 51 | virtual void Setup(IStream* _stream)=0; 52 | /// Stop the transformation. 53 | virtual void Close()=0; 54 | /// Read from the target stream and transform the content. 55 | /// Returns the actual size of the content has read after transforming. 56 | /// A buffer to store the content. 57 | /// The expected size of the content in bytes in "_buffer" to receive. 58 | virtual vint Read(void* _buffer, vint _size)=0; 59 | }; 60 | 61 | /*********************************************************************** 62 | EncoderBase and DecoderBase 63 | ***********************************************************************/ 64 | 65 | /// Basic implementation of IEncoder. 66 | class EncoderBase : public Object, public IEncoder 67 | { 68 | protected: 69 | IStream* stream = nullptr; 70 | 71 | public: 72 | 73 | void Setup(IStream* _stream) override; 74 | void Close() override; 75 | }; 76 | 77 | /// Basic implementation of IDecoder. 78 | class DecoderBase : public Object, public IDecoder 79 | { 80 | protected: 81 | IStream* stream = nullptr; 82 | 83 | public: 84 | 85 | void Setup(IStream* _stream) override; 86 | void Close() override; 87 | }; 88 | } 89 | } 90 | 91 | #endif -------------------------------------------------------------------------------- /Source/Encoding/LzwEncoding.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_ENCODING_LZWENCODING 7 | #define VCZH_STREAM_ENCODING_LZWENCODING 8 | 9 | #include "Encoding.h" 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | 16 | /*********************************************************************** 17 | Compression 18 | ***********************************************************************/ 19 | 20 | namespace lzw 21 | { 22 | static const vint BufferSize = 1024; 23 | static const vint MaxDictionarySize = 1 << 24; 24 | 25 | struct Code 26 | { 27 | typedef collections::PushOnlyAllocator CodeAllocator; 28 | typedef collections::ByteObjectMap::Allocator MapAllocator; 29 | 30 | vuint8_t byte = 0; 31 | vint code = -1; 32 | Code* parent = 0; 33 | vint size = 0; 34 | collections::ByteObjectMap children; 35 | }; 36 | } 37 | 38 | class LzwBase : public Object 39 | { 40 | protected: 41 | lzw::Code::CodeAllocator codeAllocator; 42 | lzw::Code::MapAllocator mapAllocator; 43 | lzw::Code* root; 44 | vint eofIndex = -1; 45 | vint nextIndex = 0; 46 | vint indexBits = 1; 47 | 48 | void UpdateIndexBits(); 49 | lzw::Code* CreateCode(lzw::Code* parent, vuint8_t byte); 50 | 51 | LzwBase(); 52 | LzwBase(bool (&existingBytes)[256]); 53 | ~LzwBase(); 54 | }; 55 | 56 | /// An encoder to compress data using the Lzw algorithm. 57 | /// 58 | /// You are not recommended to compress data more than 1 mega bytes at once using the encoder directly. 59 | /// and is recommended. 60 | /// 61 | class LzwEncoder : public LzwBase, public EncoderBase 62 | { 63 | protected: 64 | vuint8_t buffer[lzw::BufferSize]; 65 | vint bufferUsedBits = 0; 66 | lzw::Code* prefix; 67 | 68 | void Flush(); 69 | void WriteNumber(vint number, vint bitSize); 70 | public: 71 | /// Create an encoder. 72 | LzwEncoder(); 73 | /// Create an encoder, specifying what bytes will never appear in the data to compress. 74 | /// 75 | /// A filter array 76 | /// If existingBytes[x] == true, it means x will possibly appear. 77 | /// If existingBytes[x] == false, it means x will never appear. 78 | /// 79 | /// 80 | /// The behavior is undefined, if existingBytes[x] == false, but byte x is actually in the data to compress. 81 | /// 82 | LzwEncoder(bool (&existingBytes)[256]); 83 | ~LzwEncoder(); 84 | 85 | void Close()override; 86 | vint Write(void* _buffer, vint _size)override; 87 | }; 88 | 89 | /// An decoder to decompress data using the Lzw algorithm. 90 | /// 91 | /// You are not recommended to compress data more than 1 mega bytes at once using the encoder directly. 92 | /// and is recommended. 93 | /// 94 | class LzwDecoder :public LzwBase, public DecoderBase 95 | { 96 | protected: 97 | collections::List dictionary; 98 | lzw::Code* lastCode = 0; 99 | 100 | vuint8_t inputBuffer[lzw::BufferSize]; 101 | vint inputBufferSize = 0; 102 | vint inputBufferUsedBits = 0; 103 | 104 | collections::Array outputBuffer; 105 | vint outputBufferSize = 0; 106 | vint outputBufferUsedBytes = 0; 107 | 108 | bool ReadNumber(vint& number, vint bitSize); 109 | void PrepareOutputBuffer(vint size); 110 | void ExpandCodeToOutputBuffer(lzw::Code* code); 111 | public: 112 | /// Create a decoder. 113 | LzwDecoder(); 114 | /// Create an encoder, specifying what bytes will never appear in the decompressed data. 115 | /// 116 | /// A filter array 117 | /// If existingBytes[x] == true, it means x will possibly appear. 118 | /// If existingBytes[x] == false, it means x will never appear. 119 | /// 120 | /// 121 | /// The array "existingBytes" should exactly match the one given to . 122 | /// 123 | LzwDecoder(bool (&existingBytes)[256]); 124 | ~LzwDecoder(); 125 | 126 | vint Read(void* _buffer, vint _size)override; 127 | }; 128 | 129 | /*********************************************************************** 130 | Helper Functions 131 | ***********************************************************************/ 132 | 133 | /// Copy data from a readable input stream to a writable output stream. 134 | /// Data copied in bytes. 135 | /// The readable input stream. 136 | /// The writable output stream. 137 | extern vint CopyStream(stream::IStream& inputStream, stream::IStream& outputStream); 138 | 139 | /// Compress data from a readable input stream to a writable output stream. 140 | /// Data copied in bytes. 141 | /// The readable input stream. 142 | /// The writable output stream. 143 | /// 144 | /// Data is compressed in multiple batches, 145 | /// the is expected output stream to have data in multiple parts. 146 | /// In each part, the first 4 bytes is the data before compression in bytes. 147 | /// the rest is the compressed data. 148 | /// 149 | /// 172 | extern void CompressStream(stream::IStream& inputStream, stream::IStream& outputStream); 173 | 174 | /// Decompress data from a readable input stream (with compressed data) to a writable output stream (with uncompressed data). 175 | /// Data copied in bytes. 176 | /// The readable input stream. 177 | /// The writable output stream. 178 | /// 179 | /// Data is compressed in multiple batches, 180 | /// the is expected input stream to have data in multiple parts. 181 | /// In each part, the first 4 bytes is the data before compression in bytes. 182 | /// the rest is the compressed data. 183 | /// 184 | /// 207 | extern void DecompressStream(stream::IStream& inputStream, stream::IStream& outputStream); 208 | } 209 | } 210 | 211 | #endif -------------------------------------------------------------------------------- /Source/FileSystem.Linux.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "FileSystem.h" 7 | #include "Locale.h" 8 | #include "Stream/FileStream.h" 9 | #include "Stream/MemoryWrapperStream.h" 10 | #include "Stream/Accessor.h" 11 | #include "Stream/EncodingStream.h" 12 | #include 13 | #include 14 | #include 15 | 16 | #ifndef VCZH_GCC 17 | static_assert(false, "Do not build this file for Windows applications."); 18 | #endif 19 | 20 | namespace vl 21 | { 22 | namespace filesystem 23 | { 24 | using namespace collections; 25 | using namespace stream; 26 | 27 | /*********************************************************************** 28 | FilePath 29 | ***********************************************************************/ 30 | 31 | const wchar_t FilePath::Delimiter; 32 | 33 | void FilePath::Initialize() 34 | { 35 | { 36 | Array buffer(fullPath.Length() + 1); 37 | wcscpy(&buffer[0], fullPath.Buffer()); 38 | NormalizeDelimiters(buffer); 39 | fullPath = &buffer[0]; 40 | } 41 | 42 | if (fullPath.Length() == 0) 43 | fullPath = WString::Unmanaged(L"/"); 44 | 45 | if (fullPath[0] != Delimiter) 46 | { 47 | char buffer[PATH_MAX] = { 0 }; 48 | getcwd(buffer, PATH_MAX); 49 | fullPath = atow(AString(buffer)) + WString::FromChar(Delimiter) + fullPath; 50 | } 51 | 52 | { 53 | collections::List components; 54 | GetPathComponents(fullPath, components); 55 | for (int i = 0; i < components.Count(); i++) 56 | { 57 | if (components[i] == L".") 58 | { 59 | components.RemoveAt(i); 60 | i--; 61 | } 62 | else if (components[i] == L"..") 63 | { 64 | if (i > 0) 65 | { 66 | components.RemoveAt(i); 67 | components.RemoveAt(i - 1); 68 | i -= 2; 69 | } 70 | else 71 | { 72 | throw ArgumentException(L"Illegal path."); 73 | } 74 | } 75 | } 76 | 77 | fullPath = ComponentsToPath(components); 78 | } 79 | 80 | TrimLastDelimiter(fullPath); 81 | } 82 | 83 | bool FilePath::IsFile()const 84 | { 85 | struct stat info; 86 | AString path = wtoa(fullPath); 87 | int result = stat(path.Buffer(), &info); 88 | if(result != 0) return false; 89 | else return S_ISREG(info.st_mode); 90 | } 91 | 92 | bool FilePath::IsFolder()const 93 | { 94 | struct stat info; 95 | AString path = wtoa(fullPath); 96 | int result = stat(path.Buffer(), &info); 97 | if(result != 0) return false; 98 | else return S_ISDIR(info.st_mode); 99 | } 100 | 101 | bool FilePath::IsRoot()const 102 | { 103 | return fullPath == L"/"; 104 | } 105 | 106 | WString FilePath::GetRelativePathFor(const FilePath& _filePath) const 107 | { 108 | if (fullPath.Length() == 0 || _filePath.fullPath.Length() == 0 || fullPath[0] != _filePath.fullPath[0]) 109 | { 110 | return _filePath.fullPath; 111 | } 112 | 113 | collections::List srcComponents, tgtComponents, resultComponents; 114 | GetPathComponents(IsFolder() ? fullPath : GetFolder().GetFullPath(), srcComponents); 115 | GetPathComponents(_filePath.fullPath, tgtComponents); 116 | 117 | int minLength = srcComponents.Count() <= tgtComponents.Count() ? srcComponents.Count() : tgtComponents.Count(); 118 | int lastCommonComponent = 0; 119 | for (int i = 0; i < minLength; i++) 120 | { 121 | if (srcComponents[i] == tgtComponents[i]) 122 | { 123 | lastCommonComponent = i; 124 | } 125 | else 126 | break; 127 | } 128 | 129 | for (int i = lastCommonComponent + 1; i < srcComponents.Count(); i++) 130 | { 131 | resultComponents.Add(L".."); 132 | } 133 | 134 | for (int i = lastCommonComponent + 1; i < tgtComponents.Count(); i++) 135 | { 136 | resultComponents.Add(tgtComponents[i]); 137 | } 138 | 139 | return ComponentsToPath(resultComponents); 140 | } 141 | 142 | /*********************************************************************** 143 | File 144 | ***********************************************************************/ 145 | 146 | bool File::Delete()const 147 | { 148 | AString path = wtoa(filePath.GetFullPath()); 149 | return unlink(path.Buffer()) == 0; 150 | } 151 | 152 | bool File::Rename(const WString& newName)const 153 | { 154 | AString oldFileName = wtoa(filePath.GetFullPath()); 155 | AString newFileName = wtoa((filePath.GetFolder() / newName).GetFullPath()); 156 | return rename(oldFileName.Buffer(), newFileName.Buffer()) == 0; 157 | } 158 | 159 | /*********************************************************************** 160 | Folder 161 | ***********************************************************************/ 162 | 163 | bool Folder::GetFolders(collections::List& folders)const 164 | { 165 | if (!Exists()) return false; 166 | 167 | DIR *dir; 168 | AString searchPath = wtoa(filePath.GetFullPath()); 169 | 170 | if ((dir = opendir(searchPath.Buffer())) == NULL) 171 | { 172 | return false; 173 | } 174 | 175 | struct dirent* entry; 176 | while ((entry = readdir(dir)) != NULL) 177 | { 178 | WString childName = atow(AString(entry->d_name)); 179 | FilePath childFullPath = filePath / childName; 180 | if (childName != L"." && childName != L".." && childFullPath.IsFolder()) 181 | { 182 | folders.Add(Folder(childFullPath)); 183 | } 184 | } 185 | 186 | if (closedir(dir) != 0) 187 | { 188 | return false; 189 | } 190 | 191 | return true; 192 | } 193 | 194 | bool Folder::GetFiles(collections::List& files)const 195 | { 196 | if (!Exists()) return false; 197 | 198 | DIR* dir; 199 | AString searchPath = wtoa(filePath.GetFullPath()); 200 | 201 | if ((dir = opendir(searchPath.Buffer())) == NULL) 202 | { 203 | return false; 204 | } 205 | 206 | struct dirent* entry; 207 | while ((entry = readdir(dir)) != NULL) 208 | { 209 | FilePath childFullPath = filePath / (atow(AString(entry->d_name))); 210 | if (childFullPath.IsFile()) 211 | { 212 | files.Add(File(childFullPath)); 213 | } 214 | } 215 | 216 | if (closedir(dir) != 0) 217 | { 218 | return false; 219 | } 220 | 221 | return true; 222 | } 223 | 224 | bool Folder::CreateNonRecursively()const 225 | { 226 | AString path = wtoa(filePath.GetFullPath()); 227 | return mkdir(path.Buffer(), 0777) == 0; 228 | } 229 | 230 | bool Folder::DeleteNonRecursively()const 231 | { 232 | AString path = wtoa(filePath.GetFullPath()); 233 | return rmdir(path.Buffer()) == 0; 234 | } 235 | 236 | bool Folder::Rename(const WString& newName)const 237 | { 238 | AString oldFileName = wtoa(filePath.GetFullPath()); 239 | AString newFileName = wtoa((filePath.GetFolder() / newName).GetFullPath()); 240 | return rename(oldFileName.Buffer(), newFileName.Buffer()) == 0; 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /Source/FileSystem.Windows.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "FileSystem.h" 7 | #include "Locale.h" 8 | #include "Stream/FileStream.h" 9 | #include "Stream/MemoryWrapperStream.h" 10 | #include "Stream/Accessor.h" 11 | #include "Stream/EncodingStream.h" 12 | #include 13 | #include 14 | 15 | #ifndef VCZH_MSVC 16 | static_assert(false, "Do not build this file for non-Windows applications."); 17 | #endif 18 | 19 | #pragma comment(lib, "Shlwapi.lib") 20 | 21 | namespace vl 22 | { 23 | namespace filesystem 24 | { 25 | using namespace collections; 26 | using namespace stream; 27 | 28 | /*********************************************************************** 29 | FilePath 30 | ***********************************************************************/ 31 | 32 | void FilePath::Initialize() 33 | { 34 | { 35 | Array buffer(fullPath.Length() + 1); 36 | wcscpy_s(&buffer[0], fullPath.Length() + 1, fullPath.Buffer()); 37 | NormalizeDelimiters(buffer); 38 | fullPath = &buffer[0]; 39 | } 40 | 41 | if (fullPath != L"") 42 | { 43 | if (fullPath.Length() < 2 || fullPath[1] != L':') 44 | { 45 | wchar_t buffer[MAX_PATH + 1] = { 0 }; 46 | auto result = GetCurrentDirectory(sizeof(buffer) / sizeof(*buffer), buffer); 47 | if (result > MAX_PATH + 1 || result == 0) 48 | { 49 | throw ArgumentException(L"Failed to call GetCurrentDirectory.", L"vl::filesystem::FilePath::Initialize", L""); 50 | } 51 | fullPath = WString(buffer) + L"\\" + fullPath; 52 | } 53 | { 54 | wchar_t buffer[MAX_PATH + 1] = { 0 }; 55 | if (fullPath.Length() == 2 && fullPath[1] == L':') 56 | { 57 | fullPath += L"\\"; 58 | } 59 | auto result = GetFullPathName(fullPath.Buffer(), sizeof(buffer) / sizeof(*buffer), buffer, NULL); 60 | if (result > MAX_PATH + 1 || result == 0) 61 | { 62 | throw ArgumentException(L"The path is illegal.", L"vl::filesystem::FilePath::FilePath", L"_filePath"); 63 | } 64 | 65 | { 66 | wchar_t shortPath[MAX_PATH + 1]; 67 | wchar_t longPath[MAX_PATH + 1]; 68 | if (GetShortPathName(buffer, shortPath, MAX_PATH) > 0) 69 | { 70 | if (GetLongPathName(shortPath, longPath, MAX_PATH) > 0) 71 | { 72 | memcpy(buffer, longPath, sizeof(buffer)); 73 | } 74 | } 75 | } 76 | fullPath = buffer; 77 | } 78 | } 79 | 80 | TrimLastDelimiter(fullPath); 81 | } 82 | 83 | bool FilePath::IsFile()const 84 | { 85 | WIN32_FILE_ATTRIBUTE_DATA info; 86 | BOOL result = GetFileAttributesEx(fullPath.Buffer(), GetFileExInfoStandard, &info); 87 | if (!result) return false; 88 | return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0; 89 | } 90 | 91 | bool FilePath::IsFolder()const 92 | { 93 | WIN32_FILE_ATTRIBUTE_DATA info; 94 | BOOL result = GetFileAttributesEx(fullPath.Buffer(), GetFileExInfoStandard, &info); 95 | if (!result) return false; 96 | return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; 97 | } 98 | 99 | bool FilePath::IsRoot()const 100 | { 101 | return fullPath == L""; 102 | } 103 | 104 | WString FilePath::GetRelativePathFor(const FilePath& _filePath) const 105 | { 106 | if (fullPath.Length() == 0 || _filePath.fullPath.Length() == 0 || fullPath[0] != _filePath.fullPath[0]) 107 | { 108 | return _filePath.fullPath; 109 | } 110 | 111 | wchar_t buffer[MAX_PATH + 1] = { 0 }; 112 | PathRelativePathTo( 113 | buffer, 114 | fullPath.Buffer(), 115 | (IsFolder() ? FILE_ATTRIBUTE_DIRECTORY : 0), 116 | _filePath.fullPath.Buffer(), 117 | (_filePath.IsFolder() ? FILE_ATTRIBUTE_DIRECTORY : 0) 118 | ); 119 | return buffer; 120 | } 121 | 122 | /*********************************************************************** 123 | File 124 | ***********************************************************************/ 125 | 126 | bool File::Delete()const 127 | { 128 | return DeleteFile(filePath.GetFullPath().Buffer()) != 0; 129 | } 130 | 131 | bool File::Rename(const WString& newName)const 132 | { 133 | WString oldFileName = filePath.GetFullPath(); 134 | WString newFileName = (filePath.GetFolder() / newName).GetFullPath(); 135 | return MoveFile(oldFileName.Buffer(), newFileName.Buffer()) != 0; 136 | } 137 | 138 | /*********************************************************************** 139 | Folder 140 | ***********************************************************************/ 141 | 142 | bool Folder::GetFolders(collections::List& folders)const 143 | { 144 | if (filePath.IsRoot()) 145 | { 146 | auto bufferSize = GetLogicalDriveStrings(0, nullptr); 147 | if (bufferSize > 0) 148 | { 149 | Array buffer(bufferSize); 150 | if (GetLogicalDriveStrings((DWORD)buffer.Count(), &buffer[0]) > 0) 151 | { 152 | auto begin = &buffer[0]; 153 | auto end = begin + buffer.Count(); 154 | while (begin < end && *begin) 155 | { 156 | WString driveString = begin; 157 | begin += driveString.Length() + 1; 158 | folders.Add(Folder(FilePath(driveString))); 159 | } 160 | return true; 161 | } 162 | } 163 | return false; 164 | } 165 | else 166 | { 167 | if (!Exists()) return false; 168 | WIN32_FIND_DATA findData; 169 | HANDLE findHandle = INVALID_HANDLE_VALUE; 170 | 171 | while (true) 172 | { 173 | if (findHandle == INVALID_HANDLE_VALUE) 174 | { 175 | WString searchPath = (filePath / L"*").GetFullPath(); 176 | findHandle = FindFirstFile(searchPath.Buffer(), &findData); 177 | if (findHandle == INVALID_HANDLE_VALUE) 178 | { 179 | break; 180 | } 181 | } 182 | else 183 | { 184 | BOOL result = FindNextFile(findHandle, &findData); 185 | if (result == 0) 186 | { 187 | FindClose(findHandle); 188 | break; 189 | } 190 | } 191 | 192 | if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 193 | { 194 | if (wcscmp(findData.cFileName, L".") != 0 && wcscmp(findData.cFileName, L"..") != 0) 195 | { 196 | folders.Add(Folder(filePath / findData.cFileName)); 197 | } 198 | } 199 | } 200 | return true; 201 | } 202 | } 203 | 204 | bool Folder::GetFiles(collections::List& files)const 205 | { 206 | if (filePath.IsRoot()) 207 | { 208 | return true; 209 | } 210 | if (!Exists()) return false; 211 | WIN32_FIND_DATA findData; 212 | HANDLE findHandle = INVALID_HANDLE_VALUE; 213 | 214 | while (true) 215 | { 216 | if (findHandle == INVALID_HANDLE_VALUE) 217 | { 218 | WString searchPath = (filePath / L"*").GetFullPath(); 219 | findHandle = FindFirstFile(searchPath.Buffer(), &findData); 220 | if (findHandle == INVALID_HANDLE_VALUE) 221 | { 222 | break; 223 | } 224 | } 225 | else 226 | { 227 | BOOL result = FindNextFile(findHandle, &findData); 228 | if (result == 0) 229 | { 230 | FindClose(findHandle); 231 | break; 232 | } 233 | } 234 | 235 | if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 236 | { 237 | files.Add(File(filePath / findData.cFileName)); 238 | } 239 | } 240 | return true; 241 | } 242 | 243 | bool Folder::CreateNonRecursively()const 244 | { 245 | return CreateDirectory(filePath.GetFullPath().Buffer(), NULL) != 0; 246 | } 247 | 248 | bool Folder::DeleteNonRecursively()const 249 | { 250 | return RemoveDirectory(filePath.GetFullPath().Buffer()) != 0; 251 | } 252 | 253 | bool Folder::Rename(const WString& newName)const 254 | { 255 | WString oldFileName = filePath.GetFullPath(); 256 | WString newFileName = (filePath.GetFolder() / newName).GetFullPath(); 257 | return MoveFile(oldFileName.Buffer(), newFileName.Buffer()) != 0; 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /Source/HttpUtility.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_HTTPUTILITY 7 | #define VCZH_HTTPUTILITY 8 | 9 | #include 10 | 11 | #ifdef VCZH_MSVC 12 | 13 | namespace vl 14 | { 15 | 16 | /*********************************************************************** 17 | HTTP Utility 18 | ***********************************************************************/ 19 | 20 | /// An http requiest. 21 | class HttpRequest 22 | { 23 | typedef collections::Array BodyBuffer; 24 | typedef collections::List StringList; 25 | typedef collections::Dictionary HeaderMap; 26 | public: 27 | /// Name of the server, like "gaclib.net". 28 | WString server; 29 | /// Port of the server, like 80. 30 | vint port = 0; 31 | /// Query of the request, like "/index.html". 32 | WString query; 33 | /// Set to true if the request uses SSL, or https. 34 | bool secure = false; 35 | /// User name to authorize. Set to empty if authorization is not needed. 36 | WString username; 37 | /// Password to authorize. Set to empty if authorization is not needed. 38 | WString password; 39 | /// HTTP method, like "GET", "POST", "PUT", "DELETE", etc. 40 | WString method; 41 | /// Cookie. Set to empty if cookie is not needed. 42 | WString cookie; 43 | /// Request body. This is a byte array. 44 | BodyBuffer body; 45 | /// Content type, like "text/xml". 46 | WString contentType; 47 | /// Accept type list, elements like "text/xml". 48 | StringList acceptTypes; 49 | /// A dictionary to contain extra headers. 50 | HeaderMap extraHeaders; 51 | 52 | /// Create an empty request. 53 | HttpRequest() = default; 54 | 55 | /// Set , , and fields for you using an URL. 56 | /// Returns true if this operation succeeded. 57 | /// The URL. 58 | bool SetHost(const WString& inputQuery); 59 | 60 | /// Fill the text body in UTF-8. 61 | /// The text to fill. 62 | void SetBodyUtf8(const WString& bodyString); 63 | }; 64 | 65 | /// A type representing an http response. 66 | class HttpResponse 67 | { 68 | typedef collections::Array BodyBuffer; 69 | public: 70 | /// Status code, like 200. 71 | vint statusCode = 0; 72 | /// Response body. This is a byte array. 73 | BodyBuffer body; 74 | /// Returned cookie from the server. 75 | WString cookie; 76 | 77 | HttpResponse() = default; 78 | 79 | /// Get the text body, encoding is assumed to be UTF-8. 80 | /// The response body as text. 81 | WString GetBodyUtf8(); 82 | }; 83 | 84 | /// Send an http request and receive a response. 85 | /// Returns true if this operation succeeded, even when the server returns 404. 86 | /// The request to send. 87 | /// Returns the response. 88 | /// 89 | ///

90 | /// This function will block the calling thread until the respons is returned. 91 | ///

92 | ///

93 | /// This function is only available in Windows. 94 | ///

95 | ///
96 | /// 107 | extern bool HttpQuery(const HttpRequest& request, HttpResponse& response); 108 | 109 | /// Encode a text as part of the url. This function can be used to create arguments in an URL. 110 | /// The encoded text. 111 | /// The text to encode. 112 | /// 113 | ///

114 | /// When a character is not a digit or a letter, 115 | /// it is first encoded to UTF-8, 116 | /// then each byte is written as "%" with two hex digits. 117 | ///

118 | ///

119 | /// This function is only available in Windows. 120 | ///

121 | ///
122 | extern WString UrlEncodeQuery(const WString& query); 123 | } 124 | 125 | #endif 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /Source/Locale.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "Locale.h" 7 | 8 | namespace vl 9 | { 10 | 11 | /*********************************************************************** 12 | Locale 13 | ***********************************************************************/ 14 | 15 | Locale::Locale(const WString& _localeName) 16 | :localeName(_localeName) 17 | { 18 | } 19 | 20 | const WString& Locale::GetName()const 21 | { 22 | return localeName; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/Stream/BroadcastStream.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include 7 | #include "BroadcastStream.h" 8 | 9 | namespace vl 10 | { 11 | namespace stream 12 | { 13 | /*********************************************************************** 14 | BroadcastStream 15 | ***********************************************************************/ 16 | 17 | BroadcastStream::BroadcastStream() 18 | :closed(false) 19 | ,position(0) 20 | { 21 | } 22 | 23 | BroadcastStream::~BroadcastStream() 24 | { 25 | } 26 | 27 | BroadcastStream::StreamList& BroadcastStream::Targets() 28 | { 29 | return streams; 30 | } 31 | 32 | bool BroadcastStream::CanRead()const 33 | { 34 | return false; 35 | } 36 | 37 | bool BroadcastStream::CanWrite()const 38 | { 39 | return !closed; 40 | } 41 | 42 | bool BroadcastStream::CanSeek()const 43 | { 44 | return false; 45 | } 46 | 47 | bool BroadcastStream::CanPeek()const 48 | { 49 | return false; 50 | } 51 | 52 | bool BroadcastStream::IsLimited()const 53 | { 54 | return false; 55 | } 56 | 57 | bool BroadcastStream::IsAvailable()const 58 | { 59 | return !closed; 60 | } 61 | 62 | void BroadcastStream::Close() 63 | { 64 | closed=true; 65 | position=-1; 66 | } 67 | 68 | pos_t BroadcastStream::Position()const 69 | { 70 | return position; 71 | } 72 | 73 | pos_t BroadcastStream::Size()const 74 | { 75 | return position; 76 | } 77 | 78 | void BroadcastStream::Seek(pos_t _size) 79 | { 80 | CHECK_FAIL(L"BroadcastStream::Seek(pos_t)#Operation not supported."); 81 | } 82 | 83 | void BroadcastStream::SeekFromBegin(pos_t _size) 84 | { 85 | CHECK_FAIL(L"BroadcastStream::SeekFromBegin(pos_t)#Operation not supported."); 86 | } 87 | 88 | void BroadcastStream::SeekFromEnd(pos_t _size) 89 | { 90 | CHECK_FAIL(L"BroadcastStream::SeekFromEnd(pos_t)#Operation not supported."); 91 | } 92 | 93 | vint BroadcastStream::Read(void* _buffer, vint _size) 94 | { 95 | CHECK_FAIL(L"BroadcastStream::Read(void*, vint)#Operation not supported."); 96 | } 97 | 98 | vint BroadcastStream::Write(void* _buffer, vint _size) 99 | { 100 | // TODO: (enumerable) foreach 101 | for(vint i=0;iWrite(_buffer, _size); 104 | CHECK_ERROR(written == _size, L"BroadcastStream::Write(void*, vint)#Failed to copy data to the output stream."); 105 | } 106 | position+=_size; 107 | return _size; 108 | } 109 | 110 | vint BroadcastStream::Peek(void* _buffer, vint _size) 111 | { 112 | CHECK_FAIL(L"BroadcastStream::Peek(void*, vint)#Operation not supported."); 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /Source/Stream/BroadcastStream.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_BROADCASTSTREAM 7 | #define VCZH_STREAM_BROADCASTSTREAM 8 | 9 | #include "Interfaces.h" 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | /// A writable stream that copy the written content to multiple output streams. 16 | /// 17 | /// When writing happens, the boreadcast stream will only performance one write attempt to each output stream. 18 | /// 19 | class BroadcastStream : public Object, public virtual IStream 20 | { 21 | typedef collections::List StreamList; 22 | protected: 23 | bool closed; 24 | pos_t position; 25 | StreamList streams; 26 | public: 27 | /// Create a boradcast stream. 28 | BroadcastStream(); 29 | ~BroadcastStream(); 30 | 31 | /// 32 | /// Get the list of output streams. 33 | /// You can change this list to subscribe or unsubscribe. 34 | /// 35 | /// The list of output streams. 36 | StreamList& Targets(); 37 | bool CanRead()const; 38 | bool CanWrite()const; 39 | bool CanSeek()const; 40 | bool CanPeek()const; 41 | bool IsLimited()const; 42 | bool IsAvailable()const; 43 | void Close(); 44 | pos_t Position()const; 45 | pos_t Size()const; 46 | void Seek(pos_t _size); 47 | void SeekFromBegin(pos_t _size); 48 | void SeekFromEnd(pos_t _size); 49 | vint Read(void* _buffer, vint _size); 50 | vint Write(void* _buffer, vint _size); 51 | vint Peek(void* _buffer, vint _size); 52 | }; 53 | } 54 | } 55 | 56 | #endif -------------------------------------------------------------------------------- /Source/Stream/CacheStream.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include 7 | #include "CacheStream.h" 8 | 9 | namespace vl 10 | { 11 | namespace stream 12 | { 13 | /*********************************************************************** 14 | CacheStream 15 | ***********************************************************************/ 16 | 17 | void CacheStream::Flush() 18 | { 19 | if(dirtyLength>0) 20 | { 21 | if(target->Position()!=start+dirtyStart) 22 | { 23 | target->SeekFromBegin(start+dirtyStart); 24 | } 25 | target->Write(buffer+dirtyStart, dirtyLength); 26 | } 27 | dirtyStart=0; 28 | dirtyLength=0; 29 | availableLength=0; 30 | } 31 | 32 | void CacheStream::Load(pos_t _position) 33 | { 34 | if(target->Position()!=_position) 35 | { 36 | target->SeekFromBegin(_position); 37 | } 38 | start=_position; 39 | if(target->CanRead()) 40 | { 41 | availableLength=target->Read(buffer, block); 42 | } 43 | } 44 | 45 | vint CacheStream::InternalRead(void* _buffer, vint _size) 46 | { 47 | vint readed=0; 48 | if(position>=start && positionreaded) 58 | { 59 | Flush(); 60 | if(_size-readed>=block) 61 | { 62 | if(CanSeek()) 63 | { 64 | target->SeekFromBegin(position+readed); 65 | } 66 | vint additional=target->Read(_buffer, _size-readed); 67 | if(additional!=-1) 68 | { 69 | readed+=additional; 70 | } 71 | } 72 | else 73 | { 74 | Load(position+readed); 75 | vint remain=_size-readed; 76 | vint min=availableLength=start && position0) 109 | { 110 | availableLength+=availableOffset; 111 | } 112 | } 113 | 114 | if(_size>written) 115 | { 116 | Flush(); 117 | if(_size-written>=block) 118 | { 119 | if(CanSeek()) 120 | { 121 | target->SeekFromBegin(position+written); 122 | } 123 | vint additional=target->Write(_buffer, _size-written); 124 | if(additional!=-1) 125 | { 126 | written+=additional; 127 | } 128 | } 129 | else 130 | { 131 | Load(position+written); 132 | dirtyLength=_size-written; 133 | memcpy(buffer, _buffer, dirtyLength); 134 | written+=dirtyLength; 135 | } 136 | } 137 | return written; 138 | } 139 | 140 | CacheStream::CacheStream(IStream& _target, vint _block) 141 | :target(&_target) 142 | ,block(_block) 143 | ,start(0) 144 | ,position(0) 145 | ,dirtyStart(0) 146 | ,dirtyLength(0) 147 | ,availableLength(0) 148 | ,operatedSize(0) 149 | { 150 | if(block<=0) 151 | { 152 | block=65536; 153 | } 154 | buffer=new char[block]; 155 | } 156 | 157 | CacheStream::~CacheStream() 158 | { 159 | Close(); 160 | } 161 | 162 | bool CacheStream::CanRead()const 163 | { 164 | return target!=0 && target->CanRead(); 165 | } 166 | 167 | bool CacheStream::CanWrite()const 168 | { 169 | return target!=0 && target->CanWrite(); 170 | } 171 | 172 | bool CacheStream::CanSeek()const 173 | { 174 | return target!=0 && target->CanSeek(); 175 | } 176 | 177 | bool CacheStream::CanPeek()const 178 | { 179 | return target!=0 && target->CanPeek(); 180 | } 181 | 182 | bool CacheStream::IsLimited()const 183 | { 184 | return target!=0 && target->IsLimited(); 185 | } 186 | 187 | bool CacheStream::IsAvailable()const 188 | { 189 | return target!=0 && target->IsAvailable(); 190 | } 191 | 192 | void CacheStream::Close() 193 | { 194 | Flush(); 195 | target=0; 196 | delete[] buffer; 197 | buffer=0; 198 | position=-1; 199 | dirtyStart=0; 200 | dirtyLength=0; 201 | availableLength=0; 202 | operatedSize=-1; 203 | } 204 | 205 | pos_t CacheStream::Position()const 206 | { 207 | return position; 208 | } 209 | 210 | pos_t CacheStream::Size()const 211 | { 212 | if(target!=0) 213 | { 214 | if(IsLimited()) 215 | { 216 | return target->Size(); 217 | } 218 | else 219 | { 220 | return operatedSize; 221 | } 222 | } 223 | else 224 | { 225 | return -1; 226 | } 227 | } 228 | 229 | void CacheStream::Seek(pos_t _size) 230 | { 231 | SeekFromBegin(position+_size); 232 | } 233 | 234 | void CacheStream::SeekFromBegin(pos_t _size) 235 | { 236 | if(CanSeek()) 237 | { 238 | if(_size<0) 239 | { 240 | position=0; 241 | } 242 | else if(_size>Size()) 243 | { 244 | position=Size(); 245 | } 246 | else 247 | { 248 | position=_size; 249 | } 250 | } 251 | } 252 | 253 | void CacheStream::SeekFromEnd(pos_t _size) 254 | { 255 | SeekFromBegin(Size()-_size); 256 | } 257 | 258 | vint CacheStream::Read(void* _buffer, vint _size) 259 | { 260 | CHECK_ERROR(CanRead(), L"CacheStream::Read(void*, vint)#Stream is closed or operation not supported."); 261 | CHECK_ERROR(_size>=0, L"CacheStream::Read(void*, vint)#Argument size cannot be negative."); 262 | 263 | _size=InternalRead(_buffer, _size); 264 | position+=_size; 265 | if(operatedSize=0, L"CacheStream::Read(void*, vint)#Argument size cannot be negative."); 276 | 277 | if(IsLimited()) 278 | { 279 | pos_t size=Size(); 280 | if(size!=-1) 281 | { 282 | vint remain=(vint)(size-(position+_size)); 283 | if(remain<0) 284 | { 285 | _size-=remain; 286 | } 287 | } 288 | } 289 | 290 | _size=InternalWrite(_buffer, _size); 291 | position+=_size; 292 | if(operatedSize=0, L"CacheStream::Read(void*, vint)#Argument size cannot be negative."); 303 | 304 | return InternalRead(_buffer, _size); 305 | } 306 | } 307 | } -------------------------------------------------------------------------------- /Source/Stream/CacheStream.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_CACHESTREAM 7 | #define VCZH_STREAM_CACHESTREAM 8 | 9 | #include "Interfaces.h" 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | /// 16 | ///

17 | /// A potentially readable, peekable, writable, seekable and finite stream that creates on another stream. 18 | /// Each feature is available if the target stream has the same feature. 19 | ///

20 | ///

21 | /// When you read from the cache strema, 22 | /// it will read a specified size of content from the target stream at once and cache, 23 | /// reducing the number of operations on the target stream. 24 | ///

25 | ///

26 | /// When you write to the cache stream, 27 | /// it will cache all the data to write, 28 | /// and write to the target stream after the cache is full, 29 | /// reducing the number of operations on the target stream. 30 | ///

31 | ///
32 | class CacheStream : public Object, public virtual IStream 33 | { 34 | protected: 35 | IStream* target; 36 | vint block; 37 | pos_t start; 38 | pos_t position; 39 | 40 | char* buffer; 41 | vint dirtyStart; 42 | vint dirtyLength; 43 | vint availableLength; 44 | pos_t operatedSize; 45 | 46 | void Flush(); 47 | void Load(pos_t _position); 48 | vint InternalRead(void* _buffer, vint _size); 49 | vint InternalWrite(void* _buffer, vint _size); 50 | public: 51 | /// Create a cache stream from a target stream. 52 | /// The target stream. 53 | /// Size of the cache. 54 | CacheStream(IStream& _target, vint _block=65536); 55 | ~CacheStream(); 56 | 57 | bool CanRead()const; 58 | bool CanWrite()const; 59 | bool CanSeek()const; 60 | bool CanPeek()const; 61 | bool IsLimited()const; 62 | bool IsAvailable()const; 63 | void Close(); 64 | pos_t Position()const; 65 | pos_t Size()const; 66 | void Seek(pos_t _size); 67 | void SeekFromBegin(pos_t _size); 68 | void SeekFromEnd(pos_t _size); 69 | vint Read(void* _buffer, vint _size); 70 | vint Write(void* _buffer, vint _size); 71 | vint Peek(void* _buffer, vint _size); 72 | }; 73 | } 74 | } 75 | 76 | #endif -------------------------------------------------------------------------------- /Source/Stream/EncodingStream.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "EncodingStream.h" 7 | 8 | namespace vl 9 | { 10 | namespace stream 11 | { 12 | /*********************************************************************** 13 | EncoderStream 14 | ***********************************************************************/ 15 | 16 | EncoderStream::EncoderStream(IStream& _stream, IEncoder& _encoder) 17 | :stream(&_stream) 18 | ,encoder(&_encoder) 19 | ,position(0) 20 | { 21 | encoder->Setup(stream); 22 | } 23 | 24 | EncoderStream::~EncoderStream() 25 | { 26 | Close(); 27 | } 28 | 29 | bool EncoderStream::CanRead()const 30 | { 31 | return false; 32 | } 33 | 34 | bool EncoderStream::CanWrite()const 35 | { 36 | return IsAvailable(); 37 | } 38 | 39 | bool EncoderStream::CanSeek()const 40 | { 41 | return false; 42 | } 43 | 44 | bool EncoderStream::CanPeek()const 45 | { 46 | return false; 47 | } 48 | 49 | bool EncoderStream::IsLimited()const 50 | { 51 | return stream!=0 && stream->IsLimited(); 52 | } 53 | 54 | bool EncoderStream::IsAvailable()const 55 | { 56 | return stream!=0 && stream->IsAvailable(); 57 | } 58 | 59 | void EncoderStream::Close() 60 | { 61 | encoder->Close(); 62 | stream=0; 63 | } 64 | 65 | pos_t EncoderStream::Position()const 66 | { 67 | return IsAvailable()?position:-1; 68 | } 69 | 70 | pos_t EncoderStream::Size()const 71 | { 72 | return -1; 73 | } 74 | 75 | void EncoderStream::Seek(pos_t _size) 76 | { 77 | CHECK_FAIL(L"EncoderStream::Seek(pos_t)#Operation not supported."); 78 | } 79 | 80 | void EncoderStream::SeekFromBegin(pos_t _size) 81 | { 82 | CHECK_FAIL(L"EncoderStream::SeekFromBegin(pos_t)#Operation not supported."); 83 | } 84 | 85 | void EncoderStream::SeekFromEnd(pos_t _size) 86 | { 87 | CHECK_FAIL(L"EncoderStream::SeekFromEnd(pos_t)#Operation not supported."); 88 | } 89 | 90 | vint EncoderStream::Read(void* _buffer, vint _size) 91 | { 92 | CHECK_FAIL(L"EncoderStream::Read(void*, vint)#Operation not supported."); 93 | } 94 | 95 | vint EncoderStream::Write(void* _buffer, vint _size) 96 | { 97 | vint result=encoder->Write(_buffer, _size); 98 | if(result>=0) 99 | { 100 | position+=result; 101 | } 102 | return result; 103 | } 104 | 105 | vint EncoderStream::Peek(void* _buffer, vint _size) 106 | { 107 | CHECK_FAIL(L"EncoderStream::Peek(void*, vint)#Operation not supported."); 108 | } 109 | 110 | /*********************************************************************** 111 | DecoderStream 112 | ***********************************************************************/ 113 | 114 | DecoderStream::DecoderStream(IStream& _stream, IDecoder& _decoder) 115 | :stream(&_stream) 116 | ,decoder(&_decoder) 117 | ,position(0) 118 | { 119 | decoder->Setup(stream); 120 | } 121 | 122 | DecoderStream::~DecoderStream() 123 | { 124 | Close(); 125 | } 126 | 127 | bool DecoderStream::CanRead()const 128 | { 129 | return IsAvailable(); 130 | } 131 | 132 | bool DecoderStream::CanWrite()const 133 | { 134 | return false; 135 | } 136 | 137 | bool DecoderStream::CanSeek()const 138 | { 139 | return false; 140 | } 141 | 142 | bool DecoderStream::CanPeek()const 143 | { 144 | return false; 145 | } 146 | 147 | bool DecoderStream::IsLimited()const 148 | { 149 | return stream!=0 && stream->IsLimited(); 150 | } 151 | 152 | bool DecoderStream::IsAvailable()const 153 | { 154 | return stream!=0 && stream->IsAvailable(); 155 | } 156 | 157 | void DecoderStream::Close() 158 | { 159 | decoder->Close(); 160 | stream=0; 161 | } 162 | 163 | pos_t DecoderStream::Position()const 164 | { 165 | return IsAvailable()?position:-1; 166 | } 167 | 168 | pos_t DecoderStream::Size()const 169 | { 170 | return -1; 171 | } 172 | 173 | void DecoderStream::Seek(pos_t _size) 174 | { 175 | CHECK_FAIL(L"DecoderStream::Seek(pos_t)#Operation not supported."); 176 | } 177 | 178 | void DecoderStream::SeekFromBegin(pos_t _size) 179 | { 180 | CHECK_FAIL(L"DecoderStream::SeekFromBegin(pos_t)#Operation not supported."); 181 | } 182 | 183 | void DecoderStream::SeekFromEnd(pos_t _size) 184 | { 185 | CHECK_FAIL(L"DecoderStream::SeekFromEnd(pos_t)#Operation not supported."); 186 | } 187 | 188 | vint DecoderStream::Read(void* _buffer, vint _size) 189 | { 190 | vint result=decoder->Read(_buffer, _size); 191 | if(result>=0) 192 | { 193 | position+=result; 194 | } 195 | return result; 196 | } 197 | 198 | vint DecoderStream::Write(void* _buffer, vint _size) 199 | { 200 | CHECK_FAIL(L"DecoderStream::Write(void*, vint)#Operation not supported."); 201 | } 202 | 203 | vint DecoderStream::Peek(void* _buffer, vint _size) 204 | { 205 | CHECK_FAIL(L"DecoderStream::Peek(void*, vint)#Operation not supported."); 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /Source/Stream/EncodingStream.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_ENCODINGSTREAM 7 | #define VCZH_STREAM_ENCODINGSTREAM 8 | 9 | #include "../Encoding/Encoding.h" 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | /*********************************************************************** 16 | Encoding Related 17 | ***********************************************************************/ 18 | 19 | /// Encoder stream, a writable and potentially finite stream using [T:vl.stream.IEncoder] to transform content. 20 | class EncoderStream : public virtual IStream 21 | { 22 | protected: 23 | IStream* stream; 24 | IEncoder* encoder; 25 | pos_t position; 26 | 27 | public: 28 | /// Create en encoder stream. 29 | /// The output stream to write. 30 | /// The encoder to transform content. 31 | EncoderStream(IStream& _stream, IEncoder& _encoder); 32 | ~EncoderStream(); 33 | 34 | bool CanRead()const; 35 | bool CanWrite()const; 36 | bool CanSeek()const; 37 | bool CanPeek()const; 38 | bool IsLimited()const; 39 | bool IsAvailable()const; 40 | void Close(); 41 | pos_t Position()const; 42 | pos_t Size()const; 43 | void Seek(pos_t _size); 44 | void SeekFromBegin(pos_t _size); 45 | void SeekFromEnd(pos_t _size); 46 | vint Read(void* _buffer, vint _size); 47 | vint Write(void* _buffer, vint _size); 48 | vint Peek(void* _buffer, vint _size); 49 | }; 50 | 51 | /// Decoder stream, a readable and potentially finite stream using [T:vl.stream.IDecoder] to transform content. 52 | class DecoderStream : public virtual IStream 53 | { 54 | protected: 55 | IStream* stream; 56 | IDecoder* decoder; 57 | pos_t position; 58 | 59 | public: 60 | /// Create a decoder stream. 61 | /// The input stream to read. 62 | /// The decoder to transform content. 63 | DecoderStream(IStream& _stream, IDecoder& _decoder); 64 | ~DecoderStream(); 65 | 66 | bool CanRead()const; 67 | bool CanWrite()const; 68 | bool CanSeek()const; 69 | bool CanPeek()const; 70 | bool IsLimited()const; 71 | bool IsAvailable()const; 72 | void Close(); 73 | pos_t Position()const; 74 | pos_t Size()const; 75 | void Seek(pos_t _size); 76 | void SeekFromBegin(pos_t _size); 77 | void SeekFromEnd(pos_t _size); 78 | vint Read(void* _buffer, vint _size); 79 | vint Write(void* _buffer, vint _size); 80 | vint Peek(void* _buffer, vint _size); 81 | }; 82 | } 83 | } 84 | 85 | #endif -------------------------------------------------------------------------------- /Source/Stream/FileStream.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "FileStream.h" 7 | #if defined VCZH_GCC 8 | #include 9 | #endif 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | 16 | #if defined VCZH_GCC 17 | void _fseeki64(FILE* file, pos_t offset, int origin) 18 | { 19 | fseek(file, (long)offset, origin); 20 | } 21 | #endif 22 | 23 | /*********************************************************************** 24 | FileStream 25 | ***********************************************************************/ 26 | 27 | FileStream::FileStream(const WString& fileName, AccessRight _accessRight) 28 | :accessRight(_accessRight) 29 | { 30 | const wchar_t* mode=L"rb"; 31 | switch(accessRight) 32 | { 33 | case ReadOnly: 34 | mode=L"rb"; 35 | break; 36 | case WriteOnly: 37 | mode=L"wb"; 38 | break; 39 | case ReadWrite: 40 | mode=L"w+b"; 41 | break; 42 | } 43 | 44 | #if defined VCZH_MSVC 45 | if(_wfopen_s(&file, fileName.Buffer(), mode)!=0) 46 | { 47 | file=0; 48 | } 49 | #elif defined VCZH_GCC 50 | AString fileNameA = wtoa(fileName); 51 | AString modeA = wtoa(mode); 52 | file = fopen(fileNameA.Buffer(), modeA.Buffer()); 53 | #endif 54 | } 55 | 56 | FileStream::~FileStream() 57 | { 58 | Close(); 59 | } 60 | 61 | bool FileStream::CanRead()const 62 | { 63 | return file!=0 && (accessRight==ReadOnly || accessRight==ReadWrite); 64 | } 65 | 66 | bool FileStream::CanWrite()const 67 | { 68 | return file!=0 && (accessRight==WriteOnly || accessRight==ReadWrite); 69 | } 70 | 71 | bool FileStream::CanSeek()const 72 | { 73 | return file!=0; 74 | } 75 | 76 | bool FileStream::CanPeek()const 77 | { 78 | return file!=0 && (accessRight==ReadOnly || accessRight==ReadWrite); 79 | } 80 | 81 | bool FileStream::IsLimited()const 82 | { 83 | return file!=0 && accessRight==ReadOnly; 84 | } 85 | 86 | bool FileStream::IsAvailable()const 87 | { 88 | return file!=0; 89 | } 90 | 91 | void FileStream::Close() 92 | { 93 | if(file!=0) 94 | { 95 | fclose(file); 96 | file=0; 97 | } 98 | } 99 | 100 | pos_t FileStream::Position()const 101 | { 102 | if(file!=0) 103 | { 104 | #if defined VCZH_MSVC 105 | fpos_t position=0; 106 | if(fgetpos(file, &position)==0) 107 | { 108 | return position; 109 | } 110 | #elif defined VCZH_GCC 111 | return (pos_t)ftell(file); 112 | #endif 113 | } 114 | return -1; 115 | } 116 | 117 | pos_t FileStream::Size()const 118 | { 119 | if(file!=0) 120 | { 121 | #if defined VCZH_MSVC 122 | fpos_t position=0; 123 | if(fgetpos(file, &position)==0) 124 | { 125 | if(fseek(file, 0, SEEK_END)==0) 126 | { 127 | pos_t size=Position(); 128 | if(fsetpos(file, &position)==0) 129 | { 130 | return size; 131 | } 132 | } 133 | } 134 | #elif defined VCZH_GCC 135 | long position = ftell(file); 136 | fseek(file, 0, SEEK_END); 137 | long size=ftell(file); 138 | fseek(file, position, SEEK_SET); 139 | return (pos_t)size; 140 | #endif 141 | } 142 | return -1; 143 | } 144 | 145 | void FileStream::Seek(pos_t _size) 146 | { 147 | if(Position()+_size>Size()) 148 | { 149 | _fseeki64(file, 0, SEEK_END); 150 | } 151 | else if(Position()+_size<0) 152 | { 153 | _fseeki64(file, 0, SEEK_SET); 154 | } 155 | else 156 | { 157 | _fseeki64(file, _size, SEEK_CUR); 158 | } 159 | } 160 | 161 | void FileStream::SeekFromBegin(pos_t _size) 162 | { 163 | if(_size>Size()) 164 | { 165 | _fseeki64(file, 0, SEEK_END); 166 | } 167 | else if(_size<0) 168 | { 169 | _fseeki64(file, 0, SEEK_SET); 170 | } 171 | else 172 | { 173 | _fseeki64(file, _size, SEEK_SET); 174 | } 175 | } 176 | 177 | void FileStream::SeekFromEnd(pos_t _size) 178 | { 179 | if(_size<0) 180 | { 181 | _fseeki64(file, 0, SEEK_END); 182 | } 183 | else if(_size>Size()) 184 | { 185 | _fseeki64(file, 0, SEEK_SET); 186 | } 187 | else 188 | { 189 | _fseeki64(file, -_size, SEEK_END); 190 | } 191 | } 192 | 193 | vint FileStream::Read(void* _buffer, vint _size) 194 | { 195 | CHECK_ERROR(file!=0, L"FileStream::Read(pos_t)#Stream is closed, cannot perform this operation."); 196 | CHECK_ERROR(_size>=0, L"FileStream::Read(void*, vint)#Argument size cannot be negative."); 197 | return fread(_buffer, 1, _size, file); 198 | } 199 | 200 | vint FileStream::Write(void* _buffer, vint _size) 201 | { 202 | CHECK_ERROR(file!=0, L"FileStream::Write(pos_t)#Stream is closed, cannot perform this operation."); 203 | CHECK_ERROR(_size>=0, L"FileStream::Write(void*, vint)#Argument size cannot be negative."); 204 | return fwrite(_buffer, 1, _size, file); 205 | } 206 | 207 | vint FileStream::Peek(void* _buffer, vint _size) 208 | { 209 | CHECK_ERROR(file!=0, L"FileStream::Peek(pos_t)#Stream is closed, cannot perform this operation."); 210 | CHECK_ERROR(_size>=0, L"FileStream::Peek(void*, vint)#Argument size cannot be negative."); 211 | #if defined VCZH_MSVC 212 | fpos_t position=0; 213 | if(fgetpos(file, &position)==0) 214 | { 215 | size_t count=fread(_buffer, 1, _size, file); 216 | if(fsetpos(file, &position)==0) 217 | { 218 | return count; 219 | } 220 | } 221 | return -1; 222 | #elif defined VCZH_GCC 223 | long position=ftell(file); 224 | size_t count=fread(_buffer, 1, _size, file); 225 | fseek(file, position, SEEK_SET); 226 | return count; 227 | #endif 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /Source/Stream/FileStream.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_FILESTREAM 7 | #define VCZH_STREAM_FILESTREAM 8 | 9 | #include 10 | #include "Interfaces.h" 11 | 12 | namespace vl 13 | { 14 | namespace stream 15 | { 16 | /// A file stream. If the given file name is not working, the stream could be unavailable. 17 | class FileStream : public Object, public virtual IStream 18 | { 19 | public: 20 | /// Access to the file. 21 | enum AccessRight 22 | { 23 | /// The file is opened to read, making this stream readable, seekable and finite. 24 | ReadOnly, 25 | /// The file is opened to write, making this stream writable. 26 | WriteOnly, 27 | /// The file is opened to both read and write, making this stream readable, seekable and writable. 28 | ReadWrite 29 | }; 30 | protected: 31 | AccessRight accessRight; 32 | FILE* file; 33 | public: 34 | /// Create a file stream from a given file name. 35 | /// The file to operate. 36 | /// Expected operations on the file. 37 | FileStream(const WString& fileName, AccessRight _accessRight); 38 | ~FileStream(); 39 | 40 | bool CanRead()const; 41 | bool CanWrite()const; 42 | bool CanSeek()const; 43 | bool CanPeek()const; 44 | bool IsLimited()const; 45 | bool IsAvailable()const; 46 | void Close(); 47 | pos_t Position()const; 48 | pos_t Size()const; 49 | void Seek(pos_t _size); 50 | void SeekFromBegin(pos_t _size); 51 | void SeekFromEnd(pos_t _size); 52 | vint Read(void* _buffer, vint _size); 53 | vint Write(void* _buffer, vint _size); 54 | vint Peek(void* _buffer, vint _size); 55 | }; 56 | } 57 | } 58 | 59 | #endif -------------------------------------------------------------------------------- /Source/Stream/Interfaces.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_INTERFACES 7 | #define VCZH_STREAM_INTERFACES 8 | 9 | #include 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | /// 16 | ///

17 | /// Interface for streams. 18 | ///

19 | ///

20 | /// Please notice that, even if you get a stream object, if [M:vl.stream.IStream.IsAvailable] returns false, all other methods cannot be used. 21 | ///

22 | ///

23 | /// Not all methods are available for all types of streams. 24 | /// Feature testing functions must be called before calling other methods, if it is not sure that what kind of stream is being operated against: 25 | ///

26 | ///

27 | ///

    28 | ///
  • Readable: A stream is readable if [M:vl.stream.IStream.CanRead] returns true.
  • 29 | ///
  • Peekable: A stream is peekable if [M:vl.stream.IStream.CanPeek] returns true.
  • 30 | ///
  • Writable: A stream is writable if [M:vl.stream.IStream.CanWrite] returns true.
  • 31 | ///
  • Seekable: A stream is readable if [M:vl.stream.IStream.CanSeek] returns true.
  • 32 | ///
  • Finite: A stream is finite if [M:vl.stream.IStream.IsLimited] returns true.
  • 33 | ///
34 | ///

35 | ///
36 | class IStream : public virtual Interface 37 | { 38 | public: 39 | /// Test if the stream is readable. 40 | /// Returns true if the stream is readable. 41 | virtual bool CanRead()const=0; 42 | /// Test if the stream is writable. 43 | /// Returns true if the stream is writable. 44 | virtual bool CanWrite()const=0; 45 | /// Test if the stream is seekable. 46 | /// Returns true if the stream is seekable. 47 | virtual bool CanSeek()const=0; 48 | /// Test if the stream is peekable. 49 | /// Returns true if the stream is peekable. 50 | virtual bool CanPeek()const=0; 51 | /// Test if the content of the stream is finite. A writable stream can also be limited, it means that you can only write limited content to the stream. 52 | /// Returns true if the content of the stream is finite. 53 | virtual bool IsLimited()const=0; 54 | /// Test if the stream is available. For example, if you create a readable [T:vl.stream.FileStream] giving a wrong file name, it will be unavailable. 55 | /// Returns true if the stream is available. 56 | virtual bool IsAvailable()const=0; 57 | /// Close the stream, making the stream unavailable. 58 | virtual void Close()=0; 59 | /// Get the current position in the stream. 60 | /// The position in the stream. Returns -1 if the stream is unavailable. 61 | virtual pos_t Position()const=0; 62 | /// Get the size of the content in this stream. 63 | /// The size of the content in this stream. Returns -1 if the size is unsizable or unavailable. 64 | virtual pos_t Size()const=0; 65 | /// Step forward or backward from the current position. It will crash if the stream is unseekable or unavailable. 66 | /// The length to step forward if it is a positive number. The length to step backward if it is a negative number 67 | virtual void Seek(pos_t _size)=0; 68 | /// Step forward from the beginning. It will crash if the stream is unseekable or unavailable. 69 | /// The length to step forward. 70 | virtual void SeekFromBegin(pos_t _size)=0; 71 | /// Step backward from the end. It will crash if the stream is unseekable or unavailable. 72 | /// The length to step backward. 73 | virtual void SeekFromEnd(pos_t _size)=0; 74 | /// Read from the current position and step forward. It will crash if the stream is unreadable or unavailable. 75 | /// Returns the actual size of the content that has read. Returns 0 if a stream has no more data to read. 76 | /// A buffer to store the content. 77 | /// The size of the content that is expected to read. 78 | virtual vint Read(void* _buffer, vint _size)=0; 79 | /// Write to the current position and step forward. It will crash if the stream is unwritable or unavailable. 80 | /// Returns the actual size of the content that has written. Returns 0 if a stream has not enough space to write. 81 | /// A buffer storing the content to write. 82 | /// The size of the content that is expected to write. 83 | virtual vint Write(void* _buffer, vint _size)=0; 84 | /// Read from the current position without stepping forward. It will crash if the stream is unpeekable or unavailable. 85 | /// Returns the actual size of the content that is read. Returns 0 if a stream has no more data to read. 86 | /// A buffer to store the content. 87 | /// The size of the content that is expected to read. 88 | virtual vint Peek(void* _buffer, vint _size)=0; 89 | }; 90 | } 91 | } 92 | 93 | #endif -------------------------------------------------------------------------------- /Source/Stream/MemoryStream.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include 7 | #include "MemoryStream.h" 8 | 9 | namespace vl 10 | { 11 | namespace stream 12 | { 13 | /*********************************************************************** 14 | MemoryStream 15 | ***********************************************************************/ 16 | 17 | void MemoryStream::PrepareSpace(vint totalSpace) 18 | { 19 | if(totalSpace>capacity) 20 | { 21 | totalSpace=(totalSpace/block+1)*block; 22 | char* newBuffer=new char[totalSpace]; 23 | if(buffer) 24 | { 25 | memcpy(newBuffer, buffer, size); 26 | delete[] buffer; 27 | } 28 | buffer=newBuffer; 29 | capacity=totalSpace; 30 | } 31 | } 32 | 33 | MemoryStream::MemoryStream(vint _block) 34 | :block(_block) 35 | ,buffer(0) 36 | ,size(0) 37 | ,position(0) 38 | ,capacity(0) 39 | { 40 | if(block<=0) 41 | { 42 | block=65536; 43 | } 44 | } 45 | 46 | MemoryStream::~MemoryStream() 47 | { 48 | Close(); 49 | } 50 | 51 | bool MemoryStream::CanRead()const 52 | { 53 | return block!=0; 54 | } 55 | 56 | bool MemoryStream::CanWrite()const 57 | { 58 | return block!=0; 59 | } 60 | 61 | bool MemoryStream::CanSeek()const 62 | { 63 | return block!=0; 64 | } 65 | 66 | bool MemoryStream::CanPeek()const 67 | { 68 | return block!=0; 69 | } 70 | 71 | bool MemoryStream::IsLimited()const 72 | { 73 | return false; 74 | } 75 | 76 | bool MemoryStream::IsAvailable()const 77 | { 78 | return block!=0; 79 | } 80 | 81 | void MemoryStream::Close() 82 | { 83 | if(buffer) 84 | { 85 | delete[] buffer; 86 | } 87 | block=0; 88 | buffer=0; 89 | size=-1; 90 | position=-1; 91 | capacity=0; 92 | } 93 | 94 | pos_t MemoryStream::Position()const 95 | { 96 | return position; 97 | } 98 | 99 | pos_t MemoryStream::Size()const 100 | { 101 | return size; 102 | } 103 | 104 | void MemoryStream::Seek(pos_t _size) 105 | { 106 | SeekFromBegin(position+_size); 107 | } 108 | 109 | void MemoryStream::SeekFromBegin(pos_t _size) 110 | { 111 | CHECK_ERROR(block!=0, L"MemoryStream::SeekFromBegin(pos_t)#Stream is closed, cannot perform this operation."); 112 | vint expected=(vint)_size; 113 | if(expected<0) 114 | { 115 | position=0; 116 | } 117 | else if(expected>=size) 118 | { 119 | position=size; 120 | } 121 | else 122 | { 123 | position=expected; 124 | } 125 | } 126 | 127 | void MemoryStream::SeekFromEnd(pos_t _size) 128 | { 129 | SeekFromBegin(size-_size); 130 | } 131 | 132 | vint MemoryStream::Read(void* _buffer, vint _size) 133 | { 134 | CHECK_ERROR(block!=0, L"MemoryStream::Read(pos_t)#Stream is closed, cannot perform this operation."); 135 | CHECK_ERROR(_size>=0, L"MemoryStream::Read(void*, vint)#Argument size cannot be negative."); 136 | vint max=size-position; 137 | if(_size>max) 138 | { 139 | _size=max; 140 | } 141 | memmove(_buffer, buffer+position, _size); 142 | position+=_size; 143 | return _size; 144 | } 145 | 146 | vint MemoryStream::Write(void* _buffer, vint _size) 147 | { 148 | CHECK_ERROR(block!=0, L"MemoryStream::Write(pos_t)#Stream is closed, cannot perform this operation."); 149 | CHECK_ERROR(_size>=0, L"MemoryStream::Write(void*, vint)#Argument size cannot be negative."); 150 | PrepareSpace(size+_size); 151 | memmove(buffer+position, _buffer, _size); 152 | position+=_size; 153 | if(size=0, L"MemoryStream::Peek(void*, vint)#Argument size cannot be negative."); 164 | vint max=size-position; 165 | if(_size>max) 166 | { 167 | _size=max; 168 | } 169 | memmove(_buffer, buffer+position, _size); 170 | return _size; 171 | } 172 | 173 | void* MemoryStream::GetInternalBuffer() 174 | { 175 | return buffer; 176 | } 177 | } 178 | } -------------------------------------------------------------------------------- /Source/Stream/MemoryStream.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_MEMORYSTREAM 7 | #define VCZH_STREAM_MEMORYSTREAM 8 | 9 | #include "Interfaces.h" 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | /// A readable, peekable, writable and seekable stream that creates on a buffer. 16 | class MemoryStream : public Object, public virtual IStream 17 | { 18 | protected: 19 | vint block; 20 | char* buffer; 21 | vint size; 22 | vint position; 23 | vint capacity; 24 | 25 | void PrepareSpace(vint totalSpace); 26 | public: 27 | /// Create a memory stream. 28 | /// 29 | /// Size for each allocation. 30 | /// When the allocated buffer is not big enough for writing, 31 | /// the buffer will be rebuilt with an extension of "_block" in bytes. 32 | /// 33 | MemoryStream(vint _block=65536); 34 | ~MemoryStream(); 35 | 36 | bool CanRead()const; 37 | bool CanWrite()const; 38 | bool CanSeek()const; 39 | bool CanPeek()const; 40 | bool IsLimited()const; 41 | bool IsAvailable()const; 42 | void Close(); 43 | pos_t Position()const; 44 | pos_t Size()const; 45 | void Seek(pos_t _size); 46 | void SeekFromBegin(pos_t _size); 47 | void SeekFromEnd(pos_t _size); 48 | vint Read(void* _buffer, vint _size); 49 | vint Write(void* _buffer, vint _size); 50 | vint Peek(void* _buffer, vint _size); 51 | void* GetInternalBuffer(); 52 | }; 53 | } 54 | } 55 | 56 | #endif -------------------------------------------------------------------------------- /Source/Stream/MemoryWrapperStream.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include 7 | #include "MemoryWrapperStream.h" 8 | 9 | namespace vl 10 | { 11 | namespace stream 12 | { 13 | /*********************************************************************** 14 | MemoryWrapperStream 15 | ***********************************************************************/ 16 | 17 | MemoryWrapperStream::MemoryWrapperStream(void* _buffer, vint _size) 18 | :buffer((char*)_buffer) 19 | ,size(_size) 20 | ,position(0) 21 | { 22 | if(size<=0) 23 | { 24 | buffer=0; 25 | size=0; 26 | } 27 | } 28 | 29 | MemoryWrapperStream::~MemoryWrapperStream() 30 | { 31 | } 32 | 33 | bool MemoryWrapperStream::CanRead()const 34 | { 35 | return buffer!=0; 36 | } 37 | 38 | bool MemoryWrapperStream::CanWrite()const 39 | { 40 | return buffer!=0; 41 | } 42 | 43 | bool MemoryWrapperStream::CanSeek()const 44 | { 45 | return buffer!=0; 46 | } 47 | 48 | bool MemoryWrapperStream::CanPeek()const 49 | { 50 | return buffer!=0; 51 | } 52 | 53 | bool MemoryWrapperStream::IsLimited()const 54 | { 55 | return buffer!=0; 56 | } 57 | 58 | bool MemoryWrapperStream::IsAvailable()const 59 | { 60 | return buffer!=0; 61 | } 62 | 63 | void MemoryWrapperStream::Close() 64 | { 65 | buffer=0; 66 | size=-1; 67 | position=-1; 68 | } 69 | 70 | pos_t MemoryWrapperStream::Position()const 71 | { 72 | return position; 73 | } 74 | 75 | pos_t MemoryWrapperStream::Size()const 76 | { 77 | return size; 78 | } 79 | 80 | void MemoryWrapperStream::Seek(pos_t _size) 81 | { 82 | SeekFromBegin(position+_size); 83 | } 84 | 85 | void MemoryWrapperStream::SeekFromBegin(pos_t _size) 86 | { 87 | CHECK_ERROR(buffer!=0, L"MemoryWrapperStream::SeekFromBegin(pos_t)#Stream is closed, cannot perform this operation."); 88 | vint expected=(vint)_size; 89 | if(expected<0) 90 | { 91 | position=0; 92 | } 93 | else if(expected>=size) 94 | { 95 | position=size; 96 | } 97 | else 98 | { 99 | position=expected; 100 | } 101 | } 102 | 103 | void MemoryWrapperStream::SeekFromEnd(pos_t _size) 104 | { 105 | SeekFromBegin(size-_size); 106 | } 107 | 108 | vint MemoryWrapperStream::Read(void* _buffer, vint _size) 109 | { 110 | CHECK_ERROR(buffer!=0, L"MemoryWrapperStream::Read(pos_t)#Stream is closed, cannot perform this operation."); 111 | CHECK_ERROR(_size>=0, L"MemoryWrapperStream::Read(void*, vint)#Argument size cannot be negative."); 112 | vint max=size-position; 113 | if(_size>max) 114 | { 115 | _size=max; 116 | } 117 | memmove(_buffer, buffer+position, _size); 118 | position+=_size; 119 | return _size; 120 | } 121 | 122 | vint MemoryWrapperStream::Write(void* _buffer, vint _size) 123 | { 124 | CHECK_ERROR(buffer!=0, L"MemoryWrapperStream::Write(pos_t)#Stream is closed, cannot perform this operation."); 125 | CHECK_ERROR(_size>=0, L"MemoryWrapperStream::Write(void*, vint)#Argument size cannot be negative."); 126 | vint max=size-position; 127 | if(_size>max) 128 | { 129 | _size=max; 130 | } 131 | memmove(buffer+position, _buffer, _size); 132 | position+=_size; 133 | return _size; 134 | } 135 | 136 | vint MemoryWrapperStream::Peek(void* _buffer, vint _size) 137 | { 138 | CHECK_ERROR(buffer!=0, L"MemoryWrapperStream::Peek(pos_t)#Stream is closed, cannot perform this operation."); 139 | CHECK_ERROR(_size>=0, L"MemoryWrapperStream::Peek(void*, vint)#Argument size cannot be negative."); 140 | vint max=size-position; 141 | if(_size>max) 142 | { 143 | _size=max; 144 | } 145 | memmove(_buffer, buffer+position, _size); 146 | return _size; 147 | } 148 | } 149 | } -------------------------------------------------------------------------------- /Source/Stream/MemoryWrapperStream.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_MEMORYWRAPPERSTREAM 7 | #define VCZH_STREAM_MEMORYWRAPPERSTREAM 8 | 9 | #include "Interfaces.h" 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | /// A readable, peekable, writable, seekable and finite stream that creates on a buffer. 16 | class MemoryWrapperStream : public Object, public virtual IStream 17 | { 18 | protected: 19 | char* buffer; 20 | vint size; 21 | vint position; 22 | public: 23 | /// Create a memory wrapper stream. 24 | /// The buffer to operate. 25 | /// Size of the buffer in bytes. 26 | MemoryWrapperStream(void* _buffer, vint _size); 27 | ~MemoryWrapperStream(); 28 | 29 | bool CanRead()const; 30 | bool CanWrite()const; 31 | bool CanSeek()const; 32 | bool CanPeek()const; 33 | bool IsLimited()const; 34 | bool IsAvailable()const; 35 | void Close(); 36 | pos_t Position()const; 37 | pos_t Size()const; 38 | void Seek(pos_t _size); 39 | void SeekFromBegin(pos_t _size); 40 | void SeekFromEnd(pos_t _size); 41 | vint Read(void* _buffer, vint _size); 42 | vint Write(void* _buffer, vint _size); 43 | vint Peek(void* _buffer, vint _size); 44 | }; 45 | } 46 | } 47 | 48 | #endif -------------------------------------------------------------------------------- /Source/Stream/RecorderStream.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include 7 | #include "RecorderStream.h" 8 | 9 | namespace vl 10 | { 11 | namespace stream 12 | { 13 | /*********************************************************************** 14 | RecorderStream 15 | ***********************************************************************/ 16 | 17 | RecorderStream::RecorderStream(IStream& _in, IStream& _out) 18 | :in(&_in) 19 | , out(&_out) 20 | { 21 | } 22 | 23 | RecorderStream::~RecorderStream() 24 | { 25 | } 26 | 27 | bool RecorderStream::CanRead()const 28 | { 29 | return IsAvailable() && in->CanRead(); 30 | } 31 | 32 | bool RecorderStream::CanWrite()const 33 | { 34 | return false; 35 | } 36 | 37 | bool RecorderStream::CanSeek()const 38 | { 39 | return false; 40 | } 41 | 42 | bool RecorderStream::CanPeek()const 43 | { 44 | return false; 45 | } 46 | 47 | bool RecorderStream::IsLimited()const 48 | { 49 | return IsAvailable() && in->IsLimited(); 50 | } 51 | 52 | bool RecorderStream::IsAvailable()const 53 | { 54 | return in != 0 && out != 0 && in->IsAvailable() && out->IsAvailable(); 55 | } 56 | 57 | void RecorderStream::Close() 58 | { 59 | in = nullptr; 60 | out = nullptr; 61 | } 62 | 63 | pos_t RecorderStream::Position()const 64 | { 65 | return IsAvailable() ? in->Position() : -1; 66 | } 67 | 68 | pos_t RecorderStream::Size()const 69 | { 70 | return IsAvailable() ? in->Size() : -1; 71 | } 72 | 73 | void RecorderStream::Seek(pos_t _size) 74 | { 75 | CHECK_FAIL(L"RecorderStream::Seek(pos_t)#Operation not supported."); 76 | } 77 | 78 | void RecorderStream::SeekFromBegin(pos_t _size) 79 | { 80 | CHECK_FAIL(L"RecorderStream::SeekFromBegin(pos_t)#Operation not supported."); 81 | } 82 | 83 | void RecorderStream::SeekFromEnd(pos_t _size) 84 | { 85 | CHECK_FAIL(L"RecorderStream::SeekFromEnd(pos_t)#Operation not supported."); 86 | } 87 | 88 | vint RecorderStream::Read(void* _buffer, vint _size) 89 | { 90 | _size = in->Read(_buffer, _size); 91 | vint written = out->Write(_buffer, _size); 92 | CHECK_ERROR(written == _size, L"RecorderStream::Read(void*, vint)#Failed to copy data to the output stream."); 93 | return _size; 94 | } 95 | 96 | vint RecorderStream::Write(void* _buffer, vint _size) 97 | { 98 | CHECK_FAIL(L"RecorderStream::Write(void*, vint)#Operation not supported."); 99 | } 100 | 101 | vint RecorderStream::Peek(void* _buffer, vint _size) 102 | { 103 | CHECK_FAIL(L"RecorderStream::Peek(void*, vint)#Operation not supported."); 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /Source/Stream/RecorderStream.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #ifndef VCZH_STREAM_RECORDERSTREAM 7 | #define VCZH_STREAM_RECORDERSTREAM 8 | 9 | #include "Interfaces.h" 10 | 11 | namespace vl 12 | { 13 | namespace stream 14 | { 15 | /// 16 | /// A readable stream that, reads from one stream, and copy everything that is read to another stream. 17 | /// The stream is unavailable if one of the input stream or the output stream is unavailable. 18 | /// The stream is readable, and potentially finite. 19 | /// 20 | /// 21 | /// When reading happens, the recorder stream will only performance one write attempt to the output stream. 22 | /// 23 | class RecorderStream : public Object, public virtual IStream 24 | { 25 | protected: 26 | IStream* in; 27 | IStream* out; 28 | public: 29 | /// Create a recorder stream. 30 | /// 31 | /// The input stream. 32 | /// This recorder stream is readable only when the input stream is readable 33 | /// This recorder stream is finite only when the input stream is finite 34 | /// 35 | /// 36 | /// The output stream. 37 | /// 38 | RecorderStream(IStream& _in, IStream& _out); 39 | ~RecorderStream(); 40 | 41 | bool CanRead()const; 42 | bool CanWrite()const; 43 | bool CanSeek()const; 44 | bool CanPeek()const; 45 | bool IsLimited()const; 46 | bool IsAvailable()const; 47 | void Close(); 48 | pos_t Position()const; 49 | pos_t Size()const; 50 | void Seek(pos_t _size); 51 | void SeekFromBegin(pos_t _size); 52 | void SeekFromEnd(pos_t _size); 53 | vint Read(void* _buffer, vint _size); 54 | vint Write(void* _buffer, vint _size); 55 | vint Peek(void* _buffer, vint _size); 56 | }; 57 | } 58 | } 59 | 60 | #endif -------------------------------------------------------------------------------- /Source/Threading.cpp: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Author: Zihan Chen (vczh) 3 | Licensed under https://github.com/vczh-libraries/License 4 | ***********************************************************************/ 5 | 6 | #include "Threading.h" 7 | 8 | #if defined VCZH_ARM 9 | #include 10 | #elif defined VCZH_MSVC || defined VCZH_GCC 11 | #include 12 | #endif 13 | 14 | namespace vl 15 | { 16 | 17 | /*********************************************************************** 18 | SpinLock 19 | ***********************************************************************/ 20 | 21 | SpinLock::Scope::Scope(SpinLock& _spinLock) 22 | :spinLock(&_spinLock) 23 | { 24 | spinLock->Enter(); 25 | } 26 | 27 | SpinLock::Scope::~Scope() 28 | { 29 | spinLock->Leave(); 30 | } 31 | 32 | bool SpinLock::TryEnter() 33 | { 34 | return token.exchange(1) == 0; 35 | } 36 | 37 | void SpinLock::Enter() 38 | { 39 | vint expected = 0; 40 | while (!token.compare_exchange_strong(expected, 1)) 41 | { 42 | while (token != 0) 43 | { 44 | #ifdef VCZH_ARM 45 | __yield(); 46 | #else 47 | _mm_pause(); 48 | #endif 49 | } 50 | } 51 | } 52 | 53 | void SpinLock::Leave() 54 | { 55 | token.exchange(0); 56 | } 57 | 58 | /*********************************************************************** 59 | ThreadLocalStorage 60 | ***********************************************************************/ 61 | 62 | void ThreadLocalStorage::Clear() 63 | { 64 | CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Clear()#Cannot access a disposed ThreadLocalStorage."); 65 | if(destructor) 66 | { 67 | if (auto data = Get()) 68 | { 69 | destructor(data); 70 | } 71 | } 72 | Set(nullptr); 73 | } 74 | 75 | void ThreadLocalStorage::Dispose() 76 | { 77 | CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Dispose()#Cannot access a disposed ThreadLocalStorage."); 78 | Clear(); 79 | disposed = true; 80 | } 81 | 82 | struct TlsStorageLink 83 | { 84 | ThreadLocalStorage* storage = nullptr; 85 | TlsStorageLink* next = nullptr; 86 | }; 87 | 88 | volatile bool tlsFixed = false; 89 | TlsStorageLink* tlsHead = nullptr; 90 | TlsStorageLink** tlsTail = &tlsHead; 91 | 92 | void ThreadLocalStorage::PushStorage(ThreadLocalStorage* storage) 93 | { 94 | CHECK_ERROR(!tlsFixed, L"vl::ThreadLocalStorage::PushStorage(ThreadLocalStorage*)#Cannot create new ThreadLocalStorage instance after calling ThreadLocalStorage::FixStorages()."); 95 | auto link = new TlsStorageLink; 96 | link->storage = storage; 97 | *tlsTail = link; 98 | tlsTail = &link->next; 99 | } 100 | 101 | void ThreadLocalStorage::FixStorages() 102 | { 103 | tlsFixed = true; 104 | } 105 | 106 | void ThreadLocalStorage::ClearStorages() 107 | { 108 | FixStorages(); 109 | auto current = tlsHead; 110 | while (current) 111 | { 112 | current->storage->Clear(); 113 | current = current->next; 114 | } 115 | } 116 | 117 | void ThreadLocalStorage::DisposeStorages() 118 | { 119 | FixStorages(); 120 | auto current = tlsHead; 121 | tlsHead = nullptr; 122 | tlsTail = nullptr; 123 | while (current) 124 | { 125 | current->storage->Dispose(); 126 | 127 | auto temp = current; 128 | current = current->next; 129 | delete temp; 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | ## 2.0 4 | 5 | - Extensible `vl::filesystem`. 6 | - Virtual or filter files/folders (even on root, for wasm or unit test). 7 | - `vl::filesystem::OSFileSystem` static class to access file system as `Filepath`, `File` and `Folder`. 8 | - `vl::filesystem::InjectFileSystemImpl(vl::filesystem::IFileSystemImpl*)`, `nullptr` to cancel, a default implementation using `vl::filesystem::OSFileSystem` will take place. 9 | - Need to affect `FileStream`. 10 | 11 | ## Optional 12 | -------------------------------------------------------------------------------- /Test/Linux/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Import/Vlpp.h" 2 | 3 | using namespace vl; 4 | 5 | WString GetTestResourcePath() 6 | { 7 | return L"../Resources/"; 8 | } 9 | 10 | WString GetTestOutputPath() 11 | { 12 | return L"../Output/"; 13 | } 14 | 15 | int main(int argc, char* argv[]) 16 | { 17 | vint result = unittest::UnitTest::RunAndDisposeTests(argc, argv); 18 | FinalizeGlobalStorage(); 19 | return result; 20 | } 21 | -------------------------------------------------------------------------------- /Test/Linux/vmake: -------------------------------------------------------------------------------- 1 | <# 2 | CPP_TARGET=./Bin/UnitTest 3 | CPP_VCXPROJ=../UnitTest/UnitTest/UnitTest.vcxproj 4 | CPP_REMOVES=( 5 | "../../Import/Vlpp.Windows.cpp" 6 | "../../Source/FileSystem.Windows.cpp" 7 | "../../Source/HttpUtility.Windows.cpp" 8 | "../../Source/Locale.Windows.cpp" 9 | "../../Source/Threading.Windows.cpp" 10 | "../../Source/Encoding/CharFormat/CharFormat.Windows.cpp" 11 | "../Source/TestLocale.cpp" 12 | "../UnitTest/UnitTest/Main.cpp" 13 | ) 14 | CPP_ADDS=("Main.cpp") 15 | FOLDERS=("../Output") 16 | TARGETS=("${CPP_TARGET}") 17 | CPP_COMPILE_OPTIONS="-I ../../Import" 18 | #> 19 | <#@ include "${VCPROOT}/vl/vmake-cpp" #> 20 | -------------------------------------------------------------------------------- /Test/Source/TestLocale.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Source/Locale.h" 2 | #include "../../Source/Stream/Accessor.h" 3 | #include "../../Source/Stream/EncodingStream.h" 4 | #include "../../Source/Encoding/CharFormat/CharFormat.h" 5 | #include "../../Source/Stream/FileStream.h" 6 | 7 | using namespace vl; 8 | using namespace vl::collections; 9 | using namespace vl::stream; 10 | 11 | extern WString GetTestOutputPath(); 12 | 13 | TEST_FILE 14 | { 15 | TEST_CASE(L"Locale comparisong") 16 | { 17 | { 18 | Locale a, b; 19 | TEST_ASSERT((a == b) == true); 20 | TEST_ASSERT((a != b) == false); 21 | TEST_ASSERT((a < b) == false); 22 | TEST_ASSERT((a <= b) == true); 23 | TEST_ASSERT((a > b) == false); 24 | TEST_ASSERT((a >= b) == true); 25 | } 26 | { 27 | Locale a(L"a"), b(L"b"); 28 | TEST_ASSERT((a == b) == false); 29 | TEST_ASSERT((a != b) == true); 30 | TEST_ASSERT((a < b) == true); 31 | TEST_ASSERT((a <= b) == true); 32 | TEST_ASSERT((a > b) == false); 33 | TEST_ASSERT((a >= b) == false); 34 | } 35 | }); 36 | 37 | TEST_CASE(L"Print locale awared data") 38 | { 39 | DateTime dt = DateTime::LocalTime(); 40 | 41 | FileStream fileStream(GetTestOutputPath() + L"Locale.txt", FileStream::WriteOnly); 42 | BomEncoder encoder(BomEncoder::Utf16); 43 | EncoderStream encoderStream(fileStream, encoder); 44 | StreamWriter writer(encoderStream); 45 | 46 | writer.WriteLine(L"Invariant locale: " + Locale::Invariant().GetName()); 47 | writer.WriteLine(L"User default locale: " + Locale::UserDefault().GetName()); 48 | writer.WriteLine(L"System default locale: " + Locale::SystemDefault().GetName()); 49 | 50 | writer.WriteLine(L"========================================"); 51 | { 52 | Locale locale = Locale::UserDefault(); 53 | WString input = L"abcdeABCDEabcdeABCDE战斗戰鬥あいうえおアイウエオアイウエオ"; 54 | writer.WriteLine(L"[Normal] => " + input); 55 | writer.WriteLine(L"[ToFullWidth] => " + locale.ToFullWidth(input)); 56 | writer.WriteLine(L"[ToHalfWidth] => " + locale.ToHalfWidth(input)); 57 | writer.WriteLine(L"[ToHiragana] => " + locale.ToHiragana(input)); 58 | writer.WriteLine(L"[ToKatagana] => " + locale.ToKatagana(input)); 59 | writer.WriteLine(L"[ToLower] => " + locale.ToLower(input)); 60 | writer.WriteLine(L"[ToUpper] => " + locale.ToUpper(input)); 61 | writer.WriteLine(L"[ToLinguisticLower] => " + locale.ToLinguisticLower(input)); 62 | writer.WriteLine(L"[ToLinguisticUpper] => " + locale.ToLinguisticUpper(input)); 63 | writer.WriteLine(L"[ToSimplifiedChinese] => " + locale.ToSimplifiedChinese(input)); 64 | writer.WriteLine(L"[ToTraditionalChinese] => " + locale.ToTraditionalChinese(input)); 65 | writer.WriteLine(L"[ToTileCase] => " + locale.ToTileCase(input)); 66 | } 67 | 68 | List locales; 69 | Locale::Enumerate(locales); 70 | locales.Insert(0, Locale::Invariant()); 71 | for (auto locale : locales) 72 | { 73 | writer.WriteLine(L"========================================"); 74 | writer.WriteLine(L"Locale: " + locale.GetName()); 75 | writer.WriteLine(L"[Number 0] => " + locale.FormatNumber(L"0")); 76 | writer.WriteLine(L"[Number 1] => " + locale.FormatNumber(L"1")); 77 | writer.WriteLine(L"[Number -1] => " + locale.FormatNumber(L"-1")); 78 | writer.WriteLine(L"[Number 100.2] => " + locale.FormatNumber(L"100.2")); 79 | writer.WriteLine(L"[Number -100.2] => " + locale.FormatNumber(L"-100.2")); 80 | writer.WriteLine(L"[Currency 0] => " + locale.FormatCurrency(L"0")); 81 | writer.WriteLine(L"[Currency 1] => " + locale.FormatCurrency(L"1")); 82 | writer.WriteLine(L"[Currency -1] => " + locale.FormatCurrency(L"-1")); 83 | writer.WriteLine(L"[Currency 100.2] => " + locale.FormatCurrency(L"100.2")); 84 | writer.WriteLine(L"[Currency -100.2] => " + locale.FormatCurrency(L"-100.2")); 85 | { 86 | writer.WriteString(L"[ShortDayOfWeek]"); 87 | for (vint i = 0; i <= 6; i++) 88 | { 89 | writer.WriteString(L" " + locale.GetShortDayOfWeekName(i)); 90 | } 91 | writer.WriteLine(L""); 92 | 93 | writer.WriteString(L"[LongDayOfWeek]"); 94 | for (vint i = 0; i <= 6; i++) 95 | { 96 | writer.WriteString(L" " + locale.GetLongDayOfWeekName(i)); 97 | } 98 | writer.WriteLine(L""); 99 | 100 | writer.WriteString(L"[ShortMonth]"); 101 | for (vint i = 1; i <= 12; i++) 102 | { 103 | writer.WriteString(L" " + locale.GetShortMonthName(i)); 104 | } 105 | writer.WriteLine(L""); 106 | 107 | writer.WriteString(L"[LongMonth]"); 108 | for (vint i = 1; i <= 12; i++) 109 | { 110 | writer.WriteString(L" " + locale.GetLongMonthName(i)); 111 | } 112 | writer.WriteLine(L""); 113 | } 114 | { 115 | List formats; 116 | locale.GetLongDateFormats(formats); 117 | for (auto format : formats) 118 | { 119 | writer.WriteLine(L"[LongDate]" + format + L" => " + locale.FormatDate(format, dt)); 120 | } 121 | } 122 | { 123 | List formats; 124 | locale.GetShortDateFormats(formats); 125 | for (auto format : formats) 126 | { 127 | writer.WriteLine(L"[ShortDate]" + format + L" => " + locale.FormatDate(format, dt)); 128 | } 129 | } 130 | { 131 | List formats; 132 | locale.GetYearMonthDateFormats(formats); 133 | for (auto format : formats) 134 | { 135 | writer.WriteLine(L"[YearMonth]" + format + L" => " + locale.FormatDate(format, dt)); 136 | } 137 | } 138 | { 139 | List formats; 140 | locale.GetLongTimeFormats(formats); 141 | for (auto format : formats) 142 | { 143 | writer.WriteLine(L"[LongTime]" + format + L" => " + locale.FormatTime(format, dt)); 144 | } 145 | } 146 | { 147 | List formats; 148 | locale.GetShortTimeFormats(formats); 149 | for (auto format : formats) 150 | { 151 | writer.WriteLine(L"[ShortTime]" + format + L" => " + locale.FormatTime(format, dt)); 152 | } 153 | } 154 | } 155 | }); 156 | } -------------------------------------------------------------------------------- /Test/Source/TestLocaleString.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Source/Locale.h" 2 | #include "../../Source/Stream/Accessor.h" 3 | #include "../../Source/Encoding/CharFormat/CharFormat.h" 4 | #include "../../Source/Stream/FileStream.h" 5 | 6 | using namespace vl; 7 | using namespace vl::collections; 8 | using namespace vl::stream; 9 | 10 | extern WString GetTestOutputPath(); 11 | 12 | TEST_FILE 13 | { 14 | TEST_CASE(L"Test locale awared date/time format") 15 | { 16 | DateTime dt = DateTime::FromDateTime(2000, 1, 2, 13, 2, 3); 17 | auto df = L"yyyy,MM,MMM,MMMM,dd,ddd,dddd"; 18 | auto ds = L"2000,01,Jan,January,02,Sun,Sunday"; 19 | auto tf = L"hh,HH,mm,ss,tt"; 20 | auto ts = L"01,13,02,03,PM"; 21 | TEST_ASSERT(INVLOC.FormatDate(df, dt) == ds); 22 | TEST_ASSERT(INVLOC.FormatTime(tf, dt) == ts); 23 | }); 24 | 25 | TEST_CASE(L"Test locale awared string comparison") 26 | { 27 | TEST_ASSERT(INVLOC.Compare(L"abc", L"abc", Locale::Normalization::None) == 0); 28 | TEST_ASSERT(INVLOC.Compare(L"abc", L"ABC", Locale::Normalization::None) != 0); 29 | TEST_ASSERT(INVLOC.Compare(L"abc", L"abc", Locale::Normalization::IgnoreCase) == 0); 30 | TEST_ASSERT(INVLOC.Compare(L"abc", L"ABC", Locale::Normalization::IgnoreCase) == 0); 31 | 32 | TEST_ASSERT(INVLOC.CompareOrdinal(L"abc", L"abc") == 0); 33 | TEST_ASSERT(INVLOC.CompareOrdinal(L"abc", L"ABC") != 0); 34 | TEST_ASSERT(INVLOC.CompareOrdinalIgnoreCase(L"abc", L"abc") == 0); 35 | TEST_ASSERT(INVLOC.CompareOrdinalIgnoreCase(L"abc", L"ABC") == 0); 36 | }); 37 | 38 | TEST_CASE(L"Test locale awared string searching") 39 | { 40 | TEST_ASSERT(INVLOC.FindFirst(L"abc", L"vczh", Locale::Normalization::None).key == -1); 41 | TEST_ASSERT(INVLOC.FindFirst(L"abcvczhdefvczhghi", L"vczh", Locale::Normalization::None).key == 3); 42 | TEST_ASSERT(INVLOC.FindFirst(L"abc", L"Vczh", Locale::Normalization::None).key == -1); 43 | TEST_ASSERT(INVLOC.FindFirst(L"abcvczhdefvczhghi", L"Vczh", Locale::Normalization::None).key == -1); 44 | TEST_ASSERT(INVLOC.FindFirst(L"abc", L"vczh", Locale::Normalization::IgnoreCase).key == -1); 45 | TEST_ASSERT(INVLOC.FindFirst(L"abcvczhdefvczhghi", L"vczh", Locale::Normalization::IgnoreCase).key == 3); 46 | TEST_ASSERT(INVLOC.FindFirst(L"abc", L"Vczh", Locale::Normalization::IgnoreCase).key == -1); 47 | TEST_ASSERT(INVLOC.FindFirst(L"abcvczhdefvczhghi", L"Vczh", Locale::Normalization::IgnoreCase).key == 3); 48 | 49 | TEST_ASSERT(INVLOC.FindLast(L"abc", L"vczh", Locale::Normalization::None).key == -1); 50 | TEST_ASSERT(INVLOC.FindLast(L"abcvczhdefvczhghi", L"vczh", Locale::Normalization::None).key == 10); 51 | TEST_ASSERT(INVLOC.FindLast(L"abc", L"Vczh", Locale::Normalization::None).key == -1); 52 | TEST_ASSERT(INVLOC.FindLast(L"abcvczhdefvczhghi", L"Vczh", Locale::Normalization::None).key == -1); 53 | TEST_ASSERT(INVLOC.FindLast(L"abc", L"vczh", Locale::Normalization::IgnoreCase).key == -1); 54 | TEST_ASSERT(INVLOC.FindLast(L"abcvczhdefvczhghi", L"vczh", Locale::Normalization::IgnoreCase).key == 10); 55 | TEST_ASSERT(INVLOC.FindLast(L"abc", L"Vczh", Locale::Normalization::IgnoreCase).key == -1); 56 | TEST_ASSERT(INVLOC.FindLast(L"abcvczhdefvczhghi", L"Vczh", Locale::Normalization::IgnoreCase).key == 10); 57 | 58 | TEST_ASSERT(INVLOC.StartsWith(L"abc", L"a", Locale::Normalization::None) == true); 59 | TEST_ASSERT(INVLOC.StartsWith(L"abc", L"A", Locale::Normalization::None) == false); 60 | TEST_ASSERT(INVLOC.StartsWith(L"abc", L"c", Locale::Normalization::None) == false); 61 | TEST_ASSERT(INVLOC.StartsWith(L"abc", L"C", Locale::Normalization::None) == false); 62 | TEST_ASSERT(INVLOC.StartsWith(L"abc", L"a", Locale::Normalization::IgnoreCase) == true); 63 | TEST_ASSERT(INVLOC.StartsWith(L"abc", L"A", Locale::Normalization::IgnoreCase) == true); 64 | TEST_ASSERT(INVLOC.StartsWith(L"abc", L"c", Locale::Normalization::IgnoreCase) == false); 65 | TEST_ASSERT(INVLOC.StartsWith(L"abc", L"C", Locale::Normalization::IgnoreCase) == false); 66 | 67 | TEST_ASSERT(INVLOC.EndsWith(L"abc", L"a", Locale::Normalization::None) == false); 68 | TEST_ASSERT(INVLOC.EndsWith(L"abc", L"A", Locale::Normalization::None) == false); 69 | TEST_ASSERT(INVLOC.EndsWith(L"abc", L"c", Locale::Normalization::None) == true); 70 | TEST_ASSERT(INVLOC.EndsWith(L"abc", L"C", Locale::Normalization::None) == false); 71 | TEST_ASSERT(INVLOC.EndsWith(L"abc", L"a", Locale::Normalization::IgnoreCase) == false); 72 | TEST_ASSERT(INVLOC.EndsWith(L"abc", L"A", Locale::Normalization::IgnoreCase) == false); 73 | TEST_ASSERT(INVLOC.EndsWith(L"abc", L"c", Locale::Normalization::IgnoreCase) == true); 74 | TEST_ASSERT(INVLOC.EndsWith(L"abc", L"C", Locale::Normalization::IgnoreCase) == true); 75 | }); 76 | 77 | TEST_CASE(L"Test locale awared string casing") 78 | { 79 | TEST_ASSERT(INVLOC.ToUpper(L"abcABC") == L"ABCABC"); 80 | TEST_ASSERT(INVLOC.ToLower(L"abcABC") == L"abcabc"); 81 | TEST_ASSERT(INVLOC.ToLinguisticUpper(L"abcABC") == L"ABCABC"); 82 | TEST_ASSERT(INVLOC.ToLinguisticLower(L"abcABC") == L"abcabc"); 83 | }); 84 | } -------------------------------------------------------------------------------- /Test/Source/TestSerialization.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Source/Stream/Serialization.h" 2 | #include "../../Source/Stream/MemoryStream.h" 3 | 4 | using namespace vl; 5 | using namespace vl::stream; 6 | using namespace vl::collections; 7 | 8 | namespace TestSerialization_TestObjects 9 | { 10 | enum Seasons1 11 | { 12 | Spring, 13 | Summer, 14 | Autumn, 15 | Winter, 16 | }; 17 | 18 | enum class Seasons2 19 | { 20 | Spring, 21 | Summer, 22 | Autumn, 23 | Winter, 24 | }; 25 | 26 | struct Strings 27 | { 28 | WString sa; 29 | U8String sb; 30 | U16String sc; 31 | U32String sd; 32 | }; 33 | } 34 | using namespace TestSerialization_TestObjects; 35 | 36 | namespace vl 37 | { 38 | namespace stream 39 | { 40 | namespace internal 41 | { 42 | BEGIN_SERIALIZATION(Strings) 43 | SERIALIZE(sa) 44 | SERIALIZE(sb) 45 | SERIALIZE(sc) 46 | SERIALIZE(sd) 47 | END_SERIALIZATION 48 | } 49 | } 50 | } 51 | 52 | TEST_FILE 53 | { 54 | TEST_CASE(L"Serialize PODs") 55 | { 56 | MemoryStream memoryStream; 57 | vint8_t a1 = 2, a2; 58 | vuint8_t b1 = 3, b2; 59 | vint16_t c1 = 5, c2; 60 | vuint16_t d1 = 7, d2; 61 | vint32_t e1 = 11, e2; 62 | vuint32_t f1 = 13, f2; 63 | vint64_t g1 = 17, g2; 64 | vuint64_t h1 = 19, h2; 65 | float i1 = 23, i2; 66 | double j1 = 27, j2; 67 | char cha1 = 'A', cha2; 68 | wchar_t chb1 = L'B', chb2; 69 | char8_t chc1 = u8'C', chc2; 70 | char16_t chd1 = u'D', chd2; 71 | char32_t che1 = U'E', che2; 72 | bool ba1 = true, ba2; 73 | bool bb1 = false, bb2; 74 | { 75 | internal::ContextFreeWriter writer(memoryStream); 76 | writer << a1 << b1 << c1 << d1 << e1 << f1 << g1 << h1 << i1 << j1; 77 | writer << cha1 << chb1 << chc1 << chd1 << che1; 78 | writer << ba1 << bb1; 79 | } 80 | memoryStream.SeekFromBegin(0); 81 | { 82 | internal::ContextFreeReader reader(memoryStream); 83 | reader << a2 << b2 << c2 << d2 << e2 << f2 << g2 << h2 << i2 << j2; 84 | reader << cha2 << chb2 << chc2 << chd2 << che2; 85 | reader << ba2 << bb2; 86 | TEST_ASSERT(memoryStream.Position() == memoryStream.Size()); 87 | } 88 | TEST_ASSERT(a1 == a2); 89 | TEST_ASSERT(b1 == b2); 90 | TEST_ASSERT(c1 == c2); 91 | TEST_ASSERT(d1 == d2); 92 | TEST_ASSERT(e1 == e2); 93 | TEST_ASSERT(f1 == f2); 94 | TEST_ASSERT(g1 == g2); 95 | TEST_ASSERT(h1 == h2); 96 | TEST_ASSERT(i1 == i2); 97 | 98 | TEST_ASSERT(cha1 == cha2); 99 | TEST_ASSERT(chb1 == chb2); 100 | TEST_ASSERT(chc1 == chc2); 101 | TEST_ASSERT(chd1 == chd2); 102 | TEST_ASSERT(che1 == che2); 103 | 104 | TEST_ASSERT(ba1 == ba2); 105 | TEST_ASSERT(bb1 == bb2); 106 | }); 107 | 108 | TEST_CASE(L"Serialize Strings") 109 | { 110 | MemoryStream memoryStream; 111 | WString sa1 = L"𩰪㦲𦰗𠀼 𣂕𣴑𣱳𦁚 Vczh is genius!@我是天才", sa2; 112 | U8String sb1 = u8"𩰪㦲𦰗𠀼 𣂕𣴑𣱳𦁚 Vczh is genius!@我是天才", sb2; 113 | U16String sc1 = u"𩰪㦲𦰗𠀼 𣂕𣴑𣱳𦁚 Vczh is genius!@我是天才", sc2; 114 | U32String sd1 = U"𩰪㦲𦰗𠀼 𣂕𣴑𣱳𦁚 Vczh is genius!@我是天才", sd2; 115 | { 116 | internal::ContextFreeWriter writer(memoryStream); 117 | writer << sa1 << sb1 << sc1 << sd1; 118 | } 119 | memoryStream.SeekFromBegin(0); 120 | { 121 | internal::ContextFreeReader reader(memoryStream); 122 | reader << sa2 << sb2 << sc2 << sd2; 123 | TEST_ASSERT(memoryStream.Position() == memoryStream.Size()); 124 | } 125 | TEST_ASSERT(sa1 == sa2); 126 | TEST_ASSERT(sb1 == sb2); 127 | TEST_ASSERT(sc1 == sc2); 128 | TEST_ASSERT(sd1 == sd2); 129 | }); 130 | 131 | TEST_CASE(L"Serialize Enums (1)") 132 | { 133 | MemoryStream memoryStream; 134 | 135 | Seasons1 ea1 = Spring, ea2; 136 | Seasons1 eb1 = Summer, eb2; 137 | Seasons1 ec1 = Autumn, ec2; 138 | Seasons1 ed1 = Winter, ed2; 139 | 140 | Seasons2 eca1 = Seasons2::Spring, eca2; 141 | Seasons2 ecb1 = Seasons2::Summer, ecb2; 142 | Seasons2 ecc1 = Seasons2::Autumn, ecc2; 143 | Seasons2 ecd1 = Seasons2::Winter, ecd2; 144 | 145 | Strings e1, e2; 146 | e1.sa = L"𩰪㦲𦰗𠀼 𣂕𣴑𣱳𦁚 Vczh is genius!@我是天才"; 147 | e1.sb = u8"𩰪㦲𦰗𠀼 𣂕𣴑𣱳𦁚 Vczh is genius!@我是天才"; 148 | e1.sc = u"𩰪㦲𦰗𠀼 𣂕𣴑𣱳𦁚 Vczh is genius!@我是天才"; 149 | e1.sd = U"𩰪㦲𦰗𠀼 𣂕𣴑𣱳𦁚 Vczh is genius!@我是天才"; 150 | 151 | { 152 | internal::ContextFreeWriter writer(memoryStream); 153 | writer << ea1 << eb1 << ec1 << ed1 << eca1 << ecb1 << ecc1 << ecd1 << e1; 154 | } 155 | memoryStream.SeekFromBegin(0); 156 | { 157 | internal::ContextFreeReader reader(memoryStream); 158 | reader << ea2 << eb2 << ec2 << ed2 << eca2 << ecb2 << ecc2 << ecd2 << e2; 159 | TEST_ASSERT(memoryStream.Position() == memoryStream.Size()); 160 | } 161 | 162 | TEST_ASSERT(ea1 == ea2); 163 | TEST_ASSERT(eb1 == eb2); 164 | TEST_ASSERT(ec1 == ec2); 165 | TEST_ASSERT(ed1 == ed2); 166 | 167 | TEST_ASSERT(eca1 == eca2); 168 | TEST_ASSERT(ecb1 == ecb2); 169 | TEST_ASSERT(ecc1 == ecc2); 170 | TEST_ASSERT(ecd1 == ecd2); 171 | 172 | TEST_ASSERT(e1.sa == e2.sa); 173 | TEST_ASSERT(e1.sb == e2.sb); 174 | TEST_ASSERT(e1.sc == e2.sc); 175 | TEST_ASSERT(e1.sd == e2.sd); 176 | }); 177 | 178 | TEST_CASE(L"Serialize Generic Types") 179 | { 180 | MemoryStream memoryStream; 181 | auto a1 = Ptr(new Strings); 182 | Ptr a2; 183 | a1->sa = L"𩰪㦲𦰗𠀼 𣂕𣴑𣱳𦁚 Vczh is genius!@我是天才"; 184 | a1->sb = u8"𩰪㦲𦰗𠀼 𣂕𣴑𣱳𦁚 Vczh is genius!@我是天才"; 185 | a1->sc = u"𩰪㦲𦰗𠀼 𣂕𣴑𣱳𦁚 Vczh is genius!@我是天才"; 186 | a1->sd = U"𩰪㦲𦰗𠀼 𣂕𣴑𣱳𦁚 Vczh is genius!@我是天才"; 187 | Nullable b1 = *a1.Obj(), b2; 188 | { 189 | internal::ContextFreeWriter writer(memoryStream); 190 | writer << a1 << b1; 191 | } 192 | memoryStream.SeekFromBegin(0); 193 | { 194 | internal::ContextFreeReader reader(memoryStream); 195 | reader << a2 << b2; 196 | TEST_ASSERT(memoryStream.Position() == memoryStream.Size()); 197 | } 198 | TEST_ASSERT(a2); 199 | TEST_ASSERT(b2); 200 | TEST_ASSERT(a1->sa == a2->sa); 201 | TEST_ASSERT(a1->sb == a2->sb); 202 | TEST_ASSERT(a1->sc == a2->sc); 203 | TEST_ASSERT(a1->sd == a2->sd); 204 | TEST_ASSERT(b1.Value().sa == b2.Value().sa); 205 | TEST_ASSERT(b1.Value().sb == b2.Value().sb); 206 | TEST_ASSERT(b1.Value().sc == b2.Value().sc); 207 | TEST_ASSERT(b1.Value().sd == b2.Value().sd); 208 | }); 209 | 210 | TEST_CASE(L"Serialize Collections") 211 | { 212 | MemoryStream memoryStream; 213 | List a1, a2; 214 | Array b1(4), b2; 215 | Dictionary c1, c2; 216 | Group d1, d2; 217 | 218 | a1.Add(Seasons2::Spring); 219 | a1.Add(Seasons2::Summer); 220 | a1.Add(Seasons2::Autumn); 221 | a1.Add(Seasons2::Winter); 222 | 223 | b1[0] = Seasons2::Spring; 224 | b1[1] = Seasons2::Summer; 225 | b1[2] = Seasons2::Autumn; 226 | b1[3] = Seasons2::Winter; 227 | 228 | c1.Add(1, Seasons2::Spring); 229 | c1.Add(2, Seasons2::Summer); 230 | c1.Add(3, Seasons2::Autumn); 231 | c1.Add(4, Seasons2::Winter); 232 | 233 | d1.Add(1, Seasons2::Spring); 234 | d1.Add(1, Seasons2::Summer); 235 | d1.Add(2, Seasons2::Autumn); 236 | d1.Add(2, Seasons2::Winter); 237 | d1.Add(3, Seasons2::Spring); 238 | d1.Add(3, Seasons2::Summer); 239 | d1.Add(4, Seasons2::Autumn); 240 | d1.Add(4, Seasons2::Winter); 241 | 242 | { 243 | internal::ContextFreeWriter writer(memoryStream); 244 | writer << a1 << b1 << c1 << d1; 245 | } 246 | memoryStream.SeekFromBegin(0); 247 | { 248 | internal::ContextFreeReader reader(memoryStream); 249 | reader << a2 << b2 << c2 << d2; 250 | TEST_ASSERT(memoryStream.Position() == memoryStream.Size()); 251 | } 252 | TEST_ASSERT(CompareEnumerable(a1, a2) == 0); 253 | TEST_ASSERT(CompareEnumerable(b1, b2) == 0); 254 | TEST_ASSERT(CompareEnumerable(c1, c2) == 0); 255 | TEST_ASSERT(CompareEnumerable(d1, d2) == 0); 256 | }); 257 | 258 | TEST_CASE(L"Serialize Others") 259 | { 260 | MemoryStream memoryStream; 261 | wchar_t a1[] = L"𩰪㦲𦰗𠀼 𣂕𣴑𣱳𦁚 Vczh is genius!@我是天才"; 262 | wchar_t a2[sizeof(a1) / sizeof(*a1)]; 263 | { 264 | internal::ContextFreeWriter writer(memoryStream); 265 | MemoryWrapperStream s(a1, sizeof(a1)); 266 | writer << (IStream&)s; 267 | } 268 | memoryStream.SeekFromBegin(0); 269 | { 270 | internal::ContextFreeReader reader(memoryStream); 271 | MemoryWrapperStream s(a2, sizeof(a2)); 272 | reader << (IStream&)s; 273 | TEST_ASSERT(memoryStream.Position() == memoryStream.Size()); 274 | } 275 | TEST_ASSERT(memcmp(a1, a2, sizeof(a1)) == 0); 276 | }); 277 | } -------------------------------------------------------------------------------- /Test/Source/TestStreamBase64.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Source/Stream/Accessor.h" 2 | #include "../../Source/Stream/EncodingStream.h" 3 | #include "../../Source/Encoding/Base64Encoding.h" 4 | 5 | using namespace vl; 6 | using namespace vl::stream; 7 | using namespace vl::collections; 8 | 9 | namespace TestStreamBase64_TestObjects 10 | { 11 | template 12 | void TestBase64OnBytesPerData(const uint8_t(&bytes)[Bytes], const char8_t(&chars)[Chars]) 13 | { 14 | MemoryStream memoryStream; 15 | { 16 | Utf8Base64Encoder encoder; 17 | EncoderStream encoderStream(memoryStream, encoder); 18 | vint written = encoderStream.Write((void*)bytes, Bytes); 19 | TEST_ASSERT(written == Bytes); 20 | } 21 | memoryStream.SeekFromBegin(0); 22 | { 23 | StreamReader_ reader(memoryStream); 24 | auto base64 = reader.ReadToEnd(); 25 | TEST_ASSERT(base64 == chars); 26 | } 27 | memoryStream.SeekFromBegin(0); 28 | { 29 | Utf8Base64Decoder decoder; 30 | DecoderStream decoderStream(memoryStream, decoder); 31 | uint8_t buffer[Bytes]; 32 | vint read = decoderStream.Read(buffer, Bytes); 33 | TEST_ASSERT(read == Bytes); 34 | TEST_ASSERT(memcmp(buffer, bytes, Bytes) == 0); 35 | TEST_ASSERT(decoderStream.Read(buffer, 1) == 0); 36 | } 37 | } 38 | 39 | template 40 | void TestBase64OnBytesPerByte(const uint8_t(&bytes)[Bytes], const char8_t(&chars)[Chars]) 41 | { 42 | MemoryStream memoryStream; 43 | { 44 | Utf8Base64Encoder encoder; 45 | EncoderStream encoderStream(memoryStream, encoder); 46 | for (vint i = 0; i < Bytes; i++) 47 | { 48 | vint written = encoderStream.Write((void*)(bytes + i), 1); 49 | TEST_ASSERT(written == 1); 50 | } 51 | } 52 | memoryStream.SeekFromBegin(0); 53 | { 54 | StreamReader_ reader(memoryStream); 55 | auto base64 = reader.ReadToEnd(); 56 | TEST_ASSERT(base64 == chars); 57 | } 58 | memoryStream.SeekFromBegin(0); 59 | { 60 | Utf8Base64Decoder decoder; 61 | DecoderStream decoderStream(memoryStream, decoder); 62 | for (vint i = 0; i < Bytes; i++) 63 | { 64 | uint8_t byte = 0; 65 | vint read = decoderStream.Read(&byte, 1); 66 | TEST_ASSERT(read == 1); 67 | TEST_ASSERT(byte == bytes[i]); 68 | } 69 | { 70 | uint8_t byte; 71 | TEST_ASSERT(decoderStream.Read(&byte, 1) == 0); 72 | } 73 | } 74 | } 75 | 76 | template 77 | void TestBase64OnBytes(const uint8_t(&bytes)[Bytes], const char8_t(&chars)[Chars]) 78 | { 79 | TestBase64OnBytesPerData(bytes, chars); 80 | TestBase64OnBytesPerByte(bytes, chars); 81 | } 82 | 83 | template 84 | void TestBase64OnChars(const char8_t(&bytes)[Bytes], const char8_t(&chars)[Chars]) 85 | { 86 | TestBase64OnBytes(reinterpret_cast(bytes), chars); 87 | } 88 | } 89 | using namespace TestStreamBase64_TestObjects; 90 | 91 | TEST_FILE 92 | { 93 | TEST_CASE(L"Wikipedia[light w]") 94 | { 95 | TestBase64OnChars(u8"light w", u8"bGlnaHQgdw=="); 96 | }); 97 | TEST_CASE(L"Wikipedia[light wo]") 98 | { 99 | TestBase64OnChars(u8"light wo", u8"bGlnaHQgd28="); 100 | }); 101 | TEST_CASE(L"Wikipedia[light wor]") 102 | { 103 | TestBase64OnChars(u8"light wor", u8"bGlnaHQgd29y"); 104 | }); 105 | TEST_CASE(L"Wikipedia[light work]") 106 | { 107 | TestBase64OnChars(u8"light work", u8"bGlnaHQgd29yaw=="); 108 | }); 109 | TEST_CASE(L"Wikipedia[light work.]") 110 | { 111 | TestBase64OnChars(u8"light work.", u8"bGlnaHQgd29yay4="); 112 | }); 113 | 114 | TEST_CASE(L"0b01010101") 115 | { 116 | uint8_t bytes[] = { 0b01010101 }; 117 | TestBase64OnBytes(bytes, u8"VQ=="); 118 | }); 119 | 120 | TEST_CASE(L"0b01010101 0b10101010") 121 | { 122 | uint8_t bytes[] = { 0b01010101,0b10101010 }; 123 | TestBase64OnBytes(bytes, u8"Vao="); 124 | }); 125 | 126 | TEST_CASE(L"0b01010101 0b10101010 0b01011010") 127 | { 128 | uint8_t bytes[] = { 0b01010101,0b10101010,0b01011010 }; 129 | TestBase64OnBytes(bytes, u8"Vapa"); 130 | }); 131 | } -------------------------------------------------------------------------------- /Test/Source/TestStreamLzw.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Source/Stream/FileStream.h" 2 | #include "../../Source/Stream/MemoryStream.h" 3 | #include "../../Source/Stream/EncodingStream.h" 4 | #include "../../Source/Encoding/LzwEncoding.h" 5 | 6 | using namespace vl; 7 | using namespace vl::stream; 8 | using namespace vl::collections; 9 | 10 | extern WString GetTestOutputPath(); 11 | 12 | TEST_FILE 13 | { 14 | /*********************************************************************** 15 | Lzw 16 | ***********************************************************************/ 17 | 18 | auto TestLzwEncodingWithEncoderAndDecoder = [](const char* input, LzwEncoder& encoder, LzwDecoder& decoder) 19 | { 20 | MemoryStream stream; 21 | vint size = strlen(input); 22 | { 23 | EncoderStream encoderStream(stream, encoder); 24 | vint size = strlen(input); 25 | TEST_ASSERT(encoderStream.Write((void*)input, size) == size); 26 | } 27 | stream.SeekFromBegin(0); 28 | unittest::UnitTest::PrintMessage(L" [" + atow(input) + L"]", unittest::UnitTest::MessageKind::Info); 29 | unittest::UnitTest::PrintMessage(L" " + itow(size) + L" -> " + i64tow(stream.Size()), unittest::UnitTest::MessageKind::Info); 30 | { 31 | Array output(size + 1); 32 | DecoderStream decoderStream(stream, decoder); 33 | TEST_ASSERT(decoderStream.Read(&output[0], size) == size); 34 | TEST_ASSERT(decoderStream.Read(&output[0], size) == 0); 35 | output[size] = 0; 36 | TEST_ASSERT(strcmp(input, &output[0]) == 0); 37 | } 38 | }; 39 | 40 | auto TestLzwEncodingDefault = [&](const char* input) 41 | { 42 | LzwEncoder encoder; 43 | LzwDecoder decoder; 44 | TestLzwEncodingWithEncoderAndDecoder(input, encoder, decoder); 45 | }; 46 | 47 | auto TestLzwEncodingPrepared = [&](const char* input) 48 | { 49 | bool existingBytes[256] = { 0 }; 50 | const char* current = input; 51 | while (vuint8_t c = (vuint8_t)*current++) 52 | { 53 | existingBytes[c] = true; 54 | } 55 | 56 | LzwEncoder encoder(existingBytes); 57 | LzwDecoder decoder(existingBytes); 58 | TestLzwEncodingWithEncoderAndDecoder(input, encoder, decoder); 59 | }; 60 | 61 | TEST_CASE(L"Test Lzw Encoding") 62 | { 63 | const char* buffer[] = 64 | { 65 | "", 66 | "0000000000000000000000000000000000000000", 67 | "Vczh is genius!Vczh is genius!Vczh is genius!", 68 | }; 69 | 70 | for (vint i = 0; i < sizeof(buffer) / sizeof(*buffer); i++) 71 | { 72 | TestLzwEncodingDefault(buffer[i]); 73 | TestLzwEncodingPrepared(buffer[i]); 74 | } 75 | }); 76 | 77 | #if defined VCZH_MSVC && defined NDEBUG 78 | 79 | auto Copy = [](IStream& dst, IStream& src, Array& buffer, vint totalSize) 80 | { 81 | vint BufferSize = buffer.Count(); 82 | while (true) 83 | { 84 | vint size = src.Read(&buffer[0], BufferSize); 85 | if (size == 0) 86 | { 87 | break; 88 | } 89 | dst.Write(&buffer[0], size); 90 | } 91 | }; 92 | 93 | TEST_CASE(L"Test Lzw Performance") 94 | { 95 | const vint BufferSize = 33554432; 96 | Array buffer(BufferSize); 97 | MemoryStream compressedStream(BufferSize), decompressedStream(BufferSize); 98 | unittest::UnitTest::PrintMessage(L" Reading UnitTest.pdb ...", unittest::UnitTest::MessageKind::Info); 99 | { 100 | FileStream fileStream(GetTestOutputPath() + L"../UnitTest/Release/UnitTest.pdb", FileStream::ReadOnly); 101 | Copy(decompressedStream, fileStream, buffer, (vint)fileStream.Size()); 102 | } 103 | 104 | decompressedStream.SeekFromBegin(0); 105 | vint totalSize = (vint)decompressedStream.Size(); 106 | 107 | unittest::UnitTest::PrintMessage(L" Compressing UnitTest.pdb ...", unittest::UnitTest::MessageKind::Info); 108 | { 109 | DateTime begin = DateTime::LocalTime(); 110 | 111 | LzwEncoder encoder; 112 | EncoderStream encoderStream(compressedStream, encoder); 113 | Copy(encoderStream, decompressedStream, buffer, totalSize); 114 | 115 | DateTime end = DateTime::LocalTime(); 116 | 117 | double time = (end.osMilliseconds - begin.osMilliseconds) / 1000.0; 118 | unittest::UnitTest::PrintMessage(L" Time elasped: " + ftow(time) + L" seconds", unittest::UnitTest::MessageKind::Info); 119 | unittest::UnitTest::PrintMessage(L" Performance: " + ftow(totalSize / time / (1 << 20)) + L" MB/s", unittest::UnitTest::MessageKind::Info); 120 | } 121 | 122 | compressedStream.SeekFromBegin(0); 123 | unittest::UnitTest::PrintMessage(L" " + i64tow(totalSize) + L" -> " + i64tow(compressedStream.Size()), unittest::UnitTest::MessageKind::Info); 124 | 125 | unittest::UnitTest::PrintMessage(L" Decompressing UnitTest.pdb ...", unittest::UnitTest::MessageKind::Info); 126 | { 127 | DateTime begin = DateTime::LocalTime(); 128 | 129 | LzwDecoder decoder; 130 | DecoderStream decoderStream(compressedStream, decoder); 131 | Copy(decompressedStream, decoderStream, buffer, totalSize); 132 | 133 | DateTime end = DateTime::LocalTime(); 134 | double time = (end.osMilliseconds - begin.osMilliseconds) / 1000.0; 135 | unittest::UnitTest::PrintMessage(L" Time elasped: " + ftow(time) + L" seconds", unittest::UnitTest::MessageKind::Info); 136 | unittest::UnitTest::PrintMessage(L" Performance: " + ftow(totalSize / time / (1 << 20)) + L" MB/s", unittest::UnitTest::MessageKind::Info); 137 | } 138 | }); 139 | #endif 140 | } -------------------------------------------------------------------------------- /Test/Source/TestStreamReaderWriter.cpp: -------------------------------------------------------------------------------- 1 | #include "../../Source/Stream/MemoryWrapperStream.h" 2 | #include "../../Source/Stream/Accessor.h" 3 | 4 | using namespace vl; 5 | using namespace vl::stream; 6 | 7 | const vint BUFFER_SIZE = 1024; 8 | 9 | TEST_FILE 10 | { 11 | /*********************************************************************** 12 | StringReader 13 | ***********************************************************************/ 14 | 15 | TEST_CASE(L"Test StringReader") 16 | { 17 | const wchar_t text[] = L"1:Vczh is genius!\r\n2:Vczh is genius!\r\n3:Vczh is genius!\r\n4:Vczh is genius!"; 18 | StringReader reader(text); 19 | 20 | TEST_ASSERT(reader.ReadChar() == L'1'); 21 | TEST_ASSERT(reader.ReadString(5) == L":Vczh"); 22 | TEST_ASSERT(reader.ReadLine() == L" is genius!"); 23 | TEST_ASSERT(reader.ReadLine() == L"2:Vczh is genius!"); 24 | TEST_ASSERT(reader.ReadToEnd() == L"3:Vczh is genius!\r\n4:Vczh is genius!"); 25 | }); 26 | 27 | TEST_CASE(L"Test StringReader with ending CRLF") 28 | { 29 | const wchar_t text[] = L"1:Vczh is genius!\r\n2:Vczh is genius!!\r\n3:Vczh is genius!!!\r\n4:Vczh is genius!!!!\r\n"; 30 | const wchar_t* lines[] = {L"1:Vczh is genius!", L"2:Vczh is genius!!", L"3:Vczh is genius!!!", L"4:Vczh is genius!!!!",L""}; 31 | StringReader reader(text); 32 | vint index = 0; 33 | 34 | while (index < sizeof(lines) / sizeof(*lines)) 35 | { 36 | TEST_ASSERT(reader.IsEnd() == false); 37 | TEST_ASSERT(reader.ReadLine() == lines[index++]); 38 | } 39 | TEST_ASSERT(reader.IsEnd() == true); 40 | }); 41 | 42 | TEST_CASE(L"Test StringReader without ending CRLF") 43 | { 44 | const wchar_t text[] = L"1:Vczh is genius!\r\n2:Vczh is genius!!\r\n3:Vczh is genius!!!\r\n4:Vczh is genius!!!!"; 45 | const wchar_t* lines[] = {L"1:Vczh is genius!", L"2:Vczh is genius!!", L"3:Vczh is genius!!!", L"4:Vczh is genius!!!!"}; 46 | StringReader reader(text); 47 | vint index = 0; 48 | 49 | while (index < sizeof(lines) / sizeof(*lines)) 50 | { 51 | TEST_ASSERT(reader.IsEnd() == false); 52 | TEST_ASSERT(reader.ReadLine() == lines[index++]); 53 | } 54 | TEST_ASSERT(reader.IsEnd() == true); 55 | }); 56 | 57 | /*********************************************************************** 58 | StreamReader 59 | ***********************************************************************/ 60 | 61 | TEST_CASE(L"Test StreamReader") 62 | { 63 | wchar_t text[] = L"1:Vczh is genius!\r\n2:Vczh is genius!\r\n3:Vczh is genius!\r\n4:Vczh is genius!"; 64 | MemoryWrapperStream stream(text, sizeof(text) - sizeof(*text)); 65 | StreamReader reader(stream); 66 | 67 | TEST_ASSERT(reader.ReadChar() == L'1'); 68 | TEST_ASSERT(reader.ReadString(5) == L":Vczh"); 69 | TEST_ASSERT(reader.ReadLine() == L" is genius!"); 70 | TEST_ASSERT(reader.ReadLine() == L"2:Vczh is genius!"); 71 | TEST_ASSERT(reader.ReadToEnd() == L"3:Vczh is genius!\r\n4:Vczh is genius!"); 72 | }); 73 | 74 | TEST_CASE(L"Test StreamReader with ending CRLF") 75 | { 76 | wchar_t text[] = L"1:Vczh is genius!\r\n2:Vczh is genius!!\r\n3:Vczh is genius!!!\r\n4:Vczh is genius!!!!\r\n"; 77 | const wchar_t* lines[] = {L"1:Vczh is genius!", L"2:Vczh is genius!!", L"3:Vczh is genius!!!", L"4:Vczh is genius!!!!",L""}; 78 | MemoryWrapperStream stream(text, sizeof(text) - sizeof(*text)); 79 | StreamReader reader(stream); 80 | vint index = 0; 81 | 82 | while (index < sizeof(lines) / sizeof(*lines)) 83 | { 84 | TEST_ASSERT(reader.IsEnd() == false); 85 | TEST_ASSERT(reader.ReadLine() == lines[index++]); 86 | } 87 | TEST_ASSERT(reader.IsEnd() == true); 88 | }); 89 | 90 | TEST_CASE(L"Test StreamReader without ending CRLF") 91 | { 92 | wchar_t text[] = L"1:Vczh is genius!\r\n2:Vczh is genius!!\r\n3:Vczh is genius!!!\r\n4:Vczh is genius!!!!"; 93 | const wchar_t* lines[] = {L"1:Vczh is genius!", L"2:Vczh is genius!!", L"3:Vczh is genius!!!", L"4:Vczh is genius!!!!"}; 94 | MemoryWrapperStream stream(text, sizeof(text) - sizeof(*text)); 95 | StreamReader reader(stream); 96 | vint index = 0; 97 | 98 | while (index < sizeof(lines) / sizeof(*lines)) 99 | { 100 | TEST_ASSERT(reader.IsEnd() == false); 101 | TEST_ASSERT(reader.ReadLine() == lines[index++]); 102 | } 103 | TEST_ASSERT(reader.IsEnd() == true); 104 | }); 105 | 106 | /*********************************************************************** 107 | StreamWriter 108 | ***********************************************************************/ 109 | 110 | TEST_CASE(L"Test StreamWriter") 111 | { 112 | wchar_t text[BUFFER_SIZE] = {0}; 113 | MemoryWrapperStream stream(text, sizeof(text) - sizeof(*text)); 114 | StreamWriter writer(stream); 115 | 116 | writer.WriteChar(L'1'); 117 | writer.WriteChar(L':'); 118 | writer.WriteChar(L'V'); 119 | writer.WriteChar(L'c'); 120 | writer.WriteChar(L'z'); 121 | writer.WriteChar(L'h'); 122 | writer.WriteChar(L' '); 123 | writer.WriteChar(L'i'); 124 | writer.WriteChar(L's'); 125 | writer.WriteChar(L' '); 126 | writer.WriteChar(L'g'); 127 | writer.WriteChar(L'e'); 128 | writer.WriteChar(L'n'); 129 | writer.WriteChar(L'i'); 130 | writer.WriteChar(L'u'); 131 | writer.WriteChar(L's'); 132 | writer.WriteChar(L'!'); 133 | writer.WriteString(L""); 134 | writer.WriteString(L"\r\n2:Vczh is genius!"); 135 | writer.WriteString(WString(L"")); 136 | writer.WriteLine(L""); 137 | writer.WriteLine(L"3:Vczh is genius!"); 138 | writer.WriteLine(WString(L"4:Vczh is genius!")); 139 | 140 | wchar_t baseline[] = L"1:Vczh is genius!\r\n2:Vczh is genius!\r\n3:Vczh is genius!\r\n4:Vczh is genius!\r\n"; 141 | TEST_ASSERT(wcscmp(text, baseline) == 0); 142 | }); 143 | } -------------------------------------------------------------------------------- /Test/UnitTest/UnitTest.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.421 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTest", "UnitTest\UnitTest.vcxproj", "{F17D112F-3D02-4A6B-BB8D-14E7D409774E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug|x64 = Debug|x64 12 | Release|Win32 = Release|Win32 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {F17D112F-3D02-4A6B-BB8D-14E7D409774E}.Debug|Win32.ActiveCfg = Debug|Win32 17 | {F17D112F-3D02-4A6B-BB8D-14E7D409774E}.Debug|Win32.Build.0 = Debug|Win32 18 | {F17D112F-3D02-4A6B-BB8D-14E7D409774E}.Debug|x64.ActiveCfg = Debug|x64 19 | {F17D112F-3D02-4A6B-BB8D-14E7D409774E}.Debug|x64.Build.0 = Debug|x64 20 | {F17D112F-3D02-4A6B-BB8D-14E7D409774E}.Release|Win32.ActiveCfg = Release|Win32 21 | {F17D112F-3D02-4A6B-BB8D-14E7D409774E}.Release|Win32.Build.0 = Release|Win32 22 | {F17D112F-3D02-4A6B-BB8D-14E7D409774E}.Release|x64.ActiveCfg = Release|x64 23 | {F17D112F-3D02-4A6B-BB8D-14E7D409774E}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {94AC2324-507B-4586-876F-70E3D182CC9B} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Test/UnitTest/UnitTest/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "../../../Source/FileSystem.h" 2 | #include 3 | 4 | using namespace vl; 5 | using namespace vl::filesystem; 6 | 7 | WString GetExePath() 8 | { 9 | wchar_t buffer[65536]; 10 | GetModuleFileName(NULL, buffer, sizeof(buffer)/sizeof(*buffer)); 11 | vint pos=-1; 12 | vint index=0; 13 | while(buffer[index]) 14 | { 15 | if(buffer[index]==L'\\' || buffer[index]==L'/') 16 | { 17 | pos=index; 18 | } 19 | index++; 20 | } 21 | return WString::CopyFrom(buffer, pos + 1); 22 | } 23 | 24 | WString GetTestOutputPath() 25 | { 26 | #ifdef _WIN64 27 | return GetExePath() + L"../../../Output/"; 28 | #else 29 | return GetExePath() + L"../../Output/"; 30 | #endif 31 | } 32 | 33 | TEST_FILE 34 | { 35 | TEST_CASE_ASSERT(Folder(GetTestOutputPath()).Exists()); 36 | } 37 | 38 | int wmain(int argc , wchar_t* argv[]) 39 | { 40 | { 41 | Folder folder(GetTestOutputPath()); 42 | if (!folder.Exists()) 43 | { 44 | folder.Create(false); 45 | } 46 | } 47 | int result = unittest::UnitTest::RunAndDisposeTests(argc, argv); 48 | FinalizeGlobalStorage(); 49 | #ifdef VCZH_CHECK_MEMORY_LEAKS 50 | _CrtDumpMemoryLeaks(); 51 | #endif 52 | return result; 53 | } -------------------------------------------------------------------------------- /Test/UnitTest/UnitTest/UnitTest.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {a0063a3f-fb90-4cc0-8369-26646e7eadf7} 18 | 19 | 20 | {746901f8-38ae-4ba3-9387-83468fc5559b} 21 | 22 | 23 | {f2b75d6f-510b-43a2-a249-bf08fc5e8147} 24 | 25 | 26 | {2b7c4044-fe5b-4999-9e23-0b399a59e43e} 27 | 28 | 29 | {9b8e7dd7-d560-48af-92fe-eae76a82879c} 30 | 31 | 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Common 50 | 51 | 52 | Common 53 | 54 | 55 | Common 56 | 57 | 58 | Common 59 | 60 | 61 | Common 62 | 63 | 64 | Common\Stream 65 | 66 | 67 | Common\Stream 68 | 69 | 70 | Common\Stream 71 | 72 | 73 | Common\Stream 74 | 75 | 76 | Common\Stream 77 | 78 | 79 | Common\Stream 80 | 81 | 82 | Source Files 83 | 84 | 85 | Import 86 | 87 | 88 | Source Files 89 | 90 | 91 | Source Files 92 | 93 | 94 | Source Files 95 | 96 | 97 | Source Files 98 | 99 | 100 | Common\Stream 101 | 102 | 103 | Import 104 | 105 | 106 | Import 107 | 108 | 109 | Common 110 | 111 | 112 | Common 113 | 114 | 115 | Common 116 | 117 | 118 | Common 119 | 120 | 121 | Common 122 | 123 | 124 | Common\Encoding\CharFormat 125 | 126 | 127 | Common\Encoding\CharFormat 128 | 129 | 130 | Common\Encoding\CharFormat 131 | 132 | 133 | Common\Encoding\CharFormat 134 | 135 | 136 | Common\Encoding 137 | 138 | 139 | Common\Stream 140 | 141 | 142 | Common\Encoding\CharFormat 143 | 144 | 145 | Common\Encoding\CharFormat 146 | 147 | 148 | Common\Encoding 149 | 150 | 151 | Common\Encoding 152 | 153 | 154 | Source Files 155 | 156 | 157 | 158 | 159 | Common 160 | 161 | 162 | Common 163 | 164 | 165 | Common 166 | 167 | 168 | Common 169 | 170 | 171 | Common\Stream 172 | 173 | 174 | Common\Stream 175 | 176 | 177 | Common\Stream 178 | 179 | 180 | Common\Stream 181 | 182 | 183 | Common\Stream 184 | 185 | 186 | Common\Stream 187 | 188 | 189 | Common\Stream 190 | 191 | 192 | Import 193 | 194 | 195 | Common\Stream 196 | 197 | 198 | Common\Stream 199 | 200 | 201 | Common\Encoding\CharFormat 202 | 203 | 204 | Common\Encoding 205 | 206 | 207 | Common\Encoding 208 | 209 | 210 | Common\Stream 211 | 212 | 213 | Common\Encoding\CharFormat 214 | 215 | 216 | Common\Encoding\CharFormat 217 | 218 | 219 | Common\Encoding\CharFormat 220 | 221 | 222 | Common\Encoding 223 | 224 | 225 | --------------------------------------------------------------------------------