/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. // Note: The utilities in this file should typically not be used directly, // consider including UnicodeFunctions.h or UnicodeIterator.h instead. // // (At least) the following string types can be bound with these helper functions: // Types Input Output Null-Terminator // CryStringT, (::string, ::wstring): yes yes implied by type (also Stack and Fixed variants) // std::basic_string, std::string, std::wstring: yes yes implied by type // QString: yes yes implied by type // std::vector, std::list, std::deque: yes yes not present // T[] (fixed-length buffer): yes yes guaranteed to be emitted on output, accepted on input // T * and size_t (user-specified-size buffer): no yes guaranteed to be emitted on output // const T * (null-terminated string): yes no expected // const T[] (literal): yes no implied as the last item in the array // pair of iterators over T: yes no should not be included in the range // uint32 (single UCS code-point): yes no not present // If some other string type is not listed, you can still use it for input easily by passing begin/end iterators. // Note: For all types, T can be any 8-bit, 16-bit or 32-bit integral or character type. // Further T types may be processed by explicitly passing InputEncoding and OutputEncoding. // We never actively tested such scenario's, so no guarantees on floating and user-defined types as code-units. #pragma once #ifndef assert // Some tools use CRT's assert, most engine and game modules use CryAssert.h (via platform.h maybe). // We don't want to force a choice upon all code that uses Unicode utilities, so we just assume assert is defined. #error This header uses assert macro, please provide an applicable definition before including UnicodeXXX.h #endif #include "UnicodeEncoding.h" #include // For str(n)len and memcpy. #include // For wcs(n)len. #include // For size_t and ptrdiff_t. #include // For std::iterator_traits. #include // For std::basic_string. #include // For std::vector. #include // For std::list. #include // For std::deque. #include // ... standard type-traits (as of C++11). #if defined(AZ_RESTRICTED_PLATFORM) #undef AZ_RESTRICTED_SECTION #define UNICODEBINDING_H_SECTION_1 1 #define UNICODEBINDING_H_SECTION_2 2 #endif // Forward declare the supported types. // Before actually instantiating a binding however, you need to have the full definition included. // Also, this allows us to work with QChar/QString as declared names without a dependency on Qt. template class CryStackStringT; template class CryFixedStringT; template class CryFixedWStringT; template class CryStringLocalT; template class CryStringT; class QChar; class QString; namespace Unicode { namespace Detail { // Import standard type traits. // This requires C++11 compiler support. using std::add_const; using std::conditional; using std::extent; using std::integral_constant; using std::is_arithmetic; using std::is_array; using std::is_base_of; using std::is_const; using std::is_convertible; using std::is_integral; using std::is_pointer; using std::is_same; using std::make_unsigned; using std::remove_cv; using std::remove_extent; using std::remove_pointer; // SVoid: // Result type will be void if T is well-formed. // Note: This is mostly used to test the presence of member types at compile-time. template struct SVoid { typedef void type; }; // SValidChar: // Determine if T is a valid character type in the given compile-time context. // The InferEncoding flag is set if the encoding has to be detected automatically. // The Input flag is set if the type is used for input (and not set if the type is used for output). template struct SValidChar { typedef typename remove_cv::type BaseType; static const bool isArithmeticType = is_arithmetic::value; static const bool isQChar = is_same::value; static const bool isUsable = isArithmeticType || isQChar; static const bool isValidQualified = !is_const::value || Input; static const bool isKnownSize = sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4; static const bool isValidInferred = isKnownSize || !InferEncoding; static const bool value = isUsable && isValidQualified && isValidInferred; }; // SPackedIterators: // A pair of iterators over some range. // Note: Packing iterators into a single object allows us to pass them as a single argument like all other types. template struct SPackedIterators { const T begin, end; SPackedIterators(const T& _begin, const T& _end) : begin(_begin) , end(_end) {} }; // SPackedBuffer: // A buffer-pointer/length tuple. // Note: Packing them into a single object allows us to pass them as a single argument like all other types. template struct SPackedBuffer { T buffer; size_t size; SPackedBuffer(T _buffer, size_t _size) : buffer(_buffer) , size(_size) {} }; // SDependentType: // Makes the name of type T dependent on X (which is otherwise meaningless). // Note: This is used to force two-phase lookup so we don't need the definition of T until instantiation. // This way we can convince standards-compliant compilers Clang and GCC to not require definition of forward-declared types. // Specifically, we forward-declare Qt's QString and QChar, for which the definition will never be available outside Editor. template struct SDependentType { typedef T type; }; // EBind: // Methods of binding a type for input and/or output. // Note: These are used for tag-dispatch by binding functions, and are private to the implementation. enum EBind { // Input Output Description eBind_Impossible, // No No Can't bind this type. eBind_Iterators, // Yes Yes Bind by using begin() and end() member functions. eBind_Data, // Yes Yes Bind by using data() and size() member functions. eBind_Literal, // Yes No Bind a fixed size buffer (const element, aka string literal). eBind_Buffer, // Yes No Bind a fixed size buffer (non-const element) that may be null-terminated. eBind_PackedBuffer, // No Yes Bind a user-specified size buffer (non-const element). eBind_NullTerminated, // Yes No Bind a null-terminated buffer of unknown length (C string). eBind_CodePoint, // Yes No Bind a single code-point value. }; // SBindIterator: // Find the EBind for input from iterator pair of type T at compile-time. // If the type is not supported, the resulting value will be eBind_Impossible template struct SBindIterator { typedef const void CharType; static const EBind value = eBind_Impossible; }; template struct SBindIterator { typedef typename add_const::type CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Iterators : eBind_Impossible; }; template struct SBindIterator::type, typename SVoid::type > { typedef typename add_const::type CharType; typedef typename T::iterator_category IteratorCategory; static const bool isInputIterator = is_base_of::value; static const bool isValid = SValidChar::value; static const EBind value = isValid && isInputIterator ? eBind_Iterators : eBind_Impossible; }; // SBindObject: // Find the EBind for input from object of type T at compile-time. // If the type is not supported, the resulting value will be eBind_Impossible. template struct SBindObject { typedef typename add_const< typename conditional< is_array::value, typename remove_extent::type, typename remove_pointer::type >::type >::type CharType; static const size_t FixedSize = extent::value; COMPILE_TIME_ASSERT(!is_array::value || FixedSize > 0); static const bool isConstArray = is_array::value && is_const::type>::value; static const bool isBufferArray = is_array::value && !isConstArray; static const bool isPointer = is_pointer::value; static const bool isCodePoint = is_integral::value; static const bool isValidChar = SValidChar::value; static const EBind value = !isValidChar ? eBind_Impossible : isConstArray ? eBind_Literal : isBufferArray ? eBind_Buffer : isPointer ? eBind_NullTerminated : isCodePoint ? eBind_CodePoint : eBind_Impossible; }; template struct SBindObject, InferEncoding> { typedef typename add_const::type CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Data : eBind_Impossible; }; template struct SBindObject, InferEncoding> { typedef typename add_const::type CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Data : eBind_Impossible; }; template struct SBindObject, InferEncoding> { typedef typename add_const::type CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Iterators : eBind_Impossible; }; template struct SBindObject, InferEncoding> { typedef typename add_const::type CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Iterators : eBind_Impossible; }; template struct SBindObject, InferEncoding> { typedef typename add_const::type CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Data : eBind_Impossible; }; template struct SBindObject, InferEncoding> { typedef typename add_const::type CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Data : eBind_Impossible; }; template struct SBindObject, InferEncoding> { typedef typename add_const::type CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Data : eBind_Impossible; }; template struct SBindObject, InferEncoding> { typedef char CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Data : eBind_Impossible; }; template struct SBindObject, InferEncoding> { typedef wchar_t CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Data : eBind_Impossible; }; template struct SBindObject { typedef const QChar CharType; static const EBind value = eBind_Data; }; template struct SBindObject, InferEncoding> { typedef typename SBindIterator::CharType CharType; static const EBind value = eBind_Iterators; }; // SBindOutput: // Find the EBind for output to object of type T at compile-time. // If the type is not supported, the resulting value will be eBind_Impossible. template struct SBindOutput { typedef typename remove_extent::type CharType; static const size_t FixedSize = extent::value; static const bool isArray = is_array::value; static const bool isValid = SValidChar::type, InferEncoding, false>::value; static const EBind value = isArray && isValid ? eBind_Buffer : eBind_Impossible; }; template struct SBindOutput, InferEncoding> { typedef OutputCharType CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_PackedBuffer : eBind_Impossible; }; template struct SBindOutput, InferEncoding> { typedef CharT CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Data : eBind_Impossible; }; template struct SBindOutput, InferEncoding> { typedef T CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Data : eBind_Impossible; }; template struct SBindOutput, InferEncoding> { typedef T CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Iterators : eBind_Impossible; }; template struct SBindOutput, InferEncoding> { typedef T CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Iterators : eBind_Impossible; }; template struct SBindOutput, InferEncoding> { typedef T CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Data : eBind_Impossible; }; template struct SBindOutput, InferEncoding> { typedef T CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Data : eBind_Impossible; }; template struct SBindOutput, InferEncoding> { typedef T CharType; static const bool isValid = SValidChar::value; static const EBind value = isValid ? eBind_Data : eBind_Impossible; }; template struct SBindOutput { typedef QChar CharType; static const EBind value = eBind_Data; }; // SInferEncoding: // Infers the encoding of the given character type. // Note: This will always pick an UTF encoding type based on the size of the element type. template struct SInferEncoding { typedef SBindObject ObjectType; typedef SBindIterator IteratorType; typedef typename conditional< IteratorType::value != eBind_Impossible, typename IteratorType::CharType, typename ObjectType::CharType >::type CharType; static const EEncoding value = sizeof(CharType) == 1 ? eEncoding_UTF8 : sizeof(CharType) == 2 ? eEncoding_UTF16 : eEncoding_UTF32; COMPILE_TIME_ASSERT(value != eEncoding_UTF32 || sizeof(CharType) == 4); }; // SBindCharacter: // Pick the base character type to use during input or output with this element type. template::value, bool IsQChar = is_same::type>::value> struct SBindCharacter { typedef typename make_unsigned::type BaseType; // The standard doesn't define if a character type is signed or unsigned. typedef typename remove_cv::type UnqualifiedType; typedef typename conditional::type type; }; template struct SBindCharacter { COMPILE_TIME_ASSERT(is_arithmetic::value); typedef typename remove_cv::type UnqualifiedType; typedef typename conditional::type type; }; template struct SBindCharacter { typedef typename conditional::type type; typedef typename SDependentType::type ActuallyQChar; // Force two-phase name lookup on QChar. COMPILE_TIME_ASSERT(sizeof(ActuallyQChar) == sizeof(type)); // In case Qt ever changes QChar. }; // SBindPointer: // Pick the pointer type to use during input or output with buffers (potentially inside string types). template struct SBindPointer { COMPILE_TIME_ASSERT(is_pointer::value || is_array::value); typedef typename conditional< is_pointer::value, typename remove_pointer::type, typename remove_extent::type >::type UnboundCharType; typedef typename SBindCharacter::type BoundCharType; typedef BoundCharType* type; }; // SAutomaticallyDeduced: // Placeholder type that is never defined, used by SRequire for SFINAE overloading. struct SAutomaticallyDeduced; // SRequire: // Helper for SFINAE overloading. // Similar to C++11's std::enable_if, which is not in boost (with that exact name anyway). template struct SRequire { typedef T type; }; template struct SRequire {}; // SafeCast: // Cast a pointer to type T, but only allowing safe casts. // This guards against bad code in other functions since it prevents unintended casts. template inline T SafeCast(SourceChar* ptr, typename SRequire::value>::type* = 0) { // Allow casts from pointer-to-integral to unrelated pointer-to-integral, provided they are of the same size. typedef typename remove_pointer::type TargetChar; COMPILE_TIME_ASSERT(is_integral::value && is_integral::value); COMPILE_TIME_ASSERT(sizeof(SourceChar) == sizeof(TargetChar)); return reinterpret_cast(ptr); } template inline T SafeCast(SourceChar* ptr, typename SRequire::type, QChar>::value>::type* = 0) { // Allow casts from pointer-to-QChar to unrelated pointer-to-integral, provided they are of the same size. typedef typename remove_pointer::type TargetChar; COMPILE_TIME_ASSERT(is_integral::value); COMPILE_TIME_ASSERT(sizeof(SourceChar) == sizeof(TargetChar)); return reinterpret_cast(ptr); } template inline T SafeCast(SourceChar* ptr, typename SRequire::value&& !is_same::type, QChar>::value>::type* = 0) { // Any other casts that are allowed by C++. return static_cast(ptr); } // SCharacterTrait: // Exposes some basic traits for a given character. // Note: Map to (hopefully optimized) CRT functions where possible. template::value> struct SCharacterTrait { static size_t StrLen(const T* nts) // Fall-back strlen. { size_t result = 0; while (*nts != 0) { ++nts; ++result; } return result; } static size_t StrNLen(const T* ptr, size_t len) // Fall-back strnlen. { size_t result = 0; while (*ptr != 0 && result != len) { ++ptr; ++result; } return result; } }; template struct SCharacterTrait { static size_t StrLen(const T* nts) // Narrow CRT strlen. { return ::strlen(SafeCast(nts)); } static size_t StrNLen(const T* ptr, size_t len) // Narrow CRT strnlen. { return ::strnlen(SafeCast(ptr), len); } }; template struct SCharacterTrait { static size_t StrLen(const T* nts) // Wide CRT strlen. { return ::wcslen(SafeCast(nts)); } static size_t StrNLen(const T* ptr, size_t len) // Wide CRT strnlen. { #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION UNICODEBINDING_H_SECTION_1 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/UnicodeBinding_h_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/UnicodeBinding_h_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/UnicodeBinding_h_salem.inl" #endif #endif return ::wcsnlen(SafeCast(ptr), len); #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION UNICODEBINDING_H_SECTION_2 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/UnicodeBinding_h_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/UnicodeBinding_h_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/UnicodeBinding_h_salem.inl" #endif #endif } }; // void Feed(const SPackedIterators &its, Sink &out, tag): // Feeds the provided sink from provided packed iterator-range. template inline void Feed(const SPackedIterators& its, Sink& out, integral_constant) { typedef typename std::iterator_traits::value_type UnboundCharType; typedef typename SBindCharacter::type BoundCharType; for (InputIteratorType it = its.begin; it != its.end; ++it) { const UnboundCharType unbound = *it; const BoundCharType bound = static_cast(unbound); const uint32 item = static_cast(bound); out(item); } } // void Feed(const SPackedIterators &its, Sink &out, tag): // Feeds the provided sink from provided packed pointer-range. // This is slightly better code-generation than using generic iterators. template inline void Feed(const SPackedIterators& its, Sink& out, integral_constant) { typedef typename SBindPointer::type PointerType; assert(reinterpret_cast(its.begin) <= reinterpret_cast(its.end) && "Invalid range specified"); const size_t length = its.end - its.begin; PointerType ptr = SafeCast(its.begin); assert((ptr || !length) && "Passed a non-empty range containing a null-pointer"); for (size_t i = 0; i < length; ++i, ++ptr) { const uint32 item = static_cast(*ptr); out(item); } } // void Feed(const InputStringType &in, Sink &out, tag): // Feeds the provided sink from a container, using it's iterators. // Note: Dispatches to one of the packed-range overloads. template inline void Feed(const InputStringType& in, Sink& out, integral_constant tag) { typedef typename InputStringType::const_iterator IteratorType; Detail::SPackedIterators its(in.begin(), in.end()); Feed(its, out, tag); } // void Feed(const InputStringType &in, Sink &out, tag): // Feeds the provided sink from a string-object's buffer. template inline void Feed(const InputStringType& in, Sink& out, integral_constant) { typedef typename InputStringType::size_type SizeType; typedef typename InputStringType::value_type ValueType; typedef typename SBindPointer::type PointerType; const SizeType length = in.size(); if (length) { PointerType ptr = SafeCast(in.data()); for (SizeType i = 0; i < length; ++i, ++ptr) { const uint32 item = static_cast(*ptr); out(item); } } } // void Feed(const InputStringType &in, Sink &out, tag): // Feeds the provided sink from a string-literal. // Note: The literal is assumed to be null-terminated. // It's possible that a const-element fixed-size-buffer is mistaken as a literal. // However, we expect no-one uses such buffers that are not null-terminated already. // If somehow this use-case is desired, either terminate the buffer, or remove const from the buffer, or pass iterators. template inline void Feed(const InputStringType& in, Sink& out, integral_constant) { COMPILE_TIME_ASSERT(is_array::value && extent::value > 0); typedef typename SBindPointer::type PointerType; const size_t length = extent::value - 1; PointerType ptr = SafeCast(in); assert(ptr[length] == 0 && "Literal is not null-terminated"); for (size_t i = 0; i < length; ++i, ++ptr) { const uint32 item = static_cast(*ptr); out(item); } } // void Feed(const InputStringType &in, Sink &out, tag): // Feeds the provided sink from a non-const-element fixed-size buffer. // Note: The buffer is allowed to be null-terminated, but it's not required. template inline void Feed(const InputStringType& in, Sink& out, integral_constant) { COMPILE_TIME_ASSERT(is_array::value && extent::value > 0); typedef typename SBindPointer::type PointerType; typedef typename SBindPointer::BoundCharType CharType; const size_t length = extent::value; PointerType ptr = SafeCast(in); for (size_t i = 0; i < length; ++i, ++ptr) { const CharType unbound = *ptr; if (unbound == 0) { break; } const uint32 item = static_cast(unbound); out(item); } } // void Feed(const InputStringType &in, Sink &out, tag): // Feeds the provided sink from a null-terminated C-style string. template inline void Feed(const InputStringType& in, Sink& out, integral_constant) { COMPILE_TIME_ASSERT(is_pointer::value); typedef typename SBindPointer::type PointerType; typedef typename SBindPointer::BoundCharType CharType; PointerType ptr = SafeCast(in); if (ptr) { while (true) { const CharType unbound = *ptr; ++ptr; if (unbound == 0) { break; } const uint32 item = static_cast(unbound); out(item); } } } // void Feed(const InputCharType &in, Sink &out, tag): // Feeds the provided sink from a single value (interpreted as an UCS code-point). template inline void Feed(const InputCharType& in, Sink& out, integral_constant) { COMPILE_TIME_ASSERT(is_arithmetic::value); const uint32 item = static_cast(in); out(item); } // size_t EncodedLength(const SPackedIterators &its, tag): // Determines the length of the input sequence in a range of iterators. template inline size_t EncodedLength(const SPackedIterators& its, integral_constant) { return std::distance(its.begin, its.end); // std::distance will pick optimal implementation depending on iterator category. } // size_t EncodedLength(const InputStringType &in, tag): // Determines the length of an input container, which would otherwise be enumerated with iterators. template inline size_t EncodedLength(const InputStringType& in, integral_constant) { return in.size(); // Can there be a container without size()? At the very least, not in the supported types. } // size_t EncodedLength(const InputStringType &in, tag): // Determines the length of the input container. The container uses contiguous element layout. template inline size_t EncodedLength(const InputStringType& in, integral_constant) { return in.size(); } // size_t EncodedLength(const InputStringType &in, tag): // Determines the length of the input string-literal. This is a compile-time constant. template inline size_t EncodedLength(const InputStringType& in, integral_constant) { COMPILE_TIME_ASSERT(is_array::value && extent::value > 0); return extent::value - 1; } // size_t EncodedLength(const InputStringType &in, tag): // Determines the length of the input fixed-size-buffer. We look for an (optional) null-terminator in the buffer. template inline size_t EncodedLength(const InputStringType& in, integral_constant) { COMPILE_TIME_ASSERT(is_array::value && extent::value > 0); typedef typename remove_extent::type CharType; return SCharacterTrait::StrNLen(in, extent::value); } // size_t EncodedLength(const InputStringType &in, tag): // Determines the length of the input used-specified buffer. We look for an (optional) null-terminator in the buffer. template inline size_t EncodedLength(const SPackedBuffer& in, integral_constant) { return in.buffer ? SCharacterTrait::StrNLen(in.buffer, in.size) : 0; } // size_t EncodedLength(const InputStringType &in, tag): // Determines the length of the input null-terminated c-style string. We just use strlen() if available. template inline size_t EncodedLength(const InputStringType& in, integral_constant) { COMPILE_TIME_ASSERT(is_pointer::value); typedef typename remove_pointer::type CharType; return in ? SCharacterTrait::StrLen(in) : 0; } // size_t EncodedLength(const InputCharType &in, tag): // Determines the length of a single UCS code-point. This is always 1. template inline size_t EncodedLength(const InputCharType& in, integral_constant) { COMPILE_TIME_ASSERT(is_arithmetic::value); return 1; } // const void *EncodedPointer(const SPackedIterators &its, tag): // Get a pointer to contiguous storage for an iterator range. // Note: This can only work if the iterators are pointers, or the storage won't be guaranteed contiguous. template inline const void* EncodedPointer(const SPackedIterators& its, integral_constant) { return its.begin; } // const void *EncodedPointer(const InputStringType &in, tag): // Get a pointer to contiguous storage for string/vector object. // Note: This can only work for containers that actually use contiguous storage, which is determined by the SBindXXX helpers. template inline const void* EncodedPointer(const InputStringType& in, integral_constant) { return in.data(); } // const void *EncodedPointer(const InputStringType &in, tag): // Get a pointer to contiguous storage for a string-literal. template inline const void* EncodedPointer(const InputStringType& in, integral_constant) { COMPILE_TIME_ASSERT(is_array::value && extent::value > 0); return in; // We can just let the array type decay to a pointer. } // const void *EncodedPointer(const InputStringType &in, tag): // Get a pointer to contiguous storage for a fixed-size-buffer. template inline const void* EncodedPointer(const InputStringType& in, integral_constant) { COMPILE_TIME_ASSERT(is_array::value && extent::value > 0); return in; // We can just let the array type decay to a pointer. } // const void *EncodedPointer(const InputStringType &in, tag): // Get a pointer to contiguous storage for a null-terminated c-style-string. template inline const void* EncodedPointer(const InputStringType& in, integral_constant) { COMPILE_TIME_ASSERT(is_pointer::value); return in; // Implied } // const void *EncodedPointer(const InputCharType &in, tag): // Get a pointer to contiguous storage for a single UCS code-point. template inline const void* EncodedPointer(const InputCharType& in, integral_constant) { COMPILE_TIME_ASSERT(is_arithmetic::value); return ∈ // Take the address of the parameter (which is kept on the stack of the caller). } // SWriteSink: // A helper that performs writing to the type T and can be passed as Sink type to a trans-coder helper. template struct SWriteSink; template struct SWriteSink { typedef typename T::value_type OutputCharType; T& out; SWriteSink(T& _out, size_t) : out(_out) { if (!Append) { // If not appending, clear the object beforehand. out.clear(); } } void operator()(uint32 item) { const OutputCharType bound = static_cast(item); out.push_back(bound); // We assume this can't fail and STL container takes care of memory. } void operator()(const void*, size_t); // Not implemented. void HintSequence(uint32 length) {} // Don't care about sequences. bool CanWrite() const { return true; } // Always writable }; template struct SWriteSink { typedef SBindPointer BindHelper; typedef typename BindHelper::UnboundCharType CharType; CharType* ptr; SWriteSink(T& out, size_t length) { const size_t offset = Append ? out.size() : 0; length += offset; out.resize(length); // resize() can't fail without exceptions, so assert instead. assert((out.size() == length) && "Buffer resize failed (out-of-memory?)"); const CharType* base = length ? out.data() : 0; ptr = const_cast(base + offset); } void operator()(uint32 item) { *SafeCast(ptr) = static_cast(item); ++ptr; } void operator()(const void* src, size_t length) { ::memcpy(ptr, src, length * sizeof(CharType)); ptr += length; } void HintSequence(uint32 length) {} // Don't care about sequences. bool CanWrite() const { return true; } // Always writable }; template struct SWriteSink, Append, eBind_PackedBuffer> { typedef typename remove_pointer

::type ElementType; typedef SBindPointer BindHelper; typedef typename BindHelper::UnboundCharType CharType; CharType* ptr; CharType* const terminator; SWriteSink(CharType* _terminator) : terminator(_terminator) {} SWriteSink(SPackedBuffer

& out, size_t) : terminator(out.size && out.buffer ? out.buffer + out.size - 1 : 0) { const size_t offset = Append ? EncodedLength(out, integral_constant()) : 0; const size_t fixedOffset = Append && offset >= out.size ? out.size - 1 // In case the buffer is already full and not terminated. : offset; CharType* base = static_cast(out.buffer); ptr = terminator ? base + fixedOffset : 0; } ~SWriteSink() { if (ptr) { *ptr = 0; // Guarantees that the output is null-terminated. } } void operator()(uint32 item) { if (ptr != terminator) // Guarantees we don't overflow the buffer. { *SafeCast(ptr) = static_cast(item); ++ptr; } } void operator()(const void* src, size_t length) { const size_t maxLength = terminator - ptr; if (length > maxLength) { length = maxLength; } ::memcpy(ptr, src, length * sizeof(CharType)); ptr += length; } void HintSequence(uint32 length) { if (terminator && (ptr + length >= terminator)) { // This sequence will overflow the buffer. // In this case, we prefer to not generate any part of the sequence. // Terminate at the current position and flag as full. *ptr = 0; ptr = terminator; } } bool CanWrite() const { return terminator != ptr; } }; template struct SWriteSink // Uses above implementation with specialized constructor : SWriteSink::type*>, Append, eBind_PackedBuffer> { typedef typename remove_extent::type ElementType; typedef SWriteSink, Append, eBind_PackedBuffer> Super; typedef SBindPointer BindHelper; typedef typename BindHelper::UnboundCharType CharType; SWriteSink(T& out, size_t) : Super(out + extent::value - 1) { const size_t offset = Append ? EncodedLength(out, integral_constant()) : 0; const size_t fixedOffset = Append && offset >= extent::value ? extent::value - 1 // In case the buffer is already full and not terminated. : offset; CharType* base = static_cast(out); Super::ptr = out + fixedOffset; // Qualification for Super required for two-phase lookup. } }; // SIsBlockCopyable: // Check if block-copy optimization is possible for these types. // InputType should be an instantiation of SBindObject or SBindIterator. // OutputType should be an instantiation of SBindOutput. // Note: This doesn't take into account safe/unsafe conversions, just if the underlying storage types are compatible. template struct SIsBlockCopyable { template struct SIsContiguous { static const bool value = M == eBind_Data || M == eBind_Literal || M == eBind_Buffer || M == eBind_PackedBuffer || M == eBind_NullTerminated || M == eBind_CodePoint; }; template struct SIsPointers { static const bool value = false; }; template struct SIsPointers > { static const bool value = true; }; typedef typename SBindCharacter::type InputCharType; typedef typename SBindCharacter::type OutputCharType; static const bool isIntegral = is_integral::value && is_integral::value; static const bool isSameSize = sizeof(InputCharType) == sizeof(OutputCharType); static const bool isInputContiguous = (SIsContiguous::value || SIsPointers::value); static const bool isOutputContiguous = (SIsContiguous::value || SIsPointers::value); static const bool value = isIntegral && isSameSize && isInputContiguous && isOutputContiguous; }; } }