bucky_objects/objects/file.rs
1use crate::*;
2
3use std::convert::TryFrom;
4
5#[derive(Clone, Debug)]
6pub struct FileDescContent {
7 len: u64,
8 hash: HashValue,
9}
10
11impl FileDescContent {
12 pub fn new(len: u64, hash: HashValue) -> Self {
13 Self { len, hash }
14 }
15
16 pub fn len(&self) -> u64 {
17 self.len
18 }
19
20 pub fn hash(&self) -> &HashValue {
21 &self.hash
22 }
23}
24
25impl DescContent for FileDescContent {
26 fn obj_type() -> u16 {
27 ObjectTypeCode::File.into()
28 }
29
30 type OwnerType = Option<ObjectId>;
31 type AreaType = SubDescNone;
32 type AuthorType = Option<ObjectId>;
33 type PublicKeyType = SubDescNone;
34}
35
36impl RawEncode for FileDescContent {
37 fn raw_measure(&self, purpose: &Option<RawEncodePurpose>) -> BuckyResult<usize> {
38 let size =
39 0 + self.len.raw_measure(purpose).map_err(|e| {
40 log::error!("FileDescContent::raw_measure/len error:{}", e);
41 e
42 })? + self.hash.raw_measure(purpose).map_err(|e| {
43 log::error!("FileDescContent::raw_measure/hash error:{}", e);
44 e
45 })?;
46 Ok(size)
47 }
48
49 fn raw_encode<'a>(
50 &self,
51 buf: &'a mut [u8],
52 purpose: &Option<RawEncodePurpose>,
53 ) -> BuckyResult<&'a mut [u8]> {
54 let size = self.raw_measure(purpose).unwrap();
55 if buf.len() < size {
56 return Err(BuckyError::new(
57 BuckyErrorCode::OutOfLimit,
58 "[raw_encode] not enough buffer for FileDescContent",
59 ));
60 }
61
62 let buf = self.len.raw_encode(buf, purpose).map_err(|e| {
63 log::error!("FileDescContent::raw_encode/len error:{}", e);
64 e
65 })?;
66
67 let buf = self.hash.raw_encode(buf, purpose).map_err(|e| {
68 log::error!("FileDescContent::raw_encode/hash error:{}", e);
69 e
70 })?;
71 Ok(buf)
72 }
73}
74
75impl<'de> RawDecode<'de> for FileDescContent {
76 fn raw_decode(buf: &'de [u8]) -> BuckyResult<(Self, &'de [u8])> {
77 let (len, buf) = u64::raw_decode(buf).map_err(|e| {
78 log::error!("FileDescContent::raw_decode/len error:{}", e);
79 e
80 })?;
81
82 let (hash, buf) = HashValue::raw_decode(buf).map_err(|e| {
83 log::error!("FileDescContent::raw_decode/hash error:{}", e);
84 e
85 })?;
86
87 Ok((Self { len, hash }, buf))
88 }
89}
90
91#[derive(Debug, Clone)]
92pub enum ChunkBundleHashMethod {
93 // calc hash in list seq order
94 Serial = 0,
95}
96
97impl ChunkBundleHashMethod {
98 pub fn as_str(&self) -> &str {
99 match *self {
100 Self::Serial => "serial",
101 }
102 }
103}
104
105#[derive(Debug, Clone)]
106pub struct ChunkBundle{
107 list: Vec<ChunkId>,
108 hash_method: ChunkBundleHashMethod,
109}
110
111impl ChunkBundle {
112 pub fn new(list: Vec<ChunkId>, hash_method: ChunkBundleHashMethod) -> Self {
113 Self {
114 list,
115 hash_method,
116 }
117 }
118
119 pub fn len(&self) -> u64 {
120 self.list.iter().fold(0u64, |acc, id| {
121 acc + id.len() as u64
122 })
123 }
124
125 pub fn hash_method(&self) -> &ChunkBundleHashMethod {
126 &self.hash_method
127 }
128
129 pub fn chunk_list(&self) -> &Vec<ChunkId> {
130 &self.list
131 }
132
133 pub fn calc_hash_value(&self) -> HashValue {
134 match self.hash_method {
135 ChunkBundleHashMethod::Serial => {
136 self.calc_serial_hash_value()
137 }
138 }
139 }
140
141 fn calc_serial_hash_value(&self) -> HashValue {
142 use sha2::Digest;
143
144 let mut sha256 = sha2::Sha256::new();
145 self.list.iter().for_each(|id| {
146 sha256.input(id.as_slice());
147 });
148
149 sha256.result().into()
150 }
151}
152
153#[derive(Debug, Clone)]
154pub enum ChunkList {
155 ChunkInList(Vec<ChunkId>),
156 ChunkInFile(FileId),
157 ChunkInBundle(ChunkBundle),
158}
159
160impl ChunkList {
161 pub fn inner_chunk_list(&self) -> Option<&Vec<ChunkId>> {
162 match self {
163 Self::ChunkInList(list) => Some(list),
164 Self::ChunkInBundle(bundle) => {
165 Some(bundle.chunk_list())
166 }
167 Self::ChunkInFile(_) => None,
168 }
169 }
170}
171
172/*
173impl RawEncode for ChunkList {
174 fn raw_measure(&self, purpose: &Option<RawEncodePurpose>) -> BuckyResult<usize> {
175 let len = match self {
176 ChunkList::ChunkInList(list) => {
177 1 + list.raw_measure(purpose).map_err(|e| {
178 log::error!("ChunkList::raw_measure/list error:{}", e);
179 e
180 })?
181 }
182 ChunkList::ChunkInFile(id) => {
183 1 + id.raw_measure(purpose).map_err(|e| {
184 log::error!("ChunkList::raw_measure/id error:{}", e);
185 e
186 })?
187 }
188 ChunkList::ChunkInBundle(bundle) => {
189 1 + bundle.raw_measure(purpose).map_err(|e| {
190 log::error!("ChunkBundle::raw_measure/id error:{}", e);
191 e
192 })?
193 }
194 };
195 Ok(len)
196 }
197
198 fn raw_encode<'a>(
199 &self,
200 buf: &'a mut [u8],
201 purpose: &Option<RawEncodePurpose>,
202 ) -> BuckyResult<&'a mut [u8]> {
203 let size = self.raw_measure(purpose).unwrap();
204 if buf.len() < size {
205 return Err(BuckyError::new(
206 BuckyErrorCode::OutOfLimit,
207 "[raw_encode] not enough buffer for ChunkList",
208 ));
209 }
210 let mut buf = buf;
211 match self {
212 ChunkList::ChunkInList(list) => {
213 buf = 0u8.raw_encode(buf, purpose).map_err(|e| {
214 log::error!("ChunkList::raw_encode/flag_0 error:{}", e);
215 e
216 })?;
217
218 buf = list.raw_encode(buf, purpose).map_err(|e| {
219 log::error!("ChunkList::raw_encode/list error:{}", e);
220 e
221 })?;
222 }
223 ChunkList::ChunkInFile(id) => {
224 buf = 1u8.raw_encode(buf, purpose).map_err(|e| {
225 log::error!("ChunkList::raw_encode/flag_1 error:{}", e);
226 e
227 })?;
228
229 buf = id.raw_encode(buf, purpose).map_err(|e| {
230 log::error!("ChunkList::raw_encode/id error:{}", e);
231 e
232 })?;
233 }
234 ChunkList::ChunkInBundle(bundle) => {
235 buf = 2u8.raw_encode(buf, purpose).map_err(|e| {
236 log::error!("ChunkList::raw_encode/flag_0 error:{}", e);
237 e
238 })?;
239
240 buf = bundle.raw_encode(buf, purpose).map_err(|e| {
241 log::error!("ChunkList::raw_encode/list error:{}", e);
242 e
243 })?;
244 }
245 }
246
247 Ok(buf)
248 }
249}
250
251impl RawDecode<'_> for ChunkList {
252 fn raw_decode(buf: &[u8]) -> BuckyResult<(Self, &[u8])> {
253 let (list_type, buf) = u8::raw_decode(buf).map_err(|e| {
254 log::error!("ChunkList::raw_decode/id error:{}", e);
255 e
256 })?;
257
258 match list_type {
259 0 => {
260 let (list, buf) = Vec::<ChunkId>::raw_decode(buf).map_err(|e| {
261 log::error!("ChunkList::raw_decode/list error:{}", e);
262 e
263 })?;
264
265 Ok((ChunkList::ChunkInList(list), buf))
266 }
267 1 => {
268 let (id, buf) = FileId::raw_decode(buf).map_err(|e| {
269 log::error!("ChunkList::raw_decode/id error:{}", e);
270 e
271 })?;
272
273 Ok((ChunkList::ChunkInFile(id), buf))
274 }
275 2 => {
276 let (bundle, buf) = ChunkBundle::raw_decode(buf).map_err(|e| {
277 log::error!("ChunkBundle::raw_decode error:{}", e);
278 e
279 })?;
280
281 Ok((ChunkList::ChunkInBundle(bundle), buf))
282 }
283 _ => {
284 unreachable!()
285 }
286 }
287 }
288}
289*/
290/*
291impl<'v> RawDiffWithContext<'v, VecDiffContext<'v, ChunkId>> for ChunkList {
292 fn diff_measure(
293 &self,
294 right: &'v Self,
295 ctx: &mut VecDiffContext<'v, ChunkId>,
296 ) -> BuckyResult<usize> {
297 let size = u8::raw_bytes().unwrap()
298 + match self {
299 ChunkList::ChunkInList(left_list) => match right {
300 ChunkList::ChunkInList(right_list) => left_list.diff_measure(right_list, ctx),
301 _ => Err(BuckyError::new(
302 BuckyErrorCode::NotMatch,
303 "chunk list type not match, left is chunk in list, right is chunk in file",
304 )),
305 },
306 ChunkList::ChunkInFile(left_file_id) => match right {
307 ChunkList::ChunkInFile(right_file_id) => {
308 left_file_id.diff_measure(right_file_id)
309 }
310 _ => Err(BuckyError::new(
311 BuckyErrorCode::NotMatch,
312 "chunk list type not match, left is chunk in file, right is chunk in list",
313 )),
314 },
315 }?;
316 Ok(size)
317 }
318
319 fn diff<'d>(
320 &self,
321 right: &Self,
322 buf: &'d mut [u8],
323 ctx: &mut VecDiffContext<'v, ChunkId>,
324 ) -> BuckyResult<&'d mut [u8]> {
325 let size = self.raw_measure(&None).unwrap();
326 if buf.len() < size {
327 return Err(BuckyError::new(
328 BuckyErrorCode::OutOfLimit,
329 "[raw_diff] not enough buffer for VecDiffContext",
330 ));
331 }
332
333 let buf = match self {
334 ChunkList::ChunkInList(left_list) => match right {
335 ChunkList::ChunkInList(right_list) => {
336 let buf = 0u8.raw_encode(buf, &None).map_err(|e| {
337 log::error!("ChunkList::diff/flag_0 error:{}", e);
338 e
339 })?;
340
341 left_list.diff(right_list, buf, ctx)
342 }
343 _ => Err(BuckyError::new(
344 BuckyErrorCode::NotMatch,
345 "chunk list type not match, left is chunk in list, right is chunk in file",
346 )),
347 },
348 ChunkList::ChunkInFile(left_file_id) => match right {
349 ChunkList::ChunkInFile(right_file_id) => {
350 let buf = 1u8
351 .raw_encode(buf, &None)
352 .map_err(|e| {
353 log::error!("ChunkList::diff/flag_1 error:{}", e);
354 e
355 })
356 .map_err(|e| {
357 log::error!("ChunkList::patch/left_file_id error:{}", e);
358 e
359 })?;
360
361 left_file_id.diff(right_file_id, buf)
362 }
363 _ => Err(BuckyError::new(
364 BuckyErrorCode::NotMatch,
365 "chunk list type not match, left is chunk in file, right is chunk in list",
366 )),
367 },
368 }?;
369
370 Ok(buf)
371 }
372}
373
374impl<'de> RawPatch<'de> for ChunkList {
375 fn patch(self, buf: &'de [u8]) -> BuckyResult<(Self, &'de [u8])> {
376 let (flag, buf) = u8::raw_decode(buf).map_err(|e| {
377 log::error!("ChunkList::patch/flag error:{}", e);
378 e
379 })?;
380
381 match flag {
382 0u8=>{
383 match self {
384 ChunkList::ChunkInList(left_list)=>{
385 let (left_list ,buf)= left_list.patch(buf).map_err(|e|{
386 log::error!("ChunkList::patch/left_list error:{}", e);
387 e
388 })?;
389
390 Ok((ChunkList::ChunkInList(left_list),buf))
391 },
392 ChunkList::ChunkInFile(_left_file_id)=>{
393 Err(BuckyError::new(BuckyErrorCode::NotMatch,"decoded chunk list type not match, left is chunk in file, right is chunk in list"))
394 }
395 }
396 },
397 1u8=>{
398 match self {
399 ChunkList::ChunkInList(_left_list)=>{
400 Err(BuckyError::new(BuckyErrorCode::NotMatch,"decoded chunk list type not match, left is chunk in list, right is chunk in file"))
401 },
402 ChunkList::ChunkInFile(left_file_id)=>{
403 let (left_file_id, buf) = left_file_id.patch(buf).map_err(|e|{
404 log::error!("ChunkList::patch/left_file_id error:{}", e);
405 e
406 })?;
407 Ok((ChunkList::ChunkInFile(left_file_id), buf))
408 }
409 }
410 },
411 _=>{
412 Err(BuckyError::new(BuckyErrorCode::NotSupport, "decoded chunk list type is not support"))
413 }
414 }
415 }
416}
417*/
418
419#[derive(Clone, Debug)]
420pub struct FileBodyContent {
421 chunk_list: ChunkList,
422}
423
424impl BodyContent for FileBodyContent {
425 fn format(&self) -> u8 {
426 OBJECT_CONTENT_CODEC_FORMAT_PROTOBUF
427 }
428}
429
430impl FileBodyContent {
431 pub fn new(chunk_list: ChunkList) -> Self {
432 Self { chunk_list }
433 }
434
435 pub fn chunk_list(&self) -> &ChunkList {
436 &self.chunk_list
437 }
438
439 pub fn inner_chunk_list(&self) -> Option<&Vec<ChunkId>> {
440 self.chunk_list.inner_chunk_list()
441 }
442
443 pub fn into_chunk_list(self) -> ChunkList {
444 self.chunk_list
445 }
446}
447
448/*
449impl<'v> RawDiffWithContext<'v, VecDiffContext<'v, ChunkId>> for FileBodyContent {
450 fn diff_measure(
451 &self,
452 right: &'v Self,
453 ctx: &mut VecDiffContext<'v, ChunkId>,
454 ) -> BuckyResult<usize> {
455 self.chunk_list.diff_measure(&right.chunk_list, ctx)
456 }
457
458 fn diff<'d>(
459 &self,
460 right: &Self,
461 buf: &'d mut [u8],
462 ctx: &mut VecDiffContext<'v, ChunkId>,
463 ) -> BuckyResult<&'d mut [u8]> {
464 self.chunk_list.diff(&right.chunk_list, buf, ctx)
465 }
466}
467
468impl<'de> RawPatch<'de> for FileBodyContent {
469 fn patch(self, buf: &'de [u8]) -> BuckyResult<(Self, &'de [u8])> {
470 let (chunk_list, buf) = self.chunk_list.patch(buf).map_err(|e| {
471 log::error!("FileBodyContent::patch/chunk_list error:{}", e);
472 e
473 })?;
474 Ok((Self { chunk_list }, buf))
475 }
476}
477*/
478
479// 使用protobuf对body进行编解码
480impl TryFrom<protos::ChunkList> for ChunkList {
481 type Error = BuckyError;
482
483 fn try_from(mut value: protos::ChunkList) -> BuckyResult<Self> {
484 let ret = match value.get_field_type() {
485 protos::ChunkList_Type::ChunkInFile => {
486 Self::ChunkInFile(ProtobufCodecHelper::decode_buf(value.take_file_id())?)
487 }
488 protos::ChunkList_Type::ChunkInList => Self::ChunkInList(
489 ProtobufCodecHelper::decode_buf_list(value.take_chunk_id_list())?,
490 ),
491 protos::ChunkList_Type::ChunkInBundle => {
492 let list: Vec<ChunkId> = ProtobufCodecHelper::decode_buf_list(value.take_chunk_id_list())?;
493 match value.get_hash_method() {
494 protos::ChunkList_HashMethod::Serial => {
495 let bundle = ChunkBundle::new(list, ChunkBundleHashMethod::Serial);
496 Self::ChunkInBundle(bundle)
497 }
498 }
499 }
500 };
501 Ok(ret)
502 }
503}
504
505impl TryFrom<&ChunkList> for protos::ChunkList {
506 type Error = BuckyError;
507
508 fn try_from(value: &ChunkList) -> BuckyResult<Self> {
509 let mut ret = protos::ChunkList::new();
510
511 match value {
512 ChunkList::ChunkInFile(id) => {
513 ret.set_field_type(protos::ChunkList_Type::ChunkInFile);
514 ret.set_file_id(id.to_vec()?);
515 }
516 ChunkList::ChunkInList(list) => {
517 ret.set_field_type(protos::ChunkList_Type::ChunkInList);
518 ret.set_chunk_id_list(ProtobufCodecHelper::encode_buf_list(list)?);
519 }
520 ChunkList::ChunkInBundle(bundle) => {
521 ret.set_field_type(protos::ChunkList_Type::ChunkInBundle);
522 ret.set_chunk_id_list(ProtobufCodecHelper::encode_buf_list(bundle.chunk_list())?);
523 match bundle.hash_method() {
524 ChunkBundleHashMethod::Serial => {
525 ret.set_hash_method(protos::ChunkList_HashMethod::Serial);
526 }
527 }
528 }
529 }
530
531 Ok(ret)
532 }
533}
534
535impl TryFrom<protos::FileBodyContent> for FileBodyContent {
536 type Error = BuckyError;
537
538 fn try_from(mut value: protos::FileBodyContent) -> BuckyResult<Self> {
539 Ok(Self {
540 chunk_list: ChunkList::try_from(value.take_chunk_list())?,
541 })
542 }
543}
544
545impl TryFrom<&FileBodyContent> for protos::FileBodyContent {
546 type Error = BuckyError;
547
548 fn try_from(value: &FileBodyContent) -> BuckyResult<Self> {
549 let mut ret = protos::FileBodyContent::new();
550
551 ret.set_chunk_list(protos::ChunkList::try_from(&value.chunk_list)?);
552 Ok(ret)
553 }
554}
555
556crate::inner_impl_default_protobuf_raw_codec!(FileBodyContent);
557
558pub type FileType = NamedObjType<FileDescContent, FileBodyContent>;
559pub type FileBuilder = NamedObjectBuilder<FileDescContent, FileBodyContent>;
560
561pub type FileDesc = NamedObjectDesc<FileDescContent>;
562pub type FileId = NamedObjectId<FileType>;
563pub type File = NamedObjectBase<FileType>;
564
565impl FileDesc {
566 pub fn file_id(&self) -> FileId {
567 FileId::try_from(self.calculate_id()).unwrap()
568 }
569}
570
571impl File {
572 pub fn new(owner: ObjectId, len: u64, hash: HashValue, chunk_list: ChunkList) -> FileBuilder {
573 let desc_content = FileDescContent::new(len, hash);
574 let body_content = FileBodyContent::new(chunk_list);
575
576 FileBuilder::new(desc_content, body_content).owner(owner)
577 }
578
579 pub fn new_no_owner(len: u64, hash: HashValue, chunk_list: ChunkList) -> FileBuilder {
580 let desc_content = FileDescContent::new(len, hash);
581 let body_content = FileBodyContent::new(chunk_list);
582
583 FileBuilder::new(desc_content, body_content)
584 }
585
586 pub fn len(&self) -> u64 {
587 self.desc().content().len()
588 }
589
590 pub fn hash(&self) -> &HashValue {
591 self.desc().content().hash()
592 }
593}
594
595#[cfg(test)]
596mod test_file {
597 use crate::*;
598 use std::str::FromStr;
599 //use std::convert::From;
600 //use std::path::Path;
601
602 // #[test]
603 // fn file_load() {
604 // let p = Path::new("f:\\temp\\file.obj");
605 // if p.exists() {
606 // let mut v = Vec::<u8>::new();
607 // let (file, _) = File::decode_from_file(p, &mut v).unwrap();
608 // println!("{:?}", file);
609 // }
610 //
611 // }
612
613 #[test]
614 fn file() {
615 let owner = ObjectId::default();
616 let hash = HashValue::default();
617
618 let chunk_list = vec![ChunkId::default(), ChunkId::default()];
619
620 let chunk_list = ChunkList::ChunkInList(chunk_list);
621 let chunk_list_2 = chunk_list.clone();
622 let _chunk_list_3 = chunk_list.clone();
623
624 let file = File::new(owner, 100, hash, chunk_list)
625 .no_create_time()
626 .build();
627
628 let file_id = file.desc().file_id();
629 let _chunk_list_4 = ChunkList::ChunkInFile(file_id.clone());
630
631 let hash = hash.clone();
632
633 let file2 = File::new(owner, 100, hash, chunk_list_2)
634 .no_create_time()
635 .build();
636 let file_id_2 = file2.desc().file_id();
637
638 println!("\n file_id:{:?}, \n file_id_2:{:?}\n\n", file_id, file_id_2);
639
640 // assert!(false);
641 assert!(file_id.to_string() == file_id_2.to_string());
642
643 // let p = Path::new("f:\\temp\\file.obj");
644 // if p.parent().unwrap().exists() {
645 // file.encode_to_file(p, false);
646 // }
647 // let p = Path::new("f:\\temp\\chunk_256.obj");
648 // if p.parent().unwrap().exists() {
649 // chunk_list_3.encode_to_file(p, false);
650 // }
651 // let p = Path::new("f:\\temp\\chunk_in_file.obj");
652 // if p.parent().unwrap().exists() {
653 // chunk_list_4.encode_to_file(p, false);
654 // }
655 }
656
657 #[test]
658 fn test_codec() {
659 let chunk_list = ["7C8WXUGc4cDjrFrcaYE28FwkykTc7D67sq6nVpwmc73C","7C8WXUGvbdJrK7X7wfPzhWFKU1c14GQiMcvrmqMwmQm3","7C8WXUGqpDS3fwmQhX4PT8zJKBcPPT7SWznKCzSJyX4h","7C8WXUH97ftxKchxX1xPYfjUqk4TVgARnRSA1Z6vki2u","7C8WXUGnjjTWstqyEdAMtsUPHESV8AM4JUzNABnKiCmD","7C8xA3NNwQPw5UFBQa4PL3u1iN9htZj2se5j54FGE9cv"];
660 let list: Vec<ChunkId> = chunk_list.iter().map(|id| ChunkId::from_str(&id).unwrap()).collect();
661
662 let chunks = ChunkList::ChunkInList(list);
663 let body = FileBodyContent::new(chunks);
664 let buf = body.to_vec().unwrap();
665 println!("body len={}", buf.len());
666
667 let code = "00080a02002f3d25175662554800000000434c42a2c2a288367e7533c9aec22813d876bbe84ab6cf370f0a130000002800000000016a8000c7b07e826cb5f241178c5d22e4b5dc743c55e1443e8b4405fc5f04c0ac5a44d800002f3d28a5280da6000140d10ace01080012205c00004000140c3c275d3399655537e1482621cd95d7a4e143e3abc5d65bfc0f12205c000040009d2f79890697bcc7d47d1a571f5bcbce2c45a8be8fb630db2c31f612205c0000400079cbf395974b7512b0ef24f974076c606041002ef8fbe60ba1763e12205c00004000f9ce26d7332885a7a381ed87c4a994d823491f9701c89a54bda35a12205c0000400063073223a62acb1a53e45d2d363b5aac5355f4649e0da8355a833812205c00802a0095289410d3e504516a1afd429021d4767cb3be26a0ff288716fabb";
668 let mut buf = vec![];
669 let file = File::clone_from_hex(&code, &mut buf).unwrap();
670 println!("{}", file.format_json());
671
672 //let new_code = file.to_vec().unwrap();
673 //let new_code = hex::encode(&new_code);
674 //assert_eq!(new_code, code);
675 }
676}