View Javadoc
1   /*
2    * Copyright 2017 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    *   https://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.netty5.handler.codec.http2;
17  
18  import io.netty5.buffer.api.Buffer;
19  import io.netty5.buffer.api.BufferAllocator;
20  import io.netty5.microbench.util.AbstractMicrobenchmark;
21  import org.openjdk.jmh.annotations.Benchmark;
22  import org.openjdk.jmh.annotations.BenchmarkMode;
23  import org.openjdk.jmh.annotations.Fork;
24  import org.openjdk.jmh.annotations.Measurement;
25  import org.openjdk.jmh.annotations.Mode;
26  import org.openjdk.jmh.annotations.OutputTimeUnit;
27  import org.openjdk.jmh.annotations.Scope;
28  import org.openjdk.jmh.annotations.Setup;
29  import org.openjdk.jmh.annotations.State;
30  import org.openjdk.jmh.annotations.Threads;
31  import org.openjdk.jmh.annotations.Warmup;
32  
33  import java.util.concurrent.TimeUnit;
34  
35  @Threads(1)
36  @State(Scope.Benchmark)
37  @Fork(1)
38  @Warmup(iterations = 5)
39  @Measurement(iterations = 10)
40  @OutputTimeUnit(TimeUnit.NANOSECONDS)
41  public class HpackDecoderULE128Benchmark extends AbstractMicrobenchmark {
42      private static final Http2Exception DECODE_ULE_128_TO_LONG_DECOMPRESSION_EXCEPTION =
43              new Http2Exception(Http2Error.COMPRESSION_ERROR);
44      private static final Http2Exception DECODE_ULE_128_TO_INT_DECOMPRESSION_EXCEPTION =
45              new Http2Exception(Http2Error.COMPRESSION_ERROR);
46      private static final Http2Exception DECODE_ULE_128_DECOMPRESSION_EXCEPTION =
47              new Http2Exception(Http2Error.COMPRESSION_ERROR);
48  
49      private Buffer longMaxBuf;
50      private Buffer intMaxBuf;
51  
52      @Setup
53      public void setup() {
54          byte[] longMax = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
55                            (byte) 0xFF, (byte) 0x7F};
56          longMaxBuf = BufferAllocator.onHeapUnpooled().copyOf(longMax);
57          byte[] intMax = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x07};
58          intMaxBuf = BufferAllocator.onHeapUnpooled().copyOf(intMax);
59      }
60  
61      @Benchmark
62      @BenchmarkMode(Mode.AverageTime)
63      public long decodeMaxLong() throws Http2Exception {
64          long v = decodeULE128(longMaxBuf, 0L);
65          longMaxBuf.readerOffset(0);
66          return v;
67      }
68  
69      @Benchmark
70      @BenchmarkMode(Mode.AverageTime)
71      public long decodeMaxIntWithLong() throws Http2Exception {
72          long v = decodeULE128(intMaxBuf, 0L);
73          intMaxBuf.readerOffset(0);
74          return v;
75      }
76  
77      @Benchmark
78      @BenchmarkMode(Mode.AverageTime)
79      public int decodeMaxInt() throws Http2Exception {
80          int v = decodeULE128(intMaxBuf, 0);
81          intMaxBuf.readerOffset(0);
82          return v;
83      }
84  
85      @Benchmark
86      @BenchmarkMode(Mode.AverageTime)
87      public int decodeMaxIntUsingLong() throws Http2Exception {
88          int v = decodeULE128UsingLong(intMaxBuf, 0);
89          intMaxBuf.readerOffset(0);
90          return v;
91      }
92  
93      static int decodeULE128UsingLong(Buffer in, int result) throws Http2Exception {
94          final int readerOffset = in.readerOffset();
95          final long v = decodeULE128(in, (long) result);
96          if (v > Integer.MAX_VALUE) {
97              in.readerOffset(readerOffset);
98              throw DECODE_ULE_128_TO_INT_DECOMPRESSION_EXCEPTION;
99          }
100         return (int) v;
101     }
102 
103     static long decodeULE128(Buffer in, long result) throws Http2Exception {
104         assert result <= 0x7f && result >= 0;
105         final boolean resultStartedAtZero = result == 0;
106         final int writerOffset = in.writerOffset();
107         for (int readerOffset = in.readerOffset(), shift = 0; readerOffset < writerOffset; ++readerOffset, shift += 7) {
108             byte b = in.getByte(readerOffset);
109             if (shift == 56 && ((b & 0x80) != 0 || b == 0x7F && !resultStartedAtZero)) {
110                 // the maximum value that can be represented by a signed 64 bit number is:
111                 // [0x01L, 0x7fL] + 0x7fL + (0x7fL << 7) + (0x7fL << 14) + (0x7fL << 21) + (0x7fL << 28) + (0x7fL << 35)
112                 // + (0x7fL << 42) + (0x7fL << 49) + (0x7eL << 56)
113                 // OR
114                 // 0x0L + 0x7fL + (0x7fL << 7) + (0x7fL << 14) + (0x7fL << 21) + (0x7fL << 28) + (0x7fL << 35) +
115                 // (0x7fL << 42) + (0x7fL << 49) + (0x7fL << 56)
116                 // this means any more shifts will result longMaxBuf overflow so we should break out and throw an error.
117                 throw DECODE_ULE_128_TO_LONG_DECOMPRESSION_EXCEPTION;
118             }
119 
120             if ((b & 0x80) == 0) {
121                 in.readerOffset(readerOffset + 1);
122                 return result + ((b & 0x7FL) << shift);
123             }
124             result += (b & 0x7FL) << shift;
125         }
126 
127         throw DECODE_ULE_128_DECOMPRESSION_EXCEPTION;
128     }
129 
130     static int decodeULE128(Buffer in, int result) throws Http2Exception {
131         assert result <= 0x7f && result >= 0;
132         final boolean resultStartedAtZero = result == 0;
133         final int writerOffset = in.writerOffset();
134         for (int readerOffset = in.readerOffset(), shift = 0; readerOffset < writerOffset; ++readerOffset, shift += 7) {
135             byte b = in.getByte(readerOffset);
136             if (shift == 28 && ((b & 0x80) != 0 || !resultStartedAtZero && b > 6 || resultStartedAtZero && b > 7)) {
137                 // the maximum value that can be represented by a signed 32 bit number is:
138                 // [0x1,0x7f] + 0x7f + (0x7f << 7) + (0x7f << 14) + (0x7f << 21) + (0x6 << 28)
139                 // OR
140                 // 0x0 + 0x7f + (0x7f << 7) + (0x7f << 14) + (0x7f << 21) + (0x7 << 28)
141                 // this means any more shifts will result longMaxBuf overflow so we should break out and throw an error.
142                 throw DECODE_ULE_128_TO_INT_DECOMPRESSION_EXCEPTION;
143             }
144 
145             if ((b & 0x80) == 0) {
146                 in.readerOffset(readerOffset + 1);
147                 return result + ((b & 0x7F) << shift);
148             }
149             result += (b & 0x7F) << shift;
150         }
151 
152         throw DECODE_ULE_128_DECOMPRESSION_EXCEPTION;
153     }
154 }