From: Michael Zucchi Date: Mon, 22 Apr 2019 00:51:09 +0000 (+0930) Subject: Initial import of pre-beta 1.0. X-Git-Tag: nativez-1.0~4 X-Git-Url: https://code.zedzone.au/cvs?a=commitdiff_plain;h=9726132f7947b7142f8bf064f6ea9aaa26c4868f;p=nativez Initial import of pre-beta 1.0. --- 9726132f7947b7142f8bf064f6ea9aaa26c4868f diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..470b0ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/nbproject/private/ +/bin/ +/build/ +/dist/ + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..27737b7 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ + +dist_VERSION=0.99 +dist_NAME=nativez +dist_EXTRA=README \ + build.xml \ + nbproject/build-impl.xml \ + nbproject/genfiles.properties \ + nbproject/project.properties \ + nbproject/project.xml + +include config.make + +java_MODULES=notzed.nativez + +include java.make + diff --git a/README b/README new file mode 100644 index 0000000..e73f60d --- /dev/null +++ b/README @@ -0,0 +1,109 @@ + +Copyright (C) 2018,2019 Michael Zucchi + +See the section LICENSE for details. + +Introduction +------------ + +This is a C link library and Java base class which can be used to help +implement libraries which use the Java Native Interface (JNI). + +Compile +------- + +This module must be compiled under a GNU/Linux system. Cross +compilation is possible to windows-amd64 by installing +gcc-mingw-w64-x86-64. + +$ make +$ make TARGET=linux-amd64 +$ make TARGET=windows-amd64 + +The target-specific jmodule: + +`bin/notzed.nativez//notzed.nativez.jmod' + +Other files of import which can be used for an IDE, dependent project +compilation, or non-modular use: + +`bin/notzed.nativez/notzed.nativez.jar' - portable modular jar +`bin/notzed.nativez//include' - header files +`bin/notzed.nativez//include' - header files +`bin/notzed.nativez//lib/libnativez.so' - linux shared library +`bin/modules/notzed.nativez/' - compiled class files + +Usage +----- + +Java Library +- - - - - -- + +Define a common base class and derive from +`au.notzed.nativez.NativeZ'. This (and all derived classes) must +implement the single long (pointer) constructor and load the +corresponding dynamic library which implements the native interfaces +from any derived classes. + +Each derived class should include a `static void release(long p)' +method which will be called once the object is no longer accessible +from Java. This will typically be a native method that frees the +associated resources. + +The module for the library must export the package containing the +derived classes to the `notzed.nativez' module using the `opens' +directive. + +Native Library +- - - - - - -- + +The native library should link with -lnativez and include "nativez.h". + +Before using any functions invoke the nativez_OnLoad() function, +typically in the native library's JNI_OnLoad() function. + +Any instance of a class derived from NativeZ must be created using the +NativeZ_* functions. + +Native functions should take object arguments and resolve them to +native pointers using NativeZ_getP(). + +The nativez_* functions are just general JNI and useful cross-platform +utility functions which may be freely used within (any) JNI code. + +LICENSE +------- + +This is supplied under the permissive 3-clause BSD license. It allows +for commercial use. See also notzed.nativez/legal/LICENSE. + + Copyright (C) 2018,2019 Michael Zucchi + + 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. + + (3)The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..5bf2874 --- /dev/null +++ b/build.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + Builds, tests, and runs the project notzed.nativez. + + + diff --git a/config.make b/config.make new file mode 100644 index 0000000..08853c7 --- /dev/null +++ b/config.make @@ -0,0 +1,36 @@ + +TARGET ?= linux-amd64 + +JAVA_HOME ?= /usr/local/jdk-11.0.2 +JAVAFX_HOME ?= /usr/local/javafx-sdk-11.0.2 +FFMPEG_HOME ?= /opt/ffmpeg/4.0 + +JAVAC ?= javac +JAR ?= jar +JMOD ?= jmod + +JAVACFLAGS += -source 11 + +# Linux options +linux-amd64_CPPFLAGS = \ + -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux +linux-amd64_CFLAGS = -fPIC -Wall +linux-amd64_CC = cc +linux-amd64_LD = ld + +linux-amd64_SO = .so +linux-amd64_LIB = lib +linux-amd64_JMOD_LIBS = --libs + +# Windows options +windows-amd64_CPPFLAGS= \ + -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux \ + -DHAVE_ALIGNED_MALLOC \ + -DWIN32 +windows-amd64_CC = x86_64-w64-mingw32-gcc +windows-amd64_LD = x86_64-w64-mingw32-ld +windows-amd64_LDFLAGS = -Wl,--subsystem,windows + +windows-amd64_SO = .dll +windows-amd64_LIB = +windows-amd64_JMOD_LIBS = --cmds diff --git a/java.make b/java.make new file mode 100644 index 0000000..c7074ab --- /dev/null +++ b/java.make @@ -0,0 +1,274 @@ +# +# Copyright (C) 2019 Michael Zucchi +# +# This is the copyright for java.make +# +# 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 3 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, see . +# + +# General purpose modular java makefile that supports native library +# compilation directly. Non-recrusve implementation. +# +# Uses metamake programming with some file conventions to implement +# auto-make-like features. + +# Define modules +# -------------- +# java_MODULES list of java modules to compile. The sources must +# exist in src//classes. Resource files are +# stored in src//classes. Source-code +# generators must exist in src//gen. Native +# libraries must exist in src//jni. + +# Global variables + +# JAVA_HOME location of jdk. +# JAVAC java compiler to use. Default is 'javac' on the path. +# JAVACFLAGS javac flags applied to all invocations. +# JAR jar command. +# JARFLAGS jar flags +# JMOD jmod command. +# JMODFLAGS jmod flags. + +# Module specific variables + +# _JDEPMOD Lists modules which this one depends on. + +# _JAVACFLAGS Extra module-specific flags for each command. +# _JARFLAGS +# _JMODFLAGS + + +# _JAVA Java sources. This is set internally. +# _RESOURCES .jar resources. This is set internally. + +# Define libraries +# ---------------- + +# Each module can define one or more native libraries. + +# These are compiled after the java sources have been compiled as that +# process also generates any native binding headers. + +# _JNI_LIBRARIES list of libraries to build. +# library names match System.loadLibrary(). + +# Global variables + +# _LDFLAGS +# _LDLIBS +# _CPPFLAGS +# _CFLAGS +# SO shared library suffix +# LIB shared library prefix + +# Utility functions +# +# $(call library-path,,) will resolve to the library file name. + +# Per library variables. + +# _SOURCES .c, .cc, .C - source files for library. Paths are relative to src//jni. +# _HEADERS header files for jmod + +# _LDFLAGS link flags +# _LIBADD extra objects to add to link line +# _LDLIBS link libraries +# _CPPFLAGS c pre-processor flags. "-Isrc//jni -Ibin/include/" is implicit. +# _CCFLAGS c compiler flags + +# _DEPENDENCIES A list of other objects on which this library depends before linking. + +# .c files have dependencies automatically generated + + +# Outputs +# ------- + +# All intermediate and output files are written to bin/ + +# This layout is enforced by javac +# bin/include// .h files from javac -h +# bin/modules// .class files from javac + +# This layout is convenient for netbeans +# bin/gen//gen/ .c, exe files for generator free use +# bin/gen//classes/ .java files from generator _JAVA_GENERATED + +# bin/status/ marker files for makefile + +# bin//.jar .jar modular + +# Native code + +# bin///lib .so librareies for jmod _LIBRARIES = libname +# bin///obj .o, .d files for library _SOURCES +# bin///include .h files for jmod _HEADERS +# bin///.jmod .jmod module + +# ###################################################################### + +E:= +S:=$(E) $(E) + +# All modules with native code +java_JMODS=$(foreach module,$(java_MODULES),$(if $(wildcard src/$(module)/jni/jni.make),$(module))) +# Only modules with no native code +java_JARS=$(foreach module,$(java_MODULES),$(if $(wildcard src/$(module)/jni/jni.make),,$(module))) +# Modules with generated java source +java_JGEN=$(foreach module,$(java_MODULES),$(if $(wildcard src/$(module)/gen/gen.make),$(module))) + +# Define per-module source and resource variables +$(foreach module,$(java_MODULES),$(eval $(module)_JAVA := $(shell find src/$(module)/classes -type f -name '*.java'))) +$(foreach module,$(java_MODULES),$(eval $(module)_RESOURCES := $(shell find src/$(module)/classes -type f \! -name '*.java'))) + +# external jni deps +$(foreach mod,$(java_MODULES),$(eval $(mod)_JDEPMOD_JNI=$(foreach jdep,$($(mod)_JDEPMOD),$(if $(wildcard src/$(mod)/jni),$(jdep))))) + +# Make some of the setup dirs (should it be a target?) +$(shell install -d bin/status bin/modules) + +# ###################################################################### + +all: jar +bin: +jni: +sources: + +.PHONY: all clean jar jni bin sources +clean: + rm -rf bin + +include $(patsubst %,src/%/gen/gen.make,$(java_JGEN)) +include $(patsubst %,src/%/jni/jni.make,$(java_JMODS)) + +# ###################################################################### +# Java +# ###################################################################### + +define java_targets= +# Rules for module $(1) +bin/status/$(1).data: $$($(1)_RESOURCES) +bin/status/$(1).classes: $(patsubst %,bin/status/%.classes,$($(1)_JDEPMOD)) $$($(1)_JAVA) +jar $(1): bin/$(1)/$(1).jar $(if $(wildcard src/$(1)/jni/jni.make),bin/$(1)/$(TARGET)/$(1).jmod) +bin: bin/status/$(1).classes bin/status/$(1).data +sources: bin/$(1)/$(1)-sources.zip + +# Create modular jar +bin/$(1)/$(1).jar: bin/status/$(1).classes bin/status/$(1).data + @install -d $$(@D) + $(JAR) cf $$@ \ + $(JARFLAGS) $$($(1)_JARFLAGS) \ + -C bin/modules/$(1) . + +# Create a jmod +bin/$(1)/$(TARGET)/$(1).jmod: bin/status/$(1).classes bin/status/$(1).data + rm -f $$@ + @install -d $$(@D) + $$(JMOD) create \ + $$(JMODFLAGS) $$($(1)_JMODFLAGS) \ + --target-platform $(TARGET) \ + --class-path bin/modules/$(1) \ + $$(if $$(wildcard bin/$(1)/$(TARGET)/include),--header-files bin/$(1)/$(TARGET)/include) \ + $$(if $$(wildcard src/$(1)/legal),--legal-notices src/$(1)/legal) \ + $$($(TARGET)_JMOD_LIBS) bin/$(1)/$(TARGET)/lib \ + $$@ + +# Create an IDE source zip, paths have to match --module-source-path +bin/$(1)/$(1)-sources.zip: bin/status/$(1).classes + jar -c -f $$@ -M \ + $$(patsubst src/$(1)/classes/%,-C src/$(1)/classes %,$$(filter src/$(1)/classes/%,$$($(1)_JAVA))) \ + $$(patsubst bin/gen/$(1)/classes/%,-C bin/gen/$(1)/classes %,$$(filter bin/gen/$(1)/classes/%,$$($(1)_JAVA))) + +endef + +#$(foreach module,$(java_MODULES),$(info $(call java_targets,$(module)))) +$(foreach module,$(java_MODULES),$(eval $(call java_targets,$(module)))) + +# ###################################################################### +# Global pattern rules + +# Stage resources +bin/status/%.data: + for data in $(patsubst src/$*/classes/%,"%",$($*_RESOURCES)) ; do \ + install -vDC "src/$*/classes/$$data" "bin/modules/$*/$$data" || exit 1 ; \ + done + touch $@ + +# Compile one module. This only updates (javac -h) headers if they changed. +bin/status/%.classes: + $(JAVAC) \ + --module-source-path "src/*/classes:bin/gen/*/classes" \ + $(JAVACFLAGS) $($*_JAVACFLAGS) \ + -h bin/inc \ + -d bin/modules \ + -m $* \ + $($*_JAVA) + if [ -d bin/inc/$* ] ; then \ + install -DC -t bin/include/$* bin/inc/$*/*.h ; \ + fi + touch $@ + +# ###################################################################### +# C stuff +# ###################################################################### + +SUFFIXES=.c .C .cc +SO=$($(TARGET)_SO) +LIB=$($(TARGET)_LIB) + +library-path=bin/$(1)/$(TARGET)/lib/$(LIB)$(2)$(SO) +library-dir=bin/$(1)/$(TARGET)/lib/ + +define jni_library= +# Rule for library $(2) in module $(1) +$(2)_OBJS = $(foreach sx,$(SUFFIXES),$(patsubst %$(sx), bin/$(1)/$(TARGET)/obj/%.o, $(filter %$(sx),$($(2)_SOURCES)))) +$(2)_SRCS = $(addprefix src/$(1)/jni/,$($(2)_SOURCES)) + +bin/$(1)/$(TARGET)/lib/$(LIB)$(2)$(SO): $$($(2)_OBJS) $($(2)_LIBADD) $($(2)_DEPENDENCIES) + @install -d $$(@D) + $($(TARGET)_CC) -o $$@ -shared \ + $($(TARGET)_LDFLAGS) $($(2)_LDFLAGS) $$($(2)_OBJS) $($(2)_LIBADD) $($(TARGET)_LDLIBS) $($(2)_LDLIBS) + +bin/$(1)/$(TARGET)/obj/%.o: src/$(1)/jni/%.c bin/status/$(1).classes + @install -d $$(@D) + $($(TARGET)_CC) -Isrc/$(1)/jni -Ibin/include/$(1) $($(TARGET)_CPPFLAGS) $($(2)_CPPFLAGS) \ + $($(TARGET)_CFLAGS) $($(2)_CFLAGS) -c -o $$@ $$< + +bin/$(1)/$(TARGET)/include/%.h: src/$(1)/jni/%.h + install -D $$< $$@ + +bin/$(1)/$(TARGET)/obj/%.d: src/$(1)/jni/%.c bin/status/$(1).classes + @install -d $$(@D) + @rm -f $$@ + @$($(TARGET)_CC) -MM -MT "bin/$(1)/$(TARGET)/obj/$$*.o" -Isrc/$(1)/jni -Ibin/include/$(1) \ + $($(TARGET)_CPPFLAGS) $($(2)_CPPFLAGS) $$< -o $$@.d 2>/dev/null + @sed 's,\($$*\.o\) *:,\1 $$@ : ,g' $$@.d > $$@ ; rm $$@.d + +bin jni $(1) bin/$(1)/$(TARGET)/$(1).jmod: bin/$(1)/$(TARGET)/lib/$(LIB)$(2)$(SO) $(addprefix bin/$(1)/$(TARGET)/include/,$($(2)_HEADERS)) + +$(if $(filter clean dist,$(MAKECMDGOALS)),,-include $$($(2)_OBJS:.o=.d)) +endef + +#$(foreach module,$(java_JMODS),$(foreach library,$($(module)_JNI_LIBRARIES),$(info $(call jni_library,$(module),$(library))))) +$(foreach module,$(java_JMODS),$(foreach library,$($(module)_JNI_LIBRARIES),$(eval $(call jni_library,$(module),$(library))))) + +# ###################################################################### + +dist: + tar cfz bin/$(dist_NAME)-$(dist_VERSION).tar.gz \ + --transform=s,^,$(dist_NAME)-$(dist_VERSION)/, \ + config.make java.make Makefile src \ + $(dist_EXTRA) + diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml new file mode 100644 index 0000000..17ac4b8 --- /dev/null +++ b/nbproject/build-impl.xml @@ -0,0 +1,1719 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + self.setSelected(!new java.io.File(file, "module-info.java").exists()); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @{paths} + + + + + + + + + + + + @{paths} + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.modules.dir + Must set dist.javadoc.dir + Must set build.test.modules.dir + Must set build.test.results.dir + Must set build.classes.excludes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + function coalesce(input, keyValueSeparator, multiSeparator, entrySeparator) { + var result = []; + var values = {}; + + (typeof input === "string" ? input.split(entrySeparator) : input).forEach(function(entry) { + var idx = entry.indexOf(keyValueSeparator); + if (idx < 1) { + result.push(entry); + } else { + var key = entry.substring(0, idx); + var val = entry.substring(idx + 1); + if (!values[key]) { + values[key] = []; + } + values[key].push(val.trim()); + } + }); + Object.keys(values).sort().forEach(function(k) { + result.push(k + keyValueSeparator + values[k].join(multiSeparator)); + }); + return result.join(" " + entrySeparator); + } + self.project.setProperty(attributes.get("property"), + coalesce(attributes.get("value"), + attributes.get("value-sep"), + attributes.get("entry-sep"), + attributes.get("multi-sep") + )); + + + + + + + + + function expandGroup(grp) { + var exp = []; + var item = ""; + var depth = 0; + + for (i = 0; i < grp.length; i++) { + var c = grp[i]; + switch (c) { + case '{': + if (depth++ === 0) { + continue; + } + break; + case '}': + if (--depth === 0) { + exp.push(item); + continue; + } + break; + case ',': + if (depth === 1) { + exp.push(item); + item = ""; + continue; + } + default: + break; + } + item = item + c; + } + return exp; + } + + function pathVariants(spec, res) { + res = res || []; + var start = spec.indexOf('{'); + if (start === -1) { + res.push(spec); + return res; + } + var depth = 1; + var end; + for (end = start + 1; end < spec.length && depth > 0; end++) { + var c = spec[end]; + switch (c) { + case '{': depth++; break; + case '}': depth--; break; + } + } + var prefix = spec.substring(0, start); + var suffix = spec.substring(end); + expandGroup(spec.slice(start, end)).forEach(function (item) { + pathVariants(prefix + item + suffix, res); + }) + return res; + } + + function toRegexp2(spec, filepattern, separator) { + var prefixes = []; + var suffixes = []; + pathVariants(spec).forEach(function(item) { + suffixes.push(item); + }); + var tail = ""; + var separatorString = separator; + if (separatorString == "\\") { + separatorString = "\\\\"; + } + if (filepattern && filepattern != tail) { + tail = separatorString + filepattern; + } + return "([^" + separatorString +"]+)\\Q" + separator + "\\E(" + suffixes.join("|") + ")" + tail; + } + self.project.setProperty(attributes.get("property"), + toRegexp2(attributes.get("modsource"), attributes.get("filepattern"), self.project.getProperty("file.separator"))); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No main class specified + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + self.setSelected(!new java.io.File(file, "module-info.class").exists()); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties new file mode 100644 index 0000000..7d6d351 --- /dev/null +++ b/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=d287ed4e +build.xml.script.CRC32=b55362bc +build.xml.stylesheet.CRC32=32069288@1.6.1 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=d287ed4e +nbproject/build-impl.xml.script.CRC32=11e54fa7 +nbproject/build-impl.xml.stylesheet.CRC32=0f0529df@1.6.1 diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..0922c67 --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,101 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=notzed.nativez +application.vendor=notzed +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +build.modules.dir=${build.dir}/modules +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.modules.dir=${build.dir}/test/modules +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.modulepath=\ + ${run.modulepath} +debug.test.classpath=\ + ${run.test.classpath} +debug.test.modulepath=\ + ${run.test.modulepath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.javadoc.dir=${dist.dir}/javadoc +dist.jlink.dir=${dist.dir}/jlink +dist.jlink.output=${dist.jlink.dir}/notzed.nativez +endorsed.classpath= +excludes= +file.reference.javafx.base.jar=/data/linux-amd64/javafx-sdk-11.0.2/lib/javafx.base.jar +file.reference.javafx.controls.jar=/data/linux-amd64/javafx-sdk-11.0.2/lib/javafx.controls.jar +file.reference.javafx.graphics.jar=/data/linux-amd64/javafx-sdk-11.0.2/lib/javafx.graphics.jar +includes=** +jar.compress=false +javac.classpath= +# Space-separated list of extra javac options +javac.compilerargs=-Xlint:unchecked +javac.deprecation=false +javac.external.vm=false +javac.modulepath=\ + ${file.reference.javafx.base.jar}:\ + ${file.reference.javafx.graphics.jar}:\ + ${file.reference.javafx.controls.jar} +javac.processormodulepath= +javac.processorpath=\ + ${javac.classpath} +javac.source=11 +javac.target=11 +javac.test.classpath=\ + ${javac.classpath} +javac.test.modulepath=\ + ${javac.modulepath}:\ + ${build.modules.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.html5=false +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +# The jlink additional root modules to resolve +jlink.additionalmodules= +# The jlink additional command line parameters +jlink.additionalparam= +jlink.launcher=true +jlink.launcher.name=notzed.nativez +platform.active=default_platform +run.classpath= +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs=-Djava.library.path=bin/notzed.nativez/linux-amd64/lib +run.modulepath=\ + ${javac.modulepath}:\ + ${build.modules.dir} +run.test.classpath=\ + ${javac.test.classpath} +run.test.modulepath=\ + ${javac.test.modulepath}:\ + ${build.test.modules.dir} +source.encoding=UTF-8 +src.dir=src +src.dir.path=classes +test.src.dir=src +test.src.dir.path=tests diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..ffa6cd9 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,15 @@ + + + org.netbeans.modules.java.j2semodule + + + notzed.nativez + + + + + + + + + diff --git a/src/notzed.nativez/classes/au/notzed/nativez/NativeZ.java b/src/notzed.nativez/classes/au/notzed/nativez/NativeZ.java new file mode 100644 index 0000000..f33ce74 --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/NativeZ.java @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2018 Michael Zucchi + * + * This file is part of nativez + * + * 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. + * + * (3)The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ +package au.notzed.nativez; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Native object manager. + *

+ */ +public abstract class NativeZ { + + private final static boolean dolog = false; + + private static Logger log() { + return Logger.getLogger("notzed.nativez"); + } + + /** + * Globally system unique key to a native resource. + *

+ * This will typically be a C pointer. + */ + final long p; + + /** + * Resource index. + */ + static private final PointerTable map = new PointerTable(); + + /** + * Reference queue for stale objects. + */ + static private final ReferenceQueue references = new ReferenceQueue<>(); + + static { + Thread cleanup = new Thread(NativeZ::cleaner, "NativeZ cleaner"); + cleanup.setPriority(Thread.MAX_PRIORITY); + cleanup.setDaemon(true); + cleanup.start(); + } + + /** + * Create a new object. + * + * @param p + */ + protected NativeZ(long p) { + this.p = p; + } + + /** + * Retrieve the resource key. + * + * @return + */ + public long getP() { + return p; + } + + /** + * Natives are considered equal if their p values are equal. + * + * @param obj + * @return + */ + @Override + public boolean equals(Object obj) { + return (obj instanceof NativeZ) && p == ((NativeZ) obj).p; + } + + @Override + public int hashCode() { + return CHandle.hashCode(p); + } + + /** + * Internally create a new instance. + *

+ * The module must be open to notzed.nativez for this to work. + * + * @param + * @param jtype Type of object to create. + * @param p The reference handle. + * @return A newly created object. + * @throws RuntimeException if the object cannot be instantiated. + */ + private static T createInstance(Class jtype, long p) { + cleanerStep(); + try { + Class[] params = {Long.TYPE}; + Constructor cc = jtype.getDeclaredConstructor(params); + + cc.setAccessible(true); + + return cc.newInstance(p); + } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + log().log(Level.SEVERE, null, ex); + throw new RuntimeException(ex); + } + } + + /** + * Register a new managed resource. + *

+ * Use this function if the object does not use the default constructor + * or has been created separately. + *

+ * Do not use this for internally reference counted resources. + * + * @param + * @param o The object to register. + * @return o is returned. + */ + public static T register(T o) { + CHandle h = new CResourceHandle(o, references, o.p); + + synchronized (map) { + map.putAlways(h); + } + + return o; + } + + /** + * Create a new managed object for a resource. + *

+ * Use this function when possible if using the default constructor + * as it saves a native to managed transition and also allows + * for concurrent instantiation. + *

+ * Do not call this if the resource may have already been registered or + * if it is internally reference counted, use {@link resolve} instead. + * + * @param jtype managed object type. + * @param p unique resource key. + * @return A newly created object. + * @throws RuntimeException if the object cannot be instantiated. + */ + public static T create(Class jtype, long p) { + T o; + CHandle h; + + if (dolog) + log().fine(() -> String.format(" create $%016x %s", p, jtype.getName())); + + o = createInstance(jtype, p); + h = new CResourceHandle(o, references, p); + + synchronized (map) { + map.putAlways(h); + } + + return o; + } + + /** + * Create a new or resolve an existing managed object for a resource. + *

+ * If the object already exists it is simply returned otherwise + * a new instance is created and registered. It is safe to call this + * instead of {@link create} for all cases. + *

+ * If the resources are managed natively via reference counting then this + * function must be used to ensure a unique instance is created. + *

+ * This may be called on the same resource key via multiple threads and + * is internally synchronised. + * + * @param jtype type of object to createInstance. + * @param p native pointer (or other native key). + * @return A unique Java object which manages the native resource. + */ + public static T resolve(Class jtype, long p) { + T o; + + if (dolog) + log().fine(() -> String.format(" resolve $%016x %s", p, jtype.getName())); + + // Instantiation needs to be synchronized for obvious reasons. + synchronized (map) { + CHandle h = (CHandle) map.get(p); + + if (h == null || (o = jtype.cast(h.get())) == null) { + o = createInstance(jtype, p); + h = new CResourceHandle(o, references, p); + map.putAlways(h); + } + } + return o; + } + + /** + * Create a non-managed object for a resource. + *

+ * This is a way to access internal members and other data which is + * not allocated explicitly. + *

+ * It is up to the caller to ensure the reference remains valid while in + * use, for example by retaining a reference to it's container. + *

+ * This may be called on the same resource key via multiple threads and + * is internally synchronised. + * + * @param jtype object type + * @param p unqiue resource key. As this must be globaly unique it + * cannot reference embedded structures that begin at the start of + * a container. A workaround is to offset the value and account for + * that in the implementation. + * @return A unique Java object for accessing the native resource. + */ + public static T refer(Class jtype, long p) { + T o; + + if (dolog) + log().fine(() -> String.format(" refer $%016x %s", p, jtype.getName())); + + synchronized (map) { + CHandle h = (CHandle) map.get(p); + + if (h == null || (o = jtype.cast(h.get())) == null) { + o = createInstance(jtype, p); + h = new CReferenceHandle(o, references, p); + map.putAlways(h); + } + } + + return o; + } + + /** + * Explicitly release a resource. + *

+ * The object is advanced to the refernce queue immediately. + *

+ * Applications MUST NOT access this object afterwards, even if + * they retain references to it. + */ + public void release() { + WeakReference ref; + + synchronized (map) { + ref = map.remove(p); + } + + if (ref != null) { + if (dolog) + log().fine(() -> String.format(" force $%016x %s", p, getClass().getName())); + + ref.enqueue(); + } + } + + /** + * Explicitly release multiple resources. + *

+ * A convenience function to release multiple resources explicitly. + *

+ * null objects are safely ignored. + * + * @param list + */ + public static void release(NativeZ... list) { + for (NativeZ o : list) { + if (o != null) { + o.release(); + } + } + } + + @Override + public String toString() { + return String.format("[$%016x: %s]", p, getClass().getSimpleName()); + } + + private static void cleanerStep() { + try { + CHandle stale = (CHandle) references.poll(); + if (stale != null) { + synchronized (map) { + map.remove(stale.p); + } + stale.release(); + } + } catch (Throwable ex) { + } + } + + /** + * Cleaner thread. + *

+ * This polls the reference queue and releases objects via + * their static release method. + */ + private static void cleaner() { + if (dolog) + log().info("Native finaliser started"); + try { + while (true) { + CHandle stale = (CHandle) references.remove(); + do { + try { + synchronized (map) { + map.remove(stale.p); + } + stale.release(); + } catch (Throwable ex) { + } + stale = (CHandle) references.poll(); + } while (stale != null); + } + } catch (InterruptedException ex) { + } + } + + /** + * Base class for a reference handle. + *

+ * This is a {@link WeakReference} that maintains the hashtable bucket + * chain and enough information to release the resource without referencing + * it. + */ + abstract private static class CHandle extends WeakReference { + + protected long p; + final Class jtype; + CHandle next; + + public CHandle(NativeZ referent, ReferenceQueue references, long p) { + super(referent, references); + this.p = p; + this.jtype = referent.getClass(); + } + + /** + * Actually release the native resource. + */ + abstract void release(); + + @Override + public boolean equals(Object obj) { + return (obj instanceof CHandle) && ((CHandle) obj).p == p; + } + + @Override + public int hashCode() { + return hashCode(p); + } + + /** + * Simple hashcode for native pointers. + *

+ * This simply strips the bottom 4 bits from the pointer as + * on a 64-bit system the low 3 bits are typically zero and the 4th + * isn't very well distributed. + * + * @param p + * @return + */ + public static final int hashCode(long p) { + return (int) p >>> 4; + } + } + + /** + * A dummy reference handle. + *

+ * The {@link release} function simply clears the pointer. + * + * @todo This may not be needed as it's only purpose is to allow + * unique global references across managed and unmanaged resources and + * multiple instances of unmanaged objects are safe. However, jjmpeg has + * some strange cases so it needs confirming. + */ + private static class CReferenceHandle extends CHandle { + + public CReferenceHandle(NativeZ referent, ReferenceQueue references, long p) { + super(referent, references, p); + } + + @Override + void release() { + if (dolog) + log().fine(() -> String.format(" %016x: Ignoring release %s", p, jtype.getSimpleName())); + p = 0; + } + } + + /** + * A CResourceHandle is a weak reference that maintains enough + * non-object-referring data to be able to release native resources. + *

+ * It does this by saving the native pointer `p' and the + * object class which manages it. A static method `void release(long)' + * on the specific class is used to release the native resource when + * it is no longer reachable or has been explicitly released. + */ + private static class CResourceHandle extends CHandle { + + public CResourceHandle(NativeZ referent, ReferenceQueue references, long p) { + super(referent, references, p); + } + + @Override + void release() { + try { + if (p != 0) { + if (dolog) + log().fine(() -> String.format(" reles $%016x %s", p, jtype.getName())); + + Method mm = jtype.getDeclaredMethod("release", Long.TYPE); + mm.setAccessible(true); + mm.invoke(null, p); + } + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + if (dolog) + log().log(Level.SEVERE, jtype.getName(), ex); + ex.printStackTrace(System.out); + } finally { + p = 0; + } + } + } + + /** + * Lightweight pointer hashtable. + *

+ * This serves two purposes: + *

    + *
  1. Track and resolve unique objects based on memory address; + *
  2. Hold hard references to the WeakReference as required by the gc system. + *
+ *

+ * CHandle's are chained directly from the index table, the p field + * is used as a key directly, and hash values are not cached. This combines + * to save significant memory per node. + */ + private static class PointerTable { + + int mask = 63; + int size = 0; + CHandle[] table = new CHandle[64]; + + private void resize(int length) { + CHandle[] ntable = new CHandle[length]; + int nmask = length - 1; + + for (int i = 0; i < table.length; i++) { + CHandle h = table[i]; + + while (h != null) { + CHandle n = h.next; + int k = h.hashCode() & nmask; + + h.next = ntable[k]; + ntable[k] = h; + + h = n; + } + } + + table = ntable; + mask = nmask; + } + + public CHandle put(CHandle h) { + CHandle o = remove(h.p); + + putAlways(h); + + return o; + } + + public void putAlways(CHandle h) { + if (size > table.length * 2) + resize(table.length * 2); + + int i = h.hashCode() & mask; + + h.next = table[i]; + table[i] = h; + size += 1; + } + + public CHandle get(long p) { + int i = CHandle.hashCode(p) & mask; + CHandle h = table[i]; + + while (h != null && h.p != p) + h = h.next; + return h; + } + + public CHandle remove(long p) { + int i = CHandle.hashCode(p) & mask; + CHandle h = table[i]; + CHandle a = null; + + while (h != null && h.p != p) { + a = h; + h = h.next; + } + if (h != null) { + if (a != null) + a.next = h.next; + else + table[i] = h.next; + size -= 1; + } + + return h; + } + } +} diff --git a/src/notzed.nativez/classes/module-info.java b/src/notzed.nativez/classes/module-info.java new file mode 100644 index 0000000..84a8720 --- /dev/null +++ b/src/notzed.nativez/classes/module-info.java @@ -0,0 +1,6 @@ + +module notzed.nativez { + requires java.logging; + + exports au.notzed.nativez; +} diff --git a/src/notzed.nativez/jni/jni.make b/src/notzed.nativez/jni/jni.make new file mode 100644 index 0000000..c9908db --- /dev/null +++ b/src/notzed.nativez/jni/jni.make @@ -0,0 +1,6 @@ + +notzed.nativez_JNI_LIBRARIES = nativez + +nativez_SOURCES = nativez-jni.c nativez-$(TARGET:-amd64=).c +nativez_CFLAGS = -Os -Wmissing-prototypes +nativez_HEADERS = nativez.h diff --git a/src/notzed.nativez/jni/nativez-jni.c b/src/notzed.nativez/jni/nativez-jni.c new file mode 100644 index 0000000..d3c7593 --- /dev/null +++ b/src/notzed.nativez/jni/nativez-jni.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2017,2019 Michael Zucchi + * + * This file is part of nativez + * + * 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. + * + * (3)The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ + +#include +#include + +#include "nativez.h" + +#define NZOBJECT "au/notzed/nativez/NativeZ" + +static JavaVM *vm; + +static jclass Buffer_classid; +static jmethodID Buffer_hasArray_; +static jmethodID Buffer_isDirect_; +static jmethodID Buffer_array_; +static jmethodID Buffer_arrayOffset_; +static jmethodID Buffer_position_; +static jmethodID Buffer_position_i; +static jmethodID Buffer_limit_; +static jmethodID Buffer_limit_i; + +static jclass ByteBuffer_classid; +static jmethodID ByteBuffer_order_l; + +static jobject ByteOrder_nativeOrder; // ByteOrder.nativeOrder() + +static jclass NativeZ_classid; +static jmethodID NativeZ_create_lj; +static jmethodID NativeZ_register_l; +static jmethodID NativeZ_resolve_lj; +static jmethodID NativeZ_refer_lj; +static jfieldID NativeZ_p; + +static const NZRefTable reftable[] = { + JCLASS(Buffer_classid, "java/nio/Buffer"), + JMETHOD(Buffer_hasArray_, "hasArray", "()Z"), + JMETHOD(Buffer_isDirect_, "isDirect", "()Z"), + JMETHOD(Buffer_array_, "array", "()Ljava/lang/Object;"), + JMETHOD(Buffer_arrayOffset_, "arrayOffset", "()I"), + JMETHOD(Buffer_position_, "position", "()I"), + JMETHOD(Buffer_position_i, "position", "(I)Ljava/nio/Buffer;"), + JMETHOD(Buffer_limit_, "limit", "()I"), + JMETHOD(Buffer_limit_i, "limit", "(I)Ljava/nio/Buffer;"), + + JCLASS(ByteBuffer_classid, "java/nio/ByteBuffer"), + JMETHOD(ByteBuffer_order_l, "order", "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"), + + JCLASS(NativeZ_classid, NZOBJECT), + JSMETHOD(NativeZ_create_lj, "create", "(Ljava/lang/Class;J)L" NZOBJECT ";"), + JSMETHOD(NativeZ_register_l, "register", "(L" NZOBJECT ";)L" NZOBJECT ";"), + JSMETHOD(NativeZ_resolve_lj, "resolve", "(Ljava/lang/Class;J)L" NZOBJECT ";"), + JSMETHOD(NativeZ_refer_lj, "refer", "(Ljava/lang/Class;J)L" NZOBJECT ";"), + JFIELD(NativeZ_p, "p", "J"), + + JEND +}; + +jint nativez_OnLoad(JavaVM *vmi, JNIEnv *env) { + /* Save VM - required for callbacks from threads */ + vm = vmi; + + if (nativez_ResolveReferences(env, reftable) != 0) + return -1; + + jclass jc = (*env)->FindClass(env, "java/nio/ByteOrder"); + if (jc == NULL) + return -1; + jmethodID ByteOrder_nativeOrder_ = (*env)->GetStaticMethodID(env, jc, "nativeOrder", "()Ljava/nio/ByteOrder;"); + ByteOrder_nativeOrder = (*env)->NewGlobalRef(env, (*env)->CallStaticObjectMethodA(env, jc, ByteOrder_nativeOrder_, NULL)); + + return 0; +} + +void *nativez_AllocAligned(size_t align, size_t size) { + void *mem; +#ifdef HAVE_ALIGNED_MALLOC + mem = _aligned_malloc(size, align); + return mem; +#else + if (posix_memalign(&mem, align, size) == 0) + return mem; + return NULL; +#endif +} + +void nativez_FreeAligned(void *mem) { +#ifdef HAVE_ALIGNED_MALLOC + _aligned_free(mem); +#else + free(mem); +#endif +} + +JNIEnv *nativez_AttachCurrentThread(void) { + JNIEnv *env = NULL; + + (*vm)->AttachCurrentThread(vm, (void **)&env, NULL); + + // one assumes this is null if error return? + return env; +} + +JNIEnv *nativez_AttachCurrentThreadAsDaemon(void) { + JNIEnv *env = NULL; + + (*vm)->AttachCurrentThreadAsDaemon(vm, (void **)&env, NULL); + + // one assumes this is null if error return? + return env; +} + +/* ********************************************************************** */ + +void nativez_ThrowException(JNIEnv *env, const char *type, const char *msg) { + jclass jc = (*env)->FindClass(env, type); + + if (jc) + (*env)->ThrowNew(env, jc, msg); + else { + fprintf(stderr, "Unable to throw exception `%s': `%s'\n", type, msg); + fflush(stderr); + } +} + +void nativez_ThrowOutOfMemoryError(JNIEnv *env, const char *msg) { + nativez_ThrowException(env, "java/lang/OutOfMemoryError", msg); +} + +/* ********************************************************************** */ + +jobject nativez_NewDirectBuffer(JNIEnv *env, void *data, jlong size) { + jobject jo; + + jo = (*env)->NewDirectByteBuffer(env, data, size); + if (jo) { + jvalue arg = { .l = ByteOrder_nativeOrder }; + + (*env)->CallObjectMethodA(env, jo, ByteBuffer_order_l, &arg); + } + + return jo; +} + +jboolean nativez_BufferHasArray(JNIEnv *env, jobject jbuffer) { + return (*env)->CallBooleanMethodA(env, jbuffer, Buffer_hasArray_, NULL); +} + +jboolean nativez_BufferIsDirect(JNIEnv *env, jobject jbuffer) { + return (*env)->CallBooleanMethodA(env, jbuffer, Buffer_isDirect_, NULL); +} + +jarray nativez_BufferArray(JNIEnv *env, jobject jbuffer) { + return (*env)->CallObjectMethodA(env, jbuffer, Buffer_array_, NULL); +} + +jint nativez_BufferArrayOffset(JNIEnv *env, jobject jbuffer) { + return (*env)->CallIntMethodA(env, jbuffer, Buffer_arrayOffset_, NULL); +} + +jint nativez_BufferPosition(JNIEnv *env, jobject jbuffer) { + return (*env)->CallIntMethodA(env, jbuffer, Buffer_position_, NULL); +} + +jint nativez_BufferLimit(JNIEnv *env, jobject jbuffer) { + return (*env)->CallIntMethodA(env, jbuffer, Buffer_limit_, NULL); +} + +void nativez_BufferSetPosition(JNIEnv *env, jobject jbuffer, jint jposition) { + jvalue arg = { .i = jposition }; + (void)(*env)->CallObjectMethodA(env, jbuffer, Buffer_position_i, &arg); +} + +void nativez_BufferSetLimit(JNIEnv *env, jobject jbuffer, jint jlimit) { + jvalue arg = { .i = jlimit }; + (void)(*env)->CallObjectMethodA(env, jbuffer, Buffer_limit_i, &arg); +} + +const char *nativez_GetString(JNIEnv *env, jstring js) { + return js ? (*env)->GetStringUTFChars(env, js, NULL) : NULL; +} + +void nativez_ReleaseString(JNIEnv *env, jstring js, const char *s) { + if (js) + (*env)->ReleaseStringUTFChars(env, js, s); +} + +jstring nativez_NewString(JNIEnv *env, const char *s) { + return s ? (*env)->NewStringUTF(env, s) : NULL; +} + +/* ********************************************************************** */ + +int nativez_ResolveReferences(JNIEnv *env, const NZRefTable *table) { + jclass jc = NULL; + const char *cname = NULL; + int failed = 0; + + for (int i=0;table[i].type;i++) { + switch (table[i].type) { + case 1: + jc = (*env)->FindClass(env, table[i].name); + if (!jc) { + fprintf(stderr, "Unablet to resolve class `%s'\n", table[i].name); + fflush(stderr); + return -1; + } + if (table[i].ptr) { + jc = (*env)->NewGlobalRef(env, jc); + *(table[i].ptr) = jc; + } + cname = table[i].name; + break; + case 2: // method + *(table[i].ptr) = (*env)->GetMethodID(env, jc, table[i].name, table[i].signature); + break; + case 3: // static method + *(table[i].ptr) = (*env)->GetStaticMethodID(env, jc, table[i].name, table[i].signature); + break; + case 4: // field + *(table[i].ptr) = (*env)->GetFieldID(env, jc, table[i].name, table[i].signature); + break; + case 5: // static field + *(table[i].ptr) = (*env)->GetStaticFieldID(env, jc, table[i].name, table[i].signature); + break; + } + + if ((table[i].ptr) && !*(table[i].ptr)) { + failed--; + fprintf(stderr, "Unable to resolve `%s.%s' sig `%s'\n", cname, table[i].name, table[i].signature); + fflush(stderr); + } + } + + return failed; +} + +/* ********************************************************************** */ + +int nativez_NonNull(JNIEnv *env, const char *what, void *ptr) { + if (ptr) + return 1; + + nativez_ThrowException(env, "java/lang/NullPointerException", what); + return 0; +} + +/* ********************************************************************** */ + +/** + * Retrieve pointer arguments. + * + * MUST always call ReleasePointers() with the same arguments regardles of return value. + */ +jboolean nativez_GetPointers(JNIEnv *env, const char *desc, nzpointer * __restrict info) { + char c; + while ((c = *desc++)) { + switch (c) { + case 'P': + if (!info->object) + goto nullPointer; + case 'p': + if (info->object && !(info->p.v = (*env)->GetPrimitiveArrayCritical(env, info->object, NULL))) + return 0; + break; + case 'U': + if (!info->object) + goto nullPointer; + case 'u': + if (info->object && !(info->p.U = (*env)->GetStringUTFChars(env, info->object, NULL))) + return 0; + break; + case 'N': + if (!info->object) + goto nullPointer; + case 'n': + if (info->object) + info->p.N = (void *)(uintptr_t)(*env)->GetLongField(env, info->object, NativeZ_p); + break; + default: + return 0; + } + info++; + } + return 1; + + nullPointer: + nativez_ThrowException(env, "java/lang/NullPointerException", "Argument missing"); + return 0; +} + +/** + * Release pointer arguments. + */ +void nativez_ReleasePointers(JNIEnv *env, const char *desc, nzpointer * __restrict info) { + char c; + while ((c = *desc++)) { + if (info->p.v) { + switch (c) { + case 'P': + case 'p': + (*env)->ReleasePrimitiveArrayCritical(env, info->object, info->p.v, 0); + break; + case 'U': + case 'u': + (*env)->ReleaseStringUTFChars(env, info->object, info->p.U); + break; + case 'N': + case 'n': + break; + } + } + info++; + } +} + +/* ********************************************************************** */ + +jobject NativeZ_create(JNIEnv *env, jclass jc, void *p) { + if (p) { + jvalue jargs[] = { + { .l = jc }, + { .j = (uintptr_t)p } + }; + + return (*env)->CallStaticObjectMethodA(env, NativeZ_classid, NativeZ_create_lj, jargs); + } else + return NULL; +} + +jobject NativeZ_register(JNIEnv *env, jobject jo) { + if (jo) { + jvalue jargs[] = { + { .l = jo } + }; + + return (*env)->CallStaticObjectMethodA(env, NativeZ_classid, NativeZ_register_l, jargs); + } else + return NULL; +} + +jobject NativeZ_resolve(JNIEnv *env, jclass jc, void *p) { + if (p) { + jvalue jargs[] = { + { .l = jc }, + { .j = (uintptr_t)p } + }; + + return (*env)->CallStaticObjectMethodA(env, NativeZ_classid, NativeZ_resolve_lj, jargs); + } else + return NULL; +} + +jobject NativeZ_refer(JNIEnv *env, jclass jc, void *p) { + if (p) { + jvalue jargs[] = { + { .l = jc }, + { .j = (uintptr_t)p } + }; + + return (*env)->CallStaticObjectMethodA(env, NativeZ_classid, NativeZ_refer_lj, jargs); + } else + return NULL; +} + +void *NativeZ_getP(JNIEnv *env, jobject jo) { + if (jo) + return (void *)(uintptr_t)(*env)->GetLongField(env, jo, NativeZ_p); + else + return NULL; +} diff --git a/src/notzed.nativez/jni/nativez-linux.c b/src/notzed.nativez/jni/nativez-linux.c new file mode 100644 index 0000000..05ce767 --- /dev/null +++ b/src/notzed.nativez/jni/nativez-linux.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2019 Michael Zucchi + * + * This file is part of nativez + * + * 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. + * + * (3)The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ + +#include + +#include "nativez.h" + +int nativez_ResolveLibraries(JNIEnv *env, const NZLibTable *table) { + char name[64]; + + for (int i=0;table[i].ptr;i++) { + void *lib; + + sprintf(name, "lib%s.so", table[i].name); + + lib = dlopen(name, RTLD_LAZY | RTLD_GLOBAL); + if (!lib) { + fprintf(stderr, "Unable to open library `%s'\n", name); + perror("dlopen"); + fflush(stderr); + return -1; + } + + *(table[i].ptr) = lib; + } + return 0; +} + +int nativez_ResolveFunctions(JNIEnv *env, const NZFuncTable * const table) { + void *lib = NULL; + for (int i=0;table[i].ptr;i++) { + if (table[i].name) { + void *entry = dlsym(lib, table[i].name); + + *(table[i].ptr) = entry; + if (!entry) { + fprintf(stderr, "Unable to resolve `%s'\n", table[i].name); + fflush(stderr); + } + } else { + lib = *(table[i].ptr); + } + } + + return 0; +} diff --git a/src/notzed.nativez/jni/nativez-windows.c b/src/notzed.nativez/jni/nativez-windows.c new file mode 100644 index 0000000..1b8241d --- /dev/null +++ b/src/notzed.nativez/jni/nativez-windows.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2019 Michael Zucchi + * + * This file is part of nativez + * + * 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. + * + * (3)The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ + +#include + +#include "nativez.h" + +int nativez_ResolveLibraries(JNIEnv *env, const NZLibTable *table) { + char name[64]; + + for (int i=0;table[i].ptr;i++) { + void *lib; + + sprintf(name, "%s-%d.dll", table[i].name, table[i].version); + + lib = LoadLibrary(name); + if (!lib) { + fprintf(stderr, "Unable to open library `%s'\n", name); + perror("dlopen"); + fflush(stderr); + return -1; + } + + *(table[i].ptr) = lib; + } + return 0; +} + +int nativez_ResolveFunctions(JNIEnv *env, const NZFuncTable * const table) { + void *lib = NULL; + for (int i=0;table[i].ptr;i++) { + if (table[i].name) { + void *entry = GetProcAddress(lib, table[i].name); + + *(table[i].ptr) = entry; + if (!entry) { + fprintf(stderr, "Unable to resolve `%s'\n", table[i].name); + fflush(stderr); + } + } else { + lib = *(table[i].ptr); + } + } + + return 0; +} diff --git a/src/notzed.nativez/jni/nativez.h b/src/notzed.nativez/jni/nativez.h new file mode 100644 index 0000000..74c37e6 --- /dev/null +++ b/src/notzed.nativez/jni/nativez.h @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2018 Michael Zucchi + * + * This file is part of nativez + * + * 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. + * + * (3)The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ + +#ifndef _NATIVEZ_H +#define _NATIVEZ_H + +#include + +/* + For resolving classes and methods with little code + Sequence: (JCLASS JMETHOD* JSMETHOD* JFIELD* JSFIEKD* + */ + +// Resolve a class stored in a variable +#define JCLASS(x, name) { 1, name, NULL, (void **)&x } +// Resolve a class not saved +#define JVCLASS(name) { 1, name, NULL, NULL } +// Resolve an object method on the last resolved class +#define JMETHOD(x, name, sig) { 2, name, sig, (void **)&x } +// Resolve a static method on the last resolved class +#define JSMETHOD(x, name, sig) { 3, name, sig, (void **)&x } +// Resolve an object field on the last resolved class +#define JFIELD(x, name, sig) { 4, name, sig, (void **)&x } +// Resolve a static field on the last resolved class +#define JSFIELD(x, name, sig) { 5, name, sig, (void **)&x } +// End the table +#define JEND { 0, NULL, NULL, NULL } + +typedef struct NZRefTable { + const int type; + const char *name; + const char *signature; + void **ptr; +} NZRefTable; + +int nativez_ResolveReferences(JNIEnv *env, const NZRefTable *table); + +/* + For resolving link libraries. + */ + +// Define a library base name, version and flags. +// A variable `_lib' must exist accessible to the compilation +// unit to store the library handle. +#define DLSO(name, v, flags) { #name, v, flags, (void **)&name ## _lib } + +typedef struct NZLibTable { + const char * name; /* name of library */ + const int version; /* version of library, used on some platforms */ + const int flags; /* DLSO_* flags */ + void **ptr; /* pointer to library handle */ +} NZLibTable; + +#define DLSO_NONCORE 1 // non-core library, if missing is safely ignored + +int nativez_ResolveLibraries(JNIEnv *env, const NZLibTable *table); + +/* + Resolve functions from libraries, table is 0-terminated. + + Sequence: (DLLIB DLFUNC*)* DLEND +*/ + +// Set the current library variable. It doesn't include the _lib suffix. +#define DLLIB(x) { NULL, &x ## _lib } +// Resolve a function by name. The variable holding the function pointer must start with 'd'. +#define DLFUNC(x) { #x, (void **)&(d ## x) } +// Terminate the list +#define DLEND { NULL, NULL } + +typedef struct JJFuncTable { + const char *name; + void **ptr; +} NZFuncTable; + +// Check if function exists, return value if not +#define DLCHECK_RET(env, x, ret) do { if (!nativez_NonNull(env, #x, (void *)d ## x)) { return ret; } } while (0) +// Call a function pointer +#define DLCALL(x) (* d ## x) + +int nativez_ResolveFunctions(JNIEnv *env, const NZFuncTable *table); + +/** + * Checks if ptr is non-null, if it is null throws a NullPointer + * exception with the given description and returns true. + */ +int nativez_NonNull(JNIEnv *env, const char *what, void *ptr); + +/** + * For converting array or string arguments to native pointers. + */ +typedef struct nzpointer nzpointer; + +struct nzpointer { + const jobject object; + union { + void *v; + jint *i; + jfloat *f; + jdouble *d; + const char *U; + void *N; + } p; +}; + +/** + * Retrieve pointers from java objects. + * + * desc is a string which defines the types of the arguments. + * + * p j*Array as GetPrimitiveArrayCritical + * u String as UTF8 + * n Native.Z p pointer + * P j*Array that may not be null + * U String that may not be null + * N NativeZ that may not be null + * + * info is an array of initialsed nzpointer objects with the .object field set to the + * source object and the p field nullified. Each element corresponds to the + * same index desc. + * + * Note: MUST always call ReleasePointers() with the same arguments regardles of return value. + * + * Returns true if all pointers were resolved successfully. + */ +jboolean nativez_GetPointers(JNIEnv *env, const char *desc, nzpointer * __restrict info); +void nativez_ReleasePointers(JNIEnv *env, const char *desc, nzpointer *info); + +/*** + * Call GetStringUTFChars but handles null js + */ +const char *nativez_GetString(JNIEnv *env, jstring js); + +/** + * Call ReleaseStringUTFChars but handles null js + */ +void nativez_ReleaseString(JNIEnv *env, jstring js, const char *s); + +/** + * Call NewStringUTF but handle NULL s + */ +jstring nativez_NewString(JNIEnv *env, const char *s); + +/** + * Throw a basic named exception with a simple message. + */ +void nativez_ThrowException(JNIEnv *env, const char *cname, const char *msg); +void nativez_ThrowOutOfMemoryError(JNIEnv *env, const char *msg); + +/** + * (Byte)Buffer methods + */ +jobject nativez_NewDirectBuffer(JNIEnv *env, void *data, jlong size); +jboolean nativez_BufferHasArray(JNIEnv *env, jobject jbuffer); +jboolean nativez_BufferIsDirect(JNIEnv *env, jobject jbuffer); +jarray nativez_BufferArray(JNIEnv *env, jobject jbuffer); +jint nativez_BufferArrayOffset(JNIEnv *env, jobject jbuffer); +jint nativez_BufferPosition(JNIEnv *env, jobject jbuffer); +jint nativez_BufferLimit(JNIEnv *env, jobject jbuffer); +void nativez_BufferSetPosition(JNIEnv *env, jobject jbuffer, jint jposition); +void nativez_BufferSetLimit(JNIEnv *env, jobject jbuffer, jint jlimit); + +/** + * Allocate aligned memory, throws an exception on error. + */ +void *nativez_AllocAligned(size_t align, size_t size); +void nativez_FreeAligned(void *mem); + +/* + Call this in the OnLoad method +*/ +jint nativez_OnLoad(JavaVM *vmi, JNIEnv *env); + +/* + Attach current thread, so it can be used in callbacks. + */ +JNIEnv *nativez_AttachCurrentThread(void); +JNIEnv *nativez_AttachCurrentThreadAsDaemon(void); + +/* + It is always safe to call resolve() for the freeable object case, + create() just implements an optimised path. + + resolve() is required for objects which are refcounted on the C side. + */ + +/* Create a new object, e.g. one that has just been allocated and can't possibly already exist in java */ +jobject NativeZ_create(JNIEnv *env, jclass jc, void *p); +/* Register a newly created object */ +jobject NativeZ_register(JNIEnv *env, jobject object); +/* Turn a pointer into a freeable object. If it already exists the same object will be returned */ +jobject NativeZ_resolve(JNIEnv *env, jclass jc, void *p); +/* Turn a pointer into a non-freeable object. If it already exists the same object will be returned */ +jobject NativeZ_refer(JNIEnv *env, jclass jc, void *p); +/* Retreive the native pointer stored in NativeZ subclass instance jo */ +void *NativeZ_getP(JNIEnv *env, jobject jo); + +#endif diff --git a/src/notzed.nativez/legal/LICENSE b/src/notzed.nativez/legal/LICENSE new file mode 100644 index 0000000..e58c522 --- /dev/null +++ b/src/notzed.nativez/legal/LICENSE @@ -0,0 +1,31 @@ + + Copyright (C) 2018,2019 Michael Zucchi + + 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. + + (3)The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +