Skip to content

Commit eb4b23f

Browse files
committed
feat(cell): add upper/lower hex print support for bitstring
1 parent c8c1103 commit eb4b23f

File tree

5 files changed

+185
-63
lines changed

5 files changed

+185
-63
lines changed

.github/workflows/master.yml

+9-1
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,20 @@ jobs:
5959
profile: minimal
6060
toolchain: stable
6161
override: true
62-
components: rustfmt, clippy
62+
components: clippy
63+
64+
- name: Install nightly toolchain
65+
uses: actions-rs/toolchain@v1
66+
with:
67+
profile: minimal
68+
toolchain: nightly
69+
components: rustfmt
6370

6471
- name: Run cargo fmt
6572
uses: actions-rs/cargo@v1
6673
with:
6774
command: fmt
75+
toolchain: nightly
6876
args: --all -- --check
6977

7078
- name: Run cargo clippy

src/cell/builder.rs

+40-24
Original file line numberDiff line numberDiff line change
@@ -1051,34 +1051,50 @@ impl CellBuilder {
10511051

10521052
/// Returns an object which will display data as a bitstring
10531053
/// with a termination bit.
1054-
pub fn display_data(&self) -> impl std::fmt::Display + std::fmt::Binary + '_ {
1055-
struct DisplayData<'a>(&'a CellBuilder);
1054+
#[inline]
1055+
pub fn display_data(&self) -> DisplayCellBuilderData<'_> {
1056+
DisplayCellBuilderData(self)
1057+
}
10561058

1057-
impl std::fmt::Display for DisplayData<'_> {
1058-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1059-
std::fmt::Display::fmt(
1060-
&Bitstring {
1061-
bytes: &self.0.data,
1062-
bit_len: self.0.bit_len,
1063-
},
1064-
f,
1065-
)
1066-
}
1059+
#[inline]
1060+
fn as_bitstring(&self) -> Bitstring<'_> {
1061+
Bitstring {
1062+
bytes: &self.data,
1063+
bit_len: self.bit_len,
10671064
}
1065+
}
1066+
}
10681067

1069-
impl std::fmt::Binary for DisplayData<'_> {
1070-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1071-
std::fmt::Binary::fmt(
1072-
&Bitstring {
1073-
bytes: &self.0.data,
1074-
bit_len: self.0.bit_len,
1075-
},
1076-
f,
1077-
)
1078-
}
1079-
}
1068+
/// Helper struct to print the cell builder data.
1069+
#[derive(Clone, Copy)]
1070+
#[repr(transparent)]
1071+
pub struct DisplayCellBuilderData<'a>(&'a CellBuilder);
10801072

1081-
DisplayData(self)
1073+
impl std::fmt::Display for DisplayCellBuilderData<'_> {
1074+
#[inline]
1075+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1076+
std::fmt::LowerHex::fmt(self, f)
1077+
}
1078+
}
1079+
1080+
impl std::fmt::LowerHex for DisplayCellBuilderData<'_> {
1081+
#[inline]
1082+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1083+
std::fmt::LowerHex::fmt(&self.0.as_bitstring(), f)
1084+
}
1085+
}
1086+
1087+
impl std::fmt::UpperHex for DisplayCellBuilderData<'_> {
1088+
#[inline]
1089+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1090+
std::fmt::UpperHex::fmt(&self.0.as_bitstring(), f)
1091+
}
1092+
}
1093+
1094+
impl std::fmt::Binary for DisplayCellBuilderData<'_> {
1095+
#[inline]
1096+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1097+
std::fmt::Binary::fmt(&self.0.as_bitstring(), f)
10821098
}
10831099
}
10841100

src/cell/mod.rs

+29-8
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@ use std::str::FromStr;
55

66
pub use everscale_types_proc::{Load, Store};
77

8-
pub use self::builder::{CellBuilder, CellRefsBuilder, Store};
8+
pub use self::builder::{CellBuilder, CellRefsBuilder, DisplayCellBuilderData, Store};
99
pub use self::cell_context::{CellContext, CellParts, LoadMode};
1010
#[cfg(not(feature = "sync"))]
1111
pub use self::cell_impl::rc::{Cell, CellInner, WeakCell};
1212
#[cfg(feature = "sync")]
1313
pub use self::cell_impl::sync::{Cell, CellInner, WeakCell};
1414
pub use self::cell_impl::{StaticCell, VirtualCellWrapper};
1515
pub use self::lazy::{Lazy, LazyExotic};
16-
pub use self::slice::{CellSlice, CellSliceParts, CellSliceRange, ExactSize, Load, LoadCell};
16+
pub use self::slice::{
17+
CellSlice, CellSliceParts, CellSliceRange, DisplayCellSliceData, ExactSize, Load, LoadCell,
18+
};
1719
pub use self::usage_tree::{UsageTree, UsageTreeMode, UsageTreeWithSubtrees};
1820
use crate::error::{Error, ParseHashBytesError};
1921
use crate::util::Bitstring;
@@ -667,6 +669,16 @@ impl HashBytes {
667669
None => unsafe { std::hint::unreachable_unchecked() },
668670
}
669671
}
672+
673+
#[inline(always)]
674+
fn fmt_hex(&self, f: &mut std::fmt::Formatter<'_>, table: &[u8; 16]) -> std::fmt::Result {
675+
let mut output = [0u8; 64];
676+
crate::util::encode_to_hex_slice(&self.0, &mut output, table).ok();
677+
678+
// SAFETY: output is guaranteed to contain only [0-9a-f]
679+
let output = unsafe { std::str::from_utf8_unchecked(&output) };
680+
f.write_str(output)
681+
}
670682
}
671683

672684
impl Default for HashBytes {
@@ -811,13 +823,21 @@ impl FromStr for HashBytes {
811823
}
812824

813825
impl std::fmt::Display for HashBytes {
826+
#[inline]
814827
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
815-
let mut output = [0u8; 64];
816-
hex::encode_to_slice(self, &mut output).ok();
828+
std::fmt::LowerHex::fmt(self, f)
829+
}
830+
}
817831

818-
// SAFETY: output is guaranteed to contain only [0-9a-f]
819-
let output = unsafe { std::str::from_utf8_unchecked(&output) };
820-
f.write_str(output)
832+
impl std::fmt::LowerHex for HashBytes {
833+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
834+
Self::fmt_hex(self, f, crate::util::HEX_CHARS_LOWER)
835+
}
836+
}
837+
838+
impl std::fmt::UpperHex for HashBytes {
839+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
840+
Self::fmt_hex(self, f, crate::util::HEX_CHARS_UPPER)
821841
}
822842
}
823843

@@ -866,7 +886,8 @@ impl serde::Serialize for HashBytes {
866886
{
867887
if serializer.is_human_readable() {
868888
let mut output = [0u8; 64];
869-
hex::encode_to_slice(self.0.as_slice(), &mut output).ok();
889+
crate::util::encode_to_hex_slice(&self.0, &mut output, crate::util::HEX_CHARS_LOWER)
890+
.ok();
870891

871892
// SAFETY: output is guaranteed to contain only [0-9a-f]
872893
let output = unsafe { std::str::from_utf8_unchecked(&output) };

src/cell/slice.rs

+43-25
Original file line numberDiff line numberDiff line change
@@ -1780,36 +1780,54 @@ impl<'a> CellSlice<'a> {
17801780

17811781
/// Returns an object which will display data as a bitstring
17821782
/// with a termination bit.
1783-
pub fn display_data<'b: 'a>(&'b self) -> impl std::fmt::Display + std::fmt::Binary + 'b {
1784-
fn make_bitstring<'b: 'a, 'a>(
1785-
s: &'b CellSlice<'a>,
1786-
bytes: &'b mut [u8; 128],
1787-
) -> Result<Bitstring<'b>, std::fmt::Error> {
1788-
let bit_len = s.size_bits();
1789-
if s.get_raw(0, bytes, bit_len).is_err() {
1790-
return Err(std::fmt::Error);
1791-
}
1792-
Ok(Bitstring { bytes, bit_len })
1793-
}
1783+
#[inline]
1784+
pub fn display_data<'b: 'a>(&'b self) -> DisplayCellSliceData<'b, 'a> {
1785+
DisplayCellSliceData(self)
1786+
}
1787+
}
17941788

1795-
struct DisplayData<'b, 'a>(&'b CellSlice<'a>);
1789+
/// Helper struct to print the cell slice data.
1790+
#[derive(Clone, Copy)]
1791+
#[repr(transparent)]
1792+
pub struct DisplayCellSliceData<'b, 'a>(&'b CellSlice<'a>);
17961793

1797-
impl<'b: 'a, 'a> std::fmt::Display for DisplayData<'b, 'a> {
1798-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1799-
let mut bytes = [0u8; 128];
1800-
std::fmt::Display::fmt(&ok!(make_bitstring(self.0, &mut bytes)), f)
1801-
}
1802-
}
1794+
impl<'b: 'a, 'a> std::fmt::Display for DisplayCellSliceData<'b, 'a> {
1795+
#[inline]
1796+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1797+
std::fmt::LowerHex::fmt(self, f)
1798+
}
1799+
}
18031800

1804-
impl<'b: 'a, 'a> std::fmt::Binary for DisplayData<'b, 'a> {
1805-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1806-
let mut bytes = [0u8; 128];
1807-
std::fmt::Binary::fmt(&ok!(make_bitstring(self.0, &mut bytes)), f)
1808-
}
1809-
}
1801+
impl<'b: 'a, 'a> std::fmt::LowerHex for DisplayCellSliceData<'b, 'a> {
1802+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1803+
let mut bytes = [0u8; 128];
1804+
std::fmt::LowerHex::fmt(&ok!(make_bitstring(self.0, &mut bytes)), f)
1805+
}
1806+
}
1807+
1808+
impl<'b: 'a, 'a> std::fmt::UpperHex for DisplayCellSliceData<'b, 'a> {
1809+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1810+
let mut bytes = [0u8; 128];
1811+
std::fmt::UpperHex::fmt(&ok!(make_bitstring(self.0, &mut bytes)), f)
1812+
}
1813+
}
1814+
1815+
impl<'b: 'a, 'a> std::fmt::Binary for DisplayCellSliceData<'b, 'a> {
1816+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1817+
let mut bytes = [0u8; 128];
1818+
std::fmt::Binary::fmt(&ok!(make_bitstring(self.0, &mut bytes)), f)
1819+
}
1820+
}
18101821

1811-
DisplayData(self)
1822+
fn make_bitstring<'b: 'a, 'a>(
1823+
s: &'b CellSlice<'a>,
1824+
bytes: &'b mut [u8; 128],
1825+
) -> Result<Bitstring<'b>, std::fmt::Error> {
1826+
let bit_len = s.size_bits();
1827+
if s.get_raw(0, bytes, bit_len).is_err() {
1828+
return Err(std::fmt::Error);
18121829
}
1830+
Ok(Bitstring { bytes, bit_len })
18131831
}
18141832

18151833
impl ExactSize for CellSlice<'_> {

src/util.rs

+64-5
Original file line numberDiff line numberDiff line change
@@ -301,10 +301,8 @@ impl Bitstring<'_> {
301301

302302
Ok((data, bit_len))
303303
}
304-
}
305304

306-
impl std::fmt::Display for Bitstring<'_> {
307-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
305+
fn fmt_hex<const UPPER: bool>(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308306
const CHUNK_LEN: usize = 16;
309307

310308
let bit_len = std::cmp::min(self.bit_len as usize, self.bytes.len() * 8) as u16;
@@ -325,7 +323,12 @@ impl std::fmt::Display for Bitstring<'_> {
325323
let mut chunk = [0u8; CHUNK_LEN * 2];
326324
for data in bytes.chunks(CHUNK_LEN) {
327325
let chunk = &mut chunk[..data.len() * 2];
328-
hex::encode_to_slice(data, chunk).unwrap();
326+
327+
if UPPER {
328+
encode_to_hex_slice(data, chunk, HEX_CHARS_UPPER).unwrap();
329+
} else {
330+
encode_to_hex_slice(data, chunk, HEX_CHARS_LOWER).unwrap();
331+
}
329332

330333
// SAFETY: result was constructed from valid ascii `HEX_CHARS_LOWER`
331334
ok!(f.write_str(unsafe { std::str::from_utf8_unchecked(chunk) }));
@@ -337,13 +340,39 @@ impl std::fmt::Display for Bitstring<'_> {
337340
if rem == 1 {
338341
last_byte >>= 4;
339342
}
340-
ok!(write!(f, "{last_byte:0rem$x}{tag}"));
343+
344+
if UPPER {
345+
ok!(write!(f, "{last_byte:0rem$X}{tag}"));
346+
} else {
347+
ok!(write!(f, "{last_byte:0rem$x}{tag}"));
348+
}
341349
}
342350

343351
Ok(())
344352
}
345353
}
346354

355+
impl std::fmt::Display for Bitstring<'_> {
356+
#[inline]
357+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
358+
std::fmt::LowerHex::fmt(self, f)
359+
}
360+
}
361+
362+
impl std::fmt::LowerHex for Bitstring<'_> {
363+
#[inline]
364+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
365+
Self::fmt_hex::<false>(self, f)
366+
}
367+
}
368+
369+
impl std::fmt::UpperHex for Bitstring<'_> {
370+
#[inline]
371+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
372+
Self::fmt_hex::<true>(self, f)
373+
}
374+
}
375+
347376
impl std::fmt::Binary for Bitstring<'_> {
348377
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349378
let bit_len = std::cmp::min(self.bit_len as usize, self.bytes.len() * 8) as u16;
@@ -369,6 +398,36 @@ impl std::fmt::Binary for Bitstring<'_> {
369398
}
370399
}
371400

401+
pub(crate) fn encode_to_hex_slice(
402+
input: &[u8],
403+
output: &mut [u8],
404+
table: &[u8; 16],
405+
) -> Result<(), hex::FromHexError> {
406+
if input.len() * 2 != output.len() {
407+
return Err(hex::FromHexError::InvalidStringLength);
408+
}
409+
410+
for (byte, output) in input.iter().zip(output.chunks_exact_mut(2)) {
411+
let (high, low) = byte2hex(*byte, table);
412+
output[0] = high;
413+
output[1] = low;
414+
}
415+
416+
Ok(())
417+
}
418+
419+
#[inline]
420+
#[must_use]
421+
fn byte2hex(byte: u8, table: &[u8; 16]) -> (u8, u8) {
422+
let high = table[((byte & 0xf0) >> 4) as usize];
423+
let low = table[(byte & 0x0f) as usize];
424+
425+
(high, low)
426+
}
427+
428+
pub(crate) const HEX_CHARS_LOWER: &[u8; 16] = b"0123456789abcdef";
429+
pub(crate) const HEX_CHARS_UPPER: &[u8; 16] = b"0123456789ABCDEF";
430+
372431
#[allow(unused)]
373432
pub(crate) fn debug_tuple_field1_finish(
374433
f: &mut std::fmt::Formatter<'_>,

0 commit comments

Comments
 (0)