Skip to content

Commit d878166

Browse files
HJLebbinkTest User
authored andcommitted
added CRC32, CRC32C, SHA1, SHA256 and CRC64NVME
1 parent 2e94a0e commit d878166

21 files changed

+1572
-15
lines changed

Cargo.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ keywords = ["object-storage", "minio", "s3"]
1111
categories = ["api-bindings", "web-programming::http-client"]
1212

1313
[features]
14-
default = ["default-tls", "default-crypto"]
14+
default = ["default-tls", "default-crypto", "ring"]
1515
default-tls = ["reqwest/default-tls"]
1616
native-tls = ["reqwest/native-tls"]
1717
rustls-tls = ["reqwest/rustls-tls"]
@@ -40,6 +40,8 @@ async-trait = "0.1"
4040
base64 = "0.22"
4141
chrono = { version = "0.4", features = ["serde"] }
4242
crc = "3.3"
43+
crc32c = "0.6"
44+
crc32fast = "1.4"
4345
dashmap = "6.1.0"
4446
env_logger = "0.11"
4547
hmac = { version = "0.12", optional = true }
@@ -55,6 +57,7 @@ ring = { version = "0.17", optional = true, default-features = false, features =
5557
serde = { version = "1.0", features = ["derive"] }
5658
serde_json = "1.0"
5759
serde_yaml = "0.9"
60+
sha1 = "0.10"
5861
sha2 = { version = "0.10", optional = true }
5962
urlencoding = "2.1"
6063
xmltree = "0.12"
@@ -95,3 +98,8 @@ name = "load_balancing_with_hooks"
9598
name = "s3-api"
9699
path = "benches/s3/api_benchmarks.rs"
97100
harness = false
101+
102+
[[bench]]
103+
name = "bench_checksums"
104+
path = "benches/bench_checksums.rs"
105+
harness = false

benches/bench_checksums.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
2+
// Copyright 2025 MinIO, Inc.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
use criterion::{Criterion, Throughput, criterion_group, criterion_main};
17+
use minio::s3::utils::{crc32_checksum, crc32c, crc64nvme_checksum, sha1_hash, sha256_checksum};
18+
19+
fn bench_checksums(c: &mut Criterion) {
20+
let sizes = vec![
21+
("1KB", 1024),
22+
("10KB", 10 * 1024),
23+
("100KB", 100 * 1024),
24+
("1MB", 1024 * 1024),
25+
("10MB", 10 * 1024 * 1024),
26+
];
27+
28+
for (name, size) in sizes {
29+
let data = vec![0u8; size];
30+
31+
let mut group = c.benchmark_group(format!("checksum_{}", name));
32+
group.throughput(Throughput::Bytes(size as u64));
33+
34+
group.bench_function("CRC32", |b| b.iter(|| crc32_checksum(&data)));
35+
36+
group.bench_function("CRC32C", |b| b.iter(|| crc32c(&data)));
37+
38+
group.bench_function("CRC64NVME", |b| b.iter(|| crc64nvme_checksum(&data)));
39+
40+
group.bench_function("SHA1", |b| b.iter(|| sha1_hash(&data)));
41+
42+
group.bench_function("SHA256", |b| b.iter(|| sha256_checksum(&data)));
43+
44+
group.finish();
45+
}
46+
}
47+
48+
criterion_group!(benches, bench_checksums);
49+
criterion_main!(benches);

examples/file_downloader.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
6161
.await?;
6262

6363
get_object
64-
.content()?
64+
.content()
65+
.await?
6566
.to_file(Path::new(download_path))
6667
.await?;
6768

src/s3/builders/append_object.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ pub struct AppendObject {
6464
#[builder(!default)] // force required
6565
/// value of x-amz-write-offset-bytes
6666
offset_bytes: u64,
67+
68+
/// Optional checksum algorithm for data integrity verification during append.
69+
///
70+
/// When specified, computes a checksum of the appended data using the selected algorithm
71+
/// (CRC32, CRC32C, SHA1, SHA256, or CRC64NVME). The checksum is sent with the append
72+
/// operation and verified by the server.
73+
#[builder(default, setter(into))]
74+
checksum_algorithm: Option<crate::s3::utils::ChecksumAlgorithm>,
6775
}
6876

6977
impl S3Api for AppendObject {
@@ -83,6 +91,7 @@ pub type AppendObjectBldr = AppendObjectBuilder<(
8391
(),
8492
(Arc<SegmentedBytes>,),
8593
(u64,),
94+
(),
8695
)>;
8796

8897
impl ToS3Request for AppendObject {
@@ -94,6 +103,25 @@ impl ToS3Request for AppendObject {
94103
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
95104
headers.add(X_AMZ_WRITE_OFFSET_BYTES, self.offset_bytes.to_string());
96105

106+
if let Some(algorithm) = self.checksum_algorithm {
107+
use crate::s3::header_constants::*;
108+
use crate::s3::utils::{ChecksumAlgorithm, compute_checksum};
109+
110+
let data_bytes = self.data.to_bytes();
111+
let checksum_value = compute_checksum(algorithm, &data_bytes);
112+
headers.add(X_AMZ_CHECKSUM_ALGORITHM, algorithm.as_str().to_string());
113+
114+
match algorithm {
115+
ChecksumAlgorithm::CRC32 => headers.add(X_AMZ_CHECKSUM_CRC32, checksum_value),
116+
ChecksumAlgorithm::CRC32C => headers.add(X_AMZ_CHECKSUM_CRC32C, checksum_value),
117+
ChecksumAlgorithm::SHA1 => headers.add(X_AMZ_CHECKSUM_SHA1, checksum_value),
118+
ChecksumAlgorithm::SHA256 => headers.add(X_AMZ_CHECKSUM_SHA256, checksum_value),
119+
ChecksumAlgorithm::CRC64NVME => {
120+
headers.add(X_AMZ_CHECKSUM_CRC64NVME, checksum_value)
121+
}
122+
}
123+
}
124+
97125
Ok(S3Request::builder()
98126
.client(self.client)
99127
.method(Method::PUT)
@@ -144,6 +172,13 @@ pub struct AppendObjectContent {
144172
/// Value of x-amz-write-offset-bytes
145173
#[builder(default)]
146174
offset_bytes: u64,
175+
/// Optional checksum algorithm for data integrity verification during append.
176+
///
177+
/// When specified, computes checksums for appended data using the selected algorithm
178+
/// (CRC32, CRC32C, SHA1, SHA256, or CRC64NVME). The checksum is computed for each
179+
/// chunk and sent with the append operation.
180+
#[builder(default, setter(into))]
181+
checksum_algorithm: Option<crate::s3::utils::ChecksumAlgorithm>,
147182
}
148183

149184
/// Builder type for [`AppendObjectContent`] that is returned by [`MinioClient::append_object_content`](crate::s3::client::MinioClient::append_object_content).
@@ -162,6 +197,7 @@ pub type AppendObjectContentBldr = AppendObjectContentBuilder<(
162197
(),
163198
(),
164199
(),
200+
(),
165201
)>;
166202

167203
impl AppendObjectContent {
@@ -229,6 +265,7 @@ impl AppendObjectContent {
229265
offset_bytes: current_file_size,
230266
sse: self.sse,
231267
data: Arc::new(seg_bytes),
268+
checksum_algorithm: self.checksum_algorithm,
232269
};
233270
ao.send().await
234271
} else if let Some(expected) = object_size.value()
@@ -296,6 +333,7 @@ impl AppendObjectContent {
296333
sse: self.sse.clone(),
297334
data: Arc::new(part_content),
298335
offset_bytes: next_offset_bytes,
336+
checksum_algorithm: self.checksum_algorithm,
299337
};
300338
let resp: AppendObjectResponse = append_object.send().await?;
301339
//println!("AppendObjectResponse: object_size={:?}", resp.object_size);

src/s3/builders/copy_object.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ use crate::s3::response_traits::HasEtagFromBody;
2727
use crate::s3::sse::{Sse, SseCustomerKey};
2828
use crate::s3::types::{Directive, PartInfo, Retention, S3Api, S3Request, ToS3Request};
2929
use crate::s3::utils::{
30-
UtcTime, check_bucket_name, check_object_name, check_sse, check_ssec, to_http_header_value,
31-
to_iso8601utc, url_encode,
30+
ChecksumAlgorithm, UtcTime, check_bucket_name, check_object_name, check_sse, check_ssec,
31+
to_http_header_value, to_iso8601utc, url_encode,
3232
};
3333
use async_recursion::async_recursion;
3434
use http::Method;
@@ -59,6 +59,13 @@ pub struct UploadPartCopy {
5959
part_number: u16,
6060
#[builder(default)]
6161
headers: Multimap,
62+
/// Optional checksum algorithm for data integrity verification during part copy.
63+
///
64+
/// When specified, the server computes a checksum of the copied part data using
65+
/// this algorithm. Use the same algorithm for all parts in a multipart upload.
66+
/// Supported algorithms: CRC32, CRC32C, SHA1, SHA256, CRC64NVME.
67+
#[builder(default, setter(into))]
68+
checksum_algorithm: Option<crate::s3::utils::ChecksumAlgorithm>,
6269
}
6370

6471
impl S3Api for UploadPartCopy {
@@ -78,6 +85,7 @@ pub type UploadPartCopyBldr = UploadPartCopyBuilder<(
7885
(String,),
7986
(),
8087
(),
88+
(),
8189
)>;
8290

8391
impl ToS3Request for UploadPartCopy {
@@ -100,6 +108,10 @@ impl ToS3Request for UploadPartCopy {
100108
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
101109
headers.add_multimap(self.headers);
102110

111+
if let Some(algorithm) = self.checksum_algorithm {
112+
headers.add(X_AMZ_CHECKSUM_ALGORITHM, algorithm.as_str().to_string());
113+
}
114+
103115
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
104116
{
105117
query_params.add("partNumber", self.part_number.to_string());
@@ -150,6 +162,8 @@ pub struct CopyObjectInternal {
150162
metadata_directive: Option<Directive>,
151163
#[builder(default, setter(into))]
152164
tagging_directive: Option<Directive>,
165+
#[builder(default, setter(into))]
166+
checksum_algorithm: Option<crate::s3::utils::ChecksumAlgorithm>,
153167
}
154168

155169
impl S3Api for CopyObjectInternal {
@@ -175,6 +189,7 @@ pub type CopyObjectInternalBldr = CopyObjectInternalBuilder<(
175189
(),
176190
(),
177191
(),
192+
(),
178193
)>;
179194

180195
impl ToS3Request for CopyObjectInternal {
@@ -261,6 +276,10 @@ impl ToS3Request for CopyObjectInternal {
261276
if let Some(v) = self.source.ssec {
262277
headers.add_multimap(v.copy_headers());
263278
}
279+
280+
if let Some(algorithm) = self.checksum_algorithm {
281+
headers.add(X_AMZ_CHECKSUM_ALGORITHM, algorithm.as_str().to_string());
282+
}
264283
};
265284

266285
Ok(S3Request::builder()
@@ -310,6 +329,13 @@ pub struct CopyObject {
310329
metadata_directive: Option<Directive>,
311330
#[builder(default, setter(into))]
312331
tagging_directive: Option<Directive>,
332+
/// Optional checksum algorithm for data integrity verification during copy.
333+
///
334+
/// When specified, the server computes a checksum of the destination object using
335+
/// this algorithm during the copy operation. Supported algorithms: CRC32, CRC32C,
336+
/// SHA1, SHA256, CRC64NVME. The checksum value is included in response headers for verification.
337+
#[builder(default, setter(into))]
338+
checksum_algorithm: Option<crate::s3::utils::ChecksumAlgorithm>,
313339
}
314340

315341
/// Builder type for [`CopyObject`] that is returned by [`MinioClient::copy_object`](crate::s3::client::MinioClient::copy_object).
@@ -331,6 +357,7 @@ pub type CopyObjectBldr = CopyObjectBuilder<(
331357
(),
332358
(),
333359
(),
360+
(),
334361
)>;
335362

336363
impl CopyObject {
@@ -434,6 +461,7 @@ impl CopyObject {
434461
.source(self.source)
435462
.metadata_directive(self.metadata_directive)
436463
.tagging_directive(self.tagging_directive)
464+
.checksum_algorithm(self.checksum_algorithm)
437465
.build()
438466
.send()
439467
.await?;
@@ -629,6 +657,10 @@ impl ComposeObjectInternal {
629657
number: part_number,
630658
etag,
631659
size,
660+
checksum_crc32: resp.get_checksum(ChecksumAlgorithm::CRC32),
661+
checksum_crc32c: resp.get_checksum(ChecksumAlgorithm::CRC32C),
662+
checksum_sha1: resp.get_checksum(ChecksumAlgorithm::SHA1),
663+
checksum_sha256: resp.get_checksum(ChecksumAlgorithm::SHA256),
632664
});
633665
} else {
634666
while size > 0 {
@@ -669,6 +701,10 @@ impl ComposeObjectInternal {
669701
number: part_number,
670702
etag,
671703
size,
704+
checksum_crc32: resp.get_checksum(ChecksumAlgorithm::CRC32),
705+
checksum_crc32c: resp.get_checksum(ChecksumAlgorithm::CRC32C),
706+
checksum_sha1: resp.get_checksum(ChecksumAlgorithm::SHA1),
707+
checksum_sha256: resp.get_checksum(ChecksumAlgorithm::SHA256),
672708
});
673709

674710
offset += length;

0 commit comments

Comments
 (0)