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 self.signal_idx += 1;
1348
1349 let res = self.table.get_entry(self.signal_idx - 1);
1351 debug_assert!(res.is_some());
1352 res
1353 }
1354}
1355
1356fn read_value_change_alias2(
1357 mut chain_bytes: &[u8],
1358 max_handle: u64,
1359 last_table_entry: u32,
1360) -> ReadResult<OffsetTable> {
1361 let mut table = Vec::with_capacity(max_handle as usize);
1362 let mut offset: Option<NonZeroU32> = None;
1363 let mut prev_alias = 0u32;
1364 let mut prev_offset_idx = 0usize;
1365 while !chain_bytes.is_empty() {
1366 let idx = table.len();
1367 let kind = chain_bytes[0];
1368 if (kind & 1) == 1 {
1369 let shval = read_variant_i64(&mut chain_bytes)? >> 1;
1370 match shval.cmp(&0) {
1371 Ordering::Greater => {
1372 let new_offset = NonZeroU32::new(
1374 (offset.map(|o| o.get()).unwrap_or_default() as i64 + shval) as u32,
1375 )
1376 .unwrap();
1377 if let Some(prev_offset) = offset {
1379 let len = NonZeroU32::new(new_offset.get() - prev_offset.get()).unwrap();
1380 table[prev_offset_idx] = SignalDataLoc::Offset(prev_offset, len);
1381 }
1382 offset = Some(new_offset);
1383 prev_offset_idx = idx;
1384 table.push(SignalDataLoc::None);
1386 }
1387 Ordering::Less => {
1388 prev_alias = (-shval - 1) as u32;
1390 table.push(SignalDataLoc::Alias(prev_alias));
1391 }
1392 Ordering::Equal => {
1393 table.push(SignalDataLoc::Alias(prev_alias));
1395 }
1396 }
1397 } else {
1398 let (value, _) = read_variant_u32(&mut chain_bytes)?;
1400 let zeros = value >> 1;
1401 for _ in 0..zeros {
1402 table.push(SignalDataLoc::None);
1403 }
1404 }
1405 }
1406
1407 if let Some(prev_offset) = offset {
1409 let len = NonZeroU32::new(last_table_entry - prev_offset.get()).unwrap();
1410 table[prev_offset_idx] = SignalDataLoc::Offset(prev_offset, len);
1411 }
1412
1413 Ok(table.into())
1414}
1415
1416fn read_value_change_alias(
1417 mut chain_bytes: &[u8],
1418 max_handle: u64,
1419 last_table_entry: u32,
1420) -> ReadResult<OffsetTable> {
1421 let mut table = Vec::with_capacity(max_handle as usize);
1422 let mut prev_offset_idx = 0usize;
1423 let mut offset: Option<NonZeroU32> = None;
1424 while !chain_bytes.is_empty() {
1425 let (raw_val, _) = read_variant_u32(&mut chain_bytes)?;
1426 let idx = table.len();
1427 if raw_val == 0 {
1428 let (raw_alias, _) = read_variant_u32(&mut chain_bytes)?;
1429 let alias = ((raw_alias as i64) - 1) as u32;
1430 table.push(SignalDataLoc::Alias(alias));
1431 } else if (raw_val & 1) == 1 {
1432 let new_offset =
1434 NonZeroU32::new(offset.map(|o| o.get()).unwrap_or_default() + (raw_val >> 1))
1435 .unwrap();
1436 if let Some(prev_offset) = offset {
1438 let len = NonZeroU32::new(new_offset.get() - prev_offset.get()).unwrap();
1439 table[prev_offset_idx] = SignalDataLoc::Offset(prev_offset, len);
1440 }
1441 offset = Some(new_offset);
1442 prev_offset_idx = idx;
1443 table.push(SignalDataLoc::None);
1445 } else {
1446 let zeros = raw_val >> 1;
1448 for _ in 0..zeros {
1449 table.push(SignalDataLoc::None);
1450 }
1451 }
1452 }
1453
1454 if let Some(prev_offset) = offset {
1456 let len = NonZeroU32::new(last_table_entry - prev_offset.get()).unwrap();
1457 table[prev_offset_idx] = SignalDataLoc::Offset(prev_offset, len);
1458 }
1459
1460 Ok(table.into())
1461}
1462
1463#[derive(Debug, Copy, Clone)]
1465enum SignalDataLoc {
1466 None,
1468 Alias(u32),
1470 Offset(NonZeroU32, NonZeroU32),
1472}
1473
1474pub(crate) fn read_signal_locs(
1475 input: &mut (impl Read + Seek),
1476 chain_len_offset: u64,
1477 section_kind: DataSectionKind,
1478 max_handle: u64,
1479 start: u64,
1480) -> ReadResult<OffsetTable> {
1481 input.seek(SeekFrom::Start(chain_len_offset))?;
1482 let chain_compressed_length = read_u64(input)?;
1483
1484 let chain_start = chain_len_offset - chain_compressed_length;
1486 input.seek(SeekFrom::Start(chain_start))?;
1487 let chain_bytes = read_bytes(input, chain_compressed_length as usize)?;
1488
1489 let last_table_entry = (chain_start - start) as u32; if section_kind == DataSectionKind::DynamicAlias2 {
1491 read_value_change_alias2(&chain_bytes, max_handle, last_table_entry)
1492 } else {
1493 read_value_change_alias(&chain_bytes, max_handle, last_table_entry)
1494 }
1495}
1496
1497#[cfg(test)]
1498mod tests {
1499 use super::*;
1500 use proptest::prelude::*;
1501
1502 #[test]
1503 fn data_struct_sizes() {
1504 assert_eq!(
1505 std::mem::size_of::<SignalDataLoc>(),
1506 std::mem::size_of::<u64>() + std::mem::size_of::<u32>()
1507 );
1508 }
1509
1510 #[test]
1511 fn test_read_variant_i64() {
1512 let in1 = [0x13];
1514 assert_eq!(read_variant_i64(&mut in1.as_slice()).unwrap(), 19);
1515 let in0 = [0x7b];
1517 assert_eq!(read_variant_i64(&mut in0.as_slice()).unwrap(), -5);
1518 }
1519
1520 #[test]
1521 fn regression_test_read_write_variant_i64() {
1522 do_test_read_write_variant_i64(-36028797018963969);
1523 do_test_read_write_variant_i64(-4611686018427387905);
1524 }
1525
1526 fn do_test_read_write_variant_i64(value: i64) {
1527 let mut buf = std::io::Cursor::new(vec![0u8; 24]);
1528 write_variant_i64(&mut buf, value).unwrap();
1529 buf.seek(SeekFrom::Start(0)).unwrap();
1530 let read_value = read_variant_i64(&mut buf).unwrap();
1531 assert_eq!(read_value, value);
1532 }
1533
1534 proptest! {
1535 #[test]
1536 fn test_read_write_variant_u64(value: u64) {
1537 let mut buf = std::io::Cursor::new(vec![0u8; 24]);
1538 write_variant_u64(&mut buf, value).unwrap();
1539 buf.seek(SeekFrom::Start(0)).unwrap();
1540 let (read_value, _) = read_variant_u64(&mut buf).unwrap();
1541 assert_eq!(read_value, value);
1542 }
1543
1544 #[test]
1545 fn test_read_write_variant_i64(value: i64) {
1546 do_test_read_write_variant_i64(value);
1547 }
1548 }
1549
1550 #[test]
1551 fn test_read_c_str_fixed_length() {
1552 let input = [b'h', b'i', 0u8, b'x'];
1553 assert_eq!(
1554 read_c_str_fixed_length(&mut input.as_slice(), 4).unwrap(),
1555 "hi"
1556 );
1557 let input2 = [b'h', b'i', b'i', 0u8, b'x'];
1558 assert_eq!(
1559 read_c_str_fixed_length(&mut input2.as_slice(), 5).unwrap(),
1560 "hii"
1561 );
1562 }
1563
1564 fn is_valid_c_str(value: &str, max_len: usize) -> bool {
1566 let string_bytes: &[u8] = value.as_bytes();
1567 let len_constraint = string_bytes.len() < max_len;
1568 let non_zero_constraint = !string_bytes.contains(&0u8);
1569 len_constraint && non_zero_constraint
1570 }
1571
1572 fn is_valid_alphanumeric_c_str(value: &str, max_len: usize) -> bool {
1573 let alphanumeric_constraint = value.chars().all(|c| c.is_alphanumeric());
1574 is_valid_c_str(value, max_len) && alphanumeric_constraint
1575 }
1576
1577 proptest! {
1578 #[test]
1579 fn test_write_c_str_fixed_length(string: String, max_len in 1 .. 400usize) {
1580 prop_assume!(is_valid_c_str(&string, max_len));
1581 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1582 write_c_str_fixed_length(&mut buf, &string, max_len).unwrap();
1583 buf.seek(SeekFrom::Start(0)).unwrap();
1584 assert_eq!(
1585 read_c_str_fixed_length(&mut buf, max_len).unwrap(),
1586 string
1587 );
1588 }
1589 }
1590
1591 proptest! {
1592 #[test]
1593 fn test_write_c_str(string: String, max_len in 1 .. 400usize) {
1594 prop_assume!(is_valid_c_str(&string, max_len));
1595 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1596 write_c_str(&mut buf, &string).unwrap();
1597 buf.seek(SeekFrom::Start(0)).unwrap();
1598 assert_eq!(
1599 read_c_str(&mut buf, max_len).unwrap(),
1600 string
1601 );
1602 }
1603 }
1604
1605 proptest! {
1606 #[test]
1607 fn test_read_write_header(header: Header) {
1608 prop_assume!(header.version.len() <= HEADER_VERSION_MAX_LEN);
1610 prop_assume!(header.date.len() <= HEADER_DATE_MAX_LEN );
1611
1612 let mut buf = [0u8; 512];
1613 write_header(&mut buf.as_mut(), &header).unwrap();
1614 let (actual_header, endian) = read_header(&mut buf.as_slice()).unwrap();
1615 assert_eq!(endian, FloatingPointEndian::Little);
1616 assert_eq!(actual_header, header);
1617 }
1618 }
1619
1620 proptest! {
1621 #[test]
1622 fn test_compress_bytes(bytes: Vec<u8>, allow_uncompressed: bool) {
1623 let mut buf = std::io::Cursor::new(vec![0u8; bytes.len() * 2]);
1624 let compressed_len = write_compressed_bytes(&mut buf, &bytes, 3, allow_uncompressed).unwrap();
1625 if allow_uncompressed {
1626 assert!(compressed_len <= bytes.len());
1627 }
1628 buf.seek(SeekFrom::Start(0)).unwrap();
1629 let uncompressed = read_zlib_compressed_bytes(&mut buf, bytes.len() as u64, compressed_len as u64, allow_uncompressed).unwrap();
1630 assert_eq!(uncompressed, bytes);
1631 }
1632 }
1633
1634 proptest! {
1635 #[test]
1636 fn test_read_write_blackout(mut blackouts: Vec<BlackoutData>) {
1637 blackouts.sort_by(|a, b| a.time.cmp(&b.time));
1639
1640 let max_len = blackouts.len() * 5 + 3 * 8;
1642 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1643 write_blackout(&mut buf, &blackouts).unwrap();
1644 buf.seek(SeekFrom::Start(0)).unwrap();
1645 let actual = read_blackout(&mut buf).unwrap();
1646 assert_eq!(actual.len(), blackouts.len());
1647 assert_eq!(actual, blackouts);
1648 }
1649 }
1650
1651 proptest! {
1652 #[test]
1653 fn test_read_write_geometry(signals: Vec<SignalInfo>) {
1654 let max_len = signals.len() * 4 + 3 * 8;
1655 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1656 write_geometry(&mut buf, &signals, 3).unwrap();
1657 buf.seek(SeekFrom::Start(0)).unwrap();
1658 let actual = read_geometry(&mut buf).unwrap();
1659 assert_eq!(actual.len(), signals.len());
1660 assert_eq!(actual, signals);
1661 }
1662 }
1663
1664 fn hierarchy_entry_with_valid_c_strings(entry: &FstHierarchyEntry) -> bool {
1666 match entry {
1667 FstHierarchyEntry::Scope {
1668 name, component, ..
1669 } => {
1670 is_valid_c_str(name, HIERARCHY_NAME_MAX_SIZE)
1671 && is_valid_c_str(component, HIERARCHY_NAME_MAX_SIZE)
1672 }
1673 FstHierarchyEntry::UpScope => true,
1674 FstHierarchyEntry::Var { name, .. } => is_valid_c_str(name, HIERARCHY_NAME_MAX_SIZE),
1675 FstHierarchyEntry::PathName { name, .. } => {
1676 is_valid_c_str(name, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1677 }
1678 FstHierarchyEntry::SourceStem { .. } => true,
1679 FstHierarchyEntry::Comment { string } => {
1680 is_valid_c_str(string, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1681 }
1682 FstHierarchyEntry::EnumTable { name, mapping, .. } => {
1683 is_valid_alphanumeric_c_str(name, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1684 && mapping.iter().all(|(k, v)| {
1685 is_valid_alphanumeric_c_str(k, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1686 && is_valid_alphanumeric_c_str(v, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1687 })
1688 }
1689 FstHierarchyEntry::EnumTableRef { .. } => true,
1690 FstHierarchyEntry::VhdlVarInfo { type_name, .. } => {
1691 is_valid_c_str(type_name, HIERARCHY_NAME_MAX_SIZE)
1692 }
1693 FstHierarchyEntry::AttributeEnd => true,
1694 }
1695 }
1696
1697 fn hierarchy_entry_with_valid_mapping(entry: &FstHierarchyEntry) -> bool {
1699 match entry {
1700 FstHierarchyEntry::EnumTable { mapping, .. } => mapping
1701 .iter()
1702 .all(|(k, v)| is_valid_mapping_str(k) && is_valid_mapping_str(v)),
1703 _ => true,
1704 }
1705 }
1706 fn is_valid_mapping_str(value: &str) -> bool {
1707 !value.is_empty() && !value.contains(' ')
1708 }
1709
1710 fn hierarchy_entry_with_valid_port_width(entry: &FstHierarchyEntry) -> bool {
1712 if let FstHierarchyEntry::Var {
1713 tpe: FstVarType::Port,
1714 length,
1715 ..
1716 } = entry
1717 {
1718 *length < (u32::MAX / 3) - 2
1719 } else {
1720 true
1721 }
1722 }
1723
1724 fn read_write_hierarchy_entry(entry: FstHierarchyEntry) {
1725 let base_handle_count: u32 = match &entry {
1727 FstHierarchyEntry::Var {
1728 handle, is_alias, ..
1729 } => {
1730 if *is_alias {
1731 0
1732 } else {
1733 handle.get_index() as u32
1734 }
1735 }
1736 _ => 0,
1737 };
1738
1739 let max_len = 1024 * 64;
1740 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1741 let mut handle_count = base_handle_count;
1742 write_hierarchy_entry(&mut buf, &mut handle_count, &entry).unwrap();
1743 if base_handle_count > 0 {
1744 assert_eq!(handle_count, base_handle_count + 1);
1745 }
1746 buf.seek(SeekFrom::Start(0)).unwrap();
1747 handle_count = base_handle_count;
1748 let actual = read_hierarchy_entry(&mut buf, &mut handle_count)
1749 .unwrap()
1750 .unwrap();
1751 assert_eq!(actual, entry);
1752 }
1753
1754 #[test]
1755 fn test_read_write_hierarchy_path_name_entry() {
1756 let entry = FstHierarchyEntry::PathName {
1757 id: 1,
1758 name: "".to_string(),
1759 };
1760 read_write_hierarchy_entry(entry);
1761 }
1762
1763 proptest! {
1764 #[test]
1765 fn test_prop_read_write_hierarchy_entry(entry: FstHierarchyEntry) {
1766 prop_assume!(hierarchy_entry_with_valid_c_strings(&entry));
1767 prop_assume!(hierarchy_entry_with_valid_mapping(&entry));
1768 prop_assume!(hierarchy_entry_with_valid_port_width(&entry));
1769 read_write_hierarchy_entry(entry);
1770 }
1771 }
1772
1773 #[test]
1775 fn test_read_write_hierarchy_entry() {
1776 let entry = FstHierarchyEntry::Comment {
1778 string: "TEST ".repeat((8000 + 4) / 5),
1779 };
1780 read_write_hierarchy_entry(entry);
1781 }
1782
1783 fn do_test_read_write_hierarchy_bytes(tpe: HierarchyCompression, bytes: Vec<u8>) {
1784 let max_len = match tpe {
1785 HierarchyCompression::Uncompressed => bytes.len(),
1786 _ => std::cmp::max(64, bytes.len() + 3 * 8),
1787 };
1788 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1789 write_hierarchy_bytes(&mut buf, tpe, &bytes).unwrap();
1790 buf.seek(SeekFrom::Start(0)).unwrap();
1791 let actual = read_hierarchy_bytes(&mut buf, tpe).unwrap();
1792 assert_eq!(actual, bytes);
1793 }
1794
1795 #[test]
1796 fn test_read_write_hierarchy_bytes_regression() {
1797 do_test_read_write_hierarchy_bytes(HierarchyCompression::Lz4, vec![]);
1798 do_test_read_write_hierarchy_bytes(HierarchyCompression::ZLib, vec![]);
1799 }
1800
1801 proptest! {
1802 #[test]
1803 fn test_prop_read_write_hierarchy_bytes(tpe: HierarchyCompression, bytes: Vec<u8>) {
1804 do_test_read_write_hierarchy_bytes(tpe, bytes);
1805 }
1806 }
1807
1808 fn read_write_time_table(mut table: Vec<u64>, compressed: bool) {
1809 table.sort();
1811 let max_len = std::cmp::max(64, table.len() * 8 + 3 * 8);
1812 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1813 let comp = if compressed { Some(3) } else { None };
1814 write_time_table(&mut buf, comp, &table).unwrap();
1815 let section_start = 0u64;
1816 let section_length = buf.stream_position().unwrap();
1817 buf.seek(SeekFrom::Start(0)).unwrap();
1818 let (actual_len, actual_table) =
1819 read_time_table(&mut buf, section_start, section_length).unwrap();
1820 assert_eq!(actual_len, section_length);
1821 assert_eq!(actual_table, table);
1822 }
1823
1824 #[test]
1825 fn test_read_write_time_table_uncompressed() {
1826 let table = vec![1, 0];
1827 read_write_time_table(table, false);
1828 }
1829
1830 #[test]
1831 fn test_read_write_time_table_compressed() {
1832 let table = (0..10000).collect();
1833 read_write_time_table(table, true);
1834 }
1835
1836 proptest! {
1837 #[test]
1838 fn test_prop_read_write_time_table(table: Vec<u64>, compressed: bool) {
1839 read_write_time_table(table, compressed);
1840 }
1841 }
1842}