/* * 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; } }