class RpcOutputStream extends FileOutputStream {

	public long writeConverted(byte[] sourceBytes) throws IOException, FileDecoderException, FileEncoderException {
		int len = sourceBytes.length;
		int start = 0;
		int bom = 0;

		if (writeUtf8Bom) {
			this.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF}, 0, 3);
			bom = 3;
			writeUtf8Bom = false;
		}

		if (len <= 0) {
			return 0;
		}

		long bytesWritten = len + bom - start;

		switch (this.fileType) {
			case FST_UNICODE:
			case FST_UTF16:
			case FST_XUTF16:
			case FST_XUNICODE:
				// Convert to local charset if set
				if (this.converter != null) {
					if (this.localDigester != null) {
						this.localDigester.update(sourceBytes);
					}
					//Convert line endings before converting to unicode
					if (this.lineEndStream != null) {
						// Use intermediate buffer to hold line ending converted
						// source bytes
						ByteArrayOutputStream out = new ByteArrayOutputStream(
								RpcPropertyDefs.RPC_DEFAULT_FILE_BUF_SIZE);
						this.lineEndStream.write(out, sourceBytes, start, len);

						sourceBytes = out.toByteArray();
						len = sourceBytes.length;
						start = 0;
					}

					ByteBuffer sourceBuffer = ByteBuffer.wrap(sourceBytes);

					ByteBuffer converted = this.converter.convert(sourceBuffer);
					if (converted != null) {
						sourceBytes = converted.array();
						start = converted.position();
						len = converted.limit();
					} else {
						len = 0;
					}

					if (len <= 0) {
						return 0;
					}

					this.write(sourceBytes, start, len);
					bytesWritten = len + bom - start;
					break;
				}

			case FST_TEXT:
			case FST_UTF8:
			case FST_XTEXT:
			case FST_XUTF8:
				if (this.localDigester != null) {
					this.localDigester.update(sourceBytes, start, len);
				}
				if (this.lineEndStream != null) {
					this.lineEndStream.write(sourceBytes, start, len);
				} else {
					this.write(sourceBytes, start, len);
				}
				bytesWritten = len + bom - start;
				break;

			case FST_GUNZIP:
			case FST_XGUNZIP:

				// NOTE: We always copy the last eight bytes of what's passed into
				// us here so we can use it for trailer / footer calculation
				// later, when we don't have direct access to it (we can't retrieve it
				// from the Inflater without a  bit of skulduggery, unfortunately).
				//
				// This is not as straightforward as it looks due to the endless
				// possibilities for cross-packet trailers, etc., so what's here
				// can almost certainly be reworked in a more efficient way
				// in later versions (using FilterOutputStream?) --- HR.

				long bytesWrittenPrior = this.outStream.getBytesWritten();

				if (!headerRead) {
					ByteArrayInputStream byteStream
							= new ByteArrayInputStream(sourceBytes, 0, len);
					readHeader(byteStream, new RpcCRC32Checksum());
					headerRead = true;
					int bytesAvailable = byteStream.available();
					if (bytesAvailable > 0) {
						this.outStream.write(sourceBytes, (len - bytesAvailable),
								bytesAvailable);

						if (bytesAvailable >= TRAILER_SIZE) {
							System.arraycopy(
									sourceBytes,
									(len - TRAILER_SIZE),
									this.footerBytes,
									0,
									TRAILER_SIZE);
						} else {
							// Copy what we can; if it turns out the rest was in the next
							// packet, we'll detect that then...

							System.arraycopy(
									sourceBytes,
									(len - bytesAvailable),
									this.footerBytes,
									0,
									bytesAvailable);
						}
					}
				} else {
					this.outStream.write(sourceBytes, 0, len);

					if (len >= TRAILER_SIZE) {
						System.arraycopy(
								sourceBytes,
								(len - TRAILER_SIZE),
								this.footerBytes,
								0,
								TRAILER_SIZE);
					} else {
						// first move the last (TRAILER_SIZE-len) bytes to the
						// beginning of the footer

						System.arraycopy(
								this.footerBytes,
								len,
								this.footerBytes,
								0,
								TRAILER_SIZE - len);

						// Add to what's (hopefully) there already...

						System.arraycopy(
								sourceBytes,
								0,
								this.footerBytes,
								TRAILER_SIZE - len,
								len);
					}
				}

				bytesWritten = this.outStream.getBytesWritten() - bytesWrittenPrior;
				break;

			default:
				if (this.localDigester != null) {
					this.localDigester.update(sourceBytes, 0, len);
				}
				this.write(sourceBytes, 0, len);
				bytesWritten = len + bom - 0;
		}

		return bytesWritten;
	}

	public  RpcOutputStream(RpcPerforceFile file, RpcConnection rpcConnection, boolean useLocalDigester);
	private  RpcOutputStream(RpcPerforceFile file, P4Charset p4Charset, boolean isUnicodeServer, boolean useLocalDigester, int filesys_utf8bom);

}