Files
wehub-resource-sync 7254f7b4d1
Build / Build (macos-latest) (push) Has been cancelled
Build / Build (ubuntu-latest) (push) Has been cancelled
Build / Build (windows-latest) (push) Has been cancelled
Build / Analyze (javascript) (push) Has been cancelled
Build / Analyze (python) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:37:45 +08:00

834 lines
32 KiB
JavaScript

// Experimental
import * as base from './base.js';
import * as protobuf from './protobuf.js';
const om = {};
const svp = {};
om.ModelFactory = class {
async match(context) {
const container = om.Container.open(context);
if (container) {
return context.set('om', container);
}
return null;
}
async open(context) {
const target = context.value;
await target.read();
const metadata = await context.metadata('om-metadata.json');
return new om.Model(metadata, target);
}
};
om.Model = class {
constructor(metadata, target) {
this.format = target.format;
this.version = target.signature === 'PICO' ? target.model.version : '';
const context = {
metadata,
signature: target.signature,
weights: target.weights
};
this.modules = target.model.graph.map((graph) => new om.Graph(context, graph));
}
};
om.Graph = class {
constructor(context, graph) {
switch (context.signature) {
case 'IMOD': this.name = graph.name; break;
case 'PICO': this.name = graph.id.toString(); break;
default: throw new om.Error(`Unsupported DaVinci OM ${context.signature} signature.`);
}
this.nodes = [];
this.inputs = [];
this.outputs = [];
const values = new Map();
values.map = (name, type, tensor) => {
if (!values.has(name)) {
values.set(name, new om.Value(name, type || null, tensor || null));
} else if ((type && !type.equals(values.get(name).type)) ||
(tensor && tensor !== values.get(name).initializer)) {
throw new om.Error(`Duplicate value '${name}'.`);
}
return values.get(name);
};
const tensors = new Map();
const ops = [];
for (const op of graph.op) {
if (op.type === 'Const' && op.attr && op.attr.value) {
const desc = op.attr.value.t.desc;
let data = null;
if (op.attr.value.t.data.length !== 0) {
data = op.attr.value.t.data;
} else if (context.weights === null) {
data = null;
} else if (desc.attr.merged_offset) {
const offset = desc.attr.merged_offset.i.toNumber();
data = context.weights.slice(offset, offset + desc.weight_size.toNumber());
} else {
const offset = desc.data_offset.toNumber();
data = context.weights.slice(offset, offset + desc.weight_size.toNumber());
}
const type = om.Utility.tensorType(desc);
const tensor = new om.Tensor('Constant', type, data);
tensors.set(op.name, tensor);
continue;
}
ops.push(op);
}
for (const op of ops) {
const node = new om.Node(context, op, graph, values, tensors);
this.nodes.push(node);
}
}
};
om.Node = class {
constructor(context, op, graph, values, tensors) {
this.name = op.name || '';
this.type = context.metadata.type(op.type) || { name: op.type };
this.inputs = [];
this.outputs = [];
this.attributes = [];
this.chain = [];
this.controlDependencies = [];
this.device = null;
if (op.input) {
let index = 0;
for (let i = 0; i < op.input.length; i++) {
const input = op.input[i];
if (input === '') {
continue;
}
const name = this.type.inputs && i < this.type.inputs.length ? this.type.inputs[i].name : `input${index === 0 ? '' : index}`;
index++;
const end = this.type.inputs && i < this.type.inputs.length && this.type.inputs[i].type && this.type.inputs[i].type === 'Tensor[]' ? op.input.length : i + 1;
const list = [];
for (let j = i; j < end; j++) {
const input = op.input[j];
if (input === '') {
continue;
}
const index = input.lastIndexOf(':');
const identifier = input.substring(0, index);
const src_index = input.substring(index + 1);
if (src_index === '-1') {
this.controlDependencies.push(values.map(name));
continue;
}
const type = om.Utility.tensorType(op.input_desc[j]);
const tensor = tensors.get(identifier);
const value = values.map(input, type, tensor);
list.push(value);
}
const argument = new om.Argument(name, list);
this.inputs.push(argument);
i = end - 1;
}
}
if (op.output_desc) {
for (let i = 0; i < op.output_desc.length; i++) {
const identifier = `${this.name}:${i}`;
const type = om.Utility.tensorType(op.output_desc[i]);
const name = this.type.outputs && i < this.type.outputs.length ? this.type.outputs[i].name : `output${i === 0 ? '' : i}`;
const value = values.map(identifier, type);
const argument = new om.Argument(name, [value]);
this.outputs.push(argument);
}
}
for (const [name, obj] of Object.entries(op.attr || {})) {
if (name === 'device') {
this.device = obj;
continue;
}
if (name === 'original_op_names') {
continue;
}
if (name === 'relu_flag' && obj.b) {
const node = new om.Node(context, { type: 'ReLU' }, graph, obj);
this.chain.push(node);
continue;
}
let value = obj;
let type = null;
switch (obj.value) {
case 'i': {
value = obj.i;
type = 'int64';
break;
}
case 'f': {
value = obj.f;
type = 'float32';
break;
}
case 'b': {
value = obj.b;
type = 'boolean';
break;
}
case 'bt': {
value = null;
if (obj.bt.length !== 0) {
type = 'tensor';
const shape = new om.TensorShape([obj.bt.length / 4]);
value = new om.Tensor('Constant', new om.TensorType('float32', shape), obj.bt);
}
break;
}
case 'dt': {
type = 'DataType';
value = om.Utility.dtype(Number(obj.dt));
break;
}
case 's': {
if (typeof obj.s === 'string') {
value = obj.s;
} else if (obj.s.every((c) => c >= 32 && c <= 128)) {
value = om.Utility.decodeText(obj.s);
} else {
value = obj.s;
}
type = 'string';
break;
}
case 'g': {
type = 'graph';
value = new om.Graph(context, obj.g);
break;
}
case 'func': {
break;
}
case 'list': {
const list = obj.list;
value = [];
if (list.s && list.s.length > 0) {
value = list.s.map((v) => String.fromCharCode.apply(null, new Uint16Array(v))).join(', ');
type = 'string[]';
} else if (list.b && list.b.length > 0) {
value = list.b;
type = 'boolean[]';
} else if (list.i && list.i.length > 0) {
value = list.i;
type = 'int64[]';
} else if (list.f && list.f.length > 0) {
value = list.f;
type = 'float32[]';
} else if (list.type && list.type.length > 0) {
type = 'type[]';
value = list.type.map((type) => om.Node.enum2Dtype(type) || '?');
} else if (list.shape && list.shape.length > 0) {
type = 'shape[]';
value = list.shape.map((shape) => new om.TensorShape(shape));
}
break;
}
case 'list_list_int': {
value = obj.list_list_int.list_list_i.map((list) => list.list_i);
break;
}
case 't': {
type = 'tensor';
value = new om.Tensor('Constant', om.Utility.tensorType(obj.t.desc), obj.t.bytes);
break;
}
case undefined: {
value = null;
break;
}
default: {
throw new om.Error(`Unsupported attribute type '${JSON.stringify(obj).substring(0, 32)}'.`);
}
}
const attribute = new om.Argument(name, value, type);
this.attributes.push(attribute);
}
}
};
om.Argument = class {
constructor(name, value, type = null) {
this.name = name;
this.value = value;
this.type = type;
}
};
om.Value = class {
constructor(name, type, initializer = null) {
if (typeof name !== 'string') {
throw new om.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = initializer ? initializer.type : type;
this.initializer = initializer;
}
};
om.Tensor = class {
constructor(category, type, value) {
this.category = category;
this.type = type;
this.values = value;
}
};
om.TensorType = class {
constructor(dataType, shape, denotation) {
this.dataType = dataType;
this.shape = shape;
this.denotation = denotation;
}
equals(obj) {
return obj && this.dataType === obj.dataType && this.shape && this.shape.equals(obj.shape);
}
toString() {
return this.dataType + this.shape.toString();
}
};
om.TensorShape = class {
constructor(dimensions) {
this.dimensions = dimensions.map((dim) => typeof dim === 'bigint' ? dim.toNumber() : dim);
}
equals(obj) {
if (obj && Array.isArray(obj.dimensions) && Array.isArray(this.dimensions)) {
if (this.dimensions.length === obj.dimensions.length &&
obj.dimensions.every((value, index) => this.dimensions[index] === value)) {
return true;
}
if (obj.dimensions.every((dim) => Number.isInteger(dim)) && this.dimensions.every((dim) => Number.isInteger(dim))) {
const a = obj.dimensions.reduce((a, b) => a * b, 1);
const b = this.dimensions.reduce((a, b) => a * b, 1);
return a === b;
}
}
return false;
}
toString() {
if (this.dimensions && Array.isArray(this.dimensions) && this.dimensions.length > 0) {
return `[${this.dimensions.map((dim) => dim ? dim.toString() : '?').join(',')}]`;
}
return '';
}
};
om.Container = class {
static open(context) {
const stream = context.stream;
if (stream && stream.length >= 256) {
const buffer = stream.peek(4);
const signature = Array.from(buffer).map((c) => String.fromCharCode(c)).join('');
if (signature === 'IMOD' || signature === 'PICO') {
return new om.Container(context, signature);
}
}
return null;
}
constructor(context, signature) {
this.context = context;
this.signature = signature;
this.weights = null;
}
async read() {
if (this.context) {
const stream = this.context.stream;
const reader = base.BinaryReader.open(stream);
reader.skip(4);
switch (this.signature) {
case 'IMOD': {
const decoder = new TextDecoder('utf-8');
this.format = 'DaVinci OM';
const header = {};
header.headsize = reader.uint32();
header.version = reader.uint32();
header.checksum = reader.read(64);
header.length = reader.uint32();
header.is_encrypt = reader.byte();
header.is_checksum = reader.byte();
header.modeltype = reader.byte(); // 0=IR model, 1=standard model, 2=OM Tiny model
header.genmode = reader.byte(); // 0=offline, 1=online
header.name = decoder.decode(reader.read(32));
header.ops = reader.uint32();
header.userdefineinfo = reader.read(32);
header.om_ir_version = reader.uint32();
header.model_num = header.version >= 0x20000000 ? reader.uint32() : 1;
header.platform_version = decoder.decode(reader.read(20));
header.platform_type = reader.byte();
header.padd = [reader.byte(), reader.byte(), reader.byte()];
header.model_length = reader.uint64();
header.need_check_os_cpu_info = reader.byte();
header.is_unknow_model = reader.byte(); // 0:static model 1:dynamic model
header.reserved = reader.read(62);
const partitions = new Map();
let size = -1;
for (let align = 4; align <= 8; align += 4) {
reader.seek(header.headsize);
const count = reader.uint32();
reader.skip(align - 4);
size = 4 + (align - 4) + (count * 3 * align);
for (let i = 0; i < count; i++) {
const type = align === 4 ? reader.uint32() : reader.uint64().toNumber();
const offset = align === 4 ? reader.uint32() : reader.uint64().toNumber();
const size = align === 4 ? reader.uint32() : reader.uint64().toNumber();
if (type >= 32 || partitions.has(type) || (offset + size) >= stream.length) {
partitions.clear();
break;
}
partitions.set(type, { offset, size });
}
if (partitions.size > 0) {
break;
}
}
if (!partitions.has(0)) {
throw new om.Error('File does not contain a model definition.');
}
const offset = header.headsize + size;
for (const [type, partition] of partitions) {
reader.seek(offset + partition.offset);
const buffer = reader.read(partition.size);
switch (type) {
case 0: { // MODEL_DEF
this.model = buffer;
break;
}
case 1: { // WEIGHTS_DATA
this.weights = buffer;
break;
}
case 2: // TASK_INFO
case 3: // TBE_KERNELS
case 4: { // CUST_AICPU_KERNELS
break;
}
case 5: { // DEVICE_CONFIG, SO_BINS
this.devices = new Map();
const decoder = new TextDecoder('ascii');
const reader = base.BinaryReader.open(buffer);
reader.uint32();
for (let position = 4; position < partition.size;) {
const length = reader.uint32();
const buffer = reader.read(length);
const name = decoder.decode(buffer);
const device = reader.uint32();
this.devices.set(name, device);
position += 4 + length + 4;
}
break;
}
case 6: // FLOW_MODEL
case 7: // FLOW_SUBMODEL
case 8: // MODEL_INOUT_INFO
case 9: // STATIC_TASK_DESC
case 10: // DYNAMIC_TASK_DESC
case 11: // TASK_PARAM
case 12: // TILING_DATA
case 20: // PRE_MODEL_DESC
case 21: // PRE_MODEL_SQE
case 22: // PRE_KERNEL_ARGS
case 23: // PRE_MODEL_DESC_EXTEND
case 24: { // BUNDLE_MODEL_INFO
break;
}
default: {
throw new om.Error(`Unsupported DaVinci OM partition type '${type}'.`);
}
}
}
om.proto = await this.context.require('./om-proto');
om.proto = om.proto.ge.proto;
try {
const reader = protobuf.BinaryReader.open(this.model);
this.model = om.proto.ModelDef.decode(reader);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new om.Error(`File format is not ge.proto.ModelDef (${message.replace(/\.$/, '')}).`);
}
break;
}
case 'PICO': {
this.format = 'DaVinci OM SVP'; // SVP = Smart Vision Platform
reader.uint32(); // reserved
this.size = reader.uint32();
const param_size = reader.uint32();
const param_offset = reader.uint32();
reader.uint32(); // tmp_bufsize
const tfm_offset = reader.uint32();
reader.uint32(); // tfm_size
reader.seek(param_offset);
this.param = reader.read(param_size);
const buffer = reader.read(tfm_offset - reader.position);
this.model = new svp.ModelDef(buffer);
break;
}
default: {
throw new om.Error(`Unsupported DaVinci OM ${this.signature} signature.`);
}
}
delete this.context;
}
}
};
om.Utility = class {
static dtype(value) {
om.Utility._types = om.Utility._types || [
'undefined', 'float32', 'float16', 'int8', 'uint8', 'int16', 'uint16', 'int32',
'int64', 'uint32', 'uint64', 'boolean', 'float64', 'string', 'dual_sub_int8', 'dual_sub_uint8',
'complex<float32>', 'complex<float64>', 'qint8', 'qint16', 'qint32', 'quint8', 'quint16', 'resource',
'stringref', 'dual', 'variant', 'bfloat16', 'int4', 'uint1', 'int2', 'uint2'
];
if (value >= om.Utility._types.length) {
throw new om.Error(`Unsupported dtype '${value}'.`);
}
return om.Utility._types[value];
}
static tensorType(desc) {
if (desc && desc.shape && Array.isArray(desc.shape.dim)) {
const dataType = desc && desc.dtype ? om.Utility.dtype(desc.dtype) : '?';
const shape = new om.TensorShape(desc.shape.dim);
return new om.TensorType(dataType, shape, desc.layout);
}
return null;
}
static decodeText(value) {
om.Utility._textDecoder = om.Utility._textDecoder || new TextDecoder('utf-8');
return om.Utility._textDecoder.decode(value);
}
};
om.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading DaVinci OM model.';
}
};
svp.ModelDef = class ModelDef {
constructor(buffer) {
const reader = new svp.BinaryReader(buffer);
this.attr = {};
this.graph = [];
this.name = reader.find(0x800D, 'string');
this.batch_num = reader.find(0x600A);
this.version = '';
while (reader.position < reader.length) {
const tag = reader.uint16();
const value = reader.value(tag);
switch (tag & 0x1fff) {
case 0x0040: {
this.graph.push(new svp.GraphDef(value));
break;
}
case 0x0111: {
const op = new svp.OpDef(value);
for (const item of this.graph) {
if (op.attr && op.attr.seg_id && op.attr.seg_id.i === item.id) {
let out_num = 0;
if (typeof op.output_index === 'number') {
out_num = op.output_index + 1;
} else {
const input_num = op.input.map((element) => element.split(":")[1]);
out_num = input_num.length > 0 ? Math.max(...input_num) + 1 : 1;
}
const out_types = [];
if (op.data_flow && op.data_flow !== '') {
const data = op.data_flow;
if (data.indexOf('o[{t') !== -1) {
const outs = data.substring(data.indexOf('o[{t')).split(',');
for (const out of outs) {
const startIndex = out.indexOf("\"");
const endIndex = out.indexOf("\"", startIndex + 1);
out_types.push(out.substring(startIndex + 1, endIndex));
}
}
}
const out_list = [];
while (out_num > 0) {
const output_desc = {};
output_desc.shape = { dim: op.output_shape_vector };
output_desc.layout = 'NCHW';
if (op.data_flow && out_types.length >= out_num) {
output_desc.dtype = out_types[op.output_index + 1 - out_num];
}
out_list.push(output_desc);
out_num--;
}
let curr_op = null;
for (const op_item of item.op) {
if (op_item.id === op.id) {
curr_op = op_item;
break;
}
}
if (curr_op === null) {
op.output_desc = op.output_desc.concat(out_list);
item.op.push(op);
} else {
curr_op.output_desc = curr_op.output_desc.concat(out_list);
}
break;
}
}
break;
}
default: {
break;
}
}
}
if (this.graph.length > 1) {
for (let i = 1; i < this.graph.length; i++) {
this.graph[0].op = this.graph[0].op.concat(this.graph[i].op);
}
}
this.version = this.graph[0].op.length === 0 ? 'release' : 'debug';
}
};
svp.GraphDef = class {
constructor(buffer) {
this.input = [];
this.output = [];
this.op = [];
this.attr = {};
const reader = new svp.BinaryReader(buffer);
const input = (buffer) => {
const input = {};
const reader = new svp.BinaryReader(buffer);
while (reader.position < reader.length) {
const tag = reader.uint16();
switch (tag & 0x1fff) {
case 0x0051: input.id = reader.value(tag); break;
case 0x0058: input.name = reader.value(tag, 'string').trim(); break;
case 0x005a: input.shape_vector = reader.value(tag, 'uint32[]'); break;
default: reader.value(tag); break;
}
}
return input;
};
const output = (buffer) => {
const output = {};
const reader = new svp.BinaryReader(buffer);
while (reader.position < reader.length) {
const tag = reader.uint16();
switch (tag & 0x1fff) {
case 0x0061: output.id = reader.value(tag); break;
case 0x0066: output.name = reader.value(tag, 'string').trim(); break;
case 0x0069: output.shape_vector = reader.value(tag, 'uint32[]'); break;
case 0x0110: output.layer_num = reader.value(tag); break;
default: reader.value(tag); break;
}
}
return output;
};
while (reader.position < reader.length) {
const tag = reader.uint16();
const value = reader.value(tag);
switch (tag & 0x1fff) {
case 0x0041: this.id = value; break;
case 0x0050: this.input.push(input(value)); break;
case 0x0060: this.output.push(output(value)); break;
default: break;
}
}
}
};
svp.OpDef = class {
constructor(buffer) {
this.input = [];
this.attr = {};
this.input_i = [];
this.output_i = [];
this.input_desc = [];
this.output_desc = [];
const reader = new svp.BinaryReader(buffer);
while (reader.position < reader.length) {
const tag = reader.uint16();
switch (tag & 0x1fff) {
case 0x0114: this.name = reader.value(tag, 'string').trim(); break;
case 0x0112: this.id = reader.value(tag); break;
case 0x0119: this.attr.output_m2m_flag = reader.attribute(tag, 'i'); break;
case 0x0121: this.attr.batch_flag = reader.attribute(tag, 'i'); break;
case 0x0124: this.attr.dequant_scale = reader.attribute(tag, 'i'); break;
case 0x0126: this.attr.output_address = reader.attribute(tag, 'i'); break;
case 0x0125: this.attr.dequant_offset = reader.attribute(tag, 'i'); break;
case 0x0127: this.attr.first_inst_addr = reader.attribute(tag, 'i'); break;
case 0x0128: this.attr.last_inst_addr = reader.attribute(tag, 'i'); break;
case 0x013B: this.attr.is_fusion_layer = reader.attribute(tag, 'i'); break;
case 0x013C: this.input = reader.value(tag, 'string').split(','); break;
case 0x014B: this.attr.seg_id = reader.attribute(tag, 'i'); break;
case 0x0150: this.attr.is_not_last_merge_layer = reader.attribute(tag, 'i'); break;
case 0x0151: this.attr.is_dump_avavilable = reader.attribute(tag, 'i'); break;
case 0x0153: this.attr.debug_dump_offset = reader.attribute(tag, 'i'); break;
case 0x0152: this.type = reader.value(tag, 'string'); break;
case 0x0154: this.output_shape_vector = reader.value(tag, 'uint32[]'); break;
case 0x0155: this.input_index = reader.value(tag); break;
case 0x015B: this.output_index = reader.value(tag); break;
case 0x0156: this.attr.trap_inst_pc = reader.attribute(tag, 'i'); break;
case 0x0157: this.attr.profile_layer_id = reader.attribute(tag, 'i'); break;
case 0xA15A:
this.data_flow = reader.value(tag, 'string');
this.attr.data_flow = new svp.AttrDef(this.data_flow.replace('i[{t', 'input[{type').replace(',f[{t', '\tforward[{type').replace(',o[{t', '\toutput[{type').replace(',{[t', ',{type'), 's');
break;
default: reader.value(tag); break;
}
}
for (let i = 0; i < this.input.length; i++) {
this.input_desc.push({ layout: 'NCHW', shape: {} });
}
}
};
svp.AttrDef = class {
constructor(item, type) {
switch (type) {
case 's': this.s = item; break;
case 'i': this.i = item; break;
default: throw new svp.Error(`Unsupported attribute type '${type}'.`);
}
}
get value() {
if (this.s !== undefined) {
return 's';
}
if (this.i !== undefined) {
return 'i';
}
return undefined;
}
};
svp.BinaryReader = class {
constructor(buffer) {
this._reader = base.BinaryReader.open(buffer);
}
get length() {
return this._reader.length;
}
get position() {
return this._reader.position;
}
seek(position) {
this._reader.seek(position);
}
read(length) {
return this._reader.read(length);
}
int8() {
return this._reader.int8();
}
uint16() {
return this._reader.uint16();
}
uint32() {
return this._reader.uint32();
}
value(tag, type) {
let value = 0;
switch (tag >> 13) {
case 1: value = this.int8(); break;
case 2: value = this.uint16(); break;
case 3: value = this.uint32(); break;
case 4: value = this.read(this.int8()); break;
case 5: value = this.read(this.uint16()); break;
case 6: value = this.read(this.uint32()); break;
default: throw new svp.Error(`Unsupported value identifier '${tag}'.`);
}
return type ? this._cast(value, type, tag) : value;
}
find(tag, type) {
let value = null;
let match = false;
while (!match && this.position < this.length) {
const current = this.uint16();
value = this.value(current);
match = current === tag;
}
this.seek(0);
return match && type ? this._cast(value, type, tag) : value;
}
attribute(tag, type) {
const value = this.value(tag);
return new svp.AttrDef(value, type);
}
_cast(value, type, tag) {
switch (type) {
case 'string': {
if (value instanceof Uint8Array) {
svp.BinaryReader._decoder = svp.BinaryReader._decoder || new TextDecoder('utf-8');
return svp.BinaryReader._decoder.decode(value).replace(/\0.*$/g, '');
}
throw new om.Error(`Invalid 'string' tag '${tag.toString(16)}'.`);
}
case 'uint32[]': {
const reader = base.BinaryReader.open(value);
value = [];
while (reader.position < reader.length) {
value.push(reader.uint32());
}
return value;
}
default: {
return value;
}
}
}
};
svp.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading DaVinci SVP model.';
}
};
export const ModelFactory = om.ModelFactory;