################
C# Array Wrapper
################

The ``TS3D.Exchange.ArrayWrapper`` class, a static utility collection, simplifies interactions between C-style arrays and C# arrays. 
It provides functions to handle data conversion and copying, reducing boilerplate code and potential errors.

About Arrays in C and C#
========================

Arrays are fundamental to both C and C#, but their nature differs significantly.

In C, arrays are low-level and managed via pointers.  
The syntax is straightforward but requires manual memory management.
In C#, arrays are high-level and managed by the runtime, providing safety and bounds checking:  

.. tabs::

   .. tab:: C#

      .. code-block:: cs

         int[] numbers = new int[5];
         for (int i = 0; i < numbers.Length; i++) numbers[i] = i * 2;  
         
   .. tab:: C

      .. code-block:: c

         int numbers[5];
         for (int i = 0; i < 5; i++) numbers[i] = i * 2;  

The HOOPS Exchange API extensively uses C-style arrays, which require manual memory management and direct manipulation.  
A bridge between these low-level C arrays and high-level C# arrays is essential, as managed arrays in C# offer better safety, simplicity, and performance.

The ``System.Runtime.InteropServices.Marshal`` class from the .NET API plays a crucial role in interacting with unmanaged code by facilitating conversions between managed and unmanaged data structures.  
This class provides the necessary tools for allocating memory, copying data, and ensuring proper memory management when working with interop scenarios.

For detailed information, refer to `the official .NET documentation <https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal?view=net-8.0>`__.

Reading and Writing Fundamental Types
=====================================

Most arrays managed by HOOPS Exchange are sequences of integral types as described in :ref:`api_typedefs`.
The following functions allow you to convert them from and to arrays in C# :ref:`cs_bindings_types`.

.. csv-table::

   Convert to C#, Convert from C#, Underlying Value Type, Array Type (C#)
   ``ReadIntArray``,``WriteIntArray``,``A3DInt32``,``int[]``
   ``ReadUIntArray``,``WriteUIntArray``,``A3DUns32``,``uint[]``
   ``ReadDoubleArray``,``WriteDoubleArray``,``A3DDouble``,``double[]``
   ``ReadBoolArray``,``WriteBoolArray``,``A3DBool``,``bool[]``
   ``ReadByteArray``,``WriteByteArray``,``A3DUns8``,``byte[]``
   ``ReadPtrArray``,``WritePtrArray``,``A3DVoid*``,``IntPtr[]``
   ``ReadStructArray<T>``,``WriteStructArray<T>``,``T``,``T[]``
   ``ReadStringArray``,``WriteStringArray``,``A3DUTF8Char*``,``string[]``

A lot of arrays in HOOPS Exchange are arrays of pointers.
For example, arrays of entities, like :cpp:member:`A3DAsmModelFileData::m_ppPOccurrences`.
They are interpreted as ``IntPtr`` and thus can be accessed using ``ReadPtrArray`` and ``WritePtrArray``.

.. rubric:: Reading Into a C# Array

All read functions follow a similar signature:

.. object:: static int[] ReadIntArray(IntPtr unmanagedArray, uint length)

Refer to the table above to adapt the function name and return type according to the array you are reading from.

For example, to get a list of child product occurrences and read them into an array of ``IntPtr``, use:

.. code-block:: cs

   A3DAsmProductOccurrenceData product_occurrence_data;
   API.Initialize(out product_occurrence_data);
   
   API.A3DAsmModelFileGet(product_occurrence_handle, ref product_occurrence_data);

   IntPtr[] children_handles = ArrayWrapper.ReadIntPtrArray(
       product_occurrence_data.m_ppPOccurrences,
       product_occurrence_data.m_uiPOccurrencesSize
   );

.. rubric:: Writing Into Unmanaged Memory

All write functions follow a similar signature:

.. object:: static IntPtr WriteIntArray(int[] array)

Refer to the table above to adapt the function name and parameter type according to the array you are reading from.

The inverse of the previous example can be written this way:

.. code-block:: cs

   A3DAsmProductOccurrenceData product_occurrence_data;
   API.Initialize(out product_occurrence_data);

   IntPtr[] children_handles = ... ;

   product_occurrence_data.m_ppPOccurrences     = ArrayWrapper.WritePtrArray(children_handles);
   product_occurrence_data.m_uiPOccurrencesSize = children_handles.Length;

   API.A3DAsmModelFileCreate(ref product_occurrence_data, out product_occurrence_handle);

Converting Arrays of Structures
-------------------------------

It is possible to handle arrays of data structures using ``ReadStructArray<T>`` and ``WriteStructArray<T>``.

These generic functions work similarly to the other functions, but they accept any structure binding (Struct.cs) and expect an underlying type ``T`` matching the structure's name.
For example, an ``A3DVector3DData[]`` can be converted to and from an array of :cpp:struct:`A3DVector3DData` using ``ReadStructArray<A3DVector3DData>`` and ``WriteStructArray<A3DVector3DData>``.

Converting Arrays of String
===========================

In HOOPS Exchange, strings are encoded as null-terminated UTF-8 sequences of bytes (``A3DUTF8Char``).
To convert to and from arrays of strings, use ``ReadStringArray`` and ``WriteStringArray``.

Arrays and Wrapper Classes
==========================

If you are using :doc:`classes` instead of the :doc:`bindings`, this conversion is abstracted away using :ref:`cs_wrapper_properties`.

