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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! This crate provides safe but low-level for the Wasm imports available in the OC-Wasm
//! environment.

#![cfg_attr(not(feature = "std"), no_std)]
#![warn(
	// Turn on extra language lints.
	future_incompatible,
	missing_abi,
	nonstandard_style,
	rust_2018_idioms,
	// Disabled due to <https://github.com/rust-lang/rust/issues/69952>.
	// single_use_lifetimes,
	trivial_casts,
	trivial_numeric_casts,
	unused,
	unused_crate_dependencies,
	unused_import_braces,
	unused_lifetimes,
	unused_qualifications,

	// Turn on extra Rustdoc lints.
	rustdoc::all,

	// Turn on extra Clippy lints.
	clippy::cargo,
	clippy::pedantic,
)]

pub mod component;
pub mod computer;
pub mod descriptor;
pub mod error;
pub mod execute;

use core::fmt::{Display, Formatter};
use core::str::FromStr;
use minicbor::{
	data::{Tag, Type},
	decode, encode,
};
use uuid::Uuid;

/// A component address.
///
/// This is just a UUID. It supports `minicbor`. When encoding, it encodes as a byte string tagged
/// with the Identifier (39) tag. When decoding, it decodes from an optional Identifier (39) tag
/// followed by either a byte string or a UTF-8 string.
#[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct Address(Uuid);

impl Address {
	#[must_use = "This function is only useful for its return value"]
	pub const fn as_bytes(&self) -> &[u8; 16] {
		self.0.as_bytes()
	}

	#[must_use = "This function is only useful for its return value"]
	pub const fn from_bytes(b: [u8; 16]) -> Self {
		Self(Uuid::from_bytes(b))
	}
}

impl Display for Address {
	fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
		self.0.fmt(f)
	}
}

impl FromStr for Address {
	type Err = <Uuid as FromStr>::Err;

	fn from_str(s: &str) -> Result<Self, Self::Err> {
		Ok(Self(<Uuid as FromStr>::from_str(s)?))
	}
}

impl decode::Decode<'_> for Address {
	fn decode(d: &mut decode::Decoder<'_>) -> Result<Self, decode::Error> {
		use core::convert::TryInto;

		// Check the datatype of the next item.
		let mut datatype = d.datatype()?;

		// If it’s a tag, it must have value 39 (Identifier), and the tagged value is the UUID.
		if datatype == Type::Tag {
			let tag = d.tag()?;
			if tag != Tag::Unassigned(39) {
				return Err(decode::Error::Message("expected tag 39"));
			}
			datatype = d.datatype()?;
		}

		// Now the value must appear as either byte string holding a binary UUID or a UTF-8 string
		// holding a text UUID. Indefinite forms do not need to be supported as OC-Wasm never
		// generates them in data passed to the Wasm module instance.
		match datatype {
			Type::Bytes => {
				let b = d.bytes()?;
				Ok(Self::from_bytes(b.try_into().map_err(|_| {
					decode::Error::Message("expected 16 bytes")
				})?))
			}
			Type::String => {
				let s = d.str()?;
				Ok(
					Self::from_str(s)
						.map_err(|_| decode::Error::Message("expected UUID string"))?,
				)
			}
			_ => Err(decode::Error::Message("expected byte or UTF-8 string")),
		}
	}
}

impl encode::Encode for Address {
	fn encode<W: encode::Write>(
		&self,
		e: &mut encode::Encoder<W>,
	) -> Result<(), encode::Error<W::Error>> {
		e.tag(Tag::Unassigned(39))?.bytes(self.as_bytes())?;
		Ok(())
	}
}

/// Panics or traps depending on the state of the `panic` feature.
///
/// If the `panic` feature is enabled, this macro panics with the given message. If it is disabled,
/// this macro invokes Wasm `UNREACHABLE` (trap) instruction, instantly terminating execution; the
/// message is ignored.
#[cfg(feature = "panic")]
#[macro_export]
macro_rules! panic_or_trap {
	($message: literal) => {
		core::panic!($message)
	};
}

/// Panics or traps depending on the state of the `panic` feature.
///
/// If the `panic` feature is enabled, this macro panics with the given message. If it is disabled,
/// this macro invokes Wasm `UNREACHABLE` (trap) instruction, instantly terminating execution; the
/// message is ignored.
#[cfg(not(feature = "panic"))]
#[macro_export]
macro_rules! panic_or_trap {
	($message: literal) => {
		unsafe { core::arch::wasm32::unreachable() }
	};
}

mod helpers;