1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // CegoFileHandler.cc
4 // ------------------
5 // Cego database file handler implementation
6 //
7 // Design and Implementation by Bjoern Lemke
8 //
9 // (C)opyright 2000-2019 Bjoern Lemke
10 //
11 // IMPLEMENTATION MODULE
12 //
13 // Class: CegoFileHandler
14 //
15 // Description: The filehandler class provides access to all datafiles. Datafiles consists
16 //              of a defined number of pages managed by the this class.
17 //              At the header of a datafile, a bitmap is stored for used page information
18 //
19 // Status: CLEAN
20 //
21 ///////////////////////////////////////////////////////////////////////////////
22 
23 #include <lfcbase/Exception.h>
24 
25 #include "CegoFileHandler.h"
26 #include "CegoBufferPage.h"
27 
28 #define BIT_PER_BYTE 8
29 #define FILEHEADSIZE sizeof(int) + sizeof(FileType) + sizeof(int) + sizeof(PageIdType)
30 
31 extern bool __fsyncOn;
32 
CegoFileHandler(const Chain & logFile,const Chain & progName)33 CegoFileHandler::CegoFileHandler(const Chain& logFile, const Chain& progName) : CegoModule(logFile, progName)
34 {
35     for (int i = 0; i<FILMNG_MAXDATAFILE; i++)
36     {
37 	_isReg[i]=false;
38 	_tabSetId[i]=0;
39 	_fhList[i]=0;
40 	_buMask[i]=0;
41 	_commitMask[i]=0;
42 	_pageOffset[i]=0;
43     }
44     _appendFid = 0;
45     _isReadOnly = false;
46     _modId = getModId("CegoFileHandler");
47 }
48 
~CegoFileHandler()49 CegoFileHandler::~CegoFileHandler()
50 {
51     for (int i = 0; i<FILMNG_MAXDATAFILE; i++)
52     {
53 	if ( _fhList[i] != 0 )
54 	{
55 	    _fhList[i]->close();
56 	    delete _fhList[i];
57 	}
58 	if ( _buMask[i] != 0 )
59 	{
60 	    delete _buMask[i];
61 	}
62 	if ( _commitMask[i] != 0 )
63 	{
64 	    delete _commitMask[i];
65 	}
66     }
67 }
68 
setReadOnly(bool isReadOnly)69 void CegoFileHandler::setReadOnly(bool isReadOnly)
70 {
71     _isReadOnly = isReadOnly;
72 }
73 
setBackup(int fileId,bool m)74 void CegoFileHandler::setBackup(int fileId, bool m)
75 {
76     if ( m == true )
77     {
78 	_buMask[fileId] = new unsigned[ (  getNumPages(fileId) / (BIT_PER_BYTE * sizeof(unsigned))) + 1];
79 
80 	int i;
81 
82 	for (i = 0; i < ( getNumPages(fileId) / (BIT_PER_BYTE * sizeof(unsigned))) + 1; i++)
83 	{
84 	    _buMask[fileId][i] = 0;
85 	}
86 	_fbmMask[fileId] = false;
87     }
88     else
89     {
90 	if ( _buMask[fileId] != 0 )
91 	{
92 	    delete _buMask[fileId];
93 	    _buMask[fileId] = 0;
94 	}
95     }
96 }
97 
needPageBackup(PageIdType pageId)98 bool CegoFileHandler::needPageBackup(PageIdType pageId)
99 {
100     int fileId = getFileIdForPageId(pageId);
101 
102     // check if backup is activated for file
103     if ( _buMask[fileId] )
104     {
105 	// is page already marked, no need to backup page
106 	if ( isPageMarked(pageId, fileId) )
107 	    return false;
108 	return true;
109     }
110 
111     return false;
112 }
113 
markPage(PageIdType pageId)114 void CegoFileHandler::markPage(PageIdType pageId)
115 {
116     int fileId = getFileIdForPageId(pageId);
117 
118     if ( _buMask[fileId] == 0 )
119     {
120 	Chain msg = Chain("No backup mode for fileId ") + Chain(fileId);
121 	throw Exception(EXLOC, msg);
122     }
123 
124     unsigned bmid = ( pageId - _pageOffset[fileId] ) / ( BIT_PER_BYTE * sizeof(unsigned));
125     unsigned bmoffset = ( pageId - _pageOffset[fileId] ) % ( BIT_PER_BYTE * sizeof(unsigned));
126 
127     unsigned bm = _buMask[fileId][bmid];
128 
129     unsigned f = ~(~(unsigned)0 << 1);
130     f = f << bmoffset;
131 
132     _buMask[fileId][bmid] = bm | f;
133 
134 }
135 
136 
isPageMarked(PageIdType pageId,int fileId)137 bool CegoFileHandler::isPageMarked(PageIdType pageId, int fileId)
138 {
139 
140     if ( _buMask[fileId] == 0 )
141     {
142 	Chain msg = Chain("No backup mode for fileId ") + Chain(fileId);
143 	throw Exception(EXLOC, msg);
144     }
145 
146     unsigned bmid = ( pageId - _pageOffset[fileId] ) / ( BIT_PER_BYTE * sizeof(unsigned));
147     unsigned bmoffset = ( pageId - _pageOffset[fileId] ) % ( BIT_PER_BYTE * sizeof(unsigned));
148 
149     unsigned bm = _buMask[fileId][bmid];
150 
151     unsigned checkbit = bm >> bmoffset & ~(~(unsigned)0 << 1);
152 
153     if (  checkbit )
154     {
155 	return true;
156     }
157     return false;
158 }
159 
160 
getPageOffset(int fileId)161 PageIdType CegoFileHandler::getPageOffset(int fileId)
162 {
163     return _pageOffset[fileId];
164 }
165 
getFBMSize(int fileId)166 int CegoFileHandler::getFBMSize(int fileId)
167 {
168     return (_numPages[fileId] / (BIT_PER_BYTE * sizeof(unsigned))) + 1;
169 }
170 
readFBM(int fileId,unsigned * fbm,CegoLockHandler * pLockHandle)171 void CegoFileHandler::readFBM(int fileId, unsigned *fbm, CegoLockHandler *pLockHandle)
172 {
173     File *pF;
174 
175     try
176     {
177 	pF = getHandle(fileId);
178     }
179     catch ( Exception e )
180     {
181 	throw e;
182     }
183 
184     pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
185 
186     try
187     {
188 
189 	pF->seek(FILEHEADSIZE);
190 
191 	// int tabSetId;
192 	// pF->readByte((char*)&tabSetId, sizeof(int));
193 
194 	int numBitmap = (_numPages[fileId] / (BIT_PER_BYTE * sizeof(unsigned))) + 1;
195 	int i;
196 	for (i = 0; i < numBitmap; i++)
197 	{
198 	    pF->readByte((char*)&fbm[i], sizeof(unsigned));
199 	}
200 
201 	pLockHandle->unlockDataFile(fileId);
202 
203     }
204     catch ( Exception e )
205     {
206 	pLockHandle->unlockDataFile(fileId);
207 	throw e;
208     }
209 }
210 
writeFBM(int fileId,unsigned * fbm,CegoLockHandler * pLockHandle)211 void CegoFileHandler::writeFBM(int fileId, unsigned *fbm, CegoLockHandler *pLockHandle)
212 {
213     if ( _isReadOnly == true )
214 	return;
215 
216     File *pF;
217 
218     try
219     {
220 	pF = getHandle(fileId);
221     }
222     catch ( Exception e )
223     {
224 	throw e;
225     }
226 
227     pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
228 
229     try
230     {
231 	pF->seek(FILEHEADSIZE);
232 
233 	int numBitmap = (_numPages[fileId] / (BIT_PER_BYTE * sizeof(unsigned))) + 1;
234 	int i;
235 
236 	for (i = 0; i < numBitmap; i++)
237 	{
238 	    pF->writeByte((char*)&fbm[i], sizeof(unsigned));
239 	}
240 
241 	if ( __fsyncOn )
242 	    pF->flush();
243 
244 	pLockHandle->unlockDataFile(fileId);
245 
246     }
247     catch ( Exception e )
248     {
249 	pLockHandle->unlockDataFile(fileId);
250 	throw e;
251     }
252 }
253 
254 /* the cleanDataFile method traces the corresponding datafile to check for
255    allocated but unused pages. This may happen, if the allocatePage method
256    was called, but for any reason ( e.g. a system crash ) the page could never be written
257    back to file. In this case, the fixStat attribute is still zero. so the page
258    can be released for other use
259 
260    Since introduction of the more complete cleanup procedure based on database object analysis ( CegoTableManager::cleanTableSet )
261    this method is obsolete
262 */
263 
264 
265 /*
266 void CegoFileHandler::cleanDataFile(int fileId, CegoLockHandler *pLockHandle)
267 {
268     File* pF;
269 
270     try
271     {
272 	pF = getHandle(fileId);
273     }
274     catch ( Exception e )
275     {
276 	throw e;
277     }
278 
279     int offset = FILEHEADSIZE
280 	+  ((_numPages[fileId] / ( BIT_PER_BYTE * sizeof(unsigned)))+1) * sizeof(unsigned) ;
281 
282     int pageIndex;
283 
284     for (pageIndex = 0; pageIndex < _numPages[fileId]; pageIndex++)
285     {
286 	pF->seek(offset + pageIndex * ( _pageSize + sizeof(unsigned)));
287 	unsigned fixStat;
288 	pF->readByte((char*)&fixStat, sizeof(unsigned));
289 
290 	PageIdType pageId = _pageOffset[fileId] + pageIndex;
291 
292 	if ( fixStat == 0 && isClaimed(pageId, pLockHandle) == true )
293 	{
294 	    log(_modId, Logger::NOTICE, Chain("Releasing page ") + Chain(pageId));
295 	    unsigned *fbm;
296 	    int fbmSize;
297 
298 	    releasePage(pageId, pLockHandle, fbm, fbmSize);
299 	}
300     }
301 }
302 */
303 
304 /* a new datafile is created using the initDataFile method. With the given tabSetId, path, fileId, size and type
305    the file is created and initialized. Note, that after creation the datafile is still NOT registered for further usage.
306    This must be done using the regDataFile method */
307 
initDataFile(int tabSetId,const Chain & path,int fileId,int numPages,PageIdType pageOffset,FileType ft)308 void CegoFileHandler::initDataFile(int tabSetId, const Chain& path, int fileId, int numPages, PageIdType pageOffset, FileType ft)
309 {
310     if ( _isReadOnly == true )
311 	return;
312 
313     File* pF = new File(path);
314 
315     if ( pF->exists() )
316     {
317 	Chain msg = Chain("Datafile ") + path + Chain(" already exists");
318 	delete pF;
319 	throw Exception( EXLOC, msg);
320     }
321 
322     try
323     {
324 	pF->open(File::WRITE);
325 
326 	pF->writeByte((char*)&tabSetId, sizeof(int));
327 	pF->writeByte((char*)&ft, sizeof(FileType));
328 	// cout << "Init datafile " << path << " with numPages " << numPages << endl;
329 	pF->writeByte((char*)&numPages, sizeof(int));
330 
331 	pF->writeByte((char*)&pageOffset, sizeof(PageIdType));
332 
333 	int i;
334 
335 	for (i = 0; i < (numPages / (BIT_PER_BYTE * sizeof(unsigned))) + 1; i++)
336 	{
337 	    unsigned bm = 0;
338 	    pF->writeByte((char*)&bm, sizeof(unsigned));
339 	}
340 
341 	char* initBuf = new char[_pageSize];
342 	CegoBufferPage ip(initBuf, _pageSize);
343 	ip.initPage(CegoBufferPage::TABLE);
344 
345 	for (i = 0; i < numPages; i++)
346 	{
347 	    unsigned fixStat = 0;
348 	    pF->writeByte((char*)&fixStat, sizeof(unsigned));
349 	    pF->writeByte(initBuf, _pageSize);
350 	}
351 	delete[] initBuf;
352 
353 	pF->close();
354 	delete pF;
355     }
356     catch ( Exception e )
357     {
358 	delete pF;
359 	throw e;
360     }
361 }
362 
resetDataFile(int fileId)363 void CegoFileHandler::resetDataFile(int fileId)
364 {
365 
366     if ( _isReadOnly == true )
367 	return;
368 
369     File* pF;
370 
371     try
372     {
373 	pF = getHandle(fileId);
374     }
375     catch ( Exception e )
376     {
377 	throw e;
378     }
379 
380     pF->seek(FILEHEADSIZE);
381 
382     int i;
383 
384     for (i = 0; i < ( _numPages[fileId] / (BIT_PER_BYTE * sizeof(unsigned))) + 1; i++)
385     {
386 	unsigned bm = 0;
387 	pF->writeByte((char*)&bm, sizeof(unsigned));
388     }
389 
390     if ( __fsyncOn )
391 	pF->flush();
392 
393 }
394 
395 
396 
397 /* the regDataFile method is used for datafile registration. The given file is checked
398    for consistency and is registered */
399 
regDataFile(int tabSetId,const Chain & path,int fileId,CegoLockHandler * pLockHandle)400 void CegoFileHandler::regDataFile(int tabSetId, const Chain& path, int fileId, CegoLockHandler *pLockHandle)
401 {
402     pLockHandle->lockDataFile(fileId, CegoLockHandler::READ);
403 
404     if ( _isReg[fileId] )
405     {
406 	pLockHandle->unlockDataFile(fileId);
407 
408 	if ( _path[fileId] == path )
409 	{
410 	    log(_modId, Logger::NOTICE, Chain("Data file ") + path + Chain(" already exists, skipping"));
411 	    return;
412 	}
413 	else
414 	{
415 	    Chain msg = Chain("Datafile Id ") + Chain(fileId) + Chain(" already occupied");
416 	    throw Exception(EXLOC, msg);
417 	}
418     }
419 
420     int checkTabSetId;
421     int numPage;
422     PageIdType pageOffset;
423 
424     FileType ft;
425 
426     try
427     {
428 	File checkFile(path);
429 	checkFile.open(File::READ);
430 
431 	checkFile.readByte((char*)&checkTabSetId, sizeof(int));
432 	checkFile.readByte((char*)&ft, sizeof(FileType));
433 	checkFile.readByte((char*)&numPage, sizeof(int));
434 	checkFile.readByte((char*)&pageOffset, sizeof(PageIdType));
435 
436 	checkFile.close();
437     }
438     catch ( Exception e )
439     {
440 	Chain ferr;
441 	e.pop(ferr);
442 	pLockHandle->unlockDataFile(fileId);
443 	Chain msg = Chain("Cannot access datafile <") + path + Chain("> :") + ferr;
444 	throw Exception(EXLOC, msg);
445     }
446 
447     if ( tabSetId != checkTabSetId )
448     {
449 	pLockHandle->unlockDataFile(fileId);
450 	throw Exception(EXLOC, "Table Set Id does not match");
451     }
452 
453     _pageOffset[fileId] = pageOffset;
454     _isReg[fileId] = true;
455     _path[fileId] = path;
456     _tabSetId[fileId] = tabSetId;
457     _fileType[fileId] = ft;
458     _numPages[fileId] = numPage;
459 
460     pLockHandle->unlockDataFile(fileId);
461 }
462 
463 /* all registered files of the corresponding table set are closed and unregistered
464    with the releaseFiles method */
465 
releaseFiles(int tabSetId)466 void CegoFileHandler::releaseFiles(int tabSetId)
467 {
468     for (int i = 0; i<FILMNG_MAXDATAFILE; i++)
469     {
470 	if ( _tabSetId[i] == tabSetId )
471 	{
472 	    if ( _fhList[i] != 0 )
473 	    {
474 		_fhList[i]->close();
475 		_fhList[i]=0;
476 	    }
477 	    _tabSetId[i]=0;
478 	    _isReg[i]=false;
479 	}
480     }
481 }
482 
483 /* a single database page is written to file using the writePage method. With the corresponding fileId, pageId
484    and the pointer to the data, the file handler can perform the write operation. The fixStat parameter indicates
485    the number of bufferFix operations on the page and is normally used by the BufferPool class for the page
486    relocation strategy */
487 
writePage(PageIdType pageId,unsigned fixStat,char * pageData,CegoLockHandler * pLockHandle)488 void CegoFileHandler::writePage(PageIdType pageId, unsigned fixStat, char* pageData, CegoLockHandler *pLockHandle)
489 {
490     if ( _isReadOnly == true )
491 	throw Exception(EXLOC, Chain("Cannot write page in read only mode"));
492 
493     int fileId = getFileIdForPageId(pageId);
494 
495     File* pF;
496 
497     try
498     {
499 	pF = getHandle(fileId);
500     }
501     catch ( Exception e )
502     {
503 	throw e;
504     }
505 
506     pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
507 
508     try
509     {
510 	unsigned long offset = FILEHEADSIZE
511 	    +  ((_numPages[fileId] / ( BIT_PER_BYTE * sizeof(unsigned) )) + 1) * sizeof(unsigned);
512 
513 	pF->seek(offset + ( pageId - _pageOffset[fileId] ) * ( _pageSize + sizeof(unsigned)));
514 	pF->writeByte((char*)&fixStat, sizeof(unsigned));
515 	pF->writeByte(pageData, _pageSize);
516 
517 	if ( _buMask[fileId] )
518 	    markPage(pageId);
519 
520 	if ( __fsyncOn )
521 	    pF->flush();
522 
523 	pLockHandle->unlockDataFile(fileId);
524     }
525     catch ( Exception e )
526     {
527 	pLockHandle->unlockDataFile(fileId);
528 	throw e;
529     }
530 }
531 
532 /* pages from datafiles are read by using the readPage method. The method seeks to the appropriate offset
533    in the corresponding datafile and reads the page data into the given data pointer */
534 
readPage(PageIdType pageId,int & tabSetId,unsigned & fixStat,char * pageData,CegoLockHandler * pLockHandle)535 void CegoFileHandler::readPage(PageIdType pageId, int& tabSetId, unsigned& fixStat, char* pageData, CegoLockHandler *pLockHandle)
536 {
537     int fileId = getFileIdForPageId(pageId);
538 
539     File *pF;
540 
541     try
542     {
543 	pF = getHandle(fileId);
544     }
545     catch ( Exception e )
546     {
547 	throw e;
548     }
549 
550     pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
551 
552     try
553     {
554 	pF->seek(0);
555 	pF->readByte((char*)&tabSetId, sizeof(int));
556 
557 	unsigned long offset = FILEHEADSIZE
558 	    +  ((_numPages[fileId] / ( BIT_PER_BYTE * sizeof(unsigned) )) + 1) * sizeof(unsigned) ;
559 
560 	pF->seek(offset + ( pageId - _pageOffset[fileId] ) * ( _pageSize + sizeof(unsigned)) );
561 
562 	pF->readByte((char*)&fixStat, sizeof(unsigned));
563 
564 	unsigned long rb = pF->readByte(pageData, _pageSize);
565 	while ( rb < _pageSize )
566 	{
567 	    unsigned long rrb = pF->readByte((char*)((long long)pageData + rb), _pageSize - rb);
568 
569 	    if ( rrb == 0 )
570 	      throw Exception(EXLOC, Chain("Incomplete file read"));
571 
572 	    rb+=rrb;
573 	}
574 
575 	pLockHandle->unlockDataFile(fileId);
576     }
577     catch ( Exception e )
578     {
579 	pLockHandle->unlockDataFile(fileId);
580 	throw e;
581     }
582 }
583 
584 /* the claimPage method allocates a given page in the given file.
585    Normally this method is used for "well known entry pages", e.g. the system catalog page
586    which is used as an entry point. */
587 
claimPage(PageIdType pageId,CegoLockHandler * pLockHandle)588 void CegoFileHandler::claimPage(PageIdType pageId, CegoLockHandler *pLockHandle)
589 {
590     if ( _isReadOnly == true )
591 	throw Exception(EXLOC, Chain("Cannot claim page in read only mode"));
592 
593     int fileId = getFileIdForPageId(pageId);
594 
595     File *pF;
596 
597     try
598     {
599 	pF = getHandle(fileId);
600     }
601     catch ( Exception e )
602     {
603 	throw e;
604     }
605 
606     pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
607 
608     try
609     {
610 	unsigned bmid = ( pageId - _pageOffset[fileId] ) / ( BIT_PER_BYTE * sizeof(unsigned));
611 	unsigned bmoffset = ( pageId - _pageOffset[fileId] ) % ( BIT_PER_BYTE * sizeof(unsigned));
612 
613 	pF->seek(FILEHEADSIZE + bmid * sizeof(unsigned));
614 
615 	unsigned bm;
616 	pF->readByte((char*)&bm, sizeof(unsigned));
617 
618 	unsigned f = ~(~(unsigned)0 << 1);
619 	f = f << bmoffset;
620 
621 	bm = bm | f;
622 
623 	pF->seek(FILEHEADSIZE + bmid * sizeof(unsigned));
624 	pF->writeByte((char*)&bm, sizeof(unsigned));
625 
626 	if ( _buMask[fileId] )
627 	    _fbmMask[fileId] = true;
628 
629 	if ( __fsyncOn )
630 	    pF->flush();
631 
632 	pLockHandle->unlockDataFile(fileId);
633     }
634     catch ( Exception e )
635     {
636 	pLockHandle->unlockDataFile(fileId);
637 	throw e;
638     }
639 
640     // write page data to datafile to avoid, that cleanup procedure frees the page
641 
642     char* initBuf = new char[_pageSize];
643     CegoBufferPage ip(initBuf, _pageSize);
644     ip.initPage(CegoBufferPage::TABLE);
645 
646     writePage(pageId, 1, initBuf, pLockHandle);
647 
648 }
649 
650 /* any new required page can be requested using the allocatePage method.
651    the required filetype must be specified.
652    the method returns the assigned file and page information */
653 
allocatePage(int tabSetId,FileType ft,PageIdType & pageId,CegoLockHandler * pLockHandle,unsigned * & fbm,int & fbmSize,bool doAppend)654 void CegoFileHandler::allocatePage(int tabSetId, FileType ft, PageIdType& pageId, CegoLockHandler *pLockHandle, unsigned* &fbm, int& fbmSize, bool doAppend)
655 {
656     if ( _isReadOnly == true )
657 	throw Exception(EXLOC, Chain("Cannot allocate page in read only mode"));
658 
659     int fid = 0;
660     if ( doAppend )
661 	fid = _appendFid;
662 
663     while ( fid < FILMNG_MAXDATAFILE )
664     {
665 	if ( _isReg[fid] && _tabSetId[fid] == tabSetId && _fileType[fid] == ft )
666 	{
667 	    File* pF;
668 	    pF = getHandle(fid);
669 
670 	    pLockHandle->lockDataFile(fid, CegoLockHandler::WRITE);
671 
672 	    try
673 	    {
674 		unsigned ws = sizeof(unsigned)*BIT_PER_BYTE;
675 
676 		PageIdType k;
677 
678 		if ( doAppend == false || _appendPos[fid] == 0)
679 		{
680 		    pF->seek(FILEHEADSIZE);
681 		    k = 0;
682 		    // we reset the append postion to zero, so next allocation with append = true will search from the beginning
683 		    _appendPos[fid] = 0;
684 		}
685 		else
686 		{
687 		    // cout << "Seek to " << 2*sizeof(int) + sizeof(FileType) + (_appendPos[fid] / ws ) * sizeof(unsigned) << endl;
688 		    pF->seek(FILEHEADSIZE + ( _appendPos[fid] / ws) * sizeof(unsigned));
689 		    k = _appendPos[fid];
690 		}
691 
692 		// cout << "k=" << k << " numPages = " << _numPages[fid] << endl;
693 
694 		bool isEop = k >= _numPages[fid];
695 
696 		while (!isEop)
697 		{
698 		    unsigned bm;
699 
700 		    pF->readByte((char*)&bm, sizeof(unsigned));
701 
702 		    // printBitMap(bm);
703 
704 		    if ( (bm & ~0) != ~0)
705 		    {
706 			// we start next append search with this fid
707 			_appendFid = fid;
708 
709 			// free page available
710 			int l = 0;
711 			while (l < ws && ! isEop)
712 			{
713 			    if ( k+l < _numPages[fid])
714 			    {
715 				unsigned checkbit = bm >> l & ~(~(unsigned)0 << 1);
716 				if (checkbit == 0)
717 				{
718 				    pageId = k + l + _pageOffset[fid];
719 
720 				    /*
721 				    cout << "Allocated page :" << endl;
722 				    cout << "Fid = " <<  fid << endl;
723 				    cout << "PageOffset=" << _pageOffset[fid] << endl;
724 				    cout << "PageId = " << pageId << endl;
725 				    */
726 
727 				    // cout << "FH: " << fid << "," <<  k << "," << l << " using pageId " << pageId << endl;
728 
729 				    unsigned f = ~(~(unsigned)0 << 1);
730 				    f = f << l;
731 				    // printBitMap(bm);
732 				    // cout << "1 bm=" << bm << endl;
733 				    bm = bm | f;
734 				    // printBitMap(bm);
735 
736 				    if ( _buMask[fid] )
737 				    {
738 					if ( _fbmMask[fid] == false )
739 					{
740 					    fbmSize = getFBMSize(fid);
741 					    fbm = new unsigned[fbmSize];
742 					    readFBM(fid, fbm, pLockHandle);
743 					}
744 					else
745 					{
746 					    fbmSize = 0;
747 					}
748 
749 					_fbmMask[fid] = true;
750 				    }
751 
752 				    // cout << "Writing bitmask to file .." << endl;
753 				    pF->seek(FILEHEADSIZE + k / BIT_PER_BYTE);
754 				    pF->writeByte((char*)&bm, sizeof(unsigned));
755 				    if ( __fsyncOn )
756 					pF->flush();
757 
758 				    pLockHandle->unlockDataFile(fid);
759 
760 				    if ( _appendPos[fid] < k )
761 				    {
762 					_appendPos[fid] = k;
763 				    }
764 				    return;
765 				}
766 				l++;
767 			    }
768 			    else
769 			    {
770 				isEop = true;
771 			    }
772 			}
773 		    }
774 		    k+=ws;
775 		}
776 		pLockHandle->unlockDataFile(fid);
777 	    }
778 	    catch ( Exception e )
779 	    {
780 		pLockHandle->unlockDataFile(fid);
781 		throw e;
782 	    }
783 	}
784 	fid++;
785     }
786 
787     if ( doAppend )
788     {
789 	_appendFid=0;
790 	// if no more pages available with append mode, we make another try without append
791 	allocatePage(tabSetId, ft, pageId, pLockHandle, fbm, fbmSize, false);
792     }
793     else
794     {
795 	Chain fileType;
796 	if ( ft == DATAFILE )
797 	    fileType = Chain("Data");
798 	else if ( ft == SYSTEMFILE )
799 	    fileType = Chain("System");
800 	else if ( ft == TEMP )
801 	    fileType = Chain("Temp");
802 	throw Exception(EXLOC, fileType + Chain(" file pages exceeded "));
803     }
804 }
805 
806 /* if a page is no longer used, it can be released using the releasePage method.
807    Normally this is used, in case of delete operations or in case of temporarily used pages */
808 
releasePage(PageIdType pageId,CegoLockHandler * pLockHandle,unsigned * & fbm,int & fbmSize)809 void CegoFileHandler::releasePage(PageIdType pageId, CegoLockHandler *pLockHandle, unsigned* &fbm, int& fbmSize)
810 {
811     if ( _isReadOnly == true )
812 	throw Exception(EXLOC, Chain("Cannot releases page in read only mode"));
813 
814     int fileId = getFileIdForPageId(pageId);
815 
816     File* pF;
817     pF = getHandle(fileId);
818 
819     pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
820 
821     try
822     {
823 	if ( _buMask[fileId] )
824 	{
825 	    if ( _fbmMask[fileId] == false )
826 	    {
827 		fbmSize = getFBMSize(fileId);
828 		fbm = new unsigned[fbmSize];
829 		readFBM(fileId, fbm, pLockHandle);
830 	    }
831 	}
832 	else
833 	{
834 	    fbmSize = 0;
835 	}
836 
837 	unsigned bmid = ( pageId - _pageOffset[fileId] ) / ( sizeof(unsigned) * BIT_PER_BYTE );
838 	unsigned bmoffset = ( pageId - _pageOffset[fileId] ) % ( sizeof(unsigned) * BIT_PER_BYTE) ;
839 
840 	// cout << "FH release : " << bmid << "," << bmoffset << " using pageId " << pageId << endl;
841 
842 	pF->seek(FILEHEADSIZE + ( bmid * sizeof(unsigned)));
843 
844 	unsigned bm;
845 	pF->readByte((char*)&bm, sizeof(unsigned));
846 
847 	unsigned f = ~(~(unsigned)0 << 1);
848 	f = f << bmoffset;
849 
850 	// printBitMap(bm);
851 	// printBitMap(f);
852 
853 	bm = bm & ~f;
854 
855 	// cout << "Bitmap after " << endl;
856 	// printBitMap(bm);
857 
858 	// cout << "Write to pos " << sizeof(int) + sizeof(FileType) + sizeof(int) + ( bmid * sizeof(int)) << endl;
859 
860 	pF->seek(FILEHEADSIZE + ( bmid * sizeof(unsigned)));
861 	pF->writeByte((char*)&bm, sizeof(unsigned));
862 	if ( __fsyncOn )
863 	    pF->flush();
864 
865 	if ( _buMask[fileId] )
866 	    _fbmMask[fileId] = true;
867 
868 
869 	pLockHandle->unlockDataFile(fileId);
870     }
871     catch ( Exception e )
872     {
873 	pLockHandle->unlockDataFile(fileId);
874 	throw e;
875     }
876 }
877 
commitPageEntry(PageIdType pageId)878 void CegoFileHandler::commitPageEntry(PageIdType pageId)
879 {
880     int fileId = getFileIdForPageId(pageId);
881 
882     if ( _commitMask[fileId] == 0 )
883     {
884     	_commitMask[fileId] = new unsigned[ (  getNumPages(fileId) / (BIT_PER_BYTE * sizeof(unsigned))) + 1];
885 
886 	int i;
887 
888 	for (i = 0; i < ( getNumPages(fileId) / (BIT_PER_BYTE * sizeof(unsigned))) + 1; i++)
889 	{
890 	    _commitMask[fileId][i] = 0;
891 	}
892     }
893 
894     unsigned bmid = ( pageId - _pageOffset[fileId] ) / ( BIT_PER_BYTE * sizeof(unsigned));
895     unsigned bmoffset = ( pageId - _pageOffset[fileId] ) % ( BIT_PER_BYTE * sizeof(unsigned));
896 
897     unsigned bm = _commitMask[fileId][bmid];
898 
899     unsigned f = ~(~(unsigned)0 << 1);
900     f = f << bmoffset;
901 
902     _commitMask[fileId][bmid] = bm | f;
903 
904 }
905 
cleanPages(CegoLockHandler * pLockHandle)906 int CegoFileHandler::cleanPages(CegoLockHandler *pLockHandle)
907 {
908     if ( _isReadOnly == true )
909 	throw Exception(EXLOC, Chain("Cannot clean pages in read only mode"));
910 
911     int numCleanup = 0;
912 
913     for (int i = 0; i<FILMNG_MAXDATAFILE; i++)
914     {
915 	if ( _isReg[i] && _fileType[i] == DATAFILE && _commitMask[i])
916 	{
917 
918 	    int fbmSize = getFBMSize(i);
919 	    unsigned* fbm = new unsigned[fbmSize];
920 	    readFBM(i, fbm, pLockHandle);
921 
922 	    bool cleanupNeeded = false;
923 
924 	    for ( int j=0; j<fbmSize; j++ )
925 	    {
926 		// cout << "Checking mask " << _commitMask[i][j] << endl;
927 
928 		if ( fbm[j] != _commitMask[i][j] )
929 		{
930 		    // cleanup required
931 
932 
933 		    cleanupNeeded = true;
934 
935 		    numCleanup += mapDiff(fbm[i], _commitMask[i][j] );
936 
937 		    /*
938 		    cout << "Commited :" << endl;
939 		    printBitMap(_commitMask[i][j]);
940 
941 		    cout << "Written :" << endl;
942 		    printBitMap(fbm[j]);
943 		    */
944 		}
945 	    }
946 
947 	    if ( cleanupNeeded )
948 		writeFBM(i, _commitMask[i], pLockHandle);
949 
950 	    delete[] fbm;
951 	}
952     }
953 
954     return numCleanup;
955 }
956 
957 
958 /* returns the number of total pages for the given fileId */
959 
getNumPages(int fileId)960 int CegoFileHandler::getNumPages(int fileId)
961 {
962     try
963     {
964 	// check valid fileId
965 	getHandle(fileId);
966     }
967     catch ( Exception e )
968     {
969 	throw e;
970     }
971 
972     return _numPages[fileId];
973 }
974 
975 /* returns the number of used pages for the given fileId */
976 
getNumUsedPages(int fileId,CegoLockHandler * pLockHandle)977 int CegoFileHandler::getNumUsedPages(int fileId, CegoLockHandler *pLockHandle)
978 {
979     File *pF;
980 
981     try
982     {
983 	pF = getHandle(fileId);
984     }
985     catch ( Exception e )
986     {
987 	throw e;
988     }
989 
990     pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
991 
992     try
993     {
994 	pF->seek(sizeof(int) + sizeof(FileType));
995 
996 	int numPage;
997 	pF->readByte((char*)&numPage, sizeof(int));
998 
999 	PageIdType pageOffset;
1000 	pF->readByte((char*)&pageOffset, sizeof(PageIdType));
1001 
1002 	int p;
1003 
1004 	int usedPages = 0;
1005 
1006 	for ( p = 0; p < (numPage / (BIT_PER_BYTE * sizeof(unsigned))) + 1; p++ )
1007 	{
1008 	    unsigned bm = 0;
1009 	    pF->readByte((char*)&bm, sizeof(unsigned));
1010 
1011 	    // printBitMap(bm);
1012 
1013 	    int i,j;
1014 	    for (i = 0; i< sizeof(unsigned); i++)
1015 	    {
1016 		for (j=0; j < BIT_PER_BYTE; j++)
1017 		{
1018 		    int one = 1;
1019 
1020 		    if ( one & bm )
1021 		    {
1022 			usedPages++;
1023 		    }
1024 		    bm = bm >> 1;
1025 
1026 		}
1027 	    }
1028 	}
1029 
1030 	pLockHandle->unlockDataFile(fileId);
1031 
1032 	return usedPages;
1033     }
1034     catch ( Exception e )
1035     {
1036 	pLockHandle->unlockDataFile(fileId);
1037 	throw e;
1038     }
1039 }
1040 
1041 ////////////////////////////////////////////////
1042 ////////////// private methods /////////////////
1043 ////////////////////////////////////////////////
1044 
1045 /* to proof, if a given pageId  is already in use,
1046    the isClaimed method is used */
1047 
isClaimed(PageIdType pageId,CegoLockHandler * pLockHandle)1048 bool CegoFileHandler::isClaimed(PageIdType pageId, CegoLockHandler *pLockHandle)
1049 {
1050     int fileId = getFileIdForPageId(pageId);
1051 
1052     File *pF;
1053 
1054     try
1055     {
1056 	pF = getHandle(fileId);
1057     }
1058     catch ( Exception e )
1059     {
1060 	throw e;
1061     }
1062 
1063     if ( pageId >= _pageOffset[fileId] + _numPages[fileId] )
1064     {
1065 	throw Exception(EXLOC, Chain("Invalid page id"));
1066     }
1067 
1068     pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
1069 
1070     try
1071     {
1072 	unsigned bmid = ( pageId - _pageOffset[fileId] ) / ( BIT_PER_BYTE * sizeof(unsigned));
1073 	unsigned bmoffset = ( pageId - _pageOffset[fileId] ) % ( BIT_PER_BYTE * sizeof(unsigned));
1074 
1075 	pF->seek(FILEHEADSIZE + bmid * sizeof(unsigned));
1076 
1077 	unsigned bm;
1078 	pF->readByte((char*)&bm, sizeof(unsigned));
1079 
1080 	pLockHandle->unlockDataFile(fileId);
1081 
1082 	unsigned checkbit = bm >> bmoffset & ~(~(unsigned)0 << 1);
1083 
1084 	if (  checkbit )
1085 	{
1086 	    return true;
1087 	}
1088 	return false;
1089     }
1090     catch ( Exception e )
1091     {
1092 	pLockHandle->unlockDataFile(fileId);
1093 	throw e;
1094     }
1095 }
1096 
getFileIdForPageId(PageIdType pageId)1097 int CegoFileHandler::getFileIdForPageId(PageIdType pageId)
1098 {
1099     int i=0;
1100 
1101     while ( i < FILMNG_MAXDATAFILE )
1102     {
1103 	if ( _isReg[i] == true && pageId >= _pageOffset[i] && pageId < _pageOffset[i] + _numPages[i] )
1104 	{
1105 	    return i;
1106 	}
1107 	i++;
1108     }
1109     throw Exception(EXLOC, Chain("Invalid page id ") + Chain(pageId));
1110 }
1111 
1112 /* getHandle returns the file handle for the given file id. The
1113    file id must be valid and registered. If already open, the method
1114    returns the file handle. Otherwise, the corresponding file is opened
1115    for reading and writing and the handle is returned. */
1116 
getHandle(int fileId)1117 File* CegoFileHandler::getHandle(int fileId)
1118 {
1119     if ( fileId > FILMNG_MAXDATAFILE - 1 )
1120     {
1121 	Chain msg = "File Id " + Chain(fileId)  + " out of valid range";
1122 	throw Exception(EXLOC, msg);
1123     }
1124 
1125     if ( ! _isReg[fileId] )
1126     {
1127 	Chain msg = "File Id " + Chain(fileId)  + " not registered";
1128 	throw Exception(EXLOC, msg);
1129     }
1130 
1131     if ( _fhList[fileId] == 0 )
1132     {
1133 	File* pF = new File(_path[fileId]);
1134 
1135 	try
1136 	{
1137 	    if ( _isReadOnly == true )
1138 		pF->open(File::READ);
1139 	    else
1140 		pF->open(File::READWRITE);
1141 
1142 	    _fhList[fileId] = pF;
1143 	    _appendPos[fileId] = 0;
1144 	}
1145 	catch (Exception e)
1146 	{
1147 	    Chain ferr;
1148 	    e.pop(ferr);
1149 	    Chain msg = "Cannot get file handle " + Chain(fileId) + Chain(": ") + ferr;
1150 	    throw Exception(EXLOC, msg);
1151 	}
1152     }
1153     File *pF = _fhList[fileId];
1154     return pF;
1155 }
1156 
1157 ////////////////////////////////////////////////
1158 //////// debugging/testing methods /////////////
1159 ////////////////////////////////////////////////
1160 
printBitMap(unsigned bm)1161 void CegoFileHandler::printBitMap(unsigned bm)
1162 {
1163     printf("--- BM -----------\n");
1164 
1165     int i,j;
1166     for (i = 0; i< sizeof(unsigned); i++)
1167     {
1168 	for (j=0; j < BIT_PER_BYTE; j++)
1169 	{
1170 	    unsigned one = 1;
1171 
1172 	    if ( one & bm )
1173 		printf("1");
1174 	    else
1175 		printf("0");
1176 	    bm = bm >> 1;
1177 
1178 	}
1179     }
1180     printf("\n");
1181 
1182     printf("--------------\n");
1183 }
1184 
mapDiff(unsigned bm1,unsigned bm2)1185 int CegoFileHandler::mapDiff(unsigned bm1, unsigned bm2)
1186 {
1187     int numDiff = 0;
1188 
1189     int i,j;
1190     for (i = 0; i< sizeof(unsigned); i++)
1191     {
1192 	for (j=0; j < BIT_PER_BYTE; j++)
1193 	{
1194 	    unsigned one = 1;
1195 
1196 	    if ( ( one & bm1 ) != (  one & bm2 ) )
1197 		numDiff++;
1198 
1199 	    bm1 = bm1 >> 1;
1200 	    bm2 = bm2 >> 1;
1201 
1202 
1203 	}
1204     }
1205 
1206     return numDiff;
1207 }
1208