Skip to content

Commit 765fc8d

Browse files
committed
add support for no-std
How it was done =============== First step was to replace the references to the `std` crate by `core` or `alloc` when possible. Which was always the case, except for the `Error` trait which isn't available in `core` on stable[1]. Another change, that may impact the performance a bit, was to replace the usage of Hash{Map,Set} by the B-Tree variant, since the hash-based one aren't available in core (due to a lack of a secure random number generator). There should be no visible impact on default build, since we're still using hashtable when `std` is enabled (which is the default behavior). Maybe I should give a shot to `hashbrown` to bring back the hashtable version into `no-std` as well. The last change was a bit more annoying (and surprising): most of the math functions aren't available in core[2]. So I've implemented a fallback using `libm` when `std` isn't enabled. Note that due to Cargo limitation[3] `libm` is always pulled as a dependency, but it won't be linked unless necessary thanks to conditional compilation. Known limitations ================ Cannot use the `geo` feature without `std`, mainly because the `geo` crate itself isn't `no-std` (as well as a bunch of its dependencies I guess). -------- [1]: rust-lang/rust#103765 [2]: rust-lang/rfcs#2505 [3]: rust-lang/cargo#1839 Closes: #19
1 parent 1b081cf commit 765fc8d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+313
-192
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ Possible sections are:
1414
<!-- next-header -->
1515
## [Unreleased] - ReleaseDate
1616

17+
### Added
18+
19+
- add `no_std` support (`std` is still enabled by default though)
20+
1721
## [0.5.1] - 2024-01-27
1822

1923
### Fixed

Cargo.toml

+5-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ homepage = "https://docs.rs/h3o"
1010
repository = "https://github.com/HydroniumLabs/h3o"
1111
license = "BSD-3-Clause"
1212
keywords = ["geography", "geospatial", "gis", "h3", "spatial-index"]
13-
categories = ["science::geo"]
13+
categories = ["science::geo", "no-std"]
1414

1515
[package.metadata.docs.rs]
1616
all-features = true
@@ -24,21 +24,22 @@ pre-release-replacements = [
2424
]
2525

2626
[features]
27-
default = []
27+
default = ["std"]
28+
std = ["dep:ahash"]
2829
geo = ["dep:geo", "dep:geojson"]
2930
serde = ["dep:serde", "dep:serde_repr"]
3031
tools = ["polyfit-rs"]
3132

3233
[dependencies]
33-
ahash = { version = "0.8", default-features = false, features = ["std", "compile-time-rng"] }
34+
ahash = { version = "0.8", optional = true, default-features = false, features = ["std", "compile-time-rng"] }
3435
arbitrary = { version = "1.0", optional = true, default-features = false }
35-
auto_ops = { version = "0.3", default-features = false }
3636
konst = { version = "0.3", default-features = false, features = ["parsing"] }
3737
either = { version = "1.0", default-features = false }
3838
float_eq = { version = "1.0", default-features = false }
3939
geo = { version = "0.27", optional = true, default-features = false }
4040
geojson = { version = "0.24", optional = true, default-features = false, features = ["geo-types"] }
4141
h3o-bit = { version = "0.1", default-features = false }
42+
libm = { version = "0.2", default-features = false }
4243
polyfit-rs = { version = "0.2", optional = true, default-features = false }
4344
serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] }
4445
serde_repr = { version = "0.1", optional = true, default-features = false }

src/base_cell.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
coord::{CoordIJK, FaceIJK},
33
error, Direction, Face, NUM_PENTAGONS, NUM_PENT_VERTS,
44
};
5-
use std::fmt;
5+
use core::fmt;
66

77
/// Maximum value for a base cell.
88
pub const MAX: u8 = 121;

src/boundary.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::LatLng;
2-
use std::{fmt, ops::Deref};
2+
use core::{fmt, ops::Deref};
33

44
/// Maximum number of cell boundary vertices.
55
///
@@ -42,14 +42,14 @@ impl Deref for Boundary {
4242

4343
impl fmt::Display for Boundary {
4444
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45-
write!(
46-
f,
47-
"[{}]",
48-
self.iter()
49-
.map(ToString::to_string)
50-
.collect::<Vec<_>>()
51-
.join("-")
52-
)
45+
write!(f, "[",)?;
46+
for (i, ll) in self.iter().enumerate() {
47+
if i != 0 {
48+
write!(f, "-")?;
49+
}
50+
write!(f, "{ll}")?;
51+
}
52+
write!(f, "]",)
5353
}
5454
}
5555

src/coord/cube.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::CoordIJK;
2+
use crate::math::{abs, round};
23

34
/// Cube coordinates.
45
///
@@ -29,11 +30,11 @@ impl CoordCube {
2930

3031
#[allow(clippy::cast_possible_truncation)] // on purpose
3132
let (mut ri, mut rj, mut rk) =
32-
{ (i.round() as i32, j.round() as i32, k.round() as i32) };
33+
{ (round(i) as i32, round(j) as i32, round(k) as i32) };
3334

34-
let i_diff = (f64::from(ri) - i).abs();
35-
let j_diff = (f64::from(rj) - j).abs();
36-
let k_diff = (f64::from(rk) - k).abs();
35+
let i_diff = abs(f64::from(ri) - i);
36+
let j_diff = abs(f64::from(rj) - j);
37+
let k_diff = abs(f64::from(rk) - k);
3738

3839
// Round, maintaining valid cube coords.
3940
if i_diff > j_diff && i_diff > k_diff {

src/coord/ijk.rs

+45-27
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@
99
//! a unique address consisting of the minimal positive `IJK` components; this
1010
//! always results in at most two non-zero components.
1111
12-
#![allow(clippy::use_self)] // False positive with `auto_ops::impl_op_ex`
13-
1412
use super::{CoordCube, Vec2d, SQRT3_2};
15-
use crate::{error::HexGridError, Direction};
16-
use auto_ops::impl_op_ex;
17-
use std::{cmp, fmt};
13+
use crate::{
14+
error::HexGridError,
15+
math::{mul_add, round},
16+
Direction,
17+
};
18+
use core::{
19+
cmp, fmt,
20+
ops::{Add, MulAssign, Sub},
21+
};
1822

1923
// -----------------------------------------------------------------------------
2024

@@ -116,7 +120,7 @@ impl CoordIJK {
116120
}
117121

118122
pub fn distance(&self, other: &Self) -> i32 {
119-
let diff = (self - other).normalize();
123+
let diff = (*self - *other).normalize();
120124

121125
cmp::max(diff.i.abs(), cmp::max(diff.j.abs(), diff.k.abs()))
122126
}
@@ -133,7 +137,7 @@ impl CoordIJK {
133137
(f64::from(2 * i + j) / 7., f64::from(3 * j - i) / 7.)
134138
};
135139

136-
Self::new(i.round() as i32, j.round() as i32, 0).normalize()
140+
Self::new(round(i) as i32, round(j) as i32, 0).normalize()
137141
}
138142

139143
/// Returns the normalized `IJK` coordinates of the indexing parent of a
@@ -154,7 +158,7 @@ impl CoordIJK {
154158
)
155159
};
156160

157-
Self::new(i.round() as i32, j.round() as i32, 0).checked_normalize()
161+
Self::new(round(i) as i32, round(j) as i32, 0).checked_normalize()
158162
}
159163

160164
/// Returns the normalized `IJK` coordinates of the hex centered on the
@@ -194,7 +198,7 @@ impl CoordIJK {
194198
/// Returns the normalized `IJK` coordinates of the hex in the specified
195199
/// direction from the current position.
196200
pub fn neighbor(&self, direction: Direction) -> Self {
197-
(self + direction.coordinate()).normalize()
201+
(*self + direction.coordinate()).normalize()
198202
}
199203

200204
/// Returns the `IJK` coordinates after a 60 degrees rotation.
@@ -213,35 +217,49 @@ impl CoordIJK {
213217
}
214218
}
215219

216-
impl_op_ex!(+ |lhs: &CoordIJK, rhs: &CoordIJK| -> CoordIJK {
217-
CoordIJK{
218-
i: lhs.i + rhs.i,
219-
j: lhs.j + rhs.j,
220-
k: lhs.k + rhs.k,
220+
// -----------------------------------------------------------------------------
221+
222+
impl Add for CoordIJK {
223+
type Output = Self;
224+
225+
fn add(self, other: Self) -> Self {
226+
Self {
227+
i: self.i + other.i,
228+
j: self.j + other.j,
229+
k: self.k + other.k,
230+
}
221231
}
222-
});
232+
}
233+
234+
impl Sub for CoordIJK {
235+
type Output = Self;
223236

224-
impl_op_ex!(-|lhs: &CoordIJK, rhs: &CoordIJK| -> CoordIJK {
225-
CoordIJK {
226-
i: lhs.i - rhs.i,
227-
j: lhs.j - rhs.j,
228-
k: lhs.k - rhs.k,
237+
fn sub(self, other: Self) -> Self {
238+
Self {
239+
i: self.i - other.i,
240+
j: self.j - other.j,
241+
k: self.k - other.k,
242+
}
243+
}
244+
}
245+
246+
impl MulAssign<i32> for CoordIJK {
247+
fn mul_assign(&mut self, rhs: i32) {
248+
self.i *= rhs;
249+
self.j *= rhs;
250+
self.k *= rhs;
229251
}
230-
});
252+
}
231253

232-
impl_op_ex!(*= |lhs: &mut CoordIJK, rhs: i32| {
233-
lhs.i *= rhs;
234-
lhs.j *= rhs;
235-
lhs.k *= rhs;
236-
});
254+
// -----------------------------------------------------------------------------
237255

238256
impl From<CoordIJK> for Vec2d {
239257
// Returns the center point in 2D cartesian coordinates of a hex.
240258
fn from(value: CoordIJK) -> Self {
241259
let i = f64::from(value.i - value.k);
242260
let j = f64::from(value.j - value.k);
243261

244-
Self::new(0.5_f64.mul_add(-j, i), j * SQRT3_2)
262+
Self::new(mul_add(0.5, -j, i), j * SQRT3_2)
245263
}
246264
}
247265

src/coord/latlng.rs

+37-34
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ use super::{
33
RES0_U_GNOMONIC, SQRT7_POWERS,
44
};
55
use crate::{
6-
error::InvalidLatLng, face, CellIndex, Face, Resolution, EARTH_RADIUS_KM,
7-
TWO_PI,
6+
error::InvalidLatLng,
7+
face,
8+
math::{acos, asin, atan2, cos, mul_add, sin, sqrt, tan},
9+
CellIndex, Face, Resolution, EARTH_RADIUS_KM, TWO_PI,
810
};
9-
use float_eq::float_eq;
10-
use std::{
11+
use core::{
1112
f64::consts::{FRAC_PI_2, PI},
1213
fmt,
1314
};
15+
use float_eq::float_eq;
1416

1517
/// Epsilon of ~0.1mm in degrees.
1618
const EPSILON_DEG: f64 = 0.000000001;
@@ -153,15 +155,16 @@ impl LatLng {
153155
/// ```
154156
#[must_use]
155157
pub fn distance_rads(self, other: Self) -> f64 {
156-
let sin_lat = ((other.lat - self.lat) / 2.).sin();
157-
let sin_lng = ((other.lng - self.lng) / 2.).sin();
158+
let sin_lat = sin((other.lat - self.lat) / 2.);
159+
let sin_lng = sin((other.lng - self.lng) / 2.);
158160

159-
let a = sin_lat.mul_add(
161+
let a = mul_add(
162+
sin_lat,
160163
sin_lat,
161-
self.lat.cos() * other.lat.cos() * sin_lng * sin_lng,
164+
cos(self.lat) * cos(other.lat) * sin_lng * sin_lng,
162165
);
163166

164-
2. * a.sqrt().atan2((1. - a).sqrt())
167+
2. * atan2(sqrt(a), sqrt(1. - a))
165168
}
166169

167170
/// The great circle distance, in kilometers, between two spherical
@@ -231,15 +234,15 @@ impl LatLng {
231234

232235
let r = {
233236
// cos(r) = 1 - 2 * sin^2(r/2) = 1 - 2 * (sqd / 4) = 1 - sqd/2
234-
let r = (1. - distance / 2.).acos();
237+
let r = acos(1. - distance / 2.);
235238

236239
if r < EPSILON {
237240
return Vec2d::new(0., 0.);
238241
}
239242

240243
// Perform gnomonic scaling of `r` (`tan(r)`) and scale for current
241244
// resolution length `u`.
242-
(r.tan() / RES0_U_GNOMONIC) * SQRT7_POWERS[usize::from(resolution)]
245+
(tan(r) / RES0_U_GNOMONIC) * SQRT7_POWERS[usize::from(resolution)]
243246
};
244247

245248
let theta = {
@@ -255,7 +258,7 @@ impl LatLng {
255258
};
256259

257260
// Convert to local x, y.
258-
Vec2d::new(r * theta.cos(), r * theta.sin())
261+
Vec2d::new(r * cos(theta), r * sin(theta))
259262
}
260263

261264
/// Finds the closest icosahedral face from the current coordinate.
@@ -290,12 +293,12 @@ impl LatLng {
290293
/// Computes the azimuth to `other` from `self`, in radians.
291294
#[must_use]
292295
pub(crate) fn azimuth(self, other: &Self) -> f64 {
293-
(other.lat.cos() * (other.lng - self.lng).sin()).atan2(
294-
self.lat.cos().mul_add(
295-
other.lat.sin(),
296-
-self.lat.sin()
297-
* other.lat.cos()
298-
* (other.lng - self.lng).cos(),
296+
atan2(
297+
cos(other.lat) * sin(other.lng - self.lng),
298+
mul_add(
299+
cos(self.lat),
300+
sin(other.lat),
301+
-sin(self.lat) * cos(other.lat) * cos(other.lng - self.lng),
299302
),
300303
)
301304
}
@@ -319,14 +322,14 @@ impl LatLng {
319322
self.lat - distance // Due South.
320323
}
321324
} else {
322-
self.lat
323-
.sin()
324-
.mul_add(
325-
distance.cos(),
326-
self.lat.cos() * distance.sin() * azimuth.cos(),
325+
asin(
326+
mul_add(
327+
sin(self.lat),
328+
cos(distance),
329+
cos(self.lat) * sin(distance) * cos(azimuth),
327330
)
328-
.clamp(-1., 1.)
329-
.asin()
331+
.clamp(-1., 1.),
332+
)
330333
};
331334

332335
// Handle poles.
@@ -341,11 +344,11 @@ impl LatLng {
341344
self.lng
342345
} else {
343346
let sinlng =
344-
(azimuth.sin() * distance.sin() / lat.cos()).clamp(-1., 1.);
345-
let coslng = self.lat.sin().mul_add(-lat.sin(), distance.cos())
346-
/ self.lat.cos()
347-
/ lat.cos();
348-
self.lng + sinlng.atan2(coslng)
347+
(sin(azimuth) * sin(distance) / cos(lat)).clamp(-1., 1.);
348+
let coslng = mul_add(sin(self.lat), sin(-lat), cos(distance))
349+
/ cos(self.lat)
350+
/ cos(lat);
351+
self.lng + atan2(sinlng, coslng)
349352
};
350353

351354
// XXX: make sure longitudes are in the proper bounds.
@@ -401,11 +404,11 @@ impl From<LatLng> for Vec3d {
401404
/// Computes the 3D coordinate on unit sphere from the latitude and
402405
/// longitude.
403406
fn from(value: LatLng) -> Self {
404-
let r = value.lat.cos();
407+
let r = cos(value.lat);
405408

406-
let z = value.lat.sin();
407-
let x = value.lng.cos() * r;
408-
let y = value.lng.sin() * r;
409+
let z = sin(value.lat);
410+
let x = cos(value.lng) * r;
411+
let y = sin(value.lng) * r;
409412

410413
Self::new(x, y, z)
411414
}

src/coord/localij.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::{
2222
index::bits,
2323
BaseCell, CellIndex, Direction, Resolution, CCW, CW, DEFAULT_CELL_INDEX,
2424
};
25-
use std::{fmt, num::NonZeroU8};
25+
use core::{fmt, num::NonZeroU8};
2626

2727
// -----------------------------------------------------------------------------
2828

0 commit comments

Comments
 (0)