Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  package divconq.json3.util;
  
  import java.util.Arrays;
  
TextBuffer is a class similar to java.lang.StringBuffer, with following differences:
  • TextBuffer uses segments character arrays, to avoid having to do additional array copies when array is not big enough. This means that only reallocating that is necessary is done only once: if and when caller wants to access contents in a linear array (char[], String).
  • TextBuffer can also be initialized in "shared mode", in which it will just act as a wrapper to a single char array managed by another object (like parser that owns it)
  • TextBuffer is not synchronized.
 
 public final class TextBuffer
 {
     final static char[] NO_CHARS = new char[0];

    
Let's start with sizable but not huge buffer, will grow as necessary
 
     final static int MIN_SEGMENT_LEN = 1000;
    
    
Let's limit maximum segment length to something sensible like 256k
 
     final static int MAX_SEGMENT_LEN = 0x40000;
     
     /*
     /**********************************************************
     /* Configuration:
     /**********************************************************
      */
 
     private final BufferRecycler _allocator;
 
     /*
     /**********************************************************
     /* Shared input buffers
     /**********************************************************
      */

    
Shared input buffer; stored here in case some input can be returned as is, without being copied to collector's own buffers. Note that this is read-only for this Object.
 
     private char[] _inputBuffer;

    
Character offset of first char in input buffer; -1 to indicate that input buffer currently does not contain any useful char data
 
     private int _inputStart;
 
     private int _inputLen;
 
     /*
     /**********************************************************
     /* Aggregation segments (when not using input buf)
     /**********************************************************
      */

    
List of segments prior to currently active segment.
 
     private ArrayList<char[]> _segments;

    
Flag that indicates whether _seqments is non-empty
 
     private boolean _hasSegments = false;
 
     // // // Currently used segment; not (yet) contained in _seqments
 
    
Amount of characters in segments in _segments
 
     private int _segmentSize;
 
     private char[] _currentSegment;

    
Number of characters in currently active (last) segment
 
     private int _currentSize;
    /*
    /**********************************************************
    /* Caching of results
    /**********************************************************
     */

    
String that will be constructed when the whole contents are needed; will be temporarily stored in case asked for again.
    private String _resultString;
    private char[] _resultArray;
    /*
    /**********************************************************
    /* Life-cycle
    /**********************************************************
     */
    public TextBuffer(BufferRecycler allocator) {
         = allocator;
    }
    /*
     * Method called to indicate that the underlying buffers should now
     * be recycled if they haven't yet been recycled. Although caller
     * can still use this text buffer, it is not advisable to call this
     * method if that is likely, since next time a buffer is needed,
     * buffers need to reallocated.
     * Note: calling this method automatically also clears contents
     * of the buffer.
     */
    public void releaseBuffers()
    {
        if ( == null) {
            resetWithEmpty();
        } else {
            if ( != null) {
                // First, let's get rid of all but the largest char array
                resetWithEmpty();
                // And then return that array
                char[] buf = ;
                 = null;
                .releaseCharBuffer(.buf);
            }
        }
    }
    /*
     * Method called to clear out any content text buffer may have, and
     * initializes buffer to use non-shared data.
     */
    public void resetWithEmpty()
    {
         = -1; // indicates shared buffer not used
         = 0;
         = 0;
         = null;
         = null;
         = null;
        // And then reset internal input buffers, if necessary:
        if () {
            clearSegments();
        }
    }
    /*
     * Method called to initialize the buffer with a shared copy of data;
     * this means that buffer will just have pointers to actual data. It
     * also means that if anything is to be appended to the buffer, it
     * will first have to unshare it (make a local copy).
     */
    public void resetWithShared(char[] bufint startint len)
    {
        // First, let's clear intermediate values, if any:
         = null;
         = null;
        // Then let's mark things we need about input buffer
         = buf;
         = start;
         = len;
        // And then reset internal input buffers, if necessary:
        if () {
            clearSegments();
        }
    }
    public void resetWithCopy(char[] bufint startint len)
    {
         = null;
         = -1; // indicates shared buffer not used
         = 0;
         = null;
         = null;
        // And then reset internal input buffers, if necessary:
        if () {
            clearSegments();
        } else if ( == null) {
             = buf(len);
        }
         =  = 0;
        append(bufstartlen);
    }
    public void resetWithString(String value)
    {
         = null;
         = -1;
         = 0;
         = value;
         = null;
        if () {
            clearSegments();
        }
         = 0;
        
    }
    
    /*
     * Helper method used to find a buffer to use, ideally one
     * recycled earlier.
     */
    private char[] buf(int needed)
    {
        if ( != null) {
            return .allocCharBuffer(.needed);
        }
        return new char[Math.max(needed)];
    }
    private void clearSegments()
    {
         = false;
        /* Let's start using _last_ segment from list; for one, it's
         * the biggest one, and it's also most likely to be cached
         */
        /* 28-Aug-2009, tatu: Actually, the current segment should
         *   be the biggest one, already
         */
        //_currentSegment = _segments.get(_segments.size() - 1);
        .clear();
         =  = 0;
    }
    /*
    /**********************************************************
    /* Accessors for implementing public interface
    /**********************************************************
     */
    /*
     * @return Number of characters currently stored by this collector
     */
    public int size() {
        if ( >= 0) { // shared copy from input buf
            return ;
        }
        if ( != null) {
            return .;
        }
        if ( != null) {
            return .length();
        }
        // local segmented buffers
        return  + ;
    }
    public int getTextOffset() {
        /* Only shared input buffer can have non-zero offset; buffer
         * segments start at 0, and if we have to create a combo buffer,
         * that too will start from beginning of the buffer
         */
        return ( >= 0) ?  : 0;
    }
    /*
     * Method that can be used to check whether textual contents can
     * be efficiently accessed using {@link #getTextBuffer}.
     */
    public boolean hasTextAsCharacters()
    {
        // if we have array in some form, sure
        if ( >= 0 ||  != null)  return true;
        // not if we have String as value
        if ( != nullreturn false;
        return true;
    }
    
    public char[] getTextBuffer()
    {
        // Are we just using shared input buffer?
        if ( >= 0) return ;
        if ( != null)  return ;
        if ( != null) {
            return ( = .toCharArray());
        }
        // Nope; but does it fit in just one segment?
        if (!)  return ;
        // Nope, need to have/create a non-segmented array and return it
        return contentsAsArray();
    }
    /*
    /**********************************************************
    /* Other accessors:
    /**********************************************************
     */
    public String contentsAsString()
    {
        if ( == null) {
            // Has array been requested? Can make a shortcut, if so:
            if ( != null) {
                 = new String();
            } else {
                // Do we use shared array?
                if ( >= 0) {
                    if ( < 1) {
                        return ( = "");
                    }
                     = new String();
                } else { // nope... need to copy
                    // But first, let's see if we have just one buffer
                    int segLen = ;
                    int currLen = ;
                    
                    if (segLen == 0) { // yup
                         = (currLen == 0) ? "" : new String(, 0, currLen);
                    } else { // no, need to combine
                        StringBuilder sb = new StringBuilder(segLen + currLen);
                        // First stored segments
                        if ( != null) {
                            for (int i = 0, len = .size(); i < len; ++i) {
                                char[] curr = .get(i);
                                sb.append(curr, 0, curr.length);
                            }
                        }
                        // And finally, current segment:
                        sb.append(, 0, );
                         = sb.toString();
                    }
                }
            }
        }
        return ;
    }
 
    public char[] contentsAsArray() {
        char[] result = ;
        if (result == null) {
             = result = resultArray();
        }
        return result;
    }
    /*
     * Convenience method for converting contents of the buffer
     * into a {@link BigDecimal}.
     */
    {
        // Already got a pre-cut array?
        if ( != null) {
            return NumberInput.parseBigDecimal();
        }
        // Or a shared buffer?
        if (( >= 0) && ( != null)) {
            return NumberInput.parseBigDecimal();
        }
        // Or if not, just a single buffer (the usual case)
        if (( == 0) && ( != null)) {
            return NumberInput.parseBigDecimal(, 0, );
        }
        // If not, let's just get it aggregated...
        return NumberInput.parseBigDecimal(contentsAsArray());
    }
    /*
     * Convenience method for converting contents of the buffer
     * into a Double value.
     */
    public double contentsAsDouble() throws NumberFormatException {
        return NumberInput.parseDouble(contentsAsString());
    }
    /*
    /**********************************************************
    /* Public mutators:
    /**********************************************************
     */
    /*
     * Method called to make sure that buffer is not using shared input
     * buffer; if it is, it will copy such contents to private buffer.
     */
    public void ensureNotShared() {
        if ( >= 0) {
            unshare(16);
        }
    }
    public void append(char c) {
        // Using shared buffer so far?
        if ( >= 0) {
            unshare(16);
        }
         = null;
         = null;
        // Room in current segment?
        char[] curr = ;
        if ( >= curr.length) {
            expand(1);
            curr = ;
        }
        curr[++] = c;
    }
    public void append(char[] cint startint len)
    {
        // Can't append to shared buf (sanity check)
        if ( >= 0) {
            unshare(len);
        }
         = null;
         = null;
        // Room in current segment?
        char[] curr = ;
        int max = curr.length - ;
            
        if (max >= len) {
            System.arraycopy(cstartcurrlen);
             += len;
            return;
        }
        // No room for all, need to copy part(s):
        if (max > 0) {
            System.arraycopy(cstartcurrmax);
            start += max;
            len -= max;
        }
        /* And then allocate new segment; we are guaranteed to now
         * have enough room in segment.
         */
        // Except, as per [Issue-24], not for HUGE appends... so:
        do {
            expand(len);
            int amount = Math.min(.len);
            System.arraycopy(cstart, 0, amount);
             += amount;
            start += amount;
            len -= amount;
        } while (len > 0);
    }
    public void append(String strint offsetint len)
    {
        // Can't append to shared buf (sanity check)
        if ( >= 0) {
            unshare(len);
        }
         = null;
         = null;
        // Room in current segment?
        char[] curr = ;
        int max = curr.length - ;
        if (max >= len) {
            str.getChars(offsetoffset+lencurr);
             += len;
            return;
        }
        // No room for all, need to copy part(s):
        if (max > 0) {
            str.getChars(offsetoffset+maxcurr);
            len -= max;
            offset += max;
        }
        /* And then allocate new segment; we are guaranteed to now
         * have enough room in segment.
         */
        // Except, as per [Issue-24], not for HUGE appends... so:
        do {
            expand(len);
            int amount = Math.min(.len);
            str.getChars(offsetoffset+amount, 0);
             += amount;
            offset += amount;
            len -= amount;
        } while (len > 0);
    }
    /*
    /**********************************************************
    /* Raw access, for high-performance use:
    /**********************************************************
     */
    public char[] getCurrentSegment()
    {
        /* Since the intention of the caller is to directly add stuff into
         * buffers, we should NOT have anything in shared buffer... ie. may
         * need to unshare contents.
         */
        if ( >= 0) {
            unshare(1);
        } else {
            char[] curr = ;
            if (curr == null) {
                 = buf(0);
            } else if ( >= curr.length) {
                // Plus, we better have room for at least one more char
                expand(1);
            }
        }
        return ;
    }
    public char[] emptyAndGetCurrentSegment()
    {
        // inlined 'resetWithEmpty()'
         = -1; // indicates shared buffer not used
         = 0;
         = 0;
         = null;
         = null;
         = null;
        // And then reset internal input buffers, if necessary:
        if () {
            clearSegments();
        }
        char[] curr = ;
        if (curr == null) {
             = curr = buf(0);
        }
        return curr;
    }
    public int getCurrentSegmentSize() { return ; }
    public void setCurrentLength(int len) {  = len; }
    public char[] finishCurrentSegment() {
        if ( == null) {
             = new ArrayList<char[]>();
        }
         = true;
        .add();
        int oldLen = .;
         += oldLen;
         = 0;
        // Let's grow segments by 50%
        int newLen = oldLen + (oldLen >> 1);
        if (newLen < ) {
            newLen = ;
        } else if (newLen > ) {
            newLen = ;
        }
        char[] curr = carr(newLen);
         = curr;
        return curr;
    }
    /*
     * Method called to expand size of the current segment, to
     * accommodate for more contiguous content. Usually only
     * used when parsing tokens like names if even then.
     */
    public char[] expandCurrentSegment()
    {
        final char[] curr = ;
        // Let's grow by 50%
        final int len = curr.length;
        // Must grow by at least 1 char, no matter what
        int newLen = (len == ) ? (+1) : Math.min(len + (len >> 1));
        return ( = Arrays.copyOf(currnewLen));
    }
    /*
     * Method called to expand size of the current segment, to
     * accommodate for more contiguous content. Usually only
     * used when parsing tokens like names if even then.
     * 
     * @param minSize Required minimum strength of the current segment
     *
     * @since 2.4.0
     */
    public char[] expandCurrentSegment(int minSize) {
        char[] curr = ;
        if (curr.length >= minSizereturn curr;
         = curr = Arrays.copyOf(currminSize);
        return curr;
    }
    /*
    /**********************************************************
    /* Standard methods:
    /**********************************************************
     */
    /*
     * Note: calling this method may not be as efficient as calling
     * {@link #contentsAsString}, since it's not guaranteed that resulting
     * String is cached.
     */
    @Override public String toString() { return contentsAsString(); }
    /*
    /**********************************************************
    /* Internal methods:
    /**********************************************************
     */
    /*
     * Method called if/when we need to append content when we have been
     * initialized to use shared buffer.
     */
    private void unshare(int needExtra)
    {
        int sharedLen = ;
         = 0;
        char[] inputBuf = ;
         = null;
        int start = ;
         = -1;
        // Is buffer big enough, or do we need to reallocate?
        int needed = sharedLen+needExtra;
        if ( == null || needed > .) {
             = buf(needed);
        }
        if (sharedLen > 0) {
            System.arraycopy(inputBufstart, 0, sharedLen);
        }
         = 0;
         = sharedLen;
    }
    /*
     * Method called when current segment is full, to allocate new
     * segment.
     */
    private void expand(int minNewSegmentSize)
    {
        // First, let's move current segment to segment list:
        if ( == null) {
             = new ArrayList<char[]>();
        }
        char[] curr = ;
         = true;
        .add(curr);
         += curr.length;
         = 0;
        int oldLen = curr.length;
        
        // Let's grow segments by 50% minimum
        int newLen = oldLen + (oldLen >> 1);
        if (newLen < ) {
            newLen = ;
        } else if (newLen > ) {
            newLen = ;
        }
         = carr(newLen);
    }
    private char[] resultArray()
    {
        if ( != null) { // Can take a shortcut...
            return .toCharArray();
        }
        // Do we use shared array?
        if ( >= 0) {
            final int len = ;
            if (len < 1) {
                return ;
            }
            final int start = ;
            if (start == 0) {
                return Arrays.copyOf(len);
            }
            return Arrays.copyOfRange(startstart+len);
        }
        // nope, not shared
        int size = size();
        if (size < 1) {
            return ;
        }
        int offset = 0;
        final char[] result = carr(size);
        if ( != null) {
            for (int i = 0, len = .size(); i < len; ++i) {
                char[] curr = .get(i);
                int currLen = curr.length;
                System.arraycopy(curr, 0, resultoffsetcurrLen);
                offset += currLen;
            }
        }
        System.arraycopy(, 0, resultoffset);
        return result;
    }
    private char[] carr(int len) { return new char[len]; }
New to GrepCode? Check out our FAQ X