diff --git a/frameworks/moveos-stdlib/sources/bcs.move b/frameworks/moveos-stdlib/sources/bcs.move index 0fe9f05f1b..377a1aa137 100644 --- a/frameworks/moveos-stdlib/sources/bcs.move +++ b/frameworks/moveos-stdlib/sources/bcs.move @@ -21,6 +21,13 @@ module moveos_std::bcs{ const ErrorInvalidBool: u64 = 4; const ErrorOutOfRange: u64 = 5; const ErrorLengthOutOfRange: u64 = 6; + + /// A helper struct that saves resources on operations. For better + /// vector performance, it stores reversed bytes of the BCS and + /// enables use of `vector::pop_back`. + public struct BCS has store, copy, drop { + bytes: vector + } public fun to_bytes(v: &MoveValue): vector{ std::bcs::to_bytes(v) @@ -46,13 +53,29 @@ module moveos_std::bcs{ from_bytes
(v) } + /// Creates a new instance of BCS wrapper that holds inversed + /// bytes for better performance. + public fun new(mut bytes: vector): BCS { + bytes.reverse(); + BCS { bytes } + } + + /// Unpack the `BCS` struct returning the leftover bytes. + /// Useful for passing the data further after partial deserialization. + public fun into_remainder_bytes(bcs: BCS): vector { + let BCS { mut bytes } = bcs; + bytes.reverse(); + bytes + } + /// Read `address` value from the bcs-serialized bytes. - public fun peel_address(v: vector): address { - assert!(vector::length(&v) >= 32, ErrorInvalidLength); + public fun peel_address(bcs: &mut BCS): address { + let bytes = bcs.bytes; + assert!(vector::length(bytes) >= 32, ErrorInvalidLength); let i = 0; let addr_bytes = vector::empty(); while (i < 32) { - let byte = vector::pop_back(&mut v); + let byte = vector::pop_back(&mut bytes); vector::push_back(&mut addr_bytes, byte); i = i + 1; }; @@ -60,8 +83,9 @@ module moveos_std::bcs{ } /// Read a `bool` value from bcs-serialized bytes. - public fun peel_bool(v: vector): bool { - let value = peel_u8(v); + public fun peel_bool(bcs: &mut BCS): bool { + let bytes = bcs.bytes; + let value = peel_u8(bytes); if (value == 0) { return false } else if (value == 1) { @@ -72,19 +96,21 @@ module moveos_std::bcs{ } /// Read `u8` value from bcs-serialized bytes. - public fun peel_u8(v: vector): u8 { - assert!(vector::length(&v) >= 1, ErrorOutOfRange); - vector::pop_back(&mut v) + public fun peel_u8(bcs: &mut BCS): u8 { + let bytes = bcs.bytes; + assert!(vector::length(&bytes) >= 1, ErrorOutOfRange); + vector::pop_back(&mut bytes) } /// Read `u16` value from bcs-serialized bytes. - public fun peel_u16(v: vector): u16 { - assert!(vector::length(&v) >= 2, ErrorOutOfRange); + public fun peel_u16(bcs: &mut BCS): u16 { + let bytes = bcs.bytes; + assert!(vector::length(&bytes) >= 2, ErrorOutOfRange); let value = 0; let i = 0; let bits = 16u8; while (i < bits) { - let byte = (vector::pop_back(&mut v) as u16); + let byte = (vector::pop_back(&mut bytes) as u16); value = value + (byte << (i as u8)); i = i + 8; }; @@ -93,13 +119,14 @@ module moveos_std::bcs{ } /// Read `u32` value from bcs-serialized bytes. - public fun peel_u32(v: vector): u32 { - assert!(vector::length(&v) >= 4, ErrorOutOfRange); + public fun peel_u32(bcs: &mut BCS): u32 { + let bytes = bcs.bytes; + assert!(vector::length(&bytes) >= 4, ErrorOutOfRange); let value = 0; let i = 0; let bits = 32u8; while (i < bits) { - let byte = (vector::pop_back(&mut v) as u32); + let byte = (vector::pop_back(&mut bytes) as u32); value = value + (byte << (i as u8)); i = i + 8; }; @@ -108,13 +135,14 @@ module moveos_std::bcs{ } /// Read `u64` value from bcs-serialized bytes. - public fun peel_u64(v: vector): u64 { - assert!(vector::length(&v) >= 8, ErrorOutOfRange); + public fun peel_u64(bcs: &mut BCS): u64 { + let bytes = bcs.bytes; + assert!(vector::length(&bytes) >= 8, ErrorOutOfRange); let value = 0; let i = 0; let bits = 64u8; while (i < bits) { - let byte = (vector::pop_back(&mut v) as u64); + let byte = (vector::pop_back(&mut bytes) as u64); value = value + (byte << (i as u8)); i = i + 8; }; @@ -123,13 +151,14 @@ module moveos_std::bcs{ } /// Read `u128` value from bcs-serialized bytes. - public fun peel_u128(v: vector): u128 { - assert!(vector::length(&v) >= 16, ErrorOutOfRange); + public fun peel_u128(bcs: &mut BCS): u128 { + let bytes = bcs.bytes; + assert!(vector::length(&bytes) >= 16, ErrorOutOfRange); let value = 0; let i = 0; let bits = 128u8; while (i < bits) { - let byte = (vector::pop_back(&mut v) as u128); + let byte = (vector::pop_back(&mut bytes) as u128); value = value + (byte << (i as u8)); i = i + 8; }; @@ -138,13 +167,14 @@ module moveos_std::bcs{ } /// Read `u256` value from bcs-serialized bytes. - public fun peel_u256(v: vector): u256 { - assert!(vector::length(&v) >= 32, ErrorOutOfRange); + public fun peel_u256(bcs: &mut BCS): u256 { + let bytes = bcs.bytes; + assert!(vector::length(&bytes) >= 32, ErrorOutOfRange); let value = 0; let i = 0; let bits = 256u16; while (i < bits) { - let byte = (vector::pop_back(&mut v) as u256); + let byte = (vector::pop_back(&mut bytes) as u256); value = value + (byte << (i as u8)); i = i + 8; }; @@ -159,13 +189,14 @@ module moveos_std::bcs{ /// /// In BCS `vector` length is implemented with ULEB128; /// See more here: https://en.wikipedia.org/wiki/LEB128 - public fun peel_vec_length(v: vector): u64 { + public fun peel_vec_length(bcs: &mut BCS): u64 { + let bytes = bcs.bytes; let total = 0u64; let shift = 0; let len = 0; loop { assert!(len <= 4, ErrorLengthOutOfRange); - let byte = (vector::pop_back(&mut v) as u64); + let byte = (vector::pop_back(&mut bytes) as u64); len = len + 1; total = total | ((byte & 0x7f) << shift); if ((byte & 0x80) == 0) { @@ -177,12 +208,13 @@ module moveos_std::bcs{ } /// Peel a vector of `address` from serialized bytes. - public fun peel_vec_address(v: vector): vector
{ - let len = peel_vec_length(v); + public fun peel_vec_address(bcs: &mut BCS): vector
{ + let bytes = bcs.bytes; + let len = peel_vec_length(bytes); let i = 0; let addresses = vector::empty
(); while (i < len) { - let address = peel_address(v); + let address = peel_address(bytes); vector::push_back(&mut addresses, address); i = i + 1; }; @@ -190,12 +222,13 @@ module moveos_std::bcs{ } /// Peel a vector of `address` from serialized bytes. - public fun peel_vec_bool(v: vector): vector { - let len = peel_vec_length(v); + public fun peel_vec_bool(bcs: &mut BCS): vector { + let bytes = bcs.bytes; + let len = peel_vec_length(bytes); let i = 0; let bools = vector::empty(); while (i < len) { - let bool = peel_bool(v); + let bool = peel_bool(bytes); vector::push_back(&mut bools, bool); i = i + 1; }; @@ -203,12 +236,13 @@ module moveos_std::bcs{ } /// Peel a vector of `u8` (eg string) from serialized bytes. - public fun peel_vec_u8(v: vector): vector { - let len = peel_vec_length(v); + public fun peel_vec_u8(bcs: &mut BCS): vector { + let bytes = bcs.bytes; + let len = peel_vec_length(bytes); let i = 0; let u8s = vector::empty(); while (i < len) { - let u8 = peel_u8(v); + let u8 = peel_u8(bytes); vector::push_back(&mut u8s, u8); i = i + 1; }; @@ -216,12 +250,13 @@ module moveos_std::bcs{ } /// Peel a `vector>` (eg vec of string) from serialized bytes. - public fun peel_vec_vec_u8(v: vector): vector> { - let len = peel_vec_length(v); + public fun peel_vec_vec_u8(bcs: &mut BCS): vector> { + let bytes = bcs.bytes; + let len = peel_vec_length(bytes); let i = 0; let vec_u8s = vector::empty>(); while (i < len) { - let vec_u8 = peel_vec_u8(v); + let vec_u8 = peel_vec_u8(bytes); vector::push_back(&mut vec_u8s, vec_u8); i = i + 1; }; @@ -229,12 +264,13 @@ module moveos_std::bcs{ } /// Peel a vector of `u16` from serialized bytes. - public fun peel_vec_u16(v: vector): vector { - let len = peel_vec_length(v); + public fun peel_vec_u16(bcs: &mut BCS): vector { + let bytes = bcs.bytes; + let len = peel_vec_length(bytes); let i = 0; let u16s = vector::empty(); while (i < len) { - let u16 = peel_u16(v); + let u16 = peel_u16(bytes); vector::push_back(&mut u16s, u16); i = i + 1; }; @@ -242,12 +278,13 @@ module moveos_std::bcs{ } /// Peel a vector of `u32` from serialized bytes. - public fun peel_vec_u32(v: vector): vector { - let len = peel_vec_length(v); + public fun peel_vec_u32(bcs: &mut BCS): vector { + let bytes = bcs.bytes; + let len = peel_vec_length(bytes); let i = 0; let u32s = vector::empty(); while (i < len) { - let u32 = peel_u32(v); + let u32 = peel_u32(bytes); vector::push_back(&mut u32s, u32); i = i + 1; }; @@ -255,12 +292,13 @@ module moveos_std::bcs{ } /// Peel a vector of `u64` from serialized bytes. - public fun peel_vec_u64(v: vector): vector { - let len = peel_vec_length(v); + public fun peel_vec_u64(bcs: &mut BCS): vector { + let bytes = bcs.bytes; + let len = peel_vec_length(bytes); let i = 0; let u64s = vector::empty(); while (i < len) { - let u64 = peel_u64(v); + let u64 = peel_u64(bytes); vector::push_back(&mut u64s, u64); i = i + 1; }; @@ -268,12 +306,13 @@ module moveos_std::bcs{ } /// Peel a vector of `u128` from serialized bytes. - public fun peel_vec_u128(v: vector): vector { - let len = peel_vec_length(v); + public fun peel_vec_u128(bcs: &mut BCS): vector { + let bytes = bcs.bytes; + let len = peel_vec_length(bytes); let i = 0; let u128s = vector::empty(); while (i < len) { - let u128 = peel_u128(v); + let u128 = peel_u128(bytes); vector::push_back(&mut u128s, u128); i = i + 1; }; @@ -281,12 +320,13 @@ module moveos_std::bcs{ } /// Peel a vector of `u256` from serialized bytes. - public fun peel_vec_u256(v: vector): vector { - let len = peel_vec_length(v); + public fun peel_vec_u256(bcs: &mut BCS): vector { + let bytes = bcs.bytes; + let len = peel_vec_length(bytes); let i = 0; let u256s = vector::empty(); while (i < len) { - let u256 = peel_u256(v); + let u256 = peel_u256(bytes); vector::push_back(&mut u256s, u256); i = i + 1; }; @@ -296,72 +336,80 @@ module moveos_std::bcs{ // === Option === /// Peel `Option
` from serialized bytes. - public fun peel_option_address(v: vector): Option
{ - if (peel_bool(v)) { - option::some(peel_address(v)) + public fun peel_option_address(bcs: &mut BCS): Option
{ + let bytes = bcs.bytes; + if (peel_bool(bytes)) { + option::some(peel_address(bytes)) } else { option::none() } } /// Peel `Option` from serialized bytes. - public fun peel_option_bool(v: vector): Option { - if (peel_bool(v)) { - option::some(peel_bool(v)) + public fun peel_option_bool(bcs: &mut BCS): Option { + let bytes = bcs.bytes; + if (peel_bool(bytes)) { + option::some(peel_bool(bytes)) } else { option::none() } } /// Peel `Option` from serialized bytes. - public fun peel_option_u8(v: vector): Option { - if (peel_bool(v)) { - option::some(peel_u8(v)) + public fun peel_option_u8(bcs: &mut BCS): Option { + let bytes = bcs.bytes; + if (peel_bool(bytes)) { + option::some(peel_u8(bytes)) } else { option::none() } } /// Peel `Option` from serialized bytes. - public fun peel_option_u16(v: vector): Option { - if (peel_bool(v)) { - option::some(peel_u16(v)) + public fun peel_option_u16(bcs: &mut BCS): Option { + let bytes = bcs.bytes; + if (peel_bool(bytes)) { + option::some(peel_u16(bytes)) } else { option::none() } } /// Peel `Option` from serialized bytes. - public fun peel_option_u32(v: vector): Option { - if (peel_bool(v)) { - option::some(peel_u32(v)) + public fun peel_option_u32(bcs: &mut BCS): Option { + let bytes = bcs.bytes; + if (peel_bool(bytes)) { + option::some(peel_u32(bytes)) } else { option::none() } } /// Peel `Option` from serialized bytes. - public fun peel_option_u64(v: vector): Option { - if (peel_bool(v)) { - option::some(peel_u64(v)) + public fun peel_option_u64(bcs: &mut BCS): Option { + let bytes = bcs.bytes; + if (peel_bool(bytes)) { + option::some(peel_u64(bytes)) } else { option::none() } } /// Peel `Option` from serialized bytes. - public fun peel_option_u128(v: vector): Option { - if (peel_bool(v)) { - option::some(peel_u128(v)) + public fun peel_option_u128(bcs: &mut BCS): Option { + let bytes = bcs.bytes; + if (peel_bool(bytes)) { + option::some(peel_u128(bytes)) } else { option::none() } } /// Peel `Option` from serialized bytes. - public fun peel_option_u256(v: vector): Option { - if (peel_bool(v)) { - option::some(peel_u256(v)) + public fun peel_option_u256(bcs: &mut BCS): Option { + let bytes = bcs.bytes; + if (peel_bool(bytes)) { + option::some(peel_u256(bytes)) } else { option::none() } @@ -387,4 +435,5 @@ module moveos_std::bcs{ native public(friend) fun native_from_bytes(bytes: vector): Option; // TODO: add test cases for this module. + }