n3 2.11.6 → 2.11.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/N3Lexer.js CHANGED
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _buffer = require("buffer");
8
7
  var _IRIs = _interopRequireDefault(require("./IRIs"));
9
8
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
9
  // **N3Lexer** tokenizes N3 documents.
@@ -94,6 +93,9 @@ function isSeparatorCode(code) {
94
93
  return code === SPACE || code === TAB || code === LF || code === CR || code === HASH;
95
94
  }
96
95
 
96
+ // Unfinished input in a stream up to this length is tokenized again with every chunk
97
+ const MIN_RESCAN_LENGTH = 1024;
98
+
97
99
  // ## Constructor
98
100
  class N3Lexer {
99
101
  constructor(options) {
@@ -721,31 +723,39 @@ class N3Lexer {
721
723
  }
722
724
  // Otherwise, the input must be a stream
723
725
  else {
724
- this._pendingBuffer = null;
726
+ let decoder,
727
+ retryLength = 0;
725
728
  if (typeof input.setEncoding === 'function') input.setEncoding('utf8');
726
729
  // Adds the data chunk to the buffer and parses as far as possible
727
730
  input.on('data', data => {
728
731
  if (this._tokenization === tokenization && this._input !== null && data.length !== 0) {
729
- // Prepend any previous pending writes
730
- if (this._pendingBuffer) {
731
- data = _buffer.Buffer.concat([this._pendingBuffer, data]);
732
- this._pendingBuffer = null;
733
- }
734
- // Hold if the buffer ends in an incomplete unicode sequence
735
- if (data[data.length - 1] & 0x80) {
736
- this._pendingBuffer = data;
732
+ // Decode bytes, keeping an incomplete trailing character for the next chunk
733
+ if (typeof data !== 'string') {
734
+ decoder = decoder || new TextDecoder('utf-8', {
735
+ ignoreBOM: true
736
+ });
737
+ if (!(data = decoder.decode(data, {
738
+ stream: true
739
+ }))) return;
737
740
  }
738
- // Otherwise, tokenize as far as possible
739
- else {
740
- // Only read a BOM at the start
741
- if (typeof this._input === 'undefined') this._input = this._readStartingBom(typeof data === 'string' ? data : data.toString());else this._input += data;
741
+ // Only read a BOM at the start
742
+ if (typeof this._input === 'undefined') this._input = this._readStartingBom(data);else this._input += data;
743
+ // Tokenize as far as possible. When a previous attempt left a long unfinished token,
744
+ // wait until the buffered input has doubled, so the token is not rescanned for every chunk.
745
+ if (this._input.length >= retryLength) {
742
746
  this._tokenizeToEnd(callback, false);
747
+ retryLength = this._input !== null && this._input.length > MIN_RESCAN_LENGTH ? 2 * this._input.length : 0;
743
748
  }
744
749
  }
745
750
  });
746
751
  // Parses until the end
747
752
  input.on('end', () => {
748
- if (this._tokenization === tokenization && typeof this._input === 'string') this._tokenizeToEnd(callback, true);
753
+ if (this._tokenization === tokenization && this._input !== null) {
754
+ // Decode any incomplete character left at the end
755
+ const rest = decoder ? decoder.decode() : '';
756
+ if (rest) this._input = typeof this._input === 'string' ? this._input + rest : rest;
757
+ if (typeof this._input === 'string') this._tokenizeToEnd(callback, true);
758
+ }
749
759
  });
750
760
  input.on('error', error => {
751
761
  if (this._tokenization === tokenization) callback(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n3",
3
- "version": "2.11.6",
3
+ "version": "2.11.7",
4
4
  "description": "Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.",
5
5
  "author": "Ruben Verborgh <ruben.verborgh@gmail.com>",
6
6
  "keywords": [
package/src/N3Lexer.js CHANGED
@@ -1,5 +1,4 @@
1
1
  // **N3Lexer** tokenizes N3 documents.
2
- import { Buffer } from 'buffer';
3
2
  import namespaces from './IRIs';
4
3
 
5
4
  const { xsd } = namespaces;
@@ -59,6 +58,9 @@ function isSeparatorCode(code) {
59
58
  return code === SPACE || code === TAB || code === LF || code === CR || code === HASH;
60
59
  }
61
60
 
61
+ // Unfinished input in a stream up to this length is tokenized again with every chunk
62
+ const MIN_RESCAN_LENGTH = 1024;
63
+
62
64
  // ## Constructor
63
65
  export default class N3Lexer {
64
66
  constructor(options) {
@@ -747,36 +749,42 @@ export default class N3Lexer {
747
749
  }
748
750
  // Otherwise, the input must be a stream
749
751
  else {
750
- this._pendingBuffer = null;
752
+ let decoder, retryLength = 0;
751
753
  if (typeof input.setEncoding === 'function')
752
754
  input.setEncoding('utf8');
753
755
  // Adds the data chunk to the buffer and parses as far as possible
754
756
  input.on('data', data => {
755
757
  if (this._tokenization === tokenization && this._input !== null && data.length !== 0) {
756
- // Prepend any previous pending writes
757
- if (this._pendingBuffer) {
758
- data = Buffer.concat([this._pendingBuffer, data]);
759
- this._pendingBuffer = null;
760
- }
761
- // Hold if the buffer ends in an incomplete unicode sequence
762
- if (data[data.length - 1] & 0x80) {
763
- this._pendingBuffer = data;
758
+ // Decode bytes, keeping an incomplete trailing character for the next chunk
759
+ if (typeof data !== 'string') {
760
+ decoder = decoder || new TextDecoder('utf-8', { ignoreBOM: true });
761
+ if (!(data = decoder.decode(data, { stream: true })))
762
+ return;
764
763
  }
765
- // Otherwise, tokenize as far as possible
766
- else {
767
- // Only read a BOM at the start
768
- if (typeof this._input === 'undefined')
769
- this._input = this._readStartingBom(typeof data === 'string' ? data : data.toString());
770
- else
771
- this._input += data;
764
+ // Only read a BOM at the start
765
+ if (typeof this._input === 'undefined')
766
+ this._input = this._readStartingBom(data);
767
+ else
768
+ this._input += data;
769
+ // Tokenize as far as possible. When a previous attempt left a long unfinished token,
770
+ // wait until the buffered input has doubled, so the token is not rescanned for every chunk.
771
+ if (this._input.length >= retryLength) {
772
772
  this._tokenizeToEnd(callback, false);
773
+ retryLength = this._input !== null && this._input.length > MIN_RESCAN_LENGTH ?
774
+ 2 * this._input.length : 0;
773
775
  }
774
776
  }
775
777
  });
776
778
  // Parses until the end
777
779
  input.on('end', () => {
778
- if (this._tokenization === tokenization && typeof this._input === 'string')
779
- this._tokenizeToEnd(callback, true);
780
+ if (this._tokenization === tokenization && this._input !== null) {
781
+ // Decode any incomplete character left at the end
782
+ const rest = decoder ? decoder.decode() : '';
783
+ if (rest)
784
+ this._input = typeof this._input === 'string' ? this._input + rest : rest;
785
+ if (typeof this._input === 'string')
786
+ this._tokenizeToEnd(callback, true);
787
+ }
780
788
  });
781
789
  input.on('error', error => {
782
790
  if (this._tokenization === tokenization)