001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.fusesource.hawtbuf;
018    
019    import java.io.*;
020    import java.nio.ByteBuffer;
021    import java.util.ArrayList;
022    import java.util.List;
023    
024    /**
025     * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
026     */
027    public class Buffer implements Comparable<Buffer> {
028    
029        public byte[] data;
030        public int offset;
031        public int length;
032    
033        public Buffer(Buffer other) {
034            this(other.data, other.offset, other.length);
035        }
036    
037        public Buffer(int size) {
038            this(new byte[size]);
039        }
040        
041        public Buffer(byte data[]) {
042            this(data, 0, data.length);
043        }
044    
045        public Buffer(byte data[], int offset, int length) {
046            this.data = data;
047            this.offset = offset;
048            this.length = length;
049        }
050    
051            public final Buffer slice(int low, int high) {
052            int sz;
053            if (high < 0) {
054                sz = length + high;
055            } else {
056                sz = high - low;
057            }
058            if (sz < 0) {
059                sz = 0;
060            }
061            return new Buffer(data, (offset + low), sz);
062        }
063    
064        public final byte[] getData() {
065            return data;
066        }
067        public final Buffer data(byte[] data) {
068            this.data = data;
069            return this;
070        }
071    
072        public final int getLength() {
073            return length;
074        }
075    
076        public final int length() {
077            return length;
078        }
079        public final Buffer length(int length) {
080            this.length = length;
081            return this;
082        }
083    
084        public final int getOffset() {
085            return offset;
086        }
087        public final Buffer offset(int offset) {
088            this.offset = offset;
089            return this;
090        }
091    
092        final public Buffer deepCopy() {
093            byte t[] = new byte[length];
094            System.arraycopy(data, offset, t, 0, length);
095            return new Buffer(t);
096        }
097    
098        final public Buffer compact() {
099            if (length != data.length) {
100                return new Buffer(toByteArray());
101            }
102            return this;
103        }
104    
105        final public byte[] toByteArray() {
106            byte[] data = this.data;
107            int length = this.length;
108            if (length != data.length) {
109                byte t[] = new byte[length];
110                System.arraycopy(data, offset, t, 0, length);
111                data = t;
112            }
113            return data;
114        }
115    
116        final public byte get(int i) {
117            return data[offset + i];
118        }
119    
120        final public boolean equals(Buffer obj) {
121            byte[] data = this.data;
122            int offset = this.offset;
123            int length = this.length;
124    
125            if (length != obj.length) {
126                return false;
127            }
128            
129            byte[] objData = obj.data;
130            int objOffset = obj.offset;
131            
132            for (int i = 0; i < length; i++) {
133                if (objData[objOffset + i] != data[offset + i]) {
134                    return false;
135                }
136            }
137            return true;
138        }
139    
140        final public BufferInputStream in() {
141            return new BufferInputStream(this);
142        }
143    
144        final public BufferOutputStream out() {
145            return new BufferOutputStream(this);
146        }
147    
148        final public BufferEditor bigEndianEditor() {
149            return new BufferEditor.BigEndianBufferEditor(this);
150        }
151    
152        final public BufferEditor littleEndianEditor() {
153            return new BufferEditor.LittleEndianBufferEditor(this);
154        }
155    
156        final public boolean isEmpty() {
157            return length == 0;
158        }
159    
160        final public boolean contains(byte value) {
161            return indexOf(value, 0) >= 0;
162        }
163    
164        final public int indexOf(byte value) {
165            return indexOf(value, 0);
166        }
167        
168        final public int indexOf(byte value, int pos) {
169            byte[] data = this.data;
170            int offset = this.offset;
171            int length = this.length;
172            
173            for (int i = pos; i < length; i++) {
174                if (data[offset + i] == value) {
175                    return i;
176                }
177            }
178            return -1;
179        }
180    
181        final public boolean startsWith(Buffer other) {
182            return indexOf(other, 0)==0;
183        }
184        
185        final public int indexOf(Buffer needle) {
186            return indexOf(needle, 0);
187        }
188        
189        final public int indexOf(Buffer needle, int pos) {
190            int max = length - needle.length;
191            for (int i = pos; i <= max; i++) {
192                if (matches(needle, i)) {
193                    return i;
194                }
195            }
196            return -1;
197        }
198        
199        final public boolean containsAt(Buffer needle, int pos) {
200            if( (length-pos) < needle.length ) {
201                return false;
202            }
203            return matches(needle, pos);
204        }
205        
206        final private boolean matches(Buffer needle, int pos) {
207            byte[] data = this.data;
208            int offset = this.offset;
209            int needleLength = needle.length;
210            byte[] needleData = needle.data;
211            int needleOffset = needle.offset;
212            
213            for (int i = 0; i < needleLength; i++) {
214                if( data[offset + pos+ i] != needleData[needleOffset + i] ) {
215                    return false;
216                }
217            }
218            return true;
219        }
220    
221        
222        final public Buffer trim() {
223            return trimFront().trimEnd();
224        }
225    
226        final public Buffer trimEnd() {
227            byte data[] = this.data;
228            int offset = this.offset;
229            int length = this.length;
230            int end = offset+this.length-1;
231            int pos = end;
232            
233            while ((offset <= pos) && (data[pos] <= ' ')) {
234                pos--;
235            }
236            return (pos == end) ? this : new Buffer(data, offset, (length-(end-pos))); 
237        }
238    
239        final public Buffer trimFront() {
240            byte data[] = this.data;
241            int offset = this.offset;
242            int end = offset+this.length;
243            int pos = offset;
244            while ((pos < end) && (data[pos] <= ' ')) {
245                pos++;
246            }
247            return (pos == offset) ? this : new Buffer(data, pos, (length-(pos-offset))); 
248        }
249    
250        final public Buffer buffer() {
251            return new Buffer(this);
252        }
253        final public AsciiBuffer ascii() {
254            return new AsciiBuffer(this);
255        }
256        final public  UTF8Buffer utf8() {
257            return new UTF8Buffer(this);
258        }
259        
260        final public Buffer[] split(byte separator) {
261            ArrayList<Buffer> rc = new ArrayList<Buffer>();
262            
263            byte data[] = this.data;
264            int pos = this.offset;
265            int nextStart = pos;
266            int end = pos+this.length;
267    
268            while( pos < end ) {
269                if( data[pos]==separator ) {
270                    if( nextStart < pos ) {
271                        rc.add(new Buffer(data, nextStart, (pos-nextStart)));
272                    }
273                    nextStart = pos+1;
274                }
275                pos++;
276            }
277            if( nextStart < pos ) {
278                rc.add(new Buffer(data, nextStart, (pos-nextStart)));
279            }
280    
281            return rc.toArray(new Buffer[rc.size()]);
282        }
283    
284        public void reset() {
285            offset = 0;
286            length = data.length;
287        }
288    
289        ///////////////////////////////////////////////////////////////////
290        // Overrides
291        ///////////////////////////////////////////////////////////////////
292        
293        @Override
294        public int hashCode() {
295            byte[] data = this.data;
296            int offset = this.offset;
297            int length = this.length;
298    
299            byte[] target = new byte[4];
300            for (int i = 0; i < length; i++) {
301                target[i % 4] ^= data[offset + i];
302            }
303            return target[0] << 24 | target[1] << 16 | target[2] << 8 | target[3];
304        }
305    
306        @Override
307        public boolean equals(Object obj) {
308            if (obj == this)
309                return true;
310    
311            if (obj == null || obj.getClass() != Buffer.class)
312                return false;
313    
314            return equals((Buffer) obj);
315        }
316    
317        @Override
318        public String toString() {
319            String str = AsciiBuffer.decode(this);
320            if( str.length() > 500 ) {
321                str = str.substring(0, 500)+"(truncated)";
322            }
323            return "{ offset: "+offset+", length: "+length+", data: \""+str+"\" }";
324        }
325    
326        public int compareTo(Buffer o) {
327            if( this == o )
328                return 0;
329            
330            byte[] data = this.data;
331            int offset = this.offset;
332            int length = this.length;
333            
334            int oLength = o.length;
335            int oOffset = o.offset;
336            byte[] oData = o.data;
337            
338            int minLength = Math.min(length, oLength);
339            if (offset == oOffset) {
340                int pos = offset;
341                int limit = minLength + offset;
342                while (pos < limit) {
343                    byte b1 = data[pos];
344                    byte b2 = oData[pos];
345                    if (b1 != b2) {
346                        return b1 - b2;
347                    }
348                    pos++;
349                }
350            } else {
351                int offset1 = offset;
352                int offset2 = oOffset;
353                while ( minLength-- != 0) {
354                    byte b1 = data[offset1++];
355                    byte b2 = oData[offset2++];
356                    if (b1 != b2) {
357                        return b1 - b2;
358                    }
359                }
360            }
361            return length - oLength;
362        }
363    
364        /**
365         * same as out.write(data, offset, length);
366         */
367        public void writeTo(DataOutput out) throws IOException {
368            out.write(data, offset, length);
369        }
370    
371        /**
372         * same as out.write(data, offset, length);
373         */
374        public void writeTo(OutputStream out) throws IOException {
375            out.write(data, offset, length);
376        }
377    
378        /**
379         * same as in.readFully(data, offset, length);
380         */
381        public void readFrom(DataInput in) throws IOException {
382            in.readFully(data, offset, length);
383        }
384    
385        /**
386         * same as in.read(data, offset, length);
387         */
388        public int readFrom(InputStream in) throws IOException {
389            return in.read(data, offset, length);
390        }
391    
392        ///////////////////////////////////////////////////////////////////
393        // Statics
394        ///////////////////////////////////////////////////////////////////
395        
396        public static String string(Buffer value) {
397            if( value==null ) {
398                return null;
399            }
400            return value.toString();
401        }
402    
403        final public static Buffer join(List<Buffer> items, Buffer seperator) {
404            if (items.isEmpty())
405                return new Buffer(seperator.data, 0, 0);
406    
407            int size = 0;
408            for (Buffer item : items) {
409                size += item.length;
410            }
411            size += seperator.length * (items.size() - 1);
412    
413            int pos = 0;
414            byte data[] = new byte[size];
415            for (Buffer item : items) {
416                if (pos != 0) {
417                    System.arraycopy(seperator.data, seperator.offset, data, pos, seperator.length);
418                    pos += seperator.length;
419                }
420                System.arraycopy(item.data, item.offset, data, pos, item.length);
421                pos += item.length;
422            }
423    
424            return new Buffer(data, 0, size);
425        }
426    
427    
428        public ByteBuffer toByteBuffer() {
429            return ByteBuffer.wrap(data, offset, length);
430        }
431    
432        public static AsciiBuffer ascii(String value) {
433            return AsciiBuffer.ascii(value);
434        }
435        public static AsciiBuffer ascii(Buffer buffer) {
436            return AsciiBuffer.ascii(buffer);
437        }
438    
439        public static UTF8Buffer utf8(String value) {
440            return UTF8Buffer.utf8(value);
441        }
442        public static UTF8Buffer utf8(Buffer buffer) {
443            return UTF8Buffer.utf8(buffer);
444        }
445    }