503 lines
13 KiB
Java
503 lines
13 KiB
Java
/*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (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.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is the Netscape Messaging Access SDK Version 3.5 code,
|
|
* released on or about June 15, 1998. *
|
|
* The Initial Developer of the Original Code is Netscape Communications
|
|
* Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s): ______________________________________.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 1997 and 1998 Netscape Communications Corporation
|
|
* (http://home.netscape.com/misc/trademarks.html)
|
|
*/
|
|
|
|
package netscape.messaging.mime;
|
|
|
|
import java.io.BufferedOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
|
|
/**
|
|
* ByteString is similar to java.lang.String, except that it contains
|
|
* bytes instead of characters.
|
|
*/
|
|
public class ByteString extends Object implements Cloneable {
|
|
protected byte[] buffer;
|
|
protected int lo;
|
|
protected int hi;
|
|
|
|
protected static byte CR = '\r';
|
|
protected static byte LF = '\n';
|
|
protected static byte[] CRLF = "\r\n".getBytes();
|
|
|
|
protected ByteString () {
|
|
buffer = null;
|
|
lo = 0;
|
|
hi = 0;
|
|
}
|
|
|
|
public ByteString (String string) {
|
|
buffer = string.getBytes();
|
|
lo = 0;
|
|
hi = buffer.length;
|
|
}
|
|
|
|
public ByteString (byte[] buffer) {
|
|
this.buffer = buffer;
|
|
this.lo = 0;
|
|
this.hi = buffer.length;
|
|
}
|
|
|
|
public ByteString (byte[] buffer, int lo, int hi) {
|
|
if (lo > hi) throw new IndexOutOfBoundsException();
|
|
this.buffer = buffer;
|
|
this.lo = lo;
|
|
this.hi = hi;
|
|
}
|
|
|
|
/**
|
|
* hashCode() -- Overrides java.lang.Object.hashCode()
|
|
*/
|
|
public final int hashCode () {
|
|
int result = 0;
|
|
// TODO: improve hash function
|
|
for (int i = lo; i < hi; i++) {
|
|
result += buffer[i];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* equals() -- Overrides java.lang.Object.equals()
|
|
*/
|
|
public final boolean equals (Object that) {
|
|
if (this == that)
|
|
return true;
|
|
else
|
|
return equals(((ByteString) that).buffer, ((ByteString) that).lo, ((ByteString) that).size());
|
|
}
|
|
|
|
public final boolean equals (byte[] buf) {
|
|
return equals(buf, 0, buf.length);
|
|
}
|
|
|
|
public final boolean equals (byte[] buf, int low, int length) {
|
|
|
|
if (this.size() != length)
|
|
return false;
|
|
for (int i = 0; i < length; i++) {
|
|
if (this.byteAt(i) != buf[low++])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compare this to that in lex order
|
|
*/
|
|
public final boolean greaterOrEqual (ByteString that) {
|
|
int m = this.size();
|
|
int n = that.size();
|
|
for (int i = 0; i < n; i++) {
|
|
if (i >= m)
|
|
return false;
|
|
else {
|
|
byte x = this.byteAt(i);
|
|
byte y = that.byteAt(i);
|
|
if (x < y)
|
|
return false;
|
|
else if (x > y)
|
|
return true;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Return number of bytes
|
|
*/
|
|
public final int size () {
|
|
return (hi-lo);
|
|
}
|
|
|
|
/**
|
|
* Return byte at given offset.
|
|
*/
|
|
public final byte byteAt (int i) {
|
|
if (lo + i >= hi) throw new IndexOutOfBoundsException();
|
|
return buffer[lo+i];
|
|
}
|
|
|
|
/**
|
|
* Check whether the ByteString starts with the given byte[].
|
|
* Ignores case.
|
|
*/
|
|
private final boolean startsWith (byte[] that) {
|
|
if (that.length > hi - lo) return false;
|
|
for (int i = 0; i < that.length; i++) {
|
|
char x = (char)(buffer[lo+i]);
|
|
char y = (char)(that[i]);
|
|
if (x != y && (x < 'a' || x > 'z' || (x + 'A' - 'a') != y))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns the offset for the first occurrence of a given byte
|
|
* in the ByteString if present, and -1 otherwise.
|
|
*/
|
|
public final int firstIndexOf (byte x) {
|
|
for (int i = lo; i < hi; i++) {
|
|
if (buffer[i] == x)
|
|
return (i-lo);
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
/**
|
|
* Returns the offset for the first occurrence of a given byte
|
|
* in the ByteString to the right of the given offset if present, and
|
|
* -1 otherwise.
|
|
*/
|
|
public final int firstIndexOf (byte x, int offset) {
|
|
for (int i = lo+offset+1; i < hi; i++) {
|
|
if (buffer[i] == x)
|
|
return (i-lo);
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
/**
|
|
* Returns the offset for the last occurrence of a given byte
|
|
* in the ByteString if present, and -1 otherwise.
|
|
*/
|
|
public final int lastIndexOf (byte x) {
|
|
for (int i = hi; --i >= lo; ) {
|
|
if (buffer[i] == x)
|
|
return (i-lo);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Returns the offset for the last occurrence of a given byte
|
|
* in the ByteString to the left of the given offset if present, and
|
|
* -1 otherwise.
|
|
*/
|
|
public final int lastIndexOf (byte x, int offset) {
|
|
for (int i = lo+offset; --i >= lo; ) {
|
|
if (buffer[i] == x)
|
|
return (i-lo);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Return the offset for the first occurrence of a given byte[]
|
|
* in the ByteString to the right of the given offset if present,
|
|
* and -1 otherwise. Case insensitive.
|
|
*/
|
|
public final int firstIndexOf (byte[] that) {
|
|
return firstIndexOf(that, 0);
|
|
}
|
|
|
|
/**
|
|
* Return the offset for the first occurrence of a given byte[]
|
|
* in the ByteString to the right of the given offset if present,
|
|
* and -1 otherwise. Case insensitive.
|
|
*/
|
|
public final int firstIndexOf (byte[] that, int offset) {
|
|
|
|
for (int i = lo+offset; i <= hi-that.length; i++) {
|
|
int j;
|
|
for (j = 0; j < that.length; j++) {
|
|
char x = (char)(buffer[i+j]);
|
|
char y = (char)(that[j]);
|
|
if (x != y && (x < 'a' || x > 'z' || (x + 'A' - 'a') != y))
|
|
break;
|
|
}
|
|
if (j >= that.length) {
|
|
return i-lo;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Convert to String
|
|
*/
|
|
public final String toString () {
|
|
return new String(buffer, lo, hi-lo);
|
|
}
|
|
|
|
/**
|
|
* Convert to byte[]
|
|
* clone version
|
|
*/
|
|
public final byte[] getBytes () { return getBytes( true ); }
|
|
|
|
/**
|
|
* Convert to byte[]
|
|
* clonable version
|
|
*/
|
|
public final byte[] getBytes ( boolean clone )
|
|
{
|
|
if ( clone )
|
|
{
|
|
byte[] result = new byte[hi-lo];
|
|
System.arraycopy(buffer, lo, result, 0, hi-lo);
|
|
return result;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
public final byte[] getBytes ( int len[] )
|
|
{
|
|
len[0] = hi;
|
|
return buffer;
|
|
}
|
|
|
|
/**
|
|
* Extract X out of ByteString X+Y+Z, where Y is the given delimiter.
|
|
* Replace original ByteString (X+Y+Z) by Z. Not MT-safe.
|
|
* Returns null if Y is absent.
|
|
*/
|
|
public final ByteString extractTill (byte[] y) {
|
|
int offset = firstIndexOf(y, 0);
|
|
if (offset < 0) {
|
|
return null;
|
|
}
|
|
else {
|
|
ByteString result = substring(0, offset);
|
|
lo += (offset + y.length);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Remove Y from ByteString Y+Z, where Y is the given token,
|
|
* If successful, replace original ByteString (Y+Z) by Z, and
|
|
* & return true. Else return false. Not MT-safe.
|
|
* Throw ArrayIndexOutOfBoundsException if ByteString does
|
|
* not start with Y.
|
|
*/
|
|
public final boolean extractToken (byte[] y) {
|
|
if (startsWith(y)) {
|
|
lo += y.length;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Discard COUNT bytes from ByteString
|
|
*/
|
|
public final void discard(int count) {
|
|
if (lo + count > hi) throw new IndexOutOfBoundsException();
|
|
lo += count;
|
|
}
|
|
|
|
/**
|
|
* Discard X+Y from ByteString X+Y+Z, where Y is the given token.
|
|
* Throw ArrayIndexOutOfBoundsException if Y is absent.
|
|
*/
|
|
public final void discardTill (byte[] y) {
|
|
lo += (firstIndexOf(y, 0) + y.length);
|
|
}
|
|
|
|
/**
|
|
* Discard X+Y from ByteString X+Y+Z, where Y is LF or CRLF.
|
|
* Throw ArrayIndexOutOfBoundsException if Y is absent.
|
|
*/
|
|
public final void discardLine () {
|
|
lo += (firstIndexOf(LF, 0) + 1);
|
|
}
|
|
|
|
/**
|
|
* Checks for absence of complete lines
|
|
*/
|
|
public final boolean noLines () {
|
|
return firstIndexOf(LF, 0) < 0;
|
|
}
|
|
|
|
/**
|
|
* Checks for absence of complete lines to the right of the given offset
|
|
*/
|
|
public final boolean noLines (int offset) {
|
|
return firstIndexOf(LF, offset) < 0;
|
|
}
|
|
|
|
/*
|
|
* Extract the first line and return it as a substring.
|
|
* Throw ArrayIndexOutOfBoundsException if there are no lines.
|
|
*/
|
|
public final ByteString extractLine() {
|
|
ByteString result;
|
|
int offset = firstIndexOf(LF, 0);
|
|
if (offset > 1 && byteAt(offset-1) == CR) {
|
|
result = substring(0, offset-1);
|
|
lo += (offset + 1);
|
|
}
|
|
else {
|
|
result = substring(0, offset);
|
|
lo += (offset + 1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* Extract the first line and write it to given output stream.
|
|
* Throw ArrayIndexOutOfBoundsException if there are no lines.
|
|
* return number of byte wrote
|
|
*/
|
|
|
|
public final int extractLine(OutputStream stream)
|
|
throws IOException {
|
|
|
|
int origin = lo;
|
|
|
|
int offset = firstIndexOf(LF, 0);
|
|
if (offset > 1 && byteAt(offset-1) == CR) {
|
|
stream.write(buffer, lo, offset-1);
|
|
stream.write(CRLF);
|
|
lo += (offset + 1);
|
|
}
|
|
else {
|
|
stream.write(buffer, lo, offset);
|
|
stream.write(CRLF);
|
|
lo += (offset + 1);
|
|
}
|
|
return (lo - origin);
|
|
}
|
|
|
|
public void discardLeadingSpace()
|
|
{
|
|
// skip leading space
|
|
for (; lo < hi; lo++) {
|
|
if (buffer[lo] != ' ' )
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* bulk write to a stream.
|
|
*/
|
|
public final void write (OutputStream stream)
|
|
throws IOException {
|
|
|
|
stream.write(buffer, lo, hi-lo);
|
|
}
|
|
|
|
/**
|
|
* Extract a long
|
|
**/
|
|
public final long extractLong() {
|
|
long n = 0;
|
|
for (int i = lo; i < lo+8; i++) {
|
|
n <<= 8;
|
|
n += buffer[i];
|
|
}
|
|
lo += 8;
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
* Extract an int
|
|
**/
|
|
public final int extractInt() {
|
|
int n = 0;
|
|
for (int i = lo; i < lo+4; i++) {
|
|
n <<= 8;
|
|
n += buffer[i];
|
|
}
|
|
lo += 4;
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
* Return substring from offset start (inclusive) till offset
|
|
* finish (exclusive).
|
|
*/
|
|
public ByteString substring (int start, int finish) {
|
|
if (start > finish || lo+finish > hi)
|
|
throw new IndexOutOfBoundsException();
|
|
return new ByteString(buffer, lo+start, lo+finish);
|
|
}
|
|
|
|
/**
|
|
* Return substring from offset start (inclusive) till the
|
|
* end of the ByteString.
|
|
*/
|
|
public ByteString substring (int start) {
|
|
if (lo+start > hi) throw new IndexOutOfBoundsException();
|
|
return new ByteString(buffer, lo+start, hi);
|
|
}
|
|
|
|
/**
|
|
* Display the contents of the ByteString for
|
|
* the purpose of debugging
|
|
*/
|
|
public void displayBuffer (String caption, BufferedOutputStream stream) {
|
|
int saved = lo;
|
|
|
|
try {
|
|
for (int i = 0; !noLines(); i++) {
|
|
stream.write(caption.getBytes());
|
|
extractLine(stream);
|
|
}
|
|
stream.write(caption.getBytes());
|
|
stream.write(getBytes());
|
|
lo = saved;
|
|
stream.flush();
|
|
} catch (IOException e) {
|
|
System.err.println(e + " while displaying contents of ByteString");
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Clones an instance of the ByteString object.
|
|
* @exception CloneNotSupportedException could be thrown by constituent components.
|
|
*/
|
|
public Object clone () throws CloneNotSupportedException
|
|
{
|
|
ByteString l_theClone = (ByteString) super.clone();
|
|
|
|
if (buffer != null)
|
|
{
|
|
l_theClone.buffer = new byte [buffer.length];
|
|
System.arraycopy (buffer, 0, l_theClone.buffer, 0, buffer.length);
|
|
}
|
|
|
|
return (l_theClone);
|
|
}
|
|
|
|
|
|
public void setSize( int len )
|
|
{
|
|
hi = len;
|
|
lo = 0;
|
|
}
|
|
}
|