Add option to parse non-varint Protobuf fields as floats

This commit is contained in:
Chris Pinola 2020-09-14 13:26:45 -04:00
parent c9d9730726
commit 6d2cacc30c
3 changed files with 149 additions and 8 deletions

View file

@ -17,8 +17,9 @@ class Protobuf {
* Protobuf constructor
*
* @param {byteArray|Uint8Array} data
* @param {boolean} parseFixedAsFloats
*/
constructor(data) {
constructor(data, parseFixedAsFloats=false) {
// Check we have a byteArray or Uint8Array
if (data instanceof Array || data instanceof Uint8Array) {
this.data = data;
@ -26,6 +27,9 @@ class Protobuf {
throw new Error("Protobuf input must be a byteArray or Uint8Array");
}
// Unpack config
this.parseFixedAsFloats = parseFixedAsFloats;
// Set up masks
this.TYPE = 0x07;
this.NUMBER = 0x78;
@ -80,10 +84,11 @@ class Protobuf {
* Parse Protobuf data
*
* @param {byteArray} input
* @param {boolean} parseFixedAsFloats
* @returns {Object}
*/
static decode(input) {
const pb = new Protobuf(input);
static decode(input, parseFixedAsFloats=false) {
const pb = new Protobuf(input, parseFixedAsFloats);
return pb._parse();
}
@ -149,13 +154,13 @@ class Protobuf {
return { "key": key, "value": this._varInt() };
// fixed 64
case 1:
return { "key": key, "value": this._uint64() };
return { "key": key, "value": this.parseFixedAsFloats ? this._float64() : this._uint64() };
// length delimited
case 2:
return { "key": key, "value": this._lenDelim() };
// fixed 32
case 5:
return { "key": key, "value": this._uint32() };
return { "key": key, "value": this.parseFixedAsFloats ? this._float32() : this._uint32() };
// unknown type
default:
throw new Error("Unknown type 0x" + type.toString(16));
@ -256,7 +261,7 @@ class Protobuf {
let field;
try {
// Attempt to parse as a new Protobuf Object
const pbObject = new Protobuf(fieldBytes);
const pbObject = new Protobuf(fieldBytes, this.parseFixedAsFloats);
field = pbObject._parse();
} catch (err) {
// Otherwise treat as bytes
@ -280,6 +285,34 @@ class Protobuf {
this.offset += 4;
return value;
}
/**
* Read a 32 bit floating point from the data
*
* @private
* @returns {number}
*/
_float32() {
const sizeInBytes = 4;
const dataview = new DataView(new Uint8Array(this.data.slice(this.offset, this.offset + sizeInBytes)).buffer);
const value = dataview.getFloat32(0, true);
this.offset += sizeInBytes;
return value;
}
/**
* Read a 64 bit floating point from the data
*
* @private
* @returns {number}
*/
_float64() {
const sizeInBytes = 8;
const dataview = new DataView(new Uint8Array(this.data.slice(this.offset, this.offset + sizeInBytes)).buffer);
const value = dataview.getFloat64(0, true);
this.offset += sizeInBytes;
return value;
}
}
export default Protobuf;

View file

@ -25,7 +25,13 @@ class ProtobufDecode extends Operation {
this.infoURL = "https://wikipedia.org/wiki/Protocol_Buffers";
this.inputType = "ArrayBuffer";
this.outputType = "JSON";
this.args = [];
this.args = [
{
"name": "Decode non-varints as floats",
"type": "boolean",
"value": false
}
];
}
/**
@ -36,7 +42,7 @@ class ProtobufDecode extends Operation {
run(input, args) {
input = new Uint8Array(input);
try {
return Protobuf.decode(input);
return Protobuf.decode(input, args[0]);
} catch (err) {
throw new OperationError(err);
}

View file

@ -33,4 +33,106 @@ TestRegister.addTests([
}
]
},
/**
* Input generated from:
* ```
$ cat test.proto
syntax = "proto3";
message Test {
float a = 1;
}
$ protoc --version
libprotoc 3.11.1
$ echo a:1 | protoc --encode=Test test.proto | xxd -p
0d0000803f
```
*/
{
name: "Protobuf Decode - parse fixed32 as integer",
input: "0d0000803f",
expectedOutput: JSON.stringify({
"1": 32831,
}, null, 4),
recipeConfig: [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "Protobuf Decode",
"args": [false]
}
]
},
{
name: "Protobuf Decode - parse fixed32 as float32",
input: "0d0000803f",
expectedOutput: JSON.stringify({
"1": 1,
}, null, 4),
recipeConfig: [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "Protobuf Decode",
"args": [true]
}
]
},
/**
* Input generated from:
* ```
$ cat test.proto
syntax = "proto3";
message Test {
double a = 1;
}
$ protoc --version
libprotoc 3.11.1
$ echo a:1 | protoc --encode=Test test.proto | xxd -p
09000000000000f03f
```
*/
{
name: "Protobuf Decode - parse fixed64 as integer",
input: "09000000000000f03f",
expectedOutput: JSON.stringify({
"1": 61503,
}, null, 4),
recipeConfig: [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "Protobuf Decode",
"args": [false]
}
]
},
{
name: "Protobuf Decode - parse fixed64 as float64",
input: "09000000000000f03f",
expectedOutput: JSON.stringify({
"1": 1,
}, null, 4),
recipeConfig: [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "Protobuf Decode",
"args": [true]
}
]
}
]);