diff --git a/rust/indexes/src/changelog_utils.rs b/rust/indexes/src/changelog_utils.rs
new file mode 100644
--- /dev/null
+++ b/rust/indexes/src/changelog_utils.rs
@@ -0,0 +1,38 @@
+// Copyright 2017 Facebook, Inc.
+//
+// This software may be used and distributed according to the terms of the
+// GNU General Public License version 2 or any later version.
+
+use std::u32;
+use radixbuf::key::KeyId;
+use radixbuf::errors as rerrors;
+
+/// See "index ng" comment in mercurial/revlog.py
+pub const CHANGELOG_ENTRY_SIZE: usize = 64;
+
+// Offsets of fields in a changelog entry
+pub const CHANGELOG_ENTRY_P1_OFFSET: usize = 24;
+pub const CHANGELOG_ENTRY_NODE_OFFSET: usize = 32;
+
+/// Return the minimal revision number the changelog.i does not have.
+pub fn changelog_end_rev<T: AsRef<[u8]>>(changelogi: &T) -> u32 {
+    let changelogi = changelogi.as_ref();
+    let rev = changelogi.len() / CHANGELOG_ENTRY_SIZE;
+    if rev > u32::MAX as usize {
+        panic!("rev exceeds 32 bit integers")
+    }
+    rev as u32
+}
+
+/// Helper method similar to `radixbuf::key::FixedKey::read`, but takes a revision number instead.
+pub fn rev_to_node<K: AsRef<[u8]>>(changelogi: &K, rev: KeyId) -> rerrors::Result<&[u8]> {
+    let buf = changelogi.as_ref();
+    let rev_usize: usize = rev.into();
+    let start_pos = rev_usize * CHANGELOG_ENTRY_SIZE + CHANGELOG_ENTRY_NODE_OFFSET;
+    let end_pos = start_pos + 20;
+    if buf.len() < end_pos {
+        Err(rerrors::ErrorKind::InvalidKeyId(rev).into())
+    } else {
+        Ok(&buf[start_pos..end_pos])
+    }
+}
diff --git a/rust/indexes/src/lib.rs b/rust/indexes/src/lib.rs
--- a/rust/indexes/src/lib.rs
+++ b/rust/indexes/src/lib.rs
@@ -13,6 +13,7 @@
 
 pub mod errors;
 pub mod nodemap;
+mod changelog_utils;
 mod pybuf;
 
 #[allow(non_camel_case_types)]
diff --git a/rust/indexes/src/nodemap.rs b/rust/indexes/src/nodemap.rs
--- a/rust/indexes/src/nodemap.rs
+++ b/rust/indexes/src/nodemap.rs
@@ -9,6 +9,7 @@
                       RADIX_NCHILDREN};
 use radixbuf::key::KeyId;
 use radixbuf::errors as rerrors;
+use changelog_utils::{changelog_end_rev, rev_to_node};
 
 /// An index for node to rev lookups.
 ///
@@ -73,10 +74,7 @@
 const MAIN_RADIX_OFFSET: u32 = 1;
 const SIDE_RADIX_OFFSET: u32 = 0;
 
-const CHANGELOG_ENTRY_SIZE: u64 = 64;
-
-impl<C: AsRef<[u8]>, I: AsRef<[u32]>> NodeRevMap<C, I>
-{
+impl<C: AsRef<[u8]>, I: AsRef<[u32]>> NodeRevMap<C, I> {
     /// Initialize NodeMap from a non-inlined version of changelog.i and an incomplete index.
     pub fn new(changelogi: C, main_index: I) -> Result<Self> {
         // Sanity check if the index is corrupted or not.
@@ -197,16 +195,6 @@
     }
 }
 
-/// Return the minimal revision number the changelog.i does not have.
-fn changelog_end_rev<T: AsRef<[u8]>>(changelogi: &T) -> u32 {
-    let changelogi = changelogi.as_ref();
-    let rev = changelogi.len() as u64 / CHANGELOG_ENTRY_SIZE;
-    if rev > u32::MAX as u64 {
-        panic!("rev exceeds 32 bit integers")
-    }
-    rev as u32
-}
-
 /// Read the given range of revisions (from `start_rev` (inclusive) to
 /// `end_rev` (exclusive)) from changelogi. Insert them to the radix
 /// index.
@@ -225,19 +213,6 @@
     Ok(())
 }
 
-/// Helper method similar to `radixbuf::key::FixedKey::read`, but takes a revision number instead.
-fn rev_to_node<K: AsRef<[u8]>>(changelogi: &K, rev: KeyId) -> rerrors::Result<&[u8]> {
-    let buf = changelogi.as_ref();
-    let rev_usize: usize = rev.into();
-    let start_pos = rev_usize * 64 + 32;
-    let end_pos = start_pos + 20;
-    if buf.len() < end_pos {
-        Err(rerrors::ErrorKind::InvalidKeyId(rev).into())
-    } else {
-        Ok(&buf[start_pos..end_pos])
-    }
-}
-
 /// Convert hex base16 sequence to binary base16 sequence.
 fn hex_to_bin_base16<T: AsRef<[u8]>>(base16: T) -> Option<Vec<u8>> {
     let base16 = base16.as_ref();