зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1660551 - Update mp4parse-rust to d5a37fd. r=kinetik
Differential Revision: https://phabricator.services.mozilla.com/D87931
This commit is contained in:
Родитель
94c26b235a
Коммит
14a9a05444
|
@ -15,7 +15,7 @@ tag = "v0.4.10"
|
|||
[source."https://github.com/mozilla/mp4parse-rust"]
|
||||
git = "https://github.com/mozilla/mp4parse-rust"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "63325444ae3388599f2f222775eebdde4c2f9f30"
|
||||
rev = "d5a37fd0bd51e06a53274c68213b00136aba83a6"
|
||||
|
||||
[source."https://github.com/mozilla/application-services"]
|
||||
git = "https://github.com/mozilla/application-services"
|
||||
|
|
|
@ -3121,7 +3121,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "mp4parse"
|
||||
version = "0.11.4"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=63325444ae3388599f2f222775eebdde4c2f9f30#63325444ae3388599f2f222775eebdde4c2f9f30"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=d5a37fd0bd51e06a53274c68213b00136aba83a6#d5a37fd0bd51e06a53274c68213b00136aba83a6"
|
||||
dependencies = [
|
||||
"bitreader",
|
||||
"byteorder",
|
||||
|
@ -3138,7 +3138,7 @@ version = "0.1.0"
|
|||
[[package]]
|
||||
name = "mp4parse_capi"
|
||||
version = "0.11.4"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=63325444ae3388599f2f222775eebdde4c2f9f30#63325444ae3388599f2f222775eebdde4c2f9f30"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=d5a37fd0bd51e06a53274c68213b00136aba83a6#d5a37fd0bd51e06a53274c68213b00136aba83a6"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"log",
|
||||
|
@ -3388,19 +3388,21 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.39"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
|
||||
checksum = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
||||
dependencies = [
|
||||
"autocfg 0.1.6",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.37"
|
||||
version = "0.1.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124"
|
||||
checksum = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e"
|
||||
dependencies = [
|
||||
"autocfg 0.1.6",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"107804fbf8f667fbad45e7dea9fa1bb32ce8ef5580b543a54455e678d7769708","src/boxes.rs":"5f84805435af90034075709867e02c74a198e26dc628a9fc95df034928ee5bbc","src/fallible.rs":"836a36c2bc9803aead4bb24621e4fa6176c77b3752e69459a1f36555eb8bf2ec","src/lib.rs":"7c8bde48b42f5470a937d3affc4452e06b2158ff07c207f39376bdca11efa832","src/macros.rs":"76c840f9299797527fe71aa5b378ffb01312767372b45cc62deddb19775400ae","src/tests.rs":"f1a27e785d4006cd910ca3c48c8a972da1db9c9b4a67185f67a191ddc3c69328","tests/bug-1655846.avif":"e0a5a06225800fadf05f5352503a4cec11af73eef705c43b4acab5f4a99dea50","tests/overflow.rs":"16b591d8def1a155b3b997622f6ea255536870d99c3d8f97c51755b77a50de3c","tests/public.rs":"fd646ffd5fab8beed5949b87482048ba400438fa90860f86f357a7f6141dc649"},"package":null}
|
||||
{"files":{"Cargo.toml":"f48619a9cfbcfcaf657d6b1e4894d2c68afd4b1c620d42d6923f814611bf6186","src/boxes.rs":"5f84805435af90034075709867e02c74a198e26dc628a9fc95df034928ee5bbc","src/fallible.rs":"7dc89699f1e75433ab5c6bbd23807383b3b918fe572d41e68e5a270591b0a4ab","src/lib.rs":"ff16461743a5e09c2dc8ade092206e4c84ddcae061766b38d53eff6f94916b2e","src/macros.rs":"76c840f9299797527fe71aa5b378ffb01312767372b45cc62deddb19775400ae","src/tests.rs":"f1a27e785d4006cd910ca3c48c8a972da1db9c9b4a67185f67a191ddc3c69328","tests/bug-1655846.avif":"e0a5a06225800fadf05f5352503a4cec11af73eef705c43b4acab5f4a99dea50","tests/overflow.rs":"16b591d8def1a155b3b997622f6ea255536870d99c3d8f97c51755b77a50de3c","tests/public.rs":"fd646ffd5fab8beed5949b87482048ba400438fa90860f86f357a7f6141dc649"},"package":null}
|
|
@ -28,7 +28,7 @@ travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" }
|
|||
byteorder = "1.2.1"
|
||||
bitreader = { version = "0.3.2" }
|
||||
hashbrown = "0.7.1"
|
||||
num-traits = "0.2.0"
|
||||
num-traits = "=0.2.10"
|
||||
log = "0.4"
|
||||
static_assertions = "1.1.0"
|
||||
|
||||
|
|
|
@ -256,7 +256,7 @@ impl<T> TryVec<T> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn reserve(&mut self, additional: usize) -> Result<()> {
|
||||
pub fn reserve(&mut self, additional: usize) -> Result<()> {
|
||||
#[cfg(feature = "mp4parse_fallible")]
|
||||
{
|
||||
let available = self
|
||||
|
|
|
@ -58,7 +58,7 @@ impl ToU64 for usize {
|
|||
|
||||
/// A trait to indicate a type can be infallibly converted to `usize`.
|
||||
/// This should only be implemented for infallible conversions, so only unsigned types are valid.
|
||||
trait ToUsize {
|
||||
pub trait ToUsize {
|
||||
fn to_usize(self) -> usize;
|
||||
}
|
||||
|
||||
|
@ -975,16 +975,16 @@ pub struct TrackTimeScale<T: Num>(pub T, pub usize);
|
|||
/// A time to be scaled by the track's local (mdhd) timescale.
|
||||
/// Members are time in scale units and the track id.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct TrackScaledTime<T: Num>(pub T, pub usize);
|
||||
pub struct TrackScaledTime<T>(pub T, pub usize);
|
||||
|
||||
impl<T> std::ops::Add for TrackScaledTime<T>
|
||||
where
|
||||
T: Num,
|
||||
T: num_traits::CheckedAdd,
|
||||
{
|
||||
type Output = TrackScaledTime<T>;
|
||||
type Output = Option<Self>;
|
||||
|
||||
fn add(self, other: TrackScaledTime<T>) -> TrackScaledTime<T> {
|
||||
TrackScaledTime::<T>(self.0 + other.0, self.1)
|
||||
fn add(self, other: TrackScaledTime<T>) -> Self::Output {
|
||||
self.0.checked_add(&other.0).map(|sum| Self(sum, self.1))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1953,6 +1953,7 @@ fn read_stbl<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
|
|||
}
|
||||
|
||||
/// Parse an ftyp box.
|
||||
/// See ISO 14496-12:2015 § 4.3
|
||||
fn read_ftyp<T: Read>(src: &mut BMFFBox<T>) -> Result<FileTypeBox> {
|
||||
let major = be_u32(src)?;
|
||||
let minor = be_u32(src)?;
|
||||
|
@ -1962,7 +1963,7 @@ fn read_ftyp<T: Read>(src: &mut BMFFBox<T>) -> Result<FileTypeBox> {
|
|||
}
|
||||
// Is a brand_count of zero valid?
|
||||
let brand_count = bytes_left / 4;
|
||||
let mut brands = TryVec::new();
|
||||
let mut brands = TryVec::with_capacity(brand_count.try_into()?)?;
|
||||
for _ in 0..brand_count {
|
||||
brands.push(be_u32(src)?.into())?;
|
||||
}
|
||||
|
@ -2058,10 +2059,11 @@ fn read_tkhd<T: Read>(src: &mut BMFFBox<T>) -> Result<TrackHeaderBox> {
|
|||
}
|
||||
|
||||
/// Parse a elst box.
|
||||
/// See ISO 14496-12:2015 § 8.6.6
|
||||
fn read_elst<T: Read>(src: &mut BMFFBox<T>) -> Result<EditListBox> {
|
||||
let (version, _) = read_fullbox_extra(src)?;
|
||||
let edit_count = be_u32_with_limit(src)?;
|
||||
let mut edits = TryVec::new();
|
||||
let mut edits = TryVec::with_capacity(edit_count.to_usize())?;
|
||||
for _ in 0..edit_count {
|
||||
let (segment_duration, media_time) = match version {
|
||||
1 => {
|
||||
|
@ -2133,10 +2135,11 @@ fn read_mdhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MediaHeaderBox> {
|
|||
}
|
||||
|
||||
/// Parse a stco box.
|
||||
/// See ISO 14496-12:2015 § 8.7.5
|
||||
fn read_stco<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> {
|
||||
let (_, _) = read_fullbox_extra(src)?;
|
||||
let offset_count = be_u32_with_limit(src)?;
|
||||
let mut offsets = TryVec::new();
|
||||
let mut offsets = TryVec::with_capacity(offset_count.to_usize())?;
|
||||
for _ in 0..offset_count {
|
||||
offsets.push(be_u32(src)?.into())?;
|
||||
}
|
||||
|
@ -2148,10 +2151,11 @@ fn read_stco<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> {
|
|||
}
|
||||
|
||||
/// Parse a co64 box.
|
||||
/// See ISO 14496-12:2015 § 8.7.5
|
||||
fn read_co64<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> {
|
||||
let (_, _) = read_fullbox_extra(src)?;
|
||||
let offset_count = be_u32_with_limit(src)?;
|
||||
let mut offsets = TryVec::new();
|
||||
let mut offsets = TryVec::with_capacity(offset_count.to_usize())?;
|
||||
for _ in 0..offset_count {
|
||||
offsets.push(be_u64(src)?)?;
|
||||
}
|
||||
|
@ -2163,10 +2167,11 @@ fn read_co64<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> {
|
|||
}
|
||||
|
||||
/// Parse a stss box.
|
||||
/// See ISO 14496-12:2015 § 8.6.2
|
||||
fn read_stss<T: Read>(src: &mut BMFFBox<T>) -> Result<SyncSampleBox> {
|
||||
let (_, _) = read_fullbox_extra(src)?;
|
||||
let sample_count = be_u32_with_limit(src)?;
|
||||
let mut samples = TryVec::new();
|
||||
let mut samples = TryVec::with_capacity(sample_count.to_usize())?;
|
||||
for _ in 0..sample_count {
|
||||
samples.push(be_u32(src)?)?;
|
||||
}
|
||||
|
@ -2178,10 +2183,11 @@ fn read_stss<T: Read>(src: &mut BMFFBox<T>) -> Result<SyncSampleBox> {
|
|||
}
|
||||
|
||||
/// Parse a stsc box.
|
||||
/// See ISO 14496-12:2015 § 8.7.4
|
||||
fn read_stsc<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleToChunkBox> {
|
||||
let (_, _) = read_fullbox_extra(src)?;
|
||||
let sample_count = be_u32_with_limit(src)?;
|
||||
let mut samples = TryVec::new();
|
||||
let mut samples = TryVec::with_capacity(sample_count.to_usize())?;
|
||||
for _ in 0..sample_count {
|
||||
let first_chunk = be_u32(src)?;
|
||||
let samples_per_chunk = be_u32_with_limit(src)?;
|
||||
|
@ -2199,16 +2205,23 @@ fn read_stsc<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleToChunkBox> {
|
|||
Ok(SampleToChunkBox { samples })
|
||||
}
|
||||
|
||||
/// Parse a Composition Time to Sample Box
|
||||
/// See ISO 14496-12:2015 § 8.6.1.3
|
||||
fn read_ctts<T: Read>(src: &mut BMFFBox<T>) -> Result<CompositionOffsetBox> {
|
||||
let (version, _) = read_fullbox_extra(src)?;
|
||||
|
||||
let counts = u64::from(be_u32_with_limit(src)?);
|
||||
let counts = be_u32_with_limit(src)?;
|
||||
|
||||
if src.bytes_left() < counts.checked_mul(8).expect("counts -> bytes overflow") {
|
||||
if src.bytes_left()
|
||||
< counts
|
||||
.checked_mul(8)
|
||||
.expect("counts -> bytes overflow")
|
||||
.into()
|
||||
{
|
||||
return Err(Error::InvalidData("insufficient data in 'ctts' box"));
|
||||
}
|
||||
|
||||
let mut offsets = TryVec::new();
|
||||
let mut offsets = TryVec::with_capacity(counts.to_usize())?;
|
||||
for _ in 0..counts {
|
||||
let (sample_count, time_offset) = match version {
|
||||
// According to spec, Version0 shoule be used when version == 0;
|
||||
|
@ -2235,12 +2248,14 @@ fn read_ctts<T: Read>(src: &mut BMFFBox<T>) -> Result<CompositionOffsetBox> {
|
|||
}
|
||||
|
||||
/// Parse a stsz box.
|
||||
/// See ISO 14496-12:2015 § 8.7.3.2
|
||||
fn read_stsz<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleSizeBox> {
|
||||
let (_, _) = read_fullbox_extra(src)?;
|
||||
let sample_size = be_u32(src)?;
|
||||
let sample_count = be_u32_with_limit(src)?;
|
||||
let mut sample_sizes = TryVec::new();
|
||||
if sample_size == 0 {
|
||||
sample_sizes.reserve(sample_count.to_usize())?;
|
||||
for _ in 0..sample_count {
|
||||
sample_sizes.push(be_u32(src)?)?;
|
||||
}
|
||||
|
@ -2256,10 +2271,11 @@ fn read_stsz<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleSizeBox> {
|
|||
}
|
||||
|
||||
/// Parse a stts box.
|
||||
/// See ISO 14496-12:2015 § 8.6.1.2
|
||||
fn read_stts<T: Read>(src: &mut BMFFBox<T>) -> Result<TimeToSampleBox> {
|
||||
let (_, _) = read_fullbox_extra(src)?;
|
||||
let sample_count = be_u32_with_limit(src)?;
|
||||
let mut samples = TryVec::new();
|
||||
let mut samples = TryVec::with_capacity(sample_count.to_usize())?;
|
||||
for _ in 0..sample_count {
|
||||
let sample_count = be_u32_with_limit(src)?;
|
||||
let sample_delta = be_u32(src)?;
|
||||
|
@ -2421,7 +2437,7 @@ fn find_descriptor(data: &[u8], esds: &mut ES_Descriptor) -> Result<()> {
|
|||
let des = &mut Cursor::new(remains);
|
||||
let tag = des.read_u8()?;
|
||||
|
||||
// See ISO 14496-1:2010 § 8.3.3 for interpreting size of exandable classes
|
||||
// See ISO 14496-1:2010 § 8.3.3 for interpreting size of expandable classes
|
||||
|
||||
let mut end: u32 = 0; // It's u8 without declaration type that is incorrect.
|
||||
// MSB of extend_or_len indicates more bytes, up to 4 bytes.
|
||||
|
@ -2627,7 +2643,11 @@ fn read_ds_descriptor(data: &[u8], esds: &mut ES_Descriptor) -> Result<()> {
|
|||
esds.extended_audio_object_type = extended_audio_object_type;
|
||||
esds.audio_sample_rate = Some(sample_frequency_value);
|
||||
esds.audio_channel_count = Some(channel_counts);
|
||||
assert!(esds.decoder_specific_data.is_empty());
|
||||
if !esds.decoder_specific_data.is_empty() {
|
||||
return Err(Error::InvalidData(
|
||||
"There can be only one DecSpecificInfoTag descriptor",
|
||||
));
|
||||
}
|
||||
esds.decoder_specific_data.extend_from_slice(data)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -2695,6 +2715,7 @@ fn read_es_descriptor(data: &[u8], esds: &mut ES_Descriptor) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// See ISO 14496-14:2010 § 6.7.2
|
||||
fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
|
||||
let (_, _) = read_fullbox_extra(src)?;
|
||||
|
||||
|
@ -2709,6 +2730,7 @@ fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
|
|||
}
|
||||
|
||||
/// Parse `FLACSpecificBox`.
|
||||
/// See https://github.com/xiph/flac/blob/master/doc/isoflac.txt § 3.3.2
|
||||
fn read_dfla<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACSpecificBox> {
|
||||
let (version, flags) = read_fullbox_extra(src)?;
|
||||
if version != 0 {
|
||||
|
@ -3135,6 +3157,7 @@ fn read_audio_sample_entry<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleEntry>
|
|||
|
||||
/// Parse a stsd box.
|
||||
/// See ISO 14496-12:2015 § 8.5.2
|
||||
/// See ISO 14496-14:2010 § 6.7.2
|
||||
fn read_stsd<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleDescriptionBox> {
|
||||
let (_, _) = read_fullbox_extra(src)?;
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"13408d7785c5fe40f9db2bac1f93e8cf2aca7c35b5d2ba9acbbb23eb3a71e40a","cbindgen.toml":"5c9429f271d6e914d81b63e6509c04ffe84cab11ed3a53a2ed4715e5d5ace80e","examples/dump.rs":"598f828f07bac9b204d3eb7af4efd7158087382fc322dcce913a28729f854f70","src/lib.rs":"dfd3bccfb80aaab2389d11ae00242709d6c5dae8a299b55098bf5ec39698a097","tests/test_chunk_out_of_range.rs":"b5da583218d98027ed973a29c67434a91a1306f2d2fb39ec4d640d4824c308ce","tests/test_encryption.rs":"ca98516ff423c03b5fcc17b05f993f13b32485e4cf3ba86faf1bea72681d75ce","tests/test_fragment.rs":"e90eb5a4418d30002655466c0c4b3125c7fd70a74b6871471eaa172f1def9db8","tests/test_rotation.rs":"fb43c2f2dfa496d151c33bdd46c0fd3252387c23cc71e2cac9ed0234de715a81","tests/test_sample_table.rs":"adc8d264c46aef78c047377fbb792a4400d6db98bc44583b464d7b3dc182c884","tests/test_workaround_stsc.rs":"7dd419f3d55b9a3a039cac57e58a9240a9c8166bcd4356c24f69f731c3ced83b"},"package":null}
|
||||
{"files":{"Cargo.toml":"12f3f7561fa1d5facc70b63b68288997ca6fde3dd5d6fabcfe2e7d8ec5940ab2","cbindgen.toml":"5c9429f271d6e914d81b63e6509c04ffe84cab11ed3a53a2ed4715e5d5ace80e","examples/dump.rs":"83462422315c22e496960bae922edb23105c0aa272d2b106edd7574ff068513a","src/lib.rs":"e90c6bdfcf321115b19f0148b5f273c9516bc7dfdae16c7c01a76ba8be51dad3","tests/test_chunk_out_of_range.rs":"b5da583218d98027ed973a29c67434a91a1306f2d2fb39ec4d640d4824c308ce","tests/test_encryption.rs":"ca98516ff423c03b5fcc17b05f993f13b32485e4cf3ba86faf1bea72681d75ce","tests/test_fragment.rs":"e90eb5a4418d30002655466c0c4b3125c7fd70a74b6871471eaa172f1def9db8","tests/test_rotation.rs":"fb43c2f2dfa496d151c33bdd46c0fd3252387c23cc71e2cac9ed0234de715a81","tests/test_sample_table.rs":"185755909b2f4e0ea99604bb423a07623d614a180accdaebd1c98aef2c2e3ae6","tests/test_workaround_stsc.rs":"7dd419f3d55b9a3a039cac57e58a9240a9c8166bcd4356c24f69f731c3ced83b"},"package":null}
|
|
@ -26,7 +26,7 @@ travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" }
|
|||
byteorder = "1.2.1"
|
||||
log = "0.4"
|
||||
mp4parse = {version = "0.11.2", path = "../mp4parse"}
|
||||
num-traits = "0.2.0"
|
||||
num-traits = "=0.2.10"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.7.1"
|
||||
|
|
|
@ -62,9 +62,7 @@ fn dump_file(filename: &str) {
|
|||
for i in 0..counts {
|
||||
let mut track_info = Mp4parseTrackInfo {
|
||||
track_type: Mp4parseTrackType::Audio,
|
||||
track_id: 0,
|
||||
duration: 0,
|
||||
media_time: 0,
|
||||
..Default::default()
|
||||
};
|
||||
match mp4parse_get_track_info(parser, i, &mut track_info) {
|
||||
Mp4parseStatus::Ok => {
|
||||
|
|
|
@ -40,8 +40,14 @@ extern crate mp4parse;
|
|||
extern crate num_traits;
|
||||
|
||||
use byteorder::WriteBytesExt;
|
||||
use num_traits::{CheckedAdd, CheckedSub};
|
||||
use num_traits::{PrimInt, Zero};
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use std::io::Read;
|
||||
use std::ops::Neg;
|
||||
use std::ops::{Add, Sub};
|
||||
|
||||
// Symbols we need from our rust api.
|
||||
use mp4parse::read_avif;
|
||||
|
@ -55,6 +61,7 @@ use mp4parse::MediaContext;
|
|||
use mp4parse::MediaScaledTime;
|
||||
use mp4parse::MediaTimeScale;
|
||||
use mp4parse::SampleEntry;
|
||||
use mp4parse::ToUsize;
|
||||
use mp4parse::Track;
|
||||
use mp4parse::TrackScaledTime;
|
||||
use mp4parse::TrackTimeScale;
|
||||
|
@ -144,36 +151,134 @@ impl Default for Mp4ParseEncryptionSchemeType {
|
|||
}
|
||||
}
|
||||
|
||||
/// A zero-overhead wrapper around integer types for the sake of always
|
||||
/// requiring checked arithmetic
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct CheckedInteger<T>(pub T);
|
||||
|
||||
impl<T> From<T> for CheckedInteger<T> {
|
||||
fn from(i: T) -> Self {
|
||||
Self(i)
|
||||
}
|
||||
}
|
||||
|
||||
// Orphan rules prevent a more general implementation, but this suffices
|
||||
impl From<CheckedInteger<i64>> for i64 {
|
||||
fn from(checked: CheckedInteger<i64>) -> i64 {
|
||||
checked.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: Into<T>> Add<U> for CheckedInteger<T>
|
||||
where
|
||||
T: CheckedAdd,
|
||||
{
|
||||
type Output = Option<Self>;
|
||||
|
||||
fn add(self, other: U) -> Self::Output {
|
||||
self.0.checked_add(&other.into()).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: Into<T>> Sub<U> for CheckedInteger<T>
|
||||
where
|
||||
T: CheckedSub,
|
||||
{
|
||||
type Output = Option<Self>;
|
||||
|
||||
fn sub(self, other: U) -> Self::Output {
|
||||
self.0.checked_sub(&other.into()).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement subtraction of checked `u64`s returning i64
|
||||
// This is necessary for handling Mp4parseTrackInfo::media_time gracefully
|
||||
impl Sub for CheckedInteger<u64> {
|
||||
type Output = Option<CheckedInteger<i64>>;
|
||||
|
||||
fn sub(self, other: Self) -> Self::Output {
|
||||
if self >= other {
|
||||
self.0
|
||||
.checked_sub(other.0)
|
||||
.and_then(|u| i64::try_from(u).ok())
|
||||
.map(CheckedInteger)
|
||||
} else {
|
||||
other
|
||||
.0
|
||||
.checked_sub(self.0)
|
||||
.and_then(|u| i64::try_from(u).ok())
|
||||
.map(i64::neg)
|
||||
.map(CheckedInteger)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u64_subtraction_returning_i64() {
|
||||
// self > other
|
||||
assert_eq!(
|
||||
CheckedInteger(2u64) - CheckedInteger(1u64),
|
||||
Some(CheckedInteger(1i64))
|
||||
);
|
||||
|
||||
// self == other
|
||||
assert_eq!(
|
||||
CheckedInteger(1u64) - CheckedInteger(1u64),
|
||||
Some(CheckedInteger(0i64))
|
||||
);
|
||||
|
||||
// difference too large to store in i64
|
||||
assert_eq!(CheckedInteger(u64::MAX) - CheckedInteger(1u64), None);
|
||||
|
||||
// self < other
|
||||
assert_eq!(
|
||||
CheckedInteger(1u64) - CheckedInteger(2u64),
|
||||
Some(CheckedInteger(-1i64))
|
||||
);
|
||||
|
||||
// difference not representable due to overflow
|
||||
assert_eq!(CheckedInteger(1u64) - CheckedInteger(u64::MAX), None);
|
||||
}
|
||||
|
||||
impl<T: std::cmp::PartialEq> PartialEq<T> for CheckedInteger<T> {
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.0 == *other
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Mp4parseTrackInfo {
|
||||
pub track_type: Mp4parseTrackType,
|
||||
pub track_id: u32,
|
||||
pub duration: u64,
|
||||
pub media_time: i64, // wants to be u64? understand how elst adjustment works
|
||||
pub media_time: CheckedInteger<i64>, // wants to be u64? understand how elst adjustment works
|
||||
// TODO(kinetik): include crypto guff
|
||||
// If this changes to u64, we can get rid of the strange
|
||||
// impl Sub for CheckedInteger<u64>
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, PartialEq)]
|
||||
pub struct Mp4parseIndice {
|
||||
/// The byte offset in the file where the indexed sample begins.
|
||||
pub start_offset: u64,
|
||||
pub start_offset: CheckedInteger<u64>,
|
||||
/// The byte offset in the file where the indexed sample ends. This is
|
||||
/// equivalent to `start_offset` + the length in bytes of the indexed
|
||||
/// sample. Typically this will be the `start_offset` of the next sample
|
||||
/// in the file.
|
||||
pub end_offset: u64,
|
||||
pub end_offset: CheckedInteger<u64>,
|
||||
/// The time in microseconds when the indexed sample should be displayed.
|
||||
/// Analogous to the concept of presentation time stamp (pts).
|
||||
pub start_composition: i64,
|
||||
pub start_composition: CheckedInteger<i64>,
|
||||
/// The time in microseconds when the indexed sample should stop being
|
||||
/// displayed. Typically this would be the `start_composition` time of the
|
||||
/// next sample if samples were ordered by composition time.
|
||||
pub end_composition: i64,
|
||||
pub end_composition: CheckedInteger<i64>,
|
||||
/// The time in microseconds that the indexed sample should be decoded at.
|
||||
/// Analogous to the concept of decode time stamp (dts).
|
||||
pub start_decode: i64,
|
||||
pub start_decode: CheckedInteger<i64>,
|
||||
/// Set if the indexed sample is a sync sample. The meaning of sync is
|
||||
/// somewhat codec specific, but essentially amounts to if the sample is a
|
||||
/// key frame.
|
||||
|
@ -660,19 +765,23 @@ pub unsafe extern "C" fn mp4parse_get_track_info(
|
|||
let track = &context.tracks[track_index];
|
||||
|
||||
if let (Some(track_timescale), Some(context_timescale)) = (track.timescale, context.timescale) {
|
||||
let media_time = match track.media_time.map_or(Some(0), |media_time| {
|
||||
let media_time: CheckedInteger<_> = match track.media_time.map_or(Some(0), |media_time| {
|
||||
track_time_to_us(media_time, track_timescale)
|
||||
}) {
|
||||
Some(time) => time as i64,
|
||||
Some(time) => time.into(),
|
||||
None => return Mp4parseStatus::Invalid,
|
||||
};
|
||||
let empty_duration = match track.empty_duration.map_or(Some(0), |empty_duration| {
|
||||
let empty_duration: CheckedInteger<_> =
|
||||
match track.empty_duration.map_or(Some(0), |empty_duration| {
|
||||
media_time_to_us(empty_duration, context_timescale)
|
||||
}) {
|
||||
Some(time) => time as i64,
|
||||
Some(time) => time.into(),
|
||||
None => return Mp4parseStatus::Invalid,
|
||||
};
|
||||
info.media_time = match media_time - empty_duration {
|
||||
Some(difference) => difference,
|
||||
None => return Mp4parseStatus::Invalid,
|
||||
};
|
||||
info.media_time = media_time - empty_duration;
|
||||
|
||||
if let Some(track_duration) = track.duration {
|
||||
match track_time_to_us(track_duration, track_timescale) {
|
||||
|
@ -1135,12 +1244,17 @@ fn get_indice_table(
|
|||
}
|
||||
|
||||
let media_time = match (&track.media_time, &track.timescale) {
|
||||
(&Some(t), &Some(s)) => track_time_to_us(t, s).map(|v| v as i64),
|
||||
(&Some(t), &Some(s)) => track_time_to_us(t, s)
|
||||
.and_then(|v| i64::try_from(v).ok())
|
||||
.map(Into::into),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let empty_duration = match (&track.empty_duration, &context.timescale) {
|
||||
(&Some(e), &Some(s)) => media_time_to_us(e, s).map(|v| v as i64),
|
||||
let empty_duration: Option<CheckedInteger<_>> =
|
||||
match (&track.empty_duration, &context.timescale) {
|
||||
(&Some(e), &Some(s)) => media_time_to_us(e, s)
|
||||
.and_then(|v| i64::try_from(v).ok())
|
||||
.map(Into::into),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
@ -1148,10 +1262,10 @@ fn get_indice_table(
|
|||
// 'media_time' maps start time onward, 'empty_duration' adds time offset
|
||||
// before first frame is displayed.
|
||||
let offset_time = match (empty_duration, media_time) {
|
||||
(Some(e), Some(m)) => e - m,
|
||||
(Some(e), Some(m)) => (e - m).ok_or(Err(Mp4parseStatus::Invalid))?,
|
||||
(Some(e), None) => e,
|
||||
(None, Some(m)) => m,
|
||||
_ => 0,
|
||||
_ => 0.into(),
|
||||
};
|
||||
|
||||
if let Some(v) = create_sample_table(track, offset_time) {
|
||||
|
@ -1178,6 +1292,7 @@ struct TimeOffsetIterator<'a> {
|
|||
impl<'a> Iterator for TimeOffsetIterator<'a> {
|
||||
type Item = i64;
|
||||
|
||||
#[allow(clippy::reversed_empty_ranges)]
|
||||
fn next(&mut self) -> Option<i64> {
|
||||
let has_sample = self.cur_sample_range.next().or_else(|| {
|
||||
// At end of current TimeOffset, find the next TimeOffset.
|
||||
|
@ -1234,6 +1349,7 @@ struct TimeToSampleIterator<'a> {
|
|||
impl<'a> Iterator for TimeToSampleIterator<'a> {
|
||||
type Item = u32;
|
||||
|
||||
#[allow(clippy::reversed_empty_ranges)]
|
||||
fn next(&mut self) -> Option<u32> {
|
||||
let has_sample = self.cur_sample_count.next().or_else(|| {
|
||||
self.cur_sample_count = match self.stts_iter.next() {
|
||||
|
@ -1271,6 +1387,21 @@ impl<'a> TimeToSampleIterator<'a> {
|
|||
// For example:
|
||||
// (1, 5), (5, 10), (9, 2) => (1, 5), (2, 5), (3, 5), (4, 5), (5, 10), (6, 10),
|
||||
// (7, 10), (8, 10), (9, 2)
|
||||
fn sample_to_chunk_iter<'a>(
|
||||
stsc_samples: &'a TryVec<mp4parse::SampleToChunk>,
|
||||
stco_offsets: &'a TryVec<u64>,
|
||||
) -> SampleToChunkIterator<'a> {
|
||||
SampleToChunkIterator {
|
||||
chunks: (0..0),
|
||||
sample_count: 0,
|
||||
stsc_peek_iter: stsc_samples.as_slice().iter().peekable(),
|
||||
remain_chunk_count: stco_offsets
|
||||
.len()
|
||||
.try_into()
|
||||
.expect("stco.entry_count is u32"),
|
||||
}
|
||||
}
|
||||
|
||||
struct SampleToChunkIterator<'a> {
|
||||
chunks: std::ops::Range<u32>,
|
||||
sample_count: u32,
|
||||
|
@ -1285,7 +1416,12 @@ impl<'a> Iterator for SampleToChunkIterator<'a> {
|
|||
let has_chunk = self.chunks.next().or_else(|| {
|
||||
self.chunks = self.locate();
|
||||
self.remain_chunk_count
|
||||
.checked_sub(self.chunks.len() as u32)
|
||||
.checked_sub(
|
||||
self.chunks
|
||||
.len()
|
||||
.try_into()
|
||||
.expect("len() of a Range<u32> must fit in u32"),
|
||||
)
|
||||
.and_then(|res| {
|
||||
self.remain_chunk_count = res;
|
||||
self.chunks.next()
|
||||
|
@ -1297,6 +1433,7 @@ impl<'a> Iterator for SampleToChunkIterator<'a> {
|
|||
}
|
||||
|
||||
impl<'a> SampleToChunkIterator<'a> {
|
||||
#[allow(clippy::reversed_empty_ranges)]
|
||||
fn locate(&mut self) -> std::ops::Range<u32> {
|
||||
loop {
|
||||
return match (self.stsc_peek_iter.next(), self.stsc_peek_iter.peek()) {
|
||||
|
@ -1324,7 +1461,11 @@ impl<'a> SampleToChunkIterator<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<TryVec<Mp4parseIndice>> {
|
||||
#[allow(clippy::reversed_empty_ranges)]
|
||||
fn create_sample_table(
|
||||
track: &Track,
|
||||
track_offset_time: CheckedInteger<i64>,
|
||||
) -> Option<TryVec<Mp4parseIndice>> {
|
||||
let timescale = match track.timescale {
|
||||
Some(ref t) => TrackTimeScale::<i64>(t.0 as i64, t.1),
|
||||
_ => return None,
|
||||
|
@ -1341,31 +1482,32 @@ fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<TryVec<M
|
|||
_ => false,
|
||||
};
|
||||
|
||||
let mut sample_table = TryVec::new();
|
||||
let mut sample_size_iter = stsz.sample_sizes.iter();
|
||||
|
||||
// Get 'stsc' iterator for (chunk_id, chunk_sample_count) and calculate the sample
|
||||
// offset address.
|
||||
let stsc_iter = SampleToChunkIterator {
|
||||
chunks: (0..0),
|
||||
sample_count: 0,
|
||||
stsc_peek_iter: stsc.samples.as_slice().iter().peekable(),
|
||||
remain_chunk_count: stco.offsets.len() as u32,
|
||||
};
|
||||
|
||||
for i in stsc_iter {
|
||||
// With large numbers of samples, the cost of many allocations dominates,
|
||||
// so it's worth iterating twice to allocate sample_table just once.
|
||||
let total_sample_count = sample_to_chunk_iter(&stsc.samples, &stco.offsets)
|
||||
.by_ref()
|
||||
.map(|(_, sample_counts)| sample_counts.to_usize())
|
||||
.sum();
|
||||
let mut sample_table = TryVec::with_capacity(total_sample_count).ok()?;
|
||||
|
||||
for i in sample_to_chunk_iter(&stsc.samples, &stco.offsets) {
|
||||
let chunk_id = i.0 as usize;
|
||||
let sample_counts = i.1;
|
||||
let mut cur_position = match stco.offsets.get(chunk_id) {
|
||||
Some(&i) => i,
|
||||
Some(&i) => i.into(),
|
||||
_ => return None,
|
||||
};
|
||||
for _ in 0..sample_counts {
|
||||
let start_offset = cur_position;
|
||||
let end_offset = match (stsz.sample_size, sample_size_iter.next()) {
|
||||
(_, Some(t)) => start_offset + u64::from(*t),
|
||||
(t, _) if t > 0 => start_offset + u64::from(t),
|
||||
_ => 0,
|
||||
(_, Some(t)) => (start_offset + *t)?,
|
||||
(t, _) if t > 0 => (start_offset + t)?,
|
||||
_ => 0.into(),
|
||||
};
|
||||
if end_offset == 0 {
|
||||
return None;
|
||||
|
@ -1376,10 +1518,8 @@ fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<TryVec<M
|
|||
.push(Mp4parseIndice {
|
||||
start_offset,
|
||||
end_offset,
|
||||
start_composition: 0,
|
||||
end_composition: 0,
|
||||
start_decode: 0,
|
||||
sync: !has_sync_table,
|
||||
..Default::default()
|
||||
})
|
||||
.ok()?;
|
||||
}
|
||||
|
@ -1389,7 +1529,7 @@ fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<TryVec<M
|
|||
if let Some(ref v) = track.stss {
|
||||
for iter in &v.samples {
|
||||
match iter
|
||||
.checked_sub(1)
|
||||
.checked_sub(&1)
|
||||
.and_then(|idx| sample_table.get_mut(idx as usize))
|
||||
{
|
||||
Some(elem) => elem.sync = true,
|
||||
|
@ -1426,25 +1566,20 @@ fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<TryVec<M
|
|||
let mut sum_delta = TrackScaledTime::<i64>(0, track.id);
|
||||
for sample in sample_table.as_mut_slice() {
|
||||
let decode_time = sum_delta;
|
||||
sum_delta = sum_delta + stts_iter.next_delta();
|
||||
sum_delta = (sum_delta + stts_iter.next_delta())?;
|
||||
|
||||
// ctts_offset is the current sample offset time.
|
||||
let ctts_offset = ctts_offset_iter.next_offset_time();
|
||||
|
||||
let start_composition = track_time_to_us(decode_time + ctts_offset, timescale);
|
||||
let start_composition = track_time_to_us((decode_time + ctts_offset)?, timescale)?;
|
||||
|
||||
let end_composition = track_time_to_us(sum_delta + ctts_offset, timescale);
|
||||
let end_composition = track_time_to_us((sum_delta + ctts_offset)?, timescale)?;
|
||||
|
||||
let start_decode = track_time_to_us(decode_time, timescale);
|
||||
let start_decode = track_time_to_us(decode_time, timescale)?;
|
||||
|
||||
match (start_composition, end_composition, start_decode) {
|
||||
(Some(s_c), Some(e_c), Some(s_d)) => {
|
||||
sample.start_composition = s_c + track_offset_time;
|
||||
sample.end_composition = e_c + track_offset_time;
|
||||
sample.start_decode = s_d;
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
sample.start_composition = (track_offset_time + start_composition)?;
|
||||
sample.end_composition = (track_offset_time + end_composition)?;
|
||||
sample.start_decode = start_decode.into();
|
||||
}
|
||||
|
||||
// Correct composition end time due to 'ctts' causes composition time re-ordering.
|
||||
|
@ -1453,14 +1588,15 @@ fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<TryVec<M
|
|||
// calculate to correct the composition end time.
|
||||
if !sample_table.is_empty() {
|
||||
// Create an index table refers to sample_table and sorted by start_composisiton time.
|
||||
let mut sort_table = TryVec::new();
|
||||
let mut sort_table = TryVec::with_capacity(sample_table.len()).ok()?;
|
||||
|
||||
for i in 0..sample_table.len() {
|
||||
sort_table.push(i).ok()?;
|
||||
}
|
||||
|
||||
sort_table.sort_by_key(|i| match sample_table.get(*i) {
|
||||
Some(v) => v.start_composition,
|
||||
_ => 0,
|
||||
_ => 0.into(),
|
||||
});
|
||||
|
||||
for indices in sort_table.windows(2) {
|
||||
|
@ -1600,13 +1736,14 @@ fn get_pssh_info(
|
|||
|
||||
pssh_data.clear();
|
||||
for pssh in &context.psshs {
|
||||
let content_len = pssh.box_content.len();
|
||||
if content_len > std::u32::MAX as usize {
|
||||
return Err(Mp4parseStatus::Invalid);
|
||||
}
|
||||
let content_len = pssh
|
||||
.box_content
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_| Mp4parseStatus::Invalid)?;
|
||||
let mut data_len = TryVec::new();
|
||||
if data_len
|
||||
.write_u32::<byteorder::NativeEndian>(content_len as u32)
|
||||
.write_u32::<byteorder::NativeEndian>(content_len)
|
||||
.is_err()
|
||||
{
|
||||
return Err(Mp4parseStatus::Io);
|
||||
|
@ -1684,9 +1821,7 @@ fn arg_validation() {
|
|||
|
||||
let mut dummy_info = Mp4parseTrackInfo {
|
||||
track_type: Mp4parseTrackType::Video,
|
||||
track_id: 0,
|
||||
duration: 0,
|
||||
media_time: 0,
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
Mp4parseStatus::BadArg,
|
||||
|
@ -1754,9 +1889,7 @@ fn arg_validation_with_parser() {
|
|||
|
||||
let mut dummy_info = Mp4parseTrackInfo {
|
||||
track_type: Mp4parseTrackType::Video,
|
||||
track_id: 0,
|
||||
duration: 0,
|
||||
media_time: 0,
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
Mp4parseStatus::BadArg,
|
||||
|
@ -1828,9 +1961,7 @@ fn minimal_mp4_get_track_info() {
|
|||
|
||||
let mut info = Mp4parseTrackInfo {
|
||||
track_type: Mp4parseTrackType::Video,
|
||||
track_id: 0,
|
||||
duration: 0,
|
||||
media_time: 0,
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(Mp4parseStatus::Ok, unsafe {
|
||||
mp4parse_get_track_info(parser, 0, &mut info)
|
||||
|
@ -1902,9 +2033,7 @@ fn minimal_mp4_get_track_info_invalid_track_number() {
|
|||
|
||||
let mut info = Mp4parseTrackInfo {
|
||||
track_type: Mp4parseTrackType::Video,
|
||||
track_id: 0,
|
||||
duration: 0,
|
||||
media_time: 0,
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(Mp4parseStatus::BadArg, unsafe {
|
||||
mp4parse_get_track_info(parser, 3, &mut info)
|
||||
|
|
|
@ -48,19 +48,19 @@ fn parse_sample_table() {
|
|||
|
||||
// Compare the value from stagefright.
|
||||
let audio_indice_0 = Mp4parseIndice {
|
||||
start_offset: 27_046,
|
||||
end_offset: 27_052,
|
||||
start_composition: 0,
|
||||
end_composition: 46_439,
|
||||
start_decode: 0,
|
||||
start_offset: 27_046.into(),
|
||||
end_offset: 27_052.into(),
|
||||
start_composition: 0.into(),
|
||||
end_composition: 46_439.into(),
|
||||
start_decode: 0.into(),
|
||||
sync: true,
|
||||
};
|
||||
let audio_indice_215 = Mp4parseIndice {
|
||||
start_offset: 283_550,
|
||||
end_offset: 283_556,
|
||||
start_composition: 9_984_580,
|
||||
end_composition: 10_031_020,
|
||||
start_decode: 9_984_580,
|
||||
start_offset: 283_550.into(),
|
||||
end_offset: 283_556.into(),
|
||||
start_composition: 9_984_580.into(),
|
||||
end_composition: 10_031_020.into(),
|
||||
start_decode: 9_984_580.into(),
|
||||
sync: true,
|
||||
};
|
||||
assert_eq!(indice.length, 216);
|
||||
|
@ -83,19 +83,19 @@ fn parse_sample_table() {
|
|||
|
||||
// Compare the last few data from stagefright.
|
||||
let video_indice_291 = Mp4parseIndice {
|
||||
start_offset: 280_226,
|
||||
end_offset: 280_855,
|
||||
start_composition: 9_838_333,
|
||||
end_composition: 9_871_677,
|
||||
start_decode: 9_710_000,
|
||||
start_offset: 280_226.into(),
|
||||
end_offset: 280_855.into(),
|
||||
start_composition: 9_838_333.into(),
|
||||
end_composition: 9_871_677.into(),
|
||||
start_decode: 9_710_000.into(),
|
||||
sync: false,
|
||||
};
|
||||
let video_indice_292 = Mp4parseIndice {
|
||||
start_offset: 280_855,
|
||||
end_offset: 281_297,
|
||||
start_composition: 9_805_011,
|
||||
end_composition: 9_838_333,
|
||||
start_decode: 9_710_011,
|
||||
start_offset: 280_855.into(),
|
||||
end_offset: 281_297.into(),
|
||||
start_composition: 9_805_011.into(),
|
||||
end_composition: 9_838_333.into(),
|
||||
start_decode: 9_710_011.into(),
|
||||
sync: false,
|
||||
};
|
||||
// TODO: start_composition time in stagefright is 9905000, but it is 9904999 in parser, it
|
||||
|
@ -103,19 +103,19 @@ fn parse_sample_table() {
|
|||
//let video_indice_293 = Mp4parseIndice { start_offset: 281_297, end_offset: 281_919, start_composition: 9_905_000, end_composition: 9_938_344, start_decode: 9_776_666, sync: false };
|
||||
//let video_indice_294 = Mp4parseIndice { start_offset: 281_919, end_offset: 282_391, start_composition: 9_871_677, end_composition: 9_905_000, start_decode: 9_776_677, sync: false };
|
||||
let video_indice_295 = Mp4parseIndice {
|
||||
start_offset: 282_391,
|
||||
end_offset: 283_032,
|
||||
start_composition: 9_971_666,
|
||||
end_composition: 9_971_677,
|
||||
start_decode: 9_843_333,
|
||||
start_offset: 282_391.into(),
|
||||
end_offset: 283_032.into(),
|
||||
start_composition: 9_971_666.into(),
|
||||
end_composition: 9_971_677.into(),
|
||||
start_decode: 9_843_333.into(),
|
||||
sync: false,
|
||||
};
|
||||
let video_indice_296 = Mp4parseIndice {
|
||||
start_offset: 283_092,
|
||||
end_offset: 283_526,
|
||||
start_composition: 9_938_344,
|
||||
end_composition: 9_971_666,
|
||||
start_decode: 9_843_344,
|
||||
start_offset: 283_092.into(),
|
||||
end_offset: 283_526.into(),
|
||||
start_composition: 9_938_344.into(),
|
||||
end_composition: 9_971_666.into(),
|
||||
start_decode: 9_843_344.into(),
|
||||
sync: false,
|
||||
};
|
||||
|
||||
|
@ -169,27 +169,27 @@ fn parse_sample_table_with_elst() {
|
|||
// Due to 'elst', the start_composition and end_composition are negative
|
||||
// at first two samples.
|
||||
let audio_indice_0 = Mp4parseIndice {
|
||||
start_offset: 6992,
|
||||
end_offset: 7363,
|
||||
start_composition: -36281,
|
||||
end_composition: -13062,
|
||||
start_decode: 0,
|
||||
start_offset: 6992.into(),
|
||||
end_offset: 7363.into(),
|
||||
start_composition: (-36281).into(),
|
||||
end_composition: (-13062).into(),
|
||||
start_decode: 0.into(),
|
||||
sync: true,
|
||||
};
|
||||
let audio_indice_1 = Mp4parseIndice {
|
||||
start_offset: 7363,
|
||||
end_offset: 7735,
|
||||
start_composition: -13062,
|
||||
end_composition: 10158,
|
||||
start_decode: 23219,
|
||||
start_offset: 7363.into(),
|
||||
end_offset: 7735.into(),
|
||||
start_composition: (-13062).into(),
|
||||
end_composition: 10158.into(),
|
||||
start_decode: 23219.into(),
|
||||
sync: true,
|
||||
};
|
||||
let audio_indice_2 = Mp4parseIndice {
|
||||
start_offset: 7735,
|
||||
end_offset: 8106,
|
||||
start_composition: 10158,
|
||||
end_composition: 33378,
|
||||
start_decode: 46439,
|
||||
start_offset: 7735.into(),
|
||||
end_offset: 8106.into(),
|
||||
start_composition: 10158.into(),
|
||||
end_composition: 33378.into(),
|
||||
start_decode: 46439.into(),
|
||||
sync: true,
|
||||
};
|
||||
assert_eq!(indice.length, 21);
|
||||
|
@ -236,35 +236,35 @@ fn parse_sample_table_with_negative_ctts() {
|
|||
|
||||
// There are negative value in 'ctts' table.
|
||||
let video_indice_0 = Mp4parseIndice {
|
||||
start_offset: 48,
|
||||
end_offset: 890,
|
||||
start_composition: 0,
|
||||
end_composition: 33_333,
|
||||
start_decode: 0,
|
||||
start_offset: 48.into(),
|
||||
end_offset: 890.into(),
|
||||
start_composition: 0.into(),
|
||||
end_composition: 33_333.into(),
|
||||
start_decode: 0.into(),
|
||||
sync: true,
|
||||
};
|
||||
let video_indice_1 = Mp4parseIndice {
|
||||
start_offset: 890,
|
||||
end_offset: 913,
|
||||
start_composition: 133_333,
|
||||
end_composition: 166_666,
|
||||
start_decode: 33_333,
|
||||
start_offset: 890.into(),
|
||||
end_offset: 913.into(),
|
||||
start_composition: 133_333.into(),
|
||||
end_composition: 166_666.into(),
|
||||
start_decode: 33_333.into(),
|
||||
sync: false,
|
||||
};
|
||||
let video_indice_2 = Mp4parseIndice {
|
||||
start_offset: 913,
|
||||
end_offset: 934,
|
||||
start_composition: 66_666,
|
||||
end_composition: 100_000,
|
||||
start_decode: 66_666,
|
||||
start_offset: 913.into(),
|
||||
end_offset: 934.into(),
|
||||
start_composition: 66_666.into(),
|
||||
end_composition: 100_000.into(),
|
||||
start_decode: 66_666.into(),
|
||||
sync: false,
|
||||
};
|
||||
let video_indice_3 = Mp4parseIndice {
|
||||
start_offset: 934,
|
||||
end_offset: 955,
|
||||
start_composition: 33_333,
|
||||
end_composition: 66_666,
|
||||
start_decode: 100_000,
|
||||
start_offset: 934.into(),
|
||||
end_offset: 955.into(),
|
||||
start_composition: 33_333.into(),
|
||||
end_composition: 66_666.into(),
|
||||
start_decode: 100_000.into(),
|
||||
sync: false,
|
||||
};
|
||||
assert_eq!(indice.length, 300);
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"f491aec76f2252ca15a333dd3cfd18cfd91ebf841c7d607857dfa75c7080cb9a","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"7ad5fb625f7ef61595a2180f3b26715457552faa8bb08526c70b416da29b2533","RELEASES.md":"ebe114c148e4fc43b63c2523e13e9d903f07db139ab70f48320f9cb8c17ac9d8","benches/roots.rs":"df3554c0025d78235b5dca975b641f3bb10cae8d5ad3a79eb6cbd1a21475f133","bors.toml":"1c81ede536a37edd30fe4e622ff0531b25372403ac9475a5d6c50f14156565a2","build.rs":"16de2aa57e754fc1526d0400b5d87a3f771296705fca54601aa598b6f74ded8f","ci/rustup.sh":"2aa9e89e4af81ed9da86bdcf7cdabe512287c877248783b69eed1eccf09ad6bb","ci/test_full.sh":"fd4928a73c13905d939d009801bd448a7a9d2ca00a30260eedd1feb03fc88e11","src/lib.rs":"1e19a19aa0d414d7548e4bc9510f89bed122430564e044302edd4a21a1b83134","src/roots.rs":"51a994a5e0bf505911cf912954283f52e7aa582ce0cd1c483e6b2e4c09a47b9e","tests/roots.rs":"ef70f711cb1544311c343dbaf411ad2598432e82b6dfa3d166c1d99096991d9e"},"package":"e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"}
|
||||
{"files":{"Cargo.toml":"54f4df5d505f0f8581a9455d2ea52bc385f003f97116c3d13b0e8e0cc491903f","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"7ad5fb625f7ef61595a2180f3b26715457552faa8bb08526c70b416da29b2533","RELEASES.md":"ea8b30468b1d1c16ebe9e148a62df378cb9d3198bb4095c889fe521245690f9d","benches/gcd.rs":"9b5c0ae8ccd6c7fc8f8384fb351d10cfdd0be5fbea9365f9ea925d8915b015bf","benches/roots.rs":"79b4ab2d8fe7bbf43fe65314d2e1bc206165bc4cb34b3ceaa899f9ea7af31c09","build.rs":"56d4fbb7a55750e61d2074df2735a31995c1decbd988c0e722926235e0fed487","src/lib.rs":"937d6a77d6542b47aafb872df7181dcafc1ab596df0e5d78b2e6577ae9463dd0","src/roots.rs":"2a9b908bd3666b5cffc58c1b37d329e46ed02f71ad6d5deea1e8440c10660e1a","tests/roots.rs":"a0caa4142899ec8cb806a7a0d3410c39d50de97cceadc4c2ceca707be91b1ddd"},"package":"b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"}
|
|
@ -3,7 +3,7 @@
|
|||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
|
@ -12,9 +12,10 @@
|
|||
|
||||
[package]
|
||||
name = "num-integer"
|
||||
version = "0.1.39"
|
||||
version = "0.1.41"
|
||||
authors = ["The Rust Project Developers"]
|
||||
build = "build.rs"
|
||||
exclude = ["/ci/*", "/.travis.yml", "/bors.toml"]
|
||||
description = "Integer traits and functions"
|
||||
homepage = "https://github.com/rust-num/num-integer"
|
||||
documentation = "https://docs.rs/num-integer"
|
||||
|
@ -28,6 +29,8 @@ features = ["std"]
|
|||
[dependencies.num-traits]
|
||||
version = "0.2.4"
|
||||
default-features = false
|
||||
[build-dependencies.autocfg]
|
||||
version = "0.1.3"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
|
|
|
@ -1,4 +1,26 @@
|
|||
# Release 0.1.39
|
||||
# Release 0.1.41 (2019-05-21)
|
||||
|
||||
- [Fixed feature detection on `no_std` targets][25].
|
||||
|
||||
**Contributors**: @cuviper
|
||||
|
||||
[25]: https://github.com/rust-num/num-integer/pull/25
|
||||
|
||||
# Release 0.1.40 (2019-05-20)
|
||||
|
||||
- [Optimized primitive `gcd` by avoiding memory swaps][11].
|
||||
- [Fixed `lcm(0, 0)` to return `0`, rather than panicking][18].
|
||||
- [Added `Integer::div_ceil`, `next_multiple_of`, and `prev_multiple_of`][16].
|
||||
- [Added `Integer::gcd_lcm`, `extended_gcd`, and `extended_gcd_lcm`][19].
|
||||
|
||||
**Contributors**: @cuviper, @ignatenkobrain, @smarnach, @strake
|
||||
|
||||
[11]: https://github.com/rust-num/num-integer/pull/11
|
||||
[16]: https://github.com/rust-num/num-integer/pull/16
|
||||
[18]: https://github.com/rust-num/num-integer/pull/18
|
||||
[19]: https://github.com/rust-num/num-integer/pull/19
|
||||
|
||||
# Release 0.1.39 (2018-06-20)
|
||||
|
||||
- [The new `Roots` trait provides `sqrt`, `cbrt`, and `nth_root` methods][9],
|
||||
calculating an `Integer`'s principal roots rounded toward zero.
|
||||
|
@ -7,7 +29,7 @@
|
|||
|
||||
[9]: https://github.com/rust-num/num-integer/pull/9
|
||||
|
||||
# Release 0.1.38
|
||||
# Release 0.1.38 (2018-05-11)
|
||||
|
||||
- [Support for 128-bit integers is now automatically detected and enabled.][8]
|
||||
Setting the `i128` crate feature now causes the build script to panic if such
|
||||
|
@ -17,7 +39,7 @@
|
|||
|
||||
[8]: https://github.com/rust-num/num-integer/pull/8
|
||||
|
||||
# Release 0.1.37
|
||||
# Release 0.1.37 (2018-05-10)
|
||||
|
||||
- [`Integer` is now implemented for `i128` and `u128`][7] starting with Rust
|
||||
1.26, enabled by the new `i128` crate feature.
|
||||
|
@ -26,7 +48,7 @@
|
|||
|
||||
[7]: https://github.com/rust-num/num-integer/pull/7
|
||||
|
||||
# Release 0.1.36
|
||||
# Release 0.1.36 (2018-02-06)
|
||||
|
||||
- [num-integer now has its own source repository][num-356] at [rust-num/num-integer][home].
|
||||
- [Corrected the argument order documented in `Integer::is_multiple_of`][1]
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
//! Benchmark comparing the current GCD implemtation against an older one.
|
||||
|
||||
#![feature(test)]
|
||||
|
||||
extern crate num_integer;
|
||||
extern crate num_traits;
|
||||
extern crate test;
|
||||
|
||||
use num_integer::Integer;
|
||||
use num_traits::{AsPrimitive, Bounded, Signed};
|
||||
use test::{black_box, Bencher};
|
||||
|
||||
trait GcdOld: Integer {
|
||||
fn gcd_old(&self, other: &Self) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! impl_gcd_old_for_isize {
|
||||
($T:ty) => {
|
||||
impl GcdOld for $T {
|
||||
/// Calculates the Greatest Common Divisor (GCD) of the number and
|
||||
/// `other`. The result is always positive.
|
||||
#[inline]
|
||||
fn gcd_old(&self, other: &Self) -> Self {
|
||||
// Use Stein's algorithm
|
||||
let mut m = *self;
|
||||
let mut n = *other;
|
||||
if m == 0 || n == 0 {
|
||||
return (m | n).abs();
|
||||
}
|
||||
|
||||
// find common factors of 2
|
||||
let shift = (m | n).trailing_zeros();
|
||||
|
||||
// The algorithm needs positive numbers, but the minimum value
|
||||
// can't be represented as a positive one.
|
||||
// It's also a power of two, so the gcd can be
|
||||
// calculated by bitshifting in that case
|
||||
|
||||
// Assuming two's complement, the number created by the shift
|
||||
// is positive for all numbers except gcd = abs(min value)
|
||||
// The call to .abs() causes a panic in debug mode
|
||||
if m == Self::min_value() || n == Self::min_value() {
|
||||
return (1 << shift).abs();
|
||||
}
|
||||
|
||||
// guaranteed to be positive now, rest like unsigned algorithm
|
||||
m = m.abs();
|
||||
n = n.abs();
|
||||
|
||||
// divide n and m by 2 until odd
|
||||
// m inside loop
|
||||
n >>= n.trailing_zeros();
|
||||
|
||||
while m != 0 {
|
||||
m >>= m.trailing_zeros();
|
||||
if n > m {
|
||||
std::mem::swap(&mut n, &mut m)
|
||||
}
|
||||
m -= n;
|
||||
}
|
||||
|
||||
n << shift
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_gcd_old_for_isize!(i8);
|
||||
impl_gcd_old_for_isize!(i16);
|
||||
impl_gcd_old_for_isize!(i32);
|
||||
impl_gcd_old_for_isize!(i64);
|
||||
impl_gcd_old_for_isize!(isize);
|
||||
impl_gcd_old_for_isize!(i128);
|
||||
|
||||
macro_rules! impl_gcd_old_for_usize {
|
||||
($T:ty) => {
|
||||
impl GcdOld for $T {
|
||||
/// Calculates the Greatest Common Divisor (GCD) of the number and
|
||||
/// `other`. The result is always positive.
|
||||
#[inline]
|
||||
fn gcd_old(&self, other: &Self) -> Self {
|
||||
// Use Stein's algorithm
|
||||
let mut m = *self;
|
||||
let mut n = *other;
|
||||
if m == 0 || n == 0 {
|
||||
return m | n;
|
||||
}
|
||||
|
||||
// find common factors of 2
|
||||
let shift = (m | n).trailing_zeros();
|
||||
|
||||
// divide n and m by 2 until odd
|
||||
// m inside loop
|
||||
n >>= n.trailing_zeros();
|
||||
|
||||
while m != 0 {
|
||||
m >>= m.trailing_zeros();
|
||||
if n > m {
|
||||
std::mem::swap(&mut n, &mut m)
|
||||
}
|
||||
m -= n;
|
||||
}
|
||||
|
||||
n << shift
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_gcd_old_for_usize!(u8);
|
||||
impl_gcd_old_for_usize!(u16);
|
||||
impl_gcd_old_for_usize!(u32);
|
||||
impl_gcd_old_for_usize!(u64);
|
||||
impl_gcd_old_for_usize!(usize);
|
||||
impl_gcd_old_for_usize!(u128);
|
||||
|
||||
/// Return an iterator that yields all Fibonacci numbers fitting into a u128.
|
||||
fn fibonacci() -> impl Iterator<Item = u128> {
|
||||
(0..185).scan((0, 1), |&mut (ref mut a, ref mut b), _| {
|
||||
let tmp = *a;
|
||||
*a = *b;
|
||||
*b += tmp;
|
||||
Some(*b)
|
||||
})
|
||||
}
|
||||
|
||||
fn run_bench<T: Integer + Bounded + Copy + 'static>(b: &mut Bencher, gcd: fn(&T, &T) -> T)
|
||||
where
|
||||
T: AsPrimitive<u128>,
|
||||
u128: AsPrimitive<T>,
|
||||
{
|
||||
let max_value: u128 = T::max_value().as_();
|
||||
let pairs: Vec<(T, T)> = fibonacci()
|
||||
.collect::<Vec<_>>()
|
||||
.windows(2)
|
||||
.filter(|&pair| pair[0] <= max_value && pair[1] <= max_value)
|
||||
.map(|pair| (pair[0].as_(), pair[1].as_()))
|
||||
.collect();
|
||||
b.iter(|| {
|
||||
for &(ref m, ref n) in &pairs {
|
||||
black_box(gcd(m, n));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
macro_rules! bench_gcd {
|
||||
($T:ident) => {
|
||||
mod $T {
|
||||
use crate::{run_bench, GcdOld};
|
||||
use num_integer::Integer;
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_gcd(b: &mut Bencher) {
|
||||
run_bench(b, $T::gcd);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_gcd_old(b: &mut Bencher) {
|
||||
run_bench(b, $T::gcd_old);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bench_gcd!(u8);
|
||||
bench_gcd!(u16);
|
||||
bench_gcd!(u32);
|
||||
bench_gcd!(u64);
|
||||
bench_gcd!(u128);
|
||||
|
||||
bench_gcd!(i8);
|
||||
bench_gcd!(i16);
|
||||
bench_gcd!(i32);
|
||||
bench_gcd!(i64);
|
||||
bench_gcd!(i128);
|
|
@ -13,11 +13,7 @@ use test::{black_box, Bencher};
|
|||
|
||||
trait BenchInteger: Integer + PrimInt + WrappingAdd + WrappingMul + 'static {}
|
||||
|
||||
impl<T> BenchInteger for T
|
||||
where
|
||||
T: Integer + PrimInt + WrappingAdd + WrappingMul + 'static,
|
||||
{
|
||||
}
|
||||
impl<T> BenchInteger for T where T: Integer + PrimInt + WrappingAdd + WrappingMul + 'static {}
|
||||
|
||||
fn bench<T, F>(b: &mut Bencher, v: &[T], f: F, n: u32)
|
||||
where
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
status = [
|
||||
"continuous-integration/travis-ci/push",
|
||||
]
|
|
@ -1,35 +1,14 @@
|
|||
extern crate autocfg;
|
||||
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
fn main() {
|
||||
if probe("fn main() { 0i128; }") {
|
||||
let ac = autocfg::new();
|
||||
if ac.probe_type("i128") {
|
||||
println!("cargo:rustc-cfg=has_i128");
|
||||
} else if env::var_os("CARGO_FEATURE_I128").is_some() {
|
||||
panic!("i128 support was not detected!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Test if a code snippet can be compiled
|
||||
fn probe(code: &str) -> bool {
|
||||
let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
|
||||
let out_dir = env::var_os("OUT_DIR").expect("environment variable OUT_DIR");
|
||||
|
||||
let mut child = Command::new(rustc)
|
||||
.arg("--out-dir")
|
||||
.arg(out_dir)
|
||||
.arg("--emit=obj")
|
||||
.arg("-")
|
||||
.stdin(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("rustc probe");
|
||||
|
||||
child
|
||||
.stdin
|
||||
.as_mut()
|
||||
.expect("rustc stdin")
|
||||
.write_all(code.as_bytes())
|
||||
.expect("write rustc stdin");
|
||||
|
||||
child.wait().expect("rustc probe").success()
|
||||
|
||||
autocfg::rerun_path(file!());
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/sh
|
||||
# Use rustup to locally run the same suite of tests as .travis.yml.
|
||||
# (You should first install/update 1.8.0, stable, beta, and nightly.)
|
||||
|
||||
set -ex
|
||||
|
||||
export TRAVIS_RUST_VERSION
|
||||
for TRAVIS_RUST_VERSION in 1.8.0 1.15.0 1.20.0 stable beta nightly; do
|
||||
run="rustup run $TRAVIS_RUST_VERSION"
|
||||
$run cargo build --verbose
|
||||
$run $PWD/ci/test_full.sh
|
||||
done
|
|
@ -1,23 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
echo Testing num-integer on rustc ${TRAVIS_RUST_VERSION}
|
||||
|
||||
# num-integer should build and test everywhere.
|
||||
cargo build --verbose
|
||||
cargo test --verbose
|
||||
|
||||
# test `no_std`
|
||||
cargo build --verbose --no-default-features
|
||||
cargo test --verbose --no-default-features
|
||||
|
||||
# test `i128`
|
||||
if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable)$ ]]; then
|
||||
cargo build --verbose --features=i128
|
||||
cargo test --verbose --features=i128
|
||||
fi
|
||||
|
||||
if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then
|
||||
cargo test --verbose --all-features --benches
|
||||
fi
|
|
@ -15,21 +15,20 @@
|
|||
//! The `num-integer` crate is tested for rustc 1.8 and greater.
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/num-integer/0.1")]
|
||||
|
||||
#![no_std]
|
||||
#[cfg(feature = "std")]
|
||||
extern crate std;
|
||||
|
||||
extern crate num_traits as traits;
|
||||
|
||||
use core::ops::Add;
|
||||
use core::mem;
|
||||
use core::ops::Add;
|
||||
|
||||
use traits::{Num, Signed};
|
||||
use traits::{Num, Signed, Zero};
|
||||
|
||||
mod roots;
|
||||
pub use roots::Roots;
|
||||
pub use roots::{sqrt, cbrt, nth_root};
|
||||
pub use roots::{cbrt, nth_root, sqrt};
|
||||
|
||||
pub trait Integer: Sized + Num + PartialOrd + Ord + Eq {
|
||||
/// Floored integer division.
|
||||
|
@ -74,6 +73,31 @@ pub trait Integer: Sized + Num + PartialOrd + Ord + Eq {
|
|||
/// ~~~
|
||||
fn mod_floor(&self, other: &Self) -> Self;
|
||||
|
||||
/// Ceiled integer division.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ~~~
|
||||
/// # use num_integer::Integer;
|
||||
/// assert_eq!(( 8).div_ceil( &3), 3);
|
||||
/// assert_eq!(( 8).div_ceil(&-3), -2);
|
||||
/// assert_eq!((-8).div_ceil( &3), -2);
|
||||
/// assert_eq!((-8).div_ceil(&-3), 3);
|
||||
///
|
||||
/// assert_eq!(( 1).div_ceil( &2), 1);
|
||||
/// assert_eq!(( 1).div_ceil(&-2), 0);
|
||||
/// assert_eq!((-1).div_ceil( &2), 0);
|
||||
/// assert_eq!((-1).div_ceil(&-2), 1);
|
||||
/// ~~~
|
||||
fn div_ceil(&self, other: &Self) -> Self {
|
||||
let (q, r) = self.div_mod_floor(other);
|
||||
if r.is_zero() {
|
||||
q
|
||||
} else {
|
||||
q + Self::one()
|
||||
}
|
||||
}
|
||||
|
||||
/// Greatest Common Divisor (GCD).
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -93,9 +117,93 @@ pub trait Integer: Sized + Num + PartialOrd + Ord + Eq {
|
|||
/// # use num_integer::Integer;
|
||||
/// assert_eq!(7.lcm(&3), 21);
|
||||
/// assert_eq!(2.lcm(&4), 4);
|
||||
/// assert_eq!(0.lcm(&0), 0);
|
||||
/// ~~~
|
||||
fn lcm(&self, other: &Self) -> Self;
|
||||
|
||||
/// Greatest Common Divisor (GCD) and
|
||||
/// Lowest Common Multiple (LCM) together.
|
||||
///
|
||||
/// Potentially more efficient than calling `gcd` and `lcm`
|
||||
/// individually for identical inputs.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ~~~
|
||||
/// # use num_integer::Integer;
|
||||
/// assert_eq!(10.gcd_lcm(&4), (2, 20));
|
||||
/// assert_eq!(8.gcd_lcm(&9), (1, 72));
|
||||
/// ~~~
|
||||
#[inline]
|
||||
fn gcd_lcm(&self, other: &Self) -> (Self, Self) {
|
||||
(self.gcd(other), self.lcm(other))
|
||||
}
|
||||
|
||||
/// Greatest common divisor and Bézout coefficients.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ~~~
|
||||
/// # extern crate num_integer;
|
||||
/// # extern crate num_traits;
|
||||
/// # fn main() {
|
||||
/// # use num_integer::{ExtendedGcd, Integer};
|
||||
/// # use num_traits::NumAssign;
|
||||
/// fn check<A: Copy + Integer + NumAssign>(a: A, b: A) -> bool {
|
||||
/// let ExtendedGcd { gcd, x, y, .. } = a.extended_gcd(&b);
|
||||
/// gcd == x * a + y * b
|
||||
/// }
|
||||
/// assert!(check(10isize, 4isize));
|
||||
/// assert!(check(8isize, 9isize));
|
||||
/// # }
|
||||
/// ~~~
|
||||
#[inline]
|
||||
fn extended_gcd(&self, other: &Self) -> ExtendedGcd<Self>
|
||||
where
|
||||
Self: Clone,
|
||||
{
|
||||
let mut s = (Self::zero(), Self::one());
|
||||
let mut t = (Self::one(), Self::zero());
|
||||
let mut r = (other.clone(), self.clone());
|
||||
|
||||
while !r.0.is_zero() {
|
||||
let q = r.1.clone() / r.0.clone();
|
||||
let f = |mut r: (Self, Self)| {
|
||||
mem::swap(&mut r.0, &mut r.1);
|
||||
r.0 = r.0 - q.clone() * r.1.clone();
|
||||
r
|
||||
};
|
||||
r = f(r);
|
||||
s = f(s);
|
||||
t = f(t);
|
||||
}
|
||||
|
||||
if r.1 >= Self::zero() {
|
||||
ExtendedGcd {
|
||||
gcd: r.1,
|
||||
x: s.1,
|
||||
y: t.1,
|
||||
_hidden: (),
|
||||
}
|
||||
} else {
|
||||
ExtendedGcd {
|
||||
gcd: Self::zero() - r.1,
|
||||
x: Self::zero() - s.1,
|
||||
y: Self::zero() - t.1,
|
||||
_hidden: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Greatest common divisor, least common multiple, and Bézout coefficients.
|
||||
#[inline]
|
||||
fn extended_gcd_lcm(&self, other: &Self) -> (ExtendedGcd<Self>, Self)
|
||||
where
|
||||
Self: Clone + Signed,
|
||||
{
|
||||
(self.extended_gcd(other), self.lcm(other))
|
||||
}
|
||||
|
||||
/// Deprecated, use `is_multiple_of` instead.
|
||||
fn divides(&self, other: &Self) -> bool;
|
||||
|
||||
|
@ -172,6 +280,80 @@ pub trait Integer: Sized + Num + PartialOrd + Ord + Eq {
|
|||
fn div_mod_floor(&self, other: &Self) -> (Self, Self) {
|
||||
(self.div_floor(other), self.mod_floor(other))
|
||||
}
|
||||
|
||||
/// Rounds up to nearest multiple of argument.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// For signed types, `a.next_multiple_of(b) = a.prev_multiple_of(b.neg())`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ~~~
|
||||
/// # use num_integer::Integer;
|
||||
/// assert_eq!(( 16).next_multiple_of(& 8), 16);
|
||||
/// assert_eq!(( 23).next_multiple_of(& 8), 24);
|
||||
/// assert_eq!(( 16).next_multiple_of(&-8), 16);
|
||||
/// assert_eq!(( 23).next_multiple_of(&-8), 16);
|
||||
/// assert_eq!((-16).next_multiple_of(& 8), -16);
|
||||
/// assert_eq!((-23).next_multiple_of(& 8), -16);
|
||||
/// assert_eq!((-16).next_multiple_of(&-8), -16);
|
||||
/// assert_eq!((-23).next_multiple_of(&-8), -24);
|
||||
/// ~~~
|
||||
#[inline]
|
||||
fn next_multiple_of(&self, other: &Self) -> Self
|
||||
where
|
||||
Self: Clone,
|
||||
{
|
||||
let m = self.mod_floor(other);
|
||||
self.clone()
|
||||
+ if m.is_zero() {
|
||||
Self::zero()
|
||||
} else {
|
||||
other.clone() - m
|
||||
}
|
||||
}
|
||||
|
||||
/// Rounds down to nearest multiple of argument.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// For signed types, `a.prev_multiple_of(b) = a.next_multiple_of(b.neg())`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ~~~
|
||||
/// # use num_integer::Integer;
|
||||
/// assert_eq!(( 16).prev_multiple_of(& 8), 16);
|
||||
/// assert_eq!(( 23).prev_multiple_of(& 8), 16);
|
||||
/// assert_eq!(( 16).prev_multiple_of(&-8), 16);
|
||||
/// assert_eq!(( 23).prev_multiple_of(&-8), 24);
|
||||
/// assert_eq!((-16).prev_multiple_of(& 8), -16);
|
||||
/// assert_eq!((-23).prev_multiple_of(& 8), -24);
|
||||
/// assert_eq!((-16).prev_multiple_of(&-8), -16);
|
||||
/// assert_eq!((-23).prev_multiple_of(&-8), -16);
|
||||
/// ~~~
|
||||
#[inline]
|
||||
fn prev_multiple_of(&self, other: &Self) -> Self
|
||||
where
|
||||
Self: Clone,
|
||||
{
|
||||
self.clone() - self.mod_floor(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// Greatest common divisor and Bézout coefficients
|
||||
///
|
||||
/// ```no_build
|
||||
/// let e = isize::extended_gcd(a, b);
|
||||
/// assert_eq!(e.gcd, e.x*a + e.y*b);
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ExtendedGcd<A> {
|
||||
pub gcd: A,
|
||||
pub x: A,
|
||||
pub y: A,
|
||||
_hidden: (),
|
||||
}
|
||||
|
||||
/// Simultaneous integer division and modulus
|
||||
|
@ -194,6 +376,11 @@ pub fn mod_floor<T: Integer>(x: T, y: T) -> T {
|
|||
pub fn div_mod_floor<T: Integer>(x: T, y: T) -> (T, T) {
|
||||
x.div_mod_floor(&y)
|
||||
}
|
||||
/// Ceiled integer division
|
||||
#[inline]
|
||||
pub fn div_ceil<T: Integer>(x: T, y: T) -> T {
|
||||
x.div_ceil(&y)
|
||||
}
|
||||
|
||||
/// Calculates the Greatest Common Divisor (GCD) of the number and `other`. The
|
||||
/// result is always positive.
|
||||
|
@ -207,18 +394,26 @@ pub fn lcm<T: Integer>(x: T, y: T) -> T {
|
|||
x.lcm(&y)
|
||||
}
|
||||
|
||||
/// Calculates the Greatest Common Divisor (GCD) and
|
||||
/// Lowest Common Multiple (LCM) of the number and `other`.
|
||||
#[inline(always)]
|
||||
pub fn gcd_lcm<T: Integer>(x: T, y: T) -> (T, T) {
|
||||
x.gcd_lcm(&y)
|
||||
}
|
||||
|
||||
macro_rules! impl_integer_for_isize {
|
||||
($T:ty, $test_mod:ident) => (
|
||||
($T:ty, $test_mod:ident) => {
|
||||
impl Integer for $T {
|
||||
/// Floored integer division
|
||||
#[inline]
|
||||
fn div_floor(&self, other: &Self) -> Self {
|
||||
// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_,
|
||||
// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf)
|
||||
match self.div_rem(other) {
|
||||
(d, r) if (r > 0 && *other < 0)
|
||||
|| (r < 0 && *other > 0) => d - 1,
|
||||
(d, _) => d,
|
||||
let (d, r) = self.div_rem(other);
|
||||
if (r > 0 && *other < 0) || (r < 0 && *other > 0) {
|
||||
d - 1
|
||||
} else {
|
||||
d
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,10 +422,11 @@ macro_rules! impl_integer_for_isize {
|
|||
fn mod_floor(&self, other: &Self) -> Self {
|
||||
// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_,
|
||||
// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf)
|
||||
match *self % *other {
|
||||
r if (r > 0 && *other < 0)
|
||||
|| (r < 0 && *other > 0) => r + *other,
|
||||
r => r,
|
||||
let r = *self % *other;
|
||||
if (r > 0 && *other < 0) || (r < 0 && *other > 0) {
|
||||
r + *other
|
||||
} else {
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,10 +435,21 @@ macro_rules! impl_integer_for_isize {
|
|||
fn div_mod_floor(&self, other: &Self) -> (Self, Self) {
|
||||
// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_,
|
||||
// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf)
|
||||
match self.div_rem(other) {
|
||||
(d, r) if (r > 0 && *other < 0)
|
||||
|| (r < 0 && *other > 0) => (d - 1, r + *other),
|
||||
(d, r) => (d, r),
|
||||
let (d, r) = self.div_rem(other);
|
||||
if (r > 0 && *other < 0) || (r < 0 && *other > 0) {
|
||||
(d - 1, r + *other)
|
||||
} else {
|
||||
(d, r)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn div_ceil(&self, other: &Self) -> Self {
|
||||
let (d, r) = self.div_rem(other);
|
||||
if (r > 0 && *other > 0) || (r < 0 && *other < 0) {
|
||||
d + 1
|
||||
} else {
|
||||
d
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,7 +460,9 @@ macro_rules! impl_integer_for_isize {
|
|||
// Use Stein's algorithm
|
||||
let mut m = *self;
|
||||
let mut n = *other;
|
||||
if m == 0 || n == 0 { return (m | n).abs() }
|
||||
if m == 0 || n == 0 {
|
||||
return (m | n).abs();
|
||||
}
|
||||
|
||||
// find common factors of 2
|
||||
let shift = (m | n).trailing_zeros();
|
||||
|
@ -267,7 +476,7 @@ macro_rules! impl_integer_for_isize {
|
|||
// is positive for all numbers except gcd = abs(min value)
|
||||
// The call to .abs() causes a panic in debug mode
|
||||
if m == Self::min_value() || n == Self::min_value() {
|
||||
return (1 << shift).abs()
|
||||
return (1 << shift).abs();
|
||||
}
|
||||
|
||||
// guaranteed to be positive now, rest like unsigned algorithm
|
||||
|
@ -275,24 +484,51 @@ macro_rules! impl_integer_for_isize {
|
|||
n = n.abs();
|
||||
|
||||
// divide n and m by 2 until odd
|
||||
// m inside loop
|
||||
m >>= m.trailing_zeros();
|
||||
n >>= n.trailing_zeros();
|
||||
|
||||
while m != 0 {
|
||||
m >>= m.trailing_zeros();
|
||||
if n > m { mem::swap(&mut n, &mut m) }
|
||||
while m != n {
|
||||
if m > n {
|
||||
m -= n;
|
||||
m >>= m.trailing_zeros();
|
||||
} else {
|
||||
n -= m;
|
||||
n >>= n.trailing_zeros();
|
||||
}
|
||||
}
|
||||
m << shift
|
||||
}
|
||||
|
||||
n << shift
|
||||
#[inline]
|
||||
fn extended_gcd_lcm(&self, other: &Self) -> (ExtendedGcd<Self>, Self) {
|
||||
let egcd = self.extended_gcd(other);
|
||||
// should not have to recalculate abs
|
||||
let lcm = if egcd.gcd.is_zero() {
|
||||
Self::zero()
|
||||
} else {
|
||||
(*self * (*other / egcd.gcd)).abs()
|
||||
};
|
||||
(egcd, lcm)
|
||||
}
|
||||
|
||||
/// Calculates the Lowest Common Multiple (LCM) of the number and
|
||||
/// `other`.
|
||||
#[inline]
|
||||
fn lcm(&self, other: &Self) -> Self {
|
||||
self.gcd_lcm(other).1
|
||||
}
|
||||
|
||||
/// Calculates the Greatest Common Divisor (GCD) and
|
||||
/// Lowest Common Multiple (LCM) of the number and `other`.
|
||||
#[inline]
|
||||
fn gcd_lcm(&self, other: &Self) -> (Self, Self) {
|
||||
if self.is_zero() && other.is_zero() {
|
||||
return (Self::zero(), Self::zero());
|
||||
}
|
||||
let gcd = self.gcd(other);
|
||||
// should not have to recalculate abs
|
||||
(*self * (*other / self.gcd(other))).abs()
|
||||
let lcm = (*self * (*other / gcd)).abs();
|
||||
(gcd, lcm)
|
||||
}
|
||||
|
||||
/// Deprecated, use `is_multiple_of` instead.
|
||||
|
@ -309,11 +545,15 @@ macro_rules! impl_integer_for_isize {
|
|||
|
||||
/// Returns `true` if the number is divisible by `2`
|
||||
#[inline]
|
||||
fn is_even(&self) -> bool { (*self) & 1 == 0 }
|
||||
fn is_even(&self) -> bool {
|
||||
(*self) & 1 == 0
|
||||
}
|
||||
|
||||
/// Returns `true` if the number is not divisible by `2`
|
||||
#[inline]
|
||||
fn is_odd(&self) -> bool { !self.is_even() }
|
||||
fn is_odd(&self) -> bool {
|
||||
!self.is_even()
|
||||
}
|
||||
|
||||
/// Simultaneous truncated integer division and modulus.
|
||||
#[inline]
|
||||
|
@ -324,8 +564,8 @@ macro_rules! impl_integer_for_isize {
|
|||
|
||||
#[cfg(test)]
|
||||
mod $test_mod {
|
||||
use Integer;
|
||||
use core::mem;
|
||||
use Integer;
|
||||
|
||||
/// Checks that the division rule holds for:
|
||||
///
|
||||
|
@ -333,14 +573,14 @@ macro_rules! impl_integer_for_isize {
|
|||
/// - `d`: denominator (divisor)
|
||||
/// - `qr`: quotient and remainder
|
||||
#[cfg(test)]
|
||||
fn test_division_rule((n,d): ($T, $T), (q,r): ($T, $T)) {
|
||||
fn test_division_rule((n, d): ($T, $T), (q, r): ($T, $T)) {
|
||||
assert_eq!(d * q + r, n);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_rem() {
|
||||
fn test_nd_dr(nd: ($T,$T), qr: ($T,$T)) {
|
||||
let (n,d) = nd;
|
||||
fn test_nd_dr(nd: ($T, $T), qr: ($T, $T)) {
|
||||
let (n, d) = nd;
|
||||
let separate_div_rem = (n / d, n % d);
|
||||
let combined_div_rem = n.div_rem(&d);
|
||||
|
||||
|
@ -351,21 +591,21 @@ macro_rules! impl_integer_for_isize {
|
|||
test_division_rule(nd, combined_div_rem);
|
||||
}
|
||||
|
||||
test_nd_dr(( 8, 3), ( 2, 2));
|
||||
test_nd_dr(( 8, -3), (-2, 2));
|
||||
test_nd_dr((8, 3), (2, 2));
|
||||
test_nd_dr((8, -3), (-2, 2));
|
||||
test_nd_dr((-8, 3), (-2, -2));
|
||||
test_nd_dr((-8, -3), ( 2, -2));
|
||||
test_nd_dr((-8, -3), (2, -2));
|
||||
|
||||
test_nd_dr(( 1, 2), ( 0, 1));
|
||||
test_nd_dr(( 1, -2), ( 0, 1));
|
||||
test_nd_dr((-1, 2), ( 0, -1));
|
||||
test_nd_dr((-1, -2), ( 0, -1));
|
||||
test_nd_dr((1, 2), (0, 1));
|
||||
test_nd_dr((1, -2), (0, 1));
|
||||
test_nd_dr((-1, 2), (0, -1));
|
||||
test_nd_dr((-1, -2), (0, -1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_mod_floor() {
|
||||
fn test_nd_dm(nd: ($T,$T), dm: ($T,$T)) {
|
||||
let (n,d) = nd;
|
||||
fn test_nd_dm(nd: ($T, $T), dm: ($T, $T)) {
|
||||
let (n, d) = nd;
|
||||
let separate_div_mod_floor = (n.div_floor(&d), n.mod_floor(&d));
|
||||
let combined_div_mod_floor = n.div_mod_floor(&d);
|
||||
|
||||
|
@ -376,15 +616,15 @@ macro_rules! impl_integer_for_isize {
|
|||
test_division_rule(nd, combined_div_mod_floor);
|
||||
}
|
||||
|
||||
test_nd_dm(( 8, 3), ( 2, 2));
|
||||
test_nd_dm(( 8, -3), (-3, -1));
|
||||
test_nd_dm((8, 3), (2, 2));
|
||||
test_nd_dm((8, -3), (-3, -1));
|
||||
test_nd_dm((-8, 3), (-3, 1));
|
||||
test_nd_dm((-8, -3), ( 2, -2));
|
||||
test_nd_dm((-8, -3), (2, -2));
|
||||
|
||||
test_nd_dm(( 1, 2), ( 0, 1));
|
||||
test_nd_dm(( 1, -2), (-1, -1));
|
||||
test_nd_dm((1, 2), (0, 1));
|
||||
test_nd_dm((1, -2), (-1, -1));
|
||||
test_nd_dm((-1, 2), (-1, 1));
|
||||
test_nd_dm((-1, -2), ( 0, -1));
|
||||
test_nd_dm((-1, -2), (0, -1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -414,7 +654,7 @@ macro_rules! impl_integer_for_isize {
|
|||
// for i8
|
||||
for i in -127..127 {
|
||||
for j in -127..127 {
|
||||
assert_eq!(euclidean_gcd(i,j), i.gcd(&j));
|
||||
assert_eq!(euclidean_gcd(i, j), i.gcd(&j));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -422,7 +662,7 @@ macro_rules! impl_integer_for_isize {
|
|||
// FIXME: Use inclusive ranges for above loop when implemented
|
||||
let i = 127;
|
||||
for j in -127..127 {
|
||||
assert_eq!(euclidean_gcd(i,j), i.gcd(&j));
|
||||
assert_eq!(euclidean_gcd(i, j), i.gcd(&j));
|
||||
}
|
||||
assert_eq!(127.gcd(&127), 127);
|
||||
}
|
||||
|
@ -473,6 +713,49 @@ macro_rules! impl_integer_for_isize {
|
|||
assert_eq!((11 as $T).lcm(&5), 55 as $T);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gcd_lcm() {
|
||||
use core::iter::once;
|
||||
for i in once(0)
|
||||
.chain((1..).take(127).flat_map(|a| once(a).chain(once(-a))))
|
||||
.chain(once(-128))
|
||||
{
|
||||
for j in once(0)
|
||||
.chain((1..).take(127).flat_map(|a| once(a).chain(once(-a))))
|
||||
.chain(once(-128))
|
||||
{
|
||||
assert_eq!(i.gcd_lcm(&j), (i.gcd(&j), i.lcm(&j)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extended_gcd_lcm() {
|
||||
use core::fmt::Debug;
|
||||
use traits::NumAssign;
|
||||
use ExtendedGcd;
|
||||
|
||||
fn check<A: Copy + Debug + Integer + NumAssign>(a: A, b: A) {
|
||||
let ExtendedGcd { gcd, x, y, .. } = a.extended_gcd(&b);
|
||||
assert_eq!(gcd, x * a + y * b);
|
||||
}
|
||||
|
||||
use core::iter::once;
|
||||
for i in once(0)
|
||||
.chain((1..).take(127).flat_map(|a| once(a).chain(once(-a))))
|
||||
.chain(once(-128))
|
||||
{
|
||||
for j in once(0)
|
||||
.chain((1..).take(127).flat_map(|a| once(a).chain(once(-a))))
|
||||
.chain(once(-128))
|
||||
{
|
||||
check(i, j);
|
||||
let (ExtendedGcd { gcd, .. }, lcm) = i.extended_gcd_lcm(&j);
|
||||
assert_eq!((gcd, lcm), (i.gcd(&j), i.lcm(&j)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_even() {
|
||||
assert_eq!((-4 as $T).is_even(), true);
|
||||
|
@ -499,7 +782,7 @@ macro_rules! impl_integer_for_isize {
|
|||
assert_eq!((4 as $T).is_odd(), false);
|
||||
}
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
impl_integer_for_isize!(i8, test_integer_i8);
|
||||
|
@ -511,7 +794,7 @@ impl_integer_for_isize!(isize, test_integer_isize);
|
|||
impl_integer_for_isize!(i128, test_integer_i128);
|
||||
|
||||
macro_rules! impl_integer_for_usize {
|
||||
($T:ty, $test_mod:ident) => (
|
||||
($T:ty, $test_mod:ident) => {
|
||||
impl Integer for $T {
|
||||
/// Unsigned integer division. Returns the same result as `div` (`/`).
|
||||
#[inline]
|
||||
|
@ -525,34 +808,68 @@ macro_rules! impl_integer_for_usize {
|
|||
*self % *other
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn div_ceil(&self, other: &Self) -> Self {
|
||||
*self / *other + (0 != *self % *other) as Self
|
||||
}
|
||||
|
||||
/// Calculates the Greatest Common Divisor (GCD) of the number and `other`
|
||||
#[inline]
|
||||
fn gcd(&self, other: &Self) -> Self {
|
||||
// Use Stein's algorithm
|
||||
let mut m = *self;
|
||||
let mut n = *other;
|
||||
if m == 0 || n == 0 { return m | n }
|
||||
if m == 0 || n == 0 {
|
||||
return m | n;
|
||||
}
|
||||
|
||||
// find common factors of 2
|
||||
let shift = (m | n).trailing_zeros();
|
||||
|
||||
// divide n and m by 2 until odd
|
||||
// m inside loop
|
||||
m >>= m.trailing_zeros();
|
||||
n >>= n.trailing_zeros();
|
||||
|
||||
while m != 0 {
|
||||
m >>= m.trailing_zeros();
|
||||
if n > m { mem::swap(&mut n, &mut m) }
|
||||
while m != n {
|
||||
if m > n {
|
||||
m -= n;
|
||||
m >>= m.trailing_zeros();
|
||||
} else {
|
||||
n -= m;
|
||||
n >>= n.trailing_zeros();
|
||||
}
|
||||
}
|
||||
m << shift
|
||||
}
|
||||
|
||||
n << shift
|
||||
#[inline]
|
||||
fn extended_gcd_lcm(&self, other: &Self) -> (ExtendedGcd<Self>, Self) {
|
||||
let egcd = self.extended_gcd(other);
|
||||
// should not have to recalculate abs
|
||||
let lcm = if egcd.gcd.is_zero() {
|
||||
Self::zero()
|
||||
} else {
|
||||
*self * (*other / egcd.gcd)
|
||||
};
|
||||
(egcd, lcm)
|
||||
}
|
||||
|
||||
/// Calculates the Lowest Common Multiple (LCM) of the number and `other`.
|
||||
#[inline]
|
||||
fn lcm(&self, other: &Self) -> Self {
|
||||
*self * (*other / self.gcd(other))
|
||||
self.gcd_lcm(other).1
|
||||
}
|
||||
|
||||
/// Calculates the Greatest Common Divisor (GCD) and
|
||||
/// Lowest Common Multiple (LCM) of the number and `other`.
|
||||
#[inline]
|
||||
fn gcd_lcm(&self, other: &Self) -> (Self, Self) {
|
||||
if self.is_zero() && other.is_zero() {
|
||||
return (Self::zero(), Self::zero());
|
||||
}
|
||||
let gcd = self.gcd(other);
|
||||
let lcm = *self * (*other / gcd);
|
||||
(gcd, lcm)
|
||||
}
|
||||
|
||||
/// Deprecated, use `is_multiple_of` instead.
|
||||
|
@ -588,8 +905,8 @@ macro_rules! impl_integer_for_usize {
|
|||
|
||||
#[cfg(test)]
|
||||
mod $test_mod {
|
||||
use Integer;
|
||||
use core::mem;
|
||||
use Integer;
|
||||
|
||||
#[test]
|
||||
fn test_div_mod_floor() {
|
||||
|
@ -625,7 +942,7 @@ macro_rules! impl_integer_for_usize {
|
|||
|
||||
for i in 0..255 {
|
||||
for j in 0..255 {
|
||||
assert_eq!(euclidean_gcd(i,j), i.gcd(&j));
|
||||
assert_eq!(euclidean_gcd(i, j), i.gcd(&j));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -633,7 +950,7 @@ macro_rules! impl_integer_for_usize {
|
|||
// FIXME: Use inclusive ranges for above loop when implemented
|
||||
let i = 255;
|
||||
for j in 0..255 {
|
||||
assert_eq!(euclidean_gcd(i,j), i.gcd(&j));
|
||||
assert_eq!(euclidean_gcd(i, j), i.gcd(&j));
|
||||
}
|
||||
assert_eq!(255.gcd(&255), 255);
|
||||
}
|
||||
|
@ -648,6 +965,15 @@ macro_rules! impl_integer_for_usize {
|
|||
assert_eq!((15 as $T).lcm(&17), 255 as $T);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gcd_lcm() {
|
||||
for i in (0..).take(256) {
|
||||
for j in (0..).take(256) {
|
||||
assert_eq!(i.gcd_lcm(&j), (i.gcd(&j), i.lcm(&j)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_multiple_of() {
|
||||
assert!((6 as $T).is_multiple_of(&(6 as $T)));
|
||||
|
@ -673,7 +999,7 @@ macro_rules! impl_integer_for_usize {
|
|||
assert_eq!((4 as $T).is_odd(), false);
|
||||
}
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
impl_integer_for_usize!(u8, test_integer_u8);
|
||||
|
@ -692,7 +1018,8 @@ pub struct IterBinomial<T> {
|
|||
}
|
||||
|
||||
impl<T> IterBinomial<T>
|
||||
where T: Integer,
|
||||
where
|
||||
T: Integer,
|
||||
{
|
||||
/// For a given n, iterate over all binomial coefficients binomial(n, k), for k=0...n.
|
||||
///
|
||||
|
@ -714,13 +1041,16 @@ impl<T> IterBinomial<T>
|
|||
/// For larger n, `T` should be a bigint type.
|
||||
pub fn new(n: T) -> IterBinomial<T> {
|
||||
IterBinomial {
|
||||
k: T::zero(), a: T::one(), n: n
|
||||
k: T::zero(),
|
||||
a: T::one(),
|
||||
n: n,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Iterator for IterBinomial<T>
|
||||
where T: Integer + Clone
|
||||
where
|
||||
T: Integer + Clone,
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
|
@ -732,7 +1062,7 @@ impl<T> Iterator for IterBinomial<T>
|
|||
multiply_and_divide(
|
||||
self.a.clone(),
|
||||
self.n.clone() - self.k.clone() + T::one(),
|
||||
self.k.clone()
|
||||
self.k.clone(),
|
||||
)
|
||||
} else {
|
||||
T::one()
|
||||
|
@ -748,7 +1078,7 @@ impl<T> Iterator for IterBinomial<T>
|
|||
fn multiply_and_divide<T: Integer + Clone>(r: T, a: T, b: T) -> T {
|
||||
// See http://blog.plover.com/math/choose-2.html for the idea.
|
||||
let g = gcd(r.clone(), b.clone());
|
||||
r/g.clone() * (a / (b/g))
|
||||
r / g.clone() * (a / (b / g))
|
||||
}
|
||||
|
||||
/// Calculate the binomial coefficient.
|
||||
|
@ -792,7 +1122,8 @@ pub fn binomial<T: Integer + Clone>(mut n: T, k: T) -> T {
|
|||
|
||||
/// Calculate the multinomial coefficient.
|
||||
pub fn multinomial<T: Integer + Clone>(k: &[T]) -> T
|
||||
where for<'a> T: Add<&'a T, Output = T>
|
||||
where
|
||||
for<'a> T: Add<&'a T, Output = T>,
|
||||
{
|
||||
let mut r = T::one();
|
||||
let mut p = T::zero();
|
||||
|
@ -806,16 +1137,20 @@ pub fn multinomial<T: Integer + Clone>(k: &[T]) -> T
|
|||
#[test]
|
||||
fn test_lcm_overflow() {
|
||||
macro_rules! check {
|
||||
($t:ty, $x:expr, $y:expr, $r:expr) => { {
|
||||
($t:ty, $x:expr, $y:expr, $r:expr) => {{
|
||||
let x: $t = $x;
|
||||
let y: $t = $y;
|
||||
let o = x.checked_mul(y);
|
||||
assert!(o.is_none(),
|
||||
assert!(
|
||||
o.is_none(),
|
||||
"sanity checking that {} input {} * {} overflows",
|
||||
stringify!($t), x, y);
|
||||
stringify!($t),
|
||||
x,
|
||||
y
|
||||
);
|
||||
assert_eq!(x.lcm(&y), $r);
|
||||
assert_eq!(y.lcm(&x), $r);
|
||||
} }
|
||||
}};
|
||||
}
|
||||
|
||||
// Original bug (Issue #166)
|
||||
|
@ -834,13 +1169,13 @@ fn test_lcm_overflow() {
|
|||
#[test]
|
||||
fn test_iter_binomial() {
|
||||
macro_rules! check_simple {
|
||||
($t:ty) => { {
|
||||
($t:ty) => {{
|
||||
let n: $t = 3;
|
||||
let expected = [1, 3, 3, 1];
|
||||
for (b, &e) in IterBinomial::new(n).zip(&expected) {
|
||||
assert_eq!(b, e);
|
||||
}
|
||||
} }
|
||||
}};
|
||||
}
|
||||
|
||||
check_simple!(u8);
|
||||
|
@ -853,14 +1188,14 @@ fn test_iter_binomial() {
|
|||
check_simple!(i64);
|
||||
|
||||
macro_rules! check_binomial {
|
||||
($t:ty, $n:expr) => { {
|
||||
($t:ty, $n:expr) => {{
|
||||
let n: $t = $n;
|
||||
let mut k: $t = 0;
|
||||
for b in IterBinomial::new(n) {
|
||||
assert_eq!(b, binomial(n, k));
|
||||
k += 1;
|
||||
}
|
||||
} }
|
||||
}};
|
||||
}
|
||||
|
||||
// Check the largest n for which there is no overflow.
|
||||
|
@ -877,7 +1212,7 @@ fn test_iter_binomial() {
|
|||
#[test]
|
||||
fn test_binomial() {
|
||||
macro_rules! check {
|
||||
($t:ty, $x:expr, $y:expr, $r:expr) => { {
|
||||
($t:ty, $x:expr, $y:expr, $r:expr) => {{
|
||||
let x: $t = $x;
|
||||
let y: $t = $y;
|
||||
let expected: $t = $r;
|
||||
|
@ -885,7 +1220,7 @@ fn test_binomial() {
|
|||
if y <= x {
|
||||
assert_eq!(binomial(x, x - y), expected);
|
||||
}
|
||||
} }
|
||||
}};
|
||||
}
|
||||
check!(u8, 9, 4, 126);
|
||||
check!(u8, 0, 0, 1);
|
||||
|
@ -933,12 +1268,12 @@ fn test_binomial() {
|
|||
#[test]
|
||||
fn test_multinomial() {
|
||||
macro_rules! check_binomial {
|
||||
($t:ty, $k:expr) => { {
|
||||
($t:ty, $k:expr) => {{
|
||||
let n: $t = $k.iter().fold(0, |acc, &x| acc + x);
|
||||
let k: &[$t] = $k;
|
||||
assert_eq!(k.len(), 2);
|
||||
assert_eq!(multinomial(k), binomial(n, k[0]));
|
||||
} }
|
||||
}};
|
||||
}
|
||||
|
||||
check_binomial!(u8, &[4, 5]);
|
||||
|
@ -968,11 +1303,11 @@ fn test_multinomial() {
|
|||
check_binomial!(i64, &[4, 10]);
|
||||
|
||||
macro_rules! check_multinomial {
|
||||
($t:ty, $k:expr, $r:expr) => { {
|
||||
($t:ty, $k:expr, $r:expr) => {{
|
||||
let k: &[$t] = $k;
|
||||
let expected: $t = $r;
|
||||
assert_eq!(multinomial(k), expected);
|
||||
} }
|
||||
}};
|
||||
}
|
||||
|
||||
check_multinomial!(u8, &[2, 1, 2], 30);
|
||||
|
|
|
@ -202,37 +202,39 @@ fn log2<T: PrimInt>(x: T) -> u32 {
|
|||
macro_rules! unsigned_roots {
|
||||
($T:ident) => {
|
||||
impl Roots for $T {
|
||||
#[inline]
|
||||
fn nth_root(&self, n: u32) -> Self {
|
||||
fn go(a: $T, n: u32) -> $T {
|
||||
// Specialize small roots
|
||||
match n {
|
||||
0 => panic!("can't find a root of degree 0!"),
|
||||
1 => return *self,
|
||||
2 => return self.sqrt(),
|
||||
3 => return self.cbrt(),
|
||||
1 => return a,
|
||||
2 => return a.sqrt(),
|
||||
3 => return a.cbrt(),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// The root of values less than 2ⁿ can only be 0 or 1.
|
||||
if bits::<$T>() <= n || *self < (1 << n) {
|
||||
return (*self > 0) as $T;
|
||||
if bits::<$T>() <= n || a < (1 << n) {
|
||||
return (a > 0) as $T;
|
||||
}
|
||||
|
||||
if bits::<$T>() > 64 {
|
||||
// 128-bit division is slow, so do a bitwise `nth_root` until it's small enough.
|
||||
return if *self <= core::u64::MAX as $T {
|
||||
(*self as u64).nth_root(n) as $T
|
||||
return if a <= core::u64::MAX as $T {
|
||||
(a as u64).nth_root(n) as $T
|
||||
} else {
|
||||
let lo = (self >> n).nth_root(n) << 1;
|
||||
let lo = (a >> n).nth_root(n) << 1;
|
||||
let hi = lo + 1;
|
||||
// 128-bit `checked_mul` also involves division, but we can't always
|
||||
// compute `hiⁿ` without risking overflow. Try to avoid it though...
|
||||
if hi.next_power_of_two().trailing_zeros() * n >= bits::<$T>() {
|
||||
match checked_pow(hi, n as usize) {
|
||||
Some(x) if x <= *self => hi,
|
||||
Some(x) if x <= a => hi,
|
||||
_ => lo,
|
||||
}
|
||||
} else {
|
||||
if hi.pow(n) <= *self {
|
||||
if hi.pow(n) <= a {
|
||||
hi
|
||||
} else {
|
||||
lo
|
||||
|
@ -262,24 +264,27 @@ macro_rules! unsigned_roots {
|
|||
let n1 = n - 1;
|
||||
let next = |x: $T| {
|
||||
let y = match checked_pow(x, n1 as usize) {
|
||||
Some(ax) => self / ax,
|
||||
Some(ax) => a / ax,
|
||||
None => 0,
|
||||
};
|
||||
(y + x * n1 as $T) / n as $T
|
||||
};
|
||||
fixpoint(guess(*self, n), next)
|
||||
fixpoint(guess(a, n), next)
|
||||
}
|
||||
go(*self, n)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sqrt(&self) -> Self {
|
||||
fn go(a: $T) -> $T {
|
||||
if bits::<$T>() > 64 {
|
||||
// 128-bit division is slow, so do a bitwise `sqrt` until it's small enough.
|
||||
// https://en.wikipedia.org/wiki/Integer_square_root#Using_bitwise_operations
|
||||
return if *self <= core::u64::MAX as $T {
|
||||
(*self as u64).sqrt() as $T
|
||||
return if a <= core::u64::MAX as $T {
|
||||
(a as u64).sqrt() as $T
|
||||
} else {
|
||||
let lo = (self >> 2u32).sqrt() << 1;
|
||||
let lo = (a >> 2u32).sqrt() << 1;
|
||||
let hi = lo + 1;
|
||||
if hi * hi <= *self {
|
||||
if hi * hi <= a {
|
||||
hi
|
||||
} else {
|
||||
lo
|
||||
|
@ -287,8 +292,8 @@ macro_rules! unsigned_roots {
|
|||
};
|
||||
}
|
||||
|
||||
if *self < 4 {
|
||||
return (*self > 0) as Self;
|
||||
if a < 4 {
|
||||
return (a > 0) as $T;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
|
@ -304,19 +309,23 @@ macro_rules! unsigned_roots {
|
|||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
|
||||
let next = |x: $T| (self / x + x) >> 1;
|
||||
fixpoint(guess(*self), next)
|
||||
let next = |x: $T| (a / x + x) >> 1;
|
||||
fixpoint(guess(a), next)
|
||||
}
|
||||
go(*self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cbrt(&self) -> Self {
|
||||
fn go(a: $T) -> $T {
|
||||
if bits::<$T>() > 64 {
|
||||
// 128-bit division is slow, so do a bitwise `cbrt` until it's small enough.
|
||||
return if *self <= core::u64::MAX as $T {
|
||||
(*self as u64).cbrt() as $T
|
||||
return if a <= core::u64::MAX as $T {
|
||||
(a as u64).cbrt() as $T
|
||||
} else {
|
||||
let lo = (self >> 3u32).cbrt() << 1;
|
||||
let lo = (a >> 3u32).cbrt() << 1;
|
||||
let hi = lo + 1;
|
||||
if hi * hi * hi <= *self {
|
||||
if hi * hi * hi <= a {
|
||||
hi
|
||||
} else {
|
||||
lo
|
||||
|
@ -326,7 +335,7 @@ macro_rules! unsigned_roots {
|
|||
|
||||
if bits::<$T>() <= 32 {
|
||||
// Implementation based on Hacker's Delight `icbrt2`
|
||||
let mut x = *self;
|
||||
let mut x = a;
|
||||
let mut y2 = 0;
|
||||
let mut y = 0;
|
||||
let smax = bits::<$T>() / 3;
|
||||
|
@ -344,11 +353,11 @@ macro_rules! unsigned_roots {
|
|||
return y;
|
||||
}
|
||||
|
||||
if *self < 8 {
|
||||
return (*self > 0) as Self;
|
||||
if a < 8 {
|
||||
return (a > 0) as $T;
|
||||
}
|
||||
if *self <= core::u32::MAX as $T {
|
||||
return (*self as u32).cbrt() as $T;
|
||||
if a <= core::u32::MAX as $T {
|
||||
return (a as u32).cbrt() as $T;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
|
@ -364,8 +373,10 @@ macro_rules! unsigned_roots {
|
|||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Cube_root#Numerical_methods
|
||||
let next = |x: $T| (self / (x * x) + x * 2) / 3;
|
||||
fixpoint(guess(*self), next)
|
||||
let next = |x: $T| (a / (x * x) + x * 2) / 3;
|
||||
fixpoint(guess(a), next)
|
||||
}
|
||||
go(*self)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,11 +10,7 @@ use std::mem;
|
|||
|
||||
trait TestInteger: Roots + PrimInt + Debug + AsPrimitive<f64> + 'static {}
|
||||
|
||||
impl<T> TestInteger for T
|
||||
where
|
||||
T: Roots + PrimInt + Debug + AsPrimitive<f64> + 'static,
|
||||
{
|
||||
}
|
||||
impl<T> TestInteger for T where T: Roots + PrimInt + Debug + AsPrimitive<f64> + 'static {}
|
||||
|
||||
/// Check that each root is correct
|
||||
///
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"c13da0848b07a604569216fdb430f87d43b83252055ab1f6e237fc4020897b2b","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"f3be0ace34d626865f124c492483c30cadc3362c17aabefed45fa2686c67a070","RELEASES.md":"1031b964f47fd9c33e0249d1343b3292c830d7e2e6c8d88931dcbc25a20a0337","bors.toml":"1c81ede536a37edd30fe4e622ff0531b25372403ac9475a5d6c50f14156565a2","build.rs":"16de2aa57e754fc1526d0400b5d87a3f771296705fca54601aa598b6f74ded8f","ci/rustup.sh":"2aa9e89e4af81ed9da86bdcf7cdabe512287c877248783b69eed1eccf09ad6bb","ci/test_full.sh":"22ab0d413456c350a8a0e62e3614da628dad06eb4c923b4d162aef4cb47b9dd2","src/lib.rs":"e6b0f870ab6e741f15293b76b11e5bc01193aa8e08c13f5f67e2f85c3808636f"},"package":"af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124"}
|
||||
{"files":{"Cargo.toml":"d0ebc04247f9987c23051c00f74e6c6f1df4f29147c5169ab61018865a7b1fcc","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"f3be0ace34d626865f124c492483c30cadc3362c17aabefed45fa2686c67a070","RELEASES.md":"b238cf5d43af9324607e4127efb55bd90f257d71b20c5d2d1108bcb2355560db","build.rs":"56d4fbb7a55750e61d2074df2735a31995c1decbd988c0e722926235e0fed487","src/lib.rs":"9aaa9570d8dc978fc8d653f30d8646bd9891062fd307726a84724bc86155c0f9"},"package":"76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e"}
|
|
@ -3,7 +3,7 @@
|
|||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
|
@ -12,9 +12,10 @@
|
|||
|
||||
[package]
|
||||
name = "num-iter"
|
||||
version = "0.1.37"
|
||||
version = "0.1.39"
|
||||
authors = ["The Rust Project Developers"]
|
||||
build = "build.rs"
|
||||
exclude = ["/ci/*", "/.travis.yml", "/bors.toml"]
|
||||
description = "External iterators for generic mathematics"
|
||||
homepage = "https://github.com/rust-num/num-iter"
|
||||
documentation = "https://docs.rs/num-iter"
|
||||
|
@ -32,6 +33,8 @@ default-features = false
|
|||
[dependencies.num-traits]
|
||||
version = "0.2.4"
|
||||
default-features = false
|
||||
[build-dependencies.autocfg]
|
||||
version = "0.1.3"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
|
|
|
@ -1,4 +1,18 @@
|
|||
# Release 0.1.37
|
||||
# Release 0.1.39 (2019-05-21)
|
||||
|
||||
- [Fixed feature detection on `no_std` targets][11].
|
||||
|
||||
**Contributors**: @cuviper
|
||||
|
||||
[11]: https://github.com/rust-num/num-iter/pull/11
|
||||
|
||||
# Release 0.1.38 (2019-05-20)
|
||||
|
||||
- Maintenance update -- no functional changes.
|
||||
|
||||
**Contributors**: @cuviper, @ignatenkobrain
|
||||
|
||||
# Release 0.1.37 (2018-05-11)
|
||||
|
||||
- [Support for 128-bit integers is now automatically detected and enabled.][5]
|
||||
Setting the `i128` crate feature now causes the build script to panic if such
|
||||
|
@ -8,7 +22,7 @@
|
|||
|
||||
[5]: https://github.com/rust-num/num-iter/pull/5
|
||||
|
||||
# Release 0.1.36
|
||||
# Release 0.1.36 (2018-05-10)
|
||||
|
||||
- [The iterators are now implemented for `i128` and `u128`][7] starting with
|
||||
Rust 1.26, enabled by the new `i128` crate feature.
|
||||
|
@ -17,7 +31,7 @@
|
|||
|
||||
[4]: https://github.com/rust-num/num-iter/pull/4
|
||||
|
||||
# Release 0.1.35
|
||||
# Release 0.1.35 (2018-02-06)
|
||||
|
||||
- [num-iter now has its own source repository][num-356] at [rust-num/num-iter][home].
|
||||
- [There is now a `std` feature][2], enabled by default, along with the implication
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
status = [
|
||||
"continuous-integration/travis-ci/push",
|
||||
]
|
|
@ -1,35 +1,14 @@
|
|||
extern crate autocfg;
|
||||
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
fn main() {
|
||||
if probe("fn main() { 0i128; }") {
|
||||
let ac = autocfg::new();
|
||||
if ac.probe_type("i128") {
|
||||
println!("cargo:rustc-cfg=has_i128");
|
||||
} else if env::var_os("CARGO_FEATURE_I128").is_some() {
|
||||
panic!("i128 support was not detected!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Test if a code snippet can be compiled
|
||||
fn probe(code: &str) -> bool {
|
||||
let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
|
||||
let out_dir = env::var_os("OUT_DIR").expect("environment variable OUT_DIR");
|
||||
|
||||
let mut child = Command::new(rustc)
|
||||
.arg("--out-dir")
|
||||
.arg(out_dir)
|
||||
.arg("--emit=obj")
|
||||
.arg("-")
|
||||
.stdin(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("rustc probe");
|
||||
|
||||
child
|
||||
.stdin
|
||||
.as_mut()
|
||||
.expect("rustc stdin")
|
||||
.write_all(code.as_bytes())
|
||||
.expect("write rustc stdin");
|
||||
|
||||
child.wait().expect("rustc probe").success()
|
||||
|
||||
autocfg::rerun_path(file!());
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/sh
|
||||
# Use rustup to locally run the same suite of tests as .travis.yml.
|
||||
# (You should first install/update 1.8.0, stable, beta, and nightly.)
|
||||
|
||||
set -ex
|
||||
|
||||
export TRAVIS_RUST_VERSION
|
||||
for TRAVIS_RUST_VERSION in 1.8.0 1.15.0 1.20.0 stable beta nightly; do
|
||||
run="rustup run $TRAVIS_RUST_VERSION"
|
||||
$run cargo build --verbose
|
||||
$run $PWD/ci/test_full.sh
|
||||
done
|
|
@ -1,19 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
echo Testing num-iter on rustc ${TRAVIS_RUST_VERSION}
|
||||
|
||||
# num-iter should build and test everywhere.
|
||||
cargo build --verbose
|
||||
cargo test --verbose
|
||||
|
||||
# test `no_std`
|
||||
cargo build --verbose --no-default-features
|
||||
cargo test --verbose --no-default-features
|
||||
|
||||
# test `i128`
|
||||
if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable)$ ]]; then
|
||||
cargo build --verbose --features=i128
|
||||
cargo test --verbose --features=i128
|
||||
fi
|
|
@ -15,25 +15,24 @@
|
|||
//! The `num-iter` crate is tested for rustc 1.8 and greater.
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/num-iter/0.1")]
|
||||
|
||||
#![no_std]
|
||||
#[cfg(feature = "std")]
|
||||
extern crate std;
|
||||
|
||||
extern crate num_traits as traits;
|
||||
extern crate num_integer as integer;
|
||||
extern crate num_traits as traits;
|
||||
|
||||
use integer::Integer;
|
||||
use traits::{Zero, One, CheckedAdd, ToPrimitive};
|
||||
use core::ops::{Add, Sub};
|
||||
use core::usize;
|
||||
use integer::Integer;
|
||||
use traits::{CheckedAdd, One, ToPrimitive, Zero};
|
||||
|
||||
/// An iterator over the range [start, stop)
|
||||
#[derive(Clone)]
|
||||
pub struct Range<A> {
|
||||
state: A,
|
||||
stop: A,
|
||||
one: A
|
||||
one: A,
|
||||
}
|
||||
|
||||
/// Returns an iterator over the given range [start, stop) (that is, starting
|
||||
|
@ -51,9 +50,14 @@ pub struct Range<A> {
|
|||
/// ```
|
||||
#[inline]
|
||||
pub fn range<A>(start: A, stop: A) -> Range<A>
|
||||
where A: Add<A, Output = A> + PartialOrd + Clone + One
|
||||
where
|
||||
A: Add<A, Output = A> + PartialOrd + Clone + One,
|
||||
{
|
||||
Range{state: start, stop: stop, one: One::one()}
|
||||
Range {
|
||||
state: start,
|
||||
stop: stop,
|
||||
one: One::one(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -82,7 +86,8 @@ fn unsigned<T: ToPrimitive>(x: &T) -> Option<u64> {
|
|||
|
||||
// FIXME: rust-lang/rust#10414: Unfortunate type bound
|
||||
impl<A> Iterator for Range<A>
|
||||
where A: Add<A, Output = A> + PartialOrd + Clone + ToPrimitive
|
||||
where
|
||||
A: Add<A, Output = A> + PartialOrd + Clone + ToPrimitive,
|
||||
{
|
||||
type Item = A;
|
||||
|
||||
|
@ -113,7 +118,7 @@ impl<A> Iterator for Range<A>
|
|||
return match b.wrapping_sub(a).to_usize() {
|
||||
Some(len) => (len, Some(len)),
|
||||
None => (usize::MAX, None),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,7 +130,8 @@ impl<A> Iterator for Range<A>
|
|||
/// `Integer` is required to ensure the range will be the same regardless of
|
||||
/// the direction it is consumed.
|
||||
impl<A> DoubleEndedIterator for Range<A>
|
||||
where A: Integer + Clone + ToPrimitive
|
||||
where
|
||||
A: Integer + Clone + ToPrimitive,
|
||||
{
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<A> {
|
||||
|
@ -148,13 +154,18 @@ pub struct RangeInclusive<A> {
|
|||
/// Return an iterator over the range [start, stop]
|
||||
#[inline]
|
||||
pub fn range_inclusive<A>(start: A, stop: A) -> RangeInclusive<A>
|
||||
where A: Add<A, Output = A> + PartialOrd + Clone + One
|
||||
where
|
||||
A: Add<A, Output = A> + PartialOrd + Clone + One,
|
||||
{
|
||||
RangeInclusive{range: range(start, stop), done: false}
|
||||
RangeInclusive {
|
||||
range: range(start, stop),
|
||||
done: false,
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Iterator for RangeInclusive<A>
|
||||
where A: Add<A, Output = A> + PartialOrd + Clone + ToPrimitive
|
||||
where
|
||||
A: Add<A, Output = A> + PartialOrd + Clone + ToPrimitive,
|
||||
{
|
||||
type Item = A;
|
||||
|
||||
|
@ -182,7 +193,7 @@ impl<A> Iterator for RangeInclusive<A>
|
|||
let lo = lo.saturating_add(1);
|
||||
let hi = match hi {
|
||||
Some(x) => x.checked_add(1),
|
||||
None => None
|
||||
None => None,
|
||||
};
|
||||
(lo, hi)
|
||||
}
|
||||
|
@ -190,7 +201,8 @@ impl<A> Iterator for RangeInclusive<A>
|
|||
}
|
||||
|
||||
impl<A> DoubleEndedIterator for RangeInclusive<A>
|
||||
where A: Sub<A, Output = A> + Integer + Clone + ToPrimitive
|
||||
where
|
||||
A: Sub<A, Output = A> + Integer + Clone + ToPrimitive,
|
||||
{
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<A> {
|
||||
|
@ -219,14 +231,21 @@ pub struct RangeStep<A> {
|
|||
/// Return an iterator over the range [start, stop) by `step`. It handles overflow by stopping.
|
||||
#[inline]
|
||||
pub fn range_step<A>(start: A, stop: A, step: A) -> RangeStep<A>
|
||||
where A: CheckedAdd + PartialOrd + Clone + Zero
|
||||
where
|
||||
A: CheckedAdd + PartialOrd + Clone + Zero,
|
||||
{
|
||||
let rev = step < Zero::zero();
|
||||
RangeStep{state: start, stop: stop, step: step, rev: rev}
|
||||
RangeStep {
|
||||
state: start,
|
||||
stop: stop,
|
||||
step: step,
|
||||
rev: rev,
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Iterator for RangeStep<A>
|
||||
where A: CheckedAdd + PartialOrd + Clone
|
||||
where
|
||||
A: CheckedAdd + PartialOrd + Clone,
|
||||
{
|
||||
type Item = A;
|
||||
|
||||
|
@ -236,7 +255,7 @@ impl<A> Iterator for RangeStep<A>
|
|||
let result = self.state.clone();
|
||||
match self.state.checked_add(&self.step) {
|
||||
Some(x) => self.state = x,
|
||||
None => self.state = self.stop.clone()
|
||||
None => self.state = self.stop.clone(),
|
||||
}
|
||||
Some(result)
|
||||
} else {
|
||||
|
@ -258,25 +277,34 @@ pub struct RangeStepInclusive<A> {
|
|||
/// Return an iterator over the range [start, stop] by `step`. It handles overflow by stopping.
|
||||
#[inline]
|
||||
pub fn range_step_inclusive<A>(start: A, stop: A, step: A) -> RangeStepInclusive<A>
|
||||
where A: CheckedAdd + PartialOrd + Clone + Zero
|
||||
where
|
||||
A: CheckedAdd + PartialOrd + Clone + Zero,
|
||||
{
|
||||
let rev = step < Zero::zero();
|
||||
RangeStepInclusive{state: start, stop: stop, step: step, rev: rev, done: false}
|
||||
RangeStepInclusive {
|
||||
state: start,
|
||||
stop: stop,
|
||||
step: step,
|
||||
rev: rev,
|
||||
done: false,
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Iterator for RangeStepInclusive<A>
|
||||
where A: CheckedAdd + PartialOrd + Clone + PartialEq
|
||||
where
|
||||
A: CheckedAdd + PartialOrd + Clone + PartialEq,
|
||||
{
|
||||
type Item = A;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<A> {
|
||||
if !self.done && ((self.rev && self.state >= self.stop) ||
|
||||
(!self.rev && self.state <= self.stop)) {
|
||||
if !self.done
|
||||
&& ((self.rev && self.state >= self.stop) || (!self.rev && self.state <= self.stop))
|
||||
{
|
||||
let result = self.state.clone();
|
||||
match self.state.checked_add(&self.step) {
|
||||
Some(x) => self.state = x,
|
||||
None => self.done = true
|
||||
None => self.done = true,
|
||||
}
|
||||
Some(result)
|
||||
} else {
|
||||
|
@ -287,10 +315,10 @@ impl<A> Iterator for RangeStepInclusive<A>
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::{isize, usize};
|
||||
use core::ops::{Add, Mul};
|
||||
use core::cmp::Ordering;
|
||||
use core::iter;
|
||||
use core::ops::{Add, Mul};
|
||||
use core::{isize, usize};
|
||||
use traits::{One, ToPrimitive};
|
||||
|
||||
#[test]
|
||||
|
@ -299,8 +327,12 @@ mod tests {
|
|||
struct Foo;
|
||||
|
||||
impl ToPrimitive for Foo {
|
||||
fn to_i64(&self) -> Option<i64> { None }
|
||||
fn to_u64(&self) -> Option<u64> { None }
|
||||
fn to_i64(&self) -> Option<i64> {
|
||||
None
|
||||
}
|
||||
fn to_u64(&self) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Foo> for Foo {
|
||||
|
@ -343,12 +375,9 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
assert!(super::range(0, 5)
|
||||
.eq([0, 1, 2, 3, 4].iter().cloned()));
|
||||
assert!(super::range(-10, -1)
|
||||
.eq([-10, -9, -8, -7, -6, -5, -4, -3, -2].iter().cloned()));
|
||||
assert!(super::range(0, 5).rev()
|
||||
.eq([4, 3, 2, 1, 0].iter().cloned()));
|
||||
assert!(super::range(0, 5).eq([0, 1, 2, 3, 4].iter().cloned()));
|
||||
assert!(super::range(-10, -1).eq([-10, -9, -8, -7, -6, -5, -4, -3, -2].iter().cloned()));
|
||||
assert!(super::range(0, 5).rev().eq([4, 3, 2, 1, 0].iter().cloned()));
|
||||
assert_eq!(super::range(200, -5).count(), 0);
|
||||
assert_eq!(super::range(200, -5).rev().count(), 0);
|
||||
assert_eq!(super::range(200, 200).count(), 0);
|
||||
|
@ -356,9 +385,15 @@ mod tests {
|
|||
|
||||
assert_eq!(super::range(0, 100).size_hint(), (100, Some(100)));
|
||||
// this test is only meaningful when sizeof usize < sizeof u64
|
||||
assert_eq!(super::range(usize::MAX - 1, usize::MAX).size_hint(), (1, Some(1)));
|
||||
assert_eq!(
|
||||
super::range(usize::MAX - 1, usize::MAX).size_hint(),
|
||||
(1, Some(1))
|
||||
);
|
||||
assert_eq!(super::range(-10, -1).size_hint(), (9, Some(9)));
|
||||
assert_eq!(super::range(isize::MIN, isize::MAX).size_hint(), (usize::MAX, Some(usize::MAX)));
|
||||
assert_eq!(
|
||||
super::range(isize::MIN, isize::MAX).size_hint(),
|
||||
(usize::MAX, Some(usize::MAX))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -366,39 +401,58 @@ mod tests {
|
|||
fn test_range_128() {
|
||||
use core::{i128, u128};
|
||||
|
||||
assert!(super::range(0i128, 5)
|
||||
.eq([0, 1, 2, 3, 4].iter().cloned()));
|
||||
assert!(super::range(-10i128, -1)
|
||||
.eq([-10, -9, -8, -7, -6, -5, -4, -3, -2].iter().cloned()));
|
||||
assert!(super::range(0u128, 5).rev()
|
||||
assert!(super::range(0i128, 5).eq([0, 1, 2, 3, 4].iter().cloned()));
|
||||
assert!(super::range(-10i128, -1).eq([-10, -9, -8, -7, -6, -5, -4, -3, -2].iter().cloned()));
|
||||
assert!(super::range(0u128, 5)
|
||||
.rev()
|
||||
.eq([4, 3, 2, 1, 0].iter().cloned()));
|
||||
|
||||
assert_eq!(super::range(i128::MIN, i128::MIN+1).size_hint(), (1, Some(1)));
|
||||
assert_eq!(super::range(i128::MAX - 1, i128::MAX).size_hint(), (1, Some(1)));
|
||||
assert_eq!(super::range(i128::MIN, i128::MAX).size_hint(), (usize::MAX, None));
|
||||
assert_eq!(
|
||||
super::range(i128::MIN, i128::MIN + 1).size_hint(),
|
||||
(1, Some(1))
|
||||
);
|
||||
assert_eq!(
|
||||
super::range(i128::MAX - 1, i128::MAX).size_hint(),
|
||||
(1, Some(1))
|
||||
);
|
||||
assert_eq!(
|
||||
super::range(i128::MIN, i128::MAX).size_hint(),
|
||||
(usize::MAX, None)
|
||||
);
|
||||
|
||||
assert_eq!(super::range(u128::MAX - 1, u128::MAX).size_hint(), (1, Some(1)));
|
||||
assert_eq!(super::range(0, usize::MAX as u128).size_hint(), (usize::MAX, Some(usize::MAX)));
|
||||
assert_eq!(super::range(0, usize::MAX as u128 + 1).size_hint(), (usize::MAX, None));
|
||||
assert_eq!(
|
||||
super::range(u128::MAX - 1, u128::MAX).size_hint(),
|
||||
(1, Some(1))
|
||||
);
|
||||
assert_eq!(
|
||||
super::range(0, usize::MAX as u128).size_hint(),
|
||||
(usize::MAX, Some(usize::MAX))
|
||||
);
|
||||
assert_eq!(
|
||||
super::range(0, usize::MAX as u128 + 1).size_hint(),
|
||||
(usize::MAX, None)
|
||||
);
|
||||
assert_eq!(super::range(0, i128::MAX).size_hint(), (usize::MAX, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_inclusive() {
|
||||
assert!(super::range_inclusive(0, 5).eq([0, 1, 2, 3, 4, 5].iter().cloned()));
|
||||
assert!(super::range_inclusive(0, 5)
|
||||
.eq([0, 1, 2, 3, 4, 5].iter().cloned()));
|
||||
assert!(super::range_inclusive(0, 5).rev()
|
||||
.rev()
|
||||
.eq([5, 4, 3, 2, 1, 0].iter().cloned()));
|
||||
assert_eq!(super::range_inclusive(200, -5).count(), 0);
|
||||
assert_eq!(super::range_inclusive(200, -5).rev().count(), 0);
|
||||
assert!(super::range_inclusive(200, 200)
|
||||
.eq(iter::once(200)));
|
||||
assert!(super::range_inclusive(200, 200).rev()
|
||||
.eq(iter::once(200)));
|
||||
assert_eq!(super::range_inclusive(isize::MIN, isize::MAX - 1).size_hint(),
|
||||
(usize::MAX, Some(usize::MAX)));
|
||||
assert_eq!(super::range_inclusive(isize::MIN, isize::MAX).size_hint(),
|
||||
(usize::MAX, None));
|
||||
assert!(super::range_inclusive(200, 200).eq(iter::once(200)));
|
||||
assert!(super::range_inclusive(200, 200).rev().eq(iter::once(200)));
|
||||
assert_eq!(
|
||||
super::range_inclusive(isize::MIN, isize::MAX - 1).size_hint(),
|
||||
(usize::MAX, Some(usize::MAX))
|
||||
);
|
||||
assert_eq!(
|
||||
super::range_inclusive(isize::MIN, isize::MAX).size_hint(),
|
||||
(usize::MAX, None)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -406,40 +460,42 @@ mod tests {
|
|||
fn test_range_inclusive_128() {
|
||||
use core::i128;
|
||||
|
||||
assert!(super::range_inclusive(0u128, 5).eq([0, 1, 2, 3, 4, 5].iter().cloned()));
|
||||
assert!(super::range_inclusive(0u128, 5)
|
||||
.eq([0, 1, 2, 3, 4, 5].iter().cloned()));
|
||||
assert!(super::range_inclusive(0u128, 5).rev()
|
||||
.rev()
|
||||
.eq([5, 4, 3, 2, 1, 0].iter().cloned()));
|
||||
assert_eq!(super::range_inclusive(200i128, -5).count(), 0);
|
||||
assert_eq!(super::range_inclusive(200i128, -5).rev().count(), 0);
|
||||
assert!(super::range_inclusive(200u128, 200).eq(iter::once(200)));
|
||||
assert!(super::range_inclusive(200u128, 200)
|
||||
.rev()
|
||||
.eq(iter::once(200)));
|
||||
assert!(super::range_inclusive(200u128, 200).rev()
|
||||
.eq(iter::once(200)));
|
||||
assert_eq!(super::range_inclusive(isize::MIN as i128, isize::MAX as i128 - 1).size_hint(),
|
||||
(usize::MAX, Some(usize::MAX)));
|
||||
assert_eq!(super::range_inclusive(isize::MIN as i128, isize::MAX as i128).size_hint(),
|
||||
(usize::MAX, None));
|
||||
assert_eq!(super::range_inclusive(isize::MIN as i128, isize::MAX as i128 + 1).size_hint(),
|
||||
(usize::MAX, None));
|
||||
assert_eq!(super::range_inclusive(i128::MIN, i128::MAX).size_hint(),
|
||||
(usize::MAX, None));
|
||||
assert_eq!(
|
||||
super::range_inclusive(isize::MIN as i128, isize::MAX as i128 - 1).size_hint(),
|
||||
(usize::MAX, Some(usize::MAX))
|
||||
);
|
||||
assert_eq!(
|
||||
super::range_inclusive(isize::MIN as i128, isize::MAX as i128).size_hint(),
|
||||
(usize::MAX, None)
|
||||
);
|
||||
assert_eq!(
|
||||
super::range_inclusive(isize::MIN as i128, isize::MAX as i128 + 1).size_hint(),
|
||||
(usize::MAX, None)
|
||||
);
|
||||
assert_eq!(
|
||||
super::range_inclusive(i128::MIN, i128::MAX).size_hint(),
|
||||
(usize::MAX, None)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_step() {
|
||||
assert!(super::range_step(0, 20, 5)
|
||||
.eq([0, 5, 10, 15].iter().cloned()));
|
||||
assert!(super::range_step(20, 0, -5)
|
||||
.eq([20, 15, 10, 5].iter().cloned()));
|
||||
assert!(super::range_step(20, 0, -6)
|
||||
.eq([20, 14, 8, 2].iter().cloned()));
|
||||
assert!(super::range_step(200u8, 255, 50)
|
||||
.eq([200u8, 250].iter().cloned()));
|
||||
assert!(super::range_step(200, -5, 1)
|
||||
.eq(iter::empty()));
|
||||
assert!(super::range_step(200, 200, 1)
|
||||
.eq(iter::empty()));
|
||||
assert!(super::range_step(0, 20, 5).eq([0, 5, 10, 15].iter().cloned()));
|
||||
assert!(super::range_step(20, 0, -5).eq([20, 15, 10, 5].iter().cloned()));
|
||||
assert!(super::range_step(20, 0, -6).eq([20, 14, 8, 2].iter().cloned()));
|
||||
assert!(super::range_step(200u8, 255, 50).eq([200u8, 250].iter().cloned()));
|
||||
assert!(super::range_step(200, -5, 1).eq(iter::empty()));
|
||||
assert!(super::range_step(200, 200, 1).eq(iter::empty()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -447,34 +503,22 @@ mod tests {
|
|||
fn test_range_step_128() {
|
||||
use core::u128::MAX as UMAX;
|
||||
|
||||
assert!(super::range_step(0u128, 20, 5)
|
||||
.eq([0, 5, 10, 15].iter().cloned()));
|
||||
assert!(super::range_step(20i128, 0, -5)
|
||||
.eq([20, 15, 10, 5].iter().cloned()));
|
||||
assert!(super::range_step(20i128, 0, -6)
|
||||
.eq([20, 14, 8, 2].iter().cloned()));
|
||||
assert!(super::range_step(UMAX - 55, UMAX, 50)
|
||||
.eq([UMAX - 55, UMAX - 5].iter().cloned()));
|
||||
assert!(super::range_step(200i128, -5, 1)
|
||||
.eq(iter::empty()));
|
||||
assert!(super::range_step(200i128, 200, 1)
|
||||
.eq(iter::empty()));
|
||||
assert!(super::range_step(0u128, 20, 5).eq([0, 5, 10, 15].iter().cloned()));
|
||||
assert!(super::range_step(20i128, 0, -5).eq([20, 15, 10, 5].iter().cloned()));
|
||||
assert!(super::range_step(20i128, 0, -6).eq([20, 14, 8, 2].iter().cloned()));
|
||||
assert!(super::range_step(UMAX - 55, UMAX, 50).eq([UMAX - 55, UMAX - 5].iter().cloned()));
|
||||
assert!(super::range_step(200i128, -5, 1).eq(iter::empty()));
|
||||
assert!(super::range_step(200i128, 200, 1).eq(iter::empty()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_step_inclusive() {
|
||||
assert!(super::range_step_inclusive(0, 20, 5)
|
||||
.eq([0, 5, 10, 15, 20].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(20, 0, -5)
|
||||
.eq([20, 15, 10, 5, 0].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(20, 0, -6)
|
||||
.eq([20, 14, 8, 2].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(200u8, 255, 50)
|
||||
.eq([200u8, 250].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(200, -5, 1)
|
||||
.eq(iter::empty()));
|
||||
assert!(super::range_step_inclusive(200, 200, 1)
|
||||
.eq(iter::once(200)));
|
||||
assert!(super::range_step_inclusive(0, 20, 5).eq([0, 5, 10, 15, 20].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(20, 0, -5).eq([20, 15, 10, 5, 0].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(20, 0, -6).eq([20, 14, 8, 2].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(200u8, 255, 50).eq([200u8, 250].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(200, -5, 1).eq(iter::empty()));
|
||||
assert!(super::range_step_inclusive(200, 200, 1).eq(iter::once(200)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -482,17 +526,12 @@ mod tests {
|
|||
fn test_range_step_inclusive_128() {
|
||||
use core::u128::MAX as UMAX;
|
||||
|
||||
assert!(super::range_step_inclusive(0u128, 20, 5)
|
||||
.eq([0, 5, 10, 15, 20].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(20i128, 0, -5)
|
||||
.eq([20, 15, 10, 5, 0].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(20i128, 0, -6)
|
||||
.eq([20, 14, 8, 2].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(0u128, 20, 5).eq([0, 5, 10, 15, 20].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(20i128, 0, -5).eq([20, 15, 10, 5, 0].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(20i128, 0, -6).eq([20, 14, 8, 2].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(UMAX - 55, UMAX, 50)
|
||||
.eq([UMAX - 55, UMAX - 5].iter().cloned()));
|
||||
assert!(super::range_step_inclusive(200i128, -5, 1)
|
||||
.eq(iter::empty()));
|
||||
assert!(super::range_step_inclusive(200i128, 200, 1)
|
||||
.eq(iter::once(200)));
|
||||
assert!(super::range_step_inclusive(200i128, -5, 1).eq(iter::empty()));
|
||||
assert!(super::range_step_inclusive(200i128, 200, 1).eq(iter::once(200)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ description = "Shared Rust code for libxul"
|
|||
geckoservo = { path = "../../../../servo/ports/geckolib" }
|
||||
kvstore = { path = "../../../components/kvstore" }
|
||||
lmdb-rkv-sys = { version = "0.11", features = ["mdb_idl_logn_9"] }
|
||||
mp4parse_capi = { git = "https://github.com/mozilla/mp4parse-rust", rev = "63325444ae3388599f2f222775eebdde4c2f9f30" }
|
||||
mp4parse_capi = { git = "https://github.com/mozilla/mp4parse-rust", rev = "d5a37fd0bd51e06a53274c68213b00136aba83a6" }
|
||||
nserror = { path = "../../../../xpcom/rust/nserror" }
|
||||
nsstring = { path = "../../../../xpcom/rust/nsstring" }
|
||||
netwerk_helper = { path = "../../../../netwerk/base/rust-helper" }
|
||||
|
|
Загрузка…
Ссылка в новой задаче