diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,7 @@
+## 5.8.0
+
+* Update to 5.8.0 upstream sources
+
 ## 5.6.3
 
 * Update to 5.6.3 upstream sources
diff --git a/cbits/common/my_landlock.h b/cbits/common/my_landlock.h
new file mode 100644
--- /dev/null
+++ b/cbits/common/my_landlock.h
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: 0BSD
+
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       my_landlock.h
+/// \brief      Linux Landlock sandbox helper functions
+//
+//  Author:     Lasse Collin
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MY_LANDLOCK_H
+#define MY_LANDLOCK_H
+
+#include "sysdefs.h"
+
+#include <linux/landlock.h>
+#include <sys/syscall.h>
+#include <sys/prctl.h>
+
+
+/// \brief      Initialize Landlock ruleset attributes to forbid everything
+///
+/// The supported Landlock ABI is checked at runtime and only the supported
+/// actions are forbidden in the attributes. Thus, if the attributes are
+/// used with my_landlock_create_ruleset(), it shouldn't fail.
+///
+/// \return     On success, the Landlock ABI version is returned (a positive
+///             integer). If Landlock isn't supported, -1 is returned.
+static int
+my_landlock_ruleset_attr_forbid_all(struct landlock_ruleset_attr *attr)
+{
+	memzero(attr, sizeof(*attr));
+
+	const int abi_version = syscall(SYS_landlock_create_ruleset,
+			(void *)NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
+	if (abi_version <= 0)
+		return -1;
+
+	// ABI 1 except the few at the end
+	attr->handled_access_fs
+			= LANDLOCK_ACCESS_FS_EXECUTE
+			| LANDLOCK_ACCESS_FS_WRITE_FILE
+			| LANDLOCK_ACCESS_FS_READ_FILE
+			| LANDLOCK_ACCESS_FS_READ_DIR
+			| LANDLOCK_ACCESS_FS_REMOVE_DIR
+			| LANDLOCK_ACCESS_FS_REMOVE_FILE
+			| LANDLOCK_ACCESS_FS_MAKE_CHAR
+			| LANDLOCK_ACCESS_FS_MAKE_DIR
+			| LANDLOCK_ACCESS_FS_MAKE_REG
+			| LANDLOCK_ACCESS_FS_MAKE_SOCK
+			| LANDLOCK_ACCESS_FS_MAKE_FIFO
+			| LANDLOCK_ACCESS_FS_MAKE_BLOCK
+			| LANDLOCK_ACCESS_FS_MAKE_SYM
+#ifdef LANDLOCK_ACCESS_FS_REFER
+			| LANDLOCK_ACCESS_FS_REFER // ABI 2
+#endif
+#ifdef LANDLOCK_ACCESS_FS_TRUNCATE
+			| LANDLOCK_ACCESS_FS_TRUNCATE // ABI 3
+#endif
+#ifdef LANDLOCK_ACCESS_FS_IOCTL_DEV
+			| LANDLOCK_ACCESS_FS_IOCTL_DEV // ABI 5
+#endif
+			;
+
+#ifdef LANDLOCK_ACCESS_NET_BIND_TCP
+	// ABI 4
+	attr->handled_access_net
+			= LANDLOCK_ACCESS_NET_BIND_TCP
+			| LANDLOCK_ACCESS_NET_CONNECT_TCP;
+#endif
+
+#ifdef LANDLOCK_SCOPE_SIGNAL
+	 // ABI 6
+	 attr->scoped
+			 = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
+			 | LANDLOCK_SCOPE_SIGNAL;
+#endif
+
+	// Disable flags that require a new ABI version.
+	switch (abi_version) {
+	case 1:
+#ifdef LANDLOCK_ACCESS_FS_REFER
+		attr->handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
+#endif
+		FALLTHROUGH;
+
+	case 2:
+#ifdef LANDLOCK_ACCESS_FS_TRUNCATE
+		attr->handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
+#endif
+		FALLTHROUGH;
+
+	case 3:
+#ifdef LANDLOCK_ACCESS_NET_BIND_TCP
+		attr->handled_access_net = 0;
+#endif
+		FALLTHROUGH;
+
+	case 4:
+#ifdef LANDLOCK_ACCESS_FS_IOCTL_DEV
+		attr->handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
+#endif
+		FALLTHROUGH;
+
+	case 5:
+#ifdef LANDLOCK_SCOPE_SIGNAL
+		attr->scoped = 0;
+#endif
+		FALLTHROUGH;
+
+	default:
+		// We only know about the features of the ABIs 1-6.
+		break;
+	}
+
+	return abi_version;
+}
+
+
+/// \brief      Wrapper for the landlock_create_ruleset(2) syscall
+///
+/// Syscall wrappers provide argument type checking.
+///
+/// \note       Remember to call `prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)` too!
+static inline int
+my_landlock_create_ruleset(const struct landlock_ruleset_attr *attr,
+		size_t size, uint32_t flags)
+{
+	return syscall(SYS_landlock_create_ruleset, attr, size, flags);
+}
+
+
+/// \brief      Wrapper for the landlock_restrict_self(2) syscall
+static inline int
+my_landlock_restrict_self(int ruleset_fd, uint32_t flags)
+{
+	return syscall(SYS_landlock_restrict_self, ruleset_fd, flags);
+}
+
+#endif
diff --git a/cbits/common/sysdefs.h b/cbits/common/sysdefs.h
--- a/cbits/common/sysdefs.h
+++ b/cbits/common/sysdefs.h
@@ -168,17 +168,24 @@
 #	define __bool_true_false_are_defined 1
 #endif
 
+// We may need alignas from C11/C17/C23.
+#if __STDC_VERSION__ >= 202311
+	// alignas is a keyword in C23. Do nothing.
+#elif __STDC_VERSION__ >= 201112
+#	include <stdalign.h>
+#elif defined(__GNUC__) || defined(__clang__)
+#	define alignas(n) __attribute__((__aligned__(n)))
+#else
+#	define alignas(n)
+#endif
+
 #include <string.h>
 
-// Visual Studio 2013 update 2 supports only __inline, not inline.
-// MSVC v19.0 / VS 2015 and newer support both.
+// MSVC v19.00 (VS 2015 version 14.0) and later should work.
 //
 // MSVC v19.27 (VS 2019 version 16.7) added support for restrict.
 // Older ones support only __restrict.
 #ifdef _MSC_VER
-#	if _MSC_VER < 1900 && !defined(inline)
-#		define inline __inline
-#	endif
 #	if _MSC_VER < 1927 && !defined(restrict)
 #		define restrict __restrict
 #	endif
@@ -206,6 +213,15 @@
 #	define lzma_attr_alloc_size(x) __attribute__((__alloc_size__(x)))
 #else
 #	define lzma_attr_alloc_size(x)
+#endif
+
+#if __STDC_VERSION__ >= 202311
+#	define FALLTHROUGH [[__fallthrough__]]
+#elif (defined(__GNUC__) && __GNUC__ >= 7) \
+		|| (defined(__clang_major__) && __clang_major__ >= 10)
+#	define FALLTHROUGH __attribute__((__fallthrough__))
+#else
+#	define FALLTHROUGH ((void)0)
 #endif
 
 #endif
diff --git a/cbits/common/tuklib_common.h b/cbits/common/tuklib_common.h
--- a/cbits/common/tuklib_common.h
+++ b/cbits/common/tuklib_common.h
@@ -56,6 +56,13 @@
 #	define TUKLIB_GNUC_REQ(major, minor) 0
 #endif
 
+#if defined(__GNUC__) || defined(__clang__)
+#	define tuklib_attr_format_printf(fmt_index, args_index) \
+		__attribute__((__format__(__printf__, fmt_index, args_index)))
+#else
+#	define tuklib_attr_format_printf(fmt_index, args_index)
+#endif
+
 // tuklib_attr_noreturn attribute is used to mark functions as non-returning.
 // We cannot use "noreturn" as the macro name because then C23 code that
 // uses [[noreturn]] would break as it would expand to [[ [[noreturn]] ]].
@@ -68,9 +75,7 @@
 //     __attribute__((nonnull(1)))
 //     extern void foo(const char *s);
 //
-// FIXME: Update __STDC_VERSION__ for the final C23 version. 202000 is used
-// by GCC 13 and Clang 15 with -std=c2x.
-#if   defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202000
+#if   defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311
 #	define tuklib_attr_noreturn [[noreturn]]
 #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112
 #	define tuklib_attr_noreturn _Noreturn
diff --git a/cbits/common/tuklib_gettext.h b/cbits/common/tuklib_gettext.h
--- a/cbits/common/tuklib_gettext.h
+++ b/cbits/common/tuklib_gettext.h
@@ -40,4 +40,15 @@
 #endif
 #define N_(msgid) msgid
 
+// Optional: Strings that are word wrapped using tuklib_mbstr_wrap may be
+// marked with W_("foo) in the source code. xgettext can then add a comment
+// to all such strings to inform translators. The following option needs to
+// be added to XGETTEXT_OPTIONS in po/Makevars or in an equivalent place:
+//
+// '--keyword=W_:1,"This is word wrapped at spaces. The Unicode character U+00A0 works as a non-breaking space. Tab (\t) is interpret as a zero-width space (the tab itself is not displayed); U+200B is NOT supported. Manual word wrapping with \n is supported but requires care."'
+//
+// NOTE: The double-quotes in the --keyword argument above must be passed to
+// xgettext as is, thus one needs the single-quotes in Makevars.
+#define W_(msgid) _(msgid)
+
 #endif
diff --git a/cbits/common/tuklib_mbstr.h b/cbits/common/tuklib_mbstr.h
--- a/cbits/common/tuklib_mbstr.h
+++ b/cbits/common/tuklib_mbstr.h
@@ -27,10 +27,7 @@
 ///
 /// This is somewhat similar to wcswidth() but works on multibyte strings.
 ///
-/// \param      str         String whose width is to be calculated. If the
-///                         current locale uses a multibyte character set
-///                         that has shift states, the string must begin
-///                         and end in the initial shift state.
+/// \param      str         String whose width is to be calculated.
 /// \param      bytes       If this is not NULL, *bytes is set to the
 ///                         value returned by strlen(str) (even if an
 ///                         error occurs when calculating the width).
@@ -38,8 +35,24 @@
 /// \return     On success, the number of columns needed to display the
 ///             string e.g. in a terminal emulator is returned. On error,
 ///             (size_t)-1 is returned. Possible errors include invalid,
-///             partial, or non-printable multibyte character in str, or
-///             that str doesn't end in the initial shift state.
+///             partial, or non-printable multibyte character in str.
+
+#define tuklib_mbstr_width_mem TUKLIB_SYMBOL(tuklib_mbstr_width_mem)
+extern size_t tuklib_mbstr_width_mem(const char *str, size_t len);
+///<
+/// \brief      Get the number of columns needed for the multibyte buffer
+///
+/// This is like tuklib_mbstr_width() except that this takes the buffer
+/// length in bytes as the second argument. This allows using the function
+/// for buffers that aren't terminated with '\0'.
+///
+/// \param      str         String whose width is to be calculated.
+/// \param      len         Number of bytes to read from str.
+///
+/// \return     On success, the number of columns needed to display the
+///             string e.g. in a terminal emulator is returned. On error,
+///             (size_t)-1 is returned. Possible errors include invalid,
+///             partial, or non-printable multibyte character in str.
 
 #define tuklib_mbstr_fw TUKLIB_SYMBOL(tuklib_mbstr_fw)
 extern int tuklib_mbstr_fw(const char *str, int columns_min);
diff --git a/cbits/common/tuklib_mbstr_nonprint.c b/cbits/common/tuklib_mbstr_nonprint.c
new file mode 100644
--- /dev/null
+++ b/cbits/common/tuklib_mbstr_nonprint.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: 0BSD
+
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       tuklib_mbstr_nonprint.c
+/// \brief      Find and replace non-printable characters with question marks
+//
+//  Author:     Lasse Collin
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "tuklib_mbstr_nonprint.h"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_MBRTOWC
+#	include <wchar.h>
+#	include <wctype.h>
+#else
+#	include <ctype.h>
+#endif
+
+
+static bool
+is_next_printable(const char *str, size_t len, size_t *next_len)
+{
+#ifdef HAVE_MBRTOWC
+	// This assumes that character sets with locking shift states aren't
+	// used, and thus mbsinit() is never needed.
+	mbstate_t ps;
+	memset(&ps, 0, sizeof(ps));
+
+	wchar_t wc;
+	*next_len = mbrtowc(&wc, str, len, &ps);
+
+	if (*next_len == (size_t)-2) {
+		// Incomplete multibyte sequence: Treat the whole sequence
+		// as a single non-printable multibyte character that ends
+		// the string.
+		*next_len = len;
+		return false;
+	}
+
+	// Check more broadly than just ret == (size_t)-1 to be safe
+	// in case mbrtowc() returns something weird. This check
+	// covers (size_t)-1 (that is, SIZE_MAX) too because len is from
+	// strlen() and the terminating '\0' isn't part of the length.
+	if (*next_len < 1 || *next_len > len) {
+		// Invalid multibyte sequence: Treat the first byte as
+		// a non-printable single-byte character. Decoding will
+		// be restarted from the next byte on the next call to
+		// this function.
+		*next_len = 1;
+		return false;
+	}
+
+#	if defined(_WIN32) && !defined(__CYGWIN__)
+	// On Windows, wchar_t stores UTF-16 code units, thus characters
+	// outside the Basic Multilingual Plane (BMP) don't fit into
+	// a single wchar_t. In an UTF-8 locale, UCRT's mbrtowc() returns
+	// successfully when the input is a non-BMP character but the
+	// output is the replacement character U+FFFD.
+	//
+	// iswprint() returns 0 for U+FFFD on Windows for some reason. Treat
+	// U+FFFD as printable and thus also all non-BMP chars as printable.
+	if (wc == 0xFFFD)
+		return true;
+#	endif
+
+	return iswprint((wint_t)wc) != 0;
+#else
+	(void)len;
+	*next_len = 1;
+	return isprint((unsigned char)str[0]) != 0;
+#endif
+}
+
+
+static bool
+has_nonprint(const char *str, size_t len)
+{
+	for (size_t i = 0; i < len; ) {
+		size_t next_len;
+		if (!is_next_printable(str + i, len - i, &next_len))
+			return true;
+
+		i += next_len;
+	}
+
+	return false;
+}
+
+
+extern bool
+tuklib_has_nonprint(const char *str)
+{
+	const int saved_errno = errno;
+	const bool ret = has_nonprint(str, strlen(str));
+	errno = saved_errno;
+	return ret;
+}
+
+
+extern const char *
+tuklib_mask_nonprint_r(const char *str, char **mem)
+{
+	const int saved_errno = errno;
+
+	// Free the old string, if any.
+	free(*mem);
+	*mem = NULL;
+
+	// If the whole input string contains only printable characters,
+	// return the input string.
+	const size_t len = strlen(str);
+	if (!has_nonprint(str, len)) {
+		errno = saved_errno;
+		return str;
+	}
+
+	// Allocate memory for the masked string. Since we use the single-byte
+	// character '?' to mask non-printable characters, it's possible that
+	// a few bytes less memory would be needed in reality if multibyte
+	// characters are masked.
+	//
+	// If allocation fails, return "???" because it should be safer than
+	// returning the unmasked string.
+	*mem = malloc(len + 1);
+	if (*mem == NULL) {
+		errno = saved_errno;
+		return "???";
+	}
+
+	// Replace all non-printable characters with '?'.
+	char *dest = *mem;
+
+	for (size_t i = 0; i < len; ) {
+		size_t next_len;
+		if (is_next_printable(str + i, len - i, &next_len)) {
+			memcpy(dest, str + i, next_len);
+			dest += next_len;
+		} else {
+			*dest++ = '?';
+		}
+
+		i += next_len;
+	}
+
+	*dest = '\0';
+
+	errno = saved_errno;
+	return *mem;
+}
+
+
+extern const char *
+tuklib_mask_nonprint(const char *str)
+{
+	static char *mem = NULL;
+	return tuklib_mask_nonprint_r(str, &mem);
+}
diff --git a/cbits/common/tuklib_mbstr_nonprint.h b/cbits/common/tuklib_mbstr_nonprint.h
new file mode 100644
--- /dev/null
+++ b/cbits/common/tuklib_mbstr_nonprint.h
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: 0BSD
+
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       tuklib_mbstr_nonprint.h
+/// \brief      Find and replace non-printable characters with question marks
+///
+/// If mbrtowc(3) is available, it and iswprint(3) is used to check if all
+/// characters are printable. Otherwise single-byte character set is assumed
+/// and isprint(3) is used.
+//
+//  Author:     Lasse Collin
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef TUKLIB_MBSTR_NONPRINT_H
+#define TUKLIB_MBSTR_NONPRINT_H
+
+#include "tuklib_common.h"
+TUKLIB_DECLS_BEGIN
+
+#define tuklib_has_nonprint TUKLIB_SYMBOL(tuklib_has_nonprint)
+extern bool tuklib_has_nonprint(const char *str);
+///<
+/// \brief      Check if a string contains any non-printable characters
+///
+/// \return     false if str contains only valid multibyte characters and
+///             iswprint(3) returns non-zero for all of them; true otherwise.
+///             The value of errno is preserved.
+///
+/// \note       In case mbrtowc(3) isn't available, single-byte character set
+///             is assumed and isprint(3) is used instead of iswprint(3).
+
+#define tuklib_mask_nonprint_r TUKLIB_SYMBOL(tuklib_mask_nonprint_r)
+extern const char *tuklib_mask_nonprint_r(const char *str, char **mem);
+///<
+/// \brief      Replace non-printable characters with question marks
+///
+/// \param      str     Untrusted string, for example, a filename
+/// \param      mem     This function always calls free(*mem) to free the old
+///                     allocation and then sets *mem = NULL. Before the first
+///                     call, *mem should be initialized to NULL. If this
+///                     function needs to allocate memory for a modified
+///                     string, a pointer to the allocated memory will be
+///                     stored to *mem. Otherwise *mem will remain NULL.
+///
+/// \return     If tuklib_has_nonprint(str) returns false, this function
+///             returns str. Otherwise memory is allocated to hold a modified
+///             string and a pointer to that is returned. The pointer to the
+///             allocated memory is also stored to *mem. A modified string
+///             has the problematic characters replaced by '?'. If memory
+///             allocation fails, "???" is returned and *mem is NULL.
+///             The value of errno is preserved.
+
+#define tuklib_mask_nonprint TUKLIB_SYMBOL(tuklib_mask_nonprint)
+extern const char *tuklib_mask_nonprint(const char *str);
+///<
+/// \brief      Replace non-printable characters with question marks
+///
+/// This is a convenience function for single-threaded use. This calls
+/// tuklib_mask_nonprint_r() using an internal static variable to hold
+/// the possible allocation.
+///
+/// \param      str     Untrusted string, for example, a filename
+///
+/// \return     See tuklib_mask_nonprint_r().
+///
+/// \note       This function is not thread safe!
+
+TUKLIB_DECLS_END
+#endif
diff --git a/cbits/common/tuklib_mbstr_width.c b/cbits/common/tuklib_mbstr_width.c
--- a/cbits/common/tuklib_mbstr_width.c
+++ b/cbits/common/tuklib_mbstr_width.c
@@ -24,9 +24,17 @@
 	if (bytes != NULL)
 		*bytes = len;
 
+	return tuklib_mbstr_width_mem(str, len);
+}
+
+
+extern size_t
+tuklib_mbstr_width_mem(const char *str, size_t len)
+{
 #ifndef HAVE_MBRTOWC
 	// In single-byte mode, the width of the string is the same
 	// as its length.
+	(void)str;
 	return len;
 
 #else
@@ -41,7 +49,7 @@
 	while (i < len) {
 		wchar_t wc;
 		const size_t ret = mbrtowc(&wc, str + i, len - i, &state);
-		if (ret < 1 || ret > len)
+		if (ret < 1 || ret > len - i)
 			return (size_t)-1;
 
 		i += ret;
@@ -62,9 +70,14 @@
 #endif
 	}
 
-	// Require that the string ends in the initial shift state.
-	// This way the caller can be combine the string with other
-	// strings without needing to worry about the shift states.
+	// It's good to check that the string ended in the initial state.
+	// However, in practice this is redundant:
+	//
+	//   - No one will use this code with character sets that have
+	//     locking shift states.
+	//
+	//   - We already checked that mbrtowc() didn't return (size_t)-2
+	//     which would indicate a partial multibyte character.
 	if (!mbsinit(&state))
 		return (size_t)-1;
 
diff --git a/cbits/common/tuklib_mbstr_wrap.c b/cbits/common/tuklib_mbstr_wrap.c
new file mode 100644
--- /dev/null
+++ b/cbits/common/tuklib_mbstr_wrap.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: 0BSD
+
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       tuklib_mbstr_wrap.c
+/// \brief      Word wraps a string and prints it to a FILE stream
+///
+/// This depends on tuklib_mbstr_width.c.
+//
+//  Author:     Lasse Collin
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "tuklib_mbstr.h"
+#include "tuklib_mbstr_wrap.h"
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+extern int
+tuklib_wraps(FILE *outfile, const struct tuklib_wrap_opt *opt, const char *str)
+{
+	// left_cont may be less than left_margin. In that case, if the first
+	// word is extremely long, it will stay on the first line even if
+	// the line then gets overlong.
+	//
+	// On the other hand, left2_cont < left2_margin isn't allowed because
+	// it could result in inconsistent behavior when a very long word
+	// comes right after a \v.
+	//
+	// It is fine to have left2_margin < left_margin although it would be
+	// an odd use case.
+	if (!(opt->left_margin < opt->right_margin
+			&& opt->left_cont < opt->right_margin
+			&& opt->left2_margin <= opt->left2_cont
+			&& opt->left2_cont < opt->right_margin))
+		return TUKLIB_WRAP_ERR_OPT;
+
+	// This is set to TUKLIB_WRAP_WARN_OVERLONG if one or more
+	// output lines extend past opt->right_margin columns.
+	int warn_overlong = 0;
+
+	// Indentation of the first output line after \n or \r.
+	// \v sets this to opt->left2_margin.
+	// \r resets this back to the original value.
+	size_t first_indent = opt->left_margin;
+
+	// Indentation of the output lines that occur due to word wrapping.
+	// \v sets this to opt->left2_cont and \r back to the original value.
+	size_t cont_indent = opt->left_cont;
+
+	// If word wrapping occurs, the newline isn't printed unless more
+	// text would be put on the continuation line. This is also used
+	// when \v needs to start on a new line.
+	bool pending_newline = false;
+
+	// Spaces are printed only when there is something else to put
+	// after the spaces on the line. This avoids unwanted empty lines
+	// in the output and makes it possible to ignore possible spaces
+	// before a \v character.
+	size_t pending_spaces = first_indent;
+
+	// Current output column. When cur_col == pending_spaces, nothing
+	// has been actually printed to the current output line.
+	size_t cur_col = pending_spaces;
+
+	while (true) {
+		// Number of bytes until the *next* line-break opportunity.
+		size_t len = 0;
+
+		// Number of columns until the *next* line-break opportunity.
+		size_t width = 0;
+
+		// Text between a pair of \b characters is treated as
+		// an unbreakable block even if it contains spaces.
+		// It must not contain any control characters before
+		// the closing \b.
+		bool unbreakable = false;
+
+		while (true) {
+			// Find the next character that we handle specially.
+			// In an unbreakable block, search only for the
+			// closing \b; if missing, the unbreakable block
+			// extends to the end of the string.
+			const size_t n = strcspn(str + len,
+					unbreakable ? "\b" : " \t\n\r\v\b");
+
+			// Calculate how many columns the characters need.
+			const size_t w = tuklib_mbstr_width_mem(str + len, n);
+			if (w == (size_t)-1)
+				return TUKLIB_WRAP_ERR_STR;
+
+			width += w;
+			len += n;
+
+			// \b isn't a line-break opportunity so it has to
+			// be handled here. For simplicity, empty blocks
+			// are treated as zero-width characters.
+			if (str[len] == '\b') {
+				++len;
+				unbreakable = !unbreakable;
+				continue;
+			}
+
+			break;
+		}
+
+		// Determine if adding this chunk of text would make the
+		// current output line exceed opt->right_margin columns.
+		const bool too_long = cur_col + width > opt->right_margin;
+
+		// Wrap the line if needed. However:
+		//
+		//   - Don't wrap if the current column is less than where
+		//     the continuation line would begin. In that case
+		//     the chunk wouldn't fit on the next line either so
+		//     we just have to produce an overlong line.
+		//
+		//   - Don't wrap if so far the line only contains spaces.
+		//     Wrapping in that case would leave a weird empty line.
+		//     NOTE: This "only contains spaces" condition is the
+		//     reason why left2_margin > left2_cont isn't allowed.
+		if (too_long && cur_col > cont_indent
+				&& cur_col > pending_spaces) {
+			// There might be trailing spaces or zero-width spaces
+			// which need to be ignored to keep the output pretty.
+			//
+			// Spaces need to be ignored because in some
+			// writing styles there are two spaces after
+			// a full stop. Example string:
+			//
+			//     "Foo bar.  Abc def."
+			//              ^
+			// If the first space after the first full stop
+			// triggers word wrapping, both spaces must be
+			// ignored. Otherwise the next line would be
+			// indented too much.
+			//
+			// Zero-width spaces are ignored the same way
+			// because they are meaningless if an adjacent
+			// character is a space.
+			while (*str == ' ' || *str == '\t')
+				++str;
+
+			// Don't print the newline here; only mark it as
+			// pending. This avoids an unwanted empty line if
+			// there is a \n or \r or \0 after the spaces have
+			// been ignored.
+			pending_newline = true;
+			pending_spaces = cont_indent;
+			cur_col = pending_spaces;
+
+			// Since str may have been incremented due to the
+			// ignored spaces, the loop needs to be restarted.
+			continue;
+		}
+
+		// Print the current chunk of text before the next
+		// line-break opportunity. If the chunk was empty,
+		// don't print anything so that the pending newline
+		// and pending spaces aren't printed on their own.
+		if (len > 0) {
+			if (pending_newline) {
+				pending_newline = false;
+				if (putc('\n', outfile) == EOF)
+					return TUKLIB_WRAP_ERR_IO;
+			}
+
+			while (pending_spaces > 0) {
+				if (putc(' ', outfile) == EOF)
+					return TUKLIB_WRAP_ERR_IO;
+
+				--pending_spaces;
+			}
+
+			for (size_t i = 0; i < len; ++i) {
+				// Ignore unbreakable block characters (\b).
+				const int c = (unsigned char)str[i];
+				if (c != '\b' && putc(c, outfile) == EOF)
+					return TUKLIB_WRAP_ERR_IO;
+			}
+
+			str += len;
+			cur_col += width;
+
+			// Remember if the line got overlong. If no other
+			// errors occur, we return warn_overlong. It might
+			// help in catching problematic strings.
+			if (too_long)
+				warn_overlong = TUKLIB_WRAP_WARN_OVERLONG;
+		}
+
+		// Handle the special character after the chunk of text.
+		switch (*str) {
+		case ' ':
+			// Regular space.
+			++cur_col;
+			++pending_spaces;
+			break;
+
+		case '\v':
+			// Set the alternative indentation settings.
+			first_indent = opt->left2_margin;
+			cont_indent = opt->left2_cont;
+
+			if (first_indent > cur_col) {
+				// Add one or more spaces to reach
+				// the column specified in first_indent.
+				pending_spaces += first_indent - cur_col;
+			} else {
+				// There is no room to add even one space
+				// before reaching the column first_indent.
+				pending_newline = true;
+				pending_spaces = first_indent;
+			}
+
+			cur_col = first_indent;
+			break;
+
+		case '\0': // Implicit newline at the end of the string.
+		case '\r': // Newline that also resets the effect of \v.
+		case '\n': // Newline without resetting the indentation mode.
+			if (putc('\n', outfile) == EOF)
+				return TUKLIB_WRAP_ERR_IO;
+
+			if (*str == '\0')
+				return warn_overlong;
+
+			if (*str == '\r') {
+				first_indent = opt->left_margin;
+				cont_indent = opt->left_cont;
+			}
+
+			pending_newline = false;
+			pending_spaces = first_indent;
+			cur_col = first_indent;
+			break;
+		}
+
+		// Skip the specially-handled character.
+		++str;
+	}
+}
+
+
+extern int
+tuklib_wrapf(FILE *stream, const struct tuklib_wrap_opt *opt,
+		const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+
+#ifdef HAVE_VASPRINTF
+	va_start(ap, fmt);
+
+#ifdef __clang__
+#	pragma GCC diagnostic push
+#	pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#endif
+	const int n = vasprintf(&buf, fmt, ap);
+#ifdef __clang__
+#	pragma GCC diagnostic pop
+#endif
+
+	va_end(ap);
+	if (n == -1)
+		return TUKLIB_WRAP_ERR_FORMAT;
+#else
+	// Fixed buffer size is dumb but in practice one shouldn't need
+	// huge strings for *formatted* output. This simple method is safe
+	// with pre-C99 vsnprintf() implementations too which don't return
+	// the required buffer size (they return -1 or buf_size - 1) or
+	// which might not null-terminate the buffer in case it's too small.
+	const size_t buf_size = 128 * 1024;
+	buf = malloc(buf_size);
+	if (buf == NULL)
+		return TUKLIB_WRAP_ERR_FORMAT;
+
+	va_start(ap, fmt);
+	const int n = vsnprintf(buf, buf_size, fmt, ap);
+	va_end(ap);
+
+	if (n <= 0 || n >= (int)(buf_size - 1)) {
+		free(buf);
+		return TUKLIB_WRAP_ERR_FORMAT;
+	}
+#endif
+
+	const int ret = tuklib_wraps(stream, opt, buf);
+	free(buf);
+	return ret;
+}
diff --git a/cbits/common/tuklib_mbstr_wrap.h b/cbits/common/tuklib_mbstr_wrap.h
new file mode 100644
--- /dev/null
+++ b/cbits/common/tuklib_mbstr_wrap.h
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: 0BSD
+
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       tuklib_mbstr_wrap.h
+/// \brief      Word wrapping for multibyte strings
+///
+/// The word wrapping functions are intended to be usable, for example,
+/// for printing --help text in command line tools. While manually-wrapped
+/// --help text allows precise formatting, such freedom requires translators
+/// to count spaces and determine where line breaks should occur. It's
+/// tedious and error prone, and experience has shown that only some
+/// translators do it well. Automatic word wrapping is less flexible but
+/// results in polished-enough look with less effort from everyone.
+/// Right-to-left languages and languages that don't use spaces between
+/// words will still need extra effort though.
+//
+//  Author:     Lasse Collin
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef TUKLIB_MBSTR_WRAP_H
+#define TUKLIB_MBSTR_WRAP_H
+
+#include "tuklib_common.h"
+#include <stdio.h>
+
+TUKLIB_DECLS_BEGIN
+
+/// One or more output lines exceeded right_margin.
+/// This only a warning; everything was still printed successfully.
+#define TUKLIB_WRAP_WARN_OVERLONG   0x01
+
+/// Error writing to to the output FILE. The error flag in the FILE
+/// should have been set as well.
+#define TUKLIB_WRAP_ERR_IO          0x02
+
+/// Invalid options in struct tuklib_wrap_opt.
+/// Nothing was printed.
+#define TUKLIB_WRAP_ERR_OPT         0x04
+
+/// Invalid or unsupported multibyte character in the input string:
+/// either mbrtowc() failed or wcwidth() returned a negative value.
+#define TUKLIB_WRAP_ERR_STR         0x08
+
+/// Only tuklib_wrapf(): Error in converting the format string.
+/// It's either a memory allocation failure or something bad with the
+/// format string or arguments.
+#define TUKLIB_WRAP_ERR_FORMAT      0x10
+
+/// Options for tuklib_wraps() and tuklib_wrapf()
+struct tuklib_wrap_opt {
+	/// Indentation of the first output line after `\n` or `\r`.
+	/// This can be anything less than right_margin.
+	unsigned short left_margin;
+
+	/// Column where word-wrapped continuation lines start.
+	/// This can be anything less than right_margin.
+	unsigned short left_cont;
+
+	/// Column where the text after `\v` will start, either on the current
+	/// line (when there is room to add at least one space) or on a new
+	/// empty line.
+	unsigned short left2_margin;
+
+	/// Like left_cont but for text after a `\v`. However, this must
+	/// be greater than or equal to left2_margin in addition to being
+	/// less than right_margin.
+	unsigned short left2_cont;
+
+	/// For 80-column terminals, it is recommended to use 79 here for
+	/// maximum portability. 80 will work most of the time but it will
+	/// result in unwanted empty lines in the rare case where a terminal
+	/// moves the cursor to the beginning of the next line immediately
+	/// when the last column has been used.
+	unsigned short right_margin;
+};
+
+#define tuklib_wraps TUKLIB_SYMBOL(tuklib_wraps)
+extern int tuklib_wraps(FILE *stream, const struct tuklib_wrap_opt *opt,
+		const char *str);
+///<
+/// \brief      Word wrap a multibyte string and write it to a FILE
+///
+/// Word wrapping is done only at spaces and at the special control characters
+/// described below. Multiple consecutive spaces are handled properly: strings
+/// that have two (or more) spaces after a full sentence will look good even
+/// when the spaces occur at a word wrapping boundary. Trailing spaces are
+/// ignored at the end of a line or at the end of a string.
+///
+/// The following control characters have been repurposed:
+///
+///   - `\t` = Zero-width space allows a line break without producing any
+///            output by itself. This can be useful after hard hyphens as
+///            hyphens aren't otherwise used for line breaking. This can also
+///            be useful in languages that don't use spaces between words.
+///            (The Unicode character U+200B isn't supported.)
+///   - `\b` = Text between a pair of `\b` characters is treated as an
+///            unbreakable block (not wrapped even if there are spaces).
+///            For example, a non-breaking space can be done like
+///            in `"123\b \bMiB"`. Control characters (like `\n` or `\t`)
+///            aren't allowed before the closing `\b`. If closing `\b` is
+///            missing, the block extends to the end of the string. Empty
+///            blocks are treated as zero-width characters. If line breaks
+///            are possible around an empty block (like in `"foo \b\b bar"`
+///            or `"foo \b"`), it can result in weird output.
+///   - `\v` = Change to alternative indentation (left2_margin).
+///   - `\r` = Reset back to the initial indentation and add a newline.
+///            The next line will be indented by left_margin.
+///   - `\n` = Add a newline without resetting the effect of `\v`. The
+///            next line will be indented by left_margin or left2_margin
+///            (not left_cont or left2_cont).
+///
+/// Only `\n` should appear in translatable strings. `\t` works too but
+/// even that might confuse some translators even if there is a TRANSLATORS
+/// comment explaining its meaning.
+///
+/// To use the other control characters in messages, one should use
+/// tuklib_wrapf() with appropriate printf format string to combine
+/// translatable strings with non-translatable portions. For example:
+///
+/// \code{.c}
+/// static const struct tuklib_wrap_opt wrap2 = { 2,  2, 22, 22, 79 };
+/// int e = 0;
+/// ...
+/// e |= tuklib_wrapf(stdout, &wrap2,
+///                   "-h, --help\v%s\r"
+///                   "    --version\v%s",
+///                   W_("display this help and exit"),
+///                   W_("display version information and exit"));
+/// ...
+/// if (e != 0) {
+///     // Handle warning or error.
+///     ...
+/// }
+/// \endcode
+///
+/// Control characters other than `\n` and `\t` are unusable in
+/// translatable strings:
+///
+///   - Gettext tools show annoying warnings if C escape sequences other
+///     than `\n` or `\t` are seen. (Otherwise they still work perfectly
+///     fine though.)
+///
+///   - While at least Poedit and Lokalize support all escapes, some
+///     editors only support `\n` and `\t`.
+///
+///   - They could confuse some translators, resulting in broken
+///     translations.
+///
+/// Using non-control characters would solve some issues but it wouldn't
+/// help with the unfortunate real-world issue that some translators would
+/// likely have trouble understanding a new syntax. The Gettext manual
+/// specifically warns about this, see the subheading "No unusual markup"
+/// in `info (gettext)Preparing Strings`. (While using `\t` for zero-width
+/// space is such custom markup, most translators will never need it.)
+///
+/// Translators can use the Unicode character U+00A0 (or U+202F) if they
+/// need a non-breaking space. For example, in French a non-breaking space
+/// may be needed before colons and question marks (U+00A0 is common in
+/// real-world French PO files).
+///
+/// Using a non-ASCII char in a string in the C code (like `"123\u00A0MiB"`)
+/// can work if one tells xgettext that input encoding is UTF-8, one
+/// ensures that the C compiler uses UTF-8 as the input charset, and one
+/// is certain that the program is *always* run under an UTF-8 locale.
+/// Unfortunately a portable program cannot make this kind of assumptions,
+/// which means that there is no pretty way to have a non-breaking space in
+/// a translatable string.
+///
+/// Optional: To tell translators which strings are automatically word
+/// wrapped, see the macro `W_` in tuklib_gettext.h.
+///
+/// \param      stream      Output FILE stream. For decent performance, it
+///                         should be in buffered mode because this function
+///                         writes the output one byte at a time with fputc().
+/// \param      opt         Word wrapping options.
+/// \param      str         Null-terminated multibyte string that is in
+///                         the encoding used by the current locale.
+///
+/// \return     Returns 0 on success. If an error or warning occurs, one of
+///             TUKLIB_WRAP_* codes is returned. Those codes are powers
+///             of two. When warning/error detection can be delayed, the
+///             return values can be accumulated from multiple calls using
+///             bitwise-or into a single variable which can be checked after
+///             all strings have (hopefully) been printed.
+
+#define tuklib_wrapf TUKLIB_SYMBOL(tuklib_wrapf)
+tuklib_attr_format_printf(3, 4)
+extern int tuklib_wrapf(FILE *stream, const struct tuklib_wrap_opt *opt,
+		const char *fmt, ...);
+///<
+/// \brief      Format and word-wrap a multibyte string and write it to a FILE
+///
+/// This is like tuklib_wraps() except that this takes a printf
+/// format string.
+///
+/// \note       On platforms that lack vasprintf(), the intermediate
+///             result from vsnprintf() must fit into a 128 KiB buffer.
+///             TUKLIB_WRAP_ERR_FORMAT is returned if it doesn't but
+///             only on platforms that lack vasprintf().
+
+TUKLIB_DECLS_END
+#endif
diff --git a/cbits/common/tuklib_physmem.c b/cbits/common/tuklib_physmem.c
--- a/cbits/common/tuklib_physmem.c
+++ b/cbits/common/tuklib_physmem.c
@@ -91,18 +91,11 @@
 		// supports reporting values greater than 4 GiB. To keep the
 		// code working also on older Windows versions, use
 		// GlobalMemoryStatusEx() conditionally.
-		HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+		HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
 		if (kernel32 != NULL) {
 			typedef BOOL (WINAPI *gmse_type)(LPMEMORYSTATUSEX);
-#ifdef CAN_DISABLE_WCAST_FUNCTION_TYPE
-#	pragma GCC diagnostic push
-#	pragma GCC diagnostic ignored "-Wcast-function-type"
-#endif
 			gmse_type gmse = (gmse_type)GetProcAddress(
 					kernel32, "GlobalMemoryStatusEx");
-#ifdef CAN_DISABLE_WCAST_FUNCTION_TYPE
-#	pragma GCC diagnostic pop
-#endif
 			if (gmse != NULL) {
 				MEMORYSTATUSEX meminfo;
 				meminfo.dwLength = sizeof(meminfo);
@@ -155,7 +148,7 @@
 			ret += entries[i].end - entries[i].start + 1;
 
 #elif defined(TUKLIB_PHYSMEM_AIX)
-	ret = _system_configuration.physmem;
+	ret = (uint64_t)_system_configuration.physmem;
 
 #elif defined(TUKLIB_PHYSMEM_SYSCONF)
 	const long pagesize = sysconf(_SC_PAGESIZE);
diff --git a/cbits/config.h.in b/cbits/config.h.in
--- a/cbits/config.h.in
+++ b/cbits/config.h.in
@@ -330,6 +330,9 @@
 /* Define to 1 if you have the 'utimes' function. */
 #undef HAVE_UTIMES
 
+/* Define to 1 if you have the 'vasprintf' function. */
+#undef HAVE_VASPRINTF
+
 /* Define to 1 or 0, depending whether the compiler supports simple visibility
    declarations. */
 #undef HAVE_VISIBILITY
diff --git a/cbits/liblzma/api/lzma/bcj.h b/cbits/liblzma/api/lzma/bcj.h
--- a/cbits/liblzma/api/lzma/bcj.h
+++ b/cbits/liblzma/api/lzma/bcj.h
@@ -96,3 +96,100 @@
 	uint32_t start_offset;
 
 } lzma_options_bcj;
+
+
+/**
+ * \brief       Raw ARM64 BCJ encoder
+ *
+ * This is for special use cases only.
+ *
+ * \param       start_offset  The lowest 32 bits of the offset in the
+ *                            executable being filtered. For the ARM64
+ *                            filter, this must be a multiple of four.
+ *                            For the very best results, this should also
+ *                            be in sync with 4096-byte page boundaries
+ *                            in the executable due to how ARM64's ADRP
+ *                            instruction works.
+ * \param       buf           Buffer to be filtered in place
+ * \param       size          Size of the buffer
+ *
+ * \return      Number of bytes that were processed in `buf`. This is at most
+ *              `size`. With the ARM64 filter, the return value is always
+ *              a multiple of 4, and at most 3 bytes are left unfiltered.
+ *
+ * \since       5.7.1alpha
+ */
+extern LZMA_API(size_t) lzma_bcj_arm64_encode(
+		uint32_t start_offset, uint8_t *buf, size_t size) lzma_nothrow;
+
+/**
+ * \brief       Raw ARM64 BCJ decoder
+ *
+ * See lzma_bcj_arm64_encode().
+ *
+ * \since       5.7.1alpha
+ */
+extern LZMA_API(size_t) lzma_bcj_arm64_decode(
+		uint32_t start_offset, uint8_t *buf, size_t size) lzma_nothrow;
+
+
+/**
+ * \brief       Raw RISC-V BCJ encoder
+ *
+ * This is for special use cases only.
+ *
+ * \param       start_offset  The lowest 32 bits of the offset in the
+ *                            executable being filtered. For the RISC-V
+ *                            filter, this must be a multiple of 2.
+ * \param       buf           Buffer to be filtered in place
+ * \param       size          Size of the buffer
+ *
+ * \return      Number of bytes that were processed in `buf`. This is at most
+ *              `size`. With the RISC-V filter, the return value is always
+ *              a multiple of 2, and at most 7 bytes are left unfiltered.
+ *
+ * \since       5.7.1alpha
+ */
+extern LZMA_API(size_t) lzma_bcj_riscv_encode(
+		uint32_t start_offset, uint8_t *buf, size_t size) lzma_nothrow;
+
+/**
+ * \brief       Raw RISC-V BCJ decoder
+ *
+ * See lzma_bcj_riscv_encode().
+ *
+ * \since       5.7.1alpha
+ */
+extern LZMA_API(size_t) lzma_bcj_riscv_decode(
+		uint32_t start_offset, uint8_t *buf, size_t size) lzma_nothrow;
+
+
+/**
+ * \brief       Raw x86 BCJ encoder
+ *
+ * This is for special use cases only.
+ *
+ * \param       start_offset  The lowest 32 bits of the offset in the
+ *                            executable being filtered. For the x86
+ *                            filter, all values are valid.
+ * \param       buf           Buffer to be filtered in place
+ * \param       size          Size of the buffer
+ *
+ * \return      Number of bytes that were processed in `buf`. This is at most
+ *              `size`. For the x86 filter, the return value is always
+ *              a multiple of 1, and at most 4 bytes are left unfiltered.
+ *
+ * \since       5.7.1alpha
+ */
+extern LZMA_API(size_t) lzma_bcj_x86_encode(
+		uint32_t start_offset, uint8_t *buf, size_t size) lzma_nothrow;
+
+/**
+ * \brief       Raw x86 BCJ decoder
+ *
+ * See lzma_bcj_x86_encode().
+ *
+ * \since       5.7.1alpha
+ */
+extern LZMA_API(size_t) lzma_bcj_x86_decode(
+		uint32_t start_offset, uint8_t *buf, size_t size) lzma_nothrow;
diff --git a/cbits/liblzma/api/lzma/container.h b/cbits/liblzma/api/lzma/container.h
--- a/cbits/liblzma/api/lzma/container.h
+++ b/cbits/liblzma/api/lzma/container.h
@@ -573,7 +573,7 @@
  * The action argument must be LZMA_FINISH and the return value will never be
  * LZMA_OK. Thus the encoding is always done with a single lzma_code() after
  * the initialization. The benefit of the combination of initialization
- * function and lzma_code() is that memory allocations can be re-used for
+ * function and lzma_code() is that memory allocations can be reused for
  * better performance.
  *
  * lzma_code() will try to encode as much input as is possible to fit into
diff --git a/cbits/liblzma/api/lzma/version.h b/cbits/liblzma/api/lzma/version.h
--- a/cbits/liblzma/api/lzma/version.h
+++ b/cbits/liblzma/api/lzma/version.h
@@ -19,10 +19,10 @@
 #define LZMA_VERSION_MAJOR 5
 
 /** \brief Minor version number of the liblzma release. */
-#define LZMA_VERSION_MINOR 6
+#define LZMA_VERSION_MINOR 8
 
 /** \brief Patch version number of the liblzma release. */
-#define LZMA_VERSION_PATCH 4
+#define LZMA_VERSION_PATCH 0
 
 /**
  * \brief Version stability marker
diff --git a/cbits/liblzma/check/check.h b/cbits/liblzma/check/check.h
--- a/cbits/liblzma/check/check.h
+++ b/cbits/liblzma/check/check.h
@@ -95,24 +95,6 @@
 } lzma_check_state;
 
 
-/// lzma_crc32_table[0] is needed by LZ encoder so we need to keep
-/// the array two-dimensional.
-#ifdef HAVE_SMALL
-lzma_attr_visibility_hidden
-extern uint32_t lzma_crc32_table[1][256];
-
-extern void lzma_crc32_init(void);
-
-#else
-
-lzma_attr_visibility_hidden
-extern const uint32_t lzma_crc32_table[8][256];
-
-lzma_attr_visibility_hidden
-extern const uint64_t lzma_crc64_table[4][256];
-#endif
-
-
 /// \brief      Initialize *check depending on type
 extern void lzma_check_init(lzma_check_state *check, lzma_check type);
 
diff --git a/cbits/liblzma/check/crc32_arm64.h b/cbits/liblzma/check/crc32_arm64.h
--- a/cbits/liblzma/check/crc32_arm64.h
+++ b/cbits/liblzma/check/crc32_arm64.h
@@ -7,7 +7,7 @@
 //
 //  Authors:    Chenxi Mao
 //              Jia Tan
-//              Hans Jansen
+//              Lasse Collin
 //
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -49,25 +49,50 @@
 {
 	crc = ~crc;
 
-	// Align the input buffer because this was shown to be
-	// significantly faster than unaligned accesses.
-	const size_t align_amount = my_min(size, (0U - (uintptr_t)buf) & 7);
+	if (size >= 8) {
+		// Align the input buffer because this was shown to be
+		// significantly faster than unaligned accesses.
+		const size_t align = (0 - (uintptr_t)buf) & 7;
 
-	for (const uint8_t *limit = buf + align_amount; buf < limit; ++buf)
-		crc = __crc32b(crc, *buf);
+		if (align & 1)
+			crc = __crc32b(crc, *buf++);
 
-	size -= align_amount;
+		if (align & 2) {
+			crc = __crc32h(crc, aligned_read16le(buf));
+			buf += 2;
+		}
 
-	// Process 8 bytes at a time. The end point is determined by
-	// ignoring the least significant three bits of size to ensure
-	// we do not process past the bounds of the buffer. This guarantees
-	// that limit is a multiple of 8 and is strictly less than size.
-	for (const uint8_t *limit = buf + (size & ~(size_t)7);
-			buf < limit; buf += 8)
-		crc = __crc32d(crc, aligned_read64le(buf));
+		if (align & 4) {
+			crc = __crc32w(crc, aligned_read32le(buf));
+			buf += 4;
+		}
 
+		size -= align;
+
+		// Process 8 bytes at a time. The end point is determined by
+		// ignoring the least significant three bits of size to
+		// ensure we do not process past the bounds of the buffer.
+		// This guarantees that limit is a multiple of 8 and is
+		// strictly less than size.
+		for (const uint8_t *limit = buf + (size & ~(size_t)7);
+				buf < limit; buf += 8)
+			crc = __crc32d(crc, aligned_read64le(buf));
+
+		size &= 7;
+	}
+
 	// Process the remaining bytes that are not 8 byte aligned.
-	for (const uint8_t *limit = buf + (size & 7); buf < limit; ++buf)
+	if (size & 4) {
+		crc = __crc32w(crc, aligned_read32le(buf));
+		buf += 4;
+	}
+
+	if (size & 2) {
+		crc = __crc32h(crc, aligned_read16le(buf));
+		buf += 2;
+	}
+
+	if (size & 1)
 		crc = __crc32b(crc, *buf);
 
 	return ~crc;
diff --git a/cbits/liblzma/check/crc32_fast.c b/cbits/liblzma/check/crc32_fast.c
--- a/cbits/liblzma/check/crc32_fast.c
+++ b/cbits/liblzma/check/crc32_fast.c
@@ -7,7 +7,6 @@
 //
 //  Authors:    Lasse Collin
 //              Ilya Kurdyukov
-//              Hans Jansen
 //
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -15,10 +14,12 @@
 #include "crc_common.h"
 
 #if defined(CRC_X86_CLMUL)
-#	define BUILDING_CRC32_CLMUL
+#	define BUILDING_CRC_CLMUL 32
 #	include "crc_x86_clmul.h"
 #elif defined(CRC32_ARM64)
 #	include "crc32_arm64.h"
+#elif defined(CRC32_LOONGARCH)
+#	include "crc32_loongarch.h"
 #endif
 
 
@@ -28,8 +29,19 @@
 // Generic CRC32 //
 ///////////////////
 
+#ifdef WORDS_BIGENDIAN
+#	include "crc32_table_be.h"
+#else
+#	include "crc32_table_le.h"
+#endif
+
+
+#ifdef HAVE_CRC_X86_ASM
+extern uint32_t lzma_crc32_generic(
+		const uint8_t *buf, size_t size, uint32_t crc);
+#else
 static uint32_t
-crc32_generic(const uint8_t *buf, size_t size, uint32_t crc)
+lzma_crc32_generic(const uint8_t *buf, size_t size, uint32_t crc)
 {
 	crc = ~crc;
 
@@ -85,7 +97,8 @@
 
 	return ~crc;
 }
-#endif
+#endif // HAVE_CRC_X86_ASM
+#endif // CRC32_GENERIC
 
 
 #if defined(CRC32_GENERIC) && defined(CRC32_ARCH_OPTIMIZED)
@@ -119,7 +132,7 @@
 crc32_resolve(void)
 {
 	return is_arch_extension_supported()
-			? &crc32_arch_optimized : &crc32_generic;
+			? &crc32_arch_optimized : &lzma_crc32_generic;
 }
 
 
@@ -164,27 +177,6 @@
 lzma_crc32(const uint8_t *buf, size_t size, uint32_t crc)
 {
 #if defined(CRC32_GENERIC) && defined(CRC32_ARCH_OPTIMIZED)
-	// On x86-64, if CLMUL is available, it is the best for non-tiny
-	// inputs, being over twice as fast as the generic slice-by-four
-	// version. However, for size <= 16 it's different. In the extreme
-	// case of size == 1 the generic version can be five times faster.
-	// At size >= 8 the CLMUL starts to become reasonable. It
-	// varies depending on the alignment of buf too.
-	//
-	// The above doesn't include the overhead of mythread_once().
-	// At least on x86-64 GNU/Linux, pthread_once() is very fast but
-	// it still makes lzma_crc32(buf, 1, crc) 50-100 % slower. When
-	// size reaches 12-16 bytes the overhead becomes negligible.
-	//
-	// So using the generic version for size <= 16 may give better
-	// performance with tiny inputs but if such inputs happen rarely
-	// it's not so obvious because then the lookup table of the
-	// generic version may not be in the processor cache.
-#ifdef CRC_USE_GENERIC_FOR_SMALL_INPUTS
-	if (size <= 16)
-		return crc32_generic(buf, size, crc);
-#endif
-
 /*
 #ifndef HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR
 	// See crc32_dispatch(). This would be the alternative which uses
@@ -199,6 +191,6 @@
 	return crc32_arch_optimized(buf, size, crc);
 
 #else
-	return crc32_generic(buf, size, crc);
+	return lzma_crc32_generic(buf, size, crc);
 #endif
 }
diff --git a/cbits/liblzma/check/crc32_loongarch.h b/cbits/liblzma/check/crc32_loongarch.h
new file mode 100644
--- /dev/null
+++ b/cbits/liblzma/check/crc32_loongarch.h
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: 0BSD
+
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       crc32_loongarch.h
+/// \brief      CRC32 calculation with LoongArch optimization
+//
+//  Authors:    Xi Ruoyao
+//              Lasse Collin
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef LZMA_CRC32_LOONGARCH_H
+#define LZMA_CRC32_LOONGARCH_H
+
+#include <larchintrin.h>
+
+
+static uint32_t
+crc32_arch_optimized(const uint8_t *buf, size_t size, uint32_t crc_unsigned)
+{
+	int32_t crc = (int32_t)~crc_unsigned;
+
+	if (size >= 8) {
+		const size_t align = (0 - (uintptr_t)buf) & 7;
+
+		if (align & 1)
+			crc = __crc_w_b_w((int8_t)*buf++, crc);
+
+		if (align & 2) {
+			crc = __crc_w_h_w((int16_t)aligned_read16le(buf), crc);
+			buf += 2;
+		}
+
+		if (align & 4) {
+			crc = __crc_w_w_w((int32_t)aligned_read32le(buf), crc);
+			buf += 4;
+		}
+
+		size -= align;
+
+		for (const uint8_t *limit = buf + (size & ~(size_t)7);
+				buf < limit; buf += 8)
+			crc = __crc_w_d_w((int64_t)aligned_read64le(buf), crc);
+
+		size &= 7;
+	}
+
+	if (size & 4) {
+		crc = __crc_w_w_w((int32_t)aligned_read32le(buf), crc);
+		buf += 4;
+	}
+
+	if (size & 2) {
+		crc = __crc_w_h_w((int16_t)aligned_read16le(buf), crc);
+		buf += 2;
+	}
+
+	if (size & 1)
+		crc = __crc_w_b_w((int8_t)*buf, crc);
+
+	return (uint32_t)~crc;
+}
+
+#endif // LZMA_CRC32_LOONGARCH_H
diff --git a/cbits/liblzma/check/crc32_small.c b/cbits/liblzma/check/crc32_small.c
--- a/cbits/liblzma/check/crc32_small.c
+++ b/cbits/liblzma/check/crc32_small.c
@@ -10,8 +10,11 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "check.h"
+#include "crc_common.h"
 
 
+// The table is used by the LZ encoder too, thus it's not static like
+// in crc64_small.c.
 uint32_t lzma_crc32_table[1][256];
 
 
diff --git a/cbits/liblzma/check/crc32_table.c b/cbits/liblzma/check/crc32_table.c
deleted file mode 100644
--- a/cbits/liblzma/check/crc32_table.c
+++ /dev/null
@@ -1,42 +0,0 @@
-// SPDX-License-Identifier: 0BSD
-
-///////////////////////////////////////////////////////////////////////////////
-//
-/// \file       crc32_table.c
-/// \brief      Precalculated CRC32 table with correct endianness
-//
-//  Author:     Lasse Collin
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#include "common.h"
-
-
-// FIXME: Compared to crc_common.h this has to check for __x86_64__ too
-// so that in 32-bit builds crc32_x86.S won't break due to a missing table.
-#if defined(HAVE_USABLE_CLMUL) && ((defined(__x86_64__) && defined(__SSSE3__) \
-			&& defined(__SSE4_1__) && defined(__PCLMUL__)) \
-		|| (defined(__e2k__) && __iset__ >= 6))
-#	define NO_CRC32_TABLE
-
-#elif defined(HAVE_ARM64_CRC32) \
-		&& !defined(WORDS_BIGENDIAN) \
-		&& defined(__ARM_FEATURE_CRC32)
-#	define NO_CRC32_TABLE
-#endif
-
-
-#if !defined(HAVE_ENCODERS) && defined(NO_CRC32_TABLE)
-// No table needed. Use a typedef to avoid an empty translation unit.
-typedef void lzma_crc32_dummy;
-
-#else
-// Having the declaration here silences clang -Wmissing-variable-declarations.
-extern const uint32_t lzma_crc32_table[8][256];
-
-#	ifdef WORDS_BIGENDIAN
-#		include "crc32_table_be.h"
-#	else
-#		include "crc32_table_le.h"
-#	endif
-#endif
diff --git a/cbits/liblzma/check/crc64_fast.c b/cbits/liblzma/check/crc64_fast.c
--- a/cbits/liblzma/check/crc64_fast.c
+++ b/cbits/liblzma/check/crc64_fast.c
@@ -14,7 +14,7 @@
 #include "crc_common.h"
 
 #if defined(CRC_X86_CLMUL)
-#	define BUILDING_CRC64_CLMUL
+#	define BUILDING_CRC_CLMUL 64
 #	include "crc_x86_clmul.h"
 #endif
 
@@ -25,6 +25,18 @@
 // Generic slice-by-four CRC64 //
 /////////////////////////////////
 
+#if defined(WORDS_BIGENDIAN)
+#	include "crc64_table_be.h"
+#else
+#	include "crc64_table_le.h"
+#endif
+
+
+#ifdef HAVE_CRC_X86_ASM
+extern uint64_t lzma_crc64_generic(
+		const uint8_t *buf, size_t size, uint64_t crc);
+#else
+
 #ifdef WORDS_BIGENDIAN
 #	define A1(x) ((x) >> 56)
 #else
@@ -34,7 +46,7 @@
 
 // See the comments in crc32_fast.c. They aren't duplicated here.
 static uint64_t
-crc64_generic(const uint8_t *buf, size_t size, uint64_t crc)
+lzma_crc64_generic(const uint8_t *buf, size_t size, uint64_t crc)
 {
 	crc = ~crc;
 
@@ -78,7 +90,8 @@
 
 	return ~crc;
 }
-#endif
+#endif // HAVE_CRC_X86_ASM
+#endif // CRC64_GENERIC
 
 
 #if defined(CRC64_GENERIC) && defined(CRC64_ARCH_OPTIMIZED)
@@ -97,7 +110,7 @@
 crc64_resolve(void)
 {
 	return is_arch_extension_supported()
-			? &crc64_arch_optimized : &crc64_generic;
+			? &crc64_arch_optimized : &lzma_crc64_generic;
 }
 
 #ifdef HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR
@@ -133,24 +146,24 @@
 extern LZMA_API(uint64_t)
 lzma_crc64(const uint8_t *buf, size_t size, uint64_t crc)
 {
-#if defined(CRC64_GENERIC) && defined(CRC64_ARCH_OPTIMIZED)
-
-#ifdef CRC_USE_GENERIC_FOR_SMALL_INPUTS
-	if (size <= 16)
-		return crc64_generic(buf, size, crc);
+#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) \
+		&& defined(_M_IX86) && defined(CRC64_ARCH_OPTIMIZED)
+	// VS2015-2022 might corrupt the ebx register on 32-bit x86 when
+	// the CLMUL code is enabled. This hack forces MSVC to store and
+	// restore ebx. This is only needed here, not in lzma_crc32().
+	__asm  mov ebx, ebx
 #endif
+
+#if defined(CRC64_GENERIC) && defined(CRC64_ARCH_OPTIMIZED)
 	return crc64_func(buf, size, crc);
 
 #elif defined(CRC64_ARCH_OPTIMIZED)
 	// If arch-optimized version is used unconditionally without runtime
 	// CPU detection then omitting the generic version and its 8 KiB
 	// lookup table makes the library smaller.
-	//
-	// FIXME: Lookup table isn't currently omitted on 32-bit x86,
-	// see crc64_table.c.
 	return crc64_arch_optimized(buf, size, crc);
 
 #else
-	return crc64_generic(buf, size, crc);
+	return lzma_crc64_generic(buf, size, crc);
 #endif
 }
diff --git a/cbits/liblzma/check/crc64_table.c b/cbits/liblzma/check/crc64_table.c
deleted file mode 100644
--- a/cbits/liblzma/check/crc64_table.c
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-License-Identifier: 0BSD
-
-///////////////////////////////////////////////////////////////////////////////
-//
-/// \file       crc64_table.c
-/// \brief      Precalculated CRC64 table with correct endianness
-//
-//  Author:     Lasse Collin
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#include "common.h"
-
-
-// FIXME: Compared to crc_common.h this has to check for __x86_64__ too
-// so that in 32-bit builds crc64_x86.S won't break due to a missing table.
-#if defined(HAVE_USABLE_CLMUL) && ((defined(__x86_64__) && defined(__SSSE3__) \
-			&& defined(__SSE4_1__) && defined(__PCLMUL__)) \
-		|| (defined(__e2k__) && __iset__ >= 6))
-#	define NO_CRC64_TABLE
-#endif
-
-
-#ifdef NO_CRC64_TABLE
-// No table needed. Use a typedef to avoid an empty translation unit.
-typedef void lzma_crc64_dummy;
-
-#else
-// Having the declaration here silences clang -Wmissing-variable-declarations.
-extern const uint64_t lzma_crc64_table[4][256];
-
-#	if defined(WORDS_BIGENDIAN)
-#		include "crc64_table_be.h"
-#	else
-#		include "crc64_table_le.h"
-#	endif
-#endif
diff --git a/cbits/liblzma/check/crc_clmul_consts_gen.c b/cbits/liblzma/check/crc_clmul_consts_gen.c
new file mode 100644
--- /dev/null
+++ b/cbits/liblzma/check/crc_clmul_consts_gen.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: 0BSD
+
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       crc_clmul_consts_gen.c
+/// \brief      Generate constants for CLMUL CRC code
+///
+/// Compiling: gcc -std=c99 -o crc_clmul_consts_gen crc_clmul_consts_gen.c
+///
+/// This is for CRCs that use reversed bit order (bit reflection).
+/// The same CLMUL CRC code can be used with CRC64 and smaller ones like
+/// CRC32 apart from one special case: CRC64 needs an extra step in the
+/// Barrett reduction to handle the 65th bit; the smaller ones don't.
+/// Otherwise it's enough to just change the polynomial and the derived
+/// constants and use the same code.
+///
+/// See the Intel white paper "Fast CRC Computation for Generic Polynomials
+/// Using PCLMULQDQ Instruction" from 2009.
+//
+//  Author:     Lasse Collin
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <inttypes.h>
+#include <stdio.h>
+
+
+/// CRC32 (Ethernet) polynomial in reversed representation
+static const uint64_t p32 = 0xedb88320;
+
+// CRC64 (ECMA-182) polynomial in reversed representation
+static const uint64_t p64 = 0xc96c5795d7870f42;
+
+
+/// Calculates floor(x^128 / p) where p is a CRC64 polynomial in
+/// reversed representation. The result is in reversed representation too.
+static uint64_t
+calc_cldiv(uint64_t p)
+{
+	// Quotient
+	uint64_t q = 0;
+
+	// Align the x^64 term with the x^128 (the implied high bits of the
+	// divisor and the dividend) and do the first step of polynomial long
+	// division, calculating the first remainder. The variable q remains
+	// zero because the highest bit of the quotient is an implied bit 1
+	// (we kind of set q = 1 << -1).
+	uint64_t r = p;
+
+	// Then process the remaining 64 terms. Note that r has no implied
+	// high bit, only q and p do. (And remember that a high bit in the
+	// polynomial is stored at a low bit in the variable due to the
+	// reversed bit order.)
+	for (unsigned i = 0; i < 64; ++i) {
+		q |= (r & 1) << i;
+		r = (r >> 1) ^ (r & 1 ? p : 0);
+	}
+
+	return q;
+}
+
+
+/// Calculate the remainder of carryless division:
+///
+///     x^(bits + n - 1) % p, where n=64 (for CRC64)
+///
+/// p must be in reversed representation which omits the bit of
+/// the highest term of the polynomial. Instead, it is an implied bit
+/// at kind of like "1 << -1" position, as if it had just been shifted out.
+///
+/// The return value is in the reversed bit order. (There are no implied bits.)
+static uint64_t
+calc_clrem(uint64_t p, unsigned bits)
+{
+	// Do the first step of polynomial long division.
+	uint64_t r = p;
+
+	// Then process the remaining terms. Start with i = 1 instead of i = 0
+	// to account for the -1 in x^(bits + n - 1). This -1 is convenient
+	// with the reversed bit order. See the "Bit-Reflection" section in
+	// the Intel white paper.
+	for (unsigned i = 1; i < bits; ++i)
+		r = (r >> 1) ^ (r & 1 ? p : 0);
+
+	return r;
+}
+
+
+extern int
+main(void)
+{
+	puts("// CRC64");
+
+	// The order of the two 64-bit constants in a vector don't matter.
+	// It feels logical to put them in this order as it matches the
+	// order in which the input bytes are read.
+	printf("const __m128i fold512 = _mm_set_epi64x("
+		"0x%016" PRIx64 ", 0x%016" PRIx64 ");\n",
+		calc_clrem(p64, 4 * 128 - 64),
+		calc_clrem(p64, 4 * 128));
+
+	printf("const __m128i fold128 = _mm_set_epi64x("
+		"0x%016" PRIx64 ", 0x%016" PRIx64 ");\n",
+		calc_clrem(p64, 128 - 64),
+		calc_clrem(p64, 128));
+
+	// When we multiply by mu, we care about the high bits of the result
+	// (in reversed bit order!). It doesn't matter that the low bit gets
+	// shifted out because the affected output bits will be ignored.
+	// Below we add the implied high bit with "| 1" after the shifting
+	// so that the high bits of the multiplication will be correct.
+	//
+	// p64 is shifted left by one so that the final multiplication
+	// in Barrett reduction won't be misaligned by one bit. We could
+	// use "(p64 << 1) | 1" instead of "p64 << 1" too but it makes
+	// no difference as that bit won't affect the relevant output bits
+	// (we only care about the lowest 64 bits of the result, that is,
+	// lowest in the reversed bit order).
+	//
+	// NOTE: The 65rd bit of p64 gets shifted out. It needs to be
+	// compensated with 64-bit shift and xor in the CRC64 code.
+	printf("const __m128i mu_p = _mm_set_epi64x("
+		"0x%016" PRIx64 ", 0x%016" PRIx64 ");\n",
+		(calc_cldiv(p64) << 1) | 1,
+		p64 << 1);
+
+	puts("");
+
+	puts("// CRC32");
+
+	printf("const __m128i fold512 = _mm_set_epi64x("
+		"0x%08" PRIx64 ", 0x%08" PRIx64 ");\n",
+		calc_clrem(p32, 4 * 128 - 64),
+		calc_clrem(p32, 4 * 128));
+
+	printf("const __m128i fold128 = _mm_set_epi64x("
+		"0x%08" PRIx64 ", 0x%08" PRIx64 ");\n",
+		calc_clrem(p32, 128 - 64),
+		calc_clrem(p32, 128));
+
+	// CRC32 calculation is done by modulus scaling it to a CRC64.
+	// Since the CRC is in reversed representation, only the mu
+	// constant changes with the modulus scaling. This method avoids
+	// one additional constant and one additional clmul in the final
+	// reduction steps, making the code both simpler and faster.
+	//
+	// p32 is shifted left by one so that the final multiplication
+	// in Barrett reduction won't be misaligned by one bit. We could
+	// use "(p32 << 1) | 1" instead of "p32 << 1" too but it makes
+	// no difference as that bit won't affect the relevant output bits.
+	//
+	// NOTE: The 33-bit value fits in 64 bits so, unlike with CRC64,
+	// there is no need to compensate for any missing bits in the code.
+	printf("const __m128i mu_p = _mm_set_epi64x("
+		"0x%016" PRIx64 ", 0x%" PRIx64 ");\n",
+		(calc_cldiv(p32) << 1) | 1,
+		p32 << 1);
+
+	return 0;
+}
diff --git a/cbits/liblzma/check/crc_common.h b/cbits/liblzma/check/crc_common.h
--- a/cbits/liblzma/check/crc_common.h
+++ b/cbits/liblzma/check/crc_common.h
@@ -3,11 +3,10 @@
 ///////////////////////////////////////////////////////////////////////////////
 //
 /// \file       crc_common.h
-/// \brief      Some functions and macros for CRC32 and CRC64
+/// \brief      Macros and declarations for CRC32 and CRC64
 //
 //  Authors:    Lasse Collin
 //              Ilya Kurdyukov
-//              Hans Jansen
 //              Jia Tan
 //
 ///////////////////////////////////////////////////////////////////////////////
@@ -18,6 +17,10 @@
 #include "common.h"
 
 
+/////////////
+// Generic //
+/////////////
+
 #ifdef WORDS_BIGENDIAN
 #	define A(x) ((x) >> 24)
 #	define B(x) (((x) >> 16) & 0xFF)
@@ -38,43 +41,63 @@
 #endif
 
 
-// CRC CLMUL code needs this because accessing input buffers that aren't
-// aligned to the vector size will inherently trip the address sanitizer.
-#if lzma_has_attribute(__no_sanitize_address__)
-#	define crc_attr_no_sanitize_address \
-			__attribute__((__no_sanitize_address__))
+/// lzma_crc32_table[0] is needed by LZ encoder so we need to keep
+/// the array two-dimensional.
+#ifdef HAVE_SMALL
+lzma_attr_visibility_hidden
+extern uint32_t lzma_crc32_table[1][256];
+
+extern void lzma_crc32_init(void);
+
 #else
-#	define crc_attr_no_sanitize_address
-#endif
 
-// Keep this in sync with changes to crc32_arm64.h
-#if defined(_WIN32) || defined(HAVE_GETAUXVAL) \
-		|| defined(HAVE_ELF_AUX_INFO) \
-		|| (defined(__APPLE__) && defined(HAVE_SYSCTLBYNAME))
-#	define ARM64_RUNTIME_DETECTION 1
+lzma_attr_visibility_hidden
+extern const uint32_t lzma_crc32_table[8][256];
+
+lzma_attr_visibility_hidden
+extern const uint64_t lzma_crc64_table[4][256];
 #endif
 
 
+///////////////////
+// Configuration //
+///////////////////
+
+// NOTE: This config isn't used if HAVE_SMALL is defined!
+
+// These are defined if the generic slicing-by-n implementations and their
+// lookup tables are built.
 #undef CRC32_GENERIC
 #undef CRC64_GENERIC
 
+// These are defined if an arch-specific version is built. If both this
+// and matching _GENERIC is defined then runtime detection must be used.
 #undef CRC32_ARCH_OPTIMIZED
 #undef CRC64_ARCH_OPTIMIZED
 
 // The x86 CLMUL is used for both CRC32 and CRC64.
 #undef CRC_X86_CLMUL
 
+// Many ARM64 processor have CRC32 instructions.
+// CRC64 could be done with CLMUL but it's not implemented yet.
 #undef CRC32_ARM64
-#undef CRC64_ARM64_CLMUL
 
-#undef CRC_USE_GENERIC_FOR_SMALL_INPUTS
+// 64-bit LoongArch has CRC32 instructions.
+#undef CRC32_LOONGARCH
 
+
+// ARM64
+//
+// Keep this in sync with changes to crc32_arm64.h
+#if defined(_WIN32) || defined(HAVE_GETAUXVAL) \
+		|| defined(HAVE_ELF_AUX_INFO) \
+		|| (defined(__APPLE__) && defined(HAVE_SYSCTLBYNAME))
+#	define CRC_ARM64_RUNTIME_DETECTION 1
+#endif
+
 // ARM64 CRC32 instruction is only useful for CRC32. Currently, only
 // little endian is supported since we were unable to test on a big
 // endian machine.
-//
-// NOTE: Keep this and the next check in sync with the macro
-//       NO_CRC32_TABLE in crc32_table.c
 #if defined(HAVE_ARM64_CRC32) && !defined(WORDS_BIGENDIAN)
 	// Allow ARM64 CRC32 instruction without a runtime check if
 	// __ARM_FEATURE_CRC32 is defined. GCC and Clang only define
@@ -82,21 +105,40 @@
 #	if defined(__ARM_FEATURE_CRC32)
 #		define CRC32_ARCH_OPTIMIZED 1
 #		define CRC32_ARM64 1
-#	elif defined(ARM64_RUNTIME_DETECTION)
+#	elif defined(CRC_ARM64_RUNTIME_DETECTION)
 #		define CRC32_ARCH_OPTIMIZED 1
 #		define CRC32_ARM64 1
 #		define CRC32_GENERIC 1
 #	endif
 #endif
 
-#if defined(HAVE_USABLE_CLMUL)
-// If CLMUL is allowed unconditionally in the compiler options then the
-// generic version can be omitted. Note that this doesn't work with MSVC
-// as I don't know how to detect the features here.
+
+// LoongArch
 //
-// NOTE: Keep this in sync with the NO_CRC32_TABLE macro in crc32_table.c
-// and NO_CRC64_TABLE in crc64_table.c.
-#	if (defined(__SSSE3__) && defined(__SSE4_1__) && defined(__PCLMUL__)) \
+// Only 64-bit LoongArch is supported for now. No runtime detection
+// is needed because the LoongArch specification says that the CRC32
+// instructions are a part of the Basic Integer Instructions and
+// they shall be implemented by 64-bit LoongArch implementations.
+#ifdef HAVE_LOONGARCH_CRC32
+#	define CRC32_ARCH_OPTIMIZED 1
+#	define CRC32_LOONGARCH 1
+#endif
+
+
+// x86 and E2K
+#if defined(HAVE_USABLE_CLMUL)
+	// If CLMUL is allowed unconditionally in the compiler options then
+	// the generic version and the tables can be omitted. Exceptions:
+	//
+	//   - If 32-bit x86 assembly files are enabled then those are always
+	//     built and runtime detection is used even if compiler flags
+	//     were set to allow CLMUL unconditionally.
+	//
+	//   - This doesn't work with MSVC as I don't know how to detect
+	//     the features here.
+	//
+#	if (defined(__SSSE3__) && defined(__SSE4_1__) && defined(__PCLMUL__) \
+			&& !defined(HAVE_CRC_X86_ASM)) \
 		|| (defined(__e2k__) && __iset__ >= 6)
 #		define CRC32_ARCH_OPTIMIZED 1
 #		define CRC64_ARCH_OPTIMIZED 1
@@ -107,21 +149,12 @@
 #		define CRC32_ARCH_OPTIMIZED 1
 #		define CRC64_ARCH_OPTIMIZED 1
 #		define CRC_X86_CLMUL 1
-
-/*
-		// The generic code is much faster with 1-8-byte inputs and
-		// has similar performance up to 16 bytes  at least in
-		// microbenchmarks (it depends on input buffer alignment
-		// too). If both versions are built, this #define will use
-		// the generic version for inputs up to 16 bytes and CLMUL
-		// for bigger inputs. It saves a little in code size since
-		// the special cases for 0-16-byte inputs will be omitted
-		// from the CLMUL code.
-#		define CRC_USE_GENERIC_FOR_SMALL_INPUTS 1
-*/
 #	endif
 #endif
 
+
+// Fallback configuration
+//
 // For CRC32 use the generic slice-by-eight implementation if no optimized
 // version is available.
 #if !defined(CRC32_ARCH_OPTIMIZED) && !defined(CRC32_GENERIC)
diff --git a/cbits/liblzma/check/crc_x86_clmul.h b/cbits/liblzma/check/crc_x86_clmul.h
--- a/cbits/liblzma/check/crc_x86_clmul.h
+++ b/cbits/liblzma/check/crc_x86_clmul.h
@@ -8,26 +8,20 @@
 /// The CRC32 and CRC64 implementations use 32/64-bit x86 SSSE3, SSE4.1, and
 /// CLMUL instructions. This is compatible with Elbrus 2000 (E2K) too.
 ///
-/// They were derived from
+/// See the Intel white paper "Fast CRC Computation for Generic Polynomials
+/// Using PCLMULQDQ Instruction" from 2009. The original file seems to be
+/// gone from Intel's website but a version is available here:
 /// https://www.researchgate.net/publication/263424619_Fast_CRC_computation
-/// and the public domain code from https://github.com/rawrunprotected/crc
-/// (URLs were checked on 2023-10-14).
+/// (The link was checked on 2024-06-11.)
 ///
 /// While this file has both CRC32 and CRC64 implementations, only one
-/// should be built at a time to ensure that crc_simd_body() is inlined
-/// even with compilers with which lzma_always_inline expands to plain inline.
-/// The version to build is selected by defining BUILDING_CRC32_CLMUL or
-/// BUILDING_CRC64_CLMUL before including this file.
+/// can be built at a time. The version to build is selected by defining
+/// BUILDING_CRC_CLMUL to 32 or 64 before including this file.
 ///
-/// FIXME: Builds for 32-bit x86 use the assembly .S files by default
-/// unless configured with --disable-assembler. Even then the lookup table
-/// isn't omitted in crc64_table.c since it doesn't know that assembly
-/// code has been disabled.
+/// NOTE: The x86 CLMUL CRC implementation was rewritten for XZ Utils 5.8.0.
 //
-//  Authors:    Ilya Kurdyukov
-//              Hans Jansen
-//              Lasse Collin
-//              Jia Tan
+//  Authors:    Lasse Collin
+//              Ilya Kurdyukov
 //
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -37,6 +31,10 @@
 #endif
 #define LZMA_CRC_X86_CLMUL_H
 
+#if BUILDING_CRC_CLMUL != 32 && BUILDING_CRC_CLMUL != 64
+#	error BUILDING_CRC_CLMUL is undefined or has an invalid value
+#endif
+
 #include <immintrin.h>
 
 #if defined(_MSC_VER)
@@ -59,330 +57,277 @@
 #endif
 
 
-#define MASK_L(in, mask, r) r = _mm_shuffle_epi8(in, mask)
+// GCC and Clang would produce good code with _mm_set_epi64x
+// but MSVC needs _mm_cvtsi64_si128 on x86-64.
+#if defined(__i386__) || defined(_M_IX86)
+#	define my_set_low64(a) _mm_set_epi64x(0, (a))
+#else
+#	define my_set_low64(a) _mm_cvtsi64_si128(a)
+#endif
 
-#define MASK_H(in, mask, r) \
-	r = _mm_shuffle_epi8(in, _mm_xor_si128(mask, vsign))
 
-#define MASK_LH(in, mask, low, high) \
-	MASK_L(in, mask, low); \
-	MASK_H(in, mask, high)
+// Align it so that the whole array is within the same cache line.
+// More than one unaligned load can be done from this during the
+// same CRC function call.
+//
+// The bytes [0] to [31] are used with AND to clear the low bytes. (With ANDN
+// those could be used to clear the high bytes too but it's not needed here.)
+//
+// The bytes [16] to [47] are for left shifts.
+// The bytes [32] to [63] are for right shifts.
+alignas(64)
+static uint8_t vmasks[64] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
 
 
+// *Unaligned* 128-bit load
 crc_attr_target
-crc_attr_no_sanitize_address
-static lzma_always_inline void
-crc_simd_body(const uint8_t *buf, const size_t size, __m128i *v0, __m128i *v1,
-		const __m128i vfold16, const __m128i initial_crc)
+static inline __m128i
+my_load128(const uint8_t *p)
 {
-	// Create a vector with 8-bit values 0 to 15. This is used to
-	// construct control masks for _mm_blendv_epi8 and _mm_shuffle_epi8.
-	const __m128i vramp = _mm_setr_epi32(
-			0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c);
+	return _mm_loadu_si128((const __m128i *)p);
+}
 
-	// This is used to inverse the control mask of _mm_shuffle_epi8
-	// so that bytes that wouldn't be picked with the original mask
-	// will be picked and vice versa.
-	const __m128i vsign = _mm_set1_epi8(-0x80);
 
-	// Memory addresses A to D and the distances between them:
-	//
-	//     A           B     C         D
-	//     [skip_start][size][skip_end]
-	//     [     size2      ]
-	//
-	// A and D are 16-byte aligned. B and C are 1-byte aligned.
-	// skip_start and skip_end are 0-15 bytes. size is at least 1 byte.
-	//
-	// A = aligned_buf will initially point to this address.
-	// B = The address pointed by the caller-supplied buf.
-	// C = buf + size == aligned_buf + size2
-	// D = buf + size + skip_end == aligned_buf + size2 + skip_end
-	const size_t skip_start = (size_t)((uintptr_t)buf & 15);
-	const size_t skip_end = (size_t)((0U - (uintptr_t)(buf + size)) & 15);
-	const __m128i *aligned_buf = (const __m128i *)(
-			(uintptr_t)buf & ~(uintptr_t)15);
+// Keep the highest "count" bytes as is and clear the remaining low bytes.
+crc_attr_target
+static inline __m128i
+keep_high_bytes(__m128i v, size_t count)
+{
+	return _mm_and_si128(my_load128((vmasks + count)), v);
+}
 
-	// If size2 <= 16 then the whole input fits into a single 16-byte
-	// vector. If size2 > 16 then at least two 16-byte vectors must
-	// be processed. If size2 > 16 && size <= 16 then there is only
-	// one 16-byte vector's worth of input but it is unaligned in memory.
-	//
-	// NOTE: There is no integer overflow here if the arguments
-	// are valid. If this overflowed, buf + size would too.
-	const size_t size2 = skip_start + size;
 
-	// Masks to be used with _mm_blendv_epi8 and _mm_shuffle_epi8:
-	// The first skip_start or skip_end bytes in the vectors will have
-	// the high bit (0x80) set. _mm_blendv_epi8 and _mm_shuffle_epi8
-	// will produce zeros for these positions. (Bitwise-xor of these
-	// masks with vsign will produce the opposite behavior.)
-	const __m128i mask_start
-			= _mm_sub_epi8(vramp, _mm_set1_epi8((char)skip_start));
-	const __m128i mask_end
-			= _mm_sub_epi8(vramp, _mm_set1_epi8((char)skip_end));
+// Shift the 128-bit value left by "amount" bytes (not bits).
+crc_attr_target
+static inline __m128i
+shift_left(__m128i v, size_t amount)
+{
+	return _mm_shuffle_epi8(v, my_load128((vmasks + 32 - amount)));
+}
 
-	// Get the first 1-16 bytes into data0. If loading less than 16
-	// bytes, the bytes are loaded to the high bits of the vector and
-	// the least significant positions are filled with zeros.
-	const __m128i data0 = _mm_blendv_epi8(_mm_load_si128(aligned_buf),
-			_mm_setzero_si128(), mask_start);
-	aligned_buf++;
 
-	__m128i v2, v3;
-
-#ifndef CRC_USE_GENERIC_FOR_SMALL_INPUTS
-	if (size <= 16) {
-		// Right-shift initial_crc by 1-16 bytes based on "size"
-		// and store the result in v1 (high bytes) and v0 (low bytes).
-		//
-		// NOTE: The highest 8 bytes of initial_crc are zeros so
-		// v1 will be filled with zeros if size >= 8. The highest
-		// 8 bytes of v1 will always become zeros.
-		//
-		// [      v1      ][      v0      ]
-		//  [ initial_crc  ]                  size == 1
-		//   [ initial_crc  ]                 size == 2
-		//                [ initial_crc  ]    size == 15
-		//                 [ initial_crc  ]   size == 16 (all in v0)
-		const __m128i mask_low = _mm_add_epi8(
-				vramp, _mm_set1_epi8((char)(size - 16)));
-		MASK_LH(initial_crc, mask_low, *v0, *v1);
-
-		if (size2 <= 16) {
-			// There are 1-16 bytes of input and it is all
-			// in data0. Copy the input bytes to v3. If there
-			// are fewer than 16 bytes, the low bytes in v3
-			// will be filled with zeros. That is, the input
-			// bytes are stored to the same position as
-			// (part of) initial_crc is in v0.
-			MASK_L(data0, mask_end, v3);
-		} else {
-			// There are 2-16 bytes of input but not all bytes
-			// are in data0.
-			const __m128i data1 = _mm_load_si128(aligned_buf);
-
-			// Collect the 2-16 input bytes from data0 and data1
-			// to v2 and v3, and bitwise-xor them with the
-			// low bits of initial_crc in v0. Note that the
-			// the second xor is below this else-block as it
-			// is shared with the other branch.
-			MASK_H(data0, mask_end, v2);
-			MASK_L(data1, mask_end, v3);
-			*v0 = _mm_xor_si128(*v0, v2);
-		}
-
-		*v0 = _mm_xor_si128(*v0, v3);
-		*v1 = _mm_alignr_epi8(*v1, *v0, 8);
-	} else
-#endif
-	{
-		// There is more than 16 bytes of input.
-		const __m128i data1 = _mm_load_si128(aligned_buf);
-		const __m128i *end = (const __m128i*)(
-				(const char *)aligned_buf - 16 + size2);
-		aligned_buf++;
-
-		MASK_LH(initial_crc, mask_start, *v0, *v1);
-		*v0 = _mm_xor_si128(*v0, data0);
-		*v1 = _mm_xor_si128(*v1, data1);
-
-		while (aligned_buf < end) {
-			*v1 = _mm_xor_si128(*v1, _mm_clmulepi64_si128(
-					*v0, vfold16, 0x00));
-			*v0 = _mm_xor_si128(*v1, _mm_clmulepi64_si128(
-					*v0, vfold16, 0x11));
-			*v1 = _mm_load_si128(aligned_buf++);
-		}
-
-		if (aligned_buf != end) {
-			MASK_H(*v0, mask_end, v2);
-			MASK_L(*v0, mask_end, *v0);
-			MASK_L(*v1, mask_end, v3);
-			*v1 = _mm_or_si128(v2, v3);
-		}
-
-		*v1 = _mm_xor_si128(*v1, _mm_clmulepi64_si128(
-				*v0, vfold16, 0x00));
-		*v0 = _mm_xor_si128(*v1, _mm_clmulepi64_si128(
-				*v0, vfold16, 0x11));
-		*v1 = _mm_srli_si128(*v0, 8);
-	}
+// Shift the 128-bit value right by "amount" bytes (not bits).
+crc_attr_target
+static inline __m128i
+shift_right(__m128i v, size_t amount)
+{
+	return _mm_shuffle_epi8(v, my_load128((vmasks + 32 + amount)));
 }
 
 
-/////////////////////
-// x86 CLMUL CRC32 //
-/////////////////////
-
-/*
-// These functions were used to generate the constants
-// at the top of crc32_arch_optimized().
-static uint64_t
-calc_lo(uint64_t p, uint64_t a, int n)
+crc_attr_target
+static inline __m128i
+fold(__m128i v, __m128i k)
 {
-	uint64_t b = 0; int i;
-	for (i = 0; i < n; i++) {
-		b = b >> 1 | (a & 1) << (n - 1);
-		a = (a >> 1) ^ ((0 - (a & 1)) & p);
-	}
-	return b;
+	__m128i a = _mm_clmulepi64_si128(v, k, 0x00);
+	__m128i b = _mm_clmulepi64_si128(v, k, 0x11);
+	return _mm_xor_si128(a, b);
 }
 
-// same as ~crc(&a, sizeof(a), ~0)
-static uint64_t
-calc_hi(uint64_t p, uint64_t a, int n)
+
+crc_attr_target
+static inline __m128i
+fold_xor(__m128i v, __m128i k, const uint8_t *buf)
 {
-	int i;
-	for (i = 0; i < n; i++)
-		a = (a >> 1) ^ ((0 - (a & 1)) & p);
-	return a;
+	return _mm_xor_si128(my_load128(buf), fold(v, k));
 }
-*/
 
-#ifdef BUILDING_CRC32_CLMUL
 
+#if BUILDING_CRC_CLMUL == 32
 crc_attr_target
-crc_attr_no_sanitize_address
 static uint32_t
 crc32_arch_optimized(const uint8_t *buf, size_t size, uint32_t crc)
+#else
+crc_attr_target
+static uint64_t
+crc64_arch_optimized(const uint8_t *buf, size_t size, uint64_t crc)
+#endif
 {
-#ifndef CRC_USE_GENERIC_FOR_SMALL_INPUTS
-	// The code assumes that there is at least one byte of input.
+	// We will assume that there is at least one byte of input.
 	if (size == 0)
 		return crc;
-#endif
 
-	// uint32_t poly = 0xedb88320;
-	const int64_t p = 0x1db710640; // p << 1
-	const int64_t mu = 0x1f7011641; // calc_lo(p, p, 32) << 1 | 1
-	const int64_t k5 = 0x163cd6124; // calc_hi(p, p, 32) << 1
-	const int64_t k4 = 0x0ccaa009e; // calc_hi(p, p, 64) << 1
-	const int64_t k3 = 0x1751997d0; // calc_hi(p, p, 128) << 1
+	// See crc_clmul_consts_gen.c.
+#if BUILDING_CRC_CLMUL == 32
+	const __m128i fold512 = _mm_set_epi64x(0x1d9513d7, 0x8f352d95);
+	const __m128i fold128 = _mm_set_epi64x(0xccaa009e, 0xae689191);
+	const __m128i mu_p = _mm_set_epi64x(
+		(int64_t)0xb4e5b025f7011641, 0x1db710640);
+#else
+	const __m128i fold512 = _mm_set_epi64x(
+		(int64_t)0x081f6054a7842df4, (int64_t)0x6ae3efbb9dd441f3);
 
-	const __m128i vfold4 = _mm_set_epi64x(mu, p);
-	const __m128i vfold8 = _mm_set_epi64x(0, k5);
-	const __m128i vfold16 = _mm_set_epi64x(k4, k3);
+	const __m128i fold128 = _mm_set_epi64x(
+		(int64_t)0xdabe95afc7875f40, (int64_t)0xe05dd497ca393ae4);
 
-	__m128i v0, v1, v2;
+	const __m128i mu_p = _mm_set_epi64x(
+		(int64_t)0x9c3e466c172963d5, (int64_t)0x92d8af2baf0e1e84);
+#endif
 
-	crc_simd_body(buf, size, &v0, &v1, vfold16,
-			_mm_cvtsi32_si128((int32_t)~crc));
+	__m128i v0, v1, v2, v3;
 
-	v1 = _mm_xor_si128(
-			_mm_clmulepi64_si128(v0, vfold16, 0x10), v1); // xxx0
-	v2 = _mm_shuffle_epi32(v1, 0xe7); // 0xx0
-	v0 = _mm_slli_epi64(v1, 32);  // [0]
-	v0 = _mm_clmulepi64_si128(v0, vfold8, 0x00);
-	v0 = _mm_xor_si128(v0, v2);   // [1] [2]
-	v2 = _mm_clmulepi64_si128(v0, vfold4, 0x10);
-	v2 = _mm_clmulepi64_si128(v2, vfold4, 0x00);
-	v0 = _mm_xor_si128(v0, v2);   // [2]
-	return ~(uint32_t)_mm_extract_epi32(v0, 2);
-}
-#endif // BUILDING_CRC32_CLMUL
+	crc = ~crc;
 
+	if (size < 8) {
+		uint64_t x = crc;
+		size_t i = 0;
 
-/////////////////////
-// x86 CLMUL CRC64 //
-/////////////////////
+		// Checking the bit instead of comparing the size means
+		// that we don't need to update the size between the steps.
+		if (size & 4) {
+			x ^= read32le(buf);
+			buf += 4;
+			i = 32;
+		}
 
-/*
-// These functions were used to generate the constants
-// at the top of crc64_arch_optimized().
-static uint64_t
-calc_lo(uint64_t poly)
-{
-	uint64_t a = poly;
-	uint64_t b = 0;
+		if (size & 2) {
+			x ^= (uint64_t)read16le(buf) << i;
+			buf += 2;
+			i += 16;
+		}
 
-	for (unsigned i = 0; i < 64; ++i) {
-		b = (b >> 1) | (a << 63);
-		a = (a >> 1) ^ (a & 1 ? poly : 0);
-	}
+		if (size & 1)
+			x ^= (uint64_t)*buf << i;
 
-	return b;
-}
+		v0 = my_set_low64((int64_t)x);
+		v0 = shift_left(v0, 8 - size);
 
-static uint64_t
-calc_hi(uint64_t poly, uint64_t a)
-{
-	for (unsigned i = 0; i < 64; ++i)
-		a = (a >> 1) ^ (a & 1 ? poly : 0);
+	} else if (size < 16) {
+		v0 = my_set_low64((int64_t)(crc ^ read64le(buf)));
 
-	return a;
-}
-*/
+		// NOTE: buf is intentionally left 8 bytes behind so that
+		// we can read the last 1-7 bytes with read64le(buf + size).
+		size -= 8;
 
-#ifdef BUILDING_CRC64_CLMUL
+		// Handling 8-byte input specially is a speed optimization
+		// as the clmul can be skipped. A branch is also needed to
+		// avoid a too high shift amount.
+		if (size > 0) {
+			const size_t padding = 8 - size;
+			uint64_t high = read64le(buf + size) >> (padding * 8);
 
-// MSVC (VS2015 - VS2022) produces bad 32-bit x86 code from the CLMUL CRC
-// code when optimizations are enabled (release build). According to the bug
-// report, the ebx register is corrupted and the calculated result is wrong.
-// Trying to workaround the problem with "__asm mov ebx, ebx" didn't help.
-// The following pragma works and performance is still good. x86-64 builds
-// and CRC32 CLMUL aren't affected by this problem. The problem does not
-// happen in crc_simd_body() either (which is shared with CRC32 CLMUL anyway).
-//
-// NOTE: Another pragma after crc64_arch_optimized() restores
-// the optimizations. If the #if condition here is updated,
-// the other one must be updated too.
-#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) \
-		&& defined(_M_IX86)
-#	pragma optimize("g", off)
+#if defined(__i386__) || defined(_M_IX86)
+			// Simple but likely not the best code for 32-bit x86.
+			v0 = _mm_insert_epi32(v0, (int32_t)high, 2);
+			v0 = _mm_insert_epi32(v0, (int32_t)(high >> 32), 3);
+#else
+			v0 = _mm_insert_epi64(v0, (int64_t)high, 1);
 #endif
 
-crc_attr_target
-crc_attr_no_sanitize_address
-static uint64_t
-crc64_arch_optimized(const uint8_t *buf, size_t size, uint64_t crc)
-{
-#ifndef CRC_USE_GENERIC_FOR_SMALL_INPUTS
-	// The code assumes that there is at least one byte of input.
-	if (size == 0)
-		return crc;
-#endif
+			v0 = shift_left(v0, padding);
 
-	// const uint64_t poly = 0xc96c5795d7870f42; // CRC polynomial
-	const uint64_t p  = 0x92d8af2baf0e1e85; // (poly << 1) | 1
-	const uint64_t mu = 0x9c3e466c172963d5; // (calc_lo(poly) << 1) | 1
-	const uint64_t k2 = 0xdabe95afc7875f40; // calc_hi(poly, 1)
-	const uint64_t k1 = 0xe05dd497ca393ae4; // calc_hi(poly, k2)
+			v1 = _mm_srli_si128(v0, 8);
+			v0 = _mm_clmulepi64_si128(v0, fold128, 0x10);
+			v0 = _mm_xor_si128(v0, v1);
+		}
+	} else {
+		v0 = my_set_low64((int64_t)crc);
 
-	const __m128i vfold8 = _mm_set_epi64x((int64_t)p, (int64_t)mu);
-	const __m128i vfold16 = _mm_set_epi64x((int64_t)k2, (int64_t)k1);
+		// To align or not to align the buf pointer? If the end of
+		// the buffer isn't aligned, aligning the pointer here would
+		// make us do an extra folding step with the associated byte
+		// shuffling overhead. The cost of that would need to be
+		// lower than the benefit of aligned reads. Testing on an old
+		// Intel Ivy Bridge processor suggested that aligning isn't
+		// worth the cost but it likely depends on the processor and
+		// buffer size. Unaligned loads (MOVDQU) should be fast on
+		// x86 processors that support PCLMULQDQ, so we don't align
+		// the buf pointer here.
 
-	__m128i v0, v1, v2;
+		// Read the first (and possibly the only) full 16 bytes.
+		v0 = _mm_xor_si128(v0, my_load128(buf));
+		buf += 16;
+		size -= 16;
 
-#if defined(__i386__) || defined(_M_IX86)
-	crc_simd_body(buf, size, &v0, &v1, vfold16,
-			_mm_set_epi64x(0, (int64_t)~crc));
-#else
-	// GCC and Clang would produce good code with _mm_set_epi64x
-	// but MSVC needs _mm_cvtsi64_si128 on x86-64.
-	crc_simd_body(buf, size, &v0, &v1, vfold16,
-			_mm_cvtsi64_si128((int64_t)~crc));
-#endif
+		if (size >= 48) {
+			v1 = my_load128(buf);
+			v2 = my_load128(buf + 16);
+			v3 = my_load128(buf + 32);
+			buf += 48;
+			size -= 48;
 
-	v1 = _mm_xor_si128(_mm_clmulepi64_si128(v0, vfold16, 0x10), v1);
-	v0 = _mm_clmulepi64_si128(v1, vfold8, 0x00);
-	v2 = _mm_clmulepi64_si128(v0, vfold8, 0x10);
-	v0 = _mm_xor_si128(_mm_xor_si128(v1, _mm_slli_si128(v0, 8)), v2);
+			while (size >= 64) {
+				v0 = fold_xor(v0, fold512, buf);
+				v1 = fold_xor(v1, fold512, buf + 16);
+				v2 = fold_xor(v2, fold512, buf + 32);
+				v3 = fold_xor(v3, fold512, buf + 48);
+				buf += 64;
+				size -= 64;
+			}
 
+			v0 = _mm_xor_si128(v1, fold(v0, fold128));
+			v0 = _mm_xor_si128(v2, fold(v0, fold128));
+			v0 = _mm_xor_si128(v3, fold(v0, fold128));
+		}
+
+		while (size >= 16) {
+			v0 = fold_xor(v0, fold128, buf);
+			buf += 16;
+			size -= 16;
+		}
+
+		if (size > 0) {
+			// We want the last "size" number of input bytes to
+			// be at the high bits of v1. First do a full 16-byte
+			// load and then mask the low bytes to zeros.
+			v1 = my_load128(buf + size - 16);
+			v1 = keep_high_bytes(v1, size);
+
+			// Shift high bytes from v0 to the low bytes of v1.
+			//
+			// Alternatively we could replace the combination
+			// keep_high_bytes + shift_right + _mm_or_si128 with
+			// _mm_shuffle_epi8 + _mm_blendv_epi8 but that would
+			// require larger tables for the masks. Now there are
+			// three loads (instead of two) from the mask tables
+			// but they all are from the same cache line.
+			v1 = _mm_or_si128(v1, shift_right(v0, size));
+
+			// Shift high bytes of v0 away, padding the
+			// low bytes with zeros.
+			v0 = shift_left(v0, 16 - size);
+
+			v0 = _mm_xor_si128(v1, fold(v0, fold128));
+		}
+
+		v1 = _mm_srli_si128(v0, 8);
+		v0 = _mm_clmulepi64_si128(v0, fold128, 0x10);
+		v0 = _mm_xor_si128(v0, v1);
+	}
+
+	// Barrett reduction
+
+#if BUILDING_CRC_CLMUL == 32
+	v1 = _mm_clmulepi64_si128(v0, mu_p, 0x10); // v0 * mu
+	v1 = _mm_clmulepi64_si128(v1, mu_p, 0x00); // v1 * p
+	v0 = _mm_xor_si128(v0, v1);
+	return ~(uint32_t)_mm_extract_epi32(v0, 2);
+#else
+	// Because p is 65 bits but one bit doesn't fit into the 64-bit
+	// half of __m128i, finish the second clmul by shifting v1 left
+	// by 64 bits and xorring it to the final result.
+	v1 = _mm_clmulepi64_si128(v0, mu_p, 0x10); // v0 * mu
+	v2 = _mm_slli_si128(v1, 8);
+	v1 = _mm_clmulepi64_si128(v1, mu_p, 0x00); // v1 * p
+	v0 = _mm_xor_si128(v0, v2);
+	v0 = _mm_xor_si128(v0, v1);
 #if defined(__i386__) || defined(_M_IX86)
 	return ~(((uint64_t)(uint32_t)_mm_extract_epi32(v0, 3) << 32) |
 			(uint64_t)(uint32_t)_mm_extract_epi32(v0, 2));
 #else
 	return ~(uint64_t)_mm_extract_epi64(v0, 1);
 #endif
-}
-
-#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) \
-		&& defined(_M_IX86)
-#	pragma optimize("", on)
 #endif
-
-#endif // BUILDING_CRC64_CLMUL
+}
 
 
 // Even though this is an inline function, compile it only when needed.
diff --git a/cbits/liblzma/common/alone_decoder.c b/cbits/liblzma/common/alone_decoder.c
--- a/cbits/liblzma/common/alone_decoder.c
+++ b/cbits/liblzma/common/alone_decoder.c
@@ -134,8 +134,7 @@
 
 		coder->pos = 0;
 		coder->sequence = SEQ_CODER_INIT;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_CODER_INIT: {
 		if (coder->memusage > coder->memlimit)
diff --git a/cbits/liblzma/common/auto_decoder.c b/cbits/liblzma/common/auto_decoder.c
--- a/cbits/liblzma/common/auto_decoder.c
+++ b/cbits/liblzma/common/auto_decoder.c
@@ -79,7 +79,7 @@
 				return LZMA_GET_CHECK;
 		}
 
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_CODE: {
 		const lzma_ret ret = coder->next.code(
@@ -91,9 +91,8 @@
 			return ret;
 
 		coder->sequence = SEQ_FINISH;
+		FALLTHROUGH;
 	}
-
-	// Fall through
 
 	case SEQ_FINISH:
 		// When LZMA_CONCATENATED was used and we were decoding
diff --git a/cbits/liblzma/common/block_decoder.c b/cbits/liblzma/common/block_decoder.c
--- a/cbits/liblzma/common/block_decoder.c
+++ b/cbits/liblzma/common/block_decoder.c
@@ -146,10 +146,9 @@
 		coder->block->uncompressed_size = coder->uncompressed_size;
 
 		coder->sequence = SEQ_PADDING;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_PADDING:
 		// Compressed Data is padded to a multiple of four bytes.
 		while (coder->compressed_size & 3) {
@@ -173,8 +172,7 @@
 			lzma_check_finish(&coder->check, coder->block->check);
 
 		coder->sequence = SEQ_CHECK;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_CHECK: {
 		const size_t check_size = lzma_check_size(coder->block->check);
diff --git a/cbits/liblzma/common/block_encoder.c b/cbits/liblzma/common/block_encoder.c
--- a/cbits/liblzma/common/block_encoder.c
+++ b/cbits/liblzma/common/block_encoder.c
@@ -94,10 +94,9 @@
 		coder->block->uncompressed_size = coder->uncompressed_size;
 
 		coder->sequence = SEQ_PADDING;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_PADDING:
 		// Pad Compressed Data to a multiple of four bytes. We can
 		// use coder->compressed_size for this since we don't need
@@ -117,8 +116,7 @@
 		lzma_check_finish(&coder->check, coder->block->check);
 
 		coder->sequence = SEQ_CHECK;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_CHECK: {
 		const size_t check_size = lzma_check_size(coder->block->check);
diff --git a/cbits/liblzma/common/common.c b/cbits/liblzma/common/common.c
--- a/cbits/liblzma/common/common.c
+++ b/cbits/liblzma/common/common.c
@@ -348,7 +348,7 @@
 		else
 			strm->internal->sequence = ISEQ_END;
 
-	// Fall through
+		FALLTHROUGH;
 
 	case LZMA_NO_CHECK:
 	case LZMA_UNSUPPORTED_CHECK:
diff --git a/cbits/liblzma/common/file_info.c b/cbits/liblzma/common/file_info.c
--- a/cbits/liblzma/common/file_info.c
+++ b/cbits/liblzma/common/file_info.c
@@ -298,15 +298,13 @@
 		// Start looking for Stream Padding and Stream Footer
 		// at the end of the file.
 		coder->file_target_pos = coder->file_size;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_PADDING_SEEK:
 		coder->sequence = SEQ_PADDING_DECODE;
 		return_if_error(reverse_seek(
 				coder, in_start, in_pos, in_size));
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_PADDING_DECODE: {
 		// Copy to coder->temp first. This keeps the code simpler if
@@ -356,9 +354,9 @@
 		if (coder->temp_size < LZMA_STREAM_HEADER_SIZE)
 			return_if_error(reverse_seek(
 					coder, in_start, in_pos, in_size));
-	}
 
-	// Fall through
+		FALLTHROUGH;
+	}
 
 	case SEQ_FOOTER:
 		// Copy the Stream Footer field into coder->temp.
@@ -414,7 +412,7 @@
 				return LZMA_SEEK_NEEDED;
 		}
 
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_INDEX_INIT: {
 		// Calculate the amount of memory already used by the earlier
@@ -444,10 +442,9 @@
 
 		coder->index_remaining = coder->footer_flags.backward_size;
 		coder->sequence = SEQ_INDEX_DECODE;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_INDEX_DECODE: {
 		// Decode (a part of) the Index. If the whole Index is already
 		// in coder->temp, read it from there. Otherwise read from
@@ -574,9 +571,9 @@
 			return_if_error(reverse_seek(coder,
 					in_start, in_pos, in_size));
 		}
-	}
 
-	// Fall through
+		FALLTHROUGH;
+	}
 
 	case SEQ_HEADER_DECODE:
 		// Copy the Stream Header field into coder->temp.
@@ -596,8 +593,7 @@
 				coder->temp + coder->temp_size)));
 
 		coder->sequence = SEQ_HEADER_COMPARE;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_HEADER_COMPARE:
 		// Compare Stream Header against Stream Footer. They must
diff --git a/cbits/liblzma/common/index_decoder.c b/cbits/liblzma/common/index_decoder.c
--- a/cbits/liblzma/common/index_decoder.c
+++ b/cbits/liblzma/common/index_decoder.c
@@ -93,8 +93,7 @@
 
 		coder->pos = 0;
 		coder->sequence = SEQ_MEMUSAGE;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_MEMUSAGE:
 		if (lzma_index_memusage(1, coder->count) > coder->memlimit) {
@@ -153,8 +152,7 @@
 	case SEQ_PADDING_INIT:
 		coder->pos = lzma_index_padding_size(coder->index);
 		coder->sequence = SEQ_PADDING;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_PADDING:
 		if (coder->pos > 0) {
@@ -170,8 +168,7 @@
 				*in_pos - in_start, coder->crc32);
 
 		coder->sequence = SEQ_CRC32;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_CRC32:
 		do {
diff --git a/cbits/liblzma/common/index_encoder.c b/cbits/liblzma/common/index_encoder.c
--- a/cbits/liblzma/common/index_encoder.c
+++ b/cbits/liblzma/common/index_encoder.c
@@ -93,8 +93,7 @@
 		}
 
 		coder->sequence = SEQ_UNPADDED;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_UNPADDED:
 	case SEQ_UNCOMPRESSED: {
@@ -127,8 +126,7 @@
 				*out_pos - out_start, coder->crc32);
 
 		coder->sequence = SEQ_CRC32;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_CRC32:
 		// We don't use the main loop, because we don't want
diff --git a/cbits/liblzma/common/index_hash.c b/cbits/liblzma/common/index_hash.c
--- a/cbits/liblzma/common/index_hash.c
+++ b/cbits/liblzma/common/index_hash.c
@@ -267,9 +267,9 @@
 		index_hash->pos = (LZMA_VLI_C(4) - index_size_unpadded(
 				index_hash->records.count,
 				index_hash->records.index_list_size)) & 3;
-		index_hash->sequence = SEQ_PADDING;
 
-	// Fall through
+		index_hash->sequence = SEQ_PADDING;
+		FALLTHROUGH;
 
 	case SEQ_PADDING:
 		if (index_hash->pos > 0) {
@@ -302,8 +302,7 @@
 				*in_pos - in_start, index_hash->crc32);
 
 		index_hash->sequence = SEQ_CRC32;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_CRC32:
 		do {
diff --git a/cbits/liblzma/common/lzip_decoder.c b/cbits/liblzma/common/lzip_decoder.c
--- a/cbits/liblzma/common/lzip_decoder.c
+++ b/cbits/liblzma/common/lzip_decoder.c
@@ -150,10 +150,9 @@
 		coder->member_size = sizeof(lzip_id_string);
 
 		coder->sequence = SEQ_VERSION;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_VERSION:
 		if (*in_pos >= in_size)
 			return LZMA_OK;
@@ -173,7 +172,7 @@
 		if (coder->tell_any_check)
 			return LZMA_GET_CHECK;
 
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_DICT_SIZE: {
 		if (*in_pos >= in_size)
@@ -220,10 +219,9 @@
 		// LZMA_MEMLIMIT_ERROR we need to be able to restart after
 		// the memlimit has been increased.
 		coder->sequence = SEQ_CODER_INIT;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_CODER_INIT: {
 		if (coder->memusage > coder->memlimit)
 			return LZMA_MEMLIMIT_ERROR;
@@ -243,10 +241,9 @@
 
 		coder->crc32 = 0;
 		coder->sequence = SEQ_LZMA_STREAM;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_LZMA_STREAM: {
 		const size_t in_start = *in_pos;
 		const size_t out_start = *out_pos;
@@ -273,9 +270,8 @@
 			return ret;
 
 		coder->sequence = SEQ_MEMBER_FOOTER;
+		FALLTHROUGH;
 	}
-
-	// Fall through
 
 	case SEQ_MEMBER_FOOTER: {
 		// The footer of .lz version 0 lacks the Member size field.
diff --git a/cbits/liblzma/common/memcmplen.h b/cbits/liblzma/common/memcmplen.h
--- a/cbits/liblzma/common/memcmplen.h
+++ b/cbits/liblzma/common/memcmplen.h
@@ -58,8 +58,7 @@
 
 #if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
 		&& (((TUKLIB_GNUC_REQ(3, 4) || defined(__clang__)) \
-				&& (defined(__x86_64__) \
-					|| defined(__aarch64__))) \
+				&& SIZE_MAX == UINT64_MAX) \
 			|| (defined(__INTEL_COMPILER) && defined(__x86_64__)) \
 			|| (defined(__INTEL_COMPILER) && defined(_M_X64)) \
 			|| (defined(_MSC_VER) && (defined(_M_X64) \
diff --git a/cbits/liblzma/common/stream_decoder.c b/cbits/liblzma/common/stream_decoder.c
--- a/cbits/liblzma/common/stream_decoder.c
+++ b/cbits/liblzma/common/stream_decoder.c
@@ -154,9 +154,9 @@
 
 		if (coder->tell_any_check)
 			return LZMA_GET_CHECK;
-	}
 
-	// Fall through
+		FALLTHROUGH;
+	}
 
 	case SEQ_BLOCK_HEADER: {
 		if (*in_pos >= in_size)
@@ -187,10 +187,9 @@
 
 		coder->pos = 0;
 		coder->sequence = SEQ_BLOCK_INIT;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_BLOCK_INIT: {
 		// Checking memusage and doing the initialization needs
 		// its own sequence point because we need to be able to
@@ -252,10 +251,9 @@
 			return ret;
 
 		coder->sequence = SEQ_BLOCK_RUN;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_BLOCK_RUN: {
 		const lzma_ret ret = coder->block_decoder.code(
 				coder->block_decoder.coder, allocator,
@@ -291,10 +289,9 @@
 			return ret;
 
 		coder->sequence = SEQ_STREAM_FOOTER;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_STREAM_FOOTER: {
 		// Copy the Stream Footer to the internal buffer.
 		lzma_bufcpy(in, in_pos, in_size, coder->buffer, &coder->pos,
@@ -331,9 +328,8 @@
 			return LZMA_STREAM_END;
 
 		coder->sequence = SEQ_STREAM_PADDING;
+		FALLTHROUGH;
 	}
-
-	// Fall through
 
 	case SEQ_STREAM_PADDING:
 		assert(coder->concatenated);
diff --git a/cbits/liblzma/common/stream_decoder_mt.c b/cbits/liblzma/common/stream_decoder_mt.c
--- a/cbits/liblzma/common/stream_decoder_mt.c
+++ b/cbits/liblzma/common/stream_decoder_mt.c
@@ -1077,9 +1077,9 @@
 
 		if (coder->tell_any_check)
 			return LZMA_GET_CHECK;
-	}
 
-	// Fall through
+		FALLTHROUGH;
+	}
 
 	case SEQ_BLOCK_HEADER: {
 		const size_t in_old = *in_pos;
@@ -1214,10 +1214,9 @@
 		}
 
 		coder->sequence = SEQ_BLOCK_INIT;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_BLOCK_INIT: {
 		// Check if decoding is possible at all with the current
 		// memlimit_stop which we must never exceed.
@@ -1303,10 +1302,9 @@
 		}
 
 		coder->sequence = SEQ_BLOCK_THR_INIT;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_BLOCK_THR_INIT: {
 		// We need to wait for a multiple conditions to become true
 		// until we can initialize the Block decoder and let a worker
@@ -1508,10 +1506,9 @@
 		}
 
 		coder->sequence = SEQ_BLOCK_THR_RUN;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_BLOCK_THR_RUN: {
 		if (action == LZMA_FINISH && coder->fail_fast) {
 			// We know that we won't get more input and that
@@ -1613,10 +1610,9 @@
 		coder->mem_direct_mode = coder->mem_next_filters;
 
 		coder->sequence = SEQ_BLOCK_DIRECT_RUN;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_BLOCK_DIRECT_RUN: {
 		const size_t in_old = *in_pos;
 		const size_t out_old = *out_pos;
@@ -1652,8 +1648,7 @@
 			return LZMA_OK;
 
 		coder->sequence = SEQ_INDEX_DECODE;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_INDEX_DECODE: {
 		// If we don't have any input, don't call
@@ -1672,10 +1667,9 @@
 			return ret;
 
 		coder->sequence = SEQ_STREAM_FOOTER;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_STREAM_FOOTER: {
 		// Copy the Stream Footer to the internal buffer.
 		const size_t in_old = *in_pos;
@@ -1714,9 +1708,8 @@
 			return LZMA_STREAM_END;
 
 		coder->sequence = SEQ_STREAM_PADDING;
+		FALLTHROUGH;
 	}
-
-	// Fall through
 
 	case SEQ_STREAM_PADDING:
 		assert(coder->concatenated);
diff --git a/cbits/liblzma/common/stream_encoder_mt.c b/cbits/liblzma/common/stream_encoder_mt.c
--- a/cbits/liblzma/common/stream_encoder_mt.c
+++ b/cbits/liblzma/common/stream_encoder_mt.c
@@ -731,8 +731,7 @@
 
 		coder->header_pos = 0;
 		coder->sequence = SEQ_BLOCK;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_BLOCK: {
 		// Initialized to silence warnings.
@@ -851,9 +850,9 @@
 		// to be ready to be copied out.
 		coder->progress_out += lzma_index_size(coder->index)
 				+ LZMA_STREAM_HEADER_SIZE;
-	}
 
-	// Fall through
+		FALLTHROUGH;
+	}
 
 	case SEQ_INDEX: {
 		// Call the Index encoder. It doesn't take any input, so
@@ -873,9 +872,8 @@
 			return LZMA_PROG_ERROR;
 
 		coder->sequence = SEQ_STREAM_FOOTER;
+		FALLTHROUGH;
 	}
-
-	// Fall through
 
 	case SEQ_STREAM_FOOTER:
 		lzma_bufcpy(coder->header, &coder->header_pos,
diff --git a/cbits/liblzma/common/string_conversion.c b/cbits/liblzma/common/string_conversion.c
--- a/cbits/liblzma/common/string_conversion.c
+++ b/cbits/liblzma/common/string_conversion.c
@@ -12,6 +12,11 @@
 #include "filter_common.h"
 
 
+// liblzma itself doesn't use gettext to translate messages.
+// Mark the strings still so that xz can translate them.
+#define N_(msgid) msgid
+
+
 /////////////////////
 // String building //
 /////////////////////
@@ -319,7 +324,7 @@
 	assert(*str < str_end);
 
 	if (!(**str >= '0' && **str <= '9'))
-		return "Unsupported preset";
+		return N_("Unsupported preset");
 
 	*preset = (uint32_t)(**str - '0');
 
@@ -331,7 +336,7 @@
 			break;
 
 		default:
-			return "Unsupported preset flag";
+			return N_("Unsupported flag in the preset");
 		}
 	}
 
@@ -350,7 +355,7 @@
 
 	lzma_options_lzma *opts = filter_options;
 	if (lzma_lzma_preset(opts, preset))
-		return "Unsupported preset";
+		return N_("Unsupported preset");
 
 	return NULL;
 }
@@ -442,7 +447,7 @@
 		return errmsg;
 
 	if (opts->lc + opts->lp > LZMA_LCLP_MAX)
-		return "The sum of lc and lp must not exceed 4";
+		return N_("The sum of lc and lp must not exceed 4");
 
 	return NULL;
 }
@@ -578,21 +583,21 @@
 		// Fail if the '=' wasn't found or the option name is missing
 		// (the first char is '=').
 		if (equals_sign == NULL || **str == '=')
-			return "Options must be 'name=value' pairs separated "
-					"with commas";
+			return N_("Options must be 'name=value' pairs "
+					"separated with commas");
 
 		// Reject a too long option name so that the memcmp()
 		// in the loop below won't read past the end of the
 		// string in optmap[i].name.
 		const size_t name_len = (size_t)(equals_sign - *str);
 		if (name_len > NAME_LEN_MAX)
-			return "Unknown option name";
+			return N_("Unknown option name");
 
 		// Find the option name from optmap[].
 		size_t i = 0;
 		while (true) {
 			if (i == optmap_size)
-				return "Unknown option name";
+				return N_("Unknown option name");
 
 			if (memcmp(*str, optmap[i].name, name_len) == 0
 					&& optmap[i].name[name_len] == '\0')
@@ -609,7 +614,7 @@
 		// string so check it here.
 		const size_t value_len = (size_t)(name_eq_value_end - *str);
 		if (value_len == 0)
-			return "Option value cannot be empty";
+			return N_("Option value cannot be empty");
 
 		// LZMA1/2 preset has its own parsing function.
 		if (optmap[i].type == OPTMAP_TYPE_LZMA_PRESET) {
@@ -630,14 +635,14 @@
 			// in the loop below won't read past the end of the
 			// string in optmap[i].u.map[j].name.
 			if (value_len > NAME_LEN_MAX)
-				return "Invalid option value";
+				return N_("Invalid option value");
 
 			const name_value_map *map = optmap[i].u.map;
 			size_t j = 0;
 			while (true) {
 				// The array is terminated with an empty name.
 				if (map[j].name[0] == '\0')
-					return "Invalid option value";
+					return N_("Invalid option value");
 
 				if (memcmp(*str, map[j].name, value_len) == 0
 						&& map[j].name[value_len]
@@ -651,7 +656,8 @@
 		} else if (**str < '0' || **str > '9') {
 			// Note that "max" isn't supported while it is
 			// supported in xz. It's not useful here.
-			return "Value is not a non-negative decimal integer";
+			return N_("Value is not a non-negative "
+					"decimal integer");
 		} else {
 			// strtoul() has locale-specific behavior so it cannot
 			// be relied on to get reproducible results since we
@@ -665,13 +671,13 @@
 			v = 0;
 			do {
 				if (v > UINT32_MAX / 10)
-					return "Value out of range";
+					return N_("Value out of range");
 
 				v *= 10;
 
 				const uint32_t add = (uint32_t)(*p - '0');
 				if (UINT32_MAX - add < v)
-					return "Value out of range";
+					return N_("Value out of range");
 
 				v += add;
 				++p;
@@ -696,8 +702,9 @@
 				if ((optmap[i].flags & OPTMAP_USE_BYTE_SUFFIX)
 						== 0) {
 					*str = multiplier_start;
-					return "This option does not support "
-						"any integer suffixes";
+					return N_("This option does not "
+						"support any multiplier "
+						"suffixes");
 				}
 
 				uint32_t shift;
@@ -720,8 +727,13 @@
 
 				default:
 					*str = multiplier_start;
-					return "Invalid multiplier suffix "
-							"(KiB, MiB, or GiB)";
+
+					// TRANSLATORS: Don't translate the
+					// suffixes "KiB", "MiB", or "GiB"
+					// because a user can only specify
+					// untranslated suffixes.
+					return N_("Invalid multiplier suffix "
+							"(KiB, MiB, or GiB)");
 				}
 
 				++p;
@@ -740,19 +752,19 @@
 				// Now we must have no chars remaining.
 				if (p < name_eq_value_end) {
 					*str = multiplier_start;
-					return "Invalid multiplier suffix "
-							"(KiB, MiB, or GiB)";
+					return N_("Invalid multiplier suffix "
+							"(KiB, MiB, or GiB)");
 				}
 
 				if (v > (UINT32_MAX >> shift))
-					return "Value out of range";
+					return N_("Value out of range");
 
 				v <<= shift;
 			}
 
 			if (v < optmap[i].u.range.min
 					|| v > optmap[i].u.range.max)
-				return "Value out of range";
+				return N_("Value out of range");
 		}
 
 		// Set the value in filter_options. Enums are handled
@@ -814,15 +826,15 @@
 	// string in filter_name_map[i].name.
 	const size_t name_len = (size_t)(name_end - *str);
 	if (name_len > NAME_LEN_MAX)
-		return "Unknown filter name";
+		return N_("Unknown filter name");
 
 	for (size_t i = 0; i < ARRAY_SIZE(filter_name_map); ++i) {
 		if (memcmp(*str, filter_name_map[i].name, name_len) == 0
 				&& filter_name_map[i].name[name_len] == '\0') {
 			if (only_xz && filter_name_map[i].id
 					>= LZMA_FILTER_RESERVED_START)
-				return "This filter cannot be used in "
-						"the .xz format";
+				return N_("This filter cannot be used in "
+						"the .xz format");
 
 			// Allocate the filter-specific options and
 			// initialize the memory with zeros.
@@ -830,7 +842,7 @@
 					filter_name_map[i].opts_size,
 					allocator);
 			if (options == NULL)
-				return "Memory allocation failed";
+				return N_("Memory allocation failed");
 
 			// Filter name was found so the input string is good
 			// at least this far.
@@ -850,7 +862,7 @@
 		}
 	}
 
-	return "Unknown filter name";
+	return N_("Unknown filter name");
 }
 
 
@@ -869,8 +881,8 @@
 		++*str;
 
 	if (**str == '\0')
-		return "Empty string is not allowed, "
-				"try \"6\" if a default value is needed";
+		return N_("Empty string is not allowed, "
+				"try '6' if a default value is needed");
 
 	// Detect the type of the string.
 	//
@@ -893,7 +905,7 @@
 			// there are no chars other than spaces.
 			for (size_t i = 1; str_end[i] != '\0'; ++i)
 				if (str_end[i] != ' ')
-					return "Unsupported preset";
+					return N_("Unsupported preset");
 		} else {
 			// There are no trailing spaces. Use the whole string.
 			str_end = *str + str_len;
@@ -906,11 +918,11 @@
 
 		lzma_options_lzma *opts = lzma_alloc(sizeof(*opts), allocator);
 		if (opts == NULL)
-			return "Memory allocation failed";
+			return N_("Memory allocation failed");
 
 		if (lzma_lzma_preset(opts, preset)) {
 			lzma_free(opts, allocator);
-			return "Unsupported preset";
+			return N_("Unsupported preset");
 		}
 
 		filters[0].id = LZMA_FILTER_LZMA2;
@@ -934,7 +946,7 @@
 	size_t i = 0;
 	do {
 		if (i == LZMA_FILTERS_MAX) {
-			errmsg = "The maximum number of filters is four";
+			errmsg = N_("The maximum number of filters is four");
 			goto error;
 		}
 
@@ -956,7 +968,7 @@
 		// Inputs that have "--" at the end or "-- " in the middle
 		// will result in an empty filter name.
 		if (filter_end == *str) {
-			errmsg = "Filter name is missing";
+			errmsg = N_("Filter name is missing");
 			goto error;
 		}
 
@@ -983,8 +995,8 @@
 		const lzma_ret ret = lzma_validate_chain(temp_filters, &dummy);
 		assert(ret == LZMA_OK || ret == LZMA_OPTIONS_ERROR);
 		if (ret != LZMA_OK) {
-			errmsg = "Invalid filter chain "
-					"('lzma2' missing at the end?)";
+			errmsg = N_("Invalid filter chain "
+					"('lzma2' missing at the end?)");
 			goto error;
 		}
 	}
@@ -1012,17 +1024,26 @@
 	if (error_pos != NULL)
 		*error_pos = 0;
 
-	if (str == NULL || filters == NULL)
+	if (str == NULL || filters == NULL) {
+		// Don't translate this because it's only shown in case of
+		// a programming error.
 		return "Unexpected NULL pointer argument(s) "
 				"to lzma_str_to_filters()";
+	}
 
 	// Validate the flags.
 	const uint32_t supported_flags
 			= LZMA_STR_ALL_FILTERS
 			| LZMA_STR_NO_VALIDATION;
 
-	if (flags & ~supported_flags)
+	if (flags & ~supported_flags) {
+		// This message is possible only if the caller uses flags
+		// that are only supported in a newer liblzma version (or
+		// the flags are simply buggy). Don't translate this at least
+		// when liblzma itself doesn't use gettext; xz and liblzma
+		// are usually upgraded at the same time.
 		return "Unsupported flags to lzma_str_to_filters()";
+	}
 
 	const char *used = str;
 	const char *errmsg = str_to_filters(&used, filters, flags, allocator);
diff --git a/cbits/liblzma/lz/lz_decoder.c b/cbits/liblzma/lz/lz_decoder.c
--- a/cbits/liblzma/lz/lz_decoder.c
+++ b/cbits/liblzma/lz/lz_decoder.c
@@ -53,9 +53,9 @@
 static void
 lz_decoder_reset(lzma_coder *coder)
 {
-	coder->dict.pos = 2 * LZ_DICT_REPEAT_MAX;
+	coder->dict.pos = LZ_DICT_INIT_POS;
 	coder->dict.full = 0;
-	coder->dict.buf[2 * LZ_DICT_REPEAT_MAX - 1] = '\0';
+	coder->dict.buf[LZ_DICT_INIT_POS - 1] = '\0';
 	coder->dict.has_wrapped = false;
 	coder->dict.need_reset = false;
 	return;
@@ -261,10 +261,12 @@
 	// recommended to give aligned buffers to liblzma.
 	//
 	// Reserve 2 * LZ_DICT_REPEAT_MAX bytes of extra space which is
-	// needed for alloc_size.
+	// needed for alloc_size. Reserve also LZ_DICT_EXTRA bytes of extra
+	// space which is *not* counted in alloc_size or coder->dict.size.
 	//
 	// Avoid integer overflow.
-	if (lz_options.dict_size > SIZE_MAX - 15 - 2 * LZ_DICT_REPEAT_MAX)
+	if (lz_options.dict_size > SIZE_MAX - 15 - 2 * LZ_DICT_REPEAT_MAX
+			- LZ_DICT_EXTRA)
 		return LZMA_MEM_ERROR;
 
 	lz_options.dict_size = (lz_options.dict_size + 15) & ~((size_t)(15));
@@ -277,7 +279,13 @@
 	// Allocate and initialize the dictionary.
 	if (coder->dict.size != alloc_size) {
 		lzma_free(coder->dict.buf, allocator);
-		coder->dict.buf = lzma_alloc(alloc_size, allocator);
+
+		// The LZ_DICT_EXTRA bytes at the end of the buffer aren't
+		// included in alloc_size. These extra bytes allow
+		// dict_repeat() to read and write more data than requested.
+		// Otherwise this extra space is ignored.
+		coder->dict.buf = lzma_alloc(alloc_size + LZ_DICT_EXTRA,
+				allocator);
 		if (coder->dict.buf == NULL)
 			return LZMA_MEM_ERROR;
 
@@ -320,5 +328,6 @@
 extern uint64_t
 lzma_lz_decoder_memusage(size_t dictionary_size)
 {
-	return sizeof(lzma_coder) + (uint64_t)(dictionary_size);
+	return sizeof(lzma_coder) + (uint64_t)(dictionary_size)
+			+ 2 * LZ_DICT_REPEAT_MAX + LZ_DICT_EXTRA;
 }
diff --git a/cbits/liblzma/lz/lz_decoder.h b/cbits/liblzma/lz/lz_decoder.h
--- a/cbits/liblzma/lz/lz_decoder.h
+++ b/cbits/liblzma/lz/lz_decoder.h
@@ -15,11 +15,41 @@
 
 #include "common.h"
 
+#ifdef HAVE_IMMINTRIN_H
+#	include <immintrin.h>
+#endif
 
-/// Maximum length of a match rounded up to a nice power of 2 which is
-/// a good size for aligned memcpy(). The allocated dictionary buffer will
-/// be 2 * LZ_DICT_REPEAT_MAX bytes larger than the actual dictionary size:
+
+// dict_repeat() implementation variant:
+// 0 = Byte-by-byte copying only.
+// 1 = Use memcpy() for non-overlapping copies.
+// 2 = Use x86 SSE2 for non-overlapping copies.
+#ifndef LZMA_LZ_DECODER_CONFIG
+#	if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
+		&& defined(HAVE_IMMINTRIN_H) \
+		&& (defined(__SSE2__) || defined(_M_X64) \
+			|| (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
+#		define LZMA_LZ_DECODER_CONFIG 2
+#	else
+#		define LZMA_LZ_DECODER_CONFIG 1
+#	endif
+#endif
+
+/// Byte-by-byte and memcpy() copy exactly the amount needed. Other methods
+/// can copy up to LZ_DICT_EXTRA bytes more than requested, and this amount
+/// of extra space is needed at the end of the allocated dictionary buffer.
 ///
+/// NOTE: If this is increased, update LZMA_DICT_REPEAT_MAX too.
+#if LZMA_LZ_DECODER_CONFIG >= 2
+#	define LZ_DICT_EXTRA 32
+#else
+#	define LZ_DICT_EXTRA 0
+#endif
+
+/// Maximum number of bytes that dict_repeat() may copy. The allocated
+/// dictionary buffer will be 2 * LZ_DICT_REPEAT_MAX + LZMA_DICT_EXTRA bytes
+/// larger than the actual dictionary size:
+///
 /// (1) Every time the decoder reaches the end of the dictionary buffer,
 ///     the last LZ_DICT_REPEAT_MAX bytes will be copied to the beginning.
 ///     This way dict_repeat() will only need to copy from one place,
@@ -27,15 +57,27 @@
 ///
 /// (2) The other LZ_DICT_REPEAT_MAX bytes is kept as a buffer between
 ///     the oldest byte still in the dictionary and the current write
-///     position. This way dict_repeat(dict, dict->size - 1, &len)
+///     position. This way dict_repeat() with the maximum valid distance
 ///     won't need memmove() as the copying cannot overlap.
 ///
+/// (3) LZ_DICT_EXTRA bytes are required at the end of the dictionary buffer
+///     so that extra copying done by dict_repeat() won't write or read past
+///     the end of the allocated buffer. This amount is *not* counted as part
+///     of lzma_dict.size.
+///
 /// Note that memcpy() still cannot be used if distance < len.
 ///
-/// LZMA's longest match length is 273 so pick a multiple of 16 above that.
+/// LZMA's longest match length is 273 bytes. The LZMA decoder looks at
+/// the lowest four bits of the dictionary position, thus 273 must be
+/// rounded up to the next multiple of 16 (288). In addition, optimized
+/// dict_repeat() copies 32 bytes at a time, thus this must also be
+/// a multiple of 32.
 #define LZ_DICT_REPEAT_MAX 288
 
+/// Initial position in lzma_dict.buf when the dictionary is empty.
+#define LZ_DICT_INIT_POS (2 * LZ_DICT_REPEAT_MAX)
 
+
 typedef struct {
 	/// Pointer to the dictionary buffer.
 	uint8_t *buf;
@@ -158,7 +200,8 @@
 
 /// Repeat *len bytes at distance.
 static inline bool
-dict_repeat(lzma_dict *dict, uint32_t distance, uint32_t *len)
+dict_repeat(lzma_dict *restrict dict,
+		uint32_t distance, uint32_t *restrict len)
 {
 	// Don't write past the end of the dictionary.
 	const size_t dict_avail = dict->limit - dict->pos;
@@ -169,9 +212,17 @@
 	if (distance >= dict->pos)
 		back += dict->size - LZ_DICT_REPEAT_MAX;
 
-	// Repeat a block of data from the history. Because memcpy() is faster
-	// than copying byte by byte in a loop, the copying process gets split
-	// into two cases.
+#if LZMA_LZ_DECODER_CONFIG == 0
+	// Minimal byte-by-byte method. This might be the least bad choice
+	// if memcpy() isn't fast and there's no replacement for it below.
+	while (left-- > 0) {
+		dict->buf[dict->pos++] = dict->buf[back++];
+	}
+
+#else
+	// Because memcpy() or a similar method can be faster than copying
+	// byte by byte in a loop, the copying process is split into
+	// two cases.
 	if (distance < left) {
 		// Source and target areas overlap, thus we can't use
 		// memcpy() nor even memmove() safely.
@@ -179,32 +230,56 @@
 			dict->buf[dict->pos++] = dict->buf[back++];
 		} while (--left > 0);
 	} else {
+#	if LZMA_LZ_DECODER_CONFIG == 1
 		memcpy(dict->buf + dict->pos, dict->buf + back, left);
 		dict->pos += left;
+
+#	elif LZMA_LZ_DECODER_CONFIG == 2
+		// This can copy up to 32 bytes more than required.
+		// (If left == 0, we still copy 32 bytes.)
+		size_t pos = dict->pos;
+		dict->pos += left;
+		do {
+			const __m128i x0 = _mm_loadu_si128(
+					(__m128i *)(dict->buf + back));
+			const __m128i x1 = _mm_loadu_si128(
+					(__m128i *)(dict->buf + back + 16));
+			back += 32;
+			_mm_storeu_si128(
+					(__m128i *)(dict->buf + pos), x0);
+			_mm_storeu_si128(
+					(__m128i *)(dict->buf + pos + 16), x1);
+			pos += 32;
+		} while (pos < dict->pos);
+
+#	else
+#		error "Invalid LZMA_LZ_DECODER_CONFIG value"
+#	endif
 	}
+#endif
 
 	// Update how full the dictionary is.
 	if (!dict->has_wrapped)
-		dict->full = dict->pos - 2 * LZ_DICT_REPEAT_MAX;
+		dict->full = dict->pos - LZ_DICT_INIT_POS;
 
 	return *len != 0;
 }
 
 
 static inline void
-dict_put(lzma_dict *dict, uint8_t byte)
+dict_put(lzma_dict *restrict dict, uint8_t byte)
 {
 	dict->buf[dict->pos++] = byte;
 
 	if (!dict->has_wrapped)
-		dict->full = dict->pos - 2 * LZ_DICT_REPEAT_MAX;
+		dict->full = dict->pos - LZ_DICT_INIT_POS;
 }
 
 
 /// Puts one byte into the dictionary. Returns true if the dictionary was
 /// already full and the byte couldn't be added.
 static inline bool
-dict_put_safe(lzma_dict *dict, uint8_t byte)
+dict_put_safe(lzma_dict *restrict dict, uint8_t byte)
 {
 	if (unlikely(dict->pos == dict->limit))
 		return true;
@@ -234,7 +309,7 @@
 			dict->buf, &dict->pos, dict->limit);
 
 	if (!dict->has_wrapped)
-		dict->full = dict->pos - 2 * LZ_DICT_REPEAT_MAX;
+		dict->full = dict->pos - LZ_DICT_INIT_POS;
 
 	return;
 }
diff --git a/cbits/liblzma/lz/lz_encoder.c b/cbits/liblzma/lz/lz_encoder.c
--- a/cbits/liblzma/lz/lz_encoder.c
+++ b/cbits/liblzma/lz/lz_encoder.c
@@ -15,7 +15,7 @@
 
 // See lz_encoder_hash.h. This is a bit hackish but avoids making
 // endianness a conditional in makefiles.
-#if defined(WORDS_BIGENDIAN) && !defined(HAVE_SMALL)
+#ifdef LZMA_LZ_HASH_TABLE_IS_NEEDED
 #	include "lz_encoder_hash_table.h"
 #endif
 
diff --git a/cbits/liblzma/lz/lz_encoder_hash.h b/cbits/liblzma/lz/lz_encoder_hash.h
--- a/cbits/liblzma/lz/lz_encoder_hash.h
+++ b/cbits/liblzma/lz/lz_encoder_hash.h
@@ -5,23 +5,37 @@
 /// \file       lz_encoder_hash.h
 /// \brief      Hash macros for match finders
 //
-//  Author:     Igor Pavlov
+//  Authors:    Igor Pavlov
+//              Lasse Collin
 //
 ///////////////////////////////////////////////////////////////////////////////
 
 #ifndef LZMA_LZ_ENCODER_HASH_H
 #define LZMA_LZ_ENCODER_HASH_H
 
-#if defined(WORDS_BIGENDIAN) && !defined(HAVE_SMALL)
-	// This is to make liblzma produce the same output on big endian
-	// systems that it does on little endian systems. lz_encoder.c
-	// takes care of including the actual table.
+// We need to know if CRC32_GENERIC is defined and we may need the declaration
+// of lzma_crc32_table[][].
+#include "crc_common.h"
+
+// If HAVE_SMALL is defined, then lzma_crc32_table[][] exists and
+// it's little endian even on big endian systems.
+//
+// If HAVE_SMALL isn't defined, lzma_crc32_table[][] is in native endian
+// but we want a little endian one so that the compressed output won't
+// depend on the processor endianness. Big endian systems are less common
+// so those get the burden of an extra 1 KiB table.
+//
+// If HAVE_SMALL isn't defined and CRC32_GENERIC isn't defined either,
+// then lzma_crc32_table[][] doesn't exist.
+#if defined(HAVE_SMALL) \
+		|| (defined(CRC32_GENERIC) && !defined(WORDS_BIGENDIAN))
+#	define hash_table lzma_crc32_table[0]
+#else
+	// lz_encoder.c takes care of including the actual table.
 	lzma_attr_visibility_hidden
 	extern const uint32_t lzma_lz_hash_table[256];
 #	define hash_table lzma_lz_hash_table
-#else
-#	include "check.h"
-#	define hash_table lzma_crc32_table[0]
+#	define LZMA_LZ_HASH_TABLE_IS_NEEDED 1
 #endif
 
 #define HASH_2_SIZE (UINT32_C(1) << 10)
diff --git a/cbits/liblzma/lzma/lzma2_encoder.c b/cbits/liblzma/lzma/lzma2_encoder.c
--- a/cbits/liblzma/lzma/lzma2_encoder.c
+++ b/cbits/liblzma/lzma/lzma2_encoder.c
@@ -159,8 +159,7 @@
 		coder->uncompressed_size = 0;
 		coder->compressed_size = 0;
 		coder->sequence = SEQ_LZMA_ENCODE;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_LZMA_ENCODE: {
 		// Calculate how much more uncompressed data this chunk
@@ -219,10 +218,9 @@
 		lzma2_header_lzma(coder);
 
 		coder->sequence = SEQ_LZMA_COPY;
+		FALLTHROUGH;
 	}
 
-	// Fall through
-
 	case SEQ_LZMA_COPY:
 		// Copy the compressed chunk along its headers to the
 		// output buffer.
@@ -244,8 +242,7 @@
 			return LZMA_OK;
 
 		coder->sequence = SEQ_UNCOMPRESSED_COPY;
-
-	// Fall through
+		FALLTHROUGH;
 
 	case SEQ_UNCOMPRESSED_COPY:
 		// Copy the uncompressed data as is from the dictionary
diff --git a/cbits/liblzma/simple/arm.c b/cbits/liblzma/simple/arm.c
--- a/cbits/liblzma/simple/arm.c
+++ b/cbits/liblzma/simple/arm.c
@@ -18,8 +18,10 @@
 		uint32_t now_pos, bool is_encoder,
 		uint8_t *buffer, size_t size)
 {
+	size &= ~(size_t)3;
+
 	size_t i;
-	for (i = 0; i + 4 <= size; i += 4) {
+	for (i = 0; i < size; i += 4) {
 		if (buffer[i + 3] == 0xEB) {
 			uint32_t src = ((uint32_t)(buffer[i + 2]) << 16)
 					| ((uint32_t)(buffer[i + 1]) << 8)
diff --git a/cbits/liblzma/simple/arm64.c b/cbits/liblzma/simple/arm64.c
--- a/cbits/liblzma/simple/arm64.c
+++ b/cbits/liblzma/simple/arm64.c
@@ -28,6 +28,8 @@
 		uint32_t now_pos, bool is_encoder,
 		uint8_t *buffer, size_t size)
 {
+	size &= ~(size_t)3;
+
 	size_t i;
 
 	// Clang 14.0.6 on x86-64 makes this four times bigger and 40 % slower
@@ -37,7 +39,7 @@
 #ifdef __clang__
 #	pragma clang loop vectorize(disable)
 #endif
-	for (i = 0; i + 4 <= size; i += 4) {
+	for (i = 0; i < size; i += 4) {
 		uint32_t pc = (uint32_t)(now_pos + i);
 		uint32_t instr = read32le(buffer + i);
 
@@ -122,6 +124,15 @@
 {
 	return arm64_coder_init(next, allocator, filters, true);
 }
+
+
+extern LZMA_API(size_t)
+lzma_bcj_arm64_encode(uint32_t start_offset, uint8_t *buf, size_t size)
+{
+	// start_offset must be a multiple of four.
+	start_offset &= ~UINT32_C(3);
+	return arm64_code(NULL, start_offset, true, buf, size);
+}
 #endif
 
 
@@ -132,5 +143,14 @@
 		const lzma_filter_info *filters)
 {
 	return arm64_coder_init(next, allocator, filters, false);
+}
+
+
+extern LZMA_API(size_t)
+lzma_bcj_arm64_decode(uint32_t start_offset, uint8_t *buf, size_t size)
+{
+	// start_offset must be a multiple of four.
+	start_offset &= ~UINT32_C(3);
+	return arm64_code(NULL, start_offset, false, buf, size);
 }
 #endif
diff --git a/cbits/liblzma/simple/armthumb.c b/cbits/liblzma/simple/armthumb.c
--- a/cbits/liblzma/simple/armthumb.c
+++ b/cbits/liblzma/simple/armthumb.c
@@ -18,8 +18,13 @@
 		uint32_t now_pos, bool is_encoder,
 		uint8_t *buffer, size_t size)
 {
+	if (size < 4)
+		return 0;
+
+	size -= 4;
+
 	size_t i;
-	for (i = 0; i + 4 <= size; i += 2) {
+	for (i = 0; i <= size; i += 2) {
 		if ((buffer[i + 1] & 0xF8) == 0xF0
 				&& (buffer[i + 3] & 0xF8) == 0xF8) {
 			uint32_t src = (((uint32_t)(buffer[i + 1]) & 7) << 19)
diff --git a/cbits/liblzma/simple/ia64.c b/cbits/liblzma/simple/ia64.c
--- a/cbits/liblzma/simple/ia64.c
+++ b/cbits/liblzma/simple/ia64.c
@@ -25,8 +25,10 @@
 		4, 4, 0, 0, 4, 4, 0, 0
 	};
 
+	size &= ~(size_t)15;
+
 	size_t i;
-	for (i = 0; i + 16 <= size; i += 16) {
+	for (i = 0; i < size; i += 16) {
 		const uint32_t instr_template = buffer[i] & 0x1F;
 		const uint32_t mask = BRANCH_TABLE[instr_template];
 		uint32_t bit_pos = 5;
diff --git a/cbits/liblzma/simple/powerpc.c b/cbits/liblzma/simple/powerpc.c
--- a/cbits/liblzma/simple/powerpc.c
+++ b/cbits/liblzma/simple/powerpc.c
@@ -18,8 +18,10 @@
 		uint32_t now_pos, bool is_encoder,
 		uint8_t *buffer, size_t size)
 {
+	size &= ~(size_t)3;
+
 	size_t i;
-	for (i = 0; i + 4 <= size; i += 4) {
+	for (i = 0; i < size; i += 4) {
 		// PowerPC branch 6(48) 24(Offset) 1(Abs) 1(Link)
 		if ((buffer[i] >> 2) == 0x12
 				&& ((buffer[i + 3] & 3) == 1)) {
diff --git a/cbits/liblzma/simple/riscv.c b/cbits/liblzma/simple/riscv.c
--- a/cbits/liblzma/simple/riscv.c
+++ b/cbits/liblzma/simple/riscv.c
@@ -617,6 +617,15 @@
 	return lzma_simple_coder_init(next, allocator, filters,
 			&riscv_encode, 0, 8, 2, true);
 }
+
+
+extern LZMA_API(size_t)
+lzma_bcj_riscv_encode(uint32_t start_offset, uint8_t *buf, size_t size)
+{
+	// start_offset must be a multiple of two.
+	start_offset &= ~UINT32_C(1);
+	return riscv_encode(NULL, start_offset, true, buf, size);
+}
 #endif
 
 
@@ -751,5 +760,14 @@
 {
 	return lzma_simple_coder_init(next, allocator, filters,
 			&riscv_decode, 0, 8, 2, false);
+}
+
+
+extern LZMA_API(size_t)
+lzma_bcj_riscv_decode(uint32_t start_offset, uint8_t *buf, size_t size)
+{
+	// start_offset must be a multiple of two.
+	start_offset &= ~UINT32_C(1);
+	return riscv_decode(NULL, start_offset, false, buf, size);
 }
 #endif
diff --git a/cbits/liblzma/simple/sparc.c b/cbits/liblzma/simple/sparc.c
--- a/cbits/liblzma/simple/sparc.c
+++ b/cbits/liblzma/simple/sparc.c
@@ -18,9 +18,10 @@
 		uint32_t now_pos, bool is_encoder,
 		uint8_t *buffer, size_t size)
 {
-	size_t i;
-	for (i = 0; i + 4 <= size; i += 4) {
+	size &= ~(size_t)3;
 
+	size_t i;
+	for (i = 0; i < size; i += 4) {
 		if ((buffer[i] == 0x40 && (buffer[i + 1] & 0xC0) == 0x00)
 				|| (buffer[i] == 0x7F
 				&& (buffer[i + 1] & 0xC0) == 0xC0)) {
diff --git a/cbits/liblzma/simple/x86.c b/cbits/liblzma/simple/x86.c
--- a/cbits/liblzma/simple/x86.c
+++ b/cbits/liblzma/simple/x86.c
@@ -143,6 +143,18 @@
 {
 	return x86_coder_init(next, allocator, filters, true);
 }
+
+
+extern LZMA_API(size_t)
+lzma_bcj_x86_encode(uint32_t start_offset, uint8_t *buf, size_t size)
+{
+	lzma_simple_x86 simple = {
+		.prev_mask = 0,
+		.prev_pos = (uint32_t)(-5),
+	};
+
+	return x86_code(&simple, start_offset, true, buf, size);
+}
 #endif
 
 
@@ -153,5 +165,17 @@
 		const lzma_filter_info *filters)
 {
 	return x86_coder_init(next, allocator, filters, false);
+}
+
+
+extern LZMA_API(size_t)
+lzma_bcj_x86_decode(uint32_t start_offset, uint8_t *buf, size_t size)
+{
+	lzma_simple_x86 simple = {
+		.prev_mask = 0,
+		.prev_pos = (uint32_t)(-5),
+	};
+
+	return x86_code(&simple, start_offset, false, buf, size);
 }
 #endif
diff --git a/configure b/configure
--- a/configure
+++ b/configure
@@ -4903,22 +4903,22 @@
 else case e in #(
   e) gl_test_posix_shell_script='
        func_return () {
-	 (exit $1)
+         (exit $1)
        }
        func_success () {
-	 func_return 0
+         func_return 0
        }
        func_failure () {
-	 func_return 1
+         func_return 1
        }
        func_ret_success () {
-	 return 0
+         return 0
        }
        func_ret_failure () {
-	 return 1
+         return 1
        }
        subshell_umask_sanity () {
-	 (umask 22; (umask 0); test $(umask) -eq 22)
+         (umask 22; (umask 0); test $(umask) -eq 22)
        }
        test "$(echo foo)" = foo &&
        func_success &&
@@ -4929,11 +4929,11 @@
        subshell_umask_sanity
      '
      for gl_cv_posix_shell in \
-	 "$CONFIG_SHELL" "$SHELL" /bin/sh /bin/bash /bin/ksh /bin/sh5 no; do
+         "$CONFIG_SHELL" "$SHELL" /bin/sh /bin/bash /bin/ksh /bin/sh5 no; do
        case $gl_cv_posix_shell in
          /*)
-	   "$gl_cv_posix_shell" -c "$gl_test_posix_shell_script" 2>/dev/null \
-	     && break;;
+           "$gl_cv_posix_shell" -c "$gl_test_posix_shell_script" 2>/dev/null \
+             && break;;
        esac
      done ;;
 esac
@@ -20052,8 +20052,6 @@
 esac
 
 
-  gl_replace_getopt=
-
   if test -z "$gl_replace_getopt"; then
            for ac_header in getopt.h
 do :
@@ -21117,6 +21115,12 @@
 if test "x$ac_cv_func_wcwidth" = xyes
 then :
   printf "%s\n" "#define HAVE_WCWIDTH 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "vasprintf" "ac_cv_func_vasprintf"
+if test "x$ac_cv_func_vasprintf" = xyes
+then :
+  printf "%s\n" "#define HAVE_VASPRINTF 1" >>confdefs.h
 
 fi
 
diff --git a/xz-clib.cabal b/xz-clib.cabal
--- a/xz-clib.cabal
+++ b/xz-clib.cabal
@@ -1,6 +1,6 @@
 cabal-version:       2.2
 name:                xz-clib
-version:             5.6.4
+version:             5.8.0
 
 synopsis:            LZMA/XZ clibs
 description:         C source code for the LZMA/XZ compression and decompression library
@@ -54,12 +54,13 @@
   default-language:    Haskell2010
   c-sources:
     cbits/common/tuklib_cpucores.c
+    cbits/common/tuklib_mbstr_nonprint.c
+    cbits/common/tuklib_mbstr_wrap.c
     cbits/common/tuklib_physmem.c
     cbits/liblzma/check/check.c
     cbits/liblzma/check/crc32_fast.c
-    cbits/liblzma/check/crc32_table.c
     cbits/liblzma/check/crc64_fast.c
-    cbits/liblzma/check/crc64_table.c
+    cbits/liblzma/check/crc_clmul_consts_gen.c
     cbits/liblzma/check/sha256.c
     cbits/liblzma/common/alone_decoder.c
     cbits/liblzma/common/alone_encoder.c
