diff --git a/classes/logstreamdisk.class.php b/classes/logstreamdisk.class.php index d0c1f34..c70d6b8 100644 --- a/classes/logstreamdisk.class.php +++ b/classes/logstreamdisk.class.php @@ -18,7 +18,18 @@ if ( !defined('IN_PHPLOGCON') ) */ class LogStreamDisk extends LogStream { private $_currentOffset = -1; + private $_currentStartPos = -1; private $_fp = null; + private $_bEOF = false; + + const _BUFFER_LENGHT = 4096; + private $_buffer = false; + private $_buffer_lenght = -1; + private $_p_buffer = -1; + + // cache for backwards reading + private $_cache_lines = null; + private $_p_cache_lines = -1; // Constructor public function LogStreamDisk($streamConfigObj) { @@ -29,16 +40,24 @@ class LogStreamDisk extends LogStream { * Open the file with read access. * * @param streamConfigObj object in: It has to be a LogSteamDiskConfig object. + * @param bStartAtEOF bool in: If set to true the file pointer is set at EOF. * @return integer Error stat */ - public function Open($arrProperties) { + public function Open($arrProperties, $bStartAtEOF = false) { if(!file_exists($this->_logStreamConfigObj->FileName)) { return ERROR_FILE_NOT_FOUND; } - - $this->_fp = fopen($this->_logStreamConfigObj->FileName, 'r'); - $this->_currentOffset = 0; + + $this->_fp = fopen($this->_logStreamConfigObj->FileName, 'r'); + if ($bStartAtEOF) { + fseek($this->_fp, 0, SEEK_END); + } + $this->_currentOffset = ftell($this->_fp); + $this->_currentStartPos = $this->_currentOffset; $this->_arrProperties = $arrProperties; + + // init read + $this->ReadNextBlock(); return SUCCESS; } @@ -55,7 +74,40 @@ class LogStreamDisk extends LogStream { return SUCCESS; } - + public function ReadNextBlock() { + //echo 'in ReadNextBlock
'; + $this->_buffer = fread($this->_fp, self::_BUFFER_LENGHT); + $this->_buffer_lenght = strlen($this->_buffer); + $this->_p_buffer = 0; + + if ($this->_buffer == false) + return ERROR_FILE_BOF; + + return SUCCESS; + } + + /** + * Read the data from a specific uID which means in this + * case from a given offset of the file. + * + * @param uID integer in/out: unique id of the data row + * @param logLine string out: data row + * @return integer Error state + * @see ReadNext() + */ + public function Read($uID, &$logLine) { + fseek($this->_fp, $uI); + + // with Read we can only read forwards. + // so we have to remember the current read + // direction + $tmp = $this->_readDirection; + $iRet = $this->ReadNext($uID, $logLine); + $this->_readDirection = $tmp; + + return $iRet; + } + /** * Read the next line from the file depending on the current * read direction. @@ -70,13 +122,51 @@ class LogStreamDisk extends LogStream { * @see ReadNext */ public function ReadNext(&$uID, &$logLine) { + if ($this->_readDirection == EnumReadDirection::Forward) { + return $this->ReadNextForwards($uID, $logLine); + } - $uID = $this->_currentOffset; + return $this->ReadNextBackwards($uID, $logLine); + } + private function ReadNextForwards(&$uID, &$logLine) { + if ($this->bEOF) { + return ERROR_FILE_EOF; + } + + if (($this->_p_buffer == $this->_buffer_lenght) && ($this->ReadNextBlock() != SUCCESS)) { + return ERROR_UNDEFINED; + } + + $line = ''; + do { + + $pos = -1; + if (($pos = strpos($this->_buffer, "\n", $this->_p_buffer)) !== false) { + $logLine = $line . substr($this->_buffer, $this->_p_buffer, $pos - $this->_p_buffer); + $this->_currentOffset = $pos - $this->_p_buffer + 1; + $this->_p_buffer = $pos + 1; + $uID = $this->_currentStartPos; + $this->_currentStartPos = $this->_currentOffset; + return SUCCESS; + } + + $line .= substr($this->_buffer, $this->_p_buffer, $this->_buffer_lenght - $this->_p_buffer); + $this->_currentOffset += $this->_buffer_lenght - $this->_p_buffer; + } while ($this->ReadNextBlock() == SUCCESS); + + return ERROR_UNDEFINED; + } + +/* + private function ReadNextForwards(&$uID, &$logLine) { + if (feof($this->_fp)) { return ERROR_FILE_EOF; } + $uID = ftell($this->_fp); + $logLine = fgets($this->_fp); if ($logLine === false) { // ToDo: error occurs, or EOF @@ -85,30 +175,71 @@ class LogStreamDisk extends LogStream { $this->_currentOffset = $this->_currentOffset + sizeof($logLine); - return 0; + return SUCCESS; + } +*/ + + private function ReadNextBackwards(&$uID, &$logLine) { + if ($this->_p_cache_lines < 0) { + if (($iRet = $this->InitCacheLines()) > 0) { // error or BOF? + return $iRet; + } + } + + // at this stage we can read from cache + $uID = $this->_cache_lines[$this->_p_cache_lines][0]; + $logLine = $this->_cache_lines[$this->_p_cache_lines][1]; + $this->_p_cache_lines--; + + return SUCCESS; } - /** - * Read the data from a specific uID which means in this - * case from a given offset of the file. - * - * @param uID integer in/out: unique id of the data row - * @param logLine string out: data row - * @return integer Error state - * @see ReadNext() - */ - public function Read($uID, &$logLine) { - $this->_currentOffset = $uID; - fseek($fp, $this->_currentOffset); + private function ClearCacheLines() { + unset($this->_cache_lines); + $this->_p_cache_lines = -1; + } - // with Read we can only read forwards. - // so we have to remember the current read - // direction - $tmp = $this->_readDirection; - $iRet = $this->ReadNext($uID, $logLine); - $this->_readDirection = $tmp; + private function InitCacheLines() { + $orig_offset = ftell($this->_fp); + if ($this->_readDirection == EnumReadDirection::Backward) { + // if we have already used the cache take the last positon + // as offset and then clear the cache + if (isset($this->_cache_lines[0][0])) { + $orig_offset = $this->_cache_lines[0][0]; + $this->ClearCacheLines(); - return $iRet; + // check if it is the first line so we have BOF + if ($orig_offset == 0) { + return ERROR_FILE_BOF; + } + } + } + + $offset = $orig_offset - 4096; + if ($offset < 0) { + $offset = 0; + } + + fseek($this->_fp, $offset); + + if ($offset != 0) { + // we do not know if we are on the beginning of a line + // therefore we simply skip the first line + fgets($this->_fp); + } + + while (($offset = ftell($this->_fp)) < $orig_offset) { + $this->_p_cache_lines++; + $this->_cache_lines[$this->_p_cache_lines][0] = $offset; + $this->_cache_lines[$this->_p_cache_lines][1] = fgets($this->_fp); + if ($this->_cache_lines[$this->_p_cache_line][1] === false) { + // probably EOF or an error + unset($this->_cache_lines[$this->_p_cache_line]); + $this->_p_cache_lines--; + break; + } + } + return SUCCESS; } /** @@ -118,17 +249,25 @@ class LogStreamDisk extends LogStream { * @return integer Error state */ public function SetFilter($filter) { - return 0; + return SUCCESS; } /** * Set the direction the stream should read data. * + * + * * @param enumReadDirectionfilter EnumReadDirection in: The new direction. * @return integer Error state */ public function SetReadDirection($enumReadDirection) { - return 0; + + // only if the read direction change we have do do anything + if ($this->_readDirection == $enumReadDirection) + return SUCCESS; + + $this->_readDirection = $enumReadDirection; + return SUCCESS; } } diff --git a/include/constants_errors.php b/include/constants_errors.php index 1f9f933..75f907b 100644 --- a/include/constants_errors.php +++ b/include/constants_errors.php @@ -2,5 +2,7 @@ define('SUCCESS ', 0); define('ERROR_FILE_NOT_FOUND', 1); define('ERROR_FILE_CANT_CLOSE', 2); -define('ERROR_FILE_EOF', 2); +define('ERROR_FILE_EOF', 3); +define('ERROR_FILE_BOF', 4); +define('ERROR_UNDEFINED', 5); ?> \ No newline at end of file