raw_chunk.rs 6.71 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
use crate::{
18
	memory::vec::Vec,
19
20
21
	storage::{
		Key,
		NonCloneMarker,
22
		Allocator,
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
	},
	env::{Env, ContractEnv},
};

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

45
46
47
48
49
50
51
52
53
54
55
/// A single cell within a chunk of raw cells.
#[derive(Debug, PartialEq, Eq)]
pub struct RawChunkCell<'a> {
	/// The key to the corresponding cell within the raw chunk.
	key: Key,
	/// Marker that prevents this type from being `Copy` or `Clone` by accident.
	non_clone: NonCloneMarker<&'a mut ()>,
}

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

67
	/// Load the bytes from the cell if not empty.
68
	pub fn load(&self) -> Option<Vec<u8>> {
69
		unsafe { ContractEnv::load(self.key) }
70
71
	}

72
73
	/// Store the bytes into the cell.
	pub fn store(&mut self, bytes: &[u8]) {
74
		unsafe { ContractEnv::store(self.key, bytes) }
75
76
	}

77
	/// Remove the bytes stored in the cell.
78
	pub fn clear(&mut self) {
79
		unsafe { ContractEnv::clear(self.key) }
80
81
82
	}
}

83
84
85
86
87
88
89
90
91
92
impl parity_codec::Encode for RawChunk {
	fn encode_to<W: parity_codec::Output>(&self, dest: &mut W) {
		self.key.encode_to(dest)
	}
}

impl parity_codec::Decode for RawChunk {
	fn decode<I: parity_codec::Input>(input: &mut I) -> Option<Self> {
		Key::decode(input)
			.map(|key| unsafe {
93
				Self::new_unchecked(key)
94
95
96
97
			})
	}
}

98
99
100
impl RawChunk {
	/// Creates a new raw cell chunk for the given key and capacity.
	///
101
	/// # Safety
102
103
104
105
106
	///
	/// This is unsafe because ...
	/// - ... it does not check if the associated
	///   contract storage does not alias with other accesses.
	/// - ... it does not check if given capacity is non zero.
107
	unsafe fn new_unchecked(key: Key) -> Self {
108
109
		Self{
			key,
110
111
112
113
114
115
116
117
			non_clone: Default::default(),
		}
	}

	/// Allocates a new raw cell chunk using the given storage allocator.
	///
	/// # Safety
	///
118
	/// This is unsafe because it does not check if the associated storage
119
120
121
122
123
	/// does not alias with storage allocated by other storage allocators.
	pub unsafe fn new_using_alloc<A>(alloc: &mut A) -> Self
	where
		A: Allocator
	{
124
		Self::new_unchecked(alloc.alloc(u32::max_value()))
125
126
	}

127
128
129
130
131
132
133
134
135
136
	/// 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.key
	}

137
	/// Returns a key for the `n`-th cell if within bounds.
138
139
140
141
	///
	/// # Error
	///
	/// Returns an error if `n` is not within bounds.
142
	fn offset_key(&self, n: u32) -> Key {
143
		self.key + n
144
145
	}

146
147
148
149
150
	/// Returns an accessor to the `n`-th cell.
	pub(crate) fn cell_at(&mut self, n: u32) -> RawChunkCell {
		unsafe {
			RawChunkCell::new_unchecked(self.offset_key(n))
		}
151
152
	}

153
	/// Loads the bytes stored in the `n`-th cell.
154
	pub fn load(&self, n: u32) -> Option<Vec<u8>> {
155
		unsafe { ContractEnv::load(self.key + n) }
156
157
	}

158
	/// Stores the given bytes into the `n`-th cell.
159
160
	pub fn store(&mut self, n: u32, bytes: &[u8]) {
		self.cell_at(n).store(bytes)
161
162
	}

Hero Bird's avatar
Hero Bird committed
163
	/// Removes the bytes stored in the `n`-th cell.
164
165
	pub fn clear(&mut self, n: u32) {
		self.cell_at(n).clear()
166
167
168
169
170
171
172
	}
}

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

173
174
175
176
	use crate::{
		test_utils::run_test,
		env::TestEnv,
	};
177
178
179

	#[test]
	fn simple() {
180
181
182
		run_test(|| {
			const TEST_LEN: u32 = 5;
			const WORD_SIZE: usize = 4;
183

184
185
186
			let mut chunk = unsafe {
				RawChunk::new_unchecked(Key([0x42; 32]))
			};
187

188
189
190
191
			// Invariants after initialization
			for i in 0..TEST_LEN {
				assert_eq!(chunk.load(i), None);
			}
192

193
194
195
196
197
			// Store some elements
			for i in 0..TEST_LEN {
				chunk.store(i, &[i as u8; WORD_SIZE]);
				assert_eq!(chunk.load(i), Some(vec![i as u8; WORD_SIZE]));
			}
198

199
200
201
202
203
204
			// Clear all elements.
			for i in 0..TEST_LEN {
				chunk.clear(i);
				assert_eq!(chunk.load(i), None);
			}
		})
205
206
207
208
	}

	#[test]
	fn count_reads_writes() {
209
210
211
		run_test(|| {
			const TEST_LEN: u32 = 5;
			const WORD_SIZE: usize = 4;
212

213
214
215
			let mut chunk = unsafe {
				RawChunk::new_unchecked(Key([0x42; 32]))
			};
216

217
218
219
			// Reads and writes after init.
			assert_eq!(TestEnv::total_reads(), 0);
			assert_eq!(TestEnv::total_writes(), 0);
220

221
222
223
224
225
226
227
			// 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);
228
229
			assert_eq!(TestEnv::total_writes(), 0);

230
231
232
233
234
235
			// Writing to all cells.
			for i in 0..TEST_LEN {
				chunk.store(i, &[i as u8; WORD_SIZE]);
				assert_eq!(TestEnv::total_reads(), TEST_LEN as u64);
				assert_eq!(TestEnv::total_writes(), i as u64 + 1);
			}
236
237
			assert_eq!(TestEnv::total_reads(), TEST_LEN as u64);
			assert_eq!(TestEnv::total_writes(), TEST_LEN as u64);
238

239
240
241
242
243
244
245
			// 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);
			}
246
			assert_eq!(TestEnv::total_reads(), TEST_LEN as u64 + LOAD_REPEATS as u64);
247
248
249
250
251
252
253
254
255
256
257
258
			assert_eq!(TestEnv::total_writes(), TEST_LEN as u64);

			// Storing multiple times to a single cell.
			const STORE_REPEATS: usize = 3;
			for n in 0..STORE_REPEATS {
				chunk.store(0, b"test");
				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);
			}
			assert_eq!(TestEnv::total_reads(), TEST_LEN as u64 + LOAD_REPEATS as u64);
			assert_eq!(TestEnv::total_writes(), TEST_LEN as u64 + STORE_REPEATS as u64);
		})
259
260
	}
}