├── README.md ├── dumpmsgpack.m ├── dumpmsgpack_test.m ├── parsemsgpack.m └── parsemsgpack_test.m /README.md: -------------------------------------------------------------------------------- 1 | # A MessagePack implementation for Matlab and Octave 2 | 3 | The code is written in pure Matlab, and has no dependencies beyond Matlab itself. And it works in recent versions of Octave, too. 4 | 5 | The files in this repository are taken from [Transplant](https://github.com/bastibe/transplant). 6 | 7 | ## Basic Usage: 8 | ```matlab 9 | data = {'life, the universe, and everything', struct('the_answer', 42)}; 10 | bytes = dumpmsgpack(data) 11 | data = parsemsgpack(bytes) 12 | % returns: {'life, the universe, and everything', containers.Map('the_answer', 42)} 13 | ``` 14 | 15 | ## Converting Matlab to MsgPack: 16 | 17 | | Matlab | MsgPack | 18 | | -------------- | ------------------------- | 19 | | string | string | 20 | | scalar | number | 21 | | logical | `true`/`false` | 22 | | vector | array of numbers | 23 | | uint8 vector | bin | 24 | | matrix | array of array of numbers | 25 | | empty matrix | nil | 26 | | cell array | array | 27 | | cell matrix | array of arrays | 28 | | struct | map | 29 | | containers.Map | map | 30 | | struct array | array of maps | 31 | | handles | raise error | 32 | 33 | There is no way of encoding exts 34 | 35 | ## Converting MsgPack to Matlab 36 | 37 | | MsgPack | Matlab | 38 | | -------------- | -------------- | 39 | | string | string | 40 | | number | scalar | 41 | | `true`/`false` | logical | 42 | | nil | empty matrix | 43 | | array | cell array | 44 | | map | containers.Map | 45 | | bin | uint8 | 46 | | ext | uint8 | 47 | 48 | Note that since `structs` don't support arbitrary field names, they can't be used for representing `maps`. We use `containers.Map` instead. 49 | 50 | ## Tests 51 | ```matlab 52 | runtests() 53 | ``` 54 | 55 | ## License 56 | 57 | MATLAB (R) is copyright of the Mathworks 58 | 59 | Copyright (c) 2014 Bastian Bechtold 60 | All rights reserved. 61 | 62 | Redistribution and use in source and binary forms, with or without 63 | modification, are permitted provided that the following conditions are 64 | met: 65 | 66 | 1. Redistributions of source code must retain the above copyright 67 | notice, this list of conditions and the following disclaimer. 68 | 69 | 2. Redistributions in binary form must reproduce the above copyright 70 | notice, this list of conditions and the following disclaimer in the 71 | documentation and/or other materials provided with the 72 | distribution. 73 | 74 | 3. Neither the name of the copyright holder nor the names of its 75 | contributors may be used to endorse or promote products derived 76 | from this software without specific prior written permission. 77 | 78 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 79 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 80 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 81 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 82 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 83 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 84 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 85 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 86 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 87 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 88 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 89 | 90 | -------------------------------------------------------------------------------- /dumpmsgpack.m: -------------------------------------------------------------------------------- 1 | %DUMPMSGPACK dumps Matlab data structures as a msgpack data 2 | % DUMPMSGPACK(DATA) 3 | % recursively walks through DATA and creates a msgpack byte buffer from it. 4 | % - strings are converted to strings 5 | % - scalars are converted to numbers 6 | % - logicals are converted to `true` and `false` 7 | % - arrays are converted to arrays of numbers 8 | % - matrices are converted to arrays of arrays of numbers 9 | % - empty matrices are converted to nil 10 | % - cell arrays are converted to arrays 11 | % - cell matrices are converted to arrays of arrays 12 | % - struct arrays are converted to arrays of maps 13 | % - structs and container.Maps are converted to maps 14 | % - function handles and matlab objects will raise an error. 15 | % 16 | % There is no way of encoding bins or exts 17 | 18 | % (c) 2016 Bastian Bechtold 19 | % This code is licensed under the BSD 3-clause license 20 | 21 | function msgpack = dumpmsgpack(data) 22 | msgpack = dump(data); 23 | % collect all parts in a cell array to avoid frequent uint8 24 | % concatenations. 25 | msgpack = [msgpack{:}]; 26 | end 27 | 28 | function msgpack = dump(data) 29 | % convert numeric matrices to cell matrices since msgpack doesn't know matrices 30 | if (isnumeric(data) || islogical(data)) && ... 31 | ~(isvector(data) && isa(data, 'uint8')) && ~isscalar(data) && ~isempty(data) 32 | data = num2cell(data); 33 | end 34 | % convert character matrices to cell of strings or cell matrices 35 | if ischar(data) && ~(isvector(data)||isempty(data)) && ndims(data) == 2 36 | data = cellstr(data); 37 | elseif ischar(data) && ~isvector(data) 38 | data = num2cell(data); 39 | end 40 | % convert struct arrays to cell of structs 41 | if isstruct(data) && ~isscalar(data) 42 | data = num2cell(data); 43 | end 44 | % standardize on always using maps instead of structs 45 | if isstruct(data) 46 | if ~isempty(fieldnames(data)) 47 | data = containers.Map(fieldnames(data), struct2cell(data)); 48 | else 49 | data = containers.Map(); 50 | end 51 | end 52 | 53 | if isnumeric(data) && isempty(data) 54 | msgpack = {uint8(192)}; % encode nil 55 | elseif isa(data, 'uint8') && numel(data) > 1 56 | msgpack = dumpbin(data); 57 | elseif islogical(data) 58 | if data 59 | msgpack = {uint8(195)}; % encode true 60 | else 61 | msgpack = {uint8(194)}; % encode false 62 | end 63 | elseif isinteger(data) 64 | msgpack = {dumpinteger(data)}; 65 | elseif isnumeric(data) 66 | msgpack = {dumpfloat(data)}; 67 | elseif ischar(data) 68 | msgpack = dumpstring(data); 69 | elseif iscell(data) 70 | msgpack = dumpcell(data); 71 | elseif isa(data, 'containers.Map') 72 | msgpack = dumpmap(data); 73 | else 74 | error('transplant:dumpmsgpack:unknowntype', ... 75 | ['Unknown type "' class(data) '"']); 76 | end 77 | end 78 | 79 | function bytes = scalar2bytes(value) 80 | % reverse byte order to convert from little endian to big endian 81 | bytes = typecast(swapbytes(value), 'uint8'); 82 | end 83 | 84 | function msgpack = dumpinteger(value) 85 | % if the values are small enough, encode as fixnum: 86 | if value >= 0 && value < 128 87 | % first bit is 0, last 7 bits are value 88 | msgpack = uint8(value); 89 | return 90 | elseif value < 0 && value > -32 91 | % first three bits are 111, last 5 bytes are value 92 | msgpack = typecast(int8(value), 'uint8'); 93 | return 94 | end 95 | 96 | % otherwise, encode by type: 97 | switch class(value) 98 | case 'uint8' % encode as uint8 99 | msgpack = uint8([204, value]); 100 | case 'uint16' % encode as uint16 101 | msgpack = uint8([205, scalar2bytes(value)]); 102 | case 'uint32' % encode as uint32 103 | msgpack = uint8([206, scalar2bytes(value)]); 104 | case 'uint64' % encode as uint64 105 | msgpack = uint8([207, scalar2bytes(value)]); 106 | case 'int8' % encode as int8 107 | msgpack = uint8([208, scalar2bytes(value)]); 108 | case 'int16' % encode as int16 109 | msgpack = uint8([209, scalar2bytes(value)]); 110 | case 'int32' % encode as int32 111 | msgpack = uint8([210, scalar2bytes(value)]); 112 | case 'int64' % encode as int64 113 | msgpack = uint8([211, scalar2bytes(value)]); 114 | otherwise 115 | error('transplant:dumpmsgpack:unknowninteger', ... 116 | ['Unknown integer type "' class(value) '"']); 117 | end 118 | end 119 | 120 | function msgpack = dumpfloat(value) 121 | % do double first, as it is more common in Matlab 122 | if isa(value, 'double') % encode as float64 123 | msgpack = uint8([203, scalar2bytes(value)]); 124 | elseif isa(value, 'single') % encode as float32 125 | msgpack = uint8([202, scalar2bytes(value)]); 126 | else 127 | error('transplant:dumpmsgpack:unknownfloat', ... 128 | ['Unknown float type "' class(value) '"']); 129 | end 130 | end 131 | 132 | function msgpack = dumpstring(value) 133 | b10100000 = 160; 134 | 135 | encoded = unicode2native(value, 'utf-8'); 136 | len = length(encoded); 137 | 138 | if len < 32 % encode as fixint: 139 | % first three bits are 101, last 5 are length: 140 | msgpack = {uint8(bitor(len, b10100000)), encoded}; 141 | elseif len < 256 % encode as str8 142 | msgpack = {uint8([217, len]), encoded}; 143 | elseif len < 2^16 % encode as str16 144 | msgpack = {uint8(218), scalar2bytes(uint16(len)), encoded}; 145 | elseif len < 2^32 % encode as str32 146 | msgpack = {uint8(219), scalar2bytes(uint32(len)), encoded}; 147 | else 148 | error('transplant:dumpmsgpack:stringtoolong', ... 149 | sprintf('String is too long (%d bytes)', len)); 150 | end 151 | end 152 | 153 | function msgpack = dumpbin(value) 154 | len = length(value); 155 | if len < 256 % encode as bin8 156 | msgpack = {uint8([196, len]) value(:)'}; 157 | elseif len < 2^16 % encode as bin16 158 | msgpack = {uint8(197), scalar2bytes(uint16(len)), value(:)'}; 159 | elseif len < 2^32 % encode as bin32 160 | msgpack = {uint8(198), scalar2bytes(uint32(len)), value(:)'}; 161 | else 162 | error('transplant:dumpmsgpack:bintoolong', ... 163 | sprintf('Bin is too long (%d bytes)', len)); 164 | end 165 | end 166 | 167 | function msgpack = dumpcell(value) 168 | b10010000 = 144; 169 | 170 | % Msgpack can only work with 1D-arrays. Thus, Convert a 171 | % multidimensional AxBxC array into a cell-of-cell-of-cell, so 172 | % that indexing value{a, b, c} becomes value{a}{b}{c}. 173 | if length(value) ~= prod(size(value)) 174 | for n=ndims(value):-1:2 175 | value = cellfun(@squeeze, num2cell(value, n), ... 176 | 'uniformoutput', false); 177 | end 178 | end 179 | 180 | % write header 181 | len = length(value); 182 | if len < 16 % encode as fixarray 183 | % first four bits are 1001, last 4 are length 184 | msgpack = {uint8(bitor(len, b10010000))}; 185 | elseif len < 2^16 % encode as array16 186 | msgpack = {uint8(220), scalar2bytes(uint16(len))}; 187 | elseif len < 2^32 % encode as array32 188 | msgpack = {uint8(221), scalar2bytes(uint32(len))}; 189 | else 190 | error('transplant:dumpmsgpack:arraytoolong', ... 191 | sprintf('Array is too long (%d elements)', len)); 192 | end 193 | 194 | % write values 195 | for n=1:len 196 | stuff = dump(value{n}); 197 | msgpack = [msgpack stuff{:}]; 198 | end 199 | end 200 | 201 | function msgpack = dumpmap(value) 202 | b10000000 = 128; 203 | 204 | % write header 205 | len = length(value); 206 | if len < 16 % encode as fixmap 207 | % first four bits are 1000, last 4 are length 208 | msgpack = {uint8(bitor(len, b10000000))}; 209 | elseif len < 2^16 % encode as map16 210 | msgpack = {uint8(222), scalar2bytes(uint16(len))}; 211 | elseif len < 2^32 % encode as map32 212 | msgpack = {uint8(223), scalar2bytes(uint32(len))}; 213 | else 214 | error('transplant:dumpmsgpack:maptoolong', ... 215 | sprintf('Map is too long (%d elements)', len)); 216 | end 217 | 218 | % write key-value pairs 219 | keys = value.keys(); 220 | values = value.values(); 221 | for n=1:len 222 | keystuff = dump(keys{n}); 223 | valuestuff = dump(values{n}); 224 | msgpack = [msgpack, keystuff{:}, valuestuff{:}]; 225 | end 226 | end 227 | -------------------------------------------------------------------------------- /dumpmsgpack_test.m: -------------------------------------------------------------------------------- 1 | %% positive integer dumping 2 | if dumpmsgpack(int8(0)) ~= uint8(0) 3 | error('Dumping 0 failed') 4 | end 5 | if any(dumpmsgpack(uint8(1)) ~= uint8(1)) 6 | error('Dumping positive fixnum failed') 7 | end 8 | if any(dumpmsgpack(uint8(128)) ~= uint8([204, 128])) 9 | error('Dumping uint8 failed') 10 | end 11 | if any(dumpmsgpack(uint16(256)) ~= uint8([205, 1, 0])) 12 | error('Dumping uint16 failed') 13 | end 14 | if any(dumpmsgpack(uint32(2^16)) ~= uint8([206, 0, 1, 0, 0])) 15 | error('Dumping uint32 failed') 16 | end 17 | if any(dumpmsgpack(uint64(2^32)) ~= uint8([207, 0, 0, 0, 1, 0, 0, 0, 0])) 18 | error('Dumping uint64 failed') 19 | end 20 | 21 | %% negative integer dumping 22 | if dumpmsgpack(int8(-1)) ~= uint8(255) 23 | error('Dumping negative fixnum failed') 24 | end 25 | if any(dumpmsgpack(int8(-128)) ~= uint8([208, 128])) 26 | error('Dumping int8 failed') 27 | end 28 | if any(dumpmsgpack(int16(-256)) ~= uint8([209, 255, 0])) 29 | error('Dumping int16 failed') 30 | end 31 | if any(dumpmsgpack(int32(-2^16)) ~= uint8([210, 255, 255, 0, 0])) 32 | error('Dumping int32 failed') 33 | end 34 | if any(dumpmsgpack(int64(-2^32)) ~= uint8([211, 255, 255, 255, 255, 0, 0, 0, 0])) 35 | error('Dumping int64 failed') 36 | end 37 | 38 | %% float dumping 39 | if any(dumpmsgpack(single(1.5)) ~= uint8([202, 63, 192, 0, 0])) 40 | error('Dumping float32 failed') 41 | end 42 | if any(dumpmsgpack(double(1.5)) ~= uint8([203, 63, 248, 0, 0, 0, 0, 0, 0])) 43 | error('Dumping float64 failed') 44 | end 45 | 46 | %% string dumping 47 | if any(dumpmsgpack('foo') ~= uint8([163, 102, 111, 111])) 48 | error('Dumping fixstr failed') 49 | end 50 | if any(dumpmsgpack(repmat('a', [1, 32])) ~= uint8([217, 32, ones(1, 32)*'a'])) 51 | error('Dumping str8 failed') 52 | end 53 | if any(dumpmsgpack(repmat('a', [1, 2^8])) ~= uint8([218, 1, 0, ones(1, 2^8)*'a'])) 54 | error('Dumping str16 failed') 55 | end 56 | if any(dumpmsgpack(repmat('a', [1, 2^16])) ~= uint8([219, 0, 1, 0, 0, ones(1, 2^16)*'a'])) 57 | error('Dumping str16 failed') 58 | end 59 | 60 | %% bin dumping 61 | if any(dumpmsgpack(repmat(uint8(42), [1, 32])) ~= uint8([196, 32, ones(1, 32)*42])) 62 | error('Dumping str8 failed') 63 | end 64 | if any(dumpmsgpack(repmat(uint8(42), [1, 2^8])) ~= uint8([197, 1, 0, ones(1, 2^8)*42])) 65 | error('Dumping str16 failed') 66 | end 67 | if any(dumpmsgpack(repmat(uint8(42), [1, 2^16])) ~= uint8([198, 0, 1, 0, 0, ones(1, 2^16)*42])) 68 | error('Dumping str16 failed') 69 | end 70 | 71 | %% array dumping 72 | if any(dumpmsgpack({uint8(1), uint8(2)}) ~= uint8([146, 1, 2])) 73 | error('Dumping fixarray failed') 74 | end 75 | if any(dumpmsgpack(num2cell(repmat(uint8(42), [1, 16]))) ~= uint8([220, 0, 16, repmat(42, [1, 16])])) 76 | error('Dumping array16 failed') 77 | end 78 | % takes too long: 79 | % if any(dumpmsgpack(num2cell(repmat(uint8(42), [1, 2^16]))) ~= uint8([221, 0, 1, 0, 0 repmat(42, [1, 2^16])])) 80 | % error('Dumping array32 failed') 81 | % end 82 | 83 | %% map dumping 84 | if any(dumpmsgpack(struct('one', uint8(1), 'two', uint8(2))) ~= uint8([130, dumpmsgpack('one'), 1, dumpmsgpack('two'), 2])) 85 | error('Dumping fixmap failed') 86 | end 87 | data = struct(); 88 | msgpack = uint8([]); 89 | for n=[1 10 11 12 13 14 15 16 2 3 4 5 6 7 8 9] % default struct field order 90 | data.(['x' num2str(n)]) = uint8(n); 91 | msgpack = [msgpack dumpmsgpack(['x' num2str(n)]) uint8(n)]; 92 | end 93 | if any(dumpmsgpack(data) ~= uint8([222, 0, 16, msgpack])) 94 | error('Dumping map16 failed') 95 | end 96 | % map32 takes too long 97 | -------------------------------------------------------------------------------- /parsemsgpack.m: -------------------------------------------------------------------------------- 1 | %PARSEMSGPACK parses a msgpack byte buffer into Matlab data structures 2 | % PARSEMSGPACK(BYTES) 3 | % reads BYTES as msgpack data, and creates Matlab data structures 4 | % from it. The number of bytes consumed by the parsemsgpack call 5 | % is returned in the variable IDX. 6 | % - strings are converted to strings 7 | % - numbers are converted to appropriate numeric values 8 | % - true, false are converted to logical 1, 0 9 | % - nil is converted to [] 10 | % - arrays are converted to cell arrays 11 | % - maps are converted to containers.Map 12 | 13 | % (c) 2016 Bastian Bechtold 14 | % voluntary contributions made by Christopher Nadler (cnadler86) 15 | % This code is licensed under the BSD 3-clause license 16 | 17 | function [obj, idx] = parsemsgpack(bytes) 18 | [obj, idx] = parse(uint8(bytes(:)), 1); 19 | end 20 | 21 | function [obj, idx] = parse(bytes, idx) 22 | % masks: 23 | b10000000 = uint8(128); 24 | b11100000 = uint8(224); 25 | b00011111 = uint8(31); 26 | b11110000 = uint8(240); 27 | b00001111 = uint8(15); 28 | % values: 29 | b00000000 = uint8(0); 30 | b10010000 = uint8(144); 31 | b10100000 = uint8(160); 32 | 33 | currentbyte = bytes(idx); 34 | 35 | if bitand(b10000000, currentbyte) == b00000000 36 | % decode positive fixint 37 | obj = uint8(currentbyte); 38 | idx = idx + 1; 39 | return 40 | elseif bitand(b11100000, currentbyte) == b11100000 41 | % decode negative fixint 42 | obj = typecast(currentbyte, 'int8'); 43 | idx = idx + 1; 44 | return 45 | elseif bitand(b11110000, currentbyte) == b10000000 46 | % decode fixmap 47 | len = double(bitand(b00001111, currentbyte)); 48 | [obj, idx] = parsemap(len, bytes, idx+1); 49 | return 50 | elseif bitand(b11110000, currentbyte) == b10010000 51 | % decode fixarray 52 | len = double(bitand(b00001111, currentbyte)); 53 | [obj, idx] = parsearray(len, bytes, idx+1); 54 | return 55 | elseif bitand(b11100000, currentbyte) == b10100000 56 | % decode fixstr 57 | len = double(bitand(b00011111, currentbyte)); 58 | [obj, idx] = parsestring(len, bytes, idx + 1); 59 | return 60 | end 61 | 62 | switch currentbyte 63 | case uint8(192) % nil 64 | obj = []; 65 | idx = idx+1; 66 | case uint8(194) % false 67 | obj = false; 68 | idx = idx+1; 69 | case uint8(195) % true 70 | obj = true; 71 | idx = idx+1; 72 | case uint8(196) % bin8 73 | len = double(bytes(idx+1)); 74 | [obj, idx] = parsebytes(len, bytes, idx+2); 75 | case uint8(197) % bin16 76 | len = double(bytes2scalar(bytes(idx+1:idx+2), 'uint16')); 77 | [obj, idx] = parsebytes(len, bytes, idx+3); 78 | case uint8(198) % bin32 79 | len = double(bytes2scalar(bytes(idx+1:idx+4), 'uint32')); 80 | [obj, idx] = parsebytes(len, bytes, idx+5); 81 | case uint8(199) % ext8 82 | len = double(bytes(idx+1)); 83 | [obj, idx] = parseext(len, bytes, idx+2); 84 | case uint8(200) % ext16 85 | len = double(bytes2scalar(bytes(idx+1:idx+2), 'uint16')); 86 | [obj, idx] = parseext(len, bytes, idx+3); 87 | case uint8(201) % ext32 88 | len = double(bytes2scalar(bytes(idx+1:idx+4), 'uint32')); 89 | [obj, idx] = parseext(len, bytes, idx+5); 90 | case uint8(202) % float32 91 | obj = bytes2scalar(bytes(idx+1:idx+4), 'single'); 92 | idx = idx+5; 93 | case uint8(203) % float64 94 | obj = bytes2scalar(bytes(idx+1:idx+8), 'double'); 95 | idx = idx+9; 96 | case uint8(204) % uint8 97 | obj = bytes(idx+1); 98 | idx = idx+2; 99 | case uint8(205) % uint16 100 | obj = bytes2scalar(bytes(idx+1:idx+2), 'uint16'); 101 | idx = idx+3; 102 | case uint8(206) % uint32 103 | obj = bytes2scalar(bytes(idx+1:idx+4), 'uint32'); 104 | idx = idx+5; 105 | case uint8(207) % uint64 106 | obj = bytes2scalar(bytes(idx+1:idx+8), 'uint64'); 107 | idx = idx+9; 108 | case uint8(208) % int8 109 | obj = bytes2scalar(bytes(idx+1), 'int8'); 110 | idx = idx+2; 111 | case uint8(209) % int16 112 | obj = bytes2scalar(bytes(idx+1:idx+2), 'int16'); 113 | idx = idx+3; 114 | case uint8(210) % int32 115 | obj = bytes2scalar(bytes(idx+1:idx+4), 'int32'); 116 | idx = idx+5; 117 | case uint8(211) % int64 118 | obj = bytes2scalar(bytes(idx+1:idx+8), 'int64'); 119 | idx = idx+9; 120 | case uint8(212) % fixext1 121 | [obj, idx] = parseext(1, bytes, idx+1); 122 | case uint8(213) % fixext2 123 | [obj, idx] = parseext(2, bytes, idx+1); 124 | case uint8(214) % fixext4 125 | [obj, idx] = parseext(4, bytes, idx+1); 126 | case uint8(215) % fixext8 127 | [obj, idx] = parseext(8, bytes, idx+1); 128 | case uint8(216) % fixext16 129 | [obj, idx] = parseext(16, bytes, idx+1); 130 | case uint8(217) % str8 131 | len = double(bytes(idx+1)); 132 | [obj, idx] = parsestring(len, bytes, idx+2); 133 | case uint8(218) % str16 134 | len = double(bytes2scalar(bytes(idx+1:idx+2), 'uint16')); 135 | [obj, idx] = parsestring(len, bytes, idx+3); 136 | case uint8(219) % str32 137 | len = double(bytes2scalar(bytes(idx+1:idx+4), 'uint32')); 138 | [obj, idx] = parsestring(len, bytes, idx+5); 139 | case uint8(220) % array16 140 | len = double(bytes2scalar(bytes(idx+1:idx+2), 'uint16')); 141 | [obj, idx] = parsearray(len, bytes, idx+3); 142 | case uint8(221) % array32 143 | len = double(bytes2scalar(bytes(idx+1:idx+4), 'uint32')); 144 | [obj, idx] = parsearray(len, bytes, idx+5); 145 | case uint8(222) % map16 146 | len = double(bytes2scalar(bytes(idx+1:idx+2), 'uint16')); 147 | [obj, idx] = parsemap(len, bytes, idx+3); 148 | case uint8(223) % map32 149 | len = double(bytes2scalar(bytes(idx+1:idx+4), 'uint32')); 150 | [obj, idx] = parsemap(len, bytes, idx+5); 151 | otherwise 152 | error('transplant:parsemsgpack:unknowntype', ... 153 | ['Unknown type "' dec2bin(currentbyte) '"']); 154 | end 155 | end 156 | 157 | function value = bytes2scalar(bytes, type) 158 | % reverse byte order to convert from little-endian to big-endian 159 | value = typecast(bytes(end:-1:1), type); 160 | end 161 | 162 | function [str, idx] = parsestring(len, bytes, idx) 163 | str = native2unicode(bytes(idx:idx+len-1)', 'utf-8'); 164 | idx = idx + len; 165 | end 166 | 167 | function [out, idx] = parsebytes(len, bytes, idx) 168 | out = bytes(idx:idx+len-1); 169 | idx = idx + len; 170 | end 171 | 172 | function [out, idx] = parseext(len, bytes, idx) 173 | out.type = bytes(idx); 174 | out.data = bytes(idx+1:idx+len); 175 | idx = idx + len + 1; 176 | end 177 | 178 | function [out, idx] = parsearray(len, bytes, idx) 179 | out = cell(1, len); 180 | for n=1:len 181 | [out{n}, idx] = parse(bytes, idx); 182 | end 183 | end 184 | 185 | function [out, idx] = parsemap(len, bytes, idx) 186 | out = containers.Map(); 187 | for n=1:len 188 | [key, idx] = parse(bytes, idx); 189 | [out(key), idx] = parse(bytes, idx); 190 | end 191 | end 192 | -------------------------------------------------------------------------------- /parsemsgpack_test.m: -------------------------------------------------------------------------------- 1 | %% positive integer parsing 2 | if parsemsgpack(uint8(0)) ~= uint8(0) 3 | error('Parsing 0 failed') 4 | end 5 | if any(parsemsgpack(uint8(1)) ~= uint8(1)) 6 | error('Parsing positive fixnum failed') 7 | end 8 | if any(parsemsgpack(uint8([204, 128])) ~= uint8(128)) 9 | error('Parsing uint8 failed') 10 | end 11 | if any(parsemsgpack(uint8([205, 1, 0])) ~= uint16(256)) 12 | error('Parsing uint16 failed') 13 | end 14 | if any(parsemsgpack(uint8([206, 0, 1, 0, 0])) ~= uint32(2^16)) 15 | error('Parsing uint32 failed') 16 | end 17 | if any(parsemsgpack(uint8([207, 0, 0, 0, 1, 0, 0, 0, 0])) ~= uint64(2^32)) 18 | error('Parsing uint64 failed') 19 | end 20 | 21 | %% negative integer parsing 22 | if any(parsemsgpack(uint8(255)) ~= int8(-1)) 23 | error('Parsing negative fixnum failed') 24 | end 25 | if any(parsemsgpack(uint8([208, 128])) ~= int8(-128)) 26 | error('Parsing int8 failed') 27 | end 28 | if any(parsemsgpack(uint8([209, 255, 0])) ~= int16(-256)) 29 | error('Parsing int16 failed') 30 | end 31 | if any(parsemsgpack(uint8([210, 255, 255, 0, 0])) ~= int32(-2^16)) 32 | error('Parsing int32 failed') 33 | end 34 | if any(parsemsgpack(uint8([211, 255, 255, 255, 255, 0, 0, 0, 0])) ~= int64(-2^32)) 35 | error('Parsing int64 failed') 36 | end 37 | 38 | %% float parsing 39 | if any(parsemsgpack(uint8([202, 63, 192, 0, 0])) ~= single(1.5)) 40 | error('Parsing float32 failed') 41 | end 42 | if any(parsemsgpack(uint8([203, 63, 248, 0, 0, 0, 0, 0, 0])) ~= double(1.5)) 43 | error('Parsing float64 failed') 44 | end 45 | 46 | %% string parsing 47 | if any(parsemsgpack(uint8([163, 102, 111, 111])) ~= 'foo') 48 | error('Parsing fixstr failed') 49 | end 50 | if any(parsemsgpack(uint8([217, 32, ones(1, 32)*'a'])) ~= repmat('a', [1, 32])) 51 | error('Parsing str8 failed') 52 | end 53 | if any(parsemsgpack(uint8([218, 1, 0, ones(1, 2^8)*'a'])) ~= repmat('a', [1, 2^8])) 54 | error('Parsing str16 failed') 55 | end 56 | if any(parsemsgpack(uint8([219, 0, 1, 0, 0, ones(1, 2^16)*'a'])) ~= repmat('a', [1, 2^16])) 57 | error('Parsing str16 failed') 58 | end 59 | 60 | %% bin parsing 61 | if any(parsemsgpack(uint8([196, 32, ones(1, 32)*42])) ~= repmat(uint8(42), [1, 32])) 62 | error('Parsing str8 failed') 63 | end 64 | if any(parsemsgpack(uint8([197, 1, 0, ones(1, 2^8)*42])) ~= repmat(uint8(42), [1, 2^8])) 65 | error('Parsing str16 failed') 66 | end 67 | if any(parsemsgpack(uint8([198, 0, 1, 0, 0, ones(1, 2^16)*42])) ~= repmat(uint8(42), [1, 2^16])) 68 | error('Parsing str16 failed') 69 | end 70 | 71 | %% array parsing 72 | c = parsemsgpack(uint8([146, 1, 2])); 73 | d = {uint8(1), uint8(2)}; 74 | for n=1:max([length(c), length(d)]) 75 | if c{n} ~= d{n} 76 | error('Parsing fixarray failed') 77 | end 78 | end 79 | c = parsemsgpack(uint8([220, 0, 16, repmat(42, [1, 16])])); 80 | d = num2cell(repmat(uint8(42), [1, 16])); 81 | for n=1:max([length(c), length(d)]) 82 | if c{n} ~= d{n} 83 | error('Parsing array16 failed') 84 | end 85 | end 86 | % array32 takes too long 87 | 88 | %% map parsing 89 | c = parsemsgpack(uint8([130, dumpmsgpack('one'), 1, dumpmsgpack('two'), 2])); 90 | d = struct('one', uint8(1), 'two', uint8(2)); 91 | f = [fieldnames(d)' c.keys()]; 92 | for n=1:length(f) 93 | if c(f{n}) ~= d.(f{n}) 94 | error('Parsing fixmap failed') 95 | end 96 | end 97 | data = struct(); 98 | msgpack = uint8([222, 0, 16]); 99 | for n=[1 10 11 12 13 14 15 16 2 3 4 5 6 7 8 9] % default struct field order 100 | data.(['x' num2str(n)]) = uint8(n); 101 | msgpack = [msgpack dumpmsgpack(['x' num2str(n)]) uint8(n)]; 102 | end 103 | c = parsemsgpack(msgpack); 104 | d = data; 105 | f = [fieldnames(d)' c.keys()]; 106 | for n=1:length(f) 107 | if c(f{n}) ~= d.(f{n}) 108 | error('Parsing map16 failed') 109 | end 110 | end 111 | % map32 takes too long 112 | --------------------------------------------------------------------------------