/* * 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;
/** * Utilities to manipulate char chunks. While String is the easiest way to manipulate chars ( search, substrings, etc), * it is known to not be the most efficient solution - Strings are designed as immutable and secure objects. * * @author dac@sun.com * @author James Todd [gonzo@sun.com] * @author Costin Manolache * @author Remy Maucherat
*/ publicfinalclass CharChunk extends AbstractChunk implements CharSequence {
privatestaticfinallong serialVersionUID = 1L;
/** * Input interface, used when the buffer is empty.
*/ publicinterface CharInputChannel {
/** * Read new characters. * * @return The number of characters read * * @throws IOException If an I/O error occurs during reading
*/ int realReadChars() throws IOException;
}
/** * When we need more space we'll either grow the buffer ( up to the limit ) or send it to a channel.
*/ publicinterface CharOutputChannel {
/** * Send the bytes ( usually the internal conversion buffer ). Expect 8k output if the buffer is full. * * @param buf characters that will be written * @param off offset in the characters array * @param len length that will be written * * @throws IOException If an I/O occurs while writing the characters
*/ void realWriteChars(char buf[], int off, int len) throws IOException;
}
// --------------------
// char[] privatechar[] buff;
// transient as serialization is primarily for values via, e.g. JMX privatetransient CharInputChannel in = null; privatetransient CharOutputChannel out = null;
/** * Creates a new, uninitialized CharChunk object.
*/ public CharChunk() {
}
public CharChunk(int initial) {
allocate(initial, -1);
}
// --------------------
@Override public Object clone() throws CloneNotSupportedException { returnsuper.clone();
}
/** * Sets the buffer to the specified subarray of characters. * * @param c the characters * @param off the start offset of the characters * @param len the length of the characters
*/ publicvoid setChars(char[] c, int off, int len) {
buff = c;
start = off;
end = start + len;
isSet = true;
hasHashCode = false;
}
/** * When the buffer is empty, read the data from the input channel. * * @param in The input channel
*/ publicvoid setCharInputChannel(CharInputChannel in) { this.in = in;
}
/** * When the buffer is full, write the data to the output channel. Also used when large amount of data is appended. * If not set, the buffer will grow to the limit. * * @param out The output channel
*/ publicvoid setCharOutputChannel(CharOutputChannel out) { this.out = out;
}
// -------------------- Adding data to the buffer --------------------
publicvoid append(char c) throws IOException {
makeSpace(1); int limit = getLimitInternal();
// couldn't make space if (end >= limit) {
flushBuffer();
}
buff[end++] = c;
}
/** * Add data to the buffer. * * @param src Char array * @param off Offset * @param len Length * * @throws IOException Writing overflow data to the output channel failed
*/ publicvoid append(char src[], int off, int len) throws IOException { // will grow, up to limit
makeSpace(len); int limit = getLimitInternal();
// Optimize on a common case. // If the buffer is empty and the source is going to fill up all the // space in buffer, may as well write it directly to the output, // and avoid an extra copy if (len == limit && end == start && out != null) {
out.realWriteChars(src, off, len); return;
}
// if we are below the limit if (len <= limit - end) {
System.arraycopy(src, off, buff, end, len);
end += len; return;
}
// Need more space than we can afford, need to flush buffer.
// The buffer is already at (or bigger than) limit.
// Optimization: // If len-avail < length (i.e. after we fill the buffer with what we // can, the remaining will fit in the buffer) we'll just copy the first // part, flush, then copy the second part - 1 write and still have some // space for more. We'll still have 2 writes, but we write more on the first.
if (len + end < 2 * limit) { /* * If the request length exceeds the size of the output buffer, flush the output buffer and then write the * data directly. We can't avoid 2 writes, but we can write more on the second
*/ int avail = limit - end;
System.arraycopy(src, off, buff, end, avail);
end += avail;
flushBuffer();
System.arraycopy(src, off + avail, buff, end, len - avail);
end += len - avail;
} else { // len > buf.length + avail // long write - flush the buffer and write the rest // directly from source
flushBuffer();
out.realWriteChars(src, off, len);
}
}
/** * Append a string to the buffer. * * @param s The string * * @throws IOException Writing overflow data to the output channel failed
*/ publicvoid append(String s) throws IOException {
append(s, 0, s.length());
}
/** * Append a string to the buffer. * * @param s The string * @param off Offset * @param len Length * * @throws IOException Writing overflow data to the output channel failed
*/ publicvoid append(String s, int off, int len) throws IOException { if (s == null) { return;
}
// will grow, up to limit
makeSpace(len); int limit = getLimitInternal();
int sOff = off; int sEnd = off + len; while (sOff < sEnd) { int d = min(limit - end, sEnd - sOff);
s.getChars(sOff, sOff + d, buff, end);
sOff += d;
end += d; if (end >= limit) {
flushBuffer();
}
}
}
// -------------------- Removing data from the buffer --------------------
publicint subtract(char dest[], int off, int len) throws IOException { if (checkEof()) { return -1;
} int n = len; if (len > getLength()) {
n = getLength();
}
System.arraycopy(buff, start, dest, off, n);
start += n; return n;
}
privateboolean checkEof() throws IOException { if ((end - start) == 0) { if (in == null) { returntrue;
} int n = in.realReadChars(); if (n < 0) { returntrue;
}
} returnfalse;
}
/** * Send the buffer to the sink. Called by append() when the limit is reached. You can also call it explicitly to * force the data to be written. * * @throws IOException Writing overflow data to the output channel failed
*/ publicvoid flushBuffer() throws IOException { // assert out!=null if (out == null) { thrownew IOException(
sm.getString("chunk.overflow", Integer.valueOf(getLimit()), Integer.valueOf(buff.length)));
}
out.realWriteChars(buff, start, end - start);
end = start;
}
/** * Make space for len chars. If len is small, allocate a reserve space too. Never grow bigger than the limit or * {@link AbstractChunk#ARRAY_MAX_SIZE}. * * @param count The size
*/ publicvoid makeSpace(int count) { char[] tmp = null;
int limit = getLimitInternal();
long newSize; long desiredSize = end + count;
// Can't grow above the limit if (desiredSize > limit) {
desiredSize = limit;
}
if (buff == null) { if (desiredSize < 256) {
desiredSize = 256; // take a minimum
}
buff = newchar[(int) desiredSize];
}
// limit < buf.length (the buffer is already big) // or we already have space if (desiredSize <= buff.length) { return;
} // grow in larger chunks if (desiredSize < 2L * buff.length) {
newSize = buff.length * 2L;
} else {
newSize = buff.length * 2L + count;
}
/** * 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
*/ publicboolean equals(String s) { char[] c = buff; int len = end - start; if (c == null || len != s.length()) { returnfalse;
} int off = start; for (int i = 0; i < len; i++) { if (c[off++] != s.charAt(i)) { returnfalse;
}
} returntrue;
}
/** * 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
*/ publicboolean equalsIgnoreCase(String s) { char[] c = buff; int len = end - start; if (c == null || len != s.length()) { returnfalse;
} int off = start; for (int i = 0; i < len; i++) { if (Ascii.toLower(c[off++]) != Ascii.toLower(s.charAt(i))) { returnfalse;
}
} returntrue;
}
publicboolean equals(char b2[], int off2, int len2) { char b1[] = buff; if (b1 == null && b2 == null) { returntrue;
}
int len = end - start; if (len != len2 || b1 == null || b2 == null) { returnfalse;
}
int off1 = start;
while (len-- > 0) { if (b1[off1++] != b2[off2++]) { returnfalse;
}
} returntrue;
}
/** * @return <code>true</code> if the message bytes starts with the specified string. * * @param s The string
*/ publicboolean startsWith(String s) { char[] c = buff; int len = s.length(); if (c == null || len > end - start) { returnfalse;
} int off = start; for (int i = 0; i < len; i++) { if (c[off++] != s.charAt(i)) { returnfalse;
}
} returntrue;
}
/** * Returns true if the buffer starts with the specified string. * * @param s the string * @param pos The position * * @return <code>true</code> if the start matches
*/ publicboolean startsWithIgnoreCase(String s, int pos) { char[] c = buff; int len = s.length(); if (c == null || len + pos > end - start) { returnfalse;
} int off = start + pos; for (int i = 0; i < len; i++) { if (Ascii.toLower(c[off++]) != Ascii.toLower(s.charAt(i))) { returnfalse;
}
} returntrue;
}
/** * @return <code>true</code> if the message bytes end with the specified string. * * @param s The string
*/ publicboolean endsWith(String s) { char[] c = buff; int len = s.length(); if (c == null || len > end - start) { returnfalse;
} int off = end - len; for (int i = 0; i < len; i++) { if (c[off++] != s.charAt(i)) { returnfalse;
}
} returntrue;
}
publicint indexOf(char c) { return indexOf(c, start);
}
/** * Returns the first instance of the given character in this CharChunk starting at the specified char. If the * character is not found, -1 is returned. <br> * * @param c The character * @param starting The start position * * @return The position of the first instance of the character or -1 if the character is not found.
*/ publicint indexOf(char c, int starting) { int ret = indexOf(buff, start + starting, end, c); return (ret >= start) ? ret - start : -1;
}
/** * Returns the first instance of the given character in the given char array between the specified start and end. * <br> * * @param chars The array to search * @param start The point to start searching from in the array * @param end The point to stop searching in the array * @param s The character to search for * * @return The position of the first instance of the character or -1 if the character is not found.
*/ publicstaticint indexOf(char chars[], int start, int end, char s) { int offset = start;
while (offset < end) { char c = chars[offset]; if (c == s) { return offset;
}
offset++;
} return -1;
}
// -------------------- utils privateint min(int a, int b) { if (a < b) { return a;
} return b;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.