mirror of
https://github.com/iKarith/cppo-ng.git
synced 2024-06-01 06:41:27 +00:00
Make buffer more stylistically consistent
We're beginning to develop some consistent styling in blocksfree/cppo-ng, but the buffer code predates most of that. Let's fix that.
This commit is contained in:
parent
d3c3e1479c
commit
7c169b2053
|
@ -16,32 +16,34 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
"""Abstract base class for disk image buffers
|
||||||
|
|
||||||
|
The buffers we will work with most commonly represent 140k disk images. For
|
||||||
|
that, it'd make a lot of sense to simply use a bytearray. It's built in to
|
||||||
|
the language and pretty efficient for an interpreted OO construct. But even
|
||||||
|
ProDOS disk images can be 32M in size.
|
||||||
|
|
||||||
|
Well that's fine, because most systems have 1G or more of RAM, plus swap, you
|
||||||
|
might say. And that's true, they do have that much RAM. Only on an embedded
|
||||||
|
system like the Raspberry Pi, they may not have much or any swap. And while
|
||||||
|
ProDOS volumes will never be larger than 32M, GS/OS supports other
|
||||||
|
filesystems
|
||||||
|
|
||||||
|
HFS is even commonly used. It's true that HFS support has not been something
|
||||||
|
you could expect from AppleCommander, to say nothing of cppo, but we ought to
|
||||||
|
consider the possibility for the future. As of this writing, we cannot
|
||||||
|
really guarantee that your host operating system can handle HFS completely.
|
||||||
|
How do you access resource fork on Linux? How do you access any of it on
|
||||||
|
Windows? Can Apple OSes officially carrying the designation "macOS" even
|
||||||
|
open old HFS volumes read-only anymore? They haven't had read-write access
|
||||||
|
for some time now.
|
||||||
|
"""
|
||||||
|
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
# Rationale:
|
|
||||||
#
|
|
||||||
# The buffers we will work with most commonly represent 140k disk images. For
|
|
||||||
# that, it'd make a lot of sense to simply use a bytearray. It's built in to
|
|
||||||
# the language and pretty efficient for an interpreted OO construct. But even
|
|
||||||
# ProDOS disk images can be 32M in size.
|
|
||||||
#
|
|
||||||
# Well that's fine, because most systems have 1G or more of RAM, plus swap, you
|
|
||||||
# might say. And that's true, they do have that much RAM. Only on an embedded
|
|
||||||
# system like the Raspberry Pi, they may not have much or any swap. And while
|
|
||||||
# ProDOS volumes will never be larger than 32M, GS/OS supports other
|
|
||||||
# filesystems
|
|
||||||
#
|
|
||||||
# HFS is even commonly used. It's true that HFS support has not been something
|
|
||||||
# you could expect from AppleCommander, to say nothing of cppo, but we ought to
|
|
||||||
# consider the possibility for the future. As of this writing, we cannot
|
|
||||||
# really guarantee that your host operating system can handle HFS completely.
|
|
||||||
# How do you access resource fork on Linux? How do you access any of it on
|
|
||||||
# Windows? Can Apple OSes officially carrying the designation "macOS" even
|
|
||||||
# open old HFS volumes read-only anymore? They haven't had read-write access
|
|
||||||
# for some time now.
|
|
||||||
|
|
||||||
class BufferType(metaclass=ABCMeta):
|
class BufferType(object, metaclass=ABCMeta):
|
||||||
"""Abstract class that describes a BufferType.
|
"""Abstract class that describes a BufferType.
|
||||||
|
|
||||||
Read-only BufferType subclasses must implement read and __len__, as well as
|
Read-only BufferType subclasses must implement read and __len__, as well as
|
||||||
|
@ -61,41 +63,49 @@ class BufferType(metaclass=ABCMeta):
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def __len__(self) -> int:
|
def __len__(self) -> int:
|
||||||
"""Return len(self)"""
|
"""Implement len(self)
|
||||||
|
|
||||||
|
Subclasses must provide an implementation for this method.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def changed(self):
|
def changed(self):
|
||||||
"""Returns True if buffer has been altered
|
"""Return True if buffer has been altered
|
||||||
|
|
||||||
Always returns false for read-only buffers
|
Returns:
|
||||||
|
Always False for read-only buffers
|
||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def read(
|
def read(self, start: int, count: int) -> bytes:
|
||||||
self,
|
"""Return count bytes from buffer beginning at start
|
||||||
start: int = 0,
|
|
||||||
count: Optional[int] = None,
|
|
||||||
limit: bool = True
|
|
||||||
) -> bytearray:
|
|
||||||
"""Return bytearray of count bytes from buffer beginning at start
|
|
||||||
|
|
||||||
Should raise IndexError if an attempt to read past the end of the
|
Args:
|
||||||
buffer is made.
|
start: Starting position of bytes to return
|
||||||
|
count: Number of bytes to return
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bytes object of the requested length copied from buffer
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
IndexError if attempt to read outside the buffer is made
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def read1(
|
def read1(self, offset: int) -> int:
|
||||||
self,
|
"""Return single byte from buffer as int
|
||||||
offset: int = 0,
|
|
||||||
limit: bool = True
|
|
||||||
) -> int:
|
|
||||||
"""Return int of single byte from buffer at offset
|
|
||||||
|
|
||||||
Should raise IndexError if an attempt to read past the end of the
|
Args:
|
||||||
buffer is made.
|
offset: The position of the requested byte in the buffer
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int value of the requested byte
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
IndexError if attempt to read outside the buffer is made
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -103,26 +113,43 @@ class BufferType(metaclass=ABCMeta):
|
||||||
self,
|
self,
|
||||||
buf: bytes,
|
buf: bytes,
|
||||||
start: int,
|
start: int,
|
||||||
count: Optional[int] = None,
|
count: Optional[int] = None
|
||||||
limit: bool = True
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Writes bytes to buffer beginning at start
|
"""Write given bytes-like object to buffer at start
|
||||||
|
|
||||||
Raises NotImplementedError unless implemented by subclass
|
Subclasses should raise IndexError if an attempt to write outside the
|
||||||
|
buffer is made.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
buf: The bytes-like object to write
|
||||||
|
start: Offset to where in buffer it should be written
|
||||||
|
count: Length to write (default: length of buf)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotImplementedEror unless implemented by subclass
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('buffer does not support writing')
|
raise NotImplementedError('buffer does not support writing')
|
||||||
|
|
||||||
def resize(self, size: int) -> None:
|
def resize(self, size: int) -> None:
|
||||||
"""Resize buffer to size
|
r"""Resize a given buffer
|
||||||
|
|
||||||
Raises NotImplementedError unless implemented by subclass
|
Resizes the current buffer in place. If size < len(self), the buffer
|
||||||
|
will be truncated. If size > len(self), the buffer will be extended.
|
||||||
|
The newly added bytes will be b'\x00'
|
||||||
|
|
||||||
|
Args:
|
||||||
|
size: New size of buffer
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotImplementedError unless implemented by subclass
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('buffer does not support writing')
|
raise NotImplementedError('buffer does not support writing')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def locked(self) -> bool:
|
def locked(self) -> bool:
|
||||||
"""Returns True for read-only buffers.
|
"""Determine writability of buffer
|
||||||
|
|
||||||
Returns True unless writing is implemented by subclass
|
Returns:
|
||||||
|
True unless writing is implemented by subclass
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
"""Read/Write BufferType that lives in memory"""
|
||||||
|
|
||||||
|
|
||||||
from typing import Dict, List, Optional, Union
|
from typing import Dict, List, Optional, Union
|
||||||
from .buffertype import BufferType
|
from .buffertype import BufferType
|
||||||
|
@ -30,7 +32,7 @@ class ByteBuffer(BufferType):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
bytes_or_int: Union[bytes,int],
|
bytes_or_int: Union[bytes, int],
|
||||||
changed: bool = False,
|
changed: bool = False,
|
||||||
locked: bool = False
|
locked: bool = False
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -39,45 +41,53 @@ class ByteBuffer(BufferType):
|
||||||
self._locked = locked
|
self._locked = locked
|
||||||
|
|
||||||
def __len__(self) -> int:
|
def __len__(self) -> int:
|
||||||
"""Return len(self)"""
|
"""Implement len(self)"""
|
||||||
return len(self._buf)
|
return len(self._buf)
|
||||||
|
|
||||||
def read(
|
@property
|
||||||
self,
|
def changed(self):
|
||||||
start: int = 0,
|
"""Return True if buffer has been altered
|
||||||
count: int = 1,
|
|
||||||
limit: bool = True
|
|
||||||
) -> bytearray:
|
|
||||||
"""Return bytearray of count bytes from buffer beginning at start
|
|
||||||
|
|
||||||
By default, an IndexError will be raised if you read past the end of
|
Returns:
|
||||||
the buffer. Pass limit=False if reads outside the buffer should just
|
Always False for read-only buffers
|
||||||
return trncated or empty results as with python slicing
|
"""
|
||||||
|
return self._changed
|
||||||
|
|
||||||
|
def read(self, start: int, count: int) -> bytes:
|
||||||
|
"""Return count bytes from buffer beginning at start
|
||||||
|
|
||||||
|
Args:
|
||||||
|
start: Starting position of bytes to return
|
||||||
|
count: Number of bytes to return
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bytes object of the requested length copied from buffer
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
IndexError if attempt to read outside the buffer is made
|
||||||
"""
|
"""
|
||||||
if count is None:
|
|
||||||
count = len(self) - start
|
|
||||||
try:
|
try:
|
||||||
assert(start >= 0)
|
assert start >= 0
|
||||||
assert(count >= 0)
|
assert count >= 0
|
||||||
if limit == True:
|
assert start + count <= len(self._buf)
|
||||||
assert(start + count <= len(self._buf))
|
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
raise IndexError('buffer read with index out of range')
|
raise IndexError('buffer read with index out of range')
|
||||||
return bytes(self._buf[start:start + count])
|
return bytes(self._buf[start:start + count])
|
||||||
|
|
||||||
def read1(
|
def read1(self, offset: int) -> int:
|
||||||
self,
|
"""Return single byte from buffer as int
|
||||||
offset: int = 0,
|
|
||||||
limit: bool = True
|
|
||||||
) -> int:
|
|
||||||
"""Return int of single byte from buffer at offset
|
|
||||||
|
|
||||||
Should raise IndexError if an attempt to read past the end of the
|
Args:
|
||||||
buffer is made.
|
offset: The position of the requested byte in the buffer
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int value of the requested byte
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
IndexError if attempt to read outside the buffer is made
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if limit == True:
|
assert 0 <= offset <= len(self._buf)
|
||||||
assert 0 <= offset <= len(self._buf)
|
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
raise IndexError('buffer read with index out of range')
|
raise IndexError('buffer read with index out of range')
|
||||||
return self._buf[offset]
|
return self._buf[offset]
|
||||||
|
@ -86,14 +96,17 @@ class ByteBuffer(BufferType):
|
||||||
self,
|
self,
|
||||||
buf: bytes,
|
buf: bytes,
|
||||||
start: int,
|
start: int,
|
||||||
count: Optional[int] = None,
|
count: Optional[int] = None
|
||||||
limit: bool = True
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Writes bytes to buffer beginning at start
|
"""Write given bytes-like object to buffer at start
|
||||||
|
|
||||||
If count is not supplied, the entire bytes-like object will be written
|
Args:
|
||||||
to the buffer. An IndexError will be raised if the write would extend
|
buf: The bytes-like object to write
|
||||||
past the end of the buffer. Pass limit=False
|
start: Offset to where in buffer it should be written
|
||||||
|
count: Length to write (default: length of buf)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
IndexError if attempt to read outside the buffer is made
|
||||||
"""
|
"""
|
||||||
if self.locked:
|
if self.locked:
|
||||||
raise BufferError('cannot write to locked buffer')
|
raise BufferError('cannot write to locked buffer')
|
||||||
|
@ -101,21 +114,27 @@ class ByteBuffer(BufferType):
|
||||||
if not count:
|
if not count:
|
||||||
count = len(buf)
|
count = len(buf)
|
||||||
try:
|
try:
|
||||||
assert(start >= 0)
|
assert start >= 0
|
||||||
assert(count >= 0)
|
assert count >= 0
|
||||||
if limit == True:
|
assert start + count <= len(self._buf)
|
||||||
assert(start + count <= len(self._buf))
|
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
raise IndexError('buffer write with index out of range')
|
raise IndexError('buffer write with index out of range')
|
||||||
|
|
||||||
self._buf[start:start+count] = buf
|
self._buf[start:start+count] = buf
|
||||||
self._changed = True
|
self._changed = True
|
||||||
|
|
||||||
def resize(self, size):
|
def resize(self, size: int) -> None:
|
||||||
"""Resize buffer to size
|
r"""Resize a given buffer
|
||||||
|
|
||||||
If size is larger than len(self), the buffer is appended with zero
|
Resizes the current buffer in place. If size < len(self), the buffer
|
||||||
bytes. If it is smaller, the buffer will be truncated.
|
will be truncated. If size > len(self), the buffer will be extended.
|
||||||
|
The newly added bytes will be b'\x00'
|
||||||
|
|
||||||
|
Args:
|
||||||
|
size: New size of buffer
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BufferError if buffer is locked
|
||||||
"""
|
"""
|
||||||
if self.locked:
|
if self.locked:
|
||||||
raise BufferError('cannot write to locked buffer')
|
raise BufferError('cannot write to locked buffer')
|
||||||
|
@ -127,8 +146,12 @@ class ByteBuffer(BufferType):
|
||||||
self._changed = True
|
self._changed = True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def locked(self):
|
def locked(self) -> bool:
|
||||||
"""Returns True for read-only buffers."""
|
"""Determine writability of buffer
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if buffer has been locked to prevent writing
|
||||||
|
"""
|
||||||
return self._locked
|
return self._locked
|
||||||
|
|
||||||
@locked.setter
|
@locked.setter
|
||||||
|
|
Loading…
Reference in New Issue
Block a user