Export a change from redb to V3 bytes in memory. This is the core export method used by both file export and network transfer.
(&self, hash: &[u8; 32])
| 376 | /// This is the core export method used by both file export and |
| 377 | /// network transfer. |
| 378 | pub fn export_v3_bytes(&self, hash: &[u8; 32]) -> RedbStoreResult<Vec<u8>> { |
| 379 | let meta = self.load_meta(hash)?; |
| 380 | |
| 381 | // Reconstruct the hash dedup table |
| 382 | let hash_table = if meta.hash_table.is_empty() { |
| 383 | HashDedupTable::empty() |
| 384 | } else { |
| 385 | HashDedupTable::from_hashes(meta.hash_table.clone()).map_err(|e| { |
| 386 | RedbStoreError::Corrupt(format!("hash table reconstruction failed: {}", e)) |
| 387 | })? |
| 388 | }; |
| 389 | |
| 390 | // Build file header |
| 391 | let mut file_header_builder = FileHeader::builder() |
| 392 | .hash_table_entries(hash_table.len() as u32) |
| 393 | .graph_section_count(meta.graph_section_count) |
| 394 | .semantic_section_count(meta.semantic_section_count) |
| 395 | .contents_chunks(meta.content_chunk_count); |
| 396 | |
| 397 | if meta.has_provenance { |
| 398 | file_header_builder = file_header_builder.with_provenance(); |
| 399 | } |
| 400 | if meta.has_unhashed { |
| 401 | file_header_builder = file_header_builder.with_unhashed(); |
| 402 | } |
| 403 | if meta.has_signature { |
| 404 | file_header_builder = file_header_builder.with_signature(); |
| 405 | } |
| 406 | |
| 407 | let file_header = file_header_builder.build(); |
| 408 | |
| 409 | // Write V3 format |
| 410 | let mut output = Vec::new(); |
| 411 | let mut writer = ChangeWriter::new(&mut output, WriterOptions::default()); |
| 412 | |
| 413 | writer.write_file_header(&file_header)?; |
| 414 | writer.write_hash_table(&hash_table)?; |
| 415 | |
| 416 | // Write HEADER section |
| 417 | writer.write_change_header(&meta.header)?; |
| 418 | |
| 419 | // Write DEPS section |
| 420 | writer.write_dependencies(&meta.dependency_indices)?; |
| 421 | |
| 422 | // Write SIGNATURE section (metadata phase — before GRAPH). The |
| 423 | // payload is the stored postcard ChangeSignature; decompress from |
| 424 | // CHANGE_SIGNATURES and write as the SIGNATURE section. |
| 425 | let txn = self.db().begin_read()?; |
| 426 | { |
| 427 | let sig_table = txn.open_table(tables::CHANGE_SIGNATURES)?; |
| 428 | if let Some(value) = sig_table.get(hash)? { |
| 429 | let compressed = value.value(); |
| 430 | let sig_bytes = zstd::decode_all(compressed).map_err(|e| { |
| 431 | RedbStoreError::Corrupt(format!("signature decompression failed: {}", e)) |
| 432 | })?; |
| 433 | let signature: ChangeSignature = postcard::from_bytes(&sig_bytes).map_err(|e| { |
| 434 | RedbStoreError::Corrupt(format!("signature deserialization failed: {}", e)) |
| 435 | })?; |