diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml
new file mode 100755
index 0000000..8c8119a
--- /dev/null
+++ b/.github/workflows/bench.yml
@@ -0,0 +1,50 @@
+name: bench
+
+on:
+ push:
+ branches:
+ - main
+ - develop
+ tags:
+ - 'v[0-9]+.[0-9]+.[0-9]+'
+ schedule:
+ - cron: '0 9 * * 4'
+ workflow_dispatch:
+
+jobs:
+ test:
+ name: bench-${{matrix.name}}
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ include:
+ - name: jdk8
+ version: 8
+ distribution: temurin
+ - name: jdk11
+ version: 11
+ distribution: temurin
+ - name: jdk16
+ version: 16
+ distribution: temurin
+ - name: jdk17
+ version: 17
+ distribution: temurin
+ steps:
+ - name: checkout-${{matrix.name}}
+ uses: actions/checkout@v2
+ - name: setup-toolchain-${{matrix.name}}
+ uses: actions/setup-java@v2
+ with:
+ java-version: ${{matrix.version}}
+ distribution: ${{matrix.distribution}}
+ cache: maven
+ - name: build-${{matrix.name}}
+ run: mvn -P benchmark clean package -B -V
+ - name: benchmark-${{matrix.name}}
+ run: java -jar target/benchmark.jar AzamCodecBench -rf json
+ - name: upload-bench-results-${{matrix.name}}
+ uses: actions/upload-artifact@v1
+ with:
+ name: azamcodec_bench_${{matrix.name}}_${{github.sha}}
+ path: ./jmh-result.json
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 00e3d94..0556190 100755
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -18,13 +18,10 @@ on:
jobs:
test:
- name: test-${{matrix.distribution}}-${{matrix.version}}
+ name: test-${{matrix.name}}
runs-on: ubuntu-latest
strategy:
matrix:
- name:
- - jdk8
- - jdk11
include:
- name: jdk8
version: 8
@@ -48,4 +45,4 @@ jobs:
distribution: ${{matrix.distribution}}
cache: maven
- name: verify-${{matrix.name}}
- run: mvn verify --settings .settings.xml -Dgpg.skip -B -V
+ run: mvn clean verify --settings .settings.xml -Dgpg.skip -B -V
diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml
index 0516007..5bcc604 100755
--- a/.github/workflows/package.yml
+++ b/.github/workflows/package.yml
@@ -34,4 +34,4 @@ jobs:
MAVEN_PASSWORD: ${{secrets.MAVEN_PASSWORD}}
MAVEN_GPG_KEYNAME: ${{secrets.MAVEN_GPG_KEYNAME}}
MAVEN_GPG_PASSPHRASE: ${{secrets.MAVEN_GPG_PASSPHRASE}}
- run: mvn package -P release --batch-mode --settings .settings.xml -DskipTests=true -DperformRelease=false -Dmaven.deploy.skip=true --update-snapshots -B -V
+ run: mvn clean package -P release --batch-mode --settings .settings.xml -DskipTests=true -DperformRelease=false -Dmaven.deploy.skip=true --update-snapshots -B -V
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 3ed1d5e..d455154 100755
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -33,7 +33,7 @@ jobs:
MAVEN_PASSWORD: ${{secrets.MAVEN_PASSWORD}}
MAVEN_GPG_KEYNAME: ${{secrets.MAVEN_GPG_KEYNAME}}
MAVEN_GPG_PASSPHRASE: ${{secrets.MAVEN_GPG_PASSPHRASE}}
- run: mvn deploy -P release --batch-mode --settings .settings.xml -DperformRelease=false --update-snapshots -B -V
+ run: mvn clean deploy -P release --batch-mode --settings .settings.xml -DperformRelease=false --update-snapshots -B -V
- name: artifacts
uses: softprops/action-gh-release@v1
with:
diff --git a/.gitignore b/.gitignore
index c8067d3..b94224c 100755
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+jmh-result.json
+
# https://github.com/github/gitignore/blob/master/Java.gitignore
# Compiled class file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100755
index 0000000..02525b0
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,16 @@
+{
+ "java.format.settings.profile": "GoogleStyle",
+ "java.format.settings.url": "./eclipse-java-google-style.xml",
+ "java.configuration.updateBuildConfiguration": "automatic",
+ "[java]": {
+ "editor.formatOnSave": true,
+ "editor.detectIndentation": false,
+ "editor.insertSpaces": true,
+ "editor.tabSize": 2
+ },
+ "[xml]": {
+ "editor.detectIndentation": false,
+ "editor.insertSpaces": true,
+ "editor.tabSize": 2
+ }
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..88cdcc3
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,37 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "clean",
+ "type": "shell",
+ "command": "mvn -B clean",
+ "group": "build"
+ },
+ {
+ "label": "format",
+ "type": "shell",
+ "command": "mvn -B formatter:format xml-format:xml-format",
+ "group": "build"
+ },
+ {
+ "label": "verify",
+ "type": "shell",
+ "command": "mvn -B verify",
+ "group": "build"
+ },
+ {
+ "label": "test",
+ "type": "shell",
+ "command": "mvn -B test",
+ "group": "test"
+ },
+ {
+ "label": "bench",
+ "type": "shell",
+ "command": "mvn -B -P benchmark package && java -jar ${workspaceFolder}/target/benchmark.jar AzamCodecBench -rf json",
+ "group": "test"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index bd144c7..1fb000f 100755
--- a/README.md
+++ b/README.md
@@ -19,27 +19,67 @@ This library is published to [Maven Central](https://central.sonatype.com/artifa
```java
// Decode to ints
-int[] ints = AzamCodec.azamDecodeAllInts("");
+int[] ints = AzamCodec.azamDecodeInts("xytxvyyfh5wgg1"); // -559038737, 21, 49153
// Decode to big endian bytes
-byte[] bytes = AzamCodec.azamDecodeAllBytes("");
+byte[][] bytes = AzamCodec.azamDecodeBytes("xytxvyyfh5wgg1"); // 0xdeadbeef, 0x15, 0xc001
```
### Encoding
```java
// Encode from ints
-String encodedInts = AzamCodec.azamEncodeInts(new int[]{});
+String encodedInts = AzamCodec.azamEncodeInts(new int[]{ -559038737, 21, 49153 }); // "xytxvyyfh5wgg1"
// Encode from bytes
-String encodedBytes = AzamCodec.azamEncodeBytes(new byte[][]{});
+String encodedBytes = AzamCodec.azamEncodeBytes(new byte[][]{ //
+ new byte[] { 0xde, 0xad, 0xbe, 0xef }, //
+ new byte[] { 0x15 }, //
+ new byte[] { 0xc0, 0x01 } //
+}); // "xytxvyyfh5wgg1"
+```
+
+### Practical example
+
+Azam Codec is designed to be a sortable identifier representation, so using it to represent multi sectioned identifier is the best example.
+
+```java
+public class RecordId {
+ int tenantId;
+ int objectId;
+ int recordId;
+
+ public RecordId(String id) throws ParseException {
+ int[] sections = AzamCodec.azamDecodeInts(id);
+ this.tenantId = sections[0];
+ this.objectId = sections[1];
+ this.recordId = sections[2];
+ }
+
+ public String toString() {
+ return AzamCodec.azamEncodeInts(new int[] { this.tenantId, this.objectId, this.recordId });
+ }
+}
```
## Development
Standard Java development method applies.
-Please run the following before sending a PR:
+Please make sure that code is correctly formatted and tests are passing before sending a PR.
-* You can format sources to match style with ```mvn formatter:format xml-format:xml-format```
- * You can also use VS Code extension
-* Make sure tests are passing and source is properly formatted by running ```mvn verify```
+### Format source code
+```sh
+mvn formatter:format xml-format:xml-format
+```
+
+### Test
+```sh
+mvn verify
+```
+
+### Benchmark
+
+```sh
+mvn -P benchmark package
+java -jar target/benchmark.jar AzamCodecBench
+```
diff --git a/pom.xml b/pom.xml
old mode 100755
new mode 100644
index 4487110..cb9601f
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
io.azam.azamcodec
azamcodec
- 0.0.1
+ 0.1.0
azamcodec
An encoder and decoder implementation in Java for Azam Codec, a lexicographically sortable multi-section base16 encoding of byte array.
https://github.com/azam/azamcodec-java
@@ -52,6 +52,18 @@
4.13.1
test
+
+ org.openjdk.jmh
+ jmh-core
+ 1.36
+ test
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ 1.36
+ test
+
@@ -73,13 +85,35 @@
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.4.0
+
+
+ add-bench-source
+ generate-test-sources
+
+ add-test-source
+
+
+
+ src/bench/java
+
+
+
+
+
org.apache.maven.plugins
maven-javadoc-plugin
2.10.4
-
+
7
+
+ target/generated-test-sources
+
@@ -191,5 +225,86 @@
+
+ benchmark
+
+ false
+
+
+ true
+ true
+
+
+
+ org.openjdk.jmh
+ jmh-core
+ 1.36
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ 1.36
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.6.0
+
+
+ ${project.basedir}/src/main/java
+ ${project.basedir}/src/bench/java
+
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ 1.36
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.4.1
+
+
+ package
+
+ shade
+
+
+
+ benchmark
+
+
+ org.openjdk.jmh.Main
+
+
+
+
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+
+
+
+
+
+
+
diff --git a/src/bench/java/io/azam/azamcodec/AzamCodecBench.java b/src/bench/java/io/azam/azamcodec/AzamCodecBench.java
new file mode 100644
index 0000000..f8ccb87
--- /dev/null
+++ b/src/bench/java/io/azam/azamcodec/AzamCodecBench.java
@@ -0,0 +1,76 @@
+package io.azam.azamcodec;
+
+import org.openjdk.jmh.annotations.*;
+import org.openjdk.jmh.infra.Blackhole;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import java.text.ParseException;
+import java.util.concurrent.TimeUnit;
+
+import static io.azam.azamcodec.AzamCodec.*;
+
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@State(Scope.Benchmark)
+@Fork(value = 1, jvmArgs = {"-Xms256M", "-Xmx256M"})
+@Warmup(iterations = 3)
+@Measurement(iterations = 10)
+public class AzamCodecBench {
+ public static void main(String[] args) throws RunnerException {
+ Options opt = new OptionsBuilder().include(AzamCodecBench.class.getSimpleName()).build();
+ new Runner(opt).run();
+ }
+
+ @Benchmark
+ public void azamDecodeInts1(Blackhole bh) throws ParseException {
+ bh.consume(azamDecodeInts("zzzzzzzf"));
+ }
+
+ @Benchmark
+ public void azamDecodeInts2(Blackhole bh) throws ParseException {
+ bh.consume(azamDecodeInts("zzzzzzzfzzzzzzzf"));
+ }
+
+ @Benchmark
+ public void azamDecodeInts3(Blackhole bh) throws ParseException {
+ bh.consume(azamDecodeInts("zzzzzzzfzzzzzzzfzzzzzzzf"));
+ }
+
+ @Benchmark
+ public void azamDecodeInts4(Blackhole bh) throws ParseException {
+ bh.consume(azamDecodeInts("zzzzzzzfzzzzzzzfzzzzzzzfzzzzzzzf"));
+ }
+
+ @Benchmark
+ public void azamDecodeInts5(Blackhole bh) throws ParseException {
+ bh.consume(azamDecodeInts("zzzzzzzfzzzzzzzfzzzzzzzfzzzzzzzfzzzzzzzf"));
+ }
+
+ @Benchmark
+ public void azamEncodeInts1(Blackhole bh) throws ParseException {
+ bh.consume(azamEncodeInts(0xffffffff));
+ }
+
+ @Benchmark
+ public void azamEncodeInts2(Blackhole bh) throws ParseException {
+ bh.consume(azamEncodeInts(0xffffffff, 0xffffffff));
+ }
+
+ @Benchmark
+ public void azamEncodeInts3(Blackhole bh) throws ParseException {
+ bh.consume(azamEncodeInts(0xffffffff, 0xffffffff, 0xffffffff));
+ }
+
+ @Benchmark
+ public void azamEncodeInts4(Blackhole bh) throws ParseException {
+ bh.consume(azamEncodeInts(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff));
+ }
+
+ @Benchmark
+ public void azamEncodeInts5(Blackhole bh) throws ParseException {
+ bh.consume(azamEncodeInts(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff));
+ }
+}
diff --git a/src/main/java/io/azam/azamcodec/AzamCodec.java b/src/main/java/io/azam/azamcodec/AzamCodec.java
index 02cfca9..bf5c1f0 100755
--- a/src/main/java/io/azam/azamcodec/AzamCodec.java
+++ b/src/main/java/io/azam/azamcodec/AzamCodec.java
@@ -10,6 +10,12 @@
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
+/**
+ * Azam Codec encoder/decoder.
+ *
+ * @author azam
+ * @since 0.0.1
+ */
public class AzamCodec {
final static int[] LOWER_ALPHABETS = new int[] { //
'0', '1', '2', '3', '4', '5', '6', '7', //
@@ -20,6 +26,14 @@ public class AzamCodec {
'r', 's', 't', 'v', 'w', 'x', 'y', 'z' //
};
+ /**
+ * Consume bytes from `input`, generates Azam Codec encoded string as bytes, and writes to
+ * `output`
+ *
+ * @param output Output stream
+ * @param input Input stream
+ * @throws IOException
+ */
public static void azamEncodeStream(OutputStream output, InputStream input) throws IOException {
int buf = -1;
int prevBuf = -1;
@@ -96,6 +110,14 @@ public static void azamEncodeStream(OutputStream output, InputStream input) thro
}
}
+ /**
+ * Consume bytes from multiple streams `inputs` sequentially, generates Azam Codec encoded string
+ * as bytes, and writes to `output`
+ *
+ * @param output Output stream
+ * @param inputs Input streams
+ * @throws IOException
+ */
public static void azamEncodeStreams(OutputStream output, InputStream... inputs)
throws IOException {
if (inputs == null)
@@ -105,99 +127,159 @@ public static void azamEncodeStreams(OutputStream output, InputStream... inputs)
throw new IllegalArgumentException("Arguments contains null value");
azamEncodeStream(output, input);
}
-
}
- public static String azamEncodeBytes(byte[]... values) throws IOException {
+ /**
+ * For each byte array `values`, generate Azam Codec encoded string section, concatenate all
+ * sections and returns the string.
+ *
+ * @param values Input byte arrays
+ * @return Azam Codec encoded string
+ */
+ public static String azamEncodeBytes(byte[]... values) {
if (values == null)
throw new IllegalArgumentException("Value is null");
- ByteArrayOutputStream output = new ByteArrayOutputStream(values.length);
- for (byte[] value : values) {
- if (value == null)
- throw new IllegalArgumentException("Value contains null value");
- ByteArrayInputStream input = new ByteArrayInputStream(value);
- azamEncodeStream(output, input);
+ try (ByteArrayOutputStream output = new ByteArrayOutputStream(values.length)) {
+ for (byte[] value : values) {
+ if (value == null)
+ throw new IllegalArgumentException("Value contains null value");
+ try (ByteArrayInputStream input = new ByteArrayInputStream(value)) {
+ azamEncodeStream(output, input);
+ }
+ }
+ // All Azam Codec characters are ASCII so this should do fine
+ return new String(output.toByteArray(), StandardCharsets.US_ASCII);
+ } catch (IOException io) {
+ // We should not get here because we are only using byte array streams,
+ // unless there are severe memory IO errors
+ throw new IllegalArgumentException("Unexpected IOException", io);
}
- // All Azam Codec characters are ASCII so this should do fine
- return new String(output.toByteArray(), StandardCharsets.US_ASCII);
}
- public static String azamEncode(Number... values) throws IOException {
+ /**
+ * For each {@link java.lang.Number} array of `values`, generate Azam Codec encoded string section
+ * based on the number's byte representation in Big-Endian, concatenate all sections and returns
+ * the string.
+ *
+ * Supported instances are:
+ *
+ *
+ * - {@link java.lang.Integer}
+ * - {@link java.lang.Long}
+ * - {@link java.math.BigInteger}
+ *
+ *
+ * @param values Input numbers
+ * @return Azam Codec encoded string
+ */
+ public static String azamEncodeNumbers(Number... values) {
if (values == null)
throw new IllegalArgumentException("Value is null");
- ByteArrayOutputStream output = new ByteArrayOutputStream(values.length);
- for (Number value : values) {
- if (value == null)
- throw new IllegalArgumentException("Value contains null");
- byte[] bytes;
- if (value instanceof Integer) {
- int data = value.intValue();
- bytes = new byte[] { //
- (byte) ((data >> 24) & 0xff), //
- (byte) ((data >> 16) & 0xff), //
- (byte) ((data >> 8) & 0xff), //
- (byte) (data & 0xff), //
- };
- } else if (value instanceof Long) {
- long data = value.longValue();
- bytes = new byte[] { //
- (byte) ((data >> 56) & 0xff), //
- (byte) ((data >> 48) & 0xff), //
- (byte) ((data >> 40) & 0xff), //
- (byte) ((data >> 32) & 0xff), //
- (byte) ((data >> 24) & 0xff), //
- (byte) ((data >> 16) & 0xff), //
- (byte) ((data >> 8) & 0xff), //
- (byte) (data & 0xff), //
- };
- } else if (value instanceof BigInteger) {
- BigInteger data = (BigInteger) value;
- bytes = data.toByteArray();
- } else {
- throw new IllegalArgumentException("Value is not a supported Number");
+ try (ByteArrayOutputStream output = new ByteArrayOutputStream(values.length)) {
+ for (Number value : values) {
+ if (value == null)
+ throw new IllegalArgumentException("Value contains null");
+ byte[] bytes;
+ if (value instanceof Integer) {
+ int data = value.intValue();
+ bytes = new byte[] { //
+ (byte) ((data >> 24) & 0xff), //
+ (byte) ((data >> 16) & 0xff), //
+ (byte) ((data >> 8) & 0xff), //
+ (byte) (data & 0xff), //
+ };
+ } else if (value instanceof Long) {
+ long data = value.longValue();
+ bytes = new byte[] { //
+ (byte) ((data >> 56) & 0xff), //
+ (byte) ((data >> 48) & 0xff), //
+ (byte) ((data >> 40) & 0xff), //
+ (byte) ((data >> 32) & 0xff), //
+ (byte) ((data >> 24) & 0xff), //
+ (byte) ((data >> 16) & 0xff), //
+ (byte) ((data >> 8) & 0xff), //
+ (byte) (data & 0xff), //
+ };
+ } else if (value instanceof BigInteger) {
+ BigInteger data = (BigInteger) value;
+ bytes = data.toByteArray();
+ } else {
+ throw new IllegalArgumentException("Value is not a supported Number");
+ }
+ try (ByteArrayInputStream input = new ByteArrayInputStream(bytes)) {
+ azamEncodeStream(output, input);
+ }
}
- ByteArrayInputStream input = new ByteArrayInputStream(bytes);
- azamEncodeStream(output, input);
+ return new String(output.toByteArray(), StandardCharsets.US_ASCII);
+ } catch (IOException io) {
+ // We should not get here because we are only using byte array streams,
+ // unless there are severe memory IO errors
+ throw new IllegalArgumentException("Unexpected IOException", io);
}
- return new String(output.toByteArray(), StandardCharsets.US_ASCII);
}
- public static String azamEncodeInts(int... values) throws IOException {
+ /**
+ * For each int array of `values`, generate Azam Codec encoded string section based on the
+ * number's byte representation in Big-Endian, concatenate all sections and returns the string.
+ *
+ * @param values Input numbers
+ * @return Azam Codec encoded string
+ */
+ public static String azamEncodeInts(int... values) {
if (values == null)
throw new IllegalArgumentException("Values are null");
- ByteArrayOutputStream output = new ByteArrayOutputStream(values.length);
- for (int value : values) {
- byte[] bytes = new byte[] { //
- (byte) ((value >> 24) & 0xff), //
- (byte) ((value >> 16) & 0xff), //
- (byte) ((value >> 8) & 0xff), //
- (byte) (value >> 0 & 0xff), //
- };
- ByteArrayInputStream input = new ByteArrayInputStream(bytes);
- azamEncodeStream(output, input);
+ try (ByteArrayOutputStream output = new ByteArrayOutputStream(values.length)) {
+ for (int value : values) {
+ byte[] bytes = new byte[] { //
+ (byte) ((value >> 24) & 0xff), //
+ (byte) ((value >> 16) & 0xff), //
+ (byte) ((value >> 8) & 0xff), //
+ (byte) (value & 0xff), //
+ };
+ try (ByteArrayInputStream input = new ByteArrayInputStream(bytes)) {
+ azamEncodeStream(output, input);
+ }
+ }
+ return new String(output.toByteArray(), StandardCharsets.US_ASCII);
+ } catch (IOException io) {
+ // We should not get here because we are only using byte array streams,
+ // unless there are severe memory IO errors
+ throw new IllegalArgumentException("Unexpected IOException", io);
}
- return new String(output.toByteArray(), StandardCharsets.US_ASCII);
}
- public static String azamEncodeLongs(long... values) throws IOException {
+ /**
+ * For each long array of `values`, generate Azam Codec encoded string section based on the
+ * number's byte representation in Big-Endian, concatenate all sections and returns the string.
+ *
+ * @param values Input numbers
+ * @return Azam Codec encoded string
+ */
+ public static String azamEncodeLongs(long... values) {
if (values == null)
throw new IllegalArgumentException("Values are null");
- ByteArrayOutputStream output = new ByteArrayOutputStream(values.length);
- for (long value : values) {
- byte[] bytes = new byte[] { //
- (byte) ((value >> 56) & 0xff), //
- (byte) ((value >> 48) & 0xff), //
- (byte) ((value >> 40) & 0xff), //
- (byte) ((value >> 32) & 0xff), //
- (byte) ((value >> 24) & 0xff), //
- (byte) ((value >> 16) & 0xff), //
- (byte) ((value >> 8) & 0xff), //
- (byte) (value & 0xff), //
- };
- ByteArrayInputStream input = new ByteArrayInputStream(bytes);
- azamEncodeStream(output, input);
+ try (ByteArrayOutputStream output = new ByteArrayOutputStream(values.length)) {
+ for (long value : values) {
+ byte[] bytes = new byte[] { //
+ (byte) ((value >> 56) & 0xff), //
+ (byte) ((value >> 48) & 0xff), //
+ (byte) ((value >> 40) & 0xff), //
+ (byte) ((value >> 32) & 0xff), //
+ (byte) ((value >> 24) & 0xff), //
+ (byte) ((value >> 16) & 0xff), //
+ (byte) ((value >> 8) & 0xff), //
+ (byte) (value & 0xff), //
+ };
+ try (ByteArrayInputStream input = new ByteArrayInputStream(bytes)) {
+ azamEncodeStream(output, input);
+ }
+ }
+ return new String(output.toByteArray(), StandardCharsets.US_ASCII);
+ } catch (IOException io) {
+ // We should not get here because we are only using byte array streams,
+ // unless there are severe memory IO errors
+ throw new IllegalArgumentException("Unexpected IOException", io);
}
- return new String(output.toByteArray(), StandardCharsets.US_ASCII);
}
final static byte getNybbleValue(final int symbol) {
@@ -301,8 +383,18 @@ final static byte getNybbleValue(final int symbol) {
}
}
- public static void azamDecodeStream(InputStream input, OutputStream output)
- throws IOException, ParseException {
+ /**
+ * Consume a single section of an Azam Codec encoded stream from `input` and write decoded bytes
+ * to `output`.
+ *
+ * @param input Input stream
+ * @param output Output stream
+ * @throws EOFException On end of a stream
+ * @throws IOException On unexpected IO exceptions
+ * @throws ParseException On invalid Azam Codec characters and/or character orders
+ */
+ public static void azamDecodeStreamSection(InputStream input, OutputStream output)
+ throws EOFException, IOException, ParseException {
// Bitshift operators recasts to integer, so we have to
// mask byte variables with 0xff before doing integer operations.
@@ -384,62 +476,132 @@ public static void azamDecodeStream(InputStream input, OutputStream output)
return;
}
- public static byte[][] azamDecodeAllBytes(String value) throws IOException, ParseException {
+ /**
+ * Decode all sections of an Azam Codec encoded string `value` as arrays of byte array.
+ *
+ * @param value Azam Codec encoded string
+ * @return Decoded value as array of byte array
+ * @throws ParseException On invalid Azam Codec characters and/or character orders
+ */
+ public static byte[][] azamDecodeBytes(String value) throws ParseException {
if (value == null)
throw new IllegalArgumentException("Argument is null");
- InputStream input = new ByteArrayInputStream(value.getBytes());
- byte[][] values = new byte[0][];
- for (int i = 0;; i++) {
- try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
- azamDecodeStream(input, output);
-
- // Extend return array
- byte[][] extended = new byte[i + 1][];
- if (values.length > 0) {
- System.arraycopy(values, 0, extended, 0, values.length);
+ try (InputStream input = new ByteArrayInputStream(value.getBytes())) {
+ byte[][] values = new byte[0][];
+ for (int i = 0;; i++) {
+ try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
+ azamDecodeStreamSection(input, output);
+
+ // Extend return array
+ byte[][] extended = new byte[i + 1][];
+ if (values.length > 0) {
+ System.arraycopy(values, 0, extended, 0, values.length);
+ }
+ extended[i] = output.toByteArray();
+ values = extended;
+ } catch (EOFException eof) {
+ // azamDecodeStream throws EOFException only on empty streams,
+ // so when we receive this, it should be the last one to ignore
+ break;
}
- extended[i] = output.toByteArray();
- values = extended;
- } catch (EOFException eof) {
- // azamDecodeStream throws EOFException only on empty streams,
- // so when we receive this, it should be the last one to ignore
- break;
}
+ return values;
+ } catch (IOException io) {
+ // We should not get here because we are only using byte array streams,
+ // unless there are severe memory IO errors
+ throw new ParseException("Unexpected IO Exception: " + io.getMessage(), -1);
}
- return values;
}
- public static int[] azamDecodeAllInts(String value) throws IOException, ParseException {
+ /**
+ * Decode all sections of an Azam Codec encoded string `value` as int array.
+ *
+ * @param value Azam Codec encoded string
+ * @return Decoded value as int array
+ * @throws ParseException On invalid Azam Codec characters and/or character orders
+ */
+ public static int[] azamDecodeInts(String value) throws ParseException {
if (value == null)
throw new IllegalArgumentException("Argument is null");
- InputStream input = new ByteArrayInputStream(value.getBytes());
- int[] values = new int[0];
- for (int i = 0;; i++) {
- try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
- azamDecodeStream(input, output);
- byte[] bytes = output.toByteArray();
-
- // BigEndian byte array to int
- if (bytes == null || bytes.length == 0 || bytes.length > Integer.BYTES)
- throw new ParseException("Encoded value is empty or too long to convert to int", -1);
- int decoded = 0;
- for (int j = 0; j < bytes.length; j++) {
- decoded = decoded << 8 | (bytes[j] & 0xff);
+ try (InputStream input = new ByteArrayInputStream(value.getBytes())) {
+ int[] values = new int[0];
+ for (int i = 0;; i++) {
+ try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
+ azamDecodeStreamSection(input, output);
+ byte[] bytes = output.toByteArray();
+
+ // BigEndian byte array to int
+ if (bytes == null || bytes.length == 0 || bytes.length > Integer.BYTES)
+ throw new ParseException("Encoded value is empty or too long to convert to int", -1);
+ int decoded = 0;
+ for (int j = 0; j < bytes.length; j++) {
+ decoded = decoded << 8 | (bytes[j] & 0xff);
+ }
+
+ // Extend return array
+ int[] extended = new int[i + 1];
+ if (values.length > 0) {
+ System.arraycopy(values, 0, extended, 0, values.length);
+ }
+ extended[i] = decoded;
+ values = extended;
+ } catch (EOFException eof) {
+ // azamDecodeStream throws EOFException only on empty streams,
+ // so when we receive this, it should be the last one to ignore
+ break;
}
+ }
+ return values;
+ } catch (IOException io) {
+ // We should not get here because we are only using byte array streams,
+ // unless there are severe memory IO errors
+ throw new ParseException("Unexpected IO Exception: " + io.getMessage(), -1);
+ }
+ }
- // Extend return array
- int[] extended = new int[i + 1];
- if (values.length > 0) {
- System.arraycopy(values, 0, extended, 0, values.length);
+ /**
+ * Decode all sections of an Azam Codec encoded string `value` as long array.
+ *
+ * @param value Azam Codec encoded string
+ * @return Decoded value as long array
+ * @throws ParseException On invalid Azam Codec characters and/or character orders
+ */
+ public static long[] azamDecodeLongs(String value) throws ParseException {
+ if (value == null)
+ throw new IllegalArgumentException("Argument is null");
+ try (InputStream input = new ByteArrayInputStream(value.getBytes())) {
+ long[] values = new long[0];
+ for (int i = 0;; i++) {
+ try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
+ azamDecodeStreamSection(input, output);
+ byte[] bytes = output.toByteArray();
+
+ // BigEndian byte array to int
+ if (bytes == null || bytes.length == 0 || bytes.length > Long.BYTES)
+ throw new ParseException("Encoded value is empty or too long to convert to int", -1);
+ long decoded = 0;
+ for (int j = 0; j < bytes.length; j++) {
+ decoded = decoded << 8 | (bytes[j] & 0xff);
+ }
+
+ // Extend return array
+ long[] extended = new long[i + 1];
+ if (values.length > 0) {
+ System.arraycopy(values, 0, extended, 0, values.length);
+ }
+ extended[i] = decoded;
+ values = extended;
+ } catch (EOFException eof) {
+ // azamDecodeStream throws EOFException only on empty streams,
+ // so when we receive this, it should be the last one to ignore
+ break;
}
- extended[i] = decoded;
- values = extended;
- } catch (EOFException eof) {
- // azamDecodeStream throws EOFException only on empty streams,
- // so when we receive this, it should be the last one to ignore
- break;
}
+ return values;
+ } catch (IOException io) {
+ // We should not get here because we are only using byte array streams,
+ // unless there are severe memory IO errors
+ throw new ParseException("Unexpected IO Exception: " + io.getMessage(), -1);
}
- return values;
}
}
diff --git a/src/test/java/io/azam/azamcodec/AzamCodecTest.java b/src/test/java/io/azam/azamcodec/AzamCodecTest.java
index 0b992da..87c97b6 100755
--- a/src/test/java/io/azam/azamcodec/AzamCodecTest.java
+++ b/src/test/java/io/azam/azamcodec/AzamCodecTest.java
@@ -2,8 +2,9 @@
import java.io.IOException;
import java.text.ParseException;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import org.junit.Assert;
import org.junit.Test;
@@ -12,39 +13,171 @@
import static io.azam.azamcodec.AzamCodec.*;
/**
- * Test class for {@link io.azam.azamcodec.AzamDecode}
+ * Test class for {@link io.azam.azamcodec.AzamCodecs}
*
* @author azam
* @since 0.0.1
*/
public class AzamCodecTest {
- final static Map SAMPLES = new HashMap() {
+ static class Sample {
+ String encoded = null;
+ byte[][] bytes = null;
+ long[] longs = null;
+
+ public Sample() {}
+
+ public Sample(String encoded, byte[][] bytes, long[] longs) {
+ this.encoded = encoded;
+ this.bytes = bytes;
+ this.longs = longs;
+ }
+
+ public int[] ints() {
+ // This is equivalent of the following (since we try to keep source compatible with 1.7)
+ // return Arrays.stream(this.longs).mapToInt(Math::toIntExact).toArray();
+ int[] ints = new int[this.longs.length];
+ for (int i = 0; i < ints.length; i++) {
+ // Converts long to int on byte level not value
+ // i.e. 0xffffffffL will convert to -1 , not 4294967295 (overflow)
+ // NOTE: to do a value conversion, use ints[i] = Math.toIntExact(this.longs[i]); instead
+ ints[i] = (int) this.longs[i] & 0xffffffff;
+ }
+ return ints;
+ }
+
+ public Number[] nums() {
+ // This is equivalent of the following (since we try to keep source compatible with 1.7)
+ // return Arrays.stream(this.longs).mapToInt(Math::toIntExact).toArray();
+ Number[] nums = new Number[this.longs.length];
+ for (int i = 0; i < nums.length; i++) {
+ // Converts long to int on byte level not value
+ // i.e. 0xffffffffL will convert to -1 , not 4294967295 (overflow)
+ // NOTE: to do a value conversion, use ints[i] = Math.toIntExact(this.longs[i]); instead
+ nums[i] = Long.valueOf(this.longs[i]);
+ }
+ return nums;
+ }
+
+ public int largestBytesLength() {
+ int max = 0;
+ for (int i = 0; i < this.bytes.length; i++) {
+ if (this.bytes[i].length > max) {
+ max = this.bytes[i].length;
+ }
+ }
+ return max;
+ }
+
+ public Sample e(String encoded) {
+ this.encoded = encoded;
+ return this;
+ }
+
+ public Sample b(byte[]... bytes) {
+ this.bytes = bytes;
+ return this;
+ }
+
+ public Sample l(long... longs) {
+ this.longs = longs;
+ return this;
+ }
+ }
+
+ static Sample S(String encoded) {
+ return new Sample(encoded, null, null);
+ }
+
+ static List SAMPLES = Arrays.asList(
+ // Single sections
+ S("0").b(b(0x00)).l(0x00), //
+ S("1").b(b(0x01)).l(0x01), //
+ S("f").b(b(0x0f)).l(0x0f), //
+ S("h0").b(b(0x10)).l(0x10), //
+ S("h1").b(b(0x11)).l(0x11), //
+ S("hf").b(b(0x1f)).l(0x1f), //
+ S("z0").b(b(0xf0)).l(0xf0), //
+ S("z1").b(b(0xf1)).l(0xf1), //
+ S("zf").b(b(0xff)).l(0xffL), //
+ S("hg0").b(b(0x01, 0x00)).l(0x0100L), //
+ S("hg1").b(b(0x01, 0x01)).l(0x0101L), //
+ S("hgf").b(b(0x01, 0x0f)).l(0x010fL), //
+ S("hh0").b(b(0x01, 0x10)).l(0x0110L), //
+ S("zg0").b(b(0x0f, 0x00)).l(0x0f00L), //
+ S("zz0").b(b(0x0f, 0xf0)).l(0x0ff0L), //
+ S("zz1").b(b(0x0f, 0xf1)).l(0x0ff1L), //
+ S("zzf").b(b(0x0f, 0xff)).l(0x0fffL), //
+ S("hgg0").b(b(0x10, 0x00)).l(0x1000L), //
+ S("hgh0").b(b(0x10, 0x10)).l(0x1010L), //
+ S("zgg0").b(b(0xf0, 0x00)).l(0xf000L), //
+ S("zzz0").b(b(0xff, 0xf0)).l(0xfff0L), //
+ S("zzz1").b(b(0xff, 0xf1)).l(0xfff1L), //
+ S("zzzf").b(b(0xff, 0xff)).l(0xffffL), //
+ S("hggg0").b(b(0x01, 0x00, 0x00)).l(0x010000L), //
+ S("hggg1").b(b(0x01, 0x00, 0x01)).l(0x010001L), //
+ S("hghg1").b(b(0x01, 0x01, 0x01)).l(0x010101L), //
+ S("hggggg0").b(b(0x01, 0x00, 0x00, 0x00)).l(0x01000000L), //
+ S("hggggg1").b(b(0x01, 0x00, 0x00, 0x01)).l(0x01000001L), //
+ S("hgggggf").b(b(0x01, 0x00, 0x00, 0x0f)).l(0x0100000fL), //
+ S("zzzzzzf").b(b(0x0f, 0xff, 0xff, 0xff)).l(0x0fffffffL), //
+ S("hgggggg0").b(b(0x10, 0x00, 0x00, 0x00)).l(0x10000000L), //
+ S("hgggggg1").b(b(0x10, 0x00, 0x00, 0x01)).l(0x10000001L), //
+ S("hggggggf").b(b(0x10, 0x00, 0x00, 0x0f)).l(0x1000000fL), //
+ S("zzzzzzzf").b(b(0xff, 0xff, 0xff, 0xff)).l(0xffffffffL));
+
+ static List MULTI_SAMPLES = new ArrayList() {
{
- put("0", new byte[][] {bytes(0x00)});
- put("1", new byte[][] {bytes(0x01)});
- put("f", new byte[][] {bytes(0x0f)});
- put("h0", new byte[][] {bytes(0x10)});
- put("h1", new byte[][] {bytes(0x11)});
- put("hf", new byte[][] {bytes(0x1f)});
- put("z0", new byte[][] {bytes(0xf0)});
- put("z1", new byte[][] {bytes(0xf1)});
- put("zf", new byte[][] {bytes(0xff)});
- put("hg0", new byte[][] {bytes(0x01, 0x00)});
- put("hg1", new byte[][] {bytes(0x01, 0x01)});
- put("hgf", new byte[][] {bytes(0x01, 0x0f)});
- put("hh0", new byte[][] {bytes(0x01, 0x10)});
- put("zg0", new byte[][] {bytes(0x0f, 0x00)});
- put("zz0", new byte[][] {bytes(0x0f, 0xf0)});
- put("zz1", new byte[][] {bytes(0x0f, 0xf1)});
- put("zzf", new byte[][] {bytes(0x0f, 0xff)});
- put("hgg0", new byte[][] {bytes(0x10, 0x00)});
- put("zgg0", new byte[][] {bytes(0xf0, 0x00)});
- put("zzz1", new byte[][] {bytes(0xff, 0xf1)});
- put("zzzf", new byte[][] {bytes(0xff, 0xff)});
+ // 2 sections
+ for (Sample s1 : SAMPLES) {
+ for (Sample s2 : SAMPLES) {
+ String encoded = s1.encoded + s2.encoded;
+ byte[][] bytes = new byte[s1.bytes.length + s2.bytes.length][];
+ for (int i = 0; i < s1.bytes.length; i++) {
+ bytes[i] = new byte[s1.bytes[i].length];
+ System.arraycopy(s1.bytes[i], 0, bytes[i], 0, s1.bytes[i].length);
+ }
+ for (int i = 0; i < s2.bytes.length; i++) {
+ bytes[i + s1.bytes.length] = new byte[s2.bytes[i].length];
+ System.arraycopy(s2.bytes[i], 0, bytes[i + s1.bytes.length], 0, s2.bytes[i].length);
+ }
+ long[] longs = new long[s1.longs.length + s2.longs.length];
+ System.arraycopy(s1.longs, 0, longs, 0, s1.longs.length);
+ System.arraycopy(s2.longs, 0, longs, s1.longs.length, s2.longs.length);
+ add(S(encoded).b(bytes).l(longs));
+ }
+ }
+ // 3 sections
+ for (Sample s1 : SAMPLES) {
+ for (Sample s2 : SAMPLES) {
+ for (Sample s3 : SAMPLES) {
+ String encoded = s1.encoded + s2.encoded + s3.encoded;
+ byte[][] bytes = new byte[s1.bytes.length + s2.bytes.length + s3.bytes.length][];
+ for (int i = 0; i < s1.bytes.length; i++) {
+ bytes[i] = new byte[s1.bytes[i].length];
+ System.arraycopy(s1.bytes[i], 0, bytes[i], 0, s1.bytes[i].length);
+ }
+ for (int i = 0; i < s2.bytes.length; i++) {
+ bytes[i + s1.bytes.length] = new byte[s2.bytes[i].length];
+ System.arraycopy(s2.bytes[i], 0, bytes[i + s1.bytes.length], 0, s2.bytes[i].length);
+ }
+ for (int i = 0; i < s3.bytes.length; i++) {
+ bytes[i + s1.bytes.length + s2.bytes.length] = new byte[s3.bytes[i].length];
+ System.arraycopy(s3.bytes[i], 0, bytes[i + s1.bytes.length + s2.bytes.length], 0,
+ s3.bytes[i].length);
+ }
+ long[] longs = new long[s1.longs.length + s2.longs.length + s3.longs.length];
+ System.arraycopy(s1.longs, 0, longs, 0, s1.longs.length);
+ System.arraycopy(s2.longs, 0, longs, s1.longs.length, s2.longs.length);
+ System.arraycopy(s3.longs, 0, longs, s1.longs.length + s2.longs.length,
+ s3.longs.length);
+ add(S(encoded).b(bytes).l(longs));
+ }
+ }
+ }
}
};
- final static byte[] bytes(int... values) {
+ final static byte[] b(int... values) {
byte[] value = new byte[values.length];
for (int i = 0; i < values.length; i++) {
value[i] = (byte) (values[i] & 0xff);
@@ -52,63 +185,129 @@ final static byte[] bytes(int... values) {
return value;
}
- final static void assertAzamEncodeBytes(String expected, byte[]... values) {
- try {
- String actual = azamEncodeBytes(values);
- Assert.assertEquals(expected, actual);
- } catch (IOException e) {
- Assert.fail("azamEncodeBytes throws IOException");
+ final static byte[][] bb(byte[]... values) {
+ byte[][] value = new byte[values.length][];
+ for (int i = 0; i < values.length; i++) {
+ value[i] = values[i];
}
+ return value;
}
@Test
public void testAzamEncodeBytes() {
- for (Map.Entry entry : SAMPLES.entrySet()) {
- assertAzamEncodeBytes(entry.getKey(), entry.getValue());
+ for (Sample sample : SAMPLES) {
+ Assert.assertEquals("azamEncodeBytes failed for value " + sample.encoded, sample.encoded,
+ azamEncodeBytes(sample.bytes));
+ }
+ for (Sample sample : MULTI_SAMPLES) {
+ Assert.assertEquals("azamEncodeBytes failed for value " + sample.encoded, sample.encoded,
+ azamEncodeBytes(sample.bytes));
}
}
- void assertAzamDecodeAllBytes(String value, byte[]... expected) throws ParseException {
- try {
- byte[][] actual = azamDecodeAllBytes(value);
- for (int i = 0; i < expected.length; i++) {
- Assert.assertArrayEquals(expected[i], actual[i]);
- }
- } catch (IOException e) {
- Assert.fail("azamDecodeAllBytes throws IOException");
+ @Test
+ public void testAzamEncodeInts() {
+ for (Sample sample : SAMPLES) {
+ Assert.assertEquals("azamEncodeLongs failed for value " + sample.encoded, sample.encoded,
+ azamEncodeInts(sample.ints()));
+ }
+ for (Sample sample : MULTI_SAMPLES) {
+ Assert.assertEquals("azamEncodeLongs failed for value " + sample.encoded, sample.encoded,
+ azamEncodeInts(sample.ints()));
+ }
+ }
+
+ @Test
+ public void testAzamEncodeLongs() {
+ for (Sample sample : SAMPLES) {
+ Assert.assertEquals("azamEncodeLongs failed for value " + sample.encoded, sample.encoded,
+ azamEncodeLongs(sample.longs));
+ }
+ for (Sample sample : MULTI_SAMPLES) {
+ Assert.assertEquals("azamEncodeLongs failed for value " + sample.encoded, sample.encoded,
+ azamEncodeLongs(sample.longs));
+ }
+ }
+
+ @Test
+ public void testAzamEncode() {
+ for (Sample sample : SAMPLES) {
+ Assert.assertEquals("azamEncodeNumbers failed for value " + sample.encoded, sample.encoded,
+ azamEncodeNumbers(sample.nums()));
+ }
+ for (Sample sample : MULTI_SAMPLES) {
+ Assert.assertEquals("azamEncodeNumbers failed for value " + sample.encoded, sample.encoded,
+ azamEncodeNumbers(sample.nums()));
}
}
@Test
public void testAzamDecodeAllBytes() throws ParseException {
- for (Map.Entry entry : SAMPLES.entrySet()) {
- assertAzamDecodeAllBytes(entry.getKey(), entry.getValue());
+ for (Sample sample : SAMPLES) {
+ byte[][] actual = azamDecodeBytes(sample.encoded);
+ for (int i = 0; i < sample.bytes.length; i++) {
+ Assert.assertArrayEquals("azamDecodeBytes failed for " + sample.encoded, sample.bytes[i],
+ actual[i]);
+ }
+ }
+ for (Sample sample : MULTI_SAMPLES) {
+ byte[][] actual = azamDecodeBytes(sample.encoded);
+ for (int i = 0; i < sample.bytes.length; i++) {
+ Assert.assertArrayEquals("azamDecodeBytes failed for " + sample.encoded, sample.bytes[i],
+ actual[i]);
+ }
+ }
+ }
+
+ void assertAzamDecodeInts(String value, int... expected) {
+ try {
+ int[] actual = azamDecodeInts(value);
+ Assert.assertArrayEquals("azamDecodeInts failed for " + value, expected, actual);
+ } catch (ParseException e) {
+ Assert.fail("azamDecodeInts throws ParseException");
+ }
+ }
+
+ @Test
+ public void testAzamDecodeInts() throws ParseException {
+ for (Sample sample : SAMPLES) {
+ if (sample.largestBytesLength() <= Integer.BYTES) {
+ int[] actual = azamDecodeInts(sample.encoded);
+ Assert.assertArrayEquals("azamDecodeInts failed for " + sample.encoded, sample.ints(),
+ actual);
+ }
+ }
+ for (Sample sample : MULTI_SAMPLES) {
+ if (sample.largestBytesLength() <= Integer.BYTES) {
+ int[] actual = azamDecodeInts(sample.encoded);
+ Assert.assertArrayEquals("azamDecodeInts failed for " + sample.encoded, sample.ints(),
+ actual);
+ }
}
}
@Test
- public void testAzamDecodeAllInts() throws IOException, ParseException {
- Assert.assertArrayEquals(new int[0], azamDecodeAllInts(""));
- Assert.assertArrayEquals(new int[] {0x00}, azamDecodeAllInts("0"));
- Assert.assertArrayEquals(new int[] {0x01}, azamDecodeAllInts("1"));
- Assert.assertArrayEquals(new int[] {0x0f}, azamDecodeAllInts("f"));
- Assert.assertArrayEquals(new int[] {0x10}, azamDecodeAllInts("h0"));
- Assert.assertArrayEquals(new int[] {0x11}, azamDecodeAllInts("h1"));
- Assert.assertArrayEquals(new int[] {0x1f}, azamDecodeAllInts("hf"));
- Assert.assertArrayEquals(new int[] {0xf0}, azamDecodeAllInts("z0"));
- Assert.assertArrayEquals(new int[] {0xf1}, azamDecodeAllInts("z1"));
- Assert.assertArrayEquals(new int[] {0xff}, azamDecodeAllInts("zf"));
- Assert.assertArrayEquals(new int[] {0x0100}, azamDecodeAllInts("hg0"));
- Assert.assertArrayEquals(new int[] {0x0fff}, azamDecodeAllInts("zzf"));
- Assert.assertArrayEquals(new int[] {0xffff}, azamDecodeAllInts("zzzf"));
- Assert.assertArrayEquals(new int[] {0x0fffff}, azamDecodeAllInts("zzzzf"));
- Assert.assertArrayEquals(new int[] {0xffffff}, azamDecodeAllInts("zzzzzf"));
- Assert.assertArrayEquals(new int[] {0x0fffffff}, azamDecodeAllInts("zzzzzzf"));
- Assert.assertArrayEquals(new int[] {0xffffffff}, azamDecodeAllInts("zzzzzzzf"));
+ public void testAzamDecodeLongs() throws ParseException {
+ for (Sample sample : SAMPLES) {
+ if (sample.largestBytesLength() <= Long.BYTES) {
+ long[] actual = azamDecodeLongs(sample.encoded);
+ Assert.assertArrayEquals("azamDecodeLongs failed for " + sample.encoded, sample.longs,
+ actual);
+ }
+ }
+ for (Sample sample : MULTI_SAMPLES) {
+ if (sample.largestBytesLength() <= Long.BYTES) {
+ long[] actual = azamDecodeLongs(sample.encoded);
+ Assert.assertArrayEquals("azamDecodeLongs failed for " + sample.encoded, sample.longs,
+ actual);
+ }
+ }
}
- void assertAcamDecodeAllIntsParseException(String... values) throws IOException {
- for (String value : values) {
+ @Test
+ public void testAzamDecodeIntsParseException() {
+ String[] invalids = new String[] {"h", "hh", "hhh", "_0", "gf", "hggggggg0"};
+ for (String value : invalids) {
ThrowingRunnable runnable = new ThrowingRunnable() {
String value = null;
@@ -118,17 +317,35 @@ public ThrowingRunnable setValue(String value) {
}
@Override
- public void run() throws IOException, ParseException {
- int[] unexpected = azamDecodeAllInts(value);
+ public void run() throws ParseException {
+ int[] unexpected = azamDecodeInts(value);
}
}.setValue(value);
- Assert.assertThrows(ParseException.class, runnable);
+ Assert.assertThrows("azamDecodeInts expects ParseException for " + value,
+ ParseException.class, runnable);
}
}
@Test
- public void testAzamDecodeAllIntsParseException() throws IOException {
- assertAcamDecodeAllIntsParseException("h", "hh", "hhh", "_0", "gf");
+ public void testAzamDecodeLongsParseException() {
+ String[] invalids = new String[] {"h", "hh", "hhh", "_0", "gf", "hggggggggggggggg0"};
+ for (String value : invalids) {
+ ThrowingRunnable runnable = new ThrowingRunnable() {
+ String value = null;
+
+ public ThrowingRunnable setValue(String value) {
+ this.value = value;
+ return this;
+ }
+
+ @Override
+ public void run() throws ParseException {
+ long[] unexpected = azamDecodeLongs(value);
+ }
+ }.setValue(value);
+ Assert.assertThrows("azamDecodeLongs expects ParseException for " + value,
+ ParseException.class, runnable);
+ }
}
}