/*
* ring_buffer.h
*
* This file is part of NEST.
*
* Copyright (C) 2004 The NEST Initiative
*
* NEST 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 2 of the License, or
* (at your option) any later version.
*
* NEST 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 NEST. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef RING_BUFFER_H
#define RING_BUFFER_H
#include <valarray>
#include <list>
#include "nest.h"
#include "scheduler.h"
#include "nest_time.h"
namespace nest
{
/**
Buffer Layout.
MODIFICATION 2005-06-19:
The explanation below no longer holds if we allow direct delivery of events
from devices such as the Poisson generator. The reasoning below applies only
to events in the central queue, which are held in that queue until the
beginning of the next slice, when system time has been advance from T to
T + min_delay. Direct delivery events, in contrast are delivered when system
time is still T. Their earliest delivery time is
min T_d = T + min_del
and the latest
max T_d = T + (min_del-1) + max_del = T + min_del + max_del - 1
Since we still need to keep the entries 0..min_del-1 for readout during the
time slice beginning at T, we need a buffer with min_del+max_del elements.
SUPERSEEDED:
Let S be the time at the beginning of the present time slice (from).
All spikes arriving during this time slice, must have been emitted during
the previous time slice, which started at S - min_del. Then, the earliest
spike delivery time (compare Time Memo) is
min T_d = S-min_del + min_del = S
and the latest
max T_d = S-1 + max_del = S + (max_del - 1)
Thus,
0 <= S - T_d <= max_del - 1
so that the ring buffer needs max_del elements.
Each field represents an entry in the valarray.
*/
class RingBuffer {
public:
RingBuffer();
/**
* Add a value to the ring buffer.
* @param offs Arrival time relative to beginning of slice.
* @param double_t Value to add.
*/
void add_value(const long_t offs, const double_t);
/**
* Set a ring buffer entry to a given value.
* @param offs Arrival time relative to beginning of slice.
* @param double_t Value to set.
*/
void set_value(const long_t offs, const double_t);
/**
* Read one value from ring buffer.
* @param offs Offset of element to read within slice.
* @returns value
*/
double get_value(const long_t offs);
/**
* Initialize the buffer with noughts.
* Also resizes the buffer if necessary.
*/
void clear();
/**
* Resize the buffer according to max_thread and max_delay.
* New elements are filled with noughts.
* @note resize() has no effect if the buffer has the correct size.
*/
void resize();
/**
* Returns buffer size, for memory measurement.
*/
size_t size() const { return buffer_.size(); }
private:
//! Buffered data
std::valarray<double_t> buffer_;
/**
* Obtain buffer index.
* @param delay delivery delay for event
* @returns index to buffer element into which event should be
* recorded.
*/
size_t get_index_(const delay d) const;
};
inline
void RingBuffer::add_value(const long_t offs, const double_t v)
{
buffer_[get_index_(offs)] += v;
}
inline
void RingBuffer::set_value(const long_t offs, const double_t v)
{
buffer_[get_index_(offs)] = v;
}
inline
double RingBuffer::get_value(const long_t offs)
{
assert(0 <= offs && (size_t)offs < buffer_.size());
assert((delay)offs < Scheduler::get_min_delay());
// offs == 0 is beginning of slice, but we have to
// take modulo into account when indexing
long_t idx = get_index_(offs);
double_t val = buffer_[idx];
buffer_[idx] = 0.0; // clear buffer after reading
return val;
}
inline
size_t RingBuffer::get_index_(const delay d) const
{
const long_t idx = Scheduler::get_modulo(d);
assert(0 <= idx);
assert((size_t)idx < buffer_.size());
return idx;
}
class MultRBuffer {
public:
MultRBuffer();
/**
* Add a value to the ring buffer.
* @param offs Arrival time relative to beginning of slice.
* @param double_t Value to add.
*/
void add_value(const long_t offs, const double_t);
/**
* Read one value from ring buffer.
* @param offs Offset of element to read within slice.
* @returns value
*/
double get_value(const long_t offs);
/**
* Initialize the buffer with noughts.
*/
void clear();
/**
* Resize the buffer according to max_thread and max_delay.
*/
void resize();
/**
* Returns buffer size, for memory measurement.
*/
size_t size() const { return buffer_.size(); }
private:
//! Buffered data
std::valarray<double_t> buffer_;
/**
* Obtain buffer index.
* @param delay delivery delay for event
* @returns index to buffer element into which event should be
* recorded.
*/
size_t get_index_(const delay d) const;
};
inline
void MultRBuffer::add_value(const long_t offs, const double_t v)
{
assert(0<= offs && (size_t)offs < buffer_.size());
buffer_[get_index_(offs)] *= v;
}
inline
double MultRBuffer::get_value(const long_t offs)
{
assert(0 <= offs && (size_t)offs < buffer_.size());
assert((delay)offs < Scheduler::get_min_delay());
// offs == 0 is beginning of slice, but we have to
// take modulo into account when indexing
long_t idx = get_index_(offs);
double_t val = buffer_[idx];
buffer_[idx] = 0.0; // clear buffer after reading
return val;
}
inline
size_t MultRBuffer::get_index_(const delay d) const
{
const long_t idx = Scheduler::get_modulo(d);
assert(0 <= idx && (size_t)idx < buffer_.size());
return idx;
}
class ListRingBuffer {
public:
ListRingBuffer();
/**
* Append a value to the ring buffer list.
* @param offs Arrival time relative to beginning of slice.
* @param double_t Value to append.
*/
void append_value(const long_t offs, const double_t);
std::list<double_t>& get_list(const long_t offs);
/**
* Initialize the buffer with empty lists.
* Also resizes the buffer if necessary.
*/
void clear();
/**
* Resize the buffer according to max_thread and max_delay.
* New elements are filled with empty lists.
* @note resize() has no effect if the buffer has the correct size.
*/
void resize();
/**
* Returns buffer size, for memory measurement.
*/
size_t size() const { return buffer_.size(); }
private:
//! Buffered data
std::vector<std::list<double_t> > buffer_;
/**
* Obtain buffer index.
* @param delay delivery delay for event
* @returns index to buffer element into which event should be
* recorded.
*/
size_t get_index_(const delay d) const;
};
inline
void ListRingBuffer::append_value(const long_t offs, const double_t v)
{
buffer_[get_index_(offs)].push_back(v);
}
inline
std::list<double_t>& ListRingBuffer::get_list(const long_t offs)
{
assert(0 <= offs && (size_t)offs < buffer_.size());
assert((delay)offs < Scheduler::get_min_delay());
// offs == 0 is beginning of slice, but we have to
// take modulo into account when indexing
long_t idx = get_index_(offs);
return buffer_[idx];
}
inline
size_t ListRingBuffer::get_index_(const delay d) const
{
const long_t idx = Scheduler::get_modulo(d);
assert(0 <= idx);
assert((size_t)idx < buffer_.size());
return idx;
}
}
#endif