IF YOU WOULD LIKE TO GET AN ACCOUNT, please write an email to s dot adaszewski at gmail dot com. User accounts are meant only to report issues and/or generate pull requests. This is a purpose-specific Git hosting for ADARED projects. Thank you for your understanding!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
5.1KB

  1. //
  2. // Copyright (C) Stanislaw Adaszewski, 2020
  3. // Contact: s.adaszewski@gmail.com
  4. // Website: https://adared.ch/wba
  5. // License: GNU Affero General Public License, Version 3
  6. //
  7. const rx = /^[a-f0-9]{32}\+[0-9]+/;
  8. const rootDir = {};
  9. const streams = [];
  10. onmessage = function(e) {
  11. switch (e.data[0]) {
  12. case 'precreatePaths':
  13. precreatePaths(e.data[1]);
  14. postMessage([ 'precreatePathsResult' ]);
  15. break;
  16. case 'parseStream':
  17. parseStream(e.data[1]);
  18. postMessage([ 'parseStreamResult' ]);
  19. break;
  20. case 'listDirectory': {
  21. const lst = listDirectory(rootDir, e.data[1], e.data[2]);
  22. postMessage([ 'listDirectoryResult', lst ])
  23. break; }
  24. case 'getData':
  25. postMessage([ 'getDataResult', rootDir, streams ]);
  26. break;
  27. case 'getFile':
  28. postMessage([ 'getFileResult', getFile(rootDir, streams, e.data[1]) ]);
  29. break;
  30. default: {
  31. const err = Error('Unknown verb: ' + e.data[0]);
  32. postMessage([ 'error', err.message ]);
  33. throw err; }
  34. }
  35. }
  36. function precreatePaths(paths) {
  37. for (let i = 0; i < paths.length; i++) {
  38. mkpath(rootDir, paths[i]);
  39. }
  40. }
  41. function parseStream(s) {
  42. if (!s) return;
  43. const tokens = s.split(' ');
  44. const streamName = unescapeName(tokens[0]);
  45. let n = tokens.map(t => rx.exec(t));
  46. n = n.indexOf(null, 1);
  47. let locators = tokens.slice(1, n);
  48. let pos = 0;
  49. locators = locators.map(loc => {
  50. const sz = parseInt(loc.split('+')[1], 10);
  51. return [ loc, pos, pos += sz ];
  52. });
  53. let fileTokens = tokens.slice(n);
  54. let lastFile = null;
  55. let lastPath = null;
  56. fileTokens.map(t => {
  57. let seg = t.split(':');
  58. seg = [ parseInt(seg[0], 10), parseInt(seg[1], 10),
  59. unescapeName(seg.slice(2).join(':')) ]
  60. const path = streamName + '/' + seg[2];
  61. let f;
  62. if (path === lastPath) {
  63. f = lastFile;
  64. } else {
  65. let dirName = path.split('/');
  66. const name = dirName[dirName.length - 1];
  67. dirName = dirName.slice(0, dirName.length - 1);
  68. const d = mkpath(rootDir, dirName);
  69. lastFile = f = makeFile(d, name);
  70. lastPath = path;
  71. }
  72. appendFile(f, streams.length, seg);
  73. });
  74. streams.push(locators);
  75. }
  76. function mkdir(parent, name) {
  77. if (name in parent && (parent[name] instanceof Array))
  78. throw Error('File with the same name already exists');
  79. if (name in parent)
  80. return parent[name];
  81. const dir = {};
  82. parent[name] = dir;
  83. return dir;
  84. }
  85. function mkpath(parent, path) {
  86. if (typeof(path) === 'string')
  87. path = path.split('/');
  88. let dir = parent;
  89. for (let i = 1; i < path.length; i++) {
  90. dir = mkdir(dir, path[i]);
  91. }
  92. return dir;
  93. }
  94. function makeFile(dir, name) {
  95. if (name in dir) {
  96. if (!(dir[name] instanceof Array))
  97. throw Error('Directory with the same name already exists');
  98. return dir[name];
  99. }
  100. const f = [[], 0];
  101. dir[name] = f;
  102. return f;
  103. }
  104. function appendFile(f, sidx, seg) {
  105. f[0].push([ sidx, seg[0], seg[1] ]);
  106. f[1] += seg[1];
  107. return f;
  108. }
  109. function unescapeName(name) {
  110. return name.replace(/(\\\\|\\[0-9]{3})/g,
  111. (_, $1) => ($1 === '\\\\' ? '\\' : String.fromCharCode(parseInt($1.substr(1), 8))));
  112. }
  113. function findDir(parent, path, lenient=false) {
  114. if (typeof(path) === 'string')
  115. path = path.split('/');
  116. if (path[0] !== '.')
  117. throw Error('Path must start with a dot (.)');
  118. let dir = parent;
  119. for (let i = 1; i < path.length; i++) {
  120. if (!(path[i] in dir)) {
  121. if (lenient)
  122. return {};
  123. else
  124. throw Error('Directory not found');
  125. }
  126. dir = dir[path[i]];
  127. }
  128. return dir;
  129. }
  130. function listDirectory(rootDir, path, lenient=false) {
  131. let dir = findDir(rootDir, path, lenient);
  132. let keys = Object.keys(dir);
  133. keys.sort();
  134. let subdirs = keys.filter(k => !(dir[k] instanceof Array));
  135. let files = keys.filter(k => (dir[k] instanceof Array));
  136. let res = subdirs.map(k => [ 'd', k, null ]);
  137. res = res.concat(files.map(k => [ 'f', k, dir[k][1] ]));
  138. return res;
  139. }
  140. function getFile(rootDir, streams, path) {
  141. if (typeof(path) === 'string')
  142. path = path.split('/');
  143. if (path.length < 2)
  144. throw Error('Invalid file path');
  145. const name = path[path.length - 1];
  146. const dir = findDir(rootDir, path.slice(0, path.length - 1));
  147. if (!(name in dir))
  148. throw Error('File not found');
  149. if (!(dir[name] instanceof Array))
  150. throw Error('Path points to a directory not a file');
  151. let file = dir[name];
  152. file = [ file[0].map(seg => {
  153. const stm = streams[seg[0]];
  154. const used = stm.map(loc => !( loc[2] <= seg[1] || loc[1] >= seg[1] + seg[2] ) );
  155. const start = used.indexOf(true);
  156. const end = used.lastIndexOf(true) + 1;
  157. if (start === -1)
  158. return [];
  159. const res = [];
  160. for (let i = start; i < end; i++) {
  161. const loc = stm[i];
  162. res.push([ loc[0], Math.max(0, seg[1] - loc[1]),
  163. Math.min(loc[2] - loc[1], seg[1] + seg[2] - loc[1]) ]);
  164. }
  165. return res;
  166. }), file[1] ];
  167. file[0] = file[0].reduce((a, b) => a.concat(b));
  168. return file;
  169. }