You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
986 lines
47 KiB
C++
986 lines
47 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
|
|
// 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<T>, (::string, ::wstring): yes yes implied by type (also Stack and Fixed variants)
|
|
// std::basic_string<T>, std::string, std::wstring: yes yes implied by type
|
|
// QString: yes yes implied by type
|
|
// std::vector<T>, std::list<T>, std::deque<T>: 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 <string.h> // For str(n)len and memcpy.
|
|
#include <wchar.h> // For wcs(n)len.
|
|
#include <stddef.h> // For size_t and ptrdiff_t.
|
|
#include <iterator> // For std::iterator_traits.
|
|
#include <string> // For std::basic_string.
|
|
#include <vector> // For std::vector.
|
|
#include <list> // For std::list.
|
|
#include <deque> // For std::deque.
|
|
#include <type_traits> // ... 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<typename T, size_t S>
|
|
class CryStackStringT;
|
|
template<size_t S>
|
|
class CryFixedStringT;
|
|
template<size_t S>
|
|
class CryFixedWStringT;
|
|
template<typename T>
|
|
class CryStringLocalT;
|
|
template<typename T>
|
|
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<T>:
|
|
// 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<typename T>
|
|
struct SVoid
|
|
{
|
|
typedef void type;
|
|
};
|
|
|
|
// SValidChar<T, InferEncoding, Input>:
|
|
// 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<typename T, bool InferEncoding, bool Input>
|
|
struct SValidChar
|
|
{
|
|
typedef typename remove_cv<T>::type BaseType;
|
|
static const bool isArithmeticType = is_arithmetic<BaseType>::value;
|
|
static const bool isQChar = is_same<BaseType, QChar>::value;
|
|
static const bool isUsable = isArithmeticType || isQChar;
|
|
static const bool isValidQualified = !is_const<T>::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<T>:
|
|
// 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<typename T>
|
|
struct SPackedIterators
|
|
{
|
|
const T begin, end;
|
|
SPackedIterators(const T& _begin, const T& _end)
|
|
: begin(_begin)
|
|
, end(_end) {}
|
|
};
|
|
|
|
// SPackedBuffer<T>:
|
|
// 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<typename T>
|
|
struct SPackedBuffer
|
|
{
|
|
T buffer;
|
|
size_t size;
|
|
SPackedBuffer(T _buffer, size_t _size)
|
|
: buffer(_buffer)
|
|
, size(_size) {}
|
|
};
|
|
|
|
// SDependentType<T, X>:
|
|
// 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<typename T, int X>
|
|
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<T, InferEncoding>:
|
|
// 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<typename T, bool InferEncoding, typename HasValueType = void, typename HasIteratorCategory = void>
|
|
struct SBindIterator
|
|
{
|
|
typedef const void CharType;
|
|
static const EBind value = eBind_Impossible;
|
|
};
|
|
template<typename T, bool InferEncoding, typename HasValueType, typename HasIteratorCategory>
|
|
struct SBindIterator<T*, InferEncoding, HasValueType, HasIteratorCategory>
|
|
{
|
|
typedef typename add_const<T>::type CharType;
|
|
static const bool isValid = SValidChar<CharType, InferEncoding, true>::value;
|
|
static const EBind value = isValid ? eBind_Iterators : eBind_Impossible;
|
|
};
|
|
template<typename T, bool InferEncoding>
|
|
struct SBindIterator<T, InferEncoding,
|
|
typename SVoid<typename T::value_type>::type,
|
|
typename SVoid<typename T::iterator_category>::type
|
|
>
|
|
{
|
|
typedef typename add_const<typename T::value_type>::type CharType;
|
|
typedef typename T::iterator_category IteratorCategory;
|
|
static const bool isInputIterator = is_base_of<std::input_iterator_tag, IteratorCategory>::value;
|
|
static const bool isValid = SValidChar<CharType, InferEncoding, true>::value;
|
|
static const EBind value = isValid && isInputIterator ? eBind_Iterators : eBind_Impossible;
|
|
};
|
|
|
|
// SBindObject<T, InferEncoding>:
|
|
// 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<typename T, bool InferEncoding>
|
|
struct SBindObject
|
|
{
|
|
typedef typename add_const<
|
|
typename conditional<
|
|
is_array<T>::value,
|
|
typename remove_extent<T>::type,
|
|
typename remove_pointer<T>::type
|
|
>::type
|
|
>::type CharType;
|
|
static const size_t FixedSize = extent<T>::value;
|
|
COMPILE_TIME_ASSERT(!is_array<T>::value || FixedSize > 0);
|
|
static const bool isConstArray = is_array<T>::value && is_const<typename remove_extent<T>::type>::value;
|
|
static const bool isBufferArray = is_array<T>::value && !isConstArray;
|
|
static const bool isPointer = is_pointer<T>::value;
|
|
static const bool isCodePoint = is_integral<T>::value;
|
|
static const bool isValidChar = SValidChar<CharType, InferEncoding, true>::value;
|
|
static const EBind value =
|
|
!isValidChar ? eBind_Impossible :
|
|
isConstArray ? eBind_Literal :
|
|
isBufferArray ? eBind_Buffer :
|
|
isPointer ? eBind_NullTerminated :
|
|
isCodePoint ? eBind_CodePoint :
|
|
eBind_Impossible;
|
|
};
|
|
template<typename CharT, typename Traits, typename Allocator, bool InferEncoding>
|
|
struct SBindObject<std::basic_string<CharT, Traits, Allocator>, InferEncoding>
|
|
{
|
|
typedef typename add_const<CharT>::type CharType;
|
|
static const bool isValid = SValidChar<CharT, InferEncoding, true>::value;
|
|
static const EBind value = isValid ? eBind_Data : eBind_Impossible;
|
|
};
|
|
template<typename T, typename Allocator, bool InferEncoding>
|
|
struct SBindObject<std::vector<T, Allocator>, InferEncoding>
|
|
{
|
|
typedef typename add_const<T>::type CharType;
|
|
static const bool isValid = SValidChar<T, InferEncoding, true>::value;
|
|
static const EBind value = isValid ? eBind_Data : eBind_Impossible;
|
|
};
|
|
template<typename T, typename Allocator, bool InferEncoding>
|
|
struct SBindObject<std::list<T, Allocator>, InferEncoding>
|
|
{
|
|
typedef typename add_const<T>::type CharType;
|
|
static const bool isValid = SValidChar<T, InferEncoding, true>::value;
|
|
static const EBind value = isValid ? eBind_Iterators : eBind_Impossible;
|
|
};
|
|
template<typename T, typename Allocator, bool InferEncoding>
|
|
struct SBindObject<std::deque<T, Allocator>, InferEncoding>
|
|
{
|
|
typedef typename add_const<T>::type CharType;
|
|
static const bool isValid = SValidChar<T, InferEncoding, true>::value;
|
|
static const EBind value = isValid ? eBind_Iterators : eBind_Impossible;
|
|
};
|
|
template<typename T, bool InferEncoding>
|
|
struct SBindObject<CryStringT<T>, InferEncoding>
|
|
{
|
|
typedef typename add_const<T>::type CharType;
|
|
static const bool isValid = SValidChar<T, InferEncoding, true>::value;
|
|
static const EBind value = isValid ? eBind_Data : eBind_Impossible;
|
|
};
|
|
template<typename T, bool InferEncoding>
|
|
struct SBindObject<CryStringLocalT<T>, InferEncoding>
|
|
{
|
|
typedef typename add_const<T>::type CharType;
|
|
static const bool isValid = SValidChar<T, InferEncoding, true>::value;
|
|
static const EBind value = isValid ? eBind_Data : eBind_Impossible;
|
|
};
|
|
template<typename T, size_t S, bool InferEncoding>
|
|
struct SBindObject<CryStackStringT<T, S>, InferEncoding>
|
|
{
|
|
typedef typename add_const<T>::type CharType;
|
|
static const bool isValid = SValidChar<T, InferEncoding, true>::value;
|
|
static const EBind value = isValid ? eBind_Data : eBind_Impossible;
|
|
};
|
|
template<size_t S, bool InferEncoding>
|
|
struct SBindObject<CryFixedStringT<S>, InferEncoding>
|
|
{
|
|
typedef char CharType;
|
|
static const bool isValid = SValidChar<CharType, InferEncoding, true>::value;
|
|
static const EBind value = isValid ? eBind_Data : eBind_Impossible;
|
|
};
|
|
template<size_t S, bool InferEncoding>
|
|
struct SBindObject<CryFixedWStringT<S>, InferEncoding>
|
|
{
|
|
typedef wchar_t CharType;
|
|
static const bool isValid = SValidChar<CharType, InferEncoding, true>::value;
|
|
static const EBind value = isValid ? eBind_Data : eBind_Impossible;
|
|
};
|
|
template<bool InferEncoding>
|
|
struct SBindObject<QString, InferEncoding>
|
|
{
|
|
typedef const QChar CharType;
|
|
static const EBind value = eBind_Data;
|
|
};
|
|
template<typename T, bool InferEncoding>
|
|
struct SBindObject<SPackedIterators<T>, InferEncoding>
|
|
{
|
|
typedef typename SBindIterator<T, InferEncoding>::CharType CharType;
|
|
static const EBind value = eBind_Iterators;
|
|
};
|
|
|
|
// SBindOutput<T, InferEncoding>:
|
|
// 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<typename T, bool InferEncoding>
|
|
struct SBindOutput
|
|
{
|
|
typedef typename remove_extent<T>::type CharType;
|
|
static const size_t FixedSize = extent<T>::value;
|
|
static const bool isArray = is_array<T>::value;
|
|
static const bool isValid = SValidChar<typename remove_extent<T>::type, InferEncoding, false>::value;
|
|
static const EBind value = isArray && isValid ? eBind_Buffer : eBind_Impossible;
|
|
};
|
|
template<typename OutputCharType, bool InferEncoding>
|
|
struct SBindOutput<SPackedBuffer<OutputCharType*>, InferEncoding>
|
|
{
|
|
typedef OutputCharType CharType;
|
|
static const bool isValid = SValidChar<CharType, InferEncoding, false>::value;
|
|
static const EBind value = isValid ? eBind_PackedBuffer : eBind_Impossible;
|
|
};
|
|
template<typename CharT, typename Traits, typename Allocator, bool InferEncoding>
|
|
struct SBindOutput<std::basic_string<CharT, Traits, Allocator>, InferEncoding>
|
|
{
|
|
typedef CharT CharType;
|
|
static const bool isValid = SValidChar<CharT, InferEncoding, false>::value;
|
|
static const EBind value = isValid ? eBind_Data : eBind_Impossible;
|
|
};
|
|
template<typename T, typename Allocator, bool InferEncoding>
|
|
struct SBindOutput<std::vector<T, Allocator>, InferEncoding>
|
|
{
|
|
typedef T CharType;
|
|
static const bool isValid = SValidChar<T, InferEncoding, false>::value;
|
|
static const EBind value = isValid ? eBind_Data : eBind_Impossible;
|
|
};
|
|
template<typename T, typename Allocator, bool InferEncoding>
|
|
struct SBindOutput<std::list<T, Allocator>, InferEncoding>
|
|
{
|
|
typedef T CharType;
|
|
static const bool isValid = SValidChar<T, InferEncoding, false>::value;
|
|
static const EBind value = isValid ? eBind_Iterators : eBind_Impossible;
|
|
};
|
|
template<typename T, typename Allocator, bool InferEncoding>
|
|
struct SBindOutput<std::deque<T, Allocator>, InferEncoding>
|
|
{
|
|
typedef T CharType;
|
|
static const bool isValid = SValidChar<T, InferEncoding, false>::value;
|
|
static const EBind value = isValid ? eBind_Iterators : eBind_Impossible;
|
|
};
|
|
template<typename T, bool InferEncoding>
|
|
struct SBindOutput<CryStringT<T>, InferEncoding>
|
|
{
|
|
typedef T CharType;
|
|
static const bool isValid = SValidChar<T, InferEncoding, false>::value;
|
|
static const EBind value = isValid ? eBind_Data : eBind_Impossible;
|
|
};
|
|
template<typename T, bool InferEncoding>
|
|
struct SBindOutput<CryStringLocalT<T>, InferEncoding>
|
|
{
|
|
typedef T CharType;
|
|
static const bool isValid = SValidChar<T, InferEncoding, false>::value;
|
|
static const EBind value = isValid ? eBind_Data : eBind_Impossible;
|
|
};
|
|
template<typename T, size_t S, bool InferEncoding>
|
|
struct SBindOutput<CryStackStringT<T, S>, InferEncoding>
|
|
{
|
|
typedef T CharType;
|
|
static const bool isValid = SValidChar<T, InferEncoding, false>::value;
|
|
static const EBind value = isValid ? eBind_Data : eBind_Impossible;
|
|
};
|
|
template<bool InferEncoding>
|
|
struct SBindOutput<QString, InferEncoding>
|
|
{
|
|
typedef QChar CharType;
|
|
static const EBind value = eBind_Data;
|
|
};
|
|
|
|
// SInferEncoding<T>:
|
|
// 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<typename T, bool Input>
|
|
struct SInferEncoding
|
|
{
|
|
typedef SBindObject<T, true> ObjectType;
|
|
typedef SBindIterator<T, true> 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<T, Input>:
|
|
// Pick the base character type to use during input or output with this element type.
|
|
template<typename T, bool Input, bool Integral = is_integral<T>::value, bool IsQChar = is_same<QChar, typename remove_cv<T>::type>::value>
|
|
struct SBindCharacter
|
|
{
|
|
typedef typename make_unsigned<T>::type BaseType; // The standard doesn't define if a character type is signed or unsigned.
|
|
typedef typename remove_cv<BaseType>::type UnqualifiedType;
|
|
typedef typename conditional<Input, const UnqualifiedType, UnqualifiedType>::type type;
|
|
};
|
|
template<typename T, bool Input>
|
|
struct SBindCharacter<T, Input, false, false>
|
|
{
|
|
COMPILE_TIME_ASSERT(is_arithmetic<T>::value);
|
|
typedef typename remove_cv<T>::type UnqualifiedType;
|
|
typedef typename conditional<Input, const UnqualifiedType, UnqualifiedType>::type type;
|
|
};
|
|
template<typename T, bool Input>
|
|
struct SBindCharacter<T, Input, false, true>
|
|
{
|
|
typedef typename conditional<Input, const uint16, uint16>::type type;
|
|
typedef typename SDependentType<QChar, Input>::type ActuallyQChar; // Force two-phase name lookup on QChar.
|
|
COMPILE_TIME_ASSERT(sizeof(ActuallyQChar) == sizeof(type)); // In case Qt ever changes QChar.
|
|
};
|
|
|
|
// SBindPointer<T, Input>:
|
|
// Pick the pointer type to use during input or output with buffers (potentially inside string types).
|
|
template<typename T, bool Input>
|
|
struct SBindPointer
|
|
{
|
|
COMPILE_TIME_ASSERT(is_pointer<T>::value || is_array<T>::value);
|
|
typedef typename conditional<
|
|
is_pointer<T>::value,
|
|
typename remove_pointer<T>::type,
|
|
typename remove_extent<T>::type
|
|
>::type UnboundCharType;
|
|
typedef typename SBindCharacter<UnboundCharType, Input>::type BoundCharType;
|
|
typedef BoundCharType* type;
|
|
};
|
|
|
|
// SAutomaticallyDeduced:
|
|
// Placeholder type that is never defined, used by SRequire for SFINAE overloading.
|
|
struct SAutomaticallyDeduced;
|
|
|
|
// SRequire<Expr, T>:
|
|
// Helper for SFINAE overloading.
|
|
// Similar to C++11's std::enable_if, which is not in boost (with that exact name anyway).
|
|
template<bool SFINAE, typename T = SAutomaticallyDeduced>
|
|
struct SRequire
|
|
{
|
|
typedef T type;
|
|
};
|
|
template<typename T>
|
|
struct SRequire<false, T> {};
|
|
|
|
// SafeCast<T, SourceChar>:
|
|
// 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<typename T, typename SourceChar>
|
|
inline T SafeCast(SourceChar* ptr, typename SRequire<is_integral<SourceChar>::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<T>::type TargetChar;
|
|
COMPILE_TIME_ASSERT(is_integral<SourceChar>::value && is_integral<TargetChar>::value);
|
|
COMPILE_TIME_ASSERT(sizeof(SourceChar) == sizeof(TargetChar));
|
|
return reinterpret_cast<T>(ptr);
|
|
}
|
|
template<typename T, typename SourceChar>
|
|
inline T SafeCast(SourceChar* ptr, typename SRequire<is_same<typename remove_cv<SourceChar>::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<T>::type TargetChar;
|
|
COMPILE_TIME_ASSERT(is_integral<TargetChar>::value);
|
|
COMPILE_TIME_ASSERT(sizeof(SourceChar) == sizeof(TargetChar));
|
|
return reinterpret_cast<T>(ptr);
|
|
}
|
|
template<typename T, typename SourceChar>
|
|
inline T SafeCast(SourceChar* ptr, typename SRequire<!is_integral<SourceChar>::value&& !is_same<typename remove_cv<SourceChar>::type, QChar>::value>::type* = 0)
|
|
{
|
|
// Any other casts that are allowed by C++.
|
|
return static_cast<T>(ptr);
|
|
}
|
|
|
|
// SCharacterTrait<T>:
|
|
// Exposes some basic traits for a given character.
|
|
// Note: Map to (hopefully optimized) CRT functions where possible.
|
|
template<typename T, size_t Size = sizeof(T)* is_integral<T>::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<typename T>
|
|
struct SCharacterTrait<T, sizeof(char)>
|
|
{
|
|
static size_t StrLen(const T* nts) // Narrow CRT strlen.
|
|
{
|
|
return ::strlen(SafeCast<const char*>(nts));
|
|
}
|
|
static size_t StrNLen(const T* ptr, size_t len) // Narrow CRT strnlen.
|
|
{
|
|
return ::strnlen(SafeCast<const char*>(ptr), len);
|
|
}
|
|
};
|
|
template<typename T>
|
|
struct SCharacterTrait<T, sizeof(wchar_t)>
|
|
{
|
|
static size_t StrLen(const T* nts) // Wide CRT strlen.
|
|
{
|
|
return ::wcslen(SafeCast<const wchar_t*>(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
|
|
#include AZ_RESTRICTED_FILE(UnicodeBinding_h)
|
|
#endif
|
|
return ::wcsnlen(SafeCast<const wchar_t*>(ptr), len);
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION UNICODEBINDING_H_SECTION_2
|
|
#include AZ_RESTRICTED_FILE(UnicodeBinding_h)
|
|
#endif
|
|
}
|
|
};
|
|
|
|
// void Feed(const SPackedIterators<InputIteratorType> &its, Sink &out, tag):
|
|
// Feeds the provided sink from provided packed iterator-range.
|
|
template<typename InputIteratorType, typename Sink>
|
|
inline void Feed(const SPackedIterators<InputIteratorType>& its, Sink& out, integral_constant<EBind, eBind_Iterators>)
|
|
{
|
|
typedef typename std::iterator_traits<InputIteratorType>::value_type UnboundCharType;
|
|
typedef typename SBindCharacter<UnboundCharType, true>::type BoundCharType;
|
|
for (InputIteratorType it = its.begin; it != its.end; ++it)
|
|
{
|
|
const UnboundCharType unbound = *it;
|
|
const BoundCharType bound = static_cast<BoundCharType>(unbound);
|
|
const uint32 item = static_cast<uint32>(bound);
|
|
out(item);
|
|
}
|
|
}
|
|
|
|
// void Feed(const SPackedIterators<const InputCharType *> &its, Sink &out, tag):
|
|
// Feeds the provided sink from provided packed pointer-range.
|
|
// This is slightly better code-generation than using generic iterators.
|
|
template<typename InputCharType, typename Sink>
|
|
inline void Feed(const SPackedIterators<const InputCharType*>& its, Sink& out, integral_constant<EBind, eBind_Iterators>)
|
|
{
|
|
typedef typename SBindPointer<const InputCharType*, true>::type PointerType;
|
|
assert(reinterpret_cast<size_t>(its.begin) <= reinterpret_cast<size_t>(its.end) && "Invalid range specified");
|
|
const size_t length = its.end - its.begin;
|
|
PointerType ptr = SafeCast<PointerType>(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<uint32>(*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<typename InputStringType, typename Sink>
|
|
inline void Feed(const InputStringType& in, Sink& out, integral_constant<EBind, eBind_Iterators> tag)
|
|
{
|
|
typedef typename InputStringType::const_iterator IteratorType;
|
|
Detail::SPackedIterators<IteratorType> 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<typename InputStringType, typename Sink>
|
|
inline void Feed(const InputStringType& in, Sink& out, integral_constant<EBind, eBind_Data>)
|
|
{
|
|
typedef typename InputStringType::size_type SizeType;
|
|
typedef typename InputStringType::value_type ValueType;
|
|
typedef typename SBindPointer<const ValueType*, true>::type PointerType;
|
|
const SizeType length = in.size();
|
|
if (length)
|
|
{
|
|
PointerType ptr = SafeCast<PointerType>(in.data());
|
|
for (SizeType i = 0; i < length; ++i, ++ptr)
|
|
{
|
|
const uint32 item = static_cast<uint32>(*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<typename InputStringType, typename Sink>
|
|
inline void Feed(const InputStringType& in, Sink& out, integral_constant<EBind, eBind_Literal>)
|
|
{
|
|
COMPILE_TIME_ASSERT(is_array<InputStringType>::value && extent<InputStringType>::value > 0);
|
|
typedef typename SBindPointer<InputStringType, true>::type PointerType;
|
|
const size_t length = extent<InputStringType>::value - 1;
|
|
PointerType ptr = SafeCast<PointerType>(in);
|
|
assert(ptr[length] == 0 && "Literal is not null-terminated");
|
|
for (size_t i = 0; i < length; ++i, ++ptr)
|
|
{
|
|
const uint32 item = static_cast<uint32>(*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<typename InputStringType, typename Sink>
|
|
inline void Feed(const InputStringType& in, Sink& out, integral_constant<EBind, eBind_Buffer>)
|
|
{
|
|
COMPILE_TIME_ASSERT(is_array<InputStringType>::value && extent<InputStringType>::value > 0);
|
|
typedef typename SBindPointer<InputStringType, true>::type PointerType;
|
|
typedef typename SBindPointer<InputStringType, true>::BoundCharType CharType;
|
|
const size_t length = extent<InputStringType>::value;
|
|
PointerType ptr = SafeCast<PointerType>(in);
|
|
for (size_t i = 0; i < length; ++i, ++ptr)
|
|
{
|
|
const CharType unbound = *ptr;
|
|
if (unbound == 0)
|
|
{
|
|
break;
|
|
}
|
|
const uint32 item = static_cast<uint32>(unbound);
|
|
out(item);
|
|
}
|
|
}
|
|
|
|
// void Feed(const InputStringType &in, Sink &out, tag):
|
|
// Feeds the provided sink from a null-terminated C-style string.
|
|
template<typename InputStringType, typename Sink>
|
|
inline void Feed(const InputStringType& in, Sink& out, integral_constant<EBind, eBind_NullTerminated>)
|
|
{
|
|
COMPILE_TIME_ASSERT(is_pointer<InputStringType>::value);
|
|
typedef typename SBindPointer<InputStringType, true>::type PointerType;
|
|
typedef typename SBindPointer<InputStringType, true>::BoundCharType CharType;
|
|
PointerType ptr = SafeCast<PointerType>(in);
|
|
if (ptr)
|
|
{
|
|
while (true)
|
|
{
|
|
const CharType unbound = *ptr;
|
|
++ptr;
|
|
if (unbound == 0)
|
|
{
|
|
break;
|
|
}
|
|
const uint32 item = static_cast<uint32>(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<typename InputCharType, typename Sink>
|
|
inline void Feed(const InputCharType& in, Sink& out, integral_constant<EBind, eBind_CodePoint>)
|
|
{
|
|
COMPILE_TIME_ASSERT(is_arithmetic<InputCharType>::value);
|
|
const uint32 item = static_cast<uint32>(in);
|
|
out(item);
|
|
}
|
|
|
|
// size_t EncodedLength(const SPackedIterators<InputIteratorType> &its, tag):
|
|
// Determines the length of the input sequence in a range of iterators.
|
|
template<typename InputIteratorType>
|
|
inline size_t EncodedLength(const SPackedIterators<InputIteratorType>& its, integral_constant<EBind, eBind_Iterators>)
|
|
{
|
|
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<typename InputStringType>
|
|
inline size_t EncodedLength(const InputStringType& in, integral_constant<EBind, eBind_Iterators>)
|
|
{
|
|
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<typename InputStringType>
|
|
inline size_t EncodedLength(const InputStringType& in, integral_constant<EBind, eBind_Data>)
|
|
{
|
|
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<typename InputStringType>
|
|
inline size_t EncodedLength(const InputStringType& in, integral_constant<EBind, eBind_Literal>)
|
|
{
|
|
COMPILE_TIME_ASSERT(is_array<InputStringType>::value && extent<InputStringType>::value > 0);
|
|
return extent<InputStringType>::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<typename InputStringType>
|
|
inline size_t EncodedLength(const InputStringType& in, integral_constant<EBind, eBind_Buffer>)
|
|
{
|
|
COMPILE_TIME_ASSERT(is_array<InputStringType>::value && extent<InputStringType>::value > 0);
|
|
typedef typename remove_extent<InputStringType>::type CharType;
|
|
return SCharacterTrait<CharType>::StrNLen(in, extent<InputStringType>::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<typename InputCharType>
|
|
inline size_t EncodedLength(const SPackedBuffer<InputCharType*>& in, integral_constant<EBind, eBind_PackedBuffer>)
|
|
{
|
|
return in.buffer ? SCharacterTrait<InputCharType>::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<typename InputStringType>
|
|
inline size_t EncodedLength(const InputStringType& in, integral_constant<EBind, eBind_NullTerminated>)
|
|
{
|
|
COMPILE_TIME_ASSERT(is_pointer<InputStringType>::value);
|
|
typedef typename remove_pointer<InputStringType>::type CharType;
|
|
return in ? SCharacterTrait<CharType>::StrLen(in) : 0;
|
|
}
|
|
|
|
// size_t EncodedLength(const InputCharType &in, tag):
|
|
// Determines the length of a single UCS code-point. This is always 1.
|
|
template<typename InputCharType>
|
|
inline size_t EncodedLength([[maybe_unused]] const InputCharType& in, integral_constant<EBind, eBind_CodePoint>)
|
|
{
|
|
COMPILE_TIME_ASSERT(is_arithmetic<InputCharType>::value);
|
|
return 1;
|
|
}
|
|
|
|
// const void *EncodedPointer(const SPackedIterators<const InputCharType *> &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<typename InputCharType>
|
|
inline const void* EncodedPointer(const SPackedIterators<const InputCharType*>& its, integral_constant<EBind, eBind_Iterators>)
|
|
{
|
|
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<typename InputStringType>
|
|
inline const void* EncodedPointer(const InputStringType& in, integral_constant<EBind, eBind_Data>)
|
|
{
|
|
return in.data();
|
|
}
|
|
|
|
// const void *EncodedPointer(const InputStringType &in, tag):
|
|
// Get a pointer to contiguous storage for a string-literal.
|
|
template<typename InputStringType>
|
|
inline const void* EncodedPointer(const InputStringType& in, integral_constant<EBind, eBind_Literal>)
|
|
{
|
|
COMPILE_TIME_ASSERT(is_array<InputStringType>::value && extent<InputStringType>::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<typename InputStringType>
|
|
inline const void* EncodedPointer(const InputStringType& in, integral_constant<EBind, eBind_Buffer>)
|
|
{
|
|
COMPILE_TIME_ASSERT(is_array<InputStringType>::value && extent<InputStringType>::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<typename InputStringType>
|
|
inline const void* EncodedPointer(const InputStringType& in, integral_constant<EBind, eBind_NullTerminated>)
|
|
{
|
|
COMPILE_TIME_ASSERT(is_pointer<InputStringType>::value);
|
|
return in; // Implied
|
|
}
|
|
|
|
// const void *EncodedPointer(const InputCharType &in, tag):
|
|
// Get a pointer to contiguous storage for a single UCS code-point.
|
|
template<typename InputCharType>
|
|
inline const void* EncodedPointer(const InputCharType& in, integral_constant<EBind, eBind_CodePoint>)
|
|
{
|
|
COMPILE_TIME_ASSERT(is_arithmetic<InputCharType>::value);
|
|
return ∈ // Take the address of the parameter (which is kept on the stack of the caller).
|
|
}
|
|
|
|
// SWriteSink<T, Append, BindMethod>:
|
|
// A helper that performs writing to the type T and can be passed as Sink type to a trans-coder helper.
|
|
template<typename T, bool Append, EBind>
|
|
struct SWriteSink;
|
|
template<typename T, bool Append>
|
|
struct SWriteSink<T, Append, eBind_Iterators>
|
|
{
|
|
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<OutputCharType>(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<typename T, bool Append>
|
|
struct SWriteSink<T, Append, eBind_Data>
|
|
{
|
|
typedef SBindPointer<typename T::value_type*, false> 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<CharType*>(base + offset);
|
|
}
|
|
void operator()(uint32 item)
|
|
{
|
|
*SafeCast<typename BindHelper::type>(ptr) = static_cast<typename BindHelper::BoundCharType>(item);
|
|
++ptr;
|
|
}
|
|
void operator()(const void* src, size_t length)
|
|
{
|
|
::memcpy(ptr, src, length * sizeof(CharType));
|
|
ptr += length;
|
|
}
|
|
void HintSequence([[maybe_unused]] uint32 length) {} // Don't care about sequences.
|
|
bool CanWrite() const { return true; } // Always writable
|
|
};
|
|
template<typename P, bool Append>
|
|
struct SWriteSink<SPackedBuffer<P>, Append, eBind_PackedBuffer>
|
|
{
|
|
typedef typename remove_pointer<P>::type ElementType;
|
|
typedef SBindPointer<ElementType*, false> BindHelper;
|
|
typedef typename BindHelper::UnboundCharType CharType;
|
|
CharType* ptr;
|
|
CharType* const terminator;
|
|
SWriteSink(CharType* _terminator)
|
|
: terminator(_terminator) {}
|
|
SWriteSink(SPackedBuffer<P>& out, size_t)
|
|
: terminator(out.size && out.buffer ? out.buffer + out.size - 1 : 0)
|
|
{
|
|
const size_t offset = Append
|
|
? EncodedLength(out, integral_constant<EBind, eBind_PackedBuffer>())
|
|
: 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<CharType*>(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<typename BindHelper::type>(ptr) = static_cast<typename BindHelper::BoundCharType>(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<typename T, bool Append>
|
|
struct SWriteSink<T, Append, eBind_Buffer> // Uses above implementation with specialized constructor
|
|
: SWriteSink<SPackedBuffer<typename remove_extent<T>::type*>, Append, eBind_PackedBuffer>
|
|
{
|
|
typedef typename remove_extent<T>::type ElementType;
|
|
typedef SWriteSink<SPackedBuffer<ElementType*>, Append, eBind_PackedBuffer> Super;
|
|
typedef SBindPointer<ElementType*, false> BindHelper;
|
|
typedef typename BindHelper::UnboundCharType CharType;
|
|
SWriteSink(T& out, size_t)
|
|
: Super(out + extent<T>::value - 1)
|
|
{
|
|
const size_t offset = Append
|
|
? EncodedLength(out, integral_constant<EBind, eBind_Buffer>())
|
|
: 0;
|
|
const size_t fixedOffset = Append && offset >= extent<T>::value
|
|
? extent<T>::value - 1 // In case the buffer is already full and not terminated.
|
|
: offset;
|
|
Super::ptr = out + fixedOffset; // Qualification for Super required for two-phase lookup.
|
|
}
|
|
};
|
|
|
|
// SIsBlockCopyable<InputType, OutputType>:
|
|
// 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<typename InputType, typename OutputType>
|
|
struct SIsBlockCopyable
|
|
{
|
|
template<EBind M>
|
|
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<typename T>
|
|
struct SIsPointers
|
|
{
|
|
static const bool value = false;
|
|
};
|
|
template<typename T>
|
|
struct SIsPointers<SPackedIterators<T*> >
|
|
{
|
|
static const bool value = true;
|
|
};
|
|
typedef typename SBindCharacter<typename InputType::CharType, true>::type InputCharType;
|
|
typedef typename SBindCharacter<typename OutputType::CharType, false>::type OutputCharType;
|
|
static const bool isIntegral = is_integral<InputCharType>::value && is_integral<OutputCharType>::value;
|
|
static const bool isSameSize = sizeof(InputCharType) == sizeof(OutputCharType);
|
|
static const bool isInputContiguous = (SIsContiguous<InputType::value>::value || SIsPointers<InputType>::value);
|
|
static const bool isOutputContiguous = (SIsContiguous<OutputType::value>::value || SIsPointers<OutputType>::value);
|
|
static const bool value = isIntegral && isSameSize && isInputContiguous && isOutputContiguous;
|
|
};
|
|
}
|
|
}
|