circlebuf.hh revision 12334
12SN/A/*
211008Sandreas.sandberg@arm.com * Copyright (c) 2015 ARM Limited
311008Sandreas.sandberg@arm.com * All rights reserved
411008Sandreas.sandberg@arm.com *
511008Sandreas.sandberg@arm.com * The license below extends only to copyright in the software and shall
611008Sandreas.sandberg@arm.com * not be construed as granting a license to any other intellectual
711008Sandreas.sandberg@arm.com * property including but not limited to intellectual property relating
811008Sandreas.sandberg@arm.com * to a hardware implementation of the functionality of the software
911008Sandreas.sandberg@arm.com * licensed hereunder.  You may use the software subject to the license
1011008Sandreas.sandberg@arm.com * terms below provided that you ensure that this notice is replicated
1111008Sandreas.sandberg@arm.com * unmodified and in its entirety in all distributions of the software,
1211008Sandreas.sandberg@arm.com * modified or unmodified, in source code or in binary form.
132SN/A *
142SN/A * Redistribution and use in source and binary forms, with or without
152SN/A * modification, are permitted provided that the following conditions are
162SN/A * met: redistributions of source code must retain the above copyright
172SN/A * notice, this list of conditions and the following disclaimer;
182SN/A * redistributions in binary form must reproduce the above copyright
192SN/A * notice, this list of conditions and the following disclaimer in the
202SN/A * documentation and/or other materials provided with the distribution;
212SN/A * neither the name of the copyright holders nor the names of its
222SN/A * contributors may be used to endorse or promote products derived from
232SN/A * this software without specific prior written permission.
242SN/A *
252SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
262SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
272SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
282SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
292SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
302SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
312SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
322SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
332SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
342SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
352SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
362665Ssaidi@eecs.umich.edu *
3711008Sandreas.sandberg@arm.com * Authors: Andreas Sandberg
382SN/A */
392SN/A
4011008Sandreas.sandberg@arm.com#ifndef __BASE_CIRCLEBUF_HH__
4111008Sandreas.sandberg@arm.com#define __BASE_CIRCLEBUF_HH__
422SN/A
4311008Sandreas.sandberg@arm.com#include <algorithm>
4411008Sandreas.sandberg@arm.com#include <cassert>
4511008Sandreas.sandberg@arm.com#include <vector>
462SN/A
4712334Sgabeblack@google.com#include "base/logging.hh"
4811008Sandreas.sandberg@arm.com#include "sim/serialize.hh"
4911008Sandreas.sandberg@arm.com
5011008Sandreas.sandberg@arm.com/**
5111008Sandreas.sandberg@arm.com * Circular buffer backed by a vector
5211008Sandreas.sandberg@arm.com *
5311008Sandreas.sandberg@arm.com * The data in the cricular buffer is stored in a standard
5411008Sandreas.sandberg@arm.com * vector. _start designates the first element in the buffer and _stop
5511008Sandreas.sandberg@arm.com * points to the last element + 1 (i.e., the position of the next
5611008Sandreas.sandberg@arm.com * insertion). The _stop index may be outside the range of the backing
5711008Sandreas.sandberg@arm.com * store, which means that the actual index must be calculated as
5811008Sandreas.sandberg@arm.com * _stop % capacity.
5911008Sandreas.sandberg@arm.com *
6011008Sandreas.sandberg@arm.com * Invariants:
6111008Sandreas.sandberg@arm.com * <ul>
6211008Sandreas.sandberg@arm.com *   <li>_start <= _stop
6311008Sandreas.sandberg@arm.com *   <li>_start < capacity
6411008Sandreas.sandberg@arm.com *   <li>_stop < 2 * capacity
6511008Sandreas.sandberg@arm.com * </ul>
6611008Sandreas.sandberg@arm.com */
6711008Sandreas.sandberg@arm.comtemplate<typename T>
682SN/Aclass CircleBuf
692SN/A{
7011008Sandreas.sandberg@arm.com  public:
7111008Sandreas.sandberg@arm.com    typedef T value_type;
722SN/A
73317SN/A  public:
7411008Sandreas.sandberg@arm.com    explicit CircleBuf(size_t size)
7511008Sandreas.sandberg@arm.com        : buf(size), _start(0), _stop(0) {}
762SN/A
7711008Sandreas.sandberg@arm.com    /** Is the buffer empty? */
7811008Sandreas.sandberg@arm.com    bool empty() const { return _stop == _start; }
7911008Sandreas.sandberg@arm.com    /**
8011008Sandreas.sandberg@arm.com     * Return the maximum number of elements that can be stored in
8111008Sandreas.sandberg@arm.com     * the buffer at any one time.
8211008Sandreas.sandberg@arm.com     */
8311008Sandreas.sandberg@arm.com    size_t capacity() const { return buf.size(); }
8411008Sandreas.sandberg@arm.com    /** Return the number of elements stored in the buffer. */
8511008Sandreas.sandberg@arm.com    size_t size() const { return _stop - _start; }
8611008Sandreas.sandberg@arm.com
8711008Sandreas.sandberg@arm.com    /**
8811008Sandreas.sandberg@arm.com     * Remove all the elements in the buffer.
8911008Sandreas.sandberg@arm.com     *
9011008Sandreas.sandberg@arm.com     * Note: This does not actually remove elements from the backing
9111008Sandreas.sandberg@arm.com     * store.
9211008Sandreas.sandberg@arm.com     */
9311008Sandreas.sandberg@arm.com    void flush() {
9411008Sandreas.sandberg@arm.com        _start = 0;
9511008Sandreas.sandberg@arm.com        _stop = 0;
9611008Sandreas.sandberg@arm.com    }
9711008Sandreas.sandberg@arm.com
9811008Sandreas.sandberg@arm.com    /**
9911008Sandreas.sandberg@arm.com     * Copy buffer contents without advancing the read pointer
10011008Sandreas.sandberg@arm.com     *
10111008Sandreas.sandberg@arm.com     * @param out Output iterator/pointer
10211008Sandreas.sandberg@arm.com     * @param len Number of elements to copy
10311008Sandreas.sandberg@arm.com     */
10411008Sandreas.sandberg@arm.com    template <class OutputIterator>
10511008Sandreas.sandberg@arm.com    void peek(OutputIterator out, size_t len) const {
10611460Sandreas.sandberg@arm.com        peek(out, 0, len);
10711460Sandreas.sandberg@arm.com    }
10811460Sandreas.sandberg@arm.com
10911460Sandreas.sandberg@arm.com    /**
11011460Sandreas.sandberg@arm.com     * Copy buffer contents without advancing the read pointer
11111460Sandreas.sandberg@arm.com     *
11211460Sandreas.sandberg@arm.com     * @param out Output iterator/pointer
11311460Sandreas.sandberg@arm.com     * @param offset Offset into the ring buffer
11411460Sandreas.sandberg@arm.com     * @param len Number of elements to copy
11511460Sandreas.sandberg@arm.com     */
11611460Sandreas.sandberg@arm.com    template <class OutputIterator>
11711460Sandreas.sandberg@arm.com    void peek(OutputIterator out, off_t offset, size_t len) const {
11811460Sandreas.sandberg@arm.com        panic_if(offset + len > size(),
11911008Sandreas.sandberg@arm.com                 "Trying to read past end of circular buffer.\n");
12011008Sandreas.sandberg@arm.com
12111460Sandreas.sandberg@arm.com        const off_t real_start((offset + _start) % buf.size());
12211460Sandreas.sandberg@arm.com        if (real_start + len <= buf.size()) {
12311460Sandreas.sandberg@arm.com            std::copy(buf.begin() + real_start,
12411460Sandreas.sandberg@arm.com                      buf.begin() + real_start + len,
12511008Sandreas.sandberg@arm.com                      out);
12611008Sandreas.sandberg@arm.com        } else {
12711460Sandreas.sandberg@arm.com            const size_t head_size(buf.size() - real_start);
12811008Sandreas.sandberg@arm.com            const size_t tail_size(len - head_size);
12911460Sandreas.sandberg@arm.com            std::copy(buf.begin() + real_start, buf.end(),
13011008Sandreas.sandberg@arm.com                      out);
13111008Sandreas.sandberg@arm.com            std::copy(buf.begin(), buf.begin() + tail_size,
13211008Sandreas.sandberg@arm.com                      out + head_size);
13311008Sandreas.sandberg@arm.com        }
13411008Sandreas.sandberg@arm.com    }
13511008Sandreas.sandberg@arm.com
13611008Sandreas.sandberg@arm.com    /**
13711008Sandreas.sandberg@arm.com     * Copy buffer contents and advance the read pointer
13811008Sandreas.sandberg@arm.com     *
13911008Sandreas.sandberg@arm.com     * @param out Output iterator/pointer
14011008Sandreas.sandberg@arm.com     * @param len Number of elements to read
14111008Sandreas.sandberg@arm.com     */
14211008Sandreas.sandberg@arm.com    template <class OutputIterator>
14311008Sandreas.sandberg@arm.com    void read(OutputIterator out, size_t len) {
14411008Sandreas.sandberg@arm.com        peek(out, len);
14511008Sandreas.sandberg@arm.com
14611008Sandreas.sandberg@arm.com        _start += len;
14711008Sandreas.sandberg@arm.com        normalize();
14811008Sandreas.sandberg@arm.com    }
14911008Sandreas.sandberg@arm.com
15011008Sandreas.sandberg@arm.com    /**
15111008Sandreas.sandberg@arm.com     * Add elements to the end of the ring buffers and advance.
15211008Sandreas.sandberg@arm.com     *
15311008Sandreas.sandberg@arm.com     * @param in Input iterator/pointer
15411008Sandreas.sandberg@arm.com     * @param len Number of elements to read
15511008Sandreas.sandberg@arm.com     */
15611008Sandreas.sandberg@arm.com    template <class InputIterator>
15711008Sandreas.sandberg@arm.com    void write(InputIterator in, size_t len) {
15811008Sandreas.sandberg@arm.com        // Writes that are larger than the backing store are allowed,
15911008Sandreas.sandberg@arm.com        // but only the last part of the buffer will be written.
16011008Sandreas.sandberg@arm.com        if (len > buf.size()) {
16111008Sandreas.sandberg@arm.com            in += len - buf.size();
16211008Sandreas.sandberg@arm.com            len = buf.size();
16311008Sandreas.sandberg@arm.com        }
16411008Sandreas.sandberg@arm.com
16511008Sandreas.sandberg@arm.com        const size_t next(_stop % buf.size());
16611008Sandreas.sandberg@arm.com        const size_t head_len(std::min(buf.size() - next, len));
16711008Sandreas.sandberg@arm.com
16811008Sandreas.sandberg@arm.com        std::copy(in, in + head_len, buf.begin() + next);
16911008Sandreas.sandberg@arm.com        std::copy(in + head_len, in + len, buf.begin());
17011008Sandreas.sandberg@arm.com
17111008Sandreas.sandberg@arm.com        _stop += len;
17211008Sandreas.sandberg@arm.com        // We may have written past the old _start pointer. Readjust
17311008Sandreas.sandberg@arm.com        // the _start pointer to remove the oldest entries in that
17411008Sandreas.sandberg@arm.com        // case.
17511008Sandreas.sandberg@arm.com        if (size() > buf.size())
17611008Sandreas.sandberg@arm.com            _start = _stop - buf.size();
17711008Sandreas.sandberg@arm.com
17811008Sandreas.sandberg@arm.com        normalize();
17911008Sandreas.sandberg@arm.com    }
18011008Sandreas.sandberg@arm.com
18111008Sandreas.sandberg@arm.com  protected:
18211008Sandreas.sandberg@arm.com    /**
18311008Sandreas.sandberg@arm.com     * Normalize the start and stop pointers to ensure that pointer
18411008Sandreas.sandberg@arm.com     * invariants hold after updates.
18511008Sandreas.sandberg@arm.com     */
18611008Sandreas.sandberg@arm.com    void normalize() {
18711008Sandreas.sandberg@arm.com        if (_start >= buf.size()) {
18811008Sandreas.sandberg@arm.com            _stop -= buf.size();
18911008Sandreas.sandberg@arm.com            _start -= buf.size();
19011008Sandreas.sandberg@arm.com        }
19111008Sandreas.sandberg@arm.com
19211008Sandreas.sandberg@arm.com        assert(_start < buf.size());
19311008Sandreas.sandberg@arm.com        assert(_stop < 2 * buf.size());
19411008Sandreas.sandberg@arm.com        assert(_start <= _stop);
19511008Sandreas.sandberg@arm.com    }
19611008Sandreas.sandberg@arm.com
19711008Sandreas.sandberg@arm.com  protected:
19811008Sandreas.sandberg@arm.com    std::vector<value_type> buf;
19911008Sandreas.sandberg@arm.com    size_t _start;
20011008Sandreas.sandberg@arm.com    size_t _stop;
20111008Sandreas.sandberg@arm.com
2022SN/A};
2032SN/A
20411008Sandreas.sandberg@arm.com
20511008Sandreas.sandberg@arm.com/**
20611008Sandreas.sandberg@arm.com * Simple FIFO implementation backed by a circular buffer.
20711008Sandreas.sandberg@arm.com *
20811008Sandreas.sandberg@arm.com * This class provides the same basic functionallity as the circular
20911008Sandreas.sandberg@arm.com * buffer with the folling differences:
21011008Sandreas.sandberg@arm.com * <ul>
21111008Sandreas.sandberg@arm.com *    <li>Writes are checked to ensure that overflows can't happen.
21211008Sandreas.sandberg@arm.com *    <li>Unserialization ensures that the data in the checkpoint fits
21311008Sandreas.sandberg@arm.com *        in the buffer.
21411008Sandreas.sandberg@arm.com * </ul>
21511008Sandreas.sandberg@arm.com */
21611008Sandreas.sandberg@arm.comtemplate<typename T>
21711008Sandreas.sandberg@arm.comclass Fifo
21811008Sandreas.sandberg@arm.com{
21911008Sandreas.sandberg@arm.com  public:
22011008Sandreas.sandberg@arm.com    typedef T value_type;
22111008Sandreas.sandberg@arm.com
22211008Sandreas.sandberg@arm.com  public:
22311008Sandreas.sandberg@arm.com    Fifo(size_t size)
22411008Sandreas.sandberg@arm.com        : buf(size) {}
22511008Sandreas.sandberg@arm.com
22611008Sandreas.sandberg@arm.com    bool empty() const { return buf.empty(); }
22711008Sandreas.sandberg@arm.com    size_t size() const { return buf.size(); }
22811008Sandreas.sandberg@arm.com    size_t capacity() const { return buf.capacity(); }
22911008Sandreas.sandberg@arm.com
23011008Sandreas.sandberg@arm.com    void flush() { buf.flush(); }
23111008Sandreas.sandberg@arm.com
23211008Sandreas.sandberg@arm.com    template <class OutputIterator>
23311008Sandreas.sandberg@arm.com    void peek(OutputIterator out, size_t len) const { buf.peek(out, len); }
23411008Sandreas.sandberg@arm.com    template <class OutputIterator>
23511008Sandreas.sandberg@arm.com    void read(OutputIterator out, size_t len) { buf.read(out, len); }
23611008Sandreas.sandberg@arm.com
23711008Sandreas.sandberg@arm.com    template <class InputIterator>
23811008Sandreas.sandberg@arm.com    void write(InputIterator in, size_t len) {
23911008Sandreas.sandberg@arm.com        panic_if(size() + len > capacity(),
24011008Sandreas.sandberg@arm.com                 "Trying to overfill FIFO buffer.\n");
24111008Sandreas.sandberg@arm.com        buf.write(in, len);
24211008Sandreas.sandberg@arm.com    }
24311008Sandreas.sandberg@arm.com
24411008Sandreas.sandberg@arm.com  private:
24511008Sandreas.sandberg@arm.com    CircleBuf<value_type> buf;
24611008Sandreas.sandberg@arm.com};
24711008Sandreas.sandberg@arm.com
24811008Sandreas.sandberg@arm.com
24911008Sandreas.sandberg@arm.comtemplate <typename T>
25012032Sandreas.sandberg@arm.comvoid
25111008Sandreas.sandberg@arm.comarrayParamOut(CheckpointOut &cp, const std::string &name,
25211008Sandreas.sandberg@arm.com              const CircleBuf<T> &param)
25311008Sandreas.sandberg@arm.com{
25411008Sandreas.sandberg@arm.com    std::vector<T> temp(param.size());
25511008Sandreas.sandberg@arm.com    param.peek(temp.begin(), temp.size());
25611008Sandreas.sandberg@arm.com    arrayParamOut(cp, name, temp);
25711008Sandreas.sandberg@arm.com}
25811008Sandreas.sandberg@arm.com
25911008Sandreas.sandberg@arm.comtemplate <typename T>
26012032Sandreas.sandberg@arm.comvoid
26111008Sandreas.sandberg@arm.comarrayParamIn(CheckpointIn &cp, const std::string &name,
26211008Sandreas.sandberg@arm.com             CircleBuf<T> &param)
26311008Sandreas.sandberg@arm.com{
26411008Sandreas.sandberg@arm.com    std::vector<T> temp;
26511008Sandreas.sandberg@arm.com    arrayParamIn(cp, name, temp);
26611008Sandreas.sandberg@arm.com
26711008Sandreas.sandberg@arm.com    param.flush();
26811008Sandreas.sandberg@arm.com    param.write(temp.cbegin(), temp.size());
26911008Sandreas.sandberg@arm.com}
27011008Sandreas.sandberg@arm.com
27111008Sandreas.sandberg@arm.comtemplate <typename T>
27212032Sandreas.sandberg@arm.comvoid
27311008Sandreas.sandberg@arm.comarrayParamOut(CheckpointOut &cp, const std::string &name,
27411008Sandreas.sandberg@arm.com              const Fifo<T> &param)
27511008Sandreas.sandberg@arm.com{
27611008Sandreas.sandberg@arm.com    std::vector<T> temp(param.size());
27711008Sandreas.sandberg@arm.com    param.peek(temp.begin(), temp.size());
27811008Sandreas.sandberg@arm.com    arrayParamOut(cp, name, temp);
27911008Sandreas.sandberg@arm.com}
28011008Sandreas.sandberg@arm.com
28111008Sandreas.sandberg@arm.comtemplate <typename T>
28212032Sandreas.sandberg@arm.comvoid
28311008Sandreas.sandberg@arm.comarrayParamIn(CheckpointIn &cp, const std::string &name,
28411008Sandreas.sandberg@arm.com             Fifo<T> &param)
28511008Sandreas.sandberg@arm.com{
28611008Sandreas.sandberg@arm.com    std::vector<T> temp;
28711008Sandreas.sandberg@arm.com    arrayParamIn(cp, name, temp);
28811008Sandreas.sandberg@arm.com
28911008Sandreas.sandberg@arm.com    fatal_if(param.capacity() < temp.size(),
29011008Sandreas.sandberg@arm.com             "Trying to unserialize data into too small FIFO\n");
29111008Sandreas.sandberg@arm.com
29211008Sandreas.sandberg@arm.com    param.flush();
29311008Sandreas.sandberg@arm.com    param.write(temp.cbegin(), temp.size());
29411008Sandreas.sandberg@arm.com}
29511008Sandreas.sandberg@arm.com
29611008Sandreas.sandberg@arm.com#endif // __BASE_CIRCLEBUF_HH__
297