Build a persistent key-value store that can save and reload its contents through a file-system abstraction. You receive a mock file-system API and helper functions for converting primitive values to and from bytes. Create a custom dictionary serialization format; JSON, pickle, and comparable built-in serializers are not allowed.
class FileSystem:
def save_blob(self, data: bytes) -> None:
"""Write bytes to the file system."""
pass
def get_blob(self) -> bytes:
"""Read bytes from the file system."""
pass
# Supplied utility functions
def serialize_int(value: int) -> bytes:
"""Encode an integer as bytes."""
pass
def deserialize_int(data: bytes) -> int:
"""Decode bytes as an integer."""
pass
def serialize_str(value: str) -> bytes:
"""Encode a string as bytes."""
pass
def deserialize_str(data: bytes) -> str:
"""Decode bytes as a string."""
pass
Create a KVStore class with these operations:
class KVStore:
def __init__(self, file_system: FileSystem):
"""Set up the store with a file-system object."""
pass
def put(self, key: str, value: str) -> None:
"""Add or replace a key-value pair in memory."""
pass
def get(self, key: str) -> str:
"""Return the value associated with a key."""
pass
def shutdown(self) -> None:
"""Encode the complete in-memory store and persist it."""
pass
def restore(self) -> None:
"""Read the persisted representation and rebuild the store."""
pass
fs = FileSystem()
store = KVStore(fs)
store.put("user", "Ada:LovE")
store.put("location", "San,Jose")
store.put("line\nbreak", "left=right")
store.shutdown()
reloaded = KVStore(fs)
reloaded.restore()
assert reloaded.get("user") == "Ada:LovE"
assert reloaded.get("location") == "San,Jose"
assert reloaded.get("line\nbreak") == "left=right"
Each assertion checks that punctuation and newlines survive the persistence round trip without alteration.
Input:
store = KVStore(fs)
store.put("", "")
store.shutdown()
restored = KVStore(fs)
restored.restore()
restored.get("")
Output:
""
An empty key and an empty value are both valid and must remain distinguishable from missing data.
Input:
store = KVStore(fs)
store.put("emoji", "☕")
store.put("path", "a:b,c=d")
store.shutdown()
restored = KVStore(fs)
restored.restore()
Output:
restored.get("emoji") == "☕"
restored.get("path") == "a:b,c=d"
The restored values retain Unicode characters and all delimiter characters.
key:value cannot safely represent a key like part:2 or a value containing =.:, ,, and = inside keys or valuesput, shutdown, and restore operationsSuppose no individual file may contain more than 1 KB. How would you extend the design so callers can persist and restore stores whose serialized form is larger than that limit, without needing to know how many files are involved?
Use a predictable ordering scheme such as chunk_0, chunk_1, chunk_2, and a separate metadata file such as metadata or _meta.
The file-system abstraction now addresses files by name:
from typing import List
class FileSystem:
def save_blob(self, filename: str, data: bytes) -> None:
"""Write bytes to the named file."""
pass
def get_blob(self, filename: str) -> bytes:
"""Read bytes from the named file."""
pass
def list_files(self) -> List[str]:
"""Return available file names; useful for cleanup or diagnostics."""
pass