1use neo_syscalls::NeoVMSyscall;
25use neo_types::*;
26
27#[cfg(target_arch = "wasm32")]
28#[link(wasm_import_module = "neo")]
29extern "C" {
30 #[link_name = "neo_storage_put_bytes"]
31 fn neo_storage_put_bytes(key_ptr: i32, key_len: i32, value_ptr: i32, value_len: i32);
32
33 #[link_name = "neo_storage_delete_bytes"]
34 fn neo_storage_delete_bytes(key_ptr: i32, key_len: i32);
35
36 #[link_name = "neo_storage_get_into"]
37 fn neo_storage_get_into(key_ptr: i32, key_len: i32, out_ptr: i32, out_cap: i32) -> i32;
38
39 #[link_name = "raw_storage_put_i64"]
40 fn neo_raw_storage_put_i64(key: i64, value: i64);
41
42 #[link_name = "raw_storage_get_i64"]
43 fn neo_raw_storage_get_i64(key: i64) -> i64;
44
45 #[link_name = "raw_storage_has_i64"]
46 fn neo_raw_storage_has_i64(key: i64) -> i32;
47
48 #[link_name = "raw_storage_delete_i64"]
49 fn neo_raw_storage_delete_i64(key: i64);
50}
51
52pub struct NeoStorage;
54
55impl NeoStorage {
56 pub fn get_context() -> NeoResult<NeoStorageContext> {
57 NeoVMSyscall::storage_get_context()
58 }
59
60 pub fn get_read_only_context() -> NeoResult<NeoStorageContext> {
61 NeoVMSyscall::storage_get_read_only_context()
62 }
63
64 pub fn as_read_only(context: &NeoStorageContext) -> NeoResult<NeoStorageContext> {
65 NeoVMSyscall::storage_as_read_only(context)
66 }
67
68 pub fn get(context: &NeoStorageContext, key: &NeoByteString) -> NeoResult<NeoByteString> {
76 NeoVMSyscall::storage_get(context, key)
77 }
78
79 pub fn put(
80 context: &NeoStorageContext,
81 key: &NeoByteString,
82 value: &NeoByteString,
83 ) -> NeoResult<()> {
84 NeoVMSyscall::storage_put(context, key, value)
85 }
86
87 pub fn delete(context: &NeoStorageContext, key: &NeoByteString) -> NeoResult<()> {
88 NeoVMSyscall::storage_delete(context, key)
89 }
90
91 pub fn find(
92 context: &NeoStorageContext,
93 prefix: &NeoByteString,
94 ) -> NeoResult<NeoIterator<NeoValue>> {
95 NeoVMSyscall::storage_find(context, prefix)
96 }
97}
98
99pub struct RawStorage;
107
108#[derive(Copy, Clone, PartialEq, Eq, Debug)]
110pub enum RawStorageGet {
111 Found(usize),
114 Missing,
118 BufferTooSmall(usize),
121}
122
123pub struct RawKeyBuilder<const N: usize> {
130 buf: core::mem::MaybeUninit<[u8; N]>,
131 len: usize,
132}
133
134impl<const N: usize> RawKeyBuilder<N> {
135 #[inline(always)]
136 pub const fn new() -> Self {
137 Self {
138 buf: core::mem::MaybeUninit::uninit(),
139 len: 0,
140 }
141 }
142
143 #[inline(always)]
144 pub fn push_bytes(&mut self, bytes: &[u8]) -> bool {
145 if bytes.len() > N - self.len {
146 return false;
147 }
148 unsafe {
149 core::ptr::copy_nonoverlapping(
150 bytes.as_ptr(),
151 self.buf.as_mut_ptr().cast::<u8>().add(self.len),
152 bytes.len(),
153 );
154 }
155 self.len += bytes.len();
156 true
157 }
158
159 #[inline(always)]
160 pub fn push_i64_le(&mut self, value: i64) -> bool {
161 self.push_bytes(&value.to_le_bytes())
162 }
163
164 #[inline(always)]
165 pub fn push_byte(&mut self, value: u8) -> bool {
166 if self.len == N {
167 return false;
168 }
169 unsafe {
170 *self.buf.as_mut_ptr().cast::<u8>().add(self.len) = value;
171 }
172 self.len += 1;
173 true
174 }
175
176 #[inline(always)]
177 pub fn as_slice(&self) -> &[u8] {
178 debug_assert!(self.len <= N);
179 unsafe { core::slice::from_raw_parts(self.buf.as_ptr().cast::<u8>(), self.len) }
180 }
181
182 #[inline(always)]
183 pub fn clear(&mut self) {
184 self.len = 0;
185 }
186
187 #[inline(always)]
188 pub fn len(&self) -> usize {
189 self.len
190 }
191
192 #[inline(always)]
193 pub fn is_empty(&self) -> bool {
194 self.len == 0
195 }
196}
197
198impl<const N: usize> Default for RawKeyBuilder<N> {
199 fn default() -> Self {
200 Self::new()
201 }
202}
203
204impl RawStorage {
205 pub fn put(key: &[u8], value: &[u8]) {
207 #[cfg(target_arch = "wasm32")]
208 unsafe {
209 neo_storage_put_bytes(
210 key.as_ptr() as i32,
211 key.len() as i32,
212 value.as_ptr() as i32,
213 value.len() as i32,
214 );
215 }
216 #[cfg(not(target_arch = "wasm32"))]
217 {
218 let ctx = match NeoVMSyscall::storage_get_context() {
219 Ok(c) => c,
220 Err(_) => return,
221 };
222 let _ = NeoVMSyscall::storage_put(
223 &ctx,
224 &NeoByteString::from_slice(key),
225 &NeoByteString::from_slice(value),
226 );
227 }
228 }
229
230 pub fn delete(key: &[u8]) {
232 #[cfg(target_arch = "wasm32")]
233 unsafe {
234 neo_storage_delete_bytes(key.as_ptr() as i32, key.len() as i32);
235 }
236 #[cfg(not(target_arch = "wasm32"))]
237 {
238 let ctx = match NeoVMSyscall::storage_get_context() {
239 Ok(c) => c,
240 Err(_) => return,
241 };
242 let _ = NeoVMSyscall::storage_delete(&ctx, &NeoByteString::from_slice(key));
243 }
244 }
245
246 pub fn get_into(key: &[u8], buf: &mut [u8]) -> RawStorageGet {
256 #[cfg(target_arch = "wasm32")]
257 let actual = unsafe {
258 neo_storage_get_into(
259 key.as_ptr() as i32,
260 key.len() as i32,
261 buf.as_mut_ptr() as i32,
262 buf.len() as i32,
263 )
264 };
265 #[cfg(not(target_arch = "wasm32"))]
266 let actual = host_get_into(key, buf);
267
268 if actual == -1 {
269 RawStorageGet::Missing
270 } else if actual >= 0 {
271 RawStorageGet::Found(actual as usize)
272 } else {
273 RawStorageGet::BufferTooSmall((-actual) as usize)
274 }
275 }
276
277 pub fn get_i64(key: &[u8]) -> Option<i64> {
280 let mut buf = [0u8; 8];
281 match Self::get_into(key, &mut buf) {
282 RawStorageGet::Found(8) => Some(i64::from_le_bytes(buf)),
283 _ => None,
284 }
285 }
286
287 pub fn get_u16(key: &[u8]) -> Option<u16> {
290 let mut buf = [0u8; 2];
291 match Self::get_into(key, &mut buf) {
292 RawStorageGet::Found(2) => Some(u16::from_le_bytes(buf)),
293 _ => None,
294 }
295 }
296
297 pub fn get_bool(key: &[u8]) -> Option<bool> {
300 let mut buf = [0u8; 1];
301 match Self::get_into(key, &mut buf) {
302 RawStorageGet::Found(1) => Some(buf[0] != 0),
303 _ => None,
304 }
305 }
306
307 pub fn put_i64(key: &[u8], value: i64) {
309 Self::put(key, &value.to_le_bytes());
310 }
311
312 pub fn put_u16(key: &[u8], value: u16) {
314 Self::put(key, &value.to_le_bytes());
315 }
316
317 pub fn put_bool(key: &[u8], value: bool) {
319 Self::put(key, &[value as u8]);
320 }
321
322 pub fn put_i64_key(key: i64, value: i64) {
325 #[cfg(target_arch = "wasm32")]
326 unsafe {
327 neo_raw_storage_put_i64(key, value);
328 }
329 #[cfg(not(target_arch = "wasm32"))]
330 host_put_i64_key(key, value);
331 }
332
333 pub fn get_i64_key_or_zero(key: i64) -> i64 {
335 #[cfg(target_arch = "wasm32")]
336 unsafe {
337 neo_raw_storage_get_i64(key)
338 }
339 #[cfg(not(target_arch = "wasm32"))]
340 {
341 host_get_i64_key(key).unwrap_or(0)
342 }
343 }
344
345 pub fn has_i64_key(key: i64) -> bool {
350 #[cfg(target_arch = "wasm32")]
351 unsafe {
352 neo_raw_storage_has_i64(key) != 0
353 }
354 #[cfg(not(target_arch = "wasm32"))]
355 {
356 host_has_i64_key(key)
357 }
358 }
359
360 pub fn delete_i64_key(key: i64) {
362 #[cfg(target_arch = "wasm32")]
363 unsafe {
364 neo_raw_storage_delete_i64(key);
365 }
366 #[cfg(not(target_arch = "wasm32"))]
367 {
368 let ctx = match NeoVMSyscall::storage_get_context() {
369 Ok(c) => c,
370 Err(_) => return,
371 };
372 let key_bytes = neovm_i64_bytes(key);
373 let _ = NeoVMSyscall::storage_delete(&ctx, &NeoByteString::from_slice(&key_bytes));
374 }
375 }
376}
377
378#[cfg(not(target_arch = "wasm32"))]
379fn host_get_into(key: &[u8], buf: &mut [u8]) -> i32 {
380 let ctx = match NeoVMSyscall::storage_get_context() {
381 Ok(c) => c,
382 Err(_) => return -1,
383 };
384 let stored = match NeoVMSyscall::storage_try_get(&ctx, &NeoByteString::from_slice(key)) {
385 Ok(Some(b)) => b,
386 Ok(None) => return -1,
390 Err(_) => return -1,
391 };
392 let bytes = stored.as_slice();
393 if bytes.len() > buf.len() {
394 return -(bytes.len() as i32);
395 }
396 let len = bytes.len();
397 buf[..len].copy_from_slice(bytes);
398 len as i32
399}
400
401#[cfg(not(target_arch = "wasm32"))]
402fn host_put_i64_key(key: i64, value: i64) {
403 let ctx = match NeoVMSyscall::storage_get_context() {
404 Ok(c) => c,
405 Err(_) => return,
406 };
407 let key_bytes = neovm_i64_bytes(key);
408 let value_bytes = neovm_i64_bytes(value);
409 let _ = NeoVMSyscall::storage_put(
410 &ctx,
411 &NeoByteString::from_slice(&key_bytes),
412 &NeoByteString::from_slice(&value_bytes),
413 );
414}
415
416#[cfg(not(target_arch = "wasm32"))]
417fn host_get_i64_key(key: i64) -> Option<i64> {
418 let ctx = NeoVMSyscall::storage_get_context().ok()?;
419 let key_bytes = neovm_i64_bytes(key);
420 let stored = NeoVMSyscall::storage_try_get(&ctx, &NeoByteString::from_slice(&key_bytes))
421 .ok()
422 .flatten()?;
423 storage_bytes_to_i64(stored.as_slice())
424}
425
426#[cfg(not(target_arch = "wasm32"))]
427fn host_has_i64_key(key: i64) -> bool {
428 let ctx = match NeoVMSyscall::storage_get_context() {
429 Ok(c) => c,
430 Err(_) => return false,
431 };
432 let key_bytes = neovm_i64_bytes(key);
433 NeoVMSyscall::storage_try_get(&ctx, &NeoByteString::from_slice(&key_bytes))
434 .ok()
435 .flatten()
436 .map(|stored| !stored.as_slice().is_empty())
437 .unwrap_or(false)
438}
439
440#[cfg(not(target_arch = "wasm32"))]
441fn neovm_i64_bytes(value: i64) -> Vec<u8> {
442 if value == 0 {
443 return vec![0];
444 }
445
446 let mut bytes = value.to_le_bytes().to_vec();
447 while bytes.len() > 1 {
448 let last = *bytes.last().unwrap_or(&0);
449 let prev = bytes[bytes.len() - 2];
450 let redundant_positive = last == 0x00 && (prev & 0x80) == 0;
451 let redundant_negative = last == 0xff && (prev & 0x80) != 0;
452 if redundant_positive || redundant_negative {
453 bytes.pop();
454 } else {
455 break;
456 }
457 }
458 bytes
459}
460
461#[cfg(not(target_arch = "wasm32"))]
462fn storage_bytes_to_i64(bytes: &[u8]) -> Option<i64> {
463 match bytes.len() {
464 0 => None,
465 1..=8 => {
466 let sign_extend = bytes.last().copied().unwrap_or(0) & 0x80 != 0;
467 let mut buf = if sign_extend { [0xff; 8] } else { [0u8; 8] };
468 buf[..bytes.len()].copy_from_slice(bytes);
469 Some(i64::from_le_bytes(buf))
470 }
471 _ => None,
472 }
473}