Showing
31 changed files
with
4757 additions
and
4 deletions
Too many changes to show.
To preserve performance only 31 of 31+ files are displayed.
... | ... | @@ -38,6 +38,7 @@ project "TEngine" |
38 | 38 | files { "../steamworks/luasteam.c", } |
39 | 39 | end |
40 | 40 | links { "physfs", "lua".._OPTIONS.lua, "fov", "luasocket", "luaprofiler", "lpeg", "tcodimport", "lxp", "expatstatic", "luamd5", "luazlib", "luabitop", "te4-bzip" } |
41 | + if _OPTIONS.discord then links { "te4-discord" } defines { "DISCORD_TE4" } end | |
41 | 42 | defines { "_DEFAULT_VIDEOMODE_FLAGS_='SDL_HWSURFACE|SDL_DOUBLEBUF'" } |
42 | 43 | defines { [[TENGINE_HOME_PATH='".t-engine"']], "TE4CORE_VERSION="..TE4CORE_VERSION } |
43 | 44 | buildoptions { "-O3" } |
... | ... | @@ -597,3 +598,24 @@ end |
597 | 598 | if _OPTIONS.steam then |
598 | 599 | dofile("../steamworks/build/steam-code.lua") |
599 | 600 | end |
601 | + | |
602 | +if _OPTIONS.discord then | |
603 | +project "te4-discord" | |
604 | + kind "SharedLib" | |
605 | + language "C++" | |
606 | + targetname "te4-discord" | |
607 | + | |
608 | + includedirs { "../src/discord-rpc/include/", "../src/discord-rpc/rapidjson/", } | |
609 | + | |
610 | + files { "../src/discord-rpc/te4-discord.cpp", "../src/discord-rpc/src/discord-rpc.cpp", "../src/discord-rpc/src/rpc_connection.cpp", "../src/discord-rpc/src/serialization.cpp", } | |
611 | + -- defines { "DISCORD_DISABLE_IO_THREAD" } | |
612 | + | |
613 | + -- configuration "macosx" | |
614 | + -- files {} | |
615 | + | |
616 | + configuration "linux" | |
617 | + files { "../src/discord-rpc/src/connection_unix.cpp", "../src/discord-rpc/src/discord_register_linux.cpp", } | |
618 | + | |
619 | + configuration "windows" | |
620 | + files { "../src/discord-rpc/src/connection_win.cpp", "../src/discord-rpc/src/discord_register_win.cpp", } | |
621 | +end | ... | ... |
... | ... | @@ -1019,6 +1019,7 @@ function _M:instanciate(mod, name, new_game, no_reboot, extra_module_info) |
1019 | 1019 | profile:incrLoadProfile(mod) |
1020 | 1020 | profile:currentCharacter(mod.full_version_string, "game did not tell us") |
1021 | 1021 | |
1022 | + | |
1022 | 1023 | UIBase:clearCache() |
1023 | 1024 | |
1024 | 1025 | -- Some requires cleanup, to correctly let modules apply settings | ... | ... |
... | ... | @@ -169,4 +169,8 @@ savefile_pipe = engine.SavefilePipe.new() |
169 | 169 | -- Setup FPS |
170 | 170 | core.game.setFPS(config.settings.display_fps) |
171 | 171 | |
172 | +if config.settings.disable_discord then core.discord = nil end | |
173 | +if core.discord then core.discord.init() end | |
174 | + | |
172 | 175 | util.showMainMenu(true) |
176 | + | ... | ... |
... | ... | @@ -91,6 +91,7 @@ function _M:init() |
91 | 91 | |
92 | 92 | self:loaded() |
93 | 93 | profile:currentCharacter("Main Menu", "Main Menu") |
94 | + if core.discord then core.discord.updatePresence{state="Main Menu", details="Thinking about which character to die with!", large_image="default", large_image_text="Pondering..."} end | |
94 | 95 | end |
95 | 96 | |
96 | 97 | function _M:loaded() | ... | ... |
... | ... | @@ -710,6 +710,28 @@ function _M:updateCurrentChar() |
710 | 710 | if not self.party then return end |
711 | 711 | local player = self.party:findMember{main=true} |
712 | 712 | profile:currentCharacter(self.__mod_info.full_version_string, ("%s the level %d %s %s"):format(player.name, player.level, player.descriptor.subrace, player.descriptor.subclass), player.__te4_uuid) |
713 | + if core.discord and self.zone then | |
714 | + local info = {} | |
715 | + info.zone = self:getZoneName() | |
716 | + info.char = ("Lvl %d %s %s"):format(player.level, player.descriptor.subrace, player.descriptor.subclass) | |
717 | + info.splash = "default" | |
718 | + info.splash_text = ("%s playing on %s %s; died %d time%s!"):format(player.name, player.descriptor.permadeath, player.descriptor.difficulty, player.died_times and #player.died_times or 0, (player.died_times and #player.died_times == 1) and "" or "s") | |
719 | + -- info.icon = "archmage" | |
720 | + -- info.icon_text = "lol" | |
721 | + | |
722 | + -- Determine which dlc it originates from | |
723 | + local _, _, addon = self.zone.short_name:find("^([^+]+)%+(.*)$") | |
724 | + if addon then | |
725 | + local addon_data = self.__mod_info.addons[addon] | |
726 | + if addon_data and addon_data.id_dlc then | |
727 | + info.splash = addon | |
728 | + end | |
729 | + end | |
730 | + -- Let the DLC override it in a more smart way | |
731 | + self:triggerHook{"Discord:check", info=info} | |
732 | + | |
733 | + core.discord.updatePresence{state=info.zone, details=info.char, large_image=info.splash, large_image_text=info.splash_text, small_image=info.icon, small_image_text=info.icon_text} | |
734 | + end | |
713 | 735 | end |
714 | 736 | |
715 | 737 | function _M:getSaveDescription() |
... | ... | @@ -1386,10 +1408,7 @@ function _M:chronoRestore(name, remove) |
1386 | 1408 | return true |
1387 | 1409 | end |
1388 | 1410 | |
1389 | ---- Update the zone name, if needed | |
1390 | -function _M:updateZoneName() | |
1391 | - if not self.zone_font then return end | |
1392 | - local name | |
1411 | +function _M:getZoneName() | |
1393 | 1412 | if self.zone.display_name then |
1394 | 1413 | name = self.zone.display_name() |
1395 | 1414 | else |
... | ... | @@ -1401,6 +1420,13 @@ function _M:updateZoneName() |
1401 | 1420 | name = ("%s (%d)"):format(self.zone.name, lev) |
1402 | 1421 | end |
1403 | 1422 | end |
1423 | + return name | |
1424 | +end | |
1425 | + | |
1426 | +--- Update the zone name, if needed | |
1427 | +function _M:updateZoneName() | |
1428 | + if not self.zone_font then return end | |
1429 | + local name = self:getZoneName() | |
1404 | 1430 | if self.zone_name_s and self.old_zone_name == name then return end |
1405 | 1431 | |
1406 | 1432 | local s = core.display.drawStringBlendedNewSurface(self.zone_font, name, unpack(colors.simple(colors.GOLD))) | ... | ... |
... | ... | @@ -588,6 +588,15 @@ function _M:generateListOnline() |
588 | 588 | self.c_list:drawItem(item) |
589 | 589 | end,} |
590 | 590 | |
591 | + local zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text=string.toTString"Enable Discord's Rich Presence integration to show your current character on your currently playing profile on Discord (restart the game to apply).\n#ANTIQUE_WHITE#If you do not use Discord this option doesn't do anything in either state."} | |
592 | + list[#list+1] = { zone=zone, name=string.toTString"#GOLD##{bold}#Discord's Rich Presence#WHITE##{normal}#", status=function(item) | |
593 | + return tostring(not config.settings.disable_discord and "enabled" or "disabled") | |
594 | + end, fct=function(item) | |
595 | + config.settings.disable_discord = not config.settings.disable_discord | |
596 | + game:saveSettings("disable_discord", ("disable_discord = %s\n"):format(tostring(config.settings.disable_discord))) | |
597 | + self.c_list:drawItem(item) | |
598 | + end,} | |
599 | + | |
591 | 600 | self.list = list |
592 | 601 | end |
593 | 602 | ... | ... |
src/discord-rpc/LICENSE
0 → 100644
1 | +Copyright 2017 Discord, Inc. | |
2 | + | |
3 | +Permission is hereby granted, free of charge, to any person obtaining a copy of | |
4 | +this software and associated documentation files (the "Software"), to deal in | |
5 | +the Software without restriction, including without limitation the rights to | |
6 | +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |
7 | +of the Software, and to permit persons to whom the Software is furnished to do | |
8 | +so, subject to the following conditions: | |
9 | + | |
10 | +The above copyright notice and this permission notice shall be included in all | |
11 | +copies or substantial portions of the Software. | |
12 | + | |
13 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
14 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
15 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
16 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
17 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
18 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
19 | +SOFTWARE. | ... | ... |
src/discord-rpc/include/discord-rpc.h
0 → 100644
1 | +#pragma once | |
2 | +#include <stdint.h> | |
3 | + | |
4 | +// clang-format off | |
5 | + | |
6 | +#if defined(DISCORD_DYNAMIC_LIB) | |
7 | +# if defined(_WIN32) | |
8 | +# if defined(DISCORD_BUILDING_SDK) | |
9 | +# define DISCORD_EXPORT __declspec(dllexport) | |
10 | +# else | |
11 | +# define DISCORD_EXPORT __declspec(dllimport) | |
12 | +# endif | |
13 | +# else | |
14 | +# define DISCORD_EXPORT __attribute__((visibility("default"))) | |
15 | +# endif | |
16 | +#else | |
17 | +# define DISCORD_EXPORT | |
18 | +#endif | |
19 | + | |
20 | +// clang-format on | |
21 | + | |
22 | +#ifdef __cplusplus | |
23 | +extern "C" { | |
24 | +#endif | |
25 | + | |
26 | +typedef struct DiscordRichPresence { | |
27 | + const char* state; /* max 128 bytes */ | |
28 | + const char* details; /* max 128 bytes */ | |
29 | + int64_t startTimestamp; | |
30 | + int64_t endTimestamp; | |
31 | + const char* largeImageKey; /* max 32 bytes */ | |
32 | + const char* largeImageText; /* max 128 bytes */ | |
33 | + const char* smallImageKey; /* max 32 bytes */ | |
34 | + const char* smallImageText; /* max 128 bytes */ | |
35 | + const char* partyId; /* max 128 bytes */ | |
36 | + int partySize; | |
37 | + int partyMax; | |
38 | + const char* matchSecret; /* max 128 bytes */ | |
39 | + const char* joinSecret; /* max 128 bytes */ | |
40 | + const char* spectateSecret; /* max 128 bytes */ | |
41 | + int8_t instance; | |
42 | +} DiscordRichPresence; | |
43 | + | |
44 | +typedef struct DiscordJoinRequest { | |
45 | + const char* userId; | |
46 | + const char* username; | |
47 | + const char* avatar; | |
48 | +} DiscordJoinRequest; | |
49 | + | |
50 | +typedef struct DiscordEventHandlers { | |
51 | + void (*ready)(); | |
52 | + void (*disconnected)(int errorCode, const char* message); | |
53 | + void (*errored)(int errorCode, const char* message); | |
54 | + void (*joinGame)(const char* joinSecret); | |
55 | + void (*spectateGame)(const char* spectateSecret); | |
56 | + void (*joinRequest)(const DiscordJoinRequest* request); | |
57 | +} DiscordEventHandlers; | |
58 | + | |
59 | +#define DISCORD_REPLY_NO 0 | |
60 | +#define DISCORD_REPLY_YES 1 | |
61 | +#define DISCORD_REPLY_IGNORE 2 | |
62 | + | |
63 | +DISCORD_EXPORT void Discord_Initialize(const char* applicationId, | |
64 | + DiscordEventHandlers* handlers, | |
65 | + int autoRegister, | |
66 | + const char* optionalSteamId); | |
67 | +DISCORD_EXPORT void Discord_Shutdown(void); | |
68 | + | |
69 | +/* checks for incoming messages, dispatches callbacks */ | |
70 | +DISCORD_EXPORT void Discord_RunCallbacks(void); | |
71 | + | |
72 | +/* If you disable the lib starting its own io thread, you'll need to call this from your own */ | |
73 | +#ifdef DISCORD_DISABLE_IO_THREAD | |
74 | +DISCORD_EXPORT void Discord_UpdateConnection(void); | |
75 | +#endif | |
76 | + | |
77 | +DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence); | |
78 | + | |
79 | +DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply); | |
80 | + | |
81 | +#ifdef __cplusplus | |
82 | +} /* extern "C" */ | |
83 | +#endif | ... | ... |
1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | |
2 | +// | |
3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | |
4 | +// | |
5 | +// Licensed under the MIT License (the "License"); you may not use this file except | |
6 | +// in compliance with the License. You may obtain a copy of the License at | |
7 | +// | |
8 | +// http://opensource.org/licenses/MIT | |
9 | +// | |
10 | +// Unless required by applicable law or agreed to in writing, software distributed | |
11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | |
12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | |
13 | +// specific language governing permissions and limitations under the License. | |
14 | + | |
15 | +#ifndef RAPIDJSON_ALLOCATORS_H_ | |
16 | +#define RAPIDJSON_ALLOCATORS_H_ | |
17 | + | |
18 | +#include "rapidjson.h" | |
19 | + | |
20 | +RAPIDJSON_NAMESPACE_BEGIN | |
21 | + | |
22 | +/////////////////////////////////////////////////////////////////////////////// | |
23 | +// Allocator | |
24 | + | |
25 | +/*! \class rapidjson::Allocator | |
26 | + \brief Concept for allocating, resizing and freeing memory block. | |
27 | + | |
28 | + Note that Malloc() and Realloc() are non-static but Free() is static. | |
29 | + | |
30 | + So if an allocator need to support Free(), it needs to put its pointer in | |
31 | + the header of memory block. | |
32 | + | |
33 | +\code | |
34 | +concept Allocator { | |
35 | + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). | |
36 | + | |
37 | + // Allocate a memory block. | |
38 | + // \param size of the memory block in bytes. | |
39 | + // \returns pointer to the memory block. | |
40 | + void* Malloc(size_t size); | |
41 | + | |
42 | + // Resize a memory block. | |
43 | + // \param originalPtr The pointer to current memory block. Null pointer is permitted. | |
44 | + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) | |
45 | + // \param newSize the new size in bytes. | |
46 | + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); | |
47 | + | |
48 | + // Free a memory block. | |
49 | + // \param pointer to the memory block. Null pointer is permitted. | |
50 | + static void Free(void *ptr); | |
51 | +}; | |
52 | +\endcode | |
53 | +*/ | |
54 | + | |
55 | +/////////////////////////////////////////////////////////////////////////////// | |
56 | +// CrtAllocator | |
57 | + | |
58 | +//! C-runtime library allocator. | |
59 | +/*! This class is just wrapper for standard C library memory routines. | |
60 | + \note implements Allocator concept | |
61 | +*/ | |
62 | +class CrtAllocator { | |
63 | +public: | |
64 | + static const bool kNeedFree = true; | |
65 | + void* Malloc(size_t size) { | |
66 | + if (size) // behavior of malloc(0) is implementation defined. | |
67 | + return std::malloc(size); | |
68 | + else | |
69 | + return NULL; // standardize to returning NULL. | |
70 | + } | |
71 | + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { | |
72 | + (void)originalSize; | |
73 | + if (newSize == 0) { | |
74 | + std::free(originalPtr); | |
75 | + return NULL; | |
76 | + } | |
77 | + return std::realloc(originalPtr, newSize); | |
78 | + } | |
79 | + static void Free(void *ptr) { std::free(ptr); } | |
80 | +}; | |
81 | + | |
82 | +/////////////////////////////////////////////////////////////////////////////// | |
83 | +// MemoryPoolAllocator | |
84 | + | |
85 | +//! Default memory allocator used by the parser and DOM. | |
86 | +/*! This allocator allocate memory blocks from pre-allocated memory chunks. | |
87 | + | |
88 | + It does not free memory blocks. And Realloc() only allocate new memory. | |
89 | + | |
90 | + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. | |
91 | + | |
92 | + User may also supply a buffer as the first chunk. | |
93 | + | |
94 | + If the user-buffer is full then additional chunks are allocated by BaseAllocator. | |
95 | + | |
96 | + The user-buffer is not deallocated by this allocator. | |
97 | + | |
98 | + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. | |
99 | + \note implements Allocator concept | |
100 | +*/ | |
101 | +template <typename BaseAllocator = CrtAllocator> | |
102 | +class MemoryPoolAllocator { | |
103 | +public: | |
104 | + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) | |
105 | + | |
106 | + //! Constructor with chunkSize. | |
107 | + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. | |
108 | + \param baseAllocator The allocator for allocating memory chunks. | |
109 | + */ | |
110 | + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : | |
111 | + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) | |
112 | + { | |
113 | + } | |
114 | + | |
115 | + //! Constructor with user-supplied buffer. | |
116 | + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. | |
117 | + | |
118 | + The user buffer will not be deallocated when this allocator is destructed. | |
119 | + | |
120 | + \param buffer User supplied buffer. | |
121 | + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). | |
122 | + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. | |
123 | + \param baseAllocator The allocator for allocating memory chunks. | |
124 | + */ | |
125 | + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : | |
126 | + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) | |
127 | + { | |
128 | + RAPIDJSON_ASSERT(buffer != 0); | |
129 | + RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); | |
130 | + chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer); | |
131 | + chunkHead_->capacity = size - sizeof(ChunkHeader); | |
132 | + chunkHead_->size = 0; | |
133 | + chunkHead_->next = 0; | |
134 | + } | |
135 | + | |
136 | + //! Destructor. | |
137 | + /*! This deallocates all memory chunks, excluding the user-supplied buffer. | |
138 | + */ | |
139 | + ~MemoryPoolAllocator() { | |
140 | + Clear(); | |
141 | + RAPIDJSON_DELETE(ownBaseAllocator_); | |
142 | + } | |
143 | + | |
144 | + //! Deallocates all memory chunks, excluding the user-supplied buffer. | |
145 | + void Clear() { | |
146 | + while (chunkHead_ && chunkHead_ != userBuffer_) { | |
147 | + ChunkHeader* next = chunkHead_->next; | |
148 | + baseAllocator_->Free(chunkHead_); | |
149 | + chunkHead_ = next; | |
150 | + } | |
151 | + if (chunkHead_ && chunkHead_ == userBuffer_) | |
152 | + chunkHead_->size = 0; // Clear user buffer | |
153 | + } | |
154 | + | |
155 | + //! Computes the total capacity of allocated memory chunks. | |
156 | + /*! \return total capacity in bytes. | |
157 | + */ | |
158 | + size_t Capacity() const { | |
159 | + size_t capacity = 0; | |
160 | + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) | |
161 | + capacity += c->capacity; | |
162 | + return capacity; | |
163 | + } | |
164 | + | |
165 | + //! Computes the memory blocks allocated. | |
166 | + /*! \return total used bytes. | |
167 | + */ | |
168 | + size_t Size() const { | |
169 | + size_t size = 0; | |
170 | + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) | |
171 | + size += c->size; | |
172 | + return size; | |
173 | + } | |
174 | + | |
175 | + //! Allocates a memory block. (concept Allocator) | |
176 | + void* Malloc(size_t size) { | |
177 | + if (!size) | |
178 | + return NULL; | |
179 | + | |
180 | + size = RAPIDJSON_ALIGN(size); | |
181 | + if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) | |
182 | + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) | |
183 | + return NULL; | |
184 | + | |
185 | + void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; | |
186 | + chunkHead_->size += size; | |
187 | + return buffer; | |
188 | + } | |
189 | + | |
190 | + //! Resizes a memory block (concept Allocator) | |
191 | + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { | |
192 | + if (originalPtr == 0) | |
193 | + return Malloc(newSize); | |
194 | + | |
195 | + if (newSize == 0) | |
196 | + return NULL; | |
197 | + | |
198 | + originalSize = RAPIDJSON_ALIGN(originalSize); | |
199 | + newSize = RAPIDJSON_ALIGN(newSize); | |
200 | + | |
201 | + // Do not shrink if new size is smaller than original | |
202 | + if (originalSize >= newSize) | |
203 | + return originalPtr; | |
204 | + | |
205 | + // Simply expand it if it is the last allocation and there is sufficient space | |
206 | + if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { | |
207 | + size_t increment = static_cast<size_t>(newSize - originalSize); | |
208 | + if (chunkHead_->size + increment <= chunkHead_->capacity) { | |
209 | + chunkHead_->size += increment; | |
210 | + return originalPtr; | |
211 | + } | |
212 | + } | |
213 | + | |
214 | + // Realloc process: allocate and copy memory, do not free original buffer. | |
215 | + if (void* newBuffer = Malloc(newSize)) { | |
216 | + if (originalSize) | |
217 | + std::memcpy(newBuffer, originalPtr, originalSize); | |
218 | + return newBuffer; | |
219 | + } | |
220 | + else | |
221 | + return NULL; | |
222 | + } | |
223 | + | |
224 | + //! Frees a memory block (concept Allocator) | |
225 | + static void Free(void *ptr) { (void)ptr; } // Do nothing | |
226 | + | |
227 | +private: | |
228 | + //! Copy constructor is not permitted. | |
229 | + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; | |
230 | + //! Copy assignment operator is not permitted. | |
231 | + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; | |
232 | + | |
233 | + //! Creates a new chunk. | |
234 | + /*! \param capacity Capacity of the chunk in bytes. | |
235 | + \return true if success. | |
236 | + */ | |
237 | + bool AddChunk(size_t capacity) { | |
238 | + if (!baseAllocator_) | |
239 | + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); | |
240 | + if (ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { | |
241 | + chunk->capacity = capacity; | |
242 | + chunk->size = 0; | |
243 | + chunk->next = chunkHead_; | |
244 | + chunkHead_ = chunk; | |
245 | + return true; | |
246 | + } | |
247 | + else | |
248 | + return false; | |
249 | + } | |
250 | + | |
251 | + static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. | |
252 | + | |
253 | + //! Chunk header for perpending to each chunk. | |
254 | + /*! Chunks are stored as a singly linked list. | |
255 | + */ | |
256 | + struct ChunkHeader { | |
257 | + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). | |
258 | + size_t size; //!< Current size of allocated memory in bytes. | |
259 | + ChunkHeader *next; //!< Next chunk in the linked list. | |
260 | + }; | |
261 | + | |
262 | + ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. | |
263 | + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. | |
264 | + void *userBuffer_; //!< User supplied buffer. | |
265 | + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. | |
266 | + BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. | |
267 | +}; | |
268 | + | |
269 | +RAPIDJSON_NAMESPACE_END | |
270 | + | |
271 | +#endif // RAPIDJSON_ENCODINGS_H_ | ... | ... |
This diff could not be displayed because it is too large.
1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | |
2 | +// | |
3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | |
4 | +// | |
5 | +// Licensed under the MIT License (the "License"); you may not use this file except | |
6 | +// in compliance with the License. You may obtain a copy of the License at | |
7 | +// | |
8 | +// http://opensource.org/licenses/MIT | |
9 | +// | |
10 | +// Unless required by applicable law or agreed to in writing, software distributed | |
11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | |
12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | |
13 | +// specific language governing permissions and limitations under the License. | |
14 | + | |
15 | +#ifndef RAPIDJSON_ENCODEDSTREAM_H_ | |
16 | +#define RAPIDJSON_ENCODEDSTREAM_H_ | |
17 | + | |
18 | +#include "stream.h" | |
19 | +#include "memorystream.h" | |
20 | + | |
21 | +#ifdef __GNUC__ | |
22 | +RAPIDJSON_DIAG_PUSH | |
23 | +RAPIDJSON_DIAG_OFF(effc++) | |
24 | +#endif | |
25 | + | |
26 | +#ifdef __clang__ | |
27 | +RAPIDJSON_DIAG_PUSH | |
28 | +RAPIDJSON_DIAG_OFF(padded) | |
29 | +#endif | |
30 | + | |
31 | +RAPIDJSON_NAMESPACE_BEGIN | |
32 | + | |
33 | +//! Input byte stream wrapper with a statically bound encoding. | |
34 | +/*! | |
35 | + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. | |
36 | + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. | |
37 | +*/ | |
38 | +template <typename Encoding, typename InputByteStream> | |
39 | +class EncodedInputStream { | |
40 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | |
41 | +public: | |
42 | + typedef typename Encoding::Ch Ch; | |
43 | + | |
44 | + EncodedInputStream(InputByteStream& is) : is_(is) { | |
45 | + current_ = Encoding::TakeBOM(is_); | |
46 | + } | |
47 | + | |
48 | + Ch Peek() const { return current_; } | |
49 | + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } | |
50 | + size_t Tell() const { return is_.Tell(); } | |
51 | + | |
52 | + // Not implemented | |
53 | + void Put(Ch) { RAPIDJSON_ASSERT(false); } | |
54 | + void Flush() { RAPIDJSON_ASSERT(false); } | |
55 | + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | |
56 | + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } | |
57 | + | |
58 | +private: | |
59 | + EncodedInputStream(const EncodedInputStream&); | |
60 | + EncodedInputStream& operator=(const EncodedInputStream&); | |
61 | + | |
62 | + InputByteStream& is_; | |
63 | + Ch current_; | |
64 | +}; | |
65 | + | |
66 | +//! Specialized for UTF8 MemoryStream. | |
67 | +template <> | |
68 | +class EncodedInputStream<UTF8<>, MemoryStream> { | |
69 | +public: | |
70 | + typedef UTF8<>::Ch Ch; | |
71 | + | |
72 | + EncodedInputStream(MemoryStream& is) : is_(is) { | |
73 | + if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take(); | |
74 | + if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take(); | |
75 | + if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take(); | |
76 | + } | |
77 | + Ch Peek() const { return is_.Peek(); } | |
78 | + Ch Take() { return is_.Take(); } | |
79 | + size_t Tell() const { return is_.Tell(); } | |
80 | + | |
81 | + // Not implemented | |
82 | + void Put(Ch) {} | |
83 | + void Flush() {} | |
84 | + Ch* PutBegin() { return 0; } | |
85 | + size_t PutEnd(Ch*) { return 0; } | |
86 | + | |
87 | + MemoryStream& is_; | |
88 | + | |
89 | +private: | |
90 | + EncodedInputStream(const EncodedInputStream&); | |
91 | + EncodedInputStream& operator=(const EncodedInputStream&); | |
92 | +}; | |
93 | + | |
94 | +//! Output byte stream wrapper with statically bound encoding. | |
95 | +/*! | |
96 | + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. | |
97 | + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. | |
98 | +*/ | |
99 | +template <typename Encoding, typename OutputByteStream> | |
100 | +class EncodedOutputStream { | |
101 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | |
102 | +public: | |
103 | + typedef typename Encoding::Ch Ch; | |
104 | + | |
105 | + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { | |
106 | + if (putBOM) | |
107 | + Encoding::PutBOM(os_); | |
108 | + } | |
109 | + | |
110 | + void Put(Ch c) { Encoding::Put(os_, c); } | |
111 | + void Flush() { os_.Flush(); } | |
112 | + | |
113 | + // Not implemented | |
114 | + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} | |
115 | + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} | |
116 | + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } | |
117 | + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | |
118 | + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } | |
119 | + | |
120 | +private: | |
121 | + EncodedOutputStream(const EncodedOutputStream&); | |
122 | + EncodedOutputStream& operator=(const EncodedOutputStream&); | |
123 | + | |
124 | + OutputByteStream& os_; | |
125 | +}; | |
126 | + | |
127 | +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x | |
128 | + | |
129 | +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. | |
130 | +/*! | |
131 | + \tparam CharType Type of character for reading. | |
132 | + \tparam InputByteStream type of input byte stream to be wrapped. | |
133 | +*/ | |
134 | +template <typename CharType, typename InputByteStream> | |
135 | +class AutoUTFInputStream { | |
136 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | |
137 | +public: | |
138 | + typedef CharType Ch; | |
139 | + | |
140 | + //! Constructor. | |
141 | + /*! | |
142 | + \param is input stream to be wrapped. | |
143 | + \param type UTF encoding type if it is not detected from the stream. | |
144 | + */ | |
145 | + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { | |
146 | + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); | |
147 | + DetectType(); | |
148 | + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; | |
149 | + takeFunc_ = f[type_]; | |
150 | + current_ = takeFunc_(*is_); | |
151 | + } | |
152 | + | |
153 | + UTFType GetType() const { return type_; } | |
154 | + bool HasBOM() const { return hasBOM_; } | |
155 | + | |
156 | + Ch Peek() const { return current_; } | |
157 | + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } | |
158 | + size_t Tell() const { return is_->Tell(); } | |
159 | + | |
160 | + // Not implemented | |
161 | + void Put(Ch) { RAPIDJSON_ASSERT(false); } | |
162 | + void Flush() { RAPIDJSON_ASSERT(false); } | |
163 | + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | |
164 | + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } | |
165 | + | |
166 | +private: | |
167 | + AutoUTFInputStream(const AutoUTFInputStream&); | |
168 | + AutoUTFInputStream& operator=(const AutoUTFInputStream&); | |
169 | + | |
170 | + // Detect encoding type with BOM or RFC 4627 | |
171 | + void DetectType() { | |
172 | + // BOM (Byte Order Mark): | |
173 | + // 00 00 FE FF UTF-32BE | |
174 | + // FF FE 00 00 UTF-32LE | |
175 | + // FE FF UTF-16BE | |
176 | + // FF FE UTF-16LE | |
177 | + // EF BB BF UTF-8 | |
178 | + | |
179 | + const unsigned char* c = reinterpret_cast<const unsigned char *>(is_->Peek4()); | |
180 | + if (!c) | |
181 | + return; | |
182 | + | |
183 | + unsigned bom = static_cast<unsigned>(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); | |
184 | + hasBOM_ = false; | |
185 | + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } | |
186 | + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } | |
187 | + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } | |
188 | + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } | |
189 | + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } | |
190 | + | |
191 | + // RFC 4627: Section 3 | |
192 | + // "Since the first two characters of a JSON text will always be ASCII | |
193 | + // characters [RFC0020], it is possible to determine whether an octet | |
194 | + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking | |
195 | + // at the pattern of nulls in the first four octets." | |
196 | + // 00 00 00 xx UTF-32BE | |
197 | + // 00 xx 00 xx UTF-16BE | |
198 | + // xx 00 00 00 UTF-32LE | |
199 | + // xx 00 xx 00 UTF-16LE | |
200 | + // xx xx xx xx UTF-8 | |
201 | + | |
202 | + if (!hasBOM_) { | |
203 | + unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); | |
204 | + switch (pattern) { | |
205 | + case 0x08: type_ = kUTF32BE; break; | |
206 | + case 0x0A: type_ = kUTF16BE; break; | |
207 | + case 0x01: type_ = kUTF32LE; break; | |
208 | + case 0x05: type_ = kUTF16LE; break; | |
209 | + case 0x0F: type_ = kUTF8; break; | |
210 | + default: break; // Use type defined by user. | |
211 | + } | |
212 | + } | |
213 | + | |
214 | + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. | |
215 | + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); | |
216 | + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); | |
217 | + } | |
218 | + | |
219 | + typedef Ch (*TakeFunc)(InputByteStream& is); | |
220 | + InputByteStream* is_; | |
221 | + UTFType type_; | |
222 | + Ch current_; | |
223 | + TakeFunc takeFunc_; | |
224 | + bool hasBOM_; | |
225 | +}; | |
226 | + | |
227 | +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. | |
228 | +/*! | |
229 | + \tparam CharType Type of character for writing. | |
230 | + \tparam OutputByteStream type of output byte stream to be wrapped. | |
231 | +*/ | |
232 | +template <typename CharType, typename OutputByteStream> | |
233 | +class AutoUTFOutputStream { | |
234 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | |
235 | +public: | |
236 | + typedef CharType Ch; | |
237 | + | |
238 | + //! Constructor. | |
239 | + /*! | |
240 | + \param os output stream to be wrapped. | |
241 | + \param type UTF encoding type. | |
242 | + \param putBOM Whether to write BOM at the beginning of the stream. | |
243 | + */ | |
244 | + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { | |
245 | + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); | |
246 | + | |
247 | + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. | |
248 | + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); | |
249 | + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); | |
250 | + | |
251 | + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; | |
252 | + putFunc_ = f[type_]; | |
253 | + | |
254 | + if (putBOM) | |
255 | + PutBOM(); | |
256 | + } | |
257 | + | |
258 | + UTFType GetType() const { return type_; } | |
259 | + | |
260 | + void Put(Ch c) { putFunc_(*os_, c); } | |
261 | + void Flush() { os_->Flush(); } | |
262 | + | |
263 | + // Not implemented | |
264 | + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} | |
265 | + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} | |
266 | + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } | |
267 | + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | |
268 | + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } | |
269 | + | |
270 | +private: | |
271 | + AutoUTFOutputStream(const AutoUTFOutputStream&); | |
272 | + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); | |
273 | + | |
274 | + void PutBOM() { | |
275 | + typedef void (*PutBOMFunc)(OutputByteStream&); | |
276 | + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; | |
277 | + f[type_](*os_); | |
278 | + } | |
279 | + | |
280 | + typedef void (*PutFunc)(OutputByteStream&, Ch); | |
281 | + | |
282 | + OutputByteStream* os_; | |
283 | + UTFType type_; | |
284 | + PutFunc putFunc_; | |
285 | +}; | |
286 | + | |
287 | +#undef RAPIDJSON_ENCODINGS_FUNC | |
288 | + | |
289 | +RAPIDJSON_NAMESPACE_END | |
290 | + | |
291 | +#ifdef __clang__ | |
292 | +RAPIDJSON_DIAG_POP | |
293 | +#endif | |
294 | + | |
295 | +#ifdef __GNUC__ | |
296 | +RAPIDJSON_DIAG_POP | |
297 | +#endif | |
298 | + | |
299 | +#endif // RAPIDJSON_FILESTREAM_H_ | ... | ... |
1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | |
2 | +// | |
3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | |
4 | +// | |
5 | +// Licensed under the MIT License (the "License"); you may not use this file except | |
6 | +// in compliance with the License. You may obtain a copy of the License at | |
7 | +// | |
8 | +// http://opensource.org/licenses/MIT | |
9 | +// | |
10 | +// Unless required by applicable law or agreed to in writing, software distributed | |
11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | |
12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | |
13 | +// specific language governing permissions and limitations under the License. | |
14 | + | |
15 | +#ifndef RAPIDJSON_ENCODINGS_H_ | |
16 | +#define RAPIDJSON_ENCODINGS_H_ | |
17 | + | |
18 | +#include "rapidjson.h" | |
19 | + | |
20 | +#ifdef _MSC_VER | |
21 | +RAPIDJSON_DIAG_PUSH | |
22 | +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data | |
23 | +RAPIDJSON_DIAG_OFF(4702) // unreachable code | |
24 | +#elif defined(__GNUC__) | |
25 | +RAPIDJSON_DIAG_PUSH | |
26 | +RAPIDJSON_DIAG_OFF(effc++) | |
27 | +RAPIDJSON_DIAG_OFF(overflow) | |
28 | +#endif | |
29 | + | |
30 | +RAPIDJSON_NAMESPACE_BEGIN | |
31 | + | |
32 | +/////////////////////////////////////////////////////////////////////////////// | |
33 | +// Encoding | |
34 | + | |
35 | +/*! \class rapidjson::Encoding | |
36 | + \brief Concept for encoding of Unicode characters. | |
37 | + | |
38 | +\code | |
39 | +concept Encoding { | |
40 | + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. | |
41 | + | |
42 | + enum { supportUnicode = 1 }; // or 0 if not supporting unicode | |
43 | + | |
44 | + //! \brief Encode a Unicode codepoint to an output stream. | |
45 | + //! \param os Output stream. | |
46 | + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. | |
47 | + template<typename OutputStream> | |
48 | + static void Encode(OutputStream& os, unsigned codepoint); | |
49 | + | |
50 | + //! \brief Decode a Unicode codepoint from an input stream. | |
51 | + //! \param is Input stream. | |
52 | + //! \param codepoint Output of the unicode codepoint. | |
53 | + //! \return true if a valid codepoint can be decoded from the stream. | |
54 | + template <typename InputStream> | |
55 | + static bool Decode(InputStream& is, unsigned* codepoint); | |
56 | + | |
57 | + //! \brief Validate one Unicode codepoint from an encoded stream. | |
58 | + //! \param is Input stream to obtain codepoint. | |
59 | + //! \param os Output for copying one codepoint. | |
60 | + //! \return true if it is valid. | |
61 | + //! \note This function just validating and copying the codepoint without actually decode it. | |
62 | + template <typename InputStream, typename OutputStream> | |
63 | + static bool Validate(InputStream& is, OutputStream& os); | |
64 | + | |
65 | + // The following functions are deal with byte streams. | |
66 | + | |
67 | + //! Take a character from input byte stream, skip BOM if exist. | |
68 | + template <typename InputByteStream> | |
69 | + static CharType TakeBOM(InputByteStream& is); | |
70 | + | |
71 | + //! Take a character from input byte stream. | |
72 | + template <typename InputByteStream> | |
73 | + static Ch Take(InputByteStream& is); | |
74 | + | |
75 | + //! Put BOM to output byte stream. | |
76 | + template <typename OutputByteStream> | |
77 | + static void PutBOM(OutputByteStream& os); | |
78 | + | |
79 | + //! Put a character to output byte stream. | |
80 | + template <typename OutputByteStream> | |
81 | + static void Put(OutputByteStream& os, Ch c); | |
82 | +}; | |
83 | +\endcode | |
84 | +*/ | |
85 | + | |
86 | +/////////////////////////////////////////////////////////////////////////////// | |
87 | +// UTF8 | |
88 | + | |
89 | +//! UTF-8 encoding. | |
90 | +/*! http://en.wikipedia.org/wiki/UTF-8 | |
91 | + http://tools.ietf.org/html/rfc3629 | |
92 | + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. | |
93 | + \note implements Encoding concept | |
94 | +*/ | |
95 | +template<typename CharType = char> | |
96 | +struct UTF8 { | |
97 | + typedef CharType Ch; | |
98 | + | |
99 | + enum { supportUnicode = 1 }; | |
100 | + | |
101 | + template<typename OutputStream> | |
102 | + static void Encode(OutputStream& os, unsigned codepoint) { | |
103 | + if (codepoint <= 0x7F) | |
104 | + os.Put(static_cast<Ch>(codepoint & 0xFF)); | |
105 | + else if (codepoint <= 0x7FF) { | |
106 | + os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF))); | |
107 | + os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F)))); | |
108 | + } | |
109 | + else if (codepoint <= 0xFFFF) { | |
110 | + os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF))); | |
111 | + os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); | |
112 | + os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F))); | |
113 | + } | |
114 | + else { | |
115 | + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); | |
116 | + os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF))); | |
117 | + os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F))); | |
118 | + os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); | |
119 | + os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F))); | |
120 | + } | |
121 | + } | |
122 | + | |
123 | + template<typename OutputStream> | |
124 | + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { | |
125 | + if (codepoint <= 0x7F) | |
126 | + PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF)); | |
127 | + else if (codepoint <= 0x7FF) { | |
128 | + PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF))); | |
129 | + PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F)))); | |
130 | + } | |
131 | + else if (codepoint <= 0xFFFF) { | |
132 | + PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF))); | |
133 | + PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); | |
134 | + PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F))); | |
135 | + } | |
136 | + else { | |
137 | + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); | |
138 | + PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF))); | |
139 | + PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F))); | |
140 | + PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); | |
141 | + PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F))); | |
142 | + } | |
143 | + } | |
144 | + | |
145 | + template <typename InputStream> | |
146 | + static bool Decode(InputStream& is, unsigned* codepoint) { | |
147 | +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu) | |
148 | +#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0) | |
149 | +#define TAIL() COPY(); TRANS(0x70) | |
150 | + typename InputStream::Ch c = is.Take(); | |
151 | + if (!(c & 0x80)) { | |
152 | + *codepoint = static_cast<unsigned char>(c); | |
153 | + return true; | |
154 | + } | |
155 | + | |
156 | + unsigned char type = GetRange(static_cast<unsigned char>(c)); | |
157 | + if (type >= 32) { | |
158 | + *codepoint = 0; | |
159 | + } else { | |
160 | + *codepoint = (0xFF >> type) & static_cast<unsigned char>(c); | |
161 | + } | |
162 | + bool result = true; | |
163 | + switch (type) { | |
164 | + case 2: TAIL(); return result; | |
165 | + case 3: TAIL(); TAIL(); return result; | |
166 | + case 4: COPY(); TRANS(0x50); TAIL(); return result; | |
167 | + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; | |
168 | + case 6: TAIL(); TAIL(); TAIL(); return result; | |
169 | + case 10: COPY(); TRANS(0x20); TAIL(); return result; | |
170 | + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; | |
171 | + default: return false; | |
172 | + } | |
173 | +#undef COPY | |
174 | +#undef TRANS | |
175 | +#undef TAIL | |
176 | + } | |
177 | + | |
178 | + template <typename InputStream, typename OutputStream> | |
179 | + static bool Validate(InputStream& is, OutputStream& os) { | |
180 | +#define COPY() os.Put(c = is.Take()) | |
181 | +#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0) | |
182 | +#define TAIL() COPY(); TRANS(0x70) | |
183 | + Ch c; | |
184 | + COPY(); | |
185 | + if (!(c & 0x80)) | |
186 | + return true; | |
187 | + | |
188 | + bool result = true; | |
189 | + switch (GetRange(static_cast<unsigned char>(c))) { | |
190 | + case 2: TAIL(); return result; | |
191 | + case 3: TAIL(); TAIL(); return result; | |
192 | + case 4: COPY(); TRANS(0x50); TAIL(); return result; | |
193 | + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; | |
194 | + case 6: TAIL(); TAIL(); TAIL(); return result; | |
195 | + case 10: COPY(); TRANS(0x20); TAIL(); return result; | |
196 | + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; | |
197 | + default: return false; | |
198 | + } | |
199 | +#undef COPY | |
200 | +#undef TRANS | |
201 | +#undef TAIL | |
202 | + } | |
203 | + | |
204 | + static unsigned char GetRange(unsigned char c) { | |
205 | + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ | |
206 | + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. | |
207 | + static const unsigned char type[] = { | |
208 | + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | |
209 | + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | |
210 | + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | |
211 | + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | |
212 | + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, | |
213 | + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, | |
214 | + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, | |
215 | + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, | |
216 | + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, | |
217 | + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, | |
218 | + }; | |
219 | + return type[c]; | |
220 | + } | |
221 | + | |
222 | + template <typename InputByteStream> | |
223 | + static CharType TakeBOM(InputByteStream& is) { | |
224 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | |
225 | + typename InputByteStream::Ch c = Take(is); | |
226 | + if (static_cast<unsigned char>(c) != 0xEFu) return c; | |
227 | + c = is.Take(); | |
228 | + if (static_cast<unsigned char>(c) != 0xBBu) return c; | |
229 | + c = is.Take(); | |
230 | + if (static_cast<unsigned char>(c) != 0xBFu) return c; | |
231 | + c = is.Take(); | |
232 | + return c; | |
233 | + } | |
234 | + | |
235 | + template <typename InputByteStream> | |
236 | + static Ch Take(InputByteStream& is) { | |
237 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | |
238 | + return static_cast<Ch>(is.Take()); | |
239 | + } | |
240 | + | |
241 | + template <typename OutputByteStream> | |
242 | + static void PutBOM(OutputByteStream& os) { | |
243 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | |
244 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xEFu)); | |
245 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xBBu)); | |
246 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xBFu)); | |
247 | + } | |
248 | + | |
249 | + template <typename OutputByteStream> | |
250 | + static void Put(OutputByteStream& os, Ch c) { | |
251 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | |
252 | + os.Put(static_cast<typename OutputByteStream::Ch>(c)); | |
253 | + } | |
254 | +}; | |
255 | + | |
256 | +/////////////////////////////////////////////////////////////////////////////// | |
257 | +// UTF16 | |
258 | + | |
259 | +//! UTF-16 encoding. | |
260 | +/*! http://en.wikipedia.org/wiki/UTF-16 | |
261 | + http://tools.ietf.org/html/rfc2781 | |
262 | + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. | |
263 | + \note implements Encoding concept | |
264 | + | |
265 | + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. | |
266 | + For streaming, use UTF16LE and UTF16BE, which handle endianness. | |
267 | +*/ | |
268 | +template<typename CharType = wchar_t> | |
269 | +struct UTF16 { | |
270 | + typedef CharType Ch; | |
271 | + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); | |
272 | + | |
273 | + enum { supportUnicode = 1 }; | |
274 | + | |
275 | + template<typename OutputStream> | |
276 | + static void Encode(OutputStream& os, unsigned codepoint) { | |
277 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); | |
278 | + if (codepoint <= 0xFFFF) { | |
279 | + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair | |
280 | + os.Put(static_cast<typename OutputStream::Ch>(codepoint)); | |
281 | + } | |
282 | + else { | |
283 | + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); | |
284 | + unsigned v = codepoint - 0x10000; | |
285 | + os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800)); | |
286 | + os.Put((v & 0x3FF) | 0xDC00); | |
287 | + } | |
288 | + } | |
289 | + | |
290 | + | |
291 | + template<typename OutputStream> | |
292 | + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { | |
293 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); | |
294 | + if (codepoint <= 0xFFFF) { | |
295 | + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair | |
296 | + PutUnsafe(os, static_cast<typename OutputStream::Ch>(codepoint)); | |
297 | + } | |
298 | + else { | |
299 | + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); | |
300 | + unsigned v = codepoint - 0x10000; | |
301 | + PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800)); | |
302 | + PutUnsafe(os, (v & 0x3FF) | 0xDC00); | |
303 | + } | |
304 | + } | |
305 | + | |
306 | + template <typename InputStream> | |
307 | + static bool Decode(InputStream& is, unsigned* codepoint) { | |
308 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); | |
309 | + typename InputStream::Ch c = is.Take(); | |
310 | + if (c < 0xD800 || c > 0xDFFF) { | |
311 | + *codepoint = static_cast<unsigned>(c); | |
312 | + return true; | |
313 | + } | |
314 | + else if (c <= 0xDBFF) { | |
315 | + *codepoint = (static_cast<unsigned>(c) & 0x3FF) << 10; | |
316 | + c = is.Take(); | |
317 | + *codepoint |= (static_cast<unsigned>(c) & 0x3FF); | |
318 | + *codepoint += 0x10000; | |
319 | + return c >= 0xDC00 && c <= 0xDFFF; | |
320 | + } | |
321 | + return false; | |
322 | + } | |
323 | + | |
324 | + template <typename InputStream, typename OutputStream> | |
325 | + static bool Validate(InputStream& is, OutputStream& os) { | |
326 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); | |
327 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); | |
328 | + typename InputStream::Ch c; | |
329 | + os.Put(static_cast<typename OutputStream::Ch>(c = is.Take())); | |
330 | + if (c < 0xD800 || c > 0xDFFF) | |
331 | + return true; | |
332 | + else if (c <= 0xDBFF) { | |
333 | + os.Put(c = is.Take()); | |
334 | + return c >= 0xDC00 && c <= 0xDFFF; | |
335 | + } | |
336 | + return false; | |
337 | + } | |
338 | +}; | |
339 | + | |
340 | +//! UTF-16 little endian encoding. | |
341 | +template<typename CharType = wchar_t> | |
342 | +struct UTF16LE : UTF16<CharType> { | |
343 | + template <typename InputByteStream> | |
344 | + static CharType TakeBOM(InputByteStream& is) { | |
345 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | |
346 | + CharType c = Take(is); | |
347 | + return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c; | |
348 | + } | |
349 | + | |
350 | + template <typename InputByteStream> | |
351 | + static CharType Take(InputByteStream& is) { | |
352 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | |
353 | + unsigned c = static_cast<uint8_t>(is.Take()); | |
354 | + c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8; | |
355 | + return static_cast<CharType>(c); | |
356 | + } | |
357 | + | |
358 | + template <typename OutputByteStream> | |
359 | + static void PutBOM(OutputByteStream& os) { | |
360 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | |
361 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu)); | |
362 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu)); | |
363 | + } | |
364 | + | |
365 | + template <typename OutputByteStream> | |
366 | + static void Put(OutputByteStream& os, CharType c) { | |
367 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | |
368 | + os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu)); | |
369 | + os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu)); | |
370 | + } | |
371 | +}; | |
372 | + | |
373 | +//! UTF-16 big endian encoding. | |
374 | +template<typename CharType = wchar_t> | |
375 | +struct UTF16BE : UTF16<CharType> { | |
376 | + template <typename InputByteStream> | |
377 | + static CharType TakeBOM(InputByteStream& is) { | |
378 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | |
379 | + CharType c = Take(is); | |
380 | + return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c; | |
381 | + } | |
382 | + | |
383 | + template <typename InputByteStream> | |
384 | + static CharType Take(InputByteStream& is) { | |
385 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | |
386 | + unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8; | |
387 | + c |= static_cast<uint8_t>(is.Take()); | |
388 | + return static_cast<CharType>(c); | |
389 | + } | |
390 | + | |
391 | + template <typename OutputByteStream> | |
392 | + static void PutBOM(OutputByteStream& os) { | |
393 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | |
394 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu)); | |
395 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu)); | |
396 | + } | |
397 | + | |
398 | + template <typename OutputByteStream> | |
399 | + static void Put(OutputByteStream& os, CharType c) { | |
400 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | |
401 | + os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu)); | |
402 | + os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu)); | |
403 | + } | |
404 | +}; | |
405 | + | |
406 | +/////////////////////////////////////////////////////////////////////////////// | |
407 | +// UTF32 | |
408 | + | |
409 | +//! UTF-32 encoding. | |
410 | +/*! http://en.wikipedia.org/wiki/UTF-32 | |
411 | + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. | |
412 | + \note implements Encoding concept | |
413 | + | |
414 | + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. | |
415 | + For streaming, use UTF32LE and UTF32BE, which handle endianness. | |
416 | +*/ | |
417 | +template<typename CharType = unsigned> | |
418 | +struct UTF32 { | |
419 | + typedef CharType Ch; | |
420 | + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); | |
421 | + | |
422 | + enum { supportUnicode = 1 }; | |
423 | + | |
424 | + template<typename OutputStream> | |
425 | + static void Encode(OutputStream& os, unsigned codepoint) { | |
426 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); | |
427 | + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); | |
428 | + os.Put(codepoint); | |
429 | + } | |
430 | + | |
431 | + template<typename OutputStream> | |
432 | + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { | |
433 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); | |
434 | + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); | |
435 | + PutUnsafe(os, codepoint); | |
436 | + } | |
437 | + | |
438 | + template <typename InputStream> | |
439 | + static bool Decode(InputStream& is, unsigned* codepoint) { | |
440 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); | |
441 | + Ch c = is.Take(); | |
442 | + *codepoint = c; | |
443 | + return c <= 0x10FFFF; | |
444 | + } | |
445 | + | |
446 | + template <typename InputStream, typename OutputStream> | |
447 | + static bool Validate(InputStream& is, OutputStream& os) { | |
448 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); | |
449 | + Ch c; | |
450 | + os.Put(c = is.Take()); | |
451 | + return c <= 0x10FFFF; | |
452 | + } | |
453 | +}; | |
454 | + | |
455 | +//! UTF-32 little endian enocoding. | |
456 | +template<typename CharType = unsigned> | |
457 | +struct UTF32LE : UTF32<CharType> { | |
458 | + template <typename InputByteStream> | |
459 | + static CharType TakeBOM(InputByteStream& is) { | |
460 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | |