Skip to content

Commit 4bf4ff3

Browse files
authored
Added extensions to integrate ByteString with Kotlin stdlib APIs (#203)
Implemented integration standard library APIs with ByteStings: - Base64 - HexFormat Resolves #149
1 parent 2afd99c commit 4bf4ff3

File tree

5 files changed

+543
-0
lines changed

5 files changed

+543
-0
lines changed

bytestring/api/kotlinx-io-bytestring.api

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
public final class kotlinx/io/bytestring/Base64Kt {
2+
public static final fun decode (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;II)[B
3+
public static synthetic fun decode$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)[B
4+
public static final fun decodeIntoByteArray (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;[BIII)I
5+
public static synthetic fun decodeIntoByteArray$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;[BIIIILjava/lang/Object;)I
6+
public static final fun decodeToByteString (Lkotlin/io/encoding/Base64;Ljava/lang/CharSequence;II)Lkotlinx/io/bytestring/ByteString;
7+
public static final fun decodeToByteString (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;II)Lkotlinx/io/bytestring/ByteString;
8+
public static final fun decodeToByteString (Lkotlin/io/encoding/Base64;[BII)Lkotlinx/io/bytestring/ByteString;
9+
public static synthetic fun decodeToByteString$default (Lkotlin/io/encoding/Base64;Ljava/lang/CharSequence;IIILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString;
10+
public static synthetic fun decodeToByteString$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString;
11+
public static synthetic fun decodeToByteString$default (Lkotlin/io/encoding/Base64;[BIIILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString;
12+
public static final fun encode (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;II)Ljava/lang/String;
13+
public static synthetic fun encode$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)Ljava/lang/String;
14+
public static final fun encodeIntoByteArray (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;[BIII)I
15+
public static synthetic fun encodeIntoByteArray$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;[BIIIILjava/lang/Object;)I
16+
public static final fun encodeToAppendable (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;Ljava/lang/Appendable;II)Ljava/lang/Appendable;
17+
public static synthetic fun encodeToAppendable$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;Ljava/lang/Appendable;IIILjava/lang/Object;)Ljava/lang/Appendable;
18+
public static final fun encodeToByteArray (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;II)[B
19+
public static synthetic fun encodeToByteArray$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)[B
20+
}
21+
122
public final class kotlinx/io/bytestring/ByteString : java/lang/Comparable {
223
public static final field Companion Lkotlinx/io/bytestring/ByteString$Companion;
324
public fun <init> ([BII)V
@@ -73,6 +94,15 @@ public final class kotlinx/io/bytestring/ByteStringKt {
7394
public static final fun startsWith (Lkotlinx/io/bytestring/ByteString;[B)Z
7495
}
7596

97+
public final class kotlinx/io/bytestring/HexKt {
98+
public static final fun hexToByteString (Ljava/lang/String;Lkotlin/text/HexFormat;)Lkotlinx/io/bytestring/ByteString;
99+
public static synthetic fun hexToByteString$default (Ljava/lang/String;Lkotlin/text/HexFormat;ILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString;
100+
public static final fun toHexString (Lkotlinx/io/bytestring/ByteString;IILkotlin/text/HexFormat;)Ljava/lang/String;
101+
public static final fun toHexString (Lkotlinx/io/bytestring/ByteString;Lkotlin/text/HexFormat;)Ljava/lang/String;
102+
public static synthetic fun toHexString$default (Lkotlinx/io/bytestring/ByteString;IILkotlin/text/HexFormat;ILjava/lang/Object;)Ljava/lang/String;
103+
public static synthetic fun toHexString$default (Lkotlinx/io/bytestring/ByteString;Lkotlin/text/HexFormat;ILjava/lang/Object;)Ljava/lang/String;
104+
}
105+
76106
public abstract interface annotation class kotlinx/io/bytestring/unsafe/UnsafeByteStringApi : java/lang/annotation/Annotation {
77107
}
78108

bytestring/common/src/Base64.kt

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
/*
2+
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
3+
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
4+
*/
5+
6+
package kotlinx.io.bytestring
7+
8+
import kotlin.io.encoding.Base64
9+
import kotlin.io.encoding.Base64.Default.encode
10+
import kotlin.io.encoding.Base64.Default.encodeToByteArray
11+
import kotlin.io.encoding.ExperimentalEncodingApi
12+
13+
/**
14+
* Encodes bytes from the specified [source] byte string or its subrange.
15+
* Returns a [ByteArray] containing the resulting symbols.
16+
*
17+
* If the size of the [source] byte string or its subrange is not an integral multiple of 3,
18+
* the result is padded with `'='` to an integral multiple of 4 symbols.
19+
*
20+
* Each resulting symbol occupies one byte in the returned byte array.
21+
*
22+
* Use [encode] to get the output in string form.
23+
*
24+
* @param source the byte string to encode bytes from.
25+
* @param startIndex the beginning (inclusive) of the subrange to encode, 0 by default.
26+
* @param endIndex the end (exclusive) of the subrange to encode, size of the [source] byte string by default.
27+
*
28+
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
29+
* @throws IllegalArgumentException when `startIndex > endIndex`.
30+
*
31+
* @return a [ByteArray] with the resulting symbols.
32+
*/
33+
@ExperimentalEncodingApi
34+
public fun Base64.encodeToByteArray(source: ByteString, startIndex: Int = 0, endIndex: Int = source.size): ByteArray {
35+
return encodeToByteArray(source.getBackingArrayReference(), startIndex, endIndex)
36+
}
37+
38+
/**
39+
* Encodes bytes from the specified [source] byte string or its subrange and writes resulting symbols into the [destination] array.
40+
* Returns the number of symbols written.
41+
*
42+
* If the size of the [source] byte string or its subrange is not an integral multiple of 3,
43+
* the result is padded with `'='` to an integral multiple of 4 symbols.
44+
*
45+
* @param source the byte string to encode bytes from.
46+
* @param destination the array to write symbols into.
47+
* @param destinationOffset the starting index in the [destination] array to write symbols to, 0 by default.
48+
* @param startIndex the beginning (inclusive) of the subrange to encode, 0 by default.
49+
* @param endIndex the end (exclusive) of the subrange to encode, size of the [source] byte string by default.
50+
*
51+
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
52+
* @throws IllegalArgumentException when `startIndex > endIndex`.
53+
* @throws IndexOutOfBoundsException when the resulting symbols don't fit into the [destination] array starting at the specified [destinationOffset],
54+
* or when that index is out of the [destination] array indices range.
55+
*
56+
* @return the number of symbols written into [destination] array.
57+
*/
58+
@ExperimentalEncodingApi
59+
public fun Base64.encodeIntoByteArray(
60+
source: ByteString,
61+
destination: ByteArray,
62+
destinationOffset: Int = 0,
63+
startIndex: Int = 0,
64+
endIndex: Int = source.size
65+
): Int {
66+
return encodeIntoByteArray(source.getBackingArrayReference(), destination, destinationOffset, startIndex, endIndex)
67+
}
68+
69+
/**
70+
* Encodes bytes from the specified [source] byte string or its subrange.
71+
* Returns a string with the resulting symbols.
72+
*
73+
* If the size of the [source] byte string or its subrange is not an integral multiple of 3,
74+
* the result is padded with `'='` to an integral multiple of 4 symbols.
75+
*
76+
* Use [encodeToByteArray] to get the output in [ByteArray] form.
77+
*
78+
* @param source the byte string to encode bytes from.
79+
* @param startIndex the beginning (inclusive) of the subrange to encode, 0 by default.
80+
* @param endIndex the end (exclusive) of the subrange to encode, size of the [source] byte string by default.
81+
*
82+
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
83+
* @throws IllegalArgumentException when `startIndex > endIndex`.
84+
*
85+
* @return a string with the resulting symbols.
86+
*/
87+
@ExperimentalEncodingApi
88+
public fun Base64.encode(
89+
source: ByteString,
90+
startIndex: Int = 0,
91+
endIndex: Int = source.size
92+
): String {
93+
return encode(source.getBackingArrayReference(), startIndex, endIndex)
94+
}
95+
96+
/**
97+
* Encodes bytes from the specified [source] byte string or its subrange and appends resulting symbols to the [destination] appendable.
98+
* Returns the destination appendable.
99+
*
100+
* If the size of the [source] byte string or its subrange is not an integral multiple of 3,
101+
* the result is padded with `'='` to an integral multiple of 4 symbols.
102+
*
103+
* @param source the byte string to encode bytes from.
104+
* @param destination the appendable to append symbols to.
105+
* @param startIndex the beginning (inclusive) of the subrange to encode, 0 by default.
106+
* @param endIndex the end (exclusive) of the subrange to encode, size of the [source] byte string by default.
107+
*
108+
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
109+
* @throws IllegalArgumentException when `startIndex > endIndex`.
110+
*
111+
* @return the destination appendable.
112+
*/
113+
@ExperimentalEncodingApi
114+
public fun <A : Appendable> Base64.encodeToAppendable(
115+
source: ByteString,
116+
destination: A,
117+
startIndex: Int = 0,
118+
endIndex: Int = source.size
119+
): A {
120+
return encodeToAppendable(source.getBackingArrayReference(), destination, startIndex, endIndex)
121+
}
122+
123+
124+
/**
125+
* Decodes symbols from the specified [source] byte string or its subrange.
126+
* Returns a [ByteArray] containing the resulting bytes.
127+
*
128+
* The symbols for decoding are not required to be padded.
129+
* However, if there is a padding character present, the correct amount of padding character(s) must be present.
130+
* The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited.
131+
*
132+
* @param source the byte string to decode symbols from.
133+
* @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default.
134+
* @param endIndex the end (exclusive) of the subrange to decode, size of the [source] byte string by default.
135+
*
136+
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
137+
* @throws IllegalArgumentException when `startIndex > endIndex`.
138+
* @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding.
139+
*
140+
* @return a [ByteArray] with the resulting bytes.
141+
*/
142+
@ExperimentalEncodingApi
143+
public fun Base64.decode(source: ByteString, startIndex: Int = 0, endIndex: Int = source.size): ByteArray {
144+
return decode(source.getBackingArrayReference(), startIndex, endIndex)
145+
}
146+
147+
/**
148+
* Decodes symbols from the specified [source] char sequence or its substring.
149+
* Returns a [ByteString] containing the resulting bytes.
150+
*
151+
* The symbols for decoding are not required to be padded.
152+
* However, if there is a padding character present, the correct amount of padding character(s) must be present.
153+
* The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited.
154+
*
155+
* @param source the char sequence to decode symbols from.
156+
* @param startIndex the beginning (inclusive) of the substring to decode, 0 by default.
157+
* @param endIndex the end (exclusive) of the substring to decode, length of the [source] by default.
158+
*
159+
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] indices.
160+
* @throws IllegalArgumentException when `startIndex > endIndex`.
161+
* @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding.
162+
*
163+
* @return a [ByteArray] with the resulting bytes.
164+
*/
165+
@ExperimentalEncodingApi
166+
public fun Base64.decodeToByteString(source: CharSequence, startIndex: Int = 0, endIndex: Int = source.length): ByteString {
167+
return ByteString.wrap(decode(source, startIndex, endIndex))
168+
}
169+
170+
/**
171+
* Decodes symbols from the specified [source] byte string or its subrange and writes resulting bytes into the [destination] array.
172+
* Returns the number of bytes written.
173+
*
174+
* The symbols for decoding are not required to be padded.
175+
* However, if there is a padding character present, the correct amount of padding character(s) must be present.
176+
* The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited.
177+
*
178+
* @param source the byte string to decode symbols from.
179+
* @param destination the array to write bytes into.
180+
* @param destinationOffset the starting index in the [destination] array to write bytes to, 0 by default.
181+
* @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default.
182+
* @param endIndex the end (exclusive) of the subrange to decode, size of the [source] byte string by default.
183+
*
184+
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
185+
* @throws IllegalArgumentException when `startIndex > endIndex`.
186+
* @throws IndexOutOfBoundsException when the resulting bytes don't fit into the [destination] array starting at the specified [destinationOffset],
187+
* or when that index is out of the [destination] array indices range.
188+
* @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding.
189+
*
190+
* @return the number of bytes written into [destination] array.
191+
*/
192+
@ExperimentalEncodingApi
193+
public fun Base64.decodeIntoByteArray(
194+
source: ByteString,
195+
destination: ByteArray,
196+
destinationOffset: Int = 0,
197+
startIndex: Int = 0,
198+
endIndex: Int = source.size
199+
): Int {
200+
return decodeIntoByteArray(source.getBackingArrayReference(), destination, destinationOffset, startIndex, endIndex)
201+
}
202+
203+
/**
204+
* Decodes symbols from the specified [source] byte string or its subrange.
205+
* Returns a [ByteString] containing the resulting bytes.
206+
*
207+
* The symbols for decoding are not required to be padded.
208+
* However, if there is a padding character present, the correct amount of padding character(s) must be present.
209+
* The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited.
210+
*
211+
* @param source the byte string to decode symbols from.
212+
* @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default.
213+
* @param endIndex the end (exclusive) of the subrange to decode, size of the [source] byte string by default.
214+
*
215+
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
216+
* @throws IllegalArgumentException when `startIndex > endIndex`.
217+
* @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding.
218+
*
219+
* @return a [ByteString] with the resulting bytes.
220+
*/
221+
@ExperimentalEncodingApi
222+
public fun Base64.decodeToByteString(source: ByteArray, startIndex: Int = 0, endIndex: Int = source.size): ByteString {
223+
return ByteString.wrap(decode(source, startIndex, endIndex))
224+
}
225+
226+
/**
227+
* Decodes symbols from the specified [source] byte string or its subrange.
228+
* Returns a [ByteString] containing the resulting bytes.
229+
*
230+
* The symbols for decoding are not required to be padded.
231+
* However, if there is a padding character present, the correct amount of padding character(s) must be present.
232+
* The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited.
233+
*
234+
* @param source the byte string to decode symbols from.
235+
* @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default.
236+
* @param endIndex the end (exclusive) of the subrange to decode, size of the [source] byte string by default.
237+
*
238+
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
239+
* @throws IllegalArgumentException when `startIndex > endIndex`.
240+
* @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding.
241+
*
242+
* @return a [ByteString] with the resulting bytes.
243+
*/
244+
@ExperimentalEncodingApi
245+
public fun Base64.decodeToByteString(source: ByteString, startIndex: Int = 0, endIndex: Int = source.size): ByteString {
246+
return ByteString.wrap(decode(source.getBackingArrayReference(), startIndex, endIndex))
247+
}

bytestring/common/src/Hex.kt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
3+
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
4+
*/
5+
6+
package kotlinx.io.bytestring
7+
8+
/**
9+
* Formats bytes in this byte string using the specified [format].
10+
*
11+
* Note that only [HexFormat.upperCase] and [HexFormat.BytesHexFormat] affect formatting.
12+
*
13+
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
14+
*
15+
* @throws IllegalArgumentException if the result length is more than [String] maximum capacity.
16+
*/
17+
@ExperimentalStdlibApi
18+
public fun ByteString.toHexString(format: HexFormat = HexFormat.Default): String {
19+
return getBackingArrayReference().toHexString(0, getBackingArrayReference().size, format)
20+
}
21+
22+
/**
23+
* Formats bytes in this byte string using the specified [HexFormat].
24+
*
25+
* Note that only [HexFormat.upperCase] and [HexFormat.BytesHexFormat] affect formatting.
26+
*
27+
* @param startIndex the beginning (inclusive) of the subrange to format, 0 by default.
28+
* @param endIndex the end (exclusive) of the subrange to format, size of this byte string by default.
29+
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
30+
*
31+
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this byte string indices.
32+
* @throws IllegalArgumentException when `startIndex > endIndex`.
33+
* @throws IllegalArgumentException if the result length is more than [String] maximum capacity.
34+
*/
35+
@ExperimentalStdlibApi
36+
public fun ByteString.toHexString(
37+
startIndex: Int = 0,
38+
endIndex: Int = size,
39+
format: HexFormat = HexFormat.Default
40+
): String {
41+
return getBackingArrayReference().toHexString(startIndex, endIndex, format)
42+
}
43+
44+
/**
45+
* Parses bytes from this string using the specified [HexFormat].
46+
*
47+
* Note that only [HexFormat.BytesHexFormat] affects parsing,
48+
* and parsing is performed in case-insensitive manner.
49+
* Also, any of the char sequences CRLF, LF and CR is considered a valid line separator.
50+
*
51+
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
52+
*
53+
* @throws IllegalArgumentException if this string does not comply with the specified [format].
54+
*/
55+
@ExperimentalStdlibApi
56+
public fun String.hexToByteString(format: HexFormat = HexFormat.Default): ByteString {
57+
return ByteString.wrap(hexToByteArray(format))
58+
}

0 commit comments

Comments
 (0)