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