Principal size for stable structure

What’s the size of a Principal that should be reserved when defining a bounded size with the stable-structures?

 pub struct AnalyticKey {
        pub satellite_id: Principal, // <--- what's the size to reserve again?
        pub key: String,
        pub session_id: String,
    }

impl BoundedStorable for AnalyticKey {
     // TODO: replace with size_of::<String>() as u32 + size_of::<String>() as u32 + ????
    const MAX_SIZE: u32 = size_of::<AnalyticKey>() as u32;
    const IS_FIXED_SIZE: bool = false;
}

29 bytes

UPDATE: it’s indeed 29 but, see Convert principal to Vec 29 bytes length - #3 by levi for implementation which requires one extra byte.

Thanks @LightningLad91 for the anwer! You are right 29 bytes.

I searched in the doc too but, first occurence lead me to the glossary https://internetcomputer.org/docs/current/references/glossary/#principal which does not reference the size nor the other documentation page.

So I asked on the forum and only later gave another shot to the documentation :wink:.

Btw. - assuming size_of cannot be used to determine the size - is there a constant in Rust for these 29 bytes size of a Principal?

Something fishy.

I define const MAX_SIZE: u32 = (size_of::<String>() as u32 * 2) + 29; which equals 77 but, when I compile the canister and give a try I get an error message Expected <= 53 bytes, found 82 bytes at runtime.

Are you possibly looking at the textual encoding of the principal rather than the raw value? Or maybe it’s the candid encoding? I know if I want the raw principal value as a blob I have to do Principal.toBlob() in Motoko.

Might good be! Thanks for the input.

What’s weird is that if I print out following in the Rust playground

fn main() {
    println!("Size {:?}", (size_of::<String>() as u32 * 2) + 29);
}

I get 77 as a result. However, when I use the exact same code - (size_of::<String>() as u32 * 2) + 29 - to define my implementation for my struct in my stable-structures, I get the error max size limit 53 at runtime.

Been coding since 6am today and it’s 10pm, so I probably need to go to sleep and have another look tomorrow again, which I’ll do :grin:.

I don’t use rust so I’m not sure exactly what’s going on but If I’m understanding correctly it looks like there’s a 5 byte difference (82 - 77) between what you expect the size to be and the actual size of the serialized structure. I brought up candid encoding before because it adds some bytes to the data being encoded. I believe it adds characters ”DIDL” plus a type tag and was wondering if that could be the 5 bytes that isn’t being accounted for with the 29 bytes.

If the principal is being encoded with candid then it could have up to 34 bytes total (I think).

Oh that’s an excellent point, thanks for the input :+1:! I’ll give a try and report.

You were right @LightningLad91, the remaining issue I had was related to the Candid encoding. It adds the ”DIDL” but also serialize values and types (from what I understand from it source code).

pub fn serialize<W: io::Write>(&mut self, mut writer: W) -> Result<()> {
        writer.write_all(b"DIDL")?;
        self.type_ser.serialize()?;
        writer.write_all(self.type_ser.get_result())?;
        writer.write_all(self.value_ser.get_result())?;
        Ok(())
    }

So in addition to defining a proper size for the principal, I also replaced Candid serialization for a custom manual serialization as they do in Internet Idenditiy: https://github.com/dfinity/internet-identity/blob/0641ea641f64a2b51b3647ad1140840c2118b5d6/src/archive/src/main.rs#L212

When it comes to the Principal I ultimately actually serialized it to a bytes length of 30 (29 bytes + one for the length). See this answer for the reason why and how: Convert principal to Vec 29 bytes length - #3 by levi

Thanks a lot for the brainstorming and inputs @LightningLad91!