yencode 1.0.8 → 1.1.2

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.
Files changed (49) hide show
  1. package/README.md +339 -231
  2. package/binding.gyp +292 -39
  3. package/crcutil-1.0/code/multiword_64_64_gcc_amd64_asm.cc +7 -7
  4. package/crcutil-1.0/code/multiword_64_64_gcc_i386_mmx.cc +14 -14
  5. package/crcutil-1.0/code/multiword_64_64_intrinsic_i386_mmx.cc +1 -1
  6. package/crcutil-1.0/code/uint128_sse2.h +2 -0
  7. package/index.js +329 -22
  8. package/package.json +2 -2
  9. package/src/common.h +299 -0
  10. package/src/crc.cc +95 -0
  11. package/src/crc.h +23 -0
  12. package/src/crc_arm.cc +175 -0
  13. package/src/crc_common.h +4 -0
  14. package/{crc_folding.c → src/crc_folding.cc} +175 -185
  15. package/src/decoder.cc +61 -0
  16. package/src/decoder.h +53 -0
  17. package/src/decoder_avx.cc +18 -0
  18. package/src/decoder_avx2.cc +18 -0
  19. package/src/decoder_avx2_base.h +615 -0
  20. package/src/decoder_common.h +512 -0
  21. package/src/decoder_neon.cc +474 -0
  22. package/src/decoder_neon64.cc +451 -0
  23. package/src/decoder_sse2.cc +16 -0
  24. package/src/decoder_sse_base.h +711 -0
  25. package/src/decoder_ssse3.cc +18 -0
  26. package/src/encoder.cc +170 -0
  27. package/src/encoder.h +21 -0
  28. package/src/encoder_avx.cc +16 -0
  29. package/src/encoder_avx2.cc +16 -0
  30. package/src/encoder_avx_base.h +564 -0
  31. package/src/encoder_common.h +109 -0
  32. package/src/encoder_neon.cc +547 -0
  33. package/src/encoder_sse2.cc +13 -0
  34. package/src/encoder_sse_base.h +724 -0
  35. package/src/encoder_ssse3.cc +18 -0
  36. package/src/hedley.h +1899 -0
  37. package/src/platform.cc +147 -0
  38. package/src/yencode.cc +449 -0
  39. package/test/_maxsize.js +9 -0
  40. package/test/_speedbase.js +147 -0
  41. package/test/speedcrc.js +20 -0
  42. package/test/speeddec.js +92 -0
  43. package/test/speedenc.js +44 -0
  44. package/{testcrc.js → test/testcrc.js} +53 -39
  45. package/test/testdec.js +183 -0
  46. package/test/testenc.js +163 -0
  47. package/test/testpostdec.js +126 -0
  48. package/test.js +0 -91
  49. package/yencode.cc +0 -1622
@@ -0,0 +1,147 @@
1
+ // test config (defaults)
2
+ var sz = 768000;
3
+ var rounds = 80;
4
+ var trials = 8;
5
+ var asyncWait = 1000;
6
+
7
+ var maxSize = require('./_maxsize');
8
+ var decimal = (''+1.1).substr(1, 1);
9
+ var fmtSpeed = function(size, time) {
10
+ var rate = ('' + (Math.round(100*(size/1048576)/time)/100)).split(decimal);
11
+
12
+ return (' ' + rate[0]).substr(-8) + decimal + ((rate[1]|0) + '00').substr(0, 2) + ' MiB/s';
13
+ };
14
+ var initBuffers = function() {
15
+ module.exports.bufWorst = new Buffer(sz);
16
+ module.exports.bufBest = new Buffer(sz);
17
+ module.exports.bufAvg = [];
18
+ module.exports.bufAvg2x = [];
19
+ module.exports.bufTarget = new Buffer(maxSize(sz));
20
+
21
+ module.exports.bufWorst.fill(224);
22
+ module.exports.bufBest.fill(0);
23
+
24
+ // use cipher as a fast, consistent RNG
25
+ var cipher = require('crypto').createCipher;
26
+ [['aes-128-cbc', 'my_incredibly_strong_password'],
27
+ ['rc4', 'my_incredibly_strong_password'],
28
+ ['aes-128-cbc', '9h8a08b08qpklnac']
29
+ ].forEach(function(cargs) {
30
+ var rand = cipher.apply(null, cargs);
31
+ var data = Buffer.concat([rand.update(module.exports.bufBest), rand.final()]).slice(0, sz);
32
+ module.exports.bufAvg.push(new Buffer(data));
33
+
34
+ // all yEnc special characters exist in range 0-61 (post shift) or 214-19 (pre-shift)
35
+ // to generate biased data, we'll pack the range down (64-191 will get packed to 192-63)
36
+ for(var i=0; i<data.length; i++) {
37
+ if(data[i] >= 64 && data[i] < 192)
38
+ data[i] = (data[i] + 128) & 0xff;
39
+ }
40
+ module.exports.bufAvg2x.push(data);
41
+ });
42
+ };
43
+ module.exports = {
44
+ size: sz,
45
+ rounds: rounds,
46
+ sleep: 0,
47
+ avgOnly: false,
48
+ decMethods: {'clean':true, 'raw':true, 'incr':true, 'rawincr':true},
49
+
50
+ bufWorst: null,
51
+ bufBest: null,
52
+ bufAvg: null,
53
+ bufAvg2x: null,
54
+ bufTarget: null,
55
+
56
+ bench: function(fn) {
57
+ var times = Array(trials);
58
+ for(var trial=0; trial<trials; trial++) {
59
+ var p=process.hrtime();
60
+ for(var i=0;i<rounds;i++) fn();
61
+ var t=process.hrtime(p);
62
+
63
+ times[trial] = t[0] + t[1]/1000000000;
64
+ }
65
+ // pick fastest time to try to avoid issues with clockspeed throttling
66
+ return Math.min.apply(null, times);
67
+ },
68
+ _benchAsync: function(fn, cb, trials, results) {
69
+ var p=process.hrtime();
70
+ for(var i=0;i<rounds;i++) fn();
71
+ results.push(process.hrtime(p));
72
+
73
+ if(--trials)
74
+ setTimeout(module.exports._benchAsync.bind(null, fn, cb, trials, results), asyncWait);
75
+ else
76
+ cb(Math.min.apply(null, results));
77
+ },
78
+ benchAsync: function(fn, cb) {
79
+ setTimeout(function() {
80
+ module.exports._benchAsync(fn, cb, trials, []);
81
+ }, asyncWait);
82
+ },
83
+ run: function(name, fn, sz2) {
84
+ var time = module.exports.bench(fn);
85
+ console.log(
86
+ (name+' ').substr(0, 25) + ':'
87
+ + fmtSpeed(sz*rounds, time)
88
+ + (sz2 ? (' ' + fmtSpeed(sz2*rounds, time)) : '')
89
+ );
90
+ },
91
+
92
+ parseArgs: function(helpText) {
93
+ process.argv.forEach(function(arg) {
94
+ arg = arg.toLowerCase();
95
+ if(arg == '-h' || arg == '--help' || arg == '-?') {
96
+ console.log(helpText + ' [{-z|--size}=bytes('+sz+')] [{-r|--rounds}=num('+rounds+')] [{-t|--trials}=num('+trials+')]');
97
+ process.exit(0);
98
+ }
99
+ if(arg == '-a' || arg == '--average-only') {
100
+ module.exports.avgOnly = true;
101
+ }
102
+
103
+ var m = arg.match(/^(-s=?|--sleep=)(\d+)$/);
104
+ if(m)
105
+ module.exports.sleep = m[2] |0;
106
+
107
+ m = arg.match(/^(-m=?|--methods=)([a-z,]+)$/);
108
+ if(m) {
109
+ var methods = module.exports.decMethods;
110
+ for(var k in methods)
111
+ methods[k] = false;
112
+ var setAMethod = false;
113
+ m[2].split(',').forEach(function(meth) {
114
+ if(meth in methods) {
115
+ setAMethod = true;
116
+ methods[meth] = true;
117
+ }
118
+ });
119
+ if(!setAMethod) {
120
+ console.log('No valid method specified');
121
+ process.exit(1);
122
+ }
123
+ }
124
+
125
+ m = arg.match(/^(-t=?|--trials=)(\d+)$/);
126
+ if(m)
127
+ trials = m[2] |0;
128
+
129
+ m = arg.match(/^(-r=?|--rounds=)(\d+)$/);
130
+ if(m) {
131
+ rounds = m[2] |0;
132
+ module.exports.rounds = rounds;
133
+ }
134
+
135
+ m = arg.match(/^(-z=?|--size=)(\d+)$/);
136
+ if(m) {
137
+ sz = m[2] |0;
138
+ module.exports.size = sz;
139
+ initBuffers();
140
+ }
141
+
142
+ });
143
+ }
144
+
145
+ };
146
+
147
+ initBuffers();
@@ -0,0 +1,20 @@
1
+ var y = require('../build/Release/yencode');
2
+ var _ = require('./_speedbase');
3
+
4
+
5
+ _.parseArgs('Syntax: node test/speedcrc [{-s|--sleep}=msecs(0)]');
6
+
7
+ // warmup
8
+ if(!_.sleep) {
9
+ _.bufAvg.forEach(function(buf, i) {
10
+ var p=process.hrtime();
11
+ for(var i=0;i<_.rounds;i+=2) y.crc32(buf);
12
+ var t=process.hrtime(p);
13
+ });
14
+ }
15
+
16
+ setTimeout(function() {
17
+ _.bufAvg.forEach(function(buf, i) {
18
+ _.run('Random ('+i+')', y.crc32.bind(null, buf));
19
+ });
20
+ }, _.sleep);
@@ -0,0 +1,92 @@
1
+ var y = require('../build/Release/yencode');
2
+ var _ = require('./_speedbase');
3
+
4
+ var bufSize = _.bufTarget.length;
5
+
6
+ var mWorst = new Buffer(bufSize);
7
+ var mAvg = _.bufAvg.map(function() {
8
+ return new Buffer(bufSize);
9
+ });
10
+ var mAvg2x = _.bufAvg2x.map(function() {
11
+ return new Buffer(bufSize);
12
+ });
13
+ var mBest = new Buffer(bufSize);
14
+ var mBest2 = new Buffer(_.size);
15
+ mBest2.fill(32);
16
+
17
+ var lenWorst = y.encodeTo(_.bufWorst, mWorst);
18
+ var lenBest = y.encodeTo(_.bufBest, mBest);
19
+ var lenAvg = Array(_.bufAvg.length);
20
+ var lenAvg2x = Array(_.bufAvg2x.length);
21
+ _.bufAvg.forEach(function(buf, i) {
22
+ lenAvg[i] = y.encodeTo(buf, mAvg[i]);
23
+ });
24
+ _.bufAvg2x.forEach(function(buf, i) {
25
+ lenAvg2x[i] = y.encodeTo(buf, mAvg2x[i]);
26
+ });
27
+
28
+
29
+
30
+ _.parseArgs('Syntax: node test/speeddec [-a|--average-only] [{-s|--sleep}=msecs(0)] [{-m|--methods}=clean,raw,rawincr]');
31
+
32
+ console.log(' Test Output rate Read rate ');
33
+
34
+ // warmup
35
+ if(!_.sleep) {
36
+ mAvg.forEach(function(buf) {
37
+ var p=process.hrtime();
38
+ for(var j=0;j<_.rounds;j+=2) y.decodeTo(buf, _.bufTarget);
39
+ for(var j=0;j<_.rounds;j+=2) y.decodeTo(buf, _.bufTarget, true);
40
+ for(var j=0;j<_.rounds;j+=2) y.decodeIncr(buf, 0, _.bufTarget);
41
+ var t=process.hrtime(p);
42
+ });
43
+ }
44
+
45
+ setTimeout(function() {
46
+ if(!_.avgOnly) {
47
+ if(_.decMethods.clean) {
48
+ _.run('Clean worst (all escaping)', y.decodeTo.bind(null, mWorst, _.bufTarget), lenWorst);
49
+ _.run('Clean best (min escaping)', y.decodeTo.bind(null, mBest, _.bufTarget), lenBest);
50
+ _.run('Clean pass (no escaping)', y.decodeTo.bind(null, mBest2, _.bufTarget));
51
+ }
52
+ if(_.decMethods.raw) {
53
+ _.run('Raw worst', y.decodeTo.bind(null, mWorst, _.bufTarget, true), lenWorst);
54
+ _.run('Raw best', y.decodeTo.bind(null, mBest, _.bufTarget, true), lenBest);
55
+ _.run('Raw pass', y.decodeTo.bind(null, mBest2, _.bufTarget, true));
56
+ }
57
+ if(_.decMethods.rawincr) {
58
+ _.run('Raw-incr worst', y.decodeIncr.bind(null, mWorst, 0, _.bufTarget), lenWorst);
59
+ _.run('Raw-incr best', y.decodeIncr.bind(null, mBest, 0, _.bufTarget), lenBest);
60
+ _.run('Raw-incr pass', y.decodeIncr.bind(null, mBest2, 0, _.bufTarget));
61
+ }
62
+ }
63
+
64
+ if(_.decMethods.clean)
65
+ mAvg.forEach(function(buf, i) {
66
+ _.run('Clean random ('+i+')', y.decodeTo.bind(null, buf, _.bufTarget), lenAvg[i]);
67
+ });
68
+ if(_.decMethods.raw)
69
+ mAvg.forEach(function(buf, i) {
70
+ _.run('Raw random ('+i+')', y.decodeTo.bind(null, buf, _.bufTarget, true), lenAvg[i]);
71
+ });
72
+ if(_.decMethods.rawincr) {
73
+ mAvg.forEach(function(buf, i) {
74
+ _.run('Raw-incr random ('+i+')', y.decodeIncr.bind(null, buf, 0, _.bufTarget), lenAvg[i]);
75
+ });
76
+ }
77
+
78
+ if(!_.avgOnly) {
79
+ if(_.decMethods.clean)
80
+ mAvg2x.forEach(function(buf, i) {
81
+ _.run('Clean random 2xEsc ('+i+')', y.decodeTo.bind(null, buf, _.bufTarget), lenAvg2x[i]);
82
+ });
83
+ if(_.decMethods.raw)
84
+ mAvg2x.forEach(function(buf, i) {
85
+ _.run('Raw random 2xEsc ('+i+')', y.decodeTo.bind(null, buf, _.bufTarget, true), lenAvg2x[i]);
86
+ });
87
+ if(_.decMethods.rawincr)
88
+ mAvg2x.forEach(function(buf, i) {
89
+ _.run('Raw-incr random 2xEsc ('+i+')', y.decodeIncr.bind(null, buf, 0, _.bufTarget), lenAvg2x[i]);
90
+ });
91
+ }
92
+ }, _.sleep);
@@ -0,0 +1,44 @@
1
+ var y = require('../build/Release/yencode');
2
+ var _ = require('./_speedbase');
3
+
4
+
5
+ _.parseArgs('Syntax: node test/speedenc [-a|--average-only] [{-s|--sleep}=msecs(0)]');
6
+
7
+
8
+ console.log(' Test Read rate Output rate ');
9
+
10
+ var lenWorst = y.encodeTo(_.bufWorst, _.bufTarget);
11
+ var lenBest = y.encodeTo(_.bufBest, _.bufTarget);
12
+ var lenAvg = Array(_.bufAvg.length);
13
+ var lenAvg2x = Array(_.bufAvg2x.length);
14
+
15
+ // warmup
16
+ if(!_.sleep) {
17
+ _.bufAvg.forEach(function(buf, i) {
18
+ var p=process.hrtime();
19
+ for(var j=0;j<_.rounds;j+=1) lenAvg[i] = y.encodeTo(buf, _.bufTarget);
20
+ var t=process.hrtime(p);
21
+ });
22
+ _.bufAvg2x.forEach(function(buf, i) {
23
+ var p=process.hrtime();
24
+ for(var j=0;j<_.rounds;j+=1) lenAvg2x[i] = y.encodeTo(buf, _.bufTarget);
25
+ var t=process.hrtime(p);
26
+ });
27
+ }
28
+
29
+ setTimeout(function() {
30
+ if(!_.avgOnly) {
31
+ _.run('Worst (all escaping)', y.encodeTo.bind(null, _.bufWorst, _.bufTarget), lenWorst);
32
+ _.run('Best (no escaping)', y.encodeTo.bind(null, _.bufBest, _.bufTarget), lenBest);
33
+ }
34
+
35
+ _.bufAvg.forEach(function(buf, i) {
36
+ _.run('Random ('+i+')', y.encodeTo.bind(null, buf, _.bufTarget), lenAvg[i]);
37
+ });
38
+ if(!_.avgOnly) {
39
+ _.bufAvg2x.forEach(function(buf, i) {
40
+ _.run('Random 2xEsc ('+i+')', y.encodeTo.bind(null, buf, _.bufTarget), lenAvg2x[i]);
41
+ });
42
+ }
43
+
44
+ }, _.sleep);
@@ -1,39 +1,53 @@
1
- // a basic script to test that raw yEnc works as expected
2
-
3
- var assert = require('assert');
4
- var y = require('./build/Release/yencode.node');
5
- var crc32 = require('buffer-crc32'); // reference implementation
6
-
7
- var ycrc32 = function(s) {
8
- return y.crc32(Buffer(s));
9
- };
10
- var doTest = function(msg, f, test, expected) {
11
- if(!Array.isArray(test)) test = [test];
12
- test[0] = Buffer(test[0]);
13
- if(!expected && test.length == 1 && f == 'crc32') expected = crc32(test[0]).toString('hex');
14
- else if(Buffer.isBuffer(expected)) expected = expected.toString('hex');
15
- assert.equal(y[f].apply(null, test).toString('hex'), expected, msg);
16
- };
17
-
18
-
19
- doTest('Empty test', 'crc32', '');
20
- doTest('Single char', 'crc32', 'z');
21
- doTest('Simple string', 'crc32', 'aabbcc');
22
- doTest('Join', 'crc32', ['cc', ycrc32('aabb')], crc32('aabbcc'));
23
- doTest('Combine', 'crc32_combine', [ycrc32('aabb'), crc32('cc'), 2], crc32('aabbcc'));
24
- doTest('Join 2', 'crc32', ['789012', ycrc32('123456')], crc32('123456789012'));
25
- doTest('Combine 2', 'crc32_combine', [ycrc32('123456'), crc32('789012'), 6], crc32('123456789012'));
26
-
27
- doTest('Join Empty', 'crc32', ['', ycrc32('z')], crc32('z'));
28
- doTest('Join Empty 2', 'crc32', ['z', ycrc32('')], crc32('z'));
29
- doTest('Join Empty 3', 'crc32', ['', ycrc32('')], crc32(''));
30
- doTest('Combine Empty', 'crc32_combine', [ycrc32(''), ycrc32('z'), 1], crc32('z'));
31
- doTest('Combine Empty 2', 'crc32_combine', [ycrc32('z'), ycrc32(''), 0], crc32('z'));
32
- doTest('Combine Empty 3', 'crc32_combine', [ycrc32(''), ycrc32(''), 0], crc32(''));
33
-
34
- assert.equal(y.crc32_zeroes(0).toString('hex'), '00000000', 'Zeroes (0)');
35
- assert.equal(y.crc32_zeroes(1).toString('hex'), 'd202ef8d', 'Zeroes (1)');
36
- assert.equal(y.crc32_zeroes(4).toString('hex'), '2144df1c', 'Zeroes (4)');
37
-
38
-
39
- console.log('All tests passed');
1
+ // a basic script to test that raw yEnc works as expected
2
+
3
+ var assert = require('assert');
4
+ var y = (function() {
5
+ try {
6
+ return require('../build/Debug/yencode.node');
7
+ } catch(x) {}
8
+ return require('../build/Release/yencode.node');
9
+ })();
10
+ var crc32 = require('buffer-crc32'); // reference implementation
11
+
12
+ var ycrc32 = function(s) {
13
+ return y.crc32(Buffer(s));
14
+ };
15
+ var doTest = function(msg, f, test, expected) {
16
+ if(!Array.isArray(test)) test = [test];
17
+ test[0] = Buffer(test[0]);
18
+ if(!expected && test.length == 1 && f == 'crc32') expected = crc32(test[0]).toString('hex');
19
+ else if(Buffer.isBuffer(expected)) expected = expected.toString('hex');
20
+ assert.equal(y[f].apply(null, test).toString('hex'), expected, msg);
21
+ };
22
+
23
+
24
+ doTest('Empty test', 'crc32', '');
25
+ doTest('Single char', 'crc32', 'z');
26
+ doTest('Simple string', 'crc32', 'aabbcc');
27
+ doTest('Join', 'crc32', ['cc', ycrc32('aabb')], crc32('aabbcc'));
28
+ doTest('Combine', 'crc32_combine', [ycrc32('aabb'), crc32('cc'), 2], crc32('aabbcc'));
29
+ doTest('Join 2', 'crc32', ['789012', ycrc32('123456')], crc32('123456789012'));
30
+ doTest('Combine 2', 'crc32_combine', [ycrc32('123456'), crc32('789012'), 6], crc32('123456789012'));
31
+
32
+ doTest('Join Empty', 'crc32', ['', ycrc32('z')], crc32('z'));
33
+ doTest('Join Empty 2', 'crc32', ['z', ycrc32('')], crc32('z'));
34
+ doTest('Join Empty 3', 'crc32', ['', ycrc32('')], crc32(''));
35
+ doTest('Combine Empty', 'crc32_combine', [ycrc32(''), ycrc32('z'), 1], crc32('z'));
36
+ doTest('Combine Empty 2', 'crc32_combine', [ycrc32('z'), ycrc32(''), 0], crc32('z'));
37
+ doTest('Combine Empty 3', 'crc32_combine', [ycrc32(''), ycrc32(''), 0], crc32(''));
38
+
39
+ assert.equal(y.crc32_zeroes(0).toString('hex'), '00000000', 'Zeroes (0)');
40
+ assert.equal(y.crc32_zeroes(1).toString('hex'), 'd202ef8d', 'Zeroes (1)');
41
+ assert.equal(y.crc32_zeroes(4).toString('hex'), '2144df1c', 'Zeroes (4)');
42
+
43
+ assert.equal(y.crc32_zeroes(0, ycrc32('')).toString('hex'), '00000000', 'Zeroes-Join (0)');
44
+ assert.equal(y.crc32_zeroes(1, ycrc32('')).toString('hex'), 'd202ef8d', 'Zeroes-Join (1)');
45
+ assert.equal(y.crc32_zeroes(0, ycrc32('z')).toString('hex'), crc32('z').toString('hex'), 'Zeroes Empty Join');
46
+ assert.equal(y.crc32_zeroes(4, ycrc32('z')).toString('hex'), crc32('z\u0000\u0000\u0000\u0000').toString('hex'), 'Zeroes (4) Join');
47
+
48
+
49
+ doTest('Random', 'crc32', 'fj[-oqijnw34-59n26 4345j8yn89032q78t9ab9gabh023quhoiBO Z GEB780a sdasdq2345673-98hq2-9348h-na9we8zdfgh-n9 8qwhn-098');
50
+ doTest('Random Continue', 'crc32', ['KZSHZ5EDOVAmDdakZZOrGSUGGKSpCJoWH7M0MHy6ohnSzvHY4DjpxXmyfWYJQoJ7tKdNhGcuRVUzrgXM', ycrc32('BdenbmoBgiB10ZkeUBjrsZV3dg2Da2fhHqU9TMdi69AHhLRck3Nk60YuFBXh6lvtefBpjdTxbeEmsaEm')], crc32('BdenbmoBgiB10ZkeUBjrsZV3dg2Da2fhHqU9TMdi69AHhLRck3Nk60YuFBXh6lvtefBpjdTxbeEmsaEmKZSHZ5EDOVAmDdakZZOrGSUGGKSpCJoWH7M0MHy6ohnSzvHY4DjpxXmyfWYJQoJ7tKdNhGcuRVUzrgXM'));
51
+
52
+
53
+ console.log('All tests passed');
@@ -0,0 +1,183 @@
1
+ // a basic script to test that raw yEnc works as expected
2
+
3
+ var assert = require('assert');
4
+ var y = (function() {
5
+ try {
6
+ return require('../build/Debug/yencode.node');
7
+ } catch(x) {}
8
+ return require('../build/Release/yencode.node');
9
+ })();
10
+
11
+ var ord = function(c) {
12
+ return c.charCodeAt(0);
13
+ }, chr = String.fromCharCode;
14
+ // slow reference yEnc implementation
15
+ var refYDec = function(src, findEnd) {
16
+ var ret = [];
17
+ if(findEnd && chr(src[0]) == '=' && chr(src[1]) == 'y') return new Buffer(0);
18
+ for (var i = 0; i < src.length; i++) {
19
+ switch(chr(src[i])) {
20
+ case '\r':
21
+ if(findEnd && chr(src[i+1]) == '\n' && chr(src[i+2]) == '=' && chr(src[i+3]) == 'y')
22
+ return new Buffer(ret);
23
+ case '\n': continue;
24
+ case '=':
25
+ i++;
26
+ if(i < src.length)
27
+ ret.push((src[i] - 42 - 64) & 0xFF);
28
+ if(chr(src[i]) == '\r') i--;
29
+ continue;
30
+ }
31
+ ret.push((src[i] - 42) & 0xFF);
32
+ }
33
+ return new Buffer(ret);
34
+ };
35
+ var refYDecRaw = function(src, findEnd) {
36
+ // undo NNTP layer
37
+ var data = [];
38
+ var i = 0;
39
+ if(src[0] == ord('.')) {
40
+ i++;
41
+ if(findEnd && src[1] == ord('\r') && src[2] == ord('\n'))
42
+ return new Buffer(0);
43
+ }
44
+ // TODO: do leading/trailing spaces/tabs need to be trimmed?
45
+ for(; i<src.length; i++) {
46
+ if(src[i] == ord('\r') && src[i+1] == ord('\n') && src[i+2] == ord('.')) {
47
+ data.push(src[i], src[i+1]);
48
+ if(findEnd && src[i+3] == ord('\r') && src[i+4] == ord('\n')) {
49
+ // it's a little vague how this is exactly handled, but we'll push a \r\n to the yenc decoder (above line)
50
+ // doing so means that =\r\n.\r\n will generate a single escaped character
51
+ break;
52
+ }
53
+ i += 2;
54
+ continue;
55
+ }
56
+ data.push(src[i]);
57
+ }
58
+
59
+ return refYDec(data, findEnd);
60
+ };
61
+ var testFuncs = [
62
+ {l: 'nntp', r: refYDecRaw, a: function(s) {
63
+ return y.decode(s, true);
64
+ }},
65
+ {l: 'plain', r: refYDec, a: y.decode},
66
+ {l: 'nntp-end', r: function(s) {
67
+ return refYDecRaw(s, true);
68
+ }, a: function(s) {
69
+ if(!s.length) return Buffer(0);
70
+ return y.decodeIncr(s).output;
71
+ }}
72
+ ];
73
+ var doTest = function(msg, data, expected) {
74
+ data = new Buffer(data);
75
+
76
+ var prepad = 48, postpad = 48;
77
+ if(data.length > 1024) {
78
+ prepad = 1;
79
+ postpad = 1;
80
+ }
81
+
82
+ for(var i=0; i<prepad; i++) {
83
+ var pre = new Buffer(i);
84
+ pre.fill(1);
85
+ for(var j=0; j<postpad; j++) {
86
+ testFuncs.forEach(function(f) {
87
+ var post = new Buffer(j);
88
+ post.fill(1);
89
+
90
+ var testData = Buffer.concat([pre, data, post]);
91
+ var x;
92
+ if(expected === undefined) x = f.r(testData).toString('hex');
93
+ else x = new Buffer(expected).toString('hex').replace(/ /g, '');
94
+ var actual = f.a(testData).toString('hex');
95
+ if(actual != x) {
96
+ console.log('Actual:', actual);
97
+ console.log('Expect:', x);
98
+ console.log('Source:', data.toString('hex'));
99
+ assert.equal(actual, x, msg + ' [' + i + '/' + j + ' ' + f.l + ']');
100
+ }
101
+ });
102
+ if(expected !== undefined) return; // if given expected string, only do one test
103
+
104
+ // TODO: test various states
105
+ }
106
+ }
107
+ };
108
+
109
+
110
+ doTest('Empty test', [], '');
111
+ doTest('Simple test', [0,1,2,10,3,61,64]);
112
+ doTest('Just newline', [10], '');
113
+ doTest('Just equals', [61], '');
114
+ doTest('Equal+newline', [61, 13], [163]);
115
+ doTest('Equal+equal', [61, 61], [211]);
116
+ doTest('Equal+equal+newline', [61, 61, 13], [211]);
117
+ doTest('Newline, equal', [10, 61], '');
118
+ doTest('Stripped dot', [13, 10, 46]);
119
+ doTest('Just dot', [46]);
120
+ doTest('Consecutive stripped dot', [13, 10, 46, 13, 10, 46]);
121
+ doTest('Bad escape stripped dot', [61, 13, 10, 46]);
122
+
123
+ doTest('NNTP end sequence', '\r\n.\r\n');
124
+ doTest('NNTP end sequence (2)', '.\r\n');
125
+ doTest('Yenc end sequence', '\r\n=y');
126
+ doTest('Yenc end sequence (2)', '=y');
127
+ doTest('Mixed end sequence', '\r\n=y\r\n.\r\n');
128
+ doTest('Mixed end sequence (2)', '\r\n.\r\n=y');
129
+ doTest('Not end sequence', '\r\n=abc');
130
+ doTest('Dot stuffed end sequence', '\r\n.=y');
131
+ doTest('Dot stuffed bad escape sequence', '\r\n.=.');
132
+ doTest('Broken end sequence', '\r\n.\ra\n');
133
+ doTest('NNTP end sequence, badly dot stuffed', '\r\n..\r\n');
134
+ doTest('NNTP end sequence, badly dot stuffed (2)', '\r\n.a=y');
135
+ doTest('Bad escape, NNTP end sequence', '=\r\n.\r\n');
136
+ doTest('Bad escape, Yenc end sequence', '=\r\n=y');
137
+
138
+
139
+ // longer tests
140
+ var b = new Buffer(256);
141
+ b.fill(0);
142
+ doTest('Long no escaping', b);
143
+ b.fill('='.charCodeAt(0));
144
+ doTest('Long all equals', b);
145
+ for(var i=1; i<b.length; i+=2)
146
+ b[i] = 64;
147
+ doTest('Long all escaped nulls', b);
148
+ b.fill('='.charCodeAt(0));
149
+ for(var i=0; i<b.length; i+=2)
150
+ b[i] = 64;
151
+ doTest('Long all invalid escaped nulls', b);
152
+ b.fill(10);
153
+ doTest('Long all newlines', b);
154
+ b.fill(223);
155
+ doTest('Long all tabs', b);
156
+
157
+ // test for past bug in ARMv8 NEON decoder where nextMask wasn't properly compensated for
158
+ doTest('Extra null issue', new Buffer('2e900a4fb6054c9126171cdc196dc41237bb1b76da9191aa5e85c1d2a2a5c638fe39054a210e8c799473cd510541fd118f3904b242a9938558c879238aae1d3bdab32e287cedb820b494f54ffae6dd0b13f73a4a9499df486a7845c612182bcef72a6e50a8e98351c35765d26c605115dc8c5c56a5e3f20ae6da8dcd78536e6d1601eb1fc3ddc774', 'hex'));
159
+ // end detection bug
160
+ doTest('End detect', new Buffer('612e6161610d610d612e793d3d0d0d2e612e2e0a0d0d61792e3d3d61612e0d0a2e0d2e0a0d79612e0a3d2e2e793d2e610a0d0a0a2e793d790d612e61612e0a3d792e2e3d2e7961793d792e0a61790a0d0a2e0d0a3d0a0d0d0d0a610a0a6161792e3d2e0a2e0d0d0d613d610a0a0a793d613d3d0a3d790d3d0a0a2e2e7979796179613d0d2e792e793d3d61792e612e2e2e793d616161790d0d2e0d0d793d0d790a0a3d0d617979790d2e0d792e612e610a0a0a0a0a79790d0a610d612e0d0a0d3d0a61792e2e0a790d0d792e790d0a2e79612e3d0a79790a0d0d3d0a0a0d3d0a7961610a2e613d792e0a612e613d610a2e0a0a79613d2e2e0d3d3d2e793d792e792e0d0d610d2e2e0d2e79610d2e790d790d3d2e3d790a0a0d0a0a0a612e2e79612e0d2e3d793d2e0a2e3d790a2e3d792e2e610d3d2e0d3d3d0a3d2e0d613d2e0d61610a3d0a2e0a0a3d3d612e3d790d6161613d3d612e3d0d0a2e0d0d0d616179790a2e3d610d612e0d2e3d0d0a3d610d0d61610a7961613d2e790d613d610a3d612e0a2e0d79790d0a610a2e2e0a2e612e2e0d792e61610a2e0d610d3d0a793d613d0d3d0a3d0d0a613d2e0a3d610a3d0d793d0d7979792e3d613d0a2e61610d793d2e0a0a2e612e0d2e2e792e0d2e613d0d790a0d2e610d2e0a2e61793d0d0d0a0a0d0d2e2e0d2e793d3d79612e0a610a610a0d610d0d2e2e790', 'hex'));
161
+
162
+ // random tests
163
+ for(var i=0; i<32; i++) {
164
+ var rand = require('crypto').pseudoRandomBytes(128*1024);
165
+ doTest('Random', rand);
166
+ }
167
+
168
+ // targeted random tests
169
+ var charset = '=\r\n.ay';
170
+ var randStr = function(n) {
171
+ var ret = new Buffer(n);
172
+ for(var i=0; i<n; i++)
173
+ ret[i] = ord(charset[(Math.random() * charset.length) | 0]);
174
+ return ret;
175
+ };
176
+ for(var i=0; i<128; i++) {
177
+ var rand = randStr(2048);
178
+ doTest('Random2', rand);
179
+ }
180
+
181
+
182
+
183
+ console.log('All tests passed');