Add a Capsule protocol implementation#181
Conversation
Motivation: The Capsule protocol is used across a few RFCs. It defines a simple TLC framing. Type and length use the QUIC variable length integer encoding. Modifications: * Add a variable length integer type. * Add types for Capsules. Encoding and decoding APIs are based on spans. Results: Capsules.
FranzBusch
left a comment
There was a problem hiding this comment.
I think adding the currency type in general is good but this also adds a new way to do binary serialization which is something we haven't agreed yet how to do.
| /// A capsule type code, as defined in RFC 9297, Section 3.2. | ||
| /// | ||
| /// Capsule types occupy an open 62-bit IANA registry. |
There was a problem hiding this comment.
Could we add a bit more here to describe what the capsule type is for?
There was a problem hiding this comment.
Can we split each type into a file and can we move all capsule files into a subfolder please
| /// A capsule type code, as defined in RFC 9297, Section 3.2. | ||
| /// | ||
| /// Capsule types occupy an open 62-bit IANA registry. | ||
| public struct CapsuleType: Sendable, Hashable { |
There was a problem hiding this comment.
Can we nest this in Capsule as Capsule.Type please
There was a problem hiding this comment.
Type is probably reserved
There was a problem hiding this comment.
I was trying to avoid Capsule.`Type` .
| /// The numeric type code. | ||
| /// | ||
| /// Must be in the range `0 ... 2^62 - 1`. | ||
| public var code: UInt64 |
There was a problem hiding this comment.
I wasn't able to find this being described as code anywhere. What about rawValue instead?
| /// ``value`` is a borrowed view into the bytes the capsule was decoded from. | ||
| /// The value's internal structure is defined by ``type``. | ||
| @available(anyAppleOS 26.0, *) | ||
| public struct Capsule: ~Escapable { |
There was a problem hiding this comment.
Why is this ~Escapable? That seems like an interesting choice and something we need to discuss a bit more in my opinion. Making these currency types ~Escapable makes them very hard to use.
There was a problem hiding this comment.
I like it because the capsule itself is more of an envelope and the value either needs further processing or could be written into an Array. I'll move away from ~Escapable for now and use an Array instead of a Span.
| /// - Returns: The `HeaderInformation` for capsule type or | ||
| /// `nil` if `input` does not yet contain a complete type and | ||
| /// length. | ||
| public static func decodeHeader(from input: inout Span<UInt8>) -> HeaderInformation? { |
There was a problem hiding this comment.
I think this goes a bit further than just adding a currency type but it actually adds serialization. Why don't we add a dependency to swift-binary-parsing and conform this type to the protocol for deserialization from there?
| public struct CapsuleType: Sendable, Hashable { | ||
| /// The numeric type code. | ||
| /// | ||
| /// Must be in the range `0 ... 2^62 - 1`. |
There was a problem hiding this comment.
You should enforce this invariant: either by making this a let and precondition-ing in the initor by usingwillSet/didSet` and doing similar.
| } | ||
|
|
||
| /// Header information for a Capsule. | ||
| public struct HeaderInformation { |
There was a problem hiding this comment.
Is this init deliberately not public? Any reason for this to not be Sendable/Hashable?
| /// The two most significant bits of the first byte select the encoded length | ||
| /// (1, 2, 4, or 8 bytes); the remaining bits hold the value in network byte | ||
| /// order. | ||
| public enum VariableLengthInteger { |
There was a problem hiding this comment.
Should there be a nod to QUIC in the name here?
There was a problem hiding this comment.
How about simply QUICVariableLengthInteger?
| /// Write`value` into `output` in network byte order. | ||
| /// | ||
| /// - Precondition: `output` has enough capcity to write `value`. | ||
| private static func write<T: BinaryInteger & FixedWidthInteger>(_ value: T, into output: inout OutputSpan<UInt8>) { |
There was a problem hiding this comment.
Should this be an extension in OutputSpan<UInt8>?
|
The parser requires the entire capsule to be in memory as one contiguous piece. Since capsules have a 62-bit length, should we allow discontiguous chunks and potentially stream the body? |
* Move Capsule and CapsuleType into separate files * Extend CapsuleType docs * Use rawValue instead of code in CapsuleType
|
My intention was to offer the header-related functions for this purpose, so the header (type + length) can be written or read independently. The value is just bytes with a meaning defined by the capsule's type, so there is no specific serialization step. This still leaves the user with the requirement to adhere to the value length of the header. |
Motivation:
The Capsule protocol is used across a few RFCs. It defines a simple TLV framing. Type and length use the QUIC variable length integer encoding.
Modifications:
Encoding and decoding APIs are based on spans.
Results:
Capsules.