Skip to content

Commit 8a60668

Browse files
authored
Merge pull request #760 from CosmWasm/multi-return
Create sections encoding for multi-value regions
2 parents 61480ca + 6a4eafd commit 8a60668

5 files changed

Lines changed: 157 additions & 26 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ and this project adheres to
4242

4343
- all: The `query` enpoint is now optional. It is still highly recommended to
4444
expose it an almost any use case though.
45+
- all: Change the encoding of the key/value region of the `db_next` import to a
46+
more generic encoding that supports an arbitrary number of sections. This
47+
encoding can then be reused for other multi value regions.
4548
- cosmwasm-std: Remove `from_address` from `BankMsg::Send`, as it always sends
4649
from the contract address, and this is consistent with other `CosmosMsg`
4750
variants.

packages/std/src/imports.rs

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::binary::Binary;
55
use crate::errors::{StdError, StdResult, SystemError};
66
use crate::memory::{alloc, build_region, consume_region, Region};
77
use crate::results::SystemResult;
8+
use crate::sections::decode_sections2;
89
use crate::serde::from_slice;
910
use crate::traits::{Api, Querier, QuerierResult, Storage};
1011
#[cfg(feature = "iterator")]
@@ -120,23 +121,13 @@ impl Iterator for ExternalIterator {
120121
fn next(&mut self) -> Option<Self::Item> {
121122
let next_result = unsafe { db_next(self.iterator_id) };
122123
let kv_region_ptr = next_result as *mut Region;
123-
let mut kv = unsafe { consume_region(kv_region_ptr) };
124-
125-
// The KV region uses the format value || key || keylen, where keylen is a fixed size big endian u32 value
126-
let keylen = u32::from_be_bytes([
127-
kv[kv.len() - 4],
128-
kv[kv.len() - 3],
129-
kv[kv.len() - 2],
130-
kv[kv.len() - 1],
131-
]) as usize;
132-
if keylen == 0 {
133-
return None;
124+
let kv = unsafe { consume_region(kv_region_ptr) };
125+
let (key, value) = decode_sections2(kv);
126+
if key.len() == 0 {
127+
None
128+
} else {
129+
Some((key, value))
134130
}
135-
136-
kv.truncate(kv.len() - 4);
137-
let key = kv.split_off(kv.len() - keylen);
138-
let value = kv;
139-
Some((key, value))
140131
}
141132
}
142133

packages/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod iterator;
1515
mod math;
1616
mod query;
1717
mod results;
18+
mod sections;
1819
mod serde;
1920
mod storage;
2021
mod traits;

packages/std/src/sections.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/// A sections decoder for the special case of two elements
2+
#[allow(dead_code)] // used in Wasm and tests only
3+
pub fn decode_sections2(data: Vec<u8>) -> (Vec<u8>, Vec<u8>) {
4+
let (rest, second) = split_tail(data);
5+
let (_, first) = split_tail(rest);
6+
(first, second)
7+
}
8+
9+
/// Splits data into the last section ("tail") and the rest.
10+
/// The tail's length information is cut off, such that it is ready to use.
11+
/// The rest is basically unparsed and contails the lengths of the remaining sections.
12+
///
13+
/// While the tail is copied into a new vector, the rest is only truncated such that
14+
/// no re-allocation is necessary.
15+
///
16+
/// If `data` contains one section only, `data` is moved into the tail entirely
17+
fn split_tail(data: Vec<u8>) -> (Vec<u8>, Vec<u8>) {
18+
let tail_len: usize = if data.len() >= 4 {
19+
u32::from_be_bytes([
20+
data[data.len() - 4],
21+
data[data.len() - 3],
22+
data[data.len() - 2],
23+
data[data.len() - 1],
24+
]) as usize
25+
} else {
26+
panic!("Cannot read section length");
27+
};
28+
let rest_len_end = data.len() - 4 - tail_len;
29+
30+
let (rest, mut tail) = if rest_len_end == 0 {
31+
// i.e. all data is the tail
32+
(Vec::new(), data)
33+
} else {
34+
let mut rest = data;
35+
let tail = rest.split_off(rest_len_end);
36+
(rest, tail)
37+
};
38+
tail.truncate(tail_len); // cut off length
39+
(rest, tail)
40+
}
41+
42+
#[cfg(test)]
43+
mod tests {
44+
use super::*;
45+
46+
#[test]
47+
fn decode_sections2_works() {
48+
let data = b"\xAA\0\0\0\x01\xBB\xCC\0\0\0\x02".to_vec();
49+
assert_eq!(decode_sections2(data), (vec![0xAA], vec![0xBB, 0xCC]));
50+
51+
let data = b"\xDE\xEF\x62\0\0\0\x03\0\0\0\0".to_vec();
52+
assert_eq!(decode_sections2(data), (vec![0xDE, 0xEF, 0x62], vec![]));
53+
54+
let data = b"\0\0\0\0\xDE\xEF\x62\0\0\0\x03".to_vec();
55+
assert_eq!(decode_sections2(data), (vec![], vec![0xDE, 0xEF, 0x62]));
56+
57+
let data = b"\0\0\0\0\0\0\0\0".to_vec();
58+
assert_eq!(decode_sections2(data), (vec![], vec![]));
59+
60+
let data = b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\x13\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\0\0\x01\x15".to_vec();
61+
assert_eq!(decode_sections2(data), (vec![0xFF; 19], vec![0x9D; 277]));
62+
}
63+
64+
#[test]
65+
fn decode_sections2_preserved_first_vector() {
66+
let original = b"\xAA\0\0\0\x01\xBB\xCC\0\0\0\x02".to_vec();
67+
let original_capacity = original.capacity();
68+
let original_ptr = original.as_ptr();
69+
let (first, second) = decode_sections2(original);
70+
71+
// This is not copied
72+
assert_eq!(first.capacity(), original_capacity);
73+
assert_eq!(first.as_ptr(), original_ptr);
74+
75+
// This is a copy
76+
assert_ne!(second.capacity(), original_capacity);
77+
assert_ne!(second.as_ptr(), original_ptr);
78+
}
79+
}

packages/vm/src/imports.rs

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -298,16 +298,37 @@ fn do_next<A: Api, S: Storage, Q: Querier>(
298298
// Empty key will later be treated as _no more element_.
299299
let (key, value) = result?.unwrap_or_else(|| (Vec::<u8>::new(), Vec::<u8>::new()));
300300

301-
// Build value || key || keylen
302-
let keylen_bytes = to_u32(key.len())?.to_be_bytes();
303-
let mut out_data = value;
304-
out_data.reserve(key.len() + 4);
305-
out_data.extend(key);
306-
out_data.extend_from_slice(&keylen_bytes);
307-
301+
let out_data = encode_sections(&[key, value])?;
308302
write_to_contract::<A, S, Q>(env, &out_data)
309303
}
310304

305+
/// Encodes multiple sections of data into one vector.
306+
///
307+
/// Each section is suffixed by a section length encoded as big endian uint32.
308+
/// Using suffixes instead of prefixes allows reading sections in reverse order,
309+
/// such that the first element does not need to be re-allocated if the contract's
310+
/// data structure supports truncation (such as a Rust vector).
311+
///
312+
/// The resulting data looks like this:
313+
///
314+
/// ```ignore
315+
/// section1 || section1_len || section2 || section2_len || section3 || section3_len || …
316+
/// ```
317+
#[allow(dead_code)]
318+
fn encode_sections(sections: &[Vec<u8>]) -> VmResult<Vec<u8>> {
319+
let mut out_len: usize = sections.iter().map(|section| section.len()).sum();
320+
out_len += 4 * sections.len();
321+
let mut out_data = Vec::with_capacity(out_len);
322+
for section in sections {
323+
let section_len = to_u32(section.len())?.to_be_bytes();
324+
out_data.extend(section);
325+
out_data.extend_from_slice(&section_len);
326+
}
327+
debug_assert_eq!(out_data.len(), out_len);
328+
debug_assert_eq!(out_data.capacity(), out_len);
329+
Ok(out_data)
330+
}
331+
311332
#[cfg(test)]
312333
mod tests {
313334
use super::*;
@@ -1131,19 +1152,19 @@ mod tests {
11311152
let kv_region_ptr = do_next::<MA, MS, MQ>(&env, id).unwrap();
11321153
assert_eq!(
11331154
force_read(&env, kv_region_ptr),
1134-
[VALUE1, KEY1, b"\0\0\0\x03"].concat()
1155+
[KEY1, b"\0\0\0\x03", VALUE1, b"\0\0\0\x06"].concat()
11351156
);
11361157

11371158
// Entry 2
11381159
let kv_region_ptr = do_next::<MA, MS, MQ>(&env, id).unwrap();
11391160
assert_eq!(
11401161
force_read(&env, kv_region_ptr),
1141-
[VALUE2, KEY2, b"\0\0\0\x04"].concat()
1162+
[KEY2, b"\0\0\0\x04", VALUE2, b"\0\0\0\x05"].concat()
11421163
);
11431164

11441165
// End
11451166
let kv_region_ptr = do_next::<MA, MS, MQ>(&env, id).unwrap();
1146-
assert_eq!(force_read(&env, kv_region_ptr), b"\0\0\0\0");
1167+
assert_eq!(force_read(&env, kv_region_ptr), b"\0\0\0\0\0\0\0\0");
11471168
// API makes no guarantees for value_ptr in this case
11481169
}
11491170

@@ -1164,4 +1185,40 @@ mod tests {
11641185
e => panic!("Unexpected error: {:?}", e),
11651186
}
11661187
}
1188+
1189+
#[test]
1190+
fn encode_sections_works_for_empty_sections() {
1191+
let enc = encode_sections(&[]).unwrap();
1192+
assert_eq!(enc, b"" as &[u8]);
1193+
let enc = encode_sections(&[vec![]]).unwrap();
1194+
assert_eq!(enc, b"\0\0\0\0" as &[u8]);
1195+
let enc = encode_sections(&[vec![], vec![]]).unwrap();
1196+
assert_eq!(enc, b"\0\0\0\0\0\0\0\0" as &[u8]);
1197+
let enc = encode_sections(&[vec![], vec![], vec![]]).unwrap();
1198+
assert_eq!(enc, b"\0\0\0\0\0\0\0\0\0\0\0\0" as &[u8]);
1199+
}
1200+
1201+
#[test]
1202+
fn encode_sections_works_for_one_element() {
1203+
let enc = encode_sections(&[]).unwrap();
1204+
assert_eq!(enc, b"" as &[u8]);
1205+
let enc = encode_sections(&[vec![0xAA]]).unwrap();
1206+
assert_eq!(enc, b"\xAA\0\0\0\x01" as &[u8]);
1207+
let enc = encode_sections(&[vec![0xAA, 0xBB]]).unwrap();
1208+
assert_eq!(enc, b"\xAA\xBB\0\0\0\x02" as &[u8]);
1209+
let enc = encode_sections(&[vec![0x9D; 277]]).unwrap();
1210+
assert_eq!(enc, b"\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\0\0\x01\x15" as &[u8]);
1211+
}
1212+
1213+
#[test]
1214+
fn encode_sections_works_for_multiple_elements() {
1215+
let enc = encode_sections(&[vec![0xAA]]).unwrap();
1216+
assert_eq!(enc, b"\xAA\0\0\0\x01" as &[u8]);
1217+
let enc = encode_sections(&[vec![0xAA], vec![0xDE, 0xDE]]).unwrap();
1218+
assert_eq!(enc, b"\xAA\0\0\0\x01\xDE\xDE\0\0\0\x02" as &[u8]);
1219+
let enc = encode_sections(&[vec![0xAA], vec![0xDE, 0xDE], vec![]]).unwrap();
1220+
assert_eq!(enc, b"\xAA\0\0\0\x01\xDE\xDE\0\0\0\x02\0\0\0\0" as &[u8]);
1221+
let enc = encode_sections(&[vec![0xAA], vec![0xDE, 0xDE], vec![], vec![0xFF; 19]]).unwrap();
1222+
assert_eq!(enc, b"\xAA\0\0\0\x01\xDE\xDE\0\0\0\x02\0\0\0\0\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\x13" as &[u8]);
1223+
}
11671224
}

0 commit comments

Comments
 (0)