Unified · Split
| @@ -1429,12 +1429,12 @@ name = "gix-actor" · 1428 unmodified lines | ||
| 1429 | 1429 | version = "0.30.0" |
| 1430 | 1430 | dependencies = [ |
| 1431 | 1431 | "bstr", |
| 1432 | − "btoi", | |
| 1433 | 1432 | "document-features", |
| 1434 | 1433 | "gix-date 0.8.3", |
| 1435 | 1434 | "gix-features 0.38.0", |
| 1436 | 1435 | "gix-hash 0.14.1", |
| 1437 | 1436 | "gix-testtools", |
| 1437 | + "gix-utils 0.1.9", | |
| 1438 | 1438 | "itoa", |
| 1439 | 1439 | "pretty_assertions", |
| 1440 | 1440 | "serde", |
| @@ -2002,7 +2002,6 @@ version = "0.30.0" · 561 unmodified lines | ||
| 2002 | 2002 | dependencies = [ |
| 2003 | 2003 | "bitflags 2.4.1", |
| 2004 | 2004 | "bstr", |
| 2005 | − "btoi", | |
| 2006 | 2005 | "document-features", |
| 2007 | 2006 | "filetime", |
| 2008 | 2007 | "fnv", |
| @@ -2013,6 +2012,7 @@ dependencies = [ · 4 unmodified lines | ||
| 2013 | 2012 | "gix-lock 13.1.0", |
| 2014 | 2013 | "gix-object 0.41.0", |
| 2015 | 2014 | "gix-traverse 0.37.0", |
| 2015 | + "gix-utils 0.1.9", | |
| 2016 | 2016 | "hashbrown 0.14.3", |
| 2017 | 2017 | "itoa", |
| 2018 | 2018 | "libc", |
| @@ -2159,7 +2159,6 @@ name = "gix-object" · 140 unmodified lines | ||
| 2159 | 2159 | version = "0.41.0" |
| 2160 | 2160 | dependencies = [ |
| 2161 | 2161 | "bstr", |
| 2162 | − "btoi", | |
| 2163 | 2162 | "criterion", |
| 2164 | 2163 | "document-features", |
| 2165 | 2164 | "gix-actor 0.30.0", |
| @@ -2167,6 +2166,7 @@ dependencies = [ · 1 unmodified line | ||
| 2167 | 2166 | "gix-features 0.38.0", |
| 2168 | 2167 | "gix-hash 0.14.1", |
| 2169 | 2168 | "gix-testtools", |
| 2169 | + "gix-utils 0.1.9", | |
| 2170 | 2170 | "gix-validate 0.8.3", |
| 2171 | 2171 | "itoa", |
| 2172 | 2172 | "pretty_assertions", |
| @@ -2347,7 +2347,6 @@ dependencies = [ · 174 unmodified lines | ||
| 2347 | 2347 | "async-std", |
| 2348 | 2348 | "async-trait", |
| 2349 | 2349 | "bstr", |
| 2350 | − "btoi", | |
| 2351 | 2350 | "document-features", |
| 2352 | 2351 | "futures-io", |
| 2353 | 2352 | "futures-lite 2.1.0", |
| @@ -2358,6 +2357,7 @@ dependencies = [ · 4 unmodified lines | ||
| 2358 | 2357 | "gix-packetline", |
| 2359 | 2358 | "gix-testtools", |
| 2360 | 2359 | "gix-transport", |
| 2360 | + "gix-utils 0.1.9", | |
| 2361 | 2361 | "maybe-async", |
| 2362 | 2362 | "serde", |
| 2363 | 2363 | "thiserror", |
| @@ -2380,7 +2380,7 @@ name = "gix-quote" · 16 unmodified lines | ||
| 2380 | 2380 | version = "0.4.10" |
| 2381 | 2381 | dependencies = [ |
| 2382 | 2382 | "bstr", |
| 2383 | − "btoi", | |
| 2383 | + "gix-utils 0.1.9", | |
| 2384 | 2384 | "thiserror", |
| 2385 | 2385 | ] |
| 2386 | 2386 | |
| @@ -19,19 +19,24 @@ serde = ["dep:serde", "bstr/serde", "gix-date/serde"] · 18 unmodified lines | ||
| 19 | 19 | [dependencies] |
| 20 | 20 | gix-features = { version = "^0.38.0", path = "../gix-features", optional = true } |
| 21 | 21 | gix-date = { version = "^0.8.3", path = "../gix-date" } |
| 22 | +gix-utils = { version = "^0.1.9", path = "../gix-utils" } | |
| 22 | 23 | |
| 23 | 24 | thiserror = "1.0.38" |
| 24 | −btoi = "0.4.2" | |
| 25 | −bstr = { version = "1.3.0", default-features = false, features = ["std", "unicode"]} | |
| 25 | +bstr = { version = "1.3.0", default-features = false, features = [ | |
| 26 | + "std", | |
| 27 | + "unicode", | |
| 28 | +] } | |
| 26 | 29 | winnow = { version = "0.6.0", features = ["simd"] } |
| 27 | 30 | itoa = "1.0.1" |
| 28 | −serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]} | |
| 31 | +serde = { version = "1.0.114", optional = true, default-features = false, features = [ | |
| 32 | + "derive", | |
| 33 | +] } | |
| 29 | 34 | |
| 30 | 35 | document-features = { version = "0.2.0", optional = true } |
| 31 | 36 | |
| 32 | 37 | [dev-dependencies] |
| 33 | 38 | pretty_assertions = "1.0.0" |
| 34 | −gix-testtools = { path = "../tests/tools"} | |
| 39 | +gix-testtools = { path = "../tests/tools" } | |
| 35 | 40 | gix-hash = { path = "../gix-hash" } |
| 36 | 41 | |
| 37 | 42 | [package.metadata.docs.rs] |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | pub(crate) mod function { |
| 2 | 2 | use bstr::ByteSlice; |
| 3 | − use btoi::btoi; | |
| 4 | 3 | use gix_date::{time::Sign, OffsetInSeconds, SecondsSinceUnixEpoch, Time}; |
| 4 | + use gix_utils::btoi::btoi; | |
| 5 | 5 | use winnow::{ |
| 6 | 6 | combinator::{alt, separated_pair, terminated}, |
| 7 | 7 | error::{AddContext, ParserError, StrContext}, |
| @@ -20,13 +20,17 @@ test = true · 19 unmodified lines | ||
| 20 | 20 | serde = ["dep:serde", "smallvec/serde", "gix-hash/serde"] |
| 21 | 21 | |
| 22 | 22 | [dependencies] |
| 23 | −gix-features = { version = "^0.38.0", path = "../gix-features", features = ["rustsha1", "progress"] } | |
| 23 | +gix-features = { version = "^0.38.0", path = "../gix-features", features = [ | |
| 24 | + "rustsha1", | |
| 25 | + "progress", | |
| 26 | +] } | |
| 24 | 27 | gix-hash = { version = "^0.14.1", path = "../gix-hash" } |
| 25 | 28 | gix-bitmap = { version = "^0.2.10", path = "../gix-bitmap" } |
| 26 | 29 | gix-object = { version = "^0.41.0", path = "../gix-object" } |
| 27 | 30 | gix-traverse = { version = "^0.37.0", path = "../gix-traverse" } |
| 28 | 31 | gix-lock = { version = "^13.0.0", path = "../gix-lock" } |
| 29 | 32 | gix-fs = { version = "^0.10.0", path = "../gix-fs" } |
| 33 | +gix-utils = { version = "^0.1.9", path = "../gix-utils" } | |
| 30 | 34 | |
| 31 | 35 | hashbrown = "0.14.3" |
| 32 | 36 | fnv = "1.0.7" |
| @@ -35,16 +39,20 @@ memmap2 = "0.9.0" · 2 unmodified lines | ||
| 35 | 39 | filetime = "0.2.15" |
| 36 | 40 | bstr = { version = "1.3.0", default-features = false } |
| 37 | 41 | |
| 38 | −serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] } | |
| 42 | +serde = { version = "1.0.114", optional = true, default-features = false, features = [ | |
| 43 | + "derive", | |
| 44 | +] } | |
| 39 | 45 | smallvec = "1.7.0" |
| 40 | −btoi = "0.4.3" | |
| 41 | 46 | itoa = "1.0.3" |
| 42 | 47 | bitflags = "2" |
| 43 | 48 | |
| 44 | 49 | document-features = { version = "0.2.0", optional = true } |
| 45 | 50 | |
| 46 | 51 | [target.'cfg(not(windows))'.dependencies] |
| 47 | −rustix = { version = "0.38.20", default-features = false, features = ["std", "fs"] } | |
| 52 | +rustix = { version = "0.38.20", default-features = false, features = [ | |
| 53 | + "std", | |
| 54 | + "fs", | |
| 55 | +] } | |
| 48 | 56 | libc = { version = "0.2.149" } |
| 49 | 57 | |
| 50 | 58 | [package.metadata.docs.rs] |
| @@ -22,10 +22,10 @@ fn one_recursive(data: &[u8], hash_len: usize) -> Option<(Tree, &[u8])> { · 21 unmodified lines | ||
| 22 | 22 | let (path, data) = split_at_byte_exclusive(data, 0)?; |
| 23 | 23 | |
| 24 | 24 | let (entry_count, data) = split_at_byte_exclusive(data, b' ')?; |
| 25 | − let num_entries: i32 = btoi::btoi(entry_count).ok()?; | |
| 25 | + let num_entries: i32 = gix_utils::btoi::btoi(entry_count).ok()?; | |
| 26 | 26 | |
| 27 | 27 | let (subtree_count, data) = split_at_byte_exclusive(data, b'\n')?; |
| 28 | − let subtree_count: usize = btoi::btou(subtree_count).ok()?; | |
| 28 | + let subtree_count: usize = gix_utils::btoi::btou(subtree_count).ok()?; | |
| 29 | 29 | |
| 30 | 30 | let (id, mut data) = if num_entries >= 0 { |
| 31 | 31 | let (hash, data) = split_at_pos(data, hash_len)?; |
| @@ -20,7 +20,13 @@ path = "./benches/decode_objects.rs" · 19 unmodified lines | ||
| 20 | 20 | |
| 21 | 21 | [features] |
| 22 | 22 | ## Data structures implement `serde::Serialize` and `serde::Deserialize`. |
| 23 | −serde = ["dep:serde", "bstr/serde", "smallvec/serde", "gix-hash/serde", "gix-actor/serde"] | |
| 23 | +serde = [ | |
| 24 | + "dep:serde", | |
| 25 | + "bstr/serde", | |
| 26 | + "smallvec/serde", | |
| 27 | + "gix-hash/serde", | |
| 28 | + "gix-actor/serde", | |
| 29 | +] | |
| 24 | 30 | ## When parsing objects by default errors will only be available on the granularity of success or failure, and with the above flag enabled |
| 25 | 31 | ## details information about the error location will be collected. |
| 26 | 32 | ## Use it in applications which expect broken or invalid objects or for debugging purposes. Incorrectly formatted objects aren't at all |
| @@ -28,28 +34,35 @@ serde = ["dep:serde", "bstr/serde", "smallvec/serde", "gix-hash/serde", "gix-act · 1 unmodified line | ||
| 28 | 34 | verbose-object-parsing-errors = ["winnow/std"] |
| 29 | 35 | |
| 30 | 36 | [dependencies] |
| 31 | −gix-features = { version = "^0.38.0", path = "../gix-features", features = ["rustsha1", "progress"] } | |
| 37 | +gix-features = { version = "^0.38.0", path = "../gix-features", features = [ | |
| 38 | + "rustsha1", | |
| 39 | + "progress", | |
| 40 | +] } | |
| 32 | 41 | gix-hash = { version = "^0.14.1", path = "../gix-hash" } |
| 33 | 42 | gix-validate = { version = "^0.8.3", path = "../gix-validate" } |
| 34 | 43 | gix-actor = { version = "^0.30.0", path = "../gix-actor" } |
| 35 | 44 | gix-date = { version = "^0.8.3", path = "../gix-date" } |
| 45 | +gix-utils = { version = "^0.1.9", path = "../gix-utils" } | |
| 36 | 46 | |
| 37 | −btoi = "0.4.2" | |
| 38 | 47 | itoa = "1.0.1" |
| 39 | 48 | thiserror = "1.0.34" |
| 40 | −bstr = { version = "1.3.0", default-features = false, features = ["std", "unicode"] } | |
| 49 | +bstr = { version = "1.3.0", default-features = false, features = [ | |
| 50 | + "std", | |
| 51 | + "unicode", | |
| 52 | +] } | |
| 41 | 53 | winnow = { version = "0.6.0", features = ["simd"] } |
| 42 | 54 | smallvec = { version = "1.4.0", features = ["write"] } |
| 43 | −serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]} | |
| 55 | +serde = { version = "1.0.114", optional = true, default-features = false, features = [ | |
| 56 | + "derive", | |
| 57 | +] } | |
| 44 | 58 | |
| 45 | 59 | document-features = { version = "0.2.0", optional = true } |
| 46 | 60 | |
| 47 | 61 | [dev-dependencies] |
| 48 | 62 | criterion = "0.5.1" |
| 49 | 63 | pretty_assertions = "1.0.0" |
| 50 | −gix-testtools = { path = "../tests/tools"} | |
| 64 | +gix-testtools = { path = "../tests/tools" } | |
| 51 | 65 | |
| 52 | 66 | [package.metadata.docs.rs] |
| 53 | 67 | all-features = true |
| 54 | 68 | features = ["document-features"] |
| 55 | − | |
| @@ -358,7 +358,7 @@ pub mod decode { · 357 unmodified lines | ||
| 358 | 358 | pub enum LooseHeaderDecodeError { |
| 359 | 359 | #[error("{message}: {number:?}")] |
| 360 | 360 | ParseIntegerError { |
| 361 | − source: btoi::ParseIntegerError, | |
| 361 | + source: gix_utils::btoi::ParseIntegerError, | |
| 362 | 362 | message: &'static str, |
| 363 | 363 | number: bstr::BString, |
| 364 | 364 | }, |
| @@ -383,7 +383,7 @@ pub mod decode { · 18 unmodified lines | ||
| 383 | 383 | message: "Did not find 0 byte in header", |
| 384 | 384 | })?; |
| 385 | 385 | let size_bytes = &input[kind_end + 1..size_end]; |
| 386 | − let size = btoi::btoi(size_bytes).map_err(|source| ParseIntegerError { | |
| 386 | + let size = gix_utils::btoi::btoi(size_bytes).map_err(|source| ParseIntegerError { | |
| 387 | 387 | source, |
| 388 | 388 | message: "Object size in header could not be parsed", |
| 389 | 389 | number: size_bytes.into(), |
| @@ -23,7 +23,12 @@ doctest = false · 22 unmodified lines | ||
| 23 | 23 | ## If set, blocking command implementations are available and will use the blocking version of the `gix-transport` crate. |
| 24 | 24 | blocking-client = ["gix-transport/blocking-client", "maybe-async/is_sync"] |
| 25 | 25 | ## As above, but provides async implementations instead. |
| 26 | −async-client = ["gix-transport/async-client", "async-trait", "futures-io", "futures-lite"] | |
| 26 | +async-client = [ | |
| 27 | + "gix-transport/async-client", | |
| 28 | + "async-trait", | |
| 29 | + "futures-io", | |
| 30 | + "futures-lite", | |
| 31 | +] | |
| 27 | 32 | |
| 28 | 33 | #! ### Other |
| 29 | 34 | ## Data structures implement `serde::Serialize` and `serde::Deserialize`. |
| @@ -40,17 +45,24 @@ path = "tests/async-protocol.rs" · 10 unmodified lines | ||
| 40 | 45 | required-features = ["async-client"] |
| 41 | 46 | |
| 42 | 47 | [dependencies] |
| 43 | −gix-features = { version = "^0.38.0", path = "../gix-features", features = ["progress"] } | |
| 48 | +gix-features = { version = "^0.38.0", path = "../gix-features", features = [ | |
| 49 | + "progress", | |
| 50 | +] } | |
| 44 | 51 | gix-transport = { version = "^0.41.0", path = "../gix-transport" } |
| 45 | 52 | gix-hash = { version = "^0.14.1", path = "../gix-hash" } |
| 46 | 53 | gix-date = { version = "^0.8.3", path = "../gix-date" } |
| 47 | 54 | gix-credentials = { version = "^0.24.0", path = "../gix-credentials" } |
| 55 | +gix-utils = { version = "^0.1.9", path = "../gix-utils" } | |
| 48 | 56 | |
| 49 | 57 | thiserror = "1.0.32" |
| 50 | −serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]} | |
| 51 | −bstr = { version = "1.3.0", default-features = false, features = ["std", "unicode"] } | |
| 58 | +serde = { version = "1.0.114", optional = true, default-features = false, features = [ | |
| 59 | + "derive", | |
| 60 | +] } | |
| 61 | +bstr = { version = "1.3.0", default-features = false, features = [ | |
| 62 | + "std", | |
| 63 | + "unicode", | |
| 64 | +] } | |
| 52 | 65 | winnow = { version = "0.6.0", features = ["simd"] } |
| 53 | −btoi = "0.4.2" | |
| 54 | 66 | |
| 55 | 67 | # for async-client |
| 56 | 68 | async-trait = { version = "0.1.51", optional = true } |
| @@ -62,7 +74,7 @@ document-features = { version = "0.2.0", optional = true } · 5 unmodified lines | ||
| 62 | 74 | |
| 63 | 75 | [dev-dependencies] |
| 64 | 76 | async-std = { version = "1.9.0", features = ["attributes"] } |
| 65 | −gix-packetline = { path = "../gix-packetline" ,version = "^0.17.3" } | |
| 77 | +gix-packetline = { path = "../gix-packetline", version = "^0.17.3" } | |
| 66 | 78 | gix-testtools = { path = "../tests/tools" } |
| 67 | 79 | |
| 68 | 80 | [package.metadata.docs.rs] |
| @@ -75,7 +75,7 @@ impl<'a> RemoteProgress<'a> { · 74 unmodified lines | ||
| 75 | 75 | |
| 76 | 76 | fn parse_number(i: &mut &[u8]) -> PResult<usize, ()> { |
| 77 | 77 | take_till(0.., |c: u8| !c.is_ascii_digit()) |
| 78 | − .try_map(btoi::btoi) | |
| 78 | + .try_map(gix_utils::btoi::btoi) | |
| 79 | 79 | .parse_next(i) |
| 80 | 80 | } |
| 81 | 81 | |
| @@ -13,6 +13,7 @@ include = ["src/**/*", "LICENSE-*"] · 12 unmodified lines | ||
| 13 | 13 | doctest = false |
| 14 | 14 | |
| 15 | 15 | [dependencies] |
| 16 | −bstr = { version = "1.3.0", default-features = false, features = ["std"]} | |
| 16 | +gix-utils = { version = "^0.1.9", path = "../gix-utils" } | |
| 17 | + | |
| 18 | +bstr = { version = "1.3.0", default-features = false, features = ["std"] } | |
| 17 | 19 | thiserror = "1.0.38" |
| 18 | −btoi = "0.4.2" | |
| @@ -89,7 +89,8 @@ pub fn undo(input: &BStr) -> Result<(Cow<'_, BStr>, usize), undo::Error> { · 88 unmodified lines | ||
| 89 | 89 | })? |
| 90 | 90 | .read_exact(&mut buf[1..]) |
| 91 | 91 | .expect("impossible to fail as numbers match"); |
| 92 | − let byte = btoi::btou_radix(&buf, 8).map_err(|e| undo::Error::new(e, original))?; | |
| 92 | + let byte = | |
| 93 | + gix_utils::btoi::btou_radix(&buf, 8).map_err(|e| undo::Error::new(e, original))?; | |
| 93 | 94 | out.push(byte); |
| 94 | 95 | input = &input[2..]; |
| 95 | 96 | consumed += 2; |
| @@ -0,0 +1,392 @@ | ||
| 1 | +// ported from https://github.com/niklasf/rust-btoi version 0.4.3 | |
| 2 | +// see https://github.com/Byron/gitoxide/issues/729#issuecomment-1941515655 | |
| 3 | + | |
| 4 | +/// An error that can occur when parsing an integer. | |
| 5 | +/// | |
| 6 | +/// * No digits | |
| 7 | +/// * Invalid digit | |
| 8 | +/// * Overflow | |
| 9 | +/// * Underflow | |
| 10 | +#[derive(Debug, Clone, PartialEq, Eq)] | |
| 11 | +pub struct ParseIntegerError { | |
| 12 | + kind: ErrorKind, | |
| 13 | +} | |
| 14 | + | |
| 15 | +#[derive(Debug, Clone, PartialEq, Eq)] | |
| 16 | +enum ErrorKind { | |
| 17 | + Empty, | |
| 18 | + InvalidDigit, | |
| 19 | + Overflow, | |
| 20 | + Underflow, | |
| 21 | +} | |
| 22 | + | |
| 23 | +impl ParseIntegerError { | |
| 24 | + fn desc(&self) -> &str { | |
| 25 | + match self.kind { | |
| 26 | + ErrorKind::Empty => "cannot parse integer without digits", | |
| 27 | + ErrorKind::InvalidDigit => "invalid digit found in slice", | |
| 28 | + ErrorKind::Overflow => "number too large to fit in target type", | |
| 29 | + ErrorKind::Underflow => "number too small to fit in target type", | |
| 30 | + } | |
| 31 | + } | |
| 32 | +} | |
| 33 | + | |
| 34 | +impl std::fmt::Display for ParseIntegerError { | |
| 35 | + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 36 | + self.desc().fmt(f) | |
| 37 | + } | |
| 38 | +} | |
| 39 | + | |
| 40 | +impl std::error::Error for ParseIntegerError { | |
| 41 | + fn description(&self) -> &str { | |
| 42 | + self.desc() | |
| 43 | + } | |
| 44 | +} | |
| 45 | +/// minimal subset of traits used by btoi_radix and btou_radix | |
| 46 | +pub trait MinNumTraits: Sized + Copy { | |
| 47 | + /// | |
| 48 | + fn from_u32(n: u32) -> Option<Self>; | |
| 49 | + /// | |
| 50 | + fn zero() -> Self; | |
| 51 | + /// | |
| 52 | + fn checked_mul(self, v: Self) -> Option<Self>; | |
| 53 | + /// | |
| 54 | + fn checked_add(self, v: Self) -> Option<Self>; | |
| 55 | + /// | |
| 56 | + fn checked_sub(self, v: Self) -> Option<Self>; | |
| 57 | +} | |
| 58 | + | |
| 59 | +impl MinNumTraits for i64 { | |
| 60 | + fn from_u32(n: u32) -> Option<i64> { | |
| 61 | + Some(n.into()) | |
| 62 | + } | |
| 63 | + | |
| 64 | + fn zero() -> Self { | |
| 65 | + 0 | |
| 66 | + } | |
| 67 | + | |
| 68 | + fn checked_mul(self, v: i64) -> Option<i64> { | |
| 69 | + i64::checked_mul(self, v) | |
| 70 | + } | |
| 71 | + | |
| 72 | + fn checked_add(self, v: i64) -> Option<i64> { | |
| 73 | + i64::checked_add(self, v) | |
| 74 | + } | |
| 75 | + | |
| 76 | + fn checked_sub(self, v: i64) -> Option<i64> { | |
| 77 | + i64::checked_sub(self, v) | |
| 78 | + } | |
| 79 | +} | |
| 80 | + | |
| 81 | +impl MinNumTraits for u64 { | |
| 82 | + fn from_u32(n: u32) -> Option<u64> { | |
| 83 | + Some(n.into()) | |
| 84 | + } | |
| 85 | + | |
| 86 | + fn zero() -> Self { | |
| 87 | + 0 | |
| 88 | + } | |
| 89 | + | |
| 90 | + fn checked_mul(self, v: u64) -> Option<u64> { | |
| 91 | + u64::checked_mul(self, v) | |
| 92 | + } | |
| 93 | + | |
| 94 | + fn checked_add(self, v: u64) -> Option<u64> { | |
| 95 | + u64::checked_add(self, v) | |
| 96 | + } | |
| 97 | + | |
| 98 | + fn checked_sub(self, v: u64) -> Option<u64> { | |
| 99 | + u64::checked_sub(self, v) | |
| 100 | + } | |
| 101 | +} | |
| 102 | + | |
| 103 | +impl MinNumTraits for usize { | |
| 104 | + fn from_u32(n: u32) -> Option<usize> { | |
| 105 | + n.try_into().ok() | |
| 106 | + } | |
| 107 | + | |
| 108 | + fn zero() -> Self { | |
| 109 | + 0 | |
| 110 | + } | |
| 111 | + | |
| 112 | + fn checked_mul(self, v: usize) -> Option<usize> { | |
| 113 | + usize::checked_mul(self, v) | |
| 114 | + } | |
| 115 | + | |
| 116 | + fn checked_add(self, v: usize) -> Option<usize> { | |
| 117 | + usize::checked_add(self, v) | |
| 118 | + } | |
| 119 | + | |
| 120 | + fn checked_sub(self, v: usize) -> Option<usize> { | |
| 121 | + usize::checked_sub(self, v) | |
| 122 | + } | |
| 123 | +} | |
| 124 | + | |
| 125 | +impl MinNumTraits for i32 { | |
| 126 | + fn from_u32(n: u32) -> Option<Self> { | |
| 127 | + n.try_into().ok() | |
| 128 | + } | |
| 129 | + | |
| 130 | + fn zero() -> Self { | |
| 131 | + 0 | |
| 132 | + } | |
| 133 | + | |
| 134 | + fn checked_mul(self, v: i32) -> Option<i32> { | |
| 135 | + i32::checked_mul(self, v) | |
| 136 | + } | |
| 137 | + | |
| 138 | + fn checked_add(self, v: i32) -> Option<i32> { | |
| 139 | + i32::checked_add(self, v) | |
| 140 | + } | |
| 141 | + | |
| 142 | + fn checked_sub(self, v: i32) -> Option<i32> { | |
| 143 | + i32::checked_sub(self, v) | |
| 144 | + } | |
| 145 | +} | |
| 146 | + | |
| 147 | +impl MinNumTraits for u8 { | |
| 148 | + fn from_u32(n: u32) -> Option<Self> { | |
| 149 | + n.try_into().ok() | |
| 150 | + } | |
| 151 | + | |
| 152 | + fn zero() -> Self { | |
| 153 | + 0 | |
| 154 | + } | |
| 155 | + | |
| 156 | + fn checked_mul(self, v: u8) -> Option<u8> { | |
| 157 | + u8::checked_mul(self, v) | |
| 158 | + } | |
| 159 | + | |
| 160 | + fn checked_add(self, v: u8) -> Option<u8> { | |
| 161 | + u8::checked_add(self, v) | |
| 162 | + } | |
| 163 | + | |
| 164 | + fn checked_sub(self, v: u8) -> Option<u8> { | |
| 165 | + u8::checked_sub(self, v) | |
| 166 | + } | |
| 167 | +} | |
| 168 | + | |
| 169 | +/// Converts a byte slice to an integer. Signs are not allowed. | |
| 170 | +/// | |
| 171 | +/// # Errors | |
| 172 | +/// | |
| 173 | +/// Returns [`ParseIntegerError`] for any of the following conditions: | |
| 174 | +/// | |
| 175 | +/// * `bytes` is empty | |
| 176 | +/// * not all characters of `bytes` are `0-9` | |
| 177 | +/// * the number overflows `I` | |
| 178 | +/// | |
| 179 | +/// # Panics | |
| 180 | +/// | |
| 181 | +/// Panics in the pathological case that there is no representation of `10` | |
| 182 | +/// in `I`. | |
| 183 | +/// | |
| 184 | +/// # Examples | |
| 185 | +/// | |
| 186 | +/// ``` | |
| 187 | +/// # use btoi::btou; | |
| 188 | +/// assert_eq!(Ok(12345), btou(b"12345")); | |
| 189 | +/// assert!(btou::<u8>(b"+1").is_err()); // only btoi allows signs | |
| 190 | +/// assert!(btou::<u8>(b"256").is_err()); // overflow | |
| 191 | +/// ``` | |
| 192 | +/// | |
| 193 | +/// [`ParseIntegerError`]: struct.ParseIntegerError.html | |
| 194 | +#[track_caller] | |
| 195 | +pub fn btou<I: MinNumTraits>(bytes: &[u8]) -> Result<I, ParseIntegerError> { | |
| 196 | + btou_radix(bytes, 10) | |
| 197 | +} | |
| 198 | + | |
| 199 | +/// Converts a byte slice in a given base to an integer. Signs are not allowed. | |
| 200 | +/// | |
| 201 | +/// # Errors | |
| 202 | +/// | |
| 203 | +/// Returns [`ParseIntegerError`] for any of the following conditions: | |
| 204 | +/// | |
| 205 | +/// * `bytes` is empty | |
| 206 | +/// * not all characters of `bytes` are `0-9`, `a-z` or `A-Z` | |
| 207 | +/// * not all characters refer to digits in the given `radix` | |
| 208 | +/// * the number overflows `I` | |
| 209 | +/// | |
| 210 | +/// # Panics | |
| 211 | +/// | |
| 212 | +/// Panics if `radix` is not in the range `2..=36` (or in the pathological | |
| 213 | +/// case that there is no representation of `radix` in `I`). | |
| 214 | +/// | |
| 215 | +/// # Examples | |
| 216 | +/// | |
| 217 | +/// ``` | |
| 218 | +/// # use btoi::btou_radix; | |
| 219 | +/// assert_eq!(Ok(255), btou_radix(b"ff", 16)); | |
| 220 | +/// assert_eq!(Ok(42), btou_radix(b"101010", 2)); | |
| 221 | +/// ``` | |
| 222 | +/// | |
| 223 | +/// [`ParseIntegerError`]: struct.ParseIntegerError.html | |
| 224 | +pub fn btou_radix<I: MinNumTraits>(bytes: &[u8], radix: u32) -> Result<I, ParseIntegerError> { | |
| 225 | + assert!( | |
| 226 | + (2..=36).contains(&radix), | |
| 227 | + "radix must lie in the range 2..=36, found {}", | |
| 228 | + radix | |
| 229 | + ); | |
| 230 | + | |
| 231 | + let base = I::from_u32(radix).expect("radix can be represented as integer"); | |
| 232 | + | |
| 233 | + if bytes.is_empty() { | |
| 234 | + return Err(ParseIntegerError { kind: ErrorKind::Empty }); | |
| 235 | + } | |
| 236 | + | |
| 237 | + let mut result = I::zero(); | |
| 238 | + | |
| 239 | + for &digit in bytes { | |
| 240 | + let x = match char::from(digit).to_digit(radix).and_then(I::from_u32) { | |
| 241 | + Some(x) => x, | |
| 242 | + None => { | |
| 243 | + return Err(ParseIntegerError { | |
| 244 | + kind: ErrorKind::InvalidDigit, | |
| 245 | + }) | |
| 246 | + } | |
| 247 | + }; | |
| 248 | + result = match result.checked_mul(base) { | |
| 249 | + Some(result) => result, | |
| 250 | + None => { | |
| 251 | + return Err(ParseIntegerError { | |
| 252 | + kind: ErrorKind::Overflow, | |
| 253 | + }) | |
| 254 | + } | |
| 255 | + }; | |
| 256 | + result = match result.checked_add(x) { | |
| 257 | + Some(result) => result, | |
| 258 | + None => { | |
| 259 | + return Err(ParseIntegerError { | |
| 260 | + kind: ErrorKind::Overflow, | |
| 261 | + }) | |
| 262 | + } | |
| 263 | + }; | |
| 264 | + } | |
| 265 | + | |
| 266 | + Ok(result) | |
| 267 | +} | |
| 268 | + | |
| 269 | +/// Converts a byte slice to an integer. | |
| 270 | +/// | |
| 271 | +/// Like [`btou`], but numbers may optionally start with a sign (`-` or `+`). | |
| 272 | +/// | |
| 273 | +/// # Errors | |
| 274 | +/// | |
| 275 | +/// Returns [`ParseIntegerError`] for any of the following conditions: | |
| 276 | +/// | |
| 277 | +/// * `bytes` has no digits | |
| 278 | +/// * not all characters of `bytes` are `0-9`, excluding an optional leading | |
| 279 | +/// sign | |
| 280 | +/// * the number overflows or underflows `I` | |
| 281 | +/// | |
| 282 | +/// # Panics | |
| 283 | +/// | |
| 284 | +/// Panics in the pathological case that there is no representation of `10` | |
| 285 | +/// in `I`. | |
| 286 | +/// | |
| 287 | +/// # Examples | |
| 288 | +/// | |
| 289 | +/// ``` | |
| 290 | +/// # use btoi::btoi; | |
| 291 | +/// assert_eq!(Ok(123), btoi(b"123")); | |
| 292 | +/// assert_eq!(Ok(123), btoi(b"+123")); | |
| 293 | +/// assert_eq!(Ok(-123), btoi(b"-123")); | |
| 294 | +/// | |
| 295 | +/// assert!(btoi::<i16>(b"123456789").is_err()); // overflow | |
| 296 | +/// assert!(btoi::<u32>(b"-1").is_err()); // underflow | |
| 297 | +/// | |
| 298 | +/// assert!(btoi::<i32>(b" 42").is_err()); // leading space | |
| 299 | +/// ``` | |
| 300 | +/// | |
| 301 | +/// [`btou`]: fn.btou.html | |
| 302 | +/// [`ParseIntegerError`]: struct.ParseIntegerError.html | |
| 303 | +pub fn btoi<I: MinNumTraits>(bytes: &[u8]) -> Result<I, ParseIntegerError> { | |
| 304 | + btoi_radix(bytes, 10) | |
| 305 | +} | |
| 306 | + | |
| 307 | +/// Converts a byte slice in a given base to an integer. | |
| 308 | +/// | |
| 309 | +/// Like [`btou_radix`], but numbers may optionally start with a sign | |
| 310 | +/// (`-` or `+`). | |
| 311 | +/// | |
| 312 | +/// # Errors | |
| 313 | +/// | |
| 314 | +/// Returns [`ParseIntegerError`] for any of the following conditions: | |
| 315 | +/// | |
| 316 | +/// * `bytes` has no digits | |
| 317 | +/// * not all characters of `bytes` are `0-9`, `a-z`, `A-Z`, exluding an | |
| 318 | +/// optional leading sign | |
| 319 | +/// * not all characters refer to digits in the given `radix`, exluding an | |
| 320 | +/// optional leading sign | |
| 321 | +/// * the number overflows or underflows `I` | |
| 322 | +/// | |
| 323 | +/// # Panics | |
| 324 | +/// | |
| 325 | +/// Panics if `radix` is not in the range `2..=36` (or in the pathological | |
| 326 | +/// case that there is no representation of `radix` in `I`). | |
| 327 | +/// | |
| 328 | +/// # Examples | |
| 329 | +/// | |
| 330 | +/// ``` | |
| 331 | +/// # use btoi::btoi_radix; | |
| 332 | +/// assert_eq!(Ok(10), btoi_radix(b"a", 16)); | |
| 333 | +/// assert_eq!(Ok(10), btoi_radix(b"+a", 16)); | |
| 334 | +/// assert_eq!(Ok(-42), btoi_radix(b"-101010", 2)); | |
| 335 | +/// ``` | |
| 336 | +/// | |
| 337 | +/// [`btou_radix`]: fn.btou_radix.html | |
| 338 | +/// [`ParseIntegerError`]: struct.ParseIntegerError.html | |
| 339 | +fn btoi_radix<I: MinNumTraits>(bytes: &[u8], radix: u32) -> Result<I, ParseIntegerError> { | |
| 340 | + assert!( | |
| 341 | + (2..=36).contains(&radix), | |
| 342 | + "radix must lie in the range 2..=36, found {}", | |
| 343 | + radix | |
| 344 | + ); | |
| 345 | + | |
| 346 | + let base = I::from_u32(radix).expect("radix can be represented as integer"); | |
| 347 | + | |
| 348 | + if bytes.is_empty() { | |
| 349 | + return Err(ParseIntegerError { kind: ErrorKind::Empty }); | |
| 350 | + } | |
| 351 | + | |
| 352 | + let digits = match bytes[0] { | |
| 353 | + b'+' => return btou_radix(&bytes[1..], radix), | |
| 354 | + b'-' => &bytes[1..], | |
| 355 | + _ => return btou_radix(bytes, radix), | |
| 356 | + }; | |
| 357 | + | |
| 358 | + if digits.is_empty() { | |
| 359 | + return Err(ParseIntegerError { kind: ErrorKind::Empty }); | |
| 360 | + } | |
| 361 | + | |
| 362 | + let mut result = I::zero(); | |
| 363 | + | |
| 364 | + for &digit in digits { | |
| 365 | + let x = match char::from(digit).to_digit(radix).and_then(I::from_u32) { | |
| 366 | + Some(x) => x, | |
| 367 | + None => { | |
| 368 | + return Err(ParseIntegerError { | |
| 369 | + kind: ErrorKind::InvalidDigit, | |
| 370 | + }) | |
| 371 | + } | |
| 372 | + }; | |
| 373 | + result = match result.checked_mul(base) { | |
| 374 | + Some(result) => result, | |
| 375 | + None => { | |
| 376 | + return Err(ParseIntegerError { | |
| 377 | + kind: ErrorKind::Underflow, | |
| 378 | + }) | |
| 379 | + } | |
| 380 | + }; | |
| 381 | + result = match result.checked_sub(x) { | |
| 382 | + Some(result) => result, | |
| 383 | + None => { | |
| 384 | + return Err(ParseIntegerError { | |
| 385 | + kind: ErrorKind::Underflow, | |
| 386 | + }) | |
| 387 | + } | |
| 388 | + }; | |
| 389 | + } | |
| 390 | + | |
| 391 | + Ok(result) | |
| 392 | +} | |
| @@ -13,6 +13,9 @@ pub mod buffers; · 12 unmodified lines | ||
| 13 | 13 | /// |
| 14 | 14 | pub mod str; |
| 15 | 15 | |
| 16 | +/// | |
| 17 | +pub mod btoi; | |
| 18 | + | |
| 16 | 19 | /// A utility to do buffer-swapping with. |
| 17 | 20 | /// |
| 18 | 21 | /// Use `src` to read from and `dest` to write to, and after actually changing data, call [Buffers::swap()]. |