1 /*
2  * Copyright 2016 The Netty Project
3  *
4  * The Netty Project licenses this file to you under the Apache License,
5  * version 2.0 (the "License"); you may not use this file except in compliance
6  * with the License. You may obtain a copy of the License at:
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */

16 package io.netty.handler.codec.compression;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.util.ByteProcessor;
20 import io.netty.util.internal.ObjectUtil;
21 import io.netty.util.internal.PlatformDependent;
22
23 import java.lang.reflect.Method;
24 import java.nio.ByteBuffer;
25 import java.util.zip.Adler32;
26 import java.util.zip.CRC32;
27 import java.util.zip.Checksum;
28
29 /**
30  * {@link Checksum} implementation which can directly act on a {@link ByteBuf}.
31  *
32  * Implementations may optimize access patterns depending on if the {@link ByteBuf} is backed by a
33  * byte array ({@link ByteBuf#hasArray()} is {@code true}) or not.
34  */

35 abstract class ByteBufChecksum implements Checksum {
36     private static final Method ADLER32_UPDATE_METHOD;
37     private static final Method CRC32_UPDATE_METHOD;
38
39     static {
40         // See if we can use fast-path when using ByteBuf that is not heap based as Adler32 and CRC32 added support
41         // for update(ByteBuffer) in JDK8.
42         ADLER32_UPDATE_METHOD = updateByteBuffer(new Adler32());
43         CRC32_UPDATE_METHOD = updateByteBuffer(new CRC32());
44     }
45
46     private final ByteProcessor updateProcessor = new ByteProcessor() {
47         @Override
48         public boolean process(byte value) throws Exception {
49             update(value);
50             return true;
51         }
52     };
53
54     private static Method updateByteBuffer(Checksum checksum) {
55         if (PlatformDependent.javaVersion() >= 8) {
56             try {
57                 Method method = checksum.getClass().getDeclaredMethod("update", ByteBuffer.class);
58                 method.invoke(checksum, ByteBuffer.allocate(1));
59                 return method;
60             } catch (Throwable ignore) {
61                 return null;
62             }
63         }
64         return null;
65     }
66
67     static ByteBufChecksum wrapChecksum(Checksum checksum) {
68         ObjectUtil.checkNotNull(checksum, "checksum");
69         if (checksum instanceof ByteBufChecksum) {
70             return (ByteBufChecksum) checksum;
71         }
72         if (checksum instanceof Adler32 && ADLER32_UPDATE_METHOD != null) {
73             return new ReflectiveByteBufChecksum(checksum, ADLER32_UPDATE_METHOD);
74         }
75         if (checksum instanceof CRC32 && CRC32_UPDATE_METHOD != null) {
76             return new ReflectiveByteBufChecksum(checksum, CRC32_UPDATE_METHOD);
77         }
78         return new SlowByteBufChecksum(checksum);
79     }
80
81     /**
82      * @see #update(byte[], intint)
83      */

84     public void update(ByteBuf b, int off, int len) {
85         if (b.hasArray()) {
86             update(b.array(), b.arrayOffset() + off, len);
87         } else {
88             b.forEachByte(off, len, updateProcessor);
89         }
90     }
91
92     private static final class ReflectiveByteBufChecksum extends SlowByteBufChecksum {
93         private final Method method;
94
95         ReflectiveByteBufChecksum(Checksum checksum, Method method) {
96             super(checksum);
97             this.method = method;
98         }
99
100         @Override
101         public void update(ByteBuf b, int off, int len) {
102             if (b.hasArray()) {
103                 update(b.array(), b.arrayOffset() + off, len);
104             } else {
105                 try {
106                     method.invoke(checksum, CompressionUtil.safeNioBuffer(b, off, len));
107                 } catch (Throwable cause) {
108                     throw new Error();
109                 }
110             }
111         }
112     }
113
114     private static class SlowByteBufChecksum extends ByteBufChecksum {
115
116         protected final Checksum checksum;
117
118         SlowByteBufChecksum(Checksum checksum) {
119             this.checksum = checksum;
120         }
121
122         @Override
123         public void update(int b) {
124             checksum.update(b);
125         }
126
127         @Override
128         public void update(byte[] b, int off, int len) {
129             checksum.update(b, off, len);
130         }
131
132         @Override
133         public long getValue() {
134             return checksum.getValue();
135         }
136
137         @Override
138         public void reset() {
139             checksum.reset();
140         }
141     }
142 }
143