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;
18 * @author Roland Haeder <webmaster@ship-simu.org>
20 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2020 Core Developer Team
21 * @license GNU GPL 3.0 or any newer version
22 * @link http://www.ship-simu.org
24 * This program is free software: you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation, either version 3 of the License, or
27 * (at your option) any later version.
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
34 * You should have received a copy of the GNU General Public License
35 * along with this program. If not, see <http://www.gnu.org/licenses/>.
37 class FileIterator extends BaseIterator implements SeekableWritableFileIterator {
42 * Protected constructor
46 protected function __construct () {
47 // Call parent constructor
48 parent::__construct(__CLASS__);
52 * Creates an instance of this class
54 * @param $binaryFileInstance An instance of a BinaryFile class
55 * @return $iteratorInstance An instance of a Iterator class
57 public final static function createFileIterator (BinaryFile $binaryFileInstance) {
59 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: binaryFileInstance=%s - CALLED!', $binaryFileInstance->__toString()));
60 $iteratorInstance = new FileIterator();
62 // Set the instance here
63 $iteratorInstance->setBinaryFileInstance($binaryFileInstance);
65 // Return the prepared instance
66 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: iteratorInstance=%s - EXIT!', $iteratorInstance->__toString()));
67 return $iteratorInstance;
71 * Gets currently read data
73 * @return $current Currently read data
74 * @throws BadMethodCallException If valid() is FALSE
76 public function current () {
77 // Is condition given?
78 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
79 if (!$this->valid()) {
81 throw new BadMethodCallException('Current key cannot be valid, forgot to invoke valid()?');
85 $current = $this->getBinaryFileInstance()->current();
88 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: current[]=%s - EXIT!', gettype($current)));
93 * Gets current seek position ("key").
95 * @return $key Current key in iteration
96 * @throws BadMethodCallException If valid() is FALSE
98 public function key () {
99 // Is condition given?
100 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
101 if (!$this->valid()) {
103 throw new BadMethodCallException('Current key cannot be valid, forgot to invoke valid()?');
106 // Get key from file instance
107 $key = $this->getBinaryFileInstance()->determineSeekPosition();
110 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: key[%s]=%s - EXIT!', gettype($key), $key));
115 * Advances to next "file" of bytes
119 public function next () {
120 // Call file instance
121 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
122 $this->getBinaryFileInstance()->next();
125 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
129 * Rewinds to the beginning of the file
131 * @return $status Status of this operation
133 public function rewind () {
134 // Call file instance
135 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
136 $this->getBinaryFileInstance()->rewind();
139 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
143 * Checks wether the current entry is valid (not at the end of the file).
144 * This method will return true if an emptied (nulled) entry has been found.
146 * @return $isValid Whether the next entry is valid
148 public function valid () {
149 // Call file instance
150 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
151 $isValid = $this->getBinaryFileInstance()->valid();
154 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: isValid=%d - EXIT!', intval($isValid)));
159 * Seeks to given position
161 * @param $seekPosition Seek position in file
162 * @param $whence Added to offset (default: only use offset to seek to)
163 * @return $status Status of this operation
164 * @throws InvalidArgumentException If a parameter is not valid
166 public function seek (int $seekPosition, int $whence = SEEK_SET) {
167 // Validate parameter
168 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: seekPosition=%d,whence=%d - CALLED!', $seekPosition, $whence));
169 if ($seekPosition < 0) {
171 throw new InvalidArgumentException(sprintf('seekPosition=%d is not valid', $seekPosition));
174 // Call file instance
175 $status = $this->getBinaryFileInstance()->seek($seekPosition, $whence);
178 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: status=%d - EXIT!', intval($status)));
185 * @return $size Size (in bytes) of file
187 public function size () {
188 // Call the file object
189 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
190 $size = $this->getBinaryFileInstance()->size();
193 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: size=%d - EXIT!', $size));
198 * Reads given amount of bytes from file.
200 * @param $bytes Amount of bytes to read
201 * @return $data Data read from file
203 public function read (int $bytes = 0) {
204 // Validate parameter
205 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: bytes=%d - CALLED!', $bytes));
208 throw new InvalidArgumentException(sprintf('bytes=%d is not valid', $bytes));
211 // Call file instance
212 $data = $this->getBinaryFileInstance()->read($bytes);
215 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: data[]=%s - EXIT!', gettype($data)));
220 * Analyzes entries in index file. This will count all found (and valid)
221 * entries, mark invalid as damaged and count gaps ("fragmentation"). If
222 * only gaps are found, the file is considered as "virgin" (no entries).
226 public function analyzeFileStructure () {
227 // Just call the file instance
228 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
229 $this->getBinaryFileInstance()->analyzeFileStructure();
232 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
236 * Checks whether the file header is initialized
238 * @return $isInitialized Whether the file header is initialized
240 public function isFileHeaderInitialized () {
241 // Just call the file instance
242 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
243 $isInitialized = $this->getBinaryFileInstance()->isFileHeaderInitialized();
246 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: isInitialized=%d - EXIT!', intval($isInitialized)));
247 return $isInitialized;
251 * Creates the assigned file
255 public function createFileHeader () {
256 // Just call the file instance
257 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
258 $this->getBinaryFileInstance()->createFileHeader();
261 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
265 * Pre-allocates file (if enabled) with some space for later faster write access.
267 * @param $type Type of the file
269 * @throws InvalidArgumentException If a parameter is not valid
271 public function preAllocateFile (string $type) {
272 // Validate parameter
273 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: type=%s - CALLED!', $type));
276 throw new InvalidArgumentException('Parameter "type" is empty');
279 // Just call the file instance
280 $this->getBinaryFileInstance()->preAllocateFile($type);
283 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
287 * Initializes counter for valid entries, arrays for damaged entries and
288 * an array for gap seek positions. If you call this method on your own,
289 * please re-analyze the file structure. So you are better to call
290 * analyzeFileStructure() instead of this method.
294 public function initCountersGapsArray () {
295 // Call file instance
296 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
297 $this->getBinaryFileInstance()->initCountersGapsArray();
300 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
304 * Getter for header size
306 * @return $totalEntries Size of file header
308 public final function getHeaderSize () {
309 // Call file instance
310 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
311 $size = $this->getBinaryFileInstance()->getHeaderSize();
314 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: size=%d - EXIT!', $size));
319 * Setter for header size
321 * @param $headerSize Size of file header
324 public final function setHeaderSize (int $headerSize) {
325 // Call file instance
326 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: headerSize=%d - CALLED!', $headerSize));
327 $this->getBinaryFileInstance()->setHeaderSize($headerSize);
330 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
334 * Getter for header array
336 * @return $header Header array
338 public final function getHeader () {
339 // Call file instance
340 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
341 $header = $this->getBinaryFileInstance()->getHeader();
344 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: header()=%d - EXIT!', count($header)));
351 * @param $header Array for a file header
354 public final function setHeader (array $header) {
355 // Call file instance
356 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: header()=%d - CALLED!', count($header)));
357 $this->getBinaryFileInstance()->setHeader($header);
360 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
364 * Updates seekPosition attribute from file to avoid to much access on file.
368 public function updateSeekPosition () {
369 // Call file instance
370 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
371 $this->getBinaryFileInstance()->updateSeekPosition();
374 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
378 * Getter for total entries
380 * @return $totalEntries Total entries in this file
382 public final function getCounter () {
383 // Call file instance
384 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
385 $counter = $this->getBinaryFileInstance()->getCounter();
388 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: counter=%d - EXIT!', $counter));
393 * "Getter" for file size
395 * @return $fileSize Size of currently loaded file
397 public function getFileSize () {
398 // Call file instance
399 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
400 $size = $this->getBinaryFileInstance()->getFileSize();
403 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: size=%d - EXIT!', $size));
408 * Writes data at given position
410 * @param $seekPosition Seek position
411 * @param $data Data to be written
412 * @param $flushHeader Whether to flush the header (default: flush)
414 * @throws InvalidArgumentException If a parameter is not valid
416 public function writeData (int $seekPosition, string $data, bool $flushHeader = true) {
417 // Validate parameter
418 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: seekPosition=%d,data(%d)=%s,flushHeader=%d - CALLED!', $seekPosition, strlen($data), $data, intval($flushHeader)));
419 if ($seekPosition < 0) {
421 throw new InvalidArgumentException(sprintf('seekPosition=%d is not valid', $seekPosition));
422 } elseif (empty($data)) {
424 throw new InvalidArgumentException('Parameter "data" is empty');
427 // Call file instance
428 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: Calling this->binaryFileInstance->writeData(%d,data()=%d,%d) ...', $seekPosition, strlen($data), intval($flushHeader)));
429 $this->getBinaryFileInstance()->writeData($seekPosition, $data, $flushHeader);
432 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: EXIT!');
436 * Writes at given position by seeking to it.
438 * @param $seekPosition Seek position in file
439 * @param $dataStream Data to be written
440 * @return mixed Number of writes bytes or false on error
441 * @throws InvalidArgumentException If a parameter is not valid
443 public function writeAtPosition (int $seekPosition, string $dataStream) {
444 // Validate parameter
445 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: seekPosition=%d,dataStream(%d)=%s - CALLED!', $seekPosition, strlen($dataStream), $dataStream));
446 if ($seekPosition < 0) {
447 // Invalid seek position
448 throw new InvalidArgumentException(sprintf('seekPosition=%d is not valid.', $seekPosition));
449 } elseif (empty($dataStream)) {
451 throw new InvalidArgumentException('Parameter "dataStream" is empty');
454 // Call iterated object's method
455 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: Calling this->binaryFileInstance->writeAtPosition(%d, %s) ...', $seekPosition, $dataStream));
456 $status = $this->getBinaryFileInstance()->writeAtPosition($seekPosition, $dataStream);
459 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: status[%s]=%d - EXIT!', gettype($status), $status));
464 * Getter for seek position
466 * @return $seekPosition Current seek position (stored here in object)
468 public function getSeekPosition () {
469 // Call file instance
470 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-ITERATOR: CALLED!');
471 $seekPosition = $this->getBinaryFileInstance()->getSeekPosition();
474 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: seekPosition[%s]=%d - EXIT!', gettype($seekPosition), $seekPosition));
475 return $seekPosition;
479 * Writes given value to the file and returns a hash and gap position for it
481 * @param $groupId Group identifier
482 * @param $value Value to be added to the stack
483 * @return $data Hash and gap position
484 * @throws InvalidArgumentException If a parameter is not valid
486 public function writeValueToFile (string $groupId, $value) {
487 // Validate parameter
488 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: groupId=%s,value[]=%s - CALLED!', $groupId, gettype($value)));
489 if (empty($groupId)) {
491 throw new InvalidArgumentException('Parameter "groupId" is empty');
492 } elseif (is_resource($value) || is_object($value)) {
493 // Resources and objects are nothing for file-based indexes (mostly)
494 throw new InvalidArgumentException(sprintf('value[]=%s is not supported by file-based indexes', gettype($value)));
497 // Call file instance
498 $data = $this->getBinaryFileInstance()->writeValueToFile($groupId, $value);
501 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: data[]=%s - EXIT!', gettype($data)));
506 * Writes given raw data to the file and returns a gap position and length
508 * @param $groupId Group identifier
509 * @param $hash Hash from encoded value
510 * @param $encoded Encoded value to be written to the file
511 * @return $data Gap position and length of the raw data
513 public function writeDataToFreeGap (string $groupId, string $hash, string $encoded) {
514 // Validate parameter
515 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: groupId=%s,hash=%s,encoded(%d)=%s - CALLED!', $groupId, $hash, strlen($encoded), $encoded));
517 // Call file instance
518 $data = $this->getBinaryFileInstance()->writeDataToFreeGap($groupId, $hash, $encoded);
521 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: data[]=%s - EXIT!', gettype($data)));
526 * Searches for next suitable gap the given length of data can fit in
527 * including padding bytes.
529 * @param $length Length of raw data
530 * @return $seekPosition Found next gap's seek position
531 * @throws InvalidArgumentException If a parameter is invalid
533 public function searchNextGap (int $length) {
534 // Validate parameter
535 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: length=%d - CALLED!', $length));
538 throw new InvalidArgumentException(sprintf('length=%d is not valid', $length));
541 // Call file instance
542 $seekPosition = $this->getBinaryFileInstance()->searchNextGap($length);
545 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-ITERATOR: seekPosition[%s]=%d - EXIT!', gettype($seekPosition), $seekPosition));
546 return $seekPosition;