Author: gdusbabek
Date: Thu Dec 9 14:38:50 2010
New Revision: 1043985
URL: http://svn.apache.org/viewvc?rev=1043985&view=rev
Log:
validation that generates less garbage. patch by gdusbabek, reviewed by
jbellis. CASSANDRA-1814
Modified:
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/AbstractType.java
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/AsciiType.java
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/BytesType.java
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/IntegerType.java
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LocalByPartionerType.java
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LongType.java
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
cassandra/branches/cassandra-0.7/test/unit/org/apache/cassandra/db/marshal/TimeUUIDTypeTest.java
Modified:
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/AbstractType.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/AbstractType.java?rev=1043985&r1=1043984&r2=1043985&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/AbstractType.java
(original)
+++
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/AbstractType.java
Thu Dec 9 14:38:50 2010
@@ -48,10 +48,7 @@ public abstract class AbstractType imple
}
/* validate that the byte array is a valid sequence for the type we are
supposed to be comparing */
- public void validate(ByteBuffer bytes)
- {
- getString(bytes);
- }
+ public abstract void validate(ByteBuffer bytes) throws MarshalException;
public Comparator<ByteBuffer> getReverseComparator()
{
Modified:
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/AsciiType.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/AsciiType.java?rev=1043985&r1=1043984&r2=1043985&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/AsciiType.java
(original)
+++
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/AsciiType.java
Thu Dec 9 14:38:50 2010
@@ -43,4 +43,15 @@ public class AsciiType extends BytesType
{
return ByteBuffer.wrap(source.getBytes(Charsets.US_ASCII));
}
+
+ public void validate(ByteBuffer bytes) throws MarshalException
+ {
+ // 0-127
+ for (int i = 0; i < bytes.remaining(); i++)
+ {
+ byte b = bytes.array()[bytes.arrayOffset() + bytes.position() +
i];
+ if (b < 0 || b > 127)
+ throw new MarshalException("Invalid byte for ascii: " +
Byte.toString(b));
+ }
+ }
}
Modified:
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/BytesType.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/BytesType.java?rev=1043985&r1=1043984&r2=1043985&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/BytesType.java
(original)
+++
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/BytesType.java
Thu Dec 9 14:38:50 2010
@@ -51,4 +51,9 @@ public class BytesType extends AbstractT
{
return ByteBuffer.wrap(source.getBytes());
}
+
+ public void validate(ByteBuffer bytes) throws MarshalException
+ {
+ // all bytes are legal.
+ }
}
Modified:
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/IntegerType.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/IntegerType.java?rev=1043985&r1=1043984&r2=1043985&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/IntegerType.java
(original)
+++
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/IntegerType.java
Thu Dec 9 14:38:50 2010
@@ -139,4 +139,9 @@ public final class IntegerType extends A
return ByteBuffer.wrap(integerType.toByteArray());
}
+
+ public void validate(ByteBuffer bytes) throws MarshalException
+ {
+ // no invalid integers.
+ }
}
Modified:
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java?rev=1043985&r1=1043984&r2=1043985&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java
(original)
+++
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java
Thu Dec 9 14:38:50 2010
@@ -63,4 +63,11 @@ public class LexicalUUIDType extends Abs
{
return ByteBuffer.wrap(UUIDGen.decompose(UUID.fromString(source)));
}
+
+ public void validate(ByteBuffer bytes) throws MarshalException
+ {
+ if (bytes.remaining() != 16 && bytes.remaining() != 0)
+ throw new MarshalException(String.format("LexicalUUID should be 16
or 0 bytes (%d)", bytes.remaining()));
+ // not sure what the version should be for this.
+ }
}
Modified:
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LocalByPartionerType.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LocalByPartionerType.java?rev=1043985&r1=1043984&r2=1043985&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LocalByPartionerType.java
(original)
+++
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LocalByPartionerType.java
Thu Dec 9 14:38:50 2010
@@ -51,4 +51,9 @@ public class LocalByPartionerType<T exte
{
return
partitioner.decorateKey(o1).compareTo(partitioner.decorateKey(o2));
}
+
+ public void validate(ByteBuffer bytes) throws MarshalException
+ {
+ throw new IllegalStateException("You shouldn't be validating this.");
+ }
}
Modified:
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LongType.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LongType.java?rev=1043985&r1=1043984&r2=1043985&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LongType.java
(original)
+++
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/LongType.java
Thu Dec 9 14:38:50 2010
@@ -81,4 +81,10 @@ public class LongType extends AbstractTy
return FBUtilities.toByteBuffer(longType);
}
+
+ public void validate(ByteBuffer bytes) throws MarshalException
+ {
+ if (bytes.remaining() != 8 && bytes.remaining() != 0)
+ throw new MarshalException(String.format("Expected 8 or 0 byte
long (%d)", bytes.remaining()));
+ }
}
Modified:
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java?rev=1043985&r1=1043984&r2=1043985&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java
(original)
+++
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java
Thu Dec 9 14:38:50 2010
@@ -98,4 +98,18 @@ public class TimeUUIDType extends Abstra
return ByteBuffer.wrap(UUIDGen.decompose(uuid));
}
+
+ public void validate(ByteBuffer bytes) throws MarshalException
+ {
+ if (bytes.remaining() != 16 && bytes.remaining() != 0)
+ throw new MarshalException(String.format("TimeUUID should be 16 or
0 bytes (%d)", bytes.remaining()));
+ ByteBuffer slice = bytes.slice();
+ // version is bits 4-7 of byte 6.
+ if (bytes.remaining() > 0)
+ {
+ slice.position(6);
+ if ((slice.get() & 0x0f) != 1)
+ throw new MarshalException("Invalid version for TimeUUID
type.");
+ }
+ }
}
Modified:
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/UTF8Type.java?rev=1043985&r1=1043984&r2=1043985&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
(original)
+++
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
Thu Dec 9 14:38:50 2010
@@ -49,4 +49,127 @@ public class UTF8Type extends BytesType
{
return ByteBuffer.wrap(source.getBytes(Charsets.UTF_8));
}
+
+ public void validate(ByteBuffer bytes) throws MarshalException
+ {
+ if (!UTF8Validator.validate(bytes.slice()))
+ throw new MarshalException("String didn't validate.");
+ }
+
+ static class UTF8Validator
+ {
+ enum State {
+ START,
+ TWO,
+ TWO_80,
+ THREE_a0bf,
+ THREE_80bf_1,
+ THREE_80bf_2,
+ FOUR_90bf,
+ FOUR_80bf_3,
+ };
+
+ // since we're not converting to java strings, we don't need to worry
about converting to surrogates.
+ // buf has already been sliced/duplicated.
+ static boolean validate(ByteBuffer buf)
+ {
+ int b = 0;
+ State state = State.START;
+ while (buf.remaining() > 0)
+ {
+ b = buf.get();
+ switch (state)
+ {
+ case START:
+ if (b >= 0)
+ {
+ // ascii, state stays start.
+ if (b > 127)
+ return false;
+ }
+ else if ((b >> 5) == -2)
+ {
+ // validate first byte of 2-byte char, 0xc2-0xdf
+ if (b == (byte) 0xc0)
+ // speical case: modified utf8 null is 0xc080.
+ state = State.TWO_80;
+ else if ((b & 0x1e) == 0)
+ return false;
+ state = State.TWO;
+ }
+ else if ((b >> 4) == -2)
+ {
+ // 3 bytes. first byte will be 0xe0 or 0xe1-0xef.
handling of second byte will differ.
+ // so 0xe0,0xa0-0xbf,0x80-0xbf or
0xe1-0xef,0x80-0xbf,0x80-0xbf.
+ if (b == (byte)0xe0)
+ state = State.THREE_a0bf;
+ else
+ state = State.THREE_80bf_2;
+ break;
+ }
+ else if ((b >> 3) == -2)
+ {
+ // 4 bytes. this is where the fun starts.
+ if (b == (byte)0xf0)
+ // 0xf0, 0x90-0xbf, 0x80-0xbf, 0x80-0xbf
+ state = State.FOUR_90bf;
+ else if (b == (byte)0xf4)
+ // 0xf4, 0x80-0xbf, 0x80-0xbf, 0x80-0xbf
+ state = State.FOUR_80bf_3;
+ else
+ // 0xf1-0xf3, 0x80-0xbf, 0x80-0xbf, 0x80-0xbf
+ state = State.FOUR_80bf_3;
+ break;
+ }
+ else
+ return false; // malformed.
+ break;
+ case TWO:
+ // validate second byte of 2-byte char, 0x80-0xbf
+ if ((b & 0xc0) != 0x80)
+ return false;
+ state = State.START;
+ break;
+ case TWO_80:
+ if (b != (byte)0x80)
+ return false;
+ state = State.START;
+ break;
+ case THREE_a0bf:
+ if ((b & 0xe0) == 0x80)
+ return false;
+ state = State.THREE_80bf_1;
+ break;
+ case THREE_80bf_1:
+ // expecting 0x80-0xbf
+ if ((b & 0xc0) != 0x80)
+ return false;
+ state = State.START;
+ break;
+ case THREE_80bf_2:
+ // expecting 0x80-bf and then another of the same.
+ if ((b & 0xc0) != 0x80)
+ return false;
+ state = State.THREE_80bf_1;
+ break;
+ case FOUR_90bf:
+ // expecting 0x90-bf. 2nd byte of 4byte sequence.
after that it should degrade to 80-bf,80-bf (like 3byte seq).
+ if ((b & 0x30) == 0)
+ return false;
+ state = State.THREE_80bf_2;
+ break;
+ case FOUR_80bf_3:
+ // expecting 0x80-bf 3 times. degenerates to
THREE_80bf_2.
+ if ((b & 0xc0) != 0x80)
+ return false;
+ state = State.THREE_80bf_2;
+ break;
+ default:
+ return false; // invalid state.
+ }
+ }
+ // if state != start, we've got underflow. that's an error.
+ return state == State.START;
+ }
+ }
}
Modified:
cassandra/branches/cassandra-0.7/test/unit/org/apache/cassandra/db/marshal/TimeUUIDTypeTest.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.7/test/unit/org/apache/cassandra/db/marshal/TimeUUIDTypeTest.java?rev=1043985&r1=1043984&r2=1043985&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.7/test/unit/org/apache/cassandra/db/marshal/TimeUUIDTypeTest.java
(original)
+++
cassandra/branches/cassandra-0.7/test/unit/org/apache/cassandra/db/marshal/TimeUUIDTypeTest.java
Thu Dec 9 14:38:50 2010
@@ -40,6 +40,8 @@ public class TimeUUIDTypeTest
UUID a = generator.generateTimeBasedUUID();
UUID b = new UUID(a.asByteArray());
+ timeUUIDType.validate(ByteBuffer.wrap(a.asByteArray()));
+ timeUUIDType.validate(ByteBuffer.wrap(b.asByteArray()));
assertEquals(0, timeUUIDType.compare(ByteBuffer.wrap(a.asByteArray()),
ByteBuffer.wrap(b.asByteArray())));
}
@@ -50,6 +52,10 @@ public class TimeUUIDTypeTest
UUID b = generator.generateTimeBasedUUID();
UUID c = generator.generateTimeBasedUUID();
+ timeUUIDType.validate(ByteBuffer.wrap(a.asByteArray()));
+ timeUUIDType.validate(ByteBuffer.wrap(b.asByteArray()));
+ timeUUIDType.validate(ByteBuffer.wrap(c.asByteArray()));
+
assert timeUUIDType.compare(ByteBuffer.wrap(a.asByteArray()),
ByteBuffer.wrap(b.asByteArray())) < 0;
assert timeUUIDType.compare(ByteBuffer.wrap(b.asByteArray()),
ByteBuffer.wrap(c.asByteArray())) < 0;
assert timeUUIDType.compare(ByteBuffer.wrap(a.asByteArray()),
ByteBuffer.wrap(c.asByteArray())) < 0;
@@ -61,6 +67,10 @@ public class TimeUUIDTypeTest
UUID a = generator.generateTimeBasedUUID();
UUID b = generator.generateTimeBasedUUID();
UUID c = generator.generateTimeBasedUUID();
+
+ timeUUIDType.validate(ByteBuffer.wrap(a.asByteArray()));
+ timeUUIDType.validate(ByteBuffer.wrap(b.asByteArray()));
+ timeUUIDType.validate(ByteBuffer.wrap(c.asByteArray()));
assert timeUUIDType.compare(ByteBuffer.wrap(c.asByteArray()),
ByteBuffer.wrap(b.asByteArray())) > 0;
assert timeUUIDType.compare(ByteBuffer.wrap(b.asByteArray()),
ByteBuffer.wrap(a.asByteArray())) > 0;