File: CanonicalLoading.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (144 lines) | stat: -rw-r--r-- 6,962 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
public protocol CanonicalLoading {
    associatedtype Operand
    associatedtype Pointer: Strideable

    func loadUInt8(at pointer: Pointer) -> Operand
    func loadUInt16(at pointer: Pointer) -> Operand
    func loadUInt32(at pointer: Pointer) -> Operand
    func loadUInt64(at pointer: Pointer) -> Operand
    func loadInt8(at pointer: Pointer) -> Operand
    func loadInt16(at pointer: Pointer) -> Operand
    func loadInt32(at pointer: Pointer) -> Operand
    func loadInt64(at pointer: Pointer) -> Operand
    func loadFloat32(at pointer: Pointer) -> Operand
    func loadFloat64(at pointer: Pointer) -> Operand
}

extension CanonicalABI {
    public static func load<Loading: CanonicalLoading, Lifting: CanonicalLifting>(
        loading: inout Loading,
        lifting: inout Lifting,
        type: WITType,
        pointer: Loading.Pointer
    ) throws -> Loading.Operand where Loading.Operand == Lifting.Operand, Lifting.Pointer == Loading.Pointer {
        func loadRecordLike(types: [WITType]) throws -> [Loading.Operand] {
            var fieldValues: [Loading.Operand] = []
            for field in fieldOffsets(fields: types) {
                let (fieldType, offset) = field
                let loaded = try load(
                    loading: &loading, lifting: &lifting,
                    type: fieldType, pointer: pointer.advanced(by: Loading.Pointer.Stride(exactly: offset)!)
                )
                fieldValues.append(loaded)
            }
            return fieldValues
        }
        switch type {
        case .bool: return lifting.liftBool(loading.loadUInt8(at: pointer))
        case .u8: return loading.loadUInt8(at: pointer)
        case .u16: return loading.loadUInt16(at: pointer)
        case .u32: return loading.loadUInt32(at: pointer)
        case .u64: return loading.loadUInt64(at: pointer)
        case .s8: return loading.loadInt8(at: pointer)
        case .s16: return loading.loadInt16(at: pointer)
        case .s32: return loading.loadInt32(at: pointer)
        case .s64: return loading.loadInt64(at: pointer)
        case .float32: return loading.loadFloat32(at: pointer)
        case .float64: return loading.loadFloat64(at: pointer)
        case .char: return lifting.liftChar(loading.loadUInt32(at: pointer))
        case .enum(let enumType):
            let discriminant = loadVariantDiscriminant(
                pointer: pointer, numberOfCases: enumType.cases.count, loading: loading
            )
            return try lifting.liftEnum(discriminant, type: enumType)
        case .flags(let flags):
            let rawValueType = CanonicalABI.rawType(ofFlags: flags.flags.count)
            let rawValues: [Loading.Operand]
            switch rawValueType {
            case .u8: rawValues = [loading.loadUInt8(at: pointer)]
            case .u16: rawValues = [loading.loadUInt16(at: pointer)]
            case .u32(let numberOfU32):
                rawValues = (0..<numberOfU32).map { i in
                    loading.loadUInt32(at: pointer.advanced(by: Loading.Pointer.Stride(exactly: i * 4)!))
                }
            }
            return try lifting.liftFlags(rawValues, type: flags)
        case .string:
            let (buffer, length) = loadList(loading: loading, pointer: pointer)
            return try lifting.liftString(pointer: buffer, length: length, encoding: "utf8")
        case .list(let element):
            let (buffer, length) = loadList(loading: loading, pointer: pointer)
            return try CanonicalABI.liftList(
                pointer: buffer, length: length,
                element: element, lifting: &lifting, loading: &loading
            )
        case .option(let wrapped):
            let discriminant = loading.loadUInt8(at: pointer)
            let offset = Loading.Pointer.Stride(exactly: payloadOffset(cases: [wrapped, nil]))!
            return try lifting.liftOption(
                discriminant: discriminant, wrapped: wrapped,
                liftPayload: {
                    try load(
                        loading: &loading, lifting: &lifting,
                        type: wrapped, pointer: pointer.advanced(by: offset)
                    )
                })
        case .result(let ok, let error):
            let discriminant = loading.loadUInt8(at: pointer)
            let offset = Loading.Pointer.Stride(exactly: payloadOffset(cases: [ok, error]))!
            return try lifting.liftResult(
                discriminant: discriminant, ok: ok, error: error,
                liftPayload: { isError in
                    guard let type = isError ? error : ok else { return nil }
                    return try load(
                        loading: &loading, lifting: &lifting,
                        type: type, pointer: pointer.advanced(by: offset)
                    )
                }
            )
        case .record(let record):
            let types = record.fields.map(\.type)
            return try lifting.liftRecord(fields: loadRecordLike(types: types), type: record)
        case .tuple(let types):
            return try lifting.liftTuple(elements: loadRecordLike(types: types), types: types)
        case .variant(let variant):
            let discriminant = loadVariantDiscriminant(
                pointer: pointer, numberOfCases: variant.cases.count, loading: loading
            )
            let payloadOffset = CanonicalABI.payloadOffset(cases: variant.cases.map(\.type))
            let payloadPtr = pointer.advanced(by: .init(exactly: payloadOffset)!)
            return try lifting.liftVariant(
                discriminant: discriminant, type: variant,
                liftPayload: { i in
                    let variantCase = variant.cases[i]
                    if let caseType = variantCase.type {
                        return try load(loading: &loading, lifting: &lifting, type: caseType, pointer: payloadPtr)
                    }
                    return nil
                })
        default:
            fatalError("TODO: loading \"\(type)\" is unimplemented")
        }
    }

    static func loadList<Loading: CanonicalLoading>(
        loading: Loading, pointer: Loading.Pointer
    ) -> (buffer: Loading.Operand, length: Loading.Operand) {
        let buffer = loading.loadUInt32(at: pointer)
        let length = loading.loadUInt32(at: pointer.advanced(by: 4))
        return (buffer, length)
    }

    static func loadVariantDiscriminant<Loading: CanonicalLoading>(
        pointer: Loading.Pointer, numberOfCases: Int, loading: Loading
    ) -> Loading.Operand {
        let discriminantType = CanonicalABI.discriminantType(numberOfCases: UInt32(numberOfCases))
        let discriminant: Loading.Operand
        switch discriminantType {
        case .u8: discriminant = loading.loadUInt8(at: pointer)
        case .u16: discriminant = loading.loadUInt16(at: pointer)
        case .u32: discriminant = loading.loadUInt32(at: pointer)
        }
        return discriminant
    }
}