Skip to content

Added extensions to integrate ByteString with Kotlin stdlib APIs #203

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions bytestring/api/kotlinx-io-bytestring.api
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
public final class kotlinx/io/bytestring/Base64Kt {
public static final fun decode (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;II)[B
public static synthetic fun decode$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)[B
public static final fun decodeIntoByteArray (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;[BIII)I
public static synthetic fun decodeIntoByteArray$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;[BIIIILjava/lang/Object;)I
public static final fun decodeToByteString (Lkotlin/io/encoding/Base64;Ljava/lang/CharSequence;II)Lkotlinx/io/bytestring/ByteString;
public static final fun decodeToByteString (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;II)Lkotlinx/io/bytestring/ByteString;
public static final fun decodeToByteString (Lkotlin/io/encoding/Base64;[BII)Lkotlinx/io/bytestring/ByteString;
public static synthetic fun decodeToByteString$default (Lkotlin/io/encoding/Base64;Ljava/lang/CharSequence;IIILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString;
public static synthetic fun decodeToByteString$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString;
public static synthetic fun decodeToByteString$default (Lkotlin/io/encoding/Base64;[BIIILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString;
public static final fun encode (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;II)Ljava/lang/String;
public static synthetic fun encode$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)Ljava/lang/String;
public static final fun encodeIntoByteArray (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;[BIII)I
public static synthetic fun encodeIntoByteArray$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;[BIIIILjava/lang/Object;)I
public static final fun encodeToAppendable (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;Ljava/lang/Appendable;II)Ljava/lang/Appendable;
public static synthetic fun encodeToAppendable$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;Ljava/lang/Appendable;IIILjava/lang/Object;)Ljava/lang/Appendable;
public static final fun encodeToByteArray (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;II)[B
public static synthetic fun encodeToByteArray$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)[B
}

public final class kotlinx/io/bytestring/ByteString : java/lang/Comparable {
public static final field Companion Lkotlinx/io/bytestring/ByteString$Companion;
public fun <init> ([BII)V
Expand Down Expand Up @@ -73,6 +94,15 @@ public final class kotlinx/io/bytestring/ByteStringKt {
public static final fun startsWith (Lkotlinx/io/bytestring/ByteString;[B)Z
}

public final class kotlinx/io/bytestring/HexKt {
public static final fun hexToByteString (Ljava/lang/String;Lkotlin/text/HexFormat;)Lkotlinx/io/bytestring/ByteString;
public static synthetic fun hexToByteString$default (Ljava/lang/String;Lkotlin/text/HexFormat;ILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString;
public static final fun toHexString (Lkotlinx/io/bytestring/ByteString;IILkotlin/text/HexFormat;)Ljava/lang/String;
public static final fun toHexString (Lkotlinx/io/bytestring/ByteString;Lkotlin/text/HexFormat;)Ljava/lang/String;
public static synthetic fun toHexString$default (Lkotlinx/io/bytestring/ByteString;IILkotlin/text/HexFormat;ILjava/lang/Object;)Ljava/lang/String;
public static synthetic fun toHexString$default (Lkotlinx/io/bytestring/ByteString;Lkotlin/text/HexFormat;ILjava/lang/Object;)Ljava/lang/String;
}

public abstract interface annotation class kotlinx/io/bytestring/unsafe/UnsafeByteStringApi : java/lang/annotation/Annotation {
}

Expand Down
247 changes: 247 additions & 0 deletions bytestring/common/src/Base64.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/

package kotlinx.io.bytestring

import kotlin.io.encoding.Base64
import kotlin.io.encoding.Base64.Default.encode
import kotlin.io.encoding.Base64.Default.encodeToByteArray
import kotlin.io.encoding.ExperimentalEncodingApi

/**
* Encodes bytes from the specified [source] byte string or its subrange.
* Returns a [ByteArray] containing the resulting symbols.
*
* If the size of the [source] byte string or its subrange is not an integral multiple of 3,
* the result is padded with `'='` to an integral multiple of 4 symbols.
*
* Each resulting symbol occupies one byte in the returned byte array.
*
* Use [encode] to get the output in string form.
*
* @param source the byte string to encode bytes from.
* @param startIndex the beginning (inclusive) of the subrange to encode, 0 by default.
* @param endIndex the end (exclusive) of the subrange to encode, size of the [source] byte string by default.
*
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
* @throws IllegalArgumentException when `startIndex > endIndex`.
*
* @return a [ByteArray] with the resulting symbols.
*/
@ExperimentalEncodingApi
public fun Base64.encodeToByteArray(source: ByteString, startIndex: Int = 0, endIndex: Int = source.size): ByteArray {
return encodeToByteArray(source.getBackingArrayReference(), startIndex, endIndex)
}

/**
* Encodes bytes from the specified [source] byte string or its subrange and writes resulting symbols into the [destination] array.
* Returns the number of symbols written.
*
* If the size of the [source] byte string or its subrange is not an integral multiple of 3,
* the result is padded with `'='` to an integral multiple of 4 symbols.
*
* @param source the byte string to encode bytes from.
* @param destination the array to write symbols into.
* @param destinationOffset the starting index in the [destination] array to write symbols to, 0 by default.
* @param startIndex the beginning (inclusive) of the subrange to encode, 0 by default.
* @param endIndex the end (exclusive) of the subrange to encode, size of the [source] byte string by default.
*
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
* @throws IllegalArgumentException when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException when the resulting symbols don't fit into the [destination] array starting at the specified [destinationOffset],
* or when that index is out of the [destination] array indices range.
*
* @return the number of symbols written into [destination] array.
*/
@ExperimentalEncodingApi
public fun Base64.encodeIntoByteArray(
source: ByteString,
destination: ByteArray,
destinationOffset: Int = 0,
startIndex: Int = 0,
endIndex: Int = source.size
): Int {
return encodeIntoByteArray(source.getBackingArrayReference(), destination, destinationOffset, startIndex, endIndex)
}

/**
* Encodes bytes from the specified [source] byte string or its subrange.
* Returns a string with the resulting symbols.
*
* If the size of the [source] byte string or its subrange is not an integral multiple of 3,
* the result is padded with `'='` to an integral multiple of 4 symbols.
*
* Use [encodeToByteArray] to get the output in [ByteArray] form.
*
* @param source the byte string to encode bytes from.
* @param startIndex the beginning (inclusive) of the subrange to encode, 0 by default.
* @param endIndex the end (exclusive) of the subrange to encode, size of the [source] byte string by default.
*
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
* @throws IllegalArgumentException when `startIndex > endIndex`.
*
* @return a string with the resulting symbols.
*/
@ExperimentalEncodingApi
public fun Base64.encode(
source: ByteString,
startIndex: Int = 0,
endIndex: Int = source.size
): String {
return encode(source.getBackingArrayReference(), startIndex, endIndex)
}

/**
* Encodes bytes from the specified [source] byte string or its subrange and appends resulting symbols to the [destination] appendable.
* Returns the destination appendable.
*
* If the size of the [source] byte string or its subrange is not an integral multiple of 3,
* the result is padded with `'='` to an integral multiple of 4 symbols.
*
* @param source the byte string to encode bytes from.
* @param destination the appendable to append symbols to.
* @param startIndex the beginning (inclusive) of the subrange to encode, 0 by default.
* @param endIndex the end (exclusive) of the subrange to encode, size of the [source] byte string by default.
*
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
* @throws IllegalArgumentException when `startIndex > endIndex`.
*
* @return the destination appendable.
*/
@ExperimentalEncodingApi
public fun <A : Appendable> Base64.encodeToAppendable(
source: ByteString,
destination: A,
startIndex: Int = 0,
endIndex: Int = source.size
): A {
return encodeToAppendable(source.getBackingArrayReference(), destination, startIndex, endIndex)
}


/**
* Decodes symbols from the specified [source] byte string or its subrange.
* Returns a [ByteArray] containing the resulting bytes.
*
* The symbols for decoding are not required to be padded.
* However, if there is a padding character present, the correct amount of padding character(s) must be present.
* The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited.
*
* @param source the byte string to decode symbols from.
* @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default.
* @param endIndex the end (exclusive) of the subrange to decode, size of the [source] byte string by default.
*
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
* @throws IllegalArgumentException when `startIndex > endIndex`.
* @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding.
*
* @return a [ByteArray] with the resulting bytes.
*/
@ExperimentalEncodingApi
public fun Base64.decode(source: ByteString, startIndex: Int = 0, endIndex: Int = source.size): ByteArray {
return decode(source.getBackingArrayReference(), startIndex, endIndex)
}

/**
* Decodes symbols from the specified [source] char sequence or its substring.
* Returns a [ByteString] containing the resulting bytes.
*
* The symbols for decoding are not required to be padded.
* However, if there is a padding character present, the correct amount of padding character(s) must be present.
* The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited.
*
* @param source the char sequence to decode symbols from.
* @param startIndex the beginning (inclusive) of the substring to decode, 0 by default.
* @param endIndex the end (exclusive) of the substring to decode, length of the [source] by default.
*
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] indices.
* @throws IllegalArgumentException when `startIndex > endIndex`.
* @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding.
*
* @return a [ByteArray] with the resulting bytes.
*/
@ExperimentalEncodingApi
public fun Base64.decodeToByteString(source: CharSequence, startIndex: Int = 0, endIndex: Int = source.length): ByteString {
return ByteString.wrap(decode(source, startIndex, endIndex))
}

/**
* Decodes symbols from the specified [source] byte string or its subrange and writes resulting bytes into the [destination] array.
* Returns the number of bytes written.
*
* The symbols for decoding are not required to be padded.
* However, if there is a padding character present, the correct amount of padding character(s) must be present.
* The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited.
*
* @param source the byte string to decode symbols from.
* @param destination the array to write bytes into.
* @param destinationOffset the starting index in the [destination] array to write bytes to, 0 by default.
* @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default.
* @param endIndex the end (exclusive) of the subrange to decode, size of the [source] byte string by default.
*
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
* @throws IllegalArgumentException when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException when the resulting bytes don't fit into the [destination] array starting at the specified [destinationOffset],
* or when that index is out of the [destination] array indices range.
* @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding.
*
* @return the number of bytes written into [destination] array.
*/
@ExperimentalEncodingApi
public fun Base64.decodeIntoByteArray(
source: ByteString,
destination: ByteArray,
destinationOffset: Int = 0,
startIndex: Int = 0,
endIndex: Int = source.size
): Int {
return decodeIntoByteArray(source.getBackingArrayReference(), destination, destinationOffset, startIndex, endIndex)
}

/**
* Decodes symbols from the specified [source] byte string or its subrange.
* Returns a [ByteString] containing the resulting bytes.
*
* The symbols for decoding are not required to be padded.
* However, if there is a padding character present, the correct amount of padding character(s) must be present.
* The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited.
*
* @param source the byte string to decode symbols from.
* @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default.
* @param endIndex the end (exclusive) of the subrange to decode, size of the [source] byte string by default.
*
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
* @throws IllegalArgumentException when `startIndex > endIndex`.
* @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding.
*
* @return a [ByteString] with the resulting bytes.
*/
@ExperimentalEncodingApi
public fun Base64.decodeToByteString(source: ByteArray, startIndex: Int = 0, endIndex: Int = source.size): ByteString {
return ByteString.wrap(decode(source, startIndex, endIndex))
}

/**
* Decodes symbols from the specified [source] byte string or its subrange.
* Returns a [ByteString] containing the resulting bytes.
*
* The symbols for decoding are not required to be padded.
* However, if there is a padding character present, the correct amount of padding character(s) must be present.
* The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited.
*
* @param source the byte string to decode symbols from.
* @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default.
* @param endIndex the end (exclusive) of the subrange to decode, size of the [source] byte string by default.
*
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices.
* @throws IllegalArgumentException when `startIndex > endIndex`.
* @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding.
*
* @return a [ByteString] with the resulting bytes.
*/
@ExperimentalEncodingApi
public fun Base64.decodeToByteString(source: ByteString, startIndex: Int = 0, endIndex: Int = source.size): ByteString {
return ByteString.wrap(decode(source.getBackingArrayReference(), startIndex, endIndex))
}
58 changes: 58 additions & 0 deletions bytestring/common/src/Hex.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/

package kotlinx.io.bytestring

/**
* Formats bytes in this byte string using the specified [format].
*
* Note that only [HexFormat.upperCase] and [HexFormat.BytesHexFormat] affect formatting.
*
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
*
* @throws IllegalArgumentException if the result length is more than [String] maximum capacity.
*/
@ExperimentalStdlibApi
public fun ByteString.toHexString(format: HexFormat = HexFormat.Default): String {
return getBackingArrayReference().toHexString(0, getBackingArrayReference().size, format)
}

/**
* Formats bytes in this byte string using the specified [HexFormat].
*
* Note that only [HexFormat.upperCase] and [HexFormat.BytesHexFormat] affect formatting.
*
* @param startIndex the beginning (inclusive) of the subrange to format, 0 by default.
* @param endIndex the end (exclusive) of the subrange to format, size of this byte string by default.
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
*
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this byte string indices.
* @throws IllegalArgumentException when `startIndex > endIndex`.
* @throws IllegalArgumentException if the result length is more than [String] maximum capacity.
*/
@ExperimentalStdlibApi
public fun ByteString.toHexString(
startIndex: Int = 0,
endIndex: Int = size,
format: HexFormat = HexFormat.Default
): String {
return getBackingArrayReference().toHexString(startIndex, endIndex, format)
}

/**
* Parses bytes from this string using the specified [HexFormat].
*
* Note that only [HexFormat.BytesHexFormat] affects parsing,
* and parsing is performed in case-insensitive manner.
* Also, any of the char sequences CRLF, LF and CR is considered a valid line separator.
*
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
*
* @throws IllegalArgumentException if this string does not comply with the specified [format].
*/
@ExperimentalStdlibApi
public fun String.hexToByteString(format: HexFormat = HexFormat.Default): ByteString {
return ByteString.wrap(hexToByteArray(format))
}
Loading