-
Notifications
You must be signed in to change notification settings - Fork 190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor converters to numeric types for aws_smithy_types::Number
#1274
Refactor converters to numeric types for aws_smithy_types::Number
#1274
Conversation
Currently, conversions from `aws_smithy_types::Number` into numeric Rust types (`{i,u}{8, 16, 32, 64}` and `f{32, 64}`) are always lossy, because they use the `as` Rust keyword to cast into the target type. This means that clients and servers are accepting lossy data: for example, if an operation is modeled to take in a 32-bit integer as input, and a client incorrectly sends an integer number that does not fit in 32 bits, the server will silently accept the truncated input. There are malformed request protocol tests that verify that servers must reject these requests. This commit removes the lossy `to_*` methods on `Number` and instead implements `TryFrom<$typ> for Number` for the target numeric type `$typ`. These converters will attempt their best to perform the conversion safely, and fail if it is lossy. The code-generated JSON parsers will now fail with `aws_smithy_json::deserialize::ErrorReason::InvalidNumber` if the number in the JSON document cannot be converted into the modeled integer type without losing precision. For floating point target types, lossy conversions are still performed, via `Number::to_f32_lossy` and `Number::to_f64_lossy`.
A new doc preview is ready to view. |
A new generated diff is ready to view. |
Rust Wrk benchmark report:Duration: 90 sec, Connections: 32, Threads: 2
|
A new generated diff is ready to view. |
A new doc preview is ready to view. |
Rust Wrk benchmark report:Duration: 90 sec, Connections: 32, Threads: 2
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a couple of simple comments and suggestions. LGTM overall. Nice test suite!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM. It seems like it may be possible to refactor the converters to share macros but probably not worth it.
I may consider adding a proptest of some invariants as well.
CHANGELOG.next.toml
Outdated
@@ -34,3 +34,9 @@ message = "Update all SDK and runtime crates to [edition 2021](https://blog.rust | |||
references = ["aws-sdk-rust#490"] | |||
meta = { "breaking" = true, "tada" = false, "bug" = false } | |||
author = "Velfi" | |||
|
|||
[[smithy-rs]] | |||
message = "Refactor converters to numeric types for `aws_smithy_types::Number`" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you indicate what the break is and how users should update their code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I doubt any are performing these conversions themselves, but here's the updated changelog: c21572d
fn from(_: aws_smithy_types::TryFromNumberError) -> Self { | ||
Error { | ||
reason: ErrorReason::InvalidNumber, | ||
offset: None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we throw away this offset is it going to make it really hard to debug? Or do we have another mechanism to track the exact field where this was a problem?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have the offset here. We only have the offset when working with the token directly in the code-generated deserializer, or when calling the expect_
functions which auto-fill it from the token in case of errors. When unescaping strings, we're also bubbling up errors without offsets. Making both cases bubble up offsets would require some refactoring in the parser generation.
pub enum Number { | ||
/// Unsigned 64-bit integer value | ||
/// Unsigned 64-bit integer value. | ||
PosInt(u64), | ||
/// Signed 64-bit integer value | ||
/// Signed 64-bit integer value. The wrapped value is _always_ negative. | ||
NegInt(i64), | ||
/// 64-bit floating-point value | ||
/// 64-bit floating-point value. | ||
Float(f64), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could consider making these variants #[non_exhaustive]
which would prevent them from being directly instantiated so that we could insure that the invariants were held
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What invariants? I don't understand the suggestion. These variants are being directly instantiated here:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
by invariants I mean: /// Signed 64-bit integer value. The wrapped value is _always_ negative.
We would add constructor functions, ::pos_int
, ::neg_int
, ::float
.
We would need to refactor code to use those. neg_int
could panic if you passed in a positive int (or return a result, eg.)
not a blocker, just a possibility
Should I add proptesting? In my tests I tested one or two values within the target type's range, as well as the values in the edges of the range (plus assert_eq!($typ::try_from(Number::PosInt(v)).unwrap(), v); with |
A new generated diff is ready to view. A new doc preview is ready to view. Rust Wrk benchmark report:Duration: 90 sec, Connections: 32, Threads: 2
|
Added |
A new generated diff is ready to view.
A new doc preview is ready to view. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, this looks good to me. Just a couple minor things.
...ain/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt
Outdated
Show resolved
Hide resolved
…nverters-to-numeric-types
…nverters-to-numeric-types
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
…nverters-to-numeric-types
A new generated diff is ready to view.
A new doc preview is ready to view. |
Currently, conversions from
aws_smithy_types::Number
into numeric Rusttypes (
{i,u}{8, 16, 32, 64}
andf{32, 64}
) are always lossy, becausethey use the
as
Rust keyword to cast into the target type. This meansthat clients and servers are accepting lossy data: for example, if an
operation is modeled to take in a 32-bit integer as input, and a client
incorrectly sends an integer number that does not fit in 32 bits, the
server will silently accept the truncated input. There are malformed
request protocol tests that verify that servers must reject these
requests.
This commit removes the lossy
to_*
methods onNumber
and insteadimplements
TryFrom<Number> for $typ
for the target numeric type$typ
. These converters will attempt their best to perform theconversion safely, and fail if it is lossy.
The code-generated JSON parsers will now fail with
aws_smithy_json::deserialize::ErrorReason::InvalidNumber
if the numberin the JSON document cannot be converted into the modeled integer type
without losing precision. For floating point target types, lossy
conversions are still performed, via
Number::to_f32_lossy
andNumber::to_f64_lossy
.Motivation and Context
Description
Testing
Checklist
CHANGELOG.next.toml
if I made changes to the smithy-rs codegen or runtime cratesCHANGELOG.next.toml
if I made changes to the AWS SDK, generated SDK code, or SDK runtime cratesBy submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.