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
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.

//! Wrapper for view rlp expected to be valid with debug info

use rlp::{Rlp, Decodable, DecoderError};

/// Wrapper for trusted rlp, which is expected to be valid, for use in views
/// When created with view!, records the file and line where it was created for debugging
pub struct ViewRlp<'a> {
	/// Wrapped Rlp, expected to be valid
	pub rlp: Rlp<'a>,
	file: &'a str,
	line: u32,
}

impl<'a, 'view> ViewRlp<'a> where 'a : 'view {
	#[doc(hidden)]
	pub fn new(bytes: &'a [u8], file: &'a str, line: u32) -> Self {
		ViewRlp {
			rlp: Rlp::new(bytes),
			file,
			line
		}
	}

	/// Returns a new instance replacing existing rlp with new rlp, maintaining debug info
	fn new_from_rlp(&self, rlp: Rlp<'a>) -> Self {
		ViewRlp {
			rlp,
			file: self.file,
			line: self.line
		}
	}

	fn maybe_at(&self, index: usize) -> Option<ViewRlp<'a>> {
		self.rlp.at(index)
			.map(|rlp| self.new_from_rlp(rlp))
			.ok()
	}

	fn expect_valid_rlp<T>(&self, r: Result<T, DecoderError>) -> T {
		r.unwrap_or_else(|e| panic!(
			"View rlp is trusted and should be valid. Constructed in {} on line {}: {}",
			self.file,
			self.line,
			e
		))
	}

	/// Returns rlp at the given index, panics if no rlp at that index
	pub fn at(&self, index: usize) -> ViewRlp<'a> {
		let rlp = self.expect_valid_rlp(self.rlp.at(index));
		self.new_from_rlp(rlp)
	}

	/// Returns an iterator over all rlp values
	pub fn iter(&'view self) -> ViewRlpIterator<'a, 'view> {
		self.into_iter()
	}

	/// Returns decoded value of this rlp, panics if rlp not valid
	pub fn as_val<T>(&self) -> T where T: Decodable {
		self.expect_valid_rlp(self.rlp.as_val())
	}

	/// Returns decoded value at the given index, panics not present or valid at that index
	pub fn val_at<T>(&self, index: usize) -> T where T : Decodable {
		self.expect_valid_rlp(self.rlp.val_at(index))
	}

	/// Returns decoded list of values, panics if rlp is invalid
	pub fn list_at<T>(&self, index: usize) -> Vec<T> where T: Decodable {
		self.expect_valid_rlp(self.rlp.list_at(index))
	}

	/// Returns the number of items in the rlp, panics if it is not a list of rlp values
	pub fn item_count(&self) -> usize {
		self.expect_valid_rlp(self.rlp.item_count())
	}

	/// Returns raw rlp bytes
	pub fn as_raw(&'view self) -> &'a [u8] {
		self.rlp.as_raw()
	}
}

/// Iterator over rlp-slice list elements.
pub struct ViewRlpIterator<'a, 'view> where 'a: 'view {
	rlp: &'view ViewRlp<'a>,
	index: usize,
}

impl<'a, 'view> IntoIterator for &'view ViewRlp<'a> where 'a: 'view {
	type Item = ViewRlp<'a>;
	type IntoIter = ViewRlpIterator<'a, 'view>;

	fn into_iter(self) -> Self::IntoIter {
		ViewRlpIterator {
			rlp: self,
			index: 0,
		}
	}
}

impl<'a, 'view> Iterator for ViewRlpIterator<'a, 'view> {
	type Item = ViewRlp<'a>;

	fn next(&mut self) -> Option<ViewRlp<'a>> {
		let index = self.index;
		let result = self.rlp.maybe_at(index);
		self.index += 1;
		result
	}
}

#[macro_export]
macro_rules! view {
	($view: ident, $bytes: expr) => {
		$view::new($crate::views::ViewRlp::new($bytes, file!(), line!()))
	};
}