/src/gdal/ogr/ogrsf_frmts/mitab/mitab_indfile.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /********************************************************************** |
2 | | * |
3 | | * Name: mitab_indfile.cpp |
4 | | * Project: MapInfo TAB Read/Write library |
5 | | * Language: C++ |
6 | | * Purpose: Implementation of the TABINDFile class used to handle |
7 | | * access to .IND file (table field indexes) attached to a .DAT file |
8 | | * Author: Daniel Morissette, dmorissette@dmsolutions.ca |
9 | | * |
10 | | ********************************************************************** |
11 | | * Copyright (c) 1999-2001, Daniel Morissette |
12 | | * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com> |
13 | | * |
14 | | * SPDX-License-Identifier: MIT |
15 | | **********************************************************************/ |
16 | | |
17 | | #include "cpl_port.h" |
18 | | #include "mitab.h" |
19 | | |
20 | | #include <cctype> |
21 | | #include <cstring> |
22 | | #include <algorithm> |
23 | | |
24 | | #include "cpl_conv.h" |
25 | | #include "cpl_error.h" |
26 | | #include "cpl_vsi.h" |
27 | | #include "mitab_priv.h" |
28 | | #include "mitab_utils.h" |
29 | | |
30 | | /*===================================================================== |
31 | | * class TABINDFile |
32 | | *====================================================================*/ |
33 | | |
34 | | constexpr GUInt32 IND_MAGIC_COOKIE = 24242424; |
35 | | |
36 | | /********************************************************************** |
37 | | * TABINDFile::TABINDFile() |
38 | | * |
39 | | * Constructor. |
40 | | **********************************************************************/ |
41 | | TABINDFile::TABINDFile() |
42 | 571 | : m_pszFname(nullptr), m_fp(nullptr), m_eAccessMode(TABRead), |
43 | 571 | m_numIndexes(0), m_papoIndexRootNodes(nullptr), m_papbyKeyBuffers(nullptr) |
44 | 571 | { |
45 | 571 | m_oBlockManager.SetName("IND"); |
46 | 571 | m_oBlockManager.SetBlockSize(512); |
47 | 571 | } |
48 | | |
49 | | /********************************************************************** |
50 | | * TABINDFile::~TABINDFile() |
51 | | * |
52 | | * Destructor. |
53 | | **********************************************************************/ |
54 | | TABINDFile::~TABINDFile() |
55 | 571 | { |
56 | 571 | Close(); |
57 | 571 | } |
58 | | |
59 | | /********************************************************************** |
60 | | * TABINDFile::Open() |
61 | | * |
62 | | * Open a .IND file, read the header and the root nodes for all the |
63 | | * field indexes, and be ready to search the indexes. |
64 | | * |
65 | | * If the filename that is passed in contains a .DAT extension then |
66 | | * the extension will be changed to .IND before trying to open the file. |
67 | | * |
68 | | * Note that we pass a pszAccess flag, but only read access is supported |
69 | | * for now (and there are no plans to support write.) |
70 | | * |
71 | | * Set bTestOpenNoError=TRUE to silently return -1 with no error message |
72 | | * if the file cannot be opened because it does not exist. |
73 | | * |
74 | | * Returns 0 on success, -1 on error. |
75 | | **********************************************************************/ |
76 | | int TABINDFile::Open(const char *pszFname, const char *pszAccess, |
77 | | GBool bTestOpenNoError /*=FALSE*/) |
78 | 571 | { |
79 | 571 | if (m_fp) |
80 | 0 | { |
81 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
82 | 0 | "Open() failed: object already contains an open file"); |
83 | 0 | return -1; |
84 | 0 | } |
85 | | |
86 | | /*----------------------------------------------------------------- |
87 | | * Validate access mode and make sure we use binary access. |
88 | | * Note that for write access, we actually need read/write access to |
89 | | * the file. |
90 | | *----------------------------------------------------------------*/ |
91 | 571 | if (STARTS_WITH_CI(pszAccess, "r") && strchr(pszAccess, '+') != nullptr) |
92 | 0 | { |
93 | 0 | m_eAccessMode = TABReadWrite; |
94 | 0 | pszAccess = "rb+"; |
95 | 0 | } |
96 | 571 | else if (STARTS_WITH_CI(pszAccess, "r")) |
97 | 571 | { |
98 | 571 | m_eAccessMode = TABRead; |
99 | 571 | pszAccess = "rb"; |
100 | 571 | } |
101 | 0 | else if (STARTS_WITH_CI(pszAccess, "w")) |
102 | 0 | { |
103 | 0 | m_eAccessMode = TABWrite; |
104 | 0 | pszAccess = "wb+"; |
105 | 0 | } |
106 | 0 | else |
107 | 0 | { |
108 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
109 | 0 | "Open() failed: access mode \"%s\" not supported", pszAccess); |
110 | 0 | return -1; |
111 | 0 | } |
112 | | |
113 | | /*----------------------------------------------------------------- |
114 | | * Change .DAT (or .TAB) extension to .IND if necessary |
115 | | *----------------------------------------------------------------*/ |
116 | 571 | m_pszFname = CPLStrdup(pszFname); |
117 | | |
118 | 571 | const int nLen = static_cast<int>(strlen(m_pszFname)); |
119 | 571 | if (nLen > 4 && !EQUAL(m_pszFname + nLen - 4, ".IND")) |
120 | 245 | strcpy(m_pszFname + nLen - 4, ".ind"); |
121 | | |
122 | 571 | #ifndef _WIN32 |
123 | 571 | TABAdjustFilenameExtension(m_pszFname); |
124 | 571 | #endif |
125 | | |
126 | | /*----------------------------------------------------------------- |
127 | | * Open file |
128 | | *----------------------------------------------------------------*/ |
129 | 571 | m_fp = VSIFOpenL(m_pszFname, pszAccess); |
130 | | |
131 | 571 | if (m_fp == nullptr) |
132 | 27 | { |
133 | 27 | if (!bTestOpenNoError) |
134 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Open() failed for %s (%s)", |
135 | 0 | m_pszFname, pszAccess); |
136 | | |
137 | 27 | CPLFree(m_pszFname); |
138 | 27 | m_pszFname = nullptr; |
139 | 27 | return -1; |
140 | 27 | } |
141 | | |
142 | | /*----------------------------------------------------------------- |
143 | | * Reset block manager to allocate first block at byte 512, after header. |
144 | | *----------------------------------------------------------------*/ |
145 | 544 | m_oBlockManager.Reset(); |
146 | 544 | m_oBlockManager.AllocNewBlock(); |
147 | | |
148 | | /*----------------------------------------------------------------- |
149 | | * Read access: Read the header block |
150 | | * This will also alloc and init the array of index root nodes. |
151 | | *----------------------------------------------------------------*/ |
152 | 544 | if ((m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) && |
153 | 544 | ReadHeader() != 0) |
154 | 90 | { |
155 | | // Failed reading header... CPLError() has already been called |
156 | 90 | Close(); |
157 | 90 | return -1; |
158 | 90 | } |
159 | | |
160 | | /*----------------------------------------------------------------- |
161 | | * Write access: Init class members and write a dummy header block |
162 | | *----------------------------------------------------------------*/ |
163 | 454 | if (m_eAccessMode == TABWrite) |
164 | 0 | { |
165 | 0 | m_numIndexes = 0; |
166 | |
|
167 | 0 | if (WriteHeader() != 0) |
168 | 0 | { |
169 | | // Failed writing header... CPLError() has already been called |
170 | 0 | Close(); |
171 | 0 | return -1; |
172 | 0 | } |
173 | 0 | } |
174 | | |
175 | 454 | return 0; |
176 | 454 | } |
177 | | |
178 | | /********************************************************************** |
179 | | * TABINDFile::Close() |
180 | | * |
181 | | * Close current file, and release all memory used. |
182 | | * |
183 | | * Returns 0 on success, -1 on error. |
184 | | **********************************************************************/ |
185 | | int TABINDFile::Close() |
186 | 1.18k | { |
187 | 1.18k | if (m_fp == nullptr) |
188 | 636 | return 0; |
189 | | |
190 | | /*----------------------------------------------------------------- |
191 | | * In Write Mode, commit all indexes to the file |
192 | | *----------------------------------------------------------------*/ |
193 | 544 | if (m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite) |
194 | 0 | { |
195 | 0 | WriteHeader(); |
196 | |
|
197 | 0 | for (int iIndex = 0; iIndex < m_numIndexes; iIndex++) |
198 | 0 | { |
199 | 0 | if (m_papoIndexRootNodes && m_papoIndexRootNodes[iIndex]) |
200 | 0 | { |
201 | 0 | CPL_IGNORE_RET_VAL( |
202 | 0 | m_papoIndexRootNodes[iIndex]->CommitToFile()); |
203 | 0 | } |
204 | 0 | } |
205 | 0 | } |
206 | | |
207 | | /*----------------------------------------------------------------- |
208 | | * Free index nodes in memory |
209 | | *----------------------------------------------------------------*/ |
210 | 5.83k | for (int iIndex = 0; iIndex < m_numIndexes; iIndex++) |
211 | 5.29k | { |
212 | 5.29k | if (m_papoIndexRootNodes && m_papoIndexRootNodes[iIndex]) |
213 | 441 | delete m_papoIndexRootNodes[iIndex]; |
214 | 5.29k | if (m_papbyKeyBuffers && m_papbyKeyBuffers[iIndex]) |
215 | 411 | CPLFree(m_papbyKeyBuffers[iIndex]); |
216 | 5.29k | } |
217 | 544 | CPLFree(m_papoIndexRootNodes); |
218 | 544 | m_papoIndexRootNodes = nullptr; |
219 | 544 | CPLFree(m_papbyKeyBuffers); |
220 | 544 | m_papbyKeyBuffers = nullptr; |
221 | 544 | m_numIndexes = 0; |
222 | | |
223 | | /*----------------------------------------------------------------- |
224 | | * Close file |
225 | | *----------------------------------------------------------------*/ |
226 | 544 | VSIFCloseL(m_fp); |
227 | 544 | m_fp = nullptr; |
228 | | |
229 | 544 | CPLFree(m_pszFname); |
230 | 544 | m_pszFname = nullptr; |
231 | | |
232 | 544 | return 0; |
233 | 1.18k | } |
234 | | |
235 | | /********************************************************************** |
236 | | * TABINDFile::ReadHeader() |
237 | | * |
238 | | * (private method) |
239 | | * Read the header block and init all class members for read access. |
240 | | * |
241 | | * Returns 0 on success, -1 on error. |
242 | | **********************************************************************/ |
243 | | int TABINDFile::ReadHeader() |
244 | 544 | { |
245 | | |
246 | 544 | CPLAssert(m_fp); |
247 | 544 | CPLAssert(m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite); |
248 | | |
249 | | /*----------------------------------------------------------------- |
250 | | * In ReadWrite mode, we need to init BlockManager with file size |
251 | | *----------------------------------------------------------------*/ |
252 | 544 | VSIStatBufL sStatBuf; |
253 | 544 | if (m_eAccessMode == TABReadWrite && VSIStatL(m_pszFname, &sStatBuf) != -1) |
254 | 0 | { |
255 | 0 | m_oBlockManager.SetLastPtr( |
256 | 0 | static_cast<int>(((sStatBuf.st_size - 1) / 512) * 512)); |
257 | 0 | } |
258 | | |
259 | | /*----------------------------------------------------------------- |
260 | | * Read the header block |
261 | | *----------------------------------------------------------------*/ |
262 | 544 | TABRawBinBlock *poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE); |
263 | 544 | if (poHeaderBlock->ReadFromFile(m_fp, 0, 512) != 0) |
264 | 42 | { |
265 | | // CPLError() has already been called. |
266 | 42 | delete poHeaderBlock; |
267 | 42 | return -1; |
268 | 42 | } |
269 | | |
270 | 502 | poHeaderBlock->GotoByteInBlock(0); |
271 | 502 | GUInt32 nMagicCookie = poHeaderBlock->ReadInt32(); |
272 | 502 | if (nMagicCookie != IND_MAGIC_COOKIE) |
273 | 1 | { |
274 | 1 | CPLError(CE_Failure, CPLE_FileIO, |
275 | 1 | "%s: Invalid Magic Cookie: got %d, expected %d", m_pszFname, |
276 | 1 | nMagicCookie, IND_MAGIC_COOKIE); |
277 | 1 | delete poHeaderBlock; |
278 | 1 | return -1; |
279 | 1 | } |
280 | | |
281 | 501 | poHeaderBlock->GotoByteInBlock(12); |
282 | 501 | m_numIndexes = poHeaderBlock->ReadInt16(); |
283 | 501 | if (m_numIndexes < 1 || m_numIndexes > 29) |
284 | 17 | { |
285 | 17 | CPLError(CE_Failure, CPLE_FileIO, |
286 | 17 | "Invalid number of indexes (%d) in file %s", m_numIndexes, |
287 | 17 | m_pszFname); |
288 | 17 | delete poHeaderBlock; |
289 | 17 | return -1; |
290 | 17 | } |
291 | | |
292 | | /*----------------------------------------------------------------- |
293 | | * Alloc and init the array of index root nodes. |
294 | | *----------------------------------------------------------------*/ |
295 | 484 | m_papoIndexRootNodes = static_cast<TABINDNode **>( |
296 | 484 | CPLCalloc(m_numIndexes, sizeof(TABINDNode *))); |
297 | | |
298 | 484 | m_papbyKeyBuffers = |
299 | 484 | static_cast<GByte **>(CPLCalloc(m_numIndexes, sizeof(GByte *))); |
300 | | |
301 | | /* First index def. starts at byte 48 */ |
302 | 484 | poHeaderBlock->GotoByteInBlock(48); |
303 | | |
304 | 977 | for (int iIndex = 0; iIndex < m_numIndexes; iIndex++) |
305 | 523 | { |
306 | | /*------------------------------------------------------------- |
307 | | * Read next index definition |
308 | | *------------------------------------------------------------*/ |
309 | 523 | GInt32 nRootNodePtr = poHeaderBlock->ReadInt32(); |
310 | 523 | poHeaderBlock->ReadInt16(); // skip... max. num of entries per node |
311 | 523 | int nTreeDepth = poHeaderBlock->ReadByte(); |
312 | 523 | int nKeyLength = poHeaderBlock->ReadByte(); |
313 | 523 | poHeaderBlock->GotoByteRel(8); // skip next 8 bytes; |
314 | | |
315 | | /*------------------------------------------------------------- |
316 | | * And init root node for this index. |
317 | | * Note that if nRootNodePtr==0 then this means that the |
318 | | * corresponding index does not exist (i.e. has been deleted?) |
319 | | * so we simply do not allocate the root node in this case. |
320 | | * An error will be produced if the user tries to access this index |
321 | | * later during execution. |
322 | | *------------------------------------------------------------*/ |
323 | 523 | if (nRootNodePtr > 0) |
324 | 441 | { |
325 | 441 | m_papoIndexRootNodes[iIndex] = new TABINDNode(m_eAccessMode); |
326 | 441 | if (m_papoIndexRootNodes[iIndex]->InitNode( |
327 | 441 | m_fp, nRootNodePtr, nKeyLength, nTreeDepth, FALSE, |
328 | 441 | &m_oBlockManager) != 0) |
329 | 30 | { |
330 | | // CPLError has already been called |
331 | 30 | delete poHeaderBlock; |
332 | 30 | return -1; |
333 | 30 | } |
334 | | |
335 | | // Alloc a temporary key buffer for this index. |
336 | | // This buffer will be used by the BuildKey() method |
337 | 411 | m_papbyKeyBuffers[iIndex] = |
338 | 411 | static_cast<GByte *>(CPLCalloc(nKeyLength + 1, sizeof(GByte))); |
339 | 411 | } |
340 | 82 | else |
341 | 82 | { |
342 | 82 | m_papoIndexRootNodes[iIndex] = nullptr; |
343 | 82 | m_papbyKeyBuffers[iIndex] = nullptr; |
344 | 82 | } |
345 | 523 | } |
346 | | |
347 | | /*----------------------------------------------------------------- |
348 | | * OK, we won't need the header block any more... free it. |
349 | | *----------------------------------------------------------------*/ |
350 | 454 | delete poHeaderBlock; |
351 | | |
352 | 454 | return 0; |
353 | 484 | } |
354 | | |
355 | | /********************************************************************** |
356 | | * TABINDFile::WriteHeader() |
357 | | * |
358 | | * (private method) |
359 | | * Write the header block based on current index information. |
360 | | * |
361 | | * Returns 0 on success, -1 on error. |
362 | | **********************************************************************/ |
363 | | int TABINDFile::WriteHeader() |
364 | 0 | { |
365 | 0 | CPLAssert(m_fp); |
366 | 0 | CPLAssert(m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite); |
367 | | |
368 | | /*----------------------------------------------------------------- |
369 | | * Write the 48 bytes of file header |
370 | | *----------------------------------------------------------------*/ |
371 | 0 | TABRawBinBlock *poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE); |
372 | 0 | poHeaderBlock->InitNewBlock(m_fp, 512, 0); |
373 | |
|
374 | 0 | poHeaderBlock->WriteInt32(IND_MAGIC_COOKIE); |
375 | |
|
376 | 0 | poHeaderBlock->WriteInt16(100); // ??? |
377 | 0 | poHeaderBlock->WriteInt16(512); // ??? |
378 | 0 | poHeaderBlock->WriteInt32(0); // ??? |
379 | |
|
380 | 0 | poHeaderBlock->WriteInt16(static_cast<GInt16>(m_numIndexes)); |
381 | |
|
382 | 0 | poHeaderBlock->WriteInt16(0x15e7); // ??? |
383 | |
|
384 | 0 | poHeaderBlock->WriteInt16(10); // ??? |
385 | 0 | poHeaderBlock->WriteInt16(0x611d); // ??? |
386 | |
|
387 | 0 | poHeaderBlock->WriteZeros(28); |
388 | | |
389 | | /*----------------------------------------------------------------- |
390 | | * The first index definition starts at byte 48 |
391 | | *----------------------------------------------------------------*/ |
392 | 0 | for (int iIndex = 0; iIndex < m_numIndexes; iIndex++) |
393 | 0 | { |
394 | 0 | TABINDNode *poRootNode = m_papoIndexRootNodes[iIndex]; |
395 | |
|
396 | 0 | if (poRootNode) |
397 | 0 | { |
398 | | /*--------------------------------------------------------- |
399 | | * Write next index definition |
400 | | *--------------------------------------------------------*/ |
401 | 0 | poHeaderBlock->WriteInt32(poRootNode->GetNodeBlockPtr()); |
402 | 0 | poHeaderBlock->WriteInt16( |
403 | 0 | static_cast<GInt16>(poRootNode->GetMaxNumEntries())); |
404 | 0 | poHeaderBlock->WriteByte( |
405 | 0 | static_cast<GByte>(poRootNode->GetSubTreeDepth())); |
406 | 0 | poHeaderBlock->WriteByte( |
407 | 0 | static_cast<GByte>(poRootNode->GetKeyLength())); |
408 | |
|
409 | 0 | poHeaderBlock->WriteZeros(8); |
410 | | |
411 | | /*--------------------------------------------------------- |
412 | | * Look for overflow of the SubTreeDepth field (byte) |
413 | | *--------------------------------------------------------*/ |
414 | 0 | if (poRootNode->GetSubTreeDepth() > 255) |
415 | 0 | { |
416 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
417 | 0 | "Index no %d is too large and will not be usable. " |
418 | 0 | "(SubTreeDepth = %d, cannot exceed 255).", |
419 | 0 | iIndex + 1, poRootNode->GetSubTreeDepth()); |
420 | 0 | return -1; |
421 | 0 | } |
422 | 0 | } |
423 | 0 | else |
424 | 0 | { |
425 | | /*--------------------------------------------------------- |
426 | | * NULL Root Node: This index has likely been deleted |
427 | | *--------------------------------------------------------*/ |
428 | 0 | poHeaderBlock->WriteZeros(16); |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | | /*----------------------------------------------------------------- |
433 | | * OK, we won't need the header block any more... write and free it. |
434 | | *----------------------------------------------------------------*/ |
435 | 0 | if (poHeaderBlock->CommitToFile() != 0) |
436 | 0 | return -1; |
437 | | |
438 | 0 | delete poHeaderBlock; |
439 | |
|
440 | 0 | return 0; |
441 | 0 | } |
442 | | |
443 | | /********************************************************************** |
444 | | * TABINDFile::ValidateIndexNo() |
445 | | * |
446 | | * Private method to validate the index no parameter of some methods... |
447 | | * returns 0 if index no. is OK, or produces an error ands returns -1 |
448 | | * if index no is not valid. |
449 | | **********************************************************************/ |
450 | | int TABINDFile::ValidateIndexNo(int nIndexNumber) |
451 | 25.0k | { |
452 | 25.0k | if (m_fp == nullptr) |
453 | 0 | { |
454 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
455 | 0 | "TABINDFile: File has not been opened yet!"); |
456 | 0 | return -1; |
457 | 0 | } |
458 | | |
459 | 25.0k | if (nIndexNumber < 1 || nIndexNumber > m_numIndexes || |
460 | 25.0k | m_papoIndexRootNodes == nullptr || |
461 | 25.0k | m_papoIndexRootNodes[nIndexNumber - 1] == nullptr) |
462 | 5.47k | { |
463 | 5.47k | CPLError(CE_Failure, CPLE_AssertionFailed, |
464 | 5.47k | "No field index number %d in %s: Valid range is [1..%d].", |
465 | 5.47k | nIndexNumber, m_pszFname, m_numIndexes); |
466 | 5.47k | return -1; |
467 | 5.47k | } |
468 | | |
469 | 19.6k | return 0; // Index seems valid |
470 | 25.0k | } |
471 | | |
472 | | /********************************************************************** |
473 | | * TABINDFile::SetIndexFieldType() |
474 | | * |
475 | | * Sets the field type for the specified index. |
476 | | * This information will then be used in building the key values, etc. |
477 | | * |
478 | | * Returns 0 on success, -1 on error. |
479 | | **********************************************************************/ |
480 | | int TABINDFile::SetIndexFieldType(int nIndexNumber, TABFieldType eType) |
481 | 188 | { |
482 | 188 | if (ValidateIndexNo(nIndexNumber) != 0) |
483 | 19 | return -1; |
484 | | |
485 | 169 | return m_papoIndexRootNodes[nIndexNumber - 1]->SetFieldType(eType); |
486 | 188 | } |
487 | | |
488 | | /********************************************************************** |
489 | | * TABINDFile::SetIndexUnique() |
490 | | * |
491 | | * Indicate that an index's keys are unique. This allows for some |
492 | | * optimization with read access. By default, an index is treated as if |
493 | | * its keys could have duplicates. |
494 | | * |
495 | | * Returns 0 on success, -1 on error. |
496 | | **********************************************************************/ |
497 | | int TABINDFile::SetIndexUnique(int nIndexNumber, GBool bUnique /*=TRUE*/) |
498 | 0 | { |
499 | 0 | if (ValidateIndexNo(nIndexNumber) != 0) |
500 | 0 | return -1; |
501 | | |
502 | 0 | m_papoIndexRootNodes[nIndexNumber - 1]->SetUnique(bUnique); |
503 | |
|
504 | 0 | return 0; |
505 | 0 | } |
506 | | |
507 | | /********************************************************************** |
508 | | * TABINDFile::BuildKey() |
509 | | * |
510 | | * Encode a field value in the form required to be compared with index |
511 | | * keys in the specified index. |
512 | | * |
513 | | * Note that index numbers are positive values starting at 1. |
514 | | * |
515 | | * Returns a reference to an internal buffer that is valid only until the |
516 | | * next call to BuildKey(). (should not be freed by the caller). |
517 | | * Returns NULL if field index is invalid. |
518 | | * |
519 | | * The first flavor of the function handles integer type of values, this |
520 | | * corresponds to MapInfo types: integer, smallint, logical and date |
521 | | **********************************************************************/ |
522 | | GByte *TABINDFile::BuildKey(int nIndexNumber, GInt32 nValue) |
523 | 12.4k | { |
524 | 12.4k | if (ValidateIndexNo(nIndexNumber) != 0) |
525 | 2.72k | return nullptr; |
526 | | |
527 | 9.72k | int nKeyLength = m_papoIndexRootNodes[nIndexNumber - 1]->GetKeyLength(); |
528 | | |
529 | | /*----------------------------------------------------------------- |
530 | | * Convert all int values to MSB using the right number of bytes |
531 | | * Note: |
532 | | * The most significant bit has to be unset for negative values, |
533 | | * and to be set for positive ones... that's the reverse of what it |
534 | | * should usually be. Adding 0x80 to the MSB byte will do the job. |
535 | | *----------------------------------------------------------------*/ |
536 | 9.72k | switch (nKeyLength) |
537 | 9.72k | { |
538 | 4.74k | case 1: |
539 | 4.74k | m_papbyKeyBuffers[nIndexNumber - 1][0] = |
540 | 4.74k | static_cast<GByte>(nValue & 0xff) + 0x80; |
541 | 4.74k | break; |
542 | 2.53k | case 2: |
543 | 2.53k | m_papbyKeyBuffers[nIndexNumber - 1][0] = |
544 | 2.53k | static_cast<GByte>(nValue / 0x100 & 0xff) + 0x80; |
545 | 2.53k | m_papbyKeyBuffers[nIndexNumber - 1][1] = |
546 | 2.53k | static_cast<GByte>(nValue & 0xff); |
547 | 2.53k | break; |
548 | 17 | case 4: |
549 | 17 | m_papbyKeyBuffers[nIndexNumber - 1][0] = |
550 | 17 | static_cast<GByte>(nValue / 0x1000000 & 0xff) + 0x80; |
551 | 17 | m_papbyKeyBuffers[nIndexNumber - 1][1] = |
552 | 17 | static_cast<GByte>(nValue / 0x10000 & 0xff); |
553 | 17 | m_papbyKeyBuffers[nIndexNumber - 1][2] = |
554 | 17 | static_cast<GByte>(nValue / 0x100 & 0xff); |
555 | 17 | m_papbyKeyBuffers[nIndexNumber - 1][3] = |
556 | 17 | static_cast<GByte>(nValue & 0xff); |
557 | 17 | break; |
558 | 2.43k | default: |
559 | 2.43k | CPLError(CE_Failure, CPLE_AssertionFailed, |
560 | 2.43k | "BuildKey(): %d bytes integer key length not supported", |
561 | 2.43k | nKeyLength); |
562 | 2.43k | break; |
563 | 9.72k | } |
564 | | |
565 | 9.72k | return m_papbyKeyBuffers[nIndexNumber - 1]; |
566 | 9.72k | } |
567 | | |
568 | | /********************************************************************** |
569 | | * TABINDFile::BuildKey() |
570 | | * |
571 | | * BuildKey() for string fields |
572 | | **********************************************************************/ |
573 | | GByte *TABINDFile::BuildKey(int nIndexNumber, const char *pszStr) |
574 | 0 | { |
575 | 0 | if (ValidateIndexNo(nIndexNumber) != 0 || pszStr == nullptr) |
576 | 0 | return nullptr; |
577 | | |
578 | 0 | int nKeyLength = m_papoIndexRootNodes[nIndexNumber - 1]->GetKeyLength(); |
579 | | |
580 | | /*----------------------------------------------------------------- |
581 | | * Strings keys are all in uppercase, and padded with '\0' |
582 | | *----------------------------------------------------------------*/ |
583 | 0 | int i = 0; |
584 | 0 | for (i = 0; i < nKeyLength && pszStr[i] != '\0'; i++) |
585 | 0 | { |
586 | 0 | m_papbyKeyBuffers[nIndexNumber - 1][i] = static_cast<GByte>( |
587 | 0 | CPLToupper(static_cast<unsigned char>(pszStr[i]))); |
588 | 0 | } |
589 | | |
590 | | /* Pad the end of the buffer with '\0' */ |
591 | 0 | for (; i < nKeyLength; i++) |
592 | 0 | { |
593 | 0 | m_papbyKeyBuffers[nIndexNumber - 1][i] = '\0'; |
594 | 0 | } |
595 | |
|
596 | 0 | return m_papbyKeyBuffers[nIndexNumber - 1]; |
597 | 0 | } |
598 | | |
599 | | /********************************************************************** |
600 | | * TABINDFile::BuildKey() |
601 | | * |
602 | | * BuildKey() for float and decimal fields |
603 | | **********************************************************************/ |
604 | | GByte *TABINDFile::BuildKey(int nIndexNumber, double dValue) |
605 | 0 | { |
606 | 0 | if (ValidateIndexNo(nIndexNumber) != 0) |
607 | 0 | return nullptr; |
608 | | |
609 | 0 | const int nKeyLength = |
610 | 0 | m_papoIndexRootNodes[nIndexNumber - 1]->GetKeyLength(); |
611 | 0 | CPLAssert(nKeyLength == 8 && sizeof(double) == 8); |
612 | | |
613 | | /*----------------------------------------------------------------- |
614 | | * Convert double and decimal values... |
615 | | * Reverse the sign of the value, and convert to MSB |
616 | | *----------------------------------------------------------------*/ |
617 | 0 | dValue = -dValue; |
618 | |
|
619 | 0 | #ifndef CPL_MSB |
620 | 0 | CPL_SWAPDOUBLE(&dValue); |
621 | 0 | #endif |
622 | |
|
623 | 0 | memcpy(m_papbyKeyBuffers[nIndexNumber - 1], |
624 | 0 | reinterpret_cast<GByte *>(&dValue), nKeyLength); |
625 | |
|
626 | 0 | return m_papbyKeyBuffers[nIndexNumber - 1]; |
627 | 0 | } |
628 | | |
629 | | /********************************************************************** |
630 | | * TABINDFile::BuildKey() |
631 | | * |
632 | | * BuildKey() for LargeInt |
633 | | **********************************************************************/ |
634 | | GByte *TABINDFile::BuildKey(int nIndexNumber, GInt64 nValue) |
635 | 0 | { |
636 | 0 | if (ValidateIndexNo(nIndexNumber) != 0) |
637 | 0 | return nullptr; |
638 | | |
639 | 0 | const int nKeyLength = |
640 | 0 | m_papoIndexRootNodes[nIndexNumber - 1]->GetKeyLength(); |
641 | 0 | CPLAssert(nKeyLength == 8 && sizeof(double) == 8); |
642 | |
|
643 | 0 | #ifndef CPL_MSB |
644 | 0 | CPL_SWAP64PTR(&nValue); |
645 | 0 | #endif |
646 | |
|
647 | 0 | memcpy(m_papbyKeyBuffers[nIndexNumber - 1], |
648 | 0 | reinterpret_cast<GByte *>(&nValue), nKeyLength); |
649 | |
|
650 | 0 | return m_papbyKeyBuffers[nIndexNumber - 1]; |
651 | 0 | } |
652 | | |
653 | | /********************************************************************** |
654 | | * TABINDFile::FindFirst() |
655 | | * |
656 | | * Search one of the indexes for a key value. |
657 | | * |
658 | | * Note that index numbers are positive values starting at 1. |
659 | | * |
660 | | * Return value: |
661 | | * - the key's corresponding record number in the .DAT file (greater than 0) |
662 | | * - 0 if the key was not found |
663 | | * - or -1 if an error happened |
664 | | **********************************************************************/ |
665 | | GInt32 TABINDFile::FindFirst(int nIndexNumber, GByte *pKeyValue) |
666 | 12.4k | { |
667 | 12.4k | if (ValidateIndexNo(nIndexNumber) != 0) |
668 | 2.72k | return -1; |
669 | | |
670 | 9.72k | return m_papoIndexRootNodes[nIndexNumber - 1]->FindFirst(pKeyValue); |
671 | 12.4k | } |
672 | | |
673 | | /********************************************************************** |
674 | | * TABINDFile::FindNext() |
675 | | * |
676 | | * Continue the Search for pKeyValue previously initiated by FindFirst(). |
677 | | * NOTE: FindFirst() MUST have been previously called for this call to |
678 | | * work... |
679 | | * |
680 | | * Note that index numbers are positive values starting at 1. |
681 | | * |
682 | | * Return value: |
683 | | * - the key's corresponding record number in the .DAT file (greater than 0) |
684 | | * - 0 if the key was not found |
685 | | * - or -1 if an error happened |
686 | | **********************************************************************/ |
687 | | GInt32 TABINDFile::FindNext(int nIndexNumber, GByte *pKeyValue) |
688 | 0 | { |
689 | 0 | if (ValidateIndexNo(nIndexNumber) != 0) |
690 | 0 | return -1; |
691 | | |
692 | 0 | return m_papoIndexRootNodes[nIndexNumber - 1]->FindNext(pKeyValue); |
693 | 0 | } |
694 | | |
695 | | /********************************************************************** |
696 | | * TABINDFile::CreateIndex() |
697 | | * |
698 | | * Create a new index with the specified field type and size. |
699 | | * Field size applies only to char field type... the other types have a |
700 | | * predefined key length. |
701 | | * |
702 | | * Key length is limited to 128 chars. char fields longer than 128 chars |
703 | | * will have their key truncated to 128 bytes. |
704 | | * |
705 | | * Note that a .IND file can contain only a maximum of 29 indexes. |
706 | | * |
707 | | * Returns the new field index on success (greater than 0), or -1 on error. |
708 | | **********************************************************************/ |
709 | | int TABINDFile::CreateIndex(TABFieldType eType, int nFieldSize) |
710 | 0 | { |
711 | 0 | int i, nNewIndexNo = -1; |
712 | |
|
713 | 0 | if (m_fp == nullptr || |
714 | 0 | (m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite)) |
715 | 0 | return -1; |
716 | | |
717 | | // __TODO__ |
718 | | // We'll need more work in TABDATFile::WriteDateTimeField() before |
719 | | // we can support indexes on fields of type DateTime (see bug #1844) |
720 | 0 | if (eType == TABFDateTime) |
721 | 0 | { |
722 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
723 | 0 | "Index on fields of type DateTime not supported yet."); |
724 | 0 | return -1; |
725 | 0 | } |
726 | | |
727 | | /*----------------------------------------------------------------- |
728 | | * Look for an empty slot in the current array, if there is none |
729 | | * then extend the array. |
730 | | *----------------------------------------------------------------*/ |
731 | 0 | for (i = 0; m_papoIndexRootNodes && i < m_numIndexes; i++) |
732 | 0 | { |
733 | 0 | if (m_papoIndexRootNodes[i] == nullptr) |
734 | 0 | { |
735 | 0 | nNewIndexNo = i; |
736 | 0 | break; |
737 | 0 | } |
738 | 0 | } |
739 | |
|
740 | 0 | if (nNewIndexNo == -1 && m_numIndexes >= 29) |
741 | 0 | { |
742 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
743 | 0 | "Cannot add new index to %s. A dataset can contain only a " |
744 | 0 | "maximum of 29 indexes.", |
745 | 0 | m_pszFname); |
746 | 0 | return -1; |
747 | 0 | } |
748 | | |
749 | 0 | if (nNewIndexNo == -1) |
750 | 0 | { |
751 | | /*------------------------------------------------------------- |
752 | | * Add a slot for new index at the end of the nodes array. |
753 | | *------------------------------------------------------------*/ |
754 | 0 | m_numIndexes++; |
755 | 0 | m_papoIndexRootNodes = static_cast<TABINDNode **>(CPLRealloc( |
756 | 0 | m_papoIndexRootNodes, m_numIndexes * sizeof(TABINDNode *))); |
757 | |
|
758 | 0 | m_papbyKeyBuffers = static_cast<GByte **>( |
759 | 0 | CPLRealloc(m_papbyKeyBuffers, m_numIndexes * sizeof(GByte *))); |
760 | |
|
761 | 0 | nNewIndexNo = m_numIndexes - 1; |
762 | 0 | } |
763 | | |
764 | | /*----------------------------------------------------------------- |
765 | | * Alloc and init new node |
766 | | * The call to InitNode() automatically allocates storage space for |
767 | | * the node in the file. |
768 | | * New nodes are created with a subtree_depth=1 since they start as |
769 | | * leaf nodes, i.e. their entries point directly to .DAT records |
770 | | *----------------------------------------------------------------*/ |
771 | 0 | int nKeyLength = ((eType == TABFInteger) ? 4 |
772 | 0 | : (eType == TABFSmallInt) ? 2 |
773 | 0 | : (eType == TABFLargeInt) ? 8 |
774 | 0 | : (eType == TABFFloat) ? 8 |
775 | 0 | : (eType == TABFDecimal) ? 8 |
776 | 0 | : (eType == TABFDate) ? 4 |
777 | 0 | : (eType == TABFTime) ? 4 |
778 | 0 | : |
779 | | /*(eType == TABFDateTime) ? 8: */ |
780 | 0 | (eType == TABFLogical) ? 4 |
781 | 0 | : std::min(128, nFieldSize)); |
782 | |
|
783 | 0 | m_papoIndexRootNodes[nNewIndexNo] = new TABINDNode(m_eAccessMode); |
784 | 0 | if (m_papoIndexRootNodes[nNewIndexNo]->InitNode(m_fp, 0, nKeyLength, |
785 | 0 | 1, // subtree depth=1 |
786 | 0 | FALSE, // not unique |
787 | 0 | &m_oBlockManager, nullptr, |
788 | 0 | 0, 0) != 0) |
789 | 0 | { |
790 | | // CPLError has already been called |
791 | 0 | return -1; |
792 | 0 | } |
793 | | |
794 | | // Alloc a temporary key buffer for this index. |
795 | | // This buffer will be used by the BuildKey() method |
796 | 0 | m_papbyKeyBuffers[nNewIndexNo] = |
797 | 0 | static_cast<GByte *>(CPLCalloc(nKeyLength + 1, sizeof(GByte))); |
798 | | |
799 | | // Return 1-based index number |
800 | 0 | return nNewIndexNo + 1; |
801 | 0 | } |
802 | | |
803 | | /********************************************************************** |
804 | | * TABINDFile::AddEntry() |
805 | | * |
806 | | * Add an .DAT record entry for pKeyValue in the specified index. |
807 | | * |
808 | | * Note that index numbers are positive values starting at 1. |
809 | | * nRecordNo is the .DAT record number, record numbers start at 1. |
810 | | * |
811 | | * Returns 0 on success, -1 on error |
812 | | **********************************************************************/ |
813 | | int TABINDFile::AddEntry(int nIndexNumber, GByte *pKeyValue, GInt32 nRecordNo) |
814 | 0 | { |
815 | 0 | if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) || |
816 | 0 | ValidateIndexNo(nIndexNumber) != 0) |
817 | 0 | return -1; |
818 | | |
819 | 0 | return m_papoIndexRootNodes[nIndexNumber - 1]->AddEntry(pKeyValue, |
820 | 0 | nRecordNo); |
821 | 0 | } |
822 | | |
823 | | /********************************************************************** |
824 | | * TABINDFile::Dump() |
825 | | * |
826 | | * Dump block contents... available only in DEBUG mode. |
827 | | **********************************************************************/ |
828 | | #ifdef DEBUG |
829 | | |
830 | | void TABINDFile::Dump(FILE *fpOut /*=NULL*/) |
831 | | { |
832 | | if (fpOut == nullptr) |
833 | | fpOut = stdout; |
834 | | |
835 | | fprintf(fpOut, "----- TABINDFile::Dump() -----\n"); |
836 | | |
837 | | if (m_fp == nullptr) |
838 | | { |
839 | | fprintf(fpOut, "File is not opened.\n"); |
840 | | } |
841 | | else |
842 | | { |
843 | | fprintf(fpOut, "File is opened: %s\n", m_pszFname); |
844 | | fprintf(fpOut, " m_numIndexes = %d\n", m_numIndexes); |
845 | | for (int i = 0; i < m_numIndexes && m_papoIndexRootNodes; i++) |
846 | | { |
847 | | if (m_papoIndexRootNodes[i]) |
848 | | { |
849 | | fprintf(fpOut, " ----- Index # %d -----\n", i + 1); |
850 | | m_papoIndexRootNodes[i]->Dump(fpOut); |
851 | | } |
852 | | } |
853 | | } |
854 | | |
855 | | fflush(fpOut); |
856 | | } |
857 | | |
858 | | #endif // DEBUG |
859 | | |
860 | | /*===================================================================== |
861 | | * class TABINDNode |
862 | | *====================================================================*/ |
863 | | |
864 | | /********************************************************************** |
865 | | * TABINDNode::TABINDNode() |
866 | | * |
867 | | * Constructor. |
868 | | **********************************************************************/ |
869 | | TABINDNode::TABINDNode(TABAccess eAccessMode /*=TABRead*/) |
870 | 519 | : m_fp(nullptr), m_eAccessMode(eAccessMode), m_poCurChildNode(nullptr), |
871 | 519 | m_poParentNodeRef(nullptr), m_poBlockManagerRef(nullptr), |
872 | 519 | m_nSubTreeDepth(0), m_nKeyLength(0), m_eFieldType(TABFUnknown), |
873 | 519 | m_bUnique(FALSE), m_nCurDataBlockPtr(0), m_nCurIndexEntry(0), |
874 | 519 | m_poDataBlock(nullptr), m_numEntriesInNode(0), m_nPrevNodePtr(0), |
875 | 519 | m_nNextNodePtr(0) |
876 | 519 | { |
877 | 519 | } |
878 | | |
879 | | /********************************************************************** |
880 | | * TABINDNode::~TABINDNode() |
881 | | * |
882 | | * Destructor. |
883 | | **********************************************************************/ |
884 | | TABINDNode::~TABINDNode() |
885 | 519 | { |
886 | 519 | if (m_poCurChildNode) |
887 | 78 | delete m_poCurChildNode; |
888 | | |
889 | 519 | if (m_poDataBlock) |
890 | 519 | delete m_poDataBlock; |
891 | 519 | } |
892 | | |
893 | | /********************************************************************** |
894 | | * TABINDNode::InitNode() |
895 | | * |
896 | | * Init a node... this function can be used either to initialize a new |
897 | | * node, or to make it point to a new data block in the file. |
898 | | * |
899 | | * By default, this call will read the data from the file at the |
900 | | * specified location if necessary, and leave the object ready to be searched. |
901 | | * |
902 | | * In write access, if the block does not exist (i.e. nBlockPtr=0) then a |
903 | | * new one is created and initialized. |
904 | | * |
905 | | * poParentNode is used in write access in order to update the parent node |
906 | | * when this node becomes full and has to be split. |
907 | | * |
908 | | * Returns 0 on success, -1 on error. |
909 | | **********************************************************************/ |
910 | | int TABINDNode::InitNode(VSILFILE *fp, int nBlockPtr, int nKeyLength, |
911 | | int nSubTreeDepth, GBool bUnique, |
912 | | TABBinBlockManager *poBlockMgr /*=NULL*/, |
913 | | TABINDNode *poParentNode /*=NULL*/, |
914 | | int nPrevNodePtr /*=0*/, int nNextNodePtr /*=0*/) |
915 | 4.75k | { |
916 | | /*----------------------------------------------------------------- |
917 | | * If the block already points to the right block, then don't do |
918 | | * anything here. |
919 | | *----------------------------------------------------------------*/ |
920 | 4.75k | if (m_fp == fp && nBlockPtr > 0 && m_nCurDataBlockPtr == nBlockPtr) |
921 | 2.72k | return 0; |
922 | | |
923 | | // Keep track of some info |
924 | 2.02k | m_fp = fp; |
925 | 2.02k | m_nKeyLength = nKeyLength; |
926 | 2.02k | m_nSubTreeDepth = nSubTreeDepth; |
927 | 2.02k | m_nCurDataBlockPtr = nBlockPtr; |
928 | 2.02k | m_bUnique = bUnique; |
929 | | |
930 | | // Do not overwrite the following values if we receive NULL (the defaults) |
931 | 2.02k | if (poBlockMgr) |
932 | 519 | m_poBlockManagerRef = poBlockMgr; |
933 | 2.02k | if (poParentNode) |
934 | 78 | m_poParentNodeRef = poParentNode; |
935 | | |
936 | | // Set some defaults |
937 | 2.02k | m_numEntriesInNode = 0; |
938 | 2.02k | m_nPrevNodePtr = nPrevNodePtr; |
939 | 2.02k | m_nNextNodePtr = nNextNodePtr; |
940 | | |
941 | 2.02k | m_nCurIndexEntry = 0; |
942 | | |
943 | | /*----------------------------------------------------------------- |
944 | | * Init RawBinBlock |
945 | | * The node's buffer has to be created with read/write access since |
946 | | * the index is a very dynamic structure! |
947 | | *----------------------------------------------------------------*/ |
948 | 2.02k | if (m_poDataBlock == nullptr) |
949 | 519 | m_poDataBlock = new TABRawBinBlock(TABReadWrite, TRUE); |
950 | | |
951 | 2.02k | if ((m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite) && |
952 | 2.02k | nBlockPtr == 0 && m_poBlockManagerRef) |
953 | 0 | { |
954 | | /*------------------------------------------------------------- |
955 | | * Write access: Create and init a new block |
956 | | *------------------------------------------------------------*/ |
957 | 0 | m_nCurDataBlockPtr = m_poBlockManagerRef->AllocNewBlock(); |
958 | 0 | m_poDataBlock->InitNewBlock(m_fp, 512, m_nCurDataBlockPtr); |
959 | |
|
960 | 0 | m_poDataBlock->WriteInt32(m_numEntriesInNode); |
961 | 0 | m_poDataBlock->WriteInt32(m_nPrevNodePtr); |
962 | 0 | m_poDataBlock->WriteInt32(m_nNextNodePtr); |
963 | 0 | } |
964 | 2.02k | else |
965 | 2.02k | { |
966 | 2.02k | CPLAssert(m_nCurDataBlockPtr > 0); |
967 | | /*------------------------------------------------------------- |
968 | | * Read the data block from the file, applies to read access, or |
969 | | * to write access (to modify an existing block) |
970 | | *------------------------------------------------------------*/ |
971 | 2.02k | if (m_poDataBlock->ReadFromFile(m_fp, m_nCurDataBlockPtr, 512) != 0) |
972 | 1.02k | { |
973 | | // CPLError() has already been called. |
974 | 1.02k | return -1; |
975 | 1.02k | } |
976 | | |
977 | 1.00k | m_poDataBlock->GotoByteInBlock(0); |
978 | 1.00k | m_numEntriesInNode = m_poDataBlock->ReadInt32(); |
979 | 1.00k | m_nPrevNodePtr = m_poDataBlock->ReadInt32(); |
980 | 1.00k | m_nNextNodePtr = m_poDataBlock->ReadInt32(); |
981 | 1.00k | } |
982 | | |
983 | | // m_poDataBlock is now positioned at the beginning of the key entries |
984 | | |
985 | 1.00k | return 0; |
986 | 2.02k | } |
987 | | |
988 | | /********************************************************************** |
989 | | * TABINDNode::GotoNodePtr() |
990 | | * |
991 | | * Move to the specified node ptr, and read the new node data from the file. |
992 | | * |
993 | | * This is just a cover function on top of InitNode() |
994 | | **********************************************************************/ |
995 | | int TABINDNode::GotoNodePtr(GInt32 nNewNodePtr) |
996 | 4.23k | { |
997 | | // First flush current changes if any. |
998 | 4.23k | if ((m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite) && |
999 | 4.23k | m_poDataBlock && m_poDataBlock->CommitToFile() != 0) |
1000 | 0 | return -1; |
1001 | | |
1002 | 4.23k | CPLAssert(nNewNodePtr % 512 == 0); |
1003 | | |
1004 | | // Then move to the requested location. |
1005 | 4.23k | return InitNode(m_fp, nNewNodePtr, m_nKeyLength, m_nSubTreeDepth, |
1006 | 4.23k | m_bUnique); |
1007 | 4.23k | } |
1008 | | |
1009 | | /********************************************************************** |
1010 | | * TABINDNode::ReadIndexEntry() |
1011 | | * |
1012 | | * Read the key value and record/node ptr for the specified index entry |
1013 | | * inside the current node data. |
1014 | | * |
1015 | | * nEntryNo is the 0-based index of the index entry that we are interested |
1016 | | * in inside the current node. |
1017 | | * |
1018 | | * Returns the record/node ptr, and copies the key value inside the |
1019 | | * buffer pointed to by *pKeyValue... this assumes that *pKeyValue points |
1020 | | * to a buffer big enough to hold the key value (m_nKeyLength bytes). |
1021 | | * If pKeyValue == NULL, then this parameter is ignored and the key value |
1022 | | * is not copied. |
1023 | | **********************************************************************/ |
1024 | | GInt32 TABINDNode::ReadIndexEntry(int nEntryNo, GByte *pKeyValue) |
1025 | 7.81k | { |
1026 | 7.81k | GInt32 nRecordPtr = 0; |
1027 | 7.81k | if (nEntryNo >= 0 && nEntryNo < m_numEntriesInNode) |
1028 | 7.81k | { |
1029 | 7.81k | if (pKeyValue) |
1030 | 0 | { |
1031 | 0 | m_poDataBlock->GotoByteInBlock(12 + nEntryNo * (m_nKeyLength + 4)); |
1032 | 0 | m_poDataBlock->ReadBytes(m_nKeyLength, pKeyValue); |
1033 | 0 | } |
1034 | 7.81k | else |
1035 | 7.81k | { |
1036 | 7.81k | m_poDataBlock->GotoByteInBlock(12 + nEntryNo * (m_nKeyLength + 4) + |
1037 | 7.81k | m_nKeyLength); |
1038 | 7.81k | } |
1039 | | |
1040 | 7.81k | nRecordPtr = m_poDataBlock->ReadInt32(); |
1041 | 7.81k | } |
1042 | | |
1043 | 7.81k | return nRecordPtr; |
1044 | 7.81k | } |
1045 | | |
1046 | | /********************************************************************** |
1047 | | * TABINDNode::IndexKeyCmp() |
1048 | | * |
1049 | | * Compare the specified index entry with the key value, and |
1050 | | * return 0 if equal, an integer less than 0 if key is smaller than |
1051 | | * index entry, and an integer greater than 0 if key is bigger than |
1052 | | * index entry. |
1053 | | * |
1054 | | * nEntryNo is the 0-based index of the index entry that we are interested |
1055 | | * in inside the current node. |
1056 | | **********************************************************************/ |
1057 | | int TABINDNode::IndexKeyCmp(const GByte *pKeyValue, int nEntryNo) |
1058 | 263k | { |
1059 | 263k | CPLAssert(pKeyValue); |
1060 | 263k | CPLAssert(nEntryNo >= 0 && nEntryNo < m_numEntriesInNode); |
1061 | | |
1062 | 263k | m_poDataBlock->GotoByteInBlock(12 + nEntryNo * (m_nKeyLength + 4)); |
1063 | 263k | CPLAssert(m_nKeyLength <= 255); |
1064 | 263k | GByte abyKey[255]; |
1065 | 263k | if (m_poDataBlock->ReadBytes(m_nKeyLength, abyKey) != 0) |
1066 | 847 | return -1; |
1067 | 263k | return memcmp(pKeyValue, abyKey, m_nKeyLength); |
1068 | 263k | } |
1069 | | |
1070 | | /********************************************************************** |
1071 | | * TABINDNode::SetFieldType() |
1072 | | * |
1073 | | * Sets the field type for the current index and recursively set all |
1074 | | * children as well. |
1075 | | * This information will then be used in building the key values, etc. |
1076 | | * |
1077 | | * Returns 0 on success, -1 on error. |
1078 | | **********************************************************************/ |
1079 | | int TABINDNode::SetFieldType(TABFieldType eType) |
1080 | 201 | { |
1081 | 201 | if (m_fp == nullptr) |
1082 | 0 | { |
1083 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
1084 | 0 | "TABINDNode::SetFieldType(): File has not been opened yet!"); |
1085 | 0 | return -1; |
1086 | 0 | } |
1087 | | |
1088 | | /*----------------------------------------------------------------- |
1089 | | * Validate field type with key length |
1090 | | *----------------------------------------------------------------*/ |
1091 | 201 | if ((eType == TABFInteger && m_nKeyLength != 4) || |
1092 | 201 | (eType == TABFSmallInt && m_nKeyLength != 2) || |
1093 | 201 | (eType == TABFLargeInt && m_nKeyLength != 8) || |
1094 | 201 | (eType == TABFFloat && m_nKeyLength != 8) || |
1095 | 201 | (eType == TABFDecimal && m_nKeyLength != 8) || |
1096 | 201 | (eType == TABFDate && m_nKeyLength != 4) || |
1097 | 201 | (eType == TABFTime && m_nKeyLength != 4) || |
1098 | 201 | (eType == TABFDateTime && m_nKeyLength != 8) || |
1099 | 201 | (eType == TABFLogical && m_nKeyLength != 4)) |
1100 | 128 | { |
1101 | 128 | CPLError(CE_Failure, CPLE_IllegalArg, |
1102 | 128 | "Index key length (%d) does not match field type (%s).", |
1103 | 128 | m_nKeyLength, TABFIELDTYPE_2_STRING(eType)); |
1104 | 128 | return -1; |
1105 | 128 | } |
1106 | | |
1107 | 73 | m_eFieldType = eType; |
1108 | | |
1109 | | /*----------------------------------------------------------------- |
1110 | | * Pass the field type info to child nodes |
1111 | | *----------------------------------------------------------------*/ |
1112 | 73 | if (m_poCurChildNode) |
1113 | 0 | return m_poCurChildNode->SetFieldType(eType); |
1114 | | |
1115 | 73 | return 0; |
1116 | 73 | } |
1117 | | |
1118 | | /********************************************************************** |
1119 | | * TABINDNode::FindFirst() |
1120 | | * |
1121 | | * Start a new search in this node and its children for a key value. |
1122 | | * If the index is not unique, then FindNext() can be used to return |
1123 | | * the other values that correspond to the key. |
1124 | | * |
1125 | | * Return value: |
1126 | | * - the key's corresponding record number in the .DAT file (greater than 0) |
1127 | | * - 0 if the key was not found |
1128 | | * - or -1 if an error happened |
1129 | | **********************************************************************/ |
1130 | | GInt32 TABINDNode::FindFirst(const GByte *pKeyValue) |
1131 | 9.72k | { |
1132 | 9.72k | std::set<int> oSetVisitedNodePtr; |
1133 | 9.72k | return FindFirst(pKeyValue, oSetVisitedNodePtr); |
1134 | 9.72k | } |
1135 | | |
1136 | | GInt32 TABINDNode::FindFirst(const GByte *pKeyValue, |
1137 | | std::set<int> &oSetVisitedNodePtr) |
1138 | 13.0k | { |
1139 | 13.0k | if (m_poDataBlock == nullptr) |
1140 | 0 | { |
1141 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
1142 | 0 | "TABINDNode::Search(): Node has not been initialized yet!"); |
1143 | 0 | return -1; |
1144 | 0 | } |
1145 | | |
1146 | | /*----------------------------------------------------------------- |
1147 | | * Unless something has been broken, this method will be called by our |
1148 | | * parent node after it has established that we are the best candidate |
1149 | | * to contain the first instance of the key value. So there is no |
1150 | | * need to look in the previous or next nodes in the chain... if the |
1151 | | * value is not found in the current node block then it is not present |
1152 | | * in the index at all. |
1153 | | * |
1154 | | * m_nCurIndexEntry will be used to keep track of the search pointer |
1155 | | * when FindNext() will be used. |
1156 | | *----------------------------------------------------------------*/ |
1157 | 13.0k | m_nCurIndexEntry = 0; |
1158 | | |
1159 | 13.0k | if (m_nSubTreeDepth == 1) |
1160 | 4.48k | { |
1161 | | /*------------------------------------------------------------- |
1162 | | * Leaf node level... we look for an exact match |
1163 | | *------------------------------------------------------------*/ |
1164 | 79.1k | while (m_nCurIndexEntry < m_numEntriesInNode) |
1165 | 77.0k | { |
1166 | 77.0k | int nCmpStatus = IndexKeyCmp(pKeyValue, m_nCurIndexEntry); |
1167 | 77.0k | if (nCmpStatus > 0) |
1168 | 74.6k | { |
1169 | | /* Not there yet... (pKey > IndexEntry) */ |
1170 | 74.6k | m_nCurIndexEntry++; |
1171 | 74.6k | } |
1172 | 2.33k | else if (nCmpStatus == 0) |
1173 | 1.59k | { |
1174 | | /* Found it! Return the record number */ |
1175 | 1.59k | return ReadIndexEntry(m_nCurIndexEntry, nullptr); |
1176 | 1.59k | } |
1177 | 743 | else |
1178 | 743 | { |
1179 | | /* Item does not exist... return 0 */ |
1180 | 743 | return 0; |
1181 | 743 | } |
1182 | 77.0k | } |
1183 | 4.48k | } |
1184 | 8.53k | else |
1185 | 8.53k | { |
1186 | | /*------------------------------------------------------------- |
1187 | | * Index Node: Find the child node that is the best candidate to |
1188 | | * contain the value |
1189 | | * |
1190 | | * In the index tree at the node level, for each node entry inside |
1191 | | * the parent node, the key value (in the parent) corresponds to |
1192 | | * the value of the first key that you will find when you access |
1193 | | * the corresponding child node. |
1194 | | * |
1195 | | * This means that to find the child that contains the searched |
1196 | | * key, we look for the first index key >= pKeyValue and the child |
1197 | | * node that we are looking for is the one that precedes it. |
1198 | | * |
1199 | | * If the first key in the list is >= pKeyValue then this means |
1200 | | * that the pKeyValue does not exist in our children and we just |
1201 | | * return 0. We do not bother searching the previous node at the |
1202 | | * same level since this is the responsibility of our parent. |
1203 | | * |
1204 | | * The same way if the last indexkey in this node is < pKeyValue |
1205 | | * we won't bother searching the next node since this should also |
1206 | | * be taken care of by our parent. |
1207 | | *------------------------------------------------------------*/ |
1208 | 189k | while (m_nCurIndexEntry < m_numEntriesInNode) |
1209 | 186k | { |
1210 | 186k | int nCmpStatus = IndexKeyCmp(pKeyValue, m_nCurIndexEntry); |
1211 | | |
1212 | 186k | if (nCmpStatus > 0 && m_nCurIndexEntry + 1 < m_numEntriesInNode) |
1213 | 181k | { |
1214 | | /* Not there yet... (pKey > IndexEntry) */ |
1215 | 181k | m_nCurIndexEntry++; |
1216 | 181k | } |
1217 | 5.41k | else |
1218 | 5.41k | { |
1219 | | /*----------------------------------------------------- |
1220 | | * We either found an indexkey >= pKeyValue or reached |
1221 | | * the last entry in this node... still have to decide |
1222 | | * what we're going to do... |
1223 | | *----------------------------------------------------*/ |
1224 | 5.41k | if (nCmpStatus < 0 && m_nCurIndexEntry == 0) |
1225 | 304 | { |
1226 | | /*------------------------------------------------- |
1227 | | * First indexkey in block is > pKeyValue... |
1228 | | * the key definitely does not exist in our children. |
1229 | | * However, we still want to drill down the rest of the |
1230 | | * tree because this function is also used when looking |
1231 | | * for a node to insert a new value. |
1232 | | *-------------------------------------------------*/ |
1233 | | // Nothing special to do... just continue processing. |
1234 | 304 | } |
1235 | | |
1236 | | /*----------------------------------------------------- |
1237 | | * If we found an node for which pKeyValue < indexkey |
1238 | | * (or pKeyValue <= indexkey for non-unique indexes) then |
1239 | | * we access the preceding child node. |
1240 | | * |
1241 | | * Note that for indexkey == pKeyValue in non-unique indexes |
1242 | | * we also check in the preceding node because when keys |
1243 | | * are not unique then there are chances that the requested |
1244 | | * key could also be found at the end of the preceding node. |
1245 | | * In this case, if we don't find the key in the preceding |
1246 | | * node then we'll do a second search in the current node. |
1247 | | *----------------------------------------------------*/ |
1248 | 5.41k | int numChildrenToVisit = 1; |
1249 | 5.41k | if (m_nCurIndexEntry > 0 && |
1250 | 5.41k | (nCmpStatus < 0 || (nCmpStatus == 0 && !m_bUnique))) |
1251 | 4.71k | { |
1252 | 4.71k | m_nCurIndexEntry--; |
1253 | 4.71k | if (nCmpStatus == 0) |
1254 | 1.57k | numChildrenToVisit = 2; |
1255 | 4.71k | } |
1256 | | |
1257 | | /*----------------------------------------------------- |
1258 | | * OK, now it is time to load/access the candidate child nodes. |
1259 | | *----------------------------------------------------*/ |
1260 | 5.41k | int nRetValue = 0; |
1261 | 5.41k | for (int iChild = 0; |
1262 | 9.85k | nRetValue == 0 && iChild < numChildrenToVisit; iChild++) |
1263 | 6.21k | { |
1264 | | // If we're doing a second pass then jump to next entry |
1265 | 6.21k | if (iChild > 0) |
1266 | 799 | m_nCurIndexEntry++; |
1267 | | |
1268 | 6.21k | int nChildNodePtr = |
1269 | 6.21k | ReadIndexEntry(m_nCurIndexEntry, nullptr); |
1270 | 6.21k | if (nChildNodePtr <= 0) |
1271 | 1.14k | { |
1272 | | /* Invalid child node??? */ |
1273 | 1.14k | nRetValue = 0; |
1274 | 1.14k | continue; |
1275 | 1.14k | } |
1276 | 5.06k | else if (oSetVisitedNodePtr.find(nChildNodePtr) != |
1277 | 5.06k | oSetVisitedNodePtr.end()) |
1278 | 276 | { |
1279 | 276 | CPLError(CE_Failure, CPLE_AppDefined, |
1280 | 276 | "Invalid child node pointer structure"); |
1281 | 276 | return -1; |
1282 | 276 | } |
1283 | 4.79k | else if ((nChildNodePtr % 512) != 0) |
1284 | 511 | { |
1285 | 511 | CPLError(CE_Failure, CPLE_AppDefined, |
1286 | 511 | "Invalid child node pointer"); |
1287 | 511 | return -1; |
1288 | 511 | } |
1289 | 4.27k | else if (m_poCurChildNode == nullptr) |
1290 | 78 | { |
1291 | | /* Child node has never been initialized...do it now!*/ |
1292 | | |
1293 | 78 | m_poCurChildNode = new TABINDNode(m_eAccessMode); |
1294 | 78 | if (m_poCurChildNode->InitNode( |
1295 | 78 | m_fp, nChildNodePtr, m_nKeyLength, |
1296 | 78 | m_nSubTreeDepth - 1, m_bUnique, |
1297 | 78 | m_poBlockManagerRef, this) != 0 || |
1298 | 78 | m_poCurChildNode->SetFieldType(m_eFieldType) != 0) |
1299 | 46 | { |
1300 | | // An error happened... and was already reported |
1301 | 46 | return -1; |
1302 | 46 | } |
1303 | 78 | } |
1304 | | |
1305 | 4.23k | if (m_poCurChildNode->GotoNodePtr(nChildNodePtr) != 0) |
1306 | 946 | { |
1307 | | // An error happened and has already been reported |
1308 | 946 | return -1; |
1309 | 946 | } |
1310 | | |
1311 | 3.28k | oSetVisitedNodePtr.insert(nChildNodePtr); |
1312 | 3.28k | nRetValue = m_poCurChildNode->FindFirst(pKeyValue, |
1313 | 3.28k | oSetVisitedNodePtr); |
1314 | 3.28k | } /*for iChild*/ |
1315 | | |
1316 | 3.63k | return nRetValue; |
1317 | 5.41k | } // else |
1318 | 186k | } // while numEntries |
1319 | | |
1320 | | // No node was found that contains the key value. |
1321 | | // We should never get here... only leaf nodes should return 0 |
1322 | 3.11k | CPLAssert(false); |
1323 | 3.11k | return 0; |
1324 | 8.53k | } |
1325 | | |
1326 | 2.14k | return 0; // Not found |
1327 | 13.0k | } |
1328 | | |
1329 | | /********************************************************************** |
1330 | | * TABINDNode::FindNext() |
1331 | | * |
1332 | | * Continue the search previously started by FindFirst() in this node |
1333 | | * and its children for a key value. |
1334 | | * |
1335 | | * Return value: |
1336 | | * - the key's corresponding record number in the .DAT file (greater than 0) |
1337 | | * - 0 if the key was not found |
1338 | | * - or -1 if an error happened |
1339 | | **********************************************************************/ |
1340 | | GInt32 TABINDNode::FindNext(GByte *pKeyValue) |
1341 | 0 | { |
1342 | 0 | if (m_poDataBlock == nullptr) |
1343 | 0 | { |
1344 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
1345 | 0 | "TABINDNode::Search(): Node has not been initialized yet!"); |
1346 | 0 | return -1; |
1347 | 0 | } |
1348 | | |
1349 | | /*----------------------------------------------------------------- |
1350 | | * m_nCurIndexEntry is the index of the last item that has been |
1351 | | * returned by FindFirst()/FindNext(). |
1352 | | *----------------------------------------------------------------*/ |
1353 | | |
1354 | 0 | if (m_nSubTreeDepth == 1) |
1355 | 0 | { |
1356 | | /*------------------------------------------------------------- |
1357 | | * Leaf node level... check if the next entry is an exact match |
1358 | | *------------------------------------------------------------*/ |
1359 | 0 | m_nCurIndexEntry++; |
1360 | 0 | if (m_nCurIndexEntry >= m_numEntriesInNode && m_nNextNodePtr > 0) |
1361 | 0 | { |
1362 | | // We're at the end of a node ... continue with next node |
1363 | 0 | GotoNodePtr(m_nNextNodePtr); |
1364 | 0 | m_nCurIndexEntry = 0; |
1365 | 0 | } |
1366 | |
|
1367 | 0 | if (m_nCurIndexEntry < m_numEntriesInNode && |
1368 | 0 | IndexKeyCmp(pKeyValue, m_nCurIndexEntry) == 0) |
1369 | 0 | { |
1370 | | /* Found it! Return the record number */ |
1371 | 0 | return ReadIndexEntry(m_nCurIndexEntry, nullptr); |
1372 | 0 | } |
1373 | 0 | else |
1374 | 0 | { |
1375 | | /* No more items with that key... return 0 */ |
1376 | 0 | return 0; |
1377 | 0 | } |
1378 | 0 | } |
1379 | 0 | else |
1380 | 0 | { |
1381 | | /*------------------------------------------------------------- |
1382 | | * Index Node: just pass the search to this child node. |
1383 | | *------------------------------------------------------------*/ |
1384 | 0 | while (m_nCurIndexEntry < m_numEntriesInNode) |
1385 | 0 | { |
1386 | 0 | if (m_poCurChildNode != nullptr) |
1387 | 0 | return m_poCurChildNode->FindNext(pKeyValue); |
1388 | 0 | } |
1389 | 0 | } |
1390 | | |
1391 | | // No more nodes were found that contain the key value. |
1392 | 0 | return 0; |
1393 | 0 | } |
1394 | | |
1395 | | /********************************************************************** |
1396 | | * TABINDNode::CommitToFile() |
1397 | | * |
1398 | | * For write access, write current block and its children to file. |
1399 | | * |
1400 | | * note: TABRawBinBlock::CommitToFile() does nothing unless the block has |
1401 | | * been modified. (it has an internal bModified flag) |
1402 | | * |
1403 | | * Returns 0 on success, -1 on error. |
1404 | | **********************************************************************/ |
1405 | | int TABINDNode::CommitToFile() |
1406 | 0 | { |
1407 | 0 | if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) || |
1408 | 0 | m_poDataBlock == nullptr) |
1409 | 0 | return -1; |
1410 | | |
1411 | 0 | if (m_poCurChildNode) |
1412 | 0 | { |
1413 | 0 | if (m_poCurChildNode->CommitToFile() != 0) |
1414 | 0 | return -1; |
1415 | | |
1416 | 0 | m_nSubTreeDepth = m_poCurChildNode->GetSubTreeDepth() + 1; |
1417 | 0 | } |
1418 | | |
1419 | 0 | return m_poDataBlock->CommitToFile(); |
1420 | 0 | } |
1421 | | |
1422 | | /********************************************************************** |
1423 | | * TABINDNode::AddEntry() |
1424 | | * |
1425 | | * Add an .DAT record entry for pKeyValue in this index |
1426 | | * |
1427 | | * nRecordNo is the .DAT record number, record numbers start at 1. |
1428 | | * |
1429 | | * In order to insert a new value, the root node first does a FindFirst() |
1430 | | * that will load the whole tree branch up to the insertion point. |
1431 | | * Then AddEntry() is recursively called up to the leaf node level for |
1432 | | * the insertion of the actual value. |
1433 | | * If the leaf node is full then it will be split and if necessary the |
1434 | | * split will propagate up in the tree through the pointer that each node |
1435 | | * has on its parent. |
1436 | | * |
1437 | | * If bAddInThisNodeOnly=TRUE, then the entry is added only locally and |
1438 | | * we do not try to update the child node. This is used when the parent |
1439 | | * of a node that is being split has to be updated. |
1440 | | * |
1441 | | * bInsertAfterCurChild forces the insertion to happen immediately after |
1442 | | * the m_nCurIndexEntry. This works only when bAddInThisNodeOnly=TRUE. |
1443 | | * The default is to search the node for a an insertion point. |
1444 | | * |
1445 | | * Returns 0 on success, -1 on error |
1446 | | **********************************************************************/ |
1447 | | int TABINDNode::AddEntry(GByte *pKeyValue, GInt32 nRecordNo, |
1448 | | GBool bAddInThisNodeOnly /*=FALSE*/, |
1449 | | GBool bInsertAfterCurChild /*=FALSE*/, |
1450 | | GBool bMakeNewEntryCurChild /*=FALSE*/) |
1451 | 0 | { |
1452 | 0 | if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) || |
1453 | 0 | m_poDataBlock == nullptr) |
1454 | 0 | return -1; |
1455 | | |
1456 | | /*----------------------------------------------------------------- |
1457 | | * If I'm the root node, then do a FindFirst() to init all the nodes |
1458 | | * and to make all of them point to the insertion point. |
1459 | | *----------------------------------------------------------------*/ |
1460 | 0 | if (m_poParentNodeRef == nullptr && !bAddInThisNodeOnly) |
1461 | 0 | { |
1462 | 0 | if (FindFirst(pKeyValue) < 0) |
1463 | 0 | return -1; // Error happened and has already been reported. |
1464 | 0 | } |
1465 | | |
1466 | 0 | if (m_poCurChildNode && !bAddInThisNodeOnly) |
1467 | 0 | { |
1468 | 0 | CPLAssert(m_nSubTreeDepth > 1); |
1469 | | /*------------------------------------------------------------- |
1470 | | * Propagate the call down to our children |
1471 | | * Note: this recursive call could result in new levels of nodes |
1472 | | * being added under our feet by SplitRootnode() so it is very |
1473 | | * important to return right after this call or we might not be |
1474 | | * able to recognize this node at the end of the call! |
1475 | | *------------------------------------------------------------*/ |
1476 | 0 | return m_poCurChildNode->AddEntry(pKeyValue, nRecordNo); |
1477 | 0 | } |
1478 | 0 | else |
1479 | 0 | { |
1480 | | /*------------------------------------------------------------- |
1481 | | * OK, we're a leaf node... this is where the real work happens!!! |
1482 | | *------------------------------------------------------------*/ |
1483 | 0 | CPLAssert(m_nSubTreeDepth == 1 || bAddInThisNodeOnly); |
1484 | | |
1485 | | /*------------------------------------------------------------- |
1486 | | * First thing to do is make sure that there is room for a new |
1487 | | * entry in this node, and to split it if necessary. |
1488 | | *------------------------------------------------------------*/ |
1489 | 0 | if (GetNumEntries() == GetMaxNumEntries()) |
1490 | 0 | { |
1491 | 0 | if (m_poParentNodeRef == nullptr) |
1492 | 0 | { |
1493 | | /*----------------------------------------------------- |
1494 | | * Splitting the root node adds one level to the tree, so |
1495 | | * after splitting we just redirect the call to our child. |
1496 | | *----------------------------------------------------*/ |
1497 | 0 | if (SplitRootNode() != 0) |
1498 | 0 | return -1; // Error happened and has already been reported |
1499 | | |
1500 | 0 | CPLAssert(m_poCurChildNode); |
1501 | 0 | CPLAssert(m_nSubTreeDepth > 1); |
1502 | 0 | return m_poCurChildNode->AddEntry( |
1503 | 0 | pKeyValue, nRecordNo, bAddInThisNodeOnly, |
1504 | 0 | bInsertAfterCurChild, bMakeNewEntryCurChild); |
1505 | 0 | } |
1506 | 0 | else |
1507 | 0 | { |
1508 | | /*----------------------------------------------------- |
1509 | | * Splitting a regular node will leave it 50% full. |
1510 | | *----------------------------------------------------*/ |
1511 | 0 | if (SplitNode() != 0) |
1512 | 0 | return -1; |
1513 | 0 | } |
1514 | 0 | } |
1515 | | |
1516 | | /*------------------------------------------------------------- |
1517 | | * Insert new key/value at the right position in node. |
1518 | | *------------------------------------------------------------*/ |
1519 | 0 | if (InsertEntry(pKeyValue, nRecordNo, bInsertAfterCurChild, |
1520 | 0 | bMakeNewEntryCurChild) != 0) |
1521 | 0 | return -1; |
1522 | 0 | } |
1523 | | |
1524 | 0 | return 0; |
1525 | 0 | } |
1526 | | |
1527 | | /********************************************************************** |
1528 | | * TABINDNode::InsertEntry() |
1529 | | * |
1530 | | * (private method) |
1531 | | * |
1532 | | * Insert a key/value pair in the current node buffer. |
1533 | | * |
1534 | | * Returns 0 on success, -1 on error |
1535 | | **********************************************************************/ |
1536 | | int TABINDNode::InsertEntry(GByte *pKeyValue, GInt32 nRecordNo, |
1537 | | GBool bInsertAfterCurChild /*=FALSE*/, |
1538 | | GBool bMakeNewEntryCurChild /*=FALSE*/) |
1539 | 0 | { |
1540 | 0 | int iInsertAt = 0; |
1541 | |
|
1542 | 0 | if (GetNumEntries() >= GetMaxNumEntries()) |
1543 | 0 | { |
1544 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
1545 | 0 | "Node is full! Cannot insert key!"); |
1546 | 0 | return -1; |
1547 | 0 | } |
1548 | | |
1549 | | /*----------------------------------------------------------------- |
1550 | | * Find the spot where the key belongs |
1551 | | *----------------------------------------------------------------*/ |
1552 | 0 | if (bInsertAfterCurChild) |
1553 | 0 | { |
1554 | 0 | iInsertAt = m_nCurIndexEntry + 1; |
1555 | 0 | } |
1556 | 0 | else |
1557 | 0 | { |
1558 | 0 | while (iInsertAt < m_numEntriesInNode) |
1559 | 0 | { |
1560 | 0 | int nCmpStatus = IndexKeyCmp(pKeyValue, iInsertAt); |
1561 | 0 | if (nCmpStatus <= 0) |
1562 | 0 | { |
1563 | 0 | break; |
1564 | 0 | } |
1565 | 0 | iInsertAt++; |
1566 | 0 | } |
1567 | 0 | } |
1568 | |
|
1569 | 0 | m_poDataBlock->GotoByteInBlock(12 + iInsertAt * (m_nKeyLength + 4)); |
1570 | | |
1571 | | /*----------------------------------------------------------------- |
1572 | | * Shift all entries that follow in the array |
1573 | | *----------------------------------------------------------------*/ |
1574 | 0 | if (iInsertAt < m_numEntriesInNode) |
1575 | 0 | { |
1576 | | // Since we use memmove() directly, we need to inform |
1577 | | // m_poDataBlock that the upper limit of the buffer will move |
1578 | 0 | m_poDataBlock->GotoByteInBlock(12 + (m_numEntriesInNode + 1) * |
1579 | 0 | (m_nKeyLength + 4)); |
1580 | 0 | m_poDataBlock->GotoByteInBlock(12 + iInsertAt * (m_nKeyLength + 4)); |
1581 | |
|
1582 | 0 | memmove(m_poDataBlock->GetCurDataPtr() + (m_nKeyLength + 4), |
1583 | 0 | m_poDataBlock->GetCurDataPtr(), |
1584 | 0 | static_cast<size_t>(m_numEntriesInNode - iInsertAt) * |
1585 | 0 | (m_nKeyLength + 4)); |
1586 | 0 | } |
1587 | | |
1588 | | /*----------------------------------------------------------------- |
1589 | | * Write new entry |
1590 | | *----------------------------------------------------------------*/ |
1591 | 0 | m_poDataBlock->WriteBytes(m_nKeyLength, pKeyValue); |
1592 | 0 | m_poDataBlock->WriteInt32(nRecordNo); |
1593 | |
|
1594 | 0 | m_numEntriesInNode++; |
1595 | 0 | m_poDataBlock->GotoByteInBlock(0); |
1596 | 0 | m_poDataBlock->WriteInt32(m_numEntriesInNode); |
1597 | |
|
1598 | 0 | if (bMakeNewEntryCurChild) |
1599 | 0 | m_nCurIndexEntry = iInsertAt; |
1600 | 0 | else if (m_nCurIndexEntry >= iInsertAt) |
1601 | 0 | m_nCurIndexEntry++; |
1602 | | |
1603 | | /*----------------------------------------------------------------- |
1604 | | * If we replaced the first entry in the node, then this node's key |
1605 | | * changes and we have to update the reference in the parent node. |
1606 | | *----------------------------------------------------------------*/ |
1607 | 0 | if (iInsertAt == 0 && m_poParentNodeRef) |
1608 | 0 | { |
1609 | 0 | if (m_poParentNodeRef->UpdateCurChildEntry(GetNodeKey(), |
1610 | 0 | GetNodeBlockPtr()) != 0) |
1611 | 0 | return -1; |
1612 | 0 | } |
1613 | | |
1614 | 0 | return 0; |
1615 | 0 | } |
1616 | | |
1617 | | /********************************************************************** |
1618 | | * TABINDNode::UpdateCurChildEntry() |
1619 | | * |
1620 | | * Update the key for the current child node. This method is called by |
1621 | | * the child when its first entry (defining its node key) is changed. |
1622 | | * |
1623 | | * Returns 0 on success, -1 on error |
1624 | | **********************************************************************/ |
1625 | | int TABINDNode::UpdateCurChildEntry(GByte *pKeyValue, GInt32 nRecordNo) |
1626 | 0 | { |
1627 | | |
1628 | | /*----------------------------------------------------------------- |
1629 | | * Update current child entry with the info for the first node. |
1630 | | * |
1631 | | * For some reason, the key for first entry of the first node of each |
1632 | | * level has to be set to 0 except for the leaf level. |
1633 | | *----------------------------------------------------------------*/ |
1634 | 0 | m_poDataBlock->GotoByteInBlock(12 + m_nCurIndexEntry * (m_nKeyLength + 4)); |
1635 | |
|
1636 | 0 | int ret; |
1637 | 0 | if (m_nCurIndexEntry == 0 && m_nSubTreeDepth > 1 && m_nPrevNodePtr == 0) |
1638 | 0 | { |
1639 | 0 | ret = m_poDataBlock->WriteZeros(m_nKeyLength); |
1640 | 0 | } |
1641 | 0 | else |
1642 | 0 | { |
1643 | 0 | ret = m_poDataBlock->WriteBytes(m_nKeyLength, pKeyValue); |
1644 | 0 | } |
1645 | 0 | if (ret == 0) |
1646 | 0 | ret = m_poDataBlock->WriteInt32(nRecordNo); |
1647 | |
|
1648 | 0 | return ret; |
1649 | 0 | } |
1650 | | |
1651 | | /********************************************************************** |
1652 | | * TABINDNode::UpdateSplitChild() |
1653 | | * |
1654 | | * Update the key and/or record ptr information corresponding to the |
1655 | | * current child node. |
1656 | | * |
1657 | | * Returns 0 on success, -1 on error |
1658 | | **********************************************************************/ |
1659 | | int TABINDNode::UpdateSplitChild(GByte *pKeyValue1, GInt32 nRecordNo1, |
1660 | | GByte *pKeyValue2, GInt32 nRecordNo2, |
1661 | | int nNewCurChildNo /* 1 or 2 */) |
1662 | 0 | { |
1663 | | |
1664 | | /*----------------------------------------------------------------- |
1665 | | * Update current child entry with the info for the first node. |
1666 | | * |
1667 | | * For some reason, the key for first entry of the first node of each |
1668 | | * level has to be set to 0 except for the leaf level. |
1669 | | *----------------------------------------------------------------*/ |
1670 | 0 | m_poDataBlock->GotoByteInBlock(12 + m_nCurIndexEntry * (m_nKeyLength + 4)); |
1671 | |
|
1672 | 0 | if (m_nCurIndexEntry == 0 && m_nSubTreeDepth > 1 && m_nPrevNodePtr == 0) |
1673 | 0 | { |
1674 | 0 | m_poDataBlock->WriteZeros(m_nKeyLength); |
1675 | 0 | } |
1676 | 0 | else |
1677 | 0 | { |
1678 | 0 | m_poDataBlock->WriteBytes(m_nKeyLength, pKeyValue1); |
1679 | 0 | } |
1680 | 0 | m_poDataBlock->WriteInt32(nRecordNo1); |
1681 | | |
1682 | | /*----------------------------------------------------------------- |
1683 | | * Add an entry for the second node after the current one and ask |
1684 | | * AddEntry() to update m_nCurIndexEntry if the new node should |
1685 | | * become the new current child. |
1686 | | *----------------------------------------------------------------*/ |
1687 | 0 | if (AddEntry(pKeyValue2, nRecordNo2, TRUE, /* bInThisNodeOnly */ |
1688 | 0 | TRUE, /* bInsertAfterCurChild */ |
1689 | 0 | (nNewCurChildNo == 2)) != 0) |
1690 | 0 | { |
1691 | 0 | return -1; |
1692 | 0 | } |
1693 | | |
1694 | 0 | return 0; |
1695 | 0 | } |
1696 | | |
1697 | | /********************************************************************** |
1698 | | * TABINDNode::SplitNode() |
1699 | | * |
1700 | | * (private method) |
1701 | | * |
1702 | | * Split a node, update the references in the parent node, etc. |
1703 | | * Note that Root Nodes cannot be split using this method... SplitRootNode() |
1704 | | * should be used instead. |
1705 | | * |
1706 | | * The node is split in a way that the current child stays inside this |
1707 | | * node object, and a new node is created for the other half of the |
1708 | | * entries. This way, the object references in this node's parent and in its |
1709 | | * current child all remain valid. The new node is not kept in memory, |
1710 | | * it is written to disk right away. |
1711 | | * |
1712 | | * Returns 0 on success, -1 on error |
1713 | | **********************************************************************/ |
1714 | | int TABINDNode::SplitNode() |
1715 | 0 | { |
1716 | 0 | CPLAssert(m_numEntriesInNode >= 2); |
1717 | 0 | CPLAssert(m_poParentNodeRef); // This func. does not work for root nodes |
1718 | | |
1719 | | /*----------------------------------------------------------------- |
1720 | | * Prepare new node |
1721 | | *----------------------------------------------------------------*/ |
1722 | 0 | int numInNode1 = (m_numEntriesInNode + 1) / 2; |
1723 | 0 | int numInNode2 = m_numEntriesInNode - numInNode1; |
1724 | |
|
1725 | 0 | TABINDNode *poNewNode = new TABINDNode(m_eAccessMode); |
1726 | |
|
1727 | 0 | if (m_nCurIndexEntry < numInNode1) |
1728 | 0 | { |
1729 | | /*------------------------------------------------------------- |
1730 | | * We will move the second half of the array to a new node. |
1731 | | *------------------------------------------------------------*/ |
1732 | 0 | if (poNewNode->InitNode(m_fp, 0, m_nKeyLength, m_nSubTreeDepth, |
1733 | 0 | m_bUnique, m_poBlockManagerRef, |
1734 | 0 | m_poParentNodeRef, GetNodeBlockPtr(), |
1735 | 0 | m_nNextNodePtr) != 0 || |
1736 | 0 | poNewNode->SetFieldType(m_eFieldType) != 0) |
1737 | 0 | { |
1738 | 0 | delete poNewNode; |
1739 | 0 | return -1; |
1740 | 0 | } |
1741 | | |
1742 | | // We have to update m_nPrevNodePtr in the node that used to follow |
1743 | | // the current node and will now follow the new node. |
1744 | 0 | if (m_nNextNodePtr) |
1745 | 0 | { |
1746 | 0 | TABINDNode *poTmpNode = new TABINDNode(m_eAccessMode); |
1747 | 0 | if (poTmpNode->InitNode( |
1748 | 0 | m_fp, m_nNextNodePtr, m_nKeyLength, m_nSubTreeDepth, |
1749 | 0 | m_bUnique, m_poBlockManagerRef, m_poParentNodeRef) != 0 || |
1750 | 0 | poTmpNode->SetPrevNodePtr(poNewNode->GetNodeBlockPtr()) != 0 || |
1751 | 0 | poTmpNode->CommitToFile() != 0) |
1752 | 0 | { |
1753 | 0 | delete poTmpNode; |
1754 | 0 | delete poNewNode; |
1755 | 0 | return -1; |
1756 | 0 | } |
1757 | 0 | delete poTmpNode; |
1758 | 0 | } |
1759 | | |
1760 | 0 | m_nNextNodePtr = poNewNode->GetNodeBlockPtr(); |
1761 | | |
1762 | | // Move half the entries to the new block |
1763 | 0 | m_poDataBlock->GotoByteInBlock(12 + numInNode1 * (m_nKeyLength + 4)); |
1764 | |
|
1765 | 0 | if (poNewNode->SetNodeBufferDirectly( |
1766 | 0 | numInNode2, m_poDataBlock->GetCurDataPtr()) != 0) |
1767 | 0 | { |
1768 | 0 | delete poNewNode; |
1769 | 0 | return -1; |
1770 | 0 | } |
1771 | | |
1772 | | #ifdef DEBUG |
1773 | | // Just in case, reset space previously used by moved entries |
1774 | | memset(m_poDataBlock->GetCurDataPtr(), 0, |
1775 | | static_cast<size_t>(numInNode2) * (m_nKeyLength + 4)); |
1776 | | #endif |
1777 | | // And update current node members |
1778 | 0 | m_numEntriesInNode = numInNode1; |
1779 | | |
1780 | | // Update parent node with new children info |
1781 | 0 | if (m_poParentNodeRef) |
1782 | 0 | { |
1783 | 0 | if (m_poParentNodeRef->UpdateSplitChild( |
1784 | 0 | GetNodeKey(), GetNodeBlockPtr(), poNewNode->GetNodeKey(), |
1785 | 0 | poNewNode->GetNodeBlockPtr(), 1) != 0) |
1786 | 0 | { |
1787 | 0 | delete poNewNode; |
1788 | 0 | return -1; |
1789 | 0 | } |
1790 | 0 | } |
1791 | 0 | } |
1792 | 0 | else |
1793 | 0 | { |
1794 | | /*------------------------------------------------------------- |
1795 | | * We will move the first half of the array to a new node. |
1796 | | *------------------------------------------------------------*/ |
1797 | 0 | if (poNewNode->InitNode(m_fp, 0, m_nKeyLength, m_nSubTreeDepth, |
1798 | 0 | m_bUnique, m_poBlockManagerRef, |
1799 | 0 | m_poParentNodeRef, m_nPrevNodePtr, |
1800 | 0 | GetNodeBlockPtr()) != 0 || |
1801 | 0 | poNewNode->SetFieldType(m_eFieldType) != 0) |
1802 | 0 | { |
1803 | 0 | delete poNewNode; |
1804 | 0 | return -1; |
1805 | 0 | } |
1806 | | |
1807 | | // We have to update m_nNextNodePtr in the node that used to precede |
1808 | | // the current node and will now precede the new node. |
1809 | 0 | if (m_nPrevNodePtr) |
1810 | 0 | { |
1811 | 0 | TABINDNode *poTmpNode = new TABINDNode(m_eAccessMode); |
1812 | 0 | if (poTmpNode->InitNode( |
1813 | 0 | m_fp, m_nPrevNodePtr, m_nKeyLength, m_nSubTreeDepth, |
1814 | 0 | m_bUnique, m_poBlockManagerRef, m_poParentNodeRef) != 0 || |
1815 | 0 | poTmpNode->SetNextNodePtr(poNewNode->GetNodeBlockPtr()) != 0 || |
1816 | 0 | poTmpNode->CommitToFile() != 0) |
1817 | 0 | { |
1818 | 0 | delete poTmpNode; |
1819 | 0 | delete poNewNode; |
1820 | 0 | return -1; |
1821 | 0 | } |
1822 | 0 | delete poTmpNode; |
1823 | 0 | } |
1824 | | |
1825 | 0 | m_nPrevNodePtr = poNewNode->GetNodeBlockPtr(); |
1826 | | |
1827 | | // Move half the entries to the new block |
1828 | 0 | m_poDataBlock->GotoByteInBlock(12 + 0); |
1829 | |
|
1830 | 0 | if (poNewNode->SetNodeBufferDirectly( |
1831 | 0 | numInNode1, m_poDataBlock->GetCurDataPtr()) != 0) |
1832 | 0 | { |
1833 | 0 | delete poNewNode; |
1834 | 0 | return -1; |
1835 | 0 | } |
1836 | | |
1837 | | // Shift the second half of the entries to beginning of buffer |
1838 | 0 | memmove(m_poDataBlock->GetCurDataPtr(), |
1839 | 0 | m_poDataBlock->GetCurDataPtr() + |
1840 | 0 | numInNode1 * (m_nKeyLength + 4), |
1841 | 0 | static_cast<size_t>(numInNode2) * (m_nKeyLength + 4)); |
1842 | |
|
1843 | | #ifdef DEBUG |
1844 | | // Just in case, reset space previously used by moved entries |
1845 | | memset(m_poDataBlock->GetCurDataPtr() + |
1846 | | static_cast<size_t>(numInNode2) * (m_nKeyLength + 4), |
1847 | | 0, static_cast<size_t>(numInNode1) * (m_nKeyLength + 4)); |
1848 | | #endif |
1849 | | |
1850 | | // And update current node members |
1851 | 0 | m_numEntriesInNode = numInNode2; |
1852 | 0 | m_nCurIndexEntry -= numInNode1; |
1853 | | |
1854 | | // Update parent node with new children info |
1855 | 0 | if (m_poParentNodeRef) |
1856 | 0 | { |
1857 | 0 | if (m_poParentNodeRef->UpdateSplitChild( |
1858 | 0 | poNewNode->GetNodeKey(), poNewNode->GetNodeBlockPtr(), |
1859 | 0 | GetNodeKey(), GetNodeBlockPtr(), 2) != 0) |
1860 | 0 | { |
1861 | 0 | delete poNewNode; |
1862 | 0 | return -1; |
1863 | 0 | } |
1864 | 0 | } |
1865 | 0 | } |
1866 | | |
1867 | | /*----------------------------------------------------------------- |
1868 | | * Update current node header |
1869 | | *----------------------------------------------------------------*/ |
1870 | 0 | m_poDataBlock->GotoByteInBlock(0); |
1871 | 0 | m_poDataBlock->WriteInt32(m_numEntriesInNode); |
1872 | 0 | m_poDataBlock->WriteInt32(m_nPrevNodePtr); |
1873 | 0 | m_poDataBlock->WriteInt32(m_nNextNodePtr); |
1874 | | |
1875 | | /*----------------------------------------------------------------- |
1876 | | * Flush and destroy temporary node |
1877 | | *----------------------------------------------------------------*/ |
1878 | 0 | if (poNewNode->CommitToFile() != 0) |
1879 | 0 | { |
1880 | 0 | delete poNewNode; |
1881 | 0 | return -1; |
1882 | 0 | } |
1883 | | |
1884 | 0 | delete poNewNode; |
1885 | |
|
1886 | 0 | return 0; |
1887 | 0 | } |
1888 | | |
1889 | | /********************************************************************** |
1890 | | * TABINDNode::SplitRootNode() |
1891 | | * |
1892 | | * (private method) |
1893 | | * |
1894 | | * Split a Root Node. |
1895 | | * First, a level of nodes must be added to the tree, then the contents |
1896 | | * of what used to be the root node is moved 1 level down and then that |
1897 | | * node is split like a regular node. |
1898 | | * |
1899 | | * Returns 0 on success, -1 on error |
1900 | | **********************************************************************/ |
1901 | | int TABINDNode::SplitRootNode() |
1902 | 0 | { |
1903 | | /*----------------------------------------------------------------- |
1904 | | * Since a root note cannot be split, we add a level of nodes |
1905 | | * under it and we'll do the split at that level. |
1906 | | *----------------------------------------------------------------*/ |
1907 | 0 | TABINDNode *poNewNode = new TABINDNode(m_eAccessMode); |
1908 | |
|
1909 | 0 | if (poNewNode->InitNode(m_fp, 0, m_nKeyLength, m_nSubTreeDepth, m_bUnique, |
1910 | 0 | m_poBlockManagerRef, this, 0, 0) != 0 || |
1911 | 0 | poNewNode->SetFieldType(m_eFieldType) != 0) |
1912 | 0 | { |
1913 | 0 | delete poNewNode; |
1914 | 0 | return -1; |
1915 | 0 | } |
1916 | | |
1917 | | // Move all entries to the new child |
1918 | 0 | m_poDataBlock->GotoByteInBlock(12 + 0); |
1919 | 0 | if (poNewNode->SetNodeBufferDirectly( |
1920 | 0 | m_numEntriesInNode, m_poDataBlock->GetCurDataPtr(), |
1921 | 0 | m_nCurIndexEntry, m_poCurChildNode) != 0) |
1922 | 0 | { |
1923 | 0 | delete poNewNode; |
1924 | 0 | return -1; |
1925 | 0 | } |
1926 | | |
1927 | | #ifdef DEBUG |
1928 | | // Just in case, reset space previously used by moved entries |
1929 | | memset(m_poDataBlock->GetCurDataPtr(), 0, |
1930 | | static_cast<size_t>(m_numEntriesInNode) * (m_nKeyLength + 4)); |
1931 | | #endif |
1932 | | |
1933 | | /*----------------------------------------------------------------- |
1934 | | * Rewrite current node. (the new root node) |
1935 | | *----------------------------------------------------------------*/ |
1936 | 0 | m_numEntriesInNode = 0; |
1937 | 0 | m_nSubTreeDepth++; |
1938 | |
|
1939 | 0 | m_poDataBlock->GotoByteInBlock(0); |
1940 | 0 | m_poDataBlock->WriteInt32(m_numEntriesInNode); |
1941 | |
|
1942 | 0 | InsertEntry(poNewNode->GetNodeKey(), poNewNode->GetNodeBlockPtr()); |
1943 | | |
1944 | | /*----------------------------------------------------------------- |
1945 | | * Keep a reference to the new child |
1946 | | *----------------------------------------------------------------*/ |
1947 | 0 | m_poCurChildNode = poNewNode; |
1948 | 0 | m_nCurIndexEntry = 0; |
1949 | | |
1950 | | /*----------------------------------------------------------------- |
1951 | | * And finally force the child to split itself |
1952 | | *----------------------------------------------------------------*/ |
1953 | 0 | return m_poCurChildNode->SplitNode(); |
1954 | 0 | } |
1955 | | |
1956 | | /********************************************************************** |
1957 | | * TABINDNode::SetNodeBufferDirectly() |
1958 | | * |
1959 | | * (private method) |
1960 | | * |
1961 | | * Set the key/value part of the nodes buffer and the pointers to the |
1962 | | * current child directly. This is used when copying info to a new node |
1963 | | * in SplitNode() and SplitRootNode() |
1964 | | * |
1965 | | * Returns 0 on success, -1 on error |
1966 | | **********************************************************************/ |
1967 | | int TABINDNode::SetNodeBufferDirectly(int numEntries, GByte *pBuf, |
1968 | | int nCurIndexEntry /*=0*/, |
1969 | | TABINDNode *poCurChild /*=NULL*/) |
1970 | 0 | { |
1971 | 0 | m_poDataBlock->GotoByteInBlock(0); |
1972 | 0 | m_poDataBlock->WriteInt32(numEntries); |
1973 | |
|
1974 | 0 | m_numEntriesInNode = numEntries; |
1975 | |
|
1976 | 0 | m_poDataBlock->GotoByteInBlock(12); |
1977 | 0 | if (m_poDataBlock->WriteBytes(numEntries * (m_nKeyLength + 4), pBuf) != 0) |
1978 | 0 | { |
1979 | 0 | return -1; // An error msg should have been reported already |
1980 | 0 | } |
1981 | | |
1982 | 0 | m_nCurIndexEntry = nCurIndexEntry; |
1983 | 0 | m_poCurChildNode = poCurChild; |
1984 | 0 | if (m_poCurChildNode) |
1985 | 0 | m_poCurChildNode->m_poParentNodeRef = this; |
1986 | |
|
1987 | 0 | return 0; |
1988 | 0 | } |
1989 | | |
1990 | | /********************************************************************** |
1991 | | * TABINDNode::GetNodeKey() |
1992 | | * |
1993 | | * Returns a reference to the key for the first entry in the node, which |
1994 | | * is also the key for this node at the level above it in the tree. |
1995 | | * |
1996 | | * Returns NULL if node is empty. |
1997 | | **********************************************************************/ |
1998 | | GByte *TABINDNode::GetNodeKey() |
1999 | 0 | { |
2000 | 0 | if (m_poDataBlock == nullptr || m_numEntriesInNode == 0) |
2001 | 0 | return nullptr; |
2002 | | |
2003 | 0 | m_poDataBlock->GotoByteInBlock(12); |
2004 | |
|
2005 | 0 | return m_poDataBlock->GetCurDataPtr(); |
2006 | 0 | } |
2007 | | |
2008 | | /********************************************************************** |
2009 | | * TABINDNode::SetPrevNodePtr() |
2010 | | * |
2011 | | * Update the m_nPrevNodePtr member. |
2012 | | * |
2013 | | * Returns 0 on success, -1 on error. |
2014 | | **********************************************************************/ |
2015 | | int TABINDNode::SetPrevNodePtr(GInt32 nPrevNodePtr) |
2016 | 0 | { |
2017 | 0 | if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) || |
2018 | 0 | m_poDataBlock == nullptr) |
2019 | 0 | return -1; |
2020 | | |
2021 | 0 | if (m_nPrevNodePtr == nPrevNodePtr) |
2022 | 0 | return 0; // Nothing to do. |
2023 | | |
2024 | 0 | m_poDataBlock->GotoByteInBlock(4); |
2025 | 0 | return m_poDataBlock->WriteInt32(nPrevNodePtr); |
2026 | 0 | } |
2027 | | |
2028 | | /********************************************************************** |
2029 | | * TABINDNode::SetNextNodePtr() |
2030 | | * |
2031 | | * Update the m_nNextNodePtr member. |
2032 | | * |
2033 | | * Returns 0 on success, -1 on error. |
2034 | | **********************************************************************/ |
2035 | | int TABINDNode::SetNextNodePtr(GInt32 nNextNodePtr) |
2036 | 0 | { |
2037 | 0 | if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) || |
2038 | 0 | m_poDataBlock == nullptr) |
2039 | 0 | return -1; |
2040 | | |
2041 | 0 | if (m_nNextNodePtr == nNextNodePtr) |
2042 | 0 | return 0; // Nothing to do. |
2043 | | |
2044 | 0 | m_poDataBlock->GotoByteInBlock(8); |
2045 | 0 | return m_poDataBlock->WriteInt32(nNextNodePtr); |
2046 | 0 | } |
2047 | | |
2048 | | /********************************************************************** |
2049 | | * TABINDNode::Dump() |
2050 | | * |
2051 | | * Dump block contents... available only in DEBUG mode. |
2052 | | **********************************************************************/ |
2053 | | #ifdef DEBUG |
2054 | | |
2055 | | void TABINDNode::Dump(FILE *fpOut /*=NULL*/) |
2056 | | { |
2057 | | if (fpOut == nullptr) |
2058 | | fpOut = stdout; |
2059 | | |
2060 | | fprintf(fpOut, "----- TABINDNode::Dump() -----\n"); |
2061 | | |
2062 | | if (m_fp == nullptr) |
2063 | | { |
2064 | | fprintf(fpOut, "Node is not initialized.\n"); |
2065 | | } |
2066 | | else |
2067 | | { |
2068 | | fprintf(fpOut, " m_numEntriesInNode = %d\n", m_numEntriesInNode); |
2069 | | fprintf(fpOut, " m_nCurDataBlockPtr = %d\n", m_nCurDataBlockPtr); |
2070 | | fprintf(fpOut, " m_nPrevNodePtr = %d\n", m_nPrevNodePtr); |
2071 | | fprintf(fpOut, " m_nNextNodePtr = %d\n", m_nNextNodePtr); |
2072 | | fprintf(fpOut, " m_nSubTreeDepth = %d\n", m_nSubTreeDepth); |
2073 | | fprintf(fpOut, " m_nKeyLength = %d\n", m_nKeyLength); |
2074 | | fprintf(fpOut, " m_eFieldtype = %s\n", |
2075 | | TABFIELDTYPE_2_STRING(m_eFieldType)); |
2076 | | if (m_nSubTreeDepth > 0) |
2077 | | { |
2078 | | GByte aKeyValBuf[255]; |
2079 | | GInt32 nRecordPtr, nValue; |
2080 | | TABINDNode oChildNode; |
2081 | | |
2082 | | if (m_nKeyLength > 254) |
2083 | | { |
2084 | | CPLError(CE_Failure, CPLE_NotSupported, |
2085 | | "Dump() cannot handle keys longer than 254 chars."); |
2086 | | return; |
2087 | | } |
2088 | | |
2089 | | fprintf(fpOut, "\n"); |
2090 | | for (int i = 0; i < m_numEntriesInNode; i++) |
2091 | | { |
2092 | | if (m_nSubTreeDepth > 1) |
2093 | | { |
2094 | | fprintf(fpOut, " >>>> Child %d of %d <<<<<\n", i, |
2095 | | m_numEntriesInNode); |
2096 | | } |
2097 | | else |
2098 | | { |
2099 | | fprintf(fpOut, " >>>> Record (leaf) %d of %d <<<<<\n", i, |
2100 | | m_numEntriesInNode); |
2101 | | } |
2102 | | |
2103 | | if (m_eFieldType == TABFChar) |
2104 | | { |
2105 | | nRecordPtr = ReadIndexEntry(i, aKeyValBuf); |
2106 | | fprintf(fpOut, " nRecordPtr = %d\n", nRecordPtr); |
2107 | | fprintf(fpOut, " Char Val= \"%s\"\n", |
2108 | | reinterpret_cast<char *>(aKeyValBuf)); |
2109 | | } |
2110 | | else if (m_nKeyLength != 4) |
2111 | | { |
2112 | | nRecordPtr = ReadIndexEntry(i, aKeyValBuf); |
2113 | | GInt32 nInt32 = 0; |
2114 | | memcpy(&nInt32, aKeyValBuf, 4); |
2115 | | GInt16 nInt16 = 0; |
2116 | | memcpy(&nInt16, aKeyValBuf + 2, 2); |
2117 | | GUInt32 nUInt32 = 0; |
2118 | | memcpy(&nUInt32, aKeyValBuf, 4); |
2119 | | fprintf(fpOut, " nRecordPtr = %d\n", nRecordPtr); |
2120 | | fprintf(fpOut, " Int Value = %d\n", nInt32); |
2121 | | fprintf(fpOut, " Int16 Val= %d\n", nInt16); |
2122 | | fprintf(fpOut, " Hex Val= 0x%8.8x\n", nUInt32); |
2123 | | } |
2124 | | else |
2125 | | { |
2126 | | nRecordPtr = |
2127 | | ReadIndexEntry(i, reinterpret_cast<GByte *>(&nValue)); |
2128 | | fprintf(fpOut, " nRecordPtr = %d\n", nRecordPtr); |
2129 | | fprintf(fpOut, " Int Value = %d\n", nValue); |
2130 | | fprintf(fpOut, " Hex Value = 0x%8.8x\n", nValue); |
2131 | | } |
2132 | | |
2133 | | if (m_nSubTreeDepth > 1) |
2134 | | { |
2135 | | CPL_IGNORE_RET_VAL( |
2136 | | oChildNode.InitNode(m_fp, nRecordPtr, m_nKeyLength, |
2137 | | m_nSubTreeDepth - 1, FALSE)); |
2138 | | oChildNode.SetFieldType(m_eFieldType); |
2139 | | oChildNode.Dump(fpOut); |
2140 | | } |
2141 | | } |
2142 | | } |
2143 | | } |
2144 | | |
2145 | | fflush(fpOut); |
2146 | | } |
2147 | | |
2148 | | #endif // DEBUG |