| @ -0,0 +1,210 @@ | |||
| #pragma once | |||
| #include "gp/array.hpp" | |||
| #include "gp/algorithm/min_max.hpp" | |||
| #include "gp/algorithm/repeat.hpp" | |||
| #include "gp/bitops.hpp" | |||
| #include "gp/buffer.hpp" | |||
| #include "gp/pointers.hpp" | |||
| #include <atomic> | |||
| #include <cstdint> | |||
| template<size_t sz> | |||
| class memory_vdisk { | |||
| static_assert(sz%128 == 0, "in memory disk expects 128 bytes page alignment"); | |||
| alignas(128) gp::array<uint8_t, sz> data; | |||
| gp::buffer<uint8_t> read(gp::buffer<uint8_t> buffer, uint64_t offset) { | |||
| auto it = data.begin()+offset; | |||
| auto ret = buffer; | |||
| for(auto& c : buffer) { | |||
| c = *(it++); | |||
| if(it == data.end()) { | |||
| ret = buffer.slice_start(it - (data.begin() + offset)); | |||
| break; | |||
| } | |||
| } | |||
| return ret; | |||
| } | |||
| gp::buffer<uint8_t> write(gp::buffer<uint8_t> buffer, uint64_t offset) { | |||
| auto it = data.begin()+offset; | |||
| auto ret = buffer; | |||
| for(auto& c : buffer) { | |||
| *(it++) = c; | |||
| if(it == data.end()) { | |||
| ret = buffer.slice_start(it - (data.begin() + offset)); | |||
| break; | |||
| } | |||
| } | |||
| return ret; | |||
| } | |||
| constexpr uint64_t size() const noexcept { | |||
| return sz; | |||
| } | |||
| static constexpr size_t page_size() noexcept { | |||
| return 128; | |||
| } | |||
| constexpr uint64_t page_count() const noexcept { | |||
| return size() / page_size(); | |||
| } | |||
| }; | |||
| template<typename vdisk_ptr> | |||
| class tagfs { | |||
| vdisk_ptr disk; | |||
| const gp::array<uint8_t, decltype(*disk)::page_size()> empty_page; | |||
| struct disk_root { | |||
| gp::endian_wrapper<uint64_t, gp::endian::little> magic; | |||
| gp::endian_wrapper<uint64_t, gp::endian::little> first_allocator_page; | |||
| gp::endian_wrapper<uint64_t, gp::endian::little> allocator_shuttle; | |||
| gp::endian_wrapper<uint64_t, gp::endian::little> allocator_page_count; | |||
| gp::endian_wrapper<uint64_t, gp::endian::little> tag_list_node; | |||
| gp::endian_wrapper<uint64_t, gp::endian::little> page_count; | |||
| }; | |||
| struct file_description { | |||
| gp::endian_wrapper<uint32_t, gp::endian::little> reference_counter; | |||
| }; | |||
| tagfs(vdisk_ptr&& _disk) | |||
| : disk(gp::forward<vdisk_ptr>(disk)) | |||
| , empty_page{[](){return 0;}} | |||
| {} | |||
| disk_root get_disk_root() { | |||
| gp::array<disk_root, 1> vret; | |||
| return *disk->read(vret.as_buffer().template cast<uint8_t>(), 0).template cast<disk_root>().begin(); | |||
| } | |||
| void set_disk_root(disk_root& root) { | |||
| gp::array<disk_root, 1> vpar{root}; | |||
| disk->write(vpar.as_buffer().template cast<uint8_t>(), 0); | |||
| } | |||
| gp::optional<uint64_t> try_set_bit(gp::buffer<uint8_t> page) { | |||
| uint64_t idx = 0; | |||
| for(auto& elem : page) { | |||
| if(elem != 0xff) { | |||
| uint8_t copy = elem; | |||
| uint8_t setter = 1; | |||
| gp::repeat(8, [&](){ | |||
| bool value = copy & 1; | |||
| if(!value) { | |||
| return; | |||
| } | |||
| copy >>= 1; | |||
| setter <<= 1; | |||
| ++idx; | |||
| }); | |||
| elem |= setter; | |||
| return idx; | |||
| } | |||
| idx += 8; | |||
| } | |||
| return gp::nullopt; | |||
| } | |||
| uint64_t next_shuttle_page(disk_root root, uint64_t shuttle) { | |||
| return | |||
| shuttle + 1 == root.first_allocator_page + root.allocator_page_count ? | |||
| root.first_allocator_page | |||
| : shuttle + 1; | |||
| } | |||
| bool try_unset_bit(gp::buffer<uint8_t> page, uint64_t idx) { | |||
| uint8_t& target_byte = *(page.begin()+(idx/8)); | |||
| uint8_t flipper = 1 << (idx%8); | |||
| if(target_byte & flipper) { | |||
| target_byte ^= flipper; | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| uint64_t allocate_page() { | |||
| disk_root root = get_disk_root(); | |||
| uint64_t begin_page = root.first_allocator_page; | |||
| uint64_t shuttle_page = root.allocator_shuttle; | |||
| uint64_t end_page = root.first_allocator_page + root.allocator_page_count; | |||
| gp::array<uint8_t, decltype(*disk)::page_size()> page_contents; | |||
| gp::optional<uint64_t> page; | |||
| do | |||
| { | |||
| auto allocator_page = disk->read(page_contents.as_buffer(), shuttle_page*decltype(*disk)::page_size()); | |||
| if(shuttle_page == end_page - 1) { | |||
| uint64_t existing_pages = root.page_count - end_page; | |||
| uint64_t allocable_pages = root.allocator_page_count*8*decltype(*disk)::page_size(); | |||
| if(existing_pages < allocable_pages) { | |||
| uint64_t extra = allocable_pages - existing_pages; | |||
| extra /= 8; | |||
| allocator_page = allocator_page.slice_start(decltype(*disk)::page_size() - extra); | |||
| } | |||
| } | |||
| page = try_set_bit(allocator_page); | |||
| if(!page.has_value()) { | |||
| root.allocator_shuttle = (shuttle_page = next_shuttle_page(shuttle_page)); | |||
| } else { | |||
| disk->write(page_contents.as_buffer(), shuttle_page*decltype(*disk)::page_size()); | |||
| page.value += decltype(*disk)::page_size()*8*(shuttle_page-begin_page); | |||
| } | |||
| } | |||
| while(!page.has_value()); | |||
| set_disk_root(root); | |||
| return page.value() + end_page; | |||
| } | |||
| bool deallocate_page(uint64_t page) { | |||
| disk_root root = get_disk_root(); | |||
| page -= root.first_allocator_page + root.allocator_page_count; | |||
| uint64_t discriminant = decltype(*disk)::page_size()*8; | |||
| uint64_t allocator_page = page/discriminant; | |||
| uint64_t pos_page = page%discriminant; | |||
| gp::array<uint8_t, decltype(*disk)::page_size()> store; | |||
| disk->read(store.as_buffer(), decltype(*disk)::page_size()*allocator_page); | |||
| bool ret = try_unset_bit(store.as_buffer(), pos_page); | |||
| disk->write(store.as_buffer(), decltype(*disk)::page_size()*allocator_page); | |||
| return ret; | |||
| } | |||
| void clear_page(uint64_t page) { | |||
| disk->write(empty_page.as_buffer(), page*decltype(*disk)::page_size()); | |||
| } | |||
| void format() { | |||
| auto sz = disk->size(); | |||
| auto page_sz = disk->page_size(); | |||
| auto page_count = sz /page_sz; | |||
| auto remaining_pages = page_count; | |||
| disk_root root; | |||
| // tagmebro | |||
| root.magic = 0x7461676D6562726F; | |||
| root.page_count = page_count; | |||
| root.first_allocator_page = 1; | |||
| root.allocator_shuttle = 1; | |||
| // Removing the root page | |||
| remaining_pages -= 1; | |||
| // calculating datapages | |||
| auto datapage_count = (8*remaining_pages*page_sz)/(1+8*page_sz); | |||
| auto allocator_pages = remaining_pages - datapage_count; | |||
| root.allocator_page_count = allocator_pages; | |||
| for(uint64_t offset = 0; offset < allocator_pages; ++offset) { | |||
| clear_page(root.first_allocator_page); | |||
| } | |||
| root.tag_list_node = 0; | |||
| set_disk_root(root); | |||
| } | |||
| }; | |||