1 | /* |
2 | * PROGRAM: JRD Access Method |
3 | * MODULE: RecordNumber.h |
4 | * DESCRIPTION: Handler class for table record number |
5 | * |
6 | * The contents of this file are subject to the Initial |
7 | * Developer's Public License Version 1.0 (the "License"); |
8 | * you may not use this file except in compliance with the |
9 | * License. You may obtain a copy of the License at |
10 | * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. |
11 | * |
12 | * Software distributed under the License is distributed AS IS, |
13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. |
14 | * See the License for the specific language governing rights |
15 | * and limitations under the License. |
16 | * |
17 | * The Original Code was created by Nickolay Samofatov |
18 | * for the Firebird Open Source RDBMS project. |
19 | * |
20 | * Copyright (c) 2004 Nickolay Samofatov <nickolay@broadviewsoftware.com> |
21 | * and all contributors signed below. |
22 | * |
23 | * All Rights Reserved. |
24 | * Contributor(s): ______________________________________. |
25 | * |
26 | * |
27 | * |
28 | */ |
29 | |
30 | #ifndef JRD_RECORDNUMBER_H |
31 | #define JRD_RECORDNUMBER_H |
32 | |
33 | #include "../common/gdsassert.h" |
34 | |
35 | const SINT64 BOF_NUMBER = QUADCONST(-1); |
36 | |
37 | // This class is to be used everywhere you may need to handle record numbers. We |
38 | // deliberately not define implicit conversions to and from integer to allow |
39 | // compiler check errors on our migration path from 32-bit to 64-bit record |
40 | // numbers. |
41 | class RecordNumber |
42 | { |
43 | public: |
44 | // Packed record number represents common layout of RDB$DB_KEY and BLOB ID. |
45 | // To ensure binary compatibility with old (pre-ODS11) databases it differs |
46 | // for big- and little-endian machines. |
47 | class Packed |
48 | { |
49 | #ifdef __clang__ |
50 | #pragma clang diagnostic push |
51 | #pragma clang diagnostic ignored "-Wunused-private-field" |
52 | #endif |
53 | |
54 | #ifdef WORDS_BIGENDIAN |
55 | private: |
56 | UCHAR bid_number_up; // Upper byte of 40-bit record number |
57 | UCHAR bid_reserved_for_relation; // Reserved for future |
58 | public: |
59 | USHORT bid_relation_id; // Relation id (or null) |
60 | private: |
61 | ULONG bid_number; // Lower bytes of 40-bit record number |
62 | // or 32-bit temporary ID of blob or array |
63 | #else |
64 | public: |
65 | USHORT bid_relation_id; /* Relation id (or null) */ |
66 | |
67 | private: |
68 | UCHAR bid_reserved_for_relation; /* Reserved for future expansion of relation space. */ |
69 | UCHAR bid_number_up; // Upper byte of 40-bit record number |
70 | ULONG bid_number; // Lower bytes of 40-bit record number |
71 | // or 32-bit temporary ID of blob or array |
72 | #endif |
73 | |
74 | #ifdef __clang__ |
75 | #pragma clang diagnostic pop |
76 | #endif |
77 | |
78 | public: |
79 | ULONG& bid_temp_id() |
80 | { |
81 | return bid_number; |
82 | } |
83 | |
84 | ULONG bid_temp_id() const |
85 | { |
86 | return bid_number; |
87 | } |
88 | |
89 | // Handle encoding of record number for RDB$DB_KEY and BLOB ID structure. |
90 | // BLOB ID is stored in database thus we do encode large record numbers |
91 | // in a manner which preserves backward compatibility with older ODS. |
92 | // The same applies to bid_decode routine below. |
93 | inline void bid_encode(SINT64 value) |
94 | { |
95 | // Use explicit casts to suppress 64-bit truncation warnings |
96 | // Store lower 32 bits of number |
97 | bid_number = static_cast<ULONG>(value); |
98 | // Store high 8 bits of number |
99 | bid_number_up = static_cast<UCHAR>(value >> 32); |
100 | } |
101 | |
102 | inline SINT64 bid_decode() const |
103 | { |
104 | return bid_number + (static_cast<FB_UINT64>(bid_number_up) << 32); |
105 | } |
106 | }; |
107 | |
108 | // Default constructor. |
109 | inline RecordNumber() : value(0), valid(false) {} |
110 | |
111 | // Copy constructor |
112 | inline RecordNumber(const RecordNumber& from) : value(from.value), valid(from.valid) {} |
113 | |
114 | // Explicit constructor from 64-bit record number value |
115 | inline explicit RecordNumber(SINT64 number) : value(number), valid(true) {} |
116 | |
117 | // Assignment operator |
118 | inline RecordNumber& operator =(const RecordNumber& from) |
119 | { |
120 | value = from.value; |
121 | valid = from.valid; |
122 | return *this; |
123 | } |
124 | |
125 | inline bool operator ==(const RecordNumber& other) const |
126 | { |
127 | return value == other.value; |
128 | } |
129 | |
130 | inline bool operator !=(const RecordNumber& other) const |
131 | { |
132 | return value != other.value; |
133 | } |
134 | |
135 | inline bool operator > (const RecordNumber& other) const |
136 | { |
137 | return value > other.value; |
138 | } |
139 | |
140 | inline bool operator < (const RecordNumber& other) const |
141 | { |
142 | return value < other.value; |
143 | } |
144 | |
145 | inline bool operator >= (const RecordNumber& other) const |
146 | { |
147 | return value >= other.value; |
148 | } |
149 | |
150 | inline bool operator <= (const RecordNumber& other) const |
151 | { |
152 | return value <= other.value; |
153 | } |
154 | |
155 | inline void decrement() { value--; } |
156 | |
157 | inline void increment() { value++; } |
158 | |
159 | inline SINT64 getValue() const { return value; } |
160 | |
161 | inline void setValue(SINT64 avalue) { value = avalue; } |
162 | |
163 | bool isBof() const { return value == BOF_NUMBER; } |
164 | |
165 | bool isValid() const { return valid; } |
166 | |
167 | inline bool checkNumber(USHORT records_per_page, // ~400 (8k page) |
168 | USHORT data_pages_per_pointer_page) const // ~2000 (8k page) |
169 | { |
170 | // We limit record number value to 40 bits and make sure decomposed value |
171 | // fits into 3 USHORTs. This all makes practical table size limit (not |
172 | // counting allocation threshold and overhead) roughtly equal to: |
173 | // 16k page - 20000 GB |
174 | // 8k page - 10000 GB |
175 | // 4k page - 2500 GB |
176 | // 2k page - 600 GB |
177 | // 1k page - 150 GB |
178 | // Large page size values are recommended for large databases because |
179 | // page allocators are generally linear. |
180 | return value < QUADCONST(0x10000000000) && |
181 | value < (SINT64) MAX_USHORT * records_per_page * data_pages_per_pointer_page; |
182 | } |
183 | |
184 | inline void (USHORT records_per_page, // ~400 (8k page) |
185 | USHORT data_pages_per_pointer_page, // ~2000 (8k page) |
186 | SSHORT& line, |
187 | SSHORT& slot, |
188 | USHORT& pp_sequence) const |
189 | { |
190 | // Use explicit casts to suppress 64-bit truncation warnings |
191 | line = static_cast<SSHORT>(value % records_per_page); |
192 | const ULONG sequence = static_cast<ULONG>(value / records_per_page); |
193 | slot = sequence % data_pages_per_pointer_page; |
194 | pp_sequence = sequence / data_pages_per_pointer_page; |
195 | } |
196 | |
197 | inline void compose(USHORT records_per_page, // ~400 (8k page) |
198 | USHORT data_pages_per_pointer_page, // ~2000 (8k page) |
199 | SSHORT line, |
200 | SSHORT slot, |
201 | USHORT pp_sequence) |
202 | { |
203 | value = (((SINT64) pp_sequence) * data_pages_per_pointer_page + slot) * records_per_page + line; |
204 | } |
205 | |
206 | // Handle encoding of record number for RDB$DB_KEY and BLOB ID structure. |
207 | inline void bid_encode(Packed* recno) const |
208 | { |
209 | recno->bid_encode(value); |
210 | } |
211 | |
212 | inline void bid_decode(const Packed* recno) |
213 | { |
214 | value = recno->bid_decode(); |
215 | } |
216 | |
217 | inline void setValid(bool to_value) |
218 | { |
219 | valid = to_value; |
220 | } |
221 | |
222 | private: |
223 | // Use signed value because negative values are widely used as flags in the |
224 | // engine. Since page number is the signed 32-bit integer and it is also may |
225 | // be stored in this structure we want sign extension to take place. |
226 | SINT64 value; |
227 | bool valid; |
228 | }; |
229 | |
230 | namespace Jrd |
231 | { |
232 | /* Blob id. A blob has two states -- temporary and permanent. In each |
233 | case, the blob id is 8 bytes (2 longwords) long. In the case of a |
234 | temporary blob, the first word is NULL and the second word points to |
235 | an internal blob block. In the case of a permanent blob, the first |
236 | word contains the relation id of the blob and the second the record |
237 | number of the first segment-clump. The two types of blobs can be |
238 | reliably distinguished by a zero or non-zero relation id. */ |
239 | |
240 | // This structure must occupy 8 bytes |
241 | struct bid |
242 | { |
243 | // This is how bid structure represented in public API. |
244 | // Must be present to enforce alignment rules when structure is declared on stack |
245 | struct bid_quad_struct |
246 | { |
247 | ULONG bid_quad_high; |
248 | ULONG bid_quad_low; |
249 | }; |
250 | union // anonymous union |
251 | { |
252 | // Internal decomposition of the structure |
253 | RecordNumber::Packed bid_internal; |
254 | bid_quad_struct bid_quad; |
255 | }; |
256 | |
257 | ULONG& bid_temp_id() |
258 | { |
259 | // Make sure that compiler packed structure like we wanted |
260 | fb_assert(sizeof(*this) == 8); |
261 | |
262 | return bid_internal.bid_temp_id(); |
263 | } |
264 | |
265 | ULONG bid_temp_id() const |
266 | { |
267 | // Make sure that compiler packed structure like we wanted |
268 | fb_assert(sizeof(*this) == 8); |
269 | |
270 | return bid_internal.bid_temp_id(); |
271 | } |
272 | |
273 | bool isEmpty() const |
274 | { |
275 | // Make sure that compiler packed structure like we wanted |
276 | fb_assert(sizeof(*this) == 8); |
277 | |
278 | return bid_quad.bid_quad_high == 0 && bid_quad.bid_quad_low == 0; |
279 | } |
280 | |
281 | void clear() |
282 | { |
283 | // Make sure that compiler packed structure like we wanted |
284 | fb_assert(sizeof(*this) == 8); |
285 | |
286 | bid_quad.bid_quad_high = 0; |
287 | bid_quad.bid_quad_low = 0; |
288 | } |
289 | |
290 | void set_temporary(ULONG temp_id) |
291 | { |
292 | // Make sure that compiler packed structure like we wanted |
293 | fb_assert(sizeof(*this) == 8); |
294 | |
295 | clear(); |
296 | bid_temp_id() = temp_id; |
297 | } |
298 | |
299 | void set_permanent(USHORT relation_id, RecordNumber num) |
300 | { |
301 | // Make sure that compiler packed structure like we wanted |
302 | fb_assert(sizeof(*this) == 8); |
303 | |
304 | clear(); |
305 | bid_internal.bid_relation_id = relation_id; |
306 | num.bid_encode(&bid_internal); |
307 | } |
308 | |
309 | RecordNumber get_permanent_number() const |
310 | { |
311 | // Make sure that compiler packed structure like we wanted |
312 | fb_assert(sizeof(*this) == 8); |
313 | |
314 | RecordNumber temp; |
315 | temp.bid_decode(&bid_internal); |
316 | return temp; |
317 | } |
318 | |
319 | bool operator == (const bid& other) const |
320 | { |
321 | // Make sure that compiler packed structure like we wanted |
322 | fb_assert(sizeof(*this) == 8); |
323 | |
324 | return bid_quad.bid_quad_high == other.bid_quad.bid_quad_high && |
325 | bid_quad.bid_quad_low == other.bid_quad.bid_quad_low; |
326 | } |
327 | }; |
328 | |
329 | } // namespace Jrd |
330 | |
331 | |
332 | #endif // JRD_RECORDNUMBER_H |
333 | |