rust/hg-core/src/dirstate_tree/on_disk.rs
changeset 47334 18b3060fe598
parent 47333 69530e5d4fe5
child 47335 ed1583a845d2
equal deleted inserted replaced
47333:69530e5d4fe5 47334:18b3060fe598
   106 fn _static_assert_size_of() {
   106 fn _static_assert_size_of() {
   107     let _ = std::mem::transmute::<Header, [u8; 72]>;
   107     let _ = std::mem::transmute::<Header, [u8; 72]>;
   108     let _ = std::mem::transmute::<Node, [u8; 57]>;
   108     let _ = std::mem::transmute::<Node, [u8; 57]>;
   109 }
   109 }
   110 
   110 
       
   111 /// Unexpected file format found in `.hg/dirstate` with the "v2" format.
       
   112 pub(crate) struct DirstateV2ParseError;
       
   113 
       
   114 impl From<DirstateV2ParseError> for HgError {
       
   115     fn from(_: DirstateV2ParseError) -> Self {
       
   116         HgError::corrupted("dirstate-v2 parse error")
       
   117     }
       
   118 }
       
   119 
       
   120 impl From<DirstateV2ParseError> for crate::DirstateError {
       
   121     fn from(error: DirstateV2ParseError) -> Self {
       
   122         HgError::from(error).into()
       
   123     }
       
   124 }
       
   125 
   111 pub(super) fn read<'on_disk>(
   126 pub(super) fn read<'on_disk>(
   112     on_disk: &'on_disk [u8],
   127     on_disk: &'on_disk [u8],
   113 ) -> Result<(DirstateMap<'on_disk>, Option<DirstateParents>), DirstateError> {
   128 ) -> Result<
       
   129     (DirstateMap<'on_disk>, Option<DirstateParents>),
       
   130     DirstateV2ParseError,
       
   131 > {
   114     if on_disk.is_empty() {
   132     if on_disk.is_empty() {
   115         return Ok((DirstateMap::empty(on_disk), None));
   133         return Ok((DirstateMap::empty(on_disk), None));
   116     }
   134     }
   117     let (header, _) = Header::from_bytes(on_disk)
   135     let (header, _) =
   118         .map_err(|_| HgError::corrupted("truncated dirstate-v2"))?;
   136         Header::from_bytes(on_disk).map_err(|_| DirstateV2ParseError)?;
   119     let Header {
   137     let Header {
   120         marker,
   138         marker,
   121         parents,
   139         parents,
   122         root,
   140         root,
   123         nodes_with_entry_count,
   141         nodes_with_entry_count,
   124         nodes_with_copy_source_count,
   142         nodes_with_copy_source_count,
   125     } = header;
   143     } = header;
   126     if marker != V2_FORMAT_MARKER {
   144     if marker != V2_FORMAT_MARKER {
   127         return Err(HgError::corrupted("missing dirstated-v2 marker").into());
   145         return Err(DirstateV2ParseError);
   128     }
   146     }
   129     let dirstate_map = DirstateMap {
   147     let dirstate_map = DirstateMap {
   130         on_disk,
   148         on_disk,
   131         root: read_nodes(on_disk, *root)?,
   149         root: read_nodes(on_disk, *root)?,
   132         nodes_with_entry_count: nodes_with_entry_count.get(),
   150         nodes_with_entry_count: nodes_with_entry_count.get(),
   138 
   156 
   139 impl Node {
   157 impl Node {
   140     pub(super) fn path<'on_disk>(
   158     pub(super) fn path<'on_disk>(
   141         &self,
   159         &self,
   142         on_disk: &'on_disk [u8],
   160         on_disk: &'on_disk [u8],
   143     ) -> Result<dirstate_map::NodeKey<'on_disk>, HgError> {
   161     ) -> Result<dirstate_map::NodeKey<'on_disk>, DirstateV2ParseError> {
   144         let full_path = read_hg_path(on_disk, self.full_path)?;
   162         let full_path = read_hg_path(on_disk, self.full_path)?;
   145         let base_name_start = usize::try_from(self.base_name_start.get())
   163         let base_name_start = usize::try_from(self.base_name_start.get())
   146             // u32 -> usize, could only panic on a 16-bit CPU
   164             // u32 -> usize, could only panic on a 16-bit CPU
   147             .expect("dirstate-v2 base_name_start out of bounds");
   165             .expect("dirstate-v2 base_name_start out of bounds");
   148         if base_name_start < full_path.len() {
   166         if base_name_start < full_path.len() {
   149             Ok(WithBasename::from_raw_parts(full_path, base_name_start))
   167             Ok(WithBasename::from_raw_parts(full_path, base_name_start))
   150         } else {
   168         } else {
   151             Err(HgError::corrupted(
   169             Err(DirstateV2ParseError)
   152                 "dirstate-v2 base_name_start out of bounds",
       
   153             ))
       
   154         }
   170         }
   155     }
   171     }
   156 
   172 
   157     pub(super) fn copy_source<'on_disk>(
   173     pub(super) fn copy_source<'on_disk>(
   158         &self,
   174         &self,
   159         on_disk: &'on_disk [u8],
   175         on_disk: &'on_disk [u8],
   160     ) -> Result<Option<Cow<'on_disk, HgPath>>, HgError> {
   176     ) -> Result<Option<Cow<'on_disk, HgPath>>, DirstateV2ParseError> {
   161         Ok(if self.copy_source.start.get() != 0 {
   177         Ok(if self.copy_source.start.get() != 0 {
   162             Some(read_hg_path(on_disk, self.copy_source)?)
   178             Some(read_hg_path(on_disk, self.copy_source)?)
   163         } else {
   179         } else {
   164             None
   180             None
   165         })
   181         })
   166     }
   182     }
   167 
   183 
   168     pub(super) fn entry(&self) -> Result<Option<DirstateEntry>, HgError> {
   184     pub(super) fn entry(
       
   185         &self,
       
   186     ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
   169         Ok(if self.entry.state != b'\0' {
   187         Ok(if self.entry.state != b'\0' {
   170             Some(DirstateEntry {
   188             Some(DirstateEntry {
   171                 state: self.entry.state.try_into()?,
   189                 state: self
       
   190                     .entry
       
   191                     .state
       
   192                     .try_into()
       
   193                     .map_err(|_| DirstateV2ParseError)?,
   172                 mode: self.entry.mode.get(),
   194                 mode: self.entry.mode.get(),
   173                 mtime: self.entry.mtime.get(),
   195                 mtime: self.entry.mtime.get(),
   174                 size: self.entry.size.get(),
   196                 size: self.entry.size.get(),
   175             })
   197             })
   176         } else {
   198         } else {
   179     }
   201     }
   180 
   202 
   181     pub(super) fn to_in_memory_node<'on_disk>(
   203     pub(super) fn to_in_memory_node<'on_disk>(
   182         &self,
   204         &self,
   183         on_disk: &'on_disk [u8],
   205         on_disk: &'on_disk [u8],
   184     ) -> Result<dirstate_map::Node<'on_disk>, HgError> {
   206     ) -> Result<dirstate_map::Node<'on_disk>, DirstateV2ParseError> {
   185         Ok(dirstate_map::Node {
   207         Ok(dirstate_map::Node {
   186             children: read_nodes(on_disk, self.children)?,
   208             children: read_nodes(on_disk, self.children)?,
   187             copy_source: self.copy_source(on_disk)?,
   209             copy_source: self.copy_source(on_disk)?,
   188             entry: self.entry()?,
   210             entry: self.entry()?,
   189             tracked_descendants_count: self.tracked_descendants_count.get(),
   211             tracked_descendants_count: self.tracked_descendants_count.get(),
   192 }
   214 }
   193 
   215 
   194 fn read_nodes(
   216 fn read_nodes(
   195     on_disk: &[u8],
   217     on_disk: &[u8],
   196     slice: ChildNodes,
   218     slice: ChildNodes,
   197 ) -> Result<dirstate_map::ChildNodes, HgError> {
   219 ) -> Result<dirstate_map::ChildNodes, DirstateV2ParseError> {
   198     read_slice::<Node>(on_disk, slice)?
   220     read_slice::<Node>(on_disk, slice)?
   199         .iter()
   221         .iter()
   200         .map(|node| {
   222         .map(|node| {
   201             Ok((node.path(on_disk)?, node.to_in_memory_node(on_disk)?))
   223             Ok((node.path(on_disk)?, node.to_in_memory_node(on_disk)?))
   202         })
   224         })
   203         .collect::<Result<_, _>>()
   225         .collect::<Result<_, _>>()
   204         .map(dirstate_map::ChildNodes::InMemory)
   226         .map(dirstate_map::ChildNodes::InMemory)
   205 }
   227 }
   206 
   228 
   207 fn read_hg_path(on_disk: &[u8], slice: Slice) -> Result<Cow<HgPath>, HgError> {
   229 fn read_hg_path(
       
   230     on_disk: &[u8],
       
   231     slice: Slice,
       
   232 ) -> Result<Cow<HgPath>, DirstateV2ParseError> {
   208     let bytes = read_slice::<u8>(on_disk, slice)?;
   233     let bytes = read_slice::<u8>(on_disk, slice)?;
   209     Ok(Cow::Borrowed(HgPath::new(bytes)))
   234     Ok(Cow::Borrowed(HgPath::new(bytes)))
   210 }
   235 }
   211 
   236 
   212 fn read_slice<T>(on_disk: &[u8], slice: Slice) -> Result<&[T], HgError>
   237 fn read_slice<T>(
       
   238     on_disk: &[u8],
       
   239     slice: Slice,
       
   240 ) -> Result<&[T], DirstateV2ParseError>
   213 where
   241 where
   214     T: BytesCast,
   242     T: BytesCast,
   215 {
   243 {
   216     // Either `usize::MAX` would result in "out of bounds" error since a single
   244     // Either `usize::MAX` would result in "out of bounds" error since a single
   217     // `&[u8]` cannot occupy the entire addess space.
   245     // `&[u8]` cannot occupy the entire addess space.
   219     let len = usize::try_from(slice.len.get()).unwrap_or(std::usize::MAX);
   247     let len = usize::try_from(slice.len.get()).unwrap_or(std::usize::MAX);
   220     on_disk
   248     on_disk
   221         .get(start..)
   249         .get(start..)
   222         .and_then(|bytes| T::slice_from_bytes(bytes, len).ok())
   250         .and_then(|bytes| T::slice_from_bytes(bytes, len).ok())
   223         .map(|(slice, _rest)| slice)
   251         .map(|(slice, _rest)| slice)
   224         .ok_or_else(|| {
   252         .ok_or_else(|| DirstateV2ParseError)
   225             HgError::corrupted("dirstate v2 slice is out of bounds")
       
   226         })
       
   227 }
   253 }
   228 
   254 
   229 pub(super) fn write(
   255 pub(super) fn write(
   230     dirstate_map: &mut DirstateMap,
   256     dirstate_map: &mut DirstateMap,
   231     parents: DirstateParents,
   257     parents: DirstateParents,