Skip to content

Commit d14f792

Browse files
authored
Merge pull request #1 from alexnad/storage_engine
Storage engine
2 parents 3d14872 + 26a473f commit d14f792

12 files changed

Lines changed: 596 additions & 164 deletions

File tree

src/storage/__init__.py

Whitespace-only changes.

src/storage/graph/__init__.py

Whitespace-only changes.

src/storage/graph/base.py

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
import struct
2+
3+
4+
class StorageHandlingException(Exception):
5+
def __init__(self, message, **kwargs):
6+
self.message = message
7+
self.kwargs = kwargs
8+
9+
def __str__(self):
10+
return self.message.format(self.kwargs)
11+
12+
13+
class FormatStringNotSupportedError(StorageHandlingException):
14+
def __init__(self, format_string):
15+
self.message = "Format string: '{format_sting}' not supported"
16+
super().__init__(self, self.message, format_string)
17+
18+
19+
class FormatStringError(StorageHandlingException):
20+
def __init__(self, format_string, table_length, map_size):
21+
self.message = "Format string '{format_string}' byte size " \
22+
"and table length:{table_length} " +\
23+
"do not match allocation map size {map_size}"
24+
super().__init__(self, self.message, format_string,
25+
table_length, map_size)
26+
27+
28+
class BlockOutOfRangeError(StorageHandlingException):
29+
def __init__(self, block, allocation_map_size):
30+
self.message = "Block:{block} is greater than the " + \
31+
"allocation map size:{allocation_map_size}"
32+
super().__init__(self, block, allocation_map_size)
33+
34+
35+
class GlobalAllocationMapMixin:
36+
"""
37+
This class defines basic functionality
38+
for global allocation maps keeping
39+
track and managing empty blocks in a file
40+
"""
41+
42+
def create_empty_table(self, number_of_elements):
43+
return (0 for i in range(number_of_elements))
44+
45+
def get_free_blocks(self, table, number_of_needed_blocks):
46+
"""
47+
Takes an allocation map
48+
Args:
49+
table (iterable(int)): representing the information in the
50+
allocation map should be an integer
51+
in range [-128;127]
52+
number_of_needed_blocks (int): how many free blocks are needed
53+
Returns
54+
list(int): numbers of free blocks
55+
"""
56+
result = []
57+
for index, byte in enumerate(table):
58+
if byte == 255:
59+
continue
60+
free_blocks_in_byte = self.free_bits[byte]
61+
62+
while free_blocks_in_byte:
63+
result.append(free_blocks_in_byte.pop(0))
64+
65+
if len(result) >= number_of_needed_blocks:
66+
break
67+
if len(result) >= number_of_needed_blocks:
68+
break
69+
70+
bits_in_byte = 8
71+
offset = bits_in_byte * index
72+
73+
return ((offset + bit) for bit in result)
74+
75+
def get_block_location(self, block):
76+
if block >= self.allocation_map_size:
77+
raise BlockOutOfRangeError(block, self.allocation_map_size)
78+
79+
byte = block // 8
80+
bit = block % 8
81+
82+
return byte, bit
83+
84+
def bit_flag(self, bit):
85+
flags = [128, 64, 32, 16, 8, 4, 2, 1]
86+
return flags[bit]
87+
88+
def free_block(self, block, table):
89+
"""
90+
Frees block by setting corresponding bit in map to 0
91+
Args:
92+
block (int): place of block in table
93+
table (list(int)): list from ints in range [-128;127]
94+
each int represent a byte from the table
95+
96+
raises BlockOutOfRangeError if the block number is higher
97+
than the allocation table max size
98+
"""
99+
byte, bit = self.get_block_location(block)
100+
101+
flag = self.bit_flag(bit)
102+
103+
if table[byte] & flag == flag:
104+
table[byte] ^= flag
105+
106+
def occupy_block(self, block, table):
107+
"""
108+
Occupies a block by setting corresponding bit in map to 1
109+
Args:
110+
block (int): place of block in table
111+
table (list(int)): list from ints in range [-128;127]
112+
each int represents a byte from the table
113+
114+
raises BlockOutOfRangeError if the block number is higher than
115+
the allocation table max size
116+
"""
117+
118+
byte, bit = self.get_block_location(block)
119+
120+
flag = self.bit_flag(bit)
121+
122+
if table[byte] & flag == 0:
123+
table[byte] ^= flag
124+
125+
126+
class FileIOMixin:
127+
"""
128+
This class defines basic functionality for handling
129+
reading and writing to a binary file
130+
"""
131+
132+
def read_bytes(self, place, size):
133+
with open(self.file_name, "rb") as f:
134+
f.seek(place)
135+
info = f.read(size)
136+
137+
return info
138+
139+
def write_bytes(self, info, place):
140+
with open(self.file_name, "wb") as f:
141+
f.seek(place)
142+
f.write(info)
143+
144+
145+
class DataConverterMixin:
146+
"""
147+
This class provides functionality for converting
148+
data (int and string) to and from bytes of fixed size
149+
for reading, writing from a binary file
150+
"""
151+
152+
# format strings defined in python docs for more information:
153+
# https://docs.python.org/3/library/struct.html#format-characters
154+
FORMAT_TYPE_SIZES = {
155+
'q': 8, # long long, 8 bytes
156+
'i': 4, # int , 4 bytes
157+
'h': 2, # short, 2 bytes
158+
'b': 1, # signed char , 1 byte
159+
}
160+
161+
INTEGER_OFFSET = {
162+
'q': 2 ** 63,
163+
'i': 2 ** 31,
164+
'h': 2 ** 15,
165+
'b': 2 ** 7
166+
}
167+
168+
STRING_END = '~' # represents end of string (e.g. '\0')
169+
170+
def encode_string(self, string, size):
171+
"""
172+
Args:
173+
string (str): string to be converted
174+
size (int): number of chars in string
175+
Returns:
176+
bytes object
177+
"""
178+
number_of_null_symbols = size - len(string)
179+
null_symbols = self.__class__.STRING_END * number_of_null_symbols
180+
string += null_symbols
181+
return string.encode(encoding='ascii')
182+
183+
def decode_string(self, bytes_string):
184+
string = bytes_string.decode(encoding='ascii')
185+
string_end = self.__class__.STRING_END
186+
return ''.join(char for char in string if char != string_end)
187+
188+
def encode_int(self, value, format_type):
189+
"""
190+
Args:
191+
value (int): the number to be converted
192+
format_type (string): one char representing the format string
193+
that will be used for the encoding and
194+
packing of the data in byte object
195+
Returns:
196+
bytes object
197+
198+
Raises FormatStringNotSupportedError
199+
"""
200+
if format_type not in self.__class__.FORMAT_TYPE_SIZES:
201+
return FormatStringNotSupportedError(format_type)
202+
203+
return struct.pack(value, format_type)
204+
205+
def decode_int(self, bytes_int, format_type):
206+
if format_type not in self.__class__.FORMAT_TYPE_SIZES:
207+
return FormatStringNotSupportedError(format_type)
208+
return struct.unpack(format_type, bytes_int)
209+
210+
def encode_table(self, table=None, format_type='b'):
211+
"""
212+
Args:
213+
table (iterable(int)): representing the information
214+
in the allocation map
215+
format_type (string): one char representing the format string
216+
that will be used for the encoding and
217+
packing of the data in byte object
218+
Returns:
219+
bytes: the information given in table ready
220+
to be writen in a binary file
221+
222+
Raises FormatStringError and FormatStringNotSupportedError
223+
"""
224+
225+
element_size = self.__class__.FORMAT_TYPE_SIZES.get(format_type)
226+
map_size_in_bytes = self.allocation_map_size / 8
227+
number_of_elements = map_size_in_bytes / element_size
228+
229+
if number_of_elements != len(table):
230+
raise FormatStringError
231+
232+
if not element_size:
233+
raise FormatStringNotSupportedError(format_type)
234+
if not table:
235+
table = self.create_empty_table(number_of_elements)
236+
237+
format_string = format_type * number_of_elements
238+
239+
return struct.pack(format_string, *table)
240+
241+
def decode_table(self, table_byte_string, format_type='b'):
242+
format_string = format_type * self.allocation_map_size
243+
return struct.unpack(format_string, table_byte_string)
244+
245+
246+
class FileManagementMixin:
247+
"""
248+
This class defines functionality for manging data location in files
249+
"""
250+
251+
def get_allocation_map_place(self, sequence_number):
252+
maps_size_in_bytes = self.allocation_map_size / 8
253+
return sequence_number * maps_size_in_bytes * (1 + self.element_size)
254+
255+
def get_element_place(self, sequence_number):
256+
maps_size_in_bytes = self.allocation_map_size / 8
257+
offset_form_page_begining = sequence_number % \
258+
maps_size_in_bytes * self.element_size
259+
260+
page_number = sequence_number // maps_size_in_bytes
261+
offset_from_file_begining = page_number * \
262+
maps_size_in_bytes * (1 + self.element_size)
263+
264+
return offset_form_page_begining + offset_from_file_begining
265+
266+
267+
class StoreBase(GlobalAllocationMapMixin, FileIOMixin,
268+
FileManagementMixin, DataConverterMixin):
269+
"""
270+
This class is the base for the node, edge and property store classes.
271+
It defines basic file structure and
272+
API for saving, retrieving and updating data.
273+
"""
274+
275+
def __init__(self, file_name, element_size, allocation_map_size=1024):
276+
"""
277+
Args:
278+
file_name (string): path to file store
279+
element_size: size of elements
280+
allocation_map_size: size of global allocation map
281+
"""
282+
self.file_name = file_name
283+
self.element_size = element_size
284+
self.allocation_map_size = allocation_map_size
285+
286+
def encode_element(self, element):
287+
"""
288+
Hook to encode custom elements to be writen to files
289+
"""
290+
291+
def decode_element(self, byte_string):
292+
"""
293+
Hook to decode custom elements read from file
294+
"""
295+
296+
def add_element(self, element):
297+
"""
298+
Hook to add an element
299+
Should return the id of the new element
300+
"""
301+
302+
def remove_element(self, element_id):
303+
"""
304+
Hook to remove elements
305+
"""
306+
307+
def get_element(self, element_id):
308+
"""
309+
Hook to access elements in store
310+
by a given id
311+
"""

src/storage/graph/free_blocks.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"0": [0, 1, 2, 3, 4, 5, 6, 7], "1": [0, 1, 2, 3, 4, 5, 6], "2": [0, 1, 2, 3, 4, 5, 7], "3": [0, 1, 2, 3, 4, 5], "4": [0, 1, 2, 3, 4, 6, 7], "5": [0, 1, 2, 3, 4, 6], "6": [0, 1, 2, 3, 4, 7], "7": [0, 1, 2, 3, 4], "8": [0, 1, 2, 3, 5, 6, 7], "9": [0, 1, 2, 3, 5, 6], "10": [0, 1, 2, 3, 5, 7], "11": [0, 1, 2, 3, 5], "12": [0, 1, 2, 3, 6, 7], "13": [0, 1, 2, 3, 6], "14": [0, 1, 2, 3, 7], "15": [0, 1, 2, 3], "16": [0, 1, 2, 4, 5, 6, 7], "17": [0, 1, 2, 4, 5, 6], "18": [0, 1, 2, 4, 5, 7], "19": [0, 1, 2, 4, 5], "20": [0, 1, 2, 4, 6, 7], "21": [0, 1, 2, 4, 6], "22": [0, 1, 2, 4, 7], "23": [0, 1, 2, 4], "24": [0, 1, 2, 5, 6, 7], "25": [0, 1, 2, 5, 6], "26": [0, 1, 2, 5, 7], "27": [0, 1, 2, 5], "28": [0, 1, 2, 6, 7], "29": [0, 1, 2, 6], "30": [0, 1, 2, 7], "31": [0, 1, 2], "32": [0, 1, 3, 4, 5, 6, 7], "33": [0, 1, 3, 4, 5, 6], "34": [0, 1, 3, 4, 5, 7], "35": [0, 1, 3, 4, 5], "36": [0, 1, 3, 4, 6, 7], "37": [0, 1, 3, 4, 6], "38": [0, 1, 3, 4, 7], "39": [0, 1, 3, 4], "40": [0, 1, 3, 5, 6, 7], "41": [0, 1, 3, 5, 6], "42": [0, 1, 3, 5, 7], "43": [0, 1, 3, 5], "44": [0, 1, 3, 6, 7], "45": [0, 1, 3, 6], "46": [0, 1, 3, 7], "47": [0, 1, 3], "48": [0, 1, 4, 5, 6, 7], "49": [0, 1, 4, 5, 6], "50": [0, 1, 4, 5, 7], "51": [0, 1, 4, 5], "52": [0, 1, 4, 6, 7], "53": [0, 1, 4, 6], "54": [0, 1, 4, 7], "55": [0, 1, 4], "56": [0, 1, 5, 6, 7], "57": [0, 1, 5, 6], "58": [0, 1, 5, 7], "59": [0, 1, 5], "60": [0, 1, 6, 7], "61": [0, 1, 6], "62": [0, 1, 7], "63": [0, 1], "64": [0, 2, 3, 4, 5, 6, 7], "65": [0, 2, 3, 4, 5, 6], "66": [0, 2, 3, 4, 5, 7], "67": [0, 2, 3, 4, 5], "68": [0, 2, 3, 4, 6, 7], "69": [0, 2, 3, 4, 6], "70": [0, 2, 3, 4, 7], "71": [0, 2, 3, 4], "72": [0, 2, 3, 5, 6, 7], "73": [0, 2, 3, 5, 6], "74": [0, 2, 3, 5, 7], "75": [0, 2, 3, 5], "76": [0, 2, 3, 6, 7], "77": [0, 2, 3, 6], "78": [0, 2, 3, 7], "79": [0, 2, 3], "80": [0, 2, 4, 5, 6, 7], "81": [0, 2, 4, 5, 6], "82": [0, 2, 4, 5, 7], "83": [0, 2, 4, 5], "84": [0, 2, 4, 6, 7], "85": [0, 2, 4, 6], "86": [0, 2, 4, 7], "87": [0, 2, 4], "88": [0, 2, 5, 6, 7], "89": [0, 2, 5, 6], "90": [0, 2, 5, 7], "91": [0, 2, 5], "92": [0, 2, 6, 7], "93": [0, 2, 6], "94": [0, 2, 7], "95": [0, 2], "96": [0, 3, 4, 5, 6, 7], "97": [0, 3, 4, 5, 6], "98": [0, 3, 4, 5, 7], "99": [0, 3, 4, 5], "100": [0, 3, 4, 6, 7], "101": [0, 3, 4, 6], "102": [0, 3, 4, 7], "103": [0, 3, 4], "104": [0, 3, 5, 6, 7], "105": [0, 3, 5, 6], "106": [0, 3, 5, 7], "107": [0, 3, 5], "108": [0, 3, 6, 7], "109": [0, 3, 6], "110": [0, 3, 7], "111": [0, 3], "112": [0, 4, 5, 6, 7], "113": [0, 4, 5, 6], "114": [0, 4, 5, 7], "115": [0, 4, 5], "116": [0, 4, 6, 7], "117": [0, 4, 6], "118": [0, 4, 7], "119": [0, 4], "120": [0, 5, 6, 7], "121": [0, 5, 6], "122": [0, 5, 7], "123": [0, 5], "124": [0, 6, 7], "125": [0, 6], "126": [0, 7], "127": [0], "128": [1, 2, 3, 4, 5, 6, 7], "129": [1, 2, 3, 4, 5, 6], "130": [1, 2, 3, 4, 5, 7], "131": [1, 2, 3, 4, 5], "132": [1, 2, 3, 4, 6, 7], "133": [1, 2, 3, 4, 6], "134": [1, 2, 3, 4, 7], "135": [1, 2, 3, 4], "136": [1, 2, 3, 5, 6, 7], "137": [1, 2, 3, 5, 6], "138": [1, 2, 3, 5, 7], "139": [1, 2, 3, 5], "140": [1, 2, 3, 6, 7], "141": [1, 2, 3, 6], "142": [1, 2, 3, 7], "143": [1, 2, 3], "144": [1, 2, 4, 5, 6, 7], "145": [1, 2, 4, 5, 6], "146": [1, 2, 4, 5, 7], "147": [1, 2, 4, 5], "148": [1, 2, 4, 6, 7], "149": [1, 2, 4, 6], "150": [1, 2, 4, 7], "151": [1, 2, 4], "152": [1, 2, 5, 6, 7], "153": [1, 2, 5, 6], "154": [1, 2, 5, 7], "155": [1, 2, 5], "156": [1, 2, 6, 7], "157": [1, 2, 6], "158": [1, 2, 7], "159": [1, 2], "160": [1, 3, 4, 5, 6, 7], "161": [1, 3, 4, 5, 6], "162": [1, 3, 4, 5, 7], "163": [1, 3, 4, 5], "164": [1, 3, 4, 6, 7], "165": [1, 3, 4, 6], "166": [1, 3, 4, 7], "167": [1, 3, 4], "168": [1, 3, 5, 6, 7], "169": [1, 3, 5, 6], "170": [1, 3, 5, 7], "171": [1, 3, 5], "172": [1, 3, 6, 7], "173": [1, 3, 6], "174": [1, 3, 7], "175": [1, 3], "176": [1, 4, 5, 6, 7], "177": [1, 4, 5, 6], "178": [1, 4, 5, 7], "179": [1, 4, 5], "180": [1, 4, 6, 7], "181": [1, 4, 6], "182": [1, 4, 7], "183": [1, 4], "184": [1, 5, 6, 7], "185": [1, 5, 6], "186": [1, 5, 7], "187": [1, 5], "188": [1, 6, 7], "189": [1, 6], "190": [1, 7], "191": [1], "192": [2, 3, 4, 5, 6, 7], "193": [2, 3, 4, 5, 6], "194": [2, 3, 4, 5, 7], "195": [2, 3, 4, 5], "196": [2, 3, 4, 6, 7], "197": [2, 3, 4, 6], "198": [2, 3, 4, 7], "199": [2, 3, 4], "200": [2, 3, 5, 6, 7], "201": [2, 3, 5, 6], "202": [2, 3, 5, 7], "203": [2, 3, 5], "204": [2, 3, 6, 7], "205": [2, 3, 6], "206": [2, 3, 7], "207": [2, 3], "208": [2, 4, 5, 6, 7], "209": [2, 4, 5, 6], "210": [2, 4, 5, 7], "211": [2, 4, 5], "212": [2, 4, 6, 7], "213": [2, 4, 6], "214": [2, 4, 7], "215": [2, 4], "216": [2, 5, 6, 7], "217": [2, 5, 6], "218": [2, 5, 7], "219": [2, 5], "220": [2, 6, 7], "221": [2, 6], "222": [2, 7], "223": [2], "224": [3, 4, 5, 6, 7], "225": [3, 4, 5, 6], "226": [3, 4, 5, 7], "227": [3, 4, 5], "228": [3, 4, 6, 7], "229": [3, 4, 6], "230": [3, 4, 7], "231": [3, 4], "232": [3, 5, 6, 7], "233": [3, 5, 6], "234": [3, 5, 7], "235": [3, 5], "236": [3, 6, 7], "237": [3, 6], "238": [3, 7], "239": [3], "240": [4, 5, 6, 7], "241": [4, 5, 6], "242": [4, 5, 7], "243": [4, 5], "244": [4, 6, 7], "245": [4, 6], "246": [4, 7], "247": [4], "248": [5, 6, 7], "249": [5, 6], "250": [5, 7], "251": [5], "252": [6, 7], "253": [6], "254": [7], "255": []}

0 commit comments

Comments
 (0)