package water.parser; import water.fvec.C16Chunk; import java.util.UUID; /** * Utility class for parsing UUIDs. * * This class creates a hash value of two longs from * a {@link BufferedString} containing a correct UUID. * */ public class ParseUUID { /** * Confirms whether the provided UUID is considered * valid. * * @param str * @return TRUE if str represents a valid UUID */ public static boolean isUUID(BufferedString str) { boolean res; int old = str.getOffset(); attemptUUIDParseLow(str); attemptUUIDParseHigh(str); res = str.getOffset() != -1; str.setOff(old); return res; } /** * Attempts to parse the provided {@link BufferedString} as * a UUID into hash value in two longs. * * Warning: as written, this method does modify the state * of the passed in BufferedString. * * @param str * @return A parsed UUID, or a null if parsing failed. */ public static UUID attemptUUIDParse(BufferedString str) { Long lo = attemptUUIDParseLow(str); Long hi = attemptUUIDParseHigh(str); return (str.getOffset() == -1) ? null : buildUUID(lo, hi); } private static UUID buildUUID(Long lo, Long hi) { return (lo == null || hi == null || (C16Chunk.isNA(lo, hi))) ? null : new UUID(hi, lo); } // -------------------------------- // Parse XXXXXXXX-XXXX-XXXX and return an arbitrary long, or set str.off==-1 // (and return Long.MIN_VALUE but this is a valid long return value). private static Long attemptUUIDParseLow(BufferedString str) { final byte[] buf = str.getBuffer(); int i=str.getOffset(); if( i+36 > buf.length ) return markBad(str); long lo=0; lo = get2(lo,buf,(i+=2)-2); lo = get2(lo,buf,(i+=2)-2); lo = get2(lo,buf,(i+=2)-2); lo = get2(lo,buf,(i+=2)-2); if( buf[i++]!='-' ) return markBad(str); lo = get2(lo,buf,(i+=2)-2); lo = get2(lo,buf,(i+=2)-2); if( buf[i++]!='-' ) return markBad(str); lo = get2(lo,buf,(i+=2)-2); return attemptUUIDParseEnd(str, lo, buf, i); } // Parse -XXXX-XXXXXXXXXXXX and return an arbitrary long, or set str.off==-1 // (and return null). public static Long attemptUUIDParseHigh(BufferedString str) { final byte[] buf = str.getBuffer(); int i=str.getOffset(); if ( i== -1 ) return markBad(str); long hi=0; if( buf[i++]!='-' ) return markBad(str); hi = get2(hi,buf,(i+=2)-2); hi = get2(hi,buf,(i+=2)-2); if( buf[i++]!='-' ) return markBad(str); hi = get2(hi,buf,(i+=2)-2); hi = get2(hi,buf,(i+=2)-2); hi = get2(hi,buf,(i+=2)-2); hi = get2(hi,buf,(i+=2)-2); hi = get2(hi,buf,(i+=2)-2); return attemptUUIDParseEnd(str, hi, buf, i); } private static Long attemptUUIDParseEnd(BufferedString str, long lo, byte[] buf, int i) { // Can never equal MIN_VALUE since only parsed 14 of 16 digits, unless // failed parse already. if( lo == Long.MIN_VALUE ) return markBad(str); // If the last 2 digits are 0x8000 and the first 14 are all 0's then might // legitimately parse MIN_VALUE, need to check for it special. str.setOff(i+2); // Mark as parsed if( lo == 0x80000000000000L && buf[i]=='0' && buf[i+1]=='0' ) return Long.MIN_VALUE; // Valid MIN_VALUE parse // First 14 digits are a random scramble; will never equal MIN_VALUE result // unless we have a failed parse in the last 2 digits lo = get2(lo,buf,i); return (lo == Long.MIN_VALUE || // broken UUID already, OR // too many valid UUID digits (i + 2 < buf.length && hdigit(0, buf[i + 2]) != Long.MIN_VALUE)) ? null : lo; } private static long get2( long x, byte[] buf, int i ) { if( x == Long.MIN_VALUE ) return x; x = hdigit(x,buf[i++]); x = hdigit(x,buf[i++]); return x; } private static long hdigit( long x, byte b ) { if( x == Long.MIN_VALUE ) return Long.MIN_VALUE; else if( b >= '0' && b <= '9' ) return (x<<4)+b-'0'; else if( b >= 'A' && b <= 'F' ) return (x<<4)+b-'A'+10; else if( b >= 'a' && b <= 'f' ) return (x<<4)+b-'a'+10; else return Long.MIN_VALUE; } private static Long markBad(BufferedString str) { str.setOff(-1); return null; } }