Continued:
[core.git] / framework / main / classes / stacker / file / class_BaseFileStack.php
1 <?php
2 // Own namespace
3 namespace CoreFramework\Stack\File;
4
5 // Import framework stuff
6 use CoreFramework\Factory\ObjectFactory;
7 use CoreFramework\Filesystem\File\BaseBinaryFile;
8
9 /**
10  * A general file-based stack class
11  *
12  * @author              Roland Haeder <webmaster@ship-simu.org>
13  * @version             0.0.0
14  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Core Developer Team
15  * @license             GNU GPL 3.0 or any newer version
16  * @link                http://www.ship-simu.org
17  *
18  * This program is free software: you can redistribute it and/or modify
19  * it under the terms of the GNU General Public License as published by
20  * the Free Software Foundation, either version 3 of the License, or
21  * (at your option) any later version.
22  *
23  * This program is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  * GNU General Public License for more details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
30  */
31 class BaseFileStack extends BaseStacker {
32         /**
33          * Magic for this stack
34          */
35         const STACK_MAGIC = 'STACKv0.1';
36
37         /**
38          * Name of array index for gap position
39          */
40         const ARRAY_INDEX_GAP_POSITION = 'gap';
41
42         /**
43          * Name of array index for hash
44          */
45         const ARRAY_INDEX_HASH = 'hash';
46
47         /**
48          * Name of array index for length of raw data
49          */
50         const ARRAY_INDEX_DATA_LENGTH = 'length';
51
52         /**
53          * Protected constructor
54          *
55          * @param       $className      Name of the class
56          * @return      void
57          */
58         protected function __construct ($className) {
59                 // Call parent constructor
60                 parent::__construct($className);
61         }
62
63         /**
64          * Reads the file header
65          *
66          * @return      void
67          * @todo        To hard assertions here, better rewrite them to exceptions
68          */
69         public function readFileHeader () {
70                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
71
72                 // First rewind to beginning as the header sits at the beginning ...
73                 $this->getIteratorInstance()->rewind();
74
75                 // Then read it (see constructor for calculation)
76                 $data = $this->getIteratorInstance()->read($this->getIteratorInstance()->getHeaderSize());
77                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Read %d bytes (%d wanted).', __METHOD__, __LINE__, strlen($data), $this->getIteratorInstance()->getHeaderSize()));
78
79                 // Have all requested bytes been read?
80                 assert(strlen($data) == $this->getIteratorInstance()->getHeaderSize());
81                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Passed assert().', __METHOD__, __LINE__));
82
83                 // Last character must be the separator
84                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] data(-1)=%s', __METHOD__, __LINE__, dechex(ord(substr($data, -1, 1)))));
85                 assert(substr($data, -1, 1) == chr(BaseBinaryFile::SEPARATOR_HEADER_ENTRIES));
86                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Passed assert().', __METHOD__, __LINE__));
87
88                 // Okay, then remove it
89                 $data = substr($data, 0, -1);
90
91                 // And update seek position
92                 $this->getIteratorInstance()->updateSeekPosition();
93
94                 /*
95                  * Now split it:
96                  *
97                  * 0 => magic
98                  * 1 => total entries
99                  * 2 => current seek position
100                  */
101                 $header = explode(chr(BaseBinaryFile::SEPARATOR_HEADER_DATA), $data);
102
103                 // Set header here
104                 $this->getIteratorInstance()->setHeader($header);
105
106                 // Check if the array has only 3 elements
107                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] header(%d)=%s', __METHOD__, __LINE__, count($header), print_r($header, TRUE)));
108                 assert(count($header) == 3);
109                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Passed assert().', __METHOD__, __LINE__));
110
111                 // Check magic
112                 assert($header[0] == self::STACK_MAGIC);
113                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Passed assert().', __METHOD__, __LINE__));
114
115                 // Check length of count and seek position
116                 assert(strlen($header[1]) == BaseBinaryFile::LENGTH_COUNT);
117                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Passed assert().', __METHOD__, __LINE__));
118                 assert(strlen($header[2]) == BaseBinaryFile::LENGTH_POSITION);
119                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Passed assert().', __METHOD__, __LINE__));
120
121                 // Decode count and seek position
122                 $header[1] = hex2bin($header[1]);
123                 $header[2] = hex2bin($header[2]);
124
125                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
126         }
127
128         /**
129          * Flushes the file header
130          *
131          * @return      void
132          */
133         public function flushFileHeader () {
134                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
135
136                 // Put all informations together
137                 $header = sprintf('%s%s%s%s%s%s',
138                         // Magic
139                         self::STACK_MAGIC,
140
141                         // Separator magic<->count
142                         chr(BaseBinaryFile::SEPARATOR_HEADER_DATA),
143
144                         // Total entries (will be zero) and pad it to 20 chars
145                         str_pad($this->dec2hex($this->getIteratorInstance()->getCounter()), BaseBinaryFile::LENGTH_COUNT, '0', STR_PAD_LEFT),
146
147                         // Separator count<->seek position
148                         chr(BaseBinaryFile::SEPARATOR_HEADER_DATA),
149
150                         // Position (will be zero)
151                         str_pad($this->dec2hex($this->getIteratorInstance()->getSeekPosition(), 2), BaseBinaryFile::LENGTH_POSITION, '0', STR_PAD_LEFT),
152
153                         // Separator position<->entries
154                         chr(BaseBinaryFile::SEPARATOR_HEADER_ENTRIES)
155                 );
156
157                 // Write it to disk (header is always at seek position 0)
158                 $this->getIteratorInstance()->writeData(0, $header, FALSE);
159
160                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
161         }
162
163         /**
164          * Initializes this file-based stack.
165          *
166          * @param       $fileName       File name of this stack
167          * @param       $type           Type of this stack (e.g. url_source for URL sources)
168          * @return      void
169          * @todo        Currently the stack file is not cached, please implement a memory-handling class and if enough RAM is found, cache the whole stack file.
170          */
171         protected function initFileStack ($fileName, $type) {
172                 // Get a stack file instance
173                 $fileInstance = ObjectFactory::createObjectByConfiguredName('stack_file_class', array($fileName, $this));
174
175                 // Get iterator instance
176                 $iteratorInstance = ObjectFactory::createObjectByConfiguredName('file_iterator_class', array($fileInstance));
177
178                 // Is the instance implementing the right interface?
179                 assert($iteratorInstance instanceof SeekableWritableFileIterator);
180
181                 // Set iterator here
182                 $this->setIteratorInstance($iteratorInstance);
183
184                 // Calculate header size
185                 $this->getIteratorInstance()->setHeaderSize(
186                         strlen(self::STACK_MAGIC) +
187                         strlen(chr(BaseBinaryFile::SEPARATOR_HEADER_DATA)) +
188                         BaseBinaryFile::LENGTH_COUNT +
189                         strlen(chr(BaseBinaryFile::SEPARATOR_HEADER_DATA)) +
190                         BaseBinaryFile::LENGTH_POSITION +
191                         strlen(chr(BaseBinaryFile::SEPARATOR_HEADER_ENTRIES))
192                 );
193
194                 // Init counters and gaps array
195                 $this->getIteratorInstance()->initCountersGapsArray();
196
197                 // Is the file's header initialized?
198                 if (!$this->getIteratorInstance()->isFileHeaderInitialized()) {
199                         // No, then create it (which may pre-allocate the stack)
200                         $this->getIteratorInstance()->createFileHeader();
201
202                         // And pre-allocate a bit
203                         $this->getIteratorInstance()->preAllocateFile('file_stack');
204                 } // END - if
205
206                 // Load the file header
207                 $this->readFileHeader();
208
209                 // Count all entries in file
210                 $this->getIteratorInstance()->analyzeFile();
211
212                 /*
213                  * Get stack index instance. This can be used for faster
214                  * "defragmentation" and startup.
215                  */
216                 $indexInstance = FileStackIndexFactory::createFileStackIndexInstance($fileName, $type);
217
218                 // And set it here
219                 $this->setIndexInstance($indexInstance);
220         }
221
222         /**
223          * Adds a value to given stack
224          *
225          * @param       $stackerName    Name of the stack
226          * @param       $value                  Value to add to this stacker
227          * @return      void
228          * @throws      FullStackerException    If the stack is full
229          */
230         protected function addValue ($stackerName, $value) {
231                 // Do some tests
232                 if ($this->isStackFull($stackerName)) {
233                         // Stacker is full
234                         throw new FullStackerException(array($this, $stackerName, $value), self::EXCEPTION_STACKER_IS_FULL);
235                 } // END - if
236
237                 // Debug message
238                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . '] stackerName=' . $stackerName . ',value[' . gettype($value) . ']=' . print_r($value, TRUE));
239
240                 // No objects/resources are allowed as their serialization takes to long
241                 assert(!is_object($value));
242                 assert(!is_resource($value));
243
244                 /*
245                  * Now add the value to the file stack which returns gap position, a
246                  * hash and length of the raw data.
247                  */
248                 $data = $this->getIteratorInstance()->writeValueToFile($stackerName, $value);
249
250                 // Add the hash and gap position to the index
251                 $this->getIndexInstance()->addHashToIndex($stackerName, $data);
252         }
253
254         /**
255          * Get last value from named stacker
256          *
257          * @param       $stackerName    Name of the stack
258          * @return      $value                  Value of last added value
259          * @throws      EmptyStackerException   If the stack is empty
260          */
261         protected function getLastValue ($stackerName) {
262                 // Is the stack not yet initialized or full?
263                 if ($this->isStackEmpty($stackerName)) {
264                         // Throw an exception
265                         throw new EmptyStackerException(array($this, $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
266                 } // END - if
267
268                 // Now get the last value
269                 /* NOISY-DEBUG: */ $this->partialStub('[' . __METHOD__ . ':' . __LINE__ . '] stackerName=' . $stackerName);
270                 $value = NULL;
271
272                 // Return it
273                 return $value;
274         }
275
276         /**
277          * Get first value from named stacker
278          *
279          * @param       $stackerName    Name of the stack
280          * @return      $value                  Value of last added value
281          * @throws      EmptyStackerException   If the stack is empty
282          */
283         protected function getFirstValue ($stackerName) {
284                 // Is the stack not yet initialized or full?
285                 if ($this->isStackEmpty($stackerName)) {
286                         // Throw an exception
287                         throw new EmptyStackerException(array($this, $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
288                 } // END - if
289
290                 // Now get the first value
291                 /* NOISY-DEBUG: */ $this->partialStub('[' . __METHOD__ . ':' . __LINE__ . '] stackerName=' . $stackerName);
292                 $value = NULL;
293
294                 // Return it
295                 return $value;
296         }
297
298         /**
299          * "Pops" last entry from stack
300          *
301          * @param       $stackerName    Name of the stack
302          * @return      $value                  Value "poped" from array
303          * @throws      EmptyStackerException   If the stack is empty
304          */
305         protected function popLast ($stackerName) {
306                 // Is the stack not yet initialized or full?
307                 if ($this->isStackEmpty($stackerName)) {
308                         // Throw an exception
309                         throw new EmptyStackerException(array($this, $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
310                 } // END - if
311
312                 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
313                 /* NOISY-DEBUG: */ $this->partialStub('[' . __METHOD__ . ':' . __LINE__ . '] stackerName=' . $stackerName);
314                 return NULL;
315         }
316
317         /**
318          * "Pops" first entry from stack
319          *
320          * @param       $stackerName    Name of the stack
321          * @return      $value                  Value "shifted" from array
322          * @throws      EmptyStackerException   If the named stacker is empty
323          */
324         protected function popFirst ($stackerName) {
325                 // Is the stack not yet initialized or full?
326                 if ($this->isStackEmpty($stackerName)) {
327                         // Throw an exception
328                         throw new EmptyStackerException(array($this, $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
329                 } // END - if
330
331                 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
332                 /* NOISY-DEBUG: */ $this->partialStub('[' . __METHOD__ . ':' . __LINE__ . '] stackerName=' . $stackerName);
333                 return NULL;
334         }
335
336         /**
337          * Checks whether the given stack is full
338          *
339          * @param       $stackerName    Name of the stack
340          * @return      $isFull                 Whether the stack is full
341          */
342         protected function isStackFull ($stackerName) {
343                 // File-based stacks will only run full if the disk space is low.
344                 // @TODO Please implement this, returning FALSE
345                 $isFull = FALSE;
346
347                 // Return result
348                 return $isFull;
349         }
350
351         /**
352          * Checks whether the given stack is empty
353          *
354          * @param       $stackerName            Name of the stack
355          * @return      $isEmpty                        Whether the stack is empty
356          * @throws      NoStackerException      If given stack is missing
357          */
358         public function isStackEmpty ($stackerName) {
359                 // So, is the stack empty?
360                 $isEmpty = (($this->getStackCount($stackerName)) == 0);
361
362                 // Return result
363                 return $isEmpty;
364         }
365
366         /**
367          * Initializes given stacker
368          *
369          * @param       $stackerName    Name of the stack
370          * @param       $forceReInit    Force re-initialization
371          * @return      void
372          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
373          */
374         public function initStack ($stackerName, $forceReInit = FALSE) {
375                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
376         }
377
378         /**
379          * Initializes all stacks
380          *
381          * @return      void
382          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
383          */
384         public function initStacks (array $stacks, $forceReInit = FALSE) {
385                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
386         }
387
388         /**
389          * Checks whether the given stack is initialized (set in array $stackers)
390          *
391          * @param       $stackerName    Name of the stack
392          * @return      $isInitialized  Whether the stack is initialized
393          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
394          */
395         public function isStackInitialized ($stackerName) {
396                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
397         }
398
399         /**
400          * Determines whether the EOF has been reached
401          *
402          * @return      $isEndOfFileReached             Whether the EOF has been reached
403          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
404          */
405         public function isEndOfFileReached () {
406                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
407         }
408
409         /**
410          * Getter for file name
411          *
412          * @return      $fileName       The current file name
413          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
414          */
415         public function getFileName () {
416                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
417         }
418
419         /**
420          * Getter for size of given stack (array count)
421          *
422          * @param       $stackerName    Name of the stack
423          * @return      $count                  Size of stack (array count)
424          */
425         public function getStackCount ($stackerName) {
426                 // Now, simply return the found count value, this must be up-to-date then!
427                 return $this->getIteratorInstance()->getCounter();
428         }
429
430         /**
431          * Calculates minimum length for one entry/block
432          *
433          * @return      $length         Minimum length for one entry/block
434          */
435         public function calculateMinimumBlockLength () {
436                 // Calulcate it
437                 $length =
438                         // Length of entry group
439                         BaseBinaryFile::LENGTH_GROUP + strlen(chr(BaseBinaryFile::SEPARATOR_GROUP_HASH)) +
440                         // Hash + value
441                         self::getHashLength() + strlen(chr(BaseBinaryFile::SEPARATOR_HASH_VALUE)) + 1 +
442                         // Final separator
443                         strlen(chr(BaseBinaryFile::SEPARATOR_ENTRIES));
444
445                 // Return it
446                 return $length;
447         }
448
449         /**
450          * Initializes counter for valid entries, arrays for damaged entries and
451          * an array for gap seek positions. If you call this method on your own,
452          * please re-analyze the file structure. So you are better to call
453          * analyzeFile() instead of this method.
454          *
455          * @return      void
456          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
457          */
458         public function initCountersGapsArray () {
459                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
460         }
461
462         /**
463          * Getter for header size
464          *
465          * @return      $totalEntries   Size of file header
466          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
467          */
468         public final function getHeaderSize () {
469                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
470         }
471
472         /**
473          * Setter for header size
474          *
475          * @param       $headerSize             Size of file header
476          * @return      void
477          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
478          */
479         public final function setHeaderSize ($headerSize) {
480                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
481         }
482
483         /**
484          * Getter for header array
485          *
486          * @return      $totalEntries   Size of file header
487          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
488          */
489         public final function getHeader () {
490                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
491         }
492
493         /**
494          * Setter for header
495          *
496          * @param       $header         Array for a file header
497          * @return      void
498          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
499          */
500         public final function setHeader (array $header) {
501                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
502         }
503
504         /**
505          * Updates seekPosition attribute from file to avoid to much access on file.
506          *
507          * @return      void
508          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
509          */
510         public function updateSeekPosition () {
511                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
512         }
513
514         /**
515          * Getter for total entries
516          *
517          * @return      $totalEntries   Total entries in this file
518          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
519          */
520         public final function getCounter () {
521                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
522         }
523
524         /**
525          * Writes data at given position
526          *
527          * @param       $seekPosition   Seek position
528          * @param       $data                   Data to be written
529          * @param       $flushHeader    Whether to flush the header (default: flush)
530          * @return      void
531          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
532          */
533         public function writeData ($seekPosition, $data, $flushHeader = TRUE) {
534                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] seekPosition=%s,data[]=%s,flushHeader=%d', __METHOD__, __LINE__, $seekPosition, gettype($data), intval($flushHeader)));
535                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
536         }
537
538         /**
539          * Writes given value to the file and returns a hash and gap position for it
540          *
541          * @param       $groupId        Group identifier
542          * @param       $value          Value to be added to the stack
543          * @return      $data           Hash and gap position
544          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
545          */
546         public function writeValueToFile ($groupId, $value) {
547                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] groupId=%s,value[%s]=%s', __METHOD__, __LINE__, $groupId, gettype($value), print_r($value, TRUE)));
548                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
549         }
550
551         /**
552          * Searches for next suitable gap the given length of data can fit in
553          * including padding bytes.
554          *
555          * @param       $length                 Length of raw data
556          * @return      $seekPosition   Found next gap's seek position
557          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
558          */
559         public function searchNextGap ($length) {
560                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] length=%s', __METHOD__, __LINE__, $length));
561                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
562         }
563
564         /**
565          * "Getter" for file size
566          *
567          * @return      $fileSize       Size of currently loaded file
568          */
569         public function getFileSize () {
570                 // Call iterator's method
571                 return $this->getIteratorInstance()->getFileSize();
572         }
573
574         /**
575          * Writes given raw data to the file and returns a gap position and length
576          *
577          * @param       $groupId        Group identifier
578          * @param       $hash           Hash from encoded value
579          * @param       $encoded        Encoded value to be written to the file
580          * @return      $data           Gap position and length of the raw data
581          */
582         public function writeDataToFreeGap ($groupId, $hash, $encoded) {
583                 // Debug message
584                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] groupId=%s,hash=%s,encoded()=%d - CALLED!', __METHOD__, __LINE__, $groupId, $hash, strlen($encoded)));
585
586                 // Raw data been written to the file
587                 $rawData = sprintf('%s%s%s%s%s',
588                         $groupId,
589                         BaseBinaryFile::SEPARATOR_GROUP_HASH,
590                         hex2bin($hash),
591                         BaseBinaryFile::SEPARATOR_HASH_VALUE,
592                         $encoded
593                 );
594
595                 // Debug message
596                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] groupId=%s,hash=%s,rawData()=%d', __METHOD__, __LINE__, $groupId, $hash, strlen($rawData)));
597
598                 // Search for next free gap
599                 $gapPosition = $this->getIteratorInstance()->searchNextGap(strlen($rawData));
600
601                 // Gap position cannot be smaller than header length + 1
602                 assert($gapPosition > $this->getIteratorInstance()->getHeaderSize());
603
604                 // Debug message
605                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] groupId=%s,hash=%s,gapPosition=%s', __METHOD__, __LINE__, $groupId, $hash, $gapPosition));
606
607                 // Then write the data at that gap
608                 $this->getIteratorInstance()->writeData($gapPosition, $rawData);
609
610                 // Debug message
611                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] groupId=%s,hash=%s,rawData()=%d - EXIT!', __METHOD__, __LINE__, $groupId, $hash, strlen($rawData)));
612
613                 // Return gap position, hash and length of raw data
614                 return array(
615                         self::ARRAY_INDEX_GAP_POSITION => $gapPosition,
616                         self::ARRAY_INDEX_HASH         => $hash,
617                         self::ARRAY_INDEX_DATA_LENGTH  => strlen($rawData)
618                 );
619         }
620
621 }