diff options
author | Niklas Brunlid <niklas.brunlid@sonyericsson.com> | 2010-09-30 10:33:28 +0200 |
---|---|---|
committer | Steve Kondik <shade@chemlab.org> | 2011-03-08 18:40:50 -0500 |
commit | 085dab209a821eeb4193ee0bbc6d30fda4e01161 (patch) | |
tree | f4ed542c889d30a444ec09e24388f4138223357b /obex | |
parent | a268bff3cd8c794266a70b32a9d0c8c03744edd0 (diff) | |
download | frameworks_base-085dab209a821eeb4193ee0bbc6d30fda4e01161.zip frameworks_base-085dab209a821eeb4193ee0bbc6d30fda4e01161.tar.gz frameworks_base-085dab209a821eeb4193ee0bbc6d30fda4e01161.tar.bz2 |
Increase javax.obex performance
When a file is transferred over OBEX the javax.obex classes allocate
the same amount of memory multiple times simply to copy the same data
around internally, only to throw those allocations away when the next
data block is processed. In the Bluetooth OPP case that meant that
64Kb was allocated around 15 times for every 64Kb transferred. Since
the transfer speed is around 1.3Mbps that means almost 2.5Mb of
memory is garbage collected every second, increasing the time needed
for each block transfer.
Fixed by keeping data in reusable byte buffer objects instead.
The time to transfer a 39MB file is reduced by about 10-20%.
Change-Id: I182c0374c2915d1b37ca12200fb36de57dabc67e
Diffstat (limited to 'obex')
-rw-r--r-- | obex/javax/obex/ClientOperation.java | 46 | ||||
-rw-r--r-- | obex/javax/obex/ClientSession.java | 111 | ||||
-rw-r--r-- | obex/javax/obex/HeaderSet.java | 2 | ||||
-rw-r--r-- | obex/javax/obex/ObexByteBuffer.java | 326 | ||||
-rw-r--r-- | obex/javax/obex/ObexHelper.java | 64 | ||||
-rw-r--r-- | obex/javax/obex/ObexSession.java | 2 | ||||
-rw-r--r-- | obex/javax/obex/PrivateInputStream.java | 42 | ||||
-rw-r--r-- | obex/javax/obex/PrivateOutputStream.java | 41 | ||||
-rw-r--r-- | obex/javax/obex/ServerOperation.java | 85 | ||||
-rw-r--r-- | obex/javax/obex/ServerSession.java | 96 |
10 files changed, 554 insertions, 261 deletions
diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java index 05b498c..9e0700f 100644 --- a/obex/javax/obex/ClientOperation.java +++ b/obex/javax/obex/ClientOperation.java @@ -37,7 +37,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; -import java.io.ByteArrayOutputStream; /** * This class implements the <code>Operation</code> interface. It will read and @@ -72,6 +71,8 @@ public final class ClientOperation implements Operation, BaseStream { private boolean mEndOfBodySent; + private ObexByteBuffer mOutBuffer; + /** * Creates new OperationImpl to read and write data to a server * @param maxSize the maximum packet size @@ -100,6 +101,8 @@ public final class ClientOperation implements Operation, BaseStream { mRequestHeader = new HeaderSet(); + mOutBuffer = new ObexByteBuffer(32); + int[] headerList = header.getHeaderList(); if (headerList != null) { @@ -396,7 +399,7 @@ public final class ClientOperation implements Operation, BaseStream { */ private boolean sendRequest(int opCode) throws IOException { boolean returnValue = false; - ByteArrayOutputStream out = new ByteArrayOutputStream(); + int bodyLength = -1; byte[] headerArray = ObexHelper.createHeader(mRequestHeader, true); if (mPrivateOutput != null) { @@ -437,9 +440,9 @@ public final class ClientOperation implements Operation, BaseStream { throw new IOException("OBEX Packet exceeds max packet size"); } - byte[] sendHeader = new byte[end - start]; - System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length); - if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput)) { + mOutBuffer.reset(); + mOutBuffer.write(headerArray, start, end - start); + if (!mParent.sendRequest(opCode, mOutBuffer, mReplyHeader, mPrivateInput)) { return false; } @@ -456,7 +459,8 @@ public final class ClientOperation implements Operation, BaseStream { return false; } } else { - out.write(headerArray); + mOutBuffer.reset(); + mOutBuffer.write(headerArray); } if (bodyLength > 0) { @@ -471,8 +475,6 @@ public final class ClientOperation implements Operation, BaseStream { bodyLength = mMaxPacketSize - headerArray.length - 6; } - byte[] body = mPrivateOutput.readBytes(bodyLength); - /* * Since this is a put request if the final bit is set or * the output stream is closed we need to send the 0x49 @@ -480,44 +482,40 @@ public final class ClientOperation implements Operation, BaseStream { */ if ((mPrivateOutput.isClosed()) && (!returnValue) && (!mEndOfBodySent) && ((opCode & 0x80) != 0)) { - out.write(0x49); + mOutBuffer.write((byte)0x49); mEndOfBodySent = true; } else { - out.write(0x48); + mOutBuffer.write((byte)0x48); } bodyLength += 3; - out.write((byte)(bodyLength >> 8)); - out.write((byte)bodyLength); - - if (body != null) { - out.write(body); - } + mOutBuffer.write((byte)(bodyLength >> 8)); + mOutBuffer.write((byte)bodyLength); + mPrivateOutput.writeTo(mOutBuffer, bodyLength - 3); } if (mPrivateOutputOpen && bodyLength <= 0 && !mEndOfBodySent) { // only 0x82 or 0x83 can send 0x49 if ((opCode & 0x80) == 0) { - out.write(0x48); + mOutBuffer.write((byte)0x48); } else { - out.write(0x49); + mOutBuffer.write((byte)0x49); mEndOfBodySent = true; - } bodyLength = 3; - out.write((byte)(bodyLength >> 8)); - out.write((byte)bodyLength); + mOutBuffer.write((byte)(bodyLength >> 8)); + mOutBuffer.write((byte)bodyLength); } - if (out.size() == 0) { + if (mOutBuffer.getLength() == 0) { if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput)) { return false; } return returnValue; } - if ((out.size() > 0) - && (!mParent.sendRequest(opCode, out.toByteArray(), mReplyHeader, mPrivateInput))) { + if ((mOutBuffer.getLength() > 0) + && (!mParent.sendRequest(opCode, mOutBuffer, mReplyHeader, mPrivateInput))) { return false; } diff --git a/obex/javax/obex/ClientSession.java b/obex/javax/obex/ClientSession.java index 0935383..597c17c 100644 --- a/obex/javax/obex/ClientSession.java +++ b/obex/javax/obex/ClientSession.java @@ -32,7 +32,6 @@ package javax.obex; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -62,11 +61,23 @@ public final class ClientSession extends ObexSession { private final OutputStream mOutput; + private ObexByteBuffer mOutBuffer; + + private ObexByteBuffer mData; + + private ObexByteBuffer mBodyBuffer; + + private ObexByteBuffer mHeaderBuffer; + public ClientSession(final ObexTransport trans) throws IOException { mInput = trans.openInputStream(); mOutput = trans.openOutputStream(); mOpen = true; mRequestActive = false; + mOutBuffer = new ObexByteBuffer(32); + mData = new ObexByteBuffer(32); + mBodyBuffer = new ObexByteBuffer(32); + mHeaderBuffer = new ObexByteBuffer(32); } public HeaderSet connect(final HeaderSet header) throws IOException { @@ -97,19 +108,20 @@ public final class ClientSession extends ObexSession { * Byte 5&6: Max OBEX Packet Length (Defined in MAX_PACKET_SIZE) * Byte 7 to n: headers */ - byte[] requestPacket = new byte[totalLength]; + ObexByteBuffer requestPacket = new ObexByteBuffer(totalLength); + // We just need to start at byte 3 since the sendRequest() method will // handle the length and 0x80. - requestPacket[0] = (byte)0x10; - requestPacket[1] = (byte)0x00; - requestPacket[2] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT >> 8); - requestPacket[3] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT & 0xFF); + requestPacket.write((byte)0x10); + requestPacket.write((byte)0x00); + requestPacket.write((byte)(ObexHelper.MAX_PACKET_SIZE_INT >> 8)); + requestPacket.write((byte)(ObexHelper.MAX_PACKET_SIZE_INT & 0xFF)); if (head != null) { - System.arraycopy(head, 0, requestPacket, 4, head.length); + requestPacket.write(head); } // check with local max packet size - if ((requestPacket.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) { + if ((totalLength + 3) > ObexHelper.MAX_PACKET_SIZE_INT) { throw new IOException("Packet size exceeds max packet size"); } @@ -214,8 +226,14 @@ public final class ClientSession extends ObexSession { } } + ObexByteBuffer headBuffer = null; + if (head != null) { + headBuffer = new ObexByteBuffer(head.length); + headBuffer.write(head); + } + HeaderSet returnHeaderSet = new HeaderSet(); - sendRequest(ObexHelper.OBEX_OPCODE_DISCONNECT, head, returnHeaderSet, null); + sendRequest(ObexHelper.OBEX_OPCODE_DISCONNECT, headBuffer, returnHeaderSet, null); /* * An OBEX DISCONNECT reply from the server: @@ -340,11 +358,11 @@ public final class ClientSession extends ObexSession { * Byte 5: constants * Byte 6 & up: headers */ - byte[] packet = new byte[totalLength]; - packet[0] = (byte)flags; - packet[1] = (byte)0x00; + ObexByteBuffer packet = new ObexByteBuffer(totalLength); + packet.write((byte)flags); + packet.write((byte)0x00); if (headset != null) { - System.arraycopy(head, 0, packet, 2, head.length); + packet.write(head); } HeaderSet returnHeaderSet = new HeaderSet(); @@ -405,31 +423,30 @@ public final class ClientSession extends ObexSession { * <code>false</code> if an authentication response failed to pass * @throws IOException if an IO error occurs */ - public boolean sendRequest(int opCode, byte[] head, HeaderSet header, + public boolean sendRequest(int opCode, ObexByteBuffer head, HeaderSet header, PrivateInputStream privateInput) throws IOException { //check header length with local max size if (head != null) { - if ((head.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) { + if ((head.getLength() + 3) > ObexHelper.MAX_PACKET_SIZE_INT) { throw new IOException("header too large "); } } - int bytesReceived; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - out.write((byte)opCode); + mOutBuffer.reset(); + mOutBuffer.write((byte)opCode); // Determine if there are any headers to send if (head == null) { - out.write(0x00); - out.write(0x03); + mOutBuffer.write((byte)0x00); + mOutBuffer.write((byte)0x03); } else { - out.write((byte)((head.length + 3) >> 8)); - out.write((byte)(head.length + 3)); - out.write(head); + mOutBuffer.write((byte)((head.getLength() + 3) >> 8)); + mOutBuffer.write((byte)(head.getLength() + 3)); + mOutBuffer.write(head, 0); } // Write the request to the output stream and flush the stream - mOutput.write(out.toByteArray()); + mOutBuffer.peek(mOutput, mOutBuffer.getLength()); mOutput.flush(); header.responseCode = mInput.read(); @@ -440,7 +457,7 @@ public final class ClientSession extends ObexSession { throw new IOException("Packet received exceeds packet size limit"); } if (length > ObexHelper.BASE_PACKET_LENGTH) { - byte[] data = null; + mData.reset(); if (opCode == ObexHelper.OBEX_OPCODE_CONNECT) { @SuppressWarnings("unused") int version = mInput.read(); @@ -454,31 +471,21 @@ public final class ClientSession extends ObexSession { } if (length > 7) { - data = new byte[length - 7]; - - bytesReceived = mInput.read(data); - while (bytesReceived != (length - 7)) { - bytesReceived += mInput.read(data, bytesReceived, data.length - - bytesReceived); - } + mData.write(mInput, length - 7); } else { return true; } } else { - data = new byte[length - 3]; - bytesReceived = mInput.read(data); - - while (bytesReceived != (length - 3)) { - bytesReceived += mInput.read(data, bytesReceived, data.length - bytesReceived); - } + mData.write(mInput, length - 3); if (opCode == ObexHelper.OBEX_OPCODE_ABORT) { return true; } } - byte[] body = ObexHelper.updateHeaderSet(header, data); - if ((privateInput != null) && (body != null)) { - privateInput.writeBytes(body, 1); + ObexHelper.updateHeaderSet(header, mData, mBodyBuffer, mHeaderBuffer); + + if ((privateInput != null) && (mBodyBuffer.getLength() > 0)) { + privateInput.writeBytes(mBodyBuffer, 1); } if (header.mConnectionID != null) { @@ -497,17 +504,23 @@ public final class ClientSession extends ObexSession { && (header.mAuthChall != null)) { if (handleAuthChall(header)) { - out.write((byte)HeaderSet.AUTH_RESPONSE); - out.write((byte)((header.mAuthResp.length + 3) >> 8)); - out.write((byte)(header.mAuthResp.length + 3)); - out.write(header.mAuthResp); + + // This can't be a member variable and mOutBuffer can't be used here + // since this is a recursive call. + // That's OK since authentication should not happen very often. + ObexByteBuffer sendHeadersBuffer = new ObexByteBuffer( + (mOutBuffer.getLength() - 3) + header.mAuthResp.length + 3); + sendHeadersBuffer.write(mOutBuffer, 3); + + sendHeadersBuffer.write((byte)HeaderSet.AUTH_RESPONSE); + sendHeadersBuffer.write((byte)((header.mAuthResp.length + 3) >> 8)); + sendHeadersBuffer.write((byte)(header.mAuthResp.length + 3)); + sendHeadersBuffer.write(header.mAuthResp); + header.mAuthChall = null; header.mAuthResp = null; - byte[] sendHeaders = new byte[out.size() - 3]; - System.arraycopy(out.toByteArray(), 3, sendHeaders, 0, sendHeaders.length); - - return sendRequest(opCode, sendHeaders, header, privateInput); + return sendRequest(opCode, sendHeadersBuffer, header, privateInput); } } } diff --git a/obex/javax/obex/HeaderSet.java b/obex/javax/obex/HeaderSet.java index b89b707..8cf15f5 100644 --- a/obex/javax/obex/HeaderSet.java +++ b/obex/javax/obex/HeaderSet.java @@ -126,7 +126,7 @@ public final class HeaderSet { /** * Represents the OBEX End of BODY header. * <P> - * The value of <code>BODY</code> is 0x49 (73). + * The value of <code>END_OF_BODY</code> is 0x49 (73). */ public static final int END_OF_BODY = 0x49; diff --git a/obex/javax/obex/ObexByteBuffer.java b/obex/javax/obex/ObexByteBuffer.java new file mode 100644 index 0000000..18dafaf --- /dev/null +++ b/obex/javax/obex/ObexByteBuffer.java @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed 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 javax.obex; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +class ObexByteBuffer { + private static final int REALLOC_EXTRA_SPACE = 24; + + private byte[] mBuffer; + + private int mIndex; + + private int mLength; + + public ObexByteBuffer(int initialSize) { + mBuffer = new byte[initialSize]; + mIndex = 0; + mLength = 0; + } + + /** + * Mark bytes at beginning or valid data as invalid. + * @param numBytes Number of bytes to consume. + */ + private void consume(int numBytes) { + mLength -= numBytes; + if (mLength > 0) { + mIndex += numBytes; + } else { + mIndex = 0; + } + } + + /** + * Make room in for new data (if needed). + * @param numBytes Number of bytes to make room for. + */ + private void acquire(int numBytes) { + int remainingSpace = mBuffer.length - (mIndex + mLength); + + // Do we need to grow or shuffle? + if (remainingSpace < numBytes) { + int availableSpace = mBuffer.length - mLength; + if (availableSpace < numBytes) { + // Need to grow. Add some extra space to avoid small growth. + byte[] newbuf = new byte[mLength + numBytes + REALLOC_EXTRA_SPACE]; + System.arraycopy(mBuffer, mIndex, newbuf, 0, mLength); + mBuffer = newbuf; + } else { + // Need to shuffle + System.arraycopy(mBuffer, mIndex, mBuffer, 0, mLength); + } + mIndex = 0; + } + } + + /** + * Get the internal byte array. Use with care. + * @return the internal byte array + */ + public byte[] getBytes() { + return mBuffer; + } + + /** + * Get number of written but not consumed bytes. + * @return number of bytes + */ + public int getLength() { + return mLength; + } + + /** + * Discard all unconsumed bytes. + */ + public void reset() { + mIndex = 0; + mLength = 0; + } + + /** + * Read and consume one byte. + * @return Next unconsumed byte. + */ + public byte read() { + if (mLength == 0) { + throw new ArrayIndexOutOfBoundsException(); + } + mLength--; + return mBuffer[mIndex++]; + } + + /** + * Read and consume bytes, and write them into a byte array. + * Will read (dest.length - destOffset) bytes. + * @param dest Array to copy data into. + * @param destOffset Where to start writing in dest. + * @return number of read bytes. + */ + public int read(byte[] dest, int destOffset) { + return read(dest, destOffset, mLength); + } + + /** + * Read and consume bytes, and write them into a byte array. + * Will read (length - destOffset) bytes. + * @param dest Array to copy data into. + * @param destOffset Where to start writing in dest. + * @param length Number of bytes to read. + * @return number of read bytes. + */ + public int read(byte[] dest, int destOffset, int length) { + peek(0, dest, destOffset, length); + consume(length); + return length; + } + + /** + * Read and consume bytes, and write them into another ObexByteBuffer. + * @param dest ObexByteBuffer to copy data into. + * @param length Number of bytes to read. + * @return number of read bytes. + */ + public int read(ObexByteBuffer dest, int length) { + if (length > mLength) { + throw new ArrayIndexOutOfBoundsException(); + } + dest.write(mBuffer, mIndex, length); + consume(length); + + return length; + } + + /** + * Read and consume all unconsumed bytes, and write them into an OutputStream. + * @param dest OutputStream to copy data into. + * @return number of read bytes. + */ + public int read(OutputStream stream) throws IOException { + return read(stream, mLength); + } + + /** + * Read and consume bytes, and write them into an OutputStream. + * @param dest OutputStream to copy data into. + * @param length Number of bytes to read. + * @return number of read bytes. + */ + public int read(OutputStream destStream, int length) throws IOException { + peek(destStream, length); + consume(length); + return length; + } + + /** + * Read (but don't consume) one byte. + * @param offset Offset into unconsumed bytes. + * @return Requested unconsumed byte. + */ + public byte peek(int offset) { + if (offset > mLength) { + throw new ArrayIndexOutOfBoundsException(); + } + return mBuffer[mIndex + offset]; + } + + /** + * Read (but don't consume) bytes and write them into a byte array. + * Will read dest.length bytes. + * @param offset Offset into unconsumed bytes. + * @param dest Array to copy data into. + */ + public void peek(int offset, byte[] dest) { + peek(offset, dest, 0, dest.length); + } + + /** + * Read (but don't consume) bytes and write them into a byte array. + * Will read (length - destOffset) bytes. + * @param offset Offset into unconsumed bytes. + * @param dest Array to copy data into. + * @param destOffset Where to start writing in dest. + * @param length Number of bytes to read. + */ + public void peek(int offset, byte[] dest, int destOffset, int length) { + if (offset > mLength || (offset + length) > mLength) { + throw new ArrayIndexOutOfBoundsException(); + } + System.arraycopy(mBuffer, mIndex + offset, dest, destOffset, length); + } + + /** + * Read (but don't consume) bytes, and write them into an OutputStream. + * @param dest OutputStream to copy data into. + * @param length Number of bytes to read. + */ + public void peek(OutputStream stream, int length) throws IOException { + if (length > mLength) { + throw new ArrayIndexOutOfBoundsException(); + } + stream.write(mBuffer, mIndex, length); + } + + /** + * Write a new byte. + * @param src Byte to write. + */ + public void write(byte src) { + acquire(1); + mBuffer[mIndex + mLength] = src; + mLength++; + } + + /** + * Read bytes from a byte array and add to unconsumed bytes. + * Will read/write src.length bytes. + * @param src Array to read from. + */ + public void write(byte[] src) { + write(src, 0, src.length); + } + + /** + * Read bytes from a byte array and add to unconsumed bytes. + * Will read/write (src.length - srcOffset) bytes. + * @param src Array to read from. + * @param srcOffset Offset into source array. + */ + public void write(byte[] src, int srcOffset) { + write(src, srcOffset, src.length - srcOffset); + } + + /** + * Read bytes from a byte array and add to unconsumed bytes. + * Will read/write (srcLength - srcOffset) bytes. + * @param src Array to read from. + * @param srcOffset Offset into source array. + * @param srcLength Number of bytes to read/write. + */ + public void write(byte[] src, int srcOffset, int srcLength) { + // Make sure we have space. + acquire(srcLength); + + // Add the new data at the end + System.arraycopy(src, srcOffset, mBuffer, mIndex + mLength, srcLength); + mLength += srcLength; + } + + /** + * Read bytes from another ObexByteBuffer and add to unconsumed bytes. + * Will read/write src.getLength() bytes. The bytes in src will not be consumed. + * @param src ObexByteBuffer to read from. + * @param srcOffset Offset into source array. + */ + public void write(ObexByteBuffer src) { + write(src.mBuffer, 0, src.getLength()); + } + + /** + * Read bytes from another ObexByteBuffer and add to unconsumed bytes. + * Will read/write (src.getLength() - srcOffset) bytes. The bytes in src will not + * be consumed. + * @param src ObexByteBuffer to read from. + * @param srcOffset Offset into source array. + */ + public void write(ObexByteBuffer src, int srcOffset) { + if (srcOffset > src.mLength) { + throw new ArrayIndexOutOfBoundsException(); + } + write(src.mBuffer, src.mIndex + srcOffset, src.mLength - src.mIndex - srcOffset); + } + + /** + * Read bytes from another ObexByteBuffer and add to unconsumed bytes. + * Will read/write (srcLength - srcOffset) bytes. The bytes in src will not be + * consumed. + * @param src ObexByteBuffer to read from. + * @param srcOffset Offset into source array. + * @param srcLength Number of bytes to read/write. + */ + public void write(ObexByteBuffer src, int srcOffset, int srcLength) { + if (srcOffset > src.mLength || (srcOffset + srcLength) > src.mLength) { + throw new ArrayIndexOutOfBoundsException(); + } + write(src.mBuffer, src.mIndex + srcOffset, srcLength); + } + + /** + * Read bytes from an InputStream and add to unconsumed bytes. + * @param src InputStream to read from + * @param srcLength Number of bytes to read + * @throws IOException + */ + public void write(InputStream src, int srcLength) throws IOException { + // First make sure we have space. + acquire(srcLength); + + // Read data until the requested number of bytes have been read. + int numBytes = 0; + do { + int readBytes = src.read(mBuffer, mIndex + mLength + numBytes, srcLength - numBytes); + if (readBytes == -1) { + throw new IOException(); + } + numBytes += readBytes; + } while (numBytes != srcLength); + mLength += numBytes; + } +} diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java index 1b66662..7e02fd5 100644 --- a/obex/javax/obex/ObexHelper.java +++ b/obex/javax/obex/ObexHelper.java @@ -151,21 +151,22 @@ public final class ObexHelper { * exception to be thrown. When it is thrown, it is ignored. * @param header the HeaderSet to update * @param headerArray the byte array containing headers - * @return the result of the last start body or end body header provided; - * the first byte in the result will specify if a body or end of - * body is received + * @param bodyBuffer Buffer to return result in. Will be reset(). Can be null. + * Will contain the result of the last start body or end body header provided; + * the first byte in the result will specify if a body or end of body is received + * @param headerBuffer Buffer to use to avoid allocations. Will be reset(). * @throws IOException if an invalid header was found */ - public static byte[] updateHeaderSet(HeaderSet header, byte[] headerArray) throws IOException { + static void updateHeaderSet(HeaderSet header, ObexByteBuffer headerArray, + ObexByteBuffer bodyBuffer, ObexByteBuffer headerBuffer) throws IOException { int index = 0; int length = 0; int headerID; byte[] value = null; - byte[] body = null; HeaderSet headerImpl = header; try { - while (index < headerArray.length) { - headerID = 0xFF & headerArray[index]; + while (index < headerArray.getLength()) { + headerID = 0xFF & headerArray.peek(index); switch (headerID & (0xC0)) { /* @@ -181,14 +182,15 @@ public final class ObexHelper { case 0x40: boolean trimTail = true; index++; - length = 0xFF & headerArray[index]; + length = 0xFF & headerArray.peek(index); length = length << 8; index++; - length += 0xFF & headerArray[index]; + length += 0xFF & headerArray.peek(index); length -= 3; index++; - value = new byte[length]; - System.arraycopy(headerArray, index, value, 0, length); + headerBuffer.reset(); + headerBuffer.write(headerArray, index, length); + value = headerBuffer.getBytes(); if (length == 0 || (length > 0 && (value[length - 1] != 0))) { trimTail = false; } @@ -198,10 +200,10 @@ public final class ObexHelper { // Remove trailing null if (trimTail == false) { headerImpl.setHeader(headerID, new String(value, 0, - value.length, "ISO8859_1")); + length, "ISO8859_1")); } else { headerImpl.setHeader(headerID, new String(value, 0, - value.length - 1, "ISO8859_1")); + length - 1, "ISO8859_1")); } } catch (UnsupportedEncodingException e) { throw e; @@ -210,27 +212,27 @@ public final class ObexHelper { case HeaderSet.AUTH_CHALLENGE: headerImpl.mAuthChall = new byte[length]; - System.arraycopy(headerArray, index, headerImpl.mAuthChall, 0, - length); + headerArray.peek(index, headerImpl.mAuthChall); break; case HeaderSet.AUTH_RESPONSE: headerImpl.mAuthResp = new byte[length]; - System.arraycopy(headerArray, index, headerImpl.mAuthResp, 0, - length); + headerArray.peek(index, headerImpl.mAuthResp); break; case HeaderSet.BODY: /* Fall Through */ case HeaderSet.END_OF_BODY: - body = new byte[length + 1]; - body[0] = (byte)headerID; - System.arraycopy(headerArray, index, body, 1, length); + if (bodyBuffer != null) { + bodyBuffer.reset(); + bodyBuffer.write((byte)headerID); + bodyBuffer.write(headerArray, index, length); + } break; case HeaderSet.TIME_ISO_8601: try { - String dateString = new String(value, "ISO8859_1"); + String dateString = new String(value, 0, length, "ISO8859_1"); Calendar temp = Calendar.getInstance(); if ((dateString.length() == 16) && (dateString.charAt(15) == 'Z')) { @@ -257,9 +259,13 @@ public final class ObexHelper { default: if ((headerID & 0xC0) == 0x00) { headerImpl.setHeader(headerID, ObexHelper.convertToUnicode( - value, true)); + value, length, true)); } else { - headerImpl.setHeader(headerID, value); + // <value> is reused and can contain garbage after [length-1]. + // Copy wanted data to temporary array. + byte[] otherValue = new byte[length]; + System.arraycopy(value, 0, otherValue, 0, length); + headerImpl.setHeader(headerID, otherValue); } } @@ -273,7 +279,7 @@ public final class ObexHelper { case 0x80: index++; try { - headerImpl.setHeader(headerID, Byte.valueOf(headerArray[index])); + headerImpl.setHeader(headerID, Byte.valueOf(headerArray.peek(index))); } catch (Exception e) { // Not a valid header so ignore } @@ -288,7 +294,7 @@ public final class ObexHelper { case 0xC0: index++; value = new byte[4]; - System.arraycopy(headerArray, index, value, 0, 4); + headerArray.peek(index, value); try { if (headerID != HeaderSet.TIME_4_BYTE) { // Determine if it is a connection ID. These @@ -317,8 +323,6 @@ public final class ObexHelper { } catch (IOException e) { throw new IOException("Header was not formatted properly"); } - - return body; } /** @@ -874,11 +878,11 @@ public final class ObexHelper { * @return a Unicode string * @throws IllegalArgumentException if the byte array has an odd length */ - public static String convertToUnicode(byte[] b, boolean includesNull) { - if (b == null || b.length == 0) { + public static String convertToUnicode(byte[] b, int len, boolean includesNull) { + if (b == null || len == 0) { return null; } - int arrayLength = b.length; + int arrayLength = len; if (!((arrayLength % 2) == 0)) { throw new IllegalArgumentException("Byte array not of a valid form"); } diff --git a/obex/javax/obex/ObexSession.java b/obex/javax/obex/ObexSession.java index a7daeb5..6c4ba43 100644 --- a/obex/javax/obex/ObexSession.java +++ b/obex/javax/obex/ObexSession.java @@ -98,7 +98,7 @@ public class ObexSession { case ObexHelper.OBEX_AUTH_REALM_CHARSET_UNICODE: // UNICODE Encoding - realm = ObexHelper.convertToUnicode(realmString, false); + realm = ObexHelper.convertToUnicode(realmString, realmString.length, false); break; default: diff --git a/obex/javax/obex/PrivateInputStream.java b/obex/javax/obex/PrivateInputStream.java index 5daee72..2bcf541 100644 --- a/obex/javax/obex/PrivateInputStream.java +++ b/obex/javax/obex/PrivateInputStream.java @@ -44,9 +44,7 @@ public final class PrivateInputStream extends InputStream { private BaseStream mParent; - private byte[] mData; - - private int mIndex; + private ObexByteBuffer mBuffer; private boolean mOpen; @@ -56,8 +54,7 @@ public final class PrivateInputStream extends InputStream { */ public PrivateInputStream(BaseStream p) { mParent = p; - mData = new byte[0]; - mIndex = 0; + mBuffer = new ObexByteBuffer(0); mOpen = true; } @@ -73,7 +70,7 @@ public final class PrivateInputStream extends InputStream { @Override public synchronized int available() throws IOException { ensureOpen(); - return mData.length - mIndex; + return mBuffer.getLength(); } /** @@ -89,12 +86,12 @@ public final class PrivateInputStream extends InputStream { @Override public synchronized int read() throws IOException { ensureOpen(); - while (mData.length == mIndex) { + while (mBuffer.getLength() == 0) { if (!mParent.continueOperation(true, true)) { return -1; } } - return (mData[mIndex++] & 0xFF); + return (mBuffer.read() & 0xFF); } @Override @@ -113,26 +110,23 @@ public final class PrivateInputStream extends InputStream { } ensureOpen(); - int currentDataLength = mData.length - mIndex; int remainReadLength = length; int offset1 = offset; int result = 0; - while (currentDataLength <= remainReadLength) { - System.arraycopy(mData, mIndex, b, offset1, currentDataLength); - mIndex += currentDataLength; - offset1 += currentDataLength; - result += currentDataLength; - remainReadLength -= currentDataLength; + while (mBuffer.getLength() <= remainReadLength) { + int readBytes = mBuffer.read(b, offset1); + + offset1 += readBytes; + result += readBytes; + remainReadLength -= readBytes; if (!mParent.continueOperation(true, true)) { return result == 0 ? -1 : result; } - currentDataLength = mData.length - mIndex; } if (remainReadLength > 0) { - System.arraycopy(mData, mIndex, b, offset1, remainReadLength); - mIndex += remainReadLength; + mBuffer.read(b, offset1, remainReadLength); result += remainReadLength; } return result; @@ -144,16 +138,8 @@ public final class PrivateInputStream extends InputStream { * @param body the data to add to the stream * @param start the start of the body to array to copy */ - public synchronized void writeBytes(byte[] body, int start) { - - int length = (body.length - start) + (mData.length - mIndex); - byte[] temp = new byte[length]; - - System.arraycopy(mData, mIndex, temp, 0, mData.length - mIndex); - System.arraycopy(body, start, temp, mData.length - mIndex, body.length - start); - - mData = temp; - mIndex = 0; + public synchronized void writeBytes(ObexByteBuffer body, int start) { + mBuffer.write(body, start); notifyAll(); } diff --git a/obex/javax/obex/PrivateOutputStream.java b/obex/javax/obex/PrivateOutputStream.java index ca420af..06dd55e 100644 --- a/obex/javax/obex/PrivateOutputStream.java +++ b/obex/javax/obex/PrivateOutputStream.java @@ -34,7 +34,6 @@ package javax.obex; import java.io.IOException; import java.io.OutputStream; -import java.io.ByteArrayOutputStream; /** * This object provides an output stream to the Operation objects used in this @@ -45,7 +44,7 @@ public final class PrivateOutputStream extends OutputStream { private BaseStream mParent; - private ByteArrayOutputStream mArray; + private ObexByteBuffer mBuffer; private boolean mOpen; @@ -57,7 +56,7 @@ public final class PrivateOutputStream extends OutputStream { */ public PrivateOutputStream(BaseStream p, int maxSize) { mParent = p; - mArray = new ByteArrayOutputStream(); + mBuffer = new ObexByteBuffer(32); mMaxPacketSize = maxSize; mOpen = true; } @@ -67,7 +66,7 @@ public final class PrivateOutputStream extends OutputStream { * @return the number of bytes written to the output stream */ public int size() { - return mArray.size(); + return mBuffer.getLength(); } /** @@ -82,8 +81,8 @@ public final class PrivateOutputStream extends OutputStream { public synchronized void write(int b) throws IOException { ensureOpen(); mParent.ensureNotDone(); - mArray.write(b); - if (mArray.size() == mMaxPacketSize) { + mBuffer.write((byte)b); + if (mBuffer.getLength() == mMaxPacketSize) { mParent.continueOperation(true, false); } } @@ -108,38 +107,30 @@ public final class PrivateOutputStream extends OutputStream { ensureOpen(); mParent.ensureNotDone(); if (count < mMaxPacketSize) { - mArray.write(buffer, offset, count); + mBuffer.write(buffer, offset, count); } else { while (remainLength >= mMaxPacketSize) { - mArray.write(buffer, offset1, mMaxPacketSize); + mBuffer.write(buffer, offset1, mMaxPacketSize); offset1 += mMaxPacketSize; remainLength = count - offset1; mParent.continueOperation(true, false); } if (remainLength > 0) { - mArray.write(buffer, offset1, remainLength); + mBuffer.write(buffer, offset1, remainLength); } } } /** - * Reads the bytes that have been written to this stream. - * @param size the size of the array to return - * @return the byte array that is written + * Write some of the bytes that have been written to this stream to + * an ObexByteBuffer. + * + * @param dest the stream to write to + * @param start where to write in the byte array + * @param size the number of bytes to write to the byte array */ - public synchronized byte[] readBytes(int size) { - if (mArray.size() > 0) { - byte[] temp = mArray.toByteArray(); - mArray.reset(); - byte[] result = new byte[size]; - System.arraycopy(temp, 0, result, 0, size); - if (temp.length != size) { - mArray.write(temp, size, temp.length - size); - } - return result; - } else { - return null; - } + public synchronized void writeTo(ObexByteBuffer dest, int size) throws IOException { + mBuffer.read(dest, size); } /** diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java index d1476d2..89c0779 100644 --- a/obex/javax/obex/ServerOperation.java +++ b/obex/javax/obex/ServerOperation.java @@ -37,7 +37,6 @@ import java.io.InputStream; import java.io.DataInputStream; import java.io.OutputStream; import java.io.DataOutputStream; -import java.io.ByteArrayOutputStream; /** * This class implements the Operation interface for server side connections. @@ -88,6 +87,12 @@ public final class ServerOperation implements Operation, BaseStream { private boolean mHasBody; + private ObexByteBuffer mData; + + private ObexByteBuffer mBodyBuffer; + + private ObexByteBuffer mHeaderBuffer; + /** * Creates new ServerOperation * @param p the parent that created this object @@ -114,7 +119,10 @@ public final class ServerOperation implements Operation, BaseStream { mRequestFinished = false; mPrivateOutputOpen = false; mHasBody = false; - int bytesReceived; + + mData = new ObexByteBuffer(32); + mBodyBuffer = new ObexByteBuffer(32); + mHeaderBuffer = new ObexByteBuffer(32); /* * Determine if this is a PUT request @@ -165,16 +173,12 @@ public final class ServerOperation implements Operation, BaseStream { * Determine if any headers were sent in the initial request */ if (length > 3) { - byte[] data = new byte[length - 3]; - bytesReceived = in.read(data); - - while (bytesReceived != data.length) { - bytesReceived += in.read(data, bytesReceived, data.length - bytesReceived); - } + mData.reset(); + mData.write(in, length - 3); - byte[] body = ObexHelper.updateHeaderSet(requestHeader, data); + ObexHelper.updateHeaderSet(requestHeader, mData, mBodyBuffer, mHeaderBuffer); - if (body != null) { + if (mBodyBuffer.getLength() > 0) { mHasBody = true; } @@ -205,8 +209,8 @@ public final class ServerOperation implements Operation, BaseStream { } - if (body != null) { - mPrivateInput.writeBytes(body, 1); + if (mBodyBuffer.getLength() > 0) { + mPrivateInput.writeBytes(mBodyBuffer, 1); } else { while ((!mGetOperation) && (!finalBitSet)) { sendReply(ResponseCodes.OBEX_HTTP_CONTINUE); @@ -281,8 +285,7 @@ public final class ServerOperation implements Operation, BaseStream { * @throws IOException if an IO error occurs */ public synchronized boolean sendReply(int type) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - int bytesReceived; + mBodyBuffer.reset(); long id = mListener.getConnectionId(); if (id == -1) { @@ -322,10 +325,10 @@ public final class ServerOperation implements Operation, BaseStream { mParent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null); throw new IOException("OBEX Packet exceeds max packet size"); } - byte[] sendHeader = new byte[end - start]; - System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length); + mHeaderBuffer.reset(); + mHeaderBuffer.write(headerArray, start, end - start); - mParent.sendResponse(type, sendHeader); + mParent.sendResponse(type, mHeaderBuffer); start = end; } @@ -336,7 +339,7 @@ public final class ServerOperation implements Operation, BaseStream { } } else { - out.write(headerArray); + mBodyBuffer.write(headerArray); } // For Get operation: if response code is OBEX_HTTP_OK, then this is the @@ -356,36 +359,34 @@ public final class ServerOperation implements Operation, BaseStream { bodyLength = mMaxPacketLength - headerArray.length - 6; } - byte[] body = mPrivateOutput.readBytes(bodyLength); - /* * Since this is a put request if the final bit is set or * the output stream is closed we need to send the 0x49 * (End of Body) otherwise, we need to send 0x48 (Body) */ if ((finalBitSet) || (mPrivateOutput.isClosed())) { - out.write(0x49); + mBodyBuffer.write((byte)0x49); } else { - out.write(0x48); + mBodyBuffer.write((byte)0x48); } bodyLength += 3; - out.write((byte)(bodyLength >> 8)); - out.write((byte)bodyLength); - out.write(body); + mBodyBuffer.write((byte)(bodyLength >> 8)); + mBodyBuffer.write((byte)bodyLength); + mPrivateOutput.writeTo(mBodyBuffer, bodyLength - 3); } } if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (orginalBodyLength <= 0)) { - out.write(0x49); + mBodyBuffer.write((byte)0x49); orginalBodyLength = 3; - out.write((byte)(orginalBodyLength >> 8)); - out.write((byte)orginalBodyLength); + mBodyBuffer.write((byte)(orginalBodyLength >> 8)); + mBodyBuffer.write((byte)orginalBodyLength); } mResponseSize = 3; - mParent.sendResponse(type, out.toByteArray()); + mParent.sendResponse(type, mBodyBuffer); if (type == ResponseCodes.OBEX_HTTP_CONTINUE) { int headerID = mInput.read(); @@ -397,14 +398,9 @@ public final class ServerOperation implements Operation, BaseStream { && (headerID != ObexHelper.OBEX_OPCODE_GET_FINAL)) { if (length > 3) { - byte[] temp = new byte[length - 3]; + mData.reset(); // First three bytes already read, compensating for this - bytesReceived = mInput.read(temp); - - while (bytesReceived != temp.length) { - bytesReceived += mInput.read(temp, bytesReceived, - temp.length - bytesReceived); - } + mData.write(mInput, length - 3); } /* @@ -442,17 +438,14 @@ public final class ServerOperation implements Operation, BaseStream { * Determine if any headers were sent in the initial request */ if (length > 3) { - byte[] data = new byte[length - 3]; - bytesReceived = mInput.read(data); + mData.reset(); + mData.write(mInput, length - 3); - while (bytesReceived != data.length) { - bytesReceived += mInput.read(data, bytesReceived, data.length - - bytesReceived); - } - byte[] body = ObexHelper.updateHeaderSet(requestHeader, data); - if (body != null) { + ObexHelper.updateHeaderSet(requestHeader, mData, mBodyBuffer, mHeaderBuffer); + if (mBodyBuffer.getLength() > 0) { mHasBody = true; } + if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) { mListener.setConnectionId(ObexHelper .convertToLong(requestHeader.mConnectionID)); @@ -481,8 +474,8 @@ public final class ServerOperation implements Operation, BaseStream { requestHeader.mAuthChall = null; } - if (body != null) { - mPrivateInput.writeBytes(body, 1); + if (mBodyBuffer.getLength() > 0) { + mPrivateInput.writeBytes(mBodyBuffer, 1); } } } diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java index a4b9759..694c8a9 100644 --- a/obex/javax/obex/ServerSession.java +++ b/obex/javax/obex/ServerSession.java @@ -60,6 +60,10 @@ public final class ServerSession extends ObexSession implements Runnable { private boolean mClosed; + private ObexByteBuffer mData; + + private ObexByteBuffer mHeaderBuffer; + /** * Creates new ServerSession. * @param trans the connection to the client @@ -80,6 +84,9 @@ public final class ServerSession extends ObexSession implements Runnable { mClosed = false; mProcessThread = new Thread(this); mProcessThread.start(); + + mData = new ObexByteBuffer(32); + mHeaderBuffer = new ObexByteBuffer(32); } /** @@ -253,24 +260,22 @@ public final class ServerSession extends ObexSession implements Runnable { * @param header the headers to include in the response * @throws IOException if an IO error occurs */ - public void sendResponse(int code, byte[] header) throws IOException { + public void sendResponse(int code, ObexByteBuffer header) throws IOException { int totalLength = 3; - byte[] data = null; + mData.reset(); if (header != null) { - totalLength += header.length; - data = new byte[totalLength]; - data[0] = (byte)code; - data[1] = (byte)(totalLength >> 8); - data[2] = (byte)totalLength; - System.arraycopy(header, 0, data, 3, header.length); + totalLength += header.getLength(); + mData.write((byte)code); + mData.write((byte)(totalLength >> 8)); + mData.write((byte)totalLength); + mData.write(header); } else { - data = new byte[totalLength]; - data[0] = (byte)code; - data[1] = (byte)0x00; - data[2] = (byte)totalLength; + mData.write((byte)code); + mData.write((byte)0x00); + mData.write((byte)totalLength); } - mOutput.write(data); + mData.read(mOutput); mOutput.flush(); } @@ -291,7 +296,6 @@ public final class ServerSession extends ObexSession implements Runnable { int totalLength = 3; byte[] head = null; int code = -1; - int bytesReceived; HeaderSet request = new HeaderSet(); HeaderSet reply = new HeaderSet(); @@ -305,15 +309,10 @@ public final class ServerSession extends ObexSession implements Runnable { totalLength = 3; } else { if (length > 5) { - byte[] headers = new byte[length - 5]; - bytesReceived = mInput.read(headers); + mData.reset(); + mData.write(mInput, length - 5); - while (bytesReceived != headers.length) { - bytesReceived += mInput.read(headers, bytesReceived, headers.length - - bytesReceived); - } - - ObexHelper.updateHeaderSet(request, headers); + ObexHelper.updateHeaderSet(request, mData, null, mHeaderBuffer); if (mListener.getConnectionId() != -1 && request.mConnectionID != null) { mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID)); @@ -386,18 +385,18 @@ public final class ServerSession extends ObexSession implements Runnable { } // Compute Length of OBEX SETPATH packet - byte[] replyData = new byte[totalLength]; - replyData[0] = (byte)code; - replyData[1] = (byte)(totalLength >> 8); - replyData[2] = (byte)totalLength; + mData.reset(); + mData.write((byte)code); + mData.write((byte)(totalLength >> 8)); + mData.write((byte)totalLength); if (head != null) { - System.arraycopy(head, 0, replyData, 3, head.length); + mData.write(head); } /* * Write the OBEX SETPATH packet to the server. Byte 0: response code * Byte 1&2: Connect Packet Length Byte 3 to n: headers */ - mOutput.write(replyData); + mData.read(mOutput); mOutput.flush(); } @@ -414,7 +413,6 @@ public final class ServerSession extends ObexSession implements Runnable { int code = ResponseCodes.OBEX_HTTP_OK; int totalLength = 3; byte[] head = null; - int bytesReceived; HeaderSet request = new HeaderSet(); HeaderSet reply = new HeaderSet(); @@ -426,15 +424,10 @@ public final class ServerSession extends ObexSession implements Runnable { totalLength = 3; } else { if (length > 3) { - byte[] headers = new byte[length - 3]; - bytesReceived = mInput.read(headers); - - while (bytesReceived != headers.length) { - bytesReceived += mInput.read(headers, bytesReceived, headers.length - - bytesReceived); - } + mData.reset(); + mData.write(mInput, length - 3); - ObexHelper.updateHeaderSet(request, headers); + ObexHelper.updateHeaderSet(request, mData, null, mHeaderBuffer); } if (mListener.getConnectionId() != -1 && request.mConnectionID != null) { @@ -485,23 +478,18 @@ public final class ServerSession extends ObexSession implements Runnable { } // Compute Length of OBEX CONNECT packet - byte[] replyData; - if (head != null) { - replyData = new byte[3 + head.length]; - } else { - replyData = new byte[3]; - } - replyData[0] = (byte)code; - replyData[1] = (byte)(totalLength >> 8); - replyData[2] = (byte)totalLength; + mData.reset(); + mData.write((byte)code); + mData.write((byte)(totalLength >> 8)); + mData.write((byte)totalLength); if (head != null) { - System.arraycopy(head, 0, replyData, 3, head.length); + mData.write(head); } /* * Write the OBEX DISCONNECT packet to the server. Byte 0: response code * Byte 1&2: Connect Packet Length Byte 3 to n: headers */ - mOutput.write(replyData); + mData.read(mOutput); mOutput.flush(); } @@ -525,7 +513,6 @@ public final class ServerSession extends ObexSession implements Runnable { int code = -1; HeaderSet request = new HeaderSet(); HeaderSet reply = new HeaderSet(); - int bytesReceived; /* * Read in the length of the OBEX packet, OBEX version, flags, and max @@ -548,15 +535,10 @@ public final class ServerSession extends ObexSession implements Runnable { totalLength = 7; } else { if (packetLength > 7) { - byte[] headers = new byte[packetLength - 7]; - bytesReceived = mInput.read(headers); - - while (bytesReceived != headers.length) { - bytesReceived += mInput.read(headers, bytesReceived, headers.length - - bytesReceived); - } + mData.reset(); + mData.write(mInput, packetLength - 7); - ObexHelper.updateHeaderSet(request, headers); + ObexHelper.updateHeaderSet(request, mData, null, mHeaderBuffer); } if (mListener.getConnectionId() != -1 && request.mConnectionID != null) { |