Skip to content

Commit 9ad9c5b

Browse files
committed
Merge branch 'perf-and-safety'
2 parents b7560a2 + 96a07e0 commit 9ad9c5b

File tree

16 files changed

+115
-89
lines changed

16 files changed

+115
-89
lines changed

gix-filter/src/worktree/encode_to_git.rs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ pub(crate) mod function {
2626
use encoding_rs::DecoderResult;
2727

2828
use super::{Error, RoundTripCheck};
29-
use crate::clear_and_set_capacity;
3029

3130
/// Decode `src` according to `src_encoding` to `UTF-8` for storage in git and place it in `buf`.
3231
/// Note that the encoding is always applied, there is no conditional even if `src_encoding` already is `UTF-8`.
@@ -40,13 +39,8 @@ pub(crate) mod function {
4039
let buf_len = decoder
4140
.max_utf8_buffer_length_without_replacement(src.len())
4241
.ok_or(Error::Overflow { input_len: src.len() })?;
43-
clear_and_set_capacity(buf, buf_len);
44-
// SAFETY: `clear_and_set_capacity` assure that we have the given `buf_len` allocated, so setting its length is only making available
45-
// what is allocated. Later we will truncate to the amount of actually written bytes.
46-
#[allow(unsafe_code)]
47-
unsafe {
48-
buf.set_len(buf_len);
49-
}
42+
buf.clear();
43+
buf.resize(buf_len, 0);
5044
let (res, read, written) = decoder.decode_to_utf8_without_replacement(src, buf, true);
5145
match res {
5246
DecoderResult::InputEmpty => {
@@ -55,11 +49,7 @@ pub(crate) mod function {
5549
"encoding_rs estimates the maximum amount of bytes written correctly"
5650
);
5751
assert_eq!(read, src.len(), "input buffer should be fully consumed");
58-
// SAFETY: we trust that `encoding_rs` reports this number correctly, and truncate everything else.
59-
#[allow(unsafe_code)]
60-
unsafe {
61-
buf.set_len(written);
62-
}
52+
buf.truncate(written);
6353
}
6454
DecoderResult::OutputFull => {
6555
unreachable!("we assure that the output buffer is big enough as per the encoder's estimate")

gix-filter/src/worktree/encode_to_worktree.rs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ pub(crate) mod function {
1717
use encoding_rs::EncoderResult;
1818

1919
use super::Error;
20-
use crate::clear_and_set_capacity;
2120

2221
/// Encode `src_utf8`, which is assumed to be UTF-8 encoded, according to `worktree_encoding` for placement in the working directory,
2322
/// and write it to `buf`, possibly resizing it.
@@ -33,13 +32,8 @@ pub(crate) mod function {
3332
.ok_or(Error::Overflow {
3433
input_len: src_utf8.len(),
3534
})?;
36-
clear_and_set_capacity(buf, buf_len);
37-
// SAFETY: `clear_and_set_capacity` assure that we have the given `buf_len` allocated, so setting its length is only making available
38-
// what is allocated. Later we will truncate to the amount of actually written bytes.
39-
#[allow(unsafe_code)]
40-
unsafe {
41-
buf.set_len(buf_len);
42-
}
35+
buf.clear();
36+
buf.resize(buf_len, 0);
4337
let src = std::str::from_utf8(src_utf8)?;
4438
let (res, read, written) = encoder.encode_from_utf8_without_replacement(src, buf, true);
4539
match res {
@@ -49,11 +43,7 @@ pub(crate) mod function {
4943
"encoding_rs estimates the maximum amount of bytes written correctly"
5044
);
5145
assert_eq!(read, src_utf8.len(), "input buffer should be fully consumed");
52-
// SAFETY: we trust that `encoding_rs` reports this number correctly, and truncate everything else.
53-
#[allow(unsafe_code)]
54-
unsafe {
55-
buf.set_len(written);
56-
}
46+
buf.truncate(written);
5747
}
5848
EncoderResult::OutputFull => {
5949
unreachable!("we assure that the output buffer is big enough as per the encoder's estimate")

gix-odb/src/store_impls/dynamic/find.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ pub(crate) mod error {
7575
}
7676
}
7777
pub use error::Error;
78+
use gix_features::zlib;
7879

7980
use crate::{store::types::PackId, Find};
8081

@@ -86,6 +87,7 @@ where
8687
&'b self,
8788
mut id: &'b gix_hash::oid,
8889
buffer: &'a mut Vec<u8>,
90+
inflate: &mut zlib::Inflate,
8991
pack_cache: &mut impl DecodeEntry,
9092
snapshot: &mut load_index::Snapshot,
9193
recursion: Option<error::DeltaBaseRecursion<'_>>,
@@ -147,6 +149,7 @@ where
147149
let res = match pack.decode_entry(
148150
entry,
149151
buffer,
152+
inflate,
150153
|id, _out| {
151154
index_file.pack_offset_by_id(id).map(|pack_offset| {
152155
gix_pack::data::decode::entry::ResolvedBase::InPack(pack.entry(pack_offset))
@@ -182,6 +185,7 @@ where
182185
.try_find_cached_inner(
183186
&base_id,
184187
&mut buf,
188+
inflate,
185189
pack_cache,
186190
snapshot,
187191
recursion
@@ -231,6 +235,7 @@ where
231235
pack.decode_entry(
232236
entry,
233237
buffer,
238+
inflate,
234239
|id, out| {
235240
index_file
236241
.pack_offset_by_id(id)
@@ -347,7 +352,8 @@ where
347352
) -> Result<Option<(gix_object::Data<'a>, Option<gix_pack::data::entry::Location>)>, Self::Error> {
348353
let id = id.as_ref();
349354
let mut snapshot = self.snapshot.borrow_mut();
350-
self.try_find_cached_inner(id, buffer, pack_cache, &mut snapshot, None)
355+
let mut inflate = self.inflate.borrow_mut();
356+
self.try_find_cached_inner(id, buffer, &mut inflate, pack_cache, &mut snapshot, None)
351357
}
352358

353359
fn location_by_oid(
@@ -364,6 +370,7 @@ where
364370

365371
let id = id.as_ref();
366372
let mut snapshot = self.snapshot.borrow_mut();
373+
let mut inflate = self.inflate.borrow_mut();
367374
'outer: loop {
368375
{
369376
let marker = snapshot.marker;
@@ -404,13 +411,14 @@ where
404411
buf.resize(entry.decompressed_size.try_into().expect("representable size"), 0);
405412
assert_eq!(pack.id, pack_id.to_intrinsic_pack_id(), "both ids must always match");
406413

407-
let res = pack.decompress_entry(&entry, buf).ok().map(|entry_size_past_header| {
408-
gix_pack::data::entry::Location {
414+
let res = pack
415+
.decompress_entry(&entry, &mut inflate, buf)
416+
.ok()
417+
.map(|entry_size_past_header| gix_pack::data::entry::Location {
409418
pack_id: pack.id,
410419
pack_offset,
411420
entry_size: entry.header_size() + entry_size_past_header,
412-
}
413-
});
421+
});
414422

415423
if idx != 0 {
416424
snapshot.indices.swap(0, idx);

gix-odb/src/store_impls/dynamic/handle.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ impl super::Store {
257257
refresh: RefreshMode::default(),
258258
ignore_replacements: false,
259259
token: Some(token),
260+
inflate: RefCell::new(Default::default()),
260261
snapshot: RefCell::new(self.collect_snapshot()),
261262
max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH,
262263
packed_object_count: Default::default(),
@@ -273,6 +274,7 @@ impl super::Store {
273274
refresh: Default::default(),
274275
ignore_replacements: false,
275276
token: Some(token),
277+
inflate: RefCell::new(Default::default()),
276278
snapshot: RefCell::new(self.collect_snapshot()),
277279
max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH,
278280
packed_object_count: Default::default(),
@@ -391,6 +393,7 @@ where
391393
}
392394
.into()
393395
},
396+
inflate: RefCell::new(Default::default()),
394397
snapshot: RefCell::new(self.store.collect_snapshot()),
395398
max_recursion_depth: self.max_recursion_depth,
396399
packed_object_count: Default::default(),

gix-odb/src/store_impls/dynamic/header.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use gix_features::zlib;
12
use std::ops::Deref;
23

34
use gix_hash::oid;
@@ -15,6 +16,7 @@ where
1516
fn try_header_inner<'b>(
1617
&'b self,
1718
mut id: &'b gix_hash::oid,
19+
inflate: &mut zlib::Inflate,
1820
snapshot: &mut load_index::Snapshot,
1921
recursion: Option<DeltaBaseRecursion<'_>>,
2022
) -> Result<Option<Header>, Error> {
@@ -71,7 +73,7 @@ where
7173
},
7274
};
7375
let entry = pack.entry(pack_offset);
74-
let res = match pack.decode_header(entry, |id| {
76+
let res = match pack.decode_header(entry, inflate, |id| {
7577
index_file.pack_offset_by_id(id).map(|pack_offset| {
7678
gix_pack::data::decode::header::ResolvedBase::InPack(pack.entry(pack_offset))
7779
})
@@ -85,6 +87,7 @@ where
8587
let hdr = self
8688
.try_header_inner(
8789
&base_id,
90+
inflate,
8891
snapshot,
8992
recursion
9093
.map(DeltaBaseRecursion::inc_depth)
@@ -127,7 +130,7 @@ where
127130
.as_ref()
128131
.expect("pack to still be available like just now");
129132
let entry = pack.entry(pack_offset);
130-
pack.decode_header(entry, |id| {
133+
pack.decode_header(entry, inflate, |id| {
131134
index_file
132135
.pack_offset_by_id(id)
133136
.map(|pack_offset| {
@@ -184,6 +187,7 @@ where
184187
fn try_header(&self, id: impl AsRef<oid>) -> Result<Option<Header>, Self::Error> {
185188
let id = id.as_ref();
186189
let mut snapshot = self.snapshot.borrow_mut();
187-
self.try_header_inner(id, &mut snapshot, None)
190+
let mut inflate = self.inflate.borrow_mut();
191+
self.try_header_inner(id, &mut inflate, &mut snapshot, None)
188192
}
189193
}

gix-odb/src/store_impls/dynamic/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! The standard object store which should fit all needs.
2+
use gix_features::zlib;
23
use std::{cell::RefCell, ops::Deref};
34

45
use crate::Store;
@@ -24,6 +25,7 @@ where
2425

2526
pub(crate) token: Option<handle::Mode>,
2627
snapshot: RefCell<load_index::Snapshot>,
28+
inflate: RefCell<zlib::Inflate>,
2729
packed_object_count: RefCell<Option<u64>>,
2830
}
2931

gix-pack/src/bundle/find.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
1+
use gix_features::zlib;
2+
13
impl crate::Bundle {
2-
/// Find an object with the given [`ObjectId`][gix_hash::ObjectId] and place its data into `out`.
4+
/// Find an object with the given [`ObjectId`](gix_hash::ObjectId) and place its data into `out`.
5+
/// `inflate` is used to decompress objects, and will be reset before first use, but not after the last use.
36
///
4-
/// [`cache`][crate::cache::DecodeEntry] is used to accelerate the lookup.
7+
/// [`cache`](crate::cache::DecodeEntry) is used to accelerate the lookup.
58
///
69
/// **Note** that ref deltas are automatically resolved within this pack only, which makes this implementation unusable
710
/// for thin packs, which by now are expected to be resolved already.
811
pub fn find<'a>(
912
&self,
1013
id: impl AsRef<gix_hash::oid>,
1114
out: &'a mut Vec<u8>,
15+
inflate: &mut zlib::Inflate,
1216
cache: &mut impl crate::cache::DecodeEntry,
1317
) -> Result<Option<(gix_object::Data<'a>, crate::data::entry::Location)>, crate::data::decode::Error> {
1418
let idx = match self.index.lookup(id) {
1519
Some(idx) => idx,
1620
None => return Ok(None),
1721
};
18-
self.get_object_by_index(idx, out, cache).map(Some)
22+
self.get_object_by_index(idx, out, inflate, cache).map(Some)
1923
}
2024

2125
/// Special-use function to get an object given an index previously returned from
22-
/// `internal_find_pack_index`.
26+
/// [index::File::](crate::index::File::lookup()).
27+
/// `inflate` is used to decompress objects, and will be reset before first use, but not after the last use.
2328
///
2429
/// # Panics
2530
///
@@ -28,6 +33,7 @@ impl crate::Bundle {
2833
&self,
2934
idx: u32,
3035
out: &'a mut Vec<u8>,
36+
inflate: &mut zlib::Inflate,
3137
cache: &mut impl crate::cache::DecodeEntry,
3238
) -> Result<(gix_object::Data<'a>, crate::data::entry::Location), crate::data::decode::Error> {
3339
let ofs = self.index.pack_offset_at_index(idx);
@@ -37,6 +43,7 @@ impl crate::Bundle {
3743
.decode_entry(
3844
pack_entry,
3945
out,
46+
inflate,
4047
|id, _out| {
4148
self.index.lookup(id).map(|idx| {
4249
crate::data::decode::entry::ResolvedBase::InPack(

gix-pack/src/cache/delta/traverse/resolve.rs

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ where
122122
let (result_size, consumed) = data::delta::decode_header_size(&delta_bytes[consumed..]);
123123
header_ofs += consumed;
124124

125-
set_len(fully_resolved_delta_bytes, result_size as usize);
125+
fully_resolved_delta_bytes.resize(result_size as usize, 0);
126126
data::delta::apply(&base_bytes, fully_resolved_delta_bytes, &delta_bytes[header_ofs..]);
127127

128128
// FIXME: this actually invalidates the "pack_offset()" computation, which is not obvious to consumers
@@ -394,28 +394,13 @@ where
394394
})
395395
}
396396

397-
fn set_len(v: &mut Vec<u8>, new_len: usize) {
398-
if new_len > v.len() {
399-
v.reserve_exact(new_len.saturating_sub(v.capacity()) + (v.capacity() - v.len()));
400-
// SAFETY:
401-
// 1. we have reserved enough capacity to fit `new_len`
402-
// 2. the caller is trusted to write into `v` to completely fill `new_len`.
403-
#[allow(unsafe_code, clippy::uninit_vec)]
404-
unsafe {
405-
v.set_len(new_len);
406-
}
407-
} else {
408-
v.truncate(new_len)
409-
}
410-
}
411-
412397
fn decompress_all_at_once_with(
413398
inflate: &mut zlib::Inflate,
414399
b: &[u8],
415400
decompressed_len: usize,
416401
out: &mut Vec<u8>,
417402
) -> Result<(), Error> {
418-
set_len(out, decompressed_len);
403+
out.resize(decompressed_len, 0);
419404
inflate.reset();
420405
inflate.once(b, out).map_err(|err| Error::ZlibInflate {
421406
source: err,

0 commit comments

Comments
 (0)