Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/out of bounds while decoding #37

Merged
merged 12 commits into from
Apr 26, 2024
60 changes: 46 additions & 14 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,11 @@ pub fn decode_polyline(polyline: &str, precision: u32) -> Result<LineString<f64>
let chars = polyline.as_bytes();

while index < chars.len() {
let (latitude_change, new_index) = trans(chars, index);
let (latitude_change, new_index) = trans(chars, index)?;
if new_index >= chars.len() {
break;
}
let (longitude_change, new_index) = trans(chars, new_index);
let (longitude_change, new_index) = trans(chars, new_index)?;
index = new_index;

lat += latitude_change;
Expand All @@ -134,16 +134,18 @@ pub fn decode_polyline(polyline: &str, precision: u32) -> Result<LineString<f64>
Ok(coordinates.into())
}

fn trans(chars: &[u8], mut index: usize) -> (i64, usize) {
let mut at_index;
fn trans(chars: &[u8], mut index: usize) -> Result<(i64, usize), String> {
let mut shift = 0;
let mut result = 0;
let mut byte;
loop {
at_index = chars[index];
byte = (at_index as u64).saturating_sub(63);
index += 1;
byte = chars[index] as u64;
if byte < 63 || (shift > 64 - 5) {
return Err(format!("Cannot decode character at index {}", index));
}
byte -= 63;
result |= (byte & 0x1f) << shift;
index += 1;
shift += 5;
if byte < 0x20 {
break;
Expand All @@ -155,7 +157,7 @@ fn trans(chars: &[u8], mut index: usize) -> (i64, usize) {
} else {
result >> 1
} as i64;
(coordinate_change, index)
Ok((coordinate_change, index))
}

#[cfg(test)]
Expand All @@ -171,7 +173,7 @@ mod tests {
}

#[test]
fn it_works() {
fn precision5() {
let test_cases = vec![
TestCase {
input: vec![[2.0, 1.0], [4.0, 3.0]].into(),
Expand All @@ -194,6 +196,30 @@ mod tests {
}
}

#[test]
fn precision6() {
let test_cases = vec![
TestCase {
input: vec![[2.0, 1.0], [4.0, 3.0]].into(),
output: "_c`|@_gayB_gayB_gayB",
},
TestCase {
input: vec![[-120.2, 38.5], [-120.95, 40.7], [-126.453, 43.252]].into(),
output: "_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI",
},
];
for test_case in test_cases {
assert_eq!(
encode_coordinates(test_case.input.clone(), 6).unwrap(),
test_case.output
);
assert_eq!(
decode_polyline(test_case.output, 6).unwrap(),
test_case.input
);
}
}

#[test]
// coordinates close to each other (below precision) should work
fn rounding_error() {
Expand All @@ -207,22 +233,28 @@ mod tests {
}

#[test]
#[should_panic]
// emoji can't be decoded
// emoji is decodable but messes up data
// TODO: handle this case in the future?
fn broken_string() {
let s = "_p~iF~ps|U_u🗑lLnnqC_mqNvxq`@";
let res = vec![[-120.2, 38.5], [-120.95, 40.7], [-126.453, 43.252]].into();
let res = vec![[-120.2, 38.5], [-120.95, 2306360.53104], [-126.453, 2306363.08304]].into();
assert_eq!(decode_polyline(s, 5).unwrap(), res);
}

#[test]
#[should_panic]
fn invalid_string() {
let s = "invalid_polyline_that_should_be_handled_gracefully";
decode_polyline(s, 6).unwrap();
}

#[test]
#[should_panic]
// Can't have a latitude > 90.0
fn bad_coords() {
let s = "_p~iF~ps|U_ulLnnqC_mqNvxq`@";
let res: LineString<f64> =
vec![[-120.2, 38.5], [-120.95, 40.7], [-126.453, 430.252]].into();
assert_eq!(encode_coordinates(res, 5).unwrap(), s);
encode_coordinates(res, 5).unwrap();
}

#[test]
Expand Down