Copy a content span from a change without cloning the full `Change`. Graph output calls this for every vertex it materializes. Using `load_change()` here is expensive for imported changes because cache hits clone the entire change, including potentially large unhashed Git metadata. This path copies only the requested bytes.
(
&self,
hash: &Hash,
start: usize,
end: usize,
buf: &mut [u8],
)
| 410 | /// hits clone the entire change, including potentially large unhashed Git |
| 411 | /// metadata. This path copies only the requested bytes. |
| 412 | pub(crate) fn copy_content_span( |
| 413 | &self, |
| 414 | hash: &Hash, |
| 415 | start: usize, |
| 416 | end: usize, |
| 417 | buf: &mut [u8], |
| 418 | ) -> ChangeStoreResult<usize> { |
| 419 | // Fast path: shared read lock — multiple threads can read concurrently. |
| 420 | // peek() doesn't update LRU order, which is an acceptable trade-off |
| 421 | // to avoid serializing all readers on a write lock. |
| 422 | if let Ok(cache) = self.cache.read() { |
| 423 | if let Some(change) = cache.peek(hash) { |
| 424 | return copy_content_from_change(hash, change, start, end, buf); |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | let path = self.change_path(hash); |
| 429 | log::debug!( |
| 430 | "Loading change content {} from {}", |
| 431 | hash.to_base32(), |
| 432 | path.display() |
| 433 | ); |
| 434 | |
| 435 | if !path.exists() { |
| 436 | return Err(ChangeStoreError::NotFound { |
| 437 | hash: hash.to_base32(), |
| 438 | }); |
| 439 | } |
| 440 | |
| 441 | let file = File::open(&path)?; |
| 442 | let mut reader = BufReader::new(file); |
| 443 | let (change, computed_hash) = Change::deserialize(&mut reader)?; |
| 444 | |
| 445 | if computed_hash != *hash { |
| 446 | return Err(ChangeStoreError::HashMismatch { |
| 447 | expected: hash.to_base32(), |
| 448 | computed: computed_hash.to_base32(), |
| 449 | }); |
| 450 | } |
| 451 | |
| 452 | let copied = copy_content_from_change(hash, &change, start, end, buf)?; |
| 453 | |
| 454 | if let Ok(mut cache) = self.cache.write() { |
| 455 | cache.insert(*hash, change); |
| 456 | } |
| 457 | |
| 458 | Ok(copied) |
| 459 | } |
| 460 | |
| 461 | /// Delete a change from disk and the cache. |
| 462 | /// |