//
// Copyright (c) APX Labs, Inc. All rights reserved.
//
//
// 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.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Java.Interop;
namespace ApxLabs.FastAndroidCamera
{
///
/// A wrapper around a Java array that reads elements directly from the pointer instead of through expensive JNI calls.
///
public sealed class FastJavaByteArray : IList, IDisposable
{
private JniObjectReference _javaRef;
#region Constructors
///
/// Creates a new FastJavaByteArray with the given number of bytes reserved.
///
/// Number of bytes to reserve
public FastJavaByteArray(int length)
{
if (length <= 0)
throw new ArgumentOutOfRangeException();
JniObjectReference localRef = JniEnvironment.Arrays.NewByteArray(length);
if (!localRef.IsValid)
throw new OutOfMemoryException();
// Retain a global reference to the byte array.
_javaRef = localRef.NewGlobalRef();
Count = length;
bool isCopy = false;
unsafe
{
// Get the pointer to the byte array using the global Handle
Raw = (byte*)JniEnvironment.Arrays.GetByteArrayElements(_javaRef, &isCopy);
}
}
///
/// Creates a FastJavaByteArray wrapper around an existing Java/JNI byte array
///
/// Native Java array handle
/// Whether to consider this byte array read-only
public FastJavaByteArray(IntPtr handle, bool readOnly = true)
{
if (handle == IntPtr.Zero)
throw new ArgumentNullException("handle");
IsReadOnly = readOnly;
// Retain a global reference to the byte array.
_javaRef = new JniObjectReference(handle).NewGlobalRef();
Count = JniEnvironment.Arrays.GetArrayLength(_javaRef);
bool isCopy = false;
unsafe
{
// Get a pinned pointer to the byte array using the global Handle
Raw = (byte*)JniEnvironment.Arrays.GetByteArrayElements(_javaRef, &isCopy);
}
}
#endregion
#region Dispose Pattern
///
/// Releases unmanaged resources and performs other cleanup operations before the
/// is reclaimed by garbage collection.
///
~FastJavaByteArray()
{
Dispose(false);
}
///
/// Releases all resource used by the object.
///
/// Call when you are finished using the
/// . The method leaves the
/// in an unusable state. After calling
/// , you must release all references to the
/// so the garbage collector can reclaim the memory that
/// the was occupying.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!_javaRef.IsValid)
return;
unsafe
{
// tell Java that we're done with this array
JniEnvironment.Arrays.ReleaseByteArrayElements(_javaRef, (sbyte*)Raw, JniReleaseArrayElementsMode.Default);
}
if (disposing)
{
JniObjectReference.Dispose(ref _javaRef);
}
}
#endregion
#region IList Properties
///
/// Count of bytes
///
public int Count { get; private set; }
///
/// Gets a value indicating whether this byte array is read only.
///
/// true if read only; otherwise, false.
public bool IsReadOnly
{
get;
private set;
}
///
/// Indexer
///
/// Index of byte
/// Byte at the given index
public byte this[int index]
{
get
{
if (index < 0 || index >= Count)
{
throw new ArgumentOutOfRangeException();
}
byte retval;
unsafe
{
retval = Raw[index];
}
return retval;
}
set
{
if (IsReadOnly)
{
throw new NotSupportedException("This FastJavaByteArray is read-only");
}
if (index < 0 || index >= Count)
{
throw new ArgumentOutOfRangeException();
}
unsafe
{
Raw[index] = value;
}
}
}
#endregion
#region IList Methods
///
/// Adds a single byte to the list. Not supported
///
/// byte to add
public void Add(byte item)
{
throw new NotSupportedException("FastJavaByteArray is fixed length");
}
///
/// Not supported
///
public void Clear()
{
throw new NotSupportedException("FastJavaByteArray is fixed length");
}
///
/// Returns true if the item is found int he array
///
/// Item to find
/// True if the item is found
public bool Contains(byte item)
{
return IndexOf(item) >= 0;
}
///
/// Copies the contents of the FastJavaByteArray into a byte array
///
/// The array to copy to.
/// The zero-based index into the destination array where CopyTo should start.
public void CopyTo(byte[] array, int arrayIndex)
{
unsafe
{
Marshal.Copy(new IntPtr(Raw), array, arrayIndex, Math.Min(Count, array.Length - arrayIndex));
}
}
///
/// Retreives enumerator
///
/// Enumerator
[DebuggerHidden]
public IEnumerator GetEnumerator()
{
return new FastJavaByteArrayEnumerator(this);
}
///
/// Retreives enumerator
///
/// Enumerator
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return new FastJavaByteArrayEnumerator(this);
}
///
/// Gets the first index of the given value
///
/// Item to search for
/// Index of found item
public int IndexOf(byte item)
{
for (int i = 0; i < Count; ++i)
{
byte current;
unsafe
{
current = Raw[i];
}
if (current == item)
return i;
}
return -1;
}
///
/// Not supported
///
///
///
public void Insert(int index, byte item)
{
throw new NotSupportedException("FastJavaByteArray is fixed length");
}
///
/// Not supported
///
///
///
public bool Remove(byte item)
{
throw new NotSupportedException("FastJavaByteArray is fixed length");
}
///
/// Not supported
///
///
public void RemoveAt(int index)
{
throw new NotSupportedException("FastJavaByteArray is fixed length");
}
#endregion
#region Public Properties
///
/// Gets the raw pointer to the underlying data.
///
public unsafe byte* Raw { get; private set; }
///
/// Gets the handle of the Java reference to the array.
///
/// The handle.
public IntPtr Handle
{
get { return _javaRef.Handle; }
}
#endregion
}
}