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