diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/CodeLanguage.java b/protocol/src/main/java/com/zfoo/protocol/serializer/CodeLanguage.java index 2994b253..4b0dea02 100644 --- a/protocol/src/main/java/com/zfoo/protocol/serializer/CodeLanguage.java +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/CodeLanguage.java @@ -19,6 +19,7 @@ import com.zfoo.protocol.serializer.gdscript.CodeGenerateGdScript; import com.zfoo.protocol.serializer.golang.CodeGenerateGolang; import com.zfoo.protocol.serializer.java.CodeGenerateJava; import com.zfoo.protocol.serializer.javascript.CodeGenerateJavaScript; +import com.zfoo.protocol.serializer.kotlin.CodeGenerateKotlin; import com.zfoo.protocol.serializer.lua.CodeGenerateLua; import com.zfoo.protocol.serializer.python.CodeGeneratePython; import com.zfoo.protocol.serializer.typescript.CodeGenerateTypeScript; @@ -35,7 +36,7 @@ public enum CodeLanguage { Java(1<<1, CodeGenerateJava.class), - Kotlin(1<<2, null), + Kotlin(1<<2, CodeGenerateKotlin.class), Scala(1<<3, null), diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/CodeGenerateKotlin.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/CodeGenerateKotlin.java new file mode 100644 index 00000000..13030fea --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/CodeGenerateKotlin.java @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.anno.Compatible; +import com.zfoo.protocol.generate.GenerateOperation; +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.generate.GenerateProtocolNote; +import com.zfoo.protocol.generate.GenerateProtocolPath; +import com.zfoo.protocol.registration.ProtocolAnalysis; +import com.zfoo.protocol.registration.ProtocolRegistration; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.serializer.CodeLanguage; +import com.zfoo.protocol.serializer.CodeTemplatePlaceholder; +import com.zfoo.protocol.serializer.ICodeGenerate; +import com.zfoo.protocol.serializer.enhance.EnhanceObjectProtocolSerializer; +import com.zfoo.protocol.serializer.reflect.*; +import com.zfoo.protocol.util.ClassUtils; +import com.zfoo.protocol.util.FileUtils; +import com.zfoo.protocol.util.ReflectionUtils; +import com.zfoo.protocol.util.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.zfoo.protocol.util.FileUtils.LS; +import static com.zfoo.protocol.util.StringUtils.TAB; + + +/** + * @author godotg + */ +public class CodeGenerateKotlin implements ICodeGenerate { + private static final Logger logger = LoggerFactory.getLogger(CodeGenerateKotlin.class); + + // custom configuration + public static String protocolOutputRootPath = "zfookt"; + private static String protocolOutputPath = StringUtils.EMPTY; + public static String protocolPackage = "com.zfoo.java"; + + private static final Map kotlinSerializerMap = new HashMap<>(); + + public static IKtSerializer kotlinSerializer(ISerializer serializer) { + return kotlinSerializerMap.get(serializer); + } + + @Override + public void init(GenerateOperation generateOperation) { + protocolOutputPath = FileUtils.joinPath(generateOperation.getProtocolPath(), protocolOutputRootPath); + FileUtils.deleteFile(new File(protocolOutputPath)); + + kotlinSerializerMap.put(BooleanSerializer.INSTANCE, new KtBooleanSerializer()); + kotlinSerializerMap.put(ByteSerializer.INSTANCE, new KtByteSerializer()); + kotlinSerializerMap.put(ShortSerializer.INSTANCE, new KtShortSerializer()); + kotlinSerializerMap.put(IntSerializer.INSTANCE, new KtIntSerializer()); + kotlinSerializerMap.put(LongSerializer.INSTANCE, new KtLongSerializer()); + kotlinSerializerMap.put(FloatSerializer.INSTANCE, new KtFloatSerializer()); + kotlinSerializerMap.put(DoubleSerializer.INSTANCE, new KtDoubleSerializer()); + kotlinSerializerMap.put(StringSerializer.INSTANCE, new KtStringSerializer()); + kotlinSerializerMap.put(ArraySerializer.INSTANCE, new KtArraySerializer()); + kotlinSerializerMap.put(ListSerializer.INSTANCE, new KtListSerializer()); + kotlinSerializerMap.put(SetSerializer.INSTANCE, new KtSetSerializer()); + kotlinSerializerMap.put(MapSerializer.INSTANCE, new KtMapSerializer()); + kotlinSerializerMap.put(ObjectProtocolSerializer.INSTANCE, new KtObjectProtocolSerializer()); + } + + @Override + public void mergerProtocol(List registrations) throws IOException { + createTemplateFile(); + var protocol_root_path = StringUtils.format("package {};", protocolPackage); + + var protocolManagerTemplate = ClassUtils.getFileFromClassPathToString("java/ProtocolManagerTemplate.java"); + var protocol_manager_registrations = new StringBuilder(); + for (var registration : registrations) { + var protocol_id = registration.protocolId(); + var protocol_name = registration.protocolConstructor().getDeclaringClass().getSimpleName(); + protocol_manager_registrations.append(StringUtils.format("protocols[{}] = Protocols.registration{};", protocol_id, protocol_name)).append(LS); + protocol_manager_registrations.append(StringUtils.format("protocolIdMap.put(Protocols.{}.class, (short){});", protocol_name, protocol_id)).append(LS); + } + + var placeholderMap = Map.of(CodeTemplatePlaceholder.protocol_root_path, protocol_root_path + , CodeTemplatePlaceholder.protocol_imports, StringUtils.EMPTY + , CodeTemplatePlaceholder.protocol_manager_registrations, protocol_manager_registrations.toString()); + var formatProtocolManagerTemplate = CodeTemplatePlaceholder.formatTemplate(protocolManagerTemplate, placeholderMap); + var protocolManagerFile = new File(StringUtils.format("{}/{}", protocolOutputRootPath, "ProtocolManager.java")); + FileUtils.writeStringToFile(protocolManagerFile, formatProtocolManagerTemplate, true); + logger.info("Generated Kotlin protocol manager file:[{}] is in path:[{}]", protocolManagerFile.getName(), protocolManagerFile.getAbsolutePath()); + + var protocol_class = new StringBuilder(); + var protocol_registration = new StringBuilder(); + for (var registration : GenerateProtocolFile.subProtocolFirst(registrations)) { + var protocol_id = registration.protocolId(); + // protocol + protocol_class.append(protocol_class(registration)).append(LS); + // registration + protocol_registration.append(protocol_registration(registration)).append(LS); + } + var protocolTemplate = ClassUtils.getFileFromClassPathToString("java/ProtocolsTemplate.java"); + var formatProtocolTemplate = CodeTemplatePlaceholder.formatTemplate(protocolTemplate, Map.of( + CodeTemplatePlaceholder.protocol_root_path, protocol_root_path + , CodeTemplatePlaceholder.protocol_imports, StringUtils.EMPTY + , CodeTemplatePlaceholder.protocol_class, protocol_class.toString().replace("public class", "public static class") + , CodeTemplatePlaceholder.protocol_registration, protocol_registration.toString() + )); + var outputPath = StringUtils.format("{}/Protocols.java", protocolOutputPath); + var file = new File(outputPath); + FileUtils.writeStringToFile(file, formatProtocolTemplate, true); + logger.info("Generated Kotlin protocol file:[{}] is in path:[{}]", file.getName(), file.getAbsolutePath()); + } + + @Override + public void foldProtocol(List registrations) throws IOException { + createTemplateFile(); + + var protocolManagerTemplate = ClassUtils.getFileFromClassPathToString("java/ProtocolManagerTemplate.java"); + var protocol_manager_registrations = new StringBuilder(); + var protocol_imports = new StringBuilder(); + for (var registration : registrations) { + var protocol_id = registration.protocolId(); + var protocol_name = registration.protocolConstructor().getDeclaringClass().getSimpleName(); + protocol_imports.append(StringUtils.format("import {}.{}.{};", protocolPackage, GenerateProtocolPath.protocolPathPeriod(protocol_id), protocol_name)).append(LS); + protocol_manager_registrations.append(StringUtils.format("protocols[{}] = {}.registration{};", protocol_id, protocol_name, protocol_name)).append(LS); + protocol_manager_registrations.append(StringUtils.format("protocolIdMap.put({}.class, (short){});", protocol_name, protocol_id)).append(LS); + } + + var placeholderMap = Map.of(CodeTemplatePlaceholder.protocol_root_path, StringUtils.format("package {};", protocolPackage) + , CodeTemplatePlaceholder.protocol_imports, protocol_imports.toString() + , CodeTemplatePlaceholder.protocol_manager_registrations, protocol_manager_registrations.toString()); + var formatProtocolManagerTemplate = CodeTemplatePlaceholder.formatTemplate(protocolManagerTemplate, placeholderMap); + var protocolManagerFile = new File(StringUtils.format("{}/{}", protocolOutputRootPath, "ProtocolManager.java")); + FileUtils.writeStringToFile(protocolManagerFile, formatProtocolManagerTemplate, true); + logger.info("Generated Kotlin protocol manager file:[{}] is in path:[{}]", protocolManagerFile.getName(), protocolManagerFile.getAbsolutePath()); + + for (var registration : registrations) { + var protocol_id = registration.protocolId(); + var protocol_name = registration.protocolConstructor().getDeclaringClass().getSimpleName(); + var protocolTemplate = ClassUtils.getFileFromClassPathToString("java/ProtocolTemplate.java"); + var protocol_root_path = StringUtils.format("package {}.{};", protocolPackage, GenerateProtocolPath.protocolPathPeriod(protocol_id)); + var formatProtocolTemplate = CodeTemplatePlaceholder.formatTemplate(protocolTemplate, Map.of( + CodeTemplatePlaceholder.protocol_root_path, protocol_root_path + , CodeTemplatePlaceholder.protocol_imports, protocol_imports_fold(registration) + , CodeTemplatePlaceholder.protocol_note, GenerateProtocolNote.protocol_note(protocol_id, CodeLanguage.Kotlin) + , CodeTemplatePlaceholder.protocol_name, protocol_name + , CodeTemplatePlaceholder.protocol_field_definition, protocol_field_definition(registration) + , CodeTemplatePlaceholder.protocol_registration, protocol_registration(registration) + )); + var outputPath = StringUtils.format("{}/{}/{}.java", protocolOutputPath, GenerateProtocolPath.protocolPathSlash(protocol_id), protocol_name); + var file = new File(outputPath); + FileUtils.writeStringToFile(file, formatProtocolTemplate, true); + logger.info("Generated Kotlin protocol file:[{}] is in path:[{}]", file.getName(), file.getAbsolutePath()); + } + } + + @Override + public void defaultProtocol(List registrations) throws IOException { + createTemplateFile(); + var protocol_root_path = StringUtils.format("package {}", protocolPackage); + + var protocolManagerTemplate = ClassUtils.getFileFromClassPathToString("kotlin/ProtocolManagerTemplate.kt"); + var protocol_manager_registrations = new StringBuilder(); + for (var registration : registrations) { + var protocol_id = registration.protocolId(); + var protocol_name = registration.protocolConstructor().getDeclaringClass().getSimpleName(); + protocol_manager_registrations.append(StringUtils.format("protocols[{}] = {}.registration{}", protocol_id, protocol_name, protocol_name)).append(LS); + protocol_manager_registrations.append(StringUtils.format("protocolIdMap[{}::class.java] = {}.toShort()", protocol_name, protocol_id)).append(LS); + } + + var placeholderMap = Map.of(CodeTemplatePlaceholder.protocol_root_path, protocol_root_path + , CodeTemplatePlaceholder.protocol_imports, StringUtils.EMPTY + , CodeTemplatePlaceholder.protocol_manager_registrations, protocol_manager_registrations.toString()); + var formatProtocolManagerTemplate = CodeTemplatePlaceholder.formatTemplate(protocolManagerTemplate, placeholderMap); + var protocolManagerFile = new File(StringUtils.format("{}/{}", protocolOutputRootPath, "ProtocolManager.kt")); + FileUtils.writeStringToFile(protocolManagerFile, formatProtocolManagerTemplate, true); + logger.info("Generated Kotlin protocol manager file:[{}] is in path:[{}]", protocolManagerFile.getName(), protocolManagerFile.getAbsolutePath()); + + for (var registration : registrations) { + var protocol_id = registration.protocolId(); + var protocol_name = registration.protocolConstructor().getDeclaringClass().getSimpleName(); + var protocolTemplate = ClassUtils.getFileFromClassPathToString("kotlin/ProtocolTemplate.kt"); + var formatProtocolTemplate = CodeTemplatePlaceholder.formatTemplate(protocolTemplate, Map.of( + CodeTemplatePlaceholder.protocol_root_path, protocol_root_path + , CodeTemplatePlaceholder.protocol_imports, StringUtils.EMPTY + , CodeTemplatePlaceholder.protocol_note, GenerateProtocolNote.protocol_note(protocol_id, CodeLanguage.Kotlin) + , CodeTemplatePlaceholder.protocol_name, protocol_name + , CodeTemplatePlaceholder.protocol_field_definition, protocol_field_definition(registration) + , CodeTemplatePlaceholder.protocol_registration, protocol_registration(registration) + )); + var outputPath = StringUtils.format("{}/{}.kt", protocolOutputPath, protocol_name); + var file = new File(outputPath); + FileUtils.writeStringToFile(file, formatProtocolTemplate, true); + logger.info("Generated Kotlin protocol file:[{}] is in path:[{}]", file.getName(), file.getAbsolutePath()); + } + } + + private void createTemplateFile() { + var rootPackage = StringUtils.format("package {}", protocolPackage); + var list = List.of("kotlin/IProtocolRegistration.kt" + , "kotlin/ByteBuffer.kt"); + for (var fileName : list) { + // IProtocolRegistration + var template = ClassUtils.getFileFromClassPathToString(fileName); + var formatTemplate = CodeTemplatePlaceholder.formatTemplate(template, Map.of( + CodeTemplatePlaceholder.protocol_root_path, rootPackage + , CodeTemplatePlaceholder.protocol_imports, StringUtils.EMPTY + )); + var createFile = new File(StringUtils.format("{}/{}", protocolOutputPath, StringUtils.substringAfterFirst(fileName, "kotlin/"))); + FileUtils.writeStringToFile(createFile, formatTemplate, false); + } + } + + private String protocol_class(ProtocolRegistration registration) { + var protocol_id = registration.protocolId(); + var protocol_name = registration.protocolConstructor().getDeclaringClass().getSimpleName(); + var protocolTemplate = ClassUtils.getFileFromClassPathToString("kotlin/ProtocolClassTemplate.kt"); + var formatProtocolTemplate = CodeTemplatePlaceholder.formatTemplate(protocolTemplate, Map.of( + CodeTemplatePlaceholder.protocol_note, GenerateProtocolNote.protocol_note(protocol_id, CodeLanguage.Kotlin) + , CodeTemplatePlaceholder.protocol_name, protocol_name + , CodeTemplatePlaceholder.protocol_id, String.valueOf(protocol_id) + , CodeTemplatePlaceholder.protocol_field_definition, protocol_field_definition(registration) + , CodeTemplatePlaceholder.protocol_registration, protocol_registration(registration) + )); + return formatProtocolTemplate; + } + + private String protocol_registration(ProtocolRegistration registration) { + var protocol_id = registration.protocolId(); + var protocol_name = registration.protocolConstructor().getDeclaringClass().getSimpleName(); + var protocolTemplate = ClassUtils.getFileFromClassPathToString("kotlin/ProtocolRegistrationTemplate.kt"); + var formatProtocolTemplate = CodeTemplatePlaceholder.formatTemplate(protocolTemplate, Map.of( + CodeTemplatePlaceholder.protocol_name, protocol_name + , CodeTemplatePlaceholder.protocol_id, String.valueOf(protocol_id) + , CodeTemplatePlaceholder.protocol_write_serialization, protocol_write_serialization(registration) + , CodeTemplatePlaceholder.protocol_read_deserialization, protocol_read_deserialization(registration) + )); + return formatProtocolTemplate; + } + + + private String protocol_imports_fold(ProtocolRegistration registration) { + var protocolId = registration.getId(); + var subProtocols = ProtocolAnalysis.getAllSubProtocolIds(protocolId); + var ktBuilder = new StringBuilder(); + for (var subProtocolId : subProtocols) { + var protocolName = EnhanceObjectProtocolSerializer.getProtocolClassSimpleName(subProtocolId); + var subProtocolPath = StringUtils.format("import {}.{}.{}", protocolPackage, GenerateProtocolPath.protocolPathPeriod(subProtocolId), protocolName); + ktBuilder.append(subProtocolPath).append(LS); + } + ktBuilder.append(StringUtils.format("import {}.IProtocolRegistration", protocolPackage)).append(LS); + ktBuilder.append(StringUtils.format("import {}.ByteBuffer", protocolPackage)).append(LS); + return ktBuilder.toString(); + } + + private String protocol_field_definition(ProtocolRegistration registration) { + var protocolId = registration.getId(); + var fields = registration.getFields(); + var fieldRegistrations = registration.getFieldRegistrations(); + var ktBuilder = new StringBuilder(); + var sequencedFields = ReflectionUtils.notStaticAndTransientFields(registration.getConstructor().getDeclaringClass()); + for (int i = 0; i < sequencedFields.size(); i++) { + var field = sequencedFields.get(i); + IFieldRegistration fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)]; + var fieldName = field.getName(); + // 生成注释 + var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.Kotlin); + for (var fieldNote : fieldNotes) { + ktBuilder.append(fieldNote).append(LS); + } + var pair = kotlinSerializer(fieldRegistration.serializer()).field(field, fieldRegistration); + ktBuilder.append(StringUtils.format("{}: {} = {}", fieldName, pair.getKey(), pair.getValue())).append(LS); + } + return ktBuilder.toString(); + } + + + private String protocol_write_serialization(ProtocolRegistration registration) { + GenerateProtocolFile.localVariableId = 0; + var fields = registration.getFields(); + var fieldRegistrations = registration.getFieldRegistrations(); + var ktBuilder = new StringBuilder(); + if (registration.isCompatible()) { + ktBuilder.append("val beforeWriteIndex = buffer.writeOffset()").append(LS); + ktBuilder.append(StringUtils.format("buffer.writeInt({})", registration.getPredictionLength())).append(LS); + } else { + ktBuilder.append("buffer.writeInt(-1)").append(LS); + } + for (var i = 0; i < fields.length; i++) { + var field = fields[i]; + var fieldRegistration = fieldRegistrations[i]; + kotlinSerializer(fieldRegistration.serializer()).writeObject(ktBuilder, "message." + field.getName(), 0, field, fieldRegistration); + } + if (registration.isCompatible()) { + ktBuilder.append(StringUtils.format("buffer.adjustPadding({}, beforeWriteIndex)", registration.getPredictionLength())).append(LS); + } + return ktBuilder.toString(); + } + + + private String protocol_read_deserialization(ProtocolRegistration registration) { + GenerateProtocolFile.localVariableId = 0; + var fields = registration.getFields(); + var fieldRegistrations = registration.getFieldRegistrations(); + var ktBuilder = new StringBuilder(); + for (var i = 0; i < fields.length; i++) { + var field = fields[i]; + var fieldRegistration = fieldRegistrations[i]; + + if (field.isAnnotationPresent(Compatible.class)) { + ktBuilder.append("if (buffer.compatibleRead(beforeReadIndex, length)) {").append(LS); + var compatibleReadObject = kotlinSerializer(fieldRegistration.serializer()).readObject(ktBuilder, 1, field, fieldRegistration); + ktBuilder.append(TAB).append(StringUtils.format("packet.{} = {}", field.getName(), compatibleReadObject)).append(LS); + ktBuilder.append("}").append(LS); + continue; + } + var readObject = kotlinSerializer(fieldRegistration.serializer()).readObject(ktBuilder, 0, field, fieldRegistration); + ktBuilder.append(StringUtils.format("packet.{} = {}", field.getName(), readObject)).append(LS); + } + return ktBuilder.toString(); + } + + public static String toKotlinClassName(String typeName) { + typeName = typeName.replaceAll("java.util.|java.lang.", StringUtils.EMPTY); + typeName = typeName.replaceAll("[a-zA-Z0-9_.]*\\.", StringUtils.EMPTY); + + switch (typeName) { + case "boolean": + case "Boolean": + typeName = "Boolean"; + return typeName; + case "byte": + case "Byte": + typeName = "Byte"; + return typeName; + case "short": + case "Short": + typeName = "Short"; + return typeName; + case "int": + case "Integer": + typeName = "Int"; + return typeName; + case "long": + case "Long": + typeName = "Long"; + return typeName; + case "float": + case "Float": + typeName = "Float"; + return typeName; + case "double": + case "Double": + typeName = "Double"; + return typeName; + case "char": + case "Character": + case "String": + typeName = "String"; + return typeName; + default: + } + + // 将boolean转为bool + typeName = typeName.replaceAll("[B|b]oolean\\[", "Boolean"); + typeName = typeName.replace("", "Boolean>"); + + // 将Byte转为byte + typeName = typeName.replace("Byte[", "Byte"); + typeName = typeName.replace("Byte>", "Byte>"); + typeName = typeName.replace("", "Short>"); + typeName = typeName.replace("", "Int>"); + typeName = typeName.replace("", "Long>"); + typeName = typeName.replace("", "Float>"); + typeName = typeName.replace("", "Double>"); + typeName = typeName.replace("", "String>"); + typeName = typeName.replace("", "String>"); + typeName = typeName.replace(" field(Field field, IFieldRegistration fieldRegistration); + + void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration); + + String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration); + +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtArraySerializer.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtArraySerializer.java new file mode 100644 index 00000000..addeb6b3 --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtArraySerializer.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.model.Pair; +import com.zfoo.protocol.registration.field.ArrayField; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.serializer.CodeLanguage; +import com.zfoo.protocol.serializer.CutDownArraySerializer; +import com.zfoo.protocol.util.StringUtils; + +import java.lang.reflect.Field; + +import static com.zfoo.protocol.util.FileUtils.LS; + +/** + * @author godotg + */ +public class KtArraySerializer implements IKtSerializer { + + @Override + public Pair field(Field field, IFieldRegistration fieldRegistration) { + var type = StringUtils.format("Array<{}>", CodeGenerateKotlin.toKotlinClassName(field.getType().getComponentType().getSimpleName())); + return new Pair<>(type, "emptyArray()"); + } + + @Override + public void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + if (CutDownArraySerializer.getInstance().writeObject(builder, objectStr, field, fieldRegistration, CodeLanguage.Kotlin)) { + return; + } + + ArrayField arrayField = (ArrayField) fieldRegistration; + + builder.append(StringUtils.format("if (({} == null) || ({}.length == 0)) {", objectStr, objectStr)).append(LS); + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append("buffer.writeInt(0);").append(LS); + GenerateProtocolFile.addTab(builder, deep); + + builder.append("} else {").append(LS); + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append(StringUtils.format("buffer.writeInt({}.length);", objectStr)).append(LS); + GenerateProtocolFile.addTab(builder, deep + 1); + String length = "length" + GenerateProtocolFile.localVariableId++; + builder.append(StringUtils.format("int {} = {}.length;", length, objectStr)).append(LS); + + String i = "i" + GenerateProtocolFile.localVariableId++; + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append(StringUtils.format("for (int {} = 0; {} < {}; {}++) {", i, i, length, i)).append(LS); + GenerateProtocolFile.addTab(builder, deep + 2); + String element = "element" + GenerateProtocolFile.localVariableId++; + builder.append(StringUtils.format("{} {} = {}[{}];", CodeGenerateKotlin.toKotlinClassName(arrayField.getType().getSimpleName()), element, objectStr, i)).append(LS); + + CodeGenerateKotlin.kotlinSerializer(arrayField.getArrayElementRegistration().serializer()) + .writeObject(builder, element, deep + 2, field, arrayField.getArrayElementRegistration()); + + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append("}").append(LS); + GenerateProtocolFile.addTab(builder, deep); + builder.append("}").append(LS); + } + + @Override + public String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + var cutDown = CutDownArraySerializer.getInstance().readObject(builder, field, fieldRegistration, CodeLanguage.Kotlin); + if (cutDown != null) { + return cutDown; + } + + + var arrayField = (ArrayField) fieldRegistration; + var result = "result" + GenerateProtocolFile.localVariableId++; + + var typeName = CodeGenerateKotlin.toKotlinClassName(arrayField.getType().getSimpleName()); + + var i = "index" + GenerateProtocolFile.localVariableId++; + var size = "size" + GenerateProtocolFile.localVariableId++; + builder.append(StringUtils.format("int {} = buffer.readInt();", size)).append(LS); + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("{}[] {} = new {}[{}];", typeName, result, typeName, size)).append(LS); + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("if ({} > 0) {", size)).append(LS); + + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append(StringUtils.format("for (int {} = 0; {} < {}; {}++) {", i, i, size, i)).append(LS); + var readObject = CodeGenerateKotlin.kotlinSerializer(arrayField.getArrayElementRegistration().serializer()) + .readObject(builder, deep + 2, field, arrayField.getArrayElementRegistration()); + GenerateProtocolFile.addTab(builder, deep + 2); + builder.append(StringUtils.format("{}[{}] = {};", result, i, readObject)); + builder.append(LS); + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append("}").append(LS); + GenerateProtocolFile.addTab(builder, deep); + builder.append("}").append(LS); + + + return result; + } +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtBooleanSerializer.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtBooleanSerializer.java new file mode 100644 index 00000000..3f07d55a --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtBooleanSerializer.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.model.Pair; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.util.StringUtils; + +import java.lang.reflect.Field; + +import static com.zfoo.protocol.util.FileUtils.LS; + +/** + * @author godotg + */ +public class KtBooleanSerializer implements IKtSerializer { + + @Override + public Pair field(Field field, IFieldRegistration fieldRegistration) { + return new Pair<>("Boolean", "false"); + } + + @Override + public void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("buffer.writeBool({});", objectStr)).append(LS); + } + + @Override + public String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration) { + String result = "result" + GenerateProtocolFile.localVariableId++; + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("boolean {} = buffer.readBool();", result)).append(LS); + return result; + } +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtByteSerializer.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtByteSerializer.java new file mode 100644 index 00000000..ab2a3201 --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtByteSerializer.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.model.Pair; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.util.StringUtils; + +import java.lang.reflect.Field; + +import static com.zfoo.protocol.util.FileUtils.LS; + +/** + * @author godotg + */ +public class KtByteSerializer implements IKtSerializer { + + @Override + public Pair field(Field field, IFieldRegistration fieldRegistration) { + return new Pair<>("Byte", "0"); + } + + @Override + public void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("buffer.writeByte({});", objectStr)).append(LS); + } + + @Override + public String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration) { + String result = "result" + GenerateProtocolFile.localVariableId++; + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("byte {} = buffer.readByte();", result)).append(LS); + return result; + } + +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtDoubleSerializer.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtDoubleSerializer.java new file mode 100644 index 00000000..89dc760f --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtDoubleSerializer.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.model.Pair; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.util.StringUtils; + +import java.lang.reflect.Field; + +import static com.zfoo.protocol.util.FileUtils.LS; + +/** + * @author godotg + */ +public class KtDoubleSerializer implements IKtSerializer { + + @Override + public Pair field(Field field, IFieldRegistration fieldRegistration) { + return new Pair<>("Float", "0f"); + } + + @Override + public void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("buffer.writeDouble({});", objectStr)).append(LS); + } + + @Override + public String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration) { + String result = "result" + GenerateProtocolFile.localVariableId++; + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("double {} = buffer.readDouble();", result)).append(LS); + return result; + } + +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtFloatSerializer.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtFloatSerializer.java new file mode 100644 index 00000000..f4938634 --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtFloatSerializer.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.model.Pair; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.util.StringUtils; + +import java.lang.reflect.Field; + +import static com.zfoo.protocol.util.FileUtils.LS; + +/** + * @author godotg + */ +public class KtFloatSerializer implements IKtSerializer { + + @Override + public Pair field(Field field, IFieldRegistration fieldRegistration) { + return new Pair<>("Double", "0.0"); + } + + @Override + public void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("buffer.writeFloat({});", objectStr)).append(LS); + } + + @Override + public String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration) { + String result = "result" + GenerateProtocolFile.localVariableId++; + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("float {} = buffer.readFloat();", result)).append(LS); + return result; + } + +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtIntSerializer.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtIntSerializer.java new file mode 100644 index 00000000..748d2796 --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtIntSerializer.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.model.Pair; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.util.StringUtils; + +import java.lang.reflect.Field; + +import static com.zfoo.protocol.util.FileUtils.LS; + +/** + * @author godotg + */ +public class KtIntSerializer implements IKtSerializer { + + @Override + public Pair field(Field field, IFieldRegistration fieldRegistration) { + return new Pair<>("Int", "0"); + } + + @Override + public void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("buffer.writeInt({});", objectStr)).append(LS); + } + + @Override + public String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration) { + String result = "result" + GenerateProtocolFile.localVariableId++; + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("int {} = buffer.readInt();", result)).append(LS); + return result; + } +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtListSerializer.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtListSerializer.java new file mode 100644 index 00000000..442151b3 --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtListSerializer.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.model.Pair; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.registration.field.ListField; +import com.zfoo.protocol.serializer.CodeLanguage; +import com.zfoo.protocol.serializer.CutDownListSerializer; +import com.zfoo.protocol.util.StringUtils; + +import java.lang.reflect.Field; + +import static com.zfoo.protocol.util.FileUtils.LS; + +/** + * @author godotg + */ +public class KtListSerializer implements IKtSerializer { + + @Override + public Pair field(Field field, IFieldRegistration fieldRegistration) { + var type = StringUtils.format("{}", CodeGenerateKotlin.toKotlinClassName(field.getGenericType().toString())); + return new Pair<>(type, "emptyList()"); + } + + @Override + public void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + if (CutDownListSerializer.getInstance().writeObject(builder, objectStr, field, fieldRegistration, CodeLanguage.Kotlin)) { + return; + } + + ListField listField = (ListField) fieldRegistration; + builder.append(StringUtils.format("if ({} == null) {", objectStr)).append(LS); + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append("buffer.writeInt(0);").append(LS); + + GenerateProtocolFile.addTab(builder, deep); + builder.append("}").append(LS); + GenerateProtocolFile.addTab(builder, deep); + builder.append("else {").append(LS); + + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append(StringUtils.format("buffer.writeInt({}.size());", objectStr)).append(LS); + + GenerateProtocolFile.addTab(builder, deep + 1); + String length = "length" + GenerateProtocolFile.localVariableId++; + builder.append(StringUtils.format("int {} = {}.size();", length, objectStr)).append(LS); + + GenerateProtocolFile.addTab(builder, deep + 1); + String element = "element" + GenerateProtocolFile.localVariableId++; + builder.append(StringUtils.format("for (var {} : {}) {", element, objectStr)).append(LS); + + CodeGenerateKotlin.kotlinSerializer(listField.getListElementRegistration().serializer()) + .writeObject(builder, element, deep + 2, field, listField.getListElementRegistration()); + + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append("}").append(LS); + GenerateProtocolFile.addTab(builder, deep); + builder.append("}").append(LS); + } + + @Override + public String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + var cutDown = CutDownListSerializer.getInstance().readObject(builder, field, fieldRegistration, CodeLanguage.Kotlin); + if (cutDown != null) { + return cutDown; + } + + var listField = (ListField) fieldRegistration; + var result = "result" + GenerateProtocolFile.localVariableId++; + + var typeName = CodeGenerateKotlin.toKotlinClassName(listField.getType().toString()); + + var i = "index" + GenerateProtocolFile.localVariableId++; + var size = "size" + GenerateProtocolFile.localVariableId++; + + builder.append(StringUtils.format("int {} = buffer.readInt();", size)).append(LS); + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("var {} = new Array{}({});", result, typeName, size)).append(LS); + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("if ({} > 0) {", size)).append(LS); + + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append(StringUtils.format("for (int {} = 0; {} < {}; {}++) {", i, i, size, i)).append(LS); + var readObject = CodeGenerateKotlin.kotlinSerializer(listField.getListElementRegistration().serializer()) + .readObject(builder, deep + 2, field, listField.getListElementRegistration()); + GenerateProtocolFile.addTab(builder, deep + 2); + builder.append(StringUtils.format("{}.add({});", result, readObject)).append(LS); + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append("}").append(LS); + GenerateProtocolFile.addTab(builder, deep); + builder.append("}").append(LS); + + + return result; + } + +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtLongSerializer.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtLongSerializer.java new file mode 100644 index 00000000..7201850b --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtLongSerializer.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.model.Pair; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.util.StringUtils; + +import java.lang.reflect.Field; + +import static com.zfoo.protocol.util.FileUtils.LS; + +/** + * @author godotg + */ +public class KtLongSerializer implements IKtSerializer { + + @Override + public Pair field(Field field, IFieldRegistration fieldRegistration) { + return new Pair<>("Long", "0"); + } + + @Override + public void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("buffer.writeLong({});", objectStr)).append(LS); + } + + @Override + public String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration) { + String result = "result" + GenerateProtocolFile.localVariableId++; + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("long {} = buffer.readLong();", result)).append(LS); + return result; + } + +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtMapSerializer.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtMapSerializer.java new file mode 100644 index 00000000..89078f3a --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtMapSerializer.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.model.Pair; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.registration.field.MapField; +import com.zfoo.protocol.serializer.CodeLanguage; +import com.zfoo.protocol.serializer.CutDownMapSerializer; +import com.zfoo.protocol.util.StringUtils; + +import java.lang.reflect.Field; + +import static com.zfoo.protocol.util.FileUtils.LS; + +/** + * @author godotg + */ +public class KtMapSerializer implements IKtSerializer { + + @Override + public Pair field(Field field, IFieldRegistration fieldRegistration) { + var type = StringUtils.format("{}", CodeGenerateKotlin.toKotlinClassName(field.getGenericType().toString())); + return new Pair<>(type, "emptyMap()"); + } + + @Override + public void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + if (CutDownMapSerializer.getInstance().writeObject(builder, objectStr, field, fieldRegistration, CodeLanguage.Kotlin)) { + return; + } + + MapField mapField = (MapField) fieldRegistration; + + builder.append(StringUtils.format("if ({} == null) {", objectStr, objectStr)).append(LS); + + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append("buffer.writeInt(0);").append(LS); + + GenerateProtocolFile.addTab(builder, deep); + builder.append("} else {").append(LS); + + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append(StringUtils.format("buffer.writeInt({}.size());", objectStr)).append(LS); + + + String i = "i" + GenerateProtocolFile.localVariableId++; + + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append(StringUtils.format("for (var {} : {}.entrySet()) {", i, objectStr)).append(LS); + + GenerateProtocolFile.addTab(builder, deep + 2); + String key = "keyElement" + GenerateProtocolFile.localVariableId++; + String value = "valueElement" + GenerateProtocolFile.localVariableId++; + builder.append(StringUtils.format("var {} = {}.getKey();", key, i)).append(LS); + + GenerateProtocolFile.addTab(builder, deep + 2); + builder.append(StringUtils.format("var {} = {}.getValue();", value, i)).append(LS); + + CodeGenerateKotlin.kotlinSerializer(mapField.getMapKeyRegistration().serializer()) + .writeObject(builder, key, deep + 2, field, mapField.getMapKeyRegistration()); + CodeGenerateKotlin.kotlinSerializer(mapField.getMapValueRegistration().serializer()) + .writeObject(builder, value, deep + 2, field, mapField.getMapValueRegistration()); + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append("}").append(LS); + GenerateProtocolFile.addTab(builder, deep); + builder.append("}").append(LS); + } + + + @Override + public String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + var cutDown = CutDownMapSerializer.getInstance().readObject(builder, field, fieldRegistration, CodeLanguage.Kotlin); + if (cutDown != null) { + return cutDown; + } + + MapField mapField = (MapField) fieldRegistration; + String result = "result" + GenerateProtocolFile.localVariableId++; + + var typeName = CodeGenerateKotlin.toKotlinClassName(mapField.getType().toString()); + + String size = "size" + GenerateProtocolFile.localVariableId++; + builder.append(StringUtils.format("int {} = buffer.readInt();", size)).append(LS); + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("var {} = new Hash{}({});", result, typeName, size)).append(LS); + + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("if ({} > 0) {", size)).append(LS); + + String i = "index" + GenerateProtocolFile.localVariableId++; + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append(StringUtils.format("for (var {} = 0; {} < {}; {}++) {", i, i, size, i)).append(LS); + + String keyObject = CodeGenerateKotlin.kotlinSerializer(mapField.getMapKeyRegistration().serializer()) + .readObject(builder, deep + 2, field, mapField.getMapKeyRegistration()); + + + String valueObject = CodeGenerateKotlin.kotlinSerializer(mapField.getMapValueRegistration().serializer()) + .readObject(builder, deep + 2, field, mapField.getMapValueRegistration()); + GenerateProtocolFile.addTab(builder, deep + 2); + + builder.append(StringUtils.format("{}.put({}, {});", result, keyObject, valueObject)).append(LS); + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append("}").append(LS); + GenerateProtocolFile.addTab(builder, deep); + builder.append("}").append(LS); + return result; + } +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtObjectProtocolSerializer.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtObjectProtocolSerializer.java new file mode 100644 index 00000000..34fb9ddd --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtObjectProtocolSerializer.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.model.Pair; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.registration.field.ObjectProtocolField; +import com.zfoo.protocol.serializer.enhance.EnhanceObjectProtocolSerializer; +import com.zfoo.protocol.util.StringUtils; + +import java.lang.reflect.Field; + +import static com.zfoo.protocol.util.FileUtils.LS; + + +/** + * @author godotg + */ +public class KtObjectProtocolSerializer implements IKtSerializer { + + @Override + public Pair field(Field field, IFieldRegistration fieldRegistration) { + ObjectProtocolField objectProtocolField = (ObjectProtocolField) fieldRegistration; + var protocolSimpleName = EnhanceObjectProtocolSerializer.getProtocolClassSimpleName(objectProtocolField.getProtocolId()); + var type = StringUtils.format("{}?", protocolSimpleName); + return new Pair<>(type, "null"); + } + + @Override + public void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration) { + ObjectProtocolField objectProtocolField = (ObjectProtocolField) fieldRegistration; + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("buffer.writePacket({}, (short){});", objectStr, objectProtocolField.getProtocolId())) + .append(LS); + } + + @Override + public String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration) { + ObjectProtocolField objectProtocolField = (ObjectProtocolField) fieldRegistration; + String result = "result" + GenerateProtocolFile.localVariableId++; + + var protocolSimpleName = EnhanceObjectProtocolSerializer.getProtocolClassSimpleName(objectProtocolField.getProtocolId()); + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("{} {} = ({}) buffer.readPacket((short) {});", protocolSimpleName, result, protocolSimpleName, objectProtocolField.getProtocolId())) + .append(LS); + return result; + } + +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtSetSerializer.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtSetSerializer.java new file mode 100644 index 00000000..f561164b --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtSetSerializer.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.model.Pair; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.registration.field.SetField; +import com.zfoo.protocol.serializer.CodeLanguage; +import com.zfoo.protocol.serializer.CutDownSetSerializer; +import com.zfoo.protocol.util.StringUtils; + +import java.lang.reflect.Field; + +import static com.zfoo.protocol.util.FileUtils.LS; + +/** + * @author godotg + */ +public class KtSetSerializer implements IKtSerializer { + + @Override + public Pair field(Field field, IFieldRegistration fieldRegistration) { + var type = StringUtils.format("{}", CodeGenerateKotlin.toKotlinClassName(field.getGenericType().toString())); + return new Pair<>(type, "emptySet()"); + } + + @Override + public void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + if (CutDownSetSerializer.getInstance().writeObject(builder, objectStr, field, fieldRegistration, CodeLanguage.Kotlin)) { + return; + } + + SetField setField = (SetField) fieldRegistration; + + builder.append(StringUtils.format("if ({} == null) {", objectStr)).append(LS); + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append("buffer.writeInt(0);").append(LS); + + GenerateProtocolFile.addTab(builder, deep); + builder.append("} else {").append(LS); + + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append(StringUtils.format("buffer.writeInt({}.size());", objectStr)).append(LS); + + String element = "i" + GenerateProtocolFile.localVariableId++; + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append(StringUtils.format("for (var {} : {}) {", element, objectStr)).append(LS); + + CodeGenerateKotlin.kotlinSerializer(setField.getSetElementRegistration().serializer()) + .writeObject(builder, element, deep + 2, field, setField.getSetElementRegistration()); + + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append("}").append(LS); + GenerateProtocolFile.addTab(builder, deep); + builder.append("}").append(LS); + } + + @Override + public String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + var cutDown = CutDownSetSerializer.getInstance().readObject(builder, field, fieldRegistration, CodeLanguage.Kotlin); + if (cutDown != null) { + return cutDown; + } + + SetField setField = (SetField) fieldRegistration; + var result = "result" + GenerateProtocolFile.localVariableId++; + + var typeName = CodeGenerateKotlin.toKotlinClassName(setField.getType().toString()); + + var i = "index" + GenerateProtocolFile.localVariableId++; + var size = "size" + GenerateProtocolFile.localVariableId++; + builder.append(StringUtils.format("int {} = buffer.readInt();", size)).append(LS); + GenerateProtocolFile.addTab(builder, deep); + // unity里不支持HashSet的初始化大小 +// builder.append("var " + result + " = new " + typeName + "(" + size + ");" + LS); + builder.append(StringUtils.format("var {} = new Hash{}();", result, typeName)).append(LS); + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("if ({} > 0) {", size)).append(LS); + + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append(StringUtils.format("for (int {} = 0; {} < {}; {}++) {", i, i, size, i)).append(LS); + + var readObject = CodeGenerateKotlin.kotlinSerializer(setField.getSetElementRegistration().serializer()) + .readObject(builder, deep + 2, field, setField.getSetElementRegistration()); + GenerateProtocolFile.addTab(builder, deep + 2); + builder.append(StringUtils.format("{}.add({});", result, readObject)).append(LS); + GenerateProtocolFile.addTab(builder, deep + 1); + builder.append("}").append(LS); + GenerateProtocolFile.addTab(builder, deep); + builder.append("}").append(LS); + + return result; + } + +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtShortSerializer.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtShortSerializer.java new file mode 100644 index 00000000..d3afddc6 --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtShortSerializer.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.model.Pair; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.util.StringUtils; + +import java.lang.reflect.Field; + +import static com.zfoo.protocol.util.FileUtils.LS; + +/** + * @author godotg + */ +public class KtShortSerializer implements IKtSerializer { + + @Override + public Pair field(Field field, IFieldRegistration fieldRegistration) { + return new Pair<>("Short", "0"); + } + + @Override + public void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("buffer.writeShort({});", objectStr)).append(LS); + } + + @Override + public String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration) { + String result = "result" + GenerateProtocolFile.localVariableId++; + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("short {} = buffer.readShort();", result)).append(LS); + return result; + } + +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtStringSerializer.java b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtStringSerializer.java new file mode 100644 index 00000000..b5511226 --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/kotlin/KtStringSerializer.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.serializer.kotlin; + +import com.zfoo.protocol.generate.GenerateProtocolFile; +import com.zfoo.protocol.model.Pair; +import com.zfoo.protocol.registration.field.IFieldRegistration; +import com.zfoo.protocol.util.StringUtils; + +import java.lang.reflect.Field; + +import static com.zfoo.protocol.util.FileUtils.LS; + +/** + * @author godotg + */ +public class KtStringSerializer implements IKtSerializer { + + @Override + public Pair field(Field field, IFieldRegistration fieldRegistration) { + return new Pair<>("String", "\"\""); + } + + @Override + public void writeObject(StringBuilder builder, String objectStr, int deep, Field field, IFieldRegistration fieldRegistration) { + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("buffer.writeString({});", objectStr)).append(LS); + } + + @Override + public String readObject(StringBuilder builder, int deep, Field field, IFieldRegistration fieldRegistration) { + String result = "result" + GenerateProtocolFile.localVariableId++; + + GenerateProtocolFile.addTab(builder, deep); + builder.append(StringUtils.format("String {} = buffer.readString();", result)).append(LS); + return result; + } + + +} diff --git a/protocol/src/main/java/com/zfoo/protocol/serializer/typescript/CodeGenerateTypeScript.java b/protocol/src/main/java/com/zfoo/protocol/serializer/typescript/CodeGenerateTypeScript.java index eb9e00f7..4b7f8072 100644 --- a/protocol/src/main/java/com/zfoo/protocol/serializer/typescript/CodeGenerateTypeScript.java +++ b/protocol/src/main/java/com/zfoo/protocol/serializer/typescript/CodeGenerateTypeScript.java @@ -265,19 +265,19 @@ public class CodeGenerateTypeScript implements ICodeGenerate { var fieldRegistrations = registration.getFieldRegistrations(); // when generate source code fields, use origin fields sort var sequencedFields = ReflectionUtils.notStaticAndTransientFields(registration.getConstructor().getDeclaringClass()); - var fieldDefinitionBuilder = new StringBuilder(); + var tsBuilder = new StringBuilder(); for (var field : sequencedFields) { var fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)]; var fieldName = field.getName(); // 生成注释 var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.TypeScript); for (var fieldNote : fieldNotes) { - fieldDefinitionBuilder.append(fieldNote).append(LS); + tsBuilder.append(fieldNote).append(LS); } var triple = tsSerializer(fieldRegistration.serializer()).field(field, fieldRegistration); - fieldDefinitionBuilder.append(StringUtils.format("{}{} = {};", triple.getMiddle(), triple.getLeft(), triple.getRight())).append(LS); + tsBuilder.append(StringUtils.format("{}{} = {};", triple.getMiddle(), triple.getLeft(), triple.getRight())).append(LS); } - return fieldDefinitionBuilder.toString(); + return tsBuilder.toString(); } private String protocol_write_serialization(ProtocolRegistration registration) { diff --git a/protocol/src/main/resources/kotlin/ByteBuffer.kt b/protocol/src/main/resources/kotlin/ByteBuffer.kt new file mode 100644 index 00000000..d508b52f --- /dev/null +++ b/protocol/src/main/resources/kotlin/ByteBuffer.kt @@ -0,0 +1,1241 @@ +${protocol_root_path} + +import java.nio.charset.Charset + +class ByteBuffer { + private var buffer = ByteArray(INIT_SIZE) + private var writeOffset = 0 + private var readOffset = 0 + fun adjustPadding(predictionLength: Int, beforewriteIndex: Int) { + // 因为写入的是可变长的int,如果预留的位置过多,则清除多余的位置 + val currentwriteIndex = writeOffset + val predictionCount = writeIntCount(predictionLength) + val length = currentwriteIndex - beforewriteIndex - predictionCount + val lengthCount = writeIntCount(length) + val padding = lengthCount - predictionCount + if (padding == 0) { + writeOffset = beforewriteIndex + writeInt(length) + writeOffset = currentwriteIndex + } else { + val bytes = ByteArray(length) + System.arraycopy(buffer, currentwriteIndex - length, bytes, 0, length) + writeOffset = beforewriteIndex + writeInt(length) + writeBytes(bytes) + } + } + + fun compatibleRead(beforereadIndex: Int, length: Int): Boolean { + return length != -1 && readOffset < length + beforereadIndex + } + + // -------------------------------------------------get/set------------------------------------------------- + fun writeOffset(): Int { + return writeOffset + } + + fun setwriteOffset(writeIndex: Int) { + if (writeIndex > buffer.size) { + throw RuntimeException( + "writeIndex[" + writeIndex + "] out of bounds exception: readerIndex: " + readOffset + + ", writerIndex: " + writeOffset + "(expected: 0 <= readerIndex <= writerIndex <= capacity:" + buffer.size + ) + } + writeOffset = writeIndex + } + + fun readOffset(): Int { + return readOffset + } + + fun setReadOffset(readIndex: Int) { + if (readIndex > writeOffset) { + throw RuntimeException( + "readIndex[" + readIndex + "] out of bounds exception: readerIndex: " + readOffset + + ", writerIndex: " + writeOffset + "(expected: 0 <= readerIndex <= writerIndex <= capacity:" + buffer.size + ) + } + readOffset = readIndex + } + + fun toBytes(): ByteArray { + val bytes = ByteArray(writeOffset) + System.arraycopy(buffer, 0, bytes, 0, writeOffset) + return bytes + } + + val isReadable: Boolean + get() = writeOffset > readOffset + + // -------------------------------------------------write/read------------------------------------------------- + fun writeBool(value: Boolean) { + ensureCapacity(1) + buffer[writeOffset] = if (value) 1.toByte() else 0.toByte() + writeOffset++ + } + + fun readBool(): Boolean { + val byteValue = buffer[readOffset] + readOffset++ + return byteValue.toInt() == 1 + } + + fun writeByte(value: Byte) { + ensureCapacity(1) + buffer[writeOffset++] = value + } + + fun readByte(): Byte { + return buffer[readOffset++] + } + + val capacity: Int + get() = buffer.size - writeOffset + + fun ensureCapacity(capacity: Int) { + while (capacity - this.capacity > 0) { + val newSize = buffer.size * 2 + if (newSize > MAX_SIZE) { + throw RuntimeException("Bytebuf max size is [655537], out of memory error") + } + val newBytes = ByteArray(newSize) + System.arraycopy(buffer, 0, newBytes, 0, buffer.size) + buffer = newBytes + } + } + + @JvmOverloads + fun writeBytes(bytes: ByteArray, length: Int = bytes.size) { + ensureCapacity(length) + System.arraycopy(bytes, 0, buffer, writeOffset, length) + writeOffset += length + } + + fun readBytes(count: Int): ByteArray { + val bytes = ByteArray(count) + System.arraycopy(buffer, readOffset, bytes, 0, count) + readOffset += count + return bytes + } + + fun writeShort(value: Short) { + ensureCapacity(2) + buffer[writeOffset++] = (value.toInt() ushr 8).toByte() + buffer[writeOffset++] = value.toByte() + } + + fun readShort(): Short { + return (buffer[readOffset++].toInt() shl 8 or (buffer[readOffset++].toInt() and 255)).toShort() + } + + // *******************************************int*************************************************** + fun writeInt(value: Int): Int { + return writeVarInt(value shl 1 xor (value shr 31)) + } + + fun writeVarInt(value: Int): Int { + var a = value ushr 7 + if (a == 0) { + writeByte(value.toByte()) + return 1 + } + ensureCapacity(5) + buffer[writeOffset++] = (value or 0x80).toByte() + var b = value ushr 14 + if (b == 0) { + buffer[writeOffset++] = a.toByte() + return 2 + } + buffer[writeOffset++] = (a or 0x80).toByte() + a = value ushr 21 + if (a == 0) { + buffer[writeOffset++] = b.toByte() + return 3 + } + buffer[writeOffset++] = (b or 0x80).toByte() + b = value ushr 28 + if (b == 0) { + buffer[writeOffset++] = a.toByte() + return 4 + } + buffer[writeOffset++] = (a or 0x80).toByte() + buffer[writeOffset++] = b.toByte() + return 5 + } + + fun readInt(): Int { + var b = readByte().toInt() + var value = b + if (b < 0) { + b = readByte().toInt() + value = value and 0x0000007F or (b shl 7) + if (b < 0) { + b = readByte().toInt() + value = value and 0x00003FFF or (b shl 14) + if (b < 0) { + b = readByte().toInt() + value = value and 0x001FFFFF or (b shl 21) + if (b < 0) { + value = value and 0x0FFFFFFF or (readByte().toInt() shl 28) + } + } + } + } + return value ushr 1 xor -(value and 1) + } + + fun writeIntCount(value: Int): Int { + var value = value + value = value shl 1 xor (value shr 31) + if (value ushr 7 == 0) { + return 1 + } + if (value ushr 14 == 0) { + return 2 + } + if (value ushr 21 == 0) { + return 3 + } + return if (value ushr 28 == 0) { + 4 + } else 5 + } + + // 写入没有压缩的int + fun writeRawInt(value: Int) { + ensureCapacity(4) + buffer[writeOffset++] = (value ushr 24).toByte() + buffer[writeOffset++] = (value ushr 16).toByte() + buffer[writeOffset++] = (value ushr 8).toByte() + buffer[writeOffset++] = value.toByte() + } + + // 读取没有压缩的int + fun readRawInt(): Int { + return buffer[readOffset++].toInt() and 255 shl 24 or (buffer[readOffset++].toInt() and 255 shl 16) or (buffer[readOffset++].toInt() and 255 shl 8) or (buffer[readOffset++].toInt() and 255) + } + + // *******************************************long************************************************** + fun writeLong(value: Long) { + val mask = value shl 1 xor (value shr 63) + if (mask ushr 32 == 0L) { + writeVarInt(mask.toInt()) + return + } + val bytes = ByteArray(9) + bytes[0] = (mask or 0x80L).toByte() + bytes[1] = (mask ushr 7 or 0x80L).toByte() + bytes[2] = (mask ushr 14 or 0x80L).toByte() + bytes[3] = (mask ushr 21 or 0x80L).toByte() + var a = (mask ushr 28).toInt() + var b = (mask ushr 35).toInt() + if (b == 0) { + bytes[4] = a.toByte() + writeBytes(bytes, 5) + return + } + bytes[4] = (a or 0x80).toByte() + a = (mask ushr 42).toInt() + if (a == 0) { + bytes[5] = b.toByte() + writeBytes(bytes, 6) + return + } + bytes[5] = (b or 0x80).toByte() + b = (mask ushr 49).toInt() + if (b == 0) { + bytes[6] = a.toByte() + writeBytes(bytes, 7) + return + } + bytes[6] = (a or 0x80).toByte() + a = (mask ushr 56).toInt() + if (a == 0) { + bytes[7] = b.toByte() + writeBytes(bytes, 8) + return + } + bytes[7] = (b or 0x80).toByte() + bytes[8] = a.toByte() + writeBytes(bytes, 9) + } + + fun readLong(): Long { + var b = readByte().toLong() + var value = b + if (b < 0) { + b = readByte().toLong() + value = value and 0x000000000000007FL or (b shl 7) + if (b < 0) { + b = readByte().toLong() + value = value and 0x0000000000003FFFL or (b shl 14) + if (b < 0) { + b = readByte().toLong() + value = value and 0x00000000001FFFFFL or (b shl 21) + if (b < 0) { + b = readByte().toLong() + value = value and 0x000000000FFFFFFFL or (b shl 28) + if (b < 0) { + b = readByte().toLong() + value = value and 0x00000007FFFFFFFFL or (b shl 35) + if (b < 0) { + b = readByte().toLong() + value = value and 0x000003FFFFFFFFFFL or (b shl 42) + if (b < 0) { + b = readByte().toLong() + value = value and 0x0001FFFFFFFFFFFFL or (b shl 49) + if (b < 0) { + b = readByte().toLong() + value = value and 0x00FFFFFFFFFFFFFFL or (b shl 56) + } + } + } + } + } + } + } + } + return value ushr 1 xor -(value and 1L) + } + + fun writeRawLong(value: Long) { + ensureCapacity(8) + buffer[writeOffset++] = (value ushr 56).toInt().toByte() + buffer[writeOffset++] = (value ushr 48).toInt().toByte() + buffer[writeOffset++] = (value ushr 40).toInt().toByte() + buffer[writeOffset++] = (value ushr 32).toInt().toByte() + buffer[writeOffset++] = (value ushr 24).toInt().toByte() + buffer[writeOffset++] = (value ushr 16).toInt().toByte() + buffer[writeOffset++] = (value ushr 8).toInt().toByte() + buffer[writeOffset++] = value.toInt().toByte() + } + + fun readRawLong(): Long { + return buffer[readOffset++].toLong() and 255L shl 56 or (buffer[readOffset++].toLong() and 255L shl 48) or (buffer[readOffset++].toLong() and 255L shl 40) or (buffer[readOffset++].toLong() and 255L shl 32) or (buffer[readOffset++].toLong() and 255L shl 24) or (buffer[readOffset++].toLong() and 255L shl 16) or (buffer[readOffset++].toLong() and 255L shl 8) or (buffer[readOffset++].toLong() and 255L) + } + + // *******************************************float*************************************************** + fun writeFloat(value: Float) { + writeRawInt(java.lang.Float.floatToRawIntBits(value)) + } + + fun readFloat(): Float { + return java.lang.Float.intBitsToFloat(readRawInt()) + } + + // *******************************************double*************************************************** + fun writeDouble(value: Double) { + writeRawLong(java.lang.Double.doubleToRawLongBits(value)) + } + + fun readDouble(): Double { + return java.lang.Double.longBitsToDouble(readRawLong()) + } + + fun writeString(value: String?) { + if (value == null || value.isEmpty()) { + writeInt(0) + return + } + val bytes = value.toByteArray(DEFAULT_CHARSET) + writeInt(bytes.size) + writeBytes(bytes) + } + + fun readString(): String { + val length = readInt() + if (length <= 0) { + return "" + } + val bytes = readBytes(length) + return String(bytes, DEFAULT_CHARSET) + } + + fun writeBooleanArray(array: BooleanArray?) { + if (array == null || array.size == 0) { + writeInt(0) + } else { + writeInt(array.size) + val length = array.size + for (index in 0 until length) { + writeBool(array[index]) + } + } + } + + fun readBooleanArray(): BooleanArray { + val size = readInt() + val array = BooleanArray(size) + if (size > 0) { + for (index in 0 until size) { + array[index] = readBool() + } + } + return array + } + + fun writeByteArray(array: ByteArray?) { + if (array == null || array.size == 0) { + writeInt(0) + } else { + writeInt(array.size) + val length = array.size + for (index in 0 until length) { + writeByte(array[index]) + } + } + } + + fun readByteArray(): ByteArray { + val size = readInt() + val array = ByteArray(size) + if (size > 0) { + for (index in 0 until size) { + array[index] = readByte() + } + } + return array + } + + fun writeShortArray(array: ShortArray?) { + if (array == null || array.size == 0) { + writeInt(0) + } else { + writeInt(array.size) + val length = array.size + for (index in 0 until length) { + writeShort(array[index]) + } + } + } + + fun readShortArray(): ShortArray { + val size = readInt() + val array = ShortArray(size) + if (size > 0) { + for (index in 0 until size) { + array[index] = readShort() + } + } + return array + } + + fun writeIntArray(array: IntArray?) { + if (array == null || array.size == 0) { + writeInt(0) + } else { + writeInt(array.size) + val length = array.size + for (index in 0 until length) { + writeInt(array[index]) + } + } + } + + fun readIntArray(): IntArray { + val size = readInt() + val array = IntArray(size) + if (size > 0) { + for (index in 0 until size) { + array[index] = readInt() + } + } + return array + } + + fun writeLongArray(array: LongArray?) { + if (array == null || array.size == 0) { + writeInt(0) + } else { + writeInt(array.size) + val length = array.size + for (index in 0 until length) { + writeLong(array[index]) + } + } + } + + fun readLongArray(): LongArray { + val size = readInt() + val array = LongArray(size) + if (size > 0) { + for (index in 0 until size) { + array[index] = readLong() + } + } + return array + } + + fun writeFloatArray(array: FloatArray?) { + if (array == null || array.size == 0) { + writeInt(0) + } else { + writeInt(array.size) + val length = array.size + for (index in 0 until length) { + writeFloat(array[index]) + } + } + } + + fun readFloatArray(): FloatArray { + val size = readInt() + val array = FloatArray(size) + if (size > 0) { + for (index in 0 until size) { + array[index] = readFloat() + } + } + return array + } + + fun writeDoubleArray(array: DoubleArray?) { + if (array == null || array.size == 0) { + writeInt(0) + } else { + writeInt(array.size) + val length = array.size + for (index in 0 until length) { + writeDouble(array[index]) + } + } + } + + fun readDoubleArray(): DoubleArray { + val size = readInt() + val array = DoubleArray(size) + if (size > 0) { + for (index in 0 until size) { + array[index] = readDouble() + } + } + return array + } + + fun writeStringArray(array: Array?) { + if (array == null || array.size == 0) { + writeInt(0) + } else { + writeInt(array.size) + val length = array.size + for (index in 0 until length) { + writeString(array[index]) + } + } + } + + fun readStringArray(): Array { + val size = readInt() + val array = arrayOfNulls(size) + if (size > 0) { + for (index in 0 until size) { + array[index] = readString() + } + } + return array + } + + fun writeBooleanList(list: List?) { + if (list == null || list.isEmpty()) { + writeInt(0) + } else { + writeInt(list.size) + for (ele in list) { + writeBool(ele) + } + } + } + + fun readBooleanList(): List { + val size = readInt() + val list: MutableList = ArrayList() + if (size > 0) { + for (index in 0 until size) { + list.add(readBool()) + } + } + return list + } + + fun writeByteList(list: List?) { + if (list == null || list.isEmpty()) { + writeInt(0) + } else { + writeInt(list.size) + for (ele in list) { + writeByte(ele) + } + } + } + + fun readByteList(): List { + val size = readInt() + val list: MutableList = ArrayList() + if (size > 0) { + for (index in 0 until size) { + list.add(readByte()) + } + } + return list + } + + fun writeShortList(list: List?) { + if (list == null || list.isEmpty()) { + writeInt(0) + } else { + writeInt(list.size) + for (ele in list) { + writeShort(ele) + } + } + } + + fun readShortList(): List { + val size = readInt() + val list: MutableList = ArrayList() + if (size > 0) { + for (index in 0 until size) { + list.add(readShort()) + } + } + return list + } + + fun writeIntList(list: List?) { + if (list == null || list.isEmpty()) { + writeInt(0) + } else { + writeInt(list.size) + for (ele in list) { + writeInt(ele) + } + } + } + + fun readIntList(): List { + val size = readInt() + val list: MutableList = ArrayList() + if (size > 0) { + for (index in 0 until size) { + list.add(readInt()) + } + } + return list + } + + fun writeLongList(list: List?) { + if (list == null || list.isEmpty()) { + writeInt(0) + } else { + writeInt(list.size) + for (ele in list) { + writeLong(ele) + } + } + } + + fun readLongList(): List { + val size = readInt() + val list: MutableList = ArrayList() + if (size > 0) { + for (index in 0 until size) { + list.add(readLong()) + } + } + return list + } + + fun writeFloatList(list: List?) { + if (list == null || list.isEmpty()) { + writeInt(0) + } else { + writeInt(list.size) + for (ele in list) { + writeFloat(ele) + } + } + } + + fun readFloatList(): List { + val size = readInt() + val list: MutableList = ArrayList() + if (size > 0) { + for (index in 0 until size) { + list.add(readFloat()) + } + } + return list + } + + fun writeDoubleList(list: List?) { + if (list == null || list.isEmpty()) { + writeInt(0) + } else { + writeInt(list.size) + for (ele in list) { + writeDouble(ele) + } + } + } + + fun readDoubleList(): List { + val size = readInt() + val list: MutableList = ArrayList() + if (size > 0) { + for (index in 0 until size) { + list.add(readDouble()) + } + } + return list + } + + fun writeStringList(list: List?) { + if (list == null || list.isEmpty()) { + writeInt(0) + } else { + writeInt(list.size) + for (ele in list) { + writeString(ele) + } + } + } + + fun readStringList(): List { + val size = readInt() + val list: MutableList = ArrayList() + if (size > 0) { + for (index in 0 until size) { + list.add(readString()) + } + } + return list + } + + fun writePacketList(list: List<*>?, protocolId: Short) { + if (list == null || list.isEmpty()) { + writeInt(0) + } else { + val protocolRegistration = ProtocolManager.getProtocol(protocolId) + writeInt(list.size) + for (ele in list) { + protocolRegistration.write(this, ele) + } + } + } + + fun readPacketList(clazz: Class?, protocolId: Short): List { + val size = readInt() + val list: MutableList = ArrayList() + if (size > 0) { + val protocolRegistration = ProtocolManager.getProtocol(protocolId) + for (index in 0 until size) { + list.add(protocolRegistration.read(this) as T) + } + } + return list + } + + fun writeBooleanSet(set: Set?) { + if (set == null || set.isEmpty()) { + writeInt(0) + } else { + writeInt(set.size) + for (ele in set) { + writeBool(ele) + } + } + } + + fun readBooleanSet(): Set { + val size = readInt() + val set: MutableSet = HashSet() + if (size > 0) { + for (index in 0 until size) { + set.add(readBool()) + } + } + return set + } + + fun writeShortSet(set: Set?) { + if (set == null || set.isEmpty()) { + writeInt(0) + } else { + writeInt(set.size) + for (ele in set) { + writeShort(ele) + } + } + } + + fun readShortSet(): Set { + val size = readInt() + val set: MutableSet = HashSet() + if (size > 0) { + for (index in 0 until size) { + set.add(readShort()) + } + } + return set + } + + fun writeIntSet(set: Set?) { + if (set == null || set.isEmpty()) { + writeInt(0) + } else { + writeInt(set.size) + for (ele in set) { + writeInt(ele) + } + } + } + + fun readIntSet(): Set { + val size = readInt() + val set: MutableSet = HashSet() + if (size > 0) { + for (index in 0 until size) { + set.add(readInt()) + } + } + return set + } + + fun writeLongSet(set: Set?) { + if (set == null || set.isEmpty()) { + writeInt(0) + } else { + writeInt(set.size) + for (ele in set) { + writeLong(ele) + } + } + } + + fun readLongSet(): Set { + val size = readInt() + val set: MutableSet = HashSet() + if (size > 0) { + for (index in 0 until size) { + set.add(readLong()) + } + } + return set + } + + fun writeFloatSet(set: Set?) { + if (set == null || set.isEmpty()) { + writeInt(0) + } else { + writeInt(set.size) + for (ele in set) { + writeFloat(ele) + } + } + } + + fun readFloatSet(): Set { + val size = readInt() + val set: MutableSet = HashSet() + if (size > 0) { + for (index in 0 until size) { + set.add(readFloat()) + } + } + return set + } + + fun writeDoubleSet(set: Set?) { + if (set == null || set.isEmpty()) { + writeInt(0) + } else { + writeInt(set.size) + for (ele in set) { + writeDouble(ele) + } + } + } + + fun readDoubleSet(): Set { + val size = readInt() + val set: MutableSet = HashSet() + if (size > 0) { + for (index in 0 until size) { + set.add(readDouble()) + } + } + return set + } + + fun writeStringSet(set: Set?) { + if (set == null || set.isEmpty()) { + writeInt(0) + } else { + writeInt(set.size) + for (ele in set) { + writeString(ele) + } + } + } + + fun readStringSet(): Set { + val size = readInt() + val set: MutableSet = HashSet() + if (size > 0) { + for (index in 0 until size) { + set.add(readString()) + } + } + return set + } + + fun writePacketSet(set: Set<*>?, protocolId: Short) { + if (set == null || set.isEmpty()) { + writeInt(0) + } else { + val protocolRegistration = ProtocolManager.getProtocol(protocolId) + writeInt(set.size) + for (element in set) { + protocolRegistration.write(this, element) + } + } + } + + fun readPacketSet(clazz: Class?, protocolId: Short): Set { + val size = readInt() + val set: MutableSet = HashSet() + if (size > 0) { + val protocolRegistration = ProtocolManager.getProtocol(protocolId) + for (index in 0 until size) { + set.add(protocolRegistration.read(this) as T) + } + } + return set + } + + fun writeIntIntMap(map: Map?) { + if (map == null || map.isEmpty()) { + writeInt(0) + } else { + writeInt(map.size) + for ((key, value) in map) { + writeInt(key) + writeInt(value) + } + } + } + + fun readIntIntMap(): Map { + val size = readInt() + val map: MutableMap = HashMap() + if (size > 0) { + for (index in 0 until size) { + val key = readInt() + val value = readInt() + map[key] = value + } + } + return map + } + + fun writeIntLongMap(map: Map?) { + if (map == null || map.isEmpty()) { + writeInt(0) + } else { + writeInt(map.size) + for ((key, value) in map) { + writeInt(key) + writeLong(value) + } + } + } + + fun readIntLongMap(): Map { + val size = readInt() + val map: MutableMap = HashMap() + if (size > 0) { + for (index in 0 until size) { + val key = readInt() + val value = readLong() + map[key] = value + } + } + return map + } + + fun writeIntStringMap(map: Map?) { + if (map == null || map.isEmpty()) { + writeInt(0) + } else { + writeInt(map.size) + for ((key, value) in map) { + writeInt(key) + writeString(value) + } + } + } + + fun readIntStringMap(): Map { + val size = readInt() + val map: MutableMap = HashMap() + if (size > 0) { + for (index in 0 until size) { + val key = readInt() + val value = readString() + map[key] = value + } + } + return map + } + + fun writeIntPacketMap(map: Map?, protocolId: Short) { + if (map == null || map.isEmpty()) { + writeInt(0) + } else { + val protocolRegistration = ProtocolManager.getProtocol(protocolId) + writeInt(map.size) + for ((key, value) in map) { + writeInt(key) + protocolRegistration.write(this, value) + } + } + } + + fun readIntPacketMap(clazz: Class?, protocolId: Short): Map { + val size = readInt() + val map: MutableMap = HashMap() + if (size > 0) { + val protocolRegistration = ProtocolManager.getProtocol(protocolId) + for (index in 0 until size) { + val key = readInt() + val value = protocolRegistration.read(this) as T + map[key] = value + } + } + return map + } + + fun writeLongIntMap(map: Map?) { + if (map == null || map.isEmpty()) { + writeInt(0) + } else { + writeInt(map.size) + for ((key, value) in map) { + writeLong(key) + writeInt(value) + } + } + } + + fun readLongIntMap(): Map { + val size = readInt() + val map: MutableMap = HashMap() + if (size > 0) { + for (index in 0 until size) { + val key = readLong() + val value = readInt() + map[key] = value + } + } + return map + } + + fun writeLongLongMap(map: Map?) { + if (map == null || map.isEmpty()) { + writeInt(0) + } else { + writeInt(map.size) + for ((key, value) in map) { + writeLong(key) + writeLong(value) + } + } + } + + fun readLongLongMap(): Map { + val size = readInt() + val map: MutableMap = HashMap() + if (size > 0) { + for (index in 0 until size) { + val key = readLong() + val value = readLong() + map[key] = value + } + } + return map + } + + fun writeLongStringMap(map: Map?) { + if (map == null || map.isEmpty()) { + writeInt(0) + } else { + writeInt(map.size) + for ((key, value) in map) { + writeLong(key) + writeString(value) + } + } + } + + fun readLongStringMap(): Map { + val size = readInt() + val map: MutableMap = HashMap() + if (size > 0) { + for (index in 0 until size) { + val key = readLong() + val value = readString() + map[key] = value + } + } + return map + } + + fun writeLongPacketMap(map: Map?, protocolId: Short) { + if (map == null || map.isEmpty()) { + writeInt(0) + } else { + val protocolRegistration = ProtocolManager.getProtocol(protocolId) + writeInt(map.size) + for ((key, value) in map) { + writeLong(key) + protocolRegistration.write(this, value) + } + } + } + + fun readLongPacketMap(clazz: Class?, protocolId: Short): Map { + val size = readInt() + val map: MutableMap = HashMap() + if (size > 0) { + val protocolRegistration = ProtocolManager.getProtocol(protocolId) + for (index in 0 until size) { + val key = readLong() + val value = protocolRegistration.read(this) as T + map[key] = value + } + } + return map + } + + fun writeStringIntMap(map: Map?) { + if (map == null || map.isEmpty()) { + writeInt(0) + } else { + writeInt(map.size) + for ((key, value) in map) { + writeString(key) + writeInt(value) + } + } + } + + fun readStringIntMap(): Map { + val size = readInt() + val map: MutableMap = HashMap() + if (size > 0) { + for (index in 0 until size) { + val key = readString() + val value = readInt() + map[key] = value + } + } + return map + } + + fun writeStringLongMap(map: Map?) { + if (map == null || map.isEmpty()) { + writeInt(0) + } else { + writeInt(map.size) + for ((key, value) in map) { + writeString(key) + writeLong(value) + } + } + } + + fun readStringLongMap(): Map { + val size = readInt() + val map: MutableMap = HashMap() + if (size > 0) { + for (index in 0 until size) { + val key = readString() + val value = readLong() + map[key] = value + } + } + return map + } + + fun writeStringStringMap(map: Map?) { + if (map == null || map.isEmpty()) { + writeInt(0) + } else { + writeInt(map.size) + for ((key, value) in map) { + writeString(key) + writeString(value) + } + } + } + + fun readStringStringMap(): Map { + val size = readInt() + val map: MutableMap = HashMap() + if (size > 0) { + for (index in 0 until size) { + val key = readString() + val value = readString() + map[key] = value + } + } + return map + } + + fun writeStringPacketMap(map: Map?, protocolId: Short) { + if (map == null || map.isEmpty()) { + writeInt(0) + } else { + val protocolRegistration = ProtocolManager.getProtocol(protocolId) + writeInt(map.size) + for ((key, value) in map) { + writeString(key) + protocolRegistration.write(this, value) + } + } + } + + fun readStringPacketMap(clazz: Class?, protocolId: Short): Map { + val size = readInt() + val map: MutableMap = HashMap() + if (size > 0) { + val protocolRegistration = ProtocolManager.getProtocol(protocolId) + for (index in 0 until size) { + val key = readString() + val value = protocolRegistration.read(this) as T + map[key] = value + } + } + return map + } + + fun writePacket(packet: Any?, protocolId: Short) { + val protocolRegistration = ProtocolManager.getProtocol(protocolId) + protocolRegistration.write(this, packet) + } + + fun readPacket(protocolId: Short): Any { + val protocolRegistration = ProtocolManager.getProtocol(protocolId) + return protocolRegistration.read(this) + } + + companion object { + private const val INIT_SIZE = 128 + private const val MAX_SIZE = 655537 + + // *******************************************String*************************************************** + const val DEFAULT_CHARSET_NAME = "UTF-8" + val DEFAULT_CHARSET = Charset.forName(DEFAULT_CHARSET_NAME) + } +} \ No newline at end of file diff --git a/protocol/src/main/resources/kotlin/IProtocolRegistration.kt b/protocol/src/main/resources/kotlin/IProtocolRegistration.kt new file mode 100644 index 00000000..71459b9b --- /dev/null +++ b/protocol/src/main/resources/kotlin/IProtocolRegistration.kt @@ -0,0 +1,7 @@ +${protocol_root_path} + +interface IProtocolRegistration { + fun protocolId(): Short + fun write(buffer: ByteBuffer, packet: Any?) + fun read(buffer: ByteBuffer): Any? +} \ No newline at end of file diff --git a/protocol/src/main/resources/kotlin/ProtocolClassTemplate.kt b/protocol/src/main/resources/kotlin/ProtocolClassTemplate.kt new file mode 100644 index 00000000..c80081c2 --- /dev/null +++ b/protocol/src/main/resources/kotlin/ProtocolClassTemplate.kt @@ -0,0 +1,4 @@ +${protocol_note} +class ${protocol_name} { + ${protocol_field_definition} +} \ No newline at end of file diff --git a/protocol/src/main/resources/kotlin/ProtocolManagerTemplate.kt b/protocol/src/main/resources/kotlin/ProtocolManagerTemplate.kt new file mode 100644 index 00000000..88401f30 --- /dev/null +++ b/protocol/src/main/resources/kotlin/ProtocolManagerTemplate.kt @@ -0,0 +1,32 @@ +${protocol_root_path} +${protocol_imports} +class ProtocolManager { + val protocols = arrayOfNulls(Short.MAX_VALUE.toInt()) + var protocolIdMap: MutableMap, Short> = HashMap() + fun initProtocol() { + // initProtocol + ${protocol_manager_registrations} + } + + fun getProtocolId(clazz: Class<*>): Short { + return protocolIdMap[clazz]!! + } + + fun getProtocol(protocolId: Short): IProtocolRegistration { + return protocols[protocolId.toInt()] + ?: throw RuntimeException("[protocolId:$protocolId] not exist") + } + + fun write(buffer: ByteBuffer, packet: Any) { + val protocolId = getProtocolId(packet.javaClass) + // write protocol id to buffer + buffer.writeShort(protocolId) + // write packet + getProtocol(protocolId).write(buffer, packet) + } + + fun read(buffer: ByteBuffer): Any? { + val protocolId = buffer.readShort() + return getProtocol(protocolId).read(buffer) + } +} \ No newline at end of file diff --git a/protocol/src/main/resources/kotlin/ProtocolRegistrationTemplate.kt b/protocol/src/main/resources/kotlin/ProtocolRegistrationTemplate.kt new file mode 100644 index 00000000..0a86c234 --- /dev/null +++ b/protocol/src/main/resources/kotlin/ProtocolRegistrationTemplate.kt @@ -0,0 +1,31 @@ +companion object { + @JvmField + val registration${protocol_name}: IProtocolRegistration = object : IProtocolRegistration { + override fun protocolId(): Short { + return ${protocol_id} + } + + override fun write(buffer: ByteBuffer, packet: Any?) { + if (packet == null) { + buffer.writeInt(0) + return + } + val message = packet as ${protocol_name} + ${protocol_write_serialization} + } + + override fun read(buffer: ByteBuffer): Any? { + val length = buffer.readInt() + if (length == 0) { + return null + } + val beforeReadIndex = buffer.readOffset() + val packet = ${protocol_name}() + ${protocol_read_deserialization} + if (length > 0) { + buffer.setReadOffset(beforeReadIndex + length) + } + return packet + } + } +} \ No newline at end of file diff --git a/protocol/src/main/resources/kotlin/ProtocolTemplate.kt b/protocol/src/main/resources/kotlin/ProtocolTemplate.kt new file mode 100644 index 00000000..bf1efdcf --- /dev/null +++ b/protocol/src/main/resources/kotlin/ProtocolTemplate.kt @@ -0,0 +1,8 @@ +${protocol_root_path} +${protocol_imports} +${protocol_note} +class ${protocol_name} { + ${protocol_field_definition} + + ${protocol_registration} +} \ No newline at end of file diff --git a/protocol/src/main/resources/kotlin/ProtocolsTemplate.java b/protocol/src/main/resources/kotlin/ProtocolsTemplate.java new file mode 100644 index 00000000..4a803366 --- /dev/null +++ b/protocol/src/main/resources/kotlin/ProtocolsTemplate.java @@ -0,0 +1,10 @@ +${protocol_root_path} +${protocol_imports} +import java.util.*; +public class Protocols { + ${protocol_class} + + // ----------------------------------------------------------------------------------------------------------------- + + ${protocol_registration} +} diff --git a/protocol/src/test/kotlin/Main.java b/protocol/src/test/kotlin/Main.java new file mode 100644 index 00000000..d3c20c13 --- /dev/null +++ b/protocol/src/test/kotlin/Main.java @@ -0,0 +1,255 @@ +import com.zfoo.java.ByteBuffer; +import com.zfoo.java.ProtocolManager; + +import java.io.*; +import java.util.List; + +/** + * @author godotg + */ +public class Main { + + + + public static void main(String[] args) throws IOException { + System.out.println("zfoo test"); + ProtocolManager.initProtocol(); + byteBufferTest(); + compatibleTest(); + normalReadTest(); + } + + + public static void byteBufferTest() { + byteTest(); + bytesTest(); + shortTest(); + intTest(); + longTest(); + floatTest(); + doubleTest(); + stringTest(); + } + + public static void byteTest() { + byte value = 9; + ByteBuffer writerByteBuffer = new ByteBuffer(); + writerByteBuffer.writeByte(value); + byte[] bytes = writerByteBuffer.toBytes(); + + ByteBuffer readerByteBuffer = new ByteBuffer(); + readerByteBuffer.writeBytes(bytes); + byte readValue = readerByteBuffer.readByte(); + assertEquals(value, readValue); + } + + public static void bytesTest() { + var value = new byte[]{1, 2, 3}; + ByteBuffer writerByteBuffer = new ByteBuffer(); + writerByteBuffer.writeBytes(value); + byte[] bytes = writerByteBuffer.toBytes(); + + ByteBuffer readerByteBuffer = new ByteBuffer(); + readerByteBuffer.writeBytes(bytes); + var readValue = readerByteBuffer.readBytes(3); + assertEquals(value, readValue); + } + + public static void shortTest() { + short value = 9999; + ByteBuffer writerByteBuffer = new ByteBuffer(); + writerByteBuffer.writeShort(value); + byte[] bytes = writerByteBuffer.toBytes(); + + ByteBuffer readerByteBuffer = new ByteBuffer(); + readerByteBuffer.writeBytes(bytes); + short readValue = readerByteBuffer.readShort(); + assertEquals(value, readValue); + } + + public static void intTest() { + int value = 99999999; + ByteBuffer writerByteBuffer = new ByteBuffer(); + writerByteBuffer.writeInt(value); + byte[] bytes = writerByteBuffer.toBytes(); + + ByteBuffer readerByteBuffer = new ByteBuffer(); + readerByteBuffer.writeBytes(bytes); + int readValue = readerByteBuffer.readInt(); + assertEquals(value, readValue); + } + + public static void longTest() { + long value = 9999999999999999L; + ByteBuffer writerByteBuffer = new ByteBuffer(); + writerByteBuffer.writeLong(value); + byte[] bytes = writerByteBuffer.toBytes(); + + ByteBuffer readerByteBuffer = new ByteBuffer(); + readerByteBuffer.writeBytes(bytes); + long readValue = readerByteBuffer.readLong(); + assertEquals(value, readValue); + } + + public static void floatTest() { + float value = 999999.56F; + ByteBuffer writerByteBuffer = new ByteBuffer(); + writerByteBuffer.writeFloat(value); + byte[] bytes = writerByteBuffer.toBytes(); + + ByteBuffer readerByteBuffer = new ByteBuffer(); + readerByteBuffer.writeBytes(bytes); + float readValue = readerByteBuffer.readFloat(); + assertEquals(value, readValue); + } + + public static void doubleTest() { + double value = 999999.56; + ByteBuffer writerByteBuffer = new ByteBuffer(); + writerByteBuffer.writeDouble(value); + byte[] bytes = writerByteBuffer.toBytes(); + + ByteBuffer readerByteBuffer = new ByteBuffer(); + readerByteBuffer.writeBytes(bytes); + double readValue = readerByteBuffer.readDouble(); + assertEquals(value, readValue); + } + + public static void stringTest() { + String value = "aaa"; + ByteBuffer writerByteBuffer = new ByteBuffer(); + writerByteBuffer.writeString(value); + byte[] bytes = writerByteBuffer.toBytes(); + + ByteBuffer readerByteBuffer = new ByteBuffer(); + readerByteBuffer.writeBytes(bytes); + String readValue = readerByteBuffer.readString(); + assertEquals(value, readValue); + } + + public static void assertEquals(Object a, Object b) { + if (a.equals(b)) { + return; + } + + throw new RuntimeException("a is not equals b"); + } + + public static void assertEquals(byte[] a, byte[] b) { + if (a == b) { + return; + } + if (a != null && b != null && a.length == b.length) { + for (var i = 0; i < a.length; i++) { + assertEquals(a[i], b[i]); + } + return; + } + throw new RuntimeException("a is not equals b"); + } + + // ----------------------------------------------------------------------------------------------------------------- + public static void compatibleTest() throws IOException { + var bytes = toByteArray(new FileInputStream("D:\\Project\\zfoo\\protocol\\src\\test\\resources\\complexObject.bytes")); + var buffer = new ByteBuffer(); + buffer.writeBytes(bytes); + + var packet = ProtocolManager.read(buffer); + + var newBuffer = new ByteBuffer(); + ProtocolManager.write(newBuffer, packet); + + buffer.setReadOffset(0); + newBuffer.setReadOffset(0); + + var equal = 0; + var notEqual = 0; + for (int i = 0; i < buffer.writeOffset(); i++) { + var a = buffer.readByte(); + var b = newBuffer.readByte(); + if (a == b) { + equal++; + } else { + notEqual++; + } + } + System.out.println(format("equal [{}], not equal [{}]", equal, notEqual)); + } + + + public static void normalReadTest() throws IOException { + +// var bytes = toByteArray(new FileInputStream("D:\\Project\\zfoo\\protocol\\src\\test\\resources\\compatible\\normal-inner-compatible.bytes")); +// var bytes = toByteArray(new FileInputStream("D:\\Project\\zfoo\\protocol\\src\\test\\resources\\compatible\\normal-out-compatible.bytes")); +// var bytes = toByteArray(new FileInputStream("D:\\Project\\zfoo\\protocol\\src\\test\\resources\\compatible\\normal-inner-compatible.bytes")); +// var bytes = toByteArray(new FileInputStream("D:\\Project\\zfoo\\protocol\\src\\test\\resources\\compatible\\normal-out-inner-compatible.bytes")); + var bytes = toByteArray(new FileInputStream("D:\\Project\\zfoo\\protocol\\src\\test\\resources\\compatible\\normal-out-inner-inner-compatible.bytes")); + + var buffer = new ByteBuffer(); + buffer.writeBytes(bytes); + var packet = ProtocolManager.read(buffer); + + System.out.println(packet); + } + + + // ----------------------------------------------------------------------------------------------------------------- + public static byte[] toByteArray(final InputStream input) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy(input, output); + var bytes = output.toByteArray(); + return bytes; + } + public static final int EOF = -1; + + // The number of bytes in a byte + public static final int ONE_BYTE = 1; + // The number of bytes in a kilobyte + public static final int BYTES_PER_KB = ONE_BYTE * 1024; + // The number of bytes in a megabyte + public static final int BYTES_PER_MB = BYTES_PER_KB * 1024; + // The number of bytes in a gigabyte + public static final long BYTES_PER_GB = BYTES_PER_MB * 1024; + public static int copy(final InputStream input, final OutputStream output) throws IOException { + byte[] buffer = new byte[BYTES_PER_KB]; + long count = 0; + int n; + while (EOF != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + + if (count > BYTES_PER_GB * 2L) { + return -1; + } + return (int) count; + } + + public static String format(final String template, final Object... args) { + // 初始化定义好的长度以获得更好的性能 + var builder = new StringBuilder(template.length() + 50); + + // 记录已经处理到的位置 + var readIndex = 0; + for (int i = 0; i < args.length; i++) { + // 占位符所在位置 + var placeholderIndex = template.indexOf("{}", readIndex); + // 剩余部分无占位符 + if (placeholderIndex == -1) { + // 不带占位符的模板直接返回 + if (readIndex == 0) { + return template; + } + break; + } + + builder.append(template, readIndex, placeholderIndex); + builder.append(args[i]); + readIndex = placeholderIndex + 2; + } + + // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + builder.append(template, readIndex, template.length()); + return builder.toString(); + } +}