// Copyright Maciej Sobczak 2008-2019. // This file is part of YAMI4. // // YAMI4 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. // // YAMI4 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 YAMI4. If not, see . #include "allocator.h" #include "fatal_errors.h" #include using namespace yami; using namespace details; namespace // unnamed { struct segment_header { segment_header * next_; segment_header * prev_; bool free_; // used only when free_ == true segment_header * next_free_; segment_header * prev_free_; union alignment { double dummy_; char data_buffer_; } aligned; }; // the smallest segment that can be be split off (including header) const std::size_t smallest_split_segment_size = 64; // the round-up amount for alignment of split segments const std::size_t round_up_amount = sizeof(double); std::size_t buffer_size( segment_header * segment, void * base, std::size_t total_size) { std::size_t result; if (segment->next_ != NULL) { // this is not the last segment result = reinterpret_cast(segment->next_) - &segment->aligned.data_buffer_; } else { // this is the last segment result = total_size - (&segment->aligned.data_buffer_ - reinterpret_cast(base)); } return result; } } // namespace unnamed allocator::allocator() : base_(NULL), size_(0), first_free_segment_(NULL) { } void allocator::set_working_area(void * buf, std::size_t size) { if (base_ != NULL) { fatal_failure(__FILE__, __LINE__); } if (buf != NULL && size != 0) { base_ = buf; size_ = size; segment_header * initial_segment = reinterpret_cast(base_); initial_segment->next_ = NULL; initial_segment->prev_ = NULL; initial_segment->free_ = true; initial_segment->next_free_ = NULL; initial_segment->prev_free_ = NULL; first_free_segment_ = initial_segment; } } void * allocator::allocate(std::size_t requested_size) { void * result = NULL; if (base_ == NULL) { result = std::malloc(requested_size); } else { // iterate over the list of free segments // and find the first that is big enough requested_size += round_up_amount - 1; requested_size /= round_up_amount; requested_size *= round_up_amount; segment_header * segment = reinterpret_cast(first_free_segment_); while (segment != NULL && result == NULL) { const std::size_t buf_size = buffer_size(segment, base_, size_); if (buf_size >= requested_size) { std::size_t remaining_space = buf_size - requested_size; if (remaining_space >= smallest_split_segment_size) { // the remaining space is enough // to contain another segment // -> split segments segment_header * new_segment = reinterpret_cast( &segment->aligned.data_buffer_ + buf_size - remaining_space); new_segment->next_ = segment->next_; new_segment->prev_ = segment; new_segment->free_ = true; segment->next_ = new_segment; if (new_segment->next_ != NULL) { new_segment->next_->prev_ = new_segment; } new_segment->next_free_ = segment->next_free_; new_segment->prev_free_ = segment; segment->next_free_ = new_segment; } result = &segment->aligned.data_buffer_; } else { segment = segment->next_free_; } } if (result != NULL) { // appropriate free segment was found // -> mark it as not free and adjust free list links segment->free_ = false; if (segment->prev_free_ != NULL) { segment->prev_free_->next_free_ = segment->next_free_; } else { // this was the first free segment first_free_segment_ = segment->next_free_; } if (segment->next_free_ != NULL) { segment->next_free_->prev_free_ = segment->prev_free_; } } } return result; } void allocator::deallocate(const void * p) { if (base_ == NULL) { std::free(const_cast(p)); } else { segment_header * segment = reinterpret_cast( static_cast(const_cast(p)) - offsetof(segment_header, aligned)); segment->free_ = true; // reestablish free list links if (segment == first_free_segment_) { fatal_failure(__FILE__, __LINE__); } if (first_free_segment_ == NULL || segment < static_cast(first_free_segment_)) { // this segment should be the new first known free segment segment->prev_free_ = NULL; segment->next_free_ = static_cast(first_free_segment_); if (first_free_segment_ != NULL) { static_cast( first_free_segment_)->prev_free_ = segment; } first_free_segment_ = segment; } else { // this segment is further than the first known free segment // -> find the closest earlier free segment segment_header * iter = segment->prev_; while (iter->free_ == false) { iter = iter->prev_; } segment->prev_free_ = iter; iter->next_free_ = segment; // -> find the closest further free segment iter = segment->next_; while (iter != NULL && iter->free_ == false) { iter = iter->next_; } segment->next_free_ = iter; if (iter != NULL) { iter->prev_free_ = segment; } } if (segment->next_ != NULL) { // this is not the last segment // -> attempt to join with the next one, if it is free segment_header * next_segment = segment->next_; if (next_segment->free_) { segment->next_ = next_segment->next_; if (next_segment->next_ != NULL) { next_segment->next_->prev_ = segment; } segment->next_free_ = next_segment->next_free_; if (next_segment->next_free_ != NULL) { next_segment->next_free_->prev_free_ = segment; } } } if (segment->prev_ != NULL) { // this is not the first segment // -> attempt to join with the previous one, if it is free segment_header * previous_segment = segment->prev_; if (previous_segment->free_) { previous_segment->next_ = segment->next_; if (segment->next_ != NULL) { segment->next_->prev_ = previous_segment; } previous_segment->next_free_ = segment->next_free_; if (segment->next_free_ != NULL) { segment->next_free_->prev_free_ = previous_segment; } } } } } void allocator::get_free_size(std::size_t & biggest, std::size_t & all) const { biggest = 0; all = 0; if (base_ != NULL) { segment_header * segment = reinterpret_cast(first_free_segment_); while (segment != NULL) { const std::size_t buf_size = buffer_size(segment, base_, size_); if (buf_size > biggest) { biggest = buf_size; } all += buf_size; segment = segment->next_free_; } } }