146 lines
4.3 KiB
C#
146 lines
4.3 KiB
C#
#define STATS
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
|
|
namespace Photon.Voice
|
|
{
|
|
// Encapsulates byte array slice, FrameFlags and dispose Action
|
|
public struct FrameBuffer
|
|
{
|
|
readonly byte[] array;
|
|
readonly int offset;
|
|
readonly int count;
|
|
readonly IDisposable disposer;
|
|
bool disposed;
|
|
int refCnt; // See Retain()
|
|
|
|
GCHandle gcHandle;
|
|
IntPtr ptr;
|
|
bool pinned;
|
|
#if STATS
|
|
static internal int statDisposerCreated;
|
|
static internal int statDisposerDisposed;
|
|
static internal int statPinned;
|
|
static internal int statUnpinned;
|
|
#else
|
|
static internal int statDisposerCreated = Int32.MaxValue;
|
|
static internal int statDisposerDisposed = Int32.MaxValue;
|
|
static internal int statPinned = Int32.MaxValue;
|
|
static internal int statUnpinned = Int32.MaxValue;
|
|
#endif
|
|
public FrameBuffer(byte[] array, int offset, int count, FrameFlags flags, IDisposable disposer)
|
|
{
|
|
this.array = array;
|
|
this.offset = offset;
|
|
this.count = count;
|
|
this.Flags = flags;
|
|
this.disposer = disposer;
|
|
this.disposed = false;
|
|
this.refCnt = 1;
|
|
|
|
this.gcHandle = new GCHandle();
|
|
this.ptr = IntPtr.Zero;
|
|
this.pinned = false;
|
|
#if STATS
|
|
if (disposer != null)
|
|
{
|
|
Interlocked.Increment(ref statDisposerCreated);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
public FrameBuffer(byte[] array, FrameFlags flags)
|
|
{
|
|
this.array = array;
|
|
this.offset = 0;
|
|
this.count = array == null ? 0 : array.Length;
|
|
this.Flags = flags;
|
|
this.disposer = null;
|
|
this.disposed = false;
|
|
this.refCnt = 1;
|
|
|
|
this.gcHandle = new GCHandle();
|
|
this.ptr = IntPtr.Zero;
|
|
this.pinned = false;
|
|
#if STATS
|
|
if (disposer != null) // false
|
|
{
|
|
Interlocked.Increment(ref statDisposerCreated);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Pins underlying buffer and returns the pointer to it with offset.
|
|
// Unpins in Dispose().
|
|
public IntPtr Ptr
|
|
{
|
|
get
|
|
{
|
|
if (!pinned)
|
|
{
|
|
gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
|
|
ptr = IntPtr.Add(gcHandle.AddrOfPinnedObject(), offset);
|
|
pinned = true;
|
|
#if STATS
|
|
Interlocked.Increment(ref statPinned);
|
|
#endif
|
|
}
|
|
return ptr;
|
|
}
|
|
}
|
|
|
|
// Use Retain() to prevent the owner from disposing the buffer when it calls Release(). Since FrameBuffer is a struct, ref counter
|
|
// is shared only between parameters in stack passed as ref, so ref counter is rather a flag, not a counter.
|
|
// To preserve the buffer for future use:
|
|
// void foo(ref FrameBuffer f1) {
|
|
// this.f2 = f1; // don't call on copy!
|
|
// f1.Retain()
|
|
// }
|
|
// in other thread or in subsequent call or when disposing the owner:
|
|
// this.f2.release();
|
|
public void Retain()
|
|
{
|
|
refCnt++;
|
|
}
|
|
|
|
// Call on each owned FrameBuffer after processing:
|
|
// var f1 = new FrameBuffer()
|
|
// foo(f1)
|
|
// f1.Release()
|
|
public void Release()
|
|
{
|
|
refCnt--;
|
|
if (refCnt <= 0)
|
|
{
|
|
Dispose();
|
|
}
|
|
}
|
|
|
|
private void Dispose()
|
|
{
|
|
// not allocated if was created with FrameBuffer()
|
|
if (pinned)
|
|
{
|
|
gcHandle.Free();
|
|
pinned = false;
|
|
#if STATS
|
|
Interlocked.Increment(ref statUnpinned);
|
|
#endif
|
|
}
|
|
if (disposer != null && !disposed)
|
|
{
|
|
disposer.Dispose();
|
|
disposed = true;
|
|
#if STATS
|
|
Interlocked.Increment(ref statDisposerDisposed);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public byte[] Array { get { return array; } }
|
|
public int Length { get { return count; } }
|
|
public int Offset { get { return offset; } }
|
|
public FrameFlags Flags { get; }
|
|
}
|
|
} |