622 lines
21 KiB
Python
622 lines
21 KiB
Python
"""
|
|
Java NIO standard library APIs implemented in Python.
|
|
java.nio package - Buffer, ByteBuffer, CharBuffer, etc.
|
|
"""
|
|
|
|
from .._core import Stub
|
|
from .lang import RuntimeException
|
|
from .io import IOException
|
|
import struct as _struct
|
|
import sys as _sys
|
|
|
|
IllegalStateException = type("IllegalStateException", (RuntimeException,), {})
|
|
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
|
|
|
|
__all__ = [
|
|
"Buffer", "ByteBuffer", "CharBuffer", "ShortBuffer",
|
|
"IntBuffer", "LongBuffer", "FloatBuffer", "DoubleBuffer",
|
|
"MappedByteBuffer", "ByteOrder",
|
|
"BufferOverflowException", "BufferUnderflowException",
|
|
"ReadOnlyBufferException", "InvalidMarkException",
|
|
]
|
|
|
|
|
|
# ============================================================
|
|
# Exception classes
|
|
# ============================================================
|
|
|
|
class BufferOverflowException(RuntimeException):
|
|
pass
|
|
|
|
class BufferUnderflowException(RuntimeException):
|
|
pass
|
|
|
|
class ReadOnlyBufferException(RuntimeException):
|
|
pass
|
|
|
|
class InvalidMarkException(IllegalStateException):
|
|
pass
|
|
|
|
|
|
# ============================================================
|
|
# ByteOrder
|
|
# ============================================================
|
|
|
|
class ByteOrder(Stub):
|
|
BIG_ENDIAN = None
|
|
LITTLE_ENDIAN = None
|
|
|
|
def __init__(self, name):
|
|
super().__init__()
|
|
self._name = name
|
|
|
|
@staticmethod
|
|
def nativeOrder():
|
|
if _sys.byteorder == "little":
|
|
return ByteOrder.LITTLE_ENDIAN
|
|
return ByteOrder.BIG_ENDIAN
|
|
|
|
def toString(self):
|
|
return self._name
|
|
|
|
|
|
ByteOrder.BIG_ENDIAN = ByteOrder("BIG_ENDIAN")
|
|
ByteOrder.LITTLE_ENDIAN = ByteOrder("LITTLE_ENDIAN")
|
|
|
|
|
|
# ============================================================
|
|
# Buffer
|
|
# ============================================================
|
|
|
|
class Buffer(Stub):
|
|
def __init__(self, capacity, backing=None):
|
|
super().__init__()
|
|
self._capacity = capacity
|
|
self._position = 0
|
|
self._limit = capacity
|
|
self._mark = -1
|
|
self._backing = backing if backing is not None else bytearray(capacity)
|
|
self._read_only = False
|
|
|
|
def capacity(self):
|
|
return self._capacity
|
|
|
|
def position(self):
|
|
return self._position
|
|
|
|
def position(self, new_position):
|
|
if new_position < 0 or new_position > self._limit:
|
|
raise IllegalArgumentException("Invalid position")
|
|
self._position = new_position
|
|
return self
|
|
|
|
def limit(self):
|
|
return self._limit
|
|
|
|
def limit(self, new_limit):
|
|
if new_limit < 0 or new_limit > self._capacity:
|
|
raise IllegalArgumentException("Invalid limit")
|
|
self._limit = new_limit
|
|
if self._position > self._limit:
|
|
self._position = self._limit
|
|
if self._mark > self._limit:
|
|
self._mark = -1
|
|
return self
|
|
|
|
def mark(self):
|
|
self._mark = self._position
|
|
return self
|
|
|
|
def reset(self):
|
|
if self._mark < 0:
|
|
raise InvalidMarkException()
|
|
self._position = self._mark
|
|
return self
|
|
|
|
def clear(self):
|
|
self._position = 0
|
|
self._limit = self._capacity
|
|
self._mark = -1
|
|
return self
|
|
|
|
def flip(self):
|
|
self._limit = self._position
|
|
self._position = 0
|
|
self._mark = -1
|
|
return self
|
|
|
|
def rewind(self):
|
|
self._position = 0
|
|
self._mark = -1
|
|
return self
|
|
|
|
def remaining(self):
|
|
return self._limit - self._position
|
|
|
|
def hasRemaining(self):
|
|
return self._position < self._limit
|
|
|
|
def isReadOnly(self):
|
|
return self._read_only
|
|
|
|
def isDirect(self):
|
|
return False
|
|
|
|
def array(self):
|
|
if self._read_only:
|
|
raise ReadOnlyBufferException()
|
|
return self._backing
|
|
|
|
def arrayOffset(self):
|
|
return 0
|
|
|
|
|
|
# ============================================================
|
|
# ByteBuffer
|
|
# ============================================================
|
|
|
|
class ByteBuffer(Buffer):
|
|
def __init__(self, capacity, backing=None, order=None):
|
|
super().__init__(capacity, backing)
|
|
self._order = order or ByteOrder.BIG_ENDIAN
|
|
|
|
@staticmethod
|
|
def allocate(capacity):
|
|
return ByteBuffer(capacity, bytearray(capacity))
|
|
|
|
@staticmethod
|
|
def allocateDirect(capacity):
|
|
return ByteBuffer(capacity, bytearray(capacity))
|
|
|
|
@staticmethod
|
|
def wrap(array, offset=0, length=None):
|
|
if length is None:
|
|
length = len(array) - offset
|
|
buf = ByteBuffer(length, bytearray(array))
|
|
buf._position = offset
|
|
buf._limit = offset + length
|
|
return buf
|
|
|
|
def put(self, b, index=None):
|
|
if self._read_only:
|
|
raise ReadOnlyBufferException()
|
|
if index is None:
|
|
if isinstance(b, int):
|
|
if self._position >= self._limit:
|
|
raise BufferOverflowException()
|
|
self._backing[self._position] = b & 0xFF
|
|
self._position += 1
|
|
elif isinstance(b, ByteBuffer):
|
|
n = b.remaining()
|
|
if self.remaining() < n:
|
|
raise BufferOverflowException()
|
|
self._backing[self._position:self._position+n] = b._backing[b._position:b._limit]
|
|
self._position += n
|
|
b._position = b._limit
|
|
elif isinstance(b, (bytes, bytearray)):
|
|
if self.remaining() < len(b):
|
|
raise BufferOverflowException()
|
|
self._backing[self._position:self._position+len(b)] = b
|
|
self._position += len(b)
|
|
else:
|
|
self._backing[index] = b & 0xFF
|
|
return self
|
|
|
|
def get(self, index=None):
|
|
if index is None:
|
|
if self._position >= self._limit:
|
|
raise BufferUnderflowException()
|
|
val = self._backing[self._position]
|
|
self._position += 1
|
|
return val & 0xFF
|
|
return self._backing[index] & 0xFF
|
|
|
|
def putInt(self, value, index=None):
|
|
data = _struct.pack(">i" if self._order == ByteOrder.BIG_ENDIAN else "<i", value)
|
|
if index is not None:
|
|
self._backing[index:index+4] = data
|
|
else:
|
|
if self.remaining() < 4:
|
|
raise BufferOverflowException()
|
|
self._backing[self._position:self._position+4] = data
|
|
self._position += 4
|
|
return self
|
|
|
|
def getInt(self, index=None):
|
|
if index is not None:
|
|
data = self._backing[index:index+4]
|
|
else:
|
|
if self.remaining() < 4:
|
|
raise BufferUnderflowException()
|
|
data = self._backing[self._position:self._position+4]
|
|
self._position += 4
|
|
return _struct.unpack(">i" if self._order == ByteOrder.BIG_ENDIAN else "<i", bytes(data))[0]
|
|
|
|
def putLong(self, value, index=None):
|
|
data = _struct.pack(">q" if self._order == ByteOrder.BIG_ENDIAN else "<q", value)
|
|
if index is not None:
|
|
self._backing[index:index+8] = data
|
|
else:
|
|
if self.remaining() < 8:
|
|
raise BufferOverflowException()
|
|
self._backing[self._position:self._position+8] = data
|
|
self._position += 8
|
|
return self
|
|
|
|
def getLong(self, index=None):
|
|
if index is not None:
|
|
data = self._backing[index:index+8]
|
|
else:
|
|
if self.remaining() < 8:
|
|
raise BufferUnderflowException()
|
|
data = self._backing[self._position:self._position+8]
|
|
self._position += 8
|
|
return _struct.unpack(">q" if self._order == ByteOrder.BIG_ENDIAN else "<q", bytes(data))[0]
|
|
|
|
def putFloat(self, value, index=None):
|
|
data = _struct.pack(">f" if self._order == ByteOrder.BIG_ENDIAN else "<f", value)
|
|
if index is not None:
|
|
self._backing[index:index+4] = data
|
|
else:
|
|
if self.remaining() < 4:
|
|
raise BufferOverflowException()
|
|
self._backing[self._position:self._position+4] = data
|
|
self._position += 4
|
|
return self
|
|
|
|
def getFloat(self, index=None):
|
|
if index is not None:
|
|
data = self._backing[index:index+4]
|
|
else:
|
|
if self.remaining() < 4:
|
|
raise BufferUnderflowException()
|
|
data = self._backing[self._position:self._position+4]
|
|
self._position += 4
|
|
return _struct.unpack(">f" if self._order == ByteOrder.BIG_ENDIAN else "<f", bytes(data))[0]
|
|
|
|
def putDouble(self, value, index=None):
|
|
data = _struct.pack(">d" if self._order == ByteOrder.BIG_ENDIAN else "<d", value)
|
|
if index is not None:
|
|
self._backing[index:index+8] = data
|
|
else:
|
|
if self.remaining() < 8:
|
|
raise BufferOverflowException()
|
|
self._backing[self._position:self._position+8] = data
|
|
self._position += 8
|
|
return self
|
|
|
|
def getDouble(self, index=None):
|
|
if index is not None:
|
|
data = self._backing[index:index+8]
|
|
else:
|
|
if self.remaining() < 8:
|
|
raise BufferUnderflowException()
|
|
data = self._backing[self._position:self._position+8]
|
|
self._position += 8
|
|
return _struct.unpack(">d" if self._order == ByteOrder.BIG_ENDIAN else "<d", bytes(data))[0]
|
|
|
|
def putShort(self, value, index=None):
|
|
data = _struct.pack(">h" if self._order == ByteOrder.BIG_ENDIAN else "<h", value)
|
|
if index is not None:
|
|
self._backing[index:index+2] = data
|
|
else:
|
|
if self.remaining() < 2:
|
|
raise BufferOverflowException()
|
|
self._backing[self._position:self._position+2] = data
|
|
self._position += 2
|
|
return self
|
|
|
|
def getShort(self, index=None):
|
|
if index is not None:
|
|
data = self._backing[index:index+2]
|
|
else:
|
|
if self.remaining() < 2:
|
|
raise BufferUnderflowException()
|
|
data = self._backing[self._position:self._position+2]
|
|
self._position += 2
|
|
return _struct.unpack(">h" if self._order == ByteOrder.BIG_ENDIAN else "<h", bytes(data))[0]
|
|
|
|
def putChar(self, value, index=None):
|
|
return self.putShort(ord(value), index)
|
|
|
|
def getChar(self, index=None):
|
|
return chr(self.getShort(index))
|
|
|
|
def asIntBuffer(self):
|
|
return IntBuffer(self.remaining() // 4, self._backing, self._position, self._order)
|
|
|
|
def asLongBuffer(self):
|
|
return LongBuffer(self.remaining() // 8, self._backing, self._position, self._order)
|
|
|
|
def asFloatBuffer(self):
|
|
return FloatBuffer(self.remaining() // 4, self._backing, self._position, self._order)
|
|
|
|
def asDoubleBuffer(self):
|
|
return DoubleBuffer(self.remaining() // 8, self._backing, self._position, self._order)
|
|
|
|
def asShortBuffer(self):
|
|
return ShortBuffer(self.remaining() // 2, self._backing, self._position, self._order)
|
|
|
|
def asCharBuffer(self):
|
|
return CharBuffer(self.remaining() // 2, self._backing, self._position, self._order)
|
|
|
|
def slice(self):
|
|
n = self.remaining()
|
|
buf = ByteBuffer(n, self._backing[self._position:self._limit])
|
|
return buf
|
|
|
|
def duplicate(self):
|
|
buf = ByteBuffer(self._capacity, bytearray(self._backing))
|
|
buf._position = self._position
|
|
buf._limit = self._limit
|
|
buf._order = self._order
|
|
return buf
|
|
|
|
def asReadOnlyBuffer(self):
|
|
buf = self.duplicate()
|
|
buf._read_only = True
|
|
return buf
|
|
|
|
def order(self, order=None):
|
|
if order is not None:
|
|
self._order = order
|
|
return self
|
|
return self._order
|
|
|
|
def compact(self):
|
|
if self._read_only:
|
|
raise ReadOnlyBufferException()
|
|
remaining = self.remaining()
|
|
self._backing[:remaining] = self._backing[self._position:self._limit]
|
|
self._position = remaining
|
|
self._limit = self._capacity
|
|
self._mark = -1
|
|
return self
|
|
|
|
def toString(self):
|
|
return f"ByteBuffer[pos={self._position} lim={self._limit} cap={self._capacity}]"
|
|
|
|
def hashCode(self):
|
|
return hash(bytes(self._backing)) & 0x7FFFFFFF
|
|
|
|
def equals(self, other):
|
|
if isinstance(other, ByteBuffer):
|
|
return bytes(self._backing[self._position:self._limit]) == bytes(other._backing[other._position:other._limit])
|
|
return False
|
|
|
|
def compareTo(self, other):
|
|
if isinstance(other, ByteBuffer):
|
|
a = bytes(self._backing[self._position:self._limit])
|
|
b = bytes(other._backing[other._position:other._limit])
|
|
return (a > b) - (a < b)
|
|
return -1
|
|
|
|
|
|
# ============================================================
|
|
# IntBuffer
|
|
# ============================================================
|
|
|
|
class IntBuffer(Buffer):
|
|
def __init__(self, capacity, backing, offset=0, order=None):
|
|
super().__init__(capacity, backing)
|
|
self._int_order = order or ByteOrder.BIG_ENDIAN
|
|
self._int_offset = offset
|
|
|
|
def get(self, index=None):
|
|
if index is not None:
|
|
idx = self._int_offset + index * 4
|
|
return _struct.unpack(">i" if self._int_order == ByteOrder.BIG_ENDIAN else "<i",
|
|
bytes(self._backing[idx:idx+4]))[0]
|
|
if self.remaining() < 4:
|
|
raise BufferUnderflowException()
|
|
val = _struct.unpack(">i" if self._int_order == ByteOrder.BIG_ENDIAN else "<i",
|
|
bytes(self._backing[self._position:self._position+4]))[0]
|
|
self._position += 4
|
|
return val
|
|
|
|
def put(self, value, index=None):
|
|
data = _struct.pack(">i" if self._int_order == ByteOrder.BIG_ENDIAN else "<i", value)
|
|
if index is not None:
|
|
idx = self._int_offset + index * 4
|
|
self._backing[idx:idx+4] = data
|
|
else:
|
|
if self.remaining() < 4:
|
|
raise BufferOverflowException()
|
|
self._backing[self._position:self._position+4] = data
|
|
self._position += 4
|
|
return self
|
|
|
|
|
|
# ============================================================
|
|
# LongBuffer
|
|
# ============================================================
|
|
|
|
class LongBuffer(Buffer):
|
|
def __init__(self, capacity, backing, offset=0, order=None):
|
|
super().__init__(capacity, backing)
|
|
self._long_order = order or ByteOrder.BIG_ENDIAN
|
|
self._long_offset = offset
|
|
|
|
def get(self, index=None):
|
|
if index is not None:
|
|
idx = self._long_offset + index * 8
|
|
return _struct.unpack(">q" if self._long_order == ByteOrder.BIG_ENDIAN else "<q",
|
|
bytes(self._backing[idx:idx+8]))[0]
|
|
if self.remaining() < 8:
|
|
raise BufferUnderflowException()
|
|
val = _struct.unpack(">q" if self._long_order == ByteOrder.BIG_ENDIAN else "<q",
|
|
bytes(self._backing[self._position:self._position+8]))[0]
|
|
self._position += 8
|
|
return val
|
|
|
|
def put(self, value, index=None):
|
|
data = _struct.pack(">q" if self._long_order == ByteOrder.BIG_ENDIAN else "<q", value)
|
|
if index is not None:
|
|
idx = self._long_offset + index * 8
|
|
self._backing[idx:idx+8] = data
|
|
else:
|
|
if self.remaining() < 8:
|
|
raise BufferOverflowException()
|
|
self._backing[self._position:self._position+8] = data
|
|
self._position += 8
|
|
return self
|
|
|
|
|
|
# ============================================================
|
|
# FloatBuffer
|
|
# ============================================================
|
|
|
|
class FloatBuffer(Buffer):
|
|
def __init__(self, capacity, backing, offset=0, order=None):
|
|
super().__init__(capacity, backing)
|
|
self._float_order = order or ByteOrder.BIG_ENDIAN
|
|
self._float_offset = offset
|
|
|
|
def get(self, index=None):
|
|
if index is not None:
|
|
idx = self._float_offset + index * 4
|
|
return _struct.unpack(">f" if self._float_order == ByteOrder.BIG_ENDIAN else "<f",
|
|
bytes(self._backing[idx:idx+4]))[0]
|
|
if self.remaining() < 4:
|
|
raise BufferUnderflowException()
|
|
val = _struct.unpack(">f" if self._float_order == ByteOrder.BIG_ENDIAN else "<f",
|
|
bytes(self._backing[self._position:self._position+4]))[0]
|
|
self._position += 4
|
|
return val
|
|
|
|
def put(self, value, index=None):
|
|
data = _struct.pack(">f" if self._float_order == ByteOrder.BIG_ENDIAN else "<f", value)
|
|
if index is not None:
|
|
idx = self._float_offset + index * 4
|
|
self._backing[idx:idx+4] = data
|
|
else:
|
|
if self.remaining() < 4:
|
|
raise BufferOverflowException()
|
|
self._backing[self._position:self._position+4] = data
|
|
self._position += 4
|
|
return self
|
|
|
|
|
|
# ============================================================
|
|
# DoubleBuffer
|
|
# ============================================================
|
|
|
|
class DoubleBuffer(Buffer):
|
|
def __init__(self, capacity, backing, offset=0, order=None):
|
|
super().__init__(capacity, backing)
|
|
self._double_order = order or ByteOrder.BIG_ENDIAN
|
|
self._double_offset = offset
|
|
|
|
def get(self, index=None):
|
|
if index is not None:
|
|
idx = self._double_offset + index * 8
|
|
return _struct.unpack(">d" if self._double_order == ByteOrder.BIG_ENDIAN else "<d",
|
|
bytes(self._backing[idx:idx+8]))[0]
|
|
if self.remaining() < 8:
|
|
raise BufferUnderflowException()
|
|
val = _struct.unpack(">d" if self._double_order == ByteOrder.BIG_ENDIAN else "<d",
|
|
bytes(self._backing[self._position:self._position+8]))[0]
|
|
self._position += 8
|
|
return val
|
|
|
|
def put(self, value, index=None):
|
|
data = _struct.pack(">d" if self._double_order == ByteOrder.BIG_ENDIAN else "<d", value)
|
|
if index is not None:
|
|
idx = self._double_offset + index * 8
|
|
self._backing[idx:idx+8] = data
|
|
else:
|
|
if self.remaining() < 8:
|
|
raise BufferOverflowException()
|
|
self._backing[self._position:self._position+8] = data
|
|
self._position += 8
|
|
return self
|
|
|
|
|
|
# ============================================================
|
|
# ShortBuffer
|
|
# ============================================================
|
|
|
|
class ShortBuffer(Buffer):
|
|
def __init__(self, capacity, backing, offset=0, order=None):
|
|
super().__init__(capacity, backing)
|
|
self._short_order = order or ByteOrder.BIG_ENDIAN
|
|
self._short_offset = offset
|
|
|
|
def get(self, index=None):
|
|
if index is not None:
|
|
idx = self._short_offset + index * 2
|
|
return _struct.unpack(">h" if self._short_order == ByteOrder.BIG_ENDIAN else "<h",
|
|
bytes(self._backing[idx:idx+2]))[0]
|
|
if self.remaining() < 2:
|
|
raise BufferUnderflowException()
|
|
val = _struct.unpack(">h" if self._short_order == ByteOrder.BIG_ENDIAN else "<h",
|
|
bytes(self._backing[self._position:self._position+2]))[0]
|
|
self._position += 2
|
|
return val
|
|
|
|
def put(self, value, index=None):
|
|
data = _struct.pack(">h" if self._short_order == ByteOrder.BIG_ENDIAN else "<h", value)
|
|
if index is not None:
|
|
idx = self._short_offset + index * 2
|
|
self._backing[idx:idx+2] = data
|
|
else:
|
|
if self.remaining() < 2:
|
|
raise BufferOverflowException()
|
|
self._backing[self._position:self._position+2] = data
|
|
self._position += 2
|
|
return self
|
|
|
|
|
|
# ============================================================
|
|
# CharBuffer
|
|
# ============================================================
|
|
|
|
class CharBuffer(Buffer):
|
|
def __init__(self, capacity, backing, offset=0, order=None):
|
|
super().__init__(capacity, backing)
|
|
self._char_order = order or ByteOrder.BIG_ENDIAN
|
|
self._char_offset = offset
|
|
|
|
def get(self, index=None):
|
|
if index is not None:
|
|
idx = self._char_offset + index * 2
|
|
val = _struct.unpack(">h" if self._char_order == ByteOrder.BIG_ENDIAN else "<h",
|
|
bytes(self._backing[idx:idx+2]))[0]
|
|
return chr(val)
|
|
if self.remaining() < 2:
|
|
raise BufferUnderflowException()
|
|
val = _struct.unpack(">h" if self._char_order == ByteOrder.BIG_ENDIAN else "<h",
|
|
bytes(self._backing[self._position:self._position+2]))[0]
|
|
self._position += 2
|
|
return chr(val)
|
|
|
|
def put(self, value, index=None):
|
|
char_val = ord(value) if isinstance(value, str) else value
|
|
data = _struct.pack(">h" if self._char_order == ByteOrder.BIG_ENDIAN else "<h", char_val)
|
|
if index is not None:
|
|
idx = self._char_offset + index * 2
|
|
self._backing[idx:idx+2] = data
|
|
else:
|
|
if self.remaining() < 2:
|
|
raise BufferOverflowException()
|
|
self._backing[self._position:self._position+2] = data
|
|
self._position += 2
|
|
return self
|
|
|
|
|
|
# ============================================================
|
|
# MappedByteBuffer
|
|
# ============================================================
|
|
|
|
class MappedByteBuffer(ByteBuffer):
|
|
def __init__(self, capacity, backing=None):
|
|
super().__init__(capacity, backing)
|
|
|
|
def force(self):
|
|
return self
|
|
|
|
def isLoaded(self):
|
|
return True
|
|
|
|
def load(self):
|
|
return self |