5
5
* ** Status** : Implemented in Kotlin 1.9.0
6
6
* ** Prototype** : Implemented
7
7
* ** Target issue** : [ KT-57762] ( https://youtrack.jetbrains.com/issue/KT-57762/ )
8
- * ** Discussion** : TBD
8
+ * ** Discussion** : [ KEEP-362 ] ( https://github.com/Kotlin/KEEP/issues/362 )
9
9
10
10
## Summary
11
11
@@ -63,6 +63,7 @@ For formatting a numeric value:
63
63
* The prefix of the hex representation
64
64
* The suffix of the hex representation
65
65
* Whether to remove leading zeros in the hex representation
66
+ * The minimum number of hexadecimal digits to be used in the hex representation
66
67
67
68
For formatting ` ByteArray ` :
68
69
* Whether upper case or lower case hexadecimal digits should be used
@@ -116,26 +117,33 @@ public class HexFormat internal constructor(
116
117
public class NumberHexFormat internal constructor(
117
118
val prefix: String,
118
119
val suffix: String,
119
- val removeLeadingZeros: Boolean
120
+ val removeLeadingZeros: Boolean,
121
+ val minLength: Int
120
122
) {
121
123
122
124
public class Builder internal constructor() {
123
125
var prefix: String = ""
124
126
var suffix: String = ""
125
127
var removeLeadingZeros: Boolean = false
128
+ var minLength: Int = 1
126
129
}
127
130
}
128
131
}
129
132
```
130
133
131
134
` BytesHexFormat ` and ` NumberHexFormat ` classes hold format options for ` ByteArray ` and numeric values, correspondingly.
132
- ` upperCase ` option, which is common to both ` ByteArray ` and numeric values, is stored in ` HexFormat ` .
135
+ The ` upperCase ` option, which is common to both ` ByteArray ` and numeric values, is stored in ` HexFormat ` .
133
136
134
137
It's not possible to instantiate a ` HexFormat ` or its builder directly. The following function is provided instead:
135
138
```
136
139
public inline fun HexFormat(builderAction: HexFormat.Builder.() -> Unit): HexFormat
137
140
```
138
141
142
+ Additionally, two predefined ` HexFormat ` instances are provided for convenience:
143
+ * ` HexFormat.Default ` - the hexadecimal format with all options set to their default values.
144
+ * ` HexFormat.UpperCase ` - the hexadecimal format with all options set to their default values,
145
+ except for the ` upperCase ` option, which is set to ` true ` .
146
+
139
147
### Formatting
140
148
141
149
For formatting, the following extension functions are proposed:
@@ -145,7 +153,7 @@ public fun ByteArray.toHexString(format: HexFormat = HexFormat.Default): String
145
153
146
154
public fun ByteArray.toHexString(
147
155
startIndex: Int = 0,
148
- endIndex: Int = size,
156
+ endIndex: Int = this. size,
149
157
format: HexFormat = HexFormat.Default
150
158
): String
151
159
@@ -154,27 +162,58 @@ public fun ByteArray.toHexString(
154
162
public fun N.toHexString(format: HexFormat = HexFormat.Default): String
155
163
```
156
164
165
+ When formatting a byte array, one can assume the following steps:
166
+ 1 . The bytes are split into lines with ` bytesPerLine ` bytes in each line,
167
+ except for the last line, which may have fewer bytes.
168
+ 2 . Each line is split into groups with ` bytesPerGroup ` bytes in each group,
169
+ except for the last group in a line, which may have fewer bytes.
170
+ 3 . All bytes are converted to their two-digit hexadecimal representation,
171
+ each prefixed by ` bytePrefix ` and suffixed by ` byteSuffix ` .
172
+ The ` upperCase ` option determines the case (` A-F ` or ` a-f ` ) of the hexadecimal digits.
173
+ 4 . Adjacent formatted bytes within each group are separated by ` byteSeparator ` .
174
+ 5 . Adjacent groups within each line are separated by ` groupSeparator ` .
175
+ 6 . Adjacent lines are separated by the line feed (LF) character ` '\n' ` .
176
+
177
+ When formatting a numeric value, the result consists of a ` prefix ` string,
178
+ the hex representation of the numeric value, and a ` suffix ` string.
179
+ The hex representation of a value is calculated by mapping each four-bit chunk of its binary representation
180
+ to the corresponding hexadecimal digit, starting with the most significant bits.
181
+ The ` upperCase ` option determines the case of the hexadecimal digits (` A-F ` or ` a-f ` ).
182
+ If the ` removeLeadingZeros ` option is ` true ` and the hex representation is longer than ` minLength ` ,
183
+ leading zeros are removed until the length matches ` minLength ` . However, if ` minLength ` exceeds the length of the
184
+ hex representation, ` removeLeadingZeros ` is ignored, and zeros are added to the start of the representation to
185
+ achieve the specified ` minLength ` .
186
+
157
187
### Parsing
158
188
159
- It is critical to be able to parse the results of the formatting functions above.
189
+ It is critical to be able to parse the results of the formatting functions mentioned above.
160
190
For parsing, the following extension functions are proposed:
161
191
```
162
- // Parses a byte array
192
+ // Parses a byte array using the options from HexFormat.bytes
163
193
public fun String.hexToByteArray(format: HexFormat = HexFormat.Default): ByteArray
164
194
165
- // Parses a numeric value
166
- // N is Byte, Short, Int, Long, and their unsigned counterparts
195
+ // Parses a numeric value using the options from HexFormat.number
196
+ // N represents Byte, Short, Int, Long, and their unsigned counterparts
167
197
public fun String.hexToN(format: HexFormat = HexFormat.Default): N
168
198
```
169
199
170
- ## Contracts
171
-
172
- * When formatting a ` ByteArray ` , the LF character is used to separate lines.
173
- * When parsing a ` ByteArray ` , any of the char sequences CRLF (` "\r\n" ` ), LF (` "\n" ` ) and CR (` "\r" ` ) are considered a valid line separator.
174
- * Parsing is performed in a case-insensitive manner.
175
- * ` NumberHexFormat.removeLeadingZeros ` is ignored when parsing.
176
- * Assigning a non-positive value to ` BytesHexFormat.Builder.bytesPerLine/bytesPerGroup ` is prohibited.
177
- In this case ` IllegalArgumentException ` is thrown.
200
+ When parsing, the input string must conform to the structure defined by the specified format options.
201
+ However, parsing is somewhat lenient:
202
+ * For byte arrays:
203
+ * Parsing is performed in a case-insensitive manner for both the hexadecimal digits and the format elements
204
+ (prefix, suffix, separators) defined in the ` HexFormat.bytes ` property.
205
+ * Any of the char sequences CRLF (` "\r\n" ` ), LF (` "\n" ` ) and CR (` "\r" ` ) is considered a valid line separator.
206
+ * For numeric values:
207
+ * Parsing is performed in a case-insensitive manner for both the hexadecimal digits and the format elements
208
+ (prefix, suffix) defined in the ` HexFormat.number ` property.
209
+ * The ` removeLeadingZeros ` and ` minLength ` options are ignored.
210
+ However, the input string must contain at least one hexadecimal digit between the ` prefix ` and ` suffix ` .
211
+ If the number of hexadecimal digits exceeds the capacity of the type being parsed, based on its bit size,
212
+ the excess leading digits must be zeros.
213
+
214
+ ### Contracts
215
+ * Assigning a non-positive value to ` BytesHexFormat.Builder.bytesPerLine/bytesPerGroup `
216
+ and ` NumberHexFormat.Builder.minLength ` is prohibited. In this case ` IllegalArgumentException ` is thrown.
178
217
* Assigning a string containing LF or CR character to ` BytesHexFormat.Builder.byteSeparator/bytePrefix/byteSuffix `
179
218
and ` NumberHexFormat.Builder.prefix/suffix ` is prohibited. In this case ` IllegalArgumentException ` is thrown.
180
219
@@ -303,11 +342,6 @@ Only a subset of Kotlin Standard Library available on all supported platforms is
303
342
304
343
## Future advancements
305
344
306
- * Adding the ability to limit the number of hex digits when formatting numeric values
307
- * ` NumberHexFormat.maxLength ` could be introduced
308
- * When formatting an ` Int ` , combination of ` maxLength = 6 ` and ` removeLeadingZeros = false ` results to exactly 6 least significant hex digits
309
- * Combination of ` maxLength = 6 ` and ` removeLeadingZeros = true ` returns at most 6 hex (least-significant) digits without leading zeros
310
- * Related request: [ KT-60787] ( https://youtrack.jetbrains.com/issue/KT-60787 )
311
345
* Overloads for parsing a substring: [ KT-58277] ( https://youtrack.jetbrains.com/issue/KT-58277 )
312
346
* Overloads for appending format result to an ` Appendable `
313
347
* ` toHexString ` might need to be renamed to ` hexToString/Appendable ` or ` hexifyToString/Appendable ` , because
0 commit comments