Compare commits
23 Commits
dev/stream
...
master
Author | SHA1 | Date | |
---|---|---|---|
366e8e8ead | |||
c8acc1f93d | |||
003186b3b1 | |||
6c95ea7a4a | |||
d24f3c7250 | |||
6672ae4d24 | |||
919014a109 | |||
1822e75fd8 | |||
d87cc2c99c | |||
306992f969 | |||
5080f35e69 | |||
![]() |
cc3bdc000c | ||
841ac432a5 | |||
489d93eff1 | |||
dcfc685b62 | |||
e9582723c9 | |||
3ae8f06b3e | |||
a3602f80b4 | |||
![]() |
1eecbf2a79 | ||
![]() |
e8bf3f89ee | ||
![]() |
37feeb5863 | ||
7494502062 | |||
6069d16c07 |
248
.editorconfig
Normal file
248
.editorconfig
Normal file
@ -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
|
@ -1,68 +0,0 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace MatFileHandler.Tests
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Abstract factory of test data.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TTestData">Type of test data.</typeparam>
|
|
||||||
public abstract class AbstractTestDataFactory<TTestData>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="AbstractTestDataFactory{TTestData}"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dataDirectory">Directory with test files.</param>
|
|
||||||
/// <param name="testFilenameConvention">A convention used to filter test files.</param>
|
|
||||||
protected AbstractTestDataFactory(string dataDirectory, ITestFilenameConvention testFilenameConvention)
|
|
||||||
{
|
|
||||||
DataDirectory = dataDirectory;
|
|
||||||
TestFilenameConvention = testFilenameConvention;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string DataDirectory { get; }
|
|
||||||
|
|
||||||
private ITestFilenameConvention TestFilenameConvention { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get test data set by name.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dataSet">Name of the data set.</param>
|
|
||||||
/// <returns>Test data.</returns>
|
|
||||||
public TTestData this[string dataSet] =>
|
|
||||||
ReadTestData(FixPath(TestFilenameConvention.ConvertTestNameToFilename(dataSet)));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a sequence of all test data sets in the factory.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A sequence of data sets.</returns>
|
|
||||||
public IEnumerable<TTestData> GetAllTestData()
|
|
||||||
{
|
|
||||||
var files = Directory.EnumerateFiles(DataDirectory).Where(TestFilenameConvention.FilterFile);
|
|
||||||
foreach (var filename in files)
|
|
||||||
{
|
|
||||||
yield return ReadTestData(filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read test data from a stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream">Input stream.</param>
|
|
||||||
/// <returns>Test data.</returns>
|
|
||||||
protected abstract TTestData ReadDataFromStream(Stream stream);
|
|
||||||
|
|
||||||
private string FixPath(string filename) => Path.Combine(DataDirectory, filename);
|
|
||||||
|
|
||||||
private TTestData ReadTestData(string filename)
|
|
||||||
{
|
|
||||||
using (var stream = new FileStream(filename, FileMode.Open))
|
|
||||||
{
|
|
||||||
return ReadDataFromStream(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@ -127,4 +125,4 @@ namespace MatFileHandler.Tests
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
102
MatFileHandler.Tests/ChecksumCalculatingStreamTests.cs
Normal file
102
MatFileHandler.Tests/ChecksumCalculatingStreamTests.cs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace MatFileHandler.Tests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Tests for the <see cref="ChecksumCalculatingStream"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public class ChecksumCalculatingStreamTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Test writing various things.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bytes">Bytes to write.</param>
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(TestData))]
|
||||||
|
public void Test(byte[] bytes)
|
||||||
|
{
|
||||||
|
using var stream = new MemoryStream();
|
||||||
|
var sut = new ChecksumCalculatingStream(stream);
|
||||||
|
sut.Write(bytes, 0, bytes.Length);
|
||||||
|
var actual = sut.GetCrc();
|
||||||
|
|
||||||
|
var expected = ReferenceCalculation(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test data for <see cref="Test"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Test data.</returns>
|
||||||
|
public static TheoryData<byte[]> TestData()
|
||||||
|
{
|
||||||
|
var empty = new byte[1234];
|
||||||
|
var nonEmpty = new byte[12345];
|
||||||
|
for (var i = 0; i < 1234; i++)
|
||||||
|
{
|
||||||
|
nonEmpty[i] = (byte)((i * i) % 256);
|
||||||
|
}
|
||||||
|
return new TheoryData<byte[]>()
|
||||||
|
{
|
||||||
|
new byte[] { 0x00 },
|
||||||
|
new byte[] { 0x01 },
|
||||||
|
new byte[] { 0xff },
|
||||||
|
new byte[] { 0xff, 0xff },
|
||||||
|
new byte[] { 0xff, 0xff, 0xff },
|
||||||
|
new byte[] { 0xff, 0xff, 0xff, 0xff },
|
||||||
|
new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||||
|
new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||||
|
new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||||
|
new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||||
|
new byte[] { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||||
|
new byte[] { 0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13, 0x17, 0x1d },
|
||||||
|
empty,
|
||||||
|
nonEmpty,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<Stream> BinaryWriterAction(Action<BinaryWriter> action)
|
||||||
|
{
|
||||||
|
return stream =>
|
||||||
|
{
|
||||||
|
using var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true);
|
||||||
|
action(writer);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint ReferenceCalculation(byte[] bytes)
|
||||||
|
{
|
||||||
|
using var stream = new MemoryStream();
|
||||||
|
stream.Write(bytes, 0, bytes.Length);
|
||||||
|
stream.Position = 0;
|
||||||
|
return CalculateAdler32Checksum(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint CalculateAdler32Checksum(MemoryStream stream)
|
||||||
|
{
|
||||||
|
uint s1 = 1;
|
||||||
|
uint s2 = 0;
|
||||||
|
const uint bigPrime = 0xFFF1;
|
||||||
|
const int bufferSize = 2048;
|
||||||
|
var buffer = new byte[bufferSize];
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var bytesRead = stream.Read(buffer, 0, bufferSize);
|
||||||
|
for (var i = 0; i < bytesRead; i++)
|
||||||
|
{
|
||||||
|
s1 = (s1 + buffer[i]) % bigPrime;
|
||||||
|
s2 = (s2 + s1) % bigPrime;
|
||||||
|
}
|
||||||
|
if (bytesRead < bufferSize)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (s2 << 16) | s1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler.Tests
|
namespace MatFileHandler.Tests
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -47,4 +45,4 @@ namespace MatFileHandler.Tests
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly ulong[] UInt64Limits = { 0UL, 18446744073709551615UL };
|
public static readonly ulong[] UInt64Limits = { 0UL, 18446744073709551615UL };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace MatFileHandler.Tests
|
namespace MatFileHandler.Tests
|
||||||
@ -47,4 +45,4 @@ namespace MatFileHandler.Tests
|
|||||||
Assert.Equal(h1, h2);
|
Assert.Equal(h1, h2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace MatFileHandler.Tests
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A filename convention based on file extensions.
|
|
||||||
/// </summary>
|
|
||||||
internal class ExtensionTestFilenameConvention : ITestFilenameConvention
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ExtensionTestFilenameConvention"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="extension">File extension.</param>
|
|
||||||
public ExtensionTestFilenameConvention(string extension)
|
|
||||||
{
|
|
||||||
Extension = extension;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Extension { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert test name to filename by adding the extension.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="testName">Test name.</param>
|
|
||||||
/// <returns>The corresponding filename.</returns>
|
|
||||||
public string ConvertTestNameToFilename(string testName)
|
|
||||||
{
|
|
||||||
return Path.ChangeExtension(testName, Extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Compare file's extension to the one specified during initialization.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filename">Filename.</param>
|
|
||||||
/// <returns>True iff the file has the extension stored in the class.</returns>
|
|
||||||
public bool FilterFile(string filename)
|
|
||||||
{
|
|
||||||
return Path.GetExtension(filename) == "." + Extension;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler.Tests
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface for handling filtering tests based on filenames.
|
|
||||||
/// </summary>
|
|
||||||
public interface ITestFilenameConvention
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Convert test name to a filename (e.g., by adding an appropriate extension).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="testName">Name of a test.</param>
|
|
||||||
/// <returns>Filename.</returns>
|
|
||||||
string ConvertTestNameToFilename(string testName);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Decide if a file contains a test based on the filename.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filename">A filename.</param>
|
|
||||||
/// <returns>True iff the file should contain a test.</returns>
|
|
||||||
bool FilterFile(string filename);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +1,20 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net472</TargetFrameworks>
|
<TargetFrameworks>net461;net472;net8.0</TargetFrameworks>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<CodeAnalysisRuleSet>..\MatFileHandler.ruleset</CodeAnalysisRuleSet>
|
|
||||||
<DocumentationFile>bin\Debug\net5.0\MatFileHandler.Tests.xml</DocumentationFile>
|
<DocumentationFile>bin\Debug\net5.0\MatFileHandler.Tests.xml</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="xunit" Version="2.9.0" />
|
<PackageReference Include="xunit" Version="2.9.0" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
<PackageReference Condition=" '$(TargetFramework)' != 'net461' " Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Condition=" '$(TargetFramework)' == 'net461' " Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@ -18,12 +17,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading all files in a given test set.
|
/// Test reading all files in a given test set.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="testSet">Name of the set.</param>
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
[Theory]
|
public void TestReader(MatFileReadingMethod method)
|
||||||
[InlineData("good")]
|
|
||||||
public void TestReader(string testSet)
|
|
||||||
{
|
{
|
||||||
foreach (var matFile in GetTests(testSet).GetAllTestData())
|
foreach (var matFile in ReadAllTestFiles(method))
|
||||||
{
|
{
|
||||||
Assert.NotEmpty(matFile.Variables);
|
Assert.NotEmpty(matFile.Variables);
|
||||||
}
|
}
|
||||||
@ -32,10 +29,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading lower and upper limits of integer data types.
|
/// Test reading lower and upper limits of integer data types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestLimits()
|
public void TestLimits(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["limits"];
|
var matFile = ReadTestFile("limits", method);
|
||||||
IArray array;
|
IArray array;
|
||||||
array = matFile["int8_"].Value;
|
array = matFile["int8_"].Value;
|
||||||
CheckLimits(array as IArrayOf<sbyte>, CommonData.Int8Limits);
|
CheckLimits(array as IArrayOf<sbyte>, CommonData.Int8Limits);
|
||||||
@ -66,10 +63,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test writing lower and upper limits of integer-based complex data types.
|
/// Test writing lower and upper limits of integer-based complex data types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestComplexLimits()
|
public void TestComplexLimits(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["limits_complex"];
|
var matFile = ReadTestFile("limits_complex", method);
|
||||||
IArray array;
|
IArray array;
|
||||||
array = matFile["int8_complex"].Value;
|
array = matFile["int8_complex"].Value;
|
||||||
CheckComplexLimits(array as IArrayOf<ComplexOf<sbyte>>, CommonData.Int8Limits);
|
CheckComplexLimits(array as IArrayOf<ComplexOf<sbyte>>, CommonData.Int8Limits);
|
||||||
@ -102,10 +99,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading an ASCII-encoded string.
|
/// Test reading an ASCII-encoded string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestAscii()
|
public void TestAscii(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["ascii"];
|
var matFile = ReadTestFile("ascii", method);
|
||||||
var arrayAscii = matFile["s"].Value as ICharArray;
|
var arrayAscii = matFile["s"].Value as ICharArray;
|
||||||
Assert.NotNull(arrayAscii);
|
Assert.NotNull(arrayAscii);
|
||||||
Assert.Equal(new[] { 1, 3 }, arrayAscii.Dimensions);
|
Assert.Equal(new[] { 1, 3 }, arrayAscii.Dimensions);
|
||||||
@ -116,10 +113,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading a Unicode string.
|
/// Test reading a Unicode string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestUnicode()
|
public void TestUnicode(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["unicode"];
|
var matFile = ReadTestFile("unicode", method);
|
||||||
var arrayUnicode = matFile["s"].Value as ICharArray;
|
var arrayUnicode = matFile["s"].Value as ICharArray;
|
||||||
Assert.NotNull(arrayUnicode);
|
Assert.NotNull(arrayUnicode);
|
||||||
Assert.Equal(new[] { 1, 2 }, arrayUnicode.Dimensions);
|
Assert.Equal(new[] { 1, 2 }, arrayUnicode.Dimensions);
|
||||||
@ -131,10 +128,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading a wide Unicode string.
|
/// Test reading a wide Unicode string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestUnicodeWide()
|
public void TestUnicodeWide(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["unicode-wide"];
|
var matFile = ReadTestFile("unicode-wide", method);
|
||||||
var arrayUnicodeWide = matFile["s"].Value as ICharArray;
|
var arrayUnicodeWide = matFile["s"].Value as ICharArray;
|
||||||
Assert.NotNull(arrayUnicodeWide);
|
Assert.NotNull(arrayUnicodeWide);
|
||||||
Assert.Equal(new[] { 1, 2 }, arrayUnicodeWide.Dimensions);
|
Assert.Equal(new[] { 1, 2 }, arrayUnicodeWide.Dimensions);
|
||||||
@ -144,10 +141,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test converting a structure array to a Double array.
|
/// Test converting a structure array to a Double array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestConvertToDoubleArray()
|
public void TestConvertToDoubleArray(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["struct"];
|
var matFile = ReadTestFile("struct", method);
|
||||||
var array = matFile.Variables[0].Value;
|
var array = matFile.Variables[0].Value;
|
||||||
Assert.Null(array.ConvertToDoubleArray());
|
Assert.Null(array.ConvertToDoubleArray());
|
||||||
}
|
}
|
||||||
@ -156,10 +153,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// Test converting a structure array to a Complex array.
|
/// Test converting a structure array to a Complex array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Should return null.</returns>
|
/// <returns>Should return null.</returns>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestConvertToComplexArray()
|
public void TestConvertToComplexArray(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["struct"];
|
var matFile = ReadTestFile("struct", method);
|
||||||
var array = matFile.Variables[0].Value;
|
var array = matFile.Variables[0].Value;
|
||||||
Assert.Null(array.ConvertToComplexArray());
|
Assert.Null(array.ConvertToComplexArray());
|
||||||
}
|
}
|
||||||
@ -167,10 +164,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading an enumeration.
|
/// Test reading an enumeration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestEnum()
|
public void TestEnum(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["enum"];
|
var matFile = ReadTestFile("enum", method);
|
||||||
var days = matFile["days"].Value;
|
var days = matFile["days"].Value;
|
||||||
var enumeration = new EnumAdapter(days);
|
var enumeration = new EnumAdapter(days);
|
||||||
Assert.Equal(5, enumeration.Values.Count);
|
Assert.Equal(5, enumeration.Values.Count);
|
||||||
@ -184,10 +181,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading a structure array.
|
/// Test reading a structure array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestStruct()
|
public void TestStruct(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["struct"];
|
var matFile = ReadTestFile("struct", method);
|
||||||
var structure = matFile["struct_"].Value as IStructureArray;
|
var structure = matFile["struct_"].Value as IStructureArray;
|
||||||
Assert.NotNull(structure);
|
Assert.NotNull(structure);
|
||||||
Assert.Equal(new[] { "x", "y" }, structure.FieldNames);
|
Assert.Equal(new[] { "x", "y" }, structure.FieldNames);
|
||||||
@ -238,10 +235,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading a sparse array.
|
/// Test reading a sparse array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestSparse()
|
public void TestSparse(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["sparse"];
|
var matFile = ReadTestFile("sparse", method);
|
||||||
var sparseArray = matFile["sparse_"].Value as ISparseArrayOf<double>;
|
var sparseArray = matFile["sparse_"].Value as ISparseArrayOf<double>;
|
||||||
Assert.NotNull(sparseArray);
|
Assert.NotNull(sparseArray);
|
||||||
Assert.Equal(new[] { 4, 5 }, sparseArray.Dimensions);
|
Assert.Equal(new[] { 4, 5 }, sparseArray.Dimensions);
|
||||||
@ -268,10 +265,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading a logical array.
|
/// Test reading a logical array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestLogical()
|
public void TestLogical(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["logical"];
|
var matFile = ReadTestFile("logical", method);
|
||||||
var array = matFile["logical_"].Value;
|
var array = matFile["logical_"].Value;
|
||||||
var logicalArray = array as IArrayOf<bool>;
|
var logicalArray = array as IArrayOf<bool>;
|
||||||
Assert.NotNull(logicalArray);
|
Assert.NotNull(logicalArray);
|
||||||
@ -286,10 +283,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading a sparse logical array.
|
/// Test reading a sparse logical array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestSparseLogical()
|
public void TestSparseLogical(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["sparse_logical"];
|
var matFile = ReadTestFile("sparse_logical", method);
|
||||||
var array = matFile["sparse_logical"].Value;
|
var array = matFile["sparse_logical"].Value;
|
||||||
var sparseArray = array as ISparseArrayOf<bool>;
|
var sparseArray = array as ISparseArrayOf<bool>;
|
||||||
Assert.NotNull (sparseArray);
|
Assert.NotNull (sparseArray);
|
||||||
@ -305,10 +302,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading a global variable.
|
/// Test reading a global variable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestGlobal()
|
public void TestGlobal(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["global"];
|
var matFile = ReadTestFile("global", method);
|
||||||
var variable = matFile.Variables.First();
|
var variable = matFile.Variables.First();
|
||||||
Assert.True(variable.IsGlobal);
|
Assert.True(variable.IsGlobal);
|
||||||
}
|
}
|
||||||
@ -316,10 +313,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading a sparse complex array.
|
/// Test reading a sparse complex array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TextSparseComplex()
|
public void TextSparseComplex(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["sparse_complex"];
|
var matFile = ReadTestFile("sparse_complex", method);
|
||||||
var array = matFile["sparse_complex"].Value;
|
var array = matFile["sparse_complex"].Value;
|
||||||
var sparseArray = array as ISparseArrayOf<Complex>;
|
var sparseArray = array as ISparseArrayOf<Complex>;
|
||||||
Assert.NotNull(sparseArray);
|
Assert.NotNull(sparseArray);
|
||||||
@ -332,10 +329,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading an object.
|
/// Test reading an object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestObject()
|
public void TestObject(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["object"];
|
var matFile = ReadTestFile("object", method);
|
||||||
var obj = matFile["object_"].Value as IMatObject;
|
var obj = matFile["object_"].Value as IMatObject;
|
||||||
Assert.NotNull(obj);
|
Assert.NotNull(obj);
|
||||||
Assert.Equal("Point", obj.ClassName);
|
Assert.Equal("Point", obj.ClassName);
|
||||||
@ -349,10 +346,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading another object.
|
/// Test reading another object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestObject2()
|
public void TestObject2(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["object2"];
|
var matFile = ReadTestFile("object2", method);
|
||||||
var obj = matFile["object2"].Value as IMatObject;
|
var obj = matFile["object2"].Value as IMatObject;
|
||||||
Assert.NotNull(obj);
|
Assert.NotNull(obj);
|
||||||
Assert.Equal("Point", obj.ClassName);
|
Assert.Equal("Point", obj.ClassName);
|
||||||
@ -372,10 +369,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test reading a table.
|
/// Test reading a table.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestTable()
|
public void TestTable(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["table"];
|
var matFile = ReadTestFile("table", method);
|
||||||
var obj = matFile["table_"].Value as IMatObject;
|
var obj = matFile["table_"].Value as IMatObject;
|
||||||
var table = new TableAdapter(obj);
|
var table = new TableAdapter(obj);
|
||||||
Assert.Equal(3, table.NumberOfRows);
|
Assert.Equal(3, table.NumberOfRows);
|
||||||
@ -390,13 +387,62 @@ namespace MatFileHandler.Tests
|
|||||||
Assert.Equal(new[] { 1.0, 3.0, 5.0, 2.0, 4.0, 6.0 }, variable2.ConvertToDoubleArray());
|
Assert.Equal(new[] { 1.0, 3.0, 5.0, 2.0, 4.0, 6.0 }, variable2.ConvertToDoubleArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test reading a deeply nested table.
|
||||||
|
/// </summary>
|
||||||
|
[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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test reading a table with strings
|
||||||
|
/// </summary>
|
||||||
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
|
public void TestTableWithStrings(MatFileReadingMethod method)
|
||||||
|
{
|
||||||
|
var matFile = ReadTestFile("table-with-strings", method);
|
||||||
|
var obj = matFile["t"].Value as IMatObject;
|
||||||
|
var table = new TableAdapter(obj);
|
||||||
|
Assert.Equal(5, table.NumberOfRows);
|
||||||
|
Assert.Equal(2, table.NumberOfVariables);
|
||||||
|
Assert.Equal(new[] { "Numbers", "Names" }, table.VariableNames);
|
||||||
|
var variable = table["Names"] as ICellArray;
|
||||||
|
var name0 = new StringAdapter(variable[0]);
|
||||||
|
Assert.Equal("One", name0[0]);
|
||||||
|
var name1 = new StringAdapter(variable[1]);
|
||||||
|
Assert.Equal("Two", name1[0]);
|
||||||
|
var name2 = new StringAdapter(variable[2]);
|
||||||
|
Assert.Equal("Three", name2[0]);
|
||||||
|
var name3 = new StringAdapter(variable[3]);
|
||||||
|
Assert.Equal("Four", name3[0]);
|
||||||
|
var name4 = new StringAdapter(variable[4]);
|
||||||
|
Assert.Equal("Five", name4[0]);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test subobjects within objects.
|
/// Test subobjects within objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestSubobjects()
|
public void TestSubobjects(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["pointWithSubpoints"];
|
var matFile = ReadTestFile("pointWithSubpoints", method);
|
||||||
var p = matFile["p"].Value as IMatObject;
|
var p = matFile["p"].Value as IMatObject;
|
||||||
Assert.Equal("Point", p.ClassName);
|
Assert.Equal("Point", p.ClassName);
|
||||||
var x = p["x"] as IMatObject;
|
var x = p["x"] as IMatObject;
|
||||||
@ -416,10 +462,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test nested objects.
|
/// Test nested objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestNestedObjects()
|
public void TestNestedObjects(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["subsubPoint"];
|
var matFile = ReadTestFile("subsubPoint", method);
|
||||||
var p = matFile["p"].Value as IMatObject;
|
var p = matFile["p"].Value as IMatObject;
|
||||||
Assert.Equal("Point", p.ClassName);
|
Assert.Equal("Point", p.ClassName);
|
||||||
Assert.Equal(new[] { 1.0 }, p["x"].ConvertToDoubleArray());
|
Assert.Equal(new[] { 1.0 }, p["x"].ConvertToDoubleArray());
|
||||||
@ -434,10 +480,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test datetime objects.
|
/// Test datetime objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestDatetime()
|
public void TestDatetime(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["datetime"];
|
var matFile = ReadTestFile("datetime", method);
|
||||||
var d = matFile["d"].Value as IMatObject;
|
var d = matFile["d"].Value as IMatObject;
|
||||||
var datetime = new DatetimeAdapter(d);
|
var datetime = new DatetimeAdapter(d);
|
||||||
Assert.Equal(new[] { 1, 2 }, datetime.Dimensions);
|
Assert.Equal(new[] { 1, 2 }, datetime.Dimensions);
|
||||||
@ -448,10 +494,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Another test for datetime objects.
|
/// Another test for datetime objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestDatetime2()
|
public void TestDatetime2(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["datetime2"];
|
var matFile = ReadTestFile("datetime2", method);
|
||||||
var d = matFile["d"].Value as IMatObject;
|
var d = matFile["d"].Value as IMatObject;
|
||||||
var datetime = new DatetimeAdapter(d);
|
var datetime = new DatetimeAdapter(d);
|
||||||
Assert.Equal(new[] { 1, 1 }, datetime.Dimensions);
|
Assert.Equal(new[] { 1, 1 }, datetime.Dimensions);
|
||||||
@ -463,10 +509,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test string objects.
|
/// Test string objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestString()
|
public void TestString(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["string"];
|
var matFile = ReadTestFile("string", method);
|
||||||
var s = matFile["s"].Value as IMatObject;
|
var s = matFile["s"].Value as IMatObject;
|
||||||
var str = new StringAdapter(s);
|
var str = new StringAdapter(s);
|
||||||
Assert.Equal(new[] { 4, 1 }, str.Dimensions);
|
Assert.Equal(new[] { 4, 1 }, str.Dimensions);
|
||||||
@ -479,10 +525,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test duration objects.
|
/// Test duration objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestDuration()
|
public void TestDuration(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["duration"];
|
var matFile = ReadTestFile("duration", method);
|
||||||
var d = matFile["d"].Value as IMatObject;
|
var d = matFile["d"].Value as IMatObject;
|
||||||
var duration = new DurationAdapter(d);
|
var duration = new DurationAdapter(d);
|
||||||
Assert.Equal(new[] { 1, 3 }, duration.Dimensions);
|
Assert.Equal(new[] { 1, 3 }, duration.Dimensions);
|
||||||
@ -494,10 +540,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test unrepresentable datetime.
|
/// Test unrepresentable datetime.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void TestDatetime_Unrepresentable()
|
public void TestDatetime_Unrepresentable(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["datetime-unrepresentable"];
|
var matFile = ReadTestFile("datetime-unrepresentable", method);
|
||||||
var obj = matFile["d"].Value as IMatObject;
|
var obj = matFile["d"].Value as IMatObject;
|
||||||
var datetime = new DatetimeAdapter(obj);
|
var datetime = new DatetimeAdapter(obj);
|
||||||
var d0 = datetime[0];
|
var d0 = datetime[0];
|
||||||
@ -507,10 +553,10 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test 3-dimensional arrays.
|
/// Test 3-dimensional arrays.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void Test_3DArrays()
|
public void Test_3DArrays(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["issue20.mat"];
|
var matFile = ReadTestFile("issue20", method);
|
||||||
var obj = matFile["a3d"].Value;
|
var obj = matFile["a3d"].Value;
|
||||||
var values = obj.ConvertToDoubleArray();
|
var values = obj.ConvertToDoubleArray();
|
||||||
Assert.Equal(Enumerable.Range(1, 24).Select(x => (double)x).ToArray(), values);
|
Assert.Equal(Enumerable.Range(1, 24).Select(x => (double)x).ToArray(), values);
|
||||||
@ -538,21 +584,51 @@ namespace MatFileHandler.Tests
|
|||||||
Assert.Equal(expected, obj.ConvertToMultidimensionalDoubleArray());
|
Assert.Equal(expected, obj.ConvertToMultidimensionalDoubleArray());
|
||||||
Assert.Null(obj.ConvertTo2dDoubleArray());
|
Assert.Null(obj.ConvertTo2dDoubleArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test four-dimensional arrays.
|
/// Test four-dimensional arrays.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(TestDataFactories))]
|
||||||
public void Test_4DArrays()
|
public void Test4DArrays(MatFileReadingMethod method)
|
||||||
{
|
{
|
||||||
var matFile = GetTests("good")["issue20.mat"];
|
var matFile = ReadTestFile("issue20", method);
|
||||||
var obj = matFile["a4d"].Value;
|
var obj = matFile["a4d"].Value;
|
||||||
Assert.Equal(Enumerable.Range(1, 120).Select(x => (double)x).ToArray(), obj.ConvertToDoubleArray());
|
Assert.Equal(Enumerable.Range(1, 120).Select(x => (double)x).ToArray(), obj.ConvertToDoubleArray());
|
||||||
Assert.Null(obj.ConvertTo2dDoubleArray());
|
Assert.Null(obj.ConvertTo2dDoubleArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AbstractTestDataFactory<IMatFile> GetTests(string factoryName) =>
|
/// <summary>
|
||||||
new PartialReadMatTestDataFactory(Path.Combine(TestDirectory, factoryName));
|
/// Returns the factories that provide test data in various configurations.
|
||||||
|
/// </summary>
|
||||||
|
public static TheoryData<MatFileReadingMethod> TestDataFactories
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new TheoryData<MatFileReadingMethod>
|
||||||
|
{
|
||||||
|
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<IMatFile> 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<T>(IArrayOf<T> array, T[] limits)
|
private static void CheckLimits<T>(IArrayOf<T> array, T[] limits)
|
||||||
where T : struct
|
where T : struct
|
||||||
@ -571,4 +647,4 @@ namespace MatFileHandler.Tests
|
|||||||
Assert.Equal(new ComplexOf<T>(limits[1], limits[0]), array[1]);
|
Assert.Equal(new ComplexOf<T>(limits[1], limits[0]), array[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
MatFileHandler.Tests/MatFileReadingMethod.cs
Normal file
27
MatFileHandler.Tests/MatFileReadingMethod.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace MatFileHandler.Tests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Method of reading .mat files for testing.
|
||||||
|
/// </summary>
|
||||||
|
public enum MatFileReadingMethod
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Undefined.
|
||||||
|
/// </summary>
|
||||||
|
Undefined = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Normal stream (like memory or file stream).
|
||||||
|
/// </summary>
|
||||||
|
NormalStream,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Partial stream (only is capable of reading one byte at a time).
|
||||||
|
/// </summary>
|
||||||
|
PartialStream,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unaligned stream (what happens if the data don't start at the beginning?).
|
||||||
|
/// </summary>
|
||||||
|
UnalignedStream,
|
||||||
|
}
|
38
MatFileHandler.Tests/MatFileReadingMethods.cs
Normal file
38
MatFileHandler.Tests/MatFileReadingMethods.cs
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
27
MatFileHandler.Tests/MatFileWriterOptionsForTests.cs
Normal file
27
MatFileHandler.Tests/MatFileWriterOptionsForTests.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace MatFileHandler.Tests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Options to give to MatFileWriter constructor for testing it.
|
||||||
|
/// </summary>
|
||||||
|
public enum MatFileWriterOptionsForTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Undefined.
|
||||||
|
/// </summary>
|
||||||
|
Undefined = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// No options.
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Option to always use compression.
|
||||||
|
/// </summary>
|
||||||
|
Always,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Option to never use compression.
|
||||||
|
/// </summary>
|
||||||
|
Never,
|
||||||
|
}
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@ -17,8 +15,8 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test writing a simple Double array.
|
/// Test writing a simple Double array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||||
public void TestWrite()
|
public void TestWrite(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||||
{
|
{
|
||||||
var builder = new DataBuilder();
|
var builder = new DataBuilder();
|
||||||
var array = builder.NewArray<double>(1, 2);
|
var array = builder.NewArray<double>(1, 2);
|
||||||
@ -26,7 +24,7 @@ namespace MatFileHandler.Tests
|
|||||||
array[1] = 17.0;
|
array[1] = 17.0;
|
||||||
var variable = builder.NewVariable("test", array);
|
var variable = builder.NewVariable("test", array);
|
||||||
var actual = builder.NewFile(new[] { variable });
|
var actual = builder.NewFile(new[] { variable });
|
||||||
MatCompareWithTestData("good", "double-array", actual);
|
MatCompareWithTestData("good", "double-array", actual, method, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -51,8 +49,8 @@ namespace MatFileHandler.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test writing lower and upper limits of integer data types.
|
/// Test writing lower and upper limits of integer data types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||||
public void TestLimits()
|
public void TestLimits(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||||
{
|
{
|
||||||
var builder = new DataBuilder();
|
var builder = new DataBuilder();
|
||||||
var int8 = builder.NewVariable("int8_", builder.NewArray(CommonData.Int8Limits, 1, 2));
|
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 int64 = builder.NewVariable("int64_", builder.NewArray(CommonData.Int64Limits, 1, 2));
|
||||||
var uint64 = builder.NewVariable("uint64_", builder.NewArray(CommonData.UInt64Limits, 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 });
|
var actual = builder.NewFile(new[] { int16, int32, int64, int8, uint16, uint32, uint64, uint8 });
|
||||||
MatCompareWithTestData("good", "limits", actual);
|
MatCompareWithTestData("good", "limits", actual, method, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test writing lower and upper limits of integer-based complex data types.
|
/// Test writing lower and upper limits of integer-based complex data types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||||
public void TestLimitsComplex()
|
public void TestLimitsComplex(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||||
{
|
{
|
||||||
var builder = new DataBuilder();
|
var builder = new DataBuilder();
|
||||||
var int8Complex = builder.NewVariable(
|
var int8Complex = builder.NewVariable(
|
||||||
@ -103,26 +101,26 @@ namespace MatFileHandler.Tests
|
|||||||
int16Complex, int32Complex, int64Complex, int8Complex,
|
int16Complex, int32Complex, int64Complex, int8Complex,
|
||||||
uint16Complex, uint32Complex, uint64Complex, uint8Complex,
|
uint16Complex, uint32Complex, uint64Complex, uint8Complex,
|
||||||
});
|
});
|
||||||
MatCompareWithTestData("good", "limits_complex", actual);
|
MatCompareWithTestData("good", "limits_complex", actual, method, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test writing a wide-Unicode symbol.
|
/// Test writing a wide-Unicode symbol.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||||
public void TestUnicodeWide()
|
public void TestUnicodeWide(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||||
{
|
{
|
||||||
var builder = new DataBuilder();
|
var builder = new DataBuilder();
|
||||||
var s = builder.NewVariable("s", builder.NewCharArray("🍆"));
|
var s = builder.NewVariable("s", builder.NewCharArray("🍆"));
|
||||||
var actual = builder.NewFile(new[] { s });
|
var actual = builder.NewFile(new[] { s });
|
||||||
MatCompareWithTestData("good", "unicode-wide", actual);
|
MatCompareWithTestData("good", "unicode-wide", actual, method, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test writing a sparse array.
|
/// Test writing a sparse array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||||
public void TestSparseArray()
|
public void TestSparseArray(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||||
{
|
{
|
||||||
var builder = new DataBuilder();
|
var builder = new DataBuilder();
|
||||||
var sparseArray = builder.NewSparseArray<double>(4, 5);
|
var sparseArray = builder.NewSparseArray<double>(4, 5);
|
||||||
@ -132,14 +130,14 @@ namespace MatFileHandler.Tests
|
|||||||
sparseArray[2, 3] = 4;
|
sparseArray[2, 3] = 4;
|
||||||
var sparse = builder.NewVariable("sparse_", sparseArray);
|
var sparse = builder.NewVariable("sparse_", sparseArray);
|
||||||
var actual = builder.NewFile(new[] { sparse });
|
var actual = builder.NewFile(new[] { sparse });
|
||||||
MatCompareWithTestData("good", "sparse", actual);
|
MatCompareWithTestData("good", "sparse", actual, method, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test writing a structure array.
|
/// Test writing a structure array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||||
public void TestStructure()
|
public void TestStructure(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||||
{
|
{
|
||||||
var builder = new DataBuilder();
|
var builder = new DataBuilder();
|
||||||
var structure = builder.NewStructureArray(new[] { "x", "y" }, 2, 3);
|
var structure = builder.NewStructureArray(new[] { "x", "y" }, 2, 3);
|
||||||
@ -160,27 +158,27 @@ namespace MatFileHandler.Tests
|
|||||||
structure["y", 1, 2] = builder.NewEmpty();
|
structure["y", 1, 2] = builder.NewEmpty();
|
||||||
var struct_ = builder.NewVariable("struct_", structure);
|
var struct_ = builder.NewVariable("struct_", structure);
|
||||||
var actual = builder.NewFile(new[] { struct_ });
|
var actual = builder.NewFile(new[] { struct_ });
|
||||||
MatCompareWithTestData("good", "struct", actual);
|
MatCompareWithTestData("good", "struct", actual, method, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test writing a logical array.
|
/// Test writing a logical array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||||
public void TestLogical()
|
public void TestLogical(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||||
{
|
{
|
||||||
var builder = new DataBuilder();
|
var builder = new DataBuilder();
|
||||||
var logical = builder.NewArray(new[] { true, false, true, true, false, true }, 2, 3);
|
var logical = builder.NewArray(new[] { true, false, true, true, false, true }, 2, 3);
|
||||||
var logicalVariable = builder.NewVariable("logical_", logical);
|
var logicalVariable = builder.NewVariable("logical_", logical);
|
||||||
var actual = builder.NewFile(new[] { logicalVariable });
|
var actual = builder.NewFile(new[] { logicalVariable });
|
||||||
MatCompareWithTestData("good", "logical", actual);
|
MatCompareWithTestData("good", "logical", actual, method, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test writing a sparse logical array.
|
/// Test writing a sparse logical array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||||
public void TestSparseLogical()
|
public void TestSparseLogical(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||||
{
|
{
|
||||||
var builder = new DataBuilder();
|
var builder = new DataBuilder();
|
||||||
var array = builder.NewSparseArray<bool>(2, 3);
|
var array = builder.NewSparseArray<bool>(2, 3);
|
||||||
@ -190,14 +188,14 @@ namespace MatFileHandler.Tests
|
|||||||
array[1, 2] = true;
|
array[1, 2] = true;
|
||||||
var sparseLogical = builder.NewVariable("sparse_logical", array);
|
var sparseLogical = builder.NewVariable("sparse_logical", array);
|
||||||
var actual = builder.NewFile(new[] { sparseLogical });
|
var actual = builder.NewFile(new[] { sparseLogical });
|
||||||
MatCompareWithTestData("good", "sparse_logical", actual);
|
MatCompareWithTestData("good", "sparse_logical", actual, method, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test writing a sparse complex array.
|
/// Test writing a sparse complex array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||||
public void TestSparseComplex()
|
public void TestSparseComplex(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||||
{
|
{
|
||||||
var builder = new DataBuilder();
|
var builder = new DataBuilder();
|
||||||
var array = builder.NewSparseArray<Complex>(2, 2);
|
var array = builder.NewSparseArray<Complex>(2, 2);
|
||||||
@ -206,26 +204,48 @@ namespace MatFileHandler.Tests
|
|||||||
array[1, 1] = 0.5 + Complex.ImaginaryOne;
|
array[1, 1] = 0.5 + Complex.ImaginaryOne;
|
||||||
var sparseComplex = builder.NewVariable("sparse_complex", array);
|
var sparseComplex = builder.NewVariable("sparse_complex", array);
|
||||||
var actual = builder.NewFile(new[] { sparseComplex });
|
var actual = builder.NewFile(new[] { sparseComplex });
|
||||||
MatCompareWithTestData("good", "sparse_complex", actual);
|
MatCompareWithTestData("good", "sparse_complex", actual, method, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test writing a global variable.
|
/// Test writing a global variable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
||||||
public void TestGlobal()
|
public void TestGlobal(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
||||||
{
|
{
|
||||||
var builder = new DataBuilder();
|
var builder = new DataBuilder();
|
||||||
var array = builder.NewArray(new double[] { 1, 3, 5 }, 1, 3);
|
var array = builder.NewArray(new double[] { 1, 3, 5 }, 1, 3);
|
||||||
var global = builder.NewVariable("global_", array, true);
|
var global = builder.NewVariable("global_", array, true);
|
||||||
var actual = builder.NewFile(new[] { global });
|
var actual = builder.NewFile(new[] { global });
|
||||||
MatCompareWithTestData("good", "global", actual);
|
MatCompareWithTestData("good", "global", actual, method, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AbstractTestDataFactory<IMatFile> GetMatTestData(string factoryName) =>
|
/// <summary>
|
||||||
new MatTestDataFactory(Path.Combine(TestDirectory, factoryName));
|
/// Various writing methods for testing writing of .mat files.
|
||||||
|
/// </summary>
|
||||||
|
public static TheoryData<MatFileWritingMethod, MatFileWriterOptionsForTests> MatFileWritingTestData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var always = new MatFileWriterOptions { UseCompression = CompressionUsage.Always};
|
||||||
|
var never = new MatFileWriterOptions { UseCompression = CompressionUsage.Never };
|
||||||
|
var data = new TheoryData<MatFileWritingMethod, MatFileWriterOptionsForTests>
|
||||||
|
{
|
||||||
|
{ MatFileWritingMethod.NormalStream, MatFileWriterOptionsForTests.None },
|
||||||
|
{ MatFileWritingMethod.NormalStream, MatFileWriterOptionsForTests.Always },
|
||||||
|
{ MatFileWritingMethod.NormalStream, MatFileWriterOptionsForTests.Never },
|
||||||
|
{ MatFileWritingMethod.UnseekableStream, MatFileWriterOptionsForTests.None },
|
||||||
|
{ MatFileWritingMethod.UnseekableStream, MatFileWriterOptionsForTests.Always },
|
||||||
|
{ MatFileWritingMethod.UnseekableStream, MatFileWriterOptionsForTests.Never },
|
||||||
|
{ MatFileWritingMethod.UnalignedStream, MatFileWriterOptionsForTests.None },
|
||||||
|
{ MatFileWritingMethod.UnalignedStream, MatFileWriterOptionsForTests.Always },
|
||||||
|
{ MatFileWritingMethod.UnalignedStream, MatFileWriterOptionsForTests.Never },
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void CompareSparseArrays<T>(ISparseArrayOf<T> expected, ISparseArrayOf<T> actual)
|
private static void CompareSparseArrays<T>(ISparseArrayOf<T> expected, ISparseArrayOf<T> actual)
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
Assert.NotNull(actual);
|
Assert.NotNull(actual);
|
||||||
@ -233,7 +253,7 @@ namespace MatFileHandler.Tests
|
|||||||
Assert.Equal(expected.Data, actual.Data);
|
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.NotNull(actual);
|
||||||
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
||||||
@ -247,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.NotNull(actual);
|
||||||
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
||||||
@ -257,21 +277,21 @@ namespace MatFileHandler.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CompareNumericalArrays<T>(IArrayOf<T> expected, IArrayOf<T> actual)
|
private static void CompareNumericalArrays<T>(IArrayOf<T> expected, IArrayOf<T> actual)
|
||||||
{
|
{
|
||||||
Assert.NotNull(actual);
|
Assert.NotNull(actual);
|
||||||
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
||||||
Assert.Equal(expected.Data, actual.Data);
|
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.NotNull(actual);
|
||||||
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
||||||
Assert.Equal(expected.String, actual.String);
|
Assert.Equal(expected.String, actual.String);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CompareMatArrays(IArray expected, IArray actual)
|
private static void CompareMatArrays(IArray expected, IArray actual)
|
||||||
{
|
{
|
||||||
switch (expected)
|
switch (expected)
|
||||||
{
|
{
|
||||||
@ -362,7 +382,7 @@ namespace MatFileHandler.Tests
|
|||||||
throw new NotSupportedException();
|
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);
|
Assert.Equal(expected.Variables.Length, actual.Variables.Length);
|
||||||
for (var i = 0; i < expected.Variables.Length; i++)
|
for (var i = 0; i < expected.Variables.Length; i++)
|
||||||
@ -375,46 +395,28 @@ namespace MatFileHandler.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CompareTestDataWithWritingOptions(
|
private static void MatCompareWithTestData(
|
||||||
IMatFile expected,
|
string factoryName,
|
||||||
|
string testName,
|
||||||
IMatFile actual,
|
IMatFile actual,
|
||||||
MatFileWriterOptions? maybeOptions)
|
MatFileWritingMethod method,
|
||||||
|
MatFileWriterOptionsForTests options)
|
||||||
{
|
{
|
||||||
byte[] buffer;
|
var fullFileName = Path.Combine("test-data", "good", $"{testName}.mat");
|
||||||
using (var stream = new MemoryStream())
|
var expected = MatFileReadingMethods.ReadMatFile(
|
||||||
{
|
MatFileReadingMethod.NormalStream,
|
||||||
var writer = maybeOptions is MatFileWriterOptions options
|
fullFileName);
|
||||||
? new MatFileWriter(stream, options)
|
var buffer = MatFileWritingMethods.WriteMatFile(method, options, actual);
|
||||||
: new MatFileWriter(stream);
|
using var stream = new MemoryStream(buffer);
|
||||||
writer.Write(actual);
|
var reader = new MatFileReader(stream);
|
||||||
buffer = stream.ToArray();
|
var actualRead = reader.Read();
|
||||||
}
|
CompareMatFiles(expected, actualRead);
|
||||||
using (var stream = new MemoryStream(buffer))
|
|
||||||
{
|
|
||||||
var reader = new MatFileReader(stream);
|
|
||||||
var actualRead = reader.Read();
|
|
||||||
CompareMatFiles(expected, actualRead);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MatCompareWithTestData(string factoryName, string testName, IMatFile actual)
|
private static ComplexOf<T>[] CreateComplexLimits<T>(T[] limits)
|
||||||
{
|
where T : struct
|
||||||
var expected = GetMatTestData(factoryName)[testName];
|
|
||||||
CompareTestDataWithWritingOptions(expected, actual, null);
|
|
||||||
CompareTestDataWithWritingOptions(
|
|
||||||
expected,
|
|
||||||
actual,
|
|
||||||
new MatFileWriterOptions { UseCompression = CompressionUsage.Always });
|
|
||||||
CompareTestDataWithWritingOptions(
|
|
||||||
expected,
|
|
||||||
actual,
|
|
||||||
new MatFileWriterOptions { UseCompression = CompressionUsage.Never });
|
|
||||||
}
|
|
||||||
|
|
||||||
private ComplexOf<T>[] CreateComplexLimits<T>(T[] limits)
|
|
||||||
where T : struct
|
|
||||||
{
|
{
|
||||||
return new[] { new ComplexOf<T>(limits[0], limits[1]), new ComplexOf<T>(limits[1], limits[0]) };
|
return new[] { new ComplexOf<T>(limits[0], limits[1]), new ComplexOf<T>(limits[1], limits[0]) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
MatFileHandler.Tests/MatFileWritingMethod.cs
Normal file
27
MatFileHandler.Tests/MatFileWritingMethod.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace MatFileHandler.Tests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Method of writing .mat files for testing.
|
||||||
|
/// </summary>
|
||||||
|
public enum MatFileWritingMethod
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Undefined.
|
||||||
|
/// </summary>
|
||||||
|
Undefined = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Normal stream (like memory or file stream).
|
||||||
|
/// </summary>
|
||||||
|
NormalStream,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A stream that cannot be seeked (like a deflate stream).
|
||||||
|
/// </summary>
|
||||||
|
UnseekableStream,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unaligned stream (what happens if the data don't start at the beginning?).
|
||||||
|
/// </summary>
|
||||||
|
UnalignedStream,
|
||||||
|
}
|
58
MatFileHandler.Tests/MatFileWritingMethods.cs
Normal file
58
MatFileHandler.Tests/MatFileWritingMethods.cs
Normal file
@ -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(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,35 +0,0 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace MatFileHandler.Tests
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Factory providing the parsed contents of .mat files.
|
|
||||||
/// </summary>
|
|
||||||
public class MatTestDataFactory : AbstractTestDataFactory<IMatFile>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="MatTestDataFactory"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="testDirectory">Directory containing test files.</param>
|
|
||||||
public MatTestDataFactory(string testDirectory)
|
|
||||||
: base(
|
|
||||||
testDirectory,
|
|
||||||
new ExtensionTestFilenameConvention("mat"))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read and parse data from a .mat file.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream">Input stream.</param>
|
|
||||||
/// <returns>Parsed contents of the file.</returns>
|
|
||||||
protected override IMatFile ReadDataFromStream(Stream stream)
|
|
||||||
{
|
|
||||||
var matFileReader = new MatFileReader(stream);
|
|
||||||
var matFile = matFileReader.Read();
|
|
||||||
return matFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace MatFileHandler.Tests
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Factory providing the parsed contents of .mat files,
|
|
||||||
/// wrapped in a <see cref="PartialReadStream"/>.
|
|
||||||
/// </summary>
|
|
||||||
public class PartialReadMatTestDataFactory : MatTestDataFactory
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="PartialReadMatTestDataFactory"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="testDirectory">Directory containing test files.</param>
|
|
||||||
public PartialReadMatTestDataFactory(string testDirectory)
|
|
||||||
: base(testDirectory)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read and parse data from a .mat file.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream">Input stream.</param>
|
|
||||||
/// <returns>Parsed contents of the file.</returns>
|
|
||||||
protected override IMatFile ReadDataFromStream(Stream stream)
|
|
||||||
{
|
|
||||||
using (var wrapper = new PartialReadStream(stream))
|
|
||||||
{
|
|
||||||
return base.ReadDataFromStream(wrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +1,21 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace MatFileHandler.Tests
|
namespace MatFileHandler.Tests
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A stream which wraps another stream and only reads one byte at a time.
|
/// A stream which wraps another stream and only reads one byte at a time,
|
||||||
|
/// while forbidding seeking in it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class PartialReadStream : Stream
|
internal sealed class PartialUnseekableReadStream : Stream
|
||||||
{
|
{
|
||||||
private readonly Stream _baseStream;
|
private readonly Stream _baseStream;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="PartialReadStream"/> class.
|
/// Initializes a new instance of the <see cref="PartialUnseekableReadStream"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="baseStream">The stream to wrap.</param>
|
/// <param name="baseStream">The stream to wrap.</param>
|
||||||
public PartialReadStream(Stream baseStream)
|
public PartialUnseekableReadStream(Stream baseStream)
|
||||||
{
|
{
|
||||||
_baseStream = baseStream;
|
_baseStream = baseStream;
|
||||||
}
|
}
|
||||||
@ -25,7 +24,7 @@ namespace MatFileHandler.Tests
|
|||||||
public override bool CanRead => _baseStream.CanRead;
|
public override bool CanRead => _baseStream.CanRead;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool CanSeek => _baseStream.CanSeek;
|
public override bool CanSeek => false;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool CanWrite => false;
|
public override bool CanWrite => false;
|
||||||
@ -36,8 +35,8 @@ namespace MatFileHandler.Tests
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override long Position
|
public override long Position
|
||||||
{
|
{
|
||||||
get => _baseStream.Position;
|
get => throw new NotSupportedException();
|
||||||
set => _baseStream.Position = value;
|
set => throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@ -55,13 +54,13 @@ namespace MatFileHandler.Tests
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override long Seek(long offset, SeekOrigin origin)
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
{
|
{
|
||||||
return _baseStream.Seek(offset, origin);
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void SetLength(long value)
|
public override void SetLength(long value)
|
||||||
{
|
{
|
||||||
_baseStream.SetLength(value);
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
68
MatFileHandler.Tests/UnseekableWriteStream.cs
Normal file
68
MatFileHandler.Tests/UnseekableWriteStream.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace MatFileHandler.Tests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A stream which wraps another stream and forbids seeking in it.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class UnseekableWriteStream : Stream
|
||||||
|
{
|
||||||
|
public UnseekableWriteStream(Stream baseStream)
|
||||||
|
{
|
||||||
|
_baseStream = baseStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Stream _baseStream;
|
||||||
|
|
||||||
|
public override bool CanRead => false;
|
||||||
|
|
||||||
|
public override bool CanSeek => false;
|
||||||
|
|
||||||
|
public override bool CanWrite => _baseStream.CanWrite;
|
||||||
|
|
||||||
|
public override long Length => _baseStream.Length;
|
||||||
|
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get => throw new NotSupportedException();
|
||||||
|
set => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Flush()
|
||||||
|
{
|
||||||
|
_baseStream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetLength(long value)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
_baseStream.Write(buffer, offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_baseStream.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
MatFileHandler.Tests/test-data/good/table-deep.mat
Normal file
BIN
MatFileHandler.Tests/test-data/good/table-deep.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/good/table-with-strings.mat
Normal file
BIN
MatFileHandler.Tests/test-data/good/table-with-strings.mat
Normal file
Binary file not shown.
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
@ -154,4 +152,4 @@ namespace MatFileHandler
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public uint NzMax;
|
public uint NzMax;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
92
MatFileHandler/ChecksumCalculatingStream.cs
Normal file
92
MatFileHandler/ChecksumCalculatingStream.cs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace MatFileHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A stream that calculates Adler32 checksum of everything
|
||||||
|
/// written to it before passing to another stream.
|
||||||
|
/// </summary>
|
||||||
|
internal class ChecksumCalculatingStream : Stream
|
||||||
|
{
|
||||||
|
private const uint BigPrime = 0xFFF1;
|
||||||
|
private readonly Stream _stream;
|
||||||
|
private uint s1;
|
||||||
|
private uint s2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ChecksumCalculatingStream"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">Wrapped stream.</param>
|
||||||
|
public ChecksumCalculatingStream(Stream stream)
|
||||||
|
{
|
||||||
|
_stream = stream;
|
||||||
|
s1 = 1;
|
||||||
|
s2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanRead => false;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanSeek => false;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanWrite => true;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override long Length => throw new NotImplementedException();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get => throw new NotImplementedException();
|
||||||
|
set => throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Flush()
|
||||||
|
{
|
||||||
|
_stream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void SetLength(long value)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
for (var i = offset; i < offset + count; i++)
|
||||||
|
{
|
||||||
|
s1 = (s1 + buffer[i]) % BigPrime;
|
||||||
|
s2 = (s2 + s1) % BigPrime;
|
||||||
|
}
|
||||||
|
|
||||||
|
_stream.Write(buffer, offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate the checksum of everything written to the stream so far.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Checksum of everything written to the stream so far.</returns>
|
||||||
|
public uint GetCrc()
|
||||||
|
{
|
||||||
|
return (s2 << 16) | s1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
@ -8,7 +6,7 @@ namespace MatFileHandler
|
|||||||
/// A structure representing a complex number where real and imaginary parts are of type T.
|
/// A structure representing a complex number where real and imaginary parts are of type T.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of real and imaginary parts.</typeparam>
|
/// <typeparam name="T">Type of real and imaginary parts.</typeparam>
|
||||||
public struct ComplexOf<T> : IEquatable<ComplexOf<T>>
|
public readonly struct ComplexOf<T> : IEquatable<ComplexOf<T>>
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -71,10 +69,11 @@ namespace MatFileHandler
|
|||||||
/// <returns>True iff another object is a complex number equal to this.</returns>
|
/// <returns>True iff another object is a complex number equal to this.</returns>
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, obj))
|
if (obj is null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj is ComplexOf<T> other && Equals(other);
|
return obj is ComplexOf<T> other && Equals(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,4 +89,4 @@ namespace MatFileHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
|
#pragma warning disable CA1822
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -52,7 +51,7 @@ namespace MatFileHandler
|
|||||||
{
|
{
|
||||||
if (data.Length != dimensions.NumberOfElements())
|
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<T>(GetStandardFlags<T>(), dimensions, string.Empty, data);
|
return new MatNumericalArrayOf<T>(GetStandardFlags<T>(), dimensions, string.Empty, data);
|
||||||
}
|
}
|
||||||
@ -157,7 +156,7 @@ namespace MatFileHandler
|
|||||||
return new MatFile(variables);
|
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
|
return new ArrayFlags
|
||||||
{
|
{
|
||||||
@ -167,7 +166,7 @@ namespace MatFileHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayFlags GetStandardFlags<T>()
|
private static ArrayFlags GetStandardFlags<T>()
|
||||||
{
|
{
|
||||||
if (typeof(T) == typeof(sbyte))
|
if (typeof(T) == typeof(sbyte))
|
||||||
{
|
{
|
||||||
@ -266,4 +265,4 @@ namespace MatFileHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -8,4 +6,4 @@ namespace MatFileHandler
|
|||||||
internal class DataElement
|
internal class DataElement
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -71,16 +69,21 @@ namespace MatFileHandler
|
|||||||
{
|
{
|
||||||
throw new NotSupportedException("Only 2-dimensional sparse arrays are supported");
|
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<T>(data, flags.ArrayFlags.Variable.HasFlag(Variable.IsLogical));
|
ConvertDataToSparseProperType<T>(data, flags.ArrayFlags.Variable.HasFlag(Variable.IsLogical));
|
||||||
if (elements == null)
|
if (maybeElements is not { } elements)
|
||||||
{
|
{
|
||||||
throw new HandlerException("Couldn't read sparse array.");
|
throw new HandlerException("Couldn't read sparse array.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var dataDictionary =
|
var dataDictionary =
|
||||||
ConvertMatlabSparseToDictionary(rowIndex, columnIndex, j => elements[j]);
|
ConvertMatlabSparseToDictionary(rowIndex, columnIndex, j => elements[j]);
|
||||||
return new MatSparseArrayOf<T>(flags, dimensions, name, dataDictionary);
|
return new MatSparseArrayOf<T>(flags, dimensions, name, dataDictionary);
|
||||||
@ -117,15 +120,12 @@ namespace MatFileHandler
|
|||||||
switch (flags.Class)
|
switch (flags.Class)
|
||||||
{
|
{
|
||||||
case ArrayType.MxChar:
|
case ArrayType.MxChar:
|
||||||
switch (realData)
|
return realData switch
|
||||||
{
|
{
|
||||||
case MiNum<byte> dataByte:
|
MiNum<byte> dataByte => ConvertToMatCharArray(flags, dimensions, name, dataByte),
|
||||||
return ConvertToMatCharArray(flags, dimensions, name, dataByte);
|
MiNum<ushort> dataUshort => ConvertToMatCharArray(flags, dimensions, name, dataUshort),
|
||||||
case MiNum<ushort> dataUshort:
|
_ => throw new NotSupportedException("Only utf8, utf16 or ushort char arrays are supported."),
|
||||||
return ConvertToMatCharArray(flags, dimensions, name, dataUshort);
|
};
|
||||||
default:
|
|
||||||
throw new NotSupportedException("Only utf8, utf16 or ushort char arrays are supported.");
|
|
||||||
}
|
|
||||||
case ArrayType.MxDouble:
|
case ArrayType.MxDouble:
|
||||||
case ArrayType.MxSingle:
|
case ArrayType.MxSingle:
|
||||||
case ArrayType.MxInt8:
|
case ArrayType.MxInt8:
|
||||||
@ -184,13 +184,12 @@ namespace MatFileHandler
|
|||||||
{
|
{
|
||||||
return DataExtraction.GetDataAsUInt8(data).Select(x => x != 0).ToArray() as T[];
|
return DataExtraction.GetDataAsUInt8(data).Select(x => x != 0).ToArray() as T[];
|
||||||
}
|
}
|
||||||
switch (data)
|
|
||||||
|
return data switch
|
||||||
{
|
{
|
||||||
case MiNum<double> _:
|
MiNum<double> => DataExtraction.GetDataAsDouble(data) as T[],
|
||||||
return DataExtraction.GetDataAsDouble(data) as T[];
|
_ => throw new NotSupportedException(),
|
||||||
default:
|
};
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MatCharArrayOf<ushort> ConvertToMatCharArray(
|
private static MatCharArrayOf<ushort> ConvertToMatCharArray(
|
||||||
@ -243,4 +242,4 @@ namespace MatFileHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@ -30,54 +28,33 @@ namespace MatFileHandler
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="reader">Input reader.</param>
|
/// <param name="reader">Input reader.</param>
|
||||||
/// <returns>Data element.</returns>
|
/// <returns>Data element.</returns>
|
||||||
public DataElement Read(BinaryReader reader)
|
public DataElement? Read(BinaryReader reader)
|
||||||
{
|
{
|
||||||
var (dataReader, tag) = ReadTag(reader);
|
var maybeTagPair = ReadTag(reader);
|
||||||
DataElement result;
|
if (maybeTagPair is not { } tagPair)
|
||||||
switch (tag.Type)
|
|
||||||
{
|
{
|
||||||
case DataType.MiInt8:
|
return null;
|
||||||
result = ReadNum<sbyte>(tag, dataReader);
|
|
||||||
break;
|
|
||||||
case DataType.MiUInt8:
|
|
||||||
case DataType.MiUtf8:
|
|
||||||
result = ReadNum<byte>(tag, dataReader);
|
|
||||||
break;
|
|
||||||
case DataType.MiInt16:
|
|
||||||
result = ReadNum<short>(tag, dataReader);
|
|
||||||
break;
|
|
||||||
case DataType.MiUInt16:
|
|
||||||
case DataType.MiUtf16:
|
|
||||||
result = ReadNum<ushort>(tag, dataReader);
|
|
||||||
break;
|
|
||||||
case DataType.MiInt32:
|
|
||||||
result = ReadNum<int>(tag, dataReader);
|
|
||||||
break;
|
|
||||||
case DataType.MiUInt32:
|
|
||||||
result = ReadNum<uint>(tag, dataReader);
|
|
||||||
break;
|
|
||||||
case DataType.MiSingle:
|
|
||||||
result = ReadNum<float>(tag, dataReader);
|
|
||||||
break;
|
|
||||||
case DataType.MiDouble:
|
|
||||||
result = ReadNum<double>(tag, dataReader);
|
|
||||||
break;
|
|
||||||
case DataType.MiInt64:
|
|
||||||
result = ReadNum<long>(tag, dataReader);
|
|
||||||
break;
|
|
||||||
case DataType.MiUInt64:
|
|
||||||
result = ReadNum<ulong>(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.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (dataReader, tag) = tagPair;
|
||||||
|
|
||||||
|
var result = tag.Type switch
|
||||||
|
{
|
||||||
|
DataType.MiInt8 => ReadNum<sbyte>(tag, dataReader),
|
||||||
|
DataType.MiUInt8 or DataType.MiUtf8 => ReadNum<byte>(tag, dataReader),
|
||||||
|
DataType.MiInt16 => ReadNum<short>(tag, dataReader),
|
||||||
|
DataType.MiUInt16 or DataType.MiUtf16 => ReadNum<ushort>(tag, dataReader),
|
||||||
|
DataType.MiInt32 => ReadNum<int>(tag, dataReader),
|
||||||
|
DataType.MiUInt32 => ReadNum<uint>(tag, dataReader),
|
||||||
|
DataType.MiSingle => ReadNum<float>(tag, dataReader),
|
||||||
|
DataType.MiDouble => ReadNum<double>(tag, dataReader),
|
||||||
|
DataType.MiInt64 => ReadNum<long>(tag, dataReader),
|
||||||
|
DataType.MiUInt64 => ReadNum<ulong>(tag, dataReader),
|
||||||
|
DataType.MiMatrix => ReadMatrix(tag, dataReader),
|
||||||
|
DataType.MiCompressed => ReadCompressed(tag, dataReader),
|
||||||
|
_ => throw new NotSupportedException("Unknown element."),
|
||||||
|
};
|
||||||
|
|
||||||
if (tag.Type != DataType.MiCompressed)
|
if (tag.Type != DataType.MiCompressed)
|
||||||
{
|
{
|
||||||
var position = reader.BaseStream.Position;
|
var position = reader.BaseStream.Position;
|
||||||
@ -122,7 +99,7 @@ namespace MatFileHandler
|
|||||||
private static ArrayFlags ReadArrayFlags(DataElement element)
|
private static ArrayFlags ReadArrayFlags(DataElement element)
|
||||||
{
|
{
|
||||||
var flagData = (element as MiNum<uint>)?.Data ??
|
var flagData = (element as MiNum<uint>)?.Data ??
|
||||||
throw new HandlerException("Unexpected type in array flags.");
|
throw new HandlerException("Unexpected type in array flags.");
|
||||||
var class_ = (ArrayType)(flagData[0] & 0xff);
|
var class_ = (ArrayType)(flagData[0] & 0xff);
|
||||||
var variableFlags = (flagData[0] >> 8) & 0x0e;
|
var variableFlags = (flagData[0] >> 8) & 0x0e;
|
||||||
return new ArrayFlags
|
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;
|
var typeHi = type >> 16;
|
||||||
if (typeHi == 0)
|
if (typeHi == 0)
|
||||||
{
|
{
|
||||||
@ -206,13 +205,13 @@ namespace MatFileHandler
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var length = typeHi;
|
var length = typeHi;
|
||||||
type = type & 0xffff;
|
type &= 0xffff;
|
||||||
var smallReader = new BinaryReader(new MemoryStream(reader.ReadBytes(4)));
|
var smallReader = new BinaryReader(new MemoryStream(reader.ReadBytes(4)));
|
||||||
return (smallReader, new Tag((DataType)type, length));
|
return (smallReader, new Tag((DataType)type, length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataElement ContinueReadingCellArray(
|
private MatCellArray ContinueReadingCellArray(
|
||||||
BinaryReader reader,
|
BinaryReader reader,
|
||||||
ArrayFlags flags,
|
ArrayFlags flags,
|
||||||
int[] dimensions,
|
int[] dimensions,
|
||||||
@ -241,7 +240,7 @@ namespace MatFileHandler
|
|||||||
var classNameElement = Read(reader) as MiNum<sbyte> ??
|
var classNameElement = Read(reader) as MiNum<sbyte> ??
|
||||||
throw new HandlerException("Unexpected type in class name.");
|
throw new HandlerException("Unexpected type in class name.");
|
||||||
var className = ReadName(classNameElement);
|
var className = ReadName(classNameElement);
|
||||||
var dataElement = Read(reader);
|
var dataElement = Read(reader) ?? throw new HandlerException("Missing opaque data element.");
|
||||||
var data = ReadData(dataElement);
|
var data = ReadData(dataElement);
|
||||||
if (data is MatNumericalArrayOf<uint> linkElement)
|
if (data is MatNumericalArrayOf<uint> linkElement)
|
||||||
{
|
{
|
||||||
@ -258,11 +257,11 @@ namespace MatFileHandler
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new Opaque(name, typeDescription, className, new int[] { }, data, subsystemData);
|
return new Opaque(name, typeDescription, className, Array.Empty<int>(), data, subsystemData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataElement ContinueReadingSparseArray(
|
private MatArray ContinueReadingSparseArray(
|
||||||
BinaryReader reader,
|
BinaryReader reader,
|
||||||
DataElement firstElement,
|
DataElement firstElement,
|
||||||
int[] dimensions,
|
int[] dimensions,
|
||||||
@ -273,7 +272,7 @@ namespace MatFileHandler
|
|||||||
throw new HandlerException("Unexpected type in row indices of a sparse array.");
|
throw new HandlerException("Unexpected type in row indices of a sparse array.");
|
||||||
var columnIndex = Read(reader) as MiNum<int> ??
|
var columnIndex = Read(reader) as MiNum<int> ??
|
||||||
throw new HandlerException("Unexpected type in column indices of a sparse array.");
|
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))
|
if (sparseArrayFlags.ArrayFlags.Variable.HasFlag(Variable.IsLogical))
|
||||||
{
|
{
|
||||||
return DataElementConverter.ConvertToMatSparseArrayOf<bool>(
|
return DataElementConverter.ConvertToMatSparseArrayOf<bool>(
|
||||||
@ -287,7 +286,7 @@ namespace MatFileHandler
|
|||||||
|
|
||||||
if (sparseArrayFlags.ArrayFlags.Variable.HasFlag(Variable.IsComplex))
|
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(
|
return DataElementConverter.ConvertToMatSparseArrayOfComplex(
|
||||||
sparseArrayFlags,
|
sparseArrayFlags,
|
||||||
dimensions,
|
dimensions,
|
||||||
@ -298,22 +297,20 @@ namespace MatFileHandler
|
|||||||
imaginaryData);
|
imaginaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (data)
|
return data switch
|
||||||
{
|
{
|
||||||
case MiNum<double> _:
|
MiNum<double> => DataElementConverter.ConvertToMatSparseArrayOf<double>(
|
||||||
return DataElementConverter.ConvertToMatSparseArrayOf<double>(
|
sparseArrayFlags,
|
||||||
sparseArrayFlags,
|
dimensions,
|
||||||
dimensions,
|
name,
|
||||||
name,
|
rowIndex.Data,
|
||||||
rowIndex.Data,
|
columnIndex.Data,
|
||||||
columnIndex.Data,
|
data),
|
||||||
data);
|
_ => throw new NotSupportedException("Only double and logical sparse arrays are supported."),
|
||||||
default:
|
};
|
||||||
throw new NotSupportedException("Only double and logical sparse arrays are supported.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataElement ContinueReadingStructure(
|
private MatStructureArray ContinueReadingStructure(
|
||||||
BinaryReader reader,
|
BinaryReader reader,
|
||||||
ArrayFlags flags,
|
ArrayFlags flags,
|
||||||
int[] dimensions,
|
int[] dimensions,
|
||||||
@ -343,30 +340,33 @@ namespace MatFileHandler
|
|||||||
return new MatStructureArray(flags, dimensions, name, fields);
|
return new MatStructureArray(flags, dimensions, name, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataElement Read(Stream stream)
|
|
||||||
{
|
|
||||||
using (var reader = new BinaryReader(stream))
|
|
||||||
{
|
|
||||||
return Read(reader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private DataElement ReadCompressed(Tag tag, BinaryReader reader)
|
private DataElement ReadCompressed(Tag tag, BinaryReader reader)
|
||||||
{
|
{
|
||||||
reader.ReadBytes(2);
|
reader.ReadBytes(2);
|
||||||
var compressedData = reader.ReadBytes(tag.Length - 6);
|
|
||||||
reader.ReadBytes(4);
|
DataElement element;
|
||||||
var resultStream = new MemoryStream();
|
|
||||||
using (var compressedStream = new MemoryStream(compressedData))
|
using (var substream = new Substream(reader.BaseStream, tag.Length - 6))
|
||||||
{
|
{
|
||||||
using (var stream = new DeflateStream(compressedStream, CompressionMode.Decompress, leaveOpen: true))
|
using (var deflateStream = new DeflateStream(substream, CompressionMode.Decompress))
|
||||||
|
using (var bufferedStream = new BufferedStream(deflateStream))
|
||||||
|
using (var positionTrackingStream = new PositionTrackingStream(bufferedStream))
|
||||||
|
using (var innerReader = new BinaryReader(positionTrackingStream))
|
||||||
{
|
{
|
||||||
stream.CopyTo(resultStream);
|
element = Read(innerReader) ?? throw new HandlerException("Missing compressed data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (substream.Position != substream.Length)
|
||||||
|
{
|
||||||
|
// In the pathological case that the deflate stream did not read the full
|
||||||
|
// length, then read out the rest manually (normally 1 byte).
|
||||||
|
reader.ReadBytes((int)(substream.Length - substream.Position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resultStream.Position = 0;
|
reader.ReadBytes(4);
|
||||||
return Read(resultStream);
|
|
||||||
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataElement ReadMatrix(Tag tag, BinaryReader reader)
|
private DataElement ReadMatrix(Tag tag, BinaryReader reader)
|
||||||
@ -376,7 +376,7 @@ namespace MatFileHandler
|
|||||||
return MatArray.Empty();
|
return MatArray.Empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
var element1 = Read(reader);
|
var element1 = Read(reader) ?? throw new HandlerException("Missing matrix data.");
|
||||||
var flags = ReadArrayFlags(element1);
|
var flags = ReadArrayFlags(element1);
|
||||||
if (flags.Class == ArrayType.MxOpaque)
|
if (flags.Class == ArrayType.MxOpaque)
|
||||||
{
|
{
|
||||||
@ -398,12 +398,12 @@ namespace MatFileHandler
|
|||||||
return ContinueReadingSparseArray(reader, element1, dimensions, name);
|
return ContinueReadingSparseArray(reader, element1, dimensions, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
var element4 = Read(reader);
|
var element4 = Read(reader) ?? throw new HandlerException("Missing matrix data.");
|
||||||
var data = ReadData(element4);
|
var data = ReadData(element4);
|
||||||
DataElement? imaginaryData = null;
|
DataElement? imaginaryData = null;
|
||||||
if (flags.Variable.HasFlag(Variable.IsComplex))
|
if (flags.Variable.HasFlag(Variable.IsComplex))
|
||||||
{
|
{
|
||||||
var element5 = Read(reader);
|
var element5 = Read(reader) ?? throw new HandlerException("Missing complex matrix data.");
|
||||||
imaginaryData = ReadData(element5);
|
imaginaryData = ReadData(element5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,26 +418,23 @@ namespace MatFileHandler
|
|||||||
switch (flags.Class)
|
switch (flags.Class)
|
||||||
{
|
{
|
||||||
case ArrayType.MxChar:
|
case ArrayType.MxChar:
|
||||||
switch (data)
|
return data switch
|
||||||
{
|
{
|
||||||
case MiNum<byte> _:
|
MiNum<byte> => DataElementConverter.ConvertToMatNumericalArrayOf<byte>(
|
||||||
return DataElementConverter.ConvertToMatNumericalArrayOf<byte>(
|
flags,
|
||||||
flags,
|
dimensions,
|
||||||
dimensions,
|
name,
|
||||||
name,
|
data,
|
||||||
data,
|
imaginaryData),
|
||||||
imaginaryData);
|
MiNum<ushort> => DataElementConverter.ConvertToMatNumericalArrayOf<ushort>(
|
||||||
case MiNum<ushort> _:
|
flags,
|
||||||
return DataElementConverter.ConvertToMatNumericalArrayOf<ushort>(
|
dimensions,
|
||||||
flags,
|
name,
|
||||||
dimensions,
|
data,
|
||||||
name,
|
imaginaryData),
|
||||||
data,
|
_ => throw new NotSupportedException(
|
||||||
imaginaryData);
|
$"This type of char array ({data.GetType()}) is not supported."),
|
||||||
default:
|
};
|
||||||
throw new NotSupportedException(
|
|
||||||
$"This type of char array ({data.GetType()}) is not supported.");
|
|
||||||
}
|
|
||||||
case ArrayType.MxInt8:
|
case ArrayType.MxInt8:
|
||||||
return DataElementConverter.ConvertToMatNumericalArrayOf<sbyte>(
|
return DataElementConverter.ConvertToMatNumericalArrayOf<sbyte>(
|
||||||
flags,
|
flags,
|
||||||
@ -523,4 +520,4 @@ namespace MatFileHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
@ -16,31 +14,21 @@ namespace MatFileHandler
|
|||||||
/// <returns>Contents of the elements, converted to Double.</returns>
|
/// <returns>Contents of the elements, converted to Double.</returns>
|
||||||
public static double[] GetDataAsDouble(DataElement element)
|
public static double[] GetDataAsDouble(DataElement element)
|
||||||
{
|
{
|
||||||
switch (element)
|
return element switch
|
||||||
{
|
{
|
||||||
case MiNum<sbyte> sbyteElement:
|
MiNum<sbyte> sbyteElement => SbyteToDouble(sbyteElement.Data),
|
||||||
return SbyteToDouble(sbyteElement.Data);
|
MiNum<byte> byteElement => ByteToDouble(byteElement.Data),
|
||||||
case MiNum<byte> byteElement:
|
MiNum<int> intElement => IntToDouble(intElement.Data),
|
||||||
return ByteToDouble(byteElement.Data);
|
MiNum<uint> uintElement => UintToDouble(uintElement.Data),
|
||||||
case MiNum<int> intElement:
|
MiNum<short> shortElement => ShortToDouble(shortElement.Data),
|
||||||
return IntToDouble(intElement.Data);
|
MiNum<ushort> ushortElement => UshortToDouble(ushortElement.Data),
|
||||||
case MiNum<uint> uintElement:
|
MiNum<long> longElement => LongToDouble(longElement.Data),
|
||||||
return UintToDouble(uintElement.Data);
|
MiNum<ulong> ulongElement => UlongToDouble(ulongElement.Data),
|
||||||
case MiNum<short> shortElement:
|
MiNum<float> floatElement => FloatToDouble(floatElement.Data),
|
||||||
return ShortToDouble(shortElement.Data);
|
MiNum<double> doubleElement => doubleElement.Data,
|
||||||
case MiNum<ushort> ushortElement:
|
_ => throw new HandlerException(
|
||||||
return UshortToDouble(ushortElement.Data);
|
$"Expected data element that would be convertible to double, found {element.GetType()}."),
|
||||||
case MiNum<long> longElement:
|
};
|
||||||
return LongToDouble(longElement.Data);
|
|
||||||
case MiNum<ulong> ulongElement:
|
|
||||||
return UlongToDouble(ulongElement.Data);
|
|
||||||
case MiNum<float> floatElement:
|
|
||||||
return FloatToDouble(floatElement.Data);
|
|
||||||
case MiNum<double> doubleElement:
|
|
||||||
return doubleElement.Data;
|
|
||||||
}
|
|
||||||
throw new HandlerException(
|
|
||||||
$"Expected data element that would be convertible to double, found {element.GetType()}.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -50,31 +38,21 @@ namespace MatFileHandler
|
|||||||
/// <returns>Contents of the elements, converted to Single.</returns>
|
/// <returns>Contents of the elements, converted to Single.</returns>
|
||||||
public static float[] GetDataAsSingle(DataElement element)
|
public static float[] GetDataAsSingle(DataElement element)
|
||||||
{
|
{
|
||||||
switch (element)
|
return element switch
|
||||||
{
|
{
|
||||||
case MiNum<sbyte> sbyteElement:
|
MiNum<sbyte> sbyteElement => SbyteToSingle(sbyteElement.Data),
|
||||||
return SbyteToSingle(sbyteElement.Data);
|
MiNum<byte> byteElement => ByteToSingle(byteElement.Data),
|
||||||
case MiNum<byte> byteElement:
|
MiNum<int> intElement => IntToSingle(intElement.Data),
|
||||||
return ByteToSingle(byteElement.Data);
|
MiNum<uint> uintElement => UintToSingle(uintElement.Data),
|
||||||
case MiNum<int> intElement:
|
MiNum<short> shortElement => ShortToSingle(shortElement.Data),
|
||||||
return IntToSingle(intElement.Data);
|
MiNum<ushort> ushortElement => UshortToSingle(ushortElement.Data),
|
||||||
case MiNum<uint> uintElement:
|
MiNum<long> longElement => LongToSingle(longElement.Data),
|
||||||
return UintToSingle(uintElement.Data);
|
MiNum<ulong> ulongElement => UlongToSingle(ulongElement.Data),
|
||||||
case MiNum<short> shortElement:
|
MiNum<float> floatElement => floatElement.Data,
|
||||||
return ShortToSingle(shortElement.Data);
|
MiNum<double> doubleElement => DoubleToSingle(doubleElement.Data),
|
||||||
case MiNum<ushort> ushortElement:
|
_ => throw new HandlerException(
|
||||||
return UshortToSingle(ushortElement.Data);
|
$"Expected data element that would be convertible to float, found {element.GetType()}."),
|
||||||
case MiNum<long> longElement:
|
};
|
||||||
return LongToSingle(longElement.Data);
|
|
||||||
case MiNum<ulong> ulongElement:
|
|
||||||
return UlongToSingle(ulongElement.Data);
|
|
||||||
case MiNum<float> floatElement:
|
|
||||||
return floatElement.Data;
|
|
||||||
case MiNum<double> doubleElement:
|
|
||||||
return DoubleToSingle(doubleElement.Data);
|
|
||||||
}
|
|
||||||
throw new HandlerException(
|
|
||||||
$"Expected data element that would be convertible to float, found {element.GetType()}.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -84,31 +62,21 @@ namespace MatFileHandler
|
|||||||
/// <returns>Contents of the elements, converted to Int8.</returns>
|
/// <returns>Contents of the elements, converted to Int8.</returns>
|
||||||
public static sbyte[] GetDataAsInt8(DataElement element)
|
public static sbyte[] GetDataAsInt8(DataElement element)
|
||||||
{
|
{
|
||||||
switch (element)
|
return element switch
|
||||||
{
|
{
|
||||||
case MiNum<sbyte> sbyteElement:
|
MiNum<sbyte> sbyteElement => sbyteElement.Data,
|
||||||
return sbyteElement.Data;
|
MiNum<byte> byteElement => ByteToSByte(byteElement.Data),
|
||||||
case MiNum<byte> byteElement:
|
MiNum<int> intElement => IntToSByte(intElement.Data),
|
||||||
return ByteToSByte(byteElement.Data);
|
MiNum<uint> uintElement => UintToSByte(uintElement.Data),
|
||||||
case MiNum<int> intElement:
|
MiNum<short> shortElement => ShortToSByte(shortElement.Data),
|
||||||
return IntToSByte(intElement.Data);
|
MiNum<ushort> ushortElement => UshortToSByte(ushortElement.Data),
|
||||||
case MiNum<uint> uintElement:
|
MiNum<long> longElement => LongToSByte(longElement.Data),
|
||||||
return UintToSByte(uintElement.Data);
|
MiNum<ulong> ulongElement => UlongToSByte(ulongElement.Data),
|
||||||
case MiNum<short> shortElement:
|
MiNum<float> floatElement => SingleToSByte(floatElement.Data),
|
||||||
return ShortToSByte(shortElement.Data);
|
MiNum<double> doubleElement => DoubleToSByte(doubleElement.Data),
|
||||||
case MiNum<ushort> ushortElement:
|
_ => throw new HandlerException(
|
||||||
return UshortToSByte(ushortElement.Data);
|
$"Expected data element that would be convertible to int8, found {element.GetType()}."),
|
||||||
case MiNum<long> longElement:
|
};
|
||||||
return LongToSByte(longElement.Data);
|
|
||||||
case MiNum<ulong> ulongElement:
|
|
||||||
return UlongToSByte(ulongElement.Data);
|
|
||||||
case MiNum<float> floatElement:
|
|
||||||
return SingleToSByte(floatElement.Data);
|
|
||||||
case MiNum<double> doubleElement:
|
|
||||||
return DoubleToSByte(doubleElement.Data);
|
|
||||||
}
|
|
||||||
throw new HandlerException(
|
|
||||||
$"Expected data element that would be convertible to int8, found {element.GetType()}.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -118,31 +86,21 @@ namespace MatFileHandler
|
|||||||
/// <returns>Contents of the elements, converted to UInt8.</returns>
|
/// <returns>Contents of the elements, converted to UInt8.</returns>
|
||||||
public static byte[] GetDataAsUInt8(DataElement element)
|
public static byte[] GetDataAsUInt8(DataElement element)
|
||||||
{
|
{
|
||||||
switch (element)
|
return element switch
|
||||||
{
|
{
|
||||||
case MiNum<sbyte> sbyteElement:
|
MiNum<sbyte> sbyteElement => SbyteToByte(sbyteElement.Data),
|
||||||
return SbyteToByte(sbyteElement.Data);
|
MiNum<byte> byteElement => byteElement.Data,
|
||||||
case MiNum<byte> byteElement:
|
MiNum<int> intElement => IntToByte(intElement.Data),
|
||||||
return byteElement.Data;
|
MiNum<uint> uintElement => UintToByte(uintElement.Data),
|
||||||
case MiNum<int> intElement:
|
MiNum<short> shortElement => ShortToByte(shortElement.Data),
|
||||||
return IntToByte(intElement.Data);
|
MiNum<ushort> ushortElement => UshortToByte(ushortElement.Data),
|
||||||
case MiNum<uint> uintElement:
|
MiNum<long> longElement => LongToByte(longElement.Data),
|
||||||
return UintToByte(uintElement.Data);
|
MiNum<ulong> ulongElement => UlongToByte(ulongElement.Data),
|
||||||
case MiNum<short> shortElement:
|
MiNum<float> floatElement => SingleToByte(floatElement.Data),
|
||||||
return ShortToByte(shortElement.Data);
|
MiNum<double> doubleElement => DoubleToByte(doubleElement.Data),
|
||||||
case MiNum<ushort> ushortElement:
|
_ => throw new HandlerException(
|
||||||
return UshortToByte(ushortElement.Data);
|
$"Expected data element that would be convertible to uint8, found {element.GetType()}."),
|
||||||
case MiNum<long> longElement:
|
};
|
||||||
return LongToByte(longElement.Data);
|
|
||||||
case MiNum<ulong> ulongElement:
|
|
||||||
return UlongToByte(ulongElement.Data);
|
|
||||||
case MiNum<float> floatElement:
|
|
||||||
return SingleToByte(floatElement.Data);
|
|
||||||
case MiNum<double> doubleElement:
|
|
||||||
return DoubleToByte(doubleElement.Data);
|
|
||||||
}
|
|
||||||
throw new HandlerException(
|
|
||||||
$"Expected data element that would be convertible to uint8, found {element.GetType()}.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -152,31 +110,21 @@ namespace MatFileHandler
|
|||||||
/// <returns>Contents of the elements, converted to Int16.</returns>
|
/// <returns>Contents of the elements, converted to Int16.</returns>
|
||||||
public static short[] GetDataAsInt16(DataElement element)
|
public static short[] GetDataAsInt16(DataElement element)
|
||||||
{
|
{
|
||||||
switch (element)
|
return element switch
|
||||||
{
|
{
|
||||||
case MiNum<sbyte> sbyteElement:
|
MiNum<sbyte> sbyteElement => SbyteToInt16(sbyteElement.Data),
|
||||||
return SbyteToInt16(sbyteElement.Data);
|
MiNum<byte> byteElement => ByteToInt16(byteElement.Data),
|
||||||
case MiNum<byte> byteElement:
|
MiNum<int> intElement => IntToInt16(intElement.Data),
|
||||||
return ByteToInt16(byteElement.Data);
|
MiNum<uint> uintElement => UintToInt16(uintElement.Data),
|
||||||
case MiNum<int> intElement:
|
MiNum<short> shortElement => shortElement.Data,
|
||||||
return IntToInt16(intElement.Data);
|
MiNum<ushort> ushortElement => UshortToInt16(ushortElement.Data),
|
||||||
case MiNum<uint> uintElement:
|
MiNum<long> longElement => LongToInt16(longElement.Data),
|
||||||
return UintToInt16(uintElement.Data);
|
MiNum<ulong> ulongElement => UlongToInt16(ulongElement.Data),
|
||||||
case MiNum<short> shortElement:
|
MiNum<float> floatElement => SingleToInt16(floatElement.Data),
|
||||||
return shortElement.Data;
|
MiNum<double> doubleElement => DoubleToInt16(doubleElement.Data),
|
||||||
case MiNum<ushort> ushortElement:
|
_ => throw new HandlerException(
|
||||||
return UshortToInt16(ushortElement.Data);
|
$"Expected data element that would be convertible to int16, found {element.GetType()}."),
|
||||||
case MiNum<long> longElement:
|
};
|
||||||
return LongToInt16(longElement.Data);
|
|
||||||
case MiNum<ulong> ulongElement:
|
|
||||||
return UlongToInt16(ulongElement.Data);
|
|
||||||
case MiNum<float> floatElement:
|
|
||||||
return SingleToInt16(floatElement.Data);
|
|
||||||
case MiNum<double> doubleElement:
|
|
||||||
return DoubleToInt16(doubleElement.Data);
|
|
||||||
}
|
|
||||||
throw new HandlerException(
|
|
||||||
$"Expected data element that would be convertible to int16, found {element.GetType()}.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -186,31 +134,21 @@ namespace MatFileHandler
|
|||||||
/// <returns>Contents of the elements, converted to UInt16.</returns>
|
/// <returns>Contents of the elements, converted to UInt16.</returns>
|
||||||
public static ushort[] GetDataAsUInt16(DataElement element)
|
public static ushort[] GetDataAsUInt16(DataElement element)
|
||||||
{
|
{
|
||||||
switch (element)
|
return element switch
|
||||||
{
|
{
|
||||||
case MiNum<sbyte> sbyteElement:
|
MiNum<sbyte> sbyteElement => SbyteToUInt16(sbyteElement.Data),
|
||||||
return SbyteToUInt16(sbyteElement.Data);
|
MiNum<byte> byteElement => ByteToUInt16(byteElement.Data),
|
||||||
case MiNum<byte> byteElement:
|
MiNum<int> intElement => IntToUInt16(intElement.Data),
|
||||||
return ByteToUInt16(byteElement.Data);
|
MiNum<uint> uintElement => UintToUInt16(uintElement.Data),
|
||||||
case MiNum<int> intElement:
|
MiNum<short> shortElement => ShortToUInt16(shortElement.Data),
|
||||||
return IntToUInt16(intElement.Data);
|
MiNum<ushort> ushortElement => ushortElement.Data,
|
||||||
case MiNum<uint> uintElement:
|
MiNum<long> longElement => LongToUInt16(longElement.Data),
|
||||||
return UintToUInt16(uintElement.Data);
|
MiNum<ulong> ulongElement => UlongToUInt16(ulongElement.Data),
|
||||||
case MiNum<short> shortElement:
|
MiNum<float> floatElement => SingleToUInt16(floatElement.Data),
|
||||||
return ShortToUInt16(shortElement.Data);
|
MiNum<double> doubleElement => DoubleToUInt16(doubleElement.Data),
|
||||||
case MiNum<ushort> ushortElement:
|
_ => throw new HandlerException(
|
||||||
return ushortElement.Data;
|
$"Expected data element that would be convertible to uint16, found {element.GetType()}."),
|
||||||
case MiNum<long> longElement:
|
};
|
||||||
return LongToUInt16(longElement.Data);
|
|
||||||
case MiNum<ulong> ulongElement:
|
|
||||||
return UlongToUInt16(ulongElement.Data);
|
|
||||||
case MiNum<float> floatElement:
|
|
||||||
return SingleToUInt16(floatElement.Data);
|
|
||||||
case MiNum<double> doubleElement:
|
|
||||||
return DoubleToUInt16(doubleElement.Data);
|
|
||||||
}
|
|
||||||
throw new HandlerException(
|
|
||||||
$"Expected data element that would be convertible to uint16, found {element.GetType()}.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -220,31 +158,21 @@ namespace MatFileHandler
|
|||||||
/// <returns>Contents of the elements, converted to Int32.</returns>
|
/// <returns>Contents of the elements, converted to Int32.</returns>
|
||||||
public static int[] GetDataAsInt32(DataElement element)
|
public static int[] GetDataAsInt32(DataElement element)
|
||||||
{
|
{
|
||||||
switch (element)
|
return element switch
|
||||||
{
|
{
|
||||||
case MiNum<sbyte> sbyteElement:
|
MiNum<sbyte> sbyteElement => SbyteToInt32(sbyteElement.Data),
|
||||||
return SbyteToInt32(sbyteElement.Data);
|
MiNum<byte> byteElement => ByteToInt32(byteElement.Data),
|
||||||
case MiNum<byte> byteElement:
|
MiNum<int> intElement => intElement.Data,
|
||||||
return ByteToInt32(byteElement.Data);
|
MiNum<uint> uintElement => UintToInt32(uintElement.Data),
|
||||||
case MiNum<int> intElement:
|
MiNum<short> shortElement => ShortToInt32(shortElement.Data),
|
||||||
return intElement.Data;
|
MiNum<ushort> ushortElement => UshortToInt32(ushortElement.Data),
|
||||||
case MiNum<uint> uintElement:
|
MiNum<long> longElement => LongToInt32(longElement.Data),
|
||||||
return UintToInt32(uintElement.Data);
|
MiNum<ulong> ulongElement => UlongToInt32(ulongElement.Data),
|
||||||
case MiNum<short> shortElement:
|
MiNum<float> floatElement => SingleToInt32(floatElement.Data),
|
||||||
return ShortToInt32(shortElement.Data);
|
MiNum<double> doubleElement => DoubleToInt32(doubleElement.Data),
|
||||||
case MiNum<ushort> ushortElement:
|
_ => throw new HandlerException(
|
||||||
return UshortToInt32(ushortElement.Data);
|
$"Expected data element that would be convertible to int32, found {element.GetType()}."),
|
||||||
case MiNum<long> longElement:
|
};
|
||||||
return LongToInt32(longElement.Data);
|
|
||||||
case MiNum<ulong> ulongElement:
|
|
||||||
return UlongToInt32(ulongElement.Data);
|
|
||||||
case MiNum<float> floatElement:
|
|
||||||
return SingleToInt32(floatElement.Data);
|
|
||||||
case MiNum<double> doubleElement:
|
|
||||||
return DoubleToInt32(doubleElement.Data);
|
|
||||||
}
|
|
||||||
throw new HandlerException(
|
|
||||||
$"Expected data element that would be convertible to int32, found {element.GetType()}.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -254,31 +182,21 @@ namespace MatFileHandler
|
|||||||
/// <returns>Contents of the elements, converted to UInt32.</returns>
|
/// <returns>Contents of the elements, converted to UInt32.</returns>
|
||||||
public static uint[] GetDataAsUInt32(DataElement element)
|
public static uint[] GetDataAsUInt32(DataElement element)
|
||||||
{
|
{
|
||||||
switch (element)
|
return element switch
|
||||||
{
|
{
|
||||||
case MiNum<sbyte> sbyteElement:
|
MiNum<sbyte> sbyteElement => SbyteToUInt32(sbyteElement.Data),
|
||||||
return SbyteToUInt32(sbyteElement.Data);
|
MiNum<byte> byteElement => ByteToUInt32(byteElement.Data),
|
||||||
case MiNum<byte> byteElement:
|
MiNum<int> intElement => IntToUInt32(intElement.Data),
|
||||||
return ByteToUInt32(byteElement.Data);
|
MiNum<uint> uintElement => uintElement.Data,
|
||||||
case MiNum<int> intElement:
|
MiNum<short> shortElement => ShortToUInt32(shortElement.Data),
|
||||||
return IntToUInt32(intElement.Data);
|
MiNum<ushort> ushortElement => UshortToUInt32(ushortElement.Data),
|
||||||
case MiNum<uint> uintElement:
|
MiNum<long> longElement => LongToUInt32(longElement.Data),
|
||||||
return uintElement.Data;
|
MiNum<ulong> ulongElement => UlongToUInt32(ulongElement.Data),
|
||||||
case MiNum<short> shortElement:
|
MiNum<float> floatElement => SingleToUInt32(floatElement.Data),
|
||||||
return ShortToUInt32(shortElement.Data);
|
MiNum<double> doubleElement => DoubleToUInt32(doubleElement.Data),
|
||||||
case MiNum<ushort> ushortElement:
|
_ => throw new HandlerException(
|
||||||
return UshortToUInt32(ushortElement.Data);
|
$"Expected data element that would be convertible to uint32, found {element.GetType()}."),
|
||||||
case MiNum<long> longElement:
|
};
|
||||||
return LongToUInt32(longElement.Data);
|
|
||||||
case MiNum<ulong> ulongElement:
|
|
||||||
return UlongToUInt32(ulongElement.Data);
|
|
||||||
case MiNum<float> floatElement:
|
|
||||||
return SingleToUInt32(floatElement.Data);
|
|
||||||
case MiNum<double> doubleElement:
|
|
||||||
return DoubleToUInt32(doubleElement.Data);
|
|
||||||
}
|
|
||||||
throw new HandlerException(
|
|
||||||
$"Expected data element that would be convertible to uint32, found {element.GetType()}.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -288,31 +206,21 @@ namespace MatFileHandler
|
|||||||
/// <returns>Contents of the elements, converted to Int64.</returns>
|
/// <returns>Contents of the elements, converted to Int64.</returns>
|
||||||
public static long[] GetDataAsInt64(DataElement element)
|
public static long[] GetDataAsInt64(DataElement element)
|
||||||
{
|
{
|
||||||
switch (element)
|
return element switch
|
||||||
{
|
{
|
||||||
case MiNum<sbyte> sbyteElement:
|
MiNum<sbyte> sbyteElement => SbyteToInt64(sbyteElement.Data),
|
||||||
return SbyteToInt64(sbyteElement.Data);
|
MiNum<byte> byteElement => ByteToInt64(byteElement.Data),
|
||||||
case MiNum<byte> byteElement:
|
MiNum<int> intElement => IntToInt64(intElement.Data),
|
||||||
return ByteToInt64(byteElement.Data);
|
MiNum<uint> uintElement => UintToInt64(uintElement.Data),
|
||||||
case MiNum<int> intElement:
|
MiNum<short> shortElement => ShortToInt64(shortElement.Data),
|
||||||
return IntToInt64(intElement.Data);
|
MiNum<ushort> ushortElement => UshortToInt64(ushortElement.Data),
|
||||||
case MiNum<uint> uintElement:
|
MiNum<long> longElement => longElement.Data,
|
||||||
return UintToInt64(uintElement.Data);
|
MiNum<ulong> ulongElement => UlongToInt64(ulongElement.Data),
|
||||||
case MiNum<short> shortElement:
|
MiNum<float> floatElement => SingleToInt64(floatElement.Data),
|
||||||
return ShortToInt64(shortElement.Data);
|
MiNum<double> doubleElement => DoubleToInt64(doubleElement.Data),
|
||||||
case MiNum<ushort> ushortElement:
|
_ => throw new HandlerException(
|
||||||
return UshortToInt64(ushortElement.Data);
|
$"Expected data element that would be convertible to int64, found {element.GetType()}."),
|
||||||
case MiNum<long> longElement:
|
};
|
||||||
return longElement.Data;
|
|
||||||
case MiNum<ulong> ulongElement:
|
|
||||||
return UlongToInt64(ulongElement.Data);
|
|
||||||
case MiNum<float> floatElement:
|
|
||||||
return SingleToInt64(floatElement.Data);
|
|
||||||
case MiNum<double> doubleElement:
|
|
||||||
return DoubleToInt64(doubleElement.Data);
|
|
||||||
}
|
|
||||||
throw new HandlerException(
|
|
||||||
$"Expected data element that would be convertible to int64, found {element.GetType()}.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -322,31 +230,21 @@ namespace MatFileHandler
|
|||||||
/// <returns>Contents of the elements, converted to UInt64.</returns>
|
/// <returns>Contents of the elements, converted to UInt64.</returns>
|
||||||
public static ulong[] GetDataAsUInt64(DataElement element)
|
public static ulong[] GetDataAsUInt64(DataElement element)
|
||||||
{
|
{
|
||||||
switch (element)
|
return element switch
|
||||||
{
|
{
|
||||||
case MiNum<sbyte> sbyteElement:
|
MiNum<sbyte> sbyteElement => SbyteToUInt64(sbyteElement.Data),
|
||||||
return SbyteToUInt64(sbyteElement.Data);
|
MiNum<byte> byteElement => ByteToUInt64(byteElement.Data),
|
||||||
case MiNum<byte> byteElement:
|
MiNum<int> intElement => IntToUInt64(intElement.Data),
|
||||||
return ByteToUInt64(byteElement.Data);
|
MiNum<uint> uintElement => UintToUInt64(uintElement.Data),
|
||||||
case MiNum<int> intElement:
|
MiNum<short> shortElement => ShortToUInt64(shortElement.Data),
|
||||||
return IntToUInt64(intElement.Data);
|
MiNum<ushort> ushortElement => UshortToUInt64(ushortElement.Data),
|
||||||
case MiNum<uint> uintElement:
|
MiNum<long> longElement => LongToUInt64(longElement.Data),
|
||||||
return UintToUInt64(uintElement.Data);
|
MiNum<ulong> ulongElement => ulongElement.Data,
|
||||||
case MiNum<short> shortElement:
|
MiNum<float> floatElement => SingleToUInt64(floatElement.Data),
|
||||||
return ShortToUInt64(shortElement.Data);
|
MiNum<double> doubleElement => DoubleToUInt64(doubleElement.Data),
|
||||||
case MiNum<ushort> ushortElement:
|
_ => throw new HandlerException(
|
||||||
return UshortToUInt64(ushortElement.Data);
|
$"Expected data element that would be convertible to uint64, found {element.GetType()}."),
|
||||||
case MiNum<long> longElement:
|
};
|
||||||
return LongToUInt64(longElement.Data);
|
|
||||||
case MiNum<ulong> ulongElement:
|
|
||||||
return ulongElement.Data;
|
|
||||||
case MiNum<float> floatElement:
|
|
||||||
return SingleToUInt64(floatElement.Data);
|
|
||||||
case MiNum<double> doubleElement:
|
|
||||||
return DoubleToUInt64(doubleElement.Data);
|
|
||||||
}
|
|
||||||
throw new HandlerException(
|
|
||||||
$"Expected data element that would be convertible to uint64, found {element.GetType()}.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// * to double
|
// * to double
|
||||||
@ -1395,4 +1293,4 @@ namespace MatFileHandler
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
@ -16,41 +14,21 @@ namespace MatFileHandler
|
|||||||
/// <returns>Size in bytes.</returns>
|
/// <returns>Size in bytes.</returns>
|
||||||
public static int Size(this DataType type)
|
public static int Size(this DataType type)
|
||||||
{
|
{
|
||||||
switch (type)
|
return type switch
|
||||||
{
|
{
|
||||||
case DataType.MiInt8:
|
DataType.MiInt8 or DataType.MiUInt8 => 1,
|
||||||
return 1;
|
DataType.MiInt16 or DataType.MiUInt16 => 2,
|
||||||
case DataType.MiUInt8:
|
DataType.MiInt32 or DataType.MiUInt32 => 4,
|
||||||
return 1;
|
DataType.MiSingle => 4,
|
||||||
case DataType.MiInt16:
|
DataType.MiDouble => 8,
|
||||||
return 2;
|
DataType.MiInt64 or DataType.MiUInt64 => 8,
|
||||||
case DataType.MiUInt16:
|
DataType.MiMatrix => 0,
|
||||||
return 2;
|
DataType.MiCompressed => 0,
|
||||||
case DataType.MiInt32:
|
DataType.MiUtf8 => 1,
|
||||||
return 4;
|
DataType.MiUtf16 => 2,
|
||||||
case DataType.MiUInt32:
|
DataType.MiUtf32 => 4,
|
||||||
return 4;
|
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null),
|
||||||
case DataType.MiSingle:
|
};
|
||||||
return 4;
|
|
||||||
case DataType.MiDouble:
|
|
||||||
return 8;
|
|
||||||
case DataType.MiInt64:
|
|
||||||
return 8;
|
|
||||||
case DataType.MiUInt64:
|
|
||||||
return 8;
|
|
||||||
case DataType.MiMatrix:
|
|
||||||
return 0;
|
|
||||||
case DataType.MiCompressed:
|
|
||||||
return 0;
|
|
||||||
case DataType.MiUtf8:
|
|
||||||
return 1;
|
|
||||||
case DataType.MiUtf16:
|
|
||||||
return 2;
|
|
||||||
case DataType.MiUtf32:
|
|
||||||
return 4;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@ -12,8 +10,6 @@ namespace MatFileHandler
|
|||||||
public class DatetimeAdapter
|
public class DatetimeAdapter
|
||||||
{
|
{
|
||||||
private readonly double[] data;
|
private readonly double[] data;
|
||||||
private readonly double[] data2;
|
|
||||||
private readonly int[] dimensions;
|
|
||||||
|
|
||||||
private readonly DateTimeOffset epoch;
|
private readonly DateTimeOffset epoch;
|
||||||
|
|
||||||
@ -36,15 +32,13 @@ namespace MatFileHandler
|
|||||||
case IArrayOf<double> dataArray:
|
case IArrayOf<double> dataArray:
|
||||||
data = dataArray.ConvertToDoubleArray()
|
data = dataArray.ConvertToDoubleArray()
|
||||||
?? throw new HandlerException("Cannot extract data for the datetime adapter.");
|
?? throw new HandlerException("Cannot extract data for the datetime adapter.");
|
||||||
data2 = new double[data.Length];
|
Dimensions = dataArray.Dimensions;
|
||||||
dimensions = dataArray.Dimensions;
|
|
||||||
break;
|
break;
|
||||||
case IArrayOf<Complex> dataComplex:
|
case IArrayOf<Complex> dataComplex:
|
||||||
var complexData = dataComplex.ConvertToComplexArray()
|
var complexData = dataComplex.ConvertToComplexArray()
|
||||||
?? throw new HandlerException("Cannot extract data for the datetime adapter.");
|
?? throw new HandlerException("Cannot extract data for the datetime adapter.");
|
||||||
data = complexData.Select(c => c.Real).ToArray();
|
data = complexData.Select(c => c.Real).ToArray();
|
||||||
data2 = complexData.Select(c => c.Imaginary).ToArray();
|
Dimensions = dataComplex.Dimensions;
|
||||||
dimensions = dataComplex.Dimensions;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new HandlerException("Datetime data not found.");
|
throw new HandlerException("Datetime data not found.");
|
||||||
@ -54,7 +48,7 @@ namespace MatFileHandler
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets datetime array dimensions.
|
/// Gets datetime array dimensions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int[] Dimensions => dimensions;
|
public int[] Dimensions { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets values of datetime object at given position in the array converted to <see cref="DateTimeOffset"/>.
|
/// Gets values of datetime object at given position in the array converted to <see cref="DateTimeOffset"/>.
|
||||||
@ -66,12 +60,12 @@ namespace MatFileHandler
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
var milliseconds = data[Dimensions.DimFlatten(list)];
|
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;
|
< -62_135_596_800_000.0 or > 253_402_300_799_999.0 => null,
|
||||||
}
|
_ => epoch.AddMilliseconds(milliseconds),
|
||||||
return epoch.AddMilliseconds(milliseconds);
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@ -81,4 +79,4 @@ namespace MatFileHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
@ -9,7 +7,6 @@ namespace MatFileHandler
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class DurationAdapter
|
public class DurationAdapter
|
||||||
{
|
{
|
||||||
private readonly int[] dimensions;
|
|
||||||
private readonly double[] data;
|
private readonly double[] data;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -27,13 +24,13 @@ namespace MatFileHandler
|
|||||||
var dataObject = matObject["millis", 0];
|
var dataObject = matObject["millis", 0];
|
||||||
data = dataObject.ConvertToDoubleArray()
|
data = dataObject.ConvertToDoubleArray()
|
||||||
?? throw new HandlerException("Cannot extract data for the duration adapter.");
|
?? throw new HandlerException("Cannot extract data for the duration adapter.");
|
||||||
dimensions = dataObject.Dimensions;
|
Dimensions = dataObject.Dimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets duration array dimensions.
|
/// Gets duration array dimensions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int[] Dimensions => dimensions;
|
public int[] Dimensions { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets duration object at given position.
|
/// Gets duration object at given position.
|
||||||
@ -42,4 +39,4 @@ namespace MatFileHandler
|
|||||||
/// <returns>Value.</returns>
|
/// <returns>Value.</returns>
|
||||||
public TimeSpan this[params int[] list] => TimeSpan.FromTicks((long)(10000.0 * data[Dimensions.DimFlatten(list)]));
|
public TimeSpan this[params int[] list] => TimeSpan.FromTicks((long)(10000.0 * data[Dimensions.DimFlatten(list)]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -57,4 +55,4 @@ namespace MatFileHandler
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IArrayOf<uint> Values { get; }
|
public IArrayOf<uint> Values { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
391
MatFileHandler/FakeWriter.cs
Normal file
391
MatFileHandler/FakeWriter.cs
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace MatFileHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A simulated writer of .mat files that just calculate the length of data that would be written.
|
||||||
|
/// </summary>
|
||||||
|
internal class FakeWriter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets current position of the writer.
|
||||||
|
/// </summary>
|
||||||
|
public int Position { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write contents of a numerical array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">A numerical array.</param>
|
||||||
|
/// <param name="name">Name of the array.</param>
|
||||||
|
public void WriteNumericalArrayContents(IArray array, string name)
|
||||||
|
{
|
||||||
|
WriteArrayFlags();
|
||||||
|
WriteDimensions(array.Dimensions);
|
||||||
|
WriteName(name);
|
||||||
|
WriteNumericalArrayValues(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write contents of a char array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="charArray">A char array.</param>
|
||||||
|
/// <param name="name">Name of the array.</param>
|
||||||
|
public void WriteCharArrayContents(ICharArray charArray, string name)
|
||||||
|
{
|
||||||
|
WriteArrayFlags();
|
||||||
|
WriteDimensions(charArray.Dimensions);
|
||||||
|
WriteName(name);
|
||||||
|
WriteDataElement(GetLengthOfByteArray<ushort>(charArray.String.Length));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write contents of a sparse array.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Array element type.</typeparam>
|
||||||
|
/// <param name="array">A sparse array.</param>
|
||||||
|
/// <param name="name">Name of the array.</param>
|
||||||
|
public void WriteSparseArrayContents<T>(
|
||||||
|
ISparseArrayOf<T> array,
|
||||||
|
string name)
|
||||||
|
where T : unmanaged, IEquatable<T>
|
||||||
|
{
|
||||||
|
(var rowsLength, var columnsLength, var dataLength, var nonZero) = PrepareSparseArrayData(array);
|
||||||
|
WriteSparseArrayFlags();
|
||||||
|
WriteDimensions(array.Dimensions);
|
||||||
|
WriteName(name);
|
||||||
|
WriteSparseArrayValues<T>(rowsLength, columnsLength, dataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write contents of a structure array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">A structure array.</param>
|
||||||
|
/// <param name="name">Name of the array.</param>
|
||||||
|
public void WriteStructureArrayContents(IStructureArray array, string name)
|
||||||
|
{
|
||||||
|
WriteArrayFlags();
|
||||||
|
WriteDimensions(array.Dimensions);
|
||||||
|
WriteName(name);
|
||||||
|
WriteFieldNames(array.FieldNames);
|
||||||
|
WriteStructureArrayValues(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write contents of a cell array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">A cell array.</param>
|
||||||
|
/// <param name="name">Name of the array.</param>
|
||||||
|
public void WriteCellArrayContents(ICellArray array, string name)
|
||||||
|
{
|
||||||
|
WriteArrayFlags();
|
||||||
|
WriteDimensions(array.Dimensions);
|
||||||
|
WriteName(name);
|
||||||
|
WriteCellArrayValues(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteTag()
|
||||||
|
{
|
||||||
|
Position += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteShortTag()
|
||||||
|
{
|
||||||
|
Position += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteWrappingContents<T>(T array, Action<FakeWriter> writeContents)
|
||||||
|
where T : IArray
|
||||||
|
{
|
||||||
|
if (array.IsEmpty)
|
||||||
|
{
|
||||||
|
WriteTag();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteTag();
|
||||||
|
writeContents(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteNumericalArrayValues(IArray value)
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case IArrayOf<sbyte> sbyteArray:
|
||||||
|
WriteDataElement(GetLengthOfByteArray<sbyte>(sbyteArray.Data.Length));
|
||||||
|
break;
|
||||||
|
case IArrayOf<byte> byteArray:
|
||||||
|
WriteDataElement(GetLengthOfByteArray<byte>(byteArray.Data.Length));
|
||||||
|
break;
|
||||||
|
case IArrayOf<short> shortArray:
|
||||||
|
WriteDataElement(GetLengthOfByteArray<short>(shortArray.Data.Length));
|
||||||
|
break;
|
||||||
|
case IArrayOf<ushort> ushortArray:
|
||||||
|
WriteDataElement(GetLengthOfByteArray<ushort>(ushortArray.Data.Length));
|
||||||
|
break;
|
||||||
|
case IArrayOf<int> intArray:
|
||||||
|
WriteDataElement(GetLengthOfByteArray<int>(intArray.Data.Length));
|
||||||
|
break;
|
||||||
|
case IArrayOf<uint> uintArray:
|
||||||
|
WriteDataElement(GetLengthOfByteArray<uint>(uintArray.Data.Length));
|
||||||
|
break;
|
||||||
|
case IArrayOf<long> longArray:
|
||||||
|
WriteDataElement(GetLengthOfByteArray<long>(longArray.Data.Length));
|
||||||
|
break;
|
||||||
|
case IArrayOf<ulong> ulongArray:
|
||||||
|
WriteDataElement(GetLengthOfByteArray<ulong>(ulongArray.Data.Length));
|
||||||
|
break;
|
||||||
|
case IArrayOf<float> floatArray:
|
||||||
|
WriteDataElement(GetLengthOfByteArray<float>(floatArray.Data.Length));
|
||||||
|
break;
|
||||||
|
case IArrayOf<double> doubleArray:
|
||||||
|
WriteDataElement(GetLengthOfByteArray<double>(doubleArray.Data.Length));
|
||||||
|
break;
|
||||||
|
case IArrayOf<bool> boolArray:
|
||||||
|
WriteDataElement(boolArray.Data.Length);
|
||||||
|
break;
|
||||||
|
case IArrayOf<ComplexOf<sbyte>> complexSbyteArray:
|
||||||
|
WriteComplexValues(GetLengthOfPairOfByteArrays(complexSbyteArray.Data));
|
||||||
|
break;
|
||||||
|
case IArrayOf<ComplexOf<byte>> complexByteArray:
|
||||||
|
WriteComplexValues(GetLengthOfPairOfByteArrays(complexByteArray.Data));
|
||||||
|
break;
|
||||||
|
case IArrayOf<ComplexOf<short>> complexShortArray:
|
||||||
|
WriteComplexValues(GetLengthOfPairOfByteArrays(complexShortArray.Data));
|
||||||
|
break;
|
||||||
|
case IArrayOf<ComplexOf<ushort>> complexUshortArray:
|
||||||
|
WriteComplexValues(GetLengthOfPairOfByteArrays(complexUshortArray.Data));
|
||||||
|
break;
|
||||||
|
case IArrayOf<ComplexOf<int>> complexIntArray:
|
||||||
|
WriteComplexValues(GetLengthOfPairOfByteArrays(complexIntArray.Data));
|
||||||
|
break;
|
||||||
|
case IArrayOf<ComplexOf<uint>> complexUintArray:
|
||||||
|
WriteComplexValues(GetLengthOfPairOfByteArrays(complexUintArray.Data));
|
||||||
|
break;
|
||||||
|
case IArrayOf<ComplexOf<long>> complexLongArray:
|
||||||
|
WriteComplexValues(GetLengthOfPairOfByteArrays(complexLongArray.Data));
|
||||||
|
break;
|
||||||
|
case IArrayOf<ComplexOf<ulong>> complexUlongArray:
|
||||||
|
WriteComplexValues(GetLengthOfPairOfByteArrays(complexUlongArray.Data));
|
||||||
|
break;
|
||||||
|
case IArrayOf<ComplexOf<float>> complexFloatArray:
|
||||||
|
WriteComplexValues(GetLengthOfPairOfByteArrays(complexFloatArray.Data));
|
||||||
|
break;
|
||||||
|
case IArrayOf<Complex> complexDoubleArray:
|
||||||
|
WriteComplexValues(GetLengthOfPairOfByteArrays(complexDoubleArray.Data));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteName(string name)
|
||||||
|
{
|
||||||
|
var nameBytes = Encoding.ASCII.GetBytes(name);
|
||||||
|
WriteDataElement(nameBytes.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteArrayFlags()
|
||||||
|
{
|
||||||
|
WriteTag();
|
||||||
|
Position += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteDimensions(int[] dimensions)
|
||||||
|
{
|
||||||
|
var buffer = GetLengthOfByteArray<int>(dimensions.Length);
|
||||||
|
WriteDataElement(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe int GetLengthOfByteArray<T>(int dataLength)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
return dataLength * sizeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe int GetLengthOfPairOfByteArrays<T>(ComplexOf<T>[] data)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
return data.Length * sizeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe int GetLengthOfPairOfByteArrays(Complex[] data)
|
||||||
|
{
|
||||||
|
return data.Length * sizeof(double);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalculatePadding(int length)
|
||||||
|
{
|
||||||
|
var rem = length % 8;
|
||||||
|
return rem == 0 ? 0 : 8 - rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteDataElement(int dataLength)
|
||||||
|
{
|
||||||
|
var maybePadding = 0;
|
||||||
|
if (dataLength > 4)
|
||||||
|
{
|
||||||
|
WriteTag();
|
||||||
|
Position += dataLength;
|
||||||
|
maybePadding = CalculatePadding(dataLength + 8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteShortTag();
|
||||||
|
Position += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position += maybePadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteComplexValues(
|
||||||
|
int dataLength)
|
||||||
|
{
|
||||||
|
WriteDataElement(dataLength);
|
||||||
|
WriteDataElement(dataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteSparseArrayValues<T>(int rowsLength, int columnsLength, int dataLength)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
WriteDataElement(GetLengthOfByteArray<int>(rowsLength));
|
||||||
|
WriteDataElement(GetLengthOfByteArray<int>(columnsLength));
|
||||||
|
if (typeof(T) == typeof(double))
|
||||||
|
{
|
||||||
|
WriteDataElement(GetLengthOfByteArray<double>(dataLength));
|
||||||
|
}
|
||||||
|
else if (typeof(T) == typeof(Complex))
|
||||||
|
{
|
||||||
|
WriteDataElement(GetLengthOfByteArray<double>(dataLength));
|
||||||
|
WriteDataElement(GetLengthOfByteArray<double>(dataLength));
|
||||||
|
}
|
||||||
|
else if (typeof(T) == typeof(bool))
|
||||||
|
{
|
||||||
|
WriteDataElement(dataLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (int rowIndexLength, int columnIndexLength, int dataLength, uint nonZero) PrepareSparseArrayData<T>(
|
||||||
|
ISparseArrayOf<T> array)
|
||||||
|
where T : struct, IEquatable<T>
|
||||||
|
{
|
||||||
|
var numberOfColumns = array.Dimensions[1];
|
||||||
|
var numberOfElements = array.Data.Values.Count(value => !value.Equals(default));
|
||||||
|
return (numberOfElements, numberOfColumns + 1, numberOfElements, (uint)numberOfElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteSparseArrayFlags()
|
||||||
|
{
|
||||||
|
WriteTag();
|
||||||
|
Position += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteFieldNames(IEnumerable<string> fieldNames)
|
||||||
|
{
|
||||||
|
var fieldNamesArray = fieldNames.Select(name => Encoding.ASCII.GetBytes(name)).ToArray();
|
||||||
|
var maxFieldName = fieldNamesArray.Max(name => name.Length) + 1;
|
||||||
|
WriteDataElement(GetLengthOfByteArray<int>(1));
|
||||||
|
var buffer = new byte[fieldNamesArray.Length * maxFieldName];
|
||||||
|
var startPosition = 0;
|
||||||
|
foreach (var name in fieldNamesArray)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < name.Length; i++)
|
||||||
|
{
|
||||||
|
buffer[startPosition + i] = name[i];
|
||||||
|
}
|
||||||
|
startPosition += maxFieldName;
|
||||||
|
}
|
||||||
|
WriteDataElement(buffer.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteStructureArrayValues(IStructureArray array)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < array.Count; i++)
|
||||||
|
{
|
||||||
|
foreach (var name in array.FieldNames)
|
||||||
|
{
|
||||||
|
WriteArray(array[name, i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteArray(IArray array, string variableName = "", bool isGlobal = false)
|
||||||
|
{
|
||||||
|
switch (array)
|
||||||
|
{
|
||||||
|
case ICharArray charArray:
|
||||||
|
WriteCharArray(charArray, variableName);
|
||||||
|
break;
|
||||||
|
case ISparseArrayOf<double> doubleSparseArray:
|
||||||
|
WriteSparseArray(doubleSparseArray, variableName);
|
||||||
|
break;
|
||||||
|
case ISparseArrayOf<Complex> complexSparseArray:
|
||||||
|
WriteSparseArray(complexSparseArray, variableName);
|
||||||
|
break;
|
||||||
|
case ISparseArrayOf<bool> boolSparseArray:
|
||||||
|
WriteSparseArray(boolSparseArray, variableName);
|
||||||
|
break;
|
||||||
|
case ICellArray cellArray:
|
||||||
|
WriteCellArray(cellArray, variableName);
|
||||||
|
break;
|
||||||
|
case IStructureArray structureArray:
|
||||||
|
WriteStructureArray(structureArray, variableName);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
WriteNumericalArray(array, variableName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteCharArray(ICharArray charArray, string name)
|
||||||
|
{
|
||||||
|
WriteWrappingContents(
|
||||||
|
charArray,
|
||||||
|
fakeWriter => fakeWriter.WriteCharArrayContents(charArray, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteSparseArray<T>(ISparseArrayOf<T> sparseArray, string name)
|
||||||
|
where T : unmanaged, IEquatable<T>
|
||||||
|
{
|
||||||
|
WriteWrappingContents(
|
||||||
|
sparseArray,
|
||||||
|
fakeWriter => fakeWriter.WriteSparseArrayContents(sparseArray, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteCellArray(ICellArray cellArray, string name)
|
||||||
|
{
|
||||||
|
WriteWrappingContents(
|
||||||
|
cellArray,
|
||||||
|
fakeWriter => fakeWriter.WriteCellArrayContents(cellArray, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteCellArrayValues(ICellArray array)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < array.Count; i++)
|
||||||
|
{
|
||||||
|
WriteArray(array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteStructureArray(
|
||||||
|
IStructureArray structureArray,
|
||||||
|
string name)
|
||||||
|
{
|
||||||
|
WriteWrappingContents(
|
||||||
|
structureArray,
|
||||||
|
fakeWriter => fakeWriter.WriteStructureArrayContents(structureArray, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteNumericalArray(
|
||||||
|
IArray numericalArray,
|
||||||
|
string name = "")
|
||||||
|
{
|
||||||
|
WriteWrappingContents(
|
||||||
|
numericalArray,
|
||||||
|
fakeWriter => fakeWriter.WriteNumericalArrayContents(numericalArray, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
@ -19,4 +17,4 @@ namespace MatFileHandler
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
#if !NET461
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
@ -72,11 +72,9 @@ namespace MatFileHandler
|
|||||||
var version = reader.ReadInt16();
|
var version = reader.ReadInt16();
|
||||||
var endian = reader.ReadInt16();
|
var endian = reader.ReadInt16();
|
||||||
var isLittleEndian = endian == 19785;
|
var isLittleEndian = endian == 19785;
|
||||||
if (!isLittleEndian)
|
return isLittleEndian
|
||||||
{
|
? new Header(text, subsystemDataOffset, version)
|
||||||
throw new NotSupportedException("Big-endian files are not supported.");
|
: throw new NotSupportedException("Big-endian files are not supported.");
|
||||||
}
|
|
||||||
return new Header(text, subsystemDataOffset, version);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetOperatingSystem()
|
private static string GetOperatingSystem()
|
||||||
@ -100,4 +98,4 @@ namespace MatFileHandler
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
@ -49,4 +47,4 @@ namespace MatFileHandler
|
|||||||
/// <returns>Array of values of the array, converted to Complex, or null if the conversion is not possible.</returns>
|
/// <returns>Array of values of the array, converted to Complex, or null if the conversion is not possible.</returns>
|
||||||
Complex[]? ConvertToComplexArray();
|
Complex[]? ConvertToComplexArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -36,4 +34,4 @@ namespace MatFileHandler
|
|||||||
/// <param name="list">Index of the element.</param>
|
/// <param name="list">Index of the element.</param>
|
||||||
T this[params int[] list] { get; set; }
|
T this[params int[] list] { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -8,4 +6,4 @@ namespace MatFileHandler
|
|||||||
public interface ICellArray : IArrayOf<IArray>
|
public interface ICellArray : IArrayOf<IArray>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -10,6 +8,8 @@ namespace MatFileHandler
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the contained string.
|
/// Gets the contained string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
#pragma warning disable CA1716, CA1720
|
||||||
string String { get; }
|
string String { get; }
|
||||||
|
#pragma warning restore CA1716, CA1720
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -27,6 +25,6 @@ namespace MatFileHandler
|
|||||||
/// <param name="name">The name of the variable to get.</param>
|
/// <param name="name">The name of the variable to get.</param>
|
||||||
/// <param name="variable">When this method returns, contains the variable with the specified name, if it is found; otherwise, null.</param>
|
/// <param name="variable">When this method returns, contains the variable with the specified name, if it is found; otherwise, null.</param>
|
||||||
/// <returns>True if the file contains a variable with the specified name; otherwise, false.</returns>
|
/// <returns>True if the file contains a variable with the specified name; otherwise, false.</returns>
|
||||||
public bool TryGetVariable(string name, out IVariable? variable);
|
bool TryGetVariable(string name, out IVariable? variable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
@ -17,4 +15,4 @@ namespace MatFileHandler
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
new IReadOnlyDictionary<(int row, int column), T> Data { get; }
|
new IReadOnlyDictionary<(int row, int column), T> Data { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
@ -21,4 +19,4 @@ namespace MatFileHandler
|
|||||||
/// <param name="list">Index of the element in the structure array.</param>
|
/// <param name="list">Index of the element in the structure array.</param>
|
||||||
IArray this[string field, params int[] list] { get; set; }
|
IArray this[string field, params int[] list] { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -22,4 +20,4 @@ namespace MatFileHandler
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsGlobal { get; }
|
bool IsGlobal { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
@ -51,7 +49,7 @@ namespace MatFileHandler
|
|||||||
/// <returns>Empty array.</returns>
|
/// <returns>Empty array.</returns>
|
||||||
public static MatArray Empty()
|
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<int>(), string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -78,4 +76,4 @@ namespace MatFileHandler
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@ -33,4 +31,4 @@ namespace MatFileHandler
|
|||||||
set => Data[Dimensions.DimFlatten(indices)] = value;
|
set => Data[Dimensions.DimFlatten(indices)] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -54,4 +52,4 @@ namespace MatFileHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@ -18,11 +16,7 @@ namespace MatFileHandler
|
|||||||
/// <param name="variables">List of variables.</param>
|
/// <param name="variables">List of variables.</param>
|
||||||
public MatFile(IEnumerable<IVariable> variables)
|
public MatFile(IEnumerable<IVariable> variables)
|
||||||
{
|
{
|
||||||
_variables = new Dictionary<string, IVariable>();
|
_variables = variables.ToDictionary(v => v.Name, v => v);
|
||||||
foreach (var variable in variables)
|
|
||||||
{
|
|
||||||
_variables[variable.Name] = variable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -41,4 +35,4 @@ namespace MatFileHandler
|
|||||||
return _variables.TryGetValue(name, out value);
|
return _variables.TryGetValue(name, out value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,45 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;net461;net472</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;net461;net472</TargetFrameworks>
|
||||||
<PackageVersion>1.4.0-beta3</PackageVersion>
|
<PackageVersion>1.4.0-beta6</PackageVersion>
|
||||||
<PackageId>MatFileHandler</PackageId>
|
<PackageId>MatFileHandler</PackageId>
|
||||||
<Title>A library for reading and writing MATLAB .mat files.</Title>
|
<Title>A library for reading and writing MATLAB .mat files.</Title>
|
||||||
<Authors>Alexander Luzgarev</Authors>
|
<Authors>Alexander Luzgarev</Authors>
|
||||||
<Description>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.</Description>
|
<Description>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.</Description>
|
||||||
<Copyright>Copyright 2017-2020 Alexander Luzgarev</Copyright>
|
<Copyright>Copyright 2017-2020 Alexander Luzgarev</Copyright>
|
||||||
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
<PackageProjectUrl>https://github.com/mahalex/MatFileHandler</PackageProjectUrl>
|
<PackageProjectUrl>https://git.mahalex.net/mahalex/MatFileHandler</PackageProjectUrl>
|
||||||
<PackageReleaseNotes>First release.</PackageReleaseNotes>
|
<PackageReleaseNotes>https://git.mahalex.net/mahalex/MatFileHandler/releases/</PackageReleaseNotes>
|
||||||
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<PackageTags>Matlab</PackageTags>
|
<PackageTags>Matlab</PackageTags>
|
||||||
<RepositoryUrl>https://github.com/mahalex/MatFileHandler</RepositoryUrl>
|
<RepositoryUrl>https://git.mahalex.net/mahalex/MatFileHandler</RepositoryUrl>
|
||||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||||
<DocumentationFile>$(OutputPath)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
<DocumentationFile>$(OutputPath)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<LangVersion>10.0</LangVersion>
|
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
|
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<AnalysisLevel>latest-Recommended</AnalysisLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
|
||||||
<CodeAnalysisRuleSet>..\MatFileHandler.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
|
||||||
<CodeAnalysisRuleSet>..\MatFileHandler.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AdditionalFiles Include="..\stylecop.json" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
|
|
||||||
<PrivateAssets>All</PrivateAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="System.ValueTuple" Version="4.4.0" Condition="'$(TargetFramework)' == 'net461'" />
|
<PackageReference Include="System.ValueTuple" Version="4.4.0" Condition="'$(TargetFramework)' == 'net461'" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\LICENSE.md" Pack="true" PackagePath=""/>
|
<PackageReference Include="Microsoft.SourceLink.Gitea" Version="8.0.0" PrivateAssets="All"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\README.md" Pack="true" PackagePath="\"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<InternalsVisibleTo Include="MatFileHandler.Tests" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
@ -28,10 +25,8 @@ namespace MatFileHandler
|
|||||||
/// <returns>Contents of the file.</returns>
|
/// <returns>Contents of the file.</returns>
|
||||||
public IMatFile Read()
|
public IMatFile Read()
|
||||||
{
|
{
|
||||||
using (var reader = new BinaryReader(Stream))
|
using var reader = new BinaryReader(new PositionTrackingStream(Stream));
|
||||||
{
|
return Read(reader);
|
||||||
return Read(reader);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -51,26 +46,24 @@ namespace MatFileHandler
|
|||||||
var dataElementReader = new DataElementReader(subsystemData);
|
var dataElementReader = new DataElementReader(subsystemData);
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
try
|
var position = reader.BaseStream.Position;
|
||||||
{
|
var dataElement = dataElementReader.Read(reader);
|
||||||
var position = reader.BaseStream.Position;
|
if (dataElement is null)
|
||||||
var dataElement = dataElementReader.Read(reader);
|
|
||||||
if (position == subsystemDataOffset)
|
|
||||||
{
|
|
||||||
var subsystemDataElement = dataElement as IArrayOf<byte>
|
|
||||||
?? 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)
|
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (position == subsystemDataOffset)
|
||||||
|
{
|
||||||
|
var subsystemDataElement = dataElement as IArrayOf<byte>
|
||||||
|
?? 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;
|
return variables;
|
||||||
@ -88,23 +81,21 @@ namespace MatFileHandler
|
|||||||
return ReadRawVariables(reader, subsystemDataOffset, subsystemData);
|
return ReadRawVariables(reader, subsystemDataOffset, subsystemData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IMatFile Read(BinaryReader reader)
|
private static MatFile Read(BinaryReader reader)
|
||||||
{
|
{
|
||||||
var header = ReadHeader(reader);
|
var header = ReadHeader(reader);
|
||||||
var rawVariables = ReadRawVariables(reader, header.SubsystemDataOffset);
|
var rawVariables = ReadRawVariables(reader, header.SubsystemDataOffset);
|
||||||
var variables = new List<IVariable>();
|
var variables = new List<IVariable>();
|
||||||
foreach (var variable in rawVariables)
|
foreach (var variable in rawVariables)
|
||||||
{
|
{
|
||||||
var array = variable.DataElement as MatArray;
|
if (variable.DataElement is MatArray array)
|
||||||
if (array is null)
|
|
||||||
{
|
{
|
||||||
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);
|
return new MatFile(variables);
|
||||||
@ -120,4 +111,4 @@ namespace MatFileHandler
|
|||||||
return SubsystemDataReader.Read(bytes, subsystemData);
|
return SubsystemDataReader.Read(bytes, subsystemData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@ -47,22 +45,28 @@ namespace MatFileHandler
|
|||||||
public void Write(IMatFile file)
|
public void Write(IMatFile file)
|
||||||
{
|
{
|
||||||
var header = Header.CreateNewHeader();
|
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);
|
switch (_options.UseCompression)
|
||||||
foreach (var variable in file.Variables)
|
|
||||||
{
|
{
|
||||||
switch (_options.UseCompression)
|
case CompressionUsage.Always:
|
||||||
{
|
if (Stream.CanSeek)
|
||||||
case CompressionUsage.Always:
|
{
|
||||||
WriteCompressedVariable(writer, variable);
|
WriteCompressedVariableToSeekableStream(writer, variable);
|
||||||
break;
|
}
|
||||||
case CompressionUsage.Never:
|
else
|
||||||
WriteVariable(writer, variable);
|
{
|
||||||
break;
|
WriteCompressedVariableToUnseekableStream(writer, variable);
|
||||||
default:
|
}
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
break;
|
||||||
|
case CompressionUsage.Never:
|
||||||
|
WriteVariable(writer, variable);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,7 +94,7 @@ namespace MatFileHandler
|
|||||||
return (s2 << 16) | s1;
|
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(Encoding.UTF8.GetBytes(header.Text));
|
||||||
writer.Write(header.SubsystemDataOffset);
|
writer.Write(header.SubsystemDataOffset);
|
||||||
@ -98,33 +102,30 @@ namespace MatFileHandler
|
|||||||
writer.Write((short)19785); // Magic number, 'IM'.
|
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((int)tag.Type);
|
||||||
writer.Write(tag.Length);
|
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.Type);
|
||||||
writer.Write((short)tag.Length);
|
writer.Write((short)tag.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WritePadding(BinaryWriter writer)
|
private static void WriteDataElement(BinaryWriter writer, DataType type, byte[] data)
|
||||||
{
|
|
||||||
var positionMod8 = writer.BaseStream.Position % 8;
|
|
||||||
if (positionMod8 != 0)
|
|
||||||
{
|
|
||||||
writer.Write(new byte[8 - positionMod8]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteDataElement(BinaryWriter writer, DataType type, byte[] data)
|
|
||||||
{
|
{
|
||||||
if (data.Length > 4)
|
if (data.Length > 4)
|
||||||
{
|
{
|
||||||
WriteTag(writer, new Tag(type, data.Length));
|
WriteTag(writer, new Tag(type, data.Length));
|
||||||
writer.Write(data);
|
writer.Write(data);
|
||||||
|
var rem = data.Length % 8;
|
||||||
|
if (rem > 0)
|
||||||
|
{
|
||||||
|
var padding = new byte[8 - rem];
|
||||||
|
writer.Write(padding);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -136,17 +137,16 @@ namespace MatFileHandler
|
|||||||
writer.Write(padding);
|
writer.Write(padding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WritePadding(writer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteDimensions(BinaryWriter writer, int[] dimensions)
|
private static void WriteDimensions(BinaryWriter writer, int[] dimensions)
|
||||||
{
|
{
|
||||||
var buffer = ConvertToByteArray(dimensions);
|
var buffer = ConvertToByteArray(dimensions);
|
||||||
WriteDataElement(writer, DataType.MiInt32, buffer);
|
WriteDataElement(writer, DataType.MiInt32, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] ConvertToByteArray<T>(T[] data)
|
private static byte[] ConvertToByteArray<T>(T[] data)
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
int size;
|
int size;
|
||||||
if (typeof(T) == typeof(sbyte))
|
if (typeof(T) == typeof(sbyte))
|
||||||
@ -189,6 +189,10 @@ namespace MatFileHandler
|
|||||||
{
|
{
|
||||||
size = sizeof(double);
|
size = sizeof(double);
|
||||||
}
|
}
|
||||||
|
else if (typeof(T) == typeof(bool))
|
||||||
|
{
|
||||||
|
size = sizeof(bool);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
@ -198,51 +202,51 @@ namespace MatFileHandler
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private (byte[] real, byte[] imaginary) ConvertToPairOfByteArrays<T>(ComplexOf<T>[] data)
|
private static (byte[] real, byte[] imaginary) ConvertToPairOfByteArrays<T>(ComplexOf<T>[] data)
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
return (ConvertToByteArray(data.Select(x => x.Real).ToArray()),
|
return (ConvertToByteArray(data.Select(x => x.Real).ToArray()),
|
||||||
ConvertToByteArray(data.Select(x => x.Imaginary).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()),
|
return (ConvertToByteArray(data.Select(x => x.Real).ToArray()),
|
||||||
ConvertToByteArray(data.Select(x => x.Imaginary).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.real);
|
||||||
WriteDataElement(writer, type, data.complex);
|
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;
|
var flag = (byte)flags.Variable;
|
||||||
WriteTag(writer, new Tag(DataType.MiUInt32, 8));
|
WriteTag(writer, new Tag(DataType.MiUInt32, 8));
|
||||||
writer.Write((byte)flags.Class);
|
writer.Write((byte)flags.Class);
|
||||||
writer.Write(flag);
|
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;
|
var flag = (byte)flags.ArrayFlags.Variable;
|
||||||
WriteTag(writer, new Tag(DataType.MiUInt32, 8));
|
WriteTag(writer, new Tag(DataType.MiUInt32, 8));
|
||||||
writer.Write((byte)flags.ArrayFlags.Class);
|
writer.Write((byte)flags.ArrayFlags.Class);
|
||||||
writer.Write(flag);
|
writer.Write(flag);
|
||||||
writer.Write(new byte[] { 0, 0 });
|
writer.Write([0, 0]);
|
||||||
writer.Write(flags.NzMax);
|
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);
|
var nameBytes = Encoding.ASCII.GetBytes(name);
|
||||||
WriteDataElement(writer, DataType.MiInt8, nameBytes);
|
WriteDataElement(writer, DataType.MiInt8, nameBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteNumericalArrayValues(BinaryWriter writer, IArray value)
|
private static void WriteNumericalArrayValues(BinaryWriter writer, IArray value)
|
||||||
{
|
{
|
||||||
switch (value)
|
switch (value)
|
||||||
{
|
{
|
||||||
@ -277,10 +281,7 @@ namespace MatFileHandler
|
|||||||
WriteDataElement(writer, DataType.MiDouble, ConvertToByteArray(doubleArray.Data));
|
WriteDataElement(writer, DataType.MiDouble, ConvertToByteArray(doubleArray.Data));
|
||||||
break;
|
break;
|
||||||
case IArrayOf<bool> boolArray:
|
case IArrayOf<bool> boolArray:
|
||||||
WriteDataElement(
|
WriteDataElement(writer, DataType.MiUInt8, ConvertToByteArray(boolArray.Data));
|
||||||
writer,
|
|
||||||
DataType.MiUInt8,
|
|
||||||
boolArray.Data.Select(element => element ? (byte)1 : (byte)0).ToArray());
|
|
||||||
break;
|
break;
|
||||||
case IArrayOf<ComplexOf<sbyte>> complexSbyteArray:
|
case IArrayOf<ComplexOf<sbyte>> complexSbyteArray:
|
||||||
WriteComplexValues(writer, DataType.MiInt8, ConvertToPairOfByteArrays(complexSbyteArray.Data));
|
WriteComplexValues(writer, DataType.MiInt8, ConvertToPairOfByteArrays(complexSbyteArray.Data));
|
||||||
@ -317,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;
|
var variableFlags = isGlobal ? Variable.IsGlobal : 0;
|
||||||
switch (array)
|
return array switch
|
||||||
{
|
{
|
||||||
case IArrayOf<sbyte> _:
|
IArrayOf<sbyte> => new ArrayFlags(ArrayType.MxInt8, variableFlags),
|
||||||
return new ArrayFlags(ArrayType.MxInt8, variableFlags);
|
IArrayOf<byte> => new ArrayFlags(ArrayType.MxUInt8, variableFlags),
|
||||||
case IArrayOf<byte> _:
|
IArrayOf<short> => new ArrayFlags(ArrayType.MxInt16, variableFlags),
|
||||||
return new ArrayFlags(ArrayType.MxUInt8, variableFlags);
|
IArrayOf<ushort> => new ArrayFlags(ArrayType.MxUInt16, variableFlags),
|
||||||
case IArrayOf<short> _:
|
IArrayOf<int> => new ArrayFlags(ArrayType.MxInt32, variableFlags),
|
||||||
return new ArrayFlags(ArrayType.MxInt16, variableFlags);
|
IArrayOf<uint> => new ArrayFlags(ArrayType.MxUInt32, variableFlags),
|
||||||
case IArrayOf<ushort> _:
|
IArrayOf<long> => new ArrayFlags(ArrayType.MxInt64, variableFlags),
|
||||||
return new ArrayFlags(ArrayType.MxUInt16, variableFlags);
|
IArrayOf<ulong> => new ArrayFlags(ArrayType.MxUInt64, variableFlags),
|
||||||
case IArrayOf<int> _:
|
IArrayOf<float> => new ArrayFlags(ArrayType.MxSingle, variableFlags),
|
||||||
return new ArrayFlags(ArrayType.MxInt32, variableFlags);
|
IArrayOf<double> => new ArrayFlags(ArrayType.MxDouble, variableFlags),
|
||||||
case IArrayOf<uint> _:
|
IArrayOf<bool> => new ArrayFlags(ArrayType.MxUInt8, variableFlags | Variable.IsLogical),
|
||||||
return new ArrayFlags(ArrayType.MxUInt32, variableFlags);
|
IArrayOf<ComplexOf<sbyte>> => new ArrayFlags(ArrayType.MxInt8, variableFlags | Variable.IsComplex),
|
||||||
case IArrayOf<long> _:
|
IArrayOf<ComplexOf<byte>> => new ArrayFlags(ArrayType.MxUInt8, variableFlags | Variable.IsComplex),
|
||||||
return new ArrayFlags(ArrayType.MxInt64, variableFlags);
|
IArrayOf<ComplexOf<short>> => new ArrayFlags(ArrayType.MxInt16, variableFlags | Variable.IsComplex),
|
||||||
case IArrayOf<ulong> _:
|
IArrayOf<ComplexOf<ushort>> => new ArrayFlags(ArrayType.MxUInt16, variableFlags | Variable.IsComplex),
|
||||||
return new ArrayFlags(ArrayType.MxUInt64, variableFlags);
|
IArrayOf<ComplexOf<int>> => new ArrayFlags(ArrayType.MxInt32, variableFlags | Variable.IsComplex),
|
||||||
case IArrayOf<float> _:
|
IArrayOf<ComplexOf<uint>> => new ArrayFlags(ArrayType.MxUInt32, variableFlags | Variable.IsComplex),
|
||||||
return new ArrayFlags(ArrayType.MxSingle, variableFlags);
|
IArrayOf<ComplexOf<long>> => new ArrayFlags(ArrayType.MxInt64, variableFlags | Variable.IsComplex),
|
||||||
case IArrayOf<double> _:
|
IArrayOf<ComplexOf<ulong>> => new ArrayFlags(ArrayType.MxUInt64, variableFlags | Variable.IsComplex),
|
||||||
return new ArrayFlags(ArrayType.MxDouble, variableFlags);
|
IArrayOf<ComplexOf<float>> => new ArrayFlags(ArrayType.MxSingle, variableFlags | Variable.IsComplex),
|
||||||
case IArrayOf<bool> _:
|
IArrayOf<Complex> => new ArrayFlags(ArrayType.MxDouble, variableFlags | Variable.IsComplex),
|
||||||
return new ArrayFlags(ArrayType.MxUInt8, variableFlags | Variable.IsLogical);
|
IStructureArray => new ArrayFlags(ArrayType.MxStruct, variableFlags),
|
||||||
case IArrayOf<ComplexOf<sbyte>> _:
|
ICellArray => new ArrayFlags(ArrayType.MxCell, variableFlags),
|
||||||
return new ArrayFlags(ArrayType.MxInt8, variableFlags | Variable.IsComplex);
|
_ => throw new NotSupportedException(),
|
||||||
case IArrayOf<ComplexOf<byte>> _:
|
};
|
||||||
return new ArrayFlags(ArrayType.MxUInt8, variableFlags | Variable.IsComplex);
|
|
||||||
case IArrayOf<ComplexOf<short>> _:
|
|
||||||
return new ArrayFlags(ArrayType.MxInt16, variableFlags | Variable.IsComplex);
|
|
||||||
case IArrayOf<ComplexOf<ushort>> _:
|
|
||||||
return new ArrayFlags(ArrayType.MxUInt16, variableFlags | Variable.IsComplex);
|
|
||||||
case IArrayOf<ComplexOf<int>> _:
|
|
||||||
return new ArrayFlags(ArrayType.MxInt32, variableFlags | Variable.IsComplex);
|
|
||||||
case IArrayOf<ComplexOf<uint>> _:
|
|
||||||
return new ArrayFlags(ArrayType.MxUInt32, variableFlags | Variable.IsComplex);
|
|
||||||
case IArrayOf<ComplexOf<long>> _:
|
|
||||||
return new ArrayFlags(ArrayType.MxInt64, variableFlags | Variable.IsComplex);
|
|
||||||
case IArrayOf<ComplexOf<ulong>> _:
|
|
||||||
return new ArrayFlags(ArrayType.MxUInt64, variableFlags | Variable.IsComplex);
|
|
||||||
case IArrayOf<ComplexOf<float>> _:
|
|
||||||
return new ArrayFlags(ArrayType.MxSingle, variableFlags | Variable.IsComplex);
|
|
||||||
case IArrayOf<Complex> _:
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SparseArrayFlags GetSparseArrayFlags<T>(ISparseArrayOf<T> array, bool isGlobal, uint nonZero)
|
private static SparseArrayFlags GetSparseArrayFlags<T>(ISparseArrayOf<T> array, bool isGlobal, uint nonZero)
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
var flags = GetArrayFlags(array, isGlobal);
|
var flags = GetArrayFlags(array, isGlobal);
|
||||||
@ -388,12 +365,16 @@ namespace MatFileHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayFlags GetCharArrayFlags(bool isGlobal)
|
private static ArrayFlags GetCharArrayFlags(bool isGlobal)
|
||||||
{
|
{
|
||||||
return new ArrayFlags(ArrayType.MxChar, isGlobal ? Variable.IsGlobal : 0);
|
return new ArrayFlags(ArrayType.MxChar, isGlobal ? Variable.IsGlobal : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteWrappingContents<T>(BinaryWriter writer, T array, Action<BinaryWriter> writeContents)
|
private static void WriteWrappingContents<T>(
|
||||||
|
BinaryWriter writer,
|
||||||
|
T array,
|
||||||
|
Action<FakeWriter> lengthCalculator,
|
||||||
|
Action<BinaryWriter> writeContents)
|
||||||
where T : IArray
|
where T : IArray
|
||||||
{
|
{
|
||||||
if (array.IsEmpty)
|
if (array.IsEmpty)
|
||||||
@ -401,19 +382,15 @@ namespace MatFileHandler
|
|||||||
WriteTag(writer, new Tag(DataType.MiMatrix, 0));
|
WriteTag(writer, new Tag(DataType.MiMatrix, 0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
using (var contents = new MemoryStream())
|
|
||||||
{
|
var fakeWriter = new FakeWriter();
|
||||||
using (var contentsWriter = new BinaryWriter(contents))
|
lengthCalculator(fakeWriter);
|
||||||
{
|
var calculatedLength = fakeWriter.Position;
|
||||||
writeContents(contentsWriter);
|
WriteTag(writer, new Tag(DataType.MiMatrix, calculatedLength));
|
||||||
WriteTag(writer, new Tag(DataType.MiMatrix, (int)contents.Length));
|
writeContents(writer);
|
||||||
contents.Position = 0;
|
|
||||||
contents.CopyTo(writer.BaseStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
WriteArrayFlags(writer, GetArrayFlags(array, isGlobal));
|
||||||
WriteDimensions(writer, array.Dimensions);
|
WriteDimensions(writer, array.Dimensions);
|
||||||
@ -421,7 +398,7 @@ namespace MatFileHandler
|
|||||||
WriteNumericalArrayValues(writer, array);
|
WriteNumericalArrayValues(writer, array);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteNumericalArray(
|
private static void WriteNumericalArray(
|
||||||
BinaryWriter writer,
|
BinaryWriter writer,
|
||||||
IArray numericalArray,
|
IArray numericalArray,
|
||||||
string name = "",
|
string name = "",
|
||||||
@ -430,10 +407,11 @@ namespace MatFileHandler
|
|||||||
WriteWrappingContents(
|
WriteWrappingContents(
|
||||||
writer,
|
writer,
|
||||||
numericalArray,
|
numericalArray,
|
||||||
|
fakeWriter => fakeWriter.WriteNumericalArrayContents(numericalArray, name),
|
||||||
contentsWriter => { WriteNumericalArrayContents(contentsWriter, numericalArray, name, isGlobal); });
|
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));
|
WriteArrayFlags(writer, GetCharArrayFlags(isGlobal));
|
||||||
WriteDimensions(writer, charArray.Dimensions);
|
WriteDimensions(writer, charArray.Dimensions);
|
||||||
@ -442,15 +420,16 @@ namespace MatFileHandler
|
|||||||
WriteDataElement(writer, DataType.MiUtf16, ConvertToByteArray(array));
|
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(
|
WriteWrappingContents(
|
||||||
writer,
|
writer,
|
||||||
charArray,
|
charArray,
|
||||||
|
fakeWriter => fakeWriter.WriteCharArrayContents(charArray, name),
|
||||||
contentsWriter => { WriteCharArrayContents(contentsWriter, charArray, name, isGlobal); });
|
contentsWriter => { WriteCharArrayContents(contentsWriter, charArray, name, isGlobal); });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteSparseArrayValues<T>(
|
private static void WriteSparseArrayValues<T>(
|
||||||
BinaryWriter writer, int[] rows, int[] columns, T[] data)
|
BinaryWriter writer, int[] rows, int[] columns, T[] data)
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
@ -460,9 +439,8 @@ namespace MatFileHandler
|
|||||||
{
|
{
|
||||||
WriteDataElement(writer, DataType.MiDouble, ConvertToByteArray(data));
|
WriteDataElement(writer, DataType.MiDouble, ConvertToByteArray(data));
|
||||||
}
|
}
|
||||||
else if (data is Complex[])
|
else if (data is Complex[] complexData)
|
||||||
{
|
{
|
||||||
var complexData = data as Complex[];
|
|
||||||
WriteDataElement(
|
WriteDataElement(
|
||||||
writer,
|
writer,
|
||||||
DataType.MiDouble,
|
DataType.MiDouble,
|
||||||
@ -472,17 +450,16 @@ namespace MatFileHandler
|
|||||||
DataType.MiDouble,
|
DataType.MiDouble,
|
||||||
ConvertToByteArray(complexData.Select(c => c.Imaginary).ToArray()));
|
ConvertToByteArray(complexData.Select(c => c.Imaginary).ToArray()));
|
||||||
}
|
}
|
||||||
else if (data is bool[])
|
else if (data is bool[] boolData)
|
||||||
{
|
{
|
||||||
var boolData = data as bool[];
|
|
||||||
WriteDataElement(
|
WriteDataElement(
|
||||||
writer,
|
writer,
|
||||||
DataType.MiUInt8,
|
DataType.MiUInt8,
|
||||||
boolData.Select(element => element ? (byte)1 : (byte)0).ToArray());
|
ConvertToByteArray(boolData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private (int[] rowIndex, int[] columnIndex, T[] data, uint nonZero) PrepareSparseArrayData<T>(
|
private static (int[] rowIndex, int[] columnIndex, T[] data, uint nonZero) PrepareSparseArrayData<T>(
|
||||||
ISparseArrayOf<T> array)
|
ISparseArrayOf<T> array)
|
||||||
where T : struct, IEquatable<T>
|
where T : struct, IEquatable<T>
|
||||||
{
|
{
|
||||||
@ -505,7 +482,7 @@ namespace MatFileHandler
|
|||||||
return (rowIndexList.ToArray(), columnIndex, valuesList.ToArray(), (uint)rowIndexList.Count);
|
return (rowIndexList.ToArray(), columnIndex, valuesList.ToArray(), (uint)rowIndexList.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteSparseArrayContents<T>(
|
private static void WriteSparseArrayContents<T>(
|
||||||
BinaryWriter writer,
|
BinaryWriter writer,
|
||||||
ISparseArrayOf<T> array,
|
ISparseArrayOf<T> array,
|
||||||
string name,
|
string name,
|
||||||
@ -519,20 +496,21 @@ namespace MatFileHandler
|
|||||||
WriteSparseArrayValues(writer, rows, columns, data);
|
WriteSparseArrayValues(writer, rows, columns, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteSparseArray<T>(BinaryWriter writer, ISparseArrayOf<T> sparseArray, string name, bool isGlobal)
|
private static void WriteSparseArray<T>(BinaryWriter writer, ISparseArrayOf<T> sparseArray, string name, bool isGlobal)
|
||||||
where T : struct, IEquatable<T>
|
where T : unmanaged, IEquatable<T>
|
||||||
{
|
{
|
||||||
WriteWrappingContents(
|
WriteWrappingContents(
|
||||||
writer,
|
writer,
|
||||||
sparseArray,
|
sparseArray,
|
||||||
|
fakeWriter => fakeWriter.WriteSparseArrayContents(sparseArray, name),
|
||||||
contentsWriter => { WriteSparseArrayContents(contentsWriter, sparseArray, name, isGlobal); });
|
contentsWriter => { WriteSparseArrayContents(contentsWriter, sparseArray, name, isGlobal); });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteFieldNames(BinaryWriter writer, IEnumerable<string> fieldNames)
|
private static void WriteFieldNames(BinaryWriter writer, IEnumerable<string> fieldNames)
|
||||||
{
|
{
|
||||||
var fieldNamesArray = fieldNames.Select(name => Encoding.ASCII.GetBytes(name)).ToArray();
|
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(writer, DataType.MiInt32, ConvertToByteArray(new[] { maxFieldName }));
|
WriteDataElement(writer, DataType.MiInt32, ConvertToByteArray([maxFieldName]));
|
||||||
var buffer = new byte[fieldNamesArray.Length * maxFieldName];
|
var buffer = new byte[fieldNamesArray.Length * maxFieldName];
|
||||||
var startPosition = 0;
|
var startPosition = 0;
|
||||||
foreach (var name in fieldNamesArray)
|
foreach (var name in fieldNamesArray)
|
||||||
@ -575,6 +553,7 @@ namespace MatFileHandler
|
|||||||
WriteWrappingContents(
|
WriteWrappingContents(
|
||||||
writer,
|
writer,
|
||||||
structureArray,
|
structureArray,
|
||||||
|
fakeWriter => fakeWriter.WriteStructureArrayContents(structureArray, name),
|
||||||
contentsWriter => { WriteStructureArrayContents(contentsWriter, structureArray, name, isGlobal); });
|
contentsWriter => { WriteStructureArrayContents(contentsWriter, structureArray, name, isGlobal); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -599,6 +578,7 @@ namespace MatFileHandler
|
|||||||
WriteWrappingContents(
|
WriteWrappingContents(
|
||||||
writer,
|
writer,
|
||||||
cellArray,
|
cellArray,
|
||||||
|
fakeWriter => fakeWriter.WriteCellArrayContents(cellArray, name),
|
||||||
contentsWriter => { WriteCellArrayContents(contentsWriter, cellArray, name, isGlobal); });
|
contentsWriter => { WriteCellArrayContents(contentsWriter, cellArray, name, isGlobal); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -635,33 +615,55 @@ namespace MatFileHandler
|
|||||||
WriteArray(writer, variable.Value, variable.Name, variable.IsGlobal);
|
WriteArray(writer, variable.Value, variable.Name, variable.IsGlobal);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteCompressedVariable(BinaryWriter writer, IVariable variable)
|
private void WriteCompressedVariableToSeekableStream(BinaryWriter writer, IVariable variable)
|
||||||
{
|
{
|
||||||
using (var compressedStream = new MemoryStream())
|
var position = writer.BaseStream.Position;
|
||||||
|
WriteTag(writer, new Tag(DataType.MiCompressed, 0));
|
||||||
|
writer.Write((byte)0x78);
|
||||||
|
writer.Write((byte)0x9c);
|
||||||
|
int compressedLength;
|
||||||
|
uint crc;
|
||||||
|
var before = writer.BaseStream.Position;
|
||||||
|
using (var compressionStream = new DeflateStream(writer.BaseStream, CompressionMode.Compress, leaveOpen: true))
|
||||||
{
|
{
|
||||||
uint crc;
|
using var checksumStream = new ChecksumCalculatingStream(compressionStream);
|
||||||
using (var originalStream = new MemoryStream())
|
using var internalWriter = new BinaryWriter(checksumStream, Encoding.UTF8, leaveOpen: true);
|
||||||
{
|
WriteVariable(internalWriter, variable);
|
||||||
using (var internalWriter = new BinaryWriter(originalStream))
|
crc = checksumStream.GetCrc();
|
||||||
{
|
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var after = writer.BaseStream.Position;
|
||||||
|
compressedLength = (int)(after - before) + 6;
|
||||||
|
|
||||||
|
writer.Write(BitConverter.GetBytes(crc).Reverse().ToArray());
|
||||||
|
writer.BaseStream.Position = position;
|
||||||
|
WriteTag(writer, new Tag(DataType.MiCompressed, compressedLength));
|
||||||
|
writer.BaseStream.Seek(0, SeekOrigin.End);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteCompressedVariableToUnseekableStream(BinaryWriter writer, IVariable variable)
|
||||||
|
{
|
||||||
|
using var compressedStream = 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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -10,7 +8,7 @@ namespace MatFileHandler
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets default options.
|
/// Gets default options.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static MatFileWriterOptions Default => new MatFileWriterOptions
|
public static MatFileWriterOptions Default => new()
|
||||||
{
|
{
|
||||||
UseCompression = CompressionUsage.Always,
|
UseCompression = CompressionUsage.Always,
|
||||||
};
|
};
|
||||||
@ -20,4 +18,4 @@ namespace MatFileHandler
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public CompressionUsage UseCompression { get; set; }
|
public CompressionUsage UseCompression { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
@ -197,4 +195,4 @@ namespace MatFileHandler
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -49,7 +47,7 @@ namespace MatFileHandler
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
var rowAndColumn = GetRowAndColumn(list);
|
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;
|
set => DataDictionary[GetRowAndColumn(list)] = value;
|
||||||
}
|
}
|
||||||
@ -61,7 +59,7 @@ namespace MatFileHandler
|
|||||||
public override double[] ConvertToDoubleArray()
|
public override double[] ConvertToDoubleArray()
|
||||||
{
|
{
|
||||||
var data = ((IArrayOf<T>)this).Data;
|
var data = ((IArrayOf<T>)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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -75,7 +73,7 @@ namespace MatFileHandler
|
|||||||
var result = new double[Dimensions[0], Dimensions[1]];
|
var result = new double[Dimensions[0], Dimensions[1]];
|
||||||
foreach (var pair in Data)
|
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;
|
return result;
|
||||||
@ -93,15 +91,12 @@ namespace MatFileHandler
|
|||||||
|
|
||||||
private (int row, int column) GetRowAndColumn(int[] indices)
|
private (int row, int column) GetRowAndColumn(int[] indices)
|
||||||
{
|
{
|
||||||
switch (indices.Length)
|
return indices.Length switch
|
||||||
{
|
{
|
||||||
case 1:
|
1 => (indices[0] % Dimensions[0], indices[0] / Dimensions[0]),
|
||||||
return (indices[0] % Dimensions[0], indices[0] / Dimensions[0]);
|
2 => (indices[0], indices[1]),
|
||||||
case 2:
|
_ => throw new NotSupportedException("Invalid index for sparse array."),
|
||||||
return (indices[0], indices[1]);
|
};
|
||||||
default:
|
|
||||||
throw new NotSupportedException("Invalid index for sparse array.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -69,7 +67,7 @@ namespace MatFileHandler
|
|||||||
"Cannot set structure elements via this[params int[]] indexer. Use this[string, int[]] instead.");
|
"Cannot set structure elements via this[params int[]] indexer. Use this[string, int[]] instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private IReadOnlyDictionary<string, IArray> ExtractStructure(int i)
|
private MatStructureArrayElement ExtractStructure(int i)
|
||||||
{
|
{
|
||||||
return new MatStructureArrayElement(this, i);
|
return new MatStructureArrayElement(this, i);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -27,4 +25,4 @@ namespace MatFileHandler
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsGlobal { get; }
|
public bool IsGlobal { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -23,4 +21,4 @@ namespace MatFileHandler
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public T[] Data { get; }
|
public T[] Data { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
MatFileHandler/ObjectParser.cs
Normal file
27
MatFileHandler/ObjectParser.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace MatFileHandler;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parser for object data.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ObjectParser
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parse object data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uintArray">Opaque link array.</param>
|
||||||
|
/// <param name="subsystemData">Current subsystem data.</param>
|
||||||
|
/// <returns>Parsed object.</returns>
|
||||||
|
public static IArray ParseObject(MatNumericalArrayOf<uint> 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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
@ -62,4 +60,4 @@ namespace MatFileHandler
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override double[]? ConvertToDoubleArray() => null;
|
public override double[]? ConvertToDoubleArray() => null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -78,15 +76,7 @@ namespace MatFileHandler
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IArray this[string field, params int[] list]
|
public IArray this[string field, params int[] list]
|
||||||
{
|
{
|
||||||
get
|
get => TryGetValue(field, out var result, list) ? result! : throw new ArgumentOutOfRangeException(nameof(list));
|
||||||
{
|
|
||||||
if (TryGetValue(field, out var result, list))
|
|
||||||
{
|
|
||||||
return result!;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IndexOutOfRangeException();
|
|
||||||
}
|
|
||||||
set => throw new NotImplementedException();
|
set => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +87,7 @@ namespace MatFileHandler
|
|||||||
set => throw new NotImplementedException();
|
set => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IReadOnlyDictionary<string, IArray> ExtractObject(int i)
|
private OpaqueObjectArrayElement ExtractObject(int i)
|
||||||
{
|
{
|
||||||
return new OpaqueObjectArrayElement(this, i);
|
return new OpaqueObjectArrayElement(this, i);
|
||||||
}
|
}
|
||||||
@ -106,7 +96,7 @@ namespace MatFileHandler
|
|||||||
{
|
{
|
||||||
var index = Dimensions.DimFlatten(list);
|
var index = Dimensions.DimFlatten(list);
|
||||||
var maybeFieldIndex = SubsystemData.ClassInformation[ClassIndex].FindField(field);
|
var maybeFieldIndex = SubsystemData.ClassInformation[ClassIndex].FindField(field);
|
||||||
if (!(maybeFieldIndex is int fieldIndex))
|
if (maybeFieldIndex is not int fieldIndex)
|
||||||
{
|
{
|
||||||
output = default;
|
output = default;
|
||||||
return false;
|
return false;
|
||||||
@ -171,8 +161,8 @@ namespace MatFileHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
#pragma warning disable CS8767
|
#pragma warning disable CS8767
|
||||||
|
/// <inheritdoc />
|
||||||
public bool TryGetValue(string key, out IArray? value)
|
public bool TryGetValue(string key, out IArray? value)
|
||||||
#pragma warning restore CS8767
|
#pragma warning restore CS8767
|
||||||
{
|
{
|
||||||
@ -186,4 +176,4 @@ namespace MatFileHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
75
MatFileHandler/PositionTrackingStream.cs
Normal file
75
MatFileHandler/PositionTrackingStream.cs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace MatFileHandler;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A stream which wraps another stream and tracks the number of bytes read
|
||||||
|
/// for the purpose of adjusting for padding.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class PositionTrackingStream : Stream
|
||||||
|
{
|
||||||
|
private readonly Stream _baseStream;
|
||||||
|
private long _position;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PositionTrackingStream"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baseStream">The stream to wrap.</param>
|
||||||
|
public PositionTrackingStream(Stream baseStream)
|
||||||
|
{
|
||||||
|
_baseStream = baseStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CanRead => _baseStream.CanRead;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CanSeek => false;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override long Length => _baseStream.Length;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get => _position;
|
||||||
|
set => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Flush() => _baseStream.Flush();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
var bytesRead = _baseStream.Read(buffer, offset, count);
|
||||||
|
|
||||||
|
_position += bytesRead;
|
||||||
|
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void SetLength(long value) => throw new NotSupportedException();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_baseStream.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
@ -33,4 +31,4 @@ namespace MatFileHandler
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public long Offset { get; }
|
public long Offset { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
@ -10,7 +8,6 @@ namespace MatFileHandler
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class StringAdapter
|
public class StringAdapter
|
||||||
{
|
{
|
||||||
private readonly int[] dimensions;
|
|
||||||
private readonly string[] strings;
|
private readonly string[] strings;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -28,13 +25,13 @@ namespace MatFileHandler
|
|||||||
var binaryData = matObject["any", 0] as IArrayOf<ulong>
|
var binaryData = matObject["any", 0] as IArrayOf<ulong>
|
||||||
?? throw new HandlerException("Cannot extract string data.");
|
?? throw new HandlerException("Cannot extract string data.");
|
||||||
|
|
||||||
(dimensions, strings) = ParseBinaryData(binaryData.Data);
|
(Dimensions, strings) = ParseBinaryData(binaryData.Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets string array dimensions.
|
/// Gets string array dimensions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int[] Dimensions => dimensions;
|
public int[] Dimensions { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets string object at given position.
|
/// Gets string object at given position.
|
||||||
@ -76,4 +73,4 @@ namespace MatFileHandler
|
|||||||
return (d, strings);
|
return (d, strings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
66
MatFileHandler/Substream.cs
Normal file
66
MatFileHandler/Substream.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace MatFileHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A stream which reads a finite section of another stream.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class Substream : Stream
|
||||||
|
{
|
||||||
|
private readonly Stream _baseStream;
|
||||||
|
private long _bytesRead;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Substream"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baseStream">The <see cref="Stream"/> to wrap.</param>
|
||||||
|
/// <param name="length">The number of bytes readable from this <see cref="Substream"/>.</param>
|
||||||
|
public Substream(Stream baseStream, long length)
|
||||||
|
{
|
||||||
|
_baseStream = baseStream;
|
||||||
|
Length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CanRead => _baseStream.CanRead;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CanSeek => false;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override long Length { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get => _bytesRead;
|
||||||
|
set => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Flush() => _baseStream.Flush();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
var bytesRead = _baseStream.Read(buffer, offset, (int)Math.Min(count, Length - _bytesRead));
|
||||||
|
|
||||||
|
_bytesRead += bytesRead;
|
||||||
|
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void SetLength(long value) => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
@ -119,12 +117,7 @@ namespace MatFileHandler
|
|||||||
/// <returns>Field index.</returns>
|
/// <returns>Field index.</returns>
|
||||||
public int? FindField(string fieldName)
|
public int? FindField(string fieldName)
|
||||||
{
|
{
|
||||||
if (fieldToIndex.TryGetValue(fieldName, out var index))
|
return fieldToIndex.TryGetValue(fieldName, out var index) ? index : null;
|
||||||
{
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,4 +185,4 @@ namespace MatFileHandler
|
|||||||
public string[] FieldNames { get; }
|
public string[] FieldNames { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -317,27 +314,47 @@ namespace MatFileHandler
|
|||||||
|
|
||||||
private static IArray TransformOpaqueData(IArray array, SubsystemData subsystemData)
|
private static IArray TransformOpaqueData(IArray array, SubsystemData subsystemData)
|
||||||
{
|
{
|
||||||
if (array is MatNumericalArrayOf<uint> uintArray)
|
if (array is MatNumericalArrayOf<uint> uintArray &&
|
||||||
|
uintArray.Data.Length == 6 &&
|
||||||
|
uintArray.Data[0] == 3707764736u)
|
||||||
{
|
{
|
||||||
if (uintArray.Data[0] == 3707764736u)
|
return ObjectParser.ParseObject(uintArray, subsystemData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array is MatCellArray cellArray)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < cellArray.Data.Length; i++)
|
||||||
{
|
{
|
||||||
var (dimensions, indexToObjectId, classIndex) = DataElementReader.ParseOpaqueData(uintArray.Data);
|
var cell = cellArray.Data[i];
|
||||||
return new OpaqueLink(
|
var transformedCell = TransformOpaqueData(cell, subsystemData);
|
||||||
uintArray.Name,
|
cellArray.Data[i] = transformedCell;
|
||||||
string.Empty,
|
}
|
||||||
string.Empty,
|
}
|
||||||
dimensions,
|
|
||||||
uintArray,
|
if (array is MatStructureArray structureArray)
|
||||||
indexToObjectId,
|
{
|
||||||
classIndex,
|
var newFields = new Dictionary<string, List<IArray>>();
|
||||||
subsystemData);
|
foreach (var pair in structureArray.Fields)
|
||||||
|
{
|
||||||
|
var values = pair.Value;
|
||||||
|
var transformedValues = new List<IArray>(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;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct ObjectClassInformation
|
private readonly struct ObjectClassInformation
|
||||||
{
|
{
|
||||||
public ObjectClassInformation(int embeddedObjectPosition, int objectPosition, int loadingOrder, int classId)
|
public ObjectClassInformation(int embeddedObjectPosition, int objectPosition, int loadingOrder, int classId)
|
||||||
{
|
{
|
||||||
@ -392,4 +409,4 @@ namespace MatFileHandler
|
|||||||
=> _keyFromEmbeddedObjectPosition[embeddedObjectPosition];
|
=> _keyFromEmbeddedObjectPosition[embeddedObjectPosition];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@ -87,9 +85,9 @@ namespace MatFileHandler
|
|||||||
.Where(i => VariableNames[i] == variableName)
|
.Where(i => VariableNames[i] == variableName)
|
||||||
.Select(i => (int?)i)
|
.Select(i => (int?)i)
|
||||||
.FirstOrDefault();
|
.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
|
var data = matObject["data"] as ICellArray
|
||||||
@ -98,4 +96,4 @@ namespace MatFileHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -114,4 +112,4 @@ namespace MatFileHandler
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int ElementSize => Type.Size();
|
public int ElementSize => Type.Size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# MatFileHandler
|
# MatFileHandler
|
||||||
|
|
||||||
|
[](https://www.nuget.org/packages/MatFileHandler/absoluteLatest)
|
||||||
|
|
||||||
MatFileHandler is a .NET library for reading and writing MATLAB .mat files
|
MatFileHandler is a .NET library for reading and writing MATLAB .mat files
|
||||||
(of so-called "Level 5"). MatFileHandler supports numerical arrays,
|
(of so-called "Level 5"). MatFileHandler supports numerical arrays,
|
||||||
logical arrays, sparse arrays, char arrays, cell arrays and structure arrays.
|
logical arrays, sparse arrays, char arrays, cell arrays and structure arrays.
|
||||||
@ -13,9 +15,8 @@ You can find (partial) technical description of MATLAB object data format
|
|||||||
This document briefly describes how to perform simple operations with .mat files
|
This document briefly describes how to perform simple operations with .mat files
|
||||||
using MatFileHandler.
|
using MatFileHandler.
|
||||||
|
|
||||||
If you have questions and/or ideas, you can [file a new issue]
|
If you have questions and/or ideas, you can [file a new issue](https://git.mahalex.net/mahalex/MatFileHandler/issues/new)
|
||||||
(https://github.com/mahalex/MatFileHandler/issues/new) or contact me directly at
|
or contact me directly at <mahalex@gmail.com>.
|
||||||
<mahalex@gmail.com>.
|
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
@ -203,7 +204,7 @@ classdef Point
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
We omit any methods (and constructos) such a class might have, because they are
|
We omit any methods (and constructors) such a class might have, because they are
|
||||||
not stored when you save an object of a class into a `.mat` file.
|
not stored when you save an object of a class into a `.mat` file.
|
||||||
|
|
||||||
Imagine that you have a `1x2 Point` object array `p` (an array of two points)
|
Imagine that you have a `1x2 Point` object array `p` (an array of two points)
|
||||||
|
@ -11,9 +11,9 @@ variables:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Use .NET Core SDK 8.x'
|
displayName: 'Use .NET Core SDK 9.x'
|
||||||
inputs:
|
inputs:
|
||||||
version: 8.x
|
version: 9.x
|
||||||
|
|
||||||
- script: dotnet build --configuration $(buildConfiguration)
|
- script: dotnet build --configuration $(buildConfiguration)
|
||||||
displayName: 'dotnet build $(buildConfiguration)'
|
displayName: 'dotnet build $(buildConfiguration)'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user