- android/ 167个模块 (app/content/os/view/widget/graphics/media/hardware/net/telephony/provider等) - java/ 35个模块 (lang/io/util/net/nio/security/crypto/sql/beans/rmi/awt等) - javax/ 4个模块 (net/crypto/security/sql) - org/ 5个模块 (json/w3c_dom/xml_sax/xml_pull/apache_http) - dalvik/ 1个模块 (system) - ~3053个类/接口,方法签名完全对齐Android SDK - 桌面跨平台运行 (Windows/macOS/Linux) - 修复所有导入错误和Python关键字冲突
1103 lines
32 KiB
Python
1103 lines
32 KiB
Python
"""
|
|
Java java.nio.channels APIs implemented in Python.
|
|
java.nio.channels package - Channel, Selector, SocketChannel, ServerSocketChannel, FileChannel, etc.
|
|
Desktop stub wrapping Python sockets.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from .._core import Stub, AndroidOnlyError
|
|
import socket as _socket
|
|
import select as _select
|
|
import threading as _threading
|
|
import os as _os
|
|
from typing import List, Optional, Set
|
|
|
|
__all__ = [
|
|
"Channel", "ReadableByteChannel", "WritableByteChannel", "ByteChannel",
|
|
"ScatteringByteChannel", "GatheringByteChannel", "InterruptibleChannel",
|
|
"SelectableChannel", "SelectionKey", "Selector",
|
|
"ServerSocketChannel", "SocketChannel", "DatagramChannel",
|
|
"Pipe", "FileChannel", "FileChannelMapMode",
|
|
"AsynchronousChannelGroup", "AsynchronousServerSocketChannel",
|
|
"AsynchronousSocketChannel", "AsynchronousFileChannel",
|
|
"CompletionHandler", "AsynchronousChannel", "AsynchronousByteChannel",
|
|
]
|
|
|
|
|
|
# ============================================================
|
|
# Channel
|
|
# ============================================================
|
|
|
|
class Channel(Stub):
|
|
"""
|
|
java.nio.channels.Channel
|
|
A nexus for I/O operations.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._open = True
|
|
|
|
def isOpen(self) -> bool:
|
|
return self._open
|
|
|
|
def close(self):
|
|
self._open = False
|
|
|
|
def toString(self) -> str:
|
|
return f"{self.__class__.__name__}@{hex(id(self))}[open={self._open}]"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self) & 0x7FFFFFFF
|
|
|
|
|
|
class ReadableByteChannel(Channel):
|
|
"""A channel that can read bytes."""
|
|
def read(self, dst) -> int:
|
|
raise NotImplementedError
|
|
|
|
|
|
class WritableByteChannel(Channel):
|
|
"""A channel that can write bytes."""
|
|
def write(self, src) -> int:
|
|
raise NotImplementedError
|
|
|
|
|
|
class ByteChannel(ReadableByteChannel, WritableByteChannel):
|
|
"""A channel that can both read and write bytes."""
|
|
pass
|
|
|
|
|
|
class ScatteringByteChannel(ReadableByteChannel):
|
|
"""A channel that can read bytes into a sequence of buffers."""
|
|
def read(self, dsts, offset=None, length=None) -> int:
|
|
total = 0
|
|
buffers = dsts[offset:offset + length] if offset is not None else dsts
|
|
for buf in buffers:
|
|
total += self.read(buf)
|
|
return total
|
|
|
|
|
|
class GatheringByteChannel(WritableByteChannel):
|
|
"""A channel that can write bytes from a sequence of buffers."""
|
|
def write(self, srcs, offset=None, length=None) -> int:
|
|
total = 0
|
|
buffers = srcs[offset:offset + length] if offset is not None else srcs
|
|
for buf in buffers:
|
|
total += self.write(buf)
|
|
return total
|
|
|
|
|
|
class InterruptibleChannel(Channel):
|
|
"""A channel that can be asynchronously closed and interrupted."""
|
|
pass
|
|
|
|
|
|
# ============================================================
|
|
# SelectionKey
|
|
# ============================================================
|
|
|
|
class SelectionKey(Stub):
|
|
"""
|
|
java.nio.channels.SelectionKey
|
|
A token representing the registration of a SelectableChannel with a Selector.
|
|
"""
|
|
OP_ACCEPT: int = 1 << 4 # 16
|
|
OP_CONNECT: int = 1 << 3 # 8
|
|
OP_READ: int = 1 << 0 # 1
|
|
OP_WRITE: int = 1 << 2 # 4
|
|
|
|
def __init__(self, channel: "SelectableChannel", selector: "Selector",
|
|
interest_ops: int = 0):
|
|
super().__init__()
|
|
self._channel = channel
|
|
self._selector = selector
|
|
self._interest_ops = interest_ops
|
|
self._ready_ops = 0
|
|
self._valid = True
|
|
self._attachment = None
|
|
|
|
def channel(self) -> "SelectableChannel":
|
|
return self._channel
|
|
|
|
def selector(self) -> "Selector":
|
|
return self._selector
|
|
|
|
def interestOps(self, ops: int = None):
|
|
if ops is not None:
|
|
self._interest_ops = ops
|
|
return self
|
|
return self._interest_ops
|
|
|
|
def readyOps(self) -> int:
|
|
return self._ready_ops
|
|
|
|
def isValid(self) -> bool:
|
|
return self._valid
|
|
|
|
def isReadable(self) -> bool:
|
|
return (self._ready_ops & SelectionKey.OP_READ) != 0
|
|
|
|
def isWritable(self) -> bool:
|
|
return (self._ready_ops & SelectionKey.OP_WRITE) != 0
|
|
|
|
def isConnectable(self) -> bool:
|
|
return (self._ready_ops & SelectionKey.OP_CONNECT) != 0
|
|
|
|
def isAcceptable(self) -> bool:
|
|
return (self._ready_ops & SelectionKey.OP_ACCEPT) != 0
|
|
|
|
def cancel(self):
|
|
self._valid = False
|
|
|
|
def attach(self, ob: Any) -> Any:
|
|
old = self._attachment
|
|
self._attachment = ob
|
|
return old
|
|
|
|
def attachment(self) -> Any:
|
|
return self._attachment
|
|
|
|
def toString(self) -> str:
|
|
return (f"SelectionKey@{hex(id(self))}[interest={self._interest_ops}, "
|
|
f"ready={self._ready_ops}, valid={self._valid}]")
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(id(self)) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# SelectableChannel
|
|
# ============================================================
|
|
|
|
class SelectableChannel(Channel):
|
|
"""
|
|
java.nio.channels.SelectableChannel
|
|
A channel that can be multiplexed via a Selector.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._blocking = True
|
|
self._lock = _threading.Lock()
|
|
self._keys = {}
|
|
|
|
def register(self, sel: "Selector", ops: int, att: Any = None) -> SelectionKey:
|
|
key = SelectionKey(self, sel, ops)
|
|
if att is not None:
|
|
key.attach(att)
|
|
self._keys[sel] = key
|
|
sel._register_key(key)
|
|
return key
|
|
|
|
def configureBlocking(self, block: bool) -> "SelectableChannel":
|
|
self._blocking = block
|
|
return self
|
|
|
|
def isBlocking(self) -> bool:
|
|
return self._blocking
|
|
|
|
def blockingLock(self) -> Any:
|
|
return self._lock
|
|
|
|
def keyFor(self, sel: "Selector") -> Optional[SelectionKey]:
|
|
return self._keys.get(sel)
|
|
|
|
def provider(self):
|
|
return None
|
|
|
|
def validOps(self) -> int:
|
|
return 0
|
|
|
|
|
|
# ============================================================
|
|
# Selector
|
|
# ============================================================
|
|
|
|
class Selector(Stub):
|
|
"""
|
|
java.nio.channels.Selector
|
|
A selector for selectable channels.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._open = True
|
|
self._keys = {}
|
|
self._selected_keys = set()
|
|
|
|
def _register_key(self, key: SelectionKey):
|
|
self._keys[key.channel()] = key
|
|
|
|
@staticmethod
|
|
def open() -> "Selector":
|
|
return Selector()
|
|
|
|
def isOpen(self) -> bool:
|
|
return self._open
|
|
|
|
def provider(self):
|
|
return None
|
|
|
|
def keys(self) -> Set[SelectionKey]:
|
|
return set(self._keys.values())
|
|
|
|
def selectedKeys(self) -> Set[SelectionKey]:
|
|
return self._selected_keys
|
|
|
|
def selectNow(self) -> int:
|
|
"""Non-blocking select. Returns number of ready channels."""
|
|
return self._do_select(0)
|
|
|
|
def select(self, timeout: int = 0) -> int:
|
|
"""Blocking select with optional timeout (ms)."""
|
|
return self._do_select(timeout)
|
|
|
|
def _do_select(self, timeout_ms: int) -> int:
|
|
"""Internal select implementation using Python select module."""
|
|
read_fds = []
|
|
write_fds = []
|
|
for ch, key in self._keys.items():
|
|
ops = key.interestOps()
|
|
if hasattr(ch, '_socket') and ch._socket:
|
|
fd = ch._socket
|
|
if ops & SelectionKey.OP_READ:
|
|
read_fds.append(fd)
|
|
if ops & SelectionKey.OP_WRITE:
|
|
write_fds.append(fd)
|
|
|
|
timeout_s = timeout_ms / 1000.0 if timeout_ms > 0 else None
|
|
ready_count = 0
|
|
|
|
try:
|
|
r_ready, w_ready, _ = _select.select(read_fds, write_fds, [], timeout_s if timeout_s else 0)
|
|
except (ValueError, _socket.error):
|
|
return 0
|
|
|
|
self._selected_keys.clear()
|
|
for fd in r_ready:
|
|
for ch, key in self._keys.items():
|
|
if hasattr(ch, '_socket') and ch._socket is fd:
|
|
key._ready_ops |= SelectionKey.OP_READ
|
|
self._selected_keys.add(key)
|
|
ready_count += 1
|
|
for fd in w_ready:
|
|
for ch, key in self._keys.items():
|
|
if hasattr(ch, '_socket') and ch._socket is fd:
|
|
key._ready_ops |= SelectionKey.OP_WRITE
|
|
self._selected_keys.add(key)
|
|
ready_count += 1
|
|
|
|
return ready_count
|
|
|
|
def wakeup(self):
|
|
"""Wakes up a selector that is blocked on a select operation."""
|
|
pass
|
|
|
|
def close(self):
|
|
self._open = False
|
|
self._keys.clear()
|
|
self._selected_keys.clear()
|
|
|
|
def toString(self) -> str:
|
|
return f"Selector@{hex(id(self))}[open={self._open}]"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# ServerSocketChannel
|
|
# ============================================================
|
|
|
|
class ServerSocketChannel(SelectableChannel):
|
|
"""
|
|
java.nio.channels.ServerSocketChannel
|
|
A selectable channel for stream-oriented listening sockets.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._socket = None
|
|
self._server_socket = None
|
|
self._local_address = None
|
|
|
|
@staticmethod
|
|
def open() -> "ServerSocketChannel":
|
|
return ServerSocketChannel()
|
|
|
|
def bind(self, local: Any, backlog: int = 50):
|
|
"""Binds the channel's socket to a local address."""
|
|
if self._socket is None:
|
|
self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM)
|
|
self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1)
|
|
if hasattr(local, 'getPort') and hasattr(local, 'getHostName'):
|
|
addr = (local.getHostName(), local.getPort())
|
|
elif isinstance(local, tuple):
|
|
addr = local
|
|
else:
|
|
addr = ('0.0.0.0', 0)
|
|
self._socket.bind(addr)
|
|
self._socket.listen(backlog)
|
|
self._local_address = self._socket.getsockname()
|
|
return self
|
|
|
|
def accept(self) -> Optional["SocketChannel"]:
|
|
"""Accepts a connection."""
|
|
if self._socket is None:
|
|
return None
|
|
try:
|
|
conn, addr = self._socket.accept()
|
|
sc = SocketChannel()
|
|
sc._socket = conn
|
|
sc._connected = True
|
|
sc._remote_address = addr
|
|
return sc
|
|
except (BlockingIOError, _socket.error):
|
|
return None
|
|
|
|
def socket(self):
|
|
"""Returns the underlying ServerSocket."""
|
|
return self._server_socket
|
|
|
|
def getLocalAddress(self):
|
|
"""Returns the local address of this channel."""
|
|
return self._local_address
|
|
|
|
def setOption(self, name, value):
|
|
if self._socket:
|
|
self._socket.setsockopt(_socket.SOL_SOCKET, name, value)
|
|
return self
|
|
|
|
def getOption(self, name):
|
|
if self._socket:
|
|
return self._socket.getsockopt(_socket.SOL_SOCKET, name)
|
|
return None
|
|
|
|
def supportsOption(self, name) -> bool:
|
|
return True
|
|
|
|
def validOps(self) -> int:
|
|
return SelectionKey.OP_ACCEPT
|
|
|
|
def close(self):
|
|
super().close()
|
|
if self._socket:
|
|
self._socket.close()
|
|
|
|
def toString(self) -> str:
|
|
return f"ServerSocketChannel@{hex(id(self))}[local={self._local_address}]"
|
|
|
|
|
|
# ============================================================
|
|
# SocketChannel
|
|
# ============================================================
|
|
|
|
class SocketChannel(SelectableChannel):
|
|
"""
|
|
java.nio.channels.SocketChannel
|
|
A selectable channel for stream-oriented connecting sockets.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._socket = None
|
|
self._connected = False
|
|
self._connection_pending = False
|
|
self._remote_address = None
|
|
self._local_address = None
|
|
self._input_shutdown = False
|
|
self._output_shutdown = False
|
|
|
|
@staticmethod
|
|
def open() -> "SocketChannel":
|
|
return SocketChannel()
|
|
|
|
def connect(self, remote: Any) -> bool:
|
|
"""Connects this channel's socket to the remote address."""
|
|
if self._socket is None:
|
|
self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM)
|
|
if hasattr(remote, 'getHostName') and hasattr(remote, 'getPort'):
|
|
addr = (remote.getHostName(), remote.getPort())
|
|
elif isinstance(remote, tuple):
|
|
addr = remote
|
|
else:
|
|
addr = (str(remote), 0)
|
|
try:
|
|
self._socket.connect(addr)
|
|
self._connected = True
|
|
self._remote_address = addr
|
|
self._local_address = self._socket.getsockname()
|
|
return True
|
|
except _socket.error:
|
|
self._connection_pending = True
|
|
return False
|
|
|
|
def finishConnect(self) -> bool:
|
|
"""Finishes the connection process."""
|
|
self._connected = True
|
|
self._connection_pending = False
|
|
return True
|
|
|
|
def isConnected(self) -> bool:
|
|
return self._connected
|
|
|
|
def isConnectionPending(self) -> bool:
|
|
return self._connection_pending
|
|
|
|
def read(self, dst) -> int:
|
|
"""Reads bytes from this channel into the given buffer."""
|
|
if not self._connected or self._socket is None:
|
|
return -1
|
|
try:
|
|
data = self._socket.recv(4096)
|
|
if not data:
|
|
return -1
|
|
from ..nio import ByteBuffer
|
|
if isinstance(dst, ByteBuffer):
|
|
for b in data:
|
|
dst.put(b)
|
|
return len(data)
|
|
except (_socket.error, BlockingIOError):
|
|
return 0
|
|
|
|
def write(self, src) -> int:
|
|
"""Writes bytes to this channel from the given buffer."""
|
|
if not self._connected or self._socket is None:
|
|
return 0
|
|
from ..nio import ByteBuffer
|
|
if isinstance(src, ByteBuffer):
|
|
data = bytearray()
|
|
for i in range(src.position(), src.limit()):
|
|
data.append(src.get(i))
|
|
try:
|
|
return self._socket.send(bytes(data))
|
|
except _socket.error:
|
|
return 0
|
|
|
|
def socket(self):
|
|
return self._socket
|
|
|
|
def getRemoteAddress(self):
|
|
return self._remote_address
|
|
|
|
def getLocalAddress(self):
|
|
return self._local_address
|
|
|
|
def shutdownInput(self):
|
|
self._input_shutdown = True
|
|
if self._socket:
|
|
try:
|
|
self._socket.shutdown(_socket.SHUT_RD)
|
|
except _socket.error:
|
|
pass
|
|
|
|
def shutdownOutput(self):
|
|
self._output_shutdown = True
|
|
if self._socket:
|
|
try:
|
|
self._socket.shutdown(_socket.SHUT_WR)
|
|
except _socket.error:
|
|
pass
|
|
|
|
def setOption(self, name, value):
|
|
if self._socket:
|
|
self._socket.setsockopt(_socket.SOL_SOCKET, name, value)
|
|
return self
|
|
|
|
def getOption(self, name):
|
|
if self._socket:
|
|
return self._socket.getsockopt(_socket.SOL_SOCKET, name)
|
|
return None
|
|
|
|
def supportsOption(self, name) -> bool:
|
|
return True
|
|
|
|
def validOps(self) -> int:
|
|
return SelectionKey.OP_READ | SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT
|
|
|
|
def close(self):
|
|
super().close()
|
|
self._connected = False
|
|
if self._socket:
|
|
self._socket.close()
|
|
|
|
def toString(self) -> str:
|
|
return f"SocketChannel@{hex(id(self))}[connected={self._connected}]"
|
|
|
|
|
|
# ============================================================
|
|
# DatagramChannel
|
|
# ============================================================
|
|
|
|
class DatagramChannel(SelectableChannel):
|
|
"""
|
|
java.nio.channels.DatagramChannel
|
|
A selectable channel for datagram-oriented sockets.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._socket = None
|
|
self._connected = False
|
|
self._remote_address = None
|
|
self._local_address = None
|
|
|
|
@staticmethod
|
|
def open() -> "DatagramChannel":
|
|
return DatagramChannel()
|
|
|
|
def bind(self, local: Any):
|
|
if self._socket is None:
|
|
self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_DGRAM)
|
|
if hasattr(local, 'getPort'):
|
|
addr = ('0.0.0.0', local.getPort())
|
|
elif isinstance(local, tuple):
|
|
addr = local
|
|
else:
|
|
addr = ('0.0.0.0', 0)
|
|
self._socket.bind(addr)
|
|
self._local_address = self._socket.getsockname()
|
|
return self
|
|
|
|
def connect(self, remote: Any):
|
|
if self._socket is None:
|
|
self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_DGRAM)
|
|
if hasattr(remote, 'getHostName') and hasattr(remote, 'getPort'):
|
|
addr = (remote.getHostName(), remote.getPort())
|
|
elif isinstance(remote, tuple):
|
|
addr = remote
|
|
else:
|
|
addr = (str(remote), 0)
|
|
self._socket.connect(addr)
|
|
self._connected = True
|
|
self._remote_address = addr
|
|
|
|
def disconnect(self):
|
|
self._connected = False
|
|
self._remote_address = None
|
|
|
|
def isConnected(self) -> bool:
|
|
return self._connected
|
|
|
|
def receive(self, dst) -> Any:
|
|
"""Receives a datagram."""
|
|
if self._socket is None:
|
|
return None
|
|
try:
|
|
data, addr = self._socket.recvfrom(65535)
|
|
from ..nio import ByteBuffer
|
|
if isinstance(dst, ByteBuffer):
|
|
for b in data:
|
|
dst.put(b)
|
|
return addr
|
|
except (_socket.error, BlockingIOError):
|
|
return None
|
|
|
|
def send(self, src, target) -> int:
|
|
"""Sends a datagram."""
|
|
if self._socket is None:
|
|
return 0
|
|
from ..nio import ByteBuffer
|
|
if isinstance(src, ByteBuffer):
|
|
data = bytearray()
|
|
for i in range(src.position(), src.limit()):
|
|
data.append(src.get(i))
|
|
if hasattr(target, 'getHostName') and hasattr(target, 'getPort'):
|
|
addr = (target.getHostName(), target.getPort())
|
|
else:
|
|
addr = target
|
|
try:
|
|
return self._socket.sendto(bytes(data), addr)
|
|
except _socket.error:
|
|
return 0
|
|
return 0
|
|
|
|
def read(self, dst) -> int:
|
|
"""Reads a datagram (connected mode)."""
|
|
return self.receive(dst)
|
|
|
|
def write(self, src) -> int:
|
|
"""Writes a datagram (connected mode)."""
|
|
return self.send(src, self._remote_address)
|
|
|
|
def socket(self):
|
|
return self._socket
|
|
|
|
def getRemoteAddress(self):
|
|
return self._remote_address
|
|
|
|
def getLocalAddress(self):
|
|
return self._local_address
|
|
|
|
def setOption(self, name, value):
|
|
if self._socket:
|
|
self._socket.setsockopt(_socket.SOL_SOCKET, name, value)
|
|
return self
|
|
|
|
def getOption(self, name):
|
|
if self._socket:
|
|
return self._socket.getsockopt(_socket.SOL_SOCKET, name)
|
|
return None
|
|
|
|
def supportsOption(self, name) -> bool:
|
|
return True
|
|
|
|
def validOps(self) -> int:
|
|
return SelectionKey.OP_READ | SelectionKey.OP_WRITE
|
|
|
|
def close(self):
|
|
super().close()
|
|
if self._socket:
|
|
self._socket.close()
|
|
|
|
def toString(self) -> str:
|
|
return f"DatagramChannel@{hex(id(self))}[connected={self._connected}]"
|
|
|
|
|
|
# ============================================================
|
|
# Pipe
|
|
# ============================================================
|
|
|
|
class Pipe(Stub):
|
|
"""
|
|
java.nio.channels.Pipe
|
|
A pair of channels that implements a unidirectional pipe.
|
|
"""
|
|
|
|
class SourceChannel(SelectableChannel):
|
|
"""The readable end of the pipe."""
|
|
def __init__(self, r_fd):
|
|
super().__init__()
|
|
self._r_fd = r_fd
|
|
|
|
def read(self, dst) -> int:
|
|
data = _os.read(self._r_fd, 4096)
|
|
from ..nio import ByteBuffer
|
|
if isinstance(dst, ByteBuffer):
|
|
for b in data:
|
|
dst.put(b)
|
|
return len(data)
|
|
|
|
def validOps(self) -> int:
|
|
return SelectionKey.OP_READ
|
|
|
|
def close(self):
|
|
super().close()
|
|
_os.close(self._r_fd)
|
|
|
|
class SinkChannel(SelectableChannel):
|
|
"""The writable end of the pipe."""
|
|
def __init__(self, w_fd):
|
|
super().__init__()
|
|
self._w_fd = w_fd
|
|
|
|
def write(self, src) -> int:
|
|
from ..nio import ByteBuffer
|
|
if isinstance(src, ByteBuffer):
|
|
data = bytearray()
|
|
for i in range(src.position(), src.limit()):
|
|
data.append(src.get(i))
|
|
return _os.write(self._w_fd, bytes(data))
|
|
return 0
|
|
|
|
def validOps(self) -> int:
|
|
return SelectionKey.OP_WRITE
|
|
|
|
def close(self):
|
|
super().close()
|
|
_os.close(self._w_fd)
|
|
|
|
@staticmethod
|
|
def open() -> "Pipe":
|
|
pipe = Pipe()
|
|
r, w = _os.pipe()
|
|
pipe._source = Pipe.SourceChannel(r)
|
|
pipe._sink = Pipe.SinkChannel(w)
|
|
return pipe
|
|
|
|
def source(self) -> SourceChannel:
|
|
return self._source
|
|
|
|
def sink(self) -> SinkChannel:
|
|
return self._sink
|
|
|
|
def toString(self) -> str:
|
|
return f"Pipe@{hex(id(self))}"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# FileChannel MapMode
|
|
# ============================================================
|
|
|
|
class FileChannelMapMode(Stub):
|
|
"""
|
|
java.nio.channels.FileChannel.MapMode
|
|
A file mapping mode.
|
|
"""
|
|
READ_ONLY: "FileChannelMapMode" = None
|
|
READ_WRITE: "FileChannelMapMode" = None
|
|
PRIVATE: "FileChannelMapMode" = None
|
|
|
|
def __init__(self, name: str):
|
|
super().__init__()
|
|
self._name = name
|
|
|
|
def toString(self) -> str:
|
|
return self._name
|
|
|
|
def equals(self, other) -> bool:
|
|
return isinstance(other, FileChannelMapMode) and self._name == other._name
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._name) & 0x7FFFFFFF
|
|
|
|
|
|
FileChannelMapMode.READ_ONLY = FileChannelMapMode("READ_ONLY")
|
|
FileChannelMapMode.READ_WRITE = FileChannelMapMode("READ_WRITE")
|
|
FileChannelMapMode.PRIVATE = FileChannelMapMode("PRIVATE")
|
|
|
|
|
|
# ============================================================
|
|
# FileChannel
|
|
# ============================================================
|
|
|
|
class FileChannel(Channel):
|
|
"""
|
|
java.nio.channels.FileChannel
|
|
A channel for reading, writing, mapping, and manipulating a file.
|
|
"""
|
|
IMPL_OPEN_LOCKING: bool = True
|
|
|
|
def __init__(self, file_path: str = None, mode: str = 'r'):
|
|
super().__init__()
|
|
self._file_path = file_path
|
|
self._mode = mode
|
|
self._fd = None
|
|
self._position = 0
|
|
if file_path:
|
|
self._fd = _os.open(file_path, self._get_os_flags(mode))
|
|
|
|
@staticmethod
|
|
def open(path: Any, options=None, attrs=None) -> "FileChannel":
|
|
"""Opens a file channel."""
|
|
return FileChannel(str(path))
|
|
|
|
def _get_os_flags(self, mode: str) -> int:
|
|
flags = 0
|
|
if 'r' in mode and 'w' not in mode and '+' not in mode:
|
|
flags = _os.O_RDONLY
|
|
elif 'w' in mode or '+' in mode:
|
|
flags = _os.O_RDWR | _os.O_CREAT
|
|
return flags
|
|
|
|
def read(self, dst) -> int:
|
|
"""Reads bytes from this channel into the given buffer."""
|
|
if self._fd is None:
|
|
return -1
|
|
try:
|
|
data = _os.read(self._fd, 4096)
|
|
if not data:
|
|
return -1
|
|
from ..nio import ByteBuffer
|
|
if isinstance(dst, ByteBuffer):
|
|
for b in data:
|
|
dst.put(b)
|
|
self._position += len(data)
|
|
return len(data)
|
|
except OSError:
|
|
return -1
|
|
|
|
def read(self, dsts, offset: int = 0, length: int = 0) -> int:
|
|
total = 0
|
|
bufs = dsts[offset:offset + length] if length else dsts
|
|
for buf in bufs:
|
|
n = self.read(buf)
|
|
if n < 0:
|
|
break
|
|
total += n
|
|
return total
|
|
|
|
def write(self, src) -> int:
|
|
"""Writes bytes to this channel from the given buffer."""
|
|
if self._fd is None:
|
|
return 0
|
|
from ..nio import ByteBuffer
|
|
if isinstance(src, ByteBuffer):
|
|
data = bytearray()
|
|
for i in range(src.position(), src.limit()):
|
|
data.append(src.get(i))
|
|
try:
|
|
written = _os.write(self._fd, bytes(data))
|
|
self._position += written
|
|
return written
|
|
except OSError:
|
|
return 0
|
|
return 0
|
|
|
|
def write(self, srcs, offset: int = 0, length: int = 0) -> int:
|
|
total = 0
|
|
bufs = srcs[offset:offset + length] if length else srcs
|
|
for buf in bufs:
|
|
total += self.write(buf)
|
|
return total
|
|
|
|
def position(self) -> int:
|
|
"""Returns this channel's file position."""
|
|
if self._fd is not None:
|
|
self._position = _os.lseek(self._fd, 0, _os.SEEK_CUR)
|
|
return self._position
|
|
|
|
def position(self, new_position: int) -> "FileChannel":
|
|
"""Sets this channel's file position."""
|
|
if self._fd is not None:
|
|
_os.lseek(self._fd, new_position, _os.SEEK_SET)
|
|
self._position = new_position
|
|
return self
|
|
|
|
def size(self) -> int:
|
|
"""Returns the current size of this channel's file."""
|
|
if self._fd is not None:
|
|
return _os.lseek(self._fd, 0, _os.SEEK_END)
|
|
return 0
|
|
|
|
def truncate(self, size: int) -> "FileChannel":
|
|
"""Truncates the file to the given size."""
|
|
if self._fd is not None:
|
|
_os.ftruncate(self._fd, size)
|
|
return self
|
|
|
|
def force(self, meta_data: bool):
|
|
"""Forces any updates to this channel's file to be written to storage."""
|
|
if self._fd is not None:
|
|
_os.fsync(self._fd)
|
|
|
|
def transferTo(self, position: int, count: int, target: WritableByteChannel) -> int:
|
|
"""Transfers bytes from this channel to the given writable channel."""
|
|
old_pos = self.position()
|
|
self.position(position)
|
|
from ..nio import ByteBuffer
|
|
buf = ByteBuffer.allocate(count)
|
|
n = self.read(buf)
|
|
buf.flip()
|
|
target.write(buf)
|
|
self.position(old_pos)
|
|
return n
|
|
|
|
def transferFrom(self, src: ReadableByteChannel, position: int, count: int) -> int:
|
|
"""Transfers bytes from the given readable channel to this channel."""
|
|
old_pos = self.position()
|
|
self.position(position)
|
|
from ..nio import ByteBuffer
|
|
buf = ByteBuffer.allocate(count)
|
|
n = src.read(buf)
|
|
buf.flip()
|
|
self.write(buf)
|
|
self.position(old_pos)
|
|
return n
|
|
|
|
def map(self, mode: FileChannelMapMode, position: int, size: int):
|
|
"""Maps a region of this channel's file into memory (stub)."""
|
|
return None
|
|
|
|
def lock(self, position: int = 0, size: int = 0, shared: bool = False):
|
|
"""Locks a region of this channel's file (stub)."""
|
|
return None
|
|
|
|
def tryLock(self, position: int = 0, size: int = 0, shared: bool = False):
|
|
"""Attempts to lock a region of this channel's file (stub)."""
|
|
return None
|
|
|
|
def close(self):
|
|
super().close()
|
|
if self._fd is not None:
|
|
_os.close(self._fd)
|
|
self._fd = None
|
|
|
|
def toString(self) -> str:
|
|
return f"FileChannel@{hex(id(self))}[path={self._file_path}]"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# AsynchronousChannel stubs
|
|
# ============================================================
|
|
|
|
class CompletionHandler(Stub):
|
|
"""
|
|
java.nio.channels.CompletionHandler
|
|
A handler for consuming the result of an asynchronous I/O operation.
|
|
"""
|
|
def completed(self, result, attachment):
|
|
raise NotImplementedError
|
|
|
|
def failed(self, exc: Exception, attachment):
|
|
raise NotImplementedError
|
|
|
|
|
|
class AsynchronousChannel(Channel):
|
|
"""An asynchronous channel for I/O operations."""
|
|
pass
|
|
|
|
|
|
class AsynchronousByteChannel(AsynchronousChannel):
|
|
"""An asynchronous channel that can read and write bytes."""
|
|
pass
|
|
|
|
|
|
class AsynchronousChannelGroup(Stub):
|
|
"""
|
|
java.nio.channels.AsynchronousChannelGroup
|
|
A grouping of asynchronous channels.
|
|
"""
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._channels = []
|
|
|
|
def shutdown(self):
|
|
pass
|
|
|
|
def shutdownNow(self):
|
|
pass
|
|
|
|
def awaitTermination(self, timeout: int, unit) -> bool:
|
|
return True
|
|
|
|
def isShutdown(self) -> bool:
|
|
return False
|
|
|
|
def isTerminated(self) -> bool:
|
|
return False
|
|
|
|
def toString(self) -> str:
|
|
return f"AsynchronousChannelGroup@{hex(id(self))}"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self) & 0x7FFFFFFF
|
|
|
|
|
|
class AsynchronousServerSocketChannel(AsynchronousChannel):
|
|
"""An asynchronous channel for stream-oriented listening sockets."""
|
|
@staticmethod
|
|
def open(group: AsynchronousChannelGroup = None) -> "AsynchronousServerSocketChannel":
|
|
return AsynchronousServerSocketChannel()
|
|
|
|
def bind(self, local: Any) -> "AsynchronousServerSocketChannel":
|
|
return self
|
|
|
|
def accept(self, attachment=None, handler: CompletionHandler = None):
|
|
pass
|
|
|
|
def getLocalAddress(self):
|
|
return None
|
|
|
|
def setOption(self, name, value):
|
|
return self
|
|
|
|
def close(self):
|
|
pass
|
|
|
|
def toString(self) -> str:
|
|
return f"AsynchronousServerSocketChannel@{hex(id(self))}"
|
|
|
|
|
|
class AsynchronousSocketChannel(AsynchronousByteChannel):
|
|
"""An asynchronous channel for stream-oriented connecting sockets."""
|
|
@staticmethod
|
|
def open(group: AsynchronousChannelGroup = None) -> "AsynchronousSocketChannel":
|
|
return AsynchronousSocketChannel()
|
|
|
|
def connect(self, remote: Any, attachment=None, handler: CompletionHandler = None):
|
|
pass
|
|
|
|
def read(self, dst, attachment=None, handler: CompletionHandler = None):
|
|
pass
|
|
|
|
def write(self, src, attachment=None, handler: CompletionHandler = None):
|
|
pass
|
|
|
|
def getRemoteAddress(self):
|
|
return None
|
|
|
|
def getLocalAddress(self):
|
|
return None
|
|
|
|
def shutdownInput(self):
|
|
pass
|
|
|
|
def shutdownOutput(self):
|
|
pass
|
|
|
|
def close(self):
|
|
pass
|
|
|
|
def toString(self) -> str:
|
|
return f"AsynchronousSocketChannel@{hex(id(self))}"
|
|
|
|
|
|
class AsynchronousFileChannel(AsynchronousChannel):
|
|
"""An asynchronous channel for reading, writing, and manipulating a file."""
|
|
@staticmethod
|
|
def open(path: Any, options=None, executor=None, attrs=None) -> "AsynchronousFileChannel":
|
|
return AsynchronousFileChannel()
|
|
|
|
def read(self, dst, position: int, attachment=None, handler: CompletionHandler = None):
|
|
pass
|
|
|
|
def write(self, src, position: int, attachment=None, handler: CompletionHandler = None):
|
|
pass
|
|
|
|
def lock(self, position: int = 0, size: int = 0, shared: bool = False,
|
|
attachment=None, handler: CompletionHandler = None):
|
|
pass
|
|
|
|
def tryLock(self, position: int = 0, size: int = 0, shared: bool = False):
|
|
return None
|
|
|
|
def size(self) -> int:
|
|
return 0
|
|
|
|
def truncate(self, size: int) -> "AsynchronousFileChannel":
|
|
return self
|
|
|
|
def force(self, meta_data: bool):
|
|
pass
|
|
|
|
def close(self):
|
|
pass
|
|
|
|
def isOpen(self) -> bool:
|
|
return True
|
|
|
|
def toString(self) -> str:
|
|
return f"AsynchronousFileChannel@{hex(id(self))}"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self) & 0x7FFFFFFF |