1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#![macro_use]
/// A handly macro for quick and dirty debugging. It reports the caller location
/// in the form of file plus line, and it also supports `format!` -like arguments.
#[allow(unused_macros)]
macro_rules! dbglog {
($($arg:tt)*) => {{
if std::cfg!(debug_assertions) {
std::eprintln!("[{}:{}] {}", std::file!(), std::line!(), std::format!($($arg)*));
}
}}
}
/// Utility functions for field validation.
pub mod validation {
/// Validates that all values are within their respective ranges.
pub const fn validate_time_components(
hour: u32,
minute: u32,
second: u32,
milli: u32,
) -> bool {
hour <= 23 && minute <= 59 && second <= 60 && milli <= 999
}
}
/// Utility functions for FIX field encoding.
pub mod encoding {
/// Converts a number to ASCII digits with zero-padding.
///
/// # Examples
///
/// ```
/// # use minifix::utils::encoding::digits_to_ascii;
/// assert_eq!(digits_to_ascii::<4>(42), [b'0', b'0', b'4', b'2']);
/// assert_eq!(digits_to_ascii::<3>(123), [b'1', b'2', b'3']);
/// ```
pub const fn digits_to_ascii<const N: usize>(mut num: u32) -> [u8; N] {
let mut result = [b'0'; N];
let mut i = N;
if num == 0 {
return result;
}
while num > 0 && i > 0 {
i -= 1;
result[i] = (num % 10) as u8 + b'0';
num /= 10;
}
result
}
/// Converts a two-digit number to ASCII.
pub const fn two_digits_to_ascii(num: u32) -> [u8; 2] {
digits_to_ascii::<2>(num)
}
/// Converts a four-digit number to ASCII.
pub const fn four_digits_to_ascii(num: u32) -> [u8; 4] {
digits_to_ascii::<4>(num)
}
/// Checks if a byte represents an ASCII digit (0-9).
pub const fn is_ascii_digit(byte: u8) -> bool {
byte >= b'0' && byte <= b'9'
}
/// Converts an ASCII digit to u32 with a multiplier.
/// This function assumes the input byte is a valid ASCII digit.
pub const fn ascii_digit_to_u32(digit: u8, multiplier: u32) -> u32 {
(digit as u32).wrapping_sub(b'0' as u32) * multiplier
}
}
/// Utility functions for parsing byte sequences.
pub mod parsing {
use memchr::memchr;
/// Finds the position of a byte within a slice, starting from a given offset.
pub fn find_byte_from(
data: &[u8],
byte: u8,
offset: usize,
) -> Option<usize> {
if offset >= data.len() {
return None;
}
memchr(byte, &data[offset..]).map(|pos| pos + offset)
}
/// Finds the next equal sign '=' in the data starting from an offset.
pub fn find_equal_sign_from(data: &[u8], offset: usize) -> Option<usize> {
find_byte_from(data, b'=', offset)
}
/// Finds the next separator byte in the data starting from an offset.
pub fn find_separator_from(
data: &[u8],
separator: u8,
offset: usize,
) -> Option<usize> {
find_byte_from(data, separator, offset)
}
/// Parses a decimal number from ASCII digits in a byte slice.
pub fn parse_decimal_ascii(data: &[u8]) -> Option<u32> {
let mut result = 0u32;
for &byte in data {
if byte < b'0' || byte > b'9' {
return None;
}
result =
result.wrapping_mul(10).wrapping_add((byte - b'0') as u32);
}
Some(result)
}
}