| { | ||
| "git": { | ||
| "sha1": "6975267434af46fc4ec70fc7c7459a2bc74c2699" | ||
| } | ||
| } | ||
| "sha1": "c83c90c05314e7334a96f34abaca4bf7f0ab1bf3" | ||
| }, | ||
| "path_in_vcs": "utils/writeable" | ||
| } |
@@ -21,3 +21,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| fn write_len(&self) -> LengthHint { | ||
| LengthHint::Exact(self.message.len()) | ||
| LengthHint::exact(self.message.len()) | ||
| } | ||
@@ -77,2 +77,3 @@ } | ||
| .writeable_to_string() | ||
| .into_owned() | ||
| }); | ||
@@ -86,2 +87,3 @@ }); | ||
| .writeable_to_string() | ||
| .into_owned() | ||
| }); | ||
@@ -95,2 +97,3 @@ }); | ||
| .writeable_to_string() | ||
| .into_owned() | ||
| }); | ||
@@ -97,0 +100,0 @@ }); |
+121
-51
@@ -42,5 +42,5 @@ # This file is automatically @generated by Cargo. | ||
| name = "bumpalo" | ||
| version = "3.8.0" | ||
| version = "3.9.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" | ||
| checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" | ||
@@ -64,5 +64,5 @@ [[package]] | ||
| name = "clap" | ||
| version = "2.33.3" | ||
| version = "2.34.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" | ||
| checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" | ||
| dependencies = [ | ||
@@ -112,5 +112,5 @@ "bitflags", | ||
| name = "crossbeam-channel" | ||
| version = "0.5.1" | ||
| version = "0.5.2" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" | ||
| checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" | ||
| dependencies = [ | ||
@@ -134,5 +134,5 @@ "cfg-if", | ||
| name = "crossbeam-epoch" | ||
| version = "0.9.5" | ||
| version = "0.9.6" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" | ||
| checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" | ||
| dependencies = [ | ||
@@ -148,5 +148,5 @@ "cfg-if", | ||
| name = "crossbeam-utils" | ||
| version = "0.8.5" | ||
| version = "0.8.6" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" | ||
| checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" | ||
| dependencies = [ | ||
@@ -165,3 +165,3 @@ "cfg-if", | ||
| "csv-core", | ||
| "itoa", | ||
| "itoa 0.4.8", | ||
| "ryu", | ||
@@ -187,2 +187,13 @@ "serde", | ||
| [[package]] | ||
| name = "getrandom" | ||
| version = "0.2.4" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" | ||
| dependencies = [ | ||
| "cfg-if", | ||
| "libc", | ||
| "wasi", | ||
| ] | ||
| [[package]] | ||
| name = "half" | ||
@@ -204,11 +215,11 @@ version = "1.8.2" | ||
| name = "icu_benchmark_macros" | ||
| version = "0.3.0" | ||
| version = "0.4.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "f8b52b58b602655250d6d5f407835bcfe9b9e1a1d22bbc59b11f0c8056a55b0f" | ||
| checksum = "ed07c547df407bdec6dd7a2c498281f786c2dcebfcd503fe8269745eb13acc19" | ||
| [[package]] | ||
| name = "itertools" | ||
| version = "0.10.1" | ||
| version = "0.10.3" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" | ||
| checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" | ||
| dependencies = [ | ||
@@ -225,6 +236,12 @@ "either", | ||
| [[package]] | ||
| name = "itoa" | ||
| version = "1.0.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" | ||
| [[package]] | ||
| name = "js-sys" | ||
| version = "0.3.55" | ||
| version = "0.3.56" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" | ||
| checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" | ||
| dependencies = [ | ||
@@ -242,5 +259,5 @@ "wasm-bindgen", | ||
| name = "libc" | ||
| version = "0.2.106" | ||
| version = "0.2.116" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673" | ||
| checksum = "565dbd88872dbe4cc8a46e527f26483c1d1f7afa6b884a3bd6cd893d4f98da74" | ||
@@ -264,5 +281,5 @@ [[package]] | ||
| name = "memoffset" | ||
| version = "0.6.4" | ||
| version = "0.6.5" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" | ||
| checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" | ||
| dependencies = [ | ||
@@ -283,5 +300,5 @@ "autocfg", | ||
| name = "num_cpus" | ||
| version = "1.13.0" | ||
| version = "1.13.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" | ||
| checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" | ||
| dependencies = [ | ||
@@ -327,6 +344,12 @@ "hermit-abi", | ||
| [[package]] | ||
| name = "ppv-lite86" | ||
| version = "0.2.16" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" | ||
| [[package]] | ||
| name = "proc-macro2" | ||
| version = "1.0.32" | ||
| version = "1.0.36" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" | ||
| checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" | ||
| dependencies = [ | ||
@@ -338,5 +361,5 @@ "unicode-xid", | ||
| name = "quote" | ||
| version = "1.0.10" | ||
| version = "1.0.15" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" | ||
| checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" | ||
| dependencies = [ | ||
@@ -347,2 +370,42 @@ "proc-macro2", | ||
| [[package]] | ||
| name = "rand" | ||
| version = "0.8.4" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" | ||
| dependencies = [ | ||
| "libc", | ||
| "rand_chacha", | ||
| "rand_core", | ||
| "rand_hc", | ||
| ] | ||
| [[package]] | ||
| name = "rand_chacha" | ||
| version = "0.3.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" | ||
| dependencies = [ | ||
| "ppv-lite86", | ||
| "rand_core", | ||
| ] | ||
| [[package]] | ||
| name = "rand_core" | ||
| version = "0.6.3" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" | ||
| dependencies = [ | ||
| "getrandom", | ||
| ] | ||
| [[package]] | ||
| name = "rand_hc" | ||
| version = "0.3.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" | ||
| dependencies = [ | ||
| "rand_core", | ||
| ] | ||
| [[package]] | ||
| name = "rayon" | ||
@@ -404,5 +467,5 @@ version = "1.5.1" | ||
| name = "ryu" | ||
| version = "1.0.5" | ||
| version = "1.0.9" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" | ||
| checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" | ||
@@ -432,5 +495,5 @@ [[package]] | ||
| name = "serde" | ||
| version = "1.0.130" | ||
| version = "1.0.136" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" | ||
| checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" | ||
@@ -449,5 +512,5 @@ [[package]] | ||
| name = "serde_derive" | ||
| version = "1.0.130" | ||
| version = "1.0.136" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" | ||
| checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" | ||
| dependencies = [ | ||
@@ -461,7 +524,7 @@ "proc-macro2", | ||
| name = "serde_json" | ||
| version = "1.0.68" | ||
| version = "1.0.78" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" | ||
| checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085" | ||
| dependencies = [ | ||
| "itoa", | ||
| "itoa 1.0.1", | ||
| "ryu", | ||
@@ -473,5 +536,5 @@ "serde", | ||
| name = "syn" | ||
| version = "1.0.81" | ||
| version = "1.0.86" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" | ||
| checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" | ||
| dependencies = [ | ||
@@ -526,6 +589,12 @@ "proc-macro2", | ||
| [[package]] | ||
| name = "wasi" | ||
| version = "0.10.2+wasi-snapshot-preview1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" | ||
| [[package]] | ||
| name = "wasm-bindgen" | ||
| version = "0.2.78" | ||
| version = "0.2.79" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" | ||
| checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" | ||
| dependencies = [ | ||
@@ -538,5 +607,5 @@ "cfg-if", | ||
| name = "wasm-bindgen-backend" | ||
| version = "0.2.78" | ||
| version = "0.2.79" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" | ||
| checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" | ||
| dependencies = [ | ||
@@ -554,5 +623,5 @@ "bumpalo", | ||
| name = "wasm-bindgen-macro" | ||
| version = "0.2.78" | ||
| version = "0.2.79" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" | ||
| checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" | ||
| dependencies = [ | ||
@@ -565,5 +634,5 @@ "quote", | ||
| name = "wasm-bindgen-macro-support" | ||
| version = "0.2.78" | ||
| version = "0.2.79" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" | ||
| checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" | ||
| dependencies = [ | ||
@@ -579,11 +648,11 @@ "proc-macro2", | ||
| name = "wasm-bindgen-shared" | ||
| version = "0.2.78" | ||
| version = "0.2.79" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" | ||
| checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" | ||
| [[package]] | ||
| name = "web-sys" | ||
| version = "0.3.55" | ||
| version = "0.3.56" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" | ||
| checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" | ||
| dependencies = [ | ||
@@ -627,6 +696,7 @@ "js-sys", | ||
| name = "writeable" | ||
| version = "0.2.1" | ||
| version = "0.3.0" | ||
| dependencies = [ | ||
| "criterion", | ||
| "icu_benchmark_macros", | ||
| "rand", | ||
| ] |
+6
-2
@@ -15,3 +15,3 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO | ||
| name = "writeable" | ||
| version = "0.2.1" | ||
| version = "0.3.0" | ||
| authors = ["The ICU4X Project Developers"] | ||
@@ -38,6 +38,10 @@ include = ["src/**/*", "examples/**/*", "benches/**/*", "tests/**/*", "Cargo.toml", "LICENSE", "README.md"] | ||
| [dev-dependencies.icu_benchmark_macros] | ||
| version = "0.3" | ||
| version = "0.4" | ||
| [dev-dependencies.rand] | ||
| version = "0.8" | ||
| features = ["small_rng"] | ||
| [features] | ||
| bench = [] | ||
| default = [] |
@@ -9,16 +9,30 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use std::fmt; | ||
| use writeable::LengthHint; | ||
| use writeable::Writeable; | ||
| use writeable::*; | ||
| struct WriteableMessage<'s> { | ||
| message: &'s str, | ||
| } | ||
| struct WriteableMessage<W: Writeable>(W); | ||
| impl Writeable for WriteableMessage<'_> { | ||
| fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { | ||
| sink.write_str(self.message) | ||
| const GREETING: Part = Part { | ||
| category: "meaning", | ||
| value: "greeting", | ||
| }; | ||
| const EMOJI: Part = Part { | ||
| category: "meaning", | ||
| value: "emoji", | ||
| }; | ||
| impl<V: Writeable> Writeable for WriteableMessage<V> { | ||
| fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result { | ||
| use fmt::Write; | ||
| sink.with_part(GREETING, |g| { | ||
| g.write_str("Hello")?; | ||
| g.write_str(" ")?; | ||
| self.0.write_to(g) | ||
| })?; | ||
| sink.write_char(' ')?; | ||
| sink.with_part(EMOJI, |e| e.write_char('😅')) | ||
| } | ||
| fn write_len(&self) -> LengthHint { | ||
| LengthHint::Exact(self.message.len()) | ||
| LengthHint::exact(11) + self.0.write_len() | ||
| } | ||
@@ -30,6 +44,12 @@ } | ||
| let writeable = WriteableMessage { | ||
| message: "hello world", | ||
| }; | ||
| assert_eq!("hello world", writeable.writeable_to_string()); | ||
| let (string, parts) = writeable_to_parts_for_test(&WriteableMessage("world")).unwrap(); | ||
| assert_eq!(string, "Hello world 😅"); | ||
| // Print the greeting only | ||
| let (start, end, _) = parts | ||
| .into_iter() | ||
| .find(|(_, _, part)| part == &GREETING) | ||
| .unwrap(); | ||
| println!("{}", &string[start..end]); | ||
| } |
+2
-2
@@ -40,3 +40,3 @@ # writeable [](https://crates.io/crates/writeable) | ||
| // "Hello, " + '!' + length of name | ||
| LengthHint::Exact(8 + self.name.len()) | ||
| LengthHint::exact(8 + self.name.len()) | ||
| } | ||
@@ -46,3 +46,3 @@ } | ||
| let message = WelcomeMessage { name: "Alice" }; | ||
| assert_writeable_eq!("Hello, Alice!", &message); | ||
| assert_writeable_eq!(&message, "Hello, Alice!"); | ||
| ``` | ||
@@ -49,0 +49,0 @@ |
+186
-66
@@ -5,88 +5,208 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use crate::LengthHint; | ||
| use crate::Writeable; | ||
| use core::convert::TryFrom; | ||
| use crate::*; | ||
| use alloc::borrow::Cow; | ||
| use core::fmt; | ||
| use core::str; | ||
| impl Writeable for u8 { | ||
| fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { | ||
| let mut buf = [b'0'; 3]; | ||
| let mut n = *self; | ||
| let mut i = 3usize; | ||
| while n != 0 { | ||
| i -= 1; | ||
| buf[i] = b'0' + (n % 10); | ||
| n /= 10; | ||
| macro_rules! impl_write_num { | ||
| ($u:ty, $i:ty, $test:ident, $log10:ident) => { | ||
| impl $crate::Writeable for $u { | ||
| fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result { | ||
| let mut buf = [b'0'; $log10(<$u>::MAX) as usize + 1]; | ||
| let mut n = *self; | ||
| let mut i = buf.len(); | ||
| while n != 0 { | ||
| i -= 1; | ||
| buf[i] = b'0' + (n % 10) as u8; | ||
| n /= 10; | ||
| } | ||
| if i == buf.len() { | ||
| debug_assert_eq!(*self, 0); | ||
| i -= 1; | ||
| } | ||
| let s = unsafe { core::str::from_utf8_unchecked(&buf[i..]) }; | ||
| sink.write_str(s) | ||
| } | ||
| fn write_len(&self) -> $crate::LengthHint { | ||
| $crate::LengthHint::exact(if *self == 0 { | ||
| 1 | ||
| } else { | ||
| $log10(*self) as usize + 1 | ||
| }) | ||
| } | ||
| } | ||
| if i == 3 { | ||
| debug_assert_eq!(*self, 0); | ||
| i = 2; | ||
| // TODO: use the library functions once stabilized. | ||
| // https://github.com/unicode-org/icu4x/issues/1428 | ||
| #[inline] | ||
| const fn $log10(s: $u) -> u32 { | ||
| let b = (<$u>::BITS - 1) - s.leading_zeros(); | ||
| // s ∈ [2ᵇ, 2ᵇ⁺¹-1] => ⌊log₁₀(s)⌋ ∈ [⌊log₁₀(2ᵇ)⌋, ⌊log₁₀(2ᵇ⁺¹-1)⌋] | ||
| // <=> ⌊log₁₀(s)⌋ ∈ [⌊log₁₀(2ᵇ)⌋, ⌊log₁₀(2ᵇ⁺¹)⌋] | ||
| // <=> ⌊log₁₀(s)⌋ ∈ [⌊b log₁₀(2)⌋, ⌊(b+1) log₁₀(2)⌋] | ||
| // The second line holds because there is no integer in | ||
| // [log₁₀(2ᶜ-1), log₁₀(2ᶜ)], if there were, there'd be some 10ⁿ in | ||
| // [2ᶜ-1, 2ᶜ], but it can't be 2ᶜ-1 due to parity nor 2ᶜ due to prime | ||
| // factors. | ||
| const M: u32 = (core::f64::consts::LOG10_2 * (1 << 26) as f64) as u32; | ||
| let low = (b * M) >> 26; | ||
| let high = ((b + 1) * M) >> 26; | ||
| // If the bounds aren't tight (e.g. 87 ∈ [64, 127] ⟹ ⌊log₁₀(87)⌋ ∈ [1,2]), | ||
| // compare to 10ʰ (100). This shouldn't happen too often as there are more | ||
| // powers of 2 than 10 (it happens for 14% of u32s). | ||
| if high == low { | ||
| low | ||
| } else if s < (10 as $u).pow(high) { | ||
| low | ||
| } else { | ||
| high | ||
| } | ||
| } | ||
| let s = unsafe { str::from_utf8_unchecked(&buf[i..]) }; | ||
| sink.write_str(s) | ||
| impl $crate::Writeable for $i { | ||
| fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result { | ||
| if self.is_negative() { | ||
| sink.write_str("-")?; | ||
| } | ||
| self.unsigned_abs().write_to(sink) | ||
| } | ||
| fn write_len(&self) -> $crate::LengthHint { | ||
| $crate::LengthHint::exact(if self.is_negative() { 1 } else { 0 }) | ||
| + self.unsigned_abs().write_len() | ||
| } | ||
| } | ||
| #[test] | ||
| fn $test() { | ||
| use $crate::assert_writeable_eq; | ||
| assert_writeable_eq!(&(0 as $u), "0"); | ||
| assert_writeable_eq!(&(0 as $u), "0"); | ||
| assert_writeable_eq!(&(-0 as $i), "0"); | ||
| assert_writeable_eq!(&(1 as $u), "1"); | ||
| assert_writeable_eq!(&(1 as $i), "1"); | ||
| assert_writeable_eq!(&(-1 as $i), "-1"); | ||
| assert_writeable_eq!(&(10 as $u), "10"); | ||
| assert_writeable_eq!(&(10 as $i), "10"); | ||
| assert_writeable_eq!(&(-10 as $i), "-10"); | ||
| assert_writeable_eq!(&(99 as $u), "99"); | ||
| assert_writeable_eq!(&(99 as $i), "99"); | ||
| assert_writeable_eq!(&(-99 as $i), "-99"); | ||
| assert_writeable_eq!(&(100 as $u), "100"); | ||
| assert_writeable_eq!(&(-100 as $i), "-100"); | ||
| assert_writeable_eq!(&<$u>::MAX, <$u>::MAX.to_string()); | ||
| assert_writeable_eq!(&<$i>::MAX, <$i>::MAX.to_string()); | ||
| assert_writeable_eq!(&<$i>::MIN, <$i>::MIN.to_string()); | ||
| use rand::{rngs::SmallRng, Rng, SeedableRng}; | ||
| let mut rng = SmallRng::seed_from_u64(4); // chosen by fair dice roll. | ||
| // guaranteed to be random. | ||
| for _ in 0..1000 { | ||
| let rand = rng.gen::<$u>(); | ||
| assert_writeable_eq!(rand, rand.to_string()); | ||
| } | ||
| } | ||
| }; | ||
| } | ||
| impl_write_num!(u8, i8, test_u8, log10_u8); | ||
| impl_write_num!(u16, i16, test_u16, log10_u16); | ||
| impl_write_num!(u32, i32, test_u32, log10_u32); | ||
| impl_write_num!(u64, i64, test_u64, log10_u64); | ||
| impl_write_num!(u128, i128, test_u128, log10_u128); | ||
| #[test] | ||
| fn assert_log10_approximation() { | ||
| for i in 1..u128::BITS { | ||
| assert_eq!(i * 59 / 196, 2f64.powf(i.into()).log10().floor() as u32); | ||
| } | ||
| } | ||
| impl Writeable for str { | ||
| #[inline] | ||
| fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { | ||
| sink.write_str(self) | ||
| } | ||
| #[inline] | ||
| fn write_len(&self) -> LengthHint { | ||
| if *self < 10 { | ||
| LengthHint::Exact(1) | ||
| } else if *self < 100 { | ||
| LengthHint::Exact(2) | ||
| } else { | ||
| LengthHint::Exact(3) | ||
| } | ||
| LengthHint::exact(self.len()) | ||
| } | ||
| /// Returns a borrowed `str`. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use writeable::Writeable; | ||
| /// use std::borrow::Cow; | ||
| /// | ||
| /// let cow = "foo".writeable_to_string(); | ||
| /// assert!(matches!(cow, Cow::Borrowed(_))); | ||
| /// ``` | ||
| #[inline] | ||
| fn writeable_to_string(&self) -> Cow<str> { | ||
| Cow::Borrowed(self) | ||
| } | ||
| } | ||
| impl Writeable for u16 { | ||
| impl Writeable for String { | ||
| #[inline] | ||
| fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { | ||
| let mut buf = [b'0'; 5]; | ||
| let mut n = *self; | ||
| let mut i = 5usize; | ||
| while n != 0 { | ||
| i -= 1; | ||
| buf[i] = b'0' + u8::try_from(n % 10).expect("<10"); | ||
| n /= 10; | ||
| } | ||
| if i == 5 { | ||
| debug_assert_eq!(*self, 0); | ||
| i = 4; | ||
| } | ||
| let s = unsafe { str::from_utf8_unchecked(&buf[i..]) }; | ||
| sink.write_str(s) | ||
| sink.write_str(self) | ||
| } | ||
| #[inline] | ||
| fn write_len(&self) -> LengthHint { | ||
| if *self < 10 { | ||
| LengthHint::Exact(1) | ||
| } else if *self < 100 { | ||
| LengthHint::Exact(2) | ||
| } else if *self < 1000 { | ||
| LengthHint::Exact(3) | ||
| } else if *self < 10000 { | ||
| LengthHint::Exact(4) | ||
| } else { | ||
| LengthHint::Exact(5) | ||
| } | ||
| LengthHint::exact(self.len()) | ||
| } | ||
| #[inline] | ||
| fn writeable_to_string(&self) -> Cow<str> { | ||
| Cow::Borrowed(self) | ||
| } | ||
| } | ||
| #[test] | ||
| fn test_u8() { | ||
| use crate::assert_writeable_eq; | ||
| assert_writeable_eq!("0", &0u8); | ||
| assert_writeable_eq!("1", &1u8); | ||
| assert_writeable_eq!("10", &10u8); | ||
| assert_writeable_eq!("99", &99u8); | ||
| assert_writeable_eq!("255", &255u8); | ||
| impl<'a, T: Writeable + ?Sized> Writeable for &T { | ||
| #[inline] | ||
| fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { | ||
| (*self).write_to(sink) | ||
| } | ||
| #[inline] | ||
| fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result { | ||
| (*self).write_to_parts(sink) | ||
| } | ||
| #[inline] | ||
| fn write_len(&self) -> LengthHint { | ||
| (*self).write_len() | ||
| } | ||
| #[inline] | ||
| fn writeable_to_string(&self) -> Cow<str> { | ||
| (*self).writeable_to_string() | ||
| } | ||
| } | ||
| #[test] | ||
| fn test_u16() { | ||
| use crate::assert_writeable_eq; | ||
| assert_writeable_eq!("0", &0u16); | ||
| assert_writeable_eq!("1", &1u16); | ||
| assert_writeable_eq!("10", &10u16); | ||
| assert_writeable_eq!("99", &99u16); | ||
| assert_writeable_eq!("65535", &65535u16); | ||
| fn test_string_impls() { | ||
| fn check_writeable_slice<W: Writeable>(writeables: &[W]) { | ||
| assert_writeable_eq!(&writeables[0], ""); | ||
| assert_writeable_eq!(&writeables[1], "abc"); | ||
| } | ||
| // test str impl | ||
| let arr: &[&str] = &["", "abc"]; | ||
| check_writeable_slice(arr); | ||
| // test String impl | ||
| let arr: &[String] = &["".to_string(), "abc".to_string()]; | ||
| check_writeable_slice(arr); | ||
| // test &T impl | ||
| let arr: &[&String] = &[&"".to_string(), &"abc".to_string()]; | ||
| check_writeable_slice(arr); | ||
| } |
+226
-51
@@ -44,3 +44,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| //! // "Hello, " + '!' + length of name | ||
| //! LengthHint::Exact(8 + self.name.len()) | ||
| //! LengthHint::exact(8 + self.name.len()) | ||
| //! } | ||
@@ -50,3 +50,3 @@ //! } | ||
| //! let message = WelcomeMessage { name: "Alice" }; | ||
| //! assert_writeable_eq!("Hello, Alice!", &message); | ||
| //! assert_writeable_eq!(&message, "Hello, Alice!"); | ||
| //! ``` | ||
@@ -61,3 +61,5 @@ //! | ||
| use alloc::borrow::Cow; | ||
| use alloc::string::String; | ||
| use alloc::vec::Vec; | ||
| use core::fmt; | ||
@@ -67,16 +69,40 @@ | ||
| /// | ||
| /// LengthHint implements std::ops::Add and similar traits for easy composition. | ||
| /// This behaves like Iterator::size_hint: it is a tuple where the first element is the | ||
| /// lower bound, and the second element is the upper bound. If the upper bound is `None` | ||
| /// either there is no known upper bound, or the upper bound is larger than usize. | ||
| /// | ||
| /// See this issue for more info: <https://github.com/unicode-org/icu4x/issues/370>. | ||
| /// LengthHint implements std::ops::{Add, Mul} and similar traits for easy composition. | ||
| /// During computation, the lower bound will saturate at usize::MAX, while the upper | ||
| /// bound will become None if usize::MAX is exceeded. | ||
| #[derive(Debug, PartialEq, Eq, Copy, Clone)] | ||
| pub enum LengthHint { | ||
| /// Default value: no hint is provided. | ||
| Undefined, | ||
| pub struct LengthHint(pub usize, pub Option<usize>); | ||
| /// An exact length hint. This value is expected to equal the actual length from write_to. | ||
| Exact(usize), | ||
| } | ||
| impl LengthHint { | ||
| pub fn undefined() -> Self { | ||
| Self(0, None) | ||
| } | ||
| impl LengthHint { | ||
| /// This is the exact length from write_to. | ||
| pub fn exact(n: usize) -> Self { | ||
| Self(n, Some(n)) | ||
| } | ||
| /// This is at least the length from write_to. | ||
| pub fn at_least(n: usize) -> Self { | ||
| Self(n, None) | ||
| } | ||
| /// This is at most the length from write_to. | ||
| pub fn at_most(n: usize) -> Self { | ||
| Self(0, Some(n)) | ||
| } | ||
| /// The length from write_to is in between n and m. | ||
| pub fn between(n: usize, m: usize) -> Self { | ||
| Self(Ord::min(n, m), Some(Ord::max(n, m))) | ||
| } | ||
| /// Returns a recommendation for the number of bytes to pre-allocate. | ||
| /// If an upper bound exists, this is used, otherwise the lower bound | ||
| /// (which might be 0). | ||
| /// | ||
@@ -94,4 +120,4 @@ /// # Examples | ||
| match self { | ||
| Self::Undefined => 0, | ||
| Self::Exact(len) => *len, | ||
| Self(lower_bound, None) => *lower_bound, | ||
| Self(_lower_bound, Some(upper_bound)) => *upper_bound, | ||
| } | ||
@@ -102,14 +128,62 @@ } | ||
| pub fn is_zero(&self) -> bool { | ||
| match self { | ||
| Self::Undefined => false, | ||
| Self::Exact(len) => *len == 0, | ||
| } | ||
| self.1 == Some(0) | ||
| } | ||
| } | ||
| #[derive(Clone, Copy, Debug, PartialEq)] | ||
| pub struct Part { | ||
| pub category: &'static str, | ||
| pub value: &'static str, | ||
| } | ||
| /// A sink that supports annotating parts of the string with Parts. | ||
| pub trait PartsWrite: fmt::Write { | ||
| type SubPartsWrite: PartsWrite + ?Sized; | ||
| fn with_part( | ||
| &mut self, | ||
| part: Part, | ||
| f: impl FnMut(&mut Self::SubPartsWrite) -> fmt::Result, | ||
| ) -> fmt::Result; | ||
| } | ||
| /// Writeable is an alternative to std::fmt::Display with the addition of a length function. | ||
| pub trait Writeable { | ||
| /// Writes bytes to the given sink. Errors from the sink are bubbled up. | ||
| fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result; | ||
| /// The default implementation delegates to write_to_parts, and discards any | ||
| /// Part annotations. | ||
| fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { | ||
| struct CoreWriteAsPartsWrite<W: fmt::Write + ?Sized>(W); | ||
| impl<W: fmt::Write + ?Sized> fmt::Write for CoreWriteAsPartsWrite<W> { | ||
| fn write_str(&mut self, s: &str) -> fmt::Result { | ||
| self.0.write_str(s) | ||
| } | ||
| fn write_char(&mut self, c: char) -> fmt::Result { | ||
| self.0.write_char(c) | ||
| } | ||
| } | ||
| impl<W: fmt::Write + ?Sized> PartsWrite for CoreWriteAsPartsWrite<W> { | ||
| type SubPartsWrite = CoreWriteAsPartsWrite<W>; | ||
| fn with_part( | ||
| &mut self, | ||
| _part: Part, | ||
| mut f: impl FnMut(&mut Self::SubPartsWrite) -> fmt::Result, | ||
| ) -> fmt::Result { | ||
| f(self) | ||
| } | ||
| } | ||
| self.write_to_parts(&mut CoreWriteAsPartsWrite(sink)) | ||
| } | ||
| /// Write bytes and Part annotations to the given sink. Errors from the | ||
| /// sink are bubbled up. The default implementation delegates to write_to, | ||
| /// and doesn't produce any Part annotations. | ||
| fn write_to_parts<S: PartsWrite + ?Sized>(&self, sink: &mut S) -> fmt::Result { | ||
| self.write_to(sink) | ||
| } | ||
| /// Returns a hint for the number of bytes that will be written to the sink. | ||
@@ -119,3 +193,3 @@ /// | ||
| fn write_len(&self) -> LengthHint { | ||
| LengthHint::Undefined | ||
| LengthHint::undefined() | ||
| } | ||
@@ -126,16 +200,52 @@ | ||
| /// | ||
| /// Not intended to be overriden. | ||
| fn writeable_to_string(&self) -> String { | ||
| /// The default impl allocates an owned String. However, if it is possible to return a | ||
| /// borrowed string, overwrite this method to return a `Cow::Borrowed`. | ||
| /// | ||
| /// To remove the `Cow` wrapper, call `.into_owned()`. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// Inspect a `Writeable` before writing it to the sink: | ||
| /// | ||
| /// ``` | ||
| /// use writeable::Writeable; | ||
| /// use core::fmt::{Write, Result}; | ||
| /// | ||
| /// fn write_if_ascii<W, S>(w: &W, sink: &mut S) -> Result | ||
| /// where | ||
| /// W: Writeable + ?Sized, | ||
| /// S: Write + ?Sized, | ||
| /// { | ||
| /// let s = w.writeable_to_string(); | ||
| /// if s.is_ascii() { | ||
| /// sink.write_str(&s) | ||
| /// } else { | ||
| /// Ok(()) | ||
| /// } | ||
| /// } | ||
| /// ``` | ||
| /// | ||
| /// Convert the `Writeable` into a fully owned `String`: | ||
| /// | ||
| /// ``` | ||
| /// use writeable::Writeable; | ||
| /// | ||
| /// fn make_string(w: &impl Writeable) -> String { | ||
| /// w.writeable_to_string().into_owned() | ||
| /// } | ||
| /// ``` | ||
| fn writeable_to_string(&self) -> Cow<str> { | ||
| let mut output = String::with_capacity(self.write_len().capacity()); | ||
| self.write_to(&mut output) | ||
| .expect("impl Write for String is infallible"); | ||
| output | ||
| Cow::Owned(output) | ||
| } | ||
| } | ||
| /// Testing macro for types implementing Writeable. The first argument should be a string, and | ||
| /// the second argument should be a `&dyn Writeable`. | ||
| /// Testing macros for types implementing Writeable. The first argument should be a | ||
| /// `Writeable`, the second argument a string, and the third argument (*_parts_eq only) | ||
| /// a list of parts (`[(usize, usize, Part)]`). | ||
| /// | ||
| /// The macro tests for equality of both string content and string length. If your Writeable | ||
| /// implementation returns an inexact string length, don't use this macro. | ||
| /// The macros tests for equality of string content, parts (*_parts_eq only), and | ||
| /// verify the size hint. | ||
| /// | ||
@@ -145,43 +255,108 @@ /// # Examples | ||
| /// ``` | ||
| /// use writeable::Writeable; | ||
| /// use writeable::LengthHint; | ||
| /// use writeable::assert_writeable_eq; | ||
| /// use std::fmt; | ||
| /// # use writeable::Writeable; | ||
| /// # use writeable::LengthHint; | ||
| /// # use writeable::Part; | ||
| /// # use writeable::assert_writeable_eq; | ||
| /// # use writeable::assert_writeable_parts_eq; | ||
| /// # use std::fmt::{self, Write}; | ||
| /// | ||
| /// const WORD: Part = Part { category: "foo", value: "word" }; | ||
| /// | ||
| /// struct Demo; | ||
| /// impl Writeable for Demo { | ||
| /// fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { | ||
| /// sink.write_str("foo") | ||
| /// fn write_to_parts<S: writeable::PartsWrite + ?Sized>(&self, sink: &mut S) -> fmt::Result { | ||
| /// sink.with_part(WORD, |w| w.write_str("foo")) | ||
| /// } | ||
| /// fn write_len(&self) -> LengthHint { | ||
| /// LengthHint::Exact(3) | ||
| /// LengthHint::exact(3) | ||
| /// } | ||
| /// } | ||
| /// | ||
| /// assert_writeable_eq!("foo", &Demo); | ||
| /// assert_writeable_eq!("foo", &Demo, "Message: {}", "Hello World"); | ||
| /// assert_writeable_eq!(&Demo, "foo"); | ||
| /// assert_writeable_eq!(&Demo, "foo", "Message: {}", "Hello World"); | ||
| /// | ||
| /// assert_writeable_parts_eq!(&Demo, "foo", [(0, 3, WORD)]); | ||
| /// assert_writeable_parts_eq!(&Demo, "foo", [(0, 3, WORD)], "Message: {}", "Hello World"); | ||
| /// ``` | ||
| #[macro_export] | ||
| macro_rules! assert_writeable_eq { | ||
| ($expected_str:expr, $actual_writeable:expr $(,)?) => { | ||
| { | ||
| use $crate::Writeable; | ||
| let writeable = $actual_writeable; | ||
| assert_eq!($expected_str, writeable.writeable_to_string()); | ||
| if let $crate::LengthHint::Exact(len) = writeable.write_len() { | ||
| assert_eq!($expected_str.len(), len); | ||
| } | ||
| ($actual_writeable:expr, $expected_str:expr $(,)?) => { | ||
| $crate::assert_writeable_eq!($actual_writeable, $expected_str, ""); | ||
| }; | ||
| ($actual_writeable:expr, $expected_str:expr, $($arg:tt)+) => {{ | ||
| let actual_writeable = &$actual_writeable; | ||
| let (actual_str, _) = $crate::writeable_to_parts_for_test(actual_writeable).unwrap(); | ||
| assert_eq!(actual_str, $expected_str, $($arg)*); | ||
| assert_eq!(actual_str, $crate::Writeable::writeable_to_string(actual_writeable), $($arg)+); | ||
| let length_hint = $crate::Writeable::write_len(actual_writeable); | ||
| assert!(length_hint.0 <= actual_str.len(), $($arg)*); | ||
| if let Some(upper) = length_hint.1 { | ||
| assert!(actual_str.len() <= upper, $($arg)*); | ||
| } | ||
| }}; | ||
| } | ||
| #[macro_export] | ||
| macro_rules! assert_writeable_parts_eq { | ||
| ($actual_writeable:expr, $expected_str:expr, $expected_parts:expr $(,)?) => { | ||
| $crate::assert_writeable_parts_eq!($actual_writeable, $expected_str, $expected_parts, ""); | ||
| }; | ||
| ($actual_writeable:expr, $expected_str:expr, $expected_parts:expr, $($arg:tt)+) => {{ | ||
| let actual_writeable = &$actual_writeable; | ||
| let (actual_str, actual_parts) = $crate::writeable_to_parts_for_test(actual_writeable).unwrap(); | ||
| assert_eq!(actual_str, $expected_str, $($arg)+); | ||
| assert_eq!(actual_str, $crate::Writeable::writeable_to_string(actual_writeable), $($arg)+); | ||
| assert_eq!(actual_parts, $expected_parts, $($arg)+); | ||
| let length_hint = $crate::Writeable::write_len(actual_writeable); | ||
| assert!(length_hint.0 <= actual_str.len(), $($arg)+); | ||
| if let Some(upper) = length_hint.1 { | ||
| assert!(actual_str.len() <= upper, $($arg)+); | ||
| } | ||
| }}; | ||
| } | ||
| ($expected_str:expr, $actual_writeable:expr, $($arg:tt)+) => { | ||
| { | ||
| use $crate::Writeable; | ||
| let writeable = $actual_writeable; | ||
| assert_eq!($expected_str, writeable.writeable_to_string(), $($arg)+); | ||
| if let $crate::LengthHint::Exact(len) = writeable.write_len() { | ||
| assert_eq!($expected_str.len(), len, $($arg)+); | ||
| } | ||
| #[doc(hidden)] | ||
| #[allow(clippy::type_complexity)] | ||
| pub fn writeable_to_parts_for_test<W: Writeable>( | ||
| writeable: &W, | ||
| ) -> Result<(String, Vec<(usize, usize, Part)>), fmt::Error> { | ||
| struct State { | ||
| string: alloc::string::String, | ||
| parts: Vec<(usize, usize, Part)>, | ||
| } | ||
| impl fmt::Write for State { | ||
| fn write_str(&mut self, s: &str) -> fmt::Result { | ||
| self.string.write_str(s) | ||
| } | ||
| fn write_char(&mut self, c: char) -> fmt::Result { | ||
| self.string.write_char(c) | ||
| } | ||
| } | ||
| impl PartsWrite for State { | ||
| type SubPartsWrite = Self; | ||
| fn with_part( | ||
| &mut self, | ||
| part: Part, | ||
| mut f: impl FnMut(&mut Self::SubPartsWrite) -> fmt::Result, | ||
| ) -> fmt::Result { | ||
| let start = self.string.len(); | ||
| f(self)?; | ||
| self.parts.push((start, self.string.len(), part)); | ||
| Ok(()) | ||
| } | ||
| } | ||
| let mut state = State { | ||
| string: alloc::string::String::new(), | ||
| parts: Vec::new(), | ||
| }; | ||
| writeable.write_to_parts(&mut state)?; | ||
| // Sort by first open and last closed | ||
| state | ||
| .parts | ||
| .sort_unstable_by_key(|(begin, end, _)| (*begin, end.wrapping_neg())); | ||
| Ok((state.string, state.parts)) | ||
| } |
+202
-45
@@ -11,9 +11,9 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| fn add(self, other: LengthHint) -> Self { | ||
| match self { | ||
| LengthHint::Undefined => LengthHint::Undefined, | ||
| LengthHint::Exact(len1) => match other { | ||
| LengthHint::Undefined => LengthHint::Undefined, | ||
| LengthHint::Exact(len2) => LengthHint::Exact(len1 + len2), | ||
| LengthHint( | ||
| self.0.saturating_add(other.0), | ||
| match (self.1, other.1) { | ||
| (Some(c), Some(d)) => c.checked_add(d), | ||
| _ => None, | ||
| }, | ||
| } | ||
| ) | ||
| } | ||
@@ -33,3 +33,3 @@ } | ||
| { | ||
| iter.fold(LengthHint::Exact(0), core::ops::Add::add) | ||
| iter.fold(LengthHint::exact(0), core::ops::Add::add) | ||
| } | ||
@@ -42,6 +42,6 @@ } | ||
| fn add(self, other: usize) -> Self { | ||
| match self { | ||
| LengthHint::Undefined => LengthHint::Undefined, | ||
| LengthHint::Exact(len) => LengthHint::Exact(len + other), | ||
| } | ||
| Self( | ||
| self.0.saturating_add(other), | ||
| self.1.and_then(|upper| upper.checked_add(other)), | ||
| ) | ||
| } | ||
@@ -56,2 +56,60 @@ } | ||
| impl core::ops::Mul<usize> for LengthHint { | ||
| type Output = Self; | ||
| fn mul(self, other: usize) -> Self { | ||
| Self( | ||
| self.0.saturating_mul(other), | ||
| self.1.and_then(|upper| upper.checked_mul(other)), | ||
| ) | ||
| } | ||
| } | ||
| impl core::ops::MulAssign<usize> for LengthHint { | ||
| fn mul_assign(&mut self, other: usize) { | ||
| *self = *self * other; | ||
| } | ||
| } | ||
| impl core::ops::BitOr<LengthHint> for LengthHint { | ||
| type Output = Self; | ||
| /// Returns a new hint that is correct wherever `self` is correct, and wherever | ||
| /// `other` is correct. | ||
| /// | ||
| /// Example: | ||
| /// ``` | ||
| /// # use writeable::{LengthHint, Writeable}; | ||
| /// # use core::fmt; | ||
| /// # fn coin_flip() -> bool { true } | ||
| /// | ||
| /// struct NonDeterministicWriteable(String, String); | ||
| /// | ||
| /// impl Writeable for NonDeterministicWriteable { | ||
| /// fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { | ||
| /// sink.write_str(if coin_flip() { &self.0 } else { &self.1 }) | ||
| /// } | ||
| /// | ||
| /// fn write_len(&self) -> LengthHint { | ||
| /// LengthHint::exact(self.0.len()) | LengthHint::exact(self.1.len()) | ||
| /// } | ||
| /// } | ||
| /// ``` | ||
| fn bitor(self, other: LengthHint) -> Self { | ||
| LengthHint( | ||
| Ord::min(self.0, other.0), | ||
| match (self.1, other.1) { | ||
| (Some(c), Some(d)) => Some(Ord::max(c, d)), | ||
| _ => None, | ||
| }, | ||
| ) | ||
| } | ||
| } | ||
| impl core::ops::BitOrAssign<LengthHint> for LengthHint { | ||
| fn bitor_assign(&mut self, other: Self) { | ||
| *self = *self | other; | ||
| } | ||
| } | ||
| impl core::iter::Sum<usize> for LengthHint { | ||
@@ -62,3 +120,3 @@ fn sum<I>(iter: I) -> Self | ||
| { | ||
| LengthHint::Exact(iter.sum::<usize>()) | ||
| LengthHint::exact(iter.sum::<usize>()) | ||
| } | ||
@@ -73,36 +131,53 @@ } | ||
| fn test_add() { | ||
| assert_eq!(LengthHint::Exact(5), LengthHint::Exact(3) + 2); | ||
| assert_eq!(LengthHint::exact(3) + 2, LengthHint::exact(5)); | ||
| assert_eq!( | ||
| LengthHint::Exact(5), | ||
| LengthHint::Exact(3) + LengthHint::Exact(2) | ||
| LengthHint::exact(3) + LengthHint::exact(2), | ||
| LengthHint::exact(5) | ||
| ); | ||
| assert_eq!( | ||
| LengthHint::Undefined, | ||
| LengthHint::Exact(3) + LengthHint::Undefined | ||
| LengthHint::exact(3) + LengthHint::undefined(), | ||
| LengthHint::at_least(3) | ||
| ); | ||
| assert_eq!(LengthHint::Undefined, LengthHint::Undefined + 2); | ||
| assert_eq!(LengthHint::undefined() + 2, LengthHint::at_least(2)); | ||
| assert_eq!( | ||
| LengthHint::Undefined, | ||
| LengthHint::Undefined + LengthHint::Exact(2) | ||
| LengthHint::undefined() + LengthHint::exact(2), | ||
| LengthHint::at_least(2) | ||
| ); | ||
| assert_eq!( | ||
| LengthHint::Undefined, | ||
| LengthHint::Undefined + LengthHint::Undefined | ||
| LengthHint::undefined() + LengthHint::undefined(), | ||
| LengthHint::undefined() | ||
| ); | ||
| let mut len = LengthHint::Exact(5); | ||
| len += LengthHint::Exact(3); | ||
| assert_eq!(LengthHint::Exact(8), len); | ||
| assert_eq!( | ||
| LengthHint::at_least(15) + LengthHint::exact(3), | ||
| LengthHint::at_least(18) | ||
| ); | ||
| assert_eq!( | ||
| LengthHint::at_least(15) + LengthHint::at_most(3), | ||
| LengthHint::at_least(15) | ||
| ); | ||
| assert_eq!(LengthHint::between(48, 92) + 5, LengthHint::between(53, 97)); | ||
| let mut len = LengthHint::exact(5); | ||
| len += LengthHint::exact(3); | ||
| assert_eq!(len, LengthHint::exact(8)); | ||
| len += 2; | ||
| assert_eq!(LengthHint::Exact(10), len); | ||
| len += LengthHint::Undefined; | ||
| assert_eq!(LengthHint::Undefined, len); | ||
| assert_eq!(len, LengthHint::exact(10)); | ||
| len += LengthHint::undefined(); | ||
| assert_eq!(len, LengthHint::at_least(10)); | ||
| len += LengthHint::Exact(3); | ||
| assert_eq!(LengthHint::Undefined, len); | ||
| len += LengthHint::exact(3); | ||
| assert_eq!(len, LengthHint::at_least(13)); | ||
| len += 2; | ||
| assert_eq!(LengthHint::Undefined, len); | ||
| len += LengthHint::Undefined; | ||
| assert_eq!(LengthHint::Undefined, len); | ||
| assert_eq!(len, LengthHint::at_least(15)); | ||
| len += LengthHint::undefined(); | ||
| assert_eq!(len, LengthHint::at_least(15)); | ||
| assert_eq!( | ||
| LengthHint::between(usize::MAX - 10, usize::MAX - 5) + LengthHint::exact(20), | ||
| LengthHint::at_least(usize::MAX) | ||
| ); | ||
| } | ||
@@ -113,27 +188,109 @@ | ||
| let lens = vec![ | ||
| LengthHint::Exact(4), | ||
| LengthHint::Exact(1), | ||
| LengthHint::Exact(1), | ||
| LengthHint::exact(4), | ||
| LengthHint::exact(1), | ||
| LengthHint::exact(1), | ||
| ]; | ||
| assert_eq!( | ||
| LengthHint::Exact(6), | ||
| lens.iter().copied().sum::<LengthHint>() | ||
| lens.iter().copied().sum::<LengthHint>(), | ||
| LengthHint::exact(6) | ||
| ); | ||
| let lens = vec![ | ||
| LengthHint::Exact(4), | ||
| LengthHint::Undefined, | ||
| LengthHint::Exact(1), | ||
| LengthHint::exact(4), | ||
| LengthHint::undefined(), | ||
| LengthHint::at_least(1), | ||
| ]; | ||
| assert_eq!( | ||
| LengthHint::Undefined, | ||
| lens.iter().copied().sum::<LengthHint>() | ||
| lens.iter().copied().sum::<LengthHint>(), | ||
| LengthHint::at_least(5) | ||
| ); | ||
| let lens = vec![ | ||
| LengthHint::exact(4), | ||
| LengthHint::undefined(), | ||
| LengthHint::at_most(1), | ||
| ]; | ||
| assert_eq!( | ||
| lens.iter().copied().sum::<LengthHint>(), | ||
| LengthHint::at_least(4) | ||
| ); | ||
| let lens = vec![4, 1, 1]; | ||
| assert_eq!( | ||
| LengthHint::Exact(6), | ||
| lens.iter().copied().sum::<LengthHint>() | ||
| lens.iter().copied().sum::<LengthHint>(), | ||
| LengthHint::exact(6) | ||
| ); | ||
| } | ||
| #[test] | ||
| fn test_mul() { | ||
| assert_eq!(LengthHint::exact(3) * 2, LengthHint::exact(6)); | ||
| assert_eq!(LengthHint::undefined() * 2, LengthHint::undefined()); | ||
| assert_eq!( | ||
| LengthHint::between(48, 92) * 2, | ||
| LengthHint::between(96, 184) | ||
| ); | ||
| let mut len = LengthHint::exact(5); | ||
| len *= 2; | ||
| assert_eq!(len, LengthHint::exact(10)); | ||
| assert_eq!( | ||
| LengthHint::between(usize::MAX - 10, usize::MAX - 5) * 2, | ||
| LengthHint::at_least(usize::MAX) | ||
| ); | ||
| } | ||
| #[test] | ||
| fn test_bitor() { | ||
| assert_eq!( | ||
| LengthHint::exact(3) | LengthHint::exact(2), | ||
| LengthHint::between(2, 3) | ||
| ); | ||
| assert_eq!( | ||
| LengthHint::exact(3) | LengthHint::undefined(), | ||
| LengthHint::undefined() | ||
| ); | ||
| assert_eq!( | ||
| LengthHint::undefined() | LengthHint::undefined(), | ||
| LengthHint::undefined() | ||
| ); | ||
| assert_eq!( | ||
| LengthHint::exact(10) | LengthHint::exact(10), | ||
| LengthHint::exact(10) | ||
| ); | ||
| assert_eq!( | ||
| LengthHint::at_least(15) | LengthHint::exact(3), | ||
| LengthHint::at_least(3) | ||
| ); | ||
| assert_eq!( | ||
| LengthHint::at_least(15) | LengthHint::at_most(18), | ||
| LengthHint::undefined() | ||
| ); | ||
| assert_eq!( | ||
| LengthHint::at_least(15) | LengthHint::at_least(18), | ||
| LengthHint::at_least(15) | ||
| ); | ||
| assert_eq!( | ||
| LengthHint::at_most(15) | LengthHint::at_most(18), | ||
| LengthHint::at_most(18) | ||
| ); | ||
| assert_eq!( | ||
| LengthHint::between(5, 10) | LengthHint::at_most(3), | ||
| LengthHint::at_most(10) | ||
| ); | ||
| let mut len = LengthHint::exact(5); | ||
| len |= LengthHint::exact(3); | ||
| assert_eq!(len, LengthHint::between(5, 3)); | ||
| } | ||
| } |
@@ -21,3 +21,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| fn write_len(&self) -> LengthHint { | ||
| LengthHint::Exact(self.message.len()) | ||
| LengthHint::exact(self.message.len()) | ||
| } | ||
@@ -32,3 +32,3 @@ } | ||
| }; | ||
| assert_writeable_eq!(input_string, &message); | ||
| assert_writeable_eq!(&message, input_string); | ||
| } |
Sorry, the diff of this file is not supported yet