123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- /*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- * All rights reserved.
- *
- * 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 Meta.WitAi.Data
- {
- public class RingBuffer<T>
- {
- public delegate void OnDataAdded(T[] data, int offset, int length);
- public delegate void ByteDataWriter(T[] buffer, int offset, int length);
- public OnDataAdded OnDataAddedEvent;
- private readonly T[] buffer;
- private int bufferIndex;
- private long bufferDataLength;
- public int Capacity => buffer.Length;
- public int GetBufferArrayIndex(long bufferDataIndex)
- {
- if (bufferDataLength <= bufferDataIndex) return -1;
- if (bufferDataLength - bufferDataIndex > buffer.Length) return -1;
- var endOffset = bufferDataLength - bufferDataIndex;
- var index = bufferIndex - endOffset;
- if (index < 0) index = buffer.Length + index;
- return (int) index;
- }
- public T this[long bufferDataIndex] => buffer[GetBufferArrayIndex(bufferDataIndex)];
- 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 RingBuffer<T> RingBuffer => 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 long AvailableByteCount => Math.Min(ringBuffer.Capacity, RequestedByteCount);
- public long RequestedByteCount => ringBuffer.bufferDataLength - bufferDataIndex;
- public long CurrentBufferDataIndex => bufferDataIndex;
- 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 void ReadIntoWriters(params ByteDataWriter[] writers)
- {
- if (!IsValid && ringBuffer.bufferDataLength > ringBuffer.Capacity)
- {
- bufferDataIndex = ringBuffer.bufferDataLength - ringBuffer.Capacity;
- }
- index = ringBuffer.GetBufferArrayIndex(bufferDataIndex);
- var length = (int) (ringBuffer.bufferDataLength - bufferDataIndex);
- if (IsValid && length > 0)
- {
- for (int i = 0; i < writers.Length; i++)
- {
- ringBuffer.WriteFromBuffer(writers[i], index, length);
- }
- }
- bufferDataIndex += length;
- index = ringBuffer.GetBufferArrayIndex(bufferDataIndex);
- }
- public Marker Clone()
- {
- return new Marker(ringBuffer, bufferDataIndex, index);
- }
- public void Offset(int amount)
- {
- bufferDataIndex += amount;
- if (bufferDataIndex < 0) bufferDataIndex = 0;
- if (bufferDataIndex > ringBuffer.bufferDataLength)
- {
- bufferDataIndex = ringBuffer.bufferDataLength;
- }
- index = ringBuffer.GetBufferArrayIndex(bufferDataIndex);
- }
- }
- 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;
- }
- }
- }
- public void WriteFromBuffer(ByteDataWriter writer, long bufferIndex, int length)
- {
- lock (buffer)
- {
- if (bufferIndex + length < buffer.Length)
- {
- writer(buffer, (int) bufferIndex, length);
- }
- else
- {
- if (length > bufferDataLength)
- {
- length = (int) (bufferDataLength - bufferIndex);
- }
- if (length > buffer.Length)
- {
- length = buffer.Length;
- }
- var l = Math.Min(buffer.Length, length);
- int endChunkLength = (int) (buffer.Length - bufferIndex);
- int wrappedChunkLength = l - endChunkLength;
- writer(buffer, (int) bufferIndex, endChunkLength);
- writer(buffer, 0, wrappedChunkLength);
- }
- }
- }
- 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 void Push(T data)
- {
- lock (buffer)
- {
- buffer[bufferIndex++] = data;
- if (bufferIndex >= buffer.Length)
- {
- bufferIndex = 0;
- }
- bufferDataLength++;
- }
- }
- 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;
- }
- }
- }
|