1 /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. 2 * Copyright 2011 Pierre Ossman for Cendio AB 3 * Copyright (C) 2012-2019 Brian P. Hinz 4 * 5 * This is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This software is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this software; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 18 * USA. 19 */ 20 21 package com.tigervnc.rdr; 22 23 import java.nio.ByteBuffer; 24 import java.nio.channels.SelectionKey; 25 26 import com.tigervnc.network.*; 27 28 public class FdOutStream extends OutStream { 29 30 static final int DEFAULT_BUF_SIZE = 16384; 31 static final int minBulkSize = 1024; 32 FdOutStream(FileDescriptor fd_, boolean blocking_, int timeoutms_, int bufSize_)33 public FdOutStream(FileDescriptor fd_, boolean blocking_, int timeoutms_, int bufSize_) 34 { 35 fd = fd_; blocking = blocking_; timeoutms = timeoutms_; 36 bufSize = ((bufSize_ > 0) ? bufSize_ : DEFAULT_BUF_SIZE); 37 b = new byte[bufSize]; 38 offset = 0; 39 ptr = sentUpTo = start = 0; 40 end = start + bufSize; 41 42 lastWrite = System.currentTimeMillis(); 43 } 44 FdOutStream(FileDescriptor fd_)45 public FdOutStream(FileDescriptor fd_) { this(fd_, true, -1, 0); } 46 setTimeout(int timeoutms_)47 public void setTimeout(int timeoutms_) { 48 timeoutms = timeoutms_; 49 } 50 setBlocking(boolean blocking_)51 public void setBlocking(boolean blocking_) { 52 blocking = blocking_; 53 } 54 length()55 public int length() 56 { 57 return offset + ptr - sentUpTo; 58 } 59 bufferUsage()60 int bufferUsage() 61 { 62 return ptr - sentUpTo; 63 } 64 getIdleTime()65 long getIdleTime() 66 { 67 return System.currentTimeMillis()-lastWrite; 68 } 69 flush()70 public void flush() 71 { 72 while (sentUpTo < ptr) { 73 int n = writeWithTimeout(b, sentUpTo, 74 ptr - sentUpTo, 75 blocking ? timeoutms : 0); 76 77 // Timeout? 78 if (n == 0) { 79 // If non-blocking then we're done here 80 if (!blocking) 81 break; 82 83 throw new TimedOut(); 84 } 85 86 sentUpTo += n; 87 offset += n; 88 } 89 90 // Managed to flush everything? 91 if (sentUpTo == ptr) 92 ptr = sentUpTo = start; 93 } 94 overrun(int itemSize, int nItems)95 protected int overrun(int itemSize, int nItems) 96 { 97 if (itemSize > bufSize) 98 throw new Exception("FdOutStream overrun: max itemSize exceeded"); 99 100 // First try to get rid of the data we have 101 flush(); 102 103 // Still not enough space? 104 if (itemSize > end - ptr) { 105 // Can we shuffle things around? 106 // (don't do this if it gains us less than 25%) 107 if ((sentUpTo - start > bufSize / 4) && 108 (itemSize < bufSize - (ptr - sentUpTo))) { 109 System.arraycopy(b, ptr, b, start, ptr - sentUpTo); 110 ptr = start + (ptr - sentUpTo); 111 sentUpTo = start; 112 } else { 113 // Have to get rid of more data, so turn off non-blocking 114 // for a bit... 115 boolean realBlocking; 116 117 realBlocking = blocking; 118 blocking = true; 119 flush(); 120 blocking = realBlocking; 121 } 122 } 123 124 int nAvail; 125 nAvail = (end - ptr) / itemSize; 126 if (nAvail < nItems) 127 return nAvail; 128 129 return nItems; 130 } 131 writeWithTimeout(byte[] data, int dataPtr, int length, int timeoutms)132 private int writeWithTimeout(byte[] data, int dataPtr, int length, int timeoutms) 133 { 134 int n; 135 136 do { 137 138 Integer tv; 139 if (timeoutms != -1) { 140 tv = new Integer(timeoutms); 141 } else { 142 tv = null; 143 } 144 145 try { 146 n = fd.select(SelectionKey.OP_WRITE, tv); 147 } catch (java.lang.Exception e) { 148 System.out.println(e.toString()); 149 throw new Exception(e.getMessage()); 150 } 151 152 } while (n < 0); 153 154 if (n == 0) return 0; 155 156 try { 157 n = fd.write(ByteBuffer.wrap(data, dataPtr, length), length); 158 } catch (java.lang.Exception e) { 159 throw new Exception(e.getMessage()); 160 } 161 162 lastWrite = System.currentTimeMillis(); 163 164 return n; 165 } 166 getFd()167 public FileDescriptor getFd() { 168 return fd; 169 } 170 setFd(FileDescriptor fd_)171 public void setFd(FileDescriptor fd_) { 172 fd = fd_; 173 } 174 getBufSize()175 public int getBufSize() { 176 return bufSize; 177 } 178 179 protected FileDescriptor fd; 180 protected boolean blocking; 181 protected int timeoutms; 182 protected int start; 183 protected int sentUpTo; 184 protected int offset; 185 protected int bufSize; 186 private long lastWrite; 187 } 188