All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.phoenix.util.DeferredStringBuilder Maven / Gradle / Ivy

/*
 * 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.phoenix.util;

/**
 * This utility class was partially copied from Salesforce's internationalization utility library
 * (com.salesforce.i18n:i18n-util:1.0.4), which was released under the 3-clause BSD License.
 * The i18n-util library is not maintained anymore, and it was using vulnerable dependencies.
 * For more info, see: https://issues.apache.org/jira/browse/PHOENIX-6818
 *
 * This class implements a StringBuilder that is incrementally copied from a source String.
 * Actual creation the new buffer is deferred until a character differs from a character at
 * the same position in the source String.  This class is useful for reducing garbage creation
 * when doing operations like escaping a String, when most Strings are not expected to contain
 * any escapable characters.  In that case, no additional memory is used (as the original
 * String is not actually copied).
 */
public final class DeferredStringBuilder implements Appendable, CharSequence {

    private StringBuilder buf;
    private int pos;
    private final CharSequence source;

    public DeferredStringBuilder(CharSequence source) {
        if (source == null) {
            this.buf = new StringBuilder(16);
        }
        this.source = source;
    }

    public DeferredStringBuilder append(char c) {
        if (this.buf == null) {
            if (this.pos < this.source.length() && c == this.source.charAt(this.pos)) {
                // characters match - just move ahead
                ++this.pos;
            } else {
                // doh - character mismatch - now we need to allocate a real StringBuilder
                this.buf = new StringBuilder(this.source.length() + 16);
                this.buf.append(this.source.subSequence(0, this.pos));
                this.buf.append(c);
            }
        } else {
            // we've already got the buf - just add this character
            this.buf.append(c);
        }
        return this;
    }

    public DeferredStringBuilder append(CharSequence csq) {
        if (csq == null) {
            return this;
        }
        return append(csq, 0, csq.length());
    }

    public DeferredStringBuilder append(CharSequence csq, int start, int end) {
        if (csq != null) {
            if (buf == null) {
                int chars = end - start;
                // For small strings or overflow, do it char by char.
                if (chars < 10 || (this.pos + chars > this.source.length())) {
                    for (int i = start; i < end; ++i) {
                        append(csq.charAt(i));
                    }
                } else {
                    CharSequence subSeq = csq.subSequence(start, end);
                    //String.equals seems to get optimized a lot quicker than the
                    // chartA + length + loop method. I don't think this will matter at all,
                    // but between this and OptimizedURLEncoder, this made these classes
                    // disappear from my profiler
                    if (this.source.subSequence(this.pos, this.pos + chars).equals(subSeq)) {
                        this.pos += chars;
                    } else {
                        this.buf = new StringBuilder(this.source.length() + 16);
                        this.buf.append(this.source.subSequence(0, this.pos));
                        this.buf.append(subSeq);
                    }
                }
            } else {
                // We know it's different, so just append the whole string.
                buf.append(csq, start, end);
            }
        }
        return this;
    }

    public char charAt(int index) {
        if (this.buf != null) {
            return this.buf.charAt(index);
        } else if (index < pos) {
            return this.source.charAt(index);
        } else {
            throw new StringIndexOutOfBoundsException(index);
        }
    }

    public CharSequence subSequence(int start, int end) {
        if (this.buf != null) {
            return this.buf.subSequence(start, end);
        } else if (end <= pos) {
            return this.source.subSequence(start, end);
        } else {
            throw new StringIndexOutOfBoundsException(end);
        }
    }

    @Override
    public String toString() {
        if (this.buf != null) {
            return this.buf.toString();
        }
        if (this.pos == this.source.length()) {
            return this.source.toString();
        }
        return this.source.subSequence(0, this.pos).toString();
    }

    public int length() {
        return this.buf != null ? this.buf.length() : this.pos;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy