Program Listing for File ringbuffer.hpp

Return to documentation for file (include/ros2_ouster/ringbuffer.hpp)

// Copyright 2022, Mirko Kugelmeier
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef ROS2_OUSTER__RINGBUFFER_HPP_
#define ROS2_OUSTER__RINGBUFFER_HPP_

#include <atomic>
#include <memory>

namespace ros2_ouster
{

class RingBuffer
{
public:
  RingBuffer(std::size_t element_size, std::size_t num_elements)
    : _element_size(element_size),
      _num_elements(num_elements),
      _head(0),
      _tail(0),
      _buf(new uint8_t[element_size * _num_elements])
  {}

  bool empty()
  {
    return _head == _tail;
  }

  bool full()
  {
    // Make sure we have a consistent value for the head index here.
    // This makes sure we do not return 'false positives' when the buffer is not actually
    // full due to a race on _head between the conditions.
    bool head = _head;

    // This ringbuffer implementation lets both head and tail loop twice around the actual number of
    // elements in the buffer to encode the difference between the empty() and full() states. The
    // buffer is full if head and tail refer to the same element but on different 'iterations'
    // of the loop
    return ((_tail * 2) % _num_elements) == head && head != _tail;
  }

  uint8_t * head()
  {
    return &_buf[(_head % _num_elements) * _element_size];
  }

  uint8_t * tail()
  {
    return &_buf[(_tail % _num_elements) * _element_size];
  }

  void pop()
  {
    _head = (_head + 1) % (_num_elements * 2);
  }

  void push()
  {
    _tail = (_tail + 1) % (_num_elements * 2);
  }

protected:
  // The size of each individual element in bytes and the max number of elements in the buffer
  const std::size_t _element_size;
  const std::size_t _num_elements;

  // Since the ringbuffer is used across threads that produce / consume the data, we use
  // std::atomic for head and tail to prevent re-ordering of assignments to these variables
  std::atomic<std::size_t> _head;
  std::atomic<std::size_t> _tail;

  // Defining the backing buffer as a unique_ptr gives us our dtor and correct copy/move
  // semantics for free
  std::unique_ptr<uint8_t[]> _buf;
};

}  // namespace ros2_ouster

#endif  // ROS2_OUSTER__RINGBUFFER_HPP_