]> git.mxchange.org Git - core.git/blob - framework/main/classes/index/class_BaseIndex.php
6f0c5a146275b35e016e4f64ce65990f0bfd2602
[core.git] / framework / main / classes / index / class_BaseIndex.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Index;
4
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Factory\ObjectFactory;
7 use Org\Mxchange\CoreFramework\Filesystem\File\BaseBinaryFile;
8 use Org\Mxchange\CoreFramework\Generic\UnsupportedOperationException;
9 use Org\Mxchange\CoreFramework\Iterator\Filesystem\SeekableWritableFileIterator;
10 use Org\Mxchange\CoreFramework\Object\BaseFrameworkSystem;
11 use Org\Mxchange\CoreFramework\Utils\String\StringUtils;
12 use Org\Mxchange\CoreFramework\Traits\Iterator\IteratorTrait;
13
14 // Import SPL stuff
15 use \SplFileInfo;
16 use \UnexpectedValueException;
17
18 /**
19  * A general index class
20  *
21  * @author              Roland Haeder <webmaster@ship-simu.org>
22  * @version             0.0.0
23  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2020 Core Developer Team
24  * @license             GNU GPL 3.0 or any newer version
25  * @link                http://www.ship-simu.org
26  *
27  * This program is free software: you can redistribute it and/or modify
28  * it under the terms of the GNU General Public License as published by
29  * the Free Software Foundation, either version 3 of the License, or
30  * (at your option) any later version.
31  *
32  * This program is distributed in the hope that it will be useful,
33  * but WITHOUT ANY WARRANTY; without even the implied warranty of
34  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
35  * GNU General Public License for more details.
36  *
37  * You should have received a copy of the GNU General Public License
38  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
39  */
40 abstract class BaseIndex extends BaseFrameworkSystem {
41         // Load traits
42         use IteratorTrait;
43
44         /**
45          * Magic for this index
46          */
47         const INDEX_MAGIC = 'INDEXv0.1';
48
49         /**
50          * Separator group->hash
51          */
52         const SEPARATOR_GROUP_HASH = 0x01;
53
54         /**
55          * Separator hash->gap position
56          */
57         const SEPARATOR_HASH_GAP_POSITION = 0x02;
58
59         /**
60          * Separator gap position->length
61          */
62         const SEPARATOR_GAP_LENGTH = 0x03;
63
64         /**
65          * Protected constructor
66          *
67          * @param       $className      Name of the class
68          * @return      void
69          */
70         protected function __construct (string $className) {
71                 // Call parent constructor
72                 parent::__construct($className);
73         }
74
75         /**
76          * Reads the file header
77          *
78          * @return      void
79          * @throws      UnexpectedValueException        If header length is invalid
80          */
81         public function readFileHeader () {
82                 // First rewind to beginning as the header sits at the beginning ...
83                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-INDEX: CALLED!');
84                 $this->getIteratorInstance()->rewind();
85
86                 // Then read it (see constructor for calculation)
87                 $data = $this->getIteratorInstance()->read($this->getIteratorInstance()->getHeaderSize());
88
89                 // Have all requested bytes been read?
90                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('Read %d bytes (%d wanted).', strlen($data), $this->getIteratorInstance()->getHeaderSize()));
91                 if (strlen($data) != $this->getIteratorInstance()->getHeaderSize()) {
92                         // Invalid header length
93                         throw new UnexpectedValueException(sprintf('data(%d)=%s is not expected length %d',
94                                 strlen($data),
95                                 $data,
96                                 $this->getIteratorInstance()->getHeaderSize()
97                         ));
98                 } elseif (substr($data, -1, 1) != chr(BaseBinaryFile::SEPARATOR_HEADER_ENTRIES)) {
99                         // Bad last character
100                         throw new UnexpectedValueException(sprintf('data=%s does not end with "%s"',
101                                 $data,
102                                 chr(BaseBinaryFile::SEPARATOR_HEADER_ENTRIES)
103                         ));
104                 }
105
106                 // Okay, then remove it
107                 $data = substr($data, 0, -1);
108
109                 // And update seek position
110                 $this->getIteratorInstance()->updateSeekPosition();
111
112                 /*
113                  * Now split it:
114                  *
115                  * 0 => magic
116                  * 1 => total entries
117                  */
118                 $header = explode(chr(BaseBinaryFile::SEPARATOR_HEADER_DATA), $data);
119
120                 // Set it here
121                 $this->getIteratorInstance()->setHeader($header);
122
123                 // Check if the array has only 3 elements
124                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('header(%d)=%s', count($header), print_r($header, true)));
125                 assert(count($header) == 2);
126
127                 // Check magic
128                 assert($header[0] == self::INDEX_MAGIC);
129
130                 // Check length of count
131                 assert(strlen($header[1]) == BaseBinaryFile::LENGTH_COUNT);
132
133                 // Decode count
134                 $header[1] = hex2bin($header[1]);
135
136                 // Trace message
137                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-INDEX: EXIT!');
138         }
139
140         /**
141          * Flushes the file header
142          *
143          * @return      void
144          */
145         public function flushFileHeader () {
146                 // Put all informations together
147                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-INDEX: CALLED!');
148                 $header = sprintf('%s%s%s%s',
149                         // Magic
150                         self::INDEX_MAGIC,
151
152                         // Separator header data
153                         chr(BaseBinaryFile::SEPARATOR_HEADER_DATA),
154
155                         // Total entries
156                         str_pad(StringUtils::dec2hex($this->getIteratorInstance()->getCounter()), BaseBinaryFile::LENGTH_COUNT, '0', STR_PAD_LEFT),
157
158                         // Separator header<->entries
159                         chr(BaseBinaryFile::SEPARATOR_HEADER_ENTRIES)
160                 );
161
162                 // Write it to disk (header is always at seek position 0)
163                 $this->getIteratorInstance()->writeData(0, $header, false);
164
165                 // Trace message
166                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-INDEX: EXIT!');
167         }
168
169         /**
170          * Initializes this index
171          *
172          * @param       $fileInfoInstance       An instance of a SplFileInfo class
173          * @return      void
174          * @todo        Currently the index file is not cached, please implement a memory-handling class and if enough RAM is found, cache the whole index file.
175          */
176         protected function initIndex (SplFileInfo $fileInfoInstance) {
177                 // Get a file i/o pointer instance for index file
178                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('fileInfoInstance[%s]=%s - CALLED!', get_class($fileInfoInstance), $fileInfoInstance));
179                 $fileInstance = ObjectFactory::createObjectByConfiguredName('index_file_class', array($fileInfoInstance, $this));
180
181                 // Get iterator instance
182                 $iteratorInstance = ObjectFactory::createObjectByConfiguredName('file_iterator_class', array($fileInstance));
183
184                 // Set iterator here
185                 $this->setIteratorInstance($iteratorInstance);
186
187                 // Calculate header size
188                 $this->getIteratorInstance()->setHeaderSize(
189                         strlen(self::INDEX_MAGIC) +
190                         strlen(chr(BaseBinaryFile::SEPARATOR_HEADER_DATA)) +
191                         BaseBinaryFile::LENGTH_COUNT +
192                         strlen(chr(BaseBinaryFile::SEPARATOR_HEADER_ENTRIES))
193                 );
194
195                 // Init counters and gaps array
196                 $this->getIteratorInstance()->initCountersGapsArray();
197
198                 // Is the file's header initialized?
199                 if (!$this->getIteratorInstance()->isFileHeaderInitialized()) {
200                         // No, then create it (which may pre-allocate the index)
201                         $this->getIteratorInstance()->createFileHeader();
202
203                         // And pre-allocate a bit
204                         $this->getIteratorInstance()->preAllocateFile('index');
205                 }
206
207                 // Load the file header
208                 $this->readFileHeader();
209
210                 // Count all entries in file
211                 $this->getIteratorInstance()->analyzeFile();
212
213                 // Trace message
214                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-INDEX: EXIT!');
215         }
216
217         /**
218          * Calculates minimum length for one entry/block
219          *
220          * @return      $length         Minimum length for one entry/block
221          */
222         public function calculateMinimumBlockLength () {
223                 // Calulcate it
224                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-INDEX: CALLED!');
225                 $length = BaseBinaryFile::LENGTH_TYPE + strlen(chr(BaseBinaryFile::SEPARATOR_TYPE_POSITION)) + BaseBinaryFile::LENGTH_POSITION + strlen(chr(BaseBinaryFile::SEPARATOR_ENTRIES));
226
227                 // Return it
228                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-INDEX: length=%d - EXIT!', $length));
229                 return $length;
230         }
231
232         /**
233          * Determines whether the EOF has been reached
234          *
235          * @return      $isEndOfFileReached             Whether the EOF has been reached
236          * @throws      UnsupportedOperationException   If this method is called
237          */
238         public function isEndOfFileReached () {
239                 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
240         }
241
242         /**
243          * Initializes counter for valid entries, arrays for damaged entries and
244          * an array for gap seek positions. If you call this method on your own,
245          * please re-analyze the file structure. So you are better to call
246          * analyzeFile() instead of this method.
247          *
248          * @return      void
249          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
250          */
251         public function initCountersGapsArray () {
252                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
253         }
254
255         /**
256          * Getter for header size
257          *
258          * @return      $totalEntries   Size of file header
259          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
260          */
261         public final function getHeaderSize () {
262                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
263         }
264
265         /**
266          * Setter for header size
267          *
268          * @param       $headerSize             Size of file header
269          * @return      void
270          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
271          */
272         public final function setHeaderSize (int $headerSize) {
273                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
274         }
275
276         /**
277          * Getter for header array
278          *
279          * @return      $totalEntries   Size of file header
280          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
281          */
282         public final function getHeader () {
283                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
284         }
285
286         /**
287          * Setter for header
288          *
289          * @param       $header         Array for a file header
290          * @return      void
291          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
292          */
293         public final function setHeader (array $header) {
294                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
295         }
296
297         /**
298          * Updates seekPosition attribute from file to avoid to much access on file.
299          *
300          * @return      void
301          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
302          */
303         public function updateSeekPosition () {
304                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
305         }
306
307         /**
308          * Getter for total entries
309          *
310          * @return      $totalEntries   Total entries in this file
311          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
312          */
313         public final function getCounter () {
314                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
315         }
316
317         /**
318          * "Getter" for file size
319          *
320          * @return      $fileSize       Size of currently loaded file
321          */
322         public function getFileSize () {
323                 // Call iterator's method
324                 return $this->getIteratorInstance()->getFileSize();
325         }
326
327         /**
328          * Writes data at given position
329          *
330          * @param       $seekPosition   Seek position
331          * @param       $data                   Data to be written
332          * @param       $flushHeader    Whether to flush the header (default: flush)
333          * @return      void
334          * @throws      UnsupportedOperationException   This method is not (and maybe never will be) supported
335          */
336         public function writeData (int $seekPosition, string $data, bool $flushHeader = true) {
337                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('seekPosition=%s,data[]=%s,flushHeader=%d - CALLED!', $seekPosition, gettype($data), intval($flushHeader)));
338                 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
339         }
340
341         /**
342          * Writes given value to the file and returns a hash and gap position for it
343          *
344          * @param       $groupId        Group identifier
345          * @param       $value          Value to be added to the stack
346          * @return      $data           Hash and gap position
347          * @throws      UnsupportedOperationException   If this method is called
348          */
349         public function writeValueToFile (string $groupId, $value) {
350                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('groupId=%s,value[%s]=%s - CALLED!', $groupId, gettype($value), print_r($value, true)));
351                 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
352         }
353
354         /**
355          * Writes given raw data to the file and returns a gap position and length
356          *
357          * @param       $groupId        Group identifier
358          * @param       $hash           Hash from encoded value
359          * @param       $encoded        Encoded value to be written to the file
360          * @return      $data           Gap position and length of the raw data
361          */
362         public function writeDataToFreeGap (string $groupId, string $hash, string $encoded) {
363                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('groupId=%s,hash=%s,encoded()=%d - CALLED!', $groupId, $hash, strlen($encoded)));
364                 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
365         }
366
367 }