Commit 2e10296e10ee6f66a4e58808b0e2af0e1ebd269f

Authored by DarkGod
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

Too many changes to show.

To preserve performance only 31 of 31+ files are displayed.

... ... @@ -66,6 +66,11 @@ newoption {
66 66 }
67 67
68 68 newoption {
  69 + trigger = "discord",
  70 + description = "Discord integration"
  71 +}
  72 +
  73 +newoption {
69 74 trigger = "web-awesomium",
70 75 description = "Use awesomium embedded browser as the webcore"
71 76 }
... ...
... ... @@ -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)))
... ...
... ... @@ -205,6 +205,8 @@ function _M:onEnterLevel(zone, level)
205 205 end
206 206
207 207 self:fireTalentCheck("callbackOnChangeLevel", "enter", zone, level)
  208 +
  209 + game:updateCurrentChar()
208 210 end
209 211
210 212 function _M:onEnterLevelEnd(zone, level)
... ...
... ... @@ -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
... ...
  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.
... ...
  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