How can i convert this match statement into a HashMap? - rust

How could I convert this match block into a HashMap? Ideally I would like to create a HashMap from a manifest file:
match layers[1][p] {
',' => tile = grass,
'*' => tile = sand,
// And so on....
}
I tried doing this earlier, but Macroquad's Texture2D is not compatible with a HashMap. (I think the compiler said 'Cannot use Option as Texture2D'.)
I would like to do something like this:
let tile = hashmap.get(layers[p]);
blit(tile);

Let's keep this simple. I don't think hashmap will simplify anything but why not demonstrate it. The true advantage of hash is that you can load tiles dynamically and make them loaded into the map. For now, though, we will hardcode some.
fn main() {
/* snip */
let mut textures = HashMap::new();
textures.insert(',', grass);
textures.insert('*', sand);
/* snip */
// mind that your program will crash if map does
// not contain the texture
let texture = textures.get(layers[p]).unwrap();
blit(texture)
}

Related

include_str for null terminated string

I need to read a file into a null terminated string at compile time.
Working in Rust OpenGL. I have a shader source code stored in a separate file. The function that will eventually read the source is gl::ShaderSource from the gl crate. All it needs is a pointer to a null terminated string (the std::ffi::CStr type).
Typically the guides I have seen read the shader source file using include_str!, then at run time allocate a whole new buffer of length +1, then copy the original source into the new buffer and put the terminating 0 at the end. I'd like to avoid all that redundant allocating and copying and just have the correctly null terminated string at compile time.
I realize it is somewhat petty to want to avoid an extra allocation for a short shader file, but the principle could apply to many other types of larger constants.
While scrolling through suggested questions during the preview I saw this: How do I expose a compile time generated static C string through FFI?
which led me to this solution:
let bytes1 = concat!(include_str!("triangle.vertex_shader"), "\0");
let bytes2 = bytes1.as_bytes();
let bytes3 = unsafe {
CStr::from_bytes_with_nul_unchecked(bytes2)
};
println!("{:?}", bytes3);
Does this accomplish avoiding the runtime allocation and copying?
Your code is unsound. It fails to verify there are no interior NUL bytes.
You can use the following function to validate the string (at compile time, with no runtime cost):
pub const fn to_cstr(s: &str) -> &CStr {
let bytes = s.as_bytes();
let mut i = 0;
while i < (bytes.len() - 1) {
assert!(bytes[i] != b'\0', "interior byte cannot be NUL");
i += 1;
}
assert!(bytes[bytes.len() - 1] == b'\0', "last byte must be NUL");
// SAFETY: We verified there are no interior NULs and that the string ends with NUL.
unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }
}
Wrap it in a little nice macro:
macro_rules! include_cstr {
( $path:literal $(,)? ) => {{
// Use a constant to force the verification to run at compile time.
const VALUE: &'static ::core::ffi::CStr = $crate::to_cstr(concat!(include_str!($path), "\0"));
VALUE
}};
}
Then use it:
let bytes = include_cstr!("triangle.vertex_shader");
If there are interior NUL bytes the code will fail to compile.
When CStr::from_bytes_with_nul() becomes const-stable, you will be able to replace to_cstr() with it.
Yes that should work as intended. If you want you can even bundle it into a simple macro.
macro_rules! include_cstr {
($file:expr) => {{
// Create as explicit constant to force from_bytes_with_nul_unchecked to
// perform compile time saftey checks.
const CSTR: &'static ::std::ffi::CStr = unsafe {
let input = concat!($file, "\0");
::std::ffi::CStr::from_bytes_with_nul_unchecked(input.as_bytes())
};
CSTR
}};
}
const VERTEX_SHADER: &'static CStr = include_cstr!("shaders/vert.glsl");
const FRAGMENT_SHADER: &'static CStr = include_cstr!("shaders/frag.glsl");

Initialize Gstreamer's PadProbeId to a default value in Rust

I am familiar with Gstreamer but new to Rust,
TLDR; I want to be able to initialize PadProbeId to a default value before using it.
The details:
I have a Bin (containing audio + video encoders and hlssink).
I have been able to add this bin to the pipeline and it works fine.
The issue I have is the audio for the stream is optional and I want to do add_probe() only when audio is available. Below is a simplified version fo what I tried to implement
let mut audio_probe_id: PadProbeId;
let mut tee_audio_pad: Pad;
if media_info.audio_available {
// get encoded audio from the tee
tee_audio_pad = audio_tee.request_pad_simple("src_%u").unwrap();
audio_probe_id = tee_audio_pad.add_probe(gst::PadProbeType::BLOCK_DOWNSTREAM, |_pad, _info| {
gst::PadProbeReturn::Ok
}).unwrap();
// link the audio_tee.src to enc_bin ghost pad
let audio_sink_pad = enc_bin.static_pad("audio").unwrap();
tee_audio_pad.link(&audio_sink_pad).unwrap();
}
enc_bin.call_async(move |bin| {
bin.sync_state_with_parent().unwrap();
if media_info.audio_available {
tee_audio_pad.remove_probe(audio_probe_id);
}
}
However because of Rust compilers restriction to using uninitialized variables, it does not let me use audio_probe_id without initializing.
I tried to initialize it like this; let mut audio_probe_id: PadProbeId = PadProbeId(NonZeroU64(u64::MAX));. However compiler complains that it is a private field.
error[E0423]: cannot initialize a tuple struct which contains private fields
Thanks a lot for your help!
The rust way to have empty variables like this is to use Option, but in your case it would simpler to have a single conditional:
if media_info.audio_available {
// get encoded audio from the tee
let tee_audio_pad = audio_tee.request_pad_simple("src_%u").unwrap();
let audio_probe_id = tee_audio_pad.add_probe(gst::PadProbeType::BLOCK_DOWNSTREAM, |_pad, _info| {
gst::PadProbeReturn::Ok
}).unwrap();
// link the audio_tee.src to enc_bin ghost pad
let audio_sink_pad = enc_bin.static_pad("audio").unwrap();
tee_audio_pad.link(&audio_sink_pad).unwrap();
enc_bin.call_async(move |bin| {
bin.sync_state_with_parent().unwrap();
tee_audio_pad.remove_probe(audio_probe_id);
}
} else {
enc_bin.call_async(move |bin| {
bin.sync_state_with_parent().unwrap();
});
}

Cannot get Hash::get_mut() and File::open() to agree about mutability

During a lengthy computation, I need to look up some data in a number of different files. I cannot know beforehand how many or which files exactly, but chances are high that each file is used many times (on the order of 100 million times).
In the first version, I opened the file (whose name is an intermediate result of the computation) each time for lookup.
In the second version, I have a HashMap<String, Box<File>> where I remember already open files and open new ones lazily on demand.
I couldn't manage to handle the mutable stuff that arises from the need to have Files to be mutable. I got something working, but it looks overly silly:
let path = format!("egtb/{}.egtb", self.signature());
let hentry = hash.get_mut(&self.signature());
let mut file = match hentry {
Some(f) => f,
None => {
let rfile = File::open(&path);
let wtf = Box::new(match rfile {
Err(ioe) => return Err(format!("could not open EGTB file {} ({})", path, ioe)),
Ok(opened) => opened,
});
hash.insert(self.signature(), wtf);
// the following won't work
// wtf
// &wtf
// &mut wtf
// So I came up with the following, but it doesn't feel right, does it?
hash.get_mut(&self.signature()).unwrap()
}
};
Is there a canonical way to get a mut File from File::open() or File::create()? In the manuals, this is always done with:
let mut file = File:open("foo.txt")?;
This means my function would have to return Result<_, io::Error> and I can't have that.
The problem seems to be that with the hash-lookup Some(f) gives me a &mut File but the Ok(f) from File::open gives me just a File, and I don't know how to make a mutable reference from that, so that the match arm's types match. I have no clear idea why the version as above at least compiles, but I'd very much like to learn how to do that without getting the File from the HashMap again.
Attempts to use wtf after it has been inserted into the hashmap fail to compile because the value was moved into the hashmap. Instead, you need to obtain the reference into the value stored in the hashmap. To do so without a second lookup, you can use the entry API:
let path = format!("egtb/{}.egtb", self.signature());
let mut file = match hash.entry(self.signature()) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => {
let rfile = File::open(&path)
.map_err(|_| format!("could not open EGTB file {} ({})", path, ioe))?;
e.insert(Box::new(rfile))
}
};
// `file` is `&mut File`, use it as needed
Note that map_err() allows you to use ? even when your function returns a Result not immediately compatible with the one you have.
Also note that there is no reason to box the File, a HashMap<String, File> would work just as nicely.

What is the proper way to consume pieces of a slice in a loop when the piece size can change each iteration?

I need to process a slice of bytes into fixed chunks, but the chunk pattern is only known at runtime:
pub fn process(mut message: &[u8], pattern: &[Pattern]) {
for element in pattern: {
match element {
(...) => {
let (chunk, message2) = message.split_at(element.size);
/* process chunk */
message = message2;
},
// ...
}
}
}
It feels awkward to have to use this message2. But if I do
let (chunk, message) = message.split_at(element.size);
Then it does not work, I assume because this actually creates a new messages binding that goes out of scope between loop iterations.
Is there a more elegant way to do this?
You are correct in your reasoning that let (chunk, message) = message.split_at(element.size); creates a new binding message within that scope and does not update the outer message value.
What you are looking for is a 'destructuring assignment' of the tuple. This would allow tuple elements to be assigned to existing variable bindings instead of creating new bindings, something like:
let chunk;
(chunk, message) = message.split_at(element.size);
Unfortunately this is currently not possible in Rust. You can see a pre-RFC which proposes to add destructuring assignment to the Rust language.
I believe what you currently have is a perfectly fine solution, perhaps rename message2 to something like rest_of_message or remaining_message.

How do I parse a page with html5ever, modify the DOM, and serialize it?

I would like to parse a web page, insert anchors at certain positions and render the modified DOM out again in order to generate docsets for Dash. Is this possible?
From the examples included in html5ever, I can see how to read an HTML file and do a poor man's HTML output, but I don't understand how I can modify the RcDom object I retrieved.
I would like to see a snippet inserting an anchor element (<a name="foo"></a>) to an RcDom.
Note: this is a question regarding Rust and html5ever specifically ... I know how to do it in other languages or simpler HTML parsers.
Here is some code that parses a document, adds an achor to the link and prints the new document:
extern crate html5ever;
use html5ever::{ParseOpts, parse_document};
use html5ever::tree_builder::TreeBuilderOpts;
use html5ever::rcdom::RcDom;
use html5ever::rcdom::NodeEnum::Element;
use html5ever::serialize::{SerializeOpts, serialize};
use html5ever::tendril::TendrilSink;
fn main() {
let opts = ParseOpts {
tree_builder: TreeBuilderOpts {
drop_doctype: true,
..Default::default()
},
..Default::default()
};
let data = "<!DOCTYPE html><html><body></body></html>".to_string();
let dom = parse_document(RcDom::default(), opts)
.from_utf8()
.read_from(&mut data.as_bytes())
.unwrap();
let document = dom.document.borrow();
let html = document.children[0].borrow();
let body = html.children[1].borrow(); // Implicit head element at children[0].
{
let mut a = body.children[0].borrow_mut();
if let Element(_, _, ref mut attributes) = a.node {
attributes[0].value.push_tendril(&From::from("#anchor"));
}
}
let mut bytes = vec![];
serialize(&mut bytes, &dom.document, SerializeOpts::default()).unwrap();
let result = String::from_utf8(bytes).unwrap();
println!("{}", result);
}
This prints the following:
<html><head></head><body></body></html>
As you can see, we can navigate through the child nodes via the children attribute.
And we can change an attribute present in the vector of attributes of an Element.

Resources