1. /*
  2. * @(#)CDROutputStream_1_2.java 1.9 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.corba.se.internal.iiop;
  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.internal.orbutil.MinorCodes;
  12. import com.sun.corba.se.internal.core.GIOPVersion;
  13. import com.sun.corba.se.internal.core.CodeSetConversion;
  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. protected void handleSpecialChunkBegin(int requiredSize)
  60. {
  61. // If we're chunking and the item won't fit in the buffer
  62. if (inBlock && requiredSize + bbwi.index > bbwi.buflen) {
  63. // Duplicating some code from end_block. Compute
  64. // and write the total chunk length.
  65. int oldSize = bbwi.index;
  66. bbwi.index = blockSizeIndex - 4;
  67. //write_long(oldSize - blockSizeIndex);
  68. writeLongWithoutAlign((oldSize - blockSizeIndex) + requiredSize);
  69. bbwi.index = oldSize;
  70. // Set the special flag so we don't end the chunk when
  71. // we fragment
  72. specialChunk = true;
  73. }
  74. }
  75. protected void handleSpecialChunkEnd()
  76. {
  77. // If we're in a chunk and the item spanned fragments
  78. if (inBlock && specialChunk) {
  79. // This is unnecessary, but I just want to show that
  80. // we're done with the current chunk. (the end_block
  81. // call is inappropriate here)
  82. inBlock = false;
  83. blockSizeIndex = -1;
  84. blockSizePosition = -1;
  85. // Start a new chunk since we fragmented during the item.
  86. // Thus, no one can go back to add more to the chunk length
  87. start_block();
  88. // Now turn off the flag so we go back to the normal
  89. // behavior of closing a chunk when we fragment and
  90. // reopening afterwards.
  91. specialChunk = false;
  92. }
  93. }
  94. // Called after writing primitives
  95. private void checkPrimitiveAcrossFragmentedChunk()
  96. {
  97. if (primitiveAcrossFragmentedChunk) {
  98. primitiveAcrossFragmentedChunk = false;
  99. inBlock = false;
  100. // It would be nice to have a StreamPosition
  101. // abstraction if we could avoid allocation
  102. // overhead.
  103. blockSizeIndex = -1;
  104. blockSizePosition = -1;
  105. // Start a new chunk
  106. start_block();
  107. }
  108. }
  109. public void write_octet(byte x) {
  110. super.write_octet(x);
  111. checkPrimitiveAcrossFragmentedChunk();
  112. }
  113. public void write_short(short x) {
  114. super.write_short(x);
  115. checkPrimitiveAcrossFragmentedChunk();
  116. }
  117. public void write_long(int x) {
  118. super.write_long(x);
  119. checkPrimitiveAcrossFragmentedChunk();
  120. }
  121. public void write_longlong(long x) {
  122. super.write_longlong(x);
  123. checkPrimitiveAcrossFragmentedChunk();
  124. }
  125. protected void alignAndReserve(int align, int n) {
  126. // In GIOP 1.2, we always end fragments at our
  127. // fragment size, which is an "evenly divisible
  128. // 8 byte boundary" (aka divisible by 16). A fragment can
  129. // end with appropriate alignment padding, but no padding
  130. // is needed with respect to the next GIOP fragment
  131. // header since it ends on an 8 byte boundary.
  132. bbwi.index += computeAlignment(align);
  133. if (bbwi.index + n > bbwi.buflen)
  134. grow(align, n);
  135. }
  136. protected void grow(int align, int n) {
  137. // Save the current size for possible post-fragmentation calculation
  138. int oldSize = bbwi.index;
  139. // See notes where specialChunk is defined, as well as the
  140. // above notes for primitiveAcrossFragmentedChunk.
  141. //
  142. // If we're writing a primitive and chunking, we need to update
  143. // the chunk length to include the length of the primitive (unless
  144. // this complexity is handled by specialChunk).
  145. //
  146. // Note that this is wasted processing in the grow case, but that
  147. // we don't actually close the chunk in that case.
  148. boolean handleChunk = (inBlock && !specialChunk);
  149. if (handleChunk) {
  150. int oldIndex = bbwi.index;
  151. bbwi.index = blockSizeIndex - 4;
  152. writeLongWithoutAlign((oldIndex - blockSizeIndex) + n);
  153. bbwi.index = oldIndex;
  154. }
  155. bbwi.needed = n;
  156. bufferManagerWrite.overflow(bbwi);
  157. // At this point, if we fragmented, we should have a ByteBufferWithInfo
  158. // with the fragment header already marshalled. The buflen and index fields
  159. // should be updated accordingly, and the fragmented flag should be set.
  160. // Note that fragmented is only true in the streaming and collect cases.
  161. if (bbwi.fragmented) {
  162. // Clear the flag
  163. bbwi.fragmented = false;
  164. // Update fragmentOffset so indirections work properly.
  165. // At this point, oldSize is the entire length of the
  166. // previous buffer. bbwi.index is the length of the
  167. // fragment header of this buffer.
  168. fragmentOffset += (oldSize - bbwi.index);
  169. // We just fragmented, and need to signal that we should
  170. // start a new chunk after writing the primitive.
  171. if (handleChunk)
  172. primitiveAcrossFragmentedChunk = true;
  173. }
  174. }
  175. public GIOPVersion getGIOPVersion() {
  176. return GIOPVersion.V1_2;
  177. }
  178. public void write_wchar(char x)
  179. {
  180. // In GIOP 1.2, a wchar is encoded as an unsigned octet length
  181. // followed by the octets of the converted wchar. This is good,
  182. // but it causes problems with our chunking code. We don't
  183. // want that octet to get put in a different chunk at the end
  184. // of the previous fragment.
  185. //
  186. // Ensure that this won't happen by overriding write_wchar_array
  187. // and doing our own handleSpecialChunkBegin/End here.
  188. CodeSetConversion.CTBConverter converter = getWCharConverter();
  189. converter.convert(x);
  190. handleSpecialChunkBegin(1 + converter.getNumBytes());
  191. write_octet((byte)converter.getNumBytes());
  192. byte[] result = converter.getBytes();
  193. // Write the bytes without messing with chunking
  194. // See CDROutputStream_1_0
  195. internalWriteOctetArray(result, 0, converter.getNumBytes());
  196. handleSpecialChunkEnd();
  197. }
  198. public void write_wchar_array(char[] value, int offset, int length)
  199. {
  200. if (value == null) {
  201. throw new BAD_PARAM(com.sun.corba.se.internal.orbutil.MinorCodes.NULL_PARAM,
  202. CompletionStatus.COMPLETED_MAYBE);
  203. }
  204. CodeSetConversion.CTBConverter converter = getWCharConverter();
  205. // Unfortunately, because of chunking, we have to convert the
  206. // entire char[] to a byte[] array first so we can know how
  207. // many bytes we're writing ahead of time. You can't split
  208. // an array of primitives into multiple chunks.
  209. int totalNumBytes = 0;
  210. // Remember that every wchar starts with an octet telling
  211. // its length. The buffer size is an upper bound estimate.
  212. byte[] buffer = new byte[converter.getMaxBytesPerChar() * length
  213. + length];
  214. for (int i = 0; i < length; i++) {
  215. // Convert one wchar
  216. converter.convert(value[offset + i]);
  217. // Make sure to add the octet length
  218. buffer[totalNumBytes++] = (byte)converter.getNumBytes();
  219. // Copy it into our buffer
  220. System.arraycopy(converter.getBytes(), 0,
  221. buffer, totalNumBytes,
  222. converter.getNumBytes());
  223. totalNumBytes += converter.getNumBytes();
  224. }
  225. // Now that we know the total length, we can deal with chunking.
  226. // Note that we don't have to worry about alignment since they're
  227. // just octets.
  228. handleSpecialChunkBegin(totalNumBytes);
  229. // Must use totalNumBytes rather than buffer.length since the
  230. // buffer.length is only the upper bound estimate.
  231. internalWriteOctetArray(buffer, 0, totalNumBytes);
  232. handleSpecialChunkEnd();
  233. }
  234. public void write_wstring(String value) {
  235. if (value == null) {
  236. throw new BAD_PARAM(com.sun.corba.se.internal.orbutil.MinorCodes.NULL_PARAM,
  237. CompletionStatus.COMPLETED_MAYBE);
  238. }
  239. // In GIOP 1.2, wstrings are not terminated by a null. The
  240. // length is the number of octets in the converted format.
  241. // A zero length string is represented with the 4 byte length
  242. // value of 0.
  243. if (value.length() == 0) {
  244. write_long(0);
  245. return;
  246. }
  247. CodeSetConversion.CTBConverter converter = getWCharConverter();
  248. converter.convert(value);
  249. handleSpecialChunkBegin(computeAlignment(4) + 4 + converter.getNumBytes());
  250. write_long(converter.getNumBytes());
  251. // Write the octet array without tampering with chunking
  252. internalWriteOctetArray(converter.getBytes(), 0, converter.getNumBytes());
  253. handleSpecialChunkEnd();
  254. }
  255. }