public function next($length = INF)
 {
     if ($this->operations->has($this->index)) {
         $nextOperation = $this->operations[$this->index];
         $offset = $this->offset;
         $operationLength = $this->calculateLength($nextOperation);
         if ($length >= $operationLength - $offset) {
             $length = $operationLength - $offset;
             $this->index += 1;
             $this->offset = 0;
         } else {
             $this->offset += $length;
         }
         if ($nextOperation->getType() === OperationInterface::TYPE_DELETE) {
             return new DeleteOperation($length);
         } else {
             if ($nextOperation->getType() === OperationInterface::TYPE_RETAIN) {
                 return new RetainOperation($length, $nextOperation->getAttributes());
             } elseif (is_string($nextOperation->getValue())) {
                 return new InsertOperation(mb_substr($nextOperation->getValue(), $offset, $length), $nextOperation->getAttributes());
             }
             return new InsertOperation($nextOperation->getValue(), $nextOperation->getAttributes());
         }
     }
     return new RetainOperation(INF);
 }
 public function chop()
 {
     $lastOperation = ($index = count($this->operations)) > 0 ? $this->operations[$index - 1] : null;
     if ($lastOperation instanceof OperationInterface && $lastOperation->getType() === OperationInterface::TYPE_RETAIN && !$lastOperation->hasAttributes()) {
         $this->operations->pop();
     }
     return $this;
 }