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> {
69 NeoVMSyscall::storage_get(context, key)
70 }
71
72 pub fn put(
73 context: &NeoStorageContext,
74 key: &NeoByteString,
75 value: &NeoByteString,
76 ) -> NeoResult<()> {
77 NeoVMSyscall::storage_put(context, key, value)
78 }
79
80 pub fn delete(context: &NeoStorageContext, key: &NeoByteString) -> NeoResult<()> {
81 NeoVMSyscall::storage_delete(context, key)
82 }
83
84 pub fn find(
85 context: &NeoStorageContext,
86 prefix: &NeoByteString,
87 ) -> NeoResult<NeoIterator<NeoValue>> {
88 NeoVMSyscall::storage_find(context, prefix)
89 }
90}
91
92pub struct RawStorage;
100
101#[derive(Copy, Clone, PartialEq, Eq, Debug)]
103pub enum RawStorageGet {
104 Found(usize),
107 Missing,
111 BufferTooSmall(usize),
114}
115
116pub struct RawKeyBuilder<const N: usize> {
123 buf: core::mem::MaybeUninit<[u8; N]>,
124 len: usize,
125}
126
127impl<const N: usize> RawKeyBuilder<N> {
128 #[inline(always)]
129 pub const fn new() -> Self {
130 Self {
131 buf: core::mem::MaybeUninit::uninit(),
132 len: 0,
133 }
134 }
135
136 #[inline(always)]
137 pub fn push_bytes(&mut self, bytes: &[u8]) -> bool {
138 if bytes.len() > N - self.len {
139 return false;
140 }
141 unsafe {
142 core::ptr::copy_nonoverlapping(
143 bytes.as_ptr(),
144 self.buf.as_mut_ptr().cast::<u8>().add(self.len),
145 bytes.len(),
146 );
147 }
148 self.len += bytes.len();
149 true
150 }
151
152 #[inline(always)]
153 pub fn push_i64_le(&mut self, value: i64) -> bool {
154 self.push_bytes(&value.to_le_bytes())
155 }
156
157 #[inline(always)]
158 pub fn push_byte(&mut self, value: u8) -> bool {
159 if self.len == N {
160 return false;
161 }
162 unsafe {
163 *self.buf.as_mut_ptr().cast::<u8>().add(self.len) = value;
164 }
165 self.len += 1;
166 true
167 }
168
169 #[inline(always)]
170 pub fn as_slice(&self) -> &[u8] {
171 debug_assert!(self.len <= N);
172 unsafe { core::slice::from_raw_parts(self.buf.as_ptr().cast::<u8>(), self.len) }
173 }
174
175 #[inline(always)]
176 pub fn clear(&mut self) {
177 self.len = 0;
178 }
179
180 #[inline(always)]
181 pub fn len(&self) -> usize {
182 self.len
183 }
184
185 #[inline(always)]
186 pub fn is_empty(&self) -> bool {
187 self.len == 0
188 }
189}
190
191impl<const N: usize> Default for RawKeyBuilder<N> {
192 fn default() -> Self {
193 Self::new()
194 }
195}
196
197impl RawStorage {
198 pub fn put(key: &[u8], value: &[u8]) {
200 #[cfg(target_arch = "wasm32")]
201 unsafe {
202 neo_storage_put_bytes(
203 key.as_ptr() as i32,
204 key.len() as i32,
205 value.as_ptr() as i32,
206 value.len() as i32,
207 );
208 }
209 #[cfg(not(target_arch = "wasm32"))]
210 {
211 let ctx = match NeoVMSyscall::storage_get_context() {
212 Ok(c) => c,
213 Err(_) => return,
214 };
215 let _ = NeoVMSyscall::storage_put(
216 &ctx,
217 &NeoByteString::from_slice(key),
218 &NeoByteString::from_slice(value),
219 );
220 }
221 }
222
223 pub fn delete(key: &[u8]) {
225 #[cfg(target_arch = "wasm32")]
226 unsafe {
227 neo_storage_delete_bytes(key.as_ptr() as i32, key.len() as i32);
228 }
229 #[cfg(not(target_arch = "wasm32"))]
230 {
231 let ctx = match NeoVMSyscall::storage_get_context() {
232 Ok(c) => c,
233 Err(_) => return,
234 };
235 let _ = NeoVMSyscall::storage_delete(&ctx, &NeoByteString::from_slice(key));
236 }
237 }
238
239 pub fn get_into(key: &[u8], buf: &mut [u8]) -> RawStorageGet {
249 #[cfg(target_arch = "wasm32")]
250 let actual = unsafe {
251 neo_storage_get_into(
252 key.as_ptr() as i32,
253 key.len() as i32,
254 buf.as_mut_ptr() as i32,
255 buf.len() as i32,
256 )
257 };
258 #[cfg(not(target_arch = "wasm32"))]
259 let actual = host_get_into(key, buf);
260
261 if actual == -1 {
262 RawStorageGet::Missing
263 } else if actual >= 0 {
264 RawStorageGet::Found(actual as usize)
265 } else {
266 RawStorageGet::BufferTooSmall((-actual) as usize)
267 }
268 }
269
270 pub fn get_i64(key: &[u8]) -> Option<i64> {
273 let mut buf = [0u8; 8];
274 match Self::get_into(key, &mut buf) {
275 RawStorageGet::Found(8) => Some(i64::from_le_bytes(buf)),
276 _ => None,
277 }
278 }
279
280 pub fn get_u16(key: &[u8]) -> Option<u16> {
283 let mut buf = [0u8; 2];
284 match Self::get_into(key, &mut buf) {
285 RawStorageGet::Found(2) => Some(u16::from_le_bytes(buf)),
286 _ => None,
287 }
288 }
289
290 pub fn get_bool(key: &[u8]) -> Option<bool> {
293 let mut buf = [0u8; 1];
294 match Self::get_into(key, &mut buf) {
295 RawStorageGet::Found(1) => Some(buf[0] != 0),
296 _ => None,
297 }
298 }
299
300 pub fn put_i64(key: &[u8], value: i64) {
302 Self::put(key, &value.to_le_bytes());
303 }
304
305 pub fn put_u16(key: &[u8], value: u16) {
307 Self::put(key, &value.to_le_bytes());
308 }
309
310 pub fn put_bool(key: &[u8], value: bool) {
312 Self::put(key, &[value as u8]);
313 }
314
315 pub fn put_i64_key(key: i64, value: i64) {
318 #[cfg(target_arch = "wasm32")]
319 unsafe {
320 neo_raw_storage_put_i64(key, value);
321 }
322 #[cfg(not(target_arch = "wasm32"))]
323 host_put_i64_key(key, value);
324 }
325
326 pub fn get_i64_key_or_zero(key: i64) -> i64 {
328 #[cfg(target_arch = "wasm32")]
329 unsafe {
330 neo_raw_storage_get_i64(key)
331 }
332 #[cfg(not(target_arch = "wasm32"))]
333 {
334 host_get_i64_key(key).unwrap_or(0)
335 }
336 }
337
338 pub fn has_i64_key(key: i64) -> bool {
343 #[cfg(target_arch = "wasm32")]
344 unsafe {
345 neo_raw_storage_has_i64(key) != 0
346 }
347 #[cfg(not(target_arch = "wasm32"))]
348 {
349 host_has_i64_key(key)
350 }
351 }
352
353 pub fn delete_i64_key(key: i64) {
355 #[cfg(target_arch = "wasm32")]
356 unsafe {
357 neo_raw_storage_delete_i64(key);
358 }
359 #[cfg(not(target_arch = "wasm32"))]
360 {
361 let ctx = match NeoVMSyscall::storage_get_context() {
362 Ok(c) => c,
363 Err(_) => return,
364 };
365 let key_bytes = neovm_i64_bytes(key);
366 let _ = NeoVMSyscall::storage_delete(&ctx, &NeoByteString::from_slice(&key_bytes));
367 }
368 }
369}
370
371#[cfg(not(target_arch = "wasm32"))]
372fn host_get_into(key: &[u8], buf: &mut [u8]) -> i32 {
373 let ctx = match NeoVMSyscall::storage_get_context() {
374 Ok(c) => c,
375 Err(_) => return -1,
376 };
377 let stored = match NeoVMSyscall::storage_try_get(&ctx, &NeoByteString::from_slice(key)) {
378 Ok(Some(b)) => b,
379 Ok(None) => return 0,
380 Err(_) => return -1,
381 };
382 let bytes = stored.as_slice();
383 if bytes.len() > buf.len() {
384 return -(bytes.len() as i32);
385 }
386 let len = bytes.len();
387 buf[..len].copy_from_slice(bytes);
388 len as i32
389}
390
391#[cfg(not(target_arch = "wasm32"))]
392fn host_put_i64_key(key: i64, value: i64) {
393 let ctx = match NeoVMSyscall::storage_get_context() {
394 Ok(c) => c,
395 Err(_) => return,
396 };
397 let key_bytes = neovm_i64_bytes(key);
398 let value_bytes = neovm_i64_bytes(value);
399 let _ = NeoVMSyscall::storage_put(
400 &ctx,
401 &NeoByteString::from_slice(&key_bytes),
402 &NeoByteString::from_slice(&value_bytes),
403 );
404}
405
406#[cfg(not(target_arch = "wasm32"))]
407fn host_get_i64_key(key: i64) -> Option<i64> {
408 let ctx = NeoVMSyscall::storage_get_context().ok()?;
409 let key_bytes = neovm_i64_bytes(key);
410 let stored = NeoVMSyscall::storage_try_get(&ctx, &NeoByteString::from_slice(&key_bytes))
411 .ok()
412 .flatten()?;
413 storage_bytes_to_i64(stored.as_slice())
414}
415
416#[cfg(not(target_arch = "wasm32"))]
417fn host_has_i64_key(key: i64) -> bool {
418 let ctx = match NeoVMSyscall::storage_get_context() {
419 Ok(c) => c,
420 Err(_) => return false,
421 };
422 let key_bytes = neovm_i64_bytes(key);
423 NeoVMSyscall::storage_try_get(&ctx, &NeoByteString::from_slice(&key_bytes))
424 .ok()
425 .flatten()
426 .map(|stored| !stored.as_slice().is_empty())
427 .unwrap_or(false)
428}
429
430#[cfg(not(target_arch = "wasm32"))]
431fn neovm_i64_bytes(value: i64) -> Vec<u8> {
432 if value == 0 {
433 return vec![0];
434 }
435
436 let mut bytes = value.to_le_bytes().to_vec();
437 while bytes.len() > 1 {
438 let last = *bytes.last().unwrap_or(&0);
439 let prev = bytes[bytes.len() - 2];
440 let redundant_positive = last == 0x00 && (prev & 0x80) == 0;
441 let redundant_negative = last == 0xff && (prev & 0x80) != 0;
442 if redundant_positive || redundant_negative {
443 bytes.pop();
444 } else {
445 break;
446 }
447 }
448 bytes
449}
450
451#[cfg(not(target_arch = "wasm32"))]
452fn storage_bytes_to_i64(bytes: &[u8]) -> Option<i64> {
453 match bytes.len() {
454 0 => None,
455 1..=8 => {
456 let sign_extend = bytes.last().copied().unwrap_or(0) & 0x80 != 0;
457 let mut buf = if sign_extend { [0xff; 8] } else { [0u8; 8] };
458 buf[..bytes.len()].copy_from_slice(bytes);
459 Some(i64::from_le_bytes(buf))
460 }
461 _ => None,
462 }
463}