1use alloc::borrow::ToOwned;
4use alloc::boxed::Box;
5use alloc::string::String;
6use core::cmp::Ordering;
7use core::fmt;
8use core::ops::Deref;
9use core::str;
10
11#[cfg(feature = "std")]
12use std::error;
13
14fn fmt_colon_delimited_hex<B>(f: &mut fmt::Formatter<'_>, bytes: B) -> fmt::Result
19where
20 B: AsRef<[u8]>,
21{
22 let len = bytes.as_ref().len();
23
24 for (i, byte) in bytes.as_ref().iter().enumerate() {
25 write!(f, "{:02x}", byte)?;
26
27 if i != len - 1 {
28 write!(f, ":")?;
29 }
30 }
31
32 Ok(())
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
37pub enum Error {
38 InvalidUtf8(core::str::Utf8Error),
39 TooLong(usize),
40}
41
42impl fmt::Display for Error {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self {
45 Error::InvalidUtf8(e) => write!(f, "Invalid UTF-8: {}", e),
46 Error::TooLong(n) => write!(f, "Memo length {} is larger than maximum of 512", n),
47 }
48 }
49}
50
51#[cfg(feature = "std")]
52impl error::Error for Error {}
53
54#[derive(Clone)]
56pub struct MemoBytes(pub(crate) Box<[u8; 512]>);
57
58impl fmt::Debug for MemoBytes {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 write!(f, "MemoBytes(")?;
61 fmt_colon_delimited_hex(f, &self.0[..])?;
62 write!(f, ")")
63 }
64}
65
66impl PartialEq for MemoBytes {
67 fn eq(&self, rhs: &MemoBytes) -> bool {
68 self.0[..] == rhs.0[..]
69 }
70}
71
72impl Eq for MemoBytes {}
73
74impl PartialOrd for MemoBytes {
75 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
76 Some(self.cmp(other))
77 }
78}
79
80impl Ord for MemoBytes {
81 fn cmp(&self, rhs: &Self) -> Ordering {
82 self.0[..].cmp(&rhs.0[..])
83 }
84}
85
86impl MemoBytes {
87 pub fn empty() -> Self {
89 let mut bytes = [0u8; 512];
90 bytes[0] = 0xF6;
91 MemoBytes(Box::new(bytes))
92 }
93
94 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
104 if bytes.len() > 512 {
105 return Err(Error::TooLong(bytes.len()));
106 }
107
108 let mut memo = [0u8; 512];
109 memo[..bytes.len()].copy_from_slice(bytes);
110 Ok(MemoBytes(Box::new(memo)))
111 }
112
113 pub fn as_array(&self) -> &[u8; 512] {
115 &self.0
116 }
117
118 pub fn into_bytes(self) -> [u8; 512] {
120 *self.0
121 }
122
123 pub fn as_slice(&self) -> &[u8] {
125 let first_null = self
126 .0
127 .iter()
128 .enumerate()
129 .rev()
130 .find(|(_, &b)| b != 0)
131 .map(|(i, _)| i + 1)
132 .unwrap_or_default();
133
134 &self.0[..first_null]
135 }
136}
137
138#[derive(Clone, PartialEq, Eq)]
140pub struct TextMemo(String);
141
142impl From<TextMemo> for String {
143 fn from(memo: TextMemo) -> String {
144 memo.0
145 }
146}
147
148impl Deref for TextMemo {
149 type Target = str;
150
151 #[inline]
152 fn deref(&self) -> &str {
153 self.0.deref()
154 }
155}
156
157#[derive(Clone, Default)]
159pub enum Memo {
160 #[default]
162 Empty,
163 Text(TextMemo),
165 Future(MemoBytes),
167 Arbitrary(Box<[u8; 511]>),
169}
170
171impl fmt::Debug for Memo {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 match self {
174 Memo::Empty => write!(f, "Memo::Empty"),
175 Memo::Text(memo) => write!(f, "Memo::Text(\"{}\")", memo.0),
176 Memo::Future(bytes) => write!(f, "Memo::Future({:0x})", bytes.0[0]),
177 Memo::Arbitrary(bytes) => {
178 write!(f, "Memo::Arbitrary(")?;
179 fmt_colon_delimited_hex(f, &bytes[..])?;
180 write!(f, ")")
181 }
182 }
183 }
184}
185
186impl PartialEq for Memo {
187 fn eq(&self, rhs: &Memo) -> bool {
188 match (self, rhs) {
189 (Memo::Empty, Memo::Empty) => true,
190 (Memo::Text(a), Memo::Text(b)) => a == b,
191 (Memo::Future(a), Memo::Future(b)) => a.0[..] == b.0[..],
192 (Memo::Arbitrary(a), Memo::Arbitrary(b)) => a[..] == b[..],
193 _ => false,
194 }
195 }
196}
197
198impl TryFrom<MemoBytes> for Memo {
199 type Error = Error;
200
201 fn try_from(bytes: MemoBytes) -> Result<Self, Self::Error> {
206 Self::try_from(&bytes)
207 }
208}
209
210impl TryFrom<&MemoBytes> for Memo {
211 type Error = Error;
212
213 fn try_from(bytes: &MemoBytes) -> Result<Self, Self::Error> {
218 match bytes.0[0] {
219 0xF6 if bytes.0.iter().skip(1).all(|&b| b == 0) => Ok(Memo::Empty),
220 0xFF => Ok(Memo::Arbitrary(Box::new(bytes.0[1..].try_into().unwrap()))),
221 b if b <= 0xF4 => str::from_utf8(bytes.as_slice())
222 .map(|r| Memo::Text(TextMemo(r.to_owned())))
223 .map_err(Error::InvalidUtf8),
224 _ => Ok(Memo::Future(bytes.clone())),
225 }
226 }
227}
228
229impl From<Memo> for MemoBytes {
230 fn from(memo: Memo) -> Self {
232 match memo {
233 Memo::Future(memo) => memo,
235 memo => (&memo).into(),
236 }
237 }
238}
239
240impl From<&Memo> for MemoBytes {
241 fn from(memo: &Memo) -> Self {
243 match memo {
244 Memo::Empty => MemoBytes::empty(),
245 Memo::Text(s) => {
246 let mut bytes = [0u8; 512];
247 let s_bytes = s.0.as_bytes();
248 bytes[..s_bytes.len()].copy_from_slice(s_bytes);
250 MemoBytes(Box::new(bytes))
251 }
252 Memo::Future(memo) => memo.clone(),
253 Memo::Arbitrary(arb) => {
254 let mut bytes = [0u8; 512];
255 bytes[0] = 0xFF;
256 bytes[1..].copy_from_slice(arb.as_ref());
257 MemoBytes(Box::new(bytes))
258 }
259 }
260 }
261}
262
263impl Memo {
264 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
269 MemoBytes::from_bytes(bytes).and_then(TryFrom::try_from)
270 }
271
272 pub fn encode(&self) -> MemoBytes {
274 self.into()
275 }
276}
277
278impl str::FromStr for Memo {
279 type Err = Error;
280
281 fn from_str(memo: &str) -> Result<Self, Self::Err> {
283 if memo.is_empty() {
284 Ok(Memo::Empty)
285 } else if memo.len() <= 512 {
286 Ok(Memo::Text(TextMemo(memo.to_owned())))
287 } else {
288 Err(Error::TooLong(memo.len()))
289 }
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use alloc::boxed::Box;
296 use alloc::str::FromStr;
297
298 use super::{Error, Memo, MemoBytes};
299
300 #[test]
301 fn memo_from_str() {
302 assert_eq!(
303 Memo::from_str("").unwrap().encode(),
304 MemoBytes(Box::new([
305 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
306 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
307 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
308 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
309 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
310 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
311 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
312 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
313 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
314 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
315 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
316 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
317 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
318 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
319 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
320 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
321 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
322 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
323 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
324 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
325 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
327 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
328 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
329 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
330 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
331 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
332 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
333 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
334 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
335 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
336 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
337 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
338 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
339 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
340 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
341 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
342 ]))
343 );
344 assert_eq!(
345 Memo::from_str(
346 "thiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \
347 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \
348 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
349 veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeryyyyyyyyyyyyyyyyyyyyyyyyyy \
350 looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong \
351 meeeeeeeeeeeeeeeeeeemooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo \
352 but it's just short enough"
353 )
354 .unwrap()
355 .encode(),
356 MemoBytes(Box::new([
357 0x74, 0x68, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
358 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
359 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
360 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
361 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
362 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x73, 0x20, 0x69, 0x69, 0x69,
363 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
364 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
365 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
366 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
367 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
368 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x73, 0x20, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
369 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
370 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
371 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
372 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
373 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
374 0x61, 0x61, 0x61, 0x61, 0x20, 0x76, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
375 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
376 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
377 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
378 0x65, 0x65, 0x72, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
379 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
380 0x79, 0x20, 0x6c, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
381 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
382 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
383 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
384 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
385 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6e, 0x67, 0x20, 0x6d,
386 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
387 0x65, 0x65, 0x65, 0x65, 0x65, 0x6d, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
388 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
389 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
390 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
391 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x20, 0x62, 0x75, 0x74, 0x20,
392 0x69, 0x74, 0x27, 0x73, 0x20, 0x6a, 0x75, 0x73, 0x74, 0x20, 0x73, 0x68, 0x6f, 0x72,
393 0x74, 0x20, 0x65, 0x6e, 0x6f, 0x75, 0x67, 0x68
394 ]))
395 );
396 assert_eq!(
397 Memo::from_str(
398 "thiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \
399 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \
400 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
401 veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeryyyyyyyyyyyyyyyyyyyyyyyyyy \
402 looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong \
403 meeeeeeeeeeeeeeeeeeemooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo \
404 but it's now a bit too long"
405 ),
406 Err(Error::TooLong(513))
407 );
408 }
409
410 #[test]
411 fn future_memo() {
412 let bytes = [0xFE; 512];
413 assert_eq!(
414 MemoBytes::from_bytes(&bytes).unwrap().try_into(),
415 Ok(Memo::Future(MemoBytes(Box::new(bytes))))
416 );
417 }
418
419 #[test]
420 fn arbitrary_memo() {
421 let bytes = [42; 511];
422 let memo = Memo::Arbitrary(Box::new(bytes));
423 let raw = memo.encode();
424 let encoded = raw.as_array();
425 assert_eq!(encoded[0], 0xFF);
426 assert_eq!(encoded[1..], bytes[..]);
427 assert_eq!(MemoBytes::from_bytes(encoded).unwrap().try_into(), Ok(memo));
428 }
429}