Migrate ProgressiveStringDecoder to Kotlin (#49149)

Summary:
Migrate com.facebook.react.modules.network.ProgressiveStringDecoder to Kotlin

## Changelog:

[INTERNAL]- Migrate com.facebook.react.modules.network.ProgressiveStringDecoder to Kotlin

Pull Request resolved: https://github.com/facebook/react-native/pull/49149

Test Plan:
```bash
yarn test-android
yarn android
```

Reviewed By: cortinico

Differential Revision: D69114070

Pulled By: rshest

fbshipit-source-id: 9f6bfc606be0236fb64216f47d8945c73faf1d1f
This commit is contained in:
Mateo Guzmán
2025-02-25 06:59:00 -08:00
committed by Facebook GitHub Bot
parent f7f3253998
commit dcd103e122
2 changed files with 85 additions and 88 deletions
@@ -1,88 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.modules.network;
import com.facebook.common.logging.FLog;
import com.facebook.react.common.ReactConstants;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
/**
* Class to decode encoded strings from byte array chunks. As in different encodings single
* character could take up to 4 characters byte array passed to decode could have parts of the
* characters which can't be correctly decoded.
*
* <p>This class is designed in assumption that original byte stream is correctly formatted string
* in given encoding. Otherwise some parts of the data won't be decoded.
*/
class ProgressiveStringDecoder {
private static final String EMPTY_STRING = "";
private final CharsetDecoder mDecoder;
private byte[] remainder = null;
/**
* @param charset expected charset of the data
*/
public ProgressiveStringDecoder(Charset charset) {
mDecoder = charset.newDecoder();
}
/**
* Parses data to String If there is a partial multi-byte symbol on the edge of the String it get
* saved to the reminder and added to the string on the decodeNext call.
*
* @param data
* @return
*/
public String decodeNext(byte[] data, int length) {
byte[] decodeData;
if (remainder != null) {
decodeData = new byte[remainder.length + length];
System.arraycopy(remainder, 0, decodeData, 0, remainder.length);
System.arraycopy(data, 0, decodeData, remainder.length, length);
length += remainder.length;
} else {
decodeData = data;
}
ByteBuffer decodeBuffer = ByteBuffer.wrap(decodeData, 0, length);
CharBuffer result = null;
boolean decoded = false;
int remainderLength = 0;
while (!decoded && (remainderLength < 4)) {
try {
result = mDecoder.decode(decodeBuffer);
decoded = true;
} catch (CharacterCodingException e) {
remainderLength++;
decodeBuffer = ByteBuffer.wrap(decodeData, 0, length - remainderLength);
}
}
boolean hasRemainder = decoded && remainderLength > 0;
if (hasRemainder) {
remainder = new byte[remainderLength];
System.arraycopy(decodeData, length - remainderLength, remainder, 0, remainderLength);
} else {
remainder = null;
}
if (!decoded) {
FLog.w(ReactConstants.TAG, "failed to decode string from byte array");
return EMPTY_STRING;
} else {
return new String(result.array(), 0, result.length());
}
}
}
@@ -0,0 +1,85 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.modules.network
import com.facebook.common.logging.FLog
import com.facebook.react.common.ReactConstants
import java.nio.ByteBuffer
import java.nio.CharBuffer
import java.nio.charset.Charset
import java.nio.charset.CharsetDecoder
/**
* Class to decode encoded strings from byte array chunks. As in different encodings single
* character could take up to 4 characters byte array passed to decode could have parts of the
* characters which can't be correctly decoded.
*
* This class is designed in assumption that original byte stream is correctly formatted string in
* given encoding. Otherwise some parts of the data won't be decoded.
*/
internal class ProgressiveStringDecoder(charset: Charset) {
private val decoder: CharsetDecoder = charset.newDecoder()
private var remainder: ByteArray? = null
/**
* Parses data to String If there is a partial multi-byte symbol on the edge of the String it get
* saved to the reminder and added to the string on the decodeNext call.
*
* @param data The byte array containing the encoded string data.
* @param initialLength The number of valid bytes in the provided data array that should be
* decoded.
* @return The decoded string. If decoding fails, an empty string is returned.
*/
fun decodeNext(data: ByteArray, initialLength: Int): String {
var length = initialLength
var decodeData: ByteArray = data
remainder?.let { r ->
decodeData = ByteArray(r.size + length)
System.arraycopy(r, 0, decodeData, 0, r.size)
System.arraycopy(data, 0, decodeData, r.size, length)
length += r.size
}
var decodeBuffer = ByteBuffer.wrap(decodeData, 0, length)
var result: CharBuffer? = null
var decoded = false
var remainderLength = 0
while (!decoded && (remainderLength < 4)) {
try {
result = decoder.decode(decodeBuffer)
decoded = true
} catch (e: CharacterCodingException) {
remainderLength++
decodeBuffer = ByteBuffer.wrap(decodeData, 0, length - remainderLength)
}
}
val hasRemainder = decoded && remainderLength > 0
if (hasRemainder) {
remainder =
ByteArray(remainderLength).apply {
System.arraycopy(decodeData, length - remainderLength, this, 0, remainderLength)
}
} else {
remainder = null
}
if (!decoded) {
FLog.w(ReactConstants.TAG, "failed to decode string from byte array")
return EMPTY_STRING
} else {
return result?.let { String(it.array(), 0, it.length) } ?: EMPTY_STRING
}
}
companion object {
private const val EMPTY_STRING = ""
}
}