diff --git a/csharp/src/Apache.Arrow/C/Import.cs b/csharp/src/Apache.Arrow/C/CArrowSchema.cs
similarity index 80%
rename from csharp/src/Apache.Arrow/C/Import.cs
rename to csharp/src/Apache.Arrow/C/CArrowSchema.cs
index 013706428a5e9..56836b8c10459 100644
--- a/csharp/src/Apache.Arrow/C/Import.cs
+++ b/csharp/src/Apache.Arrow/C/CArrowSchema.cs
@@ -23,14 +23,21 @@
using Apache.Arrow.Types;
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
-public delegate void ReleaseFFIArrowSchema(IntPtr schema);
+public delegate void ReleaseCArrowSchema(IntPtr schema);
namespace Apache.Arrow.C
{
-
-
+ ///
+ /// An Arrow C Data Interface Schema, which represents a type, field, or schema.
+ ///
+ ///
+ ///
+ /// This is used to export , , or
+ /// to other languages. It matches the layout of the
+ /// ArrowSchema struct described in https://github.com/apache/arrow/blob/main/cpp/src/arrow/c/abi.h.
+ ///
+ /// Initialize the exported C schema as an Arrow type.
+ ///
+ /// The Arrow type to export.
+ /// An uninitialized CArrowSchema.
+ public static void ExportDataType(IArrowType datatype, out CArrowSchema schema)
{
schema.format = GetFormat(datatype);
schema.name = null;
@@ -130,26 +142,32 @@ public static void ExportDataType(IArrowType datatype, out FFIArrowSchema schema
schema.release = (IntPtr self) =>
{
- var schema = Marshal.PtrToStructure(self);
+ var schema = Marshal.PtrToStructure(self);
if (schema.n_children > 0)
{
for (int i = 0; i < schema.n_children; i++)
{
FreePtr(schema.children[i]);
}
+ Marshal.FreeHGlobal((IntPtr)schema.children);
}
if (schema.dictionary != IntPtr.Zero)
{
FreePtr(schema.dictionary);
}
- Marshal.DestroyStructure(self);
+ Marshal.DestroyStructure(self);
};
schema.private_data = IntPtr.Zero;
}
- public static void ExportField(Field field, out FFIArrowSchema schema)
+ ///
+ /// Initialize the exported C schema as a field.
+ ///
+ /// Field to export.
+ /// An uninitialized CArrowSchema.
+ public static void ExportField(Field field, out CArrowSchema schema)
{
ExportDataType(field.DataType, out schema);
schema.name = field.Name;
@@ -158,23 +176,41 @@ public static void ExportField(Field field, out FFIArrowSchema schema)
schema.flags = GetFlags(field.DataType, field.IsNullable);
}
- public static void ExportSchema(Schema schema, out FFIArrowSchema out_schema)
+ ///
+ /// Initialize the exported C schema as a schema.
+ ///
+ /// Schema to export.
+ /// An uninitialized CArrowSchema
+ public static void ExportSchema(Schema schema, out CArrowSchema out_schema)
{
// TODO: top-level metadata
var struct_type = new StructType(schema.Fields.Values.ToList());
ExportDataType(struct_type, out out_schema);
}
+ ///
+ /// Allocate an unmanaged pointer and copy this instances data to it.
+ ///
+ ///
+ /// To avoid a memory leak, you must call on this
+ /// pointer when done using it.
+ ///
public IntPtr AllocateAsPtr()
{
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(this));
- Marshal.StructureToPtr(this, ptr, false);
+ Marshal.StructureToPtr(this, ptr, false);
return ptr;
}
+ ///
+ /// Free a pointer that was allocated in .
+ ///
+ ///
+ /// Do not call this on a pointer that was allocated elsewhere.
+ ///
public static void FreePtr(IntPtr ptr)
{
- var schema = Marshal.PtrToStructure(ptr);
+ var schema = Marshal.PtrToStructure(ptr);
if (schema.release != null)
{
// Call release if not already called.
@@ -190,7 +226,7 @@ public static void FreePtr(IntPtr ptr)
///
public IntPtr Export(IntPtr ptr)
{
- Marshal.StructureToPtr(this, ptr, false);
+ Marshal.StructureToPtr(this, ptr, false);
return ptr;
}
@@ -302,15 +338,32 @@ public void Visit(IArrowType type)
}
}
- public class ImportedArrowSchema : IDisposable
+ ///
+ /// A imported from somewhere else.
+ ///
+ ///
+ ///
+ /// Typically, when importing a schema we will allocate an uninitialized
+ /// , pass the pointer to the foreign function,
+ /// then construct this class with the initialized pointer.
+ ///
+ ///
+ /// var c_schema = new CArrowSchema();
+ /// IntPtr imported_ptr = c_schema.AllocateAsPtr();
+ /// foreign_export_function(imported_ptr);
+ /// var imported_type = new ImportedArrowSchema(imported_ptr);
+ /// ArrowType arrow_type = imported_type.GetAsType();
+ ///
+ ///
+ public sealed class ImportedArrowSchema : IDisposable
{
- private FFIArrowSchema _data;
+ private CArrowSchema _data;
private IntPtr _handle;
private bool _is_root;
public ImportedArrowSchema(IntPtr handle)
{
- _data = Marshal.PtrToStructure(handle);
+ _data = Marshal.PtrToStructure(handle);
if (_data.release == null)
{
throw new Exception("Tried to import a schema that has already been released.");
@@ -353,7 +406,7 @@ public ArrowType GetAsType()
var dictionary_schema = new ImportedArrowSchema(_data.dictionary, /*is_root*/ false);
var dictionary_type = dictionary_schema.GetAsType();
- bool ordered = (_data.flags & FFIArrowSchema.ARROW_FLAG_NULLABLE) == FFIArrowSchema.ARROW_FLAG_NULLABLE;
+ bool ordered = (_data.flags & CArrowSchema.ARROW_FLAG_NULLABLE) == CArrowSchema.ARROW_FLAG_NULLABLE;
return new DictionaryType(indices_type, dictionary_type, ordered);
}
@@ -444,7 +497,7 @@ public Field GetAsField()
{
string field_name = string.IsNullOrEmpty(_data.name) ? "" : _data.name;
- bool nullable = (_data.flags & FFIArrowSchema.ARROW_FLAG_NULLABLE) == FFIArrowSchema.ARROW_FLAG_NULLABLE;
+ bool nullable = (_data.flags & CArrowSchema.ARROW_FLAG_NULLABLE) == CArrowSchema.ARROW_FLAG_NULLABLE;
return new Field(field_name, GetAsType(), nullable);
}
diff --git a/csharp/test/Apache.Arrow.Tests/CDataInterfaceSchemaTests.cs b/csharp/test/Apache.Arrow.Tests/CDataInterfaceSchemaTests.cs
index a0100fd0bf1e6..5f120ee86ec93 100644
--- a/csharp/test/Apache.Arrow.Tests/CDataInterfaceSchemaTests.cs
+++ b/csharp/test/Apache.Arrow.Tests/CDataInterfaceSchemaTests.cs
@@ -134,8 +134,8 @@ public void ImportType()
foreach ((Field field, dynamic py_field) in schema.Fields.Values.AsEnumerable()
.Zip(py_fields))
{
- var ffi_schema = new FFIArrowSchema();
- IntPtr imported_ptr = ffi_schema.AllocateAsPtr();
+ var c_schema = new CArrowSchema();
+ IntPtr imported_ptr = c_schema.AllocateAsPtr();
using (Py.GIL())
{
@@ -148,7 +148,7 @@ public void ImportType()
dataTypeComparer.Visit(imported_type.GetAsType());
// Since we allocated, we are responsible for freeing the pointer.
- FFIArrowSchema.FreePtr(imported_ptr);
+ CArrowSchema.FreePtr(imported_ptr);
}
}
@@ -162,8 +162,8 @@ public void ImportField()
foreach ((Field field, dynamic py_field) in schema.Fields.Values.AsEnumerable()
.Zip(py_fields))
{
- var ffi_schema = new FFIArrowSchema();
- IntPtr imported_ptr = ffi_schema.AllocateAsPtr();
+ var c_schema = new CArrowSchema();
+ IntPtr imported_ptr = c_schema.AllocateAsPtr();
using (Py.GIL())
{
@@ -174,7 +174,7 @@ public void ImportField()
FieldComparer.Compare(field, imported_field.GetAsField());
// Since we allocated, we are responsible for freeing the pointer.
- FFIArrowSchema.FreePtr(imported_ptr);
+ CArrowSchema.FreePtr(imported_ptr);
}
}
@@ -185,8 +185,8 @@ public void ImportSchema()
Schema schema = GetTestSchema();
dynamic py_schema = GetPythonSchema();
- var ffi_schema = new FFIArrowSchema();
- IntPtr imported_ptr = ffi_schema.AllocateAsPtr();
+ var c_schema = new CArrowSchema();
+ IntPtr imported_ptr = c_schema.AllocateAsPtr();
using (Py.GIL())
{
@@ -197,7 +197,7 @@ public void ImportSchema()
SchemaComparer.Compare(schema, imported_field.GetAsSchema());
// Since we allocated, we are responsible for freeing the pointer.
- FFIArrowSchema.FreePtr(imported_ptr);
+ CArrowSchema.FreePtr(imported_ptr);
}
@@ -213,8 +213,8 @@ public void ExportType()
.Zip(py_fields))
{
IArrowType datatype = field.DataType;
- var exported_type = new FFIArrowSchema();
- FFIArrowSchema.ExportDataType(datatype, out exported_type);
+ var exported_type = new CArrowSchema();
+ CArrowSchema.ExportDataType(datatype, out exported_type);
// For Python, we need to provide the pointer
IntPtr exported_ptr = exported_type.AllocateAsPtr();
@@ -228,15 +228,15 @@ public void ExportType()
}
// Python should have called release once `exported_py_type` went out-of-scope.
- var ffi_schema = Marshal.PtrToStructure(exported_ptr);
- Assert.Null(ffi_schema.release);
- Assert.Null(ffi_schema.format);
- Assert.Equal(0, ffi_schema.flags);
- Assert.Equal(0, ffi_schema.n_children);
- Assert.Equal(IntPtr.Zero, ffi_schema.dictionary);
+ var c_schema = Marshal.PtrToStructure(exported_ptr);
+ Assert.Null(c_schema.release);
+ Assert.Null(c_schema.format);
+ Assert.Equal(0, c_schema.flags);
+ Assert.Equal(0, c_schema.n_children);
+ Assert.Equal(IntPtr.Zero, c_schema.dictionary);
// Since we allocated, we are responsible for freeing the pointer.
- FFIArrowSchema.FreePtr(exported_ptr);
+ CArrowSchema.FreePtr(exported_ptr);
}
}
@@ -250,8 +250,8 @@ public void ExportField()
foreach ((Field field, dynamic py_field) in schema.Fields.Values.AsEnumerable()
.Zip(py_fields))
{
- var exported_field = new FFIArrowSchema();
- FFIArrowSchema.ExportField(field, out exported_field);
+ var exported_field = new CArrowSchema();
+ CArrowSchema.ExportField(field, out exported_field);
// For Python, we need to provide the pointer
var exported_ptr = exported_field.AllocateAsPtr();
@@ -264,13 +264,13 @@ public void ExportField()
}
// Python should have called release once `exported_py_type` went out-of-scope.
- var ffi_schema = Marshal.PtrToStructure(exported_ptr);
+ var ffi_schema = Marshal.PtrToStructure(exported_ptr);
Assert.Null(ffi_schema.name);
Assert.Null(ffi_schema.release);
Assert.Null(ffi_schema.format);
// Since we allocated, we are responsible for freeing the pointer.
- FFIArrowSchema.FreePtr(exported_ptr);
+ CArrowSchema.FreePtr(exported_ptr);
}
}
@@ -281,8 +281,8 @@ public void ExportSchema()
Schema schema = GetTestSchema();
dynamic py_schema = GetPythonSchema();
- var exported_schema = new FFIArrowSchema();
- FFIArrowSchema.ExportSchema(schema, out exported_schema);
+ var exported_schema = new CArrowSchema();
+ CArrowSchema.ExportSchema(schema, out exported_schema);
// For Python, we need to provide the pointer
var exported_ptr = exported_schema.AllocateAsPtr();
@@ -295,7 +295,7 @@ public void ExportSchema()
}
// Since we allocated, we are responsible for freeing the pointer.
- FFIArrowSchema.FreePtr(exported_ptr);
+ CArrowSchema.FreePtr(exported_ptr);
}
}