boblsturm@0
|
1 function Hash = DataHash(Data, Opt)
|
boblsturm@0
|
2 % DATAHASH - Checksum for Matlab array of any type
|
boblsturm@0
|
3 % This function creates a hash value for an input of any type. The type and
|
boblsturm@0
|
4 % dimensions of the input are considered as default, such that UINT8([0,0]) and
|
boblsturm@0
|
5 % UINT16(0) have different hash values. Nested STRUCTs and CELLs are parsed
|
boblsturm@0
|
6 % recursively.
|
boblsturm@0
|
7 %
|
boblsturm@0
|
8 % Hash = DataHash(Data, Opt)
|
boblsturm@0
|
9 % INPUT:
|
boblsturm@0
|
10 % Data: Array of these built-in types:
|
boblsturm@0
|
11 % (U)INT8/16/32/64, SINGLE, DOUBLE, (real/complex, full/sparse)
|
boblsturm@0
|
12 % CHAR, LOGICAL, CELL (nested), STRUCT (scalar or array, nested),
|
boblsturm@0
|
13 % function_handle.
|
boblsturm@0
|
14 % Opt: Struct to specify the hashing algorithm and the output format.
|
boblsturm@0
|
15 % Opt and all its fields are optional.
|
boblsturm@0
|
16 % Opt.Method: String, known methods for Java 1.6 (Matlab 2011b):
|
boblsturm@0
|
17 % 'SHA-1', 'SHA-256', 'SHA-384', 'SHA-512', 'MD2', 'MD5'.
|
boblsturm@0
|
18 % Call DataHash without inputs to get a list of available methods.
|
boblsturm@0
|
19 % Default: 'MD5'.
|
boblsturm@0
|
20 % Opt.Format: String specifying the output format:
|
boblsturm@0
|
21 % 'hex', 'HEX': Lower/uppercase hexadecimal string.
|
boblsturm@0
|
22 % 'double', 'uint8': Numerical vector.
|
boblsturm@0
|
23 % 'base64': Base64 encoded string, only printable ASCII
|
boblsturm@0
|
24 % characters, shorter than 'hex', no padding.
|
boblsturm@0
|
25 % Default: 'hex'.
|
boblsturm@0
|
26 % Opt.Input: Type of the input as string, not case-sensitive:
|
boblsturm@0
|
27 % 'array': The contents, type and size of the input [Data] are
|
boblsturm@0
|
28 % considered for the creation of the hash. Nested CELLs
|
boblsturm@0
|
29 % and STRUCT arrays are parsed recursively. Empty arrays of
|
boblsturm@0
|
30 % different type reply different hashs.
|
boblsturm@0
|
31 % 'file': [Data] is treated as file name and the hash is calculated
|
boblsturm@0
|
32 % for the files contents.
|
boblsturm@0
|
33 % 'bin': [Data] is a numerical, LOGICAL or CHAR array. Only the
|
boblsturm@0
|
34 % binary contents of the array is considered, such that
|
boblsturm@0
|
35 % e.g. empty arrays of different type reply the same hash.
|
boblsturm@0
|
36 % 'ascii': Same as 'bin', but only the 8-bit ASCII part of the 16-bit
|
boblsturm@0
|
37 % Matlab CHARs is considered.
|
boblsturm@0
|
38 % Default: 'array'.
|
boblsturm@0
|
39 %
|
boblsturm@0
|
40 % OUTPUT:
|
boblsturm@0
|
41 % Hash: String, DOUBLE or UINT8 vector. The length depends on the hashing
|
boblsturm@0
|
42 % method.
|
boblsturm@0
|
43 %
|
boblsturm@0
|
44 % EXAMPLES:
|
boblsturm@0
|
45 % % Default: MD5, hex:
|
boblsturm@0
|
46 % DataHash([]) % 5b302b7b2099a97ba2a276640a192485
|
boblsturm@0
|
47 % % MD5, Base64:
|
boblsturm@0
|
48 % Opt = struct('Format', 'base64', 'Method', 'MD5');
|
boblsturm@0
|
49 % DataHash(int32(1:10), Opt) % +tJN9yeF89h3jOFNN55XLg
|
boblsturm@0
|
50 % % SHA-1, Base64:
|
boblsturm@0
|
51 % S.a = uint8([]);
|
boblsturm@0
|
52 % S.b = {{1:10}, struct('q', uint64(415))};
|
boblsturm@0
|
53 % Opt.Method = 'SHA-1';
|
boblsturm@0
|
54 % Opt.Format = 'HEX';
|
boblsturm@0
|
55 % DataHash(S, Opt) % 18672BE876463B25214CA9241B3C79CC926F3093
|
boblsturm@0
|
56 % % SHA-1 of binary values:
|
boblsturm@0
|
57 % Opt = struct('Method', 'SHA-1', 'Input', 'bin');
|
boblsturm@0
|
58 % DataHash(1:8, Opt) % 826cf9d3a5d74bbe415e97d4cecf03f445f69225
|
boblsturm@0
|
59 % % SHA-256, consider ASCII part only (Matlab's CHAR has 16 bits!):
|
boblsturm@0
|
60 % Opt.Method = 'SHA-256';
|
boblsturm@0
|
61 % Opt.Input = 'ascii';
|
boblsturm@0
|
62 % DataHash('abc', Opt)
|
boblsturm@0
|
63 % % ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
|
boblsturm@0
|
64 % % Or equivalently:
|
boblsturm@0
|
65 % Opt.Input = 'bin';
|
boblsturm@0
|
66 % DataHash(uint8('abc'), Opt)
|
boblsturm@0
|
67 %
|
boblsturm@0
|
68 % NOTES:
|
boblsturm@0
|
69 % Function handles and user-defined objects cannot be converted uniquely:
|
boblsturm@0
|
70 % - The subfunction ConvertFuncHandle uses the built-in function FUNCTIONS,
|
boblsturm@0
|
71 % but the replied struct can depend on the Matlab version.
|
boblsturm@0
|
72 % - It is tried to convert objects to UINT8 streams in the subfunction
|
boblsturm@0
|
73 % ConvertObject. A conversion by STRUCT() might be more appropriate.
|
boblsturm@0
|
74 % Adjust these subfunctions on demand.
|
boblsturm@0
|
75 %
|
boblsturm@0
|
76 % MATLAB CHARs have 16 bits! Use Opt.Input='ascii' for comparisons with e.g.
|
boblsturm@0
|
77 % online hash generators.
|
boblsturm@0
|
78 %
|
boblsturm@0
|
79 % Matt Raum suggested this for e.g. user-defined objects:
|
boblsturm@0
|
80 % DataHash(getByteStreamFromArray(Data)
|
boblsturm@0
|
81 % This works very well, but unfortunately getByteStreamFromArray is
|
boblsturm@0
|
82 % undocumented, such that it might vanish in the future or reply different
|
boblsturm@0
|
83 % output.
|
boblsturm@0
|
84 %
|
boblsturm@0
|
85 % For arrays the calculated hash value might be changed in new versions.
|
boblsturm@0
|
86 % Calling this function without inputs replies the version of the hash.
|
boblsturm@0
|
87 %
|
boblsturm@0
|
88 % The C-Mex function GetMD5 is 2 to 100 times faster, but obtains MD5 only:
|
boblsturm@0
|
89 % http://www.mathworks.com/matlabcentral/fileexchange/25921
|
boblsturm@0
|
90 %
|
boblsturm@0
|
91 % Tested: Matlab 7.7, 7.8, 7.13, 8.6, WinXP/32, Win7/64
|
boblsturm@0
|
92 % Author: Jan Simon, Heidelberg, (C) 2011-2016 matlab.2010(a)n(MINUS)simon.de
|
boblsturm@0
|
93 %
|
boblsturm@0
|
94 % See also: TYPECAST, CAST.
|
boblsturm@0
|
95 %
|
boblsturm@0
|
96 % Michael Kleder, "Compute Hash", no structs and cells:
|
boblsturm@0
|
97 % http://www.mathworks.com/matlabcentral/fileexchange/8944
|
boblsturm@0
|
98 % Tim, "Serialize/Deserialize", converts structs and cells to a byte stream:
|
boblsturm@0
|
99 % http://www.mathworks.com/matlabcentral/fileexchange/29457
|
boblsturm@0
|
100
|
boblsturm@0
|
101 % $JRev: R-H V:033 Sum:R+m7rAPNLvlw Date:18-Jun-2016 14:33:17 $
|
boblsturm@0
|
102 % $License: BSD (use/copy/change/redistribute on own risk, mention the author) $
|
boblsturm@0
|
103 % $File: Tools\GLFile\DataHash.m $
|
boblsturm@0
|
104 % History:
|
boblsturm@0
|
105 % 001: 01-May-2011 21:52, First version.
|
boblsturm@0
|
106 % 007: 10-Jun-2011 10:38, [Opt.Input], binary data, complex values considered.
|
boblsturm@0
|
107 % 011: 26-May-2012 15:57, Fixed: Failed for binary input and empty data.
|
boblsturm@0
|
108 % 014: 04-Nov-2012 11:37, Consider Mex-, MDL- and P-files also.
|
boblsturm@0
|
109 % Thanks to David (author 243360), who found this bug.
|
boblsturm@0
|
110 % Jan Achterhold (author 267816) suggested to consider Java objects.
|
boblsturm@0
|
111 % 016: 01-Feb-2015 20:53, Java heap space exhausted for large files.
|
boblsturm@0
|
112 % Now files are process in chunks to save memory.
|
boblsturm@0
|
113 % 017: 15-Feb-2015 19:40, Collsions: Same hash for different data.
|
boblsturm@0
|
114 % Examples: zeros(1,1) and zeros(1,1,0)
|
boblsturm@0
|
115 % complex(0) and zeros(1,1,0,0)
|
boblsturm@0
|
116 % Now the number of dimensions is included, to avoid this.
|
boblsturm@0
|
117 % 022: 30-Mar-2015 00:04, Bugfix: Failed for strings and [] without TYPECASTX.
|
boblsturm@0
|
118 % Ross found these 2 bugs, which occur when TYPECASTX is not installed.
|
boblsturm@0
|
119 % If you need the base64 format padded with '=' characters, adjust
|
boblsturm@0
|
120 % fBase64_enc as you like.
|
boblsturm@0
|
121 % 026: 29-Jun-2015 00:13, Changed hash for STRUCTs.
|
boblsturm@0
|
122 % Struct arrays are analysed field by field now, which is much faster.
|
boblsturm@0
|
123 % 027: 13-Sep-2015 19:03, 'ascii' input as abbrev. for Input='bin' and UINT8().
|
boblsturm@0
|
124 % 028: 15-Oct-2015 23:11, Example values in help section updated to v022.
|
boblsturm@0
|
125 % 029: 16-Oct-2015 22:32, Use default options for empty input.
|
boblsturm@0
|
126 % 031: 28-Feb-2016 15:10, New hash value to get same reply as GetMD5.
|
boblsturm@0
|
127 % New Matlab version (at least 2015b) use a fast method for TYPECAST, such
|
boblsturm@0
|
128 % that calling James Tursa's TYPECASTX is not needed anymore.
|
boblsturm@0
|
129 % Matlab 6.5 not supported anymore: MException for CATCH.
|
boblsturm@0
|
130 % 033: 18-Jun-2016 14:28, BUGFIX: Failed on empty files.
|
boblsturm@0
|
131 % Thanks to Christian (AuthorID 2918599).
|
boblsturm@0
|
132
|
boblsturm@0
|
133 % OPEN BUGS:
|
boblsturm@0
|
134 % Nath wrote:
|
boblsturm@0
|
135 % function handle refering to struct containing the function will create
|
boblsturm@0
|
136 % infinite loop. Is there any workaround ?
|
boblsturm@0
|
137 % Example:
|
boblsturm@0
|
138 % d= dynamicprops();
|
boblsturm@0
|
139 % addprop(d,'f');
|
boblsturm@0
|
140 % d.f= @(varargin) struct2cell(d);
|
boblsturm@0
|
141 % DataHash(d.f) % infinite loop
|
boblsturm@0
|
142 % This is caught with an error message concerning the recursion limit now.
|
boblsturm@0
|
143
|
boblsturm@0
|
144 % Main function: ===============================================================
|
boblsturm@0
|
145 % Default options: -------------------------------------------------------------
|
boblsturm@0
|
146 Method = 'MD5';
|
boblsturm@0
|
147 OutFormat = 'hex';
|
boblsturm@0
|
148 isFile = false;
|
boblsturm@0
|
149 isBin = false;
|
boblsturm@0
|
150
|
boblsturm@0
|
151 % Check number and type of inputs: ---------------------------------------------
|
boblsturm@0
|
152 nArg = nargin;
|
boblsturm@0
|
153 if nArg == 2
|
boblsturm@0
|
154 if isa(Opt, 'struct') == 0 % Bad type of 2nd input:
|
boblsturm@0
|
155 Error_L('BadInput2', '2nd input [Opt] must be a struct.');
|
boblsturm@0
|
156 end
|
boblsturm@0
|
157
|
boblsturm@0
|
158 % Specify hash algorithm:
|
boblsturm@0
|
159 if isfield(Opt, 'Method') && ~isempty(Opt.Method) % Short-circuiting
|
boblsturm@0
|
160 Method = upper(Opt.Method);
|
boblsturm@0
|
161 end
|
boblsturm@0
|
162
|
boblsturm@0
|
163 % Specify output format:
|
boblsturm@0
|
164 if isfield(Opt, 'Format') && ~isempty(Opt.Format) % Short-circuiting
|
boblsturm@0
|
165 OutFormat = Opt.Format;
|
boblsturm@0
|
166 end
|
boblsturm@0
|
167
|
boblsturm@0
|
168 % Check if the Input type is specified - default: 'array':
|
boblsturm@0
|
169 if isfield(Opt, 'Input') && ~isempty(Opt.Input) % Short-circuiting
|
boblsturm@0
|
170 if strcmpi(Opt.Input, 'File')
|
boblsturm@0
|
171 if ischar(Data) == 0
|
boblsturm@0
|
172 Error_L('CannotOpen', '1st input FileName must be a string');
|
boblsturm@0
|
173 end
|
boblsturm@0
|
174 isFile = true;
|
boblsturm@0
|
175
|
boblsturm@0
|
176 elseif strncmpi(Opt.Input, 'bin', 3) % Accept 'binary' also
|
boblsturm@0
|
177 if (isnumeric(Data) || ischar(Data) || islogical(Data)) == 0 || ...
|
boblsturm@0
|
178 issparse(Data)
|
boblsturm@0
|
179 Error_L('BadDataType', ...
|
boblsturm@0
|
180 '1st input must be numeric, CHAR or LOGICAL for binary input.');
|
boblsturm@0
|
181 end
|
boblsturm@0
|
182 isBin = true;
|
boblsturm@0
|
183
|
boblsturm@0
|
184 elseif strncmpi(Opt.Input, 'asc', 3) % 8-bit ASCII characters
|
boblsturm@0
|
185 if ~ischar(Data)
|
boblsturm@0
|
186 Error_L('BadDataType', ...
|
boblsturm@0
|
187 '1st input must be a CHAR for the input type ASCII.');
|
boblsturm@0
|
188 end
|
boblsturm@0
|
189 isBin = true;
|
boblsturm@0
|
190 Data = uint8(Data);
|
boblsturm@0
|
191 end
|
boblsturm@0
|
192 end
|
boblsturm@0
|
193
|
boblsturm@0
|
194 elseif nArg == 0 % Reply version of this function:
|
boblsturm@0
|
195 R = Version_L;
|
boblsturm@0
|
196
|
boblsturm@0
|
197 if nargout == 0
|
boblsturm@0
|
198 disp(R);
|
boblsturm@0
|
199 else
|
boblsturm@0
|
200 Hash = R;
|
boblsturm@0
|
201 end
|
boblsturm@0
|
202
|
boblsturm@0
|
203 return;
|
boblsturm@0
|
204
|
boblsturm@0
|
205 elseif nArg ~= 1 % Bad number of arguments:
|
boblsturm@0
|
206 Error_L('BadNInput', '1 or 2 inputs required.');
|
boblsturm@0
|
207 end
|
boblsturm@0
|
208
|
boblsturm@0
|
209 % Create the engine: -----------------------------------------------------------
|
boblsturm@0
|
210 try
|
boblsturm@0
|
211 Engine = java.security.MessageDigest.getInstance(Method);
|
boblsturm@0
|
212 catch
|
boblsturm@0
|
213 Error_L('BadInput2', 'Invalid algorithm: [%s].', Method);
|
boblsturm@0
|
214 end
|
boblsturm@0
|
215
|
boblsturm@0
|
216 % Create the hash value: -------------------------------------------------------
|
boblsturm@0
|
217 if isFile
|
boblsturm@0
|
218 % Open the file:
|
boblsturm@0
|
219 FID = fopen(Data, 'r');
|
boblsturm@0
|
220 if FID < 0
|
boblsturm@0
|
221 % Check existence of file:
|
boblsturm@0
|
222 Found = FileExist_L(Data);
|
boblsturm@0
|
223 if Found
|
boblsturm@0
|
224 Error_L('CantOpenFile', 'Cannot open file: %s.', Data);
|
boblsturm@0
|
225 else
|
boblsturm@0
|
226 Error_L('FileNotFound', 'File not found: %s.', Data);
|
boblsturm@0
|
227 end
|
boblsturm@0
|
228 end
|
boblsturm@0
|
229
|
boblsturm@0
|
230 % Read file in chunks to save memory and Java heap space:
|
boblsturm@0
|
231 Chunk = 1e6; % Fastest for 1e6 on Win7/64, HDD
|
boblsturm@0
|
232 Count = Chunk; % Dummy value to satisfy WHILE condition
|
boblsturm@0
|
233 while Count == Chunk
|
boblsturm@0
|
234 [Data, Count] = fread(FID, Chunk, '*uint8');
|
boblsturm@0
|
235 if Count ~= 0 % Avoid error for empty file
|
boblsturm@0
|
236 Engine.update(Data);
|
boblsturm@0
|
237 end
|
boblsturm@0
|
238 end
|
boblsturm@0
|
239 fclose(FID);
|
boblsturm@0
|
240
|
boblsturm@0
|
241 % Calculate the hash:
|
boblsturm@0
|
242 Hash = typecast(Engine.digest, 'uint8');
|
boblsturm@0
|
243
|
boblsturm@0
|
244 elseif isBin % Contents of an elementary array, type tested already:
|
boblsturm@0
|
245 if isempty(Data) % Nothing to do, Engine.update fails for empty input!
|
boblsturm@0
|
246 Hash = typecast(Engine.digest, 'uint8');
|
boblsturm@0
|
247 else % Matlab's TYPECAST is less elegant:
|
boblsturm@0
|
248 if isnumeric(Data)
|
boblsturm@0
|
249 if isreal(Data)
|
boblsturm@0
|
250 Engine.update(typecast(Data(:), 'uint8'));
|
boblsturm@0
|
251 else
|
boblsturm@0
|
252 Engine.update(typecast(real(Data(:)), 'uint8'));
|
boblsturm@0
|
253 Engine.update(typecast(imag(Data(:)), 'uint8'));
|
boblsturm@0
|
254 end
|
boblsturm@0
|
255 elseif islogical(Data) % TYPECAST cannot handle LOGICAL
|
boblsturm@0
|
256 Engine.update(typecast(uint8(Data(:)), 'uint8'));
|
boblsturm@0
|
257 elseif ischar(Data) % TYPECAST cannot handle CHAR
|
boblsturm@0
|
258 Engine.update(typecast(uint16(Data(:)), 'uint8'));
|
boblsturm@0
|
259 % Bugfix: Line removed
|
boblsturm@0
|
260 end
|
boblsturm@0
|
261 Hash = typecast(Engine.digest, 'uint8');
|
boblsturm@0
|
262 end
|
boblsturm@0
|
263 else % Array with type:
|
boblsturm@0
|
264 Engine = CoreHash(Data, Engine);
|
boblsturm@0
|
265 Hash = typecast(Engine.digest, 'uint8');
|
boblsturm@0
|
266 end
|
boblsturm@0
|
267
|
boblsturm@0
|
268 % Convert hash specific output format: -----------------------------------------
|
boblsturm@0
|
269 switch OutFormat
|
boblsturm@0
|
270 case 'hex'
|
boblsturm@0
|
271 Hash = sprintf('%.2x', double(Hash));
|
boblsturm@0
|
272 case 'HEX'
|
boblsturm@0
|
273 Hash = sprintf('%.2X', double(Hash));
|
boblsturm@0
|
274 case 'double'
|
boblsturm@0
|
275 Hash = double(reshape(Hash, 1, []));
|
boblsturm@0
|
276 case 'uint8'
|
boblsturm@0
|
277 Hash = reshape(Hash, 1, []);
|
boblsturm@0
|
278 case 'base64'
|
boblsturm@0
|
279 Hash = fBase64_enc(double(Hash));
|
boblsturm@0
|
280 otherwise
|
boblsturm@0
|
281 Error_L('BadOutFormat', ...
|
boblsturm@0
|
282 '[Opt.Format] must be: HEX, hex, uint8, double, base64.');
|
boblsturm@0
|
283 end
|
boblsturm@0
|
284
|
boblsturm@0
|
285 % return;
|
boblsturm@0
|
286
|
boblsturm@0
|
287 % ******************************************************************************
|
boblsturm@0
|
288 function Engine = CoreHash(Data, Engine)
|
boblsturm@0
|
289 % This methods uses the slower TYPECAST of Matlab
|
boblsturm@0
|
290
|
boblsturm@0
|
291 % Consider the type and dimensions of the array to distinguish arrays with the
|
boblsturm@0
|
292 % same data, but different shape: [0 x 0] and [0 x 1], [1,2] and [1;2],
|
boblsturm@0
|
293 % DOUBLE(0) and SINGLE([0,0]):
|
boblsturm@0
|
294 % < v016: [class, size, data]. BUG! 0 and zeros(1,1,0) had the same hash!
|
boblsturm@0
|
295 % >= v016: [class, ndims, size, data]
|
boblsturm@0
|
296 Engine.update([uint8(class(Data)), ...
|
boblsturm@0
|
297 typecast(uint64([ndims(Data), size(Data)]), 'uint8')]);
|
boblsturm@0
|
298
|
boblsturm@0
|
299 if issparse(Data) % Sparse arrays to struct:
|
boblsturm@0
|
300 [S.Index1, S.Index2, S.Value] = find(Data);
|
boblsturm@0
|
301 Engine = CoreHash(S, Engine);
|
boblsturm@0
|
302 elseif isstruct(Data) % Hash for all array elements and fields:
|
boblsturm@0
|
303 F = sort(fieldnames(Data)); % Ignore order of fields
|
boblsturm@0
|
304 for iField = 1:length(F) % Loop over fields
|
boblsturm@0
|
305 aField = F{iField};
|
boblsturm@0
|
306 Engine.update(uint8(aField));
|
boblsturm@0
|
307 for iS = 1:numel(Data) % Loop over elements of struct array
|
boblsturm@0
|
308 Engine = CoreHash(Data(iS).(aField), Engine);
|
boblsturm@0
|
309 end
|
boblsturm@0
|
310 end
|
boblsturm@0
|
311 elseif iscell(Data) % Get hash for all cell elements:
|
boblsturm@0
|
312 for iS = 1:numel(Data)
|
boblsturm@0
|
313 Engine = CoreHash(Data{iS}, Engine);
|
boblsturm@0
|
314 end
|
boblsturm@0
|
315 elseif isempty(Data) % Nothing to do
|
boblsturm@0
|
316 elseif isnumeric(Data)
|
boblsturm@0
|
317 if isreal(Data)
|
boblsturm@0
|
318 Engine.update(typecast(Data(:), 'uint8'));
|
boblsturm@0
|
319 else
|
boblsturm@0
|
320 Engine.update(typecast(real(Data(:)), 'uint8'));
|
boblsturm@0
|
321 Engine.update(typecast(imag(Data(:)), 'uint8'));
|
boblsturm@0
|
322 end
|
boblsturm@0
|
323 elseif islogical(Data) % TYPECAST cannot handle LOGICAL
|
boblsturm@0
|
324 Engine.update(typecast(uint8(Data(:)), 'uint8'));
|
boblsturm@0
|
325 elseif ischar(Data) % TYPECAST cannot handle CHAR
|
boblsturm@0
|
326 Engine.update(typecast(uint16(Data(:)), 'uint8'));
|
boblsturm@0
|
327 elseif isa(Data, 'function_handle')
|
boblsturm@0
|
328 Engine = CoreHash(ConvertFuncHandle(Data), Engine);
|
boblsturm@0
|
329 elseif (isobject(Data) || isjava(Data)) && ismethod(Data, 'hashCode')
|
boblsturm@0
|
330 Engine = CoreHash(char(Data.hashCode), Engine);
|
boblsturm@0
|
331 else % Most likely a user-defined object:
|
boblsturm@0
|
332 try
|
boblsturm@0
|
333 BasicData = ConvertObject(Data);
|
boblsturm@0
|
334 catch ME
|
boblsturm@0
|
335 error(['JSimon:', mfilename, ':BadDataType'], ...
|
boblsturm@0
|
336 '%s: Cannot create elementary array for type: %s\n %s', ...
|
boblsturm@0
|
337 mfilename, class(Data), ME.message);
|
boblsturm@0
|
338 end
|
boblsturm@0
|
339
|
boblsturm@0
|
340 try
|
boblsturm@0
|
341 Engine = CoreHash(BasicData, Engine);
|
boblsturm@0
|
342 catch ME
|
boblsturm@0
|
343 if strcmpi(ME.identifier, 'MATLAB:recursionLimit')
|
boblsturm@0
|
344 ME = MException(['JSimon:', mfilename, ':RecursiveType'], ...
|
boblsturm@0
|
345 '%s: Cannot create hash for recursive data type: %s', ...
|
boblsturm@0
|
346 mfilename, class(Data));
|
boblsturm@0
|
347 end
|
boblsturm@0
|
348 throw(ME);
|
boblsturm@0
|
349 end
|
boblsturm@0
|
350 end
|
boblsturm@0
|
351
|
boblsturm@0
|
352 % return;
|
boblsturm@0
|
353
|
boblsturm@0
|
354 % ******************************************************************************
|
boblsturm@0
|
355 function FuncKey = ConvertFuncHandle(FuncH)
|
boblsturm@0
|
356 % The subfunction ConvertFuncHandle converts function_handles to a struct
|
boblsturm@0
|
357 % using the Matlab function FUNCTIONS. The output of this function changes
|
boblsturm@0
|
358 % with the Matlab version, such that DataHash(@sin) replies different hashes
|
boblsturm@0
|
359 % under Matlab 6.5 and 2009a.
|
boblsturm@0
|
360 % An alternative is using the function name and name of the file for
|
boblsturm@0
|
361 % function_handles, but this is not unique for nested or anonymous functions.
|
boblsturm@0
|
362 % If the MATLABROOT is removed from the file's path, at least the hash of
|
boblsturm@0
|
363 % Matlab's toolbox functions is (usually!) not influenced by the version.
|
boblsturm@0
|
364 % Finally I'm in doubt if there is a unique method to hash function handles.
|
boblsturm@0
|
365 % Please adjust the subfunction ConvertFuncHandles to your needs.
|
boblsturm@0
|
366
|
boblsturm@0
|
367 % The Matlab version influences the conversion by FUNCTIONS:
|
boblsturm@0
|
368 % 1. The format of the struct replied FUNCTIONS is not fixed,
|
boblsturm@0
|
369 % 2. The full paths of toolbox function e.g. for @mean differ.
|
boblsturm@0
|
370 FuncKey = functions(FuncH);
|
boblsturm@0
|
371
|
boblsturm@0
|
372 % Include modification file time and file size. Suggested by Aslak Grinsted:
|
boblsturm@0
|
373 if ~isempty(FuncKey.file)
|
boblsturm@0
|
374 d = dir(FuncKey.file);
|
boblsturm@0
|
375 if ~isempty(d)
|
boblsturm@0
|
376 FuncKey.filebytes = d.bytes;
|
boblsturm@0
|
377 FuncKey.filedate = d.datenum;
|
boblsturm@0
|
378 end
|
boblsturm@0
|
379 end
|
boblsturm@0
|
380
|
boblsturm@0
|
381 % ALTERNATIVE: Use name and path. The <matlabroot> part of the toolbox functions
|
boblsturm@0
|
382 % is replaced such that the hash for @mean does not depend on the Matlab
|
boblsturm@0
|
383 % version.
|
boblsturm@0
|
384 % Drawbacks: Anonymous functions, nested functions...
|
boblsturm@0
|
385 % funcStruct = functions(FuncH);
|
boblsturm@0
|
386 % funcfile = strrep(funcStruct.file, matlabroot, '<MATLAB>');
|
boblsturm@0
|
387 % FuncKey = uint8([funcStruct.function, ' ', funcfile]);
|
boblsturm@0
|
388
|
boblsturm@0
|
389 % Finally I'm afraid there is no unique method to get a hash for a function
|
boblsturm@0
|
390 % handle. Please adjust this conversion to your needs.
|
boblsturm@0
|
391
|
boblsturm@0
|
392 % return;
|
boblsturm@0
|
393
|
boblsturm@0
|
394 % ******************************************************************************
|
boblsturm@0
|
395 function DataBin = ConvertObject(DataObj)
|
boblsturm@0
|
396 % Convert a user-defined object to a binary stream. There cannot be a unique
|
boblsturm@0
|
397 % solution, so this part is left for the user...
|
boblsturm@0
|
398
|
boblsturm@0
|
399 try % Perhaps a direct conversion is implemented:
|
boblsturm@0
|
400 DataBin = uint8(DataObj);
|
boblsturm@0
|
401
|
boblsturm@0
|
402 % Matt Raum had this excellent idea - unfortunately this function is
|
boblsturm@0
|
403 % undocumented and might not be supported in te future:
|
boblsturm@0
|
404 % DataBin = getByteStreamFromArray(DataObj);
|
boblsturm@0
|
405
|
boblsturm@0
|
406 catch % Or perhaps this is better:
|
boblsturm@0
|
407 WarnS = warning('off', 'MATLAB:structOnObject');
|
boblsturm@0
|
408 DataBin = struct(DataObj);
|
boblsturm@0
|
409 warning(WarnS);
|
boblsturm@0
|
410 end
|
boblsturm@0
|
411
|
boblsturm@0
|
412 % return;
|
boblsturm@0
|
413
|
boblsturm@0
|
414 % ******************************************************************************
|
boblsturm@0
|
415 function Out = fBase64_enc(In)
|
boblsturm@0
|
416 % Encode numeric vector of UINT8 values to base64 string.
|
boblsturm@0
|
417 % The intention of this is to create a shorter hash than the HEX format.
|
boblsturm@0
|
418 % Therefore a padding with '=' characters is omitted on purpose.
|
boblsturm@0
|
419
|
boblsturm@0
|
420 Pool = [65:90, 97:122, 48:57, 43, 47]; % [0:9, a:z, A:Z, +, /]
|
boblsturm@0
|
421 v8 = [128; 64; 32; 16; 8; 4; 2; 1];
|
boblsturm@0
|
422 v6 = [32, 16, 8, 4, 2, 1];
|
boblsturm@0
|
423
|
boblsturm@0
|
424 In = reshape(In, 1, []);
|
boblsturm@0
|
425 X = rem(floor(In(ones(8, 1), :) ./ v8(:, ones(length(In), 1))), 2);
|
boblsturm@0
|
426 Y = reshape([X(:); zeros(6 - rem(numel(X), 6), 1)], 6, []);
|
boblsturm@0
|
427 Out = char(Pool(1 + v6 * Y));
|
boblsturm@0
|
428
|
boblsturm@0
|
429 % return;
|
boblsturm@0
|
430
|
boblsturm@0
|
431 % ******************************************************************************
|
boblsturm@0
|
432 function Ex = FileExist_L(FileName)
|
boblsturm@0
|
433 % A more reliable version of EXIST(FileName, 'file'):
|
boblsturm@0
|
434 dirFile = dir(FileName);
|
boblsturm@0
|
435 if length(dirFile) == 1
|
boblsturm@0
|
436 Ex = ~(dirFile.isdir);
|
boblsturm@0
|
437 else
|
boblsturm@0
|
438 Ex = false;
|
boblsturm@0
|
439 end
|
boblsturm@0
|
440
|
boblsturm@0
|
441 % return;
|
boblsturm@0
|
442
|
boblsturm@0
|
443 % ******************************************************************************
|
boblsturm@0
|
444 function R = Version_L()
|
boblsturm@0
|
445 % The output differs between versions of this function. So give the user a
|
boblsturm@0
|
446 % chance to recognize the version:
|
boblsturm@0
|
447 % 1: 01-May-2011, Initial version
|
boblsturm@0
|
448 % 2: 15-Feb-2015, The number of dimensions is considered in addition.
|
boblsturm@0
|
449 % In version 1 these variables had the same hash:
|
boblsturm@0
|
450 % zeros(1,1) and zeros(1,1,0), complex(0) and zeros(1,1,0,0)
|
boblsturm@0
|
451 % 3: 29-Jun-2015, Struct arrays are processed field by field and not element
|
boblsturm@0
|
452 % by element, because this is much faster. In consequence the hash value
|
boblsturm@0
|
453 % differs, if the input contains a struct.
|
boblsturm@0
|
454 % 4: 28-Feb-2016 15:20, same output as GetMD5 for MD5 sums. Therefore the
|
boblsturm@0
|
455 % dimensions are casted to UINT64 at first.
|
boblsturm@0
|
456 R.HashVersion = 4;
|
boblsturm@0
|
457 R.Date = [2016, 2, 28];
|
boblsturm@0
|
458
|
boblsturm@0
|
459 R.HashMethod = {};
|
boblsturm@0
|
460 try
|
boblsturm@0
|
461 Provider = java.security.Security.getProviders;
|
boblsturm@0
|
462 for iProvider = 1:numel(Provider)
|
boblsturm@0
|
463 S = char(Provider(iProvider).getServices);
|
boblsturm@0
|
464 Index = strfind(S, 'MessageDigest.');
|
boblsturm@0
|
465 for iDigest = 1:length(Index)
|
boblsturm@0
|
466 Digest = strtok(S(Index(iDigest):end));
|
boblsturm@0
|
467 Digest = strrep(Digest, 'MessageDigest.', '');
|
boblsturm@0
|
468 R.HashMethod = cat(2, R.HashMethod, {Digest});
|
boblsturm@0
|
469 end
|
boblsturm@0
|
470 end
|
boblsturm@0
|
471 catch ME
|
boblsturm@0
|
472 fprintf(2, '%s\n', ME.message);
|
boblsturm@0
|
473 R.HashMethod = 'error';
|
boblsturm@0
|
474 end
|
boblsturm@0
|
475
|
boblsturm@0
|
476 % return;
|
boblsturm@0
|
477
|
boblsturm@0
|
478 % ******************************************************************************
|
boblsturm@0
|
479 function Error_L(ID, varargin)
|
boblsturm@0
|
480
|
boblsturm@0
|
481 error(['JSimon:', mfilename, ':', ID], ['*** %s: ', varargin{1}], ...
|
boblsturm@0
|
482 mfilename, varargin{2:nargin - 1});
|
boblsturm@0
|
483
|
boblsturm@0
|
484 % return;
|