]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - lib/zlib_inflate/inffast.c
zlib: optimize inffast when copying direct from output
[karo-tx-linux.git] / lib / zlib_inflate / inffast.c
index 8550b0c05d00191d97521356ed7d6ed2e1a84f3f..05e1559fa156f3b2cc81e2f6af32b11b0ee061f3 100644 (file)
@@ -4,6 +4,8 @@
  */
 
 #include <linux/zutil.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
 #include "inftrees.h"
 #include "inflate.h"
 #include "inffast.h"
 #ifdef POSTINC
 #  define OFF 0
 #  define PUP(a) *(a)++
+#  define UP_UNALIGNED(a) get_unaligned((a)++)
 #else
 #  define OFF 1
 #  define PUP(a) *++(a)
+#  define UP_UNALIGNED(a) get_unaligned(++(a))
 #endif
 
 /*
@@ -239,18 +243,47 @@ void inflate_fast(z_streamp strm, unsigned start)
                     }
                 }
                 else {
+                   unsigned short *sout;
+                   unsigned long loops;
+
                     from = out - dist;          /* copy direct from output */
-                    do {                        /* minimum length is three */
-                        PUP(out) = PUP(from);
-                        PUP(out) = PUP(from);
-                        PUP(out) = PUP(from);
-                        len -= 3;
-                    } while (len > 2);
-                    if (len) {
-                        PUP(out) = PUP(from);
-                        if (len > 1)
-                            PUP(out) = PUP(from);
-                    }
+                   /* minimum length is three */
+                   /* Align out addr */
+                   if (!((long)(out - 1 + OFF) & 1)) {
+                       PUP(out) = PUP(from);
+                       len--;
+                   }
+                   sout = (unsigned short *)(out - OFF);
+                   if (dist > 2) {
+                       unsigned short *sfrom;
+
+                       sfrom = (unsigned short *)(from - OFF);
+                       loops = len >> 1;
+                       do
+                           PUP(sout) = UP_UNALIGNED(sfrom);
+                       while (--loops);
+                       out = (unsigned char *)sout + OFF;
+                       from = (unsigned char *)sfrom + OFF;
+                   } else { /* dist == 1 or dist == 2 */
+                       unsigned short pat16;
+
+                       pat16 = *(sout-2+2*OFF);
+                       if (dist == 1)
+#if defined(__BIG_ENDIAN)
+                           pat16 = (pat16 & 0xff) | ((pat16 & 0xff) << 8);
+#elif defined(__LITTLE_ENDIAN)
+                           pat16 = (pat16 & 0xff00) | ((pat16 & 0xff00) >> 8);
+#else
+#error __BIG_ENDIAN nor __LITTLE_ENDIAN is defined
+#endif
+                       loops = len >> 1;
+                       do
+                           PUP(sout) = pat16;
+                       while (--loops);
+                       out = (unsigned char *)sout + OFF;
+                   }
+                   if (len & 1)
+                       PUP(out) = PUP(from);
                 }
             }
             else if ((op & 64) == 0) {          /* 2nd level distance code */