1. /*
  2. * @(#)CDROutputStream_1_2.java 1.12 04/03/01
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.corba.se.impl.encoding;
  8. import org.omg.CORBA.BAD_PARAM;
  9. import org.omg.CORBA.INTERNAL;
  10. import org.omg.CORBA.CompletionStatus;
  11. import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
  12. import com.sun.corba.se.impl.encoding.CodeSetConversion;
  13. import com.sun.corba.se.impl.orbutil.ORBConstants;
  14. public class CDROutputStream_1_2 extends CDROutputStream_1_1
  15. {
  16. // There's a situation with chunking with fragmentation
  17. // in which the alignment for a primitive value is needed
  18. // to fill fragment N, but the primitive won't fit so
  19. // must go into fragment N + 1. The behavior is the same
  20. // as that for specialChunks.
  21. //
  22. // Unfortunately, given the current code, we can't reuse
  23. // specialChunk. If you wrap each of the following
  24. // write calls with handleSpecialChunkBegin/End, you
  25. // will lose your state because the primitive calls will
  26. // change the variables, etc.
  27. //
  28. // All of the CDR code should be rewritten moving chunking
  29. // to a different level, perhaps in the buffer managers.
  30. // We want to move to a compositional model rather than
  31. // using inheritance.
  32. //
  33. // Note that in the grow case, chunks are _NOT_ closed
  34. // at grow points, now.
  35. //
  36. // **** NOTE ****
  37. // Since we will not support valuetypes with GIOP 1.1, that
  38. // also means we do not support chunking there.
  39. //
  40. protected boolean primitiveAcrossFragmentedChunk = false;
  41. // Used in chunking. Here's how this works:
  42. //
  43. // When chunking and writing an array of primitives, a string, or a
  44. // wstring, _AND_ it won't fit in the buffer do the following. (As
  45. // you can see, this is a very "special" chunk.)
  46. //
  47. // 1. Write the length of the chunk including the array length
  48. // 2. Set specialChunk to true
  49. // 3 applies to ALL chunking:
  50. // 3. In grow, if we need to fragment and specialChunk is false
  51. // a) call end_block
  52. // b) fragment
  53. // Now back to the array only case:
  54. // [write the data]
  55. // 4. if specialChunk is true
  56. // a) Close the chunk
  57. // b) Set specialChunk to false
  58. protected boolean specialChunk = false;
  59. // Indicates whether the header should be padded. In GIOP 1.2 and above, the
  60. // body must be aligned on a 8-octet boundary, and so the header needs to be
  61. // padded appropriately. However, if there is no body to a request or reply
  62. // message, there is no need to pad the header, in the unfragmented case.
  63. private boolean headerPadding;
  64. protected void handleSpecialChunkBegin(int requiredSize)
  65. {
  66. // If we're chunking and the item won't fit in the buffer
  67. if (inBlock && requiredSize + bbwi.position() > bbwi.buflen) {
  68. // Duplicating some code from end_block. Compute
  69. // and write the total chunk length.
  70. int oldSize = bbwi.position();
  71. bbwi.position(blockSizeIndex - 4);
  72. //write_long(oldSize - blockSizeIndex);
  73. writeLongWithoutAlign((oldSize - blockSizeIndex) + requiredSize);
  74. bbwi.position(oldSize);
  75. // Set the special flag so we don't end the chunk when
  76. // we fragment
  77. specialChunk = true;
  78. }
  79. }
  80. protected void handleSpecialChunkEnd()
  81. {
  82. // If we're in a chunk and the item spanned fragments
  83. if (inBlock && specialChunk) {
  84. // This is unnecessary, but I just want to show that
  85. // we're done with the current chunk. (the end_block
  86. // call is inappropriate here)
  87. inBlock = false;
  88. blockSizeIndex = -1;
  89. blockSizePosition = -1;
  90. // Start a new chunk since we fragmented during the item.
  91. // Thus, no one can go back to add more to the chunk length
  92. start_block();
  93. // Now turn off the flag so we go back to the normal
  94. // behavior of closing a chunk when we fragment and
  95. // reopening afterwards.
  96. specialChunk = false;
  97. }
  98. }
  99. // Called after writing primitives
  100. private void checkPrimitiveAcrossFragmentedChunk()
  101. {
  102. if (primitiveAcrossFragmentedChunk) {
  103. primitiveAcrossFragmentedChunk = false;
  104. inBlock = false;
  105. // It would be nice to have a StreamPosition
  106. // abstraction if we could avoid allocation
  107. // overhead.
  108. blockSizeIndex = -1;
  109. blockSizePosition = -1;
  110. // Start a new chunk
  111. start_block();
  112. }
  113. }
  114. public void write_octet(byte x) {
  115. super.write_octet(x);
  116. checkPrimitiveAcrossFragmentedChunk();
  117. }
  118. public void write_short(short x) {
  119. super.write_short(x);
  120. checkPrimitiveAcrossFragmentedChunk();
  121. }
  122. public void write_long(int x) {
  123. super.write_long(x);
  124. checkPrimitiveAcrossFragmentedChunk();
  125. }
  126. public void write_longlong(long x) {
  127. super.write_longlong(x);
  128. checkPrimitiveAcrossFragmentedChunk();
  129. }
  130. // Called by RequestMessage_1_2 or ReplyMessage_1_2 classes only.
  131. void setHeaderPadding(boolean headerPadding) {
  132. this.headerPadding = headerPadding;
  133. }
  134. protected void alignAndReserve(int align, int n) {
  135. // headerPadding bit is set by the write operation of RequestMessage_1_2
  136. // or ReplyMessage_1_2 classes. When set, the very first body write
  137. // operation (from the stub code) would trigger an alignAndReserve
  138. // method call, that would in turn add the appropriate header padding,
  139. // such that the body is aligned on a 8-octet boundary. The padding
  140. // is required for GIOP versions 1.2 and above, only if body is present.
  141. if (headerPadding == true) {
  142. headerPadding = false;
  143. alignOnBoundary(ORBConstants.GIOP_12_MSG_BODY_ALIGNMENT);
  144. }
  145. // In GIOP 1.2, we always end fragments at our
  146. // fragment size, which is an "evenly divisible
  147. // 8 byte boundary" (aka divisible by 16). A fragment can
  148. // end with appropriate alignment padding, but no padding
  149. // is needed with respect to the next GIOP fragment
  150. // header since it ends on an 8 byte boundary.
  151. bbwi.position(bbwi.position() + computeAlignment(align));
  152. if (bbwi.position() + n > bbwi.buflen)
  153. grow(align, n);
  154. }
  155. protected void grow(int align, int n) {
  156. // Save the current size for possible post-fragmentation calculation
  157. int oldSize = bbwi.position();
  158. // See notes where specialChunk is defined, as well as the
  159. // above notes for primitiveAcrossFragmentedChunk.
  160. //
  161. // If we're writing a primitive and chunking, we need to update
  162. // the chunk length to include the length of the primitive (unless
  163. // this complexity is handled by specialChunk).
  164. //
  165. // Note that this is wasted processing in the grow case, but that
  166. // we don't actually close the chunk in that case.
  167. boolean handleChunk = (inBlock && !specialChunk);
  168. if (handleChunk) {
  169. int oldIndex = bbwi.position();
  170. bbwi.position(blockSizeIndex - 4);
  171. writeLongWithoutAlign((oldIndex - blockSizeIndex) + n);
  172. bbwi.position(oldIndex);
  173. }
  174. bbwi.needed = n;
  175. bufferManagerWrite.overflow(bbwi);
  176. // At this point, if we fragmented, we should have a ByteBufferWithInfo
  177. // with the fragment header already marshalled. The buflen and position
  178. // should be updated accordingly, and the fragmented flag should be set.
  179. // Note that fragmented is only true in the streaming and collect cases.
  180. if (bbwi.fragmented) {
  181. // Clear the flag
  182. bbwi.fragmented = false;
  183. // Update fragmentOffset so indirections work properly.
  184. // At this point, oldSize is the entire length of the
  185. // previous buffer. bbwi.position() is the length of the
  186. // fragment header of this buffer.
  187. fragmentOffset += (oldSize - bbwi.position());
  188. // We just fragmented, and need to signal that we should
  189. // start a new chunk after writing the primitive.
  190. if (handleChunk)
  191. primitiveAcrossFragmentedChunk = true;
  192. }
  193. }
  194. public GIOPVersion getGIOPVersion() {
  195. return GIOPVersion.V1_2;
  196. }
  197. public void write_wchar(char x)
  198. {
  199. // In GIOP 1.2, a wchar is encoded as an unsigned octet length
  200. // followed by the octets of the converted wchar. This is good,
  201. // but it causes problems with our chunking code. We don't
  202. // want that octet to get put in a different chunk at the end
  203. // of the previous fragment.
  204. //
  205. // Ensure that this won't happen by overriding write_wchar_array
  206. // and doing our own handleSpecialChunkBegin/End here.
  207. CodeSetConversion.CTBConverter converter = getWCharConverter();
  208. converter.convert(x);
  209. handleSpecialChunkBegin(1 + converter.getNumBytes());
  210. write_octet((byte)converter.getNumBytes());
  211. byte[] result = converter.getBytes();
  212. // Write the bytes without messing with chunking
  213. // See CDROutputStream_1_0
  214. internalWriteOctetArray(result, 0, converter.getNumBytes());
  215. handleSpecialChunkEnd();
  216. }
  217. public void write_wchar_array(char[] value, int offset, int length)
  218. {
  219. if (value == null) {
  220. throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
  221. }
  222. CodeSetConversion.CTBConverter converter = getWCharConverter();
  223. // Unfortunately, because of chunking, we have to convert the
  224. // entire char[] to a byte[] array first so we can know how
  225. // many bytes we're writing ahead of time. You can't split
  226. // an array of primitives into multiple chunks.
  227. int totalNumBytes = 0;
  228. // Remember that every wchar starts with an octet telling
  229. // its length. The buffer size is an upper bound estimate.
  230. int maxLength = (int)Math.ceil(converter.getMaxBytesPerChar() * length);
  231. byte[] buffer = new byte[maxLength + length];
  232. for (int i = 0; i < length; i++) {
  233. // Convert one wchar
  234. converter.convert(value[offset + i]);
  235. // Make sure to add the octet length
  236. buffer[totalNumBytes++] = (byte)converter.getNumBytes();
  237. // Copy it into our buffer
  238. System.arraycopy(converter.getBytes(), 0,
  239. buffer, totalNumBytes,
  240. converter.getNumBytes());
  241. totalNumBytes += converter.getNumBytes();
  242. }
  243. // Now that we know the total length, we can deal with chunking.
  244. // Note that we don't have to worry about alignment since they're
  245. // just octets.
  246. handleSpecialChunkBegin(totalNumBytes);
  247. // Must use totalNumBytes rather than buffer.length since the
  248. // buffer.length is only the upper bound estimate.
  249. internalWriteOctetArray(buffer, 0, totalNumBytes);
  250. handleSpecialChunkEnd();
  251. }
  252. public void write_wstring(String value) {
  253. if (value == null) {
  254. throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
  255. }
  256. // In GIOP 1.2, wstrings are not terminated by a null. The
  257. // length is the number of octets in the converted format.
  258. // A zero length string is represented with the 4 byte length
  259. // value of 0.
  260. if (value.length() == 0) {
  261. write_long(0);
  262. return;
  263. }
  264. CodeSetConversion.CTBConverter converter = getWCharConverter();
  265. converter.convert(value);
  266. handleSpecialChunkBegin(computeAlignment(4) + 4 + converter.getNumBytes());
  267. write_long(converter.getNumBytes());
  268. // Write the octet array without tampering with chunking
  269. internalWriteOctetArray(converter.getBytes(), 0, converter.getNumBytes());
  270. handleSpecialChunkEnd();
  271. }
  272. }