content: /*
   * Copyright (C) 2016-2023 ActionTech.
   * based on code by MyCATCopyrightHolder Copyright (c) 2013, OpenCloudDB/MyCAT.
   * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher.
   */
  package com.actiontech.dble.backend.mysql;
  
  import java.io.UnsupportedEncodingException;
  import java.math.BigDecimal;
  import java.sql.Time;
  import java.sql.Timestamp;
  import java.util.Arrays;
  import java.util.Calendar;
  
  /**
   * @author mycat
   */
  public class MySQLMessage {
      public static final long NULL_LENGTH = -1;
      private static final byte[] EMPTY_BYTES = new byte[0];
  
      private final byte[] data;
      private final int length;
      private int position;
  
      public MySQLMessage(byte[] data) {
          this.data = data;
          this.length = data.length;
          this.position = 0;
      }
  
      public int length() {
          return length;
      }
  
      public int position() {
          return position;
      }
  
      public void position(int i) {
          this.position = i;
      }
  
      public byte[] bytes() {
          return data;
      }
  
      public void move(int i) {
          position += i;
      }
  
      public boolean hasRemaining() {
          return length > position;
      }
  
      public byte read(int i) {
          return data[i];
      }
  
      public byte read() {
          return data[position++];
      }
  
      public int readUB2() {
          final byte[] b = this.data;
          int i = b[position++] & 0xff;
          i |= (b[position++] & 0xff) << 8;
          return i;
      }
  
      public int readUB3() {
          final byte[] b = this.data;
          int i = b[position++] & 0xff;
          i |= (b[position++] & 0xff) << 8;
          i |= (b[position++] & 0xff) << 16;
          return i;
      }
  
      public long readUB4() {
          final byte[] b = this.data;
          long l = (long) (b[position++] & 0xff);
          l |= (long) (b[position++] & 0xff) << 8;
          l |= (long) (b[position++] & 0xff) << 16;
          l |= (long) (b[position++] & 0xff) << 24;
          return l;
      }
  
      public int readInt() {
          final byte[] b = this.data;
          int i = b[position++] & 0xff;
          i |= (b[position++] & 0xff) << 8;
          i |= (b[position++] & 0xff) << 16;
          i |= (b[position++] & 0xff) << 24;
          return i;
      }
  
      public float readFloat() {
          return Float.intBitsToFloat(readInt());
      }
  
      public long readLong() {
          final byte[] b = this.data;
          long l = (long) (b[position++] & 0xff);
          l |= (long) (b[position++] & 0xff) << 8;
          l |= (long) (b[position++] & 0xff) << 16;
          l |= (long) (b[position++] & 0xff) << 24;
          l |= (long) (b[position++] & 0xff) << 32;
          l |= (long) (b[position++] & 0xff) << 40;
          l |= (long) (b[position++] & 0xff) << 48;
          l |= (long) (b[position++] & 0xff) << 56;
          return l;
      }
  
      public double readDouble() {
          return Double.longBitsToDouble(readLong());
      }
  
      public long readLength() {
          int size = data[position++] & 0xff;
          switch (size) {
              case 251:
                  return NULL_LENGTH;
              case 252:
                  return readUB2();
              case 253:
                  return readUB3();
              case 254:
                  return readLong();
              default:
                  return size;
          }
      }
  
      public byte[] readBytes() {
          if (position >= length) {
              return EMPTY_BYTES;
          }
          byte[] ab = new byte[length - position];
          System.arraycopy(data, position, ab, 0, ab.length);
          position = length;
          return ab;
      }
  
  
      public byte[] readBytes(int size) {
          byte[] ab = new byte[size];
          System.arraycopy(data, position, ab, 0, size);
          position += size;
          return ab;
      }
  
      public byte[] readBytesWithNull() {
          final byte[] b = this.data;
          if (position >= length) {
              return EMPTY_BYTES;
          }
          int offset = -1;
          for (int i = position; i < length; i++) {
              if (b[i] == 0) {
                  offset = i;
                  break;
              }
          }
          switch (offset) {
              case -1:
                  byte[] ab1 = new byte[length - position];
                  System.arraycopy(b, position, ab1, 0, ab1.length);
                  position = length;
                  return ab1;
              case 0:
                  position++;
                  return EMPTY_BYTES;
              default:
                  byte[] ab2 = new byte[offset - position];
                  System.arraycopy(b, position, ab2, 0, ab2.length);
                  position = offset + 1;
                  return ab2;
          }
      }
  
      public byte[] readBytesWithLength() {
          int size = (int) readLength();
          if (size == NULL_LENGTH) {
              return null;
          }
          if (size <= 0) {
              return EMPTY_BYTES;
          }
  
          byte[] ab = new byte[size];
          System.arraycopy(data, position, ab, 0, ab.length);
          position += size;
          return ab;
      }
  
      public String readString(String charset) throws UnsupportedEncodingException {
          if (position >= length) {
              return null;
          }
          String javaCharset = CharsetUtil.getJavaCharset(charset);
          String s = new String(data, position, length - position, javaCharset);
          position = length;
          return s;
      }
  
      public String readStringWithNull() {
          final byte[] b = this.data;
          if (position >= length) {
              return null;
          }
          int offset = -1;
          for (int i = position; i < length; i++) {
              if (b[i] == 0) {
                  offset = i;
                  break;
              }
          }
          if (offset == -1) {
              String s = new String(b, position, length - position);
              position = length;
              return s;
          }
          if (offset > position) {
              String s = new String(b, position, offset - position);
              position = offset + 1;
              return s;
          } else {
              position++;
              return null;
          }
      }
  
      private String readStringWithLength() {
          int size = (int) readLength();
          if (size <= 0) {
              return null;
          }
          String s = new String(data, position, size);
          position += size;
          return s;
      }
  
      public String readStringWithLength(String charset) throws UnsupportedEncodingException {
          int size = (int) readLength();
          String s = new String(data, position, size, charset);
          position += size;
          return s;
      }
  
      public java.sql.Time readTime() {
          move(6);
          int hour = read();
          int minute = read();
          int second = read();
          Calendar cal = getLocalCalendar();
          cal.set(0, 0, 0, hour, minute, second);
          return new Time(cal.getTimeInMillis());
      }
  
      public java.util.Date readDate() {
          byte size = read();
          if (size >= 4) {
              int year = readUB2();
              byte month = read();
              byte date = read();
              int hour = 0;
              int minute = 0;
              int second = 0;
              Calendar cal = getLocalCalendar();
              cal.set(year, --month, date, hour, minute, second);
              return new java.sql.Date(cal.getTimeInMillis());
          } else {
              return java.sql.Date.valueOf("0000-00-00 00:00:00.000000");
          }
      }
  
      public java.util.Date readDateTime() {
          byte size = read();
          int year = readUB2();
          byte month = read();
          byte date = read();
          int hour = read();
          int minute = read();
          int second = read();
          if (size == 11) {
              long nanos = readUB4();
              Calendar cal = getLocalCalendar();
              cal.set(year, --month, date, hour, minute, second);
              Timestamp time = new Timestamp(cal.getTimeInMillis());
              time.setNanos((int) nanos);
              return time;
          } else {
              Calendar cal = getLocalCalendar();
              cal.set(year, --month, date, hour, minute, second);
              return new java.sql.Date(cal.getTimeInMillis());
          }
      }
  
      public BigDecimal readBigDecimal() {
          String src = readStringWithLength();
          return src == null ? null : new BigDecimal(src);
      }
  
      public String toString() {
          return Arrays.toString(data);
      }
  
      private static final ThreadLocal<Calendar> LOCAL_CALENDAR = new ThreadLocal<>();
  
      private static Calendar getLocalCalendar() {
          Calendar cal = LOCAL_CALENDAR.get();
          if (cal == null) {
              cal = Calendar.getInstance();
              LOCAL_CALENDAR.set(cal);
          }
          return cal;
      }
  
  
      public byte[] readBytesWithInputLength(int size) {
          byte[] ab = new byte[size];
          System.arraycopy(data, position, ab, 0, ab.length);
          position += size;
          return ab;
      }
  
  }
  