3 namespace Org\Mxchange\CoreFramework\Iterator\File;
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Filesystem\File\BinaryFile;
7 use Org\Mxchange\CoreFramework\Iterator\BaseIterator;
8 use Org\Mxchange\CoreFramework\Iterator\Filesystem\SeekableWritableFileIterator;
9 use Org\Mxchange\CoreFramework\Traits\File\BinaryFileTrait;
12 use \BadMethodCallException;
13 use \InvalidArgumentException;
14 use \OutOfBoundsException;
19 * @author Roland Haeder <webmaster@ship-simu.org>
21 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2020 Core Developer Team
22 * @license GNU GPL 3.0 or any newer version
23 * @link http://www.ship-simu.org
25 * This program is free software: you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License as published by
27 * the Free Software Foundation, either version 3 of the License, or
28 * (at your option) any later version.
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
35 * You should have received a copy of the GNU General Public License
36 * along with this program. If not, see <http://www.gnu.org/licenses/>.
38 class FileIterator extends BaseIterator implements SeekableWritableFileIterator {
43 * Protected constructor
47 protected function __construct () {
48 // Call parent constructor
49 parent::__construct(__CLASS__);
53 * Creates an instance of this class
55 * @param $binaryFileInstance An instance of a BinaryFile class
56 * @return $iteratorInstance An instance of a Iterator class
58 public final static function createFileIterator (BinaryFile $binaryFileInstance) {
60 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: binaryFileInstance=%s - CALLED!', $binaryFileInstance->__toString()));
61 $iteratorInstance = new FileIterator();
63 // Set the instance here
64 $iteratorInstance->setBinaryFileInstance($binaryFileInstance);
66 // Return the prepared instance
67 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: iteratorInstance=%s - EXIT!', $iteratorInstance->__toString()));
68 return $iteratorInstance;
72 * Gets currently read data
74 * @return $current Currently read data
75 * @throws BadMethodCallException If valid() is FALSE
77 public function current () {
78 // Is condition given?
79 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
80 if (!$this->valid()) {
82 throw new BadMethodCallException('Current key cannot be valid, forgot to invoke valid()?');
86 $current = $this->getBinaryFileInstance()->current();
89 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: current[]=%s - EXIT!', gettype($current)));
94 * Gets current seek position ("key").
96 * @return $key Current key in iteration
97 * @throws BadMethodCallException If valid() is FALSE
99 public function key () {
100 // Is condition given?
101 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
102 if (!$this->valid()) {
104 throw new BadMethodCallException('Current key cannot be valid, forgot to invoke valid()?');
107 // Get key from file instance
108 $key = $this->getBinaryFileInstance()->determineSeekPosition();
111 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: key[%s]=%s - EXIT!', gettype($key), $key));
116 * Advances to next "file" of bytes
120 public function next () {
121 // Call file instance
122 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
123 $this->getBinaryFileInstance()->next();
126 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
130 * Rewinds to the beginning of the file
132 * @return $status Status of this operation
134 public function rewind () {
135 // Call file instance
136 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
137 $this->getBinaryFileInstance()->rewind();
140 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
144 * Checks wether the current entry is valid (not at the end of the file).
145 * This method will return true if an emptied (nulled) entry has been found.
147 * @return $isValid Whether the next entry is valid
149 public function valid () {
150 // Call file instance
151 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
152 $isValid = $this->getBinaryFileInstance()->valid();
155 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: isValid=%d - EXIT!', intval($isValid)));
160 * Seeks to given position
162 * @param $seekPosition Seek position in file
163 * @param $whence Added to offset (default: only use offset to seek to)
164 * @return $status Status of this operation
165 * @throws OutOfBoundsException If the position is not seekable
167 public function seek (int $seekPosition, int $whence = SEEK_SET) {
168 // Validate parameter
169 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: seekPosition=%d,whence=%d - CALLED!', $seekPosition, $whence));
170 if ($seekPosition < 0) {
172 throw new OutOfBoundsException(sprintf('seekPosition=%d is not valid', $seekPosition));
175 // Call file instance
176 $status = $this->getBinaryFileInstance()->seek($seekPosition, $whence);
179 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: status=%d - EXIT!', intval($status)));
186 * @return $size Size (in bytes) of file
188 public function size () {
189 // Call the file object
190 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
191 $size = $this->getBinaryFileInstance()->size();
194 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: size=%d - EXIT!', $size));
199 * Reads given amount of bytes from file.
201 * @param $bytes Amount of bytes to read
202 * @return $data Data read from file
203 * @throws OutOfBoundsException If the position is not seekable
205 public function read (int $bytes = 0) {
206 // Validate parameter
207 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: bytes=%d - CALLED!', $bytes));
210 throw new OutOfBoundsException(sprintf('bytes=%d is not valid', $bytes));
213 // Call file instance
214 $data = $this->getBinaryFileInstance()->read($bytes);
217 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: data[]=%s - EXIT!', gettype($data)));
222 * Analyzes entries in index file. This will count all found (and valid)
223 * entries, mark invalid as damaged and count gaps ("fragmentation"). If
224 * only gaps are found, the file is considered as "virgin" (no entries).
228 public function analyzeFileStructure () {
229 // Just call the file instance
230 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
231 $this->getBinaryFileInstance()->analyzeFileStructure();
234 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
238 * Checks whether the file header is initialized
240 * @return $isInitialized Whether the file header is initialized
242 public function isFileHeaderInitialized () {
243 // Just call the file instance
244 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
245 $isInitialized = $this->getBinaryFileInstance()->isFileHeaderInitialized();
248 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: isInitialized=%d - EXIT!', intval($isInitialized)));
249 return $isInitialized;
253 * Creates the assigned file
257 public function createFileHeader () {
258 // Just call the file instance
259 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
260 $this->getBinaryFileInstance()->createFileHeader();
263 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
267 * Pre-allocates file (if enabled) with some space for later faster write access.
269 * @param $type Type of the file
271 * @throws InvalidArgumentException If a parameter is not valid
273 public function preAllocateFile (string $type) {
274 // Validate parameter
275 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: type=%s - CALLED!', $type));
278 throw new InvalidArgumentException('Parameter "type" is empty');
281 // Just call the file instance
282 $this->getBinaryFileInstance()->preAllocateFile($type);
285 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
289 * Initializes counter for valid entries, arrays for damaged entries and
290 * an array for gap seek positions. If you call this method on your own,
291 * please re-analyze the file structure. So you are better to call
292 * analyzeFileStructure() instead of this method.
296 public function initCountersGapsArray () {
297 // Call file instance
298 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
299 $this->getBinaryFileInstance()->initCountersGapsArray();
302 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
306 * Getter for header size
308 * @return $totalEntries Size of file header
310 public final function getHeaderSize () {
311 // Call file instance
312 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
313 $size = $this->getBinaryFileInstance()->getHeaderSize();
316 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: size=%d - EXIT!', $size));
321 * Setter for header size
323 * @param $headerSize Size of file header
326 public final function setHeaderSize (int $headerSize) {
327 // Call file instance
328 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: headerSize=%d - CALLED!', $headerSize));
329 $this->getBinaryFileInstance()->setHeaderSize($headerSize);
332 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
336 * Getter for header array
338 * @return $header Header array
340 public final function getHeader () {
341 // Call file instance
342 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
343 $header = $this->getBinaryFileInstance()->getHeader();
346 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: header()=%d - EXIT!', count($header)));
353 * @param $header Array for a file header
356 public final function setHeader (array $header) {
357 // Call file instance
358 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: header()=%d - CALLED!', count($header)));
359 $this->getBinaryFileInstance()->setHeader($header);
362 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
366 * Updates seekPosition attribute from file to avoid to much access on file.
370 public function updateSeekPosition () {
371 // Call file instance
372 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
373 $this->getBinaryFileInstance()->updateSeekPosition();
376 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
380 * Getter for total entries
382 * @return $totalEntries Total entries in this file
384 public final function getCounter () {
385 // Call file instance
386 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
387 $counter = $this->getBinaryFileInstance()->getCounter();
390 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: counter=%d - EXIT!', $counter));
395 * "Getter" for file size
397 * @return $fileSize Size of currently loaded file
399 public function getFileSize () {
400 // Call file instance
401 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
402 $size = $this->getBinaryFileInstance()->getFileSize();
405 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: size=%d - EXIT!', $size));
410 * Writes data at given position
412 * @param $seekPosition Seek position
413 * @param $data Data to be written
414 * @param $flushHeader Whether to flush the header (default: flush)
416 * @throws OutOfBoundsException If the position is not seekable
417 * @throws InvalidArgumentException If a parameter is not valid
419 public function writeData (int $seekPosition, string $data, bool $flushHeader = true) {
420 // Validate parameter
421 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: seekPosition=%d,data(%d)=%s,flushHeader=%d - CALLED!', $seekPosition, strlen($data), $data, intval($flushHeader)));
422 if ($seekPosition < 0) {
424 throw new OutOfBoundsException(sprintf('seekPosition=%d is not valid', $seekPosition));
425 } elseif (empty($data)) {
427 throw new InvalidArgumentException('Parameter "data" is empty');
430 // Call file instance
431 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: Calling this->binaryFileInstance->writeData(%d,data()=%d,%d) ...', $seekPosition, strlen($data), intval($flushHeader)));
432 $this->getBinaryFileInstance()->writeData($seekPosition, $data, $flushHeader);
435 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
439 * Writes at given position by seeking to it.
441 * @param $seekPosition Seek position in file
442 * @param $dataStream Data to be written
443 * @return mixed Number of writes bytes or false on error
444 * @throws OutOfBoundsException If the position is not seekable
445 * @throws InvalidArgumentException If a parameter is not valid
447 public function writeAtPosition (int $seekPosition, string $dataStream) {
448 // Validate parameter
449 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: seekPosition=%d,dataStream(%d)=%s - CALLED!', $seekPosition, strlen($dataStream), $dataStream));
450 if ($seekPosition < 0) {
451 // Invalid seek position
452 throw new OutOfBoundsException(sprintf('seekPosition=%d is not valid.', $seekPosition));
453 } elseif (empty($dataStream)) {
455 throw new InvalidArgumentException('Parameter "dataStream" is empty');
458 // Call iterated object's method
459 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: Calling this->binaryFileInstance->writeAtPosition(%d, %s) ...', $seekPosition, $dataStream));
460 $status = $this->getBinaryFileInstance()->writeAtPosition($seekPosition, $dataStream);
463 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: status[%s]=%d - EXIT!', gettype($status), $status));
468 * Getter for seek position
470 * @return $seekPosition Current seek position (stored here in object)
472 public function getSeekPosition () {
473 // Call file instance
474 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
475 $seekPosition = $this->getBinaryFileInstance()->getSeekPosition();
478 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: seekPosition[%s]=%d - EXIT!', gettype($seekPosition), $seekPosition));
479 return $seekPosition;
483 * Writes given value to the file and returns a hash and gap position for it
485 * @param $stackName Group identifier
486 * @param $value Value to be added to the stack
487 * @return $data Hash and gap position
488 * @throws InvalidArgumentException If a parameter is not valid
490 public function writeValueToFile (string $stackName, $value) {
491 // Validate parameter
492 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: stackName=%s,value[]=%s - CALLED!', $stackName, gettype($value)));
493 if (empty($stackName)) {
495 throw new InvalidArgumentException('Parameter "stackName" is empty');
496 } elseif (is_resource($value) || is_object($value)) {
497 // Resources and objects are nothing for file-based indexes (mostly)
498 throw new InvalidArgumentException(sprintf('value[]=%s is not supported by file-based indexes', gettype($value)));
501 // Call file instance
502 $data = $this->getBinaryFileInstance()->writeValueToFile($stackName, $value);
505 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: data[]=%s - EXIT!', gettype($data)));
510 * Writes given raw data to the file and returns a gap position and length
512 * @param $stackName Group identifier
513 * @param $hash Hash from encoded value
514 * @param $encoded Encoded value to be written to the file
515 * @return $data Gap position and length of the raw data
517 public function writeDataToFreeGap (string $stackName, string $hash, string $encoded) {
518 // Validate parameter
519 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: stackName=%s,hash=%s,encoded(%d)=%s - CALLED!', $stackName, $hash, strlen($encoded), $encoded));
521 // Call file instance
522 $data = $this->getBinaryFileInstance()->writeDataToFreeGap($stackName, $hash, $encoded);
525 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: data[]=%s - EXIT!', gettype($data)));
530 * Searches for next suitable gap the given length of data can fit in
531 * including padding bytes.
533 * @param $length Length of raw data
534 * @return $seekPosition Found next gap's seek position
535 * @throws InvalidArgumentException If a parameter is invalid
537 public function searchNextGap (int $length) {
538 // Validate parameter
539 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: length=%d - CALLED!', $length));
542 throw new InvalidArgumentException(sprintf('length=%d is not valid', $length));
545 // Call file instance
546 $seekPosition = $this->getBinaryFileInstance()->searchNextGap($length);
549 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: seekPosition[%s]=%d - EXIT!', gettype($seekPosition), $seekPosition));
550 return $seekPosition;