├── .gitignore ├── lib ├── getLabels.js └── getLinksBuffer.js ├── .github └── workflows │ └── tests.yaml ├── package.json ├── LICENSE ├── test └── index.js ├── index.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | .nyc_output 17 | -------------------------------------------------------------------------------- /lib/getLabels.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gets all labels (i.e. node ids) as array, sorted in the `forEachNode()` order 3 | */ 4 | module.exports = getLabels; 5 | 6 | function getLabels(graph) { 7 | var labels = []; 8 | graph.forEachNode(saveNode); 9 | 10 | return labels; 11 | 12 | function saveNode(node) { 13 | labels.push(node.id); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [20.x, 18.x] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v2 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - run: npm ci 25 | - run: npm run build --if-present 26 | - run: npm test 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngraph.tobinary", 3 | "version": "1.0.2", 4 | "description": "Serialize ngraph.graph to binary format", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "tap --branches=50 --lines=80 --statements=80 --functions=80 test/*.js" 8 | }, 9 | "keywords": [ 10 | "ngraph", 11 | "graph", 12 | "serialize", 13 | "binary" 14 | ], 15 | "author": "Andrei Kashcha", 16 | "license": "MIT", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/anvaka/ngraph.tobinary" 20 | }, 21 | "dependencies": { 22 | "mkdirp": "^1.0.4", 23 | "ngraph.merge": "^1.0.0" 24 | }, 25 | "devDependencies": { 26 | "ngraph.graph": "^20.0.0", 27 | "tap": "^16.3.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/getLinksBuffer.js: -------------------------------------------------------------------------------- 1 | module.exports = getLinksBuffer; 2 | 3 | function getLinksBuffer(graph, labels) { 4 | var nodeMap = Object.create(null); 5 | 6 | labels.forEach(function(element, i) { 7 | // +1 to avoid 0 uncertainty 8 | nodeMap[element] = i + 1; 9 | }); 10 | 11 | var linksCount = graph.getLinksCount(); 12 | var buf = Buffer.alloc((labels.length + linksCount) * 4); 13 | var idx = 0; 14 | 15 | graph.forEachNode(function(node) { 16 | var startWritten = false; 17 | var start = nodeMap[node.id]; 18 | graph.forEachLinkedNode(node.id, saveLink, true); 19 | 20 | function saveLink(node) { 21 | if (!startWritten) { 22 | startWritten = true; 23 | buf.writeInt32LE(-start, idx); 24 | idx += 4; 25 | } 26 | var other = nodeMap[node.id]; 27 | 28 | buf.writeInt32LE(other, idx); 29 | idx += 4; 30 | } 31 | }); 32 | 33 | return buf.slice(0, idx); 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2025 Andrei Kashcha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | var createGraph = require('ngraph.graph'); 3 | 4 | test('it can get labels', function(t) { 5 | var graph = createGraph(); 6 | graph.addLink('hello', 'world'); 7 | var labels = require('../lib/getLabels.js')(graph); 8 | t.equal(labels.length, 2, 'two labels saved'); 9 | t.ok(labels[0] === 'hello' || labels[0] === 'world', 'first label is here'); 10 | t.ok(labels[1] === 'hello' || labels[1] === 'world', 'second label is here'); 11 | t.ok(labels[0] !== labels[1], 'both label are valid'); 12 | 13 | t.end(); 14 | }); 15 | 16 | test('it can get buffer', function(t) { 17 | var graph = createGraph(); 18 | graph.addLink('hello', 'world'); 19 | var labels = require('../lib/getLabels.js')(graph); 20 | var buffer = require('../lib/getLinksBuffer.js')(graph, labels); 21 | t.equal(buffer.length / 4, 2, 'Only two records in the buffer'); 22 | var from = buffer.readInt32LE(0); 23 | t.ok(from < 0, 'First node is the source'); 24 | t.equal(labels[-from - 1], 'hello'); 25 | var to = buffer.readInt32LE(4); 26 | t.ok(to > 0, 'Second node is the destination'); 27 | t.equal(labels[to - 1], 'world'); 28 | 29 | t.end(); 30 | }); 31 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var merge = require('ngraph.merge'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var mkdirp = require('mkdirp'); 5 | module.exports = save; 6 | 7 | function save(graph, options) { 8 | options = merge(options, { 9 | outDir: '.', 10 | labels: 'labels.json', 11 | meta: 'meta.json', 12 | links: 'links.bin' 13 | }); 14 | 15 | fixPaths(); 16 | 17 | var labels = require('./lib/getLabels.js')(graph); 18 | saveLabels(labels); 19 | 20 | var linksBuffer = require('./lib/getLinksBuffer.js')(graph, labels); 21 | fs.writeFileSync(options.links, linksBuffer); 22 | console.log(graph.getLinksCount() + ' links saved to ' + options.links); 23 | 24 | saveMeta(); 25 | 26 | function fixPaths() { 27 | if (!fs.existsSync(options.outDir)) { 28 | mkdirp.sync(options.outDir); 29 | } 30 | options.labels = path.join(options.outDir, options.labels); 31 | options.meta = path.join(options.outDir, options.meta); 32 | options.links = path.join(options.outDir, options.links); 33 | } 34 | 35 | function saveMeta() { 36 | var meta = getMetaInfo(); 37 | fs.writeFileSync(options.meta, JSON.stringify(meta), 'utf8'); 38 | console.log('Meta information saved to ' + options.meta); 39 | } 40 | 41 | function getMetaInfo() { 42 | return { 43 | date: +new Date(), 44 | nodeCount: graph.getNodesCount(), 45 | linkCount: graph.getLinksCount(), 46 | nodeFile: options.labels, 47 | linkFile: options.links, 48 | version: require(path.join(__dirname, 'package.json')).version 49 | }; 50 | } 51 | 52 | function saveLabels(labels) { 53 | fs.writeFileSync(options.labels, JSON.stringify(labels), 'utf8'); 54 | console.log(labels.length + ' ids saved to ' + options.labels); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ngraph.tobinary 2 | 3 | Serialize ngraph.graph to binary format 4 | 5 | # usage 6 | 7 | ``` js 8 | // let's say you have ngraph.graph instance: 9 | var graph = require('ngraph.generators').grid(10000, 10000); 10 | var save = require('ngraph.tobinary'); 11 | save(graph); 12 | ``` 13 | 14 | This will produce three new files: 15 | 16 | * `meta.json` - information about graph (e.g. number of edges/links, file names, serializer version, etc.) 17 | * `labels.json` - a json file with array of node identifiers. 18 | * `links.bin` - a binary file with compressed information about the graph. 19 | See more details in the `links.bin format` section below 20 | 21 | ## configuration 22 | 23 | You can override default settings of the serializer by passing optional configuration 24 | argument: 25 | 26 | ``` js 27 | var graph = require('ngraph.generators').grid(10000, 10000); 28 | var save = require('ngraph.tobinary'); 29 | save(graph, { 30 | outDir: '.', // folder where to save results. '.' by default 31 | labels: 'labels.json', // name of the labels file. labels.json by default 32 | meta: 'meta.json', // name of the file with meta information. meta.json by default 33 | links: 'links.bin' // file name for links array. links.bin by default 34 | }); 35 | 36 | ``` 37 | 38 | # links.bin format 39 | 40 | This file stores entire graph. Each record in the file is Int32 written in little-endian 41 | notation. Let's consider the following example: 42 | 43 | `labels.json` content: 44 | 45 | ``` 46 | ['a', 'b', 'c', 'd'] 47 | ``` 48 | 49 | `links.bin` content (in numerical view, spaces are just for formatting): 50 | 51 | ``` 52 | -1 2 3 -2 4 53 | ``` 54 | 55 | The negative 1 identifies the first "source" node of the graph, and denotes 1 based index 56 | of the element in the `labels.json` file. So in this case it is node `a`. 57 | 58 | Following positive integers `2` and `3` mean that `a` is connected to `labels[2 - 1]` 59 | and `labels[3 - 1]`. That is nodes `b` and `c` correspondingly. 60 | 61 | Then we see `-2`. This means that there are no more connections for the node `a`, 62 | and we should consider node `labels[2 - 1]` as the next "source" node. Subsequent 63 | positive integers show connections for the node `b`. It is node `d` (`labels[4 - 1]`). 64 | 65 | # install 66 | 67 | With [npm](https://npmjs.org) do: 68 | 69 | ``` 70 | npm install ngraph.tobinary 71 | ``` 72 | 73 | # license 74 | 75 | MIT 76 | --------------------------------------------------------------------------------