Skip to content

Commit

Permalink
Added support for x-ms-blob-content-md5 in PutBlockList (#148)
Browse files Browse the repository at this point in the history
* Added support for x-ms-blob-content-md5 in PutBlockList

* removed borrow in favor of Bytes

* BlobBlockType now accepts BlockId

* Implemented suggestions from rylev and ctaggart
  • Loading branch information
MindFlavor authored Jan 19, 2021
1 parent 793878d commit 0b95c40
Show file tree
Hide file tree
Showing 16 changed files with 155 additions and 161 deletions.
44 changes: 36 additions & 8 deletions sdk/storage/examples/blob_04.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use azure_core::prelude::*;
use azure_storage::blob::prelude::*;
use azure_storage::core::prelude::*;
use bytes::{BufMut, Bytes};
use std::error::Error;
use std::sync::Arc;

Expand All @@ -27,15 +28,29 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
.as_container_client(&container_name)
.as_blob_client("test1");

let data = b"1337 azure blob test";
// this example fills a 1 KB file with ASCII text and
// sends it in chunks of 256 bytes (4 chunks).
// It then finalizes the block blob by calling
// PutBlockList. Finally it gets back
// the blob as a whole.
let mut data = bytes::BytesMut::with_capacity(1 * 1024);
for _ in 0..1 * (1024 / 64) {
data.put("the brown fox jumped over the lazy dog. 123456789Pangram12345678".as_bytes());
}
let data = data.freeze();

println!("data to send is {} bytes.", data.len());

let mut block_ids = Vec::new();
for (i, block) in data.chunks(64 * 1024 * 1024 /* 64 MiB */).enumerate() {
block_ids.push(i.to_be_bytes());
let hash = md5::compute(block).into();
let block_id = (&i.to_be_bytes() as &[u8]).into();
for i in 0..(1024 / 256) {
let slice = data.slice(i * 256..(i + 1) * 256);

let block_id = Bytes::from(format!("{}", i));
block_ids.push(block_id.clone());
let hash = md5::compute(slice.clone()).into();

let put_block_response = blob
.put_block(&block_id, block)
.put_block(block_id, slice)
.hash(&hash)
.execute()
.await?;
Expand All @@ -44,9 +59,22 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
}

let mut block_list = BlockList::default();
for id in block_ids.iter() {
block_list.blocks.push(BlobBlockType::Uncommitted(&id[..]));
for id in block_ids.into_iter() {
block_list.blocks.push(BlobBlockType::new_uncommitted(id));
}

let res = blob
.put_block_list(&block_list)
.content_md5(md5::compute(data))
.execute()
.await?;
println!("PutBlockList == {:?}", res);

let retrieved_blob = blob.get().execute().await?;
println!("retrieved_blob == {:?}", retrieved_blob);

let s = String::from_utf8(retrieved_blob.data)?;
println!("retrieved contents == {}", s);

Ok(())
}
14 changes: 4 additions & 10 deletions sdk/storage/examples/put_block_blob_00.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,15 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let mut block_list = BlockList::default();
block_list
.blocks
.push(BlobBlockType::Uncommitted(b"satanasso" as &[u8]));
.push(BlobBlockType::new_uncommitted("satanasso"));
block_list
.blocks
.push(BlobBlockType::Uncommitted(b"pollastro" as &[u8]));
.push(BlobBlockType::new_uncommitted("pollastro"));

let res = blob
.put_block(&("satanasso".into()), data.clone())
.execute()
.await?;
let res = blob.put_block("satanasso", data.clone()).execute().await?;
println!("2-put_block {:?}", res);

let res = blob
.put_block(&("pollastro".into()), data)
.execute()
.await?;
let res = blob.put_block("pollastro", data).execute().await?;
println!("3-put_block {:?}", res);

let ret = blob
Expand Down
29 changes: 19 additions & 10 deletions sdk/storage/src/blob/blob/blob_block_type.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
use std::borrow::Borrow;
use crate::BlockId;

// TODO: Change from Borrow<[u8]> to BlockId
// Also, why Borrow? :| :|
#[derive(Debug, Clone, PartialEq)]
pub enum BlobBlockType<T>
where
T: Borrow<[u8]>,
{
Committed(T),
Uncommitted(T),
Latest(T),
pub enum BlobBlockType {
Committed(BlockId),
Uncommitted(BlockId),
Latest(BlockId),
}

impl BlobBlockType {
pub fn new_committed(b: impl Into<BlockId>) -> Self {
BlobBlockType::Committed(b.into())
}

pub fn new_uncommitted(b: impl Into<BlockId>) -> Self {
BlobBlockType::Uncommitted(b.into())
}

pub fn new_latest(b: impl Into<BlockId>) -> Self {
BlobBlockType::Latest(b.into())
}
}
8 changes: 2 additions & 6 deletions sdk/storage/src/blob/blob/blob_block_with_size.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
use crate::blob::blob::BlobBlockType;
use std::borrow::Borrow;

#[derive(Debug, Clone, PartialEq)]
pub struct BlobBlockWithSize<T>
where
T: Borrow<[u8]>,
{
pub block_list_type: BlobBlockType<T>,
pub struct BlobBlockWithSize {
pub block_list_type: BlobBlockType,
pub size_in_bytes: u64,
}
63 changes: 18 additions & 45 deletions sdk/storage/src/blob/blob/block_list.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,12 @@
use crate::blob::blob::{BlobBlockType, BlockWithSizeList};
use std::borrow::Borrow;

#[derive(Default, Debug, Clone, PartialEq)]
pub struct BlockList<T>
where
T: Borrow<[u8]>,
{
pub blocks: Vec<BlobBlockType<T>>,
pub struct BlockList {
pub blocks: Vec<BlobBlockType>,
}

impl<'a> BlockList<&'a [u8]> {
pub fn to_owned(&self) -> BlockList<Vec<u8>> {
let mut bl: BlockList<Vec<u8>> = BlockList {
blocks: Vec::with_capacity(self.blocks.len()),
};

for entry in &self.blocks {
bl.blocks.push(match entry {
BlobBlockType::Committed(id) => BlobBlockType::Committed(id.to_vec()),
BlobBlockType::Uncommitted(id) => BlobBlockType::Uncommitted(id.to_vec()),
BlobBlockType::Latest(id) => BlobBlockType::Latest(id.to_vec()),
});
}

bl
}
}

impl<T> From<BlockWithSizeList<T>> for BlockList<T>
where
T: Borrow<[u8]> + Default,
{
fn from(b: BlockWithSizeList<T>) -> BlockList<T> {
impl From<BlockWithSizeList> for BlockList {
fn from(b: BlockWithSizeList) -> BlockList {
let mut bl = BlockList::default();
for block in b.blocks {
bl.blocks.push(block.block_list_type);
Expand All @@ -40,25 +15,24 @@ where
}
}

impl<T> BlockList<T>
where
T: Borrow<[u8]>,
{
impl BlockList {
pub fn to_xml(&self) -> String {
let mut s = String::new();
s.push_str("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<BlockList>\n");
for bl in &self.blocks {
let node = match bl {
BlobBlockType::Committed(content) => format!(
"\t<Committed>{}</Committed>\n",
base64::encode(content.borrow())
),
BlobBlockType::Committed(content) => {
format!(
"\t<Committed>{}</Committed>\n",
base64::encode(content.as_ref())
)
}
BlobBlockType::Uncommitted(content) => format!(
"\t<Uncommitted>{}</Uncommitted>\n",
base64::encode(content.borrow())
base64::encode(content.as_ref())
),
BlobBlockType::Latest(content) => {
format!("\t<Latest>{}</Latest>\n", base64::encode(content.borrow()))
format!("\t<Latest>{}</Latest>\n", base64::encode(content.as_ref()))
}
};

Expand All @@ -73,22 +47,21 @@ where
#[cfg(test)]
mod test {
use super::*;
use bytes::Bytes;

#[test]
fn to_xml() {
let mut blocks = BlockList { blocks: Vec::new() };
blocks
.blocks
.push(BlobBlockType::Committed(Vec::from(b"numero1" as &[u8])));
blocks
.blocks
.push(BlobBlockType::Uncommitted(Vec::from(b"numero2" as &[u8])));
.push(BlobBlockType::new_committed(Bytes::from_static(b"numero1")));
blocks
.blocks
.push(BlobBlockType::Uncommitted(Vec::from(b"numero3" as &[u8])));
.push(BlobBlockType::new_uncommitted("numero2"));
blocks
.blocks
.push(BlobBlockType::Latest(Vec::from(b"numero4" as &[u8])));
.push(BlobBlockType::new_uncommitted("numero3"));
blocks.blocks.push(BlobBlockType::new_latest("numero4"));

let _retu: &str = &blocks.to_xml();

Expand Down
25 changes: 10 additions & 15 deletions sdk/storage/src/blob/blob/block_with_size_list.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::blob::blob::BlobBlockType;
use crate::blob::blob::BlobBlockWithSize;
use azure_core::errors::AzureError;
use std::borrow::Borrow;

#[derive(Debug, Deserialize)]
struct Name {
Expand Down Expand Up @@ -38,15 +37,12 @@ struct BlockList {
}

#[derive(Default, Debug, Clone, PartialEq)]
pub struct BlockWithSizeList<T>
where
T: Borrow<[u8]>,
{
pub blocks: Vec<BlobBlockWithSize<T>>,
pub struct BlockWithSizeList {
pub blocks: Vec<BlobBlockWithSize>,
}

impl BlockWithSizeList<Vec<u8>> {
pub fn try_from(xml: &str) -> Result<BlockWithSizeList<Vec<u8>>, AzureError> {
impl BlockWithSizeList {
pub fn try_from_xml(xml: &str) -> Result<Self, AzureError> {
let bl: BlockList = serde_xml_rs::de::from_reader(xml.as_bytes())?;
debug!("bl == {:?}", bl);

Expand All @@ -56,7 +52,7 @@ impl BlockWithSizeList<Vec<u8>> {
for b_val in b {
lbs.blocks.push(BlobBlockWithSize {
block_list_type: BlobBlockType::Committed(
base64::decode(&b_val.name.value)?.to_owned(),
base64::decode(&b_val.name.value)?.into(),
),
size_in_bytes: b_val.size.value,
});
Expand All @@ -67,7 +63,7 @@ impl BlockWithSizeList<Vec<u8>> {
for b_val in b {
lbs.blocks.push(BlobBlockWithSize {
block_list_type: BlobBlockType::Uncommitted(
base64::decode(&b_val.name.value)?.to_owned(),
base64::decode(&b_val.name.value)?.into(),
),
size_in_bytes: b_val.size.value,
});
Expand Down Expand Up @@ -100,16 +96,15 @@ mod test {
</UncommittedBlocks>
</BlockList> ";

let bl = BlockWithSizeList::try_from(range).unwrap();
let bl = BlockWithSizeList::try_from_xml(range).unwrap();
assert!(bl.blocks.len() == 2);
assert!(bl.blocks[0].size_in_bytes == 200);
assert!(bl.blocks[1].size_in_bytes == 4096);

assert!(
bl.blocks[0].block_list_type
== BlobBlockType::Committed(Vec::from(b"base64-encoded-block-id" as &[u8]))
bl.blocks[0].block_list_type == BlobBlockType::new_committed("base64-encoded-block-id")
);
let b2 = BlobBlockType::Uncommitted(Vec::from(b"base64-encoded-block-id-number2" as &[u8]));
let b2 = BlobBlockType::new_uncommitted("base64-encoded-block-id-number2");
assert!(
bl.blocks[1].block_list_type == b2,
"bl.blocks[1].block_list_type == {:?}, b2 == {:?}",
Expand All @@ -122,7 +117,7 @@ mod test {
fn try_parse2() {
let range = "<?xml version=\"1.0\" encoding=\"utf-8\"?><BlockList><CommittedBlocks /><UncommittedBlocks><Block><Name>YmxvY2sx</Name><Size>62</Size></Block><Block><Name>YmxvY2sy</Name><Size>62</Size></Block><Block><Name>YmxvY2sz</Name><Size>62</Size></Block></UncommittedBlocks></BlockList>";

let bl = BlockWithSizeList::try_from(range).unwrap();
let bl = BlockWithSizeList::try_from_xml(range).unwrap();
assert!(bl.blocks.len() == 3);
assert!(bl.blocks[0].size_in_bytes == 62);
assert!(bl.blocks[1].size_in_bytes == 62);
Expand Down
20 changes: 0 additions & 20 deletions sdk/storage/src/blob/blob/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ use azure_core::{
use chrono::{DateTime, Utc};
use hyper::header;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use std::borrow::Borrow;
use std::collections::HashMap;
use std::str::FromStr;
use xml::Element;
Expand Down Expand Up @@ -66,25 +65,6 @@ pub trait BlockListTypeRequired {
}
}

pub trait BlockListSupport<'a, T>
where
T: Borrow<[u8]>,
{
type O;
fn with_block_list(self, _: &'a BlockList<T>) -> Self::O;
}

pub trait BlockListRequired<'a, T>
where
T: Borrow<[u8]> + 'a,
{
fn block_list(&self) -> &'a BlockList<T>;

fn to_string(&self) -> String {
self.block_list().to_xml()
}
}

create_enum!(
BlobType,
(BlockBlob, "BlockBlob"),
Expand Down
6 changes: 3 additions & 3 deletions sdk/storage/src/blob/blob/requests/put_block_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use bytes::Bytes;
#[derive(Debug, Clone)]
pub struct PutBlockBuilder<'a> {
blob_client: &'a BlobClient,
block_id: &'a BlockId,
block_id: BlockId,
body: Bytes,
hash: Option<&'a Hash>,
client_request_id: Option<ClientRequestId<'a>>,
Expand All @@ -19,12 +19,12 @@ pub struct PutBlockBuilder<'a> {
impl<'a> PutBlockBuilder<'a> {
pub(crate) fn new(
blob_client: &'a BlobClient,
block_id: &'a BlockId,
block_id: impl Into<BlockId>,
body: impl Into<Bytes>,
) -> Self {
Self {
blob_client,
block_id,
block_id: block_id.into(),
body: body.into(),
hash: None,
client_request_id: None,
Expand Down
Loading

0 comments on commit 0b95c40

Please sign in to comment.