bytelist-1.0.6/0000755000175000017500000000000011424446212013313 5ustar twernertwernerbytelist-1.0.6/.project0000644000175000017500000000103311424446212014757 0ustar twernertwerner bytelist org.eclipse.jdt.core.javabuilder org.maven.ide.eclipse.maven2Builder org.maven.ide.eclipse.maven2Nature org.eclipse.jdt.core.javanature bytelist-1.0.6/test/0000755000175000017500000000000011424446212014272 5ustar twernertwernerbytelist-1.0.6/test/org/0000755000175000017500000000000011424446212015061 5ustar twernertwernerbytelist-1.0.6/test/org/jruby/0000755000175000017500000000000011424446212016214 5ustar twernertwernerbytelist-1.0.6/test/org/jruby/util/0000755000175000017500000000000011424446212017171 5ustar twernertwernerbytelist-1.0.6/test/org/jruby/util/ByteListTest.java0000755000175000017500000002503111424446212022437 0ustar twernertwerner/***** BEGIN LICENSE BLOCK ***** * Version: CPL 1.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Common Public * License Version 1.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.eclipse.org/legal/cpl-v10.html * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Copyright (C) 2007 Nick Sieger * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the CPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the CPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.jruby.util; import java.io.UnsupportedEncodingException; import java.util.Arrays; import junit.framework.TestCase; public class ByteListTest extends TestCase { public void testEmptyByteListHasZeroLength() { assertEquals(0, new ByteList().length()); assertEquals(0, new ByteList(32).length()); } public void testByteListWithInitialByteArray() { byte[] bytes = new byte[] {0x0f, 0x01, 0x02, 0x03, 0x04, 0x05}; ByteList b = new ByteList(bytes); assertEquals(bytes.length, b.length()); b.set(0, 0x00); assertEquals(0x0f, bytes[0]); assertEquals(0x00, b.get(0)); ByteList b2 = new ByteList(b); assertEquals(b.length(), b2.length()); b2.set(0, 0x0f); assertEquals(0x0f, b2.get(0)); assertEquals(0x00, b.get(0)); } public void testByteListWithSubrangeOfInitialBytes() { byte[] bytes = new byte[] {0x0f, 0x01, 0x02, 0x03, 0x04, 0x05}; ByteList b = new ByteList(bytes, 2, 2); assertEquals(2, b.length()); assertEquals(0x02, b.get(0)); assertEquals(0x03, b.get(1)); ByteList b2 = new ByteList(b, 1, 1); assertEquals(1, b2.length()); assertEquals(0x03, b2.get(0)); } public void testByteListAppendSingleByte() { byte[] bytes = new byte[] {0x0f, 0x01, 0x02, 0x03, 0x04, 0x05}; ByteList b = new ByteList(1); for (int i = 0; i < bytes.length; i++) { b.append(bytes[i]); } assertEquals(new ByteList(bytes), b); } public void testByteListAppendSingleIntTruncates() { int[] ints = new int[] { 0x1001, 0x1002, 0x1003 }; ByteList b = new ByteList(1); for (int i = 0; i < ints.length; i++) { b.append(ints[i]); } assertEquals(0x01, b.get(0)); assertEquals(0x02, b.get(1)); assertEquals(0x03, b.get(2)); } public void testByteListPrependSingleByte() { byte[] bytes = new byte[] {0x01, 0x02, 0x03}; ByteList b = new ByteList(1); for (int i = 0; i < bytes.length; i++) { b.prepend(bytes[i]); } assertEquals(0x03, b.get(0)); assertEquals(0x02, b.get(1)); assertEquals(0x01, b.get(2)); bytes = new byte[] {0x0f, 0x01, 0x02, 0x03, 0x04, 0x05}; b = new ByteList(bytes); b.prepend((byte) 0x0e); assertEquals(7, b.length()); assertEquals(0x0e, b.get(0)); assertEquals(0x05, b.get(6)); } public void testByteListAppendArray() { ByteList b = new ByteList(1); b.append(new byte[] {0x01, 0x02, 0x03}); assertEquals(3, b.length()); assertEquals(0x01, b.get(0)); assertEquals(0x02, b.get(1)); assertEquals(0x03, b.get(2)); } public void testByteListAppendArrayIndexLength() { ByteList b = new ByteList(1); b.append(new byte[] {0x0f, 0x01, 0x02, 0x03, 0x04, 0x05}, 1, 4); assertEquals(4, b.length()); assertEquals(0x01, b.get(0)); assertEquals(0x02, b.get(1)); assertEquals(0x03, b.get(2)); assertEquals(0x04, b.get(3)); } public void testLengthExpandFillsWithZeros() { ByteList b = new ByteList(); b.length(10); assertEquals(0, b.get(9)); } public void notestGetAndSetOutsideOfLengthShouldFail() { ByteList b = new ByteList(10); assertEquals(0, b.length()); try { b.get(0); fail("should throw IndexOfOfBoundsException"); } catch (IndexOutOfBoundsException oob) { } try { b.set(0, 1); fail("should throw IndexOfOfBoundsException"); } catch (IndexOutOfBoundsException oob) { } } public void notestMethodsThatTakeByteArrayDoNotAllowNull() { ByteList b = new ByteList(); try { new ByteList((byte[]) null); fail("should throw NPE"); } catch (NullPointerException npe) { } try { new ByteList((byte[]) null, 0, 0); fail("should throw NPE"); } catch (NullPointerException npe) { } try { b.append((byte[])null); fail("should throw NPE"); } catch (NullPointerException npe) { } try { b.append((byte[])null, 0, 0); fail("should throw NPE"); } catch (NullPointerException npe) { } try { b.replace(null); fail("should throw NPE"); } catch (NullPointerException npe) { } } public void testReplaceSetsNewContents() { ByteList b = new ByteList(1); b.replace(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); assertEquals(5, b.length()); assertEquals(0x01, b.get(0)); assertEquals(0x05, b.get(4)); } public void testReplaceIndexOffset() { ByteList base = new ByteList(new byte[] {0x01,0x02,0x03}); ByteList b = (ByteList) base.clone(); b.replace(1, 1, new byte[] {0x04,0x05}); assertEquals(new ByteList(new byte[] {0x01,0x04,0x05,0x03}), b); b = (ByteList) base.clone(); b.replace(0, 3, new byte[] {0x00, 0x00, 0x00}, 1, 2); assertEquals(new ByteList(new byte[] {0x00, 0x00}), b); } private ByteList S(String s) throws UnsupportedEncodingException { return new ByteList(s.getBytes("ISO8859_1")); } private String S(ByteList b) throws UnsupportedEncodingException { return new String(b.bytes(), "ISO8859_1"); } public void testReplaceStrings() throws UnsupportedEncodingException { ByteList b = S("FooBar"); b.replace(0, 3, S("A")); assertEquals("ABar", S(b)); b.replace(0, 1, S("Foo")); assertEquals("FooBar", S(b)); b.replace(1, 2, S("zz")); assertEquals("FzzBar", S(b)); b.replace(1, 0, S("u")); assertEquals("FuzzBar", S(b)); } public void testReplaceLengthOutOfBounds() { ByteList b = new ByteList(new byte[] {0x01,0x02,0x03}); try { b.replace(0, 5, new byte[] { 0x00, 0x00, 0x00 }, 1, 2); fail("should have thrown exception"); } catch (IndexOutOfBoundsException e) { } } public void testEquals() { assertTrue(new ByteList().equals(new ByteList(10))); assertFalse(new ByteList().equals(new ByteList(new byte[] {0x01}))); byte[] bytes = new byte[] {0x01, 0x02, 0x03, 0x04}; ByteList b1 = new ByteList(bytes); ByteList b2 = new ByteList(); b2.append(bytes); assertTrue(b1.equals(b2)); } public void testBytesCreatesACopyOfInternalBytes() { byte[] bytes = new byte[] {0x01,0x02,0x03}; ByteList b = new ByteList(bytes); byte[] copy = b.bytes(); assertTrue(Arrays.equals(bytes, copy)); b.set(1, 0); assertTrue(Arrays.equals(bytes, copy)); } public void testCloneCopiesBytes() { ByteList b = new ByteList(new byte[] {0x01,0x02,0x03}); ByteList b2 = (ByteList) b.clone(); b.set(1, 0); assertFalse(b.equals(b2)); } public void testIndexOf() { ByteList b = new ByteList(ByteList.plain("hello bytelist")); ByteList b2 = new ByteList(ByteList.plain("bytelist")); ByteList b3 = new ByteList(ByteList.plain("el")); assertEquals(6, b.indexOf('b')); assertEquals(6, b.indexOf(b2)); assertEquals(-1, b.indexOf('x')); assertEquals(-1, b2.indexOf(b)); assertEquals(10, b.indexOf('l', 6)); assertEquals(9, b.indexOf(b3, 6)); assertEquals(-1, b.indexOf('b', 7)); assertEquals(-1, b.indexOf(b3, 10)); } public void testLastIndexOf() { ByteList b = new ByteList(ByteList.plain("hello bytelist")); ByteList b2 = new ByteList(ByteList.plain("bytelist")); ByteList b3 = new ByteList(ByteList.plain("el")); assertEquals(10, b.lastIndexOf('l')); assertEquals(6, b.lastIndexOf(b2)); assertEquals(-1, b.lastIndexOf('x')); assertEquals(-1, b2.lastIndexOf(b)); assertEquals(3, b.lastIndexOf('l', 6)); assertEquals(1, b.lastIndexOf(b3, 6)); assertEquals(-1, b.lastIndexOf('b', 5)); assertEquals(-1, b.lastIndexOf(b2, 5)); } public void testShallowDup() { ByteList b = new ByteList(10); b.append("hello".getBytes()); ByteList d = b.shallowDup(); assertEquals(5, b.length()); assertEquals(5, d.length()); } public void testEndsWithOnEmptyByteList() { ByteList blank = new ByteList(); ByteList blankAndZeroLength = new ByteList(0); ByteList str = new ByteList("hello".getBytes()); assertFalse(blank.endsWith(str)); assertFalse(blankAndZeroLength.endsWith(str)); } public void testStartsWithOnEmptyByteList() { ByteList blank = new ByteList(); ByteList blankAndZeroLength = new ByteList(0); ByteList str = new ByteList("hello".getBytes()); assertFalse(blank.startsWith(str)); assertFalse(blankAndZeroLength.startsWith(str)); } } bytelist-1.0.6/.settings/0000755000175000017500000000000011424446212015231 5ustar twernertwernerbytelist-1.0.6/.settings/org.eclipse.jdt.core.prefs0000644000175000017500000000041411424446212022212 0ustar twernertwerner#Wed Dec 30 11:53:15 CET 2009 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 org.eclipse.jdt.core.compiler.compliance=1.5 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.5 bytelist-1.0.6/pom.xml0000644000175000017500000000677011424446212014642 0ustar twernertwerner 4.0.0 org.jruby.extras bytelist jar 1.0.5 ByteList Byte array based container JIRA http://jira.codehaus.org/browse/JRUBY scm:svn:http://svn.codehaus.org/jruby scm:svn:https://svn.codehaus.org/jruby http://svn.codehaus.org/jruby MIT License http://www.opensource.org/licenses/mit-license.php repo codehaus-jruby-repository JRuby Central Repository dav:https://dav.codehaus.org/repository/jruby codehaus-jruby-snapshot-repository JRuby Central Development Repository dav:https://dav.codehaus.org/snapshots.repository/jruby codehaus-jruby-site JRuby Maven site dav:https://dav.codehaus.org/jruby/info codehaus Codehaus Repository true false http://repository.codehaus.org org.jruby.jcodings jcodings 1.0.3 junit junit 3.8.1 test src test org.apache.maven.plugins maven-source-plugin attach-sources jar org.apache.maven.plugins maven-javadoc-plugin attach-javadocs jar maven-compiler-plugin 1.5 1.5 maven-jar-plugin MANIFEST.MF bytelist-1.0.6/.classpath0000644000175000017500000000074511424446212015304 0ustar twernertwerner bytelist-1.0.6/build.xml0000644000175000017500000000334411424446212015140 0ustar twernertwerner bytelist-1.0.6/MANIFEST.MF0000644000175000017500000000013211424446212014741 0ustar twernertwernerImplementation-Title: ByteList (Byte array based container) Implementation-Version: 1.0.1 bytelist-1.0.6/.gitignore0000644000175000017500000000000711424446212015300 0ustar twernertwernertarget bytelist-1.0.6/src/0000755000175000017500000000000011424446212014102 5ustar twernertwernerbytelist-1.0.6/src/org/0000755000175000017500000000000011424446212014671 5ustar twernertwernerbytelist-1.0.6/src/org/jruby/0000755000175000017500000000000011424446212016024 5ustar twernertwernerbytelist-1.0.6/src/org/jruby/util/0000755000175000017500000000000011424446212017001 5ustar twernertwernerbytelist-1.0.6/src/org/jruby/util/ByteList.java0000644000175000017500000012533711424446212021416 0ustar twernertwerner/***** BEGIN LICENSE BLOCK ***** * Version: CPL 1.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Common Public * License Version 1.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.eclipse.org/legal/cpl-v10.html * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Copyright (C) 2007-2010 JRuby Community * Copyright (C) 2007 Charles O Nutter * Copyright (C) 2007 Nick Sieger * Copyright (C) 2007 Ola Bini * Copyright (C) 2007 William N Dortch * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the CPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the CPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.jruby.util; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.jcodings.Encoding; import org.jcodings.ascii.AsciiTables; import org.jcodings.specific.ASCIIEncoding; /** * ByteList is simple a collection of bytes in the same way a Java String is a collection * of characters. However, it's API resembles StringBuffer/StringBuilder more than String * because it is a mutable object. */ @SuppressWarnings("deprecation") public final class ByteList implements Comparable, CharSequence, Serializable { private static final long serialVersionUID = -1286166947275543731L; public static final byte[] NULL_ARRAY = new byte[0]; public static final ByteList EMPTY_BYTELIST = new ByteList(0); @Deprecated public byte[] bytes; @Deprecated public int begin; @Deprecated public int realSize; @Deprecated public Encoding encoding = ASCIIEncoding.INSTANCE; int hash; String stringValue; private static final int DEFAULT_SIZE = 4; /** * Creates a new instance of ByteList */ public ByteList() { this(DEFAULT_SIZE); } /** * Creates a new instance of Bytelist with a pre-allocated size. If you know the size ahead * of time this saves additional array allocations to grow the bytelist to the proper size. * * @param size to preallocate the bytelist to */ public ByteList(int size) { bytes = new byte[size]; realSize = 0; } /** * Create a new instance of ByteList with the bytes supplied using the specified encoding. * * Important: bytes is used as the initial backing store for the bytelist. Over time as the * bytelist is mutated this backing store may be replaced with a new one to hold the additional * bytes. If you pass in bytes and then modify the contents of the original bytes, then those * changes will get reflected. * * @param bytes to use * @param encoding */ // TODO: Deprecate and replace with a static method which implies the caveats of this constructor. public ByteList(byte[] bytes, Encoding encoding) { this.bytes = bytes; this.realSize = bytes.length; this.encoding = encoding; } /** * Create a new instance of ByteList with the contents of wrap. This constructor will make * a copy of bytes passed * * @param wrap the initial bytes for this ByteList */ public ByteList(byte[] wrap) { this(wrap, true); } /** * Create a new instance of ByteList with the contents of wrap. If copy is true then it will * array copy the contents. Otherwise it will use the byte array passed in as its initial * backing store. * * @param wrap the initial bytes for this ByteList * @param copy whether to arraycopy wrap for the backing store or not */ public ByteList(byte[] wrap, boolean copy) { assert wrap != null; if (copy) { bytes = (byte[])wrap.clone(); } else { bytes = wrap; } realSize = wrap.length; } /** * Create a new instance of byte list with the same contents as the passed in ByteList wrap. * Note that this does array copy the data for the new objects initial backing store. * * @param wrap is contents for new ByteList */ public ByteList(ByteList wrap) { this(wrap.bytes, wrap.begin, wrap.realSize); } /** * Create a new instance of ByteList with the same contents as the passed in ByteList wrap. * The copy parameter gives you control over whether you want this new ByteList to share * the same byte array for its backing store. * * ****IMPORTANT NOTES***** * copy is currently ignored and always assumed false. This constructor should just go away * so it has been marked as deprecated. * * @param wrap * @param copy * * Deprecated to coincide with JRuby 1.5 (not used by anything we can find luckily) */ @Deprecated public ByteList(ByteList wrap, boolean copy) { this(wrap.bytes, wrap.begin, wrap.realSize, false); } /** * Create a new instance of ByteList using wrap as a backing store where index is the first * index in the byte array where the data starts and len indicates how long the data portion * of the bytelist is. wrap will be array copied in this constructor. * * @param wrap the bytes to use * @param index where in the bytes the data starts * @param len how long the data is in the wrap array */ public ByteList(byte[] wrap, int index, int len) { this(wrap, index, len, true); } /** * Create a new instance of ByteList using wrap as a backing store where index is the first * index in the byte array where the data starts and len indicates how long the data portion * of the bytelist is. wrap will be array copied if copy is true OR if index != 0. * * @param wrap the bytes to use * @param index where in the bytes the data starts * @param len how long the data is in the wrap array * @param copy if true array copy wrap. otherwise use as backing store */ // FIXME: Fix the index != 0 not honoring copy and separate out into a different caller. JRuby.next would be the right time for this. public ByteList(byte[] wrap, int index, int len, boolean copy) { assert wrap != null : "'wrap' must not be null"; assert index >= 0 && index <= wrap.length : "'index' is not without bounds of 'wrap' array"; assert wrap.length >= index + len : "'index' + 'len' is longer than the 'wrap' array"; if (copy || index != 0) { bytes = new byte[len]; System.arraycopy(wrap, index, bytes, 0, len); } else { bytes = wrap; } realSize = len; } /** * Create a new instance of ByteList using wrap as a backing store where index is the first * index in the byte array where the data starts and len indicates how long the data portion * of the bytelist is. wrap's byte array will be array copied for initial backing store. * * @param wrap the bytes to use * @param index where in the bytes the data starts * @param len how long the data is in the wrap array */ public ByteList(ByteList wrap, int index, int len) { this(wrap.bytes, wrap.begin + index, len); } /** * Delete len bytes from start index. This does no bullet-proofing so it is your * responsibility to ensure you do not run off the backing store array. * * @param start index to delete from * @param len number of bytes to delete */ public void delete(int start, int len) { assert start >= begin && start < realSize : "'start' is at invalid index"; assert len >= 0 : "'len' must be positive"; assert start + len <= begin + realSize : "too many bytes requested"; realSize -= len; System.arraycopy(bytes, start + len, bytes, start, realSize); } /** * Append the byte b up to len times onto the end of the current ByteList. * * @param b is byte to be appended * @param len is number of times to repeat the append */ // FIXME: Innefficient impl since we know the len up front. public void fill(int b, int len) { for ( ; --len >= 0; ) { append(b); } } /** * @see Object#clone() */ @Override public Object clone() { return dup(); } /** * creates a duplicate of this bytelist but only in the case of a stringValue and its resulting * hash value. No other elements are duplicated. */ public ByteList dup() { ByteList dup = dup(realSize); dup.hash = hash; dup.stringValue = stringValue; return dup; } /** * Create a new ByteList but do not array copy the byte backing store. * * @return a new ByteList with same backing store */ public ByteList shallowDup() { ByteList dup = new ByteList(bytes, false); dup.realSize = realSize; dup.begin = begin; dup.encoding = encoding; dup.hash = hash; dup.stringValue = stringValue; return dup; } /** * @param length is the value of how big the buffer is going to be, not the actual length to copy * * It is used by RubyString.modify(int) to prevent COW pathological situations * (namely to COW with having length - realSize bytes ahead) */ public ByteList dup(int length) { ByteList dup = new ByteList(length); dup.append(this.bytes, this.begin, this.realSize); dup.encoding = encoding; return dup; } /** * Ensure that the bytelist is at least length bytes long. Otherwise grow the backing store * so that it is length bytes long * * @param length to use to make sure ByteList is long enough */ public void ensure(int length) { if (length >= bytes.length) { byte[] tmp = new byte[length + (length >>> 1)]; System.arraycopy(bytes, begin, tmp, 0, realSize); bytes = tmp; } } /** * Make a shared copy of this ByteList. This is used for COW'ing ByteLists, you typically * want a piece of the same backing store to be shared across ByteBuffers, while those * ByteLists will be pointing at different indexes and lengths of the same backing store. * * Note: that this does not update hash or stringValue. * * @param index new begin value for shared ByteBuffer * @param len new length/realSize for chared * @return */ public ByteList makeShared(int index, int len) { ByteList shared = new ByteList(bytes, encoding); shared.realSize = len; shared.begin = begin + index; return shared; } /** * Change ByteBuffer to have a new begin that is +index positions past begin with a new length. * * @param index new value to add to begin * @param len the new realSize/length value */ public void view(int index, int len) { realSize = len; begin = begin + index; } /** * Array copy the byte backing store so that you can guarantee that no other objects are * referencing this objects backing store. */ public void unshare() { unshare(realSize); } /** * Array copy the byte backing store so that you can guarantee that no other objects are * referencing this objects backing store. This version on unshare allows a length to be * specified which will copy length bytes from the old backing store. * * @param length is the value of how big the buffer is going to be, not the actual length to copy * * It is used by RubyString.modify(int) to prevent COW pathological situations * (namely to COW with having length - realSize bytes ahead) */ public void unshare(int length) { byte[] tmp = new byte[length]; System.arraycopy(bytes, begin, tmp, 0, Math.min(realSize, length)); bytes = tmp; begin = 0; } /** * Invalidate the hash and stringValue which may have been cached in this ByteList. */ public void invalidate() { hash = 0; stringValue = null; } /** * Prepend a byte onto the front of this ByteList. * * @param b is the byte to be prepended */ public void prepend(byte b) { grow(1); System.arraycopy(bytes, 0, bytes, 1, realSize); bytes[0] = b; realSize++; } /** * Append a single byte to the ByteList * * @param b the byte to be added * @return this instance */ public ByteList append(byte b) { grow(1); bytes[realSize++] = b; return this; } /** * Append a single int to the ByteList * * @param b the int to be added * @return this instance */ public ByteList append(int b) { append((byte)b); return this; } /** * Append up to length bytes from InputStream to the ByteList. If no bytes are read from the * stream then throw an IOException. * * @param input the stream to read bytes from * @param length how many byte to try and read * @return this instance * @throws IOException when no bytes are read */ public ByteList append(InputStream input, int length) throws IOException { grow(length); int read = 0; int n; while (read < length) { n = input.read(bytes, begin + read, length - read); if (n == -1) { if (read == 0) throw new java.io.EOFException(); break; } read += n; } realSize += read; return this; } /** * Append contents of the supplied nio ByteList up to len length onto the end of this * ByteBuffer. * * @param buffer to be appended * @param len is number of bytes you hoping to get from the ByteBuffer */ public void append(ByteBuffer buffer, int len) { grow(len); buffer.get(bytes, realSize, len); realSize += len; } /** * Append moreBytes onto the end of the current ByteList. * * @param moreBytes to be added. */ public void append(byte[] moreBytes) { assert moreBytes != null : "moreBytes is null"; grow(moreBytes.length); System.arraycopy(moreBytes, 0, bytes, realSize, moreBytes.length); realSize += moreBytes.length; } /** * Append moreBytes onto the end of the current ByteList. * * @param moreBytes to be added. */ public void append(ByteList moreBytes) { append(moreBytes.bytes, moreBytes.begin, moreBytes.realSize); } /** * Append moreBytes onto the end of the current ByteList with +index as the new begin for * len bytes from the moreBytes ByteList. * * @param moreBytes to be added. * @param index new index past current begin value * @param len is the number of bytes to append from source ByteList */ public void append(ByteList moreBytes, int index, int len) { append(moreBytes.bytes, moreBytes.begin + index, len); } /** * Append moreBytes onto the end of the current ByteList with start as the new begin for * len bytes from the moreBytes byte array. * * @param moreBytes to be added. * @param start is the new begin value * @param len is the number of bytes to append from source byte array */ public void append(byte[] moreBytes, int start, int len) { assert moreBytes != null : "moreBytes is null"; // FIXME: Problems on CI box tripping on this. Re-enable later during 1.6 development. //assert start >= 0 && (start == 0 || start < moreBytes.length) : "Invalid start"; assert len >= 0 && moreBytes.length - start >= len : "Bad length"; grow(len); System.arraycopy(moreBytes, start, bytes, realSize, len); realSize += len; } /** * Resize the ByteList's backing store to be length in size. Note that this forces the backing * store to array copy regardless of ByteLists current size or contents. It essentially will * end any COWing. * * @param length the new length for the backing store. */ public void realloc(int length) { assert length >= 0 : "Invalid length"; assert length >= realSize : "length is too small"; byte tmp[] = new byte[length]; System.arraycopy(bytes, 0, tmp, 0, realSize); bytes = tmp; } /** * Return the current length of the ByteList. * * @return the number of bytes in this ByteList. */ public int length() { return realSize; } // ENEBO: Wow...what happens if newLength < realSize...nasty shrinkage? /** * grow the bytelist to be newLength in size. * * @param newLength */ public void length(int newLength) { // assert newLength >= realSize : "newLength is too small"; grow(newLength - realSize); realSize = newLength; } /** * Number of characters in this ByteList based on its current encoding. * * @return number of characters */ public int lengthEnc() { return encoding.strLength(bytes, begin, begin + realSize); } /** * Get the byte at index from the ByteList. * * @param index to retreive byte from * @return the byte retreived */ public int get(int index) { assert index >= 0 : "index must be positive"; return bytes[begin + index]; } /** * Get the index code point in this ByteList. * * @param index is the element you want * @return the element you requested */ public int getEnc(int index) { return encoding.strCodeAt(bytes, begin, begin + realSize, index); } /** * Set the byte at index to be new value. * * @param index to set byte * @param b is the new value. */ public void set(int index, int b) { assert index >= 0 : "index must be positive"; assert begin + index < begin + realSize : "index is too large"; bytes[begin + index] = (byte)b; } /** * Replace the byte array backing store with newBytes. This method is only referred to in * deprecated method RubyString.view(CharSequence). * * We deprecated this method because it ignore begin and if we * * @param newBytes */ @Deprecated public void replace(byte[] newBytes) { assert newBytes != null; this.bytes = newBytes; realSize = newBytes.length; } /** * Unsafe version of replace(int,int,ByteList). The contract is that these * unsafe versions will not make sure thet beg and len indices are correct. */ public void unsafeReplace(int beg, int len, ByteList nbytes) { unsafeReplace(beg, len, nbytes.bytes, nbytes.begin, nbytes.realSize); } /** * Unsafe version of replace(int,int,byte[]). The contract is that these * unsafe versions will not make sure thet beg and len indices are correct. */ public void unsafeReplace(int beg, int len, byte[] buf) { unsafeReplace(beg, len, buf, 0, buf.length); } /** * Unsafe version of replace(int,int,byte[],int,int). The contract is that these * unsafe versions will not make sure thet beg and len indices are correct. */ public void unsafeReplace(int beg, int len, byte[] nbytes, int index, int count) { grow(count - len); int newSize = realSize + count - len; System.arraycopy(bytes,beg+len,bytes,beg+count,realSize - (len+beg)); System.arraycopy(nbytes,index,bytes,beg,count); realSize = newSize; } /** * Note: This is as unsafe as unsafeReplace * * @param beg * @param len * @param nbytes */ public void replace(int beg, int len, ByteList nbytes) { replace(beg, len, nbytes.bytes, nbytes.begin, nbytes.realSize); } /** * Note: This is as unsafe as unsafeReplace * @param beg * @param len * @param buf */ public void replace(int beg, int len, byte[] buf) { replace(beg, len, buf, 0, buf.length); } /** * Note: This is as unsafe as unsafeReplace * @param beg * @param len * @param nbytes * @param index * @param count */ public void replace(int beg, int len, byte[] nbytes, int index, int count) { unsafeReplace(beg, len, nbytes, index, count); } public void insert(int index, int b) { grow(1); System.arraycopy(bytes, index, bytes, index + 1, realSize - index); bytes[index] = (byte)b; realSize++; } /** * Get the index of first occurrence of c in ByteList from the beginning of the ByteList. * * @param c byte to be looking for * @return the index of the byte or -1 if not found */ public int indexOf(int c) { return indexOf(c, 0); } /** * Get the index of first occurrence of c in ByteList from the pos offset of the ByteList. * * @param c byte to be looking for * @param pos off set from beginning of ByteList to look for byte * @return the index of the byte or -1 if not found */ public int indexOf(final int c, int pos) { // not sure if this is checked elsewhere, // didn't see it in RubyString. RubyString does // cast to char, so c will be >= 0. if (c > 255) return -1; final byte b = (byte)(c&0xFF); final int size = begin + realSize; final byte[] buf = bytes; pos += begin; for ( ; pos < size && buf[pos] != b ; pos++ ) ; return pos < size ? pos - begin : -1; } /** * Get the index of first occurrence of Bytelist find in this ByteList. * * @param find the ByteList to find * @return the index of the byte or -1 if not found */ public int indexOf(ByteList find) { return indexOf(find, 0); } /** * Get the index of first occurrence of Bytelist find in this ByteList starting at index i. * * @param find the ByteList to find * @param i the index to start from * @return the index of the byte or -1 if not found */ public int indexOf(ByteList find, int i) { return indexOf(bytes, begin, realSize, find.bytes, find.begin, find.realSize, i); } /** * Get the index of first occurrence of target in source using the offset and count parameters. * fromIndex can be used to start beyond zero on source. * * @return the index of the byte or -1 if not found */ static int indexOf(byte[] source, int sourceOffset, int sourceCount, byte[] target, int targetOffset, int targetCount, int fromIndex) { if (fromIndex >= sourceCount) return (targetCount == 0 ? sourceCount : -1); if (fromIndex < 0) fromIndex = 0; if (targetCount == 0) return fromIndex; byte first = target[targetOffset]; int max = sourceOffset + (sourceCount - targetCount); for (int i = sourceOffset + fromIndex; i <= max; i++) { if (source[i] != first) while (++i <= max && source[i] != first); if (i <= max) { int j = i + 1; int end = j + targetCount - 1; for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++); if (j == end) return i - sourceOffset; } } return -1; } /** * Get the index of last occurrence of c in ByteList from the end of the ByteList. * * @param c byte to be looking for * @return the index of the byte or -1 if not found */ public int lastIndexOf(int c) { return lastIndexOf(c, realSize - 1); } /** * Get the index of last occurrence of c in ByteList from the pos offset of the ByteList. * * @param c byte to be looking for * @param pos off set from end of ByteList to look for byte * @return the index of the byte or -1 if not found */ public int lastIndexOf(final int c, int pos) { // not sure if this is checked elsewhere, // didn't see it in RubyString. RubyString does // cast to char, so c will be >= 0. if (c > 255) return -1; final byte b = (byte)(c&0xFF); final int size = begin + realSize; pos += begin; final byte[] buf = bytes; if (pos >= size) { pos = size; } else { pos++; } for ( ; --pos >= begin && buf[pos] != b ; ) ; return pos - begin; } /** * Get the index of last occurrence of find in ByteList from the end of the ByteList. * * @param find ByteList to be looking for * @return the index of the byte or -1 if not found */ public int lastIndexOf(ByteList find) { return lastIndexOf(find, realSize); } /** * Get the index of last occurrence of find in ByteList from the end of the ByteList. * * @param find ByteList to be looking for * @param pos index from end of list to search from * @return the index of the byte or -1 if not found */ public int lastIndexOf(ByteList find, int pos) { return lastIndexOf(bytes, begin, realSize, find.bytes, find.begin, find.realSize, pos); } /** * Get the index of last occurrence of target in source using the offset and count parameters. * fromIndex can be used to start beyond zero on source. * * @return the index of the byte or -1 if not found */ static int lastIndexOf(byte[] source, int sourceOffset, int sourceCount, byte[] target, int targetOffset, int targetCount, int fromIndex) { int rightIndex = sourceCount - targetCount; if (fromIndex < 0) return -1; if (fromIndex > rightIndex) fromIndex = rightIndex; if (targetCount == 0) return fromIndex; int strLastIndex = targetOffset + targetCount - 1; byte strLastChar = target[strLastIndex]; int min = sourceOffset + targetCount - 1; int i = min + fromIndex; startSearchForLastChar: while (true) { while (i >= min && source[i] != strLastChar) i--; if (i < min) return -1; int j = i - 1; int start = j - (targetCount - 1); int k = strLastIndex - 1; while (j > start) { if (source[j--] != target[k--]) { i--; continue startSearchForLastChar; } } return start - sourceOffset + 1; } } public boolean startsWith(ByteList other, int toffset) { if (realSize == 0) return false; byte[]ta = bytes; int to = begin + toffset; byte[]pa = other.bytes; int po = other.begin; int pc = other.realSize; while (--pc >= 0) if (ta[to++] != pa[po++]) return false; return true; } /** * Does this ByteList start with the supplied ByteList? * * @param other is the bytelist to compare with * @return true is this ByteList starts with other */ public boolean startsWith(ByteList other) { return startsWith(other, 0); } /** * Does this ByteList end with the supplied ByteList? * * @param other is the bytelist to compare with * @return true is this ByteList starts with other */ public boolean endsWith(ByteList other) { return startsWith(other, realSize - other.realSize); } /** * Does this ByteList equal the other ByteList? * * @param other is the bytelist to compare with * @return true is this ByteList is the same */ @Override public boolean equals(Object other) { if (other instanceof ByteList) return equal((ByteList)other); return false; } /** * Does this ByteList equal the other ByteList? * * @param other is the bytelist to compare with * @return true is this ByteList is the same */ public boolean equal(ByteList other) { if (other == this) return true; if (hash != 0 && other.hash != 0 && hash != other.hash) return false; int first, last; if ((last = realSize) == other.realSize) { byte buf[] = bytes; byte otherBuf[] = other.bytes; // scanning from front and back simultaneously, meeting in // the middle. the object is to get a mismatch as quickly as // possible. alternatives might be: scan from the middle outward // (not great because it won't pick up common variations at the // ends until late) or sample odd bytes forward and even bytes // backward (I like this one, but it's more expensive for // strings that are equal; see sample_equals below). for (first = -1; --last > first && buf[begin + last] == otherBuf[other.begin + last] && ++first < last && buf[begin + first] == otherBuf[other.begin + first] ; ) ; return first >= last; } return false; } /** * an alternative to the new version of equals, should * detect inequality faster (in many cases), but is slow * in the case of equal values (all bytes visited), due to * using n+=2, n-=2 vs. ++n, --n while iterating over the array. */ public boolean sample_equals(Object other) { if (other == this) return true; if (other instanceof ByteList) { ByteList b = (ByteList) other; int first; int last; int size; byte[] buf; if ((size = realSize) == b.realSize) { // scanning from front and back simultaneously, sampling odd // bytes on the forward iteration and even bytes on the // reverse iteration. the object is to get a mismatch as quickly // as possible. for (buf = bytes, first = -1, last = (size + 1) & ~1 ; (last -= 2) >= 0 && buf[begin + last] == b.bytes[b.begin + last] && (first += 2) < size && buf[begin + first] == b.bytes[b.begin + first] ; ) ; return last < 0 || first == size; } } return false; } /** * This comparison matches MRI comparison of Strings (rb_str_cmp). * I wish we had memcmp right now... */ public int compareTo(Object other) { return cmp((ByteList)other); } /** * This comparison matches MRI comparison of Strings (rb_str_cmp). */ public int cmp(final ByteList other) { if (other == this) return 0; final int size = realSize; final int len = Math.min(size, other.realSize); int offset = -1; // a bit of VM/JIT weirdness here: though in most cases // performance is improved if array references are kept in // a local variable (saves an instruction per access, as I // [slightly] understand it), in some cases, when two (or more?) // arrays are being accessed, the member reference is actually // faster. this is one of those cases... for ( ; ++offset < len && bytes[begin + offset] == other.bytes[other.begin + offset]; ) ; if (offset < len) { return (bytes[begin + offset]&0xFF) > (other.bytes[other.begin + offset]&0xFF) ? 1 : -1; } return size == other.realSize ? 0 : size == len ? -1 : 1; } /** * Do a case insensitive comparison with other ByteList with return types similiar to compareTo. * * @param other the ByteList to compare * @return -1, 0, or 1 */ public int caseInsensitiveCmp(final ByteList other) { if (other == this) return 0; final int size = realSize; final int len = Math.min(size, other.realSize); final int other_begin = other.begin; final byte[] other_bytes = other.bytes; for (int offset = -1; ++offset < len;) { int myCharIgnoreCase = AsciiTables.ToLowerCaseTable[bytes[begin + offset] & 0xff]; int otherCharIgnoreCase = AsciiTables.ToLowerCaseTable[other_bytes[other_begin + offset] & 0xff]; if (myCharIgnoreCase < otherCharIgnoreCase) { return -1; } else if (myCharIgnoreCase > otherCharIgnoreCase) { return 1; } } return size == other.realSize ? 0 : size == len ? -1 : 1; } /** * Returns the internal byte array. This is unsafe unless you know what you're * doing. But it can improve performance for byte-array operations that * won't change the array. * * @return the internal byte array */ public byte[] unsafeBytes() { return bytes; } /** * Get a copy of the bytes referenced by this ByteList. It will make an optimal copy and not * carry along unused bytes from COW sharing. * * @return a copy of the bytes. */ public byte[] bytes() { byte[] newBytes = new byte[realSize]; System.arraycopy(bytes, begin, newBytes, 0, realSize); return newBytes; } /** * First index of the backing array that contains data for the ByteList. Note that we have * copy-on-write (COW) semantics which means sharing the same backing store will yield different * begin and size values while using the same byte[]. * * @return the index */ public int begin() { return begin; } /** * Grow the ByteList by increaseRequested bytes. A value <0 will be a no-op. * * @param increaseRequested number of bytes to grow */ private void grow(int increaseRequested) { if (increaseRequested < 0) return; int newSize = realSize + increaseRequested; if (newSize > bytes.length) { byte[] newBytes = new byte[newSize + (newSize >> 1)]; if (bytes.length != 0) System.arraycopy(bytes, 0, newBytes, 0, realSize); bytes = newBytes; } } /** * @see Object#hashCode() */ @Override public int hashCode() { if (hash != 0) return hash; int key = 0; int index = begin; final int end = begin + realSize; while (index < end) { // equivalent of: key = key * 65599 + byte; key = ((key << 16) + (key << 6) - key) + (int)(bytes[index++]); // & 0xFF ? } key = key + (key >> 5); return hash = key; } /** * Remembers toString value, which is expensive for StringBuffer. * * @return an ISO-8859-1 representation of the byte list */ @Override public String toString() { if (stringValue == null) { stringValue = decode(bytes, begin, realSize, "ISO-8859-1"); } return stringValue; } /** * Create a bytelist with ISO_8859_1 encoding from the provided CharSequence. * * @param s the source for new ByteList * @return the new ByteList */ public static ByteList create(CharSequence s) { return new ByteList(plain(s),false); } /** * Create a byte[] from a CharSequence assuming a raw/ISO-8859-1 encoding * * @param s the CharSequence to convert * @return a byte[] */ public static byte[] plain(CharSequence s) { if (s instanceof String) return encode(s, "ISO-8859-1"); // Not a String...get it the slow way byte[] bytes = new byte[s.length()]; for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte) s.charAt(i); } return bytes; } /** * Create a byte[] from a char[] assuming a raw/ISO-8859-1 encoding * * @param s the CharSequence to convert * @return a byte[] */ public static byte[] plain(char[] s) { byte[] bytes = new byte[s.length]; for (int i = 0; i < s.length; i++) { bytes[i] = (byte) s[i]; } return bytes; } /** * Create a char[] from a byte[] assuming a raw/ISO-8859-1 encoding * * @param b the source byte[] * @param start index to start converting to char's * @param length how many bytes to convert to char's * @return a byte[] */ public static char[] plain(byte[] b, int start, int length) { assert b != null : "byte array cannot be null"; assert start >= 0 && start + length <= b.length : "Invalid start or start+length too long"; char[] chars = new char[length]; for (int i = 0; i < length; i++) { chars[i] = (char) (b[start + i] & 0xFF); } return chars; } /** * Create a char[] from a byte[] assuming a raw/ISO-8859-1 encoding * * @param b the source byte[] * @return a byte[] */ public static char[] plain(byte[] b) { assert b != null : "byte array cannot be null"; char[] chars = new char[b.length]; for (int i = 0; i < b.length; i++) { chars[i] = (char) (b[i] & 0xFF); } return chars; } // Work around bad charset handling in JDK. See // http://halfbottle.blogspot.com/2009/07/charset-continued-i-wrote-about.html private static final ConcurrentMap charsetsByAlias = new ConcurrentHashMap(); /** * Decode byte data into a String with the supplied charsetName. * * @param data to be decoded * @param offset where to start decoding from in data * @param length how many bytes to decode from data * @param charsetName used to make the resulting String * @return the new String */ public static String decode(byte[] data, int offset, int length, String charsetName) { return lookup(charsetName).decode(ByteBuffer.wrap(data, offset, length)).toString(); } /** * Decode byte data into a String with the supplied charsetName. * * @param data to be decoded * @param charsetName used to make the resulting String * @return the new String */ public static String decode(byte[] data, String charsetName) { return lookup(charsetName).decode(ByteBuffer.wrap(data)).toString(); } /** * Encode CharSequence into a set of bytes based on the charsetName. * * @param data to be encoded * @param charsetName used to extract the resulting bytes * @return the new byte[] */ public static byte[] encode(CharSequence data, String charsetName) { return lookup(charsetName).encode(CharBuffer.wrap(data)).array(); } private static Charset lookup(String alias) { Charset cs = charsetsByAlias.get(alias); if (cs == null) { cs = Charset.forName(alias); charsetsByAlias.putIfAbsent(alias, cs); } return cs; } /** * Pretend byte array is raw and each byte is also the character value * * @param ix is the index you want * @return */ public char charAt(int ix) { return (char)(this.bytes[begin + ix] & 0xFF); } /** * Create subSequence of this array between start and end offsets * * @param start index for beginning of subsequence * @param end index for end of subsequence * @return a new ByteList/CharSequence */ public CharSequence subSequence(int start, int end) { return new ByteList(this, start, end - start); } /** * Are these two byte arrays similiar (semantics similiar too compareTo). This is slightly * special in that it will only compare the same number of bytes based on the lesser of the two * lengths. * * @return -1, 0, 1 */ public static int memcmp(final byte[] first, final int firstStart, final int firstLen, final byte[] second, final int secondStart, final int secondLen) { if (first == second) return 0; final int len = Math.min(firstLen,secondLen); int offset = -1; for ( ; ++offset < len && first[firstStart + offset] == second[secondStart + offset]; ) ; if (offset < len) { return (first[firstStart + offset]&0xFF) > (second[secondStart + offset]&0xFF) ? 1 : -1; } return firstLen == secondLen ? 0 : firstLen == len ? -1 : 1; } /** * Are these two byte arrays similiar (semantics similiar too compareTo). * * @return -1, 0, 1 */ public static int memcmp(final byte[] first, final int firstStart, final byte[] second, final int secondStart, final int len) { if (first == second) return 0; int offset = -1; for ( ; ++offset < len && first[firstStart + offset] == second[secondStart + offset]; ) ; if (offset < len) { return (first[firstStart + offset]&0xFF) > (second[secondStart + offset]&0xFF) ? 1 : -1; } return 0; } /** * @return the bytes */ public final byte[] getUnsafeBytes() { return bytes; } /** * @param bytes the bytes to set */ public final void setUnsafeBytes(byte[] bytes) { assert bytes != null; this.bytes = bytes; } /** * @return the begin */ public final int getBegin() { return begin; } /** * @param begin the begin to set */ public final void setBegin(int begin) { assert begin >= 0; this.begin = begin; } /** * @return the realSize */ public final int getRealSize() { return realSize; } /** * @param realSize the realSize to set */ public final void setRealSize(int realSize) { assert realSize >= 0; this.realSize = realSize; } /** * @return the encoding */ public final Encoding getEncoding() { return encoding; } /** * @param encoding the encoding to set */ public final void setEncoding(Encoding encoding) { assert encoding != null; this.encoding = encoding; } }