Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Bytes to_buffer and to_alloc_vec #1231

Merged
merged 8 commits into from
Feb 23, 2024
Merged

Add Bytes to_buffer and to_alloc_vec #1231

merged 8 commits into from
Feb 23, 2024

Conversation

leighmcculloch
Copy link
Member

@leighmcculloch leighmcculloch commented Feb 14, 2024

What

Add Bytes to_buffer and to_alloc_vec functions that provide access to the bytes in local guest memory. The to_buffer function provides access to the bytes in a statically specified buffer. The to_alloc_vec function is available only when the alloc feature is enabled and provides access to an alloc::vec::Vec containing the bytes.

Why

@tomerweller was talking to me about allocator usage in dev and testing when experimenting and costs are not that high, and it got me thinking that I mostly don't use the allocator because it's not trivial to get data into an allocated type. It's not that hard, but I don't think it's obvious and it's something we need to explain to people how to do.

These functions provide two ways to get access to the data inside a variable length Bytes value, whether using the allocator or not.

The first, to_buffer accepts as a constant generic parameter a buffer size, and returns the buffer filled with the contents of the bytes along with a Range that describes the slice of that buffer that contains the bytes. The bytes may not fill the full buffer and the range that case describes where the bytes begins (always 0) and ends. The function is usable regardless of whether the allocator is in use, and is suitable when a reasonable maximum size is known, but an exact size is not.

The to_alloc_vec function requires the alloc feature as it dynamically allocates the memory required to store the bytes on the guest side.

Costs

Examples of costs of using these functions:

to_alloc_vec

The to_alloc_vec function with a small bytes value results in:

  • Contract Size: 3,835 bytes
  • CPU Used: 5,001,490
  • Bytes Read: 3,992
  • Fee Charged: 59,245
#[contract]
pub struct Contract;

#[contractimpl]
impl Contract {
    pub fn empty(env: Env) -> u32 {
        let bytes = Bytes::from_slice(&env, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
        let len = bytes.to_alloc_vec().as_slice().len();
        len as u32
    }
}

to_buffer::<10>

The to_buffer function with a buffer size of 10 and a small bytes value results in:

  • Contract Size: 599 bytes
  • CPU Used: 3,828,048
  • Bytes Read: 756
  • Fee Charged: 44,351
#[contract]
pub struct Contract;

#[contractimpl]
impl Contract {
    pub fn empty(env: Env) -> u32 {
        let bytes = Bytes::from_slice(&env, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
        let len = bytes.to_buffer::<10>().as_slice().len();
        len as u32
    }
}

to_buffer::<1024>

The to_buffer function with a buffer size of 1024 and a small bytes value results in:

  • Contract Size: 807 bytes
  • CPU Used: 3,914,448
  • Bytes Read: 964
  • Fee Charged: 45,418
#[contract]
pub struct Contract;

#[contractimpl]
impl Contract {
    pub fn empty(env: Env) -> u32 {
        let bytes = Bytes::from_slice(&env, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
        let len = bytes.to_buffer::<10>().as_slice().len();
        len as u32
    }
}

@leighmcculloch leighmcculloch marked this pull request as ready for review February 14, 2024 14:42
Copy link
Contributor

@graydon graydon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally like it! I expect most people will use BytesBuffer for some fixed-size N and be perfectly happy with that! Couple small nits inline.

soroban-sdk/src/bytes.rs Outdated Show resolved Hide resolved
soroban-sdk/src/bytes.rs Outdated Show resolved Hide resolved
@leighmcculloch leighmcculloch added this pull request to the merge queue Feb 23, 2024
Copy link
Contributor

@graydon graydon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You decided against Box<[u8]>? Either way, LGTM

Merged via the queue into main with commit 9d802fc Feb 23, 2024
21 checks passed
@leighmcculloch leighmcculloch deleted the tinnily-vaporium branch February 23, 2024 18:44
@leighmcculloch
Copy link
Member Author

You decided against Box<[u8]>?

The functionality for creating Box for slices is all nightly apis. I couldn't see a way to create one without using unsafe so I bailed. You can create one from a Vec but that seemed a bit pointless as it would still pull in Vec.

Lmk if I missed something and there's a safe way to do it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants