Add FST library
authorMiodrag Milanovic <mmicko@gmail.com>
Tue, 25 Jan 2022 08:53:41 +0000 (09:53 +0100)
committerMiodrag Milanovic <mmicko@gmail.com>
Tue, 25 Jan 2022 08:53:41 +0000 (09:53 +0100)
Makefile
libs/fst/block_format.txt [new file with mode: 0644]
libs/fst/config.h [new file with mode: 0644]
libs/fst/fastlz.cc [new file with mode: 0644]
libs/fst/fastlz.h [new file with mode: 0644]
libs/fst/fst_win_unistd.h [new file with mode: 0644]
libs/fst/fstapi.cc [new file with mode: 0644]
libs/fst/fstapi.h [new file with mode: 0644]
libs/fst/lz4.cc [new file with mode: 0644]
libs/fst/lz4.h [new file with mode: 0644]

index 27aa10d363d8b9c4cba57d630d64d27e9d7429f4..f67d1e1db5212abdfbc72dc311cb217cb6523a10 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -642,6 +642,10 @@ OBJS += libs/minisat/SimpSolver.o
 OBJS += libs/minisat/Solver.o
 OBJS += libs/minisat/System.o
 
+OBJS += libs/fst/fstapi.o
+OBJS += libs/fst/fastlz.o
+OBJS += libs/fst/lz4.o
+
 include $(YOSYS_SRC)/frontends/*/Makefile.inc
 include $(YOSYS_SRC)/passes/*/Makefile.inc
 include $(YOSYS_SRC)/backends/*/Makefile.inc
diff --git a/libs/fst/block_format.txt b/libs/fst/block_format.txt
new file mode 100644 (file)
index 0000000..e6fe166
--- /dev/null
@@ -0,0 +1,130 @@
+See fstapi.h for the values for the FST_BL_XXX enums.
+
+===========================================================================
+
+compressed wrapper (typically over whole file)
+
+uint8_t                FST_BL_ZWRAPPER
+uint64_t        section length
+uint64_t        length of uncompressed data
+[zlib compressed data]
+
+===========================================================================
+
+header block
+
+uint8_t                FST_BL_HDR
+uint64_t       section length
+uint64_t       start time
+uint64_t       end time
+double         endian test for "e"
+uint64_t       memory used by writer
+uint64_t       scope creation count
+uint64_t       var creation count
+uint64_t       max var idcode
+uint64_t       vc section count
+int8_t         timescale exponent
+[128 bytes]    version
+[128 bytes]    date
+
+===========================================================================
+
+geometry block
+
+uint8_t         FST_BL_GEOM
+uint64_t        section length
+uint64_t        length of uncompressed geometry data
+uint64_t        maxhandle
+[compressed data]
+
+(length of compressed data is section length - 24)
+
+===========================================================================
+
+hierarchy block
+
+uint8_t         FST_BL_HIER
+uint64_t        section length
+uint64_t        length of uncompressed hier data
+[zlib compressed data]
+
+or
+
+uint8_t         FST_BL_HIER_LZ4
+uint64_t        section length
+uint64_t        length of uncompressed hier data
+[lz4 compressed data]
+
+uint8_t         FST_BL_HIER_LZ4DUO
+uint64_t        section length
+uint64_t        length of uncompressed hier data
+varint         length of hier data compressed once with lz4
+[lz4 double compressed data]
+
+
+===========================================================================
+
+dumpon/off block
+
+uint8_t         FST_BL_BLACKOUT
+uint64_t        section length
+varint         num blackouts (section below is repeated this # times)
+[
+uint8_t                on/off (nonzero = on)
+varint         delta time
+]
+
+===========================================================================
+
+1..n value change blocks:
+
+// header
+
+uint8_t                FST_BL_VCDATA (or FST_BL_VCDATA_DYN_ALIAS)
+uint64_t       section length
+uint64_t       begin time of section
+uint64_t       end time of section
+uint64_t       amount of buffer memory required in reader for full vc traversal
+varint         maxvalpos (length of uncompressed data)
+varint         length of compressed data
+varint         maxhandle associated with this checkpoint data
+[compressed data]
+
+---
+
+// value changes
+
+varint         maxhandle associated with the value change data
+uint8_t                pack type ('F' is fastlz, '4' is lz4,
+                       others ['Z'/'!'] are zlib)
+
+varint         chain 0 compressed data length (0 = uncompressed)
+[compressed data]
+...
+varint         chain n compressed data length (0 = uncompressed)
+[compressed data]
+
+---
+
+// index: chain pointer table (from 0..maxhandle-1)
+
+varint         if &1 == 1, this is <<1 literal delta
+               if &1 == 0, this is <<1 RLE count of zeros
+               if == 0, next varint is handle of prev chain to use,
+                       bit only if FST_BL_VCDATA_DYN_ALIAS or
+                       later VCDATA format
+
+---
+
+uint64_t       index length (subtract from here to get index position)
+
+---
+
+[compressed data for time section]
+uint64_t       uncompressed data length in bytes
+uint64_t       compressed data length in bytes
+uint64_t       number of time items
+
+// end of section
+
+===========================================================================
diff --git a/libs/fst/config.h b/libs/fst/config.h
new file mode 100644 (file)
index 0000000..5a9d2a0
--- /dev/null
@@ -0,0 +1,33 @@
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+ */
+#define HAVE_ALLOCA_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#define HAVE_GETOPT_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `pthread' library (-lpthread). */
+#define HAVE_LIBPTHREAD 1
+
+/* Define to 1 if you have the `realpath' function. */
+#define HAVE_REALPATH 1
+
+/* Define to 1 if you have the <rpc/xdr.h> header file. */
+/* #undef HAVE_RPC_XDR_H */
+
+/* Define to 1 if you have the `setenv' function. */
+#define HAVE_SETENV 1
+
+/* Define to 1 if you have the `unsetenv' function. */
+#define HAVE_UNSETENV 1
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "bybell@rocketmail.com"
diff --git a/libs/fst/fastlz.cc b/libs/fst/fastlz.cc
new file mode 100644 (file)
index 0000000..68bda33
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+  FastLZ - lightning-fast lossless compression library
+
+  Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
+  Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
+  Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+
+  SPDX-License-Identifier: MIT
+*/
+
+#include "fastlz.h"
+
+#if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR)
+
+/*
+ * Always check for bound when decompressing.
+ * Generally it is best to leave it defined.
+ */
+#define FASTLZ_SAFE
+
+/*
+ * Give hints to the compiler for branch prediction optimization.
+ */
+#if defined(__GNUC__) && (__GNUC__ > 2)
+#define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
+#define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
+#else
+#define FASTLZ_EXPECT_CONDITIONAL(c) (c)
+#define FASTLZ_UNEXPECT_CONDITIONAL(c) (c)
+#endif
+
+/*
+ * Use inlined functions for supported systems.
+ */
+#if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C)
+#define FASTLZ_INLINE inline
+#elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__)
+#define FASTLZ_INLINE __inline
+#else
+#define FASTLZ_INLINE
+#endif
+
+/*
+ * Prevent accessing more than 8-bit at once, except on x86 architectures.
+ */
+#if !defined(FASTLZ_STRICT_ALIGN)
+#define FASTLZ_STRICT_ALIGN
+#if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */
+#undef FASTLZ_STRICT_ALIGN
+#elif defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(__amd64) /* GNU C */
+#undef FASTLZ_STRICT_ALIGN
+#elif defined(_M_IX86) /* Intel, MSVC */
+#undef FASTLZ_STRICT_ALIGN
+#elif defined(__386)
+#undef FASTLZ_STRICT_ALIGN
+#elif defined(_X86_) /* MinGW */
+#undef FASTLZ_STRICT_ALIGN
+#elif defined(__I86__) /* Digital Mars */
+#undef FASTLZ_STRICT_ALIGN
+#endif
+#endif
+
+/* prototypes */
+int fastlz_compress(const void *input, int length, void *output);
+int fastlz_compress_level(int level, const void *input, int length, void *output);
+int fastlz_decompress(const void *input, int length, void *output, int maxout);
+
+#define MAX_COPY 32
+#define MAX_LEN 264 /* 256 + 8 */
+#define MAX_DISTANCE 8192
+
+#if !defined(FASTLZ_STRICT_ALIGN)
+#define FASTLZ_READU16(p) *((const flzuint16 *)(p))
+#else
+#define FASTLZ_READU16(p) ((p)[0] | (p)[1] << 8)
+#endif
+
+#define HASH_LOG 13
+#define HASH_SIZE (1 << HASH_LOG)
+#define HASH_MASK (HASH_SIZE - 1)
+#define HASH_FUNCTION(v, p)                                                                                            \
+    {                                                                                                                  \
+        v = FASTLZ_READU16(p);                                                                                         \
+        v ^= FASTLZ_READU16(p + 1) ^ (v >> (16 - HASH_LOG));                                                           \
+        v &= HASH_MASK;                                                                                                \
+    }
+
+#undef FASTLZ_LEVEL
+#define FASTLZ_LEVEL 1
+
+#undef FASTLZ_COMPRESSOR
+#undef FASTLZ_DECOMPRESSOR
+#define FASTLZ_COMPRESSOR fastlz1_compress
+#define FASTLZ_DECOMPRESSOR fastlz1_decompress
+static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void *input, int length, void *output);
+static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void *input, int length, void *output, int maxout);
+#include "fastlz.cc"
+
+#undef FASTLZ_LEVEL
+#define FASTLZ_LEVEL 2
+
+#undef MAX_DISTANCE
+#define MAX_DISTANCE 8191
+#define MAX_FARDISTANCE (65535 + MAX_DISTANCE - 1)
+
+#undef FASTLZ_COMPRESSOR
+#undef FASTLZ_DECOMPRESSOR
+#define FASTLZ_COMPRESSOR fastlz2_compress
+#define FASTLZ_DECOMPRESSOR fastlz2_decompress
+static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void *input, int length, void *output);
+static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void *input, int length, void *output, int maxout);
+#include "fastlz.cc"
+
+int fastlz_compress(const void *input, int length, void *output)
+{
+    /* for short block, choose fastlz1 */
+    if (length < 65536)
+        return fastlz1_compress(input, length, output);
+
+    /* else... */
+    return fastlz2_compress(input, length, output);
+}
+
+int fastlz_decompress(const void *input, int length, void *output, int maxout)
+{
+    /* magic identifier for compression level */
+    int level = ((*(const flzuint8 *)input) >> 5) + 1;
+
+    if (level == 1)
+        return fastlz1_decompress(input, length, output, maxout);
+    if (level == 2)
+        return fastlz2_decompress(input, length, output, maxout);
+
+    /* unknown level, trigger error */
+    return 0;
+}
+
+int fastlz_compress_level(int level, const void *input, int length, void *output)
+{
+    if (level == 1)
+        return fastlz1_compress(input, length, output);
+    if (level == 2)
+        return fastlz2_compress(input, length, output);
+
+    return 0;
+}
+
+#else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */
+
+static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void *input, int length, void *output)
+{
+    const flzuint8 *ip = (const flzuint8 *)input;
+    const flzuint8 *ip_bound = ip + length - 2;
+    const flzuint8 *ip_limit = ip + length - 12;
+    flzuint8 *op = (flzuint8 *)output;
+
+    const flzuint8 *htab[HASH_SIZE];
+    const flzuint8 **hslot;
+    flzuint32 hval;
+
+    flzuint32 copy;
+
+    /* sanity check */
+    if (FASTLZ_UNEXPECT_CONDITIONAL(length < 4)) {
+        if (length) {
+            /* create literal copy only */
+            *op++ = length - 1;
+            ip_bound++;
+            while (ip <= ip_bound)
+                *op++ = *ip++;
+            return length + 1;
+        } else
+            return 0;
+    }
+
+    /* initializes hash table */
+    for (hslot = htab; hslot < htab + HASH_SIZE; hslot++)
+        *hslot = ip;
+
+    /* we start with literal copy */
+    copy = 2;
+    *op++ = MAX_COPY - 1;
+    *op++ = *ip++;
+    *op++ = *ip++;
+
+    /* main loop */
+    while (FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) {
+        const flzuint8 *ref;
+        flzuint32 distance;
+
+        /* minimum match length */
+        flzuint32 len = 3;
+
+        /* comparison starting-point */
+        const flzuint8 *anchor = ip;
+
+        /* check for a run */
+#if FASTLZ_LEVEL == 2
+        if (ip[0] == ip[-1] && FASTLZ_READU16(ip - 1) == FASTLZ_READU16(ip + 1)) {
+            distance = 1;
+            /* ip += 3; */ /* scan-build, never used */
+            ref = anchor - 1 + 3;
+            goto match;
+        }
+#endif
+
+        /* find potential match */
+        HASH_FUNCTION(hval, ip);
+        hslot = htab + hval;
+        ref = htab[hval];
+
+        /* calculate distance to the match */
+        distance = anchor - ref;
+
+        /* update hash table */
+        *hslot = anchor;
+
+        /* is this a match? check the first 3 bytes */
+        if (distance == 0 ||
+#if FASTLZ_LEVEL == 1
+            (distance >= MAX_DISTANCE) ||
+#else
+            (distance >= MAX_FARDISTANCE) ||
+#endif
+            *ref++ != *ip++ || *ref++ != *ip++ || *ref++ != *ip++)
+            goto literal;
+
+#if FASTLZ_LEVEL == 2
+        /* far, needs at least 5-byte match */
+        if (distance >= MAX_DISTANCE) {
+            if (*ip++ != *ref++ || *ip++ != *ref++)
+                goto literal;
+            len += 2;
+        }
+
+    match:
+#endif
+
+        /* last matched byte */
+        ip = anchor + len;
+
+        /* distance is biased */
+        distance--;
+
+        if (!distance) {
+            /* zero distance means a run */
+            flzuint8 x = ip[-1];
+            while (ip < ip_bound)
+                if (*ref++ != x)
+                    break;
+                else
+                    ip++;
+        } else
+            for (;;) {
+                /* safe because the outer check against ip limit */
+                if (*ref++ != *ip++)
+                    break;
+                if (*ref++ != *ip++)
+                    break;
+                if (*ref++ != *ip++)
+                    break;
+                if (*ref++ != *ip++)
+                    break;
+                if (*ref++ != *ip++)
+                    break;
+                if (*ref++ != *ip++)
+                    break;
+                if (*ref++ != *ip++)
+                    break;
+                if (*ref++ != *ip++)
+                    break;
+                while (ip < ip_bound)
+                    if (*ref++ != *ip++)
+                        break;
+                break;
+            }
+
+        /* if we have copied something, adjust the copy count */
+        if (copy)
+            /* copy is biased, '0' means 1 byte copy */
+            *(op - copy - 1) = copy - 1;
+        else
+            /* back, to overwrite the copy count */
+            op--;
+
+        /* reset literal counter */
+        copy = 0;
+
+        /* length is biased, '1' means a match of 3 bytes */
+        ip -= 3;
+        len = ip - anchor;
+
+        /* encode the match */
+#if FASTLZ_LEVEL == 2
+        if (distance < MAX_DISTANCE) {
+            if (len < 7) {
+                *op++ = (len << 5) + (distance >> 8);
+                *op++ = (distance & 255);
+            } else {
+                *op++ = (7 << 5) + (distance >> 8);
+                for (len -= 7; len >= 255; len -= 255)
+                    *op++ = 255;
+                *op++ = len;
+                *op++ = (distance & 255);
+            }
+        } else {
+            /* far away, but not yet in the another galaxy... */
+            if (len < 7) {
+                distance -= MAX_DISTANCE;
+                *op++ = (len << 5) + 31;
+                *op++ = 255;
+                *op++ = distance >> 8;
+                *op++ = distance & 255;
+            } else {
+                distance -= MAX_DISTANCE;
+                *op++ = (7 << 5) + 31;
+                for (len -= 7; len >= 255; len -= 255)
+                    *op++ = 255;
+                *op++ = len;
+                *op++ = 255;
+                *op++ = distance >> 8;
+                *op++ = distance & 255;
+            }
+        }
+#else
+
+        if (FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN - 2))
+            while (len > MAX_LEN - 2) {
+                *op++ = (7 << 5) + (distance >> 8);
+                *op++ = MAX_LEN - 2 - 7 - 2;
+                *op++ = (distance & 255);
+                len -= MAX_LEN - 2;
+            }
+
+        if (len < 7) {
+            *op++ = (len << 5) + (distance >> 8);
+            *op++ = (distance & 255);
+        } else {
+            *op++ = (7 << 5) + (distance >> 8);
+            *op++ = len - 7;
+            *op++ = (distance & 255);
+        }
+#endif
+
+        /* update the hash at match boundary */
+        HASH_FUNCTION(hval, ip);
+        htab[hval] = ip++;
+        HASH_FUNCTION(hval, ip);
+        htab[hval] = ip++;
+
+        /* assuming literal copy */
+        *op++ = MAX_COPY - 1;
+
+        continue;
+
+    literal:
+        *op++ = *anchor++;
+        ip = anchor;
+        copy++;
+        if (FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) {
+            copy = 0;
+            *op++ = MAX_COPY - 1;
+        }
+    }
+
+    /* left-over as literal copy */
+    ip_bound++;
+    while (ip <= ip_bound) {
+        *op++ = *ip++;
+        copy++;
+        if (copy == MAX_COPY) {
+            copy = 0;
+            *op++ = MAX_COPY - 1;
+        }
+    }
+
+    /* if we have copied something, adjust the copy length */
+    if (copy)
+        *(op - copy - 1) = copy - 1;
+    else
+        op--;
+
+#if FASTLZ_LEVEL == 2
+    /* marker for fastlz2 */
+    *(flzuint8 *)output |= (1 << 5);
+#endif
+
+    return op - (flzuint8 *)output;
+}
+
+static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void *input, int length, void *output, int maxout)
+{
+    const flzuint8 *ip = (const flzuint8 *)input;
+    const flzuint8 *ip_limit = ip + length;
+    flzuint8 *op = (flzuint8 *)output;
+    flzuint8 *op_limit = op + maxout;
+    flzuint32 ctrl = (*ip++) & 31;
+    int loop = 1;
+
+    do {
+        const flzuint8 *ref = op;
+        flzuint32 len = ctrl >> 5;
+        flzuint32 ofs = (ctrl & 31) << 8;
+
+        if (ctrl >= 32) {
+#if FASTLZ_LEVEL == 2
+            flzuint8 code;
+#endif
+            len--;
+            ref -= ofs;
+            if (len == 7 - 1)
+#if FASTLZ_LEVEL == 1
+                len += *ip++;
+            ref -= *ip++;
+#else
+                do {
+                    code = *ip++;
+                    len += code;
+                } while (code == 255);
+            code = *ip++;
+            ref -= code;
+
+            /* match from 16-bit distance */
+            if (FASTLZ_UNEXPECT_CONDITIONAL(code == 255))
+                if (FASTLZ_EXPECT_CONDITIONAL(ofs == (31 << 8))) {
+                    ofs = (*ip++) << 8;
+                    ofs += *ip++;
+                    ref = op - ofs - MAX_DISTANCE;
+                }
+#endif
+
+#ifdef FASTLZ_SAFE
+            if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit))
+                return 0;
+
+            if (FASTLZ_UNEXPECT_CONDITIONAL(ref - 1 < (flzuint8 *)output))
+                return 0;
+#endif
+
+            if (FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit))
+                ctrl = *ip++;
+            else
+                loop = 0;
+
+            if (ref == op) {
+                /* optimize copy for a run */
+                flzuint8 b = ref[-1];
+                *op++ = b;
+                *op++ = b;
+                *op++ = b;
+                for (; len; --len)
+                    *op++ = b;
+            } else {
+#if !defined(FASTLZ_STRICT_ALIGN)
+                const flzuint16 *p;
+                flzuint16 *q;
+#endif
+                /* copy from reference */
+                ref--;
+                *op++ = *ref++;
+                *op++ = *ref++;
+                *op++ = *ref++;
+
+#if !defined(FASTLZ_STRICT_ALIGN)
+                /* copy a byte, so that now it's word aligned */
+                if (len & 1) {
+                    *op++ = *ref++;
+                    len--;
+                }
+
+                /* copy 16-bit at once */
+                q = (flzuint16 *)op;
+                op += len;
+                p = (const flzuint16 *)ref;
+                for (len >>= 1; len > 4; len -= 4) {
+                    *q++ = *p++;
+                    *q++ = *p++;
+                    *q++ = *p++;
+                    *q++ = *p++;
+                }
+                for (; len; --len)
+                    *q++ = *p++;
+#else
+                for (; len; --len)
+                    *op++ = *ref++;
+#endif
+            }
+        } else {
+            ctrl++;
+#ifdef FASTLZ_SAFE
+            if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit))
+                return 0;
+            if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit))
+                return 0;
+#endif
+
+            *op++ = *ip++;
+            for (--ctrl; ctrl; ctrl--)
+                *op++ = *ip++;
+
+            loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit);
+            if (loop)
+                ctrl = *ip++;
+        }
+    } while (FASTLZ_EXPECT_CONDITIONAL(loop));
+
+    return op - (flzuint8 *)output;
+}
+
+#endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */
diff --git a/libs/fst/fastlz.h b/libs/fst/fastlz.h
new file mode 100644 (file)
index 0000000..1ce44a3
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+  FastLZ - lightning-fast lossless compression library
+
+  Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
+  Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
+  Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+
+  SPDX-License-Identifier: MIT
+*/
+
+#ifndef FASTLZ_H
+#define FASTLZ_H
+
+#include <inttypes.h>
+
+#define flzuint8 uint8_t
+#define flzuint16 uint16_t
+#define flzuint32 uint32_t
+
+
+#define FASTLZ_VERSION 0x000100
+
+#define FASTLZ_VERSION_MAJOR     0
+#define FASTLZ_VERSION_MINOR     0
+#define FASTLZ_VERSION_REVISION  0
+
+#define FASTLZ_VERSION_STRING "0.1.0"
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/**
+  Compress a block of data in the input buffer and returns the size of
+  compressed block. The size of input buffer is specified by length. The
+  minimum input buffer size is 16.
+
+  The output buffer must be at least 5% larger than the input buffer
+  and can not be smaller than 66 bytes.
+
+  If the input is not compressible, the return value might be larger than
+  length (input buffer size).
+
+  The input buffer and the output buffer can not overlap.
+*/
+
+int fastlz_compress(const void* input, int length, void* output);
+
+/**
+  Decompress a block of compressed data and returns the size of the
+  decompressed block. If error occurs, e.g. the compressed data is
+  corrupted or the output buffer is not large enough, then 0 (zero)
+  will be returned instead.
+
+  The input buffer and the output buffer can not overlap.
+
+  Decompression is memory safe and guaranteed not to write the output buffer
+  more than what is specified in maxout.
+ */
+
+int fastlz_decompress(const void* input, int length, void* output, int maxout);
+
+/**
+  Compress a block of data in the input buffer and returns the size of
+  compressed block. The size of input buffer is specified by length. The
+  minimum input buffer size is 16.
+
+  The output buffer must be at least 5% larger than the input buffer
+  and can not be smaller than 66 bytes.
+
+  If the input is not compressible, the return value might be larger than
+  length (input buffer size).
+
+  The input buffer and the output buffer can not overlap.
+
+  Compression level can be specified in parameter level. At the moment,
+  only level 1 and level 2 are supported.
+  Level 1 is the fastest compression and generally useful for short data.
+  Level 2 is slightly slower but it gives better compression ratio.
+
+  Note that the compressed data, regardless of the level, can always be
+  decompressed using the function fastlz_decompress above.
+*/
+
+int fastlz_compress_level(int level, const void* input, int length, void* output);
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* FASTLZ_H */
diff --git a/libs/fst/fst_win_unistd.h b/libs/fst/fst_win_unistd.h
new file mode 100644 (file)
index 0000000..09539e5
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009-2018 Tony Bybell.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef WIN_UNISTD_H
+#define WIN_UNISTD_H
+
+#include <stdlib.h>
+#ifdef _WIN64
+#include <io.h>
+#else
+#include <sys/io.h>
+#endif
+
+#include <process.h>
+
+#define ftruncate _chsize_s
+#define unlink _unlink
+#define fileno _fileno
+#define lseek _lseeki64
+
+#ifdef _WIN64
+#define ssize_t __int64
+#define SSIZE_MAX 9223372036854775807i64
+#else
+#define ssize_t long
+#define SSIZE_MAX 2147483647L
+#endif
+
+#include "stdint.h"
+
+#endif // WIN_UNISTD_H
diff --git a/libs/fst/fstapi.cc b/libs/fst/fstapi.cc
new file mode 100644 (file)
index 0000000..3ceafb1
--- /dev/null
@@ -0,0 +1,6537 @@
+/*
+ * Copyright (c) 2009-2018 Tony Bybell.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+/*
+ * possible disables:
+ *
+ * FST_DYNAMIC_ALIAS_DISABLE : dynamic aliases are not processed
+ * FST_DYNAMIC_ALIAS2_DISABLE : new encoding for dynamic aliases is not generated
+ * FST_WRITEX_DISABLE : fast write I/O routines are disabled
+ *
+ * possible enables:
+ *
+ * FST_DEBUG : not for production use, only enable for development
+ * FST_REMOVE_DUPLICATE_VC : glitch removal (has writer performance impact)
+ * HAVE_LIBPTHREAD -> FST_WRITER_PARALLEL : enables inclusion of parallel writer code
+ * FST_DO_MISALIGNED_OPS (defined automatically for x86 and some others) : CPU architecture can handle misaligned
+ * loads/stores _WAVE_HAVE_JUDY : use Judy arrays instead of Jenkins (undefine if LGPL is not acceptable)
+ *
+ */
+
+#ifndef FST_CONFIG_INCLUDE
+#define FST_CONFIG_INCLUDE "config.h"
+#endif
+#include FST_CONFIG_INCLUDE
+
+#include "fstapi.h"
+#include "fastlz.h"
+#include "lz4.h"
+#include <errno.h>
+
+#ifndef HAVE_LIBPTHREAD
+#undef FST_WRITER_PARALLEL
+#endif
+
+#ifdef FST_WRITER_PARALLEL
+#include <pthread.h>
+#endif
+
+#ifdef __MINGW32__
+#include <windows.h>
+#endif
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#elif defined(__GNUC__)
+#ifndef __MINGW32__
+#ifndef alloca
+#define alloca __builtin_alloca
+#endif
+#else
+#include <malloc.h>
+#endif
+#elif defined(_MSC_VER)
+#include <malloc.h>
+#define alloca _alloca
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX (4096)
+#endif
+
+#if defined(_MSC_VER)
+typedef int64_t fst_off_t;
+#else
+typedef off_t fst_off_t;
+#endif
+
+/* note that Judy versus Jenkins requires more experimentation: they are  */
+/* functionally equivalent though it appears Jenkins is slightly faster.  */
+/* in addition, Jenkins is not bound by the LGPL.                         */
+#ifdef _WAVE_HAVE_JUDY
+#include <Judy.h>
+#else
+/* should be more than enough for fstWriterSetSourceStem() */
+#define FST_PATH_HASHMASK ((1UL << 16) - 1)
+typedef const void *Pcvoid_t;
+typedef void *Pvoid_t;
+typedef void **PPvoid_t;
+#define JudyHSIns(a, b, c, d) JenkinsIns((a), (b), (c), (hashmask))
+#define JudyHSFreeArray(a, b) JenkinsFree((a), (hashmask))
+void JenkinsFree(void *base_i, uint32_t hashmask);
+void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint32_t hashmask);
+#endif
+
+#ifndef FST_WRITEX_DISABLE
+#define FST_WRITEX_MAX (64 * 1024)
+#else
+#define fstWritex(a, b, c) fstFwrite((b), (c), 1, fv)
+#endif
+
+/* these defines have a large impact on writer speed when a model has a */
+/* huge number of symbols.  as a default, use 128MB and increment when  */
+/* every 1M signals are defined.                                        */
+#define FST_BREAK_SIZE (1UL << 27)
+#define FST_BREAK_ADD_SIZE (1UL << 22)
+#define FST_BREAK_SIZE_MAX (1UL << 31)
+#define FST_ACTIVATE_HUGE_BREAK (1000000)
+#define FST_ACTIVATE_HUGE_INC (1000000)
+
+#define FST_WRITER_STR "fstWriter"
+#define FST_ID_NAM_SIZ (512)
+#define FST_ID_NAM_ATTR_SIZ (65536 + 4096)
+#define FST_DOUBLE_ENDTEST (2.7182818284590452354)
+#define FST_HDR_SIM_VERSION_SIZE (128)
+#define FST_HDR_DATE_SIZE (119)
+#define FST_HDR_FILETYPE_SIZE (1)
+#define FST_HDR_TIMEZERO_SIZE (8)
+#define FST_GZIO_LEN (32768)
+#define FST_HDR_FOURPACK_DUO_SIZE (4 * 1024 * 1024)
+
+#if defined(__i386__) || defined(__x86_64__) || defined(_AIX)
+#define FST_DO_MISALIGNED_OPS
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define FST_MACOSX
+#include <sys/sysctl.h>
+#endif
+
+#ifdef __GNUC__
+/* Boolean expression more often true than false */
+#define FST_LIKELY(x) __builtin_expect(!!(x), 1)
+/* Boolean expression more often false than true */
+#define FST_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define FST_LIKELY(x) (!!(x))
+#define FST_UNLIKELY(x) (!!(x))
+#endif
+
+#define FST_APIMESS "FSTAPI  | "
+
+/***********************/
+/***                 ***/
+/*** common function ***/
+/***                 ***/
+/***********************/
+
+#ifdef __MINGW32__
+#include <io.h>
+#ifndef HAVE_FSEEKO
+#define ftello _ftelli64
+#define fseeko _fseeki64
+#endif
+#endif
+
+/*
+ * the recoded "extra" values...
+ * note that FST_RCV_Q is currently unused and is for future expansion.
+ * its intended use is as another level of escape such that any arbitrary
+ * value can be stored as the value: { time_delta, 8 bits, FST_RCV_Q }.
+ * this is currently not implemented so that the branchless decode is:
+ * uint32_t shcnt = 2 << (vli & 1); tdelta = vli >> shcnt;
+ */
+#define FST_RCV_X (1 | (0 << 1))
+#define FST_RCV_Z (1 | (1 << 1))
+#define FST_RCV_H (1 | (2 << 1))
+#define FST_RCV_U (1 | (3 << 1))
+#define FST_RCV_W (1 | (4 << 1))
+#define FST_RCV_L (1 | (5 << 1))
+#define FST_RCV_D (1 | (6 << 1))
+#define FST_RCV_Q (1 | (7 << 1))
+
+#define FST_RCV_STR "xzhuwl-?"
+/*                   01234567 */
+
+/*
+ * prevent old file overwrite when currently being read
+ */
+static FILE *unlink_fopen(const char *nam, const char *mode)
+{
+    unlink(nam);
+    return (fopen(nam, mode));
+}
+
+/*
+ * system-specific temp file handling
+ */
+#ifdef __MINGW32__
+
+static FILE *tmpfile_open(char **nam)
+{
+    char *fname = NULL;
+    TCHAR szTempFileName[MAX_PATH];
+    TCHAR lpTempPathBuffer[MAX_PATH];
+    DWORD dwRetVal = 0;
+    UINT uRetVal = 0;
+    FILE *fh = NULL;
+
+    if (nam) /* cppcheck warning fix: nam is always defined, so this is not needed */
+    {
+        dwRetVal = GetTempPath(MAX_PATH, lpTempPathBuffer);
+        if ((dwRetVal > MAX_PATH) || (dwRetVal == 0)) {
+            fprintf(stderr, FST_APIMESS "GetTempPath() failed in " __FILE__ " line %d, exiting.\n", __LINE__);
+            exit(255);
+        } else {
+            uRetVal = GetTempFileName(lpTempPathBuffer, TEXT("FSTW"), 0, szTempFileName);
+            if (uRetVal == 0) {
+                fprintf(stderr, FST_APIMESS "GetTempFileName() failed in " __FILE__ " line %d, exiting.\n", __LINE__);
+                exit(255);
+            } else {
+                fname = strdup(szTempFileName);
+            }
+        }
+
+        if (fname) {
+            *nam = fname;
+            fh = unlink_fopen(fname, "w+b");
+        }
+    }
+
+    return (fh);
+}
+
+#else
+
+static FILE *tmpfile_open(char **nam)
+{
+    FILE *f = tmpfile(); /* replace with mkstemp() + fopen(), etc if this is not good enough */
+    if (nam) {
+        *nam = NULL;
+    }
+    return (f);
+}
+
+#endif
+
+static void tmpfile_close(FILE **f, char **nam)
+{
+    if (f) {
+        if (*f) {
+            fclose(*f);
+            *f = NULL;
+        }
+    }
+
+    if (nam) {
+        if (*nam) {
+            unlink(*nam);
+            free(*nam);
+            *nam = NULL;
+        }
+    }
+}
+
+/*****************************************/
+
+/*
+ * to remove warn_unused_result compile time messages
+ * (in the future there needs to be results checking)
+ */
+static size_t fstFread(void *buf, size_t siz, size_t cnt, FILE *fp) { return (fread(buf, siz, cnt, fp)); }
+
+static size_t fstFwrite(const void *buf, size_t siz, size_t cnt, FILE *fp) { return (fwrite(buf, siz, cnt, fp)); }
+
+static int fstFtruncate(int fd, fst_off_t length) { return (ftruncate(fd, length)); }
+
+/*
+ * realpath compatibility
+ */
+static char *fstRealpath(const char *path, char *resolved_path)
+{
+#if defined __USE_BSD || defined __USE_XOPEN_EXTENDED || defined __CYGWIN__ || defined HAVE_REALPATH
+#if (defined(__MACH__) && defined(__APPLE__))
+    if (!resolved_path) {
+        resolved_path = (char *)malloc(PATH_MAX + 1); /* fixes bug on Leopard when resolved_path == NULL */
+    }
+#endif
+
+    return (realpath(path, resolved_path));
+
+#else
+#ifdef __MINGW32__
+    if (!resolved_path) {
+        resolved_path = (char *)malloc(PATH_MAX + 1);
+    }
+    return (_fullpath(resolved_path, path, PATH_MAX));
+#else
+    (void)path;
+    (void)resolved_path;
+    return (NULL);
+#endif
+#endif
+}
+
+/*
+ * mmap compatibility
+ */
+#if defined __CYGWIN__ || defined __MINGW32__
+#include <limits.h>
+#define fstMmap(__addr, __len, __prot, __flags, __fd, __off) fstMmap2((__len), (__fd), (__off))
+#define fstMunmap(__addr, __len) free(__addr)
+
+static void *fstMmap2(size_t __len, int __fd, fst_off_t __off)
+{
+    (void)__off;
+
+    unsigned char *pnt = (unsigned char *)malloc(__len);
+    fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
+    size_t i;
+
+    lseek(__fd, 0, SEEK_SET);
+    for (i = 0; i < __len; i += SSIZE_MAX) {
+        read(__fd, pnt + i, ((__len - i) >= SSIZE_MAX) ? SSIZE_MAX : (__len - i));
+    }
+    lseek(__fd, cur_offs, SEEK_SET);
+    return (pnt);
+}
+#else
+#include <sys/mman.h>
+#if defined(__SUNPRO_C)
+#define FST_CADDR_T_CAST (caddr_t)
+#else
+#define FST_CADDR_T_CAST
+#endif
+#define fstMmap(__addr, __len, __prot, __flags, __fd, __off)                                                           \
+    (void *)mmap(FST_CADDR_T_CAST(__addr), (__len), (__prot), (__flags), (__fd), (__off))
+#define fstMunmap(__addr, __len)                                                                                       \
+    {                                                                                                                  \
+        if (__addr)                                                                                                    \
+            munmap(FST_CADDR_T_CAST(__addr), (__len));                                                                 \
+    }
+#endif
+
+/*
+ * regular and variable-length integer access functions
+ */
+#ifdef FST_DO_MISALIGNED_OPS
+#define fstGetUint32(x) (*(uint32_t *)(x))
+#else
+static uint32_t fstGetUint32(unsigned char *mem)
+{
+    uint32_t u32;
+    unsigned char *buf = (unsigned char *)(&u32);
+
+    buf[0] = mem[0];
+    buf[1] = mem[1];
+    buf[2] = mem[2];
+    buf[3] = mem[3];
+
+    return (*(uint32_t *)buf);
+}
+#endif
+
+static int fstWriterUint64(FILE *handle, uint64_t v)
+{
+    unsigned char buf[8];
+    int i;
+
+    for (i = 7; i >= 0; i--) {
+        buf[i] = v & 0xff;
+        v >>= 8;
+    }
+
+    fstFwrite(buf, 8, 1, handle);
+    return (8);
+}
+
+static uint64_t fstReaderUint64(FILE *f)
+{
+    uint64_t val = 0;
+    unsigned char buf[sizeof(uint64_t)];
+    unsigned int i;
+
+    fstFread(buf, sizeof(uint64_t), 1, f);
+    for (i = 0; i < sizeof(uint64_t); i++) {
+        val <<= 8;
+        val |= buf[i];
+    }
+
+    return (val);
+}
+
+static uint32_t fstGetVarint32(unsigned char *mem, int *skiplen)
+{
+    unsigned char *mem_orig = mem;
+    uint32_t rc = 0;
+    while (*mem & 0x80) {
+        mem++;
+    }
+
+    *skiplen = mem - mem_orig + 1;
+    for (;;) {
+        rc <<= 7;
+        rc |= (uint32_t)(*mem & 0x7f);
+        if (mem == mem_orig) {
+            break;
+        }
+        mem--;
+    }
+
+    return (rc);
+}
+
+static uint32_t fstGetVarint32Length(unsigned char *mem)
+{
+    unsigned char *mem_orig = mem;
+
+    while (*mem & 0x80) {
+        mem++;
+    }
+
+    return (mem - mem_orig + 1);
+}
+
+static uint32_t fstGetVarint32NoSkip(unsigned char *mem)
+{
+    unsigned char *mem_orig = mem;
+    uint32_t rc = 0;
+    while (*mem & 0x80) {
+        mem++;
+    }
+
+    for (;;) {
+        rc <<= 7;
+        rc |= (uint32_t)(*mem & 0x7f);
+        if (mem == mem_orig) {
+            break;
+        }
+        mem--;
+    }
+
+    return (rc);
+}
+
+static unsigned char *fstCopyVarint32ToLeft(unsigned char *pnt, uint32_t v)
+{
+    unsigned char *spnt;
+    uint32_t nxt = v;
+    int cnt = 1;
+    int i;
+
+    while ((nxt = nxt >> 7)) /* determine len to avoid temp buffer copying to cut down on load-hit-store */
+    {
+        cnt++;
+    }
+
+    pnt -= cnt;
+    spnt = pnt;
+    cnt--;
+
+    for (i = 0; i < cnt; i++) /* now generate left to right as normal */
+    {
+        nxt = v >> 7;
+        *(spnt++) = ((unsigned char)v) | 0x80;
+        v = nxt;
+    }
+    *spnt = (unsigned char)v;
+
+    return (pnt);
+}
+
+static unsigned char *fstCopyVarint64ToRight(unsigned char *pnt, uint64_t v)
+{
+    uint64_t nxt;
+
+    while ((nxt = v >> 7)) {
+        *(pnt++) = ((unsigned char)v) | 0x80;
+        v = nxt;
+    }
+    *(pnt++) = (unsigned char)v;
+
+    return (pnt);
+}
+
+static uint64_t fstGetVarint64(unsigned char *mem, int *skiplen)
+{
+    unsigned char *mem_orig = mem;
+    uint64_t rc = 0;
+    while (*mem & 0x80) {
+        mem++;
+    }
+
+    *skiplen = mem - mem_orig + 1;
+    for (;;) {
+        rc <<= 7;
+        rc |= (uint64_t)(*mem & 0x7f);
+        if (mem == mem_orig) {
+            break;
+        }
+        mem--;
+    }
+
+    return (rc);
+}
+
+static uint32_t fstReaderVarint32(FILE *f)
+{
+    unsigned char buf[5];
+    unsigned char *mem = buf;
+    uint32_t rc = 0;
+    int ch;
+
+    do {
+        ch = fgetc(f);
+        *(mem++) = ch;
+    } while (ch & 0x80);
+    mem--;
+
+    for (;;) {
+        rc <<= 7;
+        rc |= (uint32_t)(*mem & 0x7f);
+        if (mem == buf) {
+            break;
+        }
+        mem--;
+    }
+
+    return (rc);
+}
+
+static uint32_t fstReaderVarint32WithSkip(FILE *f, uint32_t *skiplen)
+{
+    unsigned char buf[5];
+    unsigned char *mem = buf;
+    uint32_t rc = 0;
+    int ch;
+
+    do {
+        ch = fgetc(f);
+        *(mem++) = ch;
+    } while (ch & 0x80);
+    *skiplen = mem - buf;
+    mem--;
+
+    for (;;) {
+        rc <<= 7;
+        rc |= (uint32_t)(*mem & 0x7f);
+        if (mem == buf) {
+            break;
+        }
+        mem--;
+    }
+
+    return (rc);
+}
+
+static uint64_t fstReaderVarint64(FILE *f)
+{
+    unsigned char buf[16];
+    unsigned char *mem = buf;
+    uint64_t rc = 0;
+    int ch;
+
+    do {
+        ch = fgetc(f);
+        *(mem++) = ch;
+    } while (ch & 0x80);
+    mem--;
+
+    for (;;) {
+        rc <<= 7;
+        rc |= (uint64_t)(*mem & 0x7f);
+        if (mem == buf) {
+            break;
+        }
+        mem--;
+    }
+
+    return (rc);
+}
+
+static int fstWriterVarint(FILE *handle, uint64_t v)
+{
+    uint64_t nxt;
+    unsigned char buf[10]; /* ceil(64/7) = 10 */
+    unsigned char *pnt = buf;
+    int len;
+
+    while ((nxt = v >> 7)) {
+        *(pnt++) = ((unsigned char)v) | 0x80;
+        v = nxt;
+    }
+    *(pnt++) = (unsigned char)v;
+
+    len = pnt - buf;
+    fstFwrite(buf, len, 1, handle);
+    return (len);
+}
+
+/* signed integer read/write routines are currently unused */
+static int64_t fstGetSVarint64(unsigned char *mem, int *skiplen)
+{
+    unsigned char *mem_orig = mem;
+    int64_t rc = 0;
+    const int64_t one = 1;
+    const int siz = sizeof(int64_t) * 8;
+    int shift = 0;
+    unsigned char byt;
+
+    do {
+        byt = *(mem++);
+        rc |= ((int64_t)(byt & 0x7f)) << shift;
+        shift += 7;
+
+    } while (byt & 0x80);
+
+    if ((shift < siz) && (byt & 0x40)) {
+        rc |= -(one << shift); /* sign extend */
+    }
+
+    *skiplen = mem - mem_orig;
+
+    return (rc);
+}
+
+#ifndef FST_DYNAMIC_ALIAS2_DISABLE
+static int fstWriterSVarint(FILE *handle, int64_t v)
+{
+    unsigned char buf[15]; /* ceil(64/7) = 10 + sign byte padded way up */
+    unsigned char byt;
+    unsigned char *pnt = buf;
+    int more = 1;
+    int len;
+
+    do {
+        byt = v | 0x80;
+        v >>= 7;
+
+        if (((!v) && (!(byt & 0x40))) || ((v == -1) && (byt & 0x40))) {
+            more = 0;
+            byt &= 0x7f;
+        }
+
+        *(pnt++) = byt;
+    } while (more);
+
+    len = pnt - buf;
+    fstFwrite(buf, len, 1, handle);
+    return (len);
+}
+#endif
+
+/***********************/
+/***                 ***/
+/*** writer function ***/
+/***                 ***/
+/***********************/
+
+/*
+ * private structs
+ */
+struct fstBlackoutChain
+{
+    struct fstBlackoutChain *next;
+    uint64_t tim;
+    unsigned active : 1;
+};
+
+struct fstWriterContext
+{
+    FILE *handle;
+    FILE *hier_handle;
+    FILE *geom_handle;
+    FILE *valpos_handle;
+    FILE *curval_handle;
+    FILE *tchn_handle;
+
+    unsigned char *vchg_mem;
+
+    fst_off_t hier_file_len;
+
+    uint32_t *valpos_mem;
+    unsigned char *curval_mem;
+
+    unsigned char *outval_mem; /* for two-state / Verilator-style value changes */
+    uint32_t outval_alloc_siz;
+
+    char *filename;
+
+    fstHandle maxhandle;
+    fstHandle numsigs;
+    uint32_t maxvalpos;
+
+    unsigned vc_emitted : 1;
+    unsigned is_initial_time : 1;
+    unsigned fourpack : 1;
+    unsigned fastpack : 1;
+
+    int64_t timezero;
+    fst_off_t section_header_truncpos;
+    uint32_t tchn_cnt, tchn_idx;
+    uint64_t curtime;
+    uint64_t firsttime;
+    uint32_t vchg_siz;
+    uint32_t vchg_alloc_siz;
+
+    uint32_t secnum;
+    fst_off_t section_start;
+
+    uint32_t numscopes;
+    double nan; /* nan value for uninitialized doubles */
+
+    struct fstBlackoutChain *blackout_head;
+    struct fstBlackoutChain *blackout_curr;
+    uint32_t num_blackouts;
+
+    uint64_t dump_size_limit;
+
+    unsigned char filetype; /* default is 0, FST_FT_VERILOG */
+
+    unsigned compress_hier : 1;
+    unsigned repack_on_close : 1;
+    unsigned skip_writing_section_hdr : 1;
+    unsigned size_limit_locked : 1;
+    unsigned section_header_only : 1;
+    unsigned flush_context_pending : 1;
+    unsigned parallel_enabled : 1;
+    unsigned parallel_was_enabled : 1;
+
+    /* should really be semaphores, but are bytes to cut down on read-modify-write window size */
+    unsigned char already_in_flush; /* in case control-c handlers interrupt */
+    unsigned char already_in_close; /* in case control-c handlers interrupt */
+
+#ifdef FST_WRITER_PARALLEL
+    pthread_mutex_t mutex;
+    pthread_t thread;
+    pthread_attr_t thread_attr;
+    struct fstWriterContext *xc_parent;
+#endif
+    unsigned in_pthread : 1;
+
+    size_t fst_orig_break_size;
+    size_t fst_orig_break_add_size;
+
+    size_t fst_break_size;
+    size_t fst_break_add_size;
+
+    size_t fst_huge_break_size;
+
+    fstHandle next_huge_break;
+
+    Pvoid_t path_array;
+    uint32_t path_array_count;
+
+    unsigned fseek_failed : 1;
+
+    char *geom_handle_nam;
+    char *valpos_handle_nam;
+    char *curval_handle_nam;
+    char *tchn_handle_nam;
+
+    fstEnumHandle max_enumhandle;
+};
+
+static int fstWriterFseeko(struct fstWriterContext *xc, FILE *stream, fst_off_t offset, int whence)
+{
+    int rc = fseeko(stream, offset, whence);
+
+    if (rc < 0) {
+        xc->fseek_failed = 1;
+#ifdef FST_DEBUG
+        fprintf(stderr, FST_APIMESS "Seek to #%" PRId64 " (whence = %d) failed!\n", offset, whence);
+        perror("Why");
+#endif
+    }
+
+    return (rc);
+}
+
+static uint32_t fstWriterUint32WithVarint32(struct fstWriterContext *xc, uint32_t *u, uint32_t v, const void *dbuf,
+                                            uint32_t siz)
+{
+    unsigned char *buf = xc->vchg_mem + xc->vchg_siz;
+    unsigned char *pnt = buf;
+    uint32_t nxt;
+    uint32_t len;
+
+#ifdef FST_DO_MISALIGNED_OPS
+    (*(uint32_t *)(pnt)) = (*(uint32_t *)(u));
+#else
+    memcpy(pnt, u, sizeof(uint32_t));
+#endif
+    pnt += 4;
+
+    while ((nxt = v >> 7)) {
+        *(pnt++) = ((unsigned char)v) | 0x80;
+        v = nxt;
+    }
+    *(pnt++) = (unsigned char)v;
+    memcpy(pnt, dbuf, siz);
+
+    len = pnt - buf + siz;
+    return (len);
+}
+
+static uint32_t fstWriterUint32WithVarint32AndLength(struct fstWriterContext *xc, uint32_t *u, uint32_t v,
+                                                     const void *dbuf, uint32_t siz)
+{
+    unsigned char *buf = xc->vchg_mem + xc->vchg_siz;
+    unsigned char *pnt = buf;
+    uint32_t nxt;
+    uint32_t len;
+
+#ifdef FST_DO_MISALIGNED_OPS
+    (*(uint32_t *)(pnt)) = (*(uint32_t *)(u));
+#else
+    memcpy(pnt, u, sizeof(uint32_t));
+#endif
+    pnt += 4;
+
+    while ((nxt = v >> 7)) {
+        *(pnt++) = ((unsigned char)v) | 0x80;
+        v = nxt;
+    }
+    *(pnt++) = (unsigned char)v;
+
+    v = siz;
+    while ((nxt = v >> 7)) {
+        *(pnt++) = ((unsigned char)v) | 0x80;
+        v = nxt;
+    }
+    *(pnt++) = (unsigned char)v;
+
+    memcpy(pnt, dbuf, siz);
+
+    len = pnt - buf + siz;
+    return (len);
+}
+
+/*
+ * header bytes, write here so defines are set up before anything else
+ * that needs to use them
+ */
+static void fstWriterEmitHdrBytes(struct fstWriterContext *xc)
+{
+    char vbuf[FST_HDR_SIM_VERSION_SIZE];
+    char dbuf[FST_HDR_DATE_SIZE];
+    double endtest = FST_DOUBLE_ENDTEST;
+    time_t walltime;
+
+#define FST_HDR_OFFS_TAG (0)
+    fputc(FST_BL_HDR, xc->handle); /* +0 tag */
+
+#define FST_HDR_OFFS_SECLEN (FST_HDR_OFFS_TAG + 1)
+    fstWriterUint64(xc->handle, 329); /* +1 section length */
+
+#define FST_HDR_OFFS_START_TIME (FST_HDR_OFFS_SECLEN + 8)
+    fstWriterUint64(xc->handle, 0); /* +9 start time */
+
+#define FST_HDR_OFFS_END_TIME (FST_HDR_OFFS_START_TIME + 8)
+    fstWriterUint64(xc->handle, 0); /* +17 end time */
+
+#define FST_HDR_OFFS_ENDIAN_TEST (FST_HDR_OFFS_END_TIME + 8)
+    fstFwrite(&endtest, 8, 1, xc->handle); /* +25 endian test for reals */
+
+#define FST_HDR_OFFS_MEM_USED (FST_HDR_OFFS_ENDIAN_TEST + 8)
+    fstWriterUint64(xc->handle, xc->fst_break_size); /* +33 memory used by writer */
+
+#define FST_HDR_OFFS_NUM_SCOPES (FST_HDR_OFFS_MEM_USED + 8)
+    fstWriterUint64(xc->handle, 0); /* +41 scope creation count */
+
+#define FST_HDR_OFFS_NUM_VARS (FST_HDR_OFFS_NUM_SCOPES + 8)
+    fstWriterUint64(xc->handle, 0); /* +49 var creation count */
+
+#define FST_HDR_OFFS_MAXHANDLE (FST_HDR_OFFS_NUM_VARS + 8)
+    fstWriterUint64(xc->handle, 0); /* +57 max var idcode */
+
+#define FST_HDR_OFFS_SECTION_CNT (FST_HDR_OFFS_MAXHANDLE + 8)
+    fstWriterUint64(xc->handle, 0); /* +65 vc section count */
+
+#define FST_HDR_OFFS_TIMESCALE (FST_HDR_OFFS_SECTION_CNT + 8)
+    fputc((-9) & 255, xc->handle); /* +73 timescale 1ns */
+
+#define FST_HDR_OFFS_SIM_VERSION (FST_HDR_OFFS_TIMESCALE + 1)
+    memset(vbuf, 0, FST_HDR_SIM_VERSION_SIZE);
+    strcpy(vbuf, FST_WRITER_STR);
+    fstFwrite(vbuf, FST_HDR_SIM_VERSION_SIZE, 1, xc->handle); /* +74 version */
+
+#define FST_HDR_OFFS_DATE (FST_HDR_OFFS_SIM_VERSION + FST_HDR_SIM_VERSION_SIZE)
+    memset(dbuf, 0, FST_HDR_DATE_SIZE);
+    time(&walltime);
+    strcpy(dbuf, asctime(localtime(&walltime)));
+    fstFwrite(dbuf, FST_HDR_DATE_SIZE, 1, xc->handle); /* +202 date */
+
+    /* date size is deliberately overspecified at 119 bytes (originally 128) in order to provide backfill for new args
+     */
+
+#define FST_HDR_OFFS_FILETYPE (FST_HDR_OFFS_DATE + FST_HDR_DATE_SIZE)
+    fputc(xc->filetype, xc->handle); /* +321 filetype */
+
+#define FST_HDR_OFFS_TIMEZERO (FST_HDR_OFFS_FILETYPE + FST_HDR_FILETYPE_SIZE)
+    fstWriterUint64(xc->handle, xc->timezero); /* +322 timezero */
+
+#define FST_HDR_LENGTH (FST_HDR_OFFS_TIMEZERO + FST_HDR_TIMEZERO_SIZE)
+    /* +330 next section starts here */
+    fflush(xc->handle);
+}
+
+/*
+ * mmap functions
+ */
+static void fstWriterMmapSanity(void *pnt, const char *file, int line, const char *usage)
+{
+#if !defined(__CYGWIN__) && !defined(__MINGW32__)
+    if (pnt == MAP_FAILED) {
+        fprintf(stderr, "fstMmap() assigned to %s failed: errno: %d, file %s, line %d.\n", usage, errno, file, line);
+        perror("Why");
+        pnt = NULL;
+    }
+#endif
+}
+
+static void fstWriterCreateMmaps(struct fstWriterContext *xc)
+{
+    fst_off_t curpos = ftello(xc->handle);
+
+    fflush(xc->hier_handle);
+
+    /* write out intermediate header */
+    fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_START_TIME, SEEK_SET);
+    fstWriterUint64(xc->handle, xc->firsttime);
+    fstWriterUint64(xc->handle, xc->curtime);
+    fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_NUM_SCOPES, SEEK_SET);
+    fstWriterUint64(xc->handle, xc->numscopes);
+    fstWriterUint64(xc->handle, xc->numsigs);
+    fstWriterUint64(xc->handle, xc->maxhandle);
+    fstWriterUint64(xc->handle, xc->secnum);
+    fstWriterFseeko(xc, xc->handle, curpos, SEEK_SET);
+    fflush(xc->handle);
+
+    /* do mappings */
+    if (!xc->valpos_mem) {
+        fflush(xc->valpos_handle);
+        errno = 0;
+        if (xc->maxhandle) {
+            fstWriterMmapSanity(xc->valpos_mem = (uint32_t *)fstMmap(NULL, xc->maxhandle * 4 * sizeof(uint32_t),
+                                                                     PROT_READ | PROT_WRITE, MAP_SHARED,
+                                                                     fileno(xc->valpos_handle), 0),
+                                __FILE__, __LINE__, "xc->valpos_mem");
+        }
+    }
+    if (!xc->curval_mem) {
+        fflush(xc->curval_handle);
+        errno = 0;
+        if (xc->maxvalpos) {
+            fstWriterMmapSanity(xc->curval_mem = (unsigned char *)fstMmap(NULL, xc->maxvalpos, PROT_READ | PROT_WRITE,
+                                                                          MAP_SHARED, fileno(xc->curval_handle), 0),
+                                __FILE__, __LINE__, "xc->curval_handle");
+        }
+    }
+}
+
+static void fstDestroyMmaps(struct fstWriterContext *xc, int is_closing)
+{
+#if !defined __CYGWIN__ && !defined __MINGW32__
+    (void)is_closing;
+#endif
+
+    fstMunmap(xc->valpos_mem, xc->maxhandle * 4 * sizeof(uint32_t));
+    xc->valpos_mem = NULL;
+
+#if defined __CYGWIN__ || defined __MINGW32__
+    if (xc->curval_mem) {
+        if (!is_closing) /* need to flush out for next emulated mmap() read */
+        {
+            unsigned char *pnt = xc->curval_mem;
+            int __fd = fileno(xc->curval_handle);
+            fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
+            size_t i;
+            size_t __len = xc->maxvalpos;
+
+            lseek(__fd, 0, SEEK_SET);
+            for (i = 0; i < __len; i += SSIZE_MAX) {
+                write(__fd, pnt + i, ((__len - i) >= SSIZE_MAX) ? SSIZE_MAX : (__len - i));
+            }
+            lseek(__fd, cur_offs, SEEK_SET);
+        }
+    }
+#endif
+
+    fstMunmap(xc->curval_mem, xc->maxvalpos);
+    xc->curval_mem = NULL;
+}
+
+/*
+ * set up large and small memory usages
+ * crossover point in model is FST_ACTIVATE_HUGE_BREAK number of signals
+ */
+static void fstDetermineBreakSize(struct fstWriterContext *xc)
+{
+#if defined(__linux__) || defined(FST_MACOSX)
+    int was_set = 0;
+
+#ifdef __linux__
+    FILE *f = fopen("/proc/meminfo", "rb");
+
+    if (f) {
+        char buf[257];
+        char *s;
+        while (!feof(f)) {
+            buf[0] = 0;
+            s = fgets(buf, 256, f);
+            if (s && *s) {
+                if (!strncmp(s, "MemTotal:", 9)) {
+                    size_t v = atol(s + 10);
+                    v *= 1024; /* convert to bytes */
+                    v /= 8;    /* chop down to 1/8 physical memory */
+                    if (v > FST_BREAK_SIZE) {
+                        if (v > FST_BREAK_SIZE_MAX) {
+                            v = FST_BREAK_SIZE_MAX;
+                        }
+
+                        xc->fst_huge_break_size = v;
+                        was_set = 1;
+                        break;
+                    }
+                }
+            }
+        }
+
+        fclose(f);
+    }
+
+    if (!was_set) {
+        xc->fst_huge_break_size = FST_BREAK_SIZE;
+    }
+#else
+    int mib[2];
+    int64_t v;
+    size_t length;
+
+    mib[0] = CTL_HW;
+    mib[1] = HW_MEMSIZE;
+    length = sizeof(int64_t);
+    if (!sysctl(mib, 2, &v, &length, NULL, 0)) {
+        v /= 8;
+
+        if (v > (int64_t)FST_BREAK_SIZE) {
+            if (v > (int64_t)FST_BREAK_SIZE_MAX) {
+                v = FST_BREAK_SIZE_MAX;
+            }
+
+            xc->fst_huge_break_size = v;
+            was_set = 1;
+        }
+    }
+
+    if (!was_set) {
+        xc->fst_huge_break_size = FST_BREAK_SIZE;
+    }
+#endif
+#else
+    xc->fst_huge_break_size = FST_BREAK_SIZE;
+#endif
+
+    xc->fst_break_size = xc->fst_orig_break_size = FST_BREAK_SIZE;
+    xc->fst_break_add_size = xc->fst_orig_break_add_size = FST_BREAK_ADD_SIZE;
+    xc->next_huge_break = FST_ACTIVATE_HUGE_BREAK;
+}
+
+/*
+ * file creation and close
+ */
+void *fstWriterCreate(const char *nam, int use_compressed_hier)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)calloc(1, sizeof(struct fstWriterContext));
+
+    xc->compress_hier = use_compressed_hier;
+    fstDetermineBreakSize(xc);
+
+    if ((!nam) || (!(xc->handle = unlink_fopen(nam, "w+b")))) {
+        free(xc);
+        xc = NULL;
+    } else {
+        int flen = strlen(nam);
+        char *hf = (char *)calloc(1, flen + 6);
+
+        memcpy(hf, nam, flen);
+        strcpy(hf + flen, ".hier");
+        xc->hier_handle = unlink_fopen(hf, "w+b");
+
+        xc->geom_handle = tmpfile_open(&xc->geom_handle_nam);     /* .geom */
+        xc->valpos_handle = tmpfile_open(&xc->valpos_handle_nam); /* .offs */
+        xc->curval_handle = tmpfile_open(&xc->curval_handle_nam); /* .bits */
+        xc->tchn_handle = tmpfile_open(&xc->tchn_handle_nam);     /* .tchn */
+        xc->vchg_alloc_siz = xc->fst_break_size + xc->fst_break_add_size;
+        xc->vchg_mem = (unsigned char *)malloc(xc->vchg_alloc_siz);
+
+        if (xc->hier_handle && xc->geom_handle && xc->valpos_handle && xc->curval_handle && xc->vchg_mem &&
+            xc->tchn_handle) {
+            xc->filename = strdup(nam);
+            xc->is_initial_time = 1;
+
+            fstWriterEmitHdrBytes(xc);
+            xc->nan = strtod("NaN", NULL);
+#ifdef FST_WRITER_PARALLEL
+            pthread_mutex_init(&xc->mutex, NULL);
+            pthread_attr_init(&xc->thread_attr);
+            pthread_attr_setdetachstate(&xc->thread_attr, PTHREAD_CREATE_DETACHED);
+#endif
+        } else {
+            fclose(xc->handle);
+            if (xc->hier_handle) {
+                fclose(xc->hier_handle);
+                unlink(hf);
+            }
+            tmpfile_close(&xc->geom_handle, &xc->geom_handle_nam);
+            tmpfile_close(&xc->valpos_handle, &xc->valpos_handle_nam);
+            tmpfile_close(&xc->curval_handle, &xc->curval_handle_nam);
+            tmpfile_close(&xc->tchn_handle, &xc->tchn_handle_nam);
+            free(xc->vchg_mem);
+            free(xc);
+            xc = NULL;
+        }
+
+        free(hf);
+    }
+
+    return (xc);
+}
+
+/*
+ * generation and writing out of value change data sections
+ */
+static void fstWriterEmitSectionHeader(void *ctx)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+
+    if (xc) {
+        unsigned long destlen;
+        unsigned char *dmem;
+        int rc;
+
+        destlen = xc->maxvalpos;
+        dmem = (unsigned char *)malloc(compressBound(destlen));
+        rc = compress2(dmem, &destlen, xc->curval_mem, xc->maxvalpos,
+                       4); /* was 9...which caused performance drag on traces with many signals */
+
+        fputc(FST_BL_SKIP, xc->handle); /* temporarily tag the section, use FST_BL_VCDATA on finalize */
+        xc->section_start = ftello(xc->handle);
+#ifdef FST_WRITER_PARALLEL
+        if (xc->xc_parent)
+            xc->xc_parent->section_start = xc->section_start;
+#endif
+        xc->section_header_only = 1;    /* indicates truncate might be needed */
+        fstWriterUint64(xc->handle, 0); /* placeholder = section length */
+        fstWriterUint64(xc->handle, xc->is_initial_time ? xc->firsttime : xc->curtime); /* begin time of section */
+        fstWriterUint64(xc->handle, xc->curtime); /* end time of section (placeholder) */
+        fstWriterUint64(xc->handle,
+                        0); /* placeholder = amount of buffer memory required in reader for full vc traversal */
+        fstWriterVarint(xc->handle, xc->maxvalpos); /* maxvalpos = length of uncompressed data */
+
+        if ((rc == Z_OK) && (destlen < xc->maxvalpos)) {
+            fstWriterVarint(xc->handle, destlen); /* length of compressed data */
+        } else {
+            fstWriterVarint(xc->handle, xc->maxvalpos); /* length of (unable to be) compressed data */
+        }
+        fstWriterVarint(xc->handle,
+                        xc->maxhandle); /* max handle associated with this data (in case of dynamic facility adds) */
+
+        if ((rc == Z_OK) && (destlen < xc->maxvalpos)) {
+            fstFwrite(dmem, destlen, 1, xc->handle);
+        } else /* comparison between compressed / decompressed len tells if compressed */
+        {
+            fstFwrite(xc->curval_mem, xc->maxvalpos, 1, xc->handle);
+        }
+
+        free(dmem);
+    }
+}
+
+/*
+ * only to be called directly by fst code...otherwise must
+ * be synced up with time changes
+ */
+#ifdef FST_WRITER_PARALLEL
+static void fstWriterFlushContextPrivate2(void *ctx)
+#else
+static void fstWriterFlushContextPrivate(void *ctx)
+#endif
+{
+#ifdef FST_DEBUG
+    int cnt = 0;
+#endif
+    unsigned int i;
+    unsigned char *vchg_mem;
+    FILE *f;
+    fst_off_t fpos, indxpos, endpos;
+    uint32_t prevpos;
+    int zerocnt;
+    unsigned char *scratchpad;
+    unsigned char *scratchpnt;
+    unsigned char *tmem;
+    fst_off_t tlen;
+    fst_off_t unc_memreq = 0; /* for reader */
+    unsigned char *packmem;
+    unsigned int packmemlen;
+    uint32_t *vm4ip;
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+#ifdef FST_WRITER_PARALLEL
+    struct fstWriterContext *xc2 = xc->xc_parent;
+#else
+    struct fstWriterContext *xc2 = xc;
+#endif
+
+#ifndef FST_DYNAMIC_ALIAS_DISABLE
+    Pvoid_t PJHSArray = (Pvoid_t)NULL;
+#ifndef _WAVE_HAVE_JUDY
+    uint32_t hashmask = xc->maxhandle;
+    hashmask |= hashmask >> 1;
+    hashmask |= hashmask >> 2;
+    hashmask |= hashmask >> 4;
+    hashmask |= hashmask >> 8;
+    hashmask |= hashmask >> 16;
+#endif
+#endif
+
+    if ((xc->vchg_siz <= 1) || (xc->already_in_flush))
+        return;
+    xc->already_in_flush = 1; /* should really do this with a semaphore */
+
+    xc->section_header_only = 0;
+    scratchpad = (unsigned char *)malloc(xc->vchg_siz);
+
+    vchg_mem = xc->vchg_mem;
+
+    f = xc->handle;
+    fstWriterVarint(f, xc->maxhandle); /* emit current number of handles */
+    fputc(xc->fourpack ? '4' : (xc->fastpack ? 'F' : 'Z'), f);
+    fpos = 1;
+
+    packmemlen = 1024;                             /* maintain a running "longest" allocation to */
+    packmem = (unsigned char *)malloc(packmemlen); /* prevent continual malloc...free every loop iter */
+
+    for (i = 0; i < xc->maxhandle; i++) {
+        vm4ip = &(xc->valpos_mem[4 * i]);
+
+        if (vm4ip[2]) {
+            uint32_t offs = vm4ip[2];
+            uint32_t next_offs;
+            unsigned int wrlen;
+
+            vm4ip[2] = fpos;
+
+            scratchpnt = scratchpad + xc->vchg_siz; /* build this buffer backwards */
+            if (vm4ip[1] <= 1) {
+                if (vm4ip[1] == 1) {
+                    wrlen = fstGetVarint32Length(vchg_mem + offs + 4); /* used to advance and determine wrlen */
+#ifndef FST_REMOVE_DUPLICATE_VC
+                    xc->curval_mem[vm4ip[0]] = vchg_mem[offs + 4 + wrlen]; /* checkpoint variable */
+#endif
+                    while (offs) {
+                        unsigned char val;
+                        uint32_t time_delta, rcv;
+                        next_offs = fstGetUint32(vchg_mem + offs);
+                        offs += 4;
+
+                        time_delta = fstGetVarint32(vchg_mem + offs, (int *)&wrlen);
+                        val = vchg_mem[offs + wrlen];
+                        offs = next_offs;
+
+                        switch (val) {
+                        case '0':
+                        case '1':
+                            rcv = ((val & 1) << 1) | (time_delta << 2);
+                            break; /* pack more delta bits in for 0/1 vchs */
+
+                        case 'x':
+                        case 'X':
+                            rcv = FST_RCV_X | (time_delta << 4);
+                            break;
+                        case 'z':
+                        case 'Z':
+                            rcv = FST_RCV_Z | (time_delta << 4);
+                            break;
+                        case 'h':
+                        case 'H':
+                            rcv = FST_RCV_H | (time_delta << 4);
+                            break;
+                        case 'u':
+                        case 'U':
+                            rcv = FST_RCV_U | (time_delta << 4);
+                            break;
+                        case 'w':
+                        case 'W':
+                            rcv = FST_RCV_W | (time_delta << 4);
+                            break;
+                        case 'l':
+                        case 'L':
+                            rcv = FST_RCV_L | (time_delta << 4);
+                            break;
+                        default:
+                            rcv = FST_RCV_D | (time_delta << 4);
+                            break;
+                        }
+
+                        scratchpnt = fstCopyVarint32ToLeft(scratchpnt, rcv);
+                    }
+                } else {
+                    /* variable length */
+                    /* fstGetUint32 (next_offs) + fstGetVarint32 (time_delta) + fstGetVarint32 (len) + payload */
+                    unsigned char *pnt;
+                    uint32_t record_len;
+                    uint32_t time_delta;
+
+                    while (offs) {
+                        next_offs = fstGetUint32(vchg_mem + offs);
+                        offs += 4;
+                        pnt = vchg_mem + offs;
+                        offs = next_offs;
+                        time_delta = fstGetVarint32(pnt, (int *)&wrlen);
+                        pnt += wrlen;
+                        record_len = fstGetVarint32(pnt, (int *)&wrlen);
+                        pnt += wrlen;
+
+                        scratchpnt -= record_len;
+                        memcpy(scratchpnt, pnt, record_len);
+
+                        scratchpnt = fstCopyVarint32ToLeft(scratchpnt, record_len);
+                        scratchpnt = fstCopyVarint32ToLeft(
+                                scratchpnt, (time_delta << 1)); /* reserve | 1 case for future expansion */
+                    }
+                }
+            } else {
+                wrlen = fstGetVarint32Length(vchg_mem + offs + 4); /* used to advance and determine wrlen */
+#ifndef FST_REMOVE_DUPLICATE_VC
+                memcpy(xc->curval_mem + vm4ip[0], vchg_mem + offs + 4 + wrlen, vm4ip[1]); /* checkpoint variable */
+#endif
+                while (offs) {
+                    unsigned int idx;
+                    char is_binary = 1;
+                    unsigned char *pnt;
+                    uint32_t time_delta;
+
+                    next_offs = fstGetUint32(vchg_mem + offs);
+                    offs += 4;
+
+                    time_delta = fstGetVarint32(vchg_mem + offs, (int *)&wrlen);
+
+                    pnt = vchg_mem + offs + wrlen;
+                    offs = next_offs;
+
+                    for (idx = 0; idx < vm4ip[1]; idx++) {
+                        if ((pnt[idx] == '0') || (pnt[idx] == '1')) {
+                            continue;
+                        } else {
+                            is_binary = 0;
+                            break;
+                        }
+                    }
+
+                    if (is_binary) {
+                        unsigned char acc = 0;
+                        /* new algorithm */
+                        idx = ((vm4ip[1] + 7) & ~7);
+                        switch (vm4ip[1] & 7) {
+                        case 0:
+                            do {
+                                acc = (pnt[idx + 7 - 8] & 1) << 0; /* fallthrough */
+                            case 7:
+                                acc |= (pnt[idx + 6 - 8] & 1) << 1; /* fallthrough */
+                            case 6:
+                                acc |= (pnt[idx + 5 - 8] & 1) << 2; /* fallthrough */
+                            case 5:
+                                acc |= (pnt[idx + 4 - 8] & 1) << 3; /* fallthrough */
+                            case 4:
+                                acc |= (pnt[idx + 3 - 8] & 1) << 4; /* fallthrough */
+                            case 3:
+                                acc |= (pnt[idx + 2 - 8] & 1) << 5; /* fallthrough */
+                            case 2:
+                                acc |= (pnt[idx + 1 - 8] & 1) << 6; /* fallthrough */
+                            case 1:
+                                acc |= (pnt[idx + 0 - 8] & 1) << 7;
+                                *(--scratchpnt) = acc;
+                                idx -= 8;
+                            } while (idx);
+                        }
+
+                        scratchpnt = fstCopyVarint32ToLeft(scratchpnt, (time_delta << 1));
+                    } else {
+                        scratchpnt -= vm4ip[1];
+                        memcpy(scratchpnt, pnt, vm4ip[1]);
+
+                        scratchpnt = fstCopyVarint32ToLeft(scratchpnt, (time_delta << 1) | 1);
+                    }
+                }
+            }
+
+            wrlen = scratchpad + xc->vchg_siz - scratchpnt;
+            unc_memreq += wrlen;
+            if (wrlen > 32) {
+                unsigned long destlen = wrlen;
+                unsigned char *dmem;
+                unsigned int rc;
+
+                if (!xc->fastpack) {
+                    if (wrlen <= packmemlen) {
+                        dmem = packmem;
+                    } else {
+                        free(packmem);
+                        dmem = packmem = (unsigned char *)malloc(compressBound(packmemlen = wrlen));
+                    }
+
+                    rc = compress2(dmem, &destlen, scratchpnt, wrlen, 4);
+                    if (rc == Z_OK) {
+#ifndef FST_DYNAMIC_ALIAS_DISABLE
+                        PPvoid_t pv = JudyHSIns(&PJHSArray, dmem, destlen, NULL);
+                        if (*pv) {
+                            uint32_t pvi = (intptr_t)(*pv);
+                            vm4ip[2] = -pvi;
+                        } else {
+                            *pv = (void *)(intptr_t)(i + 1);
+#endif
+                            fpos += fstWriterVarint(f, wrlen);
+                            fpos += destlen;
+                            fstFwrite(dmem, destlen, 1, f);
+#ifndef FST_DYNAMIC_ALIAS_DISABLE
+                        }
+#endif
+                    } else {
+#ifndef FST_DYNAMIC_ALIAS_DISABLE
+                        PPvoid_t pv = JudyHSIns(&PJHSArray, scratchpnt, wrlen, NULL);
+                        if (*pv) {
+                            uint32_t pvi = (intptr_t)(*pv);
+                            vm4ip[2] = -pvi;
+                        } else {
+                            *pv = (void *)(intptr_t)(i + 1);
+#endif
+                            fpos += fstWriterVarint(f, 0);
+                            fpos += wrlen;
+                            fstFwrite(scratchpnt, wrlen, 1, f);
+#ifndef FST_DYNAMIC_ALIAS_DISABLE
+                        }
+#endif
+                    }
+                } else {
+                    /* this is extremely conservative: fastlz needs +5% for worst case, lz4 needs siz+(siz/255)+16 */
+                    if (((wrlen * 2) + 2) <= packmemlen) {
+                        dmem = packmem;
+                    } else {
+                        free(packmem);
+                        dmem = packmem = (unsigned char *)malloc(packmemlen = (wrlen * 2) + 2);
+                    }
+
+                    rc = (xc->fourpack) ? LZ4_compress((char *)scratchpnt, (char *)dmem, wrlen)
+                                        : fastlz_compress(scratchpnt, wrlen, dmem);
+                    if (rc < destlen) {
+#ifndef FST_DYNAMIC_ALIAS_DISABLE
+                        PPvoid_t pv = JudyHSIns(&PJHSArray, dmem, rc, NULL);
+                        if (*pv) {
+                            uint32_t pvi = (intptr_t)(*pv);
+                            vm4ip[2] = -pvi;
+                        } else {
+                            *pv = (void *)(intptr_t)(i + 1);
+#endif
+                            fpos += fstWriterVarint(f, wrlen);
+                            fpos += rc;
+                            fstFwrite(dmem, rc, 1, f);
+#ifndef FST_DYNAMIC_ALIAS_DISABLE
+                        }
+#endif
+                    } else {
+#ifndef FST_DYNAMIC_ALIAS_DISABLE
+                        PPvoid_t pv = JudyHSIns(&PJHSArray, scratchpnt, wrlen, NULL);
+                        if (*pv) {
+                            uint32_t pvi = (intptr_t)(*pv);
+                            vm4ip[2] = -pvi;
+                        } else {
+                            *pv = (void *)(intptr_t)(i + 1);
+#endif
+                            fpos += fstWriterVarint(f, 0);
+                            fpos += wrlen;
+                            fstFwrite(scratchpnt, wrlen, 1, f);
+#ifndef FST_DYNAMIC_ALIAS_DISABLE
+                        }
+#endif
+                    }
+                }
+            } else {
+#ifndef FST_DYNAMIC_ALIAS_DISABLE
+                PPvoid_t pv = JudyHSIns(&PJHSArray, scratchpnt, wrlen, NULL);
+                if (*pv) {
+                    uint32_t pvi = (intptr_t)(*pv);
+                    vm4ip[2] = -pvi;
+                } else {
+                    *pv = (void *)(intptr_t)(i + 1);
+#endif
+                    fpos += fstWriterVarint(f, 0);
+                    fpos += wrlen;
+                    fstFwrite(scratchpnt, wrlen, 1, f);
+#ifndef FST_DYNAMIC_ALIAS_DISABLE
+                }
+#endif
+            }
+
+            /* vm4ip[3] = 0; ...redundant with clearing below */
+#ifdef FST_DEBUG
+            cnt++;
+#endif
+        }
+    }
+
+#ifndef FST_DYNAMIC_ALIAS_DISABLE
+    JudyHSFreeArray(&PJHSArray, NULL);
+#endif
+
+    free(packmem);
+    packmem = NULL; /* packmemlen = 0; */ /* scan-build */
+
+    prevpos = 0;
+    zerocnt = 0;
+    free(scratchpad);
+    scratchpad = NULL;
+
+    indxpos = ftello(f);
+    xc->secnum++;
+
+#ifndef FST_DYNAMIC_ALIAS2_DISABLE
+    if (1) {
+        uint32_t prev_alias = 0;
+
+        for (i = 0; i < xc->maxhandle; i++) {
+            vm4ip = &(xc->valpos_mem[4 * i]);
+
+            if (vm4ip[2]) {
+                if (zerocnt) {
+                    fpos += fstWriterVarint(f, (zerocnt << 1));
+                    zerocnt = 0;
+                }
+
+                if (vm4ip[2] & 0x80000000) {
+                    if (vm4ip[2] != prev_alias) {
+                        fpos += fstWriterSVarint(f, (((int64_t)((int32_t)(prev_alias = vm4ip[2]))) << 1) | 1);
+                    } else {
+                        fpos += fstWriterSVarint(f, (0 << 1) | 1);
+                    }
+                } else {
+                    fpos += fstWriterSVarint(f, ((vm4ip[2] - prevpos) << 1) | 1);
+                    prevpos = vm4ip[2];
+                }
+                vm4ip[2] = 0;
+                vm4ip[3] = 0; /* clear out tchn idx */
+            } else {
+                zerocnt++;
+            }
+        }
+    } else
+#endif
+    {
+        for (i = 0; i < xc->maxhandle; i++) {
+            vm4ip = &(xc->valpos_mem[4 * i]);
+
+            if (vm4ip[2]) {
+                if (zerocnt) {
+                    fpos += fstWriterVarint(f, (zerocnt << 1));
+                    zerocnt = 0;
+                }
+
+                if (vm4ip[2] & 0x80000000) {
+                    fpos += fstWriterVarint(f, 0); /* signal, note that using a *signed* varint would be more efficient
+                                                      than this byte escape! */
+                    fpos += fstWriterVarint(f, (-(int32_t)vm4ip[2]));
+                } else {
+                    fpos += fstWriterVarint(f, ((vm4ip[2] - prevpos) << 1) | 1);
+                    prevpos = vm4ip[2];
+                }
+                vm4ip[2] = 0;
+                vm4ip[3] = 0; /* clear out tchn idx */
+            } else {
+                zerocnt++;
+            }
+        }
+    }
+
+    if (zerocnt) {
+        /* fpos += */ fstWriterVarint(f, (zerocnt << 1)); /* scan-build */
+    }
+#ifdef FST_DEBUG
+    fprintf(stderr, FST_APIMESS "value chains: %d\n", cnt);
+#endif
+
+    xc->vchg_mem[0] = '!';
+    xc->vchg_siz = 1;
+
+    endpos = ftello(xc->handle);
+    fstWriterUint64(xc->handle, endpos - indxpos); /* write delta index position at very end of block */
+
+    /*emit time changes for block */
+    fflush(xc->tchn_handle);
+    tlen = ftello(xc->tchn_handle);
+    fstWriterFseeko(xc, xc->tchn_handle, 0, SEEK_SET);
+
+    errno = 0;
+    fstWriterMmapSanity(
+            tmem = (unsigned char *)fstMmap(NULL, tlen, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(xc->tchn_handle), 0),
+            __FILE__, __LINE__, "tmem");
+    if (tmem) {
+        unsigned long destlen = tlen;
+        unsigned char *dmem = (unsigned char *)malloc(compressBound(destlen));
+        int rc = compress2(dmem, &destlen, tmem, tlen, 9);
+
+        if ((rc == Z_OK) && (((fst_off_t)destlen) < tlen)) {
+            fstFwrite(dmem, destlen, 1, xc->handle);
+        } else /* comparison between compressed / decompressed len tells if compressed */
+        {
+            fstFwrite(tmem, tlen, 1, xc->handle);
+            destlen = tlen;
+        }
+        free(dmem);
+        fstMunmap(tmem, tlen);
+        fstWriterUint64(xc->handle, tlen);         /* uncompressed */
+        fstWriterUint64(xc->handle, destlen);      /* compressed */
+        fstWriterUint64(xc->handle, xc->tchn_cnt); /* number of time items */
+    }
+
+    xc->tchn_cnt = xc->tchn_idx = 0;
+    fstWriterFseeko(xc, xc->tchn_handle, 0, SEEK_SET);
+    fstFtruncate(fileno(xc->tchn_handle), 0);
+
+    /* write block trailer */
+    endpos = ftello(xc->handle);
+    fstWriterFseeko(xc, xc->handle, xc->section_start, SEEK_SET);
+    fstWriterUint64(xc->handle, endpos - xc->section_start); /* write block length */
+    fstWriterFseeko(xc, xc->handle, 8, SEEK_CUR);            /* skip begin time */
+    fstWriterUint64(xc->handle, xc->curtime);                /* write end time for section */
+    fstWriterUint64(xc->handle, unc_memreq); /* amount of buffer memory required in reader for full traversal */
+    fflush(xc->handle);
+
+    fstWriterFseeko(xc, xc->handle, xc->section_start - 1, SEEK_SET); /* write out FST_BL_VCDATA over FST_BL_SKIP */
+
+#ifndef FST_DYNAMIC_ALIAS_DISABLE
+#ifndef FST_DYNAMIC_ALIAS2_DISABLE
+    fputc(FST_BL_VCDATA_DYN_ALIAS2, xc->handle);
+#else
+    fputc(FST_BL_VCDATA_DYN_ALIAS, xc->handle);
+#endif
+#else
+    fputc(FST_BL_VCDATA, xc->handle);
+#endif
+
+    fflush(xc->handle);
+
+    fstWriterFseeko(xc, xc->handle, endpos, SEEK_SET); /* seek to end of file */
+
+    xc2->section_header_truncpos = endpos; /* cache in case of need to truncate */
+    if (xc->dump_size_limit) {
+        if (endpos >= ((fst_off_t)xc->dump_size_limit)) {
+            xc2->skip_writing_section_hdr = 1;
+            xc2->size_limit_locked = 1;
+            xc2->is_initial_time = 1; /* to trick emit value and emit time change */
+#ifdef FST_DEBUG
+            fprintf(stderr, FST_APIMESS "<< dump file size limit reached, stopping dumping >>\n");
+#endif
+        }
+    }
+
+    if (!xc2->skip_writing_section_hdr) {
+        fstWriterEmitSectionHeader(xc); /* emit next section header */
+    }
+    fflush(xc->handle);
+
+    xc->already_in_flush = 0;
+}
+
+#ifdef FST_WRITER_PARALLEL
+static void *fstWriterFlushContextPrivate1(void *ctx)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    struct fstWriterContext *xc_parent;
+
+    pthread_mutex_lock(&(xc->xc_parent->mutex));
+    fstWriterFlushContextPrivate2(xc);
+
+#ifdef FST_REMOVE_DUPLICATE_VC
+    free(xc->curval_mem);
+#endif
+    free(xc->valpos_mem);
+    free(xc->vchg_mem);
+    tmpfile_close(&xc->tchn_handle, &xc->tchn_handle_nam);
+    xc_parent = xc->xc_parent;
+    free(xc);
+
+    xc_parent->in_pthread = 0;
+    pthread_mutex_unlock(&(xc_parent->mutex));
+
+    return (NULL);
+}
+
+static void fstWriterFlushContextPrivate(void *ctx)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+
+    if (xc->parallel_enabled) {
+        struct fstWriterContext *xc2 = (struct fstWriterContext *)malloc(sizeof(struct fstWriterContext));
+        unsigned int i;
+
+        pthread_mutex_lock(&xc->mutex);
+        pthread_mutex_unlock(&xc->mutex);
+
+        xc->xc_parent = xc;
+        memcpy(xc2, xc, sizeof(struct fstWriterContext));
+
+        xc2->valpos_mem = (uint32_t *)malloc(xc->maxhandle * 4 * sizeof(uint32_t));
+        memcpy(xc2->valpos_mem, xc->valpos_mem, xc->maxhandle * 4 * sizeof(uint32_t));
+
+        /* curval mem is updated in the thread */
+#ifdef FST_REMOVE_DUPLICATE_VC
+        xc2->curval_mem = (unsigned char *)malloc(xc->maxvalpos);
+        memcpy(xc2->curval_mem, xc->curval_mem, xc->maxvalpos);
+#endif
+
+        xc->vchg_mem = (unsigned char *)malloc(xc->vchg_alloc_siz);
+        xc->vchg_mem[0] = '!';
+        xc->vchg_siz = 1;
+
+        for (i = 0; i < xc->maxhandle; i++) {
+            uint32_t *vm4ip = &(xc->valpos_mem[4 * i]);
+            vm4ip[2] = 0; /* zero out offset val */
+            vm4ip[3] = 0; /* zero out last time change val */
+        }
+
+        xc->tchn_cnt = xc->tchn_idx = 0;
+        xc->tchn_handle = tmpfile_open(&xc->tchn_handle_nam); /* child thread will deallocate file/name */
+        fstWriterFseeko(xc, xc->tchn_handle, 0, SEEK_SET);
+        fstFtruncate(fileno(xc->tchn_handle), 0);
+
+        xc->section_header_only = 0;
+        xc->secnum++;
+
+        while (xc->in_pthread) {
+            pthread_mutex_lock(&xc->mutex);
+            pthread_mutex_unlock(&xc->mutex);
+        };
+
+        pthread_mutex_lock(&xc->mutex);
+        xc->in_pthread = 1;
+        pthread_mutex_unlock(&xc->mutex);
+
+        pthread_create(&xc->thread, &xc->thread_attr, fstWriterFlushContextPrivate1, xc2);
+    } else {
+        if (xc->parallel_was_enabled) /* conservatively block */
+        {
+            pthread_mutex_lock(&xc->mutex);
+            pthread_mutex_unlock(&xc->mutex);
+        }
+
+        xc->xc_parent = xc;
+        fstWriterFlushContextPrivate2(xc);
+    }
+}
+#endif
+
+/*
+ * queues up a flush context operation
+ */
+void fstWriterFlushContext(void *ctx)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc) {
+        if (xc->tchn_idx > 1) {
+            xc->flush_context_pending = 1;
+        }
+    }
+}
+
+/*
+ * close out FST file
+ */
+void fstWriterClose(void *ctx)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+
+#ifdef FST_WRITER_PARALLEL
+    if (xc) {
+        pthread_mutex_lock(&xc->mutex);
+        pthread_mutex_unlock(&xc->mutex);
+    }
+#endif
+
+    if (xc && !xc->already_in_close && !xc->already_in_flush) {
+        unsigned char *tmem = NULL;
+        fst_off_t fixup_offs, tlen, hlen;
+
+        xc->already_in_close = 1; /* never need to zero this out as it is freed at bottom */
+
+        if (xc->section_header_only && xc->section_header_truncpos && (xc->vchg_siz <= 1) && (!xc->is_initial_time)) {
+            fstFtruncate(fileno(xc->handle), xc->section_header_truncpos);
+            fstWriterFseeko(xc, xc->handle, xc->section_header_truncpos, SEEK_SET);
+            xc->section_header_only = 0;
+        } else {
+            xc->skip_writing_section_hdr = 1;
+            if (!xc->size_limit_locked) {
+                if (FST_UNLIKELY(xc->is_initial_time)) /* simulation time never advanced so mock up the changes as time
+                                                          zero ones */
+                {
+                    fstHandle dupe_idx;
+
+                    fstWriterEmitTimeChange(xc, 0); /* emit some time change just to have one */
+                    for (dupe_idx = 0; dupe_idx < xc->maxhandle; dupe_idx++) /* now clone the values */
+                    {
+                        fstWriterEmitValueChange(xc, dupe_idx + 1, xc->curval_mem + xc->valpos_mem[4 * dupe_idx]);
+                    }
+                }
+                fstWriterFlushContextPrivate(xc);
+#ifdef FST_WRITER_PARALLEL
+                pthread_mutex_lock(&xc->mutex);
+                pthread_mutex_unlock(&xc->mutex);
+
+                while (xc->in_pthread) {
+                    pthread_mutex_lock(&xc->mutex);
+                    pthread_mutex_unlock(&xc->mutex);
+                };
+#endif
+            }
+        }
+        fstDestroyMmaps(xc, 1);
+        if (xc->outval_mem) {
+            free(xc->outval_mem);
+            xc->outval_mem = NULL;
+            xc->outval_alloc_siz = 0;
+        }
+
+        /* write out geom section */
+        fflush(xc->geom_handle);
+        tlen = ftello(xc->geom_handle);
+        errno = 0;
+        if (tlen) {
+            fstWriterMmapSanity(tmem = (unsigned char *)fstMmap(NULL, tlen, PROT_READ | PROT_WRITE, MAP_SHARED,
+                                                                fileno(xc->geom_handle), 0),
+                                __FILE__, __LINE__, "tmem");
+        }
+
+        if (tmem) {
+            unsigned long destlen = tlen;
+            unsigned char *dmem = (unsigned char *)malloc(compressBound(destlen));
+            int rc = compress2(dmem, &destlen, tmem, tlen, 9);
+
+            if ((rc != Z_OK) || (((fst_off_t)destlen) > tlen)) {
+                destlen = tlen;
+            }
+
+            fixup_offs = ftello(xc->handle);
+            fputc(FST_BL_SKIP, xc->handle);             /* temporary tag */
+            fstWriterUint64(xc->handle, destlen + 24);  /* section length */
+            fstWriterUint64(xc->handle, tlen);          /* uncompressed */
+                                                        /* compressed len is section length - 24 */
+            fstWriterUint64(xc->handle, xc->maxhandle); /* maxhandle */
+            fstFwrite((((fst_off_t)destlen) != tlen) ? dmem : tmem, destlen, 1, xc->handle);
+            fflush(xc->handle);
+
+            fstWriterFseeko(xc, xc->handle, fixup_offs, SEEK_SET);
+            fputc(FST_BL_GEOM, xc->handle); /* actual tag */
+
+            fstWriterFseeko(xc, xc->handle, 0, SEEK_END); /* move file pointer to end for any section adds */
+            fflush(xc->handle);
+
+            free(dmem);
+            fstMunmap(tmem, tlen);
+        }
+
+        if (xc->num_blackouts) {
+            uint64_t cur_bl = 0;
+            fst_off_t bpos, eos;
+            uint32_t i;
+
+            fixup_offs = ftello(xc->handle);
+            fputc(FST_BL_SKIP, xc->handle); /* temporary tag */
+            bpos = fixup_offs + 1;
+            fstWriterUint64(xc->handle, 0); /* section length */
+            fstWriterVarint(xc->handle, xc->num_blackouts);
+
+            for (i = 0; i < xc->num_blackouts; i++) {
+                fputc(xc->blackout_head->active, xc->handle);
+                fstWriterVarint(xc->handle, xc->blackout_head->tim - cur_bl);
+                cur_bl = xc->blackout_head->tim;
+                xc->blackout_curr = xc->blackout_head->next;
+                free(xc->blackout_head);
+                xc->blackout_head = xc->blackout_curr;
+            }
+
+            eos = ftello(xc->handle);
+            fstWriterFseeko(xc, xc->handle, bpos, SEEK_SET);
+            fstWriterUint64(xc->handle, eos - bpos);
+            fflush(xc->handle);
+
+            fstWriterFseeko(xc, xc->handle, fixup_offs, SEEK_SET);
+            fputc(FST_BL_BLACKOUT, xc->handle); /* actual tag */
+
+            fstWriterFseeko(xc, xc->handle, 0, SEEK_END); /* move file pointer to end for any section adds */
+            fflush(xc->handle);
+        }
+
+        if (xc->compress_hier) {
+            fst_off_t hl, eos;
+            gzFile zhandle;
+            int zfd;
+            int fourpack_duo = 0;
+#ifndef __MINGW32__
+            char *fnam = (char *)malloc(strlen(xc->filename) + 5 + 1);
+#endif
+
+            fixup_offs = ftello(xc->handle);
+            fputc(FST_BL_SKIP, xc->handle); /* temporary tag */
+            hlen = ftello(xc->handle);
+            fstWriterUint64(xc->handle, 0);                 /* section length */
+            fstWriterUint64(xc->handle, xc->hier_file_len); /* uncompressed length */
+
+            if (!xc->fourpack) {
+                unsigned char *mem = (unsigned char *)malloc(FST_GZIO_LEN);
+                zfd = dup(fileno(xc->handle));
+                fflush(xc->handle);
+                zhandle = gzdopen(zfd, "wb4");
+                if (zhandle) {
+                    fstWriterFseeko(xc, xc->hier_handle, 0, SEEK_SET);
+                    for (hl = 0; hl < xc->hier_file_len; hl += FST_GZIO_LEN) {
+                        unsigned len =
+                                ((xc->hier_file_len - hl) > FST_GZIO_LEN) ? FST_GZIO_LEN : (xc->hier_file_len - hl);
+                        fstFread(mem, len, 1, xc->hier_handle);
+                        gzwrite(zhandle, mem, len);
+                    }
+                    gzclose(zhandle);
+                } else {
+                    close(zfd);
+                }
+                free(mem);
+            } else {
+                int lz4_maxlen;
+                unsigned char *mem;
+                unsigned char *hmem = NULL;
+                int packed_len;
+
+                fflush(xc->handle);
+
+                lz4_maxlen = LZ4_compressBound(xc->hier_file_len);
+                mem = (unsigned char *)malloc(lz4_maxlen);
+                errno = 0;
+                if (xc->hier_file_len) {
+                    fstWriterMmapSanity(hmem = (unsigned char *)fstMmap(NULL, xc->hier_file_len, PROT_READ | PROT_WRITE,
+                                                                        MAP_SHARED, fileno(xc->hier_handle), 0),
+                                        __FILE__, __LINE__, "hmem");
+                }
+                packed_len = LZ4_compress((char *)hmem, (char *)mem, xc->hier_file_len);
+                fstMunmap(hmem, xc->hier_file_len);
+
+                fourpack_duo =
+                        (!xc->repack_on_close) &&
+                        (xc->hier_file_len > FST_HDR_FOURPACK_DUO_SIZE); /* double pack when hierarchy is large */
+
+                if (fourpack_duo) /* double packing with LZ4 is faster than gzip */
+                {
+                    unsigned char *mem_duo;
+                    int lz4_maxlen_duo;
+                    int packed_len_duo;
+
+                    lz4_maxlen_duo = LZ4_compressBound(packed_len);
+                    mem_duo = (unsigned char *)malloc(lz4_maxlen_duo);
+                    packed_len_duo = LZ4_compress((char *)mem, (char *)mem_duo, packed_len);
+
+                    fstWriterVarint(xc->handle, packed_len); /* 1st round compressed length */
+                    fstFwrite(mem_duo, packed_len_duo, 1, xc->handle);
+                    free(mem_duo);
+                } else {
+                    fstFwrite(mem, packed_len, 1, xc->handle);
+                }
+
+                free(mem);
+            }
+
+            fstWriterFseeko(xc, xc->handle, 0, SEEK_END);
+            eos = ftello(xc->handle);
+            fstWriterFseeko(xc, xc->handle, hlen, SEEK_SET);
+            fstWriterUint64(xc->handle, eos - hlen);
+            fflush(xc->handle);
+
+            fstWriterFseeko(xc, xc->handle, fixup_offs, SEEK_SET);
+            fputc(xc->fourpack ? (fourpack_duo ? FST_BL_HIER_LZ4DUO : FST_BL_HIER_LZ4) : FST_BL_HIER,
+                  xc->handle); /* actual tag now also == compression type */
+
+            fstWriterFseeko(xc, xc->handle, 0, SEEK_END); /* move file pointer to end for any section adds */
+            fflush(xc->handle);
+
+#ifndef __MINGW32__
+            sprintf(fnam, "%s.hier", xc->filename);
+            unlink(fnam);
+            free(fnam);
+#endif
+        }
+
+        /* finalize out header */
+        fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_START_TIME, SEEK_SET);
+        fstWriterUint64(xc->handle, xc->firsttime);
+        fstWriterUint64(xc->handle, xc->curtime);
+        fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_NUM_SCOPES, SEEK_SET);
+        fstWriterUint64(xc->handle, xc->numscopes);
+        fstWriterUint64(xc->handle, xc->numsigs);
+        fstWriterUint64(xc->handle, xc->maxhandle);
+        fstWriterUint64(xc->handle, xc->secnum);
+        fflush(xc->handle);
+
+        tmpfile_close(&xc->tchn_handle, &xc->tchn_handle_nam);
+        free(xc->vchg_mem);
+        xc->vchg_mem = NULL;
+        tmpfile_close(&xc->curval_handle, &xc->curval_handle_nam);
+        tmpfile_close(&xc->valpos_handle, &xc->valpos_handle_nam);
+        tmpfile_close(&xc->geom_handle, &xc->geom_handle_nam);
+        if (xc->hier_handle) {
+            fclose(xc->hier_handle);
+            xc->hier_handle = NULL;
+        }
+        if (xc->handle) {
+            if (xc->repack_on_close) {
+                FILE *fp;
+                fst_off_t offpnt, uclen;
+                int flen = strlen(xc->filename);
+                char *hf = (char *)calloc(1, flen + 5);
+
+                strcpy(hf, xc->filename);
+                strcpy(hf + flen, ".pak");
+                fp = fopen(hf, "wb");
+
+                if (fp) {
+                    gzFile dsth;
+                    int zfd;
+                    char gz_membuf[FST_GZIO_LEN];
+
+                    fstWriterFseeko(xc, xc->handle, 0, SEEK_END);
+                    uclen = ftello(xc->handle);
+
+                    fputc(FST_BL_ZWRAPPER, fp);
+                    fstWriterUint64(fp, 0);
+                    fstWriterUint64(fp, uclen);
+                    fflush(fp);
+
+                    fstWriterFseeko(xc, xc->handle, 0, SEEK_SET);
+                    zfd = dup(fileno(fp));
+                    dsth = gzdopen(zfd, "wb4");
+                    if (dsth) {
+                        for (offpnt = 0; offpnt < uclen; offpnt += FST_GZIO_LEN) {
+                            size_t this_len = ((uclen - offpnt) > FST_GZIO_LEN) ? FST_GZIO_LEN : (uclen - offpnt);
+                            fstFread(gz_membuf, this_len, 1, xc->handle);
+                            gzwrite(dsth, gz_membuf, this_len);
+                        }
+                        gzclose(dsth);
+                    } else {
+                        close(zfd);
+                    }
+                    fstWriterFseeko(xc, fp, 0, SEEK_END);
+                    offpnt = ftello(fp);
+                    fstWriterFseeko(xc, fp, 1, SEEK_SET);
+                    fstWriterUint64(fp, offpnt - 1);
+                    fclose(fp);
+                    fclose(xc->handle);
+                    xc->handle = NULL;
+
+                    unlink(xc->filename);
+                    rename(hf, xc->filename);
+                } else {
+                    xc->repack_on_close = 0;
+                    fclose(xc->handle);
+                    xc->handle = NULL;
+                }
+
+                free(hf);
+            } else {
+                fclose(xc->handle);
+                xc->handle = NULL;
+            }
+        }
+
+#ifdef __MINGW32__
+        {
+            int flen = strlen(xc->filename);
+            char *hf = (char *)calloc(1, flen + 6);
+            strcpy(hf, xc->filename);
+
+            if (xc->compress_hier) {
+                strcpy(hf + flen, ".hier");
+                unlink(hf); /* no longer needed as a section now exists for this */
+            }
+
+            free(hf);
+        }
+#endif
+
+#ifdef FST_WRITER_PARALLEL
+        pthread_mutex_destroy(&xc->mutex);
+        pthread_attr_destroy(&xc->thread_attr);
+#endif
+
+        if (xc->path_array) {
+#ifndef _WAVE_HAVE_JUDY
+            const uint32_t hashmask = FST_PATH_HASHMASK;
+#endif
+            JudyHSFreeArray(&(xc->path_array), NULL);
+        }
+
+        free(xc->filename);
+        xc->filename = NULL;
+        free(xc);
+    }
+}
+
+/*
+ * functions to set miscellaneous header/block information
+ */
+void fstWriterSetDate(void *ctx, const char *dat)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc) {
+        char s[FST_HDR_DATE_SIZE];
+        fst_off_t fpos = ftello(xc->handle);
+        int len = strlen(dat);
+
+        fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_DATE, SEEK_SET);
+        memset(s, 0, FST_HDR_DATE_SIZE);
+        memcpy(s, dat, (len < FST_HDR_DATE_SIZE) ? len : FST_HDR_DATE_SIZE);
+        fstFwrite(s, FST_HDR_DATE_SIZE, 1, xc->handle);
+        fflush(xc->handle);
+        fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET);
+    }
+}
+
+void fstWriterSetVersion(void *ctx, const char *vers)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc && vers) {
+        char s[FST_HDR_SIM_VERSION_SIZE];
+        fst_off_t fpos = ftello(xc->handle);
+        int len = strlen(vers);
+
+        fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_SIM_VERSION, SEEK_SET);
+        memset(s, 0, FST_HDR_SIM_VERSION_SIZE);
+        memcpy(s, vers, (len < FST_HDR_SIM_VERSION_SIZE) ? len : FST_HDR_SIM_VERSION_SIZE);
+        fstFwrite(s, FST_HDR_SIM_VERSION_SIZE, 1, xc->handle);
+        fflush(xc->handle);
+        fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET);
+    }
+}
+
+void fstWriterSetFileType(void *ctx, enum fstFileType filetype)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc) {
+        if (/*(filetype >= FST_FT_MIN) &&*/ (filetype <= FST_FT_MAX)) {
+            fst_off_t fpos = ftello(xc->handle);
+
+            xc->filetype = filetype;
+
+            fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_FILETYPE, SEEK_SET);
+            fputc(xc->filetype, xc->handle);
+            fflush(xc->handle);
+            fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET);
+        }
+    }
+}
+
+static void fstWriterSetAttrDoubleArgGeneric(void *ctx, int typ, uint64_t arg1, uint64_t arg2)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc) {
+        unsigned char buf[11]; /* ceil(64/7) = 10 + null term */
+        unsigned char *pnt = fstCopyVarint64ToRight(buf, arg1);
+        if (arg1) {
+            *pnt = 0; /* this converts any *nonzero* arg1 when made a varint into a null-term string */
+        }
+
+        fstWriterSetAttrBegin(xc, FST_AT_MISC, typ, (char *)buf, arg2);
+    }
+}
+
+static void fstWriterSetAttrGeneric(void *ctx, const char *comm, int typ, uint64_t arg)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc && comm) {
+        char *s = strdup(comm);
+        char *sf = s;
+
+        while (*s) {
+            if ((*s == '\n') || (*s == '\r'))
+                *s = ' ';
+            s++;
+        }
+
+        fstWriterSetAttrBegin(xc, FST_AT_MISC, typ, sf, arg);
+        free(sf);
+    }
+}
+
+static void fstWriterSetSourceStem_2(void *ctx, const char *path, unsigned int line, unsigned int use_realpath, int typ)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+
+    if (xc && path && path[0]) {
+        uint64_t sidx = 0;
+        int slen = strlen(path);
+#ifndef _WAVE_HAVE_JUDY
+        const uint32_t hashmask = FST_PATH_HASHMASK;
+        const unsigned char *path2 = (const unsigned char *)path;
+        PPvoid_t pv;
+#else
+        char *path2 = (char *)alloca(slen + 1); /* judy lacks const qualifier in its JudyHSIns definition */
+        PPvoid_t pv;
+        strcpy(path2, path);
+#endif
+
+        pv = JudyHSIns(&(xc->path_array), path2, slen, NULL);
+        if (*pv) {
+            sidx = (intptr_t)(*pv);
+        } else {
+            char *rp = NULL;
+
+            sidx = ++xc->path_array_count;
+            *pv = (void *)(intptr_t)(xc->path_array_count);
+
+            if (use_realpath) {
+                rp = fstRealpath(
+#ifndef _WAVE_HAVE_JUDY
+                        (const char *)
+#endif
+                                path2,
+                        NULL);
+            }
+
+            fstWriterSetAttrGeneric(xc,
+                                    rp ? rp :
+#ifndef _WAVE_HAVE_JUDY
+                                       (const char *)
+#endif
+                                                    path2,
+                                    FST_MT_PATHNAME, sidx);
+
+            if (rp) {
+                free(rp);
+            }
+        }
+
+        fstWriterSetAttrDoubleArgGeneric(xc, typ, sidx, line);
+    }
+}
+
+void fstWriterSetSourceStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath)
+{
+    fstWriterSetSourceStem_2(ctx, path, line, use_realpath, FST_MT_SOURCESTEM);
+}
+
+void fstWriterSetSourceInstantiationStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath)
+{
+    fstWriterSetSourceStem_2(ctx, path, line, use_realpath, FST_MT_SOURCEISTEM);
+}
+
+void fstWriterSetComment(void *ctx, const char *comm) { fstWriterSetAttrGeneric(ctx, comm, FST_MT_COMMENT, 0); }
+
+void fstWriterSetValueList(void *ctx, const char *vl) { fstWriterSetAttrGeneric(ctx, vl, FST_MT_VALUELIST, 0); }
+
+void fstWriterSetEnvVar(void *ctx, const char *envvar) { fstWriterSetAttrGeneric(ctx, envvar, FST_MT_ENVVAR, 0); }
+
+void fstWriterSetTimescale(void *ctx, int ts)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc) {
+        fst_off_t fpos = ftello(xc->handle);
+        fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_TIMESCALE, SEEK_SET);
+        fputc(ts & 255, xc->handle);
+        fflush(xc->handle);
+        fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET);
+    }
+}
+
+void fstWriterSetTimescaleFromString(void *ctx, const char *s)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc && s) {
+        int mat = 0;
+        int seconds_exp = -9;
+        int tv = atoi(s);
+        const char *pnt = s;
+
+        while (*pnt) {
+            switch (*pnt) {
+            case 'm':
+                seconds_exp = -3;
+                mat = 1;
+                break;
+            case 'u':
+                seconds_exp = -6;
+                mat = 1;
+                break;
+            case 'n':
+                seconds_exp = -9;
+                mat = 1;
+                break;
+            case 'p':
+                seconds_exp = -12;
+                mat = 1;
+                break;
+            case 'f':
+                seconds_exp = -15;
+                mat = 1;
+                break;
+            case 'a':
+                seconds_exp = -18;
+                mat = 1;
+                break;
+            case 'z':
+                seconds_exp = -21;
+                mat = 1;
+                break;
+            case 's':
+                seconds_exp = 0;
+                mat = 1;
+                break;
+            default:
+                break;
+            }
+
+            if (mat)
+                break;
+            pnt++;
+        }
+
+        if (tv == 10) {
+            seconds_exp++;
+        } else if (tv == 100) {
+            seconds_exp += 2;
+        }
+
+        fstWriterSetTimescale(ctx, seconds_exp);
+    }
+}
+
+void fstWriterSetTimezero(void *ctx, int64_t tim)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc) {
+        fst_off_t fpos = ftello(xc->handle);
+        fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_TIMEZERO, SEEK_SET);
+        fstWriterUint64(xc->handle, (xc->timezero = tim));
+        fflush(xc->handle);
+        fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET);
+    }
+}
+
+void fstWriterSetPackType(void *ctx, enum fstWriterPackType typ)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc) {
+        xc->fastpack = (typ != FST_WR_PT_ZLIB);
+        xc->fourpack = (typ == FST_WR_PT_LZ4);
+    }
+}
+
+void fstWriterSetRepackOnClose(void *ctx, int enable)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc) {
+        xc->repack_on_close = (enable != 0);
+    }
+}
+
+void fstWriterSetParallelMode(void *ctx, int enable)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc) {
+        xc->parallel_was_enabled |= xc->parallel_enabled; /* make sticky */
+        xc->parallel_enabled = (enable != 0);
+#ifndef FST_WRITER_PARALLEL
+        if (xc->parallel_enabled) {
+            fprintf(stderr, FST_APIMESS
+                    "fstWriterSetParallelMode(), FST_WRITER_PARALLEL not enabled during compile, exiting.\n");
+            exit(255);
+        }
+#endif
+    }
+}
+
+void fstWriterSetDumpSizeLimit(void *ctx, uint64_t numbytes)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc) {
+        xc->dump_size_limit = numbytes;
+    }
+}
+
+int fstWriterGetDumpSizeLimitReached(void *ctx)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc) {
+        return (xc->size_limit_locked != 0);
+    }
+
+    return (0);
+}
+
+int fstWriterGetFseekFailed(void *ctx)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc) {
+        return (xc->fseek_failed != 0);
+    }
+
+    return (0);
+}
+
+/*
+ * writer attr/scope/var creation:
+ * fstWriterCreateVar2() is used to dump VHDL or other languages, but the
+ * underlying variable needs to map to Verilog/SV via the proper fstVarType vt
+ */
+fstHandle fstWriterCreateVar2(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam,
+                              fstHandle aliasHandle, const char *type, enum fstSupplementalVarType svt,
+                              enum fstSupplementalDataType sdt)
+{
+    fstWriterSetAttrGeneric(ctx, type ? type : "", FST_MT_SUPVAR,
+                            (svt << FST_SDT_SVT_SHIFT_COUNT) | (sdt & FST_SDT_ABS_MAX));
+    return (fstWriterCreateVar(ctx, vt, vd, len, nam, aliasHandle));
+}
+
+fstHandle fstWriterCreateVar(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam,
+                             fstHandle aliasHandle)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    unsigned int i;
+    int nlen, is_real;
+
+    if (xc && nam) {
+        if (xc->valpos_mem) {
+            fstDestroyMmaps(xc, 0);
+        }
+
+        fputc(vt, xc->hier_handle);
+        fputc(vd, xc->hier_handle);
+        nlen = strlen(nam);
+        fstFwrite(nam, nlen, 1, xc->hier_handle);
+        fputc(0, xc->hier_handle);
+        xc->hier_file_len += (nlen + 3);
+
+        if ((vt == FST_VT_VCD_REAL) || (vt == FST_VT_VCD_REAL_PARAMETER) || (vt == FST_VT_VCD_REALTIME) ||
+            (vt == FST_VT_SV_SHORTREAL)) {
+            is_real = 1;
+            len = 8; /* recast number of bytes to that of what a double is */
+        } else {
+            is_real = 0;
+            if (vt == FST_VT_GEN_STRING) {
+                len = 0;
+            }
+        }
+
+        xc->hier_file_len += fstWriterVarint(xc->hier_handle, len);
+
+        if (aliasHandle > xc->maxhandle)
+            aliasHandle = 0;
+        xc->hier_file_len += fstWriterVarint(xc->hier_handle, aliasHandle);
+        xc->numsigs++;
+        if (xc->numsigs == xc->next_huge_break) {
+            if (xc->fst_break_size < xc->fst_huge_break_size) {
+                xc->next_huge_break += FST_ACTIVATE_HUGE_INC;
+                xc->fst_break_size += xc->fst_orig_break_size;
+                xc->fst_break_add_size += xc->fst_orig_break_add_size;
+
+                xc->vchg_alloc_siz = xc->fst_break_size + xc->fst_break_add_size;
+                if (xc->vchg_mem) {
+                    xc->vchg_mem = (unsigned char *)realloc(xc->vchg_mem, xc->vchg_alloc_siz);
+                }
+            }
+        }
+
+        if (!aliasHandle) {
+            uint32_t zero = 0;
+
+            if (len) {
+                fstWriterVarint(xc->geom_handle, !is_real ? len : 0); /* geom section encodes reals as zero byte */
+            } else {
+                fstWriterVarint(xc->geom_handle, 0xFFFFFFFF); /* geom section encodes zero len as 32b -1 */
+            }
+
+            fstFwrite(&xc->maxvalpos, sizeof(uint32_t), 1, xc->valpos_handle);
+            fstFwrite(&len, sizeof(uint32_t), 1, xc->valpos_handle);
+            fstFwrite(&zero, sizeof(uint32_t), 1, xc->valpos_handle);
+            fstFwrite(&zero, sizeof(uint32_t), 1, xc->valpos_handle);
+
+            if (!is_real) {
+                for (i = 0; i < len; i++) {
+                    fputc('x', xc->curval_handle);
+                }
+            } else {
+                fstFwrite(&xc->nan, 8, 1, xc->curval_handle); /* initialize doubles to NaN rather than x */
+            }
+
+            xc->maxvalpos += len;
+            xc->maxhandle++;
+            return (xc->maxhandle);
+        } else {
+            return (aliasHandle);
+        }
+    }
+
+    return (0);
+}
+
+void fstWriterSetScope(void *ctx, enum fstScopeType scopetype, const char *scopename, const char *scopecomp)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+
+    if (xc) {
+        fputc(FST_ST_VCD_SCOPE, xc->hier_handle);
+        if (/*(scopetype < FST_ST_VCD_MODULE) ||*/ (scopetype > FST_ST_MAX)) {
+            scopetype = FST_ST_VCD_MODULE;
+        }
+        fputc(scopetype, xc->hier_handle);
+        fprintf(xc->hier_handle, "%s%c%s%c", scopename ? scopename : "", 0, scopecomp ? scopecomp : "", 0);
+
+        if (scopename) {
+            xc->hier_file_len += strlen(scopename);
+        }
+        if (scopecomp) {
+            xc->hier_file_len += strlen(scopecomp);
+        }
+
+        xc->hier_file_len += 4; /* FST_ST_VCD_SCOPE + scopetype + two string terminating zeros */
+        xc->numscopes++;
+    }
+}
+
+void fstWriterSetUpscope(void *ctx)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+
+    if (xc) {
+        fputc(FST_ST_VCD_UPSCOPE, xc->hier_handle);
+        xc->hier_file_len++;
+    }
+}
+
+void fstWriterSetAttrBegin(void *ctx, enum fstAttrType attrtype, int subtype, const char *attrname, uint64_t arg)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+
+    if (xc) {
+        fputc(FST_ST_GEN_ATTRBEGIN, xc->hier_handle);
+        if (/*(attrtype < FST_AT_MISC) ||*/ (attrtype > FST_AT_MAX)) {
+            attrtype = FST_AT_MISC;
+            subtype = FST_MT_UNKNOWN;
+        }
+        fputc(attrtype, xc->hier_handle);
+
+        switch (attrtype) {
+        case FST_AT_ARRAY:
+            if ((subtype < FST_AR_NONE) || (subtype > FST_AR_MAX))
+                subtype = FST_AR_NONE;
+            break;
+        case FST_AT_ENUM:
+            if ((subtype < FST_EV_SV_INTEGER) || (subtype > FST_EV_MAX))
+                subtype = FST_EV_SV_INTEGER;
+            break;
+        case FST_AT_PACK:
+            if ((subtype < FST_PT_NONE) || (subtype > FST_PT_MAX))
+                subtype = FST_PT_NONE;
+            break;
+
+        case FST_AT_MISC:
+        default:
+            break;
+        }
+
+        fputc(subtype, xc->hier_handle);
+        fprintf(xc->hier_handle, "%s%c", attrname ? attrname : "", 0);
+
+        if (attrname) {
+            xc->hier_file_len += strlen(attrname);
+        }
+
+        xc->hier_file_len += 4; /* FST_ST_GEN_ATTRBEGIN + type + subtype + string terminating zero */
+        xc->hier_file_len += fstWriterVarint(xc->hier_handle, arg);
+    }
+}
+
+void fstWriterSetAttrEnd(void *ctx)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+
+    if (xc) {
+        fputc(FST_ST_GEN_ATTREND, xc->hier_handle);
+        xc->hier_file_len++;
+    }
+}
+
+fstEnumHandle fstWriterCreateEnumTable(void *ctx, const char *name, uint32_t elem_count, unsigned int min_valbits,
+                                       const char **literal_arr, const char **val_arr)
+{
+    fstEnumHandle handle = 0;
+    unsigned int *literal_lens = NULL;
+    unsigned int *val_lens = NULL;
+    int lit_len_tot = 0;
+    int val_len_tot = 0;
+    int name_len;
+    char elem_count_buf[16];
+    int elem_count_len;
+    int total_len;
+    int pos = 0;
+    char *attr_str = NULL;
+
+    if (ctx && name && literal_arr && val_arr && (elem_count != 0)) {
+        struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+
+        uint32_t i;
+
+        name_len = strlen(name);
+        elem_count_len = sprintf(elem_count_buf, "%" PRIu32, elem_count);
+
+        literal_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int));
+        val_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int));
+
+        for (i = 0; i < elem_count; i++) {
+            literal_lens[i] = strlen(literal_arr[i]);
+            lit_len_tot += fstUtilityBinToEscConvertedLen((unsigned char *)literal_arr[i], literal_lens[i]);
+
+            val_lens[i] = strlen(val_arr[i]);
+            val_len_tot += fstUtilityBinToEscConvertedLen((unsigned char *)val_arr[i], val_lens[i]);
+
+            if (min_valbits > 0) {
+                if (val_lens[i] < min_valbits) {
+                    val_len_tot += (min_valbits - val_lens[i]); /* additional converted len is same for '0' character */
+                }
+            }
+        }
+
+        total_len = name_len + 1 + elem_count_len + 1 + lit_len_tot + elem_count + val_len_tot + elem_count;
+
+        attr_str = (char *)malloc(total_len);
+        pos = 0;
+
+        memcpy(attr_str + pos, name, name_len);
+        pos += name_len;
+        attr_str[pos++] = ' ';
+
+        memcpy(attr_str + pos, elem_count_buf, elem_count_len);
+        pos += elem_count_len;
+        attr_str[pos++] = ' ';
+
+        for (i = 0; i < elem_count; i++) {
+            pos += fstUtilityBinToEsc((unsigned char *)attr_str + pos, (unsigned char *)literal_arr[i],
+                                      literal_lens[i]);
+            attr_str[pos++] = ' ';
+        }
+
+        for (i = 0; i < elem_count; i++) {
+            if (min_valbits > 0) {
+                if (val_lens[i] < min_valbits) {
+                    memset(attr_str + pos, '0', min_valbits - val_lens[i]);
+                    pos += (min_valbits - val_lens[i]);
+                }
+            }
+
+            pos += fstUtilityBinToEsc((unsigned char *)attr_str + pos, (unsigned char *)val_arr[i], val_lens[i]);
+            attr_str[pos++] = ' ';
+        }
+
+        attr_str[pos - 1] = 0;
+
+#ifdef FST_DEBUG
+        fprintf(stderr, FST_APIMESS "fstWriterCreateEnumTable() total_len: %d, pos: %d\n", total_len, pos);
+        fprintf(stderr, FST_APIMESS "*%s*\n", attr_str);
+#endif
+
+        fstWriterSetAttrBegin(xc, FST_AT_MISC, FST_MT_ENUMTABLE, attr_str, handle = ++xc->max_enumhandle);
+
+        free(attr_str);
+        free(val_lens);
+        free(literal_lens);
+    }
+
+    return (handle);
+}
+
+void fstWriterEmitEnumTableRef(void *ctx, fstEnumHandle handle)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (xc && handle) {
+        fstWriterSetAttrBegin(xc, FST_AT_MISC, FST_MT_ENUMTABLE, NULL, handle);
+    }
+}
+
+/*
+ * value and time change emission
+ */
+void fstWriterEmitValueChange(void *ctx, fstHandle handle, const void *val)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    const unsigned char *buf = (const unsigned char *)val;
+    uint32_t offs;
+    int len;
+
+    if (FST_LIKELY((xc) && (handle <= xc->maxhandle))) {
+        uint32_t fpos;
+        uint32_t *vm4ip;
+
+        if (FST_UNLIKELY(!xc->valpos_mem)) {
+            xc->vc_emitted = 1;
+            fstWriterCreateMmaps(xc);
+        }
+
+        handle--; /* move starting at 1 index to starting at 0 */
+        vm4ip = &(xc->valpos_mem[4 * handle]);
+
+        len = vm4ip[1];
+        if (FST_LIKELY(len)) /* len of zero = variable length, use fstWriterEmitVariableLengthValueChange */
+        {
+            if (FST_LIKELY(!xc->is_initial_time)) {
+                fpos = xc->vchg_siz;
+
+                if (FST_UNLIKELY((fpos + len + 10) > xc->vchg_alloc_siz)) {
+                    xc->vchg_alloc_siz +=
+                            (xc->fst_break_add_size +
+                             len); /* +len added in the case of extremely long vectors and small break add sizes */
+                    xc->vchg_mem = (unsigned char *)realloc(xc->vchg_mem, xc->vchg_alloc_siz);
+                    if (FST_UNLIKELY(!xc->vchg_mem)) {
+                        fprintf(stderr, FST_APIMESS "Could not realloc() in fstWriterEmitValueChange, exiting.\n");
+                        exit(255);
+                    }
+                }
+#ifdef FST_REMOVE_DUPLICATE_VC
+                offs = vm4ip[0];
+
+                if (len != 1) {
+                    if ((vm4ip[3] == xc->tchn_idx) && (vm4ip[2])) {
+                        unsigned char *old_value = xc->vchg_mem + vm4ip[2] + 4; /* the +4 skips old vm4ip[2] value */
+                        while (*(old_value++) & 0x80) { /* skips over varint encoded "xc->tchn_idx - vm4ip[3]" */
+                        }
+                        memcpy(old_value, buf, len); /* overlay new value */
+
+                        memcpy(xc->curval_mem + offs, buf, len);
+                        return;
+                    } else {
+                        if (!memcmp(xc->curval_mem + offs, buf, len)) {
+                            if (!xc->curtime) {
+                                int i;
+                                for (i = 0; i < len; i++) {
+                                    if (buf[i] != 'x')
+                                        break;
+                                }
+
+                                if (i < len)
+                                    return;
+                            } else {
+                                return;
+                            }
+                        }
+                    }
+
+                    memcpy(xc->curval_mem + offs, buf, len);
+                } else {
+                    if ((vm4ip[3] == xc->tchn_idx) && (vm4ip[2])) {
+                        unsigned char *old_value = xc->vchg_mem + vm4ip[2] + 4; /* the +4 skips old vm4ip[2] value */
+                        while (*(old_value++) & 0x80) { /* skips over varint encoded "xc->tchn_idx - vm4ip[3]" */
+                        }
+                        *old_value = *buf; /* overlay new value */
+
+                        *(xc->curval_mem + offs) = *buf;
+                        return;
+                    } else {
+                        if ((*(xc->curval_mem + offs)) == (*buf)) {
+                            if (!xc->curtime) {
+                                if (*buf != 'x')
+                                    return;
+                            } else {
+                                return;
+                            }
+                        }
+                    }
+
+                    *(xc->curval_mem + offs) = *buf;
+                }
+#endif
+                xc->vchg_siz += fstWriterUint32WithVarint32(xc, &vm4ip[2], xc->tchn_idx - vm4ip[3], buf,
+                                                            len); /* do one fwrite op only */
+                vm4ip[3] = xc->tchn_idx;
+                vm4ip[2] = fpos;
+            } else {
+                offs = vm4ip[0];
+                memcpy(xc->curval_mem + offs, buf, len);
+            }
+        }
+    }
+}
+
+void fstWriterEmitValueChange32(void *ctx, fstHandle handle, uint32_t bits, uint32_t val)
+{
+    char buf[32];
+    char *s = buf;
+    uint32_t i;
+    for (i = 0; i < bits; ++i) {
+        *s++ = '0' + ((val >> (bits - i - 1)) & 1);
+    }
+    fstWriterEmitValueChange(ctx, handle, buf);
+}
+void fstWriterEmitValueChange64(void *ctx, fstHandle handle, uint32_t bits, uint64_t val)
+{
+    char buf[64];
+    char *s = buf;
+    uint32_t i;
+    for (i = 0; i < bits; ++i) {
+        *s++ = '0' + ((val >> (bits - i - 1)) & 1);
+    }
+    fstWriterEmitValueChange(ctx, handle, buf);
+}
+void fstWriterEmitValueChangeVec32(void *ctx, fstHandle handle, uint32_t bits, const uint32_t *val)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (FST_UNLIKELY(bits <= 32)) {
+        fstWriterEmitValueChange32(ctx, handle, bits, val[0]);
+    } else if (FST_LIKELY(xc)) {
+        int bq = bits / 32;
+        int br = bits & 31;
+        int i;
+        int w;
+        uint32_t v;
+        unsigned char *s;
+        if (FST_UNLIKELY(bits > xc->outval_alloc_siz)) {
+            xc->outval_alloc_siz = bits * 2 + 1;
+            xc->outval_mem = (unsigned char *)realloc(xc->outval_mem, xc->outval_alloc_siz);
+            if (FST_UNLIKELY(!xc->outval_mem)) {
+                fprintf(stderr, FST_APIMESS "Could not realloc() in fstWriterEmitValueChangeVec32, exiting.\n");
+                exit(255);
+            }
+        }
+        s = xc->outval_mem;
+        {
+            w = bq;
+            v = val[w];
+            for (i = 0; i < br; ++i) {
+                *s++ = '0' + ((v >> (br - i - 1)) & 1);
+            }
+        }
+        for (w = bq - 1; w >= 0; --w) {
+            v = val[w];
+            for (i = (32 - 4); i >= 0; i -= 4) {
+                s[0] = '0' + ((v >> (i + 3)) & 1);
+                s[1] = '0' + ((v >> (i + 2)) & 1);
+                s[2] = '0' + ((v >> (i + 1)) & 1);
+                s[3] = '0' + ((v >> (i + 0)) & 1);
+                s += 4;
+            }
+        }
+        fstWriterEmitValueChange(ctx, handle, xc->outval_mem);
+    }
+}
+void fstWriterEmitValueChangeVec64(void *ctx, fstHandle handle, uint32_t bits, const uint64_t *val)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    if (FST_UNLIKELY(bits <= 64)) {
+        fstWriterEmitValueChange64(ctx, handle, bits, val[0]);
+    } else if (FST_LIKELY(xc)) {
+        int bq = bits / 64;
+        int br = bits & 63;
+        int i;
+        int w;
+        uint32_t v;
+        unsigned char *s;
+        if (FST_UNLIKELY(bits > xc->outval_alloc_siz)) {
+            xc->outval_alloc_siz = bits * 2 + 1;
+            xc->outval_mem = (unsigned char *)realloc(xc->outval_mem, xc->outval_alloc_siz);
+            if (FST_UNLIKELY(!xc->outval_mem)) {
+                fprintf(stderr, FST_APIMESS "Could not realloc() in fstWriterEmitValueChangeVec64, exiting.\n");
+                exit(255);
+            }
+        }
+        s = xc->outval_mem;
+        {
+            w = bq;
+            v = val[w];
+            for (i = 0; i < br; ++i) {
+                *s++ = '0' + ((v >> (br - i - 1)) & 1);
+            }
+        }
+        for (w = bq - 1; w >= 0; --w) {
+            v = val[w];
+            for (i = (64 - 4); i >= 0; i -= 4) {
+                s[0] = '0' + ((v >> (i + 3)) & 1);
+                s[1] = '0' + ((v >> (i + 2)) & 1);
+                s[2] = '0' + ((v >> (i + 1)) & 1);
+                s[3] = '0' + ((v >> (i + 0)) & 1);
+                s += 4;
+            }
+        }
+        fstWriterEmitValueChange(ctx, handle, xc->outval_mem);
+    }
+}
+
+void fstWriterEmitVariableLengthValueChange(void *ctx, fstHandle handle, const void *val, uint32_t len)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    const unsigned char *buf = (const unsigned char *)val;
+
+    if (FST_LIKELY((xc) && (handle <= xc->maxhandle))) {
+        uint32_t fpos;
+        uint32_t *vm4ip;
+
+        if (FST_UNLIKELY(!xc->valpos_mem)) {
+            xc->vc_emitted = 1;
+            fstWriterCreateMmaps(xc);
+        }
+
+        handle--; /* move starting at 1 index to starting at 0 */
+        vm4ip = &(xc->valpos_mem[4 * handle]);
+
+        /* there is no initial time dump for variable length value changes */
+        if (FST_LIKELY(!vm4ip[1])) /* len of zero = variable length */
+        {
+            fpos = xc->vchg_siz;
+
+            if (FST_UNLIKELY((fpos + len + 10 + 5) > xc->vchg_alloc_siz)) {
+                xc->vchg_alloc_siz +=
+                        (xc->fst_break_add_size + len +
+                         5); /* +len added in the case of extremely long vectors and small break add sizes */
+                xc->vchg_mem = (unsigned char *)realloc(xc->vchg_mem, xc->vchg_alloc_siz);
+                if (FST_UNLIKELY(!xc->vchg_mem)) {
+                    fprintf(stderr,
+                            FST_APIMESS "Could not realloc() in fstWriterEmitVariableLengthValueChange, exiting.\n");
+                    exit(255);
+                }
+            }
+
+            xc->vchg_siz += fstWriterUint32WithVarint32AndLength(xc, &vm4ip[2], xc->tchn_idx - vm4ip[3], buf,
+                                                                 len); /* do one fwrite op only */
+            vm4ip[3] = xc->tchn_idx;
+            vm4ip[2] = fpos;
+        }
+    }
+}
+
+void fstWriterEmitTimeChange(void *ctx, uint64_t tim)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+    unsigned int i;
+    int skip = 0;
+    if (xc) {
+        if (FST_UNLIKELY(xc->is_initial_time)) {
+            if (xc->size_limit_locked) /* this resets xc->is_initial_time to one */
+            {
+                return;
+            }
+
+            if (!xc->valpos_mem) {
+                fstWriterCreateMmaps(xc);
+            }
+
+            skip = 1;
+
+            xc->firsttime = (xc->vc_emitted) ? 0 : tim;
+            xc->curtime = 0;
+            xc->vchg_mem[0] = '!';
+            xc->vchg_siz = 1;
+            fstWriterEmitSectionHeader(xc);
+            for (i = 0; i < xc->maxhandle; i++) {
+                xc->valpos_mem[4 * i + 2] = 0; /* zero out offset val */
+                xc->valpos_mem[4 * i + 3] = 0; /* zero out last time change val */
+            }
+            xc->is_initial_time = 0;
+        } else {
+            if ((xc->vchg_siz >= xc->fst_break_size) || (xc->flush_context_pending)) {
+                xc->flush_context_pending = 0;
+                fstWriterFlushContextPrivate(xc);
+                xc->tchn_cnt++;
+                fstWriterVarint(xc->tchn_handle, xc->curtime);
+            }
+        }
+
+        if (!skip) {
+            xc->tchn_idx++;
+        }
+        fstWriterVarint(xc->tchn_handle, tim - xc->curtime);
+        xc->tchn_cnt++;
+        xc->curtime = tim;
+    }
+}
+
+void fstWriterEmitDumpActive(void *ctx, int enable)
+{
+    struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
+
+    if (xc) {
+        struct fstBlackoutChain *b = (struct fstBlackoutChain *)calloc(1, sizeof(struct fstBlackoutChain));
+
+        b->tim = xc->curtime;
+        b->active = (enable != 0);
+
+        xc->num_blackouts++;
+        if (xc->blackout_curr) {
+            xc->blackout_curr->next = b;
+            xc->blackout_curr = b;
+        } else {
+            xc->blackout_head = b;
+            xc->blackout_curr = b;
+        }
+    }
+}
+
+/***********************/
+/***                 ***/
+/*** reader function ***/
+/***                 ***/
+/***********************/
+
+/*
+ * private structs
+ */
+static const char *vartypes[] = {"event",   "integer",  "parameter", "real",   "real_parameter", "reg",     "supply0",
+                                 "supply1", "time",     "tri",       "triand", "trior",          "trireg",  "tri0",
+                                 "tri1",    "wand",     "wire",      "wor",    "port",           "sparray", "realtime",
+                                 "string",  "bit",      "logic",     "int",    "shortint",       "longint", "byte",
+                                 "enum",    "shortreal"};
+
+static const char *modtypes[] = {"module",
+                                 "task",
+                                 "function",
+                                 "begin",
+                                 "fork",
+                                 "generate",
+                                 "struct",
+                                 "union",
+                                 "class",
+                                 "interface",
+                                 "package",
+                                 "program",
+                                 "vhdl_architecture",
+                                 "vhdl_procedure",
+                                 "vhdl_function",
+                                 "vhdl_record",
+                                 "vhdl_process",
+                                 "vhdl_block",
+                                 "vhdl_for_generate",
+                                 "vhdl_if_generate",
+                                 "vhdl_generate",
+                                 "vhdl_package"};
+
+static const char *attrtypes[] = {"misc", "array", "enum", "class"};
+
+static const char *arraytypes[] = {"none", "unpacked", "packed", "sparse"};
+
+static const char *enumvaluetypes[] = {"integer",
+                                       "bit",
+                                       "logic",
+                                       "int",
+                                       "shortint",
+                                       "longint",
+                                       "byte",
+                                       "unsigned_integer",
+                                       "unsigned_bit",
+                                       "unsigned_logic",
+                                       "unsigned_int",
+                                       "unsigned_shortint",
+                                       "unsigned_longint",
+                                       "unsigned_byte"};
+
+static const char *packtypes[] = {"none", "unpacked", "packed", "tagged_packed"};
+
+struct fstCurrHier
+{
+    struct fstCurrHier *prev;
+    void *user_info;
+    int len;
+};
+
+struct fstReaderContext
+{
+    /* common entries */
+
+    FILE *f, *fh;
+
+    uint64_t start_time, end_time;
+    uint64_t mem_used_by_writer;
+    uint64_t scope_count;
+    uint64_t var_count;
+    fstHandle maxhandle;
+    uint64_t num_alias;
+    uint64_t vc_section_count;
+
+    uint32_t *signal_lens;                /* maxhandle sized */
+    unsigned char *signal_typs;           /* maxhandle sized */
+    unsigned char *process_mask;          /* maxhandle-based, bitwise sized */
+    uint32_t longest_signal_value_len;    /* longest len value encountered */
+    unsigned char *temp_signal_value_buf; /* malloced for len in longest_signal_value_len */
+
+    signed char timescale;
+    unsigned char filetype;
+
+    unsigned use_vcd_extensions : 1;
+    unsigned double_endian_match : 1;
+    unsigned native_doubles_for_cb : 1;
+    unsigned contains_geom_section : 1;
+    unsigned contains_hier_section : 1;        /* valid for hier_pos */
+    unsigned contains_hier_section_lz4duo : 1; /* valid for hier_pos (contains_hier_section_lz4 always also set) */
+    unsigned contains_hier_section_lz4 : 1;    /* valid for hier_pos */
+    unsigned limit_range_valid : 1;            /* valid for limit_range_start, limit_range_end */
+
+    char version[FST_HDR_SIM_VERSION_SIZE + 1];
+    char date[FST_HDR_DATE_SIZE + 1];
+    int64_t timezero;
+
+    char *filename, *filename_unpacked;
+    fst_off_t hier_pos;
+
+    uint32_t num_blackouts;
+    uint64_t *blackout_times;
+    unsigned char *blackout_activity;
+
+    uint64_t limit_range_start, limit_range_end;
+
+    /* entries specific to read value at time functions */
+
+    unsigned rvat_data_valid : 1;
+    uint64_t *rvat_time_table;
+    uint64_t rvat_beg_tim, rvat_end_tim;
+    unsigned char *rvat_frame_data;
+    uint64_t rvat_frame_maxhandle;
+    fst_off_t *rvat_chain_table;
+    uint32_t *rvat_chain_table_lengths;
+    uint64_t rvat_vc_maxhandle;
+    fst_off_t rvat_vc_start;
+    uint32_t *rvat_sig_offs;
+    int rvat_packtype;
+
+    uint32_t rvat_chain_len;
+    unsigned char *rvat_chain_mem;
+    fstHandle rvat_chain_facidx;
+
+    uint32_t rvat_chain_pos_tidx;
+    uint32_t rvat_chain_pos_idx;
+    uint64_t rvat_chain_pos_time;
+    unsigned rvat_chain_pos_valid : 1;
+
+    /* entries specific to hierarchy traversal */
+
+    struct fstHier hier;
+    struct fstCurrHier *curr_hier;
+    fstHandle current_handle;
+    char *curr_flat_hier_nam;
+    int flat_hier_alloc_len;
+    unsigned do_rewind : 1;
+    char str_scope_nam[FST_ID_NAM_SIZ + 1];
+    char str_scope_comp[FST_ID_NAM_SIZ + 1];
+
+    unsigned fseek_failed : 1;
+
+    /* self-buffered I/O for writes */
+
+#ifndef FST_WRITEX_DISABLE
+    int writex_pos;
+    int writex_fd;
+    unsigned char writex_buf[FST_WRITEX_MAX];
+#endif
+
+    char *f_nam;
+    char *fh_nam;
+};
+
+int fstReaderFseeko(struct fstReaderContext *xc, FILE *stream, fst_off_t offset, int whence)
+{
+    int rc = fseeko(stream, offset, whence);
+
+    if (rc < 0) {
+        xc->fseek_failed = 1;
+#ifdef FST_DEBUG
+        fprintf(stderr, FST_APIMESS "Seek to #%" PRId64 " (whence = %d) failed!\n", offset, whence);
+        perror("Why");
+#endif
+    }
+
+    return (rc);
+}
+
+#ifndef FST_WRITEX_DISABLE
+static void fstWritex(struct fstReaderContext *xc, void *v, int len)
+{
+    unsigned char *s = (unsigned char *)v;
+
+    if (len) {
+        if (len < FST_WRITEX_MAX) {
+            if (xc->writex_pos + len >= FST_WRITEX_MAX) {
+                fstWritex(xc, NULL, 0);
+            }
+
+            memcpy(xc->writex_buf + xc->writex_pos, s, len);
+            xc->writex_pos += len;
+        } else {
+            fstWritex(xc, NULL, 0);
+            if (write(xc->writex_fd, s, len)) {
+            };
+        }
+    } else {
+        if (xc->writex_pos) {
+            if (write(xc->writex_fd, xc->writex_buf, xc->writex_pos)) {
+            };
+            xc->writex_pos = 0;
+        }
+    }
+}
+#endif
+
+/*
+ * scope -> flat name handling
+ */
+static void fstReaderDeallocateScopeData(struct fstReaderContext *xc)
+{
+    struct fstCurrHier *chp;
+
+    free(xc->curr_flat_hier_nam);
+    xc->curr_flat_hier_nam = NULL;
+    while (xc->curr_hier) {
+        chp = xc->curr_hier->prev;
+        free(xc->curr_hier);
+        xc->curr_hier = chp;
+    }
+}
+
+const char *fstReaderGetCurrentFlatScope(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    if (xc) {
+        return (xc->curr_flat_hier_nam ? xc->curr_flat_hier_nam : "");
+    } else {
+        return (NULL);
+    }
+}
+
+void *fstReaderGetCurrentScopeUserInfo(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    if (xc) {
+        return (xc->curr_hier ? xc->curr_hier->user_info : NULL);
+    } else {
+        return (NULL);
+    }
+}
+
+const char *fstReaderPopScope(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    if (xc && xc->curr_hier) {
+        struct fstCurrHier *ch = xc->curr_hier;
+        if (xc->curr_hier->prev) {
+            xc->curr_flat_hier_nam[xc->curr_hier->prev->len] = 0;
+        } else {
+            *xc->curr_flat_hier_nam = 0;
+        }
+        xc->curr_hier = xc->curr_hier->prev;
+        free(ch);
+        return (xc->curr_flat_hier_nam ? xc->curr_flat_hier_nam : "");
+    }
+
+    return (NULL);
+}
+
+void fstReaderResetScope(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    if (xc) {
+        while (fstReaderPopScope(xc))
+            ; /* remove any already-built scoping info */
+    }
+}
+
+const char *fstReaderPushScope(void *ctx, const char *nam, void *user_info)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    if (xc) {
+        struct fstCurrHier *ch = (struct fstCurrHier *)malloc(sizeof(struct fstCurrHier));
+        int chl = xc->curr_hier ? xc->curr_hier->len : 0;
+        int len = chl + 1 + strlen(nam);
+        if (len >= xc->flat_hier_alloc_len) {
+            xc->curr_flat_hier_nam =
+                    xc->curr_flat_hier_nam ? (char *)realloc(xc->curr_flat_hier_nam, len + 1) : (char *)malloc(len + 1);
+        }
+
+        if (chl) {
+            xc->curr_flat_hier_nam[chl] = '.';
+            strcpy(xc->curr_flat_hier_nam + chl + 1, nam);
+        } else {
+            strcpy(xc->curr_flat_hier_nam, nam);
+            len--;
+        }
+
+        ch->len = len;
+        ch->prev = xc->curr_hier;
+        ch->user_info = user_info;
+        xc->curr_hier = ch;
+        return (xc->curr_flat_hier_nam);
+    }
+
+    return (NULL);
+}
+
+int fstReaderGetCurrentScopeLen(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    if (xc && xc->curr_hier) {
+        return (xc->curr_hier->len);
+    }
+
+    return (0);
+}
+
+int fstReaderGetFseekFailed(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    if (xc) {
+        return (xc->fseek_failed != 0);
+    }
+
+    return (0);
+}
+
+/*
+ * iter mask manipulation util functions
+ */
+int fstReaderGetFacProcessMask(void *ctx, fstHandle facidx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    if (xc) {
+        facidx--;
+        if (facidx < xc->maxhandle) {
+            int process_idx = facidx / 8;
+            int process_bit = facidx & 7;
+
+            return ((xc->process_mask[process_idx] & (1 << process_bit)) != 0);
+        }
+    }
+    return (0);
+}
+
+void fstReaderSetFacProcessMask(void *ctx, fstHandle facidx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    if (xc) {
+        facidx--;
+        if (facidx < xc->maxhandle) {
+            int idx = facidx / 8;
+            int bitpos = facidx & 7;
+
+            xc->process_mask[idx] |= (1 << bitpos);
+        }
+    }
+}
+
+void fstReaderClrFacProcessMask(void *ctx, fstHandle facidx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    if (xc) {
+        facidx--;
+        if (facidx < xc->maxhandle) {
+            int idx = facidx / 8;
+            int bitpos = facidx & 7;
+
+            xc->process_mask[idx] &= (~(1 << bitpos));
+        }
+    }
+}
+
+void fstReaderSetFacProcessMaskAll(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    if (xc) {
+        memset(xc->process_mask, 0xff, (xc->maxhandle + 7) / 8);
+    }
+}
+
+void fstReaderClrFacProcessMaskAll(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    if (xc) {
+        memset(xc->process_mask, 0x00, (xc->maxhandle + 7) / 8);
+    }
+}
+
+/*
+ * various utility read/write functions
+ */
+signed char fstReaderGetTimescale(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->timescale : 0);
+}
+
+uint64_t fstReaderGetStartTime(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->start_time : 0);
+}
+
+uint64_t fstReaderGetEndTime(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->end_time : 0);
+}
+
+uint64_t fstReaderGetMemoryUsedByWriter(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->mem_used_by_writer : 0);
+}
+
+uint64_t fstReaderGetScopeCount(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->scope_count : 0);
+}
+
+uint64_t fstReaderGetVarCount(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->var_count : 0);
+}
+
+fstHandle fstReaderGetMaxHandle(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->maxhandle : 0);
+}
+
+uint64_t fstReaderGetAliasCount(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->num_alias : 0);
+}
+
+uint64_t fstReaderGetValueChangeSectionCount(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->vc_section_count : 0);
+}
+
+int fstReaderGetDoubleEndianMatchState(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->double_endian_match : 0);
+}
+
+const char *fstReaderGetVersionString(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->version : NULL);
+}
+
+const char *fstReaderGetDateString(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->date : NULL);
+}
+
+int fstReaderGetFileType(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? (int)xc->filetype : (int)FST_FT_VERILOG);
+}
+
+int64_t fstReaderGetTimezero(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->timezero : 0);
+}
+
+uint32_t fstReaderGetNumberDumpActivityChanges(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    return (xc ? xc->num_blackouts : 0);
+}
+
+uint64_t fstReaderGetDumpActivityChangeTime(void *ctx, uint32_t idx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    if (xc && (idx < xc->num_blackouts) && (xc->blackout_times)) {
+        return (xc->blackout_times[idx]);
+    } else {
+        return (0);
+    }
+}
+
+unsigned char fstReaderGetDumpActivityChangeValue(void *ctx, uint32_t idx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    if (xc && (idx < xc->num_blackouts) && (xc->blackout_activity)) {
+        return (xc->blackout_activity[idx]);
+    } else {
+        return (0);
+    }
+}
+
+void fstReaderSetLimitTimeRange(void *ctx, uint64_t start_time, uint64_t end_time)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    if (xc) {
+        xc->limit_range_valid = 1;
+        xc->limit_range_start = start_time;
+        xc->limit_range_end = end_time;
+    }
+}
+
+void fstReaderSetUnlimitedTimeRange(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    if (xc) {
+        xc->limit_range_valid = 0;
+    }
+}
+
+void fstReaderSetVcdExtensions(void *ctx, int enable)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    if (xc) {
+        xc->use_vcd_extensions = (enable != 0);
+    }
+}
+
+void fstReaderIterBlocksSetNativeDoublesOnCallback(void *ctx, int enable)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    if (xc) {
+        xc->native_doubles_for_cb = (enable != 0);
+    }
+}
+
+/*
+ * hierarchy processing
+ */
+static void fstVcdID(char *buf, unsigned int value)
+{
+    char *pnt = buf;
+
+    /* zero is illegal for a value...it is assumed they start at one */
+    while (value) {
+        value--;
+        *(pnt++) = (char)('!' + value % 94);
+        value = value / 94;
+    }
+
+    *pnt = 0;
+}
+
+static int fstVcdIDForFwrite(char *buf, unsigned int value)
+{
+    char *pnt = buf;
+
+    /* zero is illegal for a value...it is assumed they start at one */
+    while (value) {
+        value--;
+        *(pnt++) = (char)('!' + value % 94);
+        value = value / 94;
+    }
+
+    return (pnt - buf);
+}
+
+static int fstReaderRecreateHierFile(struct fstReaderContext *xc)
+{
+    int pass_status = 1;
+
+    if (!xc->fh) {
+        fst_off_t offs_cache = ftello(xc->f);
+        char *fnam = (char *)malloc(strlen(xc->filename) + 6 + 16 + 32 + 1);
+        unsigned char *mem = (unsigned char *)malloc(FST_GZIO_LEN);
+        fst_off_t hl, uclen;
+        fst_off_t clen = 0;
+        gzFile zhandle = NULL;
+        int zfd;
+        int htyp = FST_BL_SKIP;
+
+        /* can't handle both set at once should never happen in a real file */
+        if (!xc->contains_hier_section_lz4 && xc->contains_hier_section) {
+            htyp = FST_BL_HIER;
+        } else if (xc->contains_hier_section_lz4 && !xc->contains_hier_section) {
+            htyp = xc->contains_hier_section_lz4duo ? FST_BL_HIER_LZ4DUO : FST_BL_HIER_LZ4;
+        }
+
+        sprintf(fnam, "%s.hier_%d_%p", xc->filename, getpid(), (void *)xc);
+        fstReaderFseeko(xc, xc->f, xc->hier_pos, SEEK_SET);
+        uclen = fstReaderUint64(xc->f);
+#ifndef __MINGW32__
+        fflush(xc->f);
+#endif
+        if (htyp == FST_BL_HIER) {
+            fstReaderFseeko(xc, xc->f, xc->hier_pos, SEEK_SET);
+            uclen = fstReaderUint64(xc->f);
+#ifndef __MINGW32__
+            fflush(xc->f);
+#endif
+            zfd = dup(fileno(xc->f));
+            zhandle = gzdopen(zfd, "rb");
+            if (!zhandle) {
+                close(zfd);
+                free(mem);
+                free(fnam);
+                return (0);
+            }
+        } else if ((htyp == FST_BL_HIER_LZ4) || (htyp == FST_BL_HIER_LZ4DUO)) {
+            fstReaderFseeko(xc, xc->f, xc->hier_pos - 8, SEEK_SET); /* get section len */
+            clen = fstReaderUint64(xc->f) - 16;
+            uclen = fstReaderUint64(xc->f);
+#ifndef __MINGW32__
+            fflush(xc->f);
+#endif
+        }
+
+#ifndef __MINGW32__
+        xc->fh = fopen(fnam, "w+b");
+        if (!xc->fh)
+#endif
+        {
+            xc->fh = tmpfile_open(&xc->fh_nam);
+            free(fnam);
+            fnam = NULL;
+            if (!xc->fh) {
+                tmpfile_close(&xc->fh, &xc->fh_nam);
+                free(mem);
+                return (0);
+            }
+        }
+
+#ifndef __MINGW32__
+        if (fnam)
+            unlink(fnam);
+#endif
+
+        if (htyp == FST_BL_HIER) {
+            for (hl = 0; hl < uclen; hl += FST_GZIO_LEN) {
+                size_t len = ((uclen - hl) > FST_GZIO_LEN) ? FST_GZIO_LEN : (uclen - hl);
+                size_t gzreadlen = gzread(zhandle, mem, len); /* rc should equal len... */
+                size_t fwlen;
+
+                if (gzreadlen != len) {
+                    pass_status = 0;
+                    break;
+                }
+
+                fwlen = fstFwrite(mem, len, 1, xc->fh);
+                if (fwlen != 1) {
+                    pass_status = 0;
+                    break;
+                }
+            }
+            gzclose(zhandle);
+        } else if (htyp == FST_BL_HIER_LZ4DUO) {
+            unsigned char *lz4_cmem = (unsigned char *)malloc(clen);
+            unsigned char *lz4_ucmem = (unsigned char *)malloc(uclen);
+            unsigned char *lz4_ucmem2;
+            uint64_t uclen2;
+            int skiplen2 = 0;
+
+            fstFread(lz4_cmem, clen, 1, xc->f);
+
+            uclen2 = fstGetVarint64(lz4_cmem, &skiplen2);
+            lz4_ucmem2 = (unsigned char *)malloc(uclen2);
+            pass_status =
+                    (uclen2 == (uint64_t)LZ4_decompress_safe_partial((char *)lz4_cmem + skiplen2, (char *)lz4_ucmem2,
+                                                                     clen - skiplen2, uclen2, uclen2));
+            if (pass_status) {
+                pass_status = (uclen == LZ4_decompress_safe_partial((char *)lz4_ucmem2, (char *)lz4_ucmem, uclen2,
+                                                                    uclen, uclen));
+
+                if (fstFwrite(lz4_ucmem, uclen, 1, xc->fh) != 1) {
+                    pass_status = 0;
+                }
+            }
+
+            free(lz4_ucmem2);
+            free(lz4_ucmem);
+            free(lz4_cmem);
+        } else if (htyp == FST_BL_HIER_LZ4) {
+            unsigned char *lz4_cmem = (unsigned char *)malloc(clen);
+            unsigned char *lz4_ucmem = (unsigned char *)malloc(uclen);
+
+            fstFread(lz4_cmem, clen, 1, xc->f);
+            pass_status =
+                    (uclen == LZ4_decompress_safe_partial((char *)lz4_cmem, (char *)lz4_ucmem, clen, uclen, uclen));
+
+            if (fstFwrite(lz4_ucmem, uclen, 1, xc->fh) != 1) {
+                pass_status = 0;
+            }
+
+            free(lz4_ucmem);
+            free(lz4_cmem);
+        } else /* FST_BL_SKIP */
+        {
+            pass_status = 0;
+            if (xc->fh) {
+                fclose(xc->fh);
+                xc->fh = NULL; /* needed in case .hier file is missing and there are no hier sections */
+            }
+        }
+
+        free(mem);
+        free(fnam);
+
+        fstReaderFseeko(xc, xc->f, offs_cache, SEEK_SET);
+    }
+
+    return (pass_status);
+}
+
+int fstReaderIterateHierRewind(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    int pass_status = 0;
+
+    if (xc) {
+        pass_status = 1;
+        if (!xc->fh) {
+            pass_status = fstReaderRecreateHierFile(xc);
+        }
+
+        xc->do_rewind = 1;
+    }
+
+    return (pass_status);
+}
+
+struct fstHier *fstReaderIterateHier(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    int isfeof;
+    fstHandle alias;
+    char *pnt;
+    int ch;
+
+    if (!xc)
+        return (NULL);
+
+    if (!xc->fh) {
+        if (!fstReaderRecreateHierFile(xc)) {
+            return (NULL);
+        }
+    }
+
+    if (xc->do_rewind) {
+        xc->do_rewind = 0;
+        xc->current_handle = 0;
+        fstReaderFseeko(xc, xc->fh, 0, SEEK_SET);
+        clearerr(xc->fh);
+    }
+
+    if (!(isfeof = feof(xc->fh))) {
+        int tag = fgetc(xc->fh);
+        switch (tag) {
+        case FST_ST_VCD_SCOPE:
+            xc->hier.htyp = FST_HT_SCOPE;
+            xc->hier.u.scope.typ = fgetc(xc->fh);
+            xc->hier.u.scope.name = pnt = xc->str_scope_nam;
+            while ((ch = fgetc(xc->fh))) {
+                *(pnt++) = ch;
+            }; /* scopename */
+            *pnt = 0;
+            xc->hier.u.scope.name_length = pnt - xc->hier.u.scope.name;
+
+            xc->hier.u.scope.component = pnt = xc->str_scope_comp;
+            while ((ch = fgetc(xc->fh))) {
+                *(pnt++) = ch;
+            }; /* scopecomp */
+            *pnt = 0;
+            xc->hier.u.scope.component_length = pnt - xc->hier.u.scope.component;
+            break;
+
+        case FST_ST_VCD_UPSCOPE:
+            xc->hier.htyp = FST_HT_UPSCOPE;
+            break;
+
+        case FST_ST_GEN_ATTRBEGIN:
+            xc->hier.htyp = FST_HT_ATTRBEGIN;
+            xc->hier.u.attr.typ = fgetc(xc->fh);
+            xc->hier.u.attr.subtype = fgetc(xc->fh);
+            xc->hier.u.attr.name = pnt = xc->str_scope_nam;
+            while ((ch = fgetc(xc->fh))) {
+                *(pnt++) = ch;
+            }; /* scopename */
+            *pnt = 0;
+            xc->hier.u.attr.name_length = pnt - xc->hier.u.scope.name;
+
+            xc->hier.u.attr.arg = fstReaderVarint64(xc->fh);
+
+            if (xc->hier.u.attr.typ == FST_AT_MISC) {
+                if ((xc->hier.u.attr.subtype == FST_MT_SOURCESTEM) || (xc->hier.u.attr.subtype == FST_MT_SOURCEISTEM)) {
+                    int sidx_skiplen_dummy = 0;
+                    xc->hier.u.attr.arg_from_name =
+                            fstGetVarint64((unsigned char *)xc->str_scope_nam, &sidx_skiplen_dummy);
+                }
+            }
+            break;
+
+        case FST_ST_GEN_ATTREND:
+            xc->hier.htyp = FST_HT_ATTREND;
+            break;
+
+        case FST_VT_VCD_EVENT:
+        case FST_VT_VCD_INTEGER:
+        case FST_VT_VCD_PARAMETER:
+        case FST_VT_VCD_REAL:
+        case FST_VT_VCD_REAL_PARAMETER:
+        case FST_VT_VCD_REG:
+        case FST_VT_VCD_SUPPLY0:
+        case FST_VT_VCD_SUPPLY1:
+        case FST_VT_VCD_TIME:
+        case FST_VT_VCD_TRI:
+        case FST_VT_VCD_TRIAND:
+        case FST_VT_VCD_TRIOR:
+        case FST_VT_VCD_TRIREG:
+        case FST_VT_VCD_TRI0:
+        case FST_VT_VCD_TRI1:
+        case FST_VT_VCD_WAND:
+        case FST_VT_VCD_WIRE:
+        case FST_VT_VCD_WOR:
+        case FST_VT_VCD_PORT:
+        case FST_VT_VCD_SPARRAY:
+        case FST_VT_VCD_REALTIME:
+        case FST_VT_GEN_STRING:
+        case FST_VT_SV_BIT:
+        case FST_VT_SV_LOGIC:
+        case FST_VT_SV_INT:
+        case FST_VT_SV_SHORTINT:
+        case FST_VT_SV_LONGINT:
+        case FST_VT_SV_BYTE:
+        case FST_VT_SV_ENUM:
+        case FST_VT_SV_SHORTREAL:
+            xc->hier.htyp = FST_HT_VAR;
+            xc->hier.u.var.svt_workspace = FST_SVT_NONE;
+            xc->hier.u.var.sdt_workspace = FST_SDT_NONE;
+            xc->hier.u.var.sxt_workspace = 0;
+            xc->hier.u.var.typ = tag;
+            xc->hier.u.var.direction = fgetc(xc->fh);
+            xc->hier.u.var.name = pnt = xc->str_scope_nam;
+            while ((ch = fgetc(xc->fh))) {
+                *(pnt++) = ch;
+            }; /* varname */
+            *pnt = 0;
+            xc->hier.u.var.name_length = pnt - xc->hier.u.var.name;
+            xc->hier.u.var.length = fstReaderVarint32(xc->fh);
+            if (tag == FST_VT_VCD_PORT) {
+                xc->hier.u.var.length -= 2; /* removal of delimiting spaces */
+                xc->hier.u.var.length /= 3; /* port -> signal size adjust */
+            }
+
+            alias = fstReaderVarint32(xc->fh);
+
+            if (!alias) {
+                xc->current_handle++;
+                xc->hier.u.var.handle = xc->current_handle;
+                xc->hier.u.var.is_alias = 0;
+            } else {
+                xc->hier.u.var.handle = alias;
+                xc->hier.u.var.is_alias = 1;
+            }
+
+            break;
+
+        default:
+            isfeof = 1;
+            break;
+        }
+    }
+
+    return (!isfeof ? &xc->hier : NULL);
+}
+
+int fstReaderProcessHier(void *ctx, FILE *fv)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    char *str;
+    char *pnt;
+    int ch, scopetype;
+    int vartype;
+    uint32_t len, alias;
+    /* uint32_t maxvalpos=0; */
+    unsigned int num_signal_dyn = 65536;
+    int attrtype, subtype;
+    uint64_t attrarg;
+    fstHandle maxhandle_scanbuild;
+
+    if (!xc)
+        return (0);
+
+    xc->longest_signal_value_len = 32; /* arbitrarily set at 32...this is much longer than an expanded double */
+
+    if (!xc->fh) {
+        if (!fstReaderRecreateHierFile(xc)) {
+            return (0);
+        }
+    }
+
+    str = (char *)malloc(FST_ID_NAM_ATTR_SIZ + 1);
+
+    if (fv) {
+        char time_dimension[2] = {0, 0};
+        int time_scale = 1;
+
+        fprintf(fv, "$date\n\t%s\n$end\n", xc->date);
+        fprintf(fv, "$version\n\t%s\n$end\n", xc->version);
+        if (xc->timezero)
+            fprintf(fv, "$timezero\n\t%" PRId64 "\n$end\n", xc->timezero);
+
+        switch (xc->timescale) {
+        case 2:
+            time_scale = 100;
+            time_dimension[0] = 0;
+            break;
+        case 1:
+            time_scale = 10; /* fallthrough */
+        case 0:
+            time_dimension[0] = 0;
+            break;
+
+        case -1:
+            time_scale = 100;
+            time_dimension[0] = 'm';
+            break;
+        case -2:
+            time_scale = 10; /* fallthrough */
+        case -3:
+            time_dimension[0] = 'm';
+            break;
+
+        case -4:
+            time_scale = 100;
+            time_dimension[0] = 'u';
+            break;
+        case -5:
+            time_scale = 10; /* fallthrough */
+        case -6:
+            time_dimension[0] = 'u';
+            break;
+
+        case -10:
+            time_scale = 100;
+            time_dimension[0] = 'p';
+            break;
+        case -11:
+            time_scale = 10; /* fallthrough */
+        case -12:
+            time_dimension[0] = 'p';
+            break;
+
+        case -13:
+            time_scale = 100;
+            time_dimension[0] = 'f';
+            break;
+        case -14:
+            time_scale = 10; /* fallthrough */
+        case -15:
+            time_dimension[0] = 'f';
+            break;
+
+        case -16:
+            time_scale = 100;
+            time_dimension[0] = 'a';
+            break;
+        case -17:
+            time_scale = 10; /* fallthrough */
+        case -18:
+            time_dimension[0] = 'a';
+            break;
+
+        case -19:
+            time_scale = 100;
+            time_dimension[0] = 'z';
+            break;
+        case -20:
+            time_scale = 10; /* fallthrough */
+        case -21:
+            time_dimension[0] = 'z';
+            break;
+
+        case -7:
+            time_scale = 100;
+            time_dimension[0] = 'n';
+            break;
+        case -8:
+            time_scale = 10; /* fallthrough */
+        case -9:
+        default:
+            time_dimension[0] = 'n';
+            break;
+        }
+
+        if (fv)
+            fprintf(fv, "$timescale\n\t%d%ss\n$end\n", time_scale, time_dimension);
+    }
+
+    xc->maxhandle = 0;
+    xc->num_alias = 0;
+
+    free(xc->signal_lens);
+    xc->signal_lens = (uint32_t *)malloc(num_signal_dyn * sizeof(uint32_t));
+
+    free(xc->signal_typs);
+    xc->signal_typs = (unsigned char *)malloc(num_signal_dyn * sizeof(unsigned char));
+
+    fstReaderFseeko(xc, xc->fh, 0, SEEK_SET);
+    while (!feof(xc->fh)) {
+        int tag = fgetc(xc->fh);
+        switch (tag) {
+        case FST_ST_VCD_SCOPE:
+            scopetype = fgetc(xc->fh);
+            if ((scopetype < FST_ST_MIN) || (scopetype > FST_ST_MAX))
+                scopetype = FST_ST_VCD_MODULE;
+            pnt = str;
+            while ((ch = fgetc(xc->fh))) {
+                *(pnt++) = ch;
+            }; /* scopename */
+            *pnt = 0;
+            while (fgetc(xc->fh)) {
+            }; /* scopecomp */
+
+            if (fv)
+                fprintf(fv, "$scope %s %s $end\n", modtypes[scopetype], str);
+            break;
+
+        case FST_ST_VCD_UPSCOPE:
+            if (fv)
+                fprintf(fv, "$upscope $end\n");
+            break;
+
+        case FST_ST_GEN_ATTRBEGIN:
+            attrtype = fgetc(xc->fh);
+            subtype = fgetc(xc->fh);
+            pnt = str;
+            while ((ch = fgetc(xc->fh))) {
+                *(pnt++) = ch;
+            }; /* attrname */
+            *pnt = 0;
+
+            if (!str[0]) {
+                strcpy(str, "\"\"");
+            }
+
+            attrarg = fstReaderVarint64(xc->fh);
+
+            if (fv && xc->use_vcd_extensions) {
+                switch (attrtype) {
+                case FST_AT_ARRAY:
+                    if ((subtype < FST_AR_NONE) || (subtype > FST_AR_MAX))
+                        subtype = FST_AR_NONE;
+                    fprintf(fv, "$attrbegin %s %s %s %" PRId64 " $end\n", attrtypes[attrtype], arraytypes[subtype], str,
+                            attrarg);
+                    break;
+                case FST_AT_ENUM:
+                    if ((subtype < FST_EV_SV_INTEGER) || (subtype > FST_EV_MAX))
+                        subtype = FST_EV_SV_INTEGER;
+                    fprintf(fv, "$attrbegin %s %s %s %" PRId64 " $end\n", attrtypes[attrtype], enumvaluetypes[subtype],
+                            str, attrarg);
+                    break;
+                case FST_AT_PACK:
+                    if ((subtype < FST_PT_NONE) || (subtype > FST_PT_MAX))
+                        subtype = FST_PT_NONE;
+                    fprintf(fv, "$attrbegin %s %s %s %" PRId64 " $end\n", attrtypes[attrtype], packtypes[subtype], str,
+                            attrarg);
+                    break;
+                case FST_AT_MISC:
+                default:
+                    attrtype = FST_AT_MISC;
+                    if (subtype == FST_MT_COMMENT) {
+                        fprintf(fv, "$comment\n\t%s\n$end\n", str);
+                    } else {
+                        if ((subtype == FST_MT_SOURCESTEM) || (subtype == FST_MT_SOURCEISTEM)) {
+                            int sidx_skiplen_dummy = 0;
+                            uint64_t sidx = fstGetVarint64((unsigned char *)str, &sidx_skiplen_dummy);
+
+                            fprintf(fv, "$attrbegin %s %02x %" PRId64 " %" PRId64 " $end\n", attrtypes[attrtype],
+                                    subtype, sidx, attrarg);
+                        } else {
+                            fprintf(fv, "$attrbegin %s %02x %s %" PRId64 " $end\n", attrtypes[attrtype], subtype, str,
+                                    attrarg);
+                        }
+                    }
+                    break;
+                }
+            }
+            break;
+
+        case FST_ST_GEN_ATTREND:
+            if (fv && xc->use_vcd_extensions)
+                fprintf(fv, "$attrend $end\n");
+            break;
+
+        case FST_VT_VCD_EVENT:
+        case FST_VT_VCD_INTEGER:
+        case FST_VT_VCD_PARAMETER:
+        case FST_VT_VCD_REAL:
+        case FST_VT_VCD_REAL_PARAMETER:
+        case FST_VT_VCD_REG:
+        case FST_VT_VCD_SUPPLY0:
+        case FST_VT_VCD_SUPPLY1:
+        case FST_VT_VCD_TIME:
+        case FST_VT_VCD_TRI:
+        case FST_VT_VCD_TRIAND:
+        case FST_VT_VCD_TRIOR:
+        case FST_VT_VCD_TRIREG:
+        case FST_VT_VCD_TRI0:
+        case FST_VT_VCD_TRI1:
+        case FST_VT_VCD_WAND:
+        case FST_VT_VCD_WIRE:
+        case FST_VT_VCD_WOR:
+        case FST_VT_VCD_PORT:
+        case FST_VT_VCD_SPARRAY:
+        case FST_VT_VCD_REALTIME:
+        case FST_VT_GEN_STRING:
+        case FST_VT_SV_BIT:
+        case FST_VT_SV_LOGIC:
+        case FST_VT_SV_INT:
+        case FST_VT_SV_SHORTINT:
+        case FST_VT_SV_LONGINT:
+        case FST_VT_SV_BYTE:
+        case FST_VT_SV_ENUM:
+        case FST_VT_SV_SHORTREAL:
+            vartype = tag;
+            /* vardir = */ fgetc(xc->fh); /* unused in VCD reader, but need to advance read pointer */
+            pnt = str;
+            while ((ch = fgetc(xc->fh))) {
+                *(pnt++) = ch;
+            }; /* varname */
+            *pnt = 0;
+            len = fstReaderVarint32(xc->fh);
+            alias = fstReaderVarint32(xc->fh);
+
+            if (!alias) {
+                if (xc->maxhandle == num_signal_dyn) {
+                    num_signal_dyn *= 2;
+                    xc->signal_lens = (uint32_t *)realloc(xc->signal_lens, num_signal_dyn * sizeof(uint32_t));
+                    xc->signal_typs = (unsigned char *)realloc(xc->signal_typs, num_signal_dyn * sizeof(unsigned char));
+                }
+                xc->signal_lens[xc->maxhandle] = len;
+                xc->signal_typs[xc->maxhandle] = vartype;
+
+                /* maxvalpos+=len; */
+                if (len > xc->longest_signal_value_len) {
+                    xc->longest_signal_value_len = len;
+                }
+
+                if ((vartype == FST_VT_VCD_REAL) || (vartype == FST_VT_VCD_REAL_PARAMETER) ||
+                    (vartype == FST_VT_VCD_REALTIME) || (vartype == FST_VT_SV_SHORTREAL)) {
+                    len = (vartype != FST_VT_SV_SHORTREAL) ? 64 : 32;
+                    xc->signal_typs[xc->maxhandle] = FST_VT_VCD_REAL;
+                }
+                if (fv) {
+                    char vcdid_buf[16];
+                    uint32_t modlen = (vartype != FST_VT_VCD_PORT) ? len : ((len - 2) / 3);
+                    fstVcdID(vcdid_buf, xc->maxhandle + 1);
+                    fprintf(fv, "$var %s %" PRIu32 " %s %s $end\n", vartypes[vartype], modlen, vcdid_buf, str);
+                }
+                xc->maxhandle++;
+            } else {
+                if ((vartype == FST_VT_VCD_REAL) || (vartype == FST_VT_VCD_REAL_PARAMETER) ||
+                    (vartype == FST_VT_VCD_REALTIME) || (vartype == FST_VT_SV_SHORTREAL)) {
+                    len = (vartype != FST_VT_SV_SHORTREAL) ? 64 : 32;
+                    xc->signal_typs[xc->maxhandle] = FST_VT_VCD_REAL;
+                }
+                if (fv) {
+                    char vcdid_buf[16];
+                    uint32_t modlen = (vartype != FST_VT_VCD_PORT) ? len : ((len - 2) / 3);
+                    fstVcdID(vcdid_buf, alias);
+                    fprintf(fv, "$var %s %" PRIu32 " %s %s $end\n", vartypes[vartype], modlen, vcdid_buf, str);
+                }
+                xc->num_alias++;
+            }
+
+            break;
+
+        default:
+            break;
+        }
+    }
+    if (fv)
+        fprintf(fv, "$enddefinitions $end\n");
+
+    maxhandle_scanbuild = xc->maxhandle ? xc->maxhandle
+                                        : 1; /*scan-build warning suppression, in reality we have at least one signal */
+
+    xc->signal_lens = (uint32_t *)realloc(xc->signal_lens, maxhandle_scanbuild * sizeof(uint32_t));
+    xc->signal_typs = (unsigned char *)realloc(xc->signal_typs, maxhandle_scanbuild * sizeof(unsigned char));
+
+    free(xc->process_mask);
+    xc->process_mask = (unsigned char *)calloc(1, (maxhandle_scanbuild + 7) / 8);
+
+    free(xc->temp_signal_value_buf);
+    xc->temp_signal_value_buf = (unsigned char *)malloc(xc->longest_signal_value_len + 1);
+
+    xc->var_count = xc->maxhandle + xc->num_alias;
+
+    free(str);
+    return (1);
+}
+
+/*
+ * reader file open/close functions
+ */
+int fstReaderInit(struct fstReaderContext *xc)
+{
+    fst_off_t blkpos = 0;
+    fst_off_t endfile;
+    uint64_t seclen;
+    int sectype;
+    uint64_t vc_section_count_actual = 0;
+    int hdr_incomplete = 0;
+    int hdr_seen = 0;
+    int gzread_pass_status = 1;
+
+    sectype = fgetc(xc->f);
+    if (sectype == FST_BL_ZWRAPPER) {
+        FILE *fcomp;
+        fst_off_t offpnt, uclen;
+        char gz_membuf[FST_GZIO_LEN];
+        gzFile zhandle;
+        int zfd;
+        int flen = strlen(xc->filename);
+        char *hf;
+
+        seclen = fstReaderUint64(xc->f);
+        uclen = fstReaderUint64(xc->f);
+
+        if (!seclen)
+            return (0); /* not finished compressing, this is a failed read */
+
+        hf = (char *)calloc(1, flen + 16 + 32 + 1);
+
+        sprintf(hf, "%s.upk_%d_%p", xc->filename, getpid(), (void *)xc);
+        fcomp = fopen(hf, "w+b");
+        if (!fcomp) {
+            fcomp = tmpfile_open(&xc->f_nam);
+            free(hf);
+            hf = NULL;
+            if (!fcomp) {
+                tmpfile_close(&fcomp, &xc->f_nam);
+                return (0);
+            }
+        }
+
+#if defined(FST_MACOSX)
+        setvbuf(fcomp, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */
+#endif
+
+#ifdef __MINGW32__
+        setvbuf(fcomp, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */
+        xc->filename_unpacked = hf;
+#else
+        if (hf) {
+            unlink(hf);
+            free(hf);
+        }
+#endif
+
+        fstReaderFseeko(xc, xc->f, 1 + 8 + 8, SEEK_SET);
+#ifndef __MINGW32__
+        fflush(xc->f);
+#endif
+
+        zfd = dup(fileno(xc->f));
+        zhandle = gzdopen(zfd, "rb");
+        if (zhandle) {
+            for (offpnt = 0; offpnt < uclen; offpnt += FST_GZIO_LEN) {
+                size_t this_len = ((uclen - offpnt) > FST_GZIO_LEN) ? FST_GZIO_LEN : (uclen - offpnt);
+                size_t gzreadlen = gzread(zhandle, gz_membuf, this_len);
+                size_t fwlen;
+
+                if (gzreadlen != this_len) {
+                    gzread_pass_status = 0;
+                    break;
+                }
+                fwlen = fstFwrite(gz_membuf, this_len, 1, fcomp);
+                if (fwlen != 1) {
+                    gzread_pass_status = 0;
+                    break;
+                }
+            }
+            gzclose(zhandle);
+        } else {
+            close(zfd);
+        }
+        fflush(fcomp);
+        fclose(xc->f);
+        xc->f = fcomp;
+    }
+
+    if (gzread_pass_status) {
+        fstReaderFseeko(xc, xc->f, 0, SEEK_END);
+        endfile = ftello(xc->f);
+
+        while (blkpos < endfile) {
+            fstReaderFseeko(xc, xc->f, blkpos, SEEK_SET);
+
+            sectype = fgetc(xc->f);
+            seclen = fstReaderUint64(xc->f);
+
+            if (sectype == EOF) {
+                break;
+            }
+
+            if ((hdr_incomplete) && (!seclen)) {
+                break;
+            }
+
+            if (!hdr_seen && (sectype != FST_BL_HDR)) {
+                break;
+            }
+
+            blkpos++;
+            if (sectype == FST_BL_HDR) {
+                if (!hdr_seen) {
+                    int ch;
+                    double dcheck;
+
+                    xc->start_time = fstReaderUint64(xc->f);
+                    xc->end_time = fstReaderUint64(xc->f);
+
+                    hdr_incomplete = (xc->start_time == 0) && (xc->end_time == 0);
+
+                    fstFread(&dcheck, 8, 1, xc->f);
+                    xc->double_endian_match = (dcheck == FST_DOUBLE_ENDTEST);
+                    if (!xc->double_endian_match) {
+                        union
+                        {
+                            unsigned char rvs_buf[8];
+                            double d;
+                        } vu;
+
+                        unsigned char *dcheck_alias = (unsigned char *)&dcheck;
+                        int rvs_idx;
+
+                        for (rvs_idx = 0; rvs_idx < 8; rvs_idx++) {
+                            vu.rvs_buf[rvs_idx] = dcheck_alias[7 - rvs_idx];
+                        }
+                        if (vu.d != FST_DOUBLE_ENDTEST) {
+                            break; /* either corrupt file or wrong architecture (offset +33 also functions as matchword)
+                                    */
+                        }
+                    }
+
+                    hdr_seen = 1;
+
+                    xc->mem_used_by_writer = fstReaderUint64(xc->f);
+                    xc->scope_count = fstReaderUint64(xc->f);
+                    xc->var_count = fstReaderUint64(xc->f);
+                    xc->maxhandle = fstReaderUint64(xc->f);
+                    xc->num_alias = xc->var_count - xc->maxhandle;
+                    xc->vc_section_count = fstReaderUint64(xc->f);
+                    ch = fgetc(xc->f);
+                    xc->timescale = (signed char)ch;
+                    fstFread(xc->version, FST_HDR_SIM_VERSION_SIZE, 1, xc->f);
+                    xc->version[FST_HDR_SIM_VERSION_SIZE] = 0;
+                    fstFread(xc->date, FST_HDR_DATE_SIZE, 1, xc->f);
+                    xc->date[FST_HDR_DATE_SIZE] = 0;
+                    ch = fgetc(xc->f);
+                    xc->filetype = (unsigned char)ch;
+                    xc->timezero = fstReaderUint64(xc->f);
+                }
+            } else if ((sectype == FST_BL_VCDATA) || (sectype == FST_BL_VCDATA_DYN_ALIAS) ||
+                       (sectype == FST_BL_VCDATA_DYN_ALIAS2)) {
+                if (hdr_incomplete) {
+                    uint64_t bt = fstReaderUint64(xc->f);
+                    xc->end_time = fstReaderUint64(xc->f);
+
+                    if (!vc_section_count_actual) {
+                        xc->start_time = bt;
+                    }
+                }
+
+                vc_section_count_actual++;
+            } else if (sectype == FST_BL_GEOM) {
+                if (!hdr_incomplete) {
+                    uint64_t clen = seclen - 24;
+                    uint64_t uclen = fstReaderUint64(xc->f);
+                    unsigned char *ucdata = (unsigned char *)malloc(uclen);
+                    unsigned char *pnt = ucdata;
+                    unsigned int i;
+
+                    xc->contains_geom_section = 1;
+                    xc->maxhandle = fstReaderUint64(xc->f);
+                    xc->longest_signal_value_len =
+                            32; /* arbitrarily set at 32...this is much longer than an expanded double */
+
+                    free(xc->process_mask);
+                    xc->process_mask = (unsigned char *)calloc(1, (xc->maxhandle + 7) / 8);
+
+                    if (clen != uclen) {
+                        unsigned char *cdata = (unsigned char *)malloc(clen);
+                        unsigned long destlen = uclen;
+                        unsigned long sourcelen = clen;
+                        int rc;
+
+                        fstFread(cdata, clen, 1, xc->f);
+                        rc = uncompress(ucdata, &destlen, cdata, sourcelen);
+
+                        if (rc != Z_OK) {
+                            fprintf(stderr, FST_APIMESS "fstReaderInit(), geom uncompress rc = %d, exiting.\n", rc);
+                            exit(255);
+                        }
+
+                        free(cdata);
+                    } else {
+                        fstFread(ucdata, uclen, 1, xc->f);
+                    }
+
+                    free(xc->signal_lens);
+                    xc->signal_lens = (uint32_t *)malloc(sizeof(uint32_t) * xc->maxhandle);
+                    free(xc->signal_typs);
+                    xc->signal_typs = (unsigned char *)malloc(sizeof(unsigned char) * xc->maxhandle);
+
+                    for (i = 0; i < xc->maxhandle; i++) {
+                        int skiplen;
+                        uint64_t val = fstGetVarint32(pnt, &skiplen);
+
+                        pnt += skiplen;
+
+                        if (val) {
+                            xc->signal_lens[i] = (val != 0xFFFFFFFF) ? val : 0;
+                            xc->signal_typs[i] = FST_VT_VCD_WIRE;
+                            if (xc->signal_lens[i] > xc->longest_signal_value_len) {
+                                xc->longest_signal_value_len = xc->signal_lens[i];
+                            }
+                        } else {
+                            xc->signal_lens[i] = 8; /* backpatch in real */
+                            xc->signal_typs[i] = FST_VT_VCD_REAL;
+                            /* xc->longest_signal_value_len handled above by overly large init size */
+                        }
+                    }
+
+                    free(xc->temp_signal_value_buf);
+                    xc->temp_signal_value_buf = (unsigned char *)malloc(xc->longest_signal_value_len + 1);
+
+                    free(ucdata);
+                }
+            } else if (sectype == FST_BL_HIER) {
+                xc->contains_hier_section = 1;
+                xc->hier_pos = ftello(xc->f);
+            } else if (sectype == FST_BL_HIER_LZ4DUO) {
+                xc->contains_hier_section_lz4 = 1;
+                xc->contains_hier_section_lz4duo = 1;
+                xc->hier_pos = ftello(xc->f);
+            } else if (sectype == FST_BL_HIER_LZ4) {
+                xc->contains_hier_section_lz4 = 1;
+                xc->hier_pos = ftello(xc->f);
+            } else if (sectype == FST_BL_BLACKOUT) {
+                uint32_t i;
+                uint64_t cur_bl = 0;
+                uint64_t delta;
+
+                xc->num_blackouts = fstReaderVarint32(xc->f);
+                free(xc->blackout_times);
+                xc->blackout_times = (uint64_t *)calloc(xc->num_blackouts, sizeof(uint64_t));
+                free(xc->blackout_activity);
+                xc->blackout_activity = (unsigned char *)calloc(xc->num_blackouts, sizeof(unsigned char));
+
+                for (i = 0; i < xc->num_blackouts; i++) {
+                    xc->blackout_activity[i] = fgetc(xc->f) != 0;
+                    delta = fstReaderVarint64(xc->f);
+                    cur_bl += delta;
+                    xc->blackout_times[i] = cur_bl;
+                }
+            }
+
+            blkpos += seclen;
+            if (!hdr_seen)
+                break;
+        }
+
+        if (hdr_seen) {
+            if (xc->vc_section_count != vc_section_count_actual) {
+                xc->vc_section_count = vc_section_count_actual;
+            }
+
+            if (!xc->contains_geom_section) {
+                fstReaderProcessHier(xc, NULL); /* recreate signal_lens/signal_typs info */
+            }
+        }
+    }
+
+    return (hdr_seen);
+}
+
+void *fstReaderOpenForUtilitiesOnly(void)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)calloc(1, sizeof(struct fstReaderContext));
+
+    return (xc);
+}
+
+void *fstReaderOpen(const char *nam)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)calloc(1, sizeof(struct fstReaderContext));
+
+    if ((!nam) || (!(xc->f = fopen(nam, "rb")))) {
+        free(xc);
+        xc = NULL;
+    } else {
+        int flen = strlen(nam);
+        char *hf = (char *)calloc(1, flen + 6);
+        int rc;
+
+#if defined(__MINGW32__) || defined(FST_MACOSX)
+        setvbuf(xc->f, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */
+#endif
+
+        memcpy(hf, nam, flen);
+        strcpy(hf + flen, ".hier");
+        xc->fh = fopen(hf, "rb");
+
+        free(hf);
+        xc->filename = strdup(nam);
+        rc = fstReaderInit(xc);
+
+        if ((rc) && (xc->vc_section_count) && (xc->maxhandle) &&
+            ((xc->fh) || (xc->contains_hier_section || (xc->contains_hier_section_lz4)))) {
+            /* more init */
+            xc->do_rewind = 1;
+        } else {
+            fstReaderClose(xc);
+            xc = NULL;
+        }
+    }
+
+    return (xc);
+}
+
+static void fstReaderDeallocateRvatData(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    if (xc) {
+        free(xc->rvat_chain_mem);
+        xc->rvat_chain_mem = NULL;
+        free(xc->rvat_frame_data);
+        xc->rvat_frame_data = NULL;
+        free(xc->rvat_time_table);
+        xc->rvat_time_table = NULL;
+        free(xc->rvat_chain_table);
+        xc->rvat_chain_table = NULL;
+        free(xc->rvat_chain_table_lengths);
+        xc->rvat_chain_table_lengths = NULL;
+
+        xc->rvat_data_valid = 0;
+    }
+}
+
+void fstReaderClose(void *ctx)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    if (xc) {
+        fstReaderDeallocateScopeData(xc);
+        fstReaderDeallocateRvatData(xc);
+        free(xc->rvat_sig_offs);
+        xc->rvat_sig_offs = NULL;
+
+        free(xc->process_mask);
+        xc->process_mask = NULL;
+        free(xc->blackout_times);
+        xc->blackout_times = NULL;
+        free(xc->blackout_activity);
+        xc->blackout_activity = NULL;
+        free(xc->temp_signal_value_buf);
+        xc->temp_signal_value_buf = NULL;
+        free(xc->signal_typs);
+        xc->signal_typs = NULL;
+        free(xc->signal_lens);
+        xc->signal_lens = NULL;
+        free(xc->filename);
+        xc->filename = NULL;
+
+        if (xc->fh) {
+            tmpfile_close(&xc->fh, &xc->fh_nam);
+        }
+
+        if (xc->f) {
+            tmpfile_close(&xc->f, &xc->f_nam);
+            if (xc->filename_unpacked) {
+                unlink(xc->filename_unpacked);
+                free(xc->filename_unpacked);
+            }
+        }
+
+        free(xc);
+    }
+}
+
+/*
+ * read processing
+ */
+
+/* normal read which re-interleaves the value change data */
+int fstReaderIterBlocks(void *ctx,
+                        void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx,
+                                                      const unsigned char *value),
+                        void *user_callback_data_pointer, FILE *fv)
+{
+    return (fstReaderIterBlocks2(ctx, value_change_callback, NULL, user_callback_data_pointer, fv));
+}
+
+int fstReaderIterBlocks2(void *ctx,
+                         void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time,
+                                                       fstHandle facidx, const unsigned char *value),
+                         void (*value_change_callback_varlen)(void *user_callback_data_pointer, uint64_t time,
+                                                              fstHandle facidx, const unsigned char *value,
+                                                              uint32_t len),
+                         void *user_callback_data_pointer, FILE *fv)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+
+    uint64_t previous_time = UINT64_MAX;
+    uint64_t *time_table = NULL;
+    uint64_t tsec_nitems;
+    unsigned int secnum = 0;
+    int blocks_skipped = 0;
+    fst_off_t blkpos = 0;
+    uint64_t seclen, beg_tim;
+    uint64_t end_tim;
+    uint64_t frame_uclen, frame_clen, frame_maxhandle, vc_maxhandle;
+    fst_off_t vc_start;
+    fst_off_t indx_pntr, indx_pos;
+    fst_off_t *chain_table = NULL;
+    uint32_t *chain_table_lengths = NULL;
+    unsigned char *chain_cmem;
+    unsigned char *pnt;
+    long chain_clen;
+    fstHandle idx, pidx = 0, i;
+    uint64_t pval;
+    uint64_t vc_maxhandle_largest = 0;
+    uint64_t tsec_uclen = 0, tsec_clen = 0;
+    int sectype;
+    uint64_t mem_required_for_traversal;
+    unsigned char *mem_for_traversal = NULL;
+    uint32_t traversal_mem_offs;
+    uint32_t *scatterptr, *headptr, *length_remaining;
+    uint32_t cur_blackout = 0;
+    int packtype;
+    unsigned char *mc_mem = NULL;
+    uint32_t mc_mem_len; /* corresponds to largest value encountered in chain_table_lengths[i] */
+    int dumpvars_state = 0;
+
+    if (!xc)
+        return (0);
+
+    scatterptr = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t));
+    headptr = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t));
+    length_remaining = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t));
+
+    if (fv) {
+#ifndef FST_WRITEX_DISABLE
+        fflush(fv);
+        setvbuf(fv, (char *)NULL, _IONBF,
+                0); /* even buffered IO is slow so disable it and use our own routines that don't need seeking */
+        xc->writex_fd = fileno(fv);
+#endif
+    }
+
+    for (;;) {
+        uint32_t *tc_head = NULL;
+        traversal_mem_offs = 0;
+
+        fstReaderFseeko(xc, xc->f, blkpos, SEEK_SET);
+
+        sectype = fgetc(xc->f);
+        seclen = fstReaderUint64(xc->f);
+
+        if ((sectype == EOF) || (sectype == FST_BL_SKIP)) {
+#ifdef FST_DEBUG
+            fprintf(stderr, FST_APIMESS "<< EOF >>\n");
+#endif
+            break;
+        }
+
+        blkpos++;
+        if ((sectype != FST_BL_VCDATA) && (sectype != FST_BL_VCDATA_DYN_ALIAS) &&
+            (sectype != FST_BL_VCDATA_DYN_ALIAS2)) {
+            blkpos += seclen;
+            continue;
+        }
+
+        if (!seclen)
+            break;
+
+        beg_tim = fstReaderUint64(xc->f);
+        end_tim = fstReaderUint64(xc->f);
+
+        if (xc->limit_range_valid) {
+            if (end_tim < xc->limit_range_start) {
+                blocks_skipped++;
+                blkpos += seclen;
+                continue;
+            }
+
+            if (beg_tim >
+                xc->limit_range_end) /* likely the compare in for(i=0;i<tsec_nitems;i++) below would do this earlier */
+            {
+                break;
+            }
+        }
+
+        mem_required_for_traversal = fstReaderUint64(xc->f);
+        mem_for_traversal =
+                (unsigned char *)malloc(mem_required_for_traversal + 66); /* add in potential fastlz overhead */
+#ifdef FST_DEBUG
+        fprintf(stderr, FST_APIMESS "sec: %u seclen: %d begtim: %d endtim: %d\n", secnum, (int)seclen, (int)beg_tim,
+                (int)end_tim);
+        fprintf(stderr, FST_APIMESS "mem_required_for_traversal: %d\n", (int)mem_required_for_traversal);
+#endif
+        /* process time block */
+        {
+            unsigned char *ucdata;
+            unsigned char *cdata;
+            unsigned long destlen /* = tsec_uclen */; /* scan-build */
+            unsigned long sourcelen /*= tsec_clen */; /* scan-build */
+            int rc;
+            unsigned char *tpnt;
+            uint64_t tpval;
+            unsigned int ti;
+
+            if (fstReaderFseeko(xc, xc->f, blkpos + seclen - 24, SEEK_SET) != 0)
+                break;
+            tsec_uclen = fstReaderUint64(xc->f);
+            tsec_clen = fstReaderUint64(xc->f);
+            tsec_nitems = fstReaderUint64(xc->f);
+#ifdef FST_DEBUG
+            fprintf(stderr, FST_APIMESS "time section unc: %d, com: %d (%d items)\n", (int)tsec_uclen, (int)tsec_clen,
+                    (int)tsec_nitems);
+#endif
+            if (tsec_clen > seclen)
+                break; /* corrupted tsec_clen: by definition it can't be larger than size of section */
+            ucdata = (unsigned char *)malloc(tsec_uclen);
+            if (!ucdata)
+                break; /* malloc fail as tsec_uclen out of range from corrupted file */
+            destlen = tsec_uclen;
+            sourcelen = tsec_clen;
+
+            fstReaderFseeko(xc, xc->f, -24 - ((fst_off_t)tsec_clen), SEEK_CUR);
+
+            if (tsec_uclen != tsec_clen) {
+                cdata = (unsigned char *)malloc(tsec_clen);
+                fstFread(cdata, tsec_clen, 1, xc->f);
+
+                rc = uncompress(ucdata, &destlen, cdata, sourcelen);
+
+                if (rc != Z_OK) {
+                    fprintf(stderr, FST_APIMESS "fstReaderIterBlocks2(), tsec uncompress rc = %d, exiting.\n", rc);
+                    exit(255);
+                }
+
+                free(cdata);
+            } else {
+                fstFread(ucdata, tsec_uclen, 1, xc->f);
+            }
+
+            free(time_table);
+            time_table = (uint64_t *)calloc(tsec_nitems, sizeof(uint64_t));
+            tpnt = ucdata;
+            tpval = 0;
+            for (ti = 0; ti < tsec_nitems; ti++) {
+                int skiplen;
+                uint64_t val = fstGetVarint64(tpnt, &skiplen);
+                tpval = time_table[ti] = tpval + val;
+                tpnt += skiplen;
+            }
+
+            tc_head = (uint32_t *)calloc(tsec_nitems /* scan-build */ ? tsec_nitems : 1, sizeof(uint32_t));
+            free(ucdata);
+        }
+
+        fstReaderFseeko(xc, xc->f, blkpos + 32, SEEK_SET);
+
+        frame_uclen = fstReaderVarint64(xc->f);
+        frame_clen = fstReaderVarint64(xc->f);
+        frame_maxhandle = fstReaderVarint64(xc->f);
+
+        if (secnum == 0) {
+            if ((beg_tim != time_table[0]) || (blocks_skipped)) {
+                unsigned char *mu = (unsigned char *)malloc(frame_uclen);
+                uint32_t sig_offs = 0;
+
+                if (fv) {
+                    char wx_buf[32];
+                    int wx_len;
+
+                    if (beg_tim) {
+                        if (dumpvars_state == 1) {
+                            wx_len = sprintf(wx_buf, "$end\n");
+                            fstWritex(xc, wx_buf, wx_len);
+                            dumpvars_state = 2;
+                        }
+                        wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", beg_tim);
+                        fstWritex(xc, wx_buf, wx_len);
+                        if (!dumpvars_state) {
+                            wx_len = sprintf(wx_buf, "$dumpvars\n");
+                            fstWritex(xc, wx_buf, wx_len);
+                            dumpvars_state = 1;
+                        }
+                    }
+                    if ((xc->num_blackouts) && (cur_blackout != xc->num_blackouts)) {
+                        if (beg_tim == xc->blackout_times[cur_blackout]) {
+                            wx_len = sprintf(wx_buf, "$dump%s $end\n",
+                                             (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
+                            fstWritex(xc, wx_buf, wx_len);
+                        }
+                    }
+                }
+
+                if (frame_uclen == frame_clen) {
+                    fstFread(mu, frame_uclen, 1, xc->f);
+                } else {
+                    unsigned char *mc = (unsigned char *)malloc(frame_clen);
+                    int rc;
+
+                    unsigned long destlen = frame_uclen;
+                    unsigned long sourcelen = frame_clen;
+
+                    fstFread(mc, sourcelen, 1, xc->f);
+                    rc = uncompress(mu, &destlen, mc, sourcelen);
+                    if (rc != Z_OK) {
+                        fprintf(stderr, FST_APIMESS "fstReaderIterBlocks2(), frame uncompress rc: %d, exiting.\n", rc);
+                        exit(255);
+                    }
+                    free(mc);
+                }
+
+                for (idx = 0; idx < frame_maxhandle; idx++) {
+                    int process_idx = idx / 8;
+                    int process_bit = idx & 7;
+
+                    if (xc->process_mask[process_idx] & (1 << process_bit)) {
+                        if (xc->signal_lens[idx] <= 1) {
+                            if (xc->signal_lens[idx] == 1) {
+                                unsigned char val = mu[sig_offs];
+                                if (value_change_callback) {
+                                    xc->temp_signal_value_buf[0] = val;
+                                    xc->temp_signal_value_buf[1] = 0;
+                                    value_change_callback(user_callback_data_pointer, beg_tim, idx + 1,
+                                                          xc->temp_signal_value_buf);
+                                } else {
+                                    if (fv) {
+                                        char vcd_id[16];
+
+                                        int vcdid_len = fstVcdIDForFwrite(vcd_id + 1, idx + 1);
+                                        vcd_id[0] = val; /* collapse 3 writes into one I/O call */
+                                        vcd_id[vcdid_len + 1] = '\n';
+                                        fstWritex(xc, vcd_id, vcdid_len + 2);
+                                    }
+                                }
+                            } else {
+                                /* variable-length ("0" length) records have no initial state */
+                            }
+                        } else {
+                            if (xc->signal_typs[idx] != FST_VT_VCD_REAL) {
+                                if (value_change_callback) {
+                                    memcpy(xc->temp_signal_value_buf, mu + sig_offs, xc->signal_lens[idx]);
+                                    xc->temp_signal_value_buf[xc->signal_lens[idx]] = 0;
+                                    value_change_callback(user_callback_data_pointer, beg_tim, idx + 1,
+                                                          xc->temp_signal_value_buf);
+                                } else {
+                                    if (fv) {
+                                        char vcd_id[16];
+                                        int vcdid_len = fstVcdIDForFwrite(vcd_id + 1, idx + 1);
+
+                                        vcd_id[0] = (xc->signal_typs[idx] != FST_VT_VCD_PORT) ? 'b' : 'p';
+                                        fstWritex(xc, vcd_id, 1);
+                                        fstWritex(xc, mu + sig_offs, xc->signal_lens[idx]);
+
+                                        vcd_id[0] = ' '; /* collapse 3 writes into one I/O call */
+                                        vcd_id[vcdid_len + 1] = '\n';
+                                        fstWritex(xc, vcd_id, vcdid_len + 2);
+                                    }
+                                }
+                            } else {
+                                double d;
+                                unsigned char *clone_d;
+                                unsigned char *srcdata = mu + sig_offs;
+
+                                if (value_change_callback) {
+                                    if (xc->native_doubles_for_cb) {
+                                        if (xc->double_endian_match) {
+                                            clone_d = srcdata;
+                                        } else {
+                                            int j;
+
+                                            clone_d = (unsigned char *)&d;
+                                            for (j = 0; j < 8; j++) {
+                                                clone_d[j] = srcdata[7 - j];
+                                            }
+                                        }
+                                        value_change_callback(user_callback_data_pointer, beg_tim, idx + 1, clone_d);
+                                    } else {
+                                        clone_d = (unsigned char *)&d;
+                                        if (xc->double_endian_match) {
+                                            memcpy(clone_d, srcdata, 8);
+                                        } else {
+                                            int j;
+
+                                            for (j = 0; j < 8; j++) {
+                                                clone_d[j] = srcdata[7 - j];
+                                            }
+                                        }
+                                        sprintf((char *)xc->temp_signal_value_buf, "%.16g", d);
+                                        value_change_callback(user_callback_data_pointer, beg_tim, idx + 1,
+                                                              xc->temp_signal_value_buf);
+                                    }
+                                } else {
+                                    if (fv) {
+                                        char vcdid_buf[16];
+                                        char wx_buf[64];
+                                        int wx_len;
+
+                                        clone_d = (unsigned char *)&d;
+                                        if (xc->double_endian_match) {
+                                            memcpy(clone_d, srcdata, 8);
+                                        } else {
+                                            int j;
+
+                                            for (j = 0; j < 8; j++) {
+                                                clone_d[j] = srcdata[7 - j];
+                                            }
+                                        }
+
+                                        fstVcdID(vcdid_buf, idx + 1);
+                                        wx_len = sprintf(wx_buf, "r%.16g %s\n", d, vcdid_buf);
+                                        fstWritex(xc, wx_buf, wx_len);
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    sig_offs += xc->signal_lens[idx];
+                }
+
+                free(mu);
+                fstReaderFseeko(xc, xc->f, -((fst_off_t)frame_clen), SEEK_CUR);
+            }
+        }
+
+        fstReaderFseeko(xc, xc->f, (fst_off_t)frame_clen, SEEK_CUR); /* skip past compressed data */
+
+        vc_maxhandle = fstReaderVarint64(xc->f);
+        vc_start = ftello(xc->f); /* points to '!' character */
+        packtype = fgetc(xc->f);
+
+#ifdef FST_DEBUG
+        fprintf(stderr, FST_APIMESS "frame_uclen: %d, frame_clen: %d, frame_maxhandle: %d\n", (int)frame_uclen,
+                (int)frame_clen, (int)frame_maxhandle);
+        fprintf(stderr, FST_APIMESS "vc_maxhandle: %d, packtype: %c\n", (int)vc_maxhandle, packtype);
+#endif
+
+        indx_pntr = blkpos + seclen - 24 - tsec_clen - 8;
+        fstReaderFseeko(xc, xc->f, indx_pntr, SEEK_SET);
+        chain_clen = fstReaderUint64(xc->f);
+        indx_pos = indx_pntr - chain_clen;
+#ifdef FST_DEBUG
+        fprintf(stderr, FST_APIMESS "indx_pos: %d (%d bytes)\n", (int)indx_pos, (int)chain_clen);
+#endif
+        chain_cmem = (unsigned char *)malloc(chain_clen);
+        if (!chain_cmem)
+            goto block_err;
+        fstReaderFseeko(xc, xc->f, indx_pos, SEEK_SET);
+        fstFread(chain_cmem, chain_clen, 1, xc->f);
+
+        if (vc_maxhandle > vc_maxhandle_largest) {
+            free(chain_table);
+            free(chain_table_lengths);
+
+            vc_maxhandle_largest = vc_maxhandle;
+            chain_table = (fst_off_t *)calloc((vc_maxhandle + 1), sizeof(fst_off_t));
+            chain_table_lengths = (uint32_t *)calloc((vc_maxhandle + 1), sizeof(uint32_t));
+        }
+
+        if (!chain_table || !chain_table_lengths)
+            goto block_err;
+
+        pnt = chain_cmem;
+        idx = 0;
+        pval = 0;
+
+        if (sectype == FST_BL_VCDATA_DYN_ALIAS2) {
+            uint32_t prev_alias = 0;
+
+            do {
+                int skiplen;
+
+                if (*pnt & 0x01) {
+                    int64_t shval = fstGetSVarint64(pnt, &skiplen) >> 1;
+                    if (shval > 0) {
+                        pval = chain_table[idx] = pval + shval;
+                        if (idx) {
+                            chain_table_lengths[pidx] = pval - chain_table[pidx];
+                        }
+                        pidx = idx++;
+                    } else if (shval < 0) {
+                        chain_table[idx] = 0; /* need to explicitly zero as calloc above might not run */
+                        chain_table_lengths[idx] = prev_alias =
+                                shval; /* because during this loop iter would give stale data! */
+                        idx++;
+                    } else {
+                        chain_table[idx] = 0; /* need to explicitly zero as calloc above might not run */
+                        chain_table_lengths[idx] =
+                                prev_alias; /* because during this loop iter would give stale data! */
+                        idx++;
+                    }
+                } else {
+                    uint64_t val = fstGetVarint32(pnt, &skiplen);
+
+                    fstHandle loopcnt = val >> 1;
+                    for (i = 0; i < loopcnt; i++) {
+                        chain_table[idx++] = 0;
+                    }
+                }
+
+                pnt += skiplen;
+            } while (pnt != (chain_cmem + chain_clen));
+        } else {
+            do {
+                int skiplen;
+                uint64_t val = fstGetVarint32(pnt, &skiplen);
+
+                if (!val) {
+                    pnt += skiplen;
+                    val = fstGetVarint32(pnt, &skiplen);
+                    chain_table[idx] = 0;            /* need to explicitly zero as calloc above might not run */
+                    chain_table_lengths[idx] = -val; /* because during this loop iter would give stale data! */
+                    idx++;
+                } else if (val & 1) {
+                    pval = chain_table[idx] = pval + (val >> 1);
+                    if (idx) {
+                        chain_table_lengths[pidx] = pval - chain_table[pidx];
+                    }
+                    pidx = idx++;
+                } else {
+                    fstHandle loopcnt = val >> 1;
+                    for (i = 0; i < loopcnt; i++) {
+                        chain_table[idx++] = 0;
+                    }
+                }
+
+                pnt += skiplen;
+            } while (pnt != (chain_cmem + chain_clen));
+        }
+
+        chain_table[idx] = indx_pos - vc_start;
+        chain_table_lengths[pidx] = chain_table[idx] - chain_table[pidx];
+
+        for (i = 0; i < idx; i++) {
+            int32_t v32 = chain_table_lengths[i];
+            if ((v32 < 0) && (!chain_table[i])) {
+                v32 = -v32;
+                v32--;
+                if (((uint32_t)v32) < i) /* sanity check */
+                {
+                    chain_table[i] = chain_table[v32];
+                    chain_table_lengths[i] = chain_table_lengths[v32];
+                }
+            }
+        }
+
+#ifdef FST_DEBUG
+        fprintf(stderr, FST_APIMESS "decompressed chain idx len: %" PRIu32 "\n", idx);
+#endif
+
+        mc_mem_len = 16384;
+        mc_mem = (unsigned char *)malloc(mc_mem_len); /* buffer for compressed reads */
+
+        /* check compressed VC data */
+        if (idx > xc->maxhandle)
+            idx = xc->maxhandle;
+        for (i = 0; i < idx; i++) {
+            if (chain_table[i]) {
+                int process_idx = i / 8;
+                int process_bit = i & 7;
+
+                if (xc->process_mask[process_idx] & (1 << process_bit)) {
+                    int rc = Z_OK;
+                    uint32_t val;
+                    uint32_t skiplen;
+                    uint32_t tdelta;
+
+                    fstReaderFseeko(xc, xc->f, vc_start + chain_table[i], SEEK_SET);
+                    val = fstReaderVarint32WithSkip(xc->f, &skiplen);
+                    if (val) {
+                        unsigned char *mu = mem_for_traversal + traversal_mem_offs; /* uncomp: dst */
+                        unsigned char *mc;                                          /* comp:   src */
+                        unsigned long destlen = val;
+                        unsigned long sourcelen = chain_table_lengths[i];
+
+                        if (mc_mem_len < chain_table_lengths[i]) {
+                            free(mc_mem);
+                            mc_mem = (unsigned char *)malloc(mc_mem_len = chain_table_lengths[i]);
+                        }
+                        mc = mc_mem;
+
+                        fstFread(mc, chain_table_lengths[i], 1, xc->f);
+
+                        switch (packtype) {
+                        case '4':
+                            rc = (destlen == (unsigned long)LZ4_decompress_safe_partial((char *)mc, (char *)mu,
+                                                                                        sourcelen, destlen, destlen))
+                                         ? Z_OK
+                                         : Z_DATA_ERROR;
+                            break;
+                        case 'F':
+                            fastlz_decompress(mc, sourcelen, mu, destlen); /* rc appears unreliable */
+                            break;
+                        default:
+                            rc = uncompress(mu, &destlen, mc, sourcelen);
+                            break;
+                        }
+
+                        /* data to process is for(j=0;j<destlen;j++) in mu[j] */
+                        headptr[i] = traversal_mem_offs;
+                        length_remaining[i] = val;
+                        traversal_mem_offs += val;
+                    } else {
+                        int destlen = chain_table_lengths[i] - skiplen;
+                        unsigned char *mu = mem_for_traversal + traversal_mem_offs;
+                        fstFread(mu, destlen, 1, xc->f);
+                        /* data to process is for(j=0;j<destlen;j++) in mu[j] */
+                        headptr[i] = traversal_mem_offs;
+                        length_remaining[i] = destlen;
+                        traversal_mem_offs += destlen;
+                    }
+
+                    if (rc != Z_OK) {
+                        fprintf(stderr, FST_APIMESS "fstReaderIterBlocks2(), fac: %d clen: %d (rc=%d), exiting.\n",
+                                (int)i, (int)val, rc);
+                        exit(255);
+                    }
+
+                    if (xc->signal_lens[i] == 1) {
+                        uint32_t vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[i]);
+                        uint32_t shcnt = 2 << (vli & 1);
+                        tdelta = vli >> shcnt;
+                    } else {
+                        uint32_t vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[i]);
+                        tdelta = vli >> 1;
+                    }
+
+                    scatterptr[i] = tc_head[tdelta];
+                    tc_head[tdelta] = i + 1;
+                }
+            }
+        }
+
+        free(mc_mem); /* there is no usage below for this, no real need to clear out mc_mem or mc_mem_len */
+
+        for (i = 0; i < tsec_nitems; i++) {
+            uint32_t tdelta;
+            int skiplen, skiplen2;
+            uint32_t vli;
+
+            if (fv) {
+                char wx_buf[32];
+                int wx_len;
+
+                if (time_table[i] != previous_time) {
+                    if (xc->limit_range_valid) {
+                        if (time_table[i] > xc->limit_range_end) {
+                            break;
+                        }
+                    }
+
+                    if (dumpvars_state == 1) {
+                        wx_len = sprintf(wx_buf, "$end\n");
+                        fstWritex(xc, wx_buf, wx_len);
+                        dumpvars_state = 2;
+                    }
+                    wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", time_table[i]);
+                    fstWritex(xc, wx_buf, wx_len);
+                    if (!dumpvars_state) {
+                        wx_len = sprintf(wx_buf, "$dumpvars\n");
+                        fstWritex(xc, wx_buf, wx_len);
+                        dumpvars_state = 1;
+                    }
+
+                    if ((xc->num_blackouts) && (cur_blackout != xc->num_blackouts)) {
+                        if (time_table[i] == xc->blackout_times[cur_blackout]) {
+                            wx_len = sprintf(wx_buf, "$dump%s $end\n",
+                                             (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
+                            fstWritex(xc, wx_buf, wx_len);
+                        }
+                    }
+                    previous_time = time_table[i];
+                }
+            }
+
+            while (tc_head[i]) {
+                idx = tc_head[i] - 1;
+                vli = fstGetVarint32(mem_for_traversal + headptr[idx], &skiplen);
+
+                if (xc->signal_lens[idx] <= 1) {
+                    if (xc->signal_lens[idx] == 1) {
+                        unsigned char val;
+                        if (!(vli & 1)) {
+                            /* tdelta = vli >> 2; */ /* scan-build */
+                            val = ((vli >> 1) & 1) | '0';
+                        } else {
+                            /* tdelta = vli >> 4; */ /* scan-build */
+                            val = FST_RCV_STR[((vli >> 1) & 7)];
+                        }
+
+                        if (value_change_callback) {
+                            xc->temp_signal_value_buf[0] = val;
+                            xc->temp_signal_value_buf[1] = 0;
+                            value_change_callback(user_callback_data_pointer, time_table[i], idx + 1,
+                                                  xc->temp_signal_value_buf);
+                        } else {
+                            if (fv) {
+                                char vcd_id[16];
+                                int vcdid_len = fstVcdIDForFwrite(vcd_id + 1, idx + 1);
+
+                                vcd_id[0] = val;
+                                vcd_id[vcdid_len + 1] = '\n';
+                                fstWritex(xc, vcd_id, vcdid_len + 2);
+                            }
+                        }
+                        headptr[idx] += skiplen;
+                        length_remaining[idx] -= skiplen;
+
+                        tc_head[i] = scatterptr[idx];
+                        scatterptr[idx] = 0;
+
+                        if (length_remaining[idx]) {
+                            int shamt;
+                            vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[idx]);
+                            shamt = 2 << (vli & 1);
+                            tdelta = vli >> shamt;
+
+                            scatterptr[idx] = tc_head[i + tdelta];
+                            tc_head[i + tdelta] = idx + 1;
+                        }
+                    } else {
+                        unsigned char *vdata;
+                        uint32_t len;
+
+                        vli = fstGetVarint32(mem_for_traversal + headptr[idx], &skiplen);
+                        len = fstGetVarint32(mem_for_traversal + headptr[idx] + skiplen, &skiplen2);
+                        /* tdelta = vli >> 1; */ /* scan-build */
+                        skiplen += skiplen2;
+                        vdata = mem_for_traversal + headptr[idx] + skiplen;
+
+                        if (!(vli & 1)) {
+                            if (value_change_callback_varlen) {
+                                value_change_callback_varlen(user_callback_data_pointer, time_table[i], idx + 1, vdata,
+                                                             len);
+                            } else {
+                                if (fv) {
+                                    char vcd_id[16];
+                                    int vcdid_len;
+
+                                    vcd_id[0] = 's';
+                                    fstWritex(xc, vcd_id, 1);
+
+                                    vcdid_len = fstVcdIDForFwrite(vcd_id + 1, idx + 1);
+                                    {
+                                        unsigned char *vesc = (unsigned char *)malloc(len * 4 + 1);
+                                        int vlen = fstUtilityBinToEsc(vesc, vdata, len);
+                                        fstWritex(xc, vesc, vlen);
+                                        free(vesc);
+                                    }
+
+                                    vcd_id[0] = ' ';
+                                    vcd_id[vcdid_len + 1] = '\n';
+                                    fstWritex(xc, vcd_id, vcdid_len + 2);
+                                }
+                            }
+                        }
+
+                        skiplen += len;
+                        headptr[idx] += skiplen;
+                        length_remaining[idx] -= skiplen;
+
+                        tc_head[i] = scatterptr[idx];
+                        scatterptr[idx] = 0;
+
+                        if (length_remaining[idx]) {
+                            vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[idx]);
+                            tdelta = vli >> 1;
+
+                            scatterptr[idx] = tc_head[i + tdelta];
+                            tc_head[i + tdelta] = idx + 1;
+                        }
+                    }
+                } else {
+                    uint32_t len = xc->signal_lens[idx];
+                    unsigned char *vdata;
+
+                    vli = fstGetVarint32(mem_for_traversal + headptr[idx], &skiplen);
+                    /* tdelta = vli >> 1; */ /* scan-build */
+                    vdata = mem_for_traversal + headptr[idx] + skiplen;
+
+                    if (xc->signal_typs[idx] != FST_VT_VCD_REAL) {
+                        if (!(vli & 1)) {
+                            int byte = 0;
+                            int bit;
+                            unsigned int j;
+
+                            for (j = 0; j < len; j++) {
+                                unsigned char ch;
+                                byte = j / 8;
+                                bit = 7 - (j & 7);
+                                ch = ((vdata[byte] >> bit) & 1) | '0';
+                                xc->temp_signal_value_buf[j] = ch;
+                            }
+                            xc->temp_signal_value_buf[j] = 0;
+
+                            if (value_change_callback) {
+                                value_change_callback(user_callback_data_pointer, time_table[i], idx + 1,
+                                                      xc->temp_signal_value_buf);
+                            } else {
+                                if (fv) {
+                                    unsigned char ch_bp = (xc->signal_typs[idx] != FST_VT_VCD_PORT) ? 'b' : 'p';
+
+                                    fstWritex(xc, &ch_bp, 1);
+                                    fstWritex(xc, xc->temp_signal_value_buf, len);
+                                }
+                            }
+
+                            len = byte + 1;
+                        } else {
+                            if (value_change_callback) {
+                                memcpy(xc->temp_signal_value_buf, vdata, len);
+                                xc->temp_signal_value_buf[len] = 0;
+                                value_change_callback(user_callback_data_pointer, time_table[i], idx + 1,
+                                                      xc->temp_signal_value_buf);
+                            } else {
+                                if (fv) {
+                                    unsigned char ch_bp = (xc->signal_typs[idx] != FST_VT_VCD_PORT) ? 'b' : 'p';
+
+                                    fstWritex(xc, &ch_bp, 1);
+                                    fstWritex(xc, vdata, len);
+                                }
+                            }
+                        }
+                    } else {
+                        double d;
+                        unsigned char *clone_d /*= (unsigned char *)&d */; /* scan-build */
+                        unsigned char buf[8];
+                        unsigned char *srcdata;
+
+                        if (!(vli & 1)) /* very rare case, but possible */
+                        {
+                            int bit;
+                            int j;
+
+                            for (j = 0; j < 8; j++) {
+                                unsigned char ch;
+                                bit = 7 - (j & 7);
+                                ch = ((vdata[0] >> bit) & 1) | '0';
+                                buf[j] = ch;
+                            }
+
+                            len = 1;
+                            srcdata = buf;
+                        } else {
+                            srcdata = vdata;
+                        }
+
+                        if (value_change_callback) {
+                            if (xc->native_doubles_for_cb) {
+                                if (xc->double_endian_match) {
+                                    clone_d = srcdata;
+                                } else {
+                                    int j;
+
+                                    clone_d = (unsigned char *)&d;
+                                    for (j = 0; j < 8; j++) {
+                                        clone_d[j] = srcdata[7 - j];
+                                    }
+                                }
+                                value_change_callback(user_callback_data_pointer, time_table[i], idx + 1, clone_d);
+                            } else {
+                                clone_d = (unsigned char *)&d;
+                                if (xc->double_endian_match) {
+                                    memcpy(clone_d, srcdata, 8);
+                                } else {
+                                    int j;
+
+                                    for (j = 0; j < 8; j++) {
+                                        clone_d[j] = srcdata[7 - j];
+                                    }
+                                }
+                                sprintf((char *)xc->temp_signal_value_buf, "%.16g", d);
+                                value_change_callback(user_callback_data_pointer, time_table[i], idx + 1,
+                                                      xc->temp_signal_value_buf);
+                            }
+                        } else {
+                            if (fv) {
+                                char wx_buf[32];
+                                int wx_len;
+
+                                clone_d = (unsigned char *)&d;
+                                if (xc->double_endian_match) {
+                                    memcpy(clone_d, srcdata, 8);
+                                } else {
+                                    int j;
+
+                                    for (j = 0; j < 8; j++) {
+                                        clone_d[j] = srcdata[7 - j];
+                                    }
+                                }
+
+                                wx_len = sprintf(wx_buf, "r%.16g", d);
+                                fstWritex(xc, wx_buf, wx_len);
+                            }
+                        }
+                    }
+
+                    if (fv) {
+                        char vcd_id[16];
+                        int vcdid_len = fstVcdIDForFwrite(vcd_id + 1, idx + 1);
+                        vcd_id[0] = ' ';
+                        vcd_id[vcdid_len + 1] = '\n';
+                        fstWritex(xc, vcd_id, vcdid_len + 2);
+                    }
+
+                    skiplen += len;
+                    headptr[idx] += skiplen;
+                    length_remaining[idx] -= skiplen;
+
+                    tc_head[i] = scatterptr[idx];
+                    scatterptr[idx] = 0;
+
+                    if (length_remaining[idx]) {
+                        vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[idx]);
+                        tdelta = vli >> 1;
+
+                        scatterptr[idx] = tc_head[i + tdelta];
+                        tc_head[i + tdelta] = idx + 1;
+                    }
+                }
+            }
+        }
+
+    block_err:
+        free(tc_head);
+        free(chain_cmem);
+        free(mem_for_traversal);
+        mem_for_traversal = NULL;
+
+        secnum++;
+        if (secnum == xc->vc_section_count)
+            break; /* in case file is growing, keep with original block count */
+        blkpos += seclen;
+    }
+
+    if (mem_for_traversal)
+        free(mem_for_traversal); /* scan-build */
+    free(length_remaining);
+    free(headptr);
+    free(scatterptr);
+
+    if (chain_table)
+        free(chain_table);
+    if (chain_table_lengths)
+        free(chain_table_lengths);
+
+    free(time_table);
+
+#ifndef FST_WRITEX_DISABLE
+    if (fv) {
+        fstWritex(xc, NULL, 0);
+    }
+#endif
+
+    return (1);
+}
+
+/* rvat functions */
+
+static char *fstExtractRvatDataFromFrame(struct fstReaderContext *xc, fstHandle facidx, char *buf)
+{
+    if (facidx >= xc->rvat_frame_maxhandle) {
+        return (NULL);
+    }
+
+    if (xc->signal_lens[facidx] == 1) {
+        buf[0] = (char)xc->rvat_frame_data[xc->rvat_sig_offs[facidx]];
+        buf[1] = 0;
+    } else {
+        if (xc->signal_typs[facidx] != FST_VT_VCD_REAL) {
+            memcpy(buf, xc->rvat_frame_data + xc->rvat_sig_offs[facidx], xc->signal_lens[facidx]);
+            buf[xc->signal_lens[facidx]] = 0;
+        } else {
+            double d;
+            unsigned char *clone_d = (unsigned char *)&d;
+            unsigned char *srcdata = xc->rvat_frame_data + xc->rvat_sig_offs[facidx];
+
+            if (xc->double_endian_match) {
+                memcpy(clone_d, srcdata, 8);
+            } else {
+                int j;
+
+                for (j = 0; j < 8; j++) {
+                    clone_d[j] = srcdata[7 - j];
+                }
+            }
+
+            sprintf((char *)buf, "%.16g", d);
+        }
+    }
+
+    return (buf);
+}
+
+char *fstReaderGetValueFromHandleAtTime(void *ctx, uint64_t tim, fstHandle facidx, char *buf)
+{
+    struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
+    fst_off_t blkpos = 0, prev_blkpos;
+    uint64_t beg_tim, end_tim, beg_tim2, end_tim2;
+    int sectype;
+    unsigned int secnum = 0;
+    uint64_t seclen;
+    uint64_t tsec_uclen = 0, tsec_clen = 0;
+    uint64_t tsec_nitems;
+    uint64_t frame_uclen, frame_clen;
+#ifdef FST_DEBUG
+    uint64_t mem_required_for_traversal;
+#endif
+    fst_off_t indx_pntr, indx_pos;
+    long chain_clen;
+    unsigned char *chain_cmem;
+    unsigned char *pnt;
+    fstHandle idx, pidx = 0, i;
+    uint64_t pval;
+
+    if ((!xc) || (!facidx) || (facidx > xc->maxhandle) || (!buf) || (!xc->signal_lens[facidx - 1])) {
+        return (NULL);
+    }
+
+    if (!xc->rvat_sig_offs) {
+        uint32_t cur_offs = 0;
+
+        xc->rvat_sig_offs = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t));
+        for (i = 0; i < xc->maxhandle; i++) {
+            xc->rvat_sig_offs[i] = cur_offs;
+            cur_offs += xc->signal_lens[i];
+        }
+    }
+
+    if (xc->rvat_data_valid) {
+        if ((xc->rvat_beg_tim <= tim) && (tim <= xc->rvat_end_tim)) {
+            goto process_value;
+        }
+
+        fstReaderDeallocateRvatData(xc);
+    }
+
+    xc->rvat_chain_pos_valid = 0;
+
+    for (;;) {
+        fstReaderFseeko(xc, xc->f, (prev_blkpos = blkpos), SEEK_SET);
+
+        sectype = fgetc(xc->f);
+        seclen = fstReaderUint64(xc->f);
+
+        if ((sectype == EOF) || (sectype == FST_BL_SKIP) || (!seclen)) {
+            return (NULL); /* if this loop exits on break, it's successful */
+        }
+
+        blkpos++;
+        if ((sectype != FST_BL_VCDATA) && (sectype != FST_BL_VCDATA_DYN_ALIAS) &&
+            (sectype != FST_BL_VCDATA_DYN_ALIAS2)) {
+            blkpos += seclen;
+            continue;
+        }
+
+        beg_tim = fstReaderUint64(xc->f);
+        end_tim = fstReaderUint64(xc->f);
+
+        if ((beg_tim <= tim) && (tim <= end_tim)) {
+            if ((tim == end_tim) && (tim != xc->end_time)) {
+                fst_off_t cached_pos = ftello(xc->f);
+                fstReaderFseeko(xc, xc->f, blkpos, SEEK_SET);
+
+                sectype = fgetc(xc->f);
+                seclen = fstReaderUint64(xc->f);
+
+                beg_tim2 = fstReaderUint64(xc->f);
+                end_tim2 = fstReaderUint64(xc->f);
+
+                if (((sectype != FST_BL_VCDATA) && (sectype != FST_BL_VCDATA_DYN_ALIAS) &&
+                     (sectype != FST_BL_VCDATA_DYN_ALIAS2)) ||
+                    (!seclen) || (beg_tim2 != tim)) {
+                    blkpos = prev_blkpos;
+                    break;
+                }
+                beg_tim = beg_tim2;
+                end_tim = end_tim2;
+                fstReaderFseeko(xc, xc->f, cached_pos, SEEK_SET);
+            }
+            break;
+        }
+
+        blkpos += seclen;
+        secnum++;
+    }
+
+    xc->rvat_beg_tim = beg_tim;
+    xc->rvat_end_tim = end_tim;
+
+#ifdef FST_DEBUG
+    mem_required_for_traversal =
+#endif
+            fstReaderUint64(xc->f);
+
+#ifdef FST_DEBUG
+    fprintf(stderr, FST_APIMESS "rvat sec: %u seclen: %d begtim: %d endtim: %d\n", secnum, (int)seclen, (int)beg_tim,
+            (int)end_tim);
+    fprintf(stderr, FST_APIMESS "mem_required_for_traversal: %d\n", (int)mem_required_for_traversal);
+#endif
+
+    /* process time block */
+    {
+        unsigned char *ucdata;
+        unsigned char *cdata;
+        unsigned long destlen /* = tsec_uclen */;  /* scan-build */
+        unsigned long sourcelen /* = tsec_clen */; /* scan-build */
+        int rc;
+        unsigned char *tpnt;
+        uint64_t tpval;
+        unsigned int ti;
+
+        fstReaderFseeko(xc, xc->f, blkpos + seclen - 24, SEEK_SET);
+        tsec_uclen = fstReaderUint64(xc->f);
+        tsec_clen = fstReaderUint64(xc->f);
+        tsec_nitems = fstReaderUint64(xc->f);
+#ifdef FST_DEBUG
+        fprintf(stderr, FST_APIMESS "time section unc: %d, com: %d (%d items)\n", (int)tsec_uclen, (int)tsec_clen,
+                (int)tsec_nitems);
+#endif
+        ucdata = (unsigned char *)malloc(tsec_uclen);
+        destlen = tsec_uclen;
+        sourcelen = tsec_clen;
+
+        fstReaderFseeko(xc, xc->f, -24 - ((fst_off_t)tsec_clen), SEEK_CUR);
+        if (tsec_uclen != tsec_clen) {
+            cdata = (unsigned char *)malloc(tsec_clen);
+            fstFread(cdata, tsec_clen, 1, xc->f);
+
+            rc = uncompress(ucdata, &destlen, cdata, sourcelen);
+
+            if (rc != Z_OK) {
+                fprintf(stderr, FST_APIMESS "fstReaderGetValueFromHandleAtTime(), tsec uncompress rc = %d, exiting.\n",
+                        rc);
+                exit(255);
+            }
+
+            free(cdata);
+        } else {
+            fstFread(ucdata, tsec_uclen, 1, xc->f);
+        }
+
+        xc->rvat_time_table = (uint64_t *)calloc(tsec_nitems, sizeof(uint64_t));
+        tpnt = ucdata;
+        tpval = 0;
+        for (ti = 0; ti < tsec_nitems; ti++) {
+            int skiplen;
+            uint64_t val = fstGetVarint64(tpnt, &skiplen);
+            tpval = xc->rvat_time_table[ti] = tpval + val;
+            tpnt += skiplen;
+        }
+
+        free(ucdata);
+    }
+
+    fstReaderFseeko(xc, xc->f, blkpos + 32, SEEK_SET);
+
+    frame_uclen = fstReaderVarint64(xc->f);
+    frame_clen = fstReaderVarint64(xc->f);
+    xc->rvat_frame_maxhandle = fstReaderVarint64(xc->f);
+    xc->rvat_frame_data = (unsigned char *)malloc(frame_uclen);
+
+    if (frame_uclen == frame_clen) {
+        fstFread(xc->rvat_frame_data, frame_uclen, 1, xc->f);
+    } else {
+        unsigned char *mc = (unsigned char *)malloc(frame_clen);
+        int rc;
+
+        unsigned long destlen = frame_uclen;
+        unsigned long sourcelen = frame_clen;
+
+        fstFread(mc, sourcelen, 1, xc->f);
+        rc = uncompress(xc->rvat_frame_data, &destlen, mc, sourcelen);
+        if (rc != Z_OK) {
+            fprintf(stderr, FST_APIMESS "fstReaderGetValueFromHandleAtTime(), frame decompress rc: %d, exiting.\n", rc);
+            exit(255);
+        }
+        free(mc);
+    }
+
+    xc->rvat_vc_maxhandle = fstReaderVarint64(xc->f);
+    xc->rvat_vc_start = ftello(xc->f); /* points to '!' character */
+    xc->rvat_packtype = fgetc(xc->f);
+
+#ifdef FST_DEBUG
+    fprintf(stderr, FST_APIMESS "frame_uclen: %d, frame_clen: %d, frame_maxhandle: %d\n", (int)frame_uclen,
+            (int)frame_clen, (int)xc->rvat_frame_maxhandle);
+    fprintf(stderr, FST_APIMESS "vc_maxhandle: %d\n", (int)xc->rvat_vc_maxhandle);
+#endif
+
+    indx_pntr = blkpos + seclen - 24 - tsec_clen - 8;
+    fstReaderFseeko(xc, xc->f, indx_pntr, SEEK_SET);
+    chain_clen = fstReaderUint64(xc->f);
+    indx_pos = indx_pntr - chain_clen;
+#ifdef FST_DEBUG
+    fprintf(stderr, FST_APIMESS "indx_pos: %d (%d bytes)\n", (int)indx_pos, (int)chain_clen);
+#endif
+    chain_cmem = (unsigned char *)malloc(chain_clen);
+    fstReaderFseeko(xc, xc->f, indx_pos, SEEK_SET);
+    fstFread(chain_cmem, chain_clen, 1, xc->f);
+
+    xc->rvat_chain_table = (fst_off_t *)calloc((xc->rvat_vc_maxhandle + 1), sizeof(fst_off_t));
+    xc->rvat_chain_table_lengths = (uint32_t *)calloc((xc->rvat_vc_maxhandle + 1), sizeof(uint32_t));
+
+    pnt = chain_cmem;
+    idx = 0;
+    pval = 0;
+
+    if (sectype == FST_BL_VCDATA_DYN_ALIAS2) {
+        uint32_t prev_alias = 0;
+
+        do {
+            int skiplen;
+
+            if (*pnt & 0x01) {
+                int64_t shval = fstGetSVarint64(pnt, &skiplen) >> 1;
+                if (shval > 0) {
+                    pval = xc->rvat_chain_table[idx] = pval + shval;
+                    if (idx) {
+                        xc->rvat_chain_table_lengths[pidx] = pval - xc->rvat_chain_table[pidx];
+                    }
+                    pidx = idx++;
+                } else if (shval < 0) {
+                    xc->rvat_chain_table[idx] = 0; /* need to explicitly zero as calloc above might not run */
+                    xc->rvat_chain_table_lengths[idx] = prev_alias =
+                            shval; /* because during this loop iter would give stale data! */
+                    idx++;
+                } else {
+                    xc->rvat_chain_table[idx] = 0; /* need to explicitly zero as calloc above might not run */
+                    xc->rvat_chain_table_lengths[idx] =
+                            prev_alias; /* because during this loop iter would give stale data! */
+                    idx++;
+                }
+            } else {
+                uint64_t val = fstGetVarint32(pnt, &skiplen);
+
+                fstHandle loopcnt = val >> 1;
+                for (i = 0; i < loopcnt; i++) {
+                    xc->rvat_chain_table[idx++] = 0;
+                }
+            }
+
+            pnt += skiplen;
+        } while (pnt != (chain_cmem + chain_clen));
+    } else {
+        do {
+            int skiplen;
+            uint64_t val = fstGetVarint32(pnt, &skiplen);
+
+            if (!val) {
+                pnt += skiplen;
+                val = fstGetVarint32(pnt, &skiplen);
+                xc->rvat_chain_table[idx] = 0;
+                xc->rvat_chain_table_lengths[idx] = -val;
+                idx++;
+            } else if (val & 1) {
+                pval = xc->rvat_chain_table[idx] = pval + (val >> 1);
+                if (idx) {
+                    xc->rvat_chain_table_lengths[pidx] = pval - xc->rvat_chain_table[pidx];
+                }
+                pidx = idx++;
+            } else {
+                fstHandle loopcnt = val >> 1;
+                for (i = 0; i < loopcnt; i++) {
+                    xc->rvat_chain_table[idx++] = 0;
+                }
+            }
+
+            pnt += skiplen;
+        } while (pnt != (chain_cmem + chain_clen));
+    }
+
+    free(chain_cmem);
+    xc->rvat_chain_table[idx] = indx_pos - xc->rvat_vc_start;
+    xc->rvat_chain_table_lengths[pidx] = xc->rvat_chain_table[idx] - xc->rvat_chain_table[pidx];
+
+    for (i = 0; i < idx; i++) {
+        int32_t v32 = xc->rvat_chain_table_lengths[i];
+        if ((v32 < 0) && (!xc->rvat_chain_table[i])) {
+            v32 = -v32;
+            v32--;
+            if (((uint32_t)v32) < i) /* sanity check */
+            {
+                xc->rvat_chain_table[i] = xc->rvat_chain_table[v32];
+                xc->rvat_chain_table_lengths[i] = xc->rvat_chain_table_lengths[v32];
+            }
+        }
+    }
+
+#ifdef FST_DEBUG
+    fprintf(stderr, FST_APIMESS "decompressed chain idx len: %" PRIu32 "\n", idx);
+#endif
+
+    xc->rvat_data_valid = 1;
+
+/* all data at this point is loaded or resident in fst cache, process and return appropriate value */
+process_value:
+    if (facidx > xc->rvat_vc_maxhandle) {
+        return (NULL);
+    }
+
+    facidx--; /* scale down for array which starts at zero */
+
+    if (((tim == xc->rvat_beg_tim) && (!xc->rvat_chain_table[facidx])) || (!xc->rvat_chain_table[facidx])) {
+        return (fstExtractRvatDataFromFrame(xc, facidx, buf));
+    }
+
+    if (facidx != xc->rvat_chain_facidx) {
+        if (xc->rvat_chain_mem) {
+            free(xc->rvat_chain_mem);
+            xc->rvat_chain_mem = NULL;
+
+            xc->rvat_chain_pos_valid = 0;
+        }
+    }
+
+    if (!xc->rvat_chain_mem) {
+        uint32_t skiplen;
+        fstReaderFseeko(xc, xc->f, xc->rvat_vc_start + xc->rvat_chain_table[facidx], SEEK_SET);
+        xc->rvat_chain_len = fstReaderVarint32WithSkip(xc->f, &skiplen);
+        if (xc->rvat_chain_len) {
+            unsigned char *mu = (unsigned char *)malloc(xc->rvat_chain_len);
+            unsigned char *mc = (unsigned char *)malloc(xc->rvat_chain_table_lengths[facidx]);
+            unsigned long destlen = xc->rvat_chain_len;
+            unsigned long sourcelen = xc->rvat_chain_table_lengths[facidx];
+            int rc = Z_OK;
+
+            fstFread(mc, xc->rvat_chain_table_lengths[facidx], 1, xc->f);
+
+            switch (xc->rvat_packtype) {
+            case '4':
+                rc = (destlen ==
+                      (unsigned long)LZ4_decompress_safe_partial((char *)mc, (char *)mu, sourcelen, destlen, destlen))
+                             ? Z_OK
+                             : Z_DATA_ERROR;
+                break;
+            case 'F':
+                fastlz_decompress(mc, sourcelen, mu, destlen); /* rc appears unreliable */
+                break;
+            default:
+                rc = uncompress(mu, &destlen, mc, sourcelen);
+                break;
+            }
+
+            free(mc);
+
+            if (rc != Z_OK) {
+                fprintf(stderr,
+                        FST_APIMESS "fstReaderGetValueFromHandleAtTime(), rvat decompress clen: %d (rc=%d), exiting.\n",
+                        (int)xc->rvat_chain_len, rc);
+                exit(255);
+            }
+
+            /* data to process is for(j=0;j<destlen;j++) in mu[j] */
+            xc->rvat_chain_mem = mu;
+        } else {
+            int destlen = xc->rvat_chain_table_lengths[facidx] - skiplen;
+            unsigned char *mu = (unsigned char *)malloc(xc->rvat_chain_len = destlen);
+            fstFread(mu, destlen, 1, xc->f);
+            /* data to process is for(j=0;j<destlen;j++) in mu[j] */
+            xc->rvat_chain_mem = mu;
+        }
+
+        xc->rvat_chain_facidx = facidx;
+    }
+
+    /* process value chain here */
+
+    {
+        uint32_t tidx = 0, ptidx = 0;
+        uint32_t tdelta;
+        int skiplen;
+        unsigned int iprev = xc->rvat_chain_len;
+        uint32_t pvli = 0;
+        int pskip = 0;
+
+        if ((xc->rvat_chain_pos_valid) && (tim >= xc->rvat_chain_pos_time)) {
+            i = xc->rvat_chain_pos_idx;
+            tidx = xc->rvat_chain_pos_tidx;
+        } else {
+            i = 0;
+            tidx = 0;
+            xc->rvat_chain_pos_time = xc->rvat_beg_tim;
+        }
+
+        if (xc->signal_lens[facidx] == 1) {
+            while (i < xc->rvat_chain_len) {
+                uint32_t vli = fstGetVarint32(xc->rvat_chain_mem + i, &skiplen);
+                uint32_t shcnt = 2 << (vli & 1);
+                tdelta = vli >> shcnt;
+
+                if (xc->rvat_time_table[tidx + tdelta] <= tim) {
+                    iprev = i;
+                    pvli = vli;
+                    ptidx = tidx;
+                    /* pskip = skiplen; */ /* scan-build */
+
+                    tidx += tdelta;
+                    i += skiplen;
+                } else {
+                    break;
+                }
+            }
+            if (iprev != xc->rvat_chain_len) {
+                xc->rvat_chain_pos_tidx = ptidx;
+                xc->rvat_chain_pos_idx = iprev;
+                xc->rvat_chain_pos_time = tim;
+                xc->rvat_chain_pos_valid = 1;
+
+                if (!(pvli & 1)) {
+                    buf[0] = ((pvli >> 1) & 1) | '0';
+                } else {
+                    buf[0] = FST_RCV_STR[((pvli >> 1) & 7)];
+                }
+                buf[1] = 0;
+                return (buf);
+            } else {
+                return (fstExtractRvatDataFromFrame(xc, facidx, buf));
+            }
+        } else {
+            while (i < xc->rvat_chain_len) {
+                uint32_t vli = fstGetVarint32(xc->rvat_chain_mem + i, &skiplen);
+                tdelta = vli >> 1;
+
+                if (xc->rvat_time_table[tidx + tdelta] <= tim) {
+                    iprev = i;
+                    pvli = vli;
+                    ptidx = tidx;
+                    pskip = skiplen;
+
+                    tidx += tdelta;
+                    i += skiplen;
+
+                    if (!(pvli & 1)) {
+                        i += ((xc->signal_lens[facidx] + 7) / 8);
+                    } else {
+                        i += xc->signal_lens[facidx];
+                    }
+                } else {
+                    break;
+                }
+            }
+
+            if (iprev != xc->rvat_chain_len) {
+                unsigned char *vdata = xc->rvat_chain_mem + iprev + pskip;
+
+                xc->rvat_chain_pos_tidx = ptidx;
+                xc->rvat_chain_pos_idx = iprev;
+                xc->rvat_chain_pos_time = tim;
+                xc->rvat_chain_pos_valid = 1;
+
+                if (xc->signal_typs[facidx] != FST_VT_VCD_REAL) {
+                    if (!(pvli & 1)) {
+                        int byte = 0;
+                        int bit;
+                        unsigned int j;
+
+                        for (j = 0; j < xc->signal_lens[facidx]; j++) {
+                            unsigned char ch;
+                            byte = j / 8;
+                            bit = 7 - (j & 7);
+                            ch = ((vdata[byte] >> bit) & 1) | '0';
+                            buf[j] = ch;
+                        }
+                        buf[j] = 0;
+
+                        return (buf);
+                    } else {
+                        memcpy(buf, vdata, xc->signal_lens[facidx]);
+                        buf[xc->signal_lens[facidx]] = 0;
+                        return (buf);
+                    }
+                } else {
+                    double d;
+                    unsigned char *clone_d = (unsigned char *)&d;
+                    unsigned char bufd[8];
+                    unsigned char *srcdata;
+
+                    if (!(pvli & 1)) /* very rare case, but possible */
+                    {
+                        int bit;
+                        int j;
+
+                        for (j = 0; j < 8; j++) {
+                            unsigned char ch;
+                            bit = 7 - (j & 7);
+                            ch = ((vdata[0] >> bit) & 1) | '0';
+                            bufd[j] = ch;
+                        }
+
+                        srcdata = bufd;
+                    } else {
+                        srcdata = vdata;
+                    }
+
+                    if (xc->double_endian_match) {
+                        memcpy(clone_d, srcdata, 8);
+                    } else {
+                        int j;
+
+                        for (j = 0; j < 8; j++) {
+                            clone_d[j] = srcdata[7 - j];
+                        }
+                    }
+
+                    sprintf(buf, "r%.16g", d);
+                    return (buf);
+                }
+            } else {
+                return (fstExtractRvatDataFromFrame(xc, facidx, buf));
+            }
+        }
+    }
+
+    /* return(NULL); */
+}
+
+/**********************************************************************/
+#ifndef _WAVE_HAVE_JUDY
+
+/***********************/
+/***                 ***/
+/***  jenkins hash   ***/
+/***                 ***/
+/***********************/
+
+/*
+--------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+For every delta with one or two bits set, and the deltas of all three
+  high bits or all three low bits, whether the original value of a,b,c
+  is almost all zero or is uniformly distributed,
+* If mix() is run forward or backward, at least 32 bits in a,b,c
+  have at least 1/4 probability of changing.
+* If mix() is run forward, every bit of c will change between 1/3 and
+  2/3 of the time.  (Well, 22/100 and 78/100 for some 2-bit deltas.)
+mix() was built out of 36 single-cycle latency instructions in a
+  structure that could supported 2x parallelism, like so:
+      a -= b;
+      a -= c; x = (c>>13);
+      b -= c; a ^= x;
+      b -= a; x = (a<<8);
+      c -= a; b ^= x;
+      c -= b; x = (b>>13);
+      ...
+  Unfortunately, superscalar Pentiums and Sparcs can't take advantage
+  of that parallelism.  They've also turned some of those single-cycle
+  latency instructions into multi-cycle latency instructions.  Still,
+  this is the fastest good hash I could find.  There were about 2^^68
+  to choose from.  I only looked at a billion or so.
+--------------------------------------------------------------------
+*/
+#define mix(a, b, c)                                                                                                   \
+    {                                                                                                                  \
+        a -= b;                                                                                                        \
+        a -= c;                                                                                                        \
+        a ^= (c >> 13);                                                                                                \
+        b -= c;                                                                                                        \
+        b -= a;                                                                                                        \
+        b ^= (a << 8);                                                                                                 \
+        c -= a;                                                                                                        \
+        c -= b;                                                                                                        \
+        c ^= (b >> 13);                                                                                                \
+        a -= b;                                                                                                        \
+        a -= c;                                                                                                        \
+        a ^= (c >> 12);                                                                                                \
+        b -= c;                                                                                                        \
+        b -= a;                                                                                                        \
+        b ^= (a << 16);                                                                                                \
+        c -= a;                                                                                                        \
+        c -= b;                                                                                                        \
+        c ^= (b >> 5);                                                                                                 \
+        a -= b;                                                                                                        \
+        a -= c;                                                                                                        \
+        a ^= (c >> 3);                                                                                                 \
+        b -= c;                                                                                                        \
+        b -= a;                                                                                                        \
+        b ^= (a << 10);                                                                                                \
+        c -= a;                                                                                                        \
+        c -= b;                                                                                                        \
+        c ^= (b >> 15);                                                                                                \
+    }
+
+/*
+--------------------------------------------------------------------
+j_hash() -- hash a variable-length key into a 32-bit value
+  k       : the key (the unaligned variable-length array of bytes)
+  len     : the length of the key, counting by bytes
+  initval : can be any 4-byte value
+Returns a 32-bit value.  Every bit of the key affects every bit of
+the return value.  Every 1-bit and 2-bit delta achieves avalanche.
+About 6*len+35 instructions.
+
+The best hash table sizes are powers of 2.  There is no need to do
+mod a prime (mod is sooo slow!).  If you need less than 32 bits,
+use a bitmask.  For example, if you need only 10 bits, do
+  h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (uint8_t **)k, do it like this:
+  for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
+
+By Bob Jenkins, 1996.  bob_jenkins@burtleburtle.net.  You may use this
+code any way you wish, private, educational, or commercial.  It's free.
+
+See http://burtleburtle.net/bob/hash/evahash.html
+Use for hash table lookup, or anything where one collision in 2^^32 is
+acceptable.  Do NOT use for cryptographic purposes.
+--------------------------------------------------------------------
+*/
+
+static uint32_t j_hash(const uint8_t *k, uint32_t length, uint32_t initval)
+{
+    uint32_t a, b, c, len;
+
+    /* Set up the internal state */
+    len = length;
+    a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
+    c = initval;        /* the previous hash value */
+
+    /*---------------------------------------- handle most of the key */
+    while (len >= 12) {
+        a += (k[0] + ((uint32_t)k[1] << 8) + ((uint32_t)k[2] << 16) + ((uint32_t)k[3] << 24));
+        b += (k[4] + ((uint32_t)k[5] << 8) + ((uint32_t)k[6] << 16) + ((uint32_t)k[7] << 24));
+        c += (k[8] + ((uint32_t)k[9] << 8) + ((uint32_t)k[10] << 16) + ((uint32_t)k[11] << 24));
+        mix(a, b, c);
+        k += 12;
+        len -= 12;
+    }
+
+    /*------------------------------------- handle the last 11 bytes */
+    c += length;
+    switch (len) /* all the case statements fall through */
+    {
+    case 11:
+        c += ((uint32_t)k[10] << 24); /* fallthrough */
+    case 10:
+        c += ((uint32_t)k[9] << 16); /* fallthrough */
+    case 9:
+        c += ((uint32_t)k[8] << 8); /* fallthrough */
+                                    /* the first byte of c is reserved for the length */
+    case 8:
+        b += ((uint32_t)k[7] << 24); /* fallthrough */
+    case 7:
+        b += ((uint32_t)k[6] << 16); /* fallthrough */
+    case 6:
+        b += ((uint32_t)k[5] << 8); /* fallthrough */
+    case 5:
+        b += k[4]; /* fallthrough */
+    case 4:
+        a += ((uint32_t)k[3] << 24); /* fallthrough */
+    case 3:
+        a += ((uint32_t)k[2] << 16); /* fallthrough */
+    case 2:
+        a += ((uint32_t)k[1] << 8); /* fallthrough */
+    case 1:
+        a += k[0];
+        /* case 0: nothing left to add */
+    }
+    mix(a, b, c);
+    /*-------------------------------------------- report the result */
+    return (c);
+}
+
+/********************************************************************/
+
+/***************************/
+/***                     ***/
+/***  judy HS emulation  ***/
+/***                     ***/
+/***************************/
+
+struct collchain_t
+{
+    struct collchain_t *next;
+    void *payload;
+    uint32_t fullhash, length;
+    unsigned char mem[1];
+};
+
+void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint32_t hashmask)
+{
+    struct collchain_t ***base = (struct collchain_t ***)base_i;
+    uint32_t hf, h;
+    struct collchain_t **ar;
+    struct collchain_t *chain, *pchain;
+
+    if (!*base) {
+        *base = (struct collchain_t **)calloc(1, (hashmask + 1) * sizeof(void *));
+    }
+    ar = *base;
+
+    h = (hf = j_hash(mem, length, length)) & hashmask;
+    pchain = chain = ar[h];
+    while (chain) {
+        if ((chain->fullhash == hf) && (chain->length == length) && !memcmp(chain->mem, mem, length)) {
+            if (pchain != chain) /* move hit to front */
+            {
+                pchain->next = chain->next;
+                chain->next = ar[h];
+                ar[h] = chain;
+            }
+            return (&(chain->payload));
+        }
+
+        pchain = chain;
+        chain = chain->next;
+    }
+
+    chain = (struct collchain_t *)calloc(1, sizeof(struct collchain_t) + length - 1);
+    memcpy(chain->mem, mem, length);
+    chain->fullhash = hf;
+    chain->length = length;
+    chain->next = ar[h];
+    ar[h] = chain;
+    return (&(chain->payload));
+}
+
+void JenkinsFree(void *base_i, uint32_t hashmask)
+{
+    struct collchain_t ***base = (struct collchain_t ***)base_i;
+    uint32_t h;
+    struct collchain_t **ar;
+    struct collchain_t *chain, *chain_next;
+
+    if (base && *base) {
+        ar = *base;
+        for (h = 0; h <= hashmask; h++) {
+            chain = ar[h];
+            while (chain) {
+                chain_next = chain->next;
+                free(chain);
+                chain = chain_next;
+            }
+        }
+
+        free(*base);
+        *base = NULL;
+    }
+}
+
+#endif
+
+/**********************************************************************/
+
+/************************/
+/***                  ***/
+/*** utility function ***/
+/***                  ***/
+/************************/
+
+int fstUtilityBinToEscConvertedLen(const unsigned char *s, int len)
+{
+    const unsigned char *src = s;
+    int dlen = 0;
+    int i;
+
+    for (i = 0; i < len; i++) {
+        switch (src[i]) {
+        case '\a': /* fallthrough */
+        case '\b': /* fallthrough */
+        case '\f': /* fallthrough */
+        case '\n': /* fallthrough */
+        case '\r': /* fallthrough */
+        case '\t': /* fallthrough */
+        case '\v': /* fallthrough */
+        case '\'': /* fallthrough */
+        case '\"': /* fallthrough */
+        case '\\': /* fallthrough */
+        case '\?':
+            dlen += 2;
+            break;
+        default:
+            if ((src[i] > ' ') && (src[i] <= '~')) /* no white spaces in output */
+            {
+                dlen++;
+            } else {
+                dlen += 4;
+            }
+            break;
+        }
+    }
+
+    return (dlen);
+}
+
+int fstUtilityBinToEsc(unsigned char *d, const unsigned char *s, int len)
+{
+    const unsigned char *src = s;
+    unsigned char *dst = d;
+    unsigned char val;
+    int i;
+
+    for (i = 0; i < len; i++) {
+        switch (src[i]) {
+        case '\a':
+            *(dst++) = '\\';
+            *(dst++) = 'a';
+            break;
+        case '\b':
+            *(dst++) = '\\';
+            *(dst++) = 'b';
+            break;
+        case '\f':
+            *(dst++) = '\\';
+            *(dst++) = 'f';
+            break;
+        case '\n':
+            *(dst++) = '\\';
+            *(dst++) = 'n';
+            break;
+        case '\r':
+            *(dst++) = '\\';
+            *(dst++) = 'r';
+            break;
+        case '\t':
+            *(dst++) = '\\';
+            *(dst++) = 't';
+            break;
+        case '\v':
+            *(dst++) = '\\';
+            *(dst++) = 'v';
+            break;
+        case '\'':
+            *(dst++) = '\\';
+            *(dst++) = '\'';
+            break;
+        case '\"':
+            *(dst++) = '\\';
+            *(dst++) = '\"';
+            break;
+        case '\\':
+            *(dst++) = '\\';
+            *(dst++) = '\\';
+            break;
+        case '\?':
+            *(dst++) = '\\';
+            *(dst++) = '\?';
+            break;
+        default:
+            if ((src[i] > ' ') && (src[i] <= '~')) /* no white spaces in output */
+            {
+                *(dst++) = src[i];
+            } else {
+                val = src[i];
+                *(dst++) = '\\';
+                *(dst++) = (val / 64) + '0';
+                val = val & 63;
+                *(dst++) = (val / 8) + '0';
+                val = val & 7;
+                *(dst++) = (val) + '0';
+            }
+            break;
+        }
+    }
+
+    return (dst - d);
+}
+
+/*
+ * this overwrites the original string if the destination pointer is NULL
+ */
+int fstUtilityEscToBin(unsigned char *d, unsigned char *s, int len)
+{
+    unsigned char *src = s;
+    unsigned char *dst = (!d) ? s : (s = d);
+    unsigned char val[3];
+    int i;
+
+    for (i = 0; i < len; i++) {
+        if (src[i] != '\\') {
+            *(dst++) = src[i];
+        } else {
+            switch (src[++i]) {
+            case 'a':
+                *(dst++) = '\a';
+                break;
+            case 'b':
+                *(dst++) = '\b';
+                break;
+            case 'f':
+                *(dst++) = '\f';
+                break;
+            case 'n':
+                *(dst++) = '\n';
+                break;
+            case 'r':
+                *(dst++) = '\r';
+                break;
+            case 't':
+                *(dst++) = '\t';
+                break;
+            case 'v':
+                *(dst++) = '\v';
+                break;
+            case '\'':
+                *(dst++) = '\'';
+                break;
+            case '\"':
+                *(dst++) = '\"';
+                break;
+            case '\\':
+                *(dst++) = '\\';
+                break;
+            case '\?':
+                *(dst++) = '\?';
+                break;
+
+            case 'x':
+                val[0] = toupper(src[++i]);
+                val[1] = toupper(src[++i]);
+                val[0] = ((val[0] >= 'A') && (val[0] <= 'F')) ? (val[0] - 'A' + 10) : (val[0] - '0');
+                val[1] = ((val[1] >= 'A') && (val[1] <= 'F')) ? (val[1] - 'A' + 10) : (val[1] - '0');
+                *(dst++) = val[0] * 16 + val[1];
+                break;
+
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+                val[0] = src[i] - '0';
+                val[1] = src[++i] - '0';
+                val[2] = src[++i] - '0';
+                *(dst++) = val[0] * 64 + val[1] * 8 + val[2];
+                break;
+
+            default:
+                *(dst++) = src[i];
+                break;
+            }
+        }
+    }
+
+    return (dst - s);
+}
+
+struct fstETab *fstUtilityExtractEnumTableFromString(const char *s)
+{
+    struct fstETab *et = NULL;
+    int num_spaces = 0;
+    int i;
+    int newlen;
+
+    if (s) {
+        const char *csp = strchr(s, ' ');
+        int cnt = atoi(csp + 1);
+
+        for (;;) {
+            csp = strchr(csp + 1, ' ');
+            if (csp) {
+                num_spaces++;
+            } else {
+                break;
+            }
+        }
+
+        if (num_spaces == (2 * cnt)) {
+            char *sp, *sp2;
+
+            et = (struct fstETab *)calloc(1, sizeof(struct fstETab));
+            et->elem_count = cnt;
+            et->name = strdup(s);
+            et->literal_arr = (char **)calloc(cnt, sizeof(char *));
+            et->val_arr = (char **)calloc(cnt, sizeof(char *));
+
+            sp = strchr(et->name, ' ');
+            *sp = 0;
+
+            sp = strchr(sp + 1, ' ');
+
+            for (i = 0; i < cnt; i++) {
+                sp2 = strchr(sp + 1, ' ');
+                *(char *)sp2 = 0;
+                et->literal_arr[i] = sp + 1;
+                sp = sp2;
+
+                newlen = fstUtilityEscToBin(NULL, (unsigned char *)et->literal_arr[i], strlen(et->literal_arr[i]));
+                et->literal_arr[i][newlen] = 0;
+            }
+
+            for (i = 0; i < cnt; i++) {
+                sp2 = strchr(sp + 1, ' ');
+                if (sp2) {
+                    *sp2 = 0;
+                }
+                et->val_arr[i] = sp + 1;
+                sp = sp2;
+
+                newlen = fstUtilityEscToBin(NULL, (unsigned char *)et->val_arr[i], strlen(et->val_arr[i]));
+                et->val_arr[i][newlen] = 0;
+            }
+        }
+    }
+
+    return (et);
+}
+
+void fstUtilityFreeEnumTable(struct fstETab *etab)
+{
+    if (etab) {
+        free(etab->literal_arr);
+        free(etab->val_arr);
+        free(etab->name);
+        free(etab);
+    }
+}
diff --git a/libs/fst/fstapi.h b/libs/fst/fstapi.h
new file mode 100644 (file)
index 0000000..ca8e300
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2009-2018 Tony Bybell.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef FST_API_H
+#define FST_API_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <zlib.h>
+#include <inttypes.h>
+#if defined(_MSC_VER)
+#include "fst_win_unistd.h"
+#else
+#include <unistd.h>
+#endif
+#include <time.h>
+
+#define FST_RDLOAD "FSTLOAD | "
+
+typedef uint32_t fstHandle;
+typedef uint32_t fstEnumHandle;
+
+enum fstWriterPackType
+{
+    FST_WR_PT_ZLIB = 0,
+    FST_WR_PT_FASTLZ = 1,
+    FST_WR_PT_LZ4 = 2
+};
+
+enum fstFileType
+{
+    FST_FT_MIN = 0,
+
+    FST_FT_VERILOG = 0,
+    FST_FT_VHDL = 1,
+    FST_FT_VERILOG_VHDL = 2,
+
+    FST_FT_MAX = 2
+};
+
+enum fstBlockType
+{
+    FST_BL_HDR = 0,
+    FST_BL_VCDATA = 1,
+    FST_BL_BLACKOUT = 2,
+    FST_BL_GEOM = 3,
+    FST_BL_HIER = 4,
+    FST_BL_VCDATA_DYN_ALIAS = 5,
+    FST_BL_HIER_LZ4 = 6,
+    FST_BL_HIER_LZ4DUO = 7,
+    FST_BL_VCDATA_DYN_ALIAS2 = 8,
+
+    FST_BL_ZWRAPPER = 254, /* indicates that whole trace is gz wrapped */
+    FST_BL_SKIP = 255      /* used while block is being written */
+};
+
+enum fstScopeType
+{
+    FST_ST_MIN = 0,
+
+    FST_ST_VCD_MODULE = 0,
+    FST_ST_VCD_TASK = 1,
+    FST_ST_VCD_FUNCTION = 2,
+    FST_ST_VCD_BEGIN = 3,
+    FST_ST_VCD_FORK = 4,
+    FST_ST_VCD_GENERATE = 5,
+    FST_ST_VCD_STRUCT = 6,
+    FST_ST_VCD_UNION = 7,
+    FST_ST_VCD_CLASS = 8,
+    FST_ST_VCD_INTERFACE = 9,
+    FST_ST_VCD_PACKAGE = 10,
+    FST_ST_VCD_PROGRAM = 11,
+
+    FST_ST_VHDL_ARCHITECTURE = 12,
+    FST_ST_VHDL_PROCEDURE = 13,
+    FST_ST_VHDL_FUNCTION = 14,
+    FST_ST_VHDL_RECORD = 15,
+    FST_ST_VHDL_PROCESS = 16,
+    FST_ST_VHDL_BLOCK = 17,
+    FST_ST_VHDL_FOR_GENERATE = 18,
+    FST_ST_VHDL_IF_GENERATE = 19,
+    FST_ST_VHDL_GENERATE = 20,
+    FST_ST_VHDL_PACKAGE = 21,
+
+    FST_ST_MAX = 21,
+
+    FST_ST_GEN_ATTRBEGIN = 252,
+    FST_ST_GEN_ATTREND = 253,
+
+    FST_ST_VCD_SCOPE = 254,
+    FST_ST_VCD_UPSCOPE = 255
+};
+
+enum fstVarType
+{
+    FST_VT_MIN = 0, /* start of vartypes */
+
+    FST_VT_VCD_EVENT = 0,
+    FST_VT_VCD_INTEGER = 1,
+    FST_VT_VCD_PARAMETER = 2,
+    FST_VT_VCD_REAL = 3,
+    FST_VT_VCD_REAL_PARAMETER = 4,
+    FST_VT_VCD_REG = 5,
+    FST_VT_VCD_SUPPLY0 = 6,
+    FST_VT_VCD_SUPPLY1 = 7,
+    FST_VT_VCD_TIME = 8,
+    FST_VT_VCD_TRI = 9,
+    FST_VT_VCD_TRIAND = 10,
+    FST_VT_VCD_TRIOR = 11,
+    FST_VT_VCD_TRIREG = 12,
+    FST_VT_VCD_TRI0 = 13,
+    FST_VT_VCD_TRI1 = 14,
+    FST_VT_VCD_WAND = 15,
+    FST_VT_VCD_WIRE = 16,
+    FST_VT_VCD_WOR = 17,
+    FST_VT_VCD_PORT = 18,
+    FST_VT_VCD_SPARRAY = 19, /* used to define the rownum (index) port for a sparse array */
+    FST_VT_VCD_REALTIME = 20,
+
+    FST_VT_GEN_STRING =
+            21, /* generic string type   (max len is defined dynamically via fstWriterEmitVariableLengthValueChange) */
+
+    FST_VT_SV_BIT = 22,
+    FST_VT_SV_LOGIC = 23,
+    FST_VT_SV_INT = 24,      /* declare as size = 32 */
+    FST_VT_SV_SHORTINT = 25, /* declare as size = 16 */
+    FST_VT_SV_LONGINT = 26,  /* declare as size = 64 */
+    FST_VT_SV_BYTE = 27,     /* declare as size = 8  */
+    FST_VT_SV_ENUM = 28,     /* declare as appropriate type range */
+    FST_VT_SV_SHORTREAL =
+            29, /* declare and emit same as FST_VT_VCD_REAL (needs to be emitted as double, not a float) */
+
+    FST_VT_MAX = 29 /* end of vartypes */
+};
+
+enum fstVarDir
+{
+    FST_VD_MIN = 0,
+
+    FST_VD_IMPLICIT = 0,
+    FST_VD_INPUT = 1,
+    FST_VD_OUTPUT = 2,
+    FST_VD_INOUT = 3,
+    FST_VD_BUFFER = 4,
+    FST_VD_LINKAGE = 5,
+
+    FST_VD_MAX = 5
+};
+
+enum fstHierType
+{
+    FST_HT_MIN = 0,
+
+    FST_HT_SCOPE = 0,
+    FST_HT_UPSCOPE = 1,
+    FST_HT_VAR = 2,
+    FST_HT_ATTRBEGIN = 3,
+    FST_HT_ATTREND = 4,
+
+    /* FST_HT_TREEBEGIN and FST_HT_TREEEND are not yet used by FST but are currently used when fstHier bridges other
+       formats */
+    FST_HT_TREEBEGIN = 5,
+    FST_HT_TREEEND = 6,
+
+    FST_HT_MAX = 6
+};
+
+enum fstAttrType
+{
+    FST_AT_MIN = 0,
+
+    FST_AT_MISC = 0, /* self-contained: does not need matching FST_HT_ATTREND */
+    FST_AT_ARRAY = 1,
+    FST_AT_ENUM = 2,
+    FST_AT_PACK = 3,
+
+    FST_AT_MAX = 3
+};
+
+enum fstMiscType
+{
+    FST_MT_MIN = 0,
+
+    FST_MT_COMMENT = 0,     /* use fstWriterSetComment() to emit */
+    FST_MT_ENVVAR = 1,      /* use fstWriterSetEnvVar() to emit */
+    FST_MT_SUPVAR = 2,      /* use fstWriterCreateVar2() to emit */
+    FST_MT_PATHNAME = 3,    /* reserved for fstWriterSetSourceStem() string -> number management */
+    FST_MT_SOURCESTEM = 4,  /* use fstWriterSetSourceStem() to emit */
+    FST_MT_SOURCEISTEM = 5, /* use fstWriterSetSourceInstantiationStem() to emit */
+    FST_MT_VALUELIST = 6,   /* use fstWriterSetValueList() to emit, followed by fstWriterCreateVar*() */
+    FST_MT_ENUMTABLE = 7,   /* use fstWriterCreateEnumTable() and fstWriterEmitEnumTableRef() to emit */
+    FST_MT_UNKNOWN = 8,
+
+    FST_MT_MAX = 8
+};
+
+enum fstArrayType
+{
+    FST_AR_MIN = 0,
+
+    FST_AR_NONE = 0,
+    FST_AR_UNPACKED = 1,
+    FST_AR_PACKED = 2,
+    FST_AR_SPARSE = 3,
+
+    FST_AR_MAX = 3
+};
+
+enum fstEnumValueType
+{
+    FST_EV_SV_INTEGER = 0,
+    FST_EV_SV_BIT = 1,
+    FST_EV_SV_LOGIC = 2,
+    FST_EV_SV_INT = 3,
+    FST_EV_SV_SHORTINT = 4,
+    FST_EV_SV_LONGINT = 5,
+    FST_EV_SV_BYTE = 6,
+    FST_EV_SV_UNSIGNED_INTEGER = 7,
+    FST_EV_SV_UNSIGNED_BIT = 8,
+    FST_EV_SV_UNSIGNED_LOGIC = 9,
+    FST_EV_SV_UNSIGNED_INT = 10,
+    FST_EV_SV_UNSIGNED_SHORTINT = 11,
+    FST_EV_SV_UNSIGNED_LONGINT = 12,
+    FST_EV_SV_UNSIGNED_BYTE = 13,
+
+    FST_EV_REG = 14,
+    FST_EV_TIME = 15,
+
+    FST_EV_MAX = 15
+};
+
+enum fstPackType
+{
+    FST_PT_NONE = 0,
+    FST_PT_UNPACKED = 1,
+    FST_PT_PACKED = 2,
+    FST_PT_TAGGED_PACKED = 3,
+
+    FST_PT_MAX = 3
+};
+
+enum fstSupplementalVarType
+{
+    FST_SVT_MIN = 0,
+
+    FST_SVT_NONE = 0,
+
+    FST_SVT_VHDL_SIGNAL = 1,
+    FST_SVT_VHDL_VARIABLE = 2,
+    FST_SVT_VHDL_CONSTANT = 3,
+    FST_SVT_VHDL_FILE = 4,
+    FST_SVT_VHDL_MEMORY = 5,
+
+    FST_SVT_MAX = 5
+};
+
+enum fstSupplementalDataType
+{
+    FST_SDT_MIN = 0,
+
+    FST_SDT_NONE = 0,
+
+    FST_SDT_VHDL_BOOLEAN = 1,
+    FST_SDT_VHDL_BIT = 2,
+    FST_SDT_VHDL_BIT_VECTOR = 3,
+    FST_SDT_VHDL_STD_ULOGIC = 4,
+    FST_SDT_VHDL_STD_ULOGIC_VECTOR = 5,
+    FST_SDT_VHDL_STD_LOGIC = 6,
+    FST_SDT_VHDL_STD_LOGIC_VECTOR = 7,
+    FST_SDT_VHDL_UNSIGNED = 8,
+    FST_SDT_VHDL_SIGNED = 9,
+    FST_SDT_VHDL_INTEGER = 10,
+    FST_SDT_VHDL_REAL = 11,
+    FST_SDT_VHDL_NATURAL = 12,
+    FST_SDT_VHDL_POSITIVE = 13,
+    FST_SDT_VHDL_TIME = 14,
+    FST_SDT_VHDL_CHARACTER = 15,
+    FST_SDT_VHDL_STRING = 16,
+
+    FST_SDT_MAX = 16,
+
+    FST_SDT_SVT_SHIFT_COUNT =
+            10, /* FST_SVT_* is ORed in by fstWriterCreateVar2() to the left after shifting FST_SDT_SVT_SHIFT_COUNT */
+    FST_SDT_ABS_MAX = ((1 << (FST_SDT_SVT_SHIFT_COUNT)) - 1)
+};
+
+struct fstHier
+{
+    unsigned char htyp;
+
+    union
+    {
+        /* if htyp == FST_HT_SCOPE */
+        struct fstHierScope
+        {
+            unsigned char typ; /* FST_ST_MIN ... FST_ST_MAX */
+            const char *name;
+            const char *component;
+            uint32_t name_length;      /* strlen(u.scope.name) */
+            uint32_t component_length; /* strlen(u.scope.component) */
+        } scope;
+
+        /* if htyp == FST_HT_VAR */
+        struct fstHierVar
+        {
+            unsigned char typ;           /* FST_VT_MIN ... FST_VT_MAX */
+            unsigned char direction;     /* FST_VD_MIN ... FST_VD_MAX */
+            unsigned char svt_workspace; /* zeroed out by FST reader, for client code use */
+            unsigned char sdt_workspace; /* zeroed out by FST reader, for client code use */
+            unsigned int sxt_workspace;  /* zeroed out by FST reader, for client code use */
+            const char *name;
+            uint32_t length;
+            fstHandle handle;
+            uint32_t name_length; /* strlen(u.var.name) */
+            unsigned is_alias : 1;
+        } var;
+
+        /* if htyp == FST_HT_ATTRBEGIN */
+        struct fstHierAttr
+        {
+            unsigned char typ;     /* FST_AT_MIN ... FST_AT_MAX */
+            unsigned char subtype; /* from fstMiscType, fstArrayType, fstEnumValueType, fstPackType */
+            const char *name;
+            uint64_t arg; /* number of array elements, struct members, or some other payload (possibly ignored) */
+            uint64_t arg_from_name; /* for when name is overloaded as a variable-length integer (FST_AT_MISC +
+                                       FST_MT_SOURCESTEM) */
+            uint32_t name_length;   /* strlen(u.attr.name) */
+        } attr;
+    } u;
+};
+
+struct fstETab
+{
+    char *name;
+    uint32_t elem_count;
+    char **literal_arr;
+    char **val_arr;
+};
+
+/*
+ * writer functions
+ */
+void fstWriterClose(void *ctx);
+void *fstWriterCreate(const char *nam, int use_compressed_hier);
+fstEnumHandle fstWriterCreateEnumTable(void *ctx, const char *name, uint32_t elem_count, unsigned int min_valbits,
+                                       const char **literal_arr, const char **val_arr);
+/* used for Verilog/SV */
+fstHandle fstWriterCreateVar(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam,
+                             fstHandle aliasHandle);
+/* future expansion for VHDL and other languages.  The variable type, data type, etc map onto
+   the current Verilog/SV one.  The "type" string is optional for a more verbose or custom description */
+fstHandle fstWriterCreateVar2(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam,
+                              fstHandle aliasHandle, const char *type, enum fstSupplementalVarType svt,
+                              enum fstSupplementalDataType sdt);
+void fstWriterEmitDumpActive(void *ctx, int enable);
+void fstWriterEmitEnumTableRef(void *ctx, fstEnumHandle handle);
+void fstWriterEmitValueChange(void *ctx, fstHandle handle, const void *val);
+void fstWriterEmitValueChange32(void *ctx, fstHandle handle, uint32_t bits, uint32_t val);
+void fstWriterEmitValueChange64(void *ctx, fstHandle handle, uint32_t bits, uint64_t val);
+void fstWriterEmitValueChangeVec32(void *ctx, fstHandle handle, uint32_t bits, const uint32_t *val);
+void fstWriterEmitValueChangeVec64(void *ctx, fstHandle handle, uint32_t bits, const uint64_t *val);
+void fstWriterEmitVariableLengthValueChange(void *ctx, fstHandle handle, const void *val, uint32_t len);
+void fstWriterEmitTimeChange(void *ctx, uint64_t tim);
+void fstWriterFlushContext(void *ctx);
+int fstWriterGetDumpSizeLimitReached(void *ctx);
+int fstWriterGetFseekFailed(void *ctx);
+void fstWriterSetAttrBegin(void *ctx, enum fstAttrType attrtype, int subtype, const char *attrname, uint64_t arg);
+void fstWriterSetAttrEnd(void *ctx);
+void fstWriterSetComment(void *ctx, const char *comm);
+void fstWriterSetDate(void *ctx, const char *dat);
+void fstWriterSetDumpSizeLimit(void *ctx, uint64_t numbytes);
+void fstWriterSetEnvVar(void *ctx, const char *envvar);
+void fstWriterSetFileType(void *ctx, enum fstFileType filetype);
+void fstWriterSetPackType(void *ctx, enum fstWriterPackType typ);
+void fstWriterSetParallelMode(void *ctx, int enable);
+void fstWriterSetRepackOnClose(void *ctx, int enable); /* type = 0 (none), 1 (libz) */
+void fstWriterSetScope(void *ctx, enum fstScopeType scopetype, const char *scopename, const char *scopecomp);
+void fstWriterSetSourceInstantiationStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath);
+void fstWriterSetSourceStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath);
+void fstWriterSetTimescale(void *ctx, int ts);
+void fstWriterSetTimescaleFromString(void *ctx, const char *s);
+void fstWriterSetTimezero(void *ctx, int64_t tim);
+void fstWriterSetUpscope(void *ctx);
+void fstWriterSetValueList(void *ctx, const char *vl);
+void fstWriterSetVersion(void *ctx, const char *vers);
+
+/*
+ * reader functions
+ */
+void fstReaderClose(void *ctx);
+void fstReaderClrFacProcessMask(void *ctx, fstHandle facidx);
+void fstReaderClrFacProcessMaskAll(void *ctx);
+uint64_t fstReaderGetAliasCount(void *ctx);
+const char *fstReaderGetCurrentFlatScope(void *ctx);
+void *fstReaderGetCurrentScopeUserInfo(void *ctx);
+int fstReaderGetCurrentScopeLen(void *ctx);
+const char *fstReaderGetDateString(void *ctx);
+int fstReaderGetDoubleEndianMatchState(void *ctx);
+uint64_t fstReaderGetDumpActivityChangeTime(void *ctx, uint32_t idx);
+unsigned char fstReaderGetDumpActivityChangeValue(void *ctx, uint32_t idx);
+uint64_t fstReaderGetEndTime(void *ctx);
+int fstReaderGetFacProcessMask(void *ctx, fstHandle facidx);
+int fstReaderGetFileType(void *ctx);
+int fstReaderGetFseekFailed(void *ctx);
+fstHandle fstReaderGetMaxHandle(void *ctx);
+uint64_t fstReaderGetMemoryUsedByWriter(void *ctx);
+uint32_t fstReaderGetNumberDumpActivityChanges(void *ctx);
+uint64_t fstReaderGetScopeCount(void *ctx);
+uint64_t fstReaderGetStartTime(void *ctx);
+signed char fstReaderGetTimescale(void *ctx);
+int64_t fstReaderGetTimezero(void *ctx);
+uint64_t fstReaderGetValueChangeSectionCount(void *ctx);
+char *fstReaderGetValueFromHandleAtTime(void *ctx, uint64_t tim, fstHandle facidx, char *buf);
+uint64_t fstReaderGetVarCount(void *ctx);
+const char *fstReaderGetVersionString(void *ctx);
+struct fstHier *fstReaderIterateHier(void *ctx);
+int fstReaderIterateHierRewind(void *ctx);
+int fstReaderIterBlocks(void *ctx,
+                        void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx,
+                                                      const unsigned char *value),
+                        void *user_callback_data_pointer, FILE *vcdhandle);
+int fstReaderIterBlocks2(void *ctx,
+                         void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time,
+                                                       fstHandle facidx, const unsigned char *value),
+                         void (*value_change_callback_varlen)(void *user_callback_data_pointer, uint64_t time,
+                                                              fstHandle facidx, const unsigned char *value,
+                                                              uint32_t len),
+                         void *user_callback_data_pointer, FILE *vcdhandle);
+void fstReaderIterBlocksSetNativeDoublesOnCallback(void *ctx, int enable);
+void *fstReaderOpen(const char *nam);
+void *fstReaderOpenForUtilitiesOnly(void);
+const char *fstReaderPopScope(void *ctx);
+int fstReaderProcessHier(void *ctx, FILE *vcdhandle);
+const char *fstReaderPushScope(void *ctx, const char *nam, void *user_info);
+void fstReaderResetScope(void *ctx);
+void fstReaderSetFacProcessMask(void *ctx, fstHandle facidx);
+void fstReaderSetFacProcessMaskAll(void *ctx);
+void fstReaderSetLimitTimeRange(void *ctx, uint64_t start_time, uint64_t end_time);
+void fstReaderSetUnlimitedTimeRange(void *ctx);
+void fstReaderSetVcdExtensions(void *ctx, int enable);
+
+/*
+ * utility functions
+ */
+int fstUtilityBinToEscConvertedLen(const unsigned char *s, int len); /* used for mallocs for fstUtilityBinToEsc() */
+int fstUtilityBinToEsc(unsigned char *d, const unsigned char *s, int len);
+int fstUtilityEscToBin(unsigned char *d, unsigned char *s, int len);
+struct fstETab *fstUtilityExtractEnumTableFromString(const char *s);
+void fstUtilityFreeEnumTable(struct fstETab *etab); /* must use to free fstETab properly */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libs/fst/lz4.cc b/libs/fst/lz4.cc
new file mode 100644 (file)
index 0000000..7e94f24
--- /dev/null
@@ -0,0 +1,1615 @@
+/*
+   LZ4 - Fast LZ compression algorithm
+   Copyright (C) 2011-2015, Yann Collet.
+
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   SPDX-License-Identifier: BSD-2-Clause
+
+   You can contact the author at :
+   - LZ4 source repository : https://github.com/Cyan4973/lz4
+   - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+*/
+
+/**************************************
+ *  Tuning parameters
+ **************************************/
+/*
+ * HEAPMODE :
+ * Select how default compression functions will allocate memory for their hash table,
+ * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).
+ */
+#define HEAPMODE 0
+
+/*
+ * ACCELERATION_DEFAULT :
+ * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0
+ */
+#define ACCELERATION_DEFAULT 1
+
+/**************************************
+ *  CPU Feature Detection
+ **************************************/
+/*
+ * LZ4_FORCE_SW_BITCOUNT
+ * Define this parameter if your target system or compiler does not support hardware bit count
+ */
+#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */
+#define LZ4_FORCE_SW_BITCOUNT
+#endif
+
+/**************************************
+ *  Includes
+ **************************************/
+#include "lz4.h"
+
+/**************************************
+ *  Compiler Options
+ **************************************/
+#ifdef _MSC_VER /* Visual Studio */
+#define FORCE_INLINE static __forceinline
+#include <intrin.h>
+#pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+#pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */
+#else
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */
+#if defined(__GNUC__) || defined(__clang__)
+#define FORCE_INLINE static inline __attribute__((always_inline))
+#else
+#define FORCE_INLINE static inline
+#endif
+#else
+#define FORCE_INLINE static
+#endif /* __STDC_VERSION__ */
+#endif /* _MSC_VER */
+
+/* LZ4_GCC_VERSION is defined into lz4.h */
+#if (LZ4_GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__)
+#define expect(expr, value) (__builtin_expect((expr), (value)))
+#else
+#define expect(expr, value) (expr)
+#endif
+
+#define likely(expr) expect((expr) != 0, 1)
+#define unlikely(expr) expect((expr) != 0, 0)
+
+/**************************************
+ *  Memory routines
+ **************************************/
+#include <stdlib.h> /* malloc, calloc, free */
+#define ALLOCATOR(n, s) calloc(n, s)
+#define FREEMEM free
+#include <string.h> /* memset, memcpy */
+#define MEM_INIT memset
+
+/**************************************
+ *  Basic Types
+ **************************************/
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */
+#include <stdint.h>
+typedef uint8_t BYTE;
+typedef uint16_t U16;
+typedef uint32_t U32;
+typedef int32_t S32;
+typedef uint64_t U64;
+#else
+typedef unsigned char BYTE;
+typedef unsigned short U16;
+typedef unsigned int U32;
+typedef signed int S32;
+typedef unsigned long long U64;
+#endif
+
+/**************************************
+ *  Reading and writing into memory
+ **************************************/
+#define STEPSIZE sizeof(size_t)
+
+static unsigned LZ4_64bits(void) { return sizeof(void *) == 8; }
+
+static unsigned LZ4_isLittleEndian(void)
+{
+    const union
+    {
+        U32 i;
+        BYTE c[4];
+    } one = {1}; /* don't use static : performance detrimental  */
+    return one.c[0];
+}
+
+static U16 LZ4_read16(const void *memPtr)
+{
+    U16 val16;
+    memcpy(&val16, memPtr, 2);
+    return val16;
+}
+
+static U16 LZ4_readLE16(const void *memPtr)
+{
+    if (LZ4_isLittleEndian()) {
+        return LZ4_read16(memPtr);
+    } else {
+        const BYTE *p = (const BYTE *)memPtr;
+        return (U16)((U16)p[0] + (p[1] << 8));
+    }
+}
+
+static void LZ4_writeLE16(void *memPtr, U16 value)
+{
+    if (LZ4_isLittleEndian()) {
+        memcpy(memPtr, &value, 2);
+    } else {
+        BYTE *p = (BYTE *)memPtr;
+        p[0] = (BYTE)value;
+        p[1] = (BYTE)(value >> 8);
+    }
+}
+
+static U32 LZ4_read32(const void *memPtr)
+{
+    U32 val32;
+    memcpy(&val32, memPtr, 4);
+    return val32;
+}
+
+static U64 LZ4_read64(const void *memPtr)
+{
+    U64 val64;
+    memcpy(&val64, memPtr, 8);
+    return val64;
+}
+
+static size_t LZ4_read_ARCH(const void *p)
+{
+    if (LZ4_64bits())
+        return (size_t)LZ4_read64(p);
+    else
+        return (size_t)LZ4_read32(p);
+}
+
+static void LZ4_copy4(void *dstPtr, const void *srcPtr) { memcpy(dstPtr, srcPtr, 4); }
+
+static void LZ4_copy8(void *dstPtr, const void *srcPtr) { memcpy(dstPtr, srcPtr, 8); }
+
+/* customized version of memcpy, which may overwrite up to 7 bytes beyond dstEnd */
+static void LZ4_wildCopy(void *dstPtr, const void *srcPtr, void *dstEnd)
+{
+    BYTE *d = (BYTE *)dstPtr;
+    const BYTE *s = (const BYTE *)srcPtr;
+    BYTE *e = (BYTE *)dstEnd;
+    do {
+        LZ4_copy8(d, s);
+        d += 8;
+        s += 8;
+    } while (d < e);
+}
+
+/**************************************
+ *  Common Constants
+ **************************************/
+#define MINMATCH 4
+
+#define COPYLENGTH 8
+#define LASTLITERALS 5
+#define MFLIMIT (COPYLENGTH + MINMATCH)
+static const int LZ4_minLength = (MFLIMIT + 1);
+
+#define KB *(1 << 10)
+#define MB *(1 << 20)
+#define GB *(1U << 30)
+
+#define MAXD_LOG 16
+#ifdef MAX_DISTANCE
+#undef MAX_DISTANCE
+#endif
+#define MAX_DISTANCE ((1 << MAXD_LOG) - 1)
+
+#define ML_BITS 4
+#define ML_MASK ((1U << ML_BITS) - 1)
+#define RUN_BITS (8 - ML_BITS)
+#define RUN_MASK ((1U << RUN_BITS) - 1)
+
+/**************************************
+ *  Common Utils
+ **************************************/
+#define LZ4_STATIC_ASSERT(c)                                                                                           \
+    {                                                                                                                  \
+        enum                                                                                                           \
+        {                                                                                                              \
+            LZ4_static_assert = 1 / (int)(!!(c))                                                                       \
+        };                                                                                                             \
+    } /* use only *after* variable declarations */
+
+/**************************************
+ *  Common functions
+ **************************************/
+static unsigned LZ4_NbCommonBytes(size_t val)
+{
+    if (LZ4_isLittleEndian()) {
+        if (LZ4_64bits()) {
+#if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            unsigned long r = 0;
+            _BitScanForward64(&r, (U64)val);
+            return (int)(r >> 3);
+#elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            return (__builtin_ctzll((U64)val) >> 3);
+#else
+            static const int DeBruijnBytePos[64] = {0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5,
+                                                    3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5,
+                                                    3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7};
+            return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+#endif
+        } else /* 32 bits */
+        {
+#if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            unsigned long r;
+            _BitScanForward(&r, (U32)val);
+            return (int)(r >> 3);
+#elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            return (__builtin_ctz((U32)val) >> 3);
+#else
+            static const int DeBruijnBytePos[32] = {0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1,
+                                                    3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1};
+            return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+#endif
+        }
+    } else /* Big Endian CPU */
+    {
+        if (LZ4_64bits()) {
+#if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            unsigned long r = 0;
+            _BitScanReverse64(&r, val);
+            return (unsigned)(r >> 3);
+#elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            return (__builtin_clzll((U64)val) >> 3);
+#else
+            unsigned r;
+            if (!(val >> 32)) {
+                r = 4;
+            } else {
+                r = 0;
+                val >>= 32;
+            }
+            if (!(val >> 16)) {
+                r += 2;
+                val >>= 8;
+            } else {
+                val >>= 24;
+            }
+            r += (!val);
+            return r;
+#endif
+        } else /* 32 bits */
+        {
+#if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            unsigned long r = 0;
+            _BitScanReverse(&r, (unsigned long)val);
+            return (unsigned)(r >> 3);
+#elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            return (__builtin_clz((U32)val) >> 3);
+#else
+            unsigned r;
+            if (!(val >> 16)) {
+                r = 2;
+                val >>= 8;
+            } else {
+                r = 0;
+                val >>= 24;
+            }
+            r += (!val);
+            return r;
+#endif
+        }
+    }
+}
+
+static unsigned LZ4_count(const BYTE *pIn, const BYTE *pMatch, const BYTE *pInLimit)
+{
+    const BYTE *const pStart = pIn;
+
+    while (likely(pIn < pInLimit - (STEPSIZE - 1))) {
+        size_t diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
+        if (!diff) {
+            pIn += STEPSIZE;
+            pMatch += STEPSIZE;
+            continue;
+        }
+        pIn += LZ4_NbCommonBytes(diff);
+        return (unsigned)(pIn - pStart);
+    }
+
+    if (LZ4_64bits())
+        if ((pIn < (pInLimit - 3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) {
+            pIn += 4;
+            pMatch += 4;
+        }
+    if ((pIn < (pInLimit - 1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) {
+        pIn += 2;
+        pMatch += 2;
+    }
+    if ((pIn < pInLimit) && (*pMatch == *pIn))
+        pIn++;
+    return (unsigned)(pIn - pStart);
+}
+
+#ifndef LZ4_COMMONDEFS_ONLY
+/**************************************
+ *  Local Constants
+ **************************************/
+#define LZ4_HASHLOG (LZ4_MEMORY_USAGE - 2)
+#define HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
+#define HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
+
+static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT - 1));
+static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression run slower on incompressible data */
+
+/**************************************
+ *  Local Structures and types
+ **************************************/
+typedef struct
+{
+    U32 hashTable[HASH_SIZE_U32];
+    U32 currentOffset;
+    U32 initCheck;
+    const BYTE *dictionary;
+    BYTE *bufferStart; /* obsolete, used for slideInputBuffer */
+    U32 dictSize;
+} LZ4_stream_t_internal;
+
+typedef enum
+{
+    notLimited = 0,
+    limitedOutput = 1
+} limitedOutput_directive;
+typedef enum
+{
+    byPtr,
+    byU32,
+    byU16
+} tableType_t;
+
+typedef enum
+{
+    noDict = 0,
+    withPrefix64k,
+    usingExtDict
+} dict_directive;
+typedef enum
+{
+    noDictIssue = 0,
+    dictSmall
+} dictIssue_directive;
+
+typedef enum
+{
+    endOnOutputSize = 0,
+    endOnInputSize = 1
+} endCondition_directive;
+typedef enum
+{
+    full = 0,
+    partial = 1
+} earlyEnd_directive;
+
+/**************************************
+ *  Local Utils
+ **************************************/
+int LZ4_versionNumber(void) { return LZ4_VERSION_NUMBER; }
+int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
+int LZ4_sizeofState() { return LZ4_STREAMSIZE; }
+
+/********************************
+ *  Compression functions
+ ********************************/
+
+static U32 LZ4_hashSequence(U32 sequence, tableType_t const tableType)
+{
+    if (tableType == byU16)
+        return (((sequence)*2654435761U) >> ((MINMATCH * 8) - (LZ4_HASHLOG + 1)));
+    else
+        return (((sequence)*2654435761U) >> ((MINMATCH * 8) - LZ4_HASHLOG));
+}
+
+static const U64 prime5bytes = 889523592379ULL;
+static U32 LZ4_hashSequence64(size_t sequence, tableType_t const tableType)
+{
+    const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG + 1 : LZ4_HASHLOG;
+    const U32 hashMask = (1 << hashLog) - 1;
+    return ((sequence * prime5bytes) >> (40 - hashLog)) & hashMask;
+}
+
+static U32 LZ4_hashSequenceT(size_t sequence, tableType_t const tableType)
+{
+    if (LZ4_64bits())
+        return LZ4_hashSequence64(sequence, tableType);
+    return LZ4_hashSequence((U32)sequence, tableType);
+}
+
+static U32 LZ4_hashPosition(const void *p, tableType_t tableType)
+{
+    return LZ4_hashSequenceT(LZ4_read_ARCH(p), tableType);
+}
+
+static void LZ4_putPositionOnHash(const BYTE *p, U32 h, void *tableBase, tableType_t const tableType,
+                                  const BYTE *srcBase)
+{
+    switch (tableType) {
+    case byPtr: {
+        const BYTE **hashTable = (const BYTE **)tableBase;
+        hashTable[h] = p;
+        return;
+    }
+    case byU32: {
+        U32 *hashTable = (U32 *)tableBase;
+        hashTable[h] = (U32)(p - srcBase);
+        return;
+    }
+    case byU16: {
+        U16 *hashTable = (U16 *)tableBase;
+        hashTable[h] = (U16)(p - srcBase);
+        return;
+    }
+    }
+}
+
+static void LZ4_putPosition(const BYTE *p, void *tableBase, tableType_t tableType, const BYTE *srcBase)
+{
+    U32 h = LZ4_hashPosition(p, tableType);
+    LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);
+}
+
+static const BYTE *LZ4_getPositionOnHash(U32 h, void *tableBase, tableType_t tableType, const BYTE *srcBase)
+{
+    if (tableType == byPtr) {
+        const BYTE **hashTable = (const BYTE **)tableBase;
+        return hashTable[h];
+    }
+    if (tableType == byU32) {
+        U32 *hashTable = (U32 *)tableBase;
+        return hashTable[h] + srcBase;
+    }
+    {
+        U16 *hashTable = (U16 *)tableBase;
+        return hashTable[h] + srcBase;
+    } /* default, to ensure a return */
+}
+
+static const BYTE *LZ4_getPosition(const BYTE *p, void *tableBase, tableType_t tableType, const BYTE *srcBase)
+{
+    U32 h = LZ4_hashPosition(p, tableType);
+    return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);
+}
+
+FORCE_INLINE int LZ4_compress_generic(void *const ctx, const char *const source, char *const dest, const int inputSize,
+                                      const int maxOutputSize, const limitedOutput_directive outputLimited,
+                                      const tableType_t tableType, const dict_directive dict,
+                                      const dictIssue_directive dictIssue, const U32 acceleration)
+{
+    LZ4_stream_t_internal *const dictPtr = (LZ4_stream_t_internal *)ctx;
+
+    const BYTE *ip = (const BYTE *)source;
+    const BYTE *base;
+    const BYTE *lowLimit;
+    const BYTE *const lowRefLimit = ip - dictPtr->dictSize;
+    const BYTE *const dictionary = dictPtr->dictionary;
+    const BYTE *const dictEnd = dictionary + dictPtr->dictSize;
+    const size_t dictDelta = dictEnd - (const BYTE *)source;
+    const BYTE *anchor = (const BYTE *)source;
+    const BYTE *const iend = ip + inputSize;
+    const BYTE *const mflimit = iend - MFLIMIT;
+    const BYTE *const matchlimit = iend - LASTLITERALS;
+
+    BYTE *op = (BYTE *)dest;
+    BYTE *const olimit = op + maxOutputSize;
+
+    U32 forwardH;
+    size_t refDelta = 0;
+
+    /* Init conditions */
+    if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE)
+        return 0; /* Unsupported input size, too large (or negative) */
+    switch (dict) {
+    case noDict:
+    default:
+        base = (const BYTE *)source;
+        lowLimit = (const BYTE *)source;
+        break;
+    case withPrefix64k:
+        base = (const BYTE *)source - dictPtr->currentOffset;
+        lowLimit = (const BYTE *)source - dictPtr->dictSize;
+        break;
+    case usingExtDict:
+        base = (const BYTE *)source - dictPtr->currentOffset;
+        lowLimit = (const BYTE *)source;
+        break;
+    }
+    if ((tableType == byU16) && (inputSize >= LZ4_64Klimit))
+        return 0; /* Size too large (not within 64K limit) */
+    if (inputSize < LZ4_minLength)
+        goto _last_literals; /* Input too small, no compression (all literals) */
+
+    /* First Byte */
+    LZ4_putPosition(ip, ctx, tableType, base);
+    ip++;
+    forwardH = LZ4_hashPosition(ip, tableType);
+
+    /* Main Loop */
+    for (;;) {
+        const BYTE *match;
+        BYTE *token;
+        {
+            const BYTE *forwardIp = ip;
+            unsigned step = 1;
+            unsigned searchMatchNb = acceleration << LZ4_skipTrigger;
+
+            /* Find a match */
+            do {
+                U32 h = forwardH;
+                ip = forwardIp;
+                forwardIp += step;
+                step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+                if (unlikely(forwardIp > mflimit))
+                    goto _last_literals;
+
+                match = LZ4_getPositionOnHash(h, ctx, tableType, base);
+                if (dict == usingExtDict) {
+                    if (match < (const BYTE *)source) {
+                        refDelta = dictDelta;
+                        lowLimit = dictionary;
+                    } else {
+                        refDelta = 0;
+                        lowLimit = (const BYTE *)source;
+                    }
+                }
+                forwardH = LZ4_hashPosition(forwardIp, tableType);
+                LZ4_putPositionOnHash(ip, h, ctx, tableType, base);
+
+            } while (((dictIssue == dictSmall) ? (match < lowRefLimit) : 0) ||
+                     ((tableType == byU16) ? 0 : (match + MAX_DISTANCE < ip)) ||
+                     (LZ4_read32(match + refDelta) != LZ4_read32(ip)));
+        }
+
+        /* Catch up */
+        while ((ip > anchor) && (match + refDelta > lowLimit) && (unlikely(ip[-1] == match[refDelta - 1]))) {
+            ip--;
+            match--;
+        }
+
+        {
+            /* Encode Literal length */
+            unsigned litLength = (unsigned)(ip - anchor);
+            token = op++;
+            if ((outputLimited) && (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength / 255) > olimit)))
+                return 0; /* Check output limit */
+            if (litLength >= RUN_MASK) {
+                int len = (int)litLength - RUN_MASK;
+                *token = (RUN_MASK << ML_BITS);
+                for (; len >= 255; len -= 255)
+                    *op++ = 255;
+                *op++ = (BYTE)len;
+            } else
+                *token = (BYTE)(litLength << ML_BITS);
+
+            /* Copy Literals */
+            LZ4_wildCopy(op, anchor, op + litLength);
+            op += litLength;
+        }
+
+    _next_match:
+        /* Encode Offset */
+        LZ4_writeLE16(op, (U16)(ip - match));
+        op += 2;
+
+        /* Encode MatchLength */
+        {
+            unsigned matchLength;
+
+            if ((dict == usingExtDict) && (lowLimit == dictionary)) {
+                const BYTE *limit;
+                match += refDelta;
+                limit = ip + (dictEnd - match);
+                if (limit > matchlimit)
+                    limit = matchlimit;
+                matchLength = LZ4_count(ip + MINMATCH, match + MINMATCH, limit);
+                ip += MINMATCH + matchLength;
+                if (ip == limit) {
+                    unsigned more = LZ4_count(ip, (const BYTE *)source, matchlimit);
+                    matchLength += more;
+                    ip += more;
+                }
+            } else {
+                matchLength = LZ4_count(ip + MINMATCH, match + MINMATCH, matchlimit);
+                ip += MINMATCH + matchLength;
+            }
+
+            if ((outputLimited) && (unlikely(op + (1 + LASTLITERALS) + (matchLength >> 8) > olimit)))
+                return 0; /* Check output limit */
+            if (matchLength >= ML_MASK) {
+                *token += ML_MASK;
+                matchLength -= ML_MASK;
+                for (; matchLength >= 510; matchLength -= 510) {
+                    *op++ = 255;
+                    *op++ = 255;
+                }
+                if (matchLength >= 255) {
+                    matchLength -= 255;
+                    *op++ = 255;
+                }
+                *op++ = (BYTE)matchLength;
+            } else
+                *token += (BYTE)(matchLength);
+        }
+
+        anchor = ip;
+
+        /* Test end of chunk */
+        if (ip > mflimit)
+            break;
+
+        /* Fill table */
+        LZ4_putPosition(ip - 2, ctx, tableType, base);
+
+        /* Test next position */
+        match = LZ4_getPosition(ip, ctx, tableType, base);
+        if (dict == usingExtDict) {
+            if (match < (const BYTE *)source) {
+                refDelta = dictDelta;
+                lowLimit = dictionary;
+            } else {
+                refDelta = 0;
+                lowLimit = (const BYTE *)source;
+            }
+        }
+        LZ4_putPosition(ip, ctx, tableType, base);
+        if (((dictIssue == dictSmall) ? (match >= lowRefLimit) : 1) && (match + MAX_DISTANCE >= ip) &&
+            (LZ4_read32(match + refDelta) == LZ4_read32(ip))) {
+            token = op++;
+            *token = 0;
+            goto _next_match;
+        }
+
+        /* Prepare next loop */
+        forwardH = LZ4_hashPosition(++ip, tableType);
+    }
+
+_last_literals:
+    /* Encode Last Literals */
+    {
+        const size_t lastRun = (size_t)(iend - anchor);
+        if ((outputLimited) &&
+            ((op - (BYTE *)dest) + lastRun + 1 + ((lastRun + 255 - RUN_MASK) / 255) > (U32)maxOutputSize))
+            return 0; /* Check output limit */
+        if (lastRun >= RUN_MASK) {
+            size_t accumulator = lastRun - RUN_MASK;
+            *op++ = RUN_MASK << ML_BITS;
+            for (; accumulator >= 255; accumulator -= 255)
+                *op++ = 255;
+            *op++ = (BYTE)accumulator;
+        } else {
+            *op++ = (BYTE)(lastRun << ML_BITS);
+        }
+        memcpy(op, anchor, lastRun);
+        op += lastRun;
+    }
+
+    /* End */
+    return (int)(((char *)op) - dest);
+}
+
+int LZ4_compress_fast_extState(void *state, const char *source, char *dest, int inputSize, int maxOutputSize,
+                               int acceleration)
+{
+    LZ4_resetStream((LZ4_stream_t *)state);
+    if (acceleration < 1)
+        acceleration = ACCELERATION_DEFAULT;
+
+    if (maxOutputSize >= LZ4_compressBound(inputSize)) {
+        if (inputSize < LZ4_64Klimit)
+            return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue,
+                                        acceleration);
+        else
+            return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, LZ4_64bits() ? byU32 : byPtr,
+                                        noDict, noDictIssue, acceleration);
+    } else {
+        if (inputSize < LZ4_64Klimit)
+            return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict,
+                                        noDictIssue, acceleration);
+        else
+            return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput,
+                                        LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+    }
+}
+
+int LZ4_compress_fast(const char *source, char *dest, int inputSize, int maxOutputSize, int acceleration)
+{
+#if (HEAPMODE)
+    void *ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
+#else
+    LZ4_stream_t ctx;
+    void *ctxPtr = &ctx;
+#endif
+
+    int result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);
+
+#if (HEAPMODE)
+    FREEMEM(ctxPtr);
+#endif
+    return result;
+}
+
+int LZ4_compress_default(const char *source, char *dest, int inputSize, int maxOutputSize)
+{
+    return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1);
+}
+
+/* hidden debug function */
+/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */
+int LZ4_compress_fast_force(const char *source, char *dest, int inputSize, int maxOutputSize, int acceleration)
+{
+    LZ4_stream_t ctx;
+
+    LZ4_resetStream(&ctx);
+
+    if (inputSize < LZ4_64Klimit)
+        return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict,
+                                    noDictIssue, acceleration);
+    else
+        return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput,
+                                    LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+}
+
+/********************************
+ *  destSize variant
+ ********************************/
+
+static int LZ4_compress_destSize_generic(void *const ctx, const char *const src, char *const dst, int *const srcSizePtr,
+                                         const int targetDstSize, const tableType_t tableType)
+{
+    const BYTE *ip = (const BYTE *)src;
+    const BYTE *base = (const BYTE *)src;
+    const BYTE *lowLimit = (const BYTE *)src;
+    const BYTE *anchor = ip;
+    const BYTE *const iend = ip + *srcSizePtr;
+    const BYTE *const mflimit = iend - MFLIMIT;
+    const BYTE *const matchlimit = iend - LASTLITERALS;
+
+    BYTE *op = (BYTE *)dst;
+    BYTE *const oend = op + targetDstSize;
+    BYTE *const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */;
+    BYTE *const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */);
+    BYTE *const oMaxSeq = oMaxLit - 1 /* token */;
+
+    U32 forwardH;
+
+    /* Init conditions */
+    if (targetDstSize < 1)
+        return 0; /* Impossible to store anything */
+    if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE)
+        return 0; /* Unsupported input size, too large (or negative) */
+    if ((tableType == byU16) && (*srcSizePtr >= LZ4_64Klimit))
+        return 0; /* Size too large (not within 64K limit) */
+    if (*srcSizePtr < LZ4_minLength)
+        goto _last_literals; /* Input too small, no compression (all literals) */
+
+    /* First Byte */
+    *srcSizePtr = 0;
+    LZ4_putPosition(ip, ctx, tableType, base);
+    ip++;
+    forwardH = LZ4_hashPosition(ip, tableType);
+
+    /* Main Loop */
+    for (;;) {
+        const BYTE *match;
+        BYTE *token;
+        {
+            const BYTE *forwardIp = ip;
+            unsigned step = 1;
+            unsigned searchMatchNb = 1 << LZ4_skipTrigger;
+
+            /* Find a match */
+            do {
+                U32 h = forwardH;
+                ip = forwardIp;
+                forwardIp += step;
+                step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+                if (unlikely(forwardIp > mflimit))
+                    goto _last_literals;
+
+                match = LZ4_getPositionOnHash(h, ctx, tableType, base);
+                forwardH = LZ4_hashPosition(forwardIp, tableType);
+                LZ4_putPositionOnHash(ip, h, ctx, tableType, base);
+
+            } while (((tableType == byU16) ? 0 : (match + MAX_DISTANCE < ip)) || (LZ4_read32(match) != LZ4_read32(ip)));
+        }
+
+        /* Catch up */
+        while ((ip > anchor) && (match > lowLimit) && (unlikely(ip[-1] == match[-1]))) {
+            ip--;
+            match--;
+        }
+
+        {
+            /* Encode Literal length */
+            unsigned litLength = (unsigned)(ip - anchor);
+            token = op++;
+            if (op + ((litLength + 240) / 255) + litLength > oMaxLit) {
+                /* Not enough space for a last match */
+                op--;
+                goto _last_literals;
+            }
+            if (litLength >= RUN_MASK) {
+                unsigned len = litLength - RUN_MASK;
+                *token = (RUN_MASK << ML_BITS);
+                for (; len >= 255; len -= 255)
+                    *op++ = 255;
+                *op++ = (BYTE)len;
+            } else
+                *token = (BYTE)(litLength << ML_BITS);
+
+            /* Copy Literals */
+            LZ4_wildCopy(op, anchor, op + litLength);
+            op += litLength;
+        }
+
+    _next_match:
+        /* Encode Offset */
+        LZ4_writeLE16(op, (U16)(ip - match));
+        op += 2;
+
+        /* Encode MatchLength */
+        {
+            size_t matchLength;
+
+            matchLength = LZ4_count(ip + MINMATCH, match + MINMATCH, matchlimit);
+
+            if (op + ((matchLength + 240) / 255) > oMaxMatch) {
+                /* Match description too long : reduce it */
+                matchLength = (15 - 1) + (oMaxMatch - op) * 255;
+            }
+            /*printf("offset %5i, matchLength%5i \n", (int)(ip-match), matchLength + MINMATCH);*/
+            ip += MINMATCH + matchLength;
+
+            if (matchLength >= ML_MASK) {
+                *token += ML_MASK;
+                matchLength -= ML_MASK;
+                while (matchLength >= 255) {
+                    matchLength -= 255;
+                    *op++ = 255;
+                }
+                *op++ = (BYTE)matchLength;
+            } else
+                *token += (BYTE)(matchLength);
+        }
+
+        anchor = ip;
+
+        /* Test end of block */
+        if (ip > mflimit)
+            break;
+        if (op > oMaxSeq)
+            break;
+
+        /* Fill table */
+        LZ4_putPosition(ip - 2, ctx, tableType, base);
+
+        /* Test next position */
+        match = LZ4_getPosition(ip, ctx, tableType, base);
+        LZ4_putPosition(ip, ctx, tableType, base);
+        if ((match + MAX_DISTANCE >= ip) && (LZ4_read32(match) == LZ4_read32(ip))) {
+            token = op++;
+            *token = 0;
+            goto _next_match;
+        }
+
+        /* Prepare next loop */
+        forwardH = LZ4_hashPosition(++ip, tableType);
+    }
+
+_last_literals:
+    /* Encode Last Literals */
+    {
+        size_t lastRunSize = (size_t)(iend - anchor);
+        if (op + 1 /* token */ + ((lastRunSize + 240) / 255) /* litLength */ + lastRunSize /* literals */ > oend) {
+            /* adapt lastRunSize to fill 'dst' */
+            lastRunSize = (oend - op) - 1;
+            lastRunSize -= (lastRunSize + 240) / 255;
+        }
+        ip = anchor + lastRunSize;
+
+        if (lastRunSize >= RUN_MASK) {
+            size_t accumulator = lastRunSize - RUN_MASK;
+            *op++ = RUN_MASK << ML_BITS;
+            for (; accumulator >= 255; accumulator -= 255)
+                *op++ = 255;
+            *op++ = (BYTE)accumulator;
+        } else {
+            *op++ = (BYTE)(lastRunSize << ML_BITS);
+        }
+        memcpy(op, anchor, lastRunSize);
+        op += lastRunSize;
+    }
+
+    /* End */
+    *srcSizePtr = (int)(((const char *)ip) - src);
+    return (int)(((char *)op) - dst);
+}
+
+static int LZ4_compress_destSize_extState(void *state, const char *src, char *dst, int *srcSizePtr, int targetDstSize)
+{
+    LZ4_resetStream((LZ4_stream_t *)state);
+
+    if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) /* compression success is guaranteed */
+    {
+        return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);
+    } else {
+        if (*srcSizePtr < LZ4_64Klimit)
+            return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, byU16);
+        else
+            return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize,
+                                                 LZ4_64bits() ? byU32 : byPtr);
+    }
+}
+
+int LZ4_compress_destSize(const char *src, char *dst, int *srcSizePtr, int targetDstSize)
+{
+#if (HEAPMODE)
+    void *ctx = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
+#else
+    LZ4_stream_t ctxBody;
+    void *ctx = &ctxBody;
+#endif
+
+    int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);
+
+#if (HEAPMODE)
+    FREEMEM(ctx);
+#endif
+    return result;
+}
+
+/********************************
+ *  Streaming functions
+ ********************************/
+
+LZ4_stream_t *LZ4_createStream(void)
+{
+    LZ4_stream_t *lz4s = (LZ4_stream_t *)ALLOCATOR(8, LZ4_STREAMSIZE_U64);
+    LZ4_STATIC_ASSERT(
+            LZ4_STREAMSIZE >=
+            sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */
+    LZ4_resetStream(lz4s);
+    return lz4s;
+}
+
+void LZ4_resetStream(LZ4_stream_t *LZ4_stream) { MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t)); }
+
+int LZ4_freeStream(LZ4_stream_t *LZ4_stream)
+{
+    FREEMEM(LZ4_stream);
+    return (0);
+}
+
+#define HASH_UNIT sizeof(size_t)
+int LZ4_loadDict(LZ4_stream_t *LZ4_dict, const char *dictionary, int dictSize)
+{
+    LZ4_stream_t_internal *dict = (LZ4_stream_t_internal *)LZ4_dict;
+    const BYTE *p = (const BYTE *)dictionary;
+    const BYTE *const dictEnd = p + dictSize;
+    const BYTE *base;
+
+    if ((dict->initCheck) || (dict->currentOffset > 1 GB)) /* Uninitialized structure, or reuse overflow */
+        LZ4_resetStream(LZ4_dict);
+
+    if (dictSize < (int)HASH_UNIT) {
+        dict->dictionary = NULL;
+        dict->dictSize = 0;
+        return 0;
+    }
+
+    if ((dictEnd - p) > 64 KB)
+        p = dictEnd - 64 KB;
+    dict->currentOffset += 64 KB;
+    base = p - dict->currentOffset;
+    dict->dictionary = p;
+    dict->dictSize = (U32)(dictEnd - p);
+    dict->currentOffset += dict->dictSize;
+
+    while (p <= dictEnd - HASH_UNIT) {
+        LZ4_putPosition(p, dict->hashTable, byU32, base);
+        p += 3;
+    }
+
+    return dict->dictSize;
+}
+
+static void LZ4_renormDictT(LZ4_stream_t_internal *LZ4_dict, const BYTE *src)
+{
+    if ((LZ4_dict->currentOffset > 0x80000000) ||
+        ((size_t)LZ4_dict->currentOffset > (size_t)src)) /* address space overflow */
+    {
+        /* rescale hash table */
+        U32 delta = LZ4_dict->currentOffset - 64 KB;
+        const BYTE *dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;
+        int i;
+        for (i = 0; i < HASH_SIZE_U32; i++) {
+            if (LZ4_dict->hashTable[i] < delta)
+                LZ4_dict->hashTable[i] = 0;
+            else
+                LZ4_dict->hashTable[i] -= delta;
+        }
+        LZ4_dict->currentOffset = 64 KB;
+        if (LZ4_dict->dictSize > 64 KB)
+            LZ4_dict->dictSize = 64 KB;
+        LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;
+    }
+}
+
+int LZ4_compress_fast_continue(LZ4_stream_t *LZ4_stream, const char *source, char *dest, int inputSize,
+                               int maxOutputSize, int acceleration)
+{
+    LZ4_stream_t_internal *streamPtr = (LZ4_stream_t_internal *)LZ4_stream;
+    const BYTE *const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
+
+    const BYTE *smallest = (const BYTE *)source;
+    if (streamPtr->initCheck)
+        return 0; /* Uninitialized structure detected */
+    if ((streamPtr->dictSize > 0) && (smallest > dictEnd))
+        smallest = dictEnd;
+    LZ4_renormDictT(streamPtr, smallest);
+    if (acceleration < 1)
+        acceleration = ACCELERATION_DEFAULT;
+
+    /* Check overlapping input/dictionary space */
+    {
+        const BYTE *sourceEnd = (const BYTE *)source + inputSize;
+        if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) {
+            streamPtr->dictSize = (U32)(dictEnd - sourceEnd);
+            if (streamPtr->dictSize > 64 KB)
+                streamPtr->dictSize = 64 KB;
+            if (streamPtr->dictSize < 4)
+                streamPtr->dictSize = 0;
+            streamPtr->dictionary = dictEnd - streamPtr->dictSize;
+        }
+    }
+
+    /* prefix mode : source data follows dictionary */
+    if (dictEnd == (const BYTE *)source) {
+        int result;
+        if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
+            result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32,
+                                          withPrefix64k, dictSmall, acceleration);
+        else
+            result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32,
+                                          withPrefix64k, noDictIssue, acceleration);
+        streamPtr->dictSize += (U32)inputSize;
+        streamPtr->currentOffset += (U32)inputSize;
+        return result;
+    }
+
+    /* external dictionary mode */
+    {
+        int result;
+        if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
+            result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32,
+                                          usingExtDict, dictSmall, acceleration);
+        else
+            result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32,
+                                          usingExtDict, noDictIssue, acceleration);
+        streamPtr->dictionary = (const BYTE *)source;
+        streamPtr->dictSize = (U32)inputSize;
+        streamPtr->currentOffset += (U32)inputSize;
+        return result;
+    }
+}
+
+/* Hidden debug function, to force external dictionary mode */
+int LZ4_compress_forceExtDict(LZ4_stream_t *LZ4_dict, const char *source, char *dest, int inputSize)
+{
+    LZ4_stream_t_internal *streamPtr = (LZ4_stream_t_internal *)LZ4_dict;
+    int result;
+    const BYTE *const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
+
+    const BYTE *smallest = dictEnd;
+    if (smallest > (const BYTE *)source)
+        smallest = (const BYTE *)source;
+    LZ4_renormDictT((LZ4_stream_t_internal *)LZ4_dict, smallest);
+
+    result =
+            LZ4_compress_generic(LZ4_dict, source, dest, inputSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);
+
+    streamPtr->dictionary = (const BYTE *)source;
+    streamPtr->dictSize = (U32)inputSize;
+    streamPtr->currentOffset += (U32)inputSize;
+
+    return result;
+}
+
+int LZ4_saveDict(LZ4_stream_t *LZ4_dict, char *safeBuffer, int dictSize)
+{
+    LZ4_stream_t_internal *dict = (LZ4_stream_t_internal *)LZ4_dict;
+    const BYTE *previousDictEnd = dict->dictionary + dict->dictSize;
+
+    if ((U32)dictSize > 64 KB)
+        dictSize = 64 KB; /* useless to define a dictionary > 64 KB */
+    if ((U32)dictSize > dict->dictSize)
+        dictSize = dict->dictSize;
+
+    memmove(safeBuffer, previousDictEnd - dictSize, dictSize);
+
+    dict->dictionary = (const BYTE *)safeBuffer;
+    dict->dictSize = (U32)dictSize;
+
+    return dictSize;
+}
+
+/*******************************
+ *  Decompression functions
+ *******************************/
+/*
+ * This generic decompression function cover all use cases.
+ * It shall be instantiated several times, using different sets of directives
+ * Note that it is essential this generic function is really inlined,
+ * in order to remove useless branches during compilation optimization.
+ */
+FORCE_INLINE int
+LZ4_decompress_generic(const char *const source, char *const dest, int inputSize,
+                       int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */
+
+                       int endOnInput,              /* endOnOutputSize, endOnInputSize */
+                       int partialDecoding,         /* full, partial */
+                       int targetOutputSize,        /* only used if partialDecoding==partial */
+                       int dict,                    /* noDict, withPrefix64k, usingExtDict */
+                       const BYTE *const lowPrefix, /* == dest if dict == noDict */
+                       const BYTE *const dictStart, /* only if dict==usingExtDict */
+                       const size_t dictSize        /* note : = 0 if noDict */
+)
+{
+    /* Local Variables */
+    const BYTE *ip = (const BYTE *)source;
+    const BYTE *const iend = ip + inputSize;
+
+    BYTE *op = (BYTE *)dest;
+    BYTE *const oend = op + outputSize;
+    BYTE *cpy;
+    BYTE *oexit = op + targetOutputSize;
+    const BYTE *const lowLimit = lowPrefix - dictSize;
+
+    const BYTE *const dictEnd = (const BYTE *)dictStart + dictSize;
+    const size_t dec32table[] = {4, 1, 2, 1, 4, 4, 4, 4};
+    const size_t dec64table[] = {0, 0, 0, (size_t)-1, 0, 1, 2, 3};
+
+    const int safeDecode = (endOnInput == endOnInputSize);
+    const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB)));
+
+    /* Special cases */
+    if ((partialDecoding) && (oexit > oend - MFLIMIT))
+        oexit = oend - MFLIMIT; /* targetOutputSize too high => decode everything */
+    if ((endOnInput) && (unlikely(outputSize == 0)))
+        return ((inputSize == 1) && (*ip == 0)) ? 0 : -1; /* Empty output buffer */
+    if ((!endOnInput) && (unlikely(outputSize == 0)))
+        return (*ip == 0 ? 1 : -1);
+
+    /* Main Loop */
+    while (1) {
+        unsigned token;
+        size_t length;
+        const BYTE *match;
+
+        /* get literal length */
+        token = *ip++;
+        if ((length = (token >> ML_BITS)) == RUN_MASK) {
+            unsigned s;
+            do {
+                s = *ip++;
+                length += s;
+            } while (likely((endOnInput) ? ip < iend - RUN_MASK : 1) && (s == 255));
+            if ((safeDecode) && unlikely((size_t)(op + length) < (size_t)(op)))
+                goto _output_error; /* overflow detection */
+            if ((safeDecode) && unlikely((size_t)(ip + length) < (size_t)(ip)))
+                goto _output_error; /* overflow detection */
+        }
+
+        /* copy literals */
+        cpy = op + length;
+        if (((endOnInput) &&
+             ((cpy > (partialDecoding ? oexit : oend - MFLIMIT)) || (ip + length > iend - (2 + 1 + LASTLITERALS)))) ||
+            ((!endOnInput) && (cpy > oend - COPYLENGTH))) {
+            if (partialDecoding) {
+                if (cpy > oend)
+                    goto _output_error; /* Error : write attempt beyond end of output buffer */
+                if ((endOnInput) && (ip + length > iend))
+                    goto _output_error; /* Error : read attempt beyond end of input buffer */
+            } else {
+                if ((!endOnInput) && (cpy != oend))
+                    goto _output_error; /* Error : block decoding must stop exactly there */
+                if ((endOnInput) && ((ip + length != iend) || (cpy > oend)))
+                    goto _output_error; /* Error : input must be consumed */
+            }
+            memcpy(op, ip, length);
+            ip += length;
+            op += length;
+            break; /* Necessarily EOF, due to parsing restrictions */
+        }
+        LZ4_wildCopy(op, ip, cpy);
+        ip += length;
+        op = cpy;
+
+        /* get offset */
+        match = cpy - LZ4_readLE16(ip);
+        ip += 2;
+        if ((checkOffset) && (unlikely(match < lowLimit)))
+            goto _output_error; /* Error : offset outside destination buffer */
+
+        /* get matchlength */
+        length = token & ML_MASK;
+        if (length == ML_MASK) {
+            unsigned s;
+            do {
+                if ((endOnInput) && (ip > iend - LASTLITERALS))
+                    goto _output_error;
+                s = *ip++;
+                length += s;
+            } while (s == 255);
+            if ((safeDecode) && unlikely((size_t)(op + length) < (size_t)op))
+                goto _output_error; /* overflow detection */
+        }
+        length += MINMATCH;
+
+        /* check external dictionary */
+        if ((dict == usingExtDict) && (match < lowPrefix)) {
+            if (unlikely(op + length > oend - LASTLITERALS))
+                goto _output_error; /* doesn't respect parsing restriction */
+
+            if (length <= (size_t)(lowPrefix - match)) {
+                /* match can be copied as a single segment from external dictionary */
+                match = dictEnd - (lowPrefix - match);
+                memmove(op, match, length);
+                op += length;
+            } else {
+                /* match encompass external dictionary and current segment */
+                size_t copySize = (size_t)(lowPrefix - match);
+                memcpy(op, dictEnd - copySize, copySize);
+                op += copySize;
+                copySize = length - copySize;
+                if (copySize > (size_t)(op - lowPrefix)) /* overlap within current segment */
+                {
+                    BYTE *const endOfMatch = op + copySize;
+                    const BYTE *copyFrom = lowPrefix;
+                    while (op < endOfMatch)
+                        *op++ = *copyFrom++;
+                } else {
+                    memcpy(op, lowPrefix, copySize);
+                    op += copySize;
+                }
+            }
+            continue;
+        }
+
+        /* copy repeated sequence */
+        cpy = op + length;
+        if (unlikely((op - match) < 8)) {
+            const size_t dec64 = dec64table[op - match];
+            op[0] = match[0];
+            op[1] = match[1];
+            op[2] = match[2];
+            op[3] = match[3];
+            match += dec32table[op - match];
+            LZ4_copy4(op + 4, match);
+            op += 8;
+            match -= dec64;
+        } else {
+            LZ4_copy8(op, match);
+            op += 8;
+            match += 8;
+        }
+
+        if (unlikely(cpy > oend - 12)) {
+            if (cpy > oend - LASTLITERALS)
+                goto _output_error; /* Error : last LASTLITERALS bytes must be literals */
+            if (op < oend - 8) {
+                LZ4_wildCopy(op, match, oend - 8);
+                match += (oend - 8) - op;
+                op = oend - 8;
+            }
+            while (op < cpy)
+                *op++ = *match++;
+        } else
+            LZ4_wildCopy(op, match, cpy);
+        op = cpy; /* correction */
+    }
+
+    /* end of decoding */
+    if (endOnInput)
+        return (int)(((char *)op) - dest); /* Nb of output bytes decoded */
+    else
+        return (int)(((const char *)ip) - source); /* Nb of input bytes read */
+
+    /* Overflow error detected */
+_output_error:
+    return (int)(-(((const char *)ip) - source)) - 1;
+}
+
+int LZ4_decompress_safe(const char *source, char *dest, int compressedSize, int maxDecompressedSize)
+{
+    return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict,
+                                  (BYTE *)dest, NULL, 0);
+}
+
+int LZ4_decompress_safe_partial(const char *source, char *dest, int compressedSize, int targetOutputSize,
+                                int maxDecompressedSize)
+{
+    return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, partial,
+                                  targetOutputSize, noDict, (BYTE *)dest, NULL, 0);
+}
+
+int LZ4_decompress_fast(const char *source, char *dest, int originalSize)
+{
+    return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k,
+                                  (BYTE *)(dest - 64 KB), NULL, 64 KB);
+}
+
+/* streaming decompression functions */
+
+typedef struct
+{
+    const BYTE *externalDict;
+    size_t extDictSize;
+    const BYTE *prefixEnd;
+    size_t prefixSize;
+} LZ4_streamDecode_t_internal;
+
+/*
+ * If you prefer dynamic allocation methods,
+ * LZ4_createStreamDecode()
+ * provides a pointer (void*) towards an initialized LZ4_streamDecode_t structure.
+ */
+LZ4_streamDecode_t *LZ4_createStreamDecode(void)
+{
+    LZ4_streamDecode_t *lz4s = (LZ4_streamDecode_t *)ALLOCATOR(1, sizeof(LZ4_streamDecode_t));
+    return lz4s;
+}
+
+int LZ4_freeStreamDecode(LZ4_streamDecode_t *LZ4_stream)
+{
+    FREEMEM(LZ4_stream);
+    return 0;
+}
+
+/*
+ * LZ4_setStreamDecode
+ * Use this function to instruct where to find the dictionary
+ * This function is not necessary if previous data is still available where it was decoded.
+ * Loading a size of 0 is allowed (same effect as no dictionary).
+ * Return : 1 if OK, 0 if error
+ */
+int LZ4_setStreamDecode(LZ4_streamDecode_t *LZ4_streamDecode, const char *dictionary, int dictSize)
+{
+    LZ4_streamDecode_t_internal *lz4sd = (LZ4_streamDecode_t_internal *)LZ4_streamDecode;
+    lz4sd->prefixSize = (size_t)dictSize;
+    lz4sd->prefixEnd = (const BYTE *)dictionary + dictSize;
+    lz4sd->externalDict = NULL;
+    lz4sd->extDictSize = 0;
+    return 1;
+}
+
+/*
+*_continue() :
+    These decoding functions allow decompression of multiple blocks in "streaming" mode.
+    Previously decoded blocks must still be available at the memory position where they were decoded.
+    If it's not possible, save the relevant part of decoded data into a safe buffer,
+    and indicate where it stands using LZ4_setStreamDecode()
+*/
+int LZ4_decompress_safe_continue(LZ4_streamDecode_t *LZ4_streamDecode, const char *source, char *dest,
+                                 int compressedSize, int maxOutputSize)
+{
+    LZ4_streamDecode_t_internal *lz4sd = (LZ4_streamDecode_t_internal *)LZ4_streamDecode;
+    int result;
+
+    if (lz4sd->prefixEnd == (BYTE *)dest) {
+        result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0,
+                                        usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict,
+                                        lz4sd->extDictSize);
+        if (result <= 0)
+            return result;
+        lz4sd->prefixSize += result;
+        lz4sd->prefixEnd += result;
+    } else {
+        lz4sd->extDictSize = lz4sd->prefixSize;
+        lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
+        result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0,
+                                        usingExtDict, (BYTE *)dest, lz4sd->externalDict, lz4sd->extDictSize);
+        if (result <= 0)
+            return result;
+        lz4sd->prefixSize = result;
+        lz4sd->prefixEnd = (BYTE *)dest + result;
+    }
+
+    return result;
+}
+
+int LZ4_decompress_fast_continue(LZ4_streamDecode_t *LZ4_streamDecode, const char *source, char *dest, int originalSize)
+{
+    LZ4_streamDecode_t_internal *lz4sd = (LZ4_streamDecode_t_internal *)LZ4_streamDecode;
+    int result;
+
+    if (lz4sd->prefixEnd == (BYTE *)dest) {
+        result = LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, usingExtDict,
+                                        lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
+        if (result <= 0)
+            return result;
+        lz4sd->prefixSize += originalSize;
+        lz4sd->prefixEnd += originalSize;
+    } else {
+        lz4sd->extDictSize = lz4sd->prefixSize;
+        lz4sd->externalDict = (BYTE *)dest - lz4sd->extDictSize;
+        result = LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, usingExtDict,
+                                        (BYTE *)dest, lz4sd->externalDict, lz4sd->extDictSize);
+        if (result <= 0)
+            return result;
+        lz4sd->prefixSize = originalSize;
+        lz4sd->prefixEnd = (BYTE *)dest + originalSize;
+    }
+
+    return result;
+}
+
+/*
+Advanced decoding functions :
+*_usingDict() :
+    These decoding functions work the same as "_continue" ones,
+    the dictionary must be explicitly provided within parameters
+*/
+
+FORCE_INLINE int LZ4_decompress_usingDict_generic(const char *source, char *dest, int compressedSize, int maxOutputSize,
+                                                  int safe, const char *dictStart, int dictSize)
+{
+    if (dictSize == 0)
+        return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE *)dest,
+                                      NULL, 0);
+    if (dictStart + dictSize == dest) {
+        if (dictSize >= (int)(64 KB - 1))
+            return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k,
+                                          (BYTE *)dest - 64 KB, NULL, 0);
+        return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict,
+                                      (BYTE *)dest - dictSize, NULL, 0);
+    }
+    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict,
+                                  (BYTE *)dest, (const BYTE *)dictStart, dictSize);
+}
+
+int LZ4_decompress_safe_usingDict(const char *source, char *dest, int compressedSize, int maxOutputSize,
+                                  const char *dictStart, int dictSize)
+{
+    return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize);
+}
+
+int LZ4_decompress_fast_usingDict(const char *source, char *dest, int originalSize, const char *dictStart, int dictSize)
+{
+    return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize);
+}
+
+/* debug function */
+int LZ4_decompress_safe_forceExtDict(const char *source, char *dest, int compressedSize, int maxOutputSize,
+                                     const char *dictStart, int dictSize)
+{
+    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict,
+                                  (BYTE *)dest, (const BYTE *)dictStart, dictSize);
+}
+
+/***************************************************
+ *  Obsolete Functions
+ ***************************************************/
+/* obsolete compression functions */
+int LZ4_compress_limitedOutput(const char *source, char *dest, int inputSize, int maxOutputSize)
+{
+    return LZ4_compress_default(source, dest, inputSize, maxOutputSize);
+}
+int LZ4_compress(const char *source, char *dest, int inputSize)
+{
+    return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize));
+}
+int LZ4_compress_limitedOutput_withState(void *state, const char *src, char *dst, int srcSize, int dstSize)
+{
+    return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1);
+}
+int LZ4_compress_withState(void *state, const char *src, char *dst, int srcSize)
+{
+    return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1);
+}
+int LZ4_compress_limitedOutput_continue(LZ4_stream_t *LZ4_stream, const char *src, char *dst, int srcSize,
+                                        int maxDstSize)
+{
+    return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1);
+}
+int LZ4_compress_continue(LZ4_stream_t *LZ4_stream, const char *source, char *dest, int inputSize)
+{
+    return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1);
+}
+
+/*
+These function names are deprecated and should no longer be used.
+They are only provided here for compatibility with older user programs.
+- LZ4_uncompress is totally equivalent to LZ4_decompress_fast
+- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe
+*/
+int LZ4_uncompress(const char *source, char *dest, int outputSize)
+{
+    return LZ4_decompress_fast(source, dest, outputSize);
+}
+int LZ4_uncompress_unknownOutputSize(const char *source, char *dest, int isize, int maxOutputSize)
+{
+    return LZ4_decompress_safe(source, dest, isize, maxOutputSize);
+}
+
+/* Obsolete Streaming functions */
+
+int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; }
+
+static void LZ4_init(LZ4_stream_t_internal *lz4ds, BYTE *base)
+{
+    MEM_INIT(lz4ds, 0, LZ4_STREAMSIZE);
+    lz4ds->bufferStart = base;
+}
+
+int LZ4_resetStreamState(void *state, char *inputBuffer)
+{
+    if ((((size_t)state) & 3) != 0)
+        return 1; /* Error : pointer is not aligned on 4-bytes boundary */
+    LZ4_init((LZ4_stream_t_internal *)state, (BYTE *)inputBuffer);
+    return 0;
+}
+
+void *LZ4_create(char *inputBuffer)
+{
+    void *lz4ds = ALLOCATOR(8, LZ4_STREAMSIZE_U64);
+    LZ4_init((LZ4_stream_t_internal *)lz4ds, (BYTE *)inputBuffer);
+    return lz4ds;
+}
+
+char *LZ4_slideInputBuffer(void *LZ4_Data)
+{
+    LZ4_stream_t_internal *ctx = (LZ4_stream_t_internal *)LZ4_Data;
+    int dictSize = LZ4_saveDict((LZ4_stream_t *)LZ4_Data, (char *)ctx->bufferStart, 64 KB);
+    return (char *)(ctx->bufferStart + dictSize);
+}
+
+/* Obsolete streaming decompression functions */
+
+int LZ4_decompress_safe_withPrefix64k(const char *source, char *dest, int compressedSize, int maxOutputSize)
+{
+    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k,
+                                  (BYTE *)dest - 64 KB, NULL, 64 KB);
+}
+
+int LZ4_decompress_fast_withPrefix64k(const char *source, char *dest, int originalSize)
+{
+    return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k,
+                                  (BYTE *)dest - 64 KB, NULL, 64 KB);
+}
+
+#endif /* LZ4_COMMONDEFS_ONLY */
diff --git a/libs/fst/lz4.h b/libs/fst/lz4.h
new file mode 100644 (file)
index 0000000..929cf02
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+   LZ4 - Fast LZ compression algorithm
+   Header File
+   Copyright (C) 2011-2015, Yann Collet.
+
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   SPDX-License-Identifier: BSD-2-Clause
+
+   You can contact the author at :
+   - LZ4 source repository : https://github.com/Cyan4973/lz4
+   - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+*/
+#pragma once
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * lz4.h provides block compression functions, and gives full buffer control to programmer.
+ * If you need to generate inter-operable compressed data (respecting LZ4 frame specification),
+ * and can let the library handle its own memory, please use lz4frame.h instead.
+ */
+
+/**************************************
+ *  Version
+ **************************************/
+#define LZ4_VERSION_MAJOR 1   /* for breaking interface changes  */
+#define LZ4_VERSION_MINOR 7   /* for new (non-breaking) interface capabilities */
+#define LZ4_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */
+#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR * 100 * 100 + LZ4_VERSION_MINOR * 100 + LZ4_VERSION_RELEASE)
+int LZ4_versionNumber(void);
+
+/**************************************
+ *  Tuning parameter
+ **************************************/
+/*
+ * LZ4_MEMORY_USAGE :
+ * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
+ * Increasing memory usage improves compression ratio
+ * Reduced memory usage can improve speed, due to cache effect
+ * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
+ */
+#define LZ4_MEMORY_USAGE 14
+
+/**************************************
+ *  Simple Functions
+ **************************************/
+
+int LZ4_compress_default(const char *source, char *dest, int sourceSize, int maxDestSize);
+int LZ4_decompress_safe(const char *source, char *dest, int compressedSize, int maxDecompressedSize);
+
+/*
+LZ4_compress_default() :
+    Compresses 'sourceSize' bytes from buffer 'source'
+    into already allocated 'dest' buffer of size 'maxDestSize'.
+    Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize).
+    It also runs faster, so it's a recommended setting.
+    If the function cannot compress 'source' into a more limited 'dest' budget,
+    compression stops *immediately*, and the function result is zero.
+    As a consequence, 'dest' content is not valid.
+    This function never writes outside 'dest' buffer, nor read outside 'source' buffer.
+        sourceSize  : Max supported value is LZ4_MAX_INPUT_VALUE
+        maxDestSize : full or partial size of buffer 'dest' (which must be already allocated)
+        return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize)
+              or 0 if compression fails
+
+LZ4_decompress_safe() :
+    compressedSize : is the precise full size of the compressed block.
+    maxDecompressedSize : is the size of destination buffer, which must be already allocated.
+    return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize)
+             If destination buffer is not large enough, decoding will stop and output an error code (<0).
+             If the source stream is detected malformed, the function will stop decoding and return a negative result.
+             This function is protected against buffer overflow exploits, including malicious data packets.
+             It never writes outside output buffer, nor reads outside input buffer.
+*/
+
+/**************************************
+ *  Advanced Functions
+ **************************************/
+#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
+#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize) / 255) + 16)
+
+/*
+LZ4_compressBound() :
+    Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
+    This function is primarily useful for memory allocation purposes (destination buffer size).
+    Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
+    Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize)
+        inputSize  : max supported value is LZ4_MAX_INPUT_SIZE
+        return : maximum output size in a "worst case" scenario
+              or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE)
+*/
+int LZ4_compressBound(int inputSize);
+
+/*
+LZ4_compress_fast() :
+    Same as LZ4_compress_default(), but allows to select an "acceleration" factor.
+    The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
+    It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
+    An acceleration value of "1" is the same as regular LZ4_compress_default()
+    Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1.
+*/
+int LZ4_compress_fast(const char *source, char *dest, int sourceSize, int maxDestSize, int acceleration);
+
+/*
+LZ4_compress_fast_extState() :
+    Same compression function, just using an externally allocated memory space to store compression state.
+    Use LZ4_sizeofState() to know how much memory must be allocated,
+    and allocate it on 8-bytes boundaries (using malloc() typically).
+    Then, provide it as 'void* state' to compression function.
+*/
+int LZ4_sizeofState(void);
+int LZ4_compress_fast_extState(void *state, const char *source, char *dest, int inputSize, int maxDestSize,
+                               int acceleration);
+
+/*
+LZ4_compress_destSize() :
+    Reverse the logic, by compressing as much data as possible from 'source' buffer
+    into already allocated buffer 'dest' of size 'targetDestSize'.
+    This function either compresses the entire 'source' content into 'dest' if it's large enough,
+    or fill 'dest' buffer completely with as much data as possible from 'source'.
+        *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'.
+                         New value is necessarily <= old value.
+        return : Nb bytes written into 'dest' (necessarily <= targetDestSize)
+              or 0 if compression fails
+*/
+int LZ4_compress_destSize(const char *source, char *dest, int *sourceSizePtr, int targetDestSize);
+
+/*
+LZ4_decompress_fast() :
+    originalSize : is the original and therefore uncompressed size
+    return : the number of bytes read from the source buffer (in other words, the compressed size)
+             If the source stream is detected malformed, the function will stop decoding and return a negative result.
+             Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes.
+    note : This function fully respect memory boundaries for properly formed compressed data.
+           It is a bit faster than LZ4_decompress_safe().
+           However, it does not provide any protection against intentionally modified data stream (malicious input).
+           Use this function in trusted environment only (data to decode comes from a trusted source).
+*/
+int LZ4_decompress_fast(const char *source, char *dest, int originalSize);
+
+/*
+LZ4_decompress_safe_partial() :
+    This function decompress a compressed block of size 'compressedSize' at position 'source'
+    into destination buffer 'dest' of size 'maxDecompressedSize'.
+    The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached,
+    reducing decompression time.
+    return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize)
+       Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.
+             Always control how many bytes were decoded.
+             If the source stream is detected malformed, the function will stop decoding and return a negative result.
+             This function never writes outside of output buffer, and never reads outside of input buffer. It is
+therefore protected against malicious data packets
+*/
+int LZ4_decompress_safe_partial(const char *source, char *dest, int compressedSize, int targetOutputSize,
+                                int maxDecompressedSize);
+
+/***********************************************
+ *  Streaming Compression Functions
+ ***********************************************/
+#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE - 3)) + 4)
+#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long))
+/*
+ * LZ4_stream_t
+ * information structure to track an LZ4 stream.
+ * important : init this structure content before first use !
+ * note : only allocated directly the structure if you are statically linking LZ4
+ *        If you are using liblz4 as a DLL, please use below construction methods instead.
+ */
+typedef struct
+{
+    long long table[LZ4_STREAMSIZE_U64];
+} LZ4_stream_t;
+
+/*
+ * LZ4_resetStream
+ * Use this function to init an allocated LZ4_stream_t structure
+ */
+void LZ4_resetStream(LZ4_stream_t *streamPtr);
+
+/*
+ * LZ4_createStream will allocate and initialize an LZ4_stream_t structure
+ * LZ4_freeStream releases its memory.
+ * In the context of a DLL (liblz4), please use these methods rather than the static struct.
+ * They are more future proof, in case of a change of LZ4_stream_t size.
+ */
+LZ4_stream_t *LZ4_createStream(void);
+int LZ4_freeStream(LZ4_stream_t *streamPtr);
+
+/*
+ * LZ4_loadDict
+ * Use this function to load a static dictionary into LZ4_stream.
+ * Any previous data will be forgotten, only 'dictionary' will remain in memory.
+ * Loading a size of 0 is allowed.
+ * Return : dictionary size, in bytes (necessarily <= 64 KB)
+ */
+int LZ4_loadDict(LZ4_stream_t *streamPtr, const char *dictionary, int dictSize);
+
+/*
+ * LZ4_compress_fast_continue
+ * Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression
+ * ratio. Important : Previous data blocks are assumed to still be present and unmodified ! 'dst' buffer must be already
+ * allocated. If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. If
+ * not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero.
+ */
+int LZ4_compress_fast_continue(LZ4_stream_t *streamPtr, const char *src, char *dst, int srcSize, int maxDstSize,
+                               int acceleration);
+
+/*
+ * LZ4_saveDict
+ * If previously compressed data block is not guaranteed to remain available at its memory location
+ * save it into a safer place (char* safeBuffer)
+ * Note : you don't need to call LZ4_loadDict() afterwards,
+ *        dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue()
+ * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error
+ */
+int LZ4_saveDict(LZ4_stream_t *streamPtr, char *safeBuffer, int dictSize);
+
+/************************************************
+ *  Streaming Decompression Functions
+ ************************************************/
+
+#define LZ4_STREAMDECODESIZE_U64 4
+#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
+typedef struct
+{
+    unsigned long long table[LZ4_STREAMDECODESIZE_U64];
+} LZ4_streamDecode_t;
+/*
+ * LZ4_streamDecode_t
+ * information structure to track an LZ4 stream.
+ * init this structure content using LZ4_setStreamDecode or memset() before first use !
+ *
+ * In the context of a DLL (liblz4) please prefer usage of construction methods below.
+ * They are more future proof, in case of a change of LZ4_streamDecode_t size in the future.
+ * LZ4_createStreamDecode will allocate and initialize an LZ4_streamDecode_t structure
+ * LZ4_freeStreamDecode releases its memory.
+ */
+LZ4_streamDecode_t *LZ4_createStreamDecode(void);
+int LZ4_freeStreamDecode(LZ4_streamDecode_t *LZ4_stream);
+
+/*
+ * LZ4_setStreamDecode
+ * Use this function to instruct where to find the dictionary.
+ * Setting a size of 0 is allowed (same effect as reset).
+ * Return : 1 if OK, 0 if error
+ */
+int LZ4_setStreamDecode(LZ4_streamDecode_t *LZ4_streamDecode, const char *dictionary, int dictSize);
+
+/*
+*_continue() :
+    These decoding functions allow decompression of multiple blocks in "streaming" mode.
+    Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB)
+    In the case of a ring buffers, decoding buffer must be either :
+    - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
+      In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
+    - Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
+      maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block.
+      In which case, encoding and decoding buffers do not need to be synchronized,
+      and encoding ring buffer can have any size, including small ones ( < 64 KB).
+    - _At least_ 64 KB + 8 bytes + maxBlockSize.
+      In which case, encoding and decoding buffers do not need to be synchronized,
+      and encoding ring buffer can have any size, including larger than decoding buffer.
+    Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
+    and indicate where it is saved using LZ4_setStreamDecode()
+*/
+int LZ4_decompress_safe_continue(LZ4_streamDecode_t *LZ4_streamDecode, const char *source, char *dest,
+                                 int compressedSize, int maxDecompressedSize);
+int LZ4_decompress_fast_continue(LZ4_streamDecode_t *LZ4_streamDecode, const char *source, char *dest,
+                                 int originalSize);
+
+/*
+Advanced decoding functions :
+*_usingDict() :
+    These decoding functions work the same as
+    a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue()
+    They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure.
+*/
+int LZ4_decompress_safe_usingDict(const char *source, char *dest, int compressedSize, int maxDecompressedSize,
+                                  const char *dictStart, int dictSize);
+int LZ4_decompress_fast_usingDict(const char *source, char *dest, int originalSize, const char *dictStart,
+                                  int dictSize);
+
+/**************************************
+ *  Obsolete Functions
+ **************************************/
+/* Deprecate Warnings */
+/* Should these warnings messages be a problem,
+   it is generally possible to disable them,
+   with -Wno-deprecated-declarations for gcc
+   or _CRT_SECURE_NO_WARNINGS in Visual for example.
+   You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */
+#ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK
+#define LZ4_DEPRECATE_WARNING_DEFBLOCK
+#define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#if (LZ4_GCC_VERSION >= 405) || defined(__clang__)
+#define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
+#elif (LZ4_GCC_VERSION >= 301)
+#define LZ4_DEPRECATED(message) __attribute__((deprecated))
+#elif defined(_MSC_VER)
+#define LZ4_DEPRECATED(message) __declspec(deprecated(message))
+#else
+#pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
+#define LZ4_DEPRECATED(message)
+#endif
+#endif /* LZ4_DEPRECATE_WARNING_DEFBLOCK */
+
+/* Obsolete compression functions */
+/* These functions are planned to start generate warnings by r131 approximately */
+int LZ4_compress(const char *source, char *dest, int sourceSize);
+int LZ4_compress_limitedOutput(const char *source, char *dest, int sourceSize, int maxOutputSize);
+int LZ4_compress_withState(void *state, const char *source, char *dest, int inputSize);
+int LZ4_compress_limitedOutput_withState(void *state, const char *source, char *dest, int inputSize, int maxOutputSize);
+int LZ4_compress_continue(LZ4_stream_t *LZ4_streamPtr, const char *source, char *dest, int inputSize);
+int LZ4_compress_limitedOutput_continue(LZ4_stream_t *LZ4_streamPtr, const char *source, char *dest, int inputSize,
+                                        int maxOutputSize);
+
+/* Obsolete decompression functions */
+/* These function names are completely deprecated and must no longer be used.
+   They are only provided here for compatibility with older programs.
+    - LZ4_uncompress is the same as LZ4_decompress_fast
+    - LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe
+   These function prototypes are now disabled; uncomment them only if you really need them.
+   It is highly recommended to stop using these prototypes and migrate to maintained ones */
+/* int LZ4_uncompress (const char* source, char* dest, int outputSize); */
+/* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */
+
+/* Obsolete streaming functions; use new streaming interface whenever possible */
+LZ4_DEPRECATED("use LZ4_createStream() instead") void *LZ4_create(char *inputBuffer);
+LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void);
+LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void *state, char *inputBuffer);
+LZ4_DEPRECATED("use LZ4_saveDict() instead") char *LZ4_slideInputBuffer(void *state);
+
+/* Obsolete streaming decoding functions */
+LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead")
+int LZ4_decompress_safe_withPrefix64k(const char *src, char *dst, int compressedSize, int maxDstSize);
+LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead")
+int LZ4_decompress_fast_withPrefix64k(const char *src, char *dst, int originalSize);
+
+#if defined(__cplusplus)
+}
+#endif