Lutz Roeder 4 лет назад
Родитель
Сommit
8aed3211a9
2 измененных файлов с 483 добавлено и 137 удалено
  1. 467 136
      source/kmodel.js
  2. 16 1
      test/models.json

+ 467 - 136
source/kmodel.js

@@ -5,29 +5,13 @@ var base = base || require('./base');
 kmodel.ModelFactory = class {
 
     match(context) {
-        const stream = context.stream;
-        const signatures = [
-            { identifer: [ 0x03, 0x00, 0x00, 0x00 ], match: 'kmodel.KPU' },
-            { identifer: [ 0x4C, 0x44, 0x4D, 0x4B ], match: 'kmodel.LDMK'}
-        ];
-        const signature = signatures.find((signature) => signature.identifer.length <= stream.length && stream.peek(signature.identifer.length).every((value, index) => signature.identifer[index] === undefined || signature.identifer[index] === value));
-        if (signature) {
-            return signature.match;
-        }
-        return undefined;
+        return kmodel.Reader.open(context.stream);
     }
 
     open(context, match) {
         return Promise.resolve().then(() => {
-            const stream = context.stream;
-            switch (match) {
-                case 'kmodel.KPU': {
-                    return new kmodel.Model(new kmodel.KPU(stream));
-                }
-                case 'kmodel.LDMK': {
-                    return new kmodel.Model(new kmodel.LDMK(stream));
-                }
-            }
+            const reader = match;
+            return new kmodel.Model(reader);
         });
     }
 };
@@ -73,7 +57,17 @@ kmodel.Node = class {
 
     constructor(layer) {
         this._location = layer.location;
-        this._type = { name: layer.typename, category: layer.category };
+        this._type = layer.type;
+        this._attributes = [];
+        for (const entry of Object.entries(layer)) {
+            const name = entry[0];
+            if (name === 'type' || name === 'location' || name === 'params') {
+                continue;
+            }
+            const value = entry[1];
+            const attribute = new kmodel.Attribute(name, value);
+            this._attributes.push(attribute);
+        }
     }
 
     get location() {
@@ -97,133 +91,470 @@ kmodel.Node = class {
     }
 
     get attributes() {
-        return [];
+        return this._attributes;
     }
 };
 
+kmodel.Attribute = class {
 
-kmodel.KPU = class {
+    constructor(name, value) {
+        this._name = name;
+        this._value = value;
+    }
 
-    constructor(stream) {
-        const reader = new base.BinaryReader(stream);
-        this.version = reader.uint32();
-        /* const flags = */ reader.uint32();
-        /* const arch = */ reader.uint32();
-        this.layers = new Array(reader.uint32());
-        /* const max_start_address = */ reader.uint32();
-        /* const main_mem_usage = */ reader.uint32();
-        const outputs = new Array(reader.uint32());
-        for (let i = 0; i < outputs.length; i++) {
-            outputs[i] = {
-                address: reader.uint32(),
-                size: reader.uint32()
-            };
-        }
-        for (let i = 0; i < this.layers.length; i++) {
-            this.layers[i] = {
-                location: i,
-                type: reader.uint32(),
-                body_size: reader.uint32()
-            };
-        }
-        let offset = reader.position;
-        for (const layer of this.layers) {
-            layer.offset = offset;
-            offset += layer.body_size;
-            // layer.body = reader.read(layer.body_size);
-            // delete layer.body_size;
-        }
-        const types = new Map();
-        const register = (type, name, category, callback) => {
-            types.set(type, { name: name, category: category || '', callback: callback || function() {} });
-        };
-        register(  -1, 'DUMMY');
-        register(   0, 'INVALID');
-        register(   1, 'ADD');
-        register(   2, 'QUANTIZED_ADD');
-        register(   3, 'GLOBAL_MAX_POOL2D', 'Pool');
-        register(   4, 'QUANTIZED_GLOBAL_MAX_POOL2D', 'Pool');
-        register(   5, 'GLOBAL_AVERAGE_POOL2D', 'Pool');
-        register(   6, 'QUANTIZED_GLOBAL_AVERAGE_POOL2D', 'Pool');
-        register(   7, 'MAX_POOL2D', 'Pool');
-        register(   8, 'QUANTIZED_MAX_POOL2D', 'Pool');
-        register(   9, 'AVERAGE_POOL2D', 'Pool');
-        register(   10, 'QUANTIZED_AVERAGE_POOL2D', 'Pool');
-        register(   11, 'QUANTIZE');
-        register(   12, 'DEQUANTIZE');
-        register(   13, 'REQUANTIZE');
-        register(   14, 'L2_NORMALIZATION', 'Normalization');
-        register(   15, 'SOFTMAX', 'Activation');
-        register(   16, 'CONCAT', 'Tensor');
-        register(   17, 'QUANTIZED_CONCAT', 'Tensor');
-        register(   18, 'FULLY_CONNECTED', 'Layer');
-        register(   19, 'QUANTIZED_FULLY_CONNECTED', 'Layer');
-        register(   20, 'TENSORFLOW_FLATTEN');
-        register(   21, 'QUANTIZED_TENSORFLOW_FLATTEN');
-        register( 1000, 'CONV', 'Layer');
-        register( 1001, 'DWCONV', 'Layer');
-        register( 1002, 'QUANTIZED_RESHAPE', 'Shape');
-        register( 1003, 'RESHAPE', 'Shape');
-        register(10240, 'K210_CONV', 'Layer', (/* layer, reader */) => {
-            /*
-            const flags = reader.uint32();
-            const main_mem_out_address = reader.uint32();
-            const layer_offset = reader.uint32();
-            const weights_offset = reader.uint32();
-            const bn_offset = reader.uint32();
-            const act_offset = reader.uint32();
-            */
-        });
-        register(10241, 'K210_ADD_PADDING');
-        register(10242, 'K210_REMOVE_PADDING');
-        register(10243, 'K210_UPLOAD');
-        for (const layer of this.layers) {
-            const type = types.get(layer.type);
-            if (!type || !type.callback) {
-                throw new kmodel.Error("Unsupported layer type '" + layer.type.toString() + "'.");
-            }
-            layer.typename = type.name;
-            layer.category = type.category;
-            reader.seek(layer.offset);
-            type.callback(layer, reader);
-            // delete layer.offset;
-            // delete layer.body_size;
-        }
+    get name() {
+        return this._name;
+    }
+
+    get value() {
+        return this._value;
     }
 };
 
-kmodel.LDMK = class {
+kmodel.Reader = class {
 
-    constructor(stream) {
+    static open(stream) {
         const reader = new base.BinaryReader(stream);
-        /* const identifier = */ reader.uint32();
-        this.version = reader.uint32();
-        if (this.version > 5) {
-            throw new kmodel.Error("Unsupported LDMK model version '" + this.version.toString() + "'.");
-        }
-        const header_size = reader.uint32();
-        /* const flags = */ reader.uint32();
-        /* const alignment = */ reader.uint32();
-        this.modules = new Array(reader.uint32());
-        /* const entry_module = */ reader.uint32();
-        /* const entry_function = */ reader.uint32();
-        if (header_size > reader.position) {
-            reader.skip(header_size - reader.position);
+        if (reader.length > 4) {
+            const signature = reader.uint32();
+            if (signature === 3) {
+                return new kmodel.Reader(reader, 3);
+            }
+            if (signature === 0x4B4D444C) {
+                const version = reader.uint32();
+                return new kmodel.Reader(reader, version);
+            }
         }
-        for (let i = 0; i < this.modules.length; i++) {
-            /*
-            char[16] type;
-            uint32_t version;
-            uint32_t header_size;
-            uint32_t size;
-            uint32_t mempools;
-            uint32_t shared_mempools;
-            uint32_t sections;
-            uint32_t functions;
-            uint32_t reserved0;
-            */
+        return null;
+    }
+
+    constructor(reader, version) {
+        this._reader = reader;
+        this._version = version;
+    }
+
+    get version() {
+        return this._version;
+    }
+
+    get layers() {
+        this._read();
+        return this._layers;
+    }
+
+    _read() {
+        if (this._reader) {
+            const reader = this._reader;
+            if (this._version < 3 || this._version > 5) {
+                throw new kmodel.Error("Unsupported model version '" + this.version.toString() + "'.");
+            }
+            const types = new Map();
+            const register = (type, name, category, callback) => {
+                types.set(type, { type: { name: name, category: category || '' }, callback: callback });
+            };
+            switch (this._version) {
+                case 3: {
+                    const model_header = {
+                        flags: reader.uint32(),
+                        arch: reader.uint32(),
+                        layers_length: reader.uint32(),
+                        max_start_address: reader.uint32(),
+                        main_mem_usage: reader.uint32(),
+                        output_count: reader.uint32()
+                    };
+                    this._layers = new Array(model_header.layers_length);
+                    this._outputs = new Array(model_header.output_count);
+                    for (let i = 0; i < this._outputs.length; i++) {
+                        this._outputs[i] = {
+                            address: reader.uint32(),
+                            size: reader.uint32()
+                        };
+                    }
+                    for (let i = 0; i < this._layers.length; i++) {
+                        this._layers[i] = {
+                            location: i,
+                            type: reader.uint32(),
+                            body_size: reader.uint32()
+                        };
+                    }
+                    let offset = reader.position;
+                    for (const layer of this._layers) {
+                        layer.offset = offset;
+                        offset += layer.body_size;
+                    }
+                    register(   -1, 'DUMMY');
+                    register(    0, 'INVALID');
+                    register(    1, 'ADD');
+                    register(    2, 'QUANTIZED_ADD');
+                    register(    3, 'GLOBAL_MAX_POOL2D', 'Pool');
+                    register(    4, 'QUANTIZED_GLOBAL_MAX_POOL2D', 'Pool');
+                    register(    5, 'GLOBAL_AVERAGE_POOL2D', 'Pool', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_in_address = reader.uint32();
+                        layer.main_mem_out_address = reader.uint32();
+                        layer.kernel_size = reader.uint32();
+                        layer.channels = reader.uint32();
+                    });
+                    register(    6, 'QUANTIZED_GLOBAL_AVERAGE_POOL2D', 'Pool');
+                    register(    7, 'MAX_POOL2D', 'Pool');
+                    register(    8, 'QUANTIZED_MAX_POOL2D', 'Pool', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_in_address = reader.uint32();
+                        layer.main_mem_out_address = reader.uint32();
+                        layer.in_shape = [ reader.uint32(), reader.uint32(), reader.uint32() ];
+                        layer.out_shape = [ reader.uint32(), reader.uint32(), reader.uint32() ];
+                        layer.kernel = [ reader.uint32(), reader.uint32() ];
+                        layer.stride = [ reader.uint32(), reader.uint32() ];
+                        layer.padding = [ reader.uint32(), reader.uint32() ];
+                    });
+                    register(    9, 'AVERAGE_POOL2D', 'Pool');
+                    register(   10, 'QUANTIZED_AVERAGE_POOL2D', 'Pool');
+                    register(   11, 'QUANTIZE', '', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_in_address = reader.uint32();
+                        layer.mem_out_address = reader.uint32();
+                        layer.count = reader.uint32();
+                        layer.scale = reader.float32();
+                        layer.bias = reader.float32();
+                    });
+                    register(   12, 'DEQUANTIZE', '', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_in_address = reader.uint32();
+                        layer.mem_out_address = reader.uint32();
+                        layer.count = reader.uint32();
+                        layer.scale = reader.float32();
+                        layer.bias = reader.float32();
+                    });
+                    register(   13, 'REQUANTIZE');
+                    register(   14, 'L2_NORMALIZATION', 'Normalization');
+                    register(   15, 'SOFTMAX', 'Activation', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_in_address = reader.uint32();
+                        layer.main_mem_out_address = reader.uint32();
+                        layer.channels = reader.uint32();
+                    });
+                    register(   16, 'CONCAT', 'Tensor', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_out_address = reader.uint32();
+                        layer.inputs_mem = new Array(reader.uint32());
+                        for (let i = 0; i < layer.inputs_mem.length; i++) {
+                            layer.inputs_mem[i] = {
+                                start: reader.uint32(),
+                                end: reader.uint32()
+                            };
+                        }
+                    });
+                    register(   17, 'QUANTIZED_CONCAT', 'Tensor', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_out_address = reader.uint32();
+                        layer.inputs_mem = new Array(reader.uint32());
+                        for (let i = 0; i < layer.inputs_mem.length; i++) {
+                            layer.inputs_mem[i] = {
+                                start: reader.uint32(),
+                                end: reader.uint32()
+                            };
+                        }
+                    });
+                    register(   18, 'FULLY_CONNECTED', 'Layer', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_in_address = reader.uint32();
+                        layer.main_mem_out_address = reader.uint32();
+                        layer.in_channels = reader.uint32();
+                        layer.out_channels = reader.uint32();
+                        layer.act = reader.uint32(); // {'linear':0, 'relu':1, 'relu6':2}
+                        layer.params = {
+                            weights: reader.read(4 * layer.in_channels * layer.out_channels),
+                            bias: reader.read(4 * layer.out_channels)
+                        };
+                    });
+                    register(   19, 'QUANTIZED_FULLY_CONNECTED', 'Layer');
+                    register(   20, 'TENSORFLOW_FLATTEN', 'Shape', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_in_address = reader.uint32();
+                        layer.main_mem_out_address = reader.uint32();
+                        layer.shape = [ reader.uint32(), reader.uint32(), reader.uint32() ];
+                    });
+                    register(   21, 'QUANTIZED_TENSORFLOW_FLATTEN', 'Shape', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_in_address = reader.uint32();
+                        layer.main_mem_out_address = reader.uint32();
+                        layer.shape = [ reader.uint32(), reader.uint32(), reader.uint32() ];
+                    });
+                    register( 1000, 'CONV', 'Layer');
+                    register( 1001, 'DWCONV', 'Layer');
+                    register( 1002, 'QUANTIZED_RESHAPE', 'Shape');
+                    register( 1003, 'RESHAPE', 'Shape');
+                    register(10240, 'K210_CONV', 'Layer', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_out_address = reader.uint32();
+                        /* const layer_offset = */ reader.uint32();
+                        /* const weights_offset = */ reader.uint32();
+                        /* const bn_offset = */ reader.uint32();
+                        /* const act_offset = */ reader.uint32();
+                    });
+                    register(10241, 'K210_ADD_PADDING', '', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_in_address = reader.uint32();
+                        layer.kpu_mem_out_address = reader.uint32();
+                        layer.channels = reader.uint32();
+                    });
+                    register(10242, 'K210_REMOVE_PADDING', '', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_in_address = reader.uint32();
+                        layer.kpu_mem_out_address = reader.uint32();
+                        layer.channels = reader.uint32();
+                    });
+                    register(10243, 'K210_UPLOAD', '', (layer, reader) => {
+                        layer.flags = reader.uint32();
+                        layer.main_mem_in_address = reader.uint32();
+                        layer.kpu_mem_out_address = reader.uint32();
+                        layer.shape = [ reader.uint32(), reader.uint32(), reader.uint32() ];
+                    });
+                    for (const layer of this._layers) {
+                        const type = types.get(layer.type);
+                        if (!type) {
+                            throw new kmodel.Error("Unsupported version '" + this._version.toString() + "' layer type '" + layer.type.toString() + "'.");
+                        }
+                        if (!type.callback) {
+                            throw new kmodel.Error("Unsupported version '" + this._version.toString() + "' layer '" + type.name + "'.");
+                        }
+                        layer.type = type.type;
+                        reader.seek(layer.offset);
+                        type.callback(layer, reader);
+                        delete layer.offset;
+                        delete layer.body_size;
+                        if (reader.position != (layer.offset + layer.body_size)) {
+                            // debugger;
+                        }
+                        // console.log(JSON.stringify(Object.fromEntries(Object.entries(layer).filter((entry) => !(entry[1] instanceof Uint8Array))), null, 2));
+                    }
+                    break;
+                }
+                case 4: {
+                    const model_header = {
+                        flags: reader.uint32(),
+                        target: reader.uint32(), // 0=CPU, 1=K210
+                        constants: reader.uint32(),
+                        main_mem: reader.uint32(),
+                        nodes: reader.uint32(),
+                        inputs: reader.uint32(),
+                        outputs: reader.uint32(),
+                        reserved0: reader.uint32(),
+                    };
+                    reader.memory_range = function() {
+                        return {
+                            memory_type: this.uint32(), // 0=const, 1=main, 2=k210_kpu
+                            datatype: this.uint32(), // 0=float32, 1=uint8
+                            start: this.uint32(),
+                            size: this.uint32()
+                        };
+                    };
+                    reader.runtime_shape_t = function() {
+                        return [ reader.uint32(), reader.uint32(), reader.uint32(), reader.uint32() ];
+                    };
+                    this._inputs = new Array(model_header.inputs);
+                    for (let i = 0; i < this._inputs.length; i++) {
+                        this._inputs[i] = reader.memory_range();
+                    }
+                    for (let i = 0; i < this._inputs.length; i++) {
+                        this._inputs[i].shape = reader.runtime_shape_t();
+                    }
+                    this._outputs = new Array(model_header.outputs);
+                    for (let i = 0; i < this._outputs.length; i++) {
+                        this._outputs[i] = reader.memory_range();
+                    }
+                    this._constants = reader.read(model_header.constants);
+                    this._layers = new Array(model_header.nodes);
+                    for (let i = 0; i < this._layers.length; i++) {
+                        this._layers[i] = {
+                            op_code: reader.uint32(),
+                            body_size: reader.uint32()
+                        };
+                    }
+                    let offset = reader.position;
+                    for (const layer of this._layers) {
+                        layer.offset = offset;
+                        offset += layer.body_size;
+                    }
+
+                    register(  0x00, 'binary', '');
+                    register(  0x01, 'concat', 'Tensor');
+                    register(  0x02, 'conv2d', 'Layer');
+                    register(  0x03, 'dequantize', '');
+                    register(  0x04, 'matmul', '');
+                    register(  0x05, 'pad', 'Shape');
+                    register(  0x06, 'quantize', '');
+                    register(  0x07, 'reduce', '');
+                    register(  0x08, 'reduce_window2d');
+                    register(  0x09, 'memory_copy', '');
+                    register(  0x0A, 'resize_image', '');
+                    register(  0x0B, 'softmax', 'Activation');
+                    register(  0x0C, 'transpose', 'Transform');
+                    register(  0x0D, 'strided_slice', 'Tensor');
+                    register(  0x0E, 'unary', '');
+                    register(  0x0F, 'quantized_conv2d', 'Layer');
+                    register(  0x10, 'quantized_matmul', '');
+                    register(  0x11, 'quantized_binary', '');
+                    register(  0x12, 'table_lookup1d', '');
+                    register(  0x13, 'conv2d_transpose', 'Layer');
+                    register(  0x14, 'nnil_unary_method', '');
+                    register(0x1001, 'cpu_conv2d', 'Layer');
+                    register(0x1002, 'cpu_depthwise_conv2d', 'Layer');
+                    register(0x1003, 'cpu_reduce_window2d');
+                    register(0x1004, 'cpu_quantized_conv2d', 'Layer');
+                    register(0x1005, 'cpu_quantized_depthwise_conv2d', 'Layer');
+                    register(0x2001, 'kpu_upload', '');
+                    register(0x2002, 'kpu_conv2d', 'Layer');
+                    for (const layer of this._layers) {
+                        const type = types.get(layer.op_code);
+                        if (!type) {
+                            throw new kmodel.Error("Unsupported version '" + this._version.toString() + "' layer type '" + layer.type.toString() + "'.");
+                        }
+                        if (!type.callback) {
+                            // throw new kmodel.Error("Unsupported version '" + this._version.toString() + "' layer '" + type.name + "'.");
+                        }
+                        layer.type = type.type;
+                        reader.seek(layer.offset);
+                        // type.callback(layer, reader);
+                        delete layer.offset;
+                        delete layer.body_size;
+                        if (reader.position != (layer.offset + layer.body_size)) {
+                            // debugger;
+                        }
+                        // console.log(JSON.stringify(Object.fromEntries(Object.entries(layer).filter((entry) => !(entry[1] instanceof Uint8Array))), null, 2));
+                        delete layer.op_code;
+                    }
+                    break;
+                }
+                case 5: {
+                    reader.model_header = function() {
+                        return {
+                            header_size: reader.uint32(),
+                            flags: reader.uint32(),
+                            alignment: reader.uint32(),
+                            modules: reader.uint32(),
+                            entry_module: reader.uint32(),
+                            entry_function: reader.uint32()
+                        };
+                    };
+                    reader.module_type_t = function() {
+                        const buffer = reader.read(16);
+                        return new TextDecoder('ascii').decode(buffer);
+                    };
+                    reader.module_header = function() {
+                        return {
+                            type: reader.module_type_t(),
+                            version: reader.uint32(),
+                            header_size: reader.uint32(),
+                            size: reader.uint32(),
+                            mempools: reader.uint32(),
+                            shared_mempools: reader.uint32(),
+                            sections: reader.uint32(),
+                            functions: reader.uint32(),
+                            reserved0: reader.uint32()
+                        };
+                    };
+                    reader.mempool_desc = function() {
+                        return {
+                            location: reader.byte(),
+                            reserved0: reader.read(3),
+                            size: reader.uint32()
+                        };
+                    };
+                    reader.section_header = function() {
+                        return {
+                            name: new TextDecoder('ascii').decode(reader.read(16)),
+                            flags: reader.uint32(),
+                            body_start: reader.uint32(),
+                            body_size: reader.uint32(),
+                            reserved0: reader.uint32()
+                        };
+                    };
+                    reader.function_header = function() {
+                        return {
+                            header_size: reader.uint32(),
+                            size: reader.uint32(),
+                            input_pool_size: reader.uint32(),
+                            output_pool_size: reader.uint32(),
+                            inputs: reader.uint32(),
+                            outputs: reader.uint32(),
+                            entrypoint: reader.uint32(),
+                            text_size: reader.uint32()
+                        };
+                    };
+                    reader.memory_range = function() {
+                        return {
+                            memory_type: this.byte(), // 0=const, 1=main, 2=k210_kpu
+                            datatype: this.byte(),
+                            shared_module: this.uint16(),
+                            start: this.uint32(),
+                            size: this.uint32()
+                        };
+                    };
+                    reader.shape = function() {
+                        const array = new Array(reader.uint32());
+                        for (let i = 0; i < array.length; i++) {
+                            array[i] = reader.uint32();
+                        }
+                        return array;
+                    };
+                    reader.align_position = function(alignment) {
+                        const remainder = this._position % alignment;
+                        if (remainder !== 0) {
+                            this.skip(alignment - remainder);
+                        }
+                    };
+                    const model_header = reader.model_header();
+                    if (model_header.header_size > reader.position) {
+                        reader.skip(model_header.header_size - reader.position);
+                    }
+                    this._modules = new Array(model_header.modules);
+                    for (let i = 0; i < this._modules.length; i++) {
+                        const start = reader.position;
+                        const module_header = reader.module_header();
+                        if (module_header.header_size > (reader.position - start)) {
+                            reader.skip(module_header.header_size - (reader.position - start));
+                        }
+                        const mempools = new Array(module_header.mempools);
+                        for (let i = 0; i < mempools.length; i++) {
+                            mempools[i] = reader.mempool_desc();
+                        }
+                        const shared_mempools = new Array(module_header.shared_mempools);
+                        for (let i = 0; i < shared_mempools.length; i++) {
+                            shared_mempools[i] = reader.mempool_desc();
+                        }
+                        const functions = new Array(module_header.functions);
+                        for (let i = 0; i < functions.length; i++) {
+                            const function_header = reader.function_header();
+                            const inputs = new Array(function_header.inputs);
+                            for (let i = 0; i < inputs.length; i++) {
+                                inputs[i] = reader.memory_range();
+                            }
+                            for (let i = 0; i < inputs.length; i++) {
+                                inputs[i].shape = reader.shape();
+                            }
+                            const outputs = new Array(function_header.outputs);
+                            for (let i = 0; i < outputs.length; i++) {
+                                outputs[i] = reader.memory_range();
+                            }
+                            for (let i = 0; i < outputs.length; i++) {
+                                outputs[i].shape = reader.shape();
+                            }
+                            reader.align_position(8);
+                        }
+                        const sections = new Array(module_header.sections);
+                        for (let i = 0; i < sections.length; i++) {
+                            sections[i] = reader.section_header();
+                        }
+                    }
+                    throw new kmodel.Error("Unsupported model version '" + this.version.toString() + "'.");
+                }
+                default: {
+                    throw new kmodel.Error("Unsupported model version '" + this.version.toString() + "'.");
+                }
+            }
+            delete this._reader;
         }
-        throw new kmodel.Error('kmodel.LDMK not supported.');
     }
 };
 
@@ -237,4 +568,4 @@ kmodel.Error = class extends Error {
 
 if (typeof module !== 'undefined' && typeof module.exports === 'object') {
     module.exports.ModelFactory = kmodel.ModelFactory;
-}
+}

+ 16 - 1
test/models.json

@@ -2314,10 +2314,25 @@
   {
     "type":   "kmodel",
     "target": "cifar10.kmodel",
-    "source": "https://raw.githubusercontent.com/sipeed/Maix-EMC/master/example/cifar/cifar10.kmodel",
+    "source": "https://github.com/lutzroeder/netron/files/7965167/cifar10.kmodel.zip[cifar10.kmodel]",
     "format": "kmodel v3",
     "link":   "https://github.com/lutzroeder/netron/issues/871"
   },
+  {
+    "type":   "kmodel",
+    "target": "mobilenet_v2.kmodel",
+    "source": "https://github.com/lutzroeder/netron/files/7965168/mobilenet_v2.kmodel.zip[mobilenet_v2.kmodel]",
+    "format": "kmodel v5",
+    "error":  "Unsupported model version '5' in 'mobilenet_v2.kmodel'.",
+    "link":   "https://github.com/lutzroeder/netron/issues/871"
+  },
+  {
+    "type":   "kmodel",
+    "target": "RFB-320.kmodel",
+    "source": "https://github.com/lutzroeder/netron/files/7965166/RFB-320.kmodel.zip[RFB-320.kmodel]",
+    "format": "kmodel v4",
+    "link":   "https://github.com/lutzroeder/netron/issues/871"
+  },
   {
     "type":   "lasagne",
     "target": "net2.pkl",