// ********************************************************************** // // Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** #ifndef ICE_STREAM_TRAITS_H #define ICE_STREAM_TRAITS_H #include #include #include namespace Ice { // The stream helper category // Allows streams to select the desired StreamHelper for a given streamable object; // see StreamableTraits below. // typedef int StreamHelperCategory; const StreamHelperCategory StreamHelperCategoryUnknown = 0; const StreamHelperCategory StreamHelperCategoryBuiltin = 1; const StreamHelperCategory StreamHelperCategoryStruct = 2; const StreamHelperCategory StreamHelperCategoryStructClass = 3; // struct with cpp:class metadata const StreamHelperCategory StreamHelperCategoryEnum = 4; const StreamHelperCategory StreamHelperCategorySequence = 5; const StreamHelperCategory StreamHelperCategoryDictionary = 6; const StreamHelperCategory StreamHelperCategoryProxy = 7; const StreamHelperCategory StreamHelperCategoryClass = 8; const StreamHelperCategory StreamHelperCategoryUserException = 9; // // The optional format. // // Optional data members and parameters are encoded with a specific // optional format. This optional format describes how the data is encoded // and how it can be skipped by the unmarshaling code if the optional // isn't known to the receiver. // enum OptionalFormat { OptionalFormatF1 = 0, // Fixed 1-byte encoding OptionalFormatF2 = 1, // Fixed 2 bytes encoding OptionalFormatF4 = 2, // Fixed 4 bytes encoding OptionalFormatF8 = 3, // Fixed 8 bytes encoding OptionalFormatSize = 4, // "Size encoding" on 1 to 5 bytes, e.g. enum, class identifier OptionalFormatVSize = 5, // "Size encoding" on 1 to 5 bytes followed by data, e.g. string, fixed size // struct, or containers whose size can be computed prior to marshaling OptionalFormatFSize = 6, // Fixed size on 4 bytes followed by data, e.g. variable-size struct, container. OptionalFormatClass = 7 }; // // Is the provided type a container? // For now, the implementation only checks if there is a T::iterator typedef // using SFINAE // template struct IsContainer { template static char test(typename C::iterator*); template static long test(...); static const bool value = sizeof(test(0)) == sizeof(char); }; // // Is the provided type a map? // For now, the implementation only checks if there is a T::mapped_type typedef // using SFINAE // template struct IsMap { template static char test(typename C::mapped_type*); template static long test(...); static const bool value = IsContainer::value && sizeof(test(0)) == sizeof(char); }; // // Base traits template. // Types with no specialized trait use this trait. // template struct StreamableTraits { static const StreamHelperCategory helper = IsMap::value ? StreamHelperCategoryDictionary : (IsContainer::value ? StreamHelperCategorySequence : StreamHelperCategoryUnknown); // // When extracting a sequence from a stream, we can ensure the // stream has at least StreamableTraits::minWireSize * size bytes // For containers, the minWireSize is 1 (just 1 byte for an empty container). // static const int minWireSize = 1; // // Is this type encoded on a fixed number of bytes? // Used only for marshaling/unmarshaling optional data members and parameters. // static const bool fixedLength = false; }; // // StreamableTraits specialization for array / range mapped sequences // The type can be a std::pair or a // std::pair, std::pair > // template struct StreamableTraits< ::std::pair > { static const StreamHelperCategory helper = StreamHelperCategorySequence; static const int minWireSize = 1; static const bool fixedLength = false; }; // // StreamableTraits specialization for user exceptions. // template<> struct StreamableTraits { static const StreamHelperCategory helper = StreamHelperCategoryUserException; // // There is no sequence/dictionary of UserException (so no need for minWireSize) // and no optional UserException (so no need for fixedLength) // }; // // StreamableTraits specialization for builtins (these are needed for sequence // marshaling to figure out the minWireSize of each built-in). // template<> struct StreamableTraits { static const StreamHelperCategory helper = StreamHelperCategoryBuiltin; static const int minWireSize = 1; static const bool fixedLength = true; }; template<> struct StreamableTraits { static const StreamHelperCategory helper = StreamHelperCategoryBuiltin; static const int minWireSize = 1; static const bool fixedLength = true; }; template<> struct StreamableTraits { static const StreamHelperCategory helper = StreamHelperCategoryBuiltin; static const int minWireSize = 2; static const bool fixedLength = true; }; template<> struct StreamableTraits { static const StreamHelperCategory helper = StreamHelperCategoryBuiltin; static const int minWireSize = 4; static const bool fixedLength = true; }; template<> struct StreamableTraits { static const StreamHelperCategory helper = StreamHelperCategoryBuiltin; static const int minWireSize = 8; static const bool fixedLength = true; }; template<> struct StreamableTraits { static const StreamHelperCategory helper = StreamHelperCategoryBuiltin; static const int minWireSize = 4; static const bool fixedLength = true; }; template<> struct StreamableTraits { static const StreamHelperCategory helper = StreamHelperCategoryBuiltin; static const int minWireSize = 8; static const bool fixedLength = true; }; template<> struct StreamableTraits< ::std::string> { static const StreamHelperCategory helper = StreamHelperCategoryBuiltin; static const int minWireSize = 1; static const bool fixedLength = false; }; template<> struct StreamableTraits< ::std::wstring> { static const StreamHelperCategory helper = StreamHelperCategoryBuiltin; static const int minWireSize = 1; static const bool fixedLength = false; }; // // vector is a special type in C++: the streams are responsible // to handle it like a built-in type. // template<> struct StreamableTraits< ::std::vector > { static const StreamHelperCategory helper = StreamHelperCategoryBuiltin; static const int minWireSize = 1; static const bool fixedLength = false; }; template struct StreamableTraits< ::IceInternal::ProxyHandle > { static const StreamHelperCategory helper = StreamHelperCategoryProxy; static const int minWireSize = 2; static const bool fixedLength = false; }; template struct StreamableTraits< ::IceInternal::Handle > { static const StreamHelperCategory helper = StreamHelperCategoryClass; static const int minWireSize = 1; static const bool fixedLength = false; }; // // StreamHelper templates used by streams to read and write data. // // Base StreamHelper template; it must be specialized for each type template struct StreamHelper; // Helper for builtins, delegates read/write to the stream. template struct StreamHelper { template static inline void write(S* stream, const T& v) { stream->write(v); } template static inline void read(S* stream, T& v) { stream->read(v); } }; // "helpers" for the StreamHelper below // We generate specializations, which can be instantiated explicitly and exported from DLLs // template struct StreamWriter; template struct StreamReader; // Helper for structs template struct StreamHelper { template static inline void write(S* stream, const T& v) { StreamWriter::write(stream, v); } template static inline void read(S* stream, T& v) { StreamReader::read(stream, v); } }; // Helper for class structs template struct StreamHelper { template static inline void write(S* stream, const T& v) { StreamWriter::write(stream, v); } template static inline void read(S* stream, T& v) { v = new typename T::element_type; StreamReader::read(stream, v); } }; // Helper for enums template struct StreamHelper { template static inline void write(S* stream, const T& v) { if(static_cast(v) < StreamableTraits::minValue || static_cast(v) > StreamableTraits::maxValue) { IceInternal::Ex::throwMarshalException(__FILE__, __LINE__, "enumerator out of range"); } stream->writeEnum(static_cast(v), StreamableTraits::maxValue); } template static inline void read(S* stream, T& v) { Int value = stream->readEnum(StreamableTraits::maxValue); if(value < StreamableTraits::minValue || value > StreamableTraits::maxValue) { IceInternal::Ex::throwMarshalException(__FILE__, __LINE__, "enumerator out of range"); } v = static_cast(value); } }; // Helper for sequences template struct StreamHelper { template static inline void write(S* stream, const T& v) { stream->writeSize(static_cast(v.size())); for(typename T::const_iterator p = v.begin(); p != v.end(); ++p) { stream->write(*p); } } template static inline void read(S* stream, T& v) { Int sz = stream->readAndCheckSeqSize(StreamableTraits::minWireSize); T(sz).swap(v); for(typename T::iterator p = v.begin(); p != v.end(); ++p) { stream->read(*p); } } }; // Helper for array and range:array custom sequence parameters template struct StreamHelper, StreamHelperCategorySequence> { template static inline void write(S* stream, const std::pair& v) { stream->write(v.first, v.second); } template static inline void read(S* stream, std::pair& v) { stream->read(v); } }; // Helper for range custom sequence parameters template struct StreamHelper, StreamHelperCategorySequence> { template static inline void write(S* stream, const std::pair& v) { stream->writeSize(static_cast(IceUtilInternal::distance(v.first, v.second))); for(T p = v.first; p != v.second; ++p) { stream->write(*p); } } template static inline void read(S* stream, std::pair& v) { stream->read(v); } }; template<> struct StreamHelper::const_iterator, ::std::vector::const_iterator>, StreamHelperCategorySequence> { template static inline void write(S* stream, const std::pair< ::std::vector::const_iterator, ::std::vector::const_iterator>& v) { stream->writeSize(static_cast(IceUtilInternal::distance(v.first, v.second))); for(::std::vector::const_iterator p = v.first; p != v.second; ++p) { stream->write(static_cast(*p)); } } // no read: only used for marshaling }; // Helper for zero-copy array sequence parameters template struct StreamHelper, std::pair >, StreamHelperCategorySequence> { template static inline void read(S* stream, std::pair, std::pair >& v) { stream->read(v.second, v.first); } // no write: only used for unmarshaling }; // Helper for dictionaries template struct StreamHelper { template static inline void write(S* stream, const T& v) { stream->writeSize(static_cast(v.size())); for(typename T::const_iterator p = v.begin(); p != v.end(); ++p) { stream->write(p->first); stream->write(p->second); } } template static inline void read(S* stream, T& v) { Int sz = stream->readSize(); v.clear(); while(sz--) { typename T::value_type p; stream->read(const_cast(p.first)); typename T::iterator i = v.insert(v.end(), p); stream->read(i->second); } } }; // Helper for user exceptions template struct StreamHelper { template static inline void write(S* stream, const T& v) { stream->writeException(v); } // no read: only used for marshaling }; // Helper for proxies template struct StreamHelper { template static inline void write(S* stream, const T& v) { stream->write(v); } template static inline void read(S* stream, T& v) { stream->read(v); } }; // Helper for classes template struct StreamHelper { template static inline void write(S* stream, const T& v) { stream->write(v); } template static inline void read(S* stream, T& v) { stream->read(v); } }; // // Helpers to read/write optional attributes or members. // // // Extract / compute the optionalFormat // This is used _only_ for the base StreamOptionalHelper below // /!\ Do not use in StreamOptionalHelper specializations, and do // not provide specialization not handled by the base StreamOptionalHelper // template struct GetOptionalFormat; template<> struct GetOptionalFormat { static const OptionalFormat value = OptionalFormatF1; }; template<> struct GetOptionalFormat { static const OptionalFormat value = OptionalFormatF2; }; template<> struct GetOptionalFormat { static const OptionalFormat value = OptionalFormatF4; }; template<> struct GetOptionalFormat { static const OptionalFormat value = OptionalFormatF8; }; template<> struct GetOptionalFormat { static const OptionalFormat value = OptionalFormatVSize; }; template<> struct GetOptionalFormat { static const OptionalFormat value = OptionalFormatClass; }; template struct GetOptionalFormat { static const OptionalFormat value = OptionalFormatSize; }; // Base helper: simply read/write the data template struct StreamOptionalHelper { typedef StreamableTraits Traits; // If this optionalFormat fails to compile, you must either define your specialization // for GetOptionalFormat (in which case the optional data will be marshaled/unmarshaled // with straight calls to write/read on the stream), or define your own // StreamOptionalHelper specialization (which gives you more control over marshaling) // static const OptionalFormat optionalFormat = GetOptionalFormat::value; template static inline void write(S* stream, const T& v) { stream->write(v); } template static inline void read(S* stream, T& v) { stream->read(v); } }; // Helper to write fixed size structs template struct StreamOptionalHelper { static const OptionalFormat optionalFormat = OptionalFormatVSize; template static inline void write(S* stream, const T& v) { stream->writeSize(StreamableTraits::minWireSize); stream->write(v); } template static inline void read(S* stream, T& v) { stream->skipSize(); stream->read(v); } }; // Helper to write variable size structs template struct StreamOptionalHelper { static const OptionalFormat optionalFormat = OptionalFormatFSize; template static inline void write(S* stream, const T& v) { typename S::size_type pos = stream->startSize(); stream->write(v); stream->endSize(pos); } template static inline void read(S* stream, T& v) { stream->skip(4); stream->read(v); } }; // Class structs are encoded like structs template struct StreamOptionalHelper : StreamOptionalHelper { }; // Optional proxies are encoded like variable size structs, using the FSize encoding template struct StreamOptionalHelper : StreamOptionalHelper { }; // // Helpers to read/write optional sequences or dictionaries // template struct StreamOptionalContainerHelper; // // Encode containers of variable size elements with the FSize optional // type, since we can't easily figure out the size of the container // before encoding. This is the same encoding as variable size structs // so we just re-use its implementation. // template struct StreamOptionalContainerHelper { static const OptionalFormat optionalFormat = OptionalFormatFSize; template static inline void write(S* stream, const T& v, Int) { StreamOptionalHelper::write(stream, v); } template static inline void read(S* stream, T& v) { StreamOptionalHelper::read(stream, v); } }; // // Encode containers of fixed size elements with the VSize optional // type since we can figure out the size of the container before // encoding. // template struct StreamOptionalContainerHelper { static const OptionalFormat optionalFormat = OptionalFormatVSize; template static inline void write(S* stream, const T& v, Int n) { // // The container size is the number of elements * the size of // an element and the size-encoded number of elements (1 or // 5 depending on the number of elements). // stream->writeSize(sz * n + (n < 255 ? 1 : 5)); stream->write(v); } template static inline void read(S* stream, T& v) { stream->skipSize(); stream->read(v); } }; // // Optimization: containers of 1 byte elements are encoded with the // VSize optional type. There's no need to encode an additional size // for those, the number of elements of the container can be used to // skip the optional. // template struct StreamOptionalContainerHelper { static const OptionalFormat optionalFormat = OptionalFormatVSize; template static inline void write(S* stream, const T& v, Int) { stream->write(v); } template static inline void read(S* stream, T& v) { stream->read(v); } }; // // Helper to write sequences, delegates to the optional container // helper template partial specializations. // template struct StreamOptionalHelper { typedef typename T::value_type E; static const int size = StreamableTraits::minWireSize; static const bool fixedLength = StreamableTraits::fixedLength; // The optional type of a sequence depends on whether or not elements are fixed // or variable size elements and their size. static const OptionalFormat optionalFormat = StreamOptionalContainerHelper::optionalFormat; template static inline void write(S* stream, const T& v) { StreamOptionalContainerHelper::write(stream, v, static_cast(v.size())); } template static inline void read(S* stream, T& v) { StreamOptionalContainerHelper::read(stream, v); } }; template struct StreamOptionalHelper, StreamHelperCategorySequence, false> { typedef std::pair P; static const int size = StreamableTraits::minWireSize; static const bool fixedLength = StreamableTraits::fixedLength; // The optional type of a sequence depends on whether or not elements are fixed // or variable size elements and their size. static const OptionalFormat optionalFormat = StreamOptionalContainerHelper::optionalFormat; template static inline void write(S* stream, const P& v) { Int n = static_cast(v.second - v.first); StreamOptionalContainerHelper::write(stream, v, n); } template static inline void read(S* stream, P& v) { StreamOptionalContainerHelper::read(stream, v); } }; template struct StreamOptionalHelper, StreamHelperCategorySequence, false> { typedef std::pair P; static const int size = StreamableTraits::minWireSize; static const bool fixedLength = StreamableTraits::fixedLength; // The optional type of a sequence depends on whether or not elements are fixed // or variable size elements and their size. static const OptionalFormat optionalFormat = StreamOptionalContainerHelper::optionalFormat; template static inline void write(S* stream, const P& v) { Int n = static_cast(v.second - v.first); StreamOptionalContainerHelper::write(stream, v, n); } template static inline void read(S* stream, P& v) { StreamOptionalContainerHelper::read(stream, v); } }; template struct StreamOptionalHelper, std::pair >, StreamHelperCategorySequence, false> { typedef std::pair, std::pair > P; static const int size = StreamableTraits::minWireSize; static const bool fixedLength = StreamableTraits::fixedLength; // The optional type of a sequence depends on whether or not elements are fixed // or variable size elements and their size. static const OptionalFormat optionalFormat = StreamOptionalContainerHelper::optionalFormat; template static inline void read(S* stream, P& v) { StreamOptionalContainerHelper::read(stream, v); } // no write: only used for unmarshaling }; // // Helper to write dictionaries, delegates to the optional container // helper template partial specializations. // template struct StreamOptionalHelper { typedef typename T::key_type K; typedef typename T::mapped_type V; static const int size = StreamableTraits::minWireSize + StreamableTraits::minWireSize; static const bool fixedLength = StreamableTraits::fixedLength && StreamableTraits::fixedLength; // The optional type of a dictionary depends on whether or not elements are fixed // or variable size elements. static const OptionalFormat optionalFormat = StreamOptionalContainerHelper::optionalFormat; template static inline void write(S* stream, const T& v) { StreamOptionalContainerHelper::write(stream, v, static_cast(v.size())); } template static inline void read(S* stream, T& v) { StreamOptionalContainerHelper::read(stream, v); } }; } #endif