You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

writeable

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

writeable - cargo Package Compare versions

Comparing version
0.2.1
to
0.3.0
+4
-3
.cargo_vcs_info.json
{
"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",
]

@@ -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]);
}

@@ -40,3 +40,3 @@ # writeable [![crates.io](https://img.shields.io/crates/v/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 @@

@@ -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