From 6da5684df5c531d8f7c5c3904a3590551a8d7c5e Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Sun, 25 Jun 2023 16:22:51 +0200 Subject: [PATCH 01/14] First working C implementation --- .gitignore | 2 + .../io/kaitai/struct/CClassCompiler.scala | 20 + .../main/scala/io/kaitai/struct/Main.scala | 4 +- .../kaitai/struct/languages/CCompiler.scala | 1283 +++++++++++++++++ .../components/LanguageCompilerStatic.scala | 1 + .../struct/translators/CTranslator.scala | 277 ++++ 6 files changed, 1586 insertions(+), 1 deletion(-) create mode 100644 shared/src/main/scala/io/kaitai/struct/CClassCompiler.scala create mode 100644 shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala create mode 100644 shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala diff --git a/.gitignore b/.gitignore index 73912aea3..8948bacd3 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ project/boot/ # Unpacking complete binaries for testing purposes /runnable js/npm +output/kaitai-struct-compiler-0.10-SNAPSHOT/lib +.bsp diff --git a/shared/src/main/scala/io/kaitai/struct/CClassCompiler.scala b/shared/src/main/scala/io/kaitai/struct/CClassCompiler.scala new file mode 100644 index 000000000..5c409d058 --- /dev/null +++ b/shared/src/main/scala/io/kaitai/struct/CClassCompiler.scala @@ -0,0 +1,20 @@ +package io.kaitai.struct + +import scala.collection.mutable.ListBuffer +import io.kaitai.struct.datatype.DataType.{CalcIntType, KaitaiStreamType, UserTypeInstream} +import io.kaitai.struct.datatype.{BigEndian, CalcEndian, Endianness, FixedEndian, InheritedEndian, LittleEndian} +import io.kaitai.struct.exprlang.Ast +import io.kaitai.struct.exprlang.Ast.expr +import io.kaitai.struct.exprlang.Ast.identifier +import io.kaitai.struct.exprlang.Ast.expr.{BoolOp, BinOp, UnaryOp, IfExp, Compare, Call, Attribute, CastToType, Subscript, Name} +import io.kaitai.struct.format._ +import io.kaitai.struct.languages.CCompiler +import io.kaitai.struct.languages.components.ExtraAttrs + +class CClassCompiler( + classSpecs: ClassSpecs, + override val topClass: ClassSpec, + config: RuntimeConfig +) extends ClassCompiler(classSpecs, topClass, config, CCompiler) { + +} diff --git a/shared/src/main/scala/io/kaitai/struct/Main.scala b/shared/src/main/scala/io/kaitai/struct/Main.scala index 5a1ceefc5..a55a494d1 100644 --- a/shared/src/main/scala/io/kaitai/struct/Main.scala +++ b/shared/src/main/scala/io/kaitai/struct/Main.scala @@ -1,7 +1,7 @@ package io.kaitai.struct import io.kaitai.struct.format.{ClassSpec, ClassSpecs, GenericStructClassSpec, MetaSpec} -import io.kaitai.struct.languages.{GoCompiler, NimCompiler, RustCompiler} +import io.kaitai.struct.languages.{CCompiler, GoCompiler, NimCompiler, RustCompiler} import io.kaitai.struct.languages.components.LanguageCompilerStatic import io.kaitai.struct.precompile._ import io.kaitai.struct.problems.CompilationProblem @@ -92,6 +92,8 @@ object Main { val config = updateConfigFromMeta(conf, spec.meta) val cc = lang match { + case CCompiler => + new CClassCompiler(specs, spec, conf) case GraphvizClassCompiler => new GraphvizClassCompiler(specs, spec) case GoCompiler => diff --git a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala new file mode 100644 index 000000000..294d50017 --- /dev/null +++ b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala @@ -0,0 +1,1283 @@ +package io.kaitai.struct.languages + +import io.kaitai.struct._ +import io.kaitai.struct.datatype.DataType._ +import io.kaitai.struct.datatype._ +import io.kaitai.struct.exprlang.Ast +import io.kaitai.struct.exprlang.Ast.expr +import io.kaitai.struct.format._ +import io.kaitai.struct.languages.components._ +import io.kaitai.struct.translators.{CTranslator, TypeDetector} +import scala.collection.mutable.ListBuffer + +class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) + extends LanguageCompiler(typeProvider, config) + with ObjectOrientedLanguage + with SingleOutputFile + with AllocateIOLocalVar + with EveryReadIsExpression + with UniversalDoc + with FixedContentsUsingArrayByteLiteral + with SwitchIfOps { + import CCompiler._ + + val importListSrc = new CppImportList + val importListHdr = new CppImportList + + val translator = new CTranslator(typeProvider, importListSrc, true) + + var outMethodHead = new StringLanguageOutputWriter(indent) + var outMethodBody = new StringLanguageOutputWriter(indent) + val outSrcHeader = new StringLanguageOutputWriter(indent) + val outHdrHeader = new StringLanguageOutputWriter(indent) + val outSrcDefs = new StringLanguageOutputWriter(indent) + val outSrcMain = new StringLanguageOutputWriter(indent) + val outSrcInstances = new StringLanguageOutputWriter(indent) + val outSrcInstancesFill = new StringLanguageOutputWriter(indent) + val outSrcInstancesFillArray : ListBuffer[StringLanguageOutputWriter] = ListBuffer() + val outSrcInstancesReadArray : ListBuffer[StringLanguageOutputWriter] = ListBuffer() + val outHdr = new StringLanguageOutputWriter(indent) + val outHdrEnums = new StringLanguageOutputWriter(indent) + val outHdrArrays = new StringLanguageOutputWriter(indent) + val outHdrInternalStructs : ListBuffer[StringLanguageOutputWriter] = ListBuffer() + val outHdrDefs = new StringLanguageOutputWriter(indent) + val outHdrFinish = new StringLanguageOutputWriter(indent) + val outSrcInstancesGet = new StringLanguageOutputWriter(indent) + val outSrcInternalStruct = new StringLanguageOutputWriter(indent) + val outHdrStructs : ListBuffer[StringLanguageOutputWriter] = ListBuffer() + var outMethodHasI = false + var outMethodHasInternal = false + var CurrentCheck = "" + + def printdbg(s: String) : Unit = outMethodBody.puts("//" + s) + + override def results(topClass: ClassSpec): Map[String, String] = { + val className = topClass.nameAsStr + Map( + outFileNameSource(className) -> (outSrcHeader.result + importListSrc.result + outSrcDefs.result + outSrcInternalStruct.result + outSrcMain.result + outSrcInstances.result + outSrcInstancesGet.result + outSrcInstancesFill.result), + outFileNameHeader(className) -> (outHdrHeader.result + importListHdr.result + outHdrDefs.result + outHdrEnums.result + outHdr.result + outHdrArrays.result + outHdrFinish.result) + ) + } + + override def outFileName(topClassName: String): String = topClassName.toLowerCase() + def outFileNameSource(className: String): String = outFileName(className) + ".c" + def outFileNameHeader(className: String): String = outFileName(className) + ".h" + + override def indent: String = " " + + // ############################## + // Namings + // ############################## + + def idToStr(id: Identifier): String = CCompiler.idToStr(id) + + def publicMemberName(id: Identifier): String = idToStr(id) + + override def privateMemberName(id: Identifier): String = s"${idToStr(id)}" + + override def localTemporaryName(id: Identifier): String = s"_t_${idToStr(id)}" + + override def paramName(id: Identifier): String = s"p_${idToStr(id)}" + + override def ksErrorName(err: KSError): String = CCompiler.ksErrorName(err) + + // ############################## + // Helper + // ############################## + + def getPtrSuffix(dataType: DataType): String = { + dataType match { + case t: UserType => "*" + case at: ArrayType => "*" + case KaitaiStructType | CalcKaitaiStructType => "*" + case AnyType => "*" + case sw: SwitchType => getPtrSuffix(sw.combinedType) + case _ => "" + } + } + + // ############################## + // External class handling + // ############################## + + override def opaqueClassDeclaration(classSpec: ClassSpec): Unit = { + val name = classSpec.name.head.toLowerCase(); + importListHdr.addLocal(outFileNameHeader(name)) + } + + override def importFile(file: String): Unit = { + val name = file.toLowerCase().split("/").last + importListHdr.addLocal(outFileNameHeader(name)) + outHdrDefs.puts("/* Import */") + outHdrDefs.puts(s"#ifndef HAVE_DECL_$name") + outHdrDefs.puts(s"#define HAVE_DECL_$name") + outHdrDefs.puts(s"typedef struct ksx_${name} ksx_$name;") + outHdrDefs.puts(s"#endif") + outHdrDefs.puts + } + + // ############################## + // Class and constructor handling + // ############################## + + override def fileHeader(topClassName: String): Unit = { + outSrcHeader.puts(s"/* $headerComment */") + outSrcHeader.puts + outSrcHeader.puts("#define KS_DEPEND_ON_INTERNALS") + + outHdrHeader.puts(s"/* $headerComment */") + outHdrHeader.puts + outHdrHeader.puts(s"#ifndef KAITAI_${topClassName.toUpperCase()}_H") + outHdrHeader.puts(s"#define KAITAI_${topClassName.toUpperCase()}_H") + outHdrHeader.puts + + importListSrc.addLocal(outFileNameHeader(topClassName)) + + importListHdr.addKaitai("kaitaistruct.h") + + outHdrDefs.puts + outHdrDefs.puts("/* Forward declarations */") + + outHdrArrays.puts + outHdrArrays.puts("/* Array structures */") + + outHdrEnums.puts + outHdrEnums.puts("/* Enums */") + + outHdr.puts + outHdr.puts("/* Main structures */") + + outSrcDefs.puts + outHdrDefs.puts + outSrcInstances.puts + + outHdrFinish.puts + outHdrFinish.puts("#endif") + } + + override def fileFooter(topClassName: String): Unit = { + outMethodBody.dec + outMethodBody.puts("}") + } + + override def classHeader(name: List[String]): Unit = { + val className = makeName(name) + outHdrStructs.append(new StringLanguageOutputWriter(indent)) + outHdrInternalStructs.append(new StringLanguageOutputWriter(indent)) + outSrcInstancesFillArray.append(new StringLanguageOutputWriter(indent)) + outSrcInstancesReadArray.append(new StringLanguageOutputWriter(indent)) + val outStruct = outHdrStructs.last + val outInternalStruct = outHdrInternalStructs.last + val outInstancesFill = outSrcInstancesFillArray.last + val outInstancesRead = outSrcInstancesReadArray.last + if (outHdrStructs.length == 1) { + outHdrDefs.puts(s"/* Main struct */") + outHdrDefs.puts(s"#ifndef HAVE_DECL_$className") + outHdrDefs.puts(s"#define HAVE_DECL_$className") + outHdrDefs.puts(s"typedef struct ksx_${className} ksx_${className};") + outHdrDefs.puts(s"#endif") + } else { + outHdrDefs.puts(s"typedef struct ksx_${className} ksx_${className};") + } + + outHdrArrays.puts + outHdrArrays.puts(s"struct ksx_array_${className}") + outHdrArrays.puts("{") + outHdrArrays.inc + outHdrArrays.puts("ks_usertype_generic kaitai_base;") + outHdrArrays.puts("int64_t size;") + outHdrArrays.puts(s"ksx_$className** data;") + outHdrArrays.dec + outHdrArrays.puts(s"};") + outHdrDefs.puts(s"typedef struct ksx_array_${className} ksx_array_${className};") + + outStruct.puts + outStruct.puts(s"struct ksx_${className}") + outStruct.puts("{") + outStruct.inc + outStruct.puts("ks_usertype_generic kaitai_base;") + outHdrDefs.puts(s"typedef struct ksx_${className}_internal ksx_${className}_internal;") + + outInternalStruct.puts(s"struct ksx_${className}_internal") + outInternalStruct.puts("{") + outInternalStruct.inc + + outSrcDefs.puts(s"static void ksx_fill_${className}_instances(ksx_${className}* data);") + outInstancesFill.puts(s"static void ksx_fill_${className}_instances(ksx_${className}* data)") + outInstancesFill.puts("{") + outInstancesFill.inc + outInstancesFill.puts(s"ksx_${className}_internal* internal = (ksx_${className}_internal*)HANDLE(data)->internal_read;") + outInstancesFill.puts(s"(void)internal;") + + outSrcDefs.puts(s"static void ksx_read_${className}_instances(ksx_${className}* data);") + outInstancesRead.puts(s"static void ksx_read_${className}_instances(ksx_${className}* data)") + outInstancesRead.puts("{") + outInstancesRead.inc + outInstancesRead.puts("int64_t i;") + outInstancesRead.puts("ks_stream* stream = HANDLE(data)->stream;") + outInstancesRead.puts(s"ksx_${className}_internal* internal = (ksx_${className}_internal*)HANDLE(data)->internal_read;") + outInstancesRead.puts("(void)i;") + outInstancesRead.puts("(void)stream;") + outInstancesRead.puts("(void)internal;") + } + + override def classFooter(name: List[String]): Unit = { + val className = makeName(name) + val outStruct = outHdrStructs.last + currentClassNames.remove(currentClassNames.length -1) + outHdrStructs.remove(outHdrStructs.length -1) + val outInternalStruct = outHdrInternalStructs.last + outHdrInternalStructs.remove(outHdrInternalStructs.length - 1) + val outInstancesFill = outSrcInstancesFillArray.last + val outInstancesRead = outSrcInstancesReadArray.last + outSrcInstancesFillArray.remove(outSrcInstancesFillArray.length - 1) + outSrcInstancesReadArray.remove(outSrcInstancesReadArray.length - 1) + outStruct.dec + outStruct.puts(s"};") + outInternalStruct.dec + outInternalStruct.puts(s"};") + outInternalStruct.puts + outHdr.add(outStruct) + outSrcInternalStruct.add(outInternalStruct) + outInstancesFill.dec + outInstancesFill.puts("}") + outInstancesFill.puts + outInstancesRead.dec + outInstancesRead.puts("}") + outInstancesRead.puts + outSrcInstancesFill.add(outInstancesFill) + outSrcInstancesFill.add(outInstancesRead) + } + + var currentClassNames : ListBuffer[String] = ListBuffer() + var currentRootName = "" + + override def classConstructorHeader(name: List[String], parentType: DataType, rootClassName: List[String], isHybrid: Boolean, params: List[ParamDefSpec]): Unit = { + val className = makeName(name) + CurrentCheck = "DATA" + currentClassNames.append(className) + translator.setCurrentClass(className) + val rootName = makeName(rootClassName) + currentRootName = rootName + val parentName = kaitaiType2NativeType(parentType) + val outStruct = outHdrStructs.last + val paramsArg = Utils.join(params.map(p => s"${kaitaiType2NativeType(p.dataType)}${getPtrSuffix(p.dataType)} ${paramName(p.id)}"), ", ", ", ", "") + val addParams = Utils.join(params.map(p => paramName(p.id)), ", ", ", ", "") + if (outHdrStructs.length == 1) { + outHdr.puts + outHdr.puts(s"ksx_${className}* ksx_read_${className}_from_stream(ks_stream* stream, ks_error* error$paramsArg);") + outSrcMain.puts + outSrcMain.puts(s"ksx_${className}* ksx_read_${className}_from_stream(ks_stream* stream, ks_error* error$paramsArg)") + outSrcMain.puts("{") + outSrcMain.inc + outSrcMain.puts(s"ksx_${className}* data;") + outSrcMain.puts(s"data = ksx_read_$className(stream, 0, 0, stream$addParams);") + outSrcMain.puts(s"if (error) *error = stream->config->error;") + outSrcMain.puts(s"KS_CHECK_DATA();") + outSrcMain.puts(s"ksx_read_${className}_instances(data);") + outSrcMain.puts(s"if (error) *error = stream->config->error;") + outSrcMain.puts(s"KS_CHECK_DATA();") + outSrcMain.puts(s"return data;") + outSrcMain.dec + outSrcMain.puts("}") + } + val endianess = if (isHybrid) ", ks_bool is_le" else "" + outSrcDefs.puts(s"static ksx_${className}* ksx_read_${className}(ks_stream* root_stream, ksx_${rootName}* root_data, void* parent_data, ks_stream* stream$endianess$paramsArg);"); + outSrcMain.puts + outSrcMain.puts(s"static ksx_${className}* ksx_read_${className}(ks_stream* root_stream, ksx_${rootName}* root_data, void* parent_data, ks_stream* stream$endianess$paramsArg)") + outSrcMain.puts("{") + outSrcMain.inc + + outMethodBody.inc + outMethodHead.inc + outSrcMain.puts(s"ksx_${className}* data = ks_alloc(root_stream->config, sizeof(ksx_${className}));") + + if (rootName == className) { + outMethodBody.puts(s"root_data = root_data != 0 ? root_data : data;") + } + params.foreach((p) => + outMethodBody.puts(s"data->${idToStr(p.id)} = ${paramName(p.id)};") + ) + outMethodBody.puts(s"ksx_fill_${className}_instances(data);") + + typeProvider.nowClass.meta.endian match { + case Some(_: CalcEndian) | Some(InheritedEndian) => + val name = privateMemberName(EndianIdentifier) + outStruct.puts(s"ks_bool $name;") + if (isHybrid) { + outSrcMain.puts(s"data->$name = is_le;") + } else { + outSrcMain.puts(s"data->$name = -1;") + } + case _ => + } + } + + override def classConstructorFooter: Unit = {} + + // ############################## + // Sequence reads + // ############################## + + override def runRead(name: List[String]): Unit = {} + + override def runReadCalc(): Unit = { + val errorStr = "\"Unspecified endianess!\"" + outMethodBody.puts + outMethodBody.puts(s"KS_ASSERT_DATA(data->${privateMemberName(EndianIdentifier)} == -1, $errorStr, KS_ERROR_ENDIANESS_UNSPECIFIED);") + outMethodBody.puts(s"if (data->${privateMemberName(EndianIdentifier)} == 1) {") + outMethodBody.inc + outMethodBody.puts(s"KS_CHECK_$CurrentCheck(ksx_read_${currentClassNames.last}_le(root_stream, root_data, stream, data));"); + outMethodBody.dec + outMethodBody.puts("} else {") + outMethodBody.inc + outMethodBody.puts(s"KS_CHECK_$CurrentCheck(ksx_read_${currentClassNames.last}_be(root_stream, root_data, stream, data));"); + outMethodBody.dec + outMethodBody.puts("}") + } + + var inEndianFunc = false + + override def readHeader(endian: Option[FixedEndian], isEmpty: Boolean): Unit = { + val suffix = endian match { + case Some(e) => e.toSuffix + case None => "x" + } + if (suffix == "x") { + return + } + val currentClassName = currentClassNames.last + outSrcDefs.puts(s"static void ksx_read_${currentClassName}_$suffix(ks_stream* root_stream, ksx_$currentRootName* root_data, ks_stream* stream, ksx_$currentClassName* data);"); + outSrcMain.puts(s"static void ksx_read_${currentClassName}_$suffix(ks_stream* root_stream, ksx_$currentRootName* root_data, ks_stream* stream, ksx_$currentClassName* data)"); + outSrcMain.puts("{") + outSrcMain.inc + outMethodBody.inc + outMethodHead.inc + CurrentCheck = "VOID" + inEndianFunc = true + } + + def makeFooter(instance: Boolean) : Unit = { + val className = currentClassNames.last + val outSrc = if (instance) outSrcInstances else outSrcMain + if (outMethodHasI) { + val index = translator.doName(Identifier.INDEX) + outMethodHead.puts(s"int64_t $index;") + outMethodHasI = false + } + if (outMethodHasInternal) { + outMethodHead.puts(s"ksx_${className}_internal* internal;") + } + outSrc.add(outMethodHead) + if (outMethodHead.result != "") { + outSrc.puts + } + if (!instance && !inEndianFunc) { + outSrc.puts(s"KS_CHECK_$CurrentCheck(HANDLE(data) = ks_handle_create(stream, data, KS_TYPE_USERTYPE, sizeof(ksx_${className}), sizeof(ksx_${className}_internal), (ks_usertype_generic*)parent_data));") + outMethodBody.puts("return data;") + } + if (outMethodHasInternal) { + outSrc.puts(s"internal = (ksx_${className}_internal*)HANDLE(data)->internal_read;") + outMethodHasInternal = false + } + inEndianFunc = false + outSrc.add(outMethodBody) + outSrc.dec + outSrc.puts("}") + outMethodHead = new StringLanguageOutputWriter(indent) + outMethodBody = new StringLanguageOutputWriter(indent) + } + + override def readFooter(): Unit = { + makeFooter(false) + } + + // ############################## + // Instance handling + // ############################## + + def handleInstanceReadsCommon(outInstancesRead: StringLanguageOutputWriter, attrName: Identifier, isNullable: Boolean, isArray: Boolean, isCallback: Boolean, expr: String): Unit = { + val name = idToStr(attrName) + if (isNullable) { + outInstancesRead.puts(s"if (data->${nullFlagForName(attrName)})") + outInstancesRead.puts("{") + outInstancesRead.inc + } + if (isArray) { + outInstancesRead.puts(s"for (i = 0; i < data->$name->size; i++)") + outInstancesRead.puts("{") + outInstancesRead.inc + } + if (isCallback) { + val arr = if (isArray) "[i]" else "" + outInstancesRead.puts(s"if (internal->_read_instances_$name$arr)") + outInstancesRead.puts("{") + outInstancesRead.inc + } + outInstancesRead.puts(expr) + if (isCallback) { + outInstancesRead.dec + outInstancesRead.puts("}") + } + if (isArray) { + outInstancesRead.dec + outInstancesRead.puts("}") + } + if (isNullable) { + outInstancesRead.dec + outInstancesRead.puts("}") + } + } + + def handleInstanceReadsArray(outInstancesRead: StringLanguageOutputWriter, attrType: DataType, attrName: Identifier, isNullable: Boolean): Unit = { + val name = idToStr(attrName) + attrType match { + case t: UserType => + if (t.isOpaque) return + val typename = makeName(t.classSpec.get.name) + handleInstanceReadsCommon(outInstancesRead, attrName, isNullable, true, false, s"KS_CHECK_$CurrentCheck(ksx_read_${typename}_instances(data->$name->data[i]));") + case AnyType => + handleInstanceReadsCommon(outInstancesRead, attrName, isNullable, true, true, s"KS_CHECK_$CurrentCheck(internal->_read_instances_$name[i](((void**)data->$name->data)[i]));") + val outInternalStruct = outHdrInternalStructs.last + outInternalStruct.puts(s"ks_callback* _read_instances_$name;") + case st: SwitchType => handleInstanceReadsArray(outInstancesRead, st.combinedType, attrName, isNullable) + case _ => + } + } + + def handleInstanceReads(outInstancesRead: StringLanguageOutputWriter, attrType: DataType, attrName: Identifier, isNullable: Boolean): Unit = { + CurrentCheck = "VOID" + val name = idToStr(attrName) + attrType match { + case t: UserType => + if (t.isOpaque) return + val typename = makeName(t.classSpec.get.name) + handleInstanceReadsCommon(outInstancesRead, attrName, isNullable, false, false, s"KS_CHECK_$CurrentCheck(ksx_read_${typename}_instances(data->$name));") + case at: ArrayType => + handleInstanceReadsArray(outInstancesRead, at.elType, attrName, isNullable) + case KaitaiStructType | AnyType => + handleInstanceReadsCommon(outInstancesRead, attrName, isNullable, false, true, s"KS_CHECK_$CurrentCheck(internal->_read_instances_$name(data->$name));") + val outInternalStruct = outHdrInternalStructs.last + outInternalStruct.puts(s"ks_callback _read_instances_$name;") + case st: SwitchType => handleInstanceReads(outInstancesRead, st.combinedType, attrName, isNullable) + case _ => + } + } + + def flagForInstName(ksName: Identifier) = s"_flag_${idToStr(ksName)}" + + override def instanceHeader(classNameList: List[String], instName: InstanceIdentifier, dataType: DataType, isNullable: Boolean): Unit = { + val className = makeName(classNameList) + val name = privateMemberName(instName) + translator.setCurrentClass(className) + + outMethodHead.puts(s"static void ksx_read_${className}_instance_${name}(ks_stream* root_stream, ksx_${currentRootName}* root_data, ks_stream* stream, ksx_${className}* data)") + outMethodHead.puts("{") + outMethodHead.inc + outMethodBody.inc + outSrcInstances.puts + outSrcInstances.inc + } + + override def instanceFooter: Unit = makeFooter(true) + + override def instanceCheckCacheAndReturn(instName: InstanceIdentifier, dataType: DataType): Unit = { + outMethodHasInternal = true + outMethodHead.puts("int64_t _old_pos = ks_stream_get_pos(stream);") + outMethodBody.puts(s"if (internal->${flagForInstName(instName)})") + outMethodBody.inc + outMethodBody.puts("return;") + outMethodBody.dec + } + + override def instanceDeclaration(attrName: InstanceIdentifier, attrType: DataType, isNullable: Boolean): Unit = { + val outInternalStruct = outHdrInternalStructs.last + outInternalStruct.puts(s"ks_bool ${flagForInstName(attrName)};") + CurrentCheck = "VOID" + attributeDeclarationCommon(attrName, attrType, isNullable, true) + } + + override def instanceSetCalculated(instName: InstanceIdentifier): Unit = { + outMethodHasInternal = true + outMethodBody.puts(s"internal->${flagForInstName(instName)} = 1;") + } + + override def instanceReturn(instName: InstanceIdentifier, attrType: DataType): Unit = { + outMethodBody.puts("ks_stream_seek(stream, _old_pos);") + } + + override def instanceCalculate(instName: Identifier, dataType: DataType, value: expr): Unit = + handleAssignmentSimple(instName, s"${expression(value)}") + + override def instanceClear(instName: InstanceIdentifier): Unit = {} + + // ############################## + // Attributes + // ############################## + + def attributeDeclarationCommon(attrName: Identifier, attrType: DataType, isNullable: Boolean, isInstance: Boolean): Unit = { + val outStruct = outHdrStructs.last + val outInternalStruct = outHdrInternalStructs.last + val outInstancesFill = outSrcInstancesFillArray.last + val outInstancesRead = outSrcInstancesReadArray.last + val name = idToStr(attrName) + if (name == "_parent" || name == "_root") + { + return + } + val suffix = getPtrSuffix(attrType) + val isSubtypeByte = attrName match { + case RawIdentifier(_) => true + case _ => false + } + if (isSubtypeByte) { + return + } + if (isNullable) { + outStruct.puts(s"ks_bool ${nullFlagForName(attrName)};") + } + val currentClassName = currentClassNames.last + val typeStr = kaitaiType2NativeType(attrType) + getPtrSuffix(attrType) + outStruct.puts(s"$typeStr ${privateMemberName(attrName)};") + + outSrcInstancesGet.puts(s"static ${typeStr} ksx_get_${currentClassName}_${name}(ksx_${currentClassName}* data)") + outSrcInstancesGet.puts("{") + outSrcInstancesGet.inc + if (isInstance) { + outSrcInstancesGet.puts(s"ks_stream* stream = HANDLE(data)->stream;") + outSrcInstancesGet.puts(s"KS_CHECK(ksx_read_${currentClassName}_instance_${name}(ks_stream_get_root(stream), (void*)ks_usertype_get_root((void*)data), stream, data), data->$name);") + } + attrType match { + case t: UserType => + val cast = kaitaiType2NativeType(t) + outSrcInstancesGet.puts(s"return (${cast}*)data->${name};") + case _ => + outSrcInstancesGet.puts(s"return data->${name};") + } + outSrcInstancesGet.dec + outSrcInstancesGet.puts("}") + outSrcInstancesGet.puts + + var getFunc = s"_get_${idToStr(attrName)}" + outInternalStruct.puts(s"${typeStr} (*$getFunc)(ksx_${currentClassName}* data);") + outInstancesFill.puts(s"internal->$getFunc = ksx_get_${currentClassName}_${name};") + if (isInstance) { + outInstancesRead.puts(s"internal->$getFunc(data);") + } + handleInstanceReads(outInstancesRead, attrType, attrName, isNullable) + } + + override def attributeDeclaration(attrName: Identifier, attrType: DataType, isNullable: Boolean): Unit = { + CurrentCheck = "DATA" + attributeDeclarationCommon(attrName, attrType, isNullable, false) + } + + override def attributeReader(attrName: Identifier, attrType: DataType, isNullable: Boolean): Unit = {} + + override def attrParseHybrid(leProc: () => Unit, beProc: () => Unit): Unit = { + outMethodBody.puts(s"if (data->${privateMemberName(EndianIdentifier)} == 1) {") + outMethodBody.inc + leProc() + outMethodBody.dec + outMethodBody.puts("} else {") + outMethodBody.inc + beProc() + outMethodBody.dec + outMethodBody.puts("}") + } + + override def attrFixedContentsParse(attrName: Identifier, contents: String): Unit = + outMethodBody.puts(s"${privateMemberName(attrName)} = $normalIO.EnsureFixedContents($contents);") + + def getRawIdExpr(varName: Identifier, rep: RepeatSpec): String = { + val memberName = privateMemberName(varName) + rep match { + case NoRepeat => memberName + case _ => s"$memberName->data[i]" + } + } + + override def attrProcess(proc: ProcessExpr, varSrc: Identifier, varDest: Identifier, rep: RepeatSpec): Unit = { + val srcExpr = "_raw_" + privateMemberName(varSrc) + + val expr = proc match { + case ProcessXor(xorValue) => + val t = translator.detectType(xorValue) + t match { + case t1: BytesType => + outMethodBody.puts(s"$srcExpr = ks_bytes_process_xor_bytes($srcExpr, ${expression(xorValue)});") + case Int1Type(_) => + outMethodBody.puts(s"$srcExpr = ks_bytes_process_xor_int($srcExpr, ${expression(xorValue)}, 1);") + case _ => + outMethodBody.puts("Unknown xor type: " + t.toString()) + } + case ProcessZlib => + outMethodBody.puts(s"$srcExpr = stream->config->inflate($srcExpr);") + case ProcessRotate(isLeft, rotValue) => + val expr = if (isLeft) { + expression(rotValue) + } else { + s"8 - (${expression(rotValue)})" + } + outMethodBody.puts(s"$srcExpr = ks_bytes_process_rotate_left($srcExpr, $expr);") + case ProcessCustom(typename, args) => + val name2 = idToStr(varSrc) + val procClass = typename.last + importListHdr.addLocal(outFileNameHeader(typename.last)) + outMethodHead.puts(s"ks_custom_decoder _decoder_$name2;") + outMethodBody.puts(s"_decoder_$name2 = ${procClass}_create(${args.map(expression).mkString(", ")});") + outMethodBody.puts(s"$srcExpr = _decoder_$name2.decode(_decoder_$name2.userdata, $srcExpr);") + outMethodBody.puts(s"${procClass}_destroy(_decoder_$name2);") + } + varDest match { + case RawIdentifier(_) => + case _ => + val target = getRawIdExpr(varDest, rep) + outMethodBody.puts(s"data->$target = $srcExpr;") + } + } + + // ############################## + // Validate + // ############################## + + override def attrValidateExpr( + attrId: Identifier, + attrType: DataType, + checkExpr: Ast.expr, + err: KSError, + errArgs: List[Ast.expr] + ): Unit = { + val typeStr = kaitaiType2NativeType(attrType) + val ptr = getPtrSuffix(attrType) + val name = privateMemberName(attrId) + val errArgsStr = errArgs.map(translator.translate).mkString(", ") + outMethodBody.puts(s"if (!(${translator.translate(checkExpr)}))") + outMethodBody.puts("{") + outMethodBody.inc + val errorStr = "\"Validation error\"" + outMethodBody.puts(s"KS_ASSERT_$CurrentCheck(1, $errorStr, KS_ERROR_VALIDATION_FAILED);") + outMethodBody.dec + outMethodBody.puts("}") + } + + // ############################## + // Enum + // ############################## + + override def enumDeclaration(curClass: List[String], enumName: String, enumColl: Seq[(Long, EnumValueSpec)]): Unit = { + val enumPath : ListBuffer[String] = ListBuffer() + enumPath.appendAll(curClass) + enumPath.append(enumName) + val enumClass = makeName(enumPath) + val enumClass2 = "ksx_" + enumClass + + outHdrEnums.puts + outHdrEnums.puts(s"typedef enum ${enumClass2}_") + outHdrEnums.puts(s"{") + outHdrEnums.inc + + enumColl.foreach { case (id, label) => + val value = translator.doIntLiteral(id) + outHdrEnums.puts(s"${enumClass2.toUpperCase()}_${label.name.toUpperCase()} = $value,") + } + + outHdrEnums.dec + outHdrEnums.puts(s"} $enumClass2;") + + outHdrArrays.puts + outHdrArrays.puts(s"struct ksx_array_${enumClass}") + outHdrArrays.puts("{") + outHdrArrays.inc + outHdrArrays.puts("ks_usertype_generic kaitai_base;") + outHdrArrays.puts("int64_t size;") + outHdrArrays.puts(s"ksx_$enumClass* data;") + outHdrArrays.dec + outHdrArrays.puts(s"};") + outHdrDefs.puts(s"typedef struct ksx_array_${enumClass} ksx_array_$enumClass;") + } + + // ############################## + // IO + // ############################## + + override def allocateIO(varName: Identifier, rep: RepeatSpec): String = { + val privateVarName = privateMemberName(varName) + + val ioName = s"_io_$privateVarName" + + val args = privateMemberName(varName) + + outMethodHead.puts(s"ks_stream* $ioName;") + outMethodBody.puts(s"/* Subtype with substream */") + outMethodBody.puts(s"$ioName = ks_stream_create_from_bytes(_raw_$args);") + ioName + } + + override def useIO(ioEx: expr): String = { + outMethodHead.puts(s"$kstreamName* io;") + outMethodBody.puts(s"io = ${expression(ioEx)};") + "io" + } + + override def pushPos(io: String): Unit = { + // outMethodBody.puts(s"long _pos = $io.Pos;") + } + + override def seek(io: String, pos: Ast.expr): Unit = + outMethodBody.puts(s"KS_CHECK_$CurrentCheck(ks_stream_seek(${makeIO(io)}, ${expression(pos)}));") + + override def popPos(io: String): Unit = { + // outMethodBody.puts(s"$io.Seek(_pos);") + } + + override def alignToByte(io: String): Unit = { + val io_new = makeIO(io) + outMethodBody.puts(s"KS_CHECK_$CurrentCheck(ks_stream_align_to_byte($io_new));") + } + + // ############################## + // Conditional read + // ############################## + + override def condIfHeader(expr: expr): Unit = { + outMethodBody.puts(s"if (${expression(expr)}) {") + outMethodBody.inc + } + + def nullFlagForName(ksName: Identifier) = + s"_is_valid_${idToStr(ksName)}" + + override def condIfSetNull(instName: Identifier): Unit = + outMethodBody.puts(s"data->${nullFlagForName(instName)} = 0;") + + override def condIfSetNonNull(instName: Identifier): Unit = + outMethodBody.puts(s"data->${nullFlagForName(instName)} = 1;") + + override def condIfFooter(expr: expr): Unit = { + outMethodBody.dec + outMethodBody.puts("}") + } + + // ############################## + // Assignment helper + // ############################## + + override def condRepeatCommonInit(id: Identifier, dataType: DataType, needRaw: NeedRaw): Unit = { + } + + def isTypeGenericInternal(idType : DataType): Boolean = { + idType match { + case at: ArrayType => isTypeGenericInternal(at.elType) + case t: SwitchType => + t.combinedType match { + case KaitaiStructType | AnyType => true + case _ => false + } + case _ => false + } + } + + def isTypeGeneric(id: Identifier): Boolean = { + if (idToStr(id).startsWith("_")) { + return false + } + val idType = typeProvider.determineType(id) + isTypeGenericInternal(idType) + } + + override def handleAssignmentSimple(id: Identifier, expr: String): Unit = + handleAssignmentCommon(id, expr, false) + + override def handleAssignmentTempVar(dataType: DataType, id: String, expr: String): Unit = + outMethodBody.puts(s"${kaitaiType2NativeType(dataType)} $id = $expr;") + + // ############################## + // Repeat Eos + // ############################## + + override def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit = { + val name = privateMemberName(id) + val pos = translator.doName(Identifier.INDEX) + val dataTypeArray = ArrayTypeInStream(dataType) + val arrayTypeSize = getKaitaiTypeEnumAndSize(dataType) + val io_new = makeIO(io) + outMethodHasI = true + outMethodBody.puts("/* Array (repeat-eos) */") + outMethodBody.puts(s"data->$name = ks_alloc(root_stream->config, sizeof(${kaitaiType2NativeType(dataTypeArray)}));") + outMethodBody.puts(s"data->$name->size = 0;") + outMethodBody.puts(s"data->$name->data = 0;") + outMethodBody.puts(s"KS_CHECK_$CurrentCheck(HANDLE(data->$name) = ks_handle_create(stream, data->$name, $arrayTypeSize, 0, 0));"); + outMethodBody.puts("{") + outMethodBody.inc + outMethodBody.puts(s"while (!ks_stream_is_eof($io_new)) {") + outMethodBody.puts(s"$pos = data->$name->size;"); + outMethodBody.inc + } + + override def handleAssignmentRepeatEos(id: Identifier, expr: String): Unit = { + val name = privateMemberName(id) + val ptr = getPtrSuffix(dataTypeLast) + val sizeof = s"sizeof(${kaitaiType2NativeType(dataTypeLast)}$ptr)" + outMethodBody.puts(s"data->$name->size++;") + outMethodBody.puts(s"data->$name->data = ks_realloc(root_stream->config, data->$name->data, $sizeof * data->$name->size);") + outMethodBody.puts(s"memset(data->$name->data + data->$name->size - 1, 0, $sizeof);") + if (isTypeGeneric(id)) { + outMethodHasInternal = true + outMethodBody.puts(s"internal->_read_instances_$name = ks_realloc(root_stream->config, internal->_read_instances_$name, sizeof(ks_callback) * data->$name->size);") + outMethodBody.puts(s"internal->_read_instances_$name[data->$name->size -1] = 0;") + } + handleAssignmentCommon(id, expr, true) + } + + override def condRepeatEosFooter: Unit = { + outMethodBody.dec + outMethodBody.puts("}") + outMethodBody.dec + outMethodBody.puts("}") + } + + // ############################## + // Repeat Expr + // ############################## + + override def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: expr): Unit = { + val pos = translator.doName(Identifier.INDEX) + val len = expression(repeatExpr) + val name = privateMemberName(id) + val dataTypeArray = ArrayTypeInStream(dataType) + val arrayTypeSize = getKaitaiTypeEnumAndSize(dataType) + val ptr = getPtrSuffix(dataType) + outMethodHasI = true + outMethodBody.puts("/* Array (repeat-expr) */") + outMethodBody.puts(s"data->$name = ks_alloc(root_stream->config, sizeof(${kaitaiType2NativeType(dataTypeArray)}));") + outMethodBody.puts(s"data->$name->size = $len;") + outMethodBody.puts(s"data->$name->data = ks_alloc(root_stream->config, sizeof(${kaitaiType2NativeType(dataType)}$ptr) * data->$name->size);") + if (isTypeGeneric(id)) { + outMethodHasInternal = true + outMethodBody.puts(s"internal->_read_instances_$name = ks_alloc(root_stream->config, sizeof(ks_callback) * data->$name->size);") + } + outMethodBody.puts(s"KS_CHECK_$CurrentCheck(HANDLE(data->$name) = ks_handle_create(stream, data->$name, $arrayTypeSize, 0, 0));"); + outMethodBody.puts(s"for ($pos = 0; $pos < data->$name->size; $pos++)") + outMethodBody.puts("{") + outMethodBody.inc + } + + override def handleAssignmentRepeatExpr(id: Identifier, expr: String): Unit = + handleAssignmentCommon(id, expr, true) + + override def condRepeatExprFooter: Unit = { + fileFooter(null) + } + + // ############################## + // Repeat Until + // ############################## + + override def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { + val pos = translator.doName(Identifier.INDEX) + val name = privateMemberName(id) + val dataTypeArray = ArrayTypeInStream(dataType) + val arrayTypeSize = getKaitaiTypeEnumAndSize(dataType) + val ptr = getPtrSuffix(dataType) + outMethodHasI = true + outMethodBody.puts("/* Array (repeat-until) */") + outMethodBody.puts(s"data->$name = ks_alloc(root_stream->config, sizeof(${kaitaiType2NativeType(dataTypeArray)}));") + outMethodBody.puts(s"data->$name->size = 0;") + outMethodBody.puts(s"data->$name->data = 0;") + outMethodBody.puts(s"KS_CHECK_$CurrentCheck(HANDLE(data->$name) = ks_handle_create(stream, data->$name, $arrayTypeSize, 0, 0));"); + outMethodBody.puts("{") + outMethodBody.inc + outMethodBody.puts(s"${kaitaiType2NativeType(dataType)}$ptr ${translator.doName("_")} = {0};") + outMethodBody.puts("i = 0;") + outMethodBody.puts(s"(void)${translator.doName("_")};") + outMethodBody.puts(s"do") + outMethodBody.puts("{") + outMethodBody.inc + } + + override def handleAssignmentRepeatUntil(id: Identifier, expr: String, isRaw: Boolean): Unit = { + if (isRaw) { + handleAssignmentCommon(id, expr, true) + return + } + val name = privateMemberName(id) + val nameTemp = translator.doName(Identifier.ITERATOR) + val ptr = getPtrSuffix(dataTypeLast) + val sizeof = s"sizeof(${kaitaiType2NativeType(dataTypeLast)}$ptr)" + outMethodBody.puts(s"data->$name->size++;") + outMethodBody.puts(s"data->$name->data = ks_realloc(root_stream->config, data->$name->data, $sizeof * data->$name->size);") + outMethodBody.puts(s"memset(data->$name->data + data->$name->size - 1, 0, $sizeof);") + if (isTypeGeneric(id)) { + outMethodHasInternal = true + outMethodBody.puts(s"internal->_read_instances_$name = ks_realloc(root_stream->config, internal->_read_instances_$name, sizeof(ks_callback) * data->$name->size);") + outMethodBody.puts(s"internal->_read_instances_$name[data->$name->size -1] = 0;") + } + handleAssignmentCommon(id, expr, true) + val pos = translator.doName(Identifier.INDEX) + outMethodBody.puts(s"$nameTemp = data->$name->data[$pos];"); + } + + override def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { + val pos = translator.doName(Identifier.INDEX) + typeProvider._currentIteratorType = Some(dataType) + outMethodBody.puts(s"$pos++;") + outMethodBody.dec + outMethodBody.puts(s"} while (!(${expression(untilExpr)}));") + outMethodBody.dec + outMethodBody.puts("}") + } + + // ############################## + // Expression handling + // ############################## + + override def blockScopeHeader: Unit = { + outMethodBody.puts("{") + outMethodBody.inc + } + override def blockScopeFooter: Unit = { + outMethodBody.dec + outMethodBody.puts("}") + } + + var dataTypeLast: DataType = null; + + def handleAssignmentCommon(id: Identifier, expr: String, isArray: Boolean): Unit = { + val name = privateMemberName(id) + var nameTarget = name + if (isArray) { + val pos = translator.doName(Identifier.INDEX) + nameTarget = s"$name->data[$pos]" + } + + dataTypeLast match { + case t: UserType => + if (isTypeGeneric(id)) { + val typeName = makeName(t.classSpec.get.name) + val arr = if (isArray) "[i]" else "" + outMethodBody.puts(s"KS_CHECK_$CurrentCheck(data->$nameTarget = (ks_usertype_generic*)$expr);") + outMethodHasInternal = true + outMethodBody.puts(s"internal->_read_instances_$name$arr = (void*)ksx_read_${typeName}_instances;") + return + } + case _ => + } + + id match { + case RawIdentifier(_) => + outMethodHead.puts(s"ks_bytes* _raw_$name;") + outMethodBody.puts(s"KS_CHECK_$CurrentCheck(_raw_$name = $expr);") + case _ => + outMethodBody.puts(s"KS_CHECK_$CurrentCheck(data->$nameTarget = $expr);") + } + } + + override def parseExpr(dataType: DataType, assignType: DataType, io: String, defEndian: Option[FixedEndian]): String = { + dataTypeLast = dataType; + var io_new = makeIO(io) + + dataType match { + case t: ReadableType => + s"ks_stream_read_${t.apiCall(defEndian)}($io_new)" + case blt: BytesLimitType => + s"ks_stream_read_bytes($io_new, ${expression(blt.size)})" + case _: BytesEosType => + s"ks_stream_read_bytes_full($io_new)" + case BytesTerminatedType(terminator, include, consume, eosError, _) => + val include2 = if (include) 1 else 0 + val consume2 = if (consume) 1 else 0 + val eosError2 = if (eosError) 1 else 0 + s"ks_stream_read_bytes_term($io_new, $terminator, $include2, $consume2, $eosError2)" + case BitsType1(bitEndian) => + s"ks_stream_read_bits_${bitEndian.toSuffix.toLowerCase()}($io_new, 1)" + case BitsType(width: Int, bitEndian) => + s"ks_stream_read_bits_${bitEndian.toSuffix.toLowerCase()}($io_new, $width)" + case t: UserType => + val parent = t.forcedParent match { + case Some(USER_TYPE_NO_PARENT) => "0" + case Some(fp) => translator.translate(fp) + case None => "data" + } + val typeName = makeName(t.classSpec.get.name) + val addParams = Utils.join(t.args.map((a) => + translator.detectType(a) match { + case t: UserType => "(void*)" + translator.translate(a) /* Possibly need cast to generic struct */ + case _ => translator.translate(a) + } + ), ", ", ", ", "") + val addEndian = t.classSpec.get.meta.endian match { + case Some(InheritedEndian) => s", data->${privateMemberName(EndianIdentifier)}" + case _ => "" + } + if (t.isOpaque && t.classSpec.get != typeProvider.topClass) { // Our own top class is *not* opaque! + s"ksx_read_${typeName}_from_stream($io_new, 0$addEndian$addParams)" + } else { + s"ksx_read_$typeName(root_stream, root_data, $parent, $io_new$addEndian$addParams)" + } + case _ => + outMethodBody.puts("Missing expression type: " + dataType.toString()) + "" + } + } + + override def bytesPadTermExpr(expr0: String, padRight: Option[Int], terminator: Option[Int], include: Boolean) = { + val expr1 = padRight match { + case Some(padByte) => s"ks_bytes_strip_right($expr0, $padByte)" + case None => expr0 + } + val expr2 = terminator match { + case Some(term) => s"ks_bytes_terminate($expr1, $term, ${if (include) 1 else 0})" + case None => expr1 + } + expr2 + } + + // ############################## + // Switch (normal) + // ############################## + + override def switchRequiresIfs(onType: DataType): Boolean = onType match { + case _: IntType | _: EnumType => false + case _ => true + } + + var outMethodHeadOldSwitch: StringLanguageOutputWriter = null + var outMethodBodyOldSwitch: StringLanguageOutputWriter = null + + def switchOverrideStart() : Unit = { + outMethodHeadOldSwitch = outMethodHead + outMethodBodyOldSwitch = outMethodBody + outMethodHead = new StringLanguageOutputWriter(indent) + outMethodBody = new StringLanguageOutputWriter(indent) + outMethodHead.indentLevel = outMethodBodyOldSwitch.indentLevel + outMethodBody.indentLevel = outMethodBodyOldSwitch.indentLevel + } + + def switchOverrideEnd() : Unit = { + outMethodBodyOldSwitch.add(outMethodHead) + if (outMethodHead.result != "") { + outMethodBodyOldSwitch.puts + } + outMethodBodyOldSwitch.add(outMethodBody) + + outMethodHead = outMethodHeadOldSwitch + outMethodBody = outMethodBodyOldSwitch + outMethodHeadOldSwitch = null; + outMethodBodyOldSwitch = null + } + + override def switchStart(id: Identifier, on: Ast.expr): Unit = { + outMethodBody.puts(s"switch (${expression(on)}) {") + } + + override def switchCaseFirstStart(condition: Ast.expr): Unit = switchCaseStart(condition) + + override def switchCaseStart(condition: Ast.expr): Unit = { + outMethodBody.puts(s"case ${expression(condition)}: {") + outMethodBody.inc + switchOverrideStart() + } + + override def switchCaseEnd(): Unit = { + switchOverrideEnd() + outMethodBody.puts("break;") + outMethodBody.dec + outMethodBody.puts("}") + } + + override def switchElseStart(): Unit = { + outMethodBody.puts("default: {") + outMethodBody.inc + switchOverrideStart() + } + + override def switchEnd(): Unit = { + outMethodBody.puts("}") + } + + // ############################## + // Switch (if) + // ############################## + + val NAME_SWITCH_ON = Ast.expr.Name(Ast.identifier(Identifier.SWITCH_ON)) + + override def switchIfStart(id: Identifier, on: Ast.expr, onType: DataType): Unit = { + outMethodBody.puts("{") + outMethodBody.inc + outMethodBody.puts(s"${kaitaiType2NativeType(onType)} ${expression(NAME_SWITCH_ON)} = ${expression(on)};") + } + + def switchCmpExpr(condition: Ast.expr): String = + expression( + Ast.expr.Compare( + NAME_SWITCH_ON, + Ast.cmpop.Eq, + condition + ) + ) + + override def switchIfCaseFirstStart(condition: Ast.expr): Unit = { + outMethodBody.puts(s"if (${switchCmpExpr(condition)})") + outMethodBody.puts("{") + outMethodBody.inc + switchOverrideStart() + } + + override def switchIfCaseStart(condition: Ast.expr): Unit = { + outMethodBody.puts(s"else if (${switchCmpExpr(condition)})") + outMethodBody.puts("{") + outMethodBody.inc + switchOverrideStart() + } + + override def switchIfCaseEnd(): Unit = { + switchOverrideEnd() + outMethodBody.dec + outMethodBody.puts("}") + } + + override def switchIfElseStart(): Unit = { + outMethodBody.puts("else") + outMethodBody.puts("{") + outMethodBody.inc + switchOverrideStart() + } + + override def switchIfEnd(): Unit = { + outMethodBody.dec + outMethodBody.puts("}") + } + + // ############################## + // Other + // ############################## + + override def userTypeDebugRead(id: String, dataType: DataType, assignType: DataType): Unit = {} + + override def universalDoc(doc: DocSpec): Unit = {} + + override def type2class(className: String): String = + className + +} + +object CCompiler extends LanguageCompilerStatic + with StreamStructNames + with UpperCamelCaseClasses + with ExceptionNames { + override def getCompiler( + tp: ClassTypeProvider, + config: RuntimeConfig + ): LanguageCompiler = new CCompiler(tp, config) + + def idToStr(id: Identifier): String = { + id match { + case SpecialIdentifier(name) => name + case NamedIdentifier(name) => name.toLowerCase() + case NumberedIdentifier(idx) => s"_${NumberedIdentifier.TEMPLATE}$idx" + case InstanceIdentifier(name) => name.toLowerCase() + case RawIdentifier(innerId) => idToStr(innerId) + } + } + + def kaitaiType2NativeTypeArray(attrType: DataType): String = { + attrType match { + case t: UserType => s"ksx_array_${makeName(t.classSpec.get.name)}" + case t: EnumType => s"ksx_array_${makeName(t.enumSpec.get.name)}" + case _: StrType => s"ks_array_string" + case _: BytesType => s"ks_array_bytes" + case KaitaiStructType | CalcKaitaiStructType => s"ks_array_usertype_generic" + case AnyType => "ks_array_any" + case st: SwitchType => kaitaiType2NativeTypeArray(st.combinedType) + case _ => s"ks_array_${kaitaiType2NativeType(attrType)}" + } + } + + def kaitaiType2NativeType(attrType: DataType): String = { + attrType match { + case Int1Type(false) => "uint8_t" + case IntMultiType(false, Width2, _) => "uint16_t" + case IntMultiType(false, Width4, _) => "uint32_t" + case IntMultiType(false, Width8, _) => "uint64_t" + + case Int1Type(true) => "int8_t" + case IntMultiType(true, Width2, _) => "int16_t" + case IntMultiType(true, Width4, _) => "int32_t" + case IntMultiType(true, Width8, _) => "int64_t" + + case FloatMultiType(Width4, _) => "float" + case FloatMultiType(Width8, _) => "double" + + case BitsType(_, _) => "uint64_t" + + case CalcIntType => "int64_t" + case CalcFloatType => "double" + case _: BooleanType => "ks_bool" + + case _: StrType => "ks_string*" + case _: BytesType => "ks_bytes*" + + case AnyType => "void" + case KaitaiStructType | CalcKaitaiStructType => kstructName + case KaitaiStreamType | OwnedKaitaiStreamType => kstreamName + + case t: UserType => "ksx_" + makeName(t.classSpec.get.name) + case t: EnumType => "ksx_" + makeName(t.enumSpec.get.name) + + case at: ArrayType => kaitaiType2NativeTypeArray(at.elType) + + case st: SwitchType => kaitaiType2NativeType(st.combinedType) + case _ => "Error Type" + } + } + + def getKaitaiTypeEnumAndSize(attrType: DataType): String = { + attrType match { + case Int1Type(false) => "KS_TYPE_ARRAY_UINT, 1" + case IntMultiType(false, Width2, _) => "KS_TYPE_ARRAY_UINT, 2" + case IntMultiType(false, Width4, _) => "KS_TYPE_ARRAY_UINT, 4" + case IntMultiType(false, Width8, _) => "KS_TYPE_ARRAY_UINT, 8" + + case Int1Type(true) => "KS_TYPE_ARRAY_INT, 1" + case IntMultiType(true, Width2, _) => "KS_TYPE_ARRAY_INT, 2" + case IntMultiType(true, Width4, _) => "KS_TYPE_ARRAY_INT, 4" + case IntMultiType(true, Width8, _) => "KS_TYPE_ARRAY_INT, 8" + + case FloatMultiType(Width4, _) => "KS_TYPE_ARRAY_FLOAT, 4" + case FloatMultiType(Width8, _) => "KS_TYPE_ARRAY_FLOAT, 8" + + case _: StrType => "KS_TYPE_ARRAY_STRING, sizeof(ks_string*)" + + case BitsType(_, _) => "KS_TYPE_ARRAY_UINT, 8" + case _: BytesType => s"KS_TYPE_ARRAY_BYTES, sizeof(ks_bytes*)" + + case t: UserType => s"KS_TYPE_ARRAY_USERTYPE, sizeof(ksx_${makeName(t.classSpec.get.name)}*)" + case t: EnumType => getKaitaiTypeEnumAndSize(t.basedOn) + + case _ => "KS_TYPE_UNKNOWN, 0" + } + } + + def types2class(typeName: Ast.typeId): String = + types2class(typeName.names) + + def types2class(names: Iterable[String]) = names.map(type2class).mkString(".").toLowerCase() + + def makeName(names: Iterable[String]) = { + val arr = names.toList + if (arr.length == 1) + arr.mkString("_").toLowerCase() + else + arr.drop(1).mkString("_").toLowerCase() + } + + def makeIO(io: String) = if (io == "_io") "stream" else io + + override def kstructName = "ks_usertype_generic" + override def kstreamName = "ks_stream" + override def ksErrorName(err: KSError): String = err match { + case EndOfStreamError => "EndOfStreamException" + case _ => err.name + } +} diff --git a/shared/src/main/scala/io/kaitai/struct/languages/components/LanguageCompilerStatic.scala b/shared/src/main/scala/io/kaitai/struct/languages/components/LanguageCompilerStatic.scala index 99f42503e..e609e97fc 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/components/LanguageCompilerStatic.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/components/LanguageCompilerStatic.scala @@ -9,6 +9,7 @@ trait LanguageCompilerStatic { object LanguageCompilerStatic { val NAME_TO_CLASS: Map[String, LanguageCompilerStatic] = Map( + "c" -> CCompiler, "construct" -> ConstructClassCompiler, "cpp_stl" -> CppCompiler, "csharp" -> CSharpCompiler, diff --git a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala new file mode 100644 index 000000000..37ff2ddf5 --- /dev/null +++ b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala @@ -0,0 +1,277 @@ +package io.kaitai.struct.translators + +import io.kaitai.struct.{ImportList, Utils, ClassTypeProvider} +import io.kaitai.struct.datatype._ +import io.kaitai.struct.datatype.DataType._ +import io.kaitai.struct.exprlang.Ast +import io.kaitai.struct.exprlang.Ast._ +import io.kaitai.struct.format.Identifier +import io.kaitai.struct.format.SpecialIdentifier +import io.kaitai.struct.languages.CCompiler +import io.kaitai.struct.languages.components.CppImportList + +class CTranslator(provider: ClassTypeProvider, importList: CppImportList, isInternal: Boolean) extends BaseTranslator(provider) { + + var currentClassName = "" + + def setCurrentClass(className: String): Unit = + currentClassName = className + + def doArrayLiteralInternal(t: DataType, value: Seq[expr]): String = { + val size = value.size + val args = value.map(translate).mkString(", ") + t match { + case Int1Type(false) => s"ks_array_uint8_t_from_data(stream->config, $size, $args)" + case IntMultiType(false, Width2, _) => s"ks_array_uint16_t_from_data(stream->config, $size, $args)" + case IntMultiType(false, Width4, _) => s"ks_array_uint32_t_from_data(stream->config, $size, $args)" + case IntMultiType(false, Width8, _) => s"ks_array_uint64_t_from_data(stream->config, $size, $args)" + + case Int1Type(true) => s"ks_array_int8_t_from_data(stream->config, $size, $args)" + case IntMultiType(true, Width2, _) => s"ks_array_int16_t_from_data(stream->config, $size, $args)" + case IntMultiType(true, Width4, _) => s"ks_array_int32_t_from_data(stream->config, $size, $args)" + case IntMultiType(true, Width8, _) => s"ks_array_int64_t_from_data(stream->config, $size, $args)" + + case FloatMultiType(Width4, _) => s"ks_array_float_from_data(stream->config, $size, $args)" + case FloatMultiType(Width8, _) => s"ks_array_double_from_data(stream->config, $size, $args)" + + case CalcIntType => s"ks_array_int64_t_from_data(stream->config, $size, $args)" + case CalcFloatType => s"ks_array_double_from_data(stream->config, $size, $args)" + case CalcStrType => s"ks_array_string_from_data(stream->config, $size, $args)" + case KaitaiStructType | CalcKaitaiStructType => s"ks_array_usertype_generic_from_data(stream->config, $size, $args)" + case _ => s"Missing list type: " + t.toString() + } + } + override def doArrayLiteral(t: DataType, value: Seq[expr]): String = { + t match { + case arr: CalcArrayType => doArrayLiteralInternal(arr.elType, value) + case _ => doArrayLiteralInternal(t, value) + } + } + + override def doByteArrayLiteral(arr: Seq[Byte]): String = { + val config = if (isInternal) "stream->config" else "config" + if (arr.size == 0) { + s"ks_bytes_from_data($config, 0)" + } else { + s"ks_bytes_from_data($config, ${arr.size}, ${arr.map(_ & 0xff).mkString(", ")})" + } + } + override def doByteArrayNonLiteral(elts: Seq[Ast.expr]): String = + s"ks_bytes_from_data_terminated(stream->config, ${elts.map(translate).mkString(", ")}, 0xffff)" + + override val asciiCharQuoteMap: Map[Char, String] = Map( + '\t' -> "\\t", + '\n' -> "\\n", + '\r' -> "\\r", + '"' -> "\\\"", + '\\' -> "\\\\", + + '\u0000' -> "\\0", + '\u0007' -> "\\a", + '\f' -> "\\f", + '\u000b' -> "\\v", + '\b' -> "\\b" + ) + + override def strLiteralUnicode(code: Char): String = + code.toString().getBytes("UTF-8").map(c => s"\\x" + Integer.toHexString(c & 0xff)).mkString("") + + override def doIntLiteral(n: BigInt): String = { + if (n == -9223372036854775808L) { + return s"(${n + 1}LL - 1)" + } + val suffix = if (n < -9223372036854775808L) { + "" // too low, no suffix would help anyway + } else if (n <= -2147483649L) { + "LL" // -9223372036854775808..–2147483649 + } else if (n <= 2147483647L) { + "" // -2147483648..2147483647 + } else if (n <= 4294967295L) { + "UL" // 2147483648..4294967295 + } else if (n <= 9223372036854775807L) { + "LL" // 4294967296..9223372036854775807 + } else if (n <= Utils.MAX_UINT64) { + "ULL" // 9223372036854775808..18446744073709551615 + } else { + "" // too high, no suffix would help anyway + } + + s"$n$suffix" + } + + override def strLiteralGenericCC(code: Char): String = strLiteralUnicode(code) + + override def numericBinOp(left: Ast.expr, op: Ast.operator, right: Ast.expr) = { + (detectType(left), detectType(right), op) match { + case (_: IntType, _: IntType, Ast.operator.Mod) => + s"ks_mod(${translate(left)}, ${translate(right)})" + case (_: IntType, _: IntType, Ast.operator.Div) => + s"ks_div(${translate(left)}, ${translate(right)})" + case _ => + super.numericBinOp(left, op, right) + } + } + + override def doName(s: String) = + if (s.startsWith("_")) { + s match { + case Identifier.SWITCH_ON => "on" + case Identifier.INDEX => "i" + case Identifier.IO => "stream" + case Identifier.PARENT => "HANDLE(data)->parent" + case Identifier.ROOT => "root_data" + case Identifier.ITERATOR => "_temp" + case _ => s"data->$s" + } + } else { + if (isInternal) { + s"FIELD(data, ksx_$currentClassName, $s)" + } else { + s"data->$s" + } + } + + override def doInternalName(id: Identifier): String = + doName(CCompiler.idToStr(id)) + + override def doEnumByLabel(enumTypeAbs: List[String], label: String): String = { + val enumClass = enumTypeAbs.drop(1).mkString("_") + s"KSX_${enumClass}_$label".toUpperCase() + } + override def doEnumById(enumTypeAbs: List[String], id: String): String = id + + override def doBytesCompareOp(left: Ast.expr, op: Ast.cmpop, right: Ast.expr): String = + s"(ks_bytes_compare(${translate(left)}, ${translate(right)}) ${cmpOp(op)} 0)" + + override def arraySubscript(container: expr, idx: expr): String = + s"${translate(container)}->data[${translate(idx)}]" + override def doIfExp(condition: expr, ifTrue: expr, ifFalse: expr): String = + s"(${translate(condition)} ? ${translate(ifTrue)} : ${translate(ifFalse)})" + override def doCast(value: Ast.expr, typeName: DataType): String = { + val suffix = typeName match { + case t: UserType => "*" + case _ => "" + } + s"((${CCompiler.kaitaiType2NativeType(typeName)}$suffix) (${translate(value)}))" + } + + // Predefined methods of various types + override def strToInt(s: expr, base: expr): String = { + s"ks_string_to_int(${translate(s)}, ${translate(base)})" + } + override def enumToInt(v: expr, et: EnumType): String = + translate(v) + override def floatToInt(v: expr): String = + s"(long) (${translate(v)})" + override def intToStr(i: expr, base: expr): String = { + s"ks_string_from_int(stream->config, ${translate(i)}, ${translate(base)})" + } + override def bytesToStr(bytesExpr: String, encoding: Ast.expr): String = + s"ks_string_from_bytes($bytesExpr, ${translate(encoding)})" + + override def strLength(s: expr): String = + s"${translate(s)}->len" + + override def strReverse(s: expr): String = + s"ks_string_reverse(${translate(s)})" + + override def strSubstring(s: expr, from: expr, to: expr): String = + s"ks_string_substr(${translate(s)}, ${translate(from)}, ${translate(to)})" + + override def doStrCompareOp(left: Ast.expr, op: Ast.cmpop, right: Ast.expr): String = { + s"(ks_string_compare(${translate(left)}, ${translate(right)}) ${cmpOp(op)} 0)" + } + + override def doStringLiteral(s: String): String = { + s"ks_string_from_cstr(stream->config, ${super.doStringLiteral(s)})" + } + + override def bytesFirst(b: Ast.expr): String = + s"ks_bytes_get_at(${translate(b)}, 0)" + + override def bytesLength(b: Ast.expr): String = + s"${translate(b)}->length" + + override def bytesLast(b: Ast.expr): String = { + val v = translate(b) + s"ks_bytes_get_at($v, $v->length - 1)" + } + + override def bytesSubscript(container: Ast.expr, idx: Ast.expr): String = + s"ks_bytes_get_at(${translate(container)}, ${translate(idx)})" + + override def arrayFirst(a: expr): String = + s"${translate(a)}->data[0]" + + override def arrayLast(a: expr): String = { + val v = translate(a) + s"$v->data[$v->size - 1]" + } + + override def arraySize(a: expr): String = + s"${translate(a)}->size" + + override def arrayMin(a: Ast.expr): String = { + var res = translate(a) + var typeArray = detectType(a) + typeArray match { + case t : ArrayType => + t.elType match { + case _ : IntType => s"ks_array_min_int(&$res->kaitai_base)" + case _ : FloatType => s"ks_array_min_float(&$res->kaitai_base)" + case _ : StrType => s"ks_array_min_string(&$res->kaitai_base)" + case _ : BytesType => s"ks_array_min_bytes(&$res->kaitai_base)" + case _ => "UNKNOWN_Min: " + t.toString() + } + case _ : BytesType => s"ks_bytes_min($res)" + case _ => "UNKNOWN_Min: " + typeArray.toString() + } + } + override def arrayMax(a: Ast.expr): String = { + var res = translate(a) + var typeArray = detectType(a) + typeArray match { + case t : ArrayType => + t.elType match { + case _ : IntType => s"ks_array_max_int(&$res->kaitai_base)" + case _ : FloatType => s"ks_array_max_float(&$res->kaitai_base)" + case _ : StrType => s"ks_array_max_string(&$res->kaitai_base)" + case _ : BytesType => s"ks_array_max_bytes(&$res->kaitai_base)" + case _ => "UNKNOWN_Max: " + t.toString() + } + case _ : BytesType => s"ks_bytes_max($res)" + case _ => "UNKNOWN_Max: " + typeArray.toString() + } + } + override def anyField(value: expr, attrName: String): String = { + if (attrName == "_io") + { + return s"HANDLE(${translate(value)})->stream" + } + if (attrName == "_parent") { + return s"HANDLE(${translate(value)})->parent" + } + if (!isInternal) { + return s"${translate(value)}->$attrName" + } + val dataType = detectType(value) + dataType match { + case t: UserType => + if (t.isOpaque && t.classSpec.get != provider.topClass) { // Our own top class is *not* opaque! + s"${translate(value)}->$attrName" + } else { + val typeStr = CCompiler.kaitaiType2NativeType(t) + s"FIELD(${translate(value)}, $typeStr, $attrName)" + } + case _ => s"ERROR Type" + } + } + override def strConcat(left: Ast.expr, right: Ast.expr): String = s"ks_string_concat(${translate(left)}, ${translate(right)})" + override def doBoolLiteral(n: Boolean): String = if (n) "1" else "0" + override def kaitaiStreamEof(value: Ast.expr): String = + s"ks_stream_is_eof(${translate(value)})" + override def kaitaiStreamSize(value: Ast.expr): String = + s"ks_stream_get_length(${translate(value)})" + override def kaitaiStreamPos(value: Ast.expr): String = + s"ks_stream_get_pos(${translate(value)})" +} From 1175feddae1bc6c2838d7ede254c8124b92c917e Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Mon, 1 Jan 2024 20:14:43 +0100 Subject: [PATCH 02/14] Update to Kaitai changes --- .../main/scala/io/kaitai/struct/languages/CCompiler.scala | 8 ++++---- .../scala/io/kaitai/struct/translators/CTranslator.scala | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala index 294d50017..816c1d442 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala @@ -89,7 +89,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) dataType match { case t: UserType => "*" case at: ArrayType => "*" - case KaitaiStructType | CalcKaitaiStructType => "*" + case KaitaiStructType | CalcKaitaiStructType(_) => "*" case AnyType => "*" case sw: SwitchType => getPtrSuffix(sw.combinedType) case _ => "" @@ -763,7 +763,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) // Assignment helper // ############################## - override def condRepeatCommonInit(id: Identifier, dataType: DataType, needRaw: NeedRaw): Unit = { + override def condRepeatInitAttr(id: Identifier, dataType: DataType): Unit = { } def isTypeGenericInternal(idType : DataType): Boolean = { @@ -1187,7 +1187,7 @@ object CCompiler extends LanguageCompilerStatic case t: EnumType => s"ksx_array_${makeName(t.enumSpec.get.name)}" case _: StrType => s"ks_array_string" case _: BytesType => s"ks_array_bytes" - case KaitaiStructType | CalcKaitaiStructType => s"ks_array_usertype_generic" + case KaitaiStructType | CalcKaitaiStructType(_) => s"ks_array_usertype_generic" case AnyType => "ks_array_any" case st: SwitchType => kaitaiType2NativeTypeArray(st.combinedType) case _ => s"ks_array_${kaitaiType2NativeType(attrType)}" @@ -1219,7 +1219,7 @@ object CCompiler extends LanguageCompilerStatic case _: BytesType => "ks_bytes*" case AnyType => "void" - case KaitaiStructType | CalcKaitaiStructType => kstructName + case KaitaiStructType | CalcKaitaiStructType(_) => kstructName case KaitaiStreamType | OwnedKaitaiStreamType => kstreamName case t: UserType => "ksx_" + makeName(t.classSpec.get.name) diff --git a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala index 37ff2ddf5..ae5949a9b 100644 --- a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala +++ b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala @@ -37,7 +37,7 @@ class CTranslator(provider: ClassTypeProvider, importList: CppImportList, isInte case CalcIntType => s"ks_array_int64_t_from_data(stream->config, $size, $args)" case CalcFloatType => s"ks_array_double_from_data(stream->config, $size, $args)" case CalcStrType => s"ks_array_string_from_data(stream->config, $size, $args)" - case KaitaiStructType | CalcKaitaiStructType => s"ks_array_usertype_generic_from_data(stream->config, $size, $args)" + case KaitaiStructType | CalcKaitaiStructType(_) => s"ks_array_usertype_generic_from_data(stream->config, $size, $args)" case _ => s"Missing list type: " + t.toString() } } @@ -166,8 +166,8 @@ class CTranslator(provider: ClassTypeProvider, importList: CppImportList, isInte override def intToStr(i: expr, base: expr): String = { s"ks_string_from_int(stream->config, ${translate(i)}, ${translate(base)})" } - override def bytesToStr(bytesExpr: String, encoding: Ast.expr): String = - s"ks_string_from_bytes($bytesExpr, ${translate(encoding)})" + override def bytesToStr(bytesExpr: String, encoding: String): String = + s"ks_string_from_bytes($bytesExpr, ${doStringLiteral(encoding)})" override def strLength(s: expr): String = s"${translate(s)}->len" From c5436dfee5c901b5f0fb74407a93d37b176fe480 Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Mon, 1 Jan 2024 21:17:35 +0100 Subject: [PATCH 03/14] Fold ks_handle_create into ks_alloc_obj --- .../io/kaitai/struct/languages/CCompiler.scala | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala index 816c1d442..5c223beb3 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala @@ -290,7 +290,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outMethodBody.inc outMethodHead.inc - outSrcMain.puts(s"ksx_${className}* data = ks_alloc(root_stream->config, sizeof(ksx_${className}));") + outSrcMain.puts(s"ksx_${className}* data = ks_alloc_obj(stream, sizeof(ksx_${className}), KS_TYPE_USERTYPE, sizeof(ksx_${className}), sizeof(ksx_${className}_internal), (ks_usertype_generic*)parent_data);") if (rootName == className) { outMethodBody.puts(s"root_data = root_data != 0 ? root_data : data;") @@ -373,7 +373,6 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outSrc.puts } if (!instance && !inEndianFunc) { - outSrc.puts(s"KS_CHECK_$CurrentCheck(HANDLE(data) = ks_handle_create(stream, data, KS_TYPE_USERTYPE, sizeof(ksx_${className}), sizeof(ksx_${className}_internal), (ks_usertype_generic*)parent_data));") outMethodBody.puts("return data;") } if (outMethodHasInternal) { @@ -804,10 +803,9 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) val io_new = makeIO(io) outMethodHasI = true outMethodBody.puts("/* Array (repeat-eos) */") - outMethodBody.puts(s"data->$name = ks_alloc(root_stream->config, sizeof(${kaitaiType2NativeType(dataTypeArray)}));") + outMethodBody.puts(s"data->$name = ks_alloc_obj(stream, sizeof(${kaitaiType2NativeType(dataTypeArray)}), $arrayTypeSize, 0, 0);") outMethodBody.puts(s"data->$name->size = 0;") outMethodBody.puts(s"data->$name->data = 0;") - outMethodBody.puts(s"KS_CHECK_$CurrentCheck(HANDLE(data->$name) = ks_handle_create(stream, data->$name, $arrayTypeSize, 0, 0));"); outMethodBody.puts("{") outMethodBody.inc outMethodBody.puts(s"while (!ks_stream_is_eof($io_new)) {") @@ -850,14 +848,13 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) val ptr = getPtrSuffix(dataType) outMethodHasI = true outMethodBody.puts("/* Array (repeat-expr) */") - outMethodBody.puts(s"data->$name = ks_alloc(root_stream->config, sizeof(${kaitaiType2NativeType(dataTypeArray)}));") + outMethodBody.puts(s"data->$name = ks_alloc_obj(stream, sizeof(${kaitaiType2NativeType(dataTypeArray)}), $arrayTypeSize, 0, 0);") outMethodBody.puts(s"data->$name->size = $len;") - outMethodBody.puts(s"data->$name->data = ks_alloc(root_stream->config, sizeof(${kaitaiType2NativeType(dataType)}$ptr) * data->$name->size);") + outMethodBody.puts(s"data->$name->data = ks_alloc_data(root_stream->config, sizeof(${kaitaiType2NativeType(dataType)}$ptr) * data->$name->size);") if (isTypeGeneric(id)) { outMethodHasInternal = true - outMethodBody.puts(s"internal->_read_instances_$name = ks_alloc(root_stream->config, sizeof(ks_callback) * data->$name->size);") + outMethodBody.puts(s"internal->_read_instances_$name = ks_alloc_data(root_stream->config, sizeof(ks_callback) * data->$name->size);") } - outMethodBody.puts(s"KS_CHECK_$CurrentCheck(HANDLE(data->$name) = ks_handle_create(stream, data->$name, $arrayTypeSize, 0, 0));"); outMethodBody.puts(s"for ($pos = 0; $pos < data->$name->size; $pos++)") outMethodBody.puts("{") outMethodBody.inc @@ -882,10 +879,9 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) val ptr = getPtrSuffix(dataType) outMethodHasI = true outMethodBody.puts("/* Array (repeat-until) */") - outMethodBody.puts(s"data->$name = ks_alloc(root_stream->config, sizeof(${kaitaiType2NativeType(dataTypeArray)}));") + outMethodBody.puts(s"data->$name = ks_alloc_obj(stream, sizeof(${kaitaiType2NativeType(dataTypeArray)}), $arrayTypeSize, 0, 0);") outMethodBody.puts(s"data->$name->size = 0;") outMethodBody.puts(s"data->$name->data = 0;") - outMethodBody.puts(s"KS_CHECK_$CurrentCheck(HANDLE(data->$name) = ks_handle_create(stream, data->$name, $arrayTypeSize, 0, 0));"); outMethodBody.puts("{") outMethodBody.inc outMethodBody.puts(s"${kaitaiType2NativeType(dataType)}$ptr ${translator.doName("_")} = {0};") From 7ec6b9a6889951039f0d92014cac23ab2f121643 Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Mon, 1 Jan 2024 21:49:15 +0100 Subject: [PATCH 04/14] Use ks_usertype_get_config instead of stream->config --- .../main/scala/io/kaitai/struct/translators/CTranslator.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala index ae5949a9b..6df3930ed 100644 --- a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala +++ b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala @@ -164,7 +164,7 @@ class CTranslator(provider: ClassTypeProvider, importList: CppImportList, isInte override def floatToInt(v: expr): String = s"(long) (${translate(v)})" override def intToStr(i: expr, base: expr): String = { - s"ks_string_from_int(stream->config, ${translate(i)}, ${translate(base)})" + s"ks_string_from_int(ks_usertype_get_config(&data->kaitai_base), ${translate(i)}, ${translate(base)})" } override def bytesToStr(bytesExpr: String, encoding: String): String = s"ks_string_from_bytes($bytesExpr, ${doStringLiteral(encoding)})" @@ -183,7 +183,7 @@ class CTranslator(provider: ClassTypeProvider, importList: CppImportList, isInte } override def doStringLiteral(s: String): String = { - s"ks_string_from_cstr(stream->config, ${super.doStringLiteral(s)})" + s"ks_string_from_cstr(ks_usertype_get_config(&data->kaitai_base), ${super.doStringLiteral(s)})" } override def bytesFirst(b: Ast.expr): String = From 36c68b5b373ab0d89dc2cb5212a72ec6a517ef69 Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Mon, 1 Jan 2024 22:01:35 +0100 Subject: [PATCH 05/14] Use function to get byte length --- .../main/scala/io/kaitai/struct/translators/CTranslator.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala index 6df3930ed..6c1f3f08c 100644 --- a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala +++ b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala @@ -190,11 +190,11 @@ class CTranslator(provider: ClassTypeProvider, importList: CppImportList, isInte s"ks_bytes_get_at(${translate(b)}, 0)" override def bytesLength(b: Ast.expr): String = - s"${translate(b)}->length" + s"ks_bytes_get_length(${translate(b)})" override def bytesLast(b: Ast.expr): String = { val v = translate(b) - s"ks_bytes_get_at($v, $v->length - 1)" + s"ks_bytes_get_at($v, ks_bytes_get_length($v) - 1)" } override def bytesSubscript(container: Ast.expr, idx: Ast.expr): String = From 5326ecd1763c03979b2f540d3909a0ddca29dd5f Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Mon, 1 Jan 2024 22:33:48 +0100 Subject: [PATCH 06/14] Introduce ks_stream_get_error --- .../src/main/scala/io/kaitai/struct/languages/CCompiler.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala index 5c223beb3..b2fcd186a 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala @@ -272,10 +272,10 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outSrcMain.inc outSrcMain.puts(s"ksx_${className}* data;") outSrcMain.puts(s"data = ksx_read_$className(stream, 0, 0, stream$addParams);") - outSrcMain.puts(s"if (error) *error = stream->config->error;") + outSrcMain.puts(s"if (error) *error = ks_stream_get_error(stream);") outSrcMain.puts(s"KS_CHECK_DATA();") outSrcMain.puts(s"ksx_read_${className}_instances(data);") - outSrcMain.puts(s"if (error) *error = stream->config->error;") + outSrcMain.puts(s"if (error) *error = ks_stream_get_error(stream);") outSrcMain.puts(s"KS_CHECK_DATA();") outSrcMain.puts(s"return data;") outSrcMain.dec From 1d5823fd5fd0dad933559731a7716273e1f3a217 Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Mon, 1 Jan 2024 22:39:27 +0100 Subject: [PATCH 07/14] Introduce ks_inflate --- .../src/main/scala/io/kaitai/struct/languages/CCompiler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala index b2fcd186a..1a6153e23 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala @@ -611,7 +611,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outMethodBody.puts("Unknown xor type: " + t.toString()) } case ProcessZlib => - outMethodBody.puts(s"$srcExpr = stream->config->inflate($srcExpr);") + outMethodBody.puts(s"$srcExpr = ks_inflate(ks_usertype_get_config(&data->kaitai_base), $srcExpr);") case ProcessRotate(isLeft, rotValue) => val expr = if (isLeft) { expression(rotValue) From cdef786638695a7fcc7d42ef5d299b5d1e13e6c9 Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Mon, 1 Jan 2024 22:48:07 +0100 Subject: [PATCH 08/14] Introduce ks_usertype_get_stream --- .../src/main/scala/io/kaitai/struct/languages/CCompiler.scala | 4 ++-- .../main/scala/io/kaitai/struct/translators/CTranslator.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala index 1a6153e23..fdaefb154 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala @@ -214,7 +214,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outInstancesRead.puts("{") outInstancesRead.inc outInstancesRead.puts("int64_t i;") - outInstancesRead.puts("ks_stream* stream = HANDLE(data)->stream;") + outInstancesRead.puts("ks_stream* stream = ks_usertype_get_stream(&data->kaitai_base);") outInstancesRead.puts(s"ksx_${className}_internal* internal = (ksx_${className}_internal*)HANDLE(data)->internal_read;") outInstancesRead.puts("(void)i;") outInstancesRead.puts("(void)stream;") @@ -543,7 +543,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outSrcInstancesGet.puts("{") outSrcInstancesGet.inc if (isInstance) { - outSrcInstancesGet.puts(s"ks_stream* stream = HANDLE(data)->stream;") + outSrcInstancesGet.puts(s"ks_stream* stream = ks_usertype_get_stream(&data->kaitai_base);") outSrcInstancesGet.puts(s"KS_CHECK(ksx_read_${currentClassName}_instance_${name}(ks_stream_get_root(stream), (void*)ks_usertype_get_root((void*)data), stream, data), data->$name);") } attrType match { diff --git a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala index 6c1f3f08c..c1e010519 100644 --- a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala +++ b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala @@ -246,7 +246,7 @@ class CTranslator(provider: ClassTypeProvider, importList: CppImportList, isInte override def anyField(value: expr, attrName: String): String = { if (attrName == "_io") { - return s"HANDLE(${translate(value)})->stream" + return s"ks_usertype_get_stream((ks_usertype_generic*)${translate(value)})" } if (attrName == "_parent") { return s"HANDLE(${translate(value)})->parent" From d5eb63cfc3e20a3429479193623c416f7efd4732 Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Mon, 1 Jan 2024 23:07:57 +0100 Subject: [PATCH 09/14] Introduce ks_usertype_get_internal_read --- .../main/scala/io/kaitai/struct/languages/CCompiler.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala index fdaefb154..cdc65b2cd 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala @@ -206,7 +206,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outInstancesFill.puts(s"static void ksx_fill_${className}_instances(ksx_${className}* data)") outInstancesFill.puts("{") outInstancesFill.inc - outInstancesFill.puts(s"ksx_${className}_internal* internal = (ksx_${className}_internal*)HANDLE(data)->internal_read;") + outInstancesFill.puts(s"ksx_${className}_internal* internal = (ksx_${className}_internal*)ks_usertype_get_internal_read(&data->kaitai_base);") outInstancesFill.puts(s"(void)internal;") outSrcDefs.puts(s"static void ksx_read_${className}_instances(ksx_${className}* data);") @@ -215,7 +215,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outInstancesRead.inc outInstancesRead.puts("int64_t i;") outInstancesRead.puts("ks_stream* stream = ks_usertype_get_stream(&data->kaitai_base);") - outInstancesRead.puts(s"ksx_${className}_internal* internal = (ksx_${className}_internal*)HANDLE(data)->internal_read;") + outInstancesRead.puts(s"ksx_${className}_internal* internal = (ksx_${className}_internal*)ks_usertype_get_internal_read(&data->kaitai_base);") outInstancesRead.puts("(void)i;") outInstancesRead.puts("(void)stream;") outInstancesRead.puts("(void)internal;") @@ -376,7 +376,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outMethodBody.puts("return data;") } if (outMethodHasInternal) { - outSrc.puts(s"internal = (ksx_${className}_internal*)HANDLE(data)->internal_read;") + outSrc.puts(s"internal = (ksx_${className}_internal*)ks_usertype_get_internal_read(&data->kaitai_base);") outMethodHasInternal = false } inEndianFunc = false From 6efebed126a9440a8940c2b40be3c766a1bd87c4 Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Mon, 1 Jan 2024 23:41:30 +0100 Subject: [PATCH 10/14] Introduce ks_stream_get_config --- .../kaitai/struct/languages/CCompiler.scala | 12 +++--- .../struct/translators/CTranslator.scala | 38 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala index cdc65b2cd..e7ca6c1e2 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala @@ -818,11 +818,11 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) val ptr = getPtrSuffix(dataTypeLast) val sizeof = s"sizeof(${kaitaiType2NativeType(dataTypeLast)}$ptr)" outMethodBody.puts(s"data->$name->size++;") - outMethodBody.puts(s"data->$name->data = ks_realloc(root_stream->config, data->$name->data, $sizeof * data->$name->size);") + outMethodBody.puts(s"data->$name->data = ks_realloc(ks_stream_get_config(stream), data->$name->data, $sizeof * data->$name->size);") outMethodBody.puts(s"memset(data->$name->data + data->$name->size - 1, 0, $sizeof);") if (isTypeGeneric(id)) { outMethodHasInternal = true - outMethodBody.puts(s"internal->_read_instances_$name = ks_realloc(root_stream->config, internal->_read_instances_$name, sizeof(ks_callback) * data->$name->size);") + outMethodBody.puts(s"internal->_read_instances_$name = ks_realloc(ks_stream_get_config(stream), internal->_read_instances_$name, sizeof(ks_callback) * data->$name->size);") outMethodBody.puts(s"internal->_read_instances_$name[data->$name->size -1] = 0;") } handleAssignmentCommon(id, expr, true) @@ -850,10 +850,10 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outMethodBody.puts("/* Array (repeat-expr) */") outMethodBody.puts(s"data->$name = ks_alloc_obj(stream, sizeof(${kaitaiType2NativeType(dataTypeArray)}), $arrayTypeSize, 0, 0);") outMethodBody.puts(s"data->$name->size = $len;") - outMethodBody.puts(s"data->$name->data = ks_alloc_data(root_stream->config, sizeof(${kaitaiType2NativeType(dataType)}$ptr) * data->$name->size);") + outMethodBody.puts(s"data->$name->data = ks_alloc_data(ks_stream_get_config(stream), sizeof(${kaitaiType2NativeType(dataType)}$ptr) * data->$name->size);") if (isTypeGeneric(id)) { outMethodHasInternal = true - outMethodBody.puts(s"internal->_read_instances_$name = ks_alloc_data(root_stream->config, sizeof(ks_callback) * data->$name->size);") + outMethodBody.puts(s"internal->_read_instances_$name = ks_alloc_data(ks_stream_get_config(stream), sizeof(ks_callback) * data->$name->size);") } outMethodBody.puts(s"for ($pos = 0; $pos < data->$name->size; $pos++)") outMethodBody.puts("{") @@ -902,11 +902,11 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) val ptr = getPtrSuffix(dataTypeLast) val sizeof = s"sizeof(${kaitaiType2NativeType(dataTypeLast)}$ptr)" outMethodBody.puts(s"data->$name->size++;") - outMethodBody.puts(s"data->$name->data = ks_realloc(root_stream->config, data->$name->data, $sizeof * data->$name->size);") + outMethodBody.puts(s"data->$name->data = ks_realloc(ks_stream_get_config(stream), data->$name->data, $sizeof * data->$name->size);") outMethodBody.puts(s"memset(data->$name->data + data->$name->size - 1, 0, $sizeof);") if (isTypeGeneric(id)) { outMethodHasInternal = true - outMethodBody.puts(s"internal->_read_instances_$name = ks_realloc(root_stream->config, internal->_read_instances_$name, sizeof(ks_callback) * data->$name->size);") + outMethodBody.puts(s"internal->_read_instances_$name = ks_realloc(ks_stream_get_config(stream), internal->_read_instances_$name, sizeof(ks_callback) * data->$name->size);") outMethodBody.puts(s"internal->_read_instances_$name[data->$name->size -1] = 0;") } handleAssignmentCommon(id, expr, true) diff --git a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala index c1e010519..dbe3e96bc 100644 --- a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala +++ b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala @@ -21,23 +21,23 @@ class CTranslator(provider: ClassTypeProvider, importList: CppImportList, isInte val size = value.size val args = value.map(translate).mkString(", ") t match { - case Int1Type(false) => s"ks_array_uint8_t_from_data(stream->config, $size, $args)" - case IntMultiType(false, Width2, _) => s"ks_array_uint16_t_from_data(stream->config, $size, $args)" - case IntMultiType(false, Width4, _) => s"ks_array_uint32_t_from_data(stream->config, $size, $args)" - case IntMultiType(false, Width8, _) => s"ks_array_uint64_t_from_data(stream->config, $size, $args)" - - case Int1Type(true) => s"ks_array_int8_t_from_data(stream->config, $size, $args)" - case IntMultiType(true, Width2, _) => s"ks_array_int16_t_from_data(stream->config, $size, $args)" - case IntMultiType(true, Width4, _) => s"ks_array_int32_t_from_data(stream->config, $size, $args)" - case IntMultiType(true, Width8, _) => s"ks_array_int64_t_from_data(stream->config, $size, $args)" - - case FloatMultiType(Width4, _) => s"ks_array_float_from_data(stream->config, $size, $args)" - case FloatMultiType(Width8, _) => s"ks_array_double_from_data(stream->config, $size, $args)" - - case CalcIntType => s"ks_array_int64_t_from_data(stream->config, $size, $args)" - case CalcFloatType => s"ks_array_double_from_data(stream->config, $size, $args)" - case CalcStrType => s"ks_array_string_from_data(stream->config, $size, $args)" - case KaitaiStructType | CalcKaitaiStructType(_) => s"ks_array_usertype_generic_from_data(stream->config, $size, $args)" + case Int1Type(false) => s"ks_array_uint8_t_from_data(ks_stream_get_config(stream), $size, $args)" + case IntMultiType(false, Width2, _) => s"ks_array_uint16_t_from_data(ks_stream_get_config(stream), $size, $args)" + case IntMultiType(false, Width4, _) => s"ks_array_uint32_t_from_data(ks_stream_get_config(stream), $size, $args)" + case IntMultiType(false, Width8, _) => s"ks_array_uint64_t_from_data(ks_stream_get_config(stream), $size, $args)" + + case Int1Type(true) => s"ks_array_int8_t_from_data(ks_stream_get_config(stream), $size, $args)" + case IntMultiType(true, Width2, _) => s"ks_array_int16_t_from_data(ks_stream_get_config(stream), $size, $args)" + case IntMultiType(true, Width4, _) => s"ks_array_int32_t_from_data(ks_stream_get_config(stream), $size, $args)" + case IntMultiType(true, Width8, _) => s"ks_array_int64_t_from_data(ks_stream_get_config(stream), $size, $args)" + + case FloatMultiType(Width4, _) => s"ks_array_float_from_data(ks_stream_get_config(stream), $size, $args)" + case FloatMultiType(Width8, _) => s"ks_array_double_from_data(ks_stream_get_config(stream), $size, $args)" + + case CalcIntType => s"ks_array_int64_t_from_data(ks_stream_get_config(stream), $size, $args)" + case CalcFloatType => s"ks_array_double_from_data(ks_stream_get_config(stream), $size, $args)" + case CalcStrType => s"ks_array_string_from_data(ks_stream_get_config(stream), $size, $args)" + case KaitaiStructType | CalcKaitaiStructType(_) => s"ks_array_usertype_generic_from_data(ks_stream_get_config(stream), $size, $args)" case _ => s"Missing list type: " + t.toString() } } @@ -49,7 +49,7 @@ class CTranslator(provider: ClassTypeProvider, importList: CppImportList, isInte } override def doByteArrayLiteral(arr: Seq[Byte]): String = { - val config = if (isInternal) "stream->config" else "config" + val config = if (isInternal) "ks_stream_get_config(stream)" else "config" if (arr.size == 0) { s"ks_bytes_from_data($config, 0)" } else { @@ -57,7 +57,7 @@ class CTranslator(provider: ClassTypeProvider, importList: CppImportList, isInte } } override def doByteArrayNonLiteral(elts: Seq[Ast.expr]): String = - s"ks_bytes_from_data_terminated(stream->config, ${elts.map(translate).mkString(", ")}, 0xffff)" + s"ks_bytes_from_data_terminated(ks_stream_get_config(stream), ${elts.map(translate).mkString(", ")}, 0xffff)" override val asciiCharQuoteMap: Map[Char, String] = Map( '\t' -> "\\t", From 05b5abb23b72ea7abaeea10fd9c15b0a90e8a914 Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Mon, 1 Jan 2024 23:56:23 +0100 Subject: [PATCH 11/14] Get rid of root_data and root_stream --- .../kaitai/struct/languages/CCompiler.scala | 25 ++++++++----------- .../struct/translators/CTranslator.scala | 4 +-- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala index e7ca6c1e2..e54137b67 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala @@ -271,7 +271,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outSrcMain.puts("{") outSrcMain.inc outSrcMain.puts(s"ksx_${className}* data;") - outSrcMain.puts(s"data = ksx_read_$className(stream, 0, 0, stream$addParams);") + outSrcMain.puts(s"data = ksx_read_$className(0, stream$addParams);") outSrcMain.puts(s"if (error) *error = ks_stream_get_error(stream);") outSrcMain.puts(s"KS_CHECK_DATA();") outSrcMain.puts(s"ksx_read_${className}_instances(data);") @@ -282,19 +282,16 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outSrcMain.puts("}") } val endianess = if (isHybrid) ", ks_bool is_le" else "" - outSrcDefs.puts(s"static ksx_${className}* ksx_read_${className}(ks_stream* root_stream, ksx_${rootName}* root_data, void* parent_data, ks_stream* stream$endianess$paramsArg);"); + outSrcDefs.puts(s"static ksx_${className}* ksx_read_${className}(ks_usertype_generic* parent_data, ks_stream* stream$endianess$paramsArg);"); outSrcMain.puts - outSrcMain.puts(s"static ksx_${className}* ksx_read_${className}(ks_stream* root_stream, ksx_${rootName}* root_data, void* parent_data, ks_stream* stream$endianess$paramsArg)") + outSrcMain.puts(s"static ksx_${className}* ksx_read_${className}(ks_usertype_generic* parent_data, ks_stream* stream$endianess$paramsArg)") outSrcMain.puts("{") outSrcMain.inc outMethodBody.inc outMethodHead.inc - outSrcMain.puts(s"ksx_${className}* data = ks_alloc_obj(stream, sizeof(ksx_${className}), KS_TYPE_USERTYPE, sizeof(ksx_${className}), sizeof(ksx_${className}_internal), (ks_usertype_generic*)parent_data);") + outSrcMain.puts(s"ksx_${className}* data = ks_alloc_obj(stream, sizeof(ksx_${className}), KS_TYPE_USERTYPE, sizeof(ksx_${className}), sizeof(ksx_${className}_internal), parent_data);") - if (rootName == className) { - outMethodBody.puts(s"root_data = root_data != 0 ? root_data : data;") - } params.foreach((p) => outMethodBody.puts(s"data->${idToStr(p.id)} = ${paramName(p.id)};") ) @@ -327,11 +324,11 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outMethodBody.puts(s"KS_ASSERT_DATA(data->${privateMemberName(EndianIdentifier)} == -1, $errorStr, KS_ERROR_ENDIANESS_UNSPECIFIED);") outMethodBody.puts(s"if (data->${privateMemberName(EndianIdentifier)} == 1) {") outMethodBody.inc - outMethodBody.puts(s"KS_CHECK_$CurrentCheck(ksx_read_${currentClassNames.last}_le(root_stream, root_data, stream, data));"); + outMethodBody.puts(s"KS_CHECK_$CurrentCheck(ksx_read_${currentClassNames.last}_le(stream, data));"); outMethodBody.dec outMethodBody.puts("} else {") outMethodBody.inc - outMethodBody.puts(s"KS_CHECK_$CurrentCheck(ksx_read_${currentClassNames.last}_be(root_stream, root_data, stream, data));"); + outMethodBody.puts(s"KS_CHECK_$CurrentCheck(ksx_read_${currentClassNames.last}_be(stream, data));"); outMethodBody.dec outMethodBody.puts("}") } @@ -347,8 +344,8 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) return } val currentClassName = currentClassNames.last - outSrcDefs.puts(s"static void ksx_read_${currentClassName}_$suffix(ks_stream* root_stream, ksx_$currentRootName* root_data, ks_stream* stream, ksx_$currentClassName* data);"); - outSrcMain.puts(s"static void ksx_read_${currentClassName}_$suffix(ks_stream* root_stream, ksx_$currentRootName* root_data, ks_stream* stream, ksx_$currentClassName* data)"); + outSrcDefs.puts(s"static void ksx_read_${currentClassName}_$suffix(ks_stream* stream, ksx_$currentClassName* data);"); + outSrcMain.puts(s"static void ksx_read_${currentClassName}_$suffix(ks_stream* stream, ksx_$currentClassName* data)"); outSrcMain.puts("{") outSrcMain.inc outMethodBody.inc @@ -470,7 +467,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) val name = privateMemberName(instName) translator.setCurrentClass(className) - outMethodHead.puts(s"static void ksx_read_${className}_instance_${name}(ks_stream* root_stream, ksx_${currentRootName}* root_data, ks_stream* stream, ksx_${className}* data)") + outMethodHead.puts(s"static void ksx_read_${className}_instance_${name}(ks_stream* stream, ksx_${className}* data)") outMethodHead.puts("{") outMethodHead.inc outMethodBody.inc @@ -544,7 +541,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outSrcInstancesGet.inc if (isInstance) { outSrcInstancesGet.puts(s"ks_stream* stream = ks_usertype_get_stream(&data->kaitai_base);") - outSrcInstancesGet.puts(s"KS_CHECK(ksx_read_${currentClassName}_instance_${name}(ks_stream_get_root(stream), (void*)ks_usertype_get_root((void*)data), stream, data), data->$name);") + outSrcInstancesGet.puts(s"KS_CHECK(ksx_read_${currentClassName}_instance_${name}(stream, data), data->$name);") } attrType match { case t: UserType => @@ -1009,7 +1006,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) if (t.isOpaque && t.classSpec.get != typeProvider.topClass) { // Our own top class is *not* opaque! s"ksx_read_${typeName}_from_stream($io_new, 0$addEndian$addParams)" } else { - s"ksx_read_$typeName(root_stream, root_data, $parent, $io_new$addEndian$addParams)" + s"ksx_read_$typeName((ks_usertype_generic*)${parent}, $io_new$addEndian$addParams)" } case _ => outMethodBody.puts("Missing expression type: " + dataType.toString()) diff --git a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala index dbe3e96bc..ea206f90f 100644 --- a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala +++ b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala @@ -118,8 +118,8 @@ class CTranslator(provider: ClassTypeProvider, importList: CppImportList, isInte case Identifier.SWITCH_ON => "on" case Identifier.INDEX => "i" case Identifier.IO => "stream" - case Identifier.PARENT => "HANDLE(data)->parent" - case Identifier.ROOT => "root_data" + case Identifier.PARENT => "(HANDLE(data)->parent)" + case Identifier.ROOT => s"(ksx_$currentClassName*)ks_usertype_get_root(&data->kaitai_base)" case Identifier.ITERATOR => "_temp" case _ => s"data->$s" } From 118cf8d1b58960b9d1b76ea423c9e85e206a016a Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Tue, 2 Jan 2024 00:19:55 +0100 Subject: [PATCH 12/14] Introduce ks_usertype_get_parent --- .../main/scala/io/kaitai/struct/translators/CTranslator.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala index ea206f90f..34f6dc916 100644 --- a/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala +++ b/shared/src/main/scala/io/kaitai/struct/translators/CTranslator.scala @@ -118,7 +118,7 @@ class CTranslator(provider: ClassTypeProvider, importList: CppImportList, isInte case Identifier.SWITCH_ON => "on" case Identifier.INDEX => "i" case Identifier.IO => "stream" - case Identifier.PARENT => "(HANDLE(data)->parent)" + case Identifier.PARENT => "ks_usertype_get_parent((ks_usertype_generic*)data)" case Identifier.ROOT => s"(ksx_$currentClassName*)ks_usertype_get_root(&data->kaitai_base)" case Identifier.ITERATOR => "_temp" case _ => s"data->$s" @@ -249,7 +249,7 @@ class CTranslator(provider: ClassTypeProvider, importList: CppImportList, isInte return s"ks_usertype_get_stream((ks_usertype_generic*)${translate(value)})" } if (attrName == "_parent") { - return s"HANDLE(${translate(value)})->parent" + return s"ks_usertype_get_parent((ks_usertype_generic*)${translate(value)})" } if (!isInternal) { return s"${translate(value)}->$attrName" From 59d79da042b019832951c30cdfe5ce1bc62a8327 Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Tue, 2 Jan 2024 00:26:33 +0100 Subject: [PATCH 13/14] Don't depend on internals --- shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala index e54137b67..b6b75bf2b 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala @@ -123,7 +123,6 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def fileHeader(topClassName: String): Unit = { outSrcHeader.puts(s"/* $headerComment */") outSrcHeader.puts - outSrcHeader.puts("#define KS_DEPEND_ON_INTERNALS") outHdrHeader.puts(s"/* $headerComment */") outHdrHeader.puts From 2b606c22026d0931d85ee640388d4147f8c2524e Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Tue, 2 Jan 2024 00:41:32 +0100 Subject: [PATCH 14/14] Introduce internal header --- .../src/main/scala/io/kaitai/struct/languages/CCompiler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala index b6b75bf2b..5221e2ead 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/CCompiler.scala @@ -132,7 +132,7 @@ class CCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) importListSrc.addLocal(outFileNameHeader(topClassName)) - importListHdr.addKaitai("kaitaistruct.h") + importListHdr.addKaitai("kaitaistruct_internal.h") outHdrDefs.puts outHdrDefs.puts("/* Forward declarations */")