-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRecordFile.cc
278 lines (220 loc) · 6.59 KB
/
RecordFile.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/**
* Copyright (C) 2008 by The Regents of the University of California
* Redistribution of this file is permitted under the terms of the GNU
* Public License (GPL).
*
* @author Junghoo "John" Cho <cho AT cs.ucla.edu>
* @date 3/24/2008
*/
#include "Bruinbase.h"
#include "RecordFile.h"
#include <cstring>
using std::string;
//
// helper functions for page manipultation
//
// compute the pointer to the n'th slot in a page
static char* slotPtr(char* page, int n);
// read the record in the n'th slot in the page
static void readSlot(const char* page, int n, int& key, std::string& value);
// write the record to the n'th slot in the page
static void writeSlot(char* page, int n, int key, const std::string& value);
// get # records stored in the page
static int getRecordCount(const char* page);
// update # records stored in the page
static void setRecordCount(char* page, int count);
//
// helper functions for RecordId manipulation
//
// postfix record id iterator
RecordId operator++ (RecordId& rid, int)
{
RecordId prid(rid);
// if the end of a page is reached, move to the next page
if (++rid.sid >= RecordFile::RECORDS_PER_PAGE) {
rid.pid++;
rid.sid = 0;
}
return prid;
}
// prefix record id iterator
RecordId& operator++ (RecordId& rid)
{
// if the end of a page is reached, move to the next page
if (++rid.sid >= RecordFile::RECORDS_PER_PAGE) {
rid.pid++;
rid.sid = 0;
}
return rid;
}
// RecordId comparators
bool operator < (const RecordId& r1, const RecordId& r2)
{
if (r1.pid < r2.pid) return true;
if (r1.pid > r2.pid) return false;
return (r1.sid < r2.sid);
}
bool operator > (const RecordId& r1, const RecordId& r2)
{
if (r1.pid > r2.pid) return true;
if (r1.pid < r2.pid) return false;
return (r1.sid > r2.sid);
}
bool operator <= (const RecordId& r1, const RecordId& r2)
{
if (r1.pid < r2.pid) return true;
if (r1.pid > r2.pid) return false;
return (r1.sid <= r2.sid);
}
bool operator >= (const RecordId& r1, const RecordId& r2)
{
if (r1.pid > r2.pid) return true;
if (r1.pid < r2.pid) return false;
return (r1.sid >= r2.sid);
}
bool operator == (const RecordId& r1, const RecordId& r2)
{
return ((r1.pid == r2.pid) && (r1.sid == r2.sid));
}
bool operator != (const RecordId& r1, const RecordId& r2)
{
return ((r1.pid != r2.pid) || (r1.sid != r2.sid));
}
RecordFile::RecordFile()
{
erid.pid = 0;
erid.sid = 0;
}
RecordFile::RecordFile(const string& filename, char mode)
{
open(filename, mode);
}
RC RecordFile::open(const string& filename, char mode)
{
RC rc;
char page[PageFile::PAGE_SIZE];
// open the page file
if ((rc = pf.open(filename, mode)) < 0) return rc;
//
// in the rest of this function, we set the end record id
//
// get the end pid of the file
erid.pid = pf.endPid();
// if the end pid is zero, the file is empty.
// set the end record id to (0, 0).
if (erid.pid == 0) {
erid.sid = 0;
return 0;
}
// obtain # records in the last page to set sid of the end record id.
// read the last page of the file and get # records in the page.
// remeber that the id of the last page is endPid()-1 not endPid().
if ((rc = pf.read(--erid.pid, page)) < 0) {
// an error occurred during page read
erid.pid = erid.sid = 0;
pf.close();
return rc;
}
// get # records in the last page
erid.sid = getRecordCount(page);
if (erid.sid >= RECORDS_PER_PAGE) {
// the last page is full. advance the end record id to the next page.
erid.pid++;
erid.sid = 0;
}
return 0;
}
RC RecordFile::close()
{
erid.pid = 0;
erid.sid = 0;
return pf.close();
}
RC RecordFile::read(const RecordId& rid, int& key, string& value) const
{
RC rc;
char page[PageFile::PAGE_SIZE];
// check whether the rid is in the valid range
if (rid.pid < 0 || rid.pid > erid.pid) return RC_INVALID_RID;
if (rid.sid < 0 || rid.sid >= RecordFile::RECORDS_PER_PAGE) return RC_INVALID_RID;
if (rid >= erid) return RC_INVALID_RID;
// read the page containing the record
if ((rc = pf.read(rid.pid, page)) < 0) return rc;
// read the record from the slot in the page
readSlot(page, rid.sid, key, value);
return 0;
}
RC RecordFile::append(int key, const std::string& value, RecordId& rid)
{
RC rc;
char page[PageFile::PAGE_SIZE];
// unless we are writing to the the first slot of an empty page,
// we have to read the page first
if (erid.sid > 0) {
if ((rc = pf.read(erid.pid, page)) < 0) return rc;
} else {
// if this is the first slot of an empty page
// we can simply initialize the page with zeros
memset(page, 0, PageFile::PAGE_SIZE);
}
// write the record to the first empty slot
writeSlot(page, erid.sid, key, value);
// the first four bytes in the page stores # records in the page.
// update this number.
setRecordCount(page, erid.sid + 1);
// write the page to the disk
if ((rc = pf.write(erid.pid, page)) < 0) return rc;
// we need to output the rid of the record slot
rid = erid;
// advance the end record id by one to the next empty slot
++erid;
return 0;
}
const RecordId& RecordFile::endRid() const
{
return erid;
}
static int getRecordCount(const char* page)
{
int count;
// the first four bytes of a page contains # records in the page
memcpy(&count, page, sizeof(int));
return count;
}
static void setRecordCount(char* page, int count)
{
// the first four bytes of a page contains # records in the page
memcpy(page, &count, sizeof(int));
}
static char* slotPtr(char* page, int n)
{
// compute the location of the n'th slot in a page.
// remember that the first four bytes in a page is used to store
// # records in the page and each slot consists of an integer and
// a string of length MAX_VALUE_LENGTH
return (page+sizeof(int)) + (sizeof(int)+RecordFile::MAX_VALUE_LENGTH)*n;
}
static void readSlot(const char* page, int n, int& key, std::string& value)
{
// compute the location of the record
char *ptr = slotPtr(const_cast<char*>(page), n);
// read the key
memcpy(&key, ptr, sizeof(int));
// read the value
value.assign(ptr + sizeof(int));
}
static void writeSlot(char* page, int n, int key, const std::string& value)
{
// compute the location of the record
char *ptr = slotPtr(page, n);
// store the key
memcpy(ptr, &key, sizeof(int));
// store the value.
if ((int)value.size() >= RecordFile::MAX_VALUE_LENGTH) {
// when the string is longer than MAX_VALUE_LENGTH, truncate it.
memcpy(ptr + sizeof(int), value.c_str(), RecordFile::MAX_VALUE_LENGTH -1);
*(ptr + sizeof(int) + RecordFile::MAX_VALUE_LENGTH - 1) = 0;
} else {
strcpy(ptr + sizeof(int), value.c_str());
}
}