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