mirror of
https://github.com/hanzoai/protobuf.js.git
synced 2026-09-24 17:02:08 +00:00
157 lines
7.0 KiB
JavaScript
157 lines
7.0 KiB
JavaScript
var tape = require("tape");
|
|
|
|
var protobuf = require("..");
|
|
|
|
var Writer = protobuf.Writer,
|
|
BufferWriter = protobuf.BufferWriter,
|
|
Reader = protobuf.Reader;
|
|
|
|
// Exercise both the browser (Uint8Array/Array) writer and the node BufferWriter.
|
|
var writers = [ [ "Writer", Writer ] ];
|
|
if (BufferWriter)
|
|
writers.push([ "BufferWriter", BufferWriter ]);
|
|
|
|
function toArray(buf) {
|
|
return Array.prototype.slice.call(buf);
|
|
}
|
|
|
|
// builds a length-delimited field 1 carrying `n` distinct raw bytes via fork/ldelim
|
|
function ldelimRaw(Ctor, n) {
|
|
var pattern = [];
|
|
for (var i = 0; i < n; ++i)
|
|
pattern.push(i & 255);
|
|
var buf = new Ctor().uint32(10).fork().raw(pattern).ldelim().finish();
|
|
return { buf: buf, pattern: pattern };
|
|
}
|
|
|
|
tape.test("writer initial buffer size", function(test) {
|
|
var initialBufferSize = Writer.initialBufferSize;
|
|
test.equal(Writer.initialBufferSize, 128, "defaults to 128 bytes");
|
|
|
|
Writer.initialBufferSize = 32;
|
|
writers.forEach(function(entry) {
|
|
test.equal(new entry[1]().buf.length, 32, entry[0] + " uses the configured size");
|
|
});
|
|
Writer.initialBufferSize = initialBufferSize;
|
|
|
|
test.end();
|
|
});
|
|
|
|
tape.test("writer resize / reserve-and-move", function(test) {
|
|
|
|
writers.forEach(function(entry) {
|
|
var name = entry[0], Ctor = entry[1];
|
|
|
|
test.test(name + " - length-delimited reserve vs move boundary", function(test) {
|
|
// 1-byte length (no move), 2-byte length (move by 1), 3-byte length (move by 2)
|
|
[ 0, 1, 127, 128, 200, 16383, 16384, 20000 ].forEach(function(n) {
|
|
var r = ldelimRaw(Ctor, n);
|
|
var reader = Reader.create(r.buf);
|
|
test.equal(reader.uint32(), 10, name + " tag intact for n=" + n);
|
|
var inner = reader.bytes();
|
|
test.equal(inner.length, n, name + " length prefix correct for n=" + n);
|
|
test.same(toArray(inner), r.pattern, name + " content intact after move for n=" + n);
|
|
test.equal(reader.pos, reader.len, name + " fully consumed for n=" + n);
|
|
});
|
|
test.end();
|
|
});
|
|
|
|
test.test(name + " - minimal length varint", function(test) {
|
|
// 128 must encode as [0x80, 0x01], never padded by the reservation
|
|
var buf = new Ctor().uint32(10).fork().raw(new Array(128).fill(0)).ldelim().finish();
|
|
test.same(toArray(buf).slice(0, 3), [ 10, 0x80, 0x01 ], name + " 128 -> minimal 2-byte varint");
|
|
test.equal(buf.length, 3 + 128, name + " total length");
|
|
test.end();
|
|
});
|
|
|
|
test.test(name + " - nested forks with cascading moves", function(test) {
|
|
var inner = [], outerTail = [];
|
|
for (var i = 0; i < 200; ++i) inner.push((i * 7) & 255);
|
|
for (var j = 0; j < 50; ++j) outerTail.push((j * 3) & 255);
|
|
// outer field 1 { inner field 2 = 200 bytes } + 50 trailing raw bytes
|
|
var buf = new Ctor()
|
|
.uint32(10).fork()
|
|
.uint32(18).fork().raw(inner).ldelim()
|
|
.raw(outerTail)
|
|
.ldelim()
|
|
.finish();
|
|
var reader = Reader.create(buf);
|
|
test.equal(reader.uint32(), 10, name + " outer tag");
|
|
var outer = reader.bytes();
|
|
var ri = Reader.create(outer);
|
|
test.equal(ri.uint32(), 18, name + " inner tag");
|
|
test.same(toArray(ri.bytes()), inner, name + " inner content intact");
|
|
test.same(toArray(outer).slice(ri.pos), outerTail, name + " trailing content intact");
|
|
test.end();
|
|
});
|
|
|
|
test.test(name + " - string fast path: multi-byte, surrogates, boundary", function(test) {
|
|
var cases = [
|
|
"",
|
|
"abc",
|
|
"ä".repeat(63), // 126 bytes, 1-byte length, encode-then-measure, no move
|
|
"ä".repeat(64), // 128 bytes, 2-byte length, encode-then-measure + move
|
|
"€".repeat(50), // 3-byte chars, 150 bytes, move
|
|
"😀".repeat(32), // surrogate pairs: 64 units, 128 bytes, move
|
|
"x".repeat(127), // encode-then-measure, ascii, 1-byte length
|
|
"x".repeat(128), // precompute branch (>= threshold), ascii
|
|
"y".repeat(5000), // precompute branch, forces buffer growth
|
|
"🎉mixed-ä-€-text".repeat(20)
|
|
];
|
|
cases.forEach(function(s) {
|
|
var buf = new Ctor().string(s).finish();
|
|
test.equal(Reader.create(buf).string(), s, name + " roundtrips string of " + s.length + " units");
|
|
});
|
|
test.end();
|
|
});
|
|
|
|
test.test(name + " - hybrid branches agree with reference bytes", function(test) {
|
|
// a string just below and well above the precompute threshold must both
|
|
// produce [minimal-length-varint][utf8 bytes] identical to a manual encoding
|
|
[ "ä".repeat(60), "ä".repeat(300) ].forEach(function(s) {
|
|
var utf8 = protobuf.util.utf8;
|
|
var byteLen = utf8.length(s);
|
|
var ref = new Ctor().uint32(byteLen).finish();
|
|
var buf = new Ctor().string(s).finish();
|
|
test.same(toArray(buf).slice(0, ref.length), toArray(ref), name + " length prefix matches for " + s.length + " units");
|
|
test.equal(buf.length, ref.length + byteLen, name + " total length matches for " + s.length + " units");
|
|
test.equal(Reader.create(buf).string(), s, name + " roundtrips for " + s.length + " units");
|
|
});
|
|
test.end();
|
|
});
|
|
|
|
test.test(name + " - growth across many fields", function(test) {
|
|
var w = new Ctor();
|
|
var n = 5000;
|
|
for (var i = 0; i < n; ++i)
|
|
w.uint32(i);
|
|
var reader = Reader.create(w.finish());
|
|
var ok = true;
|
|
for (var j = 0; j < n; ++j)
|
|
if (reader.uint32() !== j) { ok = false; break; }
|
|
test.ok(ok, name + " all " + n + " varints survive repeated growth");
|
|
test.equal(reader.pos, reader.len, name + " fully consumed");
|
|
test.end();
|
|
});
|
|
});
|
|
|
|
test.test("nested message roundtrip exceeding 128 bytes at multiple levels", function(test) {
|
|
var root = protobuf.Root.fromJSON({
|
|
nested: {
|
|
Inner: { fields: { data: { type: "bytes", id: 1 } } },
|
|
Outer: { fields: { inner: { type: "Inner", id: 1 }, tail: { type: "bytes", id: 2 } } }
|
|
}
|
|
});
|
|
var Outer = root.lookupType("Outer");
|
|
var big = new Uint8Array(300), tail = new Uint8Array(200);
|
|
for (var i = 0; i < 300; ++i) big[i] = (i * 5) & 255;
|
|
for (var j = 0; j < 200; ++j) tail[j] = (j * 9) & 255;
|
|
var dec = Outer.decode(Outer.encode({ inner: { data: big }, tail: tail }).finish());
|
|
test.same(toArray(dec.inner.data), toArray(big), "nested bytes intact");
|
|
test.same(toArray(dec.tail), toArray(tail), "tail bytes intact");
|
|
test.end();
|
|
});
|
|
|
|
test.end();
|
|
});
|