rust/hg-core/src/dirstate_tree/on_disk.rs
changeset 47678 065e61628980
parent 47677 da1c0cd68d53
child 47679 731286dc5321
equal deleted inserted replaced
47677:da1c0cd68d53 47678:065e61628980
   542         Ok(())
   542         Ok(())
   543     }
   543     }
   544     recur(on_disk, root.root_nodes, &mut f)
   544     recur(on_disk, root.root_nodes, &mut f)
   545 }
   545 }
   546 
   546 
       
   547 /// Returns new data together with whether that data should be appended to the
       
   548 /// existing data file whose content is at `dirstate_map.on_disk` (true),
       
   549 /// instead of written to a new data file (false).
   547 pub(super) fn write(
   550 pub(super) fn write(
   548     dirstate_map: &mut DirstateMap,
   551     dirstate_map: &mut DirstateMap,
   549 ) -> Result<Vec<u8>, DirstateError> {
   552     can_append: bool,
   550     let root_len = std::mem::size_of::<Root>();
   553 ) -> Result<(Vec<u8>, bool), DirstateError> {
       
   554     let append = can_append && dirstate_map.write_should_append();
   551 
   555 
   552     // This ignores the space for paths, and for nodes without an entry.
   556     // This ignores the space for paths, and for nodes without an entry.
   553     // TODO: better estimate? Skip the `Vec` and write to a file directly?
   557     // TODO: better estimate? Skip the `Vec` and write to a file directly?
   554     let size_guess = root_len
   558     let size_guess = std::mem::size_of::<Root>()
   555         + std::mem::size_of::<Node>()
   559         + std::mem::size_of::<Node>()
   556             * dirstate_map.nodes_with_entry_count as usize;
   560             * dirstate_map.nodes_with_entry_count as usize;
   557     let mut out = Vec::with_capacity(size_guess);
   561 
   558 
   562     let mut writer = Writer {
   559     let root_nodes =
   563         dirstate_map,
   560         write_nodes(dirstate_map, dirstate_map.root.as_ref(), &mut out)?;
   564         append,
       
   565         out: Vec::with_capacity(size_guess),
       
   566     };
       
   567 
       
   568     let root_nodes = writer.write_nodes(dirstate_map.root.as_ref())?;
   561 
   569 
   562     let root = Root {
   570     let root = Root {
   563         root_nodes,
   571         root_nodes,
   564         nodes_with_entry_count: dirstate_map.nodes_with_entry_count.into(),
   572         nodes_with_entry_count: dirstate_map.nodes_with_entry_count.into(),
   565         nodes_with_copy_source_count: dirstate_map
   573         nodes_with_copy_source_count: dirstate_map
   566             .nodes_with_copy_source_count
   574             .nodes_with_copy_source_count
   567             .into(),
   575             .into(),
   568         ignore_patterns_hash: dirstate_map.ignore_patterns_hash,
   576         ignore_patterns_hash: dirstate_map.ignore_patterns_hash,
   569     };
   577     };
   570     out.extend(root.as_bytes());
   578     writer.out.extend(root.as_bytes());
   571     Ok(out)
   579     Ok((writer.out, append))
   572 }
   580 }
   573 
   581 
   574 fn write_nodes(
   582 struct Writer<'dmap, 'on_disk> {
   575     dirstate_map: &DirstateMap,
   583     dirstate_map: &'dmap DirstateMap<'on_disk>,
   576     nodes: dirstate_map::ChildNodesRef,
   584     append: bool,
   577     out: &mut Vec<u8>,
   585     out: Vec<u8>,
   578 ) -> Result<ChildNodes, DirstateError> {
   586 }
   579     // `dirstate_map::ChildNodes` is a `HashMap` with undefined iteration
   587 
   580     // order. Sort to enable binary search in the written file.
   588 impl Writer<'_, '_> {
   581     let nodes = nodes.sorted();
   589     fn write_nodes(
   582     let nodes_len = nodes.len();
   590         &mut self,
   583 
   591         nodes: dirstate_map::ChildNodesRef,
   584     // First accumulate serialized nodes in a `Vec`
   592     ) -> Result<ChildNodes, DirstateError> {
   585     let mut on_disk_nodes = Vec::with_capacity(nodes_len);
   593         // `dirstate_map::ChildNodes` is a `HashMap` with undefined iteration
   586     for node in nodes {
   594         // order. Sort to enable binary search in the written file.
   587         let children = write_nodes(
   595         let nodes = nodes.sorted();
   588             dirstate_map,
   596         let nodes_len = nodes.len();
   589             node.children(dirstate_map.on_disk)?,
   597 
   590             out,
   598         // First accumulate serialized nodes in a `Vec`
   591         )?;
   599         let mut on_disk_nodes = Vec::with_capacity(nodes_len);
   592         let full_path = node.full_path(dirstate_map.on_disk)?;
   600         for node in nodes {
   593         let full_path = write_path(full_path.as_bytes(), out);
   601             let children =
   594         let copy_source =
   602                 self.write_nodes(node.children(self.dirstate_map.on_disk)?)?;
   595             if let Some(source) = node.copy_source(dirstate_map.on_disk)? {
   603             let full_path = node.full_path(self.dirstate_map.on_disk)?;
   596                 write_path(source.as_bytes(), out)
   604             let full_path = self.write_path(full_path.as_bytes());
       
   605             let copy_source = if let Some(source) =
       
   606                 node.copy_source(self.dirstate_map.on_disk)?
       
   607             {
       
   608                 self.write_path(source.as_bytes())
   597             } else {
   609             } else {
   598                 PathSlice {
   610                 PathSlice {
   599                     start: 0.into(),
   611                     start: 0.into(),
   600                     len: 0.into(),
   612                     len: 0.into(),
   601                 }
   613                 }
   602             };
   614             };
   603         on_disk_nodes.push(match node {
   615             on_disk_nodes.push(match node {
   604             NodeRef::InMemory(path, node) => {
   616                 NodeRef::InMemory(path, node) => {
   605                 let (state, data) = match &node.data {
   617                     let (state, data) = match &node.data {
   606                     dirstate_map::NodeData::Entry(entry) => (
   618                         dirstate_map::NodeData::Entry(entry) => (
   607                         entry.state.into(),
   619                             entry.state.into(),
   608                         Entry {
   620                             Entry {
   609                             mode: entry.mode.into(),
   621                                 mode: entry.mode.into(),
   610                             mtime: entry.mtime.into(),
   622                                 mtime: entry.mtime.into(),
   611                             size: entry.size.into(),
   623                                 size: entry.size.into(),
   612                         },
   624                             },
   613                     ),
   625                         ),
   614                     dirstate_map::NodeData::CachedDirectory { mtime } => {
   626                         dirstate_map::NodeData::CachedDirectory { mtime } => {
   615                         (b'd', Entry::from_timestamp(*mtime))
   627                             (b'd', Entry::from_timestamp(*mtime))
       
   628                         }
       
   629                         dirstate_map::NodeData::None => (
       
   630                             b'\0',
       
   631                             Entry {
       
   632                                 mode: 0.into(),
       
   633                                 mtime: 0.into(),
       
   634                                 size: 0.into(),
       
   635                             },
       
   636                         ),
       
   637                     };
       
   638                     Node {
       
   639                         children,
       
   640                         copy_source,
       
   641                         full_path,
       
   642                         base_name_start: u16::try_from(path.base_name_start())
       
   643                             // Could only panic for paths over 64 KiB
       
   644                             .expect("dirstate-v2 path length overflow")
       
   645                             .into(),
       
   646                         descendants_with_entry_count: node
       
   647                             .descendants_with_entry_count
       
   648                             .into(),
       
   649                         tracked_descendants_count: node
       
   650                             .tracked_descendants_count
       
   651                             .into(),
       
   652                         state,
       
   653                         data,
   616                     }
   654                     }
   617                     dirstate_map::NodeData::None => (
   655                 }
   618                         b'\0',
   656                 NodeRef::OnDisk(node) => Node {
   619                         Entry {
       
   620                             mode: 0.into(),
       
   621                             mtime: 0.into(),
       
   622                             size: 0.into(),
       
   623                         },
       
   624                     ),
       
   625                 };
       
   626                 Node {
       
   627                     children,
   657                     children,
   628                     copy_source,
   658                     copy_source,
   629                     full_path,
   659                     full_path,
   630                     base_name_start: u16::try_from(path.base_name_start())
   660                     ..*node
   631                         // Could only panic for paths over 64 KiB
   661                 },
   632                         .expect("dirstate-v2 path length overflow")
   662             })
   633                         .into(),
   663         }
   634                     descendants_with_entry_count: node
   664         // … so we can write them contiguously, after writing everything else
   635                         .descendants_with_entry_count
   665         // they refer to.
   636                         .into(),
   666         let start = self.current_offset();
   637                     tracked_descendants_count: node
   667         let len = u32::try_from(nodes_len)
   638                         .tracked_descendants_count
   668             // Could only panic with over 4 billion nodes
   639                         .into(),
   669             .expect("dirstate-v2 path length overflow")
   640                     state,
   670             .into();
   641                     data,
   671         self.out.extend(on_disk_nodes.as_bytes());
   642                 }
   672         Ok(ChildNodes { start, len })
   643             }
   673     }
   644             NodeRef::OnDisk(node) => Node {
   674 
   645                 children,
   675     fn current_offset(&mut self) -> Offset {
   646                 copy_source,
   676         let mut offset = self.out.len();
   647                 full_path,
   677         if self.append {
   648                 ..*node
   678             offset += self.dirstate_map.on_disk.len()
   649             },
   679         }
   650         })
   680         u32::try_from(offset)
   651     }
   681             // Could only panic for a dirstate file larger than 4 GiB
   652     // … so we can write them contiguously, after writing everything else they
   682             .expect("dirstate-v2 offset overflow")
   653     // refer to.
   683             .into()
   654     let start = current_offset(out);
   684     }
   655     let len = u32::try_from(nodes_len)
   685 
   656         // Could only panic with over 4 billion nodes
   686     fn write_path(&mut self, slice: &[u8]) -> PathSlice {
   657         .expect("dirstate-v2 path length overflow")
   687         let start = self.current_offset();
   658         .into();
   688         let len = u16::try_from(slice.len())
   659     out.extend(on_disk_nodes.as_bytes());
   689             // Could only panic for paths over 64 KiB
   660     Ok(ChildNodes { start, len })
   690             .expect("dirstate-v2 path length overflow")
   661 }
   691             .into();
   662 
   692         self.out.extend(slice.as_bytes());
   663 fn current_offset(out: &Vec<u8>) -> Offset {
   693         PathSlice { start, len }
   664     u32::try_from(out.len())
   694     }
   665         // Could only panic for a dirstate file larger than 4 GiB
   695 }
   666         .expect("dirstate-v2 offset overflow")
       
   667         .into()
       
   668 }
       
   669 
       
   670 fn write_path(slice: &[u8], out: &mut Vec<u8>) -> PathSlice {
       
   671     let start = current_offset(out);
       
   672     let len = u16::try_from(slice.len())
       
   673         // Could only panic for paths over 64 KiB
       
   674         .expect("dirstate-v2 path length overflow")
       
   675         .into();
       
   676     out.extend(slice.as_bytes());
       
   677     PathSlice { start, len }
       
   678 }