|
Java example source code file (Encoder.java)
The Encoder.java Java example source code/* * Copyright 2015 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ /* * Copyright 2014 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.netty.handler.codec.http2.internal.hpack; import io.netty.buffer.ByteBuf; import io.netty.util.AsciiString; import io.netty.util.CharsetUtil; import java.util.Arrays; import static io.netty.handler.codec.http2.internal.hpack.HpackUtil.IndexType.INCREMENTAL; import static io.netty.handler.codec.http2.internal.hpack.HpackUtil.IndexType.NEVER; import static io.netty.handler.codec.http2.internal.hpack.HpackUtil.IndexType.NONE; public final class Encoder { private static final int BUCKET_SIZE = 17; // for testing private final boolean useIndexing; private final boolean forceHuffmanOn; private final boolean forceHuffmanOff; private final HuffmanEncoder huffmanEncoder = new HuffmanEncoder(); // a linked hash map of header fields private final HeaderEntry[] headerFields = new HeaderEntry[BUCKET_SIZE]; private final HeaderEntry head = new HeaderEntry(-1, AsciiString.EMPTY_STRING, AsciiString.EMPTY_STRING, Integer.MAX_VALUE, null); private int size; private int capacity; /** * Creates a new encoder. */ public Encoder(int maxHeaderTableSize) { this(maxHeaderTableSize, true, false, false); } /** * Constructor for testing only. */ Encoder( int maxHeaderTableSize, boolean useIndexing, boolean forceHuffmanOn, boolean forceHuffmanOff ) { if (maxHeaderTableSize < 0) { throw new IllegalArgumentException("Illegal Capacity: " + maxHeaderTableSize); } this.useIndexing = useIndexing; this.forceHuffmanOn = forceHuffmanOn; this.forceHuffmanOff = forceHuffmanOff; capacity = maxHeaderTableSize; head.before = head.after = head; } /** * Encode the header field into the header block. * * <strong>The given {@link CharSequence}s must be immutable! */ public void encodeHeader(ByteBuf out, CharSequence name, CharSequence value, boolean sensitive) { // If the header value is sensitive then it must never be indexed if (sensitive) { int nameIndex = getNameIndex(name); encodeLiteral(out, name, value, NEVER, nameIndex); return; } // If the peer will only use the static table if (capacity == 0) { int staticTableIndex = StaticTable.getIndex(name, value); if (staticTableIndex == -1) { int nameIndex = StaticTable.getIndex(name); encodeLiteral(out, name, value, NONE, nameIndex); } else { encodeInteger(out, 0x80, 7, staticTableIndex); } return; } int headerSize = HeaderField.sizeOf(name, value); // If the headerSize is greater than the max table size then it must be encoded literally if (headerSize > capacity) { int nameIndex = getNameIndex(name); encodeLiteral(out, name, value, NONE, nameIndex); return; } HeaderEntry headerField = getEntry(name, value); if (headerField != null) { int index = getIndex(headerField.index) + StaticTable.length; // Section 6.1. Indexed Header Field Representation encodeInteger(out, 0x80, 7, index); } else { int staticTableIndex = StaticTable.getIndex(name, value); if (staticTableIndex != -1) { // Section 6.1. Indexed Header Field Representation encodeInteger(out, 0x80, 7, staticTableIndex); } else { int nameIndex = getNameIndex(name); if (useIndexing) { ensureCapacity(headerSize); } HpackUtil.IndexType indexType = useIndexing ? INCREMENTAL : NONE; encodeLiteral(out, name, value, indexType, nameIndex); if (useIndexing) { add(name, value); } } } } /** * Set the maximum table size. */ public void setMaxHeaderTableSize(ByteBuf out, int maxHeaderTableSize) { if (maxHeaderTableSize < 0) { throw new IllegalArgumentException("Illegal Capacity: " + maxHeaderTableSize); } if (capacity == maxHeaderTableSize) { return; } capacity = maxHeaderTableSize; ensureCapacity(0); encodeInteger(out, 0x20, 5, maxHeaderTableSize); } /** * Return the maximum table size. */ public int getMaxHeaderTableSize() { return capacity; } /** * Encode integer according to Section 5.1. */ private static void encodeInteger(ByteBuf out, int mask, int n, int i) { if (n < 0 || n > 8) { throw new IllegalArgumentException("N: " + n); } int nbits = 0xFF >>> (8 - n); if (i < nbits) { out.writeByte(mask | i); } else { out.writeByte(mask | nbits); int length = i - nbits; for (;;) { if ((length & ~0x7F) == 0) { out.writeByte(length); return; } else { out.writeByte((length & 0x7F) | 0x80); length >>>= 7; } } } } /** * Encode string literal according to Section 5.2. */ private void encodeStringLiteral(ByteBuf out, CharSequence string) { int huffmanLength = huffmanEncoder.getEncodedLength(string); if ((huffmanLength < string.length() && !forceHuffmanOff) || forceHuffmanOn) { encodeInteger(out, 0x80, 7, huffmanLength); huffmanEncoder.encode(out, string); } else { encodeInteger(out, 0x00, 7, string.length()); if (string instanceof AsciiString) { // Fast-path AsciiString asciiString = (AsciiString) string; out.writeBytes(asciiString.array(), asciiString.arrayOffset(), asciiString.length()); } else { // Only ASCII is allowed in http2 headers, so its fine to use this. // https://tools.ietf.org/html/rfc7540#section-8.1.2 out.writeCharSequence(string, CharsetUtil.ISO_8859_1); } } } /** * Encode literal header field according to Section 6.2. */ private void encodeLiteral(ByteBuf out, CharSequence name, CharSequence value, HpackUtil.IndexType indexType, int nameIndex) { int mask; int prefixBits; switch (indexType) { case INCREMENTAL: mask = 0x40; prefixBits = 6; break; case NONE: mask = 0x00; prefixBits = 4; break; case NEVER: mask = 0x10; prefixBits = 4; break; default: throw new IllegalStateException("should not reach here"); } encodeInteger(out, mask, prefixBits, nameIndex == -1 ? 0 : nameIndex); if (nameIndex == -1) { encodeStringLiteral(out, name); } encodeStringLiteral(out, value); } private int getNameIndex(CharSequence name) { int index = StaticTable.getIndex(name); if (index == -1) { index = getIndex(name); if (index >= 0) { index += StaticTable.length; } } return index; } /** * Ensure that the dynamic table has enough room to hold 'headerSize' more bytes. Removes the * oldest entry from the dynamic table until sufficient space is available. */ private void ensureCapacity(int headerSize) { while (size + headerSize > capacity) { int index = length(); if (index == 0) { break; } remove(); } } /** * Return the number of header fields in the dynamic table. Exposed for testing. */ int length() { return size == 0 ? 0 : head.after.index - head.before.index + 1; } /** * Return the size of the dynamic table. Exposed for testing. */ int size() { return size; } /** * Return the header field at the given index. Exposed for testing. */ HeaderField getHeaderField(int index) { HeaderEntry entry = head; while (index-- >= 0) { entry = entry.before; } return entry; } /** * Returns the header entry with the lowest index value for the header field. Returns null if * header field is not in the dynamic table. */ private HeaderEntry getEntry(CharSequence name, CharSequence value) { if (length() == 0 || name == null || value == null) { return null; } int h = hash(name); int i = index(h); for (HeaderEntry e = headerFields[i]; e != null; e = e.next) { if (e.hash == h && HpackUtil.equals(name, e.name) && HpackUtil.equals(value, e.value)) { return e; } } return null; } /** * Returns the lowest index value for the header field name in the dynamic table. Returns -1 if * the header field name is not in the dynamic table. */ private int getIndex(CharSequence name) { if (length() == 0 || name == null) { return -1; } int h = hash(name); int i = index(h); int index = -1; for (HeaderEntry e = headerFields[i]; e != null; e = e.next) { if (e.hash == h && HpackUtil.equals(name, e.name)) { index = e.index; break; } } return getIndex(index); } /** * Compute the index into the dynamic table given the index in the header entry. */ private int getIndex(int index) { if (index == -1) { return -1; } return index - head.before.index + 1; } /** * Add the header field to the dynamic table. Entries are evicted from the dynamic table until * the size of the table and the new header field is less than the table's capacity. If the size * of the new entry is larger than the table's capacity, the dynamic table will be cleared. */ private void add(CharSequence name, CharSequence value) { int headerSize = HeaderField.sizeOf(name, value); // Clear the table if the header field size is larger than the capacity. if (headerSize > capacity) { clear(); return; } // Evict oldest entries until we have enough capacity. while (size + headerSize > capacity) { remove(); } int h = hash(name); int i = index(h); HeaderEntry old = headerFields[i]; HeaderEntry e = new HeaderEntry(h, name, value, head.before.index - 1, old); headerFields[i] = e; e.addBefore(head); size += headerSize; } /** * Remove and return the oldest header field from the dynamic table. */ private HeaderField remove() { if (size == 0) { return null; } HeaderEntry eldest = head.after; int h = eldest.hash; int i = index(h); HeaderEntry prev = headerFields[i]; HeaderEntry e = prev; while (e != null) { HeaderEntry next = e.next; if (e == eldest) { if (prev == eldest) { headerFields[i] = next; } else { prev.next = next; } eldest.remove(); size -= eldest.size(); return eldest; } prev = e; e = next; } return null; } /** * Remove all entries from the dynamic table. */ private void clear() { Arrays.fill(headerFields, null); head.before = head.after = head; size = 0; } /** * Returns the hash code for the given header field name. */ private static int hash(CharSequence name) { int h = 0; for (int i = 0; i < name.length(); i++) { h = 31 * h + name.charAt(i); } if (h > 0) { return h; } else if (h == Integer.MIN_VALUE) { return Integer.MAX_VALUE; } else { return -h; } } /** * Returns the index into the hash table for the hash code h. */ private static int index(int h) { return h % BUCKET_SIZE; } /** * A linked hash map HeaderField entry. */ private static class HeaderEntry extends HeaderField { // These fields comprise the doubly linked list used for iteration. HeaderEntry before, after; // These fields comprise the chained list for header fields with the same hash. HeaderEntry next; int hash; // This is used to compute the index in the dynamic table. int index; /** * Creates new entry. */ HeaderEntry(int hash, CharSequence name, CharSequence value, int index, HeaderEntry next) { super(name, value); this.index = index; this.hash = hash; this.next = next; } /** * Removes this entry from the linked list. */ private void remove() { before.after = after; after.before = before; before = null; // null references to prevent nepotism in generational GC. after = null; next = null; } /** * Inserts this entry before the specified existing entry in the list. */ private void addBefore(HeaderEntry existingEntry) { after = existingEntry; before = existingEntry.before; before.after = this; after.before = this; } } } Other Java examples (source code examples)Here is a short list of links related to this Java Encoder.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.