Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: HebiRobotics/MFL
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: diffplug/MFL
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.
  • 1 commit
  • 153 files changed
  • 1 contributor

Commits on Nov 14, 2018

  1. Release 0.2 (#19)

    * Moved `common.util` to `mat.util`
    * Removed unused utilities
    * Removed multi-release jar dependency due to lack of support for some build tools (e.g. obfuscation)
    * Improved streaming file sink implementation
    * Changed `StreamingDoubleMatrix2D` to be a test example
    * `MatFile` API
    ** Renamed `size()` to `getNumEntries()`
    ** Added `clear()`
    ** Added `getEntries()` for accessing `Iterable<NamedArray>`
    ennerf authored Nov 14, 2018

    Verified

    This commit was signed with the committer’s verified signature.
    chriskrycho Chris Krycho
    Copy the full SHA
    6b7cc26 View commit details
Showing with 14,014 additions and 14,692 deletions.
  1. +1 −0 .gitignore
  2. +21 −0 CHANGELOG.adoc
  3. +4 −5 Jenkinsfile
  4. +12 −12 NOTICE
  5. +162 −16 README.adoc
  6. +0 −11 mat-file-io/CHANGELOG.adoc
  7. +0 −201 mat-file-io/LICENSE
  8. +0 −13 mat-file-io/NOTICE
  9. +0 −177 mat-file-io/README.adoc
  10. +0 −327 mat-file-io/pom.xml
  11. +0 −30 mat-file-io/src/main/java/module-info.java
  12. +0 −68 mat-file-io/src/main/java/us/hebi/matlab/common/memory/NativeAccess.java
  13. +0 −104 mat-file-io/src/main/java/us/hebi/matlab/common/memory/NativeAtomicAccess.java
  14. +0 −142 mat-file-io/src/main/java/us/hebi/matlab/common/memory/NativeMemory.java
  15. +0 −133 mat-file-io/src/main/java/us/hebi/matlab/common/memory/NativeOrderConverter.java
  16. +0 −104 mat-file-io/src/main/java/us/hebi/matlab/common/memory/NativeRegularAccess.java
  17. +279 −28 pom.xml
  18. +60 −60 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/BufferAllocator.java
  19. +223 −224 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/CharEncoding.java
  20. +53 −53 ...src/main/java/us/hebi/matlab/common/util → src/main/java/us/hebi/matlab/mat/format}/Charsets.java
  21. +266 −259 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/Mat5.java
  22. +87 −87 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/Mat5ArrayFlags.java
  23. +305 −306 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/Mat5File.java
  24. +679 −679 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/Mat5Reader.java
  25. +63 −63 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/Mat5Serializable.java
  26. +115 −115 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/Mat5Subsystem.java
  27. +192 −192 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/Mat5Tag.java
  28. +307 −307 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/Mat5TagStreamer.java
  29. +264 −264 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/Mat5Type.java
  30. +166 −167 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/Mat5WriteUtil.java
  31. +243 −240 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/Mat5Writer.java
  32. +71 −71 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/MatCell.java
  33. +91 −91 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/MatChar.java
  34. +71 −71 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/MatFunction.java
  35. +88 −88 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/MatJavaObject.java
  36. +157 −157 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/MatMatrix.java
  37. +55 −55 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/MatObjectStruct.java
  38. +79 −79 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/MatOpaque.java
  39. +214 −214 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/MatSparseCSC.java
  40. +149 −150 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/MatStruct.java
  41. +416 −416 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/McosFileWrapper.java
  42. +84 −84 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/McosObject.java
  43. +205 −205 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/McosReference.java
  44. +42 −42 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/McosRegistry.java
  45. +51 −51 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/NumberStore.java
  46. +220 −228 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/format/UniversalNumberStore.java
  47. +132 −132 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/AbstractArray.java
  48. +69 −69 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/AbstractCell.java
  49. +147 −147 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/AbstractCellBase.java
  50. +102 −102 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/AbstractCharBase.java
  51. +114 −109 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/AbstractMatFile.java
  52. +91 −91 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/AbstractMatFileBase.java
  53. +431 −431 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/AbstractMatrixBase.java
  54. +208 −208 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/AbstractSink.java
  55. +197 −197 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/AbstractSource.java
  56. +72 −72 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/AbstractSparse.java
  57. +114 −114 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/AbstractStruct.java
  58. +204 −204 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/AbstractStructBase.java
  59. +55 −55 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/Array.java
  60. +76 −76 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/Cell.java
  61. +52 −52 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/Char.java
  62. +31 −31 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/FunctionHandle.java
  63. +36 −36 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/JavaObject.java
  64. +111 −105 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/MatFile.java
  65. +93 −93 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/MatlabType.java
  66. +220 −220 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/Matrix.java
  67. +59 −59 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/NamedArray.java
  68. +42 −42 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/ObjectStruct.java
  69. +39 −39 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/Opaque.java
  70. +138 −138 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/Sink.java
  71. +280 −250 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/Sinks.java
  72. +120 −120 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/Source.java
  73. +260 −260 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/Sources.java
  74. +57 −57 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/Sparse.java
  75. +180 −180 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/StringHelper.java
  76. +122 −122 {mat-file-io → }/src/main/java/us/hebi/matlab/mat/types/Struct.java
  77. +104 −104 ...common/memory/CheckArrayBounds.java → src/main/java/us/hebi/matlab/mat/util/ArrayBoundsCheck.java
  78. +53 −53 ...ain/java/us/hebi/matlab/common/memory → src/main/java/us/hebi/matlab/mat/util}/ByteConverter.java
  79. +62 −76 ...in/java/us/hebi/matlab/common/memory → src/main/java/us/hebi/matlab/mat/util}/ByteConverters.java
  80. +91 −91 ...io/src/main/java/us/hebi/matlab/common/memory → src/main/java/us/hebi/matlab/mat/util}/Bytes.java
  81. +146 −146 {mat-file-io/src/main/java/us/hebi/matlab/common → src/main/java/us/hebi/matlab/mat}/util/Casts.java
  82. +152 −152 ...java/us/hebi/matlab/common/memory → src/main/java/us/hebi/matlab/mat/util}/HeapByteConverter.java
  83. +136 −136 ...main/java/us/hebi/matlab/common → src/main/java/us/hebi/matlab/mat}/util/IndentingAppendable.java
  84. +101 −101 ...io/src/main/java/us/hebi/matlab/common → src/main/java/us/hebi/matlab/mat}/util/PlatformInfo.java
  85. +447 −447 ...o/src/main/java/us/hebi/matlab/common → src/main/java/us/hebi/matlab/mat}/util/Preconditions.java
  86. +77 −77 {mat-file-io/src/main/java/us/hebi/matlab/common → src/main/java/us/hebi/matlab/mat}/util/Tasks.java
  87. +147 −0 src/main/java/us/hebi/matlab/mat/util/Unsafe9R.java
  88. +75 −75 ...main/java/us/hebi/matlab/common/memory → src/main/java/us/hebi/matlab/mat/util}/UnsafeAccess.java
  89. +122 −122 ...va/us/hebi/matlab/common/memory → src/main/java/us/hebi/matlab/mat/util}/UnsafeByteConverter.java
  90. +62 −62 {mat-file-io → }/src/test/java/us/hebi/matlab/mat/format/Mat5TagStreamerTest.java
  91. +153 −153 {mat-file-io → }/src/test/java/us/hebi/matlab/mat/format/Mat5WriterTest.java
  92. +293 −293 ...e-io/src/test/java/us/hebi/matlab/mat → src/test/java/us/hebi/matlab/mat/tests}/Mat5Examples.java
  93. +407 −407 ...hebi/matlab/mat/format/mat5test → src/test/java/us/hebi/matlab/mat/tests/mat5}/ArrayReadTest.java
  94. +66 −66 ...us/hebi/matlab/mat/format/mat5test → src/test/java/us/hebi/matlab/mat/tests/mat5}/FigureTest.java
  95. +183 −183 ...s/hebi/matlab/mat/format/mat5test → src/test/java/us/hebi/matlab/mat/tests/mat5}/MatTestUtil.java
  96. +287 −287 ...a/us/hebi/matlab/mat/format/mat5test → src/test/java/us/hebi/matlab/mat/tests/mat5}/McosTest.java
  97. +101 −101 .../hebi/matlab/mat/format/mat5test → src/test/java/us/hebi/matlab/mat/tests/mat5}/SimulinkTest.java
  98. +50 −50 ...hebi/matlab/mat/format/mat5test → src/test/java/us/hebi/matlab/mat/tests/mat5}/SubsystemTest.java
  99. +81 −81 ...rmat/experimental → src/test/java/us/hebi/matlab/mat/tests/serialization}/EjmlDMatrixWrapper.java
  100. +98 −98 ...t/experimental → src/test/java/us/hebi/matlab/mat/tests/serialization}/EjmlSerializationTest.java
  101. +123 −123 ...ormat/experimental → src/test/java/us/hebi/matlab/mat/tests/serialization}/EjmlSparseWrapper.java
  102. +167 −167 ...experimental → src/test/java/us/hebi/matlab/mat/tests/serialization}/StreamingDoubleMatrix2D.java
  103. +137 −93 ...rimental → src/test/java/us/hebi/matlab/mat/tests/serialization}/StreamingDoubleMatrix2DTest.java
  104. +156 −156 {mat-file-io → }/src/test/java/us/hebi/matlab/mat/types/SinkTest.java
  105. +113 −113 {mat-file-io → }/src/test/java/us/hebi/matlab/mat/types/SourceTest.java
  106. +294 −295 ...java/us/hebi/matlab/common/memory → src/test/java/us/hebi/matlab/mat/util}/ByteConverterTest.java
  107. +43 −43 ...rc/test/java/us/hebi/matlab/common/memory → src/test/java/us/hebi/matlab/mat/util}/BytesTest.java
  108. +80 −81 ...lab/common/memory/NativeMemoryTest.java → src/test/java/us/hebi/matlab/mat/util/Unsafe9RTest.java
  109. +6 −6 ...t/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/MatlabClasses/HandleClass.m
  110. +6 −6 ...at/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/MatlabClasses/OtherClass.m
  111. 0 .../mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/MatlabClasses/README.txt
  112. +10 −10 ...t/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/MatlabClasses/SimpleEmpty.m
  113. +12 −12 ...mat/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/MatlabClasses/SimpleSingleText.m
  114. +18 −18 ...rmat/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/MatlabClasses/TestHandleClass.m
  115. BIN ...tlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/bigsparse.mat
  116. BIN ...bi/matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/cell.mat
  117. BIN .../mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/complexSparse.mat
  118. BIN .../format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/denseRowColSparse.mat
  119. BIN ...tlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/emptyname.mat
  120. BIN ...i/matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/int32.mat
  121. BIN ...i/matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/int64.mat
  122. BIN ...bi/matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/int8.mat
  123. BIN ...bi/matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/java.mat
  124. BIN ...matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/logical.mat
  125. BIN ...at/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/matnativedouble.mat
  126. BIN ...t/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/matnativedouble2.mat
  127. BIN ...mat/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/multiDimComplexMatrix.mat
  128. BIN ...mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/multiDimMatrix.mat
  129. BIN ...ebi/matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/nan.mat
  130. BIN .../matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/object.mat
  131. BIN ...b/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/simplestruct.mat
  132. BIN .../matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/single.mat
  133. BIN .../matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/sparse.mat
  134. BIN .../matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/uint32.mat
  135. BIN .../matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/uint64.mat
  136. BIN ...i/matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/uint8.mat
  137. +8 −8 .../hebi/matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/utf.m
  138. BIN ...ebi/matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/arrays/utf.mat
  139. BIN ...mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/figures/plot-advanced.fig
  140. BIN ...b/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/figures/plot-simple.fig
  141. BIN ...b/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/figures/plot-simple.mat
  142. BIN ...i/matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/mcos/handles.mat
  143. BIN ...ormat/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/mcos/handlesingle_multiple.mat
  144. BIN ...tlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/mcos/simpleempty.mat
  145. BIN ...format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/mcos/simpleempty_multiple.mat
  146. BIN ...t/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/mcos/simplesingletext_multiple.mat
  147. BIN ...5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/mcos/simplesingletext_multiplearray.mat
  148. BIN ...mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/mcos/simplesingletext_unmodified.mat
  149. BIN ...bi/matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/mcos/string.mat
  150. BIN ...ebi/matlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/mcos/table.mat
  151. BIN ...atlab/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/mcos/timeseries.mat
  152. BIN ...format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/simulink/simulink_tet_out.mat
  153. BIN ...b/mat/format/mat5test → src/test/resources/us/hebi/matlab/mat/tests/mat5}/simulink/statespace.mat
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -2,3 +2,4 @@
*.iml
/.idea
settings.xml
target/
21 changes: 21 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
= Mat-File IO - Changelog

== 0.2
* Moved `common.util` to `mat.util`
* Removed unused utilities
* Removed multi-release jar dependency due to lack of support for some build tools (e.g. obfuscation)
* Improved streaming file sink implementation
* Changed `StreamingDoubleMatrix2D` to be a test example
* `MatFile` API
** Renamed `size()` to `getNumEntries()`
** Added `clear()`
** Added `getEntries()` for accessing `Iterable<NamedArray>`

== 0.1.2
* fixed binary incompatibilities with Java 6 that were introduced by compiling with JDK 9

== 0.1.1
* added license headers to all files

== 0.1
* initial release
9 changes: 4 additions & 5 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ pipeline {
jdk 'jdk8'
}
steps {
bat 'mvn clean package -PnoJava9'
bat 'mvn clean verify'
}
post {
always {
@@ -51,7 +51,7 @@ pipeline {
jdk 'jdk8'
}
steps {
sh 'mvn clean package -PnoJava9'
sh 'mvn clean verify'
}
post {
always {
@@ -71,15 +71,14 @@ pipeline {
jdk 'jdk8'
}
steps {
sh 'mvn clean package -PnoJava9'
sh 'mvn clean verify'
}
post {
always {
cleanWs()
}
}
}
/*
stage('Windows Java 9') {
agent {
node {
@@ -138,7 +137,7 @@ pipeline {
cleanWs()
}
}
}*/
}

}
}
24 changes: 12 additions & 12 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
MatlabTools - A collection of Java libraries for interfacing with MATLAB
Copyright (C) 2018 HEBI Robotics.
This library is free software; you can redistribute it and/or
modify it under the terms of the Apache 2.0 License as published
by the Apache Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Mat-File IO - a Java library for reading and writing MAT Files

Copyright (C) 2018 HEBI Robotics.

This library is free software; you can redistribute it and/or
modify it under the terms of the Apache 2.0 License as published
by the Apache Software Foundation.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

If you have any questions please contact us via https://github.com/HebiRobotics/
178 changes: 162 additions & 16 deletions README.adoc
Original file line number Diff line number Diff line change
@@ -1,39 +1,185 @@
= MatlabTools
= Mat-File IO

The `MatlabTools` repository is a collection of MATLAB related Java libraries maintained by link:http://www.hebirobotics.com/[HEBI Robotics].
== Introduction

Migrating our internal libraries to open source is an ongoing effort, so we will be adding more over time. Depending on the interest, selected projects may be moved into dedicated repositories in the future.
Mat-File IO is a Java library for reading and writing MAT Files that are compatible with MATLAB's MAT-File Format.

== Modules
It's overall design goals are; 1) to support working with large amounts of data on heap constrained and allocation limited environments, 2) to allow users to serialize custom data classes without having to convert to temporary objects, 3) to provide a user-friendly API that adheres to MATLAB's semantic behavior.

This library currently supports the https://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf[Level 5 MAT-File Format] which has been the default for `.mat` and `.fig` files since https://en.wikipedia.org/wiki/MATLAB#Release_history[MATLAB 5.0 (R8)] in 1996. This includes `save` flags `-v6` and `-v7`, but not `-v4` or `-v7.3`. See MAT-File https://de.mathworks.com/help/matlab/import_export/mat-file-versions.html[Versions] for more info.

This library is free, written in 100% Java, and has been released under an Apache v2.0 license. It works with Java 6 and higher, including Java 9 and 10.

=== Mat-File IO
== Maven Central

link:./mat-file-io[Mat-File IO] is a Java library for reading and writing MAT Files that are compatible with MATLAB's MAT-File Format.
Mat-File IO is in the Maven central repository and can easily be added to Maven, Gradle, and similar project managers.

```XML
<dependency>
<groupId>us.hebi.matlab</groupId>
<artifactId>mat-file-io</artifactId>
<version>0.1.2</version>
<version>0.2</version>
</dependency>
```

It's overall design goals are; 1) to support working with large amounts of data on heap constrained and allocation limited environments, 2) to allow users to serialize custom data classes without having to convert to temporary objects, 3) to provide a user-friendly API that adheres to MATLAB's semantic behavior.
[NOTE]
====
The initial release was done as version 0.x because we are still doing final integration tests. However, it is likely that there won't be any more breaking changes, so 1.0 is expected to be fully compatible and should be released in the near future.
====

This library is free, written in 100% Java, and has been released under an Apache v2.0 license. It works with Java 6 and higher, including Java 9 and 10.
== Basic Usage

For more information and usage examples, please refer to the link:./mat-file-io[documentation].
The **link:./src/main/java/us/hebi/matlab/mat/format/Mat5.java[Mat5]** class contains static factory methods that serve as the starting points for the public API. The basic types (e.g. `Struct`, `Cell`, `Sparse`, `Char`) follow their corresponding MATLAB semantics (e.g. a struct can't be logical) and provide a fluent API for the most common use cases. All of the numerical types (e.g. `double`, `uint8`, `int16`, `logical`, ...) are represented by the `Matrix` type which offers a unified interface for handling numeric conversions.

== Building Sources
Similarly to MATLAB, all types are internally represented as a multi-dimensional `Array`. For example, a scalar struct is really a `1x1` array of structs. Most types offer convenience overloads for scalar, linear, 2-dimensional, and N-dimensional access.

Below are some example snippets on how to use the API. For more examples, please refer to **link:./src/test/java/us/hebi/matlab/mat/tests/Mat5Examples.java[Mat5Examples]** and the various unit tests.

=== Creating and Writing MAT Files

A `MatFile` is a data structure that contains a collection of named `Array` variables.

```Java
// Create MAT file with a scalar in a nested struct
MatFile matFile = Mat5.newMatFile()
.addArray("var1", Mat5.newString("Test"))
.addArray("var2", Mat5.newScalar(7))
.addArray("var3", Mat5.newStruct().set("x", Mat5.newScalar(42)))
```

The created `MatFile` can be written to a data `Sink`. Sinks for various outputs (e.g. buffer, stream, or file) can be created via the `Sinks` factory or by extending `AbstractSink`.

```Java
// Serialize to streaming file using default configuration
try(Sink sink = Sinks.newStreamingFile("data.mat")){
matFile.writeTo(sink);
}
```

The `MatFile` can also be written to disk using a memory-mapped file. We can initialize the file with the maximum expected size, and then automatically truncate it once the `Sink` is closed.

```Java
// Serialize to memory-mapped file w/ custom deflate level
int safetySize = 256; // some (small) arrays may become larger when compressed
long maxExpectedSize = matFile.getUncompressedSerializedSize() + safetySize;
try(Sink sink = Sinks.newMappedFile("data.mat", (int) maxExpectedSize)){
Mat5.newWriter(sink)
.setDeflateLevel(Deflater.BEST_SPEED)
.writeMat(matFile);
}
```

=== Reading MAT Files

A `MatFile` can be read from a `Source`. Similar to `Sink`, a `Source` can represent various types of data inputs. The read API is setup such that users can navigate through known file structures without requiring casts or temporary variables.

```Java
// Read scalar from nested struct
try(Source source = Sources.openFile("data.mat"){
double value = Mat5.newReader(source).readFile()
.getStruct("var3")
.getMatrix("x")
.getDouble(0);
}
```

=== Advanced Filtering

In cases where users are only interested in reading a subset of `Array` variables, unwanted entries can be ignored by specifying an `ArrayFilter`.

```Java
// Filter arrays that follow some criteria based on the name, type, dimension, or global/logical flags
try(Source source = Sources.openFile("data.mat"){
MatFile mat = Mat5.newReader(source)
.setArrayFilter(header -> header.isGlobal())
.readFile();
}
```

The filter gets applied only at the root level, so arrays inside a struct or cell array won't be filtered separately.

=== Concurrent Compression

The created sources include Java 9's `module-info.java`, but are otherwise backwards compatible with Java 6. The contained unit tests may use Java 8 syntax, so the project needs to be compiled with at least JDK 8.
Almost all of the CPU time spent on reading or writing MAT files is related to compression. Fortunately, root entries are compressed independently from one another, so it's possible to do the work multi-threaded.

Building with JDK 9 and above
Users can enable concurrent reading by passing an executor service into the reader. In order to activate, the `Source` must also support sub-views (slices) on the underlying data (i.e. byte buffers or memory mapped files).

```Java
// Concurrent Decompression
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
try(Source source = Sources.openFile("data.mat"){
MatFile mat = Mat5.newReader(source)
.enableConcurrentDecompression(executor)
.readFile()
} finally {
executor.shutdown();
}
```

Concurrent writing unfortunately requires a temporary buffer for each root variable due to the size not being known ahead of time. The buffer allocation can be customized in case users want to use buffer-pools or memory-mapped buffers.

```Java
// Concurrent Compression
try(Sink sink = Sinks.newStreamingFile("data.mat")){
Mat5.newWriter(sink)
.enableConcurrentCompression(executorService)
.setDeflateLevel(Deflater.BEST_SPEED)
.writeMat(mat);
}
```

The table below shows a rough performance comparison of working with one of our production data logs.

[width="100%",options="header",cols="a,a,a,a,a"]
|====================
| Compression | Size | Threads | Write Time | Read Time
| BEST_COMPRESSION | 144 MB | 1 | 280 sec | 3.5 sec
| BEST_COMPRESSION | 144 MB | 8 | 47 sec | 0.8 sec
| BEST_SPEED | 156 MB | 1 | 7.2 sec | 3.6 sec
| BEST_SPEED | 156 MB | 8 | 1.5 sec | 0.8 sec
| NO_COMPRESSION | 422 MB | 1 | 0.07 sec | 0.2 sec
|====================

The data set was very multi-threading friendly (33x [95946x18] double matrices on the root level) and first loaded into memory to avoid disk access bottlenecks. The tests were done on a quad core with hyper-threading (Intel NUC6i7kyk).

=== Serializing Custom Classes

We often encountered cases where we needed to serialize data from an existing math library. Rather than having to convert the data into an API class, we added the ability to create light-weight wrapper classes that serialize the desired data directly.

In order for a class to be serializable, it needs to implement the `Array` interface (easiest way is to extend `AbstractArray`) as well as the `Mat5Serializable` interface. Below are examples:

* link:./src/test/java/us/hebi/matlab/mat/tests/serialization/EjmlDMatrixWrapper.java[EjmlDMatrixWrapper] serializes link:http://ejml.org[EJML]'s `DMatrix` type

* link:./src/test/java/us/hebi/matlab/mat/tests/serialization/EjmlSparseWrapper.java[EjmlSparseWrapper] serializes link:http://ejml.org[EJML]'s `DMatrixSparseCSC` sparse matrix

* link:./src/test/java/us/hebi/matlab/mat/tests/serialization/StreamingDoubleMatrix2D.java[StreamingDoubleMatrix2D] streams incoming row-major data into temporary files and combines them on serialization

== General Notes

=== Memory Efficient Serialization

The MAT 5 format stores all data fields with a header tag that contains the number of bytes and how they should be interpreted. Rather than writing into temporary buffers to determine the serialized size, we added ways to pre-compute all deterministic sizes beforehand.

The only non-deterministic case is compressing data at the root level, which we can work around by writing a dummy size and overwriting it once the final size is known. Thus, enabling compression requires the root level sink to support position seeking (i.e. in-memory buffers, memory mapped files, or random access files).

=== Support for Undocumented Features

Unfortunately, MAT 5 files have several features that aren't covered in the official documentation. This includes most of the recently added types (`table`, `timeseries`, `string`, ...), `handle` classes, `function handles`, `.fig` files, `Simulink` outputs, etc.

Our current implementation supports reading all of the `.mat` and `.fig` files we were able to generate. It also supports editing and saving of the loaded MAT files, e.g., adding entries, changing matrices, or using a different compression level. However, changes to the undocumented parts, such as setting a property on a `handle` class, will not be saved.

== Building Sources

The created sources include unit tests that make use of Java 7 and 8 syntax, so the project needs to be compiled with at least JDK 8.

mvn package

Building with JDK 8
For more information, please check the CI build-script link:Jenkinsfile[]

== Acknowledgements

https://github.com/diffplug/matfilerw[MatFileRW] (active fork of https://github.com/gradusnikov/jmatio[JMatIO] maintained by link:http://diffplug.com/[DiffPlug]) served as an inspiration for parts of the implementation as well as a source for test data. We ended up porting and supporting all of their unit tests with the exception of `Base64 MDL` decoding (which we couldn't figure out the use case for).

mvn package -PnoJava9
The implementation for reading the undocumented `MCOS` (MATLAB Class Object System) data is based on https://github.com/mbauman[Matt Bauman]'s http://nbviewer.jupyter.org/gist/mbauman/9121961[reverse engineering efforts] as well as MatFileRW's implementation by https://github.com/MJDSys[Matthew Dawson].

For more information, please check the CI build-script link:Jenkinsfile[]
`Preconditions` was copied from link:https://github.com/google/guava[Guava].
11 changes: 0 additions & 11 deletions mat-file-io/CHANGELOG.adoc

This file was deleted.

201 changes: 0 additions & 201 deletions mat-file-io/LICENSE

This file was deleted.

13 changes: 0 additions & 13 deletions mat-file-io/NOTICE

This file was deleted.

177 changes: 0 additions & 177 deletions mat-file-io/README.adoc

This file was deleted.

327 changes: 0 additions & 327 deletions mat-file-io/pom.xml

This file was deleted.

30 changes: 0 additions & 30 deletions mat-file-io/src/main/java/module-info.java

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

307 changes: 279 additions & 28 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,28 +1,279 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>us.hebi.matlab</groupId>
<artifactId>matlab-tools</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>pom</packaging>

<name>MatlabTools</name>
<description>
Collection of Java libraries for interfacing with MATLAB
</description>

<!-- Sub-Projects -->
<modules>
<module>mat-file-io</module>
</modules>

<properties>

<maven.compiler.source>6</maven.compiler.source>
<maven.compiler.target>6</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

</properties>

</project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>us.hebi.matlab</groupId>
<artifactId>mat-file-io</artifactId>
<version>0.2.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Mat-File IO</name>
<description>
Java library for reading and writing MAT Files that are compatible with MATLAB's MAT-File Format
</description>
<url>https://github.com/HebiRobotics/MatFileIO</url>

<organization>
<name>HEBI Robotics</name>
<url>http://www.hebirobotics.com</url>
</organization>
<inceptionYear>2018</inceptionYear>

<!-- Maven Central configuration copied from https://github.com/davidcarboni/releaser -->
<!-- Required: license information: -->
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
<comments>A business-friendly OSS license</comments>
</license>
</licenses>

<!-- Required: source control information: -->
<scm>
<url>https://github.com/HebiRobotics/MatFileIO</url>
<connection>scm:git:git://github.com/HebiRobotics/MatFileIO.git</connection>
<developerConnection>scm:git:git@github.com:HebiRobotics/MatFileIO.git</developerConnection>
<tag>HEAD</tag>
</scm>

<!-- Maven Central Distribution -->
<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/
</url>
</repository>
</distributionManagement>

<!-- Required: developer information: -->
<developers>
<developer>
<id>ennerf</id>
<name>Florian Enner</name>
<email>florian@hebirobotics.com</email>
<url>https://github.com/ennerf</url>
<organization>HEBI Robotics</organization>
<organizationUrl>https://github.com/HebiRobotics</organizationUrl>
</developer>
</developers>

<properties>

<!-- Set Syntax level to Java 8 so that IDEs (IntelliJ) don't complain about using
Java 8 syntax (e.g. try-with-resources) in unit tests -->
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>

<!-- Set the actual target to minimum Java version compatible with Matlab >2009a -->
<matlab.target>1.6</matlab.target>

<!-- Other Settings -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<gpg.keyname /> <!-- set via settings.xml to sign release artifacts-->

<!-- License Headers (http://www.mojohaus.org/license-maven-plugin/update-file-header-mojo.html) -->
<license.licenseName>apache_v2</license.licenseName>
<license.addJavaLicenseAfterPackage>false</license.addJavaLicenseAfterPackage>
<license.trimHeaderLine>true</license.trimHeaderLine>

<!-- For now, disable 'missing' warnings about undocumented @param and @throws -->
<doclint>html,syntax,accessibility,reference</doclint>

</properties>

<!-- Dependency management (keep in alphabetical order) -->
<dependencies>

<dependency> <!-- for custom serialization tests -->
<groupId>org.ejml</groupId>
<artifactId>ejml-all</artifactId>
<version>0.35</version>
<scope>test</scope>
</dependency>

<dependency> <!-- Unit Tests -->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

</dependencies>

<!-- Build Process -->
<build>

<!-- Include the README, NOTICE and LICENSE files: -->
<resources>
<resource>
<directory>${project.basedir}</directory>
<includes>
<include>README*</include>
<include>NOTICE*</include>
<include>LICENSE*</include>
<include>CHANGELOG*</include>
</includes>
</resource>
</resources>

<plugins>

<!-- Java 9 and later: compile in 2 stages so we can get a module-info w/ target sources -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<executions>
<!-- 1) default-compile: compiles sources w/ Java 8 -->
<!-- 2) default-testCompile: compiles tests w/ Java 8 -->
<!-- 3) Re-compiles sources for target VM -->
<execution>
<id>target-compile</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<source>${matlab.target}</source>
<target>${matlab.target}</target>
</configuration>
</execution>
</executions>
</plugin>

<!-- Add licenses to files -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>1.16</version>
<executions>
<execution>
<id>first</id>
<goals>
<goal>update-file-header</goal>
</goals>
<phase>process-sources</phase>
</execution>
</executions>
</plugin>

<!-- Optional: ensure the manifest contains artifact version information: -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
<manifestEntries>
<Automatic-Module-Name>us.hebi.matlab.mat</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>

<!-- Required: Source Jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>

<!-- Required: Javadoc Jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-javadoc</id>
<phase>verify</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>

<!-- Release -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<localCheckout>true</localCheckout>
<pushChanges>false</pushChanges>
<mavenExecutorId>forked-path</mavenExecutorId>
</configuration>
</plugin>

<!-- Deployment -->
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.7</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>

</plugins>
</build>

<profiles>

<!-- GPG Signature on release -->
<profile>
<id>release-sign-artifacts</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
<configuration>
<keyname>${gpg.keyname}</keyname>
<passphraseServerId>${gpg.keyname}</passphraseServerId>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>

</profiles>

</project>
Original file line number Diff line number Diff line change
@@ -1,60 +1,60 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import java.nio.ByteBuffer;

/**
* Interface for implementing custom memory management. Useful for working
* with buffer pools or memory mapped files.
*
* @author Florian Enner
* @since 28 Oct 2018
*/
public interface BufferAllocator {

/**
* Creates a buffer with the following properties:
* <p>
* - position() equal to 0
* - limit() equal to numBytes
* - capacity() larger or equal to numBytes
* - All remaining bytes must be zero
* <p>
* This method must be thread-safe.
*
* @param numBytes number of bytes
* @return zeroed buffer at position zero with numBytes remaining
*/
ByteBuffer allocate(int numBytes);

/**
* Releases the buffer. This method gets called when the caller
* gives up rights to use the buffer. The buffer itself may be
* used again as part of a buffer pool.
* <p>
* This method must be thread-safe.
*
* @param buffer buffer
*/
void release(ByteBuffer buffer);

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import java.nio.ByteBuffer;

/**
* Interface for implementing custom memory management. Useful for working
* with buffer pools or memory mapped files.
*
* @author Florian Enner
* @since 28 Oct 2018
*/
public interface BufferAllocator {

/**
* Creates a buffer with the following properties:
* <p>
* - position() equal to 0
* - limit() equal to numBytes
* - capacity() larger or equal to numBytes
* - All remaining bytes must be zero
* <p>
* This method must be thread-safe.
*
* @param numBytes number of bytes
* @return zeroed buffer at position zero with numBytes remaining
*/
ByteBuffer allocate(int numBytes);

/**
* Releases the buffer. This method gets called when the caller
* gives up rights to use the buffer. The buffer itself may be
* used again as part of a buffer pool.
* <p>
* This method must be thread-safe.
*
* @param buffer buffer
*/
void release(ByteBuffer buffer);

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,53 +1,53 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.common.util;

import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;

/**
* @author Florian Enner
* @since 08 Sep 2018
*/
public class Charsets {

private Charsets() {
}

// Charsets that are included in Java platform specification
// See https://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html
public static final Charset US_ASCII = Charset.forName("US-ASCII");
public static final Charset UTF_8 = Charset.forName("UTF-8");
public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
public static final Charset UTF_16LE = Charset.forName("UTF-16LE");

// Optional charsets that may or may not be supported
public static final Charset UTF_32BE = forNameOrNull("UTF-32BE");
public static final Charset UTF_32LE = forNameOrNull("UTF-32LE");

private static Charset forNameOrNull(String name) {
try {
return Charset.forName(name);
} catch (UnsupportedCharsetException uce) {
return null;
}
}
}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;

/**
* @author Florian Enner
* @since 08 Sep 2018
*/
class Charsets {

private Charsets() {
}

// Charsets that are included in Java platform specification
// See https://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html
public static final Charset US_ASCII = Charset.forName("US-ASCII");
public static final Charset UTF_8 = Charset.forName("UTF-8");
public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
public static final Charset UTF_16LE = Charset.forName("UTF-16LE");

// Optional charsets that may or may not be supported
public static final Charset UTF_32BE = forNameOrNull("UTF-32BE");
public static final Charset UTF_32LE = forNameOrNull("UTF-32LE");

private static Charset forNameOrNull(String name) {
try {
return Charset.forName(name);
} catch (UnsupportedCharsetException uce) {
return null;
}
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,87 +1,87 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.format.Mat5Serializable.Mat5Attributes;
import us.hebi.matlab.mat.types.*;

/**
* Utilities to deal with the int[2] array flags that are
* at the beginning of each array.
*
* @author Florian Enner
* @since 14 Sep 2018
*/
class Mat5ArrayFlags {

static int[] forArray(Array array) {
if (array instanceof Mat5Attributes) {
Mat5Attributes attr = ((Mat5Attributes) array);
return create(array.getType(), attr.isGlobal(), attr.isLogical(), attr.isComplex(), attr.getNzMax());
}
boolean logical = array instanceof Matrix && ((Matrix) array).isLogical();
boolean complex = array instanceof Matrix && ((Matrix) array).isComplex();
int nzMax = array instanceof Sparse ? ((Sparse) array).getNzMax() : 0;
return Mat5ArrayFlags.create(array.getType(), array.isGlobal(), logical, complex, nzMax);
}

/**
* Opaques may show up as a different public type, e.g., Object. This method ignores
* the public type and forces the array type to be Opaque.
*/
static int[] forOpaque(Opaque opaque) {
return create(MatlabType.Opaque, opaque.isGlobal(), false, false, 0);
}

private static int[] create(MatlabType type, boolean global, boolean logical, boolean complex, int nzMax) {
int attributes = type.id() & FLAG_MASK_TYPE_ID;
if (logical) attributes |= FLAG_BIT_LOGICAL;
if (global) attributes |= FLAG_BIT_GLOBAL;
if (complex) attributes |= FLAG_BIT_COMPLEX;
return new int[]{attributes, nzMax};
}

static MatlabType getType(int[] arrayFlags) {
return MatlabType.fromId(arrayFlags[0] & FLAG_MASK_TYPE_ID);
}

static boolean isComplex(int[] arrayFlags) {
return (arrayFlags[0] & FLAG_BIT_COMPLEX) != 0;
}

static boolean isGlobal(int[] arrayFlags) {
return (arrayFlags[0] & FLAG_BIT_GLOBAL) != 0;
}

static boolean isLogical(int[] arrayFlags) {
return (arrayFlags[0] & FLAG_BIT_LOGICAL) != 0;
}

static int getNzMax(int[] arrayFlags) {
return arrayFlags[1];
}

private static final int FLAG_MASK_TYPE_ID = 0xff;
private static final int FLAG_BIT_LOGICAL = 1 << 9;
private static final int FLAG_BIT_GLOBAL = 1 << 10;
private static final int FLAG_BIT_COMPLEX = 1 << 11;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.format.Mat5Serializable.Mat5Attributes;
import us.hebi.matlab.mat.types.*;

/**
* Utilities to deal with the int[2] array flags that are
* at the beginning of each array.
*
* @author Florian Enner
* @since 14 Sep 2018
*/
class Mat5ArrayFlags {

static int[] forArray(Array array) {
if (array instanceof Mat5Attributes) {
Mat5Attributes attr = ((Mat5Attributes) array);
return create(array.getType(), attr.isGlobal(), attr.isLogical(), attr.isComplex(), attr.getNzMax());
}
boolean logical = array instanceof Matrix && ((Matrix) array).isLogical();
boolean complex = array instanceof Matrix && ((Matrix) array).isComplex();
int nzMax = array instanceof Sparse ? ((Sparse) array).getNzMax() : 0;
return Mat5ArrayFlags.create(array.getType(), array.isGlobal(), logical, complex, nzMax);
}

/**
* Opaques may show up as a different public type, e.g., Object. This method ignores
* the public type and forces the array type to be Opaque.
*/
static int[] forOpaque(Opaque opaque) {
return create(MatlabType.Opaque, opaque.isGlobal(), false, false, 0);
}

private static int[] create(MatlabType type, boolean global, boolean logical, boolean complex, int nzMax) {
int attributes = type.id() & FLAG_MASK_TYPE_ID;
if (logical) attributes |= FLAG_BIT_LOGICAL;
if (global) attributes |= FLAG_BIT_GLOBAL;
if (complex) attributes |= FLAG_BIT_COMPLEX;
return new int[]{attributes, nzMax};
}

static MatlabType getType(int[] arrayFlags) {
return MatlabType.fromId(arrayFlags[0] & FLAG_MASK_TYPE_ID);
}

static boolean isComplex(int[] arrayFlags) {
return (arrayFlags[0] & FLAG_BIT_COMPLEX) != 0;
}

static boolean isGlobal(int[] arrayFlags) {
return (arrayFlags[0] & FLAG_BIT_GLOBAL) != 0;
}

static boolean isLogical(int[] arrayFlags) {
return (arrayFlags[0] & FLAG_BIT_LOGICAL) != 0;
}

static int getNzMax(int[] arrayFlags) {
return arrayFlags[1];
}

private static final int FLAG_MASK_TYPE_ID = 0xff;
private static final int FLAG_BIT_LOGICAL = 1 << 9;
private static final int FLAG_BIT_GLOBAL = 1 << 10;
private static final int FLAG_BIT_COMPLEX = 1 << 11;

}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,63 +1,63 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.Sink;

import java.io.IOException;

/**
* Marker interface for all parts (e.g. array/header/data components) that can be serialized using
* the MAT5 format. Arrays that do not implement this interface may be rejected by the Mat5Writer.
* <p>
* The serialized bytes include any necessary tags (e.g. matrix tag) and padding
*
* @author Florian Enner
* @since 29 Aug 2018
*/
public interface Mat5Serializable {

/**
* @param name name
* @return Number of serialized bytes including the Matrix tag
*/
int getMat5Size(String name);

void writeMat5(String name, Sink sink) throws IOException;

/**
* Interface for serializing custom array classes that need to
* have control over array flags/attributes without implementing
* the entire interface (e.g. sparse::getNzMax() or matrix::isComplex())
*/
interface Mat5Attributes {

boolean isLogical();

boolean isGlobal();

boolean isComplex();

int getNzMax();

}

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.Sink;

import java.io.IOException;

/**
* Marker interface for all parts (e.g. array/header/data components) that can be serialized using
* the MAT5 format. Arrays that do not implement this interface may be rejected by the Mat5Writer.
* <p>
* The serialized bytes include any necessary tags (e.g. matrix tag) and padding
*
* @author Florian Enner
* @since 29 Aug 2018
*/
public interface Mat5Serializable {

/**
* @param name name
* @return Number of serialized bytes including the Matrix tag
*/
int getMat5Size(String name);

void writeMat5(String name, Sink sink) throws IOException;

/**
* Interface for serializing custom array classes that need to
* have control over array flags/attributes without implementing
* the entire interface (e.g. sparse::getNzMax() or matrix::isComplex())
*/
interface Mat5Attributes {

boolean isLogical();

boolean isGlobal();

boolean isComplex();

int getNzMax();

}

}
Original file line number Diff line number Diff line change
@@ -1,115 +1,115 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.AbstractArray;
import us.hebi.matlab.mat.types.MatlabType;
import us.hebi.matlab.mat.types.Sink;
import us.hebi.matlab.mat.types.Sources;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;

import static us.hebi.matlab.mat.format.Mat5Type.*;
import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* The subsystem contains various types of class information. It gets stored
* as an int8 array at the end of a file and internally contains a separate
* MatFile with a slightly different format.
*
* @author Florian Enner
* @since 04 Sep 2018
*/
public final class Mat5Subsystem extends AbstractArray implements Mat5Serializable {

Mat5Subsystem(int[] dims, boolean isGlobal, ByteBuffer buffer, BufferAllocator bufferAllocator) {
super(dims, isGlobal);
this.buffer = buffer;
this.bufferAllocator = bufferAllocator;
}

@Override
public MatlabType getType() {
return MatlabType.UInt8;
}

public ByteBuffer getBuffer() {
return buffer.slice();
}

@Override
public int getMat5Size(String name) {
return Mat5.MATRIX_TAG_SIZE
+ computeArrayHeaderSize(name, this)
+ UInt8.computeSerializedSize(getNumElements());
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
writeMatrixTag(name, this, sink);
writeArrayHeader(name, this, sink);
UInt8.writeByteBufferWithTag(buffer.slice(), sink);
}

void processReferences(McosRegistry mcosRegistry) throws IOException {
if (mcosRegistry.getReferences().isEmpty())
return;

// Read the mat file that is contained within the byte buffer
subFile = Mat5.newReader(Sources.wrap(buffer.slice()))
.setMcosRegistry(mcosRegistry)
.setBufferAllocator(bufferAllocator)
.disableSubsystemProcessing() // the Subsystem's subsystem does not contain useful data
.setReducedHeader(true)
.readMat();

// The first entry in the top level subsystem (end of root file) contains the
// 'FileWrapper__' object which contains the data backing the various reference
// classes, e.g., handles. Note that more than one references can reference the
// same data, and that the referenced objects may themselves be references.
McosFileWrapper fileWrapper = subFile.getStruct(0).get("MCOS");
List<McosObject> objects = fileWrapper.parseObjects(mcosRegistry);

// Update references of all handle classes
objects.add(0, null); // bump count to match off-by-one index
for (McosReference reference : mcosRegistry.getReferences()) {
reference.setReferences(objects);
}

}

@Override
public void close() throws IOException {
if (subFile != null)
subFile.close();
bufferAllocator.release(buffer);
buffer = null;
bufferAllocator = null;
subFile = null;
}

private ByteBuffer buffer;
private BufferAllocator bufferAllocator;
private Mat5File subFile;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.AbstractArray;
import us.hebi.matlab.mat.types.MatlabType;
import us.hebi.matlab.mat.types.Sink;
import us.hebi.matlab.mat.types.Sources;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;

import static us.hebi.matlab.mat.format.Mat5Type.*;
import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* The subsystem contains various types of class information. It gets stored
* as an int8 array at the end of a file and internally contains a separate
* MatFile with a slightly different format.
*
* @author Florian Enner
* @since 04 Sep 2018
*/
public final class Mat5Subsystem extends AbstractArray implements Mat5Serializable {

Mat5Subsystem(int[] dims, boolean isGlobal, ByteBuffer buffer, BufferAllocator bufferAllocator) {
super(dims, isGlobal);
this.buffer = buffer;
this.bufferAllocator = bufferAllocator;
}

@Override
public MatlabType getType() {
return MatlabType.UInt8;
}

public ByteBuffer getBuffer() {
return buffer.slice();
}

@Override
public int getMat5Size(String name) {
return Mat5.MATRIX_TAG_SIZE
+ computeArrayHeaderSize(name, this)
+ UInt8.computeSerializedSize(getNumElements());
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
writeMatrixTag(name, this, sink);
writeArrayHeader(name, this, sink);
UInt8.writeByteBufferWithTag(buffer.slice(), sink);
}

void processReferences(McosRegistry mcosRegistry) throws IOException {
if (mcosRegistry.getReferences().isEmpty())
return;

// Read the mat file that is contained within the byte buffer
subFile = Mat5.newReader(Sources.wrap(buffer.slice()))
.setMcosRegistry(mcosRegistry)
.setBufferAllocator(bufferAllocator)
.disableSubsystemProcessing() // the Subsystem's subsystem does not contain useful data
.setReducedHeader(true)
.readMat();

// The first entry in the top level subsystem (end of root file) contains the
// 'FileWrapper__' object which contains the data backing the various reference
// classes, e.g., handles. Note that more than one references can reference the
// same data, and that the referenced objects may themselves be references.
McosFileWrapper fileWrapper = subFile.getStruct(0).get("MCOS");
List<McosObject> objects = fileWrapper.parseObjects(mcosRegistry);

// Update references of all handle classes
objects.add(0, null); // bump count to match off-by-one index
for (McosReference reference : mcosRegistry.getReferences()) {
reference.setReferences(objects);
}

}

@Override
public void close() throws IOException {
if (subFile != null)
subFile.close();
bufferAllocator.release(buffer);
buffer = null;
bufferAllocator = null;
subFile = null;
}

private ByteBuffer buffer;
private BufferAllocator bufferAllocator;
private Mat5File subFile;

}
Original file line number Diff line number Diff line change
@@ -1,192 +1,192 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.Source;

import java.io.EOFException;
import java.io.IOException;

import static us.hebi.matlab.common.memory.Bytes.*;
import static us.hebi.matlab.mat.format.Mat5Reader.*;

/**
* --- Data Element Format ---
* Each data element begins with an 8-byte tag followed immediately by the data in the
* element. The data is then followed by padding up to 8 bytes. The Matrix and Compressed
* types do not require padding.
* <p>
* [4 byte data type]
* [4 byte number of bytes]
* [N byte ... data ...]
* [0-7 byte padding]
* <p>
* --- Small Data Element Format ---
* If a data element takes up only 1 to 4 bytes, MATLAB saves storage space by storing the
* data in an 8-byte format. In this format, the Data Type and Number of Bytes fields are
* stored as 16-bit values, freeing 4 bytes in the tag in which to store the data.
* <p>
* [2 byte number of bytes]
* [2 byte data type]
* [N byte ... data ...]
* [0-7 byte padding]
* <p>
* @see <a href="http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf">MAT-File Format</a>
*
* @author Florian Enner
* @since 30 Apr 2018
*/
public class Mat5Tag {

public Mat5Type getType() {
return type;
}

public int getNumBytes() {
return numBytes;
}

public int getNumElements() {
return numBytes / type.bytes();
}

public int getPadding() {
return type.getPadding(numBytes, packed);
}

/**
* @return next tag, or null if the source is at the end
*/
public static Mat5Tag readTagOrNull(Source source) throws IOException {
try {
return readTag(source);
} catch (EOFException eof) {
return null;
}
}

/**
* @return next tag, or EOF Exception if the source is at the end
*/
public static Mat5Tag readTag(Source source) throws IOException {
final int tmp = source.readInt();
final Mat5Type type;
final int numBytes;

// Packed/Compacted header
final boolean packed = tmp >> 16 != 0;
if (!packed) {
// not packed (8 bytes)
type = Mat5Type.fromId(tmp);
numBytes = source.readInt();
} else {
// packed (4 bytes)
numBytes = tmp >> 16; // upper
type = Mat5Type.fromId(tmp & 0xFFFF); // lower
}

// Sanity check that byte size matches tag
if (numBytes % type.bytes() != 0)
throw readError("Found invalid number of bytes for tag '%s'. Expected multiple of %d. Found %d",
type, type.bytes(), numBytes);

return new Mat5Tag(type, numBytes, packed, source);
}

byte[] readAsBytes() throws IOException {
byte[] buffer = new byte[getNumBytes()];
source.readBytes(buffer, 0, buffer.length);
source.skip(getPadding());
return buffer;
}

short[] readAsShorts() throws IOException {
checkMultipleOf(SIZEOF_SHORT, "short[]");
short[] buffer = new short[getNumBytes() / SIZEOF_SHORT];
source.readShorts(buffer, 0, buffer.length);
source.skip(getPadding());
return buffer;
}

int[] readAsInts() throws IOException {
checkMultipleOf(SIZEOF_INT, "int[]");
int[] buffer = new int[getNumBytes() / SIZEOF_INT];
source.readInts(buffer, 0, buffer.length);
source.skip(getPadding());
return buffer;
}

long[] readAsLongs() throws IOException {
checkMultipleOf(SIZEOF_LONG, "long[]");
long[] buffer = new long[getNumBytes() / SIZEOF_LONG];
source.readLongs(buffer, 0, buffer.length);
source.skip(getPadding());
return buffer;
}

float[] readAsFloats() throws IOException {
checkMultipleOf(SIZEOF_FLOAT, "float[]");
float[] buffer = new float[getNumBytes() / SIZEOF_FLOAT];
source.readFloats(buffer, 0, buffer.length);
source.skip(getPadding());
return buffer;
}

double[] readAsDoubles() throws IOException {
checkMultipleOf(SIZEOF_DOUBLE, "double[]");
double[] buffer = new double[getNumBytes() / SIZEOF_DOUBLE];
source.readDoubles(buffer, 0, buffer.length);
source.skip(getPadding());
return buffer;
}

private void checkMultipleOf(int bytes, String target) throws IOException {
if (getNumBytes() % bytes != 0) {
throw readError("Tag with %d bytes cannot be read as %s", bytes, target);
}
}


@Override
public String toString() {
return "Mat5Tag{" +
"type=" + type +
", numBytes=" + numBytes +
(packed ? " (packed)" : "") +
'}';
}

private Mat5Tag(Mat5Type type, int numBytes, boolean packed, Source source) {
this.type = type;
this.numBytes = numBytes;
this.packed = packed;
this.source = source;
}

private final Mat5Type type;

private final int numBytes;

private final boolean packed;

private final Source source;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.Source;

import java.io.EOFException;
import java.io.IOException;

import static us.hebi.matlab.mat.util.Bytes.*;
import static us.hebi.matlab.mat.format.Mat5Reader.*;

/**
* --- Data Element Format ---
* Each data element begins with an 8-byte tag followed immediately by the data in the
* element. The data is then followed by padding up to 8 bytes. The Matrix and Compressed
* types do not require padding.
* <p>
* [4 byte data type]
* [4 byte number of bytes]
* [N byte ... data ...]
* [0-7 byte padding]
* <p>
* --- Small Data Element Format ---
* If a data element takes up only 1 to 4 bytes, MATLAB saves storage space by storing the
* data in an 8-byte format. In this format, the Data Type and Number of Bytes fields are
* stored as 16-bit values, freeing 4 bytes in the tag in which to store the data.
* <p>
* [2 byte number of bytes]
* [2 byte data type]
* [N byte ... data ...]
* [0-7 byte padding]
* <p>
* @see <a href="http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf">MAT-File Format</a>
*
* @author Florian Enner
* @since 30 Apr 2018
*/
public class Mat5Tag {

public Mat5Type getType() {
return type;
}

public int getNumBytes() {
return numBytes;
}

public int getNumElements() {
return numBytes / type.bytes();
}

public int getPadding() {
return type.getPadding(numBytes, packed);
}

/**
* @return next tag, or null if the source is at the end
*/
public static Mat5Tag readTagOrNull(Source source) throws IOException {
try {
return readTag(source);
} catch (EOFException eof) {
return null;
}
}

/**
* @return next tag, or EOF Exception if the source is at the end
*/
public static Mat5Tag readTag(Source source) throws IOException {
final int tmp = source.readInt();
final Mat5Type type;
final int numBytes;

// Packed/Compacted header
final boolean packed = tmp >> 16 != 0;
if (!packed) {
// not packed (8 bytes)
type = Mat5Type.fromId(tmp);
numBytes = source.readInt();
} else {
// packed (4 bytes)
numBytes = tmp >> 16; // upper
type = Mat5Type.fromId(tmp & 0xFFFF); // lower
}

// Sanity check that byte size matches tag
if (numBytes % type.bytes() != 0)
throw readError("Found invalid number of bytes for tag '%s'. Expected multiple of %d. Found %d",
type, type.bytes(), numBytes);

return new Mat5Tag(type, numBytes, packed, source);
}

byte[] readAsBytes() throws IOException {
byte[] buffer = new byte[getNumBytes()];
source.readBytes(buffer, 0, buffer.length);
source.skip(getPadding());
return buffer;
}

short[] readAsShorts() throws IOException {
checkMultipleOf(SIZEOF_SHORT, "short[]");
short[] buffer = new short[getNumBytes() / SIZEOF_SHORT];
source.readShorts(buffer, 0, buffer.length);
source.skip(getPadding());
return buffer;
}

int[] readAsInts() throws IOException {
checkMultipleOf(SIZEOF_INT, "int[]");
int[] buffer = new int[getNumBytes() / SIZEOF_INT];
source.readInts(buffer, 0, buffer.length);
source.skip(getPadding());
return buffer;
}

long[] readAsLongs() throws IOException {
checkMultipleOf(SIZEOF_LONG, "long[]");
long[] buffer = new long[getNumBytes() / SIZEOF_LONG];
source.readLongs(buffer, 0, buffer.length);
source.skip(getPadding());
return buffer;
}

float[] readAsFloats() throws IOException {
checkMultipleOf(SIZEOF_FLOAT, "float[]");
float[] buffer = new float[getNumBytes() / SIZEOF_FLOAT];
source.readFloats(buffer, 0, buffer.length);
source.skip(getPadding());
return buffer;
}

double[] readAsDoubles() throws IOException {
checkMultipleOf(SIZEOF_DOUBLE, "double[]");
double[] buffer = new double[getNumBytes() / SIZEOF_DOUBLE];
source.readDoubles(buffer, 0, buffer.length);
source.skip(getPadding());
return buffer;
}

private void checkMultipleOf(int bytes, String target) throws IOException {
if (getNumBytes() % bytes != 0) {
throw readError("Tag with %d bytes cannot be read as %s", bytes, target);
}
}


@Override
public String toString() {
return "Mat5Tag{" +
"type=" + type +
", numBytes=" + numBytes +
(packed ? " (packed)" : "") +
'}';
}

private Mat5Tag(Mat5Type type, int numBytes, boolean packed, Source source) {
this.type = type;
this.numBytes = numBytes;
this.packed = packed;
this.source = source;
}

private final Mat5Type type;

private final int numBytes;

private final boolean packed;

private final Source source;

}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,167 +1,166 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.common.util.Casts;
import us.hebi.matlab.common.util.Charsets;
import us.hebi.matlab.mat.types.Array;
import us.hebi.matlab.mat.types.Opaque;
import us.hebi.matlab.mat.types.Sink;

import java.io.IOException;
import java.nio.CharBuffer;
import java.util.zip.Deflater;

import static us.hebi.matlab.common.util.Casts.*;
import static us.hebi.matlab.mat.format.Mat5Type.Int32;
import static us.hebi.matlab.mat.format.Mat5Type.Int8;
import static us.hebi.matlab.mat.format.Mat5Type.*;
import static us.hebi.matlab.mat.format.Mat5Type.UInt32;
import static us.hebi.matlab.mat.types.MatlabType.*;

/**
* Static utility methods for serializing custom classes
*
* @author Florian Enner
* @since 26 Oct 2018
*/
public class Mat5WriteUtil {

public static void writeArrayWithTag(Array array, Sink sink) throws IOException {
writeArrayWithTag("", array, sink);
}

public static void writeArrayWithTag(String name, Array array, Sink sink) throws IOException {
if (array instanceof Mat5Serializable) {
((Mat5Serializable) array).writeMat5(name, sink);
return;
}
throw new IllegalArgumentException("Array does not support the MAT5 format");
}

public static int computeArraySize(Array array) {
return computeArraySize("", array);
}

public static int computeArraySize(String name, Array array) {
if (array instanceof Mat5Serializable)
return ((Mat5Serializable) array).getMat5Size(name);
throw new IllegalArgumentException("Array does not support the MAT5 format");
}

public static int computeArrayHeaderSize(String name, Array array) {
int arrayFlags = UInt32.computeSerializedSize(2);
int dimensions = Int32.computeSerializedSize(array.getNumDimensions());
int nameLen = Int8.computeSerializedSize(name.length()); // ascii
return arrayFlags + dimensions + nameLen;
}

public static void writeMatrixTag(String name, Mat5Serializable array, Sink sink) throws IOException {
Matrix.writeTag(array.getMat5Size(name) - Mat5.MATRIX_TAG_SIZE, sink);
}

public static void writeArrayHeader(String name, Array array, Sink sink) throws IOException {
if (array.getType() == Opaque)
throw new IllegalArgumentException("Opaque types do not share the same format as other types");

// Subfield 1: Meta data
UInt32.writeIntsWithTag(Mat5ArrayFlags.forArray(array), sink);

// Subfield 2: Dimensions
Int32.writeIntsWithTag(array.getDimensions(), sink);

// Subfield 3: Name
Int8.writeBytesWithTag(name.getBytes(Charsets.US_ASCII), sink);
}

public static int computeOpaqueSize(String name, us.hebi.matlab.mat.types.Opaque array) {
return Mat5.MATRIX_TAG_SIZE
+ UInt32.computeSerializedSize(2)
+ Int8.computeSerializedSize(name.length())
+ Int8.computeSerializedSize(array.getObjectType().length())
+ Int8.computeSerializedSize(array.getClassName().length())
+ computeArraySize(array.getContent());
}

public static void writeOpaque(String name, Opaque opaque, Sink sink) throws IOException {
// Tag
final int numBytes = computeOpaqueSize(name, opaque) - Mat5.MATRIX_TAG_SIZE;
Matrix.writeTag(numBytes, sink);

// Subfield 1: Meta data
UInt32.writeIntsWithTag(Mat5ArrayFlags.forOpaque(opaque), sink);

// Subfield 2: Ascii variable name
Int8.writeBytesWithTag(name.getBytes(Charsets.US_ASCII), sink);

// Subfield 3: Object Identifier (e.g. "MCOS", "handle", "java")
Int8.writeBytesWithTag(opaque.getObjectType().getBytes(Charsets.US_ASCII), sink);

// Subfield 4: Class name (e.g. "table", "string", "java.io.File")
Int8.writeBytesWithTag(opaque.getClassName().getBytes(Charsets.US_ASCII), sink);

// Subfield 5: Content
writeArrayWithTag(opaque.getContent(), sink);
}

static int computeCharBufferSize(CharEncoding encoding, CharBuffer buffer) {
Mat5Type tagType = Mat5Type.fromCharEncoding(encoding);
int numElements = checkedDivide(encoding.getEncodedLength(buffer), tagType.bytes());
return tagType.computeSerializedSize(numElements);
}

static void writeCharBufferWithTag(CharEncoding encoding, CharBuffer buffer, Sink sink) throws IOException {
Mat5Type tagType = Mat5Type.fromCharEncoding(encoding);
int numElements = checkedDivide(encoding.getEncodedLength(buffer), tagType.bytes());
tagType.writeTag(numElements, sink);
encoding.writeEncoded(buffer, sink);
tagType.writePadding(numElements, sink);
}

static void writeArrayWithTagDeflated(String name, Array array, Sink sink, Deflater deflater) throws IOException {

// Write placeholder tag with a dummy size so we can fill in info later
long tagPosition = sink.position();
Compressed.writeTag(DUMMY_SIZE, false, sink);
long start = sink.position();

// Compress matrix data
Sink compressed = sink.writeDeflated(deflater);
writeArrayWithTag(name, array, compressed);
compressed.close(); // triggers flush/finish

// Calculate actual size
long end = sink.position();
long compressedSize = end - start;

// Overwrite placeholder tag with the real size
sink.position(tagPosition);
Compressed.writeTag(Casts.sint32(compressedSize), false, sink);

// Return to the current real position after the compressed data
// Note that compressed tags don't require padding for alignment.
sink.position(end);

}

private static final int DUMMY_SIZE = 0;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.util.Casts;
import us.hebi.matlab.mat.types.Array;
import us.hebi.matlab.mat.types.Opaque;
import us.hebi.matlab.mat.types.Sink;

import java.io.IOException;
import java.nio.CharBuffer;
import java.util.zip.Deflater;

import static us.hebi.matlab.mat.util.Casts.*;
import static us.hebi.matlab.mat.format.Mat5Type.Int32;
import static us.hebi.matlab.mat.format.Mat5Type.Int8;
import static us.hebi.matlab.mat.format.Mat5Type.*;
import static us.hebi.matlab.mat.format.Mat5Type.UInt32;
import static us.hebi.matlab.mat.types.MatlabType.*;

/**
* Static utility methods for serializing custom classes
*
* @author Florian Enner
* @since 26 Oct 2018
*/
public class Mat5WriteUtil {

public static void writeArrayWithTag(Array array, Sink sink) throws IOException {
writeArrayWithTag("", array, sink);
}

public static void writeArrayWithTag(String name, Array array, Sink sink) throws IOException {
if (array instanceof Mat5Serializable) {
((Mat5Serializable) array).writeMat5(name, sink);
return;
}
throw new IllegalArgumentException("Array does not support the MAT5 format");
}

public static int computeArraySize(Array array) {
return computeArraySize("", array);
}

public static int computeArraySize(String name, Array array) {
if (array instanceof Mat5Serializable)
return ((Mat5Serializable) array).getMat5Size(name);
throw new IllegalArgumentException("Array does not support the MAT5 format");
}

public static int computeArrayHeaderSize(String name, Array array) {
int arrayFlags = UInt32.computeSerializedSize(2);
int dimensions = Int32.computeSerializedSize(array.getNumDimensions());
int nameLen = Int8.computeSerializedSize(name.length()); // ascii
return arrayFlags + dimensions + nameLen;
}

public static void writeMatrixTag(String name, Mat5Serializable array, Sink sink) throws IOException {
Matrix.writeTag(array.getMat5Size(name) - Mat5.MATRIX_TAG_SIZE, sink);
}

public static void writeArrayHeader(String name, Array array, Sink sink) throws IOException {
if (array.getType() == Opaque)
throw new IllegalArgumentException("Opaque types do not share the same format as other types");

// Subfield 1: Meta data
UInt32.writeIntsWithTag(Mat5ArrayFlags.forArray(array), sink);

// Subfield 2: Dimensions
Int32.writeIntsWithTag(array.getDimensions(), sink);

// Subfield 3: Name
Int8.writeBytesWithTag(name.getBytes(Charsets.US_ASCII), sink);
}

public static int computeOpaqueSize(String name, us.hebi.matlab.mat.types.Opaque array) {
return Mat5.MATRIX_TAG_SIZE
+ UInt32.computeSerializedSize(2)
+ Int8.computeSerializedSize(name.length())
+ Int8.computeSerializedSize(array.getObjectType().length())
+ Int8.computeSerializedSize(array.getClassName().length())
+ computeArraySize(array.getContent());
}

public static void writeOpaque(String name, Opaque opaque, Sink sink) throws IOException {
// Tag
final int numBytes = computeOpaqueSize(name, opaque) - Mat5.MATRIX_TAG_SIZE;
Matrix.writeTag(numBytes, sink);

// Subfield 1: Meta data
UInt32.writeIntsWithTag(Mat5ArrayFlags.forOpaque(opaque), sink);

// Subfield 2: Ascii variable name
Int8.writeBytesWithTag(name.getBytes(Charsets.US_ASCII), sink);

// Subfield 3: Object Identifier (e.g. "MCOS", "handle", "java")
Int8.writeBytesWithTag(opaque.getObjectType().getBytes(Charsets.US_ASCII), sink);

// Subfield 4: Class name (e.g. "table", "string", "java.io.File")
Int8.writeBytesWithTag(opaque.getClassName().getBytes(Charsets.US_ASCII), sink);

// Subfield 5: Content
writeArrayWithTag(opaque.getContent(), sink);
}

static int computeCharBufferSize(CharEncoding encoding, CharBuffer buffer) {
Mat5Type tagType = Mat5Type.fromCharEncoding(encoding);
int numElements = checkedDivide(encoding.getEncodedLength(buffer), tagType.bytes());
return tagType.computeSerializedSize(numElements);
}

static void writeCharBufferWithTag(CharEncoding encoding, CharBuffer buffer, Sink sink) throws IOException {
Mat5Type tagType = Mat5Type.fromCharEncoding(encoding);
int numElements = checkedDivide(encoding.getEncodedLength(buffer), tagType.bytes());
tagType.writeTag(numElements, sink);
encoding.writeEncoded(buffer, sink);
tagType.writePadding(numElements, sink);
}

static void writeArrayWithTagDeflated(String name, Array array, Sink sink, Deflater deflater) throws IOException {

// Write placeholder tag with a dummy size so we can fill in info later
long tagPosition = sink.position();
Compressed.writeTag(DUMMY_SIZE, false, sink);
long start = sink.position();

// Compress matrix data
Sink compressed = sink.writeDeflated(deflater);
writeArrayWithTag(name, array, compressed);
compressed.close(); // triggers flush/finish

// Calculate actual size
long end = sink.position();
long compressedSize = end - start;

// Overwrite placeholder tag with the real size
sink.position(tagPosition);
Compressed.writeTag(Casts.sint32(compressedSize), false, sink);

// Return to the current real position after the compressed data
// Note that compressed tags don't require padding for alignment.
sink.position(end);

}

private static final int DUMMY_SIZE = 0;

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,71 +1,71 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.AbstractCell;
import us.hebi.matlab.mat.types.Array;
import us.hebi.matlab.mat.types.Sink;

import java.io.IOException;
import java.util.Arrays;

import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* @author Florian Enner
* @since 29 Aug 2018
*/
class MatCell extends AbstractCell implements Mat5Serializable {

MatCell(int[] dims, boolean global) {
super(dims, global, new Array[getNumElements(dims)]);
Arrays.fill(contents, getEmptyValue());
}

MatCell(int[] dims, boolean global, Array[] contents) {
super(dims, global, contents);
}

@Override
protected Array getEmptyValue() {
return Mat5.EMPTY_MATRIX;
}

@Override
public int getMat5Size(String name) {
int size = Mat5.MATRIX_TAG_SIZE;
size += computeArrayHeaderSize(name, this);
for (int i = 0; i < getNumElements(); i++) {
size += computeArraySize(get(i));
}
return size;
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
writeMatrixTag(name, this, sink);
writeArrayHeader(name, this, sink);
for (int i = 0; i < getNumElements(); i++) {
writeArrayWithTag(get(i), sink);
}
}

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.AbstractCell;
import us.hebi.matlab.mat.types.Array;
import us.hebi.matlab.mat.types.Sink;

import java.io.IOException;
import java.util.Arrays;

import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* @author Florian Enner
* @since 29 Aug 2018
*/
class MatCell extends AbstractCell implements Mat5Serializable {

MatCell(int[] dims, boolean global) {
super(dims, global, new Array[getNumElements(dims)]);
Arrays.fill(contents, getEmptyValue());
}

MatCell(int[] dims, boolean global, Array[] contents) {
super(dims, global, contents);
}

@Override
protected Array getEmptyValue() {
return Mat5.EMPTY_MATRIX;
}

@Override
public int getMat5Size(String name) {
int size = Mat5.MATRIX_TAG_SIZE;
size += computeArrayHeaderSize(name, this);
for (int i = 0; i < getNumElements(); i++) {
size += computeArraySize(get(i));
}
return size;
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
writeMatrixTag(name, this, sink);
writeArrayHeader(name, this, sink);
for (int i = 0; i < getNumElements(); i++) {
writeArrayWithTag(get(i), sink);
}
}

}
Original file line number Diff line number Diff line change
@@ -1,91 +1,91 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.AbstractCharBase;
import us.hebi.matlab.mat.types.Sink;

import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Arrays;

import static us.hebi.matlab.common.util.Preconditions.*;
import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* Default char array implementation backed by a char buffer
*
* @author Florian Enner
* @since 29 Aug 2018
*/
class MatChar extends AbstractCharBase implements Mat5Serializable {

MatChar(int[] dims, CharEncoding encoding) {
this(dims, false, encoding, CharBuffer.allocate(getNumElements(dims)));
Arrays.fill(buffer.array(), ' ');
}

MatChar(int[] dims, boolean global, CharEncoding encoding, CharBuffer buffer) {
super(dims, global);
checkArgument(buffer.remaining() == getNumElements(), "Unexpected number of elements");
this.buffer = checkNotNull(buffer);
this.encoding = checkNotNull(encoding);
}

@Override
public CharSequence asCharSequence() {
return buffer.slice();
}

@Override
public char getChar(int index) {
return buffer.get(index);
}

@Override
public void setChar(int index, char value) {
buffer.put(index, value);
}

@Override
public void close() {
}

@Override
public int getMat5Size(String name) {
buffer.rewind();
return Mat5.MATRIX_TAG_SIZE
+ computeArrayHeaderSize(name, this)
+ computeCharBufferSize(encoding, buffer);
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
buffer.rewind();
writeMatrixTag(name, this, sink);
writeArrayHeader(name, this, sink);
writeCharBufferWithTag(encoding, buffer, sink);
}

protected final CharBuffer buffer;
protected final CharEncoding encoding;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.AbstractCharBase;
import us.hebi.matlab.mat.types.Sink;

import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Arrays;

import static us.hebi.matlab.mat.util.Preconditions.*;
import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* Default char array implementation backed by a char buffer
*
* @author Florian Enner
* @since 29 Aug 2018
*/
class MatChar extends AbstractCharBase implements Mat5Serializable {

MatChar(int[] dims, CharEncoding encoding) {
this(dims, false, encoding, CharBuffer.allocate(getNumElements(dims)));
Arrays.fill(buffer.array(), ' ');
}

MatChar(int[] dims, boolean global, CharEncoding encoding, CharBuffer buffer) {
super(dims, global);
checkArgument(buffer.remaining() == getNumElements(), "Unexpected number of elements");
this.buffer = checkNotNull(buffer);
this.encoding = checkNotNull(encoding);
}

@Override
public CharSequence asCharSequence() {
return buffer.slice();
}

@Override
public char getChar(int index) {
return buffer.get(index);
}

@Override
public void setChar(int index, char value) {
buffer.put(index, value);
}

@Override
public void close() {
}

@Override
public int getMat5Size(String name) {
buffer.rewind();
return Mat5.MATRIX_TAG_SIZE
+ computeArrayHeaderSize(name, this)
+ computeCharBufferSize(encoding, buffer);
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
buffer.rewind();
writeMatrixTag(name, this, sink);
writeArrayHeader(name, this, sink);
writeCharBufferWithTag(encoding, buffer, sink);
}

protected final CharBuffer buffer;
protected final CharEncoding encoding;

}
Original file line number Diff line number Diff line change
@@ -1,71 +1,71 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.*;

import java.io.IOException;

import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* @author Florian Enner
* @since 02 Sep 2018
*/
class MatFunction extends AbstractArray implements FunctionHandle, Mat5Serializable {

MatFunction(boolean isGlobal, Struct content) {
super(new int[]{1, 1}, isGlobal);
this.content = content;
}

@Override
public MatlabType getType() {
return MatlabType.Function;
}

@Override
public Struct getContent() {
return content;
}

@Override
public int getMat5Size(String name) {
return Mat5.MATRIX_TAG_SIZE
+ computeArrayHeaderSize(name, this)
+ computeArraySize(getContent());
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
writeMatrixTag(name, this, sink);
writeArrayHeader(name, this, sink);
writeArrayWithTag(content, sink);
}

@Override
public void close() throws IOException {
content.close();
}

final Struct content;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.*;

import java.io.IOException;

import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* @author Florian Enner
* @since 02 Sep 2018
*/
class MatFunction extends AbstractArray implements FunctionHandle, Mat5Serializable {

MatFunction(boolean isGlobal, Struct content) {
super(new int[]{1, 1}, isGlobal);
this.content = content;
}

@Override
public MatlabType getType() {
return MatlabType.Function;
}

@Override
public Struct getContent() {
return content;
}

@Override
public int getMat5Size(String name) {
return Mat5.MATRIX_TAG_SIZE
+ computeArrayHeaderSize(name, this)
+ computeArraySize(getContent());
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
writeMatrixTag(name, this, sink);
writeArrayHeader(name, this, sink);
writeArrayWithTag(content, sink);
}

@Override
public void close() throws IOException {
content.close();
}

final Struct content;

}
Original file line number Diff line number Diff line change
@@ -1,88 +1,88 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.*;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.nio.ByteBuffer;

/**
* @author Florian Enner
* @since 01 Sep 2018
*/
class MatJavaObject extends MatOpaque implements JavaObject {

MatJavaObject(boolean isGlobal, String className, Array content) {
super(isGlobal, "java", className, content);
}

/**
* Finds the binary data where the serialized bytes are stored.
* Note: Logic copied from MatFileRW
*
* @return Array containing the serialized bytes
*/
private Matrix getSerializedData() {
Array content = getContent();

// Usually returned directly
if (content instanceof Matrix)
return (Matrix) content;

// Sometimes returned in a structure field
if (content instanceof Struct)
return ((Struct) content).get("Values");

// Sometimes stored as a cell. In that case we'll
// take the first numeric array we can find.
if (content instanceof Cell) {
Cell cells = (Cell) content;
for (int i = 0; i < cells.getNumElements(); i++) {
Array array = cells.get(i);
if (array instanceof Matrix) {
return (Matrix) array;
}
}
}

String msg = String.format("Unexpected byte storage. Found: %s", content);
throw new IllegalStateException(msg);
}

@Override
public Object instantiateObject() throws Exception {
// Export matrix as raw bytes (can be uint8/int8/uint32)
ByteBuffer buffer = Mat5.exportBytes(getSerializedData());
final byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);

// Convert to input stream
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
try {
return ois.readObject();
} finally {
ois.close();
}
}

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.*;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.nio.ByteBuffer;

/**
* @author Florian Enner
* @since 01 Sep 2018
*/
class MatJavaObject extends MatOpaque implements JavaObject {

MatJavaObject(boolean isGlobal, String className, Array content) {
super(isGlobal, "java", className, content);
}

/**
* Finds the binary data where the serialized bytes are stored.
* Note: Logic copied from MatFileRW
*
* @return Array containing the serialized bytes
*/
private Matrix getSerializedData() {
Array content = getContent();

// Usually returned directly
if (content instanceof Matrix)
return (Matrix) content;

// Sometimes returned in a structure field
if (content instanceof Struct)
return ((Struct) content).get("Values");

// Sometimes stored as a cell. In that case we'll
// take the first numeric array we can find.
if (content instanceof Cell) {
Cell cells = (Cell) content;
for (int i = 0; i < cells.getNumElements(); i++) {
Array array = cells.get(i);
if (array instanceof Matrix) {
return (Matrix) array;
}
}
}

String msg = String.format("Unexpected byte storage. Found: %s", content);
throw new IllegalStateException(msg);
}

@Override
public Object instantiateObject() throws Exception {
// Export matrix as raw bytes (can be uint8/int8/uint32)
ByteBuffer buffer = Mat5.exportBytes(getSerializedData());
final byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);

// Convert to input stream
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
try {
return ois.readObject();
} finally {
ois.close();
}
}

}
Original file line number Diff line number Diff line change
@@ -1,157 +1,157 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.AbstractMatrixBase;
import us.hebi.matlab.mat.types.MatlabType;
import us.hebi.matlab.mat.types.Sink;

import java.io.IOException;

import static us.hebi.matlab.common.util.Preconditions.*;
import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* @author Florian Enner
* @since 29 Aug 2018
*/
class MatMatrix extends AbstractMatrixBase implements Mat5Serializable {

MatMatrix(int[] dims, boolean isGlobal, MatlabType type, boolean logical, NumberStore real, NumberStore imaginary) {
super(dims, isGlobal);
this.type = checkNotNull(type);
this.logical = logical;

this.real = checkNotNull(real);
if (real.getNumElements() != getNumElements())
throw new IllegalArgumentException("Incorrect number of elements in real store");

this.imaginary = imaginary;
this.complex = imaginary != null;
if (complex && imaginary.getNumElements() != getNumElements())
throw new IllegalArgumentException("Incorrect number of elements in imaginary store");

}

@Override
public MatlabType getType() {
return type;
}

@Override
public boolean isLogical() {
return logical;
}

protected void setLogical(boolean value) {
this.logical = value;
}

@Override
public boolean isComplex() {
return complex;
}

@Override
public long getLong(int index) {
return orLogical(real.getLong(index));
}

@Override
public void setLong(int index, long value) {
real.setLong(index, value);
}

@Override
public double getDouble(int index) {
return orLogical(real.getDouble(index));
}

@Override
public void setDouble(int index, double value) {
real.setDouble(index, value);
}

@Override
public long getImaginaryLong(int index) {
return orLogical(complex ? imaginary.getLong(index) : 0);
}

@Override
public void setImaginaryLong(int index, long value) {
checkState(complex, "Matrix is not complex");
imaginary.setLong(index, value);
}

@Override
public double getImaginaryDouble(int index) {
return orLogical(complex ? imaginary.getDouble(index) : 0);
}

@Override
public void setImaginaryDouble(int index, double value) {
checkState(complex, "Matrix is not complex");
imaginary.setDouble(index, value);
}

@Override
public int getMat5Size(String name) {
return Mat5.MATRIX_TAG_SIZE
+ computeArrayHeaderSize(name, this)
+ real.getMat5Size()
+ (complex ? imaginary.getMat5Size() : 0);
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
writeMatrixTag(name, this, sink);
writeArrayHeader(name, this, sink);
real.writeMat5(sink);
if (complex) imaginary.writeMat5(sink);
}

/**
* Internal API
*/
NumberStore getRealStore() {
return real;
}

@Override
public void close() throws IOException {
// Ignore EMPTY_MATRIX elements. At some
// point we may want to create a separate
// class to represent empty.
if(this == Mat5.EMPTY_MATRIX)
return;

real.close();
if (imaginary != null)
imaginary.close();
}

private boolean logical;
private final NumberStore real;
private final NumberStore imaginary;
private final boolean complex;
private final MatlabType type;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.AbstractMatrixBase;
import us.hebi.matlab.mat.types.MatlabType;
import us.hebi.matlab.mat.types.Sink;

import java.io.IOException;

import static us.hebi.matlab.mat.util.Preconditions.*;
import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* @author Florian Enner
* @since 29 Aug 2018
*/
class MatMatrix extends AbstractMatrixBase implements Mat5Serializable {

MatMatrix(int[] dims, boolean isGlobal, MatlabType type, boolean logical, NumberStore real, NumberStore imaginary) {
super(dims, isGlobal);
this.type = checkNotNull(type);
this.logical = logical;

this.real = checkNotNull(real);
if (real.getNumElements() != getNumElements())
throw new IllegalArgumentException("Incorrect number of elements in real store");

this.imaginary = imaginary;
this.complex = imaginary != null;
if (complex && imaginary.getNumElements() != getNumElements())
throw new IllegalArgumentException("Incorrect number of elements in imaginary store");

}

@Override
public MatlabType getType() {
return type;
}

@Override
public boolean isLogical() {
return logical;
}

protected void setLogical(boolean value) {
this.logical = value;
}

@Override
public boolean isComplex() {
return complex;
}

@Override
public long getLong(int index) {
return orLogical(real.getLong(index));
}

@Override
public void setLong(int index, long value) {
real.setLong(index, value);
}

@Override
public double getDouble(int index) {
return orLogical(real.getDouble(index));
}

@Override
public void setDouble(int index, double value) {
real.setDouble(index, value);
}

@Override
public long getImaginaryLong(int index) {
return orLogical(complex ? imaginary.getLong(index) : 0);
}

@Override
public void setImaginaryLong(int index, long value) {
checkState(complex, "Matrix is not complex");
imaginary.setLong(index, value);
}

@Override
public double getImaginaryDouble(int index) {
return orLogical(complex ? imaginary.getDouble(index) : 0);
}

@Override
public void setImaginaryDouble(int index, double value) {
checkState(complex, "Matrix is not complex");
imaginary.setDouble(index, value);
}

@Override
public int getMat5Size(String name) {
return Mat5.MATRIX_TAG_SIZE
+ computeArrayHeaderSize(name, this)
+ real.getMat5Size()
+ (complex ? imaginary.getMat5Size() : 0);
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
writeMatrixTag(name, this, sink);
writeArrayHeader(name, this, sink);
real.writeMat5(sink);
if (complex) imaginary.writeMat5(sink);
}

/**
* Internal API
*/
NumberStore getRealStore() {
return real;
}

@Override
public void close() throws IOException {
// Ignore EMPTY_MATRIX elements. At some
// point we may want to create a separate
// class to represent empty.
if(this == Mat5.EMPTY_MATRIX)
return;

real.close();
if (imaginary != null)
imaginary.close();
}

private boolean logical;
private final NumberStore real;
private final NumberStore imaginary;
private final boolean complex;
private final MatlabType type;

}
Original file line number Diff line number Diff line change
@@ -1,55 +1,55 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.Array;
import us.hebi.matlab.mat.types.MatlabType;
import us.hebi.matlab.mat.types.ObjectStruct;

/**
* @author Florian Enner
* @since 03 Sep 2018
*/
class MatObjectStruct extends MatStruct implements ObjectStruct {

MatObjectStruct(int[] dims, boolean isGlobal, String className, String[] names, Array[][] values) {
super(dims, isGlobal, names, values);
this.className = className;
}

@Override
public MatlabType getType() {
return MatlabType.Object;
}

@Override
public String getPackageName() {
return "";
}

@Override
public String getClassName() {
return className;
}

private final String className;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.Array;
import us.hebi.matlab.mat.types.MatlabType;
import us.hebi.matlab.mat.types.ObjectStruct;

/**
* @author Florian Enner
* @since 03 Sep 2018
*/
class MatObjectStruct extends MatStruct implements ObjectStruct {

MatObjectStruct(int[] dims, boolean isGlobal, String className, String[] names, Array[][] values) {
super(dims, isGlobal, names, values);
this.className = className;
}

@Override
public MatlabType getType() {
return MatlabType.Object;
}

@Override
public String getPackageName() {
return "";
}

@Override
public String getClassName() {
return className;
}

private final String className;

}
Original file line number Diff line number Diff line change
@@ -1,79 +1,79 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.*;

import java.io.IOException;

import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* @author Florian Enner
* @since 04 Sep 2018
*/
class MatOpaque extends AbstractArray implements Opaque, Mat5Serializable {

MatOpaque(boolean isGlobal, String objectType, String className, Array content) {
super(SINGLE_DIM, isGlobal);
this.content = content;
this.className = className;
this.objectType = objectType;
}

@Override
public MatlabType getType() {
return MatlabType.Opaque;
}

public String getObjectType() {
return objectType;
}

public String getClassName() {
return className;
}

public Array getContent() {
return content;
}

@Override
public int getMat5Size(String name) {
return computeOpaqueSize(name, this);
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
writeOpaque(name, this, sink);
}

@Override
public void close() throws IOException {
content.close();
}

private final String objectType;
private final String className;
private final Array content;
private static final int[] SINGLE_DIM = new int[]{1, 1};

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.*;

import java.io.IOException;

import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* @author Florian Enner
* @since 04 Sep 2018
*/
class MatOpaque extends AbstractArray implements Opaque, Mat5Serializable {

MatOpaque(boolean isGlobal, String objectType, String className, Array content) {
super(SINGLE_DIM, isGlobal);
this.content = content;
this.className = className;
this.objectType = objectType;
}

@Override
public MatlabType getType() {
return MatlabType.Opaque;
}

public String getObjectType() {
return objectType;
}

public String getClassName() {
return className;
}

public Array getContent() {
return content;
}

@Override
public int getMat5Size(String name) {
return computeOpaqueSize(name, this);
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
writeOpaque(name, this, sink);
}

@Override
public void close() throws IOException {
content.close();
}

private final String objectType;
private final String className;
private final Array content;
private static final int[] SINGLE_DIM = new int[]{1, 1};

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,150 +1,149 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.AbstractStruct;
import us.hebi.matlab.mat.types.Array;
import us.hebi.matlab.mat.types.MatlabType;
import us.hebi.matlab.mat.types.Sink;
import us.hebi.matlab.common.util.Charsets;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import static us.hebi.matlab.common.util.Preconditions.*;
import static us.hebi.matlab.mat.format.Mat5.*;
import static us.hebi.matlab.mat.format.Mat5Type.*;
import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* @author Florian Enner
* @since 29 Aug 2018
*/
class MatStruct extends AbstractStruct implements Mat5Serializable {

MatStruct(int[] dims, boolean global) {
super(dims, global);
}

MatStruct(int[] dims, boolean isGlobal, String[] names, Array[][] values) {
super(dims, isGlobal);
checkArgument(getNumDimensions() == 2, "Structures are limited to two dimensions");
int numElements = getNumElements();
for (int field = 0; field < names.length; field++) {
for (int i = 0; i < numElements; i++) {
set(names[field], i, values[field][i]);
}
}
}

@Override
protected Array getEmptyValue() {
return Mat5.EMPTY_MATRIX;
}

protected String getClassName() {
return "";
}

protected int getLongestFieldName() {
int length = 0;
for (String name : getFieldNames()) {
length = Math.max(length, name.length());
}
return length;
}

@Override
public int getMat5Size(String name) {
final List<String> fieldNames = getFieldNames();
final int numElements = getNumElements();
final int numFields = fieldNames.size();

// Common fields
int size = MATRIX_TAG_SIZE;
size += computeArrayHeaderSize(name, this);

// Subfield -/4: Object only. Not struct
if (getType() == MatlabType.Object) {
String objectClassName = getClassName();
size += Int8.computeSerializedSize(objectClassName.length());
}

// Subfield 4/5: Field Name Length
size += Int32.computeSerializedSize(1);

// Subfield 5/6: Field Names
int numChars = getLongestFieldName() * numFields;
size += Int8.computeSerializedSize(numChars); // ascii

// Subfield 6/7: Fields
for (int i = 0; i < numElements; i++) {
for (int field = 0; field < numFields; field++) {
size += computeArraySize(get(fieldNames.get(field), i));
}
}

return size;
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
final List<String> fieldNames = getFieldNames();
final int numElements = getNumElements();
final int numFields = fieldNames.size();

// Common fields
writeMatrixTag(name, this, sink);
writeArrayHeader(name, this, sink);

// Subfield -/4: Object only. Not struct
if (getType() == MatlabType.Object) {
String objectClassName = getClassName();
Int8.writeBytesWithTag(objectClassName.getBytes(Charsets.US_ASCII), sink);
}

// Subfield 4/5: Field Name Length
int longestName = getLongestFieldName();
int numChars = longestName * numFields;
Int32.writeIntsWithTag(new int[]{longestName}, sink);

// Subfield 5/6: Field Names
byte[] ascii = new byte[numChars];
Arrays.fill(ascii, (byte) '\0');
for (int i = 0; i < numFields; i++) {
String fieldName = getFieldNames().get(i);
byte[] bytes = fieldName.getBytes(Charsets.US_ASCII);
System.arraycopy(bytes, 0, ascii, i * longestName, bytes.length);
}
Int8.writeBytesWithTag(ascii, sink);

// Subfield 6/7: Fields
checkArgument(getNumDimensions() == 2, "Structures are limited to two dimensions");
for (int i = 0; i < numElements; i++) {
for (int field = 0; field < numFields; field++) {
writeArrayWithTag(get(fieldNames.get(field), i), sink);
}
}

}

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.AbstractStruct;
import us.hebi.matlab.mat.types.Array;
import us.hebi.matlab.mat.types.MatlabType;
import us.hebi.matlab.mat.types.Sink;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import static us.hebi.matlab.mat.util.Preconditions.*;
import static us.hebi.matlab.mat.format.Mat5.*;
import static us.hebi.matlab.mat.format.Mat5Type.*;
import static us.hebi.matlab.mat.format.Mat5WriteUtil.*;

/**
* @author Florian Enner
* @since 29 Aug 2018
*/
class MatStruct extends AbstractStruct implements Mat5Serializable {

MatStruct(int[] dims, boolean global) {
super(dims, global);
}

MatStruct(int[] dims, boolean isGlobal, String[] names, Array[][] values) {
super(dims, isGlobal);
checkArgument(getNumDimensions() == 2, "Structures are limited to two dimensions");
int numElements = getNumElements();
for (int field = 0; field < names.length; field++) {
for (int i = 0; i < numElements; i++) {
set(names[field], i, values[field][i]);
}
}
}

@Override
protected Array getEmptyValue() {
return Mat5.EMPTY_MATRIX;
}

protected String getClassName() {
return "";
}

protected int getLongestFieldName() {
int length = 0;
for (String name : getFieldNames()) {
length = Math.max(length, name.length());
}
return length;
}

@Override
public int getMat5Size(String name) {
final List<String> fieldNames = getFieldNames();
final int numElements = getNumElements();
final int numFields = fieldNames.size();

// Common fields
int size = MATRIX_TAG_SIZE;
size += computeArrayHeaderSize(name, this);

// Subfield -/4: Object only. Not struct
if (getType() == MatlabType.Object) {
String objectClassName = getClassName();
size += Int8.computeSerializedSize(objectClassName.length());
}

// Subfield 4/5: Field Name Length
size += Int32.computeSerializedSize(1);

// Subfield 5/6: Field Names
int numChars = getLongestFieldName() * numFields;
size += Int8.computeSerializedSize(numChars); // ascii

// Subfield 6/7: Fields
for (int i = 0; i < numElements; i++) {
for (int field = 0; field < numFields; field++) {
size += computeArraySize(get(fieldNames.get(field), i));
}
}

return size;
}

@Override
public void writeMat5(String name, Sink sink) throws IOException {
final List<String> fieldNames = getFieldNames();
final int numElements = getNumElements();
final int numFields = fieldNames.size();

// Common fields
writeMatrixTag(name, this, sink);
writeArrayHeader(name, this, sink);

// Subfield -/4: Object only. Not struct
if (getType() == MatlabType.Object) {
String objectClassName = getClassName();
Int8.writeBytesWithTag(objectClassName.getBytes(Charsets.US_ASCII), sink);
}

// Subfield 4/5: Field Name Length
int longestName = getLongestFieldName();
int numChars = longestName * numFields;
Int32.writeIntsWithTag(new int[]{longestName}, sink);

// Subfield 5/6: Field Names
byte[] ascii = new byte[numChars];
Arrays.fill(ascii, (byte) '\0');
for (int i = 0; i < numFields; i++) {
String fieldName = getFieldNames().get(i);
byte[] bytes = fieldName.getBytes(Charsets.US_ASCII);
System.arraycopy(bytes, 0, ascii, i * longestName, bytes.length);
}
Int8.writeBytesWithTag(ascii, sink);

// Subfield 6/7: Fields
checkArgument(getNumDimensions() == 2, "Structures are limited to two dimensions");
for (int i = 0; i < numElements; i++) {
for (int field = 0; field < numFields; field++) {
writeArrayWithTag(get(fieldNames.get(field), i), sink);
}
}

}

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,84 +1,84 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.Array;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Contains object data that can be referenced. Each object only
* represents a single instance, i.e., a reference array would
* contain multiple such objects. The property map forms automatically
* and in the same order as the properties were added.
*
* @author Florian Enner
* @since 06 Sep 2018
*/
class McosObject {

McosObject(String packageName, String className) {
this.packageName = packageName;
this.className = className;
}

String getPackageName() {
return packageName;
}

String getClassName() {
return className;
}

List<String> getFieldNames() {
return fieldNames;
}

void set(String name, Array value) {
if (properties.put(name, value) == null) {
fieldNames.add(name);
}
}

Array get(String name) {
return properties.get(name);
}

private final String packageName;
private final String className;
private final List<String> fieldNames = new ArrayList<String>(8);
private final Map<String, Array> properties = new HashMap<String, Array>(16);

final static McosObject EMPTY = new McosObject("", "") {
@Override
public void set(String name, Array value) {
throw new IllegalStateException("Can't set empty reference.");
}
};

@Override
public String toString() {
return "'" + getClassName() + "' class";
}
}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.Array;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Contains object data that can be referenced. Each object only
* represents a single instance, i.e., a reference array would
* contain multiple such objects. The property map forms automatically
* and in the same order as the properties were added.
*
* @author Florian Enner
* @since 06 Sep 2018
*/
class McosObject {

McosObject(String packageName, String className) {
this.packageName = packageName;
this.className = className;
}

String getPackageName() {
return packageName;
}

String getClassName() {
return className;
}

List<String> getFieldNames() {
return fieldNames;
}

void set(String name, Array value) {
if (properties.put(name, value) == null) {
fieldNames.add(name);
}
}

Array get(String name) {
return properties.get(name);
}

private final String packageName;
private final String className;
private final List<String> fieldNames = new ArrayList<String>(8);
private final Map<String, Array> properties = new HashMap<String, Array>(16);

final static McosObject EMPTY = new McosObject("", "") {
@Override
public void set(String name, Array value) {
throw new IllegalStateException("Can't set empty reference.");
}
};

@Override
public String toString() {
return "'" + getClassName() + "' class";
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,42 +1,42 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import java.util.ArrayList;
import java.util.List;

/**
* Keeps track of reference classes that require a backing object
*/
class McosRegistry {

synchronized McosReference register(McosReference reference) {
this.references.add(reference);
return reference;
}

List<McosReference> getReferences() {
return references;
}

private final List<McosReference> references = new ArrayList<McosReference>();

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import java.util.ArrayList;
import java.util.List;

/**
* Keeps track of reference classes that require a backing object
*/
class McosRegistry {

synchronized McosReference register(McosReference reference) {
this.references.add(reference);
return reference;
}

List<McosReference> getReferences() {
return references;
}

private final List<McosReference> references = new ArrayList<McosReference>();

}
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.Sink;

import java.io.Closeable;
import java.io.IOException;

/**
* Provides a way to store numerical data with different types and
* can be thought of as an array.
*
* @author Florian Enner
* @since 03 May 2018
*/
interface NumberStore extends Closeable {

int getNumElements();

long getLong(int index);

void setLong(int index, long value);

double getDouble(int index);

void setDouble(int index, double value);

int getMat5Size();

void writeMat5(Sink sink) throws IOException;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.format;

import us.hebi.matlab.mat.types.Sink;

import java.io.Closeable;
import java.io.IOException;

/**
* Provides a way to store numerical data with different types and
* can be thought of as an array.
*
* @author Florian Enner
* @since 03 May 2018
*/
interface NumberStore extends Closeable {

int getNumElements();

long getLong(int index);

void setLong(int index, long value);

double getDouble(int index);

void setDouble(int index, double value);

int getMat5Size();

void writeMat5(Sink sink) throws IOException;

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,132 +1,132 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import static us.hebi.matlab.common.util.Preconditions.*;

/**
* @author Florian Enner
* @since 02 May 2018
*/
public abstract class AbstractArray implements Array {

@Override
public int[] getDimensions() {
return dims;
}

@Override
public int getNumDimensions() {
return getDimensions().length;
}

@Override
public int getNumRows() {
return getDimensions()[0];
}

@Override
public int getNumCols() {
return getDimensions()[1];
}

@Override
public int getNumElements() {
return getNumElements(getDimensions());
}

@Override
public boolean isGlobal() {
return isGlobal;
}

@Override
public void setGlobal(boolean global) {
isGlobal = global;
}

/**
* @return cumulative product, i.e. number of elements given dimensions
*/
public static int getNumElements(int[] dimensions) {
int count = dimensions[0];
for (int i = 1; i < dimensions.length; i++) {
count *= dimensions[i];
}
return count;
}

protected static int[] calculateColMajorStrides(int[] dimensions) {
int[] dimStrides = new int[dimensions.length];
dimStrides[0] = 1;
for (int i = 0; i < dimStrides.length - 1; i++) {
dimStrides[i + 1] = dimensions[i] * dimStrides[i];
}
return dimStrides;
}

protected int getColumnMajorIndex(int row, int col) {
checkNumDimensions(2);
final int ix0 = checkIndexBounds(row, 0);
final int ix1 = checkIndexBounds(col, 1);
return ix0 * dimStrides[0] + ix1 * dimStrides[1];
}

protected int getColumnMajorIndex(int[] indices) {
checkNumDimensions(indices.length);
int index = 0;
for (int i = 0; i < indices.length; i++) {
index += dimStrides[i] * checkIndexBounds(indices[i], i);
}
return index;
}

protected void checkNumDimensions(int numDim) {
if (numDim != dimStrides.length)
throw new IllegalArgumentException("Expected " + dimStrides.length + " dimensions. Found: " + numDim);
}

protected int checkIndexBounds(int index, int dim) {
if (index >= 0 && index < dims[dim])
return index;
String msg = String.format("Index exceeds matrix dimension %d. %d/%d", dim, index, dims[dim] - 1);
throw new IllegalArgumentException(msg);
}

protected AbstractArray(int[] dims, boolean isGlobal) {
this.dims = checkNotNull(dims);
checkArgument(dims.length >= 2, "Every array must have at least two dimensions");
this.dimStrides = calculateColMajorStrides(dims);
}

@Override
public String toString() {
return StringHelper.toString(this);
}

// Common Variables
protected final int[] dims;
private boolean isGlobal;

// Internal state
private final int[] dimStrides;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import static us.hebi.matlab.mat.util.Preconditions.*;

/**
* @author Florian Enner
* @since 02 May 2018
*/
public abstract class AbstractArray implements Array {

@Override
public int[] getDimensions() {
return dims;
}

@Override
public int getNumDimensions() {
return getDimensions().length;
}

@Override
public int getNumRows() {
return getDimensions()[0];
}

@Override
public int getNumCols() {
return getDimensions()[1];
}

@Override
public int getNumElements() {
return getNumElements(getDimensions());
}

@Override
public boolean isGlobal() {
return isGlobal;
}

@Override
public void setGlobal(boolean global) {
isGlobal = global;
}

/**
* @return cumulative product, i.e. number of elements given dimensions
*/
public static int getNumElements(int[] dimensions) {
int count = dimensions[0];
for (int i = 1; i < dimensions.length; i++) {
count *= dimensions[i];
}
return count;
}

protected static int[] calculateColMajorStrides(int[] dimensions) {
int[] dimStrides = new int[dimensions.length];
dimStrides[0] = 1;
for (int i = 0; i < dimStrides.length - 1; i++) {
dimStrides[i + 1] = dimensions[i] * dimStrides[i];
}
return dimStrides;
}

protected int getColumnMajorIndex(int row, int col) {
checkNumDimensions(2);
final int ix0 = checkIndexBounds(row, 0);
final int ix1 = checkIndexBounds(col, 1);
return ix0 * dimStrides[0] + ix1 * dimStrides[1];
}

protected int getColumnMajorIndex(int[] indices) {
checkNumDimensions(indices.length);
int index = 0;
for (int i = 0; i < indices.length; i++) {
index += dimStrides[i] * checkIndexBounds(indices[i], i);
}
return index;
}

protected void checkNumDimensions(int numDim) {
if (numDim != dimStrides.length)
throw new IllegalArgumentException("Expected " + dimStrides.length + " dimensions. Found: " + numDim);
}

protected int checkIndexBounds(int index, int dim) {
if (index >= 0 && index < dims[dim])
return index;
String msg = String.format("Index exceeds matrix dimension %d. %d/%d", dim, index, dims[dim] - 1);
throw new IllegalArgumentException(msg);
}

protected AbstractArray(int[] dims, boolean isGlobal) {
this.dims = checkNotNull(dims);
checkArgument(dims.length >= 2, "Every array must have at least two dimensions");
this.dimStrides = calculateColMajorStrides(dims);
}

@Override
public String toString() {
return StringHelper.toString(this);
}

// Common Variables
protected final int[] dims;
private boolean isGlobal;

// Internal state
private final int[] dimStrides;

}
Original file line number Diff line number Diff line change
@@ -1,69 +1,69 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import java.io.IOException;
import java.util.Arrays;

import static us.hebi.matlab.common.util.Preconditions.*;

/**
* Cell array implementation.
* <p>
* Note that we don't need to check indices as the array access already
* takes care of that, i.e., throws an out of bounds exception.
*
* @author Florian Enner
* @since 07 Sep 2018
*/
public abstract class AbstractCell extends AbstractCellBase {

protected AbstractCell(int[] dims, boolean isGlobal, Array[] values) {
super(dims, isGlobal);
checkArgument(values.length == getNumElements(), "invalid length");
this.contents = values;
}

@Override
@SuppressWarnings("unchecked") // simplifies casting
public <T extends Array> T get(int index) {
return (T) contents[index];
}

@Override
public Cell set(int index, Array value) {
contents[index] = value;
return this;
}

protected abstract Array getEmptyValue();

@Override
public void close() throws IOException {
for (Array array : contents) {
array.close();
}
Arrays.fill(contents, getEmptyValue());
}

protected final Array[] contents;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import java.io.IOException;
import java.util.Arrays;

import static us.hebi.matlab.mat.util.Preconditions.*;

/**
* Cell array implementation.
* <p>
* Note that we don't need to check indices as the array access already
* takes care of that, i.e., throws an out of bounds exception.
*
* @author Florian Enner
* @since 07 Sep 2018
*/
public abstract class AbstractCell extends AbstractCellBase {

protected AbstractCell(int[] dims, boolean isGlobal, Array[] values) {
super(dims, isGlobal);
checkArgument(values.length == getNumElements(), "invalid length");
this.contents = values;
}

@Override
@SuppressWarnings("unchecked") // simplifies casting
public <T extends Array> T get(int index) {
return (T) contents[index];
}

@Override
public Cell set(int index, Array value) {
contents[index] = value;
return this;
}

protected abstract Array getEmptyValue();

@Override
public void close() throws IOException {
for (Array array : contents) {
array.close();
}
Arrays.fill(contents, getEmptyValue());
}

protected final Array[] contents;

}
Original file line number Diff line number Diff line change
@@ -1,147 +1,147 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* Forwards convenience overloads to a minimum required set
*
* @author Florian Enner
* @since 07 Sep 2018
*/
public abstract class AbstractCellBase extends AbstractArray implements Cell {

protected AbstractCellBase(int[] dims, boolean isGlobal) {
super(dims, isGlobal);
}

// ---- Actual Accessors

@Override
public abstract <T extends Array> T get(int index);

@Override
public abstract Cell set(int index, Array value);

// ---- Convenience overloads

@Override
public MatlabType getType() {
return MatlabType.Cell;
}

@Override
public Matrix getMatrix(int index) {
return get(index);
}

@Override
public Matrix getMatrix(int row, int col) {
return get(getColumnMajorIndex(row, col));
}

@Override
public Matrix getMatrix(int[] indices) {
return get(getColumnMajorIndex(indices));
}

@Override
public Sparse getSparse(int index) {
return get(index);
}

@Override
public Sparse getSparse(int row, int col) {
return get(getColumnMajorIndex(row, col));
}

@Override
public Sparse getSparse(int[] indices) {
return get(getColumnMajorIndex(indices));
}

@Override
public Char getChar(int index) {
return get(index);
}

@Override
public Char getChar(int row, int col) {
return get(getColumnMajorIndex(row, col));
}

@Override
public Char getChar(int[] indices) {
return get(getColumnMajorIndex(indices));
}

@Override
public Struct getStruct(int index) {
return get(index);
}

@Override
public Struct getStruct(int row, int col) {
return get(getColumnMajorIndex(row, col));
}

@Override
public Struct getStruct(int[] indices) {
return get(getColumnMajorIndex(indices));
}

@Override
public Cell getCell(int index) {
return get(index);
}

@Override
public Cell getCell(int row, int col) {
return get(getColumnMajorIndex(row, col));
}

@Override
public Cell getCell(int[] indices) {
return get(getColumnMajorIndex(indices));
}

@Override
public <T extends Array> T get(int row, int col) {
return get(getColumnMajorIndex(row, col));
}

@Override
public <T extends Array> T get(int[] indices) {
return get(getColumnMajorIndex(indices));
}

@Override
public Cell set(int row, int col, Array value) {
set(getColumnMajorIndex(row, col), value);
return this;
}

@Override
public Cell set(int[] indices, Array value) {
set(getColumnMajorIndex(indices), value);
return this;
}

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* Forwards convenience overloads to a minimum required set
*
* @author Florian Enner
* @since 07 Sep 2018
*/
public abstract class AbstractCellBase extends AbstractArray implements Cell {

protected AbstractCellBase(int[] dims, boolean isGlobal) {
super(dims, isGlobal);
}

// ---- Actual Accessors

@Override
public abstract <T extends Array> T get(int index);

@Override
public abstract Cell set(int index, Array value);

// ---- Convenience overloads

@Override
public MatlabType getType() {
return MatlabType.Cell;
}

@Override
public Matrix getMatrix(int index) {
return get(index);
}

@Override
public Matrix getMatrix(int row, int col) {
return get(getColumnMajorIndex(row, col));
}

@Override
public Matrix getMatrix(int[] indices) {
return get(getColumnMajorIndex(indices));
}

@Override
public Sparse getSparse(int index) {
return get(index);
}

@Override
public Sparse getSparse(int row, int col) {
return get(getColumnMajorIndex(row, col));
}

@Override
public Sparse getSparse(int[] indices) {
return get(getColumnMajorIndex(indices));
}

@Override
public Char getChar(int index) {
return get(index);
}

@Override
public Char getChar(int row, int col) {
return get(getColumnMajorIndex(row, col));
}

@Override
public Char getChar(int[] indices) {
return get(getColumnMajorIndex(indices));
}

@Override
public Struct getStruct(int index) {
return get(index);
}

@Override
public Struct getStruct(int row, int col) {
return get(getColumnMajorIndex(row, col));
}

@Override
public Struct getStruct(int[] indices) {
return get(getColumnMajorIndex(indices));
}

@Override
public Cell getCell(int index) {
return get(index);
}

@Override
public Cell getCell(int row, int col) {
return get(getColumnMajorIndex(row, col));
}

@Override
public Cell getCell(int[] indices) {
return get(getColumnMajorIndex(indices));
}

@Override
public <T extends Array> T get(int row, int col) {
return get(getColumnMajorIndex(row, col));
}

@Override
public <T extends Array> T get(int[] indices) {
return get(getColumnMajorIndex(indices));
}

@Override
public Cell set(int row, int col, Array value) {
set(getColumnMajorIndex(row, col), value);
return this;
}

@Override
public Cell set(int[] indices, Array value) {
set(getColumnMajorIndex(indices), value);
return this;
}

}
Original file line number Diff line number Diff line change
@@ -1,102 +1,102 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* @author Florian Enner
* @since 07 Sep 2018
*/
public abstract class AbstractCharBase extends AbstractArray implements Char {

protected AbstractCharBase(int[] dims, boolean isGlobal) {
super(dims, isGlobal);
}

// ---- To be implemented

@Override
public abstract CharSequence asCharSequence();

@Override
public abstract char getChar(int index);

@Override
public abstract void setChar(int index, char value);

// ---- Convenience accessors

@Override
public MatlabType getType() {
return MatlabType.Character;
}

@Override
public String getString() {
if (getNumRows() != 1)
throw new IllegalStateException("Char is not a single row char string");
return getRow(0);
}

@Override
public String getRow(int row) {
checkNumDimensions(2);
int numCols = getNumCols();

// thread local might be too large and use is likely single threaded
synchronized (builder) {
// Make sure there is enough space
builder.ensureCapacity(numCols);
builder.setLength(0);

// Read until end of string character
for (int col = 0; col < numCols; col++) {
char c = getChar(row, col);
if (c == '\0')
break;
builder.append(c);
}
return builder.toString();
}
}

@Override
public char getChar(int row, int col) {
return getChar(getColumnMajorIndex(row, col));
}

@Override
public char getChar(int[] indices) {
return getChar(getColumnMajorIndex(indices));
}

@Override
public void setChar(int row, int col, char value) {
setChar(getColumnMajorIndex(row, col), value);
}

@Override
public void setChar(int[] indices, char value) {
setChar(getColumnMajorIndex(indices), value);
}

protected final StringBuilder builder = new StringBuilder();

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* @author Florian Enner
* @since 07 Sep 2018
*/
public abstract class AbstractCharBase extends AbstractArray implements Char {

protected AbstractCharBase(int[] dims, boolean isGlobal) {
super(dims, isGlobal);
}

// ---- To be implemented

@Override
public abstract CharSequence asCharSequence();

@Override
public abstract char getChar(int index);

@Override
public abstract void setChar(int index, char value);

// ---- Convenience accessors

@Override
public MatlabType getType() {
return MatlabType.Character;
}

@Override
public String getString() {
if (getNumRows() != 1)
throw new IllegalStateException("Char is not a single row char string");
return getRow(0);
}

@Override
public String getRow(int row) {
checkNumDimensions(2);
int numCols = getNumCols();

// thread local might be too large and use is likely single threaded
synchronized (builder) {
// Make sure there is enough space
builder.ensureCapacity(numCols);
builder.setLength(0);

// Read until end of string character
for (int col = 0; col < numCols; col++) {
char c = getChar(row, col);
if (c == '\0')
break;
builder.append(c);
}
return builder.toString();
}
}

@Override
public char getChar(int row, int col) {
return getChar(getColumnMajorIndex(row, col));
}

@Override
public char getChar(int[] indices) {
return getChar(getColumnMajorIndex(indices));
}

@Override
public void setChar(int row, int col, char value) {
setChar(getColumnMajorIndex(row, col), value);
}

@Override
public void setChar(int[] indices, char value) {
setChar(getColumnMajorIndex(indices), value);
}

protected final StringBuilder builder = new StringBuilder();

}
Original file line number Diff line number Diff line change
@@ -1,109 +1,114 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

/**
* Basic implementation for keeping track of the contents of a MAT file
*
* @author Florian Enner
* @since 04 May 2018
*/
public abstract class AbstractMatFile extends AbstractMatFileBase {

@Override
@SuppressWarnings("unchecked")
public <T extends Array> T getArray(String name) {
// Fast lookup (case sensitive)
Array array = lookup.get(name);
if (array != null)
return (T) array;

// Slow fallback (not case sensitive)
for (NamedArray entry : entries) {
if (name.equalsIgnoreCase(entry.getName()))
return (T) entry.getValue();
}

throw new IllegalArgumentException("Could not find array: " + name);
}

@Override
@SuppressWarnings("unchecked")
public <T extends Array> T getArray(int index) {
return (T) entries.get(index).getValue();
}

@Override
public MatFile addArray(String name, Array value) {
return addArray(new NamedArray(name, value));
}

@Override
public MatFile addArray(NamedArray entry) {
entries.add(entry);
lookup.put(entry.getName(), entry.getValue());
return this;
}

@Override
public String toString() {
return StringHelper.toString(entries);
}

/**
* Closes all contained arrays, and throws an
* IOException if any of them failed to close.
*/
@Override
public void close() throws IOException {
IOException lastError = null;
for (NamedArray entry : entries) {
try {
entry.getValue().close();
} catch (IOException ioe) {
lastError = ioe;
}
}
entries.clear();
if (lastError != null)
throw lastError;
}

@Override
public Iterator<NamedArray> iterator() {
return entries.iterator();
}

@Override
public int size() {
return entries.size();
}

protected final HashMap<String, Array> lookup = new HashMap<String, Array>();
protected final List<NamedArray> entries = new ArrayList<NamedArray>();

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
* Basic implementation for keeping track of the contents of a MAT file
*
* @author Florian Enner
* @since 04 May 2018
*/
public abstract class AbstractMatFile extends AbstractMatFileBase {

@Override
@SuppressWarnings("unchecked")
public <T extends Array> T getArray(String name) {
// Fast lookup (case sensitive)
Array array = lookup.get(name);
if (array != null)
return (T) array;

// Slow fallback (not case sensitive)
for (NamedArray entry : entries) {
if (name.equalsIgnoreCase(entry.getName()))
return (T) entry.getValue();
}

throw new IllegalArgumentException("Could not find array: " + name);
}

@Override
@SuppressWarnings("unchecked")
public <T extends Array> T getArray(int index) {
return (T) entries.get(index).getValue();
}

@Override
public MatFile addArray(String name, Array value) {
return addArray(new NamedArray(name, value));
}

@Override
public MatFile addArray(NamedArray entry) {
entries.add(entry);
lookup.put(entry.getName(), entry.getValue());
return this;
}

@Override
public String toString() {
return StringHelper.toString(entries);
}

/**
* Closes all contained arrays, and throws an
* IOException if any of them failed to close.
*/
@Override
public void close() throws IOException {
IOException lastError = null;
for (NamedArray entry : entries) {
try {
entry.getValue().close();
} catch (IOException ioe) {
lastError = ioe;
}
}
clear();
if (lastError != null)
throw lastError;
}

@Override
public Iterable<NamedArray> getEntries() {
return entries;
}

@Override
public int getNumEntries() {
return entries.size();
}

@Override
public void clear() {
lookup.clear();
entries.clear();
}

protected final HashMap<String, Array> lookup = new HashMap<String, Array>();
protected final List<NamedArray> entries = new ArrayList<NamedArray>();

}
Original file line number Diff line number Diff line change
@@ -1,91 +1,91 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* Forwards convenience overloads to avoid cluttering the implementation
*
* @author Florian Enner
* @since 14 Sep 2018
*/
public abstract class AbstractMatFileBase implements MatFile {

@Override
public Matrix getMatrix(String name) {
return getArray(name);
}

@Override
public Sparse getSparse(String name) {
return getArray(name);
}

@Override
public Char getChar(String name) {
return getArray(name);
}

@Override
public Struct getStruct(String name) {
return getArray(name);
}

@Override
public ObjectStruct getObject(String name) {
return getArray(name);
}

@Override
public Cell getCell(String name) {
return getArray(name);
}

@Override
public Matrix getMatrix(int index) {
return getArray(index);
}

@Override
public Sparse getSparse(int index) {
return getArray(index);
}

@Override
public Char getChar(int index) {
return getArray(index);
}

@Override
public Struct getStruct(int index) {
return getArray(index);
}

@Override
public ObjectStruct getObject(int index) {
return getArray(index);
}

@Override
public Cell getCell(int index) {
return getArray(index);
}

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* Forwards convenience overloads to avoid cluttering the implementation
*
* @author Florian Enner
* @since 14 Sep 2018
*/
public abstract class AbstractMatFileBase implements MatFile {

@Override
public Matrix getMatrix(String name) {
return getArray(name);
}

@Override
public Sparse getSparse(String name) {
return getArray(name);
}

@Override
public Char getChar(String name) {
return getArray(name);
}

@Override
public Struct getStruct(String name) {
return getArray(name);
}

@Override
public ObjectStruct getObject(String name) {
return getArray(name);
}

@Override
public Cell getCell(String name) {
return getArray(name);
}

@Override
public Matrix getMatrix(int index) {
return getArray(index);
}

@Override
public Sparse getSparse(int index) {
return getArray(index);
}

@Override
public Char getChar(int index) {
return getArray(index);
}

@Override
public Struct getStruct(int index) {
return getArray(index);
}

@Override
public ObjectStruct getObject(int index) {
return getArray(index);
}

@Override
public Cell getCell(int index) {
return getArray(index);
}

}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,197 +1,197 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import us.hebi.matlab.common.memory.ByteConverter;
import us.hebi.matlab.common.memory.ByteConverters;
import us.hebi.matlab.common.memory.Bytes;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

import static us.hebi.matlab.common.memory.Bytes.*;

/**
* @author Florian Enner
* @since 26 Aug 2018
*/
public abstract class AbstractSource implements Source {

@Override
public AbstractSource order(ByteOrder byteOrder) {
this.byteOrder = byteOrder;
return this;
}

@Override
public ByteOrder order() {
if (byteOrder == null) {
throw new IllegalStateException("Byte order has not been initialized");
}
return byteOrder;
}

@Override
public byte readByte() throws IOException {
readBytes(bytes, 0, 1);
return bytes[0];
}

@Override
public short readShort() throws IOException {
readBytes(bytes, 0, SIZEOF_SHORT);
return byteConverter.getShort(order(), bytes, 0);
}

@Override
public int readInt() throws IOException {
readBytes(bytes, 0, SIZEOF_INT);
return byteConverter.getInt(order(), bytes, 0);
}

@Override
public long readLong() throws IOException {
readBytes(bytes, 0, SIZEOF_LONG);
return byteConverter.getLong(order(), bytes, 0);
}

@Override
public float readFloat() throws IOException {
readBytes(bytes, 0, SIZEOF_FLOAT);
return byteConverter.getFloat(order(), bytes, 0);
}

@Override
public double readDouble() throws IOException {
readBytes(bytes, 0, SIZEOF_DOUBLE);
return byteConverter.getDouble(order(), bytes, 0);
}

@Override
public void readByteBuffer(ByteBuffer buffer) throws IOException {
if (buffer.hasArray()) {
// Fast path
int offset = buffer.arrayOffset() + buffer.position();
int length = buffer.remaining();
readBytes(buffer.array(), offset, length);
buffer.position(buffer.limit());
} else {
// Slow path
while (buffer.hasRemaining()) {
int length = Math.min(buffer.remaining(), bytes.length);
readBytes(bytes, 0, length);
buffer.put(bytes, 0, length);
}
}
}

@Override
public void readShorts(short[] buffer, int offset, int length) throws IOException {
for (int i = 0; i < length; ) {
int n = Math.min((length - i) * SIZEOF_SHORT, bytes.length);
readBytes(bytes, 0, n);
for (int j = 0; j < n; j += SIZEOF_SHORT, i++) {
buffer[offset + i] = byteConverter.getShort(order(), bytes, j);
}
}
}

@Override
public void readInts(int[] buffer, int offset, int length) throws IOException {
for (int i = 0; i < length; ) {
int n = Math.min((length - i) * SIZEOF_INT, bytes.length);
readBytes(bytes, 0, n);
for (int j = 0; j < n; j += SIZEOF_INT, i++) {
buffer[offset + i] = byteConverter.getInt(order(), bytes, j);
}
}
}

@Override
public void readLongs(long[] buffer, int offset, int length) throws IOException {
for (int i = 0; i < length; ) {
int n = Math.min((length - i) * SIZEOF_LONG, bytes.length);
readBytes(bytes, 0, n);
for (int j = 0; j < n; j += SIZEOF_LONG, i++) {
buffer[offset + i] = byteConverter.getLong(order(), bytes, j);
}
}
}

@Override
public void readFloats(float[] buffer, int offset, int length) throws IOException {
for (int i = 0; i < length; ) {
int n = Math.min((length - i) * SIZEOF_FLOAT, bytes.length);
readBytes(bytes, 0, n);
for (int j = 0; j < n; j += SIZEOF_FLOAT, i++) {
buffer[offset + i] = byteConverter.getFloat(order(), bytes, j);
}
}
}

@Override
public void readDoubles(double[] buffer, int offset, int length) throws IOException {
for (int i = 0; i < length; ) {
int n = Math.min((length - i) * SIZEOF_DOUBLE, bytes.length);
readBytes(bytes, 0, n);
for (int j = 0; j < n; j += SIZEOF_DOUBLE, i++) {
buffer[offset + i] = byteConverter.getDouble(order(), bytes, j);
}
}
}

@Override
public void skip(long numBytes) throws IOException {
long n = 0;
while (n < numBytes) {
long count = Math.min(numBytes - n, bytes.length);
readBytes(bytes, 0, (int) count);
n += count;
}
}

@Override
public Source readInflated(int numBytes, int inflateBufferSize) throws IOException {
InputStream subInputStream = readBytesAsStream(numBytes);
InputStream inflaterInput = new InflaterInputStream(subInputStream, new Inflater(), inflateBufferSize);
return Sources.wrapInputStream(inflaterInput, bytes.length).order(order());
}

/**
* @return stream that reads up to the number of specified bytes. Close() shall not close this source
*/
protected abstract InputStream readBytesAsStream(long numBytes) throws IOException;

protected AbstractSource(int bufferSize) {
// Make sure size is always a multiple of 8, and that it can hold the 116 byte description
int size = Math.max(Bytes.nextPowerOfTwo(bufferSize), 128);
this.bytes = new byte[size];
}

private ByteOrder byteOrder = null;
private final byte[] bytes;
private static final ByteConverter byteConverter = ByteConverters.getFastest();

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import us.hebi.matlab.mat.util.ByteConverter;
import us.hebi.matlab.mat.util.ByteConverters;
import us.hebi.matlab.mat.util.Bytes;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

import static us.hebi.matlab.mat.util.Bytes.*;

/**
* @author Florian Enner
* @since 26 Aug 2018
*/
public abstract class AbstractSource implements Source {

@Override
public AbstractSource order(ByteOrder byteOrder) {
this.byteOrder = byteOrder;
return this;
}

@Override
public ByteOrder order() {
if (byteOrder == null) {
throw new IllegalStateException("Byte order has not been initialized");
}
return byteOrder;
}

@Override
public byte readByte() throws IOException {
readBytes(bytes, 0, 1);
return bytes[0];
}

@Override
public short readShort() throws IOException {
readBytes(bytes, 0, SIZEOF_SHORT);
return byteConverter.getShort(order(), bytes, 0);
}

@Override
public int readInt() throws IOException {
readBytes(bytes, 0, SIZEOF_INT);
return byteConverter.getInt(order(), bytes, 0);
}

@Override
public long readLong() throws IOException {
readBytes(bytes, 0, SIZEOF_LONG);
return byteConverter.getLong(order(), bytes, 0);
}

@Override
public float readFloat() throws IOException {
readBytes(bytes, 0, SIZEOF_FLOAT);
return byteConverter.getFloat(order(), bytes, 0);
}

@Override
public double readDouble() throws IOException {
readBytes(bytes, 0, SIZEOF_DOUBLE);
return byteConverter.getDouble(order(), bytes, 0);
}

@Override
public void readByteBuffer(ByteBuffer buffer) throws IOException {
if (buffer.hasArray()) {
// Fast path
int offset = buffer.arrayOffset() + buffer.position();
int length = buffer.remaining();
readBytes(buffer.array(), offset, length);
buffer.position(buffer.limit());
} else {
// Slow path
while (buffer.hasRemaining()) {
int length = Math.min(buffer.remaining(), bytes.length);
readBytes(bytes, 0, length);
buffer.put(bytes, 0, length);
}
}
}

@Override
public void readShorts(short[] buffer, int offset, int length) throws IOException {
for (int i = 0; i < length; ) {
int n = Math.min((length - i) * SIZEOF_SHORT, bytes.length);
readBytes(bytes, 0, n);
for (int j = 0; j < n; j += SIZEOF_SHORT, i++) {
buffer[offset + i] = byteConverter.getShort(order(), bytes, j);
}
}
}

@Override
public void readInts(int[] buffer, int offset, int length) throws IOException {
for (int i = 0; i < length; ) {
int n = Math.min((length - i) * SIZEOF_INT, bytes.length);
readBytes(bytes, 0, n);
for (int j = 0; j < n; j += SIZEOF_INT, i++) {
buffer[offset + i] = byteConverter.getInt(order(), bytes, j);
}
}
}

@Override
public void readLongs(long[] buffer, int offset, int length) throws IOException {
for (int i = 0; i < length; ) {
int n = Math.min((length - i) * SIZEOF_LONG, bytes.length);
readBytes(bytes, 0, n);
for (int j = 0; j < n; j += SIZEOF_LONG, i++) {
buffer[offset + i] = byteConverter.getLong(order(), bytes, j);
}
}
}

@Override
public void readFloats(float[] buffer, int offset, int length) throws IOException {
for (int i = 0; i < length; ) {
int n = Math.min((length - i) * SIZEOF_FLOAT, bytes.length);
readBytes(bytes, 0, n);
for (int j = 0; j < n; j += SIZEOF_FLOAT, i++) {
buffer[offset + i] = byteConverter.getFloat(order(), bytes, j);
}
}
}

@Override
public void readDoubles(double[] buffer, int offset, int length) throws IOException {
for (int i = 0; i < length; ) {
int n = Math.min((length - i) * SIZEOF_DOUBLE, bytes.length);
readBytes(bytes, 0, n);
for (int j = 0; j < n; j += SIZEOF_DOUBLE, i++) {
buffer[offset + i] = byteConverter.getDouble(order(), bytes, j);
}
}
}

@Override
public void skip(long numBytes) throws IOException {
long n = 0;
while (n < numBytes) {
long count = Math.min(numBytes - n, bytes.length);
readBytes(bytes, 0, (int) count);
n += count;
}
}

@Override
public Source readInflated(int numBytes, int inflateBufferSize) throws IOException {
InputStream subInputStream = readBytesAsStream(numBytes);
InputStream inflaterInput = new InflaterInputStream(subInputStream, new Inflater(), inflateBufferSize);
return Sources.wrapInputStream(inflaterInput, bytes.length).order(order());
}

/**
* @return stream that reads up to the number of specified bytes. Close() shall not close this source
*/
protected abstract InputStream readBytesAsStream(long numBytes) throws IOException;

protected AbstractSource(int bufferSize) {
// Make sure size is always a multiple of 8, and that it can hold the 116 byte description
int size = Math.max(Bytes.nextPowerOfTwo(bufferSize), 128);
this.bytes = new byte[size];
}

private ByteOrder byteOrder = null;
private final byte[] bytes;
private static final ByteConverter byteConverter = ByteConverters.getFastest();

}
Original file line number Diff line number Diff line change
@@ -1,72 +1,72 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* @author Florian Enner
* @since 07 Sep 2018
*/
public abstract class AbstractSparse extends AbstractMatrixBase implements Sparse {

protected AbstractSparse(int[] dims, boolean isGlobal) {
super(dims, isGlobal);
}

@Override
public MatlabType getType() {
return MatlabType.Sparse;
}

// --- Remove integer accessors as Sparse are always double matrices

@Override
public long getLong(int index) {
return (long) getDouble(index);
}

@Override
public void setLong(int index, long value) {
setDouble(index, value);
}

@Override
public long getImaginaryLong(int index) {
return (long) getImaginaryDouble(index);
}

@Override
public void setImaginaryLong(int index, long value) {
setImaginaryDouble(index, value);
}

@Override
public double getDefaultValue() {
return defaultValue;
}

@Override
public void setDefaultValue(double value) {
this.defaultValue = value;
}

protected double defaultValue = 0;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* @author Florian Enner
* @since 07 Sep 2018
*/
public abstract class AbstractSparse extends AbstractMatrixBase implements Sparse {

protected AbstractSparse(int[] dims, boolean isGlobal) {
super(dims, isGlobal);
}

@Override
public MatlabType getType() {
return MatlabType.Sparse;
}

// --- Remove integer accessors as Sparse are always double matrices

@Override
public long getLong(int index) {
return (long) getDouble(index);
}

@Override
public void setLong(int index, long value) {
setDouble(index, value);
}

@Override
public long getImaginaryLong(int index) {
return (long) getImaginaryDouble(index);
}

@Override
public void setImaginaryLong(int index, long value) {
setImaginaryDouble(index, value);
}

@Override
public double getDefaultValue() {
return defaultValue;
}

@Override
public void setDefaultValue(double value) {
this.defaultValue = value;
}

protected double defaultValue = 0;

}
Original file line number Diff line number Diff line change
@@ -1,114 +1,114 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
* Struct implementation for mapping fields and indices.
* <p>
* Note that we don't need to check indices as the array access already
* takes care of that, i.e., throws an out of bounds exception.
*
* @author Florian Enner
* @since 07 Sep 2018
*/
public abstract class AbstractStruct extends AbstractStructBase {

protected AbstractStruct(int[] dims, boolean isGlobal) {
super(dims, isGlobal);
}

@Override
public List<String> getFieldNames() {
return fields;
}

@Override
public Array[] remove(String field) {
Integer fieldIndex = indexMap.remove(checkNonEmpty(field));
if (fieldIndex == null)
throw new IllegalArgumentException("A field named '" + field + "' doesn't exist.");
fields.remove((int) fieldIndex);
return values.remove((int) fieldIndex);
}

@Override
@SuppressWarnings("unchecked") // simplifies casting
public <T extends Array> T get(String field, int index) {
Integer fieldIndex = indexMap.get(checkNonEmpty(field));
if (fieldIndex == null)
throw new IllegalArgumentException("Reference to non-existent field '" + field + "'");
return (T) values.get(fieldIndex)[index];
}

@Override
public Struct set(String field, int index, Array value) {
getOrInitValues(field)[index] = value;
return this;
}

protected abstract Array getEmptyValue();

protected Array[] getOrInitValues(String field) {
// Field exists
Integer fieldIndex = indexMap.get(field);
if (fieldIndex != null)
return values.get(fieldIndex);

// Field needs to be initialized (starts as empty)
Array[] value = new Array[getNumElements()];
Arrays.fill(value, getEmptyValue());

// Add to map
indexMap.put(field, values.size());
fields.add(field);
values.add(value);
return value;
}

protected static String checkNonEmpty(String field) {
if (field == null || field.isEmpty())
throw new IllegalArgumentException("Field name can't be empty.");
return field;
}

@Override
public void close() throws IOException {
for (Array[] value : values) {
for (Array array : value) {
array.close();
}
}
indexMap.clear();
fields.clear();
values.clear();
}

private final HashMap<String, Integer> indexMap = new HashMap<String, Integer>();
private final List<String> fields = new ArrayList<String>();
private final List<Array[]> values = new ArrayList<Array[]>();

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
* Struct implementation for mapping fields and indices.
* <p>
* Note that we don't need to check indices as the array access already
* takes care of that, i.e., throws an out of bounds exception.
*
* @author Florian Enner
* @since 07 Sep 2018
*/
public abstract class AbstractStruct extends AbstractStructBase {

protected AbstractStruct(int[] dims, boolean isGlobal) {
super(dims, isGlobal);
}

@Override
public List<String> getFieldNames() {
return fields;
}

@Override
public Array[] remove(String field) {
Integer fieldIndex = indexMap.remove(checkNonEmpty(field));
if (fieldIndex == null)
throw new IllegalArgumentException("A field named '" + field + "' doesn't exist.");
fields.remove((int) fieldIndex);
return values.remove((int) fieldIndex);
}

@Override
@SuppressWarnings("unchecked") // simplifies casting
public <T extends Array> T get(String field, int index) {
Integer fieldIndex = indexMap.get(checkNonEmpty(field));
if (fieldIndex == null)
throw new IllegalArgumentException("Reference to non-existent field '" + field + "'");
return (T) values.get(fieldIndex)[index];
}

@Override
public Struct set(String field, int index, Array value) {
getOrInitValues(field)[index] = value;
return this;
}

protected abstract Array getEmptyValue();

protected Array[] getOrInitValues(String field) {
// Field exists
Integer fieldIndex = indexMap.get(field);
if (fieldIndex != null)
return values.get(fieldIndex);

// Field needs to be initialized (starts as empty)
Array[] value = new Array[getNumElements()];
Arrays.fill(value, getEmptyValue());

// Add to map
indexMap.put(field, values.size());
fields.add(field);
values.add(value);
return value;
}

protected static String checkNonEmpty(String field) {
if (field == null || field.isEmpty())
throw new IllegalArgumentException("Field name can't be empty.");
return field;
}

@Override
public void close() throws IOException {
for (Array[] value : values) {
for (Array array : value) {
array.close();
}
}
indexMap.clear();
fields.clear();
values.clear();
}

private final HashMap<String, Integer> indexMap = new HashMap<String, Integer>();
private final List<String> fields = new ArrayList<String>();
private final List<Array[]> values = new ArrayList<Array[]>();

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,55 +1,55 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import java.io.Closeable;

/**
* Represents the 'miMATRIX' type that is used to represent
* all kinds of named variables inside a MAT file. This includes
* N-dimensional numerical arrays as well as aggregate types such
* as structs or cell arrays.
* <p>
* Note that all MATLAB variables are implemented as N-dimensional arrays,
* and that singular values or structs are arrays with 1 row and 1 column.
*
* @author Florian Enner
* @since 02 May 2018
*/
public interface Array extends Closeable {

MatlabType getType();

int[] getDimensions();

int getNumDimensions();

int getNumRows();

int getNumCols();

int getNumElements();

boolean isGlobal();

void setGlobal(boolean global);

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import java.io.Closeable;

/**
* Represents the 'miMATRIX' type that is used to represent
* all kinds of named variables inside a MAT file. This includes
* N-dimensional numerical arrays as well as aggregate types such
* as structs or cell arrays.
* <p>
* Note that all MATLAB variables are implemented as N-dimensional arrays,
* and that singular values or structs are arrays with 1 row and 1 column.
*
* @author Florian Enner
* @since 02 May 2018
*/
public interface Array extends Closeable {

MatlabType getType();

int[] getDimensions();

int getNumDimensions();

int getNumRows();

int getNumCols();

int getNumElements();

boolean isGlobal();

void setGlobal(boolean global);

}
Original file line number Diff line number Diff line change
@@ -1,76 +1,76 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* Represents MATLAB's Cell array, e.g., 'x = cell(2,2,2)'
* Behavior:
* - Contains other Arrays
* - Can be N-dimensional
*
* @author Florian Enner
* @since 06 Sep 2018
*/
public interface Cell extends Array {

Matrix getMatrix(int index);

Matrix getMatrix(int row, int col);

Matrix getMatrix(int[] indices);

Sparse getSparse(int index);

Sparse getSparse(int row, int col);

Sparse getSparse(int[] indices);

Char getChar(int index);

Char getChar(int row, int col);

Char getChar(int[] indices);

Struct getStruct(int index);

Struct getStruct(int row, int col);

Struct getStruct(int[] indices);

Cell getCell(int index);

Cell getCell(int row, int col);

Cell getCell(int[] indices);

<T extends Array> T get(int index);

<T extends Array> T get(int row, int col);

<T extends Array> T get(int[] indices);

Cell set(int index, Array value);

Cell set(int row, int col, Array value);

Cell set(int[] indices, Array value);

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* Represents MATLAB's Cell array, e.g., 'x = cell(2,2,2)'
* Behavior:
* - Contains other Arrays
* - Can be N-dimensional
*
* @author Florian Enner
* @since 06 Sep 2018
*/
public interface Cell extends Array {

Matrix getMatrix(int index);

Matrix getMatrix(int row, int col);

Matrix getMatrix(int[] indices);

Sparse getSparse(int index);

Sparse getSparse(int row, int col);

Sparse getSparse(int[] indices);

Char getChar(int index);

Char getChar(int row, int col);

Char getChar(int[] indices);

Struct getStruct(int index);

Struct getStruct(int row, int col);

Struct getStruct(int[] indices);

Cell getCell(int index);

Cell getCell(int row, int col);

Cell getCell(int[] indices);

<T extends Array> T get(int index);

<T extends Array> T get(int row, int col);

<T extends Array> T get(int[] indices);

Cell set(int index, Array value);

Cell set(int row, int col, Array value);

Cell set(int[] indices, Array value);

}
Original file line number Diff line number Diff line change
@@ -1,52 +1,52 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* Represents MATLAB's 'char' type
*
* @author Florian Enner
* @since 06 Sep 2018
*/
public interface Char extends Array {

// Entire buffer in column major order
CharSequence asCharSequence();

// Returns first row as string if numRows is 1
String getString();

// Row as column string for 2D arrays
String getRow(int row);

char getChar(int index);

char getChar(int row, int col);

char getChar(int[] indices);

void setChar(int index, char value);

void setChar(int row, int col, char value);

void setChar(int[] indices, char value);

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* Represents MATLAB's 'char' type
*
* @author Florian Enner
* @since 06 Sep 2018
*/
public interface Char extends Array {

// Entire buffer in column major order
CharSequence asCharSequence();

// Returns first row as string if numRows is 1
String getString();

// Row as column string for 2D arrays
String getRow(int row);

char getChar(int index);

char getChar(int row, int col);

char getChar(int[] indices);

void setChar(int index, char value);

void setChar(int row, int col, char value);

void setChar(int[] indices, char value);

}
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* @author Florian Enner
* @since 02 Sep 2018
*/
public interface FunctionHandle extends Array {

Struct getContent();

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* @author Florian Enner
* @since 02 Sep 2018
*/
public interface FunctionHandle extends Array {

Struct getContent();

}
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* Wraps the content of a serialized Java object.
*
* @author Florian Enner
* @since 01 Sep 2018
*/
public interface JavaObject extends Opaque {

/**
* @return instantiates Java object using deserialization
*/
Object instantiateObject() throws Exception;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* Wraps the content of a serialized Java object.
*
* @author Florian Enner
* @since 01 Sep 2018
*/
public interface JavaObject extends Opaque {

/**
* @return instantiates Java object using deserialization
*/
Object instantiateObject() throws Exception;

}
Original file line number Diff line number Diff line change
@@ -1,105 +1,111 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import java.io.Closeable;
import java.io.IOException;
import java.util.Iterator;

/**
* @author Florian Enner
* @since 14 Sep 2018
*/
public interface MatFile extends Closeable, Iterable<NamedArray> {

Matrix getMatrix(String name);

Sparse getSparse(String name);

Char getChar(String name);

Struct getStruct(String name);

ObjectStruct getObject(String name);

Cell getCell(String name);

Matrix getMatrix(int index);

Sparse getSparse(int index);

Char getChar(int index);

Struct getStruct(int index);

ObjectStruct getObject(int index);

Cell getCell(int index);

// Unchecked casting to simplify casts to uncommon types
<T extends Array> T getArray(String name);

// Unchecked casting to simplify casts to uncommon types
<T extends Array> T getArray(int index);

MatFile addArray(String name, Array value);

MatFile addArray(NamedArray entry);

/**
* @return number of contained arrays
*/
int size();

@Override
Iterator<NamedArray> iterator();

/**
* Closes all contained arrays
*/
@Override
void close() throws IOException;

/**
* Computes the resulting file size if compression is disabled. Since
* compression usually reduces the file size, this can be seen as a
* maximum expected size.
* <p>
* This is useful to e.g. pre-allocate a buffer or file that can be
* truncated once the actual file size is known.
* <p>
* Note that it is not guaranteed that compression will result in a
* smaller file size, e.g., we have seen this happen on small arrays
* with little data. Thus, for small arrays, you should add padding.
*
* @return serialized size in bytes including header
*/
long getUncompressedSerializedSize();

/**
* Serializes this mat file including header and content to
* the specified sink. The data may be compressed on the way.
*
* @return this
*/
MatFile writeTo(Sink sink) throws IOException;

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

import java.io.Closeable;
import java.io.IOException;

/**
* @author Florian Enner
* @since 14 Sep 2018
*/
public interface MatFile extends Closeable {

Matrix getMatrix(String name);

Sparse getSparse(String name);

Char getChar(String name);

Struct getStruct(String name);

ObjectStruct getObject(String name);

Cell getCell(String name);

Matrix getMatrix(int index);

Sparse getSparse(int index);

Char getChar(int index);

Struct getStruct(int index);

ObjectStruct getObject(int index);

Cell getCell(int index);

// Unchecked casting to simplify casts to uncommon types
<T extends Array> T getArray(String name);

// Unchecked casting to simplify casts to uncommon types
<T extends Array> T getArray(int index);

MatFile addArray(String name, Array value);

MatFile addArray(NamedArray entry);

/**
* @return number of arrays at the root level
*/
int getNumEntries();

/**
* @return iterable of named arrays at the root level
*/
Iterable<NamedArray> getEntries();

/**
* Clears the contained entries (without closing them)
*/
void clear();

/**
* Closes all contained arrays
*/
@Override
void close() throws IOException;

/**
* Computes the resulting file size if compression is disabled. Since
* compression usually reduces the file size, this can be seen as a
* maximum expected size.
* <p>
* This is useful to e.g. pre-allocate a buffer or file that can be
* truncated once the actual file size is known.
* <p>
* Note that it is not guaranteed that compression will result in a
* smaller file size, e.g., we have seen this happen on small arrays
* with little data. Thus, for small arrays, you should add padding.
*
* @return serialized size in bytes including header
*/
long getUncompressedSerializedSize();

/**
* Serializes this mat file including header and content to
* the specified sink. The data may be compressed on the way.
*
* @return this
*/
MatFile writeTo(Sink sink) throws IOException;

}
Original file line number Diff line number Diff line change
@@ -1,93 +1,93 @@
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* Represents the user-facing class type that MATLAB would display the data as
*/
public enum MatlabType {

Cell(1, "cell"), // mxCELL_CLASS
Structure(2, "struct"),
Object(3, "object"),
Character(4, "char"),
Sparse(5, "sparse"),
Double(6, "double"),
Single(7, "single"),
Int8(8, "int8"),
UInt8(9, "uint8"),
Int16(10, "int16"),
UInt16(11, "uint16"),
Int32(12, "int32"),
UInt32(13, "uint32"),
Int64(14, "int64"),
UInt64(15, "uint64"),

// Undocumented classes
Function(16, "function_handle"), // function handles
Opaque(17, "opaque"); // e.g. MCOS, tables, java types, etc.

@Override
public String toString() {
return name;
}

public static MatlabType fromId(int id) {
if (id > 0 && id < lookup.length) {
MatlabType type = lookup[id];
if (type != null)
return type;
}
throw new IllegalArgumentException("Unknown array type for id: " + id);
}

/**
* @return MAT 5 id
*/
public byte id() {
return id;
}

MatlabType(int id, String name) {
this.id = (byte) id;
this.name = name;
}

private final byte id;
private final String name;

private static final MatlabType[] lookup;

static {
// Determine size of lookup table
int highestId = 0;
for (MatlabType type : values()) {
highestId = Math.max(highestId, type.id);
}

// Populate lookup table
lookup = new MatlabType[highestId + 1];
for (MatlabType type : values()) {
lookup[type.id()] = type;
}
}

}
/*-
* #%L
* Mat-File IO
* %%
* Copyright (C) 2018 HEBI Robotics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

package us.hebi.matlab.mat.types;

/**
* Represents the user-facing class type that MATLAB would display the data as
*/
public enum MatlabType {

Cell(1, "cell"), // mxCELL_CLASS
Structure(2, "struct"),
Object(3, "object"),
Character(4, "char"),
Sparse(5, "sparse"),
Double(6, "double"),
Single(7, "single"),
Int8(8, "int8"),
UInt8(9, "uint8"),
Int16(10, "int16"),
UInt16(11, "uint16"),
Int32(12, "int32"),
UInt32(13, "uint32"),
Int64(14, "int64"),
UInt64(15, "uint64"),

// Undocumented classes
Function(16, "function_handle"), // function handles
Opaque(17, "opaque"); // e.g. MCOS, tables, java types, etc.

@Override
public String toString() {
return name;
}

public static MatlabType fromId(int id) {
if (id > 0 && id < lookup.length) {
MatlabType type = lookup[id];
if (type != null)
return type;
}
throw new IllegalArgumentException("Unknown array type for id: " + id);
}

/**
* @return MAT 5 id
*/
public byte id() {
return id;
}

MatlabType(int id, String name) {
this.id = (byte) id;
this.name = name;
}

private final byte id;
private final String name;

private static final MatlabType[] lookup;

static {
// Determine size of lookup table
int highestId = 0;
for (MatlabType type : values()) {
highestId = Math.max(highestId, type.id);
}

// Populate lookup table
lookup = new MatlabType[highestId + 1];
for (MatlabType type : values()) {
lookup[type.id()] = type;
}
}

}
147 changes: 147 additions & 0 deletions src/main/java/us/hebi/matlab/mat/util/Unsafe9R.java
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.