diff --git a/.editorconfig b/.editorconfig
index 7243a02e..6670bfec 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -43,3 +43,8 @@ dotnet_diagnostic.S112.severity = None
# Native methods should be wrapped
# For some reason this warning shows even on auto-generated files.
dotnet_diagnostic.S4200.severity = None
+
+# S3427: Method overloads with default parameter values should not overlap
+# If we oblige to this rule and remove the overload without the default parameter,
+# the package validator will complain.
+dotnet_diagnostic.S3427.severity = none
diff --git a/sources/TileDB.CSharp/Array.cs b/sources/TileDB.CSharp/Array.cs
index 9d8315c4..0a18998d 100644
--- a/sources/TileDB.CSharp/Array.cs
+++ b/sources/TileDB.CSharp/Array.cs
@@ -648,6 +648,54 @@ public static EncryptionType EncryptionType(Context ctx, string uri)
return (EncryptionType)tiledb_encryption_type;
}
+ ///
+ /// Gets an from the by name.
+ ///
+ /// The enumeration's name.
+ ///
+ public Enumeration GetEnumeration(string name)
+ {
+ var handle = new EnumerationHandle();
+ var successful = false;
+ tiledb_enumeration_t* enumeration_p = null;
+ try
+ {
+ using (var ctxHandle = _ctx.Handle.Acquire())
+ using (var arrayHandle = _handle.Acquire())
+ using (var ms_name = new MarshaledString(name))
+ {
+ _ctx.handle_error(Methods.tiledb_array_get_enumeration(ctxHandle, arrayHandle, ms_name, &enumeration_p));
+ }
+ successful = true;
+ }
+ finally
+ {
+ if (successful)
+ {
+ handle.InitHandle(enumeration_p);
+ }
+ else
+ {
+ handle.SetHandleAsInvalid();
+ }
+ }
+
+ return new Enumeration(_ctx, handle);
+ }
+
+ ///
+ /// Loads all enumerations of the .
+ ///
+ ///
+ public void LoadAllEnumerations()
+ {
+ using (var ctxHandle = _ctx.Handle.Acquire())
+ using (var arrayHandle = _handle.Acquire())
+ {
+ _ctx.handle_error(Methods.tiledb_array_load_all_enumerations(ctxHandle, arrayHandle));
+ }
+ }
+
///
/// Puts a multi-value metadata to the .
///
diff --git a/sources/TileDB.CSharp/ArraySchema.cs b/sources/TileDB.CSharp/ArraySchema.cs
index 376d9a60..9d33c061 100644
--- a/sources/TileDB.CSharp/ArraySchema.cs
+++ b/sources/TileDB.CSharp/ArraySchema.cs
@@ -69,6 +69,18 @@ public void AddAttributes(params Attribute[] attrs)
}
}
+ ///
+ /// Adds an in the .
+ ///
+ /// The enumeration to add.
+ public void AddEnumeration(Enumeration enumeration)
+ {
+ using var ctxHandle = _ctx.Handle.Acquire();
+ using var handle = _handle.Acquire();
+ using var enumHandle = enumeration.Handle.Acquire();
+ _ctx.handle_error(Methods.tiledb_array_schema_add_enumeration(ctxHandle, handle, enumHandle));
+ }
+
///
/// Sets whether cells with duplicate coordinates are allowed in the .
///
diff --git a/sources/TileDB.CSharp/ArraySchemaEvolution.cs b/sources/TileDB.CSharp/ArraySchemaEvolution.cs
index 76040185..f66b1375 100644
--- a/sources/TileDB.CSharp/ArraySchemaEvolution.cs
+++ b/sources/TileDB.CSharp/ArraySchemaEvolution.cs
@@ -63,6 +63,32 @@ public void DropAttribute(string attrName)
_ctx.handle_error(Methods.tiledb_array_schema_evolution_drop_attribute(ctxHandle, handle, msAttrName));
}
+ ///
+ /// Adds an to the .
+ ///
+ /// A fully constructed that will be added to the schema.
+ ///
+ public void AddEnumeration(Enumeration enumeration)
+ {
+ using var ctxHandle = _ctx.Handle.Acquire();
+ using var handle = _handle.Acquire();
+ using var enumHandle = enumeration.Handle.Acquire();
+ _ctx.handle_error(Methods.tiledb_array_schema_evolution_add_enumeration(ctxHandle, handle, enumHandle));
+ }
+
+ ///
+ /// Drops an enumeration with the given name from the .
+ ///
+ /// The name of the attribute that will be dropped from the schema.
+ ///
+ public void DropEnumeration(string enumerationName)
+ {
+ using var ctxHandle = _ctx.Handle.Acquire();
+ using var handle = _handle.Acquire();
+ using var msEnumerationName = new MarshaledString(enumerationName);
+ _ctx.handle_error(Methods.tiledb_array_schema_evolution_drop_enumeration(ctxHandle, handle, msEnumerationName));
+ }
+
///
/// Drops an from the .
///
diff --git a/sources/TileDB.CSharp/Attribute.cs b/sources/TileDB.CSharp/Attribute.cs
index a1bd479c..0f4888d0 100644
--- a/sources/TileDB.CSharp/Attribute.cs
+++ b/sources/TileDB.CSharp/Attribute.cs
@@ -3,6 +3,7 @@
using System.Runtime.InteropServices;
using System.Text;
using TileDB.Interop;
+using TileDB.CSharp.Marshalling;
using TileDB.CSharp.Marshalling.SafeHandles;
using AttributeHandle = TileDB.CSharp.Marshalling.SafeHandles.AttributeHandle;
using FilterListHandle = TileDB.CSharp.Marshalling.SafeHandles.FilterListHandle;
@@ -89,6 +90,17 @@ public void SetCellValNum(uint cellValNum)
using var handle = _handle.Acquire();
_ctx.handle_error(Methods.tiledb_attribute_set_cell_val_num(ctxHandle, handle, cellValNum));
}
+
+ ///
+ /// Sets the name of the 's enumeration.
+ ///
+ public void SetEnumerationName(string enumerationName)
+ {
+ using var ctxHandle = _ctx.Handle.Acquire();
+ using var handle = _handle.Acquire();
+ using var ms_enumerationName = new MarshaledString(enumerationName);
+ _ctx.handle_error(Methods.tiledb_attribute_set_enumeration_name(ctxHandle, handle, ms_enumerationName));
+ }
///
/// Get name of the attribute.
@@ -187,6 +199,23 @@ public ulong CellSize()
return cell_size;
}
+ ///
+ /// Gets the name of the 's enumeration.
+ ///
+ ///
+ /// A string with the name of the attribute's enumeration, or an
+ /// empty string if the attribute does not have an enumeration.
+ ///
+ public string EnumerationName()
+ {
+ using var ctxHandle = _ctx.Handle.Acquire();
+ using var handle = _handle.Acquire();
+ using var nameHolder = new StringHandleHolder();
+ _ctx.handle_error(Methods.tiledb_attribute_get_enumeration_name(ctxHandle, handle, &nameHolder._handle));
+
+ return nameHolder.ToString();
+ }
+
///
/// Sets the fill value of a nullable multi-valued .
/// Used when the fill value is multi-valued.
diff --git a/sources/TileDB.CSharp/Enumeration.cs b/sources/TileDB.CSharp/Enumeration.cs
new file mode 100644
index 00000000..aa90fe38
--- /dev/null
+++ b/sources/TileDB.CSharp/Enumeration.cs
@@ -0,0 +1,326 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using TileDB.CSharp.Marshalling;
+using TileDB.CSharp.Marshalling.SafeHandles;
+using TileDB.Interop;
+
+namespace TileDB.CSharp
+{
+ ///
+ /// Represents a TileDB enumeration object.
+ ///
+ public sealed unsafe class Enumeration : IDisposable
+ {
+ ///
+ /// This value indicates an enumeration with variable-sized members.
+ /// It may be returned from .
+ ///
+ ///
+ public const uint VariableSized = Constants.VariableSizedImpl;
+
+ private readonly Context _ctx;
+
+ private readonly EnumerationHandle _handle;
+
+ internal Enumeration(Context ctx, EnumerationHandle handle)
+ {
+ _ctx = ctx;
+ _handle = handle;
+ }
+
+ internal EnumerationHandle Handle => _handle;
+
+ ///
+ /// Creates an with fixed-sized members.
+ ///
+ /// The type of the enumeration's members.
+ /// The to use.
+ /// The name of the enumeration.
+ /// The value of .
+ /// A of the enumeration's values.
+ /// The number of values per member in the enumeration. Optional,
+ /// defaults to 1. If specified, its value must divide the length of .
+ /// The data type of the enumeration. Defaults to the
+ /// default data type of .
+ /// has a different
+ /// size from .
+ public static Enumeration Create(Context ctx, string name, bool ordered, ReadOnlySpan values, uint cellValNum = 1, DataType? dataType = null) where T : struct
+ {
+ DataType dataTypeActual = dataType ??= EnumUtil.TypeToDataType(typeof(T));
+ ErrorHandling.CheckDataType(dataTypeActual);
+
+ fixed (T* valuesPtr = &MemoryMarshal.GetReference(values))
+ {
+ EnumerationHandle handle = EnumerationHandle.Create(ctx, name, (tiledb_datatype_t)dataTypeActual, cellValNum, ordered, (byte*)valuesPtr, (ulong)values.Length * (ulong)sizeof(T), null, 0);
+ return new Enumeration(ctx, handle);
+ }
+ }
+
+ ///
+ /// Creates an with variable-sized members.
+ ///
+ /// The type of the enumeration's members.
+ /// The to use.
+ /// The name of the enumeration.
+ /// The value of .
+ /// A of all values of each member of the enumeration, concatenated.
+ /// A of the offsets to the first byte of each member of the enumeration.
+ /// The data type of the enumeration. Defaults to the
+ /// default data type of .
+ /// has a different
+ /// size from .
+ public static Enumeration Create(Context ctx, string name, bool ordered, ReadOnlySpan values, ReadOnlySpan offsets, DataType? dataType = null) where T : struct
+ {
+ DataType dataTypeActual = dataType ??= EnumUtil.TypeToDataType(typeof(T));
+ ErrorHandling.CheckDataType(dataTypeActual);
+
+ EnumerationHandle handle;
+ fixed (T* valuesPtr = &MemoryMarshal.GetReference(values))
+ fixed (ulong* offsetsPtr = &MemoryMarshal.GetReference(offsets))
+ {
+ handle = EnumerationHandle.Create(ctx, name, (tiledb_datatype_t)dataTypeActual, VariableSized, ordered, (byte*)valuesPtr, (ulong)values.Length * (ulong)sizeof(T), offsetsPtr, (ulong)offsets.Length * sizeof(ulong));
+ }
+ return new Enumeration(ctx, handle);
+ }
+
+ ///
+ /// Creates an from a collection of strings.
+ ///
+ /// The to use.
+ /// The name of the enumeration.
+ /// Whether the enumeration should be considered as ordered.
+ /// If false, prevents inequality operators in s from
+ /// being used with this enumeration.
+ /// The collection of values for the enumeration.
+ /// The data type of the enumeration. Must be a string type.
+ /// Optional, defaults to .
+ public static Enumeration Create(Context ctx, string name, bool ordered, IReadOnlyCollection values, DataType dataType = DataType.StringUtf8)
+ {
+ using MarshaledContiguousStringCollection marshaledStrings = new(values, dataType);
+ var handle = EnumerationHandle.Create(ctx, name, (tiledb_datatype_t)dataType, VariableSized,
+ ordered, marshaledStrings.Data, marshaledStrings.DataCount, marshaledStrings.Offsets, marshaledStrings.OffsetsCount);
+ return new Enumeration(ctx, handle);
+ }
+
+ ///
+ /// Disposes the .
+ ///
+ public void Dispose()
+ {
+ _handle.Dispose();
+ }
+
+ ///
+ /// Returns the number of values for each member of the .
+ ///
+ ///
+ /// If this property has a value of ,
+ /// each value has a different size.
+ /// This method exposes the tiledb_enumeration_get_cell_val_num function of the TileDB Embedded C API.
+ ///
+ public uint ValuesPerMember
+ {
+ get
+ {
+ uint result;
+ using var ctxHandle = _ctx.Handle.Acquire();
+ using var handle = _handle.Acquire();
+ _ctx.handle_error(Methods.tiledb_enumeration_get_cell_val_num(ctxHandle, handle, &result));
+ return result;
+ }
+ }
+
+ ///
+ /// The of the .
+ ///
+ public DataType DataType
+ {
+ get
+ {
+ tiledb_datatype_t result;
+ using var ctxHandle = _ctx.Handle.Acquire();
+ using var handle = _handle.Acquire();
+ _ctx.handle_error(Methods.tiledb_enumeration_get_type(ctxHandle, handle, &result));
+ return (DataType)result;
+ }
+ }
+
+ ///
+ /// Whether the should be considered as ordered.
+ ///
+ ///
+ /// If , prevents inequality operators in
+ /// s from being used with this enumeration.
+ ///
+ public bool IsOrdered
+ {
+ get
+ {
+ int result;
+ using var ctxHandle = _ctx.Handle.Acquire();
+ using var handle = _handle.Acquire();
+ _ctx.handle_error(Methods.tiledb_enumeration_get_ordered(ctxHandle, handle, &result));
+ return result != 0;
+ }
+ }
+
+ ///
+ /// Returns the name of the .
+ ///
+ public string GetName()
+ {
+ using StringHandleHolder nameHolder = new();
+ using var ctxHandle = _ctx.Handle.Acquire();
+ using var handle = _handle.Acquire();
+ _ctx.handle_error(Methods.tiledb_enumeration_get_name(ctxHandle, handle, &nameHolder._handle));
+ return nameHolder.ToString();
+ }
+
+ ///
+ /// Returns the values of the .
+ ///
+ ///
+ /// An array whose type depends on the enumeration's
+ /// and :
+ ///
+ /// - If is a string datatype, the
+ /// method will return an array of .
+ /// - If the enumeration's values have a fixed size (i.e.
+ /// is not equal to ), the method will return an array of
+ /// values of the underlying data type.
+ /// - If the enumeration's values have a variable size (i.e.
+ /// is equal to ), the method will return an array of arrays of
+ /// values of the underlying data type.
+ ///
+ ///
+ public System.Array GetValues()
+ {
+ // Keep the handles acquired for the entire method.
+ // This prevents another thread freeing the data and offsets buffers.
+ using var ctxHandle = _ctx.Handle.Acquire();
+ using var handle = _handle.Acquire();
+
+ void* dataPtr;
+ ulong* offsetsPtr;
+ ulong dataSize, offsetsSize;
+ _ctx.handle_error(Methods.tiledb_enumeration_get_data(ctxHandle, handle, &dataPtr, &dataSize));
+ _ctx.handle_error(Methods.tiledb_enumeration_get_offsets(ctxHandle, handle, (void**)&offsetsPtr, &offsetsSize));
+
+ DataType datatype;
+ _ctx.handle_error(Methods.tiledb_enumeration_get_type(ctxHandle, handle, (tiledb_datatype_t*)&datatype));
+
+ if (EnumUtil.IsStringType(datatype))
+ {
+ ReadOnlySpan offsets = new(offsetsPtr, checked((int)(offsetsSize / sizeof(ulong))));
+ string[] result = new string[offsets.Length];
+
+ for (int i = 0; i < offsets.Length; i++)
+ {
+ ulong nextOffset = i == offsets.Length - 1 ? dataSize : offsets[i + 1];
+ ReadOnlySpan bytes = new((byte*)dataPtr + offsets[i], checked((int)(nextOffset - offsets[i])));
+ result[i] = MarshaledStringOut.GetString(bytes, datatype);
+ }
+
+ return result;
+ }
+
+ Type type = EnumUtil.DataTypeToType(datatype);
+ if (type == typeof(sbyte))
+ {
+ return GetValues(dataPtr, dataSize, offsetsPtr, offsetsSize);
+ }
+ if (type == typeof(byte))
+ {
+ return GetValues(dataPtr, dataSize, offsetsPtr, offsetsSize);
+ }
+ if (type == typeof(short))
+ {
+ return GetValues(dataPtr, dataSize, offsetsPtr, offsetsSize);
+ }
+ if (type == typeof(ushort))
+ {
+ return GetValues(dataPtr, dataSize, offsetsPtr, offsetsSize);
+ }
+ if (type == typeof(int))
+ {
+ return GetValues(dataPtr, dataSize, offsetsPtr, offsetsSize);
+ }
+ if (type == typeof(uint))
+ {
+ return GetValues(dataPtr, dataSize, offsetsPtr, offsetsSize);
+ }
+ if (type == typeof(long))
+ {
+ return GetValues(dataPtr, dataSize, offsetsPtr, offsetsSize);
+ }
+ if (type == typeof(ulong))
+ {
+ return GetValues(dataPtr, dataSize, offsetsPtr, offsetsSize);
+ }
+ if (type == typeof(float))
+ {
+ return GetValues(dataPtr, dataSize, offsetsPtr, offsetsSize);
+ }
+ if (type == typeof(double))
+ {
+ return GetValues(dataPtr, dataSize, offsetsPtr, offsetsSize);
+ }
+ throw new NotSupportedException($"Unsupported enumeration type: {type}. Please open a bug report.");
+
+ System.Array GetValues(void* dataPtr, ulong dataSize, ulong* offsetsPtr, ulong offsetsSize)
+ {
+ uint cellValNum;
+ _ctx.handle_error(Methods.tiledb_enumeration_get_cell_val_num(ctxHandle, handle, &cellValNum));
+ if (cellValNum != VariableSized)
+ {
+ return new ReadOnlySpan(dataPtr, checked((int)(dataSize / (ulong)sizeof(T)))).ToArray();
+ }
+
+ ReadOnlySpan offsets = new(offsetsPtr, checked((int)(offsetsSize / sizeof(ulong))));
+ T[][] result = new T[offsets.Length][];
+ for (int i = 0; i < offsets.Length; i++)
+ {
+ ulong nextOffset = i == offsets.Length - 1 ? dataSize : offsets[i + 1];
+ result[i] = new ReadOnlySpan((byte*)dataPtr + offsets[i], checked((int)((nextOffset - offsets[i]) / (ulong)sizeof(T)))).ToArray();
+ }
+
+ return result;
+ }
+ }
+
+ ///
+ /// Returns a byte array with the raw data of the 's members.
+ ///
+ ///
+ ///
+ public byte[] GetRawData()
+ {
+ using var ctxHandle = _ctx.Handle.Acquire();
+ using var handle = _handle.Acquire();
+ void* data;
+ ulong dataSize;
+ _ctx.handle_error(Methods.tiledb_enumeration_get_data(ctxHandle, handle, &data, &dataSize));
+ return new ReadOnlySpan(data, checked((int)dataSize)).ToArray();
+ }
+
+ ///
+ /// Returns an array with the offsets to the first byte of each member of the .
+ ///
+ ///
+ /// In enumerations with fixed-size members (i.e. is not equal
+ /// to , this method will return an empty array, as each member has the same size.
+ ///
+ ///
+ ///
+ public ulong[] GetRawOffsets()
+ {
+ using var ctxHandle = _ctx.Handle.Acquire();
+ using var handle = _handle.Acquire();
+ void* data;
+ ulong dataSize;
+ _ctx.handle_error(Methods.tiledb_enumeration_get_offsets(ctxHandle, handle, &data, &dataSize));
+ return new ReadOnlySpan(data, checked((int)(dataSize / sizeof(ulong)))).ToArray();
+ }
+ }
+}
diff --git a/sources/TileDB.CSharp/Marshalling/MarshaledContiguousStringCollection.cs b/sources/TileDB.CSharp/Marshalling/MarshaledContiguousStringCollection.cs
new file mode 100644
index 00000000..c69f4faf
--- /dev/null
+++ b/sources/TileDB.CSharp/Marshalling/MarshaledContiguousStringCollection.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using TileDB.Interop;
+
+namespace TileDB.CSharp.Marshalling
+{
+ internal unsafe ref struct MarshaledContiguousStringCollection
+ {
+ public byte* Data { get; private set; }
+
+ public ulong* Offsets { get; private set; }
+
+ public ulong DataCount { get; private set; }
+
+ public ulong OffsetsCount { get; private set; }
+
+ public MarshaledContiguousStringCollection(IReadOnlyCollection strings, DataType dataType = DataType.StringAscii)
+ {
+ Encoding encoding = MarshaledString.GetEncoding(dataType);
+ try
+ {
+ Offsets = (ulong*)Marshal.AllocHGlobal(strings.Count * sizeof(ulong));
+ OffsetsCount = (ulong)strings.Count * sizeof(ulong);
+ ulong currentOffset = 0;
+ DataCount = 0;
+ foreach (var str in strings)
+ {
+ Offsets[currentOffset++] = DataCount;
+ DataCount += (ulong)encoding.GetByteCount(str);
+ }
+
+ Data = (byte*)Marshal.AllocHGlobal((IntPtr)DataCount);
+ int i = 0;
+ foreach (var str in strings)
+ {
+ currentOffset = Offsets[i];
+ encoding.GetBytes(str, new Span(Data + currentOffset, checked((int)(DataCount - currentOffset))));
+ i++;
+ }
+ }
+ catch
+ {
+ Dispose();
+ throw;
+ }
+ }
+
+ public void Dispose()
+ {
+ if (Data is not null)
+ {
+ Marshal.FreeHGlobal((IntPtr)Data);
+ Data = null;
+ DataCount = 0;
+ }
+ if (Offsets is not null)
+ {
+ Marshal.FreeHGlobal((IntPtr)Offsets);
+ Offsets = null;
+ OffsetsCount = 0;
+ }
+ }
+ }
+}
diff --git a/sources/TileDB.CSharp/Marshalling/MarshaledString.cs b/sources/TileDB.CSharp/Marshalling/MarshaledString.cs
index 428d437e..549df30a 100644
--- a/sources/TileDB.CSharp/Marshalling/MarshaledString.cs
+++ b/sources/TileDB.CSharp/Marshalling/MarshaledString.cs
@@ -19,6 +19,23 @@ public MarshaledString(string input)
Value = (sbyte*)ptr;
}
+ internal static Encoding GetEncoding(DataType dataType)
+ {
+ switch (dataType)
+ {
+ case DataType.StringAscii:
+ return Encoding.ASCII;
+ case DataType.StringUtf8:
+ return Encoding.UTF8;
+ case DataType.StringUtf16:
+ return Encoding.Unicode;
+ case DataType.StringUtf32:
+ return Encoding.UTF32;
+ }
+ ThrowHelpers.ThrowInvalidDataType(dataType);
+ return null;
+ }
+
internal static (IntPtr Pointer, int Length) AllocNullTerminated(string str)
{
if (str is null)
diff --git a/sources/TileDB.CSharp/Marshalling/MarshaledStringCollection.cs b/sources/TileDB.CSharp/Marshalling/MarshaledStringCollection.cs
index 56e1b9ec..e140de3f 100644
--- a/sources/TileDB.CSharp/Marshalling/MarshaledStringCollection.cs
+++ b/sources/TileDB.CSharp/Marshalling/MarshaledStringCollection.cs
@@ -5,7 +5,7 @@
namespace TileDB.CSharp.Marshalling
{
- internal unsafe struct MarshaledStringCollection : IDisposable
+ internal unsafe ref struct MarshaledStringCollection
{
public IntPtr* Strings { get; private set; }
diff --git a/sources/TileDB.CSharp/Marshalling/SafeHandles/EnumerationHandle.cs b/sources/TileDB.CSharp/Marshalling/SafeHandles/EnumerationHandle.cs
new file mode 100644
index 00000000..6fdd438b
--- /dev/null
+++ b/sources/TileDB.CSharp/Marshalling/SafeHandles/EnumerationHandle.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Runtime.InteropServices;
+using TileDB.Interop;
+
+namespace TileDB.CSharp.Marshalling.SafeHandles
+{
+ internal unsafe sealed class EnumerationHandle : SafeHandle
+ {
+ public EnumerationHandle() : base(IntPtr.Zero, true) { }
+
+ public EnumerationHandle(IntPtr handle, bool ownsHandle) : base(IntPtr.Zero, ownsHandle) { SetHandle(handle); }
+
+ public static EnumerationHandle Create(Context context, string name, tiledb_datatype_t datatype, uint cellValNum, bool ordered, byte* data, ulong dataSize, ulong* offsets, ulong offsetsSize)
+ {
+ var handle = new EnumerationHandle();
+ var successful = false;
+ tiledb_enumeration_t* enumeration = null;
+ try
+ {
+ using var contextHandle = context.Handle.Acquire();
+ using var ms_name = new MarshaledString(name);
+ context.handle_error(Methods.tiledb_enumeration_alloc(contextHandle, ms_name, datatype, cellValNum,
+ ordered ? 1 : 0, data, dataSize, offsets, offsetsSize, &enumeration));
+ successful = true;
+ }
+ finally
+ {
+ if (successful)
+ {
+ handle.InitHandle(enumeration);
+ }
+ else
+ {
+ handle.SetHandleAsInvalid();
+ }
+ }
+
+ return handle;
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ fixed (IntPtr* p = &handle)
+ {
+ Methods.tiledb_enumeration_free((tiledb_enumeration_t**)p);
+ }
+ return true;
+ }
+
+ internal void InitHandle(tiledb_enumeration_t* h) { SetHandle((IntPtr)h); }
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ public SafeHandleHolder Acquire() => new(this);
+ }
+}
diff --git a/sources/TileDB.CSharp/QueryCondition.cs b/sources/TileDB.CSharp/QueryCondition.cs
index f12cc4c0..11250c52 100644
--- a/sources/TileDB.CSharp/QueryCondition.cs
+++ b/sources/TileDB.CSharp/QueryCondition.cs
@@ -86,6 +86,13 @@ private void Init(string attribute_name, void* condition_value, ulong condition_
_ctx.handle_error(Methods.tiledb_query_condition_init(ctxHandle, handle, ms_attribute_name, condition_value, condition_value_size, (tiledb_query_condition_op_t)optype));
}
+ private void SetUseEnumeration(bool value)
+ {
+ using var ctxHandle = _ctx.Handle.Acquire();
+ using var handle = _handle.Acquire();
+ _ctx.handle_error(Methods.tiledb_query_condition_set_use_enumeration(ctxHandle, handle, value ? 1 : 0));
+ }
+
private static QueryCondition Combine(QueryCondition lhs, QueryCondition? rhs, QueryConditionCombinationOperatorType combination_optype)
{
var ctx = lhs._ctx;
@@ -147,9 +154,29 @@ public static QueryCondition Create(Context ctx, string attribute_name, string v
/// The type of the relationship between the attribute with
/// the name and .
public static QueryCondition Create(Context ctx, string attribute_name, T value, QueryConditionOperatorType optype) where T : struct
+ {
+ return Create(ctx, attribute_name, value, optype, true);
+ }
+
+ ///
+ /// Creates a new query condition with datatype .
+ ///
+ /// The associated with the query condition.
+ /// The name of the attribute the query condition refers to.
+ /// The value to compare the attribute with.
+ /// The type of the relationship between the attribute with
+ /// the name and .
+ /// Whether to match the query condition on the
+ /// enumerated value instead of the underlying value. Optional, defaults to
+ /// .
+ public static QueryCondition Create(Context ctx, string attribute_name, T value, QueryConditionOperatorType optype, bool useEnumeration = true) where T : struct
{
var ret = new QueryCondition(ctx);
ret.Init(attribute_name, value, optype);
+ if (!useEnumeration)
+ {
+ ret.SetUseEnumeration(false);
+ }
return ret;
}
diff --git a/sources/TileDB.CSharp/ThrowHelpers.cs b/sources/TileDB.CSharp/ThrowHelpers.cs
index 13a14ad4..e4436010 100644
--- a/sources/TileDB.CSharp/ThrowHelpers.cs
+++ b/sources/TileDB.CSharp/ThrowHelpers.cs
@@ -10,6 +10,10 @@ internal static class ThrowHelpers
public static void ThrowArgumentNull(string? paramName) =>
throw new ArgumentNullException(paramName);
+ [DoesNotReturn]
+ public static void ThrowInvalidDataType(DataType dataType, [CallerArgumentExpression(nameof(dataType))] string? paramName = null) =>
+ throw new ArgumentOutOfRangeException(nameof(dataType), dataType, "Invalid data type.");
+
[DoesNotReturn]
public static void ThrowTypeNotSupported() =>
throw new NotSupportedException("Type is not supported.");
diff --git a/tests/TileDB.CSharp.Test/EnumerationTest.cs b/tests/TileDB.CSharp.Test/EnumerationTest.cs
new file mode 100644
index 00000000..d57e4a95
--- /dev/null
+++ b/tests/TileDB.CSharp.Test/EnumerationTest.cs
@@ -0,0 +1,77 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Runtime.InteropServices;
+
+namespace TileDB.CSharp.Test
+{
+ [TestClass]
+ public class EnumerationTest
+ {
+ [TestMethod]
+ public void TestString()
+ {
+ string[] expectedValues = new[] { "Hello", "World", "👋" };
+ using Enumeration e = Enumeration.Create(Context.GetDefault(), "test_string", true, expectedValues);
+
+ System.Array values = e.GetValues();
+ byte[] rawData = e.GetRawData();
+ ulong[] rawOffsets = e.GetRawOffsets();
+
+ Assert.AreEqual("test_string", e.GetName());
+ Assert.IsTrue(e.IsOrdered);
+ Assert.AreEqual(DataType.StringUtf8, e.DataType);
+ Assert.AreEqual(Enumeration.VariableSized, e.ValuesPerMember);
+ CollectionAssert.AreEqual(expectedValues, values);
+ CollectionAssert.AreEqual("HelloWorld👋"u8.ToArray(), rawData);
+ CollectionAssert.AreEqual(new ulong[] { 0, 5, 10 }, rawOffsets);
+ }
+
+ [TestMethod]
+ [DataRow(1u)]
+ [DataRow(2u)]
+ [DataRow(4u)]
+ public void TestFixedSize(uint cellValNum)
+ {
+ int[] expectedValues = new[] { 1, 2, 3, 4 };
+
+ using Enumeration e = Enumeration.Create(Context.GetDefault(), "test_fixed", false, expectedValues, cellValNum);
+
+ System.Array values = e.GetValues();
+ byte[] rawData = e.GetRawData();
+ ulong[] rawOffsets = e.GetRawOffsets();
+
+ Assert.AreEqual("test_fixed", e.GetName());
+ Assert.IsFalse(e.IsOrdered);
+ Assert.AreEqual(DataType.Int32, e.DataType);
+ Assert.AreEqual(cellValNum, e.ValuesPerMember);
+ CollectionAssert.AreEqual(expectedValues, values);
+ CollectionAssert.AreEqual(new byte[] { 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0 }, rawData);
+ Assert.AreEqual(0, rawOffsets.Length);
+ }
+
+ [TestMethod]
+ public void TestVarSize()
+ {
+ int[] expectedValues = new int[] { 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 };
+ ulong[] expectedOffsets = new ulong[] { 0, 4, 12, 24 };
+
+ using Enumeration e = Enumeration.Create(Context.GetDefault(), "test_var", false, expectedValues, expectedOffsets);
+
+ int[][]? values = e.GetValues() as int[][];
+ byte[] rawData = e.GetRawData();
+ ulong[] rawOffsets = e.GetRawOffsets();
+
+ Assert.AreEqual("test_var", e.GetName());
+ Assert.IsFalse(e.IsOrdered);
+ Assert.AreEqual(DataType.Int32, e.DataType);
+ Assert.AreEqual(Enumeration.VariableSized, e.ValuesPerMember);
+ Assert.IsNotNull(values);
+ CollectionAssert.AreEqual(new int[] { 1 }, values[0]);
+ CollectionAssert.AreEqual(new int[] { 2, 2 }, values[1]);
+ CollectionAssert.AreEqual(new int[] { 3, 3, 3 }, values[2]);
+ CollectionAssert.AreEqual(new int[] { 4, 4, 4, 4 }, values[3]);
+ CollectionAssert.AreEqual(MemoryMarshal.AsBytes(expectedValues.AsSpan()).ToArray(), rawData);
+ Assert.AreEqual(4, rawOffsets.Length);
+ }
+ }
+}