D7788: rust-node: binary Node and conversion utilities

gracinet (Georges Racinet) phabricator at mercurial-scm.org
Mon Jan 6 19:26:16 UTC 2020


gracinet created this revision.
Herald added subscribers: mercurial-devel, kevincox, durin42.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Our choice of type makes sure that a `Node` has the exact
  wanted size. We'll use a different type for prefixes.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D7788

AFFECTED FILES
  rust/hg-core/src/dirstate/dirs_multiset.rs
  rust/hg-core/src/matchers.rs
  rust/hg-core/src/revlog.rs
  rust/hg-core/src/revlog/node.rs
  rust/hg-core/src/utils.rs
  rust/hg-core/src/utils/hg_path.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/utils/hg_path.rs b/rust/hg-core/src/utils/hg_path.rs
--- a/rust/hg-core/src/utils/hg_path.rs
+++ b/rust/hg-core/src/utils/hg_path.rs
@@ -157,7 +157,7 @@
                     return Err(HgPathError::ContainsNullByte(
                         bytes.to_vec(),
                         index,
-                    ))
+                    ));
                 }
                 b'/' => {
                     if previous_byte.is_some() && previous_byte == Some(b'/') {
diff --git a/rust/hg-core/src/utils.rs b/rust/hg-core/src/utils.rs
--- a/rust/hg-core/src/utils.rs
+++ b/rust/hg-core/src/utils.rs
@@ -18,10 +18,7 @@
 /// use crate::hg::utils::replace_slice;
 /// let mut line = b"I hate writing tests!".to_vec();
 /// replace_slice(&mut line, b"hate", b"love");
-/// assert_eq!(
-///     line,
-///     b"I love writing tests!".to_vec()
-/// );
+/// assert_eq!(line, b"I love writing tests!".to_vec());
 /// ```
 pub fn replace_slice<T>(buf: &mut [T], from: &[T], to: &[T])
 where
@@ -66,18 +63,9 @@
 
     /// ```
     /// use hg::utils::SliceExt;
-    /// assert_eq!(
-    ///     b"  to trim  ".trim(),
-    ///     b"to trim"
-    /// );
-    /// assert_eq!(
-    ///     b"to trim  ".trim(),
-    ///     b"to trim"
-    /// );
-    /// assert_eq!(
-    ///     b"  to trim".trim(),
-    ///     b"to trim"
-    /// );
+    /// assert_eq!(b"  to trim  ".trim(), b"to trim");
+    /// assert_eq!(b"to trim  ".trim(), b"to trim");
+    /// assert_eq!(b"  to trim".trim(), b"to trim");
     /// ```
     fn trim(&self) -> &[u8] {
         self.trim_start().trim_end()
diff --git a/rust/hg-core/src/revlog/node.rs b/rust/hg-core/src/revlog/node.rs
new file mode 100644
--- /dev/null
+++ b/rust/hg-core/src/revlog/node.rs
@@ -0,0 +1,91 @@
+// Copyright 2019-2020 Georges Racinet <georges.racinet at octobus.net>
+//
+// This software may be used and distributed according to the terms of the
+// GNU General Public License version 2 or any later version.
+
+//! Definitions and utilities for Revision nodes
+//!
+//! In Mercurial code base, it is customary to call "a node" the binary SHA
+//! of a revision.
+
+use std::num::ParseIntError;
+
+/// Binary revisions SHA
+pub type Node = [u8; 20];
+
+/// The node value for NULL_REVISION
+pub const NULL_NODE: Node = [0; 20];
+
+#[derive(Debug, PartialEq)]
+pub enum NodeError {
+    ExactLengthRequired(String),
+    NotHexadecimal,
+}
+
+pub fn node_from_hex(hex: &str) -> Result<Node, NodeError> {
+    if hex.len() != 40 {
+        return Err(NodeError::ExactLengthRequired(hex.to_string()));
+    }
+    let mut node = [0; 20];
+    for i in 0..20 {
+        node[i] = u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16)?
+    }
+    Ok(node)
+}
+
+pub fn node_to_hex(n: &Node) -> String {
+    let as_vec: Vec<String> = n.iter().map(|b| format!("{:02x}", b)).collect();
+    as_vec.join("")
+}
+
+/// Retrieve the `i`th half-byte from a bytes slice
+///
+/// This is also the `i`th hexadecimal digit in numeric form,
+/// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
+pub fn get_nybble(i: usize, s: &[u8]) -> u8 {
+    if i % 2 == 0 {
+        s[i / 2] >> 4
+    } else {
+        s[i / 2] & 0x0f
+    }
+}
+
+impl From<ParseIntError> for NodeError {
+    fn from(_: ParseIntError) -> Self {
+        NodeError::NotHexadecimal
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    const SAMPLE_NODE_HEX: &str = "0123456789abcdeffedcba9876543210deadbeef";
+
+    #[test]
+    fn test_node_from_hex() {
+        assert_eq!(
+            node_from_hex(SAMPLE_NODE_HEX),
+            Ok([
+                0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc,
+                0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xde, 0xad, 0xbe, 0xef
+            ])
+        );
+        let short = "0123456789abcdeffedcba9876543210";
+        assert_eq!(
+            node_from_hex(short),
+            Err(NodeError::ExactLengthRequired(short.to_string())),
+        );
+    }
+
+    #[test]
+    fn test_node_to_hex() {
+        assert_eq!(
+            node_to_hex(&[
+                0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc,
+                0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xde, 0xad, 0xbe, 0xef
+            ]),
+            SAMPLE_NODE_HEX
+        );
+    }
+}
diff --git a/rust/hg-core/src/revlog.rs b/rust/hg-core/src/revlog.rs
--- a/rust/hg-core/src/revlog.rs
+++ b/rust/hg-core/src/revlog.rs
@@ -5,7 +5,9 @@
 // GNU General Public License version 2 or any later version.
 //! Mercurial concepts for handling revision history
 
+pub mod node;
 pub mod nodemap;
+pub use node::{Node, NodeError};
 
 /// Mercurial revision numbers
 ///
diff --git a/rust/hg-core/src/matchers.rs b/rust/hg-core/src/matchers.rs
--- a/rust/hg-core/src/matchers.rs
+++ b/rust/hg-core/src/matchers.rs
@@ -81,7 +81,10 @@
 
 /// Matches everything.
 ///```
-/// use hg::{ matchers::{Matcher, AlwaysMatcher}, utils::hg_path::HgPath };
+/// use hg::{
+///     matchers::{AlwaysMatcher, Matcher},
+///     utils::hg_path::HgPath,
+/// };
 ///
 /// let matcher = AlwaysMatcher;
 ///
@@ -121,7 +124,10 @@
 /// patterns.
 ///
 ///```
-/// use hg::{ matchers::{Matcher, FileMatcher}, utils::hg_path::HgPath };
+/// use hg::{
+///     matchers::{FileMatcher, Matcher},
+///     utils::hg_path::HgPath,
+/// };
 ///
 /// let files = [HgPath::new(b"a.txt"), HgPath::new(br"re:.*\.c$")];
 /// let matcher = FileMatcher::new(&files).unwrap();
diff --git a/rust/hg-core/src/dirstate/dirs_multiset.rs b/rust/hg-core/src/dirstate/dirs_multiset.rs
--- a/rust/hg-core/src/dirstate/dirs_multiset.rs
+++ b/rust/hg-core/src/dirstate/dirs_multiset.rs
@@ -108,7 +108,7 @@
                 Entry::Vacant(_) => {
                     return Err(DirstateMapError::PathNotFound(
                         path.as_ref().to_owned(),
-                    ))
+                    ));
                 }
             };
         }



To: gracinet, #hg-reviewers
Cc: durin42, kevincox, mercurial-devel


More information about the Mercurial-devel mailing list