1/* 2 * Copyright (c) 2014, 2016, 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 */ 25package jdk.incubator.http.internal.hpack; 26 27import java.nio.ByteBuffer; 28import java.util.Arrays; 29 30import static java.lang.String.format; 31 32final class IntegerReader { 33 34 private static final int NEW = 0; 35 private static final int CONFIGURED = 1; 36 private static final int FIRST_BYTE_READ = 2; 37 private static final int DONE = 4; 38 39 private int state = NEW; 40 41 private int N; 42 private int maxValue; 43 private int value; 44 private long r; 45 private long b = 1; 46 47 public IntegerReader configure(int N) { 48 return configure(N, Integer.MAX_VALUE); 49 } 50 51 // 52 // Why is it important to configure 'maxValue' here. After all we can wait 53 // for the integer to be fully read and then check it. Can't we? 54 // 55 // Two reasons. 56 // 57 // 1. Value wraps around long won't be unnoticed. 58 // 2. It can spit out an exception as soon as it becomes clear there's 59 // an overflow. Therefore, no need to wait for the value to be fully read. 60 // 61 public IntegerReader configure(int N, int maxValue) { 62 if (state != NEW) { 63 throw new IllegalStateException("Already configured"); 64 } 65 checkPrefix(N); 66 if (maxValue < 0) { 67 throw new IllegalArgumentException( 68 "maxValue >= 0: maxValue=" + maxValue); 69 } 70 this.maxValue = maxValue; 71 this.N = N; 72 state = CONFIGURED; 73 return this; 74 } 75 76 public boolean read(ByteBuffer input) { 77 if (state == NEW) { 78 throw new IllegalStateException("Configure first"); 79 } 80 if (state == DONE) { 81 return true; 82 } 83 if (!input.hasRemaining()) { 84 return false; 85 } 86 if (state == CONFIGURED) { 87 int max = (2 << (N - 1)) - 1; 88 int n = input.get() & max; 89 if (n != max) { 90 value = n; 91 state = DONE; 92 return true; 93 } else { 94 r = max; 95 } 96 state = FIRST_BYTE_READ; 97 } 98 if (state == FIRST_BYTE_READ) { 99 // variable-length quantity (VLQ) 100 byte i; 101 do { 102 if (!input.hasRemaining()) { 103 return false; 104 } 105 i = input.get(); 106 long increment = b * (i & 127); 107 if (r + increment > maxValue) { 108 throw new IllegalArgumentException(format( 109 "Integer overflow: maxValue=%,d, value=%,d", 110 maxValue, r + increment)); 111 } 112 r += increment; 113 b *= 128; 114 } while ((128 & i) == 128); 115 116 value = (int) r; 117 state = DONE; 118 return true; 119 } 120 throw new InternalError(Arrays.toString( 121 new Object[]{state, N, maxValue, value, r, b})); 122 } 123 124 public int get() throws IllegalStateException { 125 if (state != DONE) { 126 throw new IllegalStateException("Has not been fully read yet"); 127 } 128 return value; 129 } 130 131 private static void checkPrefix(int N) { 132 if (N < 1 || N > 8) { 133 throw new IllegalArgumentException("1 <= N <= 8: N= " + N); 134 } 135 } 136 137 public IntegerReader reset() { 138 b = 1; 139 state = NEW; 140 return this; 141 } 142} 143