diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,6 +1,47 @@
 # Changelog
 
-## 0.3.0
+## 0.3.2 (Oct 2026)
+
+### Bug Fixes
+
+* On POSIX platforms, the `Path` based APIs that open an existing file in
+  `WriteMode`, e.g. `write` and `withFile` in `Streamly.FileSystem.FileIO`,
+  did not truncate it, the old contents beyond the newly written data
+  remained. The file descriptor leaked when the file could not be opened as
+  a Handle, e.g. if it was a directory.
+* Fix `timer`, `timesWith`, `absTimesWith` and `relTimesWith` stopping after a
+  major GC.
+* Fix `enumerateFrom` and `enumerateFromTo` in Stream and Unfold not
+  terminating for bounded integral types when the limit is `maxBound`.
+* Fix `Unfold.enumerateFromTo` for `Identity` calling `enumerateFromThen`.
+* Fix `Stream.splitEndBySeq` and `splitEndBySeq_` dropping the last segment
+  for separators longer than a machine word.
+* `chunksOf` in the array modules no longer emits a trailing empty array
+  when the stream length is a multiple of the chunk size, or when the stream
+  is empty.
+* Fix `classify` and `classifyIO` in Fold and Scanl, and `Fold.classifyScan`,
+  restarting the fold for a key that terminated on its first input.
+* Fix `Scanl.uniqBy` failing before the first input.
+* Fix `Stream.postscanl` emitting an output for a scan that terminates
+  without consuming any input.
+* Fix `Stream.isInfixOf` returning `False` for an empty infix.
+* Fix `RingArray.getIndex`, `cast`, `fold`, `eqArray` and `eqArrayN` for
+  out-of-range indices, misaligned heads, empty rings and array slices.
+* Fix deserialization reading beyond the end of a corrupt or truncated buffer
+  for lists and for derived `Serialize` instances.
+
+### Internal API Bug Fixes
+
+* Fix memory corruption in `MutArray.appendMax`, `MutArray.createMinOf` and
+  `RingArray.Generic.copyToMutArray`, and resource release in
+  `Fold.bracketIO` and `Fold.finallyIO`.
+
+## 0.3.1 (May 2026)
+
+* Fixed `Path.fromString` truncation when unicode chars are present.
+* Fixed `DirIO.followSymlinks` option not working correctly on macOS.
+
+## 0.3.0 (Sep 2025)
 
 See [0.2.2-0.3.0 API Changelog](/core/docs/ApiChangelogs/0.2.2-0.3.0.txt) for a
 full list of deprecations, additions, and changes to the function signatures.
diff --git a/configure b/configure
--- a/configure
+++ b/configure
@@ -1,11 +1,11 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for streamly-core 0.3.0.
+# Generated by GNU Autoconf 2.72 for streamly-core 0.3.2.
 #
 # Report bugs to <streamly@composewell.com>.
 #
 #
-# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
+# Copyright (C) 1992-1996, 1998-2017, 2020-2023 Free Software Foundation,
 # Inc.
 #
 #
@@ -17,7 +17,6 @@
 
 # Be more Bourne compatible
 DUALCASE=1; export DUALCASE # for MKS sh
-as_nop=:
 if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
 then :
   emulate sh
@@ -26,12 +25,13 @@
   # is contrary to our usage.  Disable this feature.
   alias -g '${1+"$@"}'='"$@"'
   setopt NO_GLOB_SUBST
-else $as_nop
-  case `(set -o) 2>/dev/null` in #(
+else case e in #(
+  e) case `(set -o) 2>/dev/null` in #(
   *posix*) :
     set -o posix ;; #(
   *) :
      ;;
+esac ;;
 esac
 fi
 
@@ -103,7 +103,7 @@
 
      ;;
 esac
-# We did not find ourselves, most probably we were run as `sh COMMAND'
+# We did not find ourselves, most probably we were run as 'sh COMMAND'
 # in which case we are not to be found in the path.
 if test "x$as_myself" = x; then
   as_myself=$0
@@ -133,15 +133,14 @@
 esac
 exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
 # Admittedly, this is quite paranoid, since all the known shells bail
-# out after a failed `exec'.
+# out after a failed 'exec'.
 printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
 exit 255
   fi
   # We don't want this to propagate to other subprocesses.
           { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
-  as_bourne_compatible="as_nop=:
-if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+  as_bourne_compatible="if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
 then :
   emulate sh
   NULLCMD=:
@@ -149,12 +148,13 @@
   # is contrary to our usage.  Disable this feature.
   alias -g '\${1+\"\$@\"}'='\"\$@\"'
   setopt NO_GLOB_SUBST
-else \$as_nop
-  case \`(set -o) 2>/dev/null\` in #(
+else case e in #(
+  e) case \`(set -o) 2>/dev/null\` in #(
   *posix*) :
     set -o posix ;; #(
   *) :
      ;;
+esac ;;
 esac
 fi
 "
@@ -172,8 +172,9 @@
 if ( set x; as_fn_ret_success y && test x = \"\$1\" )
 then :
 
-else \$as_nop
-  exitcode=1; echo positional parameters were not saved.
+else case e in #(
+  e) exitcode=1; echo positional parameters were not saved. ;;
+esac
 fi
 test x\$exitcode = x0 || exit 1
 blah=\$(echo \$(echo blah))
@@ -186,14 +187,15 @@
   if (eval "$as_required") 2>/dev/null
 then :
   as_have_required=yes
-else $as_nop
-  as_have_required=no
+else case e in #(
+  e) as_have_required=no ;;
+esac
 fi
   if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null
 then :
 
-else $as_nop
-  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+else case e in #(
+  e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
 as_found=false
 for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
 do
@@ -226,12 +228,13 @@
 if $as_found
 then :
 
-else $as_nop
-  if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+else case e in #(
+  e) if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
 	      as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null
 then :
   CONFIG_SHELL=$SHELL as_have_required=yes
-fi
+fi ;;
+esac
 fi
 
 
@@ -253,7 +256,7 @@
 esac
 exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
 # Admittedly, this is quite paranoid, since all the known shells bail
-# out after a failed `exec'.
+# out after a failed 'exec'.
 printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
 exit 255
 fi
@@ -273,7 +276,8 @@
 $0: under such a shell if you do have one."
   fi
   exit 1
-fi
+fi ;;
+esac
 fi
 fi
 SHELL=${CONFIG_SHELL-/bin/sh}
@@ -312,14 +316,6 @@
   as_fn_set_status $1
   exit $1
 } # as_fn_exit
-# as_fn_nop
-# ---------
-# Do nothing but, unlike ":", preserve the value of $?.
-as_fn_nop ()
-{
-  return $?
-}
-as_nop=as_fn_nop
 
 # as_fn_mkdir_p
 # -------------
@@ -388,11 +384,12 @@
   {
     eval $1+=\$2
   }'
-else $as_nop
-  as_fn_append ()
+else case e in #(
+  e) as_fn_append ()
   {
     eval $1=\$$1\$2
-  }
+  } ;;
+esac
 fi # as_fn_append
 
 # as_fn_arith ARG...
@@ -406,21 +403,14 @@
   {
     as_val=$(( $* ))
   }'
-else $as_nop
-  as_fn_arith ()
+else case e in #(
+  e) as_fn_arith ()
   {
     as_val=`expr "$@" || test $? -eq 1`
-  }
+  } ;;
+esac
 fi # as_fn_arith
 
-# as_fn_nop
-# ---------
-# Do nothing but, unlike ":", preserve the value of $?.
-as_fn_nop ()
-{
-  return $?
-}
-as_nop=as_fn_nop
 
 # as_fn_error STATUS ERROR [LINENO LOG_FD]
 # ----------------------------------------
@@ -494,6 +484,8 @@
     /[$]LINENO/=
   ' <$as_myself |
     sed '
+      t clear
+      :clear
       s/[$]LINENO.*/&-/
       t lineno
       b
@@ -542,7 +534,6 @@
 as_echo='printf %s\n'
 as_echo_n='printf %s'
 
-
 rm -f conf$$ conf$$.exe conf$$.file
 if test -d conf$$.dir; then
   rm -f conf$$.dir/conf$$.file
@@ -554,9 +545,9 @@
   if ln -s conf$$.file conf$$ 2>/dev/null; then
     as_ln_s='ln -s'
     # ... but there are two gotchas:
-    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
-    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
-    # In both cases, we have to default to `cp -pR'.
+    # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable.
+    # In both cases, we have to default to 'cp -pR'.
     ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
       as_ln_s='cp -pR'
   elif ln conf$$.file conf$$ 2>/dev/null; then
@@ -581,10 +572,12 @@
 as_executable_p=as_fn_executable_p
 
 # Sed expression to map a string onto a valid CPP name.
-as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g"
+as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated
 
 # Sed expression to map a string onto a valid variable name.
-as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g"
+as_tr_sh="eval sed '$as_sed_sh'" # deprecated
 
 
 test -n "$DJDIR" || exec 7<&0 </dev/null
@@ -610,8 +603,8 @@
 # Identity of this package.
 PACKAGE_NAME='streamly-core'
 PACKAGE_TARNAME='streamly-core'
-PACKAGE_VERSION='0.3.0'
-PACKAGE_STRING='streamly-core 0.3.0'
+PACKAGE_VERSION='0.3.2'
+PACKAGE_STRING='streamly-core 0.3.2'
 PACKAGE_BUGREPORT='streamly@composewell.com'
 PACKAGE_URL='https://streamly.composewell.com'
 
@@ -816,7 +809,7 @@
     ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
     # Reject names that are not valid shell variable names.
     expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
-      as_fn_error $? "invalid feature name: \`$ac_useropt'"
+      as_fn_error $? "invalid feature name: '$ac_useropt'"
     ac_useropt_orig=$ac_useropt
     ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
     case $ac_user_opts in
@@ -842,7 +835,7 @@
     ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
     # Reject names that are not valid shell variable names.
     expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
-      as_fn_error $? "invalid feature name: \`$ac_useropt'"
+      as_fn_error $? "invalid feature name: '$ac_useropt'"
     ac_useropt_orig=$ac_useropt
     ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
     case $ac_user_opts in
@@ -1055,7 +1048,7 @@
     ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
     # Reject names that are not valid shell variable names.
     expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
-      as_fn_error $? "invalid package name: \`$ac_useropt'"
+      as_fn_error $? "invalid package name: '$ac_useropt'"
     ac_useropt_orig=$ac_useropt
     ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
     case $ac_user_opts in
@@ -1071,7 +1064,7 @@
     ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
     # Reject names that are not valid shell variable names.
     expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
-      as_fn_error $? "invalid package name: \`$ac_useropt'"
+      as_fn_error $? "invalid package name: '$ac_useropt'"
     ac_useropt_orig=$ac_useropt
     ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
     case $ac_user_opts in
@@ -1101,8 +1094,8 @@
   | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
     x_libraries=$ac_optarg ;;
 
-  -*) as_fn_error $? "unrecognized option: \`$ac_option'
-Try \`$0 --help' for more information"
+  -*) as_fn_error $? "unrecognized option: '$ac_option'
+Try '$0 --help' for more information"
     ;;
 
   *=*)
@@ -1110,7 +1103,7 @@
     # Reject names that are not valid shell variable names.
     case $ac_envvar in #(
       '' | [0-9]* | *[!_$as_cr_alnum]* )
-      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+      as_fn_error $? "invalid variable name: '$ac_envvar'" ;;
     esac
     eval $ac_envvar=\$ac_optarg
     export $ac_envvar ;;
@@ -1160,7 +1153,7 @@
   as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
 done
 
-# There might be people who depend on the old broken behavior: `$host'
+# There might be people who depend on the old broken behavior: '$host'
 # used to hold the argument of --host etc.
 # FIXME: To remove some day.
 build=$build_alias
@@ -1228,7 +1221,7 @@
   test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
   as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
 fi
-ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_msg="sources are in $srcdir, but 'cd $srcdir' does not work"
 ac_abs_confdir=`(
 	cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
 	pwd)`
@@ -1256,7 +1249,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures streamly-core 0.3.0 to adapt to many kinds of systems.
+'configure' configures streamly-core 0.3.2 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1270,11 +1263,11 @@
       --help=short        display options specific to this package
       --help=recursive    display the short help of all the included packages
   -V, --version           display version information and exit
-  -q, --quiet, --silent   do not print \`checking ...' messages
+  -q, --quiet, --silent   do not print 'checking ...' messages
       --cache-file=FILE   cache test results in FILE [disabled]
-  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -C, --config-cache      alias for '--cache-file=config.cache'
   -n, --no-create         do not create output files
-      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+      --srcdir=DIR        find the sources in DIR [configure dir or '..']
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
@@ -1282,10 +1275,10 @@
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
                           [PREFIX]
 
-By default, \`make install' will install all the files in
-\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
-an installation prefix other than \`$ac_default_prefix' using \`--prefix',
-for instance \`--prefix=\$HOME'.
+By default, 'make install' will install all the files in
+'$ac_default_prefix/bin', '$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than '$ac_default_prefix' using '--prefix',
+for instance '--prefix=\$HOME'.
 
 For better control, use the options below.
 
@@ -1318,7 +1311,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of streamly-core 0.3.0:";;
+     short | recursive ) echo "Configuration of streamly-core 0.3.2:";;
    esac
   cat <<\_ACEOF
 
@@ -1336,7 +1329,7 @@
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
               you have headers in a nonstandard directory <include dir>
 
-Use these variables to override the choices made by `configure' or to help
+Use these variables to override the choices made by 'configure' or to help
 it to find libraries and programs with nonstandard names/locations.
 
 Report bugs to <streamly@composewell.com>.
@@ -1404,10 +1397,10 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-streamly-core configure 0.3.0
-generated by GNU Autoconf 2.71
+streamly-core configure 0.3.2
+generated by GNU Autoconf 2.72
 
-Copyright (C) 2021 Free Software Foundation, Inc.
+Copyright (C) 2023 Free Software Foundation, Inc.
 This configure script is free software; the Free Software Foundation
 gives unlimited permission to copy, distribute and modify it.
 _ACEOF
@@ -1446,11 +1439,12 @@
        } && test -s conftest.$ac_objext
 then :
   ac_retval=0
-else $as_nop
-  printf "%s\n" "$as_me: failed program was:" >&5
+else case e in #(
+  e) printf "%s\n" "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
-	ac_retval=1
+	ac_retval=1 ;;
+esac
 fi
   eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
   as_fn_set_status $ac_retval
@@ -1469,8 +1463,8 @@
 if eval test \${$3+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+  e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 $4
 #include <$2>
@@ -1478,10 +1472,12 @@
 if ac_fn_c_try_compile "$LINENO"
 then :
   eval "$3=yes"
-else $as_nop
-  eval "$3=no"
+else case e in #(
+  e) eval "$3=no" ;;
+esac
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
 fi
 eval ac_res=\$$3
 	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
@@ -1521,11 +1517,12 @@
        }
 then :
   ac_retval=0
-else $as_nop
-  printf "%s\n" "$as_me: failed program was:" >&5
+else case e in #(
+  e) printf "%s\n" "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
-	ac_retval=1
+	ac_retval=1 ;;
+esac
 fi
   # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
   # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
@@ -1548,15 +1545,15 @@
 if eval test \${$3+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+  e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 /* Define $2 to an innocuous variant, in case <limits.h> declares $2.
    For example, HP-UX 11i <limits.h> declares gettimeofday.  */
 #define $2 innocuous_$2
 
 /* System header to define __stub macros and hopefully few prototypes,
-   which can conflict with char $2 (); below.  */
+   which can conflict with char $2 (void); below.  */
 
 #include <limits.h>
 #undef $2
@@ -1567,7 +1564,7 @@
 #ifdef __cplusplus
 extern "C"
 #endif
-char $2 ();
+char $2 (void);
 /* The GNU C library defines this for functions which it implements
     to always fail with ENOSYS.  Some functions are actually named
     something starting with __ and the normal name is an alias.  */
@@ -1586,11 +1583,13 @@
 if ac_fn_c_try_link "$LINENO"
 then :
   eval "$3=yes"
-else $as_nop
-  eval "$3=no"
+else case e in #(
+  e) eval "$3=no" ;;
+esac
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
+    conftest$ac_exeext conftest.$ac_ext ;;
+esac
 fi
 eval ac_res=\$$3
 	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
@@ -1622,8 +1621,8 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by streamly-core $as_me 0.3.0, which was
-generated by GNU Autoconf 2.71.  Invocation command line was
+It was created by streamly-core $as_me 0.3.2, which was
+generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
 
@@ -1869,10 +1868,10 @@
 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;}
     sed 's/^/| /' "$ac_site_file" >&5
     . "$ac_site_file" \
-      || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+      || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error $? "failed to load site script $ac_site_file
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; }
   fi
 done
 
@@ -1908,9 +1907,7 @@
 /* Most of the following tests are stolen from RCS 5.7 src/conf.sh.  */
 struct buf { int x; };
 struct buf * (*rcsopen) (struct buf *, struct stat *, int);
-static char *e (p, i)
-     char **p;
-     int i;
+static char *e (char **p, int i)
 {
   return p[i];
 }
@@ -1924,6 +1921,21 @@
   return s;
 }
 
+/* C89 style stringification. */
+#define noexpand_stringify(a) #a
+const char *stringified = noexpand_stringify(arbitrary+token=sequence);
+
+/* C89 style token pasting.  Exercises some of the corner cases that
+   e.g. old MSVC gets wrong, but not very hard. */
+#define noexpand_concat(a,b) a##b
+#define expand_concat(a,b) noexpand_concat(a,b)
+extern int vA;
+extern int vbee;
+#define aye A
+#define bee B
+int *pvA = &expand_concat(v,aye);
+int *pvbee = &noexpand_concat(v,bee);
+
 /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
    function prototypes and stuff, but not \xHH hex character constants.
    These do not provoke an error unfortunately, instead are silently treated
@@ -1951,16 +1963,19 @@
 
 # Test code for whether the C compiler supports C99 (global declarations)
 ac_c_conftest_c99_globals='
-// Does the compiler advertise C99 conformance?
+/* Does the compiler advertise C99 conformance? */
 #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
 # error "Compiler does not advertise C99 conformance"
 #endif
 
+// See if C++-style comments work.
+
 #include <stdbool.h>
 extern int puts (const char *);
 extern int printf (const char *, ...);
 extern int dprintf (int, const char *, ...);
 extern void *malloc (size_t);
+extern void free (void *);
 
 // Check varargs macros.  These examples are taken from C99 6.10.3.5.
 // dprintf is used instead of fprintf to avoid needing to declare
@@ -2010,7 +2025,6 @@
 static inline int
 test_restrict (ccp restrict text)
 {
-  // See if C++-style comments work.
   // Iterate through items via the restricted pointer.
   // Also check for declarations in for loops.
   for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i)
@@ -2076,6 +2090,8 @@
   ia->datasize = 10;
   for (int i = 0; i < ia->datasize; ++i)
     ia->data[i] = i * 1.234;
+  // Work around memory leak warnings.
+  free (ia);
 
   // Check named initializers.
   struct named_init ni = {
@@ -2097,7 +2113,7 @@
 
 # Test code for whether the C compiler supports C11 (global declarations)
 ac_c_conftest_c11_globals='
-// Does the compiler advertise C11 conformance?
+/* Does the compiler advertise C11 conformance? */
 #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L
 # error "Compiler does not advertise C11 conformance"
 #endif
@@ -2220,12 +2236,12 @@
   eval ac_new_val=\$ac_env_${ac_var}_value
   case $ac_old_set,$ac_new_set in
     set,)
-      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
-printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&5
+printf "%s\n" "$as_me: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&2;}
       ac_cache_corrupted=: ;;
     ,set)
-      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
-printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was not set in the previous run" >&5
+printf "%s\n" "$as_me: error: '$ac_var' was not set in the previous run" >&2;}
       ac_cache_corrupted=: ;;
     ,);;
     *)
@@ -2234,18 +2250,18 @@
 	ac_old_val_w=`echo x $ac_old_val`
 	ac_new_val_w=`echo x $ac_new_val`
 	if test "$ac_old_val_w" != "$ac_new_val_w"; then
-	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
-printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' has changed since the previous run:" >&5
+printf "%s\n" "$as_me: error: '$ac_var' has changed since the previous run:" >&2;}
 	  ac_cache_corrupted=:
 	else
-	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
-printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&5
+printf "%s\n" "$as_me: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&2;}
 	  eval $ac_var=\$ac_old_val
 	fi
-	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
-printf "%s\n" "$as_me:   former value:  \`$ac_old_val'" >&2;}
-	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
-printf "%s\n" "$as_me:   current value: \`$ac_new_val'" >&2;}
+	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   former value:  '$ac_old_val'" >&5
+printf "%s\n" "$as_me:   former value:  '$ac_old_val'" >&2;}
+	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   current value: '$ac_new_val'" >&5
+printf "%s\n" "$as_me:   current value: '$ac_new_val'" >&2;}
       fi;;
   esac
   # Pass precious variables to config.status.
@@ -2261,11 +2277,11 @@
   fi
 done
 if $ac_cache_corrupted; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;}
-  as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file'
+  as_fn_error $? "run '${MAKE-make} distclean' and/or 'rm $cache_file'
 	    and start over" "$LINENO" 5
 fi
 ## -------------------- ##
@@ -2312,8 +2328,8 @@
 if test ${ac_cv_prog_CC+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  if test -n "$CC"; then
+else case e in #(
+  e) if test -n "$CC"; then
   ac_cv_prog_CC="$CC" # Let the user override the test.
 else
 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -2335,7 +2351,8 @@
   done
 IFS=$as_save_IFS
 
-fi
+fi ;;
+esac
 fi
 CC=$ac_cv_prog_CC
 if test -n "$CC"; then
@@ -2357,8 +2374,8 @@
 if test ${ac_cv_prog_ac_ct_CC+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  if test -n "$ac_ct_CC"; then
+else case e in #(
+  e) if test -n "$ac_ct_CC"; then
   ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
 else
 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -2380,7 +2397,8 @@
   done
 IFS=$as_save_IFS
 
-fi
+fi ;;
+esac
 fi
 ac_ct_CC=$ac_cv_prog_ac_ct_CC
 if test -n "$ac_ct_CC"; then
@@ -2415,8 +2433,8 @@
 if test ${ac_cv_prog_CC+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  if test -n "$CC"; then
+else case e in #(
+  e) if test -n "$CC"; then
   ac_cv_prog_CC="$CC" # Let the user override the test.
 else
 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -2438,7 +2456,8 @@
   done
 IFS=$as_save_IFS
 
-fi
+fi ;;
+esac
 fi
 CC=$ac_cv_prog_CC
 if test -n "$CC"; then
@@ -2460,8 +2479,8 @@
 if test ${ac_cv_prog_CC+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  if test -n "$CC"; then
+else case e in #(
+  e) if test -n "$CC"; then
   ac_cv_prog_CC="$CC" # Let the user override the test.
 else
   ac_prog_rejected=no
@@ -2500,7 +2519,8 @@
     ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@"
   fi
 fi
-fi
+fi ;;
+esac
 fi
 CC=$ac_cv_prog_CC
 if test -n "$CC"; then
@@ -2524,8 +2544,8 @@
 if test ${ac_cv_prog_CC+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  if test -n "$CC"; then
+else case e in #(
+  e) if test -n "$CC"; then
   ac_cv_prog_CC="$CC" # Let the user override the test.
 else
 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -2547,7 +2567,8 @@
   done
 IFS=$as_save_IFS
 
-fi
+fi ;;
+esac
 fi
 CC=$ac_cv_prog_CC
 if test -n "$CC"; then
@@ -2573,8 +2594,8 @@
 if test ${ac_cv_prog_ac_ct_CC+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  if test -n "$ac_ct_CC"; then
+else case e in #(
+  e) if test -n "$ac_ct_CC"; then
   ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
 else
 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -2596,7 +2617,8 @@
   done
 IFS=$as_save_IFS
 
-fi
+fi ;;
+esac
 fi
 ac_ct_CC=$ac_cv_prog_ac_ct_CC
 if test -n "$ac_ct_CC"; then
@@ -2634,8 +2656,8 @@
 if test ${ac_cv_prog_CC+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  if test -n "$CC"; then
+else case e in #(
+  e) if test -n "$CC"; then
   ac_cv_prog_CC="$CC" # Let the user override the test.
 else
 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -2657,7 +2679,8 @@
   done
 IFS=$as_save_IFS
 
-fi
+fi ;;
+esac
 fi
 CC=$ac_cv_prog_CC
 if test -n "$CC"; then
@@ -2679,8 +2702,8 @@
 if test ${ac_cv_prog_ac_ct_CC+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  if test -n "$ac_ct_CC"; then
+else case e in #(
+  e) if test -n "$ac_ct_CC"; then
   ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
 else
 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -2702,7 +2725,8 @@
   done
 IFS=$as_save_IFS
 
-fi
+fi ;;
+esac
 fi
 ac_ct_CC=$ac_cv_prog_ac_ct_CC
 if test -n "$ac_ct_CC"; then
@@ -2731,10 +2755,10 @@
 fi
 
 
-test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error $? "no acceptable C compiler found in \$PATH
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; }
 
 # Provide some information about the compiler.
 printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
@@ -2806,8 +2830,8 @@
   printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }
 then :
-  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
-# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+  # Autoconf-2.13 could set the ac_cv_exeext variable to 'no'.
+# So ignore a value of 'no', otherwise this would lead to 'EXEEXT = no'
 # in a Makefile.  We should not override ac_cv_exeext if it was cached,
 # so that the user can short-circuit this test for compilers unknown to
 # Autoconf.
@@ -2827,7 +2851,7 @@
 	   ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
 	fi
 	# We set ac_cv_exeext here because the later test for it is not
-	# safe: cross compilers may not add the suffix if given an `-o'
+	# safe: cross compilers may not add the suffix if given an '-o'
 	# argument, so we may need to know it at that point already.
 	# Even if this section looks crufty: it has the advantage of
 	# actually working.
@@ -2838,8 +2862,9 @@
 done
 test "$ac_cv_exeext" = no && ac_cv_exeext=
 
-else $as_nop
-  ac_file=''
+else case e in #(
+  e) ac_file='' ;;
+esac
 fi
 if test -z "$ac_file"
 then :
@@ -2848,13 +2873,14 @@
 printf "%s\n" "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
-{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error 77 "C compiler cannot create executables
-See \`config.log' for more details" "$LINENO" 5; }
-else $as_nop
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-printf "%s\n" "yes" >&6; }
+See 'config.log' for more details" "$LINENO" 5; }
+else case e in #(
+  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; } ;;
+esac
 fi
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
 printf %s "checking for C compiler default output file name... " >&6; }
@@ -2878,10 +2904,10 @@
   printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }
 then :
-  # If both `conftest.exe' and `conftest' are `present' (well, observable)
-# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
-# work properly (i.e., refer to `conftest.exe'), while it won't with
-# `rm'.
+  # If both 'conftest.exe' and 'conftest' are 'present' (well, observable)
+# catch 'conftest.exe'.  For instance with Cygwin, 'ls conftest' will
+# work properly (i.e., refer to 'conftest.exe'), while it won't with
+# 'rm'.
 for ac_file in conftest.exe conftest conftest.*; do
   test -f "$ac_file" || continue
   case $ac_file in
@@ -2891,11 +2917,12 @@
     * ) break;;
   esac
 done
-else $as_nop
-  { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+else case e in #(
+  e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error $? "cannot compute suffix of executables: cannot compile and link
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; } ;;
+esac
 fi
 rm -f conftest conftest$ac_cv_exeext
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
@@ -2911,6 +2938,8 @@
 main (void)
 {
 FILE *f = fopen ("conftest.out", "w");
+ if (!f)
+  return 1;
  return ferror (f) || fclose (f) != 0;
 
   ;
@@ -2950,26 +2979,27 @@
     if test "$cross_compiling" = maybe; then
 	cross_compiling=yes
     else
-	{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+	{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error 77 "cannot run C compiled programs.
-If you meant to cross compile, use \`--host'.
-See \`config.log' for more details" "$LINENO" 5; }
+If you meant to cross compile, use '--host'.
+See 'config.log' for more details" "$LINENO" 5; }
     fi
   fi
 fi
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
 printf "%s\n" "$cross_compiling" >&6; }
 
-rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+rm -f conftest.$ac_ext conftest$ac_cv_exeext \
+  conftest.o conftest.obj conftest.out
 ac_clean_files=$ac_clean_files_save
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
 printf %s "checking for suffix of object files... " >&6; }
 if test ${ac_cv_objext+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+  e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
 int
@@ -3001,16 +3031,18 @@
        break;;
   esac
 done
-else $as_nop
-  printf "%s\n" "$as_me: failed program was:" >&5
+else case e in #(
+  e) printf "%s\n" "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
-{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error $? "cannot compute suffix of object files: cannot compile
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; } ;;
+esac
 fi
-rm -f conftest.$ac_cv_objext conftest.$ac_ext
+rm -f conftest.$ac_cv_objext conftest.$ac_ext ;;
+esac
 fi
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
 printf "%s\n" "$ac_cv_objext" >&6; }
@@ -3021,8 +3053,8 @@
 if test ${ac_cv_c_compiler_gnu+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+  e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
 int
@@ -3039,12 +3071,14 @@
 if ac_fn_c_try_compile "$LINENO"
 then :
   ac_compiler_gnu=yes
-else $as_nop
-  ac_compiler_gnu=no
+else case e in #(
+  e) ac_compiler_gnu=no ;;
+esac
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
 ac_cv_c_compiler_gnu=$ac_compiler_gnu
-
+ ;;
+esac
 fi
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; }
@@ -3062,8 +3096,8 @@
 if test ${ac_cv_prog_cc_g+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  ac_save_c_werror_flag=$ac_c_werror_flag
+else case e in #(
+  e) ac_save_c_werror_flag=$ac_c_werror_flag
    ac_c_werror_flag=yes
    ac_cv_prog_cc_g=no
    CFLAGS="-g"
@@ -3081,8 +3115,8 @@
 if ac_fn_c_try_compile "$LINENO"
 then :
   ac_cv_prog_cc_g=yes
-else $as_nop
-  CFLAGS=""
+else case e in #(
+  e) CFLAGS=""
       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -3097,8 +3131,8 @@
 if ac_fn_c_try_compile "$LINENO"
 then :
 
-else $as_nop
-  ac_c_werror_flag=$ac_save_c_werror_flag
+else case e in #(
+  e) ac_c_werror_flag=$ac_save_c_werror_flag
 	 CFLAGS="-g"
 	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
@@ -3115,12 +3149,15 @@
 then :
   ac_cv_prog_cc_g=yes
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
-   ac_c_werror_flag=$ac_save_c_werror_flag
+   ac_c_werror_flag=$ac_save_c_werror_flag ;;
+esac
 fi
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
 printf "%s\n" "$ac_cv_prog_cc_g" >&6; }
@@ -3147,8 +3184,8 @@
 if test ${ac_cv_prog_cc_c11+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  ac_cv_prog_cc_c11=no
+else case e in #(
+  e) ac_cv_prog_cc_c11=no
 ac_save_CC=$CC
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
@@ -3165,25 +3202,28 @@
   test "x$ac_cv_prog_cc_c11" != "xno" && break
 done
 rm -f conftest.$ac_ext
-CC=$ac_save_CC
+CC=$ac_save_CC ;;
+esac
 fi
 
 if test "x$ac_cv_prog_cc_c11" = xno
 then :
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
 printf "%s\n" "unsupported" >&6; }
-else $as_nop
-  if test "x$ac_cv_prog_cc_c11" = x
+else case e in #(
+  e) if test "x$ac_cv_prog_cc_c11" = x
 then :
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
 printf "%s\n" "none needed" >&6; }
-else $as_nop
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
+else case e in #(
+  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
-     CC="$CC $ac_cv_prog_cc_c11"
+     CC="$CC $ac_cv_prog_cc_c11" ;;
+esac
 fi
   ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
-  ac_prog_cc_stdc=c11
+  ac_prog_cc_stdc=c11 ;;
+esac
 fi
 fi
 if test x$ac_prog_cc_stdc = xno
@@ -3193,8 +3233,8 @@
 if test ${ac_cv_prog_cc_c99+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  ac_cv_prog_cc_c99=no
+else case e in #(
+  e) ac_cv_prog_cc_c99=no
 ac_save_CC=$CC
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
@@ -3211,25 +3251,28 @@
   test "x$ac_cv_prog_cc_c99" != "xno" && break
 done
 rm -f conftest.$ac_ext
-CC=$ac_save_CC
+CC=$ac_save_CC ;;
+esac
 fi
 
 if test "x$ac_cv_prog_cc_c99" = xno
 then :
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
 printf "%s\n" "unsupported" >&6; }
-else $as_nop
-  if test "x$ac_cv_prog_cc_c99" = x
+else case e in #(
+  e) if test "x$ac_cv_prog_cc_c99" = x
 then :
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
 printf "%s\n" "none needed" >&6; }
-else $as_nop
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+else case e in #(
+  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
-     CC="$CC $ac_cv_prog_cc_c99"
+     CC="$CC $ac_cv_prog_cc_c99" ;;
+esac
 fi
   ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
-  ac_prog_cc_stdc=c99
+  ac_prog_cc_stdc=c99 ;;
+esac
 fi
 fi
 if test x$ac_prog_cc_stdc = xno
@@ -3239,8 +3282,8 @@
 if test ${ac_cv_prog_cc_c89+y}
 then :
   printf %s "(cached) " >&6
-else $as_nop
-  ac_cv_prog_cc_c89=no
+else case e in #(
+  e) ac_cv_prog_cc_c89=no
 ac_save_CC=$CC
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
@@ -3257,25 +3300,28 @@
   test "x$ac_cv_prog_cc_c89" != "xno" && break
 done
 rm -f conftest.$ac_ext
-CC=$ac_save_CC
+CC=$ac_save_CC ;;
+esac
 fi
 
 if test "x$ac_cv_prog_cc_c89" = xno
 then :
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
 printf "%s\n" "unsupported" >&6; }
-else $as_nop
-  if test "x$ac_cv_prog_cc_c89" = x
+else case e in #(
+  e) if test "x$ac_cv_prog_cc_c89" = x
 then :
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
 printf "%s\n" "none needed" >&6; }
-else $as_nop
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+else case e in #(
+  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
-     CC="$CC $ac_cv_prog_cc_c89"
+     CC="$CC $ac_cv_prog_cc_c89" ;;
+esac
 fi
   ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
-  ac_prog_cc_stdc=c89
+  ac_prog_cc_stdc=c89 ;;
+esac
 fi
 fi
 
@@ -3343,8 +3389,8 @@
 # config.status only pays attention to the cache file if you give it
 # the --recheck option to rerun configure.
 #
-# `ac_cv_env_foo' variables (set or unset) will be overridden when
-# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# 'ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* 'ac_cv_foo' will be assigned the
 # following values.
 
 _ACEOF
@@ -3374,14 +3420,14 @@
   (set) 2>&1 |
     case $as_nl`(ac_space=' '; set) 2>&1` in #(
     *${as_nl}ac_space=\ *)
-      # `set' does not quote correctly, so add quotes: double-quote
+      # 'set' does not quote correctly, so add quotes: double-quote
       # substitution turns \\\\ into \\, and sed turns \\ into \.
       sed -n \
 	"s/'/'\\\\''/g;
 	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
       ;; #(
     *)
-      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      # 'set' quotes correctly as required by POSIX, so do not add quotes.
       sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
       ;;
     esac |
@@ -3471,7 +3517,6 @@
 
 # Be more Bourne compatible
 DUALCASE=1; export DUALCASE # for MKS sh
-as_nop=:
 if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
 then :
   emulate sh
@@ -3480,12 +3525,13 @@
   # is contrary to our usage.  Disable this feature.
   alias -g '${1+"$@"}'='"$@"'
   setopt NO_GLOB_SUBST
-else $as_nop
-  case `(set -o) 2>/dev/null` in #(
+else case e in #(
+  e) case `(set -o) 2>/dev/null` in #(
   *posix*) :
     set -o posix ;; #(
   *) :
      ;;
+esac ;;
 esac
 fi
 
@@ -3557,7 +3603,7 @@
 
      ;;
 esac
-# We did not find ourselves, most probably we were run as `sh COMMAND'
+# We did not find ourselves, most probably we were run as 'sh COMMAND'
 # in which case we are not to be found in the path.
 if test "x$as_myself" = x; then
   as_myself=$0
@@ -3586,7 +3632,6 @@
 } # as_fn_error
 
 
-
 # as_fn_set_status STATUS
 # -----------------------
 # Set $? to STATUS, without forking.
@@ -3626,11 +3671,12 @@
   {
     eval $1+=\$2
   }'
-else $as_nop
-  as_fn_append ()
+else case e in #(
+  e) as_fn_append ()
   {
     eval $1=\$$1\$2
-  }
+  } ;;
+esac
 fi # as_fn_append
 
 # as_fn_arith ARG...
@@ -3644,11 +3690,12 @@
   {
     as_val=$(( $* ))
   }'
-else $as_nop
-  as_fn_arith ()
+else case e in #(
+  e) as_fn_arith ()
   {
     as_val=`expr "$@" || test $? -eq 1`
-  }
+  } ;;
+esac
 fi # as_fn_arith
 
 
@@ -3731,9 +3778,9 @@
   if ln -s conf$$.file conf$$ 2>/dev/null; then
     as_ln_s='ln -s'
     # ... but there are two gotchas:
-    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
-    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
-    # In both cases, we have to default to `cp -pR'.
+    # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable.
+    # In both cases, we have to default to 'cp -pR'.
     ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
       as_ln_s='cp -pR'
   elif ln conf$$.file conf$$ 2>/dev/null; then
@@ -3814,10 +3861,12 @@
 as_executable_p=as_fn_executable_p
 
 # Sed expression to map a string onto a valid CPP name.
-as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g"
+as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated
 
 # Sed expression to map a string onto a valid variable name.
-as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g"
+as_tr_sh="eval sed '$as_sed_sh'" # deprecated
 
 
 exec 6>&1
@@ -3832,8 +3881,8 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by streamly-core $as_me 0.3.0, which was
-generated by GNU Autoconf 2.71.  Invocation command line was
+This file was extended by streamly-core $as_me 0.3.2, which was
+generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
   CONFIG_HEADERS  = $CONFIG_HEADERS
@@ -3860,7 +3909,7 @@
 
 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 ac_cs_usage="\
-\`$as_me' instantiates files and other configuration actions
+'$as_me' instantiates files and other configuration actions
 from templates according to the current configuration.  Unless the files
 and actions are specified as TAGs, all are instantiated by default.
 
@@ -3888,11 +3937,11 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-streamly-core config.status 0.3.0
-configured by $0, generated by GNU Autoconf 2.71,
+streamly-core config.status 0.3.2
+configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
-Copyright (C) 2021 Free Software Foundation, Inc.
+Copyright (C) 2023 Free Software Foundation, Inc.
 This config.status script is free software; the Free Software Foundation
 gives unlimited permission to copy, distribute and modify it."
 
@@ -3943,8 +3992,8 @@
     ac_need_defaults=false;;
   --he | --h)
     # Conflict between --help and --header
-    as_fn_error $? "ambiguous option: \`$1'
-Try \`$0 --help' for more information.";;
+    as_fn_error $? "ambiguous option: '$1'
+Try '$0 --help' for more information.";;
   --help | --hel | -h )
     printf "%s\n" "$ac_cs_usage"; exit ;;
   -q | -quiet | --quiet | --quie | --qui | --qu | --q \
@@ -3952,8 +4001,8 @@
     ac_cs_silent=: ;;
 
   # This is an error.
-  -*) as_fn_error $? "unrecognized option: \`$1'
-Try \`$0 --help' for more information." ;;
+  -*) as_fn_error $? "unrecognized option: '$1'
+Try '$0 --help' for more information." ;;
 
   *) as_fn_append ac_config_targets " $1"
      ac_need_defaults=false ;;
@@ -4003,7 +4052,7 @@
   case $ac_config_target in
     "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;;
 
-  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  *) as_fn_error $? "invalid argument: '$ac_config_target'" "$LINENO" 5;;
   esac
 done
 
@@ -4021,7 +4070,7 @@
 # creating and moving files from /tmp can sometimes cause problems.
 # Hook for its removal unless debugging.
 # Note that there is a small window in which the directory will not be cleaned:
-# after its creation but before its name has been assigned to `$tmp'.
+# after its creation but before its name has been assigned to '$tmp'.
 $debug ||
 {
   tmp= ac_tmp=
@@ -4045,13 +4094,13 @@
 
 # Set up the scripts for CONFIG_HEADERS section.
 # No need to generate them if there are no CONFIG_HEADERS.
-# This happens for instance with `./config.status Makefile'.
+# This happens for instance with './config.status Makefile'.
 if test -n "$CONFIG_HEADERS"; then
 cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
 BEGIN {
 _ACEOF
 
-# Transform confdefs.h into an awk script `defines.awk', embedded as
+# Transform confdefs.h into an awk script 'defines.awk', embedded as
 # here-document in config.status, that substitutes the proper values into
 # config.h.in to produce config.h.
 
@@ -4161,7 +4210,7 @@
   esac
   case $ac_mode$ac_tag in
   :[FHL]*:*);;
-  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :L* | :C*:*) as_fn_error $? "invalid tag '$ac_tag'" "$LINENO" 5;;
   :[FH]-) ac_tag=-:-;;
   :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
   esac
@@ -4183,19 +4232,19 @@
       -) ac_f="$ac_tmp/stdin";;
       *) # Look for the file first in the build tree, then in the source tree
 	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
-	 # because $ac_f cannot contain `:'.
+	 # because $ac_f cannot contain ':'.
 	 test -f "$ac_f" ||
 	   case $ac_f in
 	   [\\/$]*) false;;
 	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
 	   esac ||
-	   as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+	   as_fn_error 1 "cannot find input file: '$ac_f'" "$LINENO" 5;;
       esac
       case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
       as_fn_append ac_file_inputs " '$ac_f'"
     done
 
-    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # Let's still pretend it is 'configure' which instantiates (i.e., don't
     # use $as_me), people would be surprised to read:
     #    /* config.h.  Generated by config.status.  */
     configure_input='Generated from '`
diff --git a/configure.ac b/configure.ac
--- a/configure.ac
+++ b/configure.ac
@@ -3,7 +3,7 @@
 # See https://www.gnu.org/software/autoconf/manual/autoconf.html for help on
 # the macros used in this file.
 
-AC_INIT([streamly-core], [0.3.0], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])
+AC_INIT([streamly-core], [0.3.2], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])
 
 # To suppress "WARNING: unrecognized options: --with-compiler"
 AC_ARG_WITH([compiler], [GHC])
diff --git a/docs/Changelog.md b/docs/Changelog.md
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -1,6 +1,47 @@
 # Changelog
 
-## 0.3.0
+## 0.3.2 (Oct 2026)
+
+### Bug Fixes
+
+* On POSIX platforms, the `Path` based APIs that open an existing file in
+  `WriteMode`, e.g. `write` and `withFile` in `Streamly.FileSystem.FileIO`,
+  did not truncate it, the old contents beyond the newly written data
+  remained. The file descriptor leaked when the file could not be opened as
+  a Handle, e.g. if it was a directory.
+* Fix `timer`, `timesWith`, `absTimesWith` and `relTimesWith` stopping after a
+  major GC.
+* Fix `enumerateFrom` and `enumerateFromTo` in Stream and Unfold not
+  terminating for bounded integral types when the limit is `maxBound`.
+* Fix `Unfold.enumerateFromTo` for `Identity` calling `enumerateFromThen`.
+* Fix `Stream.splitEndBySeq` and `splitEndBySeq_` dropping the last segment
+  for separators longer than a machine word.
+* `chunksOf` in the array modules no longer emits a trailing empty array
+  when the stream length is a multiple of the chunk size, or when the stream
+  is empty.
+* Fix `classify` and `classifyIO` in Fold and Scanl, and `Fold.classifyScan`,
+  restarting the fold for a key that terminated on its first input.
+* Fix `Scanl.uniqBy` failing before the first input.
+* Fix `Stream.postscanl` emitting an output for a scan that terminates
+  without consuming any input.
+* Fix `Stream.isInfixOf` returning `False` for an empty infix.
+* Fix `RingArray.getIndex`, `cast`, `fold`, `eqArray` and `eqArrayN` for
+  out-of-range indices, misaligned heads, empty rings and array slices.
+* Fix deserialization reading beyond the end of a corrupt or truncated buffer
+  for lists and for derived `Serialize` instances.
+
+### Internal API Bug Fixes
+
+* Fix memory corruption in `MutArray.appendMax`, `MutArray.createMinOf` and
+  `RingArray.Generic.copyToMutArray`, and resource release in
+  `Fold.bracketIO` and `Fold.finallyIO`.
+
+## 0.3.1 (May 2026)
+
+* Fixed `Path.fromString` truncation when unicode chars are present.
+* Fixed `DirIO.followSymlinks` option not working correctly on macOS.
+
+## 0.3.0 (Sep 2025)
 
 See [0.2.2-0.3.0 API Changelog](/core/docs/ApiChangelogs/0.2.2-0.3.0.txt) for a
 full list of deprecations, additions, and changes to the function signatures.
diff --git a/src/Streamly/Internal/Data/Array/Type.hs b/src/Streamly/Internal/Data/Array/Type.hs
--- a/src/Streamly/Internal/Data/Array/Type.hs
+++ b/src/Streamly/Internal/Data/Array/Type.hs
@@ -641,7 +641,7 @@
 unsnoc arr =
     if null arr
     then Nothing
-    else Just (unsafeTail arr, unsafeGetIndexRev 0 arr)
+    else Just (unsafeInit arr, unsafeGetIndexRev 0 arr)
 
 -------------------------------------------------------------------------------
 -- Streams of arrays
diff --git a/src/Streamly/Internal/Data/Fold/Combinators.hs b/src/Streamly/Internal/Data/Fold/Combinators.hs
--- a/src/Streamly/Internal/Data/Fold/Combinators.hs
+++ b/src/Streamly/Internal/Data/Fold/Combinators.hs
@@ -1780,7 +1780,7 @@
 -- >>> ref <- newIORef [Fold.take 2 Fold.sum, Fold.take 2 Fold.length :: Fold IO Int Int]
 -- >>> gen = atomicModifyIORef ref (\xs -> ([], xs))
 -- >>> Stream.toList $ Stream.scanl (Fold.distributeScan gen) (Stream.enumerateFromTo 1 10)
--- [[],[],[],[2,3],[],[],[],[],[],[],[]]
+-- [[],[],[3,2],[],[],[],[],[],[],[],[]]
 --
 {-# INLINE distributeScan #-}
 distributeScan :: Monad m => m [Fold m a b] -> Scanl m a [b]
@@ -1796,7 +1796,11 @@
         case res of
             Partial fs -> do
               r <- step fs a
-              run (Tuple' (Fold step (return r) extr fin : ys) zs) xs a
+              case r of
+                  Partial fs1 ->
+                      let fld = Fold step (return (Partial fs1)) extr fin
+                       in run (Tuple' (fld : ys) zs) xs a
+                  Done b -> run (Tuple' ys (b : zs)) xs a
             Done b -> do
               run (Tuple' ys (b : zs)) xs a
 
@@ -2129,7 +2133,7 @@
 {-# INLINE sampleFromthen #-}
 sampleFromthen :: Monad m => Int -> Int -> Fold m a b -> Fold m a b
 sampleFromthen offset size =
-    with indexed filter (\(i, _) -> (i + offset) `mod` size == 0)
+    with indexed filter (\(i, _) -> i >= offset && (i - offset) `mod` size == 0)
 
 ------------------------------------------------------------------------------
 -- Nesting
diff --git a/src/Streamly/Internal/Data/Fold/Container.hs b/src/Streamly/Internal/Data/Fold/Container.hs
--- a/src/Streamly/Internal/Data/Fold/Container.hs
+++ b/src/Streamly/Internal/Data/Fold/Container.hs
@@ -929,7 +929,7 @@
                           Partial s1 ->
                             Tuple3' (IsMap.mapInsert k s1 kv) set Nothing
                           Done b ->
-                            Tuple3' kv set (Just (k, b))
+                            Tuple3' kv (Set.insert k set) (Just (k, b))
               Done b -> return (Tuple3' kv (Set.insert k set) (Just (k, b)))
 
     step (Tuple3' kv set _) a = do
@@ -1034,7 +1034,7 @@
                           Partial s1 ->
                             Tuple3' (IsMap.mapInsert k s1 kv) set Nothing
                           Done b ->
-                            Tuple3' kv set (Just (k, b))
+                            Tuple3' kv (Set.insert k set) (Just (k, b))
               Done b -> return (Tuple3' kv (Set.insert k set) (Just (k, b)))
 
     step (Tuple3' kv set _) a = do
@@ -1124,7 +1124,7 @@
                         ref <- liftIO $ newIORef s1
                         return $ Tuple3' (IsMap.mapInsert k ref kv) set Nothing
                       Done b ->
-                        return $ Tuple3' kv set (Just (k, b))
+                        return $ Tuple3' kv (Set.insert k set) (Just (k, b))
               Done b -> return (Tuple3' kv (Set.insert k set) (Just (k, b)))
 
     step (Tuple3' kv set _) a = do
@@ -1240,7 +1240,7 @@
                         ref <- liftIO $ newIORef s1
                         return $ Tuple3' (IsMap.mapInsert k ref kv) set Nothing
                       Done b ->
-                        return $ Tuple3' kv set (Just (k, b))
+                        return $ Tuple3' kv (Set.insert k set) (Just (k, b))
               Done b -> return (Tuple3' kv (Set.insert k set) (Just (k, b)))
 
     step (Tuple3' kv set _) a = do
diff --git a/src/Streamly/Internal/Data/Fold/Exception.hs b/src/Streamly/Internal/Data/Fold/Exception.hs
--- a/src/Streamly/Internal/Data/Fold/Exception.hs
+++ b/src/Streamly/Internal/Data/Fold/Exception.hs
@@ -138,9 +138,14 @@
         res <- initial1
         case res of
             Partial s -> do
-                s1 <- step1 s a `MC.onException` liftIO (runIOFinalizer ref)
-                let fld1 = Fold step1 (pure s1) extract1 final1
-                pure $ Partial $ Tuple' ref fld1
+                r <- step1 s a `MC.onException` liftIO (runIOFinalizer ref)
+                case r of
+                    Partial s1 -> do
+                        let fld1 = Fold step1 (pure (Partial s1)) extract1 final1
+                        pure $ Partial $ Tuple' ref fld1
+                    Done b -> do
+                        liftIO $ runIOFinalizer ref
+                        pure $ Done b
             Done b -> do
                 liftIO $ runIOFinalizer ref
                 pure $ Done b
@@ -156,9 +161,11 @@
         case res of
             Partial s -> do
                 val <- final1 s `MC.onException` liftIO (runIOFinalizer ref)
-                runIOFinalizer ref
+                liftIO $ runIOFinalizer ref
                 pure val
-            Done b -> pure b
+            Done b -> do
+                liftIO $ runIOFinalizer ref
+                pure b
 
 -- | Run a side effect whenever the fold stops normally, aborts due to an
 -- exception or is garbage collected.
@@ -173,15 +180,19 @@
     initial = do
         ref <- liftIO $ newIOFinalizer aft
         res <- initial1 `MC.onException` liftIO (runIOFinalizer ref)
-        pure $ case res of
-            Done b -> Done b
-            Partial s -> Partial $ Tuple' ref s
+        case res of
+            Done b -> do
+                liftIO $ runIOFinalizer ref
+                pure $ Done b
+            Partial s -> pure $ Partial $ Tuple' ref s
 
     step (Tuple' ref s) a = do
         res <- step1 s a `MC.onException` liftIO (runIOFinalizer ref)
-        pure $ case res of
-            Done b -> Done b
-            Partial s1 -> Partial $ Tuple' ref s1
+        case res of
+            Done b -> do
+                liftIO $ runIOFinalizer ref
+                pure $ Done b
+            Partial s1 -> pure $ Partial $ Tuple' ref s1
 
     extract (Tuple' ref s) =
         extract1 s `MC.onException` liftIO (runIOFinalizer ref)
diff --git a/src/Streamly/Internal/Data/Fold/Type.hs b/src/Streamly/Internal/Data/Fold/Type.hs
--- a/src/Streamly/Internal/Data/Fold/Type.hs
+++ b/src/Streamly/Internal/Data/Fold/Type.hs
@@ -2435,7 +2435,8 @@
             Partial s -> cextract s
             Done b -> return b
 
--- | Extract the output of a fold and refold it using a 'Refold'.
+-- | Finalize a fold without any input and use its output as the initial
+-- value of a 'Refold'.
 --
 -- A fusible alternative to 'concatMap'.
 --
@@ -2443,7 +2444,7 @@
 {-# INLINE refold #-}
 refold :: Monad m => Refold m b a c -> Fold m a b -> Fold m a c
 refold (Refold step inject extract) f =
-    Fold step (extractM f >>= inject) extract extract
+    Fold step (finalM f >>= inject) extract extract
 
 ------------------------------------------------------------------------------
 -- morphInner
diff --git a/src/Streamly/Internal/Data/IORef.hs b/src/Streamly/Internal/Data/IORef.hs
--- a/src/Streamly/Internal/Data/IORef.hs
+++ b/src/Streamly/Internal/Data/IORef.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE UnboxedTuples #-}
+
 -- |
 -- Module      : Streamly.Internal.Data.IORef
 -- Copyright   : (c) 2019 Composewell Technologies
@@ -25,6 +27,8 @@
 
     -- Construction
     , newIORef
+    , mkWeakIORef
+    , touchIORef
 
     -- Write
     , writeIORef
@@ -45,7 +49,10 @@
 import Data.Kind (Type)
 #endif
 import Data.Proxy (Proxy(..))
-import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)
+import GHC.Exts (mkWeak#)
+import GHC.IO (IO(..))
+import GHC.Weak (Weak(..))
+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray(..))
 import Streamly.Internal.Data.Unbox (Unbox(..), sizeOf)
 
 import qualified Streamly.Internal.Data.MutByteArray.Type as MBA
@@ -66,6 +73,23 @@
     var <- MBA.new (sizeOf (Proxy :: Proxy a))
     pokeAt 0 var x
     return $ IORef var
+
+-- | Make a 'Weak' pointer to an 'IORef', with a finalizer that runs when the
+-- 'IORef' is no longer reachable. The key is the underlying mutable byte
+-- array, not the 'IORef' box, which the compiler may unbox and discard.
+--
+-- /Pre-release/
+mkWeakIORef :: IORef a -> IO () -> IO (Weak (IORef a))
+mkWeakIORef ref@(IORef (MutByteArray arr#)) (IO finalizer) = IO $ \s ->
+    case mkWeak# arr# ref finalizer s of
+        (# s1, w #) -> (# s1, Weak w #)
+
+-- | Keep the 'IORef' reachable at least up to this point, see 'mkWeakIORef'.
+--
+-- /Pre-release/
+{-# INLINE touchIORef #-}
+touchIORef :: IORef a -> IO ()
+touchIORef (IORef var) = MBA.touch var
 
 -- | Write a value to an 'IORef'.
 --
diff --git a/src/Streamly/Internal/Data/MutArray/Generic.hs b/src/Streamly/Internal/Data/MutArray/Generic.hs
--- a/src/Streamly/Internal/Data/MutArray/Generic.hs
+++ b/src/Streamly/Internal/Data/MutArray/Generic.hs
@@ -802,7 +802,10 @@
                 return $ D.Skip (GroupBuffer s contents start end bound)
             D.Stop ->
                 return
-                    $ D.Skip (GroupYield contents start end bound GroupFinish)
+                    $ if end == start
+                      then D.Stop
+                      else D.Skip
+                            (GroupYield contents start end bound GroupFinish)
 
     step' _ (GroupYield contents start end bound next) =
          return $ D.Yield (MutArray contents start end bound) next
@@ -873,8 +876,9 @@
 clone src = do
     let len = length src
     dst <- new len
-    unsafePutSlice src 0 dst 0 len
-    return dst
+    let dst1 = dst { arrEnd = arrStart dst + len }
+    unsafePutSlice src 0 dst1 0 len
+    return dst1
 
 -------------------------------------------------------------------------------
 -- Size
diff --git a/src/Streamly/Internal/Data/MutArray/Type.hs b/src/Streamly/Internal/Data/MutArray/Type.hs
--- a/src/Streamly/Internal/Data/MutArray/Type.hs
+++ b/src/Streamly/Internal/Data/MutArray/Type.hs
@@ -1567,7 +1567,9 @@
         r <- stepi defState st
         case r of
             D.Yield i s -> do
-                x <- liftio $ getIndex i (MutArray contents start end undefined)
+                -- We do not need arrBound but we cannot leave it undefined
+                -- because it is a strict field.
+                x <- liftio $ getIndex i (MutArray contents start end end)
                 case x of
                     Just v -> return $ D.Yield v (GetIndicesState contents start end s)
                     Nothing -> error "Invalid Index"
@@ -1916,7 +1918,10 @@
                 return $ D.Skip (GroupBuffer s contents start end bound)
             D.Stop ->
                 return
-                    $ D.Skip (GroupYield contents start end bound GroupFinish)
+                    $ if end == start
+                      then D.Stop
+                      else D.Skip
+                            (GroupYield contents start end bound GroupFinish)
 
     step' _ (GroupYield contents start end bound next) =
         return $ D.Yield (MutArray contents start end bound) next
@@ -2392,8 +2397,9 @@
     where
 
     free_ = arrBound - arrEnd
-    needed = n * SIZE_OF(a)
-    bound = arrBound + needed - free_
+    -- Keep "needed" to be at least free_ otherwise when recompute the bound in
+    -- "final" we may lose some reserve space.
+    needed = max (n * SIZE_OF(a)) free_
 
     initial = do
         assert (n >= 0) (return ())
@@ -2408,8 +2414,9 @@
         liftIO $ pokeAt end contents x
         return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
 
+    -- If the array gets reallocated the original bound does not remain valid.
     final (ArrayUnsafe contents start end) =
-        MutArray contents start end bound
+        MutArray contents start end (start + arrEnd - arrStart + needed)
 
 {-# DEPRECATED writeAppendNUnsafe "Please use unsafeAppendN instead." #-}
 {-# INLINE writeAppendNUnsafe #-}
@@ -2779,7 +2786,7 @@
     step arr@(MutArray _ start end bound) x
         | INDEX_NEXT(end,a) > bound = do
         let oldSize = end - start
-            newSize = max (oldSize * 2) 1
+            newSize = max (oldSize * 2) (SIZE_OF(a))
         arr1 <- liftIO $ reallocExplicitAs ps (SIZE_OF(a)) newSize arr
         unsafeSnoc arr1 x
     step arr x = unsafeSnoc arr x
diff --git a/src/Streamly/Internal/Data/MutByteArray/Type.hs b/src/Streamly/Internal/Data/MutByteArray/Type.hs
--- a/src/Streamly/Internal/Data/MutByteArray/Type.hs
+++ b/src/Streamly/Internal/Data/MutByteArray/Type.hs
@@ -360,16 +360,12 @@
     :: MutByteArray -> Int -> MutByteArray -> Int -> Int -> IO Int
 unsafeByteCmp
     (MutByteArray marr1) (I# st1#) (MutByteArray marr2) (I# st2#) (I# len#) =
+    -- compareByteArrays# must be evaluated here, a lazy result would read the
+    -- arrays after later mutations.
     IO $ \s# ->
-        let res =
-                I#
-                    (compareByteArrays#
-                         (unsafeCoerce# marr1)
-                         st1#
-                         (unsafeCoerce# marr2)
-                         st2#
-                         len#)
-         in (# s#, res #)
+        case compareByteArrays#
+                (unsafeCoerce# marr1) st1# (unsafeCoerce# marr2) st2# len# of
+            res# -> (# s#, I# res# #)
 
 -------------------------------------------------------------------------------
 -- Pinning & Unpinning
diff --git a/src/Streamly/Internal/Data/RingArray.hs b/src/Streamly/Internal/Data/RingArray.hs
--- a/src/Streamly/Internal/Data/RingArray.hs
+++ b/src/Streamly/Internal/Data/RingArray.hs
@@ -416,13 +416,14 @@
 
 -- | /O(1)/ Lookup the element at the given index relative to the ring head.
 -- Index starts from 0, could be positive or negative. Returns Nothing if the
--- index is more than or equal to the size of the ring.
+-- index is more than or equal to the length of the ring, or less than the
+-- negated length.
 --
 {-# INLINE getIndex #-}
 getIndex :: forall m a. (MonadIO m, Unbox a) => Int -> RingArray a -> m (Maybe a)
 getIndex i ring =
-    let rs = ringSize ring
-     in if i < rs && i > -rs
+    let len = length ring
+     in if i < len && i >= -len
         then Just <$> unsafeGetIndex i ring
         else return Nothing
 
@@ -623,15 +624,17 @@
 asBytes = unsafeCast
 
 -- | Cast a ring having elements of type @a@ into a ring having elements of
--- type @b@. The length of the ring should be a multiple of the size of the
--- target element otherwise 'Nothing' is returned.
+-- type @b@. The byte length of the ring and the byte offset of the ring head
+-- must be multiples of the size of the target element otherwise 'Nothing' is
+-- returned.
 --
 {-# INLINE cast #-}
 cast :: forall a b. (Unbox b) => RingArray a -> Maybe (RingArray b)
 cast ring =
     let len = byteLength ring
         r = len `mod` SIZE_OF(b)
-     in if r /= 0
+        rh = ringHead ring `mod` SIZE_OF(b)
+     in if r /= 0 || rh /= 0
         then Nothing
         else Just $ unsafeCast ring
 
@@ -653,14 +656,15 @@
     | nBytes <= p1Len = do
           part1 <-
               MutByteArray.unsafeByteCmp
-                  arrContents 0 ringContents ringHead nBytes
+                  arrContents arrStart ringContents ringHead nBytes
           pure $ part1 == 0
     | otherwise = do
           part1 <-
               MutByteArray.unsafeByteCmp
-                  arrContents 0 ringContents ringHead p1Len
+                  arrContents arrStart ringContents ringHead p1Len
           part2 <-
-              MutByteArray.unsafeByteCmp arrContents p1Len ringContents 0 p2Len
+              MutByteArray.unsafeByteCmp
+                  arrContents (arrStart + p1Len) ringContents 0 p2Len
           pure $ part1 == 0 && part2 == 0
     where
     p1Len = ringSize - ringHead
@@ -680,10 +684,10 @@
     | otherwise = do
           part1 <-
               MutByteArray.unsafeByteCmp
-                  arrContents 0 ringContents ringHead p1Len
+                  arrContents arrStart ringContents ringHead p1Len
           part2 <-
               MutByteArray.unsafeByteCmp
-                  arrContents p1Len ringContents 0 p2Len
+                  arrContents (arrStart + p1Len) ringContents 0 p2Len
           pure $ part1 == 0 && part2 == 0
     where
     p1Len = ringSize - ringHead
@@ -707,7 +711,10 @@
 fold (Fold step initial _ final) rb = do
     res <- initial
     case res of
-        Fold.Partial fs -> go SPEC rh fs
+        Fold.Partial fs ->
+            if ringSize rb == 0
+            then final fs
+            else go SPEC rh fs
         Fold.Done b -> return b
 
     where
@@ -767,8 +774,6 @@
             go acc1 (index + SIZE_OF(a))
 
 -- | Fold the entire length of a ring buffer starting at the current ring head.
---
--- Note, this will crash on ring of 0 size.
 --
 {-# INLINE foldlM' #-}
 foldlM' :: forall m a b. (MonadIO m, Unbox a)
diff --git a/src/Streamly/Internal/Data/RingArray/Generic.hs b/src/Streamly/Internal/Data/RingArray/Generic.hs
--- a/src/Streamly/Internal/Data/RingArray/Generic.hs
+++ b/src/Streamly/Internal/Data/RingArray/Generic.hs
@@ -165,8 +165,12 @@
             end = idx + len
         arr <- MutArray.emptyOf len
         arr1 <- MutArray.uninit arr len
-        MutArray.unsafePutSlice ringArr idx arr1 0 (ringMax - idx)
-        MutArray.unsafePutSlice ringArr 0 arr1 (ringMax - idx) (end - ringMax)
+        if end <= ringMax
+        then MutArray.unsafePutSlice ringArr idx arr1 0 len
+        else do
+            MutArray.unsafePutSlice ringArr idx arr1 0 (ringMax - idx)
+            MutArray.unsafePutSlice
+                ringArr 0 arr1 (ringMax - idx) (end - ringMax)
         return arr1
 
 -- This would be theoretically slower than toMutArray because of a branch
diff --git a/src/Streamly/Internal/Data/Scanl/Combinators.hs b/src/Streamly/Internal/Data/Scanl/Combinators.hs
--- a/src/Streamly/Internal/Data/Scanl/Combinators.hs
+++ b/src/Streamly/Internal/Data/Scanl/Combinators.hs
@@ -640,14 +640,16 @@
 --
 {-# INLINE uniqBy #-}
 uniqBy :: Monad m => (a -> a -> Bool) -> Scanl m a (Maybe a)
-uniqBy eq = rollingMap f
+uniqBy eq = fmap (\(Tuple' _ x) -> x) $ mkScanl step (Tuple' Nothing Nothing)
 
     where
 
-    f pre curr =
-        case pre of
-            Nothing -> Just curr
-            Just x -> if x `eq` curr then Nothing else Just curr
+    step (Tuple' pre _) curr =
+        let out =
+                case pre of
+                    Just x | x `eq` curr -> Nothing
+                    _ -> Just curr
+         in Tuple' (Just curr) out
 
 -- | See 'uniqBy'.
 --
@@ -2110,7 +2112,7 @@
 {-# INLINE sampleFromthen #-}
 sampleFromthen :: Monad m => Int -> Int -> Scanl m a b -> Scanl m a b
 sampleFromthen offset size =
-    with indexed filter (\(i, _) -> (i + offset) `mod` size == 0)
+    with indexed filter (\(i, _) -> i >= offset && (i - offset) `mod` size == 0)
 
 ------------------------------------------------------------------------------
 -- Nesting
diff --git a/src/Streamly/Internal/Data/Scanl/Container.hs b/src/Streamly/Internal/Data/Scanl/Container.hs
--- a/src/Streamly/Internal/Data/Scanl/Container.hs
+++ b/src/Streamly/Internal/Data/Scanl/Container.hs
@@ -600,7 +600,7 @@
                     return
                         $ Tuple3' (IsMap.mapInsert k s1 kv) set (Just (k, b))
                   Done b ->
-                    return $ Tuple3' kv set (Just (k, b))
+                    return $ Tuple3' kv (Set.insert k set) (Just (k, b))
               Done b -> return (Tuple3' kv (Set.insert k set) (Just (k, b)))
 
     step (Tuple3' kv set _) a = do
@@ -685,7 +685,7 @@
                             $ Tuple3'
                                 (IsMap.mapInsert k ref kv) set (Just (k, b))
                       Done b ->
-                        return $ Tuple3' kv set (Just (k, b))
+                        return $ Tuple3' kv (Set.insert k set) (Just (k, b))
               Done b -> return (Tuple3' kv (Set.insert k set) (Just (k, b)))
 
     step (Tuple3' kv set _) a = do
diff --git a/src/Streamly/Internal/Data/Serialize/TH.hs b/src/Streamly/Internal/Data/Serialize/TH.hs
--- a/src/Streamly/Internal/Data/Serialize/TH.hs
+++ b/src/Streamly/Internal/Data/Serialize/TH.hs
@@ -201,7 +201,15 @@
                      $(varE _initialOffset)
                      $(varE _arr)
                      $(varE _endOffset)
-             $(multiIfE (map (guardCon conLen off1) cons ++ [catchAll]))|]
+             -- xorCmp reads the constructor name without bounds checks, so
+             -- check that the name fits in the buffer before calling it.
+             if $(varE off1) + w8_int $(varE conLen) > $(varE _endOffset)
+             then error
+                 ("deserializeAt: constructor name at offset = "
+                     ++ show $(varE off1)
+                     ++ " exceeds the buffer length = "
+                     ++ show $(varE _endOffset))
+             else $(multiIfE (map (guardCon conLen off1) cons ++ [catchAll]))|]
 
     catchAll =
         normalGE
diff --git a/src/Streamly/Internal/Data/Serialize/TH/RecHeader.hs b/src/Streamly/Internal/Data/Serialize/TH/RecHeader.hs
--- a/src/Streamly/Internal/Data/Serialize/TH/RecHeader.hs
+++ b/src/Streamly/Internal/Data/Serialize/TH/RecHeader.hs
@@ -395,7 +395,11 @@
              deserializeAt $(varE initialOff) $(varE _arr) $(varE endOff)
          ($(varP hOff), hlen1 :: Word32) <-
              deserializeAt hlenOff $(varE _arr) $(varE endOff)
-         if (hlen1 == $(litIntegral hlen)) && $(xorCmp hval hOff _arr)
+         -- xorCmp reads the header without bounds checks, so check that the
+         -- header fits in the buffer before calling it.
+         if (hlen1 == $(litIntegral hlen))
+                && $(varE hOff) + $(litIntegral hlen) <= $(varE endOff)
+                && $(xorCmp hval hOff _arr)
          then do
              let $(varP (makeI 0)) =
                      $(varE initialOff) +
diff --git a/src/Streamly/Internal/Data/Serialize/Type.hs b/src/Streamly/Internal/Data/Serialize/Type.hs
--- a/src/Streamly/Internal/Data/Serialize/Type.hs
+++ b/src/Streamly/Internal/Data/Serialize/Type.hs
@@ -251,6 +251,8 @@
     {-# INLINABLE deserializeAt #-}
     deserializeAt off arr sz = do
         (off1, len64) <- deserializeAt off arr sz :: IO (Int, Int64)
+        when (len64 < 0) $
+            error $ "deserializeAt: negative list length " ++ show len64
         let len = (fromIntegral :: Int64 -> Int) len64
             peekList f o i | i >= 3 = do
               -- Unfold the loop three times
diff --git a/src/Streamly/Internal/Data/Stream/Eliminate.hs b/src/Streamly/Internal/Data/Stream/Eliminate.hs
--- a/src/Streamly/Internal/Data/Stream/Eliminate.hs
+++ b/src/Streamly/Internal/Data/Stream/Eliminate.hs
@@ -100,12 +100,13 @@
 
 {-# INLINE_NORMAL foldr1 #-}
 foldr1 :: Monad m => (a -> a -> a) -> Stream m a -> m (Maybe a)
-foldr1 f m = do
-     r <- uncons m
-     case r of
-         Nothing   -> return Nothing
-         Just (h, t) -> fmap Just (foldr f h t)
+foldr1 f = foldr step Nothing
 
+    where
+
+    step x Nothing = Just x
+    step x (Just acc) = Just (f x acc)
+
 ------------------------------------------------------------------------------
 -- Parsers
 ------------------------------------------------------------------------------
@@ -583,9 +584,16 @@
     => Stream m a -> Stream m a -> m Bool
 isInfixOf infx stream = do
     arr <- fold Array.create infx
-    -- XXX can use breakOnSeq instead (when available)
-    r <- null $ StreamD.drop 1 $ Nesting.splitSepBySeq_ arr Fold.drain stream
-    return (not r)
+    -- An empty infix occurs in every stream. The split below cannot detect
+    -- it in a stream of fewer than two elements, which has fewer than two
+    -- segments.
+    if Array.length arr == 0
+    then return True
+    else do
+        -- XXX can use breakOnSeq instead (when available)
+        r <- null
+            $ StreamD.drop 1 $ Nesting.splitSepBySeq_ arr Fold.drain stream
+        return (not r)
 
 -- Note: isPrefixOf uses the prefix stream only once. In contrast, isSuffixOf
 -- may use the suffix stream many times. To run in optimal memory we do not
diff --git a/src/Streamly/Internal/Data/Stream/Generate.hs b/src/Streamly/Internal/Data/Stream/Generate.hs
--- a/src/Streamly/Internal/Data/Stream/Generate.hs
+++ b/src/Streamly/Internal/Data/Stream/Generate.hs
@@ -500,7 +500,7 @@
 {-# INLINE enumerateFromToIntegral #-}
 enumerateFromToIntegral :: (Monad m, Integral a) => a -> a -> Stream m a
 enumerateFromToIntegral from to =
-    takeWhile (<= to) $ enumerateFromStepIntegral from 1
+    takeWhile (<= to) $ takeEndBy (== to) $ enumerateFromStepIntegral from 1
 
 -- | Enumerate an 'Integral' type. @enumerateFromIntegral from@ generates a
 -- stream whose first element is @from@ and the successive elements are in
diff --git a/src/Streamly/Internal/Data/Stream/Nesting.hs b/src/Streamly/Internal/Data/Stream/Nesting.hs
--- a/src/Streamly/Internal/Data/Stream/Nesting.hs
+++ b/src/Streamly/Internal/Data/Stream/Nesting.hs
@@ -3542,11 +3542,8 @@
                             let jump c = SplitOnSuffixSeqKRInit c s mba
                             yieldReinit jump b
                 Skip s -> go SPEC offset s fs
-                Stop -> do
-                    -- do not issue a blank segment when we end at pattern
-                    if offset == maxOffset && A.byteEq arr patArr
-                    then final fs >> return Stop
-                    else if withSep
+                Stop ->
+                    if withSep
                     then do
                         r <- final fs
                         skip $ SplitOnSuffixSeqYield r SplitOnSuffixSeqDone
diff --git a/src/Streamly/Internal/Data/Stream/Transform.hs b/src/Streamly/Internal/Data/Stream/Transform.hs
--- a/src/Streamly/Internal/Data/Stream/Transform.hs
+++ b/src/Streamly/Internal/Data/Stream/Transform.hs
@@ -502,17 +502,24 @@
 -- Unfortunately, we cannot define lazy scans because the Partial constructor
 -- itself is strict.
 
--- | Postscan a stream using the given fold. A postscan omits the initial
--- (default) value of the accumulator and includes the final value.
+-- | Postscan a stream using the given scan. A postscan emits one output per
+-- input element, omitting the initial value of the accumulator. Equivalently,
+-- @postscanl s = 'Data.List.drop' 1 . 'scanl' s@.
 --
 -- >>> Stream.toList $ Stream.postscanl Scanl.latest (Stream.fromList [])
 -- []
 --
--- Compare with 'scan' which includes the initial value as well:
+-- Compare with 'scanl' which additionally emits the initial value:
 --
 -- >>> Stream.toList $ Stream.scanl Scanl.latest (Stream.fromList [])
 -- [Nothing]
 --
+-- A scan that terminates at the initial step (e.g. @Scanl.take 0@) consumes no
+-- input, so the postscan is empty:
+--
+-- >>> Stream.toList $ Stream.postscanl (Scanl.take 0 Scanl.toList) (Stream.fromList [1,2,3::Int])
+-- []
+--
 -- The following example extracts the input stream up to a point where the
 -- running average of elements is no more than 10:
 --
@@ -540,7 +547,12 @@
         return
             $ case res of
                   FL.Partial fs -> Skip $ ScanDo st fs
-                  FL.Done b -> Yield b ScanDone
+                  -- A scan that is Done at the initial step has consumed no
+                  -- input, so a postscan (one output per consumed input) emits
+                  -- nothing. This keeps the invariant
+                  -- @postscanl s = drop 1 . scanl s@: the initial value is
+                  -- always dropped, whether it comes from a Partial or a Done.
+                  FL.Done _ -> Stop
     step gst (ScanDo st fs) = do
         res <- sstep (adaptState gst) st
         case res of
diff --git a/src/Streamly/Internal/Data/Stream/Type.hs b/src/Streamly/Internal/Data/Stream/Type.hs
--- a/src/Streamly/Internal/Data/Stream/Type.hs
+++ b/src/Streamly/Internal/Data/Stream/Type.hs
@@ -1015,6 +1015,8 @@
     showsPrec p dl = showParen (p > 10) $
         showString "fromList " . shows (GHC.Exts.toList dl)
 
+    show dl = "fromList " ++ show (GHC.Exts.toList dl)
+
 instance Read a => Read (Stream Identity a) where
     readPrec = parens $ prec 10 $ do
         Ident "fromList" <- lexP
@@ -2422,7 +2424,7 @@
 {-# ANN type CIterState Fuse #-}
 data CIterState s f fs b
     = CIterInit s f
-    | CIterConsume s fs
+    | CIterConsume s fs Bool
     | CIterYield b (CIterState s f fs b)
     | CIterStop
 
@@ -2444,20 +2446,27 @@
         return
             $ Skip
             $ case res of
-                  FL.Partial fs -> CIterConsume st fs
+                  FL.Partial fs -> CIterConsume st fs False
                   FL.Done fb -> CIterYield fb $ CIterInit st (return fb)
 
     {-# INLINE_LATE stepOuter #-}
     stepOuter _ (CIterInit st action) = do
         iterStep st (action >>= finject)
-    stepOuter gst (CIterConsume st fs) = do
+    stepOuter gst (CIterConsume st fs consumed) = do
         r <- step (adaptState gst) st
         case r of
-            Yield x s -> iterStep s (fstep fs x)
-            Skip s -> return $ Skip $ CIterConsume s fs
-            Stop -> do
-                b <- fextract fs
-                return $ Skip $ CIterYield b CIterStop
+            Yield x s -> do
+                res <- fstep fs x
+                return $ Skip $ case res of
+                    FL.Partial fs1 -> CIterConsume s fs1 True
+                    FL.Done fb -> CIterYield fb $ CIterInit s (return fb)
+            Skip s -> return $ Skip $ CIterConsume s fs consumed
+            Stop ->
+                if consumed
+                then do
+                    b <- fextract fs
+                    return $ Skip $ CIterYield b CIterStop
+                else return Stop
     stepOuter _ (CIterYield a next) = return $ Yield a next
     stepOuter _ CIterStop = return Stop
 
@@ -2598,6 +2607,9 @@
 -- Do not use automatic derivation for this to show as "fromList" rather than
 -- "fromList Identity".
 instance Show a => Show (Nested Identity a) where
+    {-# INLINE showsPrec #-}
+    showsPrec p (Nested xs) = showsPrec p xs
+
     {-# INLINE show #-}
     show (Nested xs) = show xs
 
diff --git a/src/Streamly/Internal/Data/StreamK/Type.hs b/src/Streamly/Internal/Data/StreamK/Type.hs
--- a/src/Streamly/Internal/Data/StreamK/Type.hs
+++ b/src/Streamly/Internal/Data/StreamK/Type.hs
@@ -1193,6 +1193,8 @@
     showsPrec p dl = showParen (p > 10) $
         showString "fromList " . shows (toList dl)
 
+    show dl = "fromList " ++ show (toList dl)
+
 instance Read a => Read (StreamK Identity a) where
     readPrec = parens $ prec 10 $ do
         Ident "fromList" <- lexP
@@ -2802,6 +2804,9 @@
 -- Do not use automatic derivation for this to show as "fromList" rather than
 -- "fromList Identity".
 instance Show a => Show (Nested Identity a) where
+    {-# INLINE showsPrec #-}
+    showsPrec p (Nested xs) = showsPrec p xs
+
     {-# INLINE show #-}
     show (Nested xs) = show xs
 
diff --git a/src/Streamly/Internal/Data/Time/Clock.hs b/src/Streamly/Internal/Data/Time/Clock.hs
--- a/src/Streamly/Internal/Data/Time/Clock.hs
+++ b/src/Streamly/Internal/Data/Time/Clock.hs
@@ -26,12 +26,12 @@
     )
 where
 
-import Control.Concurrent (threadDelay, ThreadId)
+import Control.Concurrent (forkIO, threadDelay, ThreadId)
 import Control.Concurrent.MVar (MVar, newEmptyMVar, takeMVar, tryPutMVar)
-import Control.Monad (forever, when, void)
+import Control.Monad (when, void)
 import Streamly.Internal.Data.Time.Units
     (MicroSecond64(..), fromAbsTime, addToAbsTime, toRelTime)
-import Streamly.Internal.Control.ForkIO (forkIOManaged)
+import System.Mem.Weak (Weak, deRefWeak)
 
 import qualified Streamly.Internal.Data.IORef as Unboxed
 
@@ -47,12 +47,16 @@
     t <- fromAbsTime <$> getTime clock
     Unboxed.modifyIORef' timeVar (const t)
 
+-- Returns False if the IORef is no longer reachable.
 {-# INLINE updateWithDelay #-}
 updateWithDelay :: RealFrac a =>
-    Clock -> a -> Unboxed.IORef MicroSecond64 -> IO ()
-updateWithDelay clock precision timeVar = do
+    Clock -> a -> Weak (Unboxed.IORef MicroSecond64) -> IO Bool
+updateWithDelay clock precision weakVar = do
     threadDelay (delayTime precision)
-    updateTimeVar clock timeVar
+    r <- deRefWeak weakVar
+    case r of
+        Nothing -> return False
+        Just timeVar -> updateTimeVar clock timeVar >> return True
 
     where
 
@@ -69,8 +73,8 @@
 
 -- | @asyncClock g@ starts a clock thread that updates an IORef with current
 -- time as a 64-bit value in microseconds, every 'g' seconds. The IORef can be
--- read asynchronously.  The thread exits automatically when the reference to
--- the returned 'ThreadId' is lost.
+-- read asynchronously.  The thread exits automatically when the IORef is no
+-- longer reachable.
 --
 -- Minimum granularity of clock update is 1 ms. Higher is better for
 -- performance.
@@ -82,9 +86,19 @@
 asyncClock clock g = do
     timeVar <- Unboxed.newIORef 0
     updateTimeVar clock timeVar
-    tid <- forkIOManaged $ forever (updateWithDelay clock g timeVar)
+    -- The thread holds only a weak reference to timeVar. The consumers may
+    -- drop the returned ThreadId, so a finalizer on the ThreadId may kill the
+    -- thread at the next GC while timeVar is still being read.
+    weakVar <- Unboxed.mkWeakIORef timeVar (return ())
+    tid <- forkIO $ loop weakVar
     return (tid, timeVar)
 
+    where
+
+    loop weakVar = do
+        continue <- updateWithDelay clock g weakVar
+        when continue $ loop weakVar
+
 {-# INLINE readClock #-}
 readClock :: (ThreadId, Unboxed.IORef MicroSecond64) -> IO MicroSecond64
 readClock (_, timeVar) = Unboxed.readIORef timeVar
@@ -94,7 +108,7 @@
 ------------------------------------------------------------------------------
 
 -- | Adjustable periodic timer.
-data Timer = Timer ThreadId (MVar ()) (IO ())
+data Timer = Timer ThreadId (MVar ()) (Unboxed.IORef MicroSecond64) (IO ())
 
 -- Set the expiry to current time + timer period
 {-# INLINE resetTimerExpiry #-}
@@ -104,18 +118,29 @@
     let t1 = addToAbsTime t (toRelTime period)
     Unboxed.modifyIORef' timeVar (const (fromAbsTime t1))
 
+-- Returns False if the IORef is no longer reachable.
 {-# INLINE processTimerTick #-}
 processTimerTick :: RealFrac a =>
-    Clock -> a -> Unboxed.IORef MicroSecond64 -> MVar () -> IO () -> IO ()
-processTimerTick clock precision timeVar mvar reset = do
+       Clock
+    -> a
+    -> MicroSecond64
+    -> Weak (Unboxed.IORef MicroSecond64)
+    -> MVar ()
+    -> IO Bool
+processTimerTick clock precision period weakVar mvar = do
     threadDelay (delayTime precision)
-    t <- fromAbsTime <$> getTime clock
-    expiry <- Unboxed.readIORef timeVar
-    when (t >= expiry) $ do
-        -- non-blocking put so that we can process multiple timers in a
-        -- non-blocking manner in future.
-        void $ tryPutMVar mvar ()
-        reset
+    r <- deRefWeak weakVar
+    case r of
+        Nothing -> return False
+        Just timeVar -> do
+            t <- fromAbsTime <$> getTime clock
+            expiry <- Unboxed.readIORef timeVar
+            when (t >= expiry) $ do
+                -- non-blocking put so that we can process multiple timers in
+                -- a non-blocking manner in future.
+                void $ tryPutMVar mvar ()
+                resetTimerExpiry clock period timeVar
+            return True
 
     where
 
@@ -142,21 +167,33 @@
     timeVar <- Unboxed.newIORef 0
     let p = round (period * 1e6) :: Int
         p1 = fromIntegral p :: MicroSecond64
-        reset = resetTimerExpiry clock p1 timeVar
-        process = processTimerTick clock g timeVar mvar reset
-    reset
-    tid <- forkIOManaged $ forever process
-    return $ Timer tid mvar reset
+    resetTimerExpiry clock p1 timeVar
+    -- The thread holds only a weak reference to timeVar, and exits when the
+    -- Timer is no longer reachable. It holds mvar strongly, otherwise a thread
+    -- blocked in waitTimer would be unreachable and get
+    -- BlockedIndefinitelyOnMVar.
+    weakVar <- Unboxed.mkWeakIORef timeVar (return ())
+    tid <- forkIO $ loop p1 weakVar mvar
+    return $ Timer tid mvar timeVar (resetTimerExpiry clock p1 timeVar)
 
+    where
+
+    loop p1 weakVar mvar = do
+        continue <- processTimerTick clock g p1 weakVar mvar
+        when continue $ loop p1 weakVar mvar
+
 -- | Blocking wait for a timer tick.
 {-# INLINE waitTimer #-}
 waitTimer :: Timer -> IO ()
-waitTimer (Timer _ mvar _) = takeMVar mvar
+waitTimer (Timer _ mvar timeVar _) = do
+    takeMVar mvar
+    -- Keep timeVar reachable, so that the timer thread keeps running
+    Unboxed.touchIORef timeVar
 
 -- | Resets the current period.
 {-# INLINE resetTimer #-}
 resetTimer :: Timer -> IO ()
-resetTimer (Timer _ _ reset) = reset
+resetTimer (Timer _ _ _ reset) = reset
 
 -- | Elongates the current period by specified amount.
 --
diff --git a/src/Streamly/Internal/Data/Unfold.hs b/src/Streamly/Internal/Data/Unfold.hs
--- a/src/Streamly/Internal/Data/Unfold.hs
+++ b/src/Streamly/Internal/Data/Unfold.hs
@@ -853,8 +853,8 @@
 --
 -- /Pre-release/
 {-# INLINE_NORMAL before #-}
-before :: (a -> m c) -> Unfold m a b -> Unfold m a b
-before action (Unfold step inject) = Unfold step (action >> inject)
+before :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
+before action (Unfold step inject) = Unfold step (\a -> action a >> inject a)
 
 -- The custom implementation of "after_" is slightly faster (5-7%) than
 -- "_after".  This is just to document and make sure that we can always use
diff --git a/src/Streamly/Internal/Data/Unfold/Enumeration.hs b/src/Streamly/Internal/Data/Unfold/Enumeration.hs
--- a/src/Streamly/Internal/Data/Unfold/Enumeration.hs
+++ b/src/Streamly/Internal/Data/Unfold/Enumeration.hs
@@ -201,12 +201,27 @@
 enumerateFromThenIntegral =
     lmap (\(from, next) -> (from, next - from)) enumerateFromStepIntegral
 
-{-# INLINE enumerateFromToIntegral #-}
+data EnumToState a = EnumToYield !a !a | EnumToStop
+
+-- Stops after yielding "to" instead of computing "to + 1", which overflows
+-- when "to" is maxBound.
+{-# INLINE_NORMAL enumerateFromToIntegral #-}
 enumerateFromToIntegral :: (Monad m, Integral a) => Unfold m (a, a) a
-enumerateFromToIntegral =
-    takeWhileMWithInput (\(_, to) b -> return $ b <= to)
-        $ lmap (\(from, _) -> (from, 1)) enumerateFromStepIntegral
+enumerateFromToIntegral = Unfold step inject
 
+    where
+
+    inject (from, to) =
+        return $ if from > to then EnumToStop else EnumToYield from to
+
+    {-# INLINE_LATE step #-}
+    step (EnumToYield x to) =
+        return
+            $ if x == to
+              then Yield x EnumToStop
+              else Yield x (EnumToYield (x + 1) to)
+    step EnumToStop = return Stop
+
 {-# INLINE enumerateFromThenToIntegral #-}
 enumerateFromThenToIntegral :: (Monad m, Integral a) => Unfold m (a, a, a) a
 enumerateFromThenToIntegral =
@@ -533,7 +548,7 @@
         map Identity $ lmap (bimap runIdentity runIdentity) enumerateFromThen
     {-# INLINE enumerateFromTo #-}
     enumerateFromTo  =
-        map Identity $ lmap (bimap runIdentity runIdentity) enumerateFromThen
+        map Identity $ lmap (bimap runIdentity runIdentity) enumerateFromTo
     {-# INLINE enumerateFromThenTo #-}
     enumerateFromThenTo  =
         map Identity $
diff --git a/src/Streamly/Internal/FileSystem/Path/Common.hs b/src/Streamly/Internal/FileSystem/Path/Common.hs
--- a/src/Streamly/Internal/FileSystem/Path/Common.hs
+++ b/src/Streamly/Internal/FileSystem/Path/Common.hs
@@ -1227,7 +1227,10 @@
     -> Stream Identity Char
     -> Array a
 unsafeFromChars encode s =
-    let n = runIdentity $ Stream.fold Fold.length s
+    -- The encoded array may be longer than the char count. We are encoding it
+    -- twice, but it may still be cheaper than reallocating the array or
+    -- oversizing the array.
+    let n = runIdentity $ Stream.fold Fold.length (encode s)
      in Array.fromPureStreamN n (encode s)
 
 -- XXX Writing a custom fold for parsing a Posix path may be better for
diff --git a/src/Streamly/Internal/FileSystem/Posix/File.hsc b/src/Streamly/Internal/FileSystem/Posix/File.hsc
--- a/src/Streamly/Internal/FileSystem/Posix/File.hsc
+++ b/src/Streamly/Internal/FileSystem/Posix/File.hsc
@@ -80,18 +80,24 @@
 -- Imports
 -------------------------------------------------------------------------------
 
+import Control.Exception (onException)
+import Control.Monad (when)
 import Data.Bits ((.|.), (.&.), complement)
 import Foreign.C.Error (throwErrnoIfMinus1_)
 import Foreign.C.String (CString)
 import Foreign.C.Types (CInt(..))
-import GHC.IO.Handle.FD (fdToHandle)
+import GHC.IO.Device (IODeviceType(..))
+import GHC.IO.Handle.FD (mkHandleFromFD)
 import Streamly.Internal.FileSystem.Posix.Errno (throwErrnoPathIfMinus1Retry)
 import Streamly.Internal.FileSystem.PosixPath (PosixPath)
 import System.IO (IOMode(..), Handle)
 import System.Posix.Types (Fd(..), CMode(..))
 
+import qualified GHC.IO.Device as Device
+import qualified GHC.IO.FD as FD
 import qualified Streamly.Internal.FileSystem.File.Common as File
 import qualified Streamly.Internal.FileSystem.PosixPath as Path
+import qualified System.Posix.Internals as Posix
 
 -- We want to remain close to the Posix C API. A function based API to set and
 -- clear the modes is simple, type safe and directly mirrors the C API. It does
@@ -253,33 +259,26 @@
         $ openAtCString fdMay cstr flags cmode
 
 
--- | Open a regular file, return an Fd.
+-- | The open flags and the create mode for opening a regular file in the given
+-- 'IOMode'.
 --
 -- Sets O_NOCTTY, O_NONBLOCK flags to be compatible with the base openFile
 -- behavior. O_NOCTTY affects opening of terminal special files and O_NONBLOCK
 -- affects fifo special files, and mandatory locking.
 --
-openFileFdWith :: OpenFlags -> PosixPath -> IOMode -> IO Fd
-openFileFdWith oflags path iomode = do
+openFileFlags :: OpenFlags -> IOMode -> (OpenFlags, Maybe FileMode)
+openFileFlags oflags iomode =
     case iomode of
-        ReadMode -> open1 (setReadOnly oflags1) Nothing
-        WriteMode ->
-            open1 (setWriteOnly oflags1) (Just defaultCreateMode)
+        ReadMode -> (setReadOnly oflags1, Nothing)
+        WriteMode -> (setWriteOnly oflags1, Just defaultCreateMode)
         AppendMode ->
-            open1
-                ((setAppend True . setWriteOnly) oflags1)
-                (Just defaultCreateMode)
-        ReadWriteMode ->
-            open1 (setReadWrite oflags) (Just defaultCreateMode)
+            ((setAppend True . setWriteOnly) oflags1, Just defaultCreateMode)
+        ReadWriteMode -> (setReadWrite oflags1, Just defaultCreateMode)
 
     where
 
     oflags1 = setNoCtty True $ setNonBlock True oflags
-    open1 = openAt Nothing path
 
-openFileFd :: PosixPath -> IOMode -> IO Fd
-openFileFd = openFileFdWith defaultOpenFlags
-
 foreign import ccall unsafe "unistd.h close"
    c_close :: CInt -> IO CInt
 
@@ -290,11 +289,33 @@
 -- base openFile compatible, Handle returning, APIs
 -------------------------------------------------------------------------------
 
--- | Open a regular file, return a Handle. The file is locked, the Handle is
--- NOT set up to close the file on garbage collection.
-{-# INLINE openFileHandle #-}
+-- | Like openFile in base. open() can be slow, e.g. on NFS or FUSE file
+-- systems, or block, so use an interruptible foreign call for it.
+interruptibleOpen :: CString -> CInt -> CMode -> IO CInt
+##if MIN_VERSION_base(4,16,0)
+interruptibleOpen = Posix.c_interruptible_open
+##else
+interruptibleOpen = Posix.c_safe_open
+##endif
+
+-- | Open a file and return a Handle in binary mode. Like openFile in base,
+-- the file is locked and a file opened in 'WriteMode' is truncated.
 openFileHandle :: PosixPath -> IOMode -> IO Handle
-openFileHandle p x = openFileFd p x >>= fdToHandle . fromIntegral
+openFileHandle path iomode = do
+    let (flags, cmode) = openFileFlags defaultOpenFlags iomode
+        OpenFlags flags1 = maybe flags (\_ -> setCreat True flags) cmode
+        FileMode mode = maybe defaultCreateMode id cmode
+    fd <- Path.asCString path $ \cstr ->
+        throwErrnoPathIfMinus1Retry "openFile" path
+            $ interruptibleOpen cstr flags1 mode
+    (fD, fdType) <-
+        FD.mkFD fd iomode Nothing False True `onException` c_close fd
+    -- Like base, truncate after locking the file, ftruncate fails on special
+    -- files like /dev/null.
+    when (iomode == WriteMode && fdType == RegularFile)
+        $ Device.setSize fD 0 `onException` Device.close fD
+    mkHandleFromFD fD fdType (Path.toString path) iomode False Nothing
+        `onException` Device.close fD
 
 -- | Like openFile in base package but using Path instead of FilePath.
 -- Use hSetBinaryMode on the handle if you want to use binary mode.
diff --git a/src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc b/src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc
--- a/src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc
+++ b/src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc
@@ -144,10 +144,10 @@
 -- Stat
 --------------------------------------------------------------------------------
 
-foreign import ccall unsafe "stat.h lstat"
+foreign import capi unsafe "sys/stat.h lstat"
     c_lstat :: CString -> Ptr CStat -> IO CInt
 
-foreign import ccall unsafe "stat.h stat"
+foreign import capi unsafe "sys/stat.h stat"
     c_stat :: CString -> Ptr CStat -> IO CInt
 
 s_IFMT :: CMode
diff --git a/src/config.h.in b/src/config.h.in
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -1,6 +1,6 @@
 /* src/config.h.in.  Generated from configure.ac by autoheader.  */
 
-/* Define to 1 if you have the `clock_gettime' function. */
+/* Define to 1 if you have the 'clock_gettime' function. */
 #undef HAVE_CLOCK_GETTIME
 
 /* Define to 1 if you have the <inttypes.h> header file. */
@@ -51,7 +51,7 @@
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
 
-/* Define to 1 if all of the C90 standard headers exist (not just the ones
+/* Define to 1 if all of the C89 standard headers exist (not just the ones
    required in a freestanding environment). This macro is provided for
    backward compatibility; new code need not use it. */
 #undef STDC_HEADERS
diff --git a/streamly-core.cabal b/streamly-core.cabal
--- a/streamly-core.cabal
+++ b/streamly-core.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               streamly-core
-version:            0.3.0
+version:            0.3.2
 synopsis:           Streaming, parsers, arrays, serialization and more
 description:
   For upgrading to streamly-0.9.0+ please read the
@@ -570,16 +570,16 @@
                     -- depend on streamly.
                        ghc-prim          >= 0.5.3 && < 0.14
                      , fusion-plugin-types >= 0.1 && < 0.2
-                     , base              >= 4.12  && < 4.22
+                     , base              >= 4.12  && < 4.23
                      , exceptions        >= 0.8.0 && < 0.11
                      , transformers      >= 0.5.5 && < 0.7
 
                     -- streamly-unicode-core
-                     , template-haskell  >= 2.14  && < 2.24
+                     , template-haskell  >= 2.14  && < 2.25
 
                      -- XXX to be removed
                      , filepath          >= 1.4.2 && < 1.6
-                     , containers        >= 0.6.0 && < 0.8
+                     , containers        >= 0.6.0 && < 0.9
                      , heaps             >= 0.3   && < 0.5
 
     if impl(ghc >= 9.0)
@@ -591,4 +591,4 @@
       build-depends:   monad-control     >= 1.0 && < 1.1
 
     if os(windows)
-      build-depends: Win32            >= 2.6 && < 2.14
+      build-depends: Win32            >= 2.6 && < 2.15
