blob: 165fd98247ef67d49ef4d964325e83a89cd8a02c [file] [log] [blame]
/*
* Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.corba.se.impl.encoding;
import org.omg.CORBA.BAD_PARAM;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.CompletionStatus;
import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
import com.sun.corba.se.impl.encoding.CodeSetConversion;
import com.sun.corba.se.impl.orbutil.ORBConstants;
public class CDROutputStream_1_2 extends CDROutputStream_1_1
{
// There's a situation with chunking with fragmentation
// in which the alignment for a primitive value is needed
// to fill fragment N, but the primitive won't fit so
// must go into fragment N + 1. The behavior is the same
// as that for specialChunks.
//
// Unfortunately, given the current code, we can't reuse
// specialChunk. If you wrap each of the following
// write calls with handleSpecialChunkBegin/End, you
// will lose your state because the primitive calls will
// change the variables, etc.
//
// All of the CDR code should be rewritten moving chunking
// to a different level, perhaps in the buffer managers.
// We want to move to a compositional model rather than
// using inheritance.
//
// Note that in the grow case, chunks are _NOT_ closed
// at grow points, now.
//
// **** NOTE ****
// Since we will not support valuetypes with GIOP 1.1, that
// also means we do not support chunking there.
//
protected boolean primitiveAcrossFragmentedChunk = false;
// Used in chunking. Here's how this works:
//
// When chunking and writing an array of primitives, a string, or a
// wstring, _AND_ it won't fit in the buffer do the following. (As
// you can see, this is a very "special" chunk.)
//
// 1. Write the length of the chunk including the array length
// 2. Set specialChunk to true
// 3 applies to ALL chunking:
// 3. In grow, if we need to fragment and specialChunk is false
// a) call end_block
// b) fragment
// Now back to the array only case:
// [write the data]
// 4. if specialChunk is true
// a) Close the chunk
// b) Set specialChunk to false
protected boolean specialChunk = false;
// Indicates whether the header should be padded. In GIOP 1.2 and above, the
// body must be aligned on a 8-octet boundary, and so the header needs to be
// padded appropriately. However, if there is no body to a request or reply
// message, there is no need to pad the header, in the unfragmented case.
private boolean headerPadding;
protected void handleSpecialChunkBegin(int requiredSize)
{
// If we're chunking and the item won't fit in the buffer
if (inBlock && requiredSize + bbwi.position() > bbwi.buflen) {
// Duplicating some code from end_block. Compute
// and write the total chunk length.
int oldSize = bbwi.position();
bbwi.position(blockSizeIndex - 4);
//write_long(oldSize - blockSizeIndex);
writeLongWithoutAlign((oldSize - blockSizeIndex) + requiredSize);
bbwi.position(oldSize);
// Set the special flag so we don't end the chunk when
// we fragment
specialChunk = true;
}
}
protected void handleSpecialChunkEnd()
{
// If we're in a chunk and the item spanned fragments
if (inBlock && specialChunk) {
// This is unnecessary, but I just want to show that
// we're done with the current chunk. (the end_block
// call is inappropriate here)
inBlock = false;
blockSizeIndex = -1;
blockSizePosition = -1;
// Start a new chunk since we fragmented during the item.
// Thus, no one can go back to add more to the chunk length
start_block();
// Now turn off the flag so we go back to the normal
// behavior of closing a chunk when we fragment and
// reopening afterwards.
specialChunk = false;
}
}
// Called after writing primitives
private void checkPrimitiveAcrossFragmentedChunk()
{
if (primitiveAcrossFragmentedChunk) {
primitiveAcrossFragmentedChunk = false;
inBlock = false;
// It would be nice to have a StreamPosition
// abstraction if we could avoid allocation
// overhead.
blockSizeIndex = -1;
blockSizePosition = -1;
// Start a new chunk
start_block();
}
}
public void write_octet(byte x) {
super.write_octet(x);
checkPrimitiveAcrossFragmentedChunk();
}
public void write_short(short x) {
super.write_short(x);
checkPrimitiveAcrossFragmentedChunk();
}
public void write_long(int x) {
super.write_long(x);
checkPrimitiveAcrossFragmentedChunk();
}
public void write_longlong(long x) {
super.write_longlong(x);
checkPrimitiveAcrossFragmentedChunk();
}
// Called by RequestMessage_1_2 or ReplyMessage_1_2 classes only.
void setHeaderPadding(boolean headerPadding) {
this.headerPadding = headerPadding;
}
protected void alignAndReserve(int align, int n) {
// headerPadding bit is set by the write operation of RequestMessage_1_2
// or ReplyMessage_1_2 classes. When set, the very first body write
// operation (from the stub code) would trigger an alignAndReserve
// method call, that would in turn add the appropriate header padding,
// such that the body is aligned on a 8-octet boundary. The padding
// is required for GIOP versions 1.2 and above, only if body is present.
if (headerPadding == true) {
headerPadding = false;
alignOnBoundary(ORBConstants.GIOP_12_MSG_BODY_ALIGNMENT);
}
// In GIOP 1.2, we always end fragments at our
// fragment size, which is an "evenly divisible
// 8 byte boundary" (aka divisible by 16). A fragment can
// end with appropriate alignment padding, but no padding
// is needed with respect to the next GIOP fragment
// header since it ends on an 8 byte boundary.
bbwi.position(bbwi.position() + computeAlignment(align));
if (bbwi.position() + n > bbwi.buflen)
grow(align, n);
}
protected void grow(int align, int n) {
// Save the current size for possible post-fragmentation calculation
int oldSize = bbwi.position();
// See notes where specialChunk is defined, as well as the
// above notes for primitiveAcrossFragmentedChunk.
//
// If we're writing a primitive and chunking, we need to update
// the chunk length to include the length of the primitive (unless
// this complexity is handled by specialChunk).
//
// Note that this is wasted processing in the grow case, but that
// we don't actually close the chunk in that case.
boolean handleChunk = (inBlock && !specialChunk);
if (handleChunk) {
int oldIndex = bbwi.position();
bbwi.position(blockSizeIndex - 4);
writeLongWithoutAlign((oldIndex - blockSizeIndex) + n);
bbwi.position(oldIndex);
}
bbwi.needed = n;
bufferManagerWrite.overflow(bbwi);
// At this point, if we fragmented, we should have a ByteBufferWithInfo
// with the fragment header already marshalled. The buflen and position
// should be updated accordingly, and the fragmented flag should be set.
// Note that fragmented is only true in the streaming and collect cases.
if (bbwi.fragmented) {
// Clear the flag
bbwi.fragmented = false;
// Update fragmentOffset so indirections work properly.
// At this point, oldSize is the entire length of the
// previous buffer. bbwi.position() is the length of the
// fragment header of this buffer.
fragmentOffset += (oldSize - bbwi.position());
// We just fragmented, and need to signal that we should
// start a new chunk after writing the primitive.
if (handleChunk)
primitiveAcrossFragmentedChunk = true;
}
}
public GIOPVersion getGIOPVersion() {
return GIOPVersion.V1_2;
}
public void write_wchar(char x)
{
// In GIOP 1.2, a wchar is encoded as an unsigned octet length
// followed by the octets of the converted wchar. This is good,
// but it causes problems with our chunking code. We don't
// want that octet to get put in a different chunk at the end
// of the previous fragment.
//
// Ensure that this won't happen by overriding write_wchar_array
// and doing our own handleSpecialChunkBegin/End here.
CodeSetConversion.CTBConverter converter = getWCharConverter();
converter.convert(x);
handleSpecialChunkBegin(1 + converter.getNumBytes());
write_octet((byte)converter.getNumBytes());
byte[] result = converter.getBytes();
// Write the bytes without messing with chunking
// See CDROutputStream_1_0
internalWriteOctetArray(result, 0, converter.getNumBytes());
handleSpecialChunkEnd();
}
public void write_wchar_array(char[] value, int offset, int length)
{
if (value == null) {
throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
}
CodeSetConversion.CTBConverter converter = getWCharConverter();
// Unfortunately, because of chunking, we have to convert the
// entire char[] to a byte[] array first so we can know how
// many bytes we're writing ahead of time. You can't split
// an array of primitives into multiple chunks.
int totalNumBytes = 0;
// Remember that every wchar starts with an octet telling
// its length. The buffer size is an upper bound estimate.
int maxLength = (int)Math.ceil(converter.getMaxBytesPerChar() * length);
byte[] buffer = new byte[maxLength + length];
for (int i = 0; i < length; i++) {
// Convert one wchar
converter.convert(value[offset + i]);
// Make sure to add the octet length
buffer[totalNumBytes++] = (byte)converter.getNumBytes();
// Copy it into our buffer
System.arraycopy(converter.getBytes(), 0,
buffer, totalNumBytes,
converter.getNumBytes());
totalNumBytes += converter.getNumBytes();
}
// Now that we know the total length, we can deal with chunking.
// Note that we don't have to worry about alignment since they're
// just octets.
handleSpecialChunkBegin(totalNumBytes);
// Must use totalNumBytes rather than buffer.length since the
// buffer.length is only the upper bound estimate.
internalWriteOctetArray(buffer, 0, totalNumBytes);
handleSpecialChunkEnd();
}
public void write_wstring(String value) {
if (value == null) {
throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
}
// In GIOP 1.2, wstrings are not terminated by a null. The
// length is the number of octets in the converted format.
// A zero length string is represented with the 4 byte length
// value of 0.
if (value.length() == 0) {
write_long(0);
return;
}
CodeSetConversion.CTBConverter converter = getWCharConverter();
converter.convert(value);
handleSpecialChunkBegin(computeAlignment(4) + 4 + converter.getNumBytes());
write_long(converter.getNumBytes());
// Write the octet array without tampering with chunking
internalWriteOctetArray(converter.getBytes(), 0, converter.getNumBytes());
handleSpecialChunkEnd();
}
}