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
35const 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.
41class RecordNumber
42{
43public:
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 decompose(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
222private:
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
230namespace 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
241struct 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