1 /*
   2  * Copyright (c) 1994, 2019, 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  */
  25 
  26 package java.io;
  27 
  28 /**
  29  * A data output stream lets an application write primitive Java data
  30  * types to an output stream in a portable way. An application can
  31  * then use a data input stream to read the data back in.
  32  *
  33  * @author  unascribed
  34  * @see     java.io.DataInputStream
  35  * @since   1.0
  36  */
  37 public
  38 class DataOutputStream extends FilterOutputStream implements DataOutput {
  39     /**
  40      * The number of bytes written to the data output stream so far.
  41      * If this counter overflows, it will be wrapped to Integer.MAX_VALUE.
  42      */
  43     protected int written;
  44 
  45     /**
  46      * bytearr is initialized on demand by writeUTF
  47      */
  48     private byte[] bytearr = null;
  49 
  50     /**
  51      * Creates a new data output stream to write data to the specified
  52      * underlying output stream. The counter <code>written</code> is
  53      * set to zero.
  54      *
  55      * @param   out   the underlying output stream, to be saved for later
  56      *                use.
  57      * @see     java.io.FilterOutputStream#out
  58      */
  59     public DataOutputStream(OutputStream out) {
  60         super(out);
  61         if (out == null) {
  62             throw new NullPointerException();
  63         } 
  64     }
  65 
  66     /**
  67      * Increases the written counter by the specified value
  68      * until it reaches Integer.MAX_VALUE.
  69      */
  70     private void incCount(int value) {
  71         int temp = written + value;
  72         if (temp < 0) {
  73             temp = Integer.MAX_VALUE;
  74         }
  75         written = temp;
  76     }
  77 
  78     /**
  79      * Writes the specified byte (the low eight bits of the argument
  80      * <code>b</code>) to the underlying output stream. If no exception
  81      * is thrown, the counter <code>written</code> is incremented by
  82      * <code>1</code>.
  83      * <p>
  84      * Implements the <code>write</code> method of <code>OutputStream</code>.
  85      *
  86      * @param      b   the <code>byte</code> to be written.
  87      * @exception  IOException  if an I/O error occurs.
  88      * @see        java.io.FilterOutputStream#out
  89      */
  90     public synchronized void write(int b) throws IOException {
  91         out.write(b);
  92         incCount(1);
  93     }
  94 
  95     /**
  96      * Writes <code>len</code> bytes from the specified byte array
  97      * starting at offset <code>off</code> to the underlying output stream.
  98      * If no exception is thrown, the counter <code>written</code> is
  99      * incremented by <code>len</code>.
 100      *
 101      * @param      b     the data.
 102      * @param      off   the start offset in the data.
 103      * @param      len   the number of bytes to write.
 104      * @exception  IOException  if an I/O error occurs.
 105      * @see        java.io.FilterOutputStream#out
 106      */
 107     public synchronized void write(byte b[], int off, int len)
 108         throws IOException
 109     {
 110         out.write(b, off, len);
 111         incCount(len);
 112     }
 113 
 114     /**
 115      * Flushes this data output stream. This forces any buffered output
 116      * bytes to be written out to the stream.
 117      * <p>
 118      * The <code>flush</code> method of <code>DataOutputStream</code>
 119      * calls the <code>flush</code> method of its underlying output stream.
 120      *
 121      * @exception  IOException  if an I/O error occurs.
 122      * @see        java.io.FilterOutputStream#out
 123      * @see        java.io.OutputStream#flush()
 124      */
 125     public void flush() throws IOException {
 126         out.flush();
 127     }
 128 
 129     /**
 130      * Writes a <code>boolean</code> to the underlying output stream as
 131      * a 1-byte value. The value <code>true</code> is written out as the
 132      * value <code>(byte)1</code>; the value <code>false</code> is
 133      * written out as the value <code>(byte)0</code>. If no exception is
 134      * thrown, the counter <code>written</code> is incremented by
 135      * <code>1</code>.
 136      *
 137      * @param      v   a <code>boolean</code> value to be written.
 138      * @exception  IOException  if an I/O error occurs.
 139      * @see        java.io.FilterOutputStream#out
 140      */
 141     public final void writeBoolean(boolean v) throws IOException {
 142         out.write(v ? 1 : 0);
 143         incCount(1);
 144     }
 145 
 146     /**
 147      * Writes out a <code>byte</code> to the underlying output stream as
 148      * a 1-byte value. If no exception is thrown, the counter
 149      * <code>written</code> is incremented by <code>1</code>.
 150      *
 151      * @param      v   a <code>byte</code> value to be written.
 152      * @exception  IOException  if an I/O error occurs.
 153      * @see        java.io.FilterOutputStream#out
 154      */
 155     public final void writeByte(int v) throws IOException {
 156         out.write(v);
 157         incCount(1);
 158     }
 159 
 160     /**
 161      * Writes a <code>short</code> to the underlying output stream as two
 162      * bytes, high byte first. If no exception is thrown, the counter
 163      * <code>written</code> is incremented by <code>2</code>.
 164      *
 165      * @param      v   a <code>short</code> to be written.
 166      * @exception  IOException  if an I/O error occurs.
 167      * @see        java.io.FilterOutputStream#out
 168      */
 169     public final void writeShort(int v) throws IOException {
 170         out.write((v >>> 8) & 0xFF);
 171         out.write((v >>> 0) & 0xFF);
 172         incCount(2);
 173     }
 174 
 175     /**
 176      * Writes a <code>char</code> to the underlying output stream as a
 177      * 2-byte value, high byte first. If no exception is thrown, the
 178      * counter <code>written</code> is incremented by <code>2</code>.
 179      *
 180      * @param      v   a <code>char</code> value to be written.
 181      * @exception  IOException  if an I/O error occurs.
 182      * @see        java.io.FilterOutputStream#out
 183      */
 184     public final void writeChar(int v) throws IOException {
 185         out.write((v >>> 8) & 0xFF);
 186         out.write((v >>> 0) & 0xFF);
 187         incCount(2);
 188     }
 189 
 190     /**
 191      * Writes an <code>int</code> to the underlying output stream as four
 192      * bytes, high byte first. If no exception is thrown, the counter
 193      * <code>written</code> is incremented by <code>4</code>.
 194      *
 195      * @param      v   an <code>int</code> to be written.
 196      * @exception  IOException  if an I/O error occurs.
 197      * @see        java.io.FilterOutputStream#out
 198      */
 199     public final void writeInt(int v) throws IOException {
 200         out.write((v >>> 24) & 0xFF);
 201         out.write((v >>> 16) & 0xFF);
 202         out.write((v >>>  8) & 0xFF);
 203         out.write((v >>>  0) & 0xFF);
 204         incCount(4);
 205     }
 206 
 207     private byte writeBuffer[] = new byte[8];
 208 
 209     /**
 210      * Writes a <code>long</code> to the underlying output stream as eight
 211      * bytes, high byte first. In no exception is thrown, the counter
 212      * <code>written</code> is incremented by <code>8</code>.
 213      *
 214      * @param      v   a <code>long</code> to be written.
 215      * @exception  IOException  if an I/O error occurs.
 216      * @see        java.io.FilterOutputStream#out
 217      */
 218     public final void writeLong(long v) throws IOException {
 219         writeBuffer[0] = (byte)(v >>> 56);
 220         writeBuffer[1] = (byte)(v >>> 48);
 221         writeBuffer[2] = (byte)(v >>> 40);
 222         writeBuffer[3] = (byte)(v >>> 32);
 223         writeBuffer[4] = (byte)(v >>> 24);
 224         writeBuffer[5] = (byte)(v >>> 16);
 225         writeBuffer[6] = (byte)(v >>>  8);
 226         writeBuffer[7] = (byte)(v >>>  0);
 227         out.write(writeBuffer, 0, 8);
 228         incCount(8);
 229     }
 230 
 231     /**
 232      * Converts the float argument to an <code>int</code> using the
 233      * <code>floatToIntBits</code> method in class <code>Float</code>,
 234      * and then writes that <code>int</code> value to the underlying
 235      * output stream as a 4-byte quantity, high byte first. If no
 236      * exception is thrown, the counter <code>written</code> is
 237      * incremented by <code>4</code>.
 238      *
 239      * @param      v   a <code>float</code> value to be written.
 240      * @exception  IOException  if an I/O error occurs.
 241      * @see        java.io.FilterOutputStream#out
 242      * @see        java.lang.Float#floatToIntBits(float)
 243      */
 244     public final void writeFloat(float v) throws IOException {
 245         writeInt(Float.floatToIntBits(v));
 246     }
 247 
 248     /**
 249      * Converts the double argument to a <code>long</code> using the
 250      * <code>doubleToLongBits</code> method in class <code>Double</code>,
 251      * and then writes that <code>long</code> value to the underlying
 252      * output stream as an 8-byte quantity, high byte first. If no
 253      * exception is thrown, the counter <code>written</code> is
 254      * incremented by <code>8</code>.
 255      *
 256      * @param      v   a <code>double</code> value to be written.
 257      * @exception  IOException  if an I/O error occurs.
 258      * @see        java.io.FilterOutputStream#out
 259      * @see        java.lang.Double#doubleToLongBits(double)
 260      */
 261     public final void writeDouble(double v) throws IOException {
 262         writeLong(Double.doubleToLongBits(v));
 263     }
 264 
 265     /**
 266      * Writes out the string to the underlying output stream as a
 267      * sequence of bytes. Each character in the string is written out, in
 268      * sequence, by discarding its high eight bits. If no exception is
 269      * thrown, the counter <code>written</code> is incremented by the
 270      * length of <code>s</code>.
 271      *
 272      * @param      s   a string of bytes to be written.
 273      * @exception  IOException  if an I/O error occurs.
 274      * @see        java.io.FilterOutputStream#out
 275      */
 276     public final void writeBytes(String s) throws IOException {
 277         int len = s.length();
 278         for (int i = 0 ; i < len ; i++) {
 279             out.write((byte)s.charAt(i));
 280         }
 281         incCount(len);
 282     }
 283 
 284     /**
 285      * Writes a string to the underlying output stream as a sequence of
 286      * characters. Each character is written to the data output stream as
 287      * if by the <code>writeChar</code> method. If no exception is
 288      * thrown, the counter <code>written</code> is incremented by twice
 289      * the length of <code>s</code>.
 290      *
 291      * @param      s   a <code>String</code> value to be written.
 292      * @exception  IOException  if an I/O error occurs.
 293      * @see        java.io.DataOutputStream#writeChar(int)
 294      * @see        java.io.FilterOutputStream#out
 295      */
 296     public final void writeChars(String s) throws IOException {
 297         int len = s.length();
 298         for (int i = 0 ; i < len ; i++) {
 299             int v = s.charAt(i);
 300             out.write((v >>> 8) & 0xFF);
 301             out.write((v >>> 0) & 0xFF);
 302         }
 303         incCount(len * 2);
 304     }
 305 
 306     /**
 307      * Writes a string to the underlying output stream using
 308      * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
 309      * encoding in a machine-independent manner.
 310      * <p>
 311      * First, two bytes are written to the output stream as if by the
 312      * <code>writeShort</code> method giving the number of bytes to
 313      * follow. This value is the number of bytes actually written out,
 314      * not the length of the string. Following the length, each character
 315      * of the string is output, in sequence, using the modified UTF-8 encoding
 316      * for the character. If no exception is thrown, the counter
 317      * <code>written</code> is incremented by the total number of
 318      * bytes written to the output stream. This will be at least two
 319      * plus the length of <code>str</code>, and at most two plus
 320      * thrice the length of <code>str</code>.
 321      *
 322      * @param      str   a string to be written.
 323      * @throws     UTFDataFormatException  if the modified UTF-8 encoding of
 324      *             {@code str} would exceed 65535 bytes in length
 325      * @throws     IOException  if some other I/O error occurs.
 326      * @see        #writeChars(String)
 327      */
 328     public final void writeUTF(String str) throws IOException {
 329         writeUTF(str, this);
 330     }
 331 
 332     /**
 333      * Writes a string to the specified DataOutput using
 334      * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
 335      * encoding in a machine-independent manner.
 336      * <p>
 337      * First, two bytes are written to out as if by the <code>writeShort</code>
 338      * method giving the number of bytes to follow. This value is the number of
 339      * bytes actually written out, not the length of the string. Following the
 340      * length, each character of the string is output, in sequence, using the
 341      * modified UTF-8 encoding for the character. If no exception is thrown, the
 342      * counter <code>written</code> is incremented by the total number of
 343      * bytes written to the output stream. This will be at least two
 344      * plus the length of <code>str</code>, and at most two plus
 345      * thrice the length of <code>str</code>.
 346      *
 347      * @param      str   a string to be written.
 348      * @param      out   destination to write to
 349      * @return     The number of bytes written out.
 350      * @throws     UTFDataFormatException  if the modified UTF-8 encoding of
 351      *             {@code str} would exceed 65535 bytes in length
 352      * @throws     IOException  if some other I/O error occurs.
 353      */
 354     static int writeUTF(String str, DataOutput out) throws IOException {
 355         final int strlen = str.length();
 356         int utflen = strlen; // optimized for ASCII
 357 
 358         for (int i = 0; i < strlen; i++) {
 359             int c = str.charAt(i);
 360             if (c >= 0x80 || c == 0)
 361                 utflen += (c >= 0x800) ? 2 : 1;
 362         }
 363 
 364         if (utflen > 65535 || /* overflow */ utflen < strlen)
 365             throw new UTFDataFormatException(tooLongMsg(str, utflen));
 366 
 367         final byte[] bytearr;
 368         if (out instanceof DataOutputStream) {
 369             DataOutputStream dos = (DataOutputStream)out;
 370             if (dos.bytearr == null || (dos.bytearr.length < (utflen + 2)))
 371                 dos.bytearr = new byte[(utflen*2) + 2];
 372             bytearr = dos.bytearr;
 373         } else {
 374             bytearr = new byte[utflen + 2];
 375         }
 376 
 377         int count = 0;
 378         bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
 379         bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);
 380 
 381         int i = 0;
 382         for (i = 0; i < strlen; i++) { // optimized for initial run of ASCII
 383             int c = str.charAt(i);
 384             if (c >= 0x80 || c == 0) break;
 385             bytearr[count++] = (byte) c;
 386         }
 387 
 388         for (; i < strlen; i++) {
 389             int c = str.charAt(i);
 390             if (c < 0x80 && c != 0) {
 391                 bytearr[count++] = (byte) c;
 392             } else if (c >= 0x800) {
 393                 bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
 394                 bytearr[count++] = (byte) (0x80 | ((c >>  6) & 0x3F));
 395                 bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
 396             } else {
 397                 bytearr[count++] = (byte) (0xC0 | ((c >>  6) & 0x1F));
 398                 bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
 399             }
 400         }
 401         out.write(bytearr, 0, utflen + 2);
 402         return utflen + 2;
 403     }
 404 
 405     private static String tooLongMsg(String s, int bits32) {
 406         int slen = s.length();
 407         String head = s.substring(0, 8);
 408         String tail = s.substring(slen - 8, slen);
 409         // handle int overflow with max 3x expansion
 410         long actualLength = (long)slen + Integer.toUnsignedLong(bits32 - slen);
 411         return "encoded string (" + head + "..." + tail + ") too long: "
 412             + actualLength + " bytes";
 413     }
 414 
 415     /**
 416      * Returns the current value of the counter <code>written</code>,
 417      * the number of bytes written to this data output stream so far.
 418      * If the counter overflows, it will be wrapped to Integer.MAX_VALUE.
 419      *
 420      * @return  the value of the <code>written</code> field.
 421      * @see     java.io.DataOutputStream#written
 422      */
 423     public final int size() {
 424         return written;
 425     }
 426 }