1use crate::FstSignalValue;
8use crate::types::*;
9use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
10use std::cmp::Ordering;
11#[cfg(test)]
12use std::io::Write;
13use std::io::{Read, Seek, SeekFrom};
14use std::num::NonZeroU32;
15use thiserror::Error;
16
17#[derive(Debug, Error)]
18pub enum ReaderError {
19 #[error(
20 "failed to read a null terminated string because it exceeds the expected size of {0} bytes.\n{1}"
21 )]
22 CStringTooLong(usize, String),
23 #[error("failed to parse an enum table string: {0}\n{1}")]
24 EnumTableString(String, String),
25 #[error("failed to read leb128 integer, more than the expected {0} bits")]
26 Leb128(u32),
27 #[error("failed to parse an integer")]
28 ParseInt(#[from] std::num::ParseIntError),
29 #[error("failed to decompress with lz4")]
30 Lz4Decompress(#[from] lz4_flex::block::DecompressError),
31 #[error("failed to decompress with zlib")]
32 ZLibDecompress(#[from] miniz_oxide::inflate::DecompressError),
33 #[error("failed to parse a gzip header: {0}")]
34 GZipHeader(String),
35 #[error("failed to decompress gzip stream: {0}")]
36 GZipBody(String),
37 #[error("failed to decode string")]
38 Utf8(#[from] std::str::Utf8Error),
39 #[error("failed to decode string")]
40 Utf8String(#[from] std::string::FromUtf8Error),
41 #[error("I/O operation failed")]
42 Io(#[from] std::io::Error),
43 #[error("The FST file is still being compressed into its final GZIP wrapper.")]
44 NotFinishedCompressing(),
45 #[error("Unexpected block type")]
46 BlockType(#[from] TryFromPrimitiveError<BlockType>),
47 #[error("Unexpected file type")]
48 FileType(#[from] TryFromPrimitiveError<FileType>),
49 #[error("Unexpected vhdl variable type")]
50 FstVhdlVarType(#[from] TryFromPrimitiveError<FstVhdlVarType>),
51 #[error("Unexpected vhdl data type")]
52 FstVhdlDataType(#[from] TryFromPrimitiveError<FstVhdlDataType>),
53 #[error("Unexpected variable type")]
54 FstVarType(#[from] TryFromPrimitiveError<FstVarType>),
55 #[error("Unexpected scope type")]
56 FstScopeType(#[from] TryFromPrimitiveError<FstScopeType>),
57 #[error("Unexpected variable direction")]
58 FstVarDirection(#[from] TryFromPrimitiveError<FstVarDirection>),
59 #[error("Unexpected attribute type")]
60 AttributeType(#[from] TryFromPrimitiveError<AttributeType>),
61 #[error("Unexpected misc attribute type")]
62 MiscType(#[from] TryFromPrimitiveError<MiscType>),
63 #[error("Unexpected array type")]
64 ArrayType(#[from] TryFromPrimitiveError<FstArrayType>),
65 #[error("Unexpected sv enum type")]
66 EnumType(#[from] TryFromPrimitiveError<FstEnumType>),
67 #[error("Unexpected pack type")]
68 PackType(#[from] TryFromPrimitiveError<FstPackType>),
69 #[error("The FST file is incomplete: geometry block is missing.")]
70 MissingGeometry(),
71 #[error("The FST file is incomplete: hierarchy block is missing.")]
72 MissingHierarchy(),
73}
74
75pub type ReadResult<T> = Result<T, ReaderError>;
76
77#[derive(Debug, Error)]
78pub enum ReadSignalsError<E = ()> {
79 #[error("Failed to read FST file.")]
80 ReadError(#[from] ReaderError),
81 #[error("The callback failed.")]
82 CallbackError(E),
83}
84
85impl<E> From<std::io::Error> for ReadSignalsError<E> {
86 fn from(value: std::io::Error) -> Self {
87 Self::ReadError(value.into())
88 }
89}
90
91pub type ReadSignalsResult<E> = Result<(), ReadSignalsError<E>>;
92
93#[cfg(test)]
94pub type WriteResult<T> = Result<T, ReaderError>;
95
96#[inline]
99pub(crate) fn read_variant_u32(input: &mut impl Read) -> ReadResult<(u32, u32)> {
100 let mut byte = [0u8; 1];
101 let mut res = 0u32;
102 for ii in 0..5u32 {
104 input.read_exact(&mut byte)?;
105 let value = (byte[0] as u32) & 0x7f;
106 res |= value << (7 * ii);
107 if (byte[0] & 0x80) == 0 {
108 return Ok((res, ii + 1));
109 }
110 }
111 Err(ReaderError::Leb128(32))
112}
113
114#[inline]
115pub(crate) fn read_variant_i64(input: &mut impl Read) -> ReadResult<i64> {
116 let mut byte = [0u8; 1];
117 let mut res = 0u64;
118 for ii in 0..10 {
120 input.read_exact(&mut byte)?;
121 let value = (byte[0] & 0x7f) as u64;
122 let shift_by = 7 * ii;
123 res |= value << shift_by;
124 if (byte[0] & 0x80) == 0 {
125 let sign_bit_set = (byte[0] & 0x40) != 0;
127 if (shift_by + 7) < u64::BITS && sign_bit_set {
128 res |= u64::MAX << (shift_by + 7);
129 }
130 return Ok(res as i64);
131 }
132 }
133 Err(ReaderError::Leb128(64))
134}
135
136#[inline]
137pub(crate) fn read_variant_u64(input: &mut impl Read) -> ReadResult<(u64, usize)> {
138 let mut byte = [0u8; 1];
139 let mut res = 0u64;
140 for ii in 0..10 {
141 input.read_exact(&mut byte)?;
143 let value = (byte[0] as u64) & 0x7f;
144 res |= value << (7 * ii);
145 if (byte[0] & 0x80) == 0 {
146 return Ok((res, ii + 1));
147 }
148 }
149 Err(ReaderError::Leb128(64))
150}
151
152#[cfg(test)]
153#[inline]
154pub(crate) fn write_variant_u64(output: &mut impl Write, mut value: u64) -> WriteResult<usize> {
155 if value <= 0x7f {
157 let byte = [value as u8; 1];
158 output.write_all(&byte)?;
159 return Ok(1);
160 }
161
162 let mut bytes = Vec::with_capacity(10);
163 while value != 0 {
164 let next_value = value >> 7;
165 let mask: u8 = if next_value == 0 { 0 } else { 0x80 };
166 bytes.push((value & 0x7f) as u8 | mask);
167 value = next_value;
168 }
169 assert!(bytes.len() <= 10);
170 output.write_all(&bytes)?;
171 Ok(bytes.len())
172}
173
174#[cfg(test)]
175#[inline]
176pub(crate) fn write_variant_i64(output: &mut impl Write, mut value: i64) -> WriteResult<usize> {
177 if (-64..=63).contains(&value) {
179 let byte = [value as u8 & 0x7f; 1];
180 output.write_all(&byte)?;
181 return Ok(1);
182 }
183
184 let bits = if value >= 0 {
186 64 - value.leading_zeros() + 1
187 } else {
188 64 - value.leading_ones() + 1
189 };
190 let num_bytes = bits.div_ceil(7) as usize;
191
192 let mut bytes = Vec::with_capacity(num_bytes);
193 for ii in 0..num_bytes {
194 let mark = if ii == num_bytes - 1 { 0 } else { 0x80 };
195 bytes.push((value & 0x7f) as u8 | mark);
196 value >>= 7;
197 }
198 output.write_all(&bytes)?;
199 Ok(bytes.len())
200}
201
202#[cfg(test)]
203#[inline]
204pub(crate) fn write_variant_u32(output: &mut impl Write, value: u32) -> WriteResult<usize> {
205 write_variant_u64(output, value as u64)
206}
207
208#[inline]
209pub(crate) fn read_u64(input: &mut impl Read) -> ReadResult<u64> {
210 let mut buf = [0u8; 8];
211 input.read_exact(&mut buf)?;
212 Ok(u64::from_be_bytes(buf))
213}
214
215#[cfg(test)]
216#[inline]
217pub(crate) fn write_u64(output: &mut impl Write, value: u64) -> WriteResult<()> {
218 let buf = value.to_be_bytes();
219 output.write_all(&buf)?;
220 Ok(())
221}
222
223#[inline]
224pub(crate) fn read_u8(input: &mut impl Read) -> ReadResult<u8> {
225 let mut buf = [0u8; 1];
226 input.read_exact(&mut buf)?;
227 Ok(buf[0])
228}
229
230#[cfg(test)]
231fn write_u8(output: &mut impl Write, value: u8) -> WriteResult<()> {
232 let buf = value.to_be_bytes();
233 output.write_all(&buf)?;
234 Ok(())
235}
236
237#[inline]
238pub(crate) fn read_i8(input: &mut impl Read) -> ReadResult<i8> {
239 let mut buf = [0u8; 1];
240 input.read_exact(&mut buf)?;
241 Ok(i8::from_be_bytes(buf))
242}
243
244#[cfg(test)]
245#[inline]
246fn write_i8(output: &mut impl Write, value: i8) -> WriteResult<()> {
247 let buf = value.to_be_bytes();
248 output.write_all(&buf)?;
249 Ok(())
250}
251
252pub(crate) fn read_c_str(input: &mut impl Read, max_len: usize) -> ReadResult<String> {
253 let mut bytes: Vec<u8> = Vec::with_capacity(32);
254 for _ in 0..max_len {
255 let byte = read_u8(input)?;
256 if byte == 0 {
257 return Ok(String::from_utf8(bytes)?);
258 } else {
259 bytes.push(byte);
260 }
261 }
262 Err(ReaderError::CStringTooLong(
263 max_len,
264 String::from_utf8_lossy(&bytes).to_string(),
265 ))
266}
267
268#[cfg(test)]
269fn write_c_str(output: &mut impl Write, value: &str) -> WriteResult<()> {
270 let bytes = value.as_bytes();
271 output.write_all(bytes)?;
272 write_u8(output, 0)?;
273 Ok(())
274}
275
276#[inline] pub(crate) fn read_c_str_fixed_length(input: &mut impl Read, len: usize) -> ReadResult<String> {
278 let mut bytes = read_bytes(input, len)?;
279 let zero_index = bytes.iter().position(|b| *b == 0u8).unwrap_or(len - 1);
280 let str_len = zero_index;
281 bytes.truncate(str_len);
282 Ok(String::from_utf8(bytes)?)
283}
284
285#[cfg(test)]
286#[cfg(test)]
287#[inline]
288fn write_c_str_fixed_length(
289 output: &mut impl Write,
290 value: &str,
291 max_len: usize,
292) -> WriteResult<()> {
293 let bytes = value.as_bytes();
294 if bytes.len() >= max_len {
295 todo!("Return error.")
296 }
297 output.write_all(bytes)?;
298 let zeros = vec![0u8; max_len - bytes.len()];
299 output.write_all(&zeros)?;
300 Ok(())
301}
302
303const RCV_STR: [u8; 8] = *b"xzhuwl-?";
304#[inline]
305pub(crate) fn one_bit_signal_value_to_char(vli: u32) -> u8 {
306 if (vli & 1) == 0 {
307 (((vli >> 1) & 1) as u8) | b'0'
308 } else {
309 RCV_STR[((vli >> 1) & 7) as usize]
310 }
311}
312
313#[inline]
315pub(crate) fn multi_bit_digital_signal_to_chars(bytes: &[u8], len: usize, output: &mut Vec<u8>) {
316 output.resize(len, 0);
317 for (ii, out) in output.iter_mut().enumerate() {
318 let byte_id = ii / 8;
319 let bit_id = 7 - (ii & 7);
320 let bit = (bytes[byte_id] >> bit_id) & 1;
321 *out = bit | b'0';
322 }
323}
324
325pub(crate) fn read_one_bit_signal_time_delta(bytes: &[u8], offset: u32) -> ReadResult<usize> {
326 let mut slice = &bytes[(offset as usize)..];
327 let (vli, _) = read_variant_u32(&mut slice)?;
328 let shift_count = 2u32 << (vli & 1);
329 Ok((vli >> shift_count) as usize)
330}
331
332pub(crate) fn read_multi_bit_signal_time_delta(bytes: &[u8], offset: u32) -> ReadResult<usize> {
333 let mut slice = &bytes[(offset as usize)..];
334 let (vli, _) = read_variant_u32(&mut slice)?;
335 Ok((vli >> 1) as usize)
336}
337
338pub(crate) fn read_zlib_compressed_bytes(
340 input: &mut (impl Read + Seek),
341 uncompressed_length: u64,
342 compressed_length: u64,
343 allow_uncompressed: bool,
344) -> ReadResult<Vec<u8>> {
345 let bytes = if uncompressed_length == compressed_length && allow_uncompressed {
346 read_bytes(input, compressed_length as usize)?
347 } else {
348 let start = input.stream_position()?;
349
350 let first_byte = read_u8(input)?;
352 input.seek(SeekFrom::Start(start))?;
353 let is_zlib = first_byte == 0x78;
355 debug_assert!(is_zlib, "expected a zlib compressed block!");
356
357 let compressed = read_bytes(input, compressed_length as usize)?;
358
359 miniz_oxide::inflate::decompress_to_vec_zlib_with_limit(
360 compressed.as_slice(),
361 uncompressed_length as usize,
362 )?
363 };
364 assert_eq!(bytes.len(), uncompressed_length as usize);
365 Ok(bytes)
366}
367
368#[cfg(test)]
371pub(crate) fn write_compressed_bytes(
372 output: &mut (impl Write + Seek),
373 bytes: &[u8],
374 compression_level: u8,
375 allow_uncompressed: bool,
376) -> WriteResult<usize> {
377 let compressed = miniz_oxide::deflate::compress_to_vec_zlib(bytes, compression_level);
378 if !allow_uncompressed || compressed.len() < bytes.len() {
379 output.write_all(compressed.as_slice())?;
380 Ok(compressed.len())
381 } else {
382 output.write_all(bytes)?;
384 Ok(bytes.len())
385 }
386}
387
388#[inline]
389pub(crate) fn read_bytes(input: &mut impl Read, len: usize) -> ReadResult<Vec<u8>> {
390 let mut buf: Vec<u8> = Vec::with_capacity(len);
391 input.take(len as u64).read_to_end(&mut buf)?;
392 Ok(buf)
393}
394
395pub(crate) fn read_block_tpe(input: &mut impl Read) -> ReadResult<BlockType> {
396 Ok(BlockType::try_from(read_u8(input)?)?)
397}
398
399pub(crate) fn determine_f64_endian(
400 input: &mut impl Read,
401 needle: f64,
402) -> ReadResult<FloatingPointEndian> {
403 let bytes = read_bytes(input, 8)?;
404 let mut byte_reader: &[u8] = &bytes;
405 let le = read_f64(&mut byte_reader, FloatingPointEndian::Little)?;
406 if le == needle {
407 return Ok(FloatingPointEndian::Little);
408 }
409 byte_reader = &bytes;
410 let be = read_f64(&mut byte_reader, FloatingPointEndian::Big)?;
411 if be == needle {
412 Ok(FloatingPointEndian::Big)
413 } else {
414 todo!("should not get here")
415 }
416}
417
418#[inline]
419pub(crate) fn read_f64(input: &mut impl Read, endian: FloatingPointEndian) -> ReadResult<f64> {
420 let mut buf = [0u8; 8];
421 input.read_exact(&mut buf)?;
422 match endian {
423 FloatingPointEndian::Little => Ok(f64::from_le_bytes(buf)),
424 FloatingPointEndian::Big => Ok(f64::from_be_bytes(buf)),
425 }
426}
427
428#[cfg(test)]
429#[inline]
430fn write_f64(output: &mut impl Write, value: f64) -> WriteResult<()> {
431 let buf = value.to_le_bytes();
433 output.write_all(&buf)?;
434 Ok(())
435}
436
437fn read_lz4_compressed_bytes(
438 input: &mut impl Read,
439 uncompressed_length: usize,
440 compressed_length: usize,
441) -> ReadResult<Vec<u8>> {
442 let compressed = read_bytes(input, compressed_length)?;
443 let bytes = lz4_flex::decompress(&compressed, uncompressed_length)?;
444 Ok(bytes)
445}
446
447const HEADER_LENGTH: u64 = 329;
450const HEADER_VERSION_MAX_LEN: usize = 128;
451const HEADER_DATE_MAX_LEN: usize = 119;
452pub(crate) fn read_header(input: &mut impl Read) -> ReadResult<(Header, FloatingPointEndian)> {
453 let section_length = read_u64(input)?;
454 assert_eq!(section_length, HEADER_LENGTH);
455 let start_time = read_u64(input)?;
456 let end_time = read_u64(input)?;
457 let float_endian = determine_f64_endian(input, DOUBLE_ENDIAN_TEST)?;
458 let memory_used_by_writer = read_u64(input)?;
459 let scope_count = read_u64(input)?;
460 let var_count = read_u64(input)?;
461 let max_var_id_code = read_u64(input)?;
462 let vc_section_count = read_u64(input)?;
463 let timescale_exponent = read_i8(input)?;
464 let version = read_c_str_fixed_length(input, HEADER_VERSION_MAX_LEN)?;
465 let date = read_c_str_fixed_length(input, HEADER_DATE_MAX_LEN)?;
467 let file_type = FileType::try_from(read_u8(input)?)?;
468 let time_zero = read_u64(input)?;
469
470 let header = Header {
471 start_time,
472 end_time,
473 memory_used_by_writer,
474 scope_count,
475 var_count,
476 max_var_id_code,
477 vc_section_count,
478 timescale_exponent,
479 version,
480 date,
481 file_type,
482 time_zero,
483 };
484 Ok((header, float_endian))
485}
486
487#[cfg(test)]
488pub(crate) fn write_header(output: &mut impl Write, header: &Header) -> WriteResult<()> {
489 write_u64(output, HEADER_LENGTH)?;
490 write_u64(output, header.start_time)?;
491 write_u64(output, header.end_time)?;
492 write_f64(output, DOUBLE_ENDIAN_TEST)?;
493 write_u64(output, header.memory_used_by_writer)?;
494 write_u64(output, header.scope_count)?;
495 write_u64(output, header.var_count)?;
496 write_u64(output, header.max_var_id_code)?;
497 write_u64(output, header.vc_section_count)?;
498 write_i8(output, header.timescale_exponent)?;
499 write_c_str_fixed_length(output, &header.version, HEADER_VERSION_MAX_LEN)?;
500 write_c_str_fixed_length(output, &header.date, HEADER_DATE_MAX_LEN)?;
501 write_u8(output, header.file_type as u8)?;
502 write_u64(output, header.time_zero)?;
503 Ok(())
504}
505
506pub(crate) fn read_geometry(input: &mut (impl Read + Seek)) -> ReadResult<Vec<SignalInfo>> {
509 let section_length = read_u64(input)?;
510 let uncompressed_length = read_u64(input)?;
511 let max_handle = read_u64(input)?;
512 let compressed_length = section_length - 3 * 8;
513
514 let bytes = read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, true)?;
515
516 let mut signals: Vec<SignalInfo> = Vec::with_capacity(max_handle as usize);
517 let mut byte_reader: &[u8] = &bytes;
518
519 for _ii in 0..max_handle {
520 let (value, _) = read_variant_u32(&mut byte_reader)?;
521 signals.push(SignalInfo::from_file_format(value));
522 }
523 Ok(signals)
524}
525
526#[cfg(test)]
527pub(crate) fn write_geometry(
528 output: &mut (impl Write + Seek),
529 signals: &Vec<SignalInfo>,
530 compression: u8,
531) -> WriteResult<()> {
532 let start = output.stream_position()?;
534 write_u64(output, 0)?; let mut bytes: Vec<u8> = Vec::with_capacity(signals.len() * 2);
538 for signal in signals {
539 write_variant_u64(&mut bytes, signal.to_file_format() as u64)?;
540 }
541 let uncompressed_length = bytes.len() as u64;
542 write_u64(output, uncompressed_length)?;
543 let max_handle = signals.len() as u64;
544 write_u64(output, max_handle)?;
545
546 let compressed_len = write_compressed_bytes(output, &bytes, compression, true)? as u64;
548
549 let section_length = compressed_len + 3 * 8;
551 let end = output.stream_position()?;
552 output.seek(SeekFrom::Start(start))?;
553 write_u64(output, section_length)?;
554 output.seek(SeekFrom::Start(end))?;
555
556 Ok(())
557}
558
559pub(crate) fn read_blackout(input: &mut (impl Read + Seek)) -> ReadResult<Vec<BlackoutData>> {
562 let start = input.stream_position()?;
564 let section_length = read_u64(input)?;
565 let (num_blackouts, _) = read_variant_u32(input)?;
566 let mut blackouts = Vec::with_capacity(num_blackouts as usize);
567 let mut current_blackout = 0u64;
568 for _ in 0..num_blackouts {
569 let activity = read_u8(input)? != 0;
570 let (delta, _) = read_variant_u64(input)?;
571 current_blackout += delta;
572 let bo = BlackoutData {
573 time: current_blackout,
574 contains_activity: activity,
575 };
576 blackouts.push(bo);
577 }
578 let end = input.stream_position()?;
579 assert_eq!(start + section_length, end);
580 Ok(blackouts)
581}
582
583#[cfg(test)]
584pub(crate) fn write_blackout(
585 output: &mut (impl Write + Seek),
586 blackouts: &[BlackoutData],
587) -> WriteResult<()> {
588 let start = output.stream_position()?;
590 write_u64(output, 0)?; let num_blackouts = blackouts.len() as u32;
593 write_variant_u32(output, num_blackouts)?;
594
595 let mut last_blackout = 0u64;
596 for blackout in blackouts {
597 let activity_byte = if blackout.contains_activity { 1 } else { 0 };
598 write_u8(output, activity_byte)?;
599 let delta = blackout.time - last_blackout;
600 last_blackout = blackout.time;
601 write_variant_u64(output, delta)?;
602 }
603
604 let end = output.stream_position()?;
606 output.seek(SeekFrom::Start(start))?;
607 write_u64(output, end - start)?;
608 output.seek(SeekFrom::Start(end))?;
609
610 Ok(())
611}
612
613#[cfg(test)]
615const HIERARCHY_GZIP_COMPRESSION_LEVEL: u8 = 4;
616
617fn read_gzip_compressed_bytes(
619 input: &mut impl Read,
620 uncompressed_len: usize,
621 compressed_len: usize,
622) -> ReadResult<Vec<u8>> {
623 read_gzip_header(input)?;
624 let data = read_bytes(input, compressed_len - 10)?;
626 let uncompressed =
627 miniz_oxide::inflate::decompress_to_vec_with_limit(data.as_slice(), uncompressed_len)?;
628 debug_assert_eq!(uncompressed.len(), uncompressed_len);
629 Ok(uncompressed)
630}
631
632pub(crate) fn read_gzip_header(input: &mut impl Read) -> ReadResult<()> {
633 let header = read_bytes(input, 10)?;
634 let correct_magic = header[0] == 0x1f && header[1] == 0x8b;
635 if !correct_magic {
636 return Err(ReaderError::GZipHeader(format!(
637 "expected magic bytes (0x1f, 0x8b) got {header:x?}"
638 )));
639 }
640 let is_deflate_compressed = header[2] == 8;
641 if !is_deflate_compressed {
642 return Err(ReaderError::GZipHeader(format!(
643 "expected deflate compression (8) got {:x?}",
644 header[2]
645 )));
646 }
647 let flag = header[3];
648 if flag != 0 {
649 return Err(ReaderError::GZipHeader(format!(
650 "TODO currently extra flags are not supported {flag}"
651 )));
652 }
653 Ok(())
654}
655
656pub(crate) fn read_hierarchy_bytes(
657 input: &mut (impl Read + Seek),
658 compression: HierarchyCompression,
659) -> ReadResult<Vec<u8>> {
660 Ok(match compression {
661 HierarchyCompression::Uncompressed => {
662 let mut buf: Vec<u8> = Vec::new();
663 input.read_to_end(&mut buf)?;
664 buf
665 }
666 _ => {
667 let section_length = read_u64(input)? as usize;
668 let uncompressed_length = read_u64(input)? as usize;
669 let compressed_length = section_length - 2 * 8;
670 let bytes = match compression {
671 HierarchyCompression::Uncompressed => unreachable!(),
672 HierarchyCompression::ZLib => {
673 read_gzip_compressed_bytes(input, uncompressed_length, compressed_length)?
674 }
675 HierarchyCompression::Lz4 => {
676 read_lz4_compressed_bytes(input, uncompressed_length, compressed_length)?
677 }
678 HierarchyCompression::Lz4Duo => {
679 let (len, skiplen) = read_variant_u64(input)?;
681 let lvl1_len = len as usize;
682 let lvl1 =
683 read_lz4_compressed_bytes(input, lvl1_len, compressed_length - skiplen)?;
684 let mut lvl1_reader = lvl1.as_slice();
685 read_lz4_compressed_bytes(&mut lvl1_reader, uncompressed_length, lvl1_len)?
686 }
687 };
688 assert_eq!(bytes.len(), uncompressed_length);
689 bytes
690 }
691 })
692}
693
694#[cfg(test)]
695const GZIP_HEADER: [u8; 10] = [
696 0x1f, 0x8b, 8, 0, 0, 0, 0, 0, 0, 255, ];
703
704#[cfg(test)]
706pub(crate) fn write_gzip_compressed_bytes(
707 output: &mut impl Write,
708 bytes: &[u8],
709 compression_level: u8,
710) -> ReadResult<()> {
711 output.write_all(GZIP_HEADER.as_slice())?;
712 let compressed = miniz_oxide::deflate::compress_to_vec(bytes, compression_level);
713 output.write_all(compressed.as_slice())?;
714 Ok(())
715}
716
717#[cfg(test)]
718pub(crate) fn write_hierarchy_bytes(
719 output: &mut (impl Write + Seek),
720 compression: HierarchyCompression,
721 bytes: &[u8],
722) -> WriteResult<()> {
723 match compression {
724 HierarchyCompression::Uncompressed => {
725 output.write_all(bytes)?;
726 }
727 _ => {
728 let start = output.stream_position()?;
730 write_u64(output, 0)?; let uncompressed_length = bytes.len() as u64;
732 write_u64(output, uncompressed_length)?;
733
734 match compression {
735 HierarchyCompression::Uncompressed => unreachable!(),
736 HierarchyCompression::ZLib => {
737 write_gzip_compressed_bytes(output, bytes, HIERARCHY_GZIP_COMPRESSION_LEVEL)?;
738 }
739 HierarchyCompression::Lz4 => {
740 let compressed = lz4_flex::compress(bytes);
741 output.write_all(&compressed)?;
742 }
743 HierarchyCompression::Lz4Duo => {
744 let compressed_lvl1 = lz4_flex::compress(bytes);
745 let lvl1_len = compressed_lvl1.len() as u64;
746 write_variant_u64(output, lvl1_len)?;
747 let compressed_lvl2 = lz4_flex::compress(&compressed_lvl1);
748 output.write_all(&compressed_lvl2)?;
749 }
750 };
751
752 let end = output.stream_position()?;
754 output.seek(SeekFrom::Start(start))?;
755 write_u64(output, end - start)?;
756 output.seek(SeekFrom::Start(end))?;
757 }
758 };
759
760 Ok(())
761}
762
763fn enum_table_from_string(value: String, handle: u64) -> ReadResult<FstHierarchyEntry> {
764 let parts: Vec<&str> = value.split(' ').collect();
765 if parts.len() < 2 {
766 return Err(ReaderError::EnumTableString(
767 "not enough spaces".to_string(),
768 value,
769 ));
770 }
771 let name = parts[0].to_string();
772 let element_count = parts[1].parse::<usize>()?;
773 let expected_part_len = element_count * 2;
774 if parts.len() - 2 != expected_part_len {
775 return Err(ReaderError::EnumTableString(
776 format!(
777 "expected {} parts got {}",
778 expected_part_len,
779 parts.len() - 2
780 ),
781 value,
782 ));
783 }
784 let mut mapping = Vec::with_capacity(element_count);
785 for ii in 0..element_count {
786 let name = parts[2 + ii].to_string();
787 let value = parts[2 + element_count + ii].to_string();
788 mapping.push((value, name));
789 }
790 Ok(FstHierarchyEntry::EnumTable {
792 name,
793 handle,
794 mapping,
795 })
796}
797
798#[cfg(test)]
799fn enum_table_to_string(name: &str, mapping: &[(String, String)]) -> String {
800 let mut out = String::with_capacity(name.len() + mapping.len() * 32 + 32);
801 out.push_str(name);
802 out.push(' ');
803 out.push_str(&format!("{}", mapping.len()));
804 for (_value, name) in mapping {
805 out.push(' ');
806 out.push_str(name);
807 }
808 for (value, _name) in mapping {
809 out.push(' ');
810 out.push_str(value);
811 }
812 out
813}
814
815const FST_SUP_VAR_DATA_TYPE_BITS: u32 = 10;
816const FST_SUP_VAR_DATA_TYPE_MASK: u64 = (1 << FST_SUP_VAR_DATA_TYPE_BITS) - 1;
817
818fn parse_misc_attribute(
819 name: String,
820 tpe: MiscType,
821 arg: u64,
822 arg2: Option<u64>,
823) -> ReadResult<FstHierarchyEntry> {
824 let res = match tpe {
825 MiscType::Comment => FstHierarchyEntry::Comment { string: name },
826 MiscType::EnvVar => todo!("EnvVar Attribute"), MiscType::SupVar => {
828 let var_type = (arg >> FST_SUP_VAR_DATA_TYPE_BITS) as u8;
830 let data_type = (arg & FST_SUP_VAR_DATA_TYPE_MASK) as u8;
831 FstHierarchyEntry::VhdlVarInfo {
832 type_name: name,
833 var_type: FstVhdlVarType::try_from_primitive(var_type)?,
834 data_type: FstVhdlDataType::try_from_primitive(data_type)?,
835 }
836 }
837 MiscType::PathName => FstHierarchyEntry::PathName { name, id: arg },
838 MiscType::SourceStem => FstHierarchyEntry::SourceStem {
839 is_instantiation: false,
840 path_id: arg2.unwrap(),
841 line: arg,
842 },
843 MiscType::SourceInstantiationStem => FstHierarchyEntry::SourceStem {
844 is_instantiation: true,
845 path_id: arg2.unwrap(),
846 line: arg,
847 },
848 MiscType::ValueList => todo!("ValueList Attribute"), MiscType::EnumTable => {
850 if name.is_empty() {
851 FstHierarchyEntry::EnumTableRef { handle: arg }
852 } else {
853 enum_table_from_string(name, arg)?
854 }
855 }
856 MiscType::Unknown => todo!("unknown Attribute"),
857 };
858 Ok(res)
859}
860
861fn read_hierarchy_attribute_arg2_encoded_as_name(input: &mut impl Read) -> ReadResult<u64> {
862 let (value, _) = read_variant_u64(input)?;
863 let end_byte = read_u8(input)?;
864 assert_eq!(end_byte, 0, "expected to be zero terminated!");
865 Ok(value)
866}
867
868const HIERARCHY_TPE_VCD_SCOPE: u8 = 254;
869const HIERARCHY_TPE_VCD_UP_SCOPE: u8 = 255;
870const HIERARCHY_TPE_VCD_ATTRIBUTE_BEGIN: u8 = 252;
871const HIERARCHY_TPE_VCD_ATTRIBUTE_END: u8 = 253;
872
873pub(crate) fn read_hierarchy_entry(
874 input: &mut impl Read,
875 handle_count: &mut u32,
876) -> ReadResult<Option<FstHierarchyEntry>> {
877 let entry_tpe = match read_u8(input) {
878 Ok(tpe) => tpe,
879 Err(_) => return Ok(None),
880 };
881 let entry = match entry_tpe {
882 HIERARCHY_TPE_VCD_SCOPE => {
883 let tpe = FstScopeType::try_from_primitive(read_u8(input)?)?;
885 let name = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
886 let component = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
887 FstHierarchyEntry::Scope {
888 tpe,
889 name,
890 component,
891 }
892 }
893 0..=29 => {
894 let tpe = FstVarType::try_from_primitive(entry_tpe)?;
896 let direction = FstVarDirection::try_from_primitive(read_u8(input)?)?;
897 let name = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
898 let (raw_length, _) = read_variant_u32(input)?;
899 let length = if tpe == FstVarType::Port {
900 (raw_length - 2) / 3
902 } else {
903 raw_length
904 };
905 let (alias, _) = read_variant_u32(input)?;
906 let (is_alias, handle) = if alias == 0 {
907 *handle_count += 1;
908 (false, FstSignalHandle::new(*handle_count))
909 } else {
910 (true, FstSignalHandle::new(alias))
911 };
912 FstHierarchyEntry::Var {
913 tpe,
914 direction,
915 name,
916 length,
917 handle,
918 is_alias,
919 }
920 }
921 HIERARCHY_TPE_VCD_UP_SCOPE => {
922 FstHierarchyEntry::UpScope
924 }
925 HIERARCHY_TPE_VCD_ATTRIBUTE_BEGIN => {
926 let tpe = AttributeType::try_from_primitive(read_u8(input)?)?;
927 match tpe {
928 AttributeType::Misc => {
929 let subtype = MiscType::try_from_primitive(read_u8(input)?)?;
930 let (name, arg2) = match subtype {
931 MiscType::SourceStem | MiscType::SourceInstantiationStem => {
932 let arg2 = read_hierarchy_attribute_arg2_encoded_as_name(input)?;
933 ("".to_string(), Some(arg2))
934 }
935 _ => {
936 let name = read_c_str(input, HIERARCHY_ATTRIBUTE_MAX_SIZE)?;
937 (name, None)
938 }
939 };
940 let (arg, _) = read_variant_u64(input)?;
941 parse_misc_attribute(name, subtype, arg, arg2)?
942 }
943 AttributeType::Array => {
944 let array_type = FstArrayType::try_from_primitive(read_u8(input)?)?;
945 let name = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
946 let (left_right, _) = read_variant_u64(input)?;
947 let mask32 = u32::MAX as u64;
948 let left = ((left_right >> 32) & mask32) as i32;
949 let right = (left_right & mask32) as i32;
950 FstHierarchyEntry::Array {
951 name,
952 array_type,
953 left,
954 right,
955 }
956 }
957 AttributeType::Enum => {
958 let enum_type = FstEnumType::try_from_primitive(read_u8(input)?)?;
959 let name = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
960 let (value, _) = read_variant_u64(input)?;
961
962 FstHierarchyEntry::SVEnum {
963 name,
964 enum_type,
965 value,
966 }
967 }
968 AttributeType::Pack => {
969 let pack_type = FstPackType::try_from_primitive(read_u8(input)?)?;
970 let name = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
971 let (value, _) = read_variant_u64(input)?;
972
973 FstHierarchyEntry::Pack {
974 name,
975 pack_type,
976 value,
977 }
978 }
979 }
980 }
981 HIERARCHY_TPE_VCD_ATTRIBUTE_END => {
982 FstHierarchyEntry::AttributeEnd
984 }
985
986 other => todo!("Deal with hierarchy entry of type: {other}"),
987 };
988
989 Ok(Some(entry))
990}
991
992#[cfg(test)]
993fn write_hierarchy_attribute(
994 output: &mut impl Write,
995 tpe: AttributeType,
996 subtype: u8,
997 name: &str,
998 arg: u64,
999 arg2: Option<u64>,
1000) -> WriteResult<()> {
1001 write_u8(output, HIERARCHY_TPE_VCD_ATTRIBUTE_BEGIN)?;
1002 write_u8(output, tpe as u8)?;
1003 write_u8(output, subtype)?;
1004 let raw_name_bytes = match arg2 {
1005 None => {
1006 assert!(name.len() <= HIERARCHY_ATTRIBUTE_MAX_SIZE);
1007 name.to_string().into_bytes()
1008 }
1009 Some(value) => {
1010 assert!(name.is_empty(), "cannot have a name + an arg2!");
1011 let mut buf = vec![0u8; 10];
1012 let mut buf_writer: &mut [u8] = buf.as_mut();
1013 let len = write_variant_u64(&mut buf_writer, value)?;
1014 buf.truncate(len);
1015 buf
1016 }
1017 };
1018 output.write_all(&raw_name_bytes)?;
1019 write_u8(output, 0)?; write_variant_u64(output, arg)?;
1021 Ok(())
1022}
1023
1024#[cfg(test)]
1025pub(crate) fn write_hierarchy_entry(
1026 output: &mut impl Write,
1027 handle_count: &mut u32,
1028 entry: &FstHierarchyEntry,
1029) -> WriteResult<()> {
1030 match entry {
1031 FstHierarchyEntry::Scope {
1032 tpe,
1033 name,
1034 component,
1035 } => {
1036 write_u8(output, HIERARCHY_TPE_VCD_SCOPE)?;
1037 write_u8(output, *tpe as u8)?;
1038 assert!(name.len() <= HIERARCHY_NAME_MAX_SIZE);
1039 write_c_str(output, name)?;
1040 assert!(component.len() <= HIERARCHY_NAME_MAX_SIZE);
1041 write_c_str(output, component)?;
1042 }
1043 FstHierarchyEntry::UpScope => {
1044 write_u8(output, HIERARCHY_TPE_VCD_UP_SCOPE)?;
1045 }
1046 FstHierarchyEntry::Var {
1047 tpe,
1048 direction,
1049 name,
1050 length,
1051 handle,
1052 is_alias,
1053 } => {
1054 write_u8(output, *tpe as u8)?;
1055 write_u8(output, *direction as u8)?;
1056 assert!(name.len() <= HIERARCHY_NAME_MAX_SIZE);
1057 write_c_str(output, name)?;
1058 let raw_length = if *tpe == FstVarType::Port {
1059 3 * (*length) + 2
1060 } else {
1061 *length
1062 };
1063 write_variant_u32(output, raw_length)?;
1064 if *is_alias {
1065 write_variant_u32(output, handle.get_raw())?;
1066 } else {
1067 assert_eq!(handle.get_index(), *handle_count as usize);
1069 *handle_count += 1;
1070 write_variant_u32(output, 0)?;
1072 }
1073 }
1074 FstHierarchyEntry::PathName { name, id } => write_hierarchy_attribute(
1075 output,
1076 AttributeType::Misc,
1077 MiscType::PathName as u8,
1078 name,
1079 *id,
1080 None,
1081 )?,
1082 FstHierarchyEntry::SourceStem {
1083 is_instantiation,
1084 path_id,
1085 line,
1086 } => {
1087 let subtpe = if *is_instantiation {
1088 MiscType::SourceInstantiationStem
1089 } else {
1090 MiscType::SourceStem
1091 };
1092 write_hierarchy_attribute(
1093 output,
1094 AttributeType::Misc,
1095 subtpe as u8,
1096 "",
1097 *line,
1098 Some(*path_id),
1099 )?
1100 }
1101 FstHierarchyEntry::Comment { string } => write_hierarchy_attribute(
1102 output,
1103 AttributeType::Misc,
1104 MiscType::Comment as u8,
1105 string,
1106 0,
1107 None,
1108 )?,
1109 FstHierarchyEntry::EnumTable {
1110 name,
1111 handle,
1112 mapping,
1113 } => {
1114 let table_str = enum_table_to_string(name, mapping);
1115 write_hierarchy_attribute(
1116 output,
1117 AttributeType::Misc,
1118 MiscType::EnumTable as u8,
1119 &table_str,
1120 *handle,
1121 None,
1122 )?
1123 }
1124 FstHierarchyEntry::EnumTableRef { handle } => write_hierarchy_attribute(
1125 output,
1126 AttributeType::Misc,
1127 MiscType::EnumTable as u8,
1128 "",
1129 *handle,
1130 None,
1131 )?,
1132 FstHierarchyEntry::VhdlVarInfo {
1133 type_name,
1134 var_type,
1135 data_type,
1136 } => {
1137 let arg = ((*var_type as u64) << FST_SUP_VAR_DATA_TYPE_BITS) | (*data_type as u64);
1138 write_hierarchy_attribute(
1139 output,
1140 AttributeType::Misc,
1141 MiscType::SupVar as u8,
1142 type_name,
1143 arg,
1144 None,
1145 )?;
1146 }
1147 FstHierarchyEntry::Array {
1148 name,
1149 array_type,
1150 left,
1151 right,
1152 } => {
1153 let arg = (((*left) as u64) << 32) | ((*right as u64) & 0xFFFFFFFF);
1154 write_hierarchy_attribute(
1155 output,
1156 AttributeType::Array,
1157 *array_type as u8,
1158 name,
1159 arg,
1160 None,
1161 )?;
1162 }
1163 FstHierarchyEntry::SVEnum {
1164 name,
1165 enum_type,
1166 value,
1167 } => {
1168 write_hierarchy_attribute(
1169 output,
1170 AttributeType::Enum,
1171 *enum_type as u8,
1172 name,
1173 *value,
1174 None,
1175 )?;
1176 }
1177 FstHierarchyEntry::Pack {
1178 name,
1179 pack_type,
1180 value,
1181 } => {
1182 write_hierarchy_attribute(
1183 output,
1184 AttributeType::Pack,
1185 *pack_type as u8,
1186 name,
1187 *value,
1188 None,
1189 )?;
1190 }
1191 FstHierarchyEntry::AttributeEnd => {
1192 write_u8(output, HIERARCHY_TPE_VCD_ATTRIBUTE_END)?;
1193 }
1194 }
1195
1196 Ok(())
1197}
1198
1199pub(crate) fn read_packed_signal_value_bytes(
1202 input: &mut (impl Read + Seek),
1203 len: u32,
1204 tpe: ValueChangePackType,
1205) -> ReadResult<Vec<u8>> {
1206 let (value, skiplen) = read_variant_u32(input)?;
1207 if value != 0 {
1208 let uncompressed_length = value as u64;
1209 let uncompressed: Vec<u8> = match tpe {
1210 ValueChangePackType::Lz4 => {
1211 let compressed_length = (len - skiplen) as u64;
1212 read_lz4_compressed_bytes(
1213 input,
1214 uncompressed_length as usize,
1215 compressed_length as usize,
1216 )?
1217 }
1218 ValueChangePackType::FastLz => {
1219 let compressed_length = (len - skiplen) as u64;
1220 crate::fastlz::decompress(
1221 input,
1222 compressed_length as usize,
1223 uncompressed_length as usize,
1224 )?
1225 }
1226 ValueChangePackType::Zlib => {
1227 let compressed_length = len as u64;
1228 read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, false)?
1231 }
1232 };
1233 Ok(uncompressed)
1234 } else {
1235 let dest_length = len - skiplen;
1236 let bytes = read_bytes(input, dest_length as usize)?;
1237 Ok(bytes)
1238 }
1239}
1240
1241pub(crate) fn read_time_table(
1242 input: &mut (impl Read + Seek),
1243 section_start: u64,
1244 section_length: u64,
1245) -> ReadResult<(u64, Vec<u64>)> {
1246 input.seek(SeekFrom::Start(section_start + section_length - 3 * 8))?;
1248 let uncompressed_length = read_u64(input)?;
1249 let compressed_length = read_u64(input)?;
1250 let number_of_items = read_u64(input)?;
1251 assert!(compressed_length <= section_length);
1252
1253 input.seek(SeekFrom::Current(-(3 * 8) - (compressed_length as i64)))?;
1255 let bytes = read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, true)?;
1256 let mut byte_reader: &[u8] = &bytes;
1257 let mut time_table: Vec<u64> = Vec::with_capacity(number_of_items as usize);
1258 let mut time_val: u64 = 0; for _ in 0..number_of_items {
1261 let (value, _) = read_variant_u64(&mut byte_reader)?;
1262 time_val += value;
1263 time_table.push(time_val);
1264 }
1265
1266 let time_section_length = compressed_length + 3 * 8;
1267 Ok((time_section_length, time_table))
1268}
1269
1270#[cfg(test)]
1271pub(crate) fn write_time_table(
1272 output: &mut (impl Write + Seek),
1273 compression: Option<u8>,
1274 table: &[u64],
1275) -> WriteResult<()> {
1276 let num_entries = table.len();
1278 let table = delta_compress_time_table(table)?;
1279 let (uncompressed_len, compressed_len) = match compression {
1281 Some(comp) => {
1282 let compressed = miniz_oxide::deflate::compress_to_vec_zlib(table.as_slice(), comp);
1283 if compressed.len() < table.len() {
1285 output.write_all(compressed.as_slice())?;
1286 (table.len(), compressed.len())
1287 } else {
1288 output.write_all(table.as_slice())?;
1290 (table.len(), table.len())
1291 }
1292 }
1293 None => {
1294 output.write_all(table.as_slice())?;
1295 (table.len(), table.len())
1296 }
1297 };
1298 write_u64(output, uncompressed_len as u64)?;
1299 write_u64(output, compressed_len as u64)?;
1300 write_u64(output, num_entries as u64)?;
1301
1302 Ok(())
1303}
1304
1305#[cfg(test)]
1306#[inline]
1307fn delta_compress_time_table(table: &[u64]) -> WriteResult<Vec<u8>> {
1308 let mut output = vec![];
1309 let mut prev_time = 0u64;
1310 for time in table {
1311 let delta = *time - prev_time;
1312 prev_time = *time;
1313 write_variant_u64(&mut output, delta)?;
1314 }
1315 Ok(output)
1316}
1317#[allow(clippy::too_many_arguments)]
1318#[inline]
1319pub(crate) fn read_frame<E>(
1320 input: &mut (impl Read + Seek),
1321 section_start: u64,
1322 section_length: u64,
1323 signals: &[SignalInfo],
1324 signal_filter: &BitMask,
1325 float_endian: FloatingPointEndian,
1326 start_time: u64,
1327 callback: &mut impl FnMut(u64, FstSignalHandle, FstSignalValue) -> std::result::Result<(), E>,
1328) -> ReadSignalsResult<E> {
1329 input.seek(SeekFrom::Start(section_start + 4 * 8))?;
1331 let (uncompressed_length, _) = read_variant_u64(input)?;
1332 let (compressed_length, _) = read_variant_u64(input)?;
1333 let (max_handle, _) = read_variant_u64(input)?;
1334 assert!(compressed_length <= section_length);
1335 let bytes_vec =
1336 read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, true)?;
1337 let mut bytes = std::io::Cursor::new(bytes_vec);
1338
1339 assert_eq!(signals.len(), max_handle as usize);
1340 for (idx, signal) in signals.iter().enumerate() {
1341 let signal_length = signal.len();
1342 if signal_filter.is_set(idx) {
1343 let handle = FstSignalHandle::from_index(idx);
1344 match signal_length {
1345 0 => {} len => {
1347 if !signal.is_real() {
1348 let value = read_bytes(&mut bytes, len as usize)?;
1349 callback(start_time, handle, FstSignalValue::String(&value))
1350 .map_err(ReadSignalsError::CallbackError)?;
1351 } else {
1352 let value = read_f64(&mut bytes, float_endian)?;
1353 callback(start_time, handle, FstSignalValue::Real(value))
1354 .map_err(ReadSignalsError::CallbackError)?;
1355 }
1356 }
1357 }
1358 } else {
1359 bytes.seek(SeekFrom::Current(signal_length as i64))?;
1361 }
1362 }
1363 Ok(())
1364}
1365
1366#[inline]
1367pub(crate) fn skip_frame(input: &mut (impl Read + Seek), section_start: u64) -> ReadResult<()> {
1368 input.seek(SeekFrom::Start(section_start + 4 * 8))?;
1370 let (_uncompressed_length, _) = read_variant_u64(input)?;
1371 let (compressed_length, _) = read_variant_u64(input)?;
1372 let (_max_handle, _) = read_variant_u64(input)?;
1373 input.seek(SeekFrom::Current(compressed_length as i64))?;
1374 Ok(())
1375}
1376
1377#[derive(Debug)]
1379pub(crate) struct OffsetTable(Vec<SignalDataLoc>);
1380
1381impl From<Vec<SignalDataLoc>> for OffsetTable {
1382 fn from(value: Vec<SignalDataLoc>) -> Self {
1383 Self(value)
1384 }
1385}
1386
1387impl OffsetTable {
1388 pub(crate) fn iter(&self) -> OffsetTableIter<'_> {
1389 OffsetTableIter {
1390 table: self,
1391 signal_idx: 0,
1392 }
1393 }
1394
1395 #[allow(dead_code)]
1396 pub(crate) fn len(&self) -> usize {
1397 self.0.len()
1398 }
1399
1400 fn get_entry(&self, signal_idx: usize) -> Option<OffsetEntry> {
1401 match &self.0[signal_idx] {
1402 SignalDataLoc::None => None,
1403 SignalDataLoc::Alias(alias_idx) => match &self.0[*alias_idx as usize] {
1406 SignalDataLoc::Offset(offset, len) => Some(OffsetEntry {
1407 signal_idx,
1408 offset: offset.get() as u64,
1409 len: len.get(),
1410 }),
1411 _ => unreachable!("aliases should always directly point to an offset"),
1412 },
1413 SignalDataLoc::Offset(offset, len) => Some(OffsetEntry {
1414 signal_idx,
1415 offset: offset.get() as u64,
1416 len: len.get(),
1417 }),
1418 }
1419 }
1420}
1421
1422pub(crate) struct OffsetTableIter<'a> {
1423 table: &'a OffsetTable,
1424 signal_idx: usize,
1425}
1426
1427#[derive(Debug)]
1428pub(crate) struct OffsetEntry {
1429 pub(crate) signal_idx: usize,
1430 pub(crate) offset: u64,
1431 pub(crate) len: u32,
1432}
1433impl Iterator for OffsetTableIter<'_> {
1434 type Item = OffsetEntry;
1435
1436 fn next(&mut self) -> Option<Self::Item> {
1437 while self.signal_idx < self.table.0.len()
1439 && matches!(self.table.0[self.signal_idx], SignalDataLoc::None)
1440 {
1441 self.signal_idx += 1
1442 }
1443
1444 if self.signal_idx >= self.table.0.len() {
1446 return None;
1447 }
1448
1449 let res = self.table.get_entry(self.signal_idx);
1451 debug_assert!(res.is_some());
1452
1453 self.signal_idx += 1;
1455
1456 res
1458 }
1459}
1460
1461fn read_value_change_alias2(
1462 mut chain_bytes: &[u8],
1463 max_handle: u64,
1464 last_table_entry: u32,
1465) -> ReadResult<OffsetTable> {
1466 let mut table = vec![SignalDataLoc::None; max_handle as usize];
1467 let mut idx = 0_usize;
1468 let mut offset: Option<NonZeroU32> = None;
1469 let mut prev_alias = 0u32;
1470 let mut prev_offset_idx = 0usize;
1471 while !chain_bytes.is_empty() {
1472 let kind = chain_bytes[0];
1473 if (kind & 1) == 1 {
1474 let shval = read_variant_i64(&mut chain_bytes)? >> 1;
1475 match shval.cmp(&0) {
1476 Ordering::Greater => {
1477 let new_offset = NonZeroU32::new(
1479 (offset.map(|o| o.get()).unwrap_or_default() as i64 + shval) as u32,
1480 )
1481 .unwrap();
1482 if let Some(prev_offset) = offset {
1484 let len = NonZeroU32::new(new_offset.get() - prev_offset.get()).unwrap();
1485 table[prev_offset_idx] = SignalDataLoc::Offset(prev_offset, len);
1486 }
1487 offset = Some(new_offset);
1488 prev_offset_idx = idx;
1489 idx += 1;
1491 }
1492 Ordering::Less => {
1493 prev_alias = (-shval - 1) as u32;
1495 table[idx] = SignalDataLoc::Alias(prev_alias);
1496 idx += 1;
1497 }
1498 Ordering::Equal => {
1499 table[idx] = SignalDataLoc::Alias(prev_alias);
1501 idx += 1;
1502 }
1503 }
1504 } else {
1505 let (value, _) = read_variant_u32(&mut chain_bytes)?;
1507 let zeros = value >> 1;
1508 idx += zeros as usize;
1509 }
1510 }
1511
1512 if let Some(prev_offset) = offset {
1514 let len = NonZeroU32::new(last_table_entry - prev_offset.get()).unwrap();
1515 table[prev_offset_idx] = SignalDataLoc::Offset(prev_offset, len);
1516 }
1517
1518 debug_assert_eq!(max_handle as usize, idx);
1519
1520 Ok(table.into())
1521}
1522
1523fn read_value_change_alias(
1524 mut chain_bytes: &[u8],
1525 max_handle: u64,
1526 last_table_entry: u32,
1527) -> ReadResult<OffsetTable> {
1528 let mut table = Vec::with_capacity(max_handle as usize);
1529 let mut prev_offset_idx = 0usize;
1530 let mut offset: Option<NonZeroU32> = None;
1531 while !chain_bytes.is_empty() {
1532 let (raw_val, _) = read_variant_u32(&mut chain_bytes)?;
1533 let idx = table.len();
1534 if raw_val == 0 {
1535 let (raw_alias, _) = read_variant_u32(&mut chain_bytes)?;
1536 let alias = ((raw_alias as i64) - 1) as u32;
1537 table.push(SignalDataLoc::Alias(alias));
1538 } else if (raw_val & 1) == 1 {
1539 let new_offset =
1541 NonZeroU32::new(offset.map(|o| o.get()).unwrap_or_default() + (raw_val >> 1))
1542 .unwrap();
1543 if let Some(prev_offset) = offset {
1545 let len = NonZeroU32::new(new_offset.get() - prev_offset.get()).unwrap();
1546 table[prev_offset_idx] = SignalDataLoc::Offset(prev_offset, len);
1547 }
1548 offset = Some(new_offset);
1549 prev_offset_idx = idx;
1550 table.push(SignalDataLoc::None);
1552 } else {
1553 let zeros = raw_val >> 1;
1555 for _ in 0..zeros {
1556 table.push(SignalDataLoc::None);
1557 }
1558 }
1559 }
1560
1561 if let Some(prev_offset) = offset {
1563 let len = NonZeroU32::new(last_table_entry - prev_offset.get()).unwrap();
1564 table[prev_offset_idx] = SignalDataLoc::Offset(prev_offset, len);
1565 }
1566
1567 Ok(table.into())
1568}
1569
1570#[derive(Debug, Copy, Clone)]
1572enum SignalDataLoc {
1573 None,
1575 Alias(u32),
1577 Offset(NonZeroU32, NonZeroU32),
1579}
1580
1581pub(crate) fn read_signal_locs(
1582 input: &mut (impl Read + Seek),
1583 chain_len_offset: u64,
1584 section_kind: DataSectionKind,
1585 max_handle: u64,
1586 start: u64,
1587) -> ReadResult<OffsetTable> {
1588 input.seek(SeekFrom::Start(chain_len_offset))?;
1589 let chain_compressed_length = read_u64(input)?;
1590
1591 let chain_start = chain_len_offset - chain_compressed_length;
1593 input.seek(SeekFrom::Start(chain_start))?;
1594 let chain_bytes = read_bytes(input, chain_compressed_length as usize)?;
1595
1596 let last_table_entry = (chain_start - start) as u32; if section_kind == DataSectionKind::DynamicAlias2 {
1598 read_value_change_alias2(&chain_bytes, max_handle, last_table_entry)
1599 } else {
1600 read_value_change_alias(&chain_bytes, max_handle, last_table_entry)
1601 }
1602}
1603
1604#[cfg(test)]
1605mod tests {
1606 use super::*;
1607 use proptest::prelude::*;
1608
1609 #[test]
1610 fn data_struct_sizes() {
1611 assert_eq!(
1612 std::mem::size_of::<SignalDataLoc>(),
1613 std::mem::size_of::<u64>() + std::mem::size_of::<u32>()
1614 );
1615 }
1616
1617 #[test]
1618 fn test_read_variant_i64() {
1619 let in1 = [0x13];
1621 assert_eq!(read_variant_i64(&mut in1.as_slice()).unwrap(), 19);
1622 let in0 = [0x7b];
1624 assert_eq!(read_variant_i64(&mut in0.as_slice()).unwrap(), -5);
1625 }
1626
1627 #[test]
1628 fn regression_test_read_write_variant_i64() {
1629 do_test_read_write_variant_i64(-36028797018963969);
1630 do_test_read_write_variant_i64(-4611686018427387905);
1631 }
1632
1633 fn do_test_read_write_variant_i64(value: i64) {
1634 let mut buf = std::io::Cursor::new(vec![0u8; 24]);
1635 write_variant_i64(&mut buf, value).unwrap();
1636 buf.seek(SeekFrom::Start(0)).unwrap();
1637 let read_value = read_variant_i64(&mut buf).unwrap();
1638 assert_eq!(read_value, value);
1639 }
1640
1641 proptest! {
1642 #[test]
1643 fn test_read_write_variant_u64(value: u64) {
1644 let mut buf = std::io::Cursor::new(vec![0u8; 24]);
1645 write_variant_u64(&mut buf, value).unwrap();
1646 buf.seek(SeekFrom::Start(0)).unwrap();
1647 let (read_value, _) = read_variant_u64(&mut buf).unwrap();
1648 assert_eq!(read_value, value);
1649 }
1650
1651 #[test]
1652 fn test_read_write_variant_i64(value: i64) {
1653 do_test_read_write_variant_i64(value);
1654 }
1655 }
1656
1657 #[test]
1658 fn test_read_c_str_fixed_length() {
1659 let input = [b'h', b'i', 0u8, b'x'];
1660 assert_eq!(
1661 read_c_str_fixed_length(&mut input.as_slice(), 4).unwrap(),
1662 "hi"
1663 );
1664 let input2 = [b'h', b'i', b'i', 0u8, b'x'];
1665 assert_eq!(
1666 read_c_str_fixed_length(&mut input2.as_slice(), 5).unwrap(),
1667 "hii"
1668 );
1669 }
1670
1671 fn is_valid_c_str(value: &str, max_len: usize) -> bool {
1673 let string_bytes: &[u8] = value.as_bytes();
1674 let len_constraint = string_bytes.len() < max_len;
1675 let non_zero_constraint = !string_bytes.contains(&0u8);
1676 len_constraint && non_zero_constraint
1677 }
1678
1679 fn is_valid_alphanumeric_c_str(value: &str, max_len: usize) -> bool {
1680 let alphanumeric_constraint = value.chars().all(|c| c.is_alphanumeric());
1681 is_valid_c_str(value, max_len) && alphanumeric_constraint
1682 }
1683
1684 proptest! {
1685 #[test]
1686 fn test_write_c_str_fixed_length(string: String, max_len in 1 .. 400usize) {
1687 prop_assume!(is_valid_c_str(&string, max_len));
1688 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1689 write_c_str_fixed_length(&mut buf, &string, max_len).unwrap();
1690 buf.seek(SeekFrom::Start(0)).unwrap();
1691 assert_eq!(
1692 read_c_str_fixed_length(&mut buf, max_len).unwrap(),
1693 string
1694 );
1695 }
1696 }
1697
1698 proptest! {
1699 #[test]
1700 fn test_write_c_str(string: String, max_len in 1 .. 400usize) {
1701 prop_assume!(is_valid_c_str(&string, max_len));
1702 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1703 write_c_str(&mut buf, &string).unwrap();
1704 buf.seek(SeekFrom::Start(0)).unwrap();
1705 assert_eq!(
1706 read_c_str(&mut buf, max_len).unwrap(),
1707 string
1708 );
1709 }
1710 }
1711
1712 proptest! {
1713 #[test]
1714 fn test_read_write_header(header: Header) {
1715 prop_assume!(header.version.len() <= HEADER_VERSION_MAX_LEN);
1717 prop_assume!(header.date.len() <= HEADER_DATE_MAX_LEN );
1718
1719 let mut buf = [0u8; 512];
1720 write_header(&mut buf.as_mut(), &header).unwrap();
1721 let (actual_header, endian) = read_header(&mut buf.as_slice()).unwrap();
1722 assert_eq!(endian, FloatingPointEndian::Little);
1723 assert_eq!(actual_header, header);
1724 }
1725 }
1726
1727 proptest! {
1728 #[test]
1729 fn test_compress_bytes(bytes: Vec<u8>, allow_uncompressed: bool) {
1730 let mut buf = std::io::Cursor::new(vec![0u8; bytes.len() * 2]);
1731 let compressed_len = write_compressed_bytes(&mut buf, &bytes, 3, allow_uncompressed).unwrap();
1732 if allow_uncompressed {
1733 assert!(compressed_len <= bytes.len());
1734 }
1735 buf.seek(SeekFrom::Start(0)).unwrap();
1736 let uncompressed = read_zlib_compressed_bytes(&mut buf, bytes.len() as u64, compressed_len as u64, allow_uncompressed).unwrap();
1737 assert_eq!(uncompressed, bytes);
1738 }
1739 }
1740
1741 proptest! {
1742 #[test]
1743 fn test_read_write_blackout(mut blackouts: Vec<BlackoutData>) {
1744 blackouts.sort_by_key(|a| a.time);
1746
1747 let max_len = blackouts.len() * 5 + 3 * 8;
1749 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1750 write_blackout(&mut buf, &blackouts).unwrap();
1751 buf.seek(SeekFrom::Start(0)).unwrap();
1752 let actual = read_blackout(&mut buf).unwrap();
1753 assert_eq!(actual.len(), blackouts.len());
1754 assert_eq!(actual, blackouts);
1755 }
1756 }
1757
1758 proptest! {
1759 #[test]
1760 fn test_read_write_geometry(signals: Vec<SignalInfo>) {
1761 let max_len = signals.len() * 4 + 3 * 8;
1762 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1763 write_geometry(&mut buf, &signals, 3).unwrap();
1764 buf.seek(SeekFrom::Start(0)).unwrap();
1765 let actual = read_geometry(&mut buf).unwrap();
1766 assert_eq!(actual.len(), signals.len());
1767 assert_eq!(actual, signals);
1768 }
1769 }
1770
1771 fn hierarchy_entry_with_valid_c_strings(entry: &FstHierarchyEntry) -> bool {
1773 match entry {
1774 FstHierarchyEntry::Scope {
1775 name, component, ..
1776 } => {
1777 is_valid_c_str(name, HIERARCHY_NAME_MAX_SIZE)
1778 && is_valid_c_str(component, HIERARCHY_NAME_MAX_SIZE)
1779 }
1780 FstHierarchyEntry::UpScope => true,
1781 FstHierarchyEntry::Var { name, .. } => is_valid_c_str(name, HIERARCHY_NAME_MAX_SIZE),
1782 FstHierarchyEntry::PathName { name, .. } => {
1783 is_valid_c_str(name, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1784 }
1785 FstHierarchyEntry::SourceStem { .. } => true,
1786 FstHierarchyEntry::Comment { string } => {
1787 is_valid_c_str(string, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1788 }
1789 FstHierarchyEntry::EnumTable { name, mapping, .. } => {
1790 is_valid_alphanumeric_c_str(name, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1791 && mapping.iter().all(|(k, v)| {
1792 is_valid_alphanumeric_c_str(k, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1793 && is_valid_alphanumeric_c_str(v, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1794 })
1795 }
1796 FstHierarchyEntry::EnumTableRef { .. } => true,
1797 FstHierarchyEntry::VhdlVarInfo { type_name, .. } => {
1798 is_valid_c_str(type_name, HIERARCHY_NAME_MAX_SIZE)
1799 }
1800 FstHierarchyEntry::Array { name, .. } => is_valid_c_str(name, HIERARCHY_NAME_MAX_SIZE),
1801 FstHierarchyEntry::SVEnum { name, .. } => is_valid_c_str(name, HIERARCHY_NAME_MAX_SIZE),
1802 FstHierarchyEntry::Pack { name, .. } => is_valid_c_str(name, HIERARCHY_NAME_MAX_SIZE),
1803 FstHierarchyEntry::AttributeEnd => true,
1804 }
1805 }
1806
1807 fn hierarchy_entry_with_valid_mapping(entry: &FstHierarchyEntry) -> bool {
1809 match entry {
1810 FstHierarchyEntry::EnumTable { mapping, .. } => mapping
1811 .iter()
1812 .all(|(k, v)| is_valid_mapping_str(k) && is_valid_mapping_str(v)),
1813 _ => true,
1814 }
1815 }
1816 fn is_valid_mapping_str(value: &str) -> bool {
1817 !value.is_empty() && !value.contains(' ')
1818 }
1819
1820 fn hierarchy_entry_with_valid_port_width(entry: &FstHierarchyEntry) -> bool {
1822 if let FstHierarchyEntry::Var {
1823 tpe: FstVarType::Port,
1824 length,
1825 ..
1826 } = entry
1827 {
1828 *length < (u32::MAX / 3) - 2
1829 } else {
1830 true
1831 }
1832 }
1833
1834 fn read_write_hierarchy_entry(entry: FstHierarchyEntry) {
1835 let base_handle_count: u32 = match &entry {
1837 FstHierarchyEntry::Var {
1838 handle, is_alias, ..
1839 } => {
1840 if *is_alias {
1841 0
1842 } else {
1843 handle.get_index() as u32
1844 }
1845 }
1846 _ => 0,
1847 };
1848
1849 let max_len = 1024 * 64;
1850 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1851 let mut handle_count = base_handle_count;
1852 write_hierarchy_entry(&mut buf, &mut handle_count, &entry).unwrap();
1853 if base_handle_count > 0 {
1854 assert_eq!(handle_count, base_handle_count + 1);
1855 }
1856 buf.seek(SeekFrom::Start(0)).unwrap();
1857 handle_count = base_handle_count;
1858 let actual = read_hierarchy_entry(&mut buf, &mut handle_count)
1859 .unwrap()
1860 .unwrap();
1861 assert_eq!(actual, entry);
1862 }
1863
1864 #[test]
1865 fn test_read_write_hierarchy_path_name_entry() {
1866 let entry = FstHierarchyEntry::PathName {
1867 id: 1,
1868 name: "".to_string(),
1869 };
1870 read_write_hierarchy_entry(entry);
1871 }
1872
1873 proptest! {
1874 #[test]
1875 fn test_prop_read_write_hierarchy_entry(entry: FstHierarchyEntry) {
1876 prop_assume!(hierarchy_entry_with_valid_c_strings(&entry));
1877 prop_assume!(hierarchy_entry_with_valid_mapping(&entry));
1878 prop_assume!(hierarchy_entry_with_valid_port_width(&entry));
1879 read_write_hierarchy_entry(entry);
1880 }
1881 }
1882
1883 #[test]
1885 fn test_read_write_hierarchy_entry() {
1886 let entry = FstHierarchyEntry::Comment {
1888 string: "TEST ".repeat(8000_usize.div_ceil(5)),
1889 };
1890 read_write_hierarchy_entry(entry);
1891 }
1892
1893 fn do_test_read_write_hierarchy_bytes(tpe: HierarchyCompression, bytes: Vec<u8>) {
1894 let max_len = match tpe {
1895 HierarchyCompression::Uncompressed => bytes.len(),
1896 _ => std::cmp::max(64, bytes.len() + 3 * 8),
1897 };
1898 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1899 write_hierarchy_bytes(&mut buf, tpe, &bytes).unwrap();
1900 buf.seek(SeekFrom::Start(0)).unwrap();
1901 let actual = read_hierarchy_bytes(&mut buf, tpe).unwrap();
1902 assert_eq!(actual, bytes);
1903 }
1904
1905 #[test]
1906 fn test_read_write_hierarchy_bytes_regression() {
1907 do_test_read_write_hierarchy_bytes(HierarchyCompression::Lz4, vec![]);
1908 do_test_read_write_hierarchy_bytes(HierarchyCompression::ZLib, vec![]);
1909 }
1910
1911 proptest! {
1912 #[test]
1913 fn test_prop_read_write_hierarchy_bytes(tpe: HierarchyCompression, bytes: Vec<u8>) {
1914 do_test_read_write_hierarchy_bytes(tpe, bytes);
1915 }
1916 }
1917
1918 fn read_write_time_table(mut table: Vec<u64>, compressed: bool) {
1919 table.sort();
1921 let max_len = std::cmp::max(64, table.len() * 8 + 3 * 8);
1922 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1923 let comp = if compressed { Some(3) } else { None };
1924 write_time_table(&mut buf, comp, &table).unwrap();
1925 let section_start = 0u64;
1926 let section_length = buf.stream_position().unwrap();
1927 buf.seek(SeekFrom::Start(0)).unwrap();
1928 let (actual_len, actual_table) =
1929 read_time_table(&mut buf, section_start, section_length).unwrap();
1930 assert_eq!(actual_len, section_length);
1931 assert_eq!(actual_table, table);
1932 }
1933
1934 #[test]
1935 fn test_read_write_time_table_uncompressed() {
1936 let table = vec![1, 0];
1937 read_write_time_table(table, false);
1938 }
1939
1940 #[test]
1941 fn test_read_write_time_table_compressed() {
1942 let table = (0..10000).collect();
1943 read_write_time_table(table, true);
1944 }
1945
1946 proptest! {
1947 #[test]
1948 fn test_prop_read_write_time_table(table: Vec<u64>, compressed: bool) {
1949 read_write_time_table(table, compressed);
1950 }
1951 }
1952}