001: // serverByteBuffer.java
002: // ---------------------------
003: // (C) by Michael Peter Christen; mc@anomic.de
004: // first published on http://www.anomic.de
005: // Frankfurt, Germany, 2004
006: // last major change: 11.03.2004
007: //
008: // This program is free software; you can redistribute it and/or modify
009: // it under the terms of the GNU General Public License as published by
010: // the Free Software Foundation; either version 2 of the License, or
011: // (at your option) any later version.
012: //
013: // This program is distributed in the hope that it will be useful,
014: // but WITHOUT ANY WARRANTY; without even the implied warranty of
015: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: // GNU General Public License for more details.
017: //
018: // You should have received a copy of the GNU General Public License
019: // along with this program; if not, write to the Free Software
020: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021: //
022: // Using this software in any meaning (reading, learning, copying, compiling,
023: // running) means that you agree that the Author(s) is (are) not responsible
024: // for cost, loss of data or any harm that may be caused directly or indirectly
025: // by usage of this softare or this documentation. The usage of this software
026: // is on your own risk. The installation and usage (starting/running) of this
027: // software may allow other people or application to access your computer and
028: // any attached devices and is highly dependent on the configuration of the
029: // software which must be done by the user of the software; the author(s) is
030: // (are) also not responsible for proper configuration and usage of the
031: // software, even if provoked by documentation provided together with
032: // the software.
033: //
034: // Any changes to this file according to the GPL as documented in the file
035: // gpl.txt aside this file in the shipment you received can be done to the
036: // lines that follows this copyright notice here, but changes must not be
037: // done inside the copyright notive above. A re-distribution must contain
038: // the intact and unchanged copyright notice.
039: // Contributions and changes to the program code must be marked as such.
040:
041: package de.anomic.server;
042:
043: import java.io.File;
044: import java.io.FileInputStream;
045: import java.io.FileNotFoundException;
046: import java.io.IOException;
047: import java.io.OutputStream;
048: import java.io.UnsupportedEncodingException;
049: import java.util.Properties;
050:
051: public final class serverByteBuffer extends OutputStream {
052:
053: public static final byte singlequote = (byte) 39;
054: public static final byte doublequote = (byte) 34;
055: public static final byte equal = (byte) '=';
056:
057: private byte[] buffer;
058: private int offset;
059: private int length;
060:
061: public serverByteBuffer() {
062: buffer = new byte[10];
063: length = 0;
064: offset = 0;
065: }
066:
067: public serverByteBuffer(int initLength) {
068: this .buffer = new byte[initLength];
069: this .length = 0;
070: this .offset = 0;
071: }
072:
073: public serverByteBuffer(byte[] bb) {
074: buffer = bb;
075: length = bb.length;
076: offset = 0;
077: }
078:
079: public serverByteBuffer(byte[] bb, int initLength) {
080: this .buffer = new byte[initLength];
081: System.arraycopy(bb, 0, buffer, 0, bb.length);
082: length = bb.length;
083: offset = 0;
084: }
085:
086: public serverByteBuffer(byte[] bb, int of, int le) {
087: if (of * 2 > bb.length) {
088: buffer = new byte[le];
089: System.arraycopy(bb, of, buffer, 0, le);
090: length = le;
091: offset = 0;
092: } else {
093: buffer = bb;
094: length = le;
095: offset = of;
096: }
097: }
098:
099: public serverByteBuffer(serverByteBuffer bb) {
100: buffer = bb.buffer;
101: length = bb.length;
102: offset = bb.offset;
103: }
104:
105: public serverByteBuffer(File f) throws IOException {
106: // initially fill the byte buffer with the content of a file
107: if (f.length() > (long) Integer.MAX_VALUE)
108: throw new IOException("file is too large for buffering");
109:
110: length = (int) f.length();
111: buffer = new byte[length];
112: offset = 0;
113:
114: try {
115: FileInputStream fis = new FileInputStream(f);
116: // byte[] buf = new byte[512];
117: // int p = 0;
118: // while ((l = fis.read(buf)) > 0) {
119: // System.arraycopy(buf, 0, buffer, p, l);
120: // p += l;
121: /*int l =*/fis.read(buffer);
122: // }
123: fis.close();
124: } catch (FileNotFoundException e) {
125: throw new IOException("File not found: " + f.toString()
126: + "; " + e.getMessage());
127: }
128: }
129:
130: public void clear() {
131: // we keep the byte[] and just set the pointer to write positions to zero
132: this .length = 0;
133: this .offset = 0;
134: }
135:
136: public int length() {
137: return length;
138: }
139:
140: private void grow() {
141: int newsize = buffer.length * 2 + 1;
142: if (newsize < 256)
143: newsize = 256;
144: byte[] tmp = new byte[newsize];
145: System.arraycopy(buffer, offset, tmp, 0, length);
146: buffer = tmp;
147: tmp = null;
148: offset = 0;
149: }
150:
151: public void write(int b) {
152: write((byte) (b & 0xff));
153: }
154:
155: public void write(byte b) {
156: if (offset + length + 1 > buffer.length)
157: grow();
158: buffer[offset + length++] = b;
159: }
160:
161: public void write(byte[] bb) {
162: write(bb, 0, bb.length);
163: }
164:
165: public void write(byte[] bb, int of, int le) {
166: while (offset + length + le > buffer.length)
167: grow();
168: System.arraycopy(bb, of, buffer, offset + length, le);
169: length += le;
170: }
171:
172: // overwrite does not increase the 'length' write position pointer!
173:
174: public void overwrite(int pos, int b) {
175: overwrite(pos, (byte) (b & 0xff));
176: }
177:
178: public void overwrite(int pos, byte b) {
179: if (offset + pos + 1 > buffer.length)
180: grow();
181: buffer[offset + pos] = b;
182: if (pos >= length)
183: length = pos + 1;
184: }
185:
186: public void overwrite(int pos, byte[] bb) {
187: overwrite(pos, bb, 0, bb.length);
188: }
189:
190: public void overwrite(int pos, byte[] bb, int of, int le) {
191: while (offset + pos + le > buffer.length)
192: grow();
193: System.arraycopy(bb, of, buffer, offset + pos, le);
194: if (pos + le > length)
195: length = pos + le;
196: }
197:
198: public serverByteBuffer append(byte b) {
199: write(b);
200: return this ;
201: }
202:
203: public serverByteBuffer append(int i) {
204: write((byte) (i & 0xFF));
205: return this ;
206: }
207:
208: public serverByteBuffer append(byte[] bb) {
209: write(bb);
210: return this ;
211: }
212:
213: public serverByteBuffer append(byte[] bb, int of, int le) {
214: write(bb, of, le);
215: return this ;
216: }
217:
218: public serverByteBuffer append(String s) {
219: return append(s.getBytes());
220: }
221:
222: public serverByteBuffer append(String s, String charset)
223: throws UnsupportedEncodingException {
224: return append(s.getBytes(charset));
225: }
226:
227: public serverByteBuffer append(serverByteBuffer bb) {
228: return append(bb.buffer, bb.offset, bb.length);
229: }
230:
231: public serverByteBuffer append(Object o) {
232: if (o instanceof String)
233: return append((String) o);
234: if (o instanceof byte[])
235: return append((byte[]) o);
236: return null;
237: }
238:
239: public byte byteAt(int pos) {
240: if (pos > length)
241: return -1;
242: return buffer[offset + pos];
243: }
244:
245: public void deleteByteAt(int pos) {
246: if (pos < 0)
247: return;
248: if (pos >= length)
249: return;
250: if (pos == length - 1) {
251: length--;
252: } else {
253: System.arraycopy(buffer, offset + pos + 1, buffer, offset
254: + pos, length - pos - 1);
255: }
256: }
257:
258: public int indexOf(byte b) {
259: return indexOf(b, 0);
260: }
261:
262: public int indexOf(byte[] bs) {
263: return indexOf(bs, 0);
264: }
265:
266: public int indexOf(byte b, int start) {
267: if (start >= length)
268: return -1;
269: for (int i = start; i < length; i++)
270: if (buffer[offset + i] == b)
271: return i;
272: return -1;
273: }
274:
275: public int indexOf(byte[] bs, int start) {
276: if (start + bs.length > length)
277: return -1;
278: loop: for (int i = start; i <= length - bs.length; i++) {
279: // first test only first byte
280: if (buffer[offset + i] != bs[0])
281: continue loop;
282:
283: // then test all remaining bytes
284: for (int j = 1; j < bs.length; j++) {
285: if (buffer[offset + i + j] != bs[j])
286: continue loop;
287: }
288:
289: // found hit
290: return i;
291: }
292: return -1;
293: }
294:
295: public int lastIndexOf(byte b) {
296: for (int i = length - 1; i >= 0; i--)
297: if (buffer[offset + i] == b)
298: return i;
299: return -1;
300: }
301:
302: public boolean startsWith(byte[] bs) {
303: return startsWith(bs, 0);
304: }
305:
306: public boolean startsWith(byte[] bs, int start) {
307: if (length - start < bs.length)
308: return false;
309: for (int i = 0; i < bs.length; i++) {
310: if (buffer[offset + i + start] != bs[i])
311: return false;
312: }
313: return true;
314: }
315:
316: public byte[] getBytes() {
317: return getBytes(0);
318: }
319:
320: public byte[] getBytes(int start) {
321: return getBytes(start, this .length);
322: }
323:
324: public byte[] getBytes(int start, int len) {
325: // start is inclusive, end is exclusive
326: if (len > length)
327: throw new IndexOutOfBoundsException(
328: "getBytes: len > length");
329: if (start > length)
330: throw new IndexOutOfBoundsException(
331: "getBytes: start > length");
332: if ((start == 0) && (len == length) && (len == buffer.length))
333: return buffer;
334: byte[] tmp = new byte[len];
335: System.arraycopy(buffer, offset + start, tmp, 0, len);
336: return tmp;
337: }
338:
339: public serverByteBuffer trim(int start) {
340: trim(start, this .length - start);
341: return this ;
342: }
343:
344: public serverByteBuffer trim(int start, int len) {
345: if (start + len > this .length)
346: throw new IndexOutOfBoundsException(
347: "trim: start + len > length; this.offset = "
348: + this .offset + ", this.length = "
349: + this .length + ", start = " + start
350: + ", len = " + len);
351: this .offset = this .offset + start;
352: this .length = len;
353: return this ;
354: }
355:
356: public serverByteBuffer trim() {
357: int l = 0;
358: while ((l < length) && (buffer[offset + l] <= 32)) {
359: l++;
360: }
361: int r = length - 1;
362: while ((r > l) && (buffer[offset + r] <= 32))
363: r--;
364: return trim(l, r - l + 1);
365: }
366:
367: public int isUTF8char(int start) {
368: // a sequence of bytes is a utf-8 character, if one of the following 4 conditions is true:
369: // - ASCII equivalence range; (first) byte begins with zero
370: // - first byte begins with 110, the following byte begins with 10
371: // - first byte begins with 1110, the following two bytes begin with 10
372: // - First byte begins with 11110, the following three bytes begin with 10
373: // if an utf-8 sequence is detected, the length of the sequence is returned. -1 othervise
374: if ((start < length) && ((buffer[offset + start] & 0x80) != 0))
375: return 1;
376: if ((start < length - 1)
377: && ((buffer[offset + start] & 0xE0) == 0xC0)
378: && ((buffer[offset + start + 1] & 0xC0) == 0x80))
379: return 2;
380: if ((start < length - 2)
381: && ((buffer[offset + start] & 0xF0) == 0xE0)
382: && ((buffer[offset + start + 1] & 0xC0) == 0x80)
383: && ((buffer[offset + start + 2] & 0xC0) == 0x80))
384: return 3;
385: if ((start < length - 3)
386: && ((buffer[offset + start] & 0xF8) == 0xF0)
387: && ((buffer[offset + start + 1] & 0xC0) == 0x80)
388: && ((buffer[offset + start + 2] & 0xC0) == 0x80)
389: && ((buffer[offset + start + 3] & 0xC0) == 0x80))
390: return 4;
391: return -1;
392: }
393:
394: public boolean isWhitespace(boolean includeNonLetterBytes) {
395: // returns true, if trim() would result in an empty serverByteBuffer
396: if (includeNonLetterBytes) {
397: byte b;
398: for (int i = 0; i < length; i++) {
399: b = buffer[offset + i];
400: if (((b >= '0') && (b <= '9'))
401: || ((b >= 'A') && (b <= 'Z'))
402: || ((b >= 'a') && (b <= 'z')))
403: return false;
404: }
405: } else {
406: for (int i = 0; i < length; i++)
407: if (buffer[offset + i] > 32)
408: return false;
409: }
410: return true;
411: }
412:
413: public int whitespaceStart(boolean includeNonLetterBytes) {
414: // returns number of whitespace bytes at the beginning of text
415: if (includeNonLetterBytes) {
416: byte b;
417: for (int i = 0; i < length; i++) {
418: b = buffer[offset + i];
419: if (((b >= '0') && (b <= '9'))
420: || ((b >= 'A') && (b <= 'Z'))
421: || ((b >= 'a') && (b <= 'z')))
422: return i;
423: }
424: } else {
425: for (int i = 0; i < length; i++)
426: if (buffer[offset + i] > 32)
427: return i;
428: }
429: return length;
430: }
431:
432: public int whitespaceEnd(boolean includeNonLetterBytes) {
433: // returns position of whitespace at the end of text
434: if (includeNonLetterBytes) {
435: byte b;
436: for (int i = length - 1; i >= 0; i--) {
437: b = buffer[offset + i];
438: if (((b >= '0') && (b <= '9'))
439: || ((b >= 'A') && (b <= 'Z'))
440: || ((b >= 'a') && (b <= 'z')))
441: return i + 1;
442: }
443: } else {
444: for (int i = length - 1; i >= 0; i--)
445: if (buffer[offset + i] > 32)
446: return i + 1;
447: }
448: return 0;
449: }
450:
451: public String toString() {
452: return new String(buffer, offset, length);
453: }
454:
455: public String toString(String charsetName) {
456: try {
457: return new String(this .getBytes(), charsetName);
458: } catch (UnsupportedEncodingException e) {
459: return new String(this .getBytes());
460: }
461: }
462:
463: public String toString(int left, int rightbound) {
464: return new String(buffer, offset + left, rightbound - left);
465: }
466:
467: public Properties propParser(String charset) {
468: // extract a=b or a="b" - relations from the buffer
469: int pos = offset;
470: int start;
471: String key;
472: Properties p = new Properties();
473: // eat up spaces at beginning
474: while ((pos < length) && (buffer[pos] <= 32))
475: pos++;
476: while (pos < length) {
477: // pos is at start of next key
478: start = pos;
479: while ((pos < length) && (buffer[pos] != equal))
480: pos++;
481: if (pos >= length)
482: break; // this is the case if we found no equal
483: try {
484: key = new String(buffer, start, pos - start, charset)
485: .trim().toLowerCase();
486: } catch (UnsupportedEncodingException e1) {
487: key = new String(buffer, start, pos - start).trim()
488: .toLowerCase();
489: }
490: // we have a key
491: pos++;
492: // find start of value
493: while ((pos < length) && (buffer[pos] <= 32))
494: pos++;
495: // doublequotes are obligatory. However, we want to be fuzzy if they
496: // are ommittet
497: if (pos >= length) {
498: // error case: input ended too early
499: break;
500: } else if (buffer[pos] == doublequote) {
501: // search next doublequote
502: pos++;
503: start = pos;
504: while ((pos < length) && (buffer[pos] != doublequote))
505: pos++;
506: if (pos >= length)
507: break; // this is the case if we found no parent doublequote
508: try {
509: p.setProperty(key, new String(buffer, start, pos
510: - start, charset).trim());
511: } catch (UnsupportedEncodingException e) {
512: p.setProperty(key, new String(buffer, start, pos
513: - start).trim());
514: }
515: pos++;
516: } else if (buffer[pos] == singlequote) {
517: // search next singlequote
518: pos++;
519: start = pos;
520: while ((pos < length) && (buffer[pos] != singlequote))
521: pos++;
522: if (pos >= length)
523: break; // this is the case if we found no parent singlequote
524: try {
525: p.setProperty(key, new String(buffer, start, pos
526: - start, charset).trim());
527: } catch (UnsupportedEncodingException e) {
528: p.setProperty(key, new String(buffer, start, pos
529: - start).trim());
530: }
531: pos++;
532: } else {
533: // search next whitespace
534: start = pos;
535: while ((pos < length) && (buffer[pos] > 32))
536: pos++;
537: try {
538: p.setProperty(key, new String(buffer, start, pos
539: - start, charset).trim());
540: } catch (UnsupportedEncodingException e) {
541: p.setProperty(key, new String(buffer, start, pos
542: - start).trim());
543: }
544: }
545: // pos should point now to a whitespace: eat up spaces
546: while ((pos < length) && (buffer[pos] <= 32))
547: pos++;
548: // go on with next loop
549: }
550: return p;
551: }
552:
553: public static boolean equals(byte[] buffer, byte[] pattern) {
554: return equals(buffer, 0, pattern);
555: }
556:
557: public static boolean equals(byte[] buffer, int offset,
558: byte[] pattern) {
559: // compares two byte arrays: true, if pattern appears completely at offset position
560: if (buffer.length < offset + pattern.length)
561: return false;
562: for (int i = 0; i < pattern.length; i++)
563: if (buffer[offset + i] != pattern[i])
564: return false;
565: return true;
566: }
567:
568: public void reset() {
569: this .length = 0;
570: this .offset = 0;
571: }
572:
573: public void reset(int newSize) {
574: this .resize(newSize);
575: this .reset();
576: }
577:
578: public void resize(int newSize) {
579: if (newSize < 0)
580: throw new IllegalArgumentException("Illegal array size: "
581: + newSize);
582: byte[] v = new byte[newSize];
583: System.arraycopy(this .buffer, 0, v, 0,
584: newSize > this .buffer.length ? this .buffer.length
585: : newSize);
586: this .buffer = v;
587: }
588:
589: public void writeTo(OutputStream dest) throws IOException {
590: dest.write(this.buffer, this.offset, this.length);
591: dest.flush();
592: }
593:
594: }
|