/*
 * Copyright (c) 2010, 2017 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.grizzly.http.util;

import java.nio.charset.Charset;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.memory.Buffers;

Buffer chunk representation. Helps HTTP module to avoid redundant String creation.
Author:Alexey Stashok
/** * {@link Buffer} chunk representation. * Helps HTTP module to avoid redundant String creation. * * @author Alexey Stashok */
public class BufferChunk implements Chunk {
Default encoding used to convert to strings. It should be UTF8, as most standards seem to converge, but the servlet API requires 8859_1, and this object is used mostly for servlets.
/** Default encoding used to convert to strings. It should be UTF8, as most standards seem to converge, but the servlet API requires 8859_1, and this object is used mostly for servlets. */
private static final Charset DEFAULT_CHARSET = Constants.DEFAULT_HTTP_CHARSET; private Buffer buffer; private int start; private int end; // the last byte available for this BufferChunk private int limit; String cachedString; Charset cachedStringCharset; public void setBufferChunk(final Buffer buffer, final int start, final int end) { setBufferChunk(buffer, start, end, end); } public void setBufferChunk(final Buffer buffer, final int start, final int end, final int limit) { this.buffer = buffer; this.start = start; this.end = end; this.limit = limit; resetStringCache(); } public Buffer getBuffer() { return buffer; } public void setBuffer(Buffer buffer) { this.buffer = buffer; resetStringCache(); } @Override public int getStart() { return start; } @Override public void setStart(int start) { this.start = start; resetStringCache(); } @Override public int getEnd() { return end; } @Override public void setEnd(int end) { this.end = end; resetStringCache(); } @Override public final int getLength() { return end - start; } public final boolean isNull() { return buffer == null; } public void allocate(final int size) { if (isNull() || (limit - start) < size) { setBufferChunk(Buffers.wrap(null, new byte[size]), 0, 0, size); } end = start; } @Override public void delete(final int start, final int end) { final int absDeleteStart = this.start + start; final int absDeleteEnd = this.start + end; final int diff = this.end - absDeleteEnd; if (diff == 0) { this.end = absDeleteStart; } else { final int oldPos = buffer.position(); final int oldLim = buffer.limit(); try { Buffers.setPositionLimit(buffer, absDeleteStart, absDeleteStart + diff); // we have to duplicate the buffer as, depending on the memory // manager, it may be an error to pass a buffer back to itself. final Buffer duplicate = buffer.duplicate(); buffer.put(duplicate, absDeleteEnd, diff); this.end = absDeleteStart + diff; } finally { Buffers.setPositionLimit(buffer, oldPos, oldLim); } } resetStringCache(); } public void append(final BufferChunk bc) { final int oldPos = buffer.position(); final int oldLim = buffer.limit(); final int srcLen = bc.getLength() ; Buffers.setPositionLimit(buffer, end, end + srcLen); buffer.put(bc.getBuffer(), bc.getStart(), srcLen); Buffers.setPositionLimit(buffer, oldPos, oldLim); end += srcLen; } @Override public final int indexOf(final char c, final int fromIndex) { final int idx = indexOf(buffer, start + fromIndex, end, c); return idx >= start ? idx - start : -1; } @Override public final int indexOf(final String s, final int fromIndex) { final int idx = indexOf(buffer, start + fromIndex, end, s); return idx >= start ? idx - start : -1; } boolean startsWith(String s, int pos) { final int len = s.length(); if (len > getLength() - pos) { return false; } int off = start + pos; for (int i = 0; i < len; i++) { if (buffer.get(off++) != s.charAt(i)) { return false; } } return true; } public boolean startsWithIgnoreCase(String s, int pos) { final int len = s.length(); if (len > getLength() - pos) { return false; } int off = start + pos; for (int i = 0; i < len; i++) { if (Ascii.toLower(buffer.get(off++)) != Ascii.toLower(s.charAt(i))) { return false; } } return true; }
Returns the starting index of the specified byte sequence within this Buffer.
Params:
  • b – byte sequence to search for.
Returns:the starting index of the specified byte sequence within this Buffer
/** * Returns the starting index of the specified byte sequence within this * <code>Buffer</code>. * * @param b byte sequence to search for. * * @return the starting index of the specified byte sequence within this * <code>Buffer</code> */
public int findBytesAscii(byte[] b) { final byte first = b[0]; final int from = getStart(); final int to = getEnd(); // Look for first char int srcEnd = b.length; for (int i = from; i <= to - srcEnd; i++) { if (Ascii.toLower(buffer.get(i)) != first) continue; // found first char, now look for a match int myPos = i + 1; for (int srcPos = 1; srcPos < srcEnd;) { if (Ascii.toLower(buffer.get(myPos++)) != b[srcPos++]) { break; } if (srcPos == srcEnd) { return i - from; // found it } } } return -1; } @Override public int hashCode() { return hash(); } public int hash() { int code=0; for (int i = start; i < end; i++) { code = code * 31 + buffer.get(i); } return code; } @Override public boolean equals(final Object o) { if (!(o instanceof BufferChunk)) { return false; } final BufferChunk anotherBC = (BufferChunk) o; final int len = getLength(); if (len != anotherBC.getLength()) { return false; } int offs1 = start; int offs2 = anotherBC.start; for (int i = 0; i < len; i++) { if (buffer.get(offs1++) != anotherBC.buffer.get(offs2++)) { return false; } } return true; } public boolean equals(CharSequence s) { if (getLength() != s.length()) { return false; } for (int i = start; i < end; i++) { if (buffer.get(i) != s.charAt(i - start)) { return false; } } return true; }
Compares the message Buffer to the specified byte array.
Params:
  • b – the byte[] to compare
Returns:true if the comparison succeeded, false otherwise
Since:2.3
/** * Compares the message Buffer to the specified byte array. * @param b the <code>byte[]</code> to compare * * @return true if the comparison succeeded, false otherwise * * @since 2.3 */
public boolean equals(final byte[] b) { return equals(b, 0, b.length); }
Compares the message Buffer to the specified byte array.
Params:
  • b – the byte[] to compare
  • offset – the offset in the array
  • len – number of bytes to check
Returns:true if the comparison succeeded, false otherwise
Since:2.3
/** * Compares the message Buffer to the specified byte array. * @param b the <code>byte[]</code> to compare * @param offset the offset in the array * @param len number of bytes to check * * @return true if the comparison succeeded, false otherwise * * @since 2.3 */
public boolean equals(final byte[] b, int offset, final int len) { if (getLength() != len) { return false; } for (int i = start; i < end; i++) { if (buffer.get(i) != b[offset++]) { return false; } } return true; } public static boolean equals(final byte[] c, final int cOff, final int cLen, final Buffer t, final int tOff, final int tLen) { // XXX ENCODING - this only works if encoding is UTF8-compat // ( ok for tomcat, where we compare ascii - header names, etc )!!! if (cLen != tLen) { return false; } if (c == null || t == null) { return false; } for (int i = 0; i < cLen; i++) { if (c[i + cOff] != t.get(i + tOff)) { return false; } } return true; }
Compares the message Buffer to the specified char array.
Params:
  • b – the char[] to compare
  • offset – the offset in the array
  • len – number of chars to check
Returns:true if the comparison succeeded, false otherwise
Since:2.3
/** * Compares the message Buffer to the specified char array. * @param b the <code>char[]</code> to compare * @param offset the offset in the array * @param len number of chars to check * * @return true if the comparison succeeded, false otherwise * * @since 2.3 */
public boolean equals(final char[] b, int offset, final int len) { if (getLength() != len) { return false; } for (int i = start; i < end; i++) { if (buffer.get(i) != b[offset++]) { return false; } } return true; } public boolean equalsIgnoreCase(final Object o) { if (!(o instanceof BufferChunk)) { return false; } final BufferChunk anotherBC = (BufferChunk) o; final int len = getLength(); if (len != anotherBC.getLength()) { return false; } int offs1 = start; int offs2 = anotherBC.start; for (int i = 0; i < len; i++) { if (Ascii.toLower(buffer.get(offs1++)) != Ascii.toLower(anotherBC.buffer.get(offs2++))) { return false; } } return true; } public boolean equalsIgnoreCase(CharSequence s) { if (getLength() != s.length()) { return false; } for (int i = start; i < end; i++) { if (Ascii.toLower(buffer.get(i)) != Ascii.toLower(s.charAt(i - start))) { return false; } } return true; } public boolean equalsIgnoreCase(final byte[] b) { return equalsIgnoreCase(b, 0, b.length); } public boolean equalsIgnoreCase(final byte[] b, final int offset, final int len) { if (getLength() != len) { return false; } int offs1 = start; int offs2 = offset; for (int i = 0; i < len; i++) { if (Ascii.toLower(buffer.get(offs1++)) != Ascii.toLower(b[offs2++])) { return false; } } return true; }
Compares the message Buffer to the specified char array ignoring case considerations.
Params:
  • b – the char[] to compare
  • offset – the offset in the array
  • len – number of chars to check
Returns:true if the comparison succeeded, false otherwise
Since:2.3
/** * Compares the message Buffer to the specified char array ignoring case considerations. * @param b the <code>char[]</code> to compare * @param offset the offset in the array * @param len number of chars to check * * @return true if the comparison succeeded, false otherwise * * @since 2.3 */
public boolean equalsIgnoreCase(final char[] b, int offset, final int len) { if (getLength() != len) { return false; } for (int i = start; i < end; i++) { if (Ascii.toLower(buffer.get(i)) != Ascii.toLower(b[offset++])) { return false; } } return true; }
Compares the buffer chunk to the specified byte array representing lower-case ASCII characters.
Params:
  • b – the byte[] to compare
Returns:true if the comparison succeeded, false otherwise
Since:2.1.2
/** * Compares the buffer chunk to the specified byte array representing * lower-case ASCII characters. * * @param b the <code>byte[]</code> to compare * * @return true if the comparison succeeded, false otherwise * * @since 2.1.2 */
public boolean equalsIgnoreCaseLowerCase(final byte[] b) { return equalsIgnoreCaseLowerCase(buffer, start, end, b); } @Override public String toString() { return toString(null); } public String toString(Charset charset) { if (charset == null) charset = DEFAULT_CHARSET; if (cachedString != null && charset.equals(cachedStringCharset)) { return cachedString; } cachedString = buffer.toStringContent(charset, start, end); cachedStringCharset = charset; return cachedString; } @Override public String toString(final int start, final int end) { return buffer.toStringContent(DEFAULT_CHARSET, this.start + start, this.start + end); } protected final void resetStringCache() { cachedString = null; cachedStringCharset = null; } protected final void reset() { buffer = null; start = -1; end = -1; limit = -1; resetStringCache(); } public final void recycle() { reset(); }
Notify the Chunk that its content is going to be changed directly
/** * Notify the Chunk that its content is going to be changed directly */
protected void notifyDirectUpdate() { } public static int indexOf(final Buffer buffer, int off, final int end, final char qq) { // Works only for UTF while (off < end) { final byte b = buffer.get(off); if (b == qq) { return off; } off++; } return -1; } public static int indexOf(final Buffer buffer, int off, final int end, final CharSequence s) { // Works only for UTF final int strLen = s.length(); if (strLen == 0) { return off; } if (strLen > (end - off)) return -1; int strOffs = 0; final int lastOffs = end - strLen; while (off <= lastOffs + strOffs) { final byte b = buffer.get(off); if (b == s.charAt(strOffs)) { strOffs++; if (strOffs == strLen) { return off - strLen + 1; } } else { strOffs = 0; } off++; } return -1; }
Returns:-1, 0 or +1 if inferior, equal, or superior to the String.
/** * @return -1, 0 or +1 if inferior, equal, or superior to the String. */
public int compareIgnoreCase(int start, int end, String compareTo) { int result = 0; int len = compareTo.length(); if ((end - start) < len) { len = end - start; } for (int i = 0; (i < len) && (result == 0); i++) { if (Ascii.toLower(buffer.get(i + start)) > Ascii.toLower(compareTo.charAt(i))) { result = 1; } else if (Ascii.toLower(buffer.get(i + start)) < Ascii.toLower(compareTo.charAt(i))) { result = -1; } } if (result == 0) { if (compareTo.length() > (end - start)) { result = -1; } else if (compareTo.length() < (end - start)) { result = 1; } } return result; }
Returns:-1, 0 or +1 if inferior, equal, or superior to the String.
/** * @return -1, 0 or +1 if inferior, equal, or superior to the String. */
public int compare(int start, int end, String compareTo) { int result = 0; int len = compareTo.length(); if ((end - start) < len) { len = end - start; } for (int i = 0; (i < len) && (result == 0); i++) { if (buffer.get(i + start) > compareTo.charAt(i)) { result = 1; } else if (buffer.get(i + start) < compareTo.charAt(i)) { result = -1; } } if (result == 0) { if (compareTo.length() > (end - start)) { result = -1; } else if (compareTo.length() < (end - start)) { result = 1; } } return result; }
Compares the buffer chunk to the specified byte array representing lower-case ASCII characters.
Params:
  • buffer – the byte[] to compare
  • start – buffer start
  • end – buffer end
  • cmpTo – byte[] to compare against
Returns:true if the comparison succeeded, false otherwise
Since:2.1.2
/** * Compares the buffer chunk to the specified byte array representing * lower-case ASCII characters. * * @param buffer the <code>byte[]</code> to compare * @param start buffer start * @param end buffer end * @param cmpTo byte[] to compare against * * @return true if the comparison succeeded, false otherwise * * @since 2.1.2 */
public static boolean equalsIgnoreCaseLowerCase(final Buffer buffer, final int start, final int end, final byte[] cmpTo) { final int len = end - start; if (len != cmpTo.length) { return false; } for (int i = 0; i < len; i++) { if (Ascii.toLower(buffer.get(i + start)) != cmpTo[i]) { return false; } } return true; } public static boolean startsWith(final Buffer buffer, final int start, final int end, final byte[] cmpTo) { final int len = end - start; if (len < cmpTo.length) { return false; } for (int i = 0; i < cmpTo.length; i++) { if (buffer.get(start + i) != cmpTo[i]) { return false; } } return true; } public void trimLeft() { boolean modified = false; while (buffer.get(start) <= 0x20) { modified = true; start++; } if (modified) { resetStringCache(); } } }