Skip to main content

Module 0x2::bcs

This module implements BCS (de)serialization in Move. Full specification can be found here: https://github.com/diem/bcs

Short summary (for Move-supported types):

  • address - sequence of X bytes
  • bool - byte with 0 or 1
  • u8 - a single u8 byte
  • u64 / u128 / u256 - LE bytes
  • vector - ULEB128 length + LEN elements
  • option - first byte bool: None (0) or Some (1), then value

Usage example:

/// This function reads u8 and u64 value from the input
/// and returns the rest of the bytes.
fun deserialize(bytes: vector<u8>): (u8, u64, vector<u8>) {
use sui::bcs::{Self, BCS};

let prepared: BCS = bcs::new(bytes);
let (u8_value, u64_value) = (
prepared.peel_u8(),
prepared.peel_u64()
);

// unpack bcs struct
let leftovers = prepared.into_remainder_bytes();

(u8_value, u64_value, leftovers)
}
use 0x1::bcs;
use 0x1::option;
use 0x1::vector;
use 0x2::address;

Struct BCS

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.

struct BCS has copy, drop, store
Fields
bytes: vector<u8>

Constants

For when ULEB byte is out of range (or not found).

const ELenOutOfRange: u64 = 2;

For when the boolean value different than 0 or 1.

const ENotBool: u64 = 1;

For when bytes length is less than required for deserialization.

const EOutOfRange: u64 = 0;

Function to_bytes

Get BCS serialized bytes for any value. Re-exports stdlib bcs::to_bytes.

public fun to_bytes<T>(value: &T): vector<u8>
Implementation
public fun to_bytes<T>(value: &T): vector<u8> {
    bcs::to_bytes(value)
}

Function new

Creates a new instance of BCS wrapper that holds inversed bytes for better performance.

public fun new(bytes: vector<u8>): bcs::BCS
Implementation
public fun new(mut bytes: vector<u8>): BCS {
    bytes.reverse();
    BCS { bytes }
}

Function into_remainder_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::BCS): vector<u8>
Implementation
public fun into_remainder_bytes(bcs: BCS): vector<u8> {
    let BCS { mut bytes } = bcs;
    bytes.reverse();
    bytes
}

Function peel_address

Read address from the bcs-serialized bytes.

public fun peel_address(bcs: &mut bcs::BCS): address
Implementation
public fun peel_address(bcs: &mut BCS): address {
    assert!(bcs.bytes.length() >= address::length(), EOutOfRange);
    let (mut addr_bytes, mut i) = (vector[], 0);
    while (i < address::length()) {
        addr_bytes.push_back(bcs.bytes.pop_back());
        i = i + 1;
    };
    address::from_bytes(addr_bytes)
}

Function peel_bool

Read a bool value from bcs-serialized bytes.

public fun peel_bool(bcs: &mut bcs::BCS): bool
Implementation
public fun peel_bool(bcs: &mut BCS): bool {
    let value = bcs.peel_u8();
    if (value == 0) {
        false
    } else if (value == 1) {
        true
    } else {
        abort ENotBool
    }
}

Function peel_u8

Read u8 value from bcs-serialized bytes.

public fun peel_u8(bcs: &mut bcs::BCS): u8
Implementation
public fun peel_u8(bcs: &mut BCS): u8 {
    assert!(bcs.bytes.length() >= 1, EOutOfRange);
    bcs.bytes.pop_back()
}

Function peel_u64

Read u64 value from bcs-serialized bytes.

public fun peel_u64(bcs: &mut bcs::BCS): u64
Implementation
public fun peel_u64(bcs: &mut BCS): u64 {
    assert!(bcs.bytes.length() >= 8, EOutOfRange);

    let (mut value, mut i) = (0u64, 0u8);
    while (i < 64) {
        let byte = bcs.bytes.pop_back() as u64;
        value = value + (byte << i);
        i = i + 8;
    };

    value
}

Function peel_u128

Read u128 value from bcs-serialized bytes.

public fun peel_u128(bcs: &mut bcs::BCS): u128
Implementation
public fun peel_u128(bcs: &mut BCS): u128 {
    assert!(bcs.bytes.length() >= 16, EOutOfRange);

    let (mut value, mut i) = (0u128, 0u8);
    while (i < 128) {
        let byte = bcs.bytes.pop_back() as u128;
        value = value + (byte << i);
        i = i + 8;
    };

    value
}

Function peel_u256

Read u256 value from bcs-serialized bytes.

public fun peel_u256(bcs: &mut bcs::BCS): u256
Implementation
public fun peel_u256(bcs: &mut BCS): u256 {
    assert!(bcs.bytes.length() >= 32, EOutOfRange);

    let (mut value, mut i) = (0u256, 0u16);
    while (i < 256) {
        let byte = bcs.bytes.pop_back() as u256;
        value = value + (byte << (i as u8));
        i = i + 8;
    };

    value
}

Function peel_vec_length

Read ULEB bytes expecting a vector length. Result should then be used to perform peel_* operation LEN times.

In BCS vector length is implemented with ULEB128; See more here: https://en.wikipedia.org/wiki/LEB128

public fun peel_vec_length(bcs: &mut bcs::BCS): u64
Implementation
public fun peel_vec_length(bcs: &mut BCS): u64 {
    let (mut total, mut shift, mut len) = (0u64, 0, 0);
    while (true) {
        assert!(len <= 4, ELenOutOfRange);
        let byte = bcs.bytes.pop_back() as u64;
        len = len + 1;
        total = total | ((byte & 0x7f) << shift);
        if ((byte & 0x80) == 0) {
            break
        };
        shift = shift + 7;
    };
    total
}

Function peel_vec_address

Peel a vector of address from serialized bytes.

public fun peel_vec_address(bcs: &mut bcs::BCS): vector<address>
Implementation
public fun peel_vec_address(bcs: &mut BCS): vector<address> {
    let (len, mut i, mut res) = (bcs.peel_vec_length(), 0, vector[]);
    while (i < len) {
        res.push_back(bcs.peel_address());
        i = i + 1;
    };
    res
}

Function peel_vec_bool

Peel a vector of address from serialized bytes.

public fun peel_vec_bool(bcs: &mut bcs::BCS): vector<bool>
Implementation
public fun peel_vec_bool(bcs: &mut BCS): vector<bool> {
    let (len, mut i, mut res) = (bcs.peel_vec_length(), 0, vector[]);
    while (i < len) {
        res.push_back(bcs.peel_bool());
        i = i + 1;
    };
    res
}

Function peel_vec_u8

Peel a vector of u8 (eg string) from serialized bytes.

public fun peel_vec_u8(bcs: &mut bcs::BCS): vector<u8>
Implementation
public fun peel_vec_u8(bcs: &mut BCS): vector<u8> {
    let (len, mut i, mut res) = (bcs.peel_vec_length(), 0, vector[]);
    while (i < len) {
        res.push_back(bcs.peel_u8());
        i = i + 1;
    };
    res
}

Function peel_vec_vec_u8

Peel a vector<vector<u8>> (eg vec of string) from serialized bytes.

public fun peel_vec_vec_u8(bcs: &mut bcs::BCS): vector<vector<u8>>
Implementation
public fun peel_vec_vec_u8(bcs: &mut BCS): vector<vector<u8>> {
    let (len, mut i, mut res) = (bcs.peel_vec_length(), 0, vector[]);
    while (i < len) {
        res.push_back(bcs.peel_vec_u8());
        i = i + 1;
    };
    res
}

Function peel_vec_u64

Peel a vector of u64 from serialized bytes.

public fun peel_vec_u64(bcs: &mut bcs::BCS): vector<u64>
Implementation
public fun peel_vec_u64(bcs: &mut BCS): vector<u64> {
    let (len, mut i, mut res) = (bcs.peel_vec_length(), 0, vector[]);
    while (i < len) {
        res.push_back(bcs.peel_u64());
        i = i + 1;
    };
    res
}

Function peel_vec_u128

Peel a vector of u128 from serialized bytes.

public fun peel_vec_u128(bcs: &mut bcs::BCS): vector<u128>
Implementation
public fun peel_vec_u128(bcs: &mut BCS): vector<u128> {
    let (len, mut i, mut res) = (bcs.peel_vec_length(), 0, vector[]);
    while (i < len) {
        res.push_back(bcs.peel_u128());
        i = i + 1;
    };
    res
}

Function peel_option_address

Peel Option<address> from serialized bytes.

public fun peel_option_address(bcs: &mut bcs::BCS): option::Option<address>
Implementation
public fun peel_option_address(bcs: &mut BCS): Option<address> {
    if (bcs.peel_bool()) {
        option::some(bcs.peel_address())
    } else {
        option::none()
    }
}

Function peel_option_bool

Peel Option<bool> from serialized bytes.

public fun peel_option_bool(bcs: &mut bcs::BCS): option::Option<bool>
Implementation
public fun peel_option_bool(bcs: &mut BCS): Option<bool> {
    if (bcs.peel_bool()) {
        option::some(bcs.peel_bool())
    } else {
        option::none()
    }
}

Function peel_option_u8

Peel Option<u8> from serialized bytes.

public fun peel_option_u8(bcs: &mut bcs::BCS): option::Option<u8>
Implementation
public fun peel_option_u8(bcs: &mut BCS): Option<u8> {
    if (bcs.peel_bool()) {
        option::some(bcs.peel_u8())
    } else {
        option::none()
    }
}

Function peel_option_u64

Peel Option<u64> from serialized bytes.

public fun peel_option_u64(bcs: &mut bcs::BCS): option::Option<u64>
Implementation
public fun peel_option_u64(bcs: &mut BCS): Option<u64> {
    if (bcs.peel_bool()) {
        option::some(bcs.peel_u64())
    } else {
        option::none()
    }
}

Function peel_option_u128

Peel Option<u128> from serialized bytes.

public fun peel_option_u128(bcs: &mut bcs::BCS): option::Option<u128>
Implementation
public fun peel_option_u128(bcs: &mut BCS): Option<u128> {
    if (bcs.peel_bool()) {
        option::some(bcs.peel_u128())
    } else {
        option::none()
    }
}