I have a static array initialized with some constant value:
static PROG_ROM: [u8; 850] = [0x12, 0x1d, ...];
I would like to instead load at compile-time the contents of a file into it. Sounds like a job for std::include_bytes!
, however, I have two problems with it:
The type of
include_bytes!("foo.dat")
is&[u8; 850]
i.e. it is a reference. I need this to be a bonafide static array.Even if there was an
include_bytes_static!
macro with type[u8;850]
, I would have to use it like this:static PROG_ROM: [u8; 850] = include_bytes_static!("foo.dat");
I.e. I would have to hardcode the length of the file. Instead, I would like to take the length from the length of the file contents.
So the ideal replacement for my code would be a macro to replace the whole definition, i.e. look something like this:
define_included_bytes!(PROG_ROM, "foo.dat")
and it would expand to
static PROG_ROM: [u8; 850] = [0x12, 0x1d, ...];
So how do I do this?
CodePudding user response:
Use *include_bytes!(..)
to get a [u8; _]
instead of &[u8; _]
(since arrays implement Copy
), and use include_bytes!(..).len()
(which is a const method) to specify the length of the array in the type:
static PROG_ROM: [u8; include_bytes!("foo.dat").len()] = *include_bytes!("foo.dat");
CodePudding user response:
As Chayim Friedman pointed out you can easily define that proc macro yourself:
#[proc_macro]
fn define_included_bytes(token_stream: TokenStream) -> TokenStream {
let [ident, _, file] = &token_stream.into_iter().collect::<Vec<_>>()[..] else {
panic!("expected invocation: `define_included_bytes!(IDENTIFIER, \"file_name\");");
};
let file = file.to_string().trim_matches('\"').to_string();
let data: Vec<u8> = std::fs::read(&file).expect(&format!("File {:?} could not be read", file));
format!("const {ident}: [u8; {}] = {:?};", data.len(), data).parse().unwrap()
}
Obviously this is just a hacked together proof of concept and you should thouroughly check the tokens instead of just assuming they're correct.