1 /*
   2  * Copyright (c) 2015, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.salver.writer;
  24 
  25 import java.io.IOException;
  26 import java.nio.ByteBuffer;
  27 import java.nio.CharBuffer;
  28 import java.nio.ReadOnlyBufferException;
  29 import java.nio.channels.WritableByteChannel;
  30 import java.nio.charset.CharsetEncoder;
  31 import java.nio.charset.CoderResult;
  32 import java.nio.charset.StandardCharsets;
  33 
  34 public class ChannelDumpWriter implements DumpWriter {
  35 
  36     private static final int BUFFER_CAPACITY = 256 * 1024;
  37 
  38     protected final WritableByteChannel channel;
  39     protected final ByteBuffer buffer;
  40 
  41     public ChannelDumpWriter(WritableByteChannel channel) {
  42         this(channel, ByteBuffer.allocateDirect(BUFFER_CAPACITY));
  43     }
  44 
  45     public ChannelDumpWriter(WritableByteChannel channel, ByteBuffer buffer) {
  46         this.channel = channel;
  47         this.buffer = buffer;
  48     }
  49 
  50     private void ensureAvailable(int len) throws IOException {
  51         if (buffer.isReadOnly()) {
  52             throw new ReadOnlyBufferException();
  53         }
  54         if (buffer.capacity() < len) {
  55             throw new IllegalArgumentException();
  56         }
  57         while (buffer.remaining() < len) {
  58             flush();
  59         }
  60     }
  61 
  62     @Override
  63     public ChannelDumpWriter write(byte b) throws IOException {
  64         ensureAvailable(1);
  65         buffer.put(b);
  66         return this;
  67     }
  68 
  69     @Override
  70     public ChannelDumpWriter write(byte[] arr) throws IOException {
  71         if (buffer.isReadOnly()) {
  72             throw new ReadOnlyBufferException();
  73         }
  74         int offset = 0;
  75         while (offset < arr.length) {
  76             int available = buffer.remaining();
  77             int length = Math.min(available, arr.length - offset);
  78             buffer.put(arr, offset, length);
  79             if (!buffer.hasRemaining()) {
  80                 flush();
  81             }
  82             offset += length;
  83         }
  84         return this;
  85     }
  86 
  87     @Override
  88     public ChannelDumpWriter write(ByteBuffer buf) throws IOException {
  89         if (buf == buffer) {
  90             throw new IllegalArgumentException();
  91         }
  92         if (buffer.isReadOnly()) {
  93             throw new ReadOnlyBufferException();
  94         }
  95         while (buf.hasRemaining()) {
  96             int available = buffer.remaining();
  97             int remaining = buf.remaining();
  98             for (int i = 0, n = Math.min(available, remaining); i < n; i++) {
  99                 buffer.put(buf.get());
 100             }
 101             if (!buffer.hasRemaining()) {
 102                 flush();
 103             }
 104         }
 105         return this;
 106     }
 107 
 108     @Override
 109     public ChannelDumpWriter write(CharSequence csq) throws IOException {
 110         if (buffer.isReadOnly()) {
 111             throw new ReadOnlyBufferException();
 112         }
 113         CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
 114         CharBuffer buf = CharBuffer.wrap(csq);
 115         while (true) {
 116             CoderResult result = encoder.encode(buf, buffer, true);
 117             if (result.isError()) {
 118                 throw new IOException(result.toString());
 119             }
 120             if (!buffer.hasRemaining()) {
 121                 flush();
 122             }
 123             if (result.isOverflow()) {
 124                 continue;
 125             }
 126             break;
 127         }
 128         return this;
 129     }
 130 
 131     @Override
 132     public ChannelDumpWriter writeChar(char v) throws IOException {
 133         ensureAvailable(1 << 1);
 134         buffer.putChar(v);
 135         return this;
 136     }
 137 
 138     @Override
 139     public ChannelDumpWriter writeShort(short v) throws IOException {
 140         ensureAvailable(1 << 1);
 141         buffer.putShort(v);
 142         return this;
 143     }
 144 
 145     @Override
 146     public ChannelDumpWriter writeInt(int v) throws IOException {
 147         ensureAvailable(1 << 2);
 148         buffer.putInt(v);
 149         return this;
 150     }
 151 
 152     @Override
 153     public ChannelDumpWriter writeLong(long v) throws IOException {
 154         ensureAvailable(1 << 3);
 155         buffer.putLong(v);
 156         return this;
 157     }
 158 
 159     @Override
 160     public ChannelDumpWriter writeFloat(float v) throws IOException {
 161         ensureAvailable(1 << 2);
 162         buffer.putFloat(v);
 163         return this;
 164     }
 165 
 166     @Override
 167     public ChannelDumpWriter writeDouble(double v) throws IOException {
 168         ensureAvailable(1 << 3);
 169         buffer.putDouble(v);
 170         return this;
 171     }
 172 
 173     @Override
 174     public void flush() throws IOException {
 175         if (buffer != null && channel != null) {
 176             buffer.flip();
 177             channel.write(buffer);
 178             buffer.compact();
 179         }
 180     }
 181 
 182     @Override
 183     public void close() throws IOException {
 184         if (channel != null) {
 185             try {
 186                 flush();
 187             } finally {
 188                 channel.close();
 189             }
 190         }
 191     }
 192 }