From: Amelia Coutard Date: Fri, 29 Dec 2023 04:46:58 +0000 (+0100) Subject: Plein d'assertions, pour un code plus sûr. X-Git-Url: https://git.ameliathe1st.gay/?a=commitdiff_plain;h=81dec07afb146db80be587d353310603da4bd4a4;p=voyage-au-centre-des-fichiers.git Plein d'assertions, pour un code plus sûr. --- diff --git a/Makefile b/Makefile index 9fe55cf..e7a3b31 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ endif build: # Build as default target -CXXFLAGS := -std=c++20 -fno-strict-aliasing -Wall -Wextra -pedantic -Werror \ +CXXFLAGS := -std=c++23 -fno-strict-aliasing -Wall -Wextra -pedantic -Werror \ -mgeneral-regs-only -fno-exceptions -fno-rtti -ffreestanding -O2 \ -I libcpp $(CXXFLAGS) LDFLAGS := $(CXXFLAGS) $(LDFLAGS) diff --git a/libcpp/allocator.hpp b/libcpp/allocator.hpp index f398779..131459e 100644 --- a/libcpp/allocator.hpp +++ b/libcpp/allocator.hpp @@ -59,6 +59,12 @@ concept allocator = requires(T allocator) { { allocator.deallocate(amy::declval(), amy::declval(), amy::declval()) } -> amy::same_as; }; +template +concept knowning_allocator = allocator && requires(T allocator) { + // owns(ptr) returns true if, and only if, ptr is a pointer to a memory area allocated by this allocator. + // pre: ptr is a pointer to a memory area allocated by **ANY** allocator + { allocator.owns(amy::declval()) } -> amy::same_as; +}; // reallocate(allocator, ptr, old_size, new_size, align) tries to resize this memory area, or to create a new one, such that // the contents of it, up to the byte min(old_size, new_size), are identical, and the resulting area is aligned to align. @@ -67,6 +73,11 @@ concept allocator = requires(T allocator) { // pre: ptr is a pointer to a memory area of size old_size and alignment align allocated by the allocator allocator. // pre: new_size ≥ 1 void* reallocate(allocator auto& allocator, void* ptr, amy::size old_size, amy::size new_size, amy::size align) { + assert(ptr != amy::nil); + if constexpr (knowning_allocator) { + assert(allocator.owns(ptr)); + } + assert(new_size >= 1); if (allocator.expand(ptr, old_size, new_size - old_size)) { return ptr; } @@ -79,23 +90,26 @@ void* reallocate(allocator auto& allocator, void* ptr, amy::size old_size, amy:: return res; } -template -concept knowning_allocator = allocator && requires(T allocator) { - // owns(ptr) returns true if, and only if, ptr is a pointer to a memory area allocated by this allocator. - // pre: ptr is a pointer to a memory area allocated by **ANY** allocator - { allocator.owns(amy::declval()) } -> amy::same_as; -}; - template struct stack_allocator { static_assert(data_size >= 0); static_assert(data_align >= 1); static_assert((data_align & (data_align - 1)) == 0); // Assert power of 2. - static constexpr amy::size good_size(amy::size size, amy::size) { + static constexpr amy::size good_size(amy::size size, amy::size align) { + if ! consteval { + assert(size >= 1); + assert(align >= 1); + assert((align & (align - 1)) == 0); + assert(align <= max_align); + } return size; } static constexpr amy::size max_align = data_align; void* allocate(amy::size size, amy::size align) { + assert(size >= 1); + assert(align >= 1); + assert((align & (align - 1)) == 0); + assert(align <= max_align); amy::diff aligned_first_free_byte = (first_free_byte + (align - 1)) / align * align; if (aligned_first_free_byte + size > data_size) { return amy::nil; @@ -104,6 +118,8 @@ template struct stack_allocator { return &data[aligned_first_free_byte]; } bool expand(void* ptr, amy::size size, amy::diff delta) { + assert(owns(ptr)); + assert(size + delta >= 1); if (delta == 0) { return true; } @@ -118,6 +134,7 @@ template struct stack_allocator { } } void deallocate(void* ptr, amy::size size, amy::size) { + assert(owns(ptr)); if ((amy::byte*)ptr - data + size == first_free_byte) { first_free_byte = (amy::byte*)ptr - data; } @@ -134,7 +151,6 @@ template = 1); static_assert(max_data_size >= min_data_size); - // static_assert(max_data_size >= amy::byte_size()); // Enough room for intrusive list. static_assert(min_data_align >= 1); static_assert(max_data_align >= min_data_align); static_assert((min_data_align & (min_data_align - 1)) == 0); // Assert power of 2. @@ -150,6 +166,12 @@ private: public: static constexpr amy::size good_size(amy::size size, amy::size align) { + if ! consteval { + assert(size >= 1); + assert(align >= 1); + assert((align & (align - 1)) == 0); + assert(align <= max_align); + } if (size < min_data_size || max_data_size < size || align < min_data_align || max_data_align < align) { return suballocator_t::good_size(size, align); } @@ -157,6 +179,10 @@ public: } static constexpr amy::size max_align = suballocator_t::max_align; void* allocate(amy::size size, amy::size align) { + assert(size >= 1); + assert(align >= 1); + assert((align & (align - 1)) == 0); + assert(align <= max_align); if (size < min_data_size || max_data_size < size || align < min_data_align || max_data_align < align) { return sub.allocate(size, align); } @@ -170,6 +196,11 @@ public: return old_head; } bool expand(void* ptr, amy::size size, amy::diff delta) { + assert(ptr != amy::nil); + if constexpr (knowning_allocator) { + assert(owns(ptr)); + } + assert(size + delta >= 1); if (min_data_size <= size && size <= max_data_size) { delta = (size + delta) - max_data_size; size = max_data_size; @@ -180,6 +211,10 @@ public: return sub.expand(ptr, size, delta); } void deallocate(void* ptr, amy::size size, amy::size align) { + assert(ptr != amy::nil); + if constexpr (knowning_allocator) { + assert(owns(ptr)); + } if (size < min_data_size || max_data_size < size || align < min_data_align || max_data_align < align) { return sub.deallocate(ptr, size, align); } diff --git a/libcpp/memory.hpp b/libcpp/memory.hpp index f8c3b7e..a0b2497 100644 --- a/libcpp/memory.hpp +++ b/libcpp/memory.hpp @@ -13,6 +13,7 @@ #pragma once +#include #include // See the C++ documentation for documentation. @@ -37,10 +38,14 @@ template template constexpr size byte_size() { - return sizeof(T); + constexpr size res = sizeof(T); + static_assert(res >= 0); + return res; } template constexpr size byte_align() { - return alignof(T); + constexpr size res = alignof(T); + static_assert(res >= 0); + return res; } // Implementation based on https://en.cppreference.com/w/cpp/utility/declval template constexpr T&& declval() noexcept { @@ -49,12 +54,16 @@ template constexpr T&& declval() noexcept { extern "C" inline void* memset(void* dest, int c, amy::size n) { + assert(n >= 0); + assert(0 <= c && c <= 255); while (n-- > 0) { reinterpret_cast(dest)[n] = amy::byte(c); } return dest; } extern "C" inline void* memcpy(void* dest, const void* src, amy::size n) { + assert(n >= 0); + assert(amy::ptr(dest) + n <= amy::ptr(src) || amy::ptr(src) + n <= amy::ptr(dest)); while (n-- > 0) { reinterpret_cast(dest)[n] = reinterpret_cast(src)[n]; } diff --git a/libcpp/vector.hpp b/libcpp/vector.hpp index 97cd731..84577e6 100644 --- a/libcpp/vector.hpp +++ b/libcpp/vector.hpp @@ -58,16 +58,38 @@ public: size_ = other.size_; capacity_ = other.capacity_; } - vector& operator=(const vector& other) = delete; // Later, when contracts work. Because allocators must be the same. - vector& operator=(vector&& other) = delete; + vector& operator=(const vector& other) { + assert(&allocator == &other.allocator); + vector(other).swap(*this); + } + vector& operator=(vector&& other) { + assert(&allocator == &other.allocator); + this->swap(other); + } + + void swap(vector& other) { + assert(&allocator == &other.allocator); + amy::size bak = size_; + size_ = other.size_; + other.size_ = bak; + bak = capacity_; + capacity_ = other.capacity_; + other.capacity_ = bak; + T* bak_ = data; + data = other.data; + other.data = bak; + } amy::size size() { + assert(!construction_failed()); return size_; } amy::size capacity() { + assert(!construction_failed()); return capacity_; } bool push_back(const T& v) { + assert(!construction_failed()); if (size_ == capacity_) { if (allocator.expand(data, capacity_ * amy::byte_size(), amy::byte_size())) { capacity_++; @@ -83,10 +105,14 @@ public: new(&data[size_ - 1]) T(v); return true; } - T& operator[](amy::size i) { + T& operator[](amy::diff i) { + assert(!construction_failed()); + assert(0 <= i && i < size_); return data[i]; } - const T& operator[](amy::size i) const { + const T& operator[](amy::diff i) const { + assert(!construction_failed()); + assert(0 <= i && i < size_); return data[i]; }