kopia lustrzana https://github.com/mobilinkd/tnc3-firmware
145 wiersze
3.6 KiB
C++
145 wiersze
3.6 KiB
C++
// Copyright 2015 Mobilinkd LLC <rob@mobilinkd.com>
|
|
// All rights reserved.
|
|
|
|
#ifndef MOBILINKD__SEGMENTED_BUFFER_HPP_
|
|
#define MOBILINKD__SEGMENTED_BUFFER_HPP_
|
|
|
|
#include "memory.hpp"
|
|
|
|
#include <boost/iterator/iterator_facade.hpp>
|
|
|
|
#include <cstdint>
|
|
|
|
namespace mobilinkd { namespace tnc { namespace buffer {
|
|
|
|
|
|
using boost::intrusive::list_base_hook;
|
|
using boost::intrusive::list;
|
|
using boost::intrusive::constant_time_size;
|
|
|
|
template <uint16_t SIZE, uint16_t CHUNK_SIZE = 256>
|
|
struct Pool {
|
|
typedef memory::chunk<CHUNK_SIZE> chunk_type;
|
|
typedef list<chunk_type, constant_time_size<false> > chunk_list;
|
|
chunk_type segments[SIZE];
|
|
chunk_list free_list;
|
|
|
|
Pool() {
|
|
for(uint16_t i = 0; i != SIZE; ++i) {
|
|
free_list.push_back(segments[i]);
|
|
}
|
|
}
|
|
|
|
bool allocate(chunk_list& list) {
|
|
bool result = false;
|
|
auto x = taskENTER_CRITICAL_FROM_ISR();
|
|
if (!free_list.empty()) {
|
|
list.splice(list.end(), free_list, free_list.begin());
|
|
result = true;
|
|
}
|
|
taskEXIT_CRITICAL_FROM_ISR(x);
|
|
return result;
|
|
}
|
|
|
|
void deallocate(chunk_list& list) {
|
|
auto x = taskENTER_CRITICAL_FROM_ISR();
|
|
free_list.splice(free_list.end(), list);
|
|
taskEXIT_CRITICAL_FROM_ISR(x);
|
|
}
|
|
};
|
|
|
|
template <typename POOL, POOL* allocator> struct SegmentedBufferIterator;
|
|
|
|
template <typename POOL, POOL* allocator>
|
|
struct SegmentedBuffer {
|
|
typedef uint8_t value_type;
|
|
typedef value_type* pointer;
|
|
typedef value_type& reference;
|
|
|
|
typedef SegmentedBufferIterator<POOL, allocator> iterator;
|
|
typedef const SegmentedBufferIterator<POOL, allocator> const_iterator;
|
|
|
|
typename POOL::chunk_list segments_;
|
|
typename POOL::chunk_list::iterator current_;
|
|
uint16_t size_;
|
|
|
|
SegmentedBuffer()
|
|
: segments_(), current_(segments_.end()), size_(0)
|
|
{}
|
|
|
|
~SegmentedBuffer() {
|
|
allocator->deallocate(segments_);
|
|
}
|
|
|
|
void clear() {
|
|
if (size_) {
|
|
allocator->deallocate(segments_);
|
|
size_ = 0;
|
|
current_ = segments_.end();
|
|
}
|
|
}
|
|
|
|
uint16_t size() const {return size_;}
|
|
|
|
bool push_back(value_type value) {
|
|
uint16_t offset = size_ & 0xFF;
|
|
if (offset == 0) { // Must allocate.
|
|
if (not allocator->allocate(segments_))
|
|
return false;
|
|
current_ = segments_.end();
|
|
--current_;
|
|
}
|
|
current_->buffer[offset] = value;
|
|
++size_;
|
|
return true;
|
|
}
|
|
|
|
iterator begin() __attribute__((noinline)) {
|
|
return iterator(segments_.begin(), 0);
|
|
}
|
|
iterator end() __attribute__((noinline)) {
|
|
return iterator(segments_.end(), size_);
|
|
}
|
|
};
|
|
|
|
template <typename POOL, POOL* allocator>
|
|
struct SegmentedBufferIterator : public boost::iterator_facade<
|
|
SegmentedBufferIterator<POOL, allocator>, uint8_t, boost::bidirectional_traversal_tag>
|
|
{
|
|
typename POOL::chunk_list::iterator iter_;
|
|
uint16_t index_;
|
|
|
|
SegmentedBufferIterator()
|
|
: iter_(), index_(0)
|
|
{}
|
|
|
|
SegmentedBufferIterator(typename POOL::chunk_list::iterator it, uint16_t index)
|
|
: iter_(it), index_(index)
|
|
{}
|
|
|
|
friend class boost::iterator_core_access;
|
|
|
|
void increment() {
|
|
++index_;
|
|
if ((index_ & 0xFF) == 0) ++iter_;
|
|
}
|
|
|
|
void decrement() {
|
|
if ((index_ & 0xFF) == 0) --iter_;
|
|
--index_;
|
|
}
|
|
|
|
bool equal(SegmentedBufferIterator const& other) const {
|
|
return (index_ == other.index_);
|
|
}
|
|
|
|
uint8_t& dereference() const {
|
|
return iter_->buffer[index_ & 0xFF];
|
|
}
|
|
|
|
};
|
|
|
|
}}} // mobilinkd::tnc::buffer
|
|
|
|
#endif // MOBILINKD__SEGMENTED_BUFFER_HPP_
|