from contextlib import (
contextmanager,
)
from bluebase.pf import (
PfLayoutField,
PfLayoutView,
PfPageHeader,
PfPageData,
PfPage,
)
from .error import (
RmInvalidSidError,
RmUnoccupiedSlotError,
RmInvalidRecordDataSizeError,
)
from .record import (
RmRecordKey,
RmRecordData,
RmRecord,
)
__all__ = (
'RmSlotId',
'RmPageId',
'RmPageHeader',
'RmPage',
)
[문서]
class RmSlotId(int):
"""
slot의 ID를 나타내는 클래스입니다.
"""
pass
[문서]
class RmPageId(int):
"""
page의 ID를 나타내는 클래스입니다.
"""
pass
class RmPageBitmap:
"""
RM page의 bitmap을 나타내는 클래스입니다.
PF page content의 4바이트 이후의 `size` 바이트를 사용하는 layout view-like입니다.
layout view-like의 메서드를 사용 할 때는 항상 해당 PF page가 pin 되어있어야 합니다.
Attributes:
_page (PfPage):
bitmap이 들어있는 PF page.
_offset (int):
bitmap의 시작 오프셋.
_record_capacity (int):
record의 최대 수.
size (int):
bitmap의 크기.
_view (memoryview):
bitmap의 데이터.
"""
@staticmethod
def calc_size(record_capacity: int) -> int:
"""
record의 최대 수에 필요한 bitmap의 크기를 계산합니다.
예를 들어 bitmap의 크기가 2인 경우, record를 최대 16개까지 표현할 수 있습니다.
Args:
record_capacity (int):
record의 최대 수.
Returns:
int: bitmap의 크기.
"""
raise NotImplementedError
def __init__(self,
page: PfPage,
offset: int,
record_capacity: int,
) -> None:
"""
bitmap을 초기화합니다.
Args:
page (PfPage):
bitmap이 들어있는 PF page.
offset (int):
bitmap의 시작 오프셋.
record_capacity (int):
record의 최대 수.
"""
self._page = page
self._offset = offset
self._record_capacity = record_capacity
self.size = RmPageBitmap.calc_size(record_capacity)
self._view = memoryview(page.data)[offset: offset + self.size]
def find_first_zero_bit(self) -> int:
"""
bitmap에서 첫번째로 0인 bit의 index를 반환합니다.
찾지 못하는 경우는 없다고 가정합니다.
Returns:
int:
첫번째로 0인 bit의 index.
"""
raise NotImplementedError
def set_bit(self, index: int) -> None:
"""
bitmap의 대상 bit를 1로 설정합니다.
Args:
index (int):
대상 bit의 index.
"""
raise NotImplementedError
def clear_bit(self, index: int) -> None:
"""
bitmap의 대상 bit를 0으로 설정합니다.
Args:
index (int):
대상 bit의 index.
"""
raise NotImplementedError
def check_bit(self, index: int) -> bool:
"""
bitmap의 대상 bit가 1인지 여부를 반환합니다.
Args:
index (int):
대상 bit의 index.
Returns:
bool:
bit가 1인지 여부.
"""
raise NotImplementedError
def clear_all(self) -> None:
"""
bitmap의 모든 bit를 0으로 설정합니다.
"""
raise NotImplementedError
def is_full(self) -> bool:
"""
bitmap이 가득 찼는지 여부를 반환합니다.
bitmap이 모두 1인 경우가 아니라, record capacity 만큼 1이 연속으로 있는 경우를 가득 찼다고 합니다.
Returns:
bool:
bitmap이 가득 찼는지 여부.
"""
raise NotImplementedError
[문서]
class RmPage:
"""
RM page를 나타내는 클래스입니다.
Attributes:
_pf_page (PfPage):
RM page가 wrapping하고 있는 PF page.
pid (RmPageId):
RM page의 ID.
record_size (int):
record의 크기.
record_capacity (int):
record의 최대 수.
header (RmPageHeader):
RM page의 header.
_bitmap (RmPageBitmap):
RM page의 bitmap.
"""
[문서]
def __init__(self,
pf_page: PfPage,
pid: RmPageId,
record_size: int,
) -> None:
"""
RM page를 초기화합니다.
Args:
pf_page (PfPage):
RM page가 wrapping하고 있는 PF page.
pid (RmPageId):
RM page의 ID.
record_size (int):
record의 크기.
"""
self._pf_page = pf_page
self.pid = pid
self.record_size = record_size
self.record_capacity = self._calc_record_capacity()
self.header = RmPageHeader(
page=pf_page,
offset=PfPageHeader.SIZE,
limit=RmPageHeader.SIZE,
)
self._bitmap = RmPageBitmap(
page=pf_page,
offset=PfPageHeader.SIZE + RmPageHeader.SIZE,
record_capacity=self.record_capacity,
)
def _calc_record_capacity(self) -> int:
base = PfPageHeader.SIZE + RmPageHeader.SIZE
capacity = 0
while True:
bitmap_size = RmPageBitmap.calc_size(capacity + 1)
estimate = base + bitmap_size + self.record_size * (capacity + 1)
if estimate > self._pf_page.size:
break
capacity += 1
return capacity
[문서]
@contextmanager
def pinned(self):
"""
RM page를 pin 상태로 사용할 수 있는 context를 생성합니다.
PF page의 `pinned` 메서드를 사용합니다.
Examples:
>>> with page.pinned():
... ...
"""
raise NotImplementedError
[문서]
def get_record(self, sid: RmSlotId) -> RmRecord:
"""
대상 slot에 있는 record를 가져옵니다.
PF page의 `get_content` 메서드를 사용합니다.
Args:
sid (RmSlotId):
대상 record의 slot ID.
Returns:
RmRecord:
가져온 record.
Raises:
RmInvalidSidError:
`sid` 가 유효하지 않은 경우.
RmUnoccupiedSlotError:
`sid` 가 비어있는 경우.
"""
raise NotImplementedError
[문서]
def set_record(self,
sid: RmSlotId,
data: RmRecordData,
new: bool = False,
) -> None:
"""
대상 slot에 record data를 설정합니다.
bitmap의 `check_bit`, `set_bit` 메서드와 PF page의 `set_content` 메서드를 사용합니다.
Args:
sid (RmSlotId):
대상 record의 slot ID.
data (RmRecordData):
설정할 record의 data.
new (bool):
새로운 record를 생성할지 여부.
Raises:
RmInvalidSidError:
`sid` 가 유효하지 않은 경우.
RmUnoccupiedSlotError:
`sid` 가 비어있는 경우이면서 `new` 가 `False` 인 경우.
RmInvalidRecordDataSizeError:
`data` 의 크기가 유효하지 않은 경우.
"""
raise NotImplementedError
[문서]
def clear_record(self, sid: RmSlotId) -> None:
"""
record를 삭제합니다.
실제로 삭제하지는 않고, bitmap의 해당 bit를 0으로 설정합니다.
bitmap의 `clear_bit` 메서드를 사용합니다.
Args:
sid (RmSlotId):
대상 record의 slot ID.
Raises:
RmInvalidSidError:
`sid` 가 유효하지 않은 경우.
RmUnoccupiedSlotError:
`sid` 가 비어있는 경우.
"""
raise NotImplementedError
[문서]
def clear_all_records(self) -> None:
"""
모든 record를 삭제합니다.
실제로 삭제하지는 않고, bitmap의 모든 bit를 0으로 설정합니다.
bitmap의 `clear_all` 메서드를 사용합니다.
"""
raise NotImplementedError
[문서]
def find_first_free_sid(self) -> RmSlotId:
"""
첫번째로 비어있는 slot의 ID를 반환합니다.
bitmap의 `find_first_zero_bit` 메서드를 사용합니다.
Returns:
RmSlotId:
첫번째로 비어있는 slot의 ID.
"""
raise NotImplementedError
[문서]
def is_full(self) -> bool:
"""
slot이 가득 찼는지 여부를 반환합니다.
bitmap의 `is_full` 메서드를 사용합니다.
Returns:
bool:
slot이 가득 찼는지 여부.
"""
raise NotImplementedError