11import BaseDigits
22
3+ /// A namespace for base-16 utilities.
34public
4- enum Base16
5+ enum Base16
56{
7+ /// Decodes some ``String``-like type containing an ASCII-encoded base-16 string
8+ /// to some ``RangeReplaceableCollection`` type. The order of the decoded bytes
9+ /// in the output matches the order of the (pairs of) hexadecimal digits in the
10+ /// input string.
11+ ///
12+ /// Characters (including UTF-8 continuation bytes) that are not base-16 digits
13+ /// will be interpreted as zeros. If the string does not contain an even number
14+ /// of digits, the trailing digit will be ignored.
615 @inlinable public static
716 func decode< ASCII, Bytes> ( _ ascii: ASCII , to _: Bytes . Type = Bytes . self) -> Bytes
817 where Bytes: RangeReplaceableCollection , Bytes. Element == UInt8 ,
918 ASCII: StringProtocol
1019 {
1120 self . decode ( ascii. utf8, to: Bytes . self)
1221 }
22+ /// Decodes an ASCII-encoded base-16 string to some ``RangeReplaceableCollection`` type.
23+ /// The order of the decoded bytes in the output matches the order of the (pairs of)
24+ /// hexadecimal digits in the input.
25+ ///
26+ /// Characters (including UTF-8 continuation bytes) that are not base-16 digits
27+ /// will be interpreted as zeros. If the input does not yield an even number of
28+ /// digits, the trailing digit will be ignored.
1329 @inlinable public static
1430 func decode< ASCII, Bytes> ( _ ascii: ASCII , to _: Bytes . Type = Bytes . self) -> Bytes
1531 where Bytes: RangeReplaceableCollection , Bytes. Element == UInt8 ,
@@ -25,7 +41,7 @@ enum Base16
2541 }
2642 return bytes
2743 }
28-
44+ /// Encodes a sequence of bytes to a base-16 string with the specified lettercasing.
2945 @inlinable public static
3046 func encode< Bytes, Digits> ( _ bytes: Bytes , with _: Digits . Type ) -> String
3147 where Bytes: Sequence , Bytes. Element == UInt8 , Digits: BaseDigits
@@ -42,6 +58,12 @@ enum Base16
4258}
4359extension Base16
4460{
61+ /// Decodes an ASCII-encoded base-16 string into a pre-allocated buffer,
62+ /// returning [`nil`]() if the input did not yield enough bytes to fill
63+ /// the buffer completely.
64+ ///
65+ /// Characters (including UTF-8 continuation bytes) that are not base-16 digits
66+ /// will be interpreted as zeros.
4567 @inlinable public static
4668 func decode< ASCII> ( _ ascii: ASCII ,
4769 into bytes: UnsafeMutableRawBufferPointer ) -> Void ?
@@ -62,6 +84,13 @@ extension Base16
6284 }
6385 return ( )
6486 }
87+ /// Encodes a sequence of bytes into a pre-allocated buffer as a base-16
88+ /// string with the specified lettercasing.
89+ ///
90+ /// The size of the `ascii` buffer must be exactly twice the inline size
91+ /// of `words`. If this method is used incorrectly, the output buffer may
92+ /// be incompletely initialized, but it will never write to memory outside
93+ /// of the buffer’s bounds.
6594 @inlinable public static
6695 func encode< BigEndian, Digits> ( storing words: BigEndian ,
6796 into ascii: UnsafeMutableRawBufferPointer ,
@@ -72,21 +101,22 @@ extension Base16
72101 {
73102 assert ( 2 * $0. count <= ascii. count)
74103
75- var offset : Int = ascii . startIndex
76- for byte : UInt8 in $0
104+ for ( offset, byte ) : ( Int , UInt8 )
105+ in zip ( stride ( from : ascii . startIndex , to : ascii . endIndex , by : 2 ) , $0 )
77106 {
78- ascii [ offset] = Digits [ byte >> 4 ]
79- ascii. formIndex ( after: & offset)
80- ascii [ offset] = Digits [ byte & 0x0f ]
81- ascii. formIndex ( after: & offset)
107+ ascii [ offset ] = Digits [ byte >> 4 ]
108+ ascii [ offset + 1 ] = Digits [ byte & 0x0f ]
82109 }
83110 }
84111 }
85112}
86113extension Base16
87- {
114+ {
88115 #if swift(>=5.6)
89- @inlinable public static
116+ /// Decodes an ASCII-encoded base-16 string to some (usually trivial) type.
117+ /// This is essentially the same as loading values from raw memory, so this
118+ /// method should only be used to load trivial types.
119+ @inlinable public static
90120 func decode< ASCII, BigEndian> ( _ ascii: ASCII ,
91121 loading _: BigEndian . Type = BigEndian . self) -> BigEndian ?
92122 where ASCII: Sequence , ASCII. Element == UInt8
@@ -116,13 +146,19 @@ extension Base16
116146 }
117147 #endif
118148
119-
149+ /// Encodes the raw bytes of the given value to a base-16 string with the
150+ /// specified lettercasing. The bytes with the lowest addresses appear first
151+ /// in the encoded output.
152+ ///
153+ /// This method is slightly faster than calling ``encode(_:with:)`` on an
154+ /// unsafe buffer-pointer view of `words`.
120155 @inlinable public static
121156 func encode< BigEndian, Digits> ( storing words: BigEndian ,
122157 with _: Digits . Type ) -> String
123158 where Digits: BaseDigits
124159 {
125160 let bytes : Int = 2 * MemoryLayout< BigEndian> . size
161+
126162 #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
127163 if #available( macOS 11 . 0 , iOS 14 . 0 , tvOS 14 . 0 , watchOS 14 . 0 , * )
128164 {
@@ -134,39 +170,26 @@ extension Base16
134170 return bytes
135171 }
136172 }
137- else
138- {
139- return . init(
140- decoding: try Self . encode ( storing: words, to: [ UInt8 ] . self, with: Digits . self) ,
141- as: Unicode . UTF8. self)
142- }
143- #elseif swift(>=5.4)
144- return . init( unsafeUninitializedCapacity: bytes)
145- {
146- Self . encode ( storing: words,
147- into: UnsafeMutableRawBufferPointer . init ( $0) ,
148- with: Digits . self)
149- return bytes
150- }
151- #else
173+ #endif
174+
175+ #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || swift(<5.4)
152176 return . init(
153- decoding: try Self . encode ( storing: words, to: [ UInt8 ] . self, with: Digits . self) ,
177+ decoding: [ UInt8 ] . init ( unsafeUninitializedCapacity: bytes)
178+ {
179+ Self . encode ( storing: words,
180+ into: UnsafeMutableRawBufferPointer . init ( $0) ,
181+ with: Digits . self)
182+ $1 = bytes
183+ } ,
154184 as: Unicode . UTF8. self)
155- #endif
156- }
157-
158- @inlinable public static
159- func encode< BigEndian, Digits> ( storing words: BigEndian , to _: [ UInt8 ] . Type,
160- with _: Digits . Type ) -> [ UInt8 ]
161- where Digits: BaseDigits
162- {
163- let bytes : Int = 2 * MemoryLayout< BigEndian> . size
185+ #else
164186 return . init( unsafeUninitializedCapacity: bytes)
165187 {
166188 Self . encode ( storing: words,
167189 into: UnsafeMutableRawBufferPointer . init ( $0) ,
168190 with: Digits . self)
169- $1 = bytes
191+ return bytes
170192 }
193+ #endif
171194 }
172195}
0 commit comments