blob: c043f327c56c9758163f917d62e4ff5fb46564ab [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
func leadingPrimitiveFastPathFields(_ fields: [ParsedField]) -> [ParsedField] {
var result: [ParsedField] = []
result.reserveCapacity(fields.count)
for field in fields {
if isPrimitiveFastPathField(field) {
result.append(field)
} else {
break
}
}
return result
}
private func leadingFixedPrimitiveFields(_ fields: [ParsedField]) -> [ParsedField] {
var result: [ParsedField] = []
result.reserveCapacity(fields.count)
for field in fields {
if primitiveFixedByteWidth(for: field) != nil {
result.append(field)
} else {
break
}
}
return result
}
private func primitiveFixedPrefixBytes(_ fields: [ParsedField]) -> Int {
fields.reduce(0) { partial, field in
partial + (primitiveFixedByteWidth(for: field) ?? 0)
}
}
private func primitiveFixedByteWidth(for field: ParsedField) -> Int? {
switch trimType(field.typeText) {
case "Bool", "Int8", "UInt8":
return 1
case "Int16", "UInt16":
return 2
case "Float":
return 4
case "Double":
return 8
default:
return nil
}
}
private func isPrimitiveFastPathField(_ field: ParsedField) -> Bool {
guard !field.isOptional else {
return false
}
guard field.dynamicAnyCodec == nil, field.customCodecType == nil else {
return false
}
guard field.typeID != 27, !compatibleFieldNeedsTypeInfo(field) else {
return false
}
return primitiveUnsafeWriteMethod(for: field) != nil && primitiveUnsafeReadMethod(for: field) != nil
}
func buildPrimitiveFastWriteBlock(_ fields: [ParsedField]) -> String? {
guard !fields.isEmpty else {
return nil
}
let fixedFields = leadingFixedPrimitiveFields(fields)
let remainingFields = Array(fields.dropFirst(fixedFields.count))
let fixedPrefixBytes = primitiveFixedPrefixBytes(fixedFields)
let maxNumericBytes = fields.reduce(0) { partial, field in
partial + (primitiveMaxEncodedByteWidth(for: field) ?? 0)
}
guard maxNumericBytes > 0 else {
return nil
}
let locals = fields.map { field in
"let __\(field.name) = self.\(field.name)"
}.joined(separator: "\n ")
var fixedOffset = 0
let fixedWrites = fixedFields.compactMap { field -> String? in
guard let line = primitiveUnsafeWriteFixedLine(for: field, offset: fixedOffset) else {
return nil
}
fixedOffset += primitiveFixedByteWidth(for: field) ?? 0
return line
}.joined(separator: "\n ")
let remainingWrites = remainingFields.compactMap { field in
primitiveUnsafeWriteAdvanceLine(for: field, indexExpr: "__writerIndex")
}.joined(separator: "\n ")
var bodySections: [String] = []
if !fixedWrites.isEmpty {
bodySections.append(fixedWrites)
}
if !remainingWrites.isEmpty {
bodySections.append(
"""
var __writerIndex = \(fixedPrefixBytes)
\(remainingWrites)
assert(__writerIndex <= \(maxNumericBytes))
return __writerIndex
"""
)
} else {
bodySections.append("return \(fixedPrefixBytes)")
}
let writeBody = bodySections.joined(separator: "\n ")
return """
\(locals)
UnsafeUtil.writeRegion(buffer: __buffer, maxCount: \(maxNumericBytes)) { __base in
\(writeBody)
}
"""
}
private func primitiveMaxEncodedByteWidth(for field: ParsedField) -> Int? {
switch trimType(field.typeText) {
case "Bool", "Int8", "UInt8":
return 1
case "Int16", "UInt16":
return 2
case "Float":
return 4
case "Double":
return 8
case "Int32":
return 5
case "UInt32":
return 5
case "Int64":
return 9
case "UInt64":
return 9
case "Int":
return 9
case "UInt":
return 9
default:
return nil
}
}
private func primitiveUnsafeWriteFixedLine(for field: ParsedField, offset: Int) -> String? {
guard let method = primitiveUnsafeWriteMethod(for: field) else {
return nil
}
return "_ = UnsafeUtil.\(method)(__\(field.name), to: __base, index: \(offset))"
}
private func primitiveUnsafeWriteAdvanceLine(for field: ParsedField, indexExpr: String) -> String? {
guard let method = primitiveUnsafeWriteMethod(for: field) else {
return nil
}
return "__writerIndex = UnsafeUtil.\(method)(__\(field.name), to: __base, index: \(indexExpr))"
}
private func primitiveUnsafeWriteMethod(for field: ParsedField) -> String? {
switch trimType(field.typeText) {
case "Bool":
return "writeBool"
case "Int8":
return "writeInt8"
case "Int16":
return "writeInt16"
case "Int32":
return "writeInt32"
case "Int64":
return "writeInt64"
case "Int":
return "writeInt"
case "UInt8":
return "writeUInt8"
case "UInt16":
return "writeUInt16"
case "UInt32":
return "writeUInt32"
case "UInt64":
return "writeUInt64"
case "UInt":
return "writeUInt"
case "Float":
return "writeFloat32"
case "Double":
return "writeFloat64"
default:
return nil
}
}
private func primitiveUnsafeReadMethod(for field: ParsedField) -> String? {
switch trimType(field.typeText) {
case "Bool":
return "readBool"
case "Int8":
return "readInt8"
case "Int16":
return "readInt16"
case "Int32":
return "readInt32"
case "Int64":
return "readInt64"
case "Int":
return "readInt"
case "UInt8":
return "readUInt8"
case "UInt16":
return "readUInt16"
case "UInt32":
return "readUInt32"
case "UInt64":
return "readUInt64"
case "UInt":
return "readUInt"
case "Float":
return "readFloat32"
case "Double":
return "readFloat64"
default:
return nil
}
}
private func primitiveUnsafeFixedReadMethod(for field: ParsedField) -> String? {
switch trimType(field.typeText) {
case "Bool":
return "readBoolUnchecked"
case "Int8":
return "readInt8Unchecked"
case "UInt8":
return "readUInt8Unchecked"
case "Int16":
return "readInt16Unchecked"
case "UInt16":
return "readUInt16Unchecked"
case "Float":
return "readFloat32Unchecked"
case "Double":
return "readFloat64Unchecked"
default:
return nil
}
}
private func primitiveUnsafeFixedReadExpr(for field: ParsedField, baseExpr: String, offset: Int) -> String? {
guard let method = primitiveUnsafeFixedReadMethod(for: field) else {
return nil
}
return "UnsafeUtil.\(method)(from: \(baseExpr), index: \(offset))"
}
private func primitiveUnsafePointerReadAdvanceExpr(for field: ParsedField) -> String? {
guard let method = primitiveUnsafeReadMethod(for: field) else {
return nil
}
return "try UnsafeUtil.\(method)(from: __base, length: __length, index: &__readerIndex)"
}
private struct PrimitiveFastReadLayout {
let statements: [String]
let consumedExpr: String
let fixedPrefixBytes: Int
}
private func buildPrimitiveFastReadStatements(
_ fields: [ParsedField],
assignLine: (ParsedField, String) -> String,
remainingReadExpr: (ParsedField) -> String?
) -> PrimitiveFastReadLayout? {
guard !fields.isEmpty else {
return nil
}
let fixedFields = leadingFixedPrimitiveFields(fields)
let remainingFields = Array(fields.dropFirst(fixedFields.count))
let fixedPrefixBytes = primitiveFixedPrefixBytes(fixedFields)
var fixedOffset = 0
let fixedReads = fixedFields.compactMap { field -> String? in
guard let readExpr = primitiveUnsafeFixedReadExpr(for: field, baseExpr: "__base", offset: fixedOffset) else {
return nil
}
fixedOffset += primitiveFixedByteWidth(for: field) ?? 0
return assignLine(field, readExpr)
}.joined(separator: "\n ")
let remainingReads = remainingFields.compactMap { field -> String? in
guard let readExpr = remainingReadExpr(field) else {
return nil
}
return assignLine(field, readExpr)
}.joined(separator: "\n ")
var readSections: [String] = []
if !fixedReads.isEmpty {
readSections.append(fixedReads)
}
if !remainingReads.isEmpty {
readSections.append("var __readerIndex = \(fixedPrefixBytes)")
readSections.append(remainingReads)
}
let consumedExpr = remainingReads.isEmpty ? "\(fixedPrefixBytes)" : "__readerIndex"
return PrimitiveFastReadLayout(
statements: readSections,
consumedExpr: consumedExpr,
fixedPrefixBytes: fixedPrefixBytes
)
}
private func buildPrimitiveFastReadBlock(
_ fields: [ParsedField],
assignLine: (ParsedField, String) -> String
) -> String? {
guard let readLayout = buildPrimitiveFastReadStatements(
fields,
assignLine: assignLine,
remainingReadExpr: primitiveUnsafePointerReadAdvanceExpr
) else {
return nil
}
var readSections: [String] = []
if readLayout.fixedPrefixBytes > 0 {
readSections.append("try UnsafeUtil.checkReadable(__bytes, index: 0, need: \(readLayout.fixedPrefixBytes))")
}
readSections.append(
"""
guard let __base = __bytes.baseAddress else {
throw ForyError.outOfBounds(cursor: 0, need: \(max(readLayout.fixedPrefixBytes, 1)), length: __bytes.count)
}
"""
)
readSections.append("let __length = __bytes.count")
readSections.append(contentsOf: readLayout.statements)
readSections.append("return \(readLayout.consumedExpr)")
let readBody = readSections.joined(separator: "\n ")
return """
try UnsafeUtil.readRegion(buffer: __buffer) { __bytes in
\(readBody)
}
"""
}
func buildPrimitiveFastClassReadBlock(_ fields: [ParsedField]) -> String? {
buildPrimitiveFastReadBlock(fields) { field, readExpr in
"value.\(field.name) = \(readExpr)"
}
}
func buildPrimitiveFastStructReadDeclarations(_ fields: [ParsedField]) -> String? {
guard !fields.isEmpty else {
return nil
}
return fields.map { field in
"var __\(field.name): \(field.typeText) = \(field.typeText).foryDefault()"
}.joined(separator: "\n ")
}
func buildPrimitiveFastStructReadBlock(_ fields: [ParsedField]) -> String? {
buildPrimitiveFastReadBlock(fields) { field, readExpr in
"__\(field.name) = \(readExpr)"
}
}