2023-09-08 15:52:05 +08:00
|
|
|
import os
|
|
|
|
|
import hashlib
|
|
|
|
|
import re
|
|
|
|
|
from typing import Tuple, List
|
|
|
|
|
from .chunk_store import ChunkStore
|
|
|
|
|
from .chunk import ChunkID, PositionFileRange, PositionType
|
|
|
|
|
from ..object import HashValue
|
|
|
|
|
from .tracker import ChunkTracker
|
2023-09-11 00:46:56 +08:00
|
|
|
from .chunk_list import ChunkList
|
2023-09-08 15:52:05 +08:00
|
|
|
|
2023-09-11 00:46:56 +08:00
|
|
|
class ChunkListWriter:
|
2023-09-08 15:52:05 +08:00
|
|
|
def __init__(self, chunk_store: ChunkStore, chunk_tracker: ChunkTracker):
|
|
|
|
|
self.chunk_store = chunk_store
|
|
|
|
|
self.chunk_tracker = chunk_tracker
|
|
|
|
|
|
|
|
|
|
def create_chunk_list_from_file(
|
|
|
|
|
self, file_path: str, chunk_size: int, restore: bool
|
2023-09-11 00:46:56 +08:00
|
|
|
) -> ChunkList:
|
2023-09-08 15:52:05 +08:00
|
|
|
assert (
|
|
|
|
|
chunk_size % (1024 * 1024) == 0
|
|
|
|
|
), "chunk size should be an integral multiple of 1MB"
|
|
|
|
|
chunk_list = []
|
|
|
|
|
hash_obj = hashlib.sha256()
|
|
|
|
|
|
|
|
|
|
with open(file_path, "rb") as file:
|
|
|
|
|
while True:
|
|
|
|
|
chunk = file.read(chunk_size)
|
|
|
|
|
if not chunk:
|
|
|
|
|
break
|
|
|
|
|
chunk_id = ChunkID.hash_data(chunk)
|
|
|
|
|
chunk_list.append(chunk_id)
|
|
|
|
|
|
|
|
|
|
hash_obj.update(chunk)
|
|
|
|
|
|
|
|
|
|
if restore:
|
|
|
|
|
self.chunk_tracker.add_position(
|
|
|
|
|
chunk_id, file_path, PositionType.ChunkStore
|
|
|
|
|
)
|
|
|
|
|
self.chunk_store.put_chunk(chunk_id, chunk)
|
|
|
|
|
else:
|
|
|
|
|
file_range = PositionFileRange(
|
|
|
|
|
file_path, file.tell() - chunk_size, chunk_size
|
|
|
|
|
)
|
|
|
|
|
self.chunk_tracker.add_position(
|
2023-09-11 20:32:52 +08:00
|
|
|
chunk_id, str(file_range), PositionType.FileRange
|
2023-09-08 15:52:05 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
file_hash = HashValue(hash_obj.digest())
|
|
|
|
|
print(f"calc file hash: {file_path}, {file_hash}")
|
2023-09-11 00:46:56 +08:00
|
|
|
|
|
|
|
|
return ChunkList(chunk_list, file_hash)
|
2023-09-08 15:52:05 +08:00
|
|
|
|
|
|
|
|
def create_chunk_list_from_text(
|
|
|
|
|
self, text: str, chunk_max_words: int, separator_chars: str = ".,"
|
|
|
|
|
) -> Tuple[List[ChunkID], HashValue]:
|
|
|
|
|
text_list = self._split_text_list(text, chunk_max_words, separator_chars)
|
|
|
|
|
chunk_list = []
|
|
|
|
|
hash_obj = hashlib.sha256()
|
2023-09-11 00:46:56 +08:00
|
|
|
|
2023-09-08 15:52:05 +08:00
|
|
|
for text in text_list:
|
2023-09-11 00:46:56 +08:00
|
|
|
chunk_bytes = text.encode("utf-8")
|
2023-09-08 15:52:05 +08:00
|
|
|
hash_obj.update(chunk_bytes)
|
2023-09-11 00:46:56 +08:00
|
|
|
|
2023-09-08 15:52:05 +08:00
|
|
|
chunk_id = ChunkID.hash_data(chunk_bytes)
|
|
|
|
|
chunk_list.append(chunk_id)
|
2023-09-11 00:46:56 +08:00
|
|
|
self.chunk_tracker.add_position(chunk_id, "", PositionType.ChunkStore)
|
2023-09-08 15:52:05 +08:00
|
|
|
self.chunk_store.put_chunk(chunk_id, chunk_bytes)
|
2023-09-11 00:46:56 +08:00
|
|
|
|
2023-09-08 15:52:05 +08:00
|
|
|
hash = HashValue(hash_obj.digest())
|
2023-09-11 00:46:56 +08:00
|
|
|
return ChunkList(chunk_list, hash)
|
2023-09-08 15:52:05 +08:00
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _split_text_list(
|
|
|
|
|
text: str, chunk_max_words: int, separator_chars: str = ".,"
|
|
|
|
|
) -> List[str]:
|
|
|
|
|
sentences = re.split(f"[{separator_chars}]", text)
|
|
|
|
|
chunk_list = []
|
|
|
|
|
chunk = []
|
|
|
|
|
word_count = 0
|
|
|
|
|
for sentence in sentences:
|
|
|
|
|
words = sentence.split()
|
|
|
|
|
for word in words:
|
|
|
|
|
if word_count < chunk_max_words:
|
|
|
|
|
chunk.append(word)
|
|
|
|
|
word_count += 1
|
|
|
|
|
else:
|
|
|
|
|
chunk_list.append(" ".join(chunk))
|
|
|
|
|
chunk = [word]
|
|
|
|
|
word_count = 1
|
|
|
|
|
if chunk:
|
|
|
|
|
chunk_list.append(" ".join(chunk))
|
2023-09-11 00:46:56 +08:00
|
|
|
return chunk_list
|