Repository: tafia/calamine (Rust)

Issue:
`get_value` returns incorrect value when max. index is overflown
Consider Calamine 0.14.0, and an XLSX spreadsheet with one worksheet covered by `range`.
Given `range.end() == (m_max, n_max)` where `m_max >= 0` and `n_max >= 0`,
`let a = range.get_value((m, n));` where `n == n_max + 1`,
`let b = range.get_value((m, n));` where `n == 0`, 
then `a == b`.

This is incorrect. `None` should be returned when the index of any dimension is overflown wrt. to the maximum index.


Discussion:
Thanks for the issue. Should be fixed in #121 



Patch:
diff --git a/src/cfb.rs b/src/cfb.rs
index 148d6d04..f08054c4 100644
--- a/src/cfb.rs
+++ b/src/cfb.rs
@@ -15,18 +15,23 @@ const RESERVED_SECTORS: u32 = 0xFFFFFFFA;
 /// A Cfb specific error enum
 #[derive(Debug, Fail)]
 pub enum CfbError {
-    #[fail(display = "{}", _0)] Io(#[cause] ::std::io::Error),
-
-    #[fail(display = "Invalid OLE signature (not an office document?)")] Ole,
-    #[fail(display = "Empty Root directory")] EmptyRootDir,
-    #[fail(display = "Cannot find {} stream", _0)] StreamNotFound(String),
+    #[fail(display = "{}", _0)]
+    Io(#[cause] ::std::io::Error),
+
+    #[fail(display = "Invalid OLE signature (not an office document?)")]
+    Ole,
+    #[fail(display = "Empty Root directory")]
+    EmptyRootDir,
+    #[fail(display = "Cannot find {} stream", _0)]
+    StreamNotFound(String),
     #[fail(display = "Invalid {}, expecting {} found {:X}", name, expected, found)]
     Invalid {
         name: &'static str,
         expected: &'static str,
         found: u16,
     },
-    #[fail(display = "Codepage {:X} not found", _0)] CodePageNotFound(u16),
+    #[fail(display = "Codepage {:X} not found", _0)]
+    CodePageNotFound(u16),
 }
 
 /// A struct for managing Compound File Binary format
diff --git a/src/lib.rs b/src/lib.rs
index aeac9ebd..e9f318de 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -251,6 +251,7 @@ impl<T: CellType> Range<T> {
     /// # Panics
     ///
     /// Panics if start.0 > end.0 or start.1 > end.1
+    #[inline]
     pub fn new(start: (u32, u32), end: (u32, u32)) -> Range<T> {
         assert!(start <= end, "invalid range bounds");
         Range {
@@ -390,7 +391,10 @@ impl<T: CellType> Range<T> {
     /// assert_eq!(range.get_value((2, 1)), Some(&DataType::Float(1.0)));
     /// ```
     pub fn set_value(&mut self, absolute_position: (u32, u32), value: T) {
-        assert!(self.start <= absolute_position);
+        assert!(
+            self.start.0 <= absolute_position.0 && self.start.1 <= absolute_position.1,
+            "absolute_position out of bounds"
+        );
 
         // check if we need to change range dimension (strangely happens sometimes ...)
         match (
@@ -462,12 +466,13 @@ impl<T: CellType> Range<T> {
     /// assert_eq!(range[(0, 0)], 0);
     /// ```
     pub fn get_value(&self, absolute_position: (u32, u32)) -> Option<&T> {
-        if absolute_position > self.end || absolute_position < self.start {
-            return None;
+        let p = absolute_position;
+        if p.0 >= self.start.0 && p.0 <= self.end.0 && p.1 >= self.start.1 && p.1 <= self.end.1 {
+            let idx = (absolute_position.0 - self.start.0) as usize * self.width()
+                + (absolute_position.1 - self.start.1) as usize;
+            return Some(&self.inner[idx]);
         }
-        let idx = (absolute_position.0 - self.start.0) as usize * self.width()
-            + (absolute_position.1 - self.start.1) as usize;
-        Some(&self.inner[idx])
+        None
     }
 
     /// Get an iterator over inner rows
@@ -544,7 +549,8 @@ impl<T: CellType> Index<usize> for Range<T> {
 impl<T: CellType> Index<(usize, usize)> for Range<T> {
     type Output = T;
     fn index(&self, index: (usize, usize)) -> &T {
-        let width = self.width();
+        let (height, width) = self.get_size();
+        assert!(index.1 < width && index.0 < height, "index out of bounds");
         &self.inner[index.0 * width + index.1]
     }
 }
@@ -558,7 +564,8 @@ impl<T: CellType> IndexMut<usize> for Range<T> {
 
 impl<T: CellType> IndexMut<(usize, usize)> for Range<T> {
     fn index_mut(&mut self, index: (usize, usize)) -> &mut T {
-        let width = self.width();
+        let (height, width) = self.get_size();
+        assert!(index.1 < width && index.0 < height, "index out of bounds");
         &mut self.inner[index.0 * width + index.1]
     }
 }


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index a61b0754..4fa0b866 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -136,6 +136,22 @@ fn xlsb() {
     );
 }
 
+#[test]
+fn xlsx() {
+    let path = format!("{}/tests/issues.xlsx", env!("CARGO_MANIFEST_DIR"));
+    let mut excel: Xlsx<_> = open_workbook(&path).unwrap();
+
+    let range = excel.worksheet_range("issue2").unwrap().unwrap();
+    range_eq!(
+        range,
+        [
+            [Float(1.), String("a".to_string())],
+            [Float(2.), String("b".to_string())],
+            [Float(3.), String("c".to_string())]
+        ]
+    );
+}
+
 #[test]
 fn xls() {
     let path = format!("{}/tests/issues.xls", env!("CARGO_MANIFEST_DIR"));
@@ -441,3 +457,18 @@ fn empty_sheet() {
         assert_eq!(range.get_size(), (0, 0), "wrong size");
     }
 }
+
+#[test]
+fn issue_120() {
+    let path = format!("{}/tests/issues.xlsx", env!("CARGO_MANIFEST_DIR"));
+    let mut excel: Xlsx<_> = open_workbook(&path).unwrap();
+
+    let range = excel.worksheet_range("issue2").unwrap().unwrap();
+    let end = range.end().unwrap();
+
+    let a = range.get_value((0, end.1 + 1));
+    assert_eq!(None, a);
+
+    let b = range.get_value((0, 0));
+    assert_eq!(Some(&Float(1.)), b);
+}