Add Slices: views of STL containers & C-arrays and add unit tests
This commit adds a very simple implementation of std::span but with mandatory range checks and only the most essential functionality.
This commit is contained in:
parent
97379dec1d
commit
98bca06592
726
include/exiv2/slice.hpp
Normal file
726
include/exiv2/slice.hpp
Normal file
@ -0,0 +1,726 @@
|
||||
// ********************************************************* -*- C++ -*-
|
||||
/*
|
||||
* Copyright (C) 2004-2018 Exiv2 maintainers
|
||||
*
|
||||
* This program is part of the Exiv2 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, 5th Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
/*!
|
||||
@file slice.hpp
|
||||
@brief Simple implementation of slices (=views) for STL containers and C-arrays
|
||||
@author Dan Čermák (D4N)
|
||||
<a href="mailto:dan.cermak@cgc-instruments.com">dan.cermak@cgc-instruments.com</a>
|
||||
@date 30-March-18, D4N: created
|
||||
*/
|
||||
|
||||
#ifndef EXIV2_INCLUDE_SLICE_HPP
|
||||
#define EXIV2_INCLUDE_SLICE_HPP
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Exiv2
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
// TODO: remove these custom implementations once we have C++11
|
||||
template <class T>
|
||||
struct remove_const
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct remove_const<const T>
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct remove_volatile
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
template <class T>
|
||||
struct remove_volatile<volatile T>
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
template <class T>
|
||||
struct remove_cv
|
||||
{
|
||||
typedef typename remove_const<typename remove_volatile<T>::type>::type type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct remove_pointer
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct remove_pointer<T*>
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct remove_pointer<T* const>
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Common base class of all slice implementations.
|
||||
*
|
||||
* Implements only the most basic functions, which do not require any
|
||||
* knowledge about the stored data.
|
||||
*/
|
||||
struct SliceBase
|
||||
{
|
||||
inline SliceBase(size_t begin, size_t end) : begin_(begin), end_(end)
|
||||
{
|
||||
if (begin >= end) {
|
||||
throw std::out_of_range("Begin must be smaller than end");
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Return the number of elements in the slice.
|
||||
*/
|
||||
inline size_t size() const throw()
|
||||
{
|
||||
// cannot underflow, as we know that begin < end
|
||||
return end_ - begin_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Throw an exception when index is too large.
|
||||
*
|
||||
* @throw std::out_of_range when `index` will access an element
|
||||
* outside of the slice
|
||||
*/
|
||||
inline void rangeCheck(size_t index) const
|
||||
{
|
||||
if (index >= size()) {
|
||||
throw std::out_of_range("Index outside of the slice");
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* lower and upper bounds of the slice with respect to the
|
||||
* container/array stored in storage_
|
||||
*/
|
||||
const size_t begin_, end_;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief This class provides the public-facing const-qualified methods
|
||||
* of a slice.
|
||||
*
|
||||
* The public methods are implemented in a generic fashion using a
|
||||
* storage_type. This type contains the actual reference to the data to
|
||||
* which the slice points and provides the following methods:
|
||||
*
|
||||
* - (const) value_type& unsafeAt(size_t index) (const)
|
||||
* Return the value at the given index of the underlying container,
|
||||
* without promising to perform a range check and without any
|
||||
* knowledge of the slices' size
|
||||
*
|
||||
* - const_iterator/iterator unsafeGetIteratorAt(size_t index) (const)
|
||||
* Return a (constant) iterator at the given index of the underlying
|
||||
* container. Again, no range checks are promised.
|
||||
*
|
||||
* - Constructor(data_type& data, size_t begin, size_t end)
|
||||
* Can use `begin` & `end` to perform range checks on `data`, but
|
||||
* should not store both values. Must not take ownership of `data`!
|
||||
*
|
||||
* - Must save data as a public member named `data_`.
|
||||
*
|
||||
* - Must provide appropriate typedefs for iterator, const_iterator and
|
||||
* value_type
|
||||
*/
|
||||
template <template <typename data_type> class storage_type, typename data_type>
|
||||
struct ConstSliceBase : SliceBase
|
||||
{
|
||||
typedef typename storage_type<data_type>::iterator iterator;
|
||||
typedef typename storage_type<data_type>::const_iterator const_iterator;
|
||||
typedef typename storage_type<data_type>::value_type value_type;
|
||||
|
||||
/*!
|
||||
* Default contructor, requires begin to be smaller than end,
|
||||
* otherwise an exception is thrown. Also forwards all parameters to
|
||||
* the constructor of storage_
|
||||
*/
|
||||
ConstSliceBase(data_type& data, size_t begin, size_t end)
|
||||
: SliceBase(begin, end), storage_(data, begin, end)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* Obtain a constant reference to the element with the specified
|
||||
* index in the slice.
|
||||
*
|
||||
* @throw std::out_of_range when index is out of bounds of the slice
|
||||
*/
|
||||
const value_type& at(size_t index) const
|
||||
{
|
||||
rangeCheck(index);
|
||||
// we know: begin_ < end <= size() <= SIZE_T_MAX
|
||||
// and: index < end - begin
|
||||
// thus: index + begin < end <= SIZE_T_MAX
|
||||
// => no overflow is possible
|
||||
return storage_.unsafeAt(begin_ + index);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Obtain a constant iterator to the first element in the slice.
|
||||
*/
|
||||
const_iterator cbegin() const throw()
|
||||
{
|
||||
return storage_.unsafeGetIteratorAt(begin_);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Obtain a constant iterator to the first beyond the slice.
|
||||
*/
|
||||
const_iterator cend() const throw()
|
||||
{
|
||||
return storage_.unsafeGetIteratorAt(end_);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Create a constant sub-slice with the given bounds (with respect
|
||||
* to the current slice).
|
||||
*
|
||||
* @tparam slice_type Type of the slice that this function shall
|
||||
* return. Provide it with the type of the class that derives from
|
||||
* mutable_slice_base.
|
||||
*/
|
||||
template <typename slice_type>
|
||||
slice_type subSlice(size_t begin, size_t end) const
|
||||
{
|
||||
this->rangeCheck(begin);
|
||||
// end == size() is a legal value, since end is the first
|
||||
// element beyond the slice
|
||||
// end == 0 is not a legal value (subtraction will underflow and
|
||||
// throw an exception)
|
||||
this->rangeCheck(end - 1);
|
||||
// additions are safe, begin and end are smaller than size()
|
||||
const size_t new_begin = begin + this->begin_;
|
||||
const size_t new_end = this->begin_ + end;
|
||||
if (new_end > this->end_) {
|
||||
throw std::out_of_range("Invalid input parameters to slice");
|
||||
}
|
||||
return slice_type(storage_.data_, new_begin, new_end);
|
||||
}
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Stores a reference to the actual data.
|
||||
*/
|
||||
storage_type<data_type> storage_;
|
||||
};
|
||||
|
||||
/*!
|
||||
* This class provides all public-facing non-const-qualified methods of
|
||||
* slices. It only re-implements the const-qualified versions as
|
||||
* non-const.
|
||||
*/
|
||||
template <template <typename> class storage_type, typename data_type>
|
||||
struct MutableSliceBase : public ConstSliceBase<storage_type, data_type>
|
||||
{
|
||||
typedef typename ConstSliceBase<storage_type, data_type>::iterator iterator;
|
||||
typedef typename ConstSliceBase<storage_type, data_type>::const_iterator const_iterator;
|
||||
typedef typename ConstSliceBase<storage_type, data_type>::value_type value_type;
|
||||
|
||||
/*!
|
||||
* Forwards everything to the constructor of const_slice_base
|
||||
*
|
||||
* @todo use using once we have C++11
|
||||
*/
|
||||
MutableSliceBase(data_type& data, size_t begin, size_t end)
|
||||
: ConstSliceBase<storage_type, data_type>(data, begin, end)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* Obtain a reference to the element with the specified index in the
|
||||
* slice.
|
||||
*
|
||||
* @throw std::out_of_range when index is out of bounds of the slice
|
||||
*/
|
||||
value_type& at(size_t index)
|
||||
{
|
||||
this->rangeCheck(index);
|
||||
return this->storage_.unsafeAt(this->begin_ + index);
|
||||
}
|
||||
|
||||
const value_type& at(size_t index) const
|
||||
{
|
||||
// TODO: use using base_type::at once we have C++11
|
||||
return base_type::at(index);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Obtain an iterator to the first element in the slice.
|
||||
*/
|
||||
iterator begin() throw()
|
||||
{
|
||||
return this->storage_.unsafeGetIteratorAt(this->begin_);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Obtain an iterator to the first element beyond the slice.
|
||||
*/
|
||||
iterator end() throw()
|
||||
{
|
||||
return this->storage_.unsafeGetIteratorAt(this->end_);
|
||||
}
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Explicitly convert this instance into a base-class of the
|
||||
* appropriate constant version of this slice.
|
||||
*
|
||||
* This function is required to properly implement the `subSlice()
|
||||
* const` function for mutable slices. The problem here is, that a
|
||||
* slice<T> and a slice<const T> actually don't share the same base
|
||||
* class `ConstSliceBase<storage_type, T>`. Instead `slice<T>`
|
||||
* inherits from `ConstSliceBase<storage_type, T>` and `slice<const
|
||||
* T>` inherits from `ConstSliceBase<storage_type, const T>`.
|
||||
*
|
||||
* Now, `slice<T>` can call the `subSlice() const` method from its
|
||||
* base class, but that will return a mutable `slice<T>`! Instead we
|
||||
* use this function to convert the ``slice<T>` into the parent of
|
||||
* the appropriate `slice<const T>` and call its `subSlice() const`,
|
||||
* which returns the correct type.
|
||||
*/
|
||||
ConstSliceBase<storage_type, const data_type> to_const_base() const throw()
|
||||
{
|
||||
return ConstSliceBase<storage_type, const data_type>(this->storage_.data_, this->begin_, this->end_);
|
||||
}
|
||||
|
||||
typedef ConstSliceBase<storage_type, data_type> base_type;
|
||||
|
||||
/*!
|
||||
* Create a mutable sub-slice with the given bounds (with respect to
|
||||
* the current slice).
|
||||
*
|
||||
* @tparam slice_type Type of the slice that this function shall
|
||||
* return. Provide it with the type of the class that derives from
|
||||
* mutable_slice_base.
|
||||
*/
|
||||
template <typename slice_type>
|
||||
slice_type subSlice(size_t begin, size_t end)
|
||||
{
|
||||
this->rangeCheck(begin);
|
||||
// end == size() is a legal value, since end is the first
|
||||
// element beyond the slice
|
||||
// end == 0 is not a legal value (subtraction will underflow and
|
||||
// throw an exception)
|
||||
this->rangeCheck(end - 1);
|
||||
|
||||
// additions are safe, begin & end are smaller than size()
|
||||
const size_t new_begin = begin + this->begin_;
|
||||
const size_t new_end = this->begin_ + end;
|
||||
if (new_end > this->end_) {
|
||||
throw std::out_of_range("Invalid input parameters to slice");
|
||||
}
|
||||
return slice_type(this->storage_.data_, new_begin, new_end);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Implementation of the storage concept for STL-containers.
|
||||
*
|
||||
* @tparam container Type of the STL-container.
|
||||
*/
|
||||
template <typename container>
|
||||
struct ContainerStorage
|
||||
{
|
||||
typedef typename container::iterator iterator;
|
||||
|
||||
typedef typename container::const_iterator const_iterator;
|
||||
|
||||
typedef typename Internal::remove_cv<typename container::value_type>::type value_type;
|
||||
|
||||
/*!
|
||||
* @throw std::out_of_range when end is larger than the container's
|
||||
* size.
|
||||
*/
|
||||
ContainerStorage(container& data, size_t /* begin*/, size_t end) : data_(data)
|
||||
{
|
||||
if (end > data.size()) {
|
||||
throw std::out_of_range("Invalid input parameters to slice");
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Obtain a constant reference to the element with the given `index`
|
||||
* in the container.
|
||||
*
|
||||
* @throw whatever container::at() throws
|
||||
*/
|
||||
const value_type& unsafeAt(size_t index) const
|
||||
{
|
||||
return data_.at(index);
|
||||
}
|
||||
|
||||
value_type& unsafeAt(size_t index)
|
||||
{
|
||||
return data_.at(index);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Obtain an iterator at the position of the element with the given
|
||||
* index in the container.
|
||||
*
|
||||
* @throw whatever container::begin() and std::advance() throw
|
||||
*/
|
||||
iterator unsafeGetIteratorAt(size_t index)
|
||||
{
|
||||
// we are screwed if the container got changed => try to catch it
|
||||
assert(index <= data_.size());
|
||||
|
||||
iterator it = data_.begin();
|
||||
std::advance(it, index);
|
||||
return it;
|
||||
}
|
||||
|
||||
const_iterator unsafeGetIteratorAt(size_t index) const
|
||||
{
|
||||
assert(index <= data_.size());
|
||||
|
||||
const_iterator it = data_.begin();
|
||||
std::advance(it, index);
|
||||
return it;
|
||||
}
|
||||
|
||||
container& data_;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Implementation of the storage concept for slices of C arrays.
|
||||
*
|
||||
* @tparam storage_type Type as which the C-array should be stored. Use
|
||||
* this parameter to save constant arrays as `const` and mutable ones as
|
||||
* non-`const`.
|
||||
*/
|
||||
template <typename storage_type>
|
||||
struct PtrSliceStorage
|
||||
{
|
||||
typedef typename remove_cv<typename remove_pointer<storage_type>::type>::type value_type;
|
||||
typedef value_type* iterator;
|
||||
typedef const value_type* const_iterator;
|
||||
|
||||
/*!
|
||||
* Stores ptr and checks that it is not `NULL`. The slice's bounds
|
||||
* are ignored, as we do not know the array's length.
|
||||
*
|
||||
* @throw std::invalid_argument when ptr is `NULL`
|
||||
*/
|
||||
PtrSliceStorage(storage_type ptr, size_t /*begin*/, size_t /*end*/) : data_(ptr)
|
||||
{
|
||||
// TODO: change this to nullptr once we use C++11
|
||||
if (ptr == NULL) {
|
||||
throw std::invalid_argument("Null pointer passed to slice constructor");
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Obtain a reference to the element with the given `index` in the
|
||||
* array.
|
||||
*
|
||||
* @throw nothing
|
||||
*/
|
||||
value_type& unsafeAt(size_t index) throw()
|
||||
{
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
const value_type& unsafeAt(size_t index) const throw()
|
||||
{
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
/*!
|
||||
* Obtain an iterator (=pointer) at the position of the element with
|
||||
* the given index in the container.
|
||||
*
|
||||
* @throw nothing
|
||||
*/
|
||||
iterator unsafeGetIteratorAt(size_t index) throw()
|
||||
{
|
||||
return data_ + index;
|
||||
}
|
||||
|
||||
const_iterator unsafeGetIteratorAt(size_t index) const throw()
|
||||
{
|
||||
return data_ + index;
|
||||
}
|
||||
|
||||
storage_type data_;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
/*!
|
||||
* @brief Slice (= view) for STL containers.
|
||||
*
|
||||
* This is a very simple implementation of slices (i.e. views of sub-arrays)
|
||||
* for STL containers that support O(1) element access and random access
|
||||
* iterators (like std::vector, std::array and std::string).
|
||||
*
|
||||
* A slice represents the semi-open interval [begin, end) and provides a
|
||||
* (mutable) view, it does however not own the data! It can be used to
|
||||
* conveniently pass parts of containers into functions without having to use
|
||||
* iterators or offsets.
|
||||
*
|
||||
* In contrast to C++20's std::span<T> it is impossible to read beyond the
|
||||
* container's bounds and unchecked access is not-possible (by design).
|
||||
*
|
||||
* Example usage:
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
|
||||
* std::vector<int> vec = {0, 1, 2, 3, 4};
|
||||
* slice<std::vector<int> > one_two(vec, 1, 3);
|
||||
* assert(one_two.size() == 2);
|
||||
* assert(one_two.at(0) == 1 && one_two.at(1) == 2);
|
||||
* // mutate the contents:
|
||||
* one_two.at(0) *= 2;
|
||||
* one_two.at(1) *= 3;
|
||||
* assert(one_two.at(0) == 2 && one_two.at(1) == 6);
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Slices also offer access via iterators of the same type as the underlying
|
||||
* container, so that they can be used in a comparable fashion:
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
|
||||
* std::vector<int> vec = {0, 1, 2, 3, 4};
|
||||
* slice<std::vector<int>> three_four(vec, 3, 5);
|
||||
* assert(*three_four.begin() == 3 && *three_four.end() == 4);
|
||||
* // this prints:
|
||||
* // 3
|
||||
* // 4
|
||||
* for (const auto & elem : three_four) {
|
||||
* std::cout << elem << std::endl;
|
||||
* }
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* @tparam container A STL container type, like vector or array. Must support
|
||||
* array-like access via the `at()` method.
|
||||
*/
|
||||
template <typename container>
|
||||
struct Slice : public Internal::MutableSliceBase<Internal::ContainerStorage, container>
|
||||
{
|
||||
typedef typename container::iterator iterator;
|
||||
|
||||
typedef typename container::const_iterator const_iterator;
|
||||
|
||||
typedef typename Internal::remove_cv<typename container::value_type>::type value_type;
|
||||
|
||||
/*!
|
||||
* @brief Construct a slice of the container `cont` starting at `begin`
|
||||
* (including) and ending before `end`.
|
||||
*
|
||||
* @param[in] cont Reference to the container
|
||||
* @param[in] begin First element of the slice.
|
||||
* @param[in] end First element beyond the slice.
|
||||
*
|
||||
* @throws std::out_of_range For invalid slice bounds: when end is not
|
||||
* larger than begin or when the slice's bounds are larger than the
|
||||
* container's size.
|
||||
*
|
||||
* Please note that due to the requirement that `end` must be larger
|
||||
* than `begin` (they cannot be equal) it is impossible to construct a
|
||||
* slice with zero length.
|
||||
*/
|
||||
Slice(container& cont, size_t begin, size_t end)
|
||||
: Internal::MutableSliceBase<Internal::ContainerStorage, container>(cont, begin, end)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* Construct a sub-slice of this slice with the given bounds. The bounds
|
||||
* are evaluated with respect to the current slice.
|
||||
*
|
||||
* @param[in] begin First element in the new slice.
|
||||
* @param[in] end First element beyond the new slice.
|
||||
*
|
||||
* @throw std::out_of_range when begin or end are invalid
|
||||
*/
|
||||
Slice subSlice(size_t begin, size_t end)
|
||||
{
|
||||
return Internal::MutableSliceBase<Internal::ContainerStorage, container>::template subSlice<Slice>(begin,
|
||||
end);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Constructs a new constant subSlice. Behaves otherwise exactly like
|
||||
* the non-const version.
|
||||
*/
|
||||
Slice<const container> subSlice(size_t begin, size_t end) const
|
||||
{
|
||||
return this->to_const_base().template subSlice<Slice<const container> >(begin, end);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Specialization of slices for constant containers.
|
||||
*/
|
||||
template <typename container>
|
||||
struct Slice<const container> : public Internal::ConstSliceBase<Internal::ContainerStorage, const container>
|
||||
{
|
||||
typedef typename container::iterator iterator;
|
||||
|
||||
typedef typename container::const_iterator const_iterator;
|
||||
|
||||
typedef typename Internal::remove_cv<typename container::value_type>::type value_type;
|
||||
|
||||
Slice(const container& cont, size_t begin, size_t end)
|
||||
: Internal::ConstSliceBase<Internal::ContainerStorage, const container>(cont, begin, end)
|
||||
{
|
||||
}
|
||||
|
||||
Slice subSlice(size_t begin, size_t end) const
|
||||
{
|
||||
return Internal::ConstSliceBase<Internal::ContainerStorage,
|
||||
const container>::template subSlice<Slice<const container> >(begin, end);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Specialization of slices for constant C-arrays.
|
||||
*
|
||||
* These have exactly the same interface as the slices for STL-containers,
|
||||
* with the *crucial* exception, that the slice's constructor *cannot* make
|
||||
* a proper bounds check! It can only verify that you didn't accidentally
|
||||
* swap begin and end!
|
||||
*/
|
||||
template <typename T>
|
||||
struct Slice<const T*> : public Internal::ConstSliceBase<Internal::PtrSliceStorage, const T*>
|
||||
{
|
||||
/*!
|
||||
* Constructor.
|
||||
*
|
||||
* @param[in] ptr C-array of which a slice should be constructed. Must
|
||||
* not be a null pointer.
|
||||
* @param[in] begin Index of the first element in the slice.
|
||||
* @param[in] end Index of the first element that is no longer in the
|
||||
* slice.
|
||||
*
|
||||
* Please note that the constructor has no way how to verify that
|
||||
* `begin` and `end` are not out of bounds of the provided array!
|
||||
*/
|
||||
Slice(const T* ptr, size_t begin, size_t end)
|
||||
: Internal::ConstSliceBase<Internal::PtrSliceStorage, const T*>(ptr, begin, end)
|
||||
{
|
||||
// TODO: use using in C++11
|
||||
}
|
||||
|
||||
Slice<const T*> subSlice(size_t begin, size_t end) const
|
||||
{
|
||||
return Internal::ConstSliceBase<Internal::PtrSliceStorage, const T*>::template subSlice<Slice<const T*> >(
|
||||
begin, end);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Specialization of slices for (mutable) C-arrays.
|
||||
*/
|
||||
template <typename T>
|
||||
struct Slice<T*> : public Internal::MutableSliceBase<Internal::PtrSliceStorage, T*>
|
||||
{
|
||||
Slice(T* ptr, size_t begin, size_t end)
|
||||
: Internal::MutableSliceBase<Internal::PtrSliceStorage, T*>(ptr, begin, end)
|
||||
{
|
||||
// TODO: use using in C++11
|
||||
}
|
||||
|
||||
Slice<T*> subSlice(size_t begin, size_t end)
|
||||
{
|
||||
return Internal::MutableSliceBase<Internal::PtrSliceStorage, T*>::template subSlice<Slice<T*> >(begin, end);
|
||||
}
|
||||
|
||||
Slice<const T*> subSlice(size_t begin, size_t end) const
|
||||
{
|
||||
return this->to_const_base().template subSlice<Slice<const T*> >(begin, end);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Return a new slice with the given bounds.
|
||||
*
|
||||
* Convenience wrapper around the slice's constructor for automatic template
|
||||
* parameter deduction.
|
||||
*/
|
||||
template <typename T>
|
||||
inline Slice<T> makeSlice(T& cont, size_t begin, size_t end)
|
||||
{
|
||||
return Slice<T>(cont, begin, end);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Overload of makeSlice for slices of C-arrays.
|
||||
*/
|
||||
template <typename T>
|
||||
inline Slice<T*> makeSlice(T* ptr, size_t begin, size_t end)
|
||||
{
|
||||
return Slice<T*>(ptr, begin, end);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Return a new slice spanning the whole container.
|
||||
*/
|
||||
template <typename container>
|
||||
inline Slice<container> makeSlice(container& cont)
|
||||
{
|
||||
return Slice<container>(cont, 0, cont.size());
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Return a new slice spanning from begin until the end of the
|
||||
* container.
|
||||
*/
|
||||
template <typename container>
|
||||
inline Slice<container> makeSliceFrom(container& cont, size_t begin)
|
||||
{
|
||||
return Slice<container>(cont, begin, cont.size());
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Return a new slice spanning until `end`.
|
||||
*/
|
||||
template <typename container>
|
||||
inline Slice<container> makeSliceUntil(container& cont, size_t end)
|
||||
{
|
||||
return Slice<container>(cont, 0, end);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Overload of makeSliceUntil for pointer based slices.
|
||||
*/
|
||||
template <typename T>
|
||||
inline Slice<T*> makeSliceUntil(T* ptr, size_t end)
|
||||
{
|
||||
return Slice<T*>(ptr, 0, end);
|
||||
}
|
||||
|
||||
} // namespace Exiv2
|
||||
|
||||
#endif /* EXIV2_INCLUDE_SLICE_HPP */
|
||||
@ -31,6 +31,7 @@
|
||||
|
||||
// included header files
|
||||
#include "config.h"
|
||||
#include "slice.hpp"
|
||||
#include "exiv2lib_export.h"
|
||||
|
||||
// + standard includes
|
||||
@ -274,6 +275,21 @@ namespace Exiv2 {
|
||||
long size_;
|
||||
}; // class DataBuf
|
||||
|
||||
/*!
|
||||
* @brief Create a new Slice from a DataBuf given the bounds.
|
||||
*
|
||||
* @param[in] begin, end Bounds of the new Slice. `begin` must be smaller
|
||||
* than `end` and both must not be larger than LONG_MAX.
|
||||
* @param[in] buf The DataBuf from which' data the Slice will be
|
||||
* constructed
|
||||
*
|
||||
* @throw std::invalid_argument when `end` is larger than `LONG_MAX` or
|
||||
* anything that the constructor of @ref Slice throws
|
||||
*/
|
||||
EXIV2API Slice<byte*> makeSlice(DataBuf& buf, size_t begin, size_t end);
|
||||
|
||||
//! Overload of makeSlice for `const DataBuf`, returning an immutable Slice
|
||||
EXIV2API Slice<const byte*> makeSlice(const DataBuf& buf, size_t begin, size_t end);
|
||||
|
||||
// *****************************************************************************
|
||||
// free functions
|
||||
|
||||
@ -82,6 +82,7 @@ set( LIBEXIV2_HDR
|
||||
../include/exiv2/riffvideo.hpp
|
||||
../include/exiv2/rw2image.hpp
|
||||
../include/exiv2/rwlock.hpp
|
||||
../include/exiv2/slice.hpp
|
||||
../include/exiv2/tags.hpp
|
||||
../include/exiv2/tgaimage.hpp
|
||||
../include/exiv2/tiffimage.hpp
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "i18n.h" // for _exvGettext
|
||||
#include "unused.h"
|
||||
#include "safe_op.hpp"
|
||||
#include "enforce.hpp"
|
||||
|
||||
// + standard includes
|
||||
#ifdef EXV_UNICODE_PATH
|
||||
@ -200,6 +201,24 @@ namespace Exiv2 {
|
||||
// *************************************************************************
|
||||
// free functions
|
||||
|
||||
static void checkDataBufBounds(const DataBuf& buf, size_t end) {
|
||||
enforce<std::invalid_argument>(end <= static_cast<size_t>(std::numeric_limits<long>::max()),
|
||||
"end of slice too large to be compared with DataBuf bounds.");
|
||||
enforce<std::out_of_range>(static_cast<long>(end) <= buf.size_, "Invalid slice bounds specified");
|
||||
}
|
||||
|
||||
Slice<byte*> makeSlice(DataBuf& buf, size_t begin, size_t end)
|
||||
{
|
||||
checkDataBufBounds(buf, end);
|
||||
return Slice<byte*>(buf.pData_, begin, end);
|
||||
}
|
||||
|
||||
Slice<const byte*> makeSlice(const DataBuf& buf, size_t begin, size_t end)
|
||||
{
|
||||
checkDataBufBounds(buf, end);
|
||||
return Slice<const byte*>(buf.pData_, begin, end);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Rational& r)
|
||||
{
|
||||
return os << r.first << "/" << r.second;
|
||||
|
||||
@ -10,6 +10,7 @@ add_executable(unit_tests mainTestRunner.cpp
|
||||
test_TimeValue.cpp
|
||||
test_cr2header_int.cpp
|
||||
test_helper_functions.cpp
|
||||
test_slice.cpp
|
||||
$<TARGET_OBJECTS:exiv2lib_int>
|
||||
)
|
||||
|
||||
|
||||
432
unitTests/test_slice.cpp
Normal file
432
unitTests/test_slice.cpp
Normal file
@ -0,0 +1,432 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "slice.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
#include "gtestwrapper.h"
|
||||
|
||||
using namespace Exiv2;
|
||||
|
||||
template <typename T>
|
||||
class slice;
|
||||
|
||||
/*!
|
||||
* This namespace contains the helper-function get_test_data. It is intented
|
||||
* to be used for test with the slice fixture: it returns the appropriate
|
||||
* data to the constructor of slice. For (const) T==std::vector it returns the
|
||||
* fixtures meber vec_, for (const) T==int* it returns vec_.data()
|
||||
*
|
||||
* Due to C++98's limitations, this requires a separate traits class, that
|
||||
* specifies the return type *and* a specialization of get_test_data for each
|
||||
* case (maybe some can be reduced with SFINAE, but that ain't improving
|
||||
* readability either).
|
||||
*
|
||||
* Unfortunately, C++11 will probably only make the return_type_traits go away,
|
||||
* but not the template specializations of get_test_data (for that we need
|
||||
* C++17, so see you in 2025).
|
||||
*/
|
||||
namespace cpp_98_boilerplate
|
||||
{
|
||||
template <typename T>
|
||||
struct return_type_traits
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template <typename U>
|
||||
struct return_type_traits<std::vector<U> >
|
||||
{
|
||||
typedef typename std::vector<U>& type;
|
||||
};
|
||||
|
||||
template <typename U>
|
||||
struct return_type_traits<const std::vector<U> >
|
||||
{
|
||||
typedef const typename std::vector<U>& type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
typename return_type_traits<T>::type get_test_data(slice<T>& st);
|
||||
|
||||
} // namespace cpp_98_boilerplate
|
||||
|
||||
/*!
|
||||
* Fixture for slice testing. Has one public vector of ints with size vec_size
|
||||
* that is filled with the numbers from 0 to vec_size - 1.
|
||||
*
|
||||
* The vector vec_ is used to construct slices either from a std::vector, or
|
||||
* from raw C-arrays. Which type is used, is set by the template parameter
|
||||
* T. Thus we guarantee, that the interface is completely independent of the
|
||||
* underlying datatype.
|
||||
*
|
||||
* @tparam T Type that is used to construct a slice for testing.
|
||||
*/
|
||||
template <typename T>
|
||||
class slice : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static const size_t vec_size = 10;
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
vec_.reserve(vec_size);
|
||||
for (unsigned int i = 0; i < vec_size; ++i) {
|
||||
vec_.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
Slice<T> getTestSlice(size_t begin = 1, size_t end = vec_size - 1)
|
||||
{
|
||||
return Slice<T>(cpp_98_boilerplate::get_test_data<T>(*this), begin, end);
|
||||
}
|
||||
|
||||
// TODO: once we have C++11: use initializer list
|
||||
std::vector<int> vec_;
|
||||
};
|
||||
|
||||
// specializations of get_test_data are provided here, since they must have the
|
||||
// full definition of slice available
|
||||
namespace cpp_98_boilerplate
|
||||
{
|
||||
template <>
|
||||
int* get_test_data<int*>(slice<int*>& st)
|
||||
{
|
||||
return st.vec_.data();
|
||||
}
|
||||
|
||||
template <>
|
||||
const int* get_test_data<const int*>(slice<const int*>& st)
|
||||
{
|
||||
return st.vec_.data();
|
||||
}
|
||||
|
||||
template <>
|
||||
std::vector<int>& get_test_data<std::vector<int> >(slice<std::vector<int> >& st)
|
||||
{
|
||||
return st.vec_;
|
||||
}
|
||||
|
||||
template <>
|
||||
const std::vector<int>& get_test_data<const std::vector<int> >(slice<const std::vector<int> >& st)
|
||||
{
|
||||
return st.vec_;
|
||||
}
|
||||
} // namespace cpp_98_boilerplate
|
||||
|
||||
/*!
|
||||
* Fixture to run test for mutable slices.
|
||||
*
|
||||
* It adds nothing new, it is just a separate class, so that we can run
|
||||
* different tests on it.
|
||||
*/
|
||||
template <typename T>
|
||||
class mutableSlice : public slice<T>
|
||||
{
|
||||
};
|
||||
|
||||
TYPED_TEST_CASE_P(slice);
|
||||
TYPED_TEST_CASE_P(mutableSlice);
|
||||
|
||||
TYPED_TEST_P(slice, atAccess)
|
||||
{
|
||||
// typedef Slice<TypeParam> slice_t;
|
||||
// const size_t begin = 1;
|
||||
// const size_t end = this->vec_.size() - 1;
|
||||
Slice<TypeParam> sl = this->getTestSlice();
|
||||
|
||||
ASSERT_EQ(this->vec_.size() - 2, sl.size());
|
||||
|
||||
for (unsigned int i = 0; i < sl.size(); ++i) {
|
||||
ASSERT_EQ(this->vec_.at(i + 1), sl.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO C++11: test range based for loop
|
||||
TYPED_TEST_P(slice, iteratorAccess)
|
||||
{
|
||||
Slice<TypeParam> sl = this->getTestSlice();
|
||||
|
||||
std::vector<int>::const_iterator vec_it = this->vec_.begin() + 1;
|
||||
for (typename Slice<TypeParam>::const_iterator it = sl.cbegin(); it < sl.cend(); ++it, ++vec_it) {
|
||||
ASSERT_EQ(*it, *vec_it);
|
||||
}
|
||||
|
||||
ASSERT_THROW(sl.at(sl.size()), std::out_of_range);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(slice, constructionFailsFromInvalidRange)
|
||||
{
|
||||
// start > end
|
||||
ASSERT_THROW(this->getTestSlice(2, 1), std::out_of_range);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(slice, constructionFailsWithZeroLength)
|
||||
{
|
||||
ASSERT_THROW(this->getTestSlice(1, 1), std::out_of_range);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Test the construction of subSlices and their behavior.
|
||||
*/
|
||||
TYPED_TEST_P(slice, subSliceSuccessfulConstruction)
|
||||
{
|
||||
typedef Slice<TypeParam> slice_t;
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
// | | center_vals
|
||||
// | | middle
|
||||
slice_t center_vals = this->getTestSlice(3, 7);
|
||||
ASSERT_EQ(center_vals.size(), static_cast<size_t>(4));
|
||||
ASSERT_NO_THROW(center_vals.subSlice(1, 3));
|
||||
|
||||
ASSERT_NO_THROW(center_vals.subSlice(1, center_vals.size()));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(slice, subSliceFunctions)
|
||||
{
|
||||
Slice<TypeParam> middle = this->getTestSlice(3, 7).subSlice(1, 3);
|
||||
|
||||
ASSERT_EQ(middle.size(), static_cast<size_t>(2));
|
||||
ASSERT_EQ(middle.at(1), static_cast<typename Slice<TypeParam>::value_type>(5));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(slice, subSliceFailedConstruction)
|
||||
{
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
// | | middle
|
||||
Slice<TypeParam> middle = this->getTestSlice(4, 6);
|
||||
|
||||
ASSERT_THROW(middle.subSlice(1, 5), std::out_of_range);
|
||||
ASSERT_THROW(middle.subSlice(2, 1), std::out_of_range);
|
||||
ASSERT_THROW(middle.subSlice(2, 2), std::out_of_range);
|
||||
}
|
||||
|
||||
/*! try to cause integer overflows in a sub-optimal implementation */
|
||||
TYPED_TEST_P(slice, subSliceConstructionOverflowResistance)
|
||||
{
|
||||
Slice<TypeParam> center_vals = this->getTestSlice(3, 7);
|
||||
|
||||
ASSERT_THROW(center_vals.subSlice(std::numeric_limits<size_t>::max() - 2, 3), std::out_of_range);
|
||||
ASSERT_THROW(center_vals.subSlice(2, std::numeric_limits<size_t>::max() - 1), std::out_of_range);
|
||||
}
|
||||
|
||||
/*!
|
||||
* This function's purpose is only to check whether we can pass all slices by
|
||||
* constant reference.
|
||||
*/
|
||||
template <typename T>
|
||||
void checkConstSliceValueAt(const Slice<T>& sl, typename Slice<T>::value_type value, size_t index)
|
||||
{
|
||||
ASSERT_EQ(sl.at(index), value);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Check that the contents of the slice are ascending via an iterator based for
|
||||
* loop.
|
||||
*/
|
||||
template <typename T>
|
||||
void checkConstSliceIterator(const Slice<T>& sl, typename Slice<T>::value_type first_value)
|
||||
{
|
||||
for (typename Slice<T>::const_iterator it = sl.cbegin(); it < sl.cend(); ++it) {
|
||||
ASSERT_EQ(*it, first_value++);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void checkSubSlice(const Slice<T>& sl)
|
||||
{
|
||||
ASSERT_EQ(sl.at(1), sl.subSlice(1, sl.size()).at(0));
|
||||
}
|
||||
|
||||
/*!
|
||||
* Test that all slices can be also passed as const references and still work
|
||||
*/
|
||||
TYPED_TEST_P(slice, constMethodsPreserveConst)
|
||||
{
|
||||
typedef Slice<TypeParam> slice_t;
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
// | | center_vals
|
||||
slice_t center_vals = this->getTestSlice(3, 7);
|
||||
|
||||
// check at() const works
|
||||
checkConstSliceValueAt(center_vals, 4, 1);
|
||||
|
||||
checkConstSliceIterator(center_vals, 3);
|
||||
|
||||
checkSubSlice(center_vals);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Test the non-const iterators
|
||||
*/
|
||||
TYPED_TEST_P(mutableSlice, iterators)
|
||||
{
|
||||
typedef Slice<TypeParam> slice_t;
|
||||
slice_t sl = this->getTestSlice();
|
||||
|
||||
ASSERT_EQ(*sl.begin(), static_cast<typename slice_t::value_type>(1));
|
||||
ASSERT_EQ(*sl.end(), static_cast<typename slice_t::value_type>(this->vec_size - 1));
|
||||
|
||||
for (typename slice_t::iterator it = sl.begin(); it < sl.end(); ++it) {
|
||||
*it = 2 * (*it);
|
||||
}
|
||||
|
||||
ASSERT_EQ(this->vec_.at(0), 0);
|
||||
for (size_t j = 1; j < this->vec_size - 1; ++j) {
|
||||
ASSERT_EQ(this->vec_.at(j), static_cast<typename slice_t::value_type>(2 * j));
|
||||
ASSERT_EQ(this->vec_.at(j), sl.at(j - 1));
|
||||
}
|
||||
ASSERT_EQ(this->vec_.at(this->vec_size - 1), static_cast<typename slice_t::value_type>(this->vec_size - 1));
|
||||
}
|
||||
|
||||
/*!
|
||||
* Test the non-const version of at()
|
||||
*/
|
||||
TYPED_TEST_P(mutableSlice, at)
|
||||
{
|
||||
typedef Slice<TypeParam> slice_t;
|
||||
slice_t sl = this->getTestSlice(2, 4);
|
||||
|
||||
sl.at(0) = 6;
|
||||
sl.at(1) = 12;
|
||||
|
||||
ASSERT_EQ(this->vec_.at(2), 6);
|
||||
ASSERT_EQ(this->vec_.at(3), 12);
|
||||
for (size_t j = 0; j < this->vec_size - 1; ++j) {
|
||||
if (j == 2 || j == 3) {
|
||||
continue;
|
||||
}
|
||||
ASSERT_EQ(this->vec_.at(j), static_cast<typename slice_t::value_type>(j));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(pointerSlice, failedConstructionFromNullpointer)
|
||||
{
|
||||
ASSERT_THROW(Slice<long*>(NULL, 1, 2), std::invalid_argument);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Test the construction of an invalid slices from a container (so that a proper
|
||||
* range check can be conducted)
|
||||
*/
|
||||
TEST(containerSlice, failedConstructionFromContainer)
|
||||
{
|
||||
std::vector<int> tmp(10);
|
||||
// slice end too large
|
||||
ASSERT_THROW(Slice<std::vector<int> >(tmp, 1, tmp.size() + 1), std::out_of_range);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Test all functions from the makeSlice* family.
|
||||
*/
|
||||
TEST(containerSlice, makeSlice)
|
||||
{
|
||||
std::string str = "this is a sentence";
|
||||
|
||||
Slice<std::string> is = makeSlice(str, 5, 7);
|
||||
ASSERT_TRUE(std::equal(is.begin(), is.end(), "is"));
|
||||
|
||||
Slice<std::string> sl_this = makeSliceUntil(str, 4);
|
||||
ASSERT_TRUE(std::equal(sl_this.begin(), sl_this.end(), "this"));
|
||||
|
||||
Slice<std::string> sl_sentence = makeSliceFrom(str, 10);
|
||||
ASSERT_TRUE(std::equal(sl_sentence.begin(), sl_sentence.end(), "sentence"));
|
||||
|
||||
Slice<std::string> sl_full = makeSlice(str);
|
||||
ASSERT_TRUE(std::equal(sl_full.begin(), sl_full.end(), str.c_str()));
|
||||
}
|
||||
|
||||
struct stringSlice : public ::testing::Test
|
||||
{
|
||||
std::string sentence;
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
sentence = "this is a sentence";
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(stringSlice, at)
|
||||
{
|
||||
const Slice<const std::string> is_a = makeSlice(static_cast<const std::string&>(this->sentence), 5, 10);
|
||||
|
||||
ASSERT_EQ(is_a.at(0), 'i');
|
||||
ASSERT_EQ(is_a.at(4), ' ');
|
||||
}
|
||||
|
||||
TEST_F(stringSlice, atFailure)
|
||||
{
|
||||
const Slice<const std::string> is_a = makeSlice(static_cast<const std::string&>(this->sentence), 5, 10);
|
||||
ASSERT_THROW(is_a.at(5), std::out_of_range);
|
||||
}
|
||||
|
||||
TEST_F(stringSlice, size)
|
||||
{
|
||||
const Slice<const std::string> is_a = makeSlice(static_cast<const std::string&>(this->sentence), 5, 10);
|
||||
ASSERT_EQ(is_a.size(), static_cast<size_t>(5));
|
||||
}
|
||||
|
||||
TEST_F(stringSlice, mutateString)
|
||||
{
|
||||
Slice<std::string> is_a_mutable = makeSlice(this->sentence, 5, 10);
|
||||
|
||||
for (Slice<std::string>::iterator it = is_a_mutable.begin(); it < is_a_mutable.end(); ++it) {
|
||||
*it = ' ';
|
||||
}
|
||||
|
||||
ASSERT_STREQ(this->sentence.c_str(), "this sentence");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct dataBufSlice : public ::testing::Test
|
||||
{
|
||||
static byte data[4]; // = {0xde, 0xad, 0xbe, 0xef};
|
||||
DataBuf buf;
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
buf = DataBuf(data, sizeof(data));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
byte dataBufSlice<T>::data[4] = {0xde, 0xad, 0xbe, 0xef};
|
||||
|
||||
TYPED_TEST_CASE_P(dataBufSlice);
|
||||
|
||||
TYPED_TEST_P(dataBufSlice, successfulConstruction)
|
||||
{
|
||||
// just check that makeSlice appears to work
|
||||
ASSERT_EQ(makeSlice(static_cast<TypeParam>(this->buf), 1, 3).size(), static_cast<size_t>(2));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(dataBufSlice, failedConstruction)
|
||||
{
|
||||
// check that we get an exception when end is larger than LONG_MAX
|
||||
ASSERT_THROW(
|
||||
makeSlice(static_cast<TypeParam>(this->buf), 1, static_cast<size_t>(std::numeric_limits<long>::max()) + 1),
|
||||
std::invalid_argument);
|
||||
|
||||
// check that we get an exception when end is larger than the DataBuf
|
||||
ASSERT_THROW(makeSlice(static_cast<TypeParam>(this->buf), 1, 5), std::out_of_range);
|
||||
}
|
||||
|
||||
//
|
||||
// GTest boilerplate to get the tests running for all the different types
|
||||
//
|
||||
REGISTER_TYPED_TEST_CASE_P(slice, atAccess, iteratorAccess, constructionFailsFromInvalidRange,
|
||||
constructionFailsWithZeroLength, subSliceSuccessfulConstruction, subSliceFunctions,
|
||||
subSliceFailedConstruction, subSliceConstructionOverflowResistance,
|
||||
constMethodsPreserveConst);
|
||||
|
||||
typedef ::testing::Types<const std::vector<int>, std::vector<int>, int*, const int*> test_types_t;
|
||||
INSTANTIATE_TYPED_TEST_CASE_P(, slice, test_types_t);
|
||||
|
||||
REGISTER_TYPED_TEST_CASE_P(mutableSlice, iterators, at);
|
||||
typedef ::testing::Types<std::vector<int>, int*> mut_test_types_t;
|
||||
INSTANTIATE_TYPED_TEST_CASE_P(, mutableSlice, mut_test_types_t);
|
||||
|
||||
REGISTER_TYPED_TEST_CASE_P(dataBufSlice, successfulConstruction, failedConstruction);
|
||||
typedef ::testing::Types<DataBuf&, const DataBuf&> data_buf_types_t;
|
||||
INSTANTIATE_TYPED_TEST_CASE_P(, dataBufSlice, data_buf_types_t);
|
||||
Loading…
Reference in New Issue
Block a user