mirror of https://github.com/apache/cassandra
validation that generates less garbage. patch by gdusbabek, reviewed by jbellis. CASSANDRA-1814
git-svn-id: https://svn.apache.org/repos/asf/cassandra/branches/cassandra-0.7@1043985 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
55d8d53876
commit
a4ebfe322c
|
|
@ -48,10 +48,7 @@ public abstract class AbstractType implements Comparator<ByteBuffer>
|
|||
}
|
||||
|
||||
/* 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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,4 +51,9 @@ public class BytesType extends AbstractType
|
|||
{
|
||||
return ByteBuffer.wrap(source.getBytes());
|
||||
}
|
||||
|
||||
public void validate(ByteBuffer bytes) throws MarshalException
|
||||
{
|
||||
// all bytes are legal.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,4 +139,9 @@ public final class IntegerType extends AbstractType
|
|||
|
||||
return ByteBuffer.wrap(integerType.toByteArray());
|
||||
}
|
||||
|
||||
public void validate(ByteBuffer bytes) throws MarshalException
|
||||
{
|
||||
// no invalid integers.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,4 +63,11 @@ public class LexicalUUIDType extends AbstractType
|
|||
{
|
||||
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.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,4 +51,9 @@ public class LocalByPartionerType<T extends Token> extends AbstractType
|
|||
{
|
||||
return partitioner.decorateKey(o1).compareTo(partitioner.decorateKey(o2));
|
||||
}
|
||||
|
||||
public void validate(ByteBuffer bytes) throws MarshalException
|
||||
{
|
||||
throw new IllegalStateException("You shouldn't be validating this.");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,4 +81,10 @@ public class LongType extends AbstractType
|
|||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,4 +98,18 @@ public class TimeUUIDType extends AbstractType
|
|||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue