Merge pull request #12405 from thingsboard/tbel_new_bytes_parsing_methods

tbel: add hexToBytesArray, base64ToBytesList
This commit is contained in:
Andrew Shvayka 2025-01-10 16:48:17 +02:00 committed by GitHub
commit 801c06aea0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 134 additions and 22 deletions

View File

@ -1467,6 +1467,26 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest {
assertEquals(expected, actual); assertEquals(expected, actual);
} }
// hexToBytes List or Array
@Test
public void hexToBytes_Test() throws ExecutionException, InterruptedException {
msgStr = "{}";
decoderStr = """
var validInputList = "0x01752B0367FA000500010488FFFFFFFFFFFFFFFF33";
var validInputArray = "AABBCCDDEE";
return {
"hexToBytes": hexToBytes(validInputList),
"hexToBytesArray": hexToBytesArray(validInputArray),
}
""";
Object actual = invokeScript(evalScript(decoderStr), msgStr);
LinkedHashMap<String, Object> expected = new LinkedHashMap<>();
expected.put("hexToBytes", bytesToList(new byte[]{1, 117, 43, 3, 103, -6, 0, 5, 0, 1, 4, -120, -1, -1, -1, -1, -1, -1, -1, -1, 51}));
// [-86, -69, -52, -35, -18] == new byte[]{(byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, (byte) 0xEE}
expected.put("hexToBytesArray", bytesToList(new byte[]{(byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, (byte) 0xEE}));
assertEquals( expected, actual);
}
// parseBinaryArray // parseBinaryArray
@Test @Test
public void parseBinaryArray_Test() throws ExecutionException, InterruptedException { public void parseBinaryArray_Test() throws ExecutionException, InterruptedException {
@ -1759,13 +1779,15 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest {
return { return {
"base64ToHex": base64ToHex("Kkk="), "base64ToHex": base64ToHex("Kkk="),
"bytesToBase64": bytesToBase64([42, 73]), "bytesToBase64": bytesToBase64([42, 73]),
"base64ToBytes": base64ToBytes("Kkk=") "base64ToBytes": base64ToBytes("Kkk="),
"base64ToBytesList": base64ToBytesList("AQIDBAU=")
} }
"""; """;
LinkedHashMap<String, Object> expected = new LinkedHashMap<>(); LinkedHashMap<String, Object> expected = new LinkedHashMap<>();
expected.put("base64ToHex", "2A49"); expected.put("base64ToHex", "2A49");
expected.put("bytesToBase64", "Kkk="); expected.put("bytesToBase64", "Kkk=");
expected.put("base64ToBytes", bytesToList(new byte[]{42, 73})); expected.put("base64ToBytes", bytesToList(new byte[]{42, 73}));
expected.put("base64ToBytesList", bytesToList(new byte[]{1, 2, 3, 4, 5}));
Object actual = invokeScript(evalScript(decoderStr), msgStr); Object actual = invokeScript(evalScript(decoderStr), msgStr);
assertEquals(expected, actual); assertEquals(expected, actual);
} }

View File

@ -257,6 +257,8 @@ public class TbUtils {
float.class, int.class))); float.class, int.class)));
parserConfig.addImport("hexToBytes", new MethodStub(TbUtils.class.getMethod("hexToBytes", parserConfig.addImport("hexToBytes", new MethodStub(TbUtils.class.getMethod("hexToBytes",
ExecutionContext.class, String.class))); ExecutionContext.class, String.class)));
parserConfig.addImport("hexToBytesArray", new MethodStub(TbUtils.class.getMethod("hexToBytesArray",
String.class)));
parserConfig.addImport("intToHex", new MethodStub(TbUtils.class.getMethod("intToHex", parserConfig.addImport("intToHex", new MethodStub(TbUtils.class.getMethod("intToHex",
Integer.class))); Integer.class)));
parserConfig.addImport("intToHex", new MethodStub(TbUtils.class.getMethod("intToHex", parserConfig.addImport("intToHex", new MethodStub(TbUtils.class.getMethod("intToHex",
@ -297,6 +299,8 @@ public class TbUtils {
String.class))); String.class)));
parserConfig.addImport("base64ToBytes", new MethodStub(TbUtils.class.getMethod("base64ToBytes", parserConfig.addImport("base64ToBytes", new MethodStub(TbUtils.class.getMethod("base64ToBytes",
String.class))); String.class)));
parserConfig.addImport("base64ToBytesList", new MethodStub(TbUtils.class.getMethod("base64ToBytesList",
ExecutionContext.class, String.class)));
parserConfig.addImport("bytesToBase64", new MethodStub(TbUtils.class.getMethod("bytesToBase64", parserConfig.addImport("bytesToBase64", new MethodStub(TbUtils.class.getMethod("bytesToBase64",
byte[].class))); byte[].class)));
parserConfig.addImport("bytesToHex", new MethodStub(TbUtils.class.getMethod("bytesToHex", parserConfig.addImport("bytesToHex", new MethodStub(TbUtils.class.getMethod("bytesToHex",
@ -335,7 +339,7 @@ public class TbUtils {
byte.class))); byte.class)));
parserConfig.addImport("parseByteToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseByteToBinaryArray", parserConfig.addImport("parseByteToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseByteToBinaryArray",
byte.class, int.class))); byte.class, int.class)));
parserConfig.addImport("parseByteToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseByteToBinaryArray", parserConfig.addImport("parseByteToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseByteToBinaryArray",
byte.class, int.class, boolean.class))); byte.class, int.class, boolean.class)));
parserConfig.addImport("parseBytesToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseBytesToBinaryArray", parserConfig.addImport("parseBytesToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseBytesToBinaryArray",
List.class))); List.class)));
@ -664,23 +668,16 @@ public class TbUtils {
} }
public static ExecutionArrayList<Byte> hexToBytes(ExecutionContext ctx, String value) { public static ExecutionArrayList<Byte> hexToBytes(ExecutionContext ctx, String value) {
String hex = prepareNumberString(value, true); String hex = validateAndPrepareHex(value);
if (hex == null) { byte[] data = hexToBytes(hex);
throw new IllegalArgumentException("Hex string must be not empty!");
}
int len = hex.length();
if (len % 2 > 0) {
throw new IllegalArgumentException("Hex string must be even-length.");
}
int radix = isHexadecimal(value);
if (radix != HEX_RADIX) {
throw new NumberFormatException("Value: \"" + value + "\" is not numeric or hexDecimal format!");
}
byte [] data = hexToBytes(hex);
return bytesToExecutionArrayList(ctx, data); return bytesToExecutionArrayList(ctx, data);
} }
public static byte[] hexToBytesArray(String value) {
String hex = validateAndPrepareHex(value);
return hexToBytes(hex);
}
public static List<Integer> printUnsignedBytes(ExecutionContext ctx, List<Byte> byteArray) { public static List<Integer> printUnsignedBytes(ExecutionContext ctx, List<Byte> byteArray) {
ExecutionArrayList<Integer> data = new ExecutionArrayList<>(ctx); ExecutionArrayList<Integer> data = new ExecutionArrayList<>(ctx);
for (Byte b : byteArray) { for (Byte b : byteArray) {
@ -839,6 +836,11 @@ public class TbUtils {
return Base64.getDecoder().decode(input); return Base64.getDecoder().decode(input);
} }
public static ExecutionArrayList<Byte> base64ToBytesList(ExecutionContext ctx, String input) {
byte[] bytes = Base64.getDecoder().decode(input);
return bytesToExecutionArrayList(ctx, bytes);
}
public static int parseBytesToInt(List<Byte> data) { public static int parseBytesToInt(List<Byte> data) {
return parseBytesToInt(data, 0); return parseBytesToInt(data, 0);
} }
@ -879,6 +881,48 @@ public class TbUtils {
return bb.getInt(); return bb.getInt();
} }
public static long parseBytesToUnsignedInt(byte[] data) {
return parseBytesToUnsignedInt(data, 0);
}
public static long parseBytesToUnsignedInt(byte[] data, int offset) {
return parseBytesToUnsignedInt(data, offset, validateLength(data.length, offset, BYTES_LEN_INT_MAX));
}
public static long parseBytesToUnsignedInt(byte[] data, int offset, int length) {
return parseBytesToUnsignedInt(data, offset, length, true);
}
public static long parseBytesToUnsignedInt(byte[] data, int offset, int length, boolean bigEndian) {
validationNumberByLength(data, offset, length, BYTES_LEN_INT_MAX);
ByteBuffer bb = ByteBuffer.allocate(8);
if (!bigEndian) {
bb.order(ByteOrder.LITTLE_ENDIAN);
}
bb.position(bigEndian ? 8 - length : 0);
bb.put(data, offset, length);
bb.position(0);
return bb.getLong();
}
public static long parseBytesToUnsignedInt(List<Byte> data) {
return parseBytesToUnsignedInt(data, 0);
}
public static long parseBytesToUnsignedInt(List<Byte> data, int offset) {
return parseBytesToUnsignedInt(data, offset, validateLength(data.size(), offset, BYTES_LEN_INT_MAX));
}
public static long parseBytesToUnsignedInt(List<Byte> data, int offset, int length) {
return parseBytesToUnsignedInt(data, offset, length, true);
}
public static long parseBytesToUnsignedInt(List<Byte> data, int offset, int length, boolean bigEndian) {
return parseBytesToUnsignedInt(Bytes.toArray(data), offset, length, bigEndian);
}
public static long parseBytesToLong(List<Byte> data) { public static long parseBytesToLong(List<Byte> data) {
return parseBytesToLong(data, 0); return parseBytesToLong(data, 0);
} }
@ -1304,7 +1348,7 @@ public class TbUtils {
public static byte[] parseByteToBinaryArray(byte byteValue, int binLength, boolean bigEndian) { public static byte[] parseByteToBinaryArray(byte byteValue, int binLength, boolean bigEndian) {
byte[] bins = new byte[binLength]; byte[] bins = new byte[binLength];
for (int i = 0; i < binLength; i++) { for (int i = 0; i < binLength; i++) {
if(bigEndian) { if (bigEndian) {
bins[binLength - 1 - i] = (byte) ((byteValue >> i) & 1); bins[binLength - 1 - i] = (byte) ((byteValue >> i) & 1);
} else { } else {
bins[i] = (byte) ((byteValue >> i) & 1); bins[i] = (byte) ((byteValue >> i) & 1);
@ -1435,16 +1479,32 @@ public class TbUtils {
} }
private static byte[] hexToBytes(String hex) { private static byte[] hexToBytes(String hex) {
byte [] data = new byte[hex.length()/2]; byte[] data = new byte[hex.length() / 2];
for (int i = 0; i < hex.length(); i += 2) { for (int i = 0; i < hex.length(); i += 2) {
// Extract two characters from the hex string // Extract two characters from the hex string
String byteString = hex.substring(i, i + 2); String byteString = hex.substring(i, i + 2);
// Parse the hex string to a byte // Parse the hex string to a byte
byte byteValue = (byte) Integer.parseInt(byteString, HEX_RADIX); byte byteValue = (byte) Integer.parseInt(byteString, HEX_RADIX);
// Add the byte to the ArrayList // Add the byte to the ArrayList
data[i/2] = byteValue; data[i / 2] = byteValue;
} }
return data; return data;
} }
private static String validateAndPrepareHex(String value) {
String hex = prepareNumberString(value, true);
if (hex == null) {
throw new IllegalArgumentException("Hex string must be not empty!");
}
int len = hex.length();
if (len % 2 > 0) {
throw new IllegalArgumentException("Hex string must be even-length.");
}
int radix = isHexadecimal(value);
if (radix != HEX_RADIX) {
throw new NumberFormatException("Value: \"" + value + "\" is not numeric or hexDecimal format!");
}
return hex;
}
} }

View File

@ -19,7 +19,6 @@ import com.google.common.collect.Lists;
import com.google.common.primitives.Bytes; import com.google.common.primitives.Bytes;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -36,6 +35,7 @@ import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Base64;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -787,8 +787,12 @@ public class TbUtilsTest {
public void hexToBytes_Test() { public void hexToBytes_Test() {
String input = "0x01752B0367FA000500010488FFFFFFFFFFFFFFFF33"; String input = "0x01752B0367FA000500010488FFFFFFFFFFFFFFFF33";
byte[] expected = {1, 117, 43, 3, 103, -6, 0, 5, 0, 1, 4, -120, -1, -1, -1, -1, -1, -1, -1, -1, 51}; byte[] expected = {1, 117, 43, 3, 103, -6, 0, 5, 0, 1, 4, -120, -1, -1, -1, -1, -1, -1, -1, -1, 51};
List<Byte> actual = TbUtils.hexToBytes(ctx, input); List<Byte> actualList = TbUtils.hexToBytes(ctx, input);
Assertions.assertEquals(toList(expected), actual); Assertions.assertEquals(toList(expected), actualList);
String validInput = "AABBCCDDEE";
expected = new byte[]{(byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, (byte) 0xEE};
byte[] actualBytes = TbUtils.hexToBytesArray(validInput);
Assertions.assertArrayEquals(expected, actualBytes);
try { try {
input = "0x01752B0367FA000500010488FFFFFFFFFFFFFFFF3"; input = "0x01752B0367FA000500010488FFFFFFFFFFFFFFFF3";
TbUtils.hexToBytes(ctx, input); TbUtils.hexToBytes(ctx, input);
@ -807,6 +811,12 @@ public class TbUtilsTest {
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
Assertions.assertTrue(e.getMessage().contains("Hex string must be not empty")); Assertions.assertTrue(e.getMessage().contains("Hex string must be not empty"));
} }
try {
input = null;
TbUtils.hexToBytes(ctx, input);
} catch (IllegalArgumentException e) {
Assertions.assertTrue(e.getMessage().contains("Hex string must be not empty"));
}
} }
@Test @Test
@ -1086,6 +1096,26 @@ public class TbUtilsTest {
String actual = TbUtils.hexToBase64(hex); String actual = TbUtils.hexToBase64(hex);
Assertions.assertEquals(expected, actual); Assertions.assertEquals(expected, actual);
} }
@Test
void base64ToBytesList_Test() {
String validInput = Base64.getEncoder().encodeToString(new byte[]{1, 2, 3, 4, 5});
ExecutionArrayList<Byte> actual = TbUtils.base64ToBytesList(ctx, validInput);
ExecutionArrayList<Byte> expected = new ExecutionArrayList<>(ctx);
expected.addAll(List.of((byte) 1, (byte)2, (byte)3, (byte)4, (byte)5));
Assertions.assertEquals(expected, actual);
String emptyInput = Base64.getEncoder().encodeToString(new byte[]{});
actual = TbUtils.base64ToBytesList(ctx, emptyInput);
Assertions.assertTrue(actual.isEmpty());
String invalidInput = "NotAValidBase64String";
Assertions.assertThrows(IllegalArgumentException.class, () -> {
TbUtils.base64ToBytesList(ctx, invalidInput);
});
Assertions.assertThrows(NullPointerException.class, () -> {
TbUtils.base64ToBytesList(ctx, null);
});
}
@Test @Test
public void bytesToHex_Test() { public void bytesToHex_Test() {
byte[] bb = {(byte) 0xBB, (byte) 0xAA}; byte[] bb = {(byte) 0xBB, (byte) 0xAA};