Deserialize bytes to a ViewState Backward-compatible: if the v2 fields (`kind`, `parent`) are missing (i.e., the data was written by an older version), defaults to `ViewScope::Shared` and `parent: None`.
(bytes: &[u8])
| 85 | /// (i.e., the data was written by an older version), defaults to |
| 86 | /// `ViewScope::Shared` and `parent: None`. |
| 87 | pub fn deserialize_view_state(bytes: &[u8]) -> PristineResult<ViewState> { |
| 88 | const MIN_SIZE: usize = 8 + 4; // id + name_len |
| 89 | |
| 90 | if bytes.len() < MIN_SIZE { |
| 91 | return Err(PristineError::Serialization { |
| 92 | message: "view state too short".to_string(), |
| 93 | }); |
| 94 | } |
| 95 | |
| 96 | let id = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); |
| 97 | let name_len = u32::from_le_bytes(bytes[8..12].try_into().unwrap()) as usize; |
| 98 | |
| 99 | // v1 size: header(12) + name + merkle(32) + change_count(8) |
| 100 | let v1_size = 12 + name_len + 32 + 8; |
| 101 | if bytes.len() < v1_size { |
| 102 | return Err(PristineError::Serialization { |
| 103 | message: "view state truncated".to_string(), |
| 104 | }); |
| 105 | } |
| 106 | |
| 107 | let name = String::from_utf8(bytes[12..12 + name_len].to_vec()).map_err(|_| { |
| 108 | PristineError::Serialization { |
| 109 | message: "invalid view name encoding".to_string(), |
| 110 | } |
| 111 | })?; |
| 112 | |
| 113 | let mut merkle_bytes = [0u8; 32]; |
| 114 | merkle_bytes.copy_from_slice(&bytes[12 + name_len..12 + name_len + 32]); |
| 115 | let state = Merkle::from_bytes(merkle_bytes); |
| 116 | |
| 117 | let change_count = u64::from_le_bytes( |
| 118 | bytes[12 + name_len + 32..12 + name_len + 40] |
| 119 | .try_into() |
| 120 | .unwrap(), |
| 121 | ); |
| 122 | |
| 123 | // v2 fields: kind(1) + parent(8) = 9 additional bytes |
| 124 | let v2_size = v1_size + 1 + 8; |
| 125 | let (kind, parent) = if bytes.len() >= v2_size { |
| 126 | let kind_byte = bytes[v1_size]; |
| 127 | let kind = ViewScope::from_u8(kind_byte).ok_or_else(|| PristineError::Serialization { |
| 128 | message: format!("invalid view kind: {}", kind_byte), |
| 129 | })?; |
| 130 | |
| 131 | let parent_val = u64::from_le_bytes(bytes[v1_size + 1..v1_size + 9].try_into().unwrap()); |
| 132 | let parent = if parent_val == NO_PARENT { |
| 133 | None |
| 134 | } else { |
| 135 | Some(parent_val) |
| 136 | }; |
| 137 | |
| 138 | (kind, parent) |
| 139 | } else { |
| 140 | // v1 data — default to Shared with no parent |
| 141 | (ViewScope::Shared, None) |
| 142 | }; |
| 143 | |
| 144 | Ok(ViewState { |