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