util/TerminatedArray: new class
This commit is contained in:
		
							
								
								
									
										128
									
								
								src/util/TerminatedArray.hxx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/util/TerminatedArray.hxx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | |||||||
|  | // SPDX-License-Identifier: BSD-2-Clause | ||||||
|  | // author: Max Kellermann <max.kellermann@gmail.com> | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <iterator> // for std::iterator_traits | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * A container-like class which allows iterating over an array | ||||||
|  |  * terminated by sentinel value.  Most commonly, this is an array of | ||||||
|  |  * pointers terminated by nullptr, but null-terminated C strings can | ||||||
|  |  * also be used. | ||||||
|  |  * | ||||||
|  |  * @param T the type of an item in the aray | ||||||
|  |  * @param Sentinel the sentinel value | ||||||
|  |  */ | ||||||
|  | template<typename T, T Sentinel> | ||||||
|  | class TerminatedArray { | ||||||
|  | 	T *head; | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * A special type for the end iterator which is always equal | ||||||
|  | 	 * the iterator that points to the #Sentinel value. | ||||||
|  | 	 */ | ||||||
|  | 	struct sentinel { | ||||||
|  | 		using Traits = std::iterator_traits<T *>; | ||||||
|  |  | ||||||
|  | 		using iterator_category = typename Traits::iterator_category; | ||||||
|  | 		using difference_type = typename Traits::difference_type; | ||||||
|  | 		using value_type = typename Traits::value_type; | ||||||
|  | 		using pointer = typename Traits::pointer; | ||||||
|  | 		using reference = typename Traits::reference; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | public: | ||||||
|  | 	using value_type = T; | ||||||
|  |  | ||||||
|  | 	explicit constexpr TerminatedArray(T *_head) noexcept | ||||||
|  | 		:head(_head) {} | ||||||
|  |  | ||||||
|  | 	class iterator { | ||||||
|  | 		using Traits = std::iterator_traits<T *>; | ||||||
|  |  | ||||||
|  | 		T *cursor; | ||||||
|  |  | ||||||
|  | 	public: | ||||||
|  | 		using iterator_category = typename Traits::iterator_category; | ||||||
|  | 		using difference_type = typename Traits::difference_type; | ||||||
|  | 		using value_type = typename Traits::value_type; | ||||||
|  | 		using pointer = typename Traits::pointer; | ||||||
|  | 		using reference = typename Traits::reference; | ||||||
|  |  | ||||||
|  | 		explicit constexpr iterator(T *_cursor) noexcept | ||||||
|  | 			:cursor(_cursor) {} | ||||||
|  |  | ||||||
|  | 		constexpr bool operator==(const iterator &other) const noexcept { | ||||||
|  | 			return cursor == other.cursor; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		constexpr bool operator==(const sentinel &) const noexcept { | ||||||
|  | 			return *cursor == Sentinel; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		constexpr auto &operator++() noexcept { | ||||||
|  | 			++cursor; | ||||||
|  | 			return *this; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		constexpr auto operator++(int) noexcept { | ||||||
|  | 			auto old = *this; | ||||||
|  | 			++cursor; | ||||||
|  | 			return old; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		constexpr auto &operator+=(difference_type n) noexcept { | ||||||
|  | 			cursor += n; | ||||||
|  | 			return *this; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		constexpr auto operator+(difference_type n) const noexcept { | ||||||
|  | 			return iterator{cursor + n}; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		constexpr auto &operator--() noexcept { | ||||||
|  | 			--cursor; | ||||||
|  | 			return *this; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		constexpr auto operator--(int) noexcept { | ||||||
|  | 			auto old = *this; | ||||||
|  | 			--cursor; | ||||||
|  | 			return old; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		constexpr auto &operator-=(difference_type n) noexcept { | ||||||
|  | 			cursor -= n; | ||||||
|  | 			return *this; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		constexpr auto operator-(difference_type n) const noexcept { | ||||||
|  | 			return iterator{cursor - n}; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		reference operator*() const noexcept { | ||||||
|  | 			return *cursor; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		pointer operator->() const noexcept { | ||||||
|  | 			return cursor; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	constexpr iterator begin() const noexcept { | ||||||
|  | 		return iterator{head}; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	constexpr sentinel end() const noexcept { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	constexpr iterator cbegin() const noexcept { | ||||||
|  | 		return begin(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	constexpr sentinel cend() const noexcept { | ||||||
|  | 		return end(); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
							
								
								
									
										34
									
								
								test/util/TestTerminatedArray.cxx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								test/util/TestTerminatedArray.cxx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | // SPDX-License-Identifier: BSD-2-Clause | ||||||
|  | // author: Max Kellermann <max.kellermann@gmail.com> | ||||||
|  |  | ||||||
|  | #include "util/TerminatedArray.hxx" | ||||||
|  |  | ||||||
|  | #include <gtest/gtest.h> | ||||||
|  |  | ||||||
|  | TEST(TerminatedArray, PointerArray) | ||||||
|  | { | ||||||
|  | 	const char *const raw_array[] = {"foo", "bar", nullptr}; | ||||||
|  |  | ||||||
|  | 	TerminatedArray<const char *const, nullptr> array{raw_array}; | ||||||
|  | 	EXPECT_STREQ(*array.begin(), "foo"); | ||||||
|  | 	EXPECT_STREQ(*std::next(array.begin()), "bar"); | ||||||
|  | 	EXPECT_EQ(std::prev(std::next(array.begin())), array.begin()); | ||||||
|  | 	EXPECT_NE(array.begin(), array.end()); | ||||||
|  | 	EXPECT_NE(std::next(array.begin()), array.end()); | ||||||
|  | 	EXPECT_EQ(std::next(array.begin(), 2), array.end()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST(TerminatedArray, CSTring) | ||||||
|  | { | ||||||
|  | 	const char raw_array[] = "abc"; | ||||||
|  |  | ||||||
|  | 	TerminatedArray<const char, '\0'> array{raw_array}; | ||||||
|  | 	EXPECT_EQ(*array.begin(), 'a'); | ||||||
|  | 	EXPECT_EQ(*std::next(array.begin()), 'b'); | ||||||
|  | 	EXPECT_EQ(*std::next(array.begin(), 2), 'c'); | ||||||
|  | 	EXPECT_EQ(std::prev(std::next(array.begin())), array.begin()); | ||||||
|  | 	EXPECT_NE(array.begin(), array.end()); | ||||||
|  | 	EXPECT_NE(std::next(array.begin()), array.end()); | ||||||
|  | 	EXPECT_NE(std::next(array.begin(), 2), array.end()); | ||||||
|  | 	EXPECT_EQ(std::next(array.begin(), 3), array.end()); | ||||||
|  | } | ||||||
| @@ -14,6 +14,7 @@ test( | |||||||
|     'TestSplitString.cxx', |     'TestSplitString.cxx', | ||||||
|     'TestStringStrip.cxx', |     'TestStringStrip.cxx', | ||||||
|     'TestTemplateString.cxx', |     'TestTemplateString.cxx', | ||||||
|  |     'TestTerminatedArray.cxx', | ||||||
|     'TestUriExtract.cxx', |     'TestUriExtract.cxx', | ||||||
|     'TestUriQueryParser.cxx', |     'TestUriQueryParser.cxx', | ||||||
|     'TestUriRelative.cxx', |     'TestUriRelative.cxx', | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann