summary refs log tree commit diff
path: root/iconv
diff options
context:
space:
mode:
Diffstat (limited to 'iconv')
-rw-r--r--iconv/skeleton.c132
1 files changed, 123 insertions, 9 deletions
diff --git a/iconv/skeleton.c b/iconv/skeleton.c
index 726a76f00e..ad381ec77c 100644
--- a/iconv/skeleton.c
+++ b/iconv/skeleton.c
@@ -117,6 +117,73 @@ static int to_object;
 #endif
 
 
+/* Define macros which can access unaligned buffers.  These macros are
+   supposed to be used only in code outside the inner loops.  For the inner
+   loops we have other definitions which allow optimized access.  */
+#ifdef _STRING_ARCH_unaligned
+/* We can handle unaligned memory access.  */
+# define get16(addr) *((uint16_t *) (addr))
+# define get32(addr) *((uint32_t *) (addr))
+
+/* We need no special support for writing values either.  */
+# define put16(addr, val) *((uint16_t *) (addr)) = (val)
+# define put32(addr, val) *((uint32_t *) (addr)) = (val)
+#else
+/* Distinguish between big endian and little endian.  */
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+#  define get16(addr) \
+     (((__const unsigned char *) (addr))[1] << 8			      \
+      | ((__const unsigned char *) (addr))[0])
+#  define get32(addr) \
+     (((((__const unsigned char *) (addr))[3] << 8			      \
+	| ((__const unsigned char *) (addr))[2]) << 8			      \
+       | ((__const unsigned char *) (addr))[1]) << 8			      \
+      | ((__const unsigned char *) (addr))[0])
+
+# define put16(addr, val) \
+     ({ uint16_t __val = (val);						      \
+	((__const unsigned char *) (addr))[0] = __val;			      \
+	((__const unsigned char *) (addr))[1] = __val >> 8;		      \
+	(void) 0; })
+# define put32(addr, val) \
+     ({ uint16_t __val = (val);						      \
+	((__const unsigned char *) (addr))[0] = __val;			      \
+	__val >>= 8;							      \
+	((__const unsigned char *) (addr))[1] = __val;			      \
+	__val >>= 8;							      \
+	((__const unsigned char *) (addr))[2] = __val;			      \
+	__val >>= 8;							      \
+	((__const unsigned char *) (addr))[3] = __val;			      \
+	(void) 0; })
+# else
+#  define get16(addr) \
+     (((__const unsigned char *) (addr))[0] << 8			      \
+      | ((__const unsigned char *) (addr))[1])
+#  define get32(addr) \
+     (((((__const unsigned char *) (addr))[0] << 8			      \
+	| ((__const unsigned char *) (addr))[1]) << 8			      \
+       | ((__const unsigned char *) (addr))[2]) << 8			      \
+      | ((__const unsigned char *) (addr))[3])
+
+# define put16(addr, val) \
+     ({ uint16_t __val = (val);						      \
+	((__const unsigned char *) (addr))[1] = __val;			      \
+	((__const unsigned char *) (addr))[2] = __val >> 8;		      \
+	(void) 0; })
+# define put32(addr, val) \
+     ({ uint16_t __val = (val);						      \
+	((__const unsigned char *) (addr))[3] = __val;			      \
+	__val >>= 8;							      \
+	((__const unsigned char *) (addr))[2] = __val;			      \
+	__val >>= 8;							      \
+	((__const unsigned char *) (addr))[1] = __val;			      \
+	__val >>= 8;							      \
+	((__const unsigned char *) (addr))[0] = __val;			      \
+	(void) 0; })
+# endif
+#endif
+
+
 /* For conversions from a fixed width character sets to another fixed width
    character set we we can define RESET_INPUT_BUFFER is necessary.  */
 #if !defined RESET_INPUT_BUFFER && !defined SAVE_RESET_STATE
@@ -230,6 +297,33 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data,
       unsigned char *outbuf = data->__outbuf;
       unsigned char *outend = data->__outbufend;
       unsigned char *outstart;
+#ifdef _STRING_ARCH_unaligned
+# define unaligned 0
+#else
+      /* The following assumes that encodings, which have a variable length
+	 what might unalign a buffer even though it is a aligned in the
+	 beginning, either don't have the minimal number of bytes as a divisor
+	 of the maximum length or have a minimum length of 1.  This is true
+	 for all known and supported encodings.  */
+      int unaligned;
+
+      unaligned = ((FROM_DIRECTION
+		    && ((MIN_NEEDED_FROM > 1
+			 && MAX_NEEDED_FROM % MIN_NEEDED_FROM == 0
+			 && (uintptr_t) inptr % MIN_NEEDED_FROM != 0)
+			|| (MIN_NEEDED_TO > 1
+			    && MAX_NEEDED_TO % MIN_NEEDED_TO == 0
+			    && (uintptr_t) outbuf % MIN_NEEDED_TO != 0)))
+		   || (!FROM_DIRECTION
+		       && ((MIN_NEEDED_FROM > 1
+			    && MAX_NEEDED_FROM % MIN_NEEDED_FROM == 0
+			    && (uintptr_t) outbuf % MIN_NEEDED_FROM != 0)
+			   || (MIN_NEEDED_TO > 1
+			       && MAX_NEEDED_TO % MIN_NEEDED_TO == 0
+			       && (uintptr_t) inptr % MIN_NEEDED_TO != 0))));
+# define GEN_unaligned(name) GEN_unaligned2 (name)
+# define GEN_unaligned2(name) name##_unaligned
+#endif
 
       /* This variable is used to count the number of characters we
 	 actually converted.  */
@@ -250,16 +344,36 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data,
 	  SAVE_RESET_STATE (1);
 #endif
 
-	  if (FROM_DIRECTION)
-	    /* Run the conversion loop.  */
-	    status = FROM_LOOP (inbuf, inbufend, &outbuf, outend,
-				data->__statep, step->__data, &converted
-				EXTRA_LOOP_ARGS);
+	  if (!unaligned)
+	    {
+	      if (FROM_DIRECTION)
+		/* Run the conversion loop.  */
+		status = FROM_LOOP (inbuf, inbufend, &outbuf, outend,
+				    data->__statep, step->__data, &converted
+				    EXTRA_LOOP_ARGS);
+	      else
+		/* Run the conversion loop.  */
+		status = TO_LOOP (inbuf, inbufend, &outbuf, outend,
+				  data->__statep, step->__data, &converted
+				  EXTRA_LOOP_ARGS);
+	    }
+#ifndef _STRING_ARCH_unaligned
 	  else
-	    /* Run the conversion loop.  */
-	    status = TO_LOOP (inbuf, inbufend, &outbuf, outend,
-			      data->__statep, step->__data, &converted
-			      EXTRA_LOOP_ARGS);
+	    {
+	      if (FROM_DIRECTION)
+		/* Run the conversion loop.  */
+		status = GEN_unaligned (FROM_LOOP) (inbuf, inbufend, &outbuf,
+						    outend, data->__statep,
+						    step->__data, &converted
+						    EXTRA_LOOP_ARGS);
+	      else
+		/* Run the conversion loop.  */
+		status = GEN_unaligned (TO_LOOP) (inbuf, inbufend, &outbuf,
+						  outend, data->__statep,
+						  step->__data, &converted
+						  EXTRA_LOOP_ARGS);
+	    }
+#endif
 
 	  /* We finished one use of the loops.  */
 	  ++data->__invocation_counter;