mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 06:12:43 +00:00 
			
		
		
		
	AK: Implement Span which represents a contiguous sequence of objects.
This makes it possible to pass one object rather than pointer and length individually.
This commit is contained in:
		
							parent
							
								
									d43ddd6eb7
								
							
						
					
					
						commit
						ac9c2bc492
					
				
					 5 changed files with 277 additions and 8 deletions
				
			
		|  | @ -49,6 +49,12 @@ class FlyString; | |||
| class Utf32View; | ||||
| class Utf8View; | ||||
| 
 | ||||
| template<typename T> | ||||
| class Span; | ||||
| 
 | ||||
| using ReadonlyBytes = Span<const u8>; | ||||
| using Bytes = Span<u8>; | ||||
| 
 | ||||
| template<typename T> | ||||
| class Atomic; | ||||
| 
 | ||||
|  | @ -113,6 +119,7 @@ using AK::Badge; | |||
| using AK::Bitmap; | ||||
| using AK::BufferStream; | ||||
| using AK::ByteBuffer; | ||||
| using AK::Bytes; | ||||
| using AK::CircularQueue; | ||||
| using AK::DebugLogStream; | ||||
| using AK::DoublyLinkedList; | ||||
|  | @ -131,9 +138,11 @@ using AK::NonnullOwnPtr; | |||
| using AK::NonnullRefPtr; | ||||
| using AK::Optional; | ||||
| using AK::OwnPtr; | ||||
| using AK::ReadonlyBytes; | ||||
| using AK::RefPtr; | ||||
| using AK::SharedBuffer; | ||||
| using AK::SinglyLinkedList; | ||||
| using AK::Span; | ||||
| using AK::String; | ||||
| using AK::StringBuilder; | ||||
| using AK::StringImpl; | ||||
|  |  | |||
							
								
								
									
										130
									
								
								AK/Span.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								AK/Span.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,130 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2020, the SerenityOS developers. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  * 1. Redistributions of source code must retain the above copyright notice, this | ||||
|  *    list of conditions and the following disclaimer. | ||||
|  * | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
|  *    this list of conditions and the following disclaimer in the documentation | ||||
|  *    and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
|  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||
|  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
|  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||
|  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
|  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <AK/Assertions.h> | ||||
| #include <AK/Checked.h> | ||||
| #include <AK/Types.h> | ||||
| 
 | ||||
| namespace AK { | ||||
| 
 | ||||
| template<typename T> | ||||
| class Span { | ||||
| public: | ||||
|     using Iterator = T*; | ||||
|     using ConstIterator = const T*; | ||||
| 
 | ||||
|     static_assert(!IsPointer<T>::value); | ||||
| 
 | ||||
|     ALWAYS_INLINE Span() = default; | ||||
|     ALWAYS_INLINE Span(T* values, size_t size) | ||||
|         : m_values(values) | ||||
|         , m_size(size) | ||||
|     { | ||||
|         ASSERT(!Checked<uintptr_t>::addition_would_overflow((uintptr_t)values, size * sizeof(T))); | ||||
|     } | ||||
|     ALWAYS_INLINE Span(const Span& other) | ||||
|         : m_values(other.m_values) | ||||
|         , m_size(other.m_size) | ||||
|     { | ||||
|     } | ||||
|     ALWAYS_INLINE Span(const Span<RemoveConst<T>>& other) | ||||
|         : m_values(other.m_values) | ||||
|         , m_size(other.m_size) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     ALWAYS_INLINE const T* data() const { return m_values; } | ||||
|     ALWAYS_INLINE T* data() { return m_values; } | ||||
| 
 | ||||
|     ALWAYS_INLINE ConstIterator begin() const | ||||
|     { | ||||
|         return m_values; | ||||
|     } | ||||
|     ALWAYS_INLINE ConstIterator end() const | ||||
|     { | ||||
|         return begin() + m_size; | ||||
|     } | ||||
| 
 | ||||
|     ALWAYS_INLINE Iterator begin() | ||||
|     { | ||||
|         return m_values; | ||||
|     } | ||||
|     ALWAYS_INLINE Iterator end() | ||||
|     { | ||||
|         return begin() + m_size; | ||||
|     } | ||||
| 
 | ||||
|     ALWAYS_INLINE size_t size() const { return m_size; } | ||||
| 
 | ||||
|     ALWAYS_INLINE bool is_empty() const { return m_size == 0; } | ||||
| 
 | ||||
|     ALWAYS_INLINE Span<T> subspan(size_t start, size_t size) const | ||||
|     { | ||||
|         ASSERT(start + size <= m_size); | ||||
|         return { m_values + start, size }; | ||||
|     } | ||||
| 
 | ||||
|     ALWAYS_INLINE const T& at(size_t index) const | ||||
|     { | ||||
|         ASSERT(index < m_size); | ||||
|         return m_values[index]; | ||||
|     } | ||||
|     ALWAYS_INLINE T& at(size_t index) | ||||
|     { | ||||
|         ASSERT(index < m_size); | ||||
|         return m_values[index]; | ||||
|     } | ||||
| 
 | ||||
|     ALWAYS_INLINE T& operator[](size_t index) const | ||||
|     { | ||||
|         return m_values[index]; | ||||
|     } | ||||
|     ALWAYS_INLINE T& operator[](size_t index) | ||||
|     { | ||||
|         return m_values[index]; | ||||
|     } | ||||
| 
 | ||||
|     ALWAYS_INLINE T& operator=(const T& other) | ||||
|     { | ||||
|         m_size = other.m_size; | ||||
|         m_values = other.m_values; | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|     T* m_values { nullptr }; | ||||
|     size_t m_size { 0 }; | ||||
| }; | ||||
| 
 | ||||
| using ReadonlyBytes = Span<const u8>; | ||||
| using Bytes = Span<u8>; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| using AK::Bytes; | ||||
| using AK::ReadonlyBytes; | ||||
| using AK::Span; | ||||
							
								
								
									
										126
									
								
								AK/Tests/Span.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								AK/Tests/Span.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,126 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2020, the SerenityOS developers. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  * 1. Redistributions of source code must retain the above copyright notice, this | ||||
|  *    list of conditions and the following disclaimer. | ||||
|  * | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
|  *    this list of conditions and the following disclaimer in the documentation | ||||
|  *    and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
|  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||
|  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
|  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||
|  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
|  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  */ | ||||
| 
 | ||||
| #include <AK/TestSuite.h> | ||||
| 
 | ||||
| #include <AK/Checked.h> | ||||
| #include <AK/Span.h> | ||||
| #include <AK/StdLibExtras.h> | ||||
| 
 | ||||
| TEST_CASE(default_constructor_is_empty) | ||||
| { | ||||
|     Span<int> span; | ||||
|     EXPECT(span.is_empty()); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(span_works_with_constant_types) | ||||
| { | ||||
|     const u8 buffer[4] { 1, 2, 3, 4 }; | ||||
|     ReadonlyBytes bytes { buffer, 4 }; | ||||
| 
 | ||||
|     EXPECT(AK::IsConst<AK::RemoveReference<decltype(bytes[1])>::Type>::value); | ||||
|     EXPECT_EQ(bytes[2], 3); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(span_works_with_mutable_types) | ||||
| { | ||||
|     u8 buffer[4] { 1, 2, 3, 4 }; | ||||
|     Bytes bytes { buffer, 4 }; | ||||
| 
 | ||||
|     EXPECT_EQ(bytes[2], 3); | ||||
|     ++bytes[2]; | ||||
|     EXPECT_EQ(bytes[2], 4); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(iterator_behaves_like_loop) | ||||
| { | ||||
|     u8 buffer[256]; | ||||
|     for (int idx = 0; idx < 256; ++idx) { | ||||
|         buffer[idx] = static_cast<u8>(idx); | ||||
|     } | ||||
| 
 | ||||
|     Bytes bytes { buffer, 256 }; | ||||
|     size_t idx = 0; | ||||
|     for (auto iter = bytes.begin(); iter < bytes.end(); ++iter) { | ||||
|         EXPECT_EQ(*iter, buffer[idx]); | ||||
| 
 | ||||
|         ++idx; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(modifying_is_possible) | ||||
| { | ||||
|     int values_before[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; | ||||
|     int values_after[8] = { 7, 6, 5, 4, 3, 2, 1, 0 }; | ||||
| 
 | ||||
|     Span<int> span { values_before, 8 }; | ||||
|     for (auto& value : span) { | ||||
|         value = 8 - value; | ||||
|     } | ||||
| 
 | ||||
|     for (int idx = 0; idx < 8; ++idx) { | ||||
|         EXPECT_EQ(values_before[idx], values_after[idx]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(at_and_index_operator_return_same_value) | ||||
| { | ||||
|     u8 buffer[256]; | ||||
|     for (int idx = 0; idx < 256; ++idx) { | ||||
|         buffer[idx] = static_cast<u8>(idx); | ||||
|     } | ||||
| 
 | ||||
|     Bytes bytes { buffer, 256 }; | ||||
|     for (int idx = 0; idx < 256; ++idx) { | ||||
|         EXPECT_EQ(buffer[idx], bytes[idx]); | ||||
|         EXPECT_EQ(bytes[idx], bytes.at(idx)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(can_subspan_whole_span) | ||||
| { | ||||
|     u8 buffer[16]; | ||||
|     Bytes bytes { buffer, 16 }; | ||||
| 
 | ||||
|     Bytes subspan = bytes.subspan(0, 16); | ||||
| 
 | ||||
|     EXPECT_EQ(subspan.data(), buffer); | ||||
|     EXPECT_EQ(subspan.size(), 16u); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(can_subspan_as_intended) | ||||
| { | ||||
|     const u16 buffer[8] { 1, 2, 3, 4, 5, 6, 7, 8 }; | ||||
| 
 | ||||
|     Span<const u16> span { buffer, 8 }; | ||||
|     auto subspan = span.subspan(3, 2); | ||||
| 
 | ||||
|     EXPECT_EQ(subspan.size(), 2u); | ||||
|     EXPECT_EQ(subspan[0], 4); | ||||
|     EXPECT_EQ(subspan[1], 5); | ||||
| } | ||||
| 
 | ||||
| TEST_MAIN(Span) | ||||
|  | @ -29,6 +29,7 @@ | |||
| #include <AK/Assertions.h> | ||||
| #include <AK/Forward.h> | ||||
| #include <AK/Optional.h> | ||||
| #include <AK/Span.h> | ||||
| #include <AK/StdLibExtras.h> | ||||
| #include <AK/Traits.h> | ||||
| #include <AK/kmalloc.h> | ||||
|  | @ -186,6 +187,9 @@ public: | |||
|         m_size = other.size(); | ||||
|     } | ||||
| 
 | ||||
|     Span<T> span() { return { data(), size() }; } | ||||
|     Span<const T> span() const { return { data(), size() }; } | ||||
| 
 | ||||
|     // FIXME: What about assigning from a vector with lower inline capacity?
 | ||||
|     Vector& operator=(Vector&& other) | ||||
|     { | ||||
|  |  | |||
|  | @ -37,14 +37,14 @@ void usage(void) | |||
|     exit(1); | ||||
| } | ||||
| 
 | ||||
| enum Unit { Bytes, | ||||
| enum class Unit { Bytes, | ||||
|     KiloBytes, | ||||
|     MegaBytes }; | ||||
| 
 | ||||
| int main(int argc, char** argv) | ||||
| { | ||||
|     int count = 50; | ||||
|     Unit unit = MegaBytes; | ||||
|     auto unit = Unit::MegaBytes; | ||||
| 
 | ||||
|     if (argc >= 2) { | ||||
|         auto number = String(argv[1]).to_uint(); | ||||
|  | @ -56,22 +56,22 @@ int main(int argc, char** argv) | |||
| 
 | ||||
|     if (argc >= 3) { | ||||
|         if (strcmp(argv[2], "B") == 0) | ||||
|             unit = Bytes; | ||||
|             unit = Unit::Bytes; | ||||
|         else if (strcmp(argv[2], "KB") == 0) | ||||
|             unit = KiloBytes; | ||||
|             unit = Unit::KiloBytes; | ||||
|         else if (strcmp(argv[2], "MB") == 0) | ||||
|             unit = MegaBytes; | ||||
|             unit = Unit::MegaBytes; | ||||
|         else | ||||
|             usage(); | ||||
|     } | ||||
| 
 | ||||
|     switch (unit) { | ||||
|     case Bytes: | ||||
|     case Unit::Bytes: | ||||
|         break; | ||||
|     case KiloBytes: | ||||
|     case Unit::KiloBytes: | ||||
|         count *= 1024; | ||||
|         break; | ||||
|     case MegaBytes: | ||||
|     case Unit::MegaBytes: | ||||
|         count *= 1024 * 1024; | ||||
|         break; | ||||
|     } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 asynts
						asynts