Scripts/Runtime/Data/RingBuffer.cs (154 lines of code) (raw):

/* * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the license found in the * LICENSE file in the root directory of this source tree. */ using System; using UnityEngine; namespace Facebook.WitAi.Data { public class RingBuffer<T> { public delegate void OnDataAdded(T[] data, int offset, int length); public OnDataAdded OnDataAddedEvent; private readonly T[] buffer; private int bufferIndex; private long bufferDataLength; public int Capacity => buffer.Length; public void Clear(bool eraseData = false) { bufferIndex = 0; bufferDataLength = 0; if (eraseData) { for (int i = 0; i < buffer.Length; i++) { buffer[i] = default; } } } public class Marker { private long bufferDataIndex; private int index; private readonly RingBuffer<T> ringBuffer; public Marker(RingBuffer<T> ringBuffer, long markerPosition, int bufIndex) { this.ringBuffer = ringBuffer; bufferDataIndex = markerPosition; index = bufIndex; } public bool IsValid => ringBuffer.bufferDataLength - bufferDataIndex <= ringBuffer.Capacity; public int Read(T[] buffer, int offset, int length, bool skipToNextValid = false) { int read = -1; if (!IsValid && skipToNextValid && ringBuffer.bufferDataLength > ringBuffer.Capacity) { bufferDataIndex = ringBuffer.bufferDataLength - ringBuffer.Capacity; } if (IsValid) { read = this.ringBuffer.Read(buffer, offset, length, bufferDataIndex); bufferDataIndex += read; index += read; if (index > buffer.Length) index -= buffer.Length; } return read; } } public RingBuffer(int capacity) { buffer = new T[capacity]; } private int CopyToBuffer(T[] data, int offset, int length, int bufferIndex) { if (length > buffer.Length) throw new ArgumentException( "Push data exceeds buffer size."); if (bufferIndex + length < buffer.Length) { Array.Copy(data, offset, buffer, bufferIndex, length); return bufferIndex + length; } else { int len = Mathf.Min(length, buffer.Length); int endChunkLength = buffer.Length - bufferIndex; int wrappedChunkLength = len - endChunkLength; try { Array.Copy(data, offset, buffer, bufferIndex, endChunkLength); Array.Copy(data, offset + endChunkLength, buffer, 0, wrappedChunkLength); return wrappedChunkLength; } catch (ArgumentException e) { throw e; } } } private int CopyFromBuffer(T[] data, int offset, int length, int bufferIndex) { if (length > buffer.Length) throw new ArgumentException( $"Push data exceeds buffer size {length} < {buffer.Length}" ); if (bufferIndex + length < buffer.Length) { Array.Copy(buffer, bufferIndex, data, offset, length); return bufferIndex + length; } else { var l = Mathf.Min(buffer.Length, length); int endChunkLength = buffer.Length - bufferIndex; int wrappedChunkLength = l - endChunkLength; Array.Copy(buffer, bufferIndex, data, offset, endChunkLength); Array.Copy(buffer, 0, data, offset + endChunkLength, wrappedChunkLength); return wrappedChunkLength; } } public void Push(T[] data, int offset, int length) { lock (buffer) { bufferIndex = CopyToBuffer(data, offset, length, bufferIndex); bufferDataLength += length; OnDataAddedEvent?.Invoke(data, offset, length); } } public int Read(T[] data, int offset, int length, long bufferDataIndex) { if (bufferIndex == 0 && bufferDataLength == 0) // The ring buffer has been cleared. { return 0; } lock (buffer) { int read = (int) (Math.Min(bufferDataIndex + length, bufferDataLength) - bufferDataIndex); int bufferIndex = this.bufferIndex - (int) (bufferDataLength - bufferDataIndex); if (bufferIndex < 0) { bufferIndex = buffer.Length + bufferIndex; } CopyFromBuffer(data, offset, length, bufferIndex); return read; } } public Marker CreateMarker(int offset = 0) { var markerPosition = bufferDataLength + offset; if (markerPosition < 0) { markerPosition = 0; } int bufIndex = bufferIndex + offset; if (bufIndex < 0) { bufIndex = buffer.Length + bufIndex; } if (bufIndex > buffer.Length) { bufIndex -= buffer.Length; } var marker = new Marker(this, markerPosition, bufIndex); return marker; } } }