Skip to content

Commit bcbe738

Browse files
Avoid if possible unsafe code in vrp-cli
1 parent 04e8ef3 commit bcbe738

File tree

7 files changed

+39
-26
lines changed

7 files changed

+39
-26
lines changed

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ All notable changes to this project will be documented in this file.
66
## [Unreleased]
77

88
This release brings hierarchical approach for local objective estimation. Additionally, it focuses on refactoring
9-
and forbidding usafe code.
9+
to avoid usafe code.
1010

1111
### Changed
1212

1313
* use hierarchical approach for local objective estimation
14-
* avoid unsafe code in rosomaxa/vrp-core/vrp-pragmatic/vrp-scientific crates and apply forbid_unsafe_code crate-wise
14+
* avoid unsafe code project-wise:
15+
* apply `#![forbid(unsafe_code)]` directive for rosomaxa/vrp-core/vrp-pragmatic/vrp-scientific
16+
* apply `#![deny(unsafe_code)]` directive for vrp-cli (due to FFI which is only one exception)
17+
* allow unsafe code only when necessary in research crates which are not used anyhow in production code
1518

1619
### Added
1720

vrp-cli/src/commands/generate.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ pub fn run_generate(matches: &ArgMatches) -> Result<(), String> {
7070
match generate_problem_from_args(matches) {
7171
Ok((problem, input_format)) => {
7272
let out_result = matches.get_one::<String>(OUT_RESULT_ARG_NAME).map(|path| create_file(path, "out result"));
73-
let out_buffer = create_write_buffer(out_result);
73+
let mut out_buffer = create_write_buffer(out_result);
7474

7575
match input_format.as_str() {
76-
"pragmatic" => serialize_problem(out_buffer, &problem)
76+
"pragmatic" => serialize_problem(&problem, &mut out_buffer)
7777
.map_err(|err| format!("cannot serialize as pragmatic problem: '{err}'")),
7878
_ => Err(format!("unknown output format: '{input_format}'")),
7979
}

vrp-cli/src/commands/import.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ pub fn run_import(matches: &ArgMatches) -> Result<(), String> {
4141
match import_problem(input_format, input_files) {
4242
Ok(problem) => {
4343
let out_result = matches.get_one::<String>(OUT_RESULT_ARG_NAME).map(|path| create_file(path, "out result"));
44-
let out_buffer = create_write_buffer(out_result);
45-
serialize_problem(out_buffer, &problem).map_err(|err| format!("cannot serialize result problem: '{err}'"))
44+
let mut out_buffer = create_write_buffer(out_result);
45+
serialize_problem(&problem, &mut out_buffer)
46+
.map_err(|err| format!("cannot serialize result problem: '{err}'"))
4647
}
4748
Err(err) => Err(format!("cannot import problem: '{err}'")),
4849
}

vrp-cli/src/extensions/analyze/clusters.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,15 @@ pub fn get_clusters<F: Read>(
4747
})
4848
.collect::<Vec<_>>();
4949

50-
let mut buffer = String::new();
51-
let writer = unsafe { BufWriter::new(buffer.as_mut_vec()) };
50+
let mut writer = BufWriter::new(Vec::new());
5251

53-
serialize_named_locations_as_geojson(writer, locations.as_slice())
52+
serialize_named_locations_as_geojson(locations.as_slice(), &mut writer)
5453
.map_err(|err| format!("cannot write named locations as geojson: '{err}'"))?;
5554

56-
Ok(buffer)
55+
let bytes = writer.into_inner().map_err(|err| format!("{err}"))?;
56+
let result = String::from_utf8(bytes).map_err(|err| format!("{err}"))?;
57+
58+
Ok(result)
5759
}
5860

5961
fn get_core_problem<F: Read>(

vrp-cli/src/lib.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//! - `vrp-core` crate implements default metaheuristic
1111
1212
#![warn(missing_docs)]
13+
#![deny(unsafe_code)] // NOTE: use deny instead forbid as we need allow unsafe code for c_interop
1314

1415
#[cfg(test)]
1516
#[path = "../tests/helpers/mod.rs"]
@@ -45,6 +46,7 @@ use vrp_pragmatic::get_unique_locations;
4546
use vrp_pragmatic::validation::ValidationContext;
4647

4748
#[cfg(not(target_arch = "wasm32"))]
49+
#[allow(unsafe_code)]
4850
mod c_interop {
4951
use super::*;
5052
use crate::extensions::solve::config::read_config;
@@ -110,22 +112,22 @@ mod c_interop {
110112
extern "C" fn convert_to_pragmatic(
111113
format: *const c_char,
112114
inputs: *const *const c_char,
113-
input_len: *const i32,
115+
input_len: usize,
114116
success: Callback,
115117
failure: Callback,
116118
) {
117119
catch_panic(failure, || {
118120
let format = to_string(format);
119-
let inputs = unsafe { slice::from_raw_parts(inputs, input_len as usize).to_vec() };
121+
let inputs = unsafe { slice::from_raw_parts(inputs, input_len).to_vec() };
120122
let inputs = inputs.iter().map(|p| to_string(*p)).collect::<Vec<_>>();
121123
let readers = inputs.iter().map(|p| BufReader::new(p.as_bytes())).collect::<Vec<_>>();
122124

123125
match import_problem(format.as_str(), Some(readers)) {
124126
Ok(problem) => {
125-
let mut buffer = String::new();
126-
let writer = unsafe { BufWriter::new(buffer.as_mut_vec()) };
127-
serialize_problem(writer, &problem).unwrap();
128-
let problem = CString::new(buffer.as_bytes()).unwrap();
127+
let mut writer = BufWriter::new(Vec::new());
128+
serialize_problem(&problem, &mut writer).unwrap();
129+
let bytes = writer.into_inner().expect("cannot use writer");
130+
let problem = CString::new(bytes).unwrap();
129131

130132
success(problem.as_ptr());
131133
}
@@ -351,11 +353,14 @@ mod py_interop {
351353
fn convert_to_pragmatic(format: &str, inputs: Vec<String>) -> PyResult<String> {
352354
let readers = inputs.iter().map(|p| BufReader::new(p.as_bytes())).collect::<Vec<_>>();
353355
import_problem(format, Some(readers))
354-
.map(|problem| {
355-
let mut buffer = String::new();
356-
serialize_problem(unsafe { BufWriter::new(buffer.as_mut_vec()) }, &problem).unwrap();
356+
.and_then(|problem| {
357+
let mut writer = BufWriter::new(Vec::new());
358+
serialize_problem(&problem, &mut writer).unwrap();
357359

358-
buffer
360+
writer
361+
.into_inner()
362+
.map_err(|err| format!("BufWriter: {err}"))
363+
.and_then(|bytes| String::from_utf8(bytes).map_err(|err| format!("StringUTF8: {err}")))
359364
})
360365
.map_err(PyOSError::new_err)
361366
}
@@ -458,11 +463,13 @@ mod wasm {
458463

459464
match import_problem(format, Some(readers)) {
460465
Ok(problem) => {
461-
let mut buffer = String::new();
462-
let writer = unsafe { BufWriter::new(buffer.as_mut_vec()) };
463-
serialize_problem(writer, &problem).unwrap();
466+
let mut writer = BufWriter::new(Vec::new());
467+
serialize_problem(&problem, &mut writer).unwrap();
468+
469+
let bytes = writer.into_inner().unwrap();
470+
let result = String::from_utf8(bytes).unwrap();
464471

465-
Ok(JsValue::from_str(buffer.as_str()))
472+
Ok(JsValue::from_str(result.as_str()))
466473
}
467474
Err(err) => Err(JsValue::from_str(err.to_string().as_str())),
468475
}

vrp-pragmatic/src/format/problem/model.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,6 @@ pub fn deserialize_locations<R: Read>(reader: BufReader<R>) -> Result<Vec<Locati
715715
}
716716

717717
/// Serializes `problem` in json from `writer`.
718-
pub fn serialize_problem<W: Write>(writer: BufWriter<W>, problem: &Problem) -> Result<(), Error> {
718+
pub fn serialize_problem<W: Write>(problem: &Problem, writer: &mut BufWriter<W>) -> Result<(), Error> {
719719
serde_json::to_writer_pretty(writer, problem).map_err(Error::from)
720720
}

vrp-pragmatic/src/format/solution/geo_serializer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ pub fn serialize_solution_as_geojson<W: Write>(
8484

8585
/// Serializes named location list with their color index.
8686
pub fn serialize_named_locations_as_geojson<W: Write>(
87-
writer: BufWriter<W>,
8887
locations: &[(String, Location, usize)],
88+
writer: &mut BufWriter<W>,
8989
) -> Result<(), Error> {
9090
let geo_json = create_geojson_named_locations(locations)?;
9191

0 commit comments

Comments
 (0)