// // Copyright (C) Stanislaw Adaszewski, 2020 // Contact: s.adaszewski@gmail.com // Website: https://adared.ch/wba // License: GNU Affero General Public License, Version 3 // const rx = /^[a-f0-9]{32}\+[0-9]+/; const rootDir = {}; const streams = []; onmessage = function(e) { switch (e.data[0]) { case 'precreatePaths': precreatePaths(e.data[1]); postMessage([ 'precreatePathsResult' ]); break; case 'parseStream': parseStream(e.data[1]); postMessage([ 'parseStreamResult' ]); break; case 'listDirectory': { const lst = listDirectory(rootDir, e.data[1], e.data[2]); postMessage([ 'listDirectoryResult', lst ]) break; } case 'getData': postMessage([ 'getDataResult', rootDir, streams ]); break; case 'getFile': postMessage([ 'getFileResult', getFile(rootDir, streams, e.data[1]) ]); break; default: { const err = Error('Unknown verb: ' + e.data[0]); postMessage([ 'error', err.message ]); throw err; } } } function precreatePaths(paths) { for (let i = 0; i < paths.length; i++) { mkpath(rootDir, paths[i]); } } function parseStream(s) { if (!s) return; const tokens = s.split(' '); const streamName = unescapeName(tokens[0]); let n = tokens.map(t => rx.exec(t)); n = n.indexOf(null, 1); let locators = tokens.slice(1, n); let pos = 0; locators = locators.map(loc => { const sz = parseInt(loc.split('+')[1], 10); return [ loc, pos, pos += sz ]; }); let fileTokens = tokens.slice(n); let lastFile = null; let lastPath = null; fileTokens.map(t => { let seg = t.split(':'); seg = [ parseInt(seg[0], 10), parseInt(seg[1], 10), unescapeName(seg.slice(2).join(':')) ] const path = streamName + '/' + seg[2]; let f; if (path === lastPath) { f = lastFile; } else { let dirName = path.split('/'); const name = dirName[dirName.length - 1]; dirName = dirName.slice(0, dirName.length - 1); const d = mkpath(rootDir, dirName); lastFile = f = makeFile(d, name); lastPath = path; } appendFile(f, streams.length, seg); }); streams.push(locators); } function mkdir(parent, name) { if (name in parent && (parent[name] instanceof Array)) throw Error('File with the same name already exists'); if (name in parent) return parent[name]; const dir = {}; parent[name] = dir; return dir; } function mkpath(parent, path) { if (typeof(path) === 'string') path = path.split('/'); let dir = parent; for (let i = 1; i < path.length; i++) { dir = mkdir(dir, path[i]); } return dir; } function makeFile(dir, name) { if (name in dir) { if (!(dir[name] instanceof Array)) throw Error('Directory with the same name already exists'); return dir[name]; } const f = [[], 0]; dir[name] = f; return f; } function appendFile(f, sidx, seg) { f[0].push([ sidx, seg[0], seg[1] ]); f[1] += seg[1]; return f; } function unescapeName(name) { return name.replace(/(\\\\|\\[0-9]{3})/g, (_, $1) => ($1 === '\\\\' ? '\\' : String.fromCharCode(parseInt($1.substr(1), 8)))); } function findDir(parent, path, lenient=false) { if (typeof(path) === 'string') path = path.split('/'); if (path[0] !== '.') throw Error('Path must start with a dot (.)'); let dir = parent; for (let i = 1; i < path.length; i++) { if (!(path[i] in dir)) { if (lenient) return {}; else throw Error('Directory not found'); } dir = dir[path[i]]; } return dir; } function listDirectory(rootDir, path, lenient=false) { let dir = findDir(rootDir, path, lenient); let keys = Object.keys(dir); keys.sort(); let subdirs = keys.filter(k => !(dir[k] instanceof Array)); let files = keys.filter(k => (dir[k] instanceof Array)); let res = subdirs.map(k => [ 'd', k, null ]); res = res.concat(files.map(k => [ 'f', k, dir[k][1] ])); return res; } function getFile(rootDir, streams, path) { if (typeof(path) === 'string') path = path.split('/'); if (path.length < 2) throw Error('Invalid file path'); const name = path[path.length - 1]; const dir = findDir(rootDir, path.slice(0, path.length - 1)); if (!(name in dir)) throw Error('File not found'); if (!(dir[name] instanceof Array)) throw Error('Path points to a directory not a file'); let file = dir[name]; file = [ file[0].map(seg => { const stm = streams[seg[0]]; const used = stm.map(loc => !( loc[2] <= seg[1] || loc[1] >= seg[1] + seg[2] ) ); const start = used.indexOf(true); const end = used.lastIndexOf(true) + 1; if (start === -1) return []; const res = []; for (let i = start; i < end; i++) { const loc = stm[i]; res.push([ loc[0], Math.max(0, seg[1] - loc[1]), Math.min(loc[2] - loc[1], seg[1] + seg[2] - loc[1]) ]); } return res; }), file[1] ]; file[0] = file[0].reduce((a, b) => a.concat(b)); return file; }