File: RelativeDistinguishedName.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 (203 lines) | stat: -rw-r--r-- 7,981 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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftCertificates open source project
//
// Copyright (c) 2022-2023 Apple Inc. and the SwiftCertificates project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftCertificates project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import SwiftASN1
import _CertificateInternals

/// A ``RelativeDistinguishedName`` is a collection of elements at a single level of a hierarchical
/// ``DistinguishedName``.
///
/// Distinguished names are a remnant of the X.500 directory system. In that system,
/// distinguished names were the primary key, enabling the identification of a specific entity
/// within the directory.
///
/// ``RelativeDistinguishedName``s are the elements of a ``DistinguishedName``. Each ``RelativeDistinguishedName``
/// contains one or more ``RelativeDistinguishedName/Attribute`` which are considered equivalent: that is, they
/// are each a representation of the same piece of information.
///
/// ``RelativeDistinguishedName``s are organised into a hierarchy in a ``DistinguishedName``, in order from least
/// to most specific. In almost all current use-cases a ``RelativeDistinguishedName`` will contain only a single
/// ``Attribute``.
///
/// Note that ``RelativeDistinguishedName`` does not have a stable ordering of its elements. Inserting an element
/// at index `i` does not guarantee it will remain at that location. As a result, ``RelativeDistinguishedName`` is
/// not a `MutableCollection`.
public struct RelativeDistinguishedName {
    @usableFromInline
    var attributes: _TinyArray<Attribute>

    /// Construct a ``RelativeDistinguishedName`` from a sequence of ``Attribute``.
    ///
    /// - Parameter attributes: The sequence of ``Attribute``s that make up the ``DistinguishedName``.
    @inlinable
    public init<AttributeSequence: Sequence>(_ attributes: AttributeSequence)
    where AttributeSequence.Element == RelativeDistinguishedName.Attribute {
        self.attributes = .init(attributes)
        Self._sortElements(&self.attributes)
    }

    /// Construct a ``RelativeDistinguishedName`` from a sequence of ``Attribute``.
    ///
    /// - Parameter attributes: The sequence of ``Attribute``s that make up the ``DistinguishedName``.
    @inlinable
    public init(_ attribute: Attribute) {
        self.init(CollectionOfOne(attribute))
    }

    @inlinable
    init(_ attributes: DER.LazySetOfSequence<Attribute>) throws {
        self.attributes = try .init(attributes)
        Self._sortElements(&self.attributes)
    }

    /// Create an empty ``RelativeDistinguishedName``.
    @inlinable
    public init() {
        self.attributes = .init()
    }
}

extension RelativeDistinguishedName: Hashable {}

extension RelativeDistinguishedName: Sendable {}

extension RelativeDistinguishedName: RandomAccessCollection {
    @inlinable
    public var startIndex: Int {
        self.attributes.startIndex
    }

    @inlinable
    public var endIndex: Int {
        self.attributes.endIndex
    }

    @inlinable
    public subscript(position: Int) -> RelativeDistinguishedName.Attribute {
        get {
            self.attributes[position]
        }
    }

    /// Insert a new ``Attribute`` into this ``RelativeDistinguishedName``.
    ///
    /// - Parameter attribute: The ``Attribute`` to insert.
    @inlinable
    public mutating func insert(_ attribute: RelativeDistinguishedName.Attribute) {
        self.attributes.append(attribute)
        Self._sortElements(&self.attributes)
    }

    /// Insert a `Collection` of ``Attribute``s into this ``RelativeDistinguishedName``.
    ///
    /// Note that the order of `attributes` will not be preserved.
    ///
    /// - Parameter attributes: The ``Attribute``s to be inserted.
    @inlinable
    public mutating func insert<Attributes: Collection>(contentsOf attributes: Attributes)
    where Attributes.Element == RelativeDistinguishedName.Attribute {
        self.attributes.append(contentsOf: attributes)
        Self._sortElements(&self.attributes)
    }

    /// Removes and returns the ``Attribute`` at the specified position.
    ///
    /// - Parameter index: The position of the ``Attribute`` to remove.
    /// - Returns: The ``Attribute`` at the specified index.
    @inlinable
    @discardableResult
    public mutating func remove(at index: Int) -> Element {
        self.attributes.remove(at: index)
        // removing an element doesn't change the order and therefore sorting is not required
    }

    /// Removes all the ``Attribute``s that satisfy the given predicate.
    /// - Parameter shouldBeRemoved: A closure that takes an ``Attribute`` of the
    ///   ``RelativeDistinguishedName`` as its argument and returns a Boolean value indicating
    ///   whether the ``Attribute`` should be removed from the ``RelativeDistinguishedName``.
    @inlinable
    public mutating func removeAll(where shouldBeRemoved: (Attribute) throws -> Bool) rethrows {
        try self.attributes.removeAll(where: shouldBeRemoved)
        // removing elements doesn't change the order and therefore sorting is not required
    }
}

extension RelativeDistinguishedName: CustomStringConvertible {
    @inlinable
    public var description: String {
        self.lazy.map {
            String(describing: $0)
        }.joined(separator: "+")
    }
}

extension RelativeDistinguishedName: CustomDebugStringConvertible {
    public var debugDescription: String {
        String(reflecting: String(describing: self))
    }
}

extension RelativeDistinguishedName: DERImplicitlyTaggable {
    @inlinable
    public static var defaultIdentifier: ASN1Identifier {
        .set
    }

    @inlinable
    public init(derEncoded rootNode: ASN1Node, withIdentifier identifier: ASN1Identifier) throws {
        try self.init(DER.lazySet(identifier: identifier, rootNode: rootNode))
    }

    @inlinable
    public func serialize(into coder: inout DER.Serializer, withIdentifier identifier: ASN1Identifier) throws {
        try coder.serializeSetOf(self.attributes, identifier: identifier)
    }

    @inlinable
    static func _sortElements(_ elements: inout _TinyArray<RelativeDistinguishedName.Attribute>) {
        // We keep the elements sorted at all times. This is dumb, but we assume that these objects get
        // mutated infrequently.
        // This is weird. We need to individually serialize each element, then lexicographically compare
        // them and then write them out. We could do this in place but for now let's not worry about it.
        try! elements.sort { lhs, rhs in
            var serializer = DER.Serializer()
            try serializer.serialize(lhs)
            let lhsBytes = serializer.serializedBytes

            serializer = DER.Serializer()
            try serializer.serialize(rhs)
            let rhsBytes = serializer.serializedBytes

            // Compare up to the common length lexicographically.
            for (leftByte, rightByte) in zip(lhsBytes, rhsBytes) {
                if leftByte < rightByte {
                    // true means left comes before right
                    return true
                } else if rightByte < leftByte {
                    // Right comes after left
                    return false
                }
            }

            // We got to the end of the shorter element, so all current elements are equal.
            // If lhs is shorter, it comes earlier, _unless_ all of rhs's trailing elements are zero.
            let trailing = rhsBytes.dropFirst(lhsBytes.count)
            if trailing.count == 0 || trailing.allSatisfy({ $0 == 0 }) {
                // Must return false when the two elements are equal.
                return false
            }
            return true
        }
    }
}