typed_chunk.rs 7.07 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of pDSL.
//
// pDSL 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.
//
// pDSL 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 pDSL.  If not, see <http://www.gnu.org/licenses/>.

17
18
19
20
21
22
use crate::{
	storage::{
		Key,
		NonCloneMarker,
		chunk::{
			RawChunk,
23
			RawChunkCell,
24
		},
25
		Allocator,
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
	},
};

/// A chunk of typed cells.
///
/// Provides interpreted access with offset to the associated contract storage slot.
///
/// # Guarantees
///
/// - `Owned`
/// - `Typed`
///
/// Read more about kinds of guarantees and their effect [here](../index.html#guarantees).
#[derive(Debug, PartialEq, Eq)]
pub struct TypedChunk<T> {
	/// The underlying chunk of cells.
	chunk: RawChunk,
	/// Marker that prevents this type from being `Copy` or `Clone` by accident.
	non_clone: NonCloneMarker<T>,
}

47
48
49
50
51
52
53
54
55
56
57
/// A single cell within a chunk of typed cells.
#[derive(Debug, PartialEq, Eq)]
pub struct TypedChunkCell<'a, T> {
	/// The underlying cell within the chunk of cells.
	cell: RawChunkCell<'a>,
	/// Marker that prevents this type from being `Copy` or `Clone` by accident.
	non_clone: NonCloneMarker<T>,
}

impl<'a, T> TypedChunkCell<'a, T> {
	/// Creates a new raw chunk cell from the given key.
58
59
60
61
	///
	/// # Safety
	///
	/// This is unsafe since it doesn't check aliasing of cells.
62
63
64
65
66
67
68
	pub(self) unsafe fn new_unchecked(cell: RawChunkCell<'a>) -> Self {
		Self{
			cell,
			non_clone: NonCloneMarker::default()
		}
	}

69
	/// Removes the value stored in this cell.
70
71
72
73
74
75
76
77
78
	pub fn clear(&mut self) {
		self.cell.clear()
	}
}

impl<'a, T> TypedChunkCell<'a, T>
where
	T: parity_codec::Decode
{
79
80
81
82
83
	/// Loads the value stored in the cell if any.
	///
	/// # Panics
	///
	/// If decoding of the loaded bytes fails.
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
	pub fn load(&self) -> Option<T> {
		self
			.cell
			.load()
			.map(|loaded| {
				T::decode(&mut &loaded[..])
					// Maybe we should return an error instead of panicking.
					.expect(
						"[pdsl_core::TypedChunkCell::load] Error: \
						 failed upon decoding"
					)
			})
	}
}

impl<'a, T> TypedChunkCell<'a, T>
where
	T: parity_codec::Encode
{
103
	/// Stores the value into the cell.
104
105
106
107
108
	pub fn store(&mut self, val: &T) {
		self.cell.store(&T::encode(val))
	}
}

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
impl<T> parity_codec::Encode for TypedChunk<T> {
	fn encode_to<W: parity_codec::Output>(&self, dest: &mut W) {
		self.chunk.encode_to(dest)
	}
}

impl<T> parity_codec::Decode for TypedChunk<T> {
	fn decode<I: parity_codec::Input>(input: &mut I) -> Option<Self> {
		RawChunk::decode(input)
			.map(|raw_chunk| Self{
				chunk: raw_chunk,
				non_clone: NonCloneMarker::default(),
			})
	}
}

125
impl<T> TypedChunk<T> {
126
127
128
129
130
131
132
133
134
135
136
137
138
	/// Allocates a new typed cell chunk using the given storage allocator.
	///
	/// # Safety
	///
	/// The is unsafe because it does not check if the associated storage
	/// does not alias with storage allocated by other storage allocators.
	pub unsafe fn new_using_alloc<A>(alloc: &mut A) -> Self
	where
		A: Allocator
	{
		Self{
			chunk: RawChunk::new_using_alloc(alloc),
			non_clone: Default::default(),
139
140
141
		}
	}

142
143
144
145
146
147
148
149
150
151
	/// Returns the unterlying key to the cells.
	///
	/// # Note
	///
	/// This is a low-level utility getter and should
	/// normally not be required by users.
	pub fn cells_key(&self) -> Key {
		self.chunk.cells_key()
	}

152
	/// Returns an accessor to the `n`-th cell.
153
154
155
156
	pub(crate) fn cell_at(&mut self, n: u32) -> TypedChunkCell<T> {
		unsafe {
			TypedChunkCell::new_unchecked(self.chunk.cell_at(n))
		}
157
158
	}

159
	/// Removes the value stored in the `n`-th cell.
160
	pub fn clear(&mut self, n: u32) {
161
162
		self.chunk.clear(n)
	}
163
164
165
166
167
168
}

impl<T> TypedChunk<T>
where
	T: parity_codec::Decode
{
169
170
171
172
173
	/// Loads the value stored in the `n`-th cell if any.
	///
	/// # Panics
	///
	/// If decoding of the loaded bytes fails.
174
	pub fn load(&self, n: u32) -> Option<T> {
175
176
177
		self
			.chunk
			.load(n)
178
179
180
181
182
183
184
			.map(|loaded| {
				T::decode(&mut &loaded[..])
					// Maybe we should return an error instead of panicking.
					.expect(
						"[pdsl_core::TypedChunkCell::load] Error: \
						 failed upon decoding"
					)
185
186
187
188
189
190
191
192
			})
	}
}

impl<T> TypedChunk<T>
where
	T: parity_codec::Encode
{
193
	/// Stores the value into the `n`-th cell.
194
195
	pub fn store(&mut self, n: u32, val: &T) {
		self.cell_at(n).store(val)
196
197
198
199
200
201
202
	}
}

#[cfg(all(test, feature = "test-env"))]
mod tests {
	use super::*;

203
204
205
206
	use crate::{
		test_utils::run_test,
		env::TestEnv,
	};
207
208
209

	#[test]
	fn simple() {
210
211
		run_test(|| {
			const TEST_LEN: u32 = 5;
212

213
			let mut chunk = unsafe {
214
215
216
217
				let mut alloc = crate::storage::alloc::ForwardAlloc::from_raw_parts(
					Key([0x0; 32])
				);
				TypedChunk::new_using_alloc(&mut alloc)
218
			};
219

220
221
222
223
			// Invariants after initialization
			for i in 0..TEST_LEN {
				assert_eq!(chunk.load(i), None);
			}
224

225
226
227
228
229
			// Store some elements
			for i in 0..TEST_LEN {
				chunk.store(i, &i);
				assert_eq!(chunk.load(i), Some(i));
			}
230

231
232
233
234
235
236
			// Clear all elements.
			for i in 0..TEST_LEN {
				chunk.clear(i);
				assert_eq!(chunk.load(i), None);
			}
		})
237
238
239
240
	}

	#[test]
	fn count_reads_writes() {
241
242
		run_test(|| {
			const TEST_LEN: u32 = 5;
243

244
			let mut chunk = unsafe {
245
246
247
248
				let mut alloc = crate::storage::alloc::ForwardAlloc::from_raw_parts(
					Key([0x0; 32])
				);
				TypedChunk::new_using_alloc(&mut alloc)
249
			};
250

251
252
253
			// Reads and writes after init.
			assert_eq!(TestEnv::total_reads(), 0);
			assert_eq!(TestEnv::total_writes(), 0);
254

255
256
257
258
259
260
261
			// Loading from all cells.
			for i in 0..TEST_LEN {
				chunk.load(i);
				assert_eq!(TestEnv::total_reads(), i as u64 + 1);
				assert_eq!(TestEnv::total_writes(), 0);
			}
			assert_eq!(TestEnv::total_reads(), TEST_LEN as u64);
262
263
			assert_eq!(TestEnv::total_writes(), 0);

264
265
266
267
268
269
			// Writing to all cells.
			for i in 0..TEST_LEN {
				chunk.store(i, &i);
				assert_eq!(TestEnv::total_reads(), TEST_LEN as u64);
				assert_eq!(TestEnv::total_writes(), i as u64 + 1);
			}
270
			assert_eq!(TestEnv::total_reads(), TEST_LEN as u64);
271
			assert_eq!(TestEnv::total_writes(), TEST_LEN as u64);
272

273
274
275
276
277
278
279
280
			// Loading multiple times from a single cell.
			const LOAD_REPEATS: usize = 3;
			for n in 0..LOAD_REPEATS {
				chunk.load(0);
				assert_eq!(TestEnv::total_reads(), TEST_LEN as u64 + n as u64 + 1);
				assert_eq!(TestEnv::total_writes(), TEST_LEN as u64);
			}
			assert_eq!(TestEnv::total_reads(), TEST_LEN as u64 + LOAD_REPEATS as u64);
281
			assert_eq!(TestEnv::total_writes(), TEST_LEN as u64);
282

283
284
285
286
287
288
289
			// Storing multiple times to a single cell.
			const STORE_REPEATS: usize = 3;
			for n in 0..STORE_REPEATS {
				chunk.store(0, &10);
				assert_eq!(TestEnv::total_reads(), TEST_LEN as u64 + LOAD_REPEATS as u64);
				assert_eq!(TestEnv::total_writes(), TEST_LEN as u64 + n as u64 + 1);
			}
290
			assert_eq!(TestEnv::total_reads(), TEST_LEN as u64 + LOAD_REPEATS as u64);
291
292
			assert_eq!(TestEnv::total_writes(), TEST_LEN as u64 + STORE_REPEATS as u64);
		})
293
294
	}
}