Commit 2e10296e10ee6f66a4e58808b0e2af0e1ebd269f
1 parent
c0aa2bfb
Integrated with Discord's Rich Presence feature (for Windows and Linux only for …
…now as OSX would need raising OS requirement, which will happen anyway but later on), feature is fully inoperative if Discord is not being used or if disabled in the Online options
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(InputByteS |