Skip to content

Commit 08fea24

Browse files
committed
od: add fH and fB format specifiers
1 parent 544a313 commit 08fea24

File tree

6 files changed

+145
-12
lines changed

6 files changed

+145
-12
lines changed

src/uu/od/src/formatteriteminfo.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::fmt;
1111
pub enum FormatWriter {
1212
IntWriter(fn(u64) -> String),
1313
FloatWriter(fn(f64) -> String),
14+
BFloatWriter(fn(f64) -> String),
1415
MultibyteWriter(fn(&[u8]) -> String),
1516
}
1617

@@ -25,6 +26,10 @@ impl fmt::Debug for FormatWriter {
2526
f.write_str("FloatWriter:")?;
2627
fmt::Pointer::fmt(p, f)
2728
}
29+
Self::BFloatWriter(ref p) => {
30+
f.write_str("BFloatWriter:")?;
31+
fmt::Pointer::fmt(p, f)
32+
}
2833
Self::MultibyteWriter(ref p) => {
2934
f.write_str("MultibyteWriter:")?;
3035
fmt::Pointer::fmt(&(*p as *const ()), f)

src/uu/od/src/inputdecoder.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
//
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
5-
use half::f16;
5+
// spell-checker:ignore bfloat
6+
use half::{bf16, f16};
67
use std::io;
78

89
use crate::byteorder_io::ByteOrder;
@@ -155,6 +156,13 @@ impl MemoryDecoder<'_> {
155156
_ => panic!("Invalid byte_size: {byte_size}"),
156157
}
157158
}
159+
160+
/// Returns a bfloat16 as f64 from the internal buffer at position `start`.
161+
pub fn read_bfloat(&self, start: usize) -> f64 {
162+
let bits = self.byte_order.read_u16(&self.data[start..start + 2]);
163+
let val = f32::from(bf16::from_bits(bits));
164+
f64::from(val)
165+
}
158166
}
159167

160168
#[cfg(test)]

src/uu/od/src/od.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
// spell-checker:ignore (clap) dont
77
// spell-checker:ignore (ToDO) formatteriteminfo inputdecoder inputoffset mockstream nrofbytes partialreader odfunc multifile exitcode
8-
// spell-checker:ignore Anone
8+
// spell-checker:ignore Anone bfloat
99

1010
mod byteorder_io;
1111
mod formatteriteminfo;
@@ -576,6 +576,10 @@ fn print_bytes(prefix: &str, input_decoder: &MemoryDecoder, output_info: &Output
576576
let p = input_decoder.read_float(b, f.formatter_item_info.byte_size);
577577
output_text.push_str(&func(p));
578578
}
579+
FormatWriter::BFloatWriter(func) => {
580+
let p = input_decoder.read_bfloat(b);
581+
output_text.push_str(&func(p));
582+
}
579583
FormatWriter::MultibyteWriter(func) => {
580584
output_text.push_str(&func(input_decoder.get_full_buffer(b)));
581585
}

src/uu/od/src/parse_formats.rs

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,10 @@ fn is_format_size_char(
235235
*byte_size = 8;
236236
true
237237
}
238+
(FormatTypeCategory::Float, Some('H' | 'B')) => {
239+
*byte_size = 2;
240+
true
241+
}
238242
// FormatTypeCategory::Float, 'L' => *byte_size = 16, // TODO support f128
239243
_ => false,
240244
}
@@ -290,7 +294,45 @@ fn parse_type_string(params: &str) -> Result<Vec<ParsedFormatterItemInfo>, Strin
290294

291295
let mut byte_size = 0u8;
292296
let mut show_ascii_dump = false;
293-
if is_format_size_char(ch, type_cat, &mut byte_size) {
297+
let mut float_variant = None;
298+
if type_cat == FormatTypeCategory::Float {
299+
match ch {
300+
Some(var @ ('B' | 'H')) => {
301+
byte_size = 2;
302+
float_variant = Some(var);
303+
ch = chars.next();
304+
}
305+
Some('F') => {
306+
byte_size = 4;
307+
ch = chars.next();
308+
}
309+
Some('D') => {
310+
byte_size = 8;
311+
ch = chars.next();
312+
}
313+
_ => {
314+
if is_format_size_char(ch, type_cat, &mut byte_size) {
315+
ch = chars.next();
316+
} else {
317+
let mut decimal_size = String::new();
318+
while is_format_size_decimal(ch, type_cat, &mut decimal_size) {
319+
ch = chars.next();
320+
}
321+
if !decimal_size.is_empty() {
322+
byte_size = decimal_size.parse().map_err(|_| {
323+
get_message_with_args(
324+
"od-error-invalid-number",
325+
HashMap::from([
326+
("number".to_string(), decimal_size.quote().to_string()),
327+
("spec".to_string(), params.quote().to_string()),
328+
]),
329+
)
330+
})?;
331+
}
332+
}
333+
}
334+
}
335+
} else if is_format_size_char(ch, type_cat, &mut byte_size) {
294336
ch = chars.next();
295337
} else {
296338
let mut decimal_size = String::new();
@@ -313,15 +355,23 @@ fn parse_type_string(params: &str) -> Result<Vec<ParsedFormatterItemInfo>, Strin
313355
ch = chars.next();
314356
}
315357

316-
let ft = od_format_type(type_char, byte_size).ok_or_else(|| {
317-
get_message_with_args(
318-
"od-error-invalid-size",
319-
HashMap::from([
320-
("size".to_string(), byte_size.to_string()),
321-
("spec".to_string(), params.quote().to_string()),
322-
]),
323-
)
324-
})?;
358+
let ft = if let Some(v) = float_variant {
359+
match v {
360+
'B' => FORMAT_ITEM_BF16,
361+
'H' => FORMAT_ITEM_F16,
362+
_ => unreachable!(),
363+
}
364+
} else {
365+
od_format_type(type_char, byte_size).ok_or_else(|| {
366+
get_message_with_args(
367+
"od-error-invalid-size",
368+
HashMap::from([
369+
("size".to_string(), byte_size.to_string()),
370+
("spec".to_string(), params.quote().to_string()),
371+
]),
372+
)
373+
})?
374+
};
325375
formats.push(ParsedFormatterItemInfo::new(ft, show_ascii_dump));
326376
}
327377

src/uu/od/src/prn_float.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ pub static FORMAT_ITEM_F64: FormatterItemInfo = FormatterItemInfo {
2525
formatter: FormatWriter::FloatWriter(format_item_flo64),
2626
};
2727

28+
pub static FORMAT_ITEM_BF16: FormatterItemInfo = FormatterItemInfo {
29+
byte_size: 2,
30+
print_width: 15,
31+
formatter: FormatWriter::BFloatWriter(format_item_bf16),
32+
};
33+
2834
pub fn format_item_flo16(f: f64) -> String {
2935
format!(" {}", format_flo16(f16::from_f64(f)))
3036
}
@@ -64,6 +70,10 @@ fn format_flo64_exp_precision(f: f64, width: usize, precision: usize) -> String
6470
formatted.replace('e', "e+")
6571
}
6672

73+
pub fn format_item_bf16(f: f64) -> String {
74+
format!(" {}", format_flo32(f as f32))
75+
}
76+
6777
fn format_flo16(f: f16) -> String {
6878
format_float(f64::from(f), 9, 4)
6979
}

tests/by-util/test_od.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,62 @@ fn test_f16() {
221221
.stdout_is(expected_output);
222222
}
223223

224+
#[test]
225+
fn test_fh() {
226+
let input: [u8; 14] = [
227+
0x00, 0x3c, // 0x3C00 1.0
228+
0x00, 0x00, // 0x0000 0.0
229+
0x00, 0x80, // 0x8000 -0.0
230+
0x00, 0x7c, // 0x7C00 Inf
231+
0x00, 0xfc, // 0xFC00 -Inf
232+
0x00, 0xfe, // 0xFE00 NaN
233+
0x00, 0x84,
234+
]; // 0x8400 -6.104e-5
235+
let expected_output = unindent(
236+
"
237+
0000000 1.000 0 -0 inf
238+
0000010 -inf NaN -6.104e-5
239+
0000016
240+
",
241+
);
242+
new_ucmd!()
243+
.arg("--endian=little")
244+
.arg("-tfH")
245+
.arg("-w8")
246+
.run_piped_stdin(&input[..])
247+
.success()
248+
.no_stderr()
249+
.stdout_is(expected_output);
250+
}
251+
252+
#[test]
253+
fn test_fb() {
254+
let input: [u8; 14] = [
255+
0x80, 0x3f, // 1.0
256+
0x00, 0x00, // 0.0
257+
0x00, 0x80, // -0.0
258+
0x80, 0x7f, // Inf
259+
0x80, 0xff, // -Inf
260+
0xc0, 0x7f, // NaN
261+
0x80, 0xb8,
262+
]; // -6.1035156e-5
263+
let expected_output = unindent(
264+
"
265+
0000000 1.0000000 0 -0 inf
266+
0000010 -inf NaN -6.1035156e-5
267+
0000016
268+
",
269+
);
270+
new_ucmd!()
271+
.arg("--endian=little")
272+
.arg("-tfB")
273+
.arg("-w8")
274+
.run_piped_stdin(&input[..])
275+
.success()
276+
.no_stderr()
277+
.stdout_is(expected_output);
278+
}
279+
224280
#[test]
225281
fn test_f32() {
226282
let input: [u8; 28] = [

0 commit comments

Comments
 (0)