Skip to content

Commit

Permalink
Polish the new NameDecoder implementation (#202)
Browse files Browse the repository at this point in the history
* Polish the new NameDecoder implementation

Continuation of #201

Fixed the code to avoid doing casting on runtime – this was compiling into additional JVM instructions. Now, we don't have any casts in the code after compilation.

Also: add javadocs.

* DRY up the code

* Oops
  • Loading branch information
Ostrzyciel authored Oct 26, 2024
1 parent 221ffc2 commit 2a36cda
Showing 1 changed file with 44 additions and 18 deletions.
62 changes: 44 additions & 18 deletions core/src/main/java/eu/ostrzyciel/jelly/core/NameDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,30 @@

import java.util.function.Function;

/**
* Class for decoding RDF IRIs from their Jelly representation.
* @param <TIri> The type of the IRI in the target RDF library.
*/
final class NameDecoder<TIri> {
private final class NameLookupEntry {
private static final class NameLookupEntry {
// Primary: the actual name
public String name;
// Secondary values (may be mutated without invalidating the primary value)
// Reference to the last prefix ID used to encode the IRI with this name
public int lastPrefixId;
// Serial number of the last prefix ID used to encode the IRI with this name
public int lastPrefixSerial;
public TIri lastIri;
// Last IRI encoded with this name
public Object lastIri;
}

private static final class PrefixLookupEntry {
public String prefix;
public int serial = -1;
}

private final Object[] nameLookup;
private final Object[] prefixLookup;
private final NameLookupEntry[] nameLookup;
private final PrefixLookupEntry[] prefixLookup;

private int lastPrefixIdReference = 0;
private int lastNameIdReference = 0;
Expand All @@ -28,10 +37,16 @@ private static final class PrefixLookupEntry {

private final Function<String, TIri> iriFactory;

/**
* Creates a new NameDecoder.
* @param prefixTableSize The size of the prefix lookup table.
* @param nameTableSize The size of the name lookup table.
* @param iriFactory A function that creates an IRI from a string.
*/
public NameDecoder(int prefixTableSize, int nameTableSize, Function<String, TIri> iriFactory) {
this.iriFactory = iriFactory;
nameLookup = new Object[nameTableSize];
prefixLookup = new Object[prefixTableSize];
nameLookup = new NameLookupEntry[nameTableSize];
prefixLookup = new PrefixLookupEntry[prefixTableSize];

for (int i = 0; i < nameTableSize; i++) {
nameLookup[i] = new NameLookupEntry();
Expand All @@ -41,50 +56,61 @@ public NameDecoder(int prefixTableSize, int nameTableSize, Function<String, TIri
}
}

/**
* Update the name table with a new entry.
* @param nameEntry name row
* @throws ArrayIndexOutOfBoundsException if the identifier is out of bounds
*/
public void updateNames(RdfNameEntry nameEntry) {
int id = nameEntry.id();
if (id == 0) {
lastNameIdSet++;
} else {
lastNameIdSet = id - 1;
}
@SuppressWarnings("unchecked")
NameLookupEntry entry = (NameLookupEntry) nameLookup[lastNameIdSet];
NameLookupEntry entry = nameLookup[lastNameIdSet];
entry.name = nameEntry.value();
// Enough to invalidate the last IRI – we don't have to touch the serial number.
entry.lastPrefixId = 0;
// Set to null is required to avoid a false positive in the decode method for cases without a prefix.
entry.lastIri = null;
}

/**
* Update the prefix table with a new entry.
* @param prefixEntry prefix row
* @throws ArrayIndexOutOfBoundsException if the identifier is out of bounds
*/
public void updatePrefixes(RdfPrefixEntry prefixEntry) {
int id = prefixEntry.id();
if (id == 0) {
lastPrefixIdSet++;
} else {
lastPrefixIdSet = id - 1;
}
PrefixLookupEntry entry = (PrefixLookupEntry) prefixLookup[lastPrefixIdSet];
PrefixLookupEntry entry = prefixLookup[lastPrefixIdSet];
entry.prefix = prefixEntry.value();
entry.serial++;
}

/**
*
* @param iri
* @return
* @throws RdfProtoDeserializationError
* Reconstruct an IRI from its prefix and name ids.
* @param iri IRI row from the Jelly proto
* @return full IRI combining the prefix and the name
* @throws ArrayIndexOutOfBoundsException if IRI had indices out of lookup table bounds
* @throws RdfProtoDeserializationError if the IRI reference is invalid
* @throws NullPointerException if the IRI reference is invalid
*/
@SuppressWarnings("unchecked")
public TIri decode(RdfIri iri) {
int nameId = iri.nameId();

NameLookupEntry nameEntry;
if (nameId == 0) {
nameEntry = (NameLookupEntry) nameLookup[lastNameIdReference];
nameEntry = nameLookup[lastNameIdReference];
lastNameIdReference++;
} else {
nameEntry = (NameLookupEntry) nameLookup[nameId - 1];
nameEntry = nameLookup[nameId - 1];
lastNameIdReference = nameId;
}

Expand All @@ -94,14 +120,14 @@ public TIri decode(RdfIri iri) {

if (prefixId != 0) {
// Name and prefix
PrefixLookupEntry prefixEntry = (PrefixLookupEntry) prefixLookup[prefixId - 1];
PrefixLookupEntry prefixEntry = prefixLookup[prefixId - 1];
if (nameEntry.lastPrefixId != prefixId || nameEntry.lastPrefixSerial != prefixEntry.serial) {
// Update the last prefix
nameEntry.lastPrefixId = prefixId;
nameEntry.lastPrefixSerial = prefixEntry.serial;
// And compute a new IRI
nameEntry.lastIri = iriFactory.apply(prefixEntry.prefix.concat(nameEntry.name));
return nameEntry.lastIri;
return (TIri) nameEntry.lastIri;
}
if (nameEntry.lastIri == null) {
throw JellyExceptions.rdfProtoDeserializationError(
Expand All @@ -120,6 +146,6 @@ public TIri decode(RdfIri iri) {
nameEntry.lastIri = iriFactory.apply(nameEntry.name);
}

return nameEntry.lastIri;
return (TIri) nameEntry.lastIri;
}
}

0 comments on commit 2a36cda

Please sign in to comment.