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;


Reply via email to