Good day all. I am having some trouble extracting a slice from a byte array in a form that I can then use. Defining the slice is easy enough but I cannot find the correct types to assign it to. The function takes a byte buffer and looks for a string of dashes as a start marker for the data. It then needs to extract the bytes/chars/str(s)/strings at various offsets from the first dash. In one case I need to get an indexable type as I need to further process it byte by byte, and in the other cases a string or str is required. The code has some of my attempts at it. Any suggestions would be appreciated. Thanks
fn zebra_format(in_buf: &[u8]) -> String {
let mut done: bool = false;
let mut position: usize = 0;
while done == false {
while position < in_buf.len() - 5 {
if in_buf[position] == 0x2D && in_buf[position + 1] == 0x2D && in_buf[position + 2] == 0x2D
&& in_buf[position + 3] == 0x2D && in_buf[position + 4] == 0x2D {
break;
}
position += 1;
}
let mut date = String::new();
date = date + in_buf[position + 49..11];
let trans: [u8] = in_buf[position + 121..8];
let trans: [u8] = &in_buf[position + 121..8];
let acct: str = in_buf[position + 247..40];
let prod: [u8; 38] = in_buf[position + 348..38];
//some logic to determine if at end of in_data.
done = true;
}
"abc\r\n".to_string()
}
A slice (e.g. [u8]) always needs to be behind some kind of reference, so you should be writing things like let trans = &in_buf[5..2]. It's the same with a str, you always write &str because it represents a pointer to some string data elsewhere (in_buf in this case).
Also, I don't think expressions like position + 49..11 do what you think they do. The syntax 49..11 will create a range starting at the 49'th byte going forward to the 11'th byte... Which is an empty range because the end is before the start.
Your code could probably be written a lot more expressively, but without knowing what all those byte offsets mean and how they were determined I can't show you how I'd write the parser.
One way you could change your code to fix the compile errors:
fn zebra_format(data: &[u8]) -> String {
if let Some(payload) = find_marker(data) {
let date: &str = std::str::from_str(&payload[49..49+11]).expect("bad date");
let trans: &[u8] = &payload[121..121+8];
let acct: &[u8] = &payload[247..247+40];
let prod: &[u8] = &payload[348..348 + 38];
todo!("Put the parsed data into a struct so we can do something with it")
}
todo!("Handle the possibility that we couldn't find a marker")
}
/// Scans through `data` to find an occurrence of our marker, then return
/// a pointer to the bytes after it.
fn find_marker(data: &[u8]) -> Option<&[u8]> {
let marker = b"-----";
for i in 0..data.len() {
let possible_marker = &data[i..i + marker.len()];
if possible_marker == marker {
let rest = &data[i + marker.len()..];
return Some(rest);
}
}
None
}
@Michael-F-Bryan Thank you for the reply. As I read the slice documentation in the book it said that the second value was the length, not the ending index. e.g [start_idx..legth]. I will change up the slice calls and reformat some things as you suggest and see what happens. Have a good day.
It was probably saying that a reference to a slice (i.e. &[u8]) consists of a pointer to some bytes and the length. You can think of &[T] being syntactic sugar for this:
Then when you index into a &[T] you pass it a range expression indicating the start and end indices you want. It'll then do the pointer arithmetic to return a new slice with an updated first_element pointer and length field (with bounds checks, of course).
The syntax a..b is shorthand for creating a std::ops::Range<usize>, which is defined as.
@Michael-F-Bryan That makes sense now. I hadn't clued in to the Range piece. I have changed the calls and they are now working as expected. [(position + 49)..60]. Thanks again.