Skip to content
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

Multihash Support #82

Merged
merged 11 commits into from
Oct 2, 2024
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
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ A Java implementation of [Multicodec](https://github.com/multiformats/multicodec
* `static` codecs registry
* no lookups for a codec when encoding
* direct `static` access to codecs
* confirugable set of codecs to support when decoding
* Multihash support [2.0.0-SNAPSHOT]
* configurable set of codecs to support when decoding
* Multihash support
* `Unsigned VarInt` support
* no 3rd party dependencies

Expand Down Expand Up @@ -70,31 +70,31 @@ byte[] encoded = codec.encode(input);

```java
/* get multihash decoder initialized with all multihash codecs */
var decoder = MultihashDecoder.getInstance();
var decoder = MulticodecDecoder.getInstance(Tag.Multihash);

/* decode; digest size is checked and removed */
byte[] decoded = decoder.decode(encoded);

/* or check if supported */
Multihash multihash = decoder.get(encoded).orElseThrow(() -> new IllegalArgumentException("Unsupported multihash."));
var codec = decoder.get(encoded).orElseThrow(() -> new IllegalArgumentException("Unsupported multihash."));
byte[] decoded = codec.decode(encoded);

/* or directly */
byte[] decoded = MultihashRegistry.SHA2_384.decode(encoded);
byte[] decoded = MultihashCodec.SHA2_384.decode(encoded);

/*

/* check if byte array is encoded with multihash codec */
if (MultihashRegistry.SHA2_384.isEncoded(encoded)) {
if (MultihashCodec.SHA2_384.isEncoded(encoded)) {
...
}

/* get registry initialized with all multihash codecs */
var registry = MultihashRegistry.getInstance();
var registry = MulticodecRegistry.getInstance(Tag.Multihash);

/* encode an input as multihash */
var multihash = registry.get(code).orElseThrow(() -> new IllegalArgumentException("Unsupported multihash."));
byte[] encoded = multihash.encode(input);
var codec = registry.get(code).orElseThrow(() -> new IllegalArgumentException("Unsupported multihash."));
byte[] encoded = codec.encode(input);

```

Expand All @@ -107,7 +107,7 @@ byte[] encoded = multihash.encode(input);
<dependency>
<groupId>com.apicatalog</groupId>
<artifactId>copper-multicodec</artifactId>
<version>1.0.1</version>
<version>1.1.0</version>
</dependency>
```

Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

| Version | Supported |
| ------- |:------------------:|
| 1.0.x | ✅ |
| 1.x.x | ✅ |
| 0.x.x | ❌ |

## Reporting a Vulnerability
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.apicatalog</groupId>
<artifactId>copper-multicodec</artifactId>
<version>2.0.0-SNAPSHOT</version>
<version>1.1.0</version>
<packaging>jar</packaging>
<url>https://github.com/filip26/copper-multicodec</url>
<scm>
Expand All @@ -28,7 +28,7 @@
<name>Copper Multcodec</name>

<description>
Multicodec API
Multicodec &amp; Multihash API, Registry
</description>

<licenses>
Expand Down
39 changes: 28 additions & 11 deletions src/gen/java/com/apicatalog/multicodec/CodecDef.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,35 @@

import java.io.PrintWriter;

import com.apicatalog.multicodec.Multicodec.Status;
import com.apicatalog.multicodec.Multicodec.Tag;

public class CodecDef {

String name;
Tag tag;
Long code;
String status;
Status status;
String description;

public static final CodecDef from(String[] columns) {

final CodecDef def = new CodecDef();

String type = columns[1].trim();
String status = columns[3].trim();

def.code = Long.parseLong(columns[2].trim().substring(2), 16);
def.name = columns[0].trim();
def.tag = Tag.valueOf(Character.toUpperCase(type.charAt(0)) + type.substring(1));
def.status = columns[3].trim();
if (!status.isEmpty()) {
def.status = Status.valueOf(Character.toUpperCase(status.charAt(0)) + status.substring(1));
}
def.description = columns.length > 4 ? columns[4].trim() : null;
return def;
}

public final void writeCode(PrintWriter writer) {
public final void writeCode(PrintWriter writer, Class<?> clazz) {
writer.print(" ");
writer.print("/** ");
writer.print(tag);
Expand All @@ -36,21 +40,34 @@ public final void writeCode(PrintWriter writer) {
writer.print(", ");
writer.print(description);
}
writer.print(", status = ");
writer.print(status);
if (status != null) {
writer.print(", status = ");
writer.print(status.name().toLowerCase());
}
writer.print(", code = ");
writer.print(String.format("0x%x", code));
writer.println(" */");

writer.print(" ");
writer.print("public static final Multicodec ");
writer.print("public static final ");
writer.print(clazz.getSimpleName());
writer.print(" ");
writer.print(getJavaName());
writer.print(" = Multicodec.of(\"");
writer.print(" = ");
writer.print(clazz.getSimpleName());
writer.print(".of(\"");
writer.print(name);
writer.print("\", Tag.");
writer.print(tag.name());
writer.print(", ");
writer.print(String.format("0x%x",code));
writer.print("\", ");
if (Multicodec.class.equals(clazz)) {
writer.print("Tag.");
writer.print(tag.name());
writer.print(", ");
}
writer.print(String.format("0x%x", code));
if (status != null) {
writer.print(", Multicodec.Status.");
writer.print(status);
}
writer.println(");");
}

Expand Down
28 changes: 18 additions & 10 deletions src/gen/java/com/apicatalog/multicodec/CodecTag.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

public class CodecTag {

public static void generate(String tag, String className) throws IOException {
public static void generate(final String tag, final String className, final Class<?> clazz) throws IOException {

try (final Stream<String[]> stream = Files.lines(Paths.get("table.csv"))
.map(line -> line.trim().split(","))) {
Expand All @@ -25,8 +25,14 @@ public static void generate(String tag, String className) throws IOException {
writer.println("import java.util.TreeMap;");
writer.println("import java.util.Map;");
writer.println();
writer.println("import com.apicatalog.multicodec.Multicodec;");
writer.println("import com.apicatalog.multicodec.Multicodec.Tag;");
if (Multicodec.class.equals(clazz)) {
writer.println("import com.apicatalog.multicodec.Multicodec.Tag;");
} else {
writer.println("import com.apicatalog.multicodec.Multicodec;");
}
writer.print("import ");
writer.print(clazz.getCanonicalName());
writer.println(";");
writer.println();
writer.print("/** Multicodec Registry - generated: ");
writer.print(new Date().toString());
Expand All @@ -35,7 +41,7 @@ public static void generate(String tag, String className) throws IOException {
writer.print(className);
writer.println(" {");
writer.println();

Collection<CodecDef> defs = stream
.filter(columns -> tag.equals(columns[1].trim()))
.map(CodecDef::from)
Expand All @@ -44,12 +50,14 @@ public static void generate(String tag, String className) throws IOException {
.collect(Collectors.toList());

defs.forEach(def -> {
def.writeCode(writer);
def.writeCode(writer, clazz);
writer.println();
});

writer.println(" protected static final Map<Long, Multicodec> ALL = new TreeMap<>();");
writer.println();

writer.print(" protected static final Map<Long,");
writer.print(clazz.getSimpleName());
writer.println("> ALL = new TreeMap<>();");
writer.println();
writer.println(" static {");

defs.forEach(def -> {
Expand All @@ -59,10 +67,10 @@ public static void generate(String tag, String className) throws IOException {
writer.print(def.getJavaName());
writer.println(");");
});

writer.println(" }");
writer.println();

writer.print(" protected ");
writer.print(className);
writer.println("() { /* protected */ }");
Expand Down
22 changes: 12 additions & 10 deletions src/gen/java/com/apicatalog/multicodec/CvsImporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@

import java.io.IOException;

import com.apicatalog.multihash.Multihash;

public class CvsImporter {

public static void main(final String[] args) throws IOException {
CodecTag.generate("key", "KeyCodec");
CodecTag.generate("multihash", "MultihashCodec");
CodecTag.generate("multiaddr", "MultiaddrCodec");
CodecTag.generate("hash", "HashCodec");
CodecTag.generate("cid", "CidCodec");
CodecTag.generate("namespace", "NamespaceCodec");
CodecTag.generate("multiformat", "MultiformatCodec");
CodecTag.generate("serialization", "SerializationCodec");
CodecTag.generate("transport", "TransportCodec");
CodecTag.generate("varsig", "VarsigCodec");
CodecTag.generate("key", "KeyCodec", Multicodec.class);
CodecTag.generate("multihash", "MultihashCodec", Multihash.class);
CodecTag.generate("multiaddr", "MultiaddrCodec", Multicodec.class);
CodecTag.generate("hash", "HashCodec", Multicodec.class);
CodecTag.generate("cid", "CidCodec", Multicodec.class);
CodecTag.generate("namespace", "NamespaceCodec", Multicodec.class);
CodecTag.generate("multiformat", "MultiformatCodec", Multicodec.class);
CodecTag.generate("serialization", "SerializationCodec", Multicodec.class);
CodecTag.generate("transport", "TransportCodec", Multicodec.class);
CodecTag.generate("varsig", "VarsigCodec", Multicodec.class);
}
}
48 changes: 39 additions & 9 deletions src/main/java/com/apicatalog/multicodec/Multicodec.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,35 @@ public enum Tag {
Varsig,
}

/**
* Common registration status
*/
public enum Status {
Deprecated,
Draft,
Permanent,
}

protected final String name;
protected final byte[] codeVarint;
protected final long code;
protected final Tag tag;
protected final Status status;

protected Multicodec(String name, Tag tag, long code, byte[] uvarint) {
protected Multicodec(String name, Tag tag, long code, byte[] uvarint, Status status) {
this.tag = tag;
this.name = name;
this.code = code;
this.codeVarint = uvarint;
this.status = status;
}

public static Multicodec of(String name, Tag tag, long code) {
return new Multicodec(name, tag, code, UVarInt.encode(code));
return of(name, tag, code, null);
}

public static Multicodec of(String name, Tag tag, long code, Status status) {
return new Multicodec(name, tag, code, UVarInt.encode(code), status);
}

public int length() {
Expand All @@ -63,6 +78,10 @@ public long code() {
return code;
}

public Status status() {
return status;
}

/**
* Encode a value with a codec.
*
Expand All @@ -73,9 +92,7 @@ public long code() {
*/
public byte[] encode(final byte[] value) {

if (value == null) {
throw new IllegalArgumentException("The value to encdode must not be null.");
}
Objects.requireNonNull(value);

if (value.length == 0) {
throw new IllegalArgumentException("The value to encode must be non empty byte array.");
Expand Down Expand Up @@ -111,18 +128,31 @@ public boolean isEncoded(final byte[] encoded) {
* @throws IllegalArgumentException if the encoded value cannot be decoded
*/
public byte[] decode(final byte[] encoded) {
return decode(encoded, 0);
}

/**
* Decode an encoded value
*
* @param encoded value to decode
* @param index an index from which to start (included)
* @return a decoded value
*
* @throws IllegalArgumentException if the encoded value cannot be decoded
*/
public byte[] decode(final byte[] encoded, final int index) {

Objects.requireNonNull(encoded);

if (encoded.length < (codeVarint.length + 1)) {
if ((encoded.length - index) < (codeVarint.length + 1)) {
throw new IllegalArgumentException("The value to decode must be non empty byte array, min length = " + (codeVarint.length + 1) + ", actual = " + encoded.length + ".");
}

if (!IntStream.range(0, codeVarint.length).allMatch(i -> codeVarint[i] == encoded[i])) {
if (!IntStream.range(0, codeVarint.length).allMatch(i -> codeVarint[i] == encoded[i + index])) {
throw new IllegalArgumentException("The value to decode is not encoded with " + toString() + ".");
}

return Arrays.copyOfRange(encoded, codeVarint.length, encoded.length);
return Arrays.copyOfRange(encoded, index + codeVarint.length, encoded.length - index);
}

@Override
Expand All @@ -144,6 +174,6 @@ public boolean equals(Object obj) {

@Override
public String toString() {
return "Multicodec [name=" + name + ", tag=" + tag + ", code=" + code + ", varint=" + Arrays.toString(codeVarint) + "]";
return "Multicodec [name=" + name + ", tag=" + tag + ", code=" + code + "]";
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.apicatalog.multicodec;

import java.util.Objects;
import java.util.Optional;

import com.apicatalog.multicodec.Multicodec.Tag;
Expand Down Expand Up @@ -54,9 +55,7 @@ public static MulticodecDecoder getInstance() {
*/
public Optional<Multicodec> getCodec(final byte[] encoded) {

if (encoded == null) {
throw new IllegalArgumentException("The encoded value must not be null.");
}
Objects.requireNonNull(encoded);

if (encoded.length == 0) {
throw new IllegalArgumentException("The encoded value be non empty byte array.");
Expand Down
Loading