use azure_core::{json, time::OffsetDateTime, xml};
use criterion::{criterion_group, criterion_main, Criterion};
use models::ListBlobsFlatSegmentResponse;
use std::hint::black_box;
use crate::models::{
AccessTier, BlobItemInternal, BlobName, BlobPropertiesInternal, BlobType, LeaseState,
LeaseStatus,
};
const ITEM_COUNT: [usize; 3] = [5, 25, 100];
fn deserialize_list_blobs(c: &mut Criterion) {
for count in ITEM_COUNT {
let mut blobs = ListBlobsFlatSegmentResponse {
service_endpoint: Some("https://t0123456789abcdefprim.blob.core.windows.net/".into()),
container_name: Some("container01234567".into()),
..Default::default()
};
let now = OffsetDateTime::now_utc();
for i in 0..count {
let blob = BlobItemInternal {
name: Some(BlobName {
content: Some(format!("testBlob{i}")),
..Default::default()
}),
properties: Some(BlobPropertiesInternal {
creation_time: Some(now),
last_modified: Some(now),
etag: Some(format!("{i:#15x}")),
content_length: Some(17),
content_type: Some("application/x-octet-stream".into()),
content_md5: Some([i as u8].to_vec()),
blob_type: Some(BlobType::BlockBlob),
access_tier: Some(AccessTier::Hot),
access_tier_inferred: Some(true),
lease_status: Some(LeaseStatus::Unlocked),
lease_state: Some(LeaseState::Available),
server_encrypted: Some(true),
..Default::default()
}),
..Default::default()
};
blobs.segment.blob_items.push(blob);
}
let blobs_json = json::to_json(&blobs).expect("serialize json");
let blobs_xml = xml::to_xml(&blobs).expect("serialize xml");
let mut group = c.benchmark_group(format!("deserialize_list_blobs/{count}"));
group.bench_function("json", |b| {
b.iter(|| {
let _: ListBlobsFlatSegmentResponse =
black_box(json::from_json(&blobs_json).expect("deserialize json"));
});
});
group.bench_function("xml", |b| {
b.iter(|| {
let _: ListBlobsFlatSegmentResponse =
black_box(xml::from_xml(&blobs_xml).expect("deserialize xml"));
});
});
group.finish();
}
}
criterion_group! {
name = benchmarks;
config = Criterion::default();
targets = deserialize_list_blobs
}
criterion_main!(benchmarks);
mod models {
use azure_core::{
error::{Error, ErrorKind},
time::OffsetDateTime,
Value,
};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{convert::Infallible, str::FromStr};
#[derive(Clone, Default, Deserialize, Debug, Serialize)]
#[non_exhaustive]
#[serde(rename = "EnumerationResults")]
pub struct ListBlobsFlatSegmentResponse {
#[serde(rename = "@ContainerName", skip_serializing_if = "Option::is_none")]
pub container_name: Option<String>,
#[serde(rename = "Marker", skip_serializing_if = "Option::is_none")]
pub marker: Option<String>,
#[serde(rename = "MaxResults", skip_serializing_if = "Option::is_none")]
pub max_results: Option<i32>,
#[serde(rename = "NextMarker", skip_serializing_if = "Option::is_none")]
pub next_marker: Option<String>,
#[serde(rename = "Prefix", skip_serializing_if = "Option::is_none")]
pub prefix: Option<String>,
#[serde(default, rename = "Blobs")]
pub segment: BlobFlatListSegment,
#[serde(rename = "@ServiceEndpoint", skip_serializing_if = "Option::is_none")]
pub service_endpoint: Option<String>,
}
#[derive(Clone, Default, Deserialize, Debug, Serialize)]
#[non_exhaustive]
pub struct BlobFlatListSegment {
#[serde(default, rename = "Blob")]
pub blob_items: Vec<BlobItemInternal>,
}
#[derive(Clone, Default, Deserialize, Debug, Serialize)]
#[non_exhaustive]
#[serde(rename = "Blob")]
pub struct BlobItemInternal {
#[serde(rename = "BlobTags", skip_serializing_if = "Option::is_none")]
pub blob_tags: Option<Value>,
#[serde(rename = "Deleted", skip_serializing_if = "Option::is_none")]
pub deleted: Option<bool>,
#[serde(rename = "HasVersionsOnly", skip_serializing_if = "Option::is_none")]
pub has_versions_only: Option<bool>,
#[serde(rename = "IsCurrentVersion", skip_serializing_if = "Option::is_none")]
pub is_current_version: Option<bool>,
#[serde(rename = "Metadata", skip_serializing_if = "Option::is_none")]
pub metadata: Option<Value>,
#[serde(rename = "Name", skip_serializing_if = "Option::is_none")]
pub name: Option<BlobName>,
#[serde(rename = "OrMetadata", skip_serializing_if = "Option::is_none")]
pub object_replication_metadata: Option<Value>,
#[serde(rename = "Properties", skip_serializing_if = "Option::is_none")]
pub properties: Option<BlobPropertiesInternal>,
#[serde(rename = "Snapshot", skip_serializing_if = "Option::is_none")]
pub snapshot: Option<String>,
#[serde(rename = "VersionId", skip_serializing_if = "Option::is_none")]
pub version_id: Option<String>,
}
#[derive(Clone, Default, Deserialize, Debug, Serialize)]
#[non_exhaustive]
#[serde(rename = "Properties")]
pub struct BlobPropertiesInternal {
#[serde(rename = "AccessTier", skip_serializing_if = "Option::is_none")]
pub access_tier: Option<AccessTier>,
#[serde(
default,
rename = "AccessTierChangeTime",
skip_serializing_if = "Option::is_none",
with = "azure_core::time::rfc7231::option"
)]
pub access_tier_change_time: Option<OffsetDateTime>,
#[serde(rename = "AccessTierInferred", skip_serializing_if = "Option::is_none")]
pub access_tier_inferred: Option<bool>,
#[serde(rename = "ArchiveStatus", skip_serializing_if = "Option::is_none")]
pub archive_status: Option<Value>,
#[serde(
rename = "x-ms-blob-sequence-number",
skip_serializing_if = "Option::is_none"
)]
pub blob_sequence_number: Option<i64>,
#[serde(rename = "BlobType", skip_serializing_if = "Option::is_none")]
pub blob_type: Option<BlobType>,
#[serde(rename = "Cache-Control", skip_serializing_if = "Option::is_none")]
pub cache_control: Option<String>,
#[serde(
rename = "Content-Disposition",
skip_serializing_if = "Option::is_none"
)]
pub content_disposition: Option<String>,
#[serde(rename = "Content-Encoding", skip_serializing_if = "Option::is_none")]
pub content_encoding: Option<String>,
#[serde(rename = "Content-Language", skip_serializing_if = "Option::is_none")]
pub content_language: Option<String>,
#[serde(rename = "Content-Length", skip_serializing_if = "Option::is_none")]
pub content_length: Option<u64>,
#[serde(
default,
deserialize_with = "azure_core::base64::option::deserialize",
rename = "Content-MD5",
serialize_with = "azure_core::base64::option::serialize",
skip_serializing_if = "Option::is_none"
)]
pub content_md5: Option<Vec<u8>>,
#[serde(rename = "Content-Type", skip_serializing_if = "Option::is_none")]
pub content_type: Option<String>,
#[serde(
default,
rename = "CopyCompletionTime",
skip_serializing_if = "Option::is_none",
with = "azure_core::time::rfc7231::option"
)]
pub copy_completion_time: Option<OffsetDateTime>,
#[serde(rename = "CopyId", skip_serializing_if = "Option::is_none")]
pub copy_id: Option<String>,
#[serde(rename = "CopyProgress", skip_serializing_if = "Option::is_none")]
pub copy_progress: Option<String>,
#[serde(rename = "CopySource", skip_serializing_if = "Option::is_none")]
pub copy_source: Option<String>,
#[serde(rename = "CopyStatus", skip_serializing_if = "Option::is_none")]
pub copy_status: Option<Value>,
#[serde(
rename = "CopyStatusDescription",
skip_serializing_if = "Option::is_none"
)]
pub copy_status_description: Option<String>,
#[serde(
default,
rename = "Creation-Time",
skip_serializing_if = "Option::is_none",
with = "azure_core::time::rfc7231::option"
)]
pub creation_time: Option<OffsetDateTime>,
#[serde(
rename = "CustomerProvidedKeySha256",
skip_serializing_if = "Option::is_none"
)]
pub customer_provided_key_sha256: Option<String>,
#[serde(
default,
rename = "DeletedTime",
skip_serializing_if = "Option::is_none",
with = "azure_core::time::rfc7231::option"
)]
pub deleted_time: Option<OffsetDateTime>,
#[serde(
rename = "DestinationSnapshot",
skip_serializing_if = "Option::is_none"
)]
pub destination_snapshot: Option<String>,
#[serde(rename = "EncryptionScope", skip_serializing_if = "Option::is_none")]
pub encryption_scope: Option<String>,
#[serde(rename = "Etag", skip_serializing_if = "Option::is_none")]
pub etag: Option<String>,
#[serde(
default,
rename = "Expiry-Time",
skip_serializing_if = "Option::is_none",
with = "azure_core::time::rfc7231::option"
)]
pub expires_on: Option<OffsetDateTime>,
#[serde(
default,
rename = "ImmutabilityPolicyUntilDate",
skip_serializing_if = "Option::is_none",
with = "azure_core::time::rfc7231::option"
)]
pub immutability_policy_expires_on: Option<OffsetDateTime>,
#[serde(
rename = "ImmutabilityPolicyMode",
skip_serializing_if = "Option::is_none"
)]
pub immutability_policy_mode: Option<Value>,
#[serde(rename = "IncrementalCopy", skip_serializing_if = "Option::is_none")]
pub incremental_copy: Option<bool>,
#[serde(rename = "Sealed", skip_serializing_if = "Option::is_none")]
pub is_sealed: Option<bool>,
#[serde(
default,
rename = "LastAccessTime",
skip_serializing_if = "Option::is_none",
with = "azure_core::time::rfc7231::option"
)]
pub last_accessed_on: Option<OffsetDateTime>,
#[serde(
default,
rename = "Last-Modified",
skip_serializing_if = "Option::is_none",
with = "azure_core::time::rfc7231::option"
)]
pub last_modified: Option<OffsetDateTime>,
#[serde(rename = "LeaseDuration", skip_serializing_if = "Option::is_none")]
pub lease_duration: Option<Value>,
#[serde(rename = "LeaseState", skip_serializing_if = "Option::is_none")]
pub lease_state: Option<LeaseState>,
#[serde(rename = "LeaseStatus", skip_serializing_if = "Option::is_none")]
pub lease_status: Option<LeaseStatus>,
#[serde(rename = "LegalHold", skip_serializing_if = "Option::is_none")]
pub legal_hold: Option<bool>,
#[serde(rename = "RehydratePriority", skip_serializing_if = "Option::is_none")]
pub rehydrate_priority: Option<Value>,
#[serde(
rename = "RemainingRetentionDays",
skip_serializing_if = "Option::is_none"
)]
pub remaining_retention_days: Option<i32>,
#[serde(rename = "ServerEncrypted", skip_serializing_if = "Option::is_none")]
pub server_encrypted: Option<bool>,
#[serde(rename = "TagCount", skip_serializing_if = "Option::is_none")]
pub tag_count: Option<i32>,
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[non_exhaustive]
pub enum AccessTier {
Archive,
Cold,
Cool,
Hot,
P10,
P15,
P20,
P30,
P4,
P40,
P50,
P6,
P60,
P70,
P80,
Premium,
UnknownValue(String),
}
impl FromStr for AccessTier {
type Err = Infallible;
fn from_str(s: &str) -> ::core::result::Result<Self, <Self as FromStr>::Err> {
Ok(match s {
"Archive" => AccessTier::Archive,
"Cold" => AccessTier::Cold,
"Cool" => AccessTier::Cool,
"Hot" => AccessTier::Hot,
"P10" => AccessTier::P10,
"P15" => AccessTier::P15,
"P20" => AccessTier::P20,
"P30" => AccessTier::P30,
"P4" => AccessTier::P4,
"P40" => AccessTier::P40,
"P50" => AccessTier::P50,
"P6" => AccessTier::P6,
"P60" => AccessTier::P60,
"P70" => AccessTier::P70,
"P80" => AccessTier::P80,
"Premium" => AccessTier::Premium,
_ => AccessTier::UnknownValue(s.to_string()),
})
}
}
impl AsRef<str> for AccessTier {
fn as_ref(&self) -> &str {
match self {
AccessTier::Archive => "Archive",
AccessTier::Cold => "Cold",
AccessTier::Cool => "Cool",
AccessTier::Hot => "Hot",
AccessTier::P10 => "P10",
AccessTier::P15 => "P15",
AccessTier::P20 => "P20",
AccessTier::P30 => "P30",
AccessTier::P4 => "P4",
AccessTier::P40 => "P40",
AccessTier::P50 => "P50",
AccessTier::P6 => "P6",
AccessTier::P60 => "P60",
AccessTier::P70 => "P70",
AccessTier::P80 => "P80",
AccessTier::Premium => "Premium",
AccessTier::UnknownValue(s) => s.as_str(),
}
}
}
impl<'de> Deserialize<'de> for AccessTier {
fn deserialize<D>(deserializer: D) -> ::core::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}
impl Serialize for AccessTier {
fn serialize<S>(&self, s: S) -> ::core::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(self.as_ref())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[allow(clippy::enum_variant_names)]
#[non_exhaustive]
pub enum BlobType {
AppendBlob,
BlockBlob,
PageBlob,
}
impl FromStr for BlobType {
type Err = Error;
fn from_str(s: &str) -> ::core::result::Result<Self, <Self as FromStr>::Err> {
Ok(match s {
"AppendBlob" => BlobType::AppendBlob,
"BlockBlob" => BlobType::BlockBlob,
"PageBlob" => BlobType::PageBlob,
_ => {
return Err(Error::with_message_fn(ErrorKind::DataConversion, || {
format!("unknown variant of BlobType found: \"{s}\"")
}))
}
})
}
}
impl AsRef<str> for BlobType {
fn as_ref(&self) -> &str {
match self {
BlobType::AppendBlob => "AppendBlob",
BlobType::BlockBlob => "BlockBlob",
BlobType::PageBlob => "PageBlob",
}
}
}
impl<'de> Deserialize<'de> for BlobType {
fn deserialize<D>(deserializer: D) -> ::core::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}
impl Serialize for BlobType {
fn serialize<S>(&self, s: S) -> ::core::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(self.as_ref())
}
}
#[derive(Clone, Default, Deserialize, Debug, Serialize)]
#[non_exhaustive]
pub struct BlobName {
#[serde(rename = "$text", skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(rename = "@Encoded", skip_serializing_if = "Option::is_none")]
pub encoded: Option<bool>,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub enum LeaseState {
Available,
Breaking,
Broken,
Expired,
Leased,
}
impl FromStr for LeaseState {
type Err = Error;
fn from_str(s: &str) -> ::core::result::Result<Self, <Self as FromStr>::Err> {
Ok(match s {
"available" => LeaseState::Available,
"breaking" => LeaseState::Breaking,
"broken" => LeaseState::Broken,
"expired" => LeaseState::Expired,
"leased" => LeaseState::Leased,
_ => {
return Err(Error::with_message_fn(ErrorKind::DataConversion, || {
format!("unknown variant of LeaseState found: \"{s}\"")
}))
}
})
}
}
impl AsRef<str> for LeaseState {
fn as_ref(&self) -> &str {
match self {
LeaseState::Available => "available",
LeaseState::Breaking => "breaking",
LeaseState::Broken => "broken",
LeaseState::Expired => "expired",
LeaseState::Leased => "leased",
}
}
}
impl<'de> Deserialize<'de> for LeaseState {
fn deserialize<D>(deserializer: D) -> ::core::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}
impl Serialize for LeaseState {
fn serialize<S>(&self, s: S) -> ::core::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(self.as_ref())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub enum LeaseStatus {
Locked,
Unlocked,
}
impl FromStr for LeaseStatus {
type Err = Error;
fn from_str(s: &str) -> ::core::result::Result<Self, <Self as FromStr>::Err> {
Ok(match s {
"locked" => LeaseStatus::Locked,
"unlocked" => LeaseStatus::Unlocked,
_ => {
return Err(Error::with_message_fn(ErrorKind::DataConversion, || {
format!("unknown variant of LeaseStatus found: \"{s}\"")
}))
}
})
}
}
impl AsRef<str> for LeaseStatus {
fn as_ref(&self) -> &str {
match self {
LeaseStatus::Locked => "locked",
LeaseStatus::Unlocked => "unlocked",
}
}
}
impl<'de> Deserialize<'de> for LeaseStatus {
fn deserialize<D>(deserializer: D) -> ::core::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}
impl Serialize for LeaseStatus {
fn serialize<S>(&self, s: S) -> ::core::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(self.as_ref())
}
}
}