1use crate::error::{Result, X86Error, io_error};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use sha2::{Digest, Sha256};
5use std::fs;
6use std::path::{Path, PathBuf};
7
8pub const STATE_MAGIC: u32 = 0x8676_8676;
9pub const STATE_VERSION: u32 = 6;
10pub const STATE_HEADER_LEN: usize = 16;
11pub const ZSTD_MAGIC: u32 = 0xFD2F_B528;
12
13#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
14pub struct StateHeader {
15 pub magic: u32,
16 pub version: u32,
17 pub total_length: u32,
18 pub metadata_length: u32,
19}
20
21#[derive(Debug, Clone)]
22pub struct SavedState {
23 encoded: Vec<u8>,
24 decoded: Vec<u8>,
25 metadata: Value,
26 header: StateHeader,
27 compressed: bool,
28 source: Option<PathBuf>,
29}
30
31#[derive(Debug, Clone, Serialize)]
32pub struct StateSummary {
33 pub compressed: bool,
34 pub encoded_bytes: usize,
35 pub decoded_bytes: usize,
36 pub version: u32,
37 pub buffer_count: usize,
38 pub memory_bytes: Option<u64>,
39 pub sha256: String,
40 pub source: Option<PathBuf>,
41}
42
43impl SavedState {
44 pub fn from_bytes(bytes: impl Into<Vec<u8>>) -> Result<Self> {
45 let encoded = bytes.into();
46 let (decoded, compressed) = decode(&encoded)?;
47 let header = parse_header(&decoded)?;
48 let metadata_end = STATE_HEADER_LEN + header.metadata_length as usize;
49 let metadata: Value = serde_json::from_slice(&decoded[STATE_HEADER_LEN..metadata_end])?;
50 if !metadata.get("state").is_some() || !metadata.get("buffer_infos").is_some() {
51 return Err(X86Error::InvalidState(
52 "metadata must contain state and buffer_infos".to_owned(),
53 ));
54 }
55 Ok(Self {
56 encoded,
57 decoded,
58 metadata,
59 header,
60 compressed,
61 source: None,
62 })
63 }
64
65 pub fn from_file(path: impl AsRef<Path>) -> Result<Self> {
66 let path = path.as_ref();
67 let bytes = fs::read(path).map_err(|source| io_error(path, source))?;
68 let mut state = Self::from_bytes(bytes)?;
69 state.source = Some(path.to_path_buf());
70 Ok(state)
71 }
72
73 pub fn header(&self) -> StateHeader {
74 self.header
75 }
76
77 pub fn metadata(&self) -> &Value {
78 &self.metadata
79 }
80
81 pub fn encoded_bytes(&self) -> &[u8] {
82 &self.encoded
83 }
84
85 pub fn decoded_bytes(&self) -> &[u8] {
86 &self.decoded
87 }
88
89 pub fn is_compressed(&self) -> bool {
90 self.compressed
91 }
92
93 pub fn source(&self) -> Option<&Path> {
94 self.source.as_deref()
95 }
96
97 pub fn buffer_count(&self) -> usize {
98 self.metadata
99 .get("buffer_infos")
100 .and_then(Value::as_array)
101 .map_or(0, Vec::len)
102 }
103
104 pub fn memory_bytes(&self) -> Option<u64> {
105 self.metadata
106 .get("state")
107 .and_then(Value::as_array)
108 .and_then(|items| items.first())
109 .and_then(Value::as_u64)
110 }
111
112 pub fn cpu_state_and_buffers(&self) -> Result<(Value, Vec<Vec<u8>>)> {
116 let state = self
117 .metadata
118 .get("state")
119 .cloned()
120 .ok_or_else(|| X86Error::InvalidState("missing state array".to_owned()))?;
121 let infos = self
122 .metadata
123 .get("buffer_infos")
124 .and_then(Value::as_array)
125 .ok_or_else(|| X86Error::InvalidState("missing buffer_infos array".to_owned()))?;
126 let buffer_block_start = (STATE_HEADER_LEN + self.header.metadata_length as usize + 3) & !3;
127 let mut buffers = Vec::with_capacity(infos.len());
128 for (index, info) in infos.iter().enumerate() {
129 let offset = info.get("offset").and_then(Value::as_u64).ok_or_else(|| {
130 X86Error::InvalidState(format!("buffer_infos[{index}] has no offset"))
131 })? as usize;
132 let length = info.get("length").and_then(Value::as_u64).ok_or_else(|| {
133 X86Error::InvalidState(format!("buffer_infos[{index}] has no length"))
134 })? as usize;
135 let start = buffer_block_start
136 .checked_add(offset)
137 .ok_or_else(|| X86Error::InvalidState("buffer offset overflow".to_owned()))?;
138 let end = start
139 .checked_add(length)
140 .ok_or_else(|| X86Error::InvalidState("buffer length overflow".to_owned()))?;
141 let bytes = self.decoded.get(start..end).ok_or_else(|| {
142 X86Error::InvalidState(format!("buffer {index} exceeds decoded state"))
143 })?;
144 buffers.push(bytes.to_vec());
145 }
146 Ok((state, buffers))
147 }
148
149 pub fn sha256(&self) -> String {
150 let mut hasher = Sha256::new();
151 hasher.update(&self.decoded);
152 hex::encode(hasher.finalize())
153 }
154
155 pub fn summary(&self) -> StateSummary {
156 StateSummary {
157 compressed: self.compressed,
158 encoded_bytes: self.encoded.len(),
159 decoded_bytes: self.decoded.len(),
160 version: self.header.version,
161 buffer_count: self.buffer_count(),
162 memory_bytes: self.memory_bytes(),
163 sha256: self.sha256(),
164 source: self.source.clone(),
165 }
166 }
167
168 pub fn write_decoded(&self, path: impl AsRef<Path>) -> Result<()> {
169 let path = path.as_ref();
170 fs::write(path, &self.decoded).map_err(|source| io_error(path, source))
171 }
172}
173
174fn read_u32(bytes: &[u8], offset: usize) -> Result<u32> {
175 let slice = bytes
176 .get(offset..offset + 4)
177 .ok_or_else(|| X86Error::InvalidState("truncated header".to_owned()))?;
178 Ok(u32::from_le_bytes(slice.try_into().unwrap()))
179}
180
181pub fn parse_header(bytes: &[u8]) -> Result<StateHeader> {
182 if bytes.len() < STATE_HEADER_LEN {
183 return Err(X86Error::InvalidState(
184 "state is shorter than header".to_owned(),
185 ));
186 }
187 let header = StateHeader {
188 magic: read_u32(bytes, 0)?,
189 version: read_u32(bytes, 4)?,
190 total_length: read_u32(bytes, 8)?,
191 metadata_length: read_u32(bytes, 12)?,
192 };
193 if header.magic != STATE_MAGIC {
194 return Err(X86Error::InvalidState(format!(
195 "invalid magic 0x{:08x}",
196 header.magic
197 )));
198 }
199 if header.version != STATE_VERSION {
200 return Err(X86Error::UnsupportedFormat(format!(
201 "saved state version {}; supported version is {}",
202 header.version, STATE_VERSION
203 )));
204 }
205 let metadata_end = STATE_HEADER_LEN
206 .checked_add(header.metadata_length as usize)
207 .ok_or_else(|| X86Error::InvalidState("metadata length overflow".to_owned()))?;
208 if metadata_end > bytes.len() {
209 return Err(X86Error::InvalidState(
210 "metadata exceeds state length".to_owned(),
211 ));
212 }
213 Ok(header)
214}
215
216pub fn decode(bytes: &[u8]) -> Result<(Vec<u8>, bool)> {
217 let compressed =
218 bytes.len() >= 4 && u32::from_le_bytes(bytes[0..4].try_into().unwrap()) == ZSTD_MAGIC;
219 if compressed {
220 #[cfg(feature = "zstd")]
221 {
222 let decoded = zstd::stream::decode_all(bytes)
223 .map_err(|error| X86Error::InvalidState(format!("zstd decode failed: {error}")))?;
224 return Ok((decoded, true));
225 }
226 #[cfg(not(feature = "zstd"))]
227 {
228 return Err(X86Error::UnsupportedFormat(
229 "zstd support is disabled; enable the `zstd` feature".to_owned(),
230 ));
231 }
232 }
233 Ok((bytes.to_vec(), false))
234}