diff --git a/CHANGELOG.md b/CHANGELOG.md index d78a014..d5d4442 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ number says nothing about any other port. See the Releases section of - **`pxf::FormatDocument` keeps every directive** (#130, protowire#558), as protowire-go does (protowire-go#262). It wrote only `@type`, comments and entries. So a `@dataset` document formatted to nothing and lost every row, and `@entry`, `@proto` and named directives were dropped from any document. It now writes them in document order, each after its comments. `@type` comes first, since it binds no anonymous `@proto`. A dataset is written as its header, then one row per line, with an absent cell left empty and `null` written. A named directive's body and a `@proto` body are written as they were read. A document whose only directive is `@type` formats as before. - **`pxf::Marshal` refuses a `BigFloat` whose value rounded to its `prec` is past `kMaxBigFloatExponent`** (#128, protowire#560), as protowire-go does (protowire-go#261), with `pxf.BigFloat binary exponent 16385 after rounding to prec 3 exceeds MaxBigFloatExponent=16384`. Rounding can carry: a 54-bit all-ones mantissa at exponent 16330 is at the bound as stored and past it at prec 3. It used to be written as `1.2e+4932p3`, which no reader accepts. **This narrows what encodes.** A `@default` carrier's `big_float_value` is refused the same way. Its three limit refusals now use protowire-go's words. Both paths also refuse a zero whose `prec` renders past `MaxNumericLiteralDigits`, as Go does; it used to be written as `0p`. +- **`check_decode` prints a PXF rejection with its position** (#145), `reject: 3:3: unknown field ...`, as protowire-go's does, for `--format pxf` and `pxf-pb`. - **`sbe::Codec::Marshal` cuts an overlong string on a character boundary** (#105, protowire#495), as protowire-go does (protowire-go#246). A `string` longer than its `(sbe.length)` is cut at the last whole UTF-8 character that fits and NUL-padded. A byte-exact cut could leave half a character, and then every strict decoder rejected the whole message. A `bytes` field is still cut at the limit. **The bytes written for such a string change.** - **A PXF `nan` is stored as the canonical quiet NaN, `0x7FF8000000000000`** (#113, protowire#512), as protowire-go now does (protowire-go#241). It was Go's old `math.NaN()`, `0x7FF8000000000001`. That covers values, list elements, `DoubleValue`, and `(pxf.default)`. **The PB and SBE bytes of a double `nan` read from text change.** Float bytes don't change. `pb::Marshal` also writes any NaN in a `double` or `float` struct field canonically: singular, in a list, and as a map value. A message's fields are written as they are, so a NaN read from PB keeps its payload. - **`pb::Unmarshal` refuses a `pxf.BigFloat` whose `prec` is past the precision-suffix bound** (#108, protowire#499). That is a `prec` whose literal needs more than `max_numeric_literal_digits` digits: above 13603 bits at the default, zeros included. It is checked before the value is built. An 11-byte message at prec 2^31-1 used to decode, and writing it back widened the mantissa to 256 MiB. **This rejects messages that were accepted before.** `UnmarshalOptions::max_numeric_literal_digits` moves the bound. `pb::Marshal` still writes a hand-built value past it, because it cannot refuse yet (#93). diff --git a/cmd/check_decode/main.cc b/cmd/check_decode/main.cc index 4e619f5..17f3f9d 100644 --- a/cmd/check_decode/main.cc +++ b/cmd/check_decode/main.cc @@ -308,7 +308,7 @@ bool PxfDecode(std::string_view data, opts.max_big_float_exponent = lim.max_big_float_exponent; auto status = protowire::pxf::Unmarshal(data, msg.get(), opts); if (!status.ok()) { - *err = std::string(status.message()); + *err = status.ToString(); // "line:col: msg", as Go prints it return false; } return true; @@ -359,7 +359,7 @@ bool PxfPb(std::string_view data, opts.max_big_float_exponent = lim.max_big_float_exponent; auto result = protowire::pxf::UnmarshalFull(data, msg.get(), opts); if (!result.ok()) { - *err = std::string(result.status().message()); + *err = result.status().ToString(); // "line:col: msg", as Go prints it return false; } return WriteStdout(protowire::pxf::SerializeGoOrder(*msg), err); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f8bbc4d..3c5cd81 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -81,3 +81,16 @@ if(TARGET protowire_sbe) gtest_discover_tests(protowire_sbe_test) endif() endif() + +# check_decode prints a PXF rejection with its position, as protowire-go's +# does (#145). The spec's gates compare these lines across ports. +if(TARGET check_decode) + foreach(fmt pxf pxf-pb) + add_test(NAME check_decode_position_${fmt} + COMMAND check_decode --format ${fmt} --schema test.v1.AllTypes + --proto ${CMAKE_CURRENT_SOURCE_DIR}/../testdata/test.proto + --input ${CMAKE_CURRENT_SOURCE_DIR}/../testdata/check_decode/unknown-field.pxf) + set_tests_properties(check_decode_position_${fmt} PROPERTIES + PASS_REGULAR_EXPRESSION "reject: 3:3: unknown field \"nope\" in test\\.v1\\.AllTypes") + endforeach() +endif() diff --git a/testdata/check_decode/unknown-field.pxf b/testdata/check_decode/unknown-field.pxf new file mode 100644 index 0000000..38ac41b --- /dev/null +++ b/testdata/check_decode/unknown-field.pxf @@ -0,0 +1,3 @@ + + + nope = 1