1 29 30 package org.jruby.util; 31 32 import org.jruby.Ruby; 33 import org.jruby.RubyString; 34 import org.jruby.RubyIO; 35 36 import java.nio.channels.Channel ; 37 import java.nio.channels.FileChannel ; 38 import java.nio.channels.ReadableByteChannel ; 39 import java.nio.channels.SelectableChannel ; 40 import java.nio.channels.WritableByteChannel ; 41 import java.nio.channels.IllegalBlockingModeException ; 42 import java.nio.ByteBuffer ; 43 44 import java.io.IOException ; 45 import java.io.EOFException ; 46 47 public class IOHandlerNio extends IOHandler { 48 private Channel channel; 49 50 private static final int BLOCK_SIZE = 1024 * 16; 51 private ByteBuffer outBuffer; 52 private ByteBuffer inBuffer; 53 private boolean blocking = true; 54 private boolean bufferedIO = false; 55 56 public IOHandlerNio(Ruby runtime, Channel channel) throws IOException { 57 super(runtime); 58 String mode = ""; 59 this.channel = channel; 60 if (channel instanceof ReadableByteChannel ) { 61 mode += "r"; 62 isOpen = true; 63 } 64 if (channel instanceof WritableByteChannel ) { 65 mode += "w"; 66 isOpen = true; 67 } 68 if ("rw".equals(mode)) { 69 modes = new IOModes(runtime, IOModes.RDWR); 70 isOpen = true; 71 } else { 72 if (!isOpen) { 73 mode = "r"; 77 isOpen = true; 78 } 79 modes = new IOModes(runtime, mode); 80 } 81 fileno = RubyIO.getNewFileno(); 82 outBuffer = ByteBuffer.allocate(BLOCK_SIZE); 83 } 84 85 public Channel getChannel() { 86 return channel; 87 } 88 89 public IOHandler cloneIOHandler() throws IOException { 91 return new IOHandlerNio(getRuntime(), channel); 92 } 93 94 private void checkBuffered() throws IOException { 95 if (bufferedIO) { 96 throw new IOException ("Can't mix buffered and unbuffered IO."); 97 } 98 } 99 100 101 102 public void setBlocking(boolean block) throws IOException { 103 if (!(channel instanceof SelectableChannel )) { 104 return; 105 } 106 synchronized (((SelectableChannel ) channel).blockingLock()) { 107 blocking = block; 108 try { 109 ((SelectableChannel ) channel).configureBlocking(block); 110 } catch (IllegalBlockingModeException e) { 111 } 113 } 114 } 115 116 public boolean getBlocking() { 117 return blocking; 118 } 119 120 public ByteList sysread(int length) throws EOFException , BadDescriptorException, IOException { 121 checkReadable(); 122 checkBuffered(); 123 124 ByteBuffer buffer = ByteBuffer.allocate(length); 125 int bytes_read = 0; 126 bytes_read = ((ReadableByteChannel ) channel).read(buffer); 127 if (bytes_read < 0) { 128 throw new EOFException (); 129 } 130 131 byte[] ret; 132 if (buffer.hasRemaining()) { 133 buffer.flip(); 134 ret = new byte[buffer.remaining()]; 135 buffer.get(ret); 136 } else { 137 ret = buffer.array(); 138 } 139 return new ByteList(ret,false); 140 } 141 142 public int syswrite(ByteList string) throws BadDescriptorException, IOException { 143 checkWritable(); 144 outBuffer.flip(); 145 flushOutBuffer(); 146 147 ByteBuffer buffer = ByteBuffer.wrap(string.bytes,0,string.realSize); 148 while (buffer.hasRemaining()) { 149 if (((WritableByteChannel ) channel).write(buffer) < 0) { 150 throw new IOException ("write returned less than zero"); 152 } 153 } 154 return buffer.capacity(); 155 } 156 157 public int syswrite(int c) throws BadDescriptorException, IOException { 158 ByteBuffer buffer = ByteBuffer.allocate(1); buffer.put((byte)c); 160 161 return syswrite(new ByteList(buffer.array(),false)); 162 } 163 164 public ByteList recv(int length) throws EOFException , BadDescriptorException, IOException { 165 return sysread(length); 166 } 167 168 169 private void setupBufferedIO() { 170 if (bufferedIO) { 171 return; 172 } 173 inBuffer = ByteBuffer.allocate(BLOCK_SIZE); 174 flushInBuffer(); 175 bufferedIO = true; 176 } 177 178 int ungotc = -1; 179 180 private ByteList consumeInBuffer(int length) { 181 int offset = 0; 182 if (ungotc > 0) { 183 length--; 184 offset = 1; 185 } 186 length = inBuffer.remaining() < length ? inBuffer.remaining() : length; 187 byte[] ret = new byte[length + offset]; 188 inBuffer.get(ret, offset, length); 189 if (ungotc > 0) { 190 ret[0] = (byte) (ungotc & 0xff); 191 ungotc = -1; 192 } 193 return new ByteList(ret,false); 194 } 195 196 private int fillInBuffer() throws IOException { 197 inBuffer.clear(); 198 int i = ((ReadableByteChannel ) channel).read(inBuffer); 199 inBuffer.flip(); 200 return i; 201 } 202 203 private void flushInBuffer() { 204 inBuffer.position(0); 205 inBuffer.limit(0); 206 ungotc = -1; 207 } 208 209 private void flushOutBuffer() throws IOException { 210 while (outBuffer.hasRemaining()) { 211 if (((WritableByteChannel ) channel).write(outBuffer) < 0) { 212 throw new IOException ("write returned less than zero"); 213 } 214 } 215 outBuffer.clear(); 216 } 217 218 231 232 private int buffer_index(ByteBuffer haystack, ByteList needle) { 233 search_loop: 234 for (int i = haystack.position(); i + (needle.realSize - 1) < haystack.limit(); i++) { 235 for (int j = 0; j < needle.realSize; j++) { 236 if (haystack.get(i + j) != needle.bytes[j]) { 237 continue search_loop; 238 } 239 } 240 return i; 241 } 242 return -1; 243 } 244 245 public ByteList readpartial(int length) throws IOException , BadDescriptorException, EOFException { 246 checkReadable(); 247 setupBufferedIO(); 248 249 if (!inBuffer.hasRemaining()) { 250 if (fillInBuffer() < 0) { 251 throw new EOFException (); 252 } 253 } 254 return consumeInBuffer(length); 255 } 256 257 public ByteList read(int length) throws IOException , BadDescriptorException, EOFException { 258 setupBufferedIO(); 259 checkReadable(); 260 261 boolean eof = false; 262 int remaining = length; 263 ByteList ret = new ByteList(length); 264 ret.append(consumeInBuffer(remaining)); 265 remaining -= ret.length(); 266 while (remaining > 0) { 267 int i = fillInBuffer(); 268 if (i < 0) { 269 eof = true; 270 break; 271 } 272 ret.append(consumeInBuffer(remaining)); 273 remaining -= i; 274 } 275 if (eof && ret.length() == 0) { 276 throw new EOFException (); 277 } 278 return ret; 279 } 280 281 public int write(ByteList string) throws IOException , BadDescriptorException { 282 checkWritable(); 283 284 ByteBuffer buffer = ByteBuffer.wrap(string.bytes,0,string.realSize); 285 do { 286 287 while (buffer.hasRemaining() && outBuffer.hasRemaining()) { 288 outBuffer.put(buffer.get()); 289 } 290 291 outBuffer.flip(); 292 if ((buffer.hasRemaining() && !outBuffer.hasRemaining()) || isSync()) { 293 flushOutBuffer(); 294 } 295 } while (buffer.hasRemaining()); 296 297 if(!isSync()) { 298 flushOutBuffer(); 299 } 300 return buffer.capacity(); 301 } 302 303 public ByteList gets(ByteList separator) throws IOException , BadDescriptorException, EOFException { 304 setupBufferedIO(); 305 checkReadable(); 306 307 ByteList ret = new ByteList(); 308 boolean eof = false; 309 ByteList trigger; 310 if (separator != null) { 311 trigger = separator; 312 } else { 313 trigger = ((RubyString) getRuntime().getGlobalVariables().get("$/")).getByteList(); 314 } 315 316 int idx; 317 while ((idx = buffer_index(inBuffer, trigger)) < 0 && !eof) { 318 ret.append(consumeInBuffer(BLOCK_SIZE)); 319 if (fillInBuffer() < 0) { 320 eof = true; 321 } 322 } 323 if (eof && !inBuffer.hasRemaining() && ret.length() == 0) { 324 throw new EOFException (); 325 } 326 if (idx >= 0) { 327 ret.append(consumeInBuffer((idx + trigger.realSize) - inBuffer.position())); 328 } else if (eof) { 329 ret.append(consumeInBuffer(BLOCK_SIZE)); 330 } 331 return ret; 332 } 333 334 public ByteList getsEntireStream() throws IOException , BadDescriptorException, EOFException { 335 checkReadable(); 336 setupBufferedIO(); 337 ByteList ret = new ByteList(); 338 boolean eof; 339 340 while (true) { 341 ret.append(consumeInBuffer(BLOCK_SIZE)); 342 if (fillInBuffer() < 0) { 343 eof = true; 344 break; 345 } 346 } 347 if (eof && ret.length() == 0) { 348 throw new EOFException (); 349 } 350 return ret; 351 } 352 353 public int getc() throws IOException , BadDescriptorException, EOFException { 354 checkReadable(); 355 setupBufferedIO(); 356 357 if (ungotc > 0) { 358 int i = ungotc; 359 ungotc = -1; 360 return i; 361 } 362 363 if (!inBuffer.hasRemaining()) { 364 if (fillInBuffer() < 0) { 365 throw new EOFException (); 366 } 367 } 368 return (int) (inBuffer.get() & 0xff); 369 } 370 371 public void ungetc(int c) { 372 setupBufferedIO(); 373 ungotc = c; 374 } 375 376 public void putc(int c) throws IOException , BadDescriptorException { 377 checkWritable(); 378 379 if (!outBuffer.hasRemaining()) { 380 outBuffer.flip(); 381 flushOutBuffer(); 382 } 383 384 outBuffer.put((byte) (c & 0xff)); 385 } 386 387 public void flush() throws IOException , BadDescriptorException { 388 checkWritable(); 389 outBuffer.flip(); 390 flushOutBuffer(); 391 } 392 393 395 public void sync() throws IOException , BadDescriptorException { 396 flush(); 397 } 398 399 public boolean isEOF() throws IOException , BadDescriptorException { 400 setupBufferedIO(); 401 checkReadable(); 402 403 if (ungotc > 0) { 404 return false; 405 } 406 if (inBuffer.hasRemaining()) { 407 return false; 408 } 409 if (fillInBuffer() < 0) { 410 return true; 411 } 412 return false; 413 } 414 415 416 public void close() throws IOException { 417 418 if (outBuffer.position() > 0) { 419 outBuffer.flip(); 420 flushOutBuffer(); 421 } 422 423 channel.close(); 424 } 425 426 public long pos() throws PipeException, IOException { 427 if (channel instanceof FileChannel ) { 428 if (bufferedIO) { 429 return ((FileChannel ) channel).position() - (inBuffer.remaining() + (ungotc > 0 ? 1 : 0)); 430 } else { 431 return ((FileChannel ) channel).position(); 432 } 433 } else { 434 throw new IOHandler.PipeException(); 435 } 436 } 437 438 public void seek(long offset, int type) throws IOException , InvalidValueException, PipeException { 439 checkOpen(); 440 if (channel instanceof FileChannel ) { 441 if (bufferedIO) { 442 flushInBuffer(); 443 } 444 try { 445 switch (type) { 446 case SEEK_SET: 447 ((FileChannel ) channel).position(offset); 448 break; 449 case SEEK_CUR: 450 ((FileChannel ) channel).position(((FileChannel ) channel).position() + offset); 451 break; 452 case SEEK_END: 453 ((FileChannel ) channel).position(((FileChannel ) channel).size() + offset); 454 break; 455 } 456 } catch (IllegalArgumentException e) { 457 throw new InvalidValueException(); 458 } 459 } else { 460 throw new IOHandler.PipeException(); 461 } 462 } 463 464 public void resetByModes(IOModes newModes) throws IOException , InvalidValueException { 465 if (channel instanceof FileChannel ) { 466 if (newModes.isAppendable()) { 467 try { 468 seek(0L, SEEK_END); 469 } catch(PipeException e) { 470 } catch(InvalidValueException e) {} } else if (newModes.isWritable()) { 472 try { 473 rewind(); 474 } catch(PipeException e) {} } 476 } 477 } 478 479 public void rewind() throws IOException , PipeException { 480 checkOpen(); 481 checkBuffered(); 482 if (channel instanceof FileChannel ) { 483 try { 484 seek(0, SEEK_SET); 485 } catch(InvalidValueException e) {} } else { 487 throw new IOHandler.PipeException(); 488 } 489 } 490 491 public void truncate(long length) throws IOException , PipeException { 492 if (channel instanceof FileChannel ) { 493 ((FileChannel ) channel).truncate(length); 494 } else { 495 throw new IOHandler.PipeException(); 496 } 497 } 498 499 500 public int pid() { 501 return -1; 502 } 503 504 public FileChannel getFileChannel() { 505 return null; 507 } 508 } 509 | Popular Tags |