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