Skip to content

Commit 0ad2578

Browse files
Merge pull request #23 from rodrimati1992/char_support
Added char support, 0.2.19 release
2 parents cbd5609 + c82286f commit 0ad2578

34 files changed

+849
-229
lines changed

.github/workflows/rust.yml

+5-10
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
strategy:
1313
max-parallel: 2
1414
matrix:
15-
rust: [stable, beta, nightly, 1.46.0]
15+
rust: [stable, beta, nightly, 1.46.0, 1.51.0]
1616

1717
steps:
1818
- uses: actions/checkout@v2
@@ -29,10 +29,10 @@ jobs:
2929
3030
- uses: actions/checkout@v2
3131
- name: ci-stable
32-
# apparently github hadn't updated stable by 2021-03-27?
33-
if: ${{ matrix.rust == 'beta' }}
32+
if: ${{ matrix.rust == '1.51.0' }}
3433
run: |
3534
cargo update
35+
3636
cd "${{github.workspace}}/const_format/"
3737
cargo test --features "testing const_generics"
3838
@@ -69,10 +69,5 @@ jobs:
6969
7070
cargo clean
7171
72-
cargo miri test --features "testing"
73-
cargo miri test --features "testing fmt"
74-
cargo miri test --features "testing assert"
75-
cargo miri test --features "testing derive"
76-
cargo miri test --features "testing constant_time_as_str"
77-
cargo miri test --features "testing derive constant_time_as_str"
78-
cargo miri test --features "testing derive constant_time_as_str assert"
72+
cargo miri test --tests --features "testing derive fmt assert"
73+
cargo miri test --features "testing derive fmt constant_time_as_str assert"

Changelog.md

+16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@ This is the changelog,summarising changes in each version(some minor changes may
22

33
# 0.2
44

5+
### 0.2.19
6+
7+
Added `char` support to all formatting macros.
8+
9+
Added `char`, `&[char]`, and `Option<char>` impls of FormatMarker trait, with debug formatting methods.
10+
11+
Added `Formatter::{write_char, write_char_debug}` methods.
12+
13+
Added `StrWriterMut::{as_str_alt, write_char, write_char_debug}` methods.
14+
15+
Added `StrWriter::{as_str_alt, unsize}` methods.
16+
17+
Deprecated `strwriter_as_str` macro, superceded by `StrWriter::as_str_alt`.
18+
19+
Bumped the minimum required nightly version to 2021-07-05 due to use of const-stabilized `core::str::from_utf8_unchecked`.
20+
521
### 0.2.18
622

723
Fixed potential soundness bug where unions used to do pointer casts were not `#[repr(C)]`

README.md

+5-7
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ the sections below describe the features that are available for each version.
2121
These macros are the only things available in Rust 1.46.0:
2222

2323
- [`concatcp`]:
24-
Concatenates `integers`, `bool`, and `&str` constants into a `&'static str` constant.
24+
Concatenates `integers`, `bool`, `char`, and `&str` constants into a `&'static str` constant.
2525

2626
- [`formatcp`]:
27-
[`format`]-like formatting which takes `integers`, `bool`, and `&str` constants,
27+
[`format`]-like formatting which takes `integers`, `bool`, `char`, and `&str` constants,
2828
and emits a `&'static str` constant.
2929

3030
- [`str_get`]:
@@ -37,7 +37,7 @@ Indexes a `&'static str` constant.
3737
Creates a `&'static str` by repeating a `&'static str` constant `times` times.
3838

3939
- [`str_splice`]:
40-
Replaces a substring in a &'static str constant.
40+
Replaces a substring in a `&'static str` constant.
4141

4242
### Rust 1.51.0
4343

@@ -160,7 +160,7 @@ panicking at compile-time requires a nightly feature.
160160
```rust
161161
#![feature(const_mut_refs)]
162162

163-
use const_format::{StrWriter, assertc_ne, strwriter_as_str, writec};
163+
use const_format::{StrWriter, assertc_ne, writec};
164164
use const_format::utils::str_eq;
165165

166166
macro_rules! check_valid_pizza{
@@ -286,9 +286,7 @@ provides the [`ConstDebug`] derive macro to format user-defined types at compile
286286
This implicitly uses the `syn` crate, so clean compiles take a bit longer than without the feature.
287287

288288
- "assert": implies the "fmt" feature,
289-
enables the assertion macros.<br>
290-
This is a separate cargo feature because
291-
it uses nightly Rust features that are less stable than the "fmt" feature does.
289+
enables the assertion macros.
292290

293291
- "constant_time_as_str": implies the "fmt" feature.
294292
An optimization that requires a few additional nightly features,

const_format/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "const_format"
3-
version = "0.2.18"
3+
version = "0.2.19"
44
authors = ["rodrimati1992 <[email protected]>"]
55
edition = "2018"
66
license = "Zlib"

const_format/src/__str_methods.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
mod str_replace;
33

44
#[cfg(feature = "const_generics")]
5-
pub use self::str_replace::{str_replace, str_replace_length, ReplaceInput, ReplaceInputConv};
5+
pub use self::str_replace::{ReplaceInput, ReplaceInputConv};
66

77
mod str_repeat;
88
pub use str_repeat::StrRepeatArgs;

const_format/src/__str_methods/str_replace.rs

+34-14
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,62 @@
11
use super::{bytes_find, AsciiByte};
22

3-
pub struct ReplaceInputConv<T>(pub T);
3+
pub struct ReplaceInputConv<T>(pub &'static str, pub T, pub &'static str);
44

55
impl ReplaceInputConv<u8> {
66
pub const fn conv(self) -> ReplaceInput {
7-
ReplaceInput::AsciiByte(AsciiByte::new(self.0))
7+
ReplaceInput {
8+
str: self.0,
9+
pattern: ReplacePattern::AsciiByte(AsciiByte::new(self.1)),
10+
replaced_with: self.2,
11+
}
812
}
913
}
1014

1115
impl ReplaceInputConv<&'static str> {
1216
pub const fn conv(self) -> ReplaceInput {
13-
ReplaceInput::Str(self.0)
17+
ReplaceInput {
18+
str: self.0,
19+
pattern: ReplacePattern::Str(self.1),
20+
replaced_with: self.2,
21+
}
1422
}
1523
}
1624

17-
pub enum ReplaceInput {
25+
pub struct ReplaceInput {
26+
str: &'static str,
27+
pattern: ReplacePattern,
28+
replaced_with: &'static str,
29+
}
30+
31+
#[derive(Copy, Clone)]
32+
pub enum ReplacePattern {
1833
AsciiByte(AsciiByte),
1934
Str(&'static str),
2035
}
2136

22-
pub const fn str_replace_length(inp: &str, r: ReplaceInput, replaced_with: &str) -> usize {
37+
impl ReplaceInput {
38+
pub const fn replace_length(&self) -> usize {
39+
str_replace_length(self.str, self.pattern, self.replaced_with)
40+
}
41+
pub const fn replace<const L: usize>(&self) -> [u8; L] {
42+
str_replace(self.str, self.pattern, self.replaced_with)
43+
}
44+
}
45+
46+
const fn str_replace_length(inp: &str, r: ReplacePattern, replaced_with: &str) -> usize {
2347
let inp = inp.as_bytes();
2448

2549
let replaced_len = replaced_with.len();
2650
let mut out_len = 0;
2751

2852
match r {
29-
ReplaceInput::AsciiByte(byte) => {
53+
ReplacePattern::AsciiByte(byte) => {
3054
let byte = byte.get();
3155
iter_copy_slice! {b in inp =>
3256
out_len += if b == byte { replaced_len } else { 1 };
3357
}
3458
}
35-
ReplaceInput::Str(str) => {
59+
ReplacePattern::Str(str) => {
3660
if str.is_empty() {
3761
return inp.len();
3862
}
@@ -50,11 +74,7 @@ pub const fn str_replace_length(inp: &str, r: ReplaceInput, replaced_with: &str)
5074
out_len
5175
}
5276

53-
pub const fn str_replace<const L: usize>(
54-
inp: &str,
55-
r: ReplaceInput,
56-
replaced_with: &str,
57-
) -> [u8; L] {
77+
const fn str_replace<const L: usize>(inp: &str, r: ReplacePattern, replaced_with: &str) -> [u8; L] {
5878
let inp = inp.as_bytes();
5979

6080
let replaced_with_bytes = replaced_with.as_bytes();
@@ -77,7 +97,7 @@ pub const fn str_replace<const L: usize>(
7797
}
7898

7999
match r {
80-
ReplaceInput::AsciiByte(byte) => {
100+
ReplacePattern::AsciiByte(byte) => {
81101
let byte = byte.get();
82102
iter_copy_slice! {b in inp =>
83103
if b == byte {
@@ -87,7 +107,7 @@ pub const fn str_replace<const L: usize>(
87107
}
88108
}
89109
}
90-
ReplaceInput::Str(str) => {
110+
ReplacePattern::Str(str) => {
91111
if str.is_empty() {
92112
iter_copy_slice! {b in inp =>
93113
write_byte!(b);

const_format/src/char_encoding.rs

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use crate::formatting::hex_as_ascii;
2+
3+
#[cfg(any(test, feature = "fmt"))]
4+
pub(crate) const fn char_display_len(c: char) -> usize {
5+
match c as u32 {
6+
0..=127 => 1,
7+
0x80..=0x7FF => 2,
8+
0x800..=0xFFFF => 3,
9+
0x10000..=u32::MAX => 4,
10+
}
11+
}
12+
13+
#[cfg(any(test, feature = "fmt"))]
14+
pub(crate) const fn char_debug_len(c: char) -> usize {
15+
let inner = match c {
16+
'\t' | '\r' | '\n' | '\\' | '\'' | '\"' => 2,
17+
'\x00'..='\x1F' => 4,
18+
_ => char_display_len(c),
19+
};
20+
inner + 2
21+
}
22+
23+
const fn char_to_utf8(char: char) -> ([u8; 4], usize) {
24+
let u32 = char as u32;
25+
match u32 {
26+
0..=127 => ([u32 as u8, 0, 0, 0], 1),
27+
0x80..=0x7FF => {
28+
let b0 = 0b1100_0000 | (u32 >> 6) as u8;
29+
let b1 = 0b1000_0000 | (u32 & 0b0011_1111) as u8;
30+
([b0, b1, 0, 0], 2)
31+
}
32+
0x800..=0xFFFF => {
33+
let b0 = 0b1110_0000 | (u32 >> 12) as u8;
34+
let b1 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8;
35+
let b2 = 0b1000_0000 | (u32 & 0b0011_1111) as u8;
36+
([b0, b1, b2, 0], 3)
37+
}
38+
0x10000..=u32::MAX => {
39+
let b0 = 0b1111_0000 | (u32 >> 18) as u8;
40+
let b1 = 0b1000_0000 | ((u32 >> 12) & 0b0011_1111) as u8;
41+
let b2 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8;
42+
let b3 = 0b1000_0000 | (u32 & 0b0011_1111) as u8;
43+
([b0, b1, b2, b3], 4)
44+
}
45+
}
46+
}
47+
48+
pub(crate) const fn char_to_display(char: char) -> FmtChar {
49+
let ([b0, b1, b2, b3], len) = char_to_utf8(char);
50+
FmtChar {
51+
encoded: [b0, b1, b2, b3, 0, 0],
52+
len: len as u8,
53+
}
54+
}
55+
56+
pub(crate) const fn char_to_debug(c: char) -> FmtChar {
57+
let ([b0, b1, b2, b3], len) = match c {
58+
'\t' => (*br#"\t "#, 2),
59+
'\r' => (*br#"\r "#, 2),
60+
'\n' => (*br#"\n "#, 2),
61+
'\\' => (*br#"\\ "#, 2),
62+
'\'' => (*br#"\' "#, 2),
63+
'\"' => (*br#"\" "#, 2),
64+
'\x00'..='\x1F' => {
65+
let n = c as u8;
66+
(
67+
[b'\\', b'x', hex_as_ascii(n >> 4), hex_as_ascii(n & 0b1111)],
68+
4,
69+
)
70+
}
71+
_ => char_to_utf8(c),
72+
};
73+
74+
let mut encoded = [b'\'', b0, b1, b2, b3, 0];
75+
encoded[len + 1] = b'\'';
76+
77+
FmtChar {
78+
encoded,
79+
len: (len as u8) + 2,
80+
}
81+
}
82+
83+
#[derive(Copy, Clone)]
84+
pub struct FmtChar {
85+
encoded: [u8; 6],
86+
len: u8,
87+
}
88+
89+
impl FmtChar {
90+
/// Array which contains the pre-len display/debug-formatted `char`,
91+
/// only `&self.encoded[][..self.len()]` should be copied.
92+
pub const fn encoded(&self) -> &[u8; 6] {
93+
&self.encoded
94+
}
95+
96+
pub const fn len(&self) -> usize {
97+
self.len as usize
98+
}
99+
100+
#[cfg(test)]
101+
fn as_bytes(&self) -> &[u8] {
102+
&self.encoded[..self.len()]
103+
}
104+
}
105+
106+
#[cfg(all(test, not(miri)))]
107+
mod tests;

0 commit comments

Comments
 (0)