ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/tomcat/util/buf/MessageBytes.java

Path
ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/tomcat/util/buf/MessageBytes.java
Status
scanned
Type
file
Name
MessageBytes.java
Extension
.java
Programming language
Java
Mime type
text/plain
File type
ASCII text, with CRLF line terminators
Tag

      
    
Rootfs path

      
    
Size
17901 (17.5 KB)
MD5
a412522ad3771ae94e1422c9ed3bb798
SHA1
b3f22cf54cb81870fed3b3548656f2c14b7983f3
SHA256
9044c61dfd34b3c8dfb01b13fa8c1213d4dd024a3096b4c72819650a6af01f5f
SHA512

      
    
SHA1_git
f642e804c651114a43821e89b982cdf68487e21d
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
MessageBytes.java | 17.5 KB |

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF 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. */ package org.apache.tomcat.util.buf; import java.io.IOException; import java.io.Serial; import java.io.Serializable; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.CodingErrorAction; import java.util.Locale; /** * This class is used to represent a sub array of bytes in an HTTP message. It represents all request/response elements. * The byte/char conversions are delayed and cached. Everything is recyclable. * <p> * The object can represent a byte[], a char[], or a (sub) String. All operations can be made in case-sensitive mode or * not. */ public final class MessageBytes implements Cloneable, Serializable { @Serial private static final long serialVersionUID = 1L; // primary type ( whatever is set as original value ) private int type = T_NULL; public static final int T_NULL = 0; /** * getType() is T_STR if the object used to create the MessageBytes was a String. */ public static final int T_STR = 1; /** * getType() is T_BYTES if the object used to create the MessageBytes was a byte[]. */ public static final int T_BYTES = 2; /** * getType() is T_CHARS if the object used to create the MessageBytes was a char[]. */ public static final int T_CHARS = 3; public static final char[] EMPTY_CHAR_ARRAY = new char[0]; private int hashCode = 0; // did we compute the hashcode ? private boolean hasHashCode = false; // Internal objects to represent array + offset, and specific methods private final ByteChunk byteC = new ByteChunk(); private final CharChunk charC = new CharChunk(); // String private String strValue; /** * Creates a new, uninitialized MessageBytes object. Use static newInstance() in order to allow future hooks. */ private MessageBytes() { } /** * Construct a new MessageBytes instance. * * @return the instance */ public static MessageBytes newInstance() { return factory.newInstance(); } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } public boolean isNull() { return type == T_NULL; } /** * Resets the message bytes to an uninitialized (NULL) state. */ public void recycle() { type = T_NULL; byteC.recycle(); charC.recycle(); strValue = null; hasHashCode = false; hasLongValue = false; } /** * Sets the content to the specified sub array of bytes. * * @param b the bytes * @param off the start offset of the bytes * @param len the length of the bytes */ public void setBytes(byte[] b, int off, int len) { byteC.setBytes(b, off, len); type = T_BYTES; hasHashCode = false; hasLongValue = false; } /** * Sets the content to be a char[] * * @param c the chars * @param off the start offset of the chars * @param len the length of the chars */ public void setChars(char[] c, int off, int len) { charC.setChars(c, off, len); type = T_CHARS; hasHashCode = false; hasLongValue = false; } /** * Set the content to be a string * * @param s The string */ public void setString(String s) { strValue = s; hasHashCode = false; hasLongValue = false; if (s == null) { type = T_NULL; } else { type = T_STR; } } // -------------------- Conversion and getters -------------------- /** * Compute the string value. * * @return the string */ @Override public String toString() { switch (type) { case T_NULL: case T_STR: // No conversion required break; case T_BYTES: strValue = byteC.toString(); break; case T_CHARS: strValue = charC.toString(); break; } return strValue; } /** * Convert to String (if not already of the String type) and then return the String value. * * @return The current value as a String */ public String toStringType() { switch (type) { case T_NULL: case T_STR: // No conversion required break; case T_BYTES: setString(byteC.toString()); break; case T_CHARS: setString(charC.toString()); break; } return strValue; } // ---------------------------------------- /** * Return the type of the original content. Can be T_STR, T_BYTES, T_CHARS or T_NULL * * @return the type */ public int getType() { return type; } /** * Returns the byte chunk, representing the byte[] and offset/length. Valid only if T_BYTES or after a conversion * was made. * * @return the byte chunk */ public ByteChunk getByteChunk() { return byteC; } /** * Returns the char chunk, representing the char[] and offset/length. Valid only if T_CHARS or after a conversion * was made. * * @return the char chunk */ public CharChunk getCharChunk() { return charC; } /** * Returns the string value. Valid only if T_STR or after a conversion was made. * * @return the string */ public String getString() { return strValue; } /** * @return the Charset used for string&lt;-&gt;byte conversions. */ public Charset getCharset() { return byteC.getCharset(); } /** * Set the Charset used for string&lt;-&gt;byte conversions. * * @param charset The charset */ public void setCharset(Charset charset) { byteC.setCharset(charset); } /** * Convert to bytes and fill the ByteChunk with the converted value. */ public void toBytes() { if (type == T_NULL) { byteC.recycle(); return; } if (type == T_BYTES) { // No conversion required return; } ByteBuffer bb; CharsetEncoder encoder = getCharset().newEncoder(); encoder.onMalformedInput(CodingErrorAction.REPORT); encoder.onUnmappableCharacter(CodingErrorAction.REPORT); try { if (type == T_CHARS) { bb = encoder.encode(CharBuffer.wrap(charC)); } else { // Must be T_STR bb = encoder.encode(CharBuffer.wrap(strValue)); } } catch (CharacterCodingException cce) { // Some calls to this conversion originate in application code and // the Servlet API methods do not declare a suitable exception that // can be thrown. Therefore, stick with the uncaught exception type // used by the old, pre-Java 16 optimised version of this code. throw new IllegalArgumentException(cce); } byteC.setBytes(bb.array(), bb.arrayOffset(), bb.limit()); } /** * Convert to char[] and fill the CharChunk. * <p> * Note: The conversion from bytes is not optimised - it converts to String first. However, Tomcat doesn't call this * method to convert from bytes so there is no benefit from optimising that path. */ public void toChars() { switch (type) { case T_NULL: charC.recycle(); //$FALL-THROUGH$ case T_CHARS: // No conversion required return; case T_BYTES: toString(); //$FALL-THROUGH$ case T_STR: { char[] cc = strValue.toCharArray(); charC.setChars(cc, 0, cc.length); } } } /** * Returns the length of the original buffer. * <p> * Note: The length in bytes may be different from the length in chars. * * @return the length */ public int getLength() { if (type == T_BYTES) { return byteC.getLength(); } if (type == T_CHARS) { return charC.getLength(); } if (type == T_STR) { return strValue.length(); } toString(); if (strValue == null) { return 0; } return strValue.length(); } // -------------------- equals -------------------- /** * Compares the message bytes to the specified String object. * * @param s the String to compare * * @return <code>true</code> if the comparison succeeded, <code>false</code> otherwise */ public boolean equals(String s) { return switch (type) { case T_STR -> { if (strValue == null) { yield s == null; } yield strValue.equals(s); } case T_CHARS -> charC.equals(s); case T_BYTES -> byteC.equals(s); default -> false; }; } /** * Compares the message bytes to the specified String object. * * @param s the String to compare * * @return <code>true</code> if the comparison succeeded, <code>false</code> otherwise */ public boolean equalsIgnoreCase(String s) { return switch (type) { case T_STR -> { if (strValue == null) { yield s == null; } yield strValue.equalsIgnoreCase(s); } case T_CHARS -> charC.equalsIgnoreCase(s); case T_BYTES -> byteC.equalsIgnoreCase(s); default -> false; }; } @Override public boolean equals(Object obj) { if (obj instanceof MessageBytes) { return equals((MessageBytes) obj); } return false; } public boolean equals(MessageBytes mb) { if (type == T_STR) { return mb.equals(strValue); } if (mb.type != T_CHARS && mb.type != T_BYTES) { // it's a string or int/date string value return equals(mb.toString()); } // mb is either CHARS or BYTES. // this is either CHARS or BYTES // Deal with the 4 cases ( in fact 3, one is symmetric) if (mb.type == T_CHARS && type == T_CHARS) { return charC.equals(mb.charC); } if (mb.type == T_BYTES && type == T_BYTES) { return byteC.equals(mb.byteC); } if (mb.type == T_CHARS && type == T_BYTES) { return byteC.equals(mb.charC); } if (mb.type == T_BYTES && type == T_CHARS) { return mb.byteC.equals(charC); } // can't happen return true; } /** * @return <code>true</code> if the message bytes starts with the specified string. * * @param s the string * @param pos The start position */ public boolean startsWithIgnoreCase(String s, int pos) { switch (type) { case T_STR: if (strValue == null) { return false; } if (strValue.length() < pos + s.length()) { return false; } for (int i = 0; i < s.length(); i++) { if (Ascii.toLower(s.charAt(i)) != Ascii.toLower(strValue.charAt(pos + i))) { return false; } } return true; case T_CHARS: return charC.startsWithIgnoreCase(s, pos); case T_BYTES: return byteC.startsWithIgnoreCase(s, pos); default: return false; } } // -------------------- Hash code -------------------- @Override public int hashCode() { if (hasHashCode) { return hashCode; } int code = hash(); hashCode = code; hasHashCode = true; return code; } // normal hash. private int hash() { return switch (type) { case T_STR -> { int code = 0; // We need to use the same hash function for (int i = 0; i < strValue.length(); i++) { code = code * 37 + strValue.charAt(i); } yield code; } case T_CHARS -> charC.hash(); case T_BYTES -> byteC.hash(); default -> 0; }; } // Inefficient initial implementation. Will be replaced on the next // round of tune-up public int indexOf(String s, int starting) { toString(); return strValue.indexOf(s, starting); } // Inefficient initial implementation. Will be replaced on the next // round of tune-up public int indexOf(String s) { return indexOf(s, 0); } public int indexOfIgnoreCase(String s, int starting) { toString(); String upper = strValue.toUpperCase(Locale.ENGLISH); String sU = s.toUpperCase(Locale.ENGLISH); return upper.indexOf(sU, starting); } /** * Copy the src into this MessageBytes, allocating more space if needed. * * @param src The source * * @throws IOException Writing overflow data to the output channel failed */ public void duplicate(MessageBytes src) throws IOException { switch (src.getType()) { case T_BYTES: type = T_BYTES; ByteChunk bc = src.getByteChunk(); byteC.allocate(2 * bc.getLength(), -1); byteC.append(bc); break; case T_CHARS: type = T_CHARS; CharChunk cc = src.getCharChunk(); charC.allocate(2 * cc.getLength(), -1); charC.append(cc); break; case T_STR: type = T_STR; String sc = src.getString(); this.setString(sc); break; } setCharset(src.getCharset()); } // efficient long private long longValue; private boolean hasLongValue = false; /** * Set the buffer to the representation of a long. * * @param l The long */ public void setLong(long l) { byteC.allocate(32, 64); long current = l; byte[] buf = byteC.getBuffer(); int start = 0; int end = 0; if (l == 0) { buf[end++] = (byte) '0'; } if (l < 0) { current = -l; buf[end++] = (byte) '-'; } while (current > 0) { int digit = (int) (current % 10); current = current / 10; buf[end++] = HexUtils.getHex(digit); } byteC.setStart(0); byteC.setEnd(end); // Inverting buffer end--; if (l < 0) { start++; } while (end > start) { byte temp = buf[start]; buf[start] = buf[end]; buf[end] = temp; start++; end--; } longValue = l; hasHashCode = false; hasLongValue = true; type = T_BYTES; } /** * Convert the buffer to a long, cache the value. Used for headers conversion. * * @return the long value */ public long getLong() { if (hasLongValue) { return longValue; } if (type == T_BYTES) { longValue = byteC.getLong(); } else { longValue = Long.parseLong(toString()); } hasLongValue = true; return longValue; } // -------------------- Future may be different -------------------- private static final MessageBytesFactory factory = new MessageBytesFactory(); private static class MessageBytesFactory { protected MessageBytesFactory() { } public MessageBytes newInstance() { return new MessageBytes(); } } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
6.63
Copyrights

      
    
Holders

      
    
Authors

      
    
License detections License expression License expression SPDX
apache_2_0-4bde3f57-78aa-4201-96bf-531cba09e7de apache-2.0 Apache-2.0
URL Start line End line
http://www.apache.org/licenses/LICENSE-2.0 9 9