1use crate::types::*;
7use crate::FstSignalValue;
8use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
9use std::cmp::Ordering;
10use std::io::{Read, Seek, SeekFrom, Write};
11use thiserror::Error;
12
13#[derive(Debug, Error)]
14pub enum ReaderError {
15 #[error("failed to read a null terminated string because it exceeds the expected size of {0} bytes.\n{1}")]
16 CStringTooLong(usize, String),
17 #[error("failed to parse an enum table string: {0}\n{1}")]
18 EnumTableString(String, String),
19 #[error("failed to read leb128 integer, more than the expected {0} bits")]
20 Leb128(u32),
21 #[error("failed to parse an integer")]
22 ParseInt(#[from] std::num::ParseIntError),
23 #[error("failed to decompress with lz4")]
24 Lz4Decompress(#[from] lz4_flex::block::DecompressError),
25 #[error("failed to decode string")]
26 Utf8(#[from] std::str::Utf8Error),
27 #[error("failed to decode string")]
28 Utf8String(#[from] std::string::FromUtf8Error),
29 #[error("I/O operation failed")]
30 Io(#[from] std::io::Error),
31 #[error("The FST file is still being compressed into its final GZIP wrapper.")]
32 NotFinishedCompressing(),
33 #[error("Unexpected block type")]
34 BlockType(#[from] TryFromPrimitiveError<BlockType>),
35 #[error("Unexpected file type")]
36 FileType(#[from] TryFromPrimitiveError<FileType>),
37 #[error("Unexpected vhdl variable type")]
38 FstVhdlVarType(#[from] TryFromPrimitiveError<FstVhdlVarType>),
39 #[error("Unexpected vhdl data type")]
40 FstVhdlDataType(#[from] TryFromPrimitiveError<FstVhdlDataType>),
41 #[error("Unexpected variable type")]
42 FstVarType(#[from] TryFromPrimitiveError<FstVarType>),
43 #[error("Unexpected scope type")]
44 FstScopeType(#[from] TryFromPrimitiveError<FstScopeType>),
45 #[error("Unexpected variable direction")]
46 FstVarDirection(#[from] TryFromPrimitiveError<FstVarDirection>),
47 #[error("Unexpected attribute type")]
48 AttributeType(#[from] TryFromPrimitiveError<AttributeType>),
49 #[error("Unexpected misc attribute type")]
50 MiscType(#[from] TryFromPrimitiveError<MiscType>),
51}
52
53pub type ReadResult<T> = Result<T, ReaderError>;
54
55pub type WriteResult<T> = Result<T, ReaderError>;
56
57#[inline]
60pub(crate) fn read_variant_u32(input: &mut impl Read) -> ReadResult<(u32, u32)> {
61 let mut byte = [0u8; 1];
62 let mut res = 0u32;
63 for ii in 0..5u32 {
65 input.read_exact(&mut byte)?;
66 let value = (byte[0] as u32) & 0x7f;
67 res |= value << (7 * ii);
68 if (byte[0] & 0x80) == 0 {
69 return Ok((res, ii + 1));
70 }
71 }
72 Err(ReaderError::Leb128(32))
73}
74
75#[inline]
76pub(crate) fn read_variant_i64(input: &mut impl Read) -> ReadResult<i64> {
77 let mut byte = [0u8; 1];
78 let mut res = 0i64;
79 for ii in 0..10 {
81 input.read_exact(&mut byte)?;
82 let value = (byte[0] & 0x7f) as i64;
83 let shift_by = 7 * ii;
84 res |= value << shift_by;
85 if (byte[0] & 0x80) == 0 {
86 let sign_bit_set = (byte[0] & 0x40) != 0;
88 if shift_by < (8 * 8) && sign_bit_set {
89 res |= -(1i64 << (shift_by + 7))
90 }
91 return Ok(res);
92 }
93 }
94 Err(ReaderError::Leb128(64))
95}
96
97#[inline]
98pub(crate) fn read_variant_u64(input: &mut impl Read) -> ReadResult<(u64, usize)> {
99 let mut byte = [0u8; 1];
100 let mut res = 0u64;
101 for ii in 0..10 {
102 input.read_exact(&mut byte)?;
104 let value = (byte[0] as u64) & 0x7f;
105 res |= value << (7 * ii);
106 if (byte[0] & 0x80) == 0 {
107 return Ok((res, ii + 1));
108 }
109 }
110 Err(ReaderError::Leb128(64))
111}
112
113#[inline]
114pub(crate) fn write_variant_u64(output: &mut impl Write, mut value: u64) -> WriteResult<usize> {
115 if value <= 0x7f {
117 let byte = [value as u8; 1];
118 output.write_all(&byte)?;
119 return Ok(1);
120 }
121
122 let mut bytes = Vec::with_capacity(10);
123 while value != 0 {
124 let next_value = value >> 7;
125 let mask: u8 = if next_value == 0 { 0 } else { 0x80 };
126 bytes.push((value & 0x7f) as u8 | mask);
127 value = next_value;
128 }
129 assert!(bytes.len() <= 10);
130 output.write_all(&bytes)?;
131 Ok(bytes.len())
132}
133
134#[inline]
135pub(crate) fn write_variant_u32(output: &mut impl Write, value: u32) -> WriteResult<usize> {
136 write_variant_u64(output, value as u64)
137}
138
139#[inline]
140pub(crate) fn read_u64(input: &mut impl Read) -> ReadResult<u64> {
141 let mut buf = [0u8; 8];
142 input.read_exact(&mut buf)?;
143 Ok(u64::from_be_bytes(buf))
144}
145
146#[inline]
147pub(crate) fn write_u64(output: &mut impl Write, value: u64) -> WriteResult<()> {
148 let buf = value.to_be_bytes();
149 output.write_all(&buf)?;
150 Ok(())
151}
152
153#[inline]
154pub(crate) fn read_u8(input: &mut impl Read) -> ReadResult<u8> {
155 let mut buf = [0u8; 1];
156 input.read_exact(&mut buf)?;
157 Ok(buf[0])
158}
159
160fn write_u8(output: &mut impl Write, value: u8) -> WriteResult<()> {
161 let buf = value.to_be_bytes();
162 output.write_all(&buf)?;
163 Ok(())
164}
165
166#[inline]
167pub(crate) fn read_i8(input: &mut impl Read) -> ReadResult<i8> {
168 let mut buf = [0u8; 1];
169 input.read_exact(&mut buf)?;
170 Ok(i8::from_be_bytes(buf))
171}
172
173#[inline]
174fn write_i8(output: &mut impl Write, value: i8) -> WriteResult<()> {
175 let buf = value.to_be_bytes();
176 output.write_all(&buf)?;
177 Ok(())
178}
179
180pub(crate) fn read_c_str(input: &mut impl Read, max_len: usize) -> ReadResult<String> {
181 let mut bytes: Vec<u8> = Vec::with_capacity(32);
182 for _ in 0..max_len {
183 let byte = read_u8(input)?;
184 if byte == 0 {
185 return Ok(String::from_utf8(bytes)?);
186 } else {
187 bytes.push(byte);
188 }
189 }
190 Err(ReaderError::CStringTooLong(
191 max_len,
192 String::from_utf8_lossy(&bytes).to_string(),
193 ))
194}
195
196fn write_c_str(output: &mut impl Write, value: &str) -> WriteResult<()> {
197 let bytes = value.as_bytes();
198 output.write_all(bytes)?;
199 write_u8(output, 0)?;
200 Ok(())
201}
202
203#[inline] pub(crate) fn read_c_str_fixed_length(input: &mut impl Read, len: usize) -> ReadResult<String> {
205 let mut bytes = read_bytes(input, len)?;
206 let zero_index = bytes.iter().position(|b| *b == 0u8).unwrap_or(len - 1);
207 let str_len = zero_index;
208 bytes.truncate(str_len);
209 Ok(String::from_utf8(bytes)?)
210}
211
212#[inline]
213fn write_c_str_fixed_length(
214 output: &mut impl Write,
215 value: &str,
216 max_len: usize,
217) -> WriteResult<()> {
218 let bytes = value.as_bytes();
219 if bytes.len() >= max_len {
220 todo!("Return error.")
221 }
222 output.write_all(bytes)?;
223 let zeros = vec![0u8; max_len - bytes.len()];
224 output.write_all(&zeros)?;
225 Ok(())
226}
227
228const RCV_STR: [u8; 8] = [b'x', b'z', b'h', b'u', b'w', b'l', b'-', b'?'];
229#[inline]
230pub(crate) fn one_bit_signal_value_to_char(vli: u32) -> u8 {
231 if (vli & 1) == 0 {
232 (((vli >> 1) & 1) as u8) | b'0'
233 } else {
234 RCV_STR[((vli >> 1) & 7) as usize]
235 }
236}
237
238#[inline]
240pub(crate) fn multi_bit_digital_signal_to_chars(bytes: &[u8], len: usize) -> Vec<u8> {
241 let mut chars = Vec::with_capacity(len);
242 for ii in 0..len {
243 let byte_id = ii / 8;
244 let bit_id = 7 - (ii & 7);
245 let bit = (bytes[byte_id] >> bit_id) & 1;
246 chars.push(bit | b'0');
247 }
248 chars
249}
250
251pub(crate) fn read_one_bit_signal_time_delta(bytes: &[u8], offset: u32) -> ReadResult<usize> {
252 let mut slice = &bytes[(offset as usize)..];
253 let (vli, _) = read_variant_u32(&mut slice)?;
254 let shift_count = 2u32 << (vli & 1);
255 Ok((vli >> shift_count) as usize)
256}
257
258pub(crate) fn read_multi_bit_signal_time_delta(bytes: &[u8], offset: u32) -> ReadResult<usize> {
259 let mut slice = &bytes[(offset as usize)..];
260 let (vli, _) = read_variant_u32(&mut slice)?;
261 Ok((vli >> 1) as usize)
262}
263
264pub(crate) fn read_zlib_compressed_bytes(
266 input: &mut (impl Read + Seek),
267 uncompressed_length: u64,
268 compressed_length: u64,
269 allow_uncompressed: bool,
270) -> ReadResult<Vec<u8>> {
271 let bytes = if uncompressed_length == compressed_length && allow_uncompressed {
272 read_bytes(input, compressed_length as usize)?
273 } else {
274 let start = input.stream_position().unwrap();
275 let mut d = flate2::read::ZlibDecoder::new(input);
276 let mut uncompressed: Vec<u8> = Vec::with_capacity(uncompressed_length as usize);
277 d.read_to_end(&mut uncompressed)?;
278 assert_eq!(d.total_out(), uncompressed_length);
280 d.into_inner()
282 .seek(SeekFrom::Start(start + compressed_length))?;
283 uncompressed
284 };
285 assert_eq!(bytes.len(), uncompressed_length as usize);
286 Ok(bytes)
287}
288
289pub(crate) fn write_compressed_bytes(
292 output: &mut (impl Write + Seek),
293 bytes: &[u8],
294 compression_level: flate2::Compression,
295 allow_uncompressed: bool,
296) -> WriteResult<usize> {
297 let start = output.stream_position()?;
298 let mut d = flate2::write::ZlibEncoder::new(output, compression_level);
299 d.write_all(bytes)?;
300 d.flush()?;
301 assert_eq!(d.total_in() as usize, bytes.len());
302 let compressed_written = d.total_out() as usize;
303 let output2 = d.finish()?;
304 if !allow_uncompressed || compressed_written < bytes.len() {
305 Ok(compressed_written)
306 } else {
307 output2.seek(SeekFrom::Start(start))?;
309 output2.write_all(bytes)?;
310 Ok(bytes.len())
311 }
312}
313
314#[inline]
315pub(crate) fn read_bytes(input: &mut impl Read, len: usize) -> ReadResult<Vec<u8>> {
316 let mut buf: Vec<u8> = Vec::with_capacity(len);
317 input.take(len as u64).read_to_end(&mut buf)?;
318 Ok(buf)
319}
320
321pub(crate) fn read_block_tpe(input: &mut impl Read) -> ReadResult<BlockType> {
322 Ok(BlockType::try_from(read_u8(input)?)?)
323}
324
325pub(crate) fn determine_f64_endian(
326 input: &mut impl Read,
327 needle: f64,
328) -> ReadResult<FloatingPointEndian> {
329 let bytes = read_bytes(input, 8)?;
330 let mut byte_reader: &[u8] = &bytes;
331 let le = read_f64(&mut byte_reader, FloatingPointEndian::Little)?;
332 if le == needle {
333 return Ok(FloatingPointEndian::Little);
334 }
335 byte_reader = &bytes;
336 let be = read_f64(&mut byte_reader, FloatingPointEndian::Big)?;
337 if be == needle {
338 Ok(FloatingPointEndian::Big)
339 } else {
340 todo!("should not get here")
341 }
342}
343
344#[inline]
345pub(crate) fn read_f64(input: &mut impl Read, endian: FloatingPointEndian) -> ReadResult<f64> {
346 let mut buf = [0u8; 8];
347 input.read_exact(&mut buf)?;
348 match endian {
349 FloatingPointEndian::Little => Ok(f64::from_le_bytes(buf)),
350 FloatingPointEndian::Big => Ok(f64::from_be_bytes(buf)),
351 }
352}
353
354#[inline]
355fn write_f64(output: &mut impl Write, value: f64) -> WriteResult<()> {
356 let buf = value.to_le_bytes();
358 output.write_all(&buf)?;
359 Ok(())
360}
361
362fn read_lz4_compressed_bytes(
363 input: &mut impl Read,
364 uncompressed_length: usize,
365 compressed_length: usize,
366) -> ReadResult<Vec<u8>> {
367 let compressed = read_bytes(input, compressed_length)?;
368 let bytes = lz4_flex::decompress(&compressed, uncompressed_length)?;
369 Ok(bytes)
370}
371
372const HEADER_LENGTH: u64 = 329;
375const HEADER_VERSION_MAX_LEN: usize = 128;
376const HEADER_DATE_MAX_LEN: usize = 119;
377pub(crate) fn read_header(input: &mut impl Read) -> ReadResult<(Header, FloatingPointEndian)> {
378 let section_length = read_u64(input)?;
379 assert_eq!(section_length, HEADER_LENGTH);
380 let start_time = read_u64(input)?;
381 let end_time = read_u64(input)?;
382 let float_endian = determine_f64_endian(input, DOUBLE_ENDIAN_TEST)?;
383 let memory_used_by_writer = read_u64(input)?;
384 let scope_count = read_u64(input)?;
385 let var_count = read_u64(input)?;
386 let max_var_id_code = read_u64(input)?;
387 let vc_section_count = read_u64(input)?;
388 let timescale_exponent = read_i8(input)?;
389 let version = read_c_str_fixed_length(input, HEADER_VERSION_MAX_LEN)?;
390 let date = read_c_str_fixed_length(input, HEADER_DATE_MAX_LEN)?;
392 let file_type = FileType::try_from(read_u8(input)?)?;
393 let time_zero = read_u64(input)?;
394
395 let header = Header {
396 start_time,
397 end_time,
398 memory_used_by_writer,
399 scope_count,
400 var_count,
401 max_var_id_code,
402 vc_section_count,
403 timescale_exponent,
404 version,
405 date,
406 file_type,
407 time_zero,
408 };
409 Ok((header, float_endian))
410}
411
412#[allow(dead_code)]
413pub(crate) fn write_header(output: &mut impl Write, header: &Header) -> WriteResult<()> {
414 write_u64(output, HEADER_LENGTH)?;
415 write_u64(output, header.start_time)?;
416 write_u64(output, header.end_time)?;
417 write_f64(output, DOUBLE_ENDIAN_TEST)?;
418 write_u64(output, header.memory_used_by_writer)?;
419 write_u64(output, header.scope_count)?;
420 write_u64(output, header.var_count)?;
421 write_u64(output, header.max_var_id_code)?;
422 write_u64(output, header.vc_section_count)?;
423 write_i8(output, header.timescale_exponent)?;
424 write_c_str_fixed_length(output, &header.version, HEADER_VERSION_MAX_LEN)?;
425 write_c_str_fixed_length(output, &header.date, HEADER_DATE_MAX_LEN)?;
426 write_u8(output, header.file_type as u8)?;
427 write_u64(output, header.time_zero)?;
428 Ok(())
429}
430
431pub(crate) fn read_geometry(input: &mut (impl Read + Seek)) -> ReadResult<Vec<SignalInfo>> {
434 let section_length = read_u64(input)?;
435 let uncompressed_length = read_u64(input)?;
436 let max_handle = read_u64(input)?;
437 let compressed_length = section_length - 3 * 8;
438
439 let bytes = read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, true)?;
440
441 let mut signals: Vec<SignalInfo> = Vec::with_capacity(max_handle as usize);
442 let mut byte_reader: &[u8] = &bytes;
443
444 for _ii in 0..max_handle {
445 let (value, _) = read_variant_u32(&mut byte_reader)?;
446 signals.push(SignalInfo::from_file_format(value));
447 }
448 Ok(signals)
449}
450
451#[allow(dead_code)]
452pub(crate) fn write_geometry(
453 output: &mut (impl Write + Seek),
454 signals: &Vec<SignalInfo>,
455 compression: flate2::Compression,
456) -> WriteResult<()> {
457 let start = output.stream_position()?;
459 write_u64(output, 0)?; let mut bytes: Vec<u8> = Vec::with_capacity(signals.len() * 2);
463 for signal in signals {
464 write_variant_u64(&mut bytes, signal.to_file_format() as u64)?;
465 }
466 let uncompressed_length = bytes.len() as u64;
467 write_u64(output, uncompressed_length)?;
468 let max_handle = signals.len() as u64;
469 write_u64(output, max_handle)?;
470
471 let compressed_len = write_compressed_bytes(output, &bytes, compression, true)? as u64;
473
474 let section_length = compressed_len + 3 * 8;
476 let end = output.stream_position()?;
477 output.seek(SeekFrom::Start(start))?;
478 write_u64(output, section_length)?;
479 output.seek(SeekFrom::Start(end))?;
480
481 Ok(())
482}
483
484pub(crate) fn read_blackout(input: &mut (impl Read + Seek)) -> ReadResult<Vec<BlackoutData>> {
487 let start = input.stream_position()?;
489 let section_length = read_u64(input)?;
490 let (num_blackouts, _) = read_variant_u32(input)?;
491 let mut blackouts = Vec::with_capacity(num_blackouts as usize);
492 let mut current_blackout = 0u64;
493 for _ in 0..num_blackouts {
494 let activity = read_u8(input)? != 0;
495 let (delta, _) = read_variant_u64(input)?;
496 current_blackout += delta;
497 let bo = BlackoutData {
498 time: current_blackout,
499 contains_activity: activity,
500 };
501 blackouts.push(bo);
502 }
503 let end = input.stream_position()?;
504 assert_eq!(start + section_length, end);
505 Ok(blackouts)
506}
507
508#[allow(dead_code)]
509pub(crate) fn write_blackout(
510 output: &mut (impl Write + Seek),
511 blackouts: &[BlackoutData],
512) -> WriteResult<()> {
513 let start = output.stream_position()?;
515 write_u64(output, 0)?; let num_blackouts = blackouts.len() as u32;
518 write_variant_u32(output, num_blackouts)?;
519
520 let mut last_blackout = 0u64;
521 for blackout in blackouts {
522 let activity_byte = if blackout.contains_activity { 1 } else { 0 };
523 write_u8(output, activity_byte)?;
524 let delta = blackout.time - last_blackout;
525 last_blackout = blackout.time;
526 write_variant_u64(output, delta)?;
527 }
528
529 let end = output.stream_position()?;
531 output.seek(SeekFrom::Start(start))?;
532 write_u64(output, end - start)?;
533 output.seek(SeekFrom::Start(end))?;
534
535 Ok(())
536}
537
538const HIERARCHY_GZIP_COMPRESSION_LEVEL: flate2::Compression = flate2::Compression::best();
541
542pub(crate) fn read_hierarchy_bytes(
543 input: &mut (impl Read + Seek),
544 compression: HierarchyCompression,
545) -> ReadResult<Vec<u8>> {
546 let section_length = read_u64(input)? as usize;
547 let uncompressed_length = read_u64(input)? as usize;
548 let compressed_length = section_length - 2 * 8;
549
550 let bytes = match compression {
551 HierarchyCompression::ZLib => {
552 let start = input.stream_position().unwrap();
553 let mut d = flate2::read::GzDecoder::new(input);
554 let mut uncompressed: Vec<u8> = Vec::with_capacity(uncompressed_length);
555 d.read_to_end(&mut uncompressed)?;
556 d.into_inner()
558 .seek(SeekFrom::Start(start + compressed_length as u64))?;
559 uncompressed
560 }
561 HierarchyCompression::Lz4 => {
562 read_lz4_compressed_bytes(input, uncompressed_length, compressed_length)?
563 }
564 HierarchyCompression::Lz4Duo => {
565 let (len, skiplen) = read_variant_u64(input)?;
567 let lvl1_len = len as usize;
568 let lvl1 = read_lz4_compressed_bytes(input, lvl1_len, compressed_length - skiplen)?;
569 let mut lvl1_reader = lvl1.as_slice();
570 read_lz4_compressed_bytes(&mut lvl1_reader, uncompressed_length, lvl1_len)?
571 }
572 };
573 assert_eq!(bytes.len(), uncompressed_length);
574 Ok(bytes)
575}
576
577#[allow(dead_code)]
578pub(crate) fn write_hierarchy_bytes(
579 output: &mut (impl Write + Seek),
580 compression: HierarchyCompression,
581 bytes: &[u8],
582) -> WriteResult<()> {
583 let start = output.stream_position()?;
585 write_u64(output, 0)?; let uncompressed_length = bytes.len() as u64;
587 write_u64(output, uncompressed_length)?;
588
589 let out2 = match compression {
590 HierarchyCompression::ZLib => {
591 let mut e = flate2::write::GzEncoder::new(output, HIERARCHY_GZIP_COMPRESSION_LEVEL);
592 e.write_all(bytes)?;
593 e.finish()?
594 }
595 HierarchyCompression::Lz4 => {
596 let compressed = lz4_flex::compress(bytes);
597 output.write_all(&compressed)?;
598 output
599 }
600 HierarchyCompression::Lz4Duo => {
601 let compressed_lvl1 = lz4_flex::compress(bytes);
602 let lvl1_len = compressed_lvl1.len() as u64;
603 write_variant_u64(output, lvl1_len)?;
604 let compressed_lvl2 = lz4_flex::compress(&compressed_lvl1);
605 output.write_all(&compressed_lvl2)?;
606 output
607 }
608 };
609
610 let end = out2.stream_position()?;
612 out2.seek(SeekFrom::Start(start))?;
613 write_u64(out2, end - start)?;
614 out2.seek(SeekFrom::Start(end))?;
615 Ok(())
616}
617
618fn enum_table_from_string(value: String, handle: u64) -> ReadResult<FstHierarchyEntry> {
619 let parts: Vec<&str> = value.split(' ').collect();
620 if parts.len() < 2 {
621 return Err(ReaderError::EnumTableString(
622 "not enough spaces".to_string(),
623 value,
624 ));
625 }
626 let name = parts[0].to_string();
627 let element_count = parts[1].parse::<usize>()?;
628 let expected_part_len = element_count * 2;
629 if parts.len() - 2 != expected_part_len {
630 return Err(ReaderError::EnumTableString(
631 format!(
632 "expected {} parts got {}",
633 expected_part_len,
634 parts.len() - 2
635 ),
636 value,
637 ));
638 }
639 let mut mapping = Vec::with_capacity(element_count);
640 for ii in 0..element_count {
641 let name = parts[2 + ii].to_string();
642 let value = parts[2 + element_count + ii].to_string();
643 mapping.push((value, name));
644 }
645 Ok(FstHierarchyEntry::EnumTable {
647 name,
648 handle,
649 mapping,
650 })
651}
652
653fn enum_table_to_string(name: &str, mapping: &[(String, String)]) -> String {
654 let mut out = String::with_capacity(name.len() + mapping.len() * 32 + 32);
655 out.push_str(name);
656 out.push(' ');
657 out.push_str(&format!("{}", mapping.len()));
658 for (_value, name) in mapping {
659 out.push(' ');
660 out.push_str(name);
661 }
662 for (value, _name) in mapping {
663 out.push(' ');
664 out.push_str(value);
665 }
666 out
667}
668
669const FST_SUP_VAR_DATA_TYPE_BITS: u32 = 10;
670const FST_SUP_VAR_DATA_TYPE_MASK: u64 = (1 << FST_SUP_VAR_DATA_TYPE_BITS) - 1;
671
672fn parse_misc_attribute(
673 name: String,
674 tpe: MiscType,
675 arg: u64,
676 arg2: Option<u64>,
677) -> ReadResult<FstHierarchyEntry> {
678 let res = match tpe {
679 MiscType::Comment => FstHierarchyEntry::Comment { string: name },
680 MiscType::EnvVar => todo!("EnvVar Attribute"), MiscType::SupVar => {
682 let var_type = (arg >> FST_SUP_VAR_DATA_TYPE_BITS) as u8;
684 let data_type = (arg & FST_SUP_VAR_DATA_TYPE_MASK) as u8;
685 FstHierarchyEntry::VhdlVarInfo {
686 type_name: name,
687 var_type: FstVhdlVarType::try_from_primitive(var_type)?,
688 data_type: FstVhdlDataType::try_from_primitive(data_type)?,
689 }
690 }
691 MiscType::PathName => FstHierarchyEntry::PathName { name, id: arg },
692 MiscType::SourceStem => FstHierarchyEntry::SourceStem {
693 is_instantiation: false,
694 path_id: arg2.unwrap(),
695 line: arg,
696 },
697 MiscType::SourceInstantiationStem => FstHierarchyEntry::SourceStem {
698 is_instantiation: true,
699 path_id: arg2.unwrap(),
700 line: arg,
701 },
702 MiscType::ValueList => todo!("ValueList Attribute"), MiscType::EnumTable => {
704 if name.is_empty() {
705 FstHierarchyEntry::EnumTableRef { handle: arg }
706 } else {
707 enum_table_from_string(name, arg)?
708 }
709 }
710 MiscType::Unknown => todo!("unknown Attribute"),
711 };
712 Ok(res)
713}
714
715fn read_hierarchy_attribute_arg2_encoded_as_name(input: &mut impl Read) -> ReadResult<u64> {
716 let (value, _) = read_variant_u64(input)?;
717 let end_byte = read_u8(input)?;
718 assert_eq!(end_byte, 0, "expected to be zero terminated!");
719 Ok(value)
720}
721
722const HIERARCHY_TPE_VCD_SCOPE: u8 = 254;
723const HIERARCHY_TPE_VCD_UP_SCOPE: u8 = 255;
724const HIERARCHY_TPE_VCD_ATTRIBUTE_BEGIN: u8 = 252;
725const HIERARCHY_TPE_VCD_ATTRIBUTE_END: u8 = 253;
726
727pub(crate) fn read_hierarchy_entry(
728 input: &mut impl Read,
729 handle_count: &mut u32,
730) -> ReadResult<Option<FstHierarchyEntry>> {
731 let entry_tpe = match read_u8(input) {
732 Ok(tpe) => tpe,
733 Err(_) => return Ok(None),
734 };
735 let entry = match entry_tpe {
736 HIERARCHY_TPE_VCD_SCOPE => {
737 let tpe = FstScopeType::try_from_primitive(read_u8(input)?)?;
739 let name = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
740 let component = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
741 FstHierarchyEntry::Scope {
742 tpe,
743 name,
744 component,
745 }
746 }
747 0..=29 => {
748 let tpe = FstVarType::try_from_primitive(entry_tpe)?;
750 let direction = FstVarDirection::try_from_primitive(read_u8(input)?)?;
751 let name = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
752 let (raw_length, _) = read_variant_u32(input)?;
753 let length = if tpe == FstVarType::Port {
754 (raw_length - 2) / 3
756 } else {
757 raw_length
758 };
759 let (alias, _) = read_variant_u32(input)?;
760 let (is_alias, handle) = if alias == 0 {
761 *handle_count += 1;
762 (false, FstSignalHandle::new(*handle_count))
763 } else {
764 (true, FstSignalHandle::new(alias))
765 };
766 FstHierarchyEntry::Var {
767 tpe,
768 direction,
769 name,
770 length,
771 handle,
772 is_alias,
773 }
774 }
775 HIERARCHY_TPE_VCD_UP_SCOPE => {
776 FstHierarchyEntry::UpScope
778 }
779 HIERARCHY_TPE_VCD_ATTRIBUTE_BEGIN => {
780 let tpe = AttributeType::try_from_primitive(read_u8(input)?)?;
781 let subtype = MiscType::try_from_primitive(read_u8(input)?)?;
782 match tpe {
783 AttributeType::Misc => {
784 let (name, arg2) = match subtype {
785 MiscType::SourceStem | MiscType::SourceInstantiationStem => {
786 let arg2 = read_hierarchy_attribute_arg2_encoded_as_name(input)?;
787 ("".to_string(), Some(arg2))
788 }
789 _ => {
790 let name = read_c_str(input, HIERARCHY_ATTRIBUTE_MAX_SIZE)?;
791 (name, None)
792 }
793 };
794 let (arg, _) = read_variant_u64(input)?;
795 parse_misc_attribute(name, subtype, arg, arg2)?
796 }
797 AttributeType::Array => todo!("ARRAY attributes"),
798 AttributeType::Enum => todo!("ENUM attributes"),
799 AttributeType::Pack => todo!("PACK attributes"),
800 }
801 }
802 HIERARCHY_TPE_VCD_ATTRIBUTE_END => {
803 FstHierarchyEntry::AttributeEnd
805 }
806
807 other => todo!("Deal with hierarchy entry of type: {other}"),
808 };
809
810 Ok(Some(entry))
811}
812
813fn write_hierarchy_attribute(
814 output: &mut impl Write,
815 tpe: AttributeType,
816 subtype: MiscType,
817 name: &str,
818 arg: u64,
819 arg2: Option<u64>,
820) -> WriteResult<()> {
821 write_u8(output, HIERARCHY_TPE_VCD_ATTRIBUTE_BEGIN)?;
822 write_u8(output, tpe as u8)?;
823 write_u8(output, subtype as u8)?;
824 let raw_name_bytes = match arg2 {
825 None => {
826 assert!(name.len() <= HIERARCHY_ATTRIBUTE_MAX_SIZE);
827 name.to_string().into_bytes()
828 }
829 Some(value) => {
830 assert!(name.is_empty(), "cannot have a name + an arg2!");
831 let mut buf = vec![0u8; 10];
832 let mut buf_writer: &mut [u8] = buf.as_mut();
833 let len = write_variant_u64(&mut buf_writer, value)?;
834 buf.truncate(len);
835 buf
836 }
837 };
838 output.write_all(&raw_name_bytes)?;
839 write_u8(output, 0)?; write_variant_u64(output, arg)?;
841 Ok(())
842}
843
844#[allow(dead_code)]
845pub(crate) fn write_hierarchy_entry(
846 output: &mut impl Write,
847 handle_count: &mut u32,
848 entry: &FstHierarchyEntry,
849) -> WriteResult<()> {
850 match entry {
851 FstHierarchyEntry::Scope {
852 tpe,
853 name,
854 component,
855 } => {
856 write_u8(output, HIERARCHY_TPE_VCD_SCOPE)?;
857 write_u8(output, *tpe as u8)?;
858 assert!(name.len() <= HIERARCHY_NAME_MAX_SIZE);
859 write_c_str(output, name)?;
860 assert!(component.len() <= HIERARCHY_NAME_MAX_SIZE);
861 write_c_str(output, component)?;
862 }
863 FstHierarchyEntry::UpScope => {
864 write_u8(output, HIERARCHY_TPE_VCD_UP_SCOPE)?;
865 }
866 FstHierarchyEntry::Var {
867 tpe,
868 direction,
869 name,
870 length,
871 handle,
872 is_alias,
873 } => {
874 write_u8(output, *tpe as u8)?;
875 write_u8(output, *direction as u8)?;
876 assert!(name.len() <= HIERARCHY_NAME_MAX_SIZE);
877 write_c_str(output, name)?;
878 let raw_length = if *tpe == FstVarType::Port {
879 3 * (*length) + 2
880 } else {
881 *length
882 };
883 write_variant_u32(output, raw_length)?;
884 if *is_alias {
885 write_variant_u32(output, handle.get_raw())?;
886 } else {
887 assert_eq!(handle.get_index(), *handle_count as usize);
889 *handle_count += 1;
890 write_variant_u32(output, 0)?;
892 }
893 }
894 FstHierarchyEntry::PathName { name, id } => write_hierarchy_attribute(
895 output,
896 AttributeType::Misc,
897 MiscType::PathName,
898 name,
899 *id,
900 None,
901 )?,
902 FstHierarchyEntry::SourceStem {
903 is_instantiation,
904 path_id,
905 line,
906 } => {
907 let subtpe = if *is_instantiation {
908 MiscType::SourceInstantiationStem
909 } else {
910 MiscType::SourceStem
911 };
912 write_hierarchy_attribute(
913 output,
914 AttributeType::Misc,
915 subtpe,
916 "",
917 *line,
918 Some(*path_id),
919 )?
920 }
921 FstHierarchyEntry::Comment { string } => write_hierarchy_attribute(
922 output,
923 AttributeType::Misc,
924 MiscType::Comment,
925 string,
926 0,
927 None,
928 )?,
929 FstHierarchyEntry::EnumTable {
930 name,
931 handle,
932 mapping,
933 } => {
934 let table_str = enum_table_to_string(name, mapping);
935 write_hierarchy_attribute(
936 output,
937 AttributeType::Misc,
938 MiscType::EnumTable,
939 &table_str,
940 *handle,
941 None,
942 )?
943 }
944 FstHierarchyEntry::EnumTableRef { handle } => write_hierarchy_attribute(
945 output,
946 AttributeType::Misc,
947 MiscType::EnumTable,
948 "",
949 *handle,
950 None,
951 )?,
952 FstHierarchyEntry::VhdlVarInfo {
953 type_name,
954 var_type,
955 data_type,
956 } => {
957 let arg = ((*var_type as u64) << FST_SUP_VAR_DATA_TYPE_BITS) | (*data_type as u64);
958 write_hierarchy_attribute(
959 output,
960 AttributeType::Misc,
961 MiscType::SupVar,
962 type_name,
963 arg,
964 None,
965 )?;
966 }
967 FstHierarchyEntry::AttributeEnd => {
968 write_u8(output, HIERARCHY_TPE_VCD_ATTRIBUTE_END)?;
969 }
970 }
971
972 Ok(())
973}
974
975pub(crate) fn read_packed_signal_value_bytes(
978 input: &mut (impl Read + Seek),
979 len: u32,
980 tpe: ValueChangePackType,
981) -> ReadResult<Vec<u8>> {
982 let (value, skiplen) = read_variant_u32(input)?;
983 if value != 0 {
984 let uncompressed_length = value as u64;
985 let uncompressed: Vec<u8> = match tpe {
986 ValueChangePackType::Lz4 => {
987 let compressed_length = (len - skiplen) as u64;
988 read_lz4_compressed_bytes(
989 input,
990 uncompressed_length as usize,
991 compressed_length as usize,
992 )?
993 }
994 ValueChangePackType::FastLz => {
995 let compressed_length = (len - skiplen) as u64;
996 crate::fastlz::decompress(
997 input,
998 compressed_length as usize,
999 uncompressed_length as usize,
1000 )?
1001 }
1002 ValueChangePackType::Zlib => {
1003 let compressed_length = len as u64;
1004 read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, false)?
1007 }
1008 };
1009 Ok(uncompressed)
1010 } else {
1011 let dest_length = len - skiplen;
1012 let bytes = read_bytes(input, dest_length as usize)?;
1013 Ok(bytes)
1014 }
1015}
1016
1017pub(crate) fn read_time_chain(
1018 input: &mut (impl Read + Seek),
1019 section_start: u64,
1020 section_length: u64,
1021) -> ReadResult<(u64, Vec<u64>)> {
1022 input.seek(SeekFrom::Start(section_start + section_length - 3 * 8))?;
1024 let uncompressed_length = read_u64(input)?;
1025 let compressed_length = read_u64(input)?;
1026 let number_of_items = read_u64(input)?;
1027 assert!(compressed_length <= section_length);
1028
1029 input.seek(SeekFrom::Current(-(3 * 8) - (compressed_length as i64)))?;
1031 let bytes = read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, true)?;
1032 let mut byte_reader: &[u8] = &bytes;
1033 let mut time_table: Vec<u64> = Vec::with_capacity(number_of_items as usize);
1034 let mut time_val: u64 = 0; for _ in 0..number_of_items {
1037 let (value, _) = read_variant_u64(&mut byte_reader)?;
1038 time_val += value;
1039 time_table.push(time_val);
1040 }
1041
1042 let time_section_length = compressed_length + 3 * 8;
1043 Ok((time_section_length, time_table))
1044}
1045
1046#[allow(dead_code)]
1047pub(crate) fn write_time_chain(
1048 output: &mut (impl Write + Seek),
1049 compression: Option<flate2::Compression>,
1050 table: &[u64],
1051) -> WriteResult<()> {
1052 let (uncompressed_len, compressed_len, out2) = match compression {
1054 Some(comp) => {
1055 let start = output.stream_position()?;
1056 let mut e = flate2::write::ZlibEncoder::new(output, comp);
1057 let uncompressed_len = write_time_chain_data(&mut e, table)? as u64;
1058 let out2 = e.finish()?;
1059 let end = out2.stream_position()?;
1060 let compressed_len = end - start;
1061
1062 if compressed_len >= uncompressed_len {
1064 out2.seek(SeekFrom::Start(start))?; let len = write_time_chain_data(out2, table)? as u64;
1066 (len, len, out2)
1067 } else {
1068 (uncompressed_len, compressed_len, out2)
1069 }
1070 }
1071 None => {
1072 let len = write_time_chain_data(output, table)? as u64;
1073 (len, len, output)
1074 }
1075 };
1076 write_u64(out2, uncompressed_len)?;
1077 write_u64(out2, compressed_len)?;
1078 write_u64(out2, table.len() as u64)?;
1079
1080 Ok(())
1081}
1082
1083#[inline]
1084fn write_time_chain_data(output: &mut impl Write, table: &[u64]) -> WriteResult<usize> {
1085 let mut total_len = 0;
1086 let mut prev_time = 0u64;
1087 for time in table {
1088 let delta = *time - prev_time;
1089 prev_time = *time;
1090 total_len += write_variant_u64(output, delta)?;
1091 }
1092 Ok(total_len)
1093}
1094#[allow(clippy::too_many_arguments)]
1095#[inline]
1096pub(crate) fn read_frame(
1097 input: &mut (impl Read + Seek),
1098 section_start: u64,
1099 section_length: u64,
1100 signals: &[SignalInfo],
1101 signal_filter: &BitMask,
1102 float_endian: FloatingPointEndian,
1103 start_time: u64,
1104 callback: &mut impl FnMut(u64, FstSignalHandle, FstSignalValue),
1105) -> ReadResult<()> {
1106 input.seek(SeekFrom::Start(section_start + 4 * 8))?;
1108 let (uncompressed_length, _) = read_variant_u64(input)?;
1109 let (compressed_length, _) = read_variant_u64(input)?;
1110 let (max_handle, _) = read_variant_u64(input)?;
1111 assert!(compressed_length <= section_length);
1112 let bytes_vec =
1113 read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, true)?;
1114 let mut bytes = std::io::Cursor::new(bytes_vec);
1115
1116 assert_eq!(signals.len(), max_handle as usize);
1117 for (idx, signal) in signals.iter().enumerate() {
1118 let signal_length = signal.len();
1119 if signal_filter.is_set(idx) {
1120 let handle = FstSignalHandle::from_index(idx);
1121 match signal_length {
1122 0 => {} len => {
1124 if !signal.is_real() {
1125 let value = read_bytes(&mut bytes, len as usize)?;
1126 callback(start_time, handle, FstSignalValue::String(&value));
1127 } else {
1128 let value = read_f64(&mut bytes, float_endian)?;
1129 callback(start_time, handle, FstSignalValue::Real(value));
1130 }
1131 }
1132 }
1133 } else {
1134 bytes.seek(SeekFrom::Current(signal_length as i64))?;
1136 }
1137 }
1138 Ok(())
1139}
1140
1141#[inline]
1142pub(crate) fn skip_frame(input: &mut (impl Read + Seek), section_start: u64) -> ReadResult<()> {
1143 input.seek(SeekFrom::Start(section_start + 4 * 8))?;
1145 let (_uncompressed_length, _) = read_variant_u64(input)?;
1146 let (compressed_length, _) = read_variant_u64(input)?;
1147 let (_max_handle, _) = read_variant_u64(input)?;
1148 input.seek(SeekFrom::Current(compressed_length as i64))?;
1149 Ok(())
1150}
1151
1152#[inline]
1153fn push_zeros(chain_table: &mut Vec<u64>, zeros: u32) {
1154 for _ in 0..zeros {
1155 chain_table.push(0);
1156 }
1157}
1158
1159fn read_value_change_alias2(
1160 mut chain_bytes: &[u8],
1161 max_handle: u64,
1162) -> ReadResult<(Vec<u64>, Vec<u32>, usize)> {
1163 let mut chain_table: Vec<u64> = Vec::with_capacity(max_handle as usize);
1164 let mut chain_table_lengths: Vec<u32> = vec![0u32; (max_handle + 1) as usize];
1165 let mut value = 0u64;
1166 let mut prev_alias = 0u32;
1167 let mut prev_idx = 0usize;
1168 while !chain_bytes.is_empty() {
1169 let idx = chain_table.len();
1170 let kind = chain_bytes[0];
1171 if (kind & 1) == 1 {
1172 let shval = read_variant_i64(&mut chain_bytes)? >> 1;
1173 match shval.cmp(&0) {
1174 Ordering::Greater => {
1175 value = (value as i64 + shval) as u64;
1176 if !chain_table.is_empty() {
1177 let len = (value - chain_table[prev_idx]) as u32;
1178 chain_table_lengths[prev_idx] = len;
1179 }
1180 prev_idx = idx;
1181 chain_table.push(value);
1182 }
1183 Ordering::Less => {
1184 chain_table.push(0);
1185 prev_alias = shval as u32;
1186 chain_table_lengths[idx] = prev_alias;
1187 }
1188 Ordering::Equal => {
1189 chain_table.push(0);
1190 chain_table_lengths[idx] = prev_alias;
1191 }
1192 }
1193 } else {
1194 let (value, _) = read_variant_u32(&mut chain_bytes)?;
1195 let zeros = value >> 1;
1196 push_zeros(&mut chain_table, zeros);
1197 }
1198 }
1199
1200 Ok((chain_table, chain_table_lengths, prev_idx))
1201}
1202
1203fn read_value_change_alias(
1204 mut chain_bytes: &[u8],
1205 max_handle: u64,
1206) -> ReadResult<(Vec<u64>, Vec<u32>, usize)> {
1207 let mut chain_table: Vec<u64> = Vec::with_capacity(max_handle as usize);
1208 let mut chain_table_lengths: Vec<u32> = vec![0u32; (max_handle + 1) as usize];
1209 let mut prev_idx = 0usize;
1210 let mut value = 0u64;
1211 while !chain_bytes.is_empty() {
1212 let (raw_val, _) = read_variant_u32(&mut chain_bytes)?;
1213 let idx = chain_table.len();
1214 if raw_val == 0 {
1215 chain_table.push(0); let (len, _) = read_variant_u32(&mut chain_bytes)?;
1217 chain_table_lengths[idx] = (-(len as i64)) as u32;
1218 } else if (raw_val & 1) == 1 {
1219 value += (raw_val as u64) >> 1;
1220 if idx > 0 {
1221 let len = (value - chain_table[prev_idx]) as u32;
1222 chain_table_lengths[prev_idx] = len;
1223 }
1224 chain_table.push(value);
1225 prev_idx = idx; } else {
1227 let zeros = raw_val >> 1;
1228 push_zeros(&mut chain_table, zeros);
1229 }
1230 }
1231
1232 Ok((chain_table, chain_table_lengths, prev_idx))
1233}
1234
1235fn fixup_chain_table(chain_table: &mut [u64], chain_lengths: &mut [u32]) {
1236 assert_eq!(chain_table.len(), chain_lengths.len());
1237 for ii in 0..chain_table.len() {
1238 let v32 = chain_lengths[ii] as i32;
1239 if (v32 < 0) && (chain_table[ii] == 0) {
1240 let v32_index = (-v32 - 1) as usize;
1242 if v32_index < ii {
1243 chain_table[ii] = chain_table[v32_index];
1245 chain_lengths[ii] = chain_lengths[v32_index];
1246 }
1247 }
1248 }
1249}
1250
1251pub(crate) fn read_chain_table(
1252 input: &mut (impl Read + Seek),
1253 chain_len_offset: u64,
1254 section_kind: DataSectionKind,
1255 max_handle: u64,
1256 start: u64,
1257) -> ReadResult<(Vec<u64>, Vec<u32>)> {
1258 input.seek(SeekFrom::Start(chain_len_offset))?;
1259 let chain_compressed_length = read_u64(input)?;
1260
1261 let chain_start = chain_len_offset - chain_compressed_length;
1263 input.seek(SeekFrom::Start(chain_start))?;
1264 let chain_bytes = read_bytes(input, chain_compressed_length as usize)?;
1265
1266 let (mut chain_table, mut chain_table_lengths, prev_idx) =
1267 if section_kind == DataSectionKind::DynamicAlias2 {
1268 read_value_change_alias2(&chain_bytes, max_handle)?
1269 } else {
1270 read_value_change_alias(&chain_bytes, max_handle)?
1271 };
1272 let last_table_entry = chain_start - start; chain_table.push(last_table_entry);
1274 chain_table_lengths[prev_idx] = (last_table_entry - chain_table[prev_idx]) as u32;
1275
1276 fixup_chain_table(&mut chain_table, &mut chain_table_lengths);
1277
1278 Ok((chain_table, chain_table_lengths))
1279}
1280
1281#[cfg(test)]
1282mod tests {
1283 use super::*;
1284 use proptest::prelude::*;
1285
1286 #[test]
1287 fn test_read_variant_i64() {
1288 let in1 = [0x13];
1290 assert_eq!(read_variant_i64(&mut in1.as_slice()).unwrap(), 19);
1291 let in0 = [0x7b];
1293 assert_eq!(read_variant_i64(&mut in0.as_slice()).unwrap(), -5);
1294 }
1295
1296 proptest! {
1297 #[test]
1298 fn test_read_write_variant_u64(value: u64) {
1299 let mut buf = std::io::Cursor::new(vec![0u8; 24]);
1300 write_variant_u64(&mut buf, value).unwrap();
1301 buf.seek(SeekFrom::Start(0)).unwrap();
1302 let (read_value, _) = read_variant_u64(&mut buf).unwrap();
1303 assert_eq!(read_value, value);
1304 }
1305 }
1306
1307 #[test]
1308 fn test_read_c_str_fixed_length() {
1309 let input = [b'h', b'i', 0u8, b'x'];
1310 assert_eq!(
1311 read_c_str_fixed_length(&mut input.as_slice(), 4).unwrap(),
1312 "hi"
1313 );
1314 let input2 = [b'h', b'i', b'i', 0u8, b'x'];
1315 assert_eq!(
1316 read_c_str_fixed_length(&mut input2.as_slice(), 5).unwrap(),
1317 "hii"
1318 );
1319 }
1320
1321 fn is_valid_c_str(value: &str, max_len: usize) -> bool {
1323 let string_bytes: &[u8] = value.as_bytes();
1324 let len_constraint = string_bytes.len() < max_len;
1325 let non_zero_constraint = !string_bytes.contains(&0u8);
1326 len_constraint && non_zero_constraint
1327 }
1328
1329 fn is_valid_alphanumeric_c_str(value: &str, max_len: usize) -> bool {
1330 let alphanumeric_constraint = value.chars().all(|c| c.is_alphanumeric());
1331 is_valid_c_str(value, max_len) && alphanumeric_constraint
1332 }
1333
1334 proptest! {
1335 #[test]
1336 fn test_write_c_str_fixed_length(string: String, max_len in 1 .. 400usize) {
1337 prop_assume!(is_valid_c_str(&string, max_len));
1338 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1339 write_c_str_fixed_length(&mut buf, &string, max_len).unwrap();
1340 buf.seek(SeekFrom::Start(0)).unwrap();
1341 assert_eq!(
1342 read_c_str_fixed_length(&mut buf, max_len).unwrap(),
1343 string
1344 );
1345 }
1346 }
1347
1348 proptest! {
1349 #[test]
1350 fn test_write_c_str(string: String, max_len in 1 .. 400usize) {
1351 prop_assume!(is_valid_c_str(&string, max_len));
1352 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1353 write_c_str(&mut buf, &string).unwrap();
1354 buf.seek(SeekFrom::Start(0)).unwrap();
1355 assert_eq!(
1356 read_c_str(&mut buf, max_len).unwrap(),
1357 string
1358 );
1359 }
1360 }
1361
1362 proptest! {
1363 #[test]
1364 fn test_read_write_header(header: Header) {
1365 prop_assume!(header.version.len() <= HEADER_VERSION_MAX_LEN);
1367 prop_assume!(header.date.len() <= HEADER_DATE_MAX_LEN );
1368
1369 let mut buf = [0u8; 512];
1370 write_header(&mut buf.as_mut(), &header).unwrap();
1371 let (actual_header, endian) = read_header(&mut buf.as_slice()).unwrap();
1372 assert_eq!(endian, FloatingPointEndian::Little);
1373 assert_eq!(actual_header, header);
1374 }
1375 }
1376
1377 proptest! {
1378 #[test]
1379 fn test_compress_bytes(bytes: Vec<u8>, allow_uncompressed: bool) {
1380 let mut buf = std::io::Cursor::new(vec![0u8; bytes.len() * 2]);
1381 let compressed_len = write_compressed_bytes(&mut buf, &bytes, flate2::Compression::new(9), allow_uncompressed).unwrap();
1382 if allow_uncompressed {
1383 assert!(compressed_len <= bytes.len());
1384 }
1385 buf.seek(SeekFrom::Start(0)).unwrap();
1386 let uncompressed = read_zlib_compressed_bytes(&mut buf, bytes.len() as u64, compressed_len as u64, allow_uncompressed).unwrap();
1387 assert_eq!(uncompressed, bytes);
1388 }
1389 }
1390
1391 proptest! {
1392 #[test]
1393 fn test_read_write_blackout(mut blackouts: Vec<BlackoutData>) {
1394 blackouts.sort_by(|a, b| a.time.cmp(&b.time));
1396
1397 let max_len = blackouts.len() * 5 + 3 * 8;
1399 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1400 write_blackout(&mut buf, &blackouts).unwrap();
1401 buf.seek(SeekFrom::Start(0)).unwrap();
1402 let actual = read_blackout(&mut buf).unwrap();
1403 assert_eq!(actual.len(), blackouts.len());
1404 assert_eq!(actual, blackouts);
1405 }
1406 }
1407
1408 proptest! {
1409 #[test]
1410 fn test_read_write_geometry(signals: Vec<SignalInfo>) {
1411 let max_len = signals.len() * 4 + 3 * 8;
1412 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1413 let comp = flate2::Compression::best();
1414 write_geometry(&mut buf, &signals, comp).unwrap();
1415 buf.seek(SeekFrom::Start(0)).unwrap();
1416 let actual = read_geometry(&mut buf).unwrap();
1417 assert_eq!(actual.len(), signals.len());
1418 assert_eq!(actual, signals);
1419 }
1420 }
1421
1422 fn hierarchy_entry_with_valid_c_strings(entry: &FstHierarchyEntry) -> bool {
1424 match entry {
1425 FstHierarchyEntry::Scope {
1426 name, component, ..
1427 } => {
1428 is_valid_c_str(name, HIERARCHY_NAME_MAX_SIZE)
1429 && is_valid_c_str(component, HIERARCHY_NAME_MAX_SIZE)
1430 }
1431 FstHierarchyEntry::UpScope => true,
1432 FstHierarchyEntry::Var { name, .. } => is_valid_c_str(name, HIERARCHY_NAME_MAX_SIZE),
1433 FstHierarchyEntry::PathName { name, .. } => {
1434 is_valid_c_str(name, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1435 }
1436 FstHierarchyEntry::SourceStem { .. } => true,
1437 FstHierarchyEntry::Comment { string } => {
1438 is_valid_c_str(string, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1439 }
1440 FstHierarchyEntry::EnumTable { name, mapping, .. } => {
1441 is_valid_alphanumeric_c_str(name, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1442 && mapping.iter().all(|(k, v)| {
1443 is_valid_alphanumeric_c_str(k, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1444 && is_valid_alphanumeric_c_str(v, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1445 })
1446 }
1447 FstHierarchyEntry::EnumTableRef { .. } => true,
1448 FstHierarchyEntry::VhdlVarInfo { type_name, .. } => {
1449 is_valid_c_str(type_name, HIERARCHY_NAME_MAX_SIZE)
1450 }
1451 FstHierarchyEntry::AttributeEnd => true,
1452 }
1453 }
1454
1455 fn hierarchy_entry_with_valid_mapping(entry: &FstHierarchyEntry) -> bool {
1457 match entry {
1458 FstHierarchyEntry::EnumTable { mapping, .. } => mapping
1459 .iter()
1460 .all(|(k, v)| is_valid_mapping_str(k) && is_valid_mapping_str(v)),
1461 _ => true,
1462 }
1463 }
1464 fn is_valid_mapping_str(value: &str) -> bool {
1465 !value.is_empty() && !value.contains(' ')
1466 }
1467
1468 fn hierarchy_entry_with_valid_port_width(entry: &FstHierarchyEntry) -> bool {
1470 if let FstHierarchyEntry::Var {
1471 tpe: FstVarType::Port,
1472 length,
1473 ..
1474 } = entry
1475 {
1476 *length < (u32::MAX / 3) - 2
1477 } else {
1478 true
1479 }
1480 }
1481
1482 fn read_write_hierarchy_entry(entry: FstHierarchyEntry) {
1483 let base_handle_count: u32 = match &entry {
1485 FstHierarchyEntry::Var {
1486 handle, is_alias, ..
1487 } => {
1488 if *is_alias {
1489 0
1490 } else {
1491 handle.get_index() as u32
1492 }
1493 }
1494 _ => 0,
1495 };
1496
1497 let max_len = 1024 * 64;
1498 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1499 let mut handle_count = base_handle_count;
1500 write_hierarchy_entry(&mut buf, &mut handle_count, &entry).unwrap();
1501 if base_handle_count > 0 {
1502 assert_eq!(handle_count, base_handle_count + 1);
1503 }
1504 buf.seek(SeekFrom::Start(0)).unwrap();
1505 handle_count = base_handle_count;
1506 let actual = read_hierarchy_entry(&mut buf, &mut handle_count)
1507 .unwrap()
1508 .unwrap();
1509 assert_eq!(actual, entry);
1510 }
1511
1512 #[test]
1513 fn test_read_write_hierarchy_path_name_entry() {
1514 let entry = FstHierarchyEntry::PathName {
1515 id: 1,
1516 name: "".to_string(),
1517 };
1518 read_write_hierarchy_entry(entry);
1519 }
1520
1521 proptest! {
1522 #[test]
1523 fn test_prop_read_write_hierarchy_entry(entry: FstHierarchyEntry) {
1524 prop_assume!(hierarchy_entry_with_valid_c_strings(&entry));
1525 prop_assume!(hierarchy_entry_with_valid_mapping(&entry));
1526 prop_assume!(hierarchy_entry_with_valid_port_width(&entry));
1527 read_write_hierarchy_entry(entry);
1528 }
1529 }
1530
1531 #[test]
1533 fn test_read_write_hierarchy_entry() {
1534 let entry = FstHierarchyEntry::Comment {
1536 string: "TEST ".repeat((8000 + 4) / 5),
1537 };
1538 read_write_hierarchy_entry(entry);
1539 }
1540
1541 proptest! {
1542 #[test]
1543 fn test_prop_read_write_hierarchy_bytes(tpe: HierarchyCompression, bytes: Vec<u8>) {
1544 let max_len = std::cmp::max(64, bytes.len() + 3 * 8);
1545 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1546 write_hierarchy_bytes(&mut buf, tpe, &bytes).unwrap();
1547 buf.seek(SeekFrom::Start(0)).unwrap();
1548 let actual = read_hierarchy_bytes(&mut buf, tpe).unwrap();
1549 assert_eq!(actual, bytes);
1550 }
1551 }
1552
1553 fn read_write_time_table(mut table: Vec<u64>, compressed: bool) {
1554 table.sort();
1556 let max_len = std::cmp::max(64, table.len() * 8 + 3 * 8);
1557 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1558 let comp = if compressed {
1559 Some(flate2::Compression::best())
1560 } else {
1561 None
1562 };
1563 write_time_chain(&mut buf, comp, &table).unwrap();
1564 let section_start = 0u64;
1565 let section_length = buf.stream_position().unwrap();
1566 buf.seek(SeekFrom::Start(0)).unwrap();
1567 let (actual_len, actual_table) =
1568 read_time_chain(&mut buf, section_start, section_length).unwrap();
1569 assert_eq!(actual_len, section_length);
1570 assert_eq!(actual_table, table);
1571 }
1572
1573 #[test]
1574 fn test_read_write_time_table_uncompressed() {
1575 let table = vec![1, 0];
1576 read_write_time_table(table, false);
1577 }
1578
1579 proptest! {
1580 #[test]
1581 fn test_prop_read_write_time_table(table: Vec<u64>, compressed: bool) {
1582 read_write_time_table(table, compressed);
1583 }
1584 }
1585}