// // 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 } }