diff options
| -rw-r--r-- | common/safe-bool.h | 65 | ||||
| -rw-r--r-- | common/scummsys.h | 11 | ||||
| -rw-r--r-- | common/span.h | 995 | ||||
| -rw-r--r-- | common/type-traits.h | 34 | ||||
| -rw-r--r-- | test/common/span.h | 789 | 
5 files changed, 1894 insertions, 0 deletions
| diff --git a/common/safe-bool.h b/common/safe-bool.h new file mode 100644 index 0000000000..7cbe29931c --- /dev/null +++ b/common/safe-bool.h @@ -0,0 +1,65 @@ +/** + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef COMMON_SAFE_BOOL_H +#define COMMON_SAFE_BOOL_H + +namespace Common { +	namespace impl { +		template <typename T> +		struct no_base {}; + +		template <typename T> +		struct safe_bool_impl { +			typedef T *TP; // workaround to make parsing easier +			TP stub; +			typedef TP safe_bool_impl::*type; +		}; +	} + +	/** +	 * Prevents `operator bool` from implicitly converting to other types. +	 */ +	template <typename DerivedT, typename BaseT = impl::no_base<DerivedT> > +	struct SafeBool : BaseT { +	private: +		typedef impl::safe_bool_impl<DerivedT> impl_t; +		typedef typename impl_t::type bool_type; + +	public: +		operator bool_type() const { +			return static_cast<const DerivedT *>(this)->operator_bool() ? +			&impl_t::stub : 0; +		} + +		operator bool_type() { +			return static_cast<DerivedT *>(this)->operator_bool() ? +			&impl_t::stub : 0; +		} +	}; +} // End of namespace Common + +#endif diff --git a/common/scummsys.h b/common/scummsys.h index 1845f218bd..6fd9be81c8 100644 --- a/common/scummsys.h +++ b/common/scummsys.h @@ -151,6 +151,17 @@  	#if !defined(__SYMBIAN32__)  	#include <new>  	#endif + +	/** +	 * Generates a compile-time assertion. +	 * +	 * @param expression An expression that can be evaluated at compile time. +	 * @param message An underscore-delimited message to be presented at compile +	 * time if the expression evaluates to false. +	 */ +	#define STATIC_ASSERT(expression, message) \ +		extern int STATIC_ASSERT_##message[(expression) ? 1 : -1]; \ +		(void)(STATIC_ASSERT_##message);  #endif  // The following math constants are usually defined by the system math.h header, but diff --git a/common/span.h b/common/span.h new file mode 100644 index 0000000000..db6110a91c --- /dev/null +++ b/common/span.h @@ -0,0 +1,995 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef COMMON_SPAN_H +#define COMMON_SPAN_H + +#include "common/file.h" +#include "common/memstream.h" +#include "common/safe-bool.h" +#include "common/scummsys.h" +#include "common/type-traits.h" + +namespace Common { + +#define COMMON_SPAN_TYPEDEFS \ +	typedef typename super_type::value_type value_type; \ +	typedef typename super_type::difference_type difference_type; \ +	typedef typename super_type::index_type index_type; \ +	typedef typename super_type::size_type size_type; \ +	typedef typename super_type::const_iterator const_iterator; \ +	typedef typename super_type::iterator iterator; \ +	typedef typename super_type::pointer pointer; \ +	typedef typename super_type::const_pointer const_pointer; \ +	typedef typename super_type::reference reference; \ +	typedef typename super_type::const_reference const_reference; + +enum { +	kSpanMaxSize = 0xFFFFFFFF, +	kSpanKeepOffset = 0xFFFFFFFF +}; + +#pragma mark - +#pragma mark SpanValidationMode + +enum SpanValidationMode { +	kValidateRead, +	kValidateWrite, +	kValidateSeek +}; + +namespace SpanInternal { +#pragma mark - +#pragma mark SpanIterator +	/** +	 * Bounds-checked iteration over a span of memory. +	 */ +	template <typename Span, bool IsConst> +	class SpanIterator { +		typedef typename Span::value_type span_value_type; +		typedef typename Conditional<IsConst, const Span, Span>::type span_type; + +	public: +		typedef typename Span::difference_type difference_type; +		typedef typename RemoveConst<span_value_type>::type value_type; +		typedef typename Conditional<IsConst, const span_value_type, span_value_type>::type *pointer; +		typedef typename Conditional<IsConst, const span_value_type, span_value_type>::type &reference; + +		inline SpanIterator() : _span(nullptr), _index(0) {} + +		inline SpanIterator(span_type *const span, const difference_type index) : +			_span(span), +			_index(index) { +			if (span != nullptr) { +				span->validate(index, 0, kValidateSeek); +			} +		} + +		inline SpanIterator(const SpanIterator &other) : +			_span(other._span), +			_index(other._index) {} + +		inline SpanIterator &operator=(const SpanIterator &other) { +			_span = other._span; +			_index = other._index; +			return *this; +		} + +#pragma mark - +#pragma mark SpanIterator - Dereferencing operations + +	public: +		inline reference operator*() const { +			// validation is in Span::operator[] +			return (*_span)[_index]; +		} + +		inline pointer operator->() const { +			return &operator*(); +		} + +		inline reference operator[](const difference_type index) const { +			// validation is in SpanIterator::operator+= +			return *(*this + index); +		} + +#pragma mark - +#pragma mark SpanIterator - Arithmetic operations + +	public: +		inline SpanIterator &operator+=(const difference_type delta) { +			assert(_span != nullptr); +			_span->validate(_index, delta, kValidateSeek); +			_index += delta; +			return *this; +		} + +		inline SpanIterator &operator-=(const difference_type delta) { +			return operator+=(-delta); +		} + +		inline SpanIterator &operator++() { +			return operator+=(1); +		} + +		inline SpanIterator operator++(int) { +			SpanIterator old(*this); +			operator+=(1); +			return old; +		} + +		inline SpanIterator &operator--() { +			return operator+=(-1); +		} + +		inline SpanIterator operator--(int) { +			SpanIterator old(*this); +			operator+=(-1); +			return old; +		} + +		inline SpanIterator operator+(const difference_type delta) const { +			SpanIterator it(*this); +			return it += delta; +		} + +		inline SpanIterator operator-(const difference_type delta) const { +			return operator+(-delta); +		} + +		inline difference_type operator-(const SpanIterator &other) const { +			assert(_span == other._span); +			return _index - other._index; +		} + +#pragma mark - +#pragma mark SpanIterator - Comparison operations + +	public: +		inline bool operator==(const SpanIterator& other) const { +			return _span == other._span && _index == other._index; +		} + +		inline bool operator!=(const SpanIterator& other) const { +			return !operator==(other); +		} + +		inline bool operator<(const SpanIterator& other) const { +			assert(_span == other._span); +			return _index < other._index; +		} + +		inline bool operator<=(const SpanIterator& other) const { +			return !other.operator<(*this); +		} + +		inline bool operator>(const SpanIterator& other) const { +			return other.operator<(*this); +		} + +		inline bool operator>=(const SpanIterator& other) const { +			return !operator<(other); +		} + +#pragma mark - +#pragma mark SpanIterator - Data access convenience functions + +	public: +		inline int8 getInt8() const { +			return _span->getInt8At(_index); +		} + +		inline uint8 getUint8() const { +			return _span->getUint8At(_index); +		} + +		inline int16 getInt16BE() const { +			return _span->getInt16BEAt(_index); +		} + +		inline int16 getInt16LE() const { +			return _span->getInt16LEAt(_index); +		} + +		inline uint16 getUint16BE() const { +			return _span->getUint16BEAt(_index); +		} + +		inline uint16 getUint16LE() const { +			return _span->getUint16LEAt(_index); +		} + +		inline uint32 getUint24LE() const { +			return _span->getUint24LEAt(_index); +		} + +		inline uint32 getUint32() const { +			return _span->getUint32At(_index); +		} + +		inline int32 getInt32BE() const { +			return _span->getInt32BEAt(_index); +		} + +		inline int32 getInt32LE() const { +			return _span->getInt32LEAt(_index); +		} + +		inline uint32 getUint32BE() const { +			return _span->getUint32BEAt(_index); +		} + +		inline uint32 getUint32LE() const { +			return _span->getUint32LEAt(_index); +		} + +	protected: +		span_type *_span; +		difference_type _index; +	}; +} // End of namespace SpanInternal + +#pragma mark - +#pragma mark SpanBase + +template <typename ValueType, template <typename> class Derived> +class SpanBase : public SafeBool<Derived<ValueType> > { +	typedef Derived<ValueType> derived_type; +	typedef typename AddConst<derived_type>::type const_derived_type; +	typedef typename RemoveConst<derived_type>::type mutable_derived_type; + +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) +	template <typename T, bool U> friend class SpanInternal::SpanIterator; +	template <typename T, template <typename> class U> friend class SpanBase; +	template <typename T, typename U> friend struct SafeBool; +#endif +#ifdef CXXTEST_RUNNING +	friend class ::SpanTestSuite; +#endif + +public: +	typedef ValueType value_type; +	typedef ptrdiff_t difference_type; +	typedef size_t index_type; +	typedef size_t size_type; +	typedef SpanInternal::SpanIterator<derived_type, true> const_iterator; +	typedef SpanInternal::SpanIterator<derived_type, false> iterator; +	typedef value_type *pointer; +	typedef const value_type *const_pointer; +	typedef value_type &reference; +	typedef const value_type &const_reference; + +	inline size_type byteSize() const { return impl().size() * sizeof(value_type); } + +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) +protected: +#endif +	inline SpanBase() {} +	inline SpanBase(const SpanBase &) {} +	inline SpanBase &operator=(const SpanBase &) { return this->impl(); } +	inline ~SpanBase() {} + +	inline const_derived_type &impl() const { return static_cast<const_derived_type &>(*this); } +	inline mutable_derived_type &impl() { return static_cast<mutable_derived_type &>(*this); } + +#pragma mark - +#pragma mark SpanBase - Interface + +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) +protected: +#endif +	inline void clear(); + +	inline size_type size() const; + +	inline const_iterator cbegin() const; +	inline const_iterator cend() const; +	inline const_iterator begin() const; +	inline const_iterator end() const; +	inline iterator begin(); +	inline iterator end(); + +	inline pointer data() const; + +#pragma mark - +#pragma mark SpanBase - Data access functions + +public: +	inline const_reference operator[](const index_type index) const { +		impl().validate(index, sizeof(value_type)); +		return impl().data()[index]; +	} + +	inline reference operator[](const index_type index) { +		impl().validate(index, sizeof(value_type)); +		return impl().data()[index]; +	} + +	inline int8 getInt8At(const index_type index) const { +		STATIC_ASSERT(sizeof(value_type) == sizeof(uint8), int8_can_only_be_read_from_byte_or_char_spans); +		return (int8)getUint8At(index); +	} + +	inline uint8 getUint8At(const index_type index) const { +		STATIC_ASSERT(sizeof(value_type) == sizeof(uint8), uint8_can_only_be_read_from_byte_or_char_spans); +		impl().validate(index, sizeof(uint8)); +		return (uint8)impl().data()[index]; +	} + +	inline int16 getInt16BEAt(const index_type index) const { +		STATIC_ASSERT(sizeof(value_type) <= sizeof(uint16), int16_can_only_be_read_from_int16_or_smaller_spans); +		return (int16)impl().getUint16BEAt(index); +	} + +	inline int16 getInt16LEAt(const index_type index) const { +		STATIC_ASSERT(sizeof(value_type) <= sizeof(uint16), int16_can_only_be_read_from_int16_or_smaller_spans); +		return (int16)impl().getUint16LEAt(index); +	} + +	inline uint16 getUint16BEAt(const index_type index) const { +		STATIC_ASSERT(sizeof(value_type) <= sizeof(uint16), uint16_can_only_be_read_from_int16_or_smaller_spans); +		impl().validate(index, sizeof(uint16)); +		return READ_BE_UINT16(impl().data() + index); +	} + +	inline uint16 getUint16LEAt(const index_type index) const { +		STATIC_ASSERT(sizeof(value_type) <= sizeof(uint16), uint16_can_only_be_read_from_int16_or_smaller_spans); +		impl().validate(index, sizeof(uint16)); +		return READ_LE_UINT16(impl().data() + index); +	} + +	inline uint32 getUint24LEAt(const index_type index) const { +		STATIC_ASSERT(sizeof(value_type) <= 3, uint24_can_only_be_read_from_int24_or_smaller_spans); +		impl().validate(index, 3); +		return READ_LE_UINT24(impl().data() + index); +	} + +	inline uint32 getUint32At(const index_type index) const { +		STATIC_ASSERT(sizeof(value_type) <= sizeof(uint32), uint32_can_only_be_read_from_int32_or_smaller_spans); +		impl().validate(index, sizeof(uint32)); +		return READ_UINT32(impl().data() + index); +	} + +	inline int32 getInt32BEAt(const index_type index) const { +		STATIC_ASSERT(sizeof(value_type) <= sizeof(uint32), int32_can_only_be_read_from_int32_or_smaller_spans); +		return (int32)impl().getUint32BEAt(index); +	} + +	inline int32 getInt32LEAt(const index_type index) const { +		STATIC_ASSERT(sizeof(value_type) <= sizeof(uint32), int32_can_only_be_read_from_int32_or_smaller_spans); +		return (int32)impl().getUint32LEAt(index); +	} + +	inline uint32 getUint32BEAt(const index_type index) const { +		STATIC_ASSERT(sizeof(value_type) <= sizeof(uint32), uint32_can_only_be_read_from_int32_or_smaller_spans); +		impl().validate(index, sizeof(uint32)); +		return READ_BE_UINT32(impl().data() + index); +	} + +	inline uint32 getUint32LEAt(const index_type index) const { +		STATIC_ASSERT(sizeof(value_type) <= sizeof(uint32), uint32_can_only_be_read_from_int32_or_smaller_spans); +		impl().validate(index, sizeof(uint32)); +		return READ_LE_UINT32(impl().data() + index); +	} + +	inline String getStringAt(const index_type index, size_type numEntries = kSpanMaxSize) const { +		STATIC_ASSERT(sizeof(value_type) == sizeof(char), strings_can_only_be_read_from_byte_or_char_spans); +		const char *string = (const char *)impl().data(); + +		if (numEntries == kSpanMaxSize) { +			numEntries = strnlen(string, impl().size() - index); +		} + +		impl().validate(index, numEntries); +		return String(string + index, numEntries); +	} + +	/** +	 * Returns a raw pointer to memory after validating the given index and +	 * size. Use this only in performance-critical code, like processing pixel +	 * data in a loop, where validating each read independently would introduce +	 * unnecessary overhead. +	 */ +	inline const_pointer getUnsafeDataAt(const index_type index, size_type numEntries = kSpanMaxSize) const { +		if (numEntries == kSpanMaxSize) { +			numEntries = impl().size() - index; +		} + +		impl().validate(index, numEntries * sizeof(value_type)); +		return impl().data() + index; +	} + +	inline pointer getUnsafeDataAt(const index_type index, size_type numEntries = kSpanMaxSize) { +		if (numEntries == kSpanMaxSize) { +			numEntries = impl().size() - index; +		} + +		impl().validate(index, numEntries * sizeof(value_type)); +		return impl().data() + index; +	} + +	inline MemoryReadStream toStream(const index_type index = 0, size_type numEntries = kSpanMaxSize) const { +		if (numEntries == kSpanMaxSize) { +			numEntries = impl().size(); +		} + +		impl().validate(index, numEntries * sizeof(value_type)); +		return MemoryReadStream(impl().data() + index, numEntries * sizeof(value_type), DisposeAfterUse::NO); +	} + +#pragma mark - +#pragma mark SpanBase - Operators + +public: +	template <typename Other> +	inline bool operator==(const Other &other) const { +		return impl().data() == other.impl().data() && impl().size() == other.impl().size(); +	} + +	template <typename Other> +	inline bool operator!=(const Other &other) const { +		return !operator==(other); +	} + +	template <typename Other> +	inline difference_type operator-(const Other &other) const { +		return impl().data() - other.impl().data(); +	} + +	template <typename Other> +	inline bool operator<(const Other &other) const { +		return impl().data() < other.impl().data(); +	} + +	template <typename Other> +	inline bool operator<=(const Other &other) const { +		return !other.operator<(*this); +	} + +	template <typename Other> +	inline bool operator>(const Other &other) const { +		return other.operator<(*this); +	} + +	template <typename Other> +	inline bool operator>=(const Other &other) const { +		return !operator<(other); +	} + +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) +protected: +#endif +	inline bool operator_bool() const { return impl().data() != nullptr; } + +#pragma mark - +#pragma mark SpanBase - Copying + +public: +	/** +	 * Copies data from this span to a raw pointer. To only copy a portion of +	 * the span, call subspan first. +	 */ +	inline void unsafeCopyDataTo(void *target) const { +		memcpy(target, impl().data(), impl().byteSize()); +	} + +	/** +	 * Copies the data from this span to the given target span. To only copy a +	 * portion of the span, call subspan first. +	 */ +	template <typename Other> +	inline void copyDataTo(Other &target) const { +		assert((impl().byteSize() % sizeof(typename Other::value_type)) == 0); +		target.impl().validate(0, impl().byteSize(), kValidateWrite); +		memcpy(target.impl().data(), impl().data(), impl().byteSize()); +	} + +#pragma mark - +#pragma mark SpanBase - Validation + +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) +protected: +#endif +	/** +	 * @returns true if bounds are invalid. +	 */ +	inline bool checkInvalidBounds(const index_type index, const difference_type deltaInBytes) const { +		// There is a potential that large bogus values may cause arithmetic +		// overflow, so the individual operands are checked separately first. +		// Values that are not allowed to be negative are treated as unsigned to +		// reduce the number of necessary comparisons +		const size_t maxByteOffset = index * (signed)sizeof(value_type) + deltaInBytes; +		return index > impl().size() || deltaInBytes > (difference_type)impl().byteSize() || maxByteOffset > impl().byteSize(); +	} + +	inline void validate(const index_type index, const difference_type deltaInBytes, const SpanValidationMode mode = kValidateRead) const { +		/* LCOV_EXCL_START */ +		if (impl().checkInvalidBounds(index, deltaInBytes)) { +			error("%s", impl().getValidationMessage(index, deltaInBytes, mode).c_str()); +		} +		/* LCOV_EXCL_STOP */ +	} +}; + +#pragma mark - +#pragma mark SpanImpl + +template <typename ValueType, template <typename> class Derived> +class SpanImpl : public SpanBase<ValueType, Derived> { +	typedef SpanBase<ValueType, Derived> super_type; +	typedef typename AddConst<Derived<ValueType> >::type const_derived_type; +	typedef typename RemoveConst<Derived<ValueType> >::type mutable_derived_type; + +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) +	template <typename T, template <typename> class U> friend class SpanImpl; +#endif +#ifdef CXXTEST_RUNNING +	friend class ::SpanTestSuite; +#endif + +public: +	COMMON_SPAN_TYPEDEFS + +	inline SpanImpl() : super_type(), _data(nullptr), _size(0) {} + +	inline SpanImpl(const pointer data_, const size_type size_) : +		super_type(), +		_data(data_), +		_size(size_) {} + +	template <typename Other> +	inline SpanImpl(const Other &other) : +		super_type(), +		_data(other.data()), +		_size(other.size()) {} + +	inline void clear() { +		_data = nullptr; +		_size = 0; +	} + +	inline size_type size() const { return _size; } +	inline pointer data() const { return _data; } + +	inline const_iterator cbegin() const { return const_iterator(&this->impl(), 0); } +	inline const_iterator cend() const { return const_iterator(&this->impl(), size()); } +	inline const_iterator begin() const { return const_iterator(&this->impl(), 0); } +	inline const_iterator end() const { return const_iterator(&this->impl(), size()); } +	inline iterator begin() { return iterator(&this->impl(), 0); } +	inline iterator end() { return iterator(&this->impl(), size()); } + +	const String name() const { return String::format("%p", static_cast<const void *>(data())); } + +	String getValidationMessage(const index_type index, const difference_type deltaInBytes, const SpanValidationMode mode) const { +		const char *modeName; +		switch (mode) { +			case kValidateRead: +				modeName = "reading"; +				break; +			case kValidateWrite: +				modeName = "writing"; +				break; +			case kValidateSeek: +				modeName = "seeking"; +				break; +		} + +		return String::format("Access violation %s %s: %ld + %ld > %ld", +							  modeName, +							  this->impl().name().c_str(), +							  index, +							  deltaInBytes / sizeof(value_type), +							  size()); +	} + +#pragma mark - +#pragma mark SpanImpl - Subspan + +public: +	template <typename NewValueType> +	inline const Derived<NewValueType> subspan(const index_type index, size_type numEntries = kSpanMaxSize) const { +		Derived<NewValueType> span; +		populateSubspan(span, index, numEntries); +		return span; +	} + +	template <typename NewValueType> +	inline Derived<NewValueType> subspan(const index_type index, size_type numEntries = kSpanMaxSize) { +		Derived<NewValueType> span; +		populateSubspan(span, index, numEntries); +		return span; +	} + +	inline const_derived_type subspan(const index_type index, const size_type numEntries = kSpanMaxSize) const { +		return subspan<value_type>(index, numEntries); +	} + +	inline mutable_derived_type subspan(const index_type index, const size_type numEntries = kSpanMaxSize) { +		return subspan<value_type>(index, numEntries); +	} + +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) +protected: +#endif +	template <typename NewValueType> +	void populateSubspan(Derived<NewValueType> &span, const index_type index, size_type numEntries) const { +		if (numEntries == kSpanMaxSize) { +			numEntries = CLIP<size_type>(size() - index, 0, size()); +		} + +		assert(numEntries * sizeof(value_type) % sizeof(NewValueType) == 0); +		this->validate(index, numEntries * sizeof(value_type), kValidateSeek); + +		span._data = (NewValueType *)const_cast<mutable_value_type *>(_data + index); +		span._size = numEntries * sizeof(value_type) / sizeof(NewValueType); +	} + +#pragma mark - +#pragma mark SpanImpl - Allocation + +private: +	typedef typename RemoveConst<value_type>::type mutable_value_type; +	typedef Derived<mutable_value_type> mutable_value_derived_type; + +public: +	mutable_value_derived_type &allocate(const size_type numEntries) { +		assert(_data == nullptr); +		assert(numEntries != kSpanMaxSize); +		_data = new mutable_value_type[numEntries]; +		_size = numEntries; +		return (mutable_value_derived_type &)const_cast<Derived<value_type> &>(this->impl()); +	} + +	template <typename Other> +	mutable_value_derived_type &allocateFromSpan(const Other &other) { +		assert(_data == nullptr); +		assert(sizeof(value_type) == sizeof(typename Other::value_type)); +		_data = new mutable_value_type[other.size()]; +		_size = other.size(); +		copy(other.begin(), other.end(), const_cast<mutable_value_type *>(_data)); +		return (mutable_value_derived_type &)const_cast<Derived<value_type> &>(this->impl()); +	} + +	mutable_value_derived_type &allocateFromStream(SeekableReadStream &stream, size_type numEntries = kSpanMaxSize) { +		if (numEntries == kSpanMaxSize) { +			numEntries = (stream.size() - stream.pos()) / sizeof(value_type); +		} + +		const uint32 bytesRequested = numEntries * sizeof(value_type); +		assert(stream.pos() + bytesRequested <= (uint)stream.size()); +		allocate(numEntries); +		const uint32 bytesRead = stream.read((void *)const_cast<mutable_value_type *>(_data), bytesRequested); +		assert(bytesRead == bytesRequested); +		return (mutable_value_derived_type &)const_cast<Derived<value_type> &>(this->impl()); +	} + +	value_type *_data; +	size_type _size; +}; + +#pragma mark - +#pragma mark Span + +template <typename ValueType> +class Span : public SpanImpl<ValueType, Span> { +	typedef SpanImpl<ValueType, ::Common::Span> super_type; +	typedef typename AddConst<Span<ValueType> >::type const_derived_type; +	typedef typename RemoveConst<Span<ValueType> >::type mutable_derived_type; +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) +	template <typename T> friend class Span; +#endif + +public: +	COMMON_SPAN_TYPEDEFS + +	inline Span() : super_type() {} + +	inline Span(const pointer data_, const size_type size_) : super_type(data_, size_) {} + +	// Allows unrelated sibling classes like NamedSpan to assign to superclass +	// siblings like Span +	template <typename Other> +	inline Span(const Other &other) : super_type(other) {} +}; + +#pragma mark - +#pragma mark NamedSpanImpl + +template <typename ValueType, template <typename> class Derived> +class NamedSpanImpl : public SpanImpl<ValueType, Derived> { +	typedef SpanImpl<ValueType, Derived> super_type; +	typedef typename AddConst<Derived<ValueType> >::type const_derived_type; +	typedef typename RemoveConst<Derived<ValueType> >::type mutable_derived_type; + +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) +	template <typename T, template <typename> class U> friend class NamedSpanImpl; +#endif +#ifdef CXXTEST_RUNNING +	friend class ::SpanTestSuite; +#endif + +public: +	COMMON_SPAN_TYPEDEFS + +	inline NamedSpanImpl() : super_type(), _name(), _sourceByteOffset(0) {} + +	inline NamedSpanImpl(const pointer data_, +						 const size_type size_, +						 const String &name_ = String(), +						 const size_type sourceByteOffset_ = 0) : +		super_type(data_, size_), +		_name(name_), +		_sourceByteOffset(sourceByteOffset_) {} + +	template <typename Other> +	inline NamedSpanImpl(const Other &other) : +		super_type(other), +		_name(other.name()), +		_sourceByteOffset(other.sourceByteOffset()) {} + +	inline void clear() { +		super_type::clear(); +		_name.clear(); +		_sourceByteOffset = 0; +	} + +	const String &name() const { return _name; } +	String &name() { return _name; } + +	const size_type &sourceByteOffset() const { return _sourceByteOffset; } +	size_type &sourceByteOffset() { return _sourceByteOffset; } + +private: +	String _name; +	size_type _sourceByteOffset; + +#pragma mark - +#pragma mark NamedSpanImpl - Subspan + +public: +	template <typename NewValueType> +	inline const Derived<NewValueType> subspan(const index_type index, const size_type numEntries = kSpanMaxSize, const String &name_ = String(), const size_type sourceByteOffset_ = kSpanKeepOffset) const { +		Derived<NewValueType> span; +		populateSubspan(span, index, numEntries, name_, sourceByteOffset_); +		return span; +	} + +	template <typename NewValueType> +	inline Derived<NewValueType> subspan(const index_type index, const size_type numEntries = kSpanMaxSize, const String &name_ = String(), const size_type sourceByteOffset_ = kSpanKeepOffset) { +		Derived<NewValueType> span; +		populateSubspan(span, index, numEntries, name_, sourceByteOffset_); +		return span; +	} + +	inline const_derived_type subspan(const index_type index, const size_type numEntries = kSpanMaxSize, const String &name_ = String(), const size_type sourceByteOffset_ = kSpanKeepOffset) const { +		return subspan<value_type>(index, numEntries, name_, sourceByteOffset_); +	} + +	inline mutable_derived_type subspan(const index_type index, const size_type numEntries = kSpanMaxSize, const String &name_ = String(), const size_type sourceByteOffset_ = kSpanKeepOffset) { +		return subspan<value_type>(index, numEntries, name_, sourceByteOffset_); +	} + +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) +protected: +#endif +	template <typename NewValueType> +	void populateSubspan(Derived<NewValueType> &span, const index_type index, size_type numEntries, const String &name_, const size_type sourceByteOffset_ = kSpanKeepOffset) const { +		super_type::template populateSubspan<NewValueType>(span, index, numEntries); + +		if (name_.empty()) { +			span._name = _name; +		} else { +			span._name = name_; +		} + +		if (sourceByteOffset_ == kSpanKeepOffset) { +			span._sourceByteOffset = _sourceByteOffset + index * sizeof(value_type); +		} else { +			span._sourceByteOffset = sourceByteOffset_; +		} +	} + +#pragma mark - +#pragma mark NamedSpanImpl - Validation + +public: +	String getValidationMessage(const index_type index, const difference_type deltaInBytes, const SpanValidationMode mode) const { +		const index_type indexInBytes = index * sizeof(value_type); +		const size_type maxSizeInBytes = this->impl().byteSize(); + +		return super_type::getValidationMessage(index, deltaInBytes, mode) + +			String::format(" (abs: %ld + %ld > %ld)", +						   this->impl().sourceByteOffset() + indexInBytes, +						   deltaInBytes, +						   this->impl().sourceByteOffset() + maxSizeInBytes); +	} + +#pragma mark - +#pragma mark NamedSpanImpl - Allocation + +private: +	typedef typename RemoveConst<value_type>::type mutable_value_type; +	typedef Derived<mutable_value_type> mutable_value_derived_type; + +public: +	mutable_value_derived_type &allocate(const size_type numEntries, const String &name_ = String()) { +		super_type::allocate(numEntries); +		_name = name_; +		_sourceByteOffset = 0; +		return (mutable_value_derived_type &)const_cast<Derived<value_type> &>(this->impl()); +	} + +	template <typename OtherValueType> +	mutable_value_derived_type &allocateFromSpan(const NamedSpanImpl<OtherValueType, Derived> &other) { +		super_type::allocateFromSpan(other); +		_name = other.name(); +		_sourceByteOffset = other.sourceByteOffset(); +		return (mutable_value_derived_type &)const_cast<Derived<value_type> &>(this->impl()); +	} + +	template <typename OtherValueType, template <typename> class OtherDerived> +	mutable_value_derived_type &allocateFromSpan(const SpanImpl<OtherValueType, OtherDerived> &other) { +		super_type::allocateFromSpan(other); +		return (mutable_value_derived_type &)const_cast<Derived<value_type> &>(this->impl()); +	} + +	mutable_value_derived_type &allocateFromStream(SeekableReadStream &stream, size_type numEntries = kSpanMaxSize, const String &name_ = String()) { +		super_type::allocateFromStream(stream, numEntries); +		_name = name_; +		_sourceByteOffset = 0; +		return (mutable_value_derived_type &)const_cast<Derived<value_type> &>(this->impl()); +	} + +	mutable_value_derived_type &allocateFromStream(File &file, const size_type numEntries = kSpanMaxSize) { +		return allocateFromStream(file, numEntries, file.getName()); +	} +}; + +#pragma mark - +#pragma mark NamedSpan + +template <typename ValueType> +class NamedSpan : public NamedSpanImpl<ValueType, NamedSpan> { +	typedef NamedSpanImpl<ValueType, ::Common::NamedSpan> super_type; + +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) +	template <typename T> friend class NamedSpan; +#endif + +public: +	COMMON_SPAN_TYPEDEFS + +	inline NamedSpan() : super_type() {} + +	inline NamedSpan(const pointer data_, +					 const size_type size_, +					 const String &name_ = String(), +					 const size_type sourceByteOffset_ = 0) : +		super_type(data_, size_, name_, sourceByteOffset_) {} + +	template <typename Other> +	inline NamedSpan(const Other &other) : super_type(other) {} +}; + +#pragma mark - +#pragma mark SpanOwner + +/** + * Similar to ScopedPtr, but allows holding and disposing pointers inside Spans + * without requiring an additional pointer to data, and with copyability. + */ +template <typename OwnedSpan> +class SpanOwner : public SafeBool<SpanOwner<OwnedSpan> > { +	typedef typename OwnedSpan::value_type value_type; +	typedef typename OwnedSpan::size_type size_type; +	typedef typename OwnedSpan::index_type index_type; +	typedef typename OwnedSpan::pointer pointer; +	typedef typename OwnedSpan::reference reference; +	typedef typename OwnedSpan::const_reference const_reference; + +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) +	template <typename T, typename U> friend struct SafeBool; +#endif + +public: +	inline SpanOwner() : _span() {} + +	inline SpanOwner(const OwnedSpan &span) : _span(span) {} + +	/** +	 * Creates a new owned copy of the memory from the other SpanOwner. +	 */ +	inline SpanOwner(const SpanOwner &other) { +		// Allocating memory when copy-constructing from an unallocated owner +		// will break the new owner by making it appear allocated even though +		// it doesn't (and shouldn't) contain data +		if (!other) { +			SpanOwner(); +			return; +		} + +		_span.allocateFromSpan(other._span); +	} + +	/** +	 * Transfers ownership of the Span from the other owner to this owner. +	 * If this owner already holds another Span, the old Span will be destroyed. +	 */ +	inline SpanOwner &operator=(SpanOwner &other) { +		if (this == &other) { +			return *this; +		} + +		if (_span.data()) { +			delete[] const_cast<typename RemoveConst<value_type>::type *>(_span.data()); +		} +		_span = other._span; +		other.release(); +		return *this; +	} + +	inline ~SpanOwner() { +		delete[] const_cast<typename RemoveConst<value_type>::type *>(_span.data()); +	} + +	/** +	 * Releases the memory owned by this SpanOwner to the caller. +	 */ +	inline pointer release() { +		pointer data = _span.data(); +		_span.clear(); +		return data; +	} + +	/** +	 * Destroys the memory owned by this owner. +	 */ +	inline void clear() { +		delete[] const_cast<typename RemoveConst<value_type>::type *>(_span.data()); +		_span.clear(); +	} + +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) +protected: +#endif +	inline bool operator_bool() const { return _span; } + +private: +	OwnedSpan _span; + +#pragma mark - +#pragma mark SpanOwner - Data access + +public: +	inline const OwnedSpan &operator*() const { return _span; } +	inline OwnedSpan &operator*() { return _span; } + +	inline const OwnedSpan *operator->() const { return &_span; } +	inline OwnedSpan *operator->() { return &_span; } + +	inline const_reference operator[](const index_type index) const { return _span[index]; } +	inline reference operator[](const index_type index) { return _span[index]; } +}; + +} // End of namespace Common + +#endif diff --git a/common/type-traits.h b/common/type-traits.h new file mode 100644 index 0000000000..4b17317374 --- /dev/null +++ b/common/type-traits.h @@ -0,0 +1,34 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef COMMON_TYPE_TRAITS_H +#define COMMON_TYPE_TRAITS_H + +namespace Common { +	template <bool b, class T, class U> struct Conditional { typedef T type; }; +	template <class T, class U> struct Conditional<false, T, U> { typedef U type; }; +	template <typename T> struct RemoveConst { typedef T type; }; +	template <typename T> struct RemoveConst<const T> { typedef T type; }; +	template <typename T> struct AddConst { typedef const T type; }; +} // End of namespace Common + +#endif diff --git a/test/common/span.h b/test/common/span.h new file mode 100644 index 0000000000..d73a2e2266 --- /dev/null +++ b/test/common/span.h @@ -0,0 +1,789 @@ +#include <cxxtest/TestSuite.h> + +class SpanTestSuite; + +#include "common/span.h" +#include "common/str.h" + +class SpanTestSuite : public CxxTest::TestSuite { +	struct Foo { +		int a; +	}; + +	template <typename ValueType, template <typename> class Derived> +	class SiblingSpanImpl : public Common::SpanImpl<ValueType, Derived> { +		typedef Common::SpanImpl<ValueType, Derived> super_type; +	public: +		COMMON_SPAN_TYPEDEFS +		SiblingSpanImpl() : super_type() {} +		SiblingSpanImpl(pointer data_, size_type size_) : super_type(data_, size_) {} +	}; + +	template <typename ValueType> +	class SiblingSpan : public SiblingSpanImpl<ValueType, SiblingSpan> { +		typedef SiblingSpanImpl<ValueType, ::SpanTestSuite::SiblingSpan> super_type; +	public: +		COMMON_SPAN_TYPEDEFS +		SiblingSpan() : super_type() {} +		SiblingSpan(pointer data_, size_type size_) : super_type(data_, size_) {} +	}; + +	template <typename ValueType, template <typename> class Derived> +	class SubSpanImpl : public Common::NamedSpanImpl<ValueType, Derived> { +		typedef Common::NamedSpanImpl<ValueType, Derived> super_type; +	public: +		COMMON_SPAN_TYPEDEFS +		SubSpanImpl() : super_type() {} +		SubSpanImpl(pointer data_, +					size_type size_, +					const Common::String &name_ = Common::String(), +					const size_type sourceByteOffset_ = 0) : +			super_type(data_, size_, name_, sourceByteOffset_) {} + +		template <typename Other> +		SubSpanImpl(const Other &other) : super_type(other) {} +	}; + +	template <typename ValueType> +	class SubSpan : public SubSpanImpl<ValueType, SubSpan> { +		typedef SubSpanImpl<ValueType, ::SpanTestSuite::SubSpan> super_type; +	public: +		COMMON_SPAN_TYPEDEFS +		SubSpan() : super_type() {} +		SubSpan(pointer data_, +				size_type size_, +				const Common::String &name_ = Common::String(), +				const size_type sourceByteOffset_ = 0) : +			super_type(data_, size_, name_, sourceByteOffset_) {} + +		template <typename Other> +		SubSpan(const Other &other) : super_type(other) {} +	}; + +public: +	void test_sibling_span() { +		byte data[] = { 'h', 'e', 'l', 'l', 'o' }; +		SiblingSpan<byte> ss(data, sizeof(data)); +		Common::Span<byte> superInstance = ss; +		TS_ASSERT_EQUALS(ss.data(), data); +		TS_ASSERT_EQUALS(superInstance.data(), data); +	} + +	void test_sub_span() { +		byte data[] = { 'h', 'e', 'l', 'l', 'o' }; +		SubSpan<byte> ss(data, sizeof(data), "custom subspan"); +		Common::NamedSpan<byte> namedSuper = ss; +		Common::Span<byte> unnamedSuper = ss; +		TS_ASSERT(ss.name() == "custom subspan"); +		TS_ASSERT(namedSuper.name() == ss.name()); +		TS_ASSERT(unnamedSuper.name() == Common::String::format("%p", (void *)data)); +	} + +	void test_span_iterator_const() { +		byte data[] = { 'h', 'e', 'l', 'l', 'o' }; +		const Common::Span<byte> span(data, sizeof(data)); + +		Common::Span<byte>::const_iterator it = span.cbegin(); + +		Common::Span<byte>::const_iterator sameIt(it); + +		TS_ASSERT_EQUALS(sameIt, it); + +		uint i; +		for (i = 0; it != span.cend(); ++i, ++it) { +			TS_ASSERT_EQUALS(*it, data[i]); +			TS_ASSERT_LESS_THAN(i, sizeof(data)); +		} +		TS_ASSERT_EQUALS(i, sizeof(data)); + +		it = span.cend() - 1; +		for (i = sizeof(data) - 1; it != span.cbegin(); --i, --it) { +			TS_ASSERT_EQUALS(data[i], *it); +		} +		TS_ASSERT_EQUALS(i, 0U); + +		it = span.cbegin(); + +		it += 4; +		TS_ASSERT_EQUALS(data[4], *it); + +		it -= 4; +		TS_ASSERT_EQUALS(data[0], *it); + +		TS_ASSERT_EQUALS(data[0], *it++); + +		TS_ASSERT_EQUALS(data[1], *it--); + +		TS_ASSERT_EQUALS(span.cend() - span.cbegin(), 5); + +		TS_ASSERT_EQUALS(*(span.cbegin() + 4), data[4]); + +		TS_ASSERT_EQUALS(*(span.cend() - 4), data[1]); + +		TS_ASSERT(span.cbegin() < span.cend()); + +		TS_ASSERT(span.cbegin() <= span.cend()); +		TS_ASSERT(span.cbegin() <= span.cbegin()); + +		TS_ASSERT(span.cend() > span.cbegin()); + +		TS_ASSERT(span.cend() >= span.cbegin()); +		TS_ASSERT(span.cend() >= span.cend()); +	} + +	void test_span_iterator() { +		byte data[] = { 'h', 'e', 'l', 'l', 'o' }; +		Common::Span<byte> span(data, sizeof(data)); + +		// empty iterator should default construct OK +		Common::Span<byte>::iterator defaultIt; + +		Common::Span<byte>::iterator it = span.begin(); + +		Common::Span<byte>::iterator sameIt(it); + +		TS_ASSERT_EQUALS(sameIt, it); + +		uint i; +		for (i = 0; it != span.end(); ++i, ++it) { +			TS_ASSERT_EQUALS(*it, data[i]); +			TS_ASSERT_LESS_THAN(i, sizeof(data)); +		} +		TS_ASSERT_EQUALS(i, sizeof(data)); + +		it = span.end() - 1; +		for (i = sizeof(data) - 1; it != span.begin(); --i, --it) { +			TS_ASSERT_EQUALS(data[i], *it); +		} +		TS_ASSERT_EQUALS(i, 0U); + +		it = span.begin(); + +		it += 4; +		TS_ASSERT_EQUALS(data[4], *it); + +		it -= 4; +		TS_ASSERT_EQUALS(data[0], *it); + +		TS_ASSERT_EQUALS(data[0], *it++); + +		TS_ASSERT_EQUALS(data[1], *it--); + +		TS_ASSERT_EQUALS(span.end() - span.begin(), 5); + +		TS_ASSERT_EQUALS(*(span.begin() + 4), data[4]); + +		TS_ASSERT_EQUALS(*(span.end() - 4), data[1]); + +		TS_ASSERT(span.begin() < span.end()); + +		TS_ASSERT(span.begin() <= span.end()); +		TS_ASSERT(span.begin() <= span.begin()); + +		TS_ASSERT(span.end() > span.begin()); + +		TS_ASSERT(span.end() >= span.begin()); +		TS_ASSERT(span.end() >= span.end()); + +		it = span.begin(); +		for (i = 0; it != span.end(); ++i, ++it) { +			*it = 'a' + i; +		} + +		it = span.begin(); +		for (i = 0; it != span.end(); ++i, ++it) { +			TS_ASSERT_EQUALS(*it, 'a' + i); +			TS_ASSERT_EQUALS(data[i], 'a' + i); +		} +	} + +	void test_span_iterator_integers() { +		const byte data[] = { 0xFF, 1, 2, 3, 2, 1, 0xFF }; +		Common::Span<const byte> span(data, sizeof(data)); +		Common::Span<const byte>::const_iterator it = span.cbegin(); + +		TS_ASSERT_EQUALS(it.getInt8(), -1); +		TS_ASSERT_EQUALS(it.getUint8(), 255); +		TS_ASSERT_EQUALS(it.getInt16BE(), -255); +		TS_ASSERT_EQUALS(it.getUint16BE(), 65281U); +		TS_ASSERT_EQUALS((it + 5).getInt16LE(), -255); +		TS_ASSERT_EQUALS((it + 5).getUint16LE(), 65281U); +		TS_ASSERT_EQUALS(it.getUint24LE(), 131583U); +#ifdef SCUMM_LITTLE_ENDIAN +		TS_ASSERT_EQUALS((it + 3).getUint32(), 4278256131U); +#elif SCUMM_BIG_ENDIAN +		TS_ASSERT_EQUALS(it.getUint32(), 4278256131U); +#else +#error No endianness detected +#endif +		TS_ASSERT_EQUALS(it.getInt32BE(), -16711165); +		TS_ASSERT_EQUALS(it.getUint32BE(), 4278256131U); +		TS_ASSERT_EQUALS((it + 3).getInt32LE(), -16711165); +		TS_ASSERT_EQUALS((it + 3).getUint32LE(), 4278256131U); +	} + +	void test_span_iterator_ptr() { +		Foo foo[2]; +		foo[0].a = 1; +		foo[1].a = 2; + +		const Common::Span<Foo> span(foo, 2); +		Common::Span<Foo>::const_iterator it = span.cbegin(); +		TS_ASSERT_EQUALS(it->a, 1); +		++it; +		TS_ASSERT_EQUALS(it->a, 2); + +		TS_ASSERT_EQUALS(it[0].a, 2); +		TS_ASSERT_EQUALS(it[-1].a, 1); +		--it; +		TS_ASSERT_EQUALS(it[1].a, 2); +	} + +	void test_span_owner() { +		Common::SpanOwner<Common::Span<byte> > owner; +		owner->allocate(3); +		owner[0] = 'a'; +		owner[1] = 'b'; +		owner[2] = 'c'; +		for (int i = 0; i < 3; ++i) { +			TS_ASSERT_EQUALS(owner->getUint8At(i), 'a' + i); +			TS_ASSERT_EQUALS((*owner)[i], 'a' + i); +		} + +		{ +			Common::SpanOwner<Common::NamedSpan<byte> > owner2; +			TS_ASSERT(owner2->data() == nullptr); +			owner2->allocateFromSpan(*owner); +			TS_ASSERT(owner2->data() != nullptr); +			TS_ASSERT_DIFFERS(owner->data(), owner2->data()); + +			for (int i = 0; i < 3; ++i) { +				TS_ASSERT_EQUALS(owner2->getUint8At(i), 'a' + i); +				TS_ASSERT_EQUALS((*owner2)[i], 'a' + i); +			} + +			TS_ASSERT_EQUALS((bool)owner2, true); +			owner2.release(); +			TS_ASSERT_EQUALS((bool)owner2, false); +		} + +		{ +			Common::SpanOwner<Common::Span<byte> > owner2; +			TS_ASSERT_EQUALS((bool)owner, true); +			void *dataPtr = owner->data(); +			owner2 = owner; +			TS_ASSERT_EQUALS((bool)owner, false); +			TS_ASSERT(owner->data() == nullptr); +			TS_ASSERT_EQUALS(owner2->data(), dataPtr); + +			// tests destruction of held pointer by reassignment +			owner2 = owner; + +			// tests nullipotence of assignment to self +			dataPtr = owner2->data(); +			owner2 = owner2; +			TS_ASSERT(owner2->data() == dataPtr); +		} + +		{ +			char *data = new char[6]; +			Common::strlcpy(data, "hello", 6); +			const Common::SpanOwner<Common::Span<const char> > constOwner(Common::Span<const char>(data, 6)); +			TS_ASSERT_EQUALS((*constOwner)[0], 'h'); +			TS_ASSERT_EQUALS(constOwner->getUint8At(1), 'e'); +			TS_ASSERT_EQUALS(constOwner[2], 'l'); +		} + +		{ +			TS_ASSERT_EQUALS((bool)owner, false); +			Common::SpanOwner<Common::Span<byte> > owner2(owner); +			TS_ASSERT_EQUALS((bool)owner2, false); +		} + +		{ +			owner->allocate(1); +			TS_ASSERT_EQUALS((bool)owner, true); +			Common::SpanOwner<Common::Span<byte> > owner2(owner); +			TS_ASSERT_EQUALS((bool)owner2, true); +			TS_ASSERT_DIFFERS(owner->data(), owner2->data()); +		} + +		{ +			TS_ASSERT_EQUALS((bool)owner, true); +			void *dataPtr = owner->data(); +			TS_ASSERT_EQUALS(owner.release(), dataPtr); +			TS_ASSERT_EQUALS((bool)owner, false); +		} +	} + +	void test_span_owner_named_span() { +		Common::SpanOwner<Common::NamedSpan<byte> > owner; +		owner->allocate(3, "foo"); +		owner[0] = 'a'; +		owner[1] = 'b'; +		owner[2] = 'c'; +		for (int i = 0; i < 3; ++i) { +			TS_ASSERT_EQUALS(owner->getUint8At(i), 'a' + i); +			TS_ASSERT_EQUALS((*owner)[i], 'a' + i); +		} +		TS_ASSERT(owner->name() == "foo"); + +		{ +			Common::SpanOwner<Common::NamedSpan<byte> > owner2; +			TS_ASSERT(owner2->data() == nullptr); +			owner2->allocateFromSpan(*owner); +			TS_ASSERT(owner2->data() != nullptr); +			TS_ASSERT_DIFFERS(owner->data(), owner2->data()); +			TS_ASSERT(owner2->name() == "foo"); + +			for (int i = 0; i < 3; ++i) { +				TS_ASSERT_EQUALS(owner2->getUint8At(i), 'a' + i); +				TS_ASSERT_EQUALS((*owner2)[i], 'a' + i); +			} + +			TS_ASSERT_EQUALS((bool)owner2, true); +			owner2.release(); +			TS_ASSERT_EQUALS((bool)owner2, false); +		} + +		{ +			Common::SpanOwner<Common::NamedSpan<byte> > owner2; +			TS_ASSERT_EQUALS((bool)owner, true); +			void *dataPtr = owner->data(); +			owner2 = owner; +			TS_ASSERT_EQUALS((bool)owner, false); +			TS_ASSERT(owner->data() == nullptr); +			TS_ASSERT_EQUALS(owner2->data(), dataPtr); + +			// tests destruction of held pointer by reassignment +			owner2 = owner; +		} + +		{ +			char *data = new char[6]; +			Common::strlcpy(data, "hello", 6); +			const Common::SpanOwner<Common::NamedSpan<const char> > constOwner(Common::NamedSpan<const char>(data, 6)); +			TS_ASSERT_EQUALS((*constOwner)[0], 'h'); +			TS_ASSERT_EQUALS(constOwner->getUint8At(1), 'e'); +			TS_ASSERT_EQUALS(constOwner[2], 'l'); +		} + +		{ +			TS_ASSERT_EQUALS((bool)owner, false); +			Common::SpanOwner<Common::NamedSpan<byte> > owner2(owner); +			TS_ASSERT_EQUALS((bool)owner2, false); +		} + +		{ +			owner->allocate(1); +			TS_ASSERT_EQUALS((bool)owner, true); +			Common::SpanOwner<Common::NamedSpan<byte> > owner2(owner); +			TS_ASSERT_EQUALS((bool)owner2, true); +			TS_ASSERT_DIFFERS(owner->data(), owner2->data()); +		} + +		{ +			TS_ASSERT_EQUALS((bool)owner, true); +			void *dataPtr = owner->data(); +			TS_ASSERT_EQUALS(owner.release(), dataPtr); +			TS_ASSERT_EQUALS((bool)owner, false); +		} +	} + +	void test_span_allocate_from_stream() { +		byte data[] = "hello"; +		Common::MemoryReadStream stream(data, sizeof(data)); +		Common::SpanOwner<Common::Span<byte> > owner; +		owner->allocateFromStream(stream, 2); +		TS_ASSERT(owner->data() != data); +		TS_ASSERT_EQUALS(owner->size(), 2U); +		TS_ASSERT_EQUALS(owner[0], 'h'); +		TS_ASSERT_EQUALS(owner[1], 'e'); +		owner.clear(); +		TS_ASSERT(owner->data() == nullptr); +		stream.seek(0, SEEK_SET); + +		owner->allocateFromStream(stream); +		TS_ASSERT(owner->data() != data); +		TS_ASSERT_EQUALS(owner->size(), sizeof(data)); +		TS_ASSERT_EQUALS(owner[0], 'h'); +		TS_ASSERT_EQUALS(owner[1], 'e'); +		TS_ASSERT_EQUALS(owner[2], 'l'); +		TS_ASSERT_EQUALS(owner[3], 'l'); +		TS_ASSERT_EQUALS(owner[4], 'o'); + +		Common::SpanOwner<Common::NamedSpan<const byte> > owner2; +		stream.seek(0, SEEK_SET); +		owner2->allocateFromStream(stream, Common::kSpanMaxSize, "streamname"); +		TS_ASSERT(owner2->data() != data); +		TS_ASSERT_EQUALS(owner2->size(), sizeof(data)); +		TS_ASSERT_EQUALS(owner2[0], 'h'); +		TS_ASSERT_EQUALS(owner2[1], 'e'); +		TS_ASSERT_EQUALS(owner2[2], 'l'); +		TS_ASSERT_EQUALS(owner2[3], 'l'); +		TS_ASSERT_EQUALS(owner2[4], 'o'); +		TS_ASSERT_EQUALS(owner2->name(), "streamname"); +	} + +	void test_span_byte() { +		{ +			byte data[] = { 'h', 'e', 'l', 'l', 'o' }; +			Common::Span<byte> span(data, sizeof(data)); + +			TS_ASSERT_EQUALS(span.size(), sizeof(data)); +			TS_ASSERT_EQUALS(span.byteSize(), sizeof(data)); + +			Common::Span<byte> other(span); +			TS_ASSERT_EQUALS(span, other); +			other.clear(); +			TS_ASSERT(span != other); + +			TS_ASSERT_EQUALS(span[0], 'h'); +			TS_ASSERT_EQUALS(span[1], 'e'); +			span[1] = 'o'; +			TS_ASSERT_EQUALS(span[1], 'o'); + +			TS_ASSERT((bool)span); +			span.clear(); +			TS_ASSERT(!(bool)span); +		} + +		{ +			byte data[] = { 'h', 'e', 'l', 'l', 'o' }; +			const Common::Span<const byte> span(data, sizeof(data)); + +			TS_ASSERT_EQUALS(span.size(), sizeof(data)); +			TS_ASSERT_EQUALS(span.byteSize(), sizeof(data)); + +			const Common::Span<const byte> other(span); +			TS_ASSERT_EQUALS(span, other); + +			TS_ASSERT_EQUALS(span[0], 'h'); +			TS_ASSERT_EQUALS(span[1], 'e'); +		} +	} + +	void test_span_integers() { +		const byte data[] = { 0xFF, 1, 2, 3, 2, 1, 0xFF }; +		Common::Span<const byte> span(data, sizeof(data)); + +		TS_ASSERT_EQUALS(span[0], 255); +		TS_ASSERT_EQUALS(span.getInt8At(0), -1); +		TS_ASSERT_EQUALS(span.getUint8At(0), 255U); +		TS_ASSERT_EQUALS(span.getInt16BEAt(0), -255); +		TS_ASSERT_EQUALS(span.getUint16BEAt(0), 65281U); +		TS_ASSERT_EQUALS(span.getInt16LEAt(5), -255); +		TS_ASSERT_EQUALS(span.getUint16LEAt(5), 65281U); +		TS_ASSERT_EQUALS(span.getUint24LEAt(0), 131583U); +		TS_ASSERT_EQUALS(span.getInt32BEAt(0), -16711165); +		TS_ASSERT_EQUALS(span.getUint32BEAt(0), 4278256131U); +		TS_ASSERT_EQUALS(span.getInt32LEAt(3), -16711165); +		TS_ASSERT_EQUALS(span.getUint32LEAt(3), 4278256131U); + +#ifdef SCUMM_LITTLE_ENDIAN +		TS_ASSERT_EQUALS(span.getUint32At(3), 4278256131U); +#elif SCUMM_BIG_ENDIAN +		TS_ASSERT_EQUALS(span.getUint32At(0), 4278256131U); +#else +#error No endianness detected +#endif +	} + +	void test_span_string() { +		char data[] = "hello"; +		Common::Span<char> span(data, sizeof(data)); +		TS_ASSERT_EQUALS(span[sizeof(data) - 1], '\0'); + +		TS_ASSERT(span.getStringAt(0) == data); +		TS_ASSERT(span.getStringAt(0, 2) == "he"); +		span[3] = '\0'; +		TS_ASSERT(span.getStringAt(0) == "hel"); +	} + +	void test_span_unsafe_data() { +		char data[] = "hello"; +		Common::Span<char> span(data, sizeof(data)); + +		char *ptr = span.getUnsafeDataAt(0, 6); +		TS_ASSERT_EQUALS(ptr, data); +		ptr = span.getUnsafeDataAt(0); +		TS_ASSERT_EQUALS(ptr, data); + +		const Common::Span<const char> span2(data, sizeof(data)); +		const char *ptr2 = span2.getUnsafeDataAt(0, 6); +		TS_ASSERT_EQUALS(ptr2, data); +		ptr2 = span2.getUnsafeDataAt(0); +		TS_ASSERT_EQUALS(ptr2, data); +	} + +	void test_span_subspan() { +		{ +			byte data[] = { 1, 2, 3, 4, 5, 6 }; +			Common::Span<byte> span(data, sizeof(data)); + +			TS_ASSERT_EQUALS(span.subspan(0).size(), sizeof(data) - 0); +			TS_ASSERT_EQUALS(span.subspan(2).size(), sizeof(data) - 2); +			TS_ASSERT_EQUALS(span.subspan(2, 2).size(), 2U); +			TS_ASSERT_EQUALS(span.subspan<uint16>(0).size(), sizeof(data) / 2); +			TS_ASSERT_EQUALS(span.subspan<uint16>(0).byteSize(), sizeof(data)); +			TS_ASSERT_EQUALS(span.subspan<uint16>(0, 2).size(), 1U); +			TS_ASSERT_EQUALS(span.subspan<uint16>(0, 2).byteSize(), 2U); + +#ifdef SCUMM_LITTLE_ENDIAN +			TS_ASSERT_EQUALS(span.subspan<uint16>(0)[1], 4 << 8 | 3); +#elif SCUMM_BIG_ENDIAN +			TS_ASSERT_EQUALS(span.subspan<uint16>(0)[1], 3 << 8 | 4); +#else +#error No endianness detected +#endif + +			Common::Span<uint16> shortSpan = span.subspan<uint16>(0); +			TS_ASSERT_EQUALS(shortSpan.byteSize(), span.byteSize()); +			TS_ASSERT(shortSpan.size() != span.size()); +			shortSpan[1] = 0xFFFF; +			Common::Span<byte> byteSpan = shortSpan.subspan<byte>(1); +			TS_ASSERT_EQUALS(byteSpan.size(), sizeof(data) - 1 * sizeof(uint16)); +			TS_ASSERT_EQUALS(byteSpan[0], 0xFF); +			TS_ASSERT_EQUALS(byteSpan[1], 0xFF); +		} + +		{ +			byte data[] = { 1, 2, 3, 4, 5, 6 }; +			const Common::Span<const byte> span(data, sizeof(data)); + +			TS_ASSERT_EQUALS(span.subspan(0).size(), sizeof(data) - 0); +			TS_ASSERT_EQUALS(span.subspan(2).size(), sizeof(data) - 2); +			TS_ASSERT_EQUALS(span.subspan(2, 2).size(), 2U); +			TS_ASSERT_EQUALS(span.subspan<uint16>(0).size(), sizeof(data) / 2); +			TS_ASSERT_EQUALS(span.subspan<uint16>(0).byteSize(), sizeof(data)); +			TS_ASSERT_EQUALS(span.subspan<uint16>(0, 2).size(), 1U); +			TS_ASSERT_EQUALS(span.subspan<uint16>(0, 2).byteSize(), 2U); + +#ifdef SCUMM_LITTLE_ENDIAN +			TS_ASSERT_EQUALS(span.subspan<uint16>(0)[1], 4 << 8 | 3); +#elif SCUMM_BIG_ENDIAN +			TS_ASSERT_EQUALS(span.subspan<uint16>(0)[1], 3 << 8 | 4); +#else +#error No endianness detected +#endif + +			const Common::Span<uint16> shortSpan = span.subspan<uint16>(0); +			TS_ASSERT_EQUALS(shortSpan.byteSize(), span.byteSize()); +			TS_ASSERT(shortSpan.size() != span.size()); +			Common::Span<byte> byteSpan = shortSpan.subspan<byte>(1); +			TS_ASSERT_EQUALS(byteSpan.size(), sizeof(data) - 1 * sizeof(uint16)); +			TS_ASSERT_EQUALS(byteSpan[0], 3); +			TS_ASSERT_EQUALS(byteSpan[1], 4); +		} +	} + +	void test_span_to_stream() { +		const byte data[] = { 0, 1, 2, 3 }; +		Common::Span<const byte> span(data, sizeof(data)); + +		{ +			Common::MemoryReadStream stream(span.toStream(1, 2)); +			byte out; +			TS_ASSERT_EQUALS(stream.read(&out, 1), 1U); +			TS_ASSERT_EQUALS(out, 1); +			TS_ASSERT_EQUALS(stream.read(&out, 1), 1U); +			TS_ASSERT_EQUALS(out, 2); +			TS_ASSERT_EQUALS(stream.read(&out, 1), 0U); +		} + +		{ +			Common::MemoryReadStream stream = span.toStream(); +			byte out; +			TS_ASSERT_EQUALS(stream.read(&out, 1), 1U); +			TS_ASSERT_EQUALS(out, 0); +			TS_ASSERT_EQUALS(stream.read(&out, 1), 1U); +			TS_ASSERT_EQUALS(out, 1); +			TS_ASSERT_EQUALS(stream.read(&out, 1), 1U); +			TS_ASSERT_EQUALS(out, 2); +			TS_ASSERT_EQUALS(stream.read(&out, 1), 1U); +			TS_ASSERT_EQUALS(out, 3); +			TS_ASSERT_EQUALS(stream.read(&out, 1), 0U); +		} +	} + +	void test_span_copying() { +		const byte data[] = { 0, 1, 2, 3, 4, 5 }; +		Common::Span<const byte> span(data, sizeof(data)); + +		byte targetData[sizeof(data)] = {}; +		Common::Span<byte> target(targetData, sizeof(targetData)); +		span.copyDataTo(target); +		for (uint i = 0; i < sizeof(data); ++i) { +			TS_ASSERT_EQUALS(target[i], i); +		} + +		byte out[sizeof(data)]; +		span.unsafeCopyDataTo(out); +		for (uint i = 0; i < sizeof(data); ++i) { +			TS_ASSERT_EQUALS(out[i], i); +		} +	} + +	void test_span_validation() { +		byte data[6]; +		Common::Span<byte> span(data, sizeof(data)); +		TS_ASSERT(!span.checkInvalidBounds(0, 0)); +		TS_ASSERT(!span.checkInvalidBounds(0, 6)); +		TS_ASSERT(!span.checkInvalidBounds(2, 4)); +		TS_ASSERT(!span.checkInvalidBounds(4, 2)); +		TS_ASSERT(!span.checkInvalidBounds(6, 0)); +		TS_ASSERT(!span.checkInvalidBounds(2, -2)); +		TS_ASSERT(span.checkInvalidBounds(-2, 2)); // negative index disallowed +		TS_ASSERT(span.checkInvalidBounds(6, 1)); // combined positive overflow (+7) +		TS_ASSERT(span.checkInvalidBounds(2, -4)); // negative overflow (-2) +		TS_ASSERT(span.checkInvalidBounds(0, 10)); // delta positive overflow + +		const ptrdiff_t big = 1L << (8 * sizeof(ptrdiff_t) - 1); +		TS_ASSERT(span.checkInvalidBounds(big, 0)); +		TS_ASSERT(span.checkInvalidBounds(0, big)); +		TS_ASSERT(span.checkInvalidBounds(big, big)); +	} + +	void test_span_validation_message() { +		byte data[1]; +		Common::Span<byte> span(data, sizeof(data)); + +		Common::String source = span.name(); +		Common::String actual; +		Common::String expected; + +		actual = span.getValidationMessage(12, 34, Common::kValidateRead); +		expected = Common::String::format("Access violation reading %s: 12 + 34 > 1", source.c_str()); +		TS_ASSERT_EQUALS(actual, expected); + +		actual = span.getValidationMessage(23, 45, Common::kValidateWrite); +		expected = Common::String::format("Access violation writing %s: 23 + 45 > 1", source.c_str()); +		TS_ASSERT_EQUALS(actual, expected); + +		actual = span.getValidationMessage(-34, -56, Common::kValidateSeek); +		expected = Common::String::format("Access violation seeking %s: -34 + -56 > 1", source.c_str()); +		TS_ASSERT_EQUALS(actual, expected); +	} + +	void test_span_comparators() { +		byte data[2]; +		Common::Span<const byte> span0(data, sizeof(data)); +		Common::Span<const byte> span1(data, sizeof(data)); +		Common::Span<const byte> span2(data, sizeof(data) - 1); +		Common::Span<const byte> span3(data + 1, sizeof(data) - 1); +		Common::Span<const byte> span4(data + 2, sizeof(data) - 2); + +		TS_ASSERT(span0 == span1); +		TS_ASSERT(span0 != span2); +		TS_ASSERT(span0 <= span1); +		TS_ASSERT(span0 <= span3); +		TS_ASSERT(span0 < span3); +		TS_ASSERT(span3 < span4); +		TS_ASSERT(span4 > span3); +		TS_ASSERT(span3 > span0); +		TS_ASSERT(span4 >= span4); +		TS_ASSERT(span0 >= span1); + +		TS_ASSERT_EQUALS(span1 - span0, 0); +		TS_ASSERT_EQUALS(span3 - span0, 1); +		TS_ASSERT_EQUALS(span4 - span0, 2); +		TS_ASSERT_EQUALS(span0 - span1, 0); +		TS_ASSERT_EQUALS(span0 - span3, -1); +		TS_ASSERT_EQUALS(span0 - span4, -2); +	} + +	void test_named_span() { +		byte data[6] = { 0, 1, 2, 3, 4, 5 }; +		Common::NamedSpan<byte> span(data, sizeof(data), "foo.data"); +		TS_ASSERT_EQUALS(span.name(), "foo.data"); + +		Common::String actual; +		Common::String expected; + +		actual = span.getValidationMessage(12, 34, Common::kValidateRead); +		expected = "Access violation reading foo.data: 12 + 34 > 6 (abs: 12 + 34 > 6)"; +		TS_ASSERT_EQUALS(actual, expected); + +		{ +			Common::NamedSpan<byte> subspan = span.subspan(2); + +			expected = "Access violation reading foo.data: 23 + 45 > 4 (abs: 25 + 45 > 6)"; +			actual = subspan.getValidationMessage(23, 45, Common::kValidateRead); +			TS_ASSERT_EQUALS(actual, expected); +		} + +		{ +			Common::NamedSpan<byte> subspan = span.subspan(2, Common::kSpanMaxSize, "new.data"); +			expected = "Access violation reading new.data: -34 + -56 > 4 (abs: -32 + -56 > 6)"; +			actual = subspan.getValidationMessage(-34, -56, Common::kValidateRead); +			TS_ASSERT_EQUALS(actual, expected); +		} + +		{ +			Common::NamedSpan<byte> subspan = span.subspan(2, Common::kSpanMaxSize, "new.data", 0); +			expected = "Access violation reading new.data: -34 + -56 > 4 (abs: -34 + -56 > 4)"; +			actual = subspan.getValidationMessage(-34, -56, Common::kValidateRead); +			TS_ASSERT_EQUALS(actual, expected); +		} + +		Common::NamedSpan<byte> span2; +		span = span2 = span; +		TS_ASSERT_EQUALS(span2, span); +		TS_ASSERT(span2.name() == span.name()); +		TS_ASSERT(span2.sourceByteOffset() == span.sourceByteOffset()); + +		Common::Span<byte> superclassInstance; +		superclassInstance = span; +		TS_ASSERT_EQUALS(span, superclassInstance); + +		Common::Span<byte> subclassInstance(superclassInstance); +		TS_ASSERT_EQUALS(subclassInstance, superclassInstance); + +		const Common::NamedSpan<const byte> constSpan(span); + +		{ +			Common::NamedSpan<const byte> subspan = constSpan.subspan(2); + +			expected = "Access violation reading foo.data: 23 + 45 > 4 (abs: 25 + 45 > 6)"; +			actual = subspan.getValidationMessage(23, 45, Common::kValidateRead); +			TS_ASSERT_EQUALS(actual, expected); +			TS_ASSERT_EQUALS(subspan.sourceByteOffset(), 2U); +		} + +		{ +			Common::NamedSpan<const byte> subspan = constSpan.subspan(2, Common::kSpanMaxSize, "new.data"); +			expected = "Access violation reading new.data: -34 + -56 > 4 (abs: -32 + -56 > 6)"; +			actual = subspan.getValidationMessage(-34, -56, Common::kValidateRead); +			TS_ASSERT_EQUALS(actual, expected); +		} + +		{ +			Common::NamedSpan<const byte> subspan = constSpan.subspan(2, Common::kSpanMaxSize, "new.data", 0); +			expected = "Access violation reading new.data: -34 + -56 > 4 (abs: -34 + -56 > 4)"; +			actual = subspan.getValidationMessage(-34, -56, Common::kValidateRead); +			TS_ASSERT_EQUALS(actual, expected); +		} + +		{ +			Common::NamedSpan<const byte> subspan = constSpan.subspan(2, Common::kSpanMaxSize, "new.data", 0); +			subspan.sourceByteOffset() = 2; +			expected = "Access violation reading new.data: -34 + -56 > 4 (abs: -32 + -56 > 6)"; +			actual = subspan.getValidationMessage(-34, -56, Common::kValidateRead); +			TS_ASSERT_EQUALS(actual, expected); +		} + +		{ +			Common::MemoryReadStream *stream = new Common::MemoryReadStream(data, sizeof(data)); +			Common::File file; +			file.open(stream, "test.txt"); +			Common::SpanOwner<Common::NamedSpan<const byte> > fileOwner; +			fileOwner->allocateFromStream(file); +			TS_ASSERT_EQUALS(fileOwner->size(), (uint)file.size()); +			file.close(); +			TS_ASSERT(fileOwner->name() == "test.txt"); +			for (uint i = 0; i < fileOwner->size(); ++i) { +				TS_ASSERT_EQUALS(fileOwner->getInt8At(i), data[i]); +			} +		} +	} +}; | 
