diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b8470e6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,248 @@ +# 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 diff --git a/MatFileHandler.Tests/AbstractTestDataFactory.cs b/MatFileHandler.Tests/AbstractTestDataFactory.cs deleted file mode 100755 index a7717d1..0000000 --- a/MatFileHandler.Tests/AbstractTestDataFactory.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2017-2018 Alexander Luzgarev - -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace MatFileHandler.Tests -{ - /// - /// Abstract factory of test data. - /// - /// Type of test data. - public abstract class AbstractTestDataFactory - { - /// - /// Initializes a new instance of the class. - /// - /// Directory with test files. - /// A convention used to filter test files. - protected AbstractTestDataFactory(string dataDirectory, ITestFilenameConvention testFilenameConvention) - { - DataDirectory = dataDirectory; - TestFilenameConvention = testFilenameConvention; - } - - private string DataDirectory { get; } - - private ITestFilenameConvention TestFilenameConvention { get; } - - /// - /// Get test data set by name. - /// - /// Name of the data set. - /// Test data. - public TTestData this[string dataSet] => - ReadTestData(FixPath(TestFilenameConvention.ConvertTestNameToFilename(dataSet))); - - /// - /// Get a sequence of all test data sets in the factory. - /// - /// A sequence of data sets. - public IEnumerable GetAllTestData() - { - var files = Directory.EnumerateFiles(DataDirectory).Where(TestFilenameConvention.FilterFile); - foreach (var filename in files) - { - yield return ReadTestData(filename); - } - } - - /// - /// Read test data from a stream. - /// - /// Input stream. - /// Test data. - 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, FileAccess.Read)) - { - return ReadDataFromStream(stream); - } - } - } -} \ No newline at end of file diff --git a/MatFileHandler.Tests/ArrayHandlingTests.cs b/MatFileHandler.Tests/ArrayHandlingTests.cs index 7ac644b..2f47261 100755 --- a/MatFileHandler.Tests/ArrayHandlingTests.cs +++ b/MatFileHandler.Tests/ArrayHandlingTests.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Collections.Generic; using System.Numerics; @@ -127,4 +125,4 @@ namespace MatFileHandler.Tests } } -} \ No newline at end of file +} diff --git a/MatFileHandler.Tests/ChecksumCalculatingStreamTests.cs b/MatFileHandler.Tests/ChecksumCalculatingStreamTests.cs index cdf86ab..3d26135 100644 --- a/MatFileHandler.Tests/ChecksumCalculatingStreamTests.cs +++ b/MatFileHandler.Tests/ChecksumCalculatingStreamTests.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Collections.Generic; using System.IO; @@ -17,50 +15,48 @@ namespace MatFileHandler.Tests /// /// Test writing various things. /// - /// + /// Bytes to write. [Theory] [MemberData(nameof(TestData))] - public void Test(Action action) + public void Test(byte[] bytes) { using var stream = new MemoryStream(); var sut = new ChecksumCalculatingStream(stream); - action(sut); + sut.Write(bytes, 0, bytes.Length); var actual = sut.GetCrc(); - var expected = ReferenceCalculation(action); + + var expected = ReferenceCalculation(bytes); } /// /// Test data for . /// /// Test data. - public static IEnumerable TestData() + public static TheoryData TestData() { - foreach (var data in TestData_Typed()) + var empty = new byte[1234]; + var nonEmpty = new byte[12345]; + for (var i = 0; i < 1234; i++) { - yield return new object[] { data }; + nonEmpty[i] = (byte)((i * i) % 256); } - } - - private static IEnumerable> TestData_Typed() - { - yield return BinaryWriterAction(w => w.Write(true)); - yield return BinaryWriterAction(w => w.Write(false)); - yield return BinaryWriterAction(w => w.Write(byte.MinValue)); - yield return BinaryWriterAction(w => w.Write(byte.MaxValue)); - yield return BinaryWriterAction(w => w.Write(short.MinValue)); - yield return BinaryWriterAction(w => w.Write(short.MaxValue)); - yield return BinaryWriterAction(w => w.Write(int.MinValue)); - yield return BinaryWriterAction(w => w.Write(int.MaxValue)); - yield return BinaryWriterAction(w => w.Write(long.MinValue)); - yield return BinaryWriterAction(w => w.Write(long.MaxValue)); - yield return BinaryWriterAction(w => w.Write(decimal.MinValue)); - yield return BinaryWriterAction(w => w.Write(decimal.MaxValue)); - yield return BinaryWriterAction(w => w.Write(double.MinValue)); - yield return BinaryWriterAction(w => w.Write(double.MaxValue)); - yield return BinaryWriterAction(w => w.Write(double.PositiveInfinity)); - yield return BinaryWriterAction(w => w.Write(double.NaN)); - yield return BinaryWriterAction(w => w.Write(new byte[] { 1, 2, 3, 4, 5, 6, 7 })); - yield return BinaryWriterAction(w => w.Write(Enumerable.Range(0, 255).SelectMany(x => Enumerable.Range(0, 255)).Select(x => (byte)x).ToArray())); + return new TheoryData() + { + 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 BinaryWriterAction(Action action) @@ -72,15 +68,15 @@ namespace MatFileHandler.Tests }; } - private uint ReferenceCalculation(Action action) + private static uint ReferenceCalculation(byte[] bytes) { using var stream = new MemoryStream(); - action(stream); + stream.Write(bytes, 0, bytes.Length); stream.Position = 0; return CalculateAdler32Checksum(stream); } - private static uint CalculateAdler32Checksum(Stream stream) + private static uint CalculateAdler32Checksum(MemoryStream stream) { uint s1 = 1; uint s2 = 0; diff --git a/MatFileHandler.Tests/CommonData.cs b/MatFileHandler.Tests/CommonData.cs index 5bf722d..e6d1ab3 100755 --- a/MatFileHandler.Tests/CommonData.cs +++ b/MatFileHandler.Tests/CommonData.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler.Tests { /// @@ -47,4 +45,4 @@ namespace MatFileHandler.Tests /// public static readonly ulong[] UInt64Limits = { 0UL, 18446744073709551615UL }; } -} \ No newline at end of file +} diff --git a/MatFileHandler.Tests/ComplexOfTests.cs b/MatFileHandler.Tests/ComplexOfTests.cs index bbff969..72ca4d8 100755 --- a/MatFileHandler.Tests/ComplexOfTests.cs +++ b/MatFileHandler.Tests/ComplexOfTests.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using Xunit; namespace MatFileHandler.Tests @@ -47,4 +45,4 @@ namespace MatFileHandler.Tests Assert.Equal(h1, h2); } } -} \ No newline at end of file +} diff --git a/MatFileHandler.Tests/ExtensionTestFilenameConvention.cs b/MatFileHandler.Tests/ExtensionTestFilenameConvention.cs deleted file mode 100755 index 08ebd80..0000000 --- a/MatFileHandler.Tests/ExtensionTestFilenameConvention.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2017-2018 Alexander Luzgarev - -using System.IO; - -namespace MatFileHandler.Tests -{ - /// - /// A filename convention based on file extensions. - /// - internal class ExtensionTestFilenameConvention : ITestFilenameConvention - { - /// - /// Initializes a new instance of the class. - /// - /// File extension. - public ExtensionTestFilenameConvention(string extension) - { - Extension = extension; - } - - private string Extension { get; } - - /// - /// Convert test name to filename by adding the extension. - /// - /// Test name. - /// The corresponding filename. - public string ConvertTestNameToFilename(string testName) - { - return Path.ChangeExtension(testName, Extension); - } - - /// - /// Compare file's extension to the one specified during initialization. - /// - /// Filename. - /// True iff the file has the extension stored in the class. - public bool FilterFile(string filename) - { - return Path.GetExtension(filename) == "." + Extension; - } - } -} \ No newline at end of file diff --git a/MatFileHandler.Tests/ITestFilenameConvention.cs b/MatFileHandler.Tests/ITestFilenameConvention.cs deleted file mode 100755 index 5f31cbe..0000000 --- a/MatFileHandler.Tests/ITestFilenameConvention.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2017-2018 Alexander Luzgarev - -namespace MatFileHandler.Tests -{ - /// - /// Interface for handling filtering tests based on filenames. - /// - public interface ITestFilenameConvention - { - /// - /// Convert test name to a filename (e.g., by adding an appropriate extension). - /// - /// Name of a test. - /// Filename. - string ConvertTestNameToFilename(string testName); - - /// - /// Decide if a file contains a test based on the filename. - /// - /// A filename. - /// True iff the file should contain a test. - bool FilterFile(string filename); - } -} \ No newline at end of file diff --git a/MatFileHandler.Tests/MatFileHandler.Tests.csproj b/MatFileHandler.Tests/MatFileHandler.Tests.csproj index 5a46d00..d0c201d 100755 --- a/MatFileHandler.Tests/MatFileHandler.Tests.csproj +++ b/MatFileHandler.Tests/MatFileHandler.Tests.csproj @@ -2,10 +2,9 @@ net461;net472;net8.0 false - 10.0 + latest - ..\MatFileHandler.ruleset bin\Debug\net5.0\MatFileHandler.Tests.xml diff --git a/MatFileHandler.Tests/MatFileReaderTests.cs b/MatFileHandler.Tests/MatFileReaderTests.cs index 30ba7c2..b3a3775 100755 --- a/MatFileHandler.Tests/MatFileReaderTests.cs +++ b/MatFileHandler.Tests/MatFileReaderTests.cs @@ -1,6 +1,5 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; @@ -19,9 +18,9 @@ namespace MatFileHandler.Tests /// Test reading all files in a given test set. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestReader(AbstractTestDataFactory testFactory) + public void TestReader(MatFileReadingMethod method) { - foreach (var matFile in testFactory.GetAllTestData()) + foreach (var matFile in ReadAllTestFiles(method)) { Assert.NotEmpty(matFile.Variables); } @@ -31,9 +30,9 @@ namespace MatFileHandler.Tests /// Test reading lower and upper limits of integer data types. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestLimits(AbstractTestDataFactory testFactory) + public void TestLimits(MatFileReadingMethod method) { - var matFile = testFactory["limits"]; + var matFile = ReadTestFile("limits", method); IArray array; array = matFile["int8_"].Value; CheckLimits(array as IArrayOf, CommonData.Int8Limits); @@ -65,9 +64,9 @@ namespace MatFileHandler.Tests /// Test writing lower and upper limits of integer-based complex data types. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestComplexLimits(AbstractTestDataFactory testFactory) + public void TestComplexLimits(MatFileReadingMethod method) { - var matFile = testFactory["limits_complex"]; + var matFile = ReadTestFile("limits_complex", method); IArray array; array = matFile["int8_complex"].Value; CheckComplexLimits(array as IArrayOf>, CommonData.Int8Limits); @@ -101,9 +100,9 @@ namespace MatFileHandler.Tests /// Test reading an ASCII-encoded string. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestAscii(AbstractTestDataFactory testFactory) + public void TestAscii(MatFileReadingMethod method) { - var matFile = testFactory["ascii"]; + var matFile = ReadTestFile("ascii", method); var arrayAscii = matFile["s"].Value as ICharArray; Assert.NotNull(arrayAscii); Assert.Equal(new[] { 1, 3 }, arrayAscii.Dimensions); @@ -115,9 +114,9 @@ namespace MatFileHandler.Tests /// Test reading a Unicode string. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestUnicode(AbstractTestDataFactory testFactory) + public void TestUnicode(MatFileReadingMethod method) { - var matFile = testFactory["unicode"]; + var matFile = ReadTestFile("unicode", method); var arrayUnicode = matFile["s"].Value as ICharArray; Assert.NotNull(arrayUnicode); Assert.Equal(new[] { 1, 2 }, arrayUnicode.Dimensions); @@ -130,9 +129,9 @@ namespace MatFileHandler.Tests /// Test reading a wide Unicode string. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestUnicodeWide(AbstractTestDataFactory testFactory) + public void TestUnicodeWide(MatFileReadingMethod method) { - var matFile = testFactory["unicode-wide"]; + var matFile = ReadTestFile("unicode-wide", method); var arrayUnicodeWide = matFile["s"].Value as ICharArray; Assert.NotNull(arrayUnicodeWide); Assert.Equal(new[] { 1, 2 }, arrayUnicodeWide.Dimensions); @@ -143,9 +142,9 @@ namespace MatFileHandler.Tests /// Test converting a structure array to a Double array. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestConvertToDoubleArray(AbstractTestDataFactory testFactory) + public void TestConvertToDoubleArray(MatFileReadingMethod method) { - var matFile = testFactory["struct"]; + var matFile = ReadTestFile("struct", method); var array = matFile.Variables[0].Value; Assert.Null(array.ConvertToDoubleArray()); } @@ -155,9 +154,9 @@ namespace MatFileHandler.Tests /// /// Should return null. [Theory, MemberData(nameof(TestDataFactories))] - public void TestConvertToComplexArray(AbstractTestDataFactory testFactory) + public void TestConvertToComplexArray(MatFileReadingMethod method) { - var matFile = testFactory["struct"]; + var matFile = ReadTestFile("struct", method); var array = matFile.Variables[0].Value; Assert.Null(array.ConvertToComplexArray()); } @@ -166,9 +165,9 @@ namespace MatFileHandler.Tests /// Test reading an enumeration. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestEnum(AbstractTestDataFactory testFactory) + public void TestEnum(MatFileReadingMethod method) { - var matFile = testFactory["enum"]; + var matFile = ReadTestFile("enum", method); var days = matFile["days"].Value; var enumeration = new EnumAdapter(days); Assert.Equal(5, enumeration.Values.Count); @@ -183,9 +182,9 @@ namespace MatFileHandler.Tests /// Test reading a structure array. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestStruct(AbstractTestDataFactory testFactory) + public void TestStruct(MatFileReadingMethod method) { - var matFile = testFactory["struct"]; + var matFile = ReadTestFile("struct", method); var structure = matFile["struct_"].Value as IStructureArray; Assert.NotNull(structure); Assert.Equal(new[] { "x", "y" }, structure.FieldNames); @@ -237,9 +236,9 @@ namespace MatFileHandler.Tests /// Test reading a sparse array. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestSparse(AbstractTestDataFactory testFactory) + public void TestSparse(MatFileReadingMethod method) { - var matFile = testFactory["sparse"]; + var matFile = ReadTestFile("sparse", method); var sparseArray = matFile["sparse_"].Value as ISparseArrayOf; Assert.NotNull(sparseArray); Assert.Equal(new[] { 4, 5 }, sparseArray.Dimensions); @@ -267,9 +266,9 @@ namespace MatFileHandler.Tests /// Test reading a logical array. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestLogical(AbstractTestDataFactory testFactory) + public void TestLogical(MatFileReadingMethod method) { - var matFile = testFactory["logical"]; + var matFile = ReadTestFile("logical", method); var array = matFile["logical_"].Value; var logicalArray = array as IArrayOf; Assert.NotNull(logicalArray); @@ -285,9 +284,9 @@ namespace MatFileHandler.Tests /// Test reading a sparse logical array. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestSparseLogical(AbstractTestDataFactory testFactory) + public void TestSparseLogical(MatFileReadingMethod method) { - var matFile = testFactory["sparse_logical"]; + var matFile = ReadTestFile("sparse_logical", method); var array = matFile["sparse_logical"].Value; var sparseArray = array as ISparseArrayOf; Assert.NotNull (sparseArray); @@ -304,9 +303,9 @@ namespace MatFileHandler.Tests /// Test reading a global variable. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestGlobal(AbstractTestDataFactory testFactory) + public void TestGlobal(MatFileReadingMethod method) { - var matFile = testFactory["global"]; + var matFile = ReadTestFile("global", method); var variable = matFile.Variables.First(); Assert.True(variable.IsGlobal); } @@ -315,9 +314,9 @@ namespace MatFileHandler.Tests /// Test reading a sparse complex array. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TextSparseComplex(AbstractTestDataFactory testFactory) + public void TextSparseComplex(MatFileReadingMethod method) { - var matFile = testFactory["sparse_complex"]; + var matFile = ReadTestFile("sparse_complex", method); var array = matFile["sparse_complex"].Value; var sparseArray = array as ISparseArrayOf; Assert.NotNull(sparseArray); @@ -331,9 +330,9 @@ namespace MatFileHandler.Tests /// Test reading an object. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestObject(AbstractTestDataFactory testFactory) + public void TestObject(MatFileReadingMethod method) { - var matFile = testFactory["object"]; + var matFile = ReadTestFile("object", method); var obj = matFile["object_"].Value as IMatObject; Assert.NotNull(obj); Assert.Equal("Point", obj.ClassName); @@ -348,9 +347,9 @@ namespace MatFileHandler.Tests /// Test reading another object. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestObject2(AbstractTestDataFactory testFactory) + public void TestObject2(MatFileReadingMethod method) { - var matFile = testFactory["object2"]; + var matFile = ReadTestFile("object2", method); var obj = matFile["object2"].Value as IMatObject; Assert.NotNull(obj); Assert.Equal("Point", obj.ClassName); @@ -371,9 +370,9 @@ namespace MatFileHandler.Tests /// Test reading a table. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestTable(AbstractTestDataFactory testFactory) + public void TestTable(MatFileReadingMethod method) { - var matFile = testFactory["table"]; + var matFile = ReadTestFile("table", method); var obj = matFile["table_"].Value as IMatObject; var table = new TableAdapter(obj); Assert.Equal(3, table.NumberOfRows); @@ -388,13 +387,37 @@ namespace MatFileHandler.Tests Assert.Equal(new[] { 1.0, 3.0, 5.0, 2.0, 4.0, 6.0 }, variable2.ConvertToDoubleArray()); } + /// + /// Test reading a deeply nested table. + /// + [Theory, MemberData(nameof(TestDataFactories))] + public void TestDeepTable(MatFileReadingMethod method) + { + var matFile = ReadTestFile("table-deep", method); + var obj = matFile["t"].Value as IMatObject; + var table = new TableAdapter(obj); + Assert.Equal(1, table.NumberOfRows); + Assert.Equal(2, table.NumberOfVariables); + Assert.Equal(new[] { "s", "another" }, table.VariableNames); + var s = table["s"] as IStructureArray; + Assert.Equal(new[] { "a", "b", "c" }, s.FieldNames); + var c = s["c", 0]; + var internalTable = new TableAdapter(c); + Assert.Equal(2, internalTable.NumberOfRows); + Assert.Equal(2, internalTable.NumberOfVariables); + Assert.Equal(new[] { "x", "y" }, internalTable.VariableNames); + var y = new StringAdapter(internalTable["y"]); + Assert.Equal("3", y[0]); + Assert.Equal("abc", y[1]); + } + /// /// Test reading a table with strings /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestTableWithStrings(AbstractTestDataFactory testFactory) + public void TestTableWithStrings(MatFileReadingMethod method) { - var matFile = testFactory["table-with-strings"]; + var matFile = ReadTestFile("table-with-strings", method); var obj = matFile["t"].Value as IMatObject; var table = new TableAdapter(obj); Assert.Equal(5, table.NumberOfRows); @@ -417,9 +440,9 @@ namespace MatFileHandler.Tests /// Test subobjects within objects. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestSubobjects(AbstractTestDataFactory testFactory) + public void TestSubobjects(MatFileReadingMethod method) { - var matFile = testFactory["pointWithSubpoints"]; + var matFile = ReadTestFile("pointWithSubpoints", method); var p = matFile["p"].Value as IMatObject; Assert.Equal("Point", p.ClassName); var x = p["x"] as IMatObject; @@ -440,9 +463,9 @@ namespace MatFileHandler.Tests /// Test nested objects. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestNestedObjects(AbstractTestDataFactory testFactory) + public void TestNestedObjects(MatFileReadingMethod method) { - var matFile = testFactory["subsubPoint"]; + var matFile = ReadTestFile("subsubPoint", method); var p = matFile["p"].Value as IMatObject; Assert.Equal("Point", p.ClassName); Assert.Equal(new[] { 1.0 }, p["x"].ConvertToDoubleArray()); @@ -458,9 +481,9 @@ namespace MatFileHandler.Tests /// Test datetime objects. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestDatetime(AbstractTestDataFactory testFactory) + public void TestDatetime(MatFileReadingMethod method) { - var matFile = testFactory["datetime"]; + var matFile = ReadTestFile("datetime", method); var d = matFile["d"].Value as IMatObject; var datetime = new DatetimeAdapter(d); Assert.Equal(new[] { 1, 2 }, datetime.Dimensions); @@ -472,9 +495,9 @@ namespace MatFileHandler.Tests /// Another test for datetime objects. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestDatetime2(AbstractTestDataFactory testFactory) + public void TestDatetime2(MatFileReadingMethod method) { - var matFile = testFactory["datetime2"]; + var matFile = ReadTestFile("datetime2", method); var d = matFile["d"].Value as IMatObject; var datetime = new DatetimeAdapter(d); Assert.Equal(new[] { 1, 1 }, datetime.Dimensions); @@ -487,9 +510,9 @@ namespace MatFileHandler.Tests /// Test string objects. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestString(AbstractTestDataFactory testFactory) + public void TestString(MatFileReadingMethod method) { - var matFile = testFactory["string"]; + var matFile = ReadTestFile("string", method); var s = matFile["s"].Value as IMatObject; var str = new StringAdapter(s); Assert.Equal(new[] { 4, 1 }, str.Dimensions); @@ -503,9 +526,9 @@ namespace MatFileHandler.Tests /// Test duration objects. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestDuration(AbstractTestDataFactory testFactory) + public void TestDuration(MatFileReadingMethod method) { - var matFile = testFactory["duration"]; + var matFile = ReadTestFile("duration", method); var d = matFile["d"].Value as IMatObject; var duration = new DurationAdapter(d); Assert.Equal(new[] { 1, 3 }, duration.Dimensions); @@ -518,9 +541,9 @@ namespace MatFileHandler.Tests /// Test unrepresentable datetime. /// [Theory, MemberData(nameof(TestDataFactories))] - public void TestDatetime_Unrepresentable(AbstractTestDataFactory testFactory) + public void TestDatetime_Unrepresentable(MatFileReadingMethod method) { - var matFile = testFactory["datetime-unrepresentable"]; + var matFile = ReadTestFile("datetime-unrepresentable", method); var obj = matFile["d"].Value as IMatObject; var datetime = new DatetimeAdapter(obj); var d0 = datetime[0]; @@ -531,9 +554,9 @@ namespace MatFileHandler.Tests /// Test 3-dimensional arrays. /// [Theory, MemberData(nameof(TestDataFactories))] - public void Test_3DArrays(AbstractTestDataFactory testFactory) + public void Test_3DArrays(MatFileReadingMethod method) { - var matFile = testFactory["issue20.mat"]; + var matFile = ReadTestFile("issue20", method); var obj = matFile["a3d"].Value; var values = obj.ConvertToDoubleArray(); Assert.Equal(Enumerable.Range(1, 24).Select(x => (double)x).ToArray(), values); @@ -566,9 +589,9 @@ namespace MatFileHandler.Tests /// Test four-dimensional arrays. /// [Theory, MemberData(nameof(TestDataFactories))] - public void Test_4DArrays(AbstractTestDataFactory testFactory) + public void Test4DArrays(MatFileReadingMethod method) { - var matFile = testFactory["issue20.mat"]; + var matFile = ReadTestFile("issue20", method); var obj = matFile["a4d"].Value; Assert.Equal(Enumerable.Range(1, 120).Select(x => (double)x).ToArray(), obj.ConvertToDoubleArray()); Assert.Null(obj.ConvertTo2dDoubleArray()); @@ -577,19 +600,36 @@ namespace MatFileHandler.Tests /// /// Returns the factories that provide test data in various configurations. /// - public static TheoryData> TestDataFactories + public static TheoryData TestDataFactories { get { - return new TheoryData> + return new TheoryData { - new MatTestDataFactory(Path.Combine(TestDirectory, "good")), - new PartialReadMatTestDataFactory(Path.Combine(TestDirectory, "good")), - new UnalignedMatTestDataFactory(Path.Combine(TestDirectory, "good")), + MatFileReadingMethod.NormalStream, + MatFileReadingMethod.PartialStream, + MatFileReadingMethod.UnalignedStream, }; } } + private static IMatFile ReadTestFile(string fileName, MatFileReadingMethod method) + { + var fullFileName = Path.Combine("test-data", "good", $"{fileName}.mat"); + return MatFileReadingMethods.ReadMatFile(method, fullFileName); + } + + private static IEnumerable ReadAllTestFiles(MatFileReadingMethod method) + { + foreach (var fileName in Directory.EnumerateFiles( + Path.Combine("test-data", "good"), + "*.mat")) + { + var fullFileName = fileName; + yield return MatFileReadingMethods.ReadMatFile(method, fullFileName); + } + } + private static void CheckLimits(IArrayOf array, T[] limits) where T : struct { @@ -607,4 +647,4 @@ namespace MatFileHandler.Tests Assert.Equal(new ComplexOf(limits[1], limits[0]), array[1]); } } -} \ No newline at end of file +} diff --git a/MatFileHandler.Tests/MatFileReadingMethod.cs b/MatFileHandler.Tests/MatFileReadingMethod.cs new file mode 100644 index 0000000..2ec7645 --- /dev/null +++ b/MatFileHandler.Tests/MatFileReadingMethod.cs @@ -0,0 +1,27 @@ +namespace MatFileHandler.Tests; + +/// +/// Method of reading .mat files for testing. +/// +public enum MatFileReadingMethod +{ + /// + /// Undefined. + /// + Undefined = 0, + + /// + /// Normal stream (like memory or file stream). + /// + NormalStream, + + /// + /// Partial stream (only is capable of reading one byte at a time). + /// + PartialStream, + + /// + /// Unaligned stream (what happens if the data don't start at the beginning?). + /// + UnalignedStream, +} diff --git a/MatFileHandler.Tests/MatFileReadingMethods.cs b/MatFileHandler.Tests/MatFileReadingMethods.cs new file mode 100644 index 0000000..759d447 --- /dev/null +++ b/MatFileHandler.Tests/MatFileReadingMethods.cs @@ -0,0 +1,38 @@ +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(); + } +} diff --git a/MatFileHandler.Tests/MatFileWriterOptionsForTests.cs b/MatFileHandler.Tests/MatFileWriterOptionsForTests.cs new file mode 100644 index 0000000..b3b6a74 --- /dev/null +++ b/MatFileHandler.Tests/MatFileWriterOptionsForTests.cs @@ -0,0 +1,27 @@ +namespace MatFileHandler.Tests; + +/// +/// Options to give to MatFileWriter constructor for testing it. +/// +public enum MatFileWriterOptionsForTests +{ + /// + /// Undefined. + /// + Undefined = 0, + + /// + /// No options. + /// + None, + + /// + /// Option to always use compression. + /// + Always, + + /// + /// Option to never use compression. + /// + Never, +} diff --git a/MatFileHandler.Tests/MatFileWriterTests.cs b/MatFileHandler.Tests/MatFileWriterTests.cs index a2da405..0471575 100755 --- a/MatFileHandler.Tests/MatFileWriterTests.cs +++ b/MatFileHandler.Tests/MatFileWriterTests.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.IO; using System.Numerics; @@ -17,8 +15,8 @@ namespace MatFileHandler.Tests /// /// Test writing a simple Double array. /// - [Theory, MemberData(nameof(MatFileWritingMethods))] - public void TestWrite(MatFileWritingMethod method) + [Theory, MemberData(nameof(MatFileWritingTestData))] + public void TestWrite(MatFileWritingMethod method, MatFileWriterOptionsForTests options) { var builder = new DataBuilder(); var array = builder.NewArray(1, 2); @@ -26,7 +24,7 @@ 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); + MatCompareWithTestData("good", "double-array", actual, method, options); } /// @@ -51,8 +49,8 @@ namespace MatFileHandler.Tests /// /// Test writing lower and upper limits of integer data types. /// - [Theory, MemberData(nameof(MatFileWritingMethods))] - public void TestLimits(MatFileWritingMethod method) + [Theory, MemberData(nameof(MatFileWritingTestData))] + public void TestLimits(MatFileWritingMethod method, MatFileWriterOptionsForTests options) { var builder = new DataBuilder(); var int8 = builder.NewVariable("int8_", builder.NewArray(CommonData.Int8Limits, 1, 2)); @@ -64,14 +62,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); + MatCompareWithTestData("good", "limits", actual, method, options); } /// /// Test writing lower and upper limits of integer-based complex data types. /// - [Theory, MemberData(nameof(MatFileWritingMethods))] - public void TestLimitsComplex(MatFileWritingMethod method) + [Theory, MemberData(nameof(MatFileWritingTestData))] + public void TestLimitsComplex(MatFileWritingMethod method, MatFileWriterOptionsForTests options) { var builder = new DataBuilder(); var int8Complex = builder.NewVariable( @@ -103,26 +101,26 @@ namespace MatFileHandler.Tests int16Complex, int32Complex, int64Complex, int8Complex, uint16Complex, uint32Complex, uint64Complex, uint8Complex, }); - MatCompareWithTestData("good", "limits_complex", actual, method); + MatCompareWithTestData("good", "limits_complex", actual, method, options); } /// /// Test writing a wide-Unicode symbol. /// - [Theory, MemberData(nameof(MatFileWritingMethods))] - public void TestUnicodeWide(MatFileWritingMethod method) + [Theory, MemberData(nameof(MatFileWritingTestData))] + public void TestUnicodeWide(MatFileWritingMethod method, MatFileWriterOptionsForTests options) { var builder = new DataBuilder(); var s = builder.NewVariable("s", builder.NewCharArray("🍆")); var actual = builder.NewFile(new[] { s }); - MatCompareWithTestData("good", "unicode-wide", actual, method); + MatCompareWithTestData("good", "unicode-wide", actual, method, options); } /// /// Test writing a sparse array. /// - [Theory, MemberData(nameof(MatFileWritingMethods))] - public void TestSparseArray(MatFileWritingMethod method) + [Theory, MemberData(nameof(MatFileWritingTestData))] + public void TestSparseArray(MatFileWritingMethod method, MatFileWriterOptionsForTests options) { var builder = new DataBuilder(); var sparseArray = builder.NewSparseArray(4, 5); @@ -132,14 +130,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); + MatCompareWithTestData("good", "sparse", actual, method, options); } /// /// Test writing a structure array. /// - [Theory, MemberData(nameof(MatFileWritingMethods))] - public void TestStructure(MatFileWritingMethod method) + [Theory, MemberData(nameof(MatFileWritingTestData))] + public void TestStructure(MatFileWritingMethod method, MatFileWriterOptionsForTests options) { var builder = new DataBuilder(); var structure = builder.NewStructureArray(new[] { "x", "y" }, 2, 3); @@ -160,27 +158,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); + MatCompareWithTestData("good", "struct", actual, method, options); } /// /// Test writing a logical array. /// - [Theory, MemberData(nameof(MatFileWritingMethods))] - public void TestLogical(MatFileWritingMethod method) + [Theory, MemberData(nameof(MatFileWritingTestData))] + public void TestLogical(MatFileWritingMethod method, MatFileWriterOptionsForTests options) { 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); + MatCompareWithTestData("good", "logical", actual, method, options); } /// /// Test writing a sparse logical array. /// - [Theory, MemberData(nameof(MatFileWritingMethods))] - public void TestSparseLogical(MatFileWritingMethod method) + [Theory, MemberData(nameof(MatFileWritingTestData))] + public void TestSparseLogical(MatFileWritingMethod method, MatFileWriterOptionsForTests options) { var builder = new DataBuilder(); var array = builder.NewSparseArray(2, 3); @@ -190,14 +188,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); + MatCompareWithTestData("good", "sparse_logical", actual, method, options); } /// /// Test writing a sparse complex array. /// - [Theory, MemberData(nameof(MatFileWritingMethods))] - public void TestSparseComplex(MatFileWritingMethod method) + [Theory, MemberData(nameof(MatFileWritingTestData))] + public void TestSparseComplex(MatFileWritingMethod method, MatFileWriterOptionsForTests options) { var builder = new DataBuilder(); var array = builder.NewSparseArray(2, 2); @@ -206,48 +204,48 @@ 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); + MatCompareWithTestData("good", "sparse_complex", actual, method, options); } /// /// Test writing a global variable. /// - [Theory, MemberData(nameof(MatFileWritingMethods))] - public void TestGlobal(MatFileWritingMethod method) + [Theory, MemberData(nameof(MatFileWritingTestData))] + public void TestGlobal(MatFileWritingMethod method, MatFileWriterOptionsForTests options) { 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); + MatCompareWithTestData("good", "global", actual, method, options); } /// /// Various writing methods for testing writing of .mat files. /// - public static TheoryData MatFileWritingMethods + public static TheoryData MatFileWritingTestData { get { - return new TheoryData + var always = new MatFileWriterOptions { UseCompression = CompressionUsage.Always}; + var never = new MatFileWriterOptions { UseCompression = CompressionUsage.Never }; + var data = new TheoryData { - new MatFileWritingToMemoryStream(null), - new MatFileWritingToMemoryStream(new MatFileWriterOptions { UseCompression = CompressionUsage.Always }), - new MatFileWritingToMemoryStream(new MatFileWriterOptions { UseCompression = CompressionUsage.Never }), - new MatFileWritingToUnseekableStream(null), - new MatFileWritingToUnseekableStream(new MatFileWriterOptions { UseCompression = CompressionUsage.Always }), - new MatFileWritingToUnseekableStream(new MatFileWriterOptions { UseCompression = CompressionUsage.Never }), - new MatFileWritingToUnalignedMemoryStream(null), - new MatFileWritingToUnalignedMemoryStream(new MatFileWriterOptions { UseCompression = CompressionUsage.Always }), - new MatFileWritingToUnalignedMemoryStream(new MatFileWriterOptions { UseCompression = CompressionUsage.Never }), + { 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 GetMatTestData(string factoryName) => - new MatTestDataFactory(Path.Combine(TestDirectory, factoryName)); - - private void CompareSparseArrays(ISparseArrayOf expected, ISparseArrayOf actual) + private static void CompareSparseArrays(ISparseArrayOf expected, ISparseArrayOf actual) where T : struct { Assert.NotNull(actual); @@ -255,7 +253,7 @@ namespace MatFileHandler.Tests Assert.Equal(expected.Data, actual.Data); } - private void CompareStructureArrays(IStructureArray expected, IStructureArray actual) + private static void CompareStructureArrays(IStructureArray expected, IStructureArray actual) { Assert.NotNull(actual); Assert.Equal(expected.Dimensions, actual.Dimensions); @@ -269,7 +267,7 @@ namespace MatFileHandler.Tests } } - private void CompareCellArrays(ICellArray expected, ICellArray actual) + private static void CompareCellArrays(ICellArray expected, ICellArray actual) { Assert.NotNull(actual); Assert.Equal(expected.Dimensions, actual.Dimensions); @@ -279,21 +277,21 @@ namespace MatFileHandler.Tests } } - private void CompareNumericalArrays(IArrayOf expected, IArrayOf actual) + private static void CompareNumericalArrays(IArrayOf expected, IArrayOf actual) { Assert.NotNull(actual); Assert.Equal(expected.Dimensions, actual.Dimensions); Assert.Equal(expected.Data, actual.Data); } - private void CompareCharArrays(ICharArray expected, ICharArray actual) + private static void CompareCharArrays(ICharArray expected, ICharArray actual) { Assert.NotNull(actual); Assert.Equal(expected.Dimensions, actual.Dimensions); Assert.Equal(expected.String, actual.String); } - private void CompareMatArrays(IArray expected, IArray actual) + private static void CompareMatArrays(IArray expected, IArray actual) { switch (expected) { @@ -384,7 +382,7 @@ namespace MatFileHandler.Tests throw new NotSupportedException(); } - private void CompareMatFiles(IMatFile expected, IMatFile actual) + private static void CompareMatFiles(IMatFile expected, IMatFile actual) { Assert.Equal(expected.Variables.Length, actual.Variables.Length); for (var i = 0; i < expected.Variables.Length; i++) @@ -397,24 +395,28 @@ namespace MatFileHandler.Tests } } - private void MatCompareWithTestData( + private static void MatCompareWithTestData( string factoryName, string testName, IMatFile actual, - MatFileWritingMethod method) + MatFileWritingMethod method, + MatFileWriterOptionsForTests options) { - var expected = GetMatTestData(factoryName)[testName]; - var buffer = method.WriteMatFile(actual); + 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); } - private ComplexOf[] CreateComplexLimits(T[] limits) - where T : struct + private static ComplexOf[] CreateComplexLimits(T[] limits) + where T : struct { return new[] { new ComplexOf(limits[0], limits[1]), new ComplexOf(limits[1], limits[0]) }; } } -} \ No newline at end of file +} diff --git a/MatFileHandler.Tests/MatFileWritingMethod.cs b/MatFileHandler.Tests/MatFileWritingMethod.cs index b5ba772..59f51e4 100644 --- a/MatFileHandler.Tests/MatFileWritingMethod.cs +++ b/MatFileHandler.Tests/MatFileWritingMethod.cs @@ -1,17 +1,27 @@ -// Copyright 2017-2018 Alexander Luzgarev +namespace MatFileHandler.Tests; -namespace MatFileHandler.Tests +/// +/// Method of writing .mat files for testing. +/// +public enum MatFileWritingMethod { /// - /// A method of writing IMatFile into a byte buffer. + /// Undefined. /// - public abstract class MatFileWritingMethod - { - /// - /// Write an IMatFile into a byte buffer. - /// - /// - /// - public abstract byte[] WriteMatFile(IMatFile matFile); - } -} \ No newline at end of file + Undefined = 0, + + /// + /// Normal stream (like memory or file stream). + /// + NormalStream, + + /// + /// A stream that cannot be seeked (like a deflate stream). + /// + UnseekableStream, + + /// + /// Unaligned stream (what happens if the data don't start at the beginning?). + /// + UnalignedStream, +} diff --git a/MatFileHandler.Tests/MatFileWritingMethods.cs b/MatFileHandler.Tests/MatFileWritingMethods.cs new file mode 100644 index 0000000..3d8c1bb --- /dev/null +++ b/MatFileHandler.Tests/MatFileWritingMethods.cs @@ -0,0 +1,58 @@ +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(), + }; + } +} diff --git a/MatFileHandler.Tests/MatFileWritingToMemoryStream.cs b/MatFileHandler.Tests/MatFileWritingToMemoryStream.cs deleted file mode 100644 index 5efb26a..0000000 --- a/MatFileHandler.Tests/MatFileWritingToMemoryStream.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2017-2018 Alexander Luzgarev - -using System.IO; - -namespace MatFileHandler.Tests -{ - /// - /// A method of writing an IMatFile into a MemoryStream. - /// - public class MatFileWritingToMemoryStream : MatFileWritingMethod - { - private readonly MatFileWriterOptions? _maybeOptions; - - /// - /// Initializes a new instance of the class. - /// - /// Options for the . - public MatFileWritingToMemoryStream(MatFileWriterOptions? maybeOptions) - { - _maybeOptions = maybeOptions; - } - - /// - public override byte[] WriteMatFile(IMatFile matFile) - { - using var memoryStream = new MemoryStream(); - var matFileWriter = _maybeOptions switch - { - { } options => new MatFileWriter(memoryStream, options), - _ => new MatFileWriter(memoryStream), - }; - - matFileWriter.Write(matFile); - return memoryStream.ToArray(); - } - } -} \ No newline at end of file diff --git a/MatFileHandler.Tests/MatFileWritingToUnalignedMemoryStream.cs b/MatFileHandler.Tests/MatFileWritingToUnalignedMemoryStream.cs deleted file mode 100644 index a419974..0000000 --- a/MatFileHandler.Tests/MatFileWritingToUnalignedMemoryStream.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2017-2018 Alexander Luzgarev - -using System; -using System.IO; - -namespace MatFileHandler.Tests -{ - /// - /// A method of writing an IMatFile into a stream that is "unaligned". - /// - public class MatFileWritingToUnalignedMemoryStream : MatFileWritingMethod - { - private readonly MatFileWriterOptions? _maybeOptions; - - /// - /// Initializes a new instance of the class. - /// - /// Options for the . - public MatFileWritingToUnalignedMemoryStream(MatFileWriterOptions? maybeOptions) - { - _maybeOptions = maybeOptions; - } - - /// - public override byte[] WriteMatFile(IMatFile matFile) - { - using var memoryStream = new MemoryStream(); - memoryStream.Seek(3, SeekOrigin.Begin); - var matFileWriter = _maybeOptions switch - { - { } options => new MatFileWriter(memoryStream, options), - _ => new MatFileWriter(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; - } - } -} \ No newline at end of file diff --git a/MatFileHandler.Tests/MatFileWritingToUnseekableStream.cs b/MatFileHandler.Tests/MatFileWritingToUnseekableStream.cs deleted file mode 100644 index 1a2c2d8..0000000 --- a/MatFileHandler.Tests/MatFileWritingToUnseekableStream.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2017-2018 Alexander Luzgarev - -using System.IO; - -namespace MatFileHandler.Tests -{ - /// - /// A method of writing an IMatFile into a stream that is not seekable. - /// - public class MatFileWritingToUnseekableStream : MatFileWritingMethod - { - private readonly MatFileWriterOptions? _maybeOptions; - - /// - /// Initializes a new instance of the class. - /// - /// Options for the . - public MatFileWritingToUnseekableStream(MatFileWriterOptions? maybeOptions) - { - _maybeOptions = maybeOptions; - } - - /// - public override byte[] WriteMatFile(IMatFile matFile) - { - using var memoryStream = new MemoryStream(); - using var unseekableStream = new UnseekableWriteStream(memoryStream); - var matFileWriter = _maybeOptions switch - { - { } options => new MatFileWriter(unseekableStream, options), - _ => new MatFileWriter(unseekableStream), - }; - - matFileWriter.Write(matFile); - return memoryStream.ToArray(); - } - } -} \ No newline at end of file diff --git a/MatFileHandler.Tests/MatTestDataFactory.cs b/MatFileHandler.Tests/MatTestDataFactory.cs deleted file mode 100755 index ac6935d..0000000 --- a/MatFileHandler.Tests/MatTestDataFactory.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2017-2018 Alexander Luzgarev - -using System.IO; - -namespace MatFileHandler.Tests -{ - /// - /// Factory providing the parsed contents of .mat files. - /// - public class MatTestDataFactory : AbstractTestDataFactory - { - /// - /// Initializes a new instance of the class. - /// - /// Directory containing test files. - public MatTestDataFactory(string testDirectory) - : base( - testDirectory, - new ExtensionTestFilenameConvention("mat")) - { - } - - /// - /// Read and parse data from a .mat file. - /// - /// Input stream. - /// Parsed contents of the file. - protected override IMatFile ReadDataFromStream(Stream stream) - { - var matFileReader = new MatFileReader(stream); - var matFile = matFileReader.Read(); - return matFile; - } - } -} \ No newline at end of file diff --git a/MatFileHandler.Tests/PartialReadMatTestDataFactory.cs b/MatFileHandler.Tests/PartialReadMatTestDataFactory.cs deleted file mode 100644 index 1c277b6..0000000 --- a/MatFileHandler.Tests/PartialReadMatTestDataFactory.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2017-2018 Alexander Luzgarev - -using System.IO; - -namespace MatFileHandler.Tests -{ - /// - /// Factory providing the parsed contents of .mat files, - /// wrapped in a . - /// - public class PartialReadMatTestDataFactory : MatTestDataFactory - { - /// - /// Initializes a new instance of the class. - /// - /// Directory containing test files. - public PartialReadMatTestDataFactory(string testDirectory) - : base(testDirectory) - { - } - - /// - /// Read and parse data from a .mat file. - /// - /// Input stream. - /// Parsed contents of the file. - protected override IMatFile ReadDataFromStream(Stream stream) - { - using (var wrapper = new PartialUnseekableReadStream(stream)) - { - return base.ReadDataFromStream(wrapper); - } - } - } -} diff --git a/MatFileHandler.Tests/PartialUnseekableReadStream.cs b/MatFileHandler.Tests/PartialUnseekableReadStream.cs index a78104c..f3e01e7 100644 --- a/MatFileHandler.Tests/PartialUnseekableReadStream.cs +++ b/MatFileHandler.Tests/PartialUnseekableReadStream.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.IO; @@ -9,7 +7,7 @@ namespace MatFileHandler.Tests /// A stream which wraps another stream and only reads one byte at a time, /// while forbidding seeking in it. /// - internal class PartialUnseekableReadStream : Stream + internal sealed class PartialUnseekableReadStream : Stream { private readonly Stream _baseStream; diff --git a/MatFileHandler.Tests/UnalignedMatTestDataFactory.cs b/MatFileHandler.Tests/UnalignedMatTestDataFactory.cs deleted file mode 100644 index 8cf2f48..0000000 --- a/MatFileHandler.Tests/UnalignedMatTestDataFactory.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2017-2018 Alexander Luzgarev - -using System.IO; - -namespace MatFileHandler.Tests -{ - /// - /// Factory providing the parsed contents of .mat files, - /// which start at a non-8 byte-aligned offset in the stream. - /// - public class UnalignedMatTestDataFactory : MatTestDataFactory - { - /// - /// Initializes a new instance of the class. - /// - /// Directory containing test files. - public UnalignedMatTestDataFactory(string testDirectory) - : base(testDirectory) - { - } - - /// - protected override IMatFile ReadDataFromStream(Stream stream) - { - using (var ms = new MemoryStream()) - { - ms.Seek(3, SeekOrigin.Begin); - - stream.CopyTo(ms); - - ms.Seek(3, SeekOrigin.Begin); - - return base.ReadDataFromStream(ms); - } - } - } -} diff --git a/MatFileHandler.Tests/UnseekableWriteStream.cs b/MatFileHandler.Tests/UnseekableWriteStream.cs index 2133768..8a7f62c 100644 --- a/MatFileHandler.Tests/UnseekableWriteStream.cs +++ b/MatFileHandler.Tests/UnseekableWriteStream.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.IO; @@ -8,7 +6,7 @@ namespace MatFileHandler.Tests /// /// A stream which wraps another stream and forbids seeking in it. /// - internal class UnseekableWriteStream : Stream + internal sealed class UnseekableWriteStream : Stream { public UnseekableWriteStream(Stream baseStream) { diff --git a/MatFileHandler.Tests/test-data/good/table-deep.mat b/MatFileHandler.Tests/test-data/good/table-deep.mat new file mode 100644 index 0000000..37c70e7 Binary files /dev/null and b/MatFileHandler.Tests/test-data/good/table-deep.mat differ diff --git a/MatFileHandler/ArrayFlags.cs b/MatFileHandler/ArrayFlags.cs index 4167e9d..fdcaa34 100755 --- a/MatFileHandler/ArrayFlags.cs +++ b/MatFileHandler/ArrayFlags.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; namespace MatFileHandler @@ -154,4 +152,4 @@ namespace MatFileHandler /// public uint NzMax; } -} \ No newline at end of file +} diff --git a/MatFileHandler/ChecksumCalculatingStream.cs b/MatFileHandler/ChecksumCalculatingStream.cs index e0278f0..c566e0d 100644 --- a/MatFileHandler/ChecksumCalculatingStream.cs +++ b/MatFileHandler/ChecksumCalculatingStream.cs @@ -1,92 +1,92 @@ -// Copyright 2017-2018 Alexander Luzgarev - -using System; -using System.IO; - -namespace MatFileHandler -{ - /// - /// A stream that calculates Adler32 checksum of everything - /// written to it before passing to another stream. - /// - internal class ChecksumCalculatingStream : Stream - { - private const uint BigPrime = 0xFFF1; - private readonly Stream _stream; - private uint s1 = 1; - private uint s2 = 0; - - /// - /// Initializes a new instance of the class. - /// - /// Wrapped stream. - public ChecksumCalculatingStream(Stream stream) - { - _stream = stream; - } - - /// - public override bool CanRead => false; - - /// - public override bool CanSeek => false; - - /// - public override bool CanWrite => true; - - /// - public override long Length => throw new NotImplementedException(); - - /// - public override long Position - { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); - } - - /// - public override void Flush() - { - _stream.Flush(); - } - - /// - public override int Read(byte[] buffer, int offset, int count) - { - throw new NotImplementedException(); - } - - /// - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotImplementedException(); - } - - /// - public override void SetLength(long value) - { - throw new NotImplementedException(); - } - - /// - 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); - } - - /// - /// Calculate the checksum of everything written to the stream so far. - /// - /// Checksum of everything written to the stream so far. - public uint GetCrc() - { - return (s2 << 16) | s1; - } - } -} \ No newline at end of file +using System; +using System.IO; + +namespace MatFileHandler +{ + /// + /// A stream that calculates Adler32 checksum of everything + /// written to it before passing to another stream. + /// + internal class ChecksumCalculatingStream : Stream + { + private const uint BigPrime = 0xFFF1; + private readonly Stream _stream; + private uint s1; + private uint s2; + + /// + /// Initializes a new instance of the class. + /// + /// Wrapped stream. + public ChecksumCalculatingStream(Stream stream) + { + _stream = stream; + s1 = 1; + s2 = 0; + } + + /// + public override bool CanRead => false; + + /// + public override bool CanSeek => false; + + /// + public override bool CanWrite => true; + + /// + public override long Length => throw new NotImplementedException(); + + /// + public override long Position + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + + /// + public override void Flush() + { + _stream.Flush(); + } + + /// + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + /// + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + /// + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + /// + 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); + } + + /// + /// Calculate the checksum of everything written to the stream so far. + /// + /// Checksum of everything written to the stream so far. + public uint GetCrc() + { + return (s2 << 16) | s1; + } + } +} diff --git a/MatFileHandler/ComplexOf.cs b/MatFileHandler/ComplexOf.cs index cbf7317..8c6f11f 100755 --- a/MatFileHandler/ComplexOf.cs +++ b/MatFileHandler/ComplexOf.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; namespace MatFileHandler @@ -8,7 +6,7 @@ namespace MatFileHandler /// A structure representing a complex number where real and imaginary parts are of type T. /// /// Type of real and imaginary parts. - public struct ComplexOf : IEquatable> + public readonly struct ComplexOf : IEquatable> where T : struct { /// @@ -71,10 +69,11 @@ namespace MatFileHandler /// True iff another object is a complex number equal to this. public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) + if (obj is null) { return false; } + return obj is ComplexOf other && Equals(other); } @@ -90,4 +89,4 @@ namespace MatFileHandler } } } -} \ No newline at end of file +} diff --git a/MatFileHandler/CompressionUsage.cs b/MatFileHandler/CompressionUsage.cs index 719084a..ff4068b 100644 --- a/MatFileHandler/CompressionUsage.cs +++ b/MatFileHandler/CompressionUsage.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler { /// diff --git a/MatFileHandler/DataBuilder.cs b/MatFileHandler/DataBuilder.cs index 88042b1..5e43768 100755 --- a/MatFileHandler/DataBuilder.cs +++ b/MatFileHandler/DataBuilder.cs @@ -1,10 +1,9 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Collections.Generic; using System.Linq; using System.Numerics; +#pragma warning disable CA1822 namespace MatFileHandler { /// @@ -52,7 +51,7 @@ namespace MatFileHandler { if (data.Length != dimensions.NumberOfElements()) { - throw new ArgumentException("Data size does not match the specified dimensions", "data"); + throw new ArgumentException("Data size does not match the specified dimensions", nameof(data)); } return new MatNumericalArrayOf(GetStandardFlags(), dimensions, string.Empty, data); } @@ -157,7 +156,7 @@ namespace MatFileHandler return new MatFile(variables); } - private ArrayFlags ConstructArrayFlags(ArrayType class_, bool isComplex = false, bool isLogical = false) + private static ArrayFlags ConstructArrayFlags(ArrayType class_, bool isComplex = false, bool isLogical = false) { return new ArrayFlags { @@ -167,7 +166,7 @@ namespace MatFileHandler }; } - private ArrayFlags GetStandardFlags() + private static ArrayFlags GetStandardFlags() { if (typeof(T) == typeof(sbyte)) { @@ -266,4 +265,4 @@ namespace MatFileHandler }; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/DataElement.cs b/MatFileHandler/DataElement.cs index 1da50e8..b57994d 100755 --- a/MatFileHandler/DataElement.cs +++ b/MatFileHandler/DataElement.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler { /// @@ -8,4 +6,4 @@ namespace MatFileHandler internal class DataElement { } -} \ No newline at end of file +} diff --git a/MatFileHandler/DataElementConverter.cs b/MatFileHandler/DataElementConverter.cs index 0aceeb6..d2a8bb2 100755 --- a/MatFileHandler/DataElementConverter.cs +++ b/MatFileHandler/DataElementConverter.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Collections.Generic; using System.Linq; @@ -71,16 +69,21 @@ namespace MatFileHandler { throw new NotSupportedException("Only 2-dimensional sparse arrays are supported"); } - if (data == null) + if (data is null) { - throw new ArgumentException("Null data found.", "data"); + throw new ArgumentException("Null data found.", nameof(data)); } - var elements = + + + + + var maybeElements = ConvertDataToSparseProperType(data, flags.ArrayFlags.Variable.HasFlag(Variable.IsLogical)); - if (elements == null) + if (maybeElements is not { } elements) { throw new HandlerException("Couldn't read sparse array."); } + var dataDictionary = ConvertMatlabSparseToDictionary(rowIndex, columnIndex, j => elements[j]); return new MatSparseArrayOf(flags, dimensions, name, dataDictionary); @@ -117,15 +120,12 @@ namespace MatFileHandler switch (flags.Class) { case ArrayType.MxChar: - switch (realData) + return realData switch { - case MiNum dataByte: - return ConvertToMatCharArray(flags, dimensions, name, dataByte); - case MiNum dataUshort: - return ConvertToMatCharArray(flags, dimensions, name, dataUshort); - default: - throw new NotSupportedException("Only utf8, utf16 or ushort char arrays are supported."); - } + MiNum dataByte => ConvertToMatCharArray(flags, dimensions, name, dataByte), + MiNum dataUshort => ConvertToMatCharArray(flags, dimensions, name, dataUshort), + _ => throw new NotSupportedException("Only utf8, utf16 or ushort char arrays are supported."), + }; case ArrayType.MxDouble: case ArrayType.MxSingle: case ArrayType.MxInt8: @@ -184,13 +184,12 @@ namespace MatFileHandler { return DataExtraction.GetDataAsUInt8(data).Select(x => x != 0).ToArray() as T[]; } - switch (data) + + return data switch { - case MiNum _: - return DataExtraction.GetDataAsDouble(data) as T[]; - default: - throw new NotSupportedException(); - } + MiNum => DataExtraction.GetDataAsDouble(data) as T[], + _ => throw new NotSupportedException(), + }; } private static MatCharArrayOf ConvertToMatCharArray( @@ -243,4 +242,4 @@ namespace MatFileHandler }; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/DataElementReader.cs b/MatFileHandler/DataElementReader.cs index 9da8ab6..34f2bed 100755 --- a/MatFileHandler/DataElementReader.cs +++ b/MatFileHandler/DataElementReader.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Collections.Generic; using System.IO; @@ -30,54 +28,33 @@ namespace MatFileHandler /// /// Input reader. /// Data element. - public DataElement Read(BinaryReader reader) + public DataElement? Read(BinaryReader reader) { - var (dataReader, tag) = ReadTag(reader); - DataElement result; - switch (tag.Type) + var maybeTagPair = ReadTag(reader); + if (maybeTagPair is not { } tagPair) { - case DataType.MiInt8: - result = ReadNum(tag, dataReader); - break; - case DataType.MiUInt8: - case DataType.MiUtf8: - result = ReadNum(tag, dataReader); - break; - case DataType.MiInt16: - result = ReadNum(tag, dataReader); - break; - case DataType.MiUInt16: - case DataType.MiUtf16: - result = ReadNum(tag, dataReader); - break; - case DataType.MiInt32: - result = ReadNum(tag, dataReader); - break; - case DataType.MiUInt32: - result = ReadNum(tag, dataReader); - break; - case DataType.MiSingle: - result = ReadNum(tag, dataReader); - break; - case DataType.MiDouble: - result = ReadNum(tag, dataReader); - break; - case DataType.MiInt64: - result = ReadNum(tag, dataReader); - break; - case DataType.MiUInt64: - result = ReadNum(tag, dataReader); - break; - case DataType.MiMatrix: - result = ReadMatrix(tag, dataReader); - break; - case DataType.MiCompressed: - result = ReadCompressed(tag, dataReader); - break; - default: - throw new NotSupportedException("Unknown element."); + return null; } + var (dataReader, tag) = tagPair; + + var result = tag.Type switch + { + DataType.MiInt8 => ReadNum(tag, dataReader), + DataType.MiUInt8 or DataType.MiUtf8 => ReadNum(tag, dataReader), + DataType.MiInt16 => ReadNum(tag, dataReader), + DataType.MiUInt16 or DataType.MiUtf16 => ReadNum(tag, dataReader), + DataType.MiInt32 => ReadNum(tag, dataReader), + DataType.MiUInt32 => ReadNum(tag, dataReader), + DataType.MiSingle => ReadNum(tag, dataReader), + DataType.MiDouble => ReadNum(tag, dataReader), + DataType.MiInt64 => ReadNum(tag, dataReader), + DataType.MiUInt64 => ReadNum(tag, dataReader), + DataType.MiMatrix => ReadMatrix(tag, dataReader), + DataType.MiCompressed => ReadCompressed(tag, dataReader), + _ => throw new NotSupportedException("Unknown element."), + }; + if (tag.Type != DataType.MiCompressed) { var position = reader.BaseStream.Position; @@ -122,7 +99,7 @@ namespace MatFileHandler private static ArrayFlags ReadArrayFlags(DataElement element) { var flagData = (element as MiNum)?.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 @@ -194,9 +171,31 @@ namespace MatFileHandler }; } - private static (BinaryReader reader, Tag tag) ReadTag(BinaryReader reader) + private static int? TryReadInt32(BinaryReader reader) { - var type = reader.ReadInt32(); + 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 typeHi = type >> 16; if (typeHi == 0) { @@ -206,13 +205,13 @@ namespace MatFileHandler else { var length = typeHi; - type = type & 0xffff; + type &= 0xffff; var smallReader = new BinaryReader(new MemoryStream(reader.ReadBytes(4))); return (smallReader, new Tag((DataType)type, length)); } } - private DataElement ContinueReadingCellArray( + private MatCellArray ContinueReadingCellArray( BinaryReader reader, ArrayFlags flags, int[] dimensions, @@ -241,7 +240,7 @@ namespace MatFileHandler var classNameElement = Read(reader) as MiNum ?? throw new HandlerException("Unexpected type in class name."); var className = ReadName(classNameElement); - var dataElement = Read(reader); + var dataElement = Read(reader) ?? throw new HandlerException("Missing opaque data element."); var data = ReadData(dataElement); if (data is MatNumericalArrayOf linkElement) { @@ -258,11 +257,11 @@ namespace MatFileHandler } else { - return new Opaque(name, typeDescription, className, new int[] { }, data, subsystemData); + return new Opaque(name, typeDescription, className, Array.Empty(), data, subsystemData); } } - private DataElement ContinueReadingSparseArray( + private MatArray ContinueReadingSparseArray( BinaryReader reader, DataElement firstElement, int[] dimensions, @@ -273,7 +272,7 @@ namespace MatFileHandler throw new HandlerException("Unexpected type in row indices of a sparse array."); var columnIndex = Read(reader) as MiNum ?? throw new HandlerException("Unexpected type in column indices of a sparse array."); - var data = Read(reader); + var data = Read(reader) ?? throw new HandlerException("Missing sparse array data."); if (sparseArrayFlags.ArrayFlags.Variable.HasFlag(Variable.IsLogical)) { return DataElementConverter.ConvertToMatSparseArrayOf( @@ -287,7 +286,7 @@ namespace MatFileHandler if (sparseArrayFlags.ArrayFlags.Variable.HasFlag(Variable.IsComplex)) { - var imaginaryData = Read(reader); + var imaginaryData = Read(reader) ?? throw new HandlerException("Missing imaginary part of sparse array data."); return DataElementConverter.ConvertToMatSparseArrayOfComplex( sparseArrayFlags, dimensions, @@ -298,22 +297,20 @@ namespace MatFileHandler imaginaryData); } - switch (data) + return data switch { - case MiNum _: - return DataElementConverter.ConvertToMatSparseArrayOf( - sparseArrayFlags, - dimensions, - name, - rowIndex.Data, - columnIndex.Data, - data); - default: - throw new NotSupportedException("Only double and logical sparse arrays are supported."); - } + MiNum => DataElementConverter.ConvertToMatSparseArrayOf( + sparseArrayFlags, + dimensions, + name, + rowIndex.Data, + columnIndex.Data, + data), + _ => throw new NotSupportedException("Only double and logical sparse arrays are supported."), + }; } - private DataElement ContinueReadingStructure( + private MatStructureArray ContinueReadingStructure( BinaryReader reader, ArrayFlags flags, int[] dimensions, @@ -356,7 +353,7 @@ namespace MatFileHandler using (var positionTrackingStream = new PositionTrackingStream(bufferedStream)) using (var innerReader = new BinaryReader(positionTrackingStream)) { - element = Read(innerReader); + element = Read(innerReader) ?? throw new HandlerException("Missing compressed data."); } if (substream.Position != substream.Length) @@ -379,7 +376,7 @@ namespace MatFileHandler return MatArray.Empty(); } - var element1 = Read(reader); + var element1 = Read(reader) ?? throw new HandlerException("Missing matrix data."); var flags = ReadArrayFlags(element1); if (flags.Class == ArrayType.MxOpaque) { @@ -401,12 +398,12 @@ namespace MatFileHandler return ContinueReadingSparseArray(reader, element1, dimensions, name); } - var element4 = Read(reader); + var element4 = Read(reader) ?? throw new HandlerException("Missing matrix data."); var data = ReadData(element4); DataElement? imaginaryData = null; if (flags.Variable.HasFlag(Variable.IsComplex)) { - var element5 = Read(reader); + var element5 = Read(reader) ?? throw new HandlerException("Missing complex matrix data."); imaginaryData = ReadData(element5); } @@ -421,26 +418,23 @@ namespace MatFileHandler switch (flags.Class) { case ArrayType.MxChar: - switch (data) + return data switch { - case MiNum _: - return DataElementConverter.ConvertToMatNumericalArrayOf( - flags, - dimensions, - name, - data, - imaginaryData); - case MiNum _: - return DataElementConverter.ConvertToMatNumericalArrayOf( - flags, - dimensions, - name, - data, - imaginaryData); - default: - throw new NotSupportedException( - $"This type of char array ({data.GetType()}) is not supported."); - } + MiNum => DataElementConverter.ConvertToMatNumericalArrayOf( + flags, + dimensions, + name, + data, + imaginaryData), + MiNum => DataElementConverter.ConvertToMatNumericalArrayOf( + flags, + dimensions, + name, + data, + imaginaryData), + _ => throw new NotSupportedException( + $"This type of char array ({data.GetType()}) is not supported."), + }; case ArrayType.MxInt8: return DataElementConverter.ConvertToMatNumericalArrayOf( flags, @@ -526,4 +520,4 @@ namespace MatFileHandler } } } -} \ No newline at end of file +} diff --git a/MatFileHandler/DataExtraction.cs b/MatFileHandler/DataExtraction.cs index 455fd92..5802065 100755 --- a/MatFileHandler/DataExtraction.cs +++ b/MatFileHandler/DataExtraction.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; namespace MatFileHandler @@ -16,31 +14,21 @@ namespace MatFileHandler /// Contents of the elements, converted to Double. public static double[] GetDataAsDouble(DataElement element) { - switch (element) + return element switch { - case MiNum sbyteElement: - return SbyteToDouble(sbyteElement.Data); - case MiNum byteElement: - return ByteToDouble(byteElement.Data); - case MiNum intElement: - return IntToDouble(intElement.Data); - case MiNum uintElement: - return UintToDouble(uintElement.Data); - case MiNum shortElement: - return ShortToDouble(shortElement.Data); - case MiNum ushortElement: - return UshortToDouble(ushortElement.Data); - case MiNum longElement: - return LongToDouble(longElement.Data); - case MiNum ulongElement: - return UlongToDouble(ulongElement.Data); - case MiNum floatElement: - return FloatToDouble(floatElement.Data); - case MiNum doubleElement: - return doubleElement.Data; - } - throw new HandlerException( - $"Expected data element that would be convertible to double, found {element.GetType()}."); + MiNum sbyteElement => SbyteToDouble(sbyteElement.Data), + MiNum byteElement => ByteToDouble(byteElement.Data), + MiNum intElement => IntToDouble(intElement.Data), + MiNum uintElement => UintToDouble(uintElement.Data), + MiNum shortElement => ShortToDouble(shortElement.Data), + MiNum ushortElement => UshortToDouble(ushortElement.Data), + MiNum longElement => LongToDouble(longElement.Data), + MiNum ulongElement => UlongToDouble(ulongElement.Data), + MiNum floatElement => FloatToDouble(floatElement.Data), + MiNum doubleElement => doubleElement.Data, + _ => throw new HandlerException( + $"Expected data element that would be convertible to double, found {element.GetType()}."), + }; } /// @@ -50,31 +38,21 @@ namespace MatFileHandler /// Contents of the elements, converted to Single. public static float[] GetDataAsSingle(DataElement element) { - switch (element) + return element switch { - case MiNum sbyteElement: - return SbyteToSingle(sbyteElement.Data); - case MiNum byteElement: - return ByteToSingle(byteElement.Data); - case MiNum intElement: - return IntToSingle(intElement.Data); - case MiNum uintElement: - return UintToSingle(uintElement.Data); - case MiNum shortElement: - return ShortToSingle(shortElement.Data); - case MiNum ushortElement: - return UshortToSingle(ushortElement.Data); - case MiNum longElement: - return LongToSingle(longElement.Data); - case MiNum ulongElement: - return UlongToSingle(ulongElement.Data); - case MiNum floatElement: - return floatElement.Data; - case MiNum doubleElement: - return DoubleToSingle(doubleElement.Data); - } - throw new HandlerException( - $"Expected data element that would be convertible to float, found {element.GetType()}."); + MiNum sbyteElement => SbyteToSingle(sbyteElement.Data), + MiNum byteElement => ByteToSingle(byteElement.Data), + MiNum intElement => IntToSingle(intElement.Data), + MiNum uintElement => UintToSingle(uintElement.Data), + MiNum shortElement => ShortToSingle(shortElement.Data), + MiNum ushortElement => UshortToSingle(ushortElement.Data), + MiNum longElement => LongToSingle(longElement.Data), + MiNum ulongElement => UlongToSingle(ulongElement.Data), + MiNum floatElement => floatElement.Data, + MiNum doubleElement => DoubleToSingle(doubleElement.Data), + _ => throw new HandlerException( + $"Expected data element that would be convertible to float, found {element.GetType()}."), + }; } /// @@ -84,31 +62,21 @@ namespace MatFileHandler /// Contents of the elements, converted to Int8. public static sbyte[] GetDataAsInt8(DataElement element) { - switch (element) + return element switch { - case MiNum sbyteElement: - return sbyteElement.Data; - case MiNum byteElement: - return ByteToSByte(byteElement.Data); - case MiNum intElement: - return IntToSByte(intElement.Data); - case MiNum uintElement: - return UintToSByte(uintElement.Data); - case MiNum shortElement: - return ShortToSByte(shortElement.Data); - case MiNum ushortElement: - return UshortToSByte(ushortElement.Data); - case MiNum longElement: - return LongToSByte(longElement.Data); - case MiNum ulongElement: - return UlongToSByte(ulongElement.Data); - case MiNum floatElement: - return SingleToSByte(floatElement.Data); - case MiNum doubleElement: - return DoubleToSByte(doubleElement.Data); - } - throw new HandlerException( - $"Expected data element that would be convertible to int8, found {element.GetType()}."); + MiNum sbyteElement => sbyteElement.Data, + MiNum byteElement => ByteToSByte(byteElement.Data), + MiNum intElement => IntToSByte(intElement.Data), + MiNum uintElement => UintToSByte(uintElement.Data), + MiNum shortElement => ShortToSByte(shortElement.Data), + MiNum ushortElement => UshortToSByte(ushortElement.Data), + MiNum longElement => LongToSByte(longElement.Data), + MiNum ulongElement => UlongToSByte(ulongElement.Data), + MiNum floatElement => SingleToSByte(floatElement.Data), + MiNum doubleElement => DoubleToSByte(doubleElement.Data), + _ => throw new HandlerException( + $"Expected data element that would be convertible to int8, found {element.GetType()}."), + }; } /// @@ -118,31 +86,21 @@ namespace MatFileHandler /// Contents of the elements, converted to UInt8. public static byte[] GetDataAsUInt8(DataElement element) { - switch (element) + return element switch { - case MiNum sbyteElement: - return SbyteToByte(sbyteElement.Data); - case MiNum byteElement: - return byteElement.Data; - case MiNum intElement: - return IntToByte(intElement.Data); - case MiNum uintElement: - return UintToByte(uintElement.Data); - case MiNum shortElement: - return ShortToByte(shortElement.Data); - case MiNum ushortElement: - return UshortToByte(ushortElement.Data); - case MiNum longElement: - return LongToByte(longElement.Data); - case MiNum ulongElement: - return UlongToByte(ulongElement.Data); - case MiNum floatElement: - return SingleToByte(floatElement.Data); - case MiNum doubleElement: - return DoubleToByte(doubleElement.Data); - } - throw new HandlerException( - $"Expected data element that would be convertible to uint8, found {element.GetType()}."); + MiNum sbyteElement => SbyteToByte(sbyteElement.Data), + MiNum byteElement => byteElement.Data, + MiNum intElement => IntToByte(intElement.Data), + MiNum uintElement => UintToByte(uintElement.Data), + MiNum shortElement => ShortToByte(shortElement.Data), + MiNum ushortElement => UshortToByte(ushortElement.Data), + MiNum longElement => LongToByte(longElement.Data), + MiNum ulongElement => UlongToByte(ulongElement.Data), + MiNum floatElement => SingleToByte(floatElement.Data), + MiNum doubleElement => DoubleToByte(doubleElement.Data), + _ => throw new HandlerException( + $"Expected data element that would be convertible to uint8, found {element.GetType()}."), + }; } /// @@ -152,31 +110,21 @@ namespace MatFileHandler /// Contents of the elements, converted to Int16. public static short[] GetDataAsInt16(DataElement element) { - switch (element) + return element switch { - case MiNum sbyteElement: - return SbyteToInt16(sbyteElement.Data); - case MiNum byteElement: - return ByteToInt16(byteElement.Data); - case MiNum intElement: - return IntToInt16(intElement.Data); - case MiNum uintElement: - return UintToInt16(uintElement.Data); - case MiNum shortElement: - return shortElement.Data; - case MiNum ushortElement: - return UshortToInt16(ushortElement.Data); - case MiNum longElement: - return LongToInt16(longElement.Data); - case MiNum ulongElement: - return UlongToInt16(ulongElement.Data); - case MiNum floatElement: - return SingleToInt16(floatElement.Data); - case MiNum doubleElement: - return DoubleToInt16(doubleElement.Data); - } - throw new HandlerException( - $"Expected data element that would be convertible to int16, found {element.GetType()}."); + MiNum sbyteElement => SbyteToInt16(sbyteElement.Data), + MiNum byteElement => ByteToInt16(byteElement.Data), + MiNum intElement => IntToInt16(intElement.Data), + MiNum uintElement => UintToInt16(uintElement.Data), + MiNum shortElement => shortElement.Data, + MiNum ushortElement => UshortToInt16(ushortElement.Data), + MiNum longElement => LongToInt16(longElement.Data), + MiNum ulongElement => UlongToInt16(ulongElement.Data), + MiNum floatElement => SingleToInt16(floatElement.Data), + MiNum doubleElement => DoubleToInt16(doubleElement.Data), + _ => throw new HandlerException( + $"Expected data element that would be convertible to int16, found {element.GetType()}."), + }; } /// @@ -186,31 +134,21 @@ namespace MatFileHandler /// Contents of the elements, converted to UInt16. public static ushort[] GetDataAsUInt16(DataElement element) { - switch (element) + return element switch { - case MiNum sbyteElement: - return SbyteToUInt16(sbyteElement.Data); - case MiNum byteElement: - return ByteToUInt16(byteElement.Data); - case MiNum intElement: - return IntToUInt16(intElement.Data); - case MiNum uintElement: - return UintToUInt16(uintElement.Data); - case MiNum shortElement: - return ShortToUInt16(shortElement.Data); - case MiNum ushortElement: - return ushortElement.Data; - case MiNum longElement: - return LongToUInt16(longElement.Data); - case MiNum ulongElement: - return UlongToUInt16(ulongElement.Data); - case MiNum floatElement: - return SingleToUInt16(floatElement.Data); - case MiNum doubleElement: - return DoubleToUInt16(doubleElement.Data); - } - throw new HandlerException( - $"Expected data element that would be convertible to uint16, found {element.GetType()}."); + MiNum sbyteElement => SbyteToUInt16(sbyteElement.Data), + MiNum byteElement => ByteToUInt16(byteElement.Data), + MiNum intElement => IntToUInt16(intElement.Data), + MiNum uintElement => UintToUInt16(uintElement.Data), + MiNum shortElement => ShortToUInt16(shortElement.Data), + MiNum ushortElement => ushortElement.Data, + MiNum longElement => LongToUInt16(longElement.Data), + MiNum ulongElement => UlongToUInt16(ulongElement.Data), + MiNum floatElement => SingleToUInt16(floatElement.Data), + MiNum doubleElement => DoubleToUInt16(doubleElement.Data), + _ => throw new HandlerException( + $"Expected data element that would be convertible to uint16, found {element.GetType()}."), + }; } /// @@ -220,31 +158,21 @@ namespace MatFileHandler /// Contents of the elements, converted to Int32. public static int[] GetDataAsInt32(DataElement element) { - switch (element) + return element switch { - case MiNum sbyteElement: - return SbyteToInt32(sbyteElement.Data); - case MiNum byteElement: - return ByteToInt32(byteElement.Data); - case MiNum intElement: - return intElement.Data; - case MiNum uintElement: - return UintToInt32(uintElement.Data); - case MiNum shortElement: - return ShortToInt32(shortElement.Data); - case MiNum ushortElement: - return UshortToInt32(ushortElement.Data); - case MiNum longElement: - return LongToInt32(longElement.Data); - case MiNum ulongElement: - return UlongToInt32(ulongElement.Data); - case MiNum floatElement: - return SingleToInt32(floatElement.Data); - case MiNum doubleElement: - return DoubleToInt32(doubleElement.Data); - } - throw new HandlerException( - $"Expected data element that would be convertible to int32, found {element.GetType()}."); + MiNum sbyteElement => SbyteToInt32(sbyteElement.Data), + MiNum byteElement => ByteToInt32(byteElement.Data), + MiNum intElement => intElement.Data, + MiNum uintElement => UintToInt32(uintElement.Data), + MiNum shortElement => ShortToInt32(shortElement.Data), + MiNum ushortElement => UshortToInt32(ushortElement.Data), + MiNum longElement => LongToInt32(longElement.Data), + MiNum ulongElement => UlongToInt32(ulongElement.Data), + MiNum floatElement => SingleToInt32(floatElement.Data), + MiNum doubleElement => DoubleToInt32(doubleElement.Data), + _ => throw new HandlerException( + $"Expected data element that would be convertible to int32, found {element.GetType()}."), + }; } /// @@ -254,31 +182,21 @@ namespace MatFileHandler /// Contents of the elements, converted to UInt32. public static uint[] GetDataAsUInt32(DataElement element) { - switch (element) + return element switch { - case MiNum sbyteElement: - return SbyteToUInt32(sbyteElement.Data); - case MiNum byteElement: - return ByteToUInt32(byteElement.Data); - case MiNum intElement: - return IntToUInt32(intElement.Data); - case MiNum uintElement: - return uintElement.Data; - case MiNum shortElement: - return ShortToUInt32(shortElement.Data); - case MiNum ushortElement: - return UshortToUInt32(ushortElement.Data); - case MiNum longElement: - return LongToUInt32(longElement.Data); - case MiNum ulongElement: - return UlongToUInt32(ulongElement.Data); - case MiNum floatElement: - return SingleToUInt32(floatElement.Data); - case MiNum doubleElement: - return DoubleToUInt32(doubleElement.Data); - } - throw new HandlerException( - $"Expected data element that would be convertible to uint32, found {element.GetType()}."); + MiNum sbyteElement => SbyteToUInt32(sbyteElement.Data), + MiNum byteElement => ByteToUInt32(byteElement.Data), + MiNum intElement => IntToUInt32(intElement.Data), + MiNum uintElement => uintElement.Data, + MiNum shortElement => ShortToUInt32(shortElement.Data), + MiNum ushortElement => UshortToUInt32(ushortElement.Data), + MiNum longElement => LongToUInt32(longElement.Data), + MiNum ulongElement => UlongToUInt32(ulongElement.Data), + MiNum floatElement => SingleToUInt32(floatElement.Data), + MiNum doubleElement => DoubleToUInt32(doubleElement.Data), + _ => throw new HandlerException( + $"Expected data element that would be convertible to uint32, found {element.GetType()}."), + }; } /// @@ -288,31 +206,21 @@ namespace MatFileHandler /// Contents of the elements, converted to Int64. public static long[] GetDataAsInt64(DataElement element) { - switch (element) + return element switch { - case MiNum sbyteElement: - return SbyteToInt64(sbyteElement.Data); - case MiNum byteElement: - return ByteToInt64(byteElement.Data); - case MiNum intElement: - return IntToInt64(intElement.Data); - case MiNum uintElement: - return UintToInt64(uintElement.Data); - case MiNum shortElement: - return ShortToInt64(shortElement.Data); - case MiNum ushortElement: - return UshortToInt64(ushortElement.Data); - case MiNum longElement: - return longElement.Data; - case MiNum ulongElement: - return UlongToInt64(ulongElement.Data); - case MiNum floatElement: - return SingleToInt64(floatElement.Data); - case MiNum doubleElement: - return DoubleToInt64(doubleElement.Data); - } - throw new HandlerException( - $"Expected data element that would be convertible to int64, found {element.GetType()}."); + MiNum sbyteElement => SbyteToInt64(sbyteElement.Data), + MiNum byteElement => ByteToInt64(byteElement.Data), + MiNum intElement => IntToInt64(intElement.Data), + MiNum uintElement => UintToInt64(uintElement.Data), + MiNum shortElement => ShortToInt64(shortElement.Data), + MiNum ushortElement => UshortToInt64(ushortElement.Data), + MiNum longElement => longElement.Data, + MiNum ulongElement => UlongToInt64(ulongElement.Data), + MiNum floatElement => SingleToInt64(floatElement.Data), + MiNum doubleElement => DoubleToInt64(doubleElement.Data), + _ => throw new HandlerException( + $"Expected data element that would be convertible to int64, found {element.GetType()}."), + }; } /// @@ -322,31 +230,21 @@ namespace MatFileHandler /// Contents of the elements, converted to UInt64. public static ulong[] GetDataAsUInt64(DataElement element) { - switch (element) + return element switch { - case MiNum sbyteElement: - return SbyteToUInt64(sbyteElement.Data); - case MiNum byteElement: - return ByteToUInt64(byteElement.Data); - case MiNum intElement: - return IntToUInt64(intElement.Data); - case MiNum uintElement: - return UintToUInt64(uintElement.Data); - case MiNum shortElement: - return ShortToUInt64(shortElement.Data); - case MiNum ushortElement: - return UshortToUInt64(ushortElement.Data); - case MiNum longElement: - return LongToUInt64(longElement.Data); - case MiNum ulongElement: - return ulongElement.Data; - case MiNum floatElement: - return SingleToUInt64(floatElement.Data); - case MiNum doubleElement: - return DoubleToUInt64(doubleElement.Data); - } - throw new HandlerException( - $"Expected data element that would be convertible to uint64, found {element.GetType()}."); + MiNum sbyteElement => SbyteToUInt64(sbyteElement.Data), + MiNum byteElement => ByteToUInt64(byteElement.Data), + MiNum intElement => IntToUInt64(intElement.Data), + MiNum uintElement => UintToUInt64(uintElement.Data), + MiNum shortElement => ShortToUInt64(shortElement.Data), + MiNum ushortElement => UshortToUInt64(ushortElement.Data), + MiNum longElement => LongToUInt64(longElement.Data), + MiNum ulongElement => ulongElement.Data, + MiNum floatElement => SingleToUInt64(floatElement.Data), + MiNum doubleElement => DoubleToUInt64(doubleElement.Data), + _ => throw new HandlerException( + $"Expected data element that would be convertible to uint64, found {element.GetType()}."), + }; } // * to double @@ -1395,4 +1293,4 @@ namespace MatFileHandler return result; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/DataTypeExtensions.cs b/MatFileHandler/DataTypeExtensions.cs index 2b923b4..a27d600 100755 --- a/MatFileHandler/DataTypeExtensions.cs +++ b/MatFileHandler/DataTypeExtensions.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; namespace MatFileHandler @@ -16,41 +14,21 @@ namespace MatFileHandler /// Size in bytes. public static int Size(this DataType type) { - switch (type) + return type switch { - 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); - } + 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), + }; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/DatetimeAdapter.cs b/MatFileHandler/DatetimeAdapter.cs index 5f0f604..8d817a9 100644 --- a/MatFileHandler/DatetimeAdapter.cs +++ b/MatFileHandler/DatetimeAdapter.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Linq; using System.Numerics; @@ -12,8 +10,6 @@ namespace MatFileHandler public class DatetimeAdapter { private readonly double[] data; - private readonly double[] data2; - private readonly int[] dimensions; private readonly DateTimeOffset epoch; @@ -36,15 +32,13 @@ namespace MatFileHandler case IArrayOf dataArray: data = dataArray.ConvertToDoubleArray() ?? throw new HandlerException("Cannot extract data for the datetime adapter."); - data2 = new double[data.Length]; - dimensions = dataArray.Dimensions; + Dimensions = dataArray.Dimensions; break; case IArrayOf dataComplex: var complexData = dataComplex.ConvertToComplexArray() ?? throw new HandlerException("Cannot extract data for the datetime adapter."); data = complexData.Select(c => c.Real).ToArray(); - data2 = complexData.Select(c => c.Imaginary).ToArray(); - dimensions = dataComplex.Dimensions; + Dimensions = dataComplex.Dimensions; break; default: throw new HandlerException("Datetime data not found."); @@ -54,7 +48,7 @@ namespace MatFileHandler /// /// Gets datetime array dimensions. /// - public int[] Dimensions => dimensions; + public int[] Dimensions { get; } /// /// Gets values of datetime object at given position in the array converted to . @@ -66,12 +60,12 @@ namespace MatFileHandler get { var milliseconds = data[Dimensions.DimFlatten(list)]; - if (milliseconds < -62_135_596_800_000.0 || milliseconds > 253_402_300_799_999.0) + return milliseconds switch { - return null; - } - return epoch.AddMilliseconds(milliseconds); + < -62_135_596_800_000.0 or > 253_402_300_799_999.0 => null, + _ => epoch.AddMilliseconds(milliseconds), + }; } } } -} \ No newline at end of file +} diff --git a/MatFileHandler/DimensionCalculator.cs b/MatFileHandler/DimensionCalculator.cs index ef0605c..9ef5686 100755 --- a/MatFileHandler/DimensionCalculator.cs +++ b/MatFileHandler/DimensionCalculator.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Linq; @@ -81,4 +79,4 @@ namespace MatFileHandler } } } -} \ No newline at end of file +} diff --git a/MatFileHandler/DurationAdapter.cs b/MatFileHandler/DurationAdapter.cs index aad282e..38de265 100644 --- a/MatFileHandler/DurationAdapter.cs +++ b/MatFileHandler/DurationAdapter.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; namespace MatFileHandler @@ -9,7 +7,6 @@ namespace MatFileHandler /// public class DurationAdapter { - private readonly int[] dimensions; private readonly double[] data; /// @@ -27,13 +24,13 @@ namespace MatFileHandler var dataObject = matObject["millis", 0]; data = dataObject.ConvertToDoubleArray() ?? throw new HandlerException("Cannot extract data for the duration adapter."); - dimensions = dataObject.Dimensions; + Dimensions = dataObject.Dimensions; } /// /// Gets duration array dimensions. /// - public int[] Dimensions => dimensions; + public int[] Dimensions { get; } /// /// Gets duration object at given position. @@ -42,4 +39,4 @@ namespace MatFileHandler /// Value. public TimeSpan this[params int[] list] => TimeSpan.FromTicks((long)(10000.0 * data[Dimensions.DimFlatten(list)])); } -} \ No newline at end of file +} diff --git a/MatFileHandler/EnumAdapter.cs b/MatFileHandler/EnumAdapter.cs index ada3cba..cab7535 100644 --- a/MatFileHandler/EnumAdapter.cs +++ b/MatFileHandler/EnumAdapter.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler { /// @@ -57,4 +55,4 @@ namespace MatFileHandler /// public IArrayOf Values { get; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/FakeWriter.cs b/MatFileHandler/FakeWriter.cs index 6e83fdf..5a37fd7 100644 --- a/MatFileHandler/FakeWriter.cs +++ b/MatFileHandler/FakeWriter.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Collections.Generic; using System.Linq; @@ -202,32 +200,27 @@ namespace MatFileHandler WriteDataElement(buffer); } - private unsafe int GetLengthOfByteArray(int dataLength) + private static unsafe int GetLengthOfByteArray(int dataLength) where T : unmanaged { return dataLength * sizeof(T); } - private unsafe int GetLengthOfPairOfByteArrays(ComplexOf[] data) + private static unsafe int GetLengthOfPairOfByteArrays(ComplexOf[] data) where T : unmanaged { return data.Length * sizeof(T); } - private unsafe int GetLengthOfPairOfByteArrays(Complex[] data) + private static unsafe int GetLengthOfPairOfByteArrays(Complex[] data) { return data.Length * sizeof(double); } - private int CalculatePadding(int length) + private static int CalculatePadding(int length) { var rem = length % 8; - if (rem == 0) - { - return 0; - } - - return 8 - rem; + return rem == 0 ? 0 : 8 - rem; } private void WriteDataElement(int dataLength) @@ -275,7 +268,7 @@ namespace MatFileHandler } } - private (int rowIndexLength, int columnIndexLength, int dataLength, uint nonZero) PrepareSparseArrayData( + private static (int rowIndexLength, int columnIndexLength, int dataLength, uint nonZero) PrepareSparseArrayData( ISparseArrayOf array) where T : struct, IEquatable { @@ -293,7 +286,7 @@ namespace MatFileHandler private void WriteFieldNames(IEnumerable fieldNames) { var fieldNamesArray = fieldNames.Select(name => Encoding.ASCII.GetBytes(name)).ToArray(); - var maxFieldName = fieldNamesArray.Select(name => name.Length).Max() + 1; + var maxFieldName = fieldNamesArray.Max(name => name.Length) + 1; WriteDataElement(GetLengthOfByteArray(1)); var buffer = new byte[fieldNamesArray.Length * maxFieldName]; var startPosition = 0; @@ -395,4 +388,4 @@ namespace MatFileHandler fakeWriter => fakeWriter.WriteNumericalArrayContents(numericalArray, name)); } } -} \ No newline at end of file +} diff --git a/MatFileHandler/HandlerException.cs b/MatFileHandler/HandlerException.cs index fbc6c96..02fc50d 100755 --- a/MatFileHandler/HandlerException.cs +++ b/MatFileHandler/HandlerException.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; namespace MatFileHandler @@ -19,4 +17,4 @@ namespace MatFileHandler { } } -} \ No newline at end of file +} diff --git a/MatFileHandler/Header.cs b/MatFileHandler/Header.cs index 06b68ce..dcfe568 100755 --- a/MatFileHandler/Header.cs +++ b/MatFileHandler/Header.cs @@ -1,10 +1,10 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Globalization; using System.IO; using System.Linq; +#if !NET461 using System.Runtime.InteropServices; +#endif namespace MatFileHandler { @@ -72,11 +72,9 @@ namespace MatFileHandler var version = reader.ReadInt16(); var endian = reader.ReadInt16(); var isLittleEndian = endian == 19785; - if (!isLittleEndian) - { - throw new NotSupportedException("Big-endian files are not supported."); - } - return new Header(text, subsystemDataOffset, version); + return isLittleEndian + ? new Header(text, subsystemDataOffset, version) + : throw new NotSupportedException("Big-endian files are not supported."); } private static string GetOperatingSystem() @@ -100,4 +98,4 @@ namespace MatFileHandler #endif } } -} \ No newline at end of file +} diff --git a/MatFileHandler/IArray.cs b/MatFileHandler/IArray.cs index a0b5dd4..c723d62 100755 --- a/MatFileHandler/IArray.cs +++ b/MatFileHandler/IArray.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Numerics; @@ -49,4 +47,4 @@ namespace MatFileHandler /// Array of values of the array, converted to Complex, or null if the conversion is not possible. Complex[]? ConvertToComplexArray(); } -} \ No newline at end of file +} diff --git a/MatFileHandler/IArrayOf.cs b/MatFileHandler/IArrayOf.cs index 22a959a..3b882c7 100755 --- a/MatFileHandler/IArrayOf.cs +++ b/MatFileHandler/IArrayOf.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler { /// @@ -36,4 +34,4 @@ namespace MatFileHandler /// Index of the element. T this[params int[] list] { get; set; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/ICellArray.cs b/MatFileHandler/ICellArray.cs index c18b717..979ef2b 100755 --- a/MatFileHandler/ICellArray.cs +++ b/MatFileHandler/ICellArray.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler { /// @@ -8,4 +6,4 @@ namespace MatFileHandler public interface ICellArray : IArrayOf { } -} \ No newline at end of file +} diff --git a/MatFileHandler/ICharArray.cs b/MatFileHandler/ICharArray.cs index bcd1763..87730b2 100755 --- a/MatFileHandler/ICharArray.cs +++ b/MatFileHandler/ICharArray.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler { /// @@ -10,6 +8,8 @@ namespace MatFileHandler /// /// Gets the contained string. /// +#pragma warning disable CA1716, CA1720 string String { get; } +#pragma warning restore CA1716, CA1720 } -} \ No newline at end of file +} diff --git a/MatFileHandler/IMatFile.cs b/MatFileHandler/IMatFile.cs index f961286..cfad8a2 100755 --- a/MatFileHandler/IMatFile.cs +++ b/MatFileHandler/IMatFile.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler { /// @@ -27,6 +25,6 @@ namespace MatFileHandler /// The name of the variable to get. /// When this method returns, contains the variable with the specified name, if it is found; otherwise, null. /// True if the file contains a variable with the specified name; otherwise, false. - public bool TryGetVariable(string name, out IVariable? variable); + bool TryGetVariable(string name, out IVariable? variable); } -} \ No newline at end of file +} diff --git a/MatFileHandler/IMatObject.cs b/MatFileHandler/IMatObject.cs index faab81e..3f385f8 100644 --- a/MatFileHandler/IMatObject.cs +++ b/MatFileHandler/IMatObject.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System.Collections.Generic; namespace MatFileHandler diff --git a/MatFileHandler/ISparseArrayOf.cs b/MatFileHandler/ISparseArrayOf.cs index 83229d2..8d6df94 100755 --- a/MatFileHandler/ISparseArrayOf.cs +++ b/MatFileHandler/ISparseArrayOf.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System.Collections.Generic; namespace MatFileHandler @@ -17,4 +15,4 @@ namespace MatFileHandler /// new IReadOnlyDictionary<(int row, int column), T> Data { get; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/IStructureArray.cs b/MatFileHandler/IStructureArray.cs index 27f37c3..ed51fcf 100755 --- a/MatFileHandler/IStructureArray.cs +++ b/MatFileHandler/IStructureArray.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System.Collections.Generic; namespace MatFileHandler @@ -21,4 +19,4 @@ namespace MatFileHandler /// Index of the element in the structure array. IArray this[string field, params int[] list] { get; set; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/IVariable.cs b/MatFileHandler/IVariable.cs index b3297da..39bfa10 100755 --- a/MatFileHandler/IVariable.cs +++ b/MatFileHandler/IVariable.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler { /// @@ -22,4 +20,4 @@ namespace MatFileHandler /// bool IsGlobal { get; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/MatArray.cs b/MatFileHandler/MatArray.cs index ea5cfeb..f54c2ef 100755 --- a/MatFileHandler/MatArray.cs +++ b/MatFileHandler/MatArray.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Numerics; @@ -51,7 +49,7 @@ namespace MatFileHandler /// Empty array. public static MatArray Empty() { - return new MatArray(new ArrayFlags { Class = ArrayType.MxCell, Variable = 0 }, new int[] { }, string.Empty); + return new MatArray(new ArrayFlags { Class = ArrayType.MxCell, Variable = 0 }, Array.Empty(), string.Empty); } /// @@ -78,4 +76,4 @@ namespace MatFileHandler return null; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/MatCellArray.cs b/MatFileHandler/MatCellArray.cs index 8ac8973..5f81f61 100755 --- a/MatFileHandler/MatCellArray.cs +++ b/MatFileHandler/MatCellArray.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System.Collections.Generic; using System.Linq; @@ -33,4 +31,4 @@ namespace MatFileHandler set => Data[Dimensions.DimFlatten(indices)] = value; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/MatCharArrayOf.cs b/MatFileHandler/MatCharArrayOf.cs index 5a94ea0..7095d2b 100755 --- a/MatFileHandler/MatCharArrayOf.cs +++ b/MatFileHandler/MatCharArrayOf.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler { /// @@ -54,4 +52,4 @@ namespace MatFileHandler } } } -} \ No newline at end of file +} diff --git a/MatFileHandler/MatFile.cs b/MatFileHandler/MatFile.cs index 4b01602..e814fff 100755 --- a/MatFileHandler/MatFile.cs +++ b/MatFileHandler/MatFile.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System.Collections.Generic; using System.Linq; @@ -18,11 +16,7 @@ namespace MatFileHandler /// List of variables. public MatFile(IEnumerable variables) { - _variables = new Dictionary(); - foreach (var variable in variables) - { - _variables[variable.Name] = variable; - } + _variables = variables.ToDictionary(v => v.Name, v => v); } /// @@ -41,4 +35,4 @@ namespace MatFileHandler return _variables.TryGetValue(name, out value); } } -} \ No newline at end of file +} diff --git a/MatFileHandler/MatFileHandler.csproj b/MatFileHandler/MatFileHandler.csproj index ffa8e5f..dcebe31 100755 --- a/MatFileHandler/MatFileHandler.csproj +++ b/MatFileHandler/MatFileHandler.csproj @@ -1,52 +1,44 @@  netstandard2.0;net461;net472 - 1.4.0-beta5 + 1.4.0-beta6 MatFileHandler A library for reading and writing MATLAB .mat files. Alexander Luzgarev MatFileHandler provides a simple interface for reading and writing MATLAB .mat files (of so-called "Level 5") and extracting the contents of numerical arrays, logical arrays, sparse arrays, char arrays, cell arrays and structure arrays. Copyright 2017-2020 Alexander Luzgarev MIT - https://github.com/mahalex/MatFileHandler - https://github.com/mahalex/MatFileHandler/releases/tag/v$(PackageVersion) + https://git.mahalex.net/mahalex/MatFileHandler + https://git.mahalex.net/mahalex/MatFileHandler/releases/ README.md Matlab - https://github.com/mahalex/MatFileHandler + https://git.mahalex.net/mahalex/MatFileHandler bin\$(Configuration)\ $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml true - 10.0 + true + latest enable true true true snupkg true + latest-Recommended - - - - - ..\MatFileHandler.ruleset - - - ..\MatFileHandler.ruleset - - - - - - - All - + + + + + + diff --git a/MatFileHandler/MatFileReader.cs b/MatFileHandler/MatFileReader.cs index adc29ac..a7d0b24 100755 --- a/MatFileHandler/MatFileReader.cs +++ b/MatFileHandler/MatFileReader.cs @@ -1,6 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - -using System; using System.Collections.Generic; using System.IO; @@ -28,10 +25,8 @@ namespace MatFileHandler /// Contents of the file. public IMatFile Read() { - using (var reader = new BinaryReader(new PositionTrackingStream(Stream))) - { - return Read(reader); - } + using var reader = new BinaryReader(new PositionTrackingStream(Stream)); + return Read(reader); } /// @@ -51,26 +46,24 @@ namespace MatFileHandler var dataElementReader = new DataElementReader(subsystemData); while (true) { - try - { - var position = reader.BaseStream.Position; - var dataElement = dataElementReader.Read(reader); - if (position == subsystemDataOffset) - { - var subsystemDataElement = dataElement as IArrayOf - ?? throw new HandlerException("Cannot parse subsystem data element."); - var newSubsystemData = ReadSubsystemData(subsystemDataElement.Data, subsystemData); - subsystemData.Set(newSubsystemData); - } - else - { - variables.Add(new RawVariable(position, dataElement)); - } - } - catch (EndOfStreamException) + var position = reader.BaseStream.Position; + var dataElement = dataElementReader.Read(reader); + if (dataElement is null) { break; } + + if (position == subsystemDataOffset) + { + var subsystemDataElement = dataElement as IArrayOf + ?? throw new HandlerException("Cannot parse subsystem data element."); + var newSubsystemData = ReadSubsystemData(subsystemDataElement.Data, subsystemData); + subsystemData.Set(newSubsystemData); + } + else + { + variables.Add(new RawVariable(position, dataElement)); + } } return variables; @@ -88,23 +81,21 @@ namespace MatFileHandler return ReadRawVariables(reader, subsystemDataOffset, subsystemData); } - private static IMatFile Read(BinaryReader reader) + private static MatFile Read(BinaryReader reader) { var header = ReadHeader(reader); var rawVariables = ReadRawVariables(reader, header.SubsystemDataOffset); var variables = new List(); foreach (var variable in rawVariables) { - var array = variable.DataElement as MatArray; - if (array is null) + if (variable.DataElement is MatArray array) { - continue; + variables.Add( + new MatVariable( + array, + array.Name, + array.Flags.Variable.HasFlag(Variable.IsGlobal))); } - - variables.Add(new MatVariable( - array, - array.Name, - array.Flags.Variable.HasFlag(Variable.IsGlobal))); } return new MatFile(variables); @@ -120,4 +111,4 @@ namespace MatFileHandler return SubsystemDataReader.Read(bytes, subsystemData); } } -} \ No newline at end of file +} diff --git a/MatFileHandler/MatFileWriter.cs b/MatFileHandler/MatFileWriter.cs index b6077a1..67648fe 100755 --- a/MatFileHandler/MatFileWriter.cs +++ b/MatFileHandler/MatFileWriter.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Collections.Generic; using System.IO; @@ -47,30 +45,28 @@ namespace MatFileHandler public void Write(IMatFile file) { var header = Header.CreateNewHeader(); - using (var writer = new BinaryWriter(Stream)) + using var writer = new BinaryWriter(Stream); + WriteHeader(writer, header); + foreach (var variable in file.Variables) { - WriteHeader(writer, header); - foreach (var variable in file.Variables) + switch (_options.UseCompression) { - switch (_options.UseCompression) - { - case CompressionUsage.Always: - if (Stream.CanSeek) - { - WriteCompressedVariableToSeekableStream(writer, variable); - } - else - { - WriteCompressedVariableToUnseekableStream(writer, variable); - } + case CompressionUsage.Always: + if (Stream.CanSeek) + { + WriteCompressedVariableToSeekableStream(writer, variable); + } + else + { + WriteCompressedVariableToUnseekableStream(writer, variable); + } - break; - case CompressionUsage.Never: - WriteVariable(writer, variable); - break; - default: - throw new ArgumentOutOfRangeException(); - } + break; + case CompressionUsage.Never: + WriteVariable(writer, variable); + break; + default: + throw new NotImplementedException(); } } } @@ -98,7 +94,7 @@ namespace MatFileHandler return (s2 << 16) | s1; } - private void WriteHeader(BinaryWriter writer, Header header) + private static void WriteHeader(BinaryWriter writer, Header header) { writer.Write(Encoding.UTF8.GetBytes(header.Text)); writer.Write(header.SubsystemDataOffset); @@ -106,28 +102,19 @@ namespace MatFileHandler writer.Write((short)19785); // Magic number, 'IM'. } - private void WriteTag(BinaryWriter writer, Tag tag) + private static void WriteTag(BinaryWriter writer, Tag tag) { writer.Write((int)tag.Type); writer.Write(tag.Length); } - private void WriteShortTag(BinaryWriter writer, Tag tag) + private static void WriteShortTag(BinaryWriter writer, Tag tag) { writer.Write((short)tag.Type); writer.Write((short)tag.Length); } - private void WritePadding(BinaryWriter writer) - { - var positionMod8 = writer.BaseStream.Position % 8; - if (positionMod8 != 0) - { - writer.Write(new byte[8 - positionMod8]); - } - } - - private void WriteDataElement(BinaryWriter writer, DataType type, byte[] data) + private static void WriteDataElement(BinaryWriter writer, DataType type, byte[] data) { if (data.Length > 4) { @@ -152,14 +139,14 @@ namespace MatFileHandler } } - private void WriteDimensions(BinaryWriter writer, int[] dimensions) + private static void WriteDimensions(BinaryWriter writer, int[] dimensions) { var buffer = ConvertToByteArray(dimensions); WriteDataElement(writer, DataType.MiInt32, buffer); } - private byte[] ConvertToByteArray(T[] data) - where T : struct + private static byte[] ConvertToByteArray(T[] data) + where T : struct { int size; if (typeof(T) == typeof(sbyte)) @@ -202,6 +189,10 @@ namespace MatFileHandler { size = sizeof(double); } + else if (typeof(T) == typeof(bool)) + { + size = sizeof(bool); + } else { throw new NotSupportedException(); @@ -211,51 +202,51 @@ namespace MatFileHandler return buffer; } - private (byte[] real, byte[] imaginary) ConvertToPairOfByteArrays(ComplexOf[] data) + private static (byte[] real, byte[] imaginary) ConvertToPairOfByteArrays(ComplexOf[] data) where T : struct { return (ConvertToByteArray(data.Select(x => x.Real).ToArray()), ConvertToByteArray(data.Select(x => x.Imaginary).ToArray())); } - private (byte[] real, byte[] imaginary) ConvertToPairOfByteArrays(Complex[] data) + private static (byte[] real, byte[] imaginary) ConvertToPairOfByteArrays(Complex[] data) { return (ConvertToByteArray(data.Select(x => x.Real).ToArray()), ConvertToByteArray(data.Select(x => x.Imaginary).ToArray())); } - private void WriteComplexValues(BinaryWriter writer, DataType type, (byte[] real, byte[] complex) data) + private static void WriteComplexValues(BinaryWriter writer, DataType type, (byte[] real, byte[] complex) data) { WriteDataElement(writer, type, data.real); WriteDataElement(writer, type, data.complex); } - private void WriteArrayFlags(BinaryWriter writer, ArrayFlags flags) + private static void WriteArrayFlags(BinaryWriter writer, ArrayFlags flags) { var flag = (byte)flags.Variable; WriteTag(writer, new Tag(DataType.MiUInt32, 8)); writer.Write((byte)flags.Class); writer.Write(flag); - writer.Write(new byte[] { 0, 0, 0, 0, 0, 0 }); + writer.Write([0, 0, 0, 0, 0, 0]); } - private void WriteSparseArrayFlags(BinaryWriter writer, SparseArrayFlags flags) + private static void WriteSparseArrayFlags(BinaryWriter writer, SparseArrayFlags flags) { var flag = (byte)flags.ArrayFlags.Variable; WriteTag(writer, new Tag(DataType.MiUInt32, 8)); writer.Write((byte)flags.ArrayFlags.Class); writer.Write(flag); - writer.Write(new byte[] { 0, 0 }); + writer.Write([0, 0]); writer.Write(flags.NzMax); } - private void WriteName(BinaryWriter writer, string name) + private static void WriteName(BinaryWriter writer, string name) { var nameBytes = Encoding.ASCII.GetBytes(name); WriteDataElement(writer, DataType.MiInt8, nameBytes); } - private void WriteNumericalArrayValues(BinaryWriter writer, IArray value) + private static void WriteNumericalArrayValues(BinaryWriter writer, IArray value) { switch (value) { @@ -290,10 +281,7 @@ namespace MatFileHandler WriteDataElement(writer, DataType.MiDouble, ConvertToByteArray(doubleArray.Data)); break; case IArrayOf boolArray: - WriteDataElement( - writer, - DataType.MiUInt8, - boolArray.Data.Select(element => element ? (byte)1 : (byte)0).ToArray()); + WriteDataElement(writer, DataType.MiUInt8, ConvertToByteArray(boolArray.Data)); break; case IArrayOf> complexSbyteArray: WriteComplexValues(writer, DataType.MiInt8, ConvertToPairOfByteArrays(complexSbyteArray.Data)); @@ -330,63 +318,39 @@ namespace MatFileHandler } } - private ArrayFlags GetArrayFlags(IArray array, bool isGlobal) + private static ArrayFlags GetArrayFlags(IArray array, bool isGlobal) { var variableFlags = isGlobal ? Variable.IsGlobal : 0; - switch (array) + return array switch { - case IArrayOf _: - return new ArrayFlags(ArrayType.MxInt8, variableFlags); - case IArrayOf _: - return new ArrayFlags(ArrayType.MxUInt8, variableFlags); - case IArrayOf _: - return new ArrayFlags(ArrayType.MxInt16, variableFlags); - case IArrayOf _: - return new ArrayFlags(ArrayType.MxUInt16, variableFlags); - case IArrayOf _: - return new ArrayFlags(ArrayType.MxInt32, variableFlags); - case IArrayOf _: - return new ArrayFlags(ArrayType.MxUInt32, variableFlags); - case IArrayOf _: - return new ArrayFlags(ArrayType.MxInt64, variableFlags); - case IArrayOf _: - return new ArrayFlags(ArrayType.MxUInt64, variableFlags); - case IArrayOf _: - return new ArrayFlags(ArrayType.MxSingle, variableFlags); - case IArrayOf _: - return new ArrayFlags(ArrayType.MxDouble, variableFlags); - case IArrayOf _: - return new ArrayFlags(ArrayType.MxUInt8, variableFlags | Variable.IsLogical); - case IArrayOf> _: - return new ArrayFlags(ArrayType.MxInt8, variableFlags | Variable.IsComplex); - case IArrayOf> _: - return new ArrayFlags(ArrayType.MxUInt8, variableFlags | Variable.IsComplex); - case IArrayOf> _: - return new ArrayFlags(ArrayType.MxInt16, variableFlags | Variable.IsComplex); - case IArrayOf> _: - return new ArrayFlags(ArrayType.MxUInt16, variableFlags | Variable.IsComplex); - case IArrayOf> _: - return new ArrayFlags(ArrayType.MxInt32, variableFlags | Variable.IsComplex); - case IArrayOf> _: - return new ArrayFlags(ArrayType.MxUInt32, variableFlags | Variable.IsComplex); - case IArrayOf> _: - return new ArrayFlags(ArrayType.MxInt64, variableFlags | Variable.IsComplex); - case IArrayOf> _: - return new ArrayFlags(ArrayType.MxUInt64, variableFlags | Variable.IsComplex); - case IArrayOf> _: - return new ArrayFlags(ArrayType.MxSingle, variableFlags | Variable.IsComplex); - case IArrayOf _: - return new ArrayFlags(ArrayType.MxDouble, variableFlags | Variable.IsComplex); - case IStructureArray _: - return new ArrayFlags(ArrayType.MxStruct, variableFlags); - case ICellArray _: - return new ArrayFlags(ArrayType.MxCell, variableFlags); - default: - throw new NotSupportedException(); - } + IArrayOf => new ArrayFlags(ArrayType.MxInt8, variableFlags), + IArrayOf => new ArrayFlags(ArrayType.MxUInt8, variableFlags), + IArrayOf => new ArrayFlags(ArrayType.MxInt16, variableFlags), + IArrayOf => new ArrayFlags(ArrayType.MxUInt16, variableFlags), + IArrayOf => new ArrayFlags(ArrayType.MxInt32, variableFlags), + IArrayOf => new ArrayFlags(ArrayType.MxUInt32, variableFlags), + IArrayOf => new ArrayFlags(ArrayType.MxInt64, variableFlags), + IArrayOf => new ArrayFlags(ArrayType.MxUInt64, variableFlags), + IArrayOf => new ArrayFlags(ArrayType.MxSingle, variableFlags), + IArrayOf => new ArrayFlags(ArrayType.MxDouble, variableFlags), + IArrayOf => new ArrayFlags(ArrayType.MxUInt8, variableFlags | Variable.IsLogical), + IArrayOf> => new ArrayFlags(ArrayType.MxInt8, variableFlags | Variable.IsComplex), + IArrayOf> => new ArrayFlags(ArrayType.MxUInt8, variableFlags | Variable.IsComplex), + IArrayOf> => new ArrayFlags(ArrayType.MxInt16, variableFlags | Variable.IsComplex), + IArrayOf> => new ArrayFlags(ArrayType.MxUInt16, variableFlags | Variable.IsComplex), + IArrayOf> => new ArrayFlags(ArrayType.MxInt32, variableFlags | Variable.IsComplex), + IArrayOf> => new ArrayFlags(ArrayType.MxUInt32, variableFlags | Variable.IsComplex), + IArrayOf> => new ArrayFlags(ArrayType.MxInt64, variableFlags | Variable.IsComplex), + IArrayOf> => new ArrayFlags(ArrayType.MxUInt64, variableFlags | Variable.IsComplex), + IArrayOf> => new ArrayFlags(ArrayType.MxSingle, variableFlags | Variable.IsComplex), + IArrayOf => new ArrayFlags(ArrayType.MxDouble, variableFlags | Variable.IsComplex), + IStructureArray => new ArrayFlags(ArrayType.MxStruct, variableFlags), + ICellArray => new ArrayFlags(ArrayType.MxCell, variableFlags), + _ => throw new NotSupportedException(), + }; } - private SparseArrayFlags GetSparseArrayFlags(ISparseArrayOf array, bool isGlobal, uint nonZero) + private static SparseArrayFlags GetSparseArrayFlags(ISparseArrayOf array, bool isGlobal, uint nonZero) where T : struct { var flags = GetArrayFlags(array, isGlobal); @@ -401,12 +365,12 @@ namespace MatFileHandler }; } - private ArrayFlags GetCharArrayFlags(bool isGlobal) + private static ArrayFlags GetCharArrayFlags(bool isGlobal) { return new ArrayFlags(ArrayType.MxChar, isGlobal ? Variable.IsGlobal : 0); } - private void WriteWrappingContents( + private static void WriteWrappingContents( BinaryWriter writer, T array, Action lengthCalculator, @@ -426,7 +390,7 @@ namespace MatFileHandler writeContents(writer); } - private void WriteNumericalArrayContents(BinaryWriter writer, IArray array, string name, bool isGlobal) + private static void WriteNumericalArrayContents(BinaryWriter writer, IArray array, string name, bool isGlobal) { WriteArrayFlags(writer, GetArrayFlags(array, isGlobal)); WriteDimensions(writer, array.Dimensions); @@ -434,7 +398,7 @@ namespace MatFileHandler WriteNumericalArrayValues(writer, array); } - private void WriteNumericalArray( + private static void WriteNumericalArray( BinaryWriter writer, IArray numericalArray, string name = "", @@ -447,7 +411,7 @@ namespace MatFileHandler contentsWriter => { WriteNumericalArrayContents(contentsWriter, numericalArray, name, isGlobal); }); } - private void WriteCharArrayContents(BinaryWriter writer, ICharArray charArray, string name, bool isGlobal) + private static void WriteCharArrayContents(BinaryWriter writer, ICharArray charArray, string name, bool isGlobal) { WriteArrayFlags(writer, GetCharArrayFlags(isGlobal)); WriteDimensions(writer, charArray.Dimensions); @@ -456,7 +420,7 @@ namespace MatFileHandler WriteDataElement(writer, DataType.MiUtf16, ConvertToByteArray(array)); } - private void WriteCharArray(BinaryWriter writer, ICharArray charArray, string name, bool isGlobal) + private static void WriteCharArray(BinaryWriter writer, ICharArray charArray, string name, bool isGlobal) { WriteWrappingContents( writer, @@ -465,7 +429,7 @@ namespace MatFileHandler contentsWriter => { WriteCharArrayContents(contentsWriter, charArray, name, isGlobal); }); } - private void WriteSparseArrayValues( + private static void WriteSparseArrayValues( BinaryWriter writer, int[] rows, int[] columns, T[] data) where T : struct { @@ -475,9 +439,8 @@ namespace MatFileHandler { WriteDataElement(writer, DataType.MiDouble, ConvertToByteArray(data)); } - else if (data is Complex[]) + else if (data is Complex[] complexData) { - var complexData = data as Complex[]; WriteDataElement( writer, DataType.MiDouble, @@ -487,17 +450,16 @@ namespace MatFileHandler DataType.MiDouble, ConvertToByteArray(complexData.Select(c => c.Imaginary).ToArray())); } - else if (data is bool[]) + else if (data is bool[] boolData) { - var boolData = data as bool[]; WriteDataElement( writer, DataType.MiUInt8, - boolData.Select(element => element ? (byte)1 : (byte)0).ToArray()); + ConvertToByteArray(boolData)); } } - private (int[] rowIndex, int[] columnIndex, T[] data, uint nonZero) PrepareSparseArrayData( + private static (int[] rowIndex, int[] columnIndex, T[] data, uint nonZero) PrepareSparseArrayData( ISparseArrayOf array) where T : struct, IEquatable { @@ -520,7 +482,7 @@ namespace MatFileHandler return (rowIndexList.ToArray(), columnIndex, valuesList.ToArray(), (uint)rowIndexList.Count); } - private void WriteSparseArrayContents( + private static void WriteSparseArrayContents( BinaryWriter writer, ISparseArrayOf array, string name, @@ -534,7 +496,7 @@ namespace MatFileHandler WriteSparseArrayValues(writer, rows, columns, data); } - private void WriteSparseArray(BinaryWriter writer, ISparseArrayOf sparseArray, string name, bool isGlobal) + private static void WriteSparseArray(BinaryWriter writer, ISparseArrayOf sparseArray, string name, bool isGlobal) where T : unmanaged, IEquatable { WriteWrappingContents( @@ -544,11 +506,11 @@ namespace MatFileHandler contentsWriter => { WriteSparseArrayContents(contentsWriter, sparseArray, name, isGlobal); }); } - private void WriteFieldNames(BinaryWriter writer, IEnumerable fieldNames) + private static void WriteFieldNames(BinaryWriter writer, IEnumerable fieldNames) { var fieldNamesArray = fieldNames.Select(name => Encoding.ASCII.GetBytes(name)).ToArray(); - var maxFieldName = fieldNamesArray.Select(name => name.Length).Max() + 1; - WriteDataElement(writer, DataType.MiInt32, ConvertToByteArray(new[] { maxFieldName })); + var maxFieldName = fieldNamesArray.Max(name => name.Length) + 1; + WriteDataElement(writer, DataType.MiInt32, ConvertToByteArray([maxFieldName])); var buffer = new byte[fieldNamesArray.Length * maxFieldName]; var startPosition = 0; foreach (var name in fieldNamesArray) @@ -681,31 +643,27 @@ namespace MatFileHandler private void WriteCompressedVariableToUnseekableStream(BinaryWriter writer, IVariable variable) { - using (var compressedStream = new MemoryStream()) + using var compressedStream = new MemoryStream(); + uint crc; + using (var originalStream = new MemoryStream()) { - uint crc; - using (var originalStream = new MemoryStream()) - { - using (var internalWriter = new BinaryWriter(originalStream)) - { - WriteVariable(internalWriter, variable); - originalStream.Position = 0; - crc = CalculateAdler32Checksum(originalStream); - originalStream.Position = 0; - using (var compressionStream = - new DeflateStream(compressedStream, CompressionMode.Compress, leaveOpen: true)) - { - originalStream.CopyTo(compressionStream); - } - } - } - compressedStream.Position = 0; - WriteTag(writer, new Tag(DataType.MiCompressed, (int)(compressedStream.Length + 6))); - writer.Write((byte)0x78); - writer.Write((byte)0x9c); - compressedStream.CopyTo(writer.BaseStream); - writer.Write(BitConverter.GetBytes(crc).Reverse().ToArray()); + using var internalWriter = new BinaryWriter(originalStream); + WriteVariable(internalWriter, variable); + originalStream.Position = 0; + crc = CalculateAdler32Checksum(originalStream); + originalStream.Position = 0; + using var compressionStream = new DeflateStream( + compressedStream, + CompressionMode.Compress, + leaveOpen: true); + originalStream.CopyTo(compressionStream); } + compressedStream.Position = 0; + WriteTag(writer, new Tag(DataType.MiCompressed, (int)(compressedStream.Length + 6))); + writer.Write((byte)0x78); + writer.Write((byte)0x9c); + compressedStream.CopyTo(writer.BaseStream); + writer.Write(BitConverter.GetBytes(crc).Reverse().ToArray()); } } -} \ No newline at end of file +} diff --git a/MatFileHandler/MatFileWriterOptions.cs b/MatFileHandler/MatFileWriterOptions.cs index 07b09f2..18e9da3 100644 --- a/MatFileHandler/MatFileWriterOptions.cs +++ b/MatFileHandler/MatFileWriterOptions.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler { /// @@ -10,7 +8,7 @@ namespace MatFileHandler /// /// Gets default options. /// - public static MatFileWriterOptions Default => new MatFileWriterOptions + public static MatFileWriterOptions Default => new() { UseCompression = CompressionUsage.Always, }; @@ -20,4 +18,4 @@ namespace MatFileHandler /// public CompressionUsage UseCompression { get; set; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/MatNumericalArrayOf.cs b/MatFileHandler/MatNumericalArrayOf.cs index 762dea3..9a89da3 100755 --- a/MatFileHandler/MatNumericalArrayOf.cs +++ b/MatFileHandler/MatNumericalArrayOf.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Numerics; @@ -197,4 +195,4 @@ namespace MatFileHandler return result; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/MatSparseArrayOf.cs b/MatFileHandler/MatSparseArrayOf.cs index acd34d5..4a48e94 100755 --- a/MatFileHandler/MatSparseArrayOf.cs +++ b/MatFileHandler/MatSparseArrayOf.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Collections.Generic; using System.Linq; @@ -49,7 +47,7 @@ namespace MatFileHandler get { var rowAndColumn = GetRowAndColumn(list); - return DataDictionary.ContainsKey(rowAndColumn) ? DataDictionary[rowAndColumn] : default(T); + return DataDictionary.TryGetValue(rowAndColumn, out var result) ? result : default; } set => DataDictionary[GetRowAndColumn(list)] = value; } @@ -61,7 +59,7 @@ namespace MatFileHandler public override double[] ConvertToDoubleArray() { var data = ((IArrayOf)this).Data; - return data as double[] ?? data.Select(x => Convert.ToDouble(x)).ToArray(); + return data as double[] ?? data.Select(x => Convert.ToDouble(x, System.Globalization.CultureInfo.InvariantCulture)).ToArray(); } /// @@ -75,7 +73,7 @@ namespace MatFileHandler var result = new double[Dimensions[0], Dimensions[1]]; foreach (var pair in Data) { - result[pair.Key.row, pair.Key.column] = Convert.ToDouble(pair.Value); + result[pair.Key.row, pair.Key.column] = Convert.ToDouble(pair.Value, System.Globalization.CultureInfo.InvariantCulture); } return result; @@ -93,15 +91,12 @@ namespace MatFileHandler private (int row, int column) GetRowAndColumn(int[] indices) { - switch (indices.Length) + return indices.Length switch { - case 1: - return (indices[0] % Dimensions[0], indices[0] / Dimensions[0]); - case 2: - return (indices[0], indices[1]); - default: - throw new NotSupportedException("Invalid index for sparse array."); - } + 1 => (indices[0] % Dimensions[0], indices[0] / Dimensions[0]), + 2 => (indices[0], indices[1]), + _ => throw new NotSupportedException("Invalid index for sparse array."), + }; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/MatStructureArray.cs b/MatFileHandler/MatStructureArray.cs index a111bcd..e88f6e1 100755 --- a/MatFileHandler/MatStructureArray.cs +++ b/MatFileHandler/MatStructureArray.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Collections; using System.Collections.Generic; @@ -69,7 +67,7 @@ namespace MatFileHandler "Cannot set structure elements via this[params int[]] indexer. Use this[string, int[]] instead."); } - private IReadOnlyDictionary ExtractStructure(int i) + private MatStructureArrayElement ExtractStructure(int i) { return new MatStructureArrayElement(this, i); } diff --git a/MatFileHandler/MatVariable.cs b/MatFileHandler/MatVariable.cs index 233da06..ca54a1b 100755 --- a/MatFileHandler/MatVariable.cs +++ b/MatFileHandler/MatVariable.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler { /// @@ -27,4 +25,4 @@ namespace MatFileHandler /// public bool IsGlobal { get; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/MiNum.cs b/MatFileHandler/MiNum.cs index 9e4a8f1..f966a75 100755 --- a/MatFileHandler/MiNum.cs +++ b/MatFileHandler/MiNum.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler { /// @@ -23,4 +21,4 @@ namespace MatFileHandler /// public T[] Data { get; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/ObjectParser.cs b/MatFileHandler/ObjectParser.cs new file mode 100644 index 0000000..42a8c9a --- /dev/null +++ b/MatFileHandler/ObjectParser.cs @@ -0,0 +1,27 @@ +namespace MatFileHandler; + +/// +/// Parser for object data. +/// +internal static class ObjectParser +{ + /// + /// Parse object data. + /// + /// Opaque link array. + /// Current subsystem data. + /// Parsed object. + public static IArray ParseObject(MatNumericalArrayOf uintArray, SubsystemData subsystemData) + { + var (dimensions, indexToObjectId, classIndex) = DataElementReader.ParseOpaqueData(uintArray.Data); + return new OpaqueLink( + uintArray.Name, + string.Empty, + string.Empty, + dimensions, + uintArray, + indexToObjectId, + classIndex, + subsystemData); + } +} diff --git a/MatFileHandler/Opaque.cs b/MatFileHandler/Opaque.cs index ba5711a..d56c22f 100644 --- a/MatFileHandler/Opaque.cs +++ b/MatFileHandler/Opaque.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Numerics; @@ -62,4 +60,4 @@ namespace MatFileHandler /// public override double[]? ConvertToDoubleArray() => null; } -} \ No newline at end of file +} diff --git a/MatFileHandler/OpaqueLink.cs b/MatFileHandler/OpaqueLink.cs index 003b4dc..bf2d0b9 100644 --- a/MatFileHandler/OpaqueLink.cs +++ b/MatFileHandler/OpaqueLink.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Collections; using System.Collections.Generic; @@ -78,15 +76,7 @@ namespace MatFileHandler /// public IArray this[string field, params int[] list] { - get - { - if (TryGetValue(field, out var result, list)) - { - return result!; - } - - throw new IndexOutOfRangeException(); - } + get => TryGetValue(field, out var result, list) ? result! : throw new ArgumentOutOfRangeException(nameof(list)); set => throw new NotImplementedException(); } @@ -97,7 +87,7 @@ namespace MatFileHandler set => throw new NotImplementedException(); } - private IReadOnlyDictionary ExtractObject(int i) + private OpaqueObjectArrayElement ExtractObject(int i) { return new OpaqueObjectArrayElement(this, i); } @@ -106,7 +96,7 @@ namespace MatFileHandler { var index = Dimensions.DimFlatten(list); var maybeFieldIndex = SubsystemData.ClassInformation[ClassIndex].FindField(field); - if (!(maybeFieldIndex is int fieldIndex)) + if (maybeFieldIndex is not int fieldIndex) { output = default; return false; @@ -171,8 +161,8 @@ namespace MatFileHandler } } - /// #pragma warning disable CS8767 + /// public bool TryGetValue(string key, out IArray? value) #pragma warning restore CS8767 { @@ -186,4 +176,4 @@ namespace MatFileHandler } } } -} \ No newline at end of file +} diff --git a/MatFileHandler/PositionTrackingStream.cs b/MatFileHandler/PositionTrackingStream.cs index f62b931..8d35735 100644 --- a/MatFileHandler/PositionTrackingStream.cs +++ b/MatFileHandler/PositionTrackingStream.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.IO; @@ -48,7 +46,7 @@ internal sealed class PositionTrackingStream : Stream /// public override int Read(byte[] buffer, int offset, int count) { - int bytesRead = _baseStream.Read(buffer, offset, count); + var bytesRead = _baseStream.Read(buffer, offset, count); _position += bytesRead; diff --git a/MatFileHandler/RawVariable.cs b/MatFileHandler/RawVariable.cs index c384b21..984373a 100644 --- a/MatFileHandler/RawVariable.cs +++ b/MatFileHandler/RawVariable.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; namespace MatFileHandler @@ -33,4 +31,4 @@ namespace MatFileHandler /// public long Offset { get; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/StringAdapter.cs b/MatFileHandler/StringAdapter.cs index 8815d1c..4dcec46 100644 --- a/MatFileHandler/StringAdapter.cs +++ b/MatFileHandler/StringAdapter.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Text; @@ -10,7 +8,6 @@ namespace MatFileHandler /// public class StringAdapter { - private readonly int[] dimensions; private readonly string[] strings; /// @@ -28,13 +25,13 @@ namespace MatFileHandler var binaryData = matObject["any", 0] as IArrayOf ?? throw new HandlerException("Cannot extract string data."); - (dimensions, strings) = ParseBinaryData(binaryData.Data); + (Dimensions, strings) = ParseBinaryData(binaryData.Data); } /// /// Gets string array dimensions. /// - public int[] Dimensions => dimensions; + public int[] Dimensions { get; } /// /// Gets string object at given position. @@ -76,4 +73,4 @@ namespace MatFileHandler return (d, strings); } } -} \ No newline at end of file +} diff --git a/MatFileHandler/Substream.cs b/MatFileHandler/Substream.cs index df32ef0..767583b 100644 --- a/MatFileHandler/Substream.cs +++ b/MatFileHandler/Substream.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.IO; @@ -49,7 +47,7 @@ namespace MatFileHandler /// public override int Read(byte[] buffer, int offset, int count) { - int bytesRead = _baseStream.Read(buffer, offset, (int)Math.Min(count, Length - _bytesRead)); + var bytesRead = _baseStream.Read(buffer, offset, (int)Math.Min(count, Length - _bytesRead)); _bytesRead += bytesRead; diff --git a/MatFileHandler/SubsystemData.cs b/MatFileHandler/SubsystemData.cs index fe1947e..c29e545 100644 --- a/MatFileHandler/SubsystemData.cs +++ b/MatFileHandler/SubsystemData.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Collections.Generic; @@ -119,12 +117,7 @@ namespace MatFileHandler /// Field index. public int? FindField(string fieldName) { - if (fieldToIndex.TryGetValue(fieldName, out var index)) - { - return index; - } - - return null; + return fieldToIndex.TryGetValue(fieldName, out var index) ? index : null; } } @@ -192,4 +185,4 @@ namespace MatFileHandler public string[] FieldNames { get; } } } -} \ No newline at end of file +} diff --git a/MatFileHandler/SubsystemDataReader.cs b/MatFileHandler/SubsystemDataReader.cs index aca0bcd..5dd2197 100644 --- a/MatFileHandler/SubsystemDataReader.cs +++ b/MatFileHandler/SubsystemDataReader.cs @@ -1,8 +1,5 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -317,21 +314,11 @@ namespace MatFileHandler private static IArray TransformOpaqueData(IArray array, SubsystemData subsystemData) { - if (array is MatNumericalArrayOf uintArray) + if (array is MatNumericalArrayOf uintArray && + uintArray.Data.Length == 6 && + uintArray.Data[0] == 3707764736u) { - if (uintArray.Data[0] == 3707764736u) - { - var (dimensions, indexToObjectId, classIndex) = DataElementReader.ParseOpaqueData(uintArray.Data); - return new OpaqueLink( - uintArray.Name, - string.Empty, - string.Empty, - dimensions, - uintArray, - indexToObjectId, - classIndex, - subsystemData); - } + return ObjectParser.ParseObject(uintArray, subsystemData); } if (array is MatCellArray cellArray) @@ -344,10 +331,30 @@ namespace MatFileHandler } } + if (array is MatStructureArray structureArray) + { + var newFields = new Dictionary>(); + foreach (var pair in structureArray.Fields) + { + var values = pair.Value; + var transformedValues = new List(values.Count); + foreach (var value in values) + { + var transformedValue = TransformOpaqueData(value, subsystemData); + transformedValues.Add(transformedValue); + } + newFields[pair.Key] = transformedValues; + } + foreach (var pair in newFields) + { + structureArray.Fields[pair.Key] = pair.Value; + } + } + return array; } - private struct ObjectClassInformation + private readonly struct ObjectClassInformation { public ObjectClassInformation(int embeddedObjectPosition, int objectPosition, int loadingOrder, int classId) { @@ -402,4 +409,4 @@ namespace MatFileHandler => _keyFromEmbeddedObjectPosition[embeddedObjectPosition]; } } -} \ No newline at end of file +} diff --git a/MatFileHandler/TableAdapter.cs b/MatFileHandler/TableAdapter.cs index 1c4d44d..87686d7 100644 --- a/MatFileHandler/TableAdapter.cs +++ b/MatFileHandler/TableAdapter.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - using System; using System.Linq; @@ -87,9 +85,9 @@ namespace MatFileHandler .Where(i => VariableNames[i] == variableName) .Select(i => (int?)i) .FirstOrDefault(); - if (!(maybeIndex is int index)) + if (maybeIndex is not int index) { - throw new IndexOutOfRangeException($"Variable '{variableName}' not found."); + throw new ArgumentOutOfRangeException(nameof(variableName), $"Variable '{variableName}' not found."); } var data = matObject["data"] as ICellArray @@ -98,4 +96,4 @@ namespace MatFileHandler } } } -} \ No newline at end of file +} diff --git a/MatFileHandler/Tag.cs b/MatFileHandler/Tag.cs index 35f9a23..31ad51e 100755 --- a/MatFileHandler/Tag.cs +++ b/MatFileHandler/Tag.cs @@ -1,5 +1,3 @@ -// Copyright 2017-2018 Alexander Luzgarev - namespace MatFileHandler { /// @@ -114,4 +112,4 @@ namespace MatFileHandler /// public int ElementSize => Type.Size(); } -} \ No newline at end of file +} diff --git a/README.md b/README.md index 218deef..904889a 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ You can find (partial) technical description of MATLAB object data format This document briefly describes how to perform simple operations with .mat files using MatFileHandler. -If you have questions and/or ideas, you can [file a new issue](https://github.com/mahalex/MatFileHandler/issues/new) +If you have questions and/or ideas, you can [file a new issue](https://git.mahalex.net/mahalex/MatFileHandler/issues/new) or contact me directly at . ## Changelog diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c3ba68d..3488166 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -11,9 +11,9 @@ variables: steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 8.x' + displayName: 'Use .NET Core SDK 9.x' inputs: - version: 8.x + version: 9.x - script: dotnet build --configuration $(buildConfiguration) displayName: 'dotnet build $(buildConfiguration)'