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 }