1#![doc(
2 html_logo_url = "https://cdn.rawgit.com/urschrei/polyline-ffi/master/line.svg",
3 html_root_url = "https://docs.rs/polyline-ffi/"
4)]
5#![deny(
12 clippy::cast_slice_from_raw_parts,
13 clippy::cast_slice_different_sizes,
14 clippy::invalid_null_ptr_usage,
15 clippy::ptr_as_ptr,
16 clippy::transmute_ptr_to_ref
17)]
18
19use polyline::{decode_polyline, encode_coordinates};
20use std::ffi::{CStr, CString};
21use std::slice;
22use std::{f64, ptr};
23
24use geo_types::{CoordFloat, LineString};
25use libc::c_char;
26
27#[allow(dead_code)]
29enum Precision {
30 Zero,
31 One,
32 Two,
33 Three,
34 Four,
35 Five,
36 Six,
37}
38
39fn get_precision(input: u32) -> Option<u32> {
41 match input {
42 5 => Some(Precision::Five as u32),
43 6 => Some(Precision::Six as u32),
44 _ => None,
45 }
46}
47
48#[repr(C)]
51pub struct ExternalArray {
52 pub data: *const libc::c_void,
53 pub len: libc::size_t,
54}
55
56#[repr(C)]
59pub struct InternalArray {
60 pub data: *mut libc::c_void,
61 pub len: libc::size_t,
62}
63
64impl Drop for InternalArray {
65 fn drop(&mut self) {
66 if self.data.is_null() {
67 return;
68 }
69 unsafe {
70 let p = ptr::slice_from_raw_parts_mut(self.data.cast::<[f64; 2]>(), self.len);
72 drop(Box::from_raw(p));
73 };
74 }
75}
76
77impl<T> From<LineString<T>> for InternalArray
79where
80 T: CoordFloat,
81{
82 fn from(sl: LineString<T>) -> Self {
83 let v: Vec<[T; 2]> = sl.0.iter().map(|p| [p.x, p.y]).collect();
84 let boxed = v.into_boxed_slice();
85 let blen = boxed.len();
86 let rawp = Box::into_raw(boxed);
87 InternalArray {
88 data: rawp.cast::<libc::c_void>(),
89 len: blen as libc::size_t,
90 }
91 }
92}
93
94impl From<InternalArray> for LineString<f64> {
96 fn from(arr: InternalArray) -> Self {
97 unsafe {
99 let p = ptr::slice_from_raw_parts_mut(arr.data.cast::<[f64; 2]>(), arr.len);
100 let v = Box::from_raw(p).to_vec();
101 v.into()
102 }
103 }
104}
105
106impl From<Vec<[f64; 2]>> for InternalArray {
108 fn from(v: Vec<[f64; 2]>) -> Self {
109 let boxed = v.into_boxed_slice();
110 let blen = boxed.len();
111 let rawp = Box::into_raw(boxed);
112 InternalArray {
113 data: rawp.cast::<libc::c_void>(),
114 len: blen as libc::size_t,
115 }
116 }
117}
118
119impl From<Vec<[f64; 2]>> for ExternalArray {
121 fn from(v: Vec<[f64; 2]>) -> Self {
122 let boxed = v.into_boxed_slice();
123 let blen = boxed.len();
124 let rawp = Box::into_raw(boxed);
125 ExternalArray {
126 data: rawp.cast::<libc::c_void>(),
127 len: blen as libc::size_t,
128 }
129 }
130}
131
132impl From<ExternalArray> for LineString<f64> {
134 fn from(arr: ExternalArray) -> Self {
135 unsafe {
137 let v = slice::from_raw_parts(arr.data as *mut [f64; 2], arr.len).to_vec();
138 v.into()
139 }
140 }
141}
142
143fn arr_from_string(incoming: &str, precision: u32) -> InternalArray {
145 let result: InternalArray = if get_precision(precision).is_some() {
146 match decode_polyline(incoming, precision) {
147 Ok(res) => res.into(),
148 Err(_) => vec![[f64::NAN, f64::NAN]].into(),
150 }
151 } else {
152 vec![[f64::NAN, f64::NAN]].into()
154 };
155 result
156}
157
158fn string_from_arr(incoming: ExternalArray, precision: u32) -> String {
160 let inc: LineString<_> = incoming.into();
161 if get_precision(precision).is_some() {
162 match encode_coordinates(Into::<LineString<_>>::into(inc), precision) {
163 Ok(res) => res,
164 Err(res) => res,
166 }
167 } else {
168 "Bad precision parameter supplied".to_string()
169 }
170}
171
172#[no_mangle]
189pub unsafe extern "C" fn decode_polyline_ffi(pl: *const c_char, precision: u32) -> InternalArray {
190 let s = CStr::from_ptr(pl).to_str();
191 if let Ok(unwrapped) = s {
192 arr_from_string(unwrapped, precision)
193 } else {
194 vec![[f64::NAN, f64::NAN]].into()
195 }
196}
197
198#[no_mangle]
220pub extern "C" fn encode_coordinates_ffi(coords: ExternalArray, precision: u32) -> *mut c_char {
221 let s: String = string_from_arr(coords, precision);
222 match CString::new(s) {
223 Ok(res) => res.into_raw(),
224 Err(_) => CString::new("Couldn't decode Polyline".to_string())
226 .unwrap()
227 .into_raw(),
228 }
229}
230
231#[no_mangle]
237pub extern "C" fn drop_float_array(_: InternalArray) {}
238
239#[no_mangle]
245pub unsafe extern "C" fn drop_cstring(p: *mut c_char) {
246 drop(CString::from_raw(p));
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252 use std::ptr;
253
254 #[test]
255 fn test_drop_empty_float_array() {
256 let original: LineString<_> = vec![[2.0, 1.0], [4.0, 3.0]].into();
257 let mut arr: InternalArray = original.into();
259 arr.data = ptr::null_mut();
261 drop_float_array(arr);
262 }
263
264 #[test]
265 fn test_coordinate_conversion() {
266 let input = vec![[2.0, 1.0], [4.0, 3.0]];
267 let output = "_ibE_seK_seK_seK";
268 let input_arr: ExternalArray = input.into();
269 let transformed: String = super::string_from_arr(input_arr, 5);
270 assert_eq!(transformed, output);
271 }
272
273 #[test]
274 fn test_string_conversion() {
275 let input = "_ibE_seK_seK_seK";
276 let output = vec![[2.0, 1.0], [4.0, 3.0]];
277 let transformed: InternalArray = super::arr_from_string(input, 5);
279 let v = unsafe {
281 slice::from_raw_parts(transformed.data as *mut [f64; 2], transformed.len).to_vec()
282 };
283 let ls: LineString<_> = v.into();
284 assert_eq!(ls, output.into());
285 }
286
287 #[test]
288 #[should_panic]
289 fn test_bad_string_conversion() {
290 let input = "_p~iF~ps|U_u🗑lLnnqC_mqNvxq`@";
291 let output = vec![[1.0, 2.0], [3.0, 4.0]];
292 let transformed: InternalArray = super::arr_from_string(input, 5);
294 let v = unsafe {
296 slice::from_raw_parts(transformed.data as *mut [f64; 2], transformed.len).to_vec()
297 };
298 let ls: LineString<_> = v.into();
299 assert_eq!(ls, output.into());
300 }
301
302 #[test]
303 fn test_long_vec() {
304 use std::clone::Clone;
305 let arr = include!("../test_fixtures/berlin.rs");
306 let s = include!("../test_fixtures/berlin_decoded.rs");
307 for _ in 0..9999 {
308 let a = arr.clone();
309 let n = 5;
310 let input_ls: ExternalArray = a.into();
311 let transformed: String = super::string_from_arr(input_ls, n);
312 assert_eq!(transformed, s);
313 }
314 }
315}