CDROutputStream_1_2.java revision 608:7e06bf1dcb09
1/*
2 * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package com.sun.corba.se.impl.encoding;
26
27import org.omg.CORBA.BAD_PARAM;
28import org.omg.CORBA.INTERNAL;
29import org.omg.CORBA.CompletionStatus;
30import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
31import com.sun.corba.se.impl.encoding.CodeSetConversion;
32import com.sun.corba.se.impl.orbutil.ORBConstants;
33
34public class CDROutputStream_1_2 extends CDROutputStream_1_1
35{
36    // There's a situation with chunking with fragmentation
37    // in which the alignment for a primitive value is needed
38    // to fill fragment N, but the primitive won't fit so
39    // must go into fragment N + 1.  The behavior is the same
40    // as that for specialChunks.
41    //
42    // Unfortunately, given the current code, we can't reuse
43    // specialChunk.  If you wrap each of the following
44    // write calls with handleSpecialChunkBegin/End, you
45    // will lose your state because the primitive calls will
46    // change the variables, etc.
47    //
48    // All of the CDR code should be rewritten moving chunking
49    // to a different level, perhaps in the buffer managers.
50    // We want to move to a compositional model rather than
51    // using inheritance.
52    //
53    // Note that in the grow case, chunks are _NOT_ closed
54    // at grow points, now.
55    //
56    // **** NOTE ****
57    // Since we will not support valuetypes with GIOP 1.1, that
58    // also means we do not support chunking there.
59    //
60    protected boolean primitiveAcrossFragmentedChunk = false;
61
62    // Used in chunking.  Here's how this works:
63    //
64    // When chunking and writing an array of primitives, a string, or a
65    // wstring, _AND_ it won't fit in the buffer do the following.  (As
66    // you can see, this is a very "special" chunk.)
67    //
68    //     1.  Write the length of the chunk including the array length
69    //     2.  Set specialChunk to true
70    // 3 applies to ALL chunking:
71    //     3.  In grow, if we need to fragment and specialChunk is false
72    //               a) call end_block
73    //               b) fragment
74    // Now back to the array only case:
75    //     [write the data]
76    //     4.  if specialChunk is true
77    //               a) Close the chunk
78    //               b) Set specialChunk to false
79
80    protected boolean specialChunk = false;
81
82    // Indicates whether the header should be padded. In GIOP 1.2 and above, the
83    // body must be aligned on a 8-octet boundary, and so the header needs to be
84    // padded appropriately. However, if there is no body to a request or reply
85    // message, there is no need to pad the header, in the unfragmented case.
86    private boolean headerPadding;
87
88    protected void handleSpecialChunkBegin(int requiredSize)
89    {
90        // If we're chunking and the item won't fit in the buffer
91        if (inBlock && requiredSize + bbwi.position() > bbwi.buflen) {
92
93            // Duplicating some code from end_block.  Compute
94            // and write the total chunk length.
95
96            int oldSize = bbwi.position();
97            bbwi.position(blockSizeIndex - 4);
98
99            //write_long(oldSize - blockSizeIndex);
100            writeLongWithoutAlign((oldSize - blockSizeIndex) + requiredSize);
101            bbwi.position(oldSize);
102
103            // Set the special flag so we don't end the chunk when
104            // we fragment
105            specialChunk = true;
106        }
107    }
108
109    protected void handleSpecialChunkEnd()
110    {
111        // If we're in a chunk and the item spanned fragments
112        if (inBlock && specialChunk) {
113
114            // This is unnecessary, but I just want to show that
115            // we're done with the current chunk.  (the end_block
116            // call is inappropriate here)
117            inBlock = false;
118            blockSizeIndex = -1;
119            blockSizePosition = -1;
120
121            // Start a new chunk since we fragmented during the item.
122            // Thus, no one can go back to add more to the chunk length
123            start_block();
124
125            // Now turn off the flag so we go back to the normal
126            // behavior of closing a chunk when we fragment and
127            // reopening afterwards.
128            specialChunk = false;
129        }
130    }
131
132    // Called after writing primitives
133    private void checkPrimitiveAcrossFragmentedChunk()
134    {
135        if (primitiveAcrossFragmentedChunk) {
136            primitiveAcrossFragmentedChunk = false;
137
138            inBlock = false;
139
140            // It would be nice to have a StreamPosition
141            // abstraction if we could avoid allocation
142            // overhead.
143            blockSizeIndex = -1;
144            blockSizePosition = -1;
145
146            // Start a new chunk
147            start_block();
148        }
149    }
150
151
152    public void write_octet(byte x) {
153        super.write_octet(x);
154        checkPrimitiveAcrossFragmentedChunk();
155    }
156
157    public void write_short(short x) {
158        super.write_short(x);
159        checkPrimitiveAcrossFragmentedChunk();
160    }
161
162    public void write_long(int x) {
163        super.write_long(x);
164        checkPrimitiveAcrossFragmentedChunk();
165    }
166
167    public void write_longlong(long x) {
168        super.write_longlong(x);
169        checkPrimitiveAcrossFragmentedChunk();
170    }
171
172    // Called by RequestMessage_1_2 or ReplyMessage_1_2 classes only.
173    void setHeaderPadding(boolean headerPadding) {
174        this.headerPadding = headerPadding;
175    }
176
177    protected void alignAndReserve(int align, int n) {
178
179        // headerPadding bit is set by the write operation of RequestMessage_1_2
180        // or ReplyMessage_1_2 classes. When set, the very first body write
181        // operation (from the stub code) would trigger an alignAndReserve
182        // method call, that would in turn add the appropriate header padding,
183        // such that the body is aligned on a 8-octet boundary. The padding
184        // is required for GIOP versions 1.2 and above, only if body is present.
185        if (headerPadding == true) {
186            headerPadding = false;
187            alignOnBoundary(ORBConstants.GIOP_12_MSG_BODY_ALIGNMENT);
188        }
189
190        // In GIOP 1.2, we always end fragments at our
191        // fragment size, which is an "evenly divisible
192        // 8 byte boundary" (aka divisible by 16).  A fragment can
193        // end with appropriate alignment padding, but no padding
194        // is needed with respect to the next GIOP fragment
195        // header since it ends on an 8 byte boundary.
196
197        bbwi.position(bbwi.position() + computeAlignment(align));
198
199        if (bbwi.position() + n  > bbwi.buflen)
200            grow(align, n);
201    }
202
203    protected void grow(int align, int n) {
204
205        // Save the current size for possible post-fragmentation calculation
206        int oldSize = bbwi.position();
207
208        // See notes where specialChunk is defined, as well as the
209        // above notes for primitiveAcrossFragmentedChunk.
210        //
211        // If we're writing a primitive and chunking, we need to update
212        // the chunk length to include the length of the primitive (unless
213        // this complexity is handled by specialChunk).
214        //
215        // Note that this is wasted processing in the grow case, but that
216        // we don't actually close the chunk in that case.
217        boolean handleChunk = (inBlock && !specialChunk);
218        if (handleChunk) {
219            int oldIndex = bbwi.position();
220
221            bbwi.position(blockSizeIndex - 4);
222
223            writeLongWithoutAlign((oldIndex - blockSizeIndex) + n);
224
225            bbwi.position(oldIndex);
226        }
227
228        bbwi.needed = n;
229        bufferManagerWrite.overflow(bbwi);
230
231        // At this point, if we fragmented, we should have a ByteBufferWithInfo
232        // with the fragment header already marshalled.  The buflen and position
233        // should be updated accordingly, and the fragmented flag should be set.
234
235        // Note that fragmented is only true in the streaming and collect cases.
236        if (bbwi.fragmented) {
237
238            // Clear the flag
239            bbwi.fragmented = false;
240
241            // Update fragmentOffset so indirections work properly.
242            // At this point, oldSize is the entire length of the
243            // previous buffer.  bbwi.position() is the length of the
244            // fragment header of this buffer.
245            fragmentOffset += (oldSize - bbwi.position());
246
247            // We just fragmented, and need to signal that we should
248            // start a new chunk after writing the primitive.
249            if (handleChunk)
250                primitiveAcrossFragmentedChunk = true;
251
252        }
253    }
254
255    public GIOPVersion getGIOPVersion() {
256        return GIOPVersion.V1_2;
257    }
258
259    public void write_wchar(char x)
260    {
261        // In GIOP 1.2, a wchar is encoded as an unsigned octet length
262        // followed by the octets of the converted wchar.  This is good,
263        // but it causes problems with our chunking code.  We don't
264        // want that octet to get put in a different chunk at the end
265        // of the previous fragment.
266        //
267        // Ensure that this won't happen by overriding write_wchar_array
268        // and doing our own handleSpecialChunkBegin/End here.
269        CodeSetConversion.CTBConverter converter = getWCharConverter();
270
271        converter.convert(x);
272
273        handleSpecialChunkBegin(1 + converter.getNumBytes());
274
275        write_octet((byte)converter.getNumBytes());
276
277        byte[] result = converter.getBytes();
278
279        // Write the bytes without messing with chunking
280        // See CDROutputStream_1_0
281        internalWriteOctetArray(result, 0, converter.getNumBytes());
282
283        handleSpecialChunkEnd();
284    }
285
286    public void write_wchar_array(char[] value, int offset, int length)
287    {
288        if (value == null) {
289            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
290        }
291
292        CodeSetConversion.CTBConverter converter = getWCharConverter();
293
294        // Unfortunately, because of chunking, we have to convert the
295        // entire char[] to a byte[] array first so we can know how
296        // many bytes we're writing ahead of time.  You can't split
297        // an array of primitives into multiple chunks.
298        int totalNumBytes = 0;
299
300        // Remember that every wchar starts with an octet telling
301        // its length.  The buffer size is an upper bound estimate.
302        int maxLength = (int)Math.ceil(converter.getMaxBytesPerChar() * length);
303        byte[] buffer = new byte[maxLength + length];
304
305        for (int i = 0; i < length; i++) {
306            // Convert one wchar
307            converter.convert(value[offset + i]);
308
309            // Make sure to add the octet length
310            buffer[totalNumBytes++] = (byte)converter.getNumBytes();
311
312            // Copy it into our buffer
313            System.arraycopy(converter.getBytes(), 0,
314                             buffer, totalNumBytes,
315                             converter.getNumBytes());
316
317            totalNumBytes += converter.getNumBytes();
318        }
319
320        // Now that we know the total length, we can deal with chunking.
321        // Note that we don't have to worry about alignment since they're
322        // just octets.
323        handleSpecialChunkBegin(totalNumBytes);
324
325        // Must use totalNumBytes rather than buffer.length since the
326        // buffer.length is only the upper bound estimate.
327        internalWriteOctetArray(buffer, 0, totalNumBytes);
328
329        handleSpecialChunkEnd();
330    }
331
332    public void write_wstring(String value) {
333        if (value == null) {
334            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
335        }
336
337        // In GIOP 1.2, wstrings are not terminated by a null.  The
338        // length is the number of octets in the converted format.
339        // A zero length string is represented with the 4 byte length
340        // value of 0.
341        if (value.length() == 0) {
342            write_long(0);
343            return;
344        }
345
346        CodeSetConversion.CTBConverter converter = getWCharConverter();
347
348        converter.convert(value);
349
350        handleSpecialChunkBegin(computeAlignment(4) + 4 + converter.getNumBytes());
351
352        write_long(converter.getNumBytes());
353
354        // Write the octet array without tampering with chunking
355        internalWriteOctetArray(converter.getBytes(), 0, converter.getNumBytes());
356
357        handleSpecialChunkEnd();
358    }
359}
360