byte_stream.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. export class ByteStream {
  2. constructor(parameters = {}) {
  3. if ("view" in parameters) {
  4. this.fromUint8Array(parameters.view);
  5. }
  6. else if ("buffer" in parameters) {
  7. this.fromArrayBuffer(parameters.buffer);
  8. }
  9. else if ("string" in parameters) {
  10. this.fromString(parameters.string);
  11. }
  12. else if ("hexstring" in parameters) {
  13. this.fromHexString(parameters.hexstring);
  14. }
  15. else {
  16. if ("length" in parameters && parameters.length > 0) {
  17. this.length = parameters.length;
  18. if (parameters.stub) {
  19. for (let i = 0; i < this._view.length; i++) {
  20. this._view[i] = parameters.stub;
  21. }
  22. }
  23. }
  24. else {
  25. this.length = 0;
  26. }
  27. }
  28. }
  29. set buffer(value) {
  30. this._buffer = value;
  31. this._view = new Uint8Array(this._buffer);
  32. }
  33. get buffer() {
  34. return this._buffer;
  35. }
  36. set view(value) {
  37. this._buffer = new ArrayBuffer(value.length);
  38. this._view = new Uint8Array(this._buffer);
  39. this._view.set(value);
  40. }
  41. get view() {
  42. return this._view;
  43. }
  44. get length() {
  45. return this.view.byteLength;
  46. }
  47. set length(value) {
  48. this._buffer = new ArrayBuffer(value);
  49. this._view = new Uint8Array(this._buffer);
  50. }
  51. clear() {
  52. this._buffer = new ArrayBuffer(0);
  53. this._view = new Uint8Array(this._buffer);
  54. }
  55. fromArrayBuffer(array) {
  56. this._buffer = array;
  57. this._view = new Uint8Array(this._buffer);
  58. }
  59. fromUint8Array(array) {
  60. this.fromArrayBuffer(new Uint8Array(array).buffer);
  61. }
  62. fromString(string) {
  63. const stringLength = string.length;
  64. this.length = stringLength;
  65. for (let i = 0; i < stringLength; i++)
  66. this.view[i] = string.charCodeAt(i);
  67. }
  68. toString(start = 0, length = (this.view.length - start)) {
  69. let result = "";
  70. if ((start >= this.view.length) || (start < 0)) {
  71. start = 0;
  72. }
  73. if ((length >= this.view.length) || (length < 0)) {
  74. length = this.view.length - start;
  75. }
  76. for (let i = start; i < (start + length); i++)
  77. result += String.fromCharCode(this.view[i]);
  78. return result;
  79. }
  80. fromHexString(hexString) {
  81. const stringLength = hexString.length;
  82. this.buffer = new ArrayBuffer(stringLength >> 1);
  83. this.view = new Uint8Array(this.buffer);
  84. const hexMap = new Map();
  85. hexMap.set("0", 0x00);
  86. hexMap.set("1", 0x01);
  87. hexMap.set("2", 0x02);
  88. hexMap.set("3", 0x03);
  89. hexMap.set("4", 0x04);
  90. hexMap.set("5", 0x05);
  91. hexMap.set("6", 0x06);
  92. hexMap.set("7", 0x07);
  93. hexMap.set("8", 0x08);
  94. hexMap.set("9", 0x09);
  95. hexMap.set("A", 0x0A);
  96. hexMap.set("a", 0x0A);
  97. hexMap.set("B", 0x0B);
  98. hexMap.set("b", 0x0B);
  99. hexMap.set("C", 0x0C);
  100. hexMap.set("c", 0x0C);
  101. hexMap.set("D", 0x0D);
  102. hexMap.set("d", 0x0D);
  103. hexMap.set("E", 0x0E);
  104. hexMap.set("e", 0x0E);
  105. hexMap.set("F", 0x0F);
  106. hexMap.set("f", 0x0F);
  107. let j = 0;
  108. let temp = 0x00;
  109. for (let i = 0; i < stringLength; i++) {
  110. if (!(i % 2)) {
  111. temp = hexMap.get(hexString.charAt(i)) << 4;
  112. }
  113. else {
  114. temp |= hexMap.get(hexString.charAt(i));
  115. this.view[j] = temp;
  116. j++;
  117. }
  118. }
  119. }
  120. toHexString(start = 0, length = (this.view.length - start)) {
  121. let result = "";
  122. if ((start >= this.view.length) || (start < 0)) {
  123. start = 0;
  124. }
  125. if ((length >= this.view.length) || (length < 0)) {
  126. length = this.view.length - start;
  127. }
  128. for (let i = start; i < (start + length); i++) {
  129. const str = this.view[i].toString(16).toUpperCase();
  130. result = result + ((str.length == 1) ? "0" : "") + str;
  131. }
  132. return result;
  133. }
  134. copy(start = 0, length = (this.length - start)) {
  135. if (!start && !this.length) {
  136. return new ByteStream();
  137. }
  138. if ((start < 0) || (start > (this.length - 1))) {
  139. throw new Error(`Wrong start position: ${start}`);
  140. }
  141. const stream = new ByteStream({
  142. buffer: this._buffer.slice(start, start + length)
  143. });
  144. return stream;
  145. }
  146. slice(start = 0, end = this.length) {
  147. if (!start && !this.length) {
  148. return new ByteStream();
  149. }
  150. if ((start < 0) || (start > (this.length - 1))) {
  151. throw new Error(`Wrong start position: ${start}`);
  152. }
  153. const stream = new ByteStream({
  154. buffer: this._buffer.slice(start, end),
  155. });
  156. return stream;
  157. }
  158. realloc(size) {
  159. const buffer = new ArrayBuffer(size);
  160. const view = new Uint8Array(buffer);
  161. if (size > this._view.length)
  162. view.set(this._view);
  163. else {
  164. view.set(new Uint8Array(this._buffer, 0, size));
  165. }
  166. this._buffer = buffer;
  167. this._view = new Uint8Array(this._buffer);
  168. }
  169. append(stream) {
  170. const initialSize = this.length;
  171. const streamViewLength = stream.length;
  172. const subarrayView = stream._view.subarray();
  173. this.realloc(initialSize + streamViewLength);
  174. this._view.set(subarrayView, initialSize);
  175. }
  176. insert(stream, start = 0, length = (this.length - start)) {
  177. if (start > (this.length - 1))
  178. return false;
  179. if (length > (this.length - start)) {
  180. length = this.length - start;
  181. }
  182. if (length > stream.length) {
  183. length = stream.length;
  184. }
  185. if (length == stream.length)
  186. this._view.set(stream._view, start);
  187. else {
  188. this._view.set(stream._view.subarray(0, length), start);
  189. }
  190. return true;
  191. }
  192. isEqual(stream) {
  193. if (this.length != stream.length)
  194. return false;
  195. for (let i = 0; i < stream.length; i++) {
  196. if (this.view[i] != stream.view[i])
  197. return false;
  198. }
  199. return true;
  200. }
  201. isEqualView(view) {
  202. if (view.length != this.view.length)
  203. return false;
  204. for (let i = 0; i < view.length; i++) {
  205. if (this.view[i] != view[i])
  206. return false;
  207. }
  208. return true;
  209. }
  210. findPattern(pattern, start_, length_, backward_) {
  211. const { start, length, backward } = this.prepareFindParameters(start_, length_, backward_);
  212. const patternLength = pattern.length;
  213. if (patternLength > length) {
  214. return (-1);
  215. }
  216. const patternArray = [];
  217. for (let i = 0; i < patternLength; i++)
  218. patternArray.push(pattern.view[i]);
  219. for (let i = 0; i <= (length - patternLength); i++) {
  220. let equal = true;
  221. const equalStart = (backward) ? (start - patternLength - i) : (start + i);
  222. for (let j = 0; j < patternLength; j++) {
  223. if (this.view[j + equalStart] != patternArray[j]) {
  224. equal = false;
  225. break;
  226. }
  227. }
  228. if (equal) {
  229. return (backward) ? (start - patternLength - i) : (start + patternLength + i);
  230. }
  231. }
  232. return (-1);
  233. }
  234. findFirstIn(patterns, start_, length_, backward_) {
  235. const { start, length, backward } = this.prepareFindParameters(start_, length_, backward_);
  236. const result = {
  237. id: (-1),
  238. position: (backward) ? 0 : (start + length),
  239. length: 0
  240. };
  241. for (let i = 0; i < patterns.length; i++) {
  242. const position = this.findPattern(patterns[i], start, length, backward);
  243. if (position != (-1)) {
  244. let valid = false;
  245. const patternLength = patterns[i].length;
  246. if (backward) {
  247. if ((position - patternLength) >= (result.position - result.length))
  248. valid = true;
  249. }
  250. else {
  251. if ((position - patternLength) <= (result.position - result.length))
  252. valid = true;
  253. }
  254. if (valid) {
  255. result.position = position;
  256. result.id = i;
  257. result.length = patternLength;
  258. }
  259. }
  260. }
  261. return result;
  262. }
  263. findAllIn(patterns, start_, length_) {
  264. let { start, length } = this.prepareFindParameters(start_, length_);
  265. const result = [];
  266. let patternFound = {
  267. id: (-1),
  268. position: start
  269. };
  270. do {
  271. const position = patternFound.position;
  272. patternFound = this.findFirstIn(patterns, patternFound.position, length);
  273. if (patternFound.id == (-1)) {
  274. break;
  275. }
  276. length -= (patternFound.position - position);
  277. result.push({
  278. id: patternFound.id,
  279. position: patternFound.position
  280. });
  281. } while (true);
  282. return result;
  283. }
  284. findAllPatternIn(pattern, start_, length_) {
  285. const { start, length } = this.prepareFindParameters(start_, length_);
  286. const result = [];
  287. const patternLength = pattern.length;
  288. if (patternLength > length) {
  289. return (-1);
  290. }
  291. const patternArray = Array.from(pattern.view);
  292. for (let i = 0; i <= (length - patternLength); i++) {
  293. let equal = true;
  294. const equalStart = start + i;
  295. for (let j = 0; j < patternLength; j++) {
  296. if (this.view[j + equalStart] != patternArray[j]) {
  297. equal = false;
  298. break;
  299. }
  300. }
  301. if (equal) {
  302. result.push(start + patternLength + i);
  303. i += (patternLength - 1);
  304. }
  305. }
  306. return result;
  307. }
  308. findFirstNotIn(patterns, start_, length_, backward_) {
  309. let { start, length, backward } = this.prepareFindParameters(start_, length_, backward_);
  310. const result = {
  311. left: {
  312. id: (-1),
  313. position: start
  314. },
  315. right: {
  316. id: (-1),
  317. position: 0
  318. },
  319. value: new ByteStream()
  320. };
  321. let currentLength = length;
  322. while (currentLength > 0) {
  323. result.right = this.findFirstIn(patterns, (backward) ? (start - length + currentLength) : (start + length - currentLength), currentLength, backward);
  324. if (result.right.id == (-1)) {
  325. length = currentLength;
  326. if (backward) {
  327. start -= length;
  328. }
  329. else {
  330. start = result.left.position;
  331. }
  332. result.value = new ByteStream({
  333. buffer: this._buffer.slice(start, start + length),
  334. });
  335. break;
  336. }
  337. if (result.right.position != ((backward) ? (result.left.position - patterns[result.right.id].length) : (result.left.position + patterns[result.right.id].length))) {
  338. if (backward) {
  339. start = result.right.position + patterns[result.right.id].length;
  340. length = result.left.position - result.right.position - patterns[result.right.id].length;
  341. }
  342. else {
  343. start = result.left.position;
  344. length = result.right.position - result.left.position - patterns[result.right.id].length;
  345. }
  346. result.value = new ByteStream({
  347. buffer: this._buffer.slice(start, start + length),
  348. });
  349. break;
  350. }
  351. result.left = result.right;
  352. currentLength -= patterns[result.right.id].length;
  353. }
  354. if (backward) {
  355. const temp = result.right;
  356. result.right = result.left;
  357. result.left = temp;
  358. }
  359. return result;
  360. }
  361. findAllNotIn(patterns, start_, length_) {
  362. let { start, length } = this.prepareFindParameters(start_, length_);
  363. const result = [];
  364. let patternFound = {
  365. left: {
  366. id: (-1),
  367. position: start
  368. },
  369. right: {
  370. id: (-1),
  371. position: start
  372. },
  373. value: new ByteStream()
  374. };
  375. do {
  376. const position = patternFound.right.position;
  377. patternFound = this.findFirstNotIn(patterns, patternFound.right.position, length);
  378. length -= (patternFound.right.position - position);
  379. result.push({
  380. left: {
  381. id: patternFound.left.id,
  382. position: patternFound.left.position
  383. },
  384. right: {
  385. id: patternFound.right.id,
  386. position: patternFound.right.position
  387. },
  388. value: patternFound.value
  389. });
  390. } while (patternFound.right.id != (-1));
  391. return result;
  392. }
  393. findFirstSequence(patterns, start_, length_, backward_) {
  394. let { start, length, backward } = this.prepareFindParameters(start_, length_, backward_);
  395. const firstIn = this.skipNotPatterns(patterns, start, length, backward);
  396. if (firstIn == (-1)) {
  397. return {
  398. position: (-1),
  399. value: new ByteStream()
  400. };
  401. }
  402. const firstNotIn = this.skipPatterns(patterns, firstIn, length - ((backward) ? (start - firstIn) : (firstIn - start)), backward);
  403. if (backward) {
  404. start = firstNotIn;
  405. length = (firstIn - firstNotIn);
  406. }
  407. else {
  408. start = firstIn;
  409. length = (firstNotIn - firstIn);
  410. }
  411. const value = new ByteStream({
  412. buffer: this._buffer.slice(start, start + length),
  413. });
  414. return {
  415. position: firstNotIn,
  416. value
  417. };
  418. }
  419. findAllSequences(patterns, start_, length_) {
  420. let { start, length } = this.prepareFindParameters(start_, length_);
  421. const result = [];
  422. let patternFound = {
  423. position: start,
  424. value: new ByteStream()
  425. };
  426. do {
  427. const position = patternFound.position;
  428. patternFound = this.findFirstSequence(patterns, patternFound.position, length);
  429. if (patternFound.position != (-1)) {
  430. length -= (patternFound.position - position);
  431. result.push({
  432. position: patternFound.position,
  433. value: patternFound.value,
  434. });
  435. }
  436. } while (patternFound.position != (-1));
  437. return result;
  438. }
  439. findPairedPatterns(leftPattern, rightPattern, start_, length_) {
  440. const result = [];
  441. if (leftPattern.isEqual(rightPattern))
  442. return result;
  443. const { start, length } = this.prepareFindParameters(start_, length_);
  444. let currentPositionLeft = 0;
  445. const leftPatterns = this.findAllPatternIn(leftPattern, start, length);
  446. if (!Array.isArray(leftPatterns) || leftPatterns.length == 0) {
  447. return result;
  448. }
  449. const rightPatterns = this.findAllPatternIn(rightPattern, start, length);
  450. if (!Array.isArray(rightPatterns) || rightPatterns.length == 0) {
  451. return result;
  452. }
  453. while (currentPositionLeft < leftPatterns.length) {
  454. if (rightPatterns.length == 0) {
  455. break;
  456. }
  457. if (leftPatterns[0] == rightPatterns[0]) {
  458. result.push({
  459. left: leftPatterns[0],
  460. right: rightPatterns[0]
  461. });
  462. leftPatterns.splice(0, 1);
  463. rightPatterns.splice(0, 1);
  464. continue;
  465. }
  466. if (leftPatterns[currentPositionLeft] > rightPatterns[0]) {
  467. break;
  468. }
  469. while (leftPatterns[currentPositionLeft] < rightPatterns[0]) {
  470. currentPositionLeft++;
  471. if (currentPositionLeft >= leftPatterns.length) {
  472. break;
  473. }
  474. }
  475. result.push({
  476. left: leftPatterns[currentPositionLeft - 1],
  477. right: rightPatterns[0]
  478. });
  479. leftPatterns.splice(currentPositionLeft - 1, 1);
  480. rightPatterns.splice(0, 1);
  481. currentPositionLeft = 0;
  482. }
  483. result.sort((a, b) => (a.left - b.left));
  484. return result;
  485. }
  486. findPairedArrays(inputLeftPatterns, inputRightPatterns, start_, length_) {
  487. const { start, length } = this.prepareFindParameters(start_, length_);
  488. const result = [];
  489. let currentPositionLeft = 0;
  490. const leftPatterns = this.findAllIn(inputLeftPatterns, start, length);
  491. if (leftPatterns.length == 0)
  492. return result;
  493. const rightPatterns = this.findAllIn(inputRightPatterns, start, length);
  494. if (rightPatterns.length == 0)
  495. return result;
  496. while (currentPositionLeft < leftPatterns.length) {
  497. if (rightPatterns.length == 0) {
  498. break;
  499. }
  500. if (leftPatterns[0].position == rightPatterns[0].position) {
  501. result.push({
  502. left: leftPatterns[0],
  503. right: rightPatterns[0]
  504. });
  505. leftPatterns.splice(0, 1);
  506. rightPatterns.splice(0, 1);
  507. continue;
  508. }
  509. if (leftPatterns[currentPositionLeft].position > rightPatterns[0].position) {
  510. break;
  511. }
  512. while (leftPatterns[currentPositionLeft].position < rightPatterns[0].position) {
  513. currentPositionLeft++;
  514. if (currentPositionLeft >= leftPatterns.length) {
  515. break;
  516. }
  517. }
  518. result.push({
  519. left: leftPatterns[currentPositionLeft - 1],
  520. right: rightPatterns[0]
  521. });
  522. leftPatterns.splice(currentPositionLeft - 1, 1);
  523. rightPatterns.splice(0, 1);
  524. currentPositionLeft = 0;
  525. }
  526. result.sort((a, b) => (a.left.position - b.left.position));
  527. return result;
  528. }
  529. replacePattern(searchPattern, replacePattern, start_, length_, findAllResult = null) {
  530. let result = [];
  531. let i;
  532. const output = {
  533. status: (-1),
  534. searchPatternPositions: [],
  535. replacePatternPositions: []
  536. };
  537. const { start, length } = this.prepareFindParameters(start_, length_);
  538. if (findAllResult == null) {
  539. result = this.findAllIn([searchPattern], start, length);
  540. if (result.length == 0) {
  541. return output;
  542. }
  543. }
  544. else {
  545. result = findAllResult;
  546. }
  547. output.searchPatternPositions.push(...Array.from(result, element => element.position));
  548. const patternDifference = searchPattern.length - replacePattern.length;
  549. const changedBuffer = new ArrayBuffer(this.view.length - (result.length * patternDifference));
  550. const changedView = new Uint8Array(changedBuffer);
  551. changedView.set(new Uint8Array(this.buffer, 0, start));
  552. for (i = 0; i < result.length; i++) {
  553. const currentPosition = (i == 0) ? start : result[i - 1].position;
  554. changedView.set(new Uint8Array(this.buffer, currentPosition, result[i].position - searchPattern.length - currentPosition), currentPosition - i * patternDifference);
  555. changedView.set(replacePattern.view, result[i].position - searchPattern.length - i * patternDifference);
  556. output.replacePatternPositions.push(result[i].position - searchPattern.length - i * patternDifference);
  557. }
  558. i--;
  559. changedView.set(new Uint8Array(this.buffer, result[i].position, this.length - result[i].position), result[i].position - searchPattern.length + replacePattern.length - i * patternDifference);
  560. this.buffer = changedBuffer;
  561. this.view = new Uint8Array(this.buffer);
  562. output.status = 1;
  563. return output;
  564. }
  565. skipPatterns(patterns, start_, length_, backward_) {
  566. const { start, length, backward } = this.prepareFindParameters(start_, length_, backward_);
  567. let result = start;
  568. for (let k = 0; k < patterns.length; k++) {
  569. const patternLength = patterns[k].length;
  570. const equalStart = (backward) ? (result - patternLength) : (result);
  571. let equal = true;
  572. for (let j = 0; j < patternLength; j++) {
  573. if (this.view[j + equalStart] != patterns[k].view[j]) {
  574. equal = false;
  575. break;
  576. }
  577. }
  578. if (equal) {
  579. k = (-1);
  580. if (backward) {
  581. result -= patternLength;
  582. if (result <= 0)
  583. return result;
  584. }
  585. else {
  586. result += patternLength;
  587. if (result >= (start + length))
  588. return result;
  589. }
  590. }
  591. }
  592. return result;
  593. }
  594. skipNotPatterns(patterns, start_, length_, backward_) {
  595. const { start, length, backward } = this.prepareFindParameters(start_, length_, backward_);
  596. let result = (-1);
  597. for (let i = 0; i < length; i++) {
  598. for (let k = 0; k < patterns.length; k++) {
  599. const patternLength = patterns[k].length;
  600. const equalStart = (backward) ? (start - i - patternLength) : (start + i);
  601. let equal = true;
  602. for (let j = 0; j < patternLength; j++) {
  603. if (this.view[j + equalStart] != patterns[k].view[j]) {
  604. equal = false;
  605. break;
  606. }
  607. }
  608. if (equal) {
  609. result = (backward) ? (start - i) : (start + i);
  610. break;
  611. }
  612. }
  613. if (result != (-1)) {
  614. break;
  615. }
  616. }
  617. return result;
  618. }
  619. prepareFindParameters(start = null, length = null, backward = false) {
  620. if (start === null) {
  621. start = (backward) ? this.length : 0;
  622. }
  623. if (start > this.length) {
  624. start = this.length;
  625. }
  626. if (backward) {
  627. if (length === null) {
  628. length = start;
  629. }
  630. if (length > start) {
  631. length = start;
  632. }
  633. }
  634. else {
  635. if (length === null) {
  636. length = this.length - start;
  637. }
  638. if (length > (this.length - start)) {
  639. length = this.length - start;
  640. }
  641. }
  642. return { start, length, backward };
  643. }
  644. }