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}