Skip to content

Commit 64d2135

Browse files
committed
♻️ Replace permission String allocation with Display impl
PermissionDisplay writes 11 chars directly to the formatter, eliminating heap allocation in bsd_tar_list_entries_to. JsonL/CSV paths call .to_string() only where serialization requires it.
1 parent 360ff46 commit 64d2135

1 file changed

Lines changed: 64 additions & 60 deletions

File tree

cli/src/command/list.rs

Lines changed: 64 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -782,9 +782,8 @@ fn bsd_tar_list_entries_to(
782782
for row in entries {
783783
let nlink = 0; // BSD tar show always 0
784784
let permission = row.permission_mode();
785-
let has_xattr = !row.xattrs.is_empty();
786785
let has_acl = !row.acl.is_empty();
787-
let perm = bsdtar_permission_string(&row.entry_type, permission, has_acl);
786+
let perm = PermissionDisplay::bsdtar(&row.entry_type, permission, has_acl);
788787
let size = row.raw_size.unwrap_or(0);
789788
let mtime = bsd_tar_time(now, row.modified.unwrap_or(now));
790789
let (uname, gname) = match &row.permission {
@@ -1155,63 +1154,64 @@ const fn kind_char(kind: &EntryType) -> char {
11551154
}
11561155
}
11571156

1158-
const fn bsdtar_kind_char(kind: &EntryType) -> char {
1159-
match kind {
1160-
EntryType::File(_) => '-',
1161-
EntryType::HardLink(_, _) => 'h',
1162-
EntryType::Directory(_) => 'd',
1163-
EntryType::SymbolicLink(_, _) => 'l',
1164-
}
1157+
struct PermissionDisplay {
1158+
kind: char,
1159+
permission: u16,
1160+
indicator: char,
11651161
}
11661162

1167-
fn permission_string(kind: &EntryType, permission: u16, has_xattr: bool, has_acl: bool) -> String {
1168-
#[inline(always)]
1169-
const fn paint(permission: u16, c: char, bit: u16) -> char {
1170-
if permission & bit != 0 { c } else { '-' }
1163+
impl PermissionDisplay {
1164+
const fn new(kind: &EntryType, permission: u16, has_xattr: bool, has_acl: bool) -> Self {
1165+
Self {
1166+
kind: kind_char(kind),
1167+
permission,
1168+
indicator: if has_xattr {
1169+
'@'
1170+
} else if has_acl {
1171+
'+'
1172+
} else {
1173+
' '
1174+
},
1175+
}
11711176
}
11721177

1173-
format!(
1174-
"{}{}{}{}{}{}{}{}{}{}{}",
1175-
kind_char(kind),
1176-
paint(permission, 'r', 0b100000000), // owner_read
1177-
paint(permission, 'w', 0b010000000), // owner_write
1178-
paint(permission, 'x', 0b001000000), // owner_exec
1179-
paint(permission, 'r', 0b000100000), // group_read
1180-
paint(permission, 'w', 0b000010000), // group_write
1181-
paint(permission, 'x', 0b000001000), // group_exec
1182-
paint(permission, 'r', 0b000000100), // other_read
1183-
paint(permission, 'w', 0b000000010), // other_write
1184-
paint(permission, 'x', 0b000000001), // other_exec
1185-
if has_xattr {
1186-
'@'
1187-
} else if has_acl {
1188-
'+'
1189-
} else {
1190-
' '
1191-
},
1192-
)
1178+
/// Match bsdtar's `archive_entry_strmode`: `'h'` for hardlinks,
1179+
/// only `'+'` for ACL (no `'@'` for xattr).
1180+
const fn bsdtar(kind: &EntryType, permission: u16, has_acl: bool) -> Self {
1181+
Self {
1182+
kind: match kind {
1183+
EntryType::File(_) => '-',
1184+
EntryType::HardLink(_, _) => 'h',
1185+
EntryType::Directory(_) => 'd',
1186+
EntryType::SymbolicLink(_, _) => 'l',
1187+
},
1188+
permission,
1189+
indicator: if has_acl { '+' } else { ' ' },
1190+
}
1191+
}
11931192
}
11941193

1195-
fn bsdtar_permission_string(kind: &EntryType, permission: u16, has_acl: bool) -> String {
1196-
#[inline(always)]
1197-
const fn paint(permission: u16, c: char, bit: u16) -> char {
1198-
if permission & bit != 0 { c } else { '-' }
1194+
impl fmt::Display for PermissionDisplay {
1195+
#[inline]
1196+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1197+
#[inline(always)]
1198+
const fn paint(permission: u16, c: char, bit: u16) -> char {
1199+
if permission & bit != 0 { c } else { '-' }
1200+
}
1201+
use core::fmt::Write;
1202+
let p = self.permission;
1203+
f.write_char(self.kind)?;
1204+
f.write_char(paint(p, 'r', 0b100000000))?; // owner_read
1205+
f.write_char(paint(p, 'w', 0b010000000))?; // owner_write
1206+
f.write_char(paint(p, 'x', 0b001000000))?; // owner_exec
1207+
f.write_char(paint(p, 'r', 0b000100000))?; // group_read
1208+
f.write_char(paint(p, 'w', 0b000010000))?; // group_write
1209+
f.write_char(paint(p, 'x', 0b000001000))?; // group_exec
1210+
f.write_char(paint(p, 'r', 0b000000100))?; // other_read
1211+
f.write_char(paint(p, 'w', 0b000000010))?; // other_write
1212+
f.write_char(paint(p, 'x', 0b000000001))?; // other_exec
1213+
f.write_char(self.indicator)
11991214
}
1200-
1201-
format!(
1202-
"{}{}{}{}{}{}{}{}{}{}{}",
1203-
bsdtar_kind_char(kind),
1204-
paint(permission, 'r', 0b100000000),
1205-
paint(permission, 'w', 0b010000000),
1206-
paint(permission, 'x', 0b001000000),
1207-
paint(permission, 'r', 0b000100000),
1208-
paint(permission, 'w', 0b000010000),
1209-
paint(permission, 'x', 0b000001000),
1210-
paint(permission, 'r', 0b000000100),
1211-
paint(permission, 'w', 0b000000010),
1212-
paint(permission, 'x', 0b000000001),
1213-
if has_acl { '+' } else { ' ' },
1214-
)
12151215
}
12161216

12171217
#[derive(Serialize, Debug)]
@@ -1269,12 +1269,13 @@ fn json_line_entries_to(
12691269
.map_or_else(String::new, |it| it.gname().to_string());
12701270
FileInfo {
12711271
filename: it.entry_type.name(),
1272-
permissions: permission_string(
1272+
permissions: PermissionDisplay::new(
12731273
&it.entry_type,
12741274
permission_mode,
12751275
!it.xattrs.is_empty(),
12761276
!it.acl.is_empty(),
1277-
),
1277+
)
1278+
.to_string(),
12781279
owner,
12791280
group,
12801281
raw_size: it.raw_size.unwrap_or_default(),
@@ -1381,12 +1382,15 @@ fn delimited_entries_to(
13811382

13821383
[
13831384
Some(row.entry_type.name().to_string()),
1384-
Some(permission_string(
1385-
&row.entry_type,
1386-
permission_mode,
1387-
!row.xattrs.is_empty(),
1388-
!row.acl.is_empty(),
1389-
)),
1385+
Some(
1386+
PermissionDisplay::new(
1387+
&row.entry_type,
1388+
permission_mode,
1389+
!row.xattrs.is_empty(),
1390+
!row.acl.is_empty(),
1391+
)
1392+
.to_string(),
1393+
),
13901394
Some(owner),
13911395
Some(group),
13921396
Some(row.raw_size.unwrap_or(0).to_string()),

0 commit comments

Comments
 (0)