/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.lucene.facet.taxonomy.writercache;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

Similar to StringBuilder, but with a more efficient growing strategy. This class uses char array blocks to grow.
@lucene.experimental
/** * Similar to {@link StringBuilder}, but with a more efficient growing strategy. * This class uses char array blocks to grow. * * @lucene.experimental */
class CharBlockArray implements Appendable, Serializable, CharSequence { private static final long serialVersionUID = 1L; private final static int DefaultBlockSize = 32 * 1024; // 32 KB default size final static class Block implements Serializable, Cloneable { private static final long serialVersionUID = 1L; final char[] chars; int length; Block(int size) { this.chars = new char[size]; this.length = 0; } } List<Block> blocks; Block current; int blockSize; int length; CharBlockArray() { this(DefaultBlockSize); } CharBlockArray(int blockSize) { this.blocks = new ArrayList<>(); this.blockSize = blockSize; addBlock(); } private void addBlock() { if (blockSize * (long) (blocks.size() + 1) > Integer.MAX_VALUE) { throw new IllegalStateException("cannot store more than 2 GB in CharBlockArray"); } this.current = new Block(this.blockSize); this.blocks.add(this.current); } int blockIndex(int index) { return index / blockSize; } int indexInBlock(int index) { return index % blockSize; } @Override public CharBlockArray append(CharSequence chars) { return append(chars, 0, chars.length()); } @Override public CharBlockArray append(char c) { if (this.current.length == this.blockSize) { addBlock(); } this.current.chars[this.current.length++] = c; this.length++; return this; } @Override public CharBlockArray append(CharSequence chars, int start, int length) { int end = start + length; for (int i = start; i < end; i++) { append(chars.charAt(i)); } return this; } public CharBlockArray append(char[] chars, int start, int length) { int offset = start; int remain = length; while (remain > 0) { if (this.current.length == this.blockSize) { addBlock(); } int toCopy = remain; int remainingInBlock = this.blockSize - this.current.length; if (remainingInBlock < toCopy) { toCopy = remainingInBlock; } System.arraycopy(chars, offset, this.current.chars, this.current.length, toCopy); offset += toCopy; remain -= toCopy; this.current.length += toCopy; } this.length += length; return this; } public CharBlockArray append(String s) { int remain = s.length(); int offset = 0; while (remain > 0) { if (this.current.length == this.blockSize) { addBlock(); } int toCopy = remain; int remainingInBlock = this.blockSize - this.current.length; if (remainingInBlock < toCopy) { toCopy = remainingInBlock; } s.getChars(offset, offset + toCopy, this.current.chars, this.current.length); offset += toCopy; remain -= toCopy; this.current.length += toCopy; } this.length += s.length(); return this; } @Override public char charAt(int index) { Block b = blocks.get(blockIndex(index)); return b.chars[indexInBlock(index)]; } @Override public int length() { return this.length; } @Override public CharSequence subSequence(int start, int end) { int remaining = end - start; StringBuilder sb = new StringBuilder(remaining); int blockIdx = blockIndex(start); int indexInBlock = indexInBlock(start); while (remaining > 0) { Block b = blocks.get(blockIdx++); int numToAppend = Math.min(remaining, b.length - indexInBlock); sb.append(b.chars, indexInBlock, numToAppend); remaining -= numToAppend; indexInBlock = 0; // 2nd+ iterations read from start of the block } return sb.toString(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (Block b : blocks) { sb.append(b.chars, 0, b.length); } return sb.toString(); } void flush(OutputStream out) throws IOException { ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(out); oos.writeObject(this); oos.flush(); } finally { if (oos != null) { oos.close(); } } } public static CharBlockArray open(InputStream in) throws IOException, ClassNotFoundException { ObjectInputStream ois = null; try { ois = new ObjectInputStream(in); CharBlockArray a = (CharBlockArray) ois.readObject(); return a; } finally { if (ois != null) { ois.close(); } } } }