1 /* 2 * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package jdk.incubator.http.internal.hpack; 26 27 import java.io.IOException; 28 import java.nio.ByteBuffer; 29 import java.util.Arrays; 30 31 import static java.lang.String.format; 32 33 final class IntegerReader { 34 35 private static final int NEW = 0; 36 private static final int CONFIGURED = 1; 37 private static final int FIRST_BYTE_READ = 2; 38 private static final int DONE = 4; 39 40 private int state = NEW; 41 42 private int N; 43 private int maxValue; 44 private int value; 45 private long r; 46 private long b = 1; 47 48 public IntegerReader configure(int N) { 49 return configure(N, Integer.MAX_VALUE); 50 } 51 52 // 53 // Why is it important to configure 'maxValue' here. After all we can wait 54 // for the integer to be fully read and then check it. Can't we? 55 // 56 // Two reasons. 57 // 58 // 1. Value wraps around long won't be unnoticed. 59 // 2. It can spit out an exception as soon as it becomes clear there's 60 // an overflow. Therefore, no need to wait for the value to be fully read. 61 // 62 public IntegerReader configure(int N, int maxValue) { 63 if (state != NEW) { 64 throw new IllegalStateException("Already configured"); 65 } 66 checkPrefix(N); 67 if (maxValue < 0) { 68 throw new IllegalArgumentException( 69 "maxValue >= 0: maxValue=" + maxValue); 70 } 71 this.maxValue = maxValue; 72 this.N = N; 73 state = CONFIGURED; 74 return this; 75 } 76 77 public boolean read(ByteBuffer input) throws IOException { 78 if (state == NEW) { 79 throw new IllegalStateException("Configure first"); 80 } 81 if (state == DONE) { 82 return true; 83 } 84 if (!input.hasRemaining()) { 85 return false; 86 } 87 if (state == CONFIGURED) { 88 int max = (2 << (N - 1)) - 1; 89 int n = input.get() & max; 90 if (n != max) { 91 value = n; 92 state = DONE; 93 return true; 94 } else { 95 r = max; 96 } 97 state = FIRST_BYTE_READ; 98 } 99 if (state == FIRST_BYTE_READ) { 100 // variable-length quantity (VLQ) 101 byte i; 102 do { 103 if (!input.hasRemaining()) { 104 return false; 105 } 106 i = input.get(); 107 long increment = b * (i & 127); 108 if (r + increment > maxValue) { 109 throw new IOException(format( 110 "Integer overflow: maxValue=%,d, value=%,d", 111 maxValue, r + increment)); 112 } 113 r += increment; 114 b *= 128; 115 } while ((128 & i) == 128); 116 117 value = (int) r; 118 state = DONE; 119 return true; 120 } 121 throw new InternalError(Arrays.toString( 122 new Object[]{state, N, maxValue, value, r, b})); 123 } 124 125 public int get() throws IllegalStateException { 126 if (state != DONE) { 127 throw new IllegalStateException("Has not been fully read yet"); 128 } 129 return value; 130 } 131 132 private static void checkPrefix(int N) { 133 if (N < 1 || N > 8) { 134 throw new IllegalArgumentException("1 <= N <= 8: N= " + N); 135 } 136 } 137 138 public IntegerReader reset() { 139 b = 1; 140 state = NEW; 141 return this; 142 } 143 }