From 4253570c1f7cea52c65fd7bba60aecf2551513f1 Mon Sep 17 00:00:00 2001 From: Casper Hart Date: Tue, 24 Oct 2023 23:15:31 -0400 Subject: [PATCH] fix for off by one error in file --- src/file.rs | 34 ++++++++++++++++++++++++++-------- src/header.rs | 12 ------------ src/reading.rs | 15 +++++++-------- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/file.rs b/src/file.rs index 6be6194..c6010bd 100644 --- a/src/file.rs +++ b/src/file.rs @@ -43,11 +43,7 @@ impl<'a, T> Debug for FieldRef<'a, T> { impl<'a, T> FieldRef<'a, T> { fn position_in_source(&self) -> u64 { - let record_position = self - .file - .header - .record_position(self.record_index.0) - .unwrap() as u64; + let record_position = self.file.record_position(self.record_index.0).unwrap() as u64; let position_in_record = self.file.fields_info[..self.field_index.0] .iter() .map(|i| i.field_length as u64) @@ -195,7 +191,7 @@ impl<'a, T> RecordRef<'a, T> { } fn position_in_source(&self) -> u64 { - self.file.header.record_position(self.index.0).unwrap() as u64 + self.file.record_position(self.index.0).unwrap() as u64 } } @@ -358,6 +354,7 @@ impl<'a, T> FileRecordIterator<'a, T> { pub struct File { pub(crate) inner: T, pub(crate) header: Header, + pub(crate) record_length: u16, pub(crate) fields_info: Vec, pub(crate) encoding: DynEncoding, /// Non-Memo field length is stored on a u8, @@ -388,6 +385,17 @@ impl File { pub fn set_options(&mut self, options: ReadingOptions) { self.options = options; } + + pub(crate) fn record_position(&self, index: usize) -> Option { + if index >= self.header.num_records as usize { + None + } else { + let offset = self.header.offset_to_first_record as usize + + (index * (self.record_length as usize + DELETION_FLAG_SIZE)) + + DELETION_FLAG_SIZE; + Some(offset) + } + } } impl File { @@ -430,10 +438,14 @@ impl File { let field_error = FieldIOError::new(UnsupportedCodePage(header.code_page_mark), None); Error::new(field_error, 0) })?; + + let record_length = fields_info.iter().map(|x| x.length() as u16).sum(); + Ok(Self { inner: source, // memo_reader: None, header, + record_length, fields_info, encoding, field_data_buffer: [0u8; 255], @@ -470,9 +482,16 @@ impl File { pub fn create_new(mut dst: T, table_info: TableInfo) -> Result { write_header_parts(&mut dst, &table_info.header, &table_info.fields_info)?; + let record_length = table_info + .fields_info + .iter() + .map(|x| x.field_length as u16) + .sum(); + Ok(Self { inner: dst, header: table_info.header, + record_length: record_length, fields_info: table_info.fields_info, encoding: table_info.encoding, field_data_buffer: [0u8; 255], @@ -501,8 +520,7 @@ impl File { ); let end_of_last_record = self.header.offset_to_first_record as u64 - + self.num_records() as u64 - * (DELETION_FLAG_SIZE as u64 + self.header.size_of_record as u64); + + self.num_records() as u64 * (DELETION_FLAG_SIZE as u64 + self.record_length as u64); self.inner .seek(SeekFrom::Start(end_of_last_record)) .map_err(|error| Error::io_error(error, self.num_records()))?; diff --git a/src/header.rs b/src/header.rs index 3352bf4..5df2e07 100644 --- a/src/header.rs +++ b/src/header.rs @@ -4,7 +4,6 @@ use crate::encoding::DynEncoding; use std::io::{Read, Write}; use crate::field::types::Date; -use crate::field::DELETION_FLAG_SIZE; use crate::memo::MemoFileType; // Used this as source: https://blog.codetitans.pl/post/dbf-and-language-code-page/ @@ -407,17 +406,6 @@ impl Header { dest.write_u8(0)?; Ok(()) } - - pub(crate) fn record_position(&self, index: usize) -> Option { - if index >= self.num_records as usize { - None - } else { - let offset = self.offset_to_first_record as usize - + (index * (self.size_of_record as usize + DELETION_FLAG_SIZE)) - + DELETION_FLAG_SIZE; - Some(offset) - } - } } #[cfg(test)] diff --git a/src/reading.rs b/src/reading.rs index 70e75f3..f75f565 100644 --- a/src/reading.rs +++ b/src/reading.rs @@ -152,6 +152,7 @@ impl ReaderBuilder { source: file.inner, memo_reader, header: file.header, + record_length: file.record_length, fields_info: file.fields_info, encoding: self .encoding @@ -171,6 +172,7 @@ pub struct Reader { memo_reader: Option>, header: Header, fields_info: Vec, + record_length: u16, encoding: DynEncoding, options: ReadingOptions, } @@ -208,6 +210,7 @@ impl Reader { source: file.inner, memo_reader: None, header: file.header, + record_length: file.record_length, fields_info: file.fields_info, encoding: file.encoding, options: ReadingOptions::default(), @@ -243,16 +246,12 @@ impl Reader { /// Creates an iterator of records of the type you want pub fn iter_records_as(&mut self) -> RecordIterator { - let record_size: usize = self - .fields_info - .iter() - .map(|i| i.field_length as usize) - .sum(); + let record_length = self.record_length as usize; RecordIterator { reader: self, record_type: std::marker::PhantomData, current_record: 0, - record_data_buffer: std::io::Cursor::new(vec![0u8; record_size]), + record_data_buffer: std::io::Cursor::new(vec![0u8; record_length]), field_data_buffer: [0u8; 255], } } @@ -289,8 +288,8 @@ impl Reader { /// Seek to the start of the record at `index` pub fn seek(&mut self, index: usize) -> Result<(), Error> { - let offset = self.header.offset_to_first_record as usize - + (index * self.header.size_of_record as usize); + let offset = + self.header.offset_to_first_record as usize + (index * self.record_length as usize); self.source .seek(SeekFrom::Start(offset as u64)) .map_err(|err| Error::io_error(err, 0))?;