boblsturm@0: function Hash = DataHash(Data, Opt) boblsturm@0: % DATAHASH - Checksum for Matlab array of any type boblsturm@0: % This function creates a hash value for an input of any type. The type and boblsturm@0: % dimensions of the input are considered as default, such that UINT8([0,0]) and boblsturm@0: % UINT16(0) have different hash values. Nested STRUCTs and CELLs are parsed boblsturm@0: % recursively. boblsturm@0: % boblsturm@0: % Hash = DataHash(Data, Opt) boblsturm@0: % INPUT: boblsturm@0: % Data: Array of these built-in types: boblsturm@0: % (U)INT8/16/32/64, SINGLE, DOUBLE, (real/complex, full/sparse) boblsturm@0: % CHAR, LOGICAL, CELL (nested), STRUCT (scalar or array, nested), boblsturm@0: % function_handle. boblsturm@0: % Opt: Struct to specify the hashing algorithm and the output format. boblsturm@0: % Opt and all its fields are optional. boblsturm@0: % Opt.Method: String, known methods for Java 1.6 (Matlab 2011b): boblsturm@0: % 'SHA-1', 'SHA-256', 'SHA-384', 'SHA-512', 'MD2', 'MD5'. boblsturm@0: % Call DataHash without inputs to get a list of available methods. boblsturm@0: % Default: 'MD5'. boblsturm@0: % Opt.Format: String specifying the output format: boblsturm@0: % 'hex', 'HEX': Lower/uppercase hexadecimal string. boblsturm@0: % 'double', 'uint8': Numerical vector. boblsturm@0: % 'base64': Base64 encoded string, only printable ASCII boblsturm@0: % characters, shorter than 'hex', no padding. boblsturm@0: % Default: 'hex'. boblsturm@0: % Opt.Input: Type of the input as string, not case-sensitive: boblsturm@0: % 'array': The contents, type and size of the input [Data] are boblsturm@0: % considered for the creation of the hash. Nested CELLs boblsturm@0: % and STRUCT arrays are parsed recursively. Empty arrays of boblsturm@0: % different type reply different hashs. boblsturm@0: % 'file': [Data] is treated as file name and the hash is calculated boblsturm@0: % for the files contents. boblsturm@0: % 'bin': [Data] is a numerical, LOGICAL or CHAR array. Only the boblsturm@0: % binary contents of the array is considered, such that boblsturm@0: % e.g. empty arrays of different type reply the same hash. boblsturm@0: % 'ascii': Same as 'bin', but only the 8-bit ASCII part of the 16-bit boblsturm@0: % Matlab CHARs is considered. boblsturm@0: % Default: 'array'. boblsturm@0: % boblsturm@0: % OUTPUT: boblsturm@0: % Hash: String, DOUBLE or UINT8 vector. The length depends on the hashing boblsturm@0: % method. boblsturm@0: % boblsturm@0: % EXAMPLES: boblsturm@0: % % Default: MD5, hex: boblsturm@0: % DataHash([]) % 5b302b7b2099a97ba2a276640a192485 boblsturm@0: % % MD5, Base64: boblsturm@0: % Opt = struct('Format', 'base64', 'Method', 'MD5'); boblsturm@0: % DataHash(int32(1:10), Opt) % +tJN9yeF89h3jOFNN55XLg boblsturm@0: % % SHA-1, Base64: boblsturm@0: % S.a = uint8([]); boblsturm@0: % S.b = {{1:10}, struct('q', uint64(415))}; boblsturm@0: % Opt.Method = 'SHA-1'; boblsturm@0: % Opt.Format = 'HEX'; boblsturm@0: % DataHash(S, Opt) % 18672BE876463B25214CA9241B3C79CC926F3093 boblsturm@0: % % SHA-1 of binary values: boblsturm@0: % Opt = struct('Method', 'SHA-1', 'Input', 'bin'); boblsturm@0: % DataHash(1:8, Opt) % 826cf9d3a5d74bbe415e97d4cecf03f445f69225 boblsturm@0: % % SHA-256, consider ASCII part only (Matlab's CHAR has 16 bits!): boblsturm@0: % Opt.Method = 'SHA-256'; boblsturm@0: % Opt.Input = 'ascii'; boblsturm@0: % DataHash('abc', Opt) boblsturm@0: % % ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad boblsturm@0: % % Or equivalently: boblsturm@0: % Opt.Input = 'bin'; boblsturm@0: % DataHash(uint8('abc'), Opt) boblsturm@0: % boblsturm@0: % NOTES: boblsturm@0: % Function handles and user-defined objects cannot be converted uniquely: boblsturm@0: % - The subfunction ConvertFuncHandle uses the built-in function FUNCTIONS, boblsturm@0: % but the replied struct can depend on the Matlab version. boblsturm@0: % - It is tried to convert objects to UINT8 streams in the subfunction boblsturm@0: % ConvertObject. A conversion by STRUCT() might be more appropriate. boblsturm@0: % Adjust these subfunctions on demand. boblsturm@0: % boblsturm@0: % MATLAB CHARs have 16 bits! Use Opt.Input='ascii' for comparisons with e.g. boblsturm@0: % online hash generators. boblsturm@0: % boblsturm@0: % Matt Raum suggested this for e.g. user-defined objects: boblsturm@0: % DataHash(getByteStreamFromArray(Data) boblsturm@0: % This works very well, but unfortunately getByteStreamFromArray is boblsturm@0: % undocumented, such that it might vanish in the future or reply different boblsturm@0: % output. boblsturm@0: % boblsturm@0: % For arrays the calculated hash value might be changed in new versions. boblsturm@0: % Calling this function without inputs replies the version of the hash. boblsturm@0: % boblsturm@0: % The C-Mex function GetMD5 is 2 to 100 times faster, but obtains MD5 only: boblsturm@0: % http://www.mathworks.com/matlabcentral/fileexchange/25921 boblsturm@0: % boblsturm@0: % Tested: Matlab 7.7, 7.8, 7.13, 8.6, WinXP/32, Win7/64 boblsturm@0: % Author: Jan Simon, Heidelberg, (C) 2011-2016 matlab.2010(a)n(MINUS)simon.de boblsturm@0: % boblsturm@0: % See also: TYPECAST, CAST. boblsturm@0: % boblsturm@0: % Michael Kleder, "Compute Hash", no structs and cells: boblsturm@0: % http://www.mathworks.com/matlabcentral/fileexchange/8944 boblsturm@0: % Tim, "Serialize/Deserialize", converts structs and cells to a byte stream: boblsturm@0: % http://www.mathworks.com/matlabcentral/fileexchange/29457 boblsturm@0: boblsturm@0: % $JRev: R-H V:033 Sum:R+m7rAPNLvlw Date:18-Jun-2016 14:33:17 $ boblsturm@0: % $License: BSD (use/copy/change/redistribute on own risk, mention the author) $ boblsturm@0: % $File: Tools\GLFile\DataHash.m $ boblsturm@0: % History: boblsturm@0: % 001: 01-May-2011 21:52, First version. boblsturm@0: % 007: 10-Jun-2011 10:38, [Opt.Input], binary data, complex values considered. boblsturm@0: % 011: 26-May-2012 15:57, Fixed: Failed for binary input and empty data. boblsturm@0: % 014: 04-Nov-2012 11:37, Consider Mex-, MDL- and P-files also. boblsturm@0: % Thanks to David (author 243360), who found this bug. boblsturm@0: % Jan Achterhold (author 267816) suggested to consider Java objects. boblsturm@0: % 016: 01-Feb-2015 20:53, Java heap space exhausted for large files. boblsturm@0: % Now files are process in chunks to save memory. boblsturm@0: % 017: 15-Feb-2015 19:40, Collsions: Same hash for different data. boblsturm@0: % Examples: zeros(1,1) and zeros(1,1,0) boblsturm@0: % complex(0) and zeros(1,1,0,0) boblsturm@0: % Now the number of dimensions is included, to avoid this. boblsturm@0: % 022: 30-Mar-2015 00:04, Bugfix: Failed for strings and [] without TYPECASTX. boblsturm@0: % Ross found these 2 bugs, which occur when TYPECASTX is not installed. boblsturm@0: % If you need the base64 format padded with '=' characters, adjust boblsturm@0: % fBase64_enc as you like. boblsturm@0: % 026: 29-Jun-2015 00:13, Changed hash for STRUCTs. boblsturm@0: % Struct arrays are analysed field by field now, which is much faster. boblsturm@0: % 027: 13-Sep-2015 19:03, 'ascii' input as abbrev. for Input='bin' and UINT8(). boblsturm@0: % 028: 15-Oct-2015 23:11, Example values in help section updated to v022. boblsturm@0: % 029: 16-Oct-2015 22:32, Use default options for empty input. boblsturm@0: % 031: 28-Feb-2016 15:10, New hash value to get same reply as GetMD5. boblsturm@0: % New Matlab version (at least 2015b) use a fast method for TYPECAST, such boblsturm@0: % that calling James Tursa's TYPECASTX is not needed anymore. boblsturm@0: % Matlab 6.5 not supported anymore: MException for CATCH. boblsturm@0: % 033: 18-Jun-2016 14:28, BUGFIX: Failed on empty files. boblsturm@0: % Thanks to Christian (AuthorID 2918599). boblsturm@0: boblsturm@0: % OPEN BUGS: boblsturm@0: % Nath wrote: boblsturm@0: % function handle refering to struct containing the function will create boblsturm@0: % infinite loop. Is there any workaround ? boblsturm@0: % Example: boblsturm@0: % d= dynamicprops(); boblsturm@0: % addprop(d,'f'); boblsturm@0: % d.f= @(varargin) struct2cell(d); boblsturm@0: % DataHash(d.f) % infinite loop boblsturm@0: % This is caught with an error message concerning the recursion limit now. boblsturm@0: boblsturm@0: % Main function: =============================================================== boblsturm@0: % Default options: ------------------------------------------------------------- boblsturm@0: Method = 'MD5'; boblsturm@0: OutFormat = 'hex'; boblsturm@0: isFile = false; boblsturm@0: isBin = false; boblsturm@0: boblsturm@0: % Check number and type of inputs: --------------------------------------------- boblsturm@0: nArg = nargin; boblsturm@0: if nArg == 2 boblsturm@0: if isa(Opt, 'struct') == 0 % Bad type of 2nd input: boblsturm@0: Error_L('BadInput2', '2nd input [Opt] must be a struct.'); boblsturm@0: end boblsturm@0: boblsturm@0: % Specify hash algorithm: boblsturm@0: if isfield(Opt, 'Method') && ~isempty(Opt.Method) % Short-circuiting boblsturm@0: Method = upper(Opt.Method); boblsturm@0: end boblsturm@0: boblsturm@0: % Specify output format: boblsturm@0: if isfield(Opt, 'Format') && ~isempty(Opt.Format) % Short-circuiting boblsturm@0: OutFormat = Opt.Format; boblsturm@0: end boblsturm@0: boblsturm@0: % Check if the Input type is specified - default: 'array': boblsturm@0: if isfield(Opt, 'Input') && ~isempty(Opt.Input) % Short-circuiting boblsturm@0: if strcmpi(Opt.Input, 'File') boblsturm@0: if ischar(Data) == 0 boblsturm@0: Error_L('CannotOpen', '1st input FileName must be a string'); boblsturm@0: end boblsturm@0: isFile = true; boblsturm@0: boblsturm@0: elseif strncmpi(Opt.Input, 'bin', 3) % Accept 'binary' also boblsturm@0: if (isnumeric(Data) || ischar(Data) || islogical(Data)) == 0 || ... boblsturm@0: issparse(Data) boblsturm@0: Error_L('BadDataType', ... boblsturm@0: '1st input must be numeric, CHAR or LOGICAL for binary input.'); boblsturm@0: end boblsturm@0: isBin = true; boblsturm@0: boblsturm@0: elseif strncmpi(Opt.Input, 'asc', 3) % 8-bit ASCII characters boblsturm@0: if ~ischar(Data) boblsturm@0: Error_L('BadDataType', ... boblsturm@0: '1st input must be a CHAR for the input type ASCII.'); boblsturm@0: end boblsturm@0: isBin = true; boblsturm@0: Data = uint8(Data); boblsturm@0: end boblsturm@0: end boblsturm@0: boblsturm@0: elseif nArg == 0 % Reply version of this function: boblsturm@0: R = Version_L; boblsturm@0: boblsturm@0: if nargout == 0 boblsturm@0: disp(R); boblsturm@0: else boblsturm@0: Hash = R; boblsturm@0: end boblsturm@0: boblsturm@0: return; boblsturm@0: boblsturm@0: elseif nArg ~= 1 % Bad number of arguments: boblsturm@0: Error_L('BadNInput', '1 or 2 inputs required.'); boblsturm@0: end boblsturm@0: boblsturm@0: % Create the engine: ----------------------------------------------------------- boblsturm@0: try boblsturm@0: Engine = java.security.MessageDigest.getInstance(Method); boblsturm@0: catch boblsturm@0: Error_L('BadInput2', 'Invalid algorithm: [%s].', Method); boblsturm@0: end boblsturm@0: boblsturm@0: % Create the hash value: ------------------------------------------------------- boblsturm@0: if isFile boblsturm@0: % Open the file: boblsturm@0: FID = fopen(Data, 'r'); boblsturm@0: if FID < 0 boblsturm@0: % Check existence of file: boblsturm@0: Found = FileExist_L(Data); boblsturm@0: if Found boblsturm@0: Error_L('CantOpenFile', 'Cannot open file: %s.', Data); boblsturm@0: else boblsturm@0: Error_L('FileNotFound', 'File not found: %s.', Data); boblsturm@0: end boblsturm@0: end boblsturm@0: boblsturm@0: % Read file in chunks to save memory and Java heap space: boblsturm@0: Chunk = 1e6; % Fastest for 1e6 on Win7/64, HDD boblsturm@0: Count = Chunk; % Dummy value to satisfy WHILE condition boblsturm@0: while Count == Chunk boblsturm@0: [Data, Count] = fread(FID, Chunk, '*uint8'); boblsturm@0: if Count ~= 0 % Avoid error for empty file boblsturm@0: Engine.update(Data); boblsturm@0: end boblsturm@0: end boblsturm@0: fclose(FID); boblsturm@0: boblsturm@0: % Calculate the hash: boblsturm@0: Hash = typecast(Engine.digest, 'uint8'); boblsturm@0: boblsturm@0: elseif isBin % Contents of an elementary array, type tested already: boblsturm@0: if isempty(Data) % Nothing to do, Engine.update fails for empty input! boblsturm@0: Hash = typecast(Engine.digest, 'uint8'); boblsturm@0: else % Matlab's TYPECAST is less elegant: boblsturm@0: if isnumeric(Data) boblsturm@0: if isreal(Data) boblsturm@0: Engine.update(typecast(Data(:), 'uint8')); boblsturm@0: else boblsturm@0: Engine.update(typecast(real(Data(:)), 'uint8')); boblsturm@0: Engine.update(typecast(imag(Data(:)), 'uint8')); boblsturm@0: end boblsturm@0: elseif islogical(Data) % TYPECAST cannot handle LOGICAL boblsturm@0: Engine.update(typecast(uint8(Data(:)), 'uint8')); boblsturm@0: elseif ischar(Data) % TYPECAST cannot handle CHAR boblsturm@0: Engine.update(typecast(uint16(Data(:)), 'uint8')); boblsturm@0: % Bugfix: Line removed boblsturm@0: end boblsturm@0: Hash = typecast(Engine.digest, 'uint8'); boblsturm@0: end boblsturm@0: else % Array with type: boblsturm@0: Engine = CoreHash(Data, Engine); boblsturm@0: Hash = typecast(Engine.digest, 'uint8'); boblsturm@0: end boblsturm@0: boblsturm@0: % Convert hash specific output format: ----------------------------------------- boblsturm@0: switch OutFormat boblsturm@0: case 'hex' boblsturm@0: Hash = sprintf('%.2x', double(Hash)); boblsturm@0: case 'HEX' boblsturm@0: Hash = sprintf('%.2X', double(Hash)); boblsturm@0: case 'double' boblsturm@0: Hash = double(reshape(Hash, 1, [])); boblsturm@0: case 'uint8' boblsturm@0: Hash = reshape(Hash, 1, []); boblsturm@0: case 'base64' boblsturm@0: Hash = fBase64_enc(double(Hash)); boblsturm@0: otherwise boblsturm@0: Error_L('BadOutFormat', ... boblsturm@0: '[Opt.Format] must be: HEX, hex, uint8, double, base64.'); boblsturm@0: end boblsturm@0: boblsturm@0: % return; boblsturm@0: boblsturm@0: % ****************************************************************************** boblsturm@0: function Engine = CoreHash(Data, Engine) boblsturm@0: % This methods uses the slower TYPECAST of Matlab boblsturm@0: boblsturm@0: % Consider the type and dimensions of the array to distinguish arrays with the boblsturm@0: % same data, but different shape: [0 x 0] and [0 x 1], [1,2] and [1;2], boblsturm@0: % DOUBLE(0) and SINGLE([0,0]): boblsturm@0: % < v016: [class, size, data]. BUG! 0 and zeros(1,1,0) had the same hash! boblsturm@0: % >= v016: [class, ndims, size, data] boblsturm@0: Engine.update([uint8(class(Data)), ... boblsturm@0: typecast(uint64([ndims(Data), size(Data)]), 'uint8')]); boblsturm@0: boblsturm@0: if issparse(Data) % Sparse arrays to struct: boblsturm@0: [S.Index1, S.Index2, S.Value] = find(Data); boblsturm@0: Engine = CoreHash(S, Engine); boblsturm@0: elseif isstruct(Data) % Hash for all array elements and fields: boblsturm@0: F = sort(fieldnames(Data)); % Ignore order of fields boblsturm@0: for iField = 1:length(F) % Loop over fields boblsturm@0: aField = F{iField}; boblsturm@0: Engine.update(uint8(aField)); boblsturm@0: for iS = 1:numel(Data) % Loop over elements of struct array boblsturm@0: Engine = CoreHash(Data(iS).(aField), Engine); boblsturm@0: end boblsturm@0: end boblsturm@0: elseif iscell(Data) % Get hash for all cell elements: boblsturm@0: for iS = 1:numel(Data) boblsturm@0: Engine = CoreHash(Data{iS}, Engine); boblsturm@0: end boblsturm@0: elseif isempty(Data) % Nothing to do boblsturm@0: elseif isnumeric(Data) boblsturm@0: if isreal(Data) boblsturm@0: Engine.update(typecast(Data(:), 'uint8')); boblsturm@0: else boblsturm@0: Engine.update(typecast(real(Data(:)), 'uint8')); boblsturm@0: Engine.update(typecast(imag(Data(:)), 'uint8')); boblsturm@0: end boblsturm@0: elseif islogical(Data) % TYPECAST cannot handle LOGICAL boblsturm@0: Engine.update(typecast(uint8(Data(:)), 'uint8')); boblsturm@0: elseif ischar(Data) % TYPECAST cannot handle CHAR boblsturm@0: Engine.update(typecast(uint16(Data(:)), 'uint8')); boblsturm@0: elseif isa(Data, 'function_handle') boblsturm@0: Engine = CoreHash(ConvertFuncHandle(Data), Engine); boblsturm@0: elseif (isobject(Data) || isjava(Data)) && ismethod(Data, 'hashCode') boblsturm@0: Engine = CoreHash(char(Data.hashCode), Engine); boblsturm@0: else % Most likely a user-defined object: boblsturm@0: try boblsturm@0: BasicData = ConvertObject(Data); boblsturm@0: catch ME boblsturm@0: error(['JSimon:', mfilename, ':BadDataType'], ... boblsturm@0: '%s: Cannot create elementary array for type: %s\n %s', ... boblsturm@0: mfilename, class(Data), ME.message); boblsturm@0: end boblsturm@0: boblsturm@0: try boblsturm@0: Engine = CoreHash(BasicData, Engine); boblsturm@0: catch ME boblsturm@0: if strcmpi(ME.identifier, 'MATLAB:recursionLimit') boblsturm@0: ME = MException(['JSimon:', mfilename, ':RecursiveType'], ... boblsturm@0: '%s: Cannot create hash for recursive data type: %s', ... boblsturm@0: mfilename, class(Data)); boblsturm@0: end boblsturm@0: throw(ME); boblsturm@0: end boblsturm@0: end boblsturm@0: boblsturm@0: % return; boblsturm@0: boblsturm@0: % ****************************************************************************** boblsturm@0: function FuncKey = ConvertFuncHandle(FuncH) boblsturm@0: % The subfunction ConvertFuncHandle converts function_handles to a struct boblsturm@0: % using the Matlab function FUNCTIONS. The output of this function changes boblsturm@0: % with the Matlab version, such that DataHash(@sin) replies different hashes boblsturm@0: % under Matlab 6.5 and 2009a. boblsturm@0: % An alternative is using the function name and name of the file for boblsturm@0: % function_handles, but this is not unique for nested or anonymous functions. boblsturm@0: % If the MATLABROOT is removed from the file's path, at least the hash of boblsturm@0: % Matlab's toolbox functions is (usually!) not influenced by the version. boblsturm@0: % Finally I'm in doubt if there is a unique method to hash function handles. boblsturm@0: % Please adjust the subfunction ConvertFuncHandles to your needs. boblsturm@0: boblsturm@0: % The Matlab version influences the conversion by FUNCTIONS: boblsturm@0: % 1. The format of the struct replied FUNCTIONS is not fixed, boblsturm@0: % 2. The full paths of toolbox function e.g. for @mean differ. boblsturm@0: FuncKey = functions(FuncH); boblsturm@0: boblsturm@0: % Include modification file time and file size. Suggested by Aslak Grinsted: boblsturm@0: if ~isempty(FuncKey.file) boblsturm@0: d = dir(FuncKey.file); boblsturm@0: if ~isempty(d) boblsturm@0: FuncKey.filebytes = d.bytes; boblsturm@0: FuncKey.filedate = d.datenum; boblsturm@0: end boblsturm@0: end boblsturm@0: boblsturm@0: % ALTERNATIVE: Use name and path. The part of the toolbox functions boblsturm@0: % is replaced such that the hash for @mean does not depend on the Matlab boblsturm@0: % version. boblsturm@0: % Drawbacks: Anonymous functions, nested functions... boblsturm@0: % funcStruct = functions(FuncH); boblsturm@0: % funcfile = strrep(funcStruct.file, matlabroot, ''); boblsturm@0: % FuncKey = uint8([funcStruct.function, ' ', funcfile]); boblsturm@0: boblsturm@0: % Finally I'm afraid there is no unique method to get a hash for a function boblsturm@0: % handle. Please adjust this conversion to your needs. boblsturm@0: boblsturm@0: % return; boblsturm@0: boblsturm@0: % ****************************************************************************** boblsturm@0: function DataBin = ConvertObject(DataObj) boblsturm@0: % Convert a user-defined object to a binary stream. There cannot be a unique boblsturm@0: % solution, so this part is left for the user... boblsturm@0: boblsturm@0: try % Perhaps a direct conversion is implemented: boblsturm@0: DataBin = uint8(DataObj); boblsturm@0: boblsturm@0: % Matt Raum had this excellent idea - unfortunately this function is boblsturm@0: % undocumented and might not be supported in te future: boblsturm@0: % DataBin = getByteStreamFromArray(DataObj); boblsturm@0: boblsturm@0: catch % Or perhaps this is better: boblsturm@0: WarnS = warning('off', 'MATLAB:structOnObject'); boblsturm@0: DataBin = struct(DataObj); boblsturm@0: warning(WarnS); boblsturm@0: end boblsturm@0: boblsturm@0: % return; boblsturm@0: boblsturm@0: % ****************************************************************************** boblsturm@0: function Out = fBase64_enc(In) boblsturm@0: % Encode numeric vector of UINT8 values to base64 string. boblsturm@0: % The intention of this is to create a shorter hash than the HEX format. boblsturm@0: % Therefore a padding with '=' characters is omitted on purpose. boblsturm@0: boblsturm@0: Pool = [65:90, 97:122, 48:57, 43, 47]; % [0:9, a:z, A:Z, +, /] boblsturm@0: v8 = [128; 64; 32; 16; 8; 4; 2; 1]; boblsturm@0: v6 = [32, 16, 8, 4, 2, 1]; boblsturm@0: boblsturm@0: In = reshape(In, 1, []); boblsturm@0: X = rem(floor(In(ones(8, 1), :) ./ v8(:, ones(length(In), 1))), 2); boblsturm@0: Y = reshape([X(:); zeros(6 - rem(numel(X), 6), 1)], 6, []); boblsturm@0: Out = char(Pool(1 + v6 * Y)); boblsturm@0: boblsturm@0: % return; boblsturm@0: boblsturm@0: % ****************************************************************************** boblsturm@0: function Ex = FileExist_L(FileName) boblsturm@0: % A more reliable version of EXIST(FileName, 'file'): boblsturm@0: dirFile = dir(FileName); boblsturm@0: if length(dirFile) == 1 boblsturm@0: Ex = ~(dirFile.isdir); boblsturm@0: else boblsturm@0: Ex = false; boblsturm@0: end boblsturm@0: boblsturm@0: % return; boblsturm@0: boblsturm@0: % ****************************************************************************** boblsturm@0: function R = Version_L() boblsturm@0: % The output differs between versions of this function. So give the user a boblsturm@0: % chance to recognize the version: boblsturm@0: % 1: 01-May-2011, Initial version boblsturm@0: % 2: 15-Feb-2015, The number of dimensions is considered in addition. boblsturm@0: % In version 1 these variables had the same hash: boblsturm@0: % zeros(1,1) and zeros(1,1,0), complex(0) and zeros(1,1,0,0) boblsturm@0: % 3: 29-Jun-2015, Struct arrays are processed field by field and not element boblsturm@0: % by element, because this is much faster. In consequence the hash value boblsturm@0: % differs, if the input contains a struct. boblsturm@0: % 4: 28-Feb-2016 15:20, same output as GetMD5 for MD5 sums. Therefore the boblsturm@0: % dimensions are casted to UINT64 at first. boblsturm@0: R.HashVersion = 4; boblsturm@0: R.Date = [2016, 2, 28]; boblsturm@0: boblsturm@0: R.HashMethod = {}; boblsturm@0: try boblsturm@0: Provider = java.security.Security.getProviders; boblsturm@0: for iProvider = 1:numel(Provider) boblsturm@0: S = char(Provider(iProvider).getServices); boblsturm@0: Index = strfind(S, 'MessageDigest.'); boblsturm@0: for iDigest = 1:length(Index) boblsturm@0: Digest = strtok(S(Index(iDigest):end)); boblsturm@0: Digest = strrep(Digest, 'MessageDigest.', ''); boblsturm@0: R.HashMethod = cat(2, R.HashMethod, {Digest}); boblsturm@0: end boblsturm@0: end boblsturm@0: catch ME boblsturm@0: fprintf(2, '%s\n', ME.message); boblsturm@0: R.HashMethod = 'error'; boblsturm@0: end boblsturm@0: boblsturm@0: % return; boblsturm@0: boblsturm@0: % ****************************************************************************** boblsturm@0: function Error_L(ID, varargin) boblsturm@0: boblsturm@0: error(['JSimon:', mfilename, ':', ID], ['*** %s: ', varargin{1}], ... boblsturm@0: mfilename, varargin{2:nargin - 1}); boblsturm@0: boblsturm@0: % return;