1/*
2 * Copyright (c) 2015, 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
30final class IntegerWriter {
31
32    private static final int NEW                = 0;
33    private static final int CONFIGURED         = 1;
34    private static final int FIRST_BYTE_WRITTEN = 2;
35    private static final int DONE               = 4;
36
37    private int state = NEW;
38
39    private int payload;
40    private int N;
41    private int value;
42
43    //
44    //      0   1   2   3   4   5   6   7
45    //    +---+---+---+---+---+---+---+---+
46    //    |   |   |   |   |   |   |   |   |
47    //    +---+---+---+-------------------+
48    //    |<--------->|<----------------->|
49    //       payload           N=5
50    //
51    // payload is the contents of the left-hand side part of the octet;
52    //         it is truncated to fit into 8-N bits, where 1 <= N <= 8;
53    //
54    public IntegerWriter configure(int value, int N, int payload) {
55        if (state != NEW) {
56            throw new IllegalStateException("Already configured");
57        }
58        if (value < 0) {
59            throw new IllegalArgumentException("value >= 0: value=" + value);
60        }
61        checkPrefix(N);
62        this.value = value;
63        this.N = N;
64        this.payload = payload & 0xFF & (0xFFFFFFFF << N);
65        state = CONFIGURED;
66        return this;
67    }
68
69    public boolean write(ByteBuffer output) {
70        if (state == NEW) {
71            throw new IllegalStateException("Configure first");
72        }
73        if (state == DONE) {
74            return true;
75        }
76
77        if (!output.hasRemaining()) {
78            return false;
79        }
80        if (state == CONFIGURED) {
81            int max = (2 << (N - 1)) - 1;
82            if (value < max) {
83                output.put((byte) (payload | value));
84                state = DONE;
85                return true;
86            }
87            output.put((byte) (payload | max));
88            value -= max;
89            state = FIRST_BYTE_WRITTEN;
90        }
91        if (state == FIRST_BYTE_WRITTEN) {
92            while (value >= 128 && output.hasRemaining()) {
93                output.put((byte) (value % 128 + 128));
94                value /= 128;
95            }
96            if (!output.hasRemaining()) {
97                return false;
98            }
99            output.put((byte) value);
100            state = DONE;
101            return true;
102        }
103        throw new InternalError(Arrays.toString(
104                new Object[]{state, payload, N, value}));
105    }
106
107    private static void checkPrefix(int N) {
108        if (N < 1 || N > 8) {
109            throw new IllegalArgumentException("1 <= N <= 8: N= " + N);
110        }
111    }
112
113    public IntegerWriter reset() {
114        state = NEW;
115        return this;
116    }
117}
118