1<?php 2 3/** 4 * Hoa 5 * 6 * 7 * @license 8 * 9 * New BSD License 10 * 11 * Copyright © 2007-2017, Hoa community. All rights reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions are met: 15 * * Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * * Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * * Neither the name of the Hoa nor the names of its contributors may be 21 * used to endorse or promote products derived from this software without 22 * specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37namespace Hoa\Iterator; 38 39/** 40 * Class \Hoa\Iterator\Buffer. 41 * 42 * Buffer iterator: Can go backward up to a certain limit, and forward. 43 * 44 * @copyright Copyright © 2007-2017 Hoa community 45 * @license New BSD License 46 */ 47class Buffer extends IteratorIterator implements Outer 48{ 49 /** 50 * Buffer key index. 51 * 52 * @const int 53 */ 54 const BUFFER_KEY = 0; 55 56 /** 57 * Buffer value index. 58 * 59 * @const int 60 */ 61 const BUFFER_VALUE = 1; 62 63 /** 64 * Current iterator. 65 * 66 * @var \Iterator 67 */ 68 protected $_iterator = null; 69 70 /** 71 * Buffer. 72 * 73 * @var \SplDoublyLinkedList 74 */ 75 protected $_buffer = null; 76 77 /** 78 * Maximum buffer size. 79 * 80 * @var int 81 */ 82 protected $_bufferSize = 1; 83 84 85 86 /** 87 * Construct. 88 * 89 * @param \Iterator $iterator Iterator. 90 * @param int $bufferSize Buffer size. 91 */ 92 public function __construct(\Iterator $iterator, $bufferSize) 93 { 94 $this->_iterator = $iterator; 95 $this->_bufferSize = max($bufferSize, 1); 96 $this->_buffer = new \SplDoublyLinkedList(); 97 98 return; 99 } 100 101 /** 102 * Get inner iterator. 103 * 104 * @return \Iterator 105 */ 106 public function getInnerIterator() 107 { 108 return $this->_iterator; 109 } 110 111 /** 112 * Get buffer. 113 * 114 * @return \SplDoublyLinkedList 115 */ 116 protected function getBuffer() 117 { 118 return $this->_buffer; 119 } 120 121 /** 122 * Get buffer size. 123 * 124 * @return int 125 */ 126 public function getBufferSize() 127 { 128 return $this->_bufferSize; 129 } 130 131 /** 132 * Return the current element. 133 * 134 * @return mixed 135 */ 136 public function current() 137 { 138 return $this->getBuffer()->current()[self::BUFFER_VALUE]; 139 } 140 141 /** 142 * Return the key of the current element. 143 * 144 * @return mixed 145 */ 146 public function key() 147 { 148 return $this->getBuffer()->current()[self::BUFFER_KEY]; 149 } 150 151 /** 152 * Move forward to next element. 153 * 154 * @return void 155 */ 156 public function next() 157 { 158 $innerIterator = $this->getInnerIterator(); 159 $buffer = $this->getBuffer(); 160 161 $buffer->next(); 162 163 // End of the buffer, need a new value. 164 if (false === $buffer->valid()) { 165 for ( 166 $bufferSize = count($buffer), 167 $maximumBufferSize = $this->getBufferSize(); 168 $bufferSize >= $maximumBufferSize; 169 --$bufferSize 170 ) { 171 $buffer->shift(); 172 } 173 174 $innerIterator->next(); 175 176 $buffer->push([ 177 self::BUFFER_KEY => $innerIterator->key(), 178 self::BUFFER_VALUE => $innerIterator->current() 179 ]); 180 181 // Seek to the end of the buffer. 182 $buffer->setIteratorMode($buffer::IT_MODE_LIFO | $buffer::IT_MODE_KEEP); 183 $buffer->rewind(); 184 $buffer->setIteratorMode($buffer::IT_MODE_FIFO | $buffer::IT_MODE_KEEP); 185 } 186 187 return; 188 } 189 190 /** 191 * Move backward to previous element. 192 * 193 * @return void 194 */ 195 public function previous() 196 { 197 $this->getBuffer()->prev(); 198 199 return; 200 } 201 202 /** 203 * Rewind the iterator to the first element. 204 * 205 * @return void 206 */ 207 public function rewind() 208 { 209 $innerIterator = $this->getInnerIterator(); 210 $buffer = $this->getBuffer(); 211 212 $innerIterator->rewind(); 213 214 if (true === $buffer->isEmpty()) { 215 $buffer->push([ 216 self::BUFFER_KEY => $innerIterator->key(), 217 self::BUFFER_VALUE => $innerIterator->current() 218 ]); 219 } 220 221 $buffer->rewind(); 222 223 return; 224 } 225 226 /** 227 * Check if current position is valid. 228 * 229 * @return bool 230 */ 231 public function valid() 232 { 233 return 234 $this->getBuffer()->valid() && 235 $this->getInnerIterator()->valid(); 236 } 237} 238