Skip to content

Commit

Permalink
Make Codec.set() idempotent
Browse files Browse the repository at this point in the history
- previously needlessly allocated new instance if already set
- also handle CharacterCodingException in ModelCompiler.saveTransducer()
- javadoc comments updated in
  - IParameterizedEffector, BaseParameterizedEffector
  - no interface or implementation changes

Signed-off-by: jrte <jrte.project@gmail.com>
  • Loading branch information
jrte committed Oct 9, 2023
1 parent febd0b4 commit 7ec235f
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 56 deletions.
51 changes: 18 additions & 33 deletions src/com/characterforming/jrte/engine/ModelCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,6 @@ private boolean createModelFile(File riboseModelFile, Logger rtcLogger) {
private boolean compileTransducer(File inrFile) throws RiboseException {
int size = (int)inrFile.length();
byte[] bytes = null;
boolean fail = true;
try (DataInputStream f = new DataInputStream(new FileInputStream(inrFile))) {
this.reset(inrFile);
int position = 0, length = size;
Expand All @@ -381,11 +380,11 @@ private boolean compileTransducer(File inrFile) throws RiboseException {
} catch (FileNotFoundException e) {
this.addError(String.format("%1$s: File not found '%2$s'",
this.transducerName, inrFile.getPath()));
return !fail;
return false;
} catch (IOException e) {
this.addError(String.format("%1$s: IOException compiling '%2$s'; %3$s",
this.transducerName, inrFile.getPath(), e.getMessage()));
return !fail;
return false;
}
try {
Bytes automaton = Codec.encode("Automaton");
Expand All @@ -397,15 +396,11 @@ private boolean compileTransducer(File inrFile) throws RiboseException {
this.transductor.stop();
assert this.transductor.status().isStopped();
this.saveTransducer();
fail = false;
} catch (ModelException | EffectorException | DomainErrorException e) {
} catch (ModelException | EffectorException | DomainErrorException | CharacterCodingException e) {
this.addError(String.format("%1$s: Failed to compile '%2$s'; %3$s",
this.transducerName, inrFile.getPath(), e.getMessage()));
} catch (CharacterCodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return !fail;
return true;
}

private boolean validate() {
Expand All @@ -414,7 +409,7 @@ private boolean validate() {
this.addError(String.format("Error: Invalid %2$s token '%1$s' on tape 0",
token.asString(), token.getTypeName()));
} else if (token.getSymbol().bytes().length > 1
&& !this.tapeTokens.get(2).contains(token)) {
&& !this.tapeTokens.get(2).contains(token)) {
this.addError(String.format("Error: Unrecognized signal reference '%1$s' on tape 0",
token.asString()));
}
Expand Down Expand Up @@ -445,9 +440,8 @@ private boolean validate() {
if (super.transducerNameIndex[ordinal] == null
|| !super.transducerNameIndex[ordinal].equals(e.getKey())
|| super.transducerOffsetIndex[ordinal] <= 0) {
this.addError(String.format("'%1$s': referenced but not compiled in model",
this.addError(String.format("'%1$s': referenced but not compiled in model",
e.getKey().asString()));

}
}

Expand All @@ -467,13 +461,10 @@ void saveTransducer() throws ModelException, CharacterCodingException {
int nInputs = this.kernelMatrix.length;
int nStates = this.kernelMatrix[0].length;
int nTransitions = 0;
for (int input = 0; input < nInputs; input++) {
for (int state = 0; state < nStates; state++) {
if (this.kernelMatrix[input][state][1] != 0) {
for (int input = 0; input < nInputs; input++)
for (int state = 0; state < nStates; state++)
if (this.kernelMatrix[input][state][1] != 0)
nTransitions++;
}
}
}
final int transitionCount = nTransitions;
final int fieldCount = super.getFieldCount(this.transducerOrdinal);
double sparsity = 100 * (1 - ((double)nTransitions / (double)(nStates * nInputs)));
Expand Down Expand Up @@ -569,12 +560,7 @@ void putAutomaton() throws CharacterCodingException {
this.effectorVectorMap.put(new Ints(new int[] { 0 }), 0);
for (final Integer inrInputState : inrInputStates) {
for (final Transition t : this.getTransitions(inrInputState)) {
if (!t.isFinal) {
if (t.tape != 0) {
this.addError(String.format(ModelCompiler.AMBIGUOUS_STATE_MESSAGE,
this.getTransducerName(), t.from));
continue;
}
if (t.tape == 0 && !t.isFinal) {
try {
final int rteState = this.inputStateMap.get(t.from);
final int inputOrdinal = super.getInputOrdinal(t.symbol.bytes());
Expand All @@ -589,19 +575,18 @@ void putAutomaton() throws CharacterCodingException {
} else if (chain.isParameterizedEffector()) {
transitionMatrix[inputOrdinal][rteState][1] = Transducer.action(-1 * effectVector[0], effectVector[1]);
} else {
Ints vector = new Ints(effectVector);
Integer vectorOrdinal = this.effectorVectorMap.get(vector);
if (vectorOrdinal == null) {
vectorOrdinal = this.effectorVectorMap.size();
this.effectorVectorMap.put(vector, vectorOrdinal);
}
transitionMatrix[inputOrdinal][rteState][1] = -vectorOrdinal;
Integer vectorOrdinal = this.effectorVectorMap.computeIfAbsent(
new Ints(effectVector), absent -> this.effectorVectorMap.size());
transitionMatrix[inputOrdinal][rteState][1] = -1 * vectorOrdinal;
}
}
} catch (CompilationException e) {
this.addError(String.format("%1$s: %2$s",
this.getTransducerName(), e.getMessage()));
}
} else if (t.tape != 0) {
this.addError(String.format(ModelCompiler.AMBIGUOUS_STATE_MESSAGE,
this.getTransducerName(), t.from));
}
}
}
Expand Down Expand Up @@ -985,8 +970,8 @@ private int[][] instrumentSumVectors(int msumOrdinal, int[][] effectVectors, int
|| (effectVectors[-1 * vectorOrdinal][effectVectors[-1 * vectorOrdinal].length - vectorLength] != msumOrdinal)) {
int[] effect = msumEffects[cell[0]];
int[] vector = vectorOrdinal > 0
? (vectorOrdinal > 1 ? new int[] { vectorOrdinal, 0 } : new int[] { vectorOrdinal })
: effectVectors[-1 * vectorOrdinal];
? (vectorOrdinal > 1 ? new int[] { vectorOrdinal, 0 } : new int[] { vectorOrdinal })
: effectVectors[-1 * vectorOrdinal];
int[] vectorex = Arrays.copyOf(vector, vector.length + effect.length - 1);
System.arraycopy(effect, 0, vectorex, vector.length - 1, effect.length);
Ints vxkey = new Ints(vectorex);
Expand Down
11 changes: 6 additions & 5 deletions src/com/characterforming/ribose/IParameterizedEffector.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,12 @@
* <b>P</b>[] precompiled by the respective proxy effector when the model
* is loaded into the runtime. Live effectors are not otherwise involved
* in parameter compilation or decompilation and are never passivated. They
* select their parameters by array index in their {@link #invoke(int p)}
* methods, which normally return {@link IEffector#RTX_NONE} to indicate
* no special condition. They may also return a {@link Signal} encoded by
* {@link IOutput#signal(int)}. See the javadoc comments for {@link
* IEffector} for more information regarding effector {@code RTX} codes.
* select their parameters by array index by calling {@link #getParameter(int p)}
* in their {@link #invoke(int p)} methods, which normally return {@link
* IEffector#RTX_NONE} to indicate no special condition. They may also return
* a {@link Signal} encoded by {@link IOutput#signal(int)}. See the javadoc
* comments for {@link IEffector} for more information regarding effector
* {@code RTX} codes.
* <br><br>
* All {@code IParameterizedEffector} implementations must be subclasses of
* {@link BaseParameterizedEffector}. Other than immutability ribose places
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@
* <li>{@link IParameterizedEffector#allocateParameters(int)}</li>
* <li>{@link IParameterizedEffector#compileParameter(IToken[])}</li>
* </ul>
* Default {@link showParameterType()} and {@link showParameterTokens(int)}
* methods are implemented here but subclasses <i>may</i> override these if desired.
* Otherwise, public methods not exposed in the {@link IParameterizedEffector} interface
* are for internal use only. These methods implement the parameter compilation and binding
* protocols for all parameterized effector implementations.
* Subclasses may access indexed compiled parameter objects in their {@link #invoke(int)}
* implementations using {@link #getParameter(int)}. Default {@link showParameterType()}
* and {@link showParameterTokens(int)} methods are implemented here but subclasses
* <i>may</i> override these if desired. Otherwise, public methods not exposed in the
* {@link IParameterizedEffector} interface are for internal use only. These methods
* implement the parameter compilation and binding protocols for all parameterized effector
* implementations.
*
* @param <T> the effector target type
* @param <P> the effector parameter type, constructible from IToken[] (eg new P(IToken[]))
Expand All @@ -50,7 +52,7 @@
*/
public abstract class BaseParameterizedEffector<T extends ITarget, P> extends BaseEffector<T> implements IParameterizedEffector<T, P> {

/** Effector parameters indexed and selected by parameter ordinal.*/
/** Raw and compiled effector parameters indexed and selected by parameter ordinal.*/
private IToken[][] tokens = null;
private P[] parameters = null;

Expand Down
34 changes: 22 additions & 12 deletions src/com/characterforming/ribose/base/Codec.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
* Thread local charset encoder/decoder. This is attached to a ribose thread on first use
* and detached when the thread closes a model (directly or implicitly via autoclose)
* or explicitly calls {@link IModel#detach()}. Static methods for encoding and decoding
* are provided.
* are provided. Codecs are sticky, if any codec method is invoked after {@link #detach()}
* or {@link IModel#close()} a new Codec will be attached (and require detaching).
*/
public class Codec {
private static final Charset CHARSET = Charset.forName(System.getProperty("ribose.runtime.charset", "UTF-8"));
Expand All @@ -56,14 +57,20 @@ private Codec() {
}

private static Codec set() {
Codec codec = new Codec();
Codec.LOCAL.set(codec);
Codec codec = Codec.LOCAL.get();
if (codec == null) {
codec = new Codec();
Codec.LOCAL.set(codec);
}
return codec;
}

private static Codec get() {
Codec codec = Codec.LOCAL.get() ;
return (codec == null) ? Codec.set() : codec;
Codec codec = Codec.LOCAL.get();
if (codec == null) {
codec = Codec.set();
}
return codec;
}

/**
Expand All @@ -74,38 +81,41 @@ public static void detach() {
}

/**
* Decode UTF-8 bytes to a String.
* Decode some UTF-8 bytes to a String.
*
* @param bytes the bytes to decode
* @param length the number of bytes to decode
* @param length the number of bytes to decode from offset 0
* @return a String containing the decoded text
* @throws CharacterCodingException if decoding fails
*/
public static String decode(final byte[] bytes, final int length) throws CharacterCodingException {
public static String decode(final byte[] bytes, final int length)
throws CharacterCodingException {
assert 0 <= length && length <= bytes.length;
int size = Math.max(Math.min(length, bytes.length), 0);
return Codec.get().decoder.reset().decode(ByteBuffer.wrap(bytes, 0, size)).toString();
}

/**
* Decode UTF-8 bytes to a String.
* Decode all UTF-8 bytes to a String.
*
* @param bytes the bytes to decode
* @return a String containing the decoded text
* @throws CharacterCodingException if decoding fails
*/
public static String decode(final byte[] bytes) throws CharacterCodingException {
public static String decode(final byte[] bytes)
throws CharacterCodingException {
return Codec.decode(bytes, bytes.length);
}

/**
* Encode a String.
* Encode a String to UTF-8.
*
* @param chars the string to encode
* @return the encoded Bytes
* @throws CharacterCodingException if encoding fails
*/
public static Bytes encode(final String chars) throws CharacterCodingException {
public static Bytes encode(final String chars)
throws CharacterCodingException {
ByteBuffer buffer = Codec.get().encoder.reset().encode(CharBuffer.wrap(chars.toCharArray()));
byte[] bytes = new byte[buffer.limit()];
buffer.get(bytes, 0, bytes.length);
Expand Down

0 comments on commit 7ec235f

Please sign in to comment.