Skip to content
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

Support simd_json as an optional alternative to serde_json #934

Draft
wants to merge 44 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
f522ea8
Support simd_json as an optional alternative to serde_json
Oct 18, 2024
0807675
Start hacking stuff into mutable slices
Oct 19, 2024
cba3fdf
Hack tests to use mutable strings/slices
Oct 19, 2024
6125836
So, now that simd-json Values can be Eq ...
Oct 22, 2024
f20d03c
Fix "missing method" issues (expose and use the right simd_json trait)
Oct 22, 2024
3c696e4
Adaptation to simd json parsing
Oct 22, 2024
f95180b
Amazingly, we appear to have resolved all compile issues outside of l…
Oct 22, 2024
12cb88f
Explanations
Oct 22, 2024
6f8ba9e
Revert to live simd_json crate - supports Eq values now
Oct 22, 2024
79c9d6e
Complete replacement of serde_json reference by aws_lambda_json_impl …
Oct 23, 2024
0940f12
cleanup
Oct 23, 2024
5ec7dba
fix dependencies
Oct 23, 2024
8344c12
Fix simd-related imports
Oct 23, 2024
f2b146e
Don't rename from_slice
Oct 23, 2024
d24f7bd
Generalize tests
Oct 23, 2024
137a210
Don't rename from_slice
Oct 23, 2024
f38e250
A better approach to intended use
Oct 23, 2024
3c1d23e
One error left in this round - missing trait on
Oct 23, 2024
334246e
Fix need for 'from_reader' on deserializer
Oct 23, 2024
e0317b2
We're down to the core of the issue
Oct 24, 2024
fbe4802
All compile errors "fixed" ... but SERIOUSLY unsafe at the moment
Oct 24, 2024
1893e6a
Explain the unsafe code and the constraints that make it 'safe'
Oct 25, 2024
2c26efd
Fixes for when using serde_json
Oct 25, 2024
4c0a2ee
Clippy lints for simd_json code
Oct 25, 2024
bd6a3d8
Test fixes and clippy lints
Oct 25, 2024
0548317
MARKER: All original tests work in serde_json mode
Oct 25, 2024
72e3636
Doc and change simd_json provenance
Oct 25, 2024
bcf29ef
MARKER: All but one test passed in simd_json mode!
Oct 25, 2024
ebf675f
All tests work...
Oct 25, 2024
353d14e
MARKER: serde_json mode works with no adverse affect on performance
Oct 25, 2024
bbc41da
Add debugging
Oct 25, 2024
2935c9f
More debug
Oct 25, 2024
4e0c417
Yeah, that didn't work AT ALL - literal crash!
Oct 26, 2024
4ab9c3c
The unsafe cast that works (using proposed features of simd_json)
Oct 28, 2024
23e5950
MARKER: Tests work for HTTP request deserialization via simd_json
Oct 28, 2024
08dd1b0
Remove explicit simd_json source
Oct 28, 2024
4950699
MARKER: Local integration tests work
Oct 28, 2024
614b8a3
fix doc tests
Oct 28, 2024
7b2f0bb
A bit more debugging
Oct 28, 2024
1f3f984
Comment out restart - it seems it's not required
Oct 28, 2024
05c0a0b
Tests without restart work - not net needed in this implementation
Oct 28, 2024
a939b8f
Use simd-json main
Oct 28, 2024
825e343
Simplify the cast and adjust the doc
Oct 29, 2024
a853fb9
Support feature pass_through
Oct 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
resolver = "2"
members = [
"json-impl",
"lambda-http",
"lambda-integration-tests",
"lambda-runtime-api-client",
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ If you'd like to manually create your first function, the code below shows you a

```rust,no_run
use lambda_runtime::{service_fn, LambdaEvent, Error};
use serde_json::{json, Value};
use aws_lambda_json_impl::{json, Value};

#[tokio::main]
async fn main() -> Result<(), Error> {
Expand Down Expand Up @@ -375,7 +375,7 @@ To serialize and deserialize events and responses, we suggest using the [`serde`

```rust,no_run
use serde::{Serialize, Deserialize};
use serde_json::json;
use aws_lambda_json_impl::json;
use std::error::Error;

#[derive(Serialize, Deserialize)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use lambda_runtime::{service_fn, tracing, Error, LambdaEvent};
use pizza_lib::Pizza;
use serde_json::{json, Value};
use aws_lambda_json_impl::{json, Value};

struct SQSManager {
client: aws_sdk_sqs::Client,
Expand Down
2 changes: 1 addition & 1 deletion examples/basic-error-handling/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/// See https://github.com/awslabs/aws-lambda-rust-runtime for more info on Rust runtime for AWS Lambda
use lambda_runtime::{service_fn, tracing, Error, LambdaEvent};
use serde::{Deserialize, Serialize};
use serde_json::json;
use aws_lambda_json_impl::json;
use std::fs::File;

/// A simple Lambda request structure with just one field
Expand Down
2 changes: 1 addition & 1 deletion examples/basic-streaming-response/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use lambda_runtime::{
streaming::{channel, Body, Response},
tracing, Error, LambdaEvent,
};
use serde_json::Value;
use aws_lambda_json_impl::Value;
use std::{thread, time::Duration};

async fn func(_event: LambdaEvent<Value>) -> Result<Response<Body>, Error> {
Expand Down
2 changes: 1 addition & 1 deletion examples/http-axum-apigw-authorizer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use axum::{
Router,
};
use lambda_http::{run, tracing, Error, RequestExt};
use serde_json::{json, Value};
use aws_lambda_json_impl::{json, Value};
use std::{collections::HashMap, env::set_var};

struct AuthorizerField(String);
Expand Down
2 changes: 1 addition & 1 deletion examples/http-axum-middleware/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use axum::{response::Json, routing::post, Router};
use lambda_http::request::RequestContext::ApiGatewayV1;
use lambda_http::{run, tracing, Error};
use serde_json::{json, Value};
use aws_lambda_json_impl::{json, Value};

// Sample middleware that logs the request id
async fn mw_sample(req: axum::extract::Request, next: axum::middleware::Next) -> impl axum::response::IntoResponse {
Expand Down
2 changes: 1 addition & 1 deletion examples/http-axum/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use axum::{
};
use lambda_http::{run, tracing, Error};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use aws_lambda_json_impl::{json, Value};
use std::env::set_var;

#[derive(Deserialize, Serialize)]
Expand Down
2 changes: 1 addition & 1 deletion examples/lambda-rds-iam-auth/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use aws_sigv4::{
sign::v4,
};
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use serde_json::{json, Value};
use aws_lambda_json_impl::{json, Value};
use sqlx::postgres::PgConnectOptions;
use std::env;
use std::time::{Duration, SystemTime};
Expand Down
26 changes: 26 additions & 0 deletions json-impl/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "aws_lambda_json_impl"
version = "0.15.1"
description = "AWS Lambda JSON adapter switch between serde_json and simd_json"
authors = [
"Martin Bartmett <[email protected]>",
]
license = "MIT"
homepage = "https://github.com/awslabs/aws-lambda-rust-runtime"
repository = "https://github.com/awslabs/aws-lambda-rust-runtime"
readme = "README.md"
keywords = ["lambda", "aws", "amazon", "events", "S3", "json"]
categories = ["api-bindings", "encoding", "web-programming"]
edition = "2021"

[features]
default = [ ]
simd = [ "simd-json/ordered-float", "value-trait/ordered-float" ]

[dependencies]
serde_json = { version = "^1", features = ["raw_value"] }
#simd-json = { version = "^0", optional = true }
simd-json = { git = "https://github.com/simd-lite/simd-json.git", branch = "main", optional = true }
value-trait = { version = "^0", optional = true }
bytes = { workspace = true }
serde = { version = "^1", no-default-features = true }
93 changes: 93 additions & 0 deletions json-impl/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Using serde_json as the JSON handler
#[cfg(not(feature = "simd"))]
pub use serde::*;
// Using simd_json as the JSON handler
#[cfg(feature = "simd")]
pub use simd::*;

// Implementations

#[cfg(not(feature = "simd"))]
mod serde {
use bytes::Bytes;
use serde::de::DeserializeOwned;
pub use serde_json::{
self, error::Error as JsonError, from_reader, from_slice, from_str, from_value, json, to_string,
to_string_pretty, to_value, to_writer, value::RawValue, Deserializer as JsonDeserializer, Value,
to_vec,
};
pub fn from_bytes<T>(b: Bytes) -> serde_json::Result<T>
where
T: DeserializeOwned,
{
from_slice(&b)
}

pub fn from_string<T>(s: String) -> serde_json::Result<T>
where
T: DeserializeOwned,
{
from_str(s.as_str())
}

pub fn from_vec<T>(v: Vec<u8>) -> serde_json::Result<T>
where
T: DeserializeOwned,
{
from_slice(&v)
}
}

#[cfg(feature = "simd")]
mod simd {
use bytes::Bytes;
use serde::de::DeserializeOwned;
pub use simd_json::{
self,
json,
owned::Value,
serde::{
from_owned_value as from_value,
from_reader,
from_str, //THIS requires a mutable string slice AND is unsafe
from_slice, //THIS requires a mutable slice!
to_owned_value as to_value,
to_string,
to_string_pretty,
to_writer,
to_vec,
},
tape::Value as RawValue, //THIS is gonna be the fun one!
Deserializer as JsonDeserializer,
Error as JsonError,
};
pub use value_trait::prelude::*;

pub fn from_bytes<T>(b: Bytes) -> simd_json::Result<T>
where
T: DeserializeOwned,
{
match b.try_into_mut() {
Ok(mut b) => from_slice(&mut b),
Err(b) => {
let mut v = b.to_vec();
from_slice(&mut v)
}
}
}

pub fn from_string<T>(mut s: String) -> simd_json::Result<T>
where
T: DeserializeOwned,
{
unsafe{ from_str(s.as_mut_str()) }
}

pub fn from_vec<T>(mut v: Vec<u8>) -> simd_json::Result<T>
where
T: DeserializeOwned,
{
from_slice(&mut v)
}
}

6 changes: 5 additions & 1 deletion lambda-events/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ query_map = { version = "^0.7", features = [
], optional = true }
serde = { version = "^1", features = ["derive"] }
serde_with = { version = "^3", features = ["json"], optional = true }
serde_json = "^1"
aws_lambda_json_impl = { path = "../json-impl" }
serde_dynamo = { version = "^4.1", optional = true }

[features]
default = [

# "simd_json",

"activemq",
"alb",
"apigw",
Expand Down Expand Up @@ -123,3 +126,4 @@ sqs = ["serde_with"]
streams = []
documentdb = []
eventbridge = ["chrono", "serde_with"]
simd_json = [ "aws_lambda_json_impl/simd" ]
8 changes: 4 additions & 4 deletions lambda-events/src/custom_serde/codebuild_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ mod tests {
#[serde(with = "str_time")]
pub date: TestTime,
}
let data = serde_json::json!({
let data = aws_lambda_json_impl::json!({
"date": "Sep 1, 2017 4:12:29 PM"
});

let expected = NaiveDateTime::parse_from_str("Sep 1, 2017 4:12:29 PM", CODEBUILD_TIME_FORMAT)
.unwrap()
.and_utc();
let decoded: Test = serde_json::from_value(data).unwrap();
let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap();
assert_eq!(expected, decoded.date);
}

Expand All @@ -96,14 +96,14 @@ mod tests {
#[serde(with = "optional_time")]
pub date: Option<TestTime>,
}
let data = serde_json::json!({
let data = aws_lambda_json_impl::json!({
"date": "Sep 1, 2017 4:12:29 PM"
});

let expected = NaiveDateTime::parse_from_str("Sep 1, 2017 4:12:29 PM", CODEBUILD_TIME_FORMAT)
.unwrap()
.and_utc();
let decoded: Test = serde_json::from_value(data).unwrap();
let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap();
assert_eq!(Some(expected), decoded.date);
}
}
24 changes: 12 additions & 12 deletions lambda-events/src/custom_serde/headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@ mod tests {
#[serde(deserialize_with = "deserialize_headers", default)]
pub headers: HeaderMap,
}
let data = serde_json::json!({
let data = aws_lambda_json_impl::json!({
"not_headers": {}
});

let expected = HeaderMap::new();

let decoded: Test = serde_json::from_value(data).unwrap();
let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap();
assert_eq!(expected, decoded.headers);
}

Expand All @@ -163,16 +163,16 @@ mod tests {
#[serde(serialize_with = "serialize_multi_value_headers")]
headers: HeaderMap,
}
let data = serde_json::json!({
let data = aws_lambda_json_impl::json!({
"headers": {
"Accept": ["*/*"]
}
});
let decoded: Test = serde_json::from_value(data).unwrap();
let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap();
assert_eq!(&"*/*", decoded.headers.get("Accept").unwrap());

let recoded = serde_json::to_value(decoded).unwrap();
let decoded: Test = serde_json::from_value(recoded).unwrap();
let recoded = aws_lambda_json_impl::to_value(decoded).unwrap();
let decoded: Test = aws_lambda_json_impl::from_value(recoded).unwrap();
assert_eq!(&"*/*", decoded.headers.get("Accept").unwrap());
}

Expand All @@ -183,9 +183,9 @@ mod tests {
#[serde(deserialize_with = "deserialize_headers")]
headers: HeaderMap,
}
let data = serde_json::json!({ "headers": null });
let data = aws_lambda_json_impl::json!({ "headers": null });

let decoded: Test = serde_json::from_value(data).unwrap();
let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap();
assert!(decoded.headers.is_empty());
}

Expand All @@ -203,23 +203,23 @@ mod tests {

let content_disposition =
"inline; filename=\"Schillers schönste Szenenanweisungen -Kabale und Liebe.mp4.avif\"";
let data = serde_json::json!({
let data = aws_lambda_json_impl::json!({
"headers": {
"Content-Disposition": content_disposition
},
"multi_value_headers": {
"Content-Disposition": content_disposition
}
});
let decoded: Test = serde_json::from_value(data).unwrap();
let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap();
assert_eq!(content_disposition, decoded.headers.get("Content-Disposition").unwrap());
assert_eq!(
content_disposition,
decoded.multi_value_headers.get("Content-Disposition").unwrap()
);

let recoded = serde_json::to_value(decoded).unwrap();
let decoded: Test = serde_json::from_value(recoded).unwrap();
let recoded = aws_lambda_json_impl::to_value(decoded).unwrap();
let decoded: Test = aws_lambda_json_impl::from_value(recoded).unwrap();
assert_eq!(content_disposition, decoded.headers.get("Content-Disposition").unwrap());
assert_eq!(
content_disposition,
Expand Down
20 changes: 10 additions & 10 deletions lambda-events/src/custom_serde/http_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,13 @@ mod tests {
#[serde(with = "crate::custom_serde::http_method")]
pub method: http::Method,
}
let data = serde_json::json!({
let data = aws_lambda_json_impl::json!({
"method": "DELETE"
});
let decoded: Test = serde_json::from_value(data.clone()).unwrap();
let decoded: Test = aws_lambda_json_impl::from_value(data.clone()).unwrap();
assert_eq!(http::Method::DELETE, decoded.method);

let recoded = serde_json::to_value(decoded).unwrap();
let recoded = aws_lambda_json_impl::to_value(decoded).unwrap();
assert_eq!(data, recoded);
}

Expand All @@ -86,21 +86,21 @@ mod tests {
#[serde(default)]
pub method: Option<http::Method>,
}
let data = serde_json::json!({
let data = aws_lambda_json_impl::json!({
"method": "DELETE"
});
let decoded: Test = serde_json::from_value(data.clone()).unwrap();
let decoded: Test = aws_lambda_json_impl::from_value(data.clone()).unwrap();
assert_eq!(Some(http::Method::DELETE), decoded.method);

let recoded = serde_json::to_value(decoded).unwrap();
let recoded = aws_lambda_json_impl::to_value(decoded).unwrap();
assert_eq!(data, recoded);

let data = serde_json::json!({ "method": null });
let decoded: Test = serde_json::from_value(data).unwrap();
let data = aws_lambda_json_impl::json!({ "method": null });
let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap();
assert_eq!(None, decoded.method);

let data = serde_json::json!({});
let decoded: Test = serde_json::from_value(data).unwrap();
let data = aws_lambda_json_impl::json!({});
let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap();
assert_eq!(None, decoded.method);
}
}
Loading