From 9af1e4b28677022be8adbcdd6440def719ad0037 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 1 Feb 2018 01:12:05 -0500 Subject: [PATCH] string_decoder: reset decoder on end This resets the StringDecoder's state after calling `#end`. Further writes to the decoder will act as if it were a brand new instance, allowing simple reuse. PR-URL: https://github.com/nodejs/node/pull/18494 Fixes: https://github.com/nodejs/node/issues/16564 Refs: https://github.com/nodejs/node/pull/16594 Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: Sakthipriyan Vairamani Reviewed-By: Ruben Bridgewater Reviewed-By: Anna Henningsen --- lib/string_decoder.js | 15 +++++-- test/parallel/test-string-decoder-end.js | 51 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/lib/string_decoder.js b/lib/string_decoder.js index 891ab5bdba65f3..1e569ba6b26a4c 100644 --- a/lib/string_decoder.js +++ b/lib/string_decoder.js @@ -210,8 +210,11 @@ function utf8Text(buf, i) { // character. function utf8End(buf) { const r = (buf && buf.length ? this.write(buf) : ''); - if (this.lastNeed) + if (this.lastNeed) { + this.lastNeed = 0; + this.lastTotal = 0; return r + '\ufffd'; + } return r; } @@ -246,6 +249,8 @@ function utf16End(buf) { const r = (buf && buf.length ? this.write(buf) : ''); if (this.lastNeed) { const end = this.lastTotal - this.lastNeed; + this.lastNeed = 0; + this.lastTotal = 0; return r + this.lastChar.toString('utf16le', 0, end); } return r; @@ -269,8 +274,12 @@ function base64Text(buf, i) { function base64End(buf) { const r = (buf && buf.length ? this.write(buf) : ''); - if (this.lastNeed) - return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); + if (this.lastNeed) { + const end = 3 - this.lastNeed; + this.lastNeed = 0; + this.lastTotal = 0; + return r + this.lastChar.toString('base64', 0, end); + } return r; } diff --git a/test/parallel/test-string-decoder-end.js b/test/parallel/test-string-decoder-end.js index 0284ee9f6c48c7..2762ef096289db 100644 --- a/test/parallel/test-string-decoder-end.js +++ b/test/parallel/test-string-decoder-end.js @@ -39,6 +39,46 @@ for (let i = 1; i <= 16; i++) { encodings.forEach(testEncoding); +testEnd('utf8', Buffer.of(0xE2), Buffer.of(0x61), '\uFFFDa'); +testEnd('utf8', Buffer.of(0xE2), Buffer.of(0x82), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2), Buffer.of(0xE2), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2, 0x82), Buffer.of(0x61), '\uFFFDa'); +testEnd('utf8', Buffer.of(0xE2, 0x82), Buffer.of(0xAC), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2, 0x82), Buffer.of(0xE2), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2, 0x82, 0xAC), Buffer.of(0x61), '€a'); + +testEnd('utf16le', Buffer.of(0x3D), Buffer.of(0x61, 0x00), 'a'); +testEnd('utf16le', Buffer.of(0x3D), Buffer.of(0xD8, 0x4D, 0xDC), '\u4DD8'); +testEnd('utf16le', Buffer.of(0x3D, 0xD8), Buffer.of(), '\uD83D'); +testEnd('utf16le', Buffer.of(0x3D, 0xD8), Buffer.of(0x61, 0x00), '\uD83Da'); +testEnd( + 'utf16le', + Buffer.of(0x3D, 0xD8), + Buffer.of(0x4D, 0xDC), + '\uD83D\uDC4D' +); +testEnd('utf16le', Buffer.of(0x3D, 0xD8, 0x4D), Buffer.of(), '\uD83D'); +testEnd( + 'utf16le', + Buffer.of(0x3D, 0xD8, 0x4D), + Buffer.of(0x61, 0x00), + '\uD83Da' +); +testEnd('utf16le', Buffer.of(0x3D, 0xD8, 0x4D), Buffer.of(0xDC), '\uD83D'); +testEnd( + 'utf16le', + Buffer.of(0x3D, 0xD8, 0x4D, 0xDC), + Buffer.of(0x61, 0x00), + '👍a' +); + +testEnd('base64', Buffer.of(0x61), Buffer.of(), 'YQ=='); +testEnd('base64', Buffer.of(0x61), Buffer.of(0x61), 'YQ==YQ=='); +testEnd('base64', Buffer.of(0x61, 0x61), Buffer.of(), 'YWE='); +testEnd('base64', Buffer.of(0x61, 0x61), Buffer.of(0x61), 'YWE=YQ=='); +testEnd('base64', Buffer.of(0x61, 0x61, 0x61), Buffer.of(), 'YWFh'); +testEnd('base64', Buffer.of(0x61, 0x61, 0x61), Buffer.of(0x61), 'YWFhYQ=='); + function testEncoding(encoding) { bufs.forEach((buf) => { testBuf(encoding, buf); @@ -66,3 +106,14 @@ function testBuf(encoding, buf) { assert.strictEqual(res1, res3, 'one byte at a time should match toString'); assert.strictEqual(res2, res3, 'all bytes at once should match toString'); } + +function testEnd(encoding, incomplete, next, expected) { + let res = ''; + const s = new SD(encoding); + res += s.write(incomplete); + res += s.end(); + res += s.write(next); + res += s.end(); + + assert.strictEqual(res, expected); +}