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
//! provide trees based on bitstrings
#![warn(missing_docs)]
#![doc(html_root_url = "https://docs.rs/bitstring-trees/0.1.0")]

extern crate bitstring;

pub mod map;
pub mod set;

// sometimes one wants to destruct and re-construct a value, but only
// has a mutable reference.
//
// this method put uninitialized memory in place until there is a new
// value.
//
// if re-constructing the value panics we end up with a really fucked up
// memory state - we need to kill the process.
//
// use AssertUnwindSafe quite heavily internally - we abort anyway if
// something panics.
fn replace_at<T, F>(location: &mut T, with: F)
where
	T: Sized,
	F: FnOnce(T) -> T,
{
	use std::mem;
	use std::panic::*;
	use std::process;

	let with = AssertUnwindSafe(with);

	let old = AssertUnwindSafe(
		mem::replace(location, unsafe{mem::uninitialized()})
	);
	let new = catch_unwind(move || {
		AssertUnwindSafe(with.0(old.0))
	}).unwrap_or_else(move |_e| {
		// we're screwed, give up
		process::abort();
	});
	let tmp = mem::replace(location, new.0);
	mem::forget(tmp);
}

// similar to replace_at, but allow for a second chance through
// `fallback` to construct a value to restore the memory state to
// something sane - then we can continue unwinding the stack.
//
// use AssertUnwindSafe quite heavily internally - pulling UnwindSafe
// trait on all generics is quite annoying. so this is actually
// "unsafe".
fn replace_at_and_fallback<T, F, G>(location: &mut T, with: F, fallback: G)
where
	T: Sized,
	F: FnOnce(T) -> T,
	G: FnOnce() -> T,
{
	use std::mem;
	use std::panic::*;
	use std::process;

	let with = AssertUnwindSafe(with);
	let fallback = AssertUnwindSafe(fallback);

	let old = AssertUnwindSafe(
		mem::replace(location, unsafe{mem::uninitialized()})
	);
	let (new, panic_err) = catch_unwind(move || {
		(AssertUnwindSafe(with.0(old.0)), None)
	}).unwrap_or_else(move |e| {
		// remember panic so we can resume unwinding it
		// now give `fallback` a second chance to create a value
		let e = AssertUnwindSafe(e);
		catch_unwind(move || {
			(AssertUnwindSafe(fallback.0()), Some(e.0))
		}).unwrap_or_else(move |_e| {
			// if fallback panics too, give up
			process::abort();
		})
	});
	let tmp = mem::replace(location, new.0);
	mem::forget(tmp);
	if let Some(panic_err) = panic_err {
		resume_unwind(panic_err);
	}
}