blob: abf8ac5e2e8b9cec05defdc6d8ff631612a2df83 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Buffer } from 'buffer';
import { Graph, Vertex, Edge, VertexProperty } from '../../../graph.js';
export default class GraphSerializer {
constructor(ioc) {
this.ioc = ioc;
this.ioc.serializers[ioc.DataType.GRAPH] = this;
}
canBeUsedFor(value) {
return value instanceof Graph;
}
serialize(item, fullyQualifiedFormat = true) {
if (item === undefined || item === null) {
if (fullyQualifiedFormat) {
return Buffer.from([this.ioc.DataType.GRAPH, 0x01]);
}
// value-only null fallback: zero vertices + zero edges
const zeroInt = [0x00, 0x00, 0x00, 0x00];
return Buffer.from([...zeroInt, ...zeroInt]);
}
const bufs = [];
if (fullyQualifiedFormat) {
bufs.push(Buffer.from([this.ioc.DataType.GRAPH, 0x00]));
}
const vertices = item.vertices ? Array.from(item.vertices.values()) : [];
const edges = item.edges ? Array.from(item.edges.values()) : [];
// {vertex_count}
bufs.push(this.ioc.intSerializer.serialize(vertices.length, false));
// vertices
for (const v of vertices) {
// {id}
bufs.push(this.ioc.anySerializer.serialize(v.id));
// {label} as 1-element list (value-only)
bufs.push(this.ioc.listSerializer.serialize([v.label], false));
const vps = Array.isArray(v.properties) ? v.properties : [];
// {vp_count}
bufs.push(this.ioc.intSerializer.serialize(vps.length, false));
for (const vp of vps) {
// {vp_id}
bufs.push(this.ioc.anySerializer.serialize(vp.id));
// {vp_label} as 1-element list (value-only)
bufs.push(this.ioc.listSerializer.serialize([vp.label], false));
// {vp_value}
bufs.push(this.ioc.anySerializer.serialize(vp.value));
// {parent} (always null)
bufs.push(this.ioc.unspecifiedNullSerializer.serialize(null));
// {meta_props} as value-only List<Property>
const metaProps = Array.isArray(vp.properties) ? vp.properties : [];
bufs.push(this.ioc.listSerializer.serialize(metaProps, false));
}
}
// {edge_count}
bufs.push(this.ioc.intSerializer.serialize(edges.length, false));
// edges
for (const e of edges) {
// {id}
bufs.push(this.ioc.anySerializer.serialize(e.id));
// {label} as 1-element list (value-only)
bufs.push(this.ioc.listSerializer.serialize([e.label], false));
// {inV_id}
bufs.push(this.ioc.anySerializer.serialize(e.inV && e.inV.id));
// {inV_label} (always null placeholder, fully-qualified)
bufs.push(this.ioc.unspecifiedNullSerializer.serialize(null));
// {outV_id}
bufs.push(this.ioc.anySerializer.serialize(e.outV && e.outV.id));
// {outV_label} (always null placeholder, fully-qualified)
bufs.push(this.ioc.unspecifiedNullSerializer.serialize(null));
// {parent} (always null)
bufs.push(this.ioc.unspecifiedNullSerializer.serialize(null));
// {edge_props} as value-only List<Property>
const props = Array.isArray(e.properties) ? e.properties : [];
bufs.push(this.ioc.listSerializer.serialize(props, false));
}
return Buffer.concat(bufs);
}
/**
* Async deserialization of graph value bytes from a StreamReader.
* @param {StreamReader} reader
* @param {number} valueFlag
* @param {number} typeCode
* @returns {Promise<Graph>}
*/
async deserializeValue(reader, valueFlag, typeCode) {
const graph = new Graph();
// {vertex_count} bare int
const vertexCount = await this.ioc.intSerializer.deserializeBare(reader);
for (let i = 0; i < vertexCount; i++) {
// {id} fully qualified
const vId = await this.ioc.anySerializer.deserialize(reader);
// {label} value-only list, first element
const vLabelList = await this.ioc.listSerializer.deserializeValue(reader, 0x00, this.ioc.DataType.LIST);
const vLabel = Array.isArray(vLabelList) && vLabelList.length > 0 ? vLabelList[0] : vLabelList;
const vertex = new Vertex(vId, vLabel, []);
graph.vertices.set(vId, vertex);
// {vp_count} bare int
const vpCount = await this.ioc.intSerializer.deserializeBare(reader);
for (let j = 0; j < vpCount; j++) {
// {vp_id} fully qualified
const vpId = await this.ioc.anySerializer.deserialize(reader);
// {vp_label} value-only list, first element
const vpLabelList = await this.ioc.listSerializer.deserializeValue(reader, 0x00, this.ioc.DataType.LIST);
const vpLabel = Array.isArray(vpLabelList) && vpLabelList.length > 0 ? vpLabelList[0] : vpLabelList;
// {vp_value} fully qualified
const vpValue = await this.ioc.anySerializer.deserialize(reader);
// {parent} fully qualified (always null)
await this.ioc.anySerializer.deserialize(reader);
// {meta_props} value-only list
const metaProps = await this.ioc.listSerializer.deserializeValue(reader, 0x00, this.ioc.DataType.LIST);
const vp = new VertexProperty(vpId, vpLabel, vpValue, metaProps || []);
vertex.properties.push(vp);
}
}
// {edge_count} bare int
const edgeCount = await this.ioc.intSerializer.deserializeBare(reader);
for (let i = 0; i < edgeCount; i++) {
// {id} fully qualified
const eId = await this.ioc.anySerializer.deserialize(reader);
// {label} value-only list, first element
const eLabelList = await this.ioc.listSerializer.deserializeValue(reader, 0x00, this.ioc.DataType.LIST);
const eLabel = Array.isArray(eLabelList) && eLabelList.length > 0 ? eLabelList[0] : eLabelList;
// {inV_id} fully qualified
const inVId = await this.ioc.anySerializer.deserialize(reader);
// {inV_label} fully qualified (always null placeholder) — discard
await this.ioc.anySerializer.deserialize(reader);
// {outV_id} fully qualified
const outVId = await this.ioc.anySerializer.deserialize(reader);
// {outV_label} fully qualified (always null placeholder) — discard
await this.ioc.anySerializer.deserialize(reader);
// {parent} fully qualified (always null) — discard
await this.ioc.anySerializer.deserialize(reader);
// {edge_props} value-only list
const edgeProps = await this.ioc.listSerializer.deserializeValue(reader, 0x00, this.ioc.DataType.LIST);
// Reuse vertex instances already in graph.vertices, otherwise build stand-ins
const inV = graph.vertices.get(inVId) || new Vertex(inVId, '', []);
const outV = graph.vertices.get(outVId) || new Vertex(outVId, '', []);
const edge = new Edge(eId, outV, eLabel, inV, edgeProps || []);
graph.edges.set(eId, edge);
}
return graph;
}
/**
* Async fully-qualified deserialization from a StreamReader.
* @param {StreamReader} reader
* @returns {Promise<Graph|null>}
*/
async deserialize(reader) {
const type_code = await reader.readUInt8();
if (type_code !== this.ioc.DataType.GRAPH) {
throw new Error(`GraphSerializer: unexpected {type_code}=0x${type_code.toString(16)}`);
}
const value_flag = await reader.readUInt8();
if (value_flag === 0x01) {
return null;
}
if (value_flag !== 0x00) {
throw new Error(`GraphSerializer: unexpected {value_flag}=0x${value_flag.toString(16)}`);
}
return this.deserializeValue(reader, value_flag, type_code);
}
}