Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
80e4356442 | |||
aaefd0eb86 | |||
6a286ed53d | |||
e6d236139c | |||
bdd04a80b7 | |||
433dca8c69 | |||
ef73a380bb | |||
199ab46f0c | |||
cfe51d57ae | |||
f727c6d430 | |||
82f43a7e5d | |||
99558d96c4 | |||
67905605d6 | |||
b0ae15abc9 | |||
beae9e4429 | |||
b66e380414 | |||
93be86d526 | |||
0e14434bae | |||
957234d30d |
248
.editorconfig
248
.editorconfig
@ -1,248 +0,0 @@
|
||||
# Remove the line below if you want to inherit .editorconfig settings from higher directories
|
||||
root = true
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
|
||||
dotnet_analyzer_diagnostic.category-Design.severity = warning
|
||||
dotnet_analyzer_diagnostic.category-Documentation.severity = warning
|
||||
dotnet_analyzer_diagnostic.category-Globalization.severity = warning
|
||||
dotnet_analyzer_diagnostic.category-Interoperability.severity = warning
|
||||
dotnet_analyzer_diagnostic.category-Maintainability.severity = warning
|
||||
dotnet_analyzer_diagnostic.category-Naming.severity = warning
|
||||
dotnet_analyzer_diagnostic.category-Performance.severity = warning
|
||||
dotnet_analyzer_diagnostic.category-Reliability.severity = warning
|
||||
dotnet_analyzer_diagnostic.category-Security.severity = warning
|
||||
dotnet_analyzer_diagnostic.category-Style.severity = warning
|
||||
dotnet_analyzer_diagnostic.category-Usage.severity = warning
|
||||
|
||||
dotnet_diagnostic.IDE0010.severity = none
|
||||
dotnet_diagnostic.IDE0072.severity = none
|
||||
dotnet_diagnostic.CA1707.severity = none
|
||||
dotnet_diagnostic.CA1861.severity = none
|
||||
|
||||
#### Core EditorConfig Options ####
|
||||
|
||||
# Indentation and spacing
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
tab_width = 4
|
||||
|
||||
# New line preferences
|
||||
end_of_line = crlf
|
||||
insert_final_newline = false
|
||||
|
||||
#### .NET Coding Conventions ####
|
||||
|
||||
# Organize usings
|
||||
dotnet_separate_import_directive_groups = false
|
||||
dotnet_sort_system_directives_first = false
|
||||
file_header_template = unset
|
||||
|
||||
# this. and Me. preferences
|
||||
dotnet_style_qualification_for_event = false
|
||||
dotnet_style_qualification_for_field = false
|
||||
dotnet_style_qualification_for_method = false
|
||||
dotnet_style_qualification_for_property = false
|
||||
|
||||
# Language keywords vs BCL types preferences
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true
|
||||
dotnet_style_predefined_type_for_member_access = true
|
||||
|
||||
# Parentheses preferences
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
|
||||
|
||||
# Modifier preferences
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members
|
||||
|
||||
# Expression-level preferences
|
||||
dotnet_style_coalesce_expression = true
|
||||
dotnet_style_collection_initializer = true
|
||||
dotnet_style_explicit_tuple_names = true
|
||||
dotnet_style_namespace_match_folder = true
|
||||
dotnet_style_null_propagation = true
|
||||
dotnet_style_object_initializer = true
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
dotnet_style_prefer_auto_properties = true
|
||||
dotnet_style_prefer_collection_expression = false
|
||||
dotnet_style_prefer_compound_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = false
|
||||
dotnet_style_prefer_conditional_expression_over_return = false
|
||||
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true
|
||||
dotnet_style_prefer_inferred_tuple_names = true
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true
|
||||
dotnet_style_prefer_simplified_interpolation = true
|
||||
|
||||
# Field preferences
|
||||
dotnet_style_readonly_field = true
|
||||
|
||||
# Parameter preferences
|
||||
dotnet_code_quality_unused_parameters = all:silent
|
||||
|
||||
# Suppression preferences
|
||||
dotnet_remove_unnecessary_suppression_exclusions = none
|
||||
|
||||
# New line preferences
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = true
|
||||
dotnet_style_allow_statement_immediately_after_block_experimental = true
|
||||
|
||||
#### C# Coding Conventions ####
|
||||
|
||||
# var preferences
|
||||
csharp_style_var_elsewhere = true
|
||||
csharp_style_var_for_built_in_types = true
|
||||
csharp_style_var_when_type_is_apparent = true
|
||||
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_accessors = true
|
||||
csharp_style_expression_bodied_constructors = false
|
||||
csharp_style_expression_bodied_indexers = true
|
||||
csharp_style_expression_bodied_lambdas = true
|
||||
csharp_style_expression_bodied_local_functions = false
|
||||
csharp_style_expression_bodied_methods = true:none
|
||||
csharp_style_expression_bodied_operators = false
|
||||
csharp_style_expression_bodied_properties = true
|
||||
|
||||
# Pattern matching preferences
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true
|
||||
csharp_style_prefer_extended_property_pattern = true
|
||||
csharp_style_prefer_not_pattern = true
|
||||
csharp_style_prefer_pattern_matching = true
|
||||
csharp_style_prefer_switch_expression = true
|
||||
|
||||
# Null-checking preferences
|
||||
csharp_style_conditional_delegate_call = true
|
||||
|
||||
# Modifier preferences
|
||||
csharp_prefer_static_local_function = true
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
|
||||
csharp_style_prefer_readonly_struct = true
|
||||
csharp_style_prefer_readonly_struct_member = true
|
||||
|
||||
# Code-block preferences
|
||||
csharp_prefer_braces = true
|
||||
csharp_prefer_simple_using_statement = true
|
||||
csharp_style_namespace_declarations = file_scoped:none
|
||||
csharp_style_prefer_method_group_conversion = true
|
||||
csharp_style_prefer_primary_constructors = false
|
||||
csharp_style_prefer_top_level_statements = true
|
||||
|
||||
# Expression-level preferences
|
||||
csharp_prefer_simple_default_expression = true
|
||||
csharp_style_deconstructed_variable_declaration = true
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = true
|
||||
csharp_style_inlined_variable_declaration = true
|
||||
csharp_style_prefer_index_operator = true
|
||||
csharp_style_prefer_local_over_anonymous_function = true
|
||||
csharp_style_prefer_null_check_over_type_check = true
|
||||
csharp_style_prefer_range_operator = true
|
||||
csharp_style_prefer_tuple_swap = true
|
||||
csharp_style_prefer_utf8_string_literals = true
|
||||
csharp_style_throw_expression = true
|
||||
csharp_style_unused_value_assignment_preference = discard_variable
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable
|
||||
|
||||
# 'using' directive preferences
|
||||
csharp_using_directive_placement = outside_namespace
|
||||
|
||||
# New line preferences
|
||||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
|
||||
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
|
||||
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
|
||||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
|
||||
csharp_style_allow_embedded_statements_on_same_line_experimental = true
|
||||
|
||||
#### C# Formatting Rules ####
|
||||
|
||||
# New line preferences
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_indent_switch_labels = true
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#### Naming styles ####
|
||||
|
||||
# Naming rules
|
||||
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||
|
||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
# Symbol specifications
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright 2017-2025 Alexander Luzgarev
|
||||
Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
68
MatFileHandler.Tests/AbstractTestDataFactory.cs
Executable file
68
MatFileHandler.Tests/AbstractTestDataFactory.cs
Executable file
@ -0,0 +1,68 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace MatFileHandler.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract factory of test data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TTestData">Type of test data.</typeparam>
|
||||
public abstract class AbstractTestDataFactory<TTestData>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractTestDataFactory{TTestData}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dataDirectory">Directory with test files.</param>
|
||||
/// <param name="testFilenameConvention">A convention used to filter test files.</param>
|
||||
protected AbstractTestDataFactory(string dataDirectory, ITestFilenameConvention testFilenameConvention)
|
||||
{
|
||||
DataDirectory = dataDirectory;
|
||||
TestFilenameConvention = testFilenameConvention;
|
||||
}
|
||||
|
||||
private string DataDirectory { get; }
|
||||
|
||||
private ITestFilenameConvention TestFilenameConvention { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get test data set by name.
|
||||
/// </summary>
|
||||
/// <param name="dataSet">Name of the data set.</param>
|
||||
/// <returns>Test data.</returns>
|
||||
public TTestData this[string dataSet] =>
|
||||
ReadTestData(FixPath(TestFilenameConvention.ConvertTestNameToFilename(dataSet)));
|
||||
|
||||
/// <summary>
|
||||
/// Get a sequence of all test data sets in the factory.
|
||||
/// </summary>
|
||||
/// <returns>A sequence of data sets.</returns>
|
||||
public IEnumerable<TTestData> GetAllTestData()
|
||||
{
|
||||
var files = Directory.EnumerateFiles(DataDirectory).Where(TestFilenameConvention.FilterFile);
|
||||
foreach (var filename in files)
|
||||
{
|
||||
yield return ReadTestData(filename);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read test data from a stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">Input stream.</param>
|
||||
/// <returns>Test data.</returns>
|
||||
protected abstract TTestData ReadDataFromStream(Stream stream);
|
||||
|
||||
private string FixPath(string filename) => Path.Combine(DataDirectory, filename);
|
||||
|
||||
private TTestData ReadTestData(string filename)
|
||||
{
|
||||
using (var stream = new FileStream(filename, FileMode.Open))
|
||||
{
|
||||
return ReadDataFromStream(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +1,32 @@
|
||||
using System;
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Xunit;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace MatFileHandler.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests of Matlab array manipulation.
|
||||
/// </summary>
|
||||
public sealed class ArrayHandlingTests : IDisposable
|
||||
[TestFixture]
|
||||
public class ArrayHandlingTests
|
||||
{
|
||||
private DataBuilder Builder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setup for array handling tests.
|
||||
/// Set up a DataBuilder.
|
||||
/// </summary>
|
||||
public ArrayHandlingTests()
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Builder = new DataBuilder();
|
||||
}
|
||||
|
||||
private DataBuilder Builder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Test numerical array creation.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
[Test]
|
||||
public void TestCreate()
|
||||
{
|
||||
TestCreateArrayOf<sbyte>();
|
||||
@ -51,78 +54,70 @@ namespace MatFileHandler.Tests
|
||||
/// <summary>
|
||||
/// Test numerical array manipulation.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
[Test]
|
||||
public void TestNumArray()
|
||||
{
|
||||
var array = Builder.NewArray<int>(2, 3);
|
||||
array[0, 1] = 2;
|
||||
Assert.Equal(2, array[0, 1]);
|
||||
Assert.That(array[0, 1], Is.EqualTo(2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test cell array manipulation.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
[Test]
|
||||
public void TestCellArray()
|
||||
{
|
||||
var array = Builder.NewCellArray(2, 3);
|
||||
Assert.Equal(new[] { 2, 3 }, array.Dimensions);
|
||||
Assert.That(array.Dimensions, Is.EqualTo(new[] { 2, 3 }));
|
||||
array[0, 1] = Builder.NewArray<int>(1, 2);
|
||||
Assert.True(array[1, 2].IsEmpty);
|
||||
Assert.False(array[0, 1].IsEmpty);
|
||||
Assert.That(array[1, 2].IsEmpty, Is.True);
|
||||
Assert.That(array[0, 1].IsEmpty, Is.False);
|
||||
var assigned = (IArrayOf<int>)array[0, 1];
|
||||
Assert.NotNull(assigned);
|
||||
Assert.Equal(new[] { 1, 2 }, assigned.Dimensions);
|
||||
Assert.That(assigned, Is.Not.Null);
|
||||
Assert.That(assigned.Dimensions, Is.EqualTo(new[] { 1, 2 }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test structure array manipulation.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
[Test]
|
||||
public void TestStructureArray()
|
||||
{
|
||||
var array = Builder.NewStructureArray(new[] { "x", "y" }, 2, 3);
|
||||
array["x", 0, 1] = Builder.NewArray<int>(1, 2);
|
||||
Assert.True(array["y", 0, 1].IsEmpty);
|
||||
Assert.False(array["x", 0, 1].IsEmpty);
|
||||
Assert.That(array["y", 0, 1].IsEmpty, Is.True);
|
||||
Assert.That(array["x", 0, 1].IsEmpty, Is.False);
|
||||
var assigned = (IArrayOf<int>)array["x", 0, 1];
|
||||
Assert.NotNull(assigned);
|
||||
Assert.Equal(new[] { 1, 2 }, assigned.Dimensions);
|
||||
Assert.That(assigned, Is.Not.Null);
|
||||
Assert.That(assigned.Dimensions, Is.EqualTo(new[] { 1, 2 }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test character array manipulation.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
[Test]
|
||||
public void TestString()
|
||||
{
|
||||
var array = Builder.NewCharArray("🍆");
|
||||
Assert.Equal(new[] { 1, 2 }, array.Dimensions);
|
||||
Assert.That(array.Dimensions, Is.EqualTo(new[] { 1, 2 }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test file creation.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
[Test]
|
||||
public void TestFile()
|
||||
{
|
||||
var file = Builder.NewFile(new List<IVariable>());
|
||||
Assert.NotNull(file);
|
||||
Assert.That(file, Is.Not.Null);
|
||||
}
|
||||
|
||||
private static void TestCreateArrayOf<T>()
|
||||
where T : struct
|
||||
{
|
||||
var array = new DataBuilder().NewArray<T>(2, 3);
|
||||
Assert.NotNull(array);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
Assert.That(array, Is.Not.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace MatFileHandler.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for the <see cref="ChecksumCalculatingStream"/> class.
|
||||
/// </summary>
|
||||
public class ChecksumCalculatingStreamTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Test writing various things.
|
||||
/// </summary>
|
||||
/// <param name="bytes">Bytes to write.</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(TestData))]
|
||||
public void Test(byte[] bytes)
|
||||
{
|
||||
using var stream = new MemoryStream();
|
||||
var sut = new ChecksumCalculatingStream(stream);
|
||||
sut.Write(bytes, 0, bytes.Length);
|
||||
var actual = sut.GetCrc();
|
||||
|
||||
var expected = ReferenceCalculation(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test data for <see cref="Test"/>.
|
||||
/// </summary>
|
||||
/// <returns>Test data.</returns>
|
||||
public static TheoryData<byte[]> TestData()
|
||||
{
|
||||
var empty = new byte[1234];
|
||||
var nonEmpty = new byte[12345];
|
||||
for (var i = 0; i < 1234; i++)
|
||||
{
|
||||
nonEmpty[i] = (byte)((i * i) % 256);
|
||||
}
|
||||
return new TheoryData<byte[]>()
|
||||
{
|
||||
new byte[] { 0x00 },
|
||||
new byte[] { 0x01 },
|
||||
new byte[] { 0xff },
|
||||
new byte[] { 0xff, 0xff },
|
||||
new byte[] { 0xff, 0xff, 0xff },
|
||||
new byte[] { 0xff, 0xff, 0xff, 0xff },
|
||||
new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
new byte[] { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
new byte[] { 0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13, 0x17, 0x1d },
|
||||
empty,
|
||||
nonEmpty,
|
||||
};
|
||||
}
|
||||
|
||||
private static Action<Stream> BinaryWriterAction(Action<BinaryWriter> action)
|
||||
{
|
||||
return stream =>
|
||||
{
|
||||
using var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true);
|
||||
action(writer);
|
||||
};
|
||||
}
|
||||
|
||||
private static uint ReferenceCalculation(byte[] bytes)
|
||||
{
|
||||
using var stream = new MemoryStream();
|
||||
stream.Write(bytes, 0, bytes.Length);
|
||||
stream.Position = 0;
|
||||
return CalculateAdler32Checksum(stream);
|
||||
}
|
||||
|
||||
private static uint CalculateAdler32Checksum(MemoryStream stream)
|
||||
{
|
||||
uint s1 = 1;
|
||||
uint s2 = 0;
|
||||
const uint bigPrime = 0xFFF1;
|
||||
const int bufferSize = 2048;
|
||||
var buffer = new byte[bufferSize];
|
||||
while (true)
|
||||
{
|
||||
var bytesRead = stream.Read(buffer, 0, bufferSize);
|
||||
for (var i = 0; i < bytesRead; i++)
|
||||
{
|
||||
s1 = (s1 + buffer[i]) % bigPrime;
|
||||
s2 = (s2 + s1) % bigPrime;
|
||||
}
|
||||
if (bytesRead < bufferSize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (s2 << 16) | s1;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
namespace MatFileHandler.Tests
|
||||
{
|
||||
/// <summary>
|
||||
@ -45,4 +47,4 @@ namespace MatFileHandler.Tests
|
||||
/// </summary>
|
||||
public static readonly ulong[] UInt64Limits = { 0UL, 18446744073709551615UL };
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +1,51 @@
|
||||
using Xunit;
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace MatFileHandler.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests of the ComplexOf value type.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class ComplexOfTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Test getting real and imaginary parts.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
[Test]
|
||||
public void TestAccessors()
|
||||
{
|
||||
var c = new ComplexOf<byte>(1, 2);
|
||||
Assert.Equal(1, c.Real);
|
||||
Assert.Equal(2, c.Imaginary);
|
||||
Assert.That(c.Real, Is.EqualTo(1));
|
||||
Assert.That(c.Imaginary, Is.EqualTo(2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test equality operators.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
[Test]
|
||||
public void TestEquals()
|
||||
{
|
||||
var c1 = new ComplexOf<byte>(1, 2);
|
||||
var c2 = new ComplexOf<byte>(3, 4);
|
||||
var c3 = new ComplexOf<byte>(1, 2);
|
||||
Assert.True(c1 == c3);
|
||||
Assert.True(c1 != c2);
|
||||
Assert.True(c2 != c3);
|
||||
Assert.That(c1 == c3, Is.True);
|
||||
Assert.That(c1 != c2, Is.True);
|
||||
Assert.That(c2 != c3, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test hash code calculation.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
[Test]
|
||||
public void TestGetHashCode()
|
||||
{
|
||||
var c1 = new ComplexOf<byte>(1, 2);
|
||||
var c2 = new ComplexOf<byte>(1, 2);
|
||||
var h1 = c1.GetHashCode();
|
||||
var h2 = c2.GetHashCode();
|
||||
Assert.Equal(h1, h2);
|
||||
Assert.That(h1, Is.EqualTo(h2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
43
MatFileHandler.Tests/ExtensionTestFilenameConvention.cs
Executable file
43
MatFileHandler.Tests/ExtensionTestFilenameConvention.cs
Executable file
@ -0,0 +1,43 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace MatFileHandler.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// A filename convention based on file extensions.
|
||||
/// </summary>
|
||||
internal class ExtensionTestFilenameConvention : ITestFilenameConvention
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExtensionTestFilenameConvention"/> class.
|
||||
/// </summary>
|
||||
/// <param name="extension">File extension.</param>
|
||||
public ExtensionTestFilenameConvention(string extension)
|
||||
{
|
||||
Extension = extension;
|
||||
}
|
||||
|
||||
private string Extension { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert test name to filename by adding the extension.
|
||||
/// </summary>
|
||||
/// <param name="testName">Test name.</param>
|
||||
/// <returns>The corresponding filename.</returns>
|
||||
public string ConvertTestNameToFilename(string testName)
|
||||
{
|
||||
return Path.ChangeExtension(testName, Extension);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare file's extension to the one specified during initialization.
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename.</param>
|
||||
/// <returns>True iff the file has the extension stored in the class.</returns>
|
||||
public bool FilterFile(string filename)
|
||||
{
|
||||
return Path.GetExtension(filename) == "." + Extension;
|
||||
}
|
||||
}
|
||||
}
|
24
MatFileHandler.Tests/ITestFilenameConvention.cs
Executable file
24
MatFileHandler.Tests/ITestFilenameConvention.cs
Executable file
@ -0,0 +1,24 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
namespace MatFileHandler.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for handling filtering tests based on filenames.
|
||||
/// </summary>
|
||||
public interface ITestFilenameConvention
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert test name to a filename (e.g., by adding an appropriate extension).
|
||||
/// </summary>
|
||||
/// <param name="testName">Name of a test.</param>
|
||||
/// <returns>Filename.</returns>
|
||||
string ConvertTestNameToFilename(string testName);
|
||||
|
||||
/// <summary>
|
||||
/// Decide if a file contains a test based on the filename.
|
||||
/// </summary>
|
||||
/// <param name="filename">A filename.</param>
|
||||
/// <returns>True iff the file should contain a test.</returns>
|
||||
bool FilterFile(string filename);
|
||||
}
|
||||
}
|
@ -1,23 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net461;net472;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x64;win-x86</RuntimeIdentifiers>
|
||||
<IsPackable>false</IsPackable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<DocumentationFile>bin\Debug\net5.0\MatFileHandler.Tests.xml</DocumentationFile>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<CodeAnalysisRuleSet>..\MatFileHandler.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<CodeAnalysisRuleSet>..\MatFileHandler.ruleset</CodeAnalysisRuleSet>
|
||||
<DocumentationFile>bin\Debug\netcoreapp2.0\MatFileHandler.Tests.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Condition=" '$(TargetFramework)' != 'net461' " Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Condition=" '$(TargetFramework)' == 'net461' " Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<AdditionalFiles Include="..\stylecop.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
|
||||
<PackageReference Include="NUnit" Version="3.9.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MatFileHandler\MatFileHandler.csproj" />
|
||||
@ -25,4 +26,9 @@
|
||||
<ItemGroup>
|
||||
<Content Include="test-data\**\*.mat" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004">
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
1219
MatFileHandler.Tests/MatFileReaderTests.cs
Executable file → Normal file
1219
MatFileHandler.Tests/MatFileReaderTests.cs
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
@ -1,27 +0,0 @@
|
||||
namespace MatFileHandler.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Method of reading .mat files for testing.
|
||||
/// </summary>
|
||||
public enum MatFileReadingMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// Undefined.
|
||||
/// </summary>
|
||||
Undefined = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Normal stream (like memory or file stream).
|
||||
/// </summary>
|
||||
NormalStream,
|
||||
|
||||
/// <summary>
|
||||
/// Partial stream (only is capable of reading one byte at a time).
|
||||
/// </summary>
|
||||
PartialStream,
|
||||
|
||||
/// <summary>
|
||||
/// Unaligned stream (what happens if the data don't start at the beginning?).
|
||||
/// </summary>
|
||||
UnalignedStream,
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace MatFileHandler.Tests;
|
||||
|
||||
internal static class MatFileReadingMethods
|
||||
{
|
||||
public static IMatFile ReadMatFile(MatFileReadingMethod method, string fullFileName)
|
||||
{
|
||||
using var stream = File.OpenRead(fullFileName);
|
||||
switch (method)
|
||||
{
|
||||
case MatFileReadingMethod.NormalStream:
|
||||
return ReadFromStream(stream);
|
||||
case MatFileReadingMethod.PartialStream:
|
||||
{
|
||||
using var wrapper = new PartialUnseekableReadStream(stream);
|
||||
return ReadFromStream(wrapper);
|
||||
}
|
||||
case MatFileReadingMethod.UnalignedStream:
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
ms.Seek(3, SeekOrigin.Begin);
|
||||
stream.CopyTo(ms);
|
||||
ms.Seek(3, SeekOrigin.Begin);
|
||||
return ReadFromStream(ms);
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static IMatFile ReadFromStream(Stream stream)
|
||||
{
|
||||
var reader = new MatFileReader(stream);
|
||||
return reader.Read();
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
namespace MatFileHandler.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Options to give to MatFileWriter constructor for testing it.
|
||||
/// </summary>
|
||||
public enum MatFileWriterOptionsForTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Undefined.
|
||||
/// </summary>
|
||||
Undefined = 0,
|
||||
|
||||
/// <summary>
|
||||
/// No options.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Option to always use compression.
|
||||
/// </summary>
|
||||
Always,
|
||||
|
||||
/// <summary>
|
||||
/// Option to never use compression.
|
||||
/// </summary>
|
||||
Never,
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using Xunit;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace MatFileHandler.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests of file writing API.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class MatFileWriterTests
|
||||
{
|
||||
private const string TestDirectory = "test-data";
|
||||
@ -15,8 +18,8 @@ namespace MatFileHandler.Tests
|
||||
/// <summary>
|
||||
/// Test writing a simple Double array.
|
||||
/// </summary>
|
||||
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||
public void TestWrite(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||
[Test]
|
||||
public void TestWrite()
|
||||
{
|
||||
var builder = new DataBuilder();
|
||||
var array = builder.NewArray<double>(1, 2);
|
||||
@ -24,13 +27,13 @@ namespace MatFileHandler.Tests
|
||||
array[1] = 17.0;
|
||||
var variable = builder.NewVariable("test", array);
|
||||
var actual = builder.NewFile(new[] { variable });
|
||||
MatCompareWithTestData("good", "double-array", actual, method, options);
|
||||
MatCompareWithLevel5TestData("double-array", actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test writing a large file.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
[Test]
|
||||
public void TestHuge()
|
||||
{
|
||||
var builder = new DataBuilder();
|
||||
@ -49,8 +52,8 @@ namespace MatFileHandler.Tests
|
||||
/// <summary>
|
||||
/// Test writing lower and upper limits of integer data types.
|
||||
/// </summary>
|
||||
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||
public void TestLimits(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||
[Test]
|
||||
public void TestLimits()
|
||||
{
|
||||
var builder = new DataBuilder();
|
||||
var int8 = builder.NewVariable("int8_", builder.NewArray(CommonData.Int8Limits, 1, 2));
|
||||
@ -62,14 +65,14 @@ namespace MatFileHandler.Tests
|
||||
var int64 = builder.NewVariable("int64_", builder.NewArray(CommonData.Int64Limits, 1, 2));
|
||||
var uint64 = builder.NewVariable("uint64_", builder.NewArray(CommonData.UInt64Limits, 1, 2));
|
||||
var actual = builder.NewFile(new[] { int16, int32, int64, int8, uint16, uint32, uint64, uint8 });
|
||||
MatCompareWithTestData("good", "limits", actual, method, options);
|
||||
MatCompareWithLevel5TestData("limits", actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test writing lower and upper limits of integer-based complex data types.
|
||||
/// </summary>
|
||||
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||
public void TestLimitsComplex(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||
[Test]
|
||||
public void TestLimitsComplex()
|
||||
{
|
||||
var builder = new DataBuilder();
|
||||
var int8Complex = builder.NewVariable(
|
||||
@ -101,26 +104,26 @@ namespace MatFileHandler.Tests
|
||||
int16Complex, int32Complex, int64Complex, int8Complex,
|
||||
uint16Complex, uint32Complex, uint64Complex, uint8Complex,
|
||||
});
|
||||
MatCompareWithTestData("good", "limits_complex", actual, method, options);
|
||||
MatCompareWithLevel5TestData("limits_complex", actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test writing a wide-Unicode symbol.
|
||||
/// </summary>
|
||||
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||
public void TestUnicodeWide(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||
[Test]
|
||||
public void TestUnicodeWide()
|
||||
{
|
||||
var builder = new DataBuilder();
|
||||
var s = builder.NewVariable("s", builder.NewCharArray("🍆"));
|
||||
var actual = builder.NewFile(new[] { s });
|
||||
MatCompareWithTestData("good", "unicode-wide", actual, method, options);
|
||||
MatCompareWithLevel5TestData("unicode-wide", actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test writing a sparse array.
|
||||
/// </summary>
|
||||
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||
public void TestSparseArray(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||
[Test]
|
||||
public void TestSparseArray()
|
||||
{
|
||||
var builder = new DataBuilder();
|
||||
var sparseArray = builder.NewSparseArray<double>(4, 5);
|
||||
@ -130,14 +133,14 @@ namespace MatFileHandler.Tests
|
||||
sparseArray[2, 3] = 4;
|
||||
var sparse = builder.NewVariable("sparse_", sparseArray);
|
||||
var actual = builder.NewFile(new[] { sparse });
|
||||
MatCompareWithTestData("good", "sparse", actual, method, options);
|
||||
MatCompareWithLevel5TestData("sparse", actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test writing a structure array.
|
||||
/// </summary>
|
||||
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||
public void TestStructure(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||
[Test]
|
||||
public void TestStructure()
|
||||
{
|
||||
var builder = new DataBuilder();
|
||||
var structure = builder.NewStructureArray(new[] { "x", "y" }, 2, 3);
|
||||
@ -158,27 +161,27 @@ namespace MatFileHandler.Tests
|
||||
structure["y", 1, 2] = builder.NewEmpty();
|
||||
var struct_ = builder.NewVariable("struct_", structure);
|
||||
var actual = builder.NewFile(new[] { struct_ });
|
||||
MatCompareWithTestData("good", "struct", actual, method, options);
|
||||
MatCompareWithLevel5TestData("struct", actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test writing a logical array.
|
||||
/// </summary>
|
||||
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||
public void TestLogical(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||
[Test]
|
||||
public void TestLogical()
|
||||
{
|
||||
var builder = new DataBuilder();
|
||||
var logical = builder.NewArray(new[] { true, false, true, true, false, true }, 2, 3);
|
||||
var logicalVariable = builder.NewVariable("logical_", logical);
|
||||
var actual = builder.NewFile(new[] { logicalVariable });
|
||||
MatCompareWithTestData("good", "logical", actual, method, options);
|
||||
MatCompareWithLevel5TestData("logical", actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test writing a sparse logical array.
|
||||
/// </summary>
|
||||
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||
public void TestSparseLogical(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||
[Test]
|
||||
public void TestSparseLogical()
|
||||
{
|
||||
var builder = new DataBuilder();
|
||||
var array = builder.NewSparseArray<bool>(2, 3);
|
||||
@ -188,14 +191,14 @@ namespace MatFileHandler.Tests
|
||||
array[1, 2] = true;
|
||||
var sparseLogical = builder.NewVariable("sparse_logical", array);
|
||||
var actual = builder.NewFile(new[] { sparseLogical });
|
||||
MatCompareWithTestData("good", "sparse_logical", actual, method, options);
|
||||
MatCompareWithLevel5TestData("sparse_logical", actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test writing a sparse complex array.
|
||||
/// </summary>
|
||||
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||
public void TestSparseComplex(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||
[Test]
|
||||
public void TestSparseComplex()
|
||||
{
|
||||
var builder = new DataBuilder();
|
||||
var array = builder.NewSparseArray<Complex>(2, 2);
|
||||
@ -204,60 +207,38 @@ namespace MatFileHandler.Tests
|
||||
array[1, 1] = 0.5 + Complex.ImaginaryOne;
|
||||
var sparseComplex = builder.NewVariable("sparse_complex", array);
|
||||
var actual = builder.NewFile(new[] { sparseComplex });
|
||||
MatCompareWithTestData("good", "sparse_complex", actual, method, options);
|
||||
MatCompareWithLevel5TestData("sparse_complex", actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test writing a global variable.
|
||||
/// </summary>
|
||||
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||
public void TestGlobal(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||
[Test]
|
||||
public void TestGlobal()
|
||||
{
|
||||
var builder = new DataBuilder();
|
||||
var array = builder.NewArray(new double[] { 1, 3, 5 }, 1, 3);
|
||||
var global = builder.NewVariable("global_", array, true);
|
||||
var actual = builder.NewFile(new[] { global });
|
||||
MatCompareWithTestData("good", "global", actual, method, options);
|
||||
MatCompareWithLevel5TestData("global", actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Various writing methods for testing writing of .mat files.
|
||||
/// </summary>
|
||||
public static TheoryData<MatFileWritingMethod, MatFileWriterOptionsForTests> MatFileWritingTestData
|
||||
{
|
||||
get
|
||||
{
|
||||
var always = new MatFileWriterOptions { UseCompression = CompressionUsage.Always};
|
||||
var never = new MatFileWriterOptions { UseCompression = CompressionUsage.Never };
|
||||
var data = new TheoryData<MatFileWritingMethod, MatFileWriterOptionsForTests>
|
||||
{
|
||||
{ MatFileWritingMethod.NormalStream, MatFileWriterOptionsForTests.None },
|
||||
{ MatFileWritingMethod.NormalStream, MatFileWriterOptionsForTests.Always },
|
||||
{ MatFileWritingMethod.NormalStream, MatFileWriterOptionsForTests.Never },
|
||||
{ MatFileWritingMethod.UnseekableStream, MatFileWriterOptionsForTests.None },
|
||||
{ MatFileWritingMethod.UnseekableStream, MatFileWriterOptionsForTests.Always },
|
||||
{ MatFileWritingMethod.UnseekableStream, MatFileWriterOptionsForTests.Never },
|
||||
{ MatFileWritingMethod.UnalignedStream, MatFileWriterOptionsForTests.None },
|
||||
{ MatFileWritingMethod.UnalignedStream, MatFileWriterOptionsForTests.Always },
|
||||
{ MatFileWritingMethod.UnalignedStream, MatFileWriterOptionsForTests.Never },
|
||||
};
|
||||
return data;
|
||||
}
|
||||
}
|
||||
private static AbstractTestDataFactory<IMatFile> GetMatTestData(string factoryName) =>
|
||||
new MatTestDataFactory(Path.Combine(TestDirectory, factoryName));
|
||||
|
||||
private static void CompareSparseArrays<T>(ISparseArrayOf<T> expected, ISparseArrayOf<T> actual)
|
||||
private void CompareSparseArrays<T>(ISparseArrayOf<T> expected, ISparseArrayOf<T> actual)
|
||||
where T : struct
|
||||
{
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
||||
Assert.Equal(expected.Data, actual.Data);
|
||||
Assert.That(actual, Is.Not.Null);
|
||||
Assert.That(expected.Dimensions, Is.EqualTo(actual.Dimensions));
|
||||
Assert.That(expected.Data, Is.EquivalentTo(actual.Data));
|
||||
}
|
||||
|
||||
private static void CompareStructureArrays(IStructureArray expected, IStructureArray actual)
|
||||
private void CompareStructureArrays(IStructureArray expected, IStructureArray actual)
|
||||
{
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
||||
Assert.Equal(expected.FieldNames, actual.FieldNames);
|
||||
Assert.That(actual, Is.Not.Null);
|
||||
Assert.That(expected.Dimensions, Is.EqualTo(actual.Dimensions));
|
||||
Assert.That(expected.FieldNames, Is.EquivalentTo(actual.FieldNames));
|
||||
foreach (var name in expected.FieldNames)
|
||||
{
|
||||
for (var i = 0; i < expected.Count; i++)
|
||||
@ -267,31 +248,31 @@ namespace MatFileHandler.Tests
|
||||
}
|
||||
}
|
||||
|
||||
private static void CompareCellArrays(ICellArray expected, ICellArray actual)
|
||||
private void CompareCellArrays(ICellArray expected, ICellArray actual)
|
||||
{
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
||||
Assert.That(actual, Is.Not.Null);
|
||||
Assert.That(expected.Dimensions, Is.EqualTo(actual.Dimensions));
|
||||
for (var i = 0; i < expected.Count; i++)
|
||||
{
|
||||
CompareMatArrays(expected[i], actual[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CompareNumericalArrays<T>(IArrayOf<T> expected, IArrayOf<T> actual)
|
||||
private void CompareNumericalArrays<T>(IArrayOf<T> expected, IArrayOf<T> actual)
|
||||
{
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
||||
Assert.Equal(expected.Data, actual.Data);
|
||||
Assert.That(actual, Is.Not.Null);
|
||||
Assert.That(expected.Dimensions, Is.EqualTo(actual.Dimensions));
|
||||
Assert.That(expected.Data, Is.EqualTo(actual.Data));
|
||||
}
|
||||
|
||||
private static void CompareCharArrays(ICharArray expected, ICharArray actual)
|
||||
private void CompareCharArrays(ICharArray expected, ICharArray actual)
|
||||
{
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
||||
Assert.Equal(expected.String, actual.String);
|
||||
Assert.That(actual, Is.Not.Null);
|
||||
Assert.That(expected.Dimensions, Is.EqualTo(actual.Dimensions));
|
||||
Assert.That(expected.String, Is.EqualTo(actual.String));
|
||||
}
|
||||
|
||||
private static void CompareMatArrays(IArray expected, IArray actual)
|
||||
private void CompareMatArrays(IArray expected, IArray actual)
|
||||
{
|
||||
switch (expected)
|
||||
{
|
||||
@ -376,47 +357,70 @@ namespace MatFileHandler.Tests
|
||||
}
|
||||
if (expected.IsEmpty)
|
||||
{
|
||||
Assert.True(actual.IsEmpty);
|
||||
Assert.That(actual.IsEmpty, Is.True);
|
||||
return;
|
||||
}
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
private static void CompareMatFiles(IMatFile expected, IMatFile actual)
|
||||
private void CompareMatFiles(IMatFile expected, IMatFile actual)
|
||||
{
|
||||
Assert.Equal(expected.Variables.Length, actual.Variables.Length);
|
||||
Assert.That(expected.Variables.Length, Is.EqualTo(actual.Variables.Length));
|
||||
for (var i = 0; i < expected.Variables.Length; i++)
|
||||
{
|
||||
var expectedVariable = expected.Variables[i];
|
||||
var actualVariable = actual.Variables[i];
|
||||
Assert.Equal(expectedVariable.Name, actualVariable.Name);
|
||||
Assert.Equal(expectedVariable.IsGlobal, actualVariable.IsGlobal);
|
||||
Assert.That(expectedVariable.Name, Is.EqualTo(actualVariable.Name));
|
||||
Assert.That(expectedVariable.IsGlobal, Is.EqualTo(actualVariable.IsGlobal));
|
||||
CompareMatArrays(expectedVariable.Value, actualVariable.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void MatCompareWithTestData(
|
||||
string factoryName,
|
||||
string testName,
|
||||
private void CompareTestDataWithWritingOptions(
|
||||
IMatFile expected,
|
||||
IMatFile actual,
|
||||
MatFileWritingMethod method,
|
||||
MatFileWriterOptionsForTests options)
|
||||
MatFileWriterOptions? maybeOptions)
|
||||
{
|
||||
var fullFileName = Path.Combine("test-data", "good", $"{testName}.mat");
|
||||
var expected = MatFileReadingMethods.ReadMatFile(
|
||||
MatFileReadingMethod.NormalStream,
|
||||
fullFileName);
|
||||
var buffer = MatFileWritingMethods.WriteMatFile(method, options, actual);
|
||||
using var stream = new MemoryStream(buffer);
|
||||
var reader = new MatFileReader(stream);
|
||||
var actualRead = reader.Read();
|
||||
CompareMatFiles(expected, actualRead);
|
||||
byte[] buffer;
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
var writer = maybeOptions is MatFileWriterOptions options
|
||||
? new MatFileWriter(stream, options)
|
||||
: new MatFileWriter(stream);
|
||||
writer.Write(actual);
|
||||
buffer = stream.ToArray();
|
||||
}
|
||||
using (var stream = new MemoryStream(buffer))
|
||||
{
|
||||
var reader = new MatFileReader(stream);
|
||||
var actualRead = reader.Read();
|
||||
CompareMatFiles(expected, actualRead);
|
||||
}
|
||||
}
|
||||
|
||||
private static ComplexOf<T>[] CreateComplexLimits<T>(T[] limits)
|
||||
where T : struct
|
||||
private void MatCompareWithLevel5TestData(string testName, IMatFile actual)
|
||||
{
|
||||
MatCompareWithTestData("level5", testName, actual);
|
||||
}
|
||||
|
||||
private void MatCompareWithTestData(string factoryName, string testName, IMatFile actual)
|
||||
{
|
||||
var expected = GetMatTestData(factoryName)[testName];
|
||||
CompareTestDataWithWritingOptions(expected, actual, null);
|
||||
CompareTestDataWithWritingOptions(
|
||||
expected,
|
||||
actual,
|
||||
new MatFileWriterOptions { UseCompression = CompressionUsage.Always });
|
||||
CompareTestDataWithWritingOptions(
|
||||
expected,
|
||||
actual,
|
||||
new MatFileWriterOptions { UseCompression = CompressionUsage.Never });
|
||||
}
|
||||
|
||||
private ComplexOf<T>[] CreateComplexLimits<T>(T[] limits)
|
||||
where T : struct
|
||||
{
|
||||
return new[] { new ComplexOf<T>(limits[0], limits[1]), new ComplexOf<T>(limits[1], limits[0]) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
namespace MatFileHandler.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Method of writing .mat files for testing.
|
||||
/// </summary>
|
||||
public enum MatFileWritingMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// Undefined.
|
||||
/// </summary>
|
||||
Undefined = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Normal stream (like memory or file stream).
|
||||
/// </summary>
|
||||
NormalStream,
|
||||
|
||||
/// <summary>
|
||||
/// A stream that cannot be seeked (like a deflate stream).
|
||||
/// </summary>
|
||||
UnseekableStream,
|
||||
|
||||
/// <summary>
|
||||
/// Unaligned stream (what happens if the data don't start at the beginning?).
|
||||
/// </summary>
|
||||
UnalignedStream,
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace MatFileHandler.Tests;
|
||||
|
||||
internal static class MatFileWritingMethods
|
||||
{
|
||||
public static byte[] WriteMatFile(MatFileWritingMethod method, MatFileWriterOptionsForTests options, IMatFile matFile)
|
||||
{
|
||||
switch (method)
|
||||
{
|
||||
case MatFileWritingMethod.NormalStream:
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
var matFileWriter = CreateWriter(options, memoryStream);
|
||||
matFileWriter.Write(matFile);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
case MatFileWritingMethod.UnseekableStream:
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using var unseekableStream = new UnseekableWriteStream(memoryStream);
|
||||
var matFileWriter = CreateWriter(options, unseekableStream);
|
||||
matFileWriter.Write(matFile);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
case MatFileWritingMethod.UnalignedStream:
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
memoryStream.Seek(3, SeekOrigin.Begin);
|
||||
var matFileWriter = CreateWriter(options, memoryStream);
|
||||
matFileWriter.Write(matFile);
|
||||
var fullArray = memoryStream.ToArray();
|
||||
var length = fullArray.Length - 3;
|
||||
var result = new byte[length];
|
||||
Buffer.BlockCopy(fullArray, 3, result, 0, length);
|
||||
return result;
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static MatFileWriter CreateWriter(MatFileWriterOptionsForTests options, Stream stream)
|
||||
{
|
||||
return options switch
|
||||
{
|
||||
MatFileWriterOptionsForTests.None => new MatFileWriter(stream),
|
||||
MatFileWriterOptionsForTests.Always => new MatFileWriter(
|
||||
stream,
|
||||
new MatFileWriterOptions { UseCompression = CompressionUsage.Always }),
|
||||
MatFileWriterOptionsForTests.Never => new MatFileWriter(
|
||||
stream,
|
||||
new MatFileWriterOptions { UseCompression = CompressionUsage.Never }),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
}
|
35
MatFileHandler.Tests/MatTestDataFactory.cs
Executable file
35
MatFileHandler.Tests/MatTestDataFactory.cs
Executable file
@ -0,0 +1,35 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace MatFileHandler.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory providing the parsed contents of .mat files.
|
||||
/// </summary>
|
||||
public class MatTestDataFactory : AbstractTestDataFactory<IMatFile>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MatTestDataFactory"/> class.
|
||||
/// </summary>
|
||||
/// <param name="testDirectory">Directory containing test files.</param>
|
||||
public MatTestDataFactory(string testDirectory)
|
||||
: base(
|
||||
testDirectory,
|
||||
new ExtensionTestFilenameConvention("mat"))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read and parse data from a .mat file.
|
||||
/// </summary>
|
||||
/// <param name="stream">Input stream.</param>
|
||||
/// <returns>Parsed contents of the file.</returns>
|
||||
protected override IMatFile ReadDataFromStream(Stream stream)
|
||||
{
|
||||
var matFileReader = new MatFileReader(stream);
|
||||
var matFile = matFileReader.Read();
|
||||
return matFile;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace MatFileHandler.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// A stream which wraps another stream and only reads one byte at a time,
|
||||
/// while forbidding seeking in it.
|
||||
/// </summary>
|
||||
internal sealed class PartialUnseekableReadStream : Stream
|
||||
{
|
||||
private readonly Stream _baseStream;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PartialUnseekableReadStream"/> class.
|
||||
/// </summary>
|
||||
/// <param name="baseStream">The stream to wrap.</param>
|
||||
public PartialUnseekableReadStream(Stream baseStream)
|
||||
{
|
||||
_baseStream = baseStream;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanRead => _baseStream.CanRead;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanSeek => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanWrite => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override long Length => _baseStream.Length;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException();
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Flush()
|
||||
{
|
||||
_baseStream.Flush();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return _baseStream.Read(buffer, offset, Math.Min(1, count));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_baseStream.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace MatFileHandler.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// A stream which wraps another stream and forbids seeking in it.
|
||||
/// </summary>
|
||||
internal sealed class UnseekableWriteStream : Stream
|
||||
{
|
||||
public UnseekableWriteStream(Stream baseStream)
|
||||
{
|
||||
_baseStream = baseStream;
|
||||
}
|
||||
|
||||
private readonly Stream _baseStream;
|
||||
|
||||
public override bool CanRead => false;
|
||||
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override bool CanWrite => _baseStream.CanWrite;
|
||||
|
||||
public override long Length => _baseStream.Length;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException();
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
_baseStream.Flush();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
_baseStream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_baseStream.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/ascii.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/ascii.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/global.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/global.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/limits.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/limits.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/limits_complex.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/limits_complex.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/logical.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/logical.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/matrix.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/matrix.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/matrix_complex.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/matrix_complex.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/sparse.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/sparse.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/sparse_complex.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/sparse_complex.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/sparse_logical.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/sparse_logical.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/struct.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/struct.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/unicode-wide.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/unicode-wide.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/unicode.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/unicode.mat
Normal file
Binary file not shown.
0
MatFileHandler.Tests/test-data/good/ascii.mat → MatFileHandler.Tests/test-data/level5/ascii.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/ascii.mat → MatFileHandler.Tests/test-data/level5/ascii.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/global.mat → MatFileHandler.Tests/test-data/level5/global.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/global.mat → MatFileHandler.Tests/test-data/level5/global.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/limits.mat → MatFileHandler.Tests/test-data/level5/limits.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/limits.mat → MatFileHandler.Tests/test-data/level5/limits.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/limits_complex.mat → MatFileHandler.Tests/test-data/level5/limits_complex.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/limits_complex.mat → MatFileHandler.Tests/test-data/level5/limits_complex.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/logical.mat → MatFileHandler.Tests/test-data/level5/logical.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/logical.mat → MatFileHandler.Tests/test-data/level5/logical.mat
Executable file → Normal file
BIN
MatFileHandler.Tests/test-data/level5/matrix.mat
Normal file
BIN
MatFileHandler.Tests/test-data/level5/matrix.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/level5/matrix_complex.mat
Normal file
BIN
MatFileHandler.Tests/test-data/level5/matrix_complex.mat
Normal file
Binary file not shown.
0
MatFileHandler.Tests/test-data/good/sparse.mat → MatFileHandler.Tests/test-data/level5/sparse.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/sparse.mat → MatFileHandler.Tests/test-data/level5/sparse.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/sparse_complex.mat → MatFileHandler.Tests/test-data/level5/sparse_complex.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/sparse_complex.mat → MatFileHandler.Tests/test-data/level5/sparse_complex.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/sparse_logical.mat → MatFileHandler.Tests/test-data/level5/sparse_logical.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/sparse_logical.mat → MatFileHandler.Tests/test-data/level5/sparse_logical.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/struct.mat → MatFileHandler.Tests/test-data/level5/struct.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/struct.mat → MatFileHandler.Tests/test-data/level5/struct.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/unicode-wide.mat → MatFileHandler.Tests/test-data/level5/unicode-wide.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/unicode-wide.mat → MatFileHandler.Tests/test-data/level5/unicode-wide.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/unicode.mat → MatFileHandler.Tests/test-data/level5/unicode.mat
Executable file → Normal file
0
MatFileHandler.Tests/test-data/good/unicode.mat → MatFileHandler.Tests/test-data/level5/unicode.mat
Executable file → Normal file
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
|
||||
namespace MatFileHandler
|
||||
@ -136,20 +138,4 @@ namespace MatFileHandler
|
||||
Variable = variable;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sparse array properties.
|
||||
/// </summary>
|
||||
internal struct SparseArrayFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// Usual array properties.
|
||||
/// </summary>
|
||||
public ArrayFlags ArrayFlags;
|
||||
|
||||
/// <summary>
|
||||
/// Maximal number of non-zero elements.
|
||||
/// </summary>
|
||||
public uint NzMax;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// A stream that calculates Adler32 checksum of everything
|
||||
/// written to it before passing to another stream.
|
||||
/// </summary>
|
||||
internal class ChecksumCalculatingStream : Stream
|
||||
{
|
||||
private const uint BigPrime = 0xFFF1;
|
||||
private readonly Stream _stream;
|
||||
private uint s1;
|
||||
private uint s2;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ChecksumCalculatingStream"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stream">Wrapped stream.</param>
|
||||
public ChecksumCalculatingStream(Stream stream)
|
||||
{
|
||||
_stream = stream;
|
||||
s1 = 1;
|
||||
s2 = 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanRead => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanSeek => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanWrite => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Length => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotImplementedException();
|
||||
set => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Flush()
|
||||
{
|
||||
_stream.Flush();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
for (var i = offset; i < offset + count; i++)
|
||||
{
|
||||
s1 = (s1 + buffer[i]) % BigPrime;
|
||||
s2 = (s2 + s1) % BigPrime;
|
||||
}
|
||||
|
||||
_stream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the checksum of everything written to the stream so far.
|
||||
/// </summary>
|
||||
/// <returns>Checksum of everything written to the stream so far.</returns>
|
||||
public uint GetCrc()
|
||||
{
|
||||
return (s2 << 16) | s1;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
|
||||
namespace MatFileHandler
|
||||
@ -6,7 +8,7 @@ namespace MatFileHandler
|
||||
/// A structure representing a complex number where real and imaginary parts are of type T.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of real and imaginary parts.</typeparam>
|
||||
public readonly struct ComplexOf<T> : IEquatable<ComplexOf<T>>
|
||||
public struct ComplexOf<T> : IEquatable<ComplexOf<T>>
|
||||
where T : struct
|
||||
{
|
||||
/// <summary>
|
||||
@ -69,11 +71,10 @@ namespace MatFileHandler
|
||||
/// <returns>True iff another object is a complex number equal to this.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is null)
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return obj is ComplexOf<T> other && Equals(other);
|
||||
}
|
||||
|
||||
@ -89,4 +90,4 @@ namespace MatFileHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -1,9 +1,10 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
#pragma warning disable CA1822
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
@ -27,9 +28,7 @@ namespace MatFileHandler
|
||||
where T : struct
|
||||
{
|
||||
return new MatNumericalArrayOf<T>(
|
||||
GetStandardFlags<T>(),
|
||||
dimensions,
|
||||
string.Empty,
|
||||
new T[dimensions.NumberOfElements()]);
|
||||
}
|
||||
|
||||
@ -51,9 +50,9 @@ namespace MatFileHandler
|
||||
{
|
||||
if (data.Length != dimensions.NumberOfElements())
|
||||
{
|
||||
throw new ArgumentException("Data size does not match the specified dimensions", nameof(data));
|
||||
throw new ArgumentException("Data size does not match the specified dimensions", "data");
|
||||
}
|
||||
return new MatNumericalArrayOf<T>(GetStandardFlags<T>(), dimensions, string.Empty, data);
|
||||
return new MatNumericalArrayOf<T>(dimensions, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -65,7 +64,7 @@ namespace MatFileHandler
|
||||
{
|
||||
var flags = ConstructArrayFlags(ArrayType.MxCell);
|
||||
var elements = Enumerable.Repeat(MatArray.Empty() as IArray, dimensions.NumberOfElements()).ToList();
|
||||
return new MatCellArray(flags, dimensions, string.Empty, elements);
|
||||
return new MatCellArray(dimensions, elements);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -83,7 +82,7 @@ namespace MatFileHandler
|
||||
{
|
||||
dictionary[field] = elements.ToList();
|
||||
}
|
||||
return new MatStructureArray(flags, dimensions, string.Empty, dictionary);
|
||||
return new MatStructureArray(dimensions, dictionary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -106,7 +105,7 @@ namespace MatFileHandler
|
||||
{
|
||||
var flags = ConstructArrayFlags(ArrayType.MxChar);
|
||||
var ushortArray = contents.ToCharArray().Select(c => (ushort)c).ToArray();
|
||||
return new MatCharArrayOf<ushort>(flags, dimensions, string.Empty, ushortArray, contents);
|
||||
return new MatCharArrayOf<ushort>(dimensions, ushortArray, contents);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -128,9 +127,7 @@ namespace MatFileHandler
|
||||
where T : struct
|
||||
{
|
||||
return new MatSparseArrayOf<T>(
|
||||
GetStandardSparseArrayFlags<T>(),
|
||||
dimensions,
|
||||
string.Empty,
|
||||
new Dictionary<(int, int), T>());
|
||||
}
|
||||
|
||||
@ -156,7 +153,7 @@ namespace MatFileHandler
|
||||
return new MatFile(variables);
|
||||
}
|
||||
|
||||
private static ArrayFlags ConstructArrayFlags(ArrayType class_, bool isComplex = false, bool isLogical = false)
|
||||
private ArrayFlags ConstructArrayFlags(ArrayType class_, bool isComplex = false, bool isLogical = false)
|
||||
{
|
||||
return new ArrayFlags
|
||||
{
|
||||
@ -166,7 +163,7 @@ namespace MatFileHandler
|
||||
};
|
||||
}
|
||||
|
||||
private static ArrayFlags GetStandardFlags<T>()
|
||||
private ArrayFlags GetStandardFlags<T>()
|
||||
{
|
||||
if (typeof(T) == typeof(sbyte))
|
||||
{
|
||||
@ -254,15 +251,5 @@ namespace MatFileHandler
|
||||
}
|
||||
return ConstructArrayFlags(ArrayType.MxObject);
|
||||
}
|
||||
|
||||
private SparseArrayFlags GetStandardSparseArrayFlags<T>()
|
||||
{
|
||||
var arrayFlags = GetStandardFlags<T>();
|
||||
return new SparseArrayFlags
|
||||
{
|
||||
ArrayFlags = arrayFlags,
|
||||
NzMax = 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
@ -6,4 +8,4 @@ namespace MatFileHandler
|
||||
internal class DataElement
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -14,35 +16,31 @@ namespace MatFileHandler
|
||||
/// <summary>
|
||||
/// Construct a complex sparse array.
|
||||
/// </summary>
|
||||
/// <param name="flags">Array flags.</param>
|
||||
/// <param name="dimensions">Array dimensions.</param>
|
||||
/// <param name="name">Array name.</param>
|
||||
/// <param name="rowIndex">Row indices.</param>
|
||||
/// <param name="columnIndex">Denotes index ranges for each column.</param>
|
||||
/// <param name="data">Real parts of the values.</param>
|
||||
/// <param name="imaginaryData">Imaginary parts of the values.</param>
|
||||
/// <returns>A constructed array.</returns>
|
||||
public static MatArray ConvertToMatSparseArrayOfComplex(
|
||||
SparseArrayFlags flags,
|
||||
int[] dimensions,
|
||||
string name,
|
||||
int[] rowIndex,
|
||||
int[] columnIndex,
|
||||
DataElement data,
|
||||
DataElement imaginaryData)
|
||||
{
|
||||
var realParts = DataExtraction.GetDataAsDouble(data);
|
||||
var imaginaryParts = DataExtraction.GetDataAsDouble(imaginaryData);
|
||||
var realParts = DataExtraction.GetDataAsDouble(data).ToArrayLazily();
|
||||
var imaginaryParts = DataExtraction.GetDataAsDouble(imaginaryData).ToArrayLazily();
|
||||
if (realParts == null)
|
||||
{
|
||||
throw new HandlerException("Couldn't read sparse array.");
|
||||
}
|
||||
var dataDictionary =
|
||||
ConvertMatlabSparseToDictionary(
|
||||
DataExtraction.ConvertMatlabSparseToDictionary(
|
||||
rowIndex,
|
||||
columnIndex,
|
||||
j => new Complex(realParts[j], imaginaryParts[j]));
|
||||
return new MatSparseArrayOf<Complex>(flags, dimensions, name, dataDictionary);
|
||||
return new MatSparseArrayOf<Complex>(dimensions, dataDictionary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -51,15 +49,13 @@ namespace MatFileHandler
|
||||
/// <typeparam name="T">Element type (Double or Boolean).</typeparam>
|
||||
/// <param name="flags">Array flags.</param>
|
||||
/// <param name="dimensions">Array dimensions.</param>
|
||||
/// <param name="name">Array name.</param>
|
||||
/// <param name="rowIndex">Row indices.</param>
|
||||
/// <param name="columnIndex">Denotes index ranges for each column.</param>
|
||||
/// <param name="data">The values.</param>
|
||||
/// <returns>A constructed array.</returns>
|
||||
public static MatArray ConvertToMatSparseArrayOf<T>(
|
||||
SparseArrayFlags flags,
|
||||
ArrayFlags flags,
|
||||
int[] dimensions,
|
||||
string name,
|
||||
int[] rowIndex,
|
||||
int[] columnIndex,
|
||||
DataElement data)
|
||||
@ -69,24 +65,19 @@ namespace MatFileHandler
|
||||
{
|
||||
throw new NotSupportedException("Only 2-dimensional sparse arrays are supported");
|
||||
}
|
||||
if (data is null)
|
||||
if (data == null)
|
||||
{
|
||||
throw new ArgumentException("Null data found.", nameof(data));
|
||||
throw new ArgumentException("Null data found.", "data");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
var maybeElements =
|
||||
ConvertDataToSparseProperType<T>(data, flags.ArrayFlags.Variable.HasFlag(Variable.IsLogical));
|
||||
if (maybeElements is not { } elements)
|
||||
var elements =
|
||||
ConvertDataToSparseProperType<T>(data, flags.Variable.HasFlag(Variable.IsLogical));
|
||||
if (elements == null)
|
||||
{
|
||||
throw new HandlerException("Couldn't read sparse array.");
|
||||
}
|
||||
|
||||
var dataDictionary =
|
||||
ConvertMatlabSparseToDictionary(rowIndex, columnIndex, j => elements[j]);
|
||||
return new MatSparseArrayOf<T>(flags, dimensions, name, dataDictionary);
|
||||
DataExtraction.ConvertMatlabSparseToDictionary(rowIndex, columnIndex, j => elements[j]);
|
||||
return new MatSparseArrayOf<T>(dimensions, dataDictionary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -95,7 +86,6 @@ namespace MatFileHandler
|
||||
/// <typeparam name="T">Element type.</typeparam>
|
||||
/// <param name="flags">Array flags.</param>
|
||||
/// <param name="dimensions">Array dimensions.</param>
|
||||
/// <param name="name">Array name.</param>
|
||||
/// <param name="realData">Real parts of the values.</param>
|
||||
/// <param name="imaginaryData">Imaginary parts of the values.</param>
|
||||
/// <returns>A constructed array.</returns>
|
||||
@ -107,25 +97,27 @@ namespace MatFileHandler
|
||||
public static MatArray ConvertToMatNumericalArrayOf<T>(
|
||||
ArrayFlags flags,
|
||||
int[] dimensions,
|
||||
string name,
|
||||
DataElement realData,
|
||||
DataElement? imaginaryData)
|
||||
DataElement imaginaryData)
|
||||
where T : struct
|
||||
{
|
||||
if (flags.Variable.HasFlag(Variable.IsLogical))
|
||||
{
|
||||
var data = DataExtraction.GetDataAsUInt8(realData).Select(x => x != 0).ToArray();
|
||||
return new MatNumericalArrayOf<bool>(flags, dimensions, name, data);
|
||||
var data = DataExtraction.GetDataAsUInt8(realData).ToArrayLazily().Select(x => x != 0).ToArray();
|
||||
return new MatNumericalArrayOf<bool>(dimensions, data);
|
||||
}
|
||||
switch (flags.Class)
|
||||
{
|
||||
case ArrayType.MxChar:
|
||||
return realData switch
|
||||
switch (realData)
|
||||
{
|
||||
MiNum<byte> dataByte => ConvertToMatCharArray(flags, dimensions, name, dataByte),
|
||||
MiNum<ushort> dataUshort => ConvertToMatCharArray(flags, dimensions, name, dataUshort),
|
||||
_ => throw new NotSupportedException("Only utf8, utf16 or ushort char arrays are supported."),
|
||||
};
|
||||
case MiNum<byte> dataByte:
|
||||
return ConvertToMatCharArray(dimensions, dataByte);
|
||||
case MiNum<ushort> dataUshort:
|
||||
return ConvertToMatCharArray(dimensions, dataUshort);
|
||||
default:
|
||||
throw new NotSupportedException("Only utf8, utf16 or ushort char arrays are supported.");
|
||||
}
|
||||
case ArrayType.MxDouble:
|
||||
case ArrayType.MxSingle:
|
||||
case ArrayType.MxInt8:
|
||||
@ -139,11 +131,6 @@ namespace MatFileHandler
|
||||
var dataArray = ConvertDataToProperType<T>(realData, flags.Class);
|
||||
if (flags.Variable.HasFlag(Variable.IsComplex))
|
||||
{
|
||||
if (imaginaryData is null)
|
||||
{
|
||||
throw new HandlerException("Imaginary part of a complex variable not found.");
|
||||
}
|
||||
|
||||
var dataArray2 = ConvertDataToProperType<T>(imaginaryData, flags.Class);
|
||||
if (flags.Class == ArrayType.MxDouble)
|
||||
{
|
||||
@ -151,95 +138,78 @@ namespace MatFileHandler
|
||||
(dataArray as double[])
|
||||
.Zip(dataArray2 as double[], (x, y) => new Complex(x, y))
|
||||
.ToArray();
|
||||
return new MatNumericalArrayOf<Complex>(flags, dimensions, name, complexArray);
|
||||
return new MatNumericalArrayOf<Complex>(dimensions, complexArray);
|
||||
}
|
||||
var complexDataArray = dataArray.Zip(dataArray2, (x, y) => new ComplexOf<T>(x, y)).ToArray();
|
||||
return new MatNumericalArrayOf<ComplexOf<T>>(flags, dimensions, name, complexDataArray);
|
||||
return new MatNumericalArrayOf<ComplexOf<T>>(dimensions, complexDataArray);
|
||||
}
|
||||
return new MatNumericalArrayOf<T>(flags, dimensions, name, dataArray);
|
||||
return new MatNumericalArrayOf<T>(dimensions, dataArray);
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static MatCharArrayOf<byte> ConvertToMatCharArray(
|
||||
ArrayFlags flags,
|
||||
int[] dimensions,
|
||||
string name,
|
||||
MiNum<byte> dataElement)
|
||||
{
|
||||
var data = dataElement.Data;
|
||||
return new MatCharArrayOf<byte>(flags, dimensions, name, data, Encoding.UTF8.GetString(data));
|
||||
var data = dataElement?.Data;
|
||||
return new MatCharArrayOf<byte>(dimensions, data, Encoding.UTF8.GetString(data));
|
||||
}
|
||||
|
||||
private static T[] ConvertDataToProperType<T>(DataElement data, ArrayType arrayType)
|
||||
{
|
||||
return TryConvertDataToProperType<T>(data, arrayType)
|
||||
?? throw new HandlerException($"Unexpected data type.");
|
||||
switch (arrayType)
|
||||
{
|
||||
case ArrayType.MxDouble:
|
||||
return DataExtraction.GetDataAsDouble(data).ToArrayLazily() as T[];
|
||||
case ArrayType.MxSingle:
|
||||
return DataExtraction.GetDataAsSingle(data).ToArrayLazily() as T[];
|
||||
case ArrayType.MxInt8:
|
||||
return DataExtraction.GetDataAsInt8(data).ToArrayLazily() as T[];
|
||||
case ArrayType.MxUInt8:
|
||||
return DataExtraction.GetDataAsUInt8(data).ToArrayLazily() as T[];
|
||||
case ArrayType.MxInt16:
|
||||
return DataExtraction.GetDataAsInt16(data).ToArrayLazily() as T[];
|
||||
case ArrayType.MxUInt16:
|
||||
return DataExtraction.GetDataAsUInt16(data).ToArrayLazily() as T[];
|
||||
case ArrayType.MxInt32:
|
||||
return DataExtraction.GetDataAsInt32(data).ToArrayLazily() as T[];
|
||||
case ArrayType.MxUInt32:
|
||||
return DataExtraction.GetDataAsUInt32(data).ToArrayLazily() as T[];
|
||||
case ArrayType.MxInt64:
|
||||
return DataExtraction.GetDataAsInt64(data).ToArrayLazily() as T[];
|
||||
case ArrayType.MxUInt64:
|
||||
return DataExtraction.GetDataAsUInt64(data).ToArrayLazily() as T[];
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static T[]? ConvertDataToSparseProperType<T>(DataElement data, bool isLogical)
|
||||
private static T[] ConvertDataToSparseProperType<T>(DataElement data, bool isLogical)
|
||||
{
|
||||
if (isLogical)
|
||||
{
|
||||
return DataExtraction.GetDataAsUInt8(data).Select(x => x != 0).ToArray() as T[];
|
||||
return DataExtraction.GetDataAsUInt8(data).ToArrayLazily().Select(x => x != 0).ToArray() as T[];
|
||||
}
|
||||
|
||||
return data switch
|
||||
switch (data)
|
||||
{
|
||||
MiNum<double> => DataExtraction.GetDataAsDouble(data) as T[],
|
||||
_ => throw new NotSupportedException(),
|
||||
};
|
||||
case MiNum<double> _:
|
||||
return DataExtraction.GetDataAsDouble(data).ToArrayLazily() as T[];
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static MatCharArrayOf<ushort> ConvertToMatCharArray(
|
||||
ArrayFlags flags,
|
||||
int[] dimensions,
|
||||
string name,
|
||||
MiNum<ushort> dataElement)
|
||||
{
|
||||
var data = dataElement.Data;
|
||||
var data = dataElement?.Data;
|
||||
return new MatCharArrayOf<ushort>(
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
new string(data.Select(x => (char)x).ToArray()));
|
||||
}
|
||||
|
||||
private static Dictionary<(int row, int column), T> ConvertMatlabSparseToDictionary<T>(
|
||||
int[] rowIndex,
|
||||
int[] columnIndex,
|
||||
Func<int, T> get)
|
||||
{
|
||||
var result = new Dictionary<(int, int), T>();
|
||||
for (var column = 0; column < columnIndex.Length - 1; column++)
|
||||
{
|
||||
for (var j = columnIndex[column]; j < columnIndex[column + 1]; j++)
|
||||
{
|
||||
var row = rowIndex[j];
|
||||
result[(row, column)] = get(j);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static T[]? TryConvertDataToProperType<T>(DataElement data, ArrayType arrayType)
|
||||
{
|
||||
return arrayType switch
|
||||
{
|
||||
ArrayType.MxDouble => DataExtraction.GetDataAsDouble(data) as T[],
|
||||
ArrayType.MxSingle => DataExtraction.GetDataAsSingle(data) as T[],
|
||||
ArrayType.MxInt8 => DataExtraction.GetDataAsInt8(data) as T[],
|
||||
ArrayType.MxUInt8 => DataExtraction.GetDataAsUInt8(data) as T[],
|
||||
ArrayType.MxInt16 => DataExtraction.GetDataAsInt16(data) as T[],
|
||||
ArrayType.MxUInt16 => DataExtraction.GetDataAsUInt16(data) as T[],
|
||||
ArrayType.MxInt32 => DataExtraction.GetDataAsInt32(data) as T[],
|
||||
ArrayType.MxUInt32 => DataExtraction.GetDataAsUInt32(data) as T[],
|
||||
ArrayType.MxInt64 => DataExtraction.GetDataAsInt64(data) as T[],
|
||||
ArrayType.MxUInt64 => DataExtraction.GetDataAsUInt64(data) as T[],
|
||||
_ => throw new NotSupportedException()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -28,33 +30,10 @@ namespace MatFileHandler
|
||||
/// </summary>
|
||||
/// <param name="reader">Input reader.</param>
|
||||
/// <returns>Data element.</returns>
|
||||
public DataElement? Read(BinaryReader reader)
|
||||
public DataElementWithMetadata Read(BinaryReader reader)
|
||||
{
|
||||
var maybeTagPair = ReadTag(reader);
|
||||
if (maybeTagPair is not { } tagPair)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var (dataReader, tag) = tagPair;
|
||||
|
||||
var result = tag.Type switch
|
||||
{
|
||||
DataType.MiInt8 => ReadNum<sbyte>(tag, dataReader),
|
||||
DataType.MiUInt8 or DataType.MiUtf8 => ReadNum<byte>(tag, dataReader),
|
||||
DataType.MiInt16 => ReadNum<short>(tag, dataReader),
|
||||
DataType.MiUInt16 or DataType.MiUtf16 => ReadNum<ushort>(tag, dataReader),
|
||||
DataType.MiInt32 => ReadNum<int>(tag, dataReader),
|
||||
DataType.MiUInt32 => ReadNum<uint>(tag, dataReader),
|
||||
DataType.MiSingle => ReadNum<float>(tag, dataReader),
|
||||
DataType.MiDouble => ReadNum<double>(tag, dataReader),
|
||||
DataType.MiInt64 => ReadNum<long>(tag, dataReader),
|
||||
DataType.MiUInt64 => ReadNum<ulong>(tag, dataReader),
|
||||
DataType.MiMatrix => ReadMatrix(tag, dataReader),
|
||||
DataType.MiCompressed => ReadCompressed(tag, dataReader),
|
||||
_ => throw new NotSupportedException("Unknown element."),
|
||||
};
|
||||
|
||||
var (dataReader, tag) = ReadTag(reader);
|
||||
DataElementWithMetadata result = ReadElementWithFlags(tag, dataReader);
|
||||
if (tag.Type != DataType.MiCompressed)
|
||||
{
|
||||
var position = reader.BaseStream.Position;
|
||||
@ -99,7 +78,7 @@ namespace MatFileHandler
|
||||
private static ArrayFlags ReadArrayFlags(DataElement element)
|
||||
{
|
||||
var flagData = (element as MiNum<uint>)?.Data ??
|
||||
throw new HandlerException("Unexpected type in array flags.");
|
||||
throw new HandlerException("Unexpected type in array flags.");
|
||||
var class_ = (ArrayType)(flagData[0] & 0xff);
|
||||
var variableFlags = (flagData[0] >> 8) & 0x0e;
|
||||
return new ArrayFlags
|
||||
@ -158,44 +137,18 @@ namespace MatFileHandler
|
||||
return new MiNum<T>(result);
|
||||
}
|
||||
|
||||
private static SparseArrayFlags ReadSparseArrayFlags(DataElement element)
|
||||
private static (ArrayFlags flags, uint nzMax) ReadSparseArrayFlags(DataElement element)
|
||||
{
|
||||
var arrayFlags = ReadArrayFlags(element);
|
||||
var flagData = (element as MiNum<uint>)?.Data ??
|
||||
throw new HandlerException("Unexpected type in sparse array flags.");
|
||||
var nzMax = flagData[1];
|
||||
return new SparseArrayFlags
|
||||
{
|
||||
ArrayFlags = arrayFlags,
|
||||
NzMax = nzMax,
|
||||
};
|
||||
return (arrayFlags, nzMax);
|
||||
}
|
||||
|
||||
private static int? TryReadInt32(BinaryReader reader)
|
||||
private static (BinaryReader, Tag) ReadTag(BinaryReader reader)
|
||||
{
|
||||
var buffer = new byte[4];
|
||||
var position = 0;
|
||||
while (position < 4)
|
||||
{
|
||||
var actually = reader.BaseStream.Read(buffer, position, 4 - position);
|
||||
if (actually == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
position += actually;
|
||||
}
|
||||
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
private static (BinaryReader reader, Tag tag)? ReadTag(BinaryReader reader)
|
||||
{
|
||||
var maybeType = TryReadInt32(reader);
|
||||
if (maybeType is not int type)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var type = reader.ReadInt32();
|
||||
var typeHi = type >> 16;
|
||||
if (typeHi == 0)
|
||||
{
|
||||
@ -205,121 +158,134 @@ namespace MatFileHandler
|
||||
else
|
||||
{
|
||||
var length = typeHi;
|
||||
type &= 0xffff;
|
||||
type = type & 0xffff;
|
||||
var smallReader = new BinaryReader(new MemoryStream(reader.ReadBytes(4)));
|
||||
return (smallReader, new Tag((DataType)type, length));
|
||||
}
|
||||
}
|
||||
|
||||
private MatCellArray ContinueReadingCellArray(
|
||||
private DataElement ContinueReadingCellArray(
|
||||
BinaryReader reader,
|
||||
ArrayFlags flags,
|
||||
int[] dimensions,
|
||||
string name)
|
||||
int[] dimensions)
|
||||
{
|
||||
var numberOfElements = dimensions.NumberOfElements();
|
||||
var elements = new List<IArray>();
|
||||
for (var i = 0; i < numberOfElements; i++)
|
||||
{
|
||||
var element = Read(reader) as IArray
|
||||
?? throw new HandlerException("Unable to read cell array.");
|
||||
var element = Read(reader).Element as IArray;
|
||||
elements.Add(element);
|
||||
}
|
||||
|
||||
return new MatCellArray(flags, dimensions, name, elements);
|
||||
return new MatCellArray(dimensions, elements);
|
||||
}
|
||||
|
||||
private DataElement ContinueReadingOpaque(BinaryReader reader)
|
||||
private DataElementWithMetadata ContinueReadingOpaque(BinaryReader reader)
|
||||
{
|
||||
var nameElement = Read(reader) as MiNum<sbyte> ??
|
||||
var nameElement = Read(reader).Element as MiNum<sbyte> ??
|
||||
throw new HandlerException("Unexpected type in object name.");
|
||||
var name = ReadName(nameElement);
|
||||
var anotherElement = Read(reader) as MiNum<sbyte> ??
|
||||
var anotherElement = Read(reader).Element as MiNum<sbyte> ??
|
||||
throw new HandlerException("Unexpected type in object type description.");
|
||||
var typeDescription = ReadName(anotherElement);
|
||||
var classNameElement = Read(reader) as MiNum<sbyte> ??
|
||||
var classNameElement = Read(reader).Element as MiNum<sbyte> ??
|
||||
throw new HandlerException("Unexpected type in class name.");
|
||||
var className = ReadName(classNameElement);
|
||||
var dataElement = Read(reader) ?? throw new HandlerException("Missing opaque data element.");
|
||||
var dataElement = Read(reader).Element;
|
||||
var data = ReadData(dataElement);
|
||||
if (data is MatNumericalArrayOf<uint> linkElement)
|
||||
{
|
||||
var (dimensions, indexToObjectId, classIndex) = ParseOpaqueData(linkElement.Data);
|
||||
return new OpaqueLink(
|
||||
name,
|
||||
typeDescription,
|
||||
className,
|
||||
dimensions,
|
||||
data,
|
||||
indexToObjectId,
|
||||
classIndex,
|
||||
subsystemData);
|
||||
return new DataElementWithMetadata(
|
||||
new OpaqueLink(
|
||||
typeDescription,
|
||||
className,
|
||||
dimensions,
|
||||
data,
|
||||
indexToObjectId,
|
||||
classIndex,
|
||||
subsystemData),
|
||||
default,
|
||||
name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Opaque(name, typeDescription, className, Array.Empty<int>(), data, subsystemData);
|
||||
return new DataElementWithMetadata(
|
||||
new Opaque(
|
||||
typeDescription,
|
||||
className,
|
||||
new int[] { },
|
||||
data),
|
||||
default,
|
||||
name);
|
||||
}
|
||||
}
|
||||
|
||||
private MatArray ContinueReadingSparseArray(
|
||||
private DataElementWithMetadata ContinueReadingSparseArray(
|
||||
BinaryReader reader,
|
||||
DataElement firstElement,
|
||||
int[] dimensions,
|
||||
string name)
|
||||
{
|
||||
var sparseArrayFlags = ReadSparseArrayFlags(firstElement);
|
||||
var rowIndex = Read(reader) as MiNum<int> ??
|
||||
var (arrayFlags, nzMax) = ReadSparseArrayFlags(firstElement);
|
||||
var rowIndex = Read(reader).Element as MiNum<int> ??
|
||||
throw new HandlerException("Unexpected type in row indices of a sparse array.");
|
||||
var columnIndex = Read(reader) as MiNum<int> ??
|
||||
var columnIndex = Read(reader).Element as MiNum<int> ??
|
||||
throw new HandlerException("Unexpected type in column indices of a sparse array.");
|
||||
var data = Read(reader) ?? throw new HandlerException("Missing sparse array data.");
|
||||
if (sparseArrayFlags.ArrayFlags.Variable.HasFlag(Variable.IsLogical))
|
||||
var data = Read(reader).Element;
|
||||
if (arrayFlags.Variable.HasFlag(Variable.IsLogical))
|
||||
{
|
||||
return DataElementConverter.ConvertToMatSparseArrayOf<bool>(
|
||||
sparseArrayFlags,
|
||||
dimensions,
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatSparseArrayOf<bool>(
|
||||
arrayFlags,
|
||||
dimensions,
|
||||
rowIndex.Data,
|
||||
columnIndex.Data,
|
||||
data),
|
||||
arrayFlags,
|
||||
name,
|
||||
rowIndex.Data,
|
||||
columnIndex.Data,
|
||||
data);
|
||||
nzMax);
|
||||
}
|
||||
|
||||
if (sparseArrayFlags.ArrayFlags.Variable.HasFlag(Variable.IsComplex))
|
||||
if (arrayFlags.Variable.HasFlag(Variable.IsComplex))
|
||||
{
|
||||
var imaginaryData = Read(reader) ?? throw new HandlerException("Missing imaginary part of sparse array data.");
|
||||
return DataElementConverter.ConvertToMatSparseArrayOfComplex(
|
||||
sparseArrayFlags,
|
||||
dimensions,
|
||||
var imaginaryData = Read(reader).Element;
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatSparseArrayOfComplex(
|
||||
dimensions,
|
||||
rowIndex.Data,
|
||||
columnIndex.Data,
|
||||
data,
|
||||
imaginaryData),
|
||||
arrayFlags,
|
||||
name,
|
||||
rowIndex.Data,
|
||||
columnIndex.Data,
|
||||
data,
|
||||
imaginaryData);
|
||||
nzMax);
|
||||
}
|
||||
|
||||
return data switch
|
||||
switch (data)
|
||||
{
|
||||
MiNum<double> => DataElementConverter.ConvertToMatSparseArrayOf<double>(
|
||||
sparseArrayFlags,
|
||||
dimensions,
|
||||
name,
|
||||
rowIndex.Data,
|
||||
columnIndex.Data,
|
||||
data),
|
||||
_ => throw new NotSupportedException("Only double and logical sparse arrays are supported."),
|
||||
};
|
||||
case MiNum<double> _:
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatSparseArrayOf<double>(
|
||||
arrayFlags,
|
||||
dimensions,
|
||||
rowIndex.Data,
|
||||
columnIndex.Data,
|
||||
data),
|
||||
arrayFlags,
|
||||
name,
|
||||
nzMax);
|
||||
default:
|
||||
throw new NotSupportedException("Only double and logical sparse arrays are supported.");
|
||||
}
|
||||
}
|
||||
|
||||
private MatStructureArray ContinueReadingStructure(
|
||||
private DataElement ContinueReadingStructure(
|
||||
BinaryReader reader,
|
||||
ArrayFlags flags,
|
||||
int[] dimensions,
|
||||
string name,
|
||||
int fieldNameLength)
|
||||
{
|
||||
var element = Read(reader) as MiNum<sbyte>
|
||||
?? throw new HandlerException("Unable to parse structure field names.");
|
||||
var fieldNames = ReadFieldNames(element, fieldNameLength);
|
||||
var element = Read(reader).Element;
|
||||
var fieldNames = ReadFieldNames(element as MiNum<sbyte>, fieldNameLength);
|
||||
var fields = new Dictionary<string, List<IArray>>();
|
||||
foreach (var fieldName in fieldNames)
|
||||
{
|
||||
@ -331,66 +297,111 @@ namespace MatFileHandler
|
||||
{
|
||||
foreach (var fieldName in fieldNames)
|
||||
{
|
||||
var field = Read(reader) as IArray
|
||||
?? throw new HandlerException("Unable to parse field name.");
|
||||
var field = Read(reader).Element as IArray;
|
||||
fields[fieldName].Add(field);
|
||||
}
|
||||
}
|
||||
|
||||
return new MatStructureArray(flags, dimensions, name, fields);
|
||||
return new MatStructureArray(dimensions, fields);
|
||||
}
|
||||
|
||||
private DataElement ReadCompressed(Tag tag, BinaryReader reader)
|
||||
private DataElementWithMetadata Read(Stream stream)
|
||||
{
|
||||
using (var reader = new BinaryReader(stream))
|
||||
{
|
||||
return Read(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private DataElementWithMetadata ReadCompressed(Tag tag, BinaryReader reader)
|
||||
{
|
||||
reader.ReadBytes(2);
|
||||
|
||||
DataElement element;
|
||||
|
||||
using (var substream = new Substream(reader.BaseStream, tag.Length - 6))
|
||||
var compressedData = new byte[tag.Length - 6];
|
||||
reader.BaseStream.Read(compressedData, 0, tag.Length - 6);
|
||||
reader.ReadBytes(4);
|
||||
var resultStream = new MemoryStream();
|
||||
using (var compressedStream = new MemoryStream(compressedData))
|
||||
{
|
||||
using (var deflateStream = new DeflateStream(substream, CompressionMode.Decompress))
|
||||
using (var bufferedStream = new BufferedStream(deflateStream))
|
||||
using (var positionTrackingStream = new PositionTrackingStream(bufferedStream))
|
||||
using (var innerReader = new BinaryReader(positionTrackingStream))
|
||||
using (var stream = new DeflateStream(compressedStream, CompressionMode.Decompress, leaveOpen: true))
|
||||
{
|
||||
element = Read(innerReader) ?? throw new HandlerException("Missing compressed data.");
|
||||
}
|
||||
|
||||
if (substream.Position != substream.Length)
|
||||
{
|
||||
// In the pathological case that the deflate stream did not read the full
|
||||
// length, then read out the rest manually (normally 1 byte).
|
||||
reader.ReadBytes((int)(substream.Length - substream.Position));
|
||||
stream.CopyTo(resultStream);
|
||||
}
|
||||
}
|
||||
|
||||
reader.ReadBytes(4);
|
||||
|
||||
return element;
|
||||
resultStream.Position = 0;
|
||||
return Read(resultStream);
|
||||
}
|
||||
|
||||
private DataElement ReadMatrix(Tag tag, BinaryReader reader)
|
||||
private DataElementWithMetadata ReadElementWithFlags(Tag tag, BinaryReader reader)
|
||||
{
|
||||
switch (tag.Type)
|
||||
{
|
||||
case DataType.MiMatrix:
|
||||
return ReadMatrix(tag, reader);
|
||||
case DataType.MiCompressed:
|
||||
return ReadCompressed(tag, reader);
|
||||
default:
|
||||
var element = ReadElementWithoutFlags(tag, reader);
|
||||
return new DataElementWithMetadata(element);
|
||||
}
|
||||
}
|
||||
|
||||
private DataElement ReadElementWithoutFlags(Tag tag, BinaryReader reader)
|
||||
{
|
||||
switch (tag.Type)
|
||||
{
|
||||
case DataType.MiInt8:
|
||||
return ReadNum<sbyte>(tag, reader);
|
||||
case DataType.MiUInt8:
|
||||
case DataType.MiUtf8:
|
||||
return ReadNum<byte>(tag, reader);
|
||||
case DataType.MiInt16:
|
||||
return ReadNum<short>(tag, reader);
|
||||
case DataType.MiUInt16:
|
||||
case DataType.MiUtf16:
|
||||
return ReadNum<ushort>(tag, reader);
|
||||
case DataType.MiInt32:
|
||||
return ReadNum<int>(tag, reader);
|
||||
case DataType.MiUInt32:
|
||||
return ReadNum<uint>(tag, reader);
|
||||
case DataType.MiSingle:
|
||||
return ReadNum<float>(tag, reader);
|
||||
case DataType.MiDouble:
|
||||
return ReadNum<double>(tag, reader);
|
||||
case DataType.MiInt64:
|
||||
return ReadNum<long>(tag, reader);
|
||||
case DataType.MiUInt64:
|
||||
return ReadNum<ulong>(tag, reader);
|
||||
default:
|
||||
throw new NotSupportedException("Unknown element.");
|
||||
}
|
||||
}
|
||||
|
||||
private DataElementWithMetadata ReadMatrix(Tag tag, BinaryReader reader)
|
||||
{
|
||||
if (tag.Length == 0)
|
||||
{
|
||||
return MatArray.Empty();
|
||||
return new DataElementWithMetadata(MatArray.Empty());
|
||||
}
|
||||
|
||||
var element1 = Read(reader) ?? throw new HandlerException("Missing matrix data.");
|
||||
var element1 = Read(reader).Element;
|
||||
var flags = ReadArrayFlags(element1);
|
||||
if (flags.Class == ArrayType.MxOpaque)
|
||||
{
|
||||
return ContinueReadingOpaque(reader);
|
||||
}
|
||||
|
||||
var element2 = Read(reader) as MiNum<int> ??
|
||||
throw new HandlerException("Unexpected type in array dimensions data.");
|
||||
var element2 =
|
||||
Read(reader).Element as MiNum<int>
|
||||
?? throw new HandlerException("Unexpected type in array dimensions data.");
|
||||
var dimensions = ReadDimensionsArray(element2);
|
||||
var element3 = Read(reader) as MiNum<sbyte> ?? throw new HandlerException("Unexpected type in array name.");
|
||||
var element3 =
|
||||
Read(reader).Element as MiNum<sbyte>
|
||||
?? throw new HandlerException("Unexpected type in array name.");
|
||||
var name = ReadName(element3);
|
||||
if (flags.Class == ArrayType.MxCell)
|
||||
{
|
||||
return ContinueReadingCellArray(reader, flags, dimensions, name);
|
||||
return new DataElementWithMetadata(ContinueReadingCellArray(reader, dimensions));
|
||||
}
|
||||
|
||||
if (flags.Class == ArrayType.MxSparse)
|
||||
@ -398,12 +409,12 @@ namespace MatFileHandler
|
||||
return ContinueReadingSparseArray(reader, element1, dimensions, name);
|
||||
}
|
||||
|
||||
var element4 = Read(reader) ?? throw new HandlerException("Missing matrix data.");
|
||||
var element4 = Read(reader).Element;
|
||||
var data = ReadData(element4);
|
||||
DataElement? imaginaryData = null;
|
||||
DataElement imaginaryData = null;
|
||||
if (flags.Variable.HasFlag(Variable.IsComplex))
|
||||
{
|
||||
var element5 = Read(reader) ?? throw new HandlerException("Missing complex matrix data.");
|
||||
var element5 = Read(reader).Element;
|
||||
imaginaryData = ReadData(element5);
|
||||
}
|
||||
|
||||
@ -412,112 +423,144 @@ namespace MatFileHandler
|
||||
var fieldNameLengthElement = data as MiNum<int> ??
|
||||
throw new HandlerException(
|
||||
"Unexpected type in structure field name length.");
|
||||
return ContinueReadingStructure(reader, flags, dimensions, name, fieldNameLengthElement.Data[0]);
|
||||
return new DataElementWithMetadata(
|
||||
ContinueReadingStructure(reader, dimensions, fieldNameLengthElement.Data[0]),
|
||||
flags,
|
||||
name);
|
||||
}
|
||||
|
||||
switch (flags.Class)
|
||||
{
|
||||
case ArrayType.MxChar:
|
||||
return data switch
|
||||
switch (data)
|
||||
{
|
||||
MiNum<byte> => DataElementConverter.ConvertToMatNumericalArrayOf<byte>(
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
imaginaryData),
|
||||
MiNum<ushort> => DataElementConverter.ConvertToMatNumericalArrayOf<ushort>(
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
imaginaryData),
|
||||
_ => throw new NotSupportedException(
|
||||
$"This type of char array ({data.GetType()}) is not supported."),
|
||||
};
|
||||
case MiNum<byte> _:
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatNumericalArrayOf<byte>(
|
||||
flags,
|
||||
dimensions,
|
||||
data,
|
||||
imaginaryData),
|
||||
flags,
|
||||
name);
|
||||
case MiNum<ushort> _:
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatNumericalArrayOf<ushort>(
|
||||
flags,
|
||||
dimensions,
|
||||
data,
|
||||
imaginaryData),
|
||||
flags,
|
||||
name);
|
||||
default:
|
||||
throw new NotSupportedException(
|
||||
$"This type of char array ({data.GetType()}) is not supported.");
|
||||
}
|
||||
case ArrayType.MxInt8:
|
||||
return DataElementConverter.ConvertToMatNumericalArrayOf<sbyte>(
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatNumericalArrayOf<sbyte>(
|
||||
flags,
|
||||
dimensions,
|
||||
data,
|
||||
imaginaryData),
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
imaginaryData);
|
||||
name);
|
||||
case ArrayType.MxUInt8:
|
||||
if (flags.Variable.HasFlag(Variable.IsLogical))
|
||||
{
|
||||
return DataElementConverter.ConvertToMatNumericalArrayOf<bool>(
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatNumericalArrayOf<bool>(
|
||||
flags,
|
||||
dimensions,
|
||||
data,
|
||||
imaginaryData),
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
imaginaryData);
|
||||
name);
|
||||
}
|
||||
|
||||
return DataElementConverter.ConvertToMatNumericalArrayOf<byte>(
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatNumericalArrayOf<byte>(
|
||||
flags,
|
||||
dimensions,
|
||||
data,
|
||||
imaginaryData),
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
imaginaryData);
|
||||
name);
|
||||
case ArrayType.MxInt16:
|
||||
return DataElementConverter.ConvertToMatNumericalArrayOf<short>(
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatNumericalArrayOf<short>(
|
||||
flags,
|
||||
dimensions,
|
||||
data,
|
||||
imaginaryData),
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
imaginaryData);
|
||||
name);
|
||||
case ArrayType.MxUInt16:
|
||||
return DataElementConverter.ConvertToMatNumericalArrayOf<ushort>(
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatNumericalArrayOf<ushort>(
|
||||
flags,
|
||||
dimensions,
|
||||
data,
|
||||
imaginaryData),
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
imaginaryData);
|
||||
name);
|
||||
case ArrayType.MxInt32:
|
||||
return DataElementConverter.ConvertToMatNumericalArrayOf<int>(
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatNumericalArrayOf<int>(
|
||||
flags,
|
||||
dimensions,
|
||||
data,
|
||||
imaginaryData),
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
imaginaryData);
|
||||
name);
|
||||
case ArrayType.MxUInt32:
|
||||
return DataElementConverter.ConvertToMatNumericalArrayOf<uint>(
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatNumericalArrayOf<uint>(
|
||||
flags,
|
||||
dimensions,
|
||||
data,
|
||||
imaginaryData),
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
imaginaryData);
|
||||
name);
|
||||
case ArrayType.MxInt64:
|
||||
return DataElementConverter.ConvertToMatNumericalArrayOf<long>(
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatNumericalArrayOf<long>(
|
||||
flags,
|
||||
dimensions,
|
||||
data,
|
||||
imaginaryData),
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
imaginaryData);
|
||||
name);
|
||||
case ArrayType.MxUInt64:
|
||||
return DataElementConverter.ConvertToMatNumericalArrayOf<ulong>(
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatNumericalArrayOf<ulong>(
|
||||
flags,
|
||||
dimensions,
|
||||
data,
|
||||
imaginaryData),
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
imaginaryData);
|
||||
name);
|
||||
case ArrayType.MxSingle:
|
||||
return DataElementConverter.ConvertToMatNumericalArrayOf<float>(
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatNumericalArrayOf<float>(
|
||||
flags,
|
||||
dimensions,
|
||||
data,
|
||||
imaginaryData),
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
imaginaryData);
|
||||
name);
|
||||
case ArrayType.MxDouble:
|
||||
return DataElementConverter.ConvertToMatNumericalArrayOf<double>(
|
||||
return new DataElementWithMetadata(
|
||||
DataElementConverter.ConvertToMatNumericalArrayOf<double>(
|
||||
flags,
|
||||
dimensions,
|
||||
data,
|
||||
imaginaryData),
|
||||
flags,
|
||||
dimensions,
|
||||
name,
|
||||
data,
|
||||
imaginaryData);
|
||||
name);
|
||||
default:
|
||||
throw new HandlerException("Unknown data type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
54
MatFileHandler/DataElementWithMetadata.cs
Normal file
54
MatFileHandler/DataElementWithMetadata.cs
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Data element together with array flags, variable name, and sparse array's nzMax value.
|
||||
/// </summary>
|
||||
internal class DataElementWithMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataElementWithMetadata"/> class.
|
||||
/// </summary>
|
||||
/// <param name="element">Data element.</param>
|
||||
/// <param name="flags">Array flags.</param>
|
||||
/// <param name="name">Variable name.</param>
|
||||
/// <param name="nzMax">nzMax (for sparse arrays).</param>
|
||||
public DataElementWithMetadata(DataElement element, ArrayFlags flags, string name, uint nzMax = 0)
|
||||
{
|
||||
Element = element;
|
||||
Flags = flags;
|
||||
Name = name;
|
||||
NzMax = nzMax;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataElementWithMetadata"/> class.
|
||||
/// </summary>
|
||||
/// <param name="element">Data element.</param>
|
||||
public DataElementWithMetadata(DataElement element)
|
||||
{
|
||||
Element = element;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets data element.
|
||||
/// </summary>
|
||||
public DataElement Element { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets array flags.
|
||||
/// </summary>
|
||||
public ArrayFlags Flags { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets variable name.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets nzMax (for sparse arrays).
|
||||
/// </summary>
|
||||
public uint NzMax { get; }
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
|
||||
namespace MatFileHandler
|
||||
@ -14,21 +16,41 @@ namespace MatFileHandler
|
||||
/// <returns>Size in bytes.</returns>
|
||||
public static int Size(this DataType type)
|
||||
{
|
||||
return type switch
|
||||
switch (type)
|
||||
{
|
||||
DataType.MiInt8 or DataType.MiUInt8 => 1,
|
||||
DataType.MiInt16 or DataType.MiUInt16 => 2,
|
||||
DataType.MiInt32 or DataType.MiUInt32 => 4,
|
||||
DataType.MiSingle => 4,
|
||||
DataType.MiDouble => 8,
|
||||
DataType.MiInt64 or DataType.MiUInt64 => 8,
|
||||
DataType.MiMatrix => 0,
|
||||
DataType.MiCompressed => 0,
|
||||
DataType.MiUtf8 => 1,
|
||||
DataType.MiUtf16 => 2,
|
||||
DataType.MiUtf32 => 4,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null),
|
||||
};
|
||||
case DataType.MiInt8:
|
||||
return 1;
|
||||
case DataType.MiUInt8:
|
||||
return 1;
|
||||
case DataType.MiInt16:
|
||||
return 2;
|
||||
case DataType.MiUInt16:
|
||||
return 2;
|
||||
case DataType.MiInt32:
|
||||
return 4;
|
||||
case DataType.MiUInt32:
|
||||
return 4;
|
||||
case DataType.MiSingle:
|
||||
return 4;
|
||||
case DataType.MiDouble:
|
||||
return 8;
|
||||
case DataType.MiInt64:
|
||||
return 8;
|
||||
case DataType.MiUInt64:
|
||||
return 8;
|
||||
case DataType.MiMatrix:
|
||||
return 0;
|
||||
case DataType.MiCompressed:
|
||||
return 0;
|
||||
case DataType.MiUtf8:
|
||||
return 1;
|
||||
case DataType.MiUtf16:
|
||||
return 2;
|
||||
case DataType.MiUtf32:
|
||||
return 4;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
@ -10,6 +12,8 @@ namespace MatFileHandler
|
||||
public class DatetimeAdapter
|
||||
{
|
||||
private readonly double[] data;
|
||||
private readonly double[] data2;
|
||||
private readonly int[] dimensions;
|
||||
|
||||
private readonly DateTimeOffset epoch;
|
||||
|
||||
@ -26,29 +30,27 @@ namespace MatFileHandler
|
||||
}
|
||||
|
||||
epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
switch (matObject["data", 0])
|
||||
var dataArray = matObject["data", 0] as IArrayOf<double>;
|
||||
if (dataArray is null)
|
||||
{
|
||||
case IArrayOf<double> dataArray:
|
||||
data = dataArray.ConvertToDoubleArray()
|
||||
?? throw new HandlerException("Cannot extract data for the datetime adapter.");
|
||||
Dimensions = dataArray.Dimensions;
|
||||
break;
|
||||
case IArrayOf<Complex> dataComplex:
|
||||
var complexData = dataComplex.ConvertToComplexArray()
|
||||
?? throw new HandlerException("Cannot extract data for the datetime adapter.");
|
||||
data = complexData.Select(c => c.Real).ToArray();
|
||||
Dimensions = dataComplex.Dimensions;
|
||||
break;
|
||||
default:
|
||||
throw new HandlerException("Datetime data not found.");
|
||||
var dataComplex = matObject["data", 0] as IArrayOf<Complex>;
|
||||
var complexData = dataComplex.ConvertToComplexArray();
|
||||
data = complexData.Select(c => c.Real).ToArray();
|
||||
data2 = complexData.Select(c => c.Imaginary).ToArray();
|
||||
dimensions = dataComplex.Dimensions;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = dataArray.ConvertToDoubleArray();
|
||||
data2 = new double[data.Length];
|
||||
dimensions = dataArray.Dimensions;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets datetime array dimensions.
|
||||
/// </summary>
|
||||
public int[] Dimensions { get; }
|
||||
public int[] Dimensions => dimensions;
|
||||
|
||||
/// <summary>
|
||||
/// Gets values of datetime object at given position in the array converted to <see cref="DateTimeOffset"/>.
|
||||
@ -60,12 +62,12 @@ namespace MatFileHandler
|
||||
get
|
||||
{
|
||||
var milliseconds = data[Dimensions.DimFlatten(list)];
|
||||
return milliseconds switch
|
||||
if (milliseconds < -62_135_596_800_000.0 || milliseconds > 253_402_300_799_999.0)
|
||||
{
|
||||
< -62_135_596_800_000.0 or > 253_402_300_799_999.0 => null,
|
||||
_ => epoch.AddMilliseconds(milliseconds),
|
||||
};
|
||||
return null;
|
||||
}
|
||||
return epoch.AddMilliseconds(milliseconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System.Linq;
|
||||
|
||||
namespace MatFileHandler
|
||||
@ -35,48 +36,5 @@ namespace MatFileHandler
|
||||
{
|
||||
return dimensions.Aggregate(1, (x, y) => x * y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rearrange data from a flat (column-major) array into a multi-dimensional array.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Array element type.</typeparam>
|
||||
/// <param name="dimensions">Target array dimensions.</param>
|
||||
/// <param name="data">Flat (column-major) data to rearrange.</param>
|
||||
/// <returns>An array of specified dimensions containing data from the original flat array, layed out according to column-major order.</returns>
|
||||
public static Array UnflattenArray<T>(this int[] dimensions, T[] data)
|
||||
{
|
||||
var result = Array.CreateInstance(typeof(T), dimensions);
|
||||
var n = dimensions.NumberOfElements();
|
||||
var indices = new int[dimensions.Length];
|
||||
for (var i = 0; i < n; i++)
|
||||
{
|
||||
result.SetValue(data[i], indices);
|
||||
IncrementMultiIndex(dimensions, indices);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void IncrementMultiIndex(int[] dimensions, int[] indices)
|
||||
{
|
||||
var currentPosition = 0;
|
||||
while (true)
|
||||
{
|
||||
if (currentPosition >= indices.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
indices[currentPosition]++;
|
||||
if (indices[currentPosition] >= dimensions[currentPosition])
|
||||
{
|
||||
indices[currentPosition] = 0;
|
||||
currentPosition++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
|
||||
namespace MatFileHandler
|
||||
@ -7,6 +9,7 @@ namespace MatFileHandler
|
||||
/// </summary>
|
||||
public class DurationAdapter
|
||||
{
|
||||
private readonly int[] dimensions;
|
||||
private readonly double[] data;
|
||||
|
||||
/// <summary>
|
||||
@ -22,15 +25,14 @@ namespace MatFileHandler
|
||||
}
|
||||
|
||||
var dataObject = matObject["millis", 0];
|
||||
data = dataObject.ConvertToDoubleArray()
|
||||
?? throw new HandlerException("Cannot extract data for the duration adapter.");
|
||||
Dimensions = dataObject.Dimensions;
|
||||
data = dataObject.ConvertToDoubleArray();
|
||||
dimensions = dataObject.Dimensions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets duration array dimensions.
|
||||
/// </summary>
|
||||
public int[] Dimensions { get; }
|
||||
public int[] Dimensions => dimensions;
|
||||
|
||||
/// <summary>
|
||||
/// Gets duration object at given position.
|
||||
@ -39,4 +41,4 @@ namespace MatFileHandler
|
||||
/// <returns>Value.</returns>
|
||||
public TimeSpan this[params int[] list] => TimeSpan.FromTicks((long)(10000.0 * data[Dimensions.DimFlatten(list)]));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// A better interface for using enum adapter.
|
||||
/// </summary>
|
||||
public class EnumAdapter
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EnumAdapter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="array">Source enum object.</param>
|
||||
public EnumAdapter(IArray array)
|
||||
{
|
||||
var matObject = array as Opaque;
|
||||
if (matObject?.RawData is not IStructureArray rawData)
|
||||
{
|
||||
throw new HandlerException("Cannot extract data for the enum adapter.");
|
||||
}
|
||||
|
||||
if (rawData["ValueNames"] is not IArrayOf<uint> valueNamesData)
|
||||
{
|
||||
throw new HandlerException("Cannot extract data for the enum adapter.");
|
||||
}
|
||||
|
||||
var numberOfNames = valueNamesData.Count;
|
||||
var valueNames = new string[numberOfNames];
|
||||
var names = matObject.SubsystemData.FieldNames;
|
||||
for (var i = 0; i < numberOfNames; i++)
|
||||
{
|
||||
valueNames[i] = names[valueNamesData[i] - 1];
|
||||
}
|
||||
|
||||
if (rawData["ValueIndices"] is not IArrayOf<uint> valueIndices)
|
||||
{
|
||||
throw new HandlerException("Cannot extract data for the enum adapter.");
|
||||
}
|
||||
|
||||
ClassName = matObject.ClassName;
|
||||
ValueNames = valueNames;
|
||||
Values = valueIndices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets name of the enumeration class.
|
||||
/// </summary>
|
||||
public string ClassName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets names of the enumeration values.
|
||||
/// </summary>
|
||||
public string[] ValueNames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets indices of values stored in the variable.
|
||||
/// </summary>
|
||||
public IArrayOf<uint> Values { get; }
|
||||
}
|
||||
}
|
@ -1,391 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// A simulated writer of .mat files that just calculate the length of data that would be written.
|
||||
/// </summary>
|
||||
internal class FakeWriter
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets current position of the writer.
|
||||
/// </summary>
|
||||
public int Position { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Write contents of a numerical array.
|
||||
/// </summary>
|
||||
/// <param name="array">A numerical array.</param>
|
||||
/// <param name="name">Name of the array.</param>
|
||||
public void WriteNumericalArrayContents(IArray array, string name)
|
||||
{
|
||||
WriteArrayFlags();
|
||||
WriteDimensions(array.Dimensions);
|
||||
WriteName(name);
|
||||
WriteNumericalArrayValues(array);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write contents of a char array.
|
||||
/// </summary>
|
||||
/// <param name="charArray">A char array.</param>
|
||||
/// <param name="name">Name of the array.</param>
|
||||
public void WriteCharArrayContents(ICharArray charArray, string name)
|
||||
{
|
||||
WriteArrayFlags();
|
||||
WriteDimensions(charArray.Dimensions);
|
||||
WriteName(name);
|
||||
WriteDataElement(GetLengthOfByteArray<ushort>(charArray.String.Length));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write contents of a sparse array.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Array element type.</typeparam>
|
||||
/// <param name="array">A sparse array.</param>
|
||||
/// <param name="name">Name of the array.</param>
|
||||
public void WriteSparseArrayContents<T>(
|
||||
ISparseArrayOf<T> array,
|
||||
string name)
|
||||
where T : unmanaged, IEquatable<T>
|
||||
{
|
||||
(var rowsLength, var columnsLength, var dataLength, var nonZero) = PrepareSparseArrayData(array);
|
||||
WriteSparseArrayFlags();
|
||||
WriteDimensions(array.Dimensions);
|
||||
WriteName(name);
|
||||
WriteSparseArrayValues<T>(rowsLength, columnsLength, dataLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write contents of a structure array.
|
||||
/// </summary>
|
||||
/// <param name="array">A structure array.</param>
|
||||
/// <param name="name">Name of the array.</param>
|
||||
public void WriteStructureArrayContents(IStructureArray array, string name)
|
||||
{
|
||||
WriteArrayFlags();
|
||||
WriteDimensions(array.Dimensions);
|
||||
WriteName(name);
|
||||
WriteFieldNames(array.FieldNames);
|
||||
WriteStructureArrayValues(array);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write contents of a cell array.
|
||||
/// </summary>
|
||||
/// <param name="array">A cell array.</param>
|
||||
/// <param name="name">Name of the array.</param>
|
||||
public void WriteCellArrayContents(ICellArray array, string name)
|
||||
{
|
||||
WriteArrayFlags();
|
||||
WriteDimensions(array.Dimensions);
|
||||
WriteName(name);
|
||||
WriteCellArrayValues(array);
|
||||
}
|
||||
|
||||
private void WriteTag()
|
||||
{
|
||||
Position += 8;
|
||||
}
|
||||
|
||||
private void WriteShortTag()
|
||||
{
|
||||
Position += 4;
|
||||
}
|
||||
|
||||
private void WriteWrappingContents<T>(T array, Action<FakeWriter> writeContents)
|
||||
where T : IArray
|
||||
{
|
||||
if (array.IsEmpty)
|
||||
{
|
||||
WriteTag();
|
||||
return;
|
||||
}
|
||||
|
||||
WriteTag();
|
||||
writeContents(this);
|
||||
}
|
||||
|
||||
private void WriteNumericalArrayValues(IArray value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case IArrayOf<sbyte> sbyteArray:
|
||||
WriteDataElement(GetLengthOfByteArray<sbyte>(sbyteArray.Data.Length));
|
||||
break;
|
||||
case IArrayOf<byte> byteArray:
|
||||
WriteDataElement(GetLengthOfByteArray<byte>(byteArray.Data.Length));
|
||||
break;
|
||||
case IArrayOf<short> shortArray:
|
||||
WriteDataElement(GetLengthOfByteArray<short>(shortArray.Data.Length));
|
||||
break;
|
||||
case IArrayOf<ushort> ushortArray:
|
||||
WriteDataElement(GetLengthOfByteArray<ushort>(ushortArray.Data.Length));
|
||||
break;
|
||||
case IArrayOf<int> intArray:
|
||||
WriteDataElement(GetLengthOfByteArray<int>(intArray.Data.Length));
|
||||
break;
|
||||
case IArrayOf<uint> uintArray:
|
||||
WriteDataElement(GetLengthOfByteArray<uint>(uintArray.Data.Length));
|
||||
break;
|
||||
case IArrayOf<long> longArray:
|
||||
WriteDataElement(GetLengthOfByteArray<long>(longArray.Data.Length));
|
||||
break;
|
||||
case IArrayOf<ulong> ulongArray:
|
||||
WriteDataElement(GetLengthOfByteArray<ulong>(ulongArray.Data.Length));
|
||||
break;
|
||||
case IArrayOf<float> floatArray:
|
||||
WriteDataElement(GetLengthOfByteArray<float>(floatArray.Data.Length));
|
||||
break;
|
||||
case IArrayOf<double> doubleArray:
|
||||
WriteDataElement(GetLengthOfByteArray<double>(doubleArray.Data.Length));
|
||||
break;
|
||||
case IArrayOf<bool> boolArray:
|
||||
WriteDataElement(boolArray.Data.Length);
|
||||
break;
|
||||
case IArrayOf<ComplexOf<sbyte>> complexSbyteArray:
|
||||
WriteComplexValues(GetLengthOfPairOfByteArrays(complexSbyteArray.Data));
|
||||
break;
|
||||
case IArrayOf<ComplexOf<byte>> complexByteArray:
|
||||
WriteComplexValues(GetLengthOfPairOfByteArrays(complexByteArray.Data));
|
||||
break;
|
||||
case IArrayOf<ComplexOf<short>> complexShortArray:
|
||||
WriteComplexValues(GetLengthOfPairOfByteArrays(complexShortArray.Data));
|
||||
break;
|
||||
case IArrayOf<ComplexOf<ushort>> complexUshortArray:
|
||||
WriteComplexValues(GetLengthOfPairOfByteArrays(complexUshortArray.Data));
|
||||
break;
|
||||
case IArrayOf<ComplexOf<int>> complexIntArray:
|
||||
WriteComplexValues(GetLengthOfPairOfByteArrays(complexIntArray.Data));
|
||||
break;
|
||||
case IArrayOf<ComplexOf<uint>> complexUintArray:
|
||||
WriteComplexValues(GetLengthOfPairOfByteArrays(complexUintArray.Data));
|
||||
break;
|
||||
case IArrayOf<ComplexOf<long>> complexLongArray:
|
||||
WriteComplexValues(GetLengthOfPairOfByteArrays(complexLongArray.Data));
|
||||
break;
|
||||
case IArrayOf<ComplexOf<ulong>> complexUlongArray:
|
||||
WriteComplexValues(GetLengthOfPairOfByteArrays(complexUlongArray.Data));
|
||||
break;
|
||||
case IArrayOf<ComplexOf<float>> complexFloatArray:
|
||||
WriteComplexValues(GetLengthOfPairOfByteArrays(complexFloatArray.Data));
|
||||
break;
|
||||
case IArrayOf<Complex> complexDoubleArray:
|
||||
WriteComplexValues(GetLengthOfPairOfByteArrays(complexDoubleArray.Data));
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteName(string name)
|
||||
{
|
||||
var nameBytes = Encoding.ASCII.GetBytes(name);
|
||||
WriteDataElement(nameBytes.Length);
|
||||
}
|
||||
|
||||
private void WriteArrayFlags()
|
||||
{
|
||||
WriteTag();
|
||||
Position += 8;
|
||||
}
|
||||
|
||||
private void WriteDimensions(int[] dimensions)
|
||||
{
|
||||
var buffer = GetLengthOfByteArray<int>(dimensions.Length);
|
||||
WriteDataElement(buffer);
|
||||
}
|
||||
|
||||
private static unsafe int GetLengthOfByteArray<T>(int dataLength)
|
||||
where T : unmanaged
|
||||
{
|
||||
return dataLength * sizeof(T);
|
||||
}
|
||||
|
||||
private static unsafe int GetLengthOfPairOfByteArrays<T>(ComplexOf<T>[] data)
|
||||
where T : unmanaged
|
||||
{
|
||||
return data.Length * sizeof(T);
|
||||
}
|
||||
|
||||
private static unsafe int GetLengthOfPairOfByteArrays(Complex[] data)
|
||||
{
|
||||
return data.Length * sizeof(double);
|
||||
}
|
||||
|
||||
private static int CalculatePadding(int length)
|
||||
{
|
||||
var rem = length % 8;
|
||||
return rem == 0 ? 0 : 8 - rem;
|
||||
}
|
||||
|
||||
private void WriteDataElement(int dataLength)
|
||||
{
|
||||
var maybePadding = 0;
|
||||
if (dataLength > 4)
|
||||
{
|
||||
WriteTag();
|
||||
Position += dataLength;
|
||||
maybePadding = CalculatePadding(dataLength + 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteShortTag();
|
||||
Position += 4;
|
||||
}
|
||||
|
||||
Position += maybePadding;
|
||||
}
|
||||
|
||||
private void WriteComplexValues(
|
||||
int dataLength)
|
||||
{
|
||||
WriteDataElement(dataLength);
|
||||
WriteDataElement(dataLength);
|
||||
}
|
||||
|
||||
private void WriteSparseArrayValues<T>(int rowsLength, int columnsLength, int dataLength)
|
||||
where T : unmanaged
|
||||
{
|
||||
WriteDataElement(GetLengthOfByteArray<int>(rowsLength));
|
||||
WriteDataElement(GetLengthOfByteArray<int>(columnsLength));
|
||||
if (typeof(T) == typeof(double))
|
||||
{
|
||||
WriteDataElement(GetLengthOfByteArray<double>(dataLength));
|
||||
}
|
||||
else if (typeof(T) == typeof(Complex))
|
||||
{
|
||||
WriteDataElement(GetLengthOfByteArray<double>(dataLength));
|
||||
WriteDataElement(GetLengthOfByteArray<double>(dataLength));
|
||||
}
|
||||
else if (typeof(T) == typeof(bool))
|
||||
{
|
||||
WriteDataElement(dataLength);
|
||||
}
|
||||
}
|
||||
|
||||
private static (int rowIndexLength, int columnIndexLength, int dataLength, uint nonZero) PrepareSparseArrayData<T>(
|
||||
ISparseArrayOf<T> array)
|
||||
where T : struct, IEquatable<T>
|
||||
{
|
||||
var numberOfColumns = array.Dimensions[1];
|
||||
var numberOfElements = array.Data.Values.Count(value => !value.Equals(default));
|
||||
return (numberOfElements, numberOfColumns + 1, numberOfElements, (uint)numberOfElements);
|
||||
}
|
||||
|
||||
private void WriteSparseArrayFlags()
|
||||
{
|
||||
WriteTag();
|
||||
Position += 8;
|
||||
}
|
||||
|
||||
private void WriteFieldNames(IEnumerable<string> fieldNames)
|
||||
{
|
||||
var fieldNamesArray = fieldNames.Select(name => Encoding.ASCII.GetBytes(name)).ToArray();
|
||||
var maxFieldName = fieldNamesArray.Max(name => name.Length) + 1;
|
||||
WriteDataElement(GetLengthOfByteArray<int>(1));
|
||||
var buffer = new byte[fieldNamesArray.Length * maxFieldName];
|
||||
var startPosition = 0;
|
||||
foreach (var name in fieldNamesArray)
|
||||
{
|
||||
for (var i = 0; i < name.Length; i++)
|
||||
{
|
||||
buffer[startPosition + i] = name[i];
|
||||
}
|
||||
startPosition += maxFieldName;
|
||||
}
|
||||
WriteDataElement(buffer.Length);
|
||||
}
|
||||
|
||||
private void WriteStructureArrayValues(IStructureArray array)
|
||||
{
|
||||
for (var i = 0; i < array.Count; i++)
|
||||
{
|
||||
foreach (var name in array.FieldNames)
|
||||
{
|
||||
WriteArray(array[name, i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteArray(IArray array, string variableName = "", bool isGlobal = false)
|
||||
{
|
||||
switch (array)
|
||||
{
|
||||
case ICharArray charArray:
|
||||
WriteCharArray(charArray, variableName);
|
||||
break;
|
||||
case ISparseArrayOf<double> doubleSparseArray:
|
||||
WriteSparseArray(doubleSparseArray, variableName);
|
||||
break;
|
||||
case ISparseArrayOf<Complex> complexSparseArray:
|
||||
WriteSparseArray(complexSparseArray, variableName);
|
||||
break;
|
||||
case ISparseArrayOf<bool> boolSparseArray:
|
||||
WriteSparseArray(boolSparseArray, variableName);
|
||||
break;
|
||||
case ICellArray cellArray:
|
||||
WriteCellArray(cellArray, variableName);
|
||||
break;
|
||||
case IStructureArray structureArray:
|
||||
WriteStructureArray(structureArray, variableName);
|
||||
break;
|
||||
default:
|
||||
WriteNumericalArray(array, variableName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteCharArray(ICharArray charArray, string name)
|
||||
{
|
||||
WriteWrappingContents(
|
||||
charArray,
|
||||
fakeWriter => fakeWriter.WriteCharArrayContents(charArray, name));
|
||||
}
|
||||
|
||||
private void WriteSparseArray<T>(ISparseArrayOf<T> sparseArray, string name)
|
||||
where T : unmanaged, IEquatable<T>
|
||||
{
|
||||
WriteWrappingContents(
|
||||
sparseArray,
|
||||
fakeWriter => fakeWriter.WriteSparseArrayContents(sparseArray, name));
|
||||
}
|
||||
|
||||
private void WriteCellArray(ICellArray cellArray, string name)
|
||||
{
|
||||
WriteWrappingContents(
|
||||
cellArray,
|
||||
fakeWriter => fakeWriter.WriteCellArrayContents(cellArray, name));
|
||||
}
|
||||
|
||||
private void WriteCellArrayValues(ICellArray array)
|
||||
{
|
||||
for (var i = 0; i < array.Count; i++)
|
||||
{
|
||||
WriteArray(array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteStructureArray(
|
||||
IStructureArray structureArray,
|
||||
string name)
|
||||
{
|
||||
WriteWrappingContents(
|
||||
structureArray,
|
||||
fakeWriter => fakeWriter.WriteStructureArrayContents(structureArray, name));
|
||||
}
|
||||
|
||||
private void WriteNumericalArray(
|
||||
IArray numericalArray,
|
||||
string name = "")
|
||||
{
|
||||
WriteWrappingContents(
|
||||
numericalArray,
|
||||
fakeWriter => fakeWriter.WriteNumericalArrayContents(numericalArray, name));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception related to Matlab data handling.
|
||||
/// Exception related to Matlab data handling
|
||||
/// </summary>
|
||||
public class HandlerException : Exception
|
||||
{
|
||||
@ -12,9 +14,9 @@ namespace MatFileHandler
|
||||
/// </summary>
|
||||
/// <param name="message">Error message.</param>
|
||||
/// <param name="innerException">Inner exception.</param>
|
||||
public HandlerException(string message, Exception? innerException = null)
|
||||
public HandlerException(string message, Exception innerException = null)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
81
MatFileHandler/Hdf/Attribute.cs
Normal file
81
MatFileHandler/Hdf/Attribute.cs
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using HDF.PInvoke;
|
||||
|
||||
namespace MatFileHandler.Hdf
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper for HDF attribute.
|
||||
/// </summary>
|
||||
internal struct Attribute : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Attribute"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="locationId">Containing location id.</param>
|
||||
/// <param name="name">Attribute name.</param>
|
||||
public Attribute(long locationId, string name)
|
||||
{
|
||||
Id = H5A.open_by_name(locationId, ".", name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets attribute id.
|
||||
/// </summary>
|
||||
public long Id { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (Id != -1)
|
||||
{
|
||||
H5A.close(Id);
|
||||
Id = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get HDF type of the attribute.
|
||||
/// </summary>
|
||||
/// <returns>HDF type.</returns>
|
||||
public Type GetHdfType()
|
||||
{
|
||||
return new Type(H5A.get_type(Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get HDF space of the attribute.
|
||||
/// </summary>
|
||||
/// <returns>HDF space.</returns>
|
||||
public Space GetSpace()
|
||||
{
|
||||
return new Space(H5A.get_space(Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read attribute value as boolean.
|
||||
/// </summary>
|
||||
/// <returns>Attribute value.</returns>
|
||||
public bool ReadBool()
|
||||
{
|
||||
using (var h = new MemoryHandle(sizeof(int)))
|
||||
{
|
||||
H5A.read(Id, H5T.NATIVE_INT, h.Handle);
|
||||
var result = Marshal.ReadInt32(h.Handle);
|
||||
return result != 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read attribute value to the provided memory handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">Target memory handle.</param>
|
||||
/// <param name="type">HDF type to read from the attribute.</param>
|
||||
public void ReadToHandle(MemoryHandle handle, Type type)
|
||||
{
|
||||
H5A.read(Id, type.Id, handle.Handle);
|
||||
}
|
||||
}
|
||||
}
|
71
MatFileHandler/Hdf/Class.cs
Normal file
71
MatFileHandler/Hdf/Class.cs
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using HDF.PInvoke;
|
||||
|
||||
namespace MatFileHandler.Hdf
|
||||
{
|
||||
/// <summary>
|
||||
/// HDF class.
|
||||
/// </summary>
|
||||
internal struct Class : IEquatable<Class>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compound class.
|
||||
/// </summary>
|
||||
public static readonly Class Compound = new Class(H5T.class_t.COMPOUND);
|
||||
|
||||
/// <summary>
|
||||
/// Reference class.
|
||||
/// </summary>
|
||||
public static readonly Class Reference = new Class(H5T.class_t.REFERENCE);
|
||||
|
||||
/// <summary>
|
||||
/// String class.
|
||||
/// </summary>
|
||||
public static readonly Class String = new Class(H5T.class_t.STRING);
|
||||
|
||||
private readonly H5T.class_t classT;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Class"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="classT">HDF class_t.</param>
|
||||
public Class(H5T.class_t classT)
|
||||
{
|
||||
this.classT = classT;
|
||||
}
|
||||
|
||||
public static bool operator ==(Class one, Class other)
|
||||
{
|
||||
return one.Equals(other);
|
||||
}
|
||||
|
||||
public static bool operator !=(Class one, Class other)
|
||||
{
|
||||
return !one.Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the class is equal to the other class.
|
||||
/// </summary>
|
||||
/// <param name="other">Other class.</param>
|
||||
/// <returns>True iff the classes are equal.</returns>
|
||||
public bool Equals(Class other)
|
||||
{
|
||||
return classT == other.classT;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Class other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int)classT;
|
||||
}
|
||||
}
|
||||
}
|
104
MatFileHandler/Hdf/Dataset.cs
Normal file
104
MatFileHandler/Hdf/Dataset.cs
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using HDF.PInvoke;
|
||||
|
||||
namespace MatFileHandler.Hdf
|
||||
{
|
||||
/// <summary>
|
||||
/// HDF dataset.
|
||||
/// </summary>
|
||||
internal struct Dataset : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Dataset"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="datasetId">Dataset id.</param>
|
||||
public Dataset(long datasetId)
|
||||
{
|
||||
Id = datasetId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Dataset"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="groupId">Containing group id.</param>
|
||||
/// <param name="name">Name of the dataset in the group.</param>
|
||||
public Dataset(long groupId, string name)
|
||||
{
|
||||
Id = H5D.open(groupId, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets dataset id.
|
||||
/// </summary>
|
||||
public long Id { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Check if dataset attribute with the given name exists.
|
||||
/// </summary>
|
||||
/// <param name="name">Attribute name.</param>
|
||||
/// <returns>True iff dataset has an attribute with this name.</returns>
|
||||
public bool AttributeExists(string name)
|
||||
{
|
||||
return H5A.exists_by_name(Id, ".", name) != 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (Id != -1)
|
||||
{
|
||||
H5D.close(Id);
|
||||
Id = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open attribute with given name.
|
||||
/// </summary>
|
||||
/// <param name="name">Attribute name.</param>
|
||||
/// <returns>Attribute.</returns>
|
||||
public Attribute GetAttribute(string name)
|
||||
{
|
||||
return new Attribute(Id, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get HDF space of the dataset.
|
||||
/// </summary>
|
||||
/// <returns>HDF space.</returns>
|
||||
public Space GetHdfSpace()
|
||||
{
|
||||
return new Space(H5D.get_space(Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get HDF type of the dataset.
|
||||
/// </summary>
|
||||
/// <returns>HDF type.</returns>
|
||||
public Type GetHdfType()
|
||||
{
|
||||
return new Type(H5D.get_type(Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get storage size of the dataset.
|
||||
/// </summary>
|
||||
/// <returns>Storage size.</returns>
|
||||
public int GetStorageSize()
|
||||
{
|
||||
return (int)H5D.get_storage_size(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the contents of the dataset into the memory handle.
|
||||
/// </summary>
|
||||
/// <param name="type">HDF type of the data to read.</param>
|
||||
/// <param name="handle">Memory handle.</param>
|
||||
public void ReadToHandle(Type type, MemoryHandle handle)
|
||||
{
|
||||
H5D.read(Id, type.Id, H5S.ALL, H5S.ALL, H5P.DEFAULT, handle.Handle);
|
||||
}
|
||||
}
|
||||
}
|
58
MatFileHandler/Hdf/Group.cs
Normal file
58
MatFileHandler/Hdf/Group.cs
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using HDF.PInvoke;
|
||||
|
||||
namespace MatFileHandler.Hdf
|
||||
{
|
||||
/// <summary>
|
||||
/// Hdf group.
|
||||
/// </summary>
|
||||
internal struct Group : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Group"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="groupId">Containing group id.</param>
|
||||
/// <param name="name">Name of the subgroup in the containing group.</param>
|
||||
public Group(long groupId, string name)
|
||||
{
|
||||
Id = H5G.open(groupId, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets group id.
|
||||
/// </summary>
|
||||
public long Id { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Check if group attribute with the given name exists.
|
||||
/// </summary>
|
||||
/// <param name="name">Attribute name.</param>
|
||||
/// <returns>True iff group has an attribute with this name.</returns>
|
||||
public bool AttributeExists(string name)
|
||||
{
|
||||
return H5A.exists_by_name(Id, ".", name) != 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (Id != -1)
|
||||
{
|
||||
H5G.close(Id);
|
||||
Id = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get group attribute.
|
||||
/// </summary>
|
||||
/// <param name="name">Attribute name.</param>
|
||||
/// <returns>Attribute.</returns>
|
||||
public Attribute GetAttribute(string name)
|
||||
{
|
||||
return new Attribute(Id, name);
|
||||
}
|
||||
}
|
||||
}
|
80
MatFileHandler/Hdf/MatlabClass.cs
Normal file
80
MatFileHandler/Hdf/MatlabClass.cs
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
namespace MatFileHandler.Hdf
|
||||
{
|
||||
/// <summary>
|
||||
/// Matlab classes as they appear in HDF files.
|
||||
/// </summary>
|
||||
internal enum MatlabClass
|
||||
{
|
||||
/// <summary>
|
||||
/// Empty array.
|
||||
/// </summary>
|
||||
MEmpty,
|
||||
|
||||
/// <summary>
|
||||
/// Char array.
|
||||
/// </summary>
|
||||
MChar,
|
||||
|
||||
/// <summary>
|
||||
/// Int8 array.
|
||||
/// </summary>
|
||||
MInt8,
|
||||
|
||||
/// <summary>
|
||||
/// UInt8 array.
|
||||
/// </summary>
|
||||
MUInt8,
|
||||
|
||||
/// <summary>
|
||||
/// Int16 array.
|
||||
/// </summary>
|
||||
MInt16,
|
||||
|
||||
/// <summary>
|
||||
/// UInt16 array.
|
||||
/// </summary>
|
||||
MUInt16,
|
||||
|
||||
/// <summary>
|
||||
/// Int32 array.
|
||||
/// </summary>
|
||||
MInt32,
|
||||
|
||||
/// <summary>
|
||||
/// UInt32 array.
|
||||
/// </summary>
|
||||
MUInt32,
|
||||
|
||||
/// <summary>
|
||||
/// Int64 array.
|
||||
/// </summary>
|
||||
MInt64,
|
||||
|
||||
/// <summary>
|
||||
/// UInt64 array.
|
||||
/// </summary>
|
||||
MUInt64,
|
||||
|
||||
/// <summary>
|
||||
/// Single-precision floating point array.
|
||||
/// </summary>
|
||||
MSingle,
|
||||
|
||||
/// <summary>
|
||||
/// Double-precision floating point array.
|
||||
/// </summary>
|
||||
MDouble,
|
||||
|
||||
/// <summary>
|
||||
/// Cell array.
|
||||
/// </summary>
|
||||
MCell,
|
||||
|
||||
/// <summary>
|
||||
/// Logical array.
|
||||
/// </summary>
|
||||
MLogical,
|
||||
}
|
||||
}
|
37
MatFileHandler/Hdf/MemoryHandle.cs
Normal file
37
MatFileHandler/Hdf/MemoryHandle.cs
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MatFileHandler.Hdf
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper around IntPtr to array in unmanaged memory.
|
||||
/// </summary>
|
||||
internal sealed class MemoryHandle : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MemoryHandle"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sizeInBytes">Size of the memory to be allocated.</param>
|
||||
internal MemoryHandle(int sizeInBytes)
|
||||
{
|
||||
Handle = Marshal.AllocHGlobal(sizeInBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets wrapped IntPtr.
|
||||
/// </summary>
|
||||
internal IntPtr Handle { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
if (Handle != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(Handle);
|
||||
Handle = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
85
MatFileHandler/Hdf/ReferenceArray.cs
Normal file
85
MatFileHandler/Hdf/ReferenceArray.cs
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using HDF.PInvoke;
|
||||
|
||||
namespace MatFileHandler.Hdf
|
||||
{
|
||||
/// <summary>
|
||||
/// Array of HDF references stored in an HDF dataset.
|
||||
/// </summary>
|
||||
internal struct ReferenceArray : IDisposable, IEnumerable<Dataset>
|
||||
{
|
||||
private readonly Dataset[] references;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReferenceArray"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="dataset">Containing dataset.</param>
|
||||
/// <param name="size">Array size.</param>
|
||||
public ReferenceArray(Dataset dataset, int size)
|
||||
{
|
||||
Dataset = dataset;
|
||||
Size = size;
|
||||
Buf = new MemoryHandle(Marshal.SizeOf(default(IntPtr)) * size);
|
||||
Dataset.ReadToHandle(Type.Reference, Buf);
|
||||
references = new Dataset[size];
|
||||
for (var i = 0; i < size; i++)
|
||||
{
|
||||
references[i] =
|
||||
new Dataset(H5R.dereference(
|
||||
dataset.Id,
|
||||
H5P.DEFAULT,
|
||||
H5R.type_t.OBJECT,
|
||||
Buf.Handle + (i * Marshal.SizeOf(default(IntPtr)))));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets containing dataset.
|
||||
/// </summary>
|
||||
public Dataset Dataset { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets references.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Dataset> References => references;
|
||||
|
||||
/// <summary>
|
||||
/// Gets array size.
|
||||
/// </summary>
|
||||
public int Size { get; }
|
||||
|
||||
private MemoryHandle Buf { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Buf?.Dispose();
|
||||
if (References is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var reference in References)
|
||||
{
|
||||
reference.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<Dataset> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<Dataset>)References).GetEnumerator();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return References.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
48
MatFileHandler/Hdf/Space.cs
Normal file
48
MatFileHandler/Hdf/Space.cs
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System.Linq;
|
||||
using HDF.PInvoke;
|
||||
|
||||
namespace MatFileHandler.Hdf
|
||||
{
|
||||
/// <summary>
|
||||
/// HDF space.
|
||||
/// </summary>
|
||||
internal struct Space
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Space"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="id">Space id.</param>
|
||||
public Space(long id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets space id.
|
||||
/// </summary>
|
||||
public long Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get space rank.
|
||||
/// </summary>
|
||||
/// <returns>Space rank.</returns>
|
||||
public int GetRank()
|
||||
{
|
||||
return H5S.get_simple_extent_ndims(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get dimensions of the space.
|
||||
/// </summary>
|
||||
/// <returns>Space dimensions.</returns>
|
||||
public int[] GetDimensions()
|
||||
{
|
||||
var dims = new ulong[GetRank()];
|
||||
H5S.get_simple_extent_dims(Id, dims, null);
|
||||
System.Array.Reverse(dims);
|
||||
return dims.Select(x => (int)x).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
147
MatFileHandler/Hdf/Type.cs
Normal file
147
MatFileHandler/Hdf/Type.cs
Normal file
@ -0,0 +1,147 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using HDF.PInvoke;
|
||||
|
||||
namespace MatFileHandler.Hdf
|
||||
{
|
||||
/// <summary>
|
||||
/// HDF type.
|
||||
/// </summary>
|
||||
internal struct Type
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Type"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="id">Type id.</param>
|
||||
public Type(long id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF string type.
|
||||
/// </summary>
|
||||
public static Type CS1 => new Type(H5T.C_S1);
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF double type.
|
||||
/// </summary>
|
||||
public static Type NativeDouble => new Type(H5T.NATIVE_DOUBLE);
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF float (single) type.
|
||||
/// </summary>
|
||||
public static Type NativeFloat => new Type(H5T.NATIVE_FLOAT);
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF int type.
|
||||
/// </summary>
|
||||
public static Type NativeInt => new Type(H5T.NATIVE_INT);
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF int16 type.
|
||||
/// </summary>
|
||||
public static Type NativeInt16 => new Type(H5T.NATIVE_INT16);
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF int32 type.
|
||||
/// </summary>
|
||||
public static Type NativeInt32 => new Type(H5T.NATIVE_INT32);
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF int64 type.
|
||||
/// </summary>
|
||||
public static Type NativeInt64 => new Type(H5T.NATIVE_INT64);
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF int8 type.
|
||||
/// </summary>
|
||||
public static Type NativeInt8 => new Type(H5T.NATIVE_INT8);
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF uint type.
|
||||
/// </summary>
|
||||
public static Type NativeUInt => new Type(H5T.NATIVE_UINT);
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF uint16 type
|
||||
/// </summary>
|
||||
public static Type NativeUInt16 => new Type(H5T.NATIVE_UINT16);
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF uint32 type.
|
||||
/// </summary>
|
||||
public static Type NativeUInt32 => new Type(H5T.NATIVE_UINT32);
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF uint64 type.
|
||||
/// </summary>
|
||||
public static Type NativeUInt64 => new Type(H5T.NATIVE_UINT64);
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF uint8 type.
|
||||
/// </summary>
|
||||
public static Type NativeUInt8 => new Type(H5T.NATIVE_UINT8);
|
||||
|
||||
/// <summary>
|
||||
/// Gets HDF reference type.
|
||||
/// </summary>
|
||||
public static Type Reference => new Type(H5T.STD_REF_OBJ);
|
||||
|
||||
/// <summary>
|
||||
/// Gets type id.
|
||||
/// </summary>
|
||||
public long Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create compound type of given size.
|
||||
/// </summary>
|
||||
/// <param name="size">Size of the type.</param>
|
||||
/// <returns>The created type.</returns>
|
||||
public static Type CreateCompound(int size)
|
||||
{
|
||||
return new Type(H5T.create(H5T.class_t.COMPOUND, (IntPtr)size));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get class of the type.
|
||||
/// </summary>
|
||||
/// <returns>Class of the type.</returns>
|
||||
public Class GetClass()
|
||||
{
|
||||
return new Class(H5T.get_class(Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get size of the type.
|
||||
/// </summary>
|
||||
/// <returns>Size of the type.</returns>
|
||||
public int GetSize()
|
||||
{
|
||||
return (int)H5T.get_size(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert a field into the type.
|
||||
/// </summary>
|
||||
/// <param name="name">Field name.</param>
|
||||
/// <param name="fieldType">Field type.</param>
|
||||
public void InsertField(string name, Type fieldType)
|
||||
{
|
||||
H5T.insert(Id, name, IntPtr.Zero, fieldType.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create type copy with same class and given size.
|
||||
/// </summary>
|
||||
/// <param name="size">New size.</param>
|
||||
/// <returns>New type.</returns>
|
||||
public Type WithSize(int size)
|
||||
{
|
||||
var classId = H5T.copy(Id);
|
||||
H5T.set_size(classId, (IntPtr)size);
|
||||
return new Type(classId);
|
||||
}
|
||||
}
|
||||
}
|
667
MatFileHandler/HdfFileReader.cs
Normal file
667
MatFileHandler/HdfFileReader.cs
Normal file
@ -0,0 +1,667 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using HDF.PInvoke;
|
||||
using MatFileHandler.Hdf;
|
||||
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Reader of HDF files containing MATLAB data.
|
||||
/// </summary>
|
||||
internal class HdfFileReader
|
||||
{
|
||||
private const string ClassAttributeName = "MATLAB_class";
|
||||
|
||||
private const string GlobalAttributeName = "MATLAB_global";
|
||||
|
||||
private const string SparseAttributeName = "MATLAB_sparse";
|
||||
|
||||
private readonly long fileId;
|
||||
|
||||
private List<IVariable> variables;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HdfFileReader"/> class.
|
||||
/// </summary>
|
||||
/// <param name="fileId">File id to read data from.</param>
|
||||
internal HdfFileReader(long fileId)
|
||||
{
|
||||
this.fileId = fileId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read MATLAB data from the HDF file.
|
||||
/// </summary>
|
||||
/// <returns>MATLAB data file contents.</returns>
|
||||
internal IMatFile Read()
|
||||
{
|
||||
variables = new List<IVariable>();
|
||||
var group_info = default(H5G.info_t);
|
||||
H5G.get_info(fileId, ref group_info);
|
||||
var numberOfVariables = group_info.nlinks;
|
||||
ulong idx = 0;
|
||||
while (idx < numberOfVariables)
|
||||
{
|
||||
H5L.iterate(
|
||||
fileId,
|
||||
H5.index_t.NAME,
|
||||
H5.iter_order_t.NATIVE,
|
||||
ref idx,
|
||||
VariableIterator,
|
||||
IntPtr.Zero);
|
||||
}
|
||||
|
||||
return new MatFile(variables);
|
||||
}
|
||||
|
||||
private static MatlabClass ArrayTypeFromMatlabClassName(string matlabClassName)
|
||||
{
|
||||
switch (matlabClassName)
|
||||
{
|
||||
case "canonical empty":
|
||||
return MatlabClass.MEmpty;
|
||||
case "logical":
|
||||
return MatlabClass.MLogical;
|
||||
case "char":
|
||||
return MatlabClass.MChar;
|
||||
case "int8":
|
||||
return MatlabClass.MInt8;
|
||||
case "uint8":
|
||||
return MatlabClass.MUInt8;
|
||||
case "int16":
|
||||
return MatlabClass.MInt16;
|
||||
case "uint16":
|
||||
return MatlabClass.MUInt16;
|
||||
case "int32":
|
||||
return MatlabClass.MInt32;
|
||||
case "uint32":
|
||||
return MatlabClass.MUInt32;
|
||||
case "int64":
|
||||
return MatlabClass.MInt64;
|
||||
case "uint64":
|
||||
return MatlabClass.MUInt64;
|
||||
case "single":
|
||||
return MatlabClass.MSingle;
|
||||
case "double":
|
||||
return MatlabClass.MDouble;
|
||||
case "cell":
|
||||
return MatlabClass.MCell;
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static T[] ConvertDataToProperType<T>(byte[] bytes, MatlabClass arrayType)
|
||||
where T : struct
|
||||
{
|
||||
var length = bytes.Length;
|
||||
var arrayElementSize = SizeOfArrayElement(arrayType);
|
||||
var data = new T[length / arrayElementSize];
|
||||
Buffer.BlockCopy(bytes, 0, data, 0, length);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static int[] GetDimensionsOfDataset(Dataset dataset)
|
||||
{
|
||||
return dataset.GetHdfSpace().GetDimensions();
|
||||
}
|
||||
|
||||
private static string GetMatlabClassFromAttribute(Hdf.Attribute attribute)
|
||||
{
|
||||
var type = attribute.GetHdfType();
|
||||
var cl = type.GetClass();
|
||||
if (cl != Class.String)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
var typeIdSize = type.GetSize();
|
||||
var copiedType = Hdf.Type.CS1.WithSize(type.GetSize());
|
||||
var matlabClassNameBytes = new byte[typeIdSize];
|
||||
using (var buf = new MemoryHandle(typeIdSize))
|
||||
{
|
||||
attribute.ReadToHandle(buf, copiedType);
|
||||
Marshal.Copy(buf.Handle, matlabClassNameBytes, 0, typeIdSize);
|
||||
}
|
||||
|
||||
var length = typeIdSize;
|
||||
for (var i = 0; i < typeIdSize; i++)
|
||||
{
|
||||
if (matlabClassNameBytes[i] == 0)
|
||||
{
|
||||
length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Encoding.ASCII.GetString(matlabClassNameBytes, 0, length);
|
||||
}
|
||||
|
||||
private static string GetMatlabClassOfDataset(Dataset dataset)
|
||||
{
|
||||
using (var attribute = dataset.GetAttribute(ClassAttributeName))
|
||||
{
|
||||
return GetMatlabClassFromAttribute(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetMatlabClassOfGroup(Group group)
|
||||
{
|
||||
using (var attribute = group.GetAttribute(ClassAttributeName))
|
||||
{
|
||||
return GetMatlabClassFromAttribute(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
private static H5O.type_t GetObjectType(long groupId, string fieldName)
|
||||
{
|
||||
var objectInfo = default(H5O.info_t);
|
||||
H5O.get_info_by_name(groupId, fieldName, ref objectInfo);
|
||||
return objectInfo.type;
|
||||
}
|
||||
|
||||
private static Hdf.Type H5tTypeFromHdfMatlabClass(MatlabClass arrayType)
|
||||
{
|
||||
switch (arrayType)
|
||||
{
|
||||
case MatlabClass.MInt8:
|
||||
return Hdf.Type.NativeInt8;
|
||||
case MatlabClass.MUInt8:
|
||||
case MatlabClass.MLogical:
|
||||
return Hdf.Type.NativeUInt8;
|
||||
case MatlabClass.MInt16:
|
||||
return Hdf.Type.NativeInt16;
|
||||
case MatlabClass.MUInt16:
|
||||
return Hdf.Type.NativeUInt16;
|
||||
case MatlabClass.MInt32:
|
||||
return Hdf.Type.NativeInt32;
|
||||
case MatlabClass.MUInt32:
|
||||
return Hdf.Type.NativeUInt32;
|
||||
case MatlabClass.MInt64:
|
||||
return Hdf.Type.NativeInt64;
|
||||
case MatlabClass.MUInt64:
|
||||
return Hdf.Type.NativeUInt64;
|
||||
case MatlabClass.MSingle:
|
||||
return Hdf.Type.NativeFloat;
|
||||
case MatlabClass.MDouble:
|
||||
return Hdf.Type.NativeDouble;
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static IArray ReadCellArray(Dataset dataset, int[] dims)
|
||||
{
|
||||
var numberOfElements = dims.NumberOfElements();
|
||||
var elements = new IArray[numberOfElements];
|
||||
using (var array = new ReferenceArray(dataset, numberOfElements))
|
||||
{
|
||||
var i = 0;
|
||||
foreach (var reference in array)
|
||||
{
|
||||
elements[i++] = ReadDataset(reference);
|
||||
}
|
||||
}
|
||||
|
||||
return new MatCellArray(dims, elements);
|
||||
}
|
||||
|
||||
private static IArray ReadCharArray(Dataset dataset, int[] dims)
|
||||
{
|
||||
var storageSize = dataset.GetStorageSize();
|
||||
var data = ReadDataset(dataset, Hdf.Type.NativeUInt16, storageSize);
|
||||
var uInt16Data = new ushort[data.Length / sizeof(ushort)];
|
||||
Buffer.BlockCopy(data, 0, uInt16Data, 0, data.Length);
|
||||
var str = Encoding.Unicode.GetString(data);
|
||||
return new MatCharArrayOf<ushort>(dims, uInt16Data, str);
|
||||
}
|
||||
|
||||
private static (T[] real, T[] imaginary) ReadComplexData<T>(
|
||||
Dataset dataset,
|
||||
int dataSize,
|
||||
MatlabClass arrayType)
|
||||
where T : struct
|
||||
{
|
||||
var h5Type = H5tTypeFromHdfMatlabClass(arrayType);
|
||||
var h5Size = h5Type.GetSize();
|
||||
var h5tComplexReal = Hdf.Type.CreateCompound(h5Size);
|
||||
h5tComplexReal.InsertField("real", h5Type);
|
||||
var realData = ReadDataset(dataset, h5tComplexReal, dataSize);
|
||||
var h5tComplexImaginary = Hdf.Type.CreateCompound(h5Size);
|
||||
h5tComplexImaginary.InsertField("imag", h5Type);
|
||||
var imaginaryData = ReadDataset(dataset, h5tComplexImaginary, dataSize);
|
||||
var convertedRealData = ConvertDataToProperType<T>(realData, arrayType);
|
||||
var convertedImaginaryData = ConvertDataToProperType<T>(imaginaryData, arrayType);
|
||||
return (convertedRealData, convertedImaginaryData);
|
||||
}
|
||||
|
||||
private static IArray ReadDataset(Dataset dataset)
|
||||
{
|
||||
var dims = GetDimensionsOfDataset(dataset);
|
||||
|
||||
var matlabClass = GetMatlabClassOfDataset(dataset);
|
||||
var arrayType = ArrayTypeFromMatlabClassName(matlabClass);
|
||||
|
||||
switch (arrayType)
|
||||
{
|
||||
case MatlabClass.MEmpty:
|
||||
return MatArray.Empty();
|
||||
case MatlabClass.MLogical:
|
||||
return ReadNumericalArray<bool>(dataset, dims, arrayType);
|
||||
case MatlabClass.MChar:
|
||||
return ReadCharArray(dataset, dims);
|
||||
case MatlabClass.MInt8:
|
||||
return ReadNumericalArray<sbyte>(dataset, dims, arrayType);
|
||||
case MatlabClass.MUInt8:
|
||||
return ReadNumericalArray<byte>(dataset, dims, arrayType);
|
||||
case MatlabClass.MInt16:
|
||||
return ReadNumericalArray<short>(dataset, dims, arrayType);
|
||||
case MatlabClass.MUInt16:
|
||||
return ReadNumericalArray<ushort>(dataset, dims, arrayType);
|
||||
case MatlabClass.MInt32:
|
||||
return ReadNumericalArray<int>(dataset, dims, arrayType);
|
||||
case MatlabClass.MUInt32:
|
||||
return ReadNumericalArray<uint>(dataset, dims, arrayType);
|
||||
case MatlabClass.MInt64:
|
||||
return ReadNumericalArray<long>(dataset, dims, arrayType);
|
||||
case MatlabClass.MUInt64:
|
||||
return ReadNumericalArray<ulong>(dataset, dims, arrayType);
|
||||
case MatlabClass.MSingle:
|
||||
return ReadNumericalArray<float>(dataset, dims, arrayType);
|
||||
case MatlabClass.MDouble:
|
||||
return ReadNumericalArray<double>(dataset, dims, arrayType);
|
||||
case MatlabClass.MCell:
|
||||
return ReadCellArray(dataset, dims);
|
||||
}
|
||||
|
||||
throw new NotImplementedException($"Unknown array type: {arrayType}.");
|
||||
}
|
||||
|
||||
private static byte[] ReadDataset(Dataset dataset, Hdf.Type elementType, int dataSize)
|
||||
{
|
||||
var data = new byte[dataSize];
|
||||
using (var dataBuffer = new MemoryHandle(dataSize))
|
||||
{
|
||||
dataset.ReadToHandle(elementType, dataBuffer);
|
||||
Marshal.Copy(dataBuffer.Handle, data, 0, dataSize);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static string[] ReadFieldNames(long groupId)
|
||||
{
|
||||
// Try to read fields from MATLAB_fields.
|
||||
using (var attr = new Hdf.Attribute(groupId, "MATLAB_fields"))
|
||||
{
|
||||
if (attr.Id == 0)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
var dimensions = attr.GetSpace().GetDimensions();
|
||||
var numberOfFields = dimensions.NumberOfElements();
|
||||
|
||||
var fieldType = attr.GetHdfType();
|
||||
|
||||
var fieldNamePointersSizeInBytes = numberOfFields * Marshal.SizeOf(default(H5T.hvl_t));
|
||||
var fieldNamePointers = new IntPtr[numberOfFields * 2];
|
||||
using (var fieldNamesBuf = new MemoryHandle(fieldNamePointersSizeInBytes))
|
||||
{
|
||||
attr.ReadToHandle(fieldNamesBuf, fieldType);
|
||||
Marshal.Copy(fieldNamesBuf.Handle, fieldNamePointers, 0, numberOfFields * 2);
|
||||
}
|
||||
|
||||
var fieldNames = new string[numberOfFields];
|
||||
for (var i = 0; i < numberOfFields; i++)
|
||||
{
|
||||
var stringLength = fieldNamePointers[i * 2];
|
||||
var stringPointer = fieldNamePointers[(i * 2) + 1];
|
||||
fieldNames[i] = Marshal.PtrToStringAnsi(stringPointer, (int)stringLength);
|
||||
}
|
||||
|
||||
return fieldNames;
|
||||
}
|
||||
}
|
||||
|
||||
private static IArray ReadGroup(Group group)
|
||||
{
|
||||
var matlabClass = GetMatlabClassOfGroup(group);
|
||||
if (matlabClass == "struct")
|
||||
{
|
||||
return ReadStruct(group.Id);
|
||||
}
|
||||
|
||||
if (group.AttributeExists(SparseAttributeName))
|
||||
{
|
||||
var arrayType = ArrayTypeFromMatlabClassName(matlabClass);
|
||||
|
||||
switch (arrayType)
|
||||
{
|
||||
case MatlabClass.MEmpty:
|
||||
return MatArray.Empty();
|
||||
case MatlabClass.MLogical:
|
||||
return ReadSparseArray<bool>(group.Id, arrayType);
|
||||
case MatlabClass.MInt8:
|
||||
return ReadSparseArray<sbyte>(group.Id, arrayType);
|
||||
case MatlabClass.MUInt8:
|
||||
return ReadSparseArray<byte>(group.Id, arrayType);
|
||||
case MatlabClass.MInt16:
|
||||
return ReadSparseArray<short>(group.Id, arrayType);
|
||||
case MatlabClass.MUInt16:
|
||||
return ReadSparseArray<ushort>(group.Id, arrayType);
|
||||
case MatlabClass.MInt32:
|
||||
return ReadSparseArray<int>(group.Id, arrayType);
|
||||
case MatlabClass.MUInt32:
|
||||
return ReadSparseArray<uint>(group.Id, arrayType);
|
||||
case MatlabClass.MInt64:
|
||||
return ReadSparseArray<long>(group.Id, arrayType);
|
||||
case MatlabClass.MUInt64:
|
||||
return ReadSparseArray<ulong>(group.Id, arrayType);
|
||||
case MatlabClass.MSingle:
|
||||
return ReadSparseArray<float>(group.Id, arrayType);
|
||||
case MatlabClass.MDouble:
|
||||
return ReadSparseArray<double>(group.Id, arrayType);
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static IEnumerable<ComplexOf<T>> CombineComplexOfData<T>(
|
||||
IEnumerable<T> realData,
|
||||
IEnumerable<T> imaginaryData)
|
||||
where T : struct
|
||||
{
|
||||
return realData.Zip(
|
||||
imaginaryData,
|
||||
(x, y) => new ComplexOf<T>(x, y));
|
||||
}
|
||||
|
||||
private static IEnumerable<Complex> CombineComplexData(
|
||||
IEnumerable<double> realData,
|
||||
IEnumerable<double> imaginaryData)
|
||||
{
|
||||
return realData.Zip(
|
||||
imaginaryData,
|
||||
(x, y) => new Complex(x, y));
|
||||
}
|
||||
|
||||
private static IArray ReadNumericalArray<T>(Dataset dataset, int[] dims, MatlabClass arrayType)
|
||||
where T : struct
|
||||
{
|
||||
var numberOfElements = dims.NumberOfElements();
|
||||
var dataSize = numberOfElements * SizeOfArrayElement(arrayType);
|
||||
var dataSetType = dataset.GetHdfType();
|
||||
var dataSetTypeClass = dataSetType.GetClass();
|
||||
var isCompound = dataSetTypeClass == Class.Compound;
|
||||
if (isCompound)
|
||||
{
|
||||
var (convertedRealData, convertedImaginaryData) = ReadComplexData<T>(dataset, dataSize, arrayType);
|
||||
if (arrayType == MatlabClass.MDouble)
|
||||
{
|
||||
var complexData =
|
||||
CombineComplexData(
|
||||
convertedRealData as double[],
|
||||
convertedImaginaryData as double[])
|
||||
.ToArray();
|
||||
return new MatNumericalArrayOf<Complex>(dims, complexData);
|
||||
}
|
||||
else
|
||||
{
|
||||
var complexData =
|
||||
CombineComplexOfData(
|
||||
convertedRealData,
|
||||
convertedImaginaryData)
|
||||
.ToArray();
|
||||
return new MatNumericalArrayOf<ComplexOf<T>>(dims, complexData);
|
||||
}
|
||||
}
|
||||
|
||||
var data = ReadDataset(dataset, H5tTypeFromHdfMatlabClass(arrayType), dataSize);
|
||||
var convertedData = ConvertDataToProperType<T>(data, arrayType);
|
||||
return new MatNumericalArrayOf<T>(dims, convertedData);
|
||||
}
|
||||
|
||||
private static IArray ReadSparseArray<T>(long groupId, MatlabClass arrayType)
|
||||
where T : struct
|
||||
{
|
||||
using (var sparseAttribute = new Hdf.Attribute(groupId, SparseAttributeName))
|
||||
{
|
||||
using (var numberOfRowsHandle = new MemoryHandle(sizeof(uint)))
|
||||
{
|
||||
sparseAttribute.ReadToHandle(numberOfRowsHandle, Hdf.Type.NativeUInt);
|
||||
var numberOfRows = Marshal.ReadInt32(numberOfRowsHandle.Handle);
|
||||
int[] rowIndex;
|
||||
int[] columnIndex;
|
||||
using (var irData = new Dataset(groupId, "ir"))
|
||||
{
|
||||
var ds = GetDimensionsOfDataset(irData);
|
||||
var numberOfIr = ds.NumberOfElements();
|
||||
var irBytes = ReadDataset(irData, Hdf.Type.NativeInt, numberOfIr * sizeof(int));
|
||||
rowIndex = new int[numberOfIr];
|
||||
Buffer.BlockCopy(irBytes, 0, rowIndex, 0, irBytes.Length);
|
||||
}
|
||||
|
||||
using (var jcData = new Dataset(groupId, "jc"))
|
||||
{
|
||||
var ds = GetDimensionsOfDataset(jcData);
|
||||
var numberOfJc = ds.NumberOfElements();
|
||||
var jcBytes = ReadDataset(jcData, Hdf.Type.NativeInt, numberOfJc * sizeof(int));
|
||||
columnIndex = new int[numberOfJc];
|
||||
Buffer.BlockCopy(jcBytes, 0, columnIndex, 0, jcBytes.Length);
|
||||
}
|
||||
|
||||
using (var data = new Dataset(groupId, "data"))
|
||||
{
|
||||
var ds = GetDimensionsOfDataset(data);
|
||||
var dims = new int[2];
|
||||
dims[0] = numberOfRows;
|
||||
dims[1] = columnIndex.Length - 1;
|
||||
var dataSize = ds.NumberOfElements() * SizeOfArrayElement(arrayType);
|
||||
var storageSize = data.GetStorageSize();
|
||||
var dataSetType = data.GetHdfType();
|
||||
var dataSetTypeClass = dataSetType.GetClass();
|
||||
var isCompound = dataSetTypeClass == Class.Compound;
|
||||
if (isCompound)
|
||||
{
|
||||
var (convertedRealData, convertedImaginaryData) =
|
||||
ReadComplexData<T>(data, dataSize, arrayType);
|
||||
if (arrayType == MatlabClass.MDouble)
|
||||
{
|
||||
var complexData =
|
||||
CombineComplexData(
|
||||
convertedRealData as double[],
|
||||
convertedImaginaryData as double[])
|
||||
.ToArray();
|
||||
var complexDataDictionary =
|
||||
DataExtraction.ConvertMatlabSparseToDictionary(
|
||||
rowIndex,
|
||||
columnIndex,
|
||||
j => complexData[j]);
|
||||
return new MatSparseArrayOf<Complex>(dims, complexDataDictionary);
|
||||
}
|
||||
else
|
||||
{
|
||||
var complexData =
|
||||
CombineComplexOfData<T>(
|
||||
convertedRealData,
|
||||
convertedImaginaryData)
|
||||
.ToArray();
|
||||
var complexDataDictionary =
|
||||
DataExtraction.ConvertMatlabSparseToDictionary(
|
||||
rowIndex,
|
||||
columnIndex,
|
||||
j => complexData[j]);
|
||||
return new MatSparseArrayOf<ComplexOf<T>>(dims, complexDataDictionary);
|
||||
}
|
||||
}
|
||||
|
||||
var d = ReadDataset(data, H5tTypeFromHdfMatlabClass(arrayType), dataSize);
|
||||
var elements = ConvertDataToProperType<T>(d, arrayType);
|
||||
var dataDictionary =
|
||||
DataExtraction.ConvertMatlabSparseToDictionary(rowIndex, columnIndex, j => elements[j]);
|
||||
return new MatSparseArrayOf<T>(dims, dataDictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IArray ReadStruct(long groupId)
|
||||
{
|
||||
var fieldNames = ReadFieldNames(groupId);
|
||||
var firstObjectType = GetObjectType(groupId, fieldNames[0]);
|
||||
if (firstObjectType == H5O.type_t.DATASET)
|
||||
{
|
||||
using (var firstField = new Dataset(groupId, fieldNames[0]))
|
||||
{
|
||||
var firstFieldType = firstField.GetHdfType();
|
||||
if (firstFieldType.GetClass() == Class.Reference)
|
||||
{
|
||||
if (firstField.AttributeExists(ClassAttributeName))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
else
|
||||
{
|
||||
var dimensions = GetDimensionsOfDataset(firstField);
|
||||
var numberOfElements = dimensions.NumberOfElements();
|
||||
var dictionary = new Dictionary<string, List<IArray>>();
|
||||
foreach (var fieldName in fieldNames)
|
||||
{
|
||||
var fieldType = GetObjectType(groupId, fieldName);
|
||||
dictionary[fieldName] = new List<IArray>();
|
||||
switch (fieldType)
|
||||
{
|
||||
case H5O.type_t.DATASET:
|
||||
using (var field = new Dataset(groupId, fieldName))
|
||||
{
|
||||
using (var array = new ReferenceArray(field, numberOfElements))
|
||||
{
|
||||
foreach (var reference in array)
|
||||
{
|
||||
var value = ReadDataset(reference);
|
||||
dictionary[fieldName].Add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
return new MatStructureArray(dimensions, dictionary);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static int SizeOfArrayElement(MatlabClass arrayType)
|
||||
{
|
||||
switch (arrayType)
|
||||
{
|
||||
case MatlabClass.MInt8:
|
||||
case MatlabClass.MUInt8:
|
||||
case MatlabClass.MLogical:
|
||||
return 1;
|
||||
case MatlabClass.MInt16:
|
||||
case MatlabClass.MUInt16:
|
||||
return 2;
|
||||
case MatlabClass.MInt32:
|
||||
case MatlabClass.MUInt32:
|
||||
case MatlabClass.MSingle:
|
||||
return 4;
|
||||
case MatlabClass.MInt64:
|
||||
case MatlabClass.MUInt64:
|
||||
case MatlabClass.MDouble:
|
||||
return 8;
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private bool ReadGlobalFlag(Group group)
|
||||
{
|
||||
if (!group.AttributeExists(GlobalAttributeName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using (var globalAttribute = group.GetAttribute(GlobalAttributeName))
|
||||
{
|
||||
return globalAttribute.ReadBool();
|
||||
}
|
||||
}
|
||||
|
||||
private bool ReadGlobalFlag(Dataset dataset)
|
||||
{
|
||||
if (!dataset.AttributeExists(GlobalAttributeName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using (var globalAttribute = dataset.GetAttribute(GlobalAttributeName))
|
||||
{
|
||||
return globalAttribute.ReadBool();
|
||||
}
|
||||
}
|
||||
|
||||
private int VariableIterator(long group, IntPtr name, ref H5L.info_t info, IntPtr op_data)
|
||||
{
|
||||
var variableName = Marshal.PtrToStringAnsi(name);
|
||||
var object_info = default(H5O.info_t);
|
||||
H5O.get_info_by_name(group, variableName, ref object_info);
|
||||
switch (object_info.type)
|
||||
{
|
||||
case H5O.type_t.DATASET:
|
||||
using (var dataset = new Dataset(group, variableName))
|
||||
{
|
||||
var isGlobal = ReadGlobalFlag(dataset);
|
||||
var value = ReadDataset(dataset);
|
||||
variables.Add(new MatVariable(value, variableName, isGlobal));
|
||||
}
|
||||
|
||||
break;
|
||||
case H5O.type_t.GROUP:
|
||||
if (variableName == "#refs#")
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
using (var subGroup = new Group(group, variableName))
|
||||
{
|
||||
var isGlobal = ReadGlobalFlag(subGroup);
|
||||
var groupValue = ReadGroup(subGroup);
|
||||
variables.Add(new MatVariable(groupValue, variableName, isGlobal));
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if !NET461
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace MatFileHandler
|
||||
{
|
||||
@ -13,13 +13,19 @@ namespace MatFileHandler
|
||||
/// </summary>
|
||||
internal class Header
|
||||
{
|
||||
private Header(string text, long subsystemDataOffset, int version)
|
||||
private Header(byte[] rawBytes, string text, long subsystemDataOffset, int version)
|
||||
{
|
||||
RawBytes = rawBytes;
|
||||
Text = text;
|
||||
SubsystemDataOffset = subsystemDataOffset;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets raw byte contents of the header.
|
||||
/// </summary>
|
||||
public byte[] RawBytes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header text.
|
||||
/// </summary>
|
||||
@ -55,7 +61,7 @@ namespace MatFileHandler
|
||||
platform = platform.Remove(length);
|
||||
}
|
||||
var text = $"MATLAB 5.0 MAT-file, Platform: {platform}, Created on: {dateTime}{padding}";
|
||||
return new Header(text, 0, 256);
|
||||
return new Header(null, text, 0, 256);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -65,16 +71,26 @@ namespace MatFileHandler
|
||||
/// <returns>The header read.</returns>
|
||||
public static Header Read(BinaryReader reader)
|
||||
{
|
||||
var textBytes = reader.ReadBytes(116);
|
||||
var text = System.Text.Encoding.UTF8.GetString(textBytes);
|
||||
var subsystemDataOffsetBytes = reader.ReadBytes(8);
|
||||
var subsystemDataOffset = BitConverter.ToInt64(subsystemDataOffsetBytes, 0);
|
||||
var version = reader.ReadInt16();
|
||||
var endian = reader.ReadInt16();
|
||||
var isLittleEndian = endian == 19785;
|
||||
return isLittleEndian
|
||||
? new Header(text, subsystemDataOffset, version)
|
||||
: throw new NotSupportedException("Big-endian files are not supported.");
|
||||
var rawBytes = reader.ReadBytes(128);
|
||||
using (var stream = new MemoryStream(rawBytes))
|
||||
{
|
||||
using (var newReader = new BinaryReader(stream))
|
||||
{
|
||||
var textBytes = newReader.ReadBytes(116);
|
||||
var text = System.Text.Encoding.UTF8.GetString(textBytes);
|
||||
var subsystemDataOffsetBytes = newReader.ReadBytes(8);
|
||||
var subsystemDataOffset = BitConverter.ToInt64(subsystemDataOffsetBytes, 0);
|
||||
var version = newReader.ReadInt16();
|
||||
var endian = newReader.ReadInt16();
|
||||
var isLittleEndian = endian == 19785;
|
||||
if (!isLittleEndian)
|
||||
{
|
||||
throw new NotSupportedException("Big-endian files are not supported.");
|
||||
}
|
||||
|
||||
return new Header(rawBytes, text, subsystemDataOffset, version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetOperatingSystem()
|
||||
@ -98,4 +114,4 @@ namespace MatFileHandler
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System.Numerics;
|
||||
|
||||
namespace MatFileHandler
|
||||
@ -27,24 +28,12 @@ namespace MatFileHandler
|
||||
/// Tries to convert the array to an array of Double values.
|
||||
/// </summary>
|
||||
/// <returns>Array of values of the array, converted to Double, or null if the conversion is not possible.</returns>
|
||||
double[]? ConvertToDoubleArray();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to convert the array to a 2-dimensional array of Double values.
|
||||
/// </summary>
|
||||
/// <returns>2-dimensional array of values of the array, converted to Double, or null if the conversion is not possible (for example, when the array has more than 2 dimensions).</returns>
|
||||
double[,]? ConvertTo2dDoubleArray();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to convert the array to a multidimensional array of Double values.
|
||||
/// </summary>
|
||||
/// <returns>Multidimensional array of values of the array, converted to Double, or null if the conversion is not possible.</returns>
|
||||
Array? ConvertToMultidimensionalDoubleArray();
|
||||
double[] ConvertToDoubleArray();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to convert the array to an array of Complex values.
|
||||
/// </summary>
|
||||
/// <returns>Array of values of the array, converted to Complex, or null if the conversion is not possible.</returns>
|
||||
Complex[]? ConvertToComplexArray();
|
||||
Complex[] ConvertToComplexArray();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
@ -19,7 +21,7 @@ namespace MatFileHandler
|
||||
/// * for cell arrays:
|
||||
/// IArray;
|
||||
/// * for structure arrays:
|
||||
/// IReadOnlyDictionary<string, IArray>.
|
||||
/// IReadOnlyDictionary<string, IArray>;
|
||||
/// </remarks>
|
||||
public interface IArrayOf<T> : IArray
|
||||
{
|
||||
@ -34,4 +36,4 @@ namespace MatFileHandler
|
||||
/// <param name="list">Index of the element.</param>
|
||||
T this[params int[] list] { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
@ -6,4 +8,4 @@ namespace MatFileHandler
|
||||
public interface ICellArray : IArrayOf<IArray>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
@ -8,8 +10,6 @@ namespace MatFileHandler
|
||||
/// <summary>
|
||||
/// Gets the contained string.
|
||||
/// </summary>
|
||||
#pragma warning disable CA1716, CA1720
|
||||
string String { get; }
|
||||
#pragma warning restore CA1716, CA1720
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
@ -18,13 +20,5 @@ namespace MatFileHandler
|
||||
/// </summary>
|
||||
/// <param name="variableName">Variable name.</param>
|
||||
IVariable this[string variableName] { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the variable with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variable to get.</param>
|
||||
/// <param name="variable">When this method returns, contains the variable with the specified name, if it is found; otherwise, null.</param>
|
||||
/// <returns>True if the file contains a variable with the specified name; otherwise, false.</returns>
|
||||
bool TryGetVariable(string name, out IVariable? variable);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MatFileHandler
|
||||
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MatFileHandler
|
||||
@ -13,6 +15,6 @@ namespace MatFileHandler
|
||||
/// <summary>
|
||||
/// Gets a dictionary mapping indices to values.
|
||||
/// </summary>
|
||||
new IReadOnlyDictionary<(int row, int column), T> Data { get; }
|
||||
new IReadOnlyDictionary<(int, int), T> Data { get; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MatFileHandler
|
||||
@ -19,4 +21,4 @@ namespace MatFileHandler
|
||||
/// <param name="list">Index of the element in the structure array.</param>
|
||||
IArray this[string field, params int[] list] { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
namespace MatFileHandler
|
||||
{
|
||||
/// <summary>
|
||||
@ -20,4 +22,4 @@ namespace MatFileHandler
|
||||
/// </summary>
|
||||
bool IsGlobal { get; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System.Numerics;
|
||||
|
||||
namespace MatFileHandler
|
||||
@ -11,69 +12,40 @@ namespace MatFileHandler
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MatArray"/> class.
|
||||
/// </summary>
|
||||
/// <param name="flags">Array properties.</param>
|
||||
/// <param name="dimensions">Dimensions of the array.</param>
|
||||
/// <param name="name">Array name.</param>
|
||||
protected MatArray(
|
||||
ArrayFlags flags,
|
||||
int[] dimensions,
|
||||
string name)
|
||||
protected MatArray(int[] dimensions)
|
||||
{
|
||||
Flags = flags;
|
||||
Dimensions = dimensions;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int[] Dimensions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the array name.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Count => Dimensions.NumberOfElements();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEmpty => Dimensions.Length == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets properties of the array.
|
||||
/// </summary>
|
||||
internal ArrayFlags Flags { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new empty array.
|
||||
/// </summary>
|
||||
/// <returns>Empty array.</returns>
|
||||
public static MatArray Empty()
|
||||
{
|
||||
return new MatArray(new ArrayFlags { Class = ArrayType.MxCell, Variable = 0 }, Array.Empty<int>(), string.Empty);
|
||||
return new MatArray(new int[] { });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual double[]? ConvertToDoubleArray()
|
||||
public virtual double[] ConvertToDoubleArray()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual double[,]? ConvertTo2dDoubleArray()
|
||||
{
|
||||
return ConvertToMultidimensionalDoubleArray() as double[,];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Array? ConvertToMultidimensionalDoubleArray()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Complex[]? ConvertToComplexArray()
|
||||
public virtual Complex[] ConvertToComplexArray()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Copyright 2017-2019 Alexander Luzgarev
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@ -11,12 +13,10 @@ namespace MatFileHandler
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MatCellArray"/> class.
|
||||
/// </summary>
|
||||
/// <param name="flags">Array properties.</param>
|
||||
/// <param name="dimensions">Dimensions of the array.</param>
|
||||
/// <param name="name">Array name.</param>
|
||||
/// <param name="elements">Array elements.</param>
|
||||
public MatCellArray(ArrayFlags flags, int[] dimensions, string name, IEnumerable<IArray> elements)
|
||||
: base(flags, dimensions, name)
|
||||
public MatCellArray(int[] dimensions, IEnumerable<IArray> elements)
|
||||
: base(dimensions)
|
||||
{
|
||||
Data = elements.ToArray();
|
||||
}
|
||||
@ -31,4 +31,4 @@ namespace MatFileHandler
|
||||
set => Data[Dimensions.DimFlatten(indices)] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user