Skip to content

Commit

Permalink
MemCopy: Buffer support, generate from template, add "subarray-copy"
Browse files Browse the repository at this point in the history
* Add generic parameter S to MemCopy<S,T>
  S is source, e.g., float[] or FloatBuffer
  T is target, e.g., float[] or FloatBuffer

* Generate MemCopy.java from velocity template
  (run bin/generate.groovy to generate)

* new method copyNDRangeRecursive() copies sub-region
  between flattened arrays (of different sizes)
  • Loading branch information
tpietzsch committed Sep 7, 2024
1 parent 27e705e commit 536c7e2
Show file tree
Hide file tree
Showing 8 changed files with 2,871 additions and 113 deletions.
238 changes: 238 additions & 0 deletions bin/generate.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
#!/usr/bin/env groovy

/*
* #%L
* SciJava Operations: a framework for reusable algorithms.
* %%
* Copyright (C) 2018 SciJava developers.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/

debug = System.getenv('DEBUG')
def debug(msg) {
if (debug) System.err.println("[DEBUG] $msg")
}

@Grab('org.apache.velocity:velocity:1.7')
import org.apache.velocity.app.VelocityEngine

// TODO: Get path to Groovy script and make these dirs relative to that.
templateDirectory = 'templates'
outputDirectory = 'src'

knownFiles = new java.util.HashSet();

/* Gets the last modified timestamp for the given file. */
def timestamp(dir, file) {
if (file == null) return Long.MAX_VALUE;
file = new java.io.File(dir, file);
knownFiles.add(file);
return file.lastModified();
}

/* Processes a template using Apache Velocity. */
def processTemplate(engine, context, templateFile, outFilename) {
debug("processTemplate('$engine', '$context', '$templateFile', '$outFilename')")

if (outFilename == null) return; // nothing to do

// create output directory if it does not already exist
outFile = new java.io.File(outputDirectory, outFilename);
knownFiles.add(outFile);
if (outFile.getParentFile() != null) outFile.getParentFile().mkdirs();

// apply the template and write out the result
t = engine.getTemplate(templateFile);
writer = new StringWriter();
t.merge(context, writer);
out = new PrintWriter(outFile, "UTF-8");
out.print(writer.toString());
out.close();
}

/* Evaluates a string using Groovy. */
def parseValue(sh, translationsFile, key, expression) {
try {
result = sh.evaluate(expression)
sh.setVariable(key, result)
return result
}
catch (groovy.lang.GroovyRuntimeException e) {
print("[WARNING] $translationsFile: " +
"key '$key' has unparseable value: " + e.getMessage());
}
}

/* Reads a translations File */
def readTranslation(engine, globalContext, reader, templateSubdirectory, templateFile, translationsFile, isInclude){
sh = new groovy.lang.GroovyShell();
for (;;) {
// read the line
line = reader.readLine();

if (line == null) break;
// check if the line starts a new section
if (line.startsWith("[") && line.endsWith("]")) {
// if we are parsing a .include file, return when we hit any sections
if(isInclude){
println("[WARNING] $translationsFile: Section definition in .include file. Ending processing of $translationsFile");
return context;
}
// write out the previous file
processTemplate(engine, context, templateFile, outputFilename);

// start a new file
outputFilename = line.substring(1, line.length() - 1);
if (!templateDirectory.equals(templateSubdirectory)) {
subPath = templateSubdirectory.substring(templateDirectory.length() + 1);
outputFilename = "$subPath/$outputFilename";
}
context = new org.apache.velocity.VelocityContext(globalContext);
continue;
}

// ignore blank lines
trimmedLine = line.trim();
if (trimmedLine.isEmpty()) continue;

// ignore comments
if (trimmedLine.startsWith("#")) continue;

// include any global files
if (trimmedLine.startsWith(".include")){
includeFile = line.substring(9);
if(includeFile.startsWith("templates")){
includeSubdirectory = includeFile.substring(0, includeFile.lastIndexOf("/"))
includeFile = includeFile.substring(includeFile.lastIndexOf("/"))
}
else{
includeSubdirectory = templateSubdirectory
}
globalReader = new java.io.BufferedReader(new java.io.FileReader("$includeSubdirectory/$includeFile"));
encapsulatedContext = new org.apache.velocity.VelocityContext(context)
context = readTranslation(engine, encapsulatedContext, globalReader, templateSubdirectory, templateFile, includeFile, true)
continue;
}

if (!line.contains('=')) {
print("[WARNING] $translationsFile: Ignoring spurious line: $line");
continue;
}

int idx = line.indexOf('=');
key = line.substring(0, idx).trim();
value = line.substring(idx + 1);

if (value.trim().equals('```')) {
// multi-line value
builder = new StringBuilder();
for (;;) {
line = reader.readLine();
if (line == null) {
throw new RuntimeException("Unfinished value: " + builder.toString());
}
if (line.equals('```')) {
break;
}
if (builder.length() > 0) {
builder.append("\n");
}
builder.append(line);
}
value = builder.toString();
}

context.put(key, parseValue(sh, translationsFile, key, value));
}

return context;
}

/*
* Translates a template into many files in the outputDirectory,
* given a translations file in INI style; e.g.:
*
* [filename1]
* variable1 = value1
* variable2 = value2
* ...
* [filename2]
* variable1 = value3
* variable2 = value4
* ...
*/
def translate(templateSubdirectory, templateFile, translationsFile) {
debug("translate('$templateSubdirectory', '$templateFile', '$translationsFile')")

// initialize the Velocity engine
engine = new org.apache.velocity.app.VelocityEngine();
p = new java.util.Properties();
// fail if template uses an invalid expression; e.g., an undefined variable
p.setProperty("runtime.references.strict", "true");
// tell Velocity where the templates are located
p.setProperty("file.resource.loader.path", "$templateSubdirectory");
// tell Velocity to log to stderr rather than to a velocity.log file
p.setProperty(org.apache.velocity.runtime.RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
"org.apache.velocity.runtime.log.SystemLogChute");
engine.init(p);

// read translation lines
outputFilename = null;
context = globalContext = new org.apache.velocity.VelocityContext();
reader = new java.io.BufferedReader(new java.io.FileReader("$templateSubdirectory/$translationsFile"));

readTranslation(engine, context, reader, templateSubdirectory, templateFile, translationsFile, false);

reader.close();

// process the template
processTemplate(engine, context, templateFile, outputFilename);
}

/* Recursively translates all templates in the given directory. */
def translateDirectory(templateSubdirectory) {
debug("translateDirectory('$templateSubdirectory')")

for (file in new java.io.File(templateSubdirectory).listFiles()) {
if (file.isDirectory()) {
// process subdirectories recursively
translateDirectory(file.getPath());
}
else {
// process Velocity template files only
name = file.getName();
if (!name.endsWith('.vm')) continue;
prefix = name.substring(0, name.lastIndexOf('.'));
translate(templateSubdirectory, name, prefix + '.list');
}
}
}

try {
translateDirectory(templateDirectory);
}
catch (Throwable t) {
t.printStackTrace(System.err);
throw t;
}
24 changes: 12 additions & 12 deletions src/main/java/net/imglib2/blocks/ArrayImgRangeCopier.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
Expand All @@ -44,13 +44,13 @@
*
* @param <T> a primitive array type, e.g., {@code byte[]}.
*/
class ArrayImgRangeCopier< T > implements RangeCopier< T >
class ArrayImgRangeCopier< S, T > implements RangeCopier< T >
{
private final int n;
private final int[] srcDims;
private final Ranges findRanges;
private final MemCopy< T > memCopy;
private final T oob;
private final MemCopy< S, T > memCopy;
private final S oob;

private final List< Ranges.Range >[] rangesPerDimension;
private final Ranges.Range[] ranges;
Expand All @@ -60,13 +60,13 @@ class ArrayImgRangeCopier< T > implements RangeCopier< T >
private final int[] csteps;
private final int[] lengths;

private final T src;
private final S src;

public ArrayImgRangeCopier(
final ArrayImg< ?, ? > arrayImg,
final Ranges findRanges,
final MemCopy< T > memCopy,
final T oob )
final MemCopy< S, T > memCopy,
final S oob )
{
n = arrayImg.numDimensions();

Expand All @@ -86,11 +86,11 @@ public ArrayImgRangeCopier(
csteps = new int[ n ];
lengths = new int[ n ];

src = ( T ) ( ( ( ArrayDataAccess< ? > ) arrayImg.update( null ) ).getCurrentStorageArray() );
src = ( S ) ( ( ArrayDataAccess< ? > ) arrayImg.update( null ) ).getCurrentStorageArray();
}

// creates an independent copy of {@code other}
private ArrayImgRangeCopier( ArrayImgRangeCopier< T > copier )
private ArrayImgRangeCopier( ArrayImgRangeCopier< S, T > copier )
{
n = copier.n;
srcDims = copier.srcDims.clone();
Expand All @@ -108,7 +108,7 @@ private ArrayImgRangeCopier( ArrayImgRangeCopier< T > copier )
}

@Override
public ArrayImgRangeCopier< T > newInstance()
public ArrayImgRangeCopier< S, T > newInstance()
{
return new ArrayImgRangeCopier<>( this );
}
Expand Down Expand Up @@ -220,7 +220,7 @@ private void copyRanges( final T dest )
}
}

private void copyRangesRecursively( final T src, final int srcPos, final T dest, final int destPos, final int d )
private void copyRangesRecursively( final S src, final int srcPos, final T dest, final int destPos, final int d )
{
final int length = lengths[ d ];
final int cstep = csteps[ d ];
Expand Down
Loading

0 comments on commit 536c7e2

Please sign in to comment.