diff options
Diffstat (limited to 'iconv/skeleton.c')
-rw-r--r-- | iconv/skeleton.c | 132 |
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; |