code: /*
   * Copyright (c) 2019 the Eclipse Milo Authors
   *
   * This program and the accompanying materials are made
   * available under the terms of the Eclipse Public License 2.0
   * which is available at https://www.eclipse.org/legal/epl-2.0/
   *
   * SPDX-License-Identifier: EPL-2.0
   */
  
  package org.eclipse.milo.opcua.sdk.server.util;
  
  import java.util.Arrays;
  import java.util.NoSuchElementException;
  
  import org.jetbrains.annotations.NotNull;
  
  public class RingBuffer<E> {
  
      private int read = 0;
      private int write = 0;
      private int count = 0;
  
      private final E[] buffer;
      private final int maxSize;
  
      public RingBuffer(int maxSize) {
          this.maxSize = maxSize;
  
          //noinspection unchecked
          buffer = (E[]) new Object[maxSize];
      }
  
      /**
       * Get the element at the specified index. This does not affect the next element to be returned.
       *
       * @param index The specified index.
       * @return The element at the specified index.
       * @throws IndexOutOfBoundsException if the index is out of bounds...
       */
      public E get(int index) {
          if (index >= count) {
              throw new IndexOutOfBoundsException("index=" + index);
          } else {
              return buffer[(read + index) % maxSize];
          }
      }
  
      /**
       * Set the element at the specified index. This does not affect the next element to be returned.
       *
       * @param index The specified index.
       * @param e     The element to set.
       * @throws IndexOutOfBoundsException if the index is out of bounds...
       */
      public void set(int index, E e) {
          if (index >= count) {
              throw new IndexOutOfBoundsException("index=" + index);
          } else {
              buffer[(read + index) % maxSize] = e;
          }
      }
  
      /**
       * Add an element to the buffer, replacing the oldest element in the buffer if full.
       *
       * @param e element to add.
       */
      public void add(@NotNull E e) {
          buffer[write] = e;
          write = (write + 1) % maxSize;
  
          if (count == maxSize) {
              read = (read + 1) % maxSize;
          } else {
              count += 1;
          }
      }
  
      /**
       * @return The next (oldest) element in the buffer.
       * @throws NoSuchElementException if the buffer is empty.
       */
      public E remove() {
          if (count <= 0) {
              throw new NoSuchElementException();
          } else {
              final E e = buffer[read];
              buffer[read] = null;
              read = (read + 1) % maxSize;
              count -= 1;
              return e;
          }
      }
  
      /**
       * Clear the contents of this buffer.
       */
      public void clear() {
          read = write = count = 0;
          Arrays.fill(buffer, null);
      }
  
      /**
       * @return {@code true} if the buffer is empty (size == 0).
       */
      public boolean isEmpty() {
          return size() == 0;
      }
  
      /**
       * @return The maximum allowed size (number of elements).
       */
      public int maxSize() {
          return maxSize;
      }
  
      /**
       * @return The current size (number of elements).
       */
      public int size() {
          return count;
      }
  
  }
  