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