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