From 41f69f0769eada6b2c1613da1c7a94ab7fcaec75 Mon Sep 17 00:00:00 2001 From: linbin <495561397@qq.com> Date: Thu, 11 Apr 2024 18:04:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E7=A8=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 38 ++ .idea/.gitignore | 6 + pom.xml | 48 ++ src/main/java/com/jfirer/se/ByteArray.java | 103 ++++ src/main/java/com/jfirer/se/ClassInfo.java | 130 +++++ .../java/com/jfirer/se/ClassInfoResolver.java | 83 +++ .../java/com/jfirer/se/InternalByteArray.java | 539 ++++++++++++++++++ src/main/java/com/jfirer/se/JfireSE.java | 46 ++ .../com/jfirer/se/serializer/Serializer.java | 8 + .../se/serializer/SerializerResolver.java | 25 + .../se/serializer/impl/ObjectSerializer.java | 232 ++++++++ src/test/java/org/example/AppTest.java | 38 ++ 12 files changed, 1296 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 pom.xml create mode 100644 src/main/java/com/jfirer/se/ByteArray.java create mode 100644 src/main/java/com/jfirer/se/ClassInfo.java create mode 100644 src/main/java/com/jfirer/se/ClassInfoResolver.java create mode 100644 src/main/java/com/jfirer/se/InternalByteArray.java create mode 100644 src/main/java/com/jfirer/se/JfireSE.java create mode 100644 src/main/java/com/jfirer/se/serializer/Serializer.java create mode 100644 src/main/java/com/jfirer/se/serializer/SerializerResolver.java create mode 100644 src/main/java/com/jfirer/se/serializer/impl/ObjectSerializer.java create mode 100644 src/test/java/org/example/AppTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..8bf4d45 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,6 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..7b64f1a --- /dev/null +++ b/pom.xml @@ -0,0 +1,48 @@ + + 4.0.0 + + org.example + JfireSE + 1.0-SNAPSHOT + jar + + JfireSE + http://maven.apache.org + + + UTF-8 + + + + + junit + junit + 4.13.1 + test + + + com.jfirer + baseutil + 1.1.7-SNAPSHOT + + + org.projectlombok + lombok + 1.18.30 + provided + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 16 + 16 + + + + + diff --git a/src/main/java/com/jfirer/se/ByteArray.java b/src/main/java/com/jfirer/se/ByteArray.java new file mode 100644 index 0000000..1b79b62 --- /dev/null +++ b/src/main/java/com/jfirer/se/ByteArray.java @@ -0,0 +1,103 @@ +package com.jfirer.se; + +public class ByteArray +{ + protected byte[] array; + protected int writePosi = 0; + protected int readIndex = 0; + protected boolean needCheck = true; + + protected ByteArray(int size) + { + array = new byte[size]; + } + + protected ByteArray(byte[] array) + { + this.array = array; + } + + public static ByteArray allocate(int size) + { + return new InternalByteArray(size); + } + + public static ByteArray allocate() + { + return new InternalByteArray(1024); + } + + public static ByteArray wrap(byte[] array) + { + return new InternalByteArray(array); + } + + public void setNeedCheck(boolean needCheck) + { + this.needCheck = needCheck; + } + + protected void ensureCapacity(int len) + { + if (needCheck && len > array.length - writePosi) + { + int newLen = len + writePosi > (array.length << 1) ? len + writePosi + array.length : (array.length << 1); + byte[] tmp = new byte[newLen]; + System.arraycopy(array, 0, tmp, 0, array.length); + array = tmp; + } + } + + public void clear() + { + writePosi = readIndex = 0; + } + + public void put(byte value) + { + ensureCapacity(1); + array[writePosi] = value; + writePosi += 1; + } + + public void setByte(int off, byte b) + { + array[off] = b; + } + + public void put(byte[] data) + { + ensureCapacity(data.length); + System.arraycopy(data, 0, array, writePosi, data.length); + writePosi += data.length; + } + + public byte get() + { + byte result = array[readIndex]; + readIndex += 1; + return result; + } + + public byte[] toArray() + { + byte[] result = new byte[writePosi]; + System.arraycopy(array, 0, result, 0, writePosi); + return result; + } + + public int getWritePosi() + { + return writePosi; + } + + public void setWritePosi(int writePosi) + { + this.writePosi = writePosi; + } + + public void setReadPosi(int readPosi) + { + this.readIndex = readPosi; + } +} diff --git a/src/main/java/com/jfirer/se/ClassInfo.java b/src/main/java/com/jfirer/se/ClassInfo.java new file mode 100644 index 0000000..25295d5 --- /dev/null +++ b/src/main/java/com/jfirer/se/ClassInfo.java @@ -0,0 +1,130 @@ +package com.jfirer.se; + +import com.jfirer.se.serializer.Serializer; +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +public class ClassInfo +{ + private int classId = ClassInfoResolver.NO_CLASS_ID; + private Class clazz; + private String className; + private Serializer serializer; + private JfireSE jfireSE; + private Object[] refTracking; + private int refTrackingIndex = 0; + + public void writeBytes(InternalByteArray byteArray, Object instance, boolean knownClazz) + { + if (instance == null) + { + byteArray.put(JfireSE.NULL); + return; + } + if (serializer == null) + { + serializer = jfireSE.getSerializer(clazz); + } + if (knownClazz) + { + if (jfireSE.isCycleSupport()) + { + int tracking = addTracking(instance); + if (tracking == -1) + { + byteArray.put((byte) 7); + serializer.writeBytes(byteArray, instance); + } + else + { + byteArray.put((byte) 9); + byteArray.writeVarInt(tracking); + } + } + else + { + byteArray.put((byte) 8); + serializer.writeBytes(byteArray, instance); + } + } + else + { + if (jfireSE.isCycleSupport()) + { + if (classId == ClassInfoResolver.NO_CLASS_ID) + { + byteArray.put((byte) 1); + byteArray.writeString(className); + jfireSE.getClassId(this); + addTracking(instance); + serializer.writeBytes(byteArray, instance); + } + else + { + int tracking = addTracking(instance); + if (tracking == -1) + { + byteArray.put((byte) 4); + byteArray.writeVarInt(classId); + serializer.writeBytes(byteArray, instance); + } + else + { + byteArray.put((byte) 6); + byteArray.writeVarInt(classId); + byteArray.writeVarInt(tracking); + } + } + } + else + { + jfireSE.incrDepth(); + if (classId == ClassInfoResolver.NO_CLASS_ID) + { + byteArray.put((byte) 2); + byteArray.writeString(className); + jfireSE.getClassId(this); + serializer.writeBytes(byteArray, instance); + } + else + { + byteArray.put((byte) 5); + byteArray.writeVarInt(classId); + serializer.writeBytes(byteArray, instance); + } + jfireSE.reduceDepth(); + } + } + } + + public void reset() + { + classId = ClassInfoResolver.NO_CLASS_ID; + refTrackingIndex = 0; + } + + public int addTracking(Object instance) + { + if (refTracking == null) + { + refTracking = new Object[4]; + } + for (int i = 0; i < refTrackingIndex; i++) + { + if (refTracking[i] == instance) + { + return i; + } + } + if (refTrackingIndex == refTracking.length) + { + Object[] newRefTracking = new Object[refTracking.length * 2]; + System.arraycopy(refTracking, 0, newRefTracking, 0, refTracking.length); + refTracking = newRefTracking; + } + refTracking[refTrackingIndex++] = instance; + return -1; + } +} diff --git a/src/main/java/com/jfirer/se/ClassInfoResolver.java b/src/main/java/com/jfirer/se/ClassInfoResolver.java new file mode 100644 index 0000000..df80678 --- /dev/null +++ b/src/main/java/com/jfirer/se/ClassInfoResolver.java @@ -0,0 +1,83 @@ +package com.jfirer.se; + +import com.jfirer.se.serializer.SerializerResolver; + +import java.util.IdentityHashMap; +import java.util.Map; + +public class ClassInfoResolver +{ + public static int NO_CLASS_ID = 0; + private Map store = new IdentityHashMap<>(); + private int currentClassId = 1; + private int fixedClassId = 1; + private ClassInfo[] tracking = new ClassInfo[32]; + private JfireSE jfireSE; + private SerializerResolver resolver; + + public ClassInfoResolver(SerializerResolver resolver, JfireSE jfireSE) + { + this.resolver = resolver; + this.jfireSE = jfireSE; + } + + public void getClassId(ClassInfo classInfo) + { + if (currentClassId > tracking.length) + { + ClassInfo[] newTracking = new ClassInfo[tracking.length << 1]; + System.arraycopy(tracking, 0, newTracking, 0, tracking.length); + tracking = newTracking; + } + tracking[currentClassId] = classInfo; + classInfo.setClassId(currentClassId); + currentClassId += 1; + } + + public void reset() + { + for (int i = currentClassId - 1; i > fixedClassId; i--) + { + tracking[i].reset(); + } + currentClassId = fixedClassId; + } + + public ClassInfo getClassInfo(Class clazz) + { + ClassInfo classInfo = store.get(clazz); + if (classInfo == null) + { + classInfo = new ClassInfo().setClassName(clazz.getName()).setJfireSE(jfireSE).setClazz(clazz); + store.put(clazz, classInfo); + return classInfo; + } + return classInfo; + } + + public void registerClass(Class clazz) + { + ClassInfo classInfo = getClassInfo(clazz); + if (classInfo == null) + { + classInfo = new ClassInfo().setClassName(clazz.getName()).setJfireSE(jfireSE).setClazz(clazz); + store.put(clazz, classInfo); + } + for (int i = 1; i < fixedClassId; i++) + { + if (tracking[i].getClazz() == clazz) + { + return; + } + } + if (fixedClassId > tracking.length) + { + ClassInfo[] newTracking = new ClassInfo[tracking.length << 1]; + System.arraycopy(tracking, 0, newTracking, 0, tracking.length); + tracking = newTracking; + } + tracking[fixedClassId] = classInfo; + classInfo.setClassId(fixedClassId++); + currentClassId = fixedClassId; + } +} diff --git a/src/main/java/com/jfirer/se/InternalByteArray.java b/src/main/java/com/jfirer/se/InternalByteArray.java new file mode 100644 index 0000000..7c144cd --- /dev/null +++ b/src/main/java/com/jfirer/se/InternalByteArray.java @@ -0,0 +1,539 @@ +package com.jfirer.se; + +public class InternalByteArray extends ByteArray +{ + public InternalByteArray(int size) + { + super(size); + } + + public InternalByteArray(byte[] array) + { + super(array); + } + + public void writeVarInt(int i) + { + if (i >= -120 && i <= 127) + { + ensureCapacity(1); + array[writePosi] = (byte) i; + writePosi += 1; + } + else + { + int head = -120; + if (i < 0) + { + i = ~i; + head = -124; + } + if (i <= 255) + { + ensureCapacity(2); + array[writePosi] = (byte) (head - 1); + array[writePosi + 1] = (byte) i; + writePosi += 2; + } + else if (i <= 65535) + { + ensureCapacity(3); + array[writePosi] = (byte) (head - 2); + array[writePosi + 1] = (byte) (i >>> 8); + array[writePosi + 2] = (byte) i; + writePosi += 3; + } + else if (i <= 16777215) + { + ensureCapacity(4); + array[writePosi] = (byte) (head - 3); + array[writePosi + 1] = (byte) (i >>> 16); + array[writePosi + 2] = (byte) (i >>> 8); + array[writePosi + 3] = (byte) i; + writePosi += 4; + } + else + { + ensureCapacity(5); + array[writePosi] = (byte) (head - 4); + array[writePosi + 1] = (byte) (i >>> 24); + array[writePosi + 2] = (byte) (i >>> 16); + array[writePosi + 3] = (byte) (i >>> 8); + array[writePosi + 4] = (byte) i; + writePosi += 5; + } + } + } + + public int readVarInt() + { + byte b = array[readIndex++]; + if (b >= -120 && b <= 127) + { + return b; + } + else + { + switch (b) + { + case -128: + return ~((array[readIndex++] & 255) << 24 | (array[readIndex++] & 255) << 16 | (array[readIndex++] & 255) << 8 | array[readIndex++] & 255); + case -127: + return ~((array[readIndex++] & 255) << 16 | (array[readIndex++] & 255) << 8 | array[readIndex++] & 255); + case -126: + return ~((array[readIndex++] & 255) << 8 | array[readIndex++] & 255); + case -125: + return ~(array[readIndex++] & 255); + case -124: + return (array[readIndex++] & 255) << 24 | (array[readIndex++] & 255) << 16 | (array[readIndex++] & 255) << 8 | array[readIndex++] & 255; + case -123: + return (array[readIndex++] & 255) << 16 | (array[readIndex++] & 255) << 8 | array[readIndex++] & 255; + case -122: + return (array[readIndex++] & 255) << 8 | array[readIndex++] & 255; + case -121: + return array[readIndex++] & 255; + default: + throw new IllegalArgumentException("not here"); + } + } + } + + public void writeVarLong(long i) + { + if (i >= -112L && i <= 127L) + { + ensureCapacity(1); + array[writePosi] = (byte) ((int) i); + writePosi += 1; + } + else + { + int head = -112; + if (i < 0L) + { + i = ~i; + head = -120; + } + if (i <= 255L) + { + ensureCapacity(2); + array[writePosi] = (byte) (head - 1); + array[writePosi + 1] = (byte) ((int) i); + writePosi += 2; + } + else if (i <= 65535L) + { + ensureCapacity(3); + array[writePosi] = (byte) (head - 2); + array[writePosi + 1] = (byte) ((int) (i >>> 8)); + array[writePosi + 2] = (byte) ((int) i); + writePosi += 3; + } + else if (i <= 16777215L) + { + ensureCapacity(4); + array[writePosi] = (byte) (head - 3); + array[writePosi + 1] = (byte) ((int) (i >>> 16)); + array[writePosi + 2] = (byte) ((int) (i >>> 8)); + array[writePosi + 3] = (byte) ((int) i); + writePosi += 4; + } + else if (i <= -1L) + { + ensureCapacity(5); + array[writePosi] = (byte) (head - 4); + array[writePosi + 1] = (byte) ((int) (i >>> 24)); + array[writePosi + 2] = (byte) ((int) (i >>> 16)); + array[writePosi + 3] = (byte) ((int) (i >>> 8)); + array[writePosi + 4] = (byte) ((int) i); + writePosi += 5; + } + else if (i <= 1099511627775L) + { + ensureCapacity(6); + array[writePosi] = (byte) (head - 5); + array[writePosi + 1] = (byte) ((int) (i >>> 32)); + array[writePosi + 2] = (byte) ((int) (i >>> 24)); + array[writePosi + 3] = (byte) ((int) (i >>> 16)); + array[writePosi + 4] = (byte) ((int) (i >>> 8)); + array[writePosi + 5] = (byte) ((int) i); + writePosi += 6; + } + else if (i <= 281474976710655L) + { + ensureCapacity(7); + array[writePosi] = (byte) (head - 6); + array[writePosi + 1] = (byte) ((int) (i >>> 40)); + array[writePosi + 2] = (byte) ((int) (i >>> 32)); + array[writePosi + 3] = (byte) ((int) (i >>> 24)); + array[writePosi + 4] = (byte) ((int) (i >>> 16)); + array[writePosi + 5] = (byte) ((int) (i >>> 8)); + array[writePosi + 6] = (byte) ((int) i); + writePosi += 7; + } + else if (i <= 72057594037927935L) + { + ensureCapacity(8); + array[writePosi] = (byte) (head - 7); + array[writePosi + 1] = (byte) ((int) (i >>> 48)); + array[writePosi + 2] = (byte) ((int) (i >>> 40)); + array[writePosi + 3] = (byte) ((int) (i >>> 32)); + array[writePosi + 4] = (byte) ((int) (i >>> 24)); + array[writePosi + 5] = (byte) ((int) (i >>> 16)); + array[writePosi + 6] = (byte) ((int) (i >>> 8)); + array[writePosi + 7] = (byte) ((int) i); + writePosi += 8; + } + else + { + ensureCapacity(9); + array[writePosi] = (byte) (head - 8); + array[writePosi + 1] = (byte) ((int) (i >>> 56)); + array[writePosi + 2] = (byte) ((int) (i >>> 48)); + array[writePosi + 3] = (byte) ((int) (i >>> 40)); + array[writePosi + 4] = (byte) ((int) (i >>> 32)); + array[writePosi + 5] = (byte) ((int) (i >>> 24)); + array[writePosi + 6] = (byte) ((int) (i >>> 16)); + array[writePosi + 7] = (byte) ((int) (i >>> 8)); + array[writePosi + 8] = (byte) ((int) i); + writePosi += 9; + } + } + } + + public long readVarLong() + { + byte b = array[readIndex++]; + if (b >= -112 && b <= 127) + { + return (long) b; + } + else + { + switch (b) + { + case -128: + return ~(((long) array[readIndex++] & 255L) << 56 | ((long) array[readIndex++] & 255L) << 48 | ((long) array[readIndex++] & 255L) << 40 | ((long) array[readIndex++] & 255L) << 32 | ((long) array[readIndex++] & 255L) << 24 | ((long) array[readIndex++] & 255L) << 16 | ((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L); + case -127: + return ~(((long) array[readIndex++] & 255L) << 48 | ((long) array[readIndex++] & 255L) << 40 | ((long) array[readIndex++] & 255L) << 32 | ((long) array[readIndex++] & 255L) << 24 | ((long) array[readIndex++] & 255L) << 16 | ((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L); + case -126: + return ~(((long) array[readIndex++] & 255L) << 40 | ((long) array[readIndex++] & 255L) << 32 | ((long) array[readIndex++] & 255L) << 24 | ((long) array[readIndex++] & 255L) << 16 | ((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L); + case -125: + return ~(((long) array[readIndex++] & 255L) << 32 | ((long) array[readIndex++] & 255L) << 24 | ((long) array[readIndex++] & 255L) << 16 | ((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L); + case -124: + return ~(((long) array[readIndex++] & 255L) << 24 | ((long) array[readIndex++] & 255L) << 16 | ((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L); + case -123: + return ~(((long) array[readIndex++] & 255L) << 16 | ((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L); + case -122: + return ~(((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L); + case -121: + return ~((long) array[readIndex++] & 255L); + case -120: + return ((long) array[readIndex++] & 255L) << 56 | ((long) array[readIndex++] & 255L) << 48 | ((long) array[readIndex++] & 255L) << 40 | ((long) array[readIndex++] & 255L) << 32 | ((long) array[readIndex++] & 255L) << 24 | ((long) array[readIndex++] & 255L) << 16 | ((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L; + case -119: + return ((long) array[readIndex++] & 255L) << 48 | ((long) array[readIndex++] & 255L) << 40 | ((long) array[readIndex++] & 255L) << 32 | ((long) array[readIndex++] & 255L) << 24 | ((long) array[readIndex++] & 255L) << 16 | ((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L; + case -118: + return ((long) array[readIndex++] & 255L) << 40 | ((long) array[readIndex++] & 255L) << 32 | ((long) array[readIndex++] & 255L) << 24 | ((long) array[readIndex++] & 255L) << 16 | ((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L; + case -117: + return ((long) array[readIndex++] & 255L) << 32 | ((long) array[readIndex++] & 255L) << 24 | ((long) array[readIndex++] & 255L) << 16 | ((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L; + case -116: + return ((long) array[readIndex++] & 255L) << 24 | ((long) array[readIndex++] & 255L) << 16 | ((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L; + case -115: + return ((long) array[readIndex++] & 255L) << 16 | ((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L; + case -114: + return ((long) array[readIndex++] & 255L) << 8 | (long) array[readIndex++] & 255L; + case -113: + return (long) array[readIndex++] & 255L; + default: + throw new IllegalArgumentException("not here"); + } + } + } + + public void writeVarCharWithoutCheck(char c) + { + if (c <= 251) + { + array[writePosi] = (byte) c; + writePosi += 1; + } + else if (c <= 255) + { + array[writePosi] = -4; + array[writePosi + 1] = (byte) c; + writePosi += 2; + } + else if (c <= '\uffff') + { + array[writePosi] = -3; + array[writePosi + 1] = (byte) (c >>> 8); + array[writePosi + 2] = (byte) c; + writePosi += 3; + } + } + + public void writeVarChar(char c) + { + if (c <= 251) + { + ensureCapacity(1); + array[writePosi] = (byte) c; + ++writePosi; + } + else if (c <= 255) + { + ensureCapacity(2); + array[writePosi] = -4; + array[writePosi + 1] = (byte) c; + writePosi += 2; + } + else if (c <= '\uffff') + { + ensureCapacity(3); + array[writePosi] = -3; + array[writePosi + 1] = (byte) (c >>> 8); + array[writePosi + 2] = (byte) c; + writePosi += 3; + } + } + + public char readVarChar() + { + int length = array[readIndex++] & 255; + if (length <= 251) + { + return (char) length; + } + else if (length == 252) + { + length = array[readIndex++] & 255; + return (char) length; + } + else if (length == 253) + { + length = (array[readIndex++] & 255) << 8; + length |= array[readIndex++] & 255; + return (char) length; + } + else + { + throw new IllegalArgumentException("not here"); + } + } + + public void writePositive(int positive) + { + if (positive < 0) + { + throw new UnsupportedOperationException(); + } + else + { + if (positive <= 251) + { + ensureCapacity(1); + array[writePosi] = (byte) positive; + ++writePosi; + } + else if (positive <= 255) + { + ensureCapacity(2); + array[writePosi] = -4; + array[writePosi + 1] = (byte) positive; + writePosi += 2; + } + else if (positive <= 65535) + { + ensureCapacity(3); + array[writePosi] = -3; + array[writePosi + 1] = (byte) (positive >>> 8); + array[writePosi + 2] = (byte) positive; + writePosi += 3; + } + else if (positive <= 16777215) + { + ensureCapacity(4); + array[writePosi] = -2; + array[writePosi + 1] = (byte) (positive >>> 16); + array[writePosi + 2] = (byte) (positive >>> 8); + array[writePosi + 3] = (byte) positive; + writePosi += 4; + } + else + { + ensureCapacity(5); + array[writePosi] = -1; + array[writePosi + 1] = (byte) (positive >>> 24); + array[writePosi + 2] = (byte) (positive >>> 16); + array[writePosi + 3] = (byte) (positive >>> 8); + array[writePosi + 4] = (byte) positive; + writePosi += 5; + } + } + } + + public int readPositive() + { + int length = array[readIndex++] & 255; + if (length <= 251) + { + return length; + } + else if (length == 252) + { + length = array[readIndex++] & 255; + return length; + } + else if (length == 253) + { + length = (array[readIndex++] & 255) << 8; + length |= array[readIndex++] & 255; + return length; + } + else if (length == 254) + { + length = (array[readIndex++] & 255) << 16; + length |= (array[readIndex++] & 255) << 8; + length |= array[readIndex++] & 255; + return length; + } + else if (length == 255) + { + length = (array[readIndex++] & 255) << 24; + length |= (array[readIndex++] & 255) << 16; + length |= (array[readIndex++] & 255) << 8; + length |= array[readIndex++] & 255; + return length; + } + else + { + throw new RuntimeException("wrong data"); + } + } + + public void writeInt(int i) + { + ensureCapacity(4); + array[writePosi] = (byte) (i >> 24); + array[writePosi + 1] = (byte) (i >> 16); + array[writePosi + 2] = (byte) (i >> 8); + array[writePosi + 3] = (byte) i; + writePosi += 4; + } + + public void writeShort(short s) + { + ensureCapacity(2); + array[writePosi] = (byte) (s >> 8); + array[writePosi + 1] = (byte) s; + writePosi += 2; + } + + public void writeLong(long l) + { + ensureCapacity(8); + array[writePosi] = (byte) ((int) (l >> 56)); + array[writePosi + 1] = (byte) ((int) (l >> 48)); + array[writePosi + 2] = (byte) ((int) (l >> 40)); + array[writePosi + 3] = (byte) ((int) (l >> 32)); + array[writePosi + 4] = (byte) ((int) (l >> 24)); + array[writePosi + 5] = (byte) ((int) (l >> 16)); + array[writePosi + 6] = (byte) ((int) (l >> 8)); + array[writePosi + 7] = (byte) ((int) l); + writePosi += 8; + } + + public int readInt() + { + int i = (array[readIndex] & 255) << 24; + i |= (array[readIndex + 1] & 255) << 16; + i |= (array[readIndex + 2] & 255) << 8; + i |= array[readIndex + 3] & 255; + this.readIndex += 4; + return i; + } + + public short readShort() + { + short s = (short) ((array[readIndex] & 255) << 8); + s = (short) (s | array[readIndex + 1] & 255); + this.readIndex += 2; + return s; + } + + public long readLong() + { + long l = (long) array[readIndex] << 56 | ((long) array[readIndex + 1] & 255L) << 48 | ((long) array[readIndex + 2] & 255L) << 40 | ((long) array[readIndex + 3] & 255L) << 32 | ((long) array[readIndex + 4] & 255L) << 24 | ((long) array[readIndex + 5] & 255L) << 16 | ((long) array[readIndex + 6] & 255L) << 8 | (long) array[readIndex + 7] & 255L; + this.readIndex += 8; + return l; + } + + public void writeFloat(float f) + { + writeInt(Float.floatToRawIntBits(f)); + } + + public void writeDouble(double d) + { + writeLong(Double.doubleToRawLongBits(d)); + } + + public float readFloat() + { + int i = readInt(); + return Float.intBitsToFloat(i); + } + + public double readDouble() + { + long l = readLong(); + return Double.longBitsToDouble(l); + } + + public void skipWrite(int len) + { + ensureCapacity(len); + writePosi += len; + } + + public void writeString(String value) + { + if (value == null) + { + writeVarInt(-1); + } + else + { + int length = value.length(); + writeVarInt(length); + ensureCapacity(3 * length); + for (int i = 0; i < length; ++i) + { + writeVarCharWithoutCheck(value.charAt(i)); + } + } + } + + public String readString() + { + int length = readVarInt(); + if (length == -1) + { + return null; + } + else + { + char[] src = new char[length]; + for (int i = 0; i < length; ++i) + { + src[i] = this.readVarChar(); + } + return new String(src); + } + } + + public boolean remainRead() + { + return readIndex < writePosi; + } +} diff --git a/src/main/java/com/jfirer/se/JfireSE.java b/src/main/java/com/jfirer/se/JfireSE.java new file mode 100644 index 0000000..82e998d --- /dev/null +++ b/src/main/java/com/jfirer/se/JfireSE.java @@ -0,0 +1,46 @@ +package com.jfirer.se; + +import com.jfirer.se.serializer.Serializer; +import com.jfirer.se.serializer.SerializerResolver; + +public class JfireSE +{ + public static final byte NULL = 0; + private boolean CYCLE_SUPPORT = true; + private SerializerResolver serializerResolver; + private ClassInfoResolver classInfoResolver; + private int depth = 1; + + public boolean isCycleSupport() + { + return CYCLE_SUPPORT; + } + + public Serializer getSerializer(Class clazz) + { + return serializerResolver.getSerializer(clazz, this); + } + + public ClassInfo getClassInfo(Class clazz) + { + return classInfoResolver.getClassInfo(clazz); + } + + public void getClassId(ClassInfo classInfo) + { + classInfoResolver.getClassId(classInfo); + } + + public void incrDepth() + { + if (depth++ > 256) + { + throw new IllegalStateException("序列化深度超过256,可能存在循环引用,请开启循环引用设置"); + } + } + + public void reduceDepth() + { + depth--; + } +} diff --git a/src/main/java/com/jfirer/se/serializer/Serializer.java b/src/main/java/com/jfirer/se/serializer/Serializer.java new file mode 100644 index 0000000..0b60278 --- /dev/null +++ b/src/main/java/com/jfirer/se/serializer/Serializer.java @@ -0,0 +1,8 @@ +package com.jfirer.se.serializer; + +import com.jfirer.se.InternalByteArray; + +public interface Serializer +{ + void writeBytes(InternalByteArray byteArray, Object instance); +} diff --git a/src/main/java/com/jfirer/se/serializer/SerializerResolver.java b/src/main/java/com/jfirer/se/serializer/SerializerResolver.java new file mode 100644 index 0000000..28dfd16 --- /dev/null +++ b/src/main/java/com/jfirer/se/serializer/SerializerResolver.java @@ -0,0 +1,25 @@ +package com.jfirer.se.serializer; + +import com.jfirer.se.JfireSE; + +import java.util.IdentityHashMap; +import java.util.Map; + +public class SerializerResolver +{ + private Map store = new IdentityHashMap<>(); + + public Serializer getSerializer(Class clazz, JfireSE jfireSE) + { + Serializer serializer = store.get(clazz); + if (serializer != null) + { + return serializer; + } + + } + public void registerSerializer(Class clazz, Serializer serializer) + { + store.put(clazz, serializer); + } +} diff --git a/src/main/java/com/jfirer/se/serializer/impl/ObjectSerializer.java b/src/main/java/com/jfirer/se/serializer/impl/ObjectSerializer.java new file mode 100644 index 0000000..73df80a --- /dev/null +++ b/src/main/java/com/jfirer/se/serializer/impl/ObjectSerializer.java @@ -0,0 +1,232 @@ +package com.jfirer.se.serializer.impl; + +import com.jfirer.baseutil.reflect.ReflectUtil; +import com.jfirer.baseutil.reflect.ValueAccessor; +import com.jfirer.se.ClassInfo; +import com.jfirer.se.InternalByteArray; +import com.jfirer.se.JfireSE; +import com.jfirer.se.serializer.Serializer; +import lombok.Data; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.function.Predicate; + +public class ObjectSerializer implements Serializer +{ + private FieldInfo[] primitiveFieldInfos; + private FieldInfo[] boxFieldInfos; + private FinalFieldInfo[] finalFieldInfos; + private JfireSE jfireSE; + private Class clazz; + + public ObjectSerializer(Class clazz, JfireSE jfireSE) + { + this.clazz = clazz; + this.jfireSE = jfireSE; + Class type = clazz; + List fields = new ArrayList<>(); + while (type != Object.class) + { + fields.addAll(Arrays.stream(type.getDeclaredFields()).filter(Predicate.not(field -> Modifier.isStatic(field.getModifiers()))).toList()); + type = type.getComponentType(); + } + primitiveFieldInfos = fields.stream().filter(field -> field.getType().isPrimitive()).sorted(Comparator.comparing(o -> o.getType().getName())).map(FieldInfo::new).toArray(FieldInfo[]::new); + boxFieldInfos = fields.stream().filter(field -> ReflectUtil.isPrimitiveBox(field.getType()) || field.getType() == String.class).sorted(Comparator.comparing(o -> o.getType().getName())).map(FieldInfo::new).toArray(FieldInfo[]::new); + finalFieldInfos = fields.stream().filter(Predicate.not(field -> ReflectUtil.isPrimitiveBox(field.getType())))// + .filter(Predicate.not(field -> ReflectUtil.isPrimitive(field.getType())))// + .filter(field -> field.getType() != void.class && field.getType() != Void.class)// + .filter(field -> Modifier.isFinal(field.getType().getModifiers())).sorted(Comparator.comparing(o -> o.getType().getName())).map(FinalFieldInfo::new).toArray(FinalFieldInfo[]::new); + } + + @Override + public void writeBytes(InternalByteArray byteArray, Object instance) + { + } + + @Data + class FieldInfo + { + int classId; + ValueAccessor accessor; + + FieldInfo(Field field) + { + classId = ReflectUtil.getClassId(field.getType()); + accessor = new ValueAccessor(field); + } + + void write(InternalByteArray byteArray, Object instance) + { + switch (classId) + { + case ReflectUtil.PRIMITIVE_INT -> byteArray.writeVarInt(accessor.getInt(instance)); + case ReflectUtil.PRIMITIVE_LONG -> byteArray.writeVarLong(accessor.getLong(instance)); + case ReflectUtil.PRIMITIVE_FLOAT -> byteArray.writeFloat(accessor.getFloat(instance)); + case ReflectUtil.PRIMITIVE_DOUBLE -> byteArray.writeDouble(accessor.getDouble(instance)); + case ReflectUtil.PRIMITIVE_BOOL -> byteArray.writePositive(accessor.getBoolean(instance) ? 1 : 0); + case ReflectUtil.PRIMITIVE_CHAR -> byteArray.writeVarChar(accessor.getChar(instance)); + case ReflectUtil.PRIMITIVE_SHORT -> byteArray.writeShort(accessor.getShort(instance)); + case ReflectUtil.PRIMITIVE_BYTE -> byteArray.put(accessor.getByte(instance)); + case ReflectUtil.CLASS_INT -> + { + Integer value = accessor.getIntObject(instance); + if (value == null) + { + byteArray.put(JfireSE.NULL); + } + else + { + byteArray.put((byte) 01); + byteArray.writeVarInt(value); + } + } + case ReflectUtil.CLASS_LONG -> + { + Long value = accessor.getLongObject(instance); + if (value == null) + { + byteArray.put(JfireSE.NULL); + } + else + { + byteArray.put((byte) 01); + byteArray.writeVarLong(value); + } + } + case ReflectUtil.CLASS_FLOAT -> + { + Float value = accessor.getFloatObject(instance); + if (value == null) + { + byteArray.put(JfireSE.NULL); + } + else + { + byteArray.put((byte) 01); + byteArray.writeFloat(value); + } + } + case ReflectUtil.CLASS_DOUBLE -> + { + Double value = accessor.getDoubleObject(instance); + if (value == null) + { + byteArray.put(JfireSE.NULL); + } + else + { + byteArray.put((byte) 01); + byteArray.writeDouble(value); + } + } + case ReflectUtil.CLASS_BOOL -> + { + Boolean value = accessor.getBooleanObject(instance); + if (value == null) + { + byteArray.put(JfireSE.NULL); + } + else + { + byteArray.put((byte) 01); + byteArray.writePositive(value ? 1 : 0); + } + } + case ReflectUtil.CLASS_CHAR -> + { + Character value = accessor.getCharObject(instance); + if (value == null) + { + byteArray.put(JfireSE.NULL); + } + else + { + byteArray.put((byte) 01); + byteArray.writeVarChar(value); + } + } + case ReflectUtil.CLASS_SHORT -> + { + Short value = accessor.getShortObject(instance); + if (value == null) + { + byteArray.put(JfireSE.NULL); + } + else + { + byteArray.put((byte) 01); + byteArray.writeShort(value); + } + } + case ReflectUtil.CLASS_BYTE -> + { + Byte value = accessor.getByteObject(instance); + if (value == null) + { + byteArray.put(JfireSE.NULL); + } + else + { + byteArray.put((byte) 01); + byteArray.put(value); + } + } + case ReflectUtil.CLASS_STRING -> + { + String value = (String) accessor.get(instance); + if (value == null) + { + byteArray.put(JfireSE.NULL); + } + else + { + byteArray.put((byte) 01); + byteArray.writeString(value); + } + } + } + } + } + + class FinalFieldInfo + { + ValueAccessor accessor; + ClassInfo classInfo; + + FinalFieldInfo(Field field) + { + accessor = new ValueAccessor(field); + classInfo = jfireSE.getClassInfo(field.getType()); + } + + void write(InternalByteArray byteArray, Object instance) + { + Object value = accessor.get(instance); + if (value == null) + { + byteArray.put(JfireSE.NULL); + } + else + { + byteArray.put((byte) 07); + + } + } + } + + class VariableFieldInfo + { + ValueAccessor accessor; + ClassInfo classInfo; + + VariableFieldInfo(Field field) + { + accessor = new ValueAccessor(field); + } + } +} diff --git a/src/test/java/org/example/AppTest.java b/src/test/java/org/example/AppTest.java new file mode 100644 index 0000000..d5f435d --- /dev/null +++ b/src/test/java/org/example/AppTest.java @@ -0,0 +1,38 @@ +package org.example; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +}