+ * This method searches through all registered codecs and returns the first + * match whose {@link Multicodec#name()} equals the given name. + *
+ * + * @param name the name of the codec to look up (must not be {@code null}) + * @return an {@link Optional} containing the matching {@link Multicodec}, or an + * empty {@link Optional} if no codec with the given name is found + */ + public final Optional+ * The {@code codec} subpackage contains the + * {@link com.apicatalog.multicodec.codec.MulticodecRegistry}, which maintains + * mappings between multicodec codes and their + * {@link com.apicatalog.multicodec.Multicodec} definitions. It supports + * creating registry instances containing: + *
+ * + *{@code
+ * // registry with all known codecs
+ * MulticodecRegistry registry = MulticodecRegistry.getInstance();
+ *
+ * // registry with only a subset of codecs by tag
+ * MulticodecRegistry hashes = MulticodecRegistry.getInstance(Tag.Hash);
+ * }
+ */
+package com.apicatalog.multicodec.codec;
diff --git a/src/main/java/com/apicatalog/multicodec/package-info.java b/src/main/java/com/apicatalog/multicodec/package-info.java
new file mode 100644
index 0000000..e452d2c
--- /dev/null
+++ b/src/main/java/com/apicatalog/multicodec/package-info.java
@@ -0,0 +1,35 @@
+/**
+ * Core classes for working with
+ * multicodec
+ * identifiers and encoded values.
+ *
+ * + * A multicodec is a self-describing format in which the data is + * prefixed by a varint code identifying the codec. This package provides: + *
+ * + *{@code
+ * // obtain a decoder with all known codecs
+ * MulticodecDecoder decoder = MulticodecDecoder.getInstance();
+ *
+ * byte[] encoded = ... ; // some multicodec-encoded data
+ * byte[] decoded = decoder.decode(encoded);
+ * }
+ *
+ * + * For additional codecs, see the + * {@link com.apicatalog.multicodec.codec} subpackage. + *
+ */ +package com.apicatalog.multicodec; diff --git a/src/main/java/com/apicatalog/multihash/package-info.java b/src/main/java/com/apicatalog/multihash/package-info.java new file mode 100644 index 0000000..ace677d --- /dev/null +++ b/src/main/java/com/apicatalog/multihash/package-info.java @@ -0,0 +1,20 @@ +/** + * Support for multihash. + * + *+ * A multihash is a self-describing cryptographic hash format. It + * encodes a digest as: + *
+ *+ * This package provides {@link com.apicatalog.multihash.Multihash}, a + * specialization of {@link com.apicatalog.multicodec.Multicodec} that + * implements the multihash format. Instances are immutable and thread-safe. + *
+ */ +package com.apicatalog.multihash; \ No newline at end of file diff --git a/src/main/java/com/apicatalog/uvarint/UVarInt.java b/src/main/java/com/apicatalog/uvarint/UVarInt.java index 16ae622..60396ec 100644 --- a/src/main/java/com/apicatalog/uvarint/UVarInt.java +++ b/src/main/java/com/apicatalog/uvarint/UVarInt.java @@ -1,14 +1,37 @@ package com.apicatalog.uvarint; +/** + * Utility class for encoding and decoding + * unsigned variable-length + * integers (UVarInt). + * + *+ * A UVarInt encodes a non-negative integer into one or more bytes. Each byte + * contributes 7 bits of information, and the most significant bit (MSB) + * indicates whether more bytes follow. This encoding is commonly used in + * multiformats specifications, including + * multicodec and multihash. + *
+ * + *+ * This class is {@code final} and contains only static methods. + *
+ */ public final class UVarInt { - /** Maximum encoded var length in bytes */ + /** Maximum encoded varint length in bytes. */ public static final int MAX_VAR_LENGTH = 9; + /** Mask for the 7 value bits within a varint byte. */ public static final int SEGMENT_BITS = 0x7F; + + /** Continuation bit (MSB) indicating more bytes follow. */ public static final int CONTINUE_BIT = 0x80; + + /** Mask used for detecting overflow conditions. */ public static final int INT_OVERFLOW_BITS = 0x70; + /** Precomputed maximum values for each encoded length. */ protected static long[] MAX_VALUES = { 0x7FL, 0x3FFFL, @@ -21,12 +44,20 @@ public final class UVarInt { 0x7FFFFFFFFFFFFFFFL }; + /** Prevent instantiation. */ protected UVarInt() { - /* protected */ + /* no-op */ } + /** + * Encodes the given value into a UVarInt byte array. + * + * @param value the value to encode (must be non-negative) + * @return a newly allocated byte array containing the encoded value + * @throws IllegalArgumentException if {@code value} cannot be encoded within + * {@link #MAX_VAR_LENGTH} bytes + */ public static final byte[] encode(final long value) { - final int length = byteLength(value); if (length == 1) { @@ -34,67 +65,92 @@ public static final byte[] encode(final long value) { } byte[] uvarint = new byte[length]; - write(value, uvarint, 0); - return uvarint; } + /** + * Writes the UVarInt encoding of the given value into the target array starting + * at the given index. + * + * @param value the value to encode + * @param uvarint the target byte array + * @param index the starting index + * @throws IllegalArgumentException if {@code value} cannot be encoded within + * {@link #MAX_VAR_LENGTH} bytes + * @throws NullPointerException if {@code uvarint} is {@code null} + */ public static final void write(final long value, final byte[] uvarint, final int index) { - int offset = 0; long bytes = value; - boolean next = false; + do { if (next) { - uvarint[offset + index - 1] |= UVarInt.CONTINUE_BIT; + uvarint[offset + index - 1] |= CONTINUE_BIT; } - uvarint[offset + index] = (byte) (bytes & UVarInt.SEGMENT_BITS); + uvarint[offset + index] = (byte) (bytes & SEGMENT_BITS); bytes >>>= 7; - next = bytes != 0; - offset++; - } while (next); - } + /** + * Decodes a UVarInt from the given byte array starting at index {@code 0}. + * + * @param uvarint the encoded byte array + * @return the decoded value + * @throws IllegalArgumentException if the encoding is invalid or too long + * @throws NullPointerException if {@code uvarint} is {@code null} + */ public static final long decode(final byte[] uvarint) { return decode(uvarint, 0); } + /** + * Decodes a UVarInt from the given byte array starting at the specified index. + * + * @param uvarint the encoded byte array + * @param index the starting index + * @return the decoded value + * @throws IllegalArgumentException if the encoding is invalid or too long + * @throws NullPointerException if {@code uvarint} is {@code null} + */ public static final long decode(final byte[] uvarint, int index) { - int offset = 0; - boolean next = false; long value = 0; do { - if (offset >= UVarInt.MAX_VAR_LENGTH) { - throw new IllegalArgumentException("uintvar longer than " + UVarInt.MAX_VAR_LENGTH + " has been found. Only uintvar up to " + UVarInt.MAX_VAR_LENGTH + " are supported."); + if (offset >= MAX_VAR_LENGTH) { + throw new IllegalArgumentException( + "UVarInt longer than " + MAX_VAR_LENGTH + " bytes is not supported."); } if (offset >= uvarint.length) { - throw new IllegalArgumentException("The input stream has ended unexpectedly, a next byte is expected."); + throw new IllegalArgumentException( + "Unexpected end of input: next byte is required."); } int b = uvarint[offset + index]; - - value |= (long) (b & UVarInt.SEGMENT_BITS) << (offset * 7); - - next = ((b & UVarInt.CONTINUE_BIT) != 0); - + value |= (long) (b & SEGMENT_BITS) << (offset * 7); + next = ((b & CONTINUE_BIT) != 0); offset++; - } while (next); return value; } + /** + * Returns the number of bytes required to encode the given value as a UVarInt. + * + * @param value the value to measure + * @return the number of bytes required + * @throws IllegalArgumentException if {@code value} cannot be encoded within + * {@link #MAX_VAR_LENGTH} bytes + */ public static final int byteLength(long value) { if (value <= MAX_VALUES[0]) { return 1; @@ -112,6 +168,7 @@ public static final int byteLength(long value) { return (value <= MAX_VALUES[7]) ? 8 : MAX_VAR_LENGTH; } - throw new IllegalArgumentException("A var longer than " + UVarInt.MAX_VAR_LENGTH + " has been found. Only vars up to " + UVarInt.MAX_VAR_LENGTH + " are supported."); + throw new IllegalArgumentException( + "Value exceeds maximum encodable length of " + MAX_VAR_LENGTH + " bytes."); } } diff --git a/src/main/java/com/apicatalog/uvarint/package-info.java b/src/main/java/com/apicatalog/uvarint/package-info.java new file mode 100644 index 0000000..2aa2cc1 --- /dev/null +++ b/src/main/java/com/apicatalog/uvarint/package-info.java @@ -0,0 +1,26 @@ +/** + * Utilities for working with unsigned variable-length integers (UVarInt). + * + *+ * A UVarInt is an encoding of an unsigned integer into one or more + * bytes using a variable-length scheme. Each byte contributes 7 bits of data, + * and the highest bit indicates whether more bytes follow. This encoding is + * widely used in multiformats + * specifications such as multicodec and multihash. + *
+ * + *+ * The {@link com.apicatalog.uvarint.UVarInt} class provides static methods for: + *
+ *+ * Encoded values are limited to at most + * {@link com.apicatalog.uvarint.UVarInt#MAX_VAR_LENGTH} bytes. + *
+ */ +package com.apicatalog.uvarint;