diff --git a/.gitignore b/.gitignore index c36a62fb8f..23e8c9656a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ atlassian-ide-plugin.xml *.o *.gimple .Rproj.user +.gradle \ No newline at end of file diff --git a/packages/stats/pom.xml b/packages/stats/pom.xml index a6f7c2cc03..69ee25953d 100644 --- a/packages/stats/pom.xml +++ b/packages/stats/pom.xml @@ -65,6 +65,9 @@ gnur-sources-compile compile + + true + build diff --git a/packages/stats/src/main/c/apply_optim.c b/packages/stats/src/main/c/apply_optim.c index f8afbed2fd..d72a555239 100644 --- a/packages/stats/src/main/c/apply_optim.c +++ b/packages/stats/src/main/c/apply_optim.c @@ -641,7 +641,9 @@ void cgmin(int n, double *Bvec, double *X, double *Fmin, } /* include setulb() */ -#include "lbfgsb.c" +void setulb(int n, int m, double *x, double *l, double *u, int *nbd, + double *f, double *g, double factr, double *pgtol, + double *wa, int * iwa, char *task, int iprint, int *isave); void lbfgsb(int n, int m, double *x, double *l, double *u, int *nbd, double *Fmin, optimfn fminfn, optimgr fmingr, int *fail, diff --git a/packages/stats/src/main/c/lbfgsb.c b/packages/stats/src/main/c/lbfgsb.c index 3c2819f007..7679644050 100644 --- a/packages/stats/src/main/c/lbfgsb.c +++ b/packages/stats/src/main/c/lbfgsb.c @@ -123,7 +123,6 @@ static void prn3lb(int n, double *x, double *f, char *task, int iprint, /* ================ L-BFGS-B (version 2.3) ========================== */ -static void setulb(int n, int m, double *x, double *l, double *u, int *nbd, double *f, double *g, double factr, double *pgtol, double *wa, int * iwa, char *task, int iprint, int *isave) diff --git a/packages/stats/src/main/c/lbfgsb.h b/packages/stats/src/main/c/lbfgsb.h new file mode 100644 index 0000000000..4fc78dfd45 --- /dev/null +++ b/packages/stats/src/main/c/lbfgsb.h @@ -0,0 +1,69 @@ + +static void active(int, double *, double *, int *, double *, int *, + int, int *, int *, int *); +static void bmv(int, double *, double *, int *, double *, double *, int *); +static void cauchy(int, double *, double *, + double *, int *, double *, int *, int *, + double *, double *, double *, int, double *, + double *, double *, double *, double *, int * , + int *, double *, double *, double *, double *, + int *, int, double *, int *, double *); +static void cmprlb(int, int, double *, + double *, double *, double *, double *, + double *, double *, double *, double *, int *, + double *, int *, int *, int *, int *, + int *); +static void dcsrch(double *, double *, double *, + double, double, double, + double, double, char *); +static void dcstep(double *, double *, + double *, double *, double *, double *, + double *, double *, double *, int *, double *, + double *); +static void errclb(int, int, double, + double *, double *, int *, char *, int *, int *); +static void formk(int, int *, int *, int *, int *, int *, int *, + int *, double *, double *, int, double *, + double *, double *, double *, int *, int *, int *); +static void formt(int, double *, double *, + double *, int *, double *, int *); +static void freev(int, int *, int *, + int *, int *, int *, int *, int *, int *, + int *, int, int *); +static void hpsolb(int, double *, int *, int); +static void lnsrlb(int, double *, double *, + int *, double *, double *, double *, double *, + double *, double *, double *, double *, + double *, double *, double *, double *, + double *, double *, double *, int *, int *, + int *, int *, int *, char *, int *, int *, + char *); +static void mainlb(int, int, double *, + double *, double *, int *, double *, double *, + double, double *, double *, double *, + double *, double *, double *, double *, + double *, double *, double *, double *, + double *, double *, int *, int *, int *, char *, + int, char *, int *); +static void matupd(int, int, double *, double *, double *, + double *, double *, double *, int *, int *, + int *, int *, double *, double *, double *, + double *, double *); +static void projgr(int, double *, double *, + int *, double *, double *, double *); +static void subsm(int, int, int *, int *, double *, double *, + int *, double *, double *, double *, double *, + double *, int *, int *, int *, double *, + double *, int, int *); + +static void prn1lb(int n, int m, double *l, double *u, double *x, + int iprint, double epsmch); +static void prn2lb(int n, double *x, double *f, double *g, int iprint, + int iter, int nfgv, int nact, double sbgnrm, + int nint, char *word, int iword, int iback, + double stp, double xstep); +static void prn3lb(int n, double *x, double *f, char *task, int iprint, + int info, int iter, int nfgv, int nintol, int nskip, + int nact, double sbgnrm, int nint, + char *word, int iback, double stp, double xstep, + int k); \ No newline at end of file diff --git a/script-engine/src/test/java/org/renjin/script/OptimMultiThreadTest.java b/script-engine/src/test/java/org/renjin/script/OptimMultiThreadTest.java new file mode 100644 index 0000000000..babd45de1b --- /dev/null +++ b/script-engine/src/test/java/org/renjin/script/OptimMultiThreadTest.java @@ -0,0 +1,90 @@ +/* + * Renjin : JVM-based interpreter for the R language for the statistical analysis + * Copyright © 2010-${$file.lastModified.year} BeDataDriven Groep B.V. and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, a copy is available at + * https://www.gnu.org/licenses/gpl-2.0.txt + * + */ + +package org.renjin.script; + +import org.junit.Test; +import org.renjin.sexp.DoubleVector; +import org.renjin.sexp.ListVector; + +import javax.script.ScriptEngine; + +public class OptimMultiThreadTest { + private static final ThreadLocal engines = new ThreadLocal<>(); + private static final int RUNS = 20; + private static final int THREADS_NUMBER = 2; + + + @Test + public void testMultithread() throws InterruptedException { + Thread[] threads = new Thread[THREADS_NUMBER]; + for (int i = 0; i < THREADS_NUMBER; i++) { + threads[i] = new Thread(this::task); + threads[i].start(); + } + for (int i = 0; i < THREADS_NUMBER; i++) { + threads[i].join(); + + } + } + + private void task() { + ScriptEngine engine = getEngine(); + try { + // Rosenbrock Banana function + engine.eval("fr <- function(x) { " + + " x1 <- x[1]\n" + + " x2 <- x[2]\n" + + " 100 * (x2 - x1 * x1) ^ 2 + (1 - x1) ^ 2\n" + + "}"); + } catch (Exception e) { + System.err.println(e.getMessage()); + } + for (int i = 0; i < RUNS; i++) { + try { + ListVector result = (ListVector) engine.eval("optim(c(-1.2,1), fr, method = \"L-BFGS\")"); + System.out.println(result); + + DoubleVector parameters = (DoubleVector) result.get("par"); + double p0 = parameters.getElementAsDouble(0); + double p1 = parameters.getElementAsDouble(1); + if(Math.abs(p0 - 0.9998000) > 0.00001) { + throw new AssertionError("Incorrect result: p0 = " + String.format("%f", p0)); + } + if(Math.abs(p1 - 0.9996001) > 0.00001) { + throw new AssertionError("Incorrect result: p1 = " + String.format("%f", p1)); + } + } catch (Exception e) { + System.err.println(e.getMessage()); + } + } + } + + private ScriptEngine getEngine() { + ScriptEngine engine = engines.get(); + if (engine == null) { + RenjinScriptEngineFactory factory = new RenjinScriptEngineFactory(); + engine = factory.getScriptEngine(); + engines.set(engine); + } + return engine; + } + +} diff --git a/tests/src/test/R/test.format.R b/tests/src/test/R/test.format.R new file mode 100644 index 0000000000..45ff314352 --- /dev/null +++ b/tests/src/test/R/test.format.R @@ -0,0 +1,23 @@ +# +# Renjin : JVM-based interpreter for the R language for the statistical analysis +# Copyright © 2010-2019 BeDataDriven Groep B.V. and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, a copy is available at +# https://www.gnu.org/licenses/gpl-2.0.txt +# +library(hamcrest) + +test.format.double <- function() { + assertThat(format(1/3), equalTo("0.3333333")) +} \ No newline at end of file diff --git a/tools/gcc-bridge/compiler/src/main/java/org/renjin/gcc/codegen/FunctionGenerator.java b/tools/gcc-bridge/compiler/src/main/java/org/renjin/gcc/codegen/FunctionGenerator.java index 80e0790aa7..68b10c0468 100644 --- a/tools/gcc-bridge/compiler/src/main/java/org/renjin/gcc/codegen/FunctionGenerator.java +++ b/tools/gcc-bridge/compiler/src/main/java/org/renjin/gcc/codegen/FunctionGenerator.java @@ -73,6 +73,7 @@ public class FunctionGenerator implements InvocationStrategy { private TypeOracle typeOracle; private FunctionOracle functionOracle; private ExprFactory exprFactory; + private List globalVarTransformers; private LocalStaticVarAllocator staticVarAllocator; private LocalVariableTable localSymbolTable; private LocalVariableTable localStaticSymbolTable; @@ -85,7 +86,9 @@ public class FunctionGenerator implements InvocationStrategy { private boolean compilationFailed = false; public FunctionGenerator(String className, GimpleFunction function, TypeOracle typeOracle, - GlobalVarAllocator globalVarAllocator, UnitSymbolTable symbolTable) { + GlobalVarAllocator globalVarAllocator, + List globalVarTransformers, + UnitSymbolTable symbolTable) { this.className = className; this.function = function; this.typeOracle = typeOracle; @@ -103,6 +106,7 @@ public FunctionGenerator(String className, GimpleFunction function, TypeOracle t this.staticVarAllocator = new LocalStaticVarAllocator("$" + function.getSafeMangledName() + "$", globalVarAllocator); this.localSymbolTable = new LocalVariableTable(symbolTable); this.localStaticSymbolTable = new LocalVariableTable(symbolTable); + this.globalVarTransformers = globalVarTransformers; } @@ -375,15 +379,14 @@ private void scheduleLocalVariables() { } try { - GExpr generator = functionOracle.variable(varDecl, - varDecl.isStatic() ? - staticVarAllocator : - mv.getLocalVarAllocator()); - - localSymbolTable.addVariable(varDecl.getId(), generator); - if(varDecl.isStatic()) { + GExpr generator = localStaticGenerator(varDecl); + localSymbolTable.addVariable(varDecl.getId(), generator); localStaticSymbolTable.addVariable(varDecl.getId(), generator); + + } else { + localSymbolTable.addVariable(varDecl.getId(), + functionOracle.variable(varDecl, mv.getLocalVarAllocator())); } } catch (Exception e) { @@ -392,6 +395,15 @@ private void scheduleLocalVariables() { } } + private GExpr localStaticGenerator(GimpleVarDecl varDecl) { + for (GlobalVarTransformer globalVarTransformer : globalVarTransformers) { + if(globalVarTransformer.acceptLocalStaticVar(varDecl)) { + return globalVarTransformer.generator(typeOracle, function, varDecl); + } + } + return functionOracle.variable(varDecl, staticVarAllocator); + } + private void emitBasicBlock(GimpleBasicBlock basicBlock) { mv.visitLabel(labels.of(basicBlock)); diff --git a/tools/gcc-bridge/compiler/src/main/java/org/renjin/gcc/codegen/GlobalVarTransformer.java b/tools/gcc-bridge/compiler/src/main/java/org/renjin/gcc/codegen/GlobalVarTransformer.java index 5b30536d52..0ea66c6a9e 100644 --- a/tools/gcc-bridge/compiler/src/main/java/org/renjin/gcc/codegen/GlobalVarTransformer.java +++ b/tools/gcc-bridge/compiler/src/main/java/org/renjin/gcc/codegen/GlobalVarTransformer.java @@ -21,10 +21,17 @@ import org.renjin.gcc.codegen.expr.GExpr; import org.renjin.gcc.codegen.type.TypeOracle; import org.renjin.gcc.gimple.GimpleCompilationUnit; +import org.renjin.gcc.gimple.GimpleFunction; import org.renjin.gcc.gimple.GimpleVarDecl; public interface GlobalVarTransformer { - boolean accept(GimpleVarDecl decl); + + boolean acceptGlobalVar(GimpleVarDecl decl); + + boolean acceptLocalStaticVar(GimpleVarDecl decl); GExpr generator(TypeOracle typeOracle, GimpleCompilationUnit unit, GimpleVarDecl decl); + + GExpr generator(TypeOracle typeOracle, GimpleFunction function, GimpleVarDecl decl); + } diff --git a/tools/gcc-bridge/compiler/src/main/java/org/renjin/gcc/codegen/ProvidedVarTransformer.java b/tools/gcc-bridge/compiler/src/main/java/org/renjin/gcc/codegen/ProvidedVarTransformer.java index 265e4cb349..a7d5aae793 100644 --- a/tools/gcc-bridge/compiler/src/main/java/org/renjin/gcc/codegen/ProvidedVarTransformer.java +++ b/tools/gcc-bridge/compiler/src/main/java/org/renjin/gcc/codegen/ProvidedVarTransformer.java @@ -22,6 +22,7 @@ import org.renjin.gcc.codegen.expr.GExpr; import org.renjin.gcc.codegen.type.TypeOracle; import org.renjin.gcc.gimple.GimpleCompilationUnit; +import org.renjin.gcc.gimple.GimpleFunction; import org.renjin.gcc.gimple.GimpleVarDecl; import java.util.Map; @@ -37,12 +38,22 @@ public ProvidedVarTransformer(TypeOracle typeOracle, Map globalVarTransfor GimpleVarDecl decl) { for (GlobalVarTransformer transformer : globalVarTransformers) { - if(transformer.accept(decl)) { + if(transformer.acceptGlobalVar(decl)) { return transformer.generator(typeOracle, this.unit, decl); } } diff --git a/tools/gcc-bridge/runtime/src/main/java/org/renjin/gcc/runtime/PointerPtr.java b/tools/gcc-bridge/runtime/src/main/java/org/renjin/gcc/runtime/PointerPtr.java index 25a7e30695..5499c7037a 100644 --- a/tools/gcc-bridge/runtime/src/main/java/org/renjin/gcc/runtime/PointerPtr.java +++ b/tools/gcc-bridge/runtime/src/main/java/org/renjin/gcc/runtime/PointerPtr.java @@ -25,7 +25,7 @@ */ public class PointerPtr extends AbstractPtr { - private static final int BYTES = 4; + public static final int BYTES = 4; public static final PointerPtr NULL = new PointerPtr(null, 0); diff --git a/tools/gnur-compiler/src/main/java/org/renjin/gnur/ContextVarAllocator.java b/tools/gnur-compiler/src/main/java/org/renjin/gnur/ContextVarAllocator.java index c4e6e24588..7acbff28cf 100644 --- a/tools/gnur-compiler/src/main/java/org/renjin/gnur/ContextVarAllocator.java +++ b/tools/gnur-compiler/src/main/java/org/renjin/gnur/ContextVarAllocator.java @@ -22,6 +22,7 @@ import org.renjin.gcc.codegen.expr.JLValue; import org.renjin.gcc.codegen.var.VarAllocator; import org.renjin.gcc.gimple.GimpleCompilationUnit; +import org.renjin.gcc.gimple.GimpleFunction; import org.renjin.repackaged.asm.Type; import java.util.ArrayList; @@ -42,34 +43,39 @@ public ContextVarAllocator(Type contextClass) { this.contextClass = contextClass; } + public List getContextFields() { + return fields; + } - public JLValue reserve(GimpleCompilationUnit unit, String name, Type type, Optional initialValue) { - - String varName = VarAllocator.toJavaSafeName(unit.getName()) + "$" + VarAllocator.toJavaSafeName(name); - ContextField var = new ContextField(contextClass, varName, type, initialValue); - + private JLValue reserve(String prefix, String varName, Type type, Optional initialValue) { + String qualifiedName = prefix + "$" + VarAllocator.toJavaSafeName(varName); + ContextField var = new ContextField(contextClass, qualifiedName, type, initialValue); fields.add(var); - return var.jvalue(); } - public VarAllocator forUnit(GimpleCompilationUnit unit) { + return forPrefix(VarAllocator.toJavaSafeName(unit.getName())); + } + + public VarAllocator forFunction(GimpleFunction function) { + return forPrefix(VarAllocator.toJavaSafeName(function.getUnit().getName()) + "$" + + VarAllocator.toJavaSafeName(function.getMangledName())); + } + + private VarAllocator forPrefix(String prefix) { return new VarAllocator() { @Override public JLValue reserve(String name, Type type) { - return ContextVarAllocator.this.reserve(unit, name, type, Optional.empty()); + return ContextVarAllocator.this.reserve(prefix, name, type, Optional.empty()); } @Override public JLValue reserve(String name, Type type, JExpr initialValue) { - return ContextVarAllocator.this.reserve(unit, name, type, Optional.of(initialValue)); - + return ContextVarAllocator.this.reserve(prefix, name, type, Optional.of(initialValue)); } }; } - public List getContextFields() { - return fields; - } + } diff --git a/tools/gnur-compiler/src/main/java/org/renjin/gnur/GlobalVarPlugin.java b/tools/gnur-compiler/src/main/java/org/renjin/gnur/GlobalVarPlugin.java index 34fd1760b4..61cd70275d 100644 --- a/tools/gnur-compiler/src/main/java/org/renjin/gnur/GlobalVarPlugin.java +++ b/tools/gnur-compiler/src/main/java/org/renjin/gnur/GlobalVarPlugin.java @@ -79,7 +79,7 @@ public void writeClasses(CodeGenerationContext generationContext) throws IOExcep writer.writeFields(globalVarRewriter.getContextFields()); writer.writeConstructor(generationContext, globalVarRewriter.getContextFields(), - globalVarRewriter.getContextVars()); + globalVarRewriter.getGlobalVariables()); writer.writeTo(generationContext); } diff --git a/tools/gnur-compiler/src/main/java/org/renjin/gnur/GlobalVarRewriter.java b/tools/gnur-compiler/src/main/java/org/renjin/gnur/GlobalVarRewriter.java index 4b3d730166..7c44482e12 100644 --- a/tools/gnur-compiler/src/main/java/org/renjin/gnur/GlobalVarRewriter.java +++ b/tools/gnur-compiler/src/main/java/org/renjin/gnur/GlobalVarRewriter.java @@ -19,10 +19,14 @@ package org.renjin.gnur; import org.renjin.gcc.codegen.GlobalVarTransformer; +import org.renjin.gcc.codegen.expr.ConstantValue; import org.renjin.gcc.codegen.expr.GExpr; import org.renjin.gcc.codegen.type.TypeOracle; import org.renjin.gcc.gimple.GimpleCompilationUnit; +import org.renjin.gcc.gimple.GimpleFunction; import org.renjin.gcc.gimple.GimpleVarDecl; +import org.renjin.gcc.gimple.expr.GimpleConstant; +import org.renjin.gcc.gimple.expr.GimplePrimitiveConstant; import org.renjin.repackaged.asm.Type; import java.util.ArrayList; @@ -36,14 +40,14 @@ public class GlobalVarRewriter implements GlobalVarTransformer { private final ContextVarAllocator allocator; - private List contextVars = new ArrayList<>(); + private List globalVariables = new ArrayList<>(); public GlobalVarRewriter(Type contextClass) { allocator = new ContextVarAllocator(contextClass); } @Override - public boolean accept(GimpleVarDecl decl) { + public boolean acceptGlobalVar(GimpleVarDecl decl) { // Do not include C++ run-time type information in the per-thread context. // It does not change after initialization. @@ -55,19 +59,41 @@ public boolean accept(GimpleVarDecl decl) { return true; } + @Override + public boolean acceptLocalStaticVar(GimpleVarDecl decl) { + return true; + } + @Override public GExpr generator(TypeOracle typeOracle, GimpleCompilationUnit unit, GimpleVarDecl decl) { - contextVars.add(decl); + globalVariables.add(decl); return typeOracle.forType(decl.getType()).variable(decl, allocator.forUnit(unit)); } + @Override + public GExpr generator(TypeOracle typeOracle, GimpleFunction function, GimpleVarDecl decl) { + if(!hasSafeInitialValue(decl)) { + throw new UnsupportedOperationException(String.format("TODO: %s in %s in %s has initial value %s", + decl.getName(), + function.getName(), + function.getUnit().getSourceName(), + decl.getValue().toString())); + } + globalVariables.add(decl); + return typeOracle.forType(decl.getType()).variable(decl, allocator.forFunction(function)); + } + + private boolean hasSafeInitialValue(GimpleVarDecl decl) { + return decl.getValue() == null || decl.getValue() instanceof GimpleConstant; + } + public List getContextFields() { return allocator.getContextFields(); } - public List getContextVars() { - return contextVars; + public List getGlobalVariables() { + return globalVariables; } } diff --git a/tools/gnur-runtime/src/main/java/org/renjin/gnur/api/GnuStringVector.java b/tools/gnur-runtime/src/main/java/org/renjin/gnur/api/GnuStringVector.java index e46c5d517d..85a4d13a83 100644 --- a/tools/gnur-runtime/src/main/java/org/renjin/gnur/api/GnuStringVector.java +++ b/tools/gnur-runtime/src/main/java/org/renjin/gnur/api/GnuStringVector.java @@ -18,6 +18,7 @@ */ package org.renjin.gnur.api; +import org.renjin.gcc.runtime.PointerPtr; import org.renjin.sexp.AttributeMap; import org.renjin.sexp.StringVector; @@ -92,4 +93,5 @@ public void set(int index, GnuCharSexp charValue) { public GnuCharSexp getElementAsCharSexp(int index) { return values[index]; } + } diff --git a/tools/gnur-runtime/src/main/java/org/renjin/gnur/api/Rinternals.java b/tools/gnur-runtime/src/main/java/org/renjin/gnur/api/Rinternals.java index b94f2b13d0..b24ceafa4f 100644 --- a/tools/gnur-runtime/src/main/java/org/renjin/gnur/api/Rinternals.java +++ b/tools/gnur-runtime/src/main/java/org/renjin/gnur/api/Rinternals.java @@ -567,13 +567,17 @@ public static int SETLEVELS(SEXP x, int v) { public static Object DATAPTR(SEXP x) { if (x instanceof IntVector || x instanceof LogicalVector) { - return INTEGER(x); + return Rinternals2.INTEGER(x); } else if(x instanceof DoubleVector) { - return REAL(x); + return Rinternals2.REAL(x); } else if(x instanceof ComplexVector) { return COMPLEX(x); } else if(x instanceof RawVector) { return RAW(x); +// } else if(x instanceof GnuStringVector) { +// throw new UnsupportedOperationException("TODO"); + } else if(x instanceof StringVector) { + return new StringVectorPtr((StringVector) x, 0); } else { throw new UnsupportedOperationException("DATAPTR on type " + x.getClass().getName()); } diff --git a/tools/gnur-runtime/src/main/java/org/renjin/gnur/api/StringVectorPtr.java b/tools/gnur-runtime/src/main/java/org/renjin/gnur/api/StringVectorPtr.java new file mode 100644 index 0000000000..9ffe12316b --- /dev/null +++ b/tools/gnur-runtime/src/main/java/org/renjin/gnur/api/StringVectorPtr.java @@ -0,0 +1,91 @@ +/* + * Renjin : JVM-based interpreter for the R language for the statistical analysis + * Copyright © 2010-2019 BeDataDriven Groep B.V. and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, a copy is available at + * https://www.gnu.org/licenses/gpl-2.0.txt + */ +package org.renjin.gnur.api; + +import org.renjin.gcc.runtime.*; +import org.renjin.repackaged.guava.base.Charsets; +import org.renjin.sexp.StringVector; + +public class StringVectorPtr extends AbstractPtr { + + private StringVector vector; + private int offset; + + public StringVectorPtr(StringVector vector, int offset) { + this.vector = vector; + this.offset = offset; + } + + @Override + public Object getArray() { + return vector; + } + + @Override + public int getOffsetInBytes() { + return offset * PointerPtr.BYTES; + } + + @Override + public Ptr realloc(int newSizeInBytes) { + throw new UnsupportedOperationException("TODO"); + } + + @Override + public Ptr pointerPlus(int bytes) { + if(bytes == 0) { + return this; + } + if(bytes % PointerPtr.BYTES == 0) { + return new StringVectorPtr(this.vector, this.offset + (bytes / PointerPtr.BYTES)); + } + return new OffsetPtr(this, bytes); + } + + @Override + public byte getByte(int offset) { + throw new UnsupportedOperationException("TODO"); + } + + @Override + public Ptr getAlignedPointer(int index) { + String element = vector.getElementAsString(offset + index); + if(element == null) { + return BytePtr.NULL; + } else { + return BytePtr.nullTerminatedString(element, Charsets.UTF_8); + } + } + + @Override + public void setByte(int offset, byte value) { + throw new UnsupportedOperationException("Illegal modification of a shared vector! " + + "Mis-behaving C/C++ code has tried to modify a vector that it should not."); + } + + @Override + public int toInt() { + return getOffsetInBytes(); + } + + @Override + public boolean isNull() { + return false; + } +}