// You should have received a copy of the GNU General Public License along with this program. If
// not, see <https://www.gnu.org/licenses/>.
+#include <allocator.hpp>
+#include <vector.hpp>
#include "lib/multiboot2.hpp"
#include "paging.hpp"
#include "utils.hpp"
}
{
- struct {
- os::phys_ptr<os::paging::page<0>> start_address = nullptr;
- os::phys_ptr<os::paging::page<0>> end_address = nullptr;
- } available_ram[50];
+ struct ram_range {
+ os::phys_ptr<os::paging::page<0>> start_address;
+ os::phys_ptr<os::paging::page<0>> end_address;
+ };
+ amy::stack_allocator<amy::byte_size<ram_range>() * 64, amy::byte_align<ram_range>()> available_ram_allocator;
+ amy::vector<ram_range, decltype(available_ram_allocator)> available_ram(available_ram_allocator);
+ os::assert(!available_ram.construction_failed(), "Failed to create vector.");
amy::size available_ram_length = 0;
bool module_specified = false;
if (e.get_phys_addr() <= 1024 * 1024) { // I'll ignore lower memory for now.
continue;
}
- os::assert(available_ram_length < 50, "Too much available RAM sections to initialise correctly. Will fix eventually, probably.");
- available_ram[available_ram_length++] = {.start_address = s, .end_address = e};
+ os::assert(available_ram.push_back({.start_address = s, .end_address = e}),
+ "Too much available RAM sections to initialise correctly. Will fix eventually, probably.");
}
break;
case multiboot2::info::type_t::modules:
const os::phys_ptr<os::paging::page<0>> kernel_e{amy::ptr(&_kernel_phys_end)};
// Remove kernel from available RAM:
- for (amy::ptr i = 0; i < available_ram_length; i++) {
+ for (amy::diff i = 0; i < available_ram.size(); i++) {
if (kernel_e < available_ram[i].start_address || available_ram[i].end_address < kernel_s) {
continue;
}
} else if (available_ram[i].end_address <= kernel_e) {
available_ram[i].end_address = kernel_s - 1; // Since start_address < s, start_address <= new end_address.
} else {
- os::assert(available_ram_length < 50, "Too much available RAM sections to initialise correctly. Will fix eventually, probably.");
- available_ram[available_ram_length] = available_ram[i];
+ os::assert(available_ram.push_back(available_ram[i]),
+ "Too much available RAM sections to initialise correctly. Will fix eventually, probably.");
available_ram[i].end_address = kernel_s - 1;
- available_ram[available_ram_length].start_address = kernel_e + 1;
- available_ram_length++;
+ available_ram[available_ram.size() - 1].start_address = kernel_e + 1;
}
}
// Add available RAM to the page allocator (warning: overrides the multiboot info structure and the multiboot modules):
- for (amy::ptr i = 0; i < available_ram_length; i++) {
+ for (amy::diff i = 0; i < available_ram.size(); i++) {
os::paging::page_allocator.deallocate({
.ptr = available_ram[i].start_address,
.size = amy::size(available_ram[i].end_address - available_ram[i].start_address + 1)
--- /dev/null
+// Copyright 2023 Amélia COUTARD.
+//
+// This file from the program "voyage au centre des fichiers" 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 3 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, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <types.hpp>
+#include <memory.hpp>
+#include <concept.hpp>
+
+// The design is based on Andrei Alexandrescu's "std::allocator is to allocation what std::vector is to vexation".
+// See https://github.com/CppCon/CppCon2015/tree/master/Presentations/allocator%20Is%20to%20Allocation%20what%20vector%20Is%20to%20Vexation for more details.
+
+namespace amy {
+
+template <amy::size> using allocator_helper = void;
+
+template <typename T>
+concept allocator = requires(T allocator) {
+ // max_align is the maximum alignment that this allocator can provide.
+ // post: max_align ≥ 1
+ // post: max_align is a power of 2
+ typename allocator_helper<T::max_align>;
+ // good_size(size, align) returns the actual amount of bytes that will be allocated for a call to allocate(size, align).
+ // pre: size ≥ 1
+ // pre: align ≥ 1
+ // pre: align is a power of 2
+ // pre: max_align ≡ 0 [align]
+ // post: good_size(size, align) ≥ size
+ // post: ∀n ≥ 0, good_size(size + n, align) ≥ good_size(size, align)
+ // post: ∀n ≥ 0, good_size(size, align + n) ≥ good_size(size, align)
+ { T::good_size(amy::declval<amy::size>(), amy::declval<amy::size>()) } -> amy::same_as<amy::size>;
+ typename allocator_helper<T::good_size(0, 0)>;
+ // allocate(size, align) returns a pointer to a memory area of at least size bytes, aligned to align bytes, or nullptr
+ // pre: size ≥ 1
+ // pre: align ≥ 1
+ // pre: align is a power of 2
+ // pre: max_align ≡ 0 [align]
+ { allocator.allocate(amy::declval<amy::size>(), amy::declval<amy::size>()) } -> amy::same_as<void*>;
+ // expand(ptr, size, delta) tries to change the size of the allocation at ptr from size to size+delta.
+ // If delta = 0, returns true and does nothing.
+ // If the memory area at ptr can be resized to size, do it and return true.
+ // Otherwise, return false, leaving the memory at ptr unchanged.
+ // pre: ptr is a pointer to a memory area of size size allocated by this allocator
+ // pre: size + delta ≥ 1
+ { allocator.expand(amy::declval<void*>(), amy::declval<amy::size>(), amy::declval<amy::diff>()) } -> amy::same_as<bool>;
+ // deallocate(ptr, size, align) invalidates the memory area at ptr.
+ // This function cannot fail.
+ // pre: ptr is a pointer to a memory area of size size and alignment align allocated by this allocator
+ { allocator.deallocate(amy::declval<void*>(), amy::declval<amy::size>(), amy::declval<amy::size>()) } -> amy::same_as<void>;
+};
+
+
+// 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.
+// If this fails, it will return amy::nil.
+// Otherwise, it will return the new pointer to the area. The old one shouldn't be used anymore.
+// 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) {
+ if (allocator.expand(ptr, old_size, new_size - old_size)) {
+ return ptr;
+ }
+ void* res = allocator.allocate(new_size, align);
+ if (res == amy::nil) {
+ return amy::nil;
+ }
+ amy::memcpy(res, ptr, old_size < new_size ? old_size : new_size);
+ allocator.deallocate(ptr, old_size, align);
+ return res;
+}
+
+template <typename T>
+concept knowning_allocator = allocator<T> && 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<void*>()) } -> amy::same_as<bool>;
+};
+
+template <amy::size data_size, amy::size data_align> 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) {
+ return size;
+ }
+ static constexpr amy::size max_align = data_align;
+ void* allocate(amy::size size, amy::size 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;
+ }
+ first_free_byte = aligned_first_free_byte + size;
+ return &data[aligned_first_free_byte];
+ }
+ bool expand(void* ptr, amy::size size, amy::diff delta) {
+ if (delta == 0) {
+ return true;
+ }
+ if ((amy::byte*)ptr - data + size == first_free_byte) {
+ if ((amy::byte*)ptr - data + size + delta > data_size) {
+ return false;
+ }
+ first_free_byte = (amy::byte*)ptr - data + size + delta;
+ return true;
+ } else {
+ return delta < 0;
+ }
+ }
+ void deallocate(void* ptr, amy::size size, amy::size) {
+ if ((amy::byte*)ptr - data + size == first_free_byte) {
+ first_free_byte = (amy::byte*)ptr - data;
+ }
+ }
+ bool owns(void* ptr) {
+ return amy::ptr(&data) <= amy::ptr(ptr) && amy::ptr(ptr) < amy::ptr(&data) + data_size;
+ }
+private:
+ amy::byte __attribute__((aligned(data_align))) data[data_size];
+ amy::diff first_free_byte = 0;
+};
+
+template <allocator suballocator_t, amy::size min_data_size, amy::size max_data_size, amy::size min_data_align, amy::size max_data_align>
+struct freelist {
+ static_assert(min_data_size >= 1);
+ static_assert(max_data_size >= min_data_size);
+ // static_assert(max_data_size >= amy::byte_size<amy::byte*>()); // 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.
+ static_assert((max_data_align & (max_data_align - 1)) == 0); // Assert power of 2.
+ static_assert(max_data_align <= suballocator_t::max_align);
+ static_assert(min_data_size == 1 || suballocator_t::good_size(min_data_size - 1, max_data_align) < min_data_size);
+ static_assert(suballocator_t::good_size(max_data_size, max_data_align) == max_data_size);
+private:
+ struct block {
+ block* next;
+ amy::byte padding[max_data_size - amy::byte_size<block*>];
+ };
+
+public:
+ static constexpr amy::size good_size(amy::size size, amy::size 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);
+ }
+ return max_data_size;
+ }
+ static constexpr amy::size max_align = suballocator_t::max_align;
+ void* allocate(amy::size size, amy::size align) {
+ if (size < min_data_size || max_data_size < size || align < min_data_align || max_data_align < align) {
+ return sub.allocate(size, align);
+ }
+ if (head == amy::nil) {
+ return sub.allocate(max_data_size, max_data_align);
+ }
+ block* new_head = head->next;
+ block* old_head = head;
+ old_head->~block();
+ head = new_head;
+ return old_head;
+ }
+ bool expand(void* ptr, amy::size size, amy::diff delta) {
+ if (min_data_size <= size && size <= max_data_size) {
+ delta = (size + delta) - max_data_size;
+ size = max_data_size;
+ }
+ if (min_data_size <= size + delta && size + delta <= max_data_size) {
+ delta = max_data_size - size;
+ }
+ return sub.expand(ptr, size, delta);
+ }
+ void deallocate(void* ptr, amy::size size, amy::size align) {
+ if (size < min_data_size || max_data_size < size || align < min_data_align || max_data_align < align) {
+ return sub.deallocate(ptr, size, align);
+ }
+ new(ptr) block;
+ amy::launder(reinterpret_cast<block*>(ptr))->next = head;
+ head = amy::launder(reinterpret_cast<block*>(ptr));
+ }
+ bool owns(void* ptr) requires knowning_allocator<suballocator_t> {
+ return sub.owns(ptr);
+ }
+private:
+ suballocator_t sub;
+ block* head = amy::nil;
+};
+
+} // namespace amy
--- /dev/null
+// Copyright 2023 Amélia COUTARD.
+//
+// This file from the program "voyage au centre des fichiers" 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 3 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, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <types.hpp>
+#include <memory.hpp>
+#include <allocator.hpp>
+
+namespace amy {
+
+template <typename T, allocator allocator_t>
+struct vector {
+ static_assert(amy::byte_align<T>() <= allocator_t::max_align);
+
+public:
+ vector(allocator_t& allocator): allocator(allocator) {
+ size_ = 0;
+ capacity_ = 1;
+ data = (T*)allocator.allocate(capacity_ * amy::byte_size<T>(), amy::byte_align<T>());
+ }
+ bool construction_failed() {
+ return data == amy::nil;
+ }
+ ~vector() {
+ if (data != amy::nil) {
+ for (amy::diff i = 0; i < size_; i++) {
+ data[i].~T();
+ }
+ allocator.deallocate(data, capacity_ * amy::byte_size<T>(), amy::byte_align<T>());
+ }
+ }
+
+ vector(const vector& other) {
+ size_ = other.size_;
+ capacity_ = size_ == 0 ? 1 : size_;
+ data = allocator.allocate(capacity_ * amy::byte_size<T>(), amy::byte_align<T>());
+ if (data == amy::nil) {
+ return;
+ }
+ for (amy::diff i = 0; i < size_; i++) {
+ new(&data[i]) T(other.data[i]);
+ }
+ }
+ vector(vector&& other): allocator(other.allocator) {
+ data = other.data;
+ other.data = amy::nil;
+ 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;
+
+ amy::size size() {
+ return size_;
+ }
+ amy::size capacity() {
+ return capacity_;
+ }
+ bool push_back(const T& v) {
+ if (size_ == capacity_) {
+ if (allocator.expand(data, capacity_ * amy::byte_size<T>(), amy::byte_size<T>())) {
+ capacity_++;
+ } else if (auto new_data =
+ amy::reallocate(allocator, data, capacity_ * amy::byte_size<T>(), capacity_ * 2 * amy::byte_size<T>(), amy::byte_align<T>())) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-pointer"
+ data = (T*)new_data;
+#pragma GCC diagnostic pop
+ capacity_ *= 2;
+ } else {
+ return false;
+ }
+ }
+ size_++;
+ new(&data[size_ - 1]) T(v);
+ return true;
+ }
+ T& operator[](amy::size i) {
+ return data[i];
+ }
+ const T& operator[](amy::size i) const {
+ return data[i];
+ }
+
+private:
+ T* data;
+ amy::size size_;
+ amy::size capacity_;
+ allocator_t& allocator;
+};
+
+} // namespace amy