Skip to content

Commit f79de29

Browse files
Remove EntryModeRef. Makes it worse (~30% worse on top of previous commit being ~30% worse than main)
1 parent 27149ee commit f79de29

File tree

6 files changed

+58
-188
lines changed

6 files changed

+58
-188
lines changed

gix-object/src/object/convert.rs

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -82,53 +82,29 @@ impl<'a> From<&'a tree::Entry> for tree::EntryRef<'a> {
8282
fn from(other: &'a tree::Entry) -> tree::EntryRef<'a> {
8383
let tree::Entry { mode, filename, oid } = other;
8484
tree::EntryRef {
85-
mode: mode.into(),
85+
mode: *mode,
8686
filename: filename.as_ref(),
8787
oid,
8888
}
8989
}
9090
}
9191

92-
impl<'a> TryFrom<&'a [u8]> for tree::EntryModeRef<'a> {
92+
impl<'a> TryFrom<&'a [u8]> for tree::EntryMode {
9393
type Error = &'a [u8];
9494

9595
fn try_from(mode: &'a [u8]) -> Result<Self, Self::Error> {
96-
let _can_be_parsed = tree::parse_git_mode(mode).ok_or(mode)?;
9796
let len = if let Some(delim) = mode.iter().position(|b| *b == b' ') {
9897
delim
9998
} else {
10099
mode.len()
101100
};
102-
Ok(Self { mode: &mode[..len] })
103-
}
104-
}
105-
106-
impl<'a> TryFrom<&'a [u8]> for tree::EntryMode {
107-
type Error = &'a [u8];
108-
109-
fn try_from(mode: &'a [u8]) -> Result<Self, Self::Error> {
110-
Ok(tree::EntryModeRef::try_from(mode)?.into())
111-
}
112-
}
113-
114-
impl From<tree::EntryModeRef<'_>> for tree::EntryMode {
115-
fn from(other: tree::EntryModeRef<'_>) -> tree::EntryMode {
116-
let tree::EntryModeRef { mode } = other;
117-
tree::EntryMode {
118-
git_representation: mode.to_owned().into(),
119-
value: tree::parse_git_mode(mode)
120-
.expect("By construction, the mode slice in an EntryModeRef is guaranteed to be valid and parse-able"),
121-
}
122-
}
123-
}
124-
125-
impl<'a> From<&'a tree::EntryMode> for tree::EntryModeRef<'a> {
126-
fn from(other: &'a tree::EntryMode) -> tree::EntryModeRef<'a> {
127-
let tree::EntryMode {
128-
git_representation: mode,
129-
..
130-
} = other;
131-
tree::EntryModeRef { mode: mode.as_ref() }
101+
let mut git_representation = [b' '; 6];
102+
git_representation[..len].copy_from_slice(&mode[..len]);
103+
let value = tree::parse_git_mode(&git_representation).ok_or(mode)?;
104+
Ok(Self {
105+
value,
106+
git_representation,
107+
})
132108
}
133109
}
134110

gix-object/src/tree/mod.rs

Lines changed: 33 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -36,112 +36,6 @@ pub struct Editor<'a> {
3636
tree_buf: Vec<u8>,
3737
}
3838

39-
/// The mode of items storable in a tree, similar to the file mode on a unix file system.
40-
///
41-
/// We use the Git backing representation to avoid the need for allocations.
42-
///
43-
/// Used in [`EntryRef`].
44-
///
45-
/// Note that even though it can be created from any `u16`, it should be preferable to
46-
/// create it by converting [`EntryKind`] into `EntryMode`.
47-
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
48-
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49-
pub struct EntryModeRef<'a> {
50-
pub(crate) mode: &'a [u8],
51-
}
52-
53-
impl std::fmt::Debug for EntryModeRef<'_> {
54-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55-
write!(
56-
f,
57-
"EntryModeRef({})",
58-
std::str::from_utf8(self.mode).expect("self.mode is valid UTF-8 by construction")
59-
)
60-
}
61-
}
62-
63-
impl std::fmt::Octal for EntryModeRef<'_> {
64-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65-
write!(
66-
f,
67-
"{}",
68-
std::str::from_utf8(self.mode).expect("self.mode is valid UTF-8 by construction")
69-
)
70-
}
71-
}
72-
73-
impl EntryModeRef<'_> {
74-
/// Convert this instance into its own version, creating a copy of all data.
75-
///
76-
/// This will temporarily allocate an extra copy in memory, so at worst three copies of the tree exist
77-
/// at some intermediate point in time. Use [`Self::into_owned()`] to avoid this.
78-
pub fn to_owned(&self) -> EntryMode {
79-
(*self).into_owned()
80-
}
81-
82-
/// Convert this instance into its own version, creating a copy of all data.
83-
pub fn into_owned(self) -> EntryMode {
84-
self.into()
85-
}
86-
}
87-
88-
impl EntryModeRef<'_> {
89-
/// Discretize the raw mode into an enum with well-known state while dropping unnecessary details.
90-
pub const fn kind(&self) -> EntryKind {
91-
if let Some(value) = parse_git_mode(self.mode) {
92-
parse_entry_kind_from_value(value)
93-
} else {
94-
// Won't happen by construction.
95-
// We swallow the error to allow this fn to be a `const fn`
96-
EntryKind::Commit
97-
}
98-
}
99-
100-
/// Return true if this entry mode represents a Tree/directory
101-
pub const fn is_tree(&self) -> bool {
102-
matches!(self.kind(), EntryKind::Tree)
103-
}
104-
105-
/// Return true if this entry mode represents the commit of a submodule.
106-
pub const fn is_commit(&self) -> bool {
107-
matches!(self.kind(), EntryKind::Commit)
108-
}
109-
110-
/// Return true if this entry mode represents a symbolic link
111-
pub const fn is_link(&self) -> bool {
112-
matches!(self.kind(), EntryKind::Link)
113-
}
114-
115-
/// Return true if this entry mode represents anything BUT Tree/directory
116-
pub const fn is_no_tree(&self) -> bool {
117-
!matches!(self.kind(), EntryKind::Tree)
118-
}
119-
120-
/// Return true if the entry is any kind of blob.
121-
pub const fn is_blob(&self) -> bool {
122-
matches!(self.kind(), EntryKind::Blob | EntryKind::BlobExecutable)
123-
}
124-
125-
/// Return true if the entry is an executable blob.
126-
pub const fn is_executable(&self) -> bool {
127-
matches!(self.kind(), EntryKind::BlobExecutable)
128-
}
129-
130-
/// Return true if the entry is any kind of blob or symlink.
131-
pub const fn is_blob_or_symlink(&self) -> bool {
132-
matches!(
133-
self.kind(),
134-
EntryKind::Blob | EntryKind::BlobExecutable | EntryKind::Link
135-
)
136-
}
137-
138-
/// Return the representation as used in the git internal format, which is octal and written
139-
/// to the `backing` buffer. The respective sub-slice that was written to is returned.
140-
pub fn as_bytes(&self) -> &'_ BStr {
141-
self.mode.into()
142-
}
143-
}
144-
14539
/// Parse a valid git mode into a u16
14640
/// A valid git mode can be represented by a set of 5-6 octal digits. The leftmost octal digit can
14741
/// be at most one. These conditions guarantee that it can be represented as a `u16`, although that
@@ -220,22 +114,22 @@ impl TryFrom<u32> for tree::EntryMode {
220114
///
221115
/// Note that even though it can be created from any `u16`, it should be preferable to
222116
/// create it by converting [`EntryKind`] into `EntryMode`.
223-
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
117+
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
224118
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
225119
pub struct EntryMode {
226120
pub(crate) value: u16,
227-
pub(crate) git_representation: BString,
121+
pub(crate) git_representation: [u8; 6],
228122
}
229123

230124
impl std::fmt::Debug for EntryMode {
231125
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232-
write!(f, "EntryMode(0o{})", self.git_representation)
126+
write!(f, "EntryMode(0o{})", self.as_bstr())
233127
}
234128
}
235129

236130
impl std::fmt::Octal for EntryMode {
237131
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
238-
write!(f, "{}", self.git_representation)
132+
write!(f, "{}", self.as_bstr())
239133
}
240134
}
241135

@@ -286,10 +180,8 @@ impl From<u16> for EntryKind {
286180

287181
impl From<u16> for EntryMode {
288182
fn from(value: u16) -> Self {
289-
Self {
290-
value,
291-
git_representation: format!("{value:o}").into(),
292-
}
183+
let kind: EntryKind = value.into();
184+
Self::from(kind)
293185
}
294186
}
295187

@@ -301,23 +193,22 @@ impl From<EntryMode> for u16 {
301193

302194
impl From<EntryMode> for EntryKind {
303195
fn from(value: EntryMode) -> Self {
304-
EntryModeRef::from(&value).kind()
196+
EntryMode::from(value).kind()
305197
}
306198
}
307199

308200
/// Serialization
309201
impl EntryKind {
310202
/// Return the representation as used in the git internal format.
311-
pub fn as_octal_str(&self) -> &'static BStr {
203+
pub fn as_octal_str(&self) -> &'static [u8] {
312204
use EntryKind::*;
313-
let bytes: &[u8] = match self {
205+
match self {
314206
Tree => b"40000",
315207
Blob => b"100644",
316208
BlobExecutable => b"100755",
317209
Link => b"120000",
318210
Commit => b"160000",
319-
};
320-
bytes.into()
211+
}
321212
}
322213
/// Return the representation as a human readable description
323214
pub fn as_descriptive_str(&self) -> &'static str {
@@ -331,18 +222,13 @@ impl EntryKind {
331222
}
332223
}
333224
}
334-
impl From<EntryKind> for EntryModeRef<'_> {
335-
fn from(value: EntryKind) -> Self {
336-
EntryModeRef {
337-
mode: value.as_octal_str(),
338-
}
339-
}
340-
}
341225

342226
impl From<EntryKind> for EntryMode {
343227
fn from(value: EntryKind) -> Self {
228+
let mut git_representation = [b' '; 6];
229+
git_representation[..value.as_octal_str().len()].copy_from_slice(value.as_octal_str());
344230
EntryMode {
345-
git_representation: value.as_octal_str().to_owned(),
231+
git_representation,
346232
value: value as u16,
347233
}
348234
}
@@ -392,10 +278,25 @@ impl EntryMode {
392278
)
393279
}
394280

281+
/// How many bytes of the backing representation are significant?
282+
pub fn len(&self) -> usize {
283+
if let Some(delim) = self.git_representation.iter().position(|b| *b == b' ') {
284+
delim
285+
} else {
286+
self.git_representation.len()
287+
}
288+
}
289+
395290
/// Return the representation as used in the git internal format, which is octal and written
396291
/// to the `backing` buffer. The respective sub-slice that was written to is returned.
397-
pub fn as_bytes(&self) -> &'_ BStr {
398-
self.git_representation.as_bstr()
292+
pub fn as_bytes(&self) -> &'_ [u8] {
293+
&self.git_representation[..self.len()]
294+
}
295+
296+
/// Return the representation as used in the git internal format, which is octal and written
297+
/// to the `backing` buffer. The respective sub-slice that was written to is returned.
298+
pub fn as_bstr(&self) -> &'_ BStr {
299+
self.as_bytes().as_bstr()
399300
}
400301
}
401302

@@ -419,7 +320,7 @@ impl TreeRef<'_> {
419320
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
420321
pub struct EntryRef<'a> {
421322
/// The kind of object to which `oid` is pointing.
422-
pub mode: tree::EntryModeRef<'a>,
323+
pub mode: tree::EntryMode,
423324
/// The name of the file in the parent tree.
424325
pub filename: &'a BStr,
425326
/// The id of the object representing the entry.
@@ -470,14 +371,8 @@ impl Ord for Entry {
470371
let a = self;
471372
let common = a.filename.len().min(b.filename.len());
472373
a.filename[..common].cmp(&b.filename[..common]).then_with(|| {
473-
let a = a
474-
.filename
475-
.get(common)
476-
.or_else(|| EntryModeRef::from(&a.mode).is_tree().then_some(&b'/'));
477-
let b = b
478-
.filename
479-
.get(common)
480-
.or_else(|| EntryModeRef::from(&b.mode).is_tree().then_some(&b'/'));
374+
let a = a.filename.get(common).or_else(|| a.mode.is_tree().then_some(&b'/'));
375+
let b = b.filename.get(common).or_else(|| b.mode.is_tree().then_some(&b'/'));
481376
a.cmp(&b)
482377
})
483378
}

gix-object/src/tree/ref_iter.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ mod decode {
179179

180180
pub fn fast_entry(i: &[u8]) -> Option<(&[u8], EntryRef<'_>)> {
181181
let (_, (mode_slice, i)) = extract_git_mode(i)?;
182-
let mode_ref = tree::EntryModeRef::try_from(mode_slice).ok()?;
182+
let mode = tree::EntryMode::try_from(mode_slice).ok()?;
183183
let (filename, i) = i.split_at(i.find_byte(0)?);
184184
let i = &i[1..];
185185
const HASH_LEN_FIXME: usize = 20; // TODO(SHA256): know actual/desired length or we may overshoot
@@ -190,7 +190,7 @@ mod decode {
190190
Some((
191191
i,
192192
EntryRef {
193-
mode: mode_ref,
193+
mode,
194194
filename: filename.as_bstr(),
195195
oid: gix_hash::oid::try_from_bytes(oid).expect("we counted exactly 20 bytes"),
196196
},

gix-object/src/tree/write.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use bstr::{BString, ByteSlice};
44

55
use crate::{
66
encode::SPACE,
7-
tree::{Entry, EntryModeRef, EntryRef},
7+
tree::{Entry, EntryRef},
88
Kind, Tree, TreeRef,
99
};
1010

@@ -36,7 +36,7 @@ impl crate::WriteTo for Tree {
3636
"entries for serialization must be sorted by filename"
3737
);
3838
for Entry { mode, filename, oid } in &self.entries {
39-
out.write_all(EntryModeRef::from(mode).as_bytes())?;
39+
out.write_all(mode.as_bytes())?;
4040
out.write_all(SPACE)?;
4141

4242
if filename.find_byte(0).is_some() {
@@ -60,9 +60,7 @@ impl crate::WriteTo for Tree {
6060
fn size(&self) -> u64 {
6161
self.entries
6262
.iter()
63-
.map(|Entry { mode, filename, oid }| {
64-
(EntryModeRef::from(mode).as_bytes().len() + 1 + filename.len() + 1 + oid.as_bytes().len()) as u64
65-
})
63+
.map(|Entry { mode, filename, oid }| (mode.len() + 1 + filename.len() + 1 + oid.as_bytes().len()) as u64)
6664
.sum()
6765
}
6866
}
@@ -105,9 +103,7 @@ impl crate::WriteTo for TreeRef<'_> {
105103
fn size(&self) -> u64 {
106104
self.entries
107105
.iter()
108-
.map(|EntryRef { mode, filename, oid }| {
109-
(mode.as_bytes().len() + 1 + filename.len() + 1 + oid.as_bytes().len()) as u64
110-
})
106+
.map(|EntryRef { mode, filename, oid }| (mode.len() + 1 + filename.len() + 1 + oid.as_bytes().len()) as u64)
111107
.sum()
112108
}
113109
}

gix-object/tests/object/tree/editor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ mod utils {
912912
"{} {}.{}",
913913
entry.filename,
914914
entry.oid,
915-
entry.mode.kind().as_octal_str()
915+
entry.mode.as_bstr()
916916
));
917917
}
918918
}

0 commit comments

Comments
 (0)