1/* Copyright (C) 1995-2011, 2016 Mark Adler
2 * Copyright (C) 2017 ARM Holdings Inc.
3 * Author: Adenilson Cavalcanti <adenilson.cavalcanti@arm.com>
4 *
5 * For conditions of distribution and use, see copyright notice in zlib.h
6 */
7#ifdef ARM_NEON_ADLER32
8#ifdef _M_ARM64
9# include <arm64_neon.h>
10#else
11# include <arm_neon.h>
12#endif
13#include "../../zutil.h"
14#include "../../adler32_p.h"
15
16static void NEON_accum32(uint32_t *s, const unsigned char *buf, size_t len) {
17 static const uint8_t taps[32] = {
18 32, 31, 30, 29, 28, 27, 26, 25,
19 24, 23, 22, 21, 20, 19, 18, 17,
20 16, 15, 14, 13, 12, 11, 10, 9,
21 8, 7, 6, 5, 4, 3, 2, 1 };
22
23 uint32x2_t adacc2, s2acc2, as;
24 uint8x16_t t0 = vld1q_u8(taps), t1 = vld1q_u8(taps + 16);
25
26 uint32x4_t adacc = vdupq_n_u32(p0: 0), s2acc = vdupq_n_u32(p0: 0);
27 adacc = vsetq_lane_u32(s[0], adacc, 0);
28 s2acc = vsetq_lane_u32(s[1], s2acc, 0);
29
30 while (len >= 2) {
31 uint8x16_t d0 = vld1q_u8(buf), d1 = vld1q_u8(buf + 16);
32 uint16x8_t adler, sum2;
33 s2acc = vaddq_u32(p0: s2acc, vshlq_n_u32(adacc, 5));
34 adler = vpaddlq_u8( p0: d0);
35 adler = vpadalq_u8(p0: adler, p1: d1);
36 sum2 = vmull_u8( p0: vget_low_u8(p0: t0), p1: vget_low_u8(p0: d0));
37 sum2 = vmlal_u8(p0: sum2, p1: vget_high_u8(p0: t0), p2: vget_high_u8(p0: d0));
38 sum2 = vmlal_u8(p0: sum2, p1: vget_low_u8(p0: t1), p2: vget_low_u8(p0: d1));
39 sum2 = vmlal_u8(p0: sum2, p1: vget_high_u8(p0: t1), p2: vget_high_u8(p0: d1));
40 adacc = vpadalq_u16(p0: adacc, p1: adler);
41 s2acc = vpadalq_u16(p0: s2acc, p1: sum2);
42 len -= 2;
43 buf += 32;
44 }
45
46 while (len > 0) {
47 uint8x16_t d0 = vld1q_u8(buf);
48 uint16x8_t adler, sum2;
49 s2acc = vaddq_u32(p0: s2acc, vshlq_n_u32(adacc, 4));
50 adler = vpaddlq_u8(p0: d0);
51 sum2 = vmull_u8( p0: vget_low_u8(p0: t1), p1: vget_low_u8(p0: d0));
52 sum2 = vmlal_u8(p0: sum2, p1: vget_high_u8(p0: t1), p2: vget_high_u8(p0: d0));
53 adacc = vpadalq_u16(p0: adacc, p1: adler);
54 s2acc = vpadalq_u16(p0: s2acc, p1: sum2);
55 buf += 16;
56 len--;
57 }
58
59 adacc2 = vpadd_u32(p0: vget_low_u32(p0: adacc), p1: vget_high_u32(p0: adacc));
60 s2acc2 = vpadd_u32(p0: vget_low_u32(p0: s2acc), p1: vget_high_u32(p0: s2acc));
61 as = vpadd_u32(p0: adacc2, p1: s2acc2);
62 s[0] = vget_lane_u32(as, 0);
63 s[1] = vget_lane_u32(as, 1);
64}
65
66static void NEON_handle_tail(uint32_t *pair, const unsigned char *buf, size_t len) {
67 unsigned int i;
68 for (i = 0; i < len; ++i) {
69 pair[0] += buf[i];
70 pair[1] += pair[0];
71 }
72}
73
74uint32_t adler32_neon(uint32_t adler, const unsigned char *buf, size_t len) {
75 /* split Adler-32 into component sums */
76 uint32_t sum2 = (adler >> 16) & 0xffff;
77 adler &= 0xffff;
78
79 /* in case user likes doing a byte at a time, keep it fast */
80 if (len == 1)
81 return adler32_len_1(adler, buf, sum2);
82
83 /* initial Adler-32 value (deferred check for len == 1 speed) */
84 if (buf == NULL)
85 return 1L;
86
87 /* in case short lengths are provided, keep it somewhat fast */
88 if (len < 16)
89 return adler32_len_16(adler, buf, len, sum2);
90
91 uint32_t pair[2];
92 int n = NMAX;
93 unsigned int done = 0;
94 unsigned int i;
95
96 /* Split Adler-32 into component sums, it can be supplied by
97 * the caller sites (e.g. in a PNG file).
98 */
99 pair[0] = adler;
100 pair[1] = sum2;
101
102 for (i = 0; i < len; i += n) {
103 if ((i + n) > len)
104 n = (int)(len - i);
105
106 if (n < 16)
107 break;
108
109 NEON_accum32(s: pair, buf: buf + i, len: n / 16);
110 pair[0] %= BASE;
111 pair[1] %= BASE;
112
113 done += (n / 16) * 16;
114 }
115
116 /* Handle the tail elements. */
117 if (done < len) {
118 NEON_handle_tail(pair, buf: (buf + done), len: len - done);
119 pair[0] %= BASE;
120 pair[1] %= BASE;
121 }
122
123 /* D = B * 65536 + A, see: https://en.wikipedia.org/wiki/Adler-32. */
124 return (pair[1] << 16) | pair[0];
125}
126#endif
127