1
1
use std:: fs:: File ;
2
2
use std:: os:: unix:: fs:: { FileExt , OpenOptionsExt } ;
3
-
3
+ use std:: path:: Path ;
4
+ use crate :: util:: { Deserialize , Serialize } ;
5
+ use byteorder:: { ByteOrder , LittleEndian } ;
4
6
pub const PageSize : u32 = 4096 ;
7
+ pub const META_PAGE_NUMBER : u16 = 0 ;
8
+ pub const PAGE_SIZE : u16 = 4096 ;
9
+
5
10
pub struct Dal {
6
11
file : File ,
7
- pageSize : u32
12
+ pageSize : u16 ,
13
+ freeList : FreeList ,
14
+ meta : Meta
8
15
}
9
16
10
17
impl Dal {
11
- pub fn new ( path : & str , pageSize : u32 ) -> Self {
12
- let file = std:: fs:: OpenOptions :: new ( )
13
- . create ( true )
14
- . write ( true )
15
- . append ( true )
16
- . mode ( 0o666 )
17
- . open ( path)
18
- . unwrap ( ) ;
19
-
20
- Dal {
21
- file,
22
- pageSize
18
+ pub fn new ( path : & str ) -> Self {
19
+ if ( !Path :: new ( path) . exists ( ) ) {
20
+ // database file doesn't exist, create a new database file
21
+ let file = std:: fs:: OpenOptions :: new ( )
22
+ . create ( true )
23
+ . write ( true )
24
+ . read ( true )
25
+ . append ( true )
26
+ . mode ( 0o666 )
27
+ . open ( path)
28
+ . unwrap ( ) ;
29
+
30
+ let mut freeList = FreeList :: new ( ) ;
31
+ let mut meta = Meta :: new ( ) ;
32
+
33
+ let mut dal = Dal {
34
+ file,
35
+ pageSize : PAGE_SIZE , // PAGE_SIZE is 4KB (4096)
36
+ meta,
37
+ freeList,
38
+ } ;
39
+
40
+ // set freelist to page 1 and increment maxPage in the freelist struct
41
+ let freeListPageNumber = dal. freeList . getNextPageNumber ( ) ;
42
+
43
+ dal. meta . freeListPage = freeListPageNumber;
44
+ dal
45
+ } else {
46
+ // file exists, read meta to find freelist page and deserialize accordingly
47
+ let file = std:: fs:: OpenOptions :: new ( )
48
+ . write ( true )
49
+ . read ( true )
50
+ . append ( true )
51
+ . open ( path)
52
+ . unwrap ( ) ;
53
+
54
+ let mut freeList = FreeList :: new ( ) ;
55
+ let mut meta = Meta :: new ( ) ;
56
+ let mut dal = Dal {
57
+ file,
58
+ pageSize : PAGE_SIZE ,
59
+ meta,
60
+ freeList,
61
+ } ;
62
+
63
+ // read meta from disk and store in dal struct
64
+ dal. readMeta ( ) ;
65
+
66
+ // read freeList from and store in dal struct
67
+ dal. readFreeList ( ) ;
68
+ dal
23
69
}
24
70
}
25
71
26
- pub fn allocateEmptyPage ( & self , pageNumber : Option < u64 > ) -> Page {
72
+ pub fn allocateEmptyPage ( & self , pageNumber : u16 ) -> Page {
27
73
Page :: new ( pageNumber)
28
74
}
29
75
30
- fn readPage ( & self , pageNumber : u64 ) -> Page {
31
- let mut page = self . allocateEmptyPage ( Some ( pageNumber) ) ;
32
- let offset = pageNumber * self . pageSize as u64 ;
76
+ fn readPage ( & self , pageNumber : u16 ) -> Page {
77
+ let mut page = self . allocateEmptyPage ( pageNumber) ;
78
+ let offset = pageNumber as u64 * self . pageSize as u64 ;
33
79
self . file . read_at ( & mut page. data , offset) . unwrap ( ) ;
34
80
page
35
81
}
36
82
37
83
pub fn writePage ( & mut self , page : & mut Page ) {
38
- let offset = page. pageNumber * self . pageSize as u64 ;
84
+ let offset = page. pageNumber as u64 * self . pageSize as u64 ;
39
85
self . file . write_at ( & page. data , offset) . unwrap ( ) ;
40
86
}
41
87
88
+ pub fn writeMeta ( & mut self ) -> Page {
89
+ // meta page is 0
90
+ let mut page = self . allocateEmptyPage ( META_PAGE_NUMBER ) ; // META_PAGE_NUMBER = 0
91
+
92
+ // serialize the meta struct into the `page.data` buffer
93
+ self . meta . serialize ( & mut page. data [ ..] ) ;
94
+
95
+ // write the page to disk
96
+ self . writePage ( & mut page) ;
97
+
98
+ page
99
+ }
100
+
101
+ pub fn readMeta ( & mut self ) {
102
+ // read page
103
+ let page = self . readPage ( META_PAGE_NUMBER ) ; // META_PAGE_NUMBER = 0
104
+
105
+ // create an empty meta
106
+ let mut meta = Meta :: new ( ) ;
107
+
108
+ // deserialize what's in the read `page.data` into the empty meta struct
109
+ meta. deserialize ( & page. data ) ;
110
+ self . meta = meta;
111
+ }
112
+
113
+ pub fn writeFreeList ( & mut self ) -> Page {
114
+ // get freelist page from meta
115
+ let freeListPage = self . meta . freeListPage ;
116
+
117
+ let mut page = self . allocateEmptyPage ( freeListPage) ;
118
+
119
+ // serialize free list into the empty page
120
+ self . freeList . serialize ( & mut page. data ) ;
121
+
122
+ // write page
123
+ self . writePage ( & mut page) ;
124
+ page
125
+ }
126
+
127
+ pub fn readFreeList ( & mut self ) {
128
+ // get freelist page from meta
129
+ let freeListPage = self . meta . freeListPage ;
130
+
131
+ // read freelist page
132
+ let mut page = self . readPage ( freeListPage) ;
133
+
134
+ // deserialize free list page
135
+ self . freeList . deserialize ( & mut page. data ) ;
136
+
137
+ }
138
+
139
+ pub fn getNextPageNumber ( & mut self ) -> u16 {
140
+ self . freeList . getNextPageNumber ( )
141
+
142
+ }
143
+
144
+ pub fn releasePage ( & mut self , pageNumber : u16 ) {
145
+ self . freeList . releasePage ( pageNumber) ;
146
+ }
147
+ }
148
+
149
+ impl Drop for Dal {
150
+ fn drop ( & mut self ) {
151
+ self . writeMeta ( ) ;
152
+ self . writeFreeList ( ) ;
153
+ }
42
154
}
43
155
44
156
pub struct Page {
45
- pub pageNumber : u64 ,
157
+ pub pageNumber : u16 ,
46
158
pub data : Vec < u8 >
47
159
}
48
160
49
161
impl Page {
50
- pub fn new ( pageNumber : Option < u64 > ) -> Self {
162
+ pub fn new ( pageNumber : u16 ) -> Self {
51
163
Page {
52
- pageNumber : pageNumber . unwrap_or ( 0 ) , // if no page number, set to 0
164
+ pageNumber, // if no page number, set to 0
53
165
data : vec ! [ 0_u8 ; PageSize as usize ] // create a 4kB zero filled vector
54
166
}
55
167
}
168
+ }
169
+
170
+
171
+ #[ derive( Debug ) ]
172
+ struct FreeList {
173
+ maxPage : u16 , // Holds the maximum page allocated. maxPage * PageSize = databaseFileSize
174
+ releasedPages : Vec < u16 > // Pages that were previously allocated but are now free
175
+ }
176
+
177
+ impl FreeList {
178
+ pub fn new ( ) -> Self {
179
+ Self {
180
+ maxPage : 0 ,
181
+ releasedPages : vec ! [ ]
182
+ }
183
+ }
184
+
185
+ pub fn getNextPageNumber ( & mut self ) -> u16 {
186
+ // if possible, get pages from released pages first, else increment maxPage and return it
187
+ if let Some ( releasedPageId) = self . releasedPages . pop ( ) {
188
+ releasedPageId
189
+ } else {
190
+ self . maxPage += 1 ;
191
+ self . maxPage
192
+ }
193
+ }
194
+
195
+ pub fn releasePage ( & mut self , pageNumber : u16 ) {
196
+ self . releasedPages . push ( pageNumber)
197
+ }
198
+ }
199
+
200
+ #[ derive( Debug ) ]
201
+ struct Meta {
202
+ freeListPage : u16
203
+ }
204
+
205
+ impl Meta {
206
+ pub fn new ( ) -> Self {
207
+ Self {
208
+ // start with a seed value of 0, this may change later on
209
+ freeListPage : 0
210
+ }
211
+ }
212
+ }
213
+
214
+ impl Serialize < Meta > for Meta {
215
+ fn serialize ( & self , buffer : & mut [ u8 ] ) {
216
+ // To serialize a Meta
217
+ // - 8 bytes for the freelist page
218
+ let mut cursor: usize = 0 ;
219
+ LittleEndian :: write_u16 ( & mut buffer[ cursor..] , self . freeListPage ) ;
220
+ cursor += 8 ;
221
+ }
222
+ }
223
+
224
+ impl Deserialize < Meta > for Meta {
225
+ fn deserialize ( & mut self , buffer : & [ u8 ] ) {
226
+ let mut cursor: usize = 0 ;
227
+ // read 8 bytes for freelist cursor at cursor `cursor`
228
+ let freeListPage = LittleEndian :: read_u16 ( & buffer[ cursor..] ) ;
229
+ cursor += 2 ;
230
+
231
+ self . freeListPage = freeListPage
232
+ }
233
+ }
234
+
235
+ impl Serialize < FreeList > for FreeList {
236
+ fn serialize ( & self , buffer : & mut [ u8 ] ) {
237
+
238
+ // To serialize a Meta
239
+ // - 2 bytes for max page (u16)
240
+ // - 2 bytes for length of released list (vector in our case)
241
+ // - 2 bytes for each released page number
242
+
243
+ // write max page (2 bytes)
244
+ let mut cursor: usize = 0 ;
245
+ LittleEndian :: write_u16 ( & mut buffer[ cursor ..] , self . maxPage ) ;
246
+ cursor += 2 ;
247
+
248
+ // write length of released pages (2 bytes)
249
+ LittleEndian :: write_u16 ( & mut buffer[ cursor ..] , self . releasedPages . len ( ) as u16 ) ;
250
+ cursor += 2 ;
251
+
252
+ // for each released page, write the released page number (2 bytes)
253
+ for pgNumber in & self . releasedPages {
254
+ LittleEndian :: write_u16 ( & mut buffer[ cursor ..] , * pgNumber) ;
255
+ cursor += 2 ;
256
+ }
257
+ }
258
+ }
259
+
260
+ impl Deserialize < FreeList > for FreeList {
261
+ fn deserialize ( & mut self , buffer : & [ u8 ] ) {
262
+
263
+ // To deserialize a Meta
264
+ // - 2 bytes for max page (u16)
265
+ // - 2 bytes for length of released list (vector in our case)
266
+ // - 2 bytes for each released page number
267
+
268
+ // read max page (2 bytes)
269
+ let mut cursor : usize = 0 ;
270
+ let maxPage = LittleEndian :: read_u16 ( & buffer) ;
271
+ cursor +=2 ;
272
+
273
+ // read length of released pages (2 bytes)
274
+ let numReleasedPages = LittleEndian :: read_u16 ( & buffer[ cursor .. ] ) ;
275
+ cursor +=2 ;
276
+
277
+ let mut releasedPages : Vec < u16 > = Vec :: with_capacity ( numReleasedPages as usize ) ;
278
+
279
+ // for each released page, read the released page number (2 bytes)
280
+ for _ in 0 ..numReleasedPages {
281
+ let releasedPage = LittleEndian :: read_u16 ( & buffer[ cursor as usize ..] ) ;
282
+ releasedPages. push ( releasedPage) ;
283
+ cursor += 2
284
+
285
+ }
286
+
287
+ self . maxPage = maxPage ;
288
+ self . releasedPages = releasedPages
289
+ }
56
290
}
0 commit comments