public static hid_t CreateEnumType <T>()
        {
            var size = EnumHelper.GetUnderlyingTypeSize <T>();

            if (size != 1 && size != 2 && size != 4 && size != 8)
            {
                throw new ArgumentException("Unexpected enum type size.");
            }

            var enumType = H5T.create(H5T.class_t.ENUM, new IntPtr(size));

            if (enumType < 0)
            {
                return(-1);
            }

            var type = typeof(T);

            // The mapping from symbols to integers is N:1 in C but 1:1 in HDF.
            // Consider:
            // enum MyEnum
            // {
            //     Foo = 0,
            //     Bar = Foo,
            // }
            var values = Enum.GetValues(type).OfType <T>().Distinct().ToArray();

            for (int i = 0; i < values.Length; ++i)
            {
                var value = values[i];

                var unmanagedBuffer = new UnmanagedBuffer(size);
                unmanagedBuffer.WriteEnum(value);

                // TODO: Should I call H5T.convert before H5T.enum_insert?
                if (H5T.enum_insert(enumType, Enum.GetName(type, value), unmanagedBuffer) < 0)
                {
                    H5T.close(enumType);
                    return(-1);
                }
            }

            return(enumType);
        }
        private static long GetHdfTypeIdFromType(Type type)
        {
            var elementType = type.IsArray ? type.GetElementType() : type;

            if (elementType == typeof(bool))
            {
                return(H5T.NATIVE_UINT8);
            }

            else if (elementType == typeof(byte))
            {
                return(H5T.NATIVE_UINT8);
            }

            else if (elementType == typeof(sbyte))
            {
                return(H5T.NATIVE_INT8);
            }

            else if (elementType == typeof(ushort))
            {
                return(H5T.NATIVE_UINT16);
            }

            else if (elementType == typeof(short))
            {
                return(H5T.NATIVE_INT16);
            }

            else if (elementType == typeof(uint))
            {
                return(H5T.NATIVE_UINT32);
            }

            else if (elementType == typeof(int))
            {
                return(H5T.NATIVE_INT32);
            }

            else if (elementType == typeof(ulong))
            {
                return(H5T.NATIVE_UINT64);
            }

            else if (elementType == typeof(long))
            {
                return(H5T.NATIVE_INT64);
            }

            else if (elementType == typeof(float))
            {
                return(H5T.NATIVE_FLOAT);
            }

            else if (elementType == typeof(double))
            {
                return(H5T.NATIVE_DOUBLE);
            }

            // issues: https://en.wikipedia.org/wiki/Long_double
            //else if (elementType == typeof(decimal))
            //    return H5T.NATIVE_LDOUBLE;

            else if (elementType.IsEnum)
            {
                var baseTypeId = TestUtils.GetHdfTypeIdFromType(Enum.GetUnderlyingType(elementType));
                var typeId     = H5T.enum_create(baseTypeId);

                foreach (var value in Enum.GetValues(type))
                {
                    var value_converted = Convert.ToInt64(value);
                    var name            = Enum.GetName(type, value_converted);

                    var handle = GCHandle.Alloc(value_converted, GCHandleType.Pinned);
                    H5T.enum_insert(typeId, name, handle.AddrOfPinnedObject());
                }

                return(typeId);
            }

            else if (elementType == typeof(string) || elementType == typeof(IntPtr))
            {
                var typeId = H5T.copy(H5T.C_S1);

                H5T.set_size(typeId, H5T.VARIABLE);
                H5T.set_cset(typeId, H5T.cset_t.UTF8);

                return(typeId);
            }
            else if (elementType.IsValueType && !elementType.IsPrimitive)
            {
                var typeId = H5T.create(H5T.class_t.COMPOUND, new IntPtr(Marshal.SizeOf(elementType)));

                foreach (var fieldInfo in elementType.GetFields())
                {
                    var fieldType    = TestUtils.GetHdfTypeIdFromType(fieldInfo.FieldType);
                    var attribute    = fieldInfo.GetCustomAttribute <H5NameAttribute>(true);
                    var hdfFieldName = attribute is not null ? attribute.Name : fieldInfo.Name;

                    H5T.insert(typeId, hdfFieldName, Marshal.OffsetOf(elementType, fieldInfo.Name), fieldType);

                    if (H5I.is_valid(fieldType) > 0)
                    {
                        H5T.close(fieldType);
                    }
                }

                return(typeId);
            }
            else
            {
                throw new NotSupportedException();
            }
        }
        public static hid_t CreateEnumType <T>(string[] names, T[] values)
        {
            if (names == null)
            {
                throw new ArgumentNullException("names");
            }

            if (values == null)
            {
                throw new ArgumentNullException("values");
            }

            if (names.Length == 0)
            {
                throw new ArgumentException("names");
            }

            if (values.Length == 0)
            {
                throw new ArgumentException("values");
            }

            if (names.Length != values.Length)
            {
                throw new ArgumentException("names.Length != values.Length");
            }

            if (names.Any(string.IsNullOrEmpty))
            {
                throw new ArgumentException("names");
            }

            if (names.Distinct().Count() != names.Length)
            {
                throw new ArgumentException("duplicate in names");
            }

            if (values.Distinct().Count() != values.Length)
            {
                throw new ArgumentException("duplicate in values");
            }

            var type = typeof(T);

            var size = 0;

            if (type.IsEnum)
            {
                size = EnumHelper.GetUnderlyingTypeSize <T>();
            }
            else
            {
                size = Marshal.SizeOf(type);
            }

            var enumType = H5T.create(H5T.class_t.ENUM, new IntPtr(size));

            if (enumType < 0)
            {
                throw new HDF5Exception("Failed to create enum type");
            }

            for (int i = 0; i < values.Length; ++i)
            {
                var value = values[i];

                var unmanagedBuffer = new UnmanagedBuffer(size);
                if (type.IsEnum)
                {
                    unmanagedBuffer.WriteEnum(value);
                }
                else
                {
                    unmanagedBuffer.Write(value);
                }

                // TODO: Should I call H5T.convert before H5T.enum_insert?
                if (H5T.enum_insert(enumType, names[i], unmanagedBuffer) < 0)
                {
                    H5T.close(enumType);
                    throw new HDF5Exception("Failed to insert enum: {0} = {1}", names[i], value);
                }
            }

            return(enumType);
        }