gioser

tawasuyu/brahman

Rust 570TOML 215Markdown 68JSON 198261 Definitions

⎇ 43e6b32e15dea99bd57a9a11fdafa7bd9a2a236f renaser/formato/src/lib.rs Rust · 286 lines · 11.9 KB History Who touched what raw

feat(cosmobiologia-corpus): tomografía por dominio + plantilla y guía 43e6b32e1 · sergio ·

1// =============================================================================
2//  renaser :: formato — el formato del grafo de objetos en disco
3// -----------------------------------------------------------------------------
4//  Hasta la Fase 7a, el formato del grafo de objetos —el superbloque, los
5//  registros del log, el manifiesto— vivia disperso entre `kernel/almacen.rs`
6//  y `kernel/manifiesto.rs`. Lo conocia solo el kernel.
7//
8//  La Fase 7b se lo entrega tambien a `boot`: el constructor de imagen de
9//  ANFITRION debe sembrar el disco con el grafo ya poblado —los objetos de
10//  bytecode y el Manifiesto de Genesis— para que el kernel jamas vuelva a
11//  empotrar una sola app. Para ello, kernel y boot han de hablar EXACTAMENTE
12//  el mismo formato: la misma serializacion, el mismo hash, el mismo trazado
13//  de registros en el log.
14//
15//  Esta crate es esa unica verdad. Es un nucleo `#![no_std]` —el kernel
16//  bare-metal la enlaza— y, por ser no_std, el anfitrion `boot` la compila sin
17//  friccion. Define los tipos del grafo, su (de)serializacion `postcard`, la
18//  funcion hash BLAKE3 que da identidad a cada objeto y el trazado de un
19//  registro en el log. Ni kernel ni boot vuelven a definir nada de esto.
20// =============================================================================
21
22#![no_std]
23
24extern crate alloc;
25
26use alloc::string::String;
27use alloc::vec;
28use alloc::vec::Vec;
29
30use serde::{Deserialize, Serialize};
31
32// =============================================================================
33//  Constantes del formato en disco
34// =============================================================================
35
36/// Firma magica del superbloque — «RENASer GRaFo». Distingue un disco de
37/// renaser de uno virgen o ajeno.
38pub const MAGIA: [u8; 8] = *b"RENASGRF";
39
40/// Version del formato del superbloque en disco. Un disco con otra version se
41/// reformatea al arrancar. v2 (Fase 7) — el superbloque porta el ancla
42/// `manifiesto`, gemela de `raiz`.
43pub const VERSION_SUPERBLOQUE: u32 = 2;
44
45/// Version del formato del manifiesto serializado. Independiente de la del
46/// superbloque: el manifiesto es un objeto del grafo, no una estructura fija
47/// del disco.
48pub const VERSION_MANIFIESTO: u32 = 1;
49
50/// Techo del tamaño de un objeto serializado: 1 MiB. Acota los buferes de E/S
51/// y permite descartar un registro corrupto sin leer un disparate.
52pub const MAX_OBJETO: usize = 1024 * 1024;
53
54/// Tamaño de un sector del disco, en bytes. El log se traza en multiplos de
55/// esta unidad — la misma que expone el transporte virtio-blk.
56pub const TAM_SECTOR: usize = 512;
57
58/// El identificador de un objeto: el hash BLAKE3 de su forma serializada. En
59/// un almacen direccionado por contenido, la identidad ES el contenido.
60pub type Hash = [u8; 32];
61
62// =============================================================================
63//  Los tipos del grafo
64// =============================================================================
65
66/// Un objeto del grafo: una carga util opaca y las aristas que lo enlazan con
67/// otros objetos. Los `hijos` hacen del almacen un DAG —no un arbol—: un
68/// objeto puede ser hijo de muchos, y el direccionamiento por contenido
69/// garantiza que cada contenido distinto se guarda una sola vez.
70#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
71pub struct Objeto {
72    /// La carga util del objeto: bytes crudos, que nadie interpreta aqui.
73    pub datos: Vec<u8>,
74    /// Los hashes de los objetos hijos: las aristas salientes del DAG.
75    pub hijos: Vec<Hash>,
76}
77
78/// El superbloque: el sector 0 del disco. Ancla el grafo entero — dice por
79/// donde continua el log, cual es el objeto raiz y cual el manifiesto.
80#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
81pub struct SuperBloque {
82    /// Firma magica: debe ser [`MAGIA`].
83    pub magia: [u8; 8],
84    /// Version del formato: debe ser [`VERSION_SUPERBLOQUE`].
85    pub version: u32,
86    /// Proximo sector libre del log — donde se anexara el siguiente objeto.
87    pub cursor: u64,
88    /// El objeto raiz del DAG: el punto de entrada que el userspace fija y lee.
89    pub raiz: Option<Hash>,
90    /// El Manifiesto de Genesis: el objeto que dicta que apps nacen del grafo
91    /// al arrancar. Ancla del kernel, gemela de `raiz` (del userspace).
92    pub manifiesto: Option<Hash>,
93}
94
95/// El Manifiesto de Genesis: la lista de aplicaciones que el kernel instancia
96/// al arrancar. Vive como un objeto del grafo; el superbloque guarda su hash.
97#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
98pub struct Manifiesto {
99    /// Version del formato — debe ser [`VERSION_MANIFIESTO`].
100    pub version: u32,
101    /// Las aplicaciones del userspace, en orden de arranque.
102    pub apps: Vec<EntradaApp>,
103}
104
105/// Una entrada del manifiesto: una aplicacion del userspace y todo lo que el
106/// kernel necesita para darle vida — su bytecode, su ventana, su cuota de
107/// memoria y, si lo tuviera, su ultimo estado persistido.
108#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
109pub struct EntradaApp {
110    /// Nombre legible — para los rotulos de la consola y la baliza.
111    pub nombre: String,
112    /// Hash del objeto del grafo que contiene el bytecode WASM de la app.
113    pub bytecode: Hash,
114    /// Sub-region del framebuffer asignada a la app. Campos de ancho fijo
115    /// `u32` A PROPOSITO: esto es un formato EN DISCO. La `RegionPantalla` del
116    /// kernel usa `usize` (ancho dependiente de plataforma) y no serializa.
117    pub region_x: u32,
118    pub region_y: u32,
119    pub region_ancho: u32,
120    pub region_alto: u32,
121    /// Techo de memoria lineal de la app, en bytes. Cada app lleva su cuota.
122    pub techo_memoria: u32,
123    /// Hash del ultimo estado persistido de la app (Fase 7c). `None` hasta que
124    /// la app guarde estado por primera vez.
125    pub estado: Option<Hash>,
126}
127
128// =============================================================================
129//  (De)serializacion — la forma binaria que viaja al disco
130// =============================================================================
131
132impl Objeto {
133    /// Serializa el objeto a su forma binaria `postcard`.
134    pub fn serializar(&self) -> Result<Vec<u8>, &'static str> {
135        postcard::to_allocvec(self).map_err(|_| "objeto :: serializacion fallida")
136    }
137
138    /// Reconstruye un objeto desde su forma binaria. Tolera bytes sobrantes
139    /// tras el objeto —el relleno del registro—: solo consume su prefijo.
140    pub fn deserializar(bytes: &[u8]) -> Result<Objeto, &'static str> {
141        postcard::take_from_bytes::<Objeto>(bytes)
142            .map(|(objeto, _)| objeto)
143            .map_err(|_| "objeto :: deserializacion fallida")
144    }
145}
146
147impl SuperBloque {
148    /// Serializa el superbloque a su forma binaria `postcard`.
149    pub fn serializar(&self) -> Result<Vec<u8>, &'static str> {
150        postcard::to_allocvec(self).map_err(|_| "superbloque :: serializacion fallida")
151    }
152
153    /// Reconstruye el superbloque desde el sector 0. Tolera el relleno a cero
154    /// que completa el sector: solo consume el prefijo serializado.
155    pub fn deserializar(bytes: &[u8]) -> Result<SuperBloque, &'static str> {
156        postcard::take_from_bytes::<SuperBloque>(bytes)
157            .map(|(sb, _)| sb)
158            .map_err(|_| "superbloque :: deserializacion fallida")
159    }
160}
161
162impl Manifiesto {
163    /// Serializa el manifiesto a su forma binaria `postcard` — la carga util
164    /// del objeto del grafo que lo aloja.
165    pub fn serializar(&self) -> Result<Vec<u8>, &'static str> {
166        postcard::to_allocvec(self).map_err(|_| "manifiesto :: serializacion fallida")
167    }
168
169    /// Reconstruye un manifiesto desde la carga util de su objeto. Rechaza un
170    /// formato de version desconocida en lugar de malinterpretarlo.
171    pub fn deserializar(bytes: &[u8]) -> Result<Manifiesto, &'static str> {
172        let (manifiesto, _) = postcard::take_from_bytes::<Manifiesto>(bytes)
173            .map_err(|_| "manifiesto :: deserializacion fallida")?;
174        if manifiesto.version != VERSION_MANIFIESTO {
175            return Err("manifiesto :: version de formato desconocida");
176        }
177        Ok(manifiesto)
178    }
179}
180
181// =============================================================================
182//  El hash y el trazado de un registro en el log
183// =============================================================================
184
185/// La identidad de un objeto: el hash BLAKE3 de su forma serializada. Kernel y
186/// `boot` la calculan por aqui — una sola definicion del hash, jamas dos.
187pub fn hash(bytes: &[u8]) -> Hash {
188    *blake3::hash(bytes).as_bytes()
189}
190
191/// Numero de sectores que ocupa un registro cuyo payload mide `longitud`
192/// bytes. Cada registro es `[longitud: u32 LE][payload postcard][relleno 0]`.
193pub fn sectores_registro(longitud: usize) -> u64 {
194    (4 + longitud).div_ceil(TAM_SECTOR) as u64
195}
196
197/// Compone el registro en disco de un payload: `[longitud u32 LE][payload]
198/// [relleno a cero]`, alineado a un numero entero de sectores. Es el trazado
199/// exacto que el kernel lee al reconstruir su indice — lo escriben tanto
200/// `kernel::almacen` (al anexar un objeto) como `boot` (al sembrar la imagen).
201pub fn componer_registro(payload: &[u8]) -> Vec<u8> {
202    let n = sectores_registro(payload.len()) as usize;
203    let mut registro = vec![0u8; n * TAM_SECTOR];
204    registro[0..4].copy_from_slice(&(payload.len() as u32).to_le_bytes());
205    registro[4..4 + payload.len()].copy_from_slice(payload);
206    registro
207}
208
209/// Lee la cabecera de longitud de un registro (sus 4 primeros bytes). Devuelve
210/// `None` si la longitud es cero —fin del log— o supera [`MAX_OBJETO`]
211/// —corrupcion—. Gemela de [`componer_registro`].
212pub fn longitud_registro(cabecera: &[u8]) -> Option<usize> {
213    if cabecera.len() < 4 {
214        return None;
215    }
216    let longitud =
217        u32::from_le_bytes([cabecera[0], cabecera[1], cabecera[2], cabecera[3]]) as usize;
218    if longitud == 0 || longitud > MAX_OBJETO {
219        None
220    } else {
221        Some(longitud)
222    }
223}
224
225// =============================================================================
226//  Pruebas — el formato debe ser un espejo perfecto: lo escrito se relee igual
227// =============================================================================
228
229#[cfg(test)]
230mod pruebas {
231    use super::*;
232
233    #[test]
234    fn objeto_ida_y_vuelta() {
235        let objeto = Objeto {
236            datos: vec![1, 2, 3, 4, 5],
237            hijos: vec![[7u8; 32], [9u8; 32]],
238        };
239        let bytes = objeto.serializar().unwrap();
240        assert_eq!(Objeto::deserializar(&bytes).unwrap(), objeto);
241    }
242
243    #[test]
244    fn registro_alineado_a_sector() {
245        let payload = vec![0xABu8; 600];
246        let registro = componer_registro(&payload);
247        // 4 + 600 = 604 bytes => dos sectores de 512.
248        assert_eq!(registro.len(), 2 * TAM_SECTOR);
249        assert_eq!(registro.len() % TAM_SECTOR, 0);
250        assert_eq!(longitud_registro(&registro), Some(600));
251        assert_eq!(&registro[4..604], &payload[..]);
252    }
253
254    #[test]
255    fn cabecera_a_cero_es_fin_del_log() {
256        assert_eq!(longitud_registro(&[0, 0, 0, 0]), None);
257        assert_eq!(longitud_registro(&[0xFF, 0xFF, 0xFF, 0xFF]), None);
258        assert_eq!(longitud_registro(&[3, 0, 0, 0]), Some(3));
259    }
260
261    #[test]
262    fn manifiesto_rechaza_version_ajena() {
263        let mut manifiesto = Manifiesto {
264            version: 99,
265            apps: Vec::new(),
266        };
267        let bytes = postcard::to_allocvec(&manifiesto).unwrap();
268        assert!(Manifiesto::deserializar(&bytes).is_err());
269        manifiesto.version = VERSION_MANIFIESTO;
270        assert!(Manifiesto::deserializar(&manifiesto.serializar().unwrap()).is_ok());
271    }
272
273    #[test]
274    fn superbloque_cabe_en_un_sector_y_vuelve_intacto() {
275        let sb = SuperBloque {
276            magia: MAGIA,
277            version: VERSION_SUPERBLOQUE,
278            cursor: 4096,
279            raiz: Some([1u8; 32]),
280            manifiesto: Some([2u8; 32]),
281        };
282        let bytes = sb.serializar().unwrap();
283        assert!(bytes.len() <= TAM_SECTOR);
284        assert_eq!(SuperBloque::deserializar(&bytes).unwrap(), sb);
285    }
286}