From 6421a039cebde023ad8fde81851e3a679e12915a Mon Sep 17 00:00:00 2001
From: magnum <magnum>
Date: Sat, 17 Sep 2011 00:51:51 +0200
Subject: [PATCH 7/8] UTF-16BE inline byte swapping for arch endianness

---
 src/sapG_fmt_plug.c |    3 +-
 src/unicode.c       |  183 +++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 142 insertions(+), 44 deletions(-)

diff --git a/src/sapG_fmt_plug.c b/src/sapG_fmt_plug.c
index fd59ed8..4507fa9 100644
--- a/src/sapG_fmt_plug.c
+++ b/src/sapG_fmt_plug.c
@@ -139,7 +139,8 @@ static void sapcodvng_set_key(char *key, int index)
 	// 0. translate candidates to UTF-8
 	if (options.utf8)
 		strnzcpy((char*)trPassword, key, PLAINTEXT_LENGTH + 1);
-	else { // convert from codepage -> Unicode -> UTF-8
+	else if (!options.encoding_7_bit) {
+		// convert from codepage -> Unicode -> UTF-8
 		UTF16 tmp16[PLAINTEXT_LENGTH + 1];
 #if ARCH_LITTLE_ENDIAN
 		enc_to_utf16(tmp16, PLAINTEXT_LENGTH + 1, (UTF8*)key, PLAINTEXT_LENGTH);
diff --git a/src/unicode.c b/src/unicode.c
index 32b41ee..8f723b0 100644
--- a/src/unicode.c
+++ b/src/unicode.c
@@ -186,15 +186,16 @@ static const UTF32 halfMask = 0x3FFUL;
 #define UNI_SUR_LOW_START   (UTF32)0xDC00
 #define UNI_SUR_LOW_END     (UTF32)0xDFFF
 
-// Convert UTF-8 to NT UNICODE (UTF-16LE) string. Note that regardless of
-// processor type this must be in intel (little-endian) format.
-// This code is optimised for speed. Errors result in truncation.
-//
-// Normally returns number of UTF16 characters converted. If truncated,
-// the number of UTF8 characters that was successfully read is returned
-// instead, so we can truncate our saved_plain or whatever accordingly.
-//
-// The original function in ConvertUTF.c is called ConvertUTF8toUTF16()
+/* Convert UTF-8 string to UTF-16LE, regardless of arch
+ *
+ * This code is optimised for speed. Errors result in truncation.
+ *
+ * Normally returns number of UTF16 characters converted. If truncated,
+ * the number of UTF8 characters that was successfully read is returned
+ * instead (negated), so we can truncate our saved_plain or whatever
+ * accordingly.
+ *
+ * The original function in ConvertUTF.c is called ConvertUTF8toUTF16() */
 inline int utf8_to_utf16(UTF16 *target, unsigned int len, const UTF8 *source,
                          unsigned int sourceLen)
 {
@@ -208,7 +209,7 @@ inline int utf8_to_utf16(UTF16 *target, unsigned int len, const UTF8 *source,
 	while (source < sourceEnd) {
 		if (*source < 0xC0) {
 #if ARCH_LITTLE_ENDIAN
-			*target++ = (UTF16) *source++;
+			*target++ = (UTF16)*source++;
 #else
 			UTF8 val = *source++;
 			SSVAL(target, 0, val);
@@ -252,7 +253,7 @@ inline int utf8_to_utf16(UTF16 *target, unsigned int len, const UTF8 *source,
 		ch -= offsetsFromUTF8[extraBytesToRead];
 #if 0 /* This only supports UCS-2 */
 #if ARCH_LITTLE_ENDIAN
-		*target++ = (UTF16) ch;
+		*target++ = (UTF16)ch;
 #else
 		SSVAL(target, 0, ch);
 		++target;
@@ -260,7 +261,7 @@ inline int utf8_to_utf16(UTF16 *target, unsigned int len, const UTF8 *source,
 #else /* This supports full UTF-16 with surrogate pairs */
 		if (ch <= UNI_MAX_BMP) {  /* Target is a character <= 0xFFFF */
 #if ARCH_LITTLE_ENDIAN
-			*target++ = (UTF16) ch;
+			*target++ = (UTF16)ch;
 #else
 			SSVAL(target, 0, ch);
 			++target;
@@ -294,17 +295,115 @@ inline int utf8_to_utf16(UTF16 *target, unsigned int len, const UTF8 *source,
 	return (target - targetStart);
 }
 
-// Convert a string into an NT UNICODE string. Note that regardless of
-// processor type this must be in intel (little-endian) format.
-//
-// This version converts from UTF-8 if the --encoding=utf8 option was given to John
-// and from the other character sets otherwise which is faster, since it is a simple
-// table lookup, vs computing wide characters.
-//
-//NOTE, Solaris did not build properly with this function inlined. Thus
-// the inline was removed (for now).
-//inline
-int enc_to_utf16_be(UTF16 *dst, unsigned int maxdstlen, const UTF8 *src, unsigned int srclen) {
+/* Convert to UTF-16BE instead, regardless of arch */
+inline int utf8_to_utf16_be(UTF16 *target, unsigned int len, const UTF8 *source,
+                            unsigned int sourceLen)
+{
+	const UTF16 *targetStart = target;
+	const UTF16 *targetEnd = target + len;
+	const UTF8 *sourceStart = source;
+	const UTF8 *sourceEnd = source + sourceLen;
+	UTF32 ch;
+	unsigned int extraBytesToRead;
+
+	while (source < sourceEnd) {
+		if (*source < 0xC0) {
+#if ARCH_LITTLE_ENDIAN
+			*target++ = (UTF16)*source++ << 8;
+#else
+			*target++ = (UTF16)*source++;
+#endif
+			if (*source == 0)
+				break;
+			if (target >= targetEnd) {
+				*target = 0;
+				return -1 * (source - sourceStart);
+			}
+			continue;
+		}
+		ch = *source;
+		// The original code in ConvertUTF.c has a much larger (slower)
+		// lookup table including zeros. This point must not be reached
+		// with *source < 0xC0
+		extraBytesToRead =
+		    opt_trailingBytesUTF8[ch & 0x3f];
+		if (source + extraBytesToRead >= sourceEnd) {
+			*target = 0;
+			return -1 * (source - sourceStart);
+		}
+		switch (extraBytesToRead) {
+		case 3:
+			ch <<= 6;
+			ch += *++source;
+		case 2:
+			ch <<= 6;
+			ch += *++source;
+		case 1:
+			ch <<= 6;
+			ch += *++source;
+		case 0:
+			++source;
+			break;
+		default:
+			*target = 0;
+			return -1 * (source - sourceStart);
+		}
+		ch -= offsetsFromUTF8[extraBytesToRead];
+#if 0 /* This only supports UCS-2 */
+#if ARCH_LITTLE_ENDIAN
+		*target++ = (UTF16)ch << 8 | (UTF16)ch >> 8;
+#else
+		*target++ = (UTF16)ch;
+#endif
+#else /* This supports full UTF-16 with surrogate pairs */
+		if (ch <= UNI_MAX_BMP) {  /* Target is a character <= 0xFFFF */
+#if ARCH_LITTLE_ENDIAN
+			*target++ = (UTF16)ch << 8 | (UTF16)ch >> 8;
+#else
+			*target++ = (UTF16)ch;
+#endif
+		} else {  /* target is a character in range 0xFFFF - 0x10FFFF. */
+			if (target + 1 >= targetEnd) {
+				source -= (extraBytesToRead+1); /* Back up source pointer! */
+				*target = 0;
+				return -1 * (source - sourceStart);
+			}
+			ch -= halfBase;
+#if ARCH_LITTLE_ENDIAN
+			*target = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+			*target = *target << 8 | *target >> 8;
+			target++;
+			*target = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+			*target = *target << 8 | *target >> 8;
+			target++;
+#else
+			*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+			*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+#endif
+		}
+#endif
+		if (*source == 0)
+			break;
+		if (target >= targetEnd) {
+			*target = 0;
+			return -1 * (source - sourceStart);
+		}
+	}
+	*target = 0;		// Null-terminate
+	return (target - targetStart);
+}
+
+/* Convert from current encoding to UTF-16LE regardless of system arch
+ *
+ * This version converts from UTF-8 if the --encoding=utf8 option was given to
+ * John and from the other character sets otherwise which is faster, since it
+ * is a simple table lookup, vs computing wide characters. */
+#ifndef __SUNPRO_C
+inline
+#endif
+int enc_to_utf16(UTF16 *dst, unsigned int maxdstlen, const UTF8 *src,
+                 unsigned int srclen)
+{
 	if (!(options.utf8)) {
 		int i, trunclen = (int)srclen;
 		if (trunclen > maxdstlen)
@@ -312,10 +411,11 @@ int enc_to_utf16_be(UTF16 *dst, unsigned int maxdstlen, const UTF8 *src, unsigne
 
 		for (i = 0; i < trunclen; i++) {
 #if ARCH_LITTLE_ENDIAN
-			*dst++ = CP_to_Unicode[*src] >> 8 | CP_to_Unicode[*src] << 8;
-			src++;
-#else
 			*dst++ = CP_to_Unicode[*src++];
+#else
+			UTF16 val = CP_to_Unicode[*src++];
+			SSVAL(dst, 0, val);
+			++dst;
 #endif
 		}
 		*dst = 0;
@@ -324,20 +424,18 @@ int enc_to_utf16_be(UTF16 *dst, unsigned int maxdstlen, const UTF8 *src, unsigne
 		else
 			return i;
 	} else {		// Convert from UTF-8
-		int len;
-
-		len = utf8_to_utf16(dst, maxdstlen, src, srclen);
-
-		// Byte-swap to UTF-16BE
-		while ((*dst = *dst >> 8 | *dst << 8))
-			dst++;
-
-		return len;
+		return utf8_to_utf16(dst, maxdstlen, src, srclen);
 	}
 }
-int enc_to_utf16(UTF16 *dst, unsigned int maxdstlen, const UTF8 *src,
-                 unsigned int srclen)
-{
+
+/* Convert from current codepage to UTF-16BE regardless of arch
+ * NOTE, Solaris did not build properly with this function inlined. Thus
+ * the inline was conditioned */
+#ifndef __SUNPRO_C
+inline
+#endif
+int enc_to_utf16_be(UTF16 *dst, unsigned int maxdstlen, const UTF8 *src,
+                    unsigned int srclen) {
 	if (!(options.utf8)) {
 		int i, trunclen = (int)srclen;
 		if (trunclen > maxdstlen)
@@ -345,11 +443,10 @@ int enc_to_utf16(UTF16 *dst, unsigned int maxdstlen, const UTF8 *src,
 
 		for (i = 0; i < trunclen; i++) {
 #if ARCH_LITTLE_ENDIAN
-			*dst++ = CP_to_Unicode[*src++];
+			*dst++ = CP_to_Unicode[*src] >> 8 | CP_to_Unicode[*src] << 8;
+			src++;
 #else
-			UTF16 val = CP_to_Unicode[*src++];
-			SSVAL(dst, 0, val);
-			++dst;
+			*dst++ = CP_to_Unicode[*src++];
 #endif
 		}
 		*dst = 0;
@@ -358,7 +455,7 @@ int enc_to_utf16(UTF16 *dst, unsigned int maxdstlen, const UTF8 *src,
 		else
 			return i;
 	} else {		// Convert from UTF-8
-		return utf8_to_utf16(dst, maxdstlen, src, srclen);
+		return utf8_to_utf16_be(dst, maxdstlen, src, srclen);
 	}
 }
 
-- 
1.7.4.1

