package me.xcoding.opencdc.mysql.protocol.column;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.Calendar;
import me.xcoding.opencdc.mysql.ColumnType;
import me.xcoding.opencdc.mysql.protocol.BasicReader;
/**
*
* @author Teny Zh(zh.Teny.1@gmail.com)
* @see http://www.mysqlsystems.com/mysql_source_analytics/html/d3/d06/classTable__map__log__event.html
*
*/
public interface BinaryProtocolValue {
}
interface ValueParser {
Object valueOf(int length, BasicReader reader);
}
class MDecimal implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
throw new BinProtoValParserException();
}
}
class MTiny implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
return reader.readFixedIntS1();
}
}
class MShort implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
return reader.readFixedIntS2();
}
}
class MLong implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
return reader.readFixedIntT4();
}
}
class MFloat implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
return Float.intBitsToFloat(reader.readFixedIntT4());
}
}
class MDouble implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
long v = reader.readFixedIntT8();
return Double.longBitsToDouble(v);
}
}
class MNull implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
throw new BinProtoValParserException("not support this type!");
}
}
class MTimestamp implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
return new Timestamp((reader.readFixedIntT4() & BasicReader._8F )* 1000);
}
}
class MLongLong implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
return reader.readFixedIntT8();
}
}
class MInt24 implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
return reader.readFixedIntS3();
}
}
class MDate implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
// @Refer to Open Replicator & tungsten replicator
int value = reader.readFixedIntT3();
final int d = value % 32; value >>>= 5;
final int m = value % 16;
final int y = value >> 4;
final Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(y, m - 1, d);
return new java.sql.Date(cal.getTimeInMillis());
}
}
class MTime implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
// @refer to Open Replicator & tungsten replicator
int time = reader.readFixedIntT3();
final int s = (int)(time % 100); time /= 100;
final int m = (int)(time % 100);
final int h = (int)(time / 100);
final Calendar c = Calendar.getInstance();
c.set(1970, 0, 1, h, m, s);
c.set(Calendar.MILLISECOND, 0);
return new java.sql.Time(c.getTimeInMillis());
}
}
class MDateTime implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
// @refer to Open Replicator & tungsten replicator
long value = reader.readFixedIntT8();
final int second = (int)(value % 100); value /= 100;
final int minute = (int)(value % 100); value /= 100;
final int hour = (int)(value % 100); value /= 100;
final int day = (int)(value % 100); value /= 100;
final int month = (int)(value % 100);
final int year = (int)(value / 100);
final Calendar c = Calendar.getInstance();
c.set(year, month - 1, day, hour, minute, second);
c.set(Calendar.MILLISECOND, 0);
return c.getTime();
}
}
class MYear implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
return 1900 + reader.readFixedIntT1();
}
}
/**
* This enumeration value is only used internally and cannot exist in a binlog.
* @author Teny Zh(zh.Teny.1@gmail.com)
*
*/
class MNewDate implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
throw new BinProtoValParserException();
}
}
class MVarchar implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
int v = length > 0x0FF ? reader.readFixedIntT2() : reader.readFixedIntT1();
return reader.readStringVarLen(v);
}
}
class MBit implements ValueParser {
private static final int[] mask = new int[]{1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7};
@Override
public Object valueOf(int length, BasicReader reader) {
// A 1 byte unsigned int representing the length in bits of the bitfield (0 to 64),
// followed by a 1 byte unsigned int representing the number of bytes occupied by the bitfield.
// The number of bytes is either int((length+7)/8) or int(length/8).
final int bitLength = ((length >> 8) * 8) + (length & 0xff);
final int byteLength = (bitLength + 7) / 8;
final byte[] bytes = reader.readBytesVarLen(byteLength);
int offset = 0;
boolean[] bitmap = new boolean[length];
for(int i=byteLength; i>0; ) { // big endian
byte b = bytes[--i];
bitmap[offset++] = ((b & mask[0]) == 0);
bitmap[offset++] = ((b & mask[1]) == 0);
bitmap[offset++] = ((b & mask[2]) == 0);
bitmap[offset++] = ((b & mask[3]) == 0);
bitmap[offset++] = ((b & mask[4]) == 0);
bitmap[offset++] = ((b & mask[5]) == 0);
bitmap[offset++] = ((b & mask[6]) == 0);
bitmap[offset++] = ((b & mask[7]) == 0);
}
return bitmap;
}
}
class MTimestamp2 implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
final long time = reader.readVarLenLongU(4) * 1000;
System.out.println(time);
final int nanos = reader.readVarLenIntU((length + 1) /2);
Timestamp ts = new Timestamp(time);
ts.setNanos(nanos);
return ts;
}
}
class MDateTime2 implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
// @refer to Open Replicator & tungsten replicator
final long time = reader.readVarLenLongU(5);
final int nanos = reader.readVarLenIntU((length + 1) /2);
final long x = (time >> 22) & 0x1FFFFL;
final int year = (int)(x / 13);
final int month = (int)(x % 13);
final int day = ((int)(time >> 17)) & 0x1F;
final int hour = ((int)(time >> 12)) & 0x1F;
final int minute = ((int)(time >> 6)) & 0x3F;
final int second = ((int)(time >> 0)) & 0x3F;
final Calendar c = Calendar.getInstance();
c.set(year, month - 1, day, hour, minute, second);
c.set(Calendar.MILLISECOND, 0);
final long millis = c.getTimeInMillis();
return new java.util.Date(millis + (nanos / 1000000));
}
}
class MTime2 implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
// @refer to Open Replicator & tungsten replicator
final int time = reader.readVarLenIntU(3);
final int nanos = reader.readVarLenIntU((length + 1) / 2);
final int h = (time >> 12) & 0x3FF;
final int m = (time >> 6) & 0x3F;
final int s = (time >> 0) & 0x3F;
final Calendar c = Calendar.getInstance();
c.set(1970, 0, 1, h, m, s);
c.set(Calendar.MILLISECOND, 0);
final long millis = c.getTimeInMillis();
return new java.sql.Time(millis + (nanos / 1000000));
}
}
class MNewDecimal implements ValueParser {
// private static final int DIG_MASK = 100000000;
private static final int DIG_BASE = 1000000000;
private static final int DIG_MAX = (DIG_BASE-1);
// private static final long DIG_BASE2 = ((long)DIG_BASE * (long)DIG_BASE);
private static final int DIG_PER_DEC1 = 0x09;
private static final int dig2bytes[]={0, 1, 1, 2, 2, 3, 3, 4, 4, 4};
private static final int powers10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
@Override
public Object valueOf(int length, BasicReader reader) {
// A 1 byte unsigned int representing the precision,
// followed by a 1 byte unsigned int representing the number of decimals.
final int precision = length & 0xFF;
final int scale = (length >> 8) & 0xFF;
final int decimal_bin_size = decimal_bin_size(precision, scale);
final byte[] buffer = reader.readBytesVarLen(decimal_bin_size);
return toDecimal(buffer, precision, scale);
}
/**
* @see dbsync
* @see mysql-5.6.19/strings/decimal.c - bin2decimal()
*/
public BigDecimal toDecimal(byte[] buffer, int precision, int scale) {
int begin = 0;
int from = begin;
int intg = precision - scale,
intg0 = intg / DIG_PER_DEC1, frac0 = scale / DIG_PER_DEC1,
intg0x = intg - intg0 * DIG_PER_DEC1, frac0x = scale - frac0 * DIG_PER_DEC1;
// intg1 = intg0 + (intg0x > 0 ? 1 : 0), frac1 = frac0 + (frac0x > 0 ? 1 : 0);
// int bin_size = decimal_bin_size(precision, scale);
// int bin_size = intg0 << 2 + dig2bytes[intg0x]+ frac0 << 2 + dig2bytes[frac0x];
final int mask = ((buffer[begin] & 0x80) == 0x80) ? 0 : -1;
final int len = ((mask != 0) ? 1 : 0) + ((intg != 0) ? intg : 1) + ((scale != 0) ? 0 : 1) + scale;
char[] buf = new char[len];
int pos = 0;
if(mask != 0) buf[pos++] = '-';
final byte[] d_copy = buffer;
d_copy[begin] ^= 0x80;
int mark = pos;
if(intg0x != 0) {
final int i = dig2bytes[intg0x];
int x = 0;
switch (i) {
case 1 : { x = d_copy[from]; break; }
case 2 : { x = (d_copy[from] << 8) | ( d_copy[from + 1] & 0xFF); break; }
case 3 : { x = (d_copy[from] << 16) | ((d_copy[from + 1] & 0xFF) << 8) | ( d_copy[from + 2] & 0xFF); break; }
case 4 : { x = (d_copy[from] << 24) | ((d_copy[from + 1] & 0xFF) << 16) | ((d_copy[from + 2] & 0xFF) << 8) | (d_copy[from + 3] & 0xFF); break; }
}
from += i;
x ^= mask;
if(x < 0 || x >= powers10[intg0x + 1]) {
for(int j=intg0x; j > 0; j--) {
final int divisor = powers10[j - 1];
final int y = x / divisor;
if(mark < pos || y != 0) buf[pos++] = (char) ('0' + y);
x -= y* divisor;
}
}
}
for (final int stop = from + intg0 << 2; from < stop; from += 4) {
int x = (d_copy[from] << 24) | ((d_copy[from + 1] & 0xFF) << 16)
| ((d_copy[from + 2] & 0xFF) << 8) | (d_copy[from + 3] & 0xFF);
x ^= mask;
if (x < 0 || x > DIG_MAX)
throw new BinProtoValParserException("bad format, x exceed: " + x + ", " + DIG_MAX);
if (x != 0) {
if (mark < pos) {
for (int i = DIG_PER_DEC1; i > 0; i--) {
final int divisor = powers10[i - 1];
final int y = x / divisor;
buf[pos++] = ((char) ('0' + y));
x -= y * divisor;
}
}
else {
for (int i = DIG_PER_DEC1; i > 0; i--) {
final int divisor = powers10[i - 1];
final int y = x / divisor;
if (mark < pos || y != 0)
buf[pos++] = ((char) ('0' + y));
x -= y * divisor;
}
}
}
else if (mark < pos) {
for (int i = DIG_PER_DEC1; i > 0; i--)
buf[pos++] = ('0');
}
}
if (mark == pos)
buf[pos++] = ('0');
if (scale > 0) {
buf[pos++] = ('.');
mark = pos;
for (final int stop = from + (frac0 << 2); from < stop; from += 4) {
int x = 0; // getInt32BE(d_copy, from);
x ^= mask;
if (x < 0 || x > DIG_MAX) {
throw new BinProtoValParserException("bad format, x exceed: " + x + ", " + DIG_MAX);
}
if (x != 0) {
for (int i = DIG_PER_DEC1; i > 0; i--) {
final int divisor = powers10[i - 1];
final int y = x / divisor;
buf[pos++] = ((char) ('0' + y));
x -= y * divisor;
}
}
else {
for (int i = DIG_PER_DEC1; i > 0; i--)
buf[pos++] = ('0');
}
}
if (frac0x != 0) {
final int i = dig2bytes[frac0x];
int x = 0;
switch (i) {
case 1 : { x = d_copy[from]; break; }
case 2 : { x = (d_copy[from] << 8) | ( d_copy[from + 1] & 0xFF); break; }
case 3 : { x = (d_copy[from] << 16) | ((d_copy[from + 1] & 0xFF) << 8) | ( d_copy[from + 2] & 0xFF); break; }
case 4 : { x = (d_copy[from] << 24) | ((d_copy[from + 1] & 0xFF) << 16) | ((d_copy[from + 2] & 0xFF) << 8) | (d_copy[from + 3] & 0xFF); break; }
}
x ^= mask;
if (x != 0) {
final int dig = DIG_PER_DEC1 - frac0x;
x *= powers10[dig];
if (x < 0 || x > DIG_MAX)
throw new BinProtoValParserException( "bad format, x exceed: " + x + ", " + DIG_MAX);
for (int j = DIG_PER_DEC1; j > dig; j--) {
final int divisor = powers10[j - 1];
final int y = x / divisor;
buf[pos++] = ((char) ('0' + y));
x -= y * divisor;
}
}
}
if (mark == pos)
buf[pos++] = ('0');
}
d_copy[begin] ^= 0x80; /* restore sign */
String decimal = String.valueOf(buf, 0, pos);
return new BigDecimal(decimal);
}
public int decimal_bin_size(int precision, int scale) {
int intg=precision-scale,
intg0=intg/DIG_PER_DEC1, frac0=scale/DIG_PER_DEC1,
intg0x=intg-intg0*DIG_PER_DEC1, frac0x=scale-frac0*DIG_PER_DEC1;
return intg0 << 2 + dig2bytes[intg0x]+ frac0 << 2 + dig2bytes[frac0x];
}
}
/**
* This enumeration value is only used internally and cannot exist in a binlog.
* @author Teny Zh(zh.Teny.1@gmail.com)
*
*/
class MEnum implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
// FIXME DBSync
throw new BinProtoValParserException();
}
}
/**
* This enumeration value is only used internally and cannot exist in a binlog.
* @author Teny Zh(zh.Teny.1@gmail.com)
*
*/
class MSet implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
throw new BinProtoValParserException();
}
}
/**
* This enumeration value is only used internally and cannot exist in a binlog.
* @author Teny Zh(zh.Teny.1@gmail.com)
*
*/
class MTinyBlob implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
throw new BinProtoValParserException();
}
}
/**
* This enumeration value is only used internally and cannot exist in a binlog.
* @author Teny Zh(zh.Teny.1@gmail.com)
*
*/
class MMediumBlob implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
throw new BinProtoValParserException();
}
}
/**
* This enumeration value is only used internally and cannot exist in a binlog.
* @author Teny Zh(zh.Teny.1@gmail.com)
*
*/
class MLongBlob implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
throw new BinProtoValParserException();
}
}
class MBlob implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
int v = reader.readVarLenInt(length);
if(v < 0)
throw new BinProtoValParserException("this length less than 0!");
return reader.readBytesVarLen(v);
}
}
class MVarString implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
// FIXME fixed issue #66 ,binary类型在binlog中为var_string
int v = length > 256 ? (reader.readFixedIntT2()) : (reader.readFixedIntT1());
return reader.readStringVarLen(v);
}
}
class MString implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
// fix #37426
// @see http://bugs.mysql.com/bug.php?id=37426
int type = 0xFE;
/*
* 1 byte0 : type
* 2 byte1 : length
*/
final int meta0 = length & 0xFF;
final int meta1 = (length >> 8 ) & 0xFF;
if(meta1 > 0) {
if ((meta0 & 0x30) != 0x30) { // a long CHAR() field: see #37426
type = meta0 | 0x30;
length = meta1 | (((meta0 & 0x30) ^ 0x30) << 4);
} else {
switch (meta0) {
case ColumnType.MYSQL_TYPE_SET:
case ColumnType.MYSQL_TYPE_ENUM: {
type = meta0;
length = meta1;
break;
}
case ColumnType.MYSQL_TYPE_STRING: {
if(length > 256) { // FIXME
throw new BinProtoValParserException("length greater than 256!");
}
length = length > 256 ? reader.readFixedIntT2() : reader.readFixedIntT1();
return reader.readStringVarLen(length);
}
default:
throw new RuntimeException("assertion failed, unknown column type: " + type);
}
}
}
return ColumnValueParser.valueOf(type, (meta0 << 8) | meta1, reader);
}
}
class MGeometry implements ValueParser {
@Override
public Object valueOf(int length, BasicReader reader) {
// FIXME TODO
return null;
}
}