diff --git a/src/ledger.rs b/src/ledger.rs index 2200bc675bcd3d..d302b31c38df0c 100644 --- a/src/ledger.rs +++ b/src/ledger.rs @@ -82,18 +82,64 @@ fn err_bincode_to_io(e: Box) -> io::Error { io::Error::new(io::ErrorKind::Other, e.to_string()) } -fn entry_at(file: &mut A, at: u64) -> io::Result { +fn raw_entries_at( + file: &mut A, + at: u64, + num_entries: u64, + buf: &mut [u8], +) -> io::Result<(u64, u64)> { + let mut total_read: usize = 0; + let mut num_entries_read = 0; + file.seek(SeekFrom::Start(at))?; - let len = deserialize_from(file.take(SIZEOF_U64)).map_err(err_bincode_to_io)?; - trace!("entry_at({}) len: {}", at, len); + let len_size = size_of::(); + + for _ in 0..num_entries { + let buf_left = buf.len() - total_read; + if len_size > buf_left { + break; + } + + // read in len + let len = { + let mut buf_slice = &mut buf[total_read..total_read + len_size]; + file.read(buf_slice)?; + let len: u64 = deserialize(buf_slice).map_err(err_bincode_to_io)?; + len as usize + }; + + let buf_left = buf.len() - (total_read + len_size); + if len > buf_left { + break; + } + let entry_start = total_read + len_size; + let entry_end = entry_start + len; + + file.read(&mut buf[entry_start..entry_end])?; + + // Don't update total_read until entry is read successfully + total_read += len_size; + total_read += len; + num_entries_read += 1; + } + Ok((num_entries_read, total_read as u64)) +} + +fn read_entry(file: &mut A, len: u64) -> io::Result { deserialize_from(file.take(len)).map_err(err_bincode_to_io) } +fn entry_at(file: &mut A, at: u64) -> io::Result { + let len = u64_at(file, at)?; + + read_entry(file, len) +} + fn next_entry(file: &mut A) -> io::Result { let len = deserialize_from(file.take(SIZEOF_U64)).map_err(err_bincode_to_io)?; - deserialize_from(file.take(len)).map_err(err_bincode_to_io) + read_entry(file, len) } fn u64_at(file: &mut A, at: u64) -> io::Result { @@ -116,9 +162,26 @@ impl LedgerWindow { } pub fn get_entry(&mut self, index: u64) -> io::Result { - let offset = u64_at(&mut self.index, index * SIZEOF_U64)?; + let offset = self.get_entry_offset(index)?; entry_at(&mut self.data, offset) } + + // Fill 'buf' with num_entries or most number of whole entries that fit into buf.len() + // + // Return tuple of (number of entries read, total size of entries read) + pub fn get_entries_bytes( + &mut self, + start_index: u64, + num_entries: u64, + buf: &mut [u8], + ) -> io::Result<(u64, u64)> { + let start_offset = self.get_entry_offset(start_index)?; + raw_entries_at(&mut self.data, start_offset, num_entries, buf) + } + + fn get_entry_offset(&mut self, index: u64) -> io::Result { + u64_at(&mut self.index, index * SIZEOF_U64) + } } pub fn verify_ledger(ledger_path: &str) -> io::Result<()> { @@ -874,4 +937,58 @@ mod tests { let _ignored = remove_dir_all(&ledger_path); } + #[test] + fn test_get_entries_bytes() { + let entries = make_tiny_test_entries(10); + let ledger_path = tmp_ledger_path("test_raw_entries"); + { + let mut writer = LedgerWriter::open(&ledger_path, true).unwrap(); + writer.write_entries(entries.clone()).unwrap(); + } + + let mut window = LedgerWindow::open(&ledger_path).unwrap(); + let mut buf = [0; 1024]; + let (num_entries, bytes) = window.get_entries_bytes(0, 1, &mut buf).unwrap(); + let bytes = bytes as usize; + assert_eq!(num_entries, 1); + let entry: Entry = deserialize(&buf[size_of::()..bytes]).unwrap(); + assert_eq!(entry, entries[0]); + + let (num_entries, bytes2) = window.get_entries_bytes(0, 2, &mut buf).unwrap(); + let bytes2 = bytes2 as usize; + assert_eq!(num_entries, 2); + assert!(bytes2 > bytes); + for (i, ref entry) in entries.iter().enumerate() { + info!("entry[{}] = {:?}", i, entry.id); + } + + let entry: Entry = deserialize(&buf[size_of::()..bytes]).unwrap(); + assert_eq!(entry, entries[0]); + + let entry: Entry = deserialize(&buf[bytes + size_of::()..bytes2]).unwrap(); + assert_eq!(entry, entries[1]); + + // buf size part-way into entry[1], should just return entry[0] + let mut buf = vec![0; bytes + size_of::() + 1]; + let (num_entries, bytes3) = window.get_entries_bytes(0, 2, &mut buf).unwrap(); + assert_eq!(num_entries, 1); + let bytes3 = bytes3 as usize; + assert_eq!(bytes3, bytes); + + let mut buf = vec![0; bytes2 - 1]; + let (num_entries, bytes4) = window.get_entries_bytes(0, 2, &mut buf).unwrap(); + assert_eq!(num_entries, 1); + let bytes4 = bytes4 as usize; + assert_eq!(bytes4, bytes); + + let mut buf = vec![0; bytes + size_of::() - 1]; + let (num_entries, bytes5) = window.get_entries_bytes(0, 2, &mut buf).unwrap(); + assert_eq!(num_entries, 1); + let bytes5 = bytes5 as usize; + assert_eq!(bytes5, bytes); + + // Read out of range + assert!(window.get_entries_bytes(20, 2, &mut buf).is_err()); + } + }