@Override
  public void write(byte arr[], int off, int len) throws IOException {

    for (; ; ) {
      int space = buf.length - pos;

      if (len <= space) {
        System.arraycopy(arr, off, buf, pos, len);
        pos += len;
        return;
      } else if (len > buf.length) {
        if (pos > 0) {
          flush(buf, 0, pos); // flush
          written += pos;
          pos = 0;
        }
        // don't buffer, just write to sink
        flush(arr, off, len);
        written += len;
        return;
      }

      // buffer is too big to fit in the free space, but
      // not big enough to warrant writing on its own.
      // write whatever we can fit, then flush and iterate.

      System.arraycopy(arr, off, buf, pos, space);
      written +=
          buf.length; // important to do this first, since buf.length can change after a flush!
      flush(buf, 0, buf.length);
      pos = 0;
      off += space;
      len -= space;
    }
  }
 /** Only flushes the buffer of the FastOutputStream, not that of the underlying stream. */
 public void flushBuffer() throws IOException {
   if (pos > 0) {
     written += pos;
     flush(buf, 0, pos);
     pos = 0;
   }
 }
  protected void close() {
    try {
      if (debug) {
        log.debug("Closing tlog" + this);
      }

      synchronized (this) {
        fos.flush();
        fos.close();
      }

      if (deleteOnClose) {
        try {
          Files.deleteIfExists(tlogFile.toPath());
        } catch (IOException e) {
          // TODO: should this class care if a file couldnt be deleted?
          // this just emulates previous behavior, where only SecurityException would be handled.
        }
      }
    } catch (IOException e) {
      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
    } finally {
      assert ObjectReleaseTracker.release(this);
    }
  }
  public long writeCommit(CommitUpdateCommand cmd, int flags) {
    LogCodec codec = new LogCodec(resolver);
    synchronized (this) {
      try {
        long pos = fos.size(); // if we had flushed, this should be equal to channel.position()

        if (pos == 0) {
          writeLogHeader(codec);
          pos = fos.size();
        }
        codec.init(fos);
        codec.writeTag(JavaBinCodec.ARR, 3);
        codec.writeInt(UpdateLog.COMMIT | flags); // should just take one byte
        codec.writeLong(cmd.getVersion());
        codec.writeStr(END_MESSAGE); // ensure these bytes are (almost) last in the file

        endRecord(pos);

        fos.flush(); // flush since this will be the last record in a log fill
        assert fos.size() == channel.size();

        return pos;
      } catch (IOException e) {
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
      }
    }
  }
 public void write(byte b) throws IOException {
   if (pos >= buf.length) {
     written += pos;
     flush(buf, 0, buf.length);
     pos = 0;
   }
   buf[pos++] = b;
 }
 // This could mess with any readers or reverse readers that are open, or anything that might try
 // to do a log lookup.
 // This should only be used to roll back buffered updates, not actually applied updates.
 public void rollback(long pos) throws IOException {
   synchronized (this) {
     assert snapshot_size == pos;
     fos.flush();
     raf.setLength(pos);
     fos.setWritten(pos);
     assert fos.size() == pos;
     numRecords = snapshot_numRecords;
   }
 }
  public boolean endsWithCommit() throws IOException {
    long size;
    synchronized (this) {
      fos.flush();
      size = fos.size();
    }

    // the end of the file should have the end message (added during a commit) plus a 4 byte size
    byte[] buf = new byte[END_MESSAGE.length()];
    long pos = size - END_MESSAGE.length() - 4;
    if (pos < 0) return false;
    ChannelFastInputStream is = new ChannelFastInputStream(channel, pos);
    is.read(buf);
    for (int i = 0; i < buf.length; i++) {
      if (buf[i] != END_MESSAGE.charAt(i)) return false;
    }
    return true;
  }